Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Database connection utility #44200

Merged
merged 3 commits into from
Jul 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ Sets the query execution time to ``queryExecutionTime`` milliseconds.
QStringList primaryKeyColumns;
QString geometryColumn;
bool disableSelectAtId;

};

struct TableProperty
Expand Down Expand Up @@ -551,6 +552,19 @@ Creates and returns a (possibly invalid) vector layer based on the ``sql`` state

:raises QgsProviderConnectionException: if any errors are encountered or if SQL layer creation is not supported.

.. versionadded:: 3.22
%End

virtual SqlVectorLayerOptions sqlOptions( const QString &layerSource ) throw( QgsProviderConnectionException );
%Docstring
Returns the SQL layer options from a ``layerSource``.

.. note::

the default implementation returns a default constructed option object.

:raises QgsProviderConnectionException: if any errors are encountered or if SQL layer creation is not supported.

.. versionadded:: 3.22
%End

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,6 @@ Returns ``True`` if this is a valid layer. It is up to individual providers
to determine what constitutes a valid layer.
%End


virtual void updateExtents();
%Docstring
Update the extents of the layer. Not implemented by default.
Expand Down
2 changes: 1 addition & 1 deletion python/core/auto_generated/qgsmaplayer.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -1036,7 +1036,7 @@ Read the style for the current layer from the DOM node supplied.
virtual bool writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context,
StyleCategories categories = AllStyleCategories ) const = 0;
%Docstring
Write the style for the layer into the docment provided.
Write the style for the layer into the document provided.

:param node: the node that will have the style element added to it.
:param doc: the document that will have the QDomNode added.
Expand Down
8 changes: 8 additions & 0 deletions python/core/auto_generated/qgsmaplayerutils.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ Contains utility functions for working with map layers.
Returns the combined extent of a list of ``layers``.

The ``crs`` argument specifies the desired coordinate reference system for the combined extent.
%End

static QgsAbstractDatabaseProviderConnection *databaseConnection( const QgsMapLayer *layer ) /Factory/;
%Docstring
Creates and returns the (possibly ``None``) database connection for a ``layer``.
Ownership is transferred to the caller.

.. versionadded:: 3.22
%End

};
Expand Down
25 changes: 25 additions & 0 deletions python/gui/auto_generated/qgsqueryresultwidget.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ DB connection (an instance of :py:class:`QgsAbstractDatabaseProviderConnection`)
Query results are displayed in a table view.
Query execution and result fetching can be interrupted by pressing the "Stop" push button.

The widget supports a few QueryWidgetMode modes that pre-configure the widget appearance to
be used in different contexts like when updating the SQL of an existing query layer.

.. note::

the ownership of the connection is transferred to the widget.
Expand All @@ -32,13 +35,30 @@ Query execution and result fetching can be interrupted by pressing the "Stop" pu
%End
public:


enum class QueryWidgetMode
{
SqlQueryMode,
QueryLayerUpdateMode,
};

QgsQueryResultWidget( QWidget *parent = 0, QgsAbstractDatabaseProviderConnection *connection /Transfer/ = 0 );
%Docstring
Creates a QgsQueryResultWidget with the given ``connection``, ownership is transferred to the widget.
%End

virtual ~QgsQueryResultWidget();

void setSqlVectorLayerOptions( const QgsAbstractDatabaseProviderConnection::SqlVectorLayerOptions &options );
%Docstring
Initializes the widget from ``options``.
%End

void setWidgetMode( QueryWidgetMode widgetMode );
%Docstring
Sets the widget mode to ``widgetMode``, default is SqlQueryMode.
%End

void setConnection( QgsAbstractDatabaseProviderConnection *connection /Transfer/ );
%Docstring
Sets the connection to ``connection``, ownership is transferred to the widget.
Expand All @@ -52,6 +72,11 @@ Convenience method to set the SQL editor text to ``sql``.

public slots:

void notify( const QString &title, const QString &text, Qgis::MessageLevel level = Qgis::MessageLevel::Info );
%Docstring
Displays a message with ``text`` ``title`` and ``level`` in the widget's message bar.
%End

void executeQuery();
%Docstring
Starts executing the query.
Expand Down
22 changes: 0 additions & 22 deletions python/plugins/db_manager/db_manager_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,6 @@ def initGui(self):
else:
self.iface.addPluginToMenu(QApplication.translate("DBManagerPlugin", "DB Manager"), self.action)

self.layerAction = QAction(QgsApplication.getThemeIcon('dbmanager.svg'), QApplication.translate("DBManagerPlugin", "Update SQL Layer…"),
self.iface.mainWindow())
self.layerAction.setObjectName("dbManagerUpdateSqlLayer")
self.layerAction.triggered.connect(self.onUpdateSqlLayer)
self.iface.addCustomActionForLayerType(self.layerAction, "", QgsMapLayerType.VectorLayer, False)
for l in list(QgsProject.instance().mapLayers().values()):
self.onLayerWasAdded(l)
QgsProject.instance().layerWasAdded.connect(self.onLayerWasAdded)

def unload(self):
# Remove the plugin menu item and icon
if hasattr(self.iface, 'databaseMenu'):
Expand All @@ -77,22 +68,9 @@ def unload(self):
else:
self.iface.removeToolBarIcon(self.action)

self.iface.removeCustomActionForLayerType(self.layerAction)
QgsProject.instance().layerWasAdded.disconnect(self.onLayerWasAdded)

if self.dlg is not None:
self.dlg.close()

def onLayerWasAdded(self, aMapLayer):
# Be able to update every Db layer from Postgres, Spatialite and Oracle
if hasattr(aMapLayer, 'dataProvider') and aMapLayer.dataProvider() and aMapLayer.dataProvider().name() in ['postgres', 'spatialite', 'oracle']:
self.iface.addCustomActionForLayer(self.layerAction, aMapLayer)
# virtual has QUrl source
# url = QUrl(QUrl.fromPercentEncoding(l.source()))
# url.queryItemValue('query')
# url.queryItemValue('uid')
# url.queryItemValue('geometry')

def onUpdateSqlLayer(self):
# Be able to update every Db layer from Postgres, Spatialite and Oracle
l = self.iface.activeLayer()
Expand Down
4 changes: 3 additions & 1 deletion src/app/qgisapp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
#include "qgsfixattributedialog.h"
#include "qgsprojecttimesettings.h"
#include "qgsmaplayertemporalproperties.h"
#include "qgsmaplayerutils.h"
#include "qgsmeshlayertemporalproperties.h"
#include "qgsvectorlayersavestyledialog.h"
#include "maptools/qgsappmaptools.h"
Expand Down Expand Up @@ -2236,7 +2237,8 @@ void QgisApp::resolveVectorLayerDependencies( QgsVectorLayer *vl, QgsMapLayer::S
if ( providerMetadata )
{
// Retrieve the DB connection (if any)
std::unique_ptr< QgsAbstractDatabaseProviderConnection > conn { static_cast<QgsAbstractDatabaseProviderConnection *>( providerMetadata->createConnection( vl->dataProvider()->uri().uri(), {} ) ) };

std::unique_ptr< QgsAbstractDatabaseProviderConnection > conn { QgsMapLayerUtils::databaseConnection( vl ) };
if ( conn )
{
QString tableSchema;
Expand Down
92 changes: 77 additions & 15 deletions src/app/qgsapplayertreeviewmenuprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,42 +13,44 @@
* *
***************************************************************************/
#include <QClipboard>

#include "qgsapplayertreeviewmenuprovider.h"
#include <QMessageBox>

#include "qgisapp.h"
#include "qgsapplayertreeviewmenuprovider.h"
#include "qgsapplication.h"
#include "qgsclipboard.h"
#include "qgscolorwidgets.h"
#include "qgscolorschemeregistry.h"
#include "qgscolorswatchgrid.h"
#include "qgscolorwidgets.h"
#include "qgsdialog.h"
#include "qgsgui.h"
#include "qgslayernotesmanager.h"
#include "qgslayernotesutils.h"
#include "qgslayertree.h"
#include "qgslayertreemodel.h"
#include "qgslayertreemodellegendnode.h"
#include "qgslayertreeregistrybridge.h"
#include "qgslayertreeviewdefaultactions.h"
#include "qgsmapcanvas.h"
#include "qgsmaplayerstylecategoriesmodel.h"
#include "qgsmaplayerstyleguiutils.h"
#include "qgsmaplayerutils.h"
#include "qgsmessagebar.h"
#include "qgspointcloudlayer.h"
#include "qgsproject.h"
#include "qgsqueryresultwidget.h"
#include "qgsrasterlayer.h"
#include "qgsrenderer.h"
#include "qgssymbol.h"
#include "qgssinglesymbolrenderer.h"
#include "qgsstyle.h"
#include "qgssymbol.h"
#include "qgssymbollayerutils.h"
#include "qgssymbolselectordialog.h"
#include "qgsvectordataprovider.h"
#include "qgsvectorlayer.h"
#include "qgslayertreeregistrybridge.h"
#include "qgssymbolselectordialog.h"
#include "qgssinglesymbolrenderer.h"
#include "qgsmaplayerstylecategoriesmodel.h"
#include "qgssymbollayerutils.h"
#include "qgsxmlutils.h"
#include "qgsmessagebar.h"
#include "qgspointcloudlayer.h"
#include "qgsvectorlayerlabeling.h"
#include "qgslayernotesmanager.h"
#include "qgslayernotesutils.h"
#include "qgsxmlutils.h"

#include <QMessageBox>

QgsAppLayerTreeViewMenuProvider::QgsAppLayerTreeViewMenuProvider( QgsLayerTreeView *view, QgsMapCanvas *canvas )
: mView( view )
Expand Down Expand Up @@ -217,6 +219,66 @@ QMenu *QgsAppLayerTreeViewMenuProvider::createContextMenu()
}
}

// No raster support in createSqlVectorLayer (yet)
if ( vlayer )
{
std::unique_ptr< QgsAbstractDatabaseProviderConnection> conn { QgsMapLayerUtils::databaseConnection( layer ) };
if ( conn )
menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/dbmanager.svg" ) ), tr( "Update SQL Layer…" ), menu, [ layer ]
{
std::unique_ptr< QgsAbstractDatabaseProviderConnection> conn2 { QgsMapLayerUtils::databaseConnection( layer ) };
if ( conn2 )
{
QgsDialog dialog;
dialog.setObjectName( QStringLiteral( "SqlUpdateDialog" ) );
dialog.setWindowTitle( tr( "%1 — Update SQL" ).arg( layer->name() ) );
QgsGui::enableAutoGeometryRestore( &dialog );
QgsAbstractDatabaseProviderConnection::SqlVectorLayerOptions options { conn2->sqlOptions( layer->source() ) };
options.layerName = layer->name();
QgsQueryResultWidget *queryResultWidget { new QgsQueryResultWidget( &dialog, conn2.release() ) };
queryResultWidget->setWidgetMode( QgsQueryResultWidget::QueryWidgetMode::QueryLayerUpdateMode );
queryResultWidget->setSqlVectorLayerOptions( options );
queryResultWidget->executeQuery();
queryResultWidget->layout()->setMargin( 0 );
dialog.layout()->addWidget( queryResultWidget );

connect( queryResultWidget, &QgsQueryResultWidget::createSqlVectorLayer, queryResultWidget, [queryResultWidget, layer ]( const QString &, const QString &, const QgsAbstractDatabaseProviderConnection::SqlVectorLayerOptions & options )
{
std::unique_ptr< QgsAbstractDatabaseProviderConnection> conn3 { QgsMapLayerUtils::databaseConnection( layer ) };
if ( conn3 )
{
try
{
std::unique_ptr<QgsMapLayer> sqlLayer { conn3->createSqlVectorLayer( options ) };
nyalldawson marked this conversation as resolved.
Show resolved Hide resolved
if ( sqlLayer->isValid() )
{
layer->setDataSource( sqlLayer->source(), sqlLayer->name(), sqlLayer->dataProvider()->name(), QgsDataProvider::ProviderOptions() );
queryResultWidget->notify( QObject::tr( "Layer Update Success" ), QObject::tr( "The SQL layer was updated successfully" ), Qgis::MessageLevel::Success );
}
else
{
QString error { sqlLayer->dataProvider()->error().message( QgsErrorMessage::Format::Text ) };
if ( error.isEmpty() )
{
error = QObject::tr( "layer is not valid, check the log messages for more information" );
}
queryResultWidget->notify( QObject::tr( "Layer Update Error" ), QObject::tr( "Error updating the SQL layer: %1" ).arg( error ), Qgis::MessageLevel::Critical );
}
}
catch ( QgsProviderConnectionException &ex )
{
queryResultWidget->notify( QObject::tr( "Layer Update Error" ), QObject::tr( "Error updating the SQL layer: %1" ).arg( ex.what() ), Qgis::MessageLevel::Critical );
}
}

} );

dialog.exec();

}
} );
}

addCustomLayerActions( menu, layer );
if ( layer && layer->type() == QgsMapLayerType::VectorLayer && static_cast<QgsVectorLayer *>( layer )->providerType() == QLatin1String( "virtual" ) )
{
Expand Down
19 changes: 17 additions & 2 deletions src/core/providers/ogr/qgsgeopackageproviderconnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -389,8 +389,6 @@ void QgsGeoPackageProviderConnection::setDefaultCapabilities()
mSqlLayerDefinitionCapabilities =
{
Qgis::SqlLayerDefinitionCapability::SubsetStringFilter,
Qgis::SqlLayerDefinitionCapability::PrimaryKeys,
Qgis::SqlLayerDefinitionCapability::GeometryColumn,
};
}

Expand Down Expand Up @@ -1170,6 +1168,23 @@ QMultiMap<Qgis::SqlKeywordCategory, QStringList> QgsGeoPackageProviderConnection
} );
}

QgsAbstractDatabaseProviderConnection::SqlVectorLayerOptions QgsGeoPackageProviderConnection::sqlOptions( const QString &layerSource )
{
SqlVectorLayerOptions options;
QgsProviderMetadata *providerMetadata { QgsProviderRegistry::instance()->providerMetadata( QStringLiteral( "ogr" ) ) };
Q_ASSERT( providerMetadata );
QMap<QString, QVariant> decoded = providerMetadata->decodeUri( layerSource );
if ( decoded.contains( QStringLiteral( "subset" ) ) )
{
options.sql = decoded[ QStringLiteral( "subset" ) ].toString();
}
else if ( decoded.contains( QStringLiteral( "layerName" ) ) )
{
options.sql = QStringLiteral( "SELECT * FROM %1" ).arg( QgsSqliteUtils::quotedIdentifier( decoded[ QStringLiteral( "layerName" ) ].toString() ) );
}
return options;
}

QgsGeoPackageProviderResultIterator::QgsGeoPackageProviderResultIterator( gdal::ogr_datasource_unique_ptr hDS, OGRLayerH ogrLayer )
: mHDS( std::move( hDS ) )
, mOgrLayer( ogrLayer )
Expand Down
1 change: 1 addition & 0 deletions src/core/providers/ogr/qgsgeopackageproviderconnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class QgsGeoPackageProviderConnection : public QgsAbstractDatabaseProviderConnec
QList<QgsVectorDataProvider::NativeType> nativeTypes() const override;
QgsFields fields( const QString &schema, const QString &table ) const override;
QMultiMap<Qgis::SqlKeywordCategory, QStringList> sqlDictionary() override;
SqlVectorLayerOptions sqlOptions( const QString &layerSource ) override;

private:

Expand Down
11 changes: 10 additions & 1 deletion src/core/providers/qgsabstractdatabaseproviderconnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,15 @@ Qgis::SqlLayerDefinitionCapabilities QgsAbstractDatabaseProviderConnection::sqlL
return mSqlLayerDefinitionCapabilities;
}


QString QgsAbstractDatabaseProviderConnection::tableUri( const QString &schema, const QString &name ) const
{
Q_UNUSED( schema )
Q_UNUSED( name )
throw QgsProviderConnectionException( QObject::tr( "Operation 'tableUri' is not supported" ) );
}


///@cond PRIVATE
void QgsAbstractDatabaseProviderConnection::checkCapability( QgsAbstractDatabaseProviderConnection::Capability capability ) const
{
Expand Down Expand Up @@ -1036,6 +1038,13 @@ void QgsAbstractDatabaseProviderConnection::renameVectorTable( const QString &,
checkCapability( Capability::RenameVectorTable );
}


QgsAbstractDatabaseProviderConnection::SqlVectorLayerOptions QgsAbstractDatabaseProviderConnection::sqlOptions( const QString & )
{
checkCapability( Capability::SqlLayers );
return SqlVectorLayerOptions();
}

void QgsAbstractDatabaseProviderConnection::renameRasterTable( const QString &, const QString &, const QString & ) const
{
checkCapability( Capability::RenameRasterTable );
Expand Down Expand Up @@ -1507,5 +1516,5 @@ long long QgsAbstractDatabaseProviderConnection::QueryResult::QueryResultIterato
return rowCountPrivate();
}


///@endcond private

10 changes: 10 additions & 0 deletions src/core/providers/qgsabstractdatabaseproviderconnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ class CORE_EXPORT QgsAbstractDatabaseProviderConnection : public QgsAbstractProv
QString geometryColumn;
//! If SelectAtId is disabled (default is false), not all data providers support this feature: check support with SqlLayerDefinitionCapability::SelectAtId capability
bool disableSelectAtId = false;

};

/**
Expand Down Expand Up @@ -676,6 +677,15 @@ class CORE_EXPORT QgsAbstractDatabaseProviderConnection : public QgsAbstractProv
*/
virtual QgsVectorLayer *createSqlVectorLayer( const SqlVectorLayerOptions &options ) const SIP_THROW( QgsProviderConnectionException ) SIP_FACTORY;

/**
* Returns the SQL layer options from a \a layerSource.
*
* \note the default implementation returns a default constructed option object.
* \throws QgsProviderConnectionException if any errors are encountered or if SQL layer creation is not supported.
* \since QGIS 3.22
*/
virtual SqlVectorLayerOptions sqlOptions( const QString &layerSource ) SIP_THROW( QgsProviderConnectionException );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mrylov We should implement that for HANA as well.


/**
* Executes raw \a sql and returns the (possibly empty) query results, optionally \a feedback can be provided.
*
Expand Down
Loading