QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
qgssqlcomposerdialog.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgssqlcomposerdialog.cpp
3 Dialog to compose SQL queries
4
5begin : Apr 2016
6copyright : (C) 2016 Even Rouault
7email : even.rouault at spatialys.com
8
9 Adapted/ported from DBManager dlg_query_builder
10 ***************************************************************************/
11
12/***************************************************************************
13 * *
14 * This program is free software; you can redistribute it and/or modify *
15 * it under the terms of the GNU General Public License as published by *
16 * the Free Software Foundation; either version 2 of the License, or *
17 * (at your option) any later version. *
18 * *
19 ***************************************************************************/
20
22
23#include "qgshelp.h"
24#include "qgssqlstatement.h"
25#include "qgsvectorlayer.h"
26
27#include <QKeyEvent>
28#include <QMessageBox>
29#include <QString>
30#include <Qsci/qscilexer.h>
31
32#include "moc_qgssqlcomposerdialog.cpp"
33
34using namespace Qt::StringLiterals;
35
36QgsSQLComposerDialog::QgsSQLComposerDialog( QWidget *parent, Qt::WindowFlags fl )
37 : QgsSQLComposerDialog( nullptr, parent, fl )
38{}
39
40QgsSQLComposerDialog::QgsSQLComposerDialog( QgsVectorLayer *layer, QWidget *parent, Qt::WindowFlags fl )
41 : QgsSubsetStringEditorInterface( parent, fl )
42 , mLayer( layer )
43{
44 setupUi( this );
45 connect( mTablesCombo, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsSQLComposerDialog::mTablesCombo_currentIndexChanged );
46 connect( mColumnsCombo, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsSQLComposerDialog::mColumnsCombo_currentIndexChanged );
47 connect( mSpatialPredicatesCombo, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsSQLComposerDialog::mSpatialPredicatesCombo_currentIndexChanged );
48 connect( mFunctionsCombo, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsSQLComposerDialog::mFunctionsCombo_currentIndexChanged );
49 connect( mOperatorsCombo, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsSQLComposerDialog::mOperatorsCombo_currentIndexChanged );
50 connect( mAddJoinButton, &QPushButton::clicked, this, &QgsSQLComposerDialog::mAddJoinButton_clicked );
51 connect( mRemoveJoinButton, &QPushButton::clicked, this, &QgsSQLComposerDialog::mRemoveJoinButton_clicked );
52 connect( mTableJoins, &QTableWidget::itemSelectionChanged, this, &QgsSQLComposerDialog::mTableJoins_itemSelectionChanged );
53
54 mQueryEdit->setWrapMode( QsciScintilla::WrapWord );
55 mQueryEdit->installEventFilter( this );
56 mColumnsEditor->installEventFilter( this );
57 mTablesEditor->installEventFilter( this );
58 mTableJoins->installEventFilter( this );
59 mWhereEditor->installEventFilter( this );
60 mOrderEditor->installEventFilter( this );
61 mTablesCombo->view()->installEventFilter( this );
62
63
64 connect( mButtonBox->button( QDialogButtonBox::Reset ), &QAbstractButton::clicked, this, &QgsSQLComposerDialog::reset );
65
66 connect( mQueryEdit, &QsciScintilla::textChanged, this, &QgsSQLComposerDialog::splitSQLIntoFields );
67 connect( mColumnsEditor, &QTextEdit::textChanged, this, &QgsSQLComposerDialog::buildSQLFromFields );
68 connect( mTablesEditor, &QLineEdit::textChanged, this, &QgsSQLComposerDialog::buildSQLFromFields );
69 connect( mWhereEditor, &QTextEdit::textChanged, this, &QgsSQLComposerDialog::buildSQLFromFields );
70 connect( mOrderEditor, &QTextEdit::textChanged, this, &QgsSQLComposerDialog::buildSQLFromFields );
71 connect( mTableJoins, &QTableWidget::cellChanged, this, &QgsSQLComposerDialog::buildSQLFromFields );
72 connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsSQLComposerDialog::showHelp );
73
74 QStringList baseList;
75 baseList << u"SELECT"_s;
76 baseList << u"FROM"_s;
77 baseList << u"JOIN"_s;
78 baseList << u"ON"_s;
79 baseList << u"USING"_s;
80 baseList << u"WHERE"_s;
81 baseList << u"AND"_s;
82 baseList << u"OR"_s;
83 baseList << u"NOT"_s;
84 baseList << u"IS"_s;
85 baseList << u"NULL"_s;
86 baseList << u"LIKE"_s;
87 baseList << u"ORDER"_s;
88 baseList << u"BY"_s;
89 addApis( baseList );
90
91 QStringList operatorsList;
92 operatorsList << u"AND"_s;
93 operatorsList << u"OR"_s;
94 operatorsList << u"NOT"_s;
95 operatorsList << u"="_s;
96 operatorsList << u"<"_s;
97 operatorsList << u"<="_s;
98 operatorsList << u">"_s;
99 operatorsList << u">="_s;
100 operatorsList << u"<>"_s;
101 operatorsList << u"BETWEEN"_s;
102 operatorsList << u"NOT BETWEEN"_s;
103 operatorsList << u"IS"_s;
104 operatorsList << u"IS NOT"_s;
105 operatorsList << u"IN"_s;
106 operatorsList << u"LIKE"_s;
107 addOperators( operatorsList );
108
109 mAggregatesCombo->hide();
110 mFunctionsCombo->hide();
111 mSpatialPredicatesCombo->hide();
112 mStringFunctionsCombo->hide();
113
114 delete mPageColumnsValues;
115 mPageColumnsValues = nullptr;
116
117 mRemoveJoinButton->setEnabled( false );
118
119 mTableJoins->setRowCount( 0 );
120 mTableJoins->setItem( 0, 0, new QTableWidgetItem( QString() ) );
121 mTableJoins->setItem( 0, 1, new QTableWidgetItem( QString() ) );
122}
123
125{
126 // Besides avoid memory leaks, this is useful since QSciAPIs::prepare()
127 // starts a thread. If the dialog was killed before the thread had started,
128 // he could run against a dead widget. This can happen in unit tests.
129 delete mQueryEdit->lexer()->apis();
130 mQueryEdit->lexer()->setAPIs( nullptr );
131}
132
133bool QgsSQLComposerDialog::eventFilter( QObject *obj, QEvent *event )
134{
135 if ( event->type() == QEvent::FocusIn )
136 {
137 if ( obj == mTablesCombo->view() )
138 lastSearchedText.clear();
139 else
140 mFocusedObject = obj;
141 }
142
143 // Custom search in table combobox
144 if ( event->type() == QEvent::KeyPress && obj == mTablesCombo->view() )
145 {
146 QString currentString = ( ( QKeyEvent * ) event )->text();
147 if ( !currentString.isEmpty() && ( ( currentString[0] >= 'a' && currentString[0] <= 'z' ) || ( currentString[0] >= 'A' && currentString[0] <= 'Z' ) || ( currentString[0] >= '0' && currentString[0] <= '9' ) || currentString[0] == ':' || currentString[0] == '_' || currentString[0] == ' ' || currentString[0] == '(' || currentString[0] == ')' ) )
148 {
149 // First attempt is concatenation of existing search text
150 // Second attempt is just the new character
151 const int attemptCount = ( lastSearchedText.isEmpty() ) ? 1 : 2;
152 for ( int attempt = 0; attempt < attemptCount; attempt++ )
153 {
154 if ( attempt == 0 )
155 lastSearchedText += currentString;
156 else
157 lastSearchedText = currentString;
158
159 // Find the string that contains the searched text, and in case
160 // of several matches, pickup the one where the searched text is the
161 // most at the beginning.
162 int iBestCandidate = 0;
163 int idxInTextOfBestCandidate = 1000;
164 for ( int i = 1; i < mTablesCombo->count(); i++ )
165 {
166 const int idxInText = mTablesCombo->itemText( i ).indexOf( lastSearchedText, Qt::CaseInsensitive );
167 if ( idxInText >= 0 && idxInText < idxInTextOfBestCandidate )
168 {
169 iBestCandidate = i;
170 idxInTextOfBestCandidate = idxInText;
171 }
172 }
173 if ( iBestCandidate > 0 )
174 {
175 mTablesCombo->view()->setCurrentIndex( mTablesCombo->model()->index( 0, 0 ).sibling( iBestCandidate, 0 ) );
176 return true;
177 }
178 }
179 lastSearchedText.clear();
180 }
181 }
182
183 return QDialog::eventFilter( obj, event );
184}
185
187{
188 mTableSelectedCallback = tableSelectedCallback;
189}
190
192{
193 mSQLValidatorCallback = sqlValidatorCallback;
194}
195
196void QgsSQLComposerDialog::setSql( const QString &sql )
197{
198 mResetSql = sql;
199 mQueryEdit->setText( sql );
200}
201
203{
204 return mQueryEdit->text();
205}
206
207void QgsSQLComposerDialog::accept()
208{
209 if ( mSQLValidatorCallback )
210 {
211 QString errorMsg, warningMsg;
212 if ( !mSQLValidatorCallback->isValid( sql(), errorMsg, warningMsg ) )
213 {
214 if ( errorMsg.isEmpty() )
215 errorMsg = tr( "An error occurred during evaluation of the SQL statement." );
216 QMessageBox::critical( this, tr( "SQL Evaluation" ), errorMsg );
217 return;
218 }
219 if ( !warningMsg.isEmpty() )
220 {
221 QMessageBox::warning( this, tr( "SQL Evaluation" ), warningMsg );
222 }
223 }
224 if ( mLayer )
225 {
226 mLayer->setSubsetString( sql() );
227 }
228 QDialog::accept();
229}
230
231void QgsSQLComposerDialog::buildSQLFromFields()
232{
233 if ( mAlreadyModifyingFields )
234 return;
235 mAlreadyModifyingFields = true;
236 QString sql( u"SELECT "_s );
237 if ( mDistinct )
238 sql += "DISTINCT "_L1;
239 sql += mColumnsEditor->toPlainText();
240 sql += " FROM "_L1;
241 sql += mTablesEditor->text();
242
243 const int rows = mTableJoins->rowCount();
244 for ( int i = 0; i < rows; i++ )
245 {
246 QTableWidgetItem *itemTable = mTableJoins->item( i, 0 );
247 QTableWidgetItem *itemOn = mTableJoins->item( i, 1 );
248 if ( itemTable && !itemTable->text().isEmpty() && itemOn && !itemOn->text().isEmpty() )
249 {
250 sql += " JOIN "_L1;
251 sql += itemTable->text();
252 sql += " ON "_L1;
253 sql += itemOn->text();
254 }
255 }
256
257 if ( !mWhereEditor->toPlainText().isEmpty() )
258 {
259 sql += " WHERE "_L1;
260 sql += mWhereEditor->toPlainText();
261 }
262 if ( !mOrderEditor->toPlainText().isEmpty() )
263 {
264 sql += " ORDER BY "_L1;
265 sql += mOrderEditor->toPlainText();
266 }
267 mQueryEdit->setText( sql );
268
269 mAlreadyModifyingFields = false;
270}
271
272void QgsSQLComposerDialog::splitSQLIntoFields()
273{
274 if ( mAlreadyModifyingFields )
275 return;
276 const QgsSQLStatement sql( mQueryEdit->text() );
277 if ( sql.hasParserError() )
278 return;
279 const QgsSQLStatement::NodeSelect *nodeSelect = dynamic_cast<const QgsSQLStatement::NodeSelect *>( sql.rootNode() );
280 if ( !nodeSelect )
281 return;
282 mDistinct = nodeSelect->distinct();
283 const QList<QgsSQLStatement::NodeSelectedColumn *> columns = nodeSelect->columns();
284 QString columnText;
285 const auto constColumns = columns;
286 for ( QgsSQLStatement::NodeSelectedColumn *column : constColumns )
287 {
288 if ( !columnText.isEmpty() )
289 columnText += ", "_L1;
290 columnText += column->dump();
291 }
292
293 const QList<QgsSQLStatement::NodeTableDef *> tables = nodeSelect->tables();
294 QString tablesText;
295 const auto constTables = tables;
296 for ( QgsSQLStatement::NodeTableDef *table : constTables )
297 {
298 if ( !tablesText.isEmpty() )
299 tablesText += ", "_L1;
300 loadTableColumns( QgsSQLStatement::quotedIdentifierIfNeeded( table->name() ) );
301 tablesText += table->dump();
302 }
303
304 QString whereText;
305 QgsSQLStatement::Node *where = nodeSelect->where();
306 if ( where )
307 whereText = where->dump();
308
309 QString orderText;
310 const QList<QgsSQLStatement::NodeColumnSorted *> orderColumns = nodeSelect->orderBy();
311 const auto constOrderColumns = orderColumns;
312 for ( QgsSQLStatement::NodeColumnSorted *column : constOrderColumns )
313 {
314 if ( !orderText.isEmpty() )
315 orderText += ", "_L1;
316 orderText += column->dump();
317 }
318
319 const QList<QgsSQLStatement::NodeJoin *> joins = nodeSelect->joins();
320
321 mAlreadyModifyingFields = true;
322 mColumnsEditor->setPlainText( columnText );
323 mTablesEditor->setText( tablesText );
324 mWhereEditor->setPlainText( whereText );
325 mOrderEditor->setPlainText( orderText );
326
327 mTableJoins->setRowCount( joins.size() + 1 );
328 int iRow = 0;
329 const auto constJoins = joins;
330 for ( QgsSQLStatement::NodeJoin *join : constJoins )
331 {
332 loadTableColumns( QgsSQLStatement::quotedIdentifierIfNeeded( join->tableDef()->name() ) );
333 mTableJoins->setItem( iRow, 0, new QTableWidgetItem( join->tableDef()->dump() ) );
334 if ( join->onExpr() )
335 mTableJoins->setItem( iRow, 1, new QTableWidgetItem( join->onExpr()->dump() ) );
336 else
337 mTableJoins->setItem( iRow, 1, new QTableWidgetItem( QString() ) );
338 iRow++;
339 }
340 mTableJoins->setItem( iRow, 0, new QTableWidgetItem( QString() ) );
341 mTableJoins->setItem( iRow, 1, new QTableWidgetItem( QString() ) );
342
343 mAlreadyModifyingFields = false;
344}
345
346void QgsSQLComposerDialog::addTableNames( const QStringList &list )
347{
348 const auto constList = list;
349 for ( const QString &name : constList )
350 mapTableEntryTextToName[name] = name;
351 mTablesCombo->addItems( list );
352 addApis( list );
353}
354
355void QgsSQLComposerDialog::addTableNames( const QList<PairNameTitle> &listNameTitle )
356{
357 QStringList listCombo;
358 QStringList listApi;
359 const auto constListNameTitle = listNameTitle;
360 for ( const PairNameTitle &pair : constListNameTitle )
361 {
362 listApi << pair.first;
363 QString entryText( pair.first );
364 if ( !pair.second.isEmpty() && pair.second != pair.first )
365 {
366 if ( pair.second.size() < 40 )
367 entryText += " (" + pair.second + ")";
368 else
369 entryText += " (" + pair.second.mid( 0, 20 ) + QChar( 0x2026 ) + pair.second.mid( pair.second.size() - 20 ) + ")";
370 }
371 listCombo << entryText;
372 mapTableEntryTextToName[entryText] = pair.first;
373 }
374 mTablesCombo->addItems( listCombo );
375 addApis( listApi );
376}
377
378void QgsSQLComposerDialog::addColumnNames( const QStringList &list, const QString &tableName )
379{
380 QList<PairNameType> listPair;
381 const auto constList = list;
382 for ( const QString &name : constList )
383 listPair << PairNameType( name, QString() );
384 addColumnNames( listPair, tableName );
385}
386
387static QString sanitizeType( QString type )
388{
389 if ( type.startsWith( "xs:"_L1 ) )
390 return type.mid( 3 );
391 if ( type.startsWith( "xsd:"_L1 ) )
392 return type.mid( 4 );
393 if ( type == "gml:AbstractGeometryType"_L1 )
394 return u"geometry"_s;
395 return type;
396}
397
398void QgsSQLComposerDialog::addColumnNames( const QList<PairNameType> &list, const QString &tableName )
399{
400 mAlreadySelectedTables.insert( tableName );
401 if ( mColumnsCombo->count() > 1 )
402 mColumnsCombo->insertSeparator( mColumnsCombo->count() );
403
404 QStringList listCombo;
405 QStringList listApi;
406 const auto constList = list;
407 for ( const PairNameType &pair : constList )
408 {
409 listApi << pair.first;
410 QString entryText( pair.first );
411 if ( !pair.second.isEmpty() )
412 {
413 entryText += " (" + sanitizeType( pair.second ) + ")";
414 }
415 listCombo << entryText;
416 mapColumnEntryTextToName[entryText] = pair.first;
417 }
418 mColumnsCombo->addItems( listCombo );
419
420 addApis( listApi );
421}
422
423void QgsSQLComposerDialog::addOperators( const QStringList &list )
424{
425 mOperatorsCombo->addItems( list );
426 addApis( list );
427}
428
429static QString getFunctionAbbridgedParameters( const QgsSQLComposerDialog::Function &f )
430{
431 if ( f.minArgs >= 0 && f.maxArgs > f.minArgs )
432 {
433 return QObject::tr( "%1 to %n argument(s)", nullptr, f.maxArgs ).arg( f.minArgs );
434 }
435 else if ( f.minArgs == 0 && f.maxArgs == 0 )
436 {
437 }
438 else if ( f.minArgs > 0 && f.maxArgs == f.minArgs )
439 {
440 return QObject::tr( "%n argument(s)", nullptr, f.minArgs );
441 }
442 else if ( f.minArgs >= 0 && f.maxArgs < 0 )
443 {
444 return QObject::tr( "%n argument(s) or more", nullptr, f.minArgs );
445 }
446 return QString();
447}
448
449
450void QgsSQLComposerDialog::getFunctionList( const QList<Function> &list, QStringList &listApi, QStringList &listCombo, QMap<QString, QString> &mapEntryTextToName )
451{
452 const auto constList = list;
453 for ( const Function &f : constList )
454 {
455 listApi << f.name;
456 QString entryText( f.name );
457 entryText += '('_L1;
458 if ( !f.argumentList.isEmpty() )
459 {
460 for ( int i = 0; i < f.argumentList.size(); i++ )
461 {
462 if ( f.minArgs >= 0 && i >= f.minArgs )
463 entryText += '['_L1;
464 if ( i > 0 )
465 entryText += ", "_L1;
466 if ( f.argumentList[i].name == "number"_L1 && !f.argumentList[i].type.isEmpty() )
467 {
468 entryText += sanitizeType( f.argumentList[i].type );
469 }
470 else
471 {
472 entryText += f.argumentList[i].name;
473 const QString sanitizedType( sanitizeType( f.argumentList[i].type ) );
474 if ( !f.argumentList[i].type.isEmpty() && f.argumentList[i].name != sanitizedType )
475 {
476 entryText += ": "_L1;
477 entryText += sanitizedType;
478 }
479 }
480 if ( f.minArgs >= 0 && i >= f.minArgs )
481 entryText += ']'_L1;
482 }
483 if ( entryText.size() > 60 )
484 {
485 entryText = f.name;
486 entryText += '('_L1;
487 entryText += getFunctionAbbridgedParameters( f );
488 }
489 }
490 else
491 {
492 entryText += getFunctionAbbridgedParameters( f );
493 }
494 entryText += ')'_L1;
495 if ( !f.returnType.isEmpty() )
496 entryText += ": " + sanitizeType( f.returnType );
497 listCombo << entryText;
498 mapEntryTextToName[entryText] = f.name + "(";
499 }
500}
501
502void QgsSQLComposerDialog::addSpatialPredicates( const QStringList &list )
503{
504 QList<Function> listFunction;
505 const auto constList = list;
506 for ( const QString &name : constList )
507 {
508 Function f;
509 f.name = name;
510 listFunction << f;
511 }
512 addSpatialPredicates( listFunction );
513}
514
515void QgsSQLComposerDialog::addSpatialPredicates( const QList<Function> &list )
516{
517 QStringList listApi;
518 QStringList listCombo;
519 getFunctionList( list, listApi, listCombo, mapSpatialPredicateEntryTextToName );
520 mSpatialPredicatesCombo->addItems( listCombo );
521 mSpatialPredicatesCombo->show();
522 addApis( listApi );
523}
524
525void QgsSQLComposerDialog::addFunctions( const QStringList &list )
526{
527 QList<Function> listFunction;
528 const auto constList = list;
529 for ( const QString &name : constList )
530 {
531 Function f;
532 f.name = name;
533 listFunction << f;
534 }
535 addFunctions( listFunction );
536}
537
538void QgsSQLComposerDialog::addFunctions( const QList<Function> &list )
539{
540 QStringList listApi;
541 QStringList listCombo;
542 getFunctionList( list, listApi, listCombo, mapFunctionEntryTextToName );
543 mFunctionsCombo->addItems( listCombo );
544 mFunctionsCombo->show();
545 addApis( listApi );
546}
547
548void QgsSQLComposerDialog::loadTableColumns( const QString &table )
549{
550 if ( mTableSelectedCallback )
551 {
552 if ( !mAlreadySelectedTables.contains( table ) )
553 {
554 mTableSelectedCallback->tableSelected( table );
555 mAlreadySelectedTables.insert( table );
556 }
557 }
558}
559
560static void resetCombo( QComboBox *combo )
561{
562 // We do it in a deferred way, otherwise Valgrind complains when using QTest
563 // since basically this call a recursive call to QComboBox::setCurrentIndex()
564 // which cause internal QComboBox logic to operate on a destroyed object
565 // However that isn't reproduce in live session. Anyway this hack is safe
566 // in all modes.
567 QMetaObject::invokeMethod( combo, "setCurrentIndex", Qt::QueuedConnection, Q_ARG( int, 0 ) );
568}
569
570void QgsSQLComposerDialog::mTablesCombo_currentIndexChanged( int )
571{
572 const int index = mTablesCombo->currentIndex();
573 if ( index <= 0 )
574 return;
575 QObject *obj = mFocusedObject;
576 const QString newText = mapTableEntryTextToName[mTablesCombo->currentText()];
577 loadTableColumns( newText );
578 if ( obj == mTablesEditor )
579 {
580 const QString currentText = mTablesEditor->text();
581 if ( currentText.isEmpty() )
582 mTablesEditor->setText( newText );
583 else
584 mTablesEditor->setText( currentText + ", " + newText );
585 }
586 else if ( obj == mTableJoins )
587 {
588 if ( mTableJoins->selectedItems().size() == 1 )
589 {
590 mTableJoins->selectedItems().at( 0 )->setText( newText );
591 }
592 }
593 else if ( obj == mWhereEditor )
594 {
595 mWhereEditor->insertPlainText( newText );
596 }
597 else if ( obj == mOrderEditor )
598 {
599 mOrderEditor->insertPlainText( newText );
600 }
601 else if ( obj == mQueryEdit )
602 {
603 mQueryEdit->insertText( newText );
604 }
605 resetCombo( mTablesCombo );
606}
607
608void QgsSQLComposerDialog::mColumnsCombo_currentIndexChanged( int )
609{
610 const int index = mColumnsCombo->currentIndex();
611 if ( index <= 0 )
612 return;
613 QObject *obj = mFocusedObject;
614 const QString newText = mapColumnEntryTextToName[mColumnsCombo->currentText()];
615 if ( obj == mColumnsEditor )
616 {
617 const QString currentText = mColumnsEditor->toPlainText();
618 if ( currentText.isEmpty() )
619 mColumnsEditor->insertPlainText( newText );
620 else
621 mColumnsEditor->insertPlainText( ",\n" + newText );
622 }
623 else if ( obj == mTableJoins )
624 {
625 if ( mTableJoins->selectedItems().size() == 1 && mTableJoins->selectedItems().at( 0 )->column() == 1 )
626 {
627 const QString currentText( mTableJoins->selectedItems().at( 0 )->text() );
628 if ( !currentText.isEmpty() && !currentText.contains( "="_L1 ) )
629 mTableJoins->selectedItems().at( 0 )->setText( currentText + " = " + newText );
630 else
631 mTableJoins->selectedItems().at( 0 )->setText( currentText + newText );
632 }
633 }
634 else if ( obj == mWhereEditor )
635 {
636 mWhereEditor->insertPlainText( newText );
637 }
638 else if ( obj == mOrderEditor )
639 {
640 mOrderEditor->insertPlainText( newText );
641 }
642 else if ( obj == mQueryEdit )
643 {
644 mQueryEdit->insertText( newText );
645 }
646 resetCombo( mColumnsCombo );
647}
648
649void QgsSQLComposerDialog::mFunctionsCombo_currentIndexChanged( int )
650{
651 functionCurrentIndexChanged( mFunctionsCombo, mapFunctionEntryTextToName );
652}
653
654void QgsSQLComposerDialog::mSpatialPredicatesCombo_currentIndexChanged( int )
655{
656 functionCurrentIndexChanged( mSpatialPredicatesCombo, mapSpatialPredicateEntryTextToName );
657}
658
659void QgsSQLComposerDialog::functionCurrentIndexChanged( QComboBox *combo, const QMap<QString, QString> &mapEntryTextToName )
660{
661 const int index = combo->currentIndex();
662 if ( index <= 0 )
663 return;
664 QObject *obj = mFocusedObject;
665 const QString newText = mapEntryTextToName[combo->currentText()];
666 if ( obj == mColumnsEditor )
667 {
668 mColumnsEditor->insertPlainText( newText );
669 }
670 else if ( obj == mWhereEditor )
671 {
672 mWhereEditor->insertPlainText( newText );
673 }
674 else if ( obj == mQueryEdit )
675 {
676 mQueryEdit->insertText( newText );
677 }
678 resetCombo( combo );
679}
680
681void QgsSQLComposerDialog::mOperatorsCombo_currentIndexChanged( int )
682{
683 const int index = mOperatorsCombo->currentIndex();
684 if ( index <= 0 )
685 return;
686 QObject *obj = mFocusedObject;
687 const QString newText = mOperatorsCombo->currentText();
688 if ( obj == mColumnsEditor )
689 {
690 mColumnsEditor->insertPlainText( newText );
691 }
692 else if ( obj == mWhereEditor )
693 {
694 mWhereEditor->insertPlainText( newText );
695 }
696 else if ( obj == mTableJoins )
697 {
698 if ( mTableJoins->selectedItems().size() == 1 && mTableJoins->selectedItems().at( 0 )->column() == 1 )
699 {
700 const QString currentText( mTableJoins->selectedItems().at( 0 )->text() );
701 mTableJoins->selectedItems().at( 0 )->setText( currentText + newText );
702 }
703 }
704 else if ( obj == mQueryEdit )
705 {
706 mQueryEdit->insertText( newText );
707 }
708 resetCombo( mOperatorsCombo );
709}
710
711void QgsSQLComposerDialog::mAddJoinButton_clicked()
712{
713 int insertRow = mTableJoins->currentRow();
714 const int rowCount = mTableJoins->rowCount();
715 if ( insertRow < 0 )
716 insertRow = rowCount;
717 mTableJoins->setRowCount( rowCount + 1 );
718 for ( int row = rowCount; row > insertRow + 1; row-- )
719 {
720 mTableJoins->setItem( row, 0, mTableJoins->takeItem( row - 1, 0 ) );
721 mTableJoins->setItem( row, 1, mTableJoins->takeItem( row - 1, 1 ) );
722 }
723 mTableJoins->setItem( ( insertRow == rowCount ) ? insertRow : insertRow + 1, 0, new QTableWidgetItem( QString() ) );
724 mTableJoins->setItem( ( insertRow == rowCount ) ? insertRow : insertRow + 1, 1, new QTableWidgetItem( QString() ) );
725}
726
727void QgsSQLComposerDialog::mRemoveJoinButton_clicked()
728{
729 int row = mTableJoins->currentRow();
730 if ( row < 0 )
731 return;
732 const int rowCount = mTableJoins->rowCount();
733 for ( ; row < rowCount - 1; row++ )
734 {
735 mTableJoins->setItem( row, 0, mTableJoins->takeItem( row + 1, 0 ) );
736 mTableJoins->setItem( row, 1, mTableJoins->takeItem( row + 1, 1 ) );
737 }
738 mTableJoins->setRowCount( rowCount - 1 );
739
740 buildSQLFromFields();
741}
742
743void QgsSQLComposerDialog::reset()
744{
745 mQueryEdit->setText( mResetSql );
746}
747
748void QgsSQLComposerDialog::mTableJoins_itemSelectionChanged()
749{
750 mRemoveJoinButton->setEnabled( mTableJoins->selectedItems().size() == 1 );
751}
752
753void QgsSQLComposerDialog::addApis( const QStringList &list )
754{
755 mApiList += list;
756
757 delete mQueryEdit->lexer()->apis();
758 QsciAPIs *apis = new QsciAPIs( mQueryEdit->lexer() );
759
760 const auto constMApiList = mApiList;
761 for ( const QString &str : constMApiList )
762 {
763 apis->add( str );
764 }
765
766 apis->prepare();
767 mQueryEdit->lexer()->setAPIs( apis );
768}
769
770void QgsSQLComposerDialog::setSupportMultipleTables( bool on, const QString &mainTypename )
771{
772 mJoinsLabels->setVisible( on );
773 mTableJoins->setVisible( on );
774 mAddJoinButton->setVisible( on );
775 mRemoveJoinButton->setVisible( on );
776 mTablesCombo->setVisible( on );
777
778 QString mainTypenameFormatted;
779 if ( !mainTypename.isEmpty() )
780 mainTypenameFormatted = "(" + mainTypename + ")";
781 mQueryEdit->setToolTip( tr( "This is the SQL query editor. The SQL statement can select data from several tables, \n"
782 "but it must compulsory include the main typename %1 in the selected tables, \n"
783 "and only the geometry column of the main typename can be used as the geometry column of the resulting layer." )
784 .arg( mainTypenameFormatted ) );
785}
786
787void QgsSQLComposerDialog::showHelp()
788{
789 QgsHelp::openHelp( u"working_with_ogc/ogc_client_support.html#ogc-wfs"_s );
790}
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition qgshelp.cpp:41
Callback to do validation check on dialog validation.
virtual bool isValid(const QString &sql, QString &errorReason, QString &warningMsg)=0
method should return true if the SQL is valid. Otherwise return false and set the errorReason
Callback to do actions on table selection.
virtual void tableSelected(const QString &name)=0
method called when a table is selected
bool eventFilter(QObject *obj, QEvent *event) override
QgsSQLComposerDialog(QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
constructor
void addSpatialPredicates(const QStringList &list)
add a list of spatial predicates
void setSQLValidatorCallback(SQLValidatorCallback *sqlValidatorCallback)
Set a callback that will be called when the OK button is pushed.
void setTableSelectedCallback(TableSelectedCallback *tableSelectedCallback)
Set a callback that will be called when a new table is selected, so that new column names can be adde...
QPair< QString, QString > PairNameType
pair (name, type)
void addOperators(const QStringList &list)
add a list of operators
void addColumnNames(const QStringList &list, const QString &tableName)
add a list of column names
void setSupportMultipleTables(bool bMultipleTables, const QString &mainTypename=QString())
Sets if multiple tables/joins are supported. Default is false.
void addApis(const QStringList &list)
add a list of API for autocompletion
void setSql(const QString &sql)
initialize the SQL statement
void addFunctions(const QStringList &list)
add a list of functions
void addTableNames(const QStringList &list)
add a list of table names
QPair< QString, QString > PairNameTitle
pair (name, title)
QString sql() const
Gets the SQL statement.
QList< QgsSQLStatement::NodeColumnSorted * > orderBy() const
Returns the list of order by columns.
QList< QgsSQLStatement::NodeSelectedColumn * > columns() const
Returns the list of columns.
bool distinct() const
Returns if the SELECT is DISTINCT.
QList< QgsSQLStatement::NodeJoin * > joins() const
Returns the list of joins.
QgsSQLStatement::Node * where() const
Returns the where clause.
QList< QgsSQLStatement::NodeTableDef * > tables() const
Returns the list of tables.
virtual QString dump() const =0
Abstract virtual dump method.
static QString quotedIdentifierIfNeeded(const QString &name)
Returns a quoted column reference (in double quotes) if needed, or otherwise the original string.
QgsSubsetStringEditorInterface(QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
Constructor.
Represents a vector layer which manages a vector based dataset.
description of server functions
QString returnType
Returns type, or empty if unknown.
int maxArgs
maximum number of argument (or -1 if unknown)
int minArgs
minimum number of argument (or -1 if unknown)
QList< Argument > argumentList
list of arguments. May be empty despite minArgs > 0