QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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 
22  : mResult( None )
23  , mFields( fields )
24  , mFlags( flags )
25 {
26 }
27 
29 {
30  if ( exp->rootNode() )
31  return compileNode( exp->rootNode(), mResult );
32  else
33  return Fail;
34 }
35 
37 {
38  return mResult;
39 }
40 
42 {
43  if ( op == QgsExpressionNodeBinaryOperator::BinaryOperator::boILike ||
44  op == QgsExpressionNodeBinaryOperator::BinaryOperator::boLike ||
45  op == QgsExpressionNodeBinaryOperator::BinaryOperator::boNotILike ||
46  op == QgsExpressionNodeBinaryOperator::BinaryOperator::boNotLike ||
47  op == QgsExpressionNodeBinaryOperator::BinaryOperator::boRegexp )
48  return true;
49  else
50  return false;
51 }
52 
53 QString QgsSqlExpressionCompiler::quotedIdentifier( const QString &identifier )
54 {
55  QString quoted = identifier;
56  quoted.replace( '"', QLatin1String( "\"\"" ) );
57  quoted = quoted.prepend( '\"' ).append( '\"' );
58  return quoted;
59 }
60 
61 QString QgsSqlExpressionCompiler::quotedValue( const QVariant &value, bool &ok )
62 {
63  ok = true;
64 
65  if ( value.isNull() )
66  return QStringLiteral( "NULL" );
67 
68  switch ( value.type() )
69  {
70  case QVariant::Int:
71  case QVariant::LongLong:
72  case QVariant::Double:
73  return value.toString();
74 
75  case QVariant::Bool:
76  return value.toBool() ? QStringLiteral( "TRUE" ) : QStringLiteral( "FALSE" );
77 
78  default:
79  case QVariant::String:
80  QString v = value.toString();
81  v.replace( '\'', QLatin1String( "''" ) );
82  if ( v.contains( '\\' ) )
83  return v.replace( '\\', QLatin1String( "\\\\" ) ).prepend( "E'" ).append( '\'' );
84  else
85  return v.prepend( '\'' ).append( '\'' );
86  }
87 }
88 
90 {
91  switch ( node->nodeType() )
92  {
94  {
95  const QgsExpressionNodeUnaryOperator *n = static_cast<const QgsExpressionNodeUnaryOperator *>( node );
96  switch ( n->op() )
97  {
99  {
100  QString right;
101  if ( compileNode( n->operand(), right ) == Complete )
102  {
103  result = "( NOT " + right + ')';
104  return Complete;
105  }
106 
107  return Fail;
108  }
109 
111  {
112  if ( mFlags.testFlag( NoUnaryMinus ) )
113  return Fail;
114 
115  QString right;
116  if ( compileNode( n->operand(), right ) == Complete )
117  {
118  result = "( - (" + right + "))";
119  return Complete;
120  }
121 
122  return Fail;
123  }
124  }
125 
126  break;
127  }
128 
130  {
131  const QgsExpressionNodeBinaryOperator *n = static_cast<const QgsExpressionNodeBinaryOperator *>( node );
132 
133  QString op;
134  bool partialCompilation = false;
135  bool failOnPartialNode = false;
136  switch ( n->op() )
137  {
140  {
141  // equality between column refs results in a partial compilation, since provider is performing
142  // case-insensitive matches between strings
143  partialCompilation = true;
144  }
145 
146  op = QStringLiteral( "=" );
147  break;
148 
150  op = QStringLiteral( ">=" );
151  break;
152 
154  op = QStringLiteral( ">" );
155  break;
156 
158  op = QStringLiteral( "<=" );
159  break;
160 
162  op = QStringLiteral( "<" );
163  break;
164 
166  op = QStringLiteral( "IS" );
167  break;
168 
170  op = QStringLiteral( "IS NOT" );
171  failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch );
172  break;
173 
175  op = QStringLiteral( "LIKE" );
176  partialCompilation = mFlags.testFlag( LikeIsCaseInsensitive );
177  break;
178 
180  if ( mFlags.testFlag( LikeIsCaseInsensitive ) )
181  op = QStringLiteral( "LIKE" );
182  else
183  op = QStringLiteral( "ILIKE" );
184  break;
185 
187  op = QStringLiteral( "NOT LIKE" );
188  partialCompilation = mFlags.testFlag( LikeIsCaseInsensitive );
189  failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch );
190  break;
191 
193  failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch );
194  if ( mFlags.testFlag( LikeIsCaseInsensitive ) )
195  op = QStringLiteral( "NOT LIKE" );
196  else
197  op = QStringLiteral( "NOT ILIKE" );
198  break;
199 
201  if ( mFlags.testFlag( NoNullInBooleanLogic ) )
202  {
203  if ( nodeIsNullLiteral( n->opLeft() ) || nodeIsNullLiteral( n->opRight() ) )
204  return Fail;
205  }
206 
207  op = QStringLiteral( "OR" );
208  break;
209 
211  if ( mFlags.testFlag( NoNullInBooleanLogic ) )
212  {
213  if ( nodeIsNullLiteral( n->opLeft() ) || nodeIsNullLiteral( n->opRight() ) )
214  return Fail;
215  }
216 
217  op = QStringLiteral( "AND" );
218  break;
219 
221  failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch );
222  op = QStringLiteral( "<>" );
223  break;
224 
226  op = QStringLiteral( "*" );
227  break;
228 
230  op = QStringLiteral( "+" );
231  break;
232 
234  op = QStringLiteral( "-" );
235  break;
236 
238  op = QStringLiteral( "/" );
239  break;
240 
242  op = QStringLiteral( "%" );
243  break;
244 
246  op = QStringLiteral( "||" );
247  break;
248 
250  op = QStringLiteral( "/" );
251  break;
252 
254  op = QStringLiteral( "^" );
255  break;
256 
258  op = QStringLiteral( "~" );
259  break;
260  }
261 
262  if ( op.isNull() )
263  return Fail;
264 
265  QString left;
266  Result lr( compileNode( n->opLeft(), left ) );
267 
268  if ( opIsStringComparison( n ->op() ) )
269  left = castToText( left );
270 
271  QString right;
272  Result rr( compileNode( n->opRight(), right ) );
273 
274  if ( failOnPartialNode && ( lr == Partial || rr == Partial ) )
275  return Fail;
276 
278  {
279  right = castToReal( right );
280  if ( right.isEmpty() )
281  {
282  // not supported
283  return Fail;
284  }
285  }
286 
287  result = '(' + left + ' ' + op + ' ' + right + ')';
289  {
290  result = castToInt( result );
291  if ( result.isEmpty() )
292  {
293  // not supported
294  return Fail;
295  }
296  }
297 
298  if ( lr == Complete && rr == Complete )
299  return ( partialCompilation ? Partial : Complete );
300  else if ( ( lr == Partial && rr == Complete ) || ( lr == Complete && rr == Partial ) || ( lr == Partial && rr == Partial ) )
301  return Partial;
302  else
303  return Fail;
304  }
305 
307  {
308  const QgsExpressionNodeLiteral *n = static_cast<const QgsExpressionNodeLiteral *>( node );
309  bool ok = false;
310  if ( mFlags.testFlag( CaseInsensitiveStringMatch ) && n->value().type() == QVariant::String )
311  {
312  // provider uses case insensitive matching, so if literal was a string then we only have a Partial compilation and need to
313  // double check results using QGIS' expression engine
314  result = quotedValue( n->value(), ok );
315  return ok ? Partial : Fail;
316  }
317  else
318  {
319  result = quotedValue( n->value(), ok );
320  return ok ? Complete : Fail;
321  }
322  }
323 
325  {
326  const QgsExpressionNodeColumnRef *n = static_cast<const QgsExpressionNodeColumnRef *>( node );
327 
328  if ( mFields.indexFromName( n->name() ) == -1 )
329  // Not a provider field
330  return Fail;
331 
332  result = quotedIdentifier( n->name() );
333 
334  return Complete;
335  }
336 
338  {
339  const QgsExpressionNodeInOperator *n = static_cast<const QgsExpressionNodeInOperator *>( node );
340  QStringList list;
341 
342  Result inResult = Complete;
343  const auto constList = n->list()->list();
344  for ( const QgsExpressionNode *ln : constList )
345  {
346  QString s;
347  Result r = compileNode( ln, s );
348  if ( r == Complete || r == Partial )
349  {
350  list << s;
351  if ( r == Partial )
352  inResult = Partial;
353  }
354  else
355  return r;
356  }
357 
358  QString nd;
359  Result rn = compileNode( n->node(), nd );
360  if ( rn != Complete && rn != Partial )
361  return rn;
362 
363  result = QStringLiteral( "%1 %2IN (%3)" ).arg( nd, n->isNotIn() ? QStringLiteral( "NOT " ) : QString(), list.join( ',' ) );
364  return ( inResult == Partial || rn == Partial ) ? Partial : Complete;
365  }
366 
368  {
369  const QgsExpressionNodeFunction *n = static_cast<const QgsExpressionNodeFunction *>( node );
371 
372  // get sql function to compile node expression
373  QString nd = sqlFunctionFromFunctionName( fd->name() );
374  // if no sql function the node can't be compiled
375  if ( nd.isEmpty() )
376  return Fail;
377 
378  // compile arguments
379  QStringList args;
380  Result inResult = Complete;
381  const auto constList = n->args()->list();
382  for ( const QgsExpressionNode *ln : constList )
383  {
384  QString s;
385  Result r = compileNode( ln, s );
386  if ( r == Complete || r == Partial )
387  {
388  args << s;
389  if ( r == Partial )
390  inResult = Partial;
391  }
392  else
393  return r;
394  }
395 
396  // update arguments to be adapted to SQL function
397  args = sqlArgumentsFromFunctionName( fd->name(), args );
398 
399  // build result
400  result = QStringLiteral( "%1(%2)" ).arg( nd, args.join( ',' ) );
401  return inResult == Partial ? Partial : Complete;
402  }
403 
405  break;
406 
408  break;
409  }
410 
411  return Fail;
412 }
413 
414 QString QgsSqlExpressionCompiler::sqlFunctionFromFunctionName( const QString &fnName ) const
415 {
416  Q_UNUSED( fnName )
417  return QString();
418 }
419 
420 QStringList QgsSqlExpressionCompiler::sqlArgumentsFromFunctionName( const QString &fnName, const QStringList &fnArgs ) const
421 {
422  Q_UNUSED( fnName )
423  return QStringList( fnArgs );
424 }
425 
426 QString QgsSqlExpressionCompiler::castToReal( const QString &value ) const
427 {
428  Q_UNUSED( value )
429  return QString();
430 }
431 
432 QString QgsSqlExpressionCompiler::castToText( const QString &value ) const
433 {
434  return value;
435 }
436 
437 QString QgsSqlExpressionCompiler::castToInt( const QString &value ) const
438 {
439  Q_UNUSED( value )
440  return QString();
441 }
442 
443 bool QgsSqlExpressionCompiler::nodeIsNullLiteral( const QgsExpressionNode *node ) const
444 {
445  if ( node->nodeType() != QgsExpressionNode::ntLiteral )
446  return false;
447 
448  const QgsExpressionNodeLiteral *nLit = static_cast<const QgsExpressionNodeLiteral *>( node );
449  return nLit->value().isNull();
450 }
Class for parsing and evaluation of expressions (formerly called "search strings").
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function.
virtual QString castToText(const QString &value) const
Casts a value to a text result.
Provider treats LIKE as case-insensitive.
QgsExpressionNode * opRight() const
Returns the node to the right of the operator.
virtual Result compile(const QgsExpression *exp)
Compiles an expression and returns the result of the compilation.
Container of fields for a vector layer.
Definition: qgsfields.h:42
QgsExpressionNode::NodeList * list() const
Returns the list of nodes to search for matching values within.
Provider cannot handle expression.
QgsExpressionNode * opLeft() const
Returns the node to the left of the operator.
QString name() const
The name of the column.
Provider does not support using NULL with boolean logic, e.g., "(...) OR NULL".
QgsSqlExpressionCompiler(const QgsFields &fields, QgsSqlExpressionCompiler::Flags flags=Flags())
Constructor for expression compiler.
Provider does not unary minus, e.g., " -( 100 * 2 ) = ...".
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:202
virtual QString quotedValue(const QVariant &value, bool &ok)
Returns a quoted attribute value, in the format expected by the provider.
virtual QStringList sqlArgumentsFromFunctionName(const QString &fnName, const QStringList &fnArgs) const
Returns the Arguments for SQL function for the expression function.
BinaryOperator
list of binary operators
An expression node for value IN or NOT IN clauses.
An expression node which takes it value from a feature&#39;s field.
bool opIsStringComparison(QgsExpressionNodeBinaryOperator::BinaryOperator op)
Returns true if op is one of.
Abstract base class for all nodes that can appear in an expression.
An expression node for expression functions.
static const QList< QgsExpressionFunction * > & Functions()
QString name() const
The name of the function.
virtual QString sqlFunctionFromFunctionName(const QString &fnName) const
Returns the SQL function for the expression function.
A abstract base class for defining QgsExpression functions.
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
QgsExpressionNodeUnaryOperator::UnaryOperator op() const
Returns the unary operator.
virtual QString castToInt(const QString &value) const
Casts a value to a integer result.
Result
Possible results from expression compilation.
An expression node for literal values.
Expression was partially compiled, but provider will return extra records and results must be double-...
Expression was successfully compiled and can be completely delegated to provider. ...
const QgsExpressionNode * rootNode() const
Returns the root node of the expression.
virtual QString castToReal(const QString &value) const
Casts a value to a real result.
A unary node is either negative as in boolean (not) or as in numbers (minus).
Provider performs case-insensitive string matching for all strings.
A binary expression operator, which operates on two values.
virtual QString quotedIdentifier(const QString &identifier)
Returns a quoted column identifier, in the format expected by the provider.
virtual Result compileNode(const QgsExpressionNode *node, QString &str)
Compiles an expression node and returns the result of the compilation.
virtual QString result()
Returns the compiled expression string for use by the provider.
QgsExpressionNode * node() const
Returns the expression node.
bool isNotIn() const
Returns true if this node is a "NOT IN" operator, or false if the node is a normal "IN" operator...
virtual QgsExpressionNode::NodeType nodeType() const =0
Gets the type of this node.
QgsExpressionNode * operand() const
Returns the node the operator will operate upon.
QgsExpressionNodeBinaryOperator::BinaryOperator op() const
Returns the binary operator.
QVariant value() const
The value of the literal.
int fnIndex() const
Returns the index of the node&#39;s function.
Dividing int by int results in int on provider. Subclass must implement the castToReal() function to ...