32 mQueryResultsTableView->hide();
33 mQueryResultsTableView->setItemDelegate(
new QgsQueryResultItemDelegate( mQueryResultsTableView ) );
36 connect( mExecuteButton, &QPushButton::pressed,
this, &QgsQueryResultWidget::executeQuery );
37 connect( mClearButton, &QPushButton::pressed,
this, [ = ]
39 mSqlEditor->setText( QString() );
41 connect( mLoadLayerPushButton, &QPushButton::pressed,
this, [ = ]
45 emit createSqlVectorLayer( mConnection->providerKey(), mConnection->uri(), sqlVectorLayerOptions() );
49 connect( mSqlEditor, &QgsCodeEditorSQL::textChanged,
this, &QgsQueryResultWidget::updateButtons );
50 connect( mFilterToolButton, &QToolButton::pressed,
this, [ = ]
56 std::unique_ptr<QgsVectorLayer> vlayer { mConnection->createSqlVectorLayer( sqlVectorLayerOptions() ) };
58 if ( builder.exec() == QDialog::Accepted )
60 mFilterLineEdit->setText( builder.sql() );
65 mMessageBar->pushCritical( tr(
"Error opening filter dialog" ), tr(
"There was an error while preparing SQL filter dialog: %1." ).arg( ex.
what() ) );
72 mSqlErrorText->hide();
74 mLoadAsNewLayerGroupBox->setCollapsed(
true );
82 mPkColumnsCheckBox->setVisible( showPkConfig );
83 mPkColumnsComboBox->setVisible( showPkConfig );
86 mGeometryColumnCheckBox->setVisible( showGeometryColumnConfig );
87 mGeometryColumnComboBox->setVisible( showGeometryColumnConfig );
90 mFilterLabel->setVisible( showFilterConfig );
91 mFilterToolButton->setVisible( showFilterConfig );
92 mFilterLineEdit->setVisible( showFilterConfig );
95 mAvoidSelectingAsFeatureIdCheckBox->setVisible( showDisableSelectAtId );
100 setConnection( connection );
103QgsQueryResultWidget::~QgsQueryResultWidget()
106 cancelRunningQuery();
111 mSqlVectorLayerOptions = options;
112 if ( ! options.
sql.isEmpty() )
114 setQuery( options.
sql );
118 mPkColumnsComboBox->setCheckedItems( {} );
123 mGeometryColumnCheckBox->setChecked( ! options.
geometryColumn.isEmpty() );
124 mGeometryColumnComboBox->clear();
127 mGeometryColumnComboBox->setCurrentText( options.
geometryColumn );
129 mFilterLineEdit->setText( options.
filter );
130 mLayerNameLineEdit->setText( options.
layerName );
133void QgsQueryResultWidget::setWidgetMode( QueryWidgetMode widgetMode )
135 mQueryWidgetMode = widgetMode;
136 switch ( widgetMode )
138 case QueryWidgetMode::SqlQueryMode:
139 mLoadAsNewLayerGroupBox->setTitle( tr(
"Load as New Layer" ) );
140 mLoadLayerPushButton->setText( tr(
"Load Layer" ) );
141 mLoadAsNewLayerGroupBox->setCollapsed(
true );
143 case QueryWidgetMode::QueryLayerUpdateMode:
144 mLoadAsNewLayerGroupBox->setTitle( tr(
"Update Query Layer" ) );
145 mLoadLayerPushButton->setText( tr(
"Update Layer" ) );
146 mLoadAsNewLayerGroupBox->setCollapsed(
false );
151void QgsQueryResultWidget::executeQuery()
153 mQueryResultsTableView->hide();
154 mSqlErrorText->hide();
155 mFirstRowFetched =
false;
157 cancelRunningQuery();
161 const QString sql { mSqlEditor->text( ) };
163 mWasCanceled =
false;
164 mFeedback = std::make_unique<QgsFeedback>();
165 mStopButton->setEnabled(
true );
166 mStatusLabel->show();
167 mStatusLabel->setText( tr(
"Executing query…" ) );
168 mProgressBar->show();
169 mProgressBar->setRange( 0, 0 );
170 mSqlErrorMessage.clear();
172 connect( mStopButton, &QPushButton::pressed, mFeedback.get(), [ = ]
174 mStatusLabel->setText( tr(
"Stopped" ) );
176 mProgressBar->hide();
181 connect( &mQueryResultWatcher, &QFutureWatcher<QgsAbstractDatabaseProviderConnection::QueryResult>::finished,
this, &QgsQueryResultWidget::startFetching, Qt::ConnectionType::UniqueConnection );
187 return mConnection->execSql( sql, mFeedback.get() );
191 mSqlErrorMessage = ex.
what();
195 mQueryResultWatcher.setFuture( future );
199 showError( tr(
"Connection error" ), tr(
"Cannot execute query: connection to the database is not available." ) );
203void QgsQueryResultWidget::updateButtons()
205 mFilterLineEdit->setEnabled( mFirstRowFetched );
206 mFilterToolButton->setEnabled( mFirstRowFetched );
207 mExecuteButton->setEnabled( ! mSqlEditor->text().isEmpty() );
209 mLoadAsNewLayerGroupBox->setEnabled(
210 mSqlErrorMessage.isEmpty() &&
215void QgsQueryResultWidget::updateSqlLayerColumns( )
220 mFilterToolButton->setEnabled(
true );
221 mFilterLineEdit->setEnabled(
true );
222 mPkColumnsComboBox->clear();
223 mGeometryColumnComboBox->clear();
224 const bool hasPkInformation { ! mSqlVectorLayerOptions.primaryKeyColumns.isEmpty() };
225 const bool hasGeomColInformation { ! mSqlVectorLayerOptions.geometryColumn.isEmpty() };
226 static const QStringList geomColCandidates { QStringLiteral(
"geom" ), QStringLiteral(
"geometry" ), QStringLiteral(
"the_geom" ) };
227 const QStringList constCols { mModel->columns() };
228 for (
const QString &
c : constCols )
230 const bool pkCheckedState = hasPkInformation ? mSqlVectorLayerOptions.primaryKeyColumns.contains(
c ) :
c.contains( QStringLiteral(
"id" ), Qt::CaseSensitivity::CaseInsensitive );
232 mPkColumnsComboBox->addItemWithCheckState(
c, pkCheckedState && mPkColumnsComboBox->checkedItems().isEmpty() ? Qt::CheckState::Checked : Qt::CheckState::Unchecked );
233 mGeometryColumnComboBox->addItem(
c );
234 if ( ! hasGeomColInformation && geomColCandidates.contains(
c, Qt::CaseSensitivity::CaseInsensitive ) )
236 mGeometryColumnComboBox->setCurrentText(
c );
239 mPkColumnsCheckBox->setChecked( hasPkInformation );
240 mGeometryColumnCheckBox->setChecked( hasGeomColInformation );
241 if ( hasGeomColInformation )
243 mGeometryColumnComboBox->setCurrentText( mSqlVectorLayerOptions.geometryColumn );
247void QgsQueryResultWidget::cancelRunningQuery()
256 if ( mQueryResultWatcher.isRunning() )
258 mQueryResultWatcher.waitForFinished();
262void QgsQueryResultWidget::cancelApiFetcher()
266 mApiFetcher->stopFetching();
267 mApiFetcherWorkerThread.quit();
268 mApiFetcherWorkerThread.wait();
272void QgsQueryResultWidget::startFetching()
274 if ( ! mWasCanceled )
276 if ( ! mSqlErrorMessage.isEmpty() )
278 showError( tr(
"SQL error" ), mSqlErrorMessage,
true );
284 mStatusLabel->setText( QStringLiteral(
"Query executed successfully (%1 rows, %2 ms)" )
285 .arg( QLocale().toString( mQueryResultWatcher.result().rowCount() ),
286 QLocale().toString( mQueryResultWatcher.result().queryExecutionTime() ) ) );
290 mStatusLabel->setText( QStringLiteral(
"Query executed successfully (%1 s)" ).arg( QLocale().toString( mQueryResultWatcher.result().queryExecutionTime() ) ) );
292 mProgressBar->hide();
293 mModel = std::make_unique<QgsQueryResultModel>( mQueryResultWatcher.result() );
300 connect( mModel.get(), &QgsQueryResultModel::fetchMoreRows,
this, [ = ](
long long maxRows )
302 mFetchedRowsBatchCount = 0;
303 mProgressBar->setRange( 0, maxRows );
304 mProgressBar->show();
307 connect( mModel.get(), &QgsQueryResultModel::rowsInserted,
this, [ = ](
const QModelIndex &,
int first,
int last )
309 if ( ! mFirstRowFetched )
311 emit firstResultBatchFetched();
312 mFirstRowFetched = true;
313 mQueryResultsTableView->show();
315 updateSqlLayerColumns( );
316 mActualRowCount = mModel->queryResult().rowCount();
318 mStatusLabel->setText( tr(
"Fetched rows: %1/%2 %3 %4 ms" )
319 .arg( QLocale().toString( mModel->rowCount( mModel->index( -1, -1 ) ) ),
320 mActualRowCount != -1 ? QLocale().toString( mActualRowCount ) : tr(
"unknown" ),
321 mWasCanceled ? tr(
"(stopped)" ) : QString(),
322 QLocale().toString( mQueryResultWatcher.result().queryExecutionTime() ) ) );
323 mFetchedRowsBatchCount += last - first + 1;
324 mProgressBar->setValue( mFetchedRowsBatchCount );
327 mQueryResultsTableView->setModel( mModel.get() );
328 mQueryResultsTableView->show();
330 connect( mModel.get(), &QgsQueryResultModel::fetchingComplete, mStopButton, [ = ]
332 mProgressBar->hide();
333 mStopButton->setEnabled( false );
339 mStatusLabel->setText( tr(
"SQL command aborted" ) );
340 mProgressBar->hide();
344void QgsQueryResultWidget::showError(
const QString &title,
const QString &message,
bool isSqlError )
346 mStatusLabel->show();
347 mStatusLabel->setText( tr(
"An error occurred while executing the query" ) );
348 mProgressBar->hide();
349 mQueryResultsTableView->hide();
352 mSqlErrorText->show();
353 mSqlErrorText->setText( message );
357 mMessageBar->pushCritical( title, message );
361void QgsQueryResultWidget::tokensReady(
const QStringList &tokens )
363 mSqlEditor->setExtraKeywords( mSqlEditor->extraKeywords() + tokens );
364 mSqlErrorText->setExtraKeywords( mSqlErrorText->extraKeywords() + tokens );
370 mSqlVectorLayerOptions.
sql = mSqlEditor->text();
371 mSqlVectorLayerOptions.filter = mFilterLineEdit->text();
372 mSqlVectorLayerOptions.primaryKeyColumns = mPkColumnsComboBox->checkedItems();
373 mSqlVectorLayerOptions.geometryColumn = mGeometryColumnComboBox->currentText();
374 mSqlVectorLayerOptions.layerName = mLayerNameLineEdit->text();
375 mSqlVectorLayerOptions.disableSelectAtId = mAvoidSelectingAsFeatureIdCheckBox->isChecked();
378 if ( ! mPkColumnsCheckBox->isChecked() )
382 if ( ! mGeometryColumnCheckBox->isChecked() )
391 mConnection.reset( connection );
399 const QMultiMap<Qgis::SqlKeywordCategory, QStringList> keywordsDict { connection->
sqlDictionary() };
400 QStringList keywords;
401 for (
auto it = keywordsDict.constBegin(); it != keywordsDict.constEnd(); it++ )
403 keywords.append( it.value() );
407 mSqlEditor->setExtraKeywords( keywords );
408 mSqlErrorText->setExtraKeywords( keywords );
411 mApiFetcher = std::make_unique<QgsConnectionsApiFetcher>( connection );
412 mApiFetcher->moveToThread( &mApiFetcherWorkerThread );
413 connect( &mApiFetcherWorkerThread, &QThread::started, mApiFetcher.get(), &QgsConnectionsApiFetcher::fetchTokens );
414 connect( mApiFetcher.get(), &QgsConnectionsApiFetcher::tokensReady,
this, &QgsQueryResultWidget::tokensReady );
415 connect( mApiFetcher.get(), &QgsConnectionsApiFetcher::fetchingFinished, &mApiFetcherWorkerThread, [ = ]
417 mApiFetcherWorkerThread.quit();
418 mApiFetcherWorkerThread.wait();
420 mApiFetcherWorkerThread.start();
427void QgsQueryResultWidget::setQuery(
const QString &sql )
429 mSqlEditor->setText( sql );
432void QgsQueryResultWidget::notify(
const QString &title,
const QString &text,
Qgis::MessageLevel level )
434 mMessageBar->pushMessage( title, text, level );
440void QgsConnectionsApiFetcher::fetchTokens()
442 if ( ! mStopFetching && mConnection )
449 schemas = mConnection->schemas();
450 emit tokensReady( schemas );
459 schemas.push_back( QString() );
462 for (
const auto &schema : std::as_const( schemas ) )
470 QStringList tableNames;
473 const QList<QgsAbstractDatabaseProviderConnection::TableProperty> tables = mConnection->tables( schema );
476 if ( mStopFetching ) {
return; }
477 tableNames.push_back( table.tableName() );
479 emit tokensReady( tableNames );
487 for (
const auto &table : std::as_const( tableNames ) )
495 QStringList fieldNames;
498 const QgsFields fields( mConnection->fields( schema, table ) );
499 if ( mStopFetching ) {
return; }
500 for (
const auto &
field : std::as_const( fields ) )
503 if ( mStopFetching ) {
return; }
505 emit tokensReady( fieldNames );
514 emit fetchingFinished();
517void QgsConnectionsApiFetcher::stopFetching()
523QgsQueryResultItemDelegate::QgsQueryResultItemDelegate( QObject *parent )
524 : QStyledItemDelegate( parent )
528QString QgsQueryResultItemDelegate::displayText(
const QVariant &value,
const QLocale &locale )
const
531 QString result { QgsExpressionUtils::toLocalizedString( value ) };
533 if ( result.length() > 255 )
535 result.truncate( 255 );
536 result.append( QStringLiteral(
"…" ) );
MessageLevel
Level for messages This will be used both for message log and message bar in application.
@ Warning
Warning message.
@ UnstableFeatureIds
SQL layer definition supports disabling select at id.
@ SubsetStringFilter
SQL layer definition supports subset string filter.
@ PrimaryKeys
SQL layer definition supports primary keys.
@ GeometryColumn
SQL layer definition supports geometry column.
The QgsAbstractDatabaseProviderConnection class provides common functionality for DB based connection...
@ SqlLayers
Can create vector layers from SQL SELECT queries.
@ Schemas
Can list schemas (if not set, the connection does not support schemas)
virtual Qgis::SqlLayerDefinitionCapabilities sqlLayerDefinitionCapabilities()
Returns SQL layer definition capabilities (Filters, GeometryColumn, PrimaryKeys).
virtual QMultiMap< Qgis::SqlKeywordCategory, QStringList > sqlDictionary()
Returns a dictionary of SQL keywords supported by the provider.
void collapsedStateChanged(bool collapsed)
Signal emitted when groupbox collapsed/expanded state is changed, and when first shown.
void canceled()
Internal routines can connect to this signal if they use event loop.
Container of fields for a vector layer.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Custom exception class for provider connection related exceptions.
Query Builder for layers.
@ UnknownCount
Provider returned an unknown feature count.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
The QueryResult class represents the result of a query executed by execSql()
The SqlVectorLayerOptions stores all information required to create a SQL (query) layer.
QString sql
The SQL expression that defines the SQL (query) layer.
QStringList primaryKeyColumns
List of primary key column names.
QString filter
Additional subset string (provider-side filter), not all data providers support this feature: check s...
QString layerName
Optional name for the new layer.
bool disableSelectAtId
If SelectAtId is disabled (default is false), not all data providers support this feature: check supp...
QString geometryColumn
Name of the geometry column.
The TableProperty class represents a database table or view.