mirror of
https://github.com/qgis/QGIS.git
synced 2025-03-01 00:46:20 -05:00
[FEATURE] Send filter expressions to postgres provider
This commit allows to filter features already on server side. Only supported expressions will be sent to the database. Expressions using unsupported operators or functions will gracefully fallback to local evaluation. To make use of this feature * Enable it in options: data sources * QgsFeatureRequest().setFilterExpression( expression ) * or QgsVectorLayer::getFeatures( expression )
This commit is contained in:
parent
9b6e23d3af
commit
a99deb8b88
@ -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
|
||||
)
|
||||
|
||||
########################################################
|
||||
|
152
src/providers/postgres/qgspostgresexpressioncompiler.cpp
Normal file
152
src/providers/postgres/qgspostgresexpressioncompiler.cpp
Normal file
@ -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<const QgsExpression::NodeUnaryOperator*>( 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<const QgsExpression::NodeBinaryOperator*>( 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<const QgsExpression::NodeLiteral*>( node );
|
||||
result = QgsPostgresConn::quotedValue( n->value() );
|
||||
return Complete;
|
||||
}
|
||||
|
||||
case QgsExpression::ntColumnRef:
|
||||
{
|
||||
const QgsExpression::NodeColumnRef* n = static_cast<const QgsExpression::NodeColumnRef*>( node );
|
||||
|
||||
if ( mSource->mFields.indexFromName( n->name() ) != -1 )
|
||||
{
|
||||
result = QgsPostgresConn::quotedIdentifier( n->name() );
|
||||
return Complete;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not a provider field
|
||||
return Fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Fail;
|
||||
}
|
50
src/providers/postgres/qgspostgresexpressioncompiler.h
Normal file
50
src/providers/postgres/qgspostgresexpressioncompiler.h
Normal file
@ -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
|
@ -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 <QObject>
|
||||
#include <QSettings>
|
||||
|
||||
|
||||
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();
|
||||
|
@ -55,13 +55,14 @@ class QgsPostgresFeatureSource : public QgsAbstractFeatureSource
|
||||
QSharedPointer<QgsPostgresSharedData> 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<Q
|
||||
//! end of iterating: free the resources / lock
|
||||
virtual bool close() override;
|
||||
|
||||
//! the number of features
|
||||
virtual int count() override;
|
||||
|
||||
protected:
|
||||
//! fetch next feature, return true on success
|
||||
virtual bool fetchFeature( QgsFeature& feature ) override;
|
||||
|
||||
//! fetch next feature filter expression
|
||||
bool nextFeatureFilterExpression(QgsFeature& f);
|
||||
|
||||
//! Setup the simplification of geometries to fetch using the specified simplify method
|
||||
virtual bool prepareSimplification( const QgsSimplifyMethod& simplifyMethod ) override;
|
||||
|
||||
@ -119,6 +126,8 @@ class QgsPostgresFeatureIterator : public QgsAbstractFeatureIteratorFromSource<Q
|
||||
private:
|
||||
//! returns whether the iterator supports simplify geometries on provider side
|
||||
virtual bool providerCanSimplify( QgsSimplifyMethod::MethodType methodType ) const override;
|
||||
|
||||
bool mExpressionCompiled;
|
||||
};
|
||||
|
||||
#endif // QGSPOSTGRESFEATUREITERATOR_H
|
||||
|
Loading…
x
Reference in New Issue
Block a user