summaryrefslogtreecommitdiffstats
path: root/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp')
-rw-r--r--src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp403
1 files changed, 236 insertions, 167 deletions
diff --git a/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp b/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp
index 4f70728d73..c574772fd7 100644
--- a/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp
+++ b/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtSql module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsql_sqlite_p.h"
@@ -43,6 +7,7 @@
#include <qdatetime.h>
#include <qdebug.h>
#include <qlist.h>
+#include <qloggingcategory.h>
#include <qsqlerror.h>
#include <qsqlfield.h>
#include <qsqlindex.h>
@@ -74,33 +39,24 @@ Q_DECLARE_METATYPE(sqlite3_stmt*)
QT_BEGIN_NAMESPACE
-static QString _q_escapeIdentifier(const QString &identifier)
-{
- QString res = identifier;
- if (!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"'))) {
- res.replace(QLatin1Char('"'), QLatin1String("\"\""));
- res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
- res.replace(QLatin1Char('.'), QLatin1String("\".\""));
- }
- return res;
-}
+static Q_LOGGING_CATEGORY(lcSqlite, "qt.sql.sqlite")
+
+using namespace Qt::StringLiterals;
static int qGetColumnType(const QString &tpName)
{
const QString typeName = tpName.toLower();
- if (typeName == QLatin1String("integer")
- || typeName == QLatin1String("int"))
+ if (typeName == "integer"_L1 || typeName == "int"_L1)
return QMetaType::Int;
- if (typeName == QLatin1String("double")
- || typeName == QLatin1String("float")
- || typeName == QLatin1String("real")
- || typeName.startsWith(QLatin1String("numeric")))
+ if (typeName == "double"_L1
+ || typeName == "float"_L1
+ || typeName == "real"_L1
+ || typeName.startsWith("numeric"_L1))
return QMetaType::Double;
- if (typeName == QLatin1String("blob"))
+ if (typeName == "blob"_L1)
return QMetaType::QByteArray;
- if (typeName == QLatin1String("boolean")
- || typeName == QLatin1String("bool"))
+ if (typeName == "boolean"_L1 || typeName == "bool"_L1)
return QMetaType::Bool;
return QMetaType::QString;
}
@@ -145,11 +101,64 @@ class QSQLiteDriverPrivate : public QSqlDriverPrivate
public:
inline QSQLiteDriverPrivate() : QSqlDriverPrivate(QSqlDriver::SQLite) {}
+ bool isIdentifierEscaped(QStringView identifier) const;
+ QSqlIndex getTableInfo(QSqlQuery &query, const QString &tableName,
+ bool onlyPIndex = false) const;
+
sqlite3 *access = nullptr;
QList<QSQLiteResult *> results;
QStringList notificationid;
};
+bool QSQLiteDriverPrivate::isIdentifierEscaped(QStringView identifier) const
+{
+ return identifier.size() > 2
+ && ((identifier.startsWith(u'"') && identifier.endsWith(u'"'))
+ || (identifier.startsWith(u'`') && identifier.endsWith(u'`'))
+ || (identifier.startsWith(u'[') && identifier.endsWith(u']')));
+}
+
+QSqlIndex QSQLiteDriverPrivate::getTableInfo(QSqlQuery &query, const QString &tableName,
+ bool onlyPIndex) const
+{
+ Q_Q(const QSQLiteDriver);
+ QString schema;
+ QString table = q->escapeIdentifier(tableName, QSqlDriver::TableName);
+ const auto indexOfSeparator = table.indexOf(u'.');
+ if (indexOfSeparator > -1) {
+ auto leftName = QStringView{table}.first(indexOfSeparator);
+ auto rightName = QStringView{table}.sliced(indexOfSeparator + 1);
+ if (isIdentifierEscaped(leftName) && isIdentifierEscaped(rightName)) {
+ schema = leftName.toString() + u'.';
+ table = rightName.toString();
+ }
+ }
+
+ query.exec("PRAGMA "_L1 + schema + "table_info ("_L1 + table + u')');
+ QSqlIndex ind;
+ while (query.next()) {
+ bool isPk = query.value(5).toInt();
+ if (onlyPIndex && !isPk)
+ continue;
+ QString typeName = query.value(2).toString().toLower();
+ QString defVal = query.value(4).toString();
+ if (!defVal.isEmpty() && defVal.at(0) == u'\'') {
+ const int end = defVal.lastIndexOf(u'\'');
+ if (end > 0)
+ defVal = defVal.mid(1, end - 1);
+ }
+
+ QSqlField fld(query.value(1).toString(), QMetaType(qGetColumnType(typeName)), tableName);
+ if (isPk && (typeName == "integer"_L1))
+ // INTEGER PRIMARY KEY fields are auto-generated in sqlite
+ // INT PRIMARY KEY is not the same as INTEGER PRIMARY KEY!
+ fld.setAutoValue(true);
+ fld.setRequired(query.value(3).toInt() != 0);
+ fld.setDefaultValue(defVal);
+ ind.append(fld);
+ }
+ return ind;
+}
class QSQLiteResultPrivate : public QSqlCachedResultPrivate
{
@@ -189,7 +198,7 @@ void QSQLiteResultPrivate::finalize()
return;
sqlite3_finalize(stmt);
- stmt = 0;
+ stmt = nullptr;
}
void QSQLiteResultPrivate::initColumns(bool emptyResultset)
@@ -204,10 +213,10 @@ void QSQLiteResultPrivate::initColumns(bool emptyResultset)
for (int i = 0; i < nCols; ++i) {
QString colName = QString(reinterpret_cast<const QChar *>(
sqlite3_column_name16(stmt, i))
- ).remove(QLatin1Char('"'));
+ ).remove(u'"');
const QString tableName = QString(reinterpret_cast<const QChar *>(
sqlite3_column_table_name16(stmt, i))
- ).remove(QLatin1Char('"'));
+ ).remove(u'"');
// must use typeName for resolving the type to match QSqliteDriver::record
QString typeName = QString(reinterpret_cast<const QChar *>(
sqlite3_column_decltype16(stmt, i)));
@@ -241,7 +250,6 @@ void QSQLiteResultPrivate::initColumns(bool emptyResultset)
}
QSqlField fld(colName, QMetaType(fieldType), tableName);
- fld.setSqlType(stp);
rInf.append(fld);
}
}
@@ -249,14 +257,12 @@ void QSQLiteResultPrivate::initColumns(bool emptyResultset)
bool QSQLiteResultPrivate::fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch)
{
Q_Q(QSQLiteResult);
- int res;
- int i;
if (skipRow) {
// already fetched
Q_ASSERT(!initialFetch);
skipRow = false;
- for(int i=0;i<firstRow.count();i++)
+ for(int i=0;i<firstRow.size();i++)
values[i]=firstRow[i];
return skippedStatus;
}
@@ -273,8 +279,7 @@ bool QSQLiteResultPrivate::fetchNext(QSqlCachedResult::ValueCache &values, int i
q->setAt(QSql::AfterLastRow);
return false;
}
- res = sqlite3_step(stmt);
-
+ int res = sqlite3_step(stmt);
switch(res) {
case SQLITE_ROW:
// check to see if should fill out columns
@@ -283,7 +288,7 @@ bool QSQLiteResultPrivate::fetchNext(QSqlCachedResult::ValueCache &values, int i
initColumns(false);
if (idx < 0 && !initialFetch)
return true;
- for (i = 0; i < rInf.count(); ++i) {
+ for (int i = 0; i < rInf.count(); ++i) {
switch (sqlite3_column_type(stmt, i)) {
case SQLITE_BLOB:
values[i + idx] = QByteArray(static_cast<const char *>(
@@ -385,13 +390,14 @@ bool QSQLiteResult::prepare(const QString &query)
setSelect(false);
- const void *pzTail = NULL;
+ const void *pzTail = nullptr;
+ const auto size = int((query.size() + 1) * sizeof(QChar));
#if (SQLITE_VERSION_NUMBER >= 3003011)
- int res = sqlite3_prepare16_v2(d->drv_d_func()->access, query.constData(), (query.size() + 1) * sizeof(QChar),
+ int res = sqlite3_prepare16_v2(d->drv_d_func()->access, query.constData(), size,
&d->stmt, &pzTail);
#else
- int res = sqlite3_prepare16(d->access, query.constData(), (query.size() + 1) * sizeof(QChar),
+ int res = sqlite3_prepare16(d->access, query.constData(), size,
&d->stmt, &pzTail);
#endif
@@ -415,10 +421,10 @@ bool QSQLiteResult::execBatch(bool arrayBind)
Q_D(QSqlResult);
QScopedValueRollback<QList<QVariant>> valuesScope(d->values);
QList<QVariant> values = d->values;
- if (values.count() == 0)
+ if (values.size() == 0)
return false;
- for (int i = 0; i < values.at(0).toList().count(); ++i) {
+ for (int i = 0; i < values.at(0).toList().size(); ++i) {
d->values.clear();
QScopedValueRollback<QHash<QString, QList<int>>> indexesScope(d->indexes);
auto it = d->indexes.constBegin();
@@ -452,16 +458,16 @@ bool QSQLiteResult::exec()
}
int paramCount = sqlite3_bind_parameter_count(d->stmt);
- bool paramCountIsValid = paramCount == values.count();
+ bool paramCountIsValid = paramCount == values.size();
#if (SQLITE_VERSION_NUMBER >= 3003011)
// In the case of the reuse of a named placeholder
// We need to check explicitly that paramCount is greater than or equal to 1, as sqlite
// can end up in a case where for virtual tables it returns 0 even though it
// has parameters
- if (paramCount >= 1 && paramCount < values.count()) {
+ if (paramCount >= 1 && paramCount < values.size()) {
const auto countIndexes = [](int counter, const QList<int> &indexList) {
- return counter + indexList.length();
+ return counter + indexList.size();
};
const int bindParamCount = std::accumulate(d->indexes.cbegin(),
@@ -469,7 +475,7 @@ bool QSQLiteResult::exec()
0,
countIndexes);
- paramCountIsValid = bindParamCount == values.count();
+ paramCountIsValid = bindParamCount == values.size();
// When using named placeholders, it will reuse the index for duplicated
// placeholders. So we need to ensure the QList has only one instance of
// each value as SQLite will do the rest for us.
@@ -496,9 +502,9 @@ bool QSQLiteResult::exec()
if (paramCountIsValid) {
for (int i = 0; i < paramCount; ++i) {
res = SQLITE_OK;
- const QVariant value = values.at(i);
+ const QVariant &value = values.at(i);
- if (value.isNull()) {
+ if (QSqlResultPrivate::isVariantNull(value)) {
res = sqlite3_bind_null(d->stmt, i + 1);
} else {
switch (value.userType()) {
@@ -521,28 +527,32 @@ bool QSQLiteResult::exec()
case QMetaType::QDateTime: {
const QDateTime dateTime = value.toDateTime();
const QString str = dateTime.toString(Qt::ISODateWithMs);
- res = sqlite3_bind_text16(d->stmt, i + 1, str.utf16(),
- str.size() * sizeof(ushort), SQLITE_TRANSIENT);
+ res = sqlite3_bind_text16(d->stmt, i + 1, str.data(),
+ int(str.size() * sizeof(ushort)),
+ SQLITE_TRANSIENT);
break;
}
case QMetaType::QTime: {
const QTime time = value.toTime();
const QString str = time.toString(u"hh:mm:ss.zzz");
- res = sqlite3_bind_text16(d->stmt, i + 1, str.utf16(),
- str.size() * sizeof(ushort), SQLITE_TRANSIENT);
+ res = sqlite3_bind_text16(d->stmt, i + 1, str.data(),
+ int(str.size() * sizeof(ushort)),
+ SQLITE_TRANSIENT);
break;
}
case QMetaType::QString: {
// lifetime of string == lifetime of its qvariant
const QString *str = static_cast<const QString*>(value.constData());
- res = sqlite3_bind_text16(d->stmt, i + 1, str->utf16(),
- (str->size()) * sizeof(QChar), SQLITE_STATIC);
+ res = sqlite3_bind_text16(d->stmt, i + 1, str->unicode(),
+ int(str->size()) * sizeof(QChar),
+ SQLITE_STATIC);
break; }
default: {
- QString str = value.toString();
+ const QString str = value.toString();
// SQLITE_TRANSIENT makes sure that sqlite buffers the data
- res = sqlite3_bind_text16(d->stmt, i + 1, str.utf16(),
- (str.size()) * sizeof(QChar), SQLITE_TRANSIENT);
+ res = sqlite3_bind_text16(d->stmt, i + 1, str.data(),
+ int(str.size()) * sizeof(QChar),
+ SQLITE_TRANSIENT);
break; }
}
}
@@ -654,6 +664,30 @@ static void _q_regexp_cleanup(void *cache)
}
#endif
+static void _q_lower(sqlite3_context* context, int argc, sqlite3_value** argv)
+{
+ if (Q_UNLIKELY(argc != 1)) {
+ sqlite3_result_text(context, nullptr, 0, nullptr);
+ return;
+ }
+ const QString lower = QString::fromUtf8(
+ reinterpret_cast<const char*>(sqlite3_value_text(argv[0]))).toLower();
+ const QByteArray ba = lower.toUtf8();
+ sqlite3_result_text(context, ba.data(), ba.size(), SQLITE_TRANSIENT);
+}
+
+static void _q_upper(sqlite3_context* context, int argc, sqlite3_value** argv)
+{
+ if (Q_UNLIKELY(argc != 1)) {
+ sqlite3_result_text(context, nullptr, 0, nullptr);
+ return;
+ }
+ const QString upper = QString::fromUtf8(
+ reinterpret_cast<const char*>(sqlite3_value_text(argv[0]))).toUpper();
+ const QByteArray ba = upper.toUtf8();
+ sqlite3_result_text(context, ba.data(), ba.size(), SQLITE_TRANSIENT);
+}
+
QSQLiteDriver::QSQLiteDriver(QObject * parent)
: QSqlDriver(*new QSQLiteDriverPrivate, parent)
{
@@ -719,36 +753,48 @@ bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, c
bool sharedCache = false;
bool openReadOnlyOption = false;
bool openUriOption = false;
+ bool useExtendedResultCodes = true;
+ bool useQtVfs = false;
+ bool useQtCaseFolding = false;
+ bool openNoFollow = false;
#if QT_CONFIG(regularexpression)
- static const QLatin1String regexpConnectOption = QLatin1String("QSQLITE_ENABLE_REGEXP");
+ static const auto regexpConnectOption = "QSQLITE_ENABLE_REGEXP"_L1;
bool defineRegexp = false;
int regexpCacheSize = 25;
#endif
- const auto opts = QStringView{conOpts}.split(QLatin1Char(';'));
+ const auto opts = QStringView{conOpts}.split(u';', Qt::SkipEmptyParts);
for (auto option : opts) {
option = option.trimmed();
- if (option.startsWith(QLatin1String("QSQLITE_BUSY_TIMEOUT"))) {
+ if (option.startsWith("QSQLITE_BUSY_TIMEOUT"_L1)) {
option = option.mid(20).trimmed();
- if (option.startsWith(QLatin1Char('='))) {
+ if (option.startsWith(u'=')) {
bool ok;
const int nt = option.mid(1).trimmed().toInt(&ok);
if (ok)
timeOut = nt;
}
- } else if (option == QLatin1String("QSQLITE_OPEN_READONLY")) {
+ } else if (option == "QSQLITE_USE_QT_VFS"_L1) {
+ useQtVfs = true;
+ } else if (option == "QSQLITE_OPEN_READONLY"_L1) {
openReadOnlyOption = true;
- } else if (option == QLatin1String("QSQLITE_OPEN_URI")) {
+ } else if (option == "QSQLITE_OPEN_URI"_L1) {
openUriOption = true;
- } else if (option == QLatin1String("QSQLITE_ENABLE_SHARED_CACHE")) {
+ } else if (option == "QSQLITE_ENABLE_SHARED_CACHE"_L1) {
sharedCache = true;
+ } else if (option == "QSQLITE_NO_USE_EXTENDED_RESULT_CODES"_L1) {
+ useExtendedResultCodes = false;
+ } else if (option == "QSQLITE_ENABLE_NON_ASCII_CASE_FOLDING"_L1) {
+ useQtCaseFolding = true;
+ } else if (option == "QSQLITE_OPEN_NOFOLLOW"_L1) {
+ openNoFollow = true;
}
#if QT_CONFIG(regularexpression)
else if (option.startsWith(regexpConnectOption)) {
option = option.mid(regexpConnectOption.size()).trimmed();
if (option.isEmpty()) {
defineRegexp = true;
- } else if (option.startsWith(QLatin1Char('='))) {
+ } else if (option.startsWith(u'=')) {
bool ok = false;
const int cacheSize = option.mid(1).trimmed().toInt(&ok);
if (ok) {
@@ -759,28 +805,45 @@ bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, c
}
}
#endif
+ else
+ qCWarning(lcSqlite, "Unsupported option '%ls'", qUtf16Printable(option.toString()));
}
int openMode = (openReadOnlyOption ? SQLITE_OPEN_READONLY : (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE));
openMode |= (sharedCache ? SQLITE_OPEN_SHAREDCACHE : SQLITE_OPEN_PRIVATECACHE);
if (openUriOption)
openMode |= SQLITE_OPEN_URI;
+ if (openNoFollow) {
+#if defined(SQLITE_OPEN_NOFOLLOW)
+ openMode |= SQLITE_OPEN_NOFOLLOW;
+#else
+ qCWarning(lcSqlite, "SQLITE_OPEN_NOFOLLOW not supported with the SQLite version %s", sqlite3_libversion());
+#endif
+ }
openMode |= SQLITE_OPEN_NOMUTEX;
- const int res = sqlite3_open_v2(db.toUtf8().constData(), &d->access, openMode, NULL);
+ const int res = sqlite3_open_v2(db.toUtf8().constData(), &d->access, openMode, useQtVfs ? "QtVFS" : nullptr);
if (res == SQLITE_OK) {
sqlite3_busy_timeout(d->access, timeOut);
+ sqlite3_extended_result_codes(d->access, useExtendedResultCodes);
setOpen(true);
setOpenError(false);
#if QT_CONFIG(regularexpression)
if (defineRegexp) {
auto cache = new QCache<QString, QRegularExpression>(regexpCacheSize);
- sqlite3_create_function_v2(d->access, "regexp", 2, SQLITE_UTF8, cache, &_q_regexp, NULL,
- NULL, &_q_regexp_cleanup);
+ sqlite3_create_function_v2(d->access, "regexp", 2, SQLITE_UTF8, cache,
+ &_q_regexp, nullptr,
+ nullptr, &_q_regexp_cleanup);
}
#endif
+ if (useQtCaseFolding) {
+ sqlite3_create_function_v2(d->access, "lower", 1, SQLITE_UTF8, nullptr,
+ &_q_lower, nullptr, nullptr, nullptr);
+ sqlite3_create_function_v2(d->access, "upper", 1, SQLITE_UTF8, nullptr,
+ &_q_upper, nullptr, nullptr, nullptr);
+ }
return true;
} else {
setLastError(qMakeError(d->access, tr("Error opening database"),
@@ -789,7 +852,7 @@ bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, c
if (d->access) {
sqlite3_close(d->access);
- d->access = 0;
+ d->access = nullptr;
}
return false;
@@ -800,19 +863,19 @@ void QSQLiteDriver::close()
{
Q_D(QSQLiteDriver);
if (isOpen()) {
- for (QSQLiteResult *result : qAsConst(d->results))
+ for (QSQLiteResult *result : std::as_const(d->results))
result->d_func()->finalize();
- if (d->access && (d->notificationid.count() > 0)) {
+ if (d->access && (d->notificationid.size() > 0)) {
d->notificationid.clear();
- sqlite3_update_hook(d->access, NULL, NULL);
+ sqlite3_update_hook(d->access, nullptr, nullptr);
}
const int res = sqlite3_close(d->access);
if (res != SQLITE_OK)
setLastError(qMakeError(d->access, tr("Error closing database"), QSqlError::ConnectionError, res));
- d->access = 0;
+ d->access = nullptr;
setOpen(false);
setOpenError(false);
}
@@ -829,7 +892,7 @@ bool QSQLiteDriver::beginTransaction()
return false;
QSqlQuery q(createResult());
- if (!q.exec(QLatin1String("BEGIN"))) {
+ if (!q.exec("BEGIN"_L1)) {
setLastError(QSqlError(tr("Unable to begin transaction"),
q.lastError().databaseText(), QSqlError::TransactionError));
return false;
@@ -844,7 +907,7 @@ bool QSQLiteDriver::commitTransaction()
return false;
QSqlQuery q(createResult());
- if (!q.exec(QLatin1String("COMMIT"))) {
+ if (!q.exec("COMMIT"_L1)) {
setLastError(QSqlError(tr("Unable to commit transaction"),
q.lastError().databaseText(), QSqlError::TransactionError));
return false;
@@ -859,7 +922,7 @@ bool QSQLiteDriver::rollbackTransaction()
return false;
QSqlQuery q(createResult());
- if (!q.exec(QLatin1String("ROLLBACK"))) {
+ if (!q.exec("ROLLBACK"_L1)) {
setLastError(QSqlError(tr("Unable to rollback transaction"),
q.lastError().databaseText(), QSqlError::TransactionError));
return false;
@@ -877,14 +940,14 @@ QStringList QSQLiteDriver::tables(QSql::TableType type) const
QSqlQuery q(createResult());
q.setForwardOnly(true);
- QString sql = QLatin1String("SELECT name FROM sqlite_master WHERE %1 "
- "UNION ALL SELECT name FROM sqlite_temp_master WHERE %1");
+ QString sql = "SELECT name FROM sqlite_master WHERE %1 "
+ "UNION ALL SELECT name FROM sqlite_temp_master WHERE %1"_L1;
if ((type & QSql::Tables) && (type & QSql::Views))
- sql = sql.arg(QLatin1String("type='table' OR type='view'"));
+ sql = sql.arg("type='table' OR type='view'"_L1);
else if (type & QSql::Tables)
- sql = sql.arg(QLatin1String("type='table'"));
+ sql = sql.arg("type='table'"_L1);
else if (type & QSql::Views)
- sql = sql.arg(QLatin1String("type='view'"));
+ sql = sql.arg("type='view'"_L1);
else
sql.clear();
@@ -895,74 +958,32 @@ QStringList QSQLiteDriver::tables(QSql::TableType type) const
if (type & QSql::SystemTables) {
// there are no internal tables beside this one:
- res.append(QLatin1String("sqlite_master"));
+ res.append("sqlite_master"_L1);
}
return res;
}
-static QSqlIndex qGetTableInfo(QSqlQuery &q, const QString &tableName, bool onlyPIndex = false)
-{
- QString schema;
- QString table(tableName);
- int indexOfSeparator = tableName.indexOf(QLatin1Char('.'));
- if (indexOfSeparator > -1) {
- schema = tableName.left(indexOfSeparator).append(QLatin1Char('.'));
- table = tableName.mid(indexOfSeparator + 1);
- }
- q.exec(QLatin1String("PRAGMA ") + schema + QLatin1String("table_info (") + _q_escapeIdentifier(table) + QLatin1Char(')'));
-
- QSqlIndex ind;
- while (q.next()) {
- bool isPk = q.value(5).toInt();
- if (onlyPIndex && !isPk)
- continue;
- QString typeName = q.value(2).toString().toLower();
- QString defVal = q.value(4).toString();
- if (!defVal.isEmpty() && defVal.at(0) == QLatin1Char('\'')) {
- const int end = defVal.lastIndexOf(QLatin1Char('\''));
- if (end > 0)
- defVal = defVal.mid(1, end - 1);
- }
-
- QSqlField fld(q.value(1).toString(), QMetaType(qGetColumnType(typeName)), tableName);
- if (isPk && (typeName == QLatin1String("integer")))
- // INTEGER PRIMARY KEY fields are auto-generated in sqlite
- // INT PRIMARY KEY is not the same as INTEGER PRIMARY KEY!
- fld.setAutoValue(true);
- fld.setRequired(q.value(3).toInt() != 0);
- fld.setDefaultValue(defVal);
- ind.append(fld);
- }
- return ind;
-}
-
-QSqlIndex QSQLiteDriver::primaryIndex(const QString &tblname) const
+QSqlIndex QSQLiteDriver::primaryIndex(const QString &tablename) const
{
+ Q_D(const QSQLiteDriver);
if (!isOpen())
return QSqlIndex();
- QString table = tblname;
- if (isIdentifierEscaped(table, QSqlDriver::TableName))
- table = stripDelimiters(table, QSqlDriver::TableName);
-
QSqlQuery q(createResult());
q.setForwardOnly(true);
- return qGetTableInfo(q, table, true);
+ return d->getTableInfo(q, tablename, true);
}
-QSqlRecord QSQLiteDriver::record(const QString &tbl) const
+QSqlRecord QSQLiteDriver::record(const QString &tablename) const
{
+ Q_D(const QSQLiteDriver);
if (!isOpen())
return QSqlRecord();
- QString table = tbl;
- if (isIdentifierEscaped(table, QSqlDriver::TableName))
- table = stripDelimiters(table, QSqlDriver::TableName);
-
QSqlQuery q(createResult());
q.setForwardOnly(true);
- return qGetTableInfo(q, table);
+ return d->getTableInfo(q, tablename);
}
QVariant QSQLiteDriver::handle() const
@@ -973,8 +994,52 @@ QVariant QSQLiteDriver::handle() const
QString QSQLiteDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const
{
+ Q_D(const QSQLiteDriver);
+ if (identifier.isEmpty() || isIdentifierEscaped(identifier, type))
+ return identifier;
+
+ const auto indexOfSeparator = identifier.indexOf(u'.');
+ if (indexOfSeparator > -1) {
+ auto leftName = QStringView{identifier}.first(indexOfSeparator);
+ auto rightName = QStringView{identifier}.sliced(indexOfSeparator + 1);
+ const QStringView leftEnclose = d->isIdentifierEscaped(leftName) ? u"" : u"\"";
+ const QStringView rightEnclose = d->isIdentifierEscaped(rightName) ? u"" : u"\"";
+ if (leftEnclose.isEmpty() || rightEnclose.isEmpty())
+ return (leftEnclose + leftName + leftEnclose + u'.' + rightEnclose + rightName
+ + rightEnclose);
+ }
+ return u'"' + identifier + u'"';
+}
+
+bool QSQLiteDriver::isIdentifierEscaped(const QString &identifier, IdentifierType type) const
+{
+ Q_D(const QSQLiteDriver);
Q_UNUSED(type);
- return _q_escapeIdentifier(identifier);
+ return d->isIdentifierEscaped(QStringView{identifier});
+}
+
+QString QSQLiteDriver::stripDelimiters(const QString &identifier, IdentifierType type) const
+{
+ Q_D(const QSQLiteDriver);
+ const auto indexOfSeparator = identifier.indexOf(u'.');
+ if (indexOfSeparator > -1) {
+ auto leftName = QStringView{identifier}.first(indexOfSeparator);
+ auto rightName = QStringView{identifier}.sliced(indexOfSeparator + 1);
+ const auto leftEscaped = d->isIdentifierEscaped(leftName);
+ const auto rightEscaped = d->isIdentifierEscaped(rightName);
+ if (leftEscaped || rightEscaped) {
+ if (leftEscaped)
+ leftName = leftName.sliced(1).chopped(1);
+ if (rightEscaped)
+ rightName = rightName.sliced(1).chopped(1);
+ return leftName + u'.' + rightName;
+ }
+ }
+
+ if (isIdentifierEscaped(identifier, type))
+ return identifier.mid(1, identifier.size() - 2);
+
+ return identifier;
}
static void handle_sqlite_callback(void *qobj,int aoperation, char const *adbname, char const *atablename,
@@ -993,18 +1058,19 @@ bool QSQLiteDriver::subscribeToNotification(const QString &name)
{
Q_D(QSQLiteDriver);
if (!isOpen()) {
- qWarning("Database not open.");
+ qCWarning(lcSqlite, "QSQLiteDriver::subscribeToNotification: Database not open.");
return false;
}
if (d->notificationid.contains(name)) {
- qWarning("Already subscribing to '%s'.", qPrintable(name));
+ qCWarning(lcSqlite, "QSQLiteDriver::subscribeToNotification: Already subscribing to '%ls'.",
+ qUtf16Printable(name));
return false;
}
//sqlite supports only one notification callback, so only the first is registered
d->notificationid << name;
- if (d->notificationid.count() == 1)
+ if (d->notificationid.size() == 1)
sqlite3_update_hook(d->access, &handle_sqlite_callback, reinterpret_cast<void *> (this));
return true;
@@ -1014,18 +1080,19 @@ bool QSQLiteDriver::unsubscribeFromNotification(const QString &name)
{
Q_D(QSQLiteDriver);
if (!isOpen()) {
- qWarning("Database not open.");
+ qCWarning(lcSqlite, "QSQLiteDriver::unsubscribeFromNotification: Database not open.");
return false;
}
if (!d->notificationid.contains(name)) {
- qWarning("Not subscribed to '%s'.", qPrintable(name));
+ qCWarning(lcSqlite, "QSQLiteDriver::unsubscribeFromNotification: Not subscribed to '%ls'.",
+ qUtf16Printable(name));
return false;
}
d->notificationid.removeAll(name);
if (d->notificationid.isEmpty())
- sqlite3_update_hook(d->access, NULL, NULL);
+ sqlite3_update_hook(d->access, nullptr, nullptr);
return true;
}
@@ -1044,3 +1111,5 @@ void QSQLiteDriver::handleNotification(const QString &tableName, qint64 rowid)
}
QT_END_NAMESPACE
+
+#include "moc_qsql_sqlite_p.cpp"