2013-02-17 22:14:13 +01:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
2018-07-16 01:38:05 +02:00
|
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
2013-02-17 22:14:13 +01:00
|
|
|
**
|
|
|
|
** 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
|
2018-07-16 01:38:05 +02:00
|
|
|
** 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.
|
2013-02-17 22:14:13 +01:00
|
|
|
**
|
|
|
|
** GNU Lesser General Public License Usage
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
2018-07-16 01:38:05 +02:00
|
|
|
** 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.
|
2013-02-17 22:14:13 +01:00
|
|
|
**
|
|
|
|
** GNU General Public License Usage
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
2018-07-16 01:38:05 +02:00
|
|
|
** 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.
|
2013-02-17 22:14:13 +01:00
|
|
|
**
|
|
|
|
** $QT_END_LICENSE$
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include "qsql_spatialite.h"
|
|
|
|
|
|
|
|
#include <qcoreapplication.h>
|
2018-07-16 01:38:05 +02:00
|
|
|
#include <qdatetime.h>
|
2013-02-17 22:14:13 +01:00
|
|
|
#include <qvariant.h>
|
|
|
|
#include <qsqlerror.h>
|
|
|
|
#include <qsqlfield.h>
|
|
|
|
#include <qsqlindex.h>
|
|
|
|
#include <qsqlquery.h>
|
2018-07-16 01:38:05 +02:00
|
|
|
#include <QtSql/private/qsqlcachedresult_p.h>
|
|
|
|
#include <QtSql/private/qsqldriver_p.h>
|
2013-02-17 22:14:13 +01:00
|
|
|
#include <qstringlist.h>
|
|
|
|
#include <qvector.h>
|
|
|
|
#include <qdebug.h>
|
2018-07-16 01:38:05 +02:00
|
|
|
#include <QTimeZone>
|
|
|
|
|
2017-11-14 08:14:51 +01:00
|
|
|
#include "qgsspatialiteutils.h"
|
2013-02-17 22:14:13 +01:00
|
|
|
|
|
|
|
#if defined Q_OS_WIN
|
|
|
|
# include <qt_windows.h>
|
|
|
|
#else
|
|
|
|
# include <unistd.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <sqlite3.h>
|
2018-07-16 01:38:05 +02:00
|
|
|
#include <functional>
|
2013-02-17 22:14:13 +01:00
|
|
|
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
static QString _q_escapeIdentifier( const QString &identifier )
|
2013-02-17 22:14:13 +01:00
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
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;
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
static QVariant::Type qGetColumnType( const QString &tpName )
|
2013-02-17 22:14:13 +01:00
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
const QString typeName = tpName.toLower();
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
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;
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
static QSqlError qMakeError( const spatialite_database_unique_ptr &access, const QString &descr, QSqlError::ErrorType type,
|
|
|
|
int errorCode = -1 )
|
2013-02-17 22:14:13 +01:00
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
return QSqlError( descr, access.errorMessage(), type, QString::number( errorCode ) );
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
class QSpatiaLiteResultPrivate;
|
|
|
|
|
|
|
|
class QSpatiaLiteResult : public QSqlCachedResult
|
2013-02-17 22:14:13 +01:00
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
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; }
|
2017-11-14 08:14:51 +01:00
|
|
|
spatialite_database_unique_ptr access;
|
2013-02-17 22:14:13 +01:00
|
|
|
QList <QSpatiaLiteResult *> results;
|
2018-07-16 01:38:05 +02:00
|
|
|
QStringList notificationid;
|
2013-02-17 22:14:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
class QSpatiaLiteResultPrivate: public QSqlCachedResultPrivate
|
2013-02-17 22:14:13 +01:00
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
Q_DECLARE_PUBLIC( QSpatiaLiteResult )
|
|
|
|
|
|
|
|
public:
|
|
|
|
Q_DECLARE_SQLDRIVER_PRIVATE( QSpatiaLiteDriver )
|
|
|
|
QSpatiaLiteResultPrivate( QSpatiaLiteResult *q, const QSpatiaLiteDriver *drv );
|
2013-02-17 22:14:13 +01:00
|
|
|
void cleanup();
|
2018-07-16 01:38:05 +02:00
|
|
|
bool fetchNext( QSqlCachedResult::ValueCache &values, int idx, bool initialFetch );
|
2013-02-17 22:14:13 +01:00
|
|
|
// initializes the recordInfo and the cache
|
2018-07-16 01:38:05 +02:00
|
|
|
void initColumns( bool emptyResultset );
|
2013-02-17 22:14:13 +01:00
|
|
|
void finalize();
|
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
sqlite3_statement_unique_ptr stmt;
|
2013-02-17 22:14:13 +01:00
|
|
|
|
|
|
|
bool skippedStatus; // the status of the fetchNext() that's skipped
|
|
|
|
bool skipRow; // skip the next fetchNext()?
|
|
|
|
QSqlRecord rInf;
|
|
|
|
QVector<QVariant> firstRow;
|
|
|
|
};
|
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
QSpatiaLiteResultPrivate::QSpatiaLiteResultPrivate( QSpatiaLiteResult *q, const QSpatiaLiteDriver *drv )
|
|
|
|
: QSqlCachedResultPrivate( q, drv )
|
|
|
|
, skippedStatus( false )
|
|
|
|
, skipRow( false )
|
2013-02-17 22:14:13 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void QSpatiaLiteResultPrivate::cleanup()
|
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
Q_Q( QSpatiaLiteResult );
|
|
|
|
finalize();
|
|
|
|
rInf.clear();
|
|
|
|
skippedStatus = false;
|
|
|
|
skipRow = false;
|
|
|
|
q->setAt( QSql::BeforeFirstRow );
|
|
|
|
q->setActive( false );
|
|
|
|
q->cleanup();
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void QSpatiaLiteResultPrivate::finalize()
|
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
stmt.reset();
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
void QSpatiaLiteResultPrivate::initColumns( bool emptyResultset )
|
2013-02-17 22:14:13 +01:00
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
Q_Q( QSpatiaLiteResult );
|
|
|
|
int nCols = stmt.columnCount();
|
|
|
|
if ( nCols <= 0 )
|
|
|
|
return;
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
q->init( nCols );
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
for ( int i = 0; i < nCols; ++i )
|
|
|
|
{
|
|
|
|
QString colName = stmt.columnName( i ).remove( QLatin1Char( '"' ) );
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
// must use typeName for resolving the type to match QSqliteDriver::record
|
|
|
|
QString typeName = QString( reinterpret_cast<const QChar *>(
|
|
|
|
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 );
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
QVariant::Type fieldType;
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
if ( !typeName.isEmpty() )
|
|
|
|
{
|
|
|
|
fieldType = qGetColumnType( typeName );
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
2018-07-16 01:38:05 +02:00
|
|
|
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 );
|
|
|
|
}
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
bool QSpatiaLiteResultPrivate::fetchNext( QSqlCachedResult::ValueCache &values, int idx, bool initialFetch )
|
2013-02-17 22:14:13 +01:00
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
Q_Q( QSpatiaLiteResult );
|
|
|
|
int res;
|
|
|
|
int i;
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
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();
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
switch ( res )
|
|
|
|
{
|
2013-02-17 22:14:13 +01:00
|
|
|
case SQLITE_ROW:
|
2018-07-16 01:38:05 +02:00
|
|
|
// 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<const char *>(
|
|
|
|
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 );
|
2013-02-17 22:14:13 +01:00
|
|
|
break;
|
2018-07-16 01:38:05 +02:00
|
|
|
case QSql::LowPrecisionInt64:
|
|
|
|
values[i + idx] = stmt.columnAsInt64( i );
|
2013-02-17 22:14:13 +01:00
|
|
|
break;
|
2018-07-16 01:38:05 +02:00
|
|
|
case QSql::LowPrecisionDouble:
|
|
|
|
case QSql::HighPrecision:
|
|
|
|
default:
|
|
|
|
values[i + idx] = stmt.columnAsDouble( i );
|
2013-02-17 22:14:13 +01:00
|
|
|
break;
|
2018-07-16 01:38:05 +02:00
|
|
|
};
|
|
|
|
break;
|
|
|
|
case SQLITE_NULL:
|
|
|
|
values[i + idx] = QVariant( QVariant::String );
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
values[i + idx] = stmt.columnAsText( i );
|
|
|
|
break;
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
2018-07-16 01:38:05 +02:00
|
|
|
}
|
|
|
|
return true;
|
2013-02-17 22:14:13 +01:00
|
|
|
case SQLITE_DONE:
|
2018-07-16 01:38:05 +02:00
|
|
|
if ( rInf.isEmpty() )
|
|
|
|
// must be first call.
|
|
|
|
initColumns( true );
|
|
|
|
q->setAt( QSql::AfterLastRow );
|
|
|
|
sqlite3_reset( stmt.get() );
|
|
|
|
return false;
|
2013-02-17 22:14:13 +01:00
|
|
|
case SQLITE_CONSTRAINT:
|
|
|
|
case SQLITE_ERROR:
|
2018-07-16 01:38:05 +02:00
|
|
|
// 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;
|
2013-02-17 22:14:13 +01:00
|
|
|
case SQLITE_MISUSE:
|
|
|
|
case SQLITE_BUSY:
|
|
|
|
default:
|
2018-07-16 01:38:05 +02:00
|
|
|
// 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;
|
|
|
|
}
|
2019-08-07 17:32:43 +02:00
|
|
|
#ifndef _MSC_VER // avoid warning
|
2018-07-16 01:38:05 +02:00
|
|
|
return false;
|
2019-08-07 17:32:43 +02:00
|
|
|
#endif
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
QSpatiaLiteResult::QSpatiaLiteResult( const QSpatiaLiteDriver *db )
|
|
|
|
: QSqlCachedResult( *new QSpatiaLiteResultPrivate( this, db ) )
|
2013-02-17 22:14:13 +01:00
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
Q_D( QSpatiaLiteResult );
|
|
|
|
const_cast<QSpatiaLiteDriverPrivate *>( d->drv_d_func() )->results.append( this );
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
QSpatiaLiteResult::~QSpatiaLiteResult()
|
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
Q_D( QSpatiaLiteResult );
|
|
|
|
if ( d->drv_d_func() )
|
|
|
|
const_cast<QSpatiaLiteDriverPrivate *>( d->drv_d_func() )->results.removeOne( this );
|
|
|
|
d->cleanup();
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
void QSpatiaLiteResult::virtual_hook( int id, void *data )
|
2013-02-17 22:14:13 +01:00
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
QSqlCachedResult::virtual_hook( id, data );
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
bool QSpatiaLiteResult::reset( const QString &query )
|
2013-02-17 22:14:13 +01:00
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
if ( !prepare( query ) )
|
|
|
|
return false;
|
|
|
|
return exec();
|
|
|
|
}
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
bool QSpatiaLiteResult::prepare( const QString &query )
|
|
|
|
{
|
|
|
|
Q_D( QSpatiaLiteResult );
|
|
|
|
if ( !driver() || !driver()->isOpen() || driver()->isOpenError() )
|
|
|
|
return false;
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
d->cleanup();
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
setSelect( false );
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
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;
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
static QString secondsToOffset( int seconds )
|
2013-02-17 22:14:13 +01:00
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
const QChar sign = ushort( seconds < 0 ? '-' : '+' );
|
|
|
|
seconds = qAbs( seconds );
|
|
|
|
const int hours = seconds / 3600;
|
|
|
|
const int minutes = ( seconds % 3600 ) / 60;
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
return QString( QStringLiteral( "%1%2:%3" ) ).arg( sign ).arg( hours, 2, 10, QLatin1Char( '0' ) ).arg( minutes, 2, 10, QLatin1Char( '0' ) );
|
|
|
|
}
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
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<QVariant> 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<const QByteArray *>( 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<const QString *>( 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;
|
|
|
|
}
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
2018-07-16 01:38:05 +02:00
|
|
|
}
|
|
|
|
if ( res != SQLITE_OK )
|
|
|
|
{
|
|
|
|
setLastError( qMakeError( d->drv_d_func()->access, QCoreApplication::translate( "QSpatiaLiteResult",
|
|
|
|
"Unable to bind parameters" ), QSqlError::StatementError, res ) );
|
|
|
|
d->finalize();
|
2013-02-17 22:14:13 +01:00
|
|
|
return false;
|
2018-07-16 01:38:05 +02:00
|
|
|
}
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
2018-07-16 01:38:05 +02:00
|
|
|
}
|
|
|
|
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;
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
bool QSpatiaLiteResult::gotoNext( QSqlCachedResult::ValueCache &row, int idx )
|
2013-02-17 22:14:13 +01:00
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
Q_D( QSpatiaLiteResult );
|
|
|
|
return d->fetchNext( row, idx, false );
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int QSpatiaLiteResult::size()
|
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
return -1;
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int QSpatiaLiteResult::numRowsAffected()
|
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
Q_D( const QSpatiaLiteResult );
|
|
|
|
return sqlite3_changes( d->drv_d_func()->access.get() );
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
QVariant QSpatiaLiteResult::lastInsertId() const
|
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
Q_D( const QSpatiaLiteResult );
|
|
|
|
if ( isActive() )
|
|
|
|
{
|
|
|
|
qint64 id = sqlite3_last_insert_rowid( d->drv_d_func()->access.get() );
|
|
|
|
if ( id )
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
return QVariant();
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
QSqlRecord QSpatiaLiteResult::record() const
|
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
Q_D( const QSpatiaLiteResult );
|
|
|
|
if ( !isActive() || !isSelect() )
|
|
|
|
return QSqlRecord();
|
|
|
|
return d->rInf;
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
2016-03-12 17:33:23 +01:00
|
|
|
void QSpatiaLiteResult::detachFromResultSet()
|
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
Q_D( QSpatiaLiteResult );
|
|
|
|
if ( d->stmt )
|
|
|
|
sqlite3_reset( d->stmt.get() );
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////
|
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
QSpatiaLiteDriver::QSpatiaLiteDriver( QObject *parent )
|
|
|
|
: QSqlDriver( *new QSpatiaLiteDriverPrivate, parent )
|
2013-02-17 22:14:13 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QSpatiaLiteDriver::~QSpatiaLiteDriver()
|
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
close();
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
bool QSpatiaLiteDriver::hasFeature( DriverFeature f ) const
|
2013-02-17 22:14:13 +01:00
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
switch ( f )
|
|
|
|
{
|
2013-02-17 22:14:13 +01:00
|
|
|
case BLOB:
|
|
|
|
case Transactions:
|
|
|
|
case Unicode:
|
|
|
|
case LastInsertId:
|
|
|
|
case PreparedQueries:
|
|
|
|
case PositionalPlaceholders:
|
|
|
|
case SimpleLocking:
|
|
|
|
case FinishQuery:
|
|
|
|
case LowPrecisionNumbers:
|
2018-07-16 01:38:05 +02:00
|
|
|
case EventNotifications:
|
|
|
|
return true;
|
2013-02-17 22:14:13 +01:00
|
|
|
case QuerySize:
|
|
|
|
case NamedPlaceholders:
|
|
|
|
case BatchOperations:
|
|
|
|
case MultipleResultSets:
|
2016-03-12 21:00:21 +01:00
|
|
|
case CancelQuery:
|
2018-07-16 01:38:05 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return false;
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
SQLite dbs have no user name, passwords, hosts or ports.
|
|
|
|
just file names.
|
|
|
|
*/
|
2018-07-16 01:38:05 +02:00
|
|
|
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;
|
|
|
|
|
2024-05-14 01:24:26 +02:00
|
|
|
const auto opts = conOpts.split( QLatin1Char( ';' ) );
|
2018-07-16 01:38:05 +02:00
|
|
|
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;
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
2018-07-16 01:38:05 +02:00
|
|
|
else if ( option == QLatin1String( "QSQLITE_ENABLE_SHARED_CACHE" ) )
|
|
|
|
{
|
|
|
|
sharedCache = true;
|
|
|
|
}
|
|
|
|
}
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
int openMode = ( openReadOnlyOption ? SQLITE_OPEN_READONLY : ( SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE ) );
|
|
|
|
if ( openUriOption )
|
|
|
|
openMode |= SQLITE_OPEN_URI;
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
sqlite3_enable_shared_cache( sharedCache );
|
2017-11-14 08:14:51 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
if ( d->access.open_v2( db.toUtf8().constData(), openMode, nullptr ) == SQLITE_OK )
|
|
|
|
{
|
|
|
|
sqlite3_busy_timeout( d->access.get(), timeOut );
|
|
|
|
setOpen( true );
|
|
|
|
setOpenError( false );
|
2017-11-14 08:14:51 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
setLastError( qMakeError( d->access, tr( "Error opening database" ),
|
|
|
|
QSqlError::ConnectionError ) );
|
|
|
|
setOpenError( true );
|
|
|
|
return false;
|
|
|
|
}
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void QSpatiaLiteDriver::close()
|
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
Q_D( QSpatiaLiteDriver );
|
|
|
|
if ( isOpen() )
|
|
|
|
{
|
|
|
|
for ( QSpatiaLiteResult *result : qAsConst( d->results ) )
|
|
|
|
result->d_func()->finalize();
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
if ( d->access && ( d->notificationid.count() > 0 ) )
|
|
|
|
{
|
|
|
|
d->notificationid.clear();
|
|
|
|
sqlite3_update_hook( d->access.get(), NULL, NULL );
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
2018-07-16 01:38:05 +02:00
|
|
|
|
|
|
|
d->access.reset();
|
|
|
|
|
|
|
|
setOpen( false );
|
|
|
|
setOpenError( false );
|
|
|
|
}
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
QSqlResult *QSpatiaLiteDriver::createResult() const
|
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
return new QSpatiaLiteResult( this );
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool QSpatiaLiteDriver::beginTransaction()
|
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
if ( !isOpen() || isOpenError() )
|
|
|
|
return false;
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
QSqlQuery q( createResult() );
|
|
|
|
if ( !q.exec( QLatin1String( "BEGIN" ) ) )
|
|
|
|
{
|
|
|
|
setLastError( QSqlError( tr( "Unable to begin transaction" ),
|
|
|
|
q.lastError().databaseText(), QSqlError::TransactionError ) );
|
|
|
|
return false;
|
|
|
|
}
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
return true;
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool QSpatiaLiteDriver::commitTransaction()
|
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
if ( !isOpen() || isOpenError() )
|
|
|
|
return false;
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
QSqlQuery q( createResult() );
|
|
|
|
if ( !q.exec( QLatin1String( "COMMIT" ) ) )
|
|
|
|
{
|
|
|
|
setLastError( QSqlError( tr( "Unable to commit transaction" ),
|
|
|
|
q.lastError().databaseText(), QSqlError::TransactionError ) );
|
|
|
|
return false;
|
|
|
|
}
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
return true;
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool QSpatiaLiteDriver::rollbackTransaction()
|
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
if ( !isOpen() || isOpenError() )
|
|
|
|
return false;
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
QSqlQuery q( createResult() );
|
|
|
|
if ( !q.exec( QLatin1String( "ROLLBACK" ) ) )
|
|
|
|
{
|
|
|
|
setLastError( QSqlError( tr( "Unable to rollback transaction" ),
|
|
|
|
q.lastError().databaseText(), QSqlError::TransactionError ) );
|
|
|
|
return false;
|
|
|
|
}
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
return true;
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
QStringList QSpatiaLiteDriver::tables( QSql::TableType type ) const
|
2013-02-17 22:14:13 +01:00
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
QStringList res;
|
|
|
|
if ( !isOpen() )
|
|
|
|
return res;
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
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();
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
if ( !sql.isEmpty() && q.exec( sql ) )
|
|
|
|
{
|
|
|
|
while ( q.next() )
|
|
|
|
res.append( q.value( 0 ).toString() );
|
|
|
|
}
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
if ( type & QSql::SystemTables )
|
|
|
|
{
|
|
|
|
// there are no internal tables beside this one:
|
|
|
|
res.append( QLatin1String( "sqlite_master" ) );
|
|
|
|
}
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
return res;
|
|
|
|
}
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
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;
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
QSqlIndex QSpatiaLiteDriver::primaryIndex( const QString &tblname ) const
|
2013-02-17 22:14:13 +01:00
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
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 );
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
static void handle_sqlite_callback( void *qobj, int aoperation, char const *adbname, char const *atablename,
|
|
|
|
sqlite3_int64 arowid )
|
2013-02-17 22:14:13 +01:00
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
Q_UNUSED( aoperation );
|
|
|
|
Q_UNUSED( adbname );
|
|
|
|
QSpatiaLiteDriver *driver = static_cast<QSpatiaLiteDriver *>( qobj );
|
|
|
|
if ( driver )
|
|
|
|
{
|
|
|
|
QMetaObject::invokeMethod( driver, "handleNotification", Qt::QueuedConnection,
|
|
|
|
Q_ARG( QString, QString::fromUtf8( atablename ) ), Q_ARG( qint64, arowid ) );
|
|
|
|
}
|
|
|
|
}
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
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;
|
|
|
|
}
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
//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<void *>( this ) );
|
|
|
|
|
|
|
|
return true;
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
bool QSpatiaLiteDriver::unsubscribeFromNotification( const QString &name )
|
2013-02-17 22:14:13 +01:00
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
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;
|
|
|
|
}
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
d->notificationid.removeAll( name );
|
|
|
|
if ( d->notificationid.isEmpty() )
|
|
|
|
sqlite3_update_hook( d->access.get(), NULL, NULL );
|
2013-02-17 22:14:13 +01:00
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
return true;
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
QStringList QSpatiaLiteDriver::subscribedToNotifications() const
|
2013-02-17 22:14:13 +01:00
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
Q_D( const QSpatiaLiteDriver );
|
|
|
|
return d->notificationid;
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
2018-07-16 01:38:05 +02:00
|
|
|
void QSpatiaLiteDriver::handleNotification( const QString &tableName, qint64 rowid )
|
2013-02-17 22:14:13 +01:00
|
|
|
{
|
2018-07-16 01:38:05 +02:00
|
|
|
Q_D( const QSpatiaLiteDriver );
|
|
|
|
if ( d->notificationid.contains( tableName ) )
|
|
|
|
{
|
|
|
|
emit notification( tableName, QSqlDriver::UnknownSource, QVariant( rowid ) );
|
|
|
|
}
|
2013-02-17 22:14:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
QT_END_NAMESPACE
|