1 /***************************************************************************
2 qgshanasourceselect.cpp
3 --------------------------------------
4 Date : 31-05-2019
5 Copyright : (C) SAP SE
6 Author : Maxim Rylov
7 ***************************************************************************/
8
9 /***************************************************************************
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 ***************************************************************************/
17 #include "qgsapplication.h"
18 #include "qgsdatasourceuri.h"
19 #include "qgsgui.h"
20 #include "qgshanasourceselect.h"
21 #include "qgshanaconnection.h"
22 #include "qgshananewconnection.h"
23 #include "qgshanaprovider.h"
24 #include "qgshanatablemodel.h"
25 #include "qgshanautils.h"
26 #include "qgslogger.h"
27 #include "qgsmanageconnectionsdialog.h"
28 #include "qgsproxyprogresstask.h"
29 #include "qgsquerybuilder.h"
30 #include "qgsvectorlayer.h"
31 #include "qgssettings.h"
32
33 #include <QComboBox>
34 #include <QFileDialog>
35 #include <QHeaderView>
36 #include <QInputDialog>
37 #include <QMessageBox>
38 #include <QStringList>
39 #include <QStyledItemDelegate>
40
41 //! Used to create an editor for when the user tries to change the contents of a cell
createEditor(QWidget * parent,const QStyleOptionViewItem & option,const QModelIndex & index) const42 QWidget *QgsHanaSourceSelectDelegate::createEditor(
43 QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
44 {
45 Q_UNUSED( option );
46
47 const QString tableName = index.sibling( index.row(), QgsHanaTableModel::DbtmTable ).data( Qt::DisplayRole ).toString();
48 if ( tableName.isEmpty() )
49 return nullptr;
50
51 if ( index.column() == QgsHanaTableModel::DbtmSql )
52 {
53 return new QLineEdit( parent );
54 }
55
56 if ( index.column() == QgsHanaTableModel::DbtmGeomType && index.data( Qt::UserRole + 1 ).toBool() )
57 {
58 QComboBox *cb = new QComboBox( parent );
59 for ( const QgsWkbTypes::Type type :
60 QList<QgsWkbTypes::Type>()
61 << QgsWkbTypes::Point
62 << QgsWkbTypes::LineString
63 << QgsWkbTypes::Polygon
64 << QgsWkbTypes::MultiPoint
65 << QgsWkbTypes::MultiLineString
66 << QgsWkbTypes::MultiPolygon
67 << QgsWkbTypes::CircularString
68 << QgsWkbTypes::GeometryCollection
69 << QgsWkbTypes::NoGeometry )
70 {
71 cb->addItem( QgsHanaTableModel::iconForWkbType( type ), QgsWkbTypes::displayString( type ), type );
72 }
73 return cb;
74 }
75
76 if ( index.column() == QgsHanaTableModel::DbtmPkCol )
77 {
78 const QStringList values = index.data( Qt::UserRole + 1 ).toStringList();
79
80 if ( !values.isEmpty() )
81 {
82 QComboBox *cb = new QComboBox( parent );
83 cb->setItemDelegate( new QStyledItemDelegate( parent ) );
84
85 QStandardItemModel *model = new QStandardItemModel( values.size(), 1, cb );
86
87 int row = 0;
88 for ( const QString &value : values )
89 {
90 QStandardItem *item = new QStandardItem( value );
91 item->setFlags( Qt::ItemIsUserCheckable | Qt::ItemIsEnabled );
92 item->setCheckable( true );
93 item->setData( Qt::Unchecked, Qt::CheckStateRole );
94 model->setItem( row++, 0, item );
95 }
96
97 cb->setModel( model );
98
99 return cb;
100 }
101 }
102
103 if ( index.column() == QgsHanaTableModel::DbtmSrid )
104 {
105 QLineEdit *le = new QLineEdit( parent );
106 le->setValidator( new QIntValidator( -1, 999999, parent ) );
107 return le;
108 }
109
110 return nullptr;
111 }
112
setModelData(QWidget * editor,QAbstractItemModel * model,const QModelIndex & index) const113 void QgsHanaSourceSelectDelegate::setModelData(
114 QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
115 {
116 QComboBox *cb = qobject_cast<QComboBox *>( editor );
117 if ( cb )
118 {
119 if ( index.column() == QgsHanaTableModel::DbtmGeomType )
120 {
121 const QgsWkbTypes::Type type = static_cast<QgsWkbTypes::Type>( cb->currentData().toInt() );
122
123 model->setData( index, QgsHanaTableModel::iconForWkbType( type ), Qt::DecorationRole );
124 model->setData( index, type != QgsWkbTypes::Unknown ? QgsWkbTypes::displayString( type ) : tr( "Select…" ) );
125 model->setData( index, type, Qt::UserRole + 2 );
126 }
127 else if ( index.column() == QgsHanaTableModel::DbtmPkCol )
128 {
129 QStandardItemModel *cbm = qobject_cast<QStandardItemModel *>( cb->model() );
130 QStringList cols;
131 for ( int idx = 0; idx < cbm->rowCount(); idx++ )
132 {
133 QStandardItem *item = cbm->item( idx, 0 );
134 if ( item->data( Qt::CheckStateRole ) == Qt::Checked )
135 cols << item->text();
136 }
137
138 model->setData( index, cols.isEmpty() ? tr( "Select…" ) : cols.join( QLatin1String( ", " ) ) );
139 model->setData( index, cols, Qt::UserRole + 2 );
140 }
141 }
142
143 QLineEdit *le = qobject_cast<QLineEdit *>( editor );
144 if ( le )
145 {
146 QString value( le->text() );
147
148 if ( index.column() == QgsHanaTableModel::DbtmSrid && value.isEmpty() )
149 value = tr( "Enter…" );
150
151 model->setData( index, value );
152 }
153 }
154
setEditorData(QWidget * editor,const QModelIndex & index) const155 void QgsHanaSourceSelectDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
156 {
157 QString value( index.data( Qt::DisplayRole ).toString() );
158
159 QComboBox *cb = qobject_cast<QComboBox *>( editor );
160 if ( cb )
161 {
162 if ( index.column() == QgsHanaTableModel::DbtmGeomType )
163 cb->setCurrentIndex( cb->findData( index.data( Qt::UserRole + 2 ).toInt() ) );
164
165 if ( index.column() == QgsHanaTableModel::DbtmPkCol &&
166 !index.data( Qt::UserRole + 2 ).toStringList().isEmpty() )
167 {
168 const QStringList columns = index.data( Qt::UserRole + 2 ).toStringList();
169 for ( const QString &colName : columns )
170 {
171 QStandardItemModel *cbm = qobject_cast<QStandardItemModel *>( cb->model() );
172 for ( int idx = 0; idx < cbm->rowCount(); ++idx )
173 {
174 QStandardItem *item = cbm->item( idx, 0 );
175 if ( item->text() != colName )
176 continue;
177
178 item->setData( Qt::Checked, Qt::CheckStateRole );
179 break;
180 }
181 }
182 }
183 }
184
185 QLineEdit *le = qobject_cast<QLineEdit *>( editor );
186 if ( le )
187 {
188 bool ok;
189 ( void )value.toInt( &ok );
190 if ( index.column() == QgsHanaTableModel::DbtmSrid && !ok )
191 value.clear();
192
193 le->setText( value );
194 }
195 }
196
QgsHanaSourceSelect(QWidget * parent,Qt::WindowFlags fl,QgsProviderRegistry::WidgetMode theWidgetMode)197 QgsHanaSourceSelect::QgsHanaSourceSelect(
198 QWidget *parent,
199 Qt::WindowFlags fl,
200 QgsProviderRegistry::WidgetMode theWidgetMode )
201 : QgsAbstractDataSourceWidget( parent, fl, theWidgetMode )
202 {
203 setupUi( this );
204 QgsGui::instance()->enableAutoGeometryRestore( this );
205
206 connect( btnConnect, &QPushButton::clicked, this, &QgsHanaSourceSelect::btnConnect_clicked );
207 connect( cbxAllowGeometrylessTables, &QCheckBox::stateChanged, this, &QgsHanaSourceSelect::cbxAllowGeometrylessTables_stateChanged );
208 connect( btnNew, &QPushButton::clicked, this, &QgsHanaSourceSelect::btnNew_clicked );
209 connect( btnEdit, &QPushButton::clicked, this, &QgsHanaSourceSelect::btnEdit_clicked );
210 connect( btnDelete, &QPushButton::clicked, this, &QgsHanaSourceSelect::btnDelete_clicked );
211 connect( btnSave, &QPushButton::clicked, this, &QgsHanaSourceSelect::btnSave_clicked );
212 connect( btnLoad, &QPushButton::clicked, this, &QgsHanaSourceSelect::btnLoad_clicked );
213 connect( mSearchGroupBox, &QGroupBox::toggled, this, &QgsHanaSourceSelect::mSearchGroupBox_toggled );
214 connect( mSearchTableEdit, &QLineEdit::textChanged, this, &QgsHanaSourceSelect::mSearchTableEdit_textChanged );
215 connect( mSearchColumnComboBox, &QComboBox::currentTextChanged, this, &QgsHanaSourceSelect::mSearchColumnComboBox_currentTextChanged );
216 connect( mSearchModeComboBox, &QComboBox::currentTextChanged, this, &QgsHanaSourceSelect::mSearchModeComboBox_currentTextChanged );
217 connect( cmbConnections, static_cast<void ( QComboBox::* )( int )>( &QComboBox::activated ),
218 this, &QgsHanaSourceSelect::cmbConnections_activated );
219 connect( mTablesTreeView, &QTreeView::clicked, this, &QgsHanaSourceSelect::mTablesTreeView_clicked );
220 connect( mTablesTreeView, &QTreeView::doubleClicked, this, &QgsHanaSourceSelect::mTablesTreeView_doubleClicked );
221 setupButtons( buttonBox );
222 connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsHanaSourceSelect::showHelp );
223
224 if ( widgetMode() != QgsProviderRegistry::WidgetMode::None )
225 mHoldDialogOpen->hide();
226 else
227 setWindowTitle( tr( "Add SAP HANA Table(s)" ) );
228
229 mBuildQueryButton = new QPushButton( tr( "&Set Filter" ) );
230 mBuildQueryButton->setToolTip( tr( "Set Filter" ) );
231 mBuildQueryButton->setDisabled( true );
232
233 if ( widgetMode() != QgsProviderRegistry::WidgetMode::Manager )
234 {
235 buttonBox->addButton( mBuildQueryButton, QDialogButtonBox::ActionRole );
236 connect( mBuildQueryButton, &QAbstractButton::clicked, this, &QgsHanaSourceSelect::buildQuery );
237 }
238
239 populateConnectionList();
240
241 mSearchModeComboBox->addItem( tr( "Wildcard" ) );
242 mSearchModeComboBox->addItem( tr( "RegExp" ) );
243
244 mSearchColumnComboBox->addItem( tr( "All" ) );
245 mSearchColumnComboBox->addItem( tr( "Schema" ) );
246 mSearchColumnComboBox->addItem( tr( "Table" ) );
247 mSearchColumnComboBox->addItem( tr( "Comment" ) );
248 mSearchColumnComboBox->addItem( tr( "Geometry column" ) );
249 mSearchColumnComboBox->addItem( tr( "Type" ) );
250 mSearchColumnComboBox->addItem( tr( "Feature id" ) );
251 mSearchColumnComboBox->addItem( tr( "SRID" ) );
252 mSearchColumnComboBox->addItem( tr( "Sql" ) );
253
254 mProxyModel.setParent( this );
255 mProxyModel.setFilterKeyColumn( -1 );
256 mProxyModel.setFilterCaseSensitivity( Qt::CaseInsensitive );
257 mProxyModel.setSourceModel( &mTableModel );
258
259 mTablesTreeView->setModel( &mProxyModel );
260 mTablesTreeView->setSortingEnabled( true );
261 mTablesTreeView->setEditTriggers( QAbstractItemView::CurrentChanged );
262 mTablesTreeView->setItemDelegate( new QgsHanaSourceSelectDelegate( this ) );
263
264 connect( mTablesTreeView->selectionModel(), &QItemSelectionModel::selectionChanged,
265 this, &QgsHanaSourceSelect::treeWidgetSelectionChanged );
266
267 const QgsSettings settings;
268 mTablesTreeView->setSelectionMode( settings.value( QStringLiteral( "qgis/addHanaDC" ), false ).toBool() ?
269 QAbstractItemView::ExtendedSelection : QAbstractItemView::MultiSelection );
270
271 //for Qt < 4.3.2, passing -1 to include all model columns
272 //in search does not seem to work
273 mSearchColumnComboBox->setCurrentIndex( 2 );
274
275 restoreGeometry( settings.value( QStringLiteral( "Windows/HanaSourceSelect/geometry" ) ).toByteArray() );
276 mHoldDialogOpen->setChecked( settings.value( QStringLiteral( "Windows/HanaSourceSelect/HoldDialogOpen" ), false ).toBool() );
277
278 for ( int i = 0; i < mTableModel.columnCount(); i++ )
279 {
280 mTablesTreeView->setColumnWidth( i, settings.value( QStringLiteral( "Windows/HanaSourceSelect/columnWidths/%1" )
281 .arg( i ), mTablesTreeView->columnWidth( i ) ).toInt() );
282 }
283
284 //hide the search options by default
285 //they will be shown when the user ticks
286 //the search options group box
287 mSearchLabel->setVisible( false );
288 mSearchColumnComboBox->setVisible( false );
289 mSearchColumnsLabel->setVisible( false );
290 mSearchModeComboBox->setVisible( false );
291 mSearchModeLabel->setVisible( false );
292 mSearchTableEdit->setVisible( false );
293
294 cbxAllowGeometrylessTables->setDisabled( true );
295 }
296
297 //! Autoconnected SLOTS *
298 // Slot for adding a new connection
btnNew_clicked()299 void QgsHanaSourceSelect::btnNew_clicked()
300 {
301 QgsHanaNewConnection nc( this );
302 if ( nc.exec() )
303 {
304 populateConnectionList();
305 emit connectionsChanged();
306 }
307 }
308 // Slot for deleting an existing connection
btnDelete_clicked()309 void QgsHanaSourceSelect::btnDelete_clicked()
310 {
311 const QString msg = tr( "Are you sure you want to remove the %1 connection and all associated settings?" )
312 .arg( cmbConnections->currentText() );
313 if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Confirm Delete" ), msg, QMessageBox::Yes | QMessageBox::No ) )
314 return;
315
316 QgsHanaSourceSelect::deleteConnection( cmbConnections->currentText() );
317
318 populateConnectionList();
319 emit connectionsChanged();
320 }
321
deleteConnection(const QString & name)322 void QgsHanaSourceSelect::deleteConnection( const QString &name )
323 {
324 QgsHanaSettings::removeConnection( name );
325 }
326
btnSave_clicked()327 void QgsHanaSourceSelect::btnSave_clicked()
328 {
329 QgsManageConnectionsDialog dlg( this, QgsManageConnectionsDialog::Export, QgsManageConnectionsDialog::HANA );
330 dlg.exec();
331 }
332
btnLoad_clicked()333 void QgsHanaSourceSelect::btnLoad_clicked()
334 {
335 const QString fileName = QFileDialog::getOpenFileName( this, tr( "Load Connections" ),
336 QDir::homePath(), tr( "XML files (*.xml *XML)" ) );
337 if ( fileName.isEmpty() )
338 {
339 return;
340 }
341
342 QgsManageConnectionsDialog dlg( this, QgsManageConnectionsDialog::Import,
343 QgsManageConnectionsDialog::HANA, fileName );
344 dlg.exec();
345 populateConnectionList();
346 }
347
348 // Slot for editing a connection
btnEdit_clicked()349 void QgsHanaSourceSelect::btnEdit_clicked()
350 {
351 QgsHanaNewConnection nc( this, cmbConnections->currentText() );
352 if ( nc.exec() )
353 {
354 populateConnectionList();
355 emit connectionsChanged();
356 }
357 }
358
359 //! End Autoconnected SLOTS *
360
361 // Remember which database is selected
cmbConnections_activated(int)362 void QgsHanaSourceSelect::cmbConnections_activated( int )
363 {
364 // Remember which database was selected.
365 QgsHanaSettings::setSelectedConnection( cmbConnections->currentText() );
366
367 cbxAllowGeometrylessTables->blockSignals( true );
368 QgsHanaSettings settings( cmbConnections->currentText() );
369 settings.load();
370 cbxAllowGeometrylessTables->setChecked( settings.allowGeometrylessTables() );
371 cbxAllowGeometrylessTables->blockSignals( false );
372 }
373
cbxAllowGeometrylessTables_stateChanged(int)374 void QgsHanaSourceSelect::cbxAllowGeometrylessTables_stateChanged( int )
375 {
376 btnConnect_clicked();
377 }
378
buildQuery()379 void QgsHanaSourceSelect::buildQuery()
380 {
381 setSql( mTablesTreeView->currentIndex() );
382 }
383
mTablesTreeView_clicked(const QModelIndex & index)384 void QgsHanaSourceSelect::mTablesTreeView_clicked( const QModelIndex &index )
385 {
386 mBuildQueryButton->setEnabled( index.parent().isValid() );
387 }
388
mTablesTreeView_doubleClicked(const QModelIndex & index)389 void QgsHanaSourceSelect::mTablesTreeView_doubleClicked( const QModelIndex &index )
390 {
391 const QgsSettings settings;
392 if ( settings.value( QStringLiteral( "qgis/addHANADC" ), false ).toBool() )
393 addButtonClicked();
394 else
395 setSql( index );
396 }
397
mSearchGroupBox_toggled(bool checked)398 void QgsHanaSourceSelect::mSearchGroupBox_toggled( bool checked )
399 {
400 if ( mSearchTableEdit->text().isEmpty() )
401 return;
402
403 mSearchTableEdit_textChanged( checked ? mSearchTableEdit->text() : QString( ) );
404 }
405
mSearchTableEdit_textChanged(const QString & text)406 void QgsHanaSourceSelect::mSearchTableEdit_textChanged( const QString &text )
407 {
408 if ( mSearchModeComboBox->currentText() == tr( "Wildcard" ) )
409 mProxyModel._setFilterWildcard( text );
410 else if ( mSearchModeComboBox->currentText() == tr( "RegExp" ) )
411 mProxyModel._setFilterRegExp( text );
412 }
413
mSearchColumnComboBox_currentTextChanged(const QString & text)414 void QgsHanaSourceSelect::mSearchColumnComboBox_currentTextChanged( const QString &text )
415 {
416 if ( text == tr( "All" ) )
417 {
418 mProxyModel.setFilterKeyColumn( -1 );
419 }
420 else if ( text == tr( "Schema" ) )
421 {
422 mProxyModel.setFilterKeyColumn( QgsHanaTableModel::DbtmSchema );
423 }
424 else if ( text == tr( "Table" ) )
425 {
426 mProxyModel.setFilterKeyColumn( QgsHanaTableModel::DbtmTable );
427 }
428 else if ( text == tr( "Comment" ) )
429 {
430 mProxyModel.setFilterKeyColumn( QgsHanaTableModel::DbtmComment );
431 }
432 else if ( text == tr( "Geometry column" ) )
433 {
434 mProxyModel.setFilterKeyColumn( QgsHanaTableModel::DbtmGeomCol );
435 }
436 else if ( text == tr( "Type" ) )
437 {
438 mProxyModel.setFilterKeyColumn( QgsHanaTableModel::DbtmGeomType );
439 }
440 else if ( text == tr( "Feature id" ) )
441 {
442 mProxyModel.setFilterKeyColumn( QgsHanaTableModel::DbtmPkCol );
443 }
444 else if ( text == tr( "SRID" ) )
445 {
446 mProxyModel.setFilterKeyColumn( QgsHanaTableModel::DbtmSrid );
447 }
448 else if ( text == tr( "Sql" ) )
449 {
450 mProxyModel.setFilterKeyColumn( QgsHanaTableModel::DbtmSql );
451 }
452 }
453
mSearchModeComboBox_currentTextChanged(const QString & text)454 void QgsHanaSourceSelect::mSearchModeComboBox_currentTextChanged( const QString &text )
455 {
456 Q_UNUSED( text );
457 mSearchTableEdit_textChanged( mSearchTableEdit->text() );
458 }
459
setLayerType(const QgsHanaLayerProperty & layerProperty)460 void QgsHanaSourceSelect::setLayerType( const QgsHanaLayerProperty &layerProperty )
461 {
462 mTableModel.addTableEntry( mConnectionName, layerProperty );
463 }
464
~QgsHanaSourceSelect()465 QgsHanaSourceSelect::~QgsHanaSourceSelect()
466 {
467 if ( mColumnTypeThread )
468 {
469 mColumnTypeThread->requestInterruption();
470 mColumnTypeThread->wait();
471 finishList();
472 }
473
474 QgsSettings settings;
475 settings.setValue( QStringLiteral( "Windows/HanaSourceSelect/geometry" ), saveGeometry() );
476 settings.setValue( QStringLiteral( "Windows/HanaSourceSelect/HoldDialogOpen" ), mHoldDialogOpen->isChecked() );
477
478 for ( int i = 0; i < mTableModel.columnCount(); i++ )
479 {
480 settings.setValue( QStringLiteral( "Windows/HanaSourceSelect/columnWidths/%1" )
481 .arg( i ), mTablesTreeView->columnWidth( i ) );
482 }
483 }
484
populateConnectionList()485 void QgsHanaSourceSelect::populateConnectionList()
486 {
487 cmbConnections->blockSignals( true );
488 cmbConnections->clear();
489 cmbConnections->addItems( QgsHanaSettings::getConnectionNames() );
490 cmbConnections->blockSignals( false );
491
492 setConnectionListPosition();
493
494 btnEdit->setDisabled( cmbConnections->count() == 0 );
495 btnDelete->setDisabled( cmbConnections->count() == 0 );
496 btnConnect->setDisabled( cmbConnections->count() == 0 );
497 cmbConnections->setDisabled( cmbConnections->count() == 0 );
498 }
499
selectedTables()500 QStringList QgsHanaSourceSelect::selectedTables()
501 {
502 return mSelectedTables;
503 }
504
connectionInfo()505 QString QgsHanaSourceSelect::connectionInfo()
506 {
507 return mConnectionInfo;
508 }
509
510 // Slot for performing action when the Add button is clicked
addButtonClicked()511 void QgsHanaSourceSelect::addButtonClicked()
512 {
513 mSelectedTables.clear();
514
515 const QModelIndexList indexes = mTablesTreeView->selectionModel()->selection().indexes();
516 for ( const QModelIndex &idx : indexes )
517 {
518 if ( idx.column() != QgsHanaTableModel::DbtmTable )
519 continue;
520
521 const QString uri = mTableModel.layerURI( mProxyModel.mapToSource( idx ), mConnectionName, mConnectionInfo );
522 if ( uri.isNull() )
523 continue;
524
525 mSelectedTables << uri;
526 }
527
528 if ( mSelectedTables.empty() )
529 {
530 QMessageBox::information( this, tr( "Select Table" ), tr( "You must select a table in order to add a layer." ) );
531 }
532 else
533 {
534 emit addDatabaseLayers( mSelectedTables, QStringLiteral( "hana" ) );
535 if ( !mHoldDialogOpen->isChecked() && widgetMode() == QgsProviderRegistry::WidgetMode::None )
536 accept();
537 }
538 }
539
btnConnect_clicked()540 void QgsHanaSourceSelect::btnConnect_clicked()
541 {
542 cbxAllowGeometrylessTables->setEnabled( true );
543
544 if ( mColumnTypeThread )
545 {
546 mColumnTypeThread->requestInterruption();
547 mColumnTypeThread->wait();
548 return;
549 }
550
551 const QString connName = cmbConnections->currentText();
552
553 const QModelIndex rootItemIndex = mTableModel.indexFromItem( mTableModel.invisibleRootItem() );
554 mTableModel.removeRows( 0, mTableModel.rowCount( rootItemIndex ), rootItemIndex );
555
556 QgsHanaSettings settings( connName, true );
557 settings.setAllowGeometrylessTables( cbxAllowGeometrylessTables->isChecked() );
558
559 const QgsDataSourceUri uri = settings.toDataSourceUri();
560 bool canceled = false;
561
562 const std::unique_ptr<QgsHanaConnection> conn( QgsHanaConnection::createConnection( uri, &canceled, nullptr ) );
563 if ( !conn )
564 {
565 if ( !canceled )
566 QMessageBox::warning( this, tr( "SAP HANA" ), tr( "Unable to connect to a database" ) );
567 return;
568 }
569
570 mConnectionName = connName;
571 mConnectionInfo = QgsHanaUtils::connectionInfo( uri );
572
573 QApplication::setOverrideCursor( Qt::BusyCursor );
574
575 mColumnTypeThread = std::make_unique<QgsHanaColumnTypeThread>( mConnectionName, uri, settings.allowGeometrylessTables(), settings.userTablesOnly() );
576 mColumnTypeTask = std::make_unique<QgsProxyProgressTask>( tr( "Scanning tables for %1" ).arg( mConnectionName ) );
577 QgsApplication::taskManager()->addTask( mColumnTypeTask.get() );
578
579 connect( mColumnTypeThread.get(), &QgsHanaColumnTypeThread::setLayerType,
580 this, &QgsHanaSourceSelect::setLayerType );
581 connect( mColumnTypeThread.get(), &QThread::finished,
582 this, &QgsHanaSourceSelect::columnThreadFinished );
583 connect( mColumnTypeThread.get(), &QgsHanaColumnTypeThread::progress,
584 mColumnTypeTask.get(), [&]( int i, int n )
585 {
586 mColumnTypeTask->setProxyProgress( 100.0 * static_cast<double>( i ) / n );
587 } );
588 connect( mColumnTypeThread.get(), &QgsHanaColumnTypeThread::progressMessage,
589 this, &QgsHanaSourceSelect::progressMessage );
590
591 btnConnect->setText( tr( "Stop" ) );
592 mColumnTypeThread->start();
593 }
594
finishList()595 void QgsHanaSourceSelect::finishList()
596 {
597 QApplication::restoreOverrideCursor();
598
599 mTablesTreeView->sortByColumn( QgsHanaTableModel::DbtmTable, Qt::AscendingOrder );
600 mTablesTreeView->sortByColumn( QgsHanaTableModel::DbtmSchema, Qt::AscendingOrder );
601 }
602
columnThreadFinished()603 void QgsHanaSourceSelect::columnThreadFinished()
604 {
605 const QString errorMsg = mColumnTypeThread->errorMessage();
606 mColumnTypeThread.reset( nullptr );
607 QgsProxyProgressTask *task = mColumnTypeTask.release();
608 task->finalize( errorMsg.isEmpty() );
609 if ( !errorMsg.isEmpty() )
610 pushMessage( tr( "Failed to retrieve tables for %1" ).arg( mConnectionName ), errorMsg, Qgis::MessageLevel::Warning );
611
612 btnConnect->setText( tr( "Connect" ) );
613
614 finishList();
615 }
616
refresh()617 void QgsHanaSourceSelect::refresh()
618 {
619 populateConnectionList();
620 }
621
setSql(const QModelIndex & index)622 void QgsHanaSourceSelect::setSql( const QModelIndex &index )
623 {
624 if ( !index.parent().isValid() )
625 {
626 QgsDebugMsg( "schema item found" );
627 return;
628 }
629
630 const QModelIndex idx = mProxyModel.mapToSource( index );
631 const QString uri = mTableModel.layerURI( idx, mConnectionName, mConnectionInfo );
632 if ( uri.isNull() )
633 {
634 QgsDebugMsg( "no uri" );
635 return;
636 }
637
638 const QString tableName = mTableModel.itemFromIndex( idx.sibling( idx.row(), QgsHanaTableModel::DbtmTable ) )->text();
639
640 QgsVectorLayer vlayer( uri, tableName, QStringLiteral( "hana" ) );
641 if ( !vlayer.isValid() )
642 return;
643
644 QgsQueryBuilder gb( &vlayer, this );
645 if ( gb.exec() )
646 mTableModel.setSql( mProxyModel.mapToSource( index ), gb.sql() );
647 }
648
fullDescription(const QString & schema,const QString & table,const QString & column,const QString & type)649 QString QgsHanaSourceSelect::fullDescription(
650 const QString &schema, const QString &table, const QString &column, const QString &type )
651 {
652 QString desc;
653 if ( !schema.isEmpty() )
654 desc = schema + '.';
655 desc += table + " (" + column + ") " + type;
656 return desc;
657 }
658
setConnectionListPosition()659 void QgsHanaSourceSelect::setConnectionListPosition()
660 {
661 const QString selectedConnName = QgsHanaSettings::getSelectedConnection();
662 cmbConnections->setCurrentIndex( cmbConnections->findText( selectedConnName ) );
663 if ( cmbConnections->currentIndex() < 0 )
664 cmbConnections->setCurrentIndex( selectedConnName.isNull() ? 0 : cmbConnections->count() - 1 );
665 }
666
setSearchExpression(const QString & regexp)667 void QgsHanaSourceSelect::setSearchExpression( const QString ®exp )
668 {
669 Q_UNUSED( regexp )
670 }
671
treeWidgetSelectionChanged(const QItemSelection & selected,const QItemSelection & deselected)672 void QgsHanaSourceSelect::treeWidgetSelectionChanged(
673 const QItemSelection &selected, const QItemSelection &deselected )
674 {
675 Q_UNUSED( deselected )
676 emit enableButtons( !selected.isEmpty() );
677 }
678
showHelp()679 void QgsHanaSourceSelect::showHelp()
680 {
681 QgsHelp::openHelp( QStringLiteral( "managing_data_source/opening_data.html#loading-a-database-layer" ) );
682 }
683