mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-04 00:04:03 -04:00
766 lines
23 KiB
C++
766 lines
23 KiB
C++
/***************************************************************************
|
|
qgsspatialiteconnection.cpp
|
|
---------------------
|
|
begin : October 2011
|
|
copyright : (C) 2011 by Martin Dobias
|
|
email : wonder dot sk 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 "qgsspatialiteconnection.h"
|
|
#include "moc_qgsspatialiteconnection.cpp"
|
|
#include "qgssettings.h"
|
|
#include "qgslogger.h"
|
|
#include "qgsspatialiteutils.h"
|
|
#include "qgssqliteutils.h"
|
|
#include "qgsvariantutils.h"
|
|
|
|
#include <QFileInfo>
|
|
#include <QRegularExpression>
|
|
#include <cstdlib> // atoi
|
|
|
|
#ifdef _MSC_VER
|
|
#define strcasecmp( a, b ) stricmp( a, b )
|
|
#endif
|
|
|
|
const QString QgsSpatiaLiteConnection::SPATIALITE_ARRAY_PREFIX = QStringLiteral( "json" );
|
|
const QString QgsSpatiaLiteConnection::SPATIALITE_ARRAY_SUFFIX = QStringLiteral( "list" );
|
|
|
|
QStringList QgsSpatiaLiteConnection::connectionList()
|
|
{
|
|
QgsSettings settings;
|
|
settings.beginGroup( QStringLiteral( "SpatiaLite/connections" ) );
|
|
return settings.childGroups();
|
|
}
|
|
|
|
QString QgsSpatiaLiteConnection::connectionPath( const QString &name )
|
|
{
|
|
const QgsSettings settings;
|
|
return settings.value( "/SpatiaLite/connections/" + name + "/sqlitepath" ).toString();
|
|
}
|
|
|
|
// -------
|
|
|
|
QgsSpatiaLiteConnection::QgsSpatiaLiteConnection( const QString &name )
|
|
{
|
|
// "name" can be either a saved connection or a path to database
|
|
|
|
const QgsSettings settings;
|
|
mPath = settings.value( QStringLiteral( "SpatiaLite/connections/%1/sqlitepath" ).arg( name ) ).toString();
|
|
if ( mPath.isNull() )
|
|
mPath = name; // not found in settings - probably it's a path
|
|
}
|
|
|
|
QgsSpatiaLiteConnection::Error QgsSpatiaLiteConnection::fetchTables( bool loadGeometrylessTables )
|
|
{
|
|
mErrorMsg = QString();
|
|
|
|
const QFileInfo fi( mPath );
|
|
if ( !fi.exists() )
|
|
return NotExists;
|
|
|
|
spatialite_database_unique_ptr database;
|
|
int ret = database.open( fi.canonicalFilePath() );
|
|
if ( ret )
|
|
return FailedToOpen;
|
|
|
|
ret = checkHasMetadataTables( database.get() );
|
|
if ( !mErrorMsg.isNull() || ret == LayoutUnknown )
|
|
{
|
|
// unexpected error; invalid SpatiaLite DB
|
|
return FailedToCheckMetadata;
|
|
}
|
|
|
|
if ( !getTableInfoAbstractInterface( database.get(), loadGeometrylessTables ) )
|
|
{
|
|
return FailedToGetTables;
|
|
}
|
|
|
|
return NoError;
|
|
}
|
|
|
|
bool QgsSpatiaLiteConnection::updateStatistics()
|
|
{
|
|
const QFileInfo fi( mPath );
|
|
if ( !fi.exists() )
|
|
return false;
|
|
|
|
spatialite_database_unique_ptr database;
|
|
int ret = database.open( fi.canonicalFilePath() );
|
|
if ( ret )
|
|
return false;
|
|
|
|
ret = update_layer_statistics( database.get(), nullptr, nullptr );
|
|
|
|
return ret;
|
|
}
|
|
|
|
QList<QgsVectorDataProvider::NativeType> QgsSpatiaLiteConnection::nativeTypes()
|
|
{
|
|
return QList<QgsVectorDataProvider::NativeType>()
|
|
<< QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QByteArray ), QStringLiteral( "BLOB" ), QMetaType::Type::QByteArray )
|
|
<< QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QString ), QStringLiteral( "TEXT" ), QMetaType::Type::QString )
|
|
<< QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::Double ), QStringLiteral( "FLOAT" ), QMetaType::Type::Double )
|
|
<< QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::Int ), QStringLiteral( "INTEGER" ), QMetaType::Type::LongLong )
|
|
<< QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QDate ), QStringLiteral( "DATE" ), QMetaType::Type::QDate )
|
|
<< QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QDateTime ), QStringLiteral( "TIMESTAMP" ), QMetaType::Type::QDateTime )
|
|
|
|
<< QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QStringList ), SPATIALITE_ARRAY_PREFIX.toUpper() + "TEXT" + SPATIALITE_ARRAY_SUFFIX.toUpper(), QMetaType::Type::QStringList, 0, 0, 0, 0, QMetaType::Type::QString )
|
|
<< QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QVariantList, QMetaType::Type::Double ), SPATIALITE_ARRAY_PREFIX.toUpper() + "REAL" + SPATIALITE_ARRAY_SUFFIX.toUpper(), QMetaType::Type::QVariantList, 0, 0, 0, 0, QMetaType::Type::Double )
|
|
<< QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QVariantList, QMetaType::Type::Int ), SPATIALITE_ARRAY_PREFIX.toUpper() + "INTEGER" + SPATIALITE_ARRAY_SUFFIX.toUpper(), QMetaType::Type::QVariantList, 0, 0, 0, 0, QMetaType::Type::LongLong );
|
|
}
|
|
|
|
int QgsSpatiaLiteConnection::checkHasMetadataTables( sqlite3 *handle )
|
|
{
|
|
bool gcSpatiaLite = false;
|
|
bool rsSpatiaLite = false;
|
|
bool gcSpatiaLite4 = false;
|
|
bool rsSpatiaLite4 = false;
|
|
bool tableName = false;
|
|
bool geomColumn = false;
|
|
bool coordDims = false;
|
|
bool gcSrid = false;
|
|
bool type = false;
|
|
bool geometry_type = false;
|
|
bool spatialIndex = false;
|
|
bool srsSrid = false;
|
|
bool authName = false;
|
|
bool authSrid = false;
|
|
bool refSysName = false;
|
|
bool proj4text = false;
|
|
bool srtext = false;
|
|
int ret;
|
|
const char *name = nullptr;
|
|
int i;
|
|
char **results = nullptr;
|
|
int rows;
|
|
int columns;
|
|
char *errMsg = nullptr;
|
|
|
|
// checking if table GEOMETRY_COLUMNS exists and has the expected layout
|
|
ret = sqlite3_get_table( handle, "PRAGMA table_info(geometry_columns)", &results, &rows, &columns, &errMsg );
|
|
if ( ret != SQLITE_OK )
|
|
{
|
|
mErrorMsg = tr( "table info on %1 failed" ).arg( QLatin1String( "geometry_columns" ) );
|
|
goto error;
|
|
}
|
|
if ( rows < 1 )
|
|
;
|
|
else
|
|
{
|
|
for ( i = 1; i <= rows; i++ )
|
|
{
|
|
name = results[( i * columns ) + 1];
|
|
if ( strcasecmp( name, "f_table_name" ) == 0 )
|
|
tableName = true;
|
|
if ( strcasecmp( name, "f_geometry_column" ) == 0 )
|
|
geomColumn = true;
|
|
if ( strcasecmp( name, "coord_dimension" ) == 0 )
|
|
coordDims = true;
|
|
if ( strcasecmp( name, "srid" ) == 0 )
|
|
gcSrid = true;
|
|
if ( strcasecmp( name, "type" ) == 0 )
|
|
type = true;
|
|
if ( strcasecmp( name, "geometry_type" ) == 0 )
|
|
geometry_type = true;
|
|
if ( strcasecmp( name, "spatial_index_enabled" ) == 0 )
|
|
spatialIndex = true;
|
|
}
|
|
}
|
|
sqlite3_free_table( results );
|
|
if ( tableName && geomColumn && type && coordDims && gcSrid && spatialIndex )
|
|
gcSpatiaLite = true;
|
|
if ( tableName && geomColumn && geometry_type && coordDims && gcSrid && spatialIndex )
|
|
gcSpatiaLite4 = true;
|
|
|
|
// checking if table SPATIAL_REF_SYS exists and has the expected layout
|
|
ret = sqlite3_get_table( handle, "PRAGMA table_info(spatial_ref_sys)", &results, &rows, &columns, &errMsg );
|
|
if ( ret != SQLITE_OK )
|
|
{
|
|
mErrorMsg = tr( "table info on %1 failed" ).arg( QLatin1String( "spatial_ref_sys" ) );
|
|
goto error;
|
|
}
|
|
if ( rows < 1 )
|
|
;
|
|
else
|
|
{
|
|
for ( i = 1; i <= rows; i++ )
|
|
{
|
|
name = results[( i * columns ) + 1];
|
|
if ( strcasecmp( name, "srid" ) == 0 )
|
|
srsSrid = true;
|
|
if ( strcasecmp( name, "auth_name" ) == 0 )
|
|
authName = true;
|
|
if ( strcasecmp( name, "auth_srid" ) == 0 )
|
|
authSrid = true;
|
|
if ( strcasecmp( name, "ref_sys_name" ) == 0 )
|
|
refSysName = true;
|
|
if ( strcasecmp( name, "proj4text" ) == 0 )
|
|
proj4text = true;
|
|
if ( strcasecmp( name, "srtext" ) == 0 )
|
|
srtext = true;
|
|
}
|
|
}
|
|
sqlite3_free_table( results );
|
|
if ( srsSrid && authName && authSrid && refSysName && proj4text )
|
|
rsSpatiaLite = true;
|
|
if ( srsSrid && authName && authSrid && refSysName && proj4text && srtext )
|
|
rsSpatiaLite4 = true;
|
|
|
|
// OK, this one seems to be a valid SpatiaLite DB
|
|
if ( gcSpatiaLite4 && rsSpatiaLite4 )
|
|
return LayoutCurrent;
|
|
if ( gcSpatiaLite && rsSpatiaLite )
|
|
return LayoutLegacy;
|
|
|
|
// this seems to be a valid SQLite DB, but not a SpatiaLite's one
|
|
return LayoutUnknown;
|
|
|
|
error:
|
|
// unexpected IO error
|
|
if ( errMsg )
|
|
{
|
|
mErrorMsg += '\n';
|
|
mErrorMsg += errMsg;
|
|
sqlite3_free( errMsg );
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool QgsSpatiaLiteConnection::getTableInfoAbstractInterface( sqlite3 *handle, bool loadGeometrylessTables )
|
|
{
|
|
int ret;
|
|
int i;
|
|
char **results = nullptr;
|
|
int rows;
|
|
int columns;
|
|
char *errMsg = nullptr;
|
|
QString sql;
|
|
gaiaVectorLayersListPtr list;
|
|
|
|
const char *version = spatialite_version();
|
|
if ( isdigit( *version ) && *version >= '4' )
|
|
; // OK, linked against libspatialite v.4.0 (or any subsequent)
|
|
else
|
|
{
|
|
mErrorMsg = tr( "obsolete libspatialite: AbstractInterface is unsupported" );
|
|
return false;
|
|
}
|
|
|
|
// List of system tables not to be shown if geometryless tables are requested
|
|
QStringList ignoreTableNames = QgsSqliteUtils::systemTables();
|
|
|
|
// attempting to load the VectorLayersList
|
|
list = gaiaGetVectorLayersList( handle, nullptr, nullptr, GAIA_VECTORS_LIST_FAST );
|
|
if ( list )
|
|
{
|
|
gaiaVectorLayerPtr lyr = list->First;
|
|
while ( lyr )
|
|
{
|
|
// populating the QGIS own Layers List
|
|
if ( lyr->AuthInfos )
|
|
{
|
|
if ( lyr->AuthInfos->IsHidden )
|
|
{
|
|
// skipping any Hidden layer
|
|
lyr = lyr->Next;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
const QString tableName = QString::fromUtf8( lyr->TableName );
|
|
ignoreTableNames << tableName;
|
|
const QString column = QString::fromUtf8( lyr->GeometryName );
|
|
ignoreTableNames << QStringLiteral( "idx_%1_%2" ).arg( tableName, column )
|
|
<< QStringLiteral( "idx_%1_%2_node" ).arg( tableName, column )
|
|
<< QStringLiteral( "idx_%1_%2_parent" ).arg( tableName, column )
|
|
<< QStringLiteral( "idx_%1_%2_rowid" ).arg( tableName, column );
|
|
QString type = tr( "UNKNOWN" );
|
|
switch ( lyr->GeometryType )
|
|
{
|
|
case GAIA_VECTOR_GEOMETRY:
|
|
type = tr( "GEOMETRY" );
|
|
break;
|
|
case GAIA_VECTOR_POINT:
|
|
type = tr( "POINT" );
|
|
break;
|
|
case GAIA_VECTOR_LINESTRING:
|
|
type = tr( "LINESTRING" );
|
|
break;
|
|
case GAIA_VECTOR_POLYGON:
|
|
type = tr( "POLYGON" );
|
|
break;
|
|
case GAIA_VECTOR_MULTIPOINT:
|
|
type = tr( "MULTIPOINT" );
|
|
break;
|
|
case GAIA_VECTOR_MULTILINESTRING:
|
|
type = tr( "MULTILINESTRING" );
|
|
break;
|
|
case GAIA_VECTOR_MULTIPOLYGON:
|
|
type = tr( "MULTIPOLYGON" );
|
|
break;
|
|
case GAIA_VECTOR_GEOMETRYCOLLECTION:
|
|
type = tr( "GEOMETRYCOLLECTION" );
|
|
break;
|
|
}
|
|
mTables.append( TableEntry( tableName, column, type ) );
|
|
|
|
lyr = lyr->Next;
|
|
}
|
|
gaiaFreeVectorLayersList( list );
|
|
}
|
|
|
|
if ( loadGeometrylessTables )
|
|
{
|
|
// get all tables
|
|
sql = "SELECT name "
|
|
"FROM sqlite_master "
|
|
"WHERE type in ('table', 'view')";
|
|
ret = sqlite3_get_table( handle, sql.toUtf8(), &results, &rows, &columns, &errMsg );
|
|
if ( ret != SQLITE_OK )
|
|
goto error;
|
|
if ( rows < 1 )
|
|
;
|
|
else
|
|
{
|
|
for ( i = 1; i <= rows; i++ )
|
|
{
|
|
const QString tableName = QString::fromUtf8( results[( i * columns ) + 0] );
|
|
if ( !ignoreTableNames.contains( tableName, Qt::CaseInsensitive ) )
|
|
mTables.append( TableEntry( tableName, QString(), QStringLiteral( "qgis_table" ) ) );
|
|
}
|
|
}
|
|
sqlite3_free_table( results );
|
|
}
|
|
|
|
return true;
|
|
|
|
error:
|
|
// unexpected IO error
|
|
mErrorMsg = tr( "unknown error cause" );
|
|
if ( errMsg )
|
|
{
|
|
mErrorMsg = errMsg;
|
|
sqlite3_free( errMsg );
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool QgsSpatiaLiteConnection::getTableInfo( sqlite3 *handle, bool loadGeometrylessTables )
|
|
{
|
|
int ret;
|
|
int i;
|
|
char **results = nullptr;
|
|
int rows;
|
|
int columns;
|
|
char *errMsg = nullptr;
|
|
QString sql;
|
|
|
|
// the following query return the tables containing a Geometry column
|
|
sql = "SELECT f_table_name, f_geometry_column, type "
|
|
"FROM geometry_columns";
|
|
ret = sqlite3_get_table( handle, sql.toUtf8(), &results, &rows, &columns, &errMsg );
|
|
if ( ret != SQLITE_OK )
|
|
goto error;
|
|
for ( i = 1; i <= rows; i++ )
|
|
{
|
|
if ( isRasterlite1Datasource( handle, results[( i * columns ) + 0] ) )
|
|
continue;
|
|
const QString tableName = QString::fromUtf8( results[( i * columns ) + 0] );
|
|
const QString column = QString::fromUtf8( results[( i * columns ) + 1] );
|
|
const QString type = results[( i * columns ) + 2];
|
|
if ( isDeclaredHidden( handle, tableName, column ) )
|
|
continue;
|
|
|
|
mTables.append( TableEntry( tableName, column, type ) );
|
|
}
|
|
sqlite3_free_table( results );
|
|
|
|
if ( checkViewsGeometryColumns( handle ) )
|
|
{
|
|
// the following query return the views supporting a Geometry column
|
|
sql = "SELECT view_name, view_geometry, type "
|
|
"FROM views_geometry_columns "
|
|
"JOIN geometry_columns USING (f_table_name, f_geometry_column)";
|
|
ret = sqlite3_get_table( handle, sql.toUtf8(), &results, &rows, &columns, &errMsg );
|
|
if ( ret != SQLITE_OK )
|
|
goto error;
|
|
for ( i = 1; i <= rows; i++ )
|
|
{
|
|
const QString tableName = QString::fromUtf8( results[( i * columns ) + 0] );
|
|
const QString column = QString::fromUtf8( results[( i * columns ) + 1] );
|
|
const QString type = results[( i * columns ) + 2];
|
|
if ( isDeclaredHidden( handle, tableName, column ) )
|
|
continue;
|
|
|
|
mTables.append( TableEntry( tableName, column, type ) );
|
|
}
|
|
sqlite3_free_table( results );
|
|
}
|
|
|
|
if ( checkVirtsGeometryColumns( handle ) )
|
|
{
|
|
// the following query return the VirtualShapefiles
|
|
sql = "SELECT virt_name, virt_geometry, type "
|
|
"FROM virts_geometry_columns";
|
|
ret = sqlite3_get_table( handle, sql.toUtf8(), &results, &rows, &columns, &errMsg );
|
|
if ( ret != SQLITE_OK )
|
|
goto error;
|
|
for ( i = 1; i <= rows; i++ )
|
|
{
|
|
const QString tableName = QString::fromUtf8( results[( i * columns ) + 0] );
|
|
const QString column = QString::fromUtf8( results[( i * columns ) + 1] );
|
|
const QString type = results[( i * columns ) + 2];
|
|
if ( isDeclaredHidden( handle, tableName, column ) )
|
|
continue;
|
|
|
|
mTables.append( TableEntry( tableName, column, type ) );
|
|
}
|
|
sqlite3_free_table( results );
|
|
}
|
|
|
|
if ( loadGeometrylessTables )
|
|
{
|
|
// get all tables
|
|
sql = "SELECT name "
|
|
"FROM sqlite_master "
|
|
"WHERE type in ('table', 'view')";
|
|
ret = sqlite3_get_table( handle, sql.toUtf8(), &results, &rows, &columns, &errMsg );
|
|
if ( ret != SQLITE_OK )
|
|
goto error;
|
|
for ( i = 1; i <= rows; i++ )
|
|
{
|
|
const QString tableName = QString::fromUtf8( results[( i * columns ) + 0] );
|
|
mTables.append( TableEntry( tableName, QString(), QStringLiteral( "qgis_table" ) ) );
|
|
}
|
|
sqlite3_free_table( results );
|
|
}
|
|
|
|
return true;
|
|
|
|
error:
|
|
// unexpected IO error
|
|
mErrorMsg = tr( "unknown error cause" );
|
|
if ( errMsg )
|
|
{
|
|
mErrorMsg = errMsg;
|
|
sqlite3_free( errMsg );
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool QgsSpatiaLiteConnection::checkGeometryColumnsAuth( sqlite3 *handle )
|
|
{
|
|
int ret;
|
|
int i;
|
|
char **results = nullptr;
|
|
int rows;
|
|
int columns;
|
|
bool exists = false;
|
|
|
|
// checking the metadata tables
|
|
const QString sql = QStringLiteral( "SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'geometry_columns_auth'" );
|
|
|
|
ret = sqlite3_get_table( handle, sql.toUtf8().constData(), &results, &rows, &columns, nullptr );
|
|
if ( ret != SQLITE_OK )
|
|
return false;
|
|
if ( rows < 1 )
|
|
;
|
|
else
|
|
{
|
|
for ( i = 1; i <= rows; i++ )
|
|
{
|
|
if ( results[( i * columns ) + 0] )
|
|
{
|
|
const char *name = results[( i * columns ) + 0];
|
|
if ( name )
|
|
exists = true;
|
|
}
|
|
}
|
|
}
|
|
sqlite3_free_table( results );
|
|
return exists;
|
|
}
|
|
|
|
|
|
bool QgsSpatiaLiteConnection::checkViewsGeometryColumns( sqlite3 *handle )
|
|
{
|
|
int ret;
|
|
int i;
|
|
char **results = nullptr;
|
|
int rows;
|
|
int columns;
|
|
bool exists = false;
|
|
|
|
// checking the metadata tables
|
|
const QString sql = QStringLiteral( "SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'views_geometry_columns'" );
|
|
|
|
ret = sqlite3_get_table( handle, sql.toUtf8().constData(), &results, &rows, &columns, nullptr );
|
|
if ( ret != SQLITE_OK )
|
|
return false;
|
|
if ( rows < 1 )
|
|
;
|
|
else
|
|
{
|
|
for ( i = 1; i <= rows; i++ )
|
|
{
|
|
if ( results[( i * columns ) + 0] )
|
|
{
|
|
const char *name = results[( i * columns ) + 0];
|
|
if ( name )
|
|
exists = true;
|
|
}
|
|
}
|
|
}
|
|
sqlite3_free_table( results );
|
|
return exists;
|
|
}
|
|
|
|
bool QgsSpatiaLiteConnection::checkVirtsGeometryColumns( sqlite3 *handle )
|
|
{
|
|
int ret;
|
|
int i;
|
|
char **results = nullptr;
|
|
int rows;
|
|
int columns;
|
|
bool exists = false;
|
|
|
|
// checking the metadata tables
|
|
const QString sql = QStringLiteral( "SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'virts_geometry_columns'" );
|
|
|
|
ret = sqlite3_get_table( handle, sql.toUtf8().constData(), &results, &rows, &columns, nullptr );
|
|
if ( ret != SQLITE_OK )
|
|
return false;
|
|
if ( rows < 1 )
|
|
;
|
|
else
|
|
{
|
|
for ( i = 1; i <= rows; i++ )
|
|
{
|
|
if ( results[( i * columns ) + 0] )
|
|
{
|
|
const char *name = results[( i * columns ) + 0];
|
|
if ( name )
|
|
exists = true;
|
|
}
|
|
}
|
|
}
|
|
sqlite3_free_table( results );
|
|
return exists;
|
|
}
|
|
|
|
bool QgsSpatiaLiteConnection::isRasterlite1Datasource( sqlite3 *handle, const char *table )
|
|
{
|
|
// testing for RasterLite-1 datasources
|
|
int ret;
|
|
int i;
|
|
char **results = nullptr;
|
|
int rows;
|
|
int columns;
|
|
bool exists = false;
|
|
|
|
QString tableRaster = QString::fromUtf8( table );
|
|
if ( !tableRaster.endsWith( QLatin1String( "_metadata" ) ) )
|
|
return false;
|
|
|
|
// OK, possible candidate → replace suffix
|
|
tableRaster.chop( 9 );
|
|
tableRaster += QLatin1String( "_rasters" );
|
|
|
|
// checking if the related "_RASTERS" table exists
|
|
QString sqlStr = QStringLiteral(
|
|
"SELECT name FROM sqlite_master WHERE type = 'table' AND name = '%1'"
|
|
)
|
|
.arg( tableRaster.replace( '\'', QLatin1String( "''" ) ) );
|
|
|
|
ret = sqlite3_get_table( handle, sqlStr.toUtf8().constData(), &results, &rows, &columns, nullptr );
|
|
if ( ret != SQLITE_OK )
|
|
return false;
|
|
|
|
for ( i = 1; i <= rows; i++ )
|
|
{
|
|
const char *name = results[( i * columns ) + 0];
|
|
if ( name )
|
|
{
|
|
exists = true;
|
|
break;
|
|
}
|
|
}
|
|
sqlite3_free_table( results );
|
|
return exists;
|
|
}
|
|
|
|
bool QgsSpatiaLiteConnection::isDeclaredHidden( sqlite3 *handle, const QString &table, const QString &geom )
|
|
{
|
|
int ret;
|
|
int i;
|
|
char **results = nullptr;
|
|
int rows;
|
|
int columns;
|
|
char *errMsg = nullptr;
|
|
bool isHidden = false;
|
|
|
|
if ( !checkGeometryColumnsAuth( handle ) )
|
|
return false;
|
|
// checking if some Layer has been declared as HIDDEN
|
|
const QString sql = QString( "SELECT hidden FROM geometry_columns_auth"
|
|
" WHERE f_table_name=%1 and f_geometry_column=%2" )
|
|
.arg( QgsSqliteUtils::quotedString( table ), QgsSqliteUtils::quotedString( geom ) );
|
|
|
|
ret = sqlite3_get_table( handle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
|
|
if ( ret != SQLITE_OK )
|
|
goto error;
|
|
if ( rows < 1 )
|
|
;
|
|
else
|
|
{
|
|
for ( i = 1; i <= rows; i++ )
|
|
{
|
|
if ( results[( i * columns ) + 0] )
|
|
{
|
|
if ( atoi( results[( i * columns ) + 0] ) != 0 )
|
|
isHidden = true;
|
|
}
|
|
}
|
|
}
|
|
sqlite3_free_table( results );
|
|
|
|
return isHidden;
|
|
|
|
error:
|
|
// unexpected IO error
|
|
mErrorMsg = tr( "unknown error cause" );
|
|
if ( errMsg )
|
|
{
|
|
mErrorMsg = errMsg;
|
|
sqlite3_free( errMsg );
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
static void fcnRegexp( sqlite3_context *ctx, int /*argc*/, sqlite3_value *argv[] )
|
|
{
|
|
const QRegularExpression re( reinterpret_cast<const char *>( sqlite3_value_text( argv[0] ) ) );
|
|
const QString string( reinterpret_cast<const char *>( sqlite3_value_text( argv[1] ) ) );
|
|
|
|
if ( !re.isValid() )
|
|
return sqlite3_result_error( ctx, "invalid operand", -1 );
|
|
|
|
sqlite3_result_int( ctx, string.contains( re ) );
|
|
}
|
|
|
|
|
|
QMap<QString, QgsSqliteHandle *> QgsSqliteHandle::sHandles;
|
|
QMutex QgsSqliteHandle::sHandleMutex;
|
|
|
|
|
|
bool QgsSqliteHandle::checkMetadata( sqlite3 *handle )
|
|
{
|
|
int ret;
|
|
int i;
|
|
char **results = nullptr;
|
|
int rows;
|
|
int columns;
|
|
int spatial_type = 0;
|
|
ret = sqlite3_get_table( handle, "SELECT CheckSpatialMetadata()", &results, &rows, &columns, nullptr );
|
|
if ( ret != SQLITE_OK )
|
|
goto skip;
|
|
if ( rows < 1 )
|
|
;
|
|
else
|
|
{
|
|
for ( i = 1; i <= rows; i++ )
|
|
spatial_type = atoi( results[( i * columns ) + 0] );
|
|
}
|
|
sqlite3_free_table( results );
|
|
skip:
|
|
if ( spatial_type == 1 || spatial_type == 3 )
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
QgsSqliteHandle *QgsSqliteHandle::openDb( const QString &dbPath, bool shared )
|
|
{
|
|
//QMap < QString, QgsSqliteHandle* >&handles = QgsSqliteHandle::handles;
|
|
const QMutexLocker locker( &sHandleMutex );
|
|
|
|
if ( shared && sHandles.contains( dbPath ) )
|
|
{
|
|
QgsDebugMsgLevel( QStringLiteral( "Using cached connection for %1" ).arg( dbPath ), 2 );
|
|
sHandles[dbPath]->ref++;
|
|
return sHandles[dbPath];
|
|
}
|
|
|
|
QgsDebugMsgLevel( QStringLiteral( "New sqlite connection for " ) + dbPath, 2 );
|
|
spatialite_database_unique_ptr database;
|
|
if ( database.open_v2( dbPath, shared ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY | SQLITE_OPEN_NOMUTEX, nullptr ) )
|
|
{
|
|
// failure
|
|
QgsDebugError( QStringLiteral( "Failure while connecting to: %1\n%2" )
|
|
.arg( dbPath, QString::fromUtf8( sqlite3_errmsg( database.get() ) ) ) );
|
|
return nullptr;
|
|
}
|
|
|
|
// checking the DB for sanity
|
|
if ( !checkMetadata( database.get() ) )
|
|
{
|
|
// failure
|
|
QgsDebugError( QStringLiteral( "Failure while connecting to: %1\n\ninvalid metadata tables" ).arg( dbPath ) );
|
|
return nullptr;
|
|
}
|
|
|
|
// add REGEXP function
|
|
sqlite3_create_function( database.get(), "REGEXP", 2, SQLITE_UTF8, nullptr, fcnRegexp, nullptr, nullptr );
|
|
|
|
// activating Foreign Key constraints
|
|
( void ) sqlite3_exec( database.get(), "PRAGMA foreign_keys = 1", nullptr, nullptr, nullptr );
|
|
|
|
QgsDebugMsgLevel( QStringLiteral( "Connection to the database was successful" ), 2 );
|
|
|
|
QgsSqliteHandle *handle = new QgsSqliteHandle( std::move( database ), dbPath, shared );
|
|
if ( shared )
|
|
sHandles.insert( dbPath, handle );
|
|
|
|
return handle;
|
|
}
|
|
|
|
void QgsSqliteHandle::closeDb( QgsSqliteHandle *&handle )
|
|
{
|
|
if ( handle->ref == -1 )
|
|
{
|
|
// not shared
|
|
delete handle;
|
|
}
|
|
else
|
|
{
|
|
const QMutexLocker locker( &sHandleMutex );
|
|
QMap<QString, QgsSqliteHandle *>::iterator i;
|
|
for ( i = sHandles.begin(); i != sHandles.end() && i.value() != handle; ++i )
|
|
;
|
|
|
|
Q_ASSERT( i.value() == handle );
|
|
Q_ASSERT( i.value()->ref > 0 );
|
|
|
|
if ( --i.value()->ref == 0 )
|
|
{
|
|
delete i.value();
|
|
i = sHandles.erase( i );
|
|
}
|
|
}
|
|
|
|
handle = nullptr;
|
|
}
|
|
|
|
void QgsSqliteHandle::closeAll()
|
|
{
|
|
const QMutexLocker locker( &sHandleMutex );
|
|
qDeleteAll( sHandles );
|
|
sHandles.clear();
|
|
}
|