Skip to content

Commit

Permalink
Merge pull request #346 from brushtyler/splite4_provider
Browse files Browse the repository at this point in the history
update SL provider to use SpatiaLite v4 features
  • Loading branch information
brushtyler committed Dec 4, 2012
2 parents d32a377 + 47097b7 commit 90972db
Show file tree
Hide file tree
Showing 8 changed files with 611 additions and 24 deletions.
6 changes: 6 additions & 0 deletions cmake/FindSPATIALITE.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
# SPATIALITE_INCLUDE_DIR
# SPATIALITE_LIBRARY

# This macro checks if the symbol exists
include(CheckLibraryExists)


# FIND_PATH and FIND_LIBRARY normally search standard locations
# before the specified paths. To search non-standard paths first,
Expand Down Expand Up @@ -60,6 +63,9 @@ IF (SPATIALITE_FOUND)
MESSAGE(STATUS "Found SpatiaLite: ${SPATIALITE_LIBRARY}")
ENDIF (NOT SPATIALITE_FIND_QUIETLY)

# Check for symbol gaiaDropTable
check_library_exists("${SPATIALITE_LIBRARY}" gaiaDropTable "" SPATIALITE_RECENT_VERSION)

ELSE (SPATIALITE_FOUND)

IF (SPATIALITE_FIND_REQUIRED)
Expand Down
4 changes: 4 additions & 0 deletions src/providers/spatialite/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ SET(SPATIALITE_MOC_HDRS

QT4_WRAP_CPP(SPATIALITE_MOC_SRCS ${SPATIALITE_MOC_HDRS})

IF(SPATIALITE_RECENT_VERSION)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}-DSPATIALITE_RECENT_VERSION")
ENDIF(SPATIALITE_RECENT_VERSION)


INCLUDE_DIRECTORIES(
../../core
Expand Down
192 changes: 188 additions & 4 deletions src/providers/spatialite/qgsspatialiteconnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,38 @@ QgsSpatiaLiteConnection::Error QgsSpatiaLiteConnection::fetchTables( bool loadGe
return FailedToOpen;
}

checkHasMetadataTables( handle );
if ( !mErrorMsg.isNull() )
int ret = checkHasMetadataTables( handle );
if ( !mErrorMsg.isNull() || ret == LayoutUnknown )
{
// unexpected error; invalid SpatiaLite DB
return FailedToCheckMetadata;
}

bool recentVersion = false;
#ifdef SPATIALITE_RECENT_VERSION
// only if libspatialite version is >= 4.0.0
recentVersion = true;
#endif

if ( ret == LayoutCurrent && recentVersion == false )
{
// obsolete library version
mErrorMsg = tr( "obsolete libspatialite: connecting to this DB requires using v.4.0 (or any subsequent)" );
return FailedToCheckMetadata;
}

#ifdef SPATIALITE_RECENT_VERSION
// only if libspatialite version is >= 4.0.0
// using v.4.0 Abstract Interface
if (!getTableInfoAbstractInterface( handle, loadGeometrylessTables ) )
{
return FailedToGetTables;
}
closeSpatiaLiteDb( handle );
return NoError;
#endif

// obsolete library: still using the traditional approach
if ( !getTableInfo( handle, loadGeometrylessTables ) )
{
return FailedToGetTables;
Expand All @@ -87,11 +112,40 @@ QgsSpatiaLiteConnection::Error QgsSpatiaLiteConnection::fetchTables( bool loadGe
return NoError;
}

bool QgsSpatiaLiteConnection::updateStatistics()
{
QFileInfo fi( mPath );
if ( !fi.exists() )
{
return false;
}

sqlite3* handle = openSpatiaLiteDb( fi.canonicalFilePath() );
if ( handle == NULL )
{
return false;
}

// checking the library version
bool recentVersion = false;
const char *version = spatialite_version();
if ( isdigit(*version) && *version >= '4' )
recentVersion = true;

bool ret = update_layer_statistics ( handle, NULL, NULL );

closeSpatiaLiteDb( handle );

return ret;
}

sqlite3 *QgsSpatiaLiteConnection::openSpatiaLiteDb( QString path )
{
sqlite3 *handle = NULL;
int ret;
// activating the SpatiaLite library
spatialite_init(0);

// trying to open the SQLite DB
ret = sqlite3_open_v2( path.toUtf8().constData(), &handle, SQLITE_OPEN_READWRITE, NULL );
if ( ret )
Expand All @@ -109,21 +163,25 @@ void QgsSpatiaLiteConnection::closeSpatiaLiteDb( sqlite3 * handle )
sqlite3_close( handle );
}

bool QgsSpatiaLiteConnection::checkHasMetadataTables( sqlite3* handle )
int QgsSpatiaLiteConnection::checkHasMetadataTables( sqlite3* handle )
{
bool gcSpatiaLite = false;
bool rsSpatiaLite = false;
bool gcSpatiaLite4 = false;
bool rsSpatiaLite4 = false;
bool tableName = false;
bool geomColumn = false;
bool coordDims = false;
bool gcSrid = false;
bool type = false;
bool geometry_type = false;
bool spatialIndex = false;
bool srsSrid = false;
bool authName = false;
bool authSrid = false;
bool refSysName = false;
bool proj4text = false;
bool srtext = false;
int ret;
const char *name;
int i;
Expand Down Expand Up @@ -153,13 +211,17 @@ bool QgsSpatiaLiteConnection::checkHasMetadataTables( sqlite3* handle )
gcSrid = true;
if ( strcasecmp( name, "type" ) == 0 )
type = true;
if ( strcasecmp( name, "geometry_type" ) == 0 )
geometry_type = true;
if ( strcasecmp( name, "spatial_index_enabled" ) == 0 )
spatialIndex = true;
}
}
sqlite3_free_table( results );
if ( tableName && geomColumn && type && coordDims && gcSrid && spatialIndex )
gcSpatiaLite = true;
if ( tableName && geomColumn && geometry_type && coordDims && gcSrid && spatialIndex )
gcSpatiaLite4 = true;

// checking if table SPATIAL_REF_SYS exists and has the expected layout
ret = sqlite3_get_table( handle, "PRAGMA table_info(spatial_ref_sys)", &results, &rows, &columns, &errMsg );
Expand All @@ -182,18 +244,139 @@ bool QgsSpatiaLiteConnection::checkHasMetadataTables( sqlite3* handle )
refSysName = true;
if ( strcasecmp( name, "proj4text" ) == 0 )
proj4text = true;
if ( strcasecmp( name, "srtext" ) == 0 )
srtext = true;
}
}
sqlite3_free_table( results );
if ( srsSrid && authName && authSrid && refSysName && proj4text )
rsSpatiaLite = true;
if ( srsSrid && authName && authSrid && refSysName && proj4text )
rsSpatiaLite4 = true;

// OK, this one seems to be a valid SpatiaLite DB
if ( gcSpatiaLite4 && rsSpatiaLite4 )
return LayoutCurrent;
if ( gcSpatiaLite && rsSpatiaLite )
return true;
return LayoutLegacy;

// this seems to be a valid SQLite DB, but not a SpatiaLite's one
return LayoutUnknown;

error:
// unexpected IO error
mErrorMsg = tr( "unknown error cause" );
if ( errMsg != NULL )
{
mErrorMsg = errMsg;
sqlite3_free( errMsg );
}
return false;
}

#ifdef SPATIALITE_RECENT_VERSION
// only if libspatialite version is >= 4.0.0
bool QgsSpatiaLiteConnection::getTableInfoAbstractInterface( sqlite3 * handle, bool loadGeometrylessTables )
{
int ret;
int i;
char **results;
int rows;
int columns;
char *errMsg = NULL;
bool ok = false;
QString sql;
gaiaVectorLayersListPtr list;

const char *version = spatialite_version();
if ( isdigit(*version) && *version >= '4' )
; // OK, linked against libspatialite v.4.0 (or any subsequent)
else
{
mErrorMsg = tr( "obsolete libspatialite: AbstractInterface is unsupported" );
return false;
}

// attempting to load the VectorLayersList
list = gaiaGetVectorLayersList (handle, NULL, NULL, GAIA_VECTORS_LIST_FAST);
if (list != NULL)
{
gaiaVectorLayerPtr lyr = list->First;
while ( lyr != NULL )
{
// populating the QGIS own Layers List
if (lyr->AuthInfos)
{
if ( lyr->AuthInfos->IsHidden )
{
// skipping any Hidden layer
lyr = lyr->Next;
continue;
}
}

QString tableName = QString::fromUtf8( lyr->TableName );
QString column = QString::fromUtf8( lyr->GeometryName );
QString type = tr( "UNKNOWN" );
switch (lyr->GeometryType)
{
case GAIA_VECTOR_GEOMETRY:
type = tr( "GEOMETRY" );
break;
case GAIA_VECTOR_POINT:
type = tr( "POINT" );
break;
case GAIA_VECTOR_LINESTRING:
type = tr( "LINESTRING" );
break;
case GAIA_VECTOR_POLYGON:
type = tr( "POLYGON" );
break;
case GAIA_VECTOR_MULTIPOINT:
type = tr( "MULTIPOINT" );
break;
case GAIA_VECTOR_MULTILINESTRING:
type = tr( "MULTILINESTRING" );
break;
case GAIA_VECTOR_MULTIPOLYGON:
type = tr( "MULTIPOLYGON" );
break;
case GAIA_VECTOR_GEOMETRYCOLLECTION:
type = tr( "GEOMETRYCOLLECTION" );
break;
};
mTables.append( TableEntry( tableName, column, type ) );
ok = true;

lyr = lyr->Next;
}
gaiaFreeVectorLayersList (list);
}

if ( loadGeometrylessTables )
{
// get all tables
sql = "SELECT name "
"FROM sqlite_master "
"WHERE type in ('table', 'view')";
ret = sqlite3_get_table( handle, sql.toUtf8(), &results, &rows, &columns, &errMsg );
if ( ret != SQLITE_OK )
goto error;
if ( rows < 1 )
;
else
{
for ( i = 1; i <= rows; i++ )
{
QString tableName = QString::fromUtf8( results[( i * columns ) + 0] );
mTables.append( TableEntry( tableName, QString(), "qgis_table" ) );
}
ok = true;
}
sqlite3_free_table( results );
}

return ok;

error:
// unexpected IO error
Expand All @@ -205,6 +388,7 @@ bool QgsSpatiaLiteConnection::checkHasMetadataTables( sqlite3* handle )
}
return false;
}
#endif

bool QgsSpatiaLiteConnection::getTableInfo( sqlite3 * handle, bool loadGeometrylessTables )
{
Expand Down
28 changes: 27 additions & 1 deletion src/providers/spatialite/qgsspatialiteconnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
extern "C"
{
#include <sqlite3.h>
#include <spatialite/gaiageo.h>
#include <spatialite.h>
}

class QgsSpatiaLiteConnection : public QObject
Expand Down Expand Up @@ -54,6 +56,13 @@ class QgsSpatiaLiteConnection : public QObject
FailedToGetTables,
};

enum DbLayoutVersion
{
LayoutUnknown,
LayoutLegacy,
LayoutCurrent,
};

Error fetchTables( bool loadGeometrylessTables );

/** return list of tables. fetchTables() function has to be called before */
Expand All @@ -62,17 +71,34 @@ class QgsSpatiaLiteConnection : public QObject
/** return additional error message (if an error occurred before) */
QString errorMessage() { return mErrorMsg; }

/**Updates the Internal Statistics*/
bool updateStatistics();

protected:
// SpatiaLite DB open / close
sqlite3 *openSpatiaLiteDb( QString path );
void closeSpatiaLiteDb( sqlite3 * handle );

/**Checks if geometry_columns and spatial_ref_sys exist and have expected layout*/
bool checkHasMetadataTables( sqlite3* handle );
int checkHasMetadataTables( sqlite3* handle );

/**Inserts information about the spatial tables into mTables*/
bool getTableInfo( sqlite3 * handle, bool loadGeometrylessTables );

#ifdef SPATIALITE_RECENT_VERSION
// only if libspatialite version is >= 4.0.0
/**
Inserts information about the spatial tables into mTables
please note: this method is fully based on the Abstract Interface
implemented in libspatialite starting since v.4.0
using the Abstract Interface is highly reccommended, because all
version-dependent implementation details become completly transparent,
thus completely freeing the client application to take care of them.
*/
bool getTableInfoAbstractInterface( sqlite3 * handle, bool loadGeometrylessTables );
#endif

/**cleaning well-formatted SQL strings*/
QString quotedValue( QString value ) const;

Expand Down
Loading

0 comments on commit 90972db

Please sign in to comment.