QGIS API Documentation  3.0.2-Girona (307d082)
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 
41 QString QgsSqlExpressionCompiler::quotedIdentifier( const QString &identifier )
42 {
43  QString quoted = identifier;
44  quoted.replace( '"', QLatin1String( "\"\"" ) );
45  quoted = quoted.prepend( '\"' ).append( '\"' );
46  return quoted;
47 }
48 
49 QString QgsSqlExpressionCompiler::quotedValue( const QVariant &value, bool &ok )
50 {
51  ok = true;
52 
53  if ( value.isNull() )
54  return QStringLiteral( "NULL" );
55 
56  switch ( value.type() )
57  {
58  case QVariant::Int:
59  case QVariant::LongLong:
60  case QVariant::Double:
61  return value.toString();
62 
63  case QVariant::Bool:
64  return value.toBool() ? QStringLiteral( "TRUE" ) : QStringLiteral( "FALSE" );
65 
66  default:
67  case QVariant::String:
68  QString v = value.toString();
69  v.replace( '\'', QLatin1String( "''" ) );
70  if ( v.contains( '\\' ) )
71  return v.replace( '\\', QLatin1String( "\\\\" ) ).prepend( "E'" ).append( '\'' );
72  else
73  return v.prepend( '\'' ).append( '\'' );
74  }
75 }
76 
78 {
79  switch ( node->nodeType() )
80  {
81  case QgsExpressionNode::ntUnaryOperator:
82  {
83  const QgsExpressionNodeUnaryOperator *n = static_cast<const QgsExpressionNodeUnaryOperator *>( node );
84  switch ( n->op() )
85  {
86  case QgsExpressionNodeUnaryOperator::uoNot:
87  {
88  QString right;
89  if ( compileNode( n->operand(), right ) == Complete )
90  {
91  result = "( NOT " + right + ')';
92  return Complete;
93  }
94 
95  return Fail;
96  }
97 
98  case QgsExpressionNodeUnaryOperator::uoMinus:
99  {
100  if ( mFlags.testFlag( NoUnaryMinus ) )
101  return Fail;
102 
103  QString right;
104  if ( compileNode( n->operand(), right ) == Complete )
105  {
106  result = "( - (" + right + "))";
107  return Complete;
108  }
109 
110  return Fail;
111  }
112  }
113 
114  break;
115  }
116 
117  case QgsExpressionNodeBinaryOperator::ntBinaryOperator:
118  {
119  const QgsExpressionNodeBinaryOperator *n = static_cast<const QgsExpressionNodeBinaryOperator *>( node );
120 
121  QString op;
122  bool partialCompilation = false;
123  bool failOnPartialNode = false;
124  switch ( n->op() )
125  {
126  case QgsExpressionNodeBinaryOperator::boEQ:
127  if ( mFlags.testFlag( CaseInsensitiveStringMatch ) && n->opLeft()->nodeType() == QgsExpressionNode::ntColumnRef && n->opRight()->nodeType() == QgsExpressionNode::ntColumnRef )
128  {
129  // equality between column refs results in a partial compilation, since provider is performing
130  // case-insensitive matches between strings
131  partialCompilation = true;
132  }
133 
134  op = QStringLiteral( "=" );
135  break;
136 
137  case QgsExpressionNodeBinaryOperator::boGE:
138  op = QStringLiteral( ">=" );
139  break;
140 
141  case QgsExpressionNodeBinaryOperator::boGT:
142  op = QStringLiteral( ">" );
143  break;
144 
145  case QgsExpressionNodeBinaryOperator::boLE:
146  op = QStringLiteral( "<=" );
147  break;
148 
149  case QgsExpressionNodeBinaryOperator::boLT:
150  op = QStringLiteral( "<" );
151  break;
152 
153  case QgsExpressionNodeBinaryOperator::boIs:
154  op = QStringLiteral( "IS" );
155  break;
156 
157  case QgsExpressionNodeBinaryOperator::boIsNot:
158  op = QStringLiteral( "IS NOT" );
159  failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch );
160  break;
161 
162  case QgsExpressionNodeBinaryOperator::boLike:
163  op = QStringLiteral( "LIKE" );
164  partialCompilation = mFlags.testFlag( LikeIsCaseInsensitive );
165  break;
166 
167  case QgsExpressionNodeBinaryOperator::boILike:
168  if ( mFlags.testFlag( LikeIsCaseInsensitive ) )
169  op = QStringLiteral( "LIKE" );
170  else
171  op = QStringLiteral( "ILIKE" );
172  break;
173 
174  case QgsExpressionNodeBinaryOperator::boNotLike:
175  op = QStringLiteral( "NOT LIKE" );
176  partialCompilation = mFlags.testFlag( LikeIsCaseInsensitive );
177  failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch );
178  break;
179 
180  case QgsExpressionNodeBinaryOperator::boNotILike:
181  failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch );
182  if ( mFlags.testFlag( LikeIsCaseInsensitive ) )
183  op = QStringLiteral( "NOT LIKE" );
184  else
185  op = QStringLiteral( "NOT ILIKE" );
186  break;
187 
188  case QgsExpressionNodeBinaryOperator::boOr:
189  if ( mFlags.testFlag( NoNullInBooleanLogic ) )
190  {
191  if ( nodeIsNullLiteral( n->opLeft() ) || nodeIsNullLiteral( n->opRight() ) )
192  return Fail;
193  }
194 
195  op = QStringLiteral( "OR" );
196  break;
197 
198  case QgsExpressionNodeBinaryOperator::boAnd:
199  if ( mFlags.testFlag( NoNullInBooleanLogic ) )
200  {
201  if ( nodeIsNullLiteral( n->opLeft() ) || nodeIsNullLiteral( n->opRight() ) )
202  return Fail;
203  }
204 
205  op = QStringLiteral( "AND" );
206  break;
207 
208  case QgsExpressionNodeBinaryOperator::boNE:
209  failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch );
210  op = QStringLiteral( "<>" );
211  break;
212 
213  case QgsExpressionNodeBinaryOperator::boMul:
214  op = QStringLiteral( "*" );
215  break;
216 
217  case QgsExpressionNodeBinaryOperator::boPlus:
218  op = QStringLiteral( "+" );
219  break;
220 
221  case QgsExpressionNodeBinaryOperator::boMinus:
222  op = QStringLiteral( "-" );
223  break;
224 
225  case QgsExpressionNodeBinaryOperator::boDiv:
226  op = QStringLiteral( "/" );
227  break;
228 
229  case QgsExpressionNodeBinaryOperator::boMod:
230  op = QStringLiteral( "%" );
231  break;
232 
233  case QgsExpressionNodeBinaryOperator::boConcat:
234  op = QStringLiteral( "||" );
235  break;
236 
237  case QgsExpressionNodeBinaryOperator::boIntDiv:
238  op = QStringLiteral( "/" );
239  break;
240 
241  case QgsExpressionNodeBinaryOperator::boPow:
242  op = QStringLiteral( "^" );
243  break;
244 
245  case QgsExpressionNodeBinaryOperator::boRegexp:
246  op = QStringLiteral( "~" );
247  break;
248  }
249 
250  if ( op.isNull() )
251  return Fail;
252 
253  QString left;
254  Result lr( compileNode( n->opLeft(), left ) );
255 
256  QString right;
257  Result rr( compileNode( n->opRight(), right ) );
258 
259  if ( failOnPartialNode && ( lr == Partial || rr == Partial ) )
260  return Fail;
261 
262  if ( n->op() == QgsExpressionNodeBinaryOperator::boDiv && mFlags.testFlag( IntegerDivisionResultsInInteger ) )
263  {
264  right = castToReal( right );
265  if ( right.isEmpty() )
266  {
267  // not supported
268  return Fail;
269  }
270  }
271 
272  result = '(' + left + ' ' + op + ' ' + right + ')';
273  if ( n->op() == QgsExpressionNodeBinaryOperator::boIntDiv )
274  {
275  result = castToInt( result );
276  if ( result.isEmpty() )
277  {
278  // not supported
279  return Fail;
280  }
281  }
282 
283  if ( lr == Complete && rr == Complete )
284  return ( partialCompilation ? Partial : Complete );
285  else if ( ( lr == Partial && rr == Complete ) || ( lr == Complete && rr == Partial ) || ( lr == Partial && rr == Partial ) )
286  return Partial;
287  else
288  return Fail;
289  }
290 
291  case QgsExpressionNode::ntLiteral:
292  {
293  const QgsExpressionNodeLiteral *n = static_cast<const QgsExpressionNodeLiteral *>( node );
294  bool ok = false;
295  if ( mFlags.testFlag( CaseInsensitiveStringMatch ) && n->value().type() == QVariant::String )
296  {
297  // provider uses case insensitive matching, so if literal was a string then we only have a Partial compilation and need to
298  // double check results using QGIS' expression engine
299  result = quotedValue( n->value(), ok );
300  return ok ? Partial : Fail;
301  }
302  else
303  {
304  result = quotedValue( n->value(), ok );
305  return ok ? Complete : Fail;
306  }
307  }
308 
309  case QgsExpressionNode::ntColumnRef:
310  {
311  const QgsExpressionNodeColumnRef *n = static_cast<const QgsExpressionNodeColumnRef *>( node );
312 
313  if ( mFields.indexFromName( n->name() ) == -1 )
314  // Not a provider field
315  return Fail;
316 
317  result = quotedIdentifier( n->name() );
318 
319  return Complete;
320  }
321 
322  case QgsExpressionNode::ntInOperator:
323  {
324  const QgsExpressionNodeInOperator *n = static_cast<const QgsExpressionNodeInOperator *>( node );
325  QStringList list;
326 
327  Result inResult = Complete;
328  Q_FOREACH ( const QgsExpressionNode *ln, n->list()->list() )
329  {
330  QString s;
331  Result r = compileNode( ln, s );
332  if ( r == Complete || r == Partial )
333  {
334  list << s;
335  if ( r == Partial )
336  inResult = Partial;
337  }
338  else
339  return r;
340  }
341 
342  QString nd;
343  Result rn = compileNode( n->node(), nd );
344  if ( rn != Complete && rn != Partial )
345  return rn;
346 
347  result = QStringLiteral( "%1 %2IN (%3)" ).arg( nd, n->isNotIn() ? QStringLiteral( "NOT " ) : QString(), list.join( ',' ) );
348  return ( inResult == Partial || rn == Partial ) ? Partial : Complete;
349  }
350 
351  case QgsExpressionNode::ntFunction:
352  {
353  const QgsExpressionNodeFunction *n = static_cast<const QgsExpressionNodeFunction *>( node );
354  QgsExpressionFunction *fd = QgsExpression::Functions()[n->fnIndex()];
355 
356  // get sql function to compile node expression
357  QString nd = sqlFunctionFromFunctionName( fd->name() );
358  // if no sql function the node can't be compiled
359  if ( nd.isEmpty() )
360  return Fail;
361 
362  // compile arguments
363  QStringList args;
364  Result inResult = Complete;
365  Q_FOREACH ( const QgsExpressionNode *ln, n->args()->list() )
366  {
367  QString s;
368  Result r = compileNode( ln, s );
369  if ( r == Complete || r == Partial )
370  {
371  args << s;
372  if ( r == Partial )
373  inResult = Partial;
374  }
375  else
376  return r;
377  }
378 
379  // update arguments to be adapted to SQL function
380  args = sqlArgumentsFromFunctionName( fd->name(), args );
381 
382  // build result
383  result = QStringLiteral( "%1(%2)" ).arg( nd, args.join( ',' ) );
384  return inResult == Partial ? Partial : Complete;
385  }
386 
387  case QgsExpressionNode::ntCondition:
388  break;
389  }
390 
391  return Fail;
392 }
393 
394 QString QgsSqlExpressionCompiler::sqlFunctionFromFunctionName( const QString &fnName ) const
395 {
396  Q_UNUSED( fnName );
397  return QString();
398 }
399 
400 QStringList QgsSqlExpressionCompiler::sqlArgumentsFromFunctionName( const QString &fnName, const QStringList &fnArgs ) const
401 {
402  Q_UNUSED( fnName );
403  return QStringList( fnArgs );
404 }
405 
406 QString QgsSqlExpressionCompiler::castToReal( const QString &value ) const
407 {
408  Q_UNUSED( value );
409  return QString();
410 }
411 
412 QString QgsSqlExpressionCompiler::castToInt( const QString &value ) const
413 {
414  Q_UNUSED( value );
415  return QString();
416 }
417 
418 bool QgsSqlExpressionCompiler::nodeIsNullLiteral( const QgsExpressionNode *node ) const
419 {
420  if ( node->nodeType() != QgsExpressionNode::ntLiteral )
421  return false;
422 
423  const QgsExpressionNodeLiteral *nLit = static_cast<const QgsExpressionNodeLiteral *>( node );
424  return nLit->value().isNull();
425 }
Provider treats LIKE as case-insensitive.
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
Provider cannot handle expression.
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
Get the field index from the field name.
Definition: qgsfields.cpp:184
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
Return the Arguments for SQL function for the expression function.
virtual QString sqlFunctionFromFunctionName(const QString &fnName) const
Return the SQL function for the expression function.
virtual QString castToInt(const QString &value) const
Casts a value to a integer result.
Result
Possible results from expression compilation.
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. ...
virtual QString castToReal(const QString &value) const
Casts a value to a real result.
Provider performs case-insensitive string matching for all strings.
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.
Dividing int by int results in int on provider. Subclass must implement the castToReal() function to ...