Skip to content
Permalink
Browse files
[FEATURE] update SL provider to use statistics (available from spatia…
…lite v4.0).

Patch by Alessandro Furieri.
  • Loading branch information
brushtyler committed Nov 30, 2012
1 parent 85faeb3 commit 47097b72b851a6637a6ceca737ae5d9d04b3b9b0
@@ -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,
@@ -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)
@@ -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
@@ -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;
@@ -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 )
@@ -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;
@@ -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 );
@@ -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
@@ -205,6 +388,7 @@ bool QgsSpatiaLiteConnection::checkHasMetadataTables( sqlite3* handle )
}
return false;
}
#endif

bool QgsSpatiaLiteConnection::getTableInfo( sqlite3 * handle, bool loadGeometrylessTables )
{
@@ -21,6 +21,8 @@
extern "C"
{
#include <sqlite3.h>
#include <spatialite/gaiageo.h>
#include <spatialite.h>
}

class QgsSpatiaLiteConnection : public QObject
@@ -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 */
@@ -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;

0 comments on commit 47097b7

Please sign in to comment.