mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-15 00:04:00 -04:00
Providers connections interface API
This is the implementation of the new DB connections API (grant proposal 2019). Summary The new API makes it available to QGIS core a new interface for provider connections and will allow to: replace the provider specific QgsSettings management in QGIS4 (save/load connections from the settings) NOT IN SCOPE FOR NOW. provide a unified API for common operations on DB connections: executeSql and get the results list tables names and properties and schemas create a new vector table (no rasters for now) create/rename/drop schemas and tables vacuum ....
This commit is contained in:
parent
945ac8caf4
commit
a3c4eb9947
@ -0,0 +1,6 @@
|
||||
# The following has been generated automatically from src/core/qgsabstractdatabaseproviderconnection.h
|
||||
QgsAbstractDatabaseProviderConnection.TableFlags.baseClass = QgsAbstractDatabaseProviderConnection
|
||||
TableFlags = QgsAbstractDatabaseProviderConnection # dirty hack since SIP seems to introduce the flags in module
|
||||
QgsAbstractDatabaseProviderConnection.Capability.baseClass = QgsAbstractDatabaseProviderConnection
|
||||
QgsAbstractDatabaseProviderConnection.Capabilities.baseClass = QgsAbstractDatabaseProviderConnection
|
||||
Capabilities = QgsAbstractDatabaseProviderConnection # dirty hack since SIP seems to introduce the flags in module
|
@ -0,0 +1,417 @@
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/qgsabstractdatabaseproviderconnection.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||
************************************************************************/
|
||||
|
||||
|
||||
|
||||
class QgsAbstractDatabaseProviderConnection : QgsAbstractProviderConnection
|
||||
{
|
||||
%Docstring
|
||||
The QgsAbstractDatabaseProviderConnection class provides common functionality
|
||||
for DB based connections.
|
||||
|
||||
This class performs low level DB operations without asking
|
||||
the user for confirmation or handling currently opened layers and the registry
|
||||
entries, it is responsibility of the client code to keep layers in sync.
|
||||
The class methods will throw exceptions in case the requested operation
|
||||
is not supported or cannot be performed without errors.
|
||||
|
||||
.. versionadded:: 3.10
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgsabstractdatabaseproviderconnection.h"
|
||||
%End
|
||||
public:
|
||||
static const QMetaObject staticMetaObject;
|
||||
|
||||
public:
|
||||
|
||||
enum TableFlag
|
||||
{
|
||||
Aspatial,
|
||||
Vector,
|
||||
Raster,
|
||||
View,
|
||||
MaterializedView,
|
||||
};
|
||||
|
||||
typedef QFlags<QgsAbstractDatabaseProviderConnection::TableFlag> TableFlags;
|
||||
|
||||
|
||||
struct TableProperty
|
||||
{
|
||||
|
||||
SIP_PYOBJECT __repr__();
|
||||
%MethodCode
|
||||
QString str = QStringLiteral( "<QgsAbstractDatabaseProviderConnection.TableProperty: '%1'>" ).arg( sipCpp->tableName() );
|
||||
sipRes = PyUnicode_FromString( str.toUtf8().constData() );
|
||||
%End
|
||||
|
||||
struct GeometryColumnType
|
||||
{
|
||||
SIP_PYOBJECT __repr__();
|
||||
%MethodCode
|
||||
QString str = QStringLiteral( "<QgsAbstractDatabaseProviderConnection.TableProperty.GeometryColumnType: '%1, %2'>" ).arg( QgsWkbTypes::displayString( sipCpp->wkbType ), sipCpp->crs.authid() );
|
||||
sipRes = PyUnicode_FromString( str.toUtf8().constData() );
|
||||
%End
|
||||
QgsWkbTypes::Type wkbType;
|
||||
QgsCoordinateReferenceSystem crs;
|
||||
|
||||
bool operator==( const GeometryColumnType &other ) const;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
QString tableName() const;
|
||||
%Docstring
|
||||
Returns the table name
|
||||
|
||||
.. seealso:: :py:func:`defaultName`
|
||||
%End
|
||||
|
||||
void setTableName( const QString &name );
|
||||
%Docstring
|
||||
Sets the table name to ``name``
|
||||
|
||||
.. seealso:: :py:func:`defaultName`
|
||||
%End
|
||||
|
||||
void addGeometryColumnType( const QgsWkbTypes::Type &type, const QgsCoordinateReferenceSystem &crs );
|
||||
%Docstring
|
||||
Appends the geometry column ``type`` with the given ``srid`` to the geometry column types list
|
||||
%End
|
||||
|
||||
QList<QgsAbstractDatabaseProviderConnection::TableProperty::GeometryColumnType> geometryColumnTypes() const;
|
||||
%Docstring
|
||||
Returns the list of geometry column types and CRSs.
|
||||
The method returns a list of GeometryColumnType
|
||||
%End
|
||||
|
||||
void setGeometryColumnTypes( const QList<QgsAbstractDatabaseProviderConnection::TableProperty::GeometryColumnType> &geometryColumnTypes );
|
||||
%Docstring
|
||||
Sets the geometry column types to ``geometryColumnTypes``
|
||||
%End
|
||||
|
||||
QString defaultName() const;
|
||||
%Docstring
|
||||
Returns the default name for the table entry
|
||||
|
||||
It is usually the table name but in case there are multiple geometry
|
||||
columns, the geometry column name is appendend to the table name.
|
||||
|
||||
.. seealso:: :py:func:`geometryColumnCount`
|
||||
%End
|
||||
|
||||
TableProperty at( int index ) const;
|
||||
%Docstring
|
||||
Returns the table property corresponding to the geometry type at
|
||||
the given ``index``
|
||||
%End
|
||||
|
||||
QString schema() const;
|
||||
%Docstring
|
||||
Returns the schema or an empty string for backends that do not support a schema
|
||||
%End
|
||||
|
||||
void setSchema( const QString &schema );
|
||||
%Docstring
|
||||
Sets the ``schema``
|
||||
%End
|
||||
|
||||
QString geometryColumn() const;
|
||||
%Docstring
|
||||
Returns the geometry column name
|
||||
%End
|
||||
|
||||
void setGeometryColumn( const QString &geometryColumn );
|
||||
%Docstring
|
||||
Sets the geometry column name to ``geometryColumn``
|
||||
%End
|
||||
|
||||
QStringList primaryKeyColumns() const;
|
||||
%Docstring
|
||||
Returns the list of primary key column names
|
||||
%End
|
||||
|
||||
void setPrimaryKeyColumns( const QStringList &primaryKeyColumns );
|
||||
%Docstring
|
||||
Sets the primary key column names to ``primaryKeyColumns``
|
||||
%End
|
||||
|
||||
QList<QgsCoordinateReferenceSystem> crsList() const;
|
||||
%Docstring
|
||||
Returns the list of CRSs supported by the geometry column
|
||||
%End
|
||||
|
||||
TableFlags flags() const;
|
||||
%Docstring
|
||||
Returns the table flags
|
||||
%End
|
||||
|
||||
void setFlags( const TableFlags &flags );
|
||||
%Docstring
|
||||
Sets the table ``flags``
|
||||
%End
|
||||
|
||||
QString comment() const;
|
||||
%Docstring
|
||||
Returns the table comment
|
||||
%End
|
||||
|
||||
void setComment( const QString &comment );
|
||||
%Docstring
|
||||
Sets the table ``comment``
|
||||
%End
|
||||
|
||||
QVariantMap info() const;
|
||||
%Docstring
|
||||
Returns additional information about the table
|
||||
|
||||
Provider classes may use this property
|
||||
to store custom bits of information.
|
||||
%End
|
||||
|
||||
void setInfo( const QVariantMap &info );
|
||||
%Docstring
|
||||
Sets additional information about the table to ``info``
|
||||
|
||||
Provider classes may use this property
|
||||
to store custom bits of information.
|
||||
%End
|
||||
|
||||
int geometryColumnCount() const;
|
||||
%Docstring
|
||||
Returns the number of geometry columns in the original table this entry refers to
|
||||
|
||||
This information is used internally to build the :py:func:`defaultName`
|
||||
%End
|
||||
|
||||
void setGeometryColumnCount( int geometryColumnCount );
|
||||
%Docstring
|
||||
Sets the ``geometryColumnCount``
|
||||
%End
|
||||
|
||||
void setFlag( const TableFlag &flag );
|
||||
%Docstring
|
||||
Sets a ``flag``
|
||||
%End
|
||||
|
||||
int maxCoordinateDimensions() const;
|
||||
%Docstring
|
||||
Returns the maximum coordinate dimensions of the geometries of a vector table.
|
||||
This information is calculated from the geometry columns types.
|
||||
|
||||
.. seealso:: :py:func:`geometryColumnTypes`
|
||||
%End
|
||||
|
||||
|
||||
};
|
||||
|
||||
enum Capability
|
||||
{
|
||||
CreateVectorTable,
|
||||
DropRasterTable,
|
||||
DropVectorTable,
|
||||
RenameVectorTable,
|
||||
RenameRasterTable,
|
||||
CreateSchema,
|
||||
DropSchema,
|
||||
RenameSchema,
|
||||
ExecuteSql,
|
||||
Vacuum,
|
||||
Tables,
|
||||
Schemas,
|
||||
SqlLayers,
|
||||
TableExists,
|
||||
Spatial,
|
||||
};
|
||||
|
||||
typedef QFlags<QgsAbstractDatabaseProviderConnection::Capability> Capabilities;
|
||||
|
||||
|
||||
QgsAbstractDatabaseProviderConnection( const QString &name );
|
||||
%Docstring
|
||||
Creates a new connection with ``name`` by reading its configuration from the settings.
|
||||
If a connection with this name cannot be found, an empty connection will be returned.
|
||||
%End
|
||||
|
||||
|
||||
QgsAbstractDatabaseProviderConnection( const QString &name, const QString &uri );
|
||||
%Docstring
|
||||
Creates a new connection with ``name`` and initializes the connection from the ``uri``.
|
||||
The connection is not automatically stored in the settings.
|
||||
|
||||
.. seealso:: :py:func:`store`
|
||||
%End
|
||||
|
||||
|
||||
Capabilities capabilities() const;
|
||||
%Docstring
|
||||
Returns connection capabilities
|
||||
%End
|
||||
|
||||
|
||||
virtual void createVectorTable( const QString &schema, const QString &name, const QgsFields &fields, QgsWkbTypes::Type wkbType, const QgsCoordinateReferenceSystem &srs, bool overwrite, const QMap<QString, QVariant> *options ) const throw( QgsProviderConnectionException );
|
||||
%Docstring
|
||||
Creates an empty table with ``name`` in the given ``schema`` (schema is ignored if not supported by the backend).
|
||||
Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
|
||||
:raises :: py:class:`QgsProviderConnectionException`
|
||||
%End
|
||||
|
||||
virtual bool tableExists( const QString &schema, const QString &name ) const throw( QgsProviderConnectionException );
|
||||
%Docstring
|
||||
Checks whether a table ``name`` exists in the given ``schema``.
|
||||
Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
|
||||
:raises :: py:class:`QgsProviderConnectionException`
|
||||
%End
|
||||
|
||||
virtual void dropVectorTable( const QString &schema, const QString &name ) const throw( QgsProviderConnectionException );
|
||||
%Docstring
|
||||
Drops a vector (or aspatial) table with given ``schema`` (schema is ignored if not supported by the backend) and ``name``.
|
||||
Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
|
||||
.. note::
|
||||
|
||||
it is responsibility of the caller to handle open layers and registry entries.
|
||||
|
||||
:raises :: py:class:`QgsProviderConnectionException`
|
||||
%End
|
||||
|
||||
virtual void dropRasterTable( const QString &schema, const QString &name ) const throw( QgsProviderConnectionException );
|
||||
%Docstring
|
||||
Drops a raster table with given ``schema`` (schema is ignored if not supported by the backend) and ``name``.
|
||||
Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
|
||||
.. note::
|
||||
|
||||
it is responsibility of the caller to handle open layers and registry entries.
|
||||
|
||||
:raises :: py:class:`QgsProviderConnectionException`
|
||||
%End
|
||||
|
||||
virtual void renameVectorTable( const QString &schema, const QString &name, const QString &newName ) const throw( QgsProviderConnectionException );
|
||||
%Docstring
|
||||
Renames a vector or aspatial table with given ``schema`` (schema is ignored if not supported by the backend) and ``name``.
|
||||
Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
|
||||
.. note::
|
||||
|
||||
it is responsibility of the caller to handle open layers and registry entries.
|
||||
|
||||
:raises :: py:class:`QgsProviderConnectionException`
|
||||
%End
|
||||
|
||||
virtual void renameRasterTable( const QString &schema, const QString &name, const QString &newName ) const throw( QgsProviderConnectionException );
|
||||
%Docstring
|
||||
Renames a raster table with given ``schema`` (schema is ignored if not supported by the backend) and ``name``.
|
||||
Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
|
||||
.. note::
|
||||
|
||||
it is responsibility of the caller to handle open layers and registry entries.
|
||||
|
||||
:raises :: py:class:`QgsProviderConnectionException`
|
||||
%End
|
||||
|
||||
virtual void createSchema( const QString &name ) const throw( QgsProviderConnectionException );
|
||||
%Docstring
|
||||
Creates a new schema with the specified ``name``
|
||||
|
||||
:raises :: py:class:`QgsProviderConnectionException`
|
||||
%End
|
||||
|
||||
virtual void dropSchema( const QString &name, bool force = false ) const throw( QgsProviderConnectionException );
|
||||
%Docstring
|
||||
Drops an entire schema with the specified name.
|
||||
Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
|
||||
:param name: name of the schema to be dropped
|
||||
:param force: if ``True``, a DROP CASCADE will drop all related objects
|
||||
|
||||
.. note::
|
||||
|
||||
it is responsibility of the caller to handle open layers and registry entries.
|
||||
|
||||
:raises :: py:class:`QgsProviderConnectionException`
|
||||
%End
|
||||
|
||||
virtual void renameSchema( const QString &name, const QString &newName ) const throw( QgsProviderConnectionException );
|
||||
%Docstring
|
||||
Renames a schema with the specified ``name``.
|
||||
Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
|
||||
.. note::
|
||||
|
||||
it is responsibility of the caller to handle open layers and registry entries.
|
||||
|
||||
:raises :: py:class:`QgsProviderConnectionException`
|
||||
%End
|
||||
|
||||
virtual QList<QList<QVariant>> executeSql( const QString &sql ) const throw( QgsProviderConnectionException );
|
||||
%Docstring
|
||||
Executes raw ``sql`` and returns the (possibly empty) list of results in a multi-dimensional array.
|
||||
Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
|
||||
:raises :: py:class:`QgsProviderConnectionException`
|
||||
%End
|
||||
|
||||
virtual void vacuum( const QString &schema, const QString &name ) const throw( QgsProviderConnectionException );
|
||||
%Docstring
|
||||
Vacuum the database table with given ``schema`` and ``name`` (schema is ignored if not supported by the backend).
|
||||
Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
|
||||
:raises :: py:class:`QgsProviderConnectionException`
|
||||
%End
|
||||
|
||||
|
||||
QList<QgsAbstractDatabaseProviderConnection::TableProperty> tablesInt( const QString &schema = QString(), const int flags = 0 ) const throw( QgsProviderConnectionException ) /PyName=tables/;
|
||||
%Docstring
|
||||
Returns information on the tables in the given schema.
|
||||
Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
|
||||
:param schema: name of the schema (ignored if not supported by the backend)
|
||||
:param flags: filter tables by flags, this option completely overrides search options stored in the connection
|
||||
|
||||
:raises :: py:class:`QgsProviderConnectionException`
|
||||
%End
|
||||
|
||||
|
||||
|
||||
virtual QStringList schemas( ) const throw( QgsProviderConnectionException );
|
||||
%Docstring
|
||||
Returns information about the existing schemas.
|
||||
Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
|
||||
:raises :: py:class:`QgsProviderConnectionException`
|
||||
%End
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
void checkCapability( Capability capability ) const;
|
||||
%Docstring
|
||||
Checks if ``capability`` is supported and throws and exception if it's not
|
||||
|
||||
:raises :: py:class:`QgsProviderConnectionException`
|
||||
%End
|
||||
|
||||
|
||||
};
|
||||
|
||||
QFlags<QgsAbstractDatabaseProviderConnection::Capability> operator|(QgsAbstractDatabaseProviderConnection::Capability f1, QFlags<QgsAbstractDatabaseProviderConnection::Capability> f2);
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/qgsabstractdatabaseproviderconnection.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||
************************************************************************/
|
@ -0,0 +1,97 @@
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/qgsabstractproviderconnection.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||
************************************************************************/
|
||||
|
||||
|
||||
|
||||
class QgsAbstractProviderConnection
|
||||
{
|
||||
%Docstring
|
||||
The QgsAbstractProviderConnection provides an interface for data provider connections.
|
||||
|
||||
Connections objects can be created by passing the connection name and in this case
|
||||
they are automatically loaded from the settings, or by passing a data source URI
|
||||
in the constructor.
|
||||
|
||||
Concrete classes must implement methods to retrieve, save and remove connections from
|
||||
the settings.
|
||||
|
||||
.. versionadded:: 3.10
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgsabstractproviderconnection.h"
|
||||
%End
|
||||
%ConvertToSubClassCode
|
||||
if ( dynamic_cast<QgsAbstractDatabaseProviderConnection *>( sipCpp ) != NULL )
|
||||
{
|
||||
sipType = sipType_QgsAbstractDatabaseProviderConnection;
|
||||
}
|
||||
else if ( dynamic_cast<QgsAbstractProviderConnection *>( sipCpp ) != NULL )
|
||||
{
|
||||
sipType = sipType_QgsAbstractProviderConnection;
|
||||
}
|
||||
else
|
||||
{
|
||||
sipType = 0;
|
||||
}
|
||||
%End
|
||||
public:
|
||||
|
||||
QgsAbstractProviderConnection( const QString &name );
|
||||
%Docstring
|
||||
Creates a new connection with ``name`` by reading its configuration from the settings.
|
||||
If a connection with this name cannot be found, an empty connection will be returned.
|
||||
%End
|
||||
|
||||
QgsAbstractProviderConnection( const QString &name, const QString &uri );
|
||||
%Docstring
|
||||
Creates a new connection with ``name`` and initializes the connection from the ``uri``.
|
||||
The connection is not automatically stored in the settings.
|
||||
|
||||
.. seealso:: :py:func:`store`
|
||||
%End
|
||||
|
||||
virtual ~QgsAbstractProviderConnection();
|
||||
|
||||
virtual void store( const QVariantMap &configuration = QVariantMap() ) const = 0;
|
||||
%Docstring
|
||||
Stores the connection in the settings.
|
||||
|
||||
:param configuration: stores additional connection settings that are used by the
|
||||
source select dialog and are not part of the data source URI
|
||||
%End
|
||||
|
||||
virtual void remove( ) const = 0;
|
||||
%Docstring
|
||||
Deletes the connection from the settings.
|
||||
%End
|
||||
|
||||
QString name() const;
|
||||
%Docstring
|
||||
Returns the connection name
|
||||
%End
|
||||
|
||||
QString uri() const;
|
||||
%Docstring
|
||||
Returns the connection data source URI string representation
|
||||
%End
|
||||
|
||||
void setUri( const QString &uri );
|
||||
%Docstring
|
||||
Sets the connection data source URI to ``uri``
|
||||
%End
|
||||
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/qgsabstractproviderconnection.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||
************************************************************************/
|
@ -320,6 +320,20 @@ Decodes SSL mode string into enum value. If the string is not recognized, SslPre
|
||||
Encodes SSL mode enum value into a string.
|
||||
|
||||
.. versionadded:: 3.2
|
||||
%End
|
||||
|
||||
void setTable( const QString &table );
|
||||
%Docstring
|
||||
Sets table to ``table``
|
||||
|
||||
.. versionadded:: 3.10
|
||||
%End
|
||||
|
||||
void setGeometryColumn( const QString &geometryColumn );
|
||||
%Docstring
|
||||
Sets geometry column name to ``geometryColumn``
|
||||
|
||||
.. versionadded:: 3.10
|
||||
%End
|
||||
|
||||
};
|
||||
|
@ -218,6 +218,91 @@ Returns new instance of transaction. Ownership is transferred to the caller
|
||||
.. versionadded:: 3.10
|
||||
%End
|
||||
|
||||
virtual QMap<QString, QgsAbstractProviderConnection *> connections( bool cached = true ) throw( QgsProviderConnectionException );
|
||||
%Docstring
|
||||
Returns a dictionary of stored provider connections,
|
||||
the dictionary key is the connection identifier.
|
||||
Ownership is not transferred.
|
||||
Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
|
||||
:param cached: if ``False`` connections will be re-read from the settings
|
||||
|
||||
:raises :: py:class:`QgsProviderConnectionException`
|
||||
|
||||
.. versionadded:: 3.10
|
||||
%End
|
||||
|
||||
QMap<QString, QgsAbstractDatabaseProviderConnection *> dbConnections( bool cached = true ) throw( QgsProviderConnectionException );
|
||||
%Docstring
|
||||
Returns a dictionary of database provider connections,
|
||||
the dictionary key is the connection identifier.
|
||||
Ownership is not transferred.
|
||||
Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
|
||||
:param cached: if ``False`` connections will be re-read from the settings
|
||||
|
||||
:raises :: py:class:`QgsProviderConnectionException`
|
||||
|
||||
.. versionadded:: 3.10
|
||||
%End
|
||||
|
||||
QgsAbstractProviderConnection *findConnection( const QString &name, bool cached = true ) throw( QgsProviderConnectionException );
|
||||
%Docstring
|
||||
Searches and returns a (possibly NULL) connection from the stored provider connections.
|
||||
Ownership is not transferred.
|
||||
Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
|
||||
:param name: the connection name
|
||||
:param cached: if ``False`` connections will be re-read from the settings
|
||||
|
||||
:raises :: py:class:`QgsProviderConnectionException`
|
||||
|
||||
.. versionadded:: 3.10
|
||||
%End
|
||||
|
||||
|
||||
virtual QgsAbstractProviderConnection *createConnection( const QString &name ) /Factory/;
|
||||
%Docstring
|
||||
Creates a new connection by loading the connection with the given ``name`` from the settings.
|
||||
Ownership is transferred to the caller.
|
||||
|
||||
.. versionadded:: 3.10
|
||||
%End
|
||||
|
||||
virtual QgsAbstractProviderConnection *createConnection( const QString &name, const QString &uri ) /Factory/;
|
||||
%Docstring
|
||||
Creates a new connection with the given ``name`` and data source ``uri``,
|
||||
the newly created connection is not automatically stored in the settings, call
|
||||
saveConnection() to save it.
|
||||
Ownership is transferred to the caller.
|
||||
|
||||
.. seealso:: :py:func:`saveConnection`
|
||||
|
||||
.. versionadded:: 3.10
|
||||
%End
|
||||
|
||||
virtual void deleteConnection( const QString &name ) throw( QgsProviderConnectionException );
|
||||
%Docstring
|
||||
Removes the connection with the given ``name`` from the settings.
|
||||
Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
|
||||
:raises :: py:class:`QgsProviderConnectionException`
|
||||
|
||||
.. versionadded:: 3.10
|
||||
%End
|
||||
|
||||
virtual void saveConnection( QgsAbstractProviderConnection *connection, const QVariantMap &configuration = QVariantMap() );
|
||||
%Docstring
|
||||
Stores the connection in the settings
|
||||
|
||||
:param connection: the connection to be stored in the settings
|
||||
:param configuration: stores additional connection settings that not part of the data source URI
|
||||
|
||||
.. versionadded:: 3.10
|
||||
%End
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
@ -250,7 +250,7 @@ be returned.
|
||||
Returns list of available providers by their keys
|
||||
%End
|
||||
|
||||
const QgsProviderMetadata *providerMetadata( const QString &providerKey ) const;
|
||||
QgsProviderMetadata *providerMetadata( const QString &providerKey ) const;
|
||||
%Docstring
|
||||
Returns metadata of the provider or ``None`` if not found
|
||||
%End
|
||||
|
@ -6,6 +6,8 @@
|
||||
%Include auto_generated/expression/qgsexpressionfunction.sip
|
||||
%Include auto_generated/qgstessellator.sip
|
||||
%Include auto_generated/qgis.sip
|
||||
%Include auto_generated/qgsabstractproviderconnection.sip
|
||||
%Include auto_generated/qgsabstractdatabaseproviderconnection.sip
|
||||
%Include auto_generated/qgsaction.sip
|
||||
%Include auto_generated/qgsactionscope.sip
|
||||
%Include auto_generated/qgsactionmanager.sip
|
||||
|
@ -33,3 +33,16 @@
|
||||
SIP_UNBLOCK_THREADS
|
||||
%End
|
||||
};
|
||||
|
||||
|
||||
%Exception QgsProviderConnectionException(SIP_Exception) /PyName=QgsProviderConnectionException/
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <qgsexception.h>
|
||||
%End
|
||||
%RaiseCode
|
||||
SIP_BLOCK_THREADS
|
||||
PyErr_SetString(sipException_QgsProviderConnectionException, sipExceptionRef.what().toUtf8().constData() );
|
||||
SIP_UNBLOCK_THREADS
|
||||
%End
|
||||
};
|
||||
|
@ -31,7 +31,18 @@ from .db_plugins import supportedDbTypes, createDbPlugin
|
||||
from .db_plugins.plugin import BaseError, Table, Database
|
||||
from .dlg_db_error import DlgDbError
|
||||
|
||||
from qgis.core import QgsApplication, QgsDataSourceUri, QgsVectorLayer, QgsRasterLayer, QgsMimeDataUtils
|
||||
from qgis.core import (
|
||||
QgsApplication,
|
||||
QgsDataSourceUri,
|
||||
QgsVectorLayer,
|
||||
QgsRasterLayer,
|
||||
QgsMimeDataUtils,
|
||||
QgsProviderConnectionException,
|
||||
QgsProviderRegistry,
|
||||
QgsAbstractDatabaseProviderConnection,
|
||||
QgsMessageLog,
|
||||
)
|
||||
|
||||
from qgis.utils import OverrideCursor
|
||||
|
||||
from . import resources_rc # NOQA
|
||||
|
@ -30,6 +30,9 @@ from .plugin import DbError, ConnectionError
|
||||
class DBConnector(object):
|
||||
|
||||
def __init__(self, uri):
|
||||
"""Creates a new DB connector
|
||||
"""
|
||||
|
||||
self.connection = None
|
||||
self._uri = uri
|
||||
|
||||
|
@ -31,7 +31,13 @@ from ..connector import DBConnector
|
||||
from ..plugin import ConnectionError, DbError, Table
|
||||
|
||||
from qgis.utils import spatialite_connect
|
||||
from qgis.core import QgsApplication
|
||||
from qgis.core import (
|
||||
QgsApplication,
|
||||
QgsProviderRegistry,
|
||||
QgsAbstractDatabaseProviderConnection,
|
||||
QgsProviderConnectionException,
|
||||
QgsWkbTypes,
|
||||
)
|
||||
|
||||
import sqlite3
|
||||
|
||||
@ -44,12 +50,26 @@ def classFactory():
|
||||
|
||||
class GPKGDBConnector(DBConnector):
|
||||
|
||||
def __init__(self, uri):
|
||||
DBConnector.__init__(self, uri)
|
||||
def __init__(self, uri, connection):
|
||||
"""Creates a new GPKG connector
|
||||
|
||||
:param uri: data source URI
|
||||
:type uri: QgsDataSourceUri
|
||||
:param connection: the GPKGDBPlugin parent instance
|
||||
:type connection: GPKGDBPlugin
|
||||
"""
|
||||
|
||||
DBConnector.__init__(self, uri)
|
||||
self.dbname = uri.database()
|
||||
self.connection = connection
|
||||
md = QgsProviderRegistry.instance().providerMetadata(connection.providerName())
|
||||
# QgsAbstractDatabaseProviderConnection instance
|
||||
self.core_connection = md.findConnection(connection.connectionName())
|
||||
if self.core_connection is None:
|
||||
self.core_connection = md.createConnection(connection.connectionName(), uri.database())
|
||||
self.has_raster = False
|
||||
self.mapSridToName = {}
|
||||
# To be removed when migration to new API is completed
|
||||
self._opendb()
|
||||
|
||||
def _opendb(self):
|
||||
@ -102,40 +122,12 @@ class GPKGDBConnector(DBConnector):
|
||||
return unquoted
|
||||
|
||||
def _fetchOne(self, sql):
|
||||
sql_lyr = self.gdal_ds.ExecuteSQL(sql)
|
||||
if sql_lyr is None:
|
||||
return None
|
||||
f = sql_lyr.GetNextFeature()
|
||||
if f is None:
|
||||
ret = None
|
||||
else:
|
||||
ret = [f.GetField(i) for i in range(f.GetFieldCount())]
|
||||
self.gdal_ds.ReleaseResultSet(sql_lyr)
|
||||
return ret
|
||||
|
||||
return self.core_connection.executeSql(sql)
|
||||
|
||||
def _fetchAll(self, sql, include_fid_and_geometry=False):
|
||||
sql_lyr = self.gdal_ds.ExecuteSQL(sql)
|
||||
if sql_lyr is None:
|
||||
return None
|
||||
ret = []
|
||||
while True:
|
||||
f = sql_lyr.GetNextFeature()
|
||||
if f is None:
|
||||
break
|
||||
else:
|
||||
if include_fid_and_geometry:
|
||||
field_vals = [f.GetFID()]
|
||||
if sql_lyr.GetLayerDefn().GetGeomType() != ogr.wkbNone:
|
||||
geom = f.GetGeometryRef()
|
||||
if geom is not None:
|
||||
geom = geom.ExportToWkt()
|
||||
field_vals += [geom]
|
||||
field_vals += [f.GetField(i) for i in range(f.GetFieldCount())]
|
||||
ret.append(field_vals)
|
||||
else:
|
||||
ret.append([f.GetField(i) for i in range(f.GetFieldCount())])
|
||||
self.gdal_ds.ReleaseResultSet(sql_lyr)
|
||||
return ret
|
||||
|
||||
return self.core_connection.executeSql(sql)
|
||||
|
||||
def _fetchAllFromLayer(self, table):
|
||||
|
||||
@ -286,78 +278,66 @@ class GPKGDBConnector(DBConnector):
|
||||
return sorted(items, key=cmp_to_key(lambda x, y: (x[1] > y[1]) - (x[1] < y[1])))
|
||||
|
||||
def getVectorTables(self, schema=None):
|
||||
"""Returns a list of vector table information
|
||||
"""
|
||||
|
||||
items = []
|
||||
for i in range(self.gdal_ds.GetLayerCount()):
|
||||
lyr = self.gdal_ds.GetLayer(i)
|
||||
geomtype = lyr.GetGeomType()
|
||||
if hasattr(ogr, 'GT_Flatten'):
|
||||
geomtype_flatten = ogr.GT_Flatten(geomtype)
|
||||
for table in self.core_connection.tables(schema, QgsAbstractDatabaseProviderConnection.Vector | QgsAbstractDatabaseProviderConnection.Aspatial):
|
||||
if not (table.flags() & QgsAbstractDatabaseProviderConnection.Aspatial):
|
||||
geom_type = table.geometryColumnTypes()[0]
|
||||
# Use integer PG code for SRID
|
||||
srid = geom_type.crs.postgisSrid()
|
||||
geomtype_flatten = QgsWkbTypes.flatType(geom_type.wkbType)
|
||||
geomname = 'GEOMETRY'
|
||||
if geomtype_flatten == QgsWkbTypes.Point:
|
||||
geomname = 'POINT'
|
||||
elif geomtype_flatten == QgsWkbTypes.LineString:
|
||||
geomname = 'LINESTRING'
|
||||
elif geomtype_flatten == QgsWkbTypes.Polygon:
|
||||
geomname = 'POLYGON'
|
||||
elif geomtype_flatten == QgsWkbTypes.MultiPoint:
|
||||
geomname = 'MULTIPOINT'
|
||||
elif geomtype_flatten == QgsWkbTypes.MultiLineString:
|
||||
geomname = 'MULTILINESTRING'
|
||||
elif geomtype_flatten == QgsWkbTypes.MultiPolygon:
|
||||
geomname = 'MULTIPOLYGON'
|
||||
elif geomtype_flatten == QgsWkbTypes.GeometryCollection:
|
||||
geomname = 'GEOMETRYCOLLECTION'
|
||||
elif geomtype_flatten == QgsWkbTypes.CircularString:
|
||||
geomname = 'CIRCULARSTRING'
|
||||
elif geomtype_flatten == QgsWkbTypes.CompoundCurve:
|
||||
geomname = 'COMPOUNDCURVE'
|
||||
elif geomtype_flatten == QgsWkbTypes.CurvePolygon:
|
||||
geomname = 'CURVEPOLYGON'
|
||||
elif geomtype_flatten == QgsWkbTypes.MultiCurve:
|
||||
geomname = 'MULTICURVE'
|
||||
elif geomtype_flatten == QgsWkbTypes.MultiSurface:
|
||||
geomname = 'MULTISURFACE'
|
||||
geomdim = 'XY'
|
||||
if QgsWkbTypes.hasZ(geom_type.wkbType):
|
||||
geomdim += 'Z'
|
||||
if QgsWkbTypes.hasM(geom_type.wkbType):
|
||||
geomdim += 'M'
|
||||
item = [
|
||||
Table.VectorType,
|
||||
table.tableName(),
|
||||
bool(table.flags() & QgsAbstractDatabaseProviderConnection.View), # is_view
|
||||
table.tableName(),
|
||||
table.geometryColumn(),
|
||||
geomname,
|
||||
geomdim,
|
||||
srid
|
||||
]
|
||||
self.mapSridToName[srid] = geom_type.crs.description()
|
||||
else:
|
||||
geomtype_flatten = geomtype
|
||||
geomname = 'GEOMETRY'
|
||||
if geomtype_flatten == ogr.wkbPoint:
|
||||
geomname = 'POINT'
|
||||
elif geomtype_flatten == ogr.wkbLineString:
|
||||
geomname = 'LINESTRING'
|
||||
elif geomtype_flatten == ogr.wkbPolygon:
|
||||
geomname = 'POLYGON'
|
||||
elif geomtype_flatten == ogr.wkbMultiPoint:
|
||||
geomname = 'MULTIPOINT'
|
||||
elif geomtype_flatten == ogr.wkbMultiLineString:
|
||||
geomname = 'MULTILINESTRING'
|
||||
elif geomtype_flatten == ogr.wkbMultiPolygon:
|
||||
geomname = 'MULTIPOLYGON'
|
||||
elif geomtype_flatten == ogr.wkbGeometryCollection:
|
||||
geomname = 'GEOMETRYCOLLECTION'
|
||||
elif geomtype_flatten == ogr.wkbCircularString:
|
||||
geomname = 'CIRCULARSTRING'
|
||||
elif geomtype_flatten == ogr.wkbCompoundCurve:
|
||||
geomname = 'COMPOUNDCURVE'
|
||||
elif geomtype_flatten == ogr.wkbCurvePolygon:
|
||||
geomname = 'CURVEPOLYGON'
|
||||
elif geomtype_flatten == ogr.wkbMultiCurve:
|
||||
geomname = 'MULTICURVE'
|
||||
elif geomtype_flatten == ogr.wkbMultiSurface:
|
||||
geomname = 'MULTISURFACE'
|
||||
geomdim = 'XY'
|
||||
if hasattr(ogr, 'GT_HasZ') and ogr.GT_HasZ(lyr.GetGeomType()):
|
||||
geomdim += 'Z'
|
||||
if hasattr(ogr, 'GT_HasM') and ogr.GT_HasM(lyr.GetGeomType()):
|
||||
geomdim += 'M'
|
||||
srs = lyr.GetSpatialRef()
|
||||
srid = None
|
||||
if srs is not None:
|
||||
if srs.IsProjected():
|
||||
name = srs.GetAttrValue('PROJCS', 0)
|
||||
elif srs.IsGeographic():
|
||||
name = srs.GetAttrValue('GEOGCS', 0)
|
||||
else:
|
||||
name = None
|
||||
srid = srs.GetAuthorityCode(None)
|
||||
if srid is not None:
|
||||
srid = int(srid)
|
||||
else:
|
||||
srid = self._fetchOne('SELECT srid FROM gpkg_spatial_ref_sys WHERE table_name = %s' % self.quoteString(lyr.GetName()))
|
||||
if srid is not None:
|
||||
srid = int(srid)
|
||||
self.mapSridToName[srid] = name
|
||||
item = [
|
||||
Table.TableType,
|
||||
table.tableName(),
|
||||
bool(table.flags() & QgsAbstractDatabaseProviderConnection.View),
|
||||
]
|
||||
|
||||
if geomtype == ogr.wkbNone:
|
||||
item = list([Table.TableType,
|
||||
lyr.GetName(),
|
||||
False, # is_view
|
||||
])
|
||||
else:
|
||||
item = list([Table.VectorType,
|
||||
lyr.GetName(),
|
||||
False, # is_view
|
||||
lyr.GetName(),
|
||||
lyr.GetGeometryColumn(),
|
||||
geomname,
|
||||
geomdim,
|
||||
srid])
|
||||
items.append(item)
|
||||
|
||||
return items
|
||||
|
||||
def getRasterTables(self, schema=None):
|
||||
@ -371,15 +351,22 @@ class GPKGDBConnector(DBConnector):
|
||||
srid
|
||||
"""
|
||||
|
||||
sql = u"""SELECT table_name, 0 AS is_view, table_name AS r_table_name, '' AS r_geometry_column, srs_id FROM gpkg_contents WHERE data_type = 'tiles'"""
|
||||
ret = self._fetchAll(sql)
|
||||
if ret is None:
|
||||
return []
|
||||
items = []
|
||||
for i, tbl in enumerate(ret):
|
||||
item = list(tbl)
|
||||
item.insert(0, Table.RasterType)
|
||||
for table in self.core_connection.tables(schema, QgsAbstractDatabaseProviderConnection.Raster):
|
||||
geom_type = table.geometryColumnTypes()[0]
|
||||
# Use integer PG code for SRID
|
||||
srid = geom_type.crs.postgisSrid()
|
||||
item = [
|
||||
Table.RasterType,
|
||||
table.tableName(),
|
||||
bool(table.flags() & QgsAbstractDatabaseProviderConnection.View),
|
||||
table.tableName(),
|
||||
table.geometryColumn(),
|
||||
srid,
|
||||
]
|
||||
self.mapSridToName[srid] = geom_type.crs.description()
|
||||
items.append(item)
|
||||
|
||||
return items
|
||||
|
||||
def getTableRowCount(self, table):
|
||||
@ -441,7 +428,7 @@ class GPKGDBConnector(DBConnector):
|
||||
return self._fetchAll(sql)
|
||||
|
||||
def deleteTableTrigger(self, trigger, table=None):
|
||||
""" delete trigger """
|
||||
"""Deletes trigger """
|
||||
sql = u"DROP TRIGGER %s" % self.quoteId(trigger)
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
@ -486,7 +473,7 @@ class GPKGDBConnector(DBConnector):
|
||||
|
||||
sql = u"SELECT srs_name FROM gpkg_spatial_ref_sys WHERE srs_id = %s" % self.quoteString(srid)
|
||||
res = self._fetchOne(sql)
|
||||
if res is not None:
|
||||
if res is not None and len(res) > 0:
|
||||
res = res[0]
|
||||
self.mapSridToName[srid] = res
|
||||
return res
|
||||
@ -503,7 +490,7 @@ class GPKGDBConnector(DBConnector):
|
||||
if md is None or len(md) == 0:
|
||||
sql = u"SELECT COUNT(*) FROM gpkg_contents WHERE data_type = 'tiles' AND table_name = %s" % self.quoteString(tablename)
|
||||
ret = self._fetchOne(sql)
|
||||
return ret is not None and ret[0] == 1
|
||||
return ret != [] and ret[0][0] == 1
|
||||
else:
|
||||
subdataset_name = 'GPKG:%s:%s' % (self.gdal_ds.GetDescription(), tablename)
|
||||
for key in md:
|
||||
@ -572,7 +559,7 @@ class GPKGDBConnector(DBConnector):
|
||||
return fld_defn
|
||||
|
||||
def createTable(self, table, field_defs, pkey):
|
||||
""" create ordinary table
|
||||
"""Creates ordinary table
|
||||
'fields' is array containing field definitions
|
||||
'pkey' is the primary key name
|
||||
"""
|
||||
@ -596,7 +583,7 @@ class GPKGDBConnector(DBConnector):
|
||||
return True
|
||||
|
||||
def deleteTable(self, table):
|
||||
""" delete table from the database """
|
||||
"""Deletes table from the database """
|
||||
if self.isRasterTable(table):
|
||||
return False
|
||||
|
||||
@ -607,7 +594,7 @@ class GPKGDBConnector(DBConnector):
|
||||
return False
|
||||
|
||||
def emptyTable(self, table):
|
||||
""" delete all rows from table """
|
||||
"""Deletes all rows from table """
|
||||
if self.isRasterTable(table):
|
||||
return False
|
||||
|
||||
@ -624,17 +611,16 @@ class GPKGDBConnector(DBConnector):
|
||||
:return: true on success
|
||||
:rtype: bool
|
||||
"""
|
||||
|
||||
table_name = table[1]
|
||||
provider = [p for p in QgsApplication.dataItemProviderRegistry().providers() if p.name() == 'OGR'][0]
|
||||
collection_item = provider.createDataItem(self.dbname, None)
|
||||
data_item = [c for c in collection_item.createChildren() if c.name() == table_name][0]
|
||||
result = data_item.rename(new_table)
|
||||
# we need to reopen after renaming since OGR doesn't update its
|
||||
# internal state
|
||||
if result:
|
||||
self._opendb()
|
||||
return result
|
||||
try:
|
||||
name = table[1] # 0 is schema
|
||||
vector_table_names = [t.tableName() for t in self.core_connection.tables('', QgsAbstractDatabaseProviderConnection.Vector)]
|
||||
if name in vector_table_names:
|
||||
self.core_connection.renameVectorTable('', name, new_table)
|
||||
else:
|
||||
self.core_connection.renameRasterTable('', name, new_table)
|
||||
return True
|
||||
except QgsProviderConnectionException:
|
||||
return False
|
||||
|
||||
def moveTable(self, table, new_table, new_schema=None):
|
||||
return self.renameTable(table, new_table)
|
||||
@ -644,7 +630,7 @@ class GPKGDBConnector(DBConnector):
|
||||
self._execute_and_commit("VACUUM")
|
||||
|
||||
def addTableColumn(self, table, field_def):
|
||||
""" add a column to table """
|
||||
"""Adds a column to table """
|
||||
|
||||
_, tablename = self.getSchemaTableName(table)
|
||||
lyr = self.gdal_ds.GetLayerByName(tablename)
|
||||
@ -654,7 +640,7 @@ class GPKGDBConnector(DBConnector):
|
||||
return lyr.CreateField(fld_defn) == 0
|
||||
|
||||
def deleteTableColumn(self, table, column):
|
||||
""" delete column from a table """
|
||||
"""Deletes column from a table """
|
||||
if self.isGeometryColumn(table, column):
|
||||
return False
|
||||
|
||||
@ -772,20 +758,20 @@ class GPKGDBConnector(DBConnector):
|
||||
return False # not supported
|
||||
|
||||
def addTableUniqueConstraint(self, table, column):
|
||||
""" add a unique constraint to a table """
|
||||
"""Adds a unique constraint to a table """
|
||||
return False # constraints not supported
|
||||
|
||||
def deleteTableConstraint(self, table, constraint):
|
||||
""" delete constraint in a table """
|
||||
"""Deletes constraint in a table """
|
||||
return False # constraints not supported
|
||||
|
||||
def addTablePrimaryKey(self, table, column):
|
||||
""" add a primery key (with one column) to a table """
|
||||
"""Adds a primery key (with one column) to a table """
|
||||
sql = u"ALTER TABLE %s ADD PRIMARY KEY (%s)" % (self.quoteId(table), self.quoteId(column))
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def createTableIndex(self, table, name, column, unique=False):
|
||||
""" create index on one column using default options """
|
||||
"""Creates index on one column using default options """
|
||||
unique_str = u"UNIQUE" if unique else ""
|
||||
sql = u"CREATE %s INDEX %s ON %s (%s)" % (
|
||||
unique_str, self.quoteId(name), self.quoteId(table), self.quoteId(column))
|
||||
@ -802,8 +788,11 @@ class GPKGDBConnector(DBConnector):
|
||||
_, tablename = self.getSchemaTableName(table)
|
||||
sql = u"SELECT CreateSpatialIndex(%s, %s)" % (
|
||||
self.quoteId(tablename), self.quoteId(geom_column))
|
||||
res = self._fetchOne(sql)
|
||||
return res is not None and res[0] == 1
|
||||
try:
|
||||
res = self._fetchOne(sql)
|
||||
except QgsProviderConnectionException:
|
||||
return False
|
||||
return res is not None and res[0][0] == 1
|
||||
|
||||
def deleteSpatialIndex(self, table, geom_column):
|
||||
if self.isRasterTable(table):
|
||||
@ -812,7 +801,7 @@ class GPKGDBConnector(DBConnector):
|
||||
sql = u"SELECT DisableSpatialIndex(%s, %s)" % (
|
||||
self.quoteId(tablename), self.quoteId(geom_column))
|
||||
res = self._fetchOne(sql)
|
||||
return res is not None and res[0] == 1
|
||||
return len(res) > 0 and len(res[0]) > 0 and res[0][0] == 1
|
||||
|
||||
def hasSpatialIndex(self, table, geom_column):
|
||||
if self.isRasterTable(table) or geom_column is None:
|
||||
@ -825,14 +814,14 @@ class GPKGDBConnector(DBConnector):
|
||||
ret = self._fetchOne(sql)
|
||||
gdal.PopErrorHandler()
|
||||
|
||||
if ret is None:
|
||||
if len(ret) == 0:
|
||||
# might be the case for GDAL < 2.1.2
|
||||
sql = u"SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name LIKE %s" % self.quoteString("%%rtree_" + tablename + "_%%")
|
||||
ret = self._fetchOne(sql)
|
||||
if ret is None:
|
||||
if len(ret) == 0:
|
||||
return False
|
||||
else:
|
||||
return ret[0] >= 1
|
||||
return ret[0][0] >= 1
|
||||
|
||||
def execution_error_types(self):
|
||||
return sqlite3.Error, sqlite3.ProgrammingError, sqlite3.Warning
|
||||
|
@ -27,7 +27,13 @@ from .connector import GPKGDBConnector
|
||||
from qgis.PyQt.QtCore import Qt, QFileInfo, QCoreApplication
|
||||
from qgis.PyQt.QtGui import QIcon
|
||||
from qgis.PyQt.QtWidgets import QApplication, QAction, QFileDialog
|
||||
from qgis.core import Qgis, QgsApplication, QgsDataSourceUri, QgsSettings
|
||||
from qgis.core import (
|
||||
Qgis,
|
||||
QgsApplication,
|
||||
QgsDataSourceUri,
|
||||
QgsSettings,
|
||||
QgsProviderRegistry,
|
||||
)
|
||||
from qgis.gui import QgsMessageBar
|
||||
|
||||
from ..plugin import DBPlugin, Database, Table, VectorTable, RasterTable, TableField, TableIndex, TableTrigger, \
|
||||
@ -65,23 +71,22 @@ class GPKGDBPlugin(DBPlugin):
|
||||
|
||||
def connect(self, parent=None):
|
||||
conn_name = self.connectionName()
|
||||
settings = QgsSettings()
|
||||
settings.beginGroup(u"/%s/%s" % (self.connectionSettingsKey(), conn_name))
|
||||
|
||||
if not settings.contains("path"): # non-existent entry?
|
||||
md = QgsProviderRegistry.instance().providerMetadata(self.providerName())
|
||||
conn = md.findConnection(conn_name)
|
||||
|
||||
if conn is None: # non-existent entry?
|
||||
raise InvalidDataException(self.tr(u'There is no defined database connection "{0}".').format(conn_name))
|
||||
|
||||
database = settings.value("path")
|
||||
|
||||
uri = QgsDataSourceUri()
|
||||
uri.setDatabase(database)
|
||||
uri.setDatabase(conn.uri())
|
||||
return self.connectToUri(uri)
|
||||
|
||||
@classmethod
|
||||
def addConnection(self, conn_name, uri):
|
||||
settings = QgsSettings()
|
||||
settings.beginGroup(u"/%s/%s" % (self.connectionSettingsKey(), conn_name))
|
||||
settings.setValue("path", uri.database())
|
||||
md = QgsProviderRegistry.instance().providerMetadata(self.providerName())
|
||||
conn = md.createConnection(conn_name, uri.database())
|
||||
md.saveConnection(conn)
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
@ -108,7 +113,7 @@ class GPKGDatabase(Database):
|
||||
Database.__init__(self, connection, uri)
|
||||
|
||||
def connectorsFactory(self, uri):
|
||||
return GPKGDBConnector(uri)
|
||||
return GPKGDBConnector(uri, self.connection())
|
||||
|
||||
def dataTablesFactory(self, row, db, schema=None):
|
||||
return GPKGTable(row, db, schema)
|
||||
@ -184,6 +189,16 @@ class GPKGDatabase(Database):
|
||||
class GPKGTable(Table):
|
||||
|
||||
def __init__(self, row, db, schema=None):
|
||||
"""Constructs a GPKGTable
|
||||
|
||||
:param row: a three elements array with: [table_name, is_view, is_sys_table]
|
||||
:type row: array [str, bool, bool]
|
||||
:param db: database instance
|
||||
:type db:
|
||||
:param schema: schema name, defaults to None, ignored by GPKG
|
||||
:type schema: str, optional
|
||||
"""
|
||||
|
||||
Table.__init__(self, db, None)
|
||||
self.name, self.isView, self.isSysTable = row
|
||||
|
||||
|
@ -381,7 +381,7 @@ class VectorTableInfo(TableInfo):
|
||||
if self.table.geomDim:
|
||||
tbl.append((QApplication.translate("DBManagerPlugin", "Dimension:"), self.table.geomDim))
|
||||
|
||||
srid = self.table.srid if self.table.srid is not None else -1
|
||||
srid = self.table.srid if self.table.srid not in (None, 0) else -1
|
||||
sr_info = self.table.database().connector.getSpatialRefInfo(srid) if srid != -1 else QApplication.translate(
|
||||
"DBManagerPlugin", "Undefined")
|
||||
if sr_info:
|
||||
|
@ -426,7 +426,7 @@ class OracleDBConnector(DBConnector):
|
||||
return sorted(items, key=cmp_to_key(lambda x, y: (x[1] > y[1]) - (x[1] < y[1])))
|
||||
|
||||
def updateCache(self, tableList, schema=None):
|
||||
"""Update the SQLite cache of table list for a schema."""
|
||||
"""Updates the SQLite cache of table list for a schema."""
|
||||
|
||||
data = []
|
||||
# First, we treat the list
|
||||
@ -1017,7 +1017,7 @@ class OracleDBConnector(DBConnector):
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def deleteTableTrigger(self, trigger, table):
|
||||
"""Delete the trigger on a table."""
|
||||
"""Deletes the trigger on a table."""
|
||||
schema, tablename = self.getSchemaTableName(table)
|
||||
trigger = u".".join([self.quoteId(schema), self.quoteId(trigger)])
|
||||
sql = u"DROP TRIGGER {}".format(trigger)
|
||||
@ -1194,7 +1194,7 @@ class OracleDBConnector(DBConnector):
|
||||
return False
|
||||
|
||||
def createTable(self, table, field_defs, pkey):
|
||||
"""Create ordinary table
|
||||
"""Creates ordinary table
|
||||
'fields' is array containing field definitions
|
||||
'pkey' is the primary key name
|
||||
"""
|
||||
@ -1211,7 +1211,7 @@ class OracleDBConnector(DBConnector):
|
||||
return True
|
||||
|
||||
def deleteTable(self, table):
|
||||
"""Delete table and its reference in sdo_geom_metadata."""
|
||||
"""Deletes table and its reference in sdo_geom_metadata."""
|
||||
|
||||
schema, tablename = self.getSchemaTableName(table)
|
||||
|
||||
@ -1222,13 +1222,13 @@ class OracleDBConnector(DBConnector):
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def emptyTable(self, table):
|
||||
"""Delete all the rows of a table."""
|
||||
"""Deletes all the rows of a table."""
|
||||
|
||||
sql = u"TRUNCATE TABLE {}".format(self.quoteId(table))
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def renameTable(self, table, new_table):
|
||||
"""Rename a table inside the database."""
|
||||
"""Renames a table inside the database."""
|
||||
schema, tablename = self.getSchemaTableName(table)
|
||||
if new_table == tablename:
|
||||
return
|
||||
@ -1246,7 +1246,7 @@ class OracleDBConnector(DBConnector):
|
||||
self._commit()
|
||||
|
||||
def createView(self, view, query):
|
||||
"""Create a view as defined."""
|
||||
"""Creates a view as defined."""
|
||||
sql = u"CREATE VIEW {0} AS {1}".format(self.quoteId(view),
|
||||
query)
|
||||
self._execute_and_commit(sql)
|
||||
@ -1281,7 +1281,7 @@ class OracleDBConnector(DBConnector):
|
||||
return True
|
||||
|
||||
def deleteView(self, view):
|
||||
"""Delete a view."""
|
||||
"""Deletes a view."""
|
||||
schema, tablename = self.getSchemaTableName(view)
|
||||
|
||||
if self.isVectorTable(view):
|
||||
@ -1291,30 +1291,30 @@ class OracleDBConnector(DBConnector):
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def createSchema(self, schema):
|
||||
"""Create a new empty schema in database."""
|
||||
"""Creates a new empty schema in database."""
|
||||
# Not tested
|
||||
sql = u"CREATE SCHEMA AUTHORIZATION {}".format(
|
||||
self.quoteId(schema))
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def deleteSchema(self, schema):
|
||||
"""Drop (empty) schema from database."""
|
||||
"""Drops (empty) schema from database."""
|
||||
sql = u"DROP USER {} CASCADE".format(self.quoteId(schema))
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def renameSchema(self, schema, new_schema):
|
||||
"""Rename a schema in the database."""
|
||||
"""Renames a schema in the database."""
|
||||
# Unsupported in Oracle
|
||||
pass
|
||||
|
||||
def addTableColumn(self, table, field_def):
|
||||
"""Add a column to a table."""
|
||||
"""Adds a column to a table."""
|
||||
sql = u"ALTER TABLE {0} ADD {1}".format(self.quoteId(table),
|
||||
field_def)
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def deleteTableColumn(self, table, column):
|
||||
"""Delete column from a table."""
|
||||
"""Deletes column from a table."""
|
||||
# Delete all the constraints for this column
|
||||
constraints = [f[0] for f in self.getTableConstraints(table)
|
||||
if f[2] == column]
|
||||
@ -1337,7 +1337,7 @@ class OracleDBConnector(DBConnector):
|
||||
def updateTableColumn(self, table, column, new_name=None,
|
||||
data_type=None, not_null=None,
|
||||
default=None, comment=None):
|
||||
"""Update properties of a column in a table."""
|
||||
"""Updates properties of a column in a table."""
|
||||
|
||||
schema, tablename = self.getSchemaTableName(table)
|
||||
|
||||
@ -1378,19 +1378,19 @@ class OracleDBConnector(DBConnector):
|
||||
self._commit()
|
||||
|
||||
def renameTableColumn(self, table, column, new_name):
|
||||
"""Rename column in a table."""
|
||||
"""Renames column in a table."""
|
||||
return self.updateTableColumn(table, column, new_name)
|
||||
|
||||
def setTableColumnType(self, table, column, data_type):
|
||||
"""Change column type."""
|
||||
"""Changes column type."""
|
||||
return self.updateTableColumn(table, column, None, data_type)
|
||||
|
||||
def setTableColumnNull(self, table, column, is_null):
|
||||
"""Change whether column can contain null values."""
|
||||
"""Changes whether column can contain null values."""
|
||||
return self.updateTableColumn(table, column, None, None, not is_null)
|
||||
|
||||
def setTableColumnDefault(self, table, column, default):
|
||||
"""Change column's default value.
|
||||
"""Changes column's default value.
|
||||
If default=None or an empty string drop default value.
|
||||
"""
|
||||
return self.updateTableColumn(table, column, None, None, None, default)
|
||||
@ -1417,7 +1417,7 @@ class OracleDBConnector(DBConnector):
|
||||
return res
|
||||
|
||||
def refreshMView(self, table):
|
||||
"""Refresh an MVIEW"""
|
||||
"""Refreshes an MVIEW"""
|
||||
schema, tablename = self.getSchemaTableName(table)
|
||||
mview = u"{}.{}".format(schema, tablename) if schema else tablename
|
||||
sql = u"""
|
||||
@ -1429,7 +1429,7 @@ class OracleDBConnector(DBConnector):
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def deleteMetadata(self, table, geom_column=None):
|
||||
"""Delete the metadata entry for a table"""
|
||||
"""Deletes the metadata entry for a table"""
|
||||
schema, tablename = self.getSchemaTableName(table)
|
||||
if not (self.getRawTablePrivileges('USER_SDO_GEOM_METADATA',
|
||||
'MDSYS',
|
||||
@ -1448,7 +1448,7 @@ class OracleDBConnector(DBConnector):
|
||||
|
||||
def updateMetadata(self, table, geom_column, new_geom_column=None,
|
||||
new_table=None, extent=None, srid=None):
|
||||
"""update the metadata table with the new information"""
|
||||
"""Updates the metadata table with the new information"""
|
||||
|
||||
schema, tablename = self.getSchemaTableName(table)
|
||||
if not (self.getRawTablePrivileges('USER_SDO_GEOM_METADATA',
|
||||
@ -1499,7 +1499,7 @@ class OracleDBConnector(DBConnector):
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def insertMetadata(self, table, geom_column, extent, srid, dim=2):
|
||||
""" Insert a line for the table in Oracle Metadata table."""
|
||||
"""Inserts a line for the table in Oracle Metadata table."""
|
||||
schema, tablename = self.getSchemaTableName(table)
|
||||
if not (self.getRawTablePrivileges('USER_SDO_GEOM_METADATA',
|
||||
'MDSYS',
|
||||
@ -1542,7 +1542,7 @@ class OracleDBConnector(DBConnector):
|
||||
|
||||
def addGeometryColumn(self, table, geom_column='GEOM',
|
||||
geom_type=None, srid=-1, dim=2):
|
||||
"""Add a geometry column and update Oracle Spatial
|
||||
"""Adds a geometry column and update Oracle Spatial
|
||||
metadata.
|
||||
"""
|
||||
|
||||
@ -1565,48 +1565,48 @@ class OracleDBConnector(DBConnector):
|
||||
srid, dim)
|
||||
|
||||
def deleteGeometryColumn(self, table, geom_column):
|
||||
"""Delete a geometric column."""
|
||||
"""Deletes a geometric column."""
|
||||
return self.deleteTableColumn(table, geom_column)
|
||||
|
||||
def addTableUniqueConstraint(self, table, column):
|
||||
"""Add a unique constraint to a table."""
|
||||
"""Adds a unique constraint to a table."""
|
||||
sql = u"ALTER TABLE {0} ADD UNIQUE ({1})".format(
|
||||
self.quoteId(table), self.quoteId(column))
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def deleteTableConstraint(self, table, constraint):
|
||||
"""Delete constraint in a table."""
|
||||
"""Deletes constraint in a table."""
|
||||
sql = u"ALTER TABLE {0} DROP CONSTRAINT {1}".format(
|
||||
self.quoteId(table), self.quoteId(constraint))
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def addTablePrimaryKey(self, table, column):
|
||||
"""Add a primary key (with one column) to a table."""
|
||||
"""Adds a primary key (with one column) to a table."""
|
||||
sql = u"ALTER TABLE {0} ADD PRIMARY KEY ({1})".format(
|
||||
self.quoteId(table), self.quoteId(column))
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def createTableIndex(self, table, name, column):
|
||||
"""Create index on one column using default options."""
|
||||
"""Creates index on one column using default options."""
|
||||
sql = u"CREATE INDEX {0} ON {1} ({2})".format(
|
||||
self.quoteId(name), self.quoteId(table),
|
||||
self.quoteId(column))
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def rebuildTableIndex(self, table, name):
|
||||
"""Rebuild a table index"""
|
||||
"""Rebuilds a table index"""
|
||||
schema, tablename = self.getSchemaTableName(table)
|
||||
sql = u"ALTER INDEX {} REBUILD".format(self.quoteId((schema, name)))
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def deleteTableIndex(self, table, name):
|
||||
"""Delete an index on a table."""
|
||||
"""Deletes an index on a table."""
|
||||
schema, tablename = self.getSchemaTableName(table)
|
||||
sql = u"DROP INDEX {}".format(self.quoteId((schema, name)))
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def createSpatialIndex(self, table, geom_column='GEOM'):
|
||||
"""Create a spatial index on a geometric column."""
|
||||
"""Creates a spatial index on a geometric column."""
|
||||
geom_column = geom_column.upper()
|
||||
schema, tablename = self.getSchemaTableName(table)
|
||||
idx_name = self.quoteId(u"sidx_{0}_{1}".format(tablename,
|
||||
@ -1620,7 +1620,7 @@ class OracleDBConnector(DBConnector):
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def deleteSpatialIndex(self, table, geom_column='GEOM'):
|
||||
"""Delete a spatial index of a geometric column."""
|
||||
"""Deletes a spatial index of a geometric column."""
|
||||
schema, tablename = self.getSchemaTableName(table)
|
||||
idx_name = self.quoteId(u"sidx_{0}_{1}".format(tablename,
|
||||
geom_column))
|
||||
|
@ -32,7 +32,9 @@ from qgis.core import (
|
||||
QgsApplication,
|
||||
QgsSettings,
|
||||
QgsMapLayerType,
|
||||
QgsWkbTypes
|
||||
QgsWkbTypes,
|
||||
QgsProviderConnectionException,
|
||||
QgsProviderRegistry,
|
||||
)
|
||||
from ..db_plugins import createDbPlugin
|
||||
|
||||
@ -126,9 +128,16 @@ class DBPlugin(QObject):
|
||||
return self.connect(self.parent())
|
||||
|
||||
def remove(self):
|
||||
settings = QgsSettings()
|
||||
settings.beginGroup(u"/%s/%s" % (self.connectionSettingsKey(), self.connectionName()))
|
||||
settings.remove("")
|
||||
|
||||
# Try the new API first, fallback to legacy
|
||||
try:
|
||||
md = QgsProviderRegistry.instance().providerMetadata(self.providerName())
|
||||
md.deleteConnection(self.connectionName())
|
||||
except (AttributeError, QgsProviderConnectionException) as ex:
|
||||
settings = QgsSettings()
|
||||
settings.beginGroup(u"/%s/%s" % (self.connectionSettingsKey(), self.connectionName()))
|
||||
settings.remove("")
|
||||
|
||||
self.deleted.emit()
|
||||
return True
|
||||
|
||||
@ -163,12 +172,21 @@ class DBPlugin(QObject):
|
||||
@classmethod
|
||||
def connections(self):
|
||||
# get the list of connections
|
||||
|
||||
conn_list = []
|
||||
settings = QgsSettings()
|
||||
settings.beginGroup(self.connectionSettingsKey())
|
||||
for name in settings.childGroups():
|
||||
conn_list.append(createDbPlugin(self.typeName(), name))
|
||||
settings.endGroup()
|
||||
|
||||
# First try with the new core API, if that fails, proceed with legacy code
|
||||
try:
|
||||
md = QgsProviderRegistry.instance().providerMetadata(self.providerName())
|
||||
for name in md.dbConnections().keys():
|
||||
conn_list.append(createDbPlugin(self.typeName(), name))
|
||||
except (AttributeError, QgsProviderConnectionException) as ex:
|
||||
settings = QgsSettings()
|
||||
settings.beginGroup(self.connectionSettingsKey())
|
||||
for name in settings.childGroups():
|
||||
conn_list.append(createDbPlugin(self.typeName(), name))
|
||||
settings.endGroup()
|
||||
|
||||
return conn_list
|
||||
|
||||
def databasesFactory(self, connection, uri):
|
||||
|
@ -574,7 +574,7 @@ class PostGisDBConnector(DBConnector):
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def deleteTableTrigger(self, trigger, table):
|
||||
""" delete trigger on table """
|
||||
"""Deletes trigger on table """
|
||||
sql = u"DROP TRIGGER %s ON %s" % (self.quoteId(trigger), self.quoteId(table))
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
@ -592,7 +592,7 @@ class PostGisDBConnector(DBConnector):
|
||||
return res
|
||||
|
||||
def deleteTableRule(self, rule, table):
|
||||
""" delete rule on table """
|
||||
"""Deletes rule on table """
|
||||
sql = u"DROP RULE %s ON %s" % (self.quoteId(rule), self.quoteId(table))
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
@ -689,7 +689,7 @@ class PostGisDBConnector(DBConnector):
|
||||
return False
|
||||
|
||||
def createTable(self, table, field_defs, pkey):
|
||||
""" create ordinary table
|
||||
"""Creates ordinary table
|
||||
'fields' is array containing field definitions
|
||||
'pkey' is the primary key name
|
||||
"""
|
||||
@ -706,7 +706,7 @@ class PostGisDBConnector(DBConnector):
|
||||
return True
|
||||
|
||||
def deleteTable(self, table):
|
||||
""" delete table and its reference in either geometry_columns or raster_columns """
|
||||
"""Deletes table and its reference in either geometry_columns or raster_columns """
|
||||
schema, tablename = self.getSchemaTableName(table)
|
||||
schema_part = u"%s, " % self.quoteString(schema) if schema is not None else ""
|
||||
if self.isVectorTable(table):
|
||||
@ -719,19 +719,19 @@ class PostGisDBConnector(DBConnector):
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def emptyTable(self, table):
|
||||
""" delete all rows from table """
|
||||
"""Deletes all rows from table """
|
||||
sql = u"TRUNCATE %s" % self.quoteId(table)
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def renameTable(self, table, new_table):
|
||||
""" rename a table in database """
|
||||
def renamesTable(self, table, new_table):
|
||||
"""Renames a table in database """
|
||||
schema, tablename = self.getSchemaTableName(table)
|
||||
if new_table == tablename:
|
||||
return
|
||||
|
||||
c = self._get_cursor()
|
||||
|
||||
sql = u"ALTER TABLE %s RENAME TO %s" % (self.quoteId(table), self.quoteId(new_table))
|
||||
sql = u"ALTER TABLE %s RENAME TO %s" % (self.quoteId(table), self.quoteId(new_table))
|
||||
self._execute(c, sql)
|
||||
|
||||
# update geometry_columns if PostGIS is enabled
|
||||
@ -798,13 +798,13 @@ class PostGisDBConnector(DBConnector):
|
||||
c = self._get_cursor()
|
||||
t = u"__new_table__"
|
||||
|
||||
sql = u"ALTER TABLE %s RENAME TO %s" % (self.quoteId(table), self.quoteId(t))
|
||||
sql = u"ALTER TABLE %s RENAME TO %s" % (self.quoteId(table), self.quoteId(t))
|
||||
self._execute(c, sql)
|
||||
|
||||
sql = u"ALTER TABLE %s SET SCHEMA %s" % (self.quoteId((schema, t)), self.quoteId(new_schema))
|
||||
self._execute(c, sql)
|
||||
|
||||
sql = u"ALTER TABLE %s RENAME TO %s" % (self.quoteId((new_schema, t)), self.quoteId(table))
|
||||
sql = u"ALTER TABLE %s RENAME TO %s" % (self.quoteId((new_schema, t)), self.quoteId(table))
|
||||
self._execute(c, sql)
|
||||
|
||||
# update geometry_columns if PostGIS is enabled
|
||||
@ -830,47 +830,47 @@ class PostGisDBConnector(DBConnector):
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def renameView(self, view, new_name):
|
||||
""" rename view in database """
|
||||
"""Renames view in database """
|
||||
self.renameTable(view, new_name)
|
||||
|
||||
def createSchema(self, schema):
|
||||
""" create a new empty schema in database """
|
||||
"""Creates a new empty schema in database """
|
||||
sql = u"CREATE SCHEMA %s" % self.quoteId(schema)
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def deleteSchema(self, schema):
|
||||
""" drop (empty) schema from database """
|
||||
"""Drops (empty) schema from database """
|
||||
sql = u"DROP SCHEMA %s" % self.quoteId(schema)
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def renameSchema(self, schema, new_schema):
|
||||
""" rename a schema in database """
|
||||
sql = u"ALTER SCHEMA %s RENAME TO %s" % (self.quoteId(schema), self.quoteId(new_schema))
|
||||
def renamesSchema(self, schema, new_schema):
|
||||
"""Renames a schema in database """
|
||||
sql = u"ALTER SCHEMA %s RENAME TO %s" % (self.quoteId(schema), self.quoteId(new_schema))
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def runVacuum(self):
|
||||
""" run vacuum on the db """
|
||||
"""Runs vacuum on the db """
|
||||
self._execute_and_commit("VACUUM")
|
||||
|
||||
def runVacuumAnalyze(self, table):
|
||||
""" run vacuum analyze on a table """
|
||||
"""Runs vacuum analyze on a table """
|
||||
sql = u"VACUUM ANALYZE %s" % self.quoteId(table)
|
||||
self._execute(None, sql)
|
||||
self._commit()
|
||||
|
||||
def runRefreshMaterializedView(self, table):
|
||||
""" run refresh materialized view on a table """
|
||||
"""Runs refresh materialized view on a table """
|
||||
sql = u"REFRESH MATERIALIZED VIEW %s" % self.quoteId(table)
|
||||
self._execute(None, sql)
|
||||
self._commit()
|
||||
|
||||
def addTableColumn(self, table, field_def):
|
||||
""" add a column to table """
|
||||
"""Adds a column to table """
|
||||
sql = u"ALTER TABLE %s ADD %s" % (self.quoteId(table), field_def)
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def deleteTableColumn(self, table, column):
|
||||
""" delete column from a table """
|
||||
"""Deletes column from a table """
|
||||
if self.isGeometryColumn(table, column):
|
||||
# use PostGIS function to delete geometry column correctly
|
||||
schema, tablename = self.getSchemaTableName(table)
|
||||
@ -905,9 +905,9 @@ class PostGisDBConnector(DBConnector):
|
||||
sql += u" %s %s," % (alter_col_str, a)
|
||||
self._execute(c, sql[:-1])
|
||||
|
||||
# rename the column
|
||||
#Renames the column
|
||||
if new_name is not None and new_name != column:
|
||||
sql = u"ALTER TABLE %s RENAME %s TO %s" % (
|
||||
sql = u"ALTER TABLE %s RENAME %s TO %s" % (
|
||||
self.quoteId(table), self.quoteId(column), self.quoteId(new_name))
|
||||
self._execute(c, sql)
|
||||
|
||||
@ -928,20 +928,20 @@ class PostGisDBConnector(DBConnector):
|
||||
|
||||
self._commit()
|
||||
|
||||
def renameTableColumn(self, table, column, new_name):
|
||||
""" rename column in a table """
|
||||
def renamesTableColumn(self, table, column, new_name):
|
||||
"""Renames column in a table """
|
||||
return self.updateTableColumn(table, column, new_name)
|
||||
|
||||
def setTableColumnType(self, table, column, data_type):
|
||||
""" change column type """
|
||||
"""Changes column type """
|
||||
return self.updateTableColumn(table, column, None, data_type)
|
||||
|
||||
def setTableColumnNull(self, table, column, is_null):
|
||||
""" change whether column can contain null values """
|
||||
"""Changes whether column can contain null values """
|
||||
return self.updateTableColumn(table, column, None, None, not is_null)
|
||||
|
||||
def setTableColumnDefault(self, table, column, default):
|
||||
""" change column's default value.
|
||||
"""Changes column's default value.
|
||||
If default=None or an empty string drop default value """
|
||||
return self.updateTableColumn(table, column, None, None, None, default)
|
||||
|
||||
@ -970,22 +970,22 @@ class PostGisDBConnector(DBConnector):
|
||||
return self.deleteTableColumn(table, geom_column)
|
||||
|
||||
def addTableUniqueConstraint(self, table, column):
|
||||
""" add a unique constraint to a table """
|
||||
"""Adds a unique constraint to a table """
|
||||
sql = u"ALTER TABLE %s ADD UNIQUE (%s)" % (self.quoteId(table), self.quoteId(column))
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def deleteTableConstraint(self, table, constraint):
|
||||
""" delete constraint in a table """
|
||||
"""Deletes constraint in a table """
|
||||
sql = u"ALTER TABLE %s DROP CONSTRAINT %s" % (self.quoteId(table), self.quoteId(constraint))
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def addTablePrimaryKey(self, table, column):
|
||||
""" add a primery key (with one column) to a table """
|
||||
"""Adds a primery key (with one column) to a table """
|
||||
sql = u"ALTER TABLE %s ADD PRIMARY KEY (%s)" % (self.quoteId(table), self.quoteId(column))
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def createTableIndex(self, table, name, column):
|
||||
""" create index on one column using default options """
|
||||
"""Creates index on one column using default options """
|
||||
sql = u"CREATE INDEX %s ON %s (%s)" % (self.quoteId(name), self.quoteId(table), self.quoteId(column))
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
|
@ -363,7 +363,7 @@ class SpatiaLiteDBConnector(DBConnector):
|
||||
return c.fetchall()
|
||||
|
||||
def deleteTableTrigger(self, trigger, table=None):
|
||||
""" delete trigger """
|
||||
"""Deletes trigger """
|
||||
sql = u"DROP TRIGGER %s" % self.quoteId(trigger)
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
@ -424,7 +424,7 @@ class SpatiaLiteDBConnector(DBConnector):
|
||||
return False
|
||||
|
||||
def createTable(self, table, field_defs, pkey):
|
||||
""" create ordinary table
|
||||
"""Creates ordinary table
|
||||
'fields' is array containing field definitions
|
||||
'pkey' is the primary key name
|
||||
"""
|
||||
@ -441,7 +441,7 @@ class SpatiaLiteDBConnector(DBConnector):
|
||||
return True
|
||||
|
||||
def deleteTable(self, table):
|
||||
""" delete table from the database """
|
||||
"""Deletes table from the database """
|
||||
if self.isRasterTable(table):
|
||||
return False
|
||||
|
||||
@ -456,7 +456,7 @@ class SpatiaLiteDBConnector(DBConnector):
|
||||
return True
|
||||
|
||||
def emptyTable(self, table):
|
||||
""" delete all rows from table """
|
||||
"""Deletes all rows from table """
|
||||
if self.isRasterTable(table):
|
||||
return False
|
||||
|
||||
@ -560,7 +560,7 @@ class SpatiaLiteDBConnector(DBConnector):
|
||||
self.connection.isolation_level = '' # reset to default isolation
|
||||
|
||||
def addTableColumn(self, table, field_def):
|
||||
""" add a column to table """
|
||||
"""Adds a column to table """
|
||||
sql = u"ALTER TABLE %s ADD %s" % (self.quoteId(table), field_def)
|
||||
self._execute(None, sql)
|
||||
|
||||
@ -574,7 +574,7 @@ class SpatiaLiteDBConnector(DBConnector):
|
||||
return True
|
||||
|
||||
def deleteTableColumn(self, table, column):
|
||||
""" delete column from a table """
|
||||
"""Deletes column from a table """
|
||||
if not self.isGeometryColumn(table, column):
|
||||
return False # column editing not supported
|
||||
|
||||
@ -591,15 +591,15 @@ class SpatiaLiteDBConnector(DBConnector):
|
||||
return False # column editing not supported
|
||||
|
||||
def setColumnType(self, table, column, data_type):
|
||||
""" change column type """
|
||||
"""Changes column type """
|
||||
return False # column editing not supported
|
||||
|
||||
def setColumnDefault(self, table, column, default):
|
||||
""" change column's default value. If default=None drop default value """
|
||||
"""Changes column's default value. If default=None drop default value """
|
||||
return False # column editing not supported
|
||||
|
||||
def setColumnNull(self, table, column, is_null):
|
||||
""" change whether column can contain null values """
|
||||
"""Changes whether column can contain null values """
|
||||
return False # column editing not supported
|
||||
|
||||
def isGeometryColumn(self, table, column):
|
||||
@ -622,20 +622,20 @@ class SpatiaLiteDBConnector(DBConnector):
|
||||
return self.deleteTableColumn(table, geom_column)
|
||||
|
||||
def addTableUniqueConstraint(self, table, column):
|
||||
""" add a unique constraint to a table """
|
||||
"""Adds a unique constraint to a table """
|
||||
return False # constraints not supported
|
||||
|
||||
def deleteTableConstraint(self, table, constraint):
|
||||
""" delete constraint in a table """
|
||||
"""Deletes constraint in a table """
|
||||
return False # constraints not supported
|
||||
|
||||
def addTablePrimaryKey(self, table, column):
|
||||
""" add a primery key (with one column) to a table """
|
||||
"""Adds a primery key (with one column) to a table """
|
||||
sql = u"ALTER TABLE %s ADD PRIMARY KEY (%s)" % (self.quoteId(table), self.quoteId(column))
|
||||
self._execute_and_commit(sql)
|
||||
|
||||
def createTableIndex(self, table, name, column, unique=False):
|
||||
""" create index on one column using default options """
|
||||
"""Creates index on one column using default options """
|
||||
unique_str = u"UNIQUE" if unique else ""
|
||||
sql = u"CREATE %s INDEX %s ON %s (%s)" % (
|
||||
unique_str, self.quoteId(name), self.quoteId(table), self.quoteId(column))
|
||||
|
@ -174,7 +174,7 @@ class DlgCreateTable(QDialog, Ui_Dialog):
|
||||
self.cboPrimaryKey.setCurrentIndex(selRow)
|
||||
|
||||
def addField(self):
|
||||
""" add new field to the end of field table """
|
||||
"""Adds new field to the end of field table """
|
||||
m = self.fields.model()
|
||||
newRow = m.rowCount()
|
||||
m.insertRows(newRow, 1)
|
||||
@ -209,7 +209,7 @@ class DlgCreateTable(QDialog, Ui_Dialog):
|
||||
return sel[0].row()
|
||||
|
||||
def deleteField(self):
|
||||
""" delete selected field """
|
||||
"""Deletes selected field """
|
||||
row = self.selectedField()
|
||||
if row is None:
|
||||
QMessageBox.information(self, self.tr("DB Manager"), self.tr("No field selected."))
|
||||
@ -259,7 +259,7 @@ class DlgCreateTable(QDialog, Ui_Dialog):
|
||||
self.updatePkeyCombo()
|
||||
|
||||
def createTable(self):
|
||||
""" create table with chosen fields, optionally add a geometry column """
|
||||
"""Creates table with chosen fields, optionally add a geometry column """
|
||||
if not self.hasSchemas:
|
||||
schema = None
|
||||
else:
|
||||
|
@ -165,7 +165,7 @@ class DlgImportVector(QDialog, Ui_Dialog):
|
||||
self.cboInputLayer.setEditText(filename)
|
||||
|
||||
def reloadInputLayer(self):
|
||||
""" create the input layer and update available options """
|
||||
"""Creates the input layer and update available options """
|
||||
if self.mode != self.ASK_FOR_INPUT_MODE:
|
||||
return True
|
||||
|
||||
|
@ -171,7 +171,7 @@ class DlgTableProperties(QDialog, Ui_Dialog):
|
||||
DlgDbError.showError(e, self)
|
||||
|
||||
def deleteColumn(self):
|
||||
""" delete currently selected column """
|
||||
"""Deletes currently selected column """
|
||||
index = self.currentColumn()
|
||||
if index == -1:
|
||||
return
|
||||
@ -214,7 +214,7 @@ class DlgTableProperties(QDialog, Ui_Dialog):
|
||||
self.tabs.setTabEnabled(index, False)
|
||||
|
||||
def addConstraint(self):
|
||||
""" add primary key or unique constraint """
|
||||
"""Adds primary key or unique constraint """
|
||||
|
||||
dlg = DlgCreateConstraint(self, self.table)
|
||||
if not dlg.exec_():
|
||||
@ -222,7 +222,7 @@ class DlgTableProperties(QDialog, Ui_Dialog):
|
||||
self.refresh()
|
||||
|
||||
def deleteConstraint(self):
|
||||
""" delete a constraint """
|
||||
"""Deletes a constraint """
|
||||
|
||||
index = self.currentConstraint()
|
||||
if index == -1:
|
||||
@ -275,14 +275,14 @@ class DlgTableProperties(QDialog, Ui_Dialog):
|
||||
self.tabs.setTabEnabled(index, False)
|
||||
|
||||
def createIndex(self):
|
||||
""" create an index """
|
||||
"""Creates an index """
|
||||
dlg = DlgCreateIndex(self, self.table)
|
||||
if not dlg.exec_():
|
||||
return
|
||||
self.refresh()
|
||||
|
||||
def createSpatialIndex(self):
|
||||
""" create spatial index for the geometry column """
|
||||
"""Creates spatial index for the geometry column """
|
||||
if self.table.type != self.table.VectorType:
|
||||
QMessageBox.information(self, self.tr("DB Manager"), self.tr("The selected table has no geometry."))
|
||||
return
|
||||
@ -313,7 +313,7 @@ class DlgTableProperties(QDialog, Ui_Dialog):
|
||||
return indexes[0].row()
|
||||
|
||||
def deleteIndex(self):
|
||||
""" delete currently selected index """
|
||||
"""Deletes currently selected index """
|
||||
index = self.currentIndex()
|
||||
if index == -1:
|
||||
return
|
||||
|
@ -148,6 +148,7 @@ SET(QGIS_CORE_SRCS
|
||||
providers/ogr/qgsogrconnpool.cpp
|
||||
providers/ogr/qgsogrexpressioncompiler.cpp
|
||||
providers/ogr/qgsgeopackagedataitems.cpp
|
||||
providers/ogr/qgsgeopackageproviderconnection.cpp
|
||||
providers/ogr/qgsgeopackagerasterwriter.cpp
|
||||
providers/ogr/qgsgeopackagerasterwritertask.cpp
|
||||
providers/ogr/qgsgeopackageprojectstorage.cpp
|
||||
@ -162,6 +163,8 @@ SET(QGIS_CORE_SRCS
|
||||
|
||||
qgis.cpp
|
||||
qgsabstractcontentcache.cpp
|
||||
qgsabstractproviderconnection.cpp
|
||||
qgsabstractdatabaseproviderconnection.cpp
|
||||
qgsapplication.cpp
|
||||
qgsaction.cpp
|
||||
qgsactionscope.cpp
|
||||
@ -627,6 +630,7 @@ ENDIF(NOT MSVC)
|
||||
|
||||
SET(QGIS_CORE_MOC_HDRS
|
||||
qgsabstractcontentcache.h
|
||||
qgsabstractdatabaseproviderconnection.h
|
||||
qgsapplication.h
|
||||
qgsactionmanager.h
|
||||
qgsactionscoperegistry.h
|
||||
@ -882,6 +886,8 @@ SET(QGIS_CORE_HDRS
|
||||
|
||||
qgis.h
|
||||
qgis_sip.h
|
||||
qgsabstractproviderconnection.h
|
||||
qgsabstractdatabaseproviderconnection.h
|
||||
qgsaction.h
|
||||
qgsactionscope.h
|
||||
qgsactionmanager.h
|
||||
@ -1126,6 +1132,8 @@ SET(QGIS_CORE_HDRS
|
||||
providers/memory/qgsmemoryproviderutils.h
|
||||
|
||||
providers/ogr/qgsgeopackageprojectstorage.h
|
||||
providers/ogr/qgsgeopackageproviderconnection.h
|
||||
providers/ogr/qgsogrprovider.h
|
||||
|
||||
raster/qgsbilinearrasterresampler.h
|
||||
raster/qgsbrightnesscontrastfilter.h
|
||||
|
@ -20,8 +20,6 @@
|
||||
#include <QFileDialog>
|
||||
#include <QInputDialog>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include "qgssqliteutils.h"
|
||||
#include "qgsgeopackagedataitems.h"
|
||||
#include "qgsogrdbconnection.h"
|
||||
@ -41,6 +39,7 @@
|
||||
#include "qgsproxyprogresstask.h"
|
||||
#include "qgsprojectstorageregistry.h"
|
||||
#include "qgsgeopackageprojectstorage.h"
|
||||
#include "qgsgeopackageproviderconnection.h"
|
||||
|
||||
QString QgsGeoPackageDataItemProvider::name()
|
||||
{
|
||||
@ -146,6 +145,54 @@ bool QgsGeoPackageCollectionItem::equal( const QgsDataItem *other )
|
||||
|
||||
}
|
||||
|
||||
bool QgsGeoPackageCollectionItem::deleteRasterLayer( const QString &layerName, QString &errCause )
|
||||
{
|
||||
QgsProviderMetadata *md { QgsProviderRegistry::instance()->providerMetadata( QStringLiteral( "ogr" ) ) };
|
||||
QgsAbstractDatabaseProviderConnection *conn { static_cast<QgsAbstractDatabaseProviderConnection *>( md->findConnection( name() ) ) };
|
||||
if ( conn )
|
||||
{
|
||||
try
|
||||
{
|
||||
conn->dropRasterTable( QString(), layerName );
|
||||
}
|
||||
catch ( QgsProviderConnectionException &ex )
|
||||
{
|
||||
errCause = ex.what();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errCause = QObject::tr( "There was an error retrieving the connection %1!" ).arg( name() );
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QgsGeoPackageCollectionItem::deleteVectorLayer( const QString &layerName, QString &errCause )
|
||||
{
|
||||
QgsProviderMetadata *md { QgsProviderRegistry::instance()->providerMetadata( QStringLiteral( "ogr" ) ) };
|
||||
QgsAbstractDatabaseProviderConnection *conn { static_cast<QgsAbstractDatabaseProviderConnection *>( md->findConnection( name() ) ) };
|
||||
if ( conn )
|
||||
{
|
||||
try
|
||||
{
|
||||
conn->dropVectorTable( QString(), layerName );
|
||||
}
|
||||
catch ( QgsProviderConnectionException &ex )
|
||||
{
|
||||
errCause = ex.what();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errCause = QObject::tr( "There was an error retrieving the connection %1!" ).arg( name() );
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QWidget *QgsGeoPackageRootItem::paramWidget()
|
||||
{
|
||||
return nullptr;
|
||||
@ -165,156 +212,29 @@ void QgsGeoPackageCollectionItem::deleteConnection()
|
||||
mParent->refreshConnections();
|
||||
}
|
||||
|
||||
bool QgsGeoPackageCollectionItem::vacuumGeoPackageDb( const QString &path, const QString &name, QString &errCause )
|
||||
bool QgsGeoPackageCollectionItem::vacuumGeoPackageDb( const QString &name, QString &errCause )
|
||||
{
|
||||
QgsScopedProxyProgressTask task( tr( "Vacuuming %1" ).arg( name ) );
|
||||
|
||||
bool result = false;
|
||||
// Better safe than sorry
|
||||
if ( ! path.isEmpty( ) )
|
||||
QgsProviderMetadata *md { QgsProviderRegistry::instance()->providerMetadata( QStringLiteral( "ogr" ) ) };
|
||||
QgsAbstractDatabaseProviderConnection *conn { static_cast<QgsAbstractDatabaseProviderConnection *>( md->findConnection( name ) ) };
|
||||
if ( conn )
|
||||
{
|
||||
char *errmsg = nullptr;
|
||||
sqlite3_database_unique_ptr database;
|
||||
int status = database.open_v2( path, SQLITE_OPEN_READWRITE, nullptr );
|
||||
if ( status != SQLITE_OK )
|
||||
try
|
||||
{
|
||||
errCause = sqlite3_errmsg( database.get() );
|
||||
conn->vacuum( QString(), QString() );
|
||||
}
|
||||
else
|
||||
catch ( QgsProviderConnectionException &ex )
|
||||
{
|
||||
( void )sqlite3_exec(
|
||||
database.get(), /* An open database */
|
||||
"VACUUM", /* SQL to be evaluated */
|
||||
nullptr, /* Callback function */
|
||||
nullptr, /* 1st argument to callback */
|
||||
&errmsg /* Error msg written here */
|
||||
);
|
||||
}
|
||||
if ( status != SQLITE_OK || errmsg )
|
||||
{
|
||||
errCause = tr( "There was an error compacting (VACUUM) the database <b>%1</b>: %2" )
|
||||
.arg( name,
|
||||
QString::fromUtf8( errmsg ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
sqlite3_free( errmsg );
|
||||
}
|
||||
else
|
||||
{
|
||||
// This should never happen!
|
||||
errCause = tr( "Layer path is empty: layer cannot be deleted!" );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool QgsGeoPackageCollectionItem::deleteGeoPackageRasterLayer( const QString &uri, QString &errCause )
|
||||
{
|
||||
bool result = false;
|
||||
// Better safe than sorry
|
||||
if ( ! uri.isEmpty( ) )
|
||||
{
|
||||
QVariantMap pieces( QgsProviderRegistry::instance()->decodeUri( QStringLiteral( "gdal" ), uri ) );
|
||||
QString baseUri = pieces[QStringLiteral( "path" )].toString();
|
||||
QString layerName = pieces[QStringLiteral( "layerName" )].toString();
|
||||
|
||||
if ( baseUri.isEmpty() || layerName.isEmpty() )
|
||||
{
|
||||
errCause = QStringLiteral( "Layer URI is malformed: layer <b>%1</b> cannot be deleted!" ).arg( uri );
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlite3_database_unique_ptr database;
|
||||
int status = database.open_v2( baseUri, SQLITE_OPEN_READWRITE, nullptr );
|
||||
if ( status != SQLITE_OK )
|
||||
{
|
||||
errCause = sqlite3_errmsg( database.get() );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove table
|
||||
char *errmsg = nullptr;
|
||||
char *sql = sqlite3_mprintf(
|
||||
"DROP table IF EXISTS \"%w\";"
|
||||
"DELETE FROM gpkg_contents WHERE table_name = '%q';"
|
||||
"DELETE FROM gpkg_tile_matrix WHERE table_name = '%q';"
|
||||
"DELETE FROM gpkg_tile_matrix_set WHERE table_name = '%q';",
|
||||
layerName.toUtf8().constData(),
|
||||
layerName.toUtf8().constData(),
|
||||
layerName.toUtf8().constData(),
|
||||
layerName.toUtf8().constData() );
|
||||
status = sqlite3_exec(
|
||||
database.get(), /* An open database */
|
||||
sql, /* SQL to be evaluated */
|
||||
nullptr, /* Callback function */
|
||||
nullptr, /* 1st argument to callback */
|
||||
&errmsg /* Error msg written here */
|
||||
);
|
||||
sqlite3_free( sql );
|
||||
// Remove from optional tables, may silently fail
|
||||
QStringList optionalTables;
|
||||
optionalTables << QStringLiteral( "gpkg_extensions" )
|
||||
<< QStringLiteral( "gpkg_metadata_reference" );
|
||||
for ( const QString &tableName : qgis::as_const( optionalTables ) )
|
||||
{
|
||||
char *sql = sqlite3_mprintf( "DELETE FROM %w WHERE table_name = '%q'",
|
||||
tableName.toUtf8().constData(),
|
||||
layerName.toUtf8().constData() );
|
||||
( void )sqlite3_exec(
|
||||
database.get(), /* An open database */
|
||||
sql, /* SQL to be evaluated */
|
||||
nullptr, /* Callback function */
|
||||
nullptr, /* 1st argument to callback */
|
||||
nullptr /* Error msg written here */
|
||||
);
|
||||
sqlite3_free( sql );
|
||||
}
|
||||
// Other tables, ignore errors
|
||||
{
|
||||
char *sql = sqlite3_mprintf( "DELETE FROM gpkg_2d_gridded_coverage_ancillary WHERE tile_matrix_set_name = '%q'",
|
||||
layerName.toUtf8().constData() );
|
||||
( void )sqlite3_exec(
|
||||
database.get(), /* An open database */
|
||||
sql, /* SQL to be evaluated */
|
||||
nullptr, /* Callback function */
|
||||
nullptr, /* 1st argument to callback */
|
||||
nullptr /* Error msg written here */
|
||||
);
|
||||
sqlite3_free( sql );
|
||||
}
|
||||
{
|
||||
char *sql = sqlite3_mprintf( "DELETE FROM gpkg_2d_gridded_tile_ancillary WHERE tpudt_name = '%q'",
|
||||
layerName.toUtf8().constData() );
|
||||
( void )sqlite3_exec(
|
||||
database.get(), /* An open database */
|
||||
sql, /* SQL to be evaluated */
|
||||
nullptr, /* Callback function */
|
||||
nullptr, /* 1st argument to callback */
|
||||
nullptr /* Error msg written here */
|
||||
);
|
||||
sqlite3_free( sql );
|
||||
}
|
||||
|
||||
if ( status == SQLITE_OK )
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
errCause = tr( "There was an error deleting the layer %1: %2" ).arg( layerName, QString::fromUtf8( errmsg ) );
|
||||
}
|
||||
sqlite3_free( errmsg );
|
||||
}
|
||||
errCause = ex.what();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This should never happen!
|
||||
errCause = tr( "Layer URI is empty: layer cannot be deleted!" );
|
||||
errCause = QObject::tr( "There was an error retrieving the connection %1!" ).arg( name );
|
||||
return false;
|
||||
}
|
||||
return result;
|
||||
return true;
|
||||
}
|
||||
|
||||
QgsGeoPackageConnectionItem::QgsGeoPackageConnectionItem( QgsDataItem *parent, const QString &name, const QString &path )
|
||||
@ -336,54 +256,25 @@ bool QgsGeoPackageConnectionItem::equal( const QgsDataItem *other )
|
||||
|
||||
QgsGeoPackageAbstractLayerItem::QgsGeoPackageAbstractLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &uri, QgsLayerItem::LayerType layerType, const QString &providerKey )
|
||||
: QgsLayerItem( parent, name, path, uri, layerType, providerKey )
|
||||
, mCollection( qobject_cast<QgsGeoPackageCollectionItem*>( parent ) )
|
||||
{
|
||||
mCapabilities |= Delete;
|
||||
mToolTip = uri;
|
||||
setState( Populated ); // no children are expected
|
||||
}
|
||||
|
||||
bool QgsGeoPackageAbstractLayerItem::executeDeleteLayer( QString &errCause )
|
||||
{
|
||||
errCause = QObject::tr( "The layer <b>%1</b> cannot be deleted because this feature is not yet implemented for this kind of layers." ).arg( mName );
|
||||
return false;
|
||||
}
|
||||
|
||||
static int collect_strings( void *names, int, char **argv, char ** )
|
||||
{
|
||||
*static_cast<QList<QString>*>( names ) << QString::fromUtf8( argv[ 0 ] );
|
||||
return 0;
|
||||
}
|
||||
|
||||
QStringList QgsGeoPackageAbstractLayerItem::tableNames()
|
||||
QStringList QgsGeoPackageAbstractLayerItem::tableNames() const
|
||||
{
|
||||
QStringList names;
|
||||
QVariantMap pieces( QgsProviderRegistry::instance()->decodeUri( providerKey(), mUri ) );
|
||||
QString baseUri = pieces[QStringLiteral( "path" )].toString();
|
||||
if ( !baseUri.isEmpty() )
|
||||
// note: not using providerKey() because GPKG methods are implemented in OGR
|
||||
QgsProviderMetadata *md { QgsProviderRegistry::instance()->providerMetadata( QStringLiteral( "ogr" ) ) };
|
||||
QgsGeoPackageProviderConnection *conn { static_cast<QgsGeoPackageProviderConnection *>( md->findConnection( parent()->name() ) ) };
|
||||
if ( conn )
|
||||
{
|
||||
char *errmsg = nullptr;
|
||||
sqlite3_database_unique_ptr database;
|
||||
int status = database.open_v2( baseUri, SQLITE_OPEN_READONLY, nullptr );
|
||||
if ( status == SQLITE_OK )
|
||||
for ( const QgsGeoPackageProviderConnection::TableProperty &p : conn->tables( ) )
|
||||
{
|
||||
char *sql = sqlite3_mprintf( "SELECT table_name FROM gpkg_contents;" );
|
||||
status = sqlite3_exec(
|
||||
database.get(), /* An open database */
|
||||
sql, /* SQL to be evaluated */
|
||||
collect_strings, /* Callback function */
|
||||
&names, /* 1st argument to callback */
|
||||
&errmsg /* Error msg written here */
|
||||
);
|
||||
sqlite3_free( sql );
|
||||
if ( status != SQLITE_OK )
|
||||
{
|
||||
QgsDebugMsg( QStringLiteral( "There was an error reading tables from GPKG layer %1: %2" ).arg( mUri, QString::fromUtf8( errmsg ) ) );
|
||||
}
|
||||
sqlite3_free( errmsg );
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsDebugMsg( QStringLiteral( "There was an error opening GPKG %1" ).arg( mUri ) );
|
||||
names.push_back( p.tableName() );
|
||||
}
|
||||
}
|
||||
return names;
|
||||
@ -405,6 +296,11 @@ QList<QgsMapLayer *> QgsGeoPackageAbstractLayerItem::layersInProject() const
|
||||
return layersList;
|
||||
}
|
||||
|
||||
QgsGeoPackageCollectionItem *QgsGeoPackageAbstractLayerItem::collection() const
|
||||
{
|
||||
return mCollection;
|
||||
}
|
||||
|
||||
QgsGeoPackageVectorLayerItem::QgsGeoPackageVectorLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &uri, LayerType layerType )
|
||||
: QgsGeoPackageAbstractLayerItem( parent, name, path, uri, layerType, QStringLiteral( "ogr" ) )
|
||||
{
|
||||
@ -415,17 +311,54 @@ QgsGeoPackageVectorLayerItem::QgsGeoPackageVectorLayerItem( QgsDataItem *parent,
|
||||
QgsGeoPackageRasterLayerItem::QgsGeoPackageRasterLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &uri )
|
||||
: QgsGeoPackageAbstractLayerItem( parent, name, path, uri, QgsLayerItem::LayerType::Raster, QStringLiteral( "gdal" ) )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool QgsGeoPackageRasterLayerItem::executeDeleteLayer( QString &errCause )
|
||||
{
|
||||
return QgsGeoPackageCollectionItem::deleteGeoPackageRasterLayer( mUri, errCause );
|
||||
QgsProviderMetadata *md { QgsProviderRegistry::instance()->providerMetadata( providerKey() ) };
|
||||
QgsAbstractDatabaseProviderConnection *conn { static_cast<QgsAbstractDatabaseProviderConnection *>( md->findConnection( parent()->name() ) ) };
|
||||
if ( conn )
|
||||
{
|
||||
try
|
||||
{
|
||||
conn->dropRasterTable( QString(), collection()->name() );
|
||||
}
|
||||
catch ( QgsProviderConnectionException &ex )
|
||||
{
|
||||
errCause = ex.what();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errCause = QObject::tr( "There was an error retrieving the connection %1!" ).arg( collection()->name() );
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QgsGeoPackageVectorLayerItem::executeDeleteLayer( QString &errCause )
|
||||
{
|
||||
return QgsOgrProviderUtils::deleteLayer( mUri, errCause );
|
||||
QgsProviderMetadata *md { QgsProviderRegistry::instance()->providerMetadata( providerKey() ) };
|
||||
QgsAbstractDatabaseProviderConnection *conn { static_cast<QgsAbstractDatabaseProviderConnection *>( md->findConnection( parent()->name() ) ) };
|
||||
if ( conn )
|
||||
{
|
||||
try
|
||||
{
|
||||
conn->dropVectorTable( QString(), collection()->name() );
|
||||
}
|
||||
catch ( QgsProviderConnectionException &ex )
|
||||
{
|
||||
errCause = ex.what();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errCause = QObject::tr( "There was an error retrieving the connection %1!" ).arg( collection()->name() );
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
///@endcond
|
||||
|
@ -26,53 +26,6 @@
|
||||
///@cond PRIVATE
|
||||
#define SIP_NO_FILE
|
||||
|
||||
/**
|
||||
* \brief The QgsGeoPackageAbstractLayerItem class is the base class for GeoPackage raster and vector layers
|
||||
*/
|
||||
class CORE_EXPORT QgsGeoPackageAbstractLayerItem : public QgsLayerItem
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Returns a list of all table names for the geopackage
|
||||
*/
|
||||
QStringList tableNames();
|
||||
|
||||
//! Checks if the data source has any layer in the current project returns them
|
||||
QList<QgsMapLayer *> layersInProject() const;
|
||||
|
||||
/**
|
||||
* Deletes a layer.
|
||||
* Subclasses need to implement this function with
|
||||
* the real deletion implementation
|
||||
*/
|
||||
virtual bool executeDeleteLayer( QString &errCause );
|
||||
|
||||
protected:
|
||||
QgsGeoPackageAbstractLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &uri, LayerType layerType, const QString &providerKey );
|
||||
};
|
||||
|
||||
class CORE_EXPORT QgsGeoPackageRasterLayerItem : public QgsGeoPackageAbstractLayerItem
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QgsGeoPackageRasterLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &uri );
|
||||
bool executeDeleteLayer( QString &errCause ) override;
|
||||
};
|
||||
|
||||
|
||||
class CORE_EXPORT QgsGeoPackageVectorLayerItem : public QgsGeoPackageAbstractLayerItem
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QgsGeoPackageVectorLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &uri, LayerType layerType );
|
||||
bool executeDeleteLayer( QString &errCause ) override;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief The QgsGeoPackageCollectionItem class is the base class for
|
||||
@ -91,16 +44,18 @@ class CORE_EXPORT QgsGeoPackageCollectionItem : public QgsDataCollectionItem
|
||||
static QgsLayerItem::LayerType layerTypeFromDb( const QString &geometryType );
|
||||
|
||||
//! Deletes a geopackage raster layer
|
||||
static bool deleteGeoPackageRasterLayer( const QString &uri, QString &errCause );
|
||||
bool deleteRasterLayer( const QString &layerName, QString &errCause );
|
||||
|
||||
//! Deletes a geopackage vector layer
|
||||
bool deleteVectorLayer( const QString &layerName, QString &errCause );
|
||||
|
||||
/**
|
||||
* Compacts (VACUUM) a geopackage database
|
||||
* \param path DB path
|
||||
* \param name DB name
|
||||
* \param name DB connection name
|
||||
* \param errCause contains the error message
|
||||
* \return true on success
|
||||
*/
|
||||
static bool vacuumGeoPackageDb( const QString &path, const QString &name, QString &errCause );
|
||||
static bool vacuumGeoPackageDb( const QString &name, QString &errCause );
|
||||
|
||||
void addConnection();
|
||||
void deleteConnection();
|
||||
@ -110,6 +65,66 @@ class CORE_EXPORT QgsGeoPackageCollectionItem : public QgsDataCollectionItem
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief The QgsGeoPackageAbstractLayerItem class is the base class for GeoPackage raster and vector layers
|
||||
*/
|
||||
class CORE_EXPORT QgsGeoPackageAbstractLayerItem : public QgsLayerItem
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Returns a list of all table names for the geopackage
|
||||
*/
|
||||
QStringList tableNames() const;
|
||||
|
||||
//! Checks if the data source has any layer in the current project returns them
|
||||
QList<QgsMapLayer *> layersInProject() const;
|
||||
|
||||
/**
|
||||
* Deletes a layer.
|
||||
* Subclasses need to implement this function with
|
||||
* the real deletion implementation
|
||||
*/
|
||||
virtual bool executeDeleteLayer( QString &errCause ) = 0;
|
||||
|
||||
/**
|
||||
* Returns the parent collection item
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
QgsGeoPackageCollectionItem *collection() const;
|
||||
|
||||
protected:
|
||||
QgsGeoPackageAbstractLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &uri, LayerType layerType, const QString &providerKey );
|
||||
|
||||
private:
|
||||
|
||||
//! Store a casted pointer to the parent collection
|
||||
QgsGeoPackageCollectionItem *mCollection = nullptr;
|
||||
};
|
||||
|
||||
class CORE_EXPORT QgsGeoPackageRasterLayerItem : public QgsGeoPackageAbstractLayerItem
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QgsGeoPackageRasterLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &uri );
|
||||
bool executeDeleteLayer( QString &errCause ) override;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CORE_EXPORT QgsGeoPackageVectorLayerItem : public QgsGeoPackageAbstractLayerItem
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QgsGeoPackageVectorLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &uri, LayerType layerType );
|
||||
bool executeDeleteLayer( QString &errCause ) override;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief The QgsGeoPackageConnectionItem class adds the stored
|
||||
* connection management to QgsGeoPackageCollectionItem
|
||||
|
321
src/core/providers/ogr/qgsgeopackageproviderconnection.cpp
Normal file
321
src/core/providers/ogr/qgsgeopackageproviderconnection.cpp
Normal file
@ -0,0 +1,321 @@
|
||||
/***************************************************************************
|
||||
QgsGeoPackageProviderConnection.cpp - QgsGeoPackageProviderConnection
|
||||
|
||||
---------------------
|
||||
begin : 6.8.2019
|
||||
copyright : (C) 2019 by Alessandro Pasotti
|
||||
email : elpaso at itopen dot it
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#include "qgsgeopackageproviderconnection.h"
|
||||
#include "qgsogrdbconnection.h"
|
||||
#include "qgssettings.h"
|
||||
#include "qgsogrprovider.h"
|
||||
#include "qgsmessagelog.h"
|
||||
#include "qgsproviderregistry.h"
|
||||
|
||||
// List of GPKG quoted system and dummy tables names to be excluded from the tables listing
|
||||
static const QStringList excludedTableNames { { QStringLiteral( "\"ogr_empty_table\"" ) } };
|
||||
|
||||
QgsGeoPackageProviderConnection::QgsGeoPackageProviderConnection( const QString &name ):
|
||||
QgsAbstractDatabaseProviderConnection( name )
|
||||
{
|
||||
setDefaultCapabilities();
|
||||
QgsSettings settings;
|
||||
settings.beginGroup( QStringLiteral( "ogr" ), QgsSettings::Section::Providers );
|
||||
settings.beginGroup( QStringLiteral( "GPKG" ) );
|
||||
settings.beginGroup( QStringLiteral( "connections" ) );
|
||||
settings.beginGroup( name );
|
||||
setUri( settings.value( QStringLiteral( "path" ) ).toString() );
|
||||
}
|
||||
|
||||
QgsGeoPackageProviderConnection::QgsGeoPackageProviderConnection( const QString &name, const QString &uri ):
|
||||
QgsAbstractDatabaseProviderConnection( name )
|
||||
{
|
||||
setDefaultCapabilities();
|
||||
setUri( uri );
|
||||
}
|
||||
|
||||
void QgsGeoPackageProviderConnection::store( const QVariantMap &configuration ) const
|
||||
{
|
||||
Q_UNUSED( configuration );
|
||||
QgsSettings settings;
|
||||
settings.beginGroup( QStringLiteral( "ogr" ), QgsSettings::Section::Providers );
|
||||
settings.beginGroup( QStringLiteral( "GPKG" ) );
|
||||
settings.beginGroup( QStringLiteral( "connections" ) );
|
||||
settings.beginGroup( name() );
|
||||
settings.setValue( QStringLiteral( "path" ), uri() );
|
||||
}
|
||||
|
||||
void QgsGeoPackageProviderConnection::remove() const
|
||||
{
|
||||
QgsSettings settings;
|
||||
settings.beginGroup( QStringLiteral( "ogr" ), QgsSettings::Section::Providers );
|
||||
settings.beginGroup( QStringLiteral( "GPKG" ) );
|
||||
settings.beginGroup( QStringLiteral( "connections" ) );
|
||||
settings.remove( name() );
|
||||
}
|
||||
|
||||
|
||||
void QgsGeoPackageProviderConnection::createVectorTable( const QString &schema,
|
||||
const QString &name,
|
||||
const QgsFields &fields,
|
||||
QgsWkbTypes::Type wkbType,
|
||||
const QgsCoordinateReferenceSystem &srs,
|
||||
bool overwrite,
|
||||
const QMap<QString, QVariant> *options ) const
|
||||
{
|
||||
checkCapability( Capability::CreateVectorTable );
|
||||
if ( ! schema.isEmpty() )
|
||||
{
|
||||
QgsMessageLog::logMessage( QStringLiteral( "Schema is not supported by GPKG, ignoring" ), QStringLiteral( "OGR" ), Qgis::Info );
|
||||
}
|
||||
QMap<QString, QVariant> opts { *options };
|
||||
opts[ QStringLiteral( "layerName" ) ] = QVariant( name );
|
||||
opts[ QStringLiteral( "update" ) ] = true;
|
||||
QMap<int, int> map;
|
||||
QString errCause;
|
||||
QgsVectorLayerExporter::ExportError errCode = QgsOgrProvider::createEmptyLayer(
|
||||
uri(),
|
||||
fields,
|
||||
wkbType,
|
||||
srs,
|
||||
overwrite,
|
||||
&map,
|
||||
&errCause,
|
||||
&opts
|
||||
);
|
||||
if ( errCode != QgsVectorLayerExporter::ExportError::NoError )
|
||||
{
|
||||
throw QgsProviderConnectionException( QObject::tr( "An error occurred while creating the vector layer: %1" ).arg( errCause ) );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsGeoPackageProviderConnection::dropVectorTable( const QString &schema, const QString &name ) const
|
||||
{
|
||||
checkCapability( Capability::DropVectorTable );
|
||||
if ( ! schema.isEmpty() )
|
||||
{
|
||||
QgsMessageLog::logMessage( QStringLiteral( "Schema is not supported by GPKG, ignoring" ), QStringLiteral( "OGR" ), Qgis::Info );
|
||||
}
|
||||
QString errCause;
|
||||
const QString layerUri { QStringLiteral( "%1|layername=%2" ).arg( uri(), name ) };
|
||||
if ( ! QgsOgrProviderUtils::deleteLayer( layerUri, errCause ) )
|
||||
{
|
||||
throw QgsProviderConnectionException( QObject::tr( "Error deleting vector/aspatial table %1: %2" ).arg( name ).arg( errCause ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void QgsGeoPackageProviderConnection::dropRasterTable( const QString &schema, const QString &name ) const
|
||||
{
|
||||
checkCapability( Capability::DropRasterTable );
|
||||
if ( ! schema.isEmpty() )
|
||||
{
|
||||
QgsMessageLog::logMessage( QStringLiteral( "Schema is not supported by GPKG, ignoring" ), QStringLiteral( "OGR" ), Qgis::Info );
|
||||
}
|
||||
executeGdalSqlPrivate( QStringLiteral( "DROP TABLE %1" ).arg( name ) );
|
||||
}
|
||||
|
||||
|
||||
void QgsGeoPackageProviderConnection::renameVectorTable( const QString &schema, const QString &name, const QString &newName ) const
|
||||
{
|
||||
checkCapability( Capability::RenameVectorTable );
|
||||
if ( ! schema.isEmpty() )
|
||||
{
|
||||
QgsMessageLog::logMessage( QStringLiteral( "Schema is not supported by GPKG, ignoring" ), QStringLiteral( "OGR" ), Qgis::Info );
|
||||
}
|
||||
// TODO: maybe an index?
|
||||
QString sql( QStringLiteral( "ALTER TABLE %1 RENAME TO %2" )
|
||||
.arg( QgsSqliteUtils::quotedIdentifier( name ),
|
||||
QgsSqliteUtils::quotedIdentifier( newName ) ) );
|
||||
executeGdalSqlPrivate( sql );
|
||||
sql = QStringLiteral( "UPDATE layer_styles SET f_table_name = %2 WHERE f_table_name = %1" )
|
||||
.arg( QgsSqliteUtils::quotedString( name ),
|
||||
QgsSqliteUtils::quotedString( newName ) );
|
||||
try
|
||||
{
|
||||
executeGdalSqlPrivate( sql );
|
||||
}
|
||||
catch ( QgsProviderConnectionException &ex )
|
||||
{
|
||||
QgsDebugMsgLevel( QStringLiteral( "Warning: error while updating the styles, perhaps there are no styles stored in this GPKG: %1" ).arg( ex.what() ), 4 );
|
||||
}
|
||||
}
|
||||
|
||||
QList<QList<QVariant>> QgsGeoPackageProviderConnection::executeSql( const QString &sql ) const
|
||||
{
|
||||
checkCapability( Capability::ExecuteSql );
|
||||
return executeGdalSqlPrivate( sql );
|
||||
}
|
||||
|
||||
void QgsGeoPackageProviderConnection::vacuum( const QString &schema, const QString &name ) const
|
||||
{
|
||||
Q_UNUSED( name );
|
||||
checkCapability( Capability::Vacuum );
|
||||
if ( ! schema.isEmpty() )
|
||||
{
|
||||
QgsMessageLog::logMessage( QStringLiteral( "Schema is not supported by GPKG, ignoring" ), QStringLiteral( "OGR" ), Qgis::Info );
|
||||
}
|
||||
executeGdalSqlPrivate( QStringLiteral( "VACUUM" ) );
|
||||
}
|
||||
|
||||
|
||||
QList<QgsGeoPackageProviderConnection::TableProperty> QgsGeoPackageProviderConnection::tables( const QString &schema, const TableFlags &flags ) const
|
||||
{
|
||||
checkCapability( Capability::Tables );
|
||||
if ( ! schema.isEmpty() )
|
||||
{
|
||||
QgsMessageLog::logMessage( QStringLiteral( "Schema is not supported by GPKG, ignoring" ), QStringLiteral( "OGR" ), Qgis::Info );
|
||||
}
|
||||
QList<QgsGeoPackageProviderConnection::TableProperty> tableInfo;
|
||||
QString errCause;
|
||||
QList<QVariantList> results;
|
||||
try
|
||||
{
|
||||
const QString sql { QStringLiteral( "SELECT c.table_name, data_type, description, c.srs_id, g.geometry_type_name, g.column_name "
|
||||
"FROM gpkg_contents c LEFT JOIN gpkg_geometry_columns g ON (c.table_name = g.table_name) "
|
||||
"WHERE c.table_name NOT IN (%1)" ).arg( excludedTableNames.join( ',' ) ) };
|
||||
results = executeSql( sql );
|
||||
for ( const auto &row : qgis::as_const( results ) )
|
||||
{
|
||||
if ( row.size() != 6 )
|
||||
{
|
||||
throw QgsProviderConnectionException( QObject::tr( "Error listing tables from %1: wrong number of columns returned by query" ).arg( name() ) );
|
||||
}
|
||||
QgsGeoPackageProviderConnection::TableProperty property;
|
||||
property.setTableName( row.at( 0 ).toString() );
|
||||
property.setPrimaryKeyColumns( { QStringLiteral( "fid" ) } );
|
||||
property.setGeometryColumnCount( 0 );
|
||||
static const QStringList aspatialTypes = { QStringLiteral( "attributes" ), QStringLiteral( "aspatial" ) };
|
||||
const QString dataType = row.at( 1 ).toString();
|
||||
// Table type
|
||||
if ( dataType == QStringLiteral( "tiles" ) || dataType == QStringLiteral( "2d-gridded-coverage" ) )
|
||||
{
|
||||
property.setFlag( QgsGeoPackageProviderConnection::Raster );
|
||||
}
|
||||
else if ( dataType == QStringLiteral( "features" ) )
|
||||
{
|
||||
property.setFlag( QgsGeoPackageProviderConnection::Vector );
|
||||
property.setGeometryColumn( row.at( 5 ).toString() );
|
||||
property.setGeometryColumnCount( 1 );
|
||||
}
|
||||
if ( aspatialTypes.contains( dataType ) )
|
||||
{
|
||||
property.setFlag( QgsGeoPackageProviderConnection::Aspatial );
|
||||
property.addGeometryColumnType( QgsWkbTypes::Type::NoGeometry, QgsCoordinateReferenceSystem() );
|
||||
}
|
||||
else
|
||||
{
|
||||
bool ok;
|
||||
int srid = row.at( 3 ).toInt( &ok );
|
||||
if ( !ok )
|
||||
{
|
||||
throw QgsProviderConnectionException( QObject::tr( "Error fetching srs_id table information: %1" ).arg( row.at( 3 ).toString() ) );
|
||||
}
|
||||
QgsCoordinateReferenceSystem crs = QgsCoordinateReferenceSystem::fromEpsgId( srid );
|
||||
property.addGeometryColumnType( QgsWkbTypes::parseType( row.at( 4 ).toString() ), crs );
|
||||
}
|
||||
property.setComment( row.at( 4 ).toString() );
|
||||
tableInfo.push_back( property );
|
||||
}
|
||||
|
||||
}
|
||||
catch ( QgsProviderConnectionException &ex )
|
||||
{
|
||||
errCause = ex.what();
|
||||
}
|
||||
|
||||
if ( ! errCause.isEmpty() )
|
||||
{
|
||||
throw QgsProviderConnectionException( QObject::tr( "Error listing tables from %1: %2" ).arg( name() ).arg( errCause ) );
|
||||
}
|
||||
// Filters
|
||||
if ( flags )
|
||||
{
|
||||
tableInfo.erase( std::remove_if( tableInfo.begin(), tableInfo.end(), [ & ]( const QgsAbstractDatabaseProviderConnection::TableProperty & ti )
|
||||
{
|
||||
return !( ti.flags() & flags );
|
||||
} ), tableInfo.end() );
|
||||
}
|
||||
return tableInfo ;
|
||||
}
|
||||
|
||||
void QgsGeoPackageProviderConnection::setDefaultCapabilities()
|
||||
{
|
||||
mCapabilities =
|
||||
{
|
||||
Capability::Tables,
|
||||
Capability::CreateVectorTable,
|
||||
Capability::DropVectorTable,
|
||||
Capability::RenameVectorTable,
|
||||
Capability::Vacuum,
|
||||
Capability::Spatial,
|
||||
Capability::TableExists,
|
||||
Capability::ExecuteSql,
|
||||
};
|
||||
#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,4,0)
|
||||
mCapabilities |= Capability::DropRasterTable;
|
||||
#endif
|
||||
}
|
||||
|
||||
QList<QVariantList> QgsGeoPackageProviderConnection::executeGdalSqlPrivate( const QString &sql ) const
|
||||
{
|
||||
QString errCause;
|
||||
QList<QVariantList> results;
|
||||
gdal::ogr_datasource_unique_ptr hDS( GDALOpenEx( uri().toUtf8().constData(), GDAL_OF_VECTOR | GDAL_OF_UPDATE, nullptr, nullptr, nullptr ) );
|
||||
if ( hDS )
|
||||
{
|
||||
OGRLayerH ogrLayer( GDALDatasetExecuteSQL( hDS.get(), sql.toUtf8().constData(), nullptr, nullptr ) );
|
||||
if ( ogrLayer )
|
||||
{
|
||||
gdal::ogr_feature_unique_ptr fet;
|
||||
QgsFields fields;
|
||||
while ( fet.reset( OGR_L_GetNextFeature( ogrLayer ) ), fet )
|
||||
{
|
||||
QVariantList row;
|
||||
// Try to get the right type for the returned values
|
||||
if ( fields.isEmpty() )
|
||||
{
|
||||
fields = QgsOgrUtils::readOgrFields( fet.get(), QTextCodec::codecForName( "UTF-8" ) );
|
||||
}
|
||||
if ( ! fields.isEmpty() )
|
||||
{
|
||||
QgsFeature f { QgsOgrUtils::readOgrFeature( fet.get(), fields, QTextCodec::codecForName( "UTF-8" ) ) };
|
||||
const QgsAttributes &constAttrs { f.attributes() };
|
||||
for ( int i = 0; i < constAttrs.length(); i++ )
|
||||
{
|
||||
row.push_back( constAttrs.at( i ) );
|
||||
}
|
||||
}
|
||||
else // Fallback to strings
|
||||
{
|
||||
for ( int i = 0; i < OGR_F_GetFieldCount( fet.get() ); i++ )
|
||||
{
|
||||
row.push_back( QVariant( QString::fromUtf8( OGR_F_GetFieldAsString( fet.get(), i ) ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
results.push_back( row );
|
||||
}
|
||||
GDALDatasetReleaseResultSet( hDS.get(), ogrLayer );
|
||||
}
|
||||
errCause = CPLGetLastErrorMsg( );
|
||||
}
|
||||
else
|
||||
{
|
||||
errCause = QObject::tr( "There was an error opening GPKG %1!" ).arg( uri() );
|
||||
}
|
||||
if ( ! errCause.isEmpty() )
|
||||
{
|
||||
throw QgsProviderConnectionException( QObject::tr( "Error executing SQL %1: %2" ).arg( sql ).arg( errCause ) );
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
54
src/core/providers/ogr/qgsgeopackageproviderconnection.h
Normal file
54
src/core/providers/ogr/qgsgeopackageproviderconnection.h
Normal file
@ -0,0 +1,54 @@
|
||||
/***************************************************************************
|
||||
QgsGeoPackageProviderConnection.h - QgsGeoPackageProviderConnection
|
||||
|
||||
---------------------
|
||||
begin : 6.8.2019
|
||||
copyright : (C) 2019 by Alessandro Pasotti
|
||||
email : elpaso at itopen dot it
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#ifndef QGSGEOPACKAGEPROVIDERCONNECTION_H
|
||||
#define QGSGEOPACKAGEPROVIDERCONNECTION_H
|
||||
|
||||
#include "qgsabstractdatabaseproviderconnection.h"
|
||||
|
||||
///@cond PRIVATE
|
||||
#define SIP_NO_FILE
|
||||
|
||||
class QgsGeoPackageProviderConnection : public QgsAbstractDatabaseProviderConnection
|
||||
{
|
||||
public:
|
||||
|
||||
QgsGeoPackageProviderConnection( const QString &name );
|
||||
QgsGeoPackageProviderConnection( const QString &name, const QString &uri );
|
||||
|
||||
|
||||
// QgsAbstractProviderConnection interface
|
||||
public:
|
||||
void store( const QVariantMap &configuration ) const override;
|
||||
void remove() const override;
|
||||
void createVectorTable( const QString &schema, const QString &name, const QgsFields &fields, QgsWkbTypes::Type wkbType, const QgsCoordinateReferenceSystem &srs, bool overwrite, const QMap<QString, QVariant> *options ) const override;
|
||||
void dropVectorTable( const QString &schema, const QString &name ) const override;
|
||||
void dropRasterTable( const QString &schema, const QString &name ) const override;
|
||||
void renameVectorTable( const QString &schema, const QString &name, const QString &newName ) const override;
|
||||
QList<QList<QVariant>> executeSql( const QString &sql ) const override;
|
||||
void vacuum( const QString &schema, const QString &name ) const override;
|
||||
QList<QgsAbstractDatabaseProviderConnection::TableProperty> tables( const QString &schema = QString(),
|
||||
const TableFlags &flags = nullptr ) const override;
|
||||
|
||||
private:
|
||||
|
||||
void setDefaultCapabilities();
|
||||
//! Use GDAL to execute SQL
|
||||
QList<QVariantList> executeGdalSqlPrivate( const QString &sql ) const;
|
||||
|
||||
};
|
||||
|
||||
///@endcond
|
||||
#endif // QGSGEOPACKAGEPROVIDERCONNECTION_H
|
@ -372,7 +372,7 @@ QVector<QgsDataItem *> QgsOgrDataCollectionItem::createChildren()
|
||||
return children;
|
||||
}
|
||||
|
||||
bool QgsOgrDataCollectionItem::storeConnection( const QString &path, const QString &ogrDriverName )
|
||||
bool QgsOgrDataCollectionItem::saveConnection( const QString &path, const QString &ogrDriverName )
|
||||
{
|
||||
QFileInfo fileInfo( path );
|
||||
QString connName = fileInfo.fileName();
|
||||
@ -400,7 +400,7 @@ bool QgsOgrDataCollectionItem::storeConnection( const QString &path, const QStri
|
||||
bool QgsOgrDataCollectionItem::createConnection( const QString &name, const QString &extensions, const QString &ogrDriverName )
|
||||
{
|
||||
QString path = QFileDialog::getOpenFileName( nullptr, tr( "Open %1" ).arg( name ), QString(), extensions );
|
||||
return storeConnection( path, ogrDriverName );
|
||||
return saveConnection( path, ogrDriverName );
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
@ -87,7 +87,7 @@ class CORE_EXPORT QgsOgrDataCollectionItem : public QgsDataCollectionItem
|
||||
* \param path to the DB
|
||||
* \param ogrDriverName the OGR/GDAL driver name (e.g. "GPKG")
|
||||
*/
|
||||
static bool storeConnection( const QString &path, const QString &ogrDriverName );
|
||||
static bool saveConnection( const QString &path, const QString &ogrDriverName );
|
||||
|
||||
/**
|
||||
* Utility function to create and store a new DB connection
|
||||
|
@ -70,10 +70,10 @@ QString QgsOgrDbConnection::connectionsPath( const QString &settingsKey )
|
||||
return QStringLiteral( "%1/connections" ).arg( fullKey( settingsKey ) );
|
||||
}
|
||||
|
||||
const QStringList QgsOgrDbConnection::connectionList( const QString &settingsKey )
|
||||
const QStringList QgsOgrDbConnection::connectionList( const QString &driverName )
|
||||
{
|
||||
QgsSettings settings;
|
||||
settings.beginGroup( connectionsPath( settingsKey ) );
|
||||
settings.beginGroup( connectionsPath( driverName ) );
|
||||
return settings.childGroups();
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ class CORE_EXPORT QgsOgrDbConnection : public QObject
|
||||
//! Constructor
|
||||
explicit QgsOgrDbConnection( const QString &connName, const QString &settingsKey );
|
||||
|
||||
static const QStringList connectionList( const QString &settingsKey );
|
||||
static const QStringList connectionList( const QString &driverName = QStringLiteral( "GPKG" ) );
|
||||
static void deleteConnection( const QString &connName, const QString &settingsKey );
|
||||
static QString selectedConnection( const QString &settingsKey );
|
||||
static void setSelectedConnection( const QString &connName, const QString &settingsKey );
|
||||
|
@ -43,6 +43,8 @@ email : sherman at mrcc.com
|
||||
#include "qgsgeopackageprojectstorage.h"
|
||||
#include "qgsprojectstorageregistry.h"
|
||||
#include "qgsprovidermetadata.h"
|
||||
#include "qgsogrdbconnection.h"
|
||||
#include "qgsgeopackageproviderconnection.h"
|
||||
|
||||
#include "qgis.h"
|
||||
|
||||
@ -251,116 +253,10 @@ QgsVectorLayerExporter::ExportError QgsOgrProviderMetadata::createEmptyLayer( co
|
||||
QString &errorMessage,
|
||||
const QMap<QString, QVariant> *options )
|
||||
{
|
||||
QString encoding;
|
||||
QString driverName = QStringLiteral( "GPKG" );
|
||||
QStringList dsOptions, layerOptions;
|
||||
QString layerName;
|
||||
|
||||
if ( options )
|
||||
{
|
||||
if ( options->contains( QStringLiteral( "fileEncoding" ) ) )
|
||||
encoding = options->value( QStringLiteral( "fileEncoding" ) ).toString();
|
||||
|
||||
if ( options->contains( QStringLiteral( "driverName" ) ) )
|
||||
driverName = options->value( QStringLiteral( "driverName" ) ).toString();
|
||||
|
||||
if ( options->contains( QStringLiteral( "datasourceOptions" ) ) )
|
||||
dsOptions << options->value( QStringLiteral( "datasourceOptions" ) ).toStringList();
|
||||
|
||||
if ( options->contains( QStringLiteral( "layerOptions" ) ) )
|
||||
layerOptions << options->value( QStringLiteral( "layerOptions" ) ).toStringList();
|
||||
|
||||
if ( options->contains( QStringLiteral( "layerName" ) ) )
|
||||
layerName = options->value( QStringLiteral( "layerName" ) ).toString();
|
||||
}
|
||||
|
||||
oldToNewAttrIdxMap.clear();
|
||||
errorMessage.clear();
|
||||
|
||||
QgsVectorFileWriter::ActionOnExistingFile action( QgsVectorFileWriter::CreateOrOverwriteFile );
|
||||
|
||||
bool update = false;
|
||||
if ( options && options->contains( QStringLiteral( "update" ) ) )
|
||||
{
|
||||
update = options->value( QStringLiteral( "update" ) ).toBool();
|
||||
if ( update )
|
||||
{
|
||||
if ( !overwrite && !layerName.isEmpty() )
|
||||
{
|
||||
gdal::dataset_unique_ptr hDS( GDALOpenEx( uri.toUtf8().constData(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr ) );
|
||||
if ( hDS )
|
||||
{
|
||||
if ( GDALDatasetGetLayerByName( hDS.get(), layerName.toUtf8().constData() ) )
|
||||
{
|
||||
errorMessage += QObject::tr( "Layer %2 of %1 exists and overwrite flag is false." )
|
||||
.arg( uri, layerName );
|
||||
return QgsVectorLayerExporter::ErrCreateDataSource;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( QFileInfo::exists( uri ) )
|
||||
action = QgsVectorFileWriter::CreateOrOverwriteLayer;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !overwrite && !update )
|
||||
{
|
||||
if ( QFileInfo::exists( uri ) )
|
||||
{
|
||||
errorMessage += QObject::tr( "Unable to create the datasource. %1 exists and overwrite flag is false." )
|
||||
.arg( uri );
|
||||
return QgsVectorLayerExporter::ErrCreateDataSource;
|
||||
}
|
||||
}
|
||||
|
||||
QString newLayerName( layerName );
|
||||
std::unique_ptr< QgsVectorFileWriter > writer = qgis::make_unique< QgsVectorFileWriter >(
|
||||
uri, encoding, fields, wkbType,
|
||||
srs, driverName, dsOptions, layerOptions, nullptr,
|
||||
QgsVectorFileWriter::NoSymbology, nullptr,
|
||||
layerName, action, &newLayerName );
|
||||
layerName = newLayerName;
|
||||
|
||||
QgsVectorFileWriter::WriterError error = writer->hasError();
|
||||
if ( error )
|
||||
{
|
||||
errorMessage += writer->errorMessage();
|
||||
|
||||
return ( QgsVectorLayerExporter::ExportError ) error;
|
||||
}
|
||||
|
||||
QMap<int, int> attrIdxMap = writer->attrIdxToOgrIdx();
|
||||
writer.reset();
|
||||
|
||||
{
|
||||
bool firstFieldIsFid = false;
|
||||
if ( !layerName.isEmpty() )
|
||||
{
|
||||
gdal::dataset_unique_ptr hDS( GDALOpenEx( uri.toUtf8().constData(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr ) );
|
||||
if ( hDS )
|
||||
{
|
||||
OGRLayerH hLayer = GDALDatasetGetLayerByName( hDS.get(), layerName.toUtf8().constData() );
|
||||
if ( hLayer )
|
||||
{
|
||||
// Expose the OGR FID if it comes from a "real" column (typically GPKG)
|
||||
// and make sure that this FID column is not exposed as a regular OGR field (shouldn't happen normally)
|
||||
firstFieldIsFid = !( EQUAL( OGR_L_GetFIDColumn( hLayer ), "" ) ) &&
|
||||
OGR_FD_GetFieldIndex( OGR_L_GetLayerDefn( hLayer ), OGR_L_GetFIDColumn( hLayer ) ) < 0 &&
|
||||
fields.indexFromName( OGR_L_GetFIDColumn( hLayer ) ) < 0;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ( QMap<int, int>::const_iterator attrIt = attrIdxMap.constBegin(); attrIt != attrIdxMap.constEnd(); ++attrIt )
|
||||
{
|
||||
oldToNewAttrIdxMap.insert( attrIt.key(), *attrIt + ( firstFieldIsFid ? 1 : 0 ) );
|
||||
}
|
||||
}
|
||||
|
||||
QgsOgrProviderUtils::invalidateCachedLastModifiedDate( uri );
|
||||
|
||||
return QgsVectorLayerExporter::NoError;
|
||||
return QgsOgrProvider::createEmptyLayer(
|
||||
uri, fields, wkbType, srs, overwrite,
|
||||
&oldToNewAttrIdxMap, &errorMessage, options
|
||||
);
|
||||
}
|
||||
|
||||
static QString AnalyzeURI( QString const &uri,
|
||||
@ -439,6 +335,131 @@ static QString AnalyzeURI( QString const &uri,
|
||||
|
||||
}
|
||||
|
||||
QgsVectorLayerExporter::ExportError QgsOgrProvider::createEmptyLayer( const QString &uri,
|
||||
const QgsFields &fields,
|
||||
QgsWkbTypes::Type wkbType,
|
||||
const QgsCoordinateReferenceSystem &srs,
|
||||
bool overwrite,
|
||||
QMap<int, int> *oldToNewAttrIdxMap,
|
||||
QString *errorMessage,
|
||||
const QMap<QString, QVariant> *options )
|
||||
{
|
||||
QString encoding;
|
||||
QString driverName = QStringLiteral( "GPKG" );
|
||||
QStringList dsOptions, layerOptions;
|
||||
QString layerName;
|
||||
|
||||
if ( options )
|
||||
{
|
||||
if ( options->contains( QStringLiteral( "fileEncoding" ) ) )
|
||||
encoding = options->value( QStringLiteral( "fileEncoding" ) ).toString();
|
||||
|
||||
if ( options->contains( QStringLiteral( "driverName" ) ) )
|
||||
driverName = options->value( QStringLiteral( "driverName" ) ).toString();
|
||||
|
||||
if ( options->contains( QStringLiteral( "datasourceOptions" ) ) )
|
||||
dsOptions << options->value( QStringLiteral( "datasourceOptions" ) ).toStringList();
|
||||
|
||||
if ( options->contains( QStringLiteral( "layerOptions" ) ) )
|
||||
layerOptions << options->value( QStringLiteral( "layerOptions" ) ).toStringList();
|
||||
|
||||
if ( options->contains( QStringLiteral( "layerName" ) ) )
|
||||
layerName = options->value( QStringLiteral( "layerName" ) ).toString();
|
||||
}
|
||||
|
||||
oldToNewAttrIdxMap->clear();
|
||||
if ( errorMessage )
|
||||
errorMessage->clear();
|
||||
|
||||
QgsVectorFileWriter::ActionOnExistingFile action( QgsVectorFileWriter::CreateOrOverwriteFile );
|
||||
|
||||
bool update = false;
|
||||
if ( options && options->contains( QStringLiteral( "update" ) ) )
|
||||
{
|
||||
update = options->value( QStringLiteral( "update" ) ).toBool();
|
||||
if ( update )
|
||||
{
|
||||
if ( !overwrite && !layerName.isEmpty() )
|
||||
{
|
||||
gdal::dataset_unique_ptr hDS( GDALOpenEx( uri.toUtf8().constData(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr ) );
|
||||
if ( hDS )
|
||||
{
|
||||
if ( GDALDatasetGetLayerByName( hDS.get(), layerName.toUtf8().constData() ) )
|
||||
{
|
||||
if ( errorMessage )
|
||||
*errorMessage += QObject::tr( "Layer %2 of %1 exists and overwrite flag is false." )
|
||||
.arg( uri, layerName );
|
||||
return QgsVectorLayerExporter::ErrCreateDataSource;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( QFileInfo::exists( uri ) )
|
||||
action = QgsVectorFileWriter::CreateOrOverwriteLayer;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !overwrite && !update )
|
||||
{
|
||||
if ( QFileInfo::exists( uri ) )
|
||||
{
|
||||
if ( errorMessage )
|
||||
*errorMessage += QObject::tr( "Unable to create the datasource. %1 exists and overwrite flag is false." )
|
||||
.arg( uri );
|
||||
return QgsVectorLayerExporter::ErrCreateDataSource;
|
||||
}
|
||||
}
|
||||
|
||||
QString newLayerName( layerName );
|
||||
std::unique_ptr< QgsVectorFileWriter > writer = qgis::make_unique< QgsVectorFileWriter >(
|
||||
uri, encoding, fields, wkbType,
|
||||
srs, driverName, dsOptions, layerOptions, nullptr,
|
||||
QgsVectorFileWriter::NoSymbology, nullptr,
|
||||
layerName, action, &newLayerName );
|
||||
layerName = newLayerName;
|
||||
|
||||
QgsVectorFileWriter::WriterError error = writer->hasError();
|
||||
if ( error )
|
||||
{
|
||||
if ( errorMessage )
|
||||
*errorMessage += writer->errorMessage();
|
||||
|
||||
return static_cast<QgsVectorLayerExporter::ExportError>( error );
|
||||
}
|
||||
|
||||
QMap<int, int> attrIdxMap = writer->attrIdxToOgrIdx();
|
||||
writer.reset();
|
||||
|
||||
{
|
||||
bool firstFieldIsFid = false;
|
||||
if ( !layerName.isEmpty() )
|
||||
{
|
||||
gdal::dataset_unique_ptr hDS( GDALOpenEx( uri.toUtf8().constData(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr ) );
|
||||
if ( hDS )
|
||||
{
|
||||
OGRLayerH hLayer = GDALDatasetGetLayerByName( hDS.get(), layerName.toUtf8().constData() );
|
||||
if ( hLayer )
|
||||
{
|
||||
// Expose the OGR FID if it comes from a "real" column (typically GPKG)
|
||||
// and make sure that this FID column is not exposed as a regular OGR field (shouldn't happen normally)
|
||||
firstFieldIsFid = !( EQUAL( OGR_L_GetFIDColumn( hLayer ), "" ) ) &&
|
||||
OGR_FD_GetFieldIndex( OGR_L_GetLayerDefn( hLayer ), OGR_L_GetFIDColumn( hLayer ) ) < 0 &&
|
||||
fields.indexFromName( OGR_L_GetFIDColumn( hLayer ) ) < 0;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ( QMap<int, int>::const_iterator attrIt = attrIdxMap.constBegin(); attrIt != attrIdxMap.constEnd(); ++attrIt )
|
||||
{
|
||||
oldToNewAttrIdxMap->insert( attrIt.key(), *attrIt + ( firstFieldIsFid ? 1 : 0 ) );
|
||||
}
|
||||
}
|
||||
|
||||
QgsOgrProviderUtils::invalidateCachedLastModifiedDate( uri );
|
||||
|
||||
return QgsVectorLayerExporter::NoError;
|
||||
}
|
||||
|
||||
QgsOgrProvider::QgsOgrProvider( QString const &uri, const ProviderOptions &options )
|
||||
: QgsVectorDataProvider( uri, options )
|
||||
{
|
||||
@ -6646,4 +6667,31 @@ QString QgsOgrProviderMetadata::filters( FilterType type )
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QMap<QString, QgsAbstractProviderConnection *> QgsOgrProviderMetadata::connections( bool cached )
|
||||
{
|
||||
return connectionsProtected<QgsGeoPackageProviderConnection, QgsOgrDbConnection>( cached );
|
||||
}
|
||||
|
||||
QgsAbstractProviderConnection *QgsOgrProviderMetadata::createConnection( const QString &connName )
|
||||
{
|
||||
return new QgsGeoPackageProviderConnection( connName );
|
||||
}
|
||||
|
||||
QgsAbstractProviderConnection *QgsOgrProviderMetadata::createConnection( const QString &connName, const QString &uri )
|
||||
{
|
||||
return new QgsGeoPackageProviderConnection( connName, uri );
|
||||
}
|
||||
|
||||
void QgsOgrProviderMetadata::deleteConnection( const QString &name )
|
||||
{
|
||||
deleteConnectionProtected<QgsGeoPackageProviderConnection>( name );
|
||||
}
|
||||
|
||||
void QgsOgrProviderMetadata::saveConnection( QgsAbstractProviderConnection *conn, const QVariantMap &configuration )
|
||||
{
|
||||
saveConnectionProtected( conn, configuration );
|
||||
}
|
||||
|
||||
///@endcond
|
||||
|
||||
|
@ -77,7 +77,7 @@ class QgsOgrProvider : public QgsVectorDataProvider
|
||||
bool overwrite,
|
||||
QMap<int, int> *oldToNewAttrIdxMap,
|
||||
QString *errorMessage = nullptr,
|
||||
const QMap<QString, QVariant> *coordinateTransformContext = nullptr
|
||||
const QMap<QString, QVariant> *options = nullptr
|
||||
);
|
||||
|
||||
/**
|
||||
@ -743,7 +743,9 @@ class QgsOgrLayer
|
||||
class QgsOgrProviderMetadata: public QgsProviderMetadata
|
||||
{
|
||||
public:
|
||||
|
||||
QgsOgrProviderMetadata();
|
||||
|
||||
void initProvider() override;
|
||||
void cleanupProvider() override;
|
||||
QList< QgsDataItemProvider * > dataItemProviders() const override;
|
||||
@ -772,6 +774,15 @@ class QgsOgrProviderMetadata: public QgsProviderMetadata
|
||||
|
||||
// -----
|
||||
QgsTransaction *createTransaction( const QString &connString ) override;
|
||||
|
||||
// QgsProviderMetadata interface
|
||||
public:
|
||||
QMap<QString, QgsAbstractProviderConnection *> connections( bool cached ) override;
|
||||
QgsAbstractProviderConnection *createConnection( const QString &name ) override;
|
||||
QgsAbstractProviderConnection *createConnection( const QString &name, const QString &uri ) override;
|
||||
void deleteConnection( const QString &name ) override;
|
||||
void saveConnection( QgsAbstractProviderConnection *createConnection, const QVariantMap &configuration ) override;
|
||||
|
||||
};
|
||||
|
||||
///@endcond
|
||||
|
302
src/core/qgsabstractdatabaseproviderconnection.cpp
Normal file
302
src/core/qgsabstractdatabaseproviderconnection.cpp
Normal file
@ -0,0 +1,302 @@
|
||||
/***************************************************************************
|
||||
qgsabstractdatabaseproviderconnection.cpp - QgsAbstractDatabaseProviderConnection
|
||||
|
||||
---------------------
|
||||
begin : 2.8.2019
|
||||
copyright : (C) 2019 by Alessandro Pasotti
|
||||
email : elpaso at itopen dot it
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#include "qgsabstractdatabaseproviderconnection.h"
|
||||
#include "qgsexception.h"
|
||||
#include <QVariant>
|
||||
#include <QObject>
|
||||
|
||||
QgsAbstractDatabaseProviderConnection::QgsAbstractDatabaseProviderConnection( const QString &name ):
|
||||
QgsAbstractProviderConnection( name )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QgsAbstractDatabaseProviderConnection::QgsAbstractDatabaseProviderConnection( const QString &name, const QString &uri ):
|
||||
QgsAbstractProviderConnection( name, uri )
|
||||
{
|
||||
|
||||
}
|
||||
QgsAbstractDatabaseProviderConnection::Capabilities QgsAbstractDatabaseProviderConnection::capabilities() const
|
||||
{
|
||||
return mCapabilities;
|
||||
}
|
||||
|
||||
///@cond PRIVATE
|
||||
void QgsAbstractDatabaseProviderConnection::checkCapability( QgsAbstractDatabaseProviderConnection::Capability capability ) const
|
||||
{
|
||||
if ( ! mCapabilities & capability )
|
||||
{
|
||||
static QMetaEnum metaEnum = QMetaEnum::fromType<QgsAbstractDatabaseProviderConnection::Capability>();
|
||||
const QString capName { metaEnum.valueToKey( capability ) };
|
||||
throw QgsProviderConnectionException( QObject::tr( "Operation '%1' is not supported for this connection" ).arg( capName ) );
|
||||
}
|
||||
}
|
||||
///@endcond
|
||||
|
||||
void QgsAbstractDatabaseProviderConnection::createVectorTable( const QString &schema,
|
||||
const QString &name,
|
||||
const QgsFields &fields,
|
||||
QgsWkbTypes::Type wkbType,
|
||||
const QgsCoordinateReferenceSystem &srs,
|
||||
bool overwrite,
|
||||
const QMap<QString, QVariant> *
|
||||
options ) const
|
||||
{
|
||||
Q_UNUSED( schema );
|
||||
Q_UNUSED( name );
|
||||
Q_UNUSED( fields );
|
||||
Q_UNUSED( srs );
|
||||
Q_UNUSED( overwrite );
|
||||
Q_UNUSED( options );
|
||||
Q_UNUSED( wkbType );
|
||||
throw QgsProviderConnectionException( QObject::tr( "Operation 'createVectorTable' is not supported" ) );
|
||||
}
|
||||
|
||||
void QgsAbstractDatabaseProviderConnection::renameVectorTable( const QString &, const QString &, const QString & ) const
|
||||
{
|
||||
checkCapability( Capability::RenameVectorTable );
|
||||
}
|
||||
|
||||
void QgsAbstractDatabaseProviderConnection::renameRasterTable( const QString &, const QString &, const QString & ) const
|
||||
{
|
||||
checkCapability( Capability::RenameRasterTable );
|
||||
}
|
||||
|
||||
void QgsAbstractDatabaseProviderConnection::dropVectorTable( const QString &, const QString & ) const
|
||||
{
|
||||
checkCapability( Capability::DropVectorTable );
|
||||
}
|
||||
|
||||
bool QgsAbstractDatabaseProviderConnection::tableExists( const QString &schema, const QString &name ) const
|
||||
{
|
||||
checkCapability( Capability::TableExists );
|
||||
const QList<QgsAbstractDatabaseProviderConnection::TableProperty> constTables { tables( schema ) };
|
||||
for ( const auto &t : constTables )
|
||||
{
|
||||
if ( t.tableName() == name )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void QgsAbstractDatabaseProviderConnection::dropRasterTable( const QString &, const QString & ) const
|
||||
{
|
||||
checkCapability( Capability::DropRasterTable );
|
||||
}
|
||||
|
||||
void QgsAbstractDatabaseProviderConnection::createSchema( const QString & ) const
|
||||
{
|
||||
checkCapability( Capability::CreateSchema );
|
||||
}
|
||||
|
||||
void QgsAbstractDatabaseProviderConnection::dropSchema( const QString &, bool ) const
|
||||
{
|
||||
checkCapability( Capability::DropSchema );
|
||||
}
|
||||
|
||||
void QgsAbstractDatabaseProviderConnection::renameSchema( const QString &, const QString & ) const
|
||||
{
|
||||
checkCapability( Capability::RenameSchema );
|
||||
}
|
||||
|
||||
QList<QList<QVariant>> QgsAbstractDatabaseProviderConnection::executeSql( const QString & ) const
|
||||
{
|
||||
checkCapability( Capability::ExecuteSql );
|
||||
return QList<QList<QVariant>>();
|
||||
}
|
||||
|
||||
void QgsAbstractDatabaseProviderConnection::vacuum( const QString &, const QString & ) const
|
||||
{
|
||||
checkCapability( Capability::Vacuum );
|
||||
}
|
||||
|
||||
QList<QgsAbstractDatabaseProviderConnection::TableProperty> QgsAbstractDatabaseProviderConnection::tables( const QString &, const QgsAbstractDatabaseProviderConnection::TableFlags & ) const
|
||||
{
|
||||
checkCapability( Capability::Tables );
|
||||
return QList<QgsAbstractDatabaseProviderConnection::TableProperty>();
|
||||
}
|
||||
|
||||
QList<QgsAbstractDatabaseProviderConnection::TableProperty> QgsAbstractDatabaseProviderConnection::tablesInt( const QString &schema, const int flags ) const
|
||||
{
|
||||
return tables( schema, static_cast<QgsAbstractDatabaseProviderConnection::TableFlags>( flags ) );
|
||||
}
|
||||
|
||||
|
||||
QStringList QgsAbstractDatabaseProviderConnection::schemas( ) const
|
||||
{
|
||||
checkCapability( Capability::Schemas );
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
QString QgsAbstractDatabaseProviderConnection::TableProperty::tableName() const
|
||||
{
|
||||
return mTableName;
|
||||
}
|
||||
|
||||
void QgsAbstractDatabaseProviderConnection::TableProperty::setTableName( const QString &name )
|
||||
{
|
||||
mTableName = name;
|
||||
}
|
||||
|
||||
void QgsAbstractDatabaseProviderConnection::TableProperty::addGeometryColumnType( const QgsWkbTypes::Type &type, const QgsCoordinateReferenceSystem &crs )
|
||||
{
|
||||
// Do not add the type if it's already present
|
||||
const QgsAbstractDatabaseProviderConnection::TableProperty::GeometryColumnType toAdd { type, crs };
|
||||
for ( const auto &t : qgis::as_const( mGeometryColumnTypes ) )
|
||||
{
|
||||
if ( t == toAdd )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
mGeometryColumnTypes.push_back( toAdd );
|
||||
}
|
||||
|
||||
QList<QgsAbstractDatabaseProviderConnection::TableProperty::GeometryColumnType> QgsAbstractDatabaseProviderConnection::TableProperty::geometryColumnTypes() const
|
||||
{
|
||||
return mGeometryColumnTypes;
|
||||
}
|
||||
|
||||
|
||||
QString QgsAbstractDatabaseProviderConnection::TableProperty::defaultName() const
|
||||
{
|
||||
QString n = mTableName;
|
||||
if ( mGeometryColumnCount > 1 ) n += '.' + mGeometryColumn;
|
||||
return n;
|
||||
}
|
||||
|
||||
QgsAbstractDatabaseProviderConnection::TableProperty QgsAbstractDatabaseProviderConnection::TableProperty::at( int index ) const
|
||||
{
|
||||
TableProperty property;
|
||||
|
||||
Q_ASSERT( index >= 0 && index < mGeometryColumnTypes.size() );
|
||||
|
||||
property.mGeometryColumnTypes << mGeometryColumnTypes[ index ];
|
||||
property.mSchema = mSchema;
|
||||
property.mTableName = mTableName;
|
||||
property.mGeometryColumn = mGeometryColumn;
|
||||
property.mPkColumns = mPkColumns;
|
||||
property.mGeometryColumnCount = mGeometryColumnCount;
|
||||
property.mFlags = mFlags;
|
||||
property.mComment = mComment;
|
||||
property.mInfo = mInfo;
|
||||
return property;
|
||||
}
|
||||
|
||||
void QgsAbstractDatabaseProviderConnection::TableProperty::setFlag( const QgsAbstractDatabaseProviderConnection::TableFlag &flag )
|
||||
{
|
||||
mFlags.setFlag( flag );
|
||||
}
|
||||
|
||||
int QgsAbstractDatabaseProviderConnection::TableProperty::maxCoordinateDimensions() const
|
||||
{
|
||||
int res = 0;
|
||||
for ( const TableProperty::GeometryColumnType &ct : qgis::as_const( mGeometryColumnTypes ) )
|
||||
{
|
||||
res = std::max( res, QgsWkbTypes::coordDimensions( ct.wkbType ) );
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void QgsAbstractDatabaseProviderConnection::TableProperty::setGeometryColumnTypes( const QList<QgsAbstractDatabaseProviderConnection::TableProperty::GeometryColumnType> &columnTypes )
|
||||
{
|
||||
mGeometryColumnTypes = columnTypes;
|
||||
}
|
||||
|
||||
|
||||
int QgsAbstractDatabaseProviderConnection::TableProperty::geometryColumnCount() const
|
||||
{
|
||||
return mGeometryColumnCount;
|
||||
}
|
||||
|
||||
void QgsAbstractDatabaseProviderConnection::TableProperty::setGeometryColumnCount( int geometryColumnCount )
|
||||
{
|
||||
mGeometryColumnCount = geometryColumnCount;
|
||||
}
|
||||
|
||||
QVariantMap QgsAbstractDatabaseProviderConnection::TableProperty::info() const
|
||||
{
|
||||
return mInfo;
|
||||
}
|
||||
|
||||
void QgsAbstractDatabaseProviderConnection::TableProperty::setInfo( const QVariantMap &info )
|
||||
{
|
||||
mInfo = info;
|
||||
}
|
||||
|
||||
QString QgsAbstractDatabaseProviderConnection::TableProperty::comment() const
|
||||
{
|
||||
return mComment;
|
||||
}
|
||||
|
||||
void QgsAbstractDatabaseProviderConnection::TableProperty::setComment( const QString &comment )
|
||||
{
|
||||
mComment = comment;
|
||||
}
|
||||
|
||||
QgsAbstractDatabaseProviderConnection::TableFlags QgsAbstractDatabaseProviderConnection::TableProperty::flags() const
|
||||
{
|
||||
return mFlags;
|
||||
}
|
||||
|
||||
void QgsAbstractDatabaseProviderConnection::TableProperty::setFlags( const QgsAbstractDatabaseProviderConnection::TableFlags &flags )
|
||||
{
|
||||
mFlags = flags;
|
||||
}
|
||||
|
||||
QList<QgsCoordinateReferenceSystem> QgsAbstractDatabaseProviderConnection::TableProperty::crsList() const
|
||||
{
|
||||
QList<QgsCoordinateReferenceSystem> crss;
|
||||
for ( const auto &t : qgis::as_const( mGeometryColumnTypes ) )
|
||||
{
|
||||
crss.push_back( t.crs );
|
||||
}
|
||||
return crss;
|
||||
}
|
||||
|
||||
QStringList QgsAbstractDatabaseProviderConnection::TableProperty::primaryKeyColumns() const
|
||||
{
|
||||
return mPkColumns;
|
||||
}
|
||||
|
||||
void QgsAbstractDatabaseProviderConnection::TableProperty::setPrimaryKeyColumns( const QStringList &pkColumns )
|
||||
{
|
||||
mPkColumns = pkColumns;
|
||||
}
|
||||
|
||||
QString QgsAbstractDatabaseProviderConnection::TableProperty::geometryColumn() const
|
||||
{
|
||||
return mGeometryColumn;
|
||||
}
|
||||
|
||||
void QgsAbstractDatabaseProviderConnection::TableProperty::setGeometryColumn( const QString &geometryColumn )
|
||||
{
|
||||
mGeometryColumn = geometryColumn;
|
||||
}
|
||||
|
||||
QString QgsAbstractDatabaseProviderConnection::TableProperty::schema() const
|
||||
{
|
||||
return mSchema;
|
||||
}
|
||||
|
||||
void QgsAbstractDatabaseProviderConnection::TableProperty::setSchema( const QString &schema )
|
||||
{
|
||||
mSchema = schema;
|
||||
}
|
||||
|
450
src/core/qgsabstractdatabaseproviderconnection.h
Normal file
450
src/core/qgsabstractdatabaseproviderconnection.h
Normal file
@ -0,0 +1,450 @@
|
||||
/***************************************************************************
|
||||
qgsabstractdatabaseproviderconnection.h - QgsAbstractDatabaseProviderConnection
|
||||
|
||||
---------------------
|
||||
begin : 2.8.2019
|
||||
copyright : (C) 2019 by Alessandro Pasotti
|
||||
email : elpaso at itopen dot it
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#ifndef QGSABSTRACTDATABASEPROVIDERCONNECTION_H
|
||||
#define QGSABSTRACTDATABASEPROVIDERCONNECTION_H
|
||||
|
||||
#include "qgsabstractproviderconnection.h"
|
||||
#include "qgscoordinatereferencesystem.h"
|
||||
#include "qgis_core.h"
|
||||
#include "qgsfields.h"
|
||||
#include "qgsexception.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
/**
|
||||
* The QgsAbstractDatabaseProviderConnection class provides common functionality
|
||||
* for DB based connections.
|
||||
*
|
||||
* This class performs low level DB operations without asking
|
||||
* the user for confirmation or handling currently opened layers and the registry
|
||||
* entries, it is responsibility of the client code to keep layers in sync.
|
||||
* The class methods will throw exceptions in case the requested operation
|
||||
* is not supported or cannot be performed without errors.
|
||||
*
|
||||
* \ingroup core
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
class CORE_EXPORT QgsAbstractDatabaseProviderConnection : public QgsAbstractProviderConnection
|
||||
{
|
||||
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Flags for table properties.
|
||||
*
|
||||
* Flags can be useful for filtering the tables returned
|
||||
* from tables().
|
||||
*/
|
||||
enum TableFlag
|
||||
{
|
||||
Aspatial = 1 << 1, //!< Aspatial table (it does not contain any geometry column)
|
||||
Vector = 1 << 2, //!< Vector table (it does contain one geometry column)
|
||||
Raster = 1 << 3, //!< Raster table
|
||||
View = 1 << 4, //!< View table
|
||||
MaterializedView = 1 << 5, //!< Materialized view table
|
||||
};
|
||||
|
||||
Q_ENUMS( TableFlag )
|
||||
Q_DECLARE_FLAGS( TableFlags, TableFlag )
|
||||
Q_FLAG( TableFlags )
|
||||
|
||||
/**
|
||||
* The TableProperty class represents a database table or view.
|
||||
*
|
||||
* In case the table is a vector spatial table and it has multiple
|
||||
* geometry columns, separate entries for each geometry column must
|
||||
* be created.
|
||||
*
|
||||
* In case the table is a vector spatial table and the geometry column
|
||||
* can contain multiple geometry types and/or CRSs, a clone of the property
|
||||
* for the individual geometry type/CRS can be retrieved with at(i)
|
||||
*/
|
||||
struct TableProperty
|
||||
{
|
||||
|
||||
#ifdef SIP_RUN
|
||||
SIP_PYOBJECT __repr__();
|
||||
% MethodCode
|
||||
QString str = QStringLiteral( "<QgsAbstractDatabaseProviderConnection.TableProperty: '%1'>" ).arg( sipCpp->tableName() );
|
||||
sipRes = PyUnicode_FromString( str.toUtf8().constData() );
|
||||
% End
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The GeometryColumnType struct represents the combination
|
||||
* of geometry type and CRS for the table geometry column.
|
||||
*/
|
||||
struct GeometryColumnType
|
||||
{
|
||||
#ifdef SIP_RUN
|
||||
SIP_PYOBJECT __repr__();
|
||||
% MethodCode
|
||||
QString str = QStringLiteral( "<QgsAbstractDatabaseProviderConnection.TableProperty.GeometryColumnType: '%1, %2'>" ).arg( QgsWkbTypes::displayString( sipCpp->wkbType ), sipCpp->crs.authid() );
|
||||
sipRes = PyUnicode_FromString( str.toUtf8().constData() );
|
||||
% End
|
||||
#endif
|
||||
QgsWkbTypes::Type wkbType;
|
||||
QgsCoordinateReferenceSystem crs;
|
||||
|
||||
inline bool operator==( const GeometryColumnType &other ) const
|
||||
{
|
||||
return this->crs == other.crs && this->wkbType == other.wkbType;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Returns the table name
|
||||
* \see defaultName()
|
||||
*/
|
||||
QString tableName() const;
|
||||
|
||||
/**
|
||||
* Sets the table name to \a name
|
||||
* \see defaultName()
|
||||
*/
|
||||
void setTableName( const QString &name );
|
||||
|
||||
/**
|
||||
* Appends the geometry column \a type with the given \a srid to the geometry column types list
|
||||
*/
|
||||
void addGeometryColumnType( const QgsWkbTypes::Type &type, const QgsCoordinateReferenceSystem &crs );
|
||||
|
||||
/**
|
||||
* Returns the list of geometry column types and CRSs.
|
||||
* The method returns a list of GeometryColumnType
|
||||
*/
|
||||
QList<QgsAbstractDatabaseProviderConnection::TableProperty::GeometryColumnType> geometryColumnTypes() const;
|
||||
|
||||
/**
|
||||
* Sets the geometry column types to \a geometryColumnTypes
|
||||
*/
|
||||
void setGeometryColumnTypes( const QList<QgsAbstractDatabaseProviderConnection::TableProperty::GeometryColumnType> &geometryColumnTypes );
|
||||
|
||||
/**
|
||||
* Returns the default name for the table entry
|
||||
*
|
||||
* It is usually the table name but in case there are multiple geometry
|
||||
* columns, the geometry column name is appendend to the table name.
|
||||
* \see geometryColumnCount()
|
||||
*/
|
||||
QString defaultName() const;
|
||||
|
||||
/**
|
||||
* Returns the table property corresponding to the geometry type at
|
||||
* the given \a index
|
||||
*/
|
||||
TableProperty at( int index ) const;
|
||||
|
||||
/**
|
||||
* Returns the schema or an empty string for backends that do not support a schema
|
||||
*/
|
||||
QString schema() const;
|
||||
|
||||
/**
|
||||
* Sets the \a schema
|
||||
*/
|
||||
void setSchema( const QString &schema );
|
||||
|
||||
/**
|
||||
* Returns the geometry column name
|
||||
*/
|
||||
QString geometryColumn() const;
|
||||
|
||||
/**
|
||||
* Sets the geometry column name to \a geometryColumn
|
||||
*/
|
||||
void setGeometryColumn( const QString &geometryColumn );
|
||||
|
||||
/**
|
||||
* Returns the list of primary key column names
|
||||
*/
|
||||
QStringList primaryKeyColumns() const;
|
||||
|
||||
/**
|
||||
* Sets the primary key column names to \a primaryKeyColumns
|
||||
*/
|
||||
void setPrimaryKeyColumns( const QStringList &primaryKeyColumns );
|
||||
|
||||
/**
|
||||
* Returns the list of CRSs supported by the geometry column
|
||||
*/
|
||||
QList<QgsCoordinateReferenceSystem> crsList() const;
|
||||
|
||||
/**
|
||||
* Returns the table flags
|
||||
*/
|
||||
TableFlags flags() const;
|
||||
|
||||
/**
|
||||
* Sets the table \a flags
|
||||
*/
|
||||
void setFlags( const TableFlags &flags );
|
||||
|
||||
/**
|
||||
* Returns the table comment
|
||||
*/
|
||||
QString comment() const;
|
||||
|
||||
/**
|
||||
* Sets the table \a comment
|
||||
*/
|
||||
void setComment( const QString &comment );
|
||||
|
||||
/**
|
||||
* Returns additional information about the table
|
||||
*
|
||||
* Provider classes may use this property
|
||||
* to store custom bits of information.
|
||||
*/
|
||||
QVariantMap info() const;
|
||||
|
||||
/**
|
||||
* Sets additional information about the table to \a info
|
||||
*
|
||||
* Provider classes may use this property
|
||||
* to store custom bits of information.
|
||||
*/
|
||||
void setInfo( const QVariantMap &info );
|
||||
|
||||
/**
|
||||
* Returns the number of geometry columns in the original table this entry refers to
|
||||
*
|
||||
* This information is used internally to build the \see defaultName()
|
||||
*/
|
||||
int geometryColumnCount() const;
|
||||
|
||||
/**
|
||||
* Sets the \a geometryColumnCount
|
||||
*/
|
||||
void setGeometryColumnCount( int geometryColumnCount );
|
||||
|
||||
/**
|
||||
* Sets a \a flag
|
||||
*/
|
||||
void setFlag( const TableFlag &flag );
|
||||
|
||||
/**
|
||||
* Returns the maximum coordinate dimensions of the geometries of a vector table.
|
||||
* This information is calculated from the geometry columns types.
|
||||
* \see geometryColumnTypes()
|
||||
*/
|
||||
int maxCoordinateDimensions() const;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
//! Holds the list of geometry wkb types and srids supported by the table
|
||||
QList<GeometryColumnType> mGeometryColumnTypes;
|
||||
//! Table schema
|
||||
QString mSchema;
|
||||
//! Table name
|
||||
QString mTableName;
|
||||
//! Name of the geometry column
|
||||
QString mGeometryColumn;
|
||||
//! The number of geometry columns in the table
|
||||
int mGeometryColumnCount;
|
||||
//! PK columns
|
||||
QStringList mPkColumns;
|
||||
TableFlags mFlags;
|
||||
QString mComment;
|
||||
//! Additional unstructured information about the table
|
||||
QVariantMap mInfo;
|
||||
};
|
||||
|
||||
/**
|
||||
* The Capability enum represent the operations supported by the connection
|
||||
*/
|
||||
enum Capability
|
||||
{
|
||||
CreateVectorTable = 1 << 1, //!< Can CREATE a vector (or aspatial) table/layer
|
||||
DropRasterTable = 1 << 2, //!< Can DROP a raster table/layer
|
||||
DropVectorTable = 1 << 3, //!< Can DROP a vector (or aspatial) table/layer
|
||||
RenameVectorTable = 1 << 4, //!< Can RENAME a vector (or aspatial) table/layer
|
||||
RenameRasterTable = 1 << 5, //!< Can RENAME a raster table/layer
|
||||
CreateSchema = 1 << 6, //!< Can CREATE a schema
|
||||
DropSchema = 1 << 7, //!< Can DROP a schema
|
||||
RenameSchema = 1 << 8, //!< Can RENAME a schema
|
||||
ExecuteSql = 1 << 9, //!< Can execute raw SQL queries (without returning results)
|
||||
Vacuum = 1 << 10, //!< Can run vacuum
|
||||
Tables = 1 << 11, //!< Can list tables
|
||||
Schemas = 1 << 12, //!< Can list schemas (if not set, the connection does not support schemas)
|
||||
SqlLayers = 1 << 13, //!< Can create vector layers from SQL SELECT queries
|
||||
TableExists = 1 << 14, //!< Can check if table exists
|
||||
Spatial = 1 << 15, //!< The connection supports spatial tables
|
||||
};
|
||||
|
||||
Q_ENUM( Capability )
|
||||
Q_DECLARE_FLAGS( Capabilities, Capability )
|
||||
Q_FLAG( Capabilities )
|
||||
|
||||
/**
|
||||
* Creates a new connection with \a name by reading its configuration from the settings.
|
||||
* If a connection with this name cannot be found, an empty connection will be returned.
|
||||
*/
|
||||
QgsAbstractDatabaseProviderConnection( const QString &name );
|
||||
|
||||
/**
|
||||
* Creates a new connection with \a name and initializes the connection from the \a uri.
|
||||
* The connection is not automatically stored in the settings.
|
||||
* \see store()
|
||||
*/
|
||||
|
||||
QgsAbstractDatabaseProviderConnection( const QString &name, const QString &uri );
|
||||
|
||||
// Public interface
|
||||
|
||||
/**
|
||||
* Returns connection capabilities
|
||||
*/
|
||||
Capabilities capabilities() const;
|
||||
|
||||
// Operations interface
|
||||
|
||||
/**
|
||||
* Creates an empty table with \a name in the given \a schema (schema is ignored if not supported by the backend).
|
||||
* Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
* \throws QgsProviderConnectionException
|
||||
*/
|
||||
virtual void createVectorTable( const QString &schema, const QString &name, const QgsFields &fields, QgsWkbTypes::Type wkbType, const QgsCoordinateReferenceSystem &srs, bool overwrite, const QMap<QString, QVariant> *options ) const SIP_THROW( QgsProviderConnectionException );
|
||||
|
||||
/**
|
||||
* Checks whether a table \a name exists in the given \a schema.
|
||||
* Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
* \throws QgsProviderConnectionException
|
||||
*/
|
||||
virtual bool tableExists( const QString &schema, const QString &name ) const SIP_THROW( QgsProviderConnectionException );
|
||||
|
||||
/**
|
||||
* Drops a vector (or aspatial) table with given \a schema (schema is ignored if not supported by the backend) and \a name.
|
||||
* Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
* \note it is responsibility of the caller to handle open layers and registry entries.
|
||||
* \throws QgsProviderConnectionException
|
||||
*/
|
||||
virtual void dropVectorTable( const QString &schema, const QString &name ) const SIP_THROW( QgsProviderConnectionException );
|
||||
|
||||
/**
|
||||
* Drops a raster table with given \a schema (schema is ignored if not supported by the backend) and \a name.
|
||||
* Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
* \note it is responsibility of the caller to handle open layers and registry entries.
|
||||
* \throws QgsProviderConnectionException
|
||||
*/
|
||||
virtual void dropRasterTable( const QString &schema, const QString &name ) const SIP_THROW( QgsProviderConnectionException );
|
||||
|
||||
/**
|
||||
* Renames a vector or aspatial table with given \a schema (schema is ignored if not supported by the backend) and \a name.
|
||||
* Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
* \note it is responsibility of the caller to handle open layers and registry entries.
|
||||
* \throws QgsProviderConnectionException
|
||||
*/
|
||||
virtual void renameVectorTable( const QString &schema, const QString &name, const QString &newName ) const SIP_THROW( QgsProviderConnectionException );
|
||||
|
||||
/**
|
||||
* Renames a raster table with given \a schema (schema is ignored if not supported by the backend) and \a name.
|
||||
* Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
* \note it is responsibility of the caller to handle open layers and registry entries.
|
||||
* \throws QgsProviderConnectionException
|
||||
*/
|
||||
virtual void renameRasterTable( const QString &schema, const QString &name, const QString &newName ) const SIP_THROW( QgsProviderConnectionException );
|
||||
|
||||
/**
|
||||
* Creates a new schema with the specified \a name
|
||||
* \throws QgsProviderConnectionException
|
||||
*/
|
||||
virtual void createSchema( const QString &name ) const SIP_THROW( QgsProviderConnectionException );
|
||||
|
||||
/**
|
||||
* Drops an entire schema with the specified name.
|
||||
* Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
* \param name name of the schema to be dropped
|
||||
* \param force if TRUE, a DROP CASCADE will drop all related objects
|
||||
* \note it is responsibility of the caller to handle open layers and registry entries.
|
||||
* \throws QgsProviderConnectionException
|
||||
*/
|
||||
virtual void dropSchema( const QString &name, bool force = false ) const SIP_THROW( QgsProviderConnectionException );
|
||||
|
||||
/**
|
||||
* Renames a schema with the specified \a name.
|
||||
* Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
* \note it is responsibility of the caller to handle open layers and registry entries.
|
||||
* \throws QgsProviderConnectionException
|
||||
*/
|
||||
virtual void renameSchema( const QString &name, const QString &newName ) const SIP_THROW( QgsProviderConnectionException );
|
||||
|
||||
/**
|
||||
* Executes raw \a sql and returns the (possibly empty) list of results in a multi-dimensional array.
|
||||
* Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
* \throws QgsProviderConnectionException
|
||||
*/
|
||||
virtual QList<QList<QVariant>> executeSql( const QString &sql ) const SIP_THROW( QgsProviderConnectionException );
|
||||
|
||||
/**
|
||||
* Vacuum the database table with given \a schema and \a name (schema is ignored if not supported by the backend).
|
||||
* Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
* \throws QgsProviderConnectionException
|
||||
*/
|
||||
virtual void vacuum( const QString &schema, const QString &name ) const SIP_THROW( QgsProviderConnectionException );
|
||||
|
||||
/**
|
||||
* Returns information on the tables in the given schema .
|
||||
* Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
* \param schema name of the schema (ignored if not supported by the backend)
|
||||
* \param flags filter tables by flags, this option completely overrides search options stored in the connection
|
||||
* \throws QgsProviderConnectionException
|
||||
* \note Not available in Python bindings
|
||||
*/
|
||||
virtual QList<QgsAbstractDatabaseProviderConnection::TableProperty> tables( const QString &schema = QString(), const QgsAbstractDatabaseProviderConnection::TableFlags &flags = nullptr ) const SIP_SKIP;
|
||||
|
||||
/**
|
||||
* Returns information on the tables in the given schema.
|
||||
* Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
* \param schema name of the schema (ignored if not supported by the backend)
|
||||
* \param flags filter tables by flags, this option completely overrides search options stored in the connection
|
||||
* \throws QgsProviderConnectionException
|
||||
*/
|
||||
QList<QgsAbstractDatabaseProviderConnection::TableProperty> tablesInt( const QString &schema = QString(), const int flags = 0 ) const SIP_THROW( QgsProviderConnectionException ) SIP_PYNAME( tables );
|
||||
|
||||
|
||||
// TODO: return more schema information and not just the name
|
||||
|
||||
/**
|
||||
* Returns information about the existing schemas.
|
||||
* Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
* \throws QgsProviderConnectionException
|
||||
*/
|
||||
virtual QStringList schemas( ) const SIP_THROW( QgsProviderConnectionException );
|
||||
|
||||
protected:
|
||||
|
||||
///@cond PRIVATE
|
||||
|
||||
/**
|
||||
* Checks if \a capability is supported and throws and exception if it's not
|
||||
* \throws QgsProviderConnectionException
|
||||
*/
|
||||
void checkCapability( Capability capability ) const;
|
||||
///@endcond
|
||||
|
||||
Capabilities mCapabilities = nullptr SIP_SKIP;
|
||||
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsAbstractDatabaseProviderConnection::Capabilities )
|
||||
|
||||
#endif // QGSABSTRACTDATABASEPROVIDERCONNECTION_H
|
46
src/core/qgsabstractproviderconnection.cpp
Normal file
46
src/core/qgsabstractproviderconnection.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
/***************************************************************************
|
||||
qgsabstractproviderconnection.cpp - QgsAbstractProviderConnection
|
||||
|
||||
---------------------
|
||||
begin : 2.8.2019
|
||||
copyright : (C) 2019 by Alessandro Pasotti
|
||||
email : elpaso at itopen dot it
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#include "qgsabstractproviderconnection.h"
|
||||
|
||||
|
||||
QgsAbstractProviderConnection::QgsAbstractProviderConnection( const QString &name )
|
||||
: mConnectionName( name )
|
||||
{
|
||||
// Note: concrete classes must implement the logic to read the configuration from the settings
|
||||
// and create mUri
|
||||
}
|
||||
|
||||
QgsAbstractProviderConnection::QgsAbstractProviderConnection( const QString &name, const QString &uri )
|
||||
: mConnectionName( name )
|
||||
, mUri( uri )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QString QgsAbstractProviderConnection::name() const
|
||||
{
|
||||
return mConnectionName;
|
||||
}
|
||||
|
||||
QString QgsAbstractProviderConnection::uri() const
|
||||
{
|
||||
return mUri;
|
||||
}
|
||||
|
||||
void QgsAbstractProviderConnection::setUri( const QString &uri )
|
||||
{
|
||||
mUri = uri;
|
||||
}
|
110
src/core/qgsabstractproviderconnection.h
Normal file
110
src/core/qgsabstractproviderconnection.h
Normal file
@ -0,0 +1,110 @@
|
||||
/***************************************************************************
|
||||
qgsabstractproviderconnection.h - QgsAbstractProviderConnection
|
||||
|
||||
---------------------
|
||||
begin : 2.8.2019
|
||||
copyright : (C) 2019 by Alessandro Pasotti
|
||||
email : elpaso at itopen dot it
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#ifndef QGSABSTRACTPROVIDERCONNECTION_H
|
||||
#define QGSABSTRACTPROVIDERCONNECTION_H
|
||||
|
||||
#include <QString>
|
||||
#include <QVariantMap>
|
||||
|
||||
#include "qgis_core.h"
|
||||
#include "qgis_sip.h"
|
||||
#include "qgsdatasourceuri.h"
|
||||
#include "qgsexception.h"
|
||||
|
||||
/**
|
||||
* The QgsAbstractProviderConnection provides an interface for data provider connections.
|
||||
*
|
||||
* Connections objects can be created by passing the connection name and in this case
|
||||
* they are automatically loaded from the settings, or by passing a data source URI
|
||||
* in the constructor.
|
||||
*
|
||||
* Concrete classes must implement methods to retrieve, save and remove connections from
|
||||
* the settings.
|
||||
* \ingroup core
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
class CORE_EXPORT QgsAbstractProviderConnection
|
||||
{
|
||||
|
||||
#ifdef SIP_RUN
|
||||
SIP_CONVERT_TO_SUBCLASS_CODE
|
||||
if ( dynamic_cast<QgsAbstractDatabaseProviderConnection *>( sipCpp ) != NULL )
|
||||
{
|
||||
sipType = sipType_QgsAbstractDatabaseProviderConnection;
|
||||
}
|
||||
else if ( dynamic_cast<QgsAbstractProviderConnection *>( sipCpp ) != NULL )
|
||||
{
|
||||
sipType = sipType_QgsAbstractProviderConnection;
|
||||
}
|
||||
else
|
||||
{
|
||||
sipType = 0;
|
||||
}
|
||||
SIP_END
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Creates a new connection with \a name by reading its configuration from the settings.
|
||||
* If a connection with this name cannot be found, an empty connection will be returned.
|
||||
*/
|
||||
QgsAbstractProviderConnection( const QString &name );
|
||||
|
||||
/**
|
||||
* Creates a new connection with \a name and initializes the connection from the \a uri.
|
||||
* The connection is not automatically stored in the settings.
|
||||
* \see store()
|
||||
*/
|
||||
QgsAbstractProviderConnection( const QString &name, const QString &uri );
|
||||
|
||||
virtual ~QgsAbstractProviderConnection() = default;
|
||||
|
||||
/**
|
||||
* Stores the connection in the settings.
|
||||
* \param configuration stores additional connection settings that are used by the
|
||||
* source select dialog and are not part of the data source URI
|
||||
*/
|
||||
virtual void store( const QVariantMap &configuration = QVariantMap() ) const = 0;
|
||||
|
||||
/**
|
||||
* Deletes the connection from the settings.
|
||||
*/
|
||||
virtual void remove( ) const = 0;
|
||||
|
||||
/**
|
||||
* Returns the connection name
|
||||
*/
|
||||
QString name() const;
|
||||
|
||||
/**
|
||||
* Returns the connection data source URI string representation
|
||||
*/
|
||||
QString uri() const;
|
||||
|
||||
/**
|
||||
* Sets the connection data source URI to \a uri
|
||||
*/
|
||||
void setUri( const QString &uri );
|
||||
|
||||
private:
|
||||
|
||||
QString mConnectionName;
|
||||
QString mUri;
|
||||
|
||||
};
|
||||
|
||||
#endif // QGSABSTRACTPROVIDERCONNECTION_H
|
@ -385,6 +385,16 @@ QString QgsDataSourceUri::escape( const QString &val, QChar delim = '\'' ) const
|
||||
return escaped;
|
||||
}
|
||||
|
||||
void QgsDataSourceUri::setGeometryColumn( const QString &geometryColumn )
|
||||
{
|
||||
mGeometryColumn = geometryColumn;
|
||||
}
|
||||
|
||||
void QgsDataSourceUri::setTable( const QString &table )
|
||||
{
|
||||
mTable = table;
|
||||
}
|
||||
|
||||
void QgsDataSourceUri::skipBlanks( const QString &uri, int &i )
|
||||
{
|
||||
// skip space before value
|
||||
|
@ -289,6 +289,18 @@ class CORE_EXPORT QgsDataSourceUri
|
||||
*/
|
||||
static QString encodeSslMode( SslMode sslMode );
|
||||
|
||||
/**
|
||||
* Sets table to \a table
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
void setTable( const QString &table );
|
||||
|
||||
/**
|
||||
* Sets geometry column name to \a geometryColumn
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
void setGeometryColumn( const QString &geometryColumn );
|
||||
|
||||
private:
|
||||
void skipBlanks( const QString &uri, int &i );
|
||||
QString getValue( const QString &uri, int &i );
|
||||
|
@ -90,4 +90,23 @@ class CORE_EXPORT QgsProcessingException : public QgsException
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \class QgsProviderConnectionException
|
||||
* \ingroup core
|
||||
* Custom exception class for provider connection related exceptions.
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
class CORE_EXPORT QgsProviderConnectionException: public QgsException
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for QgsProviderConnectionException, with the specified error \a message.
|
||||
*/
|
||||
QgsProviderConnectionException( const QString &message ) : QgsException( message ) {}
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -540,7 +540,7 @@ QgsGeometry QgsOgrUtils::ogrGeometryToQgsGeometry( OGRGeometryH geom )
|
||||
// get the wkb representation
|
||||
int memorySize = OGR_G_WkbSize( geom );
|
||||
unsigned char *wkb = new unsigned char[memorySize];
|
||||
OGR_G_ExportToWkb( geom, ( OGRwkbByteOrder ) QgsApplication::endian(), wkb );
|
||||
OGR_G_ExportToWkb( geom, static_cast<OGRwkbByteOrder>( QgsApplication::endian() ), wkb );
|
||||
|
||||
// Read original geometry type
|
||||
uint32_t origGeomType;
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "qgsprovidermetadata.h"
|
||||
#include "qgsdataprovider.h"
|
||||
#include "qgsmaplayer.h"
|
||||
#include "qgsexception.h"
|
||||
|
||||
QgsProviderMetadata::QgsProviderMetadata( QString const &key,
|
||||
QString const &description,
|
||||
@ -36,7 +37,7 @@ QgsProviderMetadata::QgsProviderMetadata( const QString &key, const QString &des
|
||||
|
||||
QgsProviderMetadata::~QgsProviderMetadata()
|
||||
{
|
||||
|
||||
qDeleteAll( mProviderConnections );
|
||||
}
|
||||
|
||||
QString QgsProviderMetadata::key() const
|
||||
@ -152,7 +153,7 @@ QString QgsProviderMetadata::loadStyle( const QString &, QString &errCause )
|
||||
|
||||
bool QgsProviderMetadata::createDb( const QString &, QString &errCause )
|
||||
{
|
||||
errCause = QObject::tr( "Provider %1 has no %2 method" ).arg( key(), QStringLiteral( "errCause" ) );
|
||||
errCause = QObject::tr( "Provider %1 has no %2 method" ).arg( key(), QStringLiteral( "createDb" ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -160,3 +161,83 @@ QgsTransaction *QgsProviderMetadata::createTransaction( const QString & )
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QMap<QString, QgsAbstractProviderConnection *> QgsProviderMetadata::connections( bool cached )
|
||||
{
|
||||
Q_UNUSED( cached );
|
||||
throw QgsProviderConnectionException( QObject::tr( "Provider %1 has no %2 method" ).arg( key(), QStringLiteral( "connections" ) ) );
|
||||
}
|
||||
|
||||
QMap<QString, QgsAbstractDatabaseProviderConnection *> QgsProviderMetadata::dbConnections( bool cached )
|
||||
{
|
||||
return connections<QgsAbstractDatabaseProviderConnection>( cached ) ;
|
||||
}
|
||||
|
||||
QgsAbstractProviderConnection *QgsProviderMetadata::findConnection( const QString &name, bool cached )
|
||||
{
|
||||
const QMap<QString, QgsAbstractProviderConnection *> constConns { connections( cached ) };
|
||||
for ( QgsAbstractProviderConnection *conn : constConns )
|
||||
{
|
||||
if ( conn->name() == name )
|
||||
{
|
||||
return conn;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QgsAbstractProviderConnection *QgsProviderMetadata::createConnection( const QString &name )
|
||||
{
|
||||
Q_UNUSED( name );
|
||||
throw QgsProviderConnectionException( QObject::tr( "Provider %1 has no %2 method" ).arg( key(), QStringLiteral( "connection" ) ) );
|
||||
}
|
||||
|
||||
|
||||
QgsAbstractProviderConnection *QgsProviderMetadata::createConnection( const QString &name, const QString &uri )
|
||||
{
|
||||
Q_UNUSED( name );
|
||||
Q_UNUSED( uri );
|
||||
throw QgsProviderConnectionException( QObject::tr( "Provider %1 has no %2 method" ).arg( key(), QStringLiteral( "connection" ) ) );
|
||||
}
|
||||
|
||||
void QgsProviderMetadata::deleteConnection( const QString &name )
|
||||
{
|
||||
Q_UNUSED( name );
|
||||
throw QgsProviderConnectionException( QObject::tr( "Provider %1 has no %2 method" ).arg( key(), QStringLiteral( "deleteConnection" ) ) );
|
||||
}
|
||||
|
||||
void QgsProviderMetadata::saveConnection( QgsAbstractProviderConnection *connection, const QVariantMap &configuration )
|
||||
{
|
||||
Q_UNUSED( connection );
|
||||
Q_UNUSED( configuration );
|
||||
throw QgsProviderConnectionException( QObject::tr( "Provider %1 has no %2 method" ).arg( key(), QStringLiteral( "saveConnection" ) ) );
|
||||
}
|
||||
|
||||
///@cond PRIVATE
|
||||
void QgsProviderMetadata::saveConnectionProtected( QgsAbstractProviderConnection *conn, const QVariantMap &configuration )
|
||||
{
|
||||
conn->store( configuration );
|
||||
mProviderConnections.clear();
|
||||
}
|
||||
///@endcond
|
||||
|
||||
template<typename T>
|
||||
QMap<QString, T *> QgsProviderMetadata::connections( bool cached )
|
||||
{
|
||||
QMap<QString, T *> result;
|
||||
const auto constConns { connections( cached ) };
|
||||
const QStringList constConnKeys { constConns.keys() };
|
||||
for ( const auto &c : constConnKeys )
|
||||
{
|
||||
T *casted { static_cast<T *>( constConns.value( c ) ) };
|
||||
if ( casted )
|
||||
{
|
||||
result.insert( c, casted );
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -32,7 +32,10 @@
|
||||
#include "qgis_core.h"
|
||||
#include <functional>
|
||||
#include "qgsvectorlayerexporter.h"
|
||||
#include "qgsabstractproviderconnection.h"
|
||||
#include "qgsabstractdatabaseproviderconnection.h"
|
||||
#include "qgsfields.h"
|
||||
#include "qgsexception.h"
|
||||
|
||||
class QgsDataItem;
|
||||
class QgsDataItemProvider;
|
||||
@ -158,12 +161,22 @@ class CORE_EXPORT QgsProviderMetadata
|
||||
*/
|
||||
virtual QgsDataProvider *createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options ) SIP_FACTORY;
|
||||
|
||||
#ifndef SIP_RUN
|
||||
|
||||
/**
|
||||
* Creates new empty vector layer
|
||||
* \note not available in Python bindings
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
SIP_SKIP virtual QgsVectorLayerExporter::ExportError createEmptyLayer( const QString &uri, const QgsFields &fields, QgsWkbTypes::Type wkbType, const QgsCoordinateReferenceSystem &srs, bool overwrite, QMap<int, int> &oldToNewAttrIdxMap, QString &errorMessage, const QMap<QString, QVariant> *options );
|
||||
virtual QgsVectorLayerExporter::ExportError createEmptyLayer( const QString &uri,
|
||||
const QgsFields &fields,
|
||||
QgsWkbTypes::Type wkbType,
|
||||
const QgsCoordinateReferenceSystem &srs,
|
||||
bool overwrite,
|
||||
QMap<int, int> &oldToNewAttrIdxMap,
|
||||
QString &errorMessage,
|
||||
const QMap<QString, QVariant> *options );
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Creates a new instance of the raster data provider.
|
||||
@ -249,6 +262,123 @@ class CORE_EXPORT QgsProviderMetadata
|
||||
*/
|
||||
virtual QgsTransaction *createTransaction( const QString &connString ) SIP_FACTORY;
|
||||
|
||||
/**
|
||||
* Returns a dictionary of stored provider connections,
|
||||
* the dictionary key is the connection identifier.
|
||||
* Ownership is not transferred.
|
||||
* Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
* \param cached if FALSE connections will be re-read from the settings
|
||||
* \throws QgsProviderConnectionException
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
virtual QMap<QString, QgsAbstractProviderConnection *> connections( bool cached = true ) SIP_THROW( QgsProviderConnectionException );
|
||||
|
||||
/**
|
||||
* Returns a dictionary of database provider connections,
|
||||
* the dictionary key is the connection identifier.
|
||||
* Ownership is not transferred.
|
||||
* Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
* \param cached if FALSE connections will be re-read from the settings
|
||||
* \throws QgsProviderConnectionException
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
QMap<QString, QgsAbstractDatabaseProviderConnection *> dbConnections( bool cached = true ) SIP_THROW( QgsProviderConnectionException );
|
||||
|
||||
/**
|
||||
* Searches and returns a (possibly NULL) connection from the stored provider connections.
|
||||
* Ownership is not transferred.
|
||||
* Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
* \param name the connection name
|
||||
* \param cached if FALSE connections will be re-read from the settings
|
||||
* \throws QgsProviderConnectionException
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
QgsAbstractProviderConnection *findConnection( const QString &name, bool cached = true ) SIP_THROW( QgsProviderConnectionException );
|
||||
|
||||
#ifndef SIP_RUN
|
||||
|
||||
/**
|
||||
* Returns a dictionary of provider connections of the specified type T,
|
||||
* the dictionary key is the connection identifier.
|
||||
* \param cached if FALSE connections will be re-read from the settings
|
||||
* \note not available in Python bindings
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
template <typename T> QMap<QString, T *>connections( bool cached = true );
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Creates a new connection by loading the connection with the given \a name from the settings.
|
||||
* Ownership is transferred to the caller.
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
virtual QgsAbstractProviderConnection *createConnection( const QString &name ) SIP_FACTORY ;
|
||||
|
||||
/**
|
||||
* Creates a new connection with the given \a name and data source \a uri,
|
||||
* the newly created connection is not automatically stored in the settings, call
|
||||
* saveConnection() to save it.
|
||||
* Ownership is transferred to the caller.
|
||||
* \see saveConnection()
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
virtual QgsAbstractProviderConnection *createConnection( const QString &name, const QString &uri ) SIP_FACTORY;
|
||||
|
||||
/**
|
||||
* Removes the connection with the given \a name from the settings.
|
||||
* Raises a QgsProviderConnectionException if any errors are encountered.
|
||||
* \throws QgsProviderConnectionException
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
virtual void deleteConnection( const QString &name ) SIP_THROW( QgsProviderConnectionException );
|
||||
|
||||
/**
|
||||
* Stores the connection in the settings
|
||||
* \param connection the connection to be stored in the settings
|
||||
* \param configuration stores additional connection settings that not part of the data source URI
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
virtual void saveConnection( QgsAbstractProviderConnection *connection, const QVariantMap &configuration = QVariantMap() );
|
||||
|
||||
protected:
|
||||
|
||||
#ifndef SIP_RUN
|
||||
///@cond PRIVATE
|
||||
|
||||
// Common functionality for connections management,to be moved into the class
|
||||
// when all the providers are ready
|
||||
// T_provider_conn: subclass of QgsAbstractProviderConnection,
|
||||
// T_conn: provider connection class (such as QgsOgrDbConnection or QgsPostgresConn)
|
||||
// TODO QGIS4: remove all old provider conn classes and move functionality into QgsAbstractProviderConnection subclasses
|
||||
template <class T_provider_conn, class T_conn> QMap<QString, QgsAbstractProviderConnection *> connectionsProtected( bool cached = true )
|
||||
{
|
||||
if ( ! cached || mProviderConnections.isEmpty() )
|
||||
{
|
||||
qDeleteAll( mProviderConnections );
|
||||
mProviderConnections.clear();
|
||||
const auto connNames { T_conn::connectionList() };
|
||||
for ( const auto &cname : connNames )
|
||||
{
|
||||
mProviderConnections.insert( cname, new T_provider_conn( cname ) );
|
||||
}
|
||||
}
|
||||
return mProviderConnections;
|
||||
}
|
||||
|
||||
template <class T_provider_conn> void deleteConnectionProtected( const QString &name )
|
||||
{
|
||||
T_provider_conn conn( name );
|
||||
conn.remove();
|
||||
mProviderConnections.clear();
|
||||
}
|
||||
virtual void saveConnectionProtected( QgsAbstractProviderConnection *connection, const QVariantMap &configuration = QVariantMap() );
|
||||
//! Provider connections cache
|
||||
QMap<QString, QgsAbstractProviderConnection *> mProviderConnections;
|
||||
|
||||
/// @endcond
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
@ -263,6 +393,7 @@ class CORE_EXPORT QgsProviderMetadata
|
||||
QString mLibrary;
|
||||
|
||||
CreateDataProviderFunction mCreateFunction = nullptr;
|
||||
|
||||
};
|
||||
|
||||
#endif //QGSPROVIDERMETADATA_H
|
||||
|
@ -644,7 +644,7 @@ QStringList QgsProviderRegistry::providerList() const
|
||||
return lst;
|
||||
}
|
||||
|
||||
const QgsProviderMetadata *QgsProviderRegistry::providerMetadata( const QString &providerKey ) const
|
||||
QgsProviderMetadata *QgsProviderRegistry::providerMetadata( const QString &providerKey ) const
|
||||
{
|
||||
return findMetadata_( mProviders, providerKey );
|
||||
}
|
||||
|
@ -266,7 +266,7 @@ class CORE_EXPORT QgsProviderRegistry
|
||||
QStringList providerList() const;
|
||||
|
||||
//! Returns metadata of the provider or NULLPTR if not found
|
||||
const QgsProviderMetadata *providerMetadata( const QString &providerKey ) const;
|
||||
QgsProviderMetadata *providerMetadata( const QString &providerKey ) const;
|
||||
|
||||
/**
|
||||
* Returns vector file filter string
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "gdal.h"
|
||||
#include "qgsogrdataitems.h"
|
||||
#include "qgsogrdbconnection.h"
|
||||
#include "qgsgeopackageproviderconnection.h"
|
||||
|
||||
void QgsGeoPackageItemGuiProvider::populateContextMenu( QgsDataItem *item, QMenu *menu,
|
||||
const QList<QgsDataItem *> &,
|
||||
@ -44,7 +45,7 @@ void QgsGeoPackageItemGuiProvider::populateContextMenu( QgsDataItem *item, QMenu
|
||||
{
|
||||
if ( QgsGeoPackageVectorLayerItem *layerItem = qobject_cast< QgsGeoPackageVectorLayerItem * >( item ) )
|
||||
{
|
||||
// Check capabilities: for now rename is only available for vectors
|
||||
// Check capabilities
|
||||
if ( layerItem->capabilities2() & QgsDataItem::Capability::Rename )
|
||||
{
|
||||
QAction *actionRenameLayer = new QAction( tr( "Rename Layer '%1'…" ).arg( layerItem->name() ), this );
|
||||
@ -189,12 +190,12 @@ void QgsGeoPackageItemGuiProvider::addTable()
|
||||
}
|
||||
}
|
||||
|
||||
bool QgsGeoPackageItemGuiProvider::rename( QgsDataItem *item, const QString &name, QgsDataItemGuiContext )
|
||||
bool QgsGeoPackageItemGuiProvider::rename( QgsDataItem *item, const QString &newName, QgsDataItemGuiContext )
|
||||
{
|
||||
if ( QgsGeoPackageVectorLayerItem *layerItem = qobject_cast< QgsGeoPackageVectorLayerItem * >( item ) )
|
||||
{
|
||||
// Checks that name does not exist yet
|
||||
if ( layerItem->tableNames().contains( name ) )
|
||||
if ( layerItem->tableNames().contains( newName ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -236,33 +237,25 @@ bool QgsGeoPackageItemGuiProvider::rename( QgsDataItem *item, const QString &nam
|
||||
QgsProject::instance()->removeMapLayers( layersList );
|
||||
}
|
||||
|
||||
// TODO: maybe an index?
|
||||
QString oldName = parts.value( QStringLiteral( "layerName" ) ).toString();
|
||||
|
||||
GDALDatasetH hDS = GDALOpenEx( filePath.toUtf8().constData(), GDAL_OF_VECTOR | GDAL_OF_UPDATE, nullptr, nullptr, nullptr );
|
||||
if ( hDS )
|
||||
// Actually rename
|
||||
QgsProviderMetadata *md { QgsProviderRegistry::instance()->providerMetadata( QStringLiteral( "ogr" ) ) };
|
||||
QgsGeoPackageProviderConnection *conn { static_cast<QgsGeoPackageProviderConnection *>( md->findConnection( layerItem->collection()->name() ) ) };
|
||||
if ( ! conn )
|
||||
{
|
||||
QString sql( QStringLiteral( "ALTER TABLE %1 RENAME TO %2" )
|
||||
.arg( QgsSqliteUtils::quotedIdentifier( oldName ),
|
||||
QgsSqliteUtils::quotedIdentifier( name ) ) );
|
||||
OGRLayerH ogrLayer( GDALDatasetExecuteSQL( hDS, sql.toUtf8().constData(), nullptr, nullptr ) );
|
||||
if ( ogrLayer )
|
||||
GDALDatasetReleaseResultSet( hDS, ogrLayer );
|
||||
errCause = CPLGetLastErrorMsg( );
|
||||
if ( errCause.isEmpty() )
|
||||
{
|
||||
sql = QStringLiteral( "UPDATE layer_styles SET f_table_name = %2 WHERE f_table_name = %1" )
|
||||
.arg( QgsSqliteUtils::quotedString( oldName ),
|
||||
QgsSqliteUtils::quotedString( name ) );
|
||||
ogrLayer = GDALDatasetExecuteSQL( hDS, sql.toUtf8().constData(), nullptr, nullptr );
|
||||
if ( ogrLayer )
|
||||
GDALDatasetReleaseResultSet( hDS, ogrLayer );
|
||||
}
|
||||
GDALClose( hDS );
|
||||
errCause = QObject::tr( "There was an error retrieving the connection %1!" ).arg( layerItem->collection()->name() );
|
||||
}
|
||||
else
|
||||
{
|
||||
errCause = QObject::tr( "There was an error opening %1!" ).arg( filePath );
|
||||
// TODO: maybe an index?
|
||||
QString oldName = parts.value( QStringLiteral( "layerName" ) ).toString();
|
||||
try
|
||||
{
|
||||
conn->renameVectorTable( QString(), oldName, newName );
|
||||
}
|
||||
catch ( QgsProviderConnectionException &ex )
|
||||
{
|
||||
errCause = ex.what();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -369,8 +362,9 @@ bool QgsGeoPackageItemGuiProvider::deleteLayer( QgsLayerItem *layerItem, QgsData
|
||||
|
||||
void QgsGeoPackageItemGuiProvider::vacuumGeoPackageDbAction( const QString &path, const QString &name )
|
||||
{
|
||||
Q_UNUSED( path );
|
||||
QString errCause;
|
||||
bool result = QgsGeoPackageCollectionItem::vacuumGeoPackageDb( path, name, errCause );
|
||||
bool result = QgsGeoPackageCollectionItem::vacuumGeoPackageDb( name, errCause );
|
||||
if ( !result || !errCause.isEmpty() )
|
||||
{
|
||||
QMessageBox::warning( nullptr, tr( "Database compact (VACUUM)" ), errCause );
|
||||
@ -397,7 +391,7 @@ void QgsGeoPackageItemGuiProvider::createDatabase()
|
||||
dialog.setCrs( QgsProject::instance()->defaultCrsForNewLayers() );
|
||||
if ( dialog.exec() == QDialog::Accepted )
|
||||
{
|
||||
if ( QgsOgrDataCollectionItem::storeConnection( dialog.databasePath(), QStringLiteral( "GPKG" ) ) )
|
||||
if ( QgsOgrDataCollectionItem::saveConnection( dialog.databasePath(), QStringLiteral( "GPKG" ) ) )
|
||||
{
|
||||
item->refreshConnections();
|
||||
}
|
||||
@ -562,7 +556,7 @@ bool QgsGeoPackageItemGuiProvider::handleDropGeopackage( QgsGeoPackageCollection
|
||||
// Always try to delete the imported raster, in case the gpkg has been left
|
||||
// in an inconsistent status. Ignore delete errors.
|
||||
QString deleteErr;
|
||||
item->deleteGeoPackageRasterLayer( QStringLiteral( "GPKG:%1:%2" ).arg( item->path(), dropUri.name ), deleteErr );
|
||||
item->deleteRasterLayer( dropUri.name, deleteErr );
|
||||
} );
|
||||
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
#include "qgsowsdataitems.h"
|
||||
#include "qgsowsprovider.h"
|
||||
#include "qgslogger.h"
|
||||
#include "qgsdatasourceuri.h"
|
||||
#include "qgsowsconnection.h"
|
||||
#include "qgsdataitemprovider.h"
|
||||
|
||||
|
@ -16,7 +16,6 @@
|
||||
#define QGSOWSDATAITEMS_H
|
||||
|
||||
#include "qgsdataitem.h"
|
||||
#include "qgsdatasourceuri.h"
|
||||
#include "qgswkbtypes.h"
|
||||
#include "qgsdataitemprovider.h"
|
||||
|
||||
|
@ -13,6 +13,7 @@ SET(PG_SRCS
|
||||
qgscolumntypethread.cpp
|
||||
qgspostgresexpressioncompiler.cpp
|
||||
qgspostgreslistener.cpp
|
||||
qgspostgresproviderconnection.cpp
|
||||
)
|
||||
|
||||
SET(PG_MOC_HDRS
|
||||
@ -44,6 +45,7 @@ ENDIF ()
|
||||
|
||||
SET(PG_HDRS
|
||||
qgspostgresexpressioncompiler.h
|
||||
qgspostgresproviderconnection.h
|
||||
)
|
||||
|
||||
########################################################
|
||||
|
@ -559,7 +559,7 @@ bool QgsPostgresConn::getTableInfo( bool searchGeometryColumnsOnly, bool searchP
|
||||
bool isView = relkind == QLatin1String( "v" ) || relkind == QLatin1String( "m" );
|
||||
bool isMaterializedView = relkind == QLatin1String( "m" );
|
||||
bool isForeignTable = relkind == QLatin1String( "f" );
|
||||
bool isRaster = type == QStringLiteral( "RASTER" );
|
||||
bool isRaster = type == QLatin1String( "RASTER" );
|
||||
QString comment = result.PQgetvalue( idx, 7 );
|
||||
|
||||
int srid = ssrid.isEmpty() ? std::numeric_limits<int>::min() : ssrid.toInt();
|
||||
@ -796,6 +796,7 @@ bool QgsPostgresConn::getTableInfo( bool searchGeometryColumnsOnly, bool searchP
|
||||
layerProperty.geometryColType = SctNone;
|
||||
layerProperty.relKind = relkind;
|
||||
layerProperty.isView = isView;
|
||||
layerProperty.isRaster = false;
|
||||
layerProperty.isMaterializedView = isMaterializedView;
|
||||
layerProperty.tableComment = comment;
|
||||
|
||||
@ -1566,7 +1567,7 @@ void QgsPostgresConn::retrieveLayerTypes( QgsPostgresLayerProperty &layerPropert
|
||||
layerProperty.srids.append( srid );
|
||||
}
|
||||
}
|
||||
else
|
||||
else // vectors
|
||||
{
|
||||
// our estimatation ignores that a where clause might restrict the feature type or srid
|
||||
if ( useEstimatedMetadata )
|
||||
@ -1623,6 +1624,12 @@ void QgsPostgresConn::retrieveLayerTypes( QgsPostgresLayerProperty &layerPropert
|
||||
|
||||
if ( gresult.PQresultStatus() == PGRES_TUPLES_OK )
|
||||
{
|
||||
// Remove unknown entry
|
||||
if ( gresult.PQntuples() > 0 )
|
||||
{
|
||||
layerProperty.srids.clear();
|
||||
layerProperty.types.clear();
|
||||
}
|
||||
for ( int i = 0; i < gresult.PQntuples(); i++ )
|
||||
{
|
||||
QString type = gresult.PQgetvalue( i, 0 );
|
||||
|
@ -95,7 +95,7 @@ struct QgsPostgresLayerProperty
|
||||
return n;
|
||||
}
|
||||
|
||||
QgsPostgresLayerProperty at( int i ) const
|
||||
QgsPostgresLayerProperty at( unsigned int i ) const
|
||||
{
|
||||
QgsPostgresLayerProperty property;
|
||||
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "qgspostgrestransaction.h"
|
||||
#include "qgspostgreslistener.h"
|
||||
#include "qgspostgresprojectstorage.h"
|
||||
#include "qgspostgresproviderconnection.h"
|
||||
#include "qgslogger.h"
|
||||
#include "qgsfeedback.h"
|
||||
#include "qgssettings.h"
|
||||
@ -45,6 +46,7 @@
|
||||
|
||||
#include "qgspostgresprovider.h"
|
||||
#include "qgsprovidermetadata.h"
|
||||
#include "qgspostgresproviderconnection.h"
|
||||
|
||||
|
||||
const QString QgsPostgresProvider::POSTGRES_KEY = QStringLiteral( "postgres" );
|
||||
@ -5031,6 +5033,31 @@ QgsTransaction *QgsPostgresProviderMetadata::createTransaction( const QString &c
|
||||
return new QgsPostgresTransaction( connString );
|
||||
}
|
||||
|
||||
QMap<QString, QgsAbstractProviderConnection *> QgsPostgresProviderMetadata::connections( bool cached )
|
||||
{
|
||||
return connectionsProtected<QgsPostgresProviderConnection, QgsPostgresConn>( cached );
|
||||
}
|
||||
|
||||
QgsAbstractProviderConnection *QgsPostgresProviderMetadata::createConnection( const QString &name, const QString &uri )
|
||||
{
|
||||
return new QgsPostgresProviderConnection( name, uri );
|
||||
}
|
||||
|
||||
void QgsPostgresProviderMetadata::deleteConnection( const QString &name )
|
||||
{
|
||||
deleteConnectionProtected<QgsPostgresProviderConnection>( name );
|
||||
}
|
||||
|
||||
void QgsPostgresProviderMetadata::saveConnection( QgsAbstractProviderConnection *conn, const QVariantMap &configuration )
|
||||
{
|
||||
saveConnectionProtected( conn, configuration );
|
||||
}
|
||||
|
||||
QgsAbstractProviderConnection *QgsPostgresProviderMetadata::createConnection( const QString &name )
|
||||
{
|
||||
return new QgsPostgresProviderConnection( name );
|
||||
}
|
||||
|
||||
|
||||
QgsPostgresProjectStorage *gProjectStorage = nullptr; // when not null it is owned by QgsApplication::projectStorageRegistry()
|
||||
|
||||
|
@ -84,7 +84,7 @@ class QgsPostgresProvider : public QgsVectorDataProvider
|
||||
bool overwrite,
|
||||
QMap<int, int> *oldToNewAttrIdxMap,
|
||||
QString *errorMessage = nullptr,
|
||||
const QMap<QString, QVariant> *coordinateTransformContext = nullptr
|
||||
const QMap<QString, QVariant> *options = nullptr
|
||||
);
|
||||
|
||||
/**
|
||||
@ -564,8 +564,14 @@ class QgsPostgresProviderMetadata: public QgsProviderMetadata
|
||||
bool deleteStyleById( const QString &uri, QString styleId, QString &errCause ) override;
|
||||
QString getStyleById( const QString &uri, QString styleId, QString &errCause ) override;
|
||||
QgsTransaction *createTransaction( const QString &connString ) override;
|
||||
QMap<QString, QgsAbstractProviderConnection *> connections( bool cached = true ) override;
|
||||
QgsAbstractProviderConnection *createConnection( const QString &name ) override;
|
||||
QgsAbstractProviderConnection *createConnection( const QString &name, const QString &uri ) override;
|
||||
void deleteConnection( const QString &name ) override;
|
||||
void saveConnection( QgsAbstractProviderConnection *createConnection, const QVariantMap &configuration = QVariantMap() ) override;
|
||||
void initProvider() override;
|
||||
void cleanupProvider() override;
|
||||
|
||||
};
|
||||
|
||||
// clazy:excludeall=qstring-allocations
|
||||
|
475
src/providers/postgres/qgspostgresproviderconnection.cpp
Normal file
475
src/providers/postgres/qgspostgresproviderconnection.cpp
Normal file
@ -0,0 +1,475 @@
|
||||
/***************************************************************************
|
||||
qgspostgresproviderconnection.cpp - QgsPostgresProviderConnection
|
||||
|
||||
---------------------
|
||||
begin : 2.8.2019
|
||||
copyright : (C) 2019 by Alessandro Pasotti
|
||||
email : elpaso at itopen dot it
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#include "qgspostgresproviderconnection.h"
|
||||
#include "qgspostgresconn.h"
|
||||
#include "qgspostgresconnpool.h"
|
||||
#include "qgssettings.h"
|
||||
#include "qgspostgresprovider.h"
|
||||
#include "qgsexception.h"
|
||||
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <libpq-fe.h>
|
||||
}
|
||||
|
||||
QgsPostgresProviderConnection::QgsPostgresProviderConnection( const QString &name ):
|
||||
QgsAbstractDatabaseProviderConnection( name )
|
||||
{
|
||||
// Remove the sql and table empty parts
|
||||
static const QRegularExpression removePartsRe { R"raw(\s*sql=\s*|\s*table=""\s*)raw" };
|
||||
setUri( QgsPostgresConn::connUri( name ).uri().replace( removePartsRe, QString() ) );
|
||||
setDefaultCapabilities();
|
||||
}
|
||||
|
||||
QgsPostgresProviderConnection::QgsPostgresProviderConnection( const QString &name, const QString &uri ):
|
||||
QgsAbstractDatabaseProviderConnection( name, uri )
|
||||
{
|
||||
setUri( uri );
|
||||
setDefaultCapabilities();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void QgsPostgresProviderConnection::setDefaultCapabilities()
|
||||
{
|
||||
// TODO: we might check at this point if the user actually has the privileges and return
|
||||
// properly filtered capabilities instead of all of them
|
||||
mCapabilities =
|
||||
{
|
||||
Capability::DropVectorTable,
|
||||
Capability::CreateVectorTable,
|
||||
Capability::RenameSchema,
|
||||
Capability::DropSchema,
|
||||
Capability::CreateSchema,
|
||||
Capability::RenameVectorTable,
|
||||
Capability::RenameRasterTable,
|
||||
Capability::Vacuum,
|
||||
Capability::ExecuteSql,
|
||||
Capability::SqlLayers,
|
||||
//Capability::Transaction,
|
||||
Capability::Tables,
|
||||
Capability::Schemas,
|
||||
Capability::Spatial,
|
||||
Capability::TableExists
|
||||
};
|
||||
}
|
||||
|
||||
void QgsPostgresProviderConnection::dropTablePrivate( const QString &schema, const QString &name ) const
|
||||
{
|
||||
executeSqlPrivate( QStringLiteral( "DROP TABLE %1.%2" )
|
||||
.arg( QgsPostgresConn::quotedIdentifier( schema ) )
|
||||
.arg( QgsPostgresConn::quotedIdentifier( name ) ) );
|
||||
}
|
||||
|
||||
void QgsPostgresProviderConnection::createVectorTable( const QString &schema,
|
||||
const QString &name,
|
||||
const QgsFields &fields,
|
||||
QgsWkbTypes::Type wkbType,
|
||||
const QgsCoordinateReferenceSystem &srs,
|
||||
bool overwrite,
|
||||
const QMap<QString,
|
||||
QVariant> *options ) const
|
||||
{
|
||||
|
||||
checkCapability( Capability::CreateVectorTable );
|
||||
|
||||
QgsDataSourceUri newUri { uri() };
|
||||
newUri.setSchema( schema );
|
||||
newUri.setTable( name );
|
||||
// Set geometry column if it's not aspatial
|
||||
if ( wkbType != QgsWkbTypes::Type::Unknown && wkbType != QgsWkbTypes::Type::NoGeometry )
|
||||
{
|
||||
newUri.setGeometryColumn( options->value( QStringLiteral( "geometryColumn" ), QStringLiteral( "geom" ) ).toString() );
|
||||
}
|
||||
QMap<int, int> map;
|
||||
QString errCause;
|
||||
QgsVectorLayerExporter::ExportError errCode = QgsPostgresProvider::createEmptyLayer(
|
||||
newUri.uri(),
|
||||
fields,
|
||||
wkbType,
|
||||
srs,
|
||||
overwrite,
|
||||
&map,
|
||||
&errCause,
|
||||
options
|
||||
);
|
||||
if ( errCode != QgsVectorLayerExporter::ExportError::NoError )
|
||||
{
|
||||
throw QgsProviderConnectionException( QObject::tr( "An error occurred while creating the vector layer: %1" ).arg( errCause ) );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsPostgresProviderConnection::dropVectorTable( const QString &schema, const QString &name ) const
|
||||
{
|
||||
checkCapability( Capability::DropVectorTable );
|
||||
dropTablePrivate( schema, name );
|
||||
}
|
||||
|
||||
void QgsPostgresProviderConnection::dropRasterTable( const QString &schema, const QString &name ) const
|
||||
{
|
||||
checkCapability( Capability::DropRasterTable );
|
||||
dropTablePrivate( schema, name );
|
||||
}
|
||||
|
||||
|
||||
void QgsPostgresProviderConnection::renameTablePrivate( const QString &schema, const QString &name, const QString &newName ) const
|
||||
{
|
||||
executeSqlPrivate( QStringLiteral( "ALTER TABLE %1.%2 RENAME TO %3" )
|
||||
.arg( QgsPostgresConn::quotedIdentifier( schema ) )
|
||||
.arg( QgsPostgresConn::quotedIdentifier( name ) )
|
||||
.arg( QgsPostgresConn::quotedIdentifier( newName ) ) );
|
||||
}
|
||||
|
||||
void QgsPostgresProviderConnection::renameVectorTable( const QString &schema, const QString &name, const QString &newName ) const
|
||||
{
|
||||
checkCapability( Capability::RenameVectorTable );
|
||||
renameTablePrivate( schema, name, newName );
|
||||
}
|
||||
|
||||
void QgsPostgresProviderConnection::renameRasterTable( const QString &schema, const QString &name, const QString &newName ) const
|
||||
{
|
||||
checkCapability( Capability::RenameRasterTable );
|
||||
renameTablePrivate( schema, name, newName );
|
||||
}
|
||||
|
||||
void QgsPostgresProviderConnection::createSchema( const QString &name ) const
|
||||
{
|
||||
checkCapability( Capability::CreateSchema );
|
||||
executeSqlPrivate( QStringLiteral( "CREATE SCHEMA %1" )
|
||||
.arg( QgsPostgresConn::quotedIdentifier( name ) ) );
|
||||
|
||||
}
|
||||
|
||||
void QgsPostgresProviderConnection::dropSchema( const QString &name, bool force ) const
|
||||
{
|
||||
checkCapability( Capability::DropSchema );
|
||||
executeSqlPrivate( QStringLiteral( "DROP SCHEMA %1 %2" )
|
||||
.arg( QgsPostgresConn::quotedIdentifier( name ) )
|
||||
.arg( force ? QStringLiteral( "CASCADE" ) : QString() ) );
|
||||
}
|
||||
|
||||
void QgsPostgresProviderConnection::renameSchema( const QString &name, const QString &newName ) const
|
||||
{
|
||||
checkCapability( Capability::RenameSchema );
|
||||
executeSqlPrivate( QStringLiteral( "ALTER SCHEMA %1 RENAME TO %2" )
|
||||
.arg( QgsPostgresConn::quotedIdentifier( name ) )
|
||||
.arg( QgsPostgresConn::quotedIdentifier( newName ) ) );
|
||||
}
|
||||
|
||||
QList<QVariantList> QgsPostgresProviderConnection::executeSql( const QString &sql ) const
|
||||
{
|
||||
checkCapability( Capability::ExecuteSql );
|
||||
return executeSqlPrivate( sql );
|
||||
}
|
||||
|
||||
QList<QVariantList> QgsPostgresProviderConnection::executeSqlPrivate( const QString &sql, bool resolveTypes ) const
|
||||
{
|
||||
const QgsDataSourceUri dsUri { uri() };
|
||||
QList<QVariantList> results;
|
||||
QgsPostgresConn *conn = QgsPostgresConnPool::instance()->acquireConnection( dsUri.connectionInfo( false ) );
|
||||
if ( !conn )
|
||||
{
|
||||
throw QgsProviderConnectionException( QObject::tr( "Connection failed: %1" ).arg( uri() ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsPostgresResult res( conn->PQexec( sql ) );
|
||||
QString errCause;
|
||||
if ( conn->PQstatus() != CONNECTION_OK || ! res.result() )
|
||||
{
|
||||
errCause = QObject::tr( "Connection error: %1 returned %2 [%3]" )
|
||||
.arg( sql ).arg( conn->PQstatus() )
|
||||
.arg( conn->PQerrorMessage() );
|
||||
}
|
||||
else
|
||||
{
|
||||
const QString err { conn->PQerrorMessage() };
|
||||
if ( ! err.isEmpty() )
|
||||
{
|
||||
errCause = QObject::tr( "SQL error: %1 returned %2 [%3]" )
|
||||
.arg( sql )
|
||||
.arg( conn->PQstatus() )
|
||||
.arg( err );
|
||||
}
|
||||
}
|
||||
if ( res.PQntuples() > 0 )
|
||||
{
|
||||
// Try to convert value types at least for basic simple types that can be directly mapped to Python
|
||||
QMap<int, QVariant::Type> typeMap;
|
||||
if ( resolveTypes )
|
||||
{
|
||||
for ( int rowIdx = 0; rowIdx < res.PQnfields(); rowIdx++ )
|
||||
{
|
||||
const Oid oid { res.PQftype( rowIdx ) };
|
||||
QList<QVariantList> typeRes { executeSqlPrivate( QStringLiteral( "SELECT typname FROM pg_type WHERE oid = %1" ).arg( oid ), false ) };
|
||||
// Set the default to string
|
||||
QVariant::Type vType { QVariant::Type::String };
|
||||
if ( typeRes.size() > 0 && typeRes.first().size() > 0 )
|
||||
{
|
||||
static const QStringList intTypes = { QStringLiteral( "oid" ),
|
||||
QStringLiteral( "char" ),
|
||||
QStringLiteral( "int2" ),
|
||||
QStringLiteral( "int4" ),
|
||||
QStringLiteral( "int8" )
|
||||
};
|
||||
static const QStringList floatTypes = { QStringLiteral( "float4" ),
|
||||
QStringLiteral( "float8" ),
|
||||
QStringLiteral( "numeric" )
|
||||
};
|
||||
const QString typName { typeRes.first().first().toString() };
|
||||
|
||||
if ( floatTypes.contains( typName ) )
|
||||
{
|
||||
vType = QVariant::Double;
|
||||
}
|
||||
else if ( intTypes.contains( typName ) )
|
||||
{
|
||||
vType = QVariant::LongLong;
|
||||
}
|
||||
else if ( typName == QStringLiteral( "date" ) )
|
||||
{
|
||||
vType = QVariant::Date;
|
||||
}
|
||||
else if ( typName.startsWith( QStringLiteral( "timestamp" ) ) )
|
||||
{
|
||||
vType = QVariant::DateTime;
|
||||
}
|
||||
else if ( typName == QStringLiteral( "time" ) )
|
||||
{
|
||||
vType = QVariant::Time;
|
||||
}
|
||||
else if ( typName == QStringLiteral( "bool" ) )
|
||||
{
|
||||
vType = QVariant::Bool;
|
||||
}
|
||||
}
|
||||
typeMap[ rowIdx ] = vType;
|
||||
}
|
||||
}
|
||||
for ( int rowIdx = 0; rowIdx < res.PQntuples(); rowIdx++ )
|
||||
{
|
||||
QVariantList row;
|
||||
for ( int colIdx = 0; colIdx < res.PQnfields(); colIdx++ )
|
||||
{
|
||||
if ( resolveTypes )
|
||||
{
|
||||
const QVariant::Type vType { typeMap.value( colIdx, QVariant::Type::String ) };
|
||||
QVariant val { res.PQgetvalue( rowIdx, colIdx ) };
|
||||
if ( val.canConvert( static_cast<int>( vType ) ) )
|
||||
{
|
||||
val.convert( static_cast<int>( vType ) );
|
||||
}
|
||||
row.push_back( val );
|
||||
}
|
||||
else
|
||||
{
|
||||
row.push_back( res.PQgetvalue( rowIdx, colIdx ) );
|
||||
}
|
||||
}
|
||||
results.push_back( row );
|
||||
}
|
||||
}
|
||||
QgsPostgresConnPool::instance()->releaseConnection( conn );
|
||||
if ( ! errCause.isEmpty() )
|
||||
{
|
||||
throw QgsProviderConnectionException( errCause );
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
void QgsPostgresProviderConnection::vacuum( const QString &schema, const QString &name ) const
|
||||
{
|
||||
checkCapability( Capability::Vacuum );
|
||||
executeSql( QStringLiteral( "VACUUM FULL ANALYZE %1.%2" )
|
||||
.arg( QgsPostgresConn::quotedIdentifier( schema ) )
|
||||
.arg( QgsPostgresConn::quotedIdentifier( name ) ) );
|
||||
}
|
||||
|
||||
QList<QgsPostgresProviderConnection::TableProperty> QgsPostgresProviderConnection::tables( const QString &schema, const TableFlags &flags ) const
|
||||
{
|
||||
checkCapability( Capability::Tables );
|
||||
QList<QgsPostgresProviderConnection::TableProperty> tables;
|
||||
QString errCause;
|
||||
// TODO: set flags from the connection if flags argument is 0
|
||||
const QgsDataSourceUri dsUri { uri() };
|
||||
QgsPostgresConn *conn = QgsPostgresConnPool::instance()->acquireConnection( dsUri.connectionInfo( false ) );
|
||||
if ( !conn )
|
||||
{
|
||||
errCause = QObject::tr( "Connection failed: %1" ).arg( uri() );
|
||||
}
|
||||
else
|
||||
{
|
||||
bool ok = conn->getTableInfo( false, false, true, schema );
|
||||
if ( ! ok )
|
||||
{
|
||||
errCause = QObject::tr( "Could not retrieve tables: %1" ).arg( uri() );
|
||||
}
|
||||
else
|
||||
{
|
||||
QVector<QgsPostgresLayerProperty> properties;
|
||||
const bool aspatial { ! flags || flags.testFlag( TableFlag::Aspatial ) };
|
||||
conn->supportedLayers( properties, false, schema == QStringLiteral( "public" ), aspatial, schema );
|
||||
bool dontResolveType = QgsPostgresConn::dontResolveType( name() );
|
||||
|
||||
// Cannot be const:
|
||||
for ( auto &pr : properties )
|
||||
{
|
||||
// Classify
|
||||
TableFlags prFlags;
|
||||
if ( pr.isView )
|
||||
{
|
||||
prFlags.setFlag( QgsPostgresProviderConnection::TableFlag::View );
|
||||
}
|
||||
if ( pr.isMaterializedView )
|
||||
{
|
||||
prFlags.setFlag( QgsPostgresProviderConnection::TableFlag::MaterializedView );
|
||||
}
|
||||
// Table type
|
||||
if ( pr.isRaster )
|
||||
{
|
||||
prFlags.setFlag( QgsPostgresProviderConnection::TableFlag::Raster );
|
||||
}
|
||||
else if ( pr.nSpCols != 0 )
|
||||
{
|
||||
prFlags.setFlag( QgsPostgresProviderConnection::TableFlag::Vector );
|
||||
}
|
||||
else
|
||||
{
|
||||
prFlags.setFlag( QgsPostgresProviderConnection::TableFlag::Aspatial );
|
||||
}
|
||||
// Filter
|
||||
if ( ! flags || ( prFlags & flags ) )
|
||||
{
|
||||
// retrieve layer types if needed
|
||||
if ( ! dontResolveType && ( !pr.geometryColName.isNull() &&
|
||||
( pr.types.value( 0, QgsWkbTypes::Unknown ) == QgsWkbTypes::Unknown ||
|
||||
pr.srids.value( 0, std::numeric_limits<int>::min() ) == std::numeric_limits<int>::min() ) ) )
|
||||
{
|
||||
conn->retrieveLayerTypes( pr, true /* useEstimatedMetadata */ );
|
||||
}
|
||||
QgsPostgresProviderConnection::TableProperty property;
|
||||
property.setFlags( prFlags );
|
||||
for ( int i = 0; i < std::min( pr.types.size(), pr.srids.size() ) ; i++ )
|
||||
{
|
||||
property.addGeometryColumnType( pr.types.at( i ), QgsCoordinateReferenceSystem::fromEpsgId( pr.srids.at( i ) ) );
|
||||
}
|
||||
property.setTableName( pr.tableName );
|
||||
property.setSchema( pr.schemaName );
|
||||
property.setGeometryColumn( pr.geometryColName );
|
||||
property.setPrimaryKeyColumns( pr.pkCols );
|
||||
property.setGeometryColumnCount( static_cast<int>( pr.nSpCols ) );
|
||||
property.setComment( pr.tableComment );
|
||||
tables.push_back( property );
|
||||
}
|
||||
}
|
||||
}
|
||||
QgsPostgresConnPool::instance()->releaseConnection( conn );
|
||||
}
|
||||
if ( ! errCause.isEmpty() )
|
||||
{
|
||||
throw QgsProviderConnectionException( errCause );
|
||||
}
|
||||
return tables;
|
||||
}
|
||||
|
||||
QStringList QgsPostgresProviderConnection::schemas( ) const
|
||||
{
|
||||
checkCapability( Capability::Schemas );
|
||||
QStringList schemas;
|
||||
QString errCause;
|
||||
const QgsDataSourceUri dsUri { uri() };
|
||||
QgsPostgresConn *conn = QgsPostgresConnPool::instance()->acquireConnection( dsUri.connectionInfo( false ) );
|
||||
if ( !conn )
|
||||
{
|
||||
errCause = QObject::tr( "Connection failed: %1" ).arg( uri() );
|
||||
}
|
||||
else
|
||||
{
|
||||
QList<QgsPostgresSchemaProperty> schemaProperties;
|
||||
bool ok = conn->getSchemas( schemaProperties );
|
||||
QgsPostgresConnPool::instance()->releaseConnection( conn );
|
||||
if ( ! ok )
|
||||
{
|
||||
errCause = QObject::tr( "Could not retrieve schemas: %1" ).arg( uri() );
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( const auto &s : qgis::as_const( schemaProperties ) )
|
||||
{
|
||||
schemas.push_back( s.name );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( ! errCause.isEmpty() )
|
||||
{
|
||||
throw QgsProviderConnectionException( errCause );
|
||||
}
|
||||
return schemas;
|
||||
}
|
||||
|
||||
|
||||
void QgsPostgresProviderConnection::store( const QVariantMap &configuration ) const
|
||||
{
|
||||
// TODO: move this to class configuration?
|
||||
QString baseKey = QStringLiteral( "/PostgreSQL/connections/" );
|
||||
// delete the original entry first
|
||||
remove( );
|
||||
|
||||
QgsSettings settings;
|
||||
settings.beginGroup( baseKey );
|
||||
settings.beginGroup( name() );
|
||||
|
||||
// From URI
|
||||
const QgsDataSourceUri dsUri { uri() };
|
||||
settings.setValue( "service", dsUri.service() );
|
||||
settings.setValue( "host", dsUri.host() );
|
||||
settings.setValue( "port", dsUri.port() );
|
||||
settings.setValue( "database", dsUri.database() );
|
||||
settings.setValue( "username", dsUri.username() );
|
||||
settings.setValue( "password", dsUri.password() );
|
||||
settings.setValue( "authcfg", dsUri.authConfigId() );
|
||||
settings.setValue( "sslmode", dsUri.sslMode() );
|
||||
|
||||
// From GUI config
|
||||
static const QStringList guiParameters
|
||||
{
|
||||
QStringLiteral( "publicOnly" ),
|
||||
QStringLiteral( "geometryColumnsOnly" ),
|
||||
QStringLiteral( "dontResolveType" ),
|
||||
QStringLiteral( "allowGeometrylessTables" ),
|
||||
QStringLiteral( "saveUsername" ),
|
||||
QStringLiteral( "savePassword" ),
|
||||
QStringLiteral( "estimatedMetadata" ),
|
||||
QStringLiteral( "projectsInDatabase" )
|
||||
};
|
||||
for ( const auto &p : guiParameters )
|
||||
{
|
||||
if ( configuration.contains( p ) )
|
||||
{
|
||||
settings.setValue( p, configuration.value( p ) );
|
||||
}
|
||||
}
|
||||
settings.endGroup();
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
void QgsPostgresProviderConnection::remove() const
|
||||
{
|
||||
QgsPostgresConn::deleteConnection( name() );
|
||||
}
|
||||
|
63
src/providers/postgres/qgspostgresproviderconnection.h
Normal file
63
src/providers/postgres/qgspostgresproviderconnection.h
Normal file
@ -0,0 +1,63 @@
|
||||
/***************************************************************************
|
||||
qgspostgresproviderconnection.h - QgsPostgresProviderConnection
|
||||
|
||||
---------------------
|
||||
begin : 2.8.2019
|
||||
copyright : (C) 2019 by Alessandro Pasotti
|
||||
email : elpaso at itopen dot it
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#ifndef QGSPOSTGRESPROVIDERCONNECTION_H
|
||||
#define QGSPOSTGRESPROVIDERCONNECTION_H
|
||||
#include "qgsabstractdatabaseproviderconnection.h"
|
||||
|
||||
|
||||
class QgsPostgresProviderConnection : public QgsAbstractDatabaseProviderConnection
|
||||
{
|
||||
public:
|
||||
|
||||
QgsPostgresProviderConnection( const QString &name );
|
||||
QgsPostgresProviderConnection( const QString &name, const QString &uri );
|
||||
|
||||
// QgsAbstractProviderConnection interface
|
||||
|
||||
public:
|
||||
|
||||
void createVectorTable( const QString &schema,
|
||||
const QString &name,
|
||||
const QgsFields &fields,
|
||||
QgsWkbTypes::Type wkbType,
|
||||
const QgsCoordinateReferenceSystem &srs, bool overwrite,
|
||||
const QMap<QString, QVariant> *options ) const override;
|
||||
|
||||
void dropVectorTable( const QString &schema, const QString &name ) const override;
|
||||
void dropRasterTable( const QString &schema, const QString &name ) const override;
|
||||
void renameVectorTable( const QString &schema, const QString &name, const QString &newName ) const override;
|
||||
void renameRasterTable( const QString &schema, const QString &name, const QString &newName ) const override;
|
||||
void createSchema( const QString &name ) const override;
|
||||
void dropSchema( const QString &name, bool force = false ) const override;
|
||||
void renameSchema( const QString &name, const QString &newName ) const override;
|
||||
QList<QVariantList> executeSql( const QString &sql ) const override;
|
||||
void vacuum( const QString &schema, const QString &name ) const override;
|
||||
QList<QgsAbstractDatabaseProviderConnection::TableProperty> tables( const QString &schema,
|
||||
const TableFlags &flags = nullptr ) const override;
|
||||
QStringList schemas( ) const override;
|
||||
void store( const QVariantMap &configuration = QVariantMap() ) const override;
|
||||
void remove() const override;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
QList<QVariantList> executeSqlPrivate( const QString &sql, bool resolveTypes = true ) const;
|
||||
void setDefaultCapabilities();
|
||||
void dropTablePrivate( const QString &schema, const QString &name ) const;
|
||||
void renameTablePrivate( const QString &schema, const QString &name, const QString &newName ) const;
|
||||
};
|
||||
|
||||
#endif // QGSPOSTGRESPROVIDERCONNECTION_H
|
@ -6058,3 +6058,4 @@ QGISEXTERN QgsProviderMetadata *providerMetadataFactory()
|
||||
{
|
||||
return new QgsSpatiaLiteProviderMetadata();
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,6 @@ extern "C"
|
||||
#include "qgsvirtuallayerfeatureiterator.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsproject.h"
|
||||
#include "qgsdatasourceuri.h"
|
||||
#include "qgslogger.h"
|
||||
#include "qgsapplication.h"
|
||||
|
||||
|
@ -775,7 +775,7 @@ namespace QgsWms
|
||||
for ( int i = 0; i < wfsLayerIds.size(); ++i )
|
||||
{
|
||||
QgsMapLayer *layer = project->mapLayer( wfsLayerIds.at( i ) );
|
||||
if ( layer->type() != QgsMapLayerType::VectorLayer )
|
||||
if ( ! layer || layer->type() != QgsMapLayerType::VectorLayer )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ ENDIF (WITH_SERVER)
|
||||
ADD_PYTHON_TEST(PyCoreAdittions test_core_additions.py)
|
||||
ADD_PYTHON_TEST(PyPythonRepr test_python_repr.py)
|
||||
ADD_PYTHON_TEST(PyQgsActionManager test_qgsactionmanager.py)
|
||||
ADD_PYTHON_TEST(PyQgsProviderConnectionPostgres test_qgsproviderconnection_postgres.py)
|
||||
ADD_PYTHON_TEST(PyQgsProviderConnectionGpkg test_qgsproviderconnection_ogr_gpkg.py)
|
||||
ADD_PYTHON_TEST(PyQgsAFSProvider test_provider_afs.py)
|
||||
ADD_PYTHON_TEST(PyQgsAlignmentComboBox test_qgsalignmentcombobox.py)
|
||||
ADD_PYTHON_TEST(PyQgsPythonProvider test_provider_python.py)
|
||||
|
@ -196,6 +196,10 @@ class TestPyQgsDBManagerGpkg(unittest.TestCase):
|
||||
self.assertTrue(new_table.createSpatialIndex())
|
||||
self.assertTrue(new_table.hasSpatialIndex())
|
||||
|
||||
# Test delete spatial index
|
||||
self.assertTrue(new_table.deleteSpatialIndex())
|
||||
self.assertFalse(new_table.hasSpatialIndex())
|
||||
|
||||
self.assertTrue(new_table.delete())
|
||||
|
||||
tables = db.tables()
|
||||
@ -314,7 +318,7 @@ class TestPyQgsDBManagerGpkg(unittest.TestCase):
|
||||
break
|
||||
self.assertIsNotNone(table)
|
||||
info = table.info()
|
||||
expected_html = """<div class="section"><h2>General info</h2><div><table><tr><td>Relation type: </td><td>Table </td></tr><tr><td>Rows: </td><td>Unknown (<a href="action:rows/count">find out</a>) </td></tr></table></div></div><div class="section"><h2>GeoPackage</h2><div><table><tr><td>Column: </td><td> </td></tr><tr><td>Geometry: </td><td>RASTER </td></tr><tr><td>Spatial ref: </td><td>WGS 84 geodetic (4326) </td></tr><tr><td>Extent: </td><td>2.00000, 48.80000 - 2.20000, 49.00000 </td></tr></table></div></div><div class="section"><h2>Fields</h2><div><table class="header"><tr><th># </th><th>Name </th><th>Type </th><th>Null </th><th>Default </th></tr><tr><td>0 </td><td class="underline">id </td><td>INTEGER </td><td>Y </td><td> </td></tr><tr><td>1 </td><td>zoom_level </td><td>INTEGER </td><td>N </td><td> </td></tr><tr><td>2 </td><td>tile_column </td><td>INTEGER </td><td>N </td><td> </td></tr><tr><td>3 </td><td>tile_row </td><td>INTEGER </td><td>N </td><td> </td></tr><tr><td>4 </td><td>tile_data </td><td>BLOB </td><td>N </td><td> </td></tr></table></div></div><div class="section"><h2>Indexes</h2><div><table class="header"><tr><th>Name </th><th>Column(s) </th></tr><tr><td>sqlite_autoindex_raster_table_1 </td><td>zoom_level<br>tile_column<br>tile_row </td></tr></table></div></div>"""
|
||||
expected_html = """<div class="section"><h2>General info</h2><div><table><tr><td>Relation type: </td><td>Table </td></tr><tr><td>Rows: </td><td>Unknown (<a href="action:rows/count">find out</a>) </td></tr></table></div></div><div class="section"><h2>GeoPackage</h2><div><table><tr><td>Column: </td><td> </td></tr><tr><td>Geometry: </td><td>RASTER </td></tr><tr><td>Spatial ref: </td><td>WGS 84 (4326) </td></tr><tr><td>Extent: </td><td>2.00000, 48.80000 - 2.20000, 49.00000 </td></tr></table></div></div><div class="section"><h2>Fields</h2><div><table class="header"><tr><th># </th><th>Name </th><th>Type </th><th>Null </th><th>Default </th></tr><tr><td>0 </td><td class="underline">id </td><td>INTEGER </td><td>Y </td><td> </td></tr><tr><td>1 </td><td>zoom_level </td><td>INTEGER </td><td>N </td><td> </td></tr><tr><td>2 </td><td>tile_column </td><td>INTEGER </td><td>N </td><td> </td></tr><tr><td>3 </td><td>tile_row </td><td>INTEGER </td><td>N </td><td> </td></tr><tr><td>4 </td><td>tile_data </td><td>BLOB </td><td>N </td><td> </td></tr></table></div></div><div class="section"><h2>Indexes</h2><div><table class="header"><tr><th>Name </th><th>Column(s) </th></tr><tr><td>sqlite_autoindex_raster_table_1 </td><td>zoom_level<br>tile_column<br>tile_row </td></tr></table></div></div>"""
|
||||
|
||||
self.assertEqual(info.toHtml(), expected_html)
|
||||
|
||||
|
283
tests/src/python/test_qgsproviderconnection_base.py
Normal file
283
tests/src/python/test_qgsproviderconnection_base.py
Normal file
@ -0,0 +1,283 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""QGIS Base Unit tests for QgsAbastractProviderConnection API.
|
||||
|
||||
Providers must implement a test based on TestPyQgsProviderConnectionBase
|
||||
|
||||
.. note:: This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
"""
|
||||
__author__ = 'Alessandro Pasotti'
|
||||
__date__ = '05/08/2019'
|
||||
__copyright__ = 'Copyright 2019, The QGIS Project'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from qgis.PyQt.QtCore import QCoreApplication, QVariant
|
||||
from qgis.testing import start_app, unittest
|
||||
from qgis.core import (
|
||||
QgsSettings,
|
||||
QgsProviderRegistry,
|
||||
QgsWkbTypes,
|
||||
QgsVectorLayer,
|
||||
QgsFields,
|
||||
QgsCoordinateReferenceSystem,
|
||||
QgsField,
|
||||
QgsAbstractDatabaseProviderConnection,
|
||||
QgsProviderConnectionException,
|
||||
)
|
||||
from qgis.PyQt import QtCore
|
||||
|
||||
|
||||
class TestPyQgsProviderConnectionBase():
|
||||
|
||||
# Provider test cases must define the string URI for the test
|
||||
uri = ''
|
||||
# Provider test cases must define the provider name (e.g. "postgres" or "ogr")
|
||||
providerKey = ''
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""Run before all tests"""
|
||||
QCoreApplication.setOrganizationName("QGIS_Test")
|
||||
QCoreApplication.setOrganizationDomain(cls.__name__)
|
||||
QCoreApplication.setApplicationName(cls.__name__)
|
||||
start_app()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""Run after all tests"""
|
||||
pass
|
||||
|
||||
def setUp(self):
|
||||
QgsSettings().clear()
|
||||
|
||||
def _test_save_load(self, md, uri):
|
||||
"""Common tests on connection save and load"""
|
||||
conn = md.createConnection('qgis_test1', self.uri)
|
||||
md.saveConnection(conn)
|
||||
# Check that we retrieve the new connection
|
||||
self.assertTrue('qgis_test1' in md.connections().keys())
|
||||
self.assertTrue('qgis_test1' in md.dbConnections().keys())
|
||||
return md.connections()['qgis_test1']
|
||||
|
||||
def _table_names(self, table_properties):
|
||||
"""Return table default names from table property list"""
|
||||
return [p.defaultName() for p in table_properties]
|
||||
|
||||
def _table_by_name(self, table_properties, name):
|
||||
"""Filter table properties by table name"""
|
||||
try:
|
||||
return [p for p in table_properties if p.defaultName() == name][0]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def _test_operations(self, md, conn):
|
||||
"""Common tests"""
|
||||
|
||||
capabilities = conn.capabilities()
|
||||
|
||||
# Schema operations
|
||||
if (capabilities & QgsAbstractDatabaseProviderConnection.CreateSchema and
|
||||
capabilities & QgsAbstractDatabaseProviderConnection.Schemas and
|
||||
capabilities & QgsAbstractDatabaseProviderConnection.RenameSchema and
|
||||
capabilities & QgsAbstractDatabaseProviderConnection.DropSchema):
|
||||
if capabilities & QgsAbstractDatabaseProviderConnection.DropSchema and 'myNewSchema' in conn.schemas():
|
||||
conn.dropSchema('myNewSchema', True)
|
||||
# Create
|
||||
conn.createSchema('myNewSchema')
|
||||
schemas = conn.schemas()
|
||||
self.assertTrue('myNewSchema' in schemas)
|
||||
# Create again
|
||||
with self.assertRaises(QgsProviderConnectionException) as ex:
|
||||
conn.createSchema('myNewSchema')
|
||||
# Rename
|
||||
conn.renameSchema('myNewSchema', 'myVeryNewSchema')
|
||||
schemas = conn.schemas()
|
||||
self.assertTrue('myVeryNewSchema' in schemas)
|
||||
self.assertFalse('myNewSchema' in schemas)
|
||||
# Drop
|
||||
conn.dropSchema('myVeryNewSchema')
|
||||
schemas = conn.schemas()
|
||||
self.assertFalse('myVeryNewSchema' in schemas)
|
||||
|
||||
# Table operations
|
||||
if (capabilities & QgsAbstractDatabaseProviderConnection.CreateVectorTable and
|
||||
capabilities & QgsAbstractDatabaseProviderConnection.Tables and
|
||||
capabilities & QgsAbstractDatabaseProviderConnection.RenameVectorTable and
|
||||
capabilities & QgsAbstractDatabaseProviderConnection.DropVectorTable):
|
||||
|
||||
if capabilities & QgsAbstractDatabaseProviderConnection.DropSchema and 'myNewSchema' in conn.schemas():
|
||||
conn.dropSchema('myNewSchema', True)
|
||||
if capabilities & QgsAbstractDatabaseProviderConnection.CreateSchema:
|
||||
schema = 'myNewSchema'
|
||||
conn.createSchema('myNewSchema')
|
||||
else:
|
||||
schema = 'public'
|
||||
|
||||
if 'myNewTable' in self._table_names(conn.tables(schema)):
|
||||
conn.dropVectorTable(schema, 'myNewTable')
|
||||
fields = QgsFields()
|
||||
fields.append(QgsField("string", QVariant.String))
|
||||
fields.append(QgsField("long", QVariant.LongLong))
|
||||
fields.append(QgsField("double", QVariant.Double))
|
||||
fields.append(QgsField("integer", QVariant.Int))
|
||||
fields.append(QgsField("date", QVariant.Date))
|
||||
fields.append(QgsField("datetime", QVariant.DateTime))
|
||||
fields.append(QgsField("time", QVariant.Time))
|
||||
options = {}
|
||||
crs = QgsCoordinateReferenceSystem.fromEpsgId(3857)
|
||||
typ = QgsWkbTypes.LineString
|
||||
# Create
|
||||
conn.createVectorTable(schema, 'myNewTable', fields, typ, crs, True, options)
|
||||
table_names = self._table_names(conn.tables(schema))
|
||||
self.assertTrue('myNewTable' in table_names)
|
||||
|
||||
# Check table information
|
||||
table_properties = conn.tables(schema)
|
||||
table_property = self._table_by_name(table_properties, 'myNewTable')
|
||||
self.assertEqual(table_property.maxCoordinateDimensions(), 2)
|
||||
self.assertIsNotNone(table_property)
|
||||
self.assertEqual(table_property.tableName(), 'myNewTable')
|
||||
self.assertEqual(table_property.geometryColumnCount(), 1)
|
||||
self.assertEqual(table_property.geometryColumnTypes()[0].wkbType, QgsWkbTypes.LineString)
|
||||
self.assertEqual(table_property.geometryColumnTypes()[0].crs, QgsCoordinateReferenceSystem.fromEpsgId(3857))
|
||||
self.assertEqual(table_property.defaultName(), 'myNewTable')
|
||||
|
||||
# Check aspatial tables
|
||||
conn.createVectorTable(schema, 'myNewAspatialTable', fields, QgsWkbTypes.NoGeometry, crs, True, options)
|
||||
table_properties = conn.tables(schema, QgsAbstractDatabaseProviderConnection.Aspatial)
|
||||
table_property = self._table_by_name(table_properties, 'myNewAspatialTable')
|
||||
self.assertIsNotNone(table_property)
|
||||
self.assertEqual(table_property.maxCoordinateDimensions(), 0)
|
||||
self.assertEqual(table_property.tableName(), 'myNewAspatialTable')
|
||||
self.assertEqual(table_property.geometryColumnCount(), 0)
|
||||
self.assertEqual(table_property.geometryColumn(), '')
|
||||
self.assertEqual(table_property.defaultName(), 'myNewAspatialTable')
|
||||
self.assertEqual(table_property.geometryColumnTypes()[0].wkbType, QgsWkbTypes.NoGeometry)
|
||||
self.assertFalse(table_property.geometryColumnTypes()[0].crs.isValid())
|
||||
self.assertFalse(table_property.flags() & QgsAbstractDatabaseProviderConnection.Raster)
|
||||
self.assertFalse(table_property.flags() & QgsAbstractDatabaseProviderConnection.Vector)
|
||||
self.assertTrue(table_property.flags() & QgsAbstractDatabaseProviderConnection.Aspatial)
|
||||
|
||||
# Check executeSql
|
||||
has_schema = capabilities & QgsAbstractDatabaseProviderConnection.Schemas
|
||||
if capabilities & QgsAbstractDatabaseProviderConnection.ExecuteSql:
|
||||
if has_schema:
|
||||
table = "\"%s\".\"myNewAspatialTable\"" % schema
|
||||
else:
|
||||
table = 'myNewAspatialTable'
|
||||
sql = "INSERT INTO %s (string, long, double, integer, date, datetime, time) VALUES ('QGIS Rocks - \U0001f604', 666, 1.234, 1234, '2019-07-08', '2019-07-08T12:00:12', '12:00:13.00')" % table
|
||||
res = conn.executeSql(sql)
|
||||
self.assertEqual(res, [])
|
||||
sql = "SELECT string, long, double, integer, date, datetime FROM %s" % table
|
||||
res = conn.executeSql(sql)
|
||||
# GPKG has no type for time
|
||||
self.assertEqual(res, [['QGIS Rocks - \U0001f604', 666, 1.234, 1234, QtCore.QDate(2019, 7, 8), QtCore.QDateTime(2019, 7, 8, 12, 0, 12)]])
|
||||
sql = "SELECT time FROM %s" % table
|
||||
res = conn.executeSql(sql)
|
||||
self.assertIn(res, ([[QtCore.QTime(12, 0, 13)]], [['12:00:13.00']]))
|
||||
sql = "DELETE FROM %s WHERE string = 'QGIS Rocks - \U0001f604'" % table
|
||||
res = conn.executeSql(sql)
|
||||
self.assertEqual(res, [])
|
||||
sql = "SELECT string, integer FROM %s" % table
|
||||
res = conn.executeSql(sql)
|
||||
self.assertEqual(res, [])
|
||||
|
||||
# Check that we do NOT get the aspatial table when querying for vectors
|
||||
table_names = self._table_names(conn.tables(schema, QgsAbstractDatabaseProviderConnection.Vector))
|
||||
self.assertTrue('myNewTable' in table_names)
|
||||
self.assertFalse('myNewAspatialTable' in table_names)
|
||||
|
||||
# Query for rasters (in qgis_test schema or no schema for GPKG)
|
||||
table_properties = conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Raster)
|
||||
# At leasy one raster should be there
|
||||
self.assertTrue(len(table_properties) >= 1)
|
||||
table_property = table_properties[0]
|
||||
self.assertTrue(table_property.flags() & QgsAbstractDatabaseProviderConnection.Raster)
|
||||
self.assertFalse(table_property.flags() & QgsAbstractDatabaseProviderConnection.Vector)
|
||||
self.assertFalse(table_property.flags() & QgsAbstractDatabaseProviderConnection.Aspatial)
|
||||
|
||||
# Rename
|
||||
conn.renameVectorTable(schema, 'myNewTable', 'myVeryNewTable')
|
||||
tables = self._table_names(conn.tables(schema))
|
||||
self.assertFalse('myNewTable' in tables)
|
||||
self.assertTrue('myVeryNewTable' in tables)
|
||||
# Vacuum
|
||||
if capabilities & QgsAbstractDatabaseProviderConnection.Vacuum:
|
||||
conn.vacuum('myNewSchema', 'myVeryNewTable')
|
||||
|
||||
if capabilities & QgsAbstractDatabaseProviderConnection.DropSchema:
|
||||
# Drop schema (should fail)
|
||||
with self.assertRaises(QgsProviderConnectionException) as ex:
|
||||
conn.dropSchema('myNewSchema')
|
||||
|
||||
# Check some column types operations
|
||||
table = self._table_by_name(conn.tables(schema), 'myVeryNewTable')
|
||||
self.assertEqual(len(table.geometryColumnTypes()), 1)
|
||||
ct = table.geometryColumnTypes()[0]
|
||||
self.assertEqual(ct.crs, QgsCoordinateReferenceSystem.fromEpsgId(3857))
|
||||
self.assertEqual(ct.wkbType, QgsWkbTypes.LineString)
|
||||
# Add a new (existing type)
|
||||
table.addGeometryColumnType(QgsWkbTypes.LineString, QgsCoordinateReferenceSystem.fromEpsgId(3857))
|
||||
self.assertEqual(len(table.geometryColumnTypes()), 1)
|
||||
ct = table.geometryColumnTypes()[0]
|
||||
self.assertEqual(ct.crs, QgsCoordinateReferenceSystem.fromEpsgId(3857))
|
||||
self.assertEqual(ct.wkbType, QgsWkbTypes.LineString)
|
||||
# Add a new one
|
||||
table.addGeometryColumnType(QgsWkbTypes.LineString, QgsCoordinateReferenceSystem.fromEpsgId(4326))
|
||||
self.assertEqual(len(table.geometryColumnTypes()), 2)
|
||||
ct = table.geometryColumnTypes()[0]
|
||||
self.assertEqual(ct.crs, QgsCoordinateReferenceSystem.fromEpsgId(3857))
|
||||
self.assertEqual(ct.wkbType, QgsWkbTypes.LineString)
|
||||
ct = table.geometryColumnTypes()[1]
|
||||
self.assertEqual(ct.crs, QgsCoordinateReferenceSystem.fromEpsgId(4326))
|
||||
self.assertEqual(ct.wkbType, QgsWkbTypes.LineString)
|
||||
|
||||
# Drop table
|
||||
conn.dropVectorTable(schema, 'myVeryNewTable')
|
||||
conn.dropVectorTable(schema, 'myNewAspatialTable')
|
||||
table_names = self._table_names(conn.tables(schema))
|
||||
self.assertFalse('myVeryNewTable' in table_names)
|
||||
|
||||
if capabilities & QgsAbstractDatabaseProviderConnection.DropSchema:
|
||||
# Drop schema
|
||||
conn.dropSchema('myNewSchema')
|
||||
self.assertFalse('myNewSchema' in conn.schemas())
|
||||
|
||||
conns = md.connections()
|
||||
self.assertTrue(isinstance(list(conns.values())[0], QgsAbstractDatabaseProviderConnection))
|
||||
|
||||
# Remove connection
|
||||
md.deleteConnection(conn.name())
|
||||
self.assertEqual(list(md.connections().values()), [])
|
||||
|
||||
def test_errors(self):
|
||||
"""Test SQL errors"""
|
||||
|
||||
md = QgsProviderRegistry.instance().providerMetadata(self.providerKey)
|
||||
conn = self._test_save_load(md, self.uri)
|
||||
|
||||
if conn.capabilities() & QgsAbstractDatabaseProviderConnection.Schemas:
|
||||
with self.assertRaises(QgsProviderConnectionException) as ex:
|
||||
conn.createVectorTable('notExists', 'notReally', QgsFields(), QgsWkbTypes.Point, QgsCoordinateReferenceSystem(), False, {})
|
||||
|
||||
if conn.capabilities() & QgsAbstractDatabaseProviderConnection.DropVectorTable:
|
||||
with self.assertRaises(QgsProviderConnectionException) as ex:
|
||||
conn.executeSql('DROP TABLE "notExists"')
|
||||
|
||||
# Remove connection
|
||||
md.deleteConnection(conn.name())
|
||||
self.assertEqual(list(md.connections().values()), [])
|
||||
|
||||
def test_connections(self):
|
||||
"""Main test"""
|
||||
md = QgsProviderRegistry.instance().providerMetadata(self.providerKey)
|
||||
|
||||
# Run common tests
|
||||
conn = self._test_save_load(md, self.uri)
|
||||
self._test_operations(md, conn)
|
97
tests/src/python/test_qgsproviderconnection_ogr_gpkg.py
Normal file
97
tests/src/python/test_qgsproviderconnection_ogr_gpkg.py
Normal file
@ -0,0 +1,97 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""QGIS Unit tests for OGR GeoPackage QgsAbastractProviderConnection API.
|
||||
|
||||
.. note:: This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
"""
|
||||
__author__ = 'Alessandro Pasotti'
|
||||
__date__ = '10/08/2019'
|
||||
__copyright__ = 'Copyright 2019, The QGIS Project'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from test_qgsproviderconnection_base import TestPyQgsProviderConnectionBase
|
||||
from qgis.core import (
|
||||
QgsWkbTypes,
|
||||
QgsAbstractDatabaseProviderConnection,
|
||||
QgsProviderConnectionException,
|
||||
QgsVectorLayer,
|
||||
QgsProviderRegistry,
|
||||
QgsFields,
|
||||
QgsCoordinateReferenceSystem,
|
||||
)
|
||||
from qgis.testing import unittest
|
||||
from utilities import unitTestDataPath
|
||||
|
||||
TEST_DATA_DIR = unitTestDataPath()
|
||||
|
||||
|
||||
class TestPyQgsProviderConnectionGpkg(unittest.TestCase, TestPyQgsProviderConnectionBase):
|
||||
|
||||
# Provider test cases must define the string URI for the test
|
||||
uri = ''
|
||||
# Provider test cases must define the provider name (e.g. "postgres" or "ogr")
|
||||
providerKey = 'ogr'
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""Run before all tests"""
|
||||
TestPyQgsProviderConnectionBase.setUpClass()
|
||||
gpkg_original_path = '{}/qgis_server/test_project_wms_grouped_layers.gpkg'.format(TEST_DATA_DIR)
|
||||
cls.gpkg_path = '{}/qgis_server/test_project_wms_grouped_layers_test.gpkg'.format(TEST_DATA_DIR)
|
||||
shutil.copy(gpkg_original_path, cls.gpkg_path)
|
||||
vl = QgsVectorLayer('{}|cdb_lines'.format(cls.gpkg_path), 'test', 'ogr')
|
||||
assert vl.isValid()
|
||||
cls.uri = cls.gpkg_path
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""Run after all tests"""
|
||||
os.unlink(cls.gpkg_path)
|
||||
|
||||
def test_gpkg_connections(self):
|
||||
"""Create some connections and retrieve them"""
|
||||
|
||||
md = QgsProviderRegistry.instance().providerMetadata('ogr')
|
||||
|
||||
conn = md.createConnection('qgis_test1', self.uri)
|
||||
md.saveConnection(conn)
|
||||
|
||||
# Retrieve capabilities
|
||||
capabilities = conn.capabilities()
|
||||
self.assertTrue(bool(capabilities & QgsAbstractDatabaseProviderConnection.Tables))
|
||||
self.assertFalse(bool(capabilities & QgsAbstractDatabaseProviderConnection.Schemas))
|
||||
self.assertTrue(bool(capabilities & QgsAbstractDatabaseProviderConnection.CreateVectorTable))
|
||||
self.assertTrue(bool(capabilities & QgsAbstractDatabaseProviderConnection.DropVectorTable))
|
||||
self.assertTrue(bool(capabilities & QgsAbstractDatabaseProviderConnection.RenameVectorTable))
|
||||
self.assertFalse(bool(capabilities & QgsAbstractDatabaseProviderConnection.RenameRasterTable))
|
||||
|
||||
crs = QgsCoordinateReferenceSystem.fromEpsgId(3857)
|
||||
typ = QgsWkbTypes.LineString
|
||||
conn.createVectorTable('', 'myNewAspatialTable', QgsFields(), QgsWkbTypes.NoGeometry, crs, True, {})
|
||||
conn.createVectorTable('', 'myNewTable', QgsFields(), typ, crs, True, {})
|
||||
|
||||
# Check filters and special cases
|
||||
table_names = self._table_names(conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Raster))
|
||||
self.assertTrue('osm' in table_names)
|
||||
self.assertFalse('myNewTable' in table_names)
|
||||
self.assertFalse('myNewAspatialTable' in table_names)
|
||||
|
||||
table_names = self._table_names(conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.View))
|
||||
self.assertFalse('osm' in table_names)
|
||||
self.assertFalse('myNewTable' in table_names)
|
||||
self.assertFalse('myNewAspatialTable' in table_names)
|
||||
|
||||
table_names = self._table_names(conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Aspatial))
|
||||
self.assertFalse('osm' in table_names)
|
||||
self.assertFalse('myNewTable' in table_names)
|
||||
self.assertTrue('myNewAspatialTable' in table_names)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
117
tests/src/python/test_qgsproviderconnection_postgres.py
Normal file
117
tests/src/python/test_qgsproviderconnection_postgres.py
Normal file
@ -0,0 +1,117 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""QGIS Unit tests for Postgres QgsAbastractProviderConnection API.
|
||||
|
||||
.. note:: This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
"""
|
||||
__author__ = 'Alessandro Pasotti'
|
||||
__date__ = '10/08/2019'
|
||||
__copyright__ = 'Copyright 2019, The QGIS Project'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
import os
|
||||
from test_qgsproviderconnection_base import TestPyQgsProviderConnectionBase
|
||||
from qgis.core import (
|
||||
QgsWkbTypes,
|
||||
QgsAbstractDatabaseProviderConnection,
|
||||
QgsProviderConnectionException,
|
||||
QgsVectorLayer,
|
||||
QgsProviderRegistry,
|
||||
QgsCoordinateReferenceSystem,
|
||||
QgsRasterLayer,
|
||||
)
|
||||
from qgis.testing import unittest
|
||||
from osgeo import gdal
|
||||
|
||||
|
||||
class TestPyQgsProviderConnectionPostgres(unittest.TestCase, TestPyQgsProviderConnectionBase):
|
||||
|
||||
# Provider test cases must define the string URI for the test
|
||||
uri = ''
|
||||
# Provider test cases must define the provider name (e.g. "postgres" or "ogr")
|
||||
providerKey = 'postgres'
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""Run before all tests"""
|
||||
TestPyQgsProviderConnectionBase.setUpClass()
|
||||
cls.postgres_conn = 'dbname=\'qgis_test\''
|
||||
if 'QGIS_PGTEST_DB' in os.environ:
|
||||
cls.postgres_conn = os.environ['QGIS_PGTEST_DB']
|
||||
# Create test layers
|
||||
vl = QgsVectorLayer(cls.postgres_conn + ' sslmode=disable key=\'"key1","key2"\' srid=4326 type=POINT table="qgis_test"."someData" (geom) sql=', 'test', 'postgres')
|
||||
assert vl.isValid()
|
||||
cls.uri = cls.postgres_conn + ' port=5432 sslmode=disable '
|
||||
|
||||
def test_postgis_connections(self):
|
||||
"""Create some connections and retrieve them"""
|
||||
|
||||
md = QgsProviderRegistry.instance().providerMetadata('postgres')
|
||||
|
||||
conn = md.createConnection('qgis_test1', self.uri)
|
||||
md.saveConnection(conn)
|
||||
|
||||
# Retrieve capabilities
|
||||
capabilities = conn.capabilities()
|
||||
self.assertTrue(bool(capabilities & QgsAbstractDatabaseProviderConnection.Tables))
|
||||
self.assertTrue(bool(capabilities & QgsAbstractDatabaseProviderConnection.Schemas))
|
||||
self.assertTrue(bool(capabilities & QgsAbstractDatabaseProviderConnection.CreateVectorTable))
|
||||
self.assertTrue(bool(capabilities & QgsAbstractDatabaseProviderConnection.DropVectorTable))
|
||||
self.assertTrue(bool(capabilities & QgsAbstractDatabaseProviderConnection.RenameVectorTable))
|
||||
self.assertTrue(bool(capabilities & QgsAbstractDatabaseProviderConnection.RenameRasterTable))
|
||||
|
||||
# Check filters and special cases
|
||||
table_names = self._table_names(conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Raster))
|
||||
self.assertTrue('Raster1' in table_names)
|
||||
self.assertFalse('geometryless_table' in table_names)
|
||||
self.assertFalse('geometries_table' in table_names)
|
||||
self.assertFalse('geometries_view' in table_names)
|
||||
|
||||
table_names = self._table_names(conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.View))
|
||||
self.assertFalse('Raster1' in table_names)
|
||||
self.assertFalse('geometryless_table' in table_names)
|
||||
self.assertFalse('geometries_table' in table_names)
|
||||
self.assertTrue('geometries_view' in table_names)
|
||||
|
||||
table_names = self._table_names(conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Aspatial))
|
||||
self.assertFalse('Raster1' in table_names)
|
||||
self.assertTrue('geometryless_table' in table_names)
|
||||
self.assertFalse('geometries_table' in table_names)
|
||||
self.assertFalse('geometries_view' in table_names)
|
||||
|
||||
geometries_table = self._table_by_name(conn.tables('qgis_test'), 'geometries_table')
|
||||
srids = [t.crs.postgisSrid() for t in geometries_table.geometryColumnTypes()]
|
||||
self.assertEqual(srids, [0, 0, 0, 3857, 4326, 0])
|
||||
types = [t.wkbType for t in geometries_table.geometryColumnTypes()]
|
||||
self.assertEqual(types, [7, 2, 1, 1, 1, 3])
|
||||
|
||||
# error: ERROR: relation "qgis_test.raster1" does not exist
|
||||
@unittest.skipIf(gdal.VersionInfo() < '2040000', 'This test requires GDAL >= 2.4.0')
|
||||
def test_postgis_raster_rename(self):
|
||||
"""Test raster rename"""
|
||||
|
||||
md = QgsProviderRegistry.instance().providerMetadata('postgres')
|
||||
|
||||
conn = md.createConnection('qgis_test1', self.uri)
|
||||
md.saveConnection(conn)
|
||||
|
||||
table = self._table_by_name(conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Raster), 'Raster1')
|
||||
self.assertTrue(QgsRasterLayer("PG: %s schema='qgis_test' column='%s' table='%s'" % (conn.uri(), table.geometryColumn(), table.tableName()), 'r1', 'gdal').isValid())
|
||||
conn.renameRasterTable('qgis_test', table.tableName(), 'Raster2')
|
||||
table = self._table_by_name(conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Raster), 'Raster2')
|
||||
self.assertTrue(QgsRasterLayer("PG: %s schema='qgis_test' column='%s' table='%s'" % (conn.uri(), table.geometryColumn(), table.tableName()), 'r1', 'gdal').isValid())
|
||||
table_names = self._table_names(conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Raster))
|
||||
self.assertFalse('Raster1' in table_names)
|
||||
self.assertTrue('Raster2' in table_names)
|
||||
conn.renameRasterTable('qgis_test', table.tableName(), 'Raster1')
|
||||
table_names = self._table_names(conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Raster))
|
||||
self.assertFalse('Raster2' in table_names)
|
||||
self.assertTrue('Raster1' in table_names)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
21
tests/testdata/provider/testdata_pg.sql
vendored
21
tests/testdata/provider/testdata_pg.sql
vendored
@ -617,3 +617,24 @@ CREATE OR REPLACE VIEW qgis_test.b21839_pk_unicity_view AS
|
||||
b21839_pk_unicity.geom
|
||||
FROM qgis_test.b21839_pk_unicity;
|
||||
|
||||
|
||||
|
||||
---------------------------------------------
|
||||
--
|
||||
-- Table and views for tests on QgsAbstractProviderConnection
|
||||
--
|
||||
|
||||
CREATE TABLE qgis_test.geometries_table (name VARCHAR, geom GEOMETRY);
|
||||
|
||||
INSERT INTO qgis_test.geometries_table VALUES
|
||||
('Point', 'POINT(0 0)'),
|
||||
('Point4326', 'SRID=4326;POINT(7 45)'),
|
||||
('Point3857', 'SRID=3857;POINT(100 100)'),
|
||||
('Linestring', 'LINESTRING(0 0, 1 1, 2 1, 2 2)'),
|
||||
('Polygon', 'POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'),
|
||||
('PolygonWithHole', 'POLYGON((0 0, 10 0, 10 10, 0 10, 0 0),(1 1, 1 2, 2 2, 2 1, 1 1))'),
|
||||
('Collection', 'GEOMETRYCOLLECTION(POINT(2 0),POLYGON((0 0, 1 0, 1 1, 0 1, 0 0)))');
|
||||
|
||||
CREATE VIEW qgis_test.geometries_view AS (SELECT * FROM qgis_test.geometries_table);
|
||||
|
||||
CREATE TABLE qgis_test.geometryless_table (name VARCHAR, value INTEGER);
|
||||
|
Loading…
x
Reference in New Issue
Block a user