QGIS API Documentation  3.0.2-Girona (307d082)
qgssqlstatement.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgssqlstatement.cpp
3  -------------------
4  begin : April 2016
5  copyright : (C) 2011 by Martin Dobias
6  copyright : (C) 2016 by Even Rouault
7  email : even.rouault at spatialys.com
8  ***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 
17 #include "qgssqlstatement.h"
18 
19 #include <cmath>
20 #include <limits>
21 
22 static const QRegExp IDENTIFIER_RE( "^[A-Za-z_\x80-\xff][A-Za-z0-9_\x80-\xff]*$" );
23 
24 // from parser
25 extern QgsSQLStatement::Node *parse( const QString &str, QString &parserErrorMsg );
26 
28 // operators
29 
31 {
32  // this must correspond (number and order of element) to the declaration of the enum BinaryOperator
33  "OR", "AND",
34  "=", "<>", "<=", ">=", "<", ">", "LIKE", "NOT LIKE", "ILIKE", "NOT ILIKE", "IS", "IS NOT",
35  "+", "-", "*", "/", "//", "%", "^",
36  "||"
37 };
38 
40 {
41  // this must correspond (number and order of element) to the declaration of the enum UnaryOperator
42  "NOT", "-"
43 };
44 
45 const char *QgsSQLStatement::JOIN_TYPE_TEXT[] =
46 {
47  // this must correspond (number and order of element) to the declaration of the enum JoinType
48  "", "LEFT", "LEFT OUTER", "RIGHT", "RIGHT OUTER", "CROSS", "INNER", "FULL"
49 };
50 
52 
54 {
55  if ( !mStatement.isNull() )
56  return mStatement;
57  else
58  return dump();
59 }
60 
61 QString QgsSQLStatement::dump() const
62 {
63  if ( !mRootNode )
64  return tr( "(no root)" );
65 
66  return mRootNode->dump();
67 }
68 
69 QString QgsSQLStatement::quotedIdentifier( QString name )
70 {
71  return QStringLiteral( "\"%1\"" ).arg( name.replace( '\"', QLatin1String( "\"\"" ) ) );
72 }
73 
74 QString QgsSQLStatement::quotedIdentifierIfNeeded( const QString &name )
75 {
76  // This might not be complete, but it must be at least what we recognize
77  static const char *const RESERVED_KEYWORDS[] =
78  {
79  "AND", "OR", "NOT", "LIKE", "IN", "IS", "BETWEEN", "NULL", "SELECT", "ALL", "DISTINCT", "CAST", "AS",
80  "FROM", "JOIN", "ON", "USING", "WHERE", "ORDER", "BY", "ASC", "DESC",
81  "LEFT", "RIGHT", "INNER", "OUTER", "CROSS", "FULL", "NATURAL", "UNION",
82  "OFFSET", "LIMIT", "GROUP", "HAVING"
83  };
84 
85  for ( size_t i = 0; i < sizeof( RESERVED_KEYWORDS ) / sizeof( RESERVED_KEYWORDS[0] ); ++i )
86  {
87  if ( name.compare( QString( RESERVED_KEYWORDS[i] ), Qt::CaseInsensitive ) == 0 )
88  {
89  return quotedIdentifier( name );
90  }
91  }
92  return IDENTIFIER_RE.exactMatch( name ) ? name : quotedIdentifier( name );
93 }
94 
96 {
97  if ( text.length() >= 2 && text[0] == '"' && text[text.length() - 1] == '"' )
98  {
99  // strip double quotes on start,end
100  text = text.mid( 1, text.length() - 2 );
101 
102  // make single "double quotes" from double "double quotes"
103  text.replace( QLatin1String( "\"\"" ), QLatin1String( "\"" ) );
104  }
105  return text;
106 }
107 
108 QString QgsSQLStatement::quotedString( QString text )
109 {
110  text.replace( '\'', QLatin1String( "''" ) );
111  text.replace( '\\', QLatin1String( "\\\\" ) );
112  text.replace( '\n', QLatin1String( "\\n" ) );
113  text.replace( '\t', QLatin1String( "\\t" ) );
114  return QStringLiteral( "'%1'" ).arg( text );
115 }
116 
117 QgsSQLStatement::QgsSQLStatement( const QString &expr )
118 {
120  mStatement = expr;
121 }
122 
124 {
126  mStatement = other.mStatement;
127 }
128 
130 {
131  if ( &other != this )
132  {
133  delete mRootNode;
134  mParserErrorString.clear();
136  mStatement = other.mStatement;
137  }
138  return *this;
139 }
140 
142 {
143  delete mRootNode;
144 }
145 
146 bool QgsSQLStatement::hasParserError() const { return !mParserErrorString.isNull(); }
147 
149 
151 {
152  if ( mRootNode )
153  mRootNode->accept( v );
154 }
155 
157 {
158  return mRootNode;
159 }
160 
162 {
163  Q_FOREACH ( QgsSQLStatement::NodeTableDef *table, n.tables() )
164  {
165  table->accept( *this );
166  }
167  Q_FOREACH ( QgsSQLStatement::NodeSelectedColumn *column, n.columns() )
168  {
169  column->accept( *this );
170  }
171  Q_FOREACH ( QgsSQLStatement::NodeJoin *join, n.joins() )
172  {
173  join->accept( *this );
174  }
175  QgsSQLStatement::Node *where = n.where();
176  if ( where )
177  where->accept( *this );
178  Q_FOREACH ( QgsSQLStatement::NodeColumnSorted *column, n.orderBy() )
179  {
180  column->accept( *this );
181  }
182 }
183 
185 {
186  n.tableDef()->accept( *this );
187  QgsSQLStatement::Node *expr = n.onExpr();
188  if ( expr )
189  expr->accept( *this );
190 }
191 
198 {
199  public:
200  typedef QPair<QString, QString> TableColumnPair;
201 
206 
207  void visit( const QgsSQLStatement::NodeColumnRef &n ) override;
208  void visit( const QgsSQLStatement::NodeTableDef &n ) override;
209 
210  QSet<QString> tableNamesDeclared;
211  QSet<TableColumnPair> tableNamesReferenced;
212 };
213 
215 {
216  if ( !n.tableName().isEmpty() )
217  tableNamesReferenced.insert( TableColumnPair( n.tableName(), n.name() ) );
219 }
220 
222 {
223  tableNamesDeclared.insert( n.alias().isEmpty() ? n.name() : n.alias() );
225 }
226 
227 bool QgsSQLStatement::doBasicValidationChecks( QString &errorMsgOut ) const
228 {
229  errorMsgOut.clear();
230  if ( !mRootNode )
231  {
232  errorMsgOut = tr( "No root node" );
233  return false;
234  }
236  mRootNode->accept( v );
237 
239  {
240  if ( !v.tableNamesDeclared.contains( pair.first ) )
241  {
242  if ( !errorMsgOut.isEmpty() )
243  errorMsgOut += QLatin1String( " " );
244  errorMsgOut += QString( tr( "Table %1 is referenced by column %2, but not selected in FROM / JOIN." ) ).arg( pair.first, pair.second );
245  }
246  }
247 
248  return errorMsgOut.isEmpty();
249 }
250 
252 // nodes
253 
255 {
256  NodeList *nl = new NodeList;
257  Q_FOREACH ( Node *node, mList )
258  {
259  nl->mList.append( node->clone() );
260  }
261 
262  return nl;
263 }
264 
266 {
267  QString msg;
268  bool first = true;
269  Q_FOREACH ( Node *n, mList )
270  {
271  if ( !first ) msg += QLatin1String( ", " );
272  else first = false;
273  msg += n->dump();
274  }
275  return msg;
276 }
277 
278 
279 //
280 
282 {
283  return QStringLiteral( "%1 %2" ).arg( UNARY_OPERATOR_TEXT[mOp], mOperand->dump() );
284 }
285 
287 {
288  return new NodeUnaryOperator( mOp, mOperand->clone() );
289 }
290 
291 //
292 
294 {
295  // see left/right in qgsexpressionparser.yy
296  switch ( mOp )
297  {
298  case boOr:
299  return 1;
300 
301  case boAnd:
302  return 2;
303 
304  case boEQ:
305  case boNE:
306  case boLE:
307  case boGE:
308  case boLT:
309  case boGT:
310  case boLike:
311  case boILike:
312  case boNotLike:
313  case boNotILike:
314  case boIs:
315  case boIsNot:
316  return 3;
317 
318  case boPlus:
319  case boMinus:
320  return 4;
321 
322  case boMul:
323  case boDiv:
324  case boIntDiv:
325  case boMod:
326  return 5;
327 
328  case boPow:
329  return 6;
330 
331  case boConcat:
332  return 7;
333  }
334  Q_ASSERT( false && "unexpected binary operator" );
335  return -1;
336 }
337 
339 {
340  // see left/right in qgsexpressionparser.yy
341  switch ( mOp )
342  {
343  case boOr:
344  case boAnd:
345  case boEQ:
346  case boNE:
347  case boLE:
348  case boGE:
349  case boLT:
350  case boGT:
351  case boLike:
352  case boILike:
353  case boNotLike:
354  case boNotILike:
355  case boIs:
356  case boIsNot:
357  case boPlus:
358  case boMinus:
359  case boMul:
360  case boDiv:
361  case boIntDiv:
362  case boMod:
363  case boConcat:
364  return true;
365 
366  case boPow:
367  return false;
368  }
369  Q_ASSERT( false && "unexpected binary operator" );
370  return false;
371 }
372 
374 {
378 
379  QString rdump( mOpRight->dump() );
380 
381  // avoid dumping "IS (NOT ...)" as "IS NOT ..."
382  if ( mOp == boIs && ruOp && ruOp->op() == uoNot )
383  {
384  rdump.prepend( '(' ).append( ')' );
385  }
386 
387  QString fmt;
388  if ( leftAssociative() )
389  {
390  fmt += lOp && ( lOp->precedence() < precedence() ) ? "(%1)" : "%1";
391  fmt += QLatin1String( " %2 " );
392  fmt += rOp && ( rOp->precedence() <= precedence() ) ? "(%3)" : "%3";
393  }
394  else
395  {
396  fmt += lOp && ( lOp->precedence() <= precedence() ) ? "(%1)" : "%1";
397  fmt += QLatin1String( " %2 " );
398  fmt += rOp && ( rOp->precedence() < precedence() ) ? "(%3)" : "%3";
399  }
400 
401  return fmt.arg( mOpLeft->dump(), BINARY_OPERATOR_TEXT[mOp], rdump );
402 }
403 
405 {
406  return new NodeBinaryOperator( mOp, mOpLeft->clone(), mOpRight->clone() );
407 }
408 
409 //
410 
412 {
413  return QStringLiteral( "%1 %2IN (%3)" ).arg( mNode->dump(), mNotIn ? "NOT " : "", mList->dump() );
414 }
415 
417 {
418  return new NodeInOperator( mNode->clone(), mList->clone(), mNotIn );
419 }
420 
421 //
422 
424 {
425  return QStringLiteral( "%1 %2BETWEEN %3 AND %4" ).arg( mNode->dump(), mNotBetween ? "NOT " : "", mMinVal->dump(), mMaxVal->dump() );
426 }
427 
429 {
430  return new NodeBetweenOperator( mNode->clone(), mMinVal->clone(), mMaxVal->clone(), mNotBetween );
431 }
432 
433 //
434 
436 {
437  return QStringLiteral( "%1(%2)" ).arg( mName, mArgs ? mArgs->dump() : QString() ); // function
438 }
439 
441 {
442  return new NodeFunction( mName, mArgs ? mArgs->clone() : nullptr );
443 }
444 
445 //
446 
448 {
449  if ( mValue.isNull() )
450  return QStringLiteral( "NULL" );
451 
452  switch ( mValue.type() )
453  {
454  case QVariant::Int:
455  return QString::number( mValue.toInt() );
456  case QVariant::LongLong:
457  return QString::number( mValue.toLongLong() );
458  case QVariant::Double:
459  return QString::number( mValue.toDouble() );
460  case QVariant::String:
461  return quotedString( mValue.toString() );
462  case QVariant::Bool:
463  return mValue.toBool() ? "TRUE" : "FALSE";
464  default:
465  return tr( "[unsupported type: %1; value: %2]" ).arg( mValue.typeName(), mValue.toString() );
466  }
467 }
468 
470 {
471  return new NodeLiteral( mValue );
472 }
473 
474 //
475 
477 {
478  QString ret;
479  if ( mDistinct )
480  ret += QLatin1String( "DISTINCT " );
481  if ( !mTableName.isEmpty() )
482  {
483  ret += quotedIdentifierIfNeeded( mTableName );
484  ret += '.';
485  }
486  ret += ( mStar ) ? mName : quotedIdentifierIfNeeded( mName );
487  return ret;
488 }
489 
491 {
492  return cloneThis();
493 }
494 
496 {
497  NodeColumnRef *newColumnRef = new NodeColumnRef( mTableName, mName, mStar );
498  newColumnRef->setDistinct( mDistinct );
499  return newColumnRef;
500 }
501 
502 //
503 
505 {
506  QString ret;
507  ret += mColumnNode->dump();
508  if ( !mAlias.isEmpty() )
509  {
510  ret += QLatin1String( " AS " );
511  ret += quotedIdentifierIfNeeded( mAlias );
512  }
513  return ret;
514 }
515 
517 {
518  NodeSelectedColumn *newObj = new NodeSelectedColumn( mColumnNode->clone() );
519  newObj->setAlias( mAlias );
520  return newObj;
521 }
522 
524 {
525  return cloneThis();
526 }
527 //
528 
530 {
531  QString ret;
532  ret = quotedIdentifierIfNeeded( mName );
533  if ( !mAlias.isEmpty() )
534  {
535  ret += QLatin1String( " AS " );
536  ret += quotedIdentifierIfNeeded( mAlias );
537  }
538  return ret;
539 }
540 
542 {
543  return new NodeTableDef( mName, mAlias );
544 }
545 
547 {
548  return cloneThis();
549 }
550 
551 //
552 
554 {
555  qDeleteAll( mTableList );
556  qDeleteAll( mColumns );
557  qDeleteAll( mJoins );
558  delete mWhere;
559  qDeleteAll( mOrderBy );
560 }
561 
563 {
564  QString ret = QStringLiteral( "SELECT " );
565  if ( mDistinct )
566  ret += QLatin1String( "DISTINCT " );
567  bool bFirstColumn = true;
568  Q_FOREACH ( QgsSQLStatement::NodeSelectedColumn *column, mColumns )
569  {
570  if ( !bFirstColumn )
571  ret += QLatin1String( ", " );
572  bFirstColumn = false;
573  ret += column->dump();
574  }
575  ret += QLatin1String( " FROM " );
576  bool bFirstTable = true;
577  Q_FOREACH ( QgsSQLStatement::NodeTableDef *table, mTableList )
578  {
579  if ( !bFirstTable )
580  ret += QLatin1String( ", " );
581  bFirstTable = false;
582  ret += table->dump();
583  }
584  Q_FOREACH ( QgsSQLStatement::NodeJoin *join, mJoins )
585  {
586  ret += ' ';
587  ret += join->dump();
588  }
589  if ( mWhere )
590  {
591  ret += QLatin1String( " WHERE " );
592  ret += mWhere->dump();
593  }
594  if ( !mOrderBy.isEmpty() )
595  {
596  ret += QLatin1String( " ORDER BY " );
597  bool bFirst = true;
598  Q_FOREACH ( QgsSQLStatement::NodeColumnSorted *orderBy, mOrderBy )
599  {
600  if ( !bFirst )
601  ret += QLatin1String( ", " );
602  bFirst = false;
603  ret += orderBy->dump();
604  }
605  }
606  return ret;
607 }
608 
610 {
611  QList<QgsSQLStatement::NodeSelectedColumn *> newColumnList;
612  Q_FOREACH ( QgsSQLStatement::NodeSelectedColumn *column, mColumns )
613  {
614  newColumnList.push_back( column->cloneThis() );
615  }
616  QList<QgsSQLStatement::NodeTableDef *> newTableList;
617  Q_FOREACH ( QgsSQLStatement::NodeTableDef *table, mTableList )
618  {
619  newTableList.push_back( table->cloneThis() );
620  }
621  QgsSQLStatement::NodeSelect *newSelect = new NodeSelect( newTableList, newColumnList, mDistinct );
622  Q_FOREACH ( QgsSQLStatement::NodeJoin *join, mJoins )
623  {
624  newSelect->appendJoin( join->cloneThis() );
625  }
626  if ( mWhere )
627  {
628  newSelect->setWhere( mWhere->clone() );
629  }
630  QList<QgsSQLStatement::NodeColumnSorted *> newOrderByList;
631  Q_FOREACH ( QgsSQLStatement::NodeColumnSorted *columnSorted, mOrderBy )
632  {
633  newOrderByList.push_back( columnSorted->cloneThis() );
634  }
635  newSelect->setOrderBy( newOrderByList );
636  return newSelect;
637 }
638 
639 //
640 
642 {
643  QString ret;
644  if ( mType != jtDefault )
645  {
646  ret += JOIN_TYPE_TEXT[mType];
647  ret += QLatin1String( " " );
648  }
649  ret += QLatin1String( "JOIN " );
650  ret += mTableDef->dump();
651  if ( mOnExpr )
652  {
653  ret += QLatin1String( " ON " );
654  ret += mOnExpr->dump();
655  }
656  else
657  {
658  ret += QLatin1String( " USING (" );
659  bool first = true;
660  Q_FOREACH ( QString column, mUsingColumns )
661  {
662  if ( !first )
663  ret += QLatin1String( ", " );
664  first = false;
665  ret += quotedIdentifierIfNeeded( column );
666  }
667  ret += QLatin1String( ")" );
668  }
669  return ret;
670 }
671 
673 {
674  return cloneThis();
675 }
676 
678 {
679  if ( mOnExpr )
680  return new NodeJoin( mTableDef->cloneThis(), mOnExpr->clone(), mType );
681  else
682  return new NodeJoin( mTableDef->cloneThis(), mUsingColumns, mType );
683 }
684 
685 //
686 
688 {
689  QString ret;
690  ret = mColumn->dump();
691  if ( !mAsc )
692  ret += QLatin1String( " DESC" );
693  return ret;
694 }
695 
697 {
698  return cloneThis();
699 }
700 
702 {
703  return new NodeColumnSorted( mColumn->cloneThis(), mAsc );
704 }
705 
706 //
707 
709 {
710  QString ret( QStringLiteral( "CAST(" ) );
711  ret += mNode->dump();
712  ret += QLatin1String( " AS " );
713  ret += mType;
714  ret += ')';
715  return ret;
716 }
717 
719 {
720  return new NodeCast( mNode->clone(), mType );
721 }
QgsSQLStatement::Node * clone() const override
Generate a clone of this node.
QgsSQLStatement::NodeTableDef * cloneThis() const
Clone with same type return.
static const char * JOIN_TYPE_TEXT[]
QgsSQLStatement::Node * onExpr() const
On expression. Will be nullptr if usingColumns() is not empty.
bool leftAssociative() const
Is left associative ?
void appendJoin(QgsSQLStatement::NodeJoin *join)
Append a join.
Function with a name and arguments node.
QgsSQLStatement::Node * clone() const override
Generate a clone of this node.
QgsSQLStatement::NodeList * clone() const
Creates a deep copy of this list. Ownership is transferred to the caller.
QgsSQLStatement::NodeTableDef * tableDef() const
Table definition.
QgsSQLStatement::Node * where() const
Return the where clause.
QString dump() const override
Abstract virtual dump method.
QgsSQLStatement::NodeSelectedColumn * cloneThis() const
Clone with same type return.
static const char * BINARY_OPERATOR_TEXT[]
void visit(const QgsSQLStatement::NodeColumnRef &n) override
Visit NodeColumnRef.
QString dump() const override
Abstract virtual dump method.
QString statement() const
Return the original, unmodified statement string.
Binary logical/arithmetical operator (AND, OR, =, +, ...)
static QString quotedString(QString text)
Returns a quoted version of a string (in single quotes)
QString dump() const override
Abstract virtual dump method.
QList< QgsSQLStatement::NodeSelectedColumn * > columns() const
Return the list of columns.
bool hasParserError() const
Returns true if an error occurred when parsing the input statement.
Abstract node class.
QgsSQLStatement::Node * mRootNode
void accept(QgsSQLStatement::Visitor &v) const override
Support the visitor pattern.
void setWhere(QgsSQLStatement::Node *where)
Set where clause.
QgsSQLStatement::Node * clone() const override
Generate a clone of this node.
QgsSQLStatement::Node * clone() const override
Generate a clone of this node.
A visitor that recursively explores all children.
QgsSQLStatement::Node * clone() const override
Generate a clone of this node.
void accept(QgsSQLStatement::Visitor &v) const override
Support the visitor pattern.
Class for parsing SQL statements.
QString dump() const override
Abstract virtual dump method.
virtual QString dump() const
Dump list.
QPair< QString, QString > TableColumnPair
QString name() const
Table name.
QString alias() const
Table alias.
virtual QString dump() const =0
Abstract virtual dump method.
virtual QgsSQLStatement::Node * clone() const =0
Generate a clone of this node.
void accept(QgsSQLStatement::Visitor &v) const override
Support the visitor pattern.
QString dump() const override
Abstract virtual dump method.
void setOrderBy(const QList< QgsSQLStatement::NodeColumnSorted *> &orderBy)
Set order by columns.
QgsSQLStatement::Node * clone() const override
Generate a clone of this node.
Literal value (integer, integer64, double, string)
QString name() const
The name of the column.
QList< QgsSQLStatement::NodeTableDef * > tables() const
Return the list of tables.
QString dump() const override
Abstract virtual dump method.
QgsSQLStatement::Node * clone() const override
Generate a clone of this node.
void setAlias(const QString &alias)
Set alias name.
Unary logicial/arithmetical operator ( NOT, - )
const QgsSQLStatement::Node * rootNode() const
Returns root node of the statement. Root node is null is parsing has failed.
QgsSQLStatement::NodeJoin * cloneThis() const
Clone with same type return.
QString dump() const override
Abstract virtual dump method.
QString dump() const override
Abstract virtual dump method.
QgsSQLStatement::Node * clone() const override
Generate a clone of this node.
QSet< TableColumnPair > tableNamesReferenced
QList< QgsSQLStatement::NodeColumnSorted * > orderBy() const
Return the list of order by columns.
Reference to a column.
QString parserErrorString() const
Returns parser error.
&#39;X BETWEEN y and z&#39; operator
QgsSQLStatement::NodeColumnRef * cloneThis() const
Clone with same type return.
QgsSQLStatement::Node * clone() const override
Generate a clone of this node.
QgsSQLStatement::Node * clone() const override
Generate a clone of this node.
QList< QgsSQLStatement::NodeJoin * > joins() const
Return the list of joins.
void accept(QgsSQLStatement::Visitor &v) const override
Support the visitor pattern.
void acceptVisitor(QgsSQLStatement::Visitor &v) const
Entry function for the visitor pattern.
QString dump() const
Return statement string, constructed from the internal abstract syntax tree.
bool doBasicValidationChecks(QString &errorMsgOut) const
Performs basic validity checks.
QgsSQLStatement & operator=(const QgsSQLStatement &other)
Create a copy of this statement.
static QString stripQuotedIdentifier(QString text)
Remove double quotes from an identifier.
virtual void accept(QgsSQLStatement::Visitor &v) const =0
Support the visitor pattern.
QgsSQLStatement::NodeColumnSorted * cloneThis() const
Clone with same type return.
QString dump() const override
Abstract virtual dump method.
void setDistinct(bool distinct=true)
Set whether this is prefixed by DISTINCT.
QString dump() const override
Abstract virtual dump method.
QgsSQLStatement::Node * clone() const override
Generate a clone of this node.
static QString quotedIdentifierIfNeeded(const QString &name)
Returns a quoted column reference (in double quotes) if needed, or otherwise the original string...
QgsSQLStatement::Node * clone() const override
Generate a clone of this node.
void visit(const QgsSQLStatement::NodeUnaryOperator &n) override
Visit NodeUnaryOperator.
static const char * UNARY_OPERATOR_TEXT[]
Support for visitor pattern - algorithms dealing with the statement may be implemented without modify...
QString tableName() const
The name of the table. May be empty.
QString dump() const override
Abstract virtual dump method.
QgsSQLStatement::Node * clone() const override
Generate a clone of this node.
QString dump() const override
Abstract virtual dump method.
QgsSQLStatement::UnaryOperator op() const
Operator.
QString dump() const override
Abstract virtual dump method.
static QString quotedIdentifier(QString name)
Returns a quoted column reference (in double quotes)
QgsSQLStatement::Node * parse(const QString &str, QString &parserErrorMsg)
&#39;x IN (y, z)&#39; operator
QgsSQLStatement(const QString &statement)
Creates a new statement based on the provided string.
QString mParserErrorString