QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
qgssqlexpressioncompiler.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgssqlexpressioncompiler.cpp
3  ----------------------------
4  begin : November 2015
5  copyright : (C) 2015 Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
17 #include "qgsexpressionnodeimpl.h"
18 #include "qgsexpressionfunction.h"
19 #include "qgsexpression.h"
20 
21 QgsSqlExpressionCompiler::QgsSqlExpressionCompiler( const QgsFields &fields, Flags flags, bool ignoreStaticNodes )
22  : mResult( None )
23  , mFields( fields )
24  , mFlags( flags )
25  , mIgnoreStaticNodes( ignoreStaticNodes )
26 {
27 }
28 
30 {
31  if ( exp->rootNode() )
32  return compileNode( exp->rootNode(), mResult );
33  else
34  return Fail;
35 }
36 
38 {
39  return mResult;
40 }
41 
43 {
44  if ( op == QgsExpressionNodeBinaryOperator::BinaryOperator::boILike ||
45  op == QgsExpressionNodeBinaryOperator::BinaryOperator::boLike ||
46  op == QgsExpressionNodeBinaryOperator::BinaryOperator::boNotILike ||
47  op == QgsExpressionNodeBinaryOperator::BinaryOperator::boNotLike ||
48  op == QgsExpressionNodeBinaryOperator::BinaryOperator::boRegexp )
49  return true;
50  else
51  return false;
52 }
53 
54 QString QgsSqlExpressionCompiler::quotedIdentifier( const QString &identifier )
55 {
56  QString quoted = identifier;
57  quoted.replace( '"', QLatin1String( "\"\"" ) );
58  quoted = quoted.prepend( '\"' ).append( '\"' );
59  return quoted;
60 }
61 
62 QString QgsSqlExpressionCompiler::quotedValue( const QVariant &value, bool &ok )
63 {
64  ok = true;
65 
66  if ( value.isNull() )
67  return QStringLiteral( "NULL" );
68 
69  switch ( value.type() )
70  {
71  case QVariant::Int:
72  case QVariant::LongLong:
73  case QVariant::Double:
74  return value.toString();
75 
76  case QVariant::Bool:
77  return value.toBool() ? QStringLiteral( "TRUE" ) : QStringLiteral( "FALSE" );
78 
79  default:
80  case QVariant::String:
81  QString v = value.toString();
82  v.replace( '\'', QLatin1String( "''" ) );
83  if ( v.contains( '\\' ) )
84  return v.replace( '\\', QLatin1String( "\\\\" ) ).prepend( "E'" ).append( '\'' );
85  else
86  return v.prepend( '\'' ).append( '\'' );
87  }
88 }
89 
91 {
93  if ( staticRes != Fail )
94  return staticRes;
95 
96  switch ( node->nodeType() )
97  {
99  {
100  const QgsExpressionNodeUnaryOperator *n = static_cast<const QgsExpressionNodeUnaryOperator *>( node );
101  switch ( n->op() )
102  {
104  {
105  QString right;
106  if ( compileNode( n->operand(), right ) == Complete )
107  {
108  result = "( NOT " + right + ')';
109  return Complete;
110  }
111 
112  return Fail;
113  }
114 
116  {
117  if ( mFlags.testFlag( NoUnaryMinus ) )
118  return Fail;
119 
120  QString right;
121  if ( compileNode( n->operand(), right ) == Complete )
122  {
123  result = "( - (" + right + "))";
124  return Complete;
125  }
126 
127  return Fail;
128  }
129  }
130 
131  break;
132  }
133 
135  {
136  const QgsExpressionNodeBinaryOperator *n = static_cast<const QgsExpressionNodeBinaryOperator *>( node );
137 
138  QString op;
139  bool partialCompilation = false;
140  bool failOnPartialNode = false;
141  switch ( n->op() )
142  {
145  {
146  // equality between column refs results in a partial compilation, since provider is performing
147  // case-insensitive matches between strings
148  partialCompilation = true;
149  }
150 
151  op = QStringLiteral( "=" );
152  break;
153 
155  op = QStringLiteral( ">=" );
156  break;
157 
159  op = QStringLiteral( ">" );
160  break;
161 
163  op = QStringLiteral( "<=" );
164  break;
165 
167  op = QStringLiteral( "<" );
168  break;
169 
171  op = QStringLiteral( "IS" );
172  break;
173 
175  op = QStringLiteral( "IS NOT" );
176  failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch );
177  break;
178 
180  op = QStringLiteral( "LIKE" );
181  partialCompilation = mFlags.testFlag( LikeIsCaseInsensitive );
182  break;
183 
185  if ( mFlags.testFlag( LikeIsCaseInsensitive ) )
186  op = QStringLiteral( "LIKE" );
187  else
188  op = QStringLiteral( "ILIKE" );
189  break;
190 
192  op = QStringLiteral( "NOT LIKE" );
193  partialCompilation = mFlags.testFlag( LikeIsCaseInsensitive );
194  failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch );
195  break;
196 
198  failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch );
199  if ( mFlags.testFlag( LikeIsCaseInsensitive ) )
200  op = QStringLiteral( "NOT LIKE" );
201  else
202  op = QStringLiteral( "NOT ILIKE" );
203  break;
204 
206  if ( mFlags.testFlag( NoNullInBooleanLogic ) )
207  {
208  if ( nodeIsNullLiteral( n->opLeft() ) || nodeIsNullLiteral( n->opRight() ) )
209  return Fail;
210  }
211 
212  op = QStringLiteral( "OR" );
213  break;
214 
216  if ( mFlags.testFlag( NoNullInBooleanLogic ) )
217  {
218  if ( nodeIsNullLiteral( n->opLeft() ) || nodeIsNullLiteral( n->opRight() ) )
219  return Fail;
220  }
221 
222  op = QStringLiteral( "AND" );
223  break;
224 
226  failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch );
227  op = QStringLiteral( "<>" );
228  break;
229 
231  op = QStringLiteral( "*" );
232  break;
233 
235  op = QStringLiteral( "+" );
236  break;
237 
239  op = QStringLiteral( "-" );
240  break;
241 
243  op = QStringLiteral( "/" );
244  break;
245 
247  op = QStringLiteral( "%" );
248  break;
249 
251  op = QStringLiteral( "||" );
252  break;
253 
255  op = QStringLiteral( "/" );
256  break;
257 
259  op = QStringLiteral( "^" );
260  break;
261 
263  op = QStringLiteral( "~" );
264  break;
265  }
266 
267  if ( op.isNull() )
268  return Fail;
269 
270  QString left;
271  Result lr( compileNode( n->opLeft(), left ) );
272 
273  if ( opIsStringComparison( n ->op() ) )
274  left = castToText( left );
275 
276  QString right;
277  Result rr( compileNode( n->opRight(), right ) );
278 
279  if ( failOnPartialNode && ( lr == Partial || rr == Partial ) )
280  return Fail;
281 
283  {
284  right = castToReal( right );
285  if ( right.isEmpty() )
286  {
287  // not supported
288  return Fail;
289  }
290  }
291 
292  result = '(' + left + ' ' + op + ' ' + right + ')';
294  {
295  result = castToInt( result );
296  if ( result.isEmpty() )
297  {
298  // not supported
299  return Fail;
300  }
301  }
302 
303  if ( lr == Complete && rr == Complete )
304  return ( partialCompilation ? Partial : Complete );
305  else if ( ( lr == Partial && rr == Complete ) || ( lr == Complete && rr == Partial ) || ( lr == Partial && rr == Partial ) )
306  return Partial;
307  else
308  return Fail;
309  }
310 
312  {
313  const QgsExpressionNodeLiteral *n = static_cast<const QgsExpressionNodeLiteral *>( node );
314  bool ok = false;
315  if ( mFlags.testFlag( CaseInsensitiveStringMatch ) && n->value().type() == QVariant::String )
316  {
317  // provider uses case insensitive matching, so if literal was a string then we only have a Partial compilation and need to
318  // double check results using QGIS' expression engine
319  result = quotedValue( n->value(), ok );
320  return ok ? Partial : Fail;
321  }
322  else
323  {
324  result = quotedValue( n->value(), ok );
325  return ok ? Complete : Fail;
326  }
327  }
328 
330  {
331  const QgsExpressionNodeColumnRef *n = static_cast<const QgsExpressionNodeColumnRef *>( node );
332 
333  // QGIS expressions don't care about case sensitive field naming, so we match case insensitively here to the
334  // layer's fields and then retrieve the actual case of the field name for use in the compilation
335  const int fieldIndex = mFields.lookupField( n->name() );
336  if ( fieldIndex == -1 )
337  // Not a provider field
338  return Fail;
339 
340  result = quotedIdentifier( mFields.at( fieldIndex ).name() );
341 
342  return Complete;
343  }
344 
346  {
347  const QgsExpressionNodeInOperator *n = static_cast<const QgsExpressionNodeInOperator *>( node );
348  QStringList list;
349 
350  Result inResult = Complete;
351  const auto constList = n->list()->list();
352  for ( const QgsExpressionNode *ln : constList )
353  {
354  QString s;
355  Result r = compileNode( ln, s );
356  if ( r == Complete || r == Partial )
357  {
358  list << s;
359  if ( r == Partial )
360  inResult = Partial;
361  }
362  else
363  return r;
364  }
365 
366  QString nd;
367  Result rn = compileNode( n->node(), nd );
368  if ( rn != Complete && rn != Partial )
369  return rn;
370 
371  result = QStringLiteral( "%1 %2IN (%3)" ).arg( nd, n->isNotIn() ? QStringLiteral( "NOT " ) : QString(), list.join( ',' ) );
372  return ( inResult == Partial || rn == Partial ) ? Partial : Complete;
373  }
374 
376  {
377  const QgsExpressionNodeFunction *n = static_cast<const QgsExpressionNodeFunction *>( node );
379 
380  // get sql function to compile node expression
381  QString nd = sqlFunctionFromFunctionName( fd->name() );
382  // if no sql function the node can't be compiled
383  if ( nd.isNull() )
384  return Fail;
385 
386  // compile arguments
387  QStringList args;
388  Result inResult = Complete;
389  const auto constList = n->args()->list();
390  for ( const QgsExpressionNode *ln : constList )
391  {
392  QString s;
393  Result r = compileNode( ln, s );
394  if ( r == Complete || r == Partial )
395  {
396  args << s;
397  if ( r == Partial )
398  inResult = Partial;
399  }
400  else
401  return r;
402  }
403 
404  // update arguments to be adapted to SQL function
405  args = sqlArgumentsFromFunctionName( fd->name(), args );
406 
407  // build result
408  result = !nd.isEmpty() ? QStringLiteral( "%1(%2)" ).arg( nd, args.join( ',' ) ) : args.join( ',' );
409  return inResult == Partial ? Partial : Complete;
410  }
411 
413  break;
414 
416  break;
417  }
418 
419  return Fail;
420 }
421 
422 QString QgsSqlExpressionCompiler::sqlFunctionFromFunctionName( const QString &fnName ) const
423 {
424  Q_UNUSED( fnName )
425  return QString();
426 }
427 
428 QStringList QgsSqlExpressionCompiler::sqlArgumentsFromFunctionName( const QString &fnName, const QStringList &fnArgs ) const
429 {
430  Q_UNUSED( fnName )
431  return QStringList( fnArgs );
432 }
433 
434 QString QgsSqlExpressionCompiler::castToReal( const QString &value ) const
435 {
436  Q_UNUSED( value )
437  return QString();
438 }
439 
440 QString QgsSqlExpressionCompiler::castToText( const QString &value ) const
441 {
442  return value;
443 }
444 
445 QString QgsSqlExpressionCompiler::castToInt( const QString &value ) const
446 {
447  Q_UNUSED( value )
448  return QString();
449 }
450 
452 {
453  if ( mIgnoreStaticNodes )
454  return Fail;
455 
456  if ( node->hasCachedStaticValue() )
457  {
458  bool ok = false;
459  if ( mFlags.testFlag( CaseInsensitiveStringMatch ) && node->cachedStaticValue().type() == QVariant::String )
460  {
461  // provider uses case insensitive matching, so if literal was a string then we only have a Partial compilation and need to
462  // double check results using QGIS' expression engine
463  result = quotedValue( node->cachedStaticValue(), ok );
464  return ok ? Partial : Fail;
465  }
466  else
467  {
468  result = quotedValue( node->cachedStaticValue(), ok );
469  return ok ? Complete : Fail;
470  }
471  }
472  return Fail;
473 }
474 
475 bool QgsSqlExpressionCompiler::nodeIsNullLiteral( const QgsExpressionNode *node ) const
476 {
477  if ( node->nodeType() != QgsExpressionNode::ntLiteral )
478  return false;
479 
480  const QgsExpressionNodeLiteral *nLit = static_cast<const QgsExpressionNodeLiteral *>( node );
481  return nLit->value().isNull();
482 }
A abstract base class for defining QgsExpression functions.
QString name() const
The name of the function.
A binary expression operator, which operates on two values.
QgsExpressionNodeBinaryOperator::BinaryOperator op() const
Returns the binary operator.
QgsExpressionNode * opRight() const
Returns the node to the right of the operator.
QgsExpressionNode * opLeft() const
Returns the node to the left of the operator.
BinaryOperator
list of binary operators
An expression node which takes it value from a feature's field.
QString name() const
The name of the column.
An expression node for expression functions.
int fnIndex() const
Returns the index of the node's function.
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function.
An expression node for value IN or NOT IN clauses.
QgsExpressionNode * node() const
Returns the expression node.
QgsExpressionNode::NodeList * list() const
Returns the list of nodes to search for matching values within.
bool isNotIn() const
Returns true if this node is a "NOT IN" operator, or false if the node is a normal "IN" operator.
An expression node for literal values.
QVariant value() const
The value of the literal.
A unary node is either negative as in boolean (not) or as in numbers (minus).
QgsExpressionNodeUnaryOperator::UnaryOperator op() const
Returns the unary operator.
QgsExpressionNode * operand() const
Returns the node the operator will operate upon.
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
Abstract base class for all nodes that can appear in an expression.
bool hasCachedStaticValue() const
Returns true if the node can be replaced by a static cached value.
virtual QgsExpressionNode::NodeType nodeType() const =0
Gets the type of this node.
QVariant cachedStaticValue() const
Returns the node's static cached value.
@ ntIndexOperator
Index operator.
Class for parsing and evaluation of expressions (formerly called "search strings").
static const QList< QgsExpressionFunction * > & Functions()
const QgsExpressionNode * rootNode() const
Returns the root node of the expression.
QString name
Definition: qgsfield.h:60
Container of fields for a vector layer.
Definition: qgsfields.h:45
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:344
virtual Result compileNode(const QgsExpressionNode *node, QString &str)
Compiles an expression node and returns the result of the compilation.
virtual Result compile(const QgsExpression *exp)
Compiles an expression and returns the result of the compilation.
Result
Possible results from expression compilation.
@ Fail
Provider cannot handle expression.
@ Complete
Expression was successfully compiled and can be completely delegated to provider.
@ Partial
Expression was partially compiled, but provider will return extra records and results must be double-...
virtual QStringList sqlArgumentsFromFunctionName(const QString &fnName, const QStringList &fnArgs) const
Returns the Arguments for SQL function for the expression function.
virtual Result replaceNodeByStaticCachedValueIfPossible(const QgsExpressionNode *node, QString &str)
Tries to replace a node by its static cached value where possible.
virtual QString result()
Returns the compiled expression string for use by the provider.
virtual QString quotedValue(const QVariant &value, bool &ok)
Returns a quoted attribute value, in the format expected by the provider.
virtual QString castToText(const QString &value) const
Casts a value to a text result.
virtual QString castToInt(const QString &value) const
Casts a value to a integer result.
virtual QString quotedIdentifier(const QString &identifier)
Returns a quoted column identifier, in the format expected by the provider.
virtual QString sqlFunctionFromFunctionName(const QString &fnName) const
Returns the SQL function for the expression function.
QgsSqlExpressionCompiler(const QgsFields &fields, QgsSqlExpressionCompiler::Flags flags=Flags(), bool ignoreStaticNodes=false)
Constructor for expression compiler.
virtual QString castToReal(const QString &value) const
Casts a value to a real result.
bool opIsStringComparison(QgsExpressionNodeBinaryOperator::BinaryOperator op)
Returns true if op is one of.
@ LikeIsCaseInsensitive
Provider treats LIKE as case-insensitive.
@ NoUnaryMinus
Provider does not unary minus, e.g., " -( 100 * 2 ) = ...".
@ CaseInsensitiveStringMatch
Provider performs case-insensitive string matching for all strings.
@ NoNullInBooleanLogic
Provider does not support using NULL with boolean logic, e.g., "(...) OR NULL".
@ IntegerDivisionResultsInInteger
Dividing int by int results in int on provider. Subclass must implement the castToReal() function to ...