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