diff options
| author | 2015-01-30 17:00:07 -0500 | |
|---|---|---|
| committer | 2015-01-30 17:00:07 -0500 | |
| commit | 016003905ca0e8e459e3dc33e786beda8ec92f45 (patch) | |
| tree | 34aba2d2e0d66fbf2c3821ee8358f56e40c4c95e /SQLiteStudio3/coreSQLiteStudio/db | |
| parent | 724c012ada23ef480c61fe99e3c9784b91aeb1ca (diff) | |
Imported Upstream version 3.0.2upstream/3.0.2
Diffstat (limited to 'SQLiteStudio3/coreSQLiteStudio/db')
8 files changed, 118 insertions, 31 deletions
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp index 56275aa..4b3165b 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp @@ -305,7 +305,6 @@ SqlQueryPtr AbstractDb::execHashArg(const QString& query, const QHash<QString,QV if (!isOpenInternal()) return SqlQueryPtr(new SqlErrorResults(SqlErrorCode::DB_NOT_OPEN, tr("Cannot execute query on closed database."))); - logSql(this, query, args, flags); QString newQuery = query; SqlQueryPtr queryStmt = prepare(newQuery); queryStmt->setArgs(args); @@ -323,7 +322,6 @@ SqlQueryPtr AbstractDb::execListArg(const QString& query, const QList<QVariant>& if (!isOpenInternal()) return SqlQueryPtr(new SqlErrorResults(SqlErrorCode::DB_NOT_OPEN, tr("Cannot execute query on closed database."))); - logSql(this, query, args, flags); QString newQuery = query; SqlQueryPtr queryStmt = prepare(newQuery); queryStmt->setArgs(args); @@ -636,7 +634,13 @@ void AbstractDb::detachInternal(Db* otherDb) return; } - exec(QString("DETACH %1;").arg(attachedDbMap.valueByRight(otherDb)), Flag::NO_LOCK); + QString dbName = attachedDbMap.valueByRight(otherDb); + SqlQueryPtr res = exec(QString("DETACH %1;").arg(dbName), Flag::NO_LOCK); + if (res->isError()) + { + qCritical() << "Cannot detach" << dbName << " / " << otherDb->getName() << ":" << res->getErrorText(); + return; + } attachedDbMap.removeRight(otherDb); emit detached(otherDb); } diff --git a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb2.h b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb2.h index e35e038..c521bfa 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb2.h +++ b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb2.h @@ -7,6 +7,7 @@ #include "common/unused.h" #include "db/sqlerrorcodes.h" #include "db/sqlerrorresults.h" +#include "log.h" #include <sqlite.h> #include <QThread> #include <QPointer> @@ -195,7 +196,7 @@ bool AbstractDb2<T>::openInternal() if (errMsg) { - dbErrorMessage = tr("Could not open database: %1").arg(QString::fromUtf8(errMsg)); + dbErrorMessage = QObject::tr("Could not open database: %1").arg(QString::fromUtf8(errMsg)); sqlite_freemem(errMsg); } return false; @@ -570,6 +571,8 @@ bool AbstractDb2<T>::Query::execInternal(const QList<QVariant>& args) ReadWriteLocker locker(&(db->dbOperLock), query, Dialect::Sqlite2, flags.testFlag(Db::Flag::NO_LOCK)); + logSql(db.data(), query, args, flags); + QueryWithParamCount queryWithParams = getQueryWithParamCount(query, Dialect::Sqlite2); QString singleStr = replaceNamedParams(queryWithParams.first); @@ -604,6 +607,8 @@ bool AbstractDb2<T>::Query::execInternal(const QHash<QString, QVariant>& args) ReadWriteLocker locker(&(db->dbOperLock), query, Dialect::Sqlite2, flags.testFlag(Db::Flag::NO_LOCK)); + logSql(db.data(), query, args, flags); + QueryWithParamNames queryWithParams = getQueryWithParamNames(query, Dialect::Sqlite2); QString singleStr = replaceNamedParams(queryWithParams.first); @@ -788,7 +793,7 @@ int AbstractDb2<T>::Query::fetchNext() if (!rowAvailable || !stmt) { - setError(SQLITE_MISUSE, tr("Result set expired or no row available.")); + setError(SQLITE_MISUSE, QObject::tr("Result set expired or no row available.")); return SQLITE_MISUSE; } diff --git a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb3.h b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb3.h index 89c3d96..7a84ec2 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb3.h +++ b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb3.h @@ -8,6 +8,7 @@ #include "services/collationmanager.h" #include "sqlitestudio.h" #include "db/sqlerrorcodes.h" +#include "log.h" #include <QThread> #include <QPointer> #include <QDebug> @@ -352,7 +353,7 @@ bool AbstractDb3<T>::openInternal() if (handle) T::close(handle); - dbErrorMessage = tr("Could not open database: %1").arg(extractLastError()); + dbErrorMessage = QObject::tr("Could not open database: %1").arg(extractLastError()); dbErrorCode = res; return false; } @@ -372,7 +373,7 @@ bool AbstractDb3<T>::closeInternal() int res = T::close(dbHandle); if (res != T::OK) { - dbErrorMessage = tr("Could not close database: %1").arg(extractLastError()); + dbErrorMessage = QObject::tr("Could not close database: %1").arg(extractLastError()); dbErrorCode = res; qWarning() << "Error closing database. That's weird:" << dbErrorMessage; return false; @@ -854,6 +855,8 @@ bool AbstractDb3<T>::Query::execInternal(const QList<QVariant>& args) return false; ReadWriteLocker locker(&(db->dbOperLock), query, Dialect::Sqlite3, flags.testFlag(Db::Flag::NO_LOCK)); + logSql(db.data(), query, args, flags); + QueryWithParamCount queryWithParams = getQueryWithParamCount(query, Dialect::Sqlite3); int res; @@ -890,6 +893,7 @@ bool AbstractDb3<T>::Query::execInternal(const QHash<QString, QVariant>& args) return false; ReadWriteLocker locker(&(db->dbOperLock), query, Dialect::Sqlite3, flags.testFlag(Db::Flag::NO_LOCK)); + logSql(db.data(), query, args, flags); QueryWithParamNames queryWithParams = getQueryWithParamNames(query, Dialect::Sqlite3); @@ -1075,7 +1079,7 @@ int AbstractDb3<T>::Query::fetchNext() if (!rowAvailable || !stmt) { - setError(T::MISUSE, tr("Result set expired or no row available.")); + setError(T::MISUSE, QObject::tr("Result set expired or no row available.")); return T::MISUSE; } diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.cpp b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.cpp index c840947..97b3f1d 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.cpp @@ -39,7 +39,6 @@ QueryExecutor::QueryExecutor(Db* db, const QString& query, QObject *parent) : setDb(db); setAutoDelete(false); - connect(this, SIGNAL(executionFinished(SqlQueryPtr)), this, SLOT(cleanupAfterExecFinished(SqlQueryPtr))); connect(this, SIGNAL(executionFailed(int,QString)), this, SLOT(cleanupAfterExecFailed(int,QString))); connect(DBLIST, SIGNAL(dbAboutToBeUnloaded(Db*, DbPlugin*)), this, SLOT(cleanupBeforeDbDestroy(Db*, DbPlugin*))); } @@ -138,12 +137,6 @@ void QueryExecutor::stepFailed(QueryExecutorStep* currentStep) executeSimpleMethod(); } -void QueryExecutor::cleanupAfterExecFinished(SqlQueryPtr results) -{ - UNUSED(results); - cleanup(); -} - void QueryExecutor::cleanupAfterExecFailed(int code, QString errorMessage) { UNUSED(code); @@ -213,6 +206,7 @@ void QueryExecutor::execInternal() { resultsCountingAsyncId = 0; db->interrupt(); + releaseResultsAndCleanup(); } // Reset context @@ -242,13 +236,13 @@ void QueryExecutor::interrupt() db->asyncInterrupt(); } -void QueryExecutor::countResults() +bool QueryExecutor::countResults() { if (context->skipRowCounting) - return; + return false; if (context->countingQuery.isEmpty()) // simple method doesn't provide that - return; + return false; if (asyncMode) { @@ -267,8 +261,10 @@ void QueryExecutor::countResults() { notifyError(tr("An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1") .arg(results->getErrorText())); + return false; } } + return true; } qint64 QueryExecutor::getLastExecutionTime() const @@ -416,7 +412,7 @@ void QueryExecutor::simpleExecutionFinished(SqlQueryPtr results) executionMutex.lock(); executionInProgress = false; executionMutex.unlock(); - error(results->getErrorCode(), results->getErrorText()); + handleErrorsFromSmartAndSimpleMethods(results); return; } @@ -552,6 +548,44 @@ void QueryExecutor::setNoMetaColumns(bool value) noMetaColumns = value; } +void QueryExecutor::handleErrorsFromSmartAndSimpleMethods(SqlQueryPtr results) +{ + QString simpleText = results->getErrorText(); + + // Smart text may contain messages from steps before the actual execution, but they will have negative error code. + // Positive error code means that the error came directly from SQLite. + QString smartText = context->errorCodeFromSmartExecution > 0 ? context->errorMessageFromSmartExecution : QString(); + + if (simpleText.contains("no such") && smartText.contains("no such")) + { + // This happens if user refers to invalid column in attached database. + // Smart execution will tell "no such column: xxx", while simple method will tell: + // "no such table: attach.table". In that case we're more interested in smart method message. + // This also applies to views. + error(context->errorCodeFromSmartExecution, smartText); + return; + } + + if (simpleText.contains("no such") && smartText.contains("ambiguous")) + { + // This happens when smart execution raised "amigous column name" or something like that, + // but simple method failed to work because of transparent database attaching. We prefer smart method error. + error(context->errorCodeFromSmartExecution, smartText); + return; + } + + // No special case, use simple method error + error(results->getErrorCode(), simpleText); +} + +void QueryExecutor::releaseResultsAndCleanup() +{ + // The results have to be releases, otherwise attached databases cannot be detached. + // Results handle cannot be kept elsewhere, otherwise detach will fail. + context->executionResults.clear(); + cleanup(); +} + SqlQueryPtr QueryExecutor::getResults() const { return context->executionResults; @@ -562,6 +596,11 @@ bool QueryExecutor::wasSchemaModified() const return context->schemaModified; } +bool QueryExecutor::wasDataModifyingQuery() const +{ + return context->dataModifyingQuery; +} + QList<DataType> QueryExecutor::resolveColumnTypes(Db* db, QList<QueryExecutor::ResultColumnPtr>& columns, bool noDbLocking) { QSet<Table> tables; diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.h b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.h index c4a3e4d..72c7fed 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.h +++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.h @@ -604,11 +604,36 @@ class API_EXPORT QueryExecutor : public QObject, public QRunnable bool schemaModified = false; /** + * @brief Tells if executed query was one of DELETE, UPDATE or INSERT. + */ + bool dataModifyingQuery = false; + + /** * @brief Forbids QueryExecutor to return meta columns. * * See QueryExecutor::noMetaColumns for details. */ bool noMetaColumns = false; + + /** + * @brief Contains error code from smart execution. + * + * This is always set by the smart execution method. It's useful if the smart execution + * failed and the simple execution method is being performed. In that case the query + * result will contain error code from simple execution, hiding the original error code + * from smart execution. + */ + int errorCodeFromSmartExecution = 0; + + /** + * @brief Contains error message from smart execution. + * + * This is always set by the smart execution method. It's useful if the smart execution + * failed and the simple execution method is being performed. In that case the query + * result will contain error message from simple execution, hiding the original error + * message from smart execution. + */ + QString errorMessageFromSmartExecution; }; /** @@ -654,6 +679,7 @@ class API_EXPORT QueryExecutor : public QObject, public QRunnable /** * @brief Executes counting query. + * @return true if counting query is executed (in async mode) or was executed correctly (in sync mode), false on error. * * Executes (asynchronously) counting query for currently defined query. After execution is done, the resultsCountingFinished() * signal is emitted. @@ -661,8 +687,10 @@ class API_EXPORT QueryExecutor : public QObject, public QRunnable * Counting query is made of original query wrapped with "SELECT count(*) FROM (original_query)". * * It is executed after the main query execution has finished. + * + * If query is being executed in async mode, the true result (sucess/fail) will be known from later, not from this method. */ - void countResults(); + bool countResults(); /** * @brief Gets time of how long it took to execute query. @@ -976,12 +1004,20 @@ class API_EXPORT QueryExecutor : public QObject, public QRunnable SqlQueryPtr getResults() const; bool wasSchemaModified() const; + bool wasDataModifyingQuery() const; static QList<DataType> resolveColumnTypes(Db* db, QList<ResultColumnPtr>& columns, bool noDbLocking = false); bool getNoMetaColumns() const; void setNoMetaColumns(bool value); + void handleErrorsFromSmartAndSimpleMethods(SqlQueryPtr results); + + /** + * @brief Clears results handle and detaches any attached databases. + */ + void releaseResultsAndCleanup(); + private: /** * @brief Executes query. @@ -1335,14 +1371,6 @@ class API_EXPORT QueryExecutor : public QObject, public QRunnable void stepFailed(QueryExecutorStep *currentStep); /** - * @brief Cleanup routines after successful query execution. - * @param results Query results. - * - * Releases resources that are no longer used. Currently simply calls cleanup(). - */ - void cleanupAfterExecFinished(SqlQueryPtr results); - - /** * @brief Cleanup routines after failed query execution. * @param code Error code. * @param errorMessage Error message. diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutordetectschemaalter.cpp b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutordetectschemaalter.cpp index c3c8a5c..02a73d2 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutordetectschemaalter.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutordetectschemaalter.cpp @@ -18,6 +18,10 @@ bool QueryExecutorDetectSchemaAlter::exec() case SqliteQueryType::CreateVirtualTable: context->schemaModified = true; break; + case SqliteQueryType::Insert: + case SqliteQueryType::Delete: + case SqliteQueryType::Update: + context->dataModifyingQuery = true; default: break; } diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorexecute.cpp b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorexecute.cpp index 7e0abe5..f42f647 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorexecute.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorexecute.cpp @@ -11,7 +11,7 @@ bool QueryExecutorExecute::exec() { - qDebug() << "q:" << context->processedQuery; +// qDebug() << "q:" << context->processedQuery; startTime = QDateTime::currentMSecsSinceEpoch(); return executeQueries(); @@ -32,6 +32,7 @@ bool QueryExecutorExecute::executeQueries() { QHash<QString, QVariant> bindParamsForQuery; SqlQueryPtr results; + context->rowsAffected = 0; Db::Flags flags; if (context->preloadResults) @@ -56,6 +57,8 @@ bool QueryExecutorExecute::executeQueries() handleFailResult(results); return false; } + + context->rowsAffected += results->rowsAffected(); } handleSuccessfulResult(results); return true; @@ -72,7 +75,6 @@ void QueryExecutorExecute::handleSuccessfulResult(SqlQueryPtr results) } context->executionTime = QDateTime::currentMSecsSinceEpoch() - startTime; - context->rowsAffected = results->rowsAffected(); // For PRAGMA and EXPLAIN we simply count results for rows returned SqliteQueryPtr lastQuery = context->parsedQueries.last(); @@ -92,6 +94,8 @@ void QueryExecutorExecute::handleFailResult(SqlQueryPtr results) { if (!results->isInterrupted()) { + context->errorCodeFromSmartExecution = results->getErrorCode(); + context->errorMessageFromSmartExecution = results->getErrorText(); qWarning() << "Could not execute query with smart method:" << queryExecutor->getOriginalQuery() << "\nError message:" << results->getErrorText() << "\nSkipping smart execution."; diff --git a/SQLiteStudio3/coreSQLiteStudio/db/sqlquery.h b/SQLiteStudio3/coreSQLiteStudio/db/sqlquery.h index a7610fa..4e0d9cf 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/sqlquery.h +++ b/SQLiteStudio3/coreSQLiteStudio/db/sqlquery.h @@ -133,7 +133,6 @@ class API_EXPORT SqlQuery * @brief Gets number of rows that were affected by the query. * @return Number of rows affected. * - * For SELECT statements this is number of returned rows. * For UPDATE this is number of rows updated. * For DELETE this is number of rows deleted. * FOR INSERT this is number of rows inserted (starting with SQLite 3.7.11 you can insert multiple rows with single INSERT statement). |
