Skip to content
Permalink
Browse files

[FEATURE] Expression compiler for SpatiaLite provider

  • Loading branch information
nyalldawson committed Nov 18, 2015
1 parent 5f65f87 commit 35d0dd3211a799bee1eba2a7ed62d3fda3bc4e67
@@ -30,7 +30,7 @@ QgsSqlExpressionCompiler::~QgsSqlExpressionCompiler()
QgsSqlExpressionCompiler::Result QgsSqlExpressionCompiler::compile( const QgsExpression* exp )
{
if ( exp->rootNode() )
return compile( exp->rootNode(), mResult );
return compileNode( exp->rootNode(), mResult );
else
return Fail;
}
@@ -69,7 +69,7 @@ QString QgsSqlExpressionCompiler::quotedValue( const QVariant& value )
}
}

QgsSqlExpressionCompiler::Result QgsSqlExpressionCompiler::compile( const QgsExpression::Node* node, QString& result )
QgsSqlExpressionCompiler::Result QgsSqlExpressionCompiler::compileNode( const QgsExpression::Node* node, QString& result )
{
switch ( node->nodeType() )
{
@@ -133,18 +133,26 @@ QgsSqlExpressionCompiler::Result QgsSqlExpressionCompiler::compile( const QgsExp

case QgsExpression::boLike:
op = "LIKE";
partialCompilation = mFlags.testFlag( LikeIsCaseInsensitive );
break;

case QgsExpression::boILike:
op = "ILIKE";
if ( mFlags.testFlag( LikeIsCaseInsensitive ) )
op = "LIKE";
else
op = "ILIKE";
break;

case QgsExpression::boNotLike:
op = "NOT LIKE";
partialCompilation = mFlags.testFlag( LikeIsCaseInsensitive );
break;

case QgsExpression::boNotILike:
op = "NOT ILIKE";
if ( mFlags.testFlag( LikeIsCaseInsensitive ) )
op = "NOT LIKE";
else
op = "NOT ILIKE";
break;

case QgsExpression::boOr:
@@ -198,10 +206,10 @@ QgsSqlExpressionCompiler::Result QgsSqlExpressionCompiler::compile( const QgsExp
return Fail;

QString left;
Result lr( compile( n->opLeft(), left ) );
Result lr( compileNode( n->opLeft(), left ) );

QString right;
Result rr( compile( n->opRight(), right ) );
Result rr( compileNode( n->opRight(), right ) );

result = left + ' ' + op + ' ' + right;
if ( lr == Complete && rr == Complete )
@@ -251,7 +259,7 @@ QgsSqlExpressionCompiler::Result QgsSqlExpressionCompiler::compile( const QgsExp
Q_FOREACH ( const QgsExpression::Node* ln, n->list()->list() )
{
QString s;
Result r = compile( ln, s );
Result r = compileNode( ln, s );
if ( r == Complete || r == Partial )
{
list << s;
@@ -263,7 +271,7 @@ QgsSqlExpressionCompiler::Result QgsSqlExpressionCompiler::compile( const QgsExp
}

QString nd;
Result rn = compile( n->node(), nd );
Result rn = compileNode( n->node(), nd );
if ( rn != Complete && rn != Partial )
return rn;

@@ -47,7 +47,8 @@ class CORE_EXPORT QgsSqlExpressionCompiler
*/
enum Flag
{
CaseInsensitiveStringMatch = 0x01, //!< Provider performs case-insensitive string matching
CaseInsensitiveStringMatch = 0x01, //!< Provider performs case-insensitive string matching for all strings
LikeIsCaseInsensitive = 0x02, //!< Provider treats LIKE as case-insensitive
};
Q_DECLARE_FLAGS( Flags, Flag )

@@ -86,7 +87,7 @@ class CORE_EXPORT QgsSqlExpressionCompiler
* @param str string representing compiled node should be stored in this parameter
* @returns result of node compilation
*/
virtual Result compile( const QgsExpression::Node* node, QString& str );
virtual Result compileNode( const QgsExpression::Node* node, QString& str );

QString mResult;
QgsFields mFields;
@@ -23,7 +23,7 @@ QgsOgrExpressionCompiler::QgsOgrExpressionCompiler( QgsOgrFeatureSource* source
}


QgsOgrExpressionCompiler::Result QgsOgrExpressionCompiler::compile( const QgsExpression* exp )
QgsSqlExpressionCompiler::Result QgsOgrExpressionCompiler::compile( const QgsExpression* exp )
{
//for certain driver types, OGR forwards SQL through to the underlying provider. In these cases
//the syntax may differ from OGR SQL, so we don't support compilation for these drivers
@@ -46,7 +46,7 @@ QgsOgrExpressionCompiler::Result QgsOgrExpressionCompiler::compile( const QgsExp
return QgsSqlExpressionCompiler::compile( exp );
}

QgsOgrExpressionCompiler::Result QgsOgrExpressionCompiler::compile( const QgsExpression::Node* node, QString& result )
QgsSqlExpressionCompiler::Result QgsOgrExpressionCompiler::compileNode( const QgsExpression::Node* node, QString& result )
{
switch ( node->nodeType() )
{
@@ -68,7 +68,7 @@ QgsOgrExpressionCompiler::Result QgsOgrExpressionCompiler::compile( const QgsExp

default:
//fallback to default handling
return QgsSqlExpressionCompiler::compile( node, result );
return QgsSqlExpressionCompiler::compileNode( node, result );
}
}

@@ -78,10 +78,10 @@ QgsOgrExpressionCompiler::Result QgsOgrExpressionCompiler::compile( const QgsExp
return Fail;

default:
return QgsSqlExpressionCompiler::compile( node, result );
return QgsSqlExpressionCompiler::compileNode( node, result );
}

return QgsSqlExpressionCompiler::compile( node, result );
return QgsSqlExpressionCompiler::compileNode( node, result );
}

QString QgsOgrExpressionCompiler::quotedIdentifier( const QString& identifier )
@@ -26,11 +26,11 @@ class QgsOgrExpressionCompiler : public QgsSqlExpressionCompiler

explicit QgsOgrExpressionCompiler( QgsOgrFeatureSource* source );

Result compile( const QgsExpression* exp ) override;
virtual Result compile( const QgsExpression* exp ) override;

protected:

Result compile( const QgsExpression::Node* node, QString& str ) override;
virtual Result compileNode( const QgsExpression::Node* node, QString& str ) override;
virtual QString quotedIdentifier( const QString& identifier ) override;
virtual QString quotedValue( const QVariant& value ) override;

@@ -88,15 +88,15 @@ QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource* source, bool
{
QgsOgrExpressionCompiler compiler = QgsOgrExpressionCompiler( source );

QgsOgrExpressionCompiler::Result result = compiler.compile( request.filterExpression() );
QgsSqlExpressionCompiler::Result result = compiler.compile( request.filterExpression() );

if ( result == QgsOgrExpressionCompiler::Complete || result == QgsOgrExpressionCompiler::Partial )
if ( result == QgsSqlExpressionCompiler::Complete || result == QgsSqlExpressionCompiler::Partial )
{
QString whereClause = compiler.result();
if ( OGR_L_SetAttributeFilter( ogrLayer, whereClause.toLocal8Bit().data() ) == OGRERR_NONE )
{
//if only partial success when compiling expression, we need to double-check results using QGIS' expressions
mExpressionCompiled = ( result == QgsOgrExpressionCompiler::Complete );
mExpressionCompiled = ( result == QgsSqlExpressionCompiler::Complete );
}
}
else
@@ -10,6 +10,7 @@ SET(SPATIALITE_SRCS
qgsspatialitedataitems.cpp
qgsspatialiteconnection.cpp
qgsspatialiteconnpool.cpp
qgsspatialiteexpressioncompiler.cpp
qgsspatialitefeatureiterator.cpp
qgsspatialitesourceselect.cpp
qgsspatialitetablemodel.cpp
@@ -0,0 +1,80 @@
/***************************************************************************
qgsspatialiteexpressioncompiler.cpp
-----------------------------------
begin : November 2015
copyright : (C) 2015 Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsspatialiteexpressioncompiler.h"
#include "qgssqlexpressioncompiler.h"
#include "qgsspatialiteprovider.h"

QgsSpatiaLiteExpressionCompiler::QgsSpatiaLiteExpressionCompiler( QgsSpatiaLiteFeatureSource* source )
: QgsSqlExpressionCompiler( source->mFields, QgsSqlExpressionCompiler::LikeIsCaseInsensitive )
{
}

QgsSqlExpressionCompiler::Result QgsSpatiaLiteExpressionCompiler::compileNode( const QgsExpression::Node* node, QString& result )
{
switch ( node->nodeType() )
{
case QgsExpression::ntBinaryOperator:
{
switch ( static_cast<const QgsExpression::NodeBinaryOperator*>( node )->op() )
{
case QgsExpression::boPow:
case QgsExpression::boRegexp:
return Fail; //not supported by SQLite

default:
//fallback to default handling
return QgsSqlExpressionCompiler::compileNode( node, result );
}
}

default:
return QgsSqlExpressionCompiler::compileNode( node, result );
}

return QgsSqlExpressionCompiler::compileNode( node, result );
}

QString QgsSpatiaLiteExpressionCompiler::quotedIdentifier( const QString& identifier )
{
return QgsSpatiaLiteProvider::quotedIdentifier( identifier );
}

QString QgsSpatiaLiteExpressionCompiler::quotedValue( const QVariant& value )
{
if ( value.isNull() )
return "NULL";

switch ( value.type() )
{
case QVariant::Int:
case QVariant::LongLong:
case QVariant::Double:
return value.toString();

case QVariant::Bool:
//SQLite has no boolean literals
return value.toBool() ? "1" : "0";

default:
case QVariant::String:
QString v = value.toString();
v.replace( '\'', "''" );
if ( v.contains( '\\' ) )
return v.replace( '\\', "\\\\" ).prepend( "E'" ).append( '\'' );
else
return v.prepend( '\'' ).append( '\'' );
}
}
@@ -0,0 +1,37 @@
/***************************************************************************
qgsspatialiteexpressioncompiler.h
---------------------------------
begin : November 2015
copyright : (C) 2015 Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSSPATIALITEEXPRESSIONCOMPILER_H
#define QGSSPATIALITEEXPRESSIONCOMPILER_H

#include "qgssqlexpressioncompiler.h"
#include "qgsexpression.h"
#include "qgsspatialitefeatureiterator.h"

class QgsSpatiaLiteExpressionCompiler : public QgsSqlExpressionCompiler
{
public:

explicit QgsSpatiaLiteExpressionCompiler( QgsSpatiaLiteFeatureSource* source );

protected:

virtual Result compileNode( const QgsExpression::Node* node, QString& str ) override;
virtual QString quotedIdentifier( const QString& identifier ) override;
virtual QString quotedValue( const QVariant& value ) override;

};

#endif // QGSSPATIALITEEXPRESSIONCOMPILER_H
@@ -17,6 +17,7 @@
#include "qgsspatialiteconnection.h"
#include "qgsspatialiteconnpool.h"
#include "qgsspatialiteprovider.h"
#include "qgsspatialiteexpressioncompiler.h"

#include "qgslogger.h"
#include "qgsmessagelog.h"
@@ -26,6 +27,7 @@
QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeatureSource* source, bool ownSource, const QgsFeatureRequest& request )
: QgsAbstractFeatureIteratorFromSource<QgsSpatiaLiteFeatureSource>( source, ownSource, request )
, sqliteStatement( NULL )
, mExpressionCompiled( false )
{

mHandle = QgsSpatiaLiteConnPool::instance()->acquireConnection( mSource->mSqlitePath );
@@ -62,6 +64,23 @@ QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeature
whereClauses.append( whereClause );
}
}
else if ( request.filterType() == QgsFeatureRequest::FilterExpression )
{
QgsSpatiaLiteExpressionCompiler compiler = QgsSpatiaLiteExpressionCompiler( source );

QgsSqlExpressionCompiler::Result result = compiler.compile( request.filterExpression() );

if ( result == QgsSqlExpressionCompiler::Complete || result == QgsSqlExpressionCompiler::Partial )
{
whereClause = compiler.result();
if ( !whereClause.isEmpty() )
{
whereClauses.append( whereClause );
//if only partial success when compiling expression, we need to double-check results using QGIS' expressions
mExpressionCompiled = ( result == QgsSqlExpressionCompiler::Complete );
}
}
}

if ( !mSource->mSubsetString.isEmpty() )
{
@@ -116,6 +135,14 @@ bool QgsSpatiaLiteFeatureIterator::fetchFeature( QgsFeature& feature )
return true;
}

bool QgsSpatiaLiteFeatureIterator::nextFeatureFilterExpression( QgsFeature& f )
{
if ( !mExpressionCompiled )
return QgsAbstractFeatureIterator::nextFeatureFilterExpression( f );
else
return fetchFeature( f );
}


bool QgsSpatiaLiteFeatureIterator::rewind()
{
@@ -49,6 +49,7 @@ class QgsSpatiaLiteFeatureSource : public QgsAbstractFeatureSource
QString mSqlitePath;

friend class QgsSpatiaLiteFeatureIterator;
friend class QgsSpatiaLiteExpressionCompiler;
};

class QgsSpatiaLiteFeatureIterator : public QgsAbstractFeatureIteratorFromSource<QgsSpatiaLiteFeatureSource>
@@ -69,6 +70,9 @@ class QgsSpatiaLiteFeatureIterator : public QgsAbstractFeatureIteratorFromSource
//! fetch next feature, return true on success
virtual bool fetchFeature( QgsFeature& feature ) override;

//! fetch next feature filter expression
bool nextFeatureFilterExpression( QgsFeature& f ) override;

QString whereClauseRect();
QString whereClauseFid();
QString whereClauseFids();
@@ -96,6 +100,10 @@ class QgsSpatiaLiteFeatureIterator : public QgsAbstractFeatureIteratorFromSource

bool mHasPrimaryKey;
QgsFeatureId mRowNumber;

private:

bool mExpressionCompiled;
};

#endif // QGSSPATIALITEFEATUREITERATOR_H

0 comments on commit 35d0dd3

Please sign in to comment.
You can’t perform that action at this time.