1 /***************************************************************************
2                          qgsmssqldataitems.cpp  -  description
3                          -------------------
4     begin                : 2011-10-08
5     copyright            : (C) 2011 by Tamas Szekeres
6     email                : szekerest at gmail.com
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 
18 #include "qgsmssqldataitems.h"
19 #include "qgsmssqlconnection.h"
20 #include "qgsmssqldatabase.h"
21 
22 #include "qgsmssqlgeomcolumntypethread.h"
23 #include "qgslogger.h"
24 #include "qgsmimedatautils.h"
25 #include "qgsvectorlayer.h"
26 #include "qgsvectorlayerexporter.h"
27 #include "qgsdatasourceuri.h"
28 #include "qgsmssqlprovider.h"
29 #include "qgssettings.h"
30 #include "qgsmessageoutput.h"
31 #include "qgsmssqlconnection.h"
32 #include "qgsapplication.h"
33 #include "qgsproject.h"
34 #include "qgsfieldsitem.h"
35 
36 #ifdef HAVE_GUI
37 #include "qgsmssqlsourceselect.h"
38 #endif
39 
40 #include <QMessageBox>
41 #include <QSqlDatabase>
42 #include <QSqlError>
43 
44 // ---------------------------------------------------------------------------
QgsMssqlConnectionItem(QgsDataItem * parent,const QString & name,const QString & path)45 QgsMssqlConnectionItem::QgsMssqlConnectionItem( QgsDataItem *parent, const QString &name, const QString &path )
46   : QgsDataCollectionItem( parent, name, path, QStringLiteral( "MSSQL" ) )
47   , mUseGeometryColumns( false )
48   , mUseEstimatedMetadata( false )
49   , mAllowGeometrylessTables( true )
50 {
51   mCapabilities |= Qgis::BrowserItemCapability::Fast | Qgis::BrowserItemCapability::Collapse;
52   mIconName = QStringLiteral( "mIconConnect.svg" );
53 }
54 
~QgsMssqlConnectionItem()55 QgsMssqlConnectionItem::~QgsMssqlConnectionItem()
56 {
57   stop();
58 }
59 
readConnectionSettings()60 void QgsMssqlConnectionItem::readConnectionSettings()
61 {
62   QgsSettings settings;
63   QString key = "/MSSQL/connections/" + mName;
64   mService = settings.value( key + "/service" ).toString();
65   mHost = settings.value( key + "/host" ).toString();
66   mDatabase = settings.value( key + "/database" ).toString();
67   if ( settings.value( key + "/saveUsername" ).toString() == QLatin1String( "true" ) )
68   {
69     mUsername = settings.value( key + "/username" ).toString();
70   }
71 
72   if ( settings.value( key + "/savePassword" ).toString() == QLatin1String( "true" ) )
73   {
74     mPassword = settings.value( key + "/password" ).toString();
75   }
76 
77   mSchemaSettings.clear();
78   mSchemasFilteringEnabled = settings.value( key + "/schemasFiltering" ).toBool();
79 
80   if ( mSchemasFilteringEnabled )
81   {
82     QVariant schemasSettingsVariant = settings.value( key + "/excludedSchemas" );
83     if ( schemasSettingsVariant.isValid() && schemasSettingsVariant.type() == QVariant::Map )
84       mSchemaSettings = schemasSettingsVariant.toMap();
85   }
86 
87   mUseGeometryColumns = QgsMssqlConnection::geometryColumnsOnly( mName );
88   mUseEstimatedMetadata = QgsMssqlConnection::useEstimatedMetadata( mName );
89   mAllowGeometrylessTables = QgsMssqlConnection::allowGeometrylessTables( mName );
90 
91   mConnInfo = "dbname='" + mDatabase + "' host='" + mHost + "' user='" + mUsername + "' password='" + mPassword + '\'';
92   if ( !mService.isEmpty() )
93     mConnInfo += " service='" + mService + '\'';
94   if ( mUseEstimatedMetadata )
95     mConnInfo += QLatin1String( " estimatedmetadata=true" );
96 }
97 
stop()98 void QgsMssqlConnectionItem::stop()
99 {
100   if ( mColumnTypeThread )
101   {
102     mColumnTypeThread->stop();
103     mColumnTypeThread->wait();
104     delete mColumnTypeThread;
105     mColumnTypeThread = nullptr;
106   }
107 }
108 
refresh()109 void QgsMssqlConnectionItem::refresh()
110 {
111   QgsDebugMsgLevel( "mPath = " + mPath, 3 );
112   stop();
113 
114   // Clear all children
115   const QVector<QgsDataItem *> allChidren = children();
116   for ( QgsDataItem *item : allChidren )
117   {
118     removeChildItem( item );
119     delete item;
120   }
121 
122   // read up the schemas and layers from database
123   const QVector<QgsDataItem *> items = createChildren();
124   for ( QgsDataItem *item : items )
125     addChildItem( item, true );
126 }
127 
createChildren()128 QVector<QgsDataItem *> QgsMssqlConnectionItem::createChildren()
129 {
130   setState( Qgis::BrowserItemState::Populating );
131 
132   stop();
133 
134   QVector<QgsDataItem *> children;
135   if ( deferredDelete() )
136     return children;
137 
138   readConnectionSettings();
139 
140   std::shared_ptr<QgsMssqlDatabase> db = QgsMssqlDatabase::connectDb( mService, mHost, mDatabase, mUsername, mPassword );
141 
142   if ( !db->isValid() )
143   {
144     children.append( new QgsErrorItem( this, db->errorText(), mPath + "/error" ) );
145     setAsPopulated();
146     return children;
147   }
148 
149   // build sql statement
150   QString query = QgsMssqlConnection::buildQueryForTables( mName );
151 
152   const bool disableInvalidGeometryHandling = QgsMssqlConnection::isInvalidGeometryHandlingDisabled( mName );
153 
154   // issue the sql query
155   QSqlQuery q = QSqlQuery( db->db() );
156   q.setForwardOnly( true );
157   ( void )q.exec( query );
158 
159   QSet< QString > addedSchemas;
160 
161   if ( q.isActive() )
162   {
163     QVector<QgsDataItem *> newLayers;
164     while ( q.next() )
165     {
166       QgsMssqlLayerProperty layer;
167       layer.schemaName = q.value( 0 ).toString();
168       layer.tableName = q.value( 1 ).toString();
169       layer.geometryColName = q.value( 2 ).toString();
170       layer.srid = q.value( 3 ).toString();
171       layer.type = q.value( 4 ).toString();
172       layer.isView = q.value( 5 ).toBool();
173       layer.pkCols = QStringList(); //TODO
174       layer.isGeography = false;
175 
176       // skip layers which are added already
177       bool skip = false;
178       const auto constMChildren = mChildren;
179       for ( QgsDataItem *child : constMChildren )
180       {
181         if ( child->name() == layer.schemaName )
182         {
183           const auto constChildren = child->children();
184           for ( QgsDataItem *child2 : constChildren )
185           {
186             QgsMssqlLayerItem *layerItem = qobject_cast< QgsMssqlLayerItem *>( child2 );
187             if ( child2->name() == layer.tableName && layerItem && layerItem->disableInvalidGeometryHandling() == disableInvalidGeometryHandling )
188             {
189               newLayers.append( child2 );
190               skip = true; // already added
191               break;
192             }
193           }
194         }
195       }
196 
197       if ( skip )
198         continue;
199 
200       QString type = layer.type;
201       QString srid = layer.srid;
202 
203       QgsMssqlSchemaItem *schemaItem = nullptr;
204       const auto constChildren = children;
205       for ( QgsDataItem *child : constChildren )
206       {
207         if ( child->name() == layer.schemaName )
208         {
209           schemaItem = static_cast< QgsMssqlSchemaItem * >( child );
210           break;
211         }
212       }
213 
214       if ( !schemaItem )
215       {
216         schemaItem = new QgsMssqlSchemaItem( this, layer.schemaName, mPath + '/' + layer.schemaName );
217         schemaItem->setState( Qgis::BrowserItemState::Populating );
218         addedSchemas.insert( layer.schemaName );
219         children.append( schemaItem );
220       }
221 
222       if ( !layer.geometryColName.isNull() )
223       {
224         if ( type == QLatin1String( "GEOMETRY" ) || type.isNull() || srid.isEmpty() )
225         {
226           if ( !mColumnTypeThread )
227           {
228             mColumnTypeThread = new QgsMssqlGeomColumnTypeThread( mService, mHost, mDatabase, mUsername, mPassword, true /* use estimated metadata */ );
229 
230             connect( mColumnTypeThread, &QgsMssqlGeomColumnTypeThread::setLayerType,
231                      this, &QgsMssqlConnectionItem::setLayerType );
232             connect( this, &QgsMssqlConnectionItem::addGeometryColumn,
233                      mColumnTypeThread, &QgsMssqlGeomColumnTypeThread::addGeometryColumn );
234           }
235 
236           emit addGeometryColumn( layer );
237           continue;
238         }
239       }
240 
241       QgsMssqlLayerItem *added = schemaItem->addLayer( layer, false );
242       if ( added )
243         newLayers.append( added );
244     }
245 
246     // Remove no more present items
247     const auto constMChildren = mChildren;
248     for ( QgsDataItem *child : constMChildren )
249     {
250       const auto constChildren = child->children();
251       for ( QgsDataItem *child2 : constChildren )
252       {
253         if ( findItem( newLayers, child2 ) < 0 )
254           child->deleteChildItem( child2 );
255       }
256     }
257 
258     // add missing schemas (i.e., empty schemas)
259     const QString uri = connInfo();
260     const QStringList allSchemas = QgsMssqlConnection::schemas( uri, nullptr );
261     QStringList excludedSchema = QgsMssqlConnection::excludedSchemasList( mName );
262     for ( const QString &schema : allSchemas )
263     {
264       if ( mSchemasFilteringEnabled && excludedSchema.contains( schema ) )
265         continue;  // user does not want it to be shown
266 
267       if ( addedSchemas.contains( schema ) )
268         continue;
269 
270       if ( QgsMssqlConnection::isSystemSchema( schema ) )
271         continue;
272 
273       QgsMssqlSchemaItem *schemaItem = new QgsMssqlSchemaItem( this, schema, mPath + '/' + schema );
274       schemaItem->setState( Qgis::BrowserItemState::Populated ); // no tables
275       addedSchemas.insert( schema );
276       children.append( schemaItem );
277     }
278 
279     // spawn threads (new layers will be added later on)
280     if ( mColumnTypeThread )
281     {
282       connect( mColumnTypeThread, &QThread::finished, this, &QgsMssqlConnectionItem::setAsPopulated );
283       mColumnTypeThread->start();
284     }
285     else
286     {
287       //set all as populated -- we also need to do this for newly created items, because they won't yet be children of this item
288       for ( QgsDataItem *child : std::as_const( children ) )
289       {
290         child->setState( Qgis::BrowserItemState::Populated );
291       }
292       setAsPopulated();
293     }
294   }
295   else
296   {
297     setAsPopulated();
298   }
299 
300   return children;
301 }
302 
setAsPopulated()303 void QgsMssqlConnectionItem::setAsPopulated()
304 {
305   const auto constMChildren = mChildren;
306   for ( QgsDataItem *child : constMChildren )
307   {
308     child->setState( Qgis::BrowserItemState::Populated );
309   }
310   setState( Qgis::BrowserItemState::Populated );
311 }
312 
setAllowGeometrylessTables(const bool allow)313 void QgsMssqlConnectionItem::setAllowGeometrylessTables( const bool allow )
314 {
315   mAllowGeometrylessTables = allow;
316   QgsMssqlConnection::setAllowGeometrylessTables( mName, allow );
317   refresh();
318 }
319 
setLayerType(QgsMssqlLayerProperty layerProperty)320 void QgsMssqlConnectionItem::setLayerType( QgsMssqlLayerProperty layerProperty )
321 {
322   QgsMssqlSchemaItem *schemaItem = nullptr;
323 
324   const auto constMChildren = mChildren;
325   for ( QgsDataItem *child : constMChildren )
326   {
327     if ( child->name() == layerProperty.schemaName )
328     {
329       schemaItem = static_cast< QgsMssqlSchemaItem * >( child );
330       break;
331     }
332   }
333 
334   if ( !schemaItem )
335   {
336     QgsDebugMsg( QStringLiteral( "schema item for %1 not found." ).arg( layerProperty.schemaName ) );
337     return;
338   }
339 
340   const auto constChildren = schemaItem->children();
341   for ( QgsDataItem *layerItem : constChildren )
342   {
343     if ( layerItem->name() == layerProperty.tableName )
344       return; // already added
345   }
346 
347 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
348   QStringList typeList = layerProperty.type.split( ',', QString::SkipEmptyParts );
349   QStringList sridList = layerProperty.srid.split( ',', QString::SkipEmptyParts );
350 #else
351   QStringList typeList = layerProperty.type.split( ',', Qt::SkipEmptyParts );
352   QStringList sridList = layerProperty.srid.split( ',', Qt::SkipEmptyParts );
353 #endif
354   Q_ASSERT( typeList.size() == sridList.size() );
355 
356   for ( int i = 0; i < typeList.size(); i++ )
357   {
358     QgsWkbTypes::Type wkbType = QgsMssqlTableModel::wkbTypeFromMssql( typeList[i] );
359     if ( wkbType == QgsWkbTypes::Unknown )
360     {
361       QgsDebugMsg( QStringLiteral( "unsupported geometry type:%1" ).arg( typeList[i] ) );
362       continue;
363     }
364 
365     layerProperty.type = typeList[i];
366     layerProperty.srid = sridList[i];
367     schemaItem->addLayer( layerProperty, true );
368   }
369 
370   if ( typeList.isEmpty() )
371   {
372     // this suggests that retrieval of geometry type and CRS failed if no results were returned
373     // for examle due to invalid geometries in the table (WHAAAT?)
374     // but we still want to add have such table in the list
375     schemaItem->addLayer( layerProperty, true );
376   }
377 }
378 
equal(const QgsDataItem * other)379 bool QgsMssqlConnectionItem::equal( const QgsDataItem *other )
380 {
381   if ( type() != other->type() )
382   {
383     return false;
384   }
385 
386   const QgsMssqlConnectionItem *o = qobject_cast<const QgsMssqlConnectionItem *>( other );
387   return ( mPath == o->mPath && mName == o->mName );
388 }
389 
handleDrop(const QMimeData * data,const QString & toSchema)390 bool QgsMssqlConnectionItem::handleDrop( const QMimeData *data, const QString &toSchema )
391 {
392   if ( !QgsMimeDataUtils::isUriList( data ) )
393     return false;
394 
395   // TODO: probably should show a GUI with settings etc
396   QStringList importResults;
397   bool hasError = false;
398 
399   QgsMimeDataUtils::UriList lst = QgsMimeDataUtils::decodeUriList( data );
400   const auto constLst = lst;
401   for ( const QgsMimeDataUtils::Uri &u : constLst )
402   {
403     if ( u.layerType != QLatin1String( "vector" ) )
404     {
405       importResults.append( tr( "%1: Not a vector layer!" ).arg( u.name ) );
406       hasError = true; // only vectors can be imported
407       continue;
408     }
409 
410     // open the source layer
411     const QgsVectorLayer::LayerOptions options { QgsProject::instance()->transformContext() };
412     QgsVectorLayer *srcLayer = new QgsVectorLayer( u.uri, u.name, u.providerKey, options );
413 
414     if ( srcLayer->isValid() )
415     {
416       QString tableName;
417       if ( !toSchema.isEmpty() )
418       {
419         tableName = QStringLiteral( "\"%1\".\"%2\"" ).arg( toSchema, u.name );
420       }
421       else
422       {
423         tableName = u.name;
424       }
425 
426       QString uri = connInfo() + " table=" + tableName;
427       if ( srcLayer->geometryType() != QgsWkbTypes::NullGeometry )
428         uri += QLatin1String( " (geom)" );
429 
430       std::unique_ptr< QgsVectorLayerExporterTask > exportTask( QgsVectorLayerExporterTask::withLayerOwnership( srcLayer, uri, QStringLiteral( "mssql" ), srcLayer->crs() ) );
431 
432       // when export is successful:
433       connect( exportTask.get(), &QgsVectorLayerExporterTask::exportComplete, this, [ = ]()
434       {
435         // this is gross - TODO - find a way to get access to messageBar from data items
436         QMessageBox::information( nullptr, tr( "Import to MSSQL database" ), tr( "Import was successful." ) );
437         if ( state() == Qgis::BrowserItemState::Populated )
438           refresh();
439         else
440           populate();
441       } );
442 
443       // when an error occurs:
444       connect( exportTask.get(), &QgsVectorLayerExporterTask::errorOccurred, this, [ = ]( Qgis::VectorExportResult error, const QString & errorMessage )
445       {
446         if ( error != Qgis::VectorExportResult::UserCanceled )
447         {
448           QgsMessageOutput *output = QgsMessageOutput::createMessageOutput();
449           output->setTitle( tr( "Import to MSSQL database" ) );
450           output->setMessage( tr( "Failed to import some layers!\n\n" ) + errorMessage, QgsMessageOutput::MessageText );
451           output->showMessage();
452         }
453         if ( state() == Qgis::BrowserItemState::Populated )
454           refresh();
455         else
456           populate();
457       } );
458 
459       QgsApplication::taskManager()->addTask( exportTask.release() );
460     }
461     else
462     {
463       importResults.append( tr( "%1: Not a valid layer!" ).arg( u.name ) );
464       hasError = true;
465     }
466   }
467 
468   if ( hasError )
469   {
470     QgsMessageOutput *output = QgsMessageOutput::createMessageOutput();
471     output->setTitle( tr( "Import to MSSQL database" ) );
472     output->setMessage( tr( "Failed to import some layers!\n\n" ) + importResults.join( QLatin1Char( '\n' ) ), QgsMessageOutput::MessageText );
473     output->showMessage();
474   }
475 
476   return true;
477 }
478 
479 
480 // ---------------------------------------------------------------------------
QgsMssqlLayerItem(QgsDataItem * parent,const QString & name,const QString & path,Qgis::BrowserLayerType layerType,const QgsMssqlLayerProperty & layerProperty)481 QgsMssqlLayerItem::QgsMssqlLayerItem( QgsDataItem *parent, const QString &name, const QString &path, Qgis::BrowserLayerType layerType, const QgsMssqlLayerProperty &layerProperty )
482   : QgsLayerItem( parent, name, path, QString(), layerType, QStringLiteral( "mssql" ) )
483   , mLayerProperty( layerProperty )
484 {
485   mCapabilities |= Qgis::BrowserItemCapability::Delete;
486   mUri = createUri();
487   setState( Qgis::BrowserItemState::NotPopulated );
488 }
489 
490 
createClone()491 QgsMssqlLayerItem *QgsMssqlLayerItem::createClone()
492 {
493   return new QgsMssqlLayerItem( mParent, mName, mPath, mLayerType, mLayerProperty );
494 }
495 
disableInvalidGeometryHandling() const496 bool QgsMssqlLayerItem::disableInvalidGeometryHandling() const
497 {
498   return mDisableInvalidGeometryHandling;
499 }
500 
createUri()501 QString QgsMssqlLayerItem::createUri()
502 {
503   QString pkColName = !mLayerProperty.pkCols.isEmpty() ? mLayerProperty.pkCols.at( 0 ) : QString();
504   QgsMssqlConnectionItem *connItem = qobject_cast<QgsMssqlConnectionItem *>( parent() ? parent()->parent() : nullptr );
505 
506   if ( !connItem )
507   {
508     QgsDebugMsg( QStringLiteral( "connection item not found." ) );
509     return QString();
510   }
511 
512   QgsDataSourceUri uri = QgsDataSourceUri( connItem->connInfo() );
513   uri.setDataSource( mLayerProperty.schemaName, mLayerProperty.tableName, mLayerProperty.geometryColName, mLayerProperty.sql, pkColName );
514   uri.setSrid( mLayerProperty.srid );
515   uri.setWkbType( QgsMssqlTableModel::wkbTypeFromMssql( mLayerProperty.type ) );
516   uri.setUseEstimatedMetadata( QgsMssqlConnection::useEstimatedMetadata( connItem->name() ) );
517   mDisableInvalidGeometryHandling = QgsMssqlConnection::isInvalidGeometryHandlingDisabled( connItem->name() );
518   uri.setParam( QStringLiteral( "disableInvalidGeometryHandling" ), mDisableInvalidGeometryHandling ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
519   if ( QgsMssqlConnection::geometryColumnsOnly( connItem->name() ) )
520   {
521     uri.setParam( QStringLiteral( "extentInGeometryColumns" ), QgsMssqlConnection::extentInGeometryColumns( connItem->name() ) ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
522   }
523   if ( mLayerProperty.isView )
524     uri.setParam( QStringLiteral( "primaryKeyInGeometryColumns" ), QgsMssqlConnection::primaryKeyInGeometryColumns( connItem->name() ) ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
525 
526   QgsDebugMsgLevel( QStringLiteral( "layer uri: %1" ).arg( uri.uri() ), 3 );
527   return uri.uri();
528 }
529 
530 // ---------------------------------------------------------------------------
QgsMssqlSchemaItem(QgsDataItem * parent,const QString & name,const QString & path)531 QgsMssqlSchemaItem::QgsMssqlSchemaItem( QgsDataItem *parent, const QString &name, const QString &path )
532   : QgsDatabaseSchemaItem( parent, name, path, QStringLiteral( "MSSQL" ) )
533 {
534   mIconName = QStringLiteral( "mIconDbSchema.svg" );
535   //not fertile, since children are created by QgsMssqlConnectionItem
536   mCapabilities &= ~Qgis::BrowserItemCapabilities( Qgis::BrowserItemCapability::Fertile );
537 }
538 
createChildren()539 QVector<QgsDataItem *> QgsMssqlSchemaItem::createChildren()
540 {
541   return QVector<QgsDataItem *>();
542 }
543 
544 
addLayers(QgsDataItem * newLayers)545 void QgsMssqlSchemaItem::addLayers( QgsDataItem *newLayers )
546 {
547   // Add new items
548   const auto constChildren = newLayers->children();
549   for ( QgsDataItem *child : constChildren )
550   {
551     // Is it present in children?
552     if ( findItem( mChildren, child ) >= 0 )
553     {
554       continue;
555     }
556     QgsMssqlLayerItem *layer = static_cast< QgsMssqlLayerItem * >( child )->createClone();
557     addChildItem( layer, true );
558   }
559 }
560 
addLayer(const QgsMssqlLayerProperty & layerProperty,bool refresh)561 QgsMssqlLayerItem *QgsMssqlSchemaItem::addLayer( const QgsMssqlLayerProperty &layerProperty, bool refresh )
562 {
563   QgsWkbTypes::Type wkbType = QgsMssqlTableModel::wkbTypeFromMssql( layerProperty.type );
564   QString tip = tr( "%1 as %2 in %3" ).arg( layerProperty.geometryColName, QgsWkbTypes::displayString( wkbType ), layerProperty.srid );
565 
566   Qgis::BrowserLayerType layerType;
567   QgsWkbTypes::Type flatType = QgsWkbTypes::flatType( wkbType );
568   switch ( flatType )
569   {
570     case QgsWkbTypes::Point:
571     case QgsWkbTypes::MultiPoint:
572       layerType = Qgis::BrowserLayerType::Point;
573       break;
574     case QgsWkbTypes::LineString:
575     case QgsWkbTypes::MultiLineString:
576       layerType = Qgis::BrowserLayerType::Line;
577       break;
578     case QgsWkbTypes::Polygon:
579     case QgsWkbTypes::MultiPolygon:
580       layerType = Qgis::BrowserLayerType::Polygon;
581       break;
582     default:
583       if ( layerProperty.type == QLatin1String( "NONE" ) && layerProperty.geometryColName.isEmpty() )
584       {
585         layerType = Qgis::BrowserLayerType::TableLayer;
586         tip = tr( "as geometryless table" );
587       }
588       else if ( !layerProperty.geometryColName.isEmpty() && layerProperty.type.isEmpty() )
589       {
590         // geometry column is there but we failed to determine geometry type (e.g. due to invalid geometries)
591         layerType = Qgis::BrowserLayerType::Vector;
592       }
593       else
594       {
595         return nullptr;
596       }
597   }
598 
599   QgsMssqlLayerItem *layerItem = new QgsMssqlLayerItem( this, layerProperty.tableName, mPath + '/' + layerProperty.tableName, layerType, layerProperty );
600   layerItem->setToolTip( tip );
601   if ( refresh )
602     addChildItem( layerItem, true );
603   else
604   {
605     addChild( layerItem );
606     layerItem->setParent( this );
607   }
608 
609   return layerItem;
610 }
611 
refresh()612 void QgsMssqlSchemaItem::refresh()
613 {
614   if ( auto *lParent = parent() )
615     lParent->refresh();
616 }
617 
618 
createChildren()619 QVector<QgsDataItem *> QgsMssqlLayerItem::createChildren()
620 {
621   QVector<QgsDataItem *> children;
622   children.push_back( new QgsFieldsItem( this, uri() + QStringLiteral( "/columns/ " ), createUri(), providerKey(), mLayerProperty.schemaName, mLayerProperty.tableName ) );
623   return children;
624 }
625 
626 // ---------------------------------------------------------------------------
QgsMssqlRootItem(QgsDataItem * parent,const QString & name,const QString & path)627 QgsMssqlRootItem::QgsMssqlRootItem( QgsDataItem *parent, const QString &name, const QString &path )
628   : QgsConnectionsRootItem( parent, name, path, QStringLiteral( "MSSQL" ) )
629 {
630   mIconName = QStringLiteral( "mIconMssql.svg" );
631   populate();
632 }
633 
createChildren()634 QVector<QgsDataItem *> QgsMssqlRootItem::createChildren()
635 {
636   QVector<QgsDataItem *> connections;
637   QgsSettings settings;
638   settings.beginGroup( QStringLiteral( "/MSSQL/connections" ) );
639   const auto constChildGroups = settings.childGroups();
640   for ( const QString &connName : constChildGroups )
641   {
642     connections << new QgsMssqlConnectionItem( this, connName, mPath + '/' + connName );
643   }
644   return connections;
645 }
646 
647 #ifdef HAVE_GUI
paramWidget()648 QWidget *QgsMssqlRootItem::paramWidget()
649 {
650   QgsMssqlSourceSelect *select = new QgsMssqlSourceSelect( nullptr, Qt::WindowFlags(), QgsProviderRegistry::WidgetMode::Manager );
651   connect( select, &QgsMssqlSourceSelect::connectionsChanged, this, &QgsMssqlRootItem::onConnectionsChanged );
652   return select;
653 }
654 
onConnectionsChanged()655 void QgsMssqlRootItem::onConnectionsChanged()
656 {
657   refresh();
658 }
659 #endif
660 
name()661 QString QgsMssqlDataItemProvider::name()
662 {
663   return QStringLiteral( "MSSQL" );
664 }
665 
dataProviderKey() const666 QString QgsMssqlDataItemProvider::dataProviderKey() const
667 {
668   return QStringLiteral( "mssql" );
669 }
670 
capabilities() const671 int QgsMssqlDataItemProvider::capabilities() const
672 {
673   return QgsDataProvider::Database;
674 }
675 
createDataItem(const QString & pathIn,QgsDataItem * parentItem)676 QgsDataItem *QgsMssqlDataItemProvider::createDataItem( const QString &pathIn, QgsDataItem *parentItem )
677 {
678   Q_UNUSED( pathIn )
679   return new QgsMssqlRootItem( parentItem, QStringLiteral( "MSSQL" ), QStringLiteral( "mssql:" ) );
680 }
681 
682 
layerCollection() const683 bool QgsMssqlSchemaItem::layerCollection() const
684 {
685   return true;
686 }
687