aboutsummaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/coreSQLiteStudio/db
diff options
context:
space:
mode:
Diffstat (limited to 'SQLiteStudio3/coreSQLiteStudio/db')
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp8
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/abstractdb.h8
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/abstractdb3.h18
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/db.cpp8
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/db.h10
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/dbpluginoption.h13
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/invaliddb.cpp8
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/invaliddb.h8
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumns.cpp90
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumns.h6
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorexecute.cpp9
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/stdsqlite3driver.h1
12 files changed, 97 insertions, 90 deletions
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp
index 4b3165b..68c6ad7 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp
@@ -177,22 +177,22 @@ ReadWriteLocker::Mode AbstractDb::getLockingMode(const QString &query, Flags fla
return ReadWriteLocker::getMode(query, getDialect(), flags.testFlag(Flag::NO_LOCK));
}
-QString AbstractDb::getName()
+QString AbstractDb::getName() const
{
return name;
}
-QString AbstractDb::getPath()
+QString AbstractDb::getPath() const
{
return path;
}
-quint8 AbstractDb::getVersion()
+quint8 AbstractDb::getVersion() const
{
return version;
}
-Dialect AbstractDb::getDialect()
+Dialect AbstractDb::getDialect() const
{
if (version == 2)
return Dialect::Sqlite2;
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.h b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.h
index 89edf03..3f2b4c0 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.h
+++ b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.h
@@ -45,10 +45,10 @@ class API_EXPORT AbstractDb : public Db
virtual ~AbstractDb();
bool isOpen();
- QString getName();
- QString getPath();
- quint8 getVersion();
- Dialect getDialect();
+ QString getName() const;
+ QString getPath() const;
+ quint8 getVersion() const;
+ Dialect getDialect() const;
QString getEncoding();
QHash<QString,QVariant>& getConnectionOptions();
void setName(const QString& value);
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb3.h b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb3.h
index 7a84ec2..9cb58aa 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb3.h
+++ b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb3.h
@@ -868,7 +868,8 @@ bool AbstractDb3<T>::Query::execInternal(const QList<QVariant>& args)
if (res != T::OK)
return false;
- for (int paramIdx = 1; paramIdx <= queryWithParams.second; paramIdx++)
+
+ for (int paramIdx = 1, argCount = args.size(); paramIdx <= queryWithParams.second && paramIdx <= argCount; paramIdx++)
{
res = bindParam(paramIdx, args[paramIdx-1]);
if (res != T::OK)
@@ -906,16 +907,25 @@ bool AbstractDb3<T>::Query::execInternal(const QHash<QString, QVariant>& args)
if (res != T::OK)
return false;
- int paramIdx = 1;
- foreach (const QString& paramName, queryWithParams.second)
+ int paramIdx = -1;
+ for (const QString& paramName : queryWithParams.second)
{
if (!args.contains(paramName))
{
+ qWarning() << "Could not bind parameter" << paramName << "because it was not found in passed arguments.";
+ setError(SqlErrorCode::OTHER_EXECUTION_ERROR, "Error while preparing statement: could not bind parameter " + paramName);
+ return false;
+ }
+
+ paramIdx = T::bind_parameter_index(stmt, paramName.toUtf8().constData());
+ if (paramIdx <= 0)
+ {
+ qWarning() << "Could not bind parameter" << paramName << "because it was not found in prepared statement.";
setError(SqlErrorCode::OTHER_EXECUTION_ERROR, "Error while preparing statement: could not bind parameter " + paramName);
return false;
}
- res = bindParam(paramIdx++, args[paramName]);
+ res = bindParam(paramIdx, args[paramName]);
if (res != T::OK)
{
db->extractLastError();
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/db.cpp b/SQLiteStudio3/coreSQLiteStudio/db/db.cpp
index c89349c..7676f30 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/db.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/db/db.cpp
@@ -1,5 +1,6 @@
#include "db.h"
#include <QMetaEnum>
+#include <QDebug>
Db::Db()
{
@@ -55,3 +56,10 @@ void Sqlite2ColumnDataTypeHelper::clearBinaryTypes()
{
binaryColumns.clear();
}
+
+
+QDebug operator<<(QDebug dbg, const Db* db)
+{
+ dbg.nospace() << "<DB:" << db->getName() << ">";
+ return dbg.space();
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/db.h b/SQLiteStudio3/coreSQLiteStudio/db/db.h
index e11a844..efadd41 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/db.h
+++ b/SQLiteStudio3/coreSQLiteStudio/db/db.h
@@ -195,13 +195,13 @@ class API_EXPORT Db : public QObject, public Interruptable
* @brief Gets database symbolic name.
* @return Database symbolic name (as it was defined in call to DbManager#addDb() or DbManager#updateDb()).
*/
- virtual QString getName() = 0;
+ virtual QString getName() const = 0;
/**
* @brief Gets database file path.
* @return Database file path (as it was defined in call to DbManager#addDb() or DbManager#updateDb()).
*/
- virtual QString getPath() = 0;
+ virtual QString getPath() const = 0;
/**
* @brief Gets SQLite version major number for this database.
@@ -209,7 +209,7 @@ class API_EXPORT Db : public QObject, public Interruptable
*
* You don't have to open the database. This information is always available.
*/
- virtual quint8 getVersion() = 0;
+ virtual quint8 getVersion() const = 0;
/**
* @brief Gets database dialect.
@@ -217,7 +217,7 @@ class API_EXPORT Db : public QObject, public Interruptable
*
* You don't have to open the database. This information is always available.
*/
- virtual Dialect getDialect() = 0;
+ virtual Dialect getDialect() const = 0;
/**
* @brief Gets database encoding.
@@ -816,6 +816,8 @@ class API_EXPORT Db : public QObject, public Interruptable
QDataStream &operator<<(QDataStream &out, const Db* myObj);
QDataStream &operator>>(QDataStream &in, Db*& myObj);
+QDebug operator<<(QDebug dbg, const Db* db);
+
Q_DECLARE_METATYPE(Db*)
Q_DECLARE_OPERATORS_FOR_FLAGS(Db::Flags)
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/dbpluginoption.h b/SQLiteStudio3/coreSQLiteStudio/db/dbpluginoption.h
index 7b594ef..e2ed66f 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/dbpluginoption.h
+++ b/SQLiteStudio3/coreSQLiteStudio/db/dbpluginoption.h
@@ -27,6 +27,11 @@
struct DbPluginOption
{
/**
+ * @brief Handler for custom path browser implemented by the plugin.
+ */
+ typedef std::function<QString(const QString&)> CustomBrowseHandler;
+
+ /**
* @brief Option data type
*
* Determinates what kind of input widget will be added to DbDialog.
@@ -39,7 +44,8 @@ struct DbPluginOption
DOUBLE = 3, /**< QDoubleSpinBox will be added */
FILE = 4, /**< QLineEdit will be added */
PASSWORD = 5, /**< QLineEdit with value masking will be added */
- CHOICE = 6 /**< QComboBox will be added */
+ CHOICE = 6, /**< QComboBox will be added */
+ CUSTOM_PATH_BROWSE = 7 /**< File path browse button will be handled by the plugin. No additional editor will be added. */
};
/**
@@ -92,6 +98,11 @@ struct DbPluginOption
* @brief Expected data type for the option.
*/
Type type;
+
+ /**
+ * @brief Handler for custom path browser implemented by the plugin.
+ */
+ CustomBrowseHandler customBrowseHandler = nullptr;
};
#endif // DBPLUGINOPTION_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/invaliddb.cpp b/SQLiteStudio3/coreSQLiteStudio/db/invaliddb.cpp
index e4810a1..bdf3ef6 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/invaliddb.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/db/invaliddb.cpp
@@ -12,22 +12,22 @@ bool InvalidDb::isOpen()
return false;
}
-QString InvalidDb::getName()
+QString InvalidDb::getName() const
{
return name;
}
-QString InvalidDb::getPath()
+QString InvalidDb::getPath() const
{
return path;
}
-quint8 InvalidDb::getVersion()
+quint8 InvalidDb::getVersion() const
{
return 0;
}
-Dialect InvalidDb::getDialect()
+Dialect InvalidDb::getDialect() const
{
return Dialect::Sqlite3;
}
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/invaliddb.h b/SQLiteStudio3/coreSQLiteStudio/db/invaliddb.h
index 759aa4c..a9d58e0 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/invaliddb.h
+++ b/SQLiteStudio3/coreSQLiteStudio/db/invaliddb.h
@@ -9,10 +9,10 @@ class API_EXPORT InvalidDb : public Db
InvalidDb(const QString& name, const QString& path, const QHash<QString, QVariant>& connOptions);
bool isOpen();
- QString getName();
- QString getPath();
- quint8 getVersion();
- Dialect getDialect();
+ QString getName() const;
+ QString getPath() const;
+ quint8 getVersion() const;
+ Dialect getDialect() const;
QString getEncoding();
QHash<QString, QVariant>& getConnectionOptions();
void setName(const QString& value);
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumns.cpp b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumns.cpp
index 6acfb6f..287a8ce 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumns.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumns.cpp
@@ -40,14 +40,13 @@ bool QueryExecutorColumns::exec()
core->resultColumns.clear();
// Count total rowId columns
- int rowIdColCount = 0;
for (const QueryExecutor::ResultRowIdColumnPtr& rowIdCol : context->rowIdColumns)
- rowIdColCount += rowIdCol->queryExecutorAliasToColumn.size();
+ rowIdColNames += rowIdCol->queryExecutorAliasToColumn.keys();
// Defining result columns
QueryExecutor::ResultColumnPtr resultColumn;
SqliteSelect::Core::ResultColumn* resultColumnForSelect = nullptr;
- bool isRowIdColumn = false;
+ bool rowIdColumn = false;
int i = 0;
for (const SelectResolver::Column& col : columns)
{
@@ -55,15 +54,18 @@ bool QueryExecutorColumns::exec()
resultColumn = getResultColumn(col);
// Adding new result column to the query
- isRowIdColumn = (i < rowIdColCount);
- resultColumnForSelect = getResultColumnForSelect(resultColumn, col, isRowIdColumn);
+ rowIdColumn = isRowIdColumn(col.alias);
+ if (rowIdColumn && col.alias.contains(":"))
+ continue; // duplicate ROWID column provided by SelectResolver. See isRowIdColumn() for details.
+
+ resultColumnForSelect = getResultColumnForSelect(resultColumn, col);
if (!resultColumnForSelect)
return false;
resultColumnForSelect->setParent(core);
core->resultColumns << resultColumnForSelect;
- if (!isRowIdColumn)
+ if (!rowIdColumn)
context->resultColumns << resultColumn; // store it in context for later usage by any step
i++;
@@ -89,7 +91,6 @@ QueryExecutor::ResultColumnPtr QueryExecutorColumns::getResultColumn(const Selec
resultColumn->column = resolvedColumn.column;
resultColumn->alias = resolvedColumn.alias;
resultColumn->expression = true;
- resultColumn->queryExecutorAlias = getNextColName();
}
else
{
@@ -111,20 +112,20 @@ QueryExecutor::ResultColumnPtr QueryExecutorColumns::getResultColumn(const Selec
resultColumn->tableAlias = resolvedColumn.tableAlias;
resultColumn->alias = resolvedColumn.alias;
resultColumn->displayName = resolvedColumn.displayName;
+ }
- if (isRowIdColumnAlias(resultColumn->alias))
- {
- resultColumn->queryExecutorAlias = resultColumn->alias;
- }
- else
- {
- resultColumn->queryExecutorAlias = getNextColName();
- }
+ if (isRowIdColumnAlias(resultColumn->alias))
+ {
+ resultColumn->queryExecutorAlias = resultColumn->alias;
+ }
+ else
+ {
+ resultColumn->queryExecutorAlias = getNextColName();
}
return resultColumn;
}
-SqliteSelect::Core::ResultColumn* QueryExecutorColumns::getResultColumnForSelect(const QueryExecutor::ResultColumnPtr& resultColumn, const SelectResolver::Column& col, bool rowIdColumn)
+SqliteSelect::Core::ResultColumn* QueryExecutorColumns::getResultColumnForSelect(const QueryExecutor::ResultColumnPtr& resultColumn, const SelectResolver::Column& col)
{
SqliteSelect::Core::ResultColumn* selectResultColumn = new SqliteSelect::Core::ResultColumn();
@@ -164,28 +165,6 @@ SqliteSelect::Core::ResultColumn* QueryExecutorColumns::getResultColumnForSelect
selectResultColumn->expr->table = resultColumn->table;
}
-
-// // SQLite2 requires special treatment here. It won't allow selecting db.table.col from a subquery - it needs escaped ID that reflects db.table.col.
-// if (dialect == Dialect::Sqlite2)
-// {
-// selectResultColumn->expr->rebuildTokens();
-// colString = wrapObjIfNeeded(selectResultColumn->expr->detokenize(), dialect);
-// delete selectResultColumn->expr;
-// selectResultColumn->expr = nullptr;
-
-// expr = parser.parseExpr(colString);
-// if (!expr)
-// {
-// qWarning() << "Could not parse result column expr in SQLite2's second parsing phase:" << colString;
-// if (parser.getErrors().size() > 0)
-// qWarning() << "The error was:" << parser.getErrors().first()->getFrom() << ":" << parser.getErrors().first()->getMessage();
-
-// return nullptr;
-// }
-
-// expr->setParent(selectResultColumn);
-// selectResultColumn->expr = expr;
-// }
}
if (!col.alias.isNull())
@@ -193,7 +172,7 @@ SqliteSelect::Core::ResultColumn* QueryExecutorColumns::getResultColumnForSelect
selectResultColumn->asKw = true;
selectResultColumn->alias = col.alias;
}
- else if (rowIdColumn || resultColumn->expression)
+ else
{
selectResultColumn->asKw = true;
selectResultColumn->alias = resultColumn->queryExecutorAlias;
@@ -252,12 +231,10 @@ void QueryExecutorColumns::wrapWithAliasedColumns(SqliteSelect* select)
// If alias was given, we use it. If it was anything but expression, we also use its display name,
// because it's explicit column (no matter if from table, or table alias).
baseColName = QString();
- if (!resCol->alias.isNull())
+ if (!resCol->queryExecutorAlias.isNull())
baseColName = resCol->alias;
- else if (dialect == Dialect::Sqlite3 && !resCol->expression)
+ else if (!resCol->expression)
baseColName = resCol->column;
- else if (dialect == Dialect::Sqlite2 && !resCol->expression)
- baseColName = getAliasedColumnNameForSqlite2(resCol);
if (!baseColName.isNull())
{
@@ -279,20 +256,17 @@ void QueryExecutorColumns::wrapWithAliasedColumns(SqliteSelect* select)
select->tokens = wrapSelect(select->tokens, outerColumns);
}
-QString QueryExecutorColumns::getAliasedColumnNameForSqlite2(const QueryExecutor::ResultColumnPtr& resCol)
+bool QueryExecutorColumns::isRowIdColumn(const QString& columnAlias)
{
- QStringList colNameParts;
- if (!resCol->table.isNull())
- {
- if (!resCol->database.isNull())
- {
- if (context->dbNameToAttach.containsLeft(resCol->database, Qt::CaseInsensitive))
- colNameParts << context->dbNameToAttach.valueByLeft(resCol->database, Qt::CaseInsensitive);
- else
- colNameParts << wrapObjIfNeeded(resCol->database, dialect);
- }
- colNameParts << wrapObjIfNeeded(resCol->table, dialect);
- }
- colNameParts << wrapObjIfNeeded(resCol->column, dialect);
- return colNameParts.join(".");
+ // In case of "SELECT * FROM (SELECT * FROM test);" the SelectResolver will return ROWID columns twice for each table listed,
+ // because ROWID columns are recurrently handled by QueryExecutorAddRowIds step. We need to identify such columns and make them unique
+ // in the final query.
+ // Currently all columns have QueryExecutor aliased names, so we can assume they have unified alias name in form ResCol_N.
+ // If SelectResolver returns any column like ResCol_N:X, then it means that the column is result of the query like above.
+ // Note, that this assumption is correct for RowId columns. There can be columns aliased by user and those aliases won't be unified.
+ QString aliasOnly = columnAlias;
+ if (aliasOnly.contains(":"))
+ aliasOnly = aliasOnly.left(aliasOnly.indexOf(":"));
+
+ return rowIdColNames.contains(aliasOnly);
}
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumns.h b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumns.h
index fd85651..e23b9f6 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, bool rowIdColumn);
+ SqliteSelect::Core::ResultColumn* getResultColumnForSelect(const QueryExecutor::ResultColumnPtr& resultColumn, const SelectResolver::Column& col);
/**
* @brief Translates attach name into database name.
@@ -67,8 +67,8 @@ class QueryExecutorColumns : public QueryExecutorStep
bool isRowIdColumnAlias(const QString& alias);
void wrapWithAliasedColumns(SqliteSelect* select);
-
- QString getAliasedColumnNameForSqlite2(const QueryExecutor::ResultColumnPtr& resCol);
+ bool isRowIdColumn(const QString& columnAlias);
+ QStringList rowIdColNames;
};
#endif // QUERYEXECUTORCOLUMNS_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorexecute.cpp b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorexecute.cpp
index f42f647..df2ed68 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorexecute.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorexecute.cpp
@@ -8,6 +8,7 @@
#include <QDateTime>
#include <QDebug>
#include <schemaresolver.h>
+#include <common/table.h>
bool QueryExecutorExecute::exec()
{
@@ -120,17 +121,17 @@ void QueryExecutorExecute::setupSqlite2ColumnDataTypes(SqlQueryPtr results)
if (!sqlite2Helper)
return;
- QPair<QString,QString> key;
+ Table key;
SqliteCreateTablePtr createTable;
SchemaResolver resolver(db);
- QHash<QPair<QString,QString>,SqliteCreateTablePtr> tables;
+ QHash<Table,SqliteCreateTablePtr> tables;
for (QueryExecutor::SourceTablePtr tab : context->sourceTables)
{
if (tab->table.isNull())
continue;
- key = QPair<QString,QString>(tab->database, tab->table);
+ key = Table(tab->database, tab->table);
createTable = resolver.getParsedObject(tab->database, tab->table, SchemaResolver::TABLE).dynamicCast<SqliteCreateTable>();
tables[key] = createTable;
}
@@ -142,7 +143,7 @@ void QueryExecutorExecute::setupSqlite2ColumnDataTypes(SqlQueryPtr results)
for (QueryExecutor::ResultColumnPtr resCol : context->resultColumns)
{
idx++;
- key = QPair<QString,QString>(resCol->database, resCol->table);
+ key = Table(resCol->database, resCol->table);
if (!tables.contains(key))
continue;
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/stdsqlite3driver.h b/SQLiteStudio3/coreSQLiteStudio/db/stdsqlite3driver.h
index eff3621..2f26aaf 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/stdsqlite3driver.h
+++ b/SQLiteStudio3/coreSQLiteStudio/db/stdsqlite3driver.h
@@ -41,6 +41,7 @@
static int bind_int(stmt* a1, int a2, int a3) {return Prefix##sqlite3_bind_int(a1, a2, a3);} \
static int bind_int64(stmt* a1, int a2, int64 a3) {return Prefix##sqlite3_bind_int64(a1, a2, a3);} \
static int bind_null(stmt* a1, int a2) {return Prefix##sqlite3_bind_null(a1, a2);} \
+ static int bind_parameter_index(stmt* a1, const char* a2) {return Prefix##sqlite3_bind_parameter_index(a1, a2);} \
static int bind_text16(stmt* a1, int a2, const void* a3, int a4, void(*a5)(void*)) {return Prefix##sqlite3_bind_text16(a1, a2, a3, a4, a5);} \
static void result_blob(context* a1, const void* a2, int a3, void(*a4)(void*)) {Prefix##sqlite3_result_blob(a1, a2, a3, a4);} \
static void result_double(context* a1, double a2) {Prefix##sqlite3_result_double(a1, a2);} \