aboutsummaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/coreSQLiteStudio/db
diff options
context:
space:
mode:
authorLibravatarUnit 193 <unit193@ubuntu.com>2017-02-09 04:37:26 -0500
committerLibravatarUnit 193 <unit193@ubuntu.com>2017-02-09 04:37:26 -0500
commitc9d6debf9015b7853c3e061bbc64a555d85e2fcd (patch)
tree53341bc57ae9fbad2beb5b6c08d97a68bee0ec8e /SQLiteStudio3/coreSQLiteStudio/db
parentd5caba2b1f36dc3b92fa705a06097d0597fa2ddd (diff)
parentd9aa870e5d509cc7309ab82dd102a937ab58613a (diff)
Merge tag 'upstream/3.1.1+dfsg1'
Upstream version 3.1.1+dfsg1 # gpg: Signature made Thu 09 Feb 2017 04:37:24 AM EST # gpg: using RSA key 5001E1B09AA3744B # gpg: issuer "unit193@ubuntu.com" # gpg: Good signature from "Unit 193 <unit193@ubuntu.com>" [unknown] # gpg: aka "Unit 193 <unit193@gmail.com>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 8DB3 E586 865D 2B4A 2B18 5A5C 5001 E1B0 9AA3 744B
Diffstat (limited to 'SQLiteStudio3/coreSQLiteStudio/db')
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp2
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/chainexecutor.cpp16
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/chainexecutor.h19
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.cpp126
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.h67
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutoraddrowids.cpp31
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcellsize.cpp8
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumns.cpp38
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumns.h2
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutordatasources.cpp2
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorexecute.cpp10
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorreplaceviews.cpp1
12 files changed, 216 insertions, 106 deletions
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp
index 8c08a51..cd9b972 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp
@@ -761,7 +761,7 @@ bool AbstractDb::commit()
SqlQueryPtr results = exec("COMMIT;", Flag::NO_LOCK);
if (results->isError())
{
- qCritical() << "Error while commiting a transaction: " << results->getErrorCode() << results->getErrorText();
+ qCritical() << "Error while committing a transaction: " << results->getErrorCode() << results->getErrorText();
return false;
}
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/chainexecutor.cpp b/SQLiteStudio3/coreSQLiteStudio/db/chainexecutor.cpp
index a35856b..1f553ea 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/chainexecutor.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/db/chainexecutor.cpp
@@ -2,6 +2,7 @@
#include "sqlerrorcodes.h"
#include "db/sqlquery.h"
#include <QDebug>
+#include <QDateTime>
ChainExecutor::ChainExecutor(QObject *parent) :
QObject(parent)
@@ -32,12 +33,14 @@ void ChainExecutor::exec()
{
if (!db)
{
+ emit finished(SqlQueryPtr());
emit failure(SqlErrorCode::DB_NOT_DEFINED, tr("The database for executing queries was not defined.", "chain executor"));
return;
}
if (!db->isOpen())
{
+ emit finished(SqlQueryPtr());
emit failure(SqlErrorCode::DB_NOT_OPEN, tr("The database for executing queries was not open.", "chain executor"));
return;
}
@@ -47,6 +50,7 @@ void ChainExecutor::exec()
SqlQueryPtr result = db->exec("PRAGMA foreign_keys = 0;");
if (result->isError())
{
+ emit finished(SqlQueryPtr());
emit failure(db->getErrorCode(), tr("Could not disable foreign keys in the database. Details: %1", "chain executor").arg(db->getErrorText()));
return;
}
@@ -54,6 +58,7 @@ void ChainExecutor::exec()
if (transaction && !db->begin())
{
+ emit finished(SqlQueryPtr());
emit failure(db->getErrorCode(), tr("Could not start a database transaction. Details: %1", "chain executor").arg(db->getErrorText()));
return;
}
@@ -75,7 +80,7 @@ void ChainExecutor::executeCurrentSql()
{
if (currentSqlIndex >= sqls.size())
{
- executionSuccessful();
+ executionSuccessful(lastExecutionResults);
return;
}
@@ -135,10 +140,11 @@ void ChainExecutor::executionFailure(int errorCode, const QString& errorText)
restoreFk();
successfulExecution = false;
executionErrors << ExecutionError(errorCode, errorText);
+ emit finished(lastExecutionResults);
emit failure(errorCode, errorText);
}
-void ChainExecutor::executionSuccessful()
+void ChainExecutor::executionSuccessful(SqlQueryPtr results)
{
if (transaction && !db->commit())
{
@@ -148,7 +154,8 @@ void ChainExecutor::executionSuccessful()
restoreFk();
successfulExecution = true;
- emit success();
+ emit finished(results);
+ emit success(results);
}
void ChainExecutor::executeSync()
@@ -163,11 +170,12 @@ void ChainExecutor::executeSync()
currentSqlIndex++;
}
- executionSuccessful();
+ executionSuccessful(results);
}
bool ChainExecutor::handleResults(SqlQueryPtr results)
{
+ lastExecutionResults = results;
if (results->isError())
{
if (interrupted || currentSqlIndex >= mandatoryQueries.size() || mandatoryQueries[currentSqlIndex])
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/chainexecutor.h b/SQLiteStudio3/coreSQLiteStudio/db/chainexecutor.h
index 0afbdb8..90c56f6 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/chainexecutor.h
+++ b/SQLiteStudio3/coreSQLiteStudio/db/chainexecutor.h
@@ -229,7 +229,7 @@ class API_EXPORT ChainExecutor : public QObject
*
* Commits transaction (in case of transactional execution) and emits success().
*/
- void executionSuccessful();
+ void executionSuccessful(SqlQueryPtr results);
/**
* @brief Executes all queries synchronously.
@@ -328,6 +328,8 @@ class API_EXPORT ChainExecutor : public QObject
bool disableForeignKeys = false;
bool disableObjectDropsDetection = false;
+ SqlQueryPtr lastExecutionResults;
+
public slots:
/**
* @brief Interrupts query execution.
@@ -353,10 +355,23 @@ class API_EXPORT ChainExecutor : public QObject
signals:
/**
* @brief Emitted when all mandatory queries were successfully executed.
+ * @param results Execution results from last query execution.
*
* See setMandatoryQueries() for details on mandatory queries.
*/
- void success();
+ void success(SqlQueryPtr results);
+
+ /**
+ * @brief Emitted when chain execution is finished, regardless of result.
+ * @param results Execution results from last query execution (may be null).
+ *
+ * In slot for this signal always check getSuccessfulExecution(),
+ * because execution could failed, despite results providing no error.
+ * It may happen for example if execution was interrupted,
+ * or executor could not pass through execution preparation phase
+ * (in which case results will be null).
+ */
+ void finished(SqlQueryPtr results);
/**
* @brief Emitted when major error occurred while executing a query.
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.cpp b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.cpp
index d2e7072..4a1c2f6 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.cpp
@@ -20,6 +20,7 @@
#include "queryexecutorsteps/queryexecutordetectschemaalter.h"
#include "queryexecutorsteps/queryexecutorvaluesmode.h"
#include "common/unused.h"
+#include "chainexecutor.h"
#include "log.h"
#include <QMutexLocker>
#include <QDateTime>
@@ -36,6 +37,8 @@ QueryExecutor::QueryExecutor(Db* db, const QString& query, QObject *parent) :
QObject(parent)
{
context = new Context();
+ simpleExecutor = new ChainExecutor(this);
+ simpleExecutor->setTransaction(false);
originalQuery = query;
setDb(db);
setAutoDelete(false);
@@ -43,6 +46,8 @@ QueryExecutor::QueryExecutor(Db* db, const QString& query, QObject *parent) :
connect(this, SIGNAL(executionFailed(int,QString)), this, SLOT(cleanupAfterExecFailed(int,QString)));
connect(DBLIST, SIGNAL(dbAboutToBeUnloaded(Db*, DbPlugin*)), this, SLOT(cleanupBeforeDbDestroy(Db*)));
connect(DBLIST, SIGNAL(dbRemoved(Db*)), this, SLOT(cleanupBeforeDbDestroy(Db*)));
+ connect(simpleExecutor, SIGNAL(finished(SqlQueryPtr)), this, SLOT(simpleExecutionFinished(SqlQueryPtr)));
+
}
QueryExecutor::~QueryExecutor()
@@ -205,12 +210,26 @@ void QueryExecutor::run()
void QueryExecutor::execInternal()
{
+ queriesForSimpleExecution.clear();
if (forceSimpleMode)
{
executeSimpleMethod();
return;
}
+ if (queryCountLimitForSmartMode > -1)
+ {
+ queriesForSimpleExecution = quickSplitQueries(originalQuery, false, true);
+ int queryCount = queriesForSimpleExecution.size();
+ if (queryCount > queryCountLimitForSmartMode)
+ {
+ qDebug() << "Number of queries" << queryCount << "exceeds maximum number allowed for smart execution method" <<
+ queryCountLimitForSmartMode << ". Simple method will be used to retain efficiency.";
+ executeSimpleMethod();
+ return;
+ }
+ }
+
simpleExecution = false;
interrupted = false;
@@ -280,6 +299,14 @@ bool QueryExecutor::countResults()
return true;
}
+void QueryExecutor::dbAsyncExecFinished(quint32 asyncId, SqlQueryPtr results)
+{
+ if (handleRowCountingResults(asyncId, results))
+ return;
+
+ // If this was raised by any other asyncExec, handle it here.
+}
+
qint64 QueryExecutor::getLastExecutionTime() const
{
return context->executionTime;
@@ -391,45 +418,26 @@ void QueryExecutor::exec(const QString& query)
exec();
}
-void QueryExecutor::dbAsyncExecFinished(quint32 asyncId, SqlQueryPtr results)
-{
- if (handleRowCountingResults(asyncId, results))
- return;
-
- if (!simpleExecution)
- return;
-
- if (this->asyncId == 0)
- return;
-
- if (this->asyncId != asyncId)
- return;
-
- this->asyncId = 0;
-
- simpleExecutionFinished(results);
-}
-
void QueryExecutor::executeSimpleMethod()
{
simpleExecution = true;
context->editionForbiddenReasons << EditionForbiddenReason::SMART_EXECUTION_FAILED;
- simpleExecutionStartTime = QDateTime::currentMSecsSinceEpoch();
+ if (queriesForSimpleExecution.isEmpty())
+ queriesForSimpleExecution = quickSplitQueries(originalQuery, false, true);
- if (asyncMode)
- {
- asyncId = db->asyncExec(originalQuery, context->queryParameters, Db::Flag::PRELOAD);
- }
- else
- {
- SqlQueryPtr results = db->exec(originalQuery, context->queryParameters, Db::Flag::PRELOAD);
- simpleExecutionFinished(results);
- }
+ QStringList queriesWithPagination = applyLimitForSimpleMethod(queriesForSimpleExecution);
+
+ simpleExecutor->setQueries(queriesWithPagination);
+ simpleExecutor->setDb(db);
+ simpleExecutor->setAsync(false); // this is already in a thread
+
+ simpleExecutionStartTime = QDateTime::currentMSecsSinceEpoch();
+ simpleExecutor->exec();
}
void QueryExecutor::simpleExecutionFinished(SqlQueryPtr results)
{
- if (results->isError())
+ if (results.isNull() || results->isError() || !simpleExecutor->getSuccessfulExecution())
{
executionMutex.lock();
executionInProgress = false;
@@ -437,9 +445,10 @@ void QueryExecutor::simpleExecutionFinished(SqlQueryPtr results)
handleErrorsFromSmartAndSimpleMethods(results);
return;
}
+ context->executionTime = QDateTime::currentMSecsSinceEpoch() - simpleExecutionStartTime;
if (simpleExecIsSelect())
- context->countingQuery = "SELECT count(*) AS cnt FROM ("+originalQuery+");";
+ context->countingQuery = "SELECT count(*) AS cnt FROM ("+trimQueryEnd(queriesForSimpleExecution.last())+");";
else
context->rowsCountingRequired = true;
@@ -452,7 +461,6 @@ void QueryExecutor::simpleExecutionFinished(SqlQueryPtr results)
context->resultColumns << resCol;
}
- context->executionTime = QDateTime::currentMSecsSinceEpoch() - simpleExecutionStartTime;
context->rowsAffected = results->rowsAffected();
context->totalRowsReturned = 0;
context->executionResults = results;
@@ -467,7 +475,7 @@ void QueryExecutor::simpleExecutionFinished(SqlQueryPtr results)
context->resultsHandler = nullptr;
}
- if (!forceSimpleMode)
+ if (!forceSimpleMode && queriesForSimpleExecution.size() <= queryCountLimitForSmartMode)
notifyWarn(tr("SQLiteStudio was unable to extract metadata from the query. Results won't be editable."));
emit executionFinished(results);
@@ -475,7 +483,7 @@ void QueryExecutor::simpleExecutionFinished(SqlQueryPtr results)
bool QueryExecutor::simpleExecIsSelect()
{
- TokenList tokens = Lexer::tokenize(originalQuery, db->getDialect());
+ TokenList tokens = Lexer::tokenize(queriesForSimpleExecution.last(), db->getDialect());
tokens.trim();
// First check if it's explicit "SELECT" or "VALUES" (the latter one added in SQLite 3.8.4).
@@ -563,6 +571,34 @@ bool QueryExecutor::handleRowCountingResults(quint32 asyncId, SqlQueryPtr result
return true;
}
+
+QStringList QueryExecutor::applyLimitForSimpleMethod(const QStringList &queries)
+{
+ static_qstring(tpl, "SELECT * FROM (%1) LIMIT %2 OFFSET %3");
+ QStringList result = queries;
+
+ QString lastQuery = queries.last();
+
+ bool isSelect = false;
+ getQueryAccessMode(lastQuery, db->getDialect(), &isSelect);
+ if (isSelect)
+ {
+ result.removeLast();
+ result << tpl.arg(trimQueryEnd(lastQuery), QString::number(resultsPerPage), QString::number(page * resultsPerPage));
+ }
+ return result;
+}
+
+int QueryExecutor::getQueryCountLimitForSmartMode() const
+{
+ return queryCountLimitForSmartMode;
+}
+
+void QueryExecutor::setQueryCountLimitForSmartMode(int value)
+{
+ queryCountLimitForSmartMode = value;
+}
+
bool QueryExecutor::getForceSimpleMode() const
{
return forceSimpleMode;
@@ -602,13 +638,25 @@ void QueryExecutor::handleErrorsFromSmartAndSimpleMethods(SqlQueryPtr results)
// therefore we need to check code from smart execution, before deciding which one to use).
// Just rename attach names in the message.
bool useSmartError = context->errorCodeFromSmartExecution != 0;
- QString msg = useSmartError ? context->errorMessageFromSmartExecution : results->getErrorText();
- int code = useSmartError ? context->errorCodeFromSmartExecution : results->getErrorCode();
- QString match;
- QString replaceName;
- Dialect dialect = db->getDialect();
+ QString msg;
+ int code;
+
+ if (!useSmartError && (results.isNull() || !results->isError()) && !simpleExecutor->getErrors().isEmpty())
+ {
+ code = simpleExecutor->getErrors().first().first;
+ msg = simpleExecutor->getErrors().first().second;
+ }
+ else
+ {
+ msg = useSmartError ? context->errorMessageFromSmartExecution : results->getErrorText();
+ code = useSmartError ? context->errorCodeFromSmartExecution : results->getErrorCode();
+ }
+
if (useSmartError)
{
+ QString match;
+ QString replaceName;
+ Dialect dialect = db->getDialect();
for (const QString& attachName : context->dbNameToAttach.rightValues())
{
match = attachName + ".";
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.h b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.h
index eddbe8c..4830e36 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.h
+++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.h
@@ -18,6 +18,7 @@ class Parser;
class SqliteQuery;
class QueryExecutorStep;
class DbPlugin;
+class ChainExecutor;
/**
* @brief Advanced SQL query execution handler.
@@ -1034,6 +1035,9 @@ class API_EXPORT QueryExecutor : public QObject, public QRunnable
bool isInterrupted() const;
+ int getQueryCountLimitForSmartMode() const;
+ void setQueryCountLimitForSmartMode(int value);
+
private:
/**
* @brief Executes query.
@@ -1088,17 +1092,6 @@ class API_EXPORT QueryExecutor : public QObject, public QRunnable
void executeSimpleMethod();
/**
- * @brief Handles results of simple execution.
- * @param results Results object returned from Db.
- *
- * Checks results for errors and extracts basic meta information,
- * such as rows affected, total result rows and time of execution.
- *
- * In case of success emits executionFinished(), in case of error emits executionFailed().
- */
- void simpleExecutionFinished(SqlQueryPtr results);
-
- /**
* @brief Tests whether the original query is a SELECT statement.
* @return true if the query is SELECT, or false otherwise.
*
@@ -1130,6 +1123,8 @@ class API_EXPORT QueryExecutor : public QObject, public QRunnable
*/
bool handleRowCountingResults(quint32 asyncId, SqlQueryPtr results);
+ QStringList applyLimitForSimpleMethod(const QStringList &queries);
+
/**
* @brief Query executor context object.
*
@@ -1173,6 +1168,8 @@ class API_EXPORT QueryExecutor : public QObject, public QRunnable
*/
QString originalQuery;
+ QStringList queriesForSimpleExecution;
+
/**
* @brief Predefined number of results per page.
*
@@ -1245,18 +1242,20 @@ class API_EXPORT QueryExecutor : public QObject, public QRunnable
int dataLengthLimit = -1;
/**
- * @brief Exact moment when query execution started.
+ * @brief Limit of queries, after which simple mode is used.
*
- * Expressed in number of milliseconds since 1970-01-01 00:00:00.
+ * Up to the defined limit the smart execution will be used (unless #forceSimpleMode was set).
+ * After exceeding this limit, the simple mode will be used.
+ * Set to negative number to disable this limit.
*/
- qint64 simpleExecutionStartTime;
+ int queryCountLimitForSmartMode = -1;
/**
- * @brief Asynchronous ID of query execution.
+ * @brief Exact moment when query execution started.
*
- * Asynchronous ID returned from Db::asyncExec() for the query execution.
+ * Expressed in number of milliseconds since 1970-01-01 00:00:00.
*/
- quint32 asyncId = 0;
+ qint64 simpleExecutionStartTime;
/**
* @brief Asynchronous ID of counting query execution.
@@ -1329,6 +1328,7 @@ class API_EXPORT QueryExecutor : public QObject, public QRunnable
Db::QueryResultsHandler resultsHandler = nullptr;
bool forceSimpleMode = false;
+ ChainExecutor* simpleExecutor = nullptr;
signals:
/**
@@ -1381,17 +1381,6 @@ class API_EXPORT QueryExecutor : public QObject, public QRunnable
private slots:
/**
- * @brief Handles asynchronous database execution results.
- * @param asyncId Asynchronous ID of the execution.
- * @param results Results from the execution.
- *
- * QueryExecutor checks whether the \p asyncId belongs to the counting query execution,
- * or the simple execution.
- * Dispatches query results to a proper handler method.
- */
- void dbAsyncExecFinished(quint32 asyncId, SqlQueryPtr results);
-
- /**
* @brief Calledn when an executor step has failed with its job.
*
* An executor step reported an error. "Smart execution" failed and now the executor will try
@@ -1416,6 +1405,28 @@ class API_EXPORT QueryExecutor : public QObject, public QRunnable
* be unloaded soon and we won't be able to call results destructor.
*/
void cleanupBeforeDbDestroy(Db* dbToBeUnloaded);
+
+ /**
+ * @brief Handles results of simple execution.
+ * @param results Results object returned from Db.
+ *
+ * Checks results for errors and extracts basic meta information,
+ * such as rows affected, total result rows and time of execution.
+ *
+ * In case of success emits executionFinished(), in case of error emits executionFailed().
+ */
+ void simpleExecutionFinished(SqlQueryPtr results);
+
+ /**
+ * @brief Handles asynchronous database execution results.
+ * @param asyncId Asynchronous ID of the execution.
+ * @param results Results from the execution.
+ *
+ * QueryExecutor checks whether the \p asyncId belongs to the counting query execution,
+ * or the simple execution.
+ * Dispatches query results to a proper handler method.
+ */
+ void dbAsyncExecFinished(quint32 asyncId, SqlQueryPtr results);
};
int qHash(QueryExecutor::EditionForbiddenReason reason);
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutoraddrowids.cpp b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutoraddrowids.cpp
index 8cc344c..d417072 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutoraddrowids.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutoraddrowids.cpp
@@ -31,8 +31,10 @@ bool QueryExecutorAddRowIds::exec()
}
// ...and putting it into parsed query, then update processed query
+// qDebug() << "before addrowid: " << context->processedQuery;
select->rebuildTokens();
updateQueries();
+// qDebug() << "after addrowid: " << context->processedQuery;
return true;
}
@@ -148,17 +150,30 @@ QHash<QString,QString> QueryExecutorAddRowIds::getNextColNames(const SelectResol
bool QueryExecutorAddRowIds::addResultColumns(SqliteSelect::Core* core, const SelectResolver::Table& table,
QHash<SelectResolver::Table,QHash<QString,QString>>& rowIdColsMap, bool isTopSelect)
{
+ SelectResolver::Table keyTable = table;
+ if (!rowIdColsMap.contains(table))
+ {
+ for (const SelectResolver::Table& rowIdColsMapTable : rowIdColsMap.keys())
+ {
+ if (!table.oldTableAliases.contains(rowIdColsMapTable.tableAlias, Qt::CaseInsensitive))
+ continue;
+
+ keyTable = rowIdColsMapTable;
+ }
+ }
+
+ // Find ROWID column from inner SELECT, or create new column for the table.
QHash<QString, QString> executorToRealColumns;
bool aliasOnlyAsSelectColumn = false;
- if (rowIdColsMap.contains(table))
+ if (rowIdColsMap.contains(keyTable))
{
- executorToRealColumns = rowIdColsMap[table]; // we already have resCol names from subselect
+ executorToRealColumns = rowIdColsMap[keyTable]; // we already have resCol names from subselect
aliasOnlyAsSelectColumn = true;
}
else
{
- executorToRealColumns = getNextColNames(table);
- rowIdColsMap[table] = executorToRealColumns;
+ executorToRealColumns = getNextColNames(keyTable);
+ rowIdColsMap[keyTable] = executorToRealColumns;
}
if (executorToRealColumns.size() == 0)
@@ -182,7 +197,7 @@ bool QueryExecutorAddRowIds::addResultColumns(SqliteSelect::Core* core, const Se
queryExecutorResCol->dbName = table.originalDatabase;
queryExecutorResCol->database = table.database;
queryExecutorResCol->table = table.table;
- queryExecutorResCol->tableAlias = table.alias;
+ queryExecutorResCol->tableAlias = table.tableAlias;
queryExecutorResCol->queryExecutorAliasToColumn = executorToRealColumns;
context->rowIdColumns << queryExecutorResCol;
}
@@ -207,9 +222,9 @@ bool QueryExecutorAddRowIds::addResultColumns(SqliteSelect::Core* core, const Se
else
{
resCol->expr->initId(realColumn);
- if (!table.alias.isNull())
+ if (!table.tableAlias.isNull())
{
- resCol->expr->table = table.alias;
+ resCol->expr->table = table.tableAlias;
}
else
{
@@ -222,6 +237,6 @@ bool QueryExecutorAddRowIds::addResultColumns(SqliteSelect::Core* core, const Se
resCol->asKw = true;
resCol->alias = queryExecutorColumn;
- core->resultColumns.insert(0, resCol);
+ core->resultColumns << resCol;
return true;
}
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcellsize.cpp b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcellsize.cpp
index 934a20d..54bd35a 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcellsize.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcellsize.cpp
@@ -31,21 +31,21 @@ bool QueryExecutorCellSize::applyDataLimit(SqliteSelect* select, SqliteSelect::C
bool first = true;
TokenList tokens;
- foreach (const QueryExecutor::ResultRowIdColumnPtr& col, context->rowIdColumns)
+ foreach (const QueryExecutor::ResultColumnPtr& col, context->resultColumns)
{
if (!first)
tokens += getSeparatorTokens();
- tokens += getNoLimitTokens(col);
+ tokens += getLimitTokens(col);
first = false;
}
- foreach (const QueryExecutor::ResultColumnPtr& col, context->resultColumns)
+ foreach (const QueryExecutor::ResultRowIdColumnPtr& col, context->rowIdColumns)
{
if (!first)
tokens += getSeparatorTokens();
- tokens += getLimitTokens(col);
+ tokens += getNoLimitTokens(col);
first = false;
}
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumns.cpp b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumns.cpp
index 344f2e5..30a4f5d 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumns.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumns.cpp
@@ -48,6 +48,7 @@ bool QueryExecutorColumns::exec()
SqliteSelect::Core::ResultColumn* resultColumnForSelect = nullptr;
bool rowIdColumn = false;
int i = 0;
+ QSet<QString> usedAliases;
for (const SelectResolver::Column& col : columns)
{
// Convert column to QueryExecutor result column
@@ -58,7 +59,7 @@ bool QueryExecutorColumns::exec()
if (rowIdColumn && col.alias.contains(":"))
continue; // duplicate ROWID column provided by SelectResolver. See isRowIdColumn() for details.
- resultColumnForSelect = getResultColumnForSelect(resultColumn, col);
+ resultColumnForSelect = getResultColumnForSelect(resultColumn, col, usedAliases);
if (!resultColumnForSelect)
return false;
@@ -126,7 +127,7 @@ QueryExecutor::ResultColumnPtr QueryExecutorColumns::getResultColumn(const Selec
return resultColumn;
}
-SqliteSelect::Core::ResultColumn* QueryExecutorColumns::getResultColumnForSelect(const QueryExecutor::ResultColumnPtr& resultColumn, const SelectResolver::Column& col)
+SqliteSelect::Core::ResultColumn* QueryExecutorColumns::getResultColumnForSelect(const QueryExecutor::ResultColumnPtr& resultColumn, const SelectResolver::Column& col, QSet<QString> &usedAliases)
{
SqliteSelect::Core::ResultColumn* selectResultColumn = new SqliteSelect::Core::ResultColumn();
@@ -177,6 +178,15 @@ SqliteSelect::Core::ResultColumn* QueryExecutorColumns::getResultColumnForSelect
else
selectResultColumn->alias = resultColumn->queryExecutorAlias;
+ // If this alias was already used we need to use sequential alias
+ static_qstring(aliasTpl, "%1:%2");
+ int nextAliasCounter = 1;
+ QString aliasBase = selectResultColumn->alias;
+ while (usedAliases.contains(selectResultColumn->alias))
+ selectResultColumn->alias = aliasTpl.arg(aliasBase, QString::number(nextAliasCounter++));
+
+ usedAliases += selectResultColumn->alias;
+
return selectResultColumn;
}
@@ -206,18 +216,6 @@ void QueryExecutorColumns::wrapWithAliasedColumns(SqliteSelect* select)
bool first = true;
TokenList outerColumns;
- for (const QueryExecutor::ResultRowIdColumnPtr& rowIdColumn : context->rowIdColumns)
- {
- for (const QString& alias : rowIdColumn->queryExecutorAliasToColumn.keys())
- {
- if (!first)
- outerColumns += sepTokens;
-
- outerColumns << TokenPtr::create(Token::OTHER, alias);
- first = false;
- }
- }
-
QStringList columnNamesUsed;
QString baseColName;
QString colName;
@@ -251,6 +249,18 @@ void QueryExecutorColumns::wrapWithAliasedColumns(SqliteSelect* select)
first = false;
}
+ for (const QueryExecutor::ResultRowIdColumnPtr& rowIdColumn : context->rowIdColumns)
+ {
+ for (const QString& alias : rowIdColumn->queryExecutorAliasToColumn.keys())
+ {
+ if (!first)
+ outerColumns += sepTokens;
+
+ outerColumns << TokenPtr::create(Token::OTHER, alias);
+ first = false;
+ }
+ }
+
//QString t = outerColumns.detokenize(); // keeping it for debug purposes
select->tokens = wrapSelect(select->tokens, outerColumns);
}
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumns.h b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumns.h
index e23b9f6..8e1ebd2 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumns.h
+++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumns.h
@@ -50,7 +50,7 @@ class QueryExecutorColumns : public QueryExecutorStep
* @param rowIdColumn Indicates if this is a call for ROWID column added by QueryExecutorRowId step.
* @return Result column object ready for rebuilding tokens and detokenizing.
*/
- SqliteSelect::Core::ResultColumn* getResultColumnForSelect(const QueryExecutor::ResultColumnPtr& resultColumn, const SelectResolver::Column& col);
+ SqliteSelect::Core::ResultColumn* getResultColumnForSelect(const QueryExecutor::ResultColumnPtr& resultColumn, const SelectResolver::Column& col, QSet<QString>& usedAliases);
/**
* @brief Translates attach name into database name.
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutordatasources.cpp b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutordatasources.cpp
index 31cda9e..4a422d3 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutordatasources.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutordatasources.cpp
@@ -24,7 +24,7 @@ bool QueryExecutorDataSources::exec()
QueryExecutor::SourceTablePtr table = QueryExecutor::SourceTablePtr::create();
table->database = resolvedTable.database;
table->table = resolvedTable.table;
- table->alias = resolvedTable.alias;
+ table->alias = resolvedTable.tableAlias;
context->sourceTables << table;
}
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorexecute.cpp b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorexecute.cpp
index 3036796..aaa8014 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorexecute.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorexecute.cpp
@@ -3,13 +3,14 @@
#include "db/queryexecutor.h"
#include "parser/ast/sqlitequery.h"
#include "parser/lexer.h"
+#include "log.h"
#include "parser/ast/sqlitecreatetable.h"
#include "datatype.h"
+#include "schemaresolver.h"
+#include "common/table.h"
#include <QDateTime>
#include <QDebug>
#include <QStack>
-#include <schemaresolver.h>
-#include <common/table.h>
bool QueryExecutorExecute::exec()
{
@@ -58,8 +59,9 @@ bool QueryExecutorExecute::executeQueries()
if (isBeginTransaction(query->queryType))
rowsAffectedBeforeTransaction.push(context->rowsAffected);
-// qDebug() << "Executing query:" << queryStr;
+ //qDebug() << getLogDateTime() << "Executing query:" << queryStr;
results->execute();
+ //qDebug() << getLogDateTime() << "Done.";
if (results->isError())
{
@@ -155,7 +157,7 @@ void QueryExecutorExecute::setupSqlite2ColumnDataTypes(SqlQueryPtr results)
sqlite2Helper->clearBinaryTypes();
SqliteCreateTable::Column* column = nullptr;
- int idx = -1 + context->rowIdColumns.size();
+ int idx = -1;
for (QueryExecutor::ResultColumnPtr resCol : context->resultColumns)
{
idx++;
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorreplaceviews.cpp b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorreplaceviews.cpp
index 1f2e736..affe45c 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorreplaceviews.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorreplaceviews.cpp
@@ -91,6 +91,7 @@ void QueryExecutorReplaceViews::replaceViews(SqliteSelect* select)
}
src->select = view->select;
+ src->alias = view->view;
src->database = QString::null;
src->table = QString::null;