QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
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 #include "qgis.h"
19 
20 #include <cmath>
21 #include <limits>
22 
23 static const QRegExp IDENTIFIER_RE( "^[A-Za-z_\x80-\xff][A-Za-z0-9_\x80-\xff]*$" );
24 
25 // from parser
26 extern QgsSQLStatement::Node *parse( const QString &str, QString &parserErrorMsg );
27 
29 // operators
30 
32 {
33  // this must correspond (number and order of element) to the declaration of the enum BinaryOperator
34  "OR", "AND",
35  "=", "<>", "<=", ">=", "<", ">", "LIKE", "NOT LIKE", "ILIKE", "NOT ILIKE", "IS", "IS NOT",
36  "+", "-", "*", "/", "//", "%", "^",
37  "||"
38 };
39 
41 {
42  // this must correspond (number and order of element) to the declaration of the enum UnaryOperator
43  "NOT", "-"
44 };
45 
46 const char *QgsSQLStatement::JOIN_TYPE_TEXT[] =
47 {
48  // this must correspond (number and order of element) to the declaration of the enum JoinType
49  "", "LEFT", "LEFT OUTER", "RIGHT", "RIGHT OUTER", "CROSS", "INNER", "FULL"
50 };
51 
53 
55 {
56  if ( !mStatement.isNull() )
57  return mStatement;
58  else
59  return dump();
60 }
61 
62 QString QgsSQLStatement::dump() const
63 {
64  if ( !mRootNode )
65  return tr( "(no root)" );
66 
67  return mRootNode->dump();
68 }
69 
70 QString QgsSQLStatement::quotedIdentifier( QString name )
71 {
72  return QStringLiteral( "\"%1\"" ).arg( name.replace( '\"', QLatin1String( "\"\"" ) ) );
73 }
74 
75 QString QgsSQLStatement::quotedIdentifierIfNeeded( const QString &name )
76 {
77  // This might not be complete, but it must be at least what we recognize
78  static const char *const RESERVED_KEYWORDS[] =
79  {
80  "AND", "OR", "NOT", "LIKE", "IN", "IS", "BETWEEN", "NULL", "SELECT", "ALL", "DISTINCT", "CAST", "AS",
81  "FROM", "JOIN", "ON", "USING", "WHERE", "ORDER", "BY", "ASC", "DESC",
82  "LEFT", "RIGHT", "INNER", "OUTER", "CROSS", "FULL", "NATURAL", "UNION",
83  "OFFSET", "LIMIT", "GROUP", "HAVING"
84  };
85 
86  for ( size_t i = 0; i < sizeof( RESERVED_KEYWORDS ) / sizeof( RESERVED_KEYWORDS[0] ); ++i )
87  {
88  if ( name.compare( QString( RESERVED_KEYWORDS[i] ), Qt::CaseInsensitive ) == 0 )
89  {
90  return quotedIdentifier( name );
91  }
92  }
93  return IDENTIFIER_RE.exactMatch( name ) ? name : quotedIdentifier( name );
94 }
95 
97 {
98  if ( text.length() >= 2 && text[0] == '"' && text[text.length() - 1] == '"' )
99  {
100  // strip double quotes on start,end
101  text = text.mid( 1, text.length() - 2 );
102 
103  // make single "double quotes" from double "double quotes"
104  text.replace( QLatin1String( "\"\"" ), QLatin1String( "\"" ) );
105  }
106  return text;
107 }
108 
109 QString QgsSQLStatement::quotedString( QString text )
110 {
111  text.replace( '\'', QLatin1String( "''" ) );
112  text.replace( '\\', QLatin1String( "\\\\" ) );
113  text.replace( '\n', QLatin1String( "\\n" ) );
114  text.replace( '\t', QLatin1String( "\\t" ) );
115  return QStringLiteral( "'%1'" ).arg( text );
116 }
117 
118 QgsSQLStatement::QgsSQLStatement( const QString &expr )
119 {
121  mStatement = expr;
122 }
123 
125 {
127  mStatement = other.mStatement;
128 }
129 
131 {
132  if ( &other != this )
133  {
134  delete mRootNode;
135  mParserErrorString.clear();
137  mStatement = other.mStatement;
138  }
139  return *this;
140 }
141 
143 {
144  delete mRootNode;
145 }
146 
147 bool QgsSQLStatement::hasParserError() const { return !mParserErrorString.isNull(); }
148 
150 
152 {
153  if ( mRootNode )
154  mRootNode->accept( v );
155 }
156 
158 {
159  return mRootNode;
160 }
161 
163 {
164  const auto constTables = n.tables();
165  for ( QgsSQLStatement::NodeTableDef *table : constTables )
166  {
167  table->accept( *this );
168  }
169  const auto constColumns = n.columns();
170  for ( QgsSQLStatement::NodeSelectedColumn *column : constColumns )
171  {
172  column->accept( *this );
173  }
174  const auto constJoins = n.joins();
175  for ( QgsSQLStatement::NodeJoin *join : constJoins )
176  {
177  join->accept( *this );
178  }
179  QgsSQLStatement::Node *where = n.where();
180  if ( where )
181  where->accept( *this );
182  const auto constOrderBy = n.orderBy();
183  for ( QgsSQLStatement::NodeColumnSorted *column : constOrderBy )
184  {
185  column->accept( *this );
186  }
187 }
188 
190 {
191  n.tableDef()->accept( *this );
192  QgsSQLStatement::Node *expr = n.onExpr();
193  if ( expr )
194  expr->accept( *this );
195 }
196 
203 {
204  public:
205  typedef QPair<QString, QString> TableColumnPair;
206 
211 
212  void visit( const QgsSQLStatement::NodeColumnRef &n ) override;
213  void visit( const QgsSQLStatement::NodeTableDef &n ) override;
214 
215  QSet<QString> tableNamesDeclared;
216  QSet<TableColumnPair> tableNamesReferenced;
217 };
218 
220 {
221  if ( !n.tableName().isEmpty() )
222  tableNamesReferenced.insert( TableColumnPair( n.tableName(), n.name() ) );
224 }
225 
227 {
228  tableNamesDeclared.insert( n.alias().isEmpty() ? n.name() : n.alias() );
230 }
231 
232 bool QgsSQLStatement::doBasicValidationChecks( QString &errorMsgOut ) const
233 {
234  errorMsgOut.clear();
235  if ( !mRootNode )
236  {
237  errorMsgOut = tr( "No root node" );
238  return false;
239  }
241  mRootNode->accept( v );
242 
243  for ( const QgsSQLStatementCollectTableNames::TableColumnPair &pair : qgis::as_const( v.tableNamesReferenced ) )
244  {
245  if ( !v.tableNamesDeclared.contains( pair.first ) )
246  {
247  if ( !errorMsgOut.isEmpty() )
248  errorMsgOut += QLatin1String( " " );
249  errorMsgOut += QString( tr( "Table %1 is referenced by column %2, but not selected in FROM / JOIN." ) ).arg( pair.first, pair.second );
250  }
251  }
252 
253  return errorMsgOut.isEmpty();
254 }
255 
257 // nodes
258 
260 {
261  for ( QgsSQLStatement::Node *node : mList )
262  {
263  node->accept( v );
264  }
265 }
266 
268 {
269  NodeList *nl = new NodeList;
270  const auto constMList = mList;
271  for ( Node *node : constMList )
272  {
273  nl->mList.append( node->clone() );
274  }
275 
276  return nl;
277 }
278 
280 {
281  QString msg;
282  bool first = true;
283  const auto constMList = mList;
284  for ( Node *n : constMList )
285  {
286  if ( !first ) msg += QLatin1String( ", " );
287  else first = false;
288  msg += n->dump();
289  }
290  return msg;
291 }
292 
293 
294 //
295 
297 {
298  return QStringLiteral( "%1 %2" ).arg( UNARY_OPERATOR_TEXT[mOp], mOperand->dump() );
299 }
300 
302 {
303  return new NodeUnaryOperator( mOp, mOperand->clone() );
304 }
305 
306 //
307 
309 {
310  // see left/right in qgsexpressionparser.yy
311  switch ( mOp )
312  {
313  case boOr:
314  return 1;
315 
316  case boAnd:
317  return 2;
318 
319  case boEQ:
320  case boNE:
321  case boLE:
322  case boGE:
323  case boLT:
324  case boGT:
325  case boLike:
326  case boILike:
327  case boNotLike:
328  case boNotILike:
329  case boIs:
330  case boIsNot:
331  return 3;
332 
333  case boPlus:
334  case boMinus:
335  return 4;
336 
337  case boMul:
338  case boDiv:
339  case boIntDiv:
340  case boMod:
341  return 5;
342 
343  case boPow:
344  return 6;
345 
346  case boConcat:
347  return 7;
348  }
349  Q_ASSERT( false && "unexpected binary operator" );
350  return -1;
351 }
352 
354 {
355  // see left/right in qgsexpressionparser.yy
356  switch ( mOp )
357  {
358  case boOr:
359  case boAnd:
360  case boEQ:
361  case boNE:
362  case boLE:
363  case boGE:
364  case boLT:
365  case boGT:
366  case boLike:
367  case boILike:
368  case boNotLike:
369  case boNotILike:
370  case boIs:
371  case boIsNot:
372  case boPlus:
373  case boMinus:
374  case boMul:
375  case boDiv:
376  case boIntDiv:
377  case boMod:
378  case boConcat:
379  return true;
380 
381  case boPow:
382  return false;
383  }
384  Q_ASSERT( false && "unexpected binary operator" );
385  return false;
386 }
387 
389 {
393 
394  QString rdump( mOpRight->dump() );
395 
396  // avoid dumping "IS (NOT ...)" as "IS NOT ..."
397  if ( mOp == boIs && ruOp && ruOp->op() == uoNot )
398  {
399  rdump.prepend( '(' ).append( ')' );
400  }
401 
402  QString fmt;
403  if ( leftAssociative() )
404  {
405  fmt += lOp && ( lOp->precedence() < precedence() ) ? "(%1)" : "%1";
406  fmt += QLatin1String( " %2 " );
407  fmt += rOp && ( rOp->precedence() <= precedence() ) ? "(%3)" : "%3";
408  }
409  else
410  {
411  fmt += lOp && ( lOp->precedence() <= precedence() ) ? "(%1)" : "%1";
412  fmt += QLatin1String( " %2 " );
413  fmt += rOp && ( rOp->precedence() < precedence() ) ? "(%3)" : "%3";
414  }
415 
416  return fmt.arg( mOpLeft->dump(), BINARY_OPERATOR_TEXT[mOp], rdump );
417 }
418 
420 {
421  return new NodeBinaryOperator( mOp, mOpLeft->clone(), mOpRight->clone() );
422 }
423 
424 //
425 
427 {
428  return QStringLiteral( "%1 %2IN (%3)" ).arg( mNode->dump(), mNotIn ? "NOT " : "", mList->dump() );
429 }
430 
432 {
433  return new NodeInOperator( mNode->clone(), mList->clone(), mNotIn );
434 }
435 
436 //
437 
439 {
440  return QStringLiteral( "%1 %2BETWEEN %3 AND %4" ).arg( mNode->dump(), mNotBetween ? "NOT " : "", mMinVal->dump(), mMaxVal->dump() );
441 }
442 
444 {
445  return new NodeBetweenOperator( mNode->clone(), mMinVal->clone(), mMaxVal->clone(), mNotBetween );
446 }
447 
448 //
449 
451 {
452  return QStringLiteral( "%1(%2)" ).arg( mName, mArgs ? mArgs->dump() : QString() ); // function
453 }
454 
456 {
457  return new NodeFunction( mName, mArgs ? mArgs->clone() : nullptr );
458 }
459 
460 //
461 
463 {
464  if ( mValue.isNull() )
465  return QStringLiteral( "NULL" );
466 
467  switch ( mValue.type() )
468  {
469  case QVariant::Int:
470  return QString::number( mValue.toInt() );
471  case QVariant::LongLong:
472  return QString::number( mValue.toLongLong() );
473  case QVariant::Double:
474  return QString::number( mValue.toDouble() );
475  case QVariant::String:
476  return quotedString( mValue.toString() );
477  case QVariant::Bool:
478  return mValue.toBool() ? "TRUE" : "FALSE";
479  default:
480  return tr( "[unsupported type: %1; value: %2]" ).arg( mValue.typeName(), mValue.toString() );
481  }
482 }
483 
485 {
486  return new NodeLiteral( mValue );
487 }
488 
489 //
490 
492 {
493  QString ret;
494  if ( mDistinct )
495  ret += QLatin1String( "DISTINCT " );
496  if ( !mTableName.isEmpty() )
497  {
498  ret += quotedIdentifierIfNeeded( mTableName );
499  ret += '.';
500  }
501  ret += ( mStar ) ? mName : quotedIdentifierIfNeeded( mName );
502  return ret;
503 }
504 
506 {
507  return cloneThis();
508 }
509 
511 {
512  NodeColumnRef *newColumnRef = new NodeColumnRef( mTableName, mName, mStar );
513  newColumnRef->setDistinct( mDistinct );
514  return newColumnRef;
515 }
516 
517 //
518 
520 {
521  QString ret;
522  ret += mColumnNode->dump();
523  if ( !mAlias.isEmpty() )
524  {
525  ret += QLatin1String( " AS " );
526  ret += quotedIdentifierIfNeeded( mAlias );
527  }
528  return ret;
529 }
530 
532 {
533  NodeSelectedColumn *newObj = new NodeSelectedColumn( mColumnNode->clone() );
534  newObj->setAlias( mAlias );
535  return newObj;
536 }
537 
539 {
540  return cloneThis();
541 }
542 //
543 
545 {
546  QString ret;
547  ret = quotedIdentifierIfNeeded( mName );
548  if ( !mAlias.isEmpty() )
549  {
550  ret += QLatin1String( " AS " );
551  ret += quotedIdentifierIfNeeded( mAlias );
552  }
553  return ret;
554 }
555 
557 {
558  return new NodeTableDef( mName, mAlias );
559 }
560 
562 {
563  return cloneThis();
564 }
565 
566 //
567 
569 {
570  qDeleteAll( mTableList );
571  qDeleteAll( mColumns );
572  qDeleteAll( mJoins );
573  delete mWhere;
574  qDeleteAll( mOrderBy );
575 }
576 
578 {
579  QString ret = QStringLiteral( "SELECT " );
580  if ( mDistinct )
581  ret += QLatin1String( "DISTINCT " );
582  bool bFirstColumn = true;
583  const auto constMColumns = mColumns;
584  for ( QgsSQLStatement::NodeSelectedColumn *column : constMColumns )
585  {
586  if ( !bFirstColumn )
587  ret += QLatin1String( ", " );
588  bFirstColumn = false;
589  ret += column->dump();
590  }
591  ret += QLatin1String( " FROM " );
592  bool bFirstTable = true;
593  const auto constMTableList = mTableList;
594  for ( QgsSQLStatement::NodeTableDef *table : constMTableList )
595  {
596  if ( !bFirstTable )
597  ret += QLatin1String( ", " );
598  bFirstTable = false;
599  ret += table->dump();
600  }
601  const auto constMJoins = mJoins;
602  for ( QgsSQLStatement::NodeJoin *join : constMJoins )
603  {
604  ret += ' ';
605  ret += join->dump();
606  }
607  if ( mWhere )
608  {
609  ret += QLatin1String( " WHERE " );
610  ret += mWhere->dump();
611  }
612  if ( !mOrderBy.isEmpty() )
613  {
614  ret += QLatin1String( " ORDER BY " );
615  bool bFirst = true;
616  const auto constMOrderBy = mOrderBy;
617  for ( QgsSQLStatement::NodeColumnSorted *orderBy : constMOrderBy )
618  {
619  if ( !bFirst )
620  ret += QLatin1String( ", " );
621  bFirst = false;
622  ret += orderBy->dump();
623  }
624  }
625  return ret;
626 }
627 
629 {
630  QList<QgsSQLStatement::NodeSelectedColumn *> newColumnList;
631  const auto constMColumns = mColumns;
632  for ( QgsSQLStatement::NodeSelectedColumn *column : constMColumns )
633  {
634  newColumnList.push_back( column->cloneThis() );
635  }
636  QList<QgsSQLStatement::NodeTableDef *> newTableList;
637  const auto constMTableList = mTableList;
638  for ( QgsSQLStatement::NodeTableDef *table : constMTableList )
639  {
640  newTableList.push_back( table->cloneThis() );
641  }
642  QgsSQLStatement::NodeSelect *newSelect = new NodeSelect( newTableList, newColumnList, mDistinct );
643  const auto constMJoins = mJoins;
644  for ( QgsSQLStatement::NodeJoin *join : constMJoins )
645  {
646  newSelect->appendJoin( join->cloneThis() );
647  }
648  if ( mWhere )
649  {
650  newSelect->setWhere( mWhere->clone() );
651  }
652  QList<QgsSQLStatement::NodeColumnSorted *> newOrderByList;
653  const auto constMOrderBy = mOrderBy;
654  for ( QgsSQLStatement::NodeColumnSorted *columnSorted : constMOrderBy )
655  {
656  newOrderByList.push_back( columnSorted->cloneThis() );
657  }
658  newSelect->setOrderBy( newOrderByList );
659  return newSelect;
660 }
661 
662 //
663 
665 {
666  QString ret;
667  if ( mType != jtDefault )
668  {
669  ret += JOIN_TYPE_TEXT[mType];
670  ret += QLatin1String( " " );
671  }
672  ret += QLatin1String( "JOIN " );
673  ret += mTableDef->dump();
674  if ( mOnExpr )
675  {
676  ret += QLatin1String( " ON " );
677  ret += mOnExpr->dump();
678  }
679  else
680  {
681  ret += QLatin1String( " USING (" );
682  bool first = true;
683  const auto constMUsingColumns = mUsingColumns;
684  for ( QString column : constMUsingColumns )
685  {
686  if ( !first )
687  ret += QLatin1String( ", " );
688  first = false;
689  ret += quotedIdentifierIfNeeded( column );
690  }
691  ret += QLatin1String( ")" );
692  }
693  return ret;
694 }
695 
697 {
698  return cloneThis();
699 }
700 
702 {
703  if ( mOnExpr )
704  return new NodeJoin( mTableDef->cloneThis(), mOnExpr->clone(), mType );
705  else
706  return new NodeJoin( mTableDef->cloneThis(), mUsingColumns, mType );
707 }
708 
709 //
710 
712 {
713  QString ret;
714  ret = mColumn->dump();
715  if ( !mAsc )
716  ret += QLatin1String( " DESC" );
717  return ret;
718 }
719 
721 {
722  return cloneThis();
723 }
724 
726 {
727  return new NodeColumnSorted( mColumn->cloneThis(), mAsc );
728 }
729 
730 //
731 
733 {
734  QString ret( QStringLiteral( "CAST(" ) );
735  ret += mNode->dump();
736  ret += QLatin1String( " AS " );
737  ret += mType;
738  ret += ')';
739  return ret;
740 }
741 
743 {
744  return new NodeCast( mNode->clone(), mType );
745 }
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 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.
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 the root node of the statement.
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 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