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