25 #include <QMessageBox>
28 #include <Qsci/qscilexer.h>
31 : QDialog( parent, fl )
34 connect( mTablesCombo,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsSQLComposerDialog::mTablesCombo_currentIndexChanged );
35 connect( mColumnsCombo,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsSQLComposerDialog::mColumnsCombo_currentIndexChanged );
36 connect( mSpatialPredicatesCombo,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsSQLComposerDialog::mSpatialPredicatesCombo_currentIndexChanged );
37 connect( mFunctionsCombo,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsSQLComposerDialog::mFunctionsCombo_currentIndexChanged );
38 connect( mOperatorsCombo,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsSQLComposerDialog::mOperatorsCombo_currentIndexChanged );
39 connect( mAddJoinButton, &QPushButton::clicked,
this, &QgsSQLComposerDialog::mAddJoinButton_clicked );
40 connect( mRemoveJoinButton, &QPushButton::clicked,
this, &QgsSQLComposerDialog::mRemoveJoinButton_clicked );
41 connect( mTableJoins, &QTableWidget::itemSelectionChanged,
this, &QgsSQLComposerDialog::mTableJoins_itemSelectionChanged );
43 mQueryEdit->setWrapMode( QsciScintilla::WrapWord );
44 mQueryEdit->installEventFilter(
this );
45 mColumnsEditor->installEventFilter(
this );
46 mTablesEditor->installEventFilter(
this );
47 mTableJoins->installEventFilter(
this );
48 mWhereEditor->installEventFilter(
this );
49 mOrderEditor->installEventFilter(
this );
50 mTablesCombo->view()->installEventFilter(
this );
53 connect( mButtonBox->button( QDialogButtonBox::Reset ), &QAbstractButton::clicked,
54 this, &QgsSQLComposerDialog::reset );
56 connect( mQueryEdit, &QsciScintilla::textChanged,
57 this, &QgsSQLComposerDialog::splitSQLIntoFields );
58 connect( mColumnsEditor, &QTextEdit::textChanged,
59 this, &QgsSQLComposerDialog::buildSQLFromFields );
60 connect( mTablesEditor, &QLineEdit::textChanged,
61 this, &QgsSQLComposerDialog::buildSQLFromFields );
62 connect( mWhereEditor, &QTextEdit::textChanged,
63 this, &QgsSQLComposerDialog::buildSQLFromFields );
64 connect( mOrderEditor, &QTextEdit::textChanged,
65 this, &QgsSQLComposerDialog::buildSQLFromFields );
66 connect( mTableJoins, &QTableWidget::cellChanged,
67 this, &QgsSQLComposerDialog::buildSQLFromFields );
68 connect( mButtonBox, &QDialogButtonBox::helpRequested,
69 this, &QgsSQLComposerDialog::showHelp );
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" );
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(
"IS" );
99 operatorsList << QStringLiteral(
"IS NOT" );
100 operatorsList << QStringLiteral(
"IN" );
101 operatorsList << QStringLiteral(
"LIKE" );
102 operatorsList << QStringLiteral(
"BETWEEN" );
105 mAggregatesCombo->hide();
106 mFunctionsCombo->hide();
107 mSpatialPredicatesCombo->hide();
108 mStringFunctionsCombo->hide();
110 delete mPageColumnsValues;
111 mPageColumnsValues =
nullptr;
113 mRemoveJoinButton->setEnabled(
false );
115 mTableJoins->setRowCount( 0 );
116 mTableJoins->setItem( 0, 0,
new QTableWidgetItem( QString() ) );
117 mTableJoins->setItem( 0, 1,
new QTableWidgetItem( QString() ) );
125 delete mQueryEdit->lexer()->apis();
126 mQueryEdit->lexer()->setAPIs(
nullptr );
131 if ( event->type() == QEvent::FocusIn )
133 if ( obj == mTablesCombo->view() )
134 lastSearchedText.clear();
136 mFocusedObject = obj;
140 if ( event->type() == QEvent::KeyPress && obj == mTablesCombo->view() )
142 QString currentString = ( ( QKeyEvent * )event )->text();
143 if ( !currentString.isEmpty() && ( ( currentString[0] >=
'a' && currentString[0] <=
'z' ) ||
144 ( currentString[0] >=
'A' && currentString[0] <=
'Z' ) ||
145 ( currentString[0] >=
'0' && currentString[0] <=
'9' ) ||
146 currentString[0] ==
':' || currentString[0] ==
'_' || currentString[0] ==
' ' ||
147 currentString[0] ==
'(' || currentString[0] ==
')' ) )
151 int attemptCount = ( lastSearchedText.isEmpty() ) ? 1 : 2;
152 for (
int attempt = 0; attempt < attemptCount; attempt ++ )
155 lastSearchedText += currentString;
157 lastSearchedText = currentString;
162 int iBestCandidate = 0;
163 int idxInTextOfBestCandidate = 1000;
164 for (
int i = 1; i < mTablesCombo->count(); i++ )
166 int idxInText = mTablesCombo->itemText( i ).indexOf( lastSearchedText, Qt::CaseInsensitive );
167 if ( idxInText >= 0 && idxInText < idxInTextOfBestCandidate )
170 idxInTextOfBestCandidate = idxInText;
173 if ( iBestCandidate > 0 )
175 mTablesCombo->view()->setCurrentIndex( mTablesCombo->model()->index( 0, 0 ).sibling( iBestCandidate, 0 ) );
179 lastSearchedText.clear();
183 return QDialog::eventFilter( obj, event );
188 mTableSelectedCallback = tableSelectedCallback;
193 mSQLValidatorCallback = sqlValidatorCallback;
199 mQueryEdit->setText(
sql );
204 return mQueryEdit->text();
207 void QgsSQLComposerDialog::accept()
209 if ( mSQLValidatorCallback )
211 QString errorMsg, warningMsg;
212 if ( !mSQLValidatorCallback->
isValid(
sql(), errorMsg, warningMsg ) )
214 if ( errorMsg.isEmpty() )
215 errorMsg = tr(
"An error occurred during evaluation of the SQL statement." );
216 QMessageBox::critical(
this, tr(
"SQL Evaluation" ), errorMsg );
219 if ( !warningMsg.isEmpty() )
221 QMessageBox::warning(
this, tr(
"SQL Evaluation" ), warningMsg );
227 void QgsSQLComposerDialog::buildSQLFromFields()
229 if ( mAlreadyModifyingFields )
231 mAlreadyModifyingFields =
true;
232 QString
sql( QStringLiteral(
"SELECT " ) );
234 sql += QLatin1String(
"DISTINCT " );
235 sql += mColumnsEditor->toPlainText();
236 sql += QLatin1String(
" FROM " );
237 sql += mTablesEditor->text();
239 int rows = mTableJoins->rowCount();
240 for (
int i = 0; i < rows; i++ )
242 QTableWidgetItem *itemTable = mTableJoins->item( i, 0 );
243 QTableWidgetItem *itemOn = mTableJoins->item( i, 1 );
244 if ( itemTable && !itemTable->text().isEmpty() &&
245 itemOn && !itemOn->text().isEmpty() )
247 sql += QLatin1String(
" JOIN " );
248 sql += itemTable->text();
249 sql += QLatin1String(
" ON " );
250 sql += itemOn->text();
254 if ( !mWhereEditor->toPlainText().isEmpty() )
256 sql += QLatin1String(
" WHERE " );
257 sql += mWhereEditor->toPlainText();
259 if ( !mOrderEditor->toPlainText().isEmpty() )
261 sql += QLatin1String(
" ORDER BY " );
262 sql += mOrderEditor->toPlainText();
264 mQueryEdit->setText(
sql );
266 mAlreadyModifyingFields =
false;
269 void QgsSQLComposerDialog::splitSQLIntoFields()
271 if ( mAlreadyModifyingFields )
274 if (
sql.hasParserError() )
280 QList<QgsSQLStatement::NodeSelectedColumn *> columns = nodeSelect->
columns();
282 const auto constColumns = columns;
285 if ( !columnText.isEmpty() )
286 columnText += QLatin1String(
", " );
287 columnText += column->dump();
290 QList<QgsSQLStatement::NodeTableDef *> tables = nodeSelect->
tables();
292 const auto constTables = tables;
295 if ( !tablesText.isEmpty() )
296 tablesText += QLatin1String(
", " );
298 tablesText += table->dump();
304 whereText = where->
dump();
307 QList<QgsSQLStatement::NodeColumnSorted *> orderColumns = nodeSelect->
orderBy();
308 const auto constOrderColumns = orderColumns;
311 if ( !orderText.isEmpty() )
312 orderText += QLatin1String(
", " );
313 orderText += column->dump();
316 QList<QgsSQLStatement::NodeJoin *> joins = nodeSelect->
joins();
318 mAlreadyModifyingFields =
true;
319 mColumnsEditor->setPlainText( columnText );
320 mTablesEditor->setText( tablesText );
321 mWhereEditor->setPlainText( whereText );
322 mOrderEditor->setPlainText( orderText );
324 mTableJoins->setRowCount( joins.size() + 1 );
326 const auto constJoins = joins;
330 mTableJoins->setItem( iRow, 0,
new QTableWidgetItem( join->tableDef()->dump() ) );
331 if ( join->onExpr() )
332 mTableJoins->setItem( iRow, 1,
new QTableWidgetItem( join->onExpr()->dump() ) );
334 mTableJoins->setItem( iRow, 1,
new QTableWidgetItem( QString() ) );
337 mTableJoins->setItem( iRow, 0,
new QTableWidgetItem( QString() ) );
338 mTableJoins->setItem( iRow, 1,
new QTableWidgetItem( QString() ) );
340 mAlreadyModifyingFields =
false;
345 const auto constList = list;
346 for (
const QString &name : constList )
347 mapTableEntryTextToName[name] = name;
348 mTablesCombo->addItems( list );
354 QStringList listCombo;
356 const auto constListNameTitle = listNameTitle;
359 listApi << pair.first;
360 QString entryText( pair.first );
361 if ( !pair.second.isEmpty() && pair.second != pair.first )
363 if ( pair.second.size() < 40 )
364 entryText +=
" (" + pair.second +
")";
366 entryText +=
" (" + pair.second.mid( 0, 20 ) + QChar( 0x2026 ) + pair.second.mid( pair.second.size() - 20 ) +
")";
368 listCombo << entryText;
369 mapTableEntryTextToName[entryText] = pair.first;
371 mTablesCombo->addItems( listCombo );
377 QList<PairNameType> listPair;
378 const auto constList = list;
379 for (
const QString &name : constList )
384 static QString sanitizeType( QString type )
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" );
397 mAlreadySelectedTables.insert( tableName );
398 if ( mColumnsCombo->count() > 1 )
399 mColumnsCombo->insertSeparator( mColumnsCombo->count() );
401 QStringList listCombo;
403 const auto constList = list;
406 listApi << pair.first;
407 QString entryText( pair.first );
408 if ( !pair.second.isEmpty() )
410 entryText +=
" (" + sanitizeType( pair.second ) +
")";
412 listCombo << entryText;
413 mapColumnEntryTextToName[entryText] = pair.first;
415 mColumnsCombo->addItems( listCombo );
422 mOperatorsCombo->addItems( list );
430 return QObject::tr(
"%1 to %2 arguments" ).arg( f.
minArgs ).arg( f.
maxArgs );
438 return QObject::tr(
"1 argument" );
440 return QObject::tr(
"%1 arguments" ).arg( f.
minArgs );
445 return QObject::tr(
"%1 arguments or more" ).arg( f.
minArgs );
447 return QObject::tr(
"1 argument or more" );
449 return QObject::tr(
"0 argument or more" );
455 void QgsSQLComposerDialog::getFunctionList(
const QList<Function> &list,
456 QStringList &listApi,
457 QStringList &listCombo,
458 QMap<QString, QString> &mapEntryTextToName )
460 const auto constList = list;
461 for (
const Function &f : constList )
464 QString entryText( f.
name );
465 entryText += QLatin1String(
"(" );
470 if ( f.
minArgs >= 0 && i >= f.
minArgs ) entryText += QLatin1String(
"[" );
471 if ( i > 0 ) entryText += QLatin1String(
", " );
479 QString sanitizedType( sanitizeType( f.
argumentList[i].type ) );
483 entryText += QLatin1String(
": " );
484 entryText += sanitizedType;
487 if ( f.
minArgs >= 0 && i >= f.
minArgs ) entryText += QLatin1String(
"]" );
489 if ( entryText.size() > 60 )
492 entryText += QLatin1String(
"(" );
493 entryText += getFunctionAbbridgedParameters( f );
498 entryText += getFunctionAbbridgedParameters( f );
500 entryText += QLatin1String(
")" );
502 entryText +=
": " + sanitizeType( f.
returnType );
503 listCombo << entryText;
504 mapEntryTextToName[entryText] = f.
name +
"(";
510 QList<Function> listFunction;
511 const auto constList = list;
512 for (
const QString &name : constList )
524 QStringList listCombo;
525 getFunctionList( list, listApi, listCombo, mapSpatialPredicateEntryTextToName );
526 mSpatialPredicatesCombo->addItems( listCombo );
527 mSpatialPredicatesCombo->show();
533 QList<Function> listFunction;
534 const auto constList = list;
535 for (
const QString &name : constList )
547 QStringList listCombo;
548 getFunctionList( list, listApi, listCombo, mapFunctionEntryTextToName );
549 mFunctionsCombo->addItems( listCombo );
550 mFunctionsCombo->show();
554 void QgsSQLComposerDialog::loadTableColumns(
const QString &table )
556 if ( mTableSelectedCallback )
558 if ( !mAlreadySelectedTables.contains( table ) )
561 mAlreadySelectedTables.insert( table );
566 static void resetCombo( QComboBox *combo )
573 QMetaObject::invokeMethod( combo,
"setCurrentIndex", Qt::QueuedConnection, Q_ARG(
int, 0 ) );
576 void QgsSQLComposerDialog::mTablesCombo_currentIndexChanged(
int )
578 int index = mTablesCombo->currentIndex();
581 QObject *obj = mFocusedObject;
582 QString newText = mapTableEntryTextToName[mTablesCombo->currentText()];
583 loadTableColumns( newText );
584 if ( obj == mTablesEditor )
586 QString currentText = mTablesEditor->text();
587 if ( currentText.isEmpty() )
588 mTablesEditor->setText( newText );
590 mTablesEditor->setText( currentText +
", " + newText );
592 else if ( obj == mTableJoins )
594 if ( mTableJoins->selectedItems().size() == 1 )
596 mTableJoins->selectedItems().at( 0 )->setText( newText );
599 else if ( obj == mWhereEditor )
601 mWhereEditor->insertPlainText( newText );
603 else if ( obj == mOrderEditor )
605 mOrderEditor->insertPlainText( newText );
607 else if ( obj == mQueryEdit )
609 mQueryEdit->insertText( newText );
611 resetCombo( mTablesCombo );
614 void QgsSQLComposerDialog::mColumnsCombo_currentIndexChanged(
int )
616 int index = mColumnsCombo->currentIndex();
619 QObject *obj = mFocusedObject;
620 QString newText = mapColumnEntryTextToName[mColumnsCombo->currentText()];
621 if ( obj == mColumnsEditor )
623 QString currentText = mColumnsEditor->toPlainText();
624 if ( currentText.isEmpty() )
625 mColumnsEditor->insertPlainText( newText );
627 mColumnsEditor->insertPlainText(
",\n" + newText );
629 else if ( obj == mTableJoins )
631 if ( mTableJoins->selectedItems().size() == 1 &&
632 mTableJoins->selectedItems().at( 0 )->column() == 1 )
634 QString currentText( mTableJoins->selectedItems().at( 0 )->text() );
635 if ( !currentText.isEmpty() && !currentText.contains( QLatin1String(
"=" ) ) )
636 mTableJoins->selectedItems().at( 0 )->setText( currentText +
" = " + newText );
638 mTableJoins->selectedItems().at( 0 )->setText( currentText + newText );
641 else if ( obj == mWhereEditor )
643 mWhereEditor->insertPlainText( newText );
645 else if ( obj == mOrderEditor )
647 mOrderEditor->insertPlainText( newText );
649 else if ( obj == mQueryEdit )
651 mQueryEdit->insertText( newText );
653 resetCombo( mColumnsCombo );
656 void QgsSQLComposerDialog::mFunctionsCombo_currentIndexChanged(
int )
658 functionCurrentIndexChanged( mFunctionsCombo, mapFunctionEntryTextToName );
661 void QgsSQLComposerDialog::mSpatialPredicatesCombo_currentIndexChanged(
int )
663 functionCurrentIndexChanged( mSpatialPredicatesCombo, mapSpatialPredicateEntryTextToName );
666 void QgsSQLComposerDialog::functionCurrentIndexChanged( QComboBox *combo,
667 const QMap<QString, QString> &mapEntryTextToName )
669 int index = combo->currentIndex();
672 QObject *obj = mFocusedObject;
673 QString newText = mapEntryTextToName[combo->currentText()];
674 if ( obj == mColumnsEditor )
676 mColumnsEditor->insertPlainText( newText );
678 else if ( obj == mWhereEditor )
680 mWhereEditor->insertPlainText( newText );
682 else if ( obj == mQueryEdit )
684 mQueryEdit->insertText( newText );
689 void QgsSQLComposerDialog::mOperatorsCombo_currentIndexChanged(
int )
691 int index = mOperatorsCombo->currentIndex();
694 QObject *obj = mFocusedObject;
695 QString newText = mOperatorsCombo->currentText();
696 if ( obj == mColumnsEditor )
698 mColumnsEditor->insertPlainText( newText );
700 else if ( obj == mWhereEditor )
702 mWhereEditor->insertPlainText( newText );
704 else if ( obj == mTableJoins )
706 if ( mTableJoins->selectedItems().size() == 1 &&
707 mTableJoins->selectedItems().at( 0 )->column() == 1 )
709 QString currentText( mTableJoins->selectedItems().at( 0 )->text() );
710 mTableJoins->selectedItems().at( 0 )->setText( currentText + newText );
713 else if ( obj == mQueryEdit )
715 mQueryEdit->insertText( newText );
717 resetCombo( mOperatorsCombo );
720 void QgsSQLComposerDialog::mAddJoinButton_clicked()
722 int insertRow = mTableJoins->currentRow();
723 int rowCount = mTableJoins->rowCount();
725 insertRow = rowCount;
726 mTableJoins->setRowCount( rowCount + 1 );
727 for (
int row = rowCount ; row > insertRow + 1; row -- )
729 mTableJoins->setItem( row, 0, mTableJoins->takeItem( row - 1, 0 ) );
730 mTableJoins->setItem( row, 1, mTableJoins->takeItem( row - 1, 1 ) );
732 mTableJoins->setItem( ( insertRow == rowCount ) ? insertRow : insertRow + 1, 0,
new QTableWidgetItem( QString() ) );
733 mTableJoins->setItem( ( insertRow == rowCount ) ? insertRow : insertRow + 1, 1,
new QTableWidgetItem( QString() ) );
736 void QgsSQLComposerDialog::mRemoveJoinButton_clicked()
738 int row = mTableJoins->currentRow();
741 int rowCount = mTableJoins->rowCount();
742 for ( ; row < rowCount - 1; row ++ )
744 mTableJoins->setItem( row, 0, mTableJoins->takeItem( row + 1, 0 ) );
745 mTableJoins->setItem( row, 1, mTableJoins->takeItem( row + 1, 1 ) );
747 mTableJoins->setRowCount( rowCount - 1 );
749 buildSQLFromFields();
752 void QgsSQLComposerDialog::reset()
754 mQueryEdit->setText( mResetSql );
757 void QgsSQLComposerDialog::mTableJoins_itemSelectionChanged()
759 mRemoveJoinButton->setEnabled( mTableJoins->selectedItems().size() == 1 );
766 delete mQueryEdit->lexer()->apis();
767 QsciAPIs *apis =
new QsciAPIs( mQueryEdit->lexer() );
769 const auto constMApiList = mApiList;
770 for (
const QString &str : constMApiList )
776 mQueryEdit->lexer()->setAPIs( apis );
781 mJoinsLabels->setVisible( on );
782 mTableJoins->setVisible( on );
783 mAddJoinButton->setVisible( on );
784 mRemoveJoinButton->setVisible( on );
785 mTablesCombo->setVisible( on );
787 QString mainTypenameFormatted;
788 if ( !mainTypename.isEmpty() )
789 mainTypenameFormatted =
" (" + mainTypename +
")";
790 mQueryEdit->setToolTip( tr(
"This is the SQL query editor. The SQL statement can select data from several tables, \n"
791 "but it must compulsory include the main typename%1 in the selected tables, \n"
792 "and only the geometry column of the main typename can be used as the geometry column of the resulting layer." ).arg( mainTypenameFormatted ) );
795 void QgsSQLComposerDialog::showHelp()
797 QgsHelp::openHelp( QStringLiteral(
"working_with_ogc/ogc_client_support.html#ogc-wfs" ) );