QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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()
148 && ( ( 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] == ')' ) )
149 {
150 // First attempt is concatenation of existing search text
151 // Second attempt is just the new character
152 const int attemptCount = ( lastSearchedText.isEmpty() ) ? 1 : 2;
153 for ( int attempt = 0; attempt < attemptCount; attempt++ )
154 {
155 if ( attempt == 0 )
156 lastSearchedText += currentString;
157 else
158 lastSearchedText = currentString;
159
160 // Find the string that contains the searched text, and in case
161 // of several matches, pickup the one where the searched text is the
162 // most at the beginning.
163 int iBestCandidate = 0;
164 int idxInTextOfBestCandidate = 1000;
165 for ( int i = 1; i < mTablesCombo->count(); i++ )
166 {
167 const int idxInText = mTablesCombo->itemText( i ).indexOf( lastSearchedText, Qt::CaseInsensitive );
168 if ( idxInText >= 0 && idxInText < idxInTextOfBestCandidate )
169 {
170 iBestCandidate = i;
171 idxInTextOfBestCandidate = idxInText;
172 }
173 }
174 if ( iBestCandidate > 0 )
175 {
176 mTablesCombo->view()->setCurrentIndex( mTablesCombo->model()->index( 0, 0 ).sibling( iBestCandidate, 0 ) );
177 return true;
178 }
179 }
180 lastSearchedText.clear();
181 }
182 }
183
184 return QDialog::eventFilter( obj, event );
185}
186
188{
189 mTableSelectedCallback = tableSelectedCallback;
190}
191
193{
194 mSQLValidatorCallback = sqlValidatorCallback;
195}
196
197void QgsSQLComposerDialog::setSql( const QString &sql )
198{
199 mResetSql = sql;
200 mQueryEdit->setText( sql );
201}
202
204{
205 return mQueryEdit->text();
206}
207
208void QgsSQLComposerDialog::accept()
209{
210 if ( mSQLValidatorCallback )
211 {
212 QString errorMsg, warningMsg;
213 if ( !mSQLValidatorCallback->isValid( sql(), errorMsg, warningMsg ) )
214 {
215 if ( errorMsg.isEmpty() )
216 errorMsg = tr( "An error occurred during evaluation of the SQL statement." );
217 QMessageBox::critical( this, tr( "SQL Evaluation" ), errorMsg );
218 return;
219 }
220 if ( !warningMsg.isEmpty() )
221 {
222 QMessageBox::warning( this, tr( "SQL Evaluation" ), warningMsg );
223 }
224 }
225 if ( mLayer )
226 {
227 mLayer->setSubsetString( sql() );
228 }
229 QDialog::accept();
230}
231
232void QgsSQLComposerDialog::buildSQLFromFields()
233{
234 if ( mAlreadyModifyingFields )
235 return;
236 mAlreadyModifyingFields = true;
237 QString sql( u"SELECT "_s );
238 if ( mDistinct )
239 sql += "DISTINCT "_L1;
240 sql += mColumnsEditor->toPlainText();
241 sql += " FROM "_L1;
242 sql += mTablesEditor->text();
243
244 const int rows = mTableJoins->rowCount();
245 for ( int i = 0; i < rows; i++ )
246 {
247 QTableWidgetItem *itemTable = mTableJoins->item( i, 0 );
248 QTableWidgetItem *itemOn = mTableJoins->item( i, 1 );
249 if ( itemTable && !itemTable->text().isEmpty() && itemOn && !itemOn->text().isEmpty() )
250 {
251 sql += " JOIN "_L1;
252 sql += itemTable->text();
253 sql += " ON "_L1;
254 sql += itemOn->text();
255 }
256 }
257
258 if ( !mWhereEditor->toPlainText().isEmpty() )
259 {
260 sql += " WHERE "_L1;
261 sql += mWhereEditor->toPlainText();
262 }
263 if ( !mOrderEditor->toPlainText().isEmpty() )
264 {
265 sql += " ORDER BY "_L1;
266 sql += mOrderEditor->toPlainText();
267 }
268 mQueryEdit->setText( sql );
269
270 mAlreadyModifyingFields = false;
271}
272
273void QgsSQLComposerDialog::splitSQLIntoFields()
274{
275 if ( mAlreadyModifyingFields )
276 return;
277 const QgsSQLStatement sql( mQueryEdit->text() );
278 if ( sql.hasParserError() )
279 return;
280 const QgsSQLStatement::NodeSelect *nodeSelect = dynamic_cast<const QgsSQLStatement::NodeSelect *>( sql.rootNode() );
281 if ( !nodeSelect )
282 return;
283 mDistinct = nodeSelect->distinct();
284 const QList<QgsSQLStatement::NodeSelectedColumn *> columns = nodeSelect->columns();
285 QString columnText;
286 const auto constColumns = columns;
287 for ( QgsSQLStatement::NodeSelectedColumn *column : constColumns )
288 {
289 if ( !columnText.isEmpty() )
290 columnText += ", "_L1;
291 columnText += column->dump();
292 }
293
294 const QList<QgsSQLStatement::NodeTableDef *> tables = nodeSelect->tables();
295 QString tablesText;
296 const auto constTables = tables;
297 for ( QgsSQLStatement::NodeTableDef *table : constTables )
298 {
299 if ( !tablesText.isEmpty() )
300 tablesText += ", "_L1;
301 loadTableColumns( QgsSQLStatement::quotedIdentifierIfNeeded( table->name() ) );
302 tablesText += table->dump();
303 }
304
305 QString whereText;
306 QgsSQLStatement::Node *where = nodeSelect->where();
307 if ( where )
308 whereText = where->dump();
309
310 QString orderText;
311 const QList<QgsSQLStatement::NodeColumnSorted *> orderColumns = nodeSelect->orderBy();
312 const auto constOrderColumns = orderColumns;
313 for ( QgsSQLStatement::NodeColumnSorted *column : constOrderColumns )
314 {
315 if ( !orderText.isEmpty() )
316 orderText += ", "_L1;
317 orderText += column->dump();
318 }
319
320 const QList<QgsSQLStatement::NodeJoin *> joins = nodeSelect->joins();
321
322 mAlreadyModifyingFields = true;
323 mColumnsEditor->setPlainText( columnText );
324 mTablesEditor->setText( tablesText );
325 mWhereEditor->setPlainText( whereText );
326 mOrderEditor->setPlainText( orderText );
327
328 mTableJoins->setRowCount( joins.size() + 1 );
329 int iRow = 0;
330 const auto constJoins = joins;
331 for ( QgsSQLStatement::NodeJoin *join : constJoins )
332 {
333 loadTableColumns( QgsSQLStatement::quotedIdentifierIfNeeded( join->tableDef()->name() ) );
334 mTableJoins->setItem( iRow, 0, new QTableWidgetItem( join->tableDef()->dump() ) );
335 if ( join->onExpr() )
336 mTableJoins->setItem( iRow, 1, new QTableWidgetItem( join->onExpr()->dump() ) );
337 else
338 mTableJoins->setItem( iRow, 1, new QTableWidgetItem( QString() ) );
339 iRow++;
340 }
341 mTableJoins->setItem( iRow, 0, new QTableWidgetItem( QString() ) );
342 mTableJoins->setItem( iRow, 1, new QTableWidgetItem( QString() ) );
343
344 mAlreadyModifyingFields = false;
345}
346
347void QgsSQLComposerDialog::addTableNames( const QStringList &list )
348{
349 const auto constList = list;
350 for ( const QString &name : constList )
351 mapTableEntryTextToName[name] = name;
352 mTablesCombo->addItems( list );
353 addApis( list );
354}
355
356void QgsSQLComposerDialog::addTableNames( const QList<PairNameTitle> &listNameTitle )
357{
358 QStringList listCombo;
359 QStringList listApi;
360 const auto constListNameTitle = listNameTitle;
361 for ( const PairNameTitle &pair : constListNameTitle )
362 {
363 listApi << pair.first;
364 QString entryText( pair.first );
365 if ( !pair.second.isEmpty() && pair.second != pair.first )
366 {
367 if ( pair.second.size() < 40 )
368 entryText += " (" + pair.second + ")";
369 else
370 entryText += " (" + pair.second.mid( 0, 20 ) + QChar( 0x2026 ) + pair.second.mid( pair.second.size() - 20 ) + ")";
371 }
372 listCombo << entryText;
373 mapTableEntryTextToName[entryText] = pair.first;
374 }
375 mTablesCombo->addItems( listCombo );
376 addApis( listApi );
377}
378
379void QgsSQLComposerDialog::addColumnNames( const QStringList &list, const QString &tableName )
380{
381 QList<PairNameType> listPair;
382 const auto constList = list;
383 for ( const QString &name : constList )
384 listPair << PairNameType( name, QString() );
385 addColumnNames( listPair, tableName );
386}
387
388static QString sanitizeType( QString type )
389{
390 if ( type.startsWith( "xs:"_L1 ) )
391 return type.mid( 3 );
392 if ( type.startsWith( "xsd:"_L1 ) )
393 return type.mid( 4 );
394 if ( type == "gml:AbstractGeometryType"_L1 )
395 return u"geometry"_s;
396 return type;
397}
398
399void QgsSQLComposerDialog::addColumnNames( const QList<PairNameType> &list, const QString &tableName )
400{
401 mAlreadySelectedTables.insert( tableName );
402 if ( mColumnsCombo->count() > 1 )
403 mColumnsCombo->insertSeparator( mColumnsCombo->count() );
404
405 QStringList listCombo;
406 QStringList listApi;
407 const auto constList = list;
408 for ( const PairNameType &pair : constList )
409 {
410 listApi << pair.first;
411 QString entryText( pair.first );
412 if ( !pair.second.isEmpty() )
413 {
414 entryText += " (" + sanitizeType( pair.second ) + ")";
415 }
416 listCombo << entryText;
417 mapColumnEntryTextToName[entryText] = pair.first;
418 }
419 mColumnsCombo->addItems( listCombo );
420
421 addApis( listApi );
422}
423
424void QgsSQLComposerDialog::addOperators( const QStringList &list )
425{
426 mOperatorsCombo->addItems( list );
427 addApis( list );
428}
429
430static QString getFunctionAbbridgedParameters( const QgsSQLComposerDialog::Function &f )
431{
432 if ( f.minArgs >= 0 && f.maxArgs > f.minArgs )
433 {
434 return QObject::tr( "%1 to %n argument(s)", nullptr, f.maxArgs ).arg( f.minArgs );
435 }
436 else if ( f.minArgs == 0 && f.maxArgs == 0 )
437 {
438 }
439 else if ( f.minArgs > 0 && f.maxArgs == f.minArgs )
440 {
441 return QObject::tr( "%n argument(s)", nullptr, f.minArgs );
442 }
443 else if ( f.minArgs >= 0 && f.maxArgs < 0 )
444 {
445 return QObject::tr( "%n argument(s) or more", nullptr, f.minArgs );
446 }
447 return QString();
448}
449
450
451void QgsSQLComposerDialog::getFunctionList( const QList<Function> &list, QStringList &listApi, QStringList &listCombo, QMap<QString, QString> &mapEntryTextToName )
452{
453 const auto constList = list;
454 for ( const Function &f : constList )
455 {
456 listApi << f.name;
457 QString entryText( f.name );
458 entryText += '('_L1;
459 if ( !f.argumentList.isEmpty() )
460 {
461 for ( int i = 0; i < f.argumentList.size(); i++ )
462 {
463 if ( f.minArgs >= 0 && i >= f.minArgs )
464 entryText += '['_L1;
465 if ( i > 0 )
466 entryText += ", "_L1;
467 if ( f.argumentList[i].name == "number"_L1 && !f.argumentList[i].type.isEmpty() )
468 {
469 entryText += sanitizeType( f.argumentList[i].type );
470 }
471 else
472 {
473 entryText += f.argumentList[i].name;
474 const QString sanitizedType( sanitizeType( f.argumentList[i].type ) );
475 if ( !f.argumentList[i].type.isEmpty() && f.argumentList[i].name != sanitizedType )
476 {
477 entryText += ": "_L1;
478 entryText += sanitizedType;
479 }
480 }
481 if ( f.minArgs >= 0 && i >= f.minArgs )
482 entryText += ']'_L1;
483 }
484 if ( entryText.size() > 60 )
485 {
486 entryText = f.name;
487 entryText += '('_L1;
488 entryText += getFunctionAbbridgedParameters( f );
489 }
490 }
491 else
492 {
493 entryText += getFunctionAbbridgedParameters( f );
494 }
495 entryText += ')'_L1;
496 if ( !f.returnType.isEmpty() )
497 entryText += ": " + sanitizeType( f.returnType );
498 listCombo << entryText;
499 mapEntryTextToName[entryText] = f.name + "(";
500 }
501}
502
503void QgsSQLComposerDialog::addSpatialPredicates( const QStringList &list )
504{
505 QList<Function> listFunction;
506 const auto constList = list;
507 for ( const QString &name : constList )
508 {
509 Function f;
510 f.name = name;
511 listFunction << f;
512 }
513 addSpatialPredicates( listFunction );
514}
515
516void QgsSQLComposerDialog::addSpatialPredicates( const QList<Function> &list )
517{
518 QStringList listApi;
519 QStringList listCombo;
520 getFunctionList( list, listApi, listCombo, mapSpatialPredicateEntryTextToName );
521 mSpatialPredicatesCombo->addItems( listCombo );
522 mSpatialPredicatesCombo->show();
523 addApis( listApi );
524}
525
526void QgsSQLComposerDialog::addFunctions( const QStringList &list )
527{
528 QList<Function> listFunction;
529 const auto constList = list;
530 for ( const QString &name : constList )
531 {
532 Function f;
533 f.name = name;
534 listFunction << f;
535 }
536 addFunctions( listFunction );
537}
538
539void QgsSQLComposerDialog::addFunctions( const QList<Function> &list )
540{
541 QStringList listApi;
542 QStringList listCombo;
543 getFunctionList( list, listApi, listCombo, mapFunctionEntryTextToName );
544 mFunctionsCombo->addItems( listCombo );
545 mFunctionsCombo->show();
546 addApis( listApi );
547}
548
549void QgsSQLComposerDialog::loadTableColumns( const QString &table )
550{
551 if ( mTableSelectedCallback )
552 {
553 if ( !mAlreadySelectedTables.contains( table ) )
554 {
555 mTableSelectedCallback->tableSelected( table );
556 mAlreadySelectedTables.insert( table );
557 }
558 }
559}
560
561static void resetCombo( QComboBox *combo )
562{
563 // We do it in a deferred way, otherwise Valgrind complains when using QTest
564 // since basically this call a recursive call to QComboBox::setCurrentIndex()
565 // which cause internal QComboBox logic to operate on a destroyed object
566 // However that isn't reproduce in live session. Anyway this hack is safe
567 // in all modes.
568 QMetaObject::invokeMethod( combo, "setCurrentIndex", Qt::QueuedConnection, Q_ARG( int, 0 ) );
569}
570
571void QgsSQLComposerDialog::mTablesCombo_currentIndexChanged( int )
572{
573 const int index = mTablesCombo->currentIndex();
574 if ( index <= 0 )
575 return;
576 QObject *obj = mFocusedObject;
577 const QString newText = mapTableEntryTextToName[mTablesCombo->currentText()];
578 loadTableColumns( newText );
579 if ( obj == mTablesEditor )
580 {
581 const QString currentText = mTablesEditor->text();
582 if ( currentText.isEmpty() )
583 mTablesEditor->setText( newText );
584 else
585 mTablesEditor->setText( currentText + ", " + newText );
586 }
587 else if ( obj == mTableJoins )
588 {
589 if ( mTableJoins->selectedItems().size() == 1 )
590 {
591 mTableJoins->selectedItems().at( 0 )->setText( newText );
592 }
593 }
594 else if ( obj == mWhereEditor )
595 {
596 mWhereEditor->insertPlainText( newText );
597 }
598 else if ( obj == mOrderEditor )
599 {
600 mOrderEditor->insertPlainText( newText );
601 }
602 else if ( obj == mQueryEdit )
603 {
604 mQueryEdit->insertText( newText );
605 }
606 resetCombo( mTablesCombo );
607}
608
609void QgsSQLComposerDialog::mColumnsCombo_currentIndexChanged( int )
610{
611 const int index = mColumnsCombo->currentIndex();
612 if ( index <= 0 )
613 return;
614 QObject *obj = mFocusedObject;
615 const QString newText = mapColumnEntryTextToName[mColumnsCombo->currentText()];
616 if ( obj == mColumnsEditor )
617 {
618 const QString currentText = mColumnsEditor->toPlainText();
619 if ( currentText.isEmpty() )
620 mColumnsEditor->insertPlainText( newText );
621 else
622 mColumnsEditor->insertPlainText( ",\n" + newText );
623 }
624 else if ( obj == mTableJoins )
625 {
626 if ( mTableJoins->selectedItems().size() == 1 && mTableJoins->selectedItems().at( 0 )->column() == 1 )
627 {
628 const QString currentText( mTableJoins->selectedItems().at( 0 )->text() );
629 if ( !currentText.isEmpty() && !currentText.contains( "="_L1 ) )
630 mTableJoins->selectedItems().at( 0 )->setText( currentText + " = " + newText );
631 else
632 mTableJoins->selectedItems().at( 0 )->setText( currentText + newText );
633 }
634 }
635 else if ( obj == mWhereEditor )
636 {
637 mWhereEditor->insertPlainText( newText );
638 }
639 else if ( obj == mOrderEditor )
640 {
641 mOrderEditor->insertPlainText( newText );
642 }
643 else if ( obj == mQueryEdit )
644 {
645 mQueryEdit->insertText( newText );
646 }
647 resetCombo( mColumnsCombo );
648}
649
650void QgsSQLComposerDialog::mFunctionsCombo_currentIndexChanged( int )
651{
652 functionCurrentIndexChanged( mFunctionsCombo, mapFunctionEntryTextToName );
653}
654
655void QgsSQLComposerDialog::mSpatialPredicatesCombo_currentIndexChanged( int )
656{
657 functionCurrentIndexChanged( mSpatialPredicatesCombo, mapSpatialPredicateEntryTextToName );
658}
659
660void QgsSQLComposerDialog::functionCurrentIndexChanged( QComboBox *combo, const QMap<QString, QString> &mapEntryTextToName )
661{
662 const int index = combo->currentIndex();
663 if ( index <= 0 )
664 return;
665 QObject *obj = mFocusedObject;
666 const QString newText = mapEntryTextToName[combo->currentText()];
667 if ( obj == mColumnsEditor )
668 {
669 mColumnsEditor->insertPlainText( newText );
670 }
671 else if ( obj == mWhereEditor )
672 {
673 mWhereEditor->insertPlainText( newText );
674 }
675 else if ( obj == mQueryEdit )
676 {
677 mQueryEdit->insertText( newText );
678 }
679 resetCombo( combo );
680}
681
682void QgsSQLComposerDialog::mOperatorsCombo_currentIndexChanged( int )
683{
684 const int index = mOperatorsCombo->currentIndex();
685 if ( index <= 0 )
686 return;
687 QObject *obj = mFocusedObject;
688 const QString newText = mOperatorsCombo->currentText();
689 if ( obj == mColumnsEditor )
690 {
691 mColumnsEditor->insertPlainText( newText );
692 }
693 else if ( obj == mWhereEditor )
694 {
695 mWhereEditor->insertPlainText( newText );
696 }
697 else if ( obj == mTableJoins )
698 {
699 if ( mTableJoins->selectedItems().size() == 1 && mTableJoins->selectedItems().at( 0 )->column() == 1 )
700 {
701 const QString currentText( mTableJoins->selectedItems().at( 0 )->text() );
702 mTableJoins->selectedItems().at( 0 )->setText( currentText + newText );
703 }
704 }
705 else if ( obj == mQueryEdit )
706 {
707 mQueryEdit->insertText( newText );
708 }
709 resetCombo( mOperatorsCombo );
710}
711
712void QgsSQLComposerDialog::mAddJoinButton_clicked()
713{
714 int insertRow = mTableJoins->currentRow();
715 const int rowCount = mTableJoins->rowCount();
716 if ( insertRow < 0 )
717 insertRow = rowCount;
718 mTableJoins->setRowCount( rowCount + 1 );
719 for ( int row = rowCount; row > insertRow + 1; row-- )
720 {
721 mTableJoins->setItem( row, 0, mTableJoins->takeItem( row - 1, 0 ) );
722 mTableJoins->setItem( row, 1, mTableJoins->takeItem( row - 1, 1 ) );
723 }
724 mTableJoins->setItem( ( insertRow == rowCount ) ? insertRow : insertRow + 1, 0, new QTableWidgetItem( QString() ) );
725 mTableJoins->setItem( ( insertRow == rowCount ) ? insertRow : insertRow + 1, 1, new QTableWidgetItem( QString() ) );
726}
727
728void QgsSQLComposerDialog::mRemoveJoinButton_clicked()
729{
730 int row = mTableJoins->currentRow();
731 if ( row < 0 )
732 return;
733 const int rowCount = mTableJoins->rowCount();
734 for ( ; row < rowCount - 1; row++ )
735 {
736 mTableJoins->setItem( row, 0, mTableJoins->takeItem( row + 1, 0 ) );
737 mTableJoins->setItem( row, 1, mTableJoins->takeItem( row + 1, 1 ) );
738 }
739 mTableJoins->setRowCount( rowCount - 1 );
740
741 buildSQLFromFields();
742}
743
744void QgsSQLComposerDialog::reset()
745{
746 mQueryEdit->setText( mResetSql );
747}
748
749void QgsSQLComposerDialog::mTableJoins_itemSelectionChanged()
750{
751 mRemoveJoinButton->setEnabled( mTableJoins->selectedItems().size() == 1 );
752}
753
754void QgsSQLComposerDialog::addApis( const QStringList &list )
755{
756 mApiList += list;
757
758 delete mQueryEdit->lexer()->apis();
759 QsciAPIs *apis = new QsciAPIs( mQueryEdit->lexer() );
760
761 const auto constMApiList = mApiList;
762 for ( const QString &str : constMApiList )
763 {
764 apis->add( str );
765 }
766
767 apis->prepare();
768 mQueryEdit->lexer()->setAPIs( apis );
769}
770
771void QgsSQLComposerDialog::setSupportMultipleTables( bool on, const QString &mainTypename )
772{
773 mJoinsLabels->setVisible( on );
774 mTableJoins->setVisible( on );
775 mAddJoinButton->setVisible( on );
776 mRemoveJoinButton->setVisible( on );
777 mTablesCombo->setVisible( on );
778
779 QString mainTypenameFormatted;
780 if ( !mainTypename.isEmpty() )
781 mainTypenameFormatted = "(" + mainTypename + ")";
782 mQueryEdit->setToolTip( tr(
783 "This is the SQL query editor. The SQL statement can select data from several tables, \n"
784 "but it must compulsory include the main typename %1 in the selected tables, \n"
785 "and only the geometry column of the main typename can be used as the geometry column of the resulting layer."
786 )
787 .arg( mainTypenameFormatted ) );
788}
789
790void QgsSQLComposerDialog::showHelp()
791{
792 QgsHelp::openHelp( u"working_with_ogc/ogc_client_support.html#ogc-wfs"_s );
793}
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