QGIS API Documentation  3.6.0-Noosa (5873452)
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  Q_FOREACH ( const QgsExpressionNode *ln, n->list()->list() )
344  {
345  QString s;
346  Result r = compileNode( ln, s );
347  if ( r == Complete || r == Partial )
348  {
349  list << s;
350  if ( r == Partial )
351  inResult = Partial;
352  }
353  else
354  return r;
355  }
356 
357  QString nd;
358  Result rn = compileNode( n->node(), nd );
359  if ( rn != Complete && rn != Partial )
360  return rn;
361 
362  result = QStringLiteral( "%1 %2IN (%3)" ).arg( nd, n->isNotIn() ? QStringLiteral( "NOT " ) : QString(), list.join( ',' ) );
363  return ( inResult == Partial || rn == Partial ) ? Partial : Complete;
364  }
365 
367  {
368  const QgsExpressionNodeFunction *n = static_cast<const QgsExpressionNodeFunction *>( node );
370 
371  // get sql function to compile node expression
372  QString nd = sqlFunctionFromFunctionName( fd->name() );
373  // if no sql function the node can't be compiled
374  if ( nd.isEmpty() )
375  return Fail;
376 
377  // compile arguments
378  QStringList args;
379  Result inResult = Complete;
380  Q_FOREACH ( const QgsExpressionNode *ln, n->args()->list() )
381  {
382  QString s;
383  Result r = compileNode( ln, s );
384  if ( r == Complete || r == Partial )
385  {
386  args << s;
387  if ( r == Partial )
388  inResult = Partial;
389  }
390  else
391  return r;
392  }
393 
394  // update arguments to be adapted to SQL function
395  args = sqlArgumentsFromFunctionName( fd->name(), args );
396 
397  // build result
398  result = QStringLiteral( "%1(%2)" ).arg( nd, args.join( ',' ) );
399  return inResult == Partial ? Partial : Complete;
400  }
401 
403  break;
404 
406  break;
407  }
408 
409  return Fail;
410 }
411 
412 QString QgsSqlExpressionCompiler::sqlFunctionFromFunctionName( const QString &fnName ) const
413 {
414  Q_UNUSED( fnName );
415  return QString();
416 }
417 
418 QStringList QgsSqlExpressionCompiler::sqlArgumentsFromFunctionName( const QString &fnName, const QStringList &fnArgs ) const
419 {
420  Q_UNUSED( fnName );
421  return QStringList( fnArgs );
422 }
423 
424 QString QgsSqlExpressionCompiler::castToReal( const QString &value ) const
425 {
426  Q_UNUSED( value );
427  return QString();
428 }
429 
430 QString QgsSqlExpressionCompiler::castToText( const QString &value ) const
431 {
432  return value;
433 }
434 
435 QString QgsSqlExpressionCompiler::castToInt( const QString &value ) const
436 {
437  Q_UNUSED( value );
438  return QString();
439 }
440 
441 bool QgsSqlExpressionCompiler::nodeIsNullLiteral( const QgsExpressionNode *node ) const
442 {
443  if ( node->nodeType() != QgsExpressionNode::ntLiteral )
444  return false;
445 
446  const QgsExpressionNodeLiteral *nLit = static_cast<const QgsExpressionNodeLiteral *>( node );
447  return nLit->value().isNull();
448 }
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 root node of the expression. Root node is null is parsing has failed.
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 ...