diff --git a/src/providers/postgres/CMakeLists.txt b/src/providers/postgres/CMakeLists.txt index d64532777ac..cfaa55735af 100644 --- a/src/providers/postgres/CMakeLists.txt +++ b/src/providers/postgres/CMakeLists.txt @@ -13,6 +13,7 @@ SET(PG_SRCS qgspgnewconnection.cpp qgspgtablemodel.cpp qgscolumntypethread.cpp + qgspostgresexpressioncompiler.cpp ) SET(PG_MOC_HDRS qgspostgresprovider.h @@ -27,6 +28,7 @@ SET(PG_MOC_HDRS SET(PG_HDRS qgspostgrestransaction.h + qgspostgresexpressioncompiler.h ) ######################################################## diff --git a/src/providers/postgres/qgspostgresexpressioncompiler.cpp b/src/providers/postgres/qgspostgresexpressioncompiler.cpp new file mode 100644 index 00000000000..bc9b663bb22 --- /dev/null +++ b/src/providers/postgres/qgspostgresexpressioncompiler.cpp @@ -0,0 +1,152 @@ +/*************************************************************************** + + ---------------------------------------------------- + date : 22.4.2015 + copyright : (C) 2015 by Matthias Kuhn + email : matthias (at) opengis.ch + *************************************************************************** + * * + * 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 "qgspostgresexpressioncompiler.h" + +QgsPostgresExpressionCompiler::QgsPostgresExpressionCompiler( QgsPostgresFeatureSource* source ) + : mResult( None ) + , mSource( source ) +{ +} + +QgsPostgresExpressionCompiler::~QgsPostgresExpressionCompiler() +{ + +} + +QgsPostgresExpressionCompiler::Result QgsPostgresExpressionCompiler::compile( const QgsExpression* exp ) +{ + if ( exp->rootNode() ) + return compile ( exp->rootNode(), mResult ); + else + return Fail; +} + +QgsPostgresExpressionCompiler::Result QgsPostgresExpressionCompiler::compile( const QgsExpression::Node* node, QString& result ) +{ + switch( node->nodeType() ) + { + case QgsExpression::ntUnaryOperator: + { + const QgsExpression::NodeUnaryOperator* n = static_cast( node ); + switch( n->op() ) + { + case QgsExpression::uoNot: + break; + + case QgsExpression::uoMinus: + break; + } + + break; + } + + case QgsExpression::ntBinaryOperator: + { + QString op; + QString left; + QString right; + Result lr; + Result rr; + + const QgsExpression::NodeBinaryOperator* n = static_cast( node ); + + switch ( n->op() ) + { + case QgsExpression::boEQ: + op = "="; + break; + + case QgsExpression::boGE: + op = ">="; + break; + + case QgsExpression::boGT: + op = ">"; + break; + + case QgsExpression::boLE: + op = "<="; + break; + + case QgsExpression::boLT: + op = "<"; + break; + + case QgsExpression::boLike: + op = "LIKE"; + break; + + case QgsExpression::boILike: + op = "ILIKE"; + break; + + case QgsExpression::boOr: + op = "OR"; + break; + + case QgsExpression::boAnd: + op = "AND"; + break; + + case QgsExpression::boNE: + op = "<>"; + break; + } + + if ( !op.isNull() ) + { + lr = compile( n->opLeft(), left ); + rr = compile( n->opRight(), right ); + result = left + " " + op + " " + right; + return ( lr == Complete && rr == Complete ) ? Complete : Fail; + } + else + { + return Fail; + } + + break; + } + + case QgsExpression::ntFunction: + return Fail; + break; + + case QgsExpression::ntLiteral: + { + const QgsExpression::NodeLiteral* n = static_cast( node ); + result = QgsPostgresConn::quotedValue( n->value() ); + return Complete; + } + + case QgsExpression::ntColumnRef: + { + const QgsExpression::NodeColumnRef* n = static_cast( node ); + + if ( mSource->mFields.indexFromName( n->name() ) != -1 ) + { + result = QgsPostgresConn::quotedIdentifier( n->name() ); + return Complete; + } + else + { + // Not a provider field + return Fail; + } + } + } + return Fail; +} diff --git a/src/providers/postgres/qgspostgresexpressioncompiler.h b/src/providers/postgres/qgspostgresexpressioncompiler.h new file mode 100644 index 00000000000..ad748d6f82d --- /dev/null +++ b/src/providers/postgres/qgspostgresexpressioncompiler.h @@ -0,0 +1,50 @@ +/*************************************************************************** + + ---------------------------------------------------- + date : 22.4.2015 + copyright : (C) 2015 by Matthias Kuhn + email : matthias (at) opengis.ch + *************************************************************************** + * * + * 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 QGSPOSTGRESEXPRESSIONCOMPILER_H +#define QGSPOSTGRESEXPRESSIONCOMPILER_H + +#include "qgsexpression.h" +#include "qgspostgresfeatureiterator.h" + +class QgsPostgresExpressionCompiler +{ + public: + enum Result { + None, + Complete, + Partial, + Fail + }; + + QgsPostgresExpressionCompiler( QgsPostgresFeatureSource* source ); + ~QgsPostgresExpressionCompiler(); + + Result compile( const QgsExpression* exp ); + + inline Result success() { return mSuccess; } + + const QString& result() { return mResult; } + + private: + Result compile( const QgsExpression::Node* node, QString& str ); + + private: + Result mSuccess; + QString mResult; + QgsPostgresFeatureSource* mSource; +}; + +#endif // QGSPOSTGRESEXPRESSIONCOMPILER_H diff --git a/src/providers/postgres/qgspostgresfeatureiterator.cpp b/src/providers/postgres/qgspostgresfeatureiterator.cpp index fdb9a74c653..14d42a24450 100644 --- a/src/providers/postgres/qgspostgresfeatureiterator.cpp +++ b/src/providers/postgres/qgspostgresfeatureiterator.cpp @@ -12,16 +12,18 @@ * (at your option) any later version. * * * ***************************************************************************/ +#include "qgsgeometry.h" +#include "qgspostgresconnpool.h" +#include "qgspostgresexpressioncompiler.h" #include "qgspostgresfeatureiterator.h" #include "qgspostgresprovider.h" -#include "qgspostgresconnpool.h" #include "qgspostgrestransaction.h" -#include "qgsgeometry.h" #include "qgslogger.h" #include "qgsmessagelog.h" #include +#include const int QgsPostgresFeatureIterator::sFeatureQueueSize = 2000; @@ -32,6 +34,7 @@ QgsPostgresFeatureIterator::QgsPostgresFeatureIterator( QgsPostgresFeatureSource , mFeatureQueueSize( sFeatureQueueSize ) , mFetched( 0 ) , mFetchGeometry( false ) + , mExpressionCompiled( false) { if ( !source->mTransactionConnection ) { @@ -67,6 +70,17 @@ QgsPostgresFeatureIterator::QgsPostgresFeatureIterator( QgsPostgresFeatureSource { whereClause = QgsPostgresUtils::whereClause( mRequest.filterFids(), mSource->mFields, mConn, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, mSource->mShared ); } + else if ( request.filterType() == QgsFeatureRequest::FilterExpression + && QSettings().value( "/qgis/postgres/compileExpressions", false ).toBool() ) + { + QgsPostgresExpressionCompiler compiler = QgsPostgresExpressionCompiler( source ); + + if ( compiler.compile( request.filterExpression() ) == QgsPostgresExpressionCompiler::Complete ) + { + whereClause = compiler.result(); + mExpressionCompiled = true; + } + } if ( !mSource->mSqlWhereClause.isEmpty() ) { @@ -166,6 +180,14 @@ bool QgsPostgresFeatureIterator::fetchFeature( QgsFeature& feature ) return true; } +bool QgsPostgresFeatureIterator::nextFeatureFilterExpression( QgsFeature& f ) +{ + if ( !mExpressionCompiled ) + return QgsAbstractFeatureIterator::nextFeatureFilterExpression( f ); + else + return fetchFeature( f ); +} + bool QgsPostgresFeatureIterator::prepareSimplification( const QgsSimplifyMethod& simplifyMethod ) { // setup simplification of geometries to fetch @@ -233,6 +255,11 @@ bool QgsPostgresFeatureIterator::close() return true; } +int QgsPostgresFeatureIterator::count() +{ + +} + /////////////// QString QgsPostgresFeatureIterator::whereClauseRect() @@ -407,6 +434,7 @@ bool QgsPostgresFeatureIterator::declareCursor( const QString& whereClause ) if ( !mConn->openCursor( mCursorName, query ) ) { + // reloading the fields might help next time around // TODO how to cleanly force reload of fields? P->loadFields(); close(); diff --git a/src/providers/postgres/qgspostgresfeatureiterator.h b/src/providers/postgres/qgspostgresfeatureiterator.h index e402d5614d4..0a4729b016b 100644 --- a/src/providers/postgres/qgspostgresfeatureiterator.h +++ b/src/providers/postgres/qgspostgresfeatureiterator.h @@ -55,13 +55,14 @@ class QgsPostgresFeatureSource : public QgsAbstractFeatureSource QSharedPointer mShared; /* The transaction connection (if any) gets refed/unrefed when creating/ - * destroying the QgsPostfresFeatureSource, to ensure that the transaction + * destroying the QgsPostgresFeatureSource, to ensure that the transaction * connection remains valid during the life time of the feature source * even if the QgsPostgresTransaction object which initially created the * connection has since been destroyed. */ QgsPostgresConn* mTransactionConnection; friend class QgsPostgresFeatureIterator; + friend class QgsPostgresExpressionCompiler; }; @@ -80,10 +81,16 @@ class QgsPostgresFeatureIterator : public QgsAbstractFeatureIteratorFromSource