From 6551913aa499b02f4b6de1090cd350abe0bce16d Mon Sep 17 00:00:00 2001 From: "Juergen E. Fischer" Date: Mon, 16 Jul 2018 01:38:05 +0200 Subject: [PATCH] qspatialite: 'update' qt5 port (fixes #19419) --- .docker/qgis3-build-deps.dockerfile | 1 + debian/control.in | 4 +- external/qspatialite/CMakeLists.txt | 5 +- external/qspatialite/qsql_spatialite.cpp | 1170 ++++++++++------- external/qspatialite/qsql_spatialite.h | 141 +- external/qspatialite/qsqlcachedresult_p.h | 100 -- external/qspatialite/smain.cpp | 22 +- external/qspatialite/smain.h | 6 +- .../oracle/ocispatial/qsqlcachedresult_p.h | 100 -- 9 files changed, 759 insertions(+), 790 deletions(-) delete mode 100644 external/qspatialite/qsqlcachedresult_p.h delete mode 100644 src/providers/oracle/ocispatial/qsqlcachedresult_p.h diff --git a/.docker/qgis3-build-deps.dockerfile b/.docker/qgis3-build-deps.dockerfile index a43e0ec3aedf..f486360393b0 100644 --- a/.docker/qgis3-build-deps.dockerfile +++ b/.docker/qgis3-build-deps.dockerfile @@ -89,6 +89,7 @@ RUN apt-get update \ qtpositioning5-dev \ qttools5-dev \ qttools5-dev-tools \ + qtbase5-private-dev \ saga \ spawn-fcgi \ txt2tags \ diff --git a/debian/control.in b/debian/control.in index e5a5ddc79b2a..43b32072fd51 100644 --- a/debian/control.in +++ b/debian/control.in @@ -21,7 +21,7 @@ Build-Depends: libsqlite3-dev, libspatialindex-dev, libzip-dev, - qtbase5-dev, qttools5-dev-tools, qttools5-dev, qtpositioning5-dev, qt5keychain-dev, + qtbase5-dev, qttools5-dev-tools, qttools5-dev, qtpositioning5-dev, qt5keychain-dev, qtbase5-private-dev, libqt5svg5-dev, libqt5xmlpatterns5-dev, libqt5webkit5-dev, libqt5opengl5-dev, libqt5sql5-sqlite, libqt5serialport5-dev, libqt5scintilla2-dev, libqwt-qt5-dev, libqca-qt5-2-dev, libqca-qt5-2-plugins, python3-dev, python3-all-dev, python3-sip, python3-sip-dev, @@ -40,7 +40,7 @@ Build-Depends: xfonts-base, xfonts-100dpi, xfonts-75dpi, xfonts-scalable, #sid buster bionic cosmic# libosgearth-dev, #bionic cosmic# qt3d5-dev, qt3d-assimpsceneimport-plugin, qt3d-defaultgeometryloader-plugin, qt3d-gltfsceneio-plugin, qt3d-scene2d-plugin, -#oracle# oracle-instantclient12.1-devel, oracle-instantclient12.1-basiclite, qtbase5-private-dev, +#oracle# oracle-instantclient12.1-devel, oracle-instantclient12.1-basiclite, locales, ca-certificates, ninja-build Build-Conflicts: libqgis-dev, qgis-dev #sid buster stretch xenial bionic cosmic#Standards-Version: 3.9.7 diff --git a/external/qspatialite/CMakeLists.txt b/external/qspatialite/CMakeLists.txt index df445568e142..82aaeedb2a45 100644 --- a/external/qspatialite/CMakeLists.txt +++ b/external/qspatialite/CMakeLists.txt @@ -6,7 +6,10 @@ ADD_DEFINITIONS(-DQT_PLUGIN) ADD_DEFINITIONS(-DQT_NO_DEBUG) ADD_DEFINITIONS(-DQT_SHARED) -INCLUDE_DIRECTORIES(SYSTEM ${SQLITE3_INCLUDE_DIR}) +INCLUDE_DIRECTORIES(SYSTEM + ${SQLITE3_INCLUDE_DIR} + ${Qt5Sql_PRIVATE_INCLUDE_DIRS} +) SET(QSQLSPATIALITE_SRC qsql_spatialite.cpp smain.cpp) QT5_WRAP_CPP(QSQLSPATIALITE_SRC qsql_spatialite.h smain.h) diff --git a/external/qspatialite/qsql_spatialite.cpp b/external/qspatialite/qsql_spatialite.cpp index 6f662c7cdcd9..5caf4b08e83e 100644 --- a/external/qspatialite/qsql_spatialite.cpp +++ b/external/qspatialite/qsql_spatialite.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal +** 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. ** @@ -10,30 +10,28 @@ ** 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** 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 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** 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 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** +** 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$ ** @@ -42,14 +40,19 @@ #include "qsql_spatialite.h" #include +#include #include #include #include #include #include +#include +#include #include #include #include +#include + #include "qgsspatialiteutils.h" #if defined Q_OS_WIN @@ -59,73 +62,94 @@ #endif #include - -Q_DECLARE_OPAQUE_POINTER(sqlite3*) -Q_DECLARE_OPAQUE_POINTER(sqlite3_stmt*) -Q_DECLARE_METATYPE(sqlite3*) -Q_DECLARE_METATYPE(sqlite3_stmt*) +#include QT_BEGIN_NAMESPACE -static QString _q_escapeIdentifier(const QString &identifier) +static QString _q_escapeIdentifier( const QString &identifier ) { - QString res = identifier; - if(!identifier.isEmpty() && identifier.at(0) != '"' && identifier.right(1) != QString(QLatin1Char('"')) ) { - res.replace(QLatin1Char('"'), QLatin1String("\"\"")); - res.prepend(QLatin1Char('"')).append(QLatin1Char('"')); - res.replace(QLatin1Char('.'), QLatin1String("\".\"")); - } - return res; + 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 QVariant::Type qGetColumnType(const QString &tpName) +static QVariant::Type qGetColumnType( const QString &tpName ) { - const QString typeName = tpName.toLower(); + const QString typeName = tpName.toLower(); - if (typeName == QLatin1String("integer") - || typeName == QLatin1String("int")) - return QVariant::Int; - if (typeName == QLatin1String("double") - || typeName == QLatin1String("float") - || typeName == QLatin1String("real") - || typeName.startsWith(QLatin1String("numeric"))) - return QVariant::Double; - if (typeName == QLatin1String("blob")) - return QVariant::ByteArray; - return QVariant::String; + if ( typeName == QLatin1String( "integer" ) + || typeName == QLatin1String( "int" ) ) + return QVariant::Int; + if ( typeName == QLatin1String( "double" ) + || typeName == QLatin1String( "float" ) + || typeName == QLatin1String( "real" ) + || typeName.startsWith( QLatin1String( "numeric" ) ) ) + return QVariant::Double; + if ( typeName == QLatin1String( "blob" ) ) + return QVariant::ByteArray; + return QVariant::String; } -static QSqlError qMakeError(sqlite3 *access, const QString &descr, QSqlError::ErrorType type, - int errorCode = -1) +static QSqlError qMakeError( const spatialite_database_unique_ptr &access, const QString &descr, QSqlError::ErrorType type, + int errorCode = -1 ) { - return QSqlError(descr, - QString::fromUtf16( (const ushort *) sqlite3_errmsg16(access) ), - type, errorCode); + return QSqlError( descr, access.errorMessage(), type, QString::number( errorCode ) ); } -class QSpatiaLiteDriverPrivate +class QSpatiaLiteResultPrivate; + +class QSpatiaLiteResult : public QSqlCachedResult { -public: - inline QSpatiaLiteDriverPrivate() {} + Q_DECLARE_PRIVATE( QSpatiaLiteResult ) + friend class QSpatiaLiteDriver; + + public: + explicit QSpatiaLiteResult( const QSpatiaLiteDriver *db ); + ~QSpatiaLiteResult(); + protected: + bool gotoNext( QSqlCachedResult::ValueCache &row, int idx ) Q_DECL_OVERRIDE; + bool reset( const QString &query ) Q_DECL_OVERRIDE; + bool prepare( const QString &query ) Q_DECL_OVERRIDE; + bool exec() Q_DECL_OVERRIDE; + int size() Q_DECL_OVERRIDE; + int numRowsAffected() Q_DECL_OVERRIDE; + QVariant lastInsertId() const Q_DECL_OVERRIDE; + QSqlRecord record() const Q_DECL_OVERRIDE; + void detachFromResultSet() Q_DECL_OVERRIDE; + void virtual_hook( int id, void *data ) Q_DECL_OVERRIDE; +}; + +class QSpatiaLiteDriverPrivate : public QSqlDriverPrivate +{ + Q_DECLARE_PUBLIC( QSpatiaLiteDriver ) + + public: + inline QSpatiaLiteDriverPrivate() : QSqlDriverPrivate() { dbmsType = QSqlDriver::SQLite; } spatialite_database_unique_ptr access; QList results; + QStringList notificationid; }; -class QSpatiaLiteResultPrivate +class QSpatiaLiteResultPrivate: public QSqlCachedResultPrivate { -public: - explicit QSpatiaLiteResultPrivate(QSpatiaLiteResult *res); + Q_DECLARE_PUBLIC( QSpatiaLiteResult ) + + public: + Q_DECLARE_SQLDRIVER_PRIVATE( QSpatiaLiteDriver ) + QSpatiaLiteResultPrivate( QSpatiaLiteResult *q, const QSpatiaLiteDriver *drv ); void cleanup(); - bool fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch); + bool fetchNext( QSqlCachedResult::ValueCache &values, int idx, bool initialFetch ); // initializes the recordInfo and the cache - void initColumns(bool emptyResultset); + void initColumns( bool emptyResultset ); void finalize(); - QSpatiaLiteResult* q; - sqlite3 *access; - - sqlite3_stmt *stmt; + sqlite3_statement_unique_ptr stmt; bool skippedStatus; // the status of the fetchNext() that's skipped bool skipRow; // skip the next fetchNext()? @@ -133,375 +157,442 @@ class QSpatiaLiteResultPrivate QVector firstRow; }; -QSpatiaLiteResultPrivate::QSpatiaLiteResultPrivate(QSpatiaLiteResult* res) : q(res), access(0), - stmt(0), skippedStatus(false), skipRow(false) +QSpatiaLiteResultPrivate::QSpatiaLiteResultPrivate( QSpatiaLiteResult *q, const QSpatiaLiteDriver *drv ) + : QSqlCachedResultPrivate( q, drv ) + , skippedStatus( false ) + , skipRow( false ) { } void QSpatiaLiteResultPrivate::cleanup() { - finalize(); - rInf.clear(); - skippedStatus = false; - skipRow = false; - q->setAt(QSql::BeforeFirstRow); - q->setActive(false); - q->cleanup(); + Q_Q( QSpatiaLiteResult ); + finalize(); + rInf.clear(); + skippedStatus = false; + skipRow = false; + q->setAt( QSql::BeforeFirstRow ); + q->setActive( false ); + q->cleanup(); } void QSpatiaLiteResultPrivate::finalize() { - if (!stmt) - return; - - sqlite3_finalize(stmt); - stmt = 0; + stmt.reset(); } -void QSpatiaLiteResultPrivate::initColumns(bool emptyResultset) +void QSpatiaLiteResultPrivate::initColumns( bool emptyResultset ) { - int nCols = sqlite3_column_count(stmt); - if (nCols <= 0) - return; + Q_Q( QSpatiaLiteResult ); + int nCols = stmt.columnCount(); + if ( nCols <= 0 ) + return; - q->init(nCols); + q->init( nCols ); - for (int i = 0; i < nCols; ++i) { - QString colName = QString::fromUtf16( (const ushort *) sqlite3_column_name16(stmt, i) ).remove(QLatin1Char('"')); + for ( int i = 0; i < nCols; ++i ) + { + QString colName = stmt.columnName( i ).remove( QLatin1Char( '"' ) ); - // must use typeName for resolving the type to match QSqliteDriver::record - QString typeName = QString::fromUtf16( (const ushort *) sqlite3_column_decltype16(stmt, i) ); - // sqlite3_column_type is documented to have undefined behavior if the result set is empty - int stp = emptyResultset ? -1 : sqlite3_column_type(stmt, i); + // must use typeName for resolving the type to match QSqliteDriver::record + QString typeName = QString( reinterpret_cast( + sqlite3_column_decltype16( stmt.get(), i ) ) ); + // sqlite3_column_type is documented to have undefined behavior if the result set is empty + int stp = emptyResultset ? -1 : sqlite3_column_type( stmt.get(), i ); - QVariant::Type fieldType; + QVariant::Type fieldType; - if (!typeName.isEmpty()) { - fieldType = qGetColumnType(typeName); - } else { - // Get the proper type for the field based on stp value - switch (stp) { - case SQLITE_INTEGER: - fieldType = QVariant::Int; - break; - case SQLITE_FLOAT: - fieldType = QVariant::Double; - break; - case SQLITE_BLOB: - fieldType = QVariant::ByteArray; - break; - case SQLITE_TEXT: - fieldType = QVariant::String; - break; - case SQLITE_NULL: - default: - fieldType = QVariant::Invalid; - break; - } - } - - int dotIdx = colName.lastIndexOf(QLatin1Char('.')); - QSqlField fld(colName.mid(dotIdx == -1 ? 0 : dotIdx + 1), fieldType); - fld.setSqlType(stp); - rInf.append(fld); + if ( !typeName.isEmpty() ) + { + fieldType = qGetColumnType( typeName ); } + else + { + // Get the proper type for the field based on stp value + switch ( stp ) + { + case SQLITE_INTEGER: + fieldType = QVariant::Int; + break; + case SQLITE_FLOAT: + fieldType = QVariant::Double; + break; + case SQLITE_BLOB: + fieldType = QVariant::ByteArray; + break; + case SQLITE_TEXT: + fieldType = QVariant::String; + break; + case SQLITE_NULL: + default: + fieldType = QVariant::Invalid; + break; + } + } + + QSqlField fld( colName, fieldType ); + fld.setSqlType( stp ); + rInf.append( fld ); + } } -bool QSpatiaLiteResultPrivate::fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch) +bool QSpatiaLiteResultPrivate::fetchNext( QSqlCachedResult::ValueCache &values, int idx, bool initialFetch ) { - int res; - int i; - - if (skipRow) { - // already fetched - Q_ASSERT(!initialFetch); - skipRow = false; - for(int i=0;isetLastError(QSqlError(QCoreApplication::translate("QSpatiaLiteResult", "Unable to fetch row"), - QCoreApplication::translate("QSpatiaLiteResult", "No query"), QSqlError::ConnectionError)); - q->setAt(QSql::AfterLastRow); - return false; - } - res = sqlite3_step(stmt); + if ( skipRow ) + { + // already fetched + Q_ASSERT( !initialFetch ); + skipRow = false; + for ( int i = 0; i < firstRow.count(); i++ ) + values[i] = firstRow[i]; + return skippedStatus; + } + skipRow = initialFetch; + + if ( initialFetch ) + { + firstRow.clear(); + firstRow.resize( stmt.columnCount() ); + } + + if ( !stmt ) + { + q->setLastError( QSqlError( QCoreApplication::translate( "QSpatiaLiteResult", "Unable to fetch row" ), + QCoreApplication::translate( "QSpatiaLiteResult", "No query" ), QSqlError::ConnectionError ) ); + q->setAt( QSql::AfterLastRow ); + return false; + } + res = stmt.step(); - switch(res) { + switch ( res ) + { case SQLITE_ROW: - // check to see if should fill out columns - if (rInf.isEmpty()) - // must be first call. - initColumns(false); - if (idx < 0 && !initialFetch) - return true; - for (i = 0; i < rInf.count(); ++i) { - switch (sqlite3_column_type(stmt, i)) { - case SQLITE_BLOB: - values[i + idx] = QByteArray(static_cast( - sqlite3_column_blob(stmt, i)), - sqlite3_column_bytes(stmt, i)); - break; - case SQLITE_INTEGER: - values[i + idx] = sqlite3_column_int64(stmt, i); - break; - case SQLITE_FLOAT: - switch(q->numericalPrecisionPolicy()) { - case QSql::LowPrecisionInt32: - values[i + idx] = sqlite3_column_int(stmt, i); - break; - case QSql::LowPrecisionInt64: - values[i + idx] = sqlite3_column_int64(stmt, i); - break; - case QSql::LowPrecisionDouble: - case QSql::HighPrecision: - default: - values[i + idx] = sqlite3_column_double(stmt, i); - break; - }; + // check to see if should fill out columns + if ( rInf.isEmpty() ) + // must be first call. + initColumns( false ); + if ( idx < 0 && !initialFetch ) + return true; + for ( i = 0; i < rInf.count(); ++i ) + { + switch ( sqlite3_column_type( stmt.get(), i ) ) + { + case SQLITE_BLOB: + values[i + idx] = QByteArray( static_cast( + sqlite3_column_blob( stmt.get(), i ) ), + sqlite3_column_bytes( stmt.get(), i ) ); + break; + case SQLITE_INTEGER: + values[i + idx] = stmt.columnAsInt64( i ); + break; + case SQLITE_FLOAT: + switch ( q->numericalPrecisionPolicy() ) + { + case QSql::LowPrecisionInt32: + values[i + idx] = stmt.columnAsInt64( i ); break; - case SQLITE_NULL: - values[i + idx] = QVariant(QVariant::String); + case QSql::LowPrecisionInt64: + values[i + idx] = stmt.columnAsInt64( i ); break; - default: - values[i + idx] = QString(reinterpret_cast( - sqlite3_column_text16(stmt, i)), - sqlite3_column_bytes16(stmt, i) / sizeof(QChar)); + case QSql::LowPrecisionDouble: + case QSql::HighPrecision: + default: + values[i + idx] = stmt.columnAsDouble( i ); break; - } + }; + break; + case SQLITE_NULL: + values[i + idx] = QVariant( QVariant::String ); + break; + default: + values[i + idx] = stmt.columnAsText( i ); + break; } - return true; + } + return true; case SQLITE_DONE: - if (rInf.isEmpty()) - // must be first call. - initColumns(true); - q->setAt(QSql::AfterLastRow); - sqlite3_reset(stmt); - return false; + if ( rInf.isEmpty() ) + // must be first call. + initColumns( true ); + q->setAt( QSql::AfterLastRow ); + sqlite3_reset( stmt.get() ); + return false; case SQLITE_CONSTRAINT: case SQLITE_ERROR: - // SQLITE_ERROR is a generic error code and we must call sqlite3_reset() - // to get the specific error message. - res = sqlite3_reset(stmt); - q->setLastError(qMakeError(access, QCoreApplication::translate("QSpatiaLiteResult", - "Unable to fetch row"), QSqlError::ConnectionError, res)); - q->setAt(QSql::AfterLastRow); - return false; + // SQLITE_ERROR is a generic error code and we must call sqlite3_reset() + // to get the specific error message. + res = sqlite3_reset( stmt.get() ); + q->setLastError( qMakeError( drv_d_func()->access, QCoreApplication::translate( "QSpatiaLiteResult", + "Unable to fetch row" ), QSqlError::ConnectionError, res ) ); + q->setAt( QSql::AfterLastRow ); + return false; case SQLITE_MISUSE: case SQLITE_BUSY: default: - // something wrong, don't get col info, but still return false - q->setLastError(qMakeError(access, QCoreApplication::translate("QSpatiaLiteResult", - "Unable to fetch row"), QSqlError::ConnectionError, res)); - sqlite3_reset(stmt); - q->setAt(QSql::AfterLastRow); - return false; - } + // something wrong, don't get col info, but still return false + q->setLastError( qMakeError( drv_d_func()->access, QCoreApplication::translate( "QSpatiaLiteResult", + "Unable to fetch row" ), QSqlError::ConnectionError, res ) ); + sqlite3_reset( stmt.get() ); + q->setAt( QSql::AfterLastRow ); + return false; + } + return false; } -QSpatiaLiteResult::QSpatiaLiteResult(const QSpatiaLiteDriver* db) - : QSqlCachedResult(db) +QSpatiaLiteResult::QSpatiaLiteResult( const QSpatiaLiteDriver *db ) + : QSqlCachedResult( *new QSpatiaLiteResultPrivate( this, db ) ) { - d = new QSpatiaLiteResultPrivate(this); - d->access = db->d->access.get(); - db->d->results.append(this); + Q_D( QSpatiaLiteResult ); + const_cast( d->drv_d_func() )->results.append( this ); } QSpatiaLiteResult::~QSpatiaLiteResult() { - const QSpatiaLiteDriver * sqlDriver = qobject_cast(driver()); - if (sqlDriver) - sqlDriver->d->results.removeOne(this); - d->cleanup(); - delete d; + Q_D( QSpatiaLiteResult ); + if ( d->drv_d_func() ) + const_cast( d->drv_d_func() )->results.removeOne( this ); + d->cleanup(); } -bool QSpatiaLiteResult::reset(const QString &query) +void QSpatiaLiteResult::virtual_hook( int id, void *data ) { - if (!prepare(query)) - return false; - return exec(); + QSqlCachedResult::virtual_hook( id, data ); } -bool QSpatiaLiteResult::prepare(const QString &query) +bool QSpatiaLiteResult::reset( const QString &query ) { - if (!driver() || !driver()->isOpen() || driver()->isOpenError()) - return false; - - d->cleanup(); + if ( !prepare( query ) ) + return false; + return exec(); +} - setSelect(false); +bool QSpatiaLiteResult::prepare( const QString &query ) +{ + Q_D( QSpatiaLiteResult ); + if ( !driver() || !driver()->isOpen() || driver()->isOpenError() ) + return false; - const void *pzTail = nullptr; + d->cleanup(); -#if (SQLITE_VERSION_NUMBER >= 3003011) - int res = sqlite3_prepare16_v2(d->access, query.constData(), (query.size() + 1) * sizeof(QChar), - &d->stmt, &pzTail); -#else - int res = sqlite3_prepare16(d->access, query.constData(), (query.size() + 1) * sizeof(QChar), - &d->stmt, &pzTail); -#endif + setSelect( false ); - if (res != SQLITE_OK) { - setLastError(qMakeError(d->access, QCoreApplication::translate("QSpatiaLiteResult", - "Unable to execute statement"), QSqlError::StatementError, res)); - d->finalize(); - return false; - } else if (pzTail && !QString::fromUtf16( (const ushort *) pzTail ).trimmed().isEmpty()) { - setLastError(qMakeError(d->access, QCoreApplication::translate("QSpatiaLiteResult", - "Unable to execute multiple statements at a time"), QSqlError::StatementError, SQLITE_MISUSE)); - d->finalize(); - return false; - } - return true; + int res; + d->stmt = d->drv_d_func()->access.prepare( query, res ); + if ( res != SQLITE_OK ) + { + setLastError( qMakeError( d->drv_d_func()->access, QCoreApplication::translate( "QSpatiaLiteResult", + "Unable to execute statement" ), QSqlError::StatementError, res ) ); + d->finalize(); + return false; + } + return true; } -bool QSpatiaLiteResult::exec() +static QString secondsToOffset( int seconds ) { - const QVector values = boundValues(); + const QChar sign = ushort( seconds < 0 ? '-' : '+' ); + seconds = qAbs( seconds ); + const int hours = seconds / 3600; + const int minutes = ( seconds % 3600 ) / 60; - d->skippedStatus = false; - d->skipRow = false; - d->rInf.clear(); - clearValues(); - setLastError(QSqlError()); + return QString( QStringLiteral( "%1%2:%3" ) ).arg( sign ).arg( hours, 2, 10, QLatin1Char( '0' ) ).arg( minutes, 2, 10, QLatin1Char( '0' ) ); +} - int res = sqlite3_reset(d->stmt); - if (res != SQLITE_OK) { - setLastError(qMakeError(d->access, QCoreApplication::translate("QSpatiaLiteResult", - "Unable to reset statement"), QSqlError::StatementError, res)); - d->finalize(); - return false; - } - int paramCount = sqlite3_bind_parameter_count(d->stmt); - if (paramCount == values.count()) { - for (int i = 0; i < paramCount; ++i) { - res = SQLITE_OK; - const QVariant value = values.at(i); - - if (value.isNull()) { - res = sqlite3_bind_null(d->stmt, i + 1); - } else { - switch (value.type()) { - case QVariant::ByteArray: { - const QByteArray *ba = static_cast(value.constData()); - res = sqlite3_bind_blob(d->stmt, i + 1, ba->constData(), - ba->size(), SQLITE_STATIC); - break; } - case QVariant::Int: - res = sqlite3_bind_int(d->stmt, i + 1, value.toInt()); - break; - case QVariant::Double: - res = sqlite3_bind_double(d->stmt, i + 1, value.toDouble()); - break; - case QVariant::UInt: - case QVariant::LongLong: - res = sqlite3_bind_int64(d->stmt, i + 1, value.toLongLong()); - break; - case QVariant::String: { - // lifetime of string == lifetime of its qvariant - const QString *str = static_cast(value.constData()); - res = sqlite3_bind_text16(d->stmt, i + 1, str->utf16(), - (str->size()) * sizeof(QChar), SQLITE_STATIC); - break; } - default: { - 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); - break; } - } - } - if (res != SQLITE_OK) { - setLastError(qMakeError(d->access, QCoreApplication::translate("QSpatiaLiteResult", - "Unable to bind parameters"), QSqlError::StatementError, res)); - d->finalize(); - return false; - } +static QString timespecToString( const QDateTime &dateTime ) +{ + switch ( dateTime.timeSpec() ) + { + case Qt::LocalTime: + return QString(); + case Qt::UTC: + return QStringLiteral( "Z" ); + case Qt::OffsetFromUTC: + return secondsToOffset( dateTime.offsetFromUtc() ); + case Qt::TimeZone: + return secondsToOffset( dateTime.timeZone().offsetFromUtc( dateTime ) ); + default: + return QString(); + } +} + +bool QSpatiaLiteResult::exec() +{ + Q_D( QSpatiaLiteResult ); + const QVector values = boundValues(); + + d->skippedStatus = false; + d->skipRow = false; + d->rInf.clear(); + clearValues(); + setLastError( QSqlError() ); + + int res = sqlite3_reset( d->stmt.get() ); + if ( res != SQLITE_OK ) + { + setLastError( qMakeError( d->drv_d_func()->access, QCoreApplication::translate( "QSpatiaiteResult", + "Unable to reset statement" ), QSqlError::StatementError, res ) ); + d->finalize(); + return false; + } + int paramCount = sqlite3_bind_parameter_count( d->stmt.get() ); + if ( paramCount == values.count() ) + { + for ( int i = 0; i < paramCount; ++i ) + { + res = SQLITE_OK; + const QVariant value = values.at( i ); + + if ( value.isNull() ) + { + res = sqlite3_bind_null( d->stmt.get(), i + 1 ); + } + else + { + switch ( value.type() ) + { + case QVariant::ByteArray: + { + const QByteArray *ba = static_cast( value.constData() ); + res = sqlite3_bind_blob( d->stmt.get(), i + 1, ba->constData(), + ba->size(), SQLITE_STATIC ); + break; + } + case QVariant::Int: + case QVariant::Bool: + res = sqlite3_bind_int( d->stmt.get(), i + 1, value.toInt() ); + break; + case QVariant::Double: + res = sqlite3_bind_double( d->stmt.get(), i + 1, value.toDouble() ); + break; + case QVariant::UInt: + case QVariant::LongLong: + res = sqlite3_bind_int64( d->stmt.get(), i + 1, value.toLongLong() ); + break; + case QVariant::DateTime: + { + const QDateTime dateTime = value.toDateTime(); + const QString str = dateTime.toString( QStringLiteral( "yyyy-MM-ddThh:mm:ss.zzz" ) + timespecToString( dateTime ) ); + res = sqlite3_bind_text16( d->stmt.get(), i + 1, str.utf16(), + str.size() * sizeof( ushort ), SQLITE_TRANSIENT ); + break; + } + case QVariant::Time: + { + const QTime time = value.toTime(); + const QString str = time.toString( QStringLiteral( "hh:mm:ss.zzz" ) ); + res = sqlite3_bind_text16( d->stmt.get(), i + 1, str.utf16(), + str.size() * sizeof( ushort ), SQLITE_TRANSIENT ); + break; + } + case QVariant::String: + { + // lifetime of string == lifetime of its qvariant + const QString *str = static_cast( value.constData() ); + res = sqlite3_bind_text16( d->stmt.get(), i + 1, str->utf16(), + ( str->size() ) * sizeof( QChar ), SQLITE_STATIC ); + break; + } + default: + { + QString str = value.toString(); + // SQLITE_TRANSIENT makes sure that sqlite buffers the data + res = sqlite3_bind_text16( d->stmt.get(), i + 1, str.utf16(), + ( str.size() ) * sizeof( QChar ), SQLITE_TRANSIENT ); + break; + } } - } else { - setLastError(QSqlError(QCoreApplication::translate("QSpatiaLiteResult", - "Parameter count mismatch"), QString(), QSqlError::StatementError)); - return false; - } - d->skippedStatus = d->fetchNext(d->firstRow, 0, true); - if (lastError().isValid()) { - setSelect(false); - setActive(false); + } + if ( res != SQLITE_OK ) + { + setLastError( qMakeError( d->drv_d_func()->access, QCoreApplication::translate( "QSpatiaLiteResult", + "Unable to bind parameters" ), QSqlError::StatementError, res ) ); + d->finalize(); return false; + } } - setSelect(!d->rInf.isEmpty()); - setActive(true); - return true; + } + else + { + setLastError( QSqlError( QCoreApplication::translate( "QSpatiaLiteResult", + "Parameter count mismatch" ), QString(), QSqlError::StatementError ) ); + return false; + } + d->skippedStatus = d->fetchNext( d->firstRow, 0, true ); + if ( lastError().isValid() ) + { + setSelect( false ); + setActive( false ); + return false; + } + setSelect( !d->rInf.isEmpty() ); + setActive( true ); + return true; } -bool QSpatiaLiteResult::gotoNext(QSqlCachedResult::ValueCache& row, int idx) +bool QSpatiaLiteResult::gotoNext( QSqlCachedResult::ValueCache &row, int idx ) { - return d->fetchNext(row, idx, false); + Q_D( QSpatiaLiteResult ); + return d->fetchNext( row, idx, false ); } int QSpatiaLiteResult::size() { - return -1; + return -1; } int QSpatiaLiteResult::numRowsAffected() { - return sqlite3_changes(d->access); + Q_D( const QSpatiaLiteResult ); + return sqlite3_changes( d->drv_d_func()->access.get() ); } QVariant QSpatiaLiteResult::lastInsertId() const { - if (isActive()) { - qint64 id = sqlite3_last_insert_rowid(d->access); - if (id) - return id; - } - return QVariant(); + Q_D( const QSpatiaLiteResult ); + if ( isActive() ) + { + qint64 id = sqlite3_last_insert_rowid( d->drv_d_func()->access.get() ); + if ( id ) + return id; + } + return QVariant(); } QSqlRecord QSpatiaLiteResult::record() const { - if (!isActive() || !isSelect()) - return QSqlRecord(); - return d->rInf; + Q_D( const QSpatiaLiteResult ); + if ( !isActive() || !isSelect() ) + return QSqlRecord(); + return d->rInf; } void QSpatiaLiteResult::detachFromResultSet() { - if (d->stmt) - sqlite3_reset(d->stmt); -} - -QVariant QSpatiaLiteResult::handle() const -{ - return QVariant::fromValue(d->stmt); + Q_D( QSpatiaLiteResult ); + if ( d->stmt ) + sqlite3_reset( d->stmt.get() ); } ///////////////////////////////////////////////////////// -QSpatiaLiteDriver::QSpatiaLiteDriver(QObject * parent) - : QSqlDriver(parent) +QSpatiaLiteDriver::QSpatiaLiteDriver( QObject *parent ) + : QSqlDriver( *new QSpatiaLiteDriverPrivate, parent ) { - d = new QSpatiaLiteDriverPrivate(); } QSpatiaLiteDriver::~QSpatiaLiteDriver() { - delete d; + close(); } -bool QSpatiaLiteDriver::hasFeature(DriverFeature f) const +bool QSpatiaLiteDriver::hasFeature( DriverFeature f ) const { - switch (f) { + switch ( f ) + { case BLOB: case Transactions: case Unicode: @@ -511,225 +602,332 @@ bool QSpatiaLiteDriver::hasFeature(DriverFeature f) const case SimpleLocking: case FinishQuery: case LowPrecisionNumbers: - return true; + case EventNotifications: + return true; case QuerySize: case NamedPlaceholders: case BatchOperations: - case EventNotifications: case MultipleResultSets: case CancelQuery: - return false; - } - return false; + return false; + } + return false; } /* SQLite dbs have no user name, passwords, hosts or ports. just file names. */ -bool QSpatiaLiteDriver::open(const QString & db, const QString &, const QString &, const QString &, int, const QString &conOpts) -{ - if (isOpen()) - close(); - - if (db.isEmpty()) - return false; - - bool sharedCache = false; - int openMode = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, timeOut=5000; - QStringList opts=QString(conOpts).remove(QLatin1Char(' ')).split(QLatin1Char(';')); - foreach(const QString &option, opts) { - if (option.startsWith(QLatin1String("QSQLITE_BUSY_TIMEOUT="))) { - bool ok; - int nt = option.mid(21).toInt(&ok); - if (ok) - timeOut = nt; - } - if (option == QLatin1String("QSQLITE_OPEN_READONLY")) - openMode = SQLITE_OPEN_READONLY; - if (option == QLatin1String("QSQLITE_ENABLE_SHARED_CACHE")) - sharedCache = true; +bool QSpatiaLiteDriver::open( const QString &db, const QString &, const QString &, const QString &, int, const QString &conOpts ) +{ + Q_D( QSpatiaLiteDriver ); + if ( isOpen() ) + close(); + + + int timeOut = 5000; + bool sharedCache = false; + bool openReadOnlyOption = false; + bool openUriOption = false; + + const auto opts = conOpts.splitRef( QLatin1Char( ';' ) ); + for ( auto option : opts ) + { + option = option.trimmed(); + if ( option.startsWith( QLatin1String( "QSQLITE_BUSY_TIMEOUT" ) ) ) + { + option = option.mid( 20 ).trimmed(); + if ( option.startsWith( QLatin1Char( '=' ) ) ) + { + bool ok; + const int nt = option.mid( 1 ).trimmed().toInt( &ok ); + if ( ok ) + timeOut = nt; + } + } + else if ( option == QLatin1String( "QSQLITE_OPEN_READONLY" ) ) + { + openReadOnlyOption = true; + } + else if ( option == QLatin1String( "QSQLITE_OPEN_URI" ) ) + { + openUriOption = true; } + else if ( option == QLatin1String( "QSQLITE_ENABLE_SHARED_CACHE" ) ) + { + sharedCache = true; + } + } - sqlite3_enable_shared_cache(sharedCache); + int openMode = ( openReadOnlyOption ? SQLITE_OPEN_READONLY : ( SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE ) ); + if ( openUriOption ) + openMode |= SQLITE_OPEN_URI; - spatialite_database_unique_ptr database; + sqlite3_enable_shared_cache( sharedCache ); + if ( d->access.open_v2( db.toUtf8().constData(), openMode, nullptr ) == SQLITE_OK ) + { + sqlite3_busy_timeout( d->access.get(), timeOut ); + setOpen( true ); + setOpenError( false ); - if (d->access.open_v2(db, openMode, nullptr ) == SQLITE_OK) { - sqlite3_busy_timeout(d->access.get(), timeOut); - setOpen(true); - setOpenError(false); - return true; - } else { - setLastError(qMakeError(d->access.get(), tr("Error opening database"), - QSqlError::ConnectionError)); - setOpenError(true); - return false; - } + return true; + } + else + { + setLastError( qMakeError( d->access, tr( "Error opening database" ), + QSqlError::ConnectionError ) ); + setOpenError( true ); + return false; + } } void QSpatiaLiteDriver::close() { - if (isOpen()) { - foreach (QSpatiaLiteResult *result, d->results) - result->d->finalize(); + Q_D( QSpatiaLiteDriver ); + if ( isOpen() ) + { + for ( QSpatiaLiteResult *result : qAsConst( d->results ) ) + result->d_func()->finalize(); - d->access.reset(); - setOpen(false); - setOpenError(false); + if ( d->access && ( d->notificationid.count() > 0 ) ) + { + d->notificationid.clear(); + sqlite3_update_hook( d->access.get(), NULL, NULL ); } + + d->access.reset(); + + setOpen( false ); + setOpenError( false ); + } } QSqlResult *QSpatiaLiteDriver::createResult() const { - return new QSpatiaLiteResult(this); + return new QSpatiaLiteResult( this ); } bool QSpatiaLiteDriver::beginTransaction() { - if (!isOpen() || isOpenError()) - return false; + if ( !isOpen() || isOpenError() ) + return false; - QSqlQuery q(createResult()); - if (!q.exec(QLatin1String("BEGIN"))) { - setLastError(QSqlError(tr("Unable to begin transaction"), - q.lastError().databaseText(), QSqlError::TransactionError)); - return false; - } + QSqlQuery q( createResult() ); + if ( !q.exec( QLatin1String( "BEGIN" ) ) ) + { + setLastError( QSqlError( tr( "Unable to begin transaction" ), + q.lastError().databaseText(), QSqlError::TransactionError ) ); + return false; + } - return true; + return true; } bool QSpatiaLiteDriver::commitTransaction() { - if (!isOpen() || isOpenError()) - return false; + if ( !isOpen() || isOpenError() ) + return false; - QSqlQuery q(createResult()); - if (!q.exec(QLatin1String("COMMIT"))) { - setLastError(QSqlError(tr("Unable to commit transaction"), - q.lastError().databaseText(), QSqlError::TransactionError)); - return false; - } + QSqlQuery q( createResult() ); + if ( !q.exec( QLatin1String( "COMMIT" ) ) ) + { + setLastError( QSqlError( tr( "Unable to commit transaction" ), + q.lastError().databaseText(), QSqlError::TransactionError ) ); + return false; + } - return true; + return true; } bool QSpatiaLiteDriver::rollbackTransaction() { - if (!isOpen() || isOpenError()) - return false; + if ( !isOpen() || isOpenError() ) + return false; - QSqlQuery q(createResult()); - if (!q.exec(QLatin1String("ROLLBACK"))) { - setLastError(QSqlError(tr("Unable to rollback transaction"), - q.lastError().databaseText(), QSqlError::TransactionError)); - return false; - } + QSqlQuery q( createResult() ); + if ( !q.exec( QLatin1String( "ROLLBACK" ) ) ) + { + setLastError( QSqlError( tr( "Unable to rollback transaction" ), + q.lastError().databaseText(), QSqlError::TransactionError ) ); + return false; + } - return true; + return true; } -QStringList QSpatiaLiteDriver::tables(QSql::TableType type) const +QStringList QSpatiaLiteDriver::tables( QSql::TableType type ) const { - QStringList res; - if (!isOpen()) - return res; + QStringList res; + if ( !isOpen() ) + return res; - QSqlQuery q(createResult()); - q.setForwardOnly(true); + 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" ); + if ( ( type & QSql::Tables ) && ( type & QSql::Views ) ) + sql = sql.arg( QLatin1String( "type='table' OR type='view'" ) ); + else if ( type & QSql::Tables ) + sql = sql.arg( QLatin1String( "type='table'" ) ); + else if ( type & QSql::Views ) + sql = sql.arg( QLatin1String( "type='view'" ) ); + else + sql.clear(); - QString sql = QLatin1String("SELECT name FROM sqlite_master WHERE %1 " - "UNION ALL SELECT name FROM sqlite_temp_master WHERE %1"); - if ((type & QSql::Tables) && (type & QSql::Views)) - sql = sql.arg(QLatin1String("type='table' OR type='view'")); - else if (type & QSql::Tables) - sql = sql.arg(QLatin1String("type='table'")); - else if (type & QSql::Views) - sql = sql.arg(QLatin1String("type='view'")); - else - sql.clear(); + if ( !sql.isEmpty() && q.exec( sql ) ) + { + while ( q.next() ) + res.append( q.value( 0 ).toString() ); + } - if (!sql.isEmpty() && q.exec(sql)) { - while(q.next()) - res.append(q.value(0).toString()); - } + if ( type & QSql::SystemTables ) + { + // there are no internal tables beside this one: + res.append( QLatin1String( "sqlite_master" ) ); + } - if (type & QSql::SystemTables) { - // there are no internal tables beside this one: - res.append(QLatin1String("sqlite_master")); - } + return res; +} - 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(); + QSqlField fld( q.value( 1 ).toString(), qGetColumnType( typeName ) ); + 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( q.value( 4 ) ); + ind.append( fld ); + } + return ind; } -static QSqlIndex qGetTableInfo(QSqlQuery &q, const QString &tableName, bool onlyPIndex = false) +QSqlIndex QSpatiaLiteDriver::primaryIndex( const QString &tblname ) const { - 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) + QLatin1String(")")); - - QSqlIndex ind; - while (q.next()) { - bool isPk = q.value(5).toInt(); - if (onlyPIndex && !isPk) - continue; - QString typeName = q.value(2).toString().toLower(); - QSqlField fld(q.value(1).toString(), qGetColumnType(typeName)); - 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(q.value(4)); - ind.append(fld); - } - return ind; + 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 ); +} + +QSqlRecord QSpatiaLiteDriver::record( const QString &tbl ) const +{ + 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 ); +} + +QString QSpatiaLiteDriver::escapeIdentifier( const QString &identifier, IdentifierType type ) const +{ + Q_UNUSED( type ); + return _q_escapeIdentifier( identifier ); } -QSqlIndex QSpatiaLiteDriver::primaryIndex(const QString &tblname) const +static void handle_sqlite_callback( void *qobj, int aoperation, char const *adbname, char const *atablename, + sqlite3_int64 arowid ) { - if (!isOpen()) - return QSqlIndex(); + Q_UNUSED( aoperation ); + Q_UNUSED( adbname ); + QSpatiaLiteDriver *driver = static_cast( qobj ); + if ( driver ) + { + QMetaObject::invokeMethod( driver, "handleNotification", Qt::QueuedConnection, + Q_ARG( QString, QString::fromUtf8( atablename ) ), Q_ARG( qint64, arowid ) ); + } +} - QString table = tblname; - if (isIdentifierEscaped(table, QSqlDriver::TableName)) - table = stripDelimiters(table, QSqlDriver::TableName); +bool QSpatiaLiteDriver::subscribeToNotification( const QString &name ) +{ + Q_D( QSpatiaLiteDriver ); + if ( !isOpen() ) + { + qWarning( "Database not open." ); + return false; + } + + if ( d->notificationid.contains( name ) ) + { + qWarning( "Already subscribing to '%s'.", qPrintable( name ) ); + return false; + } - QSqlQuery q(createResult()); - q.setForwardOnly(true); - return qGetTableInfo(q, table, true); + //sqlite supports only one notification callback, so only the first is registered + d->notificationid << name; + if ( d->notificationid.count() == 1 ) + sqlite3_update_hook( d->access.get(), &handle_sqlite_callback, reinterpret_cast( this ) ); + + return true; } -QSqlRecord QSpatiaLiteDriver::record(const QString &tbl) const +bool QSpatiaLiteDriver::unsubscribeFromNotification( const QString &name ) { - if (!isOpen()) - return QSqlRecord(); + Q_D( QSpatiaLiteDriver ); + if ( !isOpen() ) + { + qWarning( "Database not open." ); + return false; + } + + if ( !d->notificationid.contains( name ) ) + { + qWarning( "Not subscribed to '%s'.", qPrintable( name ) ); + return false; + } - QString table = tbl; - if (isIdentifierEscaped(table, QSqlDriver::TableName)) - table = stripDelimiters(table, QSqlDriver::TableName); + d->notificationid.removeAll( name ); + if ( d->notificationid.isEmpty() ) + sqlite3_update_hook( d->access.get(), NULL, NULL ); - QSqlQuery q(createResult()); - q.setForwardOnly(true); - return qGetTableInfo(q, table); + return true; } -QVariant QSpatiaLiteDriver::handle() const +QStringList QSpatiaLiteDriver::subscribedToNotifications() const { - return QVariant::fromValue(d->access.get()); + Q_D( const QSpatiaLiteDriver ); + return d->notificationid; } -QString QSpatiaLiteDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const +void QSpatiaLiteDriver::handleNotification( const QString &tableName, qint64 rowid ) { - Q_UNUSED(type); - return _q_escapeIdentifier(identifier); + Q_D( const QSpatiaLiteDriver ); + if ( d->notificationid.contains( tableName ) ) + { + emit notification( tableName ); + emit notification( tableName, QSqlDriver::UnknownSource, QVariant( rowid ) ); + } } QT_END_NAMESPACE diff --git a/external/qspatialite/qsql_spatialite.h b/external/qspatialite/qsql_spatialite.h index fec8fb49f509..e0cdff4f6bf4 100644 --- a/external/qspatialite/qsql_spatialite.h +++ b/external/qspatialite/qsql_spatialite.h @@ -1,7 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal +** 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. ** @@ -10,115 +10,88 @@ ** 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** 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 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** 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 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** +** 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$ ** ****************************************************************************/ -#ifndef QSQL_SQLITE_H -#define QSQL_SQLITE_H +#ifndef QSQL_SPATIALITE_H +#define QSQL_SPATIALITE_H #include -#include - -#include "qsqlcachedresult_p.h" struct sqlite3; #ifdef QT_PLUGIN -#define Q_EXPORT_SQLDRIVER_SQLITE +#define Q_EXPORT_SQLDRIVER_SPATIALITE #else -#define Q_EXPORT_SQLDRIVER_SQLITE Q_SQL_EXPORT +#define Q_EXPORT_SQLDRIVER_SPATIALITE Q_SQL_EXPORT #endif -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE -class QSpatiaLiteDriverPrivate; -class QSpatiaLiteResultPrivate; -class QSpatiaLiteDriver; - -class QSpatiaLiteResult : public QSqlCachedResult -{ - friend class QSpatiaLiteDriver; - friend class QSpatiaLiteResultPrivate; -public: - explicit QSpatiaLiteResult(const QSpatiaLiteDriver* db); - ~QSpatiaLiteResult(); - QVariant handle() const; -protected: - bool gotoNext(QSqlCachedResult::ValueCache& row, int idx); - bool reset(const QString &query); - bool prepare(const QString &query); - bool exec(); - int size(); - int numRowsAffected(); - QVariant lastInsertId() const; - QSqlRecord record() const; - void detachFromResultSet(); - -private: - QSpatiaLiteResultPrivate* d; -}; +class QSqlResult; +class QSpatiaLiteResultPrivate; +class QSpatiaLiteDriverPrivate; +class spatialite_database_unique_ptr; -class Q_EXPORT_SQLDRIVER_SQLITE QSpatiaLiteDriver : public QSqlDriver +class Q_EXPORT_SQLDRIVER_SPATIALITE QSpatiaLiteDriver : public QSqlDriver { + Q_DECLARE_PRIVATE( QSpatiaLiteDriver ) Q_OBJECT - friend class QSpatiaLiteResult; -public: - explicit QSpatiaLiteDriver(QObject *parent = nullptr); - - ~QSpatiaLiteDriver(); - bool hasFeature(DriverFeature f) const; - bool open(const QString & db, - const QString & user, - const QString & password, - const QString & host, - int port, - const QString & connOpts); - void close(); - QSqlResult *createResult() const; - bool beginTransaction(); - bool commitTransaction(); - bool rollbackTransaction(); - QStringList tables(QSql::TableType) const; - - QSqlRecord record(const QString& tablename) const; - QSqlIndex primaryIndex(const QString &table) const; - QVariant handle() const; - QString escapeIdentifier(const QString &identifier, IdentifierType) const; + friend class QSpatiaLiteResultPrivate; -private: - QSpatiaLiteDriverPrivate* d; + public: + explicit QSpatiaLiteDriver( QObject *parent = nullptr ); + ~QSpatiaLiteDriver() override; + bool hasFeature( DriverFeature f ) const override; + bool open( const QString &db, + const QString &user, + const QString &password, + const QString &host, + int port, + const QString &connOpts ) override; + void close() override; + QSqlResult *createResult() const override; + bool beginTransaction() override; + bool commitTransaction() override; + bool rollbackTransaction() override; + QStringList tables( QSql::TableType ) const override; + + QSqlRecord record( const QString &tablename ) const override; + QSqlIndex primaryIndex( const QString &tablename ) const override; + QString escapeIdentifier( const QString &identifier, IdentifierType ) const override; + + bool subscribeToNotification( const QString &name ) Q_DECL_OVERRIDE; + bool unsubscribeFromNotification( const QString &name ) Q_DECL_OVERRIDE; + QStringList subscribedToNotifications() const Q_DECL_OVERRIDE; + + private slots: + void handleNotification( const QString &tableName, qint64 rowid ); }; QT_END_NAMESPACE -QT_END_HEADER - -#endif // QSQL_SQLITE_H +#endif // QSQL_SPATIALITE_H diff --git a/external/qspatialite/qsqlcachedresult_p.h b/external/qspatialite/qsqlcachedresult_p.h deleted file mode 100644 index 445f35c74fe2..000000000000 --- a/external/qspatialite/qsqlcachedresult_p.h +++ /dev/null @@ -1,100 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSQLCACHEDRESULT_P_H -#define QSQLCACHEDRESULT_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of other Qt classes. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "QtSql/qsqlresult.h" - -QT_BEGIN_NAMESPACE - -class QVariant; -template class QVector; - -class QSqlCachedResultPrivate; - -class /* Q_SQL_EXPORT */ QSqlCachedResult: public QSqlResult -{ - public: - virtual ~QSqlCachedResult() {} - - typedef QVector ValueCache; - - protected: - QSqlCachedResult( const QSqlDriver * db ) : QSqlResult(db) {} - - void init( int colCount ); - void cleanup(); - void clearValues(); - - virtual bool gotoNext( ValueCache &values, int index ) = 0; - - QVariant data( int i ); - bool isNull( int i ); - bool fetch( int i ); - bool fetchNext(); - bool fetchPrevious(); - bool fetchFirst(); - bool fetchLast(); - - int colCount() const; - ValueCache &cache(); - - void virtual_hook( int id, void *data ); - private: - bool cacheNext(); - QSqlCachedResultPrivate *d; -}; - -QT_END_NAMESPACE - -#endif // QSQLCACHEDRESULT_P_H diff --git a/external/qspatialite/smain.cpp b/external/qspatialite/smain.cpp index a6b67504e886..212474a4f320 100644 --- a/external/qspatialite/smain.cpp +++ b/external/qspatialite/smain.cpp @@ -40,25 +40,19 @@ ****************************************************************************/ #include "smain.h" +#include #include "qsql_spatialite.h" QSpatiaLiteDriverPlugin::QSpatiaLiteDriverPlugin() - : QSqlDriverPlugin() { } -QSqlDriver* QSpatiaLiteDriverPlugin::create(const QString &name) +QSqlDriver *QSpatiaLiteDriverPlugin::create( const QString &name ) { - if (name == QLatin1String("QSPATIALITE")) { - QSpatiaLiteDriver* driver = new QSpatiaLiteDriver(); - return driver; - } - return 0; -} - -QStringList QSpatiaLiteDriverPlugin::keys() const -{ - QStringList l; - l << QLatin1String("QSPATIALITE"); - return l; + if ( name == QLatin1String( "QSPATIALITE" ) ) + { + QSpatiaLiteDriver *driver = new QSpatiaLiteDriver(); + return driver; + } + return nullptr; } diff --git a/external/qspatialite/smain.h b/external/qspatialite/smain.h index ffc76675d2f6..98bd235d573a 100644 --- a/external/qspatialite/smain.h +++ b/external/qspatialite/smain.h @@ -45,15 +45,15 @@ #include #include - class QSpatiaLiteDriverPlugin : public QSqlDriverPlugin { Q_OBJECT - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QSqlDriverFactoryInterface" FILE "qspatialite.json") + Q_PLUGIN_METADATA( IID "org.qt-project.Qt.QSqlDriverFactoryInterface" FILE "qspatialite.json" ) + public: QSpatiaLiteDriverPlugin(); - QSqlDriver* create( const QString & ); + QSqlDriver *create( const QString & ) override; QStringList keys() const; }; diff --git a/src/providers/oracle/ocispatial/qsqlcachedresult_p.h b/src/providers/oracle/ocispatial/qsqlcachedresult_p.h deleted file mode 100644 index 23da250649c9..000000000000 --- a/src/providers/oracle/ocispatial/qsqlcachedresult_p.h +++ /dev/null @@ -1,100 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSQLCACHEDRESULT_P_H -#define QSQLCACHEDRESULT_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of other Qt classes. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "QtSql/qsqlresult.h" - -QT_BEGIN_NAMESPACE - -class QVariant; -template class QVector; - -class QSqlCachedResultPrivate; - -class /* Q_SQL_EXPORT */ QSqlCachedResult: public QSqlResult -{ - public: - virtual ~QSqlCachedResult() {} - - typedef QVector ValueCache; - - protected: - QSqlCachedResult( const QSqlDriver *db ) : QSqlResult( db ) {} - - void init( int colCount ); - void cleanup(); - void clearValues(); - - virtual bool gotoNext( ValueCache &values, int index ) = 0; - - QVariant data( int i ); - bool isNull( int i ); - bool fetch( int i ); - bool fetchNext(); - bool fetchPrevious(); - bool fetchFirst(); - bool fetchLast(); - - int colCount() const; - ValueCache &cache(); - - void virtual_hook( int id, void *data ); - private: - bool cacheNext(); - QSqlCachedResultPrivate *d = nullptr; -}; - -QT_END_NAMESPACE - -#endif // QSQLCACHEDRESULT_P_H