diff options
| author | 2017-02-09 04:36:04 -0500 | |
|---|---|---|
| committer | 2017-02-09 04:36:04 -0500 | |
| commit | d9aa870e5d509cc7309ab82dd102a937ab58613a (patch) | |
| tree | d92d03d76b5c390b335f1cfd761f1a0b59ec8496 /SQLiteStudio3/coreSQLiteStudio/db | |
| parent | 68ee4cbcbe424b95969c70346283a9f217f63825 (diff) | |
Imported Upstream version 3.1.1+dfsg1upstream/3.1.1+dfsg1
Diffstat (limited to 'SQLiteStudio3/coreSQLiteStudio/db')
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; |
