QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
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 
19  : mResult( None )
20  , mFields( fields )
21  , mFlags( flags )
22 {
23 }
24 
26 {
27 
28 }
29 
31 {
32  if ( exp->rootNode() )
33  return compileNode( exp->rootNode(), mResult );
34  else
35  return Fail;
36 }
37 
39 {
40  QString quoted = identifier;
41  quoted.replace( '"', "\"\"" );
42  quoted = quoted.prepend( '\"' ).append( '\"' );
43  return quoted;
44 }
45 
47 {
48  ok = true;
49 
50  if ( value.isNull() )
51  return "NULL";
52 
53  switch ( value.type() )
54  {
55  case QVariant::Int:
56  case QVariant::LongLong:
57  case QVariant::Double:
58  return value.toString();
59 
60  case QVariant::Bool:
61  return value.toBool() ? "TRUE" : "FALSE";
62 
63  default:
64  case QVariant::String:
65  QString v = value.toString();
66  v.replace( '\'', "''" );
67  if ( v.contains( '\\' ) )
68  return v.replace( '\\', "\\\\" ).prepend( "E'" ).append( '\'' );
69  else
70  return v.prepend( '\'' ).append( '\'' );
71  }
72 }
73 
75 {
76  switch ( node->nodeType() )
77  {
79  {
80  const QgsExpression::NodeUnaryOperator* n = static_cast<const QgsExpression::NodeUnaryOperator*>( node );
81  switch ( n->op() )
82  {
84  {
85  QString right;
86  if ( compileNode( n->operand(), right ) == Complete )
87  {
88  result = "( NOT " + right + ')';
89  return Complete;
90  }
91 
92  return Fail;
93  }
94 
96  {
97  if ( mFlags.testFlag( NoUnaryMinus ) )
98  return Fail;
99 
100  QString right;
101  if ( compileNode( n->operand(), right ) == Complete )
102  {
103  result = "( - (" + right + "))";
104  return Complete;
105  }
106 
107  return Fail;
108  }
109  }
110 
111  break;
112  }
113 
115  {
116  const QgsExpression::NodeBinaryOperator* n = static_cast<const QgsExpression::NodeBinaryOperator*>( node );
117 
118  QString op;
119  bool partialCompilation = false;
120  bool failOnPartialNode = false;
121  switch ( n->op() )
122  {
123  case QgsExpression::boEQ:
125  {
126  // equality between column refs results in a partial compilation, since provider is performing
127  // case-insensitive matches between strings
128  partialCompilation = true;
129  }
130 
131  op = "=";
132  break;
133 
134  case QgsExpression::boGE:
135  op = ">=";
136  break;
137 
138  case QgsExpression::boGT:
139  op = ">";
140  break;
141 
142  case QgsExpression::boLE:
143  op = "<=";
144  break;
145 
146  case QgsExpression::boLT:
147  op = "<";
148  break;
149 
150  case QgsExpression::boIs:
151  op = "IS";
152  break;
153 
155  op = "IS NOT";
156  failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch );
157  break;
158 
160  op = "LIKE";
161  partialCompilation = mFlags.testFlag( LikeIsCaseInsensitive );
162  break;
163 
165  if ( mFlags.testFlag( LikeIsCaseInsensitive ) )
166  op = "LIKE";
167  else
168  op = "ILIKE";
169  break;
170 
172  op = "NOT LIKE";
173  partialCompilation = mFlags.testFlag( LikeIsCaseInsensitive );
174  failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch );
175  break;
176 
178  failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch );
179  if ( mFlags.testFlag( LikeIsCaseInsensitive ) )
180  op = "NOT LIKE";
181  else
182  op = "NOT ILIKE";
183  break;
184 
185  case QgsExpression::boOr:
186  if ( mFlags.testFlag( NoNullInBooleanLogic ) )
187  {
188  if ( nodeIsNullLiteral( n->opLeft() ) || nodeIsNullLiteral( n->opRight() ) )
189  return Fail;
190  }
191 
192  op = "OR";
193  break;
194 
196  if ( mFlags.testFlag( NoNullInBooleanLogic ) )
197  {
198  if ( nodeIsNullLiteral( n->opLeft() ) || nodeIsNullLiteral( n->opRight() ) )
199  return Fail;
200  }
201 
202  op = "AND";
203  break;
204 
205  case QgsExpression::boNE:
206  failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch );
207  op = "<>";
208  break;
209 
211  op = "*";
212  break;
213 
215  op = "+";
216  break;
217 
219  op = "-";
220  break;
221 
223  return Fail; // handle cast to real
224 
226  op = "%";
227  break;
228 
230  op = "||";
231  break;
232 
234  return Fail; // handle cast to int
235 
237  op = "^";
238  break;
239 
241  op = "~";
242  break;
243  }
244 
245  if ( op.isNull() )
246  return Fail;
247 
248  QString left;
249  Result lr( compileNode( n->opLeft(), left ) );
250 
251  QString right;
252  Result rr( compileNode( n->opRight(), right ) );
253 
254  if ( failOnPartialNode && ( lr == Partial || rr == Partial ) )
255  return Fail;
256 
257  result = '(' + left + ' ' + op + ' ' + right + ')';
258  if ( lr == Complete && rr == Complete )
259  return ( partialCompilation ? Partial : Complete );
260  else if (( lr == Partial && rr == Complete ) || ( lr == Complete && rr == Partial ) || ( lr == Partial && rr == Partial ) )
261  return Partial;
262  else
263  return Fail;
264  }
265 
267  {
268  const QgsExpression::NodeLiteral* n = static_cast<const QgsExpression::NodeLiteral*>( node );
269  bool ok = false;
270  if ( mFlags.testFlag( CaseInsensitiveStringMatch ) && n->value().type() == QVariant::String )
271  {
272  // provider uses case insensitive matching, so if literal was a string then we only have a Partial compilation and need to
273  // double check results using QGIS' expression engine
274  result = quotedValue( n->value(), ok );
275  return ok ? Partial : Fail;
276  }
277  else
278  {
279  result = quotedValue( n->value(), ok );
280  return ok ? Complete : Fail;
281  }
282  }
283 
285  {
286  const QgsExpression::NodeColumnRef* n = static_cast<const QgsExpression::NodeColumnRef*>( node );
287 
288  if ( mFields.indexFromName( n->name() ) == -1 )
289  // Not a provider field
290  return Fail;
291 
292  result = quotedIdentifier( n->name() );
293 
294  return Complete;
295  }
296 
298  {
299  const QgsExpression::NodeInOperator* n = static_cast<const QgsExpression::NodeInOperator*>( node );
300  QStringList list;
301 
302  Result inResult = Complete;
303  Q_FOREACH ( const QgsExpression::Node* ln, n->list()->list() )
304  {
305  QString s;
306  Result r = compileNode( ln, s );
307  if ( r == Complete || r == Partial )
308  {
309  list << s;
310  if ( r == Partial )
311  inResult = Partial;
312  }
313  else
314  return r;
315  }
316 
317  QString nd;
318  Result rn = compileNode( n->node(), nd );
319  if ( rn != Complete && rn != Partial )
320  return rn;
321 
322  result = QString( "%1 %2IN(%3)" ).arg( nd, n->isNotIn() ? "NOT " : "", list.join( "," ) );
323  return ( inResult == Partial || rn == Partial ) ? Partial : Complete;
324  }
325 
328  break;
329  }
330 
331  return Fail;
332 }
333 
334 bool QgsSqlExpressionCompiler::nodeIsNullLiteral( const QgsExpression::Node* node ) const
335 {
336  if ( node->nodeType() != QgsExpression::ntLiteral )
337  return false;
338 
339  const QgsExpression::NodeLiteral* nLit = static_cast<const QgsExpression::NodeLiteral*>( node );
340  return nLit->value().isNull();
341 }
Class for parsing and evaluation of expressions (formerly called "search strings").
virtual NodeType nodeType() const =0
Abstract virtual that returns the type of this node.
QString & append(QChar ch)
Provider treats LIKE as case-insensitive.
QString & prepend(QChar ch)
virtual Result compile(const QgsExpression *exp)
Compiles an expression and returns the result of the compilation.
Container of fields for a vector layer.
Definition: qgsfield.h:252
QString join(const QString &separator) const
Provider does not support using NULL with boolean logic, eg "(...) OR NULL".
bool isNull() const
Provider does not unary minus, eg " -( 100 * 2 ) = ...".
QgsSqlExpressionCompiler(const QgsFields &fields, const Flags &flags=Flags())
Constructor for expression compiler.
const Node * rootNode() const
Returns root node of the expression. Root node is null is parsing has failed.
bool isNull() const
virtual QString quotedValue(const QVariant &value, bool &ok)
Returns a quoted attribute value, in the format expected by the provider.
virtual QString result()
Returns the compiled expression string for use by the provider.
bool contains(QChar ch, Qt::CaseSensitivity cs) const
QString & replace(int position, int n, QChar after)
QString name() const
The name of the column.
int indexFromName(const QString &name) const
Look up field&#39;s index from name. Returns -1 on error.
Definition: qgsfield.cpp:461
Result
Possible results from expression compilation.
Provider performs case-insensitive string matching for all strings.
QVariant value() const
The value of the literal.
virtual QString quotedIdentifier(const QString &identifier)
Returns a quoted column identifier, in the format expected by the provider.
bool toBool() const
virtual Result compileNode(const QgsExpression::Node *node, QString &str)
Compiles an expression node and returns the result of the compilation.
Type type() const
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QString toString() const
QList< Node * > list()