QGIS API Documentation  2.14.11-Essen
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  break;
85 
87  break;
88  }
89 
90  break;
91  }
92 
94  {
95  const QgsExpression::NodeBinaryOperator* n = static_cast<const QgsExpression::NodeBinaryOperator*>( node );
96 
97  QString op;
98  bool partialCompilation = false;
99  bool failOnPartialNode = false;
100  switch ( n->op() )
101  {
102  case QgsExpression::boEQ:
104  {
105  // equality between column refs results in a partial compilation, since provider is performing
106  // case-insensitive matches between strings
107  partialCompilation = true;
108  }
109 
110  op = "=";
111  break;
112 
113  case QgsExpression::boGE:
114  op = ">=";
115  break;
116 
117  case QgsExpression::boGT:
118  op = ">";
119  break;
120 
121  case QgsExpression::boLE:
122  op = "<=";
123  break;
124 
125  case QgsExpression::boLT:
126  op = "<";
127  break;
128 
129  case QgsExpression::boIs:
130  op = "IS";
131  break;
132 
134  op = "IS NOT";
135  failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch );
136  break;
137 
139  op = "LIKE";
140  partialCompilation = mFlags.testFlag( LikeIsCaseInsensitive );
141  break;
142 
144  if ( mFlags.testFlag( LikeIsCaseInsensitive ) )
145  op = "LIKE";
146  else
147  op = "ILIKE";
148  break;
149 
151  op = "NOT LIKE";
152  partialCompilation = mFlags.testFlag( LikeIsCaseInsensitive );
153  failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch );
154  break;
155 
157  failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch );
158  if ( mFlags.testFlag( LikeIsCaseInsensitive ) )
159  op = "NOT LIKE";
160  else
161  op = "NOT ILIKE";
162  break;
163 
164  case QgsExpression::boOr:
165  if ( mFlags.testFlag( NoNullInBooleanLogic ) )
166  {
167  if ( nodeIsNullLiteral( n->opLeft() ) || nodeIsNullLiteral( n->opRight() ) )
168  return Fail;
169  }
170 
171  op = "OR";
172  break;
173 
175  if ( mFlags.testFlag( NoNullInBooleanLogic ) )
176  {
177  if ( nodeIsNullLiteral( n->opLeft() ) || nodeIsNullLiteral( n->opRight() ) )
178  return Fail;
179  }
180 
181  op = "AND";
182  break;
183 
184  case QgsExpression::boNE:
185  failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch );
186  op = "<>";
187  break;
188 
190  op = "*";
191  break;
192 
194  op = "+";
195  break;
196 
198  op = "-";
199  break;
200 
202  return Fail; // handle cast to real
203 
205  op = "%";
206  break;
207 
209  op = "||";
210  break;
211 
213  return Fail; // handle cast to int
214 
216  op = "^";
217  break;
218 
220  op = "~";
221  break;
222  }
223 
224  if ( op.isNull() )
225  return Fail;
226 
227  QString left;
228  Result lr( compileNode( n->opLeft(), left ) );
229 
230  QString right;
231  Result rr( compileNode( n->opRight(), right ) );
232 
233  if ( failOnPartialNode && ( lr == Partial || rr == Partial ) )
234  return Fail;
235 
236  result = '(' + left + ' ' + op + ' ' + right + ')';
237  if ( lr == Complete && rr == Complete )
238  return ( partialCompilation ? Partial : Complete );
239  else if (( lr == Partial && rr == Complete ) || ( lr == Complete && rr == Partial ) || ( lr == Partial && rr == Partial ) )
240  return Partial;
241  else
242  return Fail;
243  }
244 
246  {
247  const QgsExpression::NodeLiteral* n = static_cast<const QgsExpression::NodeLiteral*>( node );
248  bool ok = false;
249  if ( mFlags.testFlag( CaseInsensitiveStringMatch ) && n->value().type() == QVariant::String )
250  {
251  // provider uses case insensitive matching, so if literal was a string then we only have a Partial compilation and need to
252  // double check results using QGIS' expression engine
253  result = quotedValue( n->value(), ok );
254  return ok ? Partial : Fail;
255  }
256  else
257  {
258  result = quotedValue( n->value(), ok );
259  return ok ? Complete : Fail;
260  }
261  }
262 
264  {
265  const QgsExpression::NodeColumnRef* n = static_cast<const QgsExpression::NodeColumnRef*>( node );
266 
267  if ( mFields.indexFromName( n->name() ) == -1 )
268  // Not a provider field
269  return Fail;
270 
271  result = quotedIdentifier( n->name() );
272 
273  return Complete;
274  }
275 
277  {
278  const QgsExpression::NodeInOperator* n = static_cast<const QgsExpression::NodeInOperator*>( node );
279  QStringList list;
280 
281  Result inResult = Complete;
282  Q_FOREACH ( const QgsExpression::Node* ln, n->list()->list() )
283  {
284  QString s;
285  Result r = compileNode( ln, s );
286  if ( r == Complete || r == Partial )
287  {
288  list << s;
289  if ( r == Partial )
290  inResult = Partial;
291  }
292  else
293  return r;
294  }
295 
296  QString nd;
297  Result rn = compileNode( n->node(), nd );
298  if ( rn != Complete && rn != Partial )
299  return rn;
300 
301  result = QString( "%1 %2IN(%3)" ).arg( nd, n->isNotIn() ? "NOT " : "", list.join( "," ) );
302  return ( inResult == Partial || rn == Partial ) ? Partial : Complete;
303  }
304 
307  break;
308  }
309 
310  return Fail;
311 }
312 
313 bool QgsSqlExpressionCompiler::nodeIsNullLiteral( const QgsExpression::Node* node ) const
314 {
315  if ( node->nodeType() != QgsExpression::ntLiteral )
316  return false;
317 
318  const QgsExpression::NodeLiteral* nLit = static_cast<const QgsExpression::NodeLiteral*>( node );
319  return nLit->value().isNull();
320 }
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:187
QString join(const QString &separator) const
Provider does not support using NULL with boolean logic, eg "(...) OR NULL".
bool isNull() const
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:424
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()