mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-31 00:04:24 -05:00
Spatialite transactions
This commit is contained in:
parent
d2360da967
commit
d680db5153
@ -285,8 +285,6 @@ class QgsConnectionPool
|
||||
* If \a timeout is a negative value the calling thread will be blocked
|
||||
* until a connection becomes available. This is the default behavior.
|
||||
*
|
||||
*
|
||||
*
|
||||
* \returns initialized connection or NULLPTR if unsuccessful
|
||||
*/
|
||||
T acquireConnection( const QString &connInfo, int timeout = -1, bool requestMayBeNested = false )
|
||||
|
||||
@ -22,6 +22,22 @@
|
||||
#include <sqlite3.h>
|
||||
#include <spatialite.h>
|
||||
|
||||
// Define this variable to print all spatialite SQL statements
|
||||
#ifdef SPATIALITE_PRINT_ALL_SQL
|
||||
// Debugging code
|
||||
#include <QDebug>
|
||||
#include <QThread>
|
||||
static int trace_callback( unsigned, void *ctx, void *p, void * )
|
||||
{
|
||||
sqlite3_stmt *stmt = ( sqlite3_stmt * )p;
|
||||
char *sql = sqlite3_expanded_sql( stmt );
|
||||
qDebug() << "SPATIALITE" << QThread::currentThreadId() << ( sqlite3 * ) ctx << sql;
|
||||
sqlite3_free( sql );
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int spatialite_database_unique_ptr::open( const QString &path )
|
||||
{
|
||||
auto &deleter = get_deleter();
|
||||
@ -54,6 +70,16 @@ int spatialite_database_unique_ptr::open_v2( const QString &path, int flags, con
|
||||
if ( result == SQLITE_OK )
|
||||
spatialite_init_ex( database, deleter.mSpatialiteContext, 0 );
|
||||
|
||||
#ifdef SPATIALITE_PRINT_ALL_SQL
|
||||
// Log all queries
|
||||
sqlite3_trace_v2(
|
||||
database,
|
||||
SQLITE_TRACE_STMT,
|
||||
trace_callback,
|
||||
database
|
||||
);
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ SET(SPATIALITE_SRCS
|
||||
qgsspatialitefeatureiterator.cpp
|
||||
qgsspatialitetablemodel.cpp
|
||||
qgsspatialiteproviderconnection.cpp
|
||||
qgsspatialitetransaction.cpp
|
||||
)
|
||||
|
||||
IF (WITH_GUI)
|
||||
|
||||
@ -158,7 +158,13 @@ class QgsSqliteHandle
|
||||
{
|
||||
mIsValid = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a possibly cached SQLite DB object from \a path, if \a shared is FALSE
|
||||
* the DB will not be searched in the cache and a new READ ONLY connection will be returned.
|
||||
*/
|
||||
static QgsSqliteHandle *openDb( const QString &dbPath, bool shared = true );
|
||||
|
||||
static bool checkMetadata( sqlite3 *handle );
|
||||
static void closeDb( QgsSqliteHandle *&handle );
|
||||
|
||||
|
||||
@ -29,7 +29,16 @@
|
||||
QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeatureSource *source, bool ownSource, const QgsFeatureRequest &request )
|
||||
: QgsAbstractFeatureIteratorFromSource<QgsSpatiaLiteFeatureSource>( source, ownSource, request )
|
||||
{
|
||||
mHandle = QgsSpatiaLiteConnPool::instance()->acquireConnection( mSource->mSqlitePath, request.timeout(), request.requestMayBeNested() );
|
||||
|
||||
mSqliteHandle = source->transactionHandle();
|
||||
if ( ! mSqliteHandle )
|
||||
{
|
||||
mHandle = QgsSpatiaLiteConnPool::instance()->acquireConnection( mSource->mSqlitePath, request.timeout(), request.requestMayBeNested() );
|
||||
if ( mHandle )
|
||||
{
|
||||
mSqliteHandle = mHandle->handle();
|
||||
}
|
||||
}
|
||||
|
||||
mFetchGeometry = !mSource->mGeometryColumn.isNull() && !( mRequest.flags() & QgsFeatureRequest::NoGeometry );
|
||||
mHasPrimaryKey = !mSource->mPrimaryKey.isEmpty();
|
||||
@ -290,9 +299,10 @@ bool QgsSpatiaLiteFeatureIterator::close()
|
||||
|
||||
iteratorClosed();
|
||||
|
||||
if ( !mHandle )
|
||||
mClosed = true;
|
||||
|
||||
if ( !mSqliteHandle )
|
||||
{
|
||||
mClosed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -302,19 +312,24 @@ bool QgsSpatiaLiteFeatureIterator::close()
|
||||
sqliteStatement = nullptr;
|
||||
}
|
||||
|
||||
QgsSpatiaLiteConnPool::instance()->releaseConnection( mHandle );
|
||||
mHandle = nullptr;
|
||||
if ( mHandle )
|
||||
{
|
||||
QgsSpatiaLiteConnPool::instance()->releaseConnection( mHandle );
|
||||
mHandle = nullptr;
|
||||
}
|
||||
|
||||
mSqliteHandle = nullptr;
|
||||
mClosed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
////
|
||||
|
||||
|
||||
bool QgsSpatiaLiteFeatureIterator::prepareStatement( const QString &whereClause, long limit, const QString &orderBy )
|
||||
{
|
||||
if ( !mHandle )
|
||||
if ( !mSqliteHandle )
|
||||
return false;
|
||||
|
||||
try
|
||||
@ -359,10 +374,10 @@ bool QgsSpatiaLiteFeatureIterator::prepareStatement( const QString &whereClause,
|
||||
|
||||
QgsDebugMsgLevel( sql, 4 );
|
||||
|
||||
if ( sqlite3_prepare_v2( mHandle->handle(), sql.toUtf8().constData(), -1, &sqliteStatement, nullptr ) != SQLITE_OK )
|
||||
if ( sqlite3_prepare_v2( mSqliteHandle, sql.toUtf8().constData(), -1, &sqliteStatement, nullptr ) != SQLITE_OK )
|
||||
{
|
||||
// some error occurred
|
||||
QgsMessageLog::logMessage( QObject::tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( mHandle->handle() ) ), QObject::tr( "SpatiaLite" ) );
|
||||
QgsMessageLog::logMessage( QObject::tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( mSqliteHandle ) ), QObject::tr( "SpatiaLite" ) );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -489,7 +504,7 @@ bool QgsSpatiaLiteFeatureIterator::getFeature( sqlite3_stmt *stmt, QgsFeature &f
|
||||
if ( ret != SQLITE_ROW )
|
||||
{
|
||||
// some unexpected error occurred
|
||||
QgsMessageLog::logMessage( QObject::tr( "SQLite error getting feature: %1" ).arg( QString::fromUtf8( sqlite3_errmsg( mHandle->handle() ) ) ), QObject::tr( "SpatiaLite" ) );
|
||||
QgsMessageLog::logMessage( QObject::tr( "SQLite error getting feature: %1" ).arg( QString::fromUtf8( sqlite3_errmsg( mSqliteHandle ) ) ), QObject::tr( "SpatiaLite" ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -642,6 +657,7 @@ QgsSpatiaLiteFeatureSource::QgsSpatiaLiteFeatureSource( const QgsSpatiaLiteProvi
|
||||
, mSpatialIndexMbrCache( p->mSpatialIndexMbrCache )
|
||||
, mSqlitePath( p->mSqlitePath )
|
||||
, mCrs( p->crs() )
|
||||
, mTransactionHandle( p->transaction() ? p->sqliteHandle() : nullptr )
|
||||
{
|
||||
}
|
||||
|
||||
@ -649,3 +665,8 @@ QgsFeatureIterator QgsSpatiaLiteFeatureSource::getFeatures( const QgsFeatureRequ
|
||||
{
|
||||
return QgsFeatureIterator( new QgsSpatiaLiteFeatureIterator( this, false, request ) );
|
||||
}
|
||||
|
||||
sqlite3 *QgsSpatiaLiteFeatureSource::transactionHandle()
|
||||
{
|
||||
return mTransactionHandle;
|
||||
}
|
||||
|
||||
@ -34,6 +34,8 @@ class QgsSpatiaLiteFeatureSource final: public QgsAbstractFeatureSource
|
||||
|
||||
QgsFeatureIterator getFeatures( const QgsFeatureRequest &request ) override;
|
||||
|
||||
sqlite3 *transactionHandle();
|
||||
|
||||
private:
|
||||
QString mGeometryColumn;
|
||||
QString mSubsetString;
|
||||
@ -49,6 +51,7 @@ class QgsSpatiaLiteFeatureSource final: public QgsAbstractFeatureSource
|
||||
bool mSpatialIndexMbrCache;
|
||||
QString mSqlitePath;
|
||||
QgsCoordinateReferenceSystem mCrs;
|
||||
sqlite3 *mTransactionHandle = nullptr;
|
||||
|
||||
friend class QgsSpatiaLiteFeatureIterator;
|
||||
friend class QgsSpatiaLiteExpressionCompiler;
|
||||
@ -81,8 +84,10 @@ class QgsSpatiaLiteFeatureIterator final: public QgsAbstractFeatureIteratorFromS
|
||||
QVariant getFeatureAttribute( sqlite3_stmt *stmt, int ic, QVariant::Type type, QVariant::Type subType );
|
||||
void getFeatureGeometry( sqlite3_stmt *stmt, int ic, QgsFeature &feature );
|
||||
|
||||
//! wrapper of the SQLite database connection
|
||||
//! QGIS wrapper of the SQLite database connection
|
||||
QgsSqliteHandle *mHandle = nullptr;
|
||||
//! The low level connection
|
||||
sqlite3 *mSqliteHandle = nullptr;
|
||||
|
||||
/**
|
||||
* SQLite statement handle
|
||||
|
||||
@ -31,6 +31,7 @@ email : a.furieri@lqt.it
|
||||
#include "qgsfeedback.h"
|
||||
#include "qgsspatialitedataitems.h"
|
||||
#include "qgsspatialiteconnection.h"
|
||||
#include "qgsspatialitetransaction.h"
|
||||
#include "qgsspatialiteproviderconnection.h"
|
||||
|
||||
#include "qgsjsonutils.h"
|
||||
@ -51,7 +52,7 @@ const QString QgsSpatiaLiteProvider::SPATIALITE_KEY = QStringLiteral( "spatialit
|
||||
const QString QgsSpatiaLiteProvider::SPATIALITE_DESCRIPTION = QStringLiteral( "SpatiaLite data provider" );
|
||||
static const QString SPATIALITE_ARRAY_PREFIX = QStringLiteral( "json" );
|
||||
static const QString SPATIALITE_ARRAY_SUFFIX = QStringLiteral( "list" );
|
||||
|
||||
QAtomicInt QgsSpatiaLiteProvider::sSavepointId = 0;
|
||||
|
||||
bool QgsSpatiaLiteProvider::convertField( QgsField &field )
|
||||
{
|
||||
@ -459,8 +460,9 @@ QgsSpatiaLiteProvider::QgsSpatiaLiteProvider( QString const &uri, const Provider
|
||||
mPrimaryKey = anUri.keyColumn();
|
||||
mQuery = mTableName;
|
||||
|
||||
// trying to open the SQLite DB
|
||||
// Retrieve a shared connection
|
||||
mHandle = QgsSqliteHandle::openDb( mSqlitePath );
|
||||
|
||||
if ( !mHandle )
|
||||
{
|
||||
return;
|
||||
@ -474,7 +476,7 @@ QgsSpatiaLiteProvider::QgsSpatiaLiteProvider( QString const &uri, const Provider
|
||||
for ( const auto &pragma : pragmaList )
|
||||
{
|
||||
char *errMsg = nullptr;
|
||||
int ret = sqlite3_exec( mSqliteHandle, ( "PRAGMA " + pragma ).toUtf8(), nullptr, nullptr, &errMsg );
|
||||
int ret = exec_sql( QStringLiteral( "PRAGMA %1" ).arg( pragma ), errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
QgsDebugMsg( QStringLiteral( "PRAGMA " ) + pragma + QString( " failed : %1" ).arg( errMsg ? errMsg : "" ) );
|
||||
@ -539,6 +541,7 @@ QgsSpatiaLiteProvider::QgsSpatiaLiteProvider( QString const &uri, const Provider
|
||||
mEnabledCapabilities |= QgsVectorDataProvider::AddFeatures;
|
||||
mEnabledCapabilities |= QgsVectorDataProvider::AddAttributes;
|
||||
mEnabledCapabilities |= QgsVectorDataProvider::CreateAttributeIndex;
|
||||
mEnabledCapabilities |= QgsVectorDataProvider::TransactionSupport;
|
||||
}
|
||||
|
||||
if ( lyr )
|
||||
@ -880,7 +883,7 @@ void QgsSpatiaLiteProvider::fetchConstraints()
|
||||
int ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg );
|
||||
handleError( sql, errMsg, QString() );
|
||||
return;
|
||||
}
|
||||
|
||||
@ -929,7 +932,7 @@ void QgsSpatiaLiteProvider::fetchConstraints()
|
||||
int ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg );
|
||||
handleError( sql, errMsg, QString() );
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1036,7 +1039,7 @@ QString QgsSpatiaLiteProvider::defaultValueClause( int fieldIndex ) const
|
||||
return mDefaultValueClause.value( fieldIndex, QString() );
|
||||
}
|
||||
|
||||
void QgsSpatiaLiteProvider::handleError( const QString &sql, char *errorMessage, bool rollback )
|
||||
void QgsSpatiaLiteProvider::handleError( const QString &sql, char *errorMessage, const QString &savepointId )
|
||||
{
|
||||
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errorMessage ? errorMessage : tr( "unknown cause" ) ), tr( "SpatiaLite" ) );
|
||||
// unexpected error
|
||||
@ -1045,13 +1048,24 @@ void QgsSpatiaLiteProvider::handleError( const QString &sql, char *errorMessage,
|
||||
sqlite3_free( errorMessage );
|
||||
}
|
||||
|
||||
if ( rollback )
|
||||
if ( ! savepointId.isEmpty() )
|
||||
{
|
||||
// ROLLBACK after some previous error
|
||||
( void )sqlite3_exec( mSqliteHandle, "ROLLBACK", nullptr, nullptr, nullptr );
|
||||
( void )exec_sql( QStringLiteral( "ROLLBACK TRANSACTION TO \"%1\"" ).arg( savepointId ) );
|
||||
}
|
||||
}
|
||||
|
||||
int QgsSpatiaLiteProvider::exec_sql( const QString &sql, char *errMsg )
|
||||
{
|
||||
// Use transaction's handle (if any)
|
||||
return sqlite3_exec( sqliteHandle(), sql.toUtf8().constData(), nullptr, nullptr, &errMsg );
|
||||
}
|
||||
|
||||
sqlite3 *QgsSpatiaLiteProvider::sqliteHandle() const
|
||||
{
|
||||
return mTransaction && mTransaction->sqliteHandle() ? mTransaction->sqliteHandle() : mSqliteHandle;
|
||||
}
|
||||
|
||||
void QgsSpatiaLiteProvider::loadFields()
|
||||
{
|
||||
int ret;
|
||||
@ -1075,10 +1089,10 @@ void QgsSpatiaLiteProvider::loadFields()
|
||||
|
||||
sql = QStringLiteral( "PRAGMA table_info(%1)" ).arg( QgsSqliteUtils::quotedIdentifier( mTableName ) );
|
||||
|
||||
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg );
|
||||
handleError( sql, errMsg, QString() );
|
||||
return;
|
||||
}
|
||||
if ( rows < 1 )
|
||||
@ -1135,10 +1149,10 @@ void QgsSpatiaLiteProvider::loadFields()
|
||||
{
|
||||
sql = QStringLiteral( "select * from %1 limit 1" ).arg( mQuery );
|
||||
|
||||
if ( sqlite3_prepare_v2( mSqliteHandle, sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
|
||||
if ( sqlite3_prepare_v2( sqliteHandle( ), sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
|
||||
{
|
||||
// some error occurred
|
||||
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( mSqliteHandle ) ), tr( "SpatiaLite" ) );
|
||||
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( sqliteHandle( ) ) ), tr( "SpatiaLite" ) );
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1206,7 +1220,7 @@ void QgsSpatiaLiteProvider::determineViewPrimaryKey()
|
||||
int rows;
|
||||
int columns;
|
||||
char *errMsg = nullptr;
|
||||
int ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
int ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret == SQLITE_OK )
|
||||
{
|
||||
if ( rows > 0 )
|
||||
@ -1229,15 +1243,15 @@ QStringList QgsSpatiaLiteProvider::tablePrimaryKeys( const QString &tableName )
|
||||
int rows;
|
||||
int columns;
|
||||
char *errMsg = nullptr;
|
||||
if ( sqlite3_prepare_v2( mSqliteHandle, sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
|
||||
if ( sqlite3_prepare_v2( sqliteHandle( ), sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
|
||||
{
|
||||
// some error occurred
|
||||
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( mSqliteHandle ) ),
|
||||
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( sqliteHandle( ) ) ),
|
||||
tr( "SpatiaLite" ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
int ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
int ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret == SQLITE_OK )
|
||||
{
|
||||
for ( int row = 1; row <= rows; ++row )
|
||||
@ -1275,7 +1289,7 @@ bool QgsSpatiaLiteProvider::hasTriggers()
|
||||
sql = QStringLiteral( "SELECT * FROM sqlite_master WHERE type='trigger' AND tbl_name=%1" )
|
||||
.arg( QgsSqliteUtils::quotedIdentifier( mTableName ) );
|
||||
|
||||
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
sqlite3_free_table( results );
|
||||
return ( ret == SQLITE_OK && rows > 0 );
|
||||
}
|
||||
@ -1288,7 +1302,7 @@ bool QgsSpatiaLiteProvider::hasRowid()
|
||||
// table without rowid column
|
||||
QString sql = QStringLiteral( "SELECT rowid FROM %1 WHERE 0" ).arg( QgsSqliteUtils::quotedIdentifier( mTableName ) );
|
||||
char *errMsg = nullptr;
|
||||
return sqlite3_exec( mSqliteHandle, sql.toUtf8(), nullptr, nullptr, &errMsg ) == SQLITE_OK;
|
||||
return exec_sql( sql, errMsg ) == SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
@ -3719,7 +3733,7 @@ QVariant QgsSpatiaLiteProvider::minimumValue( int index ) const
|
||||
sql += " WHERE ( " + mSubsetString + ')';
|
||||
}
|
||||
|
||||
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errMsg ? errMsg : tr( "unknown cause" ) ), tr( "SpatiaLite" ) );
|
||||
@ -3782,7 +3796,7 @@ QVariant QgsSpatiaLiteProvider::maximumValue( int index ) const
|
||||
sql += " WHERE ( " + mSubsetString + ')';
|
||||
}
|
||||
|
||||
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errMsg ? errMsg : tr( "unknown cause" ) ), tr( "SpatiaLite" ) );
|
||||
@ -3852,10 +3866,10 @@ QSet<QVariant> QgsSpatiaLiteProvider::uniqueValues( int index, int limit ) const
|
||||
}
|
||||
|
||||
// SQLite prepared statement
|
||||
if ( sqlite3_prepare_v2( mSqliteHandle, sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
|
||||
if ( sqlite3_prepare_v2( sqliteHandle( ), sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
|
||||
{
|
||||
// some error occurred
|
||||
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( mSqliteHandle ) ), tr( "SpatiaLite" ) );
|
||||
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( sqliteHandle( ) ) ), tr( "SpatiaLite" ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3891,7 +3905,7 @@ QSet<QVariant> QgsSpatiaLiteProvider::uniqueValues( int index, int limit ) const
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( mSqliteHandle ) ), tr( "SpatiaLite" ) );
|
||||
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( sqliteHandle( ) ) ), tr( "SpatiaLite" ) );
|
||||
sqlite3_finalize( stmt );
|
||||
return uniqueValues;
|
||||
}
|
||||
@ -3932,10 +3946,10 @@ QStringList QgsSpatiaLiteProvider::uniqueStringsMatching( int index, const QStri
|
||||
}
|
||||
|
||||
// SQLite prepared statement
|
||||
if ( sqlite3_prepare_v2( mSqliteHandle, sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
|
||||
if ( sqlite3_prepare_v2( sqliteHandle( ), sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
|
||||
{
|
||||
// some error occurred
|
||||
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( mSqliteHandle ) ), tr( "SpatiaLite" ) );
|
||||
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( sqliteHandle( ) ) ), tr( "SpatiaLite" ) );
|
||||
|
||||
}
|
||||
else
|
||||
@ -3965,7 +3979,7 @@ QStringList QgsSpatiaLiteProvider::uniqueStringsMatching( int index, const QStri
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( mSqliteHandle ) ), tr( "SpatiaLite" ) );
|
||||
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( sqliteHandle( ) ) ), tr( "SpatiaLite" ) );
|
||||
sqlite3_finalize( stmt );
|
||||
return results;
|
||||
}
|
||||
@ -4020,7 +4034,9 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags )
|
||||
|
||||
QgsAttributes attributevec = flist[0].attributes();
|
||||
|
||||
ret = sqlite3_exec( mSqliteHandle, "BEGIN", nullptr, nullptr, &errMsg );
|
||||
const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) };
|
||||
|
||||
ret = exec_sql( QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg );
|
||||
if ( ret == SQLITE_OK )
|
||||
{
|
||||
toCommit = true;
|
||||
@ -4078,7 +4094,7 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags )
|
||||
sql += ')';
|
||||
|
||||
// SQLite prepared statement
|
||||
ret = sqlite3_prepare_v2( mSqliteHandle, sql.toUtf8().constData(), -1, &stmt, nullptr );
|
||||
ret = sqlite3_prepare_v2( sqliteHandle( ), sql.toUtf8().constData(), -1, &stmt, nullptr );
|
||||
if ( ret == SQLITE_OK )
|
||||
{
|
||||
|
||||
@ -4185,14 +4201,14 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags )
|
||||
// update feature id
|
||||
if ( !( flags & QgsFeatureSink::FastInsert ) )
|
||||
{
|
||||
feature->setId( sqlite3_last_insert_rowid( mSqliteHandle ) );
|
||||
feature->setId( sqlite3_last_insert_rowid( sqliteHandle( ) ) );
|
||||
}
|
||||
mNumberFeatures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// some unexpected error occurred
|
||||
const char *err = sqlite3_errmsg( mSqliteHandle );
|
||||
const char *err = sqlite3_errmsg( sqliteHandle( ) );
|
||||
errMsg = ( char * ) sqlite3_malloc( ( int ) strlen( err ) + 1 );
|
||||
strcpy( errMsg, err );
|
||||
break;
|
||||
@ -4202,7 +4218,7 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags )
|
||||
|
||||
if ( ret == SQLITE_DONE || ret == SQLITE_ROW )
|
||||
{
|
||||
ret = sqlite3_exec( mSqliteHandle, "COMMIT", nullptr, nullptr, &errMsg );
|
||||
ret = exec_sql( QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg );
|
||||
}
|
||||
|
||||
} // BEGIN statement
|
||||
@ -4218,9 +4234,14 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags )
|
||||
if ( toCommit )
|
||||
{
|
||||
// ROLLBACK after some previous error
|
||||
( void )sqlite3_exec( mSqliteHandle, "ROLLBACK", nullptr, nullptr, nullptr );
|
||||
( void )exec_sql( QStringLiteral( "ROLLBACK TRANSACTION TO SAVEPOINT \"%1\"" ).arg( savepointId ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( mTransaction )
|
||||
mTransaction->dirtyLastSavePoint();
|
||||
}
|
||||
|
||||
return ret == SQLITE_OK;
|
||||
}
|
||||
@ -4243,10 +4264,12 @@ bool QgsSpatiaLiteProvider::createAttributeIndex( int field )
|
||||
QString sql;
|
||||
QString fieldName;
|
||||
|
||||
int ret = sqlite3_exec( mSqliteHandle, "BEGIN", nullptr, nullptr, &errMsg );
|
||||
const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) };
|
||||
|
||||
int ret = exec_sql( QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg );
|
||||
handleError( sql, errMsg, QString() );
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -4256,20 +4279,23 @@ bool QgsSpatiaLiteProvider::createAttributeIndex( int field )
|
||||
.arg( createIndexName( mTableName, fieldName ),
|
||||
mTableName,
|
||||
QgsSqliteUtils::quotedIdentifier( fieldName ) );
|
||||
ret = sqlite3_exec( mSqliteHandle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg );
|
||||
ret = exec_sql( sql, errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg, true );
|
||||
handleError( sql, errMsg, savepointId );
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = sqlite3_exec( mSqliteHandle, "COMMIT", nullptr, nullptr, &errMsg );
|
||||
ret = exec_sql( QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg, true );
|
||||
handleError( sql, errMsg, savepointId );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( mTransaction )
|
||||
mTransaction->dirtyLastSavePoint();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -4293,20 +4319,22 @@ bool QgsSpatiaLiteProvider::deleteFeatures( const QgsFeatureIds &id )
|
||||
char *errMsg = nullptr;
|
||||
QString sql;
|
||||
|
||||
int ret = sqlite3_exec( mSqliteHandle, "BEGIN", nullptr, nullptr, &errMsg );
|
||||
const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) };
|
||||
|
||||
int ret = exec_sql( QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg );
|
||||
handleError( sql, errMsg, QString() );
|
||||
return false;
|
||||
}
|
||||
|
||||
sql = QStringLiteral( "DELETE FROM %1 WHERE %2=?" ).arg( QgsSqliteUtils::quotedIdentifier( mTableName ), QgsSqliteUtils::quotedIdentifier( mPrimaryKey ) );
|
||||
|
||||
// SQLite prepared statement
|
||||
if ( sqlite3_prepare_v2( mSqliteHandle, sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
|
||||
if ( sqlite3_prepare_v2( sqliteHandle( ), sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
|
||||
{
|
||||
// some error occurred
|
||||
pushError( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( mSqliteHandle ) ) );
|
||||
pushError( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( sqliteHandle( ) ) ) );
|
||||
return false;
|
||||
}
|
||||
else
|
||||
@ -4330,10 +4358,10 @@ bool QgsSpatiaLiteProvider::deleteFeatures( const QgsFeatureIds &id )
|
||||
else
|
||||
{
|
||||
// some unexpected error occurred
|
||||
const char *err = sqlite3_errmsg( mSqliteHandle );
|
||||
const char *err = sqlite3_errmsg( sqliteHandle( ) );
|
||||
errMsg = ( char * ) sqlite3_malloc( ( int ) strlen( err ) + 1 );
|
||||
strcpy( errMsg, err );
|
||||
handleError( sql, errMsg, true );
|
||||
handleError( sql, errMsg, savepointId );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -4341,13 +4369,16 @@ bool QgsSpatiaLiteProvider::deleteFeatures( const QgsFeatureIds &id )
|
||||
|
||||
sqlite3_finalize( stmt );
|
||||
|
||||
ret = sqlite3_exec( mSqliteHandle, "COMMIT", nullptr, nullptr, &errMsg );
|
||||
ret = exec_sql( QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg, true );
|
||||
handleError( sql, errMsg, savepointId );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( mTransaction )
|
||||
mTransaction->dirtyLastSavePoint();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -4356,28 +4387,33 @@ bool QgsSpatiaLiteProvider::truncate()
|
||||
char *errMsg = nullptr;
|
||||
QString sql;
|
||||
|
||||
int ret = sqlite3_exec( mSqliteHandle, "BEGIN", nullptr, nullptr, &errMsg );
|
||||
const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) };
|
||||
|
||||
int ret = exec_sql( QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg );
|
||||
handleError( sql, errMsg, QString() );
|
||||
return false;
|
||||
}
|
||||
|
||||
sql = QStringLiteral( "DELETE FROM %1" ).arg( QgsSqliteUtils::quotedIdentifier( mTableName ) );
|
||||
ret = sqlite3_exec( mSqliteHandle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg );
|
||||
ret = exec_sql( sql, errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg, true );
|
||||
handleError( sql, errMsg, savepointId );
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = sqlite3_exec( mSqliteHandle, "COMMIT", nullptr, nullptr, &errMsg );
|
||||
ret = exec_sql( QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg, true );
|
||||
handleError( sql, errMsg, savepointId );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( mTransaction )
|
||||
mTransaction->dirtyLastSavePoint();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -4389,10 +4425,12 @@ bool QgsSpatiaLiteProvider::addAttributes( const QList<QgsField> &attributes )
|
||||
if ( attributes.isEmpty() )
|
||||
return true;
|
||||
|
||||
int ret = sqlite3_exec( mSqliteHandle, "BEGIN", nullptr, nullptr, &errMsg );
|
||||
const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) };
|
||||
|
||||
int ret = exec_sql( QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg );
|
||||
handleError( sql, errMsg, QString() );
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -4402,27 +4440,30 @@ bool QgsSpatiaLiteProvider::addAttributes( const QList<QgsField> &attributes )
|
||||
.arg( mTableName,
|
||||
iter->name(),
|
||||
iter->typeName() );
|
||||
ret = sqlite3_exec( mSqliteHandle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg );
|
||||
ret = exec_sql( sql, errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg, true );
|
||||
handleError( sql, errMsg, savepointId );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ret = sqlite3_exec( mSqliteHandle, "COMMIT", nullptr, nullptr, &errMsg );
|
||||
ret = exec_sql( QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg, true );
|
||||
handleError( sql, errMsg, savepointId );
|
||||
return false;
|
||||
}
|
||||
|
||||
gaiaStatisticsInvalidate( mSqliteHandle, mTableName.toUtf8().constData(), mGeometryColumn.toUtf8().constData() );
|
||||
update_layer_statistics( mSqliteHandle, mTableName.toUtf8().constData(), mGeometryColumn.toUtf8().constData() );
|
||||
gaiaStatisticsInvalidate( sqliteHandle( ), mTableName.toUtf8().constData(), mGeometryColumn.toUtf8().constData() );
|
||||
update_layer_statistics( sqliteHandle( ), mTableName.toUtf8().constData(), mGeometryColumn.toUtf8().constData() );
|
||||
|
||||
// reload columns
|
||||
loadFields();
|
||||
|
||||
if ( mTransaction )
|
||||
mTransaction->dirtyLastSavePoint();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -4434,10 +4475,12 @@ bool QgsSpatiaLiteProvider::changeAttributeValues( const QgsChangedAttributesMap
|
||||
if ( attr_map.isEmpty() )
|
||||
return true;
|
||||
|
||||
int ret = sqlite3_exec( mSqliteHandle, "BEGIN", nullptr, nullptr, &errMsg );
|
||||
const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) };
|
||||
|
||||
int ret = exec_sql( QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg );
|
||||
handleError( sql, errMsg, QString() );
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -4504,7 +4547,7 @@ bool QgsSpatiaLiteProvider::changeAttributeValues( const QgsChangedAttributesMap
|
||||
auto msgPtr { static_cast<char *>( sqlite3_malloc( errM.length() + 1 ) ) };
|
||||
strcpy( static_cast<char *>( msgPtr ), errM.toStdString().c_str() );
|
||||
errMsg = msgPtr;
|
||||
handleError( jRepr, errMsg, true );
|
||||
handleError( jRepr, errMsg, savepointId );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -4521,21 +4564,24 @@ bool QgsSpatiaLiteProvider::changeAttributeValues( const QgsChangedAttributesMap
|
||||
}
|
||||
sql += QStringLiteral( " WHERE %1=%2" ).arg( QgsSqliteUtils::quotedIdentifier( mPrimaryKey ) ).arg( fid );
|
||||
|
||||
ret = sqlite3_exec( mSqliteHandle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg );
|
||||
ret = exec_sql( sql.toUtf8(), errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg, true );
|
||||
handleError( sql, errMsg, savepointId );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ret = sqlite3_exec( mSqliteHandle, "COMMIT", nullptr, nullptr, &errMsg );
|
||||
ret = exec_sql( QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg, true );
|
||||
handleError( sql, errMsg, savepointId );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( mTransaction )
|
||||
mTransaction->dirtyLastSavePoint();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -4545,10 +4591,12 @@ bool QgsSpatiaLiteProvider::changeGeometryValues( const QgsGeometryMap &geometry
|
||||
char *errMsg = nullptr;
|
||||
QString sql;
|
||||
|
||||
int ret = sqlite3_exec( mSqliteHandle, "BEGIN", nullptr, nullptr, &errMsg );
|
||||
const QString savepointId { QStringLiteral( "qgis_spatialite_internal_savepoint_%1" ).arg( ++ sSavepointId ) };
|
||||
|
||||
int ret = exec_sql( QStringLiteral( "SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg );
|
||||
handleError( sql, errMsg, QString() );
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -4560,10 +4608,10 @@ bool QgsSpatiaLiteProvider::changeGeometryValues( const QgsGeometryMap &geometry
|
||||
.arg( QgsSqliteUtils::quotedIdentifier( mPrimaryKey ) );
|
||||
|
||||
// SQLite prepared statement
|
||||
if ( sqlite3_prepare_v2( mSqliteHandle, sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
|
||||
if ( sqlite3_prepare_v2( sqliteHandle( ), sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
|
||||
{
|
||||
// some error occurred
|
||||
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( mSqliteHandle ) ), tr( "SpatiaLite" ) );
|
||||
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( sqliteHandle( ) ) ), tr( "SpatiaLite" ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -4591,10 +4639,10 @@ bool QgsSpatiaLiteProvider::changeGeometryValues( const QgsGeometryMap &geometry
|
||||
else
|
||||
{
|
||||
// some unexpected error occurred
|
||||
const char *err = sqlite3_errmsg( mSqliteHandle );
|
||||
const char *err = sqlite3_errmsg( sqliteHandle( ) );
|
||||
errMsg = ( char * ) sqlite3_malloc( ( int ) strlen( err ) + 1 );
|
||||
strcpy( errMsg, err );
|
||||
handleError( sql, errMsg, true );
|
||||
handleError( sql, errMsg, savepointId );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -4602,12 +4650,16 @@ bool QgsSpatiaLiteProvider::changeGeometryValues( const QgsGeometryMap &geometry
|
||||
|
||||
sqlite3_finalize( stmt );
|
||||
|
||||
ret = sqlite3_exec( mSqliteHandle, "COMMIT", nullptr, nullptr, &errMsg );
|
||||
ret = exec_sql( QStringLiteral( "RELEASE SAVEPOINT \"%1\"" ).arg( savepointId ), errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg, true );
|
||||
handleError( sql, errMsg, savepointId );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( mTransaction )
|
||||
mTransaction->dirtyLastSavePoint();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -4706,7 +4758,7 @@ bool QgsSpatiaLiteProvider::checkLayerType()
|
||||
"WHERE lower(name) = lower(%1) "
|
||||
"AND type in ('table', 'view') " ).arg( QgsSqliteUtils::quotedString( mTableName ) );
|
||||
|
||||
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret == SQLITE_OK && rows == 1 )
|
||||
{
|
||||
QString type = QString( results[ columns + 0 ] );
|
||||
@ -4751,7 +4803,7 @@ bool QgsSpatiaLiteProvider::checkLayerType()
|
||||
QgsSqliteUtils::quotedIdentifier( alias ) );
|
||||
|
||||
sql = QStringLiteral( "SELECT 0, %1 FROM %2 LIMIT 1" ).arg( QgsSqliteUtils::quotedIdentifier( mGeometryColumn ), mQuery );
|
||||
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
|
||||
// Try to find a PK or try to use ROWID
|
||||
if ( ret == SQLITE_OK && rows == 1 )
|
||||
@ -4761,7 +4813,7 @@ bool QgsSpatiaLiteProvider::checkLayerType()
|
||||
// 1. find the table that provides geometry
|
||||
// String containing the name of the table that provides the geometry if the layer data source is based on a query
|
||||
QString queryGeomTableName;
|
||||
if ( sqlite3_prepare_v2( mSqliteHandle, sql.toUtf8().constData(), -1, &stmt, nullptr ) == SQLITE_OK )
|
||||
if ( sqlite3_prepare_v2( sqliteHandle( ), sql.toUtf8().constData(), -1, &stmt, nullptr ) == SQLITE_OK )
|
||||
{
|
||||
queryGeomTableName = sqlite3_column_table_name( stmt, 1 );
|
||||
}
|
||||
@ -4815,7 +4867,7 @@ bool QgsSpatiaLiteProvider::checkLayerType()
|
||||
|
||||
// Try first without any injection or manipulation
|
||||
sql = QStringLiteral( "SELECT %1, %2 FROM %3 LIMIT 1" ).arg( QgsSqliteUtils::quotedIdentifier( pks.first( ) ), QgsSqliteUtils::quotedIdentifier( mGeometryColumn ), mQuery );
|
||||
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret == SQLITE_OK && rows == 1 )
|
||||
{
|
||||
mPrimaryKey = pks.first( );
|
||||
@ -4828,7 +4880,7 @@ bool QgsSpatiaLiteProvider::checkLayerType()
|
||||
.arg( QgsSqliteUtils::quotedIdentifier( tableIdentifier ) )
|
||||
.arg( pks.first() ) ) );
|
||||
sql = QStringLiteral( "SELECT %1 FROM %2 LIMIT 1" ).arg( pk ).arg( newSql );
|
||||
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret == SQLITE_OK && rows == 1 )
|
||||
{
|
||||
mQuery = newSql;
|
||||
@ -4844,7 +4896,7 @@ bool QgsSpatiaLiteProvider::checkLayerType()
|
||||
if ( ! queryGeomTableName.isEmpty() )
|
||||
{
|
||||
sql = QStringLiteral( "SELECT ROWID FROM %1 WHERE ROWID IS NOT NULL LIMIT 1" ).arg( QgsSqliteUtils::quotedIdentifier( queryGeomTableName ) );
|
||||
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret != SQLITE_OK || rows != 1 )
|
||||
{
|
||||
queryGeomTableName = QString();
|
||||
@ -4858,7 +4910,7 @@ bool QgsSpatiaLiteProvider::checkLayerType()
|
||||
.arg( QgsSqliteUtils::quotedIdentifier( tableIdentifier ),
|
||||
QStringLiteral( "ROWID" ) ) ) );
|
||||
sql = QStringLiteral( "SELECT ROWID FROM %1 WHERE ROWID IS NOT NULL LIMIT 1" ).arg( newSql );
|
||||
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret == SQLITE_OK && rows == 1 )
|
||||
{
|
||||
mQuery = newSql;
|
||||
@ -4897,7 +4949,7 @@ bool QgsSpatiaLiteProvider::checkLayerType()
|
||||
.arg( QgsSqliteUtils::quotedString( mTableName ),
|
||||
QgsSqliteUtils::quotedString( mGeometryColumn ) );
|
||||
|
||||
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
if ( errMsg && strcmp( errMsg, "no such table: geometry_columns_auth" ) == 0 )
|
||||
@ -4906,7 +4958,7 @@ bool QgsSpatiaLiteProvider::checkLayerType()
|
||||
sql = QStringLiteral( "SELECT 0 FROM geometry_columns WHERE upper(f_table_name) = upper(%1) and upper(f_geometry_column) = upper(%2)" )
|
||||
.arg( QgsSqliteUtils::quotedString( mTableName ),
|
||||
QgsSqliteUtils::quotedString( mGeometryColumn ) );
|
||||
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
}
|
||||
}
|
||||
if ( ret == SQLITE_OK && rows == 1 )
|
||||
@ -4936,7 +4988,7 @@ bool QgsSpatiaLiteProvider::checkLayerType()
|
||||
" WHERE view_name=%1 and view_geometry=%2" ).arg( QgsSqliteUtils::quotedString( mTableName ),
|
||||
QgsSqliteUtils::quotedString( mGeometryColumn ) );
|
||||
|
||||
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret == SQLITE_OK && rows == 1 )
|
||||
{
|
||||
mViewBased = true;
|
||||
@ -4956,7 +5008,7 @@ bool QgsSpatiaLiteProvider::checkLayerType()
|
||||
" WHERE virt_name=%1 and virt_geometry=%2" ).arg( QgsSqliteUtils::quotedString( mTableName ),
|
||||
QgsSqliteUtils::quotedString( mGeometryColumn ) );
|
||||
|
||||
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret == SQLITE_OK && rows == 1 )
|
||||
{
|
||||
mVShapeBased = true;
|
||||
@ -5066,10 +5118,10 @@ void QgsSpatiaLiteProvider::getViewSpatialIndexName()
|
||||
"FROM views_geometry_columns "
|
||||
"WHERE upper(view_name) = upper(%1) and upper(view_geometry) = upper(%2)" ).arg( QgsSqliteUtils::quotedString( mTableName ),
|
||||
QgsSqliteUtils::quotedString( mGeometryColumn ) );
|
||||
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg );
|
||||
handleError( sql, errMsg, QString() );
|
||||
}
|
||||
if ( rows < 1 )
|
||||
;
|
||||
@ -5118,7 +5170,7 @@ bool QgsSpatiaLiteProvider::getTableGeometryDetails()
|
||||
mIndexGeometry = mGeometryColumn;
|
||||
|
||||
QString sql;
|
||||
if ( ! versionIsAbove( mSqliteHandle, 3, 1 ) )
|
||||
if ( ! versionIsAbove( sqliteHandle( ), 3, 1 ) )
|
||||
{
|
||||
sql = QString( "SELECT type, srid, spatial_index_enabled, coord_dimension FROM geometry_columns"
|
||||
" WHERE upper(f_table_name) = upper(%1) and upper(f_geometry_column) = upper(%2)" ).arg( QgsSqliteUtils::quotedString( mTableName ),
|
||||
@ -5131,10 +5183,10 @@ bool QgsSpatiaLiteProvider::getTableGeometryDetails()
|
||||
QgsSqliteUtils::quotedString( mGeometryColumn ) );
|
||||
}
|
||||
|
||||
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg );
|
||||
handleError( sql, errMsg, QString() );
|
||||
return false;
|
||||
}
|
||||
if ( rows < 1 )
|
||||
@ -5208,7 +5260,7 @@ bool QgsSpatiaLiteProvider::getTableGeometryDetails()
|
||||
|
||||
if ( mGeomType == QgsWkbTypes::Unknown || mSrid < 0 )
|
||||
{
|
||||
handleError( sql, errMsg );
|
||||
handleError( sql, errMsg, QString() );
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -5230,10 +5282,10 @@ bool QgsSpatiaLiteProvider::getViewGeometryDetails()
|
||||
" WHERE upper(view_name) = upper(%1) and upper(view_geometry) = upper(%2)" ).arg( QgsSqliteUtils::quotedString( mTableName ),
|
||||
QgsSqliteUtils::quotedString( mGeometryColumn ) );
|
||||
|
||||
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg );
|
||||
handleError( sql, errMsg, QString() );
|
||||
return false;
|
||||
}
|
||||
if ( rows < 1 )
|
||||
@ -5288,7 +5340,7 @@ bool QgsSpatiaLiteProvider::getViewGeometryDetails()
|
||||
|
||||
if ( mGeomType == QgsWkbTypes::Unknown || mSrid < 0 )
|
||||
{
|
||||
handleError( sql, errMsg );
|
||||
handleError( sql, errMsg, QString() );
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -5308,10 +5360,10 @@ bool QgsSpatiaLiteProvider::getVShapeGeometryDetails()
|
||||
" WHERE virt_name=%1 and virt_geometry=%2" ).arg( QgsSqliteUtils::quotedString( mTableName ),
|
||||
QgsSqliteUtils::quotedString( mGeometryColumn ) );
|
||||
|
||||
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg );
|
||||
handleError( sql, errMsg, QString() );
|
||||
return false;
|
||||
}
|
||||
if ( rows < 1 )
|
||||
@ -5355,7 +5407,7 @@ bool QgsSpatiaLiteProvider::getVShapeGeometryDetails()
|
||||
|
||||
if ( mGeomType == QgsWkbTypes::Unknown || mSrid < 0 )
|
||||
{
|
||||
handleError( sql, errMsg );
|
||||
handleError( sql, errMsg, QString() );
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -5376,7 +5428,7 @@ bool QgsSpatiaLiteProvider::getQueryGeometryDetails()
|
||||
|
||||
// get stuff from the relevant column instead. This may (will?)
|
||||
// fail if there is no data in the relevant table.
|
||||
QString sql = QStringLiteral( "select srid(%1), geometrytype(%1) from %2" )
|
||||
QString sql = QStringLiteral( "SELECT srid(%1), geometrytype(%1) FROM %2" )
|
||||
.arg( QgsSqliteUtils::quotedIdentifier( mGeometryColumn ),
|
||||
mQuery );
|
||||
|
||||
@ -5388,10 +5440,10 @@ bool QgsSpatiaLiteProvider::getQueryGeometryDetails()
|
||||
|
||||
sql += QLatin1String( " limit 1" );
|
||||
|
||||
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg );
|
||||
handleError( sql, errMsg, QString() );
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -5412,23 +5464,23 @@ bool QgsSpatiaLiteProvider::getQueryGeometryDetails()
|
||||
if ( fType == QLatin1String( "GEOMETRY" ) )
|
||||
{
|
||||
// check to see if there is a unique geometry type
|
||||
sql = QString( "select distinct "
|
||||
"case"
|
||||
" when geometrytype(%1) IN ('POINT','MULTIPOINT') THEN 'POINT'"
|
||||
" when geometrytype(%1) IN ('LINESTRING','MULTILINESTRING') THEN 'LINESTRING'"
|
||||
" when geometrytype(%1) IN ('POLYGON','MULTIPOLYGON') THEN 'POLYGON'"
|
||||
" end "
|
||||
"from %2" )
|
||||
sql = QString( "SELECT DISTINCT "
|
||||
"CASE"
|
||||
" WHEN geometrytype(%1) IN ('POINT','MULTIPOINT') THEN 'POINT'"
|
||||
" WHEN geometrytype(%1) IN ('LINESTRING','MULTILINESTRING') THEN 'LINESTRING'"
|
||||
" WHEN geometrytype(%1) IN ('POLYGON','MULTIPOLYGON') THEN 'POLYGON'"
|
||||
" END "
|
||||
"FROM %2" )
|
||||
.arg( QgsSqliteUtils::quotedIdentifier( mGeometryColumn ),
|
||||
mQuery );
|
||||
|
||||
if ( !mSubsetString.isEmpty() )
|
||||
sql += " where " + mSubsetString;
|
||||
|
||||
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg );
|
||||
handleError( sql, errMsg, QString() );
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -5473,7 +5525,7 @@ bool QgsSpatiaLiteProvider::getQueryGeometryDetails()
|
||||
|
||||
if ( mGeomType == QgsWkbTypes::Unknown || mSrid < 0 )
|
||||
{
|
||||
handleError( sql, errMsg );
|
||||
handleError( sql, errMsg, QString() );
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -5491,10 +5543,10 @@ bool QgsSpatiaLiteProvider::getSridDetails()
|
||||
|
||||
QString sql = QStringLiteral( "SELECT auth_name||':'||auth_srid,proj4text FROM spatial_ref_sys WHERE srid=%1" ).arg( mSrid );
|
||||
|
||||
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg );
|
||||
handleError( sql, errMsg, QString() );
|
||||
return false;
|
||||
}
|
||||
if ( rows < 1 )
|
||||
@ -5550,10 +5602,10 @@ bool QgsSpatiaLiteProvider::getTableSummary()
|
||||
sql += " WHERE ( " + mSubsetString + ')';
|
||||
}
|
||||
|
||||
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret != SQLITE_OK )
|
||||
{
|
||||
handleError( sql, errMsg );
|
||||
handleError( sql, errMsg, QString() );
|
||||
return false;
|
||||
}
|
||||
if ( rows < 1 )
|
||||
@ -5668,6 +5720,17 @@ QList<QgsVectorLayer *> QgsSpatiaLiteProvider::searchLayers( const QList<QgsVect
|
||||
return result;
|
||||
}
|
||||
|
||||
void QgsSpatiaLiteProvider::setTransaction( QgsTransaction *transaction )
|
||||
{
|
||||
QgsDebugMsgLevel( QStringLiteral( "set transaction %1" ).arg( transaction != nullptr ), 1 );
|
||||
// static_cast since layers cannot be added to a transaction of a non-matching provider
|
||||
mTransaction = static_cast<QgsSpatiaLiteTransaction *>( transaction );
|
||||
}
|
||||
|
||||
QgsTransaction *QgsSpatiaLiteProvider::transaction( ) const
|
||||
{
|
||||
return static_cast<QgsTransaction *>( mTransaction );
|
||||
}
|
||||
|
||||
QList<QgsRelation> QgsSpatiaLiteProvider::discoverRelations( const QgsVectorLayer *self, const QList<QgsVectorLayer *> &layers ) const
|
||||
{
|
||||
@ -5677,7 +5740,7 @@ QList<QgsRelation> QgsSpatiaLiteProvider::discoverRelations( const QgsVectorLaye
|
||||
int rows;
|
||||
int columns;
|
||||
char *errMsg = nullptr;
|
||||
int ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
int ret = sqlite3_get_table( sqliteHandle( ), sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
||||
if ( ret == SQLITE_OK )
|
||||
{
|
||||
int nbFound = 0;
|
||||
@ -6140,7 +6203,21 @@ QList< QgsDataItemProvider * > QgsSpatiaLiteProviderMetadata::dataItemProviders(
|
||||
return providers;
|
||||
}
|
||||
|
||||
|
||||
QgsTransaction *QgsSpatiaLiteProviderMetadata::createTransaction( const QString &connString )
|
||||
{
|
||||
const QgsDataSourceUri dsUri{ connString };
|
||||
// Cannot use QgsSpatiaLiteConnPool::instance()->acquireConnection( dsUri.database() ) };
|
||||
// because it will return a read only connection, use the (cached) connection from the
|
||||
// layers instead.
|
||||
QgsSqliteHandle *ds { QgsSqliteHandle::openDb( dsUri.database() ) };
|
||||
if ( !ds )
|
||||
{
|
||||
QgsMessageLog::logMessage( QObject::tr( "Cannot open transaction on %1, since it is is not currently opened" ).arg( connString ),
|
||||
QObject::tr( "spatialite" ), Qgis::Critical );
|
||||
return nullptr;
|
||||
}
|
||||
return new QgsSpatiaLiteTransaction( connString, ds );
|
||||
}
|
||||
|
||||
QMap<QString, QgsAbstractProviderConnection *> QgsSpatiaLiteProviderMetadata::connections( bool cached )
|
||||
{
|
||||
|
||||
@ -43,6 +43,8 @@ class QgsField;
|
||||
|
||||
class QgsSqliteHandle;
|
||||
class QgsSpatiaLiteFeatureIterator;
|
||||
class QgsSpatiaLiteTransaction;
|
||||
class QgsTransaction;
|
||||
|
||||
#include "qgsdatasourceuri.h"
|
||||
|
||||
@ -220,6 +222,12 @@ class QgsSpatiaLiteProvider final: public QgsVectorDataProvider
|
||||
*/
|
||||
static QList<QgsVectorLayer *> searchLayers( const QList<QgsVectorLayer *> &layers, const QString &connectionInfo, const QString &tableName );
|
||||
|
||||
QgsSpatiaLiteTransaction *mTransaction = nullptr;
|
||||
|
||||
QgsTransaction *transaction() const override;
|
||||
|
||||
void setTransaction( QgsTransaction *transaction ) override;
|
||||
|
||||
QgsFields mAttributeFields;
|
||||
|
||||
//! Map of field index to default value SQL fragments
|
||||
@ -325,6 +333,10 @@ class QgsSpatiaLiteProvider final: public QgsVectorDataProvider
|
||||
//! SpatiaLite minor version
|
||||
int mSpatialiteVersionMinor = 0;
|
||||
|
||||
//! Internal transaction handling (for addFeatures etc.)
|
||||
int mSavepointId;
|
||||
static QAtomicInt sSavepointId;
|
||||
|
||||
/**
|
||||
* internal utility functions used to handle common SQLite tasks
|
||||
*/
|
||||
@ -382,7 +394,17 @@ class QgsSpatiaLiteProvider final: public QgsVectorDataProvider
|
||||
/**
|
||||
* Handles an error encountered while executing an sql statement.
|
||||
*/
|
||||
void handleError( const QString &sql, char *errorMessage, bool rollback = false );
|
||||
void handleError( const QString &sql, char *errorMessage, const QString &savepointId );
|
||||
|
||||
/**
|
||||
* Sqlite exec sql wrapper for SQL logging
|
||||
*/
|
||||
int exec_sql( const QString &sql, char *errMsg = nullptr );
|
||||
|
||||
/**
|
||||
* Returns the sqlite handle to be used, if we are inside a transaction it will be the transaction's handle
|
||||
*/
|
||||
sqlite3 *sqliteHandle( ) const;
|
||||
|
||||
friend class QgsSpatiaLiteFeatureSource;
|
||||
|
||||
@ -421,14 +443,14 @@ class QgsSpatiaLiteProviderMetadata final: public QgsProviderMetadata
|
||||
QgsAbstractProviderConnection *createConnection( const QString &name ) override;
|
||||
void deleteConnection( const QString &name ) override;
|
||||
void saveConnection( const QgsAbstractProviderConnection *connection, const QString &name ) override;
|
||||
QgsTransaction *createTransaction( const QString &connString ) override;
|
||||
|
||||
protected:
|
||||
|
||||
QgsAbstractProviderConnection *createConnection( const QString &uri, const QVariantMap &configuration ) override;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
// clazy:excludeall=qstring-allocations
|
||||
|
||||
#endif
|
||||
|
||||
89
src/providers/spatialite/qgsspatialitetransaction.cpp
Normal file
89
src/providers/spatialite/qgsspatialitetransaction.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
/***************************************************************************
|
||||
qgsspatialitetransaction.cpp - QgsSpatialiteTransaction
|
||||
|
||||
---------------------
|
||||
begin : 30.3.2020
|
||||
copyright : (C) 2020 by Alessandro Pasotti
|
||||
email : elpaso at itopen dot it
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgsspatialitetransaction.h"
|
||||
#include "qgslogger.h"
|
||||
#include <QDebug>
|
||||
|
||||
///@cond PRIVATE
|
||||
|
||||
QAtomicInt QgsSpatiaLiteTransaction::sSavepointId = 0;
|
||||
|
||||
QgsSpatiaLiteTransaction::QgsSpatiaLiteTransaction( const QString &connString, QgsSqliteHandle *sharedHandle )
|
||||
: QgsTransaction( connString )
|
||||
, mSharedHandle( sharedHandle )
|
||||
{
|
||||
if ( mSharedHandle )
|
||||
mSqliteHandle = mSharedHandle->handle();
|
||||
mSavepointId = ++sSavepointId;
|
||||
}
|
||||
|
||||
bool QgsSpatiaLiteTransaction::beginTransaction( QString &error, int /* statementTimeout */ )
|
||||
{
|
||||
return executeSql( QStringLiteral( "BEGIN" ), error );
|
||||
}
|
||||
|
||||
bool QgsSpatiaLiteTransaction::commitTransaction( QString &error )
|
||||
{
|
||||
return executeSql( QStringLiteral( "COMMIT" ), error );
|
||||
}
|
||||
|
||||
bool QgsSpatiaLiteTransaction::rollbackTransaction( QString &error )
|
||||
{
|
||||
return executeSql( QStringLiteral( "ROLLBACK" ), error );
|
||||
}
|
||||
|
||||
bool QgsSpatiaLiteTransaction::executeSql( const QString &sql, QString &errorMsg, bool isDirty, const QString &name )
|
||||
{
|
||||
|
||||
if ( ! mSqliteHandle )
|
||||
{
|
||||
QgsDebugMsg( QStringLiteral( "Spatialite handle is not set" ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
QString err;
|
||||
if ( isDirty )
|
||||
{
|
||||
createSavepoint( err );
|
||||
}
|
||||
char *errMsg = nullptr;
|
||||
if ( sqlite3_exec( mSqliteHandle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg ) != SQLITE_OK )
|
||||
{
|
||||
QgsDebugMsg( errorMsg );
|
||||
if ( isDirty )
|
||||
{
|
||||
rollbackToSavepoint( savePoints().last(), err );
|
||||
}
|
||||
sqlite3_free( errMsg );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( isDirty )
|
||||
{
|
||||
dirtyLastSavePoint();
|
||||
emit dirtied( sql, name );
|
||||
}
|
||||
|
||||
QgsDebugMsg( QStringLiteral( "... ok" ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
sqlite3 *QgsSpatiaLiteTransaction::sqliteHandle() const
|
||||
{
|
||||
return mSqliteHandle;
|
||||
}
|
||||
///@endcond
|
||||
64
src/providers/spatialite/qgsspatialitetransaction.h
Normal file
64
src/providers/spatialite/qgsspatialitetransaction.h
Normal file
@ -0,0 +1,64 @@
|
||||
/***************************************************************************
|
||||
qgsspatialitetransaction.h - QgsSpatialiteTransaction
|
||||
|
||||
---------------------
|
||||
begin : 30.3.2020
|
||||
copyright : (C) 2020 by Alessandro Pasotti
|
||||
email : elpaso at itopen dot it
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#ifndef QGSSPATIALITETRANSACTION_H
|
||||
#define QGSSPATIALITETRANSACTION_H
|
||||
|
||||
#include "qgstransaction.h"
|
||||
#include "qgsspatialiteconnection.h"
|
||||
#include "qgis_sip.h"
|
||||
|
||||
///@cond PRIVATE
|
||||
#define SIP_NO_FILE
|
||||
|
||||
class QgsSpatiaLiteTransaction : public QgsTransaction
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QgsSpatiaLiteTransaction( const QString &connString, QgsSqliteHandle *sharedHandle );
|
||||
|
||||
/**
|
||||
* Executes the SQL query in database.
|
||||
*
|
||||
* \param sql The SQL query to execute
|
||||
* \param error The error or an empty string if none
|
||||
* \param isDirty True to add an undo/redo command in the edition buffer, false otherwise
|
||||
* \param name Name of the operation ( only used if `isDirty` is true)
|
||||
*/
|
||||
bool executeSql( const QString &sql, QString &error, bool isDirty = false, const QString &name = QString() ) override;
|
||||
|
||||
/**
|
||||
* Returns the (possibly NULL) sqlite handle
|
||||
*/
|
||||
sqlite3 *sqliteHandle() const;
|
||||
|
||||
private:
|
||||
|
||||
QgsSqliteHandle *mSharedHandle = nullptr;
|
||||
int mSavepointId;
|
||||
|
||||
//! SQLite handle
|
||||
sqlite3 *mSqliteHandle = nullptr;
|
||||
|
||||
bool beginTransaction( QString &error, int statementTimeout ) override;
|
||||
bool commitTransaction( QString &error ) override;
|
||||
bool rollbackTransaction( QString &error ) override;
|
||||
|
||||
static QAtomicInt sSavepointId;
|
||||
};
|
||||
|
||||
///@endcond
|
||||
#endif // QGSSPATIALITETRANSACTION_H
|
||||
@ -17,9 +17,11 @@ import re
|
||||
import sys
|
||||
import shutil
|
||||
import tempfile
|
||||
from osgeo import ogr
|
||||
from datetime import datetime
|
||||
|
||||
from qgis.core import (QgsProviderRegistry,
|
||||
QgsDataSourceUri,
|
||||
QgsVectorLayer,
|
||||
QgsVectorDataProvider,
|
||||
QgsPointXY,
|
||||
@ -1295,6 +1297,85 @@ class TestQgsSpatialiteProvider(unittest.TestCase, ProviderTestCase):
|
||||
self.assertEqual(vl.featureCount(), 1)
|
||||
self.assertEqual(vl.getFeature(1).geometry().asWkt().upper(), 'POINT (9 45)')
|
||||
|
||||
def testTransaction(self):
|
||||
"""Test spatialite transactions"""
|
||||
|
||||
tmpfile = tempfile.mktemp('.db')
|
||||
ds = ogr.GetDriverByName('SQLite').CreateDataSource(tmpfile, options=['SPATIALITE=YES'])
|
||||
lyr = ds.CreateLayer('lyr1', geom_type=ogr.wkbPoint)
|
||||
f = ogr.Feature(lyr.GetLayerDefn())
|
||||
f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 1)'))
|
||||
lyr.CreateFeature(f)
|
||||
lyr = ds.CreateLayer('lyr2', geom_type=ogr.wkbPoint)
|
||||
f = ogr.Feature(lyr.GetLayerDefn())
|
||||
f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(2 3)'))
|
||||
lyr.CreateFeature(f)
|
||||
f = ogr.Feature(lyr.GetLayerDefn())
|
||||
f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(4 5)'))
|
||||
lyr.CreateFeature(f)
|
||||
ds = None
|
||||
|
||||
uri1 = QgsDataSourceUri()
|
||||
uri1.setDatabase(tmpfile)
|
||||
uri1.setTable('lyr1')
|
||||
uri2 = QgsDataSourceUri()
|
||||
uri2.setDatabase(tmpfile)
|
||||
uri2.setTable('lyr2')
|
||||
|
||||
vl1 = QgsVectorLayer(uri1.uri(), 'test', 'spatialite')
|
||||
self.assertTrue(vl1.isValid())
|
||||
vl2 = QgsVectorLayer(uri2.uri(), 'test', 'spatialite')
|
||||
self.assertTrue(vl2.isValid())
|
||||
|
||||
# prepare a project with transactions enabled
|
||||
p = QgsProject()
|
||||
p.setAutoTransaction(True)
|
||||
p.addMapLayers([vl1, vl2])
|
||||
|
||||
self.assertTrue(vl1.startEditing())
|
||||
self.assertIsNotNone(vl1.dataProvider().transaction())
|
||||
|
||||
self.assertTrue(vl1.deleteFeature(1))
|
||||
|
||||
# An iterator opened on the layer should see the feature deleted
|
||||
self.assertEqual(len([f for f in vl1.getFeatures(QgsFeatureRequest())]), 0)
|
||||
|
||||
# But not if opened from another connection
|
||||
vl1_external = QgsVectorLayer(uri1.uri(), 'test', 'spatialite')
|
||||
self.assertTrue(vl1_external.isValid())
|
||||
self.assertEqual(len([f for f in vl1_external.getFeatures(QgsFeatureRequest())]), 1)
|
||||
del vl1_external
|
||||
|
||||
self.assertTrue(vl1.commitChanges())
|
||||
|
||||
# Should still get zero features on vl1
|
||||
self.assertEqual(len([f for f in vl1.getFeatures(QgsFeatureRequest())]), 0)
|
||||
self.assertEqual(len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 2)
|
||||
|
||||
# Test undo/redo
|
||||
self.assertTrue(vl2.startEditing())
|
||||
self.assertIsNotNone(vl2.dataProvider().transaction())
|
||||
self.assertTrue(vl2.editBuffer().deleteFeature(1))
|
||||
self.assertEqual(len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 1)
|
||||
self.assertTrue(vl2.editBuffer().deleteFeature(2))
|
||||
self.assertEqual(len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 0)
|
||||
vl2.undoStack().undo()
|
||||
self.assertEqual(len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 1)
|
||||
vl2.undoStack().undo()
|
||||
self.assertEqual(len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 2)
|
||||
vl2.undoStack().redo()
|
||||
self.assertEqual(len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 1)
|
||||
self.assertTrue(vl2.commitChanges())
|
||||
|
||||
self.assertEqual(len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 1)
|
||||
del vl1
|
||||
del vl2
|
||||
|
||||
vl2_external = QgsVectorLayer(uri2.uri(), 'test', 'spatialite')
|
||||
self.assertTrue(vl2_external.isValid())
|
||||
self.assertEqual(len([f for f in vl2_external.getFeatures(QgsFeatureRequest())]), 1)
|
||||
del vl2_external
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user