QGIS/src/providers/spatialite/qgsspatialiteprovider.cpp
Nyall Dawson b5864cd432 [FEATURE] Improve handling of defaults (inc provider default clauses,
literal defaults, and qgis expression defaults) and automatically
handle unique value constraints on layers

Add a new method QgsVectorLayerUtils::createFeature which returns
a new feature which includes all relevant defaults. Any fields
with unique value constraints will be guaranteed to have a value
which is unique for the field.

Currently only in use by the split feature tool.

Sponsored by Canton of Zug and the QGEP project
2016-11-16 14:19:24 +01:00

5918 lines
182 KiB
C++

/***************************************************************************
qgsspatialiteprovider.cpp Data provider for SpatiaLite DBMS
begin : Dec, 2008
copyright : (C) 2008 Sandro Furieri
email : a.furieri@lqt.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 "qgis.h"
#include "qgsapplication.h"
#include "qgsfeature.h"
#include "qgsfields.h"
#include "qgsgeometry.h"
#include "qgsmessageoutput.h"
#include "qgsrectangle.h"
#include "qgscoordinatereferencesystem.h"
#include "qgslogger.h"
#include "qgsmessagelog.h"
#include "qgsvectorlayerimport.h"
#include "qgsslconnect.h"
#include "qgsspatialiteprovider.h"
#include "qgsspatialiteconnpool.h"
#include "qgsspatialitefeatureiterator.h"
#include "qgsfeedback.h"
#include <qgsjsonutils.h>
#include <qgsvectorlayer.h>
#include <QMessageBox>
#include <QFileInfo>
#include <QDir>
#include <QRegularExpression>
const QString SPATIALITE_KEY = QStringLiteral( "spatialite" );
const QString SPATIALITE_DESCRIPTION = QStringLiteral( "SpatiaLite data provider" );
static const QString SPATIALITE_ARRAY_PREFIX = QStringLiteral( "json" );
static const QString SPATIALITE_ARRAY_SUFFIX = QStringLiteral( "list" );
bool QgsSpatiaLiteProvider::convertField( QgsField &field )
{
QString fieldType = QStringLiteral( "TEXT" ); //default to string
int fieldSize = field.length();
int fieldPrec = field.precision();
switch ( field.type() )
{
case QVariant::LongLong:
fieldType = QStringLiteral( "BIGINT" );
fieldSize = -1;
fieldPrec = 0;
break;
case QVariant::DateTime:
case QVariant::Date:
case QVariant::Time:
case QVariant::String:
fieldType = QStringLiteral( "TEXT" );
fieldPrec = -1;
break;
case QVariant::Int:
fieldType = QStringLiteral( "INTEGER" );
fieldSize = -1;
fieldPrec = 0;
break;
case QVariant::Double:
if ( fieldSize <= 0 || fieldPrec <= 0 )
{
fieldType = QStringLiteral( "REAL" );
fieldSize = -1;
fieldPrec = -1;
}
else
{
fieldType = QStringLiteral( "NUMERIC" );
}
break;
case QVariant::List:
case QVariant::StringList:
{
QgsField subField = field;
subField.setType( field.subType() );
subField.setSubType( QVariant::Invalid );
if ( !convertField( subField ) ) return false;
fieldType = SPATIALITE_ARRAY_PREFIX + subField.typeName() + SPATIALITE_ARRAY_SUFFIX;
fieldSize = subField.length();
fieldPrec = subField.precision();
break;
}
default:
return false;
}
field.setTypeName( fieldType );
field.setLength( fieldSize );
field.setPrecision( fieldPrec );
return true;
}
QgsVectorLayerImport::ImportError
QgsSpatiaLiteProvider::createEmptyLayer( const QString& uri,
const QgsFields &fields,
QgsWkbTypes::Type wkbType,
const QgsCoordinateReferenceSystem& srs,
bool overwrite,
QMap<int, int> *oldToNewAttrIdxMap,
QString *errorMessage,
const QMap<QString, QVariant> *options )
{
Q_UNUSED( options );
// populate members from the uri structure
QgsDataSourceUri dsUri( uri );
QString sqlitePath = dsUri.database();
QString tableName = dsUri.table();
QString geometryColumn = dsUri.geometryColumn();
QString geometryType;
QString primaryKey = dsUri.keyColumn();
QString primaryKeyType;
QgsDebugMsg( "Database is: " + sqlitePath );
QgsDebugMsg( "Table name is: " + tableName );
QgsDebugMsg( "Geometry column is: " + geometryColumn );
// create the table
{
char *errMsg = nullptr;
int toCommit = false;
QString sql;
// trying to open the SQLite DB
QgsSqliteHandle *handle = QgsSqliteHandle::openDb( sqlitePath );
if ( !handle )
{
QgsDebugMsg( "Connection to database failed. Import of layer aborted." );
if ( errorMessage )
*errorMessage = QObject::tr( "Connection to database failed" );
return QgsVectorLayerImport::ErrConnectionFailed;
}
sqlite3 *sqliteHandle = handle->handle();
// get the pk's name and type
if ( primaryKey.isEmpty() )
{
// if no pk name was passed, define the new pk field name
int index = 0;
QString pk = primaryKey = QStringLiteral( "pk" );
for ( int fldIdx = 0; fldIdx < fields.count(); ++fldIdx )
{
if ( fields.at( fldIdx ).name() == primaryKey )
{
// it already exists, try again with a new name
primaryKey = QStringLiteral( "%1_%2" ).arg( pk ).arg( index++ );
fldIdx = -1; // it is incremented in the for loop, i.e. restarts at 0
}
}
}
else
{
// search for the passed field
for ( int fldIdx = 0; fldIdx < fields.count(); ++fldIdx )
{
if ( fields.at( fldIdx ).name() == primaryKey )
{
// found it, get the field type
QgsField fld = fields.at( fldIdx );
if ( convertField( fld ) )
{
primaryKeyType = fld.typeName();
}
}
}
}
// if the pk field doesn't exist yet, create an integer pk field
// as it's autoincremental
if ( primaryKeyType.isEmpty() )
{
primaryKeyType = QStringLiteral( "INTEGER" );
}
else
{
// if the pk field's type is bigint, use the autoincremental
// integer type instead
if ( primaryKeyType == QLatin1String( "BIGINT" ) )
{
primaryKeyType = QStringLiteral( "INTEGER" );
}
}
try
{
int ret = sqlite3_exec( sqliteHandle, "BEGIN", nullptr, nullptr, &errMsg );
if ( ret != SQLITE_OK )
throw SLException( errMsg );
toCommit = true;
if ( overwrite )
{
// delete the table if exists and the related entry in geometry_columns, then re-create it
sql = QStringLiteral( "DROP TABLE IF EXISTS %1" )
.arg( quotedIdentifier( tableName ) );
ret = sqlite3_exec( sqliteHandle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg );
if ( ret != SQLITE_OK )
throw SLException( errMsg );
sql = QStringLiteral( "DELETE FROM geometry_columns WHERE upper(f_table_name) = upper(%1)" )
.arg( quotedValue( tableName ) );
ret = sqlite3_exec( sqliteHandle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg );
if ( ret != SQLITE_OK )
throw SLException( errMsg );
}
sql = QStringLiteral( "CREATE TABLE %1 (%2 %3 PRIMARY KEY)" )
.arg( quotedIdentifier( tableName ),
quotedIdentifier( primaryKey ),
primaryKeyType );
ret = sqlite3_exec( sqliteHandle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg );
if ( ret != SQLITE_OK )
throw SLException( errMsg );
// get geometry type, dim and srid
int dim = 2;
long srid = srs.postgisSrid();
switch ( wkbType )
{
case QgsWkbTypes::Point25D:
dim = 3;
FALLTHROUGH;
case QgsWkbTypes::Point:
geometryType = QStringLiteral( "POINT" );
break;
case QgsWkbTypes::LineString25D:
dim = 3;
FALLTHROUGH;
case QgsWkbTypes::LineString:
geometryType = QStringLiteral( "LINESTRING" );
break;
case QgsWkbTypes::Polygon25D:
dim = 3;
FALLTHROUGH;
case QgsWkbTypes::Polygon:
geometryType = QStringLiteral( "POLYGON" );
break;
case QgsWkbTypes::MultiPoint25D:
dim = 3;
FALLTHROUGH;
case QgsWkbTypes::MultiPoint:
geometryType = QStringLiteral( "MULTIPOINT" );
break;
case QgsWkbTypes::MultiLineString25D:
dim = 3;
FALLTHROUGH;
case QgsWkbTypes::MultiLineString:
geometryType = QStringLiteral( "MULTILINESTRING" );
break;
case QgsWkbTypes::MultiPolygon25D:
dim = 3;
FALLTHROUGH;
case QgsWkbTypes::MultiPolygon:
geometryType = QStringLiteral( "MULTIPOLYGON" );
break;
case QgsWkbTypes::Unknown:
geometryType = QStringLiteral( "GEOMETRY" );
break;
case QgsWkbTypes::NoGeometry:
default:
dim = 0;
break;
}
// create geometry column
if ( !geometryType.isEmpty() )
{
sql = QStringLiteral( "SELECT AddGeometryColumn(%1, %2, %3, %4, %5)" )
.arg( QgsSpatiaLiteProvider::quotedValue( tableName ),
QgsSpatiaLiteProvider::quotedValue( geometryColumn ) )
.arg( srid )
.arg( QgsSpatiaLiteProvider::quotedValue( geometryType ) )
.arg( dim );
ret = sqlite3_exec( sqliteHandle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg );
if ( ret != SQLITE_OK )
throw SLException( errMsg );
}
else
{
geometryColumn = QString();
}
ret = sqlite3_exec( sqliteHandle, "COMMIT", nullptr, nullptr, &errMsg );
if ( ret != SQLITE_OK )
throw SLException( errMsg );
}
catch ( SLException &e )
{
QgsDebugMsg( QString( "creation of data source %1 failed. %2" )
.arg( tableName,
e.errorMessage() )
);
if ( errorMessage )
*errorMessage = QObject::tr( "creation of data source %1 failed. %2" )
.arg( tableName,
e.errorMessage() );
if ( toCommit )
{
// ROLLBACK after some previous error
sqlite3_exec( sqliteHandle, "ROLLBACK", nullptr, nullptr, nullptr );
}
QgsSqliteHandle::closeDb( handle );
return QgsVectorLayerImport::ErrCreateLayer;
}
QgsSqliteHandle::closeDb( handle );
QgsDebugMsg( "layer " + tableName + " created." );
}
// use the provider to edit the table
dsUri.setDataSource( QLatin1String( "" ), tableName, geometryColumn, QString(), primaryKey );
QgsSpatiaLiteProvider *provider = new QgsSpatiaLiteProvider( dsUri.uri() );
if ( !provider->isValid() )
{
QgsDebugMsg( "The layer " + tableName + " just created is not valid or not supported by the provider." );
if ( errorMessage )
*errorMessage = QObject::tr( "loading of the layer %1 failed" )
.arg( tableName );
delete provider;
return QgsVectorLayerImport::ErrInvalidLayer;
}
QgsDebugMsg( "layer loaded" );
// add fields to the layer
if ( oldToNewAttrIdxMap )
oldToNewAttrIdxMap->clear();
if ( fields.size() > 0 )
{
int offset = 1;
// get the list of fields
QList<QgsField> flist;
for ( int fldIdx = 0; fldIdx < fields.count(); ++fldIdx )
{
QgsField fld = fields.at( fldIdx );
if ( fld.name() == primaryKey )
continue;
if ( fld.name() == geometryColumn )
{
QgsDebugMsg( "Found a field with the same name of the geometry column. Skip it!" );
continue;
}
if ( !convertField( fld ) )
{
QgsDebugMsg( "error creating field " + fld.name() + ": unsupported type" );
if ( errorMessage )
*errorMessage = QObject::tr( "unsupported type for field %1" )
.arg( fld.name() );
delete provider;
return QgsVectorLayerImport::ErrAttributeTypeUnsupported;
}
QgsDebugMsg( "creating field #" + QString::number( fldIdx ) +
" -> #" + QString::number( offset ) +
" name " + fld.name() +
" type " + QString( QVariant::typeToName( fld.type() ) ) +
" typename " + fld.typeName() +
" width " + QString::number( fld.length() ) +
" precision " + QString::number( fld.precision() ) );
flist.append( fld );
if ( oldToNewAttrIdxMap )
oldToNewAttrIdxMap->insert( fldIdx, offset );
offset++;
}
if ( !provider->addAttributes( flist ) )
{
QgsDebugMsg( "error creating fields " );
if ( errorMessage )
*errorMessage = QObject::tr( "creation of fields failed" );
delete provider;
return QgsVectorLayerImport::ErrAttributeCreationFailed;
}
QgsDebugMsg( "Done creating fields" );
}
return QgsVectorLayerImport::NoError;
}
QgsSpatiaLiteProvider::QgsSpatiaLiteProvider( QString const &uri )
: QgsVectorDataProvider( uri )
, mValid( false )
, mIsQuery( false )
, mTableBased( false )
, mViewBased( false )
, mVShapeBased( false )
, mReadOnly( false )
, mGeomType( QgsWkbTypes::Unknown )
, mSqliteHandle( nullptr )
, mSrid( -1 )
, mNumberFeatures( 0 )
, mSpatialIndexRTree( false )
, mSpatialIndexMbrCache( false )
, mEnabledCapabilities( 0 )
, mGotSpatialiteVersion( false )
, mSpatialiteVersionMajor( 0 )
, mSpatialiteVersionMinor( 0 )
{
nDims = GAIA_XY;
QgsDataSourceUri anUri = QgsDataSourceUri( uri );
// parsing members from the uri structure
mTableName = anUri.table();
mGeometryColumn = anUri.geometryColumn().toLower();
mSqlitePath = anUri.database();
mSubsetString = anUri.sql();
mPrimaryKey = anUri.keyColumn();
mQuery = mTableName;
// trying to open the SQLite DB
mHandle = QgsSqliteHandle::openDb( mSqlitePath );
if ( !mHandle )
{
return;
}
mSqliteHandle = mHandle->handle();
if ( mSqliteHandle )
{
QStringList pragmaList = anUri.params( QStringLiteral( "pragma" ) );
Q_FOREACH ( const QString& pragma, pragmaList )
{
char* errMsg = nullptr;
int ret = sqlite3_exec( mSqliteHandle, ( "PRAGMA " + pragma ).toUtf8(), nullptr, nullptr, &errMsg );
if ( ret != SQLITE_OK )
{
QgsDebugMsg( QString( "PRAGMA " ) + pragma + QString( " failed : %1" ).arg( errMsg ? errMsg : "" ) );
}
sqlite3_free( errMsg );
}
}
bool alreadyDone = false;
bool ret = false;
#ifdef SPATIALITE_VERSION_GE_4_0_0
// only if libspatialite version is >= 4.0.0
gaiaVectorLayersListPtr list = nullptr;
gaiaVectorLayerPtr lyr = nullptr;
bool specialCase = false;
if ( mGeometryColumn.isEmpty() )
specialCase = true; // non-spatial table
if ( mQuery.startsWith( '(' ) && mQuery.endsWith( ')' ) )
specialCase = true;
if ( !specialCase )
{
// using v.4.0 Abstract Interface
ret = true;
list = gaiaGetVectorLayersList( mSqliteHandle,
mTableName.toUtf8().constData(),
mGeometryColumn.toUtf8().constData(),
GAIA_VECTORS_LIST_OPTIMISTIC );
if ( list )
lyr = list->First;
ret = lyr && checkLayerTypeAbstractInterface( lyr );
QgsDebugMsg( "Using checkLayerTypeAbstractInterface" );
alreadyDone = true;
}
#endif
if ( !alreadyDone )
{
// check if this one Layer is based on a Table, View or VirtualShapefile
// by using the traditional methods
ret = checkLayerType();
}
if ( !ret )
{
// invalid metadata
mNumberFeatures = 0;
QgsDebugMsg( "Invalid SpatiaLite layer" );
closeDb();
return;
}
mEnabledCapabilities = mPrimaryKey.isEmpty() ? QgsVectorDataProvider::Capabilities() : ( QgsVectorDataProvider::SelectAtId );
if (( mTableBased || mViewBased ) && !mReadOnly )
{
// enabling editing only for Tables [excluding Views and VirtualShapes]
mEnabledCapabilities |= QgsVectorDataProvider::DeleteFeatures;
if ( !mGeometryColumn.isEmpty() )
mEnabledCapabilities |= QgsVectorDataProvider::ChangeGeometries;
mEnabledCapabilities |= QgsVectorDataProvider::ChangeAttributeValues;
mEnabledCapabilities |= QgsVectorDataProvider::AddFeatures;
mEnabledCapabilities |= QgsVectorDataProvider::AddAttributes;
}
alreadyDone = false;
#ifdef SPATIALITE_VERSION_GE_4_0_0
if ( lyr )
{
// using the v.4.0 AbstractInterface
if ( !getGeometryDetailsAbstractInterface( lyr ) ) // gets srid and geometry type
{
// the table is not a geometry table
mNumberFeatures = 0;
QgsDebugMsg( "Invalid SpatiaLite layer" );
closeDb();
gaiaFreeVectorLayersList( list );
return;
}
if ( !getTableSummaryAbstractInterface( lyr ) ) // gets the extent and feature count
{
mNumberFeatures = 0;
QgsDebugMsg( "Invalid SpatiaLite layer" );
closeDb();
gaiaFreeVectorLayersList( list );
return;
}
// load the columns list
loadFieldsAbstractInterface( lyr );
gaiaFreeVectorLayersList( list );
alreadyDone = true;
}
#endif
if ( !alreadyDone )
{
// using the traditional methods
if ( !getGeometryDetails() ) // gets srid and geometry type
{
// the table is not a geometry table
mNumberFeatures = 0;
QgsDebugMsg( "Invalid SpatiaLite layer" );
closeDb();
return;
}
if ( !getTableSummary() ) // gets the extent and feature count
{
mNumberFeatures = 0;
QgsDebugMsg( "Invalid SpatiaLite layer" );
closeDb();
return;
}
// load the columns list
loadFields();
}
if ( !mSqliteHandle )
{
QgsDebugMsg( "Invalid SpatiaLite layer" );
return;
}
if ( mTableBased && hasRowid() )
{
mPrimaryKey = QStringLiteral( "ROWID" );
}
// retrieve version information
spatialiteVersion();
//fill type names into sets
setNativeTypes( QList<NativeType>()
<< QgsVectorDataProvider::NativeType( tr( "Binary object (BLOB)" ), QStringLiteral( "BLOB" ), QVariant::ByteArray )
<< QgsVectorDataProvider::NativeType( tr( "Text" ), QStringLiteral( "TEXT" ), QVariant::String )
<< QgsVectorDataProvider::NativeType( tr( "Decimal number (double)" ), QStringLiteral( "FLOAT" ), QVariant::Double )
<< QgsVectorDataProvider::NativeType( tr( "Whole number (integer)" ), QStringLiteral( "INTEGER" ), QVariant::LongLong )
<< QgsVectorDataProvider::NativeType( tr( "Array of text" ), SPATIALITE_ARRAY_PREFIX.toUpper() + "TEXT" + SPATIALITE_ARRAY_SUFFIX.toUpper(), QVariant::StringList, 0, 0, 0, 0, QVariant::String )
<< QgsVectorDataProvider::NativeType( tr( "Array of decimal numbers (double)" ), SPATIALITE_ARRAY_PREFIX.toUpper() + "REAL" + SPATIALITE_ARRAY_SUFFIX.toUpper(), QVariant::List, 0, 0, 0, 0, QVariant::Double )
<< QgsVectorDataProvider::NativeType( tr( "Array of whole numbers (integer)" ), SPATIALITE_ARRAY_PREFIX.toUpper() + "INTEGER" + SPATIALITE_ARRAY_SUFFIX.toUpper(), QVariant::List, 0, 0, 0, 0, QVariant::LongLong )
);
mValid = true;
}
QgsSpatiaLiteProvider::~QgsSpatiaLiteProvider()
{
closeDb();
invalidateConnections( mSqlitePath );
}
QgsAbstractFeatureSource* QgsSpatiaLiteProvider::featureSource() const
{
return new QgsSpatiaLiteFeatureSource( this );
}
void QgsSpatiaLiteProvider::updatePrimaryKeyCapabilities()
{
if ( mPrimaryKey.isEmpty() )
{
mEnabledCapabilities &= ~QgsVectorDataProvider::SelectAtId;
}
else
{
mEnabledCapabilities |= QgsVectorDataProvider::SelectAtId;
}
}
typedef QPair<QVariant::Type, QVariant::Type> TypeSubType;
static TypeSubType getVariantType( const QString& type )
{
// making some assumptions in order to guess a more realistic type
if ( type == QLatin1String( "int" ) ||
type == QLatin1String( "integer" ) ||
type == QLatin1String( "integer64" ) ||
type == QLatin1String( "bigint" ) ||
type == QLatin1String( "smallint" ) ||
type == QLatin1String( "tinyint" ) ||
type == QLatin1String( "boolean" ) )
return TypeSubType( QVariant::LongLong, QVariant::Invalid );
else if ( type == QLatin1String( "real" ) ||
type == QLatin1String( "double" ) ||
type == QLatin1String( "double precision" ) ||
type == QLatin1String( "float" ) )
return TypeSubType( QVariant::Double, QVariant::Invalid );
else if ( type.startsWith( SPATIALITE_ARRAY_PREFIX ) && type.endsWith( SPATIALITE_ARRAY_SUFFIX ) )
{
// New versions of OGR convert list types (StringList, IntegerList, Integer64List and RealList)
// to JSON when it stores a Spatialite table. It sets the column type as JSONSTRINGLIST,
// JSONINTEGERLIST, JSONINTEGER64LIST or JSONREALLIST
TypeSubType subType = getVariantType( type.mid( SPATIALITE_ARRAY_PREFIX.length(),
type.length() - SPATIALITE_ARRAY_PREFIX.length() - SPATIALITE_ARRAY_SUFFIX.length() ) );
return TypeSubType( subType.first == QVariant::String ? QVariant::StringList : QVariant::List, subType.first );
}
else
// for sure any SQLite value can be represented as SQLITE_TEXT
return TypeSubType( QVariant::String, QVariant::Invalid );
}
#ifdef SPATIALITE_VERSION_GE_4_0_0
// only if libspatialite version is >= 4.0.0
void QgsSpatiaLiteProvider::loadFieldsAbstractInterface( gaiaVectorLayerPtr lyr )
{
if ( !lyr )
return;
mAttributeFields.clear();
mPrimaryKey.clear(); // cazzo cazzo cazzo
mPrimaryKeyAttrs.clear();
mDefaultValues.clear();
gaiaLayerAttributeFieldPtr fld = lyr->First;
if ( !fld )
{
// defaulting to traditional loadFields()
loadFields();
return;
}
while ( fld )
{
QString name = QString::fromUtf8( fld->AttributeFieldName );
if ( name.toLower() != mGeometryColumn )
{
const char *type = "TEXT";
QVariant::Type fieldType = QVariant::String; // default: SQLITE_TEXT
if ( fld->IntegerValuesCount != 0 && fld->DoubleValuesCount == 0 &&
fld->TextValuesCount == 0 && fld->BlobValuesCount == 0 )
{
fieldType = QVariant::LongLong;
type = "INTEGER";
}
if ( fld->DoubleValuesCount != 0 && fld->TextValuesCount == 0 &&
fld->BlobValuesCount == 0 )
{
fieldType = QVariant::Double;
type = "DOUBLE";
}
mAttributeFields.append( QgsField( name, fieldType, type, 0, 0, QLatin1String( "" ) ) );
}
fld = fld->Next;
}
QString sql = QStringLiteral( "PRAGMA table_info(%1)" ).arg( quotedIdentifier( mTableName ) );
char **results;
int rows;
int columns;
char *errMsg = nullptr;
int ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( ret == SQLITE_OK )
{
for ( int i = 1; i <= rows; i++ )
{
QString name = QString::fromUtf8( results[( i * columns ) + 1] );
insertDefaultValue( i - 1, QString::fromUtf8( results[( i * columns ) + 4] ) );
QString pk = results[( i * columns ) + 5];
QString type = results[( i * columns ) + 2];
type = type.toLower();
const int fieldIndex = mAttributeFields.indexFromName( name );
if ( fieldIndex >= 0 )
{ // set the actual type name, as given by sqlite
QgsField& field = mAttributeFields[fieldIndex];
field.setTypeName( type );
// TODO: column 4 tells us if the field is nullable. Should use that info...
if ( field.type() == QVariant::String )
{ // if the type seems unknown, fix it with what we actually have
TypeSubType typeSubType = getVariantType( type );
field.setType( typeSubType.first );
field.setSubType( typeSubType.second );
}
}
if ( pk.toInt() == 0 )
continue;
if ( mPrimaryKeyAttrs.isEmpty() )
mPrimaryKey = name;
else
mPrimaryKey.clear();
mPrimaryKeyAttrs << i - 1;
}
}
// check for constraints
fetchConstraints();
// for views try to get the primary key from the meta table
if ( mViewBased && mPrimaryKey.isEmpty() )
{
determineViewPrimaryKey();
}
updatePrimaryKeyCapabilities();
sqlite3_free_table( results );
}
#endif
QString QgsSpatiaLiteProvider::spatialiteVersion()
{
if ( mGotSpatialiteVersion )
return mSpatialiteVersionInfo;
int ret;
char **results;
int rows;
int columns;
char *errMsg = nullptr;
QString sql;
sql = QStringLiteral( "SELECT spatialite_version()" );
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8(), &results, &rows, &columns, &errMsg );
if ( ret != SQLITE_OK || rows != 1 )
{
QgsMessageLog::logMessage( tr( "Retrieval of spatialite version failed" ), tr( "SpatiaLite" ) );
return QString::null;
}
mSpatialiteVersionInfo = QString::fromUtf8( results[( 1 * columns ) + 0] );
sqlite3_free_table( results );
QgsDebugMsg( "SpatiaLite version info: " + mSpatialiteVersionInfo );
QStringList spatialiteParts = mSpatialiteVersionInfo.split( ' ', QString::SkipEmptyParts );
// Get major and minor version
QStringList spatialiteVersionParts = spatialiteParts[0].split( '.', QString::SkipEmptyParts );
if ( spatialiteVersionParts.size() < 2 )
{
QgsMessageLog::logMessage( tr( "Could not parse spatialite version string '%1'" ).arg( mSpatialiteVersionInfo ), tr( "SpatiaLite" ) );
return QString::null;
}
mSpatialiteVersionMajor = spatialiteVersionParts[0].toInt();
mSpatialiteVersionMinor = spatialiteVersionParts[1].toInt();
mGotSpatialiteVersion = true;
return mSpatialiteVersionInfo;
}
void QgsSpatiaLiteProvider::fetchConstraints()
{
char **results;
char *errMsg = nullptr;
// this is not particularly robust but unfortunately sqlite offers no way to check directly
// for the presence of constraints on a field (only indexes, but not all constraints are indexes)
QString sql = QStringLiteral( "SELECT sql FROM sqlite_master WHERE type='table' AND name=%1" ).arg( quotedIdentifier( mTableName ) );
int columns = 0;
int rows = 0;
int ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( ret != SQLITE_OK )
goto error;
if ( rows < 1 )
;
else
{
QString sqlDef = QString::fromUtf8( results[ 1 ] );
// extract definition
QRegularExpression re( QStringLiteral( "\\((.*)\\)" ) );
QRegularExpressionMatch match = re.match( sqlDef );
if ( match.hasMatch() )
{
QString matched = match.captured( 1 );
Q_FOREACH ( QString field, matched.split( ',' ) )
{
field = field.trimmed();
QString fieldName = field.left( field.indexOf( ' ' ) );
QString definition = field.mid( field.indexOf( ' ' ) + 1 );
int fieldIdx = mAttributeFields.lookupField( fieldName );
if ( fieldIdx >= 0 )
{
QgsFieldConstraints constraints = mAttributeFields.at( fieldIdx ).constraints();
if ( definition.contains( "unique", Qt::CaseInsensitive ) || definition.contains( "primary key", Qt::CaseInsensitive ) )
constraints.setConstraint( QgsFieldConstraints::ConstraintUnique, QgsFieldConstraints::ConstraintOriginProvider );
if ( definition.contains( "not null", Qt::CaseInsensitive ) || definition.contains( "primary key", Qt::CaseInsensitive ) )
constraints.setConstraint( QgsFieldConstraints::ConstraintNotNull, QgsFieldConstraints::ConstraintOriginProvider );
mAttributeFields[ fieldIdx ].setConstraints( constraints );
}
}
}
}
sqlite3_free_table( results );
Q_FOREACH ( int fieldIdx, mPrimaryKeyAttrs )
{
//primary keys are unique, not null
QgsFieldConstraints constraints = mAttributeFields.at( fieldIdx ).constraints();
constraints.setConstraint( QgsFieldConstraints::ConstraintUnique, QgsFieldConstraints::ConstraintOriginProvider );
constraints.setConstraint( QgsFieldConstraints::ConstraintNotNull, QgsFieldConstraints::ConstraintOriginProvider );
mAttributeFields[ fieldIdx ].setConstraints( constraints );
}
return;
error:
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errMsg ? errMsg : tr( "unknown cause" ) ), tr( "SpatiaLite" ) );
// unexpected error
if ( errMsg )
{
sqlite3_free( errMsg );
}
}
void QgsSpatiaLiteProvider::insertDefaultValue( int fieldIndex, QString defaultVal )
{
if ( !defaultVal.isEmpty() )
{
QVariant defaultVariant;
switch ( mAttributeFields.at( fieldIndex ).type() )
{
case QVariant::LongLong:
defaultVariant = defaultVal.toLongLong();
break;
case QVariant::Double:
defaultVariant = defaultVal.toDouble();
break;
default:
{
if ( defaultVal.startsWith( '\'' ) )
defaultVal = defaultVal.remove( 0, 1 );
if ( defaultVal.endsWith( '\'' ) )
defaultVal.chop( 1 );
defaultVal.replace( "''", "'" );
defaultVariant = defaultVal;
break;
}
}
mDefaultValues.insert( fieldIndex, defaultVariant );
}
}
void QgsSpatiaLiteProvider::loadFields()
{
int ret;
int i;
sqlite3_stmt *stmt = nullptr;
char **results;
int rows;
int columns;
char *errMsg = nullptr;
QString pkName;
int pkCount = 0;
QString sql;
mAttributeFields.clear();
mDefaultValues.clear();
if ( !mIsQuery )
{
mPrimaryKey.clear();
mPrimaryKeyAttrs.clear();
sql = QStringLiteral( "PRAGMA table_info(%1)" ).arg( quotedIdentifier( mTableName ) );
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( ret != SQLITE_OK )
goto error;
if ( rows < 1 )
;
else
{
for ( i = 1; i <= rows; i++ )
{
QString name = QString::fromUtf8( results[( i * columns ) + 1] );
QString type = QString::fromUtf8( results[( i * columns ) + 2] ).toLower();
QString pk = results[( i * columns ) + 5];
if ( pk.toInt() != 0 )
{
// found a Primary Key column
pkCount++;
if ( mPrimaryKeyAttrs.isEmpty() )
pkName = name;
else
pkName.clear();
mPrimaryKeyAttrs << i - 1;
QgsDebugMsg( "found primaryKey " + name );
}
if ( name.toLower() != mGeometryColumn )
{
const TypeSubType fieldType = getVariantType( type );
mAttributeFields.append( QgsField( name, fieldType.first, type, 0, 0, QString(), fieldType.second ) );
}
insertDefaultValue( i - 1, QString::fromUtf8( results[( i * columns ) + 4] ) );
}
}
sqlite3_free_table( results );
// check for constraints
fetchConstraints();
// for views try to get the primary key from the meta table
if ( mViewBased && mPrimaryKey.isEmpty() )
{
determineViewPrimaryKey();
}
}
else
{
sql = QStringLiteral( "select * from %1 limit 1" ).arg( mQuery );
if ( sqlite3_prepare_v2( mSqliteHandle, 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" ) );
return;
}
ret = sqlite3_step( stmt );
if ( ret == SQLITE_DONE )
{
// there are no rows to fetch
sqlite3_finalize( stmt );
return;
}
if ( ret == SQLITE_ROW )
{
// one valid row has been fetched from the result set
columns = sqlite3_column_count( stmt );
for ( i = 0; i < columns; i++ )
{
QString name = QString::fromUtf8( sqlite3_column_name( stmt, i ) );
QString type = QString::fromUtf8( sqlite3_column_decltype( stmt, i ) ).toLower();
if ( type.isEmpty() )
type = QStringLiteral( "text" );
if ( name == mPrimaryKey )
{
pkCount++;
if ( mPrimaryKeyAttrs.isEmpty() )
pkName = name;
else
pkName.clear();
mPrimaryKeyAttrs << i - 1;
QgsDebugMsg( "found primaryKey " + name );
}
if ( name.toLower() != mGeometryColumn )
{
const TypeSubType fieldType = getVariantType( type );
mAttributeFields.append( QgsField( name, fieldType.first, type, 0, 0, QString(), fieldType.second ) );
}
}
}
sqlite3_finalize( stmt );
}
if ( pkCount == 1 )
{
// setting the Primary Key column name
mPrimaryKey = pkName;
}
updatePrimaryKeyCapabilities();
return;
error:
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errMsg ? errMsg : tr( "unknown cause" ) ), tr( "SpatiaLite" ) );
// unexpected error
if ( errMsg )
{
sqlite3_free( errMsg );
}
}
void QgsSpatiaLiteProvider::determineViewPrimaryKey()
{
QString sql = QString( "SELECT view_rowid"
" FROM views_geometry_columns"
" WHERE upper(view_name) = upper(%1) and upper(view_geometry) = upper(%2)" ).arg( quotedValue( mTableName ),
quotedValue( mGeometryColumn ) );
char **results;
int rows;
int columns;
char *errMsg = nullptr;
int ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( ret == SQLITE_OK )
{
if ( rows > 0 )
{
mPrimaryKey = results[1 * columns];
int idx = mAttributeFields.lookupField( mPrimaryKey );
if ( idx != -1 )
mPrimaryKeyAttrs << idx;
}
sqlite3_free_table( results );
}
}
bool QgsSpatiaLiteProvider::hasTriggers()
{
int ret;
char **results;
int rows;
int columns;
char *errMsg = nullptr;
QString sql;
sql = QStringLiteral( "SELECT * FROM sqlite_master WHERE type='trigger' AND tbl_name=%1" )
.arg( quotedIdentifier( mTableName ) );
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
sqlite3_free_table( results );
return ( ret == SQLITE_OK && rows > 0 );
}
bool QgsSpatiaLiteProvider::hasRowid()
{
if ( mAttributeFields.lookupField( QStringLiteral( "ROWID" ) ) >= 0 )
return false;
// table without rowid column
QString sql = QStringLiteral( "SELECT rowid FROM %1 WHERE 0" ).arg( quotedIdentifier( mTableName ) );
char *errMsg = nullptr;
return sqlite3_exec( mSqliteHandle, sql.toUtf8(), nullptr, nullptr, &errMsg ) == SQLITE_OK;
}
QString QgsSpatiaLiteProvider::storageType() const
{
return QStringLiteral( "SQLite database with SpatiaLite extension" );
}
QgsFeatureIterator QgsSpatiaLiteProvider::getFeatures( const QgsFeatureRequest& request ) const
{
if ( !mValid )
{
QgsDebugMsg( "Read attempt on an invalid SpatiaLite data source" );
return QgsFeatureIterator();
}
return QgsFeatureIterator( new QgsSpatiaLiteFeatureIterator( new QgsSpatiaLiteFeatureSource( this ), true, request ) );
}
int QgsSpatiaLiteProvider::computeSizeFromGeosWKB2D( const unsigned char *blob,
int size, int type, int nDims,
int little_endian, int endian_arch )
{
Q_UNUSED( size );
// calculating the size required to store this WKB
int rings;
int points;
int ib;
const unsigned char *p_in = blob + 5;
int gsize = 5;
switch ( type )
{
// compunting the required size
case GAIA_POINT:
switch ( nDims )
{
case GAIA_XY_Z_M:
gsize += 4 * sizeof( double );
break;
case GAIA_XY_M:
case GAIA_XY_Z:
gsize += 3 * sizeof( double );
break;
default:
gsize += 2 * sizeof( double );
break;
}
break;
case GAIA_LINESTRING:
points = gaiaImport32( p_in, little_endian, endian_arch );
gsize += 4;
switch ( nDims )
{
case GAIA_XY_Z_M:
gsize += points * ( 4 * sizeof( double ) );
break;
case GAIA_XY_M:
case GAIA_XY_Z:
gsize += points * ( 3 * sizeof( double ) );
break;
default:
gsize += points * ( 2 * sizeof( double ) );
break;
}
break;
case GAIA_POLYGON:
rings = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gsize += 4;
for ( ib = 0; ib < rings; ib++ )
{
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gsize += 4;
switch ( nDims )
{
case GAIA_XY_Z_M:
gsize += points * ( 4 * sizeof( double ) );
break;
case GAIA_XY_M:
case GAIA_XY_Z:
gsize += points * ( 3 * sizeof( double ) );
break;
default:
gsize += points * ( 2 * sizeof( double ) );
break;
}
p_in += points * ( 2 * sizeof( double ) );
}
break;
default:
gsize += computeSizeFromMultiWKB2D( p_in, nDims, little_endian,
endian_arch );
break;
}
return gsize;
}
int QgsSpatiaLiteProvider::computeSizeFromMultiWKB2D( const unsigned char *p_in,
int nDims,
int little_endian,
int endian_arch )
{
// calculating the size required to store this WKB
int entities;
int type;
int rings;
int points;
int ie;
int ib;
int size = 0;
entities = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
size += 4;
for ( ie = 0; ie < entities; ie++ )
{
type = gaiaImport32( p_in + 1, little_endian, endian_arch );
p_in += 5;
size += 5;
switch ( type )
{
// compunting the required size
case GAIA_POINT:
switch ( nDims )
{
case GAIA_XY_Z_M:
size += 4 * sizeof( double );
break;
case GAIA_XY_Z:
case GAIA_XY_M:
size += 3 * sizeof( double );
break;
default:
size += 2 * sizeof( double );
break;
}
p_in += 2 * sizeof( double );
break;
case GAIA_LINESTRING:
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
size += 4;
switch ( nDims )
{
case GAIA_XY_Z_M:
size += points * ( 4 * sizeof( double ) );
break;
case GAIA_XY_Z:
case GAIA_XY_M:
size += points * ( 3 * sizeof( double ) );
break;
default:
size += points * ( 2 * sizeof( double ) );
break;
}
p_in += points * ( 2 * sizeof( double ) );
break;
case GAIA_POLYGON:
rings = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
size += 4;
for ( ib = 0; ib < rings; ib++ )
{
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
size += 4;
switch ( nDims )
{
case GAIA_XY_Z_M:
size += points * ( 4 * sizeof( double ) );
break;
case GAIA_XY_Z:
case GAIA_XY_M:
size += points * ( 3 * sizeof( double ) );
break;
default:
size += points * ( 2 * sizeof( double ) );
break;
}
p_in += points * ( 2 * sizeof( double ) );
}
break;
}
}
return size;
}
int QgsSpatiaLiteProvider::computeSizeFromGeosWKB3D( const unsigned char *blob,
int size, int type, int nDims,
int little_endian, int endian_arch )
{
Q_UNUSED( size );
// calculating the size required to store this WKB
int rings;
int points;
int ib;
const unsigned char *p_in = blob + 5;
int gsize = 5;
switch ( type )
{
// compunting the required size
case GEOS_3D_POINT:
switch ( nDims )
{
case GAIA_XY_Z_M:
gsize += 4 * sizeof( double );
break;
case GAIA_XY_M:
case GAIA_XY_Z:
gsize += 3 * sizeof( double );
break;
default:
gsize += 2 * sizeof( double );
break;
}
break;
case GEOS_3D_LINESTRING:
points = gaiaImport32( p_in, little_endian, endian_arch );
gsize += 4;
switch ( nDims )
{
case GAIA_XY_Z_M:
gsize += points * ( 4 * sizeof( double ) );
break;
case GAIA_XY_M:
case GAIA_XY_Z:
gsize += points * ( 3 * sizeof( double ) );
break;
default:
gsize += points * ( 2 * sizeof( double ) );
break;
}
break;
case GEOS_3D_POLYGON:
rings = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gsize += 4;
for ( ib = 0; ib < rings; ib++ )
{
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gsize += 4;
switch ( nDims )
{
case GAIA_XY_Z_M:
gsize += points * ( 4 * sizeof( double ) );
break;
case GAIA_XY_M:
case GAIA_XY_Z:
gsize += points * ( 3 * sizeof( double ) );
break;
default:
gsize += points * ( 2 * sizeof( double ) );
break;
}
p_in += points * ( 3 * sizeof( double ) );
}
break;
default:
gsize += computeSizeFromMultiWKB3D( p_in, nDims, little_endian,
endian_arch );
break;
}
return gsize;
}
int QgsSpatiaLiteProvider::computeSizeFromMultiWKB3D( const unsigned char *p_in,
int nDims,
int little_endian,
int endian_arch )
{
// calculating the size required to store this WKB
int entities;
int type;
int rings;
int points;
int ie;
int ib;
int size = 0;
entities = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
size += 4;
for ( ie = 0; ie < entities; ie++ )
{
type = gaiaImport32( p_in + 1, little_endian, endian_arch );
p_in += 5;
size += 5;
switch ( type )
{
// compunting the required size
case GEOS_3D_POINT:
switch ( nDims )
{
case GAIA_XY_Z_M:
size += 4 * sizeof( double );
break;
case GAIA_XY_Z:
case GAIA_XY_M:
size += 3 * sizeof( double );
break;
default:
size += 2 * sizeof( double );
break;
}
p_in += 3 * sizeof( double );
break;
case GEOS_3D_LINESTRING:
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
size += 4;
switch ( nDims )
{
case GAIA_XY_Z_M:
size += points * ( 4 * sizeof( double ) );
break;
case GAIA_XY_Z:
case GAIA_XY_M:
size += points * ( 3 * sizeof( double ) );
break;
default:
size += points * ( 2 * sizeof( double ) );
break;
}
p_in += points * ( 3 * sizeof( double ) );
break;
case GEOS_3D_POLYGON:
rings = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
size += 4;
for ( ib = 0; ib < rings; ib++ )
{
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
size += 4;
switch ( nDims )
{
case GAIA_XY_Z_M:
size += points * ( 4 * sizeof( double ) );
break;
case GAIA_XY_Z:
case GAIA_XY_M:
size += points * ( 3 * sizeof( double ) );
break;
default:
size += points * ( 2 * sizeof( double ) );
break;
}
p_in += points * ( 3 * sizeof( double ) );
}
break;
}
}
return size;
}
void QgsSpatiaLiteProvider::convertFromGeosWKB( const unsigned char *blob,
int blob_size,
unsigned char **wkb,
int *geom_size,
int nDims )
{
// attempting to convert from 2D/3D GEOS own WKB
int type;
int gDims;
int gsize;
int little_endian;
int endian_arch = gaiaEndianArch();
*wkb = nullptr;
*geom_size = 0;
if ( blob_size < 5 )
return;
if ( *( blob + 0 ) == 0x01 )
little_endian = GAIA_LITTLE_ENDIAN;
else
little_endian = GAIA_BIG_ENDIAN;
type = gaiaImport32( blob + 1, little_endian, endian_arch );
if ( type == GEOS_3D_POINT || type == GEOS_3D_LINESTRING
|| type == GEOS_3D_POLYGON
|| type == GEOS_3D_MULTIPOINT || type == GEOS_3D_MULTILINESTRING
|| type == GEOS_3D_MULTIPOLYGON || type == GEOS_3D_GEOMETRYCOLLECTION )
gDims = 3;
else if ( type == GAIA_POINT || type == GAIA_LINESTRING
|| type == GAIA_POLYGON || type == GAIA_MULTIPOINT
|| type == GAIA_MULTILINESTRING || type == GAIA_MULTIPOLYGON
|| type == GAIA_GEOMETRYCOLLECTION )
gDims = 2;
else
return;
if ( gDims == 2 && nDims == GAIA_XY )
{
// already 2D: simply copying is required
unsigned char *wkbGeom = new unsigned char[blob_size + 1];
memcpy( wkbGeom, blob, blob_size );
memset( wkbGeom + blob_size, 0, 1 );
*wkb = wkbGeom;
*geom_size = blob_size + 1;
return;
}
// we need creating a GAIA WKB
if ( gDims == 3 )
gsize = computeSizeFromGeosWKB3D( blob, blob_size, type, nDims,
little_endian, endian_arch );
else
gsize = computeSizeFromGeosWKB2D( blob, blob_size, type, nDims,
little_endian, endian_arch );
unsigned char *wkbGeom = new unsigned char[gsize];
memset( wkbGeom, '\0', gsize );
if ( gDims == 3 )
convertFromGeosWKB3D( blob, blob_size, wkbGeom, gsize, nDims,
little_endian, endian_arch );
else
convertFromGeosWKB2D( blob, blob_size, wkbGeom, gsize, nDims,
little_endian, endian_arch );
*wkb = wkbGeom;
*geom_size = gsize;
}
void QgsSpatiaLiteProvider::convertFromGeosWKB2D( const unsigned char *blob,
int blob_size,
unsigned char *wkb,
int geom_size,
int nDims,
int little_endian,
int endian_arch )
{
Q_UNUSED( blob_size );
Q_UNUSED( geom_size );
// attempting to convert from 2D GEOS own WKB
int type;
int entities;
int rings;
int points;
int ie;
int ib;
int iv;
const unsigned char *p_in;
unsigned char *p_out = wkb;
double coord;
// building from GEOS 2D WKB
*p_out++ = 0x01; // little endian byte order
type = gaiaImport32( blob + 1, little_endian, endian_arch );
switch ( type )
{
// setting Geometry TYPE
case GAIA_POINT:
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_POINTZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_POINTZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_POINTM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_POINT, 1, endian_arch );
break;
}
break;
case GAIA_LINESTRING:
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_LINESTRINGZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_LINESTRINGZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_LINESTRINGM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_LINESTRING, 1, endian_arch );
break;
}
break;
case GAIA_POLYGON:
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_POLYGONZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_POLYGONZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_POLYGONM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_POLYGON, 1, endian_arch );
break;
}
break;
case GAIA_MULTIPOINT:
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_MULTIPOINTZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_MULTIPOINTZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_MULTIPOINTM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_MULTIPOINT, 1, endian_arch );
break;
}
break;
case GAIA_MULTILINESTRING:
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_MULTILINESTRINGZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_MULTILINESTRINGZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_MULTILINESTRINGM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_MULTILINESTRING, 1, endian_arch );
break;
}
break;
case GAIA_MULTIPOLYGON:
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_MULTIPOLYGONZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_MULTIPOLYGONZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_MULTIPOLYGONM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_MULTIPOLYGON, 1, endian_arch );
break;
}
break;
case GAIA_GEOMETRYCOLLECTION:
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_GEOMETRYCOLLECTIONZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_GEOMETRYCOLLECTIONZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_GEOMETRYCOLLECTIONM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_GEOMETRYCOLLECTION, 1, endian_arch );
break;
}
break;
}
p_in = blob + 5;
p_out += 4;
switch ( type )
{
// setting Geometry values
case GAIA_POINT:
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
if ( nDims == GAIA_XY_Z || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // Z
p_out += sizeof( double );
}
if ( nDims == GAIA_XY_M || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // M
p_out += sizeof( double );
}
break;
case GAIA_LINESTRING:
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
if ( nDims == GAIA_XY_Z || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // Z
p_out += sizeof( double );
}
if ( nDims == GAIA_XY_M || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // M
p_out += sizeof( double );
}
}
break;
case GAIA_POLYGON:
rings = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, rings, 1, endian_arch );
p_out += 4;
for ( ib = 0; ib < rings; ib++ )
{
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
if ( nDims == GAIA_XY_Z || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // Z
p_out += sizeof( double );
}
if ( nDims == GAIA_XY_M || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // M
p_out += sizeof( double );
}
}
}
break;
case GAIA_MULTIPOINT:
entities = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, entities, 1, endian_arch );
p_out += 4;
for ( ie = 0; ie < entities; ie++ )
{
p_in += 5;
*p_out++ = 0x01;
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_POINTZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_POINTZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_POINTM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_POINT, 1, endian_arch );
break;
}
p_out += 4;
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
if ( nDims == GAIA_XY_Z || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // Z
p_out += sizeof( double );
}
if ( nDims == GAIA_XY_M || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // M
p_out += sizeof( double );
}
}
break;
case GAIA_MULTILINESTRING:
entities = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, entities, 1, endian_arch );
p_out += 4;
for ( ie = 0; ie < entities; ie++ )
{
p_in += 5;
*p_out++ = 0x01;
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_LINESTRINGZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_LINESTRINGZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_LINESTRINGM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_LINESTRING, 1, endian_arch );
break;
}
p_out += 4;
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
if ( nDims == GAIA_XY_Z || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // Z
p_out += sizeof( double );
}
if ( nDims == GAIA_XY_M || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // M
p_out += sizeof( double );
}
}
}
break;
case GAIA_MULTIPOLYGON:
entities = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, entities, 1, endian_arch );
p_out += 4;
for ( ie = 0; ie < entities; ie++ )
{
p_in += 5;
*p_out++ = 0x01;
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_POLYGONZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_POLYGONZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_POLYGONM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_POLYGON, 1, endian_arch );
break;
}
p_out += 4;
rings = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, rings, 1, endian_arch );
p_out += 4;
for ( ib = 0; ib < rings; ib++ )
{
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
if ( nDims == GAIA_XY_Z || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // Z
p_out += sizeof( double );
}
if ( nDims == GAIA_XY_M || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // M
p_out += sizeof( double );
}
}
}
}
break;
case GAIA_GEOMETRYCOLLECTION:
entities = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, entities, 1, endian_arch );
p_out += 4;
for ( ie = 0; ie < entities; ie++ )
{
int type2 = gaiaImport32( p_in + 1, little_endian, endian_arch );
p_in += 5;
*p_out++ = 0x01;
switch ( type2 )
{
case GAIA_POINT:
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_POINTZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_POINTZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_POINTM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_POINT, 1, endian_arch );
break;
}
break;
case GAIA_LINESTRING:
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_LINESTRINGZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_LINESTRINGZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_LINESTRINGM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_LINESTRING, 1, endian_arch );
break;
}
break;
case GAIA_POLYGON:
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_POLYGONZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_POLYGONZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_POLYGONM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_POLYGON, 1, endian_arch );
break;
}
break;
}
p_out += 4;
switch ( type2 )
{
// setting sub-Geometry values
case GAIA_POINT:
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
if ( nDims == GAIA_XY_Z || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // Z
p_out += sizeof( double );
}
if ( nDims == GAIA_XY_M || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // M
p_out += sizeof( double );
}
break;
case GAIA_LINESTRING:
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
if ( nDims == GAIA_XY_Z || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // Z
p_out += sizeof( double );
}
if ( nDims == GAIA_XY_M || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // M
p_out += sizeof( double );
}
}
break;
case GAIA_POLYGON:
rings = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, rings, 1, endian_arch );
p_out += 4;
for ( ib = 0; ib < rings; ib++ )
{
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
if ( nDims == GAIA_XY_Z || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // Z
p_out += sizeof( double );
}
if ( nDims == GAIA_XY_M || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // M
p_out += sizeof( double );
}
}
}
break;
}
}
break;
}
}
void QgsSpatiaLiteProvider::convertFromGeosWKB3D( const unsigned char *blob,
int blob_size,
unsigned char *wkb,
int geom_size,
int nDims,
int little_endian,
int endian_arch )
{
Q_UNUSED( blob_size );
Q_UNUSED( geom_size );
// attempting to convert from 3D GEOS own WKB
int type;
int entities;
int rings;
int points;
int ie;
int ib;
int iv;
const unsigned char *p_in;
unsigned char *p_out = wkb;
double coord;
// building from GEOS 3D WKB
*p_out++ = 0x01; // little endian byte order
type = gaiaImport32( blob + 1, little_endian, endian_arch );
switch ( type )
{
// setting Geometry TYPE
case GEOS_3D_POINT:
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_POINTZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_POINTZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_POINTM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_POINT, 1, endian_arch );
break;
}
break;
case GEOS_3D_LINESTRING:
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_LINESTRINGZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_LINESTRINGZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_LINESTRINGM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_LINESTRING, 1, endian_arch );
break;
}
break;
case GEOS_3D_POLYGON:
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_POLYGONZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_POLYGONZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_POLYGONM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_POLYGON, 1, endian_arch );
break;
}
break;
case GEOS_3D_MULTIPOINT:
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_MULTIPOINTZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_MULTIPOINTZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_MULTIPOINTM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_MULTIPOINT, 1, endian_arch );
break;
}
break;
case GEOS_3D_MULTILINESTRING:
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_MULTILINESTRINGZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_MULTILINESTRINGZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_MULTILINESTRINGM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_MULTILINESTRING, 1, endian_arch );
break;
}
break;
case GEOS_3D_MULTIPOLYGON:
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_MULTIPOLYGONZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_MULTIPOLYGONZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_MULTIPOLYGONM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_MULTIPOLYGON, 1, endian_arch );
break;
}
break;
case GEOS_3D_GEOMETRYCOLLECTION:
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_GEOMETRYCOLLECTIONZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_GEOMETRYCOLLECTIONZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_GEOMETRYCOLLECTIONM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_GEOMETRYCOLLECTION, 1, endian_arch );
break;
}
break;
}
p_in = blob + 5;
p_out += 4;
switch ( type )
{
// setting Geometry values
case GEOS_3D_POINT:
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
p_in += sizeof( double );
if ( nDims == GAIA_XY_Z || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, coord, 1, endian_arch ); // Z
p_out += sizeof( double );
}
if ( nDims == GAIA_XY_M || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // M
p_out += sizeof( double );
}
break;
case GEOS_3D_LINESTRING:
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
p_in += sizeof( double );
if ( nDims == GAIA_XY_Z || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, coord, 1, endian_arch ); // Z
p_out += sizeof( double );
}
if ( nDims == GAIA_XY_M || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // M
p_out += sizeof( double );
}
}
break;
case GEOS_3D_POLYGON:
rings = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, rings, 1, endian_arch );
p_out += 4;
for ( ib = 0; ib < rings; ib++ )
{
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
p_in += sizeof( double );
if ( nDims == GAIA_XY_Z || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, coord, 1, endian_arch ); // Z
p_out += sizeof( double );
}
if ( nDims == GAIA_XY_M || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // M
p_out += sizeof( double );
}
}
}
break;
case GEOS_3D_MULTIPOINT:
entities = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, entities, 1, endian_arch );
p_out += 4;
for ( ie = 0; ie < entities; ie++ )
{
p_in += 5;
*p_out++ = 0x01;
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_POINTZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_POINTZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_POINTM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_POINT, 1, endian_arch );
break;
}
p_out += 4;
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
p_in += sizeof( double );
if ( nDims == GAIA_XY_Z || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, coord, 1, endian_arch ); // Z
p_out += sizeof( double );
}
if ( nDims == GAIA_XY_M || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // M
p_out += sizeof( double );
}
}
break;
case GEOS_3D_MULTILINESTRING:
entities = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, entities, 1, endian_arch );
p_out += 4;
for ( ie = 0; ie < entities; ie++ )
{
p_in += 5;
*p_out++ = 0x01;
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_LINESTRINGZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_LINESTRINGZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_LINESTRINGM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_LINESTRING, 1, endian_arch );
break;
}
p_out += 4;
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
p_in += sizeof( double );
if ( nDims == GAIA_XY_Z || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, coord, 1, endian_arch ); // Z
p_out += sizeof( double );
}
if ( nDims == GAIA_XY_M || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // M
p_out += sizeof( double );
}
}
}
break;
case GEOS_3D_MULTIPOLYGON:
entities = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, entities, 1, endian_arch );
p_out += 4;
for ( ie = 0; ie < entities; ie++ )
{
p_in += 5;
*p_out++ = 0x01;
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_POLYGONZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_POLYGONZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_POLYGONM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_POLYGON, 1, endian_arch );
break;
}
p_out += 4;
rings = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, rings, 1, endian_arch );
p_out += 4;
for ( ib = 0; ib < rings; ib++ )
{
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
p_in += sizeof( double );
if ( nDims == GAIA_XY_Z || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, coord, 1, endian_arch ); // Z
p_out += sizeof( double );
}
if ( nDims == GAIA_XY_M || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // M
p_out += sizeof( double );
}
}
}
}
break;
case GEOS_3D_GEOMETRYCOLLECTION:
entities = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, entities, 1, endian_arch );
p_out += 4;
for ( ie = 0; ie < entities; ie++ )
{
int type2 = gaiaImport32( p_in + 1, little_endian, endian_arch );
p_in += 5;
*p_out++ = 0x01;
switch ( type2 )
{
case GEOS_3D_POINT:
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_POINTZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_POINTZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_POINTM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_POINT, 1, endian_arch );
break;
}
break;
case GEOS_3D_LINESTRING:
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_LINESTRINGZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_LINESTRINGZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_LINESTRINGM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_LINESTRING, 1, endian_arch );
break;
}
break;
case GEOS_3D_POLYGON:
switch ( nDims )
{
case GAIA_XY_Z_M:
gaiaExport32( p_out, GAIA_POLYGONZM, 1, endian_arch );
break;
case GAIA_XY_Z:
gaiaExport32( p_out, GAIA_POLYGONZ, 1, endian_arch );
break;
case GAIA_XY_M:
gaiaExport32( p_out, GAIA_POLYGONM, 1, endian_arch );
break;
default:
gaiaExport32( p_out, GAIA_POLYGON, 1, endian_arch );
break;
}
break;
}
p_out += 4;
switch ( type2 )
{
// setting sub-Geometry values
case GEOS_3D_POINT:
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
p_in += sizeof( double );
if ( nDims == GAIA_XY_Z || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, coord, 1, endian_arch ); // Z
p_out += sizeof( double );
}
if ( nDims == GAIA_XY_M || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // M
p_out += sizeof( double );
}
break;
case GEOS_3D_LINESTRING:
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
p_in += sizeof( double );
if ( nDims == GAIA_XY_Z || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, coord, 1, endian_arch ); // Z
p_out += sizeof( double );
}
if ( nDims == GAIA_XY_M || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // M
p_out += sizeof( double );
}
}
break;
case GEOS_3D_POLYGON:
rings = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, rings, 1, endian_arch );
p_out += 4;
for ( ib = 0; ib < rings; ib++ )
{
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
p_in += sizeof( double );
if ( nDims == GAIA_XY_Z || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, coord, 1, endian_arch ); // Z
p_out += sizeof( double );
}
if ( nDims == GAIA_XY_M || nDims == GAIA_XY_Z_M )
{
gaiaExport64( p_out, 0.0, 1, endian_arch ); // M
p_out += sizeof( double );
}
}
}
break;
}
}
break;
}
}
void QgsSpatiaLiteProvider::convertToGeosWKB( const unsigned char *blob,
int blob_size,
unsigned char **wkb,
int *geom_size )
{
// attempting to convert to 2D/3D GEOS own WKB
int type;
int dims;
int little_endian;
int endian_arch = gaiaEndianArch();
int entities;
int rings;
int points;
int ie;
int ib;
int iv;
int gsize = 6;
const unsigned char *p_in;
unsigned char *p_out;
double coord;
*wkb = nullptr;
*geom_size = 0;
if ( blob_size < 5 )
return;
if ( *( blob + 0 ) == 0x01 )
little_endian = GAIA_LITTLE_ENDIAN;
else
little_endian = GAIA_BIG_ENDIAN;
type = gaiaImport32( blob + 1, little_endian, endian_arch );
if ( type == GAIA_POINTZ || type == GAIA_LINESTRINGZ
|| type == GAIA_POLYGONZ
|| type == GAIA_MULTIPOINTZ || type == GAIA_MULTILINESTRINGZ
|| type == GAIA_MULTIPOLYGONZ || type == GAIA_GEOMETRYCOLLECTIONZ )
dims = 3;
else if ( type == GAIA_POINTM || type == GAIA_LINESTRINGM
|| type == GAIA_POLYGONM || type == GAIA_MULTIPOINTM
|| type == GAIA_MULTILINESTRINGM || type == GAIA_MULTIPOLYGONM
|| type == GAIA_GEOMETRYCOLLECTIONM )
dims = 3;
else if ( type == GAIA_POINTZM || type == GAIA_LINESTRINGZM
|| type == GAIA_POLYGONZM || type == GAIA_MULTIPOINTZM
|| type == GAIA_MULTILINESTRINGZM || type == GAIA_MULTIPOLYGONZM
|| type == GAIA_GEOMETRYCOLLECTIONZM )
dims = 4;
else if ( type == GAIA_POINT || type == GAIA_LINESTRING
|| type == GAIA_POLYGON || type == GAIA_MULTIPOINT
|| type == GAIA_MULTILINESTRING || type == GAIA_MULTIPOLYGON
|| type == GAIA_GEOMETRYCOLLECTION )
dims = 2;
else
return;
if ( dims == 2 )
{
// already 2D: simply copying is required
unsigned char *wkbGeom = new unsigned char[blob_size + 1];
memcpy( wkbGeom, blob, blob_size );
memset( wkbGeom + blob_size, 0, 1 );
*wkb = wkbGeom;
*geom_size = blob_size + 1;
return;
}
// we need creating a 3D GEOS WKB
p_in = blob + 5;
switch ( type )
{
// compunting the required size
case GAIA_POINTZ:
case GAIA_POINTM:
case GAIA_POINTZM:
gsize += 3 * sizeof( double );
break;
case GAIA_LINESTRINGZ:
case GAIA_LINESTRINGM:
case GAIA_LINESTRINGZM:
points = gaiaImport32( p_in, little_endian, endian_arch );
gsize += 4;
gsize += points * ( 3 * sizeof( double ) );
break;
case GAIA_POLYGONZ:
case GAIA_POLYGONM:
rings = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gsize += 4;
for ( ib = 0; ib < rings; ib++ )
{
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gsize += 4;
gsize += points * ( 3 * sizeof( double ) );
p_in += points * ( 3 * sizeof( double ) );
}
break;
case GAIA_POLYGONZM:
rings = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gsize += 4;
for ( ib = 0; ib < rings; ib++ )
{
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gsize += 4;
gsize += points * ( 3 * sizeof( double ) );
p_in += points * ( 4 * sizeof( double ) );
}
break;
default:
gsize += computeMultiWKB3Dsize( p_in, little_endian, endian_arch );
break;
}
unsigned char *wkbGeom = new unsigned char[gsize];
memset( wkbGeom, '\0', gsize );
// building GEOS 3D WKB
*wkbGeom = 0x01; // little endian byte order
type = gaiaImport32( blob + 1, little_endian, endian_arch );
switch ( type )
{
// setting Geometry TYPE
case GAIA_POINTZ:
case GAIA_POINTM:
case GAIA_POINTZM:
gaiaExport32( wkbGeom + 1, GEOS_3D_POINT, 1, endian_arch );
break;
case GAIA_LINESTRINGZ:
case GAIA_LINESTRINGM:
case GAIA_LINESTRINGZM:
gaiaExport32( wkbGeom + 1, GEOS_3D_LINESTRING, 1, endian_arch );
break;
case GAIA_POLYGONZ:
case GAIA_POLYGONM:
case GAIA_POLYGONZM:
gaiaExport32( wkbGeom + 1, GEOS_3D_POLYGON, 1, endian_arch );
break;
case GAIA_MULTIPOINTZ:
case GAIA_MULTIPOINTM:
case GAIA_MULTIPOINTZM:
gaiaExport32( wkbGeom + 1, GEOS_3D_MULTIPOINT, 1, endian_arch );
break;
case GAIA_MULTILINESTRINGZ:
case GAIA_MULTILINESTRINGM:
case GAIA_MULTILINESTRINGZM:
gaiaExport32( wkbGeom + 1, GEOS_3D_MULTILINESTRING, 1, endian_arch );
break;
case GAIA_MULTIPOLYGONZ:
case GAIA_MULTIPOLYGONM:
case GAIA_MULTIPOLYGONZM:
gaiaExport32( wkbGeom + 1, GEOS_3D_MULTIPOLYGON, 1, endian_arch );
break;
case GAIA_GEOMETRYCOLLECTIONZ:
case GAIA_GEOMETRYCOLLECTIONM:
case GAIA_GEOMETRYCOLLECTIONZM:
gaiaExport32( wkbGeom + 1, GEOS_3D_GEOMETRYCOLLECTION, 1, endian_arch );
break;
}
p_in = blob + 5;
p_out = wkbGeom + 5;
switch ( type )
{
// setting Geometry values
case GAIA_POINTM:
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
gaiaExport64( p_out, 0.0, 1, endian_arch ); // Z
p_in += sizeof( double );
p_out += sizeof( double );
break;
case GAIA_POINTZ:
case GAIA_POINTZM:
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Z
p_in += sizeof( double );
p_out += sizeof( double );
if ( type == GAIA_POINTZM )
p_in += sizeof( double );
break;
case GAIA_LINESTRINGM:
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
gaiaExport64( p_out, 0.0, 1, endian_arch ); // Z
p_in += sizeof( double );
p_out += sizeof( double );
}
break;
case GAIA_LINESTRINGZ:
case GAIA_LINESTRINGZM:
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Z
p_in += sizeof( double );
p_out += sizeof( double );
if ( type == GAIA_LINESTRINGZM )
p_in += sizeof( double );
}
break;
case GAIA_POLYGONM:
rings = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, rings, 1, endian_arch );
p_out += 4;
for ( ib = 0; ib < rings; ib++ )
{
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
gaiaExport64( p_out, 0.0, 1, endian_arch ); // Z
p_in += sizeof( double );
p_out += sizeof( double );
}
}
break;
case GAIA_POLYGONZ:
case GAIA_POLYGONZM:
rings = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, rings, 1, endian_arch );
p_out += 4;
for ( ib = 0; ib < rings; ib++ )
{
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Z
p_in += sizeof( double );
p_out += sizeof( double );
if ( type == GAIA_POLYGONZM )
p_in += sizeof( double );
}
}
break;
case GAIA_MULTIPOINTM:
entities = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, entities, 1, endian_arch );
p_out += 4;
for ( ie = 0; ie < entities; ie++ )
{
p_in += 5;
*p_out++ = 0x01;
gaiaExport32( p_out, GEOS_3D_POINT, 1, endian_arch );
p_out += 4;
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
gaiaExport64( p_out, 0.0, 1, endian_arch ); // Z
p_in += sizeof( double );
p_out += sizeof( double );
}
break;
case GAIA_MULTIPOINTZ:
case GAIA_MULTIPOINTZM:
entities = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, entities, 1, endian_arch );
p_out += 4;
for ( ie = 0; ie < entities; ie++ )
{
p_in += 5;
*p_out++ = 0x01;
gaiaExport32( p_out, GEOS_3D_POINT, 1, endian_arch );
p_out += 4;
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Z
p_in += sizeof( double );
p_out += sizeof( double );
if ( type == GAIA_MULTIPOINTZM )
p_in += sizeof( double );
}
break;
case GAIA_MULTILINESTRINGM:
entities = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, entities, 1, endian_arch );
p_out += 4;
for ( ie = 0; ie < entities; ie++ )
{
p_in += 5;
*p_out++ = 0x01;
gaiaExport32( p_out, GEOS_3D_LINESTRING, 1, endian_arch );
p_out += 4;
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
gaiaExport64( p_out, 0.0, 1, endian_arch ); // Z
p_in += sizeof( double );
p_out += sizeof( double );
}
}
break;
case GAIA_MULTILINESTRINGZ:
case GAIA_MULTILINESTRINGZM:
entities = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, entities, 1, endian_arch );
p_out += 4;
for ( ie = 0; ie < entities; ie++ )
{
p_in += 5;
*p_out++ = 0x01;
gaiaExport32( p_out, GEOS_3D_LINESTRING, 1, endian_arch );
p_out += 4;
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Z
p_in += sizeof( double );
p_out += sizeof( double );
if ( type == GAIA_MULTILINESTRINGZM )
p_in += sizeof( double );
}
}
break;
case GAIA_MULTIPOLYGONM:
entities = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, entities, 1, endian_arch );
p_out += 4;
for ( ie = 0; ie < entities; ie++ )
{
p_in += 5;
*p_out++ = 0x01;
gaiaExport32( p_out, GEOS_3D_POLYGON, 1, endian_arch );
p_out += 4;
rings = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, rings, 1, endian_arch );
p_out += 4;
for ( ib = 0; ib < rings; ib++ )
{
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
gaiaExport64( p_out, 0.0, 1, endian_arch ); // Z
p_in += sizeof( double );
p_out += sizeof( double );
}
}
}
break;
case GAIA_MULTIPOLYGONZ:
case GAIA_MULTIPOLYGONZM:
entities = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, entities, 1, endian_arch );
p_out += 4;
for ( ie = 0; ie < entities; ie++ )
{
p_in += 5;
*p_out++ = 0x01;
gaiaExport32( p_out, GEOS_3D_POLYGON, 1, endian_arch );
p_out += 4;
rings = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, rings, 1, endian_arch );
p_out += 4;
for ( ib = 0; ib < rings; ib++ )
{
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Z
p_in += sizeof( double );
p_out += sizeof( double );
if ( type == GAIA_MULTIPOLYGONZM )
p_in += sizeof( double );
}
}
}
break;
case GAIA_GEOMETRYCOLLECTIONM:
case GAIA_GEOMETRYCOLLECTIONZ:
case GAIA_GEOMETRYCOLLECTIONZM:
entities = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, entities, 1, endian_arch );
p_out += 4;
for ( ie = 0; ie < entities; ie++ )
{
int type2 = gaiaImport32( p_in + 1, little_endian, endian_arch );
p_in += 5;
*p_out++ = 0x01;
switch ( type2 )
{
case GAIA_POINTZ:
case GAIA_POINTM:
case GAIA_POINTZM:
gaiaExport32( p_out, GEOS_3D_POINT, 1, endian_arch );
break;
case GAIA_LINESTRINGZ:
case GAIA_LINESTRINGM:
case GAIA_LINESTRINGZM:
gaiaExport32( p_out, GEOS_3D_LINESTRING, 1, endian_arch );
break;
case GAIA_POLYGONZ:
case GAIA_POLYGONM:
case GAIA_POLYGONZM:
gaiaExport32( p_out, GEOS_3D_POLYGON, 1, endian_arch );
break;
}
p_out += 4;
switch ( type2 )
{
// setting sub-Geometry values
case GAIA_POINTM:
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
gaiaExport64( p_out, 0.0, 1, endian_arch ); // Z
p_in += sizeof( double );
p_out += sizeof( double );
break;
case GAIA_POINTZ:
case GAIA_POINTZM:
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Z
p_in += sizeof( double );
p_out += sizeof( double );
if ( type2 == GAIA_POINTZM )
p_in += sizeof( double );
break;
case GAIA_LINESTRINGM:
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
gaiaExport64( p_out, 0.0, 1, endian_arch ); // Z
p_in += sizeof( double );
p_out += sizeof( double );
}
break;
case GAIA_LINESTRINGZ:
case GAIA_LINESTRINGZM:
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Z
p_in += sizeof( double );
p_out += sizeof( double );
if ( type2 == GAIA_LINESTRINGZM )
p_in += sizeof( double );
}
break;
case GAIA_POLYGONM:
rings = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, rings, 1, endian_arch );
p_out += 4;
for ( ib = 0; ib < rings; ib++ )
{
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
gaiaExport64( p_out, 0.0, 1, endian_arch ); // Z
p_in += sizeof( double );
p_out += sizeof( double );
}
}
break;
case GAIA_POLYGONZ:
case GAIA_POLYGONZM:
rings = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, rings, 1, endian_arch );
p_out += 4;
for ( ib = 0; ib < rings; ib++ )
{
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
gaiaExport32( p_out, points, 1, endian_arch );
p_out += 4;
for ( iv = 0; iv < points; iv++ )
{
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // X
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Y
p_in += sizeof( double );
p_out += sizeof( double );
coord = gaiaImport64( p_in, little_endian, endian_arch );
gaiaExport64( p_out, coord, 1, endian_arch ); // Z
p_in += sizeof( double );
p_out += sizeof( double );
if ( type2 == GAIA_POLYGONZM )
p_in += sizeof( double );
}
}
break;
}
}
break;
}
*wkb = wkbGeom;
*geom_size = gsize;
}
int QgsSpatiaLiteProvider::computeMultiWKB3Dsize( const unsigned char *p_in, int little_endian, int endian_arch )
{
// computing the required size to store a GEOS 3D MultiXX
int entities;
int type;
int rings;
int points;
int ie;
int ib;
int size = 0;
entities = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
size += 4;
for ( ie = 0; ie < entities; ie++ )
{
type = gaiaImport32( p_in + 1, little_endian, endian_arch );
p_in += 5;
size += 5;
switch ( type )
{
// compunting the required size
case GAIA_POINTZ:
case GAIA_POINTM:
size += 3 * sizeof( double );
p_in += 3 * sizeof( double );
break;
case GAIA_POINTZM:
size += 3 * sizeof( double );
p_in += 4 * sizeof( double );
break;
case GAIA_LINESTRINGZ:
case GAIA_LINESTRINGM:
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
size += 4;
size += points * ( 3 * sizeof( double ) );
p_in += points * ( 3 * sizeof( double ) );
break;
case GAIA_LINESTRINGZM:
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
size += 4;
size += points * ( 3 * sizeof( double ) );
p_in += points * ( 4 * sizeof( double ) );
break;
case GAIA_POLYGONZ:
case GAIA_POLYGONM:
rings = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
size += 4;
for ( ib = 0; ib < rings; ib++ )
{
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
size += 4;
size += points * ( 3 * sizeof( double ) );
p_in += points * ( 3 * sizeof( double ) );
}
break;
case GAIA_POLYGONZM:
rings = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
size += 4;
for ( ib = 0; ib < rings; ib++ )
{
points = gaiaImport32( p_in, little_endian, endian_arch );
p_in += 4;
size += 4;
size += points * ( 3 * sizeof( double ) );
p_in += points * ( 4 * sizeof( double ) );
}
break;
}
}
return size;
}
QString QgsSpatiaLiteProvider::subsetString() const
{
return mSubsetString;
}
bool QgsSpatiaLiteProvider::setSubsetString( const QString& theSQL, bool updateFeatureCount )
{
QString prevSubsetString = mSubsetString;
mSubsetString = theSQL;
// update URI
QgsDataSourceUri uri = QgsDataSourceUri( dataSourceUri() );
uri.setSql( mSubsetString );
setDataSourceUri( uri.uri() );
// update feature count and extents
if ( updateFeatureCount && getTableSummary() )
{
return true;
}
mSubsetString = prevSubsetString;
// restore URI
uri = QgsDataSourceUri( dataSourceUri() );
uri.setSql( mSubsetString );
setDataSourceUri( uri.uri() );
getTableSummary();
emit dataChanged();
return false;
}
QgsRectangle QgsSpatiaLiteProvider::extent() const
{
return mLayerExtent;
}
void QgsSpatiaLiteProvider::updateExtents()
{
getTableSummary();
}
size_t QgsSpatiaLiteProvider::layerCount() const
{
return 1;
}
/**
* Return the feature type
*/
QgsWkbTypes::Type QgsSpatiaLiteProvider::wkbType() const
{
return mGeomType;
}
/**
* Return the feature type
*/
long QgsSpatiaLiteProvider::featureCount() const
{
return mNumberFeatures;
}
QgsCoordinateReferenceSystem QgsSpatiaLiteProvider::crs() const
{
QgsCoordinateReferenceSystem srs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( mAuthId );
if ( !srs.isValid() )
{
srs = QgsCoordinateReferenceSystem::fromProj4( mProj4text );
//TODO: createFromProj4 used to save to the user database any new CRS
// this behavior was changed in order to separate creation and saving.
// Not sure if it necessary to save it here, should be checked by someone
// familiar with the code (should also give a more descriptive name to the generated CRS)
if ( srs.srsid() == 0 )
{
QString myName = QStringLiteral( " * %1 (%2)" )
.arg( QObject::tr( "Generated CRS", "A CRS automatically generated from layer info get this prefix for description" ),
srs.toProj4() );
srs.saveAsUserCrs( myName );
}
}
return srs;
}
bool QgsSpatiaLiteProvider::isValid() const
{
return mValid;
}
QString QgsSpatiaLiteProvider::name() const
{
return SPATIALITE_KEY;
} // QgsSpatiaLiteProvider::name()
QString QgsSpatiaLiteProvider::description() const
{
return SPATIALITE_DESCRIPTION;
} // QgsSpatiaLiteProvider::description()
QgsFields QgsSpatiaLiteProvider::fields() const
{
return mAttributeFields;
}
// Returns the minimum value of an attribute
QVariant QgsSpatiaLiteProvider::minimumValue( int index ) const
{
int ret;
int i;
char **results;
int rows;
int columns;
char *errMsg = nullptr;
QString minValue;
QString sql;
try
{
// get the field name
QgsField fld = field( index );
sql = QStringLiteral( "SELECT Min(%1) FROM %2" ).arg( quotedIdentifier( fld.name() ), mQuery );
if ( !mSubsetString.isEmpty() )
{
sql += " WHERE ( " + mSubsetString + ')';
}
ret = sqlite3_get_table( mSqliteHandle, 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" ) );
// unexpected error
if ( errMsg )
{
sqlite3_free( errMsg );
}
minValue = QString();
}
else
{
if ( rows < 1 )
;
else
{
for ( i = 1; i <= rows; i++ )
{
minValue = results[( i * columns ) + 0];
}
}
sqlite3_free_table( results );
if ( minValue.isEmpty() )
{
// NULL or not found
minValue = QString();
}
}
return convertValue( fld.type(), minValue );
}
catch ( SLFieldNotFound )
{
return QVariant( QVariant::Int );
}
}
// Returns the maximum value of an attribute
QVariant QgsSpatiaLiteProvider::maximumValue( int index ) const
{
int ret;
int i;
char **results;
int rows;
int columns;
char *errMsg = nullptr;
QString maxValue;
QString sql;
try
{
// get the field name
QgsField fld = field( index );
sql = QStringLiteral( "SELECT Max(%1) FROM %2" ).arg( quotedIdentifier( fld.name() ), mQuery );
if ( !mSubsetString.isEmpty() )
{
sql += " WHERE ( " + mSubsetString + ')';
}
ret = sqlite3_get_table( mSqliteHandle, 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" ) );
// unexpected error
if ( errMsg )
{
sqlite3_free( errMsg );
}
maxValue = QString();
}
else
{
if ( rows < 1 )
;
else
{
for ( i = 1; i <= rows; i++ )
{
maxValue = results[( i * columns ) + 0];
}
}
sqlite3_free_table( results );
if ( maxValue.isEmpty() )
{
// NULL or not found
maxValue = QString();
}
}
return convertValue( fld.type(), maxValue );
}
catch ( SLFieldNotFound )
{
return QVariant( QVariant::Int );
}
}
// Returns the list of unique values of an attribute
void QgsSpatiaLiteProvider::uniqueValues( int index, QList < QVariant > &uniqueValues, int limit ) const
{
sqlite3_stmt *stmt = nullptr;
QString sql;
uniqueValues.clear();
// get the field name
if ( index < 0 || index >= mAttributeFields.count() )
{
return; //invalid field
}
QgsField fld = mAttributeFields.at( index );
sql = QStringLiteral( "SELECT DISTINCT %1 FROM %2" ).arg( quotedIdentifier( fld.name() ), mQuery );
if ( !mSubsetString.isEmpty() )
{
sql += " WHERE ( " + mSubsetString + ')';
}
sql += QStringLiteral( " ORDER BY %1" ).arg( quotedIdentifier( fld.name() ) );
if ( limit >= 0 )
{
sql += QStringLiteral( " LIMIT %1" ).arg( limit );
}
// SQLite prepared statement
if ( sqlite3_prepare_v2( mSqliteHandle, 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" ) );
return;
}
while ( 1 )
{
// this one is an infinitive loop, intended to fetch any row
int ret = sqlite3_step( stmt );
if ( ret == SQLITE_DONE )
{
// there are no more rows to fetch - we can stop looping
break;
}
if ( ret == SQLITE_ROW )
{
// fetching one column value
switch ( sqlite3_column_type( stmt, 0 ) )
{
case SQLITE_INTEGER:
uniqueValues.append( QVariant( sqlite3_column_int( stmt, 0 ) ) );
break;
case SQLITE_FLOAT:
uniqueValues.append( QVariant( sqlite3_column_double( stmt, 0 ) ) );
break;
case SQLITE_TEXT:
uniqueValues.append( QVariant( QString::fromUtf8(( const char * ) sqlite3_column_text( stmt, 0 ) ) ) );
break;
default:
uniqueValues.append( QVariant( mAttributeFields.at( index ).type() ) );
break;
}
}
else
{
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( mSqliteHandle ) ), tr( "SpatiaLite" ) );
sqlite3_finalize( stmt );
return;
}
}
sqlite3_finalize( stmt );
return;
}
QStringList QgsSpatiaLiteProvider::uniqueStringsMatching( int index, const QString& substring, int limit, QgsFeedback* feedback ) const
{
QStringList results;
sqlite3_stmt *stmt = nullptr;
QString sql;
// get the field name
if ( index < 0 || index >= mAttributeFields.count() )
{
return results; //invalid field
}
QgsField fld = mAttributeFields.at( index );
sql = QStringLiteral( "SELECT DISTINCT %1 FROM %2 " ).arg( quotedIdentifier( fld.name() ), mQuery );
sql += QStringLiteral( " WHERE " ) + quotedIdentifier( fld.name() ) + QStringLiteral( " LIKE '%" ) + substring + QStringLiteral( "%'" );
if ( !mSubsetString.isEmpty() )
{
sql += QStringLiteral( " AND ( " ) + mSubsetString + ')';
}
sql += QStringLiteral( " ORDER BY %1" ).arg( quotedIdentifier( fld.name() ) );
if ( limit >= 0 )
{
sql += QStringLiteral( " LIMIT %1" ).arg( limit );
}
// SQLite prepared statement
if ( sqlite3_prepare_v2( mSqliteHandle, 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" ) );
return results;
}
while (( limit < 0 || results.size() < limit ) && ( !feedback || !feedback->isCancelled() ) )
{
// this one is an infinitive loop, intended to fetch any row
int ret = sqlite3_step( stmt );
if ( ret == SQLITE_DONE )
{
// there are no more rows to fetch - we can stop looping
break;
}
if ( ret == SQLITE_ROW )
{
// fetching one column value
switch ( sqlite3_column_type( stmt, 0 ) )
{
case SQLITE_TEXT:
results.append( QString::fromUtf8(( const char * ) sqlite3_column_text( stmt, 0 ) ) );
break;
default:
break;
}
}
else
{
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( mSqliteHandle ) ), tr( "SpatiaLite" ) );
sqlite3_finalize( stmt );
return results;
}
}
sqlite3_finalize( stmt );
return results;
}
QString QgsSpatiaLiteProvider::geomParam() const
{
QString geometry;
bool forceMulti = QgsWkbTypes::isMultiType( wkbType() );
// ST_Multi function is available from QGIS >= 2.4
bool hasMultiFunction = mSpatialiteVersionMajor > 2 ||
( mSpatialiteVersionMajor == 2 && mSpatialiteVersionMinor >= 4 );
if ( forceMulti && hasMultiFunction )
{
geometry += QLatin1String( "ST_Multi(" );
}
geometry += QStringLiteral( "GeomFromWKB(?, %2)" ).arg( mSrid );
if ( forceMulti && hasMultiFunction )
{
geometry += ')';
}
return geometry;
}
static void deleteWkbBlob( void* wkbBlob )
{
delete[]( char* )wkbBlob;
}
bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList & flist )
{
sqlite3_stmt *stmt = nullptr;
char *errMsg = nullptr;
bool toCommit = false;
QString sql;
QString values;
QString separator;
int ia, ret;
if ( flist.isEmpty() )
return true;
QgsAttributes attributevec = flist[0].attributes();
ret = sqlite3_exec( mSqliteHandle, "BEGIN", nullptr, nullptr, &errMsg );
if ( ret == SQLITE_OK )
{
toCommit = true;
sql = QStringLiteral( "INSERT INTO %1(" ).arg( quotedIdentifier( mTableName ) );
values = QStringLiteral( ") VALUES (" );
separator = QLatin1String( "" );
if ( !mGeometryColumn.isEmpty() )
{
sql += separator + quotedIdentifier( mGeometryColumn );
values += separator + geomParam();
separator = ',';
}
for ( int i = 0; i < attributevec.count(); ++i )
{
if ( i >= mAttributeFields.count() )
continue;
QString fieldname = mAttributeFields.at( i ).name();
if ( fieldname.isEmpty() || fieldname == mGeometryColumn )
continue;
sql += separator + quotedIdentifier( fieldname );
values += separator + '?';
separator = ',';
}
sql += values;
sql += ')';
// SQLite prepared statement
ret = sqlite3_prepare_v2( mSqliteHandle, sql.toUtf8().constData(), -1, &stmt, nullptr );
if ( ret == SQLITE_OK )
{
for ( QgsFeatureList::iterator feature = flist.begin(); feature != flist.end(); ++feature )
{
// looping on each feature to insert
QgsAttributes attributevec = feature->attributes();
// resetting Prepared Statement and bindings
sqlite3_reset( stmt );
sqlite3_clear_bindings( stmt );
// initializing the column counter
ia = 0;
if ( !mGeometryColumn.isEmpty() )
{
// binding GEOMETRY to Prepared Statement
if ( !feature->hasGeometry() )
{
sqlite3_bind_null( stmt, ++ia );
}
else
{
unsigned char *wkb = nullptr;
int wkb_size;
QByteArray featureWkb = feature->geometry().exportToWkb();
convertFromGeosWKB( reinterpret_cast<const unsigned char*>( featureWkb.constData() ),
featureWkb.length(),
&wkb, &wkb_size, nDims );
if ( !wkb )
sqlite3_bind_null( stmt, ++ia );
else
sqlite3_bind_blob( stmt, ++ia, wkb, wkb_size, deleteWkbBlob );
}
}
for ( int i = 0; i < attributevec.count(); ++i )
{
QVariant v = attributevec.at( i );
// binding values for each attribute
if ( i >= mAttributeFields.count() )
break;
QString fieldname = mAttributeFields.at( i ).name();
if ( fieldname.isEmpty() || fieldname == mGeometryColumn )
continue;
QVariant::Type type = mAttributeFields.at( i ).type();
if ( !v.isValid() )
{
++ia;
}
else if ( v.isNull() )
{
// binding a NULL value
sqlite3_bind_null( stmt, ++ia );
}
else if ( type == QVariant::Int )
{
// binding an INTEGER value
sqlite3_bind_int( stmt, ++ia, v.toInt() );
}
else if ( type == QVariant::LongLong )
{
// binding a LONGLONG value
sqlite3_bind_int64( stmt, ++ia, v.toLongLong() );
}
else if ( type == QVariant::Double )
{
// binding a DOUBLE value
sqlite3_bind_double( stmt, ++ia, v.toDouble() );
}
else if ( type == QVariant::String )
{
QString stringVal = v.toString();
// binding a TEXT value
QByteArray ba = stringVal.toUtf8();
sqlite3_bind_text( stmt, ++ia, ba.constData(), ba.size(), SQLITE_TRANSIENT );
}
else if ( type == QVariant::StringList || type == QVariant::List )
{
const QByteArray ba = QgsJSONUtils::encodeValue( v ).toUtf8();
sqlite3_bind_text( stmt, ++ia, ba.constData(), ba.size(), SQLITE_TRANSIENT );
}
else
{
// Unknown type: bind a NULL value
sqlite3_bind_null( stmt, ++ia );
}
}
// performing actual row insert
ret = sqlite3_step( stmt );
if ( ret == SQLITE_DONE || ret == SQLITE_ROW )
{
// update feature id
feature->setFeatureId( sqlite3_last_insert_rowid( mSqliteHandle ) );
mNumberFeatures++;
}
else
{
// some unexpected error occurred
const char *err = sqlite3_errmsg( mSqliteHandle );
errMsg = ( char * ) sqlite3_malloc(( int ) strlen( err ) + 1 );
strcpy( errMsg, err );
break;
}
}
sqlite3_finalize( stmt );
if ( ret == SQLITE_DONE || ret == SQLITE_ROW )
{
ret = sqlite3_exec( mSqliteHandle, "COMMIT", nullptr, nullptr, &errMsg );
}
} // prepared statement
} // BEGIN statement
if ( ret != SQLITE_OK )
{
pushError( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errMsg ? errMsg : tr( "unknown cause" ) ) );
if ( errMsg )
{
sqlite3_free( errMsg );
}
if ( toCommit )
{
// ROLLBACK after some previous error
( void )sqlite3_exec( mSqliteHandle, "ROLLBACK", nullptr, nullptr, nullptr );
}
}
return ret == SQLITE_OK;
}
bool QgsSpatiaLiteProvider::deleteFeatures( const QgsFeatureIds &id )
{
sqlite3_stmt *stmt = nullptr;
char *errMsg = nullptr;
bool toCommit = false;
QString sql;
int ret = sqlite3_exec( mSqliteHandle, "BEGIN", nullptr, nullptr, &errMsg );
if ( ret != SQLITE_OK )
{
// some error occurred
goto abort;
}
toCommit = true;
sql = QStringLiteral( "DELETE FROM %1 WHERE %2=?" ).arg( quotedIdentifier( mTableName ), quotedIdentifier( mPrimaryKey ) );
// SQLite prepared statement
if ( sqlite3_prepare_v2( mSqliteHandle, sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
{
// some error occurred
pushError( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( mSqliteHandle ) ) );
return false;
}
for ( QgsFeatureIds::const_iterator it = id.begin(); it != id.end(); ++it )
{
// looping on each feature to be deleted
// resetting Prepared Statement and bindings
sqlite3_reset( stmt );
sqlite3_clear_bindings( stmt );
qint64 fid = FID_TO_NUMBER( *it );
sqlite3_bind_int64( stmt, 1, fid );
// performing actual row deletion
ret = sqlite3_step( stmt );
if ( ret == SQLITE_DONE || ret == SQLITE_ROW )
{
mNumberFeatures--;
}
else
{
// some unexpected error occurred
const char *err = sqlite3_errmsg( mSqliteHandle );
errMsg = ( char * ) sqlite3_malloc(( int ) strlen( err ) + 1 );
strcpy( errMsg, err );
goto abort;
}
}
sqlite3_finalize( stmt );
ret = sqlite3_exec( mSqliteHandle, "COMMIT", nullptr, nullptr, &errMsg );
if ( ret != SQLITE_OK )
{
// some error occurred
goto abort;
}
return true;
abort:
pushError( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errMsg ? errMsg : tr( "unknown cause" ) ) );
if ( errMsg )
{
sqlite3_free( errMsg );
}
if ( toCommit )
{
// ROLLBACK after some previous error
( void )sqlite3_exec( mSqliteHandle, "ROLLBACK", nullptr, nullptr, nullptr );
}
return false;
}
bool QgsSpatiaLiteProvider::addAttributes( const QList<QgsField> &attributes )
{
char *errMsg = nullptr;
bool toCommit = false;
QString sql;
if ( attributes.isEmpty() )
return true;
int ret = sqlite3_exec( mSqliteHandle, "BEGIN", nullptr, nullptr, &errMsg );
if ( ret != SQLITE_OK )
{
// some error occurred
goto abort;
}
toCommit = true;
for ( QList<QgsField>::const_iterator iter = attributes.begin(); iter != attributes.end(); ++iter )
{
sql = QStringLiteral( "ALTER TABLE \"%1\" ADD COLUMN \"%2\" %3" )
.arg( mTableName,
iter->name(),
iter->typeName() );
ret = sqlite3_exec( mSqliteHandle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg );
if ( ret != SQLITE_OK )
{
// some error occurred
goto abort;
}
}
ret = sqlite3_exec( mSqliteHandle, "COMMIT", nullptr, nullptr, &errMsg );
if ( ret != SQLITE_OK )
{
// some error occurred
goto abort;
}
#ifdef SPATIALITE_VERSION_GE_4_0_0
sql = QStringLiteral( "UPDATE geometry_columns_statistics set last_verified = 0 WHERE f_table_name=\"%1\" AND f_geometry_column=\"%2\";" )
.arg( mTableName,
mGeometryColumn );
ret = sqlite3_exec( mSqliteHandle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg );
update_layer_statistics( mSqliteHandle, mTableName.toUtf8().constData(), mGeometryColumn.toUtf8().constData() );
#elif SPATIALITE_VERSION_G_4_1_1
gaiaStatisticsInvalidate( mSqliteHandle, tableName.toUtf8().constData(), mGeometryColumn.toUtf8().constData() );
update_layer_statistics( mSqliteHandle, mTableName.toUtf8().constData(), mGeometryColumn.toUtf8().constData() );
#endif
// reload columns
loadFields();
return true;
abort:
pushError( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errMsg ? errMsg : tr( "unknown cause" ) ) );
if ( errMsg )
{
sqlite3_free( errMsg );
}
if ( toCommit )
{
// ROLLBACK after some previous error
( void )sqlite3_exec( mSqliteHandle, "ROLLBACK", nullptr, nullptr, nullptr );
}
return false;
}
bool QgsSpatiaLiteProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_map )
{
char *errMsg = nullptr;
bool toCommit = false;
QString sql;
if ( attr_map.isEmpty() )
return true;
int ret = sqlite3_exec( mSqliteHandle, "BEGIN", nullptr, nullptr, &errMsg );
if ( ret != SQLITE_OK )
{
// some error occurred
goto abort;
}
toCommit = true;
for ( QgsChangedAttributesMap::const_iterator iter = attr_map.begin(); iter != attr_map.end(); ++iter )
{
// Loop over all changed features
QgsFeatureId fid = iter.key();
// skip added features
if ( FID_IS_NEW( fid ) )
continue;
const QgsAttributeMap &attrs = iter.value();
if ( attrs.isEmpty() )
continue;
QString sql = QStringLiteral( "UPDATE %1 SET " ).arg( quotedIdentifier( mTableName ) );
bool first = true;
// cycle through the changed attributes of the feature
for ( QgsAttributeMap::const_iterator siter = attrs.begin(); siter != attrs.end(); ++siter )
{
// Loop over all changed attributes
try
{
QgsField fld = field( siter.key() );
const QVariant& val = siter.value();
if ( !first )
sql += ',';
else
first = false;
QVariant::Type type = fld.type();
if ( val.isNull() || !val.isValid() )
{
// binding a NULL value
sql += QStringLiteral( "%1=NULL" ).arg( quotedIdentifier( fld.name() ) );
}
else if ( type == QVariant::Int || type == QVariant::LongLong || type == QVariant::Double )
{
// binding a NUMERIC value
sql += QStringLiteral( "%1=%2" ).arg( quotedIdentifier( fld.name() ) , val.toString() );
}
else if ( type == QVariant::StringList || type == QVariant::List )
{
// binding an array value
sql += QStringLiteral( "%1=%2" ).arg( quotedIdentifier( fld.name() ), quotedValue( QgsJSONUtils::encodeValue( val ) ) );
}
else
{
// binding a TEXT value
sql += QStringLiteral( "%1=%2" ).arg( quotedIdentifier( fld.name() ), quotedValue( val.toString() ) );
}
}
catch ( SLFieldNotFound )
{
// Field was missing - shouldn't happen
}
}
sql += QStringLiteral( " WHERE %1=%2" ).arg( quotedIdentifier( mPrimaryKey ) ).arg( fid );
ret = sqlite3_exec( mSqliteHandle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg );
if ( ret != SQLITE_OK )
{
// some error occurred
goto abort;
}
}
ret = sqlite3_exec( mSqliteHandle, "COMMIT", nullptr, nullptr, &errMsg );
if ( ret != SQLITE_OK )
{
// some error occurred
goto abort;
}
return true;
abort:
pushError( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errMsg ? errMsg : tr( "unknown cause" ) ) );
if ( errMsg )
{
sqlite3_free( errMsg );
}
if ( toCommit )
{
// ROLLBACK after some previous error
( void )sqlite3_exec( mSqliteHandle, "ROLLBACK", nullptr, nullptr, nullptr );
}
return false;
}
bool QgsSpatiaLiteProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
{
sqlite3_stmt *stmt = nullptr;
char *errMsg = nullptr;
bool toCommit = false;
QString sql;
int ret = sqlite3_exec( mSqliteHandle, "BEGIN", nullptr, nullptr, &errMsg );
if ( ret != SQLITE_OK )
{
// some error occurred
goto abort;
}
toCommit = true;
sql =
QStringLiteral( "UPDATE %1 SET %2=GeomFromWKB(?, %3) WHERE %4=?" )
.arg( quotedIdentifier( mTableName ),
quotedIdentifier( mGeometryColumn ) )
.arg( mSrid )
.arg( quotedIdentifier( mPrimaryKey ) );
// SQLite prepared statement
if ( sqlite3_prepare_v2( mSqliteHandle, 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" ) );
return false;
}
for ( QgsGeometryMap::const_iterator iter = geometry_map.constBegin(); iter != geometry_map.constEnd(); ++iter )
{
// resetting Prepared Statement and bindings
sqlite3_reset( stmt );
sqlite3_clear_bindings( stmt );
// binding GEOMETRY to Prepared Statement
unsigned char *wkb = nullptr;
int wkb_size;
QByteArray iterWkb = iter->exportToWkb();
convertFromGeosWKB( reinterpret_cast<const unsigned char*>( iterWkb.constData() ), iterWkb.length(), &wkb, &wkb_size, nDims );
if ( !wkb )
sqlite3_bind_null( stmt, 1 );
else
sqlite3_bind_blob( stmt, 1, wkb, wkb_size, deleteWkbBlob );
sqlite3_bind_int64( stmt, 2, FID_TO_NUMBER( iter.key() ) );
// performing actual row update
ret = sqlite3_step( stmt );
if ( ret == SQLITE_DONE || ret == SQLITE_ROW )
;
else
{
// some unexpected error occurred
const char *err = sqlite3_errmsg( mSqliteHandle );
errMsg = ( char * ) sqlite3_malloc(( int ) strlen( err ) + 1 );
strcpy( errMsg, err );
goto abort;
}
}
sqlite3_finalize( stmt );
ret = sqlite3_exec( mSqliteHandle, "COMMIT", nullptr, nullptr, &errMsg );
if ( ret != SQLITE_OK )
{
// some error occurred
goto abort;
}
return true;
abort:
pushError( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errMsg ? errMsg : tr( "unknown cause" ) ) );
if ( errMsg )
{
sqlite3_free( errMsg );
}
if ( toCommit )
{
// ROLLBACK after some previous error
( void )sqlite3_exec( mSqliteHandle, "ROLLBACK", nullptr, nullptr, nullptr );
}
return false;
}
QgsVectorDataProvider::Capabilities QgsSpatiaLiteProvider::capabilities() const
{
return mEnabledCapabilities;
}
QVariant QgsSpatiaLiteProvider::defaultValue( int fieldId ) const
{
return mDefaultValues.value( fieldId, QVariant() );
}
void QgsSpatiaLiteProvider::closeDb()
{
// trying to close the SQLite DB
if ( mHandle )
{
QgsSqliteHandle::closeDb( mHandle );
mHandle = nullptr;
}
}
QString QgsSpatiaLiteProvider::quotedIdentifier( QString id )
{
id.replace( '\"', QLatin1String( "\"\"" ) );
return id.prepend( '\"' ).append( '\"' );
}
QString QgsSpatiaLiteProvider::quotedValue( QString value )
{
if ( value.isNull() )
return QStringLiteral( "NULL" );
value.replace( '\'', QLatin1String( "''" ) );
return value.prepend( '\'' ).append( '\'' );
}
#ifdef SPATIALITE_VERSION_GE_4_0_0
// only if libspatialite version is >= 4.0.0
bool QgsSpatiaLiteProvider::checkLayerTypeAbstractInterface( gaiaVectorLayerPtr lyr )
{
if ( !lyr )
return false;
mTableBased = false;
mViewBased = false;
mVShapeBased = false;
mIsQuery = false;
mReadOnly = false;
switch ( lyr->LayerType )
{
case GAIA_VECTOR_TABLE:
mTableBased = true;
break;
case GAIA_VECTOR_VIEW:
mViewBased = true;
break;
case GAIA_VECTOR_VIRTUAL:
mVShapeBased = true;
break;
}
if ( lyr->AuthInfos )
{
if ( lyr->AuthInfos->IsReadOnly )
mReadOnly = true;
}
else if ( mViewBased )
{
mReadOnly = !hasTriggers();
}
if ( !mIsQuery )
{
mQuery = quotedIdentifier( mTableName );
}
return true;
}
#endif
bool QgsSpatiaLiteProvider::checkLayerType()
{
int ret;
int i;
char **results;
int rows;
int columns;
char *errMsg = nullptr;
int count = 0;
mTableBased = false;
mViewBased = false;
mVShapeBased = false;
mIsQuery = false;
QString sql;
if ( mGeometryColumn.isEmpty() && !( mQuery.startsWith( '(' ) && mQuery.endsWith( ')' ) ) )
{
// checking if is a non-spatial table
sql = QString( "SELECT type FROM sqlite_master "
"WHERE lower(name) = lower(%1) "
"AND type in ('table', 'view') " ).arg( quotedValue( mTableName ) );
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( ret == SQLITE_OK && rows == 1 )
{
QString type = QString( results[ columns + 0 ] );
if ( type == QLatin1String( "table" ) )
{
mTableBased = true;
mReadOnly = false;
}
else if ( type == QLatin1String( "view" ) )
{
mViewBased = true;
mReadOnly = !hasTriggers();
}
count++;
}
if ( errMsg )
{
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errMsg ), tr( "SpatiaLite" ) );
sqlite3_free( errMsg );
errMsg = nullptr;
}
sqlite3_free_table( results );
}
else if ( mQuery.startsWith( '(' ) && mQuery.endsWith( ')' ) )
{
// checking if this one is a select query
// get a new alias for the subquery
int index = 0;
QString alias;
QRegExp regex;
do
{
alias = QStringLiteral( "subQuery_%1" ).arg( QString::number( index++ ) );
QString pattern = QStringLiteral( "(\\\"?)%1\\1" ).arg( QRegExp::escape( alias ) );
regex.setPattern( pattern );
regex.setCaseSensitivity( Qt::CaseInsensitive );
}
while ( mQuery.contains( regex ) );
// convert the custom query into a subquery
mQuery = QStringLiteral( "%1 as %2" )
.arg( mQuery,
quotedIdentifier( alias ) );
sql = QStringLiteral( "SELECT 0 FROM %1 LIMIT 1" ).arg( mQuery );
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( ret == SQLITE_OK && rows == 1 )
{
mIsQuery = true;
mReadOnly = true;
count++;
}
if ( errMsg )
{
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errMsg ), tr( "SpatiaLite" ) );
sqlite3_free( errMsg );
errMsg = nullptr;
}
sqlite3_free_table( results );
}
else
{
// checking if this one is a Table-based layer
sql = QString( "SELECT read_only FROM geometry_columns "
"LEFT JOIN geometry_columns_auth "
"USING (f_table_name, f_geometry_column) "
"WHERE upper(f_table_name) = upper(%1) and upper(f_geometry_column) = upper(%2)" )
.arg( quotedValue( mTableName ),
quotedValue( mGeometryColumn ) );
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( ret != SQLITE_OK )
{
if ( errMsg && strcmp( errMsg, "no such table: geometry_columns_auth" ) == 0 )
{
sqlite3_free( errMsg );
sql = QStringLiteral( "SELECT 0 FROM geometry_columns WHERE upper(f_table_name) = upper(%1) and upper(f_geometry_column) = upper(%2)" )
.arg( quotedValue( mTableName ),
quotedValue( mGeometryColumn ) );
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
}
}
if ( ret == SQLITE_OK && rows == 1 )
{
mTableBased = true;
mReadOnly = false;
for ( i = 1; i <= rows; i++ )
{
if ( results[( i * columns ) + 0] )
{
if ( atoi( results[( i * columns ) + 0] ) != 0 )
mReadOnly = true;
}
}
count++;
}
if ( errMsg )
{
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errMsg ), tr( "SpatiaLite" ) );
sqlite3_free( errMsg );
errMsg = nullptr;
}
sqlite3_free_table( results );
// checking if this one is a View-based layer
sql = QString( "SELECT view_name, view_geometry FROM views_geometry_columns"
" WHERE view_name=%1 and view_geometry=%2" ).arg( quotedValue( mTableName ),
quotedValue( mGeometryColumn ) );
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( ret == SQLITE_OK && rows == 1 )
{
mViewBased = true;
mReadOnly = !hasTriggers();
count++;
}
if ( errMsg )
{
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errMsg ), tr( "SpatiaLite" ) );
sqlite3_free( errMsg );
errMsg = nullptr;
}
sqlite3_free_table( results );
// checking if this one is a VirtualShapefile-based layer
sql = QString( "SELECT virt_name, virt_geometry FROM virts_geometry_columns"
" WHERE virt_name=%1 and virt_geometry=%2" ).arg( quotedValue( mTableName ),
quotedValue( mGeometryColumn ) );
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( ret == SQLITE_OK && rows == 1 )
{
mVShapeBased = true;
mReadOnly = true;
count++;
}
if ( errMsg )
{
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errMsg ), tr( "SpatiaLite" ) );
sqlite3_free( errMsg );
errMsg = nullptr;
}
sqlite3_free_table( results );
}
if ( !mIsQuery )
{
mQuery = quotedIdentifier( mTableName );
}
// checking for validity
return count == 1;
}
#ifdef SPATIALITE_VERSION_GE_4_0_0
// only if libspatialite version is >= 4.0.0
bool QgsSpatiaLiteProvider::getGeometryDetailsAbstractInterface( gaiaVectorLayerPtr lyr )
{
if ( !lyr )
{
return false;
}
mIndexTable = mTableName;
mIndexGeometry = mGeometryColumn;
switch ( lyr->GeometryType )
{
case GAIA_VECTOR_POINT:
mGeomType = QgsWkbTypes::Point;
break;
case GAIA_VECTOR_LINESTRING:
mGeomType = QgsWkbTypes::LineString;
break;
case GAIA_VECTOR_POLYGON:
mGeomType = QgsWkbTypes::Polygon;
break;
case GAIA_VECTOR_MULTIPOINT:
mGeomType = QgsWkbTypes::MultiPoint;
break;
case GAIA_VECTOR_MULTILINESTRING:
mGeomType = QgsWkbTypes::MultiLineString;
break;
case GAIA_VECTOR_MULTIPOLYGON:
mGeomType = QgsWkbTypes::MultiPolygon;
break;
default:
mGeomType = QgsWkbTypes::Unknown;
break;
}
mSrid = lyr->Srid;
if ( lyr->SpatialIndex == GAIA_SPATIAL_INDEX_RTREE )
{
mSpatialIndexRTree = true;
}
if ( lyr->SpatialIndex == GAIA_SPATIAL_INDEX_MBRCACHE )
{
mSpatialIndexMbrCache = true;
}
switch ( lyr->Dimensions )
{
case GAIA_XY:
nDims = GAIA_XY;
break;
case GAIA_XY_Z:
nDims = GAIA_XY_Z;
break;
case GAIA_XY_M:
nDims = GAIA_XY_M;
break;
case GAIA_XY_Z_M:
nDims = GAIA_XY_Z_M;
break;
}
if ( mViewBased && mSpatialIndexRTree )
getViewSpatialIndexName();
return getSridDetails();
}
void QgsSpatiaLiteProvider::getViewSpatialIndexName()
{
int ret;
int i;
char **results;
int rows;
int columns;
char *errMsg = nullptr;
// retrieving the Spatial Index name supporting this View (if any)
mSpatialIndexRTree = false;
QString sql = QString( "SELECT f_table_name, f_geometry_column "
"FROM views_geometry_columns "
"WHERE upper(view_name) = upper(%1) and upper(view_geometry) = upper(%2)" ).arg( quotedValue( mTableName ),
quotedValue( mGeometryColumn ) );
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( ret != SQLITE_OK )
goto error;
if ( rows < 1 )
;
else
{
for ( i = 1; i <= rows; i++ )
{
mIndexTable = results[( i * columns ) + 0];
mIndexGeometry = results[( i * columns ) + 1];
mSpatialIndexRTree = true;
}
}
sqlite3_free_table( results );
return;
error:
// unexpected error
if ( errMsg )
{
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errMsg ), tr( "SpatiaLite" ) );
sqlite3_free( errMsg );
}
}
#endif
bool QgsSpatiaLiteProvider::getGeometryDetails()
{
bool ret = false;
if ( mGeometryColumn.isEmpty() )
{
mGeomType = QgsWkbTypes::NoGeometry;
return true;
}
if ( mTableBased )
ret = getTableGeometryDetails();
if ( mViewBased )
ret = getViewGeometryDetails();
if ( mVShapeBased )
ret = getVShapeGeometryDetails();
if ( mIsQuery )
ret = getQueryGeometryDetails();
return ret;
}
bool QgsSpatiaLiteProvider::getTableGeometryDetails()
{
int ret;
int i;
char **results;
int rows;
int columns;
char *errMsg = nullptr;
mIndexTable = mTableName;
mIndexGeometry = mGeometryColumn;
QString 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( quotedValue( mTableName ),
quotedValue( mGeometryColumn ) );
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( ret != SQLITE_OK )
goto error;
if ( rows < 1 )
;
else
{
for ( i = 1; i <= rows; i++ )
{
QString fType = results[( i * columns ) + 0];
QString xSrid = results[( i * columns ) + 1];
QString spatialIndex = results[( i * columns ) + 2];
QString dims = results[( i * columns ) + 3];
if ( fType == QLatin1String( "POINT" ) )
{
mGeomType = QgsWkbTypes::Point;
}
else if ( fType == QLatin1String( "MULTIPOINT" ) )
{
mGeomType = QgsWkbTypes::MultiPoint;
}
else if ( fType == QLatin1String( "LINESTRING" ) )
{
mGeomType = QgsWkbTypes::LineString;
}
else if ( fType == QLatin1String( "MULTILINESTRING" ) )
{
mGeomType = QgsWkbTypes::MultiLineString;
}
else if ( fType == QLatin1String( "POLYGON" ) )
{
mGeomType = QgsWkbTypes::Polygon;
}
else if ( fType == QLatin1String( "MULTIPOLYGON" ) )
{
mGeomType = QgsWkbTypes::MultiPolygon;
}
mSrid = xSrid.toInt();
if ( spatialIndex.toInt() == 1 )
{
mSpatialIndexRTree = true;
}
if ( spatialIndex.toInt() == 2 )
{
mSpatialIndexMbrCache = true;
}
if ( dims == QLatin1String( "XY" ) || dims == QLatin1String( "2" ) )
{
nDims = GAIA_XY;
}
else if ( dims == QLatin1String( "XYZ" ) || dims == QLatin1String( "3" ) )
{
nDims = GAIA_XY_Z;
}
else if ( dims == QLatin1String( "XYM" ) )
{
nDims = GAIA_XY_M;
}
else if ( dims == QLatin1String( "XYZM" ) )
{
nDims = GAIA_XY_Z_M;
}
}
}
sqlite3_free_table( results );
if ( mGeomType == QgsWkbTypes::Unknown || mSrid < 0 )
goto error;
return getSridDetails();
error:
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errMsg ? errMsg : tr( "unknown cause" ) ), tr( "SpatiaLite" ) );
// unexpected error
if ( errMsg )
{
sqlite3_free( errMsg );
}
return false;
}
bool QgsSpatiaLiteProvider::getViewGeometryDetails()
{
int ret;
int i;
char **results;
int rows;
int columns;
char *errMsg = nullptr;
QString sql = QString( "SELECT type, srid, spatial_index_enabled, f_table_name, f_geometry_column "
" FROM views_geometry_columns"
" JOIN geometry_columns USING (f_table_name, f_geometry_column)"
" WHERE upper(view_name) = upper(%1) and upper(view_geometry) = upper(%2)" ).arg( quotedValue( mTableName ),
quotedValue( mGeometryColumn ) );
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( ret != SQLITE_OK )
goto error;
if ( rows < 1 )
;
else
{
for ( i = 1; i <= rows; i++ )
{
QString fType = results[( i * columns ) + 0];
QString xSrid = results[( i * columns ) + 1];
QString spatialIndex = results[( i * columns ) + 2];
mIndexTable = results[( i * columns ) + 3];
mIndexGeometry = results[( i * columns ) + 4];
if ( fType == QLatin1String( "POINT" ) )
{
mGeomType = QgsWkbTypes::Point;
}
else if ( fType == QLatin1String( "MULTIPOINT" ) )
{
mGeomType = QgsWkbTypes::MultiPoint;
}
else if ( fType == QLatin1String( "LINESTRING" ) )
{
mGeomType = QgsWkbTypes::LineString;
}
else if ( fType == QLatin1String( "MULTILINESTRING" ) )
{
mGeomType = QgsWkbTypes::MultiLineString;
}
else if ( fType == QLatin1String( "POLYGON" ) )
{
mGeomType = QgsWkbTypes::Polygon;
}
else if ( fType == QLatin1String( "MULTIPOLYGON" ) )
{
mGeomType = QgsWkbTypes::MultiPolygon;
}
mSrid = xSrid.toInt();
if ( spatialIndex.toInt() == 1 )
{
mSpatialIndexRTree = true;
}
if ( spatialIndex.toInt() == 2 )
{
mSpatialIndexMbrCache = true;
}
}
}
sqlite3_free_table( results );
if ( mGeomType == QgsWkbTypes::Unknown || mSrid < 0 )
goto error;
return getSridDetails();
error:
// unexpected error
if ( errMsg )
{
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errMsg ), tr( "SpatiaLite" ) );
sqlite3_free( errMsg );
}
return false;
}
bool QgsSpatiaLiteProvider::getVShapeGeometryDetails()
{
int ret;
int i;
char **results;
int rows;
int columns;
char *errMsg = nullptr;
QString sql = QString( "SELECT type, srid FROM virts_geometry_columns"
" WHERE virt_name=%1 and virt_geometry=%2" ).arg( quotedValue( mTableName ),
quotedValue( mGeometryColumn ) );
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( ret != SQLITE_OK )
goto error;
if ( rows < 1 )
;
else
{
for ( i = 1; i <= rows; i++ )
{
QString fType = results[( i * columns ) + 0];
QString xSrid = results[( i * columns ) + 1];
if ( fType == QLatin1String( "POINT" ) )
{
mGeomType = QgsWkbTypes::Point;
}
else if ( fType == QLatin1String( "MULTIPOINT" ) )
{
mGeomType = QgsWkbTypes::MultiPoint;
}
else if ( fType == QLatin1String( "LINESTRING" ) )
{
mGeomType = QgsWkbTypes::LineString;
}
else if ( fType == QLatin1String( "MULTILINESTRING" ) )
{
mGeomType = QgsWkbTypes::MultiLineString;
}
else if ( fType == QLatin1String( "POLYGON" ) )
{
mGeomType = QgsWkbTypes::Polygon;
}
else if ( fType == QLatin1String( "MULTIPOLYGON" ) )
{
mGeomType = QgsWkbTypes::MultiPolygon;
}
mSrid = xSrid.toInt();
}
}
sqlite3_free_table( results );
if ( mGeomType == QgsWkbTypes::Unknown || mSrid < 0 )
goto error;
return getSridDetails();
error:
// unexpected error
if ( errMsg )
{
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errMsg ), tr( "SpatiaLite" ) );
sqlite3_free( errMsg );
}
return false;
}
bool QgsSpatiaLiteProvider::getQueryGeometryDetails()
{
int ret;
int i;
char **results;
int rows;
int columns;
char *errMsg = nullptr;
QString fType( QLatin1String( "" ) );
QString xSrid( QLatin1String( "" ) );
// 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" )
.arg( quotedIdentifier( mGeometryColumn ),
mQuery );
//it is possible that the where clause restricts the feature type
if ( !mSubsetString.isEmpty() )
{
sql += " WHERE " + mSubsetString;
}
sql += QLatin1String( " limit 1" );
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( ret != SQLITE_OK )
goto error;
if ( rows < 1 )
;
else
{
for ( i = 1; i <= rows; i++ )
{
xSrid = results[( i * columns ) + 0];
fType = results[( i * columns ) + 1];
}
}
sqlite3_free_table( results );
if ( !xSrid.isEmpty() && !fType.isEmpty() )
{
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" )
.arg( quotedIdentifier( mGeometryColumn ),
mQuery );
if ( !mSubsetString.isEmpty() )
sql += " where " + mSubsetString;
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( ret != SQLITE_OK )
goto error;
if ( rows != 1 )
;
else
{
for ( i = 1; i <= rows; i++ )
{
fType = results[( 1 * columns ) + 0];
}
}
sqlite3_free_table( results );
}
if ( fType == QLatin1String( "POINT" ) )
{
mGeomType = QgsWkbTypes::Point;
}
else if ( fType == QLatin1String( "MULTIPOINT" ) )
{
mGeomType = QgsWkbTypes::MultiPoint;
}
else if ( fType == QLatin1String( "LINESTRING" ) )
{
mGeomType = QgsWkbTypes::LineString;
}
else if ( fType == QLatin1String( "MULTILINESTRING" ) )
{
mGeomType = QgsWkbTypes::MultiLineString;
}
else if ( fType == QLatin1String( "POLYGON" ) )
{
mGeomType = QgsWkbTypes::Polygon;
}
else if ( fType == QLatin1String( "MULTIPOLYGON" ) )
{
mGeomType = QgsWkbTypes::MultiPolygon;
}
mSrid = xSrid.toInt();
}
if ( mGeomType == QgsWkbTypes::Unknown || mSrid < 0 )
goto error;
return getSridDetails();
error:
// unexpected error
if ( errMsg )
{
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errMsg ), tr( "SpatiaLite" ) );
sqlite3_free( errMsg );
}
return false;
}
bool QgsSpatiaLiteProvider::getSridDetails()
{
int ret;
int i;
char **results;
int rows;
int columns;
char *errMsg = nullptr;
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 );
if ( ret != SQLITE_OK )
goto error;
if ( rows < 1 )
;
else
{
for ( i = 1; i <= rows; i++ )
{
mAuthId = results[( i * columns ) + 0];
mProj4text = results[( i * columns ) + 1];
}
}
sqlite3_free_table( results );
return true;
error:
// unexpected error
if ( errMsg )
{
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errMsg ), tr( "SpatiaLite" ) );
sqlite3_free( errMsg );
}
return false;
}
#ifdef SPATIALITE_VERSION_GE_4_0_0
// only if libspatialite version is >= 4.0.0
bool QgsSpatiaLiteProvider::getTableSummaryAbstractInterface( gaiaVectorLayerPtr lyr )
{
if ( !lyr )
return false;
if ( lyr->ExtentInfos )
{
mLayerExtent.set( lyr->ExtentInfos->MinX, lyr->ExtentInfos->MinY,
lyr->ExtentInfos->MaxX, lyr->ExtentInfos->MaxY );
mNumberFeatures = lyr->ExtentInfos->Count;
}
else
{
mLayerExtent.setMinimal();
mNumberFeatures = 0;
}
return true;
}
#endif
bool QgsSpatiaLiteProvider::getTableSummary()
{
int ret;
int i;
char **results;
int rows;
int columns;
char *errMsg = nullptr;
QString sql = QStringLiteral( "SELECT Count(*)%1 FROM %2" )
.arg( mGeometryColumn.isEmpty() ? QLatin1String( "" ) : QStringLiteral( ",Min(MbrMinX(%1)),Min(MbrMinY(%1)),Max(MbrMaxX(%1)),Max(MbrMaxY(%1))" ).arg( quotedIdentifier( mGeometryColumn ) ),
mQuery );
if ( !mSubsetString.isEmpty() )
{
sql += " WHERE ( " + mSubsetString + ')';
}
ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( ret != SQLITE_OK )
goto error;
if ( rows < 1 )
;
else
{
for ( i = 1; i <= rows; i++ )
{
QString count = results[( i * columns ) + 0];
mNumberFeatures = count.toLong();
if ( mGeometryColumn.isEmpty() )
{
mLayerExtent.setMinimal();
}
else
{
QString minX = results[( i * columns ) + 1];
QString minY = results[( i * columns ) + 2];
QString maxX = results[( i * columns ) + 3];
QString maxY = results[( i * columns ) + 4];
mLayerExtent.set( minX.toDouble(), minY.toDouble(), maxX.toDouble(), maxY.toDouble() );
}
}
}
sqlite3_free_table( results );
return true;
error:
// unexpected error
if ( errMsg )
{
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errMsg ), tr( "SpatiaLite" ) );
sqlite3_free( errMsg );
}
return false;
}
QgsField QgsSpatiaLiteProvider::field( int index ) const
{
if ( index < 0 || index >= mAttributeFields.count() )
{
QgsMessageLog::logMessage( tr( "FAILURE: Field %1 not found." ).arg( index ), tr( "SpatiaLite" ) );
throw SLFieldNotFound();
}
return mAttributeFields.at( index );
}
void QgsSpatiaLiteProvider::invalidateConnections( const QString& connection )
{
QgsSpatiaLiteConnPool::instance()->invalidateConnections( connection );
}
/**
* Class factory to return a pointer to a newly created
* QgsSpatiaLiteProvider object
*/
QGISEXTERN QgsSpatiaLiteProvider *classFactory( const QString * uri )
{
return new QgsSpatiaLiteProvider( *uri );
}
/** Required key function (used to map the plugin to a data store type)
*/
QGISEXTERN QString providerKey()
{
return SPATIALITE_KEY;
}
/**
* Required description function
*/
QGISEXTERN QString description()
{
return SPATIALITE_DESCRIPTION;
}
/**
* Required isProvider function. Used to determine if this shared library
* is a data provider plugin
*/
QGISEXTERN bool isProvider()
{
return true;
}
QGISEXTERN QgsVectorLayerImport::ImportError createEmptyLayer(
const QString& uri,
const QgsFields &fields,
QgsWkbTypes::Type wkbType,
const QgsCoordinateReferenceSystem& srs,
bool overwrite,
QMap<int, int> *oldToNewAttrIdxMap,
QString *errorMessage,
const QMap<QString, QVariant> *options )
{
return QgsSpatiaLiteProvider::createEmptyLayer(
uri, fields, wkbType, srs, overwrite,
oldToNewAttrIdxMap, errorMessage, options
);
}
// -------------
static bool initializeSpatialMetadata( sqlite3 *sqlite_handle, QString& errCause )
{
// attempting to perform self-initialization for a newly created DB
if ( !sqlite_handle )
return false;
// checking if this DB is really empty
char **results;
int rows, columns;
int ret = sqlite3_get_table( sqlite_handle, "select count(*) from sqlite_master", &results, &rows, &columns, nullptr );
if ( ret != SQLITE_OK )
return false;
int count = 0;
if ( rows >= 1 )
{
for ( int i = 1; i <= rows; i++ )
count = atoi( results[( i * columns ) + 0] );
}
sqlite3_free_table( results );
if ( count > 0 )
return false;
bool above41 = false;
ret = sqlite3_get_table( sqlite_handle, "select spatialite_version()", &results, &rows, &columns, nullptr );
if ( ret == SQLITE_OK && rows == 1 && columns == 1 )
{
QString version = QString::fromUtf8( results[1] );
QStringList parts = version.split( ' ', QString::SkipEmptyParts );
if ( parts.size() >= 1 )
{
QStringList verparts = parts[0].split( '.', QString::SkipEmptyParts );
above41 = verparts.size() >= 2 && ( verparts[0].toInt() > 4 || ( verparts[0].toInt() == 4 && verparts[1].toInt() >= 1 ) );
}
}
sqlite3_free_table( results );
// all right, it's empty: proceding to initialize
char *errMsg = nullptr;
ret = sqlite3_exec( sqlite_handle, above41 ? "SELECT InitSpatialMetadata(1)" : "SELECT InitSpatialMetadata()", nullptr, nullptr, &errMsg );
if ( ret != SQLITE_OK )
{
errCause = QObject::tr( "Unable to initialize SpatialMetadata:\n" );
errCause += QString::fromUtf8( errMsg );
sqlite3_free( errMsg );
return false;
}
spatial_ref_sys_init( sqlite_handle, 0 );
return true;
}
QGISEXTERN bool createDb( const QString& dbPath, QString& errCause )
{
QgsDebugMsg( "creating a new db" );
QFileInfo fullPath = QFileInfo( dbPath );
QDir path = fullPath.dir();
QgsDebugMsg( QString( "making this dir: %1" ).arg( path.absolutePath() ) );
// Must be sure there is destination directory ~/.qgis
QDir().mkpath( path.absolutePath() );
// creating/opening the new database
sqlite3 *sqlite_handle;
int ret = QgsSLConnect::sqlite3_open_v2( dbPath.toUtf8().constData(), &sqlite_handle, 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 );
return false;
}
// activating Foreign Key constraints
char *errMsg = nullptr;
ret = sqlite3_exec( sqlite_handle, "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 );
return init_res;
}
// -------------
QGISEXTERN bool deleteLayer( const QString& dbPath, const QString& tableName, QString& errCause )
{
QgsDebugMsg( "deleting layer " + tableName );
QgsSqliteHandle* hndl = QgsSqliteHandle::openDb( dbPath );
if ( !hndl )
{
errCause = QObject::tr( "Connection to database failed" );
return false;
}
sqlite3* sqlite_handle = hndl->handle();
int ret;
#ifdef SPATIALITE_VERSION_GE_4_0_0
// only if libspatialite version is >= 4.0.0
// if libspatialite is v.4.0 (or higher) using the internal library
// method is highly recommended
if ( !gaiaDropTable( sqlite_handle, tableName.toUtf8().constData() ) )
{
// unexpected error
errCause = QObject::tr( "Unable to delete table %1\n" ).arg( tableName );
QgsSqliteHandle::closeDb( hndl );
return false;
}
#else
// drop the table
QString sql = QString( "DROP TABLE " ) + QgsSpatiaLiteProvider::quotedIdentifier( tableName );
QgsDebugMsg( sql );
char *errMsg = nullptr;
ret = sqlite3_exec( sqlite_handle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg );
if ( ret != SQLITE_OK )
{
errCause = QObject::tr( "Unable to delete table %1:\n" ).arg( tableName );
errCause += QString::fromUtf8( errMsg );
sqlite3_free( errMsg );
QgsSqliteHandle::closeDb( hndl );
return false;
}
// remove table from geometry columns
sql = QString( "DELETE FROM geometry_columns WHERE upper(f_table_name) = upper(%1)" )
.arg( QgsSpatiaLiteProvider::quotedValue( tableName ) );
ret = sqlite3_exec( sqlite_handle, sql.toUtf8().constData(), nullptr, nullptr, nullptr );
if ( ret != SQLITE_OK )
{
QgsDebugMsg( "sqlite error: " + QString::fromUtf8( sqlite3_errmsg( sqlite_handle ) ) );
}
#endif
// TODO: remove spatial indexes?
// run VACUUM to free unused space and compact the database
ret = sqlite3_exec( sqlite_handle, "VACUUM", nullptr, nullptr, nullptr );
if ( ret != SQLITE_OK )
{
QgsDebugMsg( "Failed to run VACUUM after deleting table on database " + dbPath );
}
QgsSqliteHandle::closeDb( hndl );
return true;
}
QgsAttributeList QgsSpatiaLiteProvider::pkAttributeIndexes() const
{
return mPrimaryKeyAttrs;
}
QList<QgsVectorLayer*> QgsSpatiaLiteProvider::searchLayers( const QList<QgsVectorLayer*>& layers, const QString& connectionInfo, const QString& tableName )
{
QList<QgsVectorLayer*> result;
Q_FOREACH ( QgsVectorLayer* layer, layers )
{
const QgsSpatiaLiteProvider* slProvider = qobject_cast<QgsSpatiaLiteProvider*>( layer->dataProvider() );
if ( slProvider && slProvider->mSqlitePath == connectionInfo && slProvider->mTableName == tableName )
{
result.append( layer );
}
}
return result;
}
QList<QgsRelation> QgsSpatiaLiteProvider::discoverRelations( const QgsVectorLayer* self, const QList<QgsVectorLayer*>& layers ) const
{
QList<QgsRelation> output;
const QString sql = QStringLiteral( "PRAGMA foreign_key_list(%1)" ).arg( QgsSpatiaLiteProvider::quotedIdentifier( mTableName ) );
char **results;
int rows;
int columns;
char *errMsg = nullptr;
int ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( ret == SQLITE_OK )
{
int nbFound = 0;
for ( int row = 1; row <= rows; ++row )
{
const QString name = "fk_" + mTableName + "_" + QString::fromUtf8( results[row * columns + 0] );
const QString position = QString::fromUtf8( results[row * columns + 1] );
const QString refTable = QString::fromUtf8( results[row * columns + 2] );
const QString fkColumn = QString::fromUtf8( results[row * columns + 3] );
const QString refColumn = QString::fromUtf8( results[row * columns + 4] );
if ( position == QLatin1String( "0" ) )
{ // first reference field => try to find if we have layers for the referenced table
const QList<QgsVectorLayer*> foundLayers = searchLayers( layers, mSqlitePath, refTable );
Q_FOREACH ( const QgsVectorLayer* foundLayer, foundLayers )
{
QgsRelation relation;
relation.setRelationName( name );
relation.setReferencingLayer( self->id() );
relation.setReferencedLayer( foundLayer->id() );
relation.addFieldPair( fkColumn, refColumn );
relation.generateId();
if ( relation.isValid() )
{
output.append( relation );
++nbFound;
}
else
{
QgsLogger::warning( "Invalid relation for " + name );
}
}
}
else
{ // multi reference field => add the field pair to all the referenced layers found
for ( int i = 0; i < nbFound; ++i )
{
output[output.size() - 1 - i].addFieldPair( fkColumn, refColumn );
}
}
}
sqlite3_free_table( results );
}
else
{
QgsLogger::warning( QStringLiteral( "SQLite error discovering relations: %1" ).arg( errMsg ) );
sqlite3_free( errMsg );
}
return output;
}
// ---------------------------------------------------------------------------
QGISEXTERN bool saveStyle( const QString& uri, const QString& qmlStyle, const QString& sldStyle,
const QString& styleName, const QString& styleDescription,
const QString& uiFileContent, bool useAsDefault, QString& errCause )
{
QgsDataSourceUri dsUri( uri );
QString sqlitePath = dsUri.database();
QgsDebugMsg( "Database is: " + sqlitePath );
// trying to open the SQLite DB
QgsSqliteHandle *handle = QgsSqliteHandle::openDb( sqlitePath );
if ( !handle )
{
QgsDebugMsg( "Connection to database failed. Save style aborted." );
errCause = QObject::tr( "Connection to database failed" );
return false;
}
sqlite3 *sqliteHandle = handle->handle();
// check if layer_styles table already exist
QString countIfExist = QStringLiteral( "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='%1';" ).arg( QStringLiteral( "layer_styles" ) );
char **results;
int rows;
int columns;
char *errMsg = nullptr;
int ret = sqlite3_get_table( sqliteHandle, countIfExist.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( SQLITE_OK != ret )
{
QgsSqliteHandle::closeDb( handle );
QgsMessageLog::logMessage( QObject::tr( "Error executing query: %1" ).arg( countIfExist ) );
errCause = QObject::tr( "Error looking for style. The query was logged" );
return false;
}
// if not table exist... create is
int howMany = 0;
if ( 1 == rows )
{
howMany = atoi( results[( rows * columns ) + 0 ] );
}
sqlite3_free_table( results );
// create table if not exist
if ( 0 == howMany )
{
QString createQuery = QString( "CREATE TABLE layer_styles("
"id INTEGER PRIMARY KEY AUTOINCREMENT"
",f_table_catalog varchar(256)"
",f_table_schema varchar(256)"
",f_table_name varchar(256)"
",f_geometry_column varchar(256)"
",styleName varchar(30)"
",styleQML text"
",styleSLD text"
",useAsDefault boolean"
",description text"
",owner varchar(30)"
",ui text"
",update_time timestamp DEFAULT CURRENT_TIMESTAMP"
")" );
ret = sqlite3_exec( sqliteHandle, createQuery.toUtf8().constData(), nullptr, nullptr, &errMsg );
if ( SQLITE_OK != ret )
{
QgsSqliteHandle::closeDb( handle );
errCause = QObject::tr( "Unable to save layer style. It's not possible to create the destination table on the database." );
return false;
}
}
QString uiFileColumn;
QString uiFileValue;
if ( !uiFileContent.isEmpty() )
{
uiFileColumn = QStringLiteral( ",ui" );
uiFileValue = QStringLiteral( ",%1" ).arg( QgsSpatiaLiteProvider::quotedValue( uiFileContent ) );
}
QString sql = QString( "INSERT INTO layer_styles("
"f_table_catalog,f_table_schema,f_table_name,f_geometry_column,styleName,styleQML,styleSLD,useAsDefault,description,owner%11"
") VALUES ("
"%1,%2,%3,%4,%5,%6,%7,%8,%9,%10%12"
")" )
.arg( QgsSpatiaLiteProvider::quotedValue( QString() ) )
.arg( QgsSpatiaLiteProvider::quotedValue( dsUri.schema() ) )
.arg( QgsSpatiaLiteProvider::quotedValue( dsUri.table() ) )
.arg( QgsSpatiaLiteProvider::quotedValue( dsUri.geometryColumn() ) )
.arg( QgsSpatiaLiteProvider::quotedValue( styleName.isEmpty() ? dsUri.table() : styleName ) )
.arg( QgsSpatiaLiteProvider::quotedValue( qmlStyle ) )
.arg( QgsSpatiaLiteProvider::quotedValue( sldStyle ) )
.arg( useAsDefault ? "1" : "0" )
.arg( QgsSpatiaLiteProvider::quotedValue( styleDescription.isEmpty() ? QDateTime::currentDateTime().toString() : styleDescription ) )
.arg( QgsSpatiaLiteProvider::quotedValue( dsUri.username() ) )
.arg( uiFileColumn )
.arg( uiFileValue );
QString checkQuery = QString( "SELECT styleName"
" FROM layer_styles"
" WHERE f_table_schema=%1"
" AND f_table_name=%2"
" AND f_geometry_column=%3"
" AND styleName=%4" )
.arg( QgsSpatiaLiteProvider::quotedValue( dsUri.schema() ) )
.arg( QgsSpatiaLiteProvider::quotedValue( dsUri.table() ) )
.arg( QgsSpatiaLiteProvider::quotedValue( dsUri.geometryColumn() ) )
.arg( QgsSpatiaLiteProvider::quotedValue( styleName.isEmpty() ? dsUri.table() : styleName ) );
ret = sqlite3_get_table( sqliteHandle, checkQuery.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( SQLITE_OK != ret )
{
QgsSqliteHandle::closeDb( handle );
QgsMessageLog::logMessage( QObject::tr( "Error executing query: %1" ).arg( checkQuery ) );
errCause = QObject::tr( "Error looking for style. The query was logged" );
return false;
}
if ( 0 != rows )
{
sqlite3_free_table( results );
if ( QMessageBox::question( nullptr, QObject::tr( "Save style in database" ),
QObject::tr( "A style named \"%1\" already exists in the database for this layer. Do you want to overwrite it?" )
.arg( styleName.isEmpty() ? dsUri.table() : styleName ),
QMessageBox::Yes | QMessageBox::No ) == QMessageBox::No )
{
QgsSqliteHandle::closeDb( handle );
errCause = QObject::tr( "Operation aborted" );
return false;
}
sql = QString( "UPDATE layer_styles"
" SET useAsDefault=%1"
",styleQML=%2"
",styleSLD=%3"
",description=%4"
",owner=%5"
" WHERE f_table_schema=%6"
" AND f_table_name=%7"
" AND f_geometry_column=%8"
" AND styleName=%9" )
.arg( useAsDefault ? "1" : "0" )
.arg( QgsSpatiaLiteProvider::quotedValue( qmlStyle ) )
.arg( QgsSpatiaLiteProvider::quotedValue( sldStyle ) )
.arg( QgsSpatiaLiteProvider::quotedValue( styleDescription.isEmpty() ? QDateTime::currentDateTime().toString() : styleDescription ) )
.arg( QgsSpatiaLiteProvider::quotedValue( dsUri.username() ) )
.arg( QgsSpatiaLiteProvider::quotedValue( dsUri.schema() ) )
.arg( QgsSpatiaLiteProvider::quotedValue( dsUri.table() ) )
.arg( QgsSpatiaLiteProvider::quotedValue( dsUri.geometryColumn() ) )
.arg( QgsSpatiaLiteProvider::quotedValue( styleName.isEmpty() ? dsUri.table() : styleName ) );
}
if ( useAsDefault )
{
QString removeDefaultSql = QString( "UPDATE layer_styles"
" SET useAsDefault=0"
" WHERE f_table_schema=%1"
" AND f_table_name=%2"
" AND f_geometry_column=%3" )
.arg( QgsSpatiaLiteProvider::quotedValue( dsUri.schema() ) )
.arg( QgsSpatiaLiteProvider::quotedValue( dsUri.table() ) )
.arg( QgsSpatiaLiteProvider::quotedValue( dsUri.geometryColumn() ) );
sql = QStringLiteral( "BEGIN; %1; %2; COMMIT;" ).arg( removeDefaultSql, sql );
}
ret = sqlite3_exec( sqliteHandle, sql.toUtf8().constData(), nullptr, nullptr, &errMsg );
if ( SQLITE_OK != ret )
{
QgsSqliteHandle::closeDb( handle );
QgsMessageLog::logMessage( QObject::tr( "Error executing query: %1" ).arg( sql ) );
errCause = QObject::tr( "Error looking for style. The query was logged" );
return false;
}
if ( errMsg )
sqlite3_free( errMsg );
QgsSqliteHandle::closeDb( handle );
return true;
}
QGISEXTERN QString loadStyle( const QString& uri, QString& errCause )
{
QgsDataSourceUri dsUri( uri );
QString sqlitePath = dsUri.database();
QgsDebugMsg( "Database is: " + sqlitePath );
// trying to open the SQLite DB
QgsSqliteHandle *handle = QgsSqliteHandle::openDb( sqlitePath );
if ( !handle )
{
QgsDebugMsg( "Connection to database failed. Save style aborted." );
errCause = QObject::tr( "Connection to database failed" );
return QLatin1String( "" );
}
sqlite3 *sqliteHandle = handle->handle();
QString selectQmlQuery = QString( "SELECT styleQML"
" FROM layer_styles"
" WHERE f_table_schema=%1"
" AND f_table_name=%2"
" AND f_geometry_column=%3"
" ORDER BY CASE WHEN useAsDefault THEN 1 ELSE 2 END"
",update_time DESC LIMIT 1" )
.arg( QgsSpatiaLiteProvider::quotedValue( dsUri.schema() ) )
.arg( QgsSpatiaLiteProvider::quotedValue( dsUri.table() ) )
.arg( QgsSpatiaLiteProvider::quotedValue( dsUri.geometryColumn() ) );
char **results;
int rows;
int columns;
char *errMsg = nullptr;
int ret = sqlite3_get_table( sqliteHandle, selectQmlQuery.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( SQLITE_OK != ret )
{
QgsSqliteHandle::closeDb( handle );
QgsMessageLog::logMessage( QObject::tr( "Error executing query: %1" ).arg( selectQmlQuery ) );
errCause = QObject::tr( "Error executing loading style. The query was logged" );
return QLatin1String( "" );
}
QString style = ( rows == 1 ) ? QString::fromUtf8( results[( rows * columns ) + 0 ] ) : QLatin1String( "" );
sqlite3_free_table( results );
QgsSqliteHandle::closeDb( handle );
return style;
}
QGISEXTERN int listStyles( const QString &uri, QStringList &ids, QStringList &names,
QStringList &descriptions, QString& errCause )
{
QgsDataSourceUri dsUri( uri );
QString sqlitePath = dsUri.database();
QgsDebugMsg( "Database is: " + sqlitePath );
// trying to open the SQLite DB
QgsSqliteHandle *handle = QgsSqliteHandle::openDb( sqlitePath );
if ( !handle )
{
QgsDebugMsg( "Connection to database failed. Save style aborted." );
errCause = QObject::tr( "Connection to database failed" );
return -1;
}
sqlite3 *sqliteHandle = handle->handle();
// check if layer_styles table already exist
QString countIfExist = QStringLiteral( "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='%1';" ).arg( QStringLiteral( "layer_styles" ) );
char **results;
int rows;
int columns;
char *errMsg = nullptr;
int ret = sqlite3_get_table( sqliteHandle, countIfExist.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( SQLITE_OK != ret )
{
QgsSqliteHandle::closeDb( handle );
QgsMessageLog::logMessage( QObject::tr( "Error executing query: %1" ).arg( countIfExist ) );
errCause = QObject::tr( "Error looking for style. The query was logged" );
return -1;
}
int howMany = 0;
if ( 1 == rows )
{
howMany = atoi( results[( rows * columns ) + 0 ] );
}
sqlite3_free_table( results );
if ( 0 == howMany )
{
QgsSqliteHandle::closeDb( handle );
QgsMessageLog::logMessage( QObject::tr( "No styles available on DB" ) );
errCause = QObject::tr( "No styles available on DB" );
return false;
}
// get them
QString selectRelatedQuery = QString( "SELECT id,styleName,description"
" FROM layer_styles"
" WHERE f_table_schema=%1"
" AND f_table_name=%2"
" AND f_geometry_column=%3" )
.arg( QgsSpatiaLiteProvider::quotedValue( dsUri.schema() ) )
.arg( QgsSpatiaLiteProvider::quotedValue( dsUri.table() ) )
.arg( QgsSpatiaLiteProvider::quotedValue( dsUri.geometryColumn() ) );
ret = sqlite3_get_table( sqliteHandle, selectRelatedQuery.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( SQLITE_OK != ret )
{
QgsSqliteHandle::closeDb( handle );
QgsMessageLog::logMessage( QObject::tr( "Error executing query: %1" ).arg( selectRelatedQuery ) );
errCause = QObject::tr( "Error loading styles. The query was logged" );
return -1;
}
int numberOfRelatedStyles = rows;
for ( int i = 1; i <= rows; i++ )
{
ids.append( results[( i * columns ) + 0 ] );
names.append( QString::fromUtf8( results[( i * columns ) + 1 ] ) );
descriptions.append( QString::fromUtf8( results[( i * columns ) + 2 ] ) );
}
sqlite3_free_table( results );
QString selectOthersQuery = QString( "SELECT id,styleName,description"
" FROM layer_styles"
" WHERE NOT (f_table_schema=%1 AND f_table_name=%2 AND f_geometry_column=%3)"
" ORDER BY update_time DESC" )
.arg( QgsSpatiaLiteProvider::quotedValue( dsUri.schema() ) )
.arg( QgsSpatiaLiteProvider::quotedValue( dsUri.table() ) )
.arg( QgsSpatiaLiteProvider::quotedValue( dsUri.geometryColumn() ) );
ret = sqlite3_get_table( sqliteHandle, selectOthersQuery.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( SQLITE_OK != ret )
{
QgsSqliteHandle::closeDb( handle );
QgsMessageLog::logMessage( QObject::tr( "Error executing query: %1" ).arg( selectOthersQuery ) );
errCause = QObject::tr( "Error executing the select query for unrelated styles. The query was logged" );
return -1;
}
for ( int i = 1; i <= rows; i++ )
{
ids.append( results[( i * columns ) + 0 ] );
names.append( QString::fromUtf8( results[( i * columns ) + 1 ] ) );
descriptions.append( QString::fromUtf8( results[( i * columns ) + 2 ] ) );
}
sqlite3_free_table( results );
QgsSqliteHandle::closeDb( handle );
return numberOfRelatedStyles;
}
QGISEXTERN QString getStyleById( const QString& uri, QString styleId, QString& errCause )
{
QgsDataSourceUri dsUri( uri );
QString sqlitePath = dsUri.database();
QgsDebugMsg( "Database is: " + sqlitePath );
// trying to open the SQLite DB
QgsSqliteHandle *handle = QgsSqliteHandle::openDb( sqlitePath );
if ( !handle )
{
QgsDebugMsg( "Connection to database failed. Save style aborted." );
errCause = QObject::tr( "Connection to database failed" );
return QLatin1String( "" );
}
sqlite3 *sqliteHandle = handle->handle();
QString style;
QString selectQmlQuery = QStringLiteral( "SELECT styleQml FROM layer_styles WHERE id=%1" ).arg( QgsSpatiaLiteProvider::quotedValue( styleId ) );
char **results;
int rows;
int columns;
char *errMsg = nullptr;
int ret = sqlite3_get_table( sqliteHandle, selectQmlQuery.toUtf8().constData(), &results, &rows, &columns, &errMsg );
if ( SQLITE_OK == ret )
{
if ( 1 == rows )
style = QString::fromUtf8( results[( rows * columns ) + 0 ] );
else
errCause = QObject::tr( "Consistency error in table '%1'. Style id should be unique" ).arg( QStringLiteral( "layer_styles" ) );
}
else
{
QgsMessageLog::logMessage( QObject::tr( "Error executing query: %1" ).arg( selectQmlQuery ) );
errCause = QObject::tr( "Error executing the select query. The query was logged" );
}
QgsSqliteHandle::closeDb( handle );
sqlite3_free_table( results );
return style;
}
QGISEXTERN void cleanupProvider()
{
QgsSqliteHandle::closeAll();
}