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