QGIS API Documentation  3.6.0-Noosa (5873452)
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  for ( QgsSQLStatement::Node *node : mList )
257  {
258  node->accept( v );
259  }
260 }
261 
263 {
264  NodeList *nl = new NodeList;
265  Q_FOREACH ( Node *node, mList )
266  {
267  nl->mList.append( node->clone() );
268  }
269 
270  return nl;
271 }
272 
274 {
275  QString msg;
276  bool first = true;
277  Q_FOREACH ( Node *n, mList )
278  {
279  if ( !first ) msg += QLatin1String( ", " );
280  else first = false;
281  msg += n->dump();
282  }
283  return msg;
284 }
285 
286 
287 //
288 
290 {
291  return QStringLiteral( "%1 %2" ).arg( UNARY_OPERATOR_TEXT[mOp], mOperand->dump() );
292 }
293 
295 {
296  return new NodeUnaryOperator( mOp, mOperand->clone() );
297 }
298 
299 //
300 
302 {
303  // see left/right in qgsexpressionparser.yy
304  switch ( mOp )
305  {
306  case boOr:
307  return 1;
308 
309  case boAnd:
310  return 2;
311 
312  case boEQ:
313  case boNE:
314  case boLE:
315  case boGE:
316  case boLT:
317  case boGT:
318  case boLike:
319  case boILike:
320  case boNotLike:
321  case boNotILike:
322  case boIs:
323  case boIsNot:
324  return 3;
325 
326  case boPlus:
327  case boMinus:
328  return 4;
329 
330  case boMul:
331  case boDiv:
332  case boIntDiv:
333  case boMod:
334  return 5;
335 
336  case boPow:
337  return 6;
338 
339  case boConcat:
340  return 7;
341  }
342  Q_ASSERT( false && "unexpected binary operator" );
343  return -1;
344 }
345 
347 {
348  // see left/right in qgsexpressionparser.yy
349  switch ( mOp )
350  {
351  case boOr:
352  case boAnd:
353  case boEQ:
354  case boNE:
355  case boLE:
356  case boGE:
357  case boLT:
358  case boGT:
359  case boLike:
360  case boILike:
361  case boNotLike:
362  case boNotILike:
363  case boIs:
364  case boIsNot:
365  case boPlus:
366  case boMinus:
367  case boMul:
368  case boDiv:
369  case boIntDiv:
370  case boMod:
371  case boConcat:
372  return true;
373 
374  case boPow:
375  return false;
376  }
377  Q_ASSERT( false && "unexpected binary operator" );
378  return false;
379 }
380 
382 {
386 
387  QString rdump( mOpRight->dump() );
388 
389  // avoid dumping "IS (NOT ...)" as "IS NOT ..."
390  if ( mOp == boIs && ruOp && ruOp->op() == uoNot )
391  {
392  rdump.prepend( '(' ).append( ')' );
393  }
394 
395  QString fmt;
396  if ( leftAssociative() )
397  {
398  fmt += lOp && ( lOp->precedence() < precedence() ) ? "(%1)" : "%1";
399  fmt += QLatin1String( " %2 " );
400  fmt += rOp && ( rOp->precedence() <= precedence() ) ? "(%3)" : "%3";
401  }
402  else
403  {
404  fmt += lOp && ( lOp->precedence() <= precedence() ) ? "(%1)" : "%1";
405  fmt += QLatin1String( " %2 " );
406  fmt += rOp && ( rOp->precedence() < precedence() ) ? "(%3)" : "%3";
407  }
408 
409  return fmt.arg( mOpLeft->dump(), BINARY_OPERATOR_TEXT[mOp], rdump );
410 }
411 
413 {
414  return new NodeBinaryOperator( mOp, mOpLeft->clone(), mOpRight->clone() );
415 }
416 
417 //
418 
420 {
421  return QStringLiteral( "%1 %2IN (%3)" ).arg( mNode->dump(), mNotIn ? "NOT " : "", mList->dump() );
422 }
423 
425 {
426  return new NodeInOperator( mNode->clone(), mList->clone(), mNotIn );
427 }
428 
429 //
430 
432 {
433  return QStringLiteral( "%1 %2BETWEEN %3 AND %4" ).arg( mNode->dump(), mNotBetween ? "NOT " : "", mMinVal->dump(), mMaxVal->dump() );
434 }
435 
437 {
438  return new NodeBetweenOperator( mNode->clone(), mMinVal->clone(), mMaxVal->clone(), mNotBetween );
439 }
440 
441 //
442 
444 {
445  return QStringLiteral( "%1(%2)" ).arg( mName, mArgs ? mArgs->dump() : QString() ); // function
446 }
447 
449 {
450  return new NodeFunction( mName, mArgs ? mArgs->clone() : nullptr );
451 }
452 
453 //
454 
456 {
457  if ( mValue.isNull() )
458  return QStringLiteral( "NULL" );
459 
460  switch ( mValue.type() )
461  {
462  case QVariant::Int:
463  return QString::number( mValue.toInt() );
464  case QVariant::LongLong:
465  return QString::number( mValue.toLongLong() );
466  case QVariant::Double:
467  return QString::number( mValue.toDouble() );
468  case QVariant::String:
469  return quotedString( mValue.toString() );
470  case QVariant::Bool:
471  return mValue.toBool() ? "TRUE" : "FALSE";
472  default:
473  return tr( "[unsupported type: %1; value: %2]" ).arg( mValue.typeName(), mValue.toString() );
474  }
475 }
476 
478 {
479  return new NodeLiteral( mValue );
480 }
481 
482 //
483 
485 {
486  QString ret;
487  if ( mDistinct )
488  ret += QLatin1String( "DISTINCT " );
489  if ( !mTableName.isEmpty() )
490  {
491  ret += quotedIdentifierIfNeeded( mTableName );
492  ret += '.';
493  }
494  ret += ( mStar ) ? mName : quotedIdentifierIfNeeded( mName );
495  return ret;
496 }
497 
499 {
500  return cloneThis();
501 }
502 
504 {
505  NodeColumnRef *newColumnRef = new NodeColumnRef( mTableName, mName, mStar );
506  newColumnRef->setDistinct( mDistinct );
507  return newColumnRef;
508 }
509 
510 //
511 
513 {
514  QString ret;
515  ret += mColumnNode->dump();
516  if ( !mAlias.isEmpty() )
517  {
518  ret += QLatin1String( " AS " );
519  ret += quotedIdentifierIfNeeded( mAlias );
520  }
521  return ret;
522 }
523 
525 {
526  NodeSelectedColumn *newObj = new NodeSelectedColumn( mColumnNode->clone() );
527  newObj->setAlias( mAlias );
528  return newObj;
529 }
530 
532 {
533  return cloneThis();
534 }
535 //
536 
538 {
539  QString ret;
540  ret = quotedIdentifierIfNeeded( mName );
541  if ( !mAlias.isEmpty() )
542  {
543  ret += QLatin1String( " AS " );
544  ret += quotedIdentifierIfNeeded( mAlias );
545  }
546  return ret;
547 }
548 
550 {
551  return new NodeTableDef( mName, mAlias );
552 }
553 
555 {
556  return cloneThis();
557 }
558 
559 //
560 
562 {
563  qDeleteAll( mTableList );
564  qDeleteAll( mColumns );
565  qDeleteAll( mJoins );
566  delete mWhere;
567  qDeleteAll( mOrderBy );
568 }
569 
571 {
572  QString ret = QStringLiteral( "SELECT " );
573  if ( mDistinct )
574  ret += QLatin1String( "DISTINCT " );
575  bool bFirstColumn = true;
576  Q_FOREACH ( QgsSQLStatement::NodeSelectedColumn *column, mColumns )
577  {
578  if ( !bFirstColumn )
579  ret += QLatin1String( ", " );
580  bFirstColumn = false;
581  ret += column->dump();
582  }
583  ret += QLatin1String( " FROM " );
584  bool bFirstTable = true;
585  Q_FOREACH ( QgsSQLStatement::NodeTableDef *table, mTableList )
586  {
587  if ( !bFirstTable )
588  ret += QLatin1String( ", " );
589  bFirstTable = false;
590  ret += table->dump();
591  }
592  Q_FOREACH ( QgsSQLStatement::NodeJoin *join, mJoins )
593  {
594  ret += ' ';
595  ret += join->dump();
596  }
597  if ( mWhere )
598  {
599  ret += QLatin1String( " WHERE " );
600  ret += mWhere->dump();
601  }
602  if ( !mOrderBy.isEmpty() )
603  {
604  ret += QLatin1String( " ORDER BY " );
605  bool bFirst = true;
606  Q_FOREACH ( QgsSQLStatement::NodeColumnSorted *orderBy, mOrderBy )
607  {
608  if ( !bFirst )
609  ret += QLatin1String( ", " );
610  bFirst = false;
611  ret += orderBy->dump();
612  }
613  }
614  return ret;
615 }
616 
618 {
619  QList<QgsSQLStatement::NodeSelectedColumn *> newColumnList;
620  Q_FOREACH ( QgsSQLStatement::NodeSelectedColumn *column, mColumns )
621  {
622  newColumnList.push_back( column->cloneThis() );
623  }
624  QList<QgsSQLStatement::NodeTableDef *> newTableList;
625  Q_FOREACH ( QgsSQLStatement::NodeTableDef *table, mTableList )
626  {
627  newTableList.push_back( table->cloneThis() );
628  }
629  QgsSQLStatement::NodeSelect *newSelect = new NodeSelect( newTableList, newColumnList, mDistinct );
630  Q_FOREACH ( QgsSQLStatement::NodeJoin *join, mJoins )
631  {
632  newSelect->appendJoin( join->cloneThis() );
633  }
634  if ( mWhere )
635  {
636  newSelect->setWhere( mWhere->clone() );
637  }
638  QList<QgsSQLStatement::NodeColumnSorted *> newOrderByList;
639  Q_FOREACH ( QgsSQLStatement::NodeColumnSorted *columnSorted, mOrderBy )
640  {
641  newOrderByList.push_back( columnSorted->cloneThis() );
642  }
643  newSelect->setOrderBy( newOrderByList );
644  return newSelect;
645 }
646 
647 //
648 
650 {
651  QString ret;
652  if ( mType != jtDefault )
653  {
654  ret += JOIN_TYPE_TEXT[mType];
655  ret += QLatin1String( " " );
656  }
657  ret += QLatin1String( "JOIN " );
658  ret += mTableDef->dump();
659  if ( mOnExpr )
660  {
661  ret += QLatin1String( " ON " );
662  ret += mOnExpr->dump();
663  }
664  else
665  {
666  ret += QLatin1String( " USING (" );
667  bool first = true;
668  Q_FOREACH ( QString column, mUsingColumns )
669  {
670  if ( !first )
671  ret += QLatin1String( ", " );
672  first = false;
673  ret += quotedIdentifierIfNeeded( column );
674  }
675  ret += QLatin1String( ")" );
676  }
677  return ret;
678 }
679 
681 {
682  return cloneThis();
683 }
684 
686 {
687  if ( mOnExpr )
688  return new NodeJoin( mTableDef->cloneThis(), mOnExpr->clone(), mType );
689  else
690  return new NodeJoin( mTableDef->cloneThis(), mUsingColumns, mType );
691 }
692 
693 //
694 
696 {
697  QString ret;
698  ret = mColumn->dump();
699  if ( !mAsc )
700  ret += QLatin1String( " DESC" );
701  return ret;
702 }
703 
705 {
706  return cloneThis();
707 }
708 
710 {
711  return new NodeColumnSorted( mColumn->cloneThis(), mAsc );
712 }
713 
714 //
715 
717 {
718  QString ret( QStringLiteral( "CAST(" ) );
719  ret += mNode->dump();
720  ret += QLatin1String( " AS " );
721  ret += mType;
722  ret += ')';
723  return ret;
724 }
725 
727 {
728  return new NodeCast( mNode->clone(), mType );
729 }
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
Returns 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
Returns 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
Returns 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)
Sets 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)
Sets 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
Returns 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)
Sets 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
Returns 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.
void accept(QgsSQLStatement::Visitor &v) const
Accept visitor.
QList< QgsSQLStatement::NodeJoin * > joins() const
Returns 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
Returns the 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)
Sets 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