QGIS API Documentation  2.14.11-Essen
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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
NodeList * list() const
QString join(const QString &separator) const
QVariant value() const
The value of the literal.
const Node * rootNode() const
Returns root node of the expression. Root node is null is parsing has failed.
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.
bool isNull() const
virtual QString quotedValue(const QVariant &value, bool &ok)
Returns a quoted attribute value, in the format expected by the provider.
QString name() const
The name of the column.
int indexFromName(const QString &name) const
Look up field's index from name. Returns -1 on error.
Definition: qgsfield.cpp:424
BinaryOperator op() const
bool contains(QChar ch, Qt::CaseSensitivity cs) const
QString & replace(int position, int n, QChar after)
Result
Possible results from expression compilation.
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.
bool toBool() const
virtual Result compileNode(const QgsExpression::Node *node, QString &str)
Compiles an expression node and returns the result of the compilation.
UnaryOperator op() const
Type type() const
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QString toString() const
QList< Node * > list()