aboutsummaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/coreSQLiteStudio/db
diff options
context:
space:
mode:
authorLibravatarUnit 193 <unit193@ubuntu.com>2015-01-30 17:00:07 -0500
committerLibravatarUnit 193 <unit193@ubuntu.com>2015-01-30 17:00:07 -0500
commit016003905ca0e8e459e3dc33e786beda8ec92f45 (patch)
tree34aba2d2e0d66fbf2c3821ee8358f56e40c4c95e /SQLiteStudio3/coreSQLiteStudio/db
parent724c012ada23ef480c61fe99e3c9784b91aeb1ca (diff)
Imported Upstream version 3.0.2upstream/3.0.2
Diffstat (limited to 'SQLiteStudio3/coreSQLiteStudio/db')
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp10
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/abstractdb2.h9
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/abstractdb3.h10
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.cpp61
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.h46
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutordetectschemaalter.cpp4
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorexecute.cpp8
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/sqlquery.h1
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).