Use std::unique_ptr with custom deleter for spatialite and sqlite

This commit is contained in:
Matthias Kuhn 2017-11-14 03:01:54 +01:00
parent 340f7abbb0
commit 4df65aca37
15 changed files with 437 additions and 407 deletions

View File

@ -10,8 +10,6 @@
class QgsAuxiliaryLayer : QgsVectorLayer
{
%Docstring

View File

@ -26,6 +26,7 @@
#include "qgsprojectionselectiondialog.h"
#include "qgscrscache.h"
#include "qgssettings.h"
#include "qgssqliteutils.h"
//qt includes
#include <QFileInfo>
@ -42,121 +43,6 @@ extern "C"
#include <proj_api.h>
}
/**
* Closes a sqlite3 database.
*/
struct QgsSqlite3Closer
{
/**
* Closes an sqlite \a database.
*/
void operator()( sqlite3 *database )
{
sqlite3_close( database );
}
};
/**
* Finalizes an sqlite3 statement.
*/
struct QgsSqlite3StatementFinalizer
{
/**
* Finalizes an sqlite3 \a statement.
*/
void operator()( sqlite3_stmt *statement )
{
sqlite3_finalize( statement );
}
};
/**
* Unique pointer for sqlite3 prepared statements, which automatically finalizes
* the statement when the pointer goes out of scope or is reset.
*/
class sqlite3_statement_unique_ptr : public std::unique_ptr< sqlite3_stmt, QgsSqlite3StatementFinalizer>
{
public:
/**
* Steps to the next record in the statement, returning the sqlite3 result code.
*/
int step()
{
return sqlite3_step( get() );
}
/**
* Returns the column value from the current statement row as a string.
*/
QString columnAsText( int column )
{
return QString::fromUtf8( ( char * ) sqlite3_column_text( get(), column ) );
}
};
/**
* Unique pointer for sqlite3 databases, which automatically closes
* the database when the pointer goes out of scope or is reset.
*/
class sqlite3_database_unique_ptr : public std::unique_ptr< sqlite3, QgsSqlite3Closer>
{
public:
/**
* Opens the database at the specified file \a path.
*
* Returns the sqlite error code, or SQLITE_OK if open was successful.
*/
int open( const QString &path )
{
sqlite3 *database = nullptr;
int result = sqlite3_open( path.toUtf8(), &database );
reset( database );
return result;
}
/**
* Opens the database at the specified file \a path.
*
* Returns the sqlite error code, or SQLITE_OK if open was successful.
*/
int open_v2( const QString &path, int flags, const char *zVfs )
{
sqlite3 *database = nullptr;
int result = sqlite3_open_v2( path.toUtf8(), &database, flags, zVfs );
reset( database );
return result;
}
/**
* Returns the most recent error message encountered by the database.
*/
QString errorMessage() const
{
return QString( sqlite3_errmsg( get() ) );
}
/**
* Prepares a \a sql statement, returning the result. The \a resultCode
* argument will be filled with the sqlite3 result code.
*/
sqlite3_statement_unique_ptr prepare( const QString &sql, int &resultCode )
{
sqlite3_stmt *preparedStatement = nullptr;
const char *tail = nullptr;
resultCode = sqlite3_prepare( get(), sql.toUtf8(), sql.toUtf8().length(), &preparedStatement, &tail );
sqlite3_statement_unique_ptr s;
s.reset( preparedStatement );
return s;
}
};
QgsCustomProjectionDialog::QgsCustomProjectionDialog( QWidget *parent, Qt::WindowFlags fl )
: QDialog( parent, fl )

View File

@ -28,7 +28,7 @@
#include "qgsproject.h"
#include "qgscoordinatereferencesystem.h"
#include "qgsprojectionselectiondialog.h"
#include "qgsslconnect.h"
#include "qgsspatialiteutils.h"
#include "qgslogger.h"
#include "qgssettings.h"
@ -382,8 +382,8 @@ bool QgsNewSpatialiteLayerDialog::apply()
quotedValue( leGeometryColumn->text() ) );
QgsDebugMsg( sqlCreateIndex ); // OK
sqlite3 *db = nullptr;
int rc = QgsSLConnect::sqlite3_open( mDatabaseComboBox->currentText().toUtf8(), &db );
spatialite_database_unique_ptr database;
int rc = database.open( mDatabaseComboBox->currentText() );
if ( rc != SQLITE_OK )
{
QMessageBox::warning( this,
@ -393,7 +393,7 @@ bool QgsNewSpatialiteLayerDialog::apply()
else
{
char *errmsg = nullptr;
rc = sqlite3_exec( db, sql.toUtf8(), nullptr, nullptr, &errmsg );
rc = sqlite3_exec( database.get(), sql.toUtf8(), nullptr, nullptr, &errmsg );
if ( rc != SQLITE_OK )
{
QMessageBox::warning( this,
@ -404,7 +404,7 @@ bool QgsNewSpatialiteLayerDialog::apply()
else
{
// create the geometry column and the spatial index
rc = sqlite3_exec( db, sqlAddGeom.toUtf8(), nullptr, nullptr, &errmsg );
rc = sqlite3_exec( database.get(), sqlAddGeom.toUtf8(), nullptr, nullptr, &errmsg );
if ( rc != SQLITE_OK )
{
QMessageBox::warning( this,
@ -415,7 +415,7 @@ bool QgsNewSpatialiteLayerDialog::apply()
else
{
// create the spatial index
rc = sqlite3_exec( db, sqlCreateIndex.toUtf8(), nullptr, nullptr, &errmsg );
rc = sqlite3_exec( database.get(), sqlCreateIndex.toUtf8(), nullptr, nullptr, &errmsg );
if ( rc != SQLITE_OK )
{
QMessageBox::warning( this,
@ -446,8 +446,6 @@ bool QgsNewSpatialiteLayerDialog::apply()
}
}
}
QgsSLConnect::sqlite3_close( db );
}
return false;

View File

@ -267,12 +267,13 @@ SET(QGIS_CORE_SRCS
qgsscalecalculator.cpp
qgsscaleutils.cpp
qgssimplifymethod.cpp
qgsslconnect.cpp
qgssnappingutils.cpp
qgsspatialindex.cpp
qgssqlexpressioncompiler.cpp
qgssqliteexpressioncompiler.cpp
qgssqlstatement.cpp
qgssqliteutils.cpp
qgsspatialiteutils.cpp
qgsstatisticalsummary.cpp
qgsstringstatisticalsummary.cpp
qgsstringutils.cpp
@ -924,6 +925,9 @@ SET(QGIS_CORE_HDRS
qgssimplifymethod.h
qgssnappingutils.h
qgsspatialindex.h
qgsspatialiteutils.h
qgssqlstatement.h
qgssqliteutils.h
qgssqlexpressioncompiler.h
qgssqlstatement.h
qgsstatisticalsummary.h

View File

@ -17,13 +17,14 @@
#include "qgsauxiliarystorage.h"
#include "qgslogger.h"
#include "qgsslconnect.h"
#include "qgsspatialiteutils.h"
#include "qgsproject.h"
#include "qgsvectorlayerlabeling.h"
#include "qgsdiagramrenderer.h"
#include "qgsmemoryproviderutils.h"
#include "qgssymbollayer.h"
#include <sqlite3.h>
#include <QFile>
const QString AS_JOINFIELD = "ASPK";
@ -509,8 +510,7 @@ QgsAuxiliaryStorage::QgsAuxiliaryStorage( const QgsProject &project, bool copy )
mFileName = asFileName;
}
sqlite3 *handler = open( mFileName );
close( handler );
open( mFileName );
}
QgsAuxiliaryStorage::QgsAuxiliaryStorage( const QString &filename, bool copy )
@ -519,8 +519,7 @@ QgsAuxiliaryStorage::QgsAuxiliaryStorage( const QString &filename, bool copy )
{
initTmpFileName();
sqlite3 *handler = open( filename );
close( handler );
open( filename );
}
QgsAuxiliaryStorage::~QgsAuxiliaryStorage()
@ -568,20 +567,19 @@ QgsAuxiliaryLayer *QgsAuxiliaryStorage::createAuxiliaryLayer( const QgsField &fi
if ( mValid && layer )
{
const QString table( layer->id() );
sqlite3 *handler = openDB( currentFileName() );
spatialite_database_unique_ptr database;
database = openDB( currentFileName() );
if ( !tableExists( table, handler ) )
if ( !tableExists( table, database.get() ) )
{
if ( !createTable( field.typeName(), table, handler ) )
if ( !createTable( field.typeName(), table, database.get() ) )
{
close( handler );
return alayer;
}
}
alayer = new QgsAuxiliaryLayer( field.name(), currentFileName(), table, layer );
alayer->startEditing();
close( handler );
}
return alayer;
@ -594,17 +592,16 @@ bool QgsAuxiliaryStorage::deleteTable( const QgsDataSourceUri &ogrUri )
if ( !uri.database().isEmpty() && !uri.table().isEmpty() )
{
sqlite3 *handler = openDB( uri.database() );
spatialite_database_unique_ptr database;
database = openDB( uri.database() );
if ( handler )
if ( database )
{
QString sql = QString( "DROP TABLE %1" ).arg( uri.table() );
rc = exec( sql, handler );
rc = exec( sql, database.get() );
sql = QString( "VACUUM" );
rc = exec( sql, handler );
close( handler );
sql = QStringLiteral( "VACUUM" );
rc = exec( sql, database.get() );
}
}
@ -618,14 +615,13 @@ bool QgsAuxiliaryStorage::duplicateTable( const QgsDataSourceUri &ogrUri, const
if ( !uri.table().isEmpty() && !uri.database().isEmpty() )
{
sqlite3 *handler = openDB( uri.database() );
spatialite_database_unique_ptr database;
database = openDB( uri.database() );
if ( handler )
if ( database )
{
QString sql = QString( "CREATE TABLE %1 AS SELECT * FROM %2" ).arg( newTable, uri.table() );
rc = exec( sql, handler );
close( handler );
QString sql = QStringLiteral( "CREATE TABLE %1 AS SELECT * FROM %2" ).arg( newTable, uri.table() );
rc = exec( sql, database.get() );
}
}
@ -675,23 +671,9 @@ void QgsAuxiliaryStorage::debugMsg( const QString &sql, sqlite3 *handler )
QgsDebugMsg( errMsg );
}
sqlite3 *QgsAuxiliaryStorage::openDB( const QString &filename )
{
sqlite3 *handler = nullptr;
bool rc = QgsSLConnect::sqlite3_open_v2( filename.toUtf8().constData(), &handler, SQLITE_OPEN_READWRITE, nullptr );
if ( rc )
{
debugMsg( "sqlite3_open_v2", handler );
return nullptr;
}
return handler;
}
bool QgsAuxiliaryStorage::createTable( const QString &type, const QString &table, sqlite3 *handler )
{
const QString sql = QString( "CREATE TABLE IF NOT EXISTS '%1' ( '%2' %3 )" ).arg( table ).arg( AS_JOINFIELD ).arg( type );
const QString sql = QStringLiteral( "CREATE TABLE IF NOT EXISTS '%1' ( '%2' %3 )" ).arg( table ).arg( AS_JOINFIELD ).arg( type );
if ( !exec( sql, handler ) )
return false;
@ -699,29 +681,42 @@ bool QgsAuxiliaryStorage::createTable( const QString &type, const QString &table
return true;
}
sqlite3 *QgsAuxiliaryStorage::createDB( const QString &filename )
spatialite_database_unique_ptr QgsAuxiliaryStorage::createDB( const QString &filename )
{
sqlite3 *handler = nullptr;
int rc;
// open/create database
rc = QgsSLConnect::sqlite3_open_v2( filename.toUtf8().constData(), &handler, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr );
spatialite_database_unique_ptr database;
rc = database.open_v2( filename, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr );
if ( rc )
{
debugMsg( "sqlite3_open_v2", handler );
return handler;
debugMsg( QStringLiteral( "sqlite3_open_v2" ), database.get() );
return database;
}
// activating Foreign Key constraints
if ( !exec( "PRAGMA foreign_keys = 1", handler ) )
return handler;
if ( !exec( QStringLiteral( "PRAGMA foreign_keys = 1" ), database.get() ) )
return database;
return handler;
return database;
}
spatialite_database_unique_ptr QgsAuxiliaryStorage::openDB( const QString &filename )
{
spatialite_database_unique_ptr database;
int rc = database.open_v2( filename, SQLITE_OPEN_READWRITE, nullptr );
if ( rc )
{
debugMsg( "sqlite3_open_v2", database.get() );
}
return database;
}
bool QgsAuxiliaryStorage::tableExists( const QString &table, sqlite3 *handler )
{
const QString sql = QString( "SELECT 1 FROM sqlite_master WHERE type='table' AND name='%1'" ).arg( table );
const QString sql = QStringLiteral( "SELECT 1 FROM sqlite_master WHERE type='table' AND name='%1'" ).arg( table );
int rows = 0;
int columns = 0;
char **results = nullptr;
@ -739,13 +734,13 @@ bool QgsAuxiliaryStorage::tableExists( const QString &table, sqlite3 *handler )
return false;
}
sqlite3 *QgsAuxiliaryStorage::open( const QString &filename )
spatialite_database_unique_ptr QgsAuxiliaryStorage::open( const QString &filename )
{
sqlite3 *handler = nullptr;
spatialite_database_unique_ptr database;
if ( filename.isEmpty() )
{
if ( ( handler = createDB( currentFileName() ) ) )
if ( ( database = createDB( currentFileName() ) ) )
mValid = true;
}
else if ( QFile::exists( filename ) )
@ -753,37 +748,28 @@ sqlite3 *QgsAuxiliaryStorage::open( const QString &filename )
if ( mCopy )
QFile::copy( filename, mTmpFileName );
if ( ( handler = openDB( currentFileName() ) ) )
if ( ( database = openDB( currentFileName() ) ) )
mValid = true;
}
else
{
if ( ( handler = createDB( currentFileName() ) ) )
if ( ( database = createDB( currentFileName() ) ) )
mValid = true;
}
return handler;
return database;
}
sqlite3 *QgsAuxiliaryStorage::open( const QgsProject &project )
spatialite_database_unique_ptr QgsAuxiliaryStorage::open( const QgsProject &project )
{
return open( filenameForProject( project ) );
}
void QgsAuxiliaryStorage::close( sqlite3 *handler )
{
if ( handler )
{
QgsSLConnect::sqlite3_close_v2( handler );
handler = nullptr;
}
}
QString QgsAuxiliaryStorage::filenameForProject( const QgsProject &project )
{
const QFileInfo info = project.fileInfo();
const QString path = info.path() + QDir::separator() + info.baseName();
return path + "." + QgsAuxiliaryStorage::extension();
return path + '.' + QgsAuxiliaryStorage::extension();
}
void QgsAuxiliaryStorage::initTmpFileName()
@ -820,7 +806,7 @@ QgsDataSourceUri QgsAuxiliaryStorage::parseOgrUri( const QgsDataSourceUri &uri )
if ( tableParts.count() < 1 )
return newUri;
const QString tableName = tableParts[0].replace( "layername=", "" );
const QString tableName = tableParts[0].replace( QStringLiteral( "layername=" ), QStringLiteral( "" ) );
newUri.setDataSource( QString(), tableName, QString() );
newUri.setDatabase( databasePath );

View File

@ -24,9 +24,7 @@
#include "qgsdiagramrenderer.h"
#include "qgsvectorlayerjoininfo.h"
#include "qgsproperty.h"
#include <sqlite3.h>
#include "qgsspatialiteutils.h"
#include <QString>
class QgsProject;
@ -385,15 +383,14 @@ class CORE_EXPORT QgsAuxiliaryStorage
static QString extension();
private:
sqlite3 *open( const QString &filename = QString() );
sqlite3 *open( const QgsProject &project );
spatialite_database_unique_ptr open( const QString &filename = QString() );
spatialite_database_unique_ptr open( const QgsProject &project );
void initTmpFileName();
static QString filenameForProject( const QgsProject &project );
static sqlite3 *createDB( const QString &filename );
static sqlite3 *openDB( const QString &filename );
static void close( sqlite3 *handler );
static spatialite_database_unique_ptr createDB( const QString &filename );
static spatialite_database_unique_ptr openDB( const QString &filename );
static bool tableExists( const QString &table, sqlite3 *handler );
static bool createTable( const QString &type, const QString &table, sqlite3 *handler );

View File

@ -28,7 +28,7 @@
#include "qgsvectordataprovider.h"
#include "qgsvectorlayereditbuffer.h"
#include "qgsvectorlayerjoinbuffer.h"
#include "qgsslconnect.h"
#include "qgsspatialiteutils.h"
#include "qgsfeatureiterator.h"
#include "qgslogger.h"
#include "qgsvectorlayerutils.h"
@ -87,8 +87,8 @@ bool QgsOfflineEditing::convertToOfflineProject( const QString &offlineDataPath,
QString dbPath = QDir( offlineDataPath ).absoluteFilePath( offlineDbFile );
if ( createSpatialiteDB( dbPath ) )
{
sqlite3 *db = nullptr;
int rc = QgsSLConnect::sqlite3_open( dbPath.toUtf8().constData(), &db );
spatialite_database_unique_ptr database;
int rc = database.open( dbPath );
if ( rc != SQLITE_OK )
{
showWarning( tr( "Could not open the SpatiaLite database" ) );
@ -96,7 +96,7 @@ bool QgsOfflineEditing::convertToOfflineProject( const QString &offlineDataPath,
else
{
// create logging tables
createLoggingTables( db );
createLoggingTables( database.get() );
emit progressStarted();
@ -140,7 +140,7 @@ bool QgsOfflineEditing::convertToOfflineProject( const QString &offlineDataPath,
if ( vl )
{
QString origLayerId = vl->id();
QgsVectorLayer *newLayer = copyVectorLayer( vl, db, dbPath, onlySelected );
QgsVectorLayer *newLayer = copyVectorLayer( vl, database.get(), dbPath, onlySelected );
if ( newLayer )
{
layerIdMapping.insert( origLayerId, newLayer );
@ -175,8 +175,6 @@ bool QgsOfflineEditing::convertToOfflineProject( const QString &offlineDataPath,
emit progressStopped();
QgsSLConnect::sqlite3_close( db );
// save offline project
QString projectTitle = QgsProject::instance()->title();
if ( projectTitle.isEmpty() )
@ -394,7 +392,6 @@ void QgsOfflineEditing::initializeSpatialMetadata( sqlite3 *sqlite_handle )
bool QgsOfflineEditing::createSpatialiteDB( const QString &offlineDbPath )
{
int ret;
sqlite3 *sqlite_handle = nullptr;
char *errMsg = nullptr;
QFile newDb( offlineDbPath );
if ( newDb.exists() )
@ -412,30 +409,25 @@ bool QgsOfflineEditing::createSpatialiteDB( const QString &offlineDbPath )
// creating/opening the new database
QString dbPath = newDb.fileName();
ret = QgsSLConnect::sqlite3_open_v2( dbPath.toUtf8().constData(), &sqlite_handle, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr );
spatialite_database_unique_ptr database;
ret = database.open_v2( dbPath, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr );
if ( ret )
{
// an error occurred
QString errCause = tr( "Could not create a new database\n" );
errCause += QString::fromUtf8( sqlite3_errmsg( sqlite_handle ) );
sqlite3_close( sqlite_handle );
errCause += database.errorMessage();
showWarning( errCause );
return false;
}
// activating Foreign Key constraints
ret = sqlite3_exec( sqlite_handle, "PRAGMA foreign_keys = 1", nullptr, nullptr, &errMsg );
ret = sqlite3_exec( database.get(), "PRAGMA foreign_keys = 1", nullptr, nullptr, &errMsg );
if ( ret != SQLITE_OK )
{
showWarning( tr( "Unable to activate FOREIGN_KEY constraints" ) );
sqlite3_free( errMsg );
QgsSLConnect::sqlite3_close( sqlite_handle );
return false;
}
initializeSpatialMetadata( sqlite_handle );
// all done: closing the DB connection
QgsSLConnect::sqlite3_close( sqlite_handle );
initializeSpatialMetadata( database.get() );
return true;
}

View File

@ -1,100 +0,0 @@
/***************************************************************************
qgsslconnect.cpp - thin wrapper class to connect to SpatiaLite databases
----------------------
begin : May 2015
copyright : (C) 2015 by Jürgen fischer
email : jef at norbit dot de
***************************************************************************
* *
* 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 "qgsslconnect.h"
#include "qgslogger.h"
#include <sqlite3.h>
#include <spatialite.h>
#if defined(SPATIALITE_HAS_INIT_EX)
QHash<sqlite3 *, void *> QgsSLConnect::sSLconns;
#endif
int QgsSLConnect::sqlite3_open( const char *filename, sqlite3 **ppDb )
{
#if defined(SPATIALITE_HAS_INIT_EX)
void *conn = spatialite_alloc_connection();
#else
spatialite_init( 0 );
#endif
int res = ::sqlite3_open( filename, ppDb );
#if defined(SPATIALITE_HAS_INIT_EX)
if ( res == SQLITE_OK )
{
spatialite_init_ex( *ppDb, conn, 0 );
sSLconns.insert( *ppDb, conn );
}
#endif
return res;
}
int QgsSLConnect::sqlite3_close( sqlite3 *db )
{
int res = ::sqlite3_close( db );
#if defined(SPATIALITE_HAS_INIT_EX)
if ( sSLconns.contains( db ) )
spatialite_cleanup_ex( sSLconns.take( db ) );
#endif
if ( res != SQLITE_OK )
{
QgsDebugMsg( QString( "sqlite3_close() failed: %1" ).arg( res ) );
}
return res;
}
int QgsSLConnect::sqlite3_open_v2( const char *filename, sqlite3 **ppDb, int flags, const char *zVfs )
{
#if defined(SPATIALITE_HAS_INIT_EX)
void *conn = spatialite_alloc_connection();
#else
spatialite_init( 0 );
#endif
int res = ::sqlite3_open_v2( filename, ppDb, flags, zVfs );
#if defined(SPATIALITE_HAS_INIT_EX)
if ( res == SQLITE_OK )
{
spatialite_init_ex( *ppDb, conn, 0 );
sSLconns.insert( *ppDb, conn );
}
#endif
return res;
}
int QgsSLConnect::sqlite3_close_v2( sqlite3 *db )
{
int res = ::sqlite3_close( db );
#if defined(SPATIALITE_HAS_INIT_EX)
if ( sSLconns.contains( db ) )
spatialite_cleanup_ex( sSLconns.take( db ) );
#endif
if ( res != SQLITE_OK )
{
QgsDebugMsg( QString( "sqlite3_close() failed: %1" ).arg( res ) );
}
return res;
}

View File

@ -1,48 +0,0 @@
/***************************************************************************
qgsslconnect.h - thin wrapper class to connect to SpatiaLite databases
----------------------
begin : May 2015
copyright : (C) 2015 by Jürgen fischer
email : jef at norbit dot de
***************************************************************************
* *
* 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 QGSSLCONNECT_H
#define QGSSLCONNECT_H
#define SIP_NO_FILE
#include <QHash>
#include "qgis_core.h"
struct sqlite3;
/**
* \ingroup core
* \class QgsSLConnect
* \note not available in Python bindings
*/
class CORE_EXPORT QgsSLConnect
{
public:
static int sqlite3_open( const char *filename, sqlite3 **ppDb );
static int sqlite3_close( sqlite3 * );
static int sqlite3_open_v2( const char *filename, sqlite3 **ppDb, int flags, const char *zVfs );
static int sqlite3_close_v2( sqlite3 * );
#if defined(SPATIALITE_HAS_INIT_EX)
private:
static QHash<sqlite3 *, void *> sSLconns;
#endif
};
#endif

View File

@ -0,0 +1,88 @@
/***************************************************************************
qgsspatialiteutils.cpp
-------------------
begin : Nov, 2017
copyright : (C) 2017 by Matthias Kuhn
email : matthias@opengis.ch
***************************************************************************/
/***************************************************************************
* *
* 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 "qgsspatialiteutils.h"
#include <sqlite3.h>
#include <spatialite.h>
int spatialite_database_unique_ptr::open( const QString &path )
{
#if defined(SPATIALITE_HAS_INIT_EX)
void *conn = spatialite_alloc_connection();
#else
spatialite_init( 0 );
#endif
sqlite3 *database = nullptr;
int result = sqlite3_open( path.toUtf8(), &database );
reset( database );
#if defined(SPATIALITE_HAS_INIT_EX)
if ( result == SQLITE_OK )
spatialite_init_ex( database, conn, 0 );
#endif
return result;
}
int spatialite_database_unique_ptr::open_v2( const QString &path, int flags, const char *zVfs )
{
#if defined(SPATIALITE_HAS_INIT_EX)
void *conn = spatialite_alloc_connection();
#else
spatialite_init( 0 );
#endif
sqlite3 *database = nullptr;
int result = sqlite3_open_v2( path.toUtf8(), &database, flags, zVfs );
reset( database );
#if defined(SPATIALITE_HAS_INIT_EX)
if ( result == SQLITE_OK )
spatialite_init_ex( database, conn, 0 );
#endif
return result;
}
QString spatialite_database_unique_ptr::errorMessage() const
{
return QString( sqlite3_errmsg( get() ) );
}
sqlite3_statement_unique_ptr spatialite_database_unique_ptr::prepare( const QString &sql, int &resultCode )
{
sqlite3_stmt *preparedStatement = nullptr;
const char *tail = nullptr;
resultCode = sqlite3_prepare( get(), sql.toUtf8(), sql.toUtf8().length(), &preparedStatement, &tail );
sqlite3_statement_unique_ptr s;
s.reset( preparedStatement );
return s;
}
void QgsSpatialiteCloser::operator()( sqlite3 *database )
{
sqlite3_close( database );
#if defined(SPATIALITE_HAS_INIT_EX)
spatialite_cleanup_ex( database );
#endif
}

View File

@ -0,0 +1,73 @@
/***************************************************************************
qgsspatialiteutils.h
-------------------
begin : Nov, 2017
copyright : (C) 2017 by Matthias Kuhn
email : matthias@opengis.ch
***************************************************************************/
/***************************************************************************
* *
* 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 QGSSPATIALITEUTILS_H
#define QGSSPATIALITEUTILS_H
#define SIP_NO_FILE
#include "qgis_core.h"
#include "qgssqliteutils.h"
/**
* Closes a spatialite database.
*/
struct CORE_EXPORT QgsSpatialiteCloser
{
/**
* Closes an spatialite \a database.
*/
void operator()( sqlite3 *database );
};
/**
* Unique pointer for spatialite databases, which automatically closes
* the database when the pointer goes out of scope or is reset.
*/
class CORE_EXPORT spatialite_database_unique_ptr : public std::unique_ptr< sqlite3, QgsSpatialiteCloser>
{
public:
/**
* Opens the database at the specified file \a path.
*
* Returns the sqlite error code, or SQLITE_OK if open was successful.
*/
int open( const QString &path );
/**
* Opens the database at the specified file \a path.
*
* Returns the sqlite error code, or SQLITE_OK if open was successful.
*/
int open_v2( const QString &path, int flags, const char *zVfs );
/**
* Returns the most recent error message encountered by the database.
*/
QString errorMessage() const;
/**
* Prepares a \a sql statement, returning the result. The \a resultCode
* argument will be filled with the sqlite3 result code.
*/
sqlite3_statement_unique_ptr prepare( const QString &sql, int &resultCode );
};
#endif // QGSSPATIALITEUTILS_H

View File

@ -0,0 +1,71 @@
/***************************************************************************
qgssqliteutils.cpp
-------------------
begin : Nov, 2017
copyright : (C) 2017 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* 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 "qgssqliteutils.h"
#include <sqlite3.h>
void QgsSqlite3Closer::operator()( sqlite3 *database )
{
sqlite3_close( database );
}
void QgsSqlite3StatementFinalizer::operator()( sqlite3_stmt *statement )
{
sqlite3_finalize( statement );
}
int sqlite3_statement_unique_ptr::step()
{
return sqlite3_step( get() );
}
QString sqlite3_statement_unique_ptr::columnAsText( int column )
{
return QString::fromUtf8( ( char * ) sqlite3_column_text( get(), column ) );
}
int sqlite3_database_unique_ptr::open( const QString &path )
{
sqlite3 *database = nullptr;
int result = sqlite3_open( path.toUtf8(), &database );
reset( database );
return result;
}
int sqlite3_database_unique_ptr::open_v2( const QString &path, int flags, const char *zVfs )
{
sqlite3 *database = nullptr;
int result = sqlite3_open_v2( path.toUtf8(), &database, flags, zVfs );
reset( database );
return result;
}
QString sqlite3_database_unique_ptr::errorMessage() const
{
return QString( sqlite3_errmsg( get() ) );
}
sqlite3_statement_unique_ptr sqlite3_database_unique_ptr::prepare( const QString &sql, int &resultCode )
{
sqlite3_stmt *preparedStatement = nullptr;
const char *tail = nullptr;
resultCode = sqlite3_prepare( get(), sql.toUtf8(), sql.toUtf8().length(), &preparedStatement, &tail );
sqlite3_statement_unique_ptr s;
s.reset( preparedStatement );
return s;
}

110
src/core/qgssqliteutils.h Normal file
View File

@ -0,0 +1,110 @@
/***************************************************************************
qgssqliteutils.h
-------------------
begin : Nov, 2017
copyright : (C) 2017 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* 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 QGSSQLITEUTILS_H
#define QGSSQLITEUTILS_H
#define SIP_NO_FILE
#include "qgis_core.h"
#include <memory>
#include <QString>
struct sqlite3;
struct sqlite3_stmt;
/**
* Closes a sqlite3 database.
*/
struct CORE_EXPORT QgsSqlite3Closer
{
/**
* Closes an sqlite \a database.
*/
void operator()( sqlite3 *database );
};
/**
* Finalizes an sqlite3 statement.
*/
struct CORE_EXPORT QgsSqlite3StatementFinalizer
{
/**
* Finalizes an sqlite3 \a statement.
*/
void operator()( sqlite3_stmt *statement );
};
/**
* Unique pointer for sqlite3 prepared statements, which automatically finalizes
* the statement when the pointer goes out of scope or is reset.
*/
class CORE_EXPORT sqlite3_statement_unique_ptr : public std::unique_ptr< sqlite3_stmt, QgsSqlite3StatementFinalizer>
{
public:
/**
* Steps to the next record in the statement, returning the sqlite3 result code.
*/
int step();
/**
* Returns the column value from the current statement row as a string.
*/
QString columnAsText( int column );
};
/**
* Unique pointer for sqlite3 databases, which automatically closes
* the database when the pointer goes out of scope or is reset.
*/
class CORE_EXPORT sqlite3_database_unique_ptr : public std::unique_ptr< sqlite3, QgsSqlite3Closer>
{
public:
/**
* Opens the database at the specified file \a path.
*
* Returns the sqlite error code, or SQLITE_OK if open was successful.
*/
int open( const QString &path );
/**
* Opens the database at the specified file \a path.
*
* Returns the sqlite error code, or SQLITE_OK if open was successful.
*/
int open_v2( const QString &path, int flags, const char *zVfs );
/**
* Returns the most recent error message encountered by the database.
*/
QString errorMessage() const;
/**
* Prepares a \a sql statement, returning the result. The \a resultCode
* argument will be filled with the sqlite3 result code.
*/
sqlite3_statement_unique_ptr prepare( const QString &sql, int &resultCode );
};
#endif // QGSSQLITEUTILS_H

View File

@ -16,6 +16,7 @@
#include "qgsslconnect.h"
#include "qgssettings.h"
#include "qgslogger.h"
#include "qgsspatialiteutils.h"
#include <QFileInfo>
#include <cstdlib> // atoi
@ -65,11 +66,12 @@ QgsSpatiaLiteConnection::Error QgsSpatiaLiteConnection::fetchTables( bool loadGe
if ( !fi.exists() )
return NotExists;
sqlite3 *handle = openSpatiaLiteDb( fi.canonicalFilePath() );
if ( !handle )
spatialite_database_unique_ptr database;
int ret = database.open( fi.canonicalFilePath() );
if ( ret )
return FailedToOpen;
int ret = checkHasMetadataTables( handle );
ret = checkHasMetadataTables( handle );
if ( !mErrorMsg.isNull() || ret == LayoutUnknown )
{
// unexpected error; invalid SpatiaLite DB
@ -92,15 +94,14 @@ QgsSpatiaLiteConnection::Error QgsSpatiaLiteConnection::fetchTables( bool loadGe
#ifdef SPATIALITE_VERSION_GE_4_0_0
// only if libspatialite version is >= 4.0.0
// using v.4.0 Abstract Interface
if ( !getTableInfoAbstractInterface( handle, loadGeometrylessTables ) )
if ( !getTableInfoAbstractInterface( database.get(), loadGeometrylessTables ) )
#else
// obsolete library: still using the traditional approach
if ( !getTableInfo( handle, loadGeometrylessTables ) )
if ( !getTableInfo( database.get(), loadGeometrylessTables ) )
#endif
{
return FailedToGetTables;
}
closeSpatiaLiteDb( handle );
return NoError;
}
@ -112,13 +113,12 @@ bool QgsSpatiaLiteConnection::updateStatistics()
if ( !fi.exists() )
return false;
sqlite3 *handle = openSpatiaLiteDb( fi.canonicalFilePath() );
if ( !handle )
spatialite_database_unique_ptr database;
int ret = database.open( fi.canonicalFilePath() );
if ( ret )
return false;
bool ret = update_layer_statistics( handle, nullptr, nullptr );
closeSpatiaLiteDb( handle );
ret = update_layer_statistics( database.get(), nullptr, nullptr );
return ret;
#else
@ -126,27 +126,6 @@ bool QgsSpatiaLiteConnection::updateStatistics()
#endif
}
sqlite3 *QgsSpatiaLiteConnection::openSpatiaLiteDb( const QString &path )
{
sqlite3 *handle = nullptr;
int ret;
// trying to open the SQLite DB
ret = QgsSLConnect::sqlite3_open_v2( path.toUtf8().constData(), &handle, SQLITE_OPEN_READWRITE, nullptr );
if ( ret )
{
// failure
mErrorMsg = sqlite3_errmsg( handle );
return nullptr;
}
return handle;
}
void QgsSpatiaLiteConnection::closeSpatiaLiteDb( sqlite3 *handle )
{
if ( handle )
QgsSLConnect::sqlite3_close( handle );
}
int QgsSpatiaLiteConnection::checkHasMetadataTables( sqlite3 *handle )
{
bool gcSpatiaLite = false;

View File

@ -30,6 +30,7 @@ email : a.furieri@lqt.it
#include "qgsspatialiteconnpool.h"
#include "qgsspatialitefeatureiterator.h"
#include "qgsfeedback.h"
#include "qgsspatialiteutils.h"
#include "qgsjsonutils.h"
#include "qgsvectorlayer.h"
@ -5352,30 +5353,25 @@ QGISEXTERN bool createDb( const QString &dbPath, QString &errCause )
QDir().mkpath( path.absolutePath() );
// creating/opening the new database
sqlite3 *sqlite_handle = nullptr;
int ret = QgsSLConnect::sqlite3_open_v2( dbPath.toUtf8().constData(), &sqlite_handle, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr );
spatialite_database_unique_ptr database;
int ret = database.open_v2( dbPath, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr );
if ( ret )
{
// an error occurred
errCause = QObject::tr( "Could not create a new database\n" );
errCause += QString::fromUtf8( sqlite3_errmsg( sqlite_handle ) );
QgsSLConnect::sqlite3_close( sqlite_handle );
errCause += database.errorMessage();
return false;
}
// activating Foreign Key constraints
char *errMsg = nullptr;
ret = sqlite3_exec( sqlite_handle, "PRAGMA foreign_keys = 1", nullptr, nullptr, &errMsg );
ret = sqlite3_exec( database.get(), "PRAGMA foreign_keys = 1", nullptr, nullptr, &errMsg );
if ( ret != SQLITE_OK )
{
errCause = QObject::tr( "Unable to activate FOREIGN_KEY constraints [%1]" ).arg( errMsg );
sqlite3_free( errMsg );
QgsSLConnect::sqlite3_close( sqlite_handle );
return false;
}
bool init_res = ::initializeSpatialMetadata( sqlite_handle, errCause );
// all done: closing the DB connection
QgsSLConnect::sqlite3_close( sqlite_handle );
bool init_res = ::initializeSpatialMetadata( database.get(), errCause );
return init_res;
}