Skip to content

Commit

Permalink
Add support for arrays in the spatialite provider
Browse files Browse the repository at this point in the history
New versions of GDAL/OGR (since trunk@35289) convert list types (StringList,
IntegerList, Integer64List and RealList) to a JSON string when it stores a
Spatialite table. It sets the column type as JSONSTRINGLIST, JSONINTEGERLIST,
JSONINTEGER64LIST or JSONREALLIST.
  • Loading branch information
Patrick Valsecchi authored and pvalsecc committed Sep 20, 2016
1 parent cd1d44b commit 6260f9d
Show file tree
Hide file tree
Showing 9 changed files with 330 additions and 58 deletions.
7 changes: 7 additions & 0 deletions python/core/qgsjsonutils.sip
Original file line number Diff line number Diff line change
Expand Up @@ -197,4 +197,11 @@ class QgsJSONUtils
* @param feature feature to export
*/
static QString exportAttributes( const QgsFeature& feature );

/** Parse a simple array (depth=1).
* @param json the JSON to parse
* @param type the type of the elements
* @note added in QGIS 3.0
*/
static QVariantList parseArray( const QString& json, QVariant::Type type );
};
79 changes: 79 additions & 0 deletions src/core/qgsjsonutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,3 +309,82 @@ QString QgsJSONUtils::exportAttributes( const QgsFeature& feature )
return attrs.prepend( '{' ).append( '}' );
}


namespace //TODO: remove when we switch off Qt4
{
void jumpSpace( const QString& txt, int& i )
{
while ( i < txt.length() && txt.at( i ).isSpace() ) ++i;
}

static QString getNextString( const QString& txt, int& i )
{
jumpSpace( txt, i );
QString cur = txt.mid( i );
if ( cur.startsWith( '"' ) )
{ //quoted element
QRegExp stringRe( "^\"((?:\\\\.|[^\"\\\\])*)\".*" );
if ( !stringRe.exactMatch( cur ) )
{
return QString::null;
}
i += stringRe.cap( 1 ).length() + 2;
jumpSpace( txt, i );
if ( !txt.mid( i ).startsWith( ',' ) && !txt.mid( i ).startsWith( ']' ) && i < txt.length() )
{
return QString::null;
}
i += 1; // jump the separator

return stringRe.cap( 1 )
.replace( "\\\"", "\"" )
.replace( "\\r", "\r" )
.replace( "\\b", "\b" )
.replace( "\\t", "\t" )
.replace( "\\/", "/" )
.replace( "\\n", "\n" )
.replace( "\\\\", "\\" );
}
else
{ //unquoted element
QString ret;
int sepPos = cur.indexOf( ',' );
if ( sepPos < 0 ) sepPos = cur.indexOf( ']' );
if ( sepPos < 0 )
{
i += cur.length();
return cur.trimmed();
}
i += sepPos + 1;
return cur.left( sepPos ).trimmed();
}
}
}

QVariantList QgsJSONUtils::parseArray( const QString& json, QVariant::Type type )
{
// TODO: switch to the Qt parser when we switch off Qt4
QVariantList result;
int i = 0;
jumpSpace( json, i );
if ( json.at( i++ ) != '[' )
{
return result;
}
while ( i < json.length() )
{
jumpSpace( json, i );
if ( json.at( i ) == ']' )
{
return result;
}
const QString value = getNextString( json, i );
if ( value.isNull() )
{
break;
}
QVariant variant( value );
if ( variant.convert( type ) ) result.append( variant );
}
return result;
}
6 changes: 6 additions & 0 deletions src/core/qgsjsonutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,12 @@ class CORE_EXPORT QgsJSONUtils
*/
static QString exportAttributes( const QgsFeature& feature );

/** Parse a simple array (depth=1).
* @param json the JSON to parse
* @param type the type of the elements
* @note added in QGIS 3.0
*/
static QVariantList parseArray( const QString& json, QVariant::Type type );
};

#endif // QGSJSONUTILS_H
30 changes: 20 additions & 10 deletions src/providers/spatialite/qgsspatialitefeatureiterator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@
#include "qgsspatialiteprovider.h"
#include "qgssqliteexpressioncompiler.h"

#include "qgsgeometry.h"
#include "qgslogger.h"
#include "qgsmessagelog.h"
#include <qgsgeometry.h>
#include <qgslogger.h>
#include <qgsmessagelog.h>
#include <qgsjsonutils.h>

#include <QSettings>

QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeatureSource* source, bool ownSource, const QgsFeatureRequest& request )
Expand Down Expand Up @@ -499,22 +501,24 @@ bool QgsSpatiaLiteFeatureIterator::getFeature( sqlite3_stmt *stmt, QgsFeature &f
{
if ( ic <= mRequest.subsetOfAttributes().size() )
{
int attrIndex = mRequest.subsetOfAttributes()[ic-1];
feature.setAttribute( attrIndex, getFeatureAttribute( stmt, ic, mSource->mFields.at( attrIndex ).type() ) );
const int attrIndex = mRequest.subsetOfAttributes()[ic-1];
const QgsField field = mSource->mFields.at( attrIndex );
feature.setAttribute( attrIndex, getFeatureAttribute( stmt, ic, field.type(), field.subType() ) );
}
}
else
{
int attrIndex = ic - 1;
feature.setAttribute( attrIndex, getFeatureAttribute( stmt, ic, mSource->mFields.at( attrIndex ).type() ) );
const int attrIndex = ic - 1;
const QgsField field = mSource->mFields.at( attrIndex );
feature.setAttribute( attrIndex, getFeatureAttribute( stmt, ic, field.type(), field.subType() ) );
}
}
}

return true;
}

QVariant QgsSpatiaLiteFeatureIterator::getFeatureAttribute( sqlite3_stmt* stmt, int ic, QVariant::Type type )
QVariant QgsSpatiaLiteFeatureIterator::getFeatureAttribute( sqlite3_stmt* stmt, int ic, QVariant::Type type, QVariant::Type subType )
{
if ( sqlite3_column_type( stmt, ic ) == SQLITE_INTEGER )
{
Expand All @@ -539,8 +543,14 @@ QVariant QgsSpatiaLiteFeatureIterator::getFeatureAttribute( sqlite3_stmt* stmt,
if ( sqlite3_column_type( stmt, ic ) == SQLITE_TEXT )
{
// TEXT value
const char *txt = ( const char * ) sqlite3_column_text( stmt, ic );
return QString::fromUtf8( txt );
const QString txt = QString::fromUtf8(( const char * ) sqlite3_column_text( stmt, ic ) );
if ( type == QVariant::List || type == QVariant::StringList )
{ // assume arrays are stored as JSON
QVariant result = QVariant( QgsJSONUtils::parseArray( txt, subType ) );
result.convert( type );
return result;
}
return txt;
}

// assuming NULL
Expand Down
2 changes: 1 addition & 1 deletion src/providers/spatialite/qgsspatialitefeatureiterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class QgsSpatiaLiteFeatureIterator : public QgsAbstractFeatureIteratorFromSource
QString quotedPrimaryKey();
bool getFeature( sqlite3_stmt *stmt, QgsFeature &feature );
QString fieldName( const QgsField& fld );
QVariant getFeatureAttribute( sqlite3_stmt* stmt, int ic, QVariant::Type type );
QVariant getFeatureAttribute( sqlite3_stmt* stmt, int ic, QVariant::Type type, QVariant::Type subType );
void getFeatureGeometry( sqlite3_stmt* stmt, int ic, QgsFeature& feature );

//! wrapper of the SQLite database connection
Expand Down
Loading

0 comments on commit 6260f9d

Please sign in to comment.