From 84131e0cfc2807d5a482967149bfee1837830b04 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Fri, 18 Dec 2020 18:15:27 +0100 Subject: [PATCH 1/4] Connections API: executeSqlWithNames, return column names This is mainly useful for Python, this way we can probably avoid calling fields() and all related cost when all we need is colum names for display. I'm not very happy with the method name and I would probably just drop the old signature for executeSql and replace it with the new one, unfortunately is public API. But if everybody is happy we could consider an API break. --- ...sabstractdatabaseproviderconnection.sip.in | 22 ++++++++++++++ .../ogr/qgsgeopackageproviderconnection.cpp | 16 ++++++---- .../ogr/qgsgeopackageproviderconnection.h | 5 ++-- .../qgsabstractdatabaseproviderconnection.cpp | 11 +++++-- .../qgsabstractdatabaseproviderconnection.h | 29 +++++++++++++++++++ .../mssql/qgsmssqlproviderconnection.cpp | 19 +++++++----- .../mssql/qgsmssqlproviderconnection.h | 4 +-- .../qgspostgresproviderconnection.cpp | 24 +++++++++++---- .../postgres/qgspostgresproviderconnection.h | 3 +- .../qgsspatialiteproviderconnection.cpp | 18 +++++++----- .../qgsspatialiteproviderconnection.h | 4 +-- .../python/test_qgsproviderconnection_base.py | 7 +++++ 12 files changed, 128 insertions(+), 34 deletions(-) diff --git a/python/core/auto_generated/qgsabstractdatabaseproviderconnection.sip.in b/python/core/auto_generated/qgsabstractdatabaseproviderconnection.sip.in index 80b8a85e40d0..319dc233deee 100644 --- a/python/core/auto_generated/qgsabstractdatabaseproviderconnection.sip.in +++ b/python/core/auto_generated/qgsabstractdatabaseproviderconnection.sip.in @@ -45,6 +45,18 @@ is not supported or cannot be performed without errors. typedef QFlags TableFlags; + struct QueryResult + { + SIP_PYOBJECT __repr__(); +%MethodCode + QString str = QStringLiteral( "" ).arg( sipCpp->rows.size() ); + sipRes = PyUnicode_FromString( str.toUtf8().constData() ); +%End + QStringList columnns; + QList> rows; + }; + + struct TableProperty { @@ -434,6 +446,16 @@ Executes raw ``sql`` and returns the (possibly empty) list of results in a multi Raises a QgsProviderConnectionException if any errors are encountered. :raises :: py:class:`QgsProviderConnectionException` +%End + + virtual QueryResult executeSqlWithNames( const QString &sql, QgsFeedback *feedback = 0 ) const throw( QgsProviderConnectionException ); +%Docstring +Executes raw ``sql`` and returns the (possibly empty) query results, optionally ``feedback`` can be provided. +Raises a QgsProviderConnectionException if any errors are encountered. + +:raises :: py:class:`QgsProviderConnectionException` + +.. versionadded:: 3.18 %End virtual void vacuum( const QString &schema, const QString &name ) const throw( QgsProviderConnectionException ); diff --git a/src/core/providers/ogr/qgsgeopackageproviderconnection.cpp b/src/core/providers/ogr/qgsgeopackageproviderconnection.cpp index d0e2a042f491..0cb7fcdd4a56 100644 --- a/src/core/providers/ogr/qgsgeopackageproviderconnection.cpp +++ b/src/core/providers/ogr/qgsgeopackageproviderconnection.cpp @@ -164,7 +164,7 @@ void QgsGeoPackageProviderConnection::renameVectorTable( const QString &schema, } } -QList> QgsGeoPackageProviderConnection::executeSql( const QString &sql, QgsFeedback *feedback ) const +QgsAbstractDatabaseProviderConnection::QueryResult QgsGeoPackageProviderConnection::executeSqlWithNames( const QString &sql, QgsFeedback *feedback ) const { checkCapability( Capability::ExecuteSql ); return executeGdalSqlPrivate( sql, feedback ); @@ -220,8 +220,8 @@ bool QgsGeoPackageProviderConnection::spatialIndexExists( const QString &schema, { QgsMessageLog::logMessage( QStringLiteral( "Schema is not supported by GPKG, ignoring" ), QStringLiteral( "OGR" ), Qgis::Info ); } - const QList res = executeGdalSqlPrivate( QStringLiteral( "SELECT HasSpatialIndex(%1, %2)" ).arg( QgsSqliteUtils::quotedString( name ), - QgsSqliteUtils::quotedString( geometryColumn ) ) ); + const auto res { executeGdalSqlPrivate( QStringLiteral( "SELECT HasSpatialIndex(%1, %2)" ).arg( QgsSqliteUtils::quotedString( name ), + QgsSqliteUtils::quotedString( geometryColumn ) ) ).rows }; return !res.isEmpty() && !res.at( 0 ).isEmpty() && res.at( 0 ).at( 0 ).toBool(); } @@ -354,9 +354,9 @@ void QgsGeoPackageProviderConnection::setDefaultCapabilities() }; } -QList QgsGeoPackageProviderConnection::executeGdalSqlPrivate( const QString &sql, QgsFeedback *feedback ) const +QgsAbstractDatabaseProviderConnection::QueryResult QgsGeoPackageProviderConnection::executeGdalSqlPrivate( const QString &sql, QgsFeedback *feedback ) const { - QList results; + QgsAbstractDatabaseProviderConnection::QueryResult results; if ( feedback && feedback->isCanceled() ) { @@ -392,6 +392,10 @@ QList QgsGeoPackageProviderConnection::executeGdalSqlPrivate( cons if ( fields.isEmpty() ) { fields = QgsOgrUtils::readOgrFields( fet.get(), QTextCodec::codecForName( "UTF-8" ) ); + for ( const auto &f : qgis::as_const( fields ) ) + { + results.columnns.push_back( f.name() ); + } } if ( ! fields.isEmpty() ) @@ -411,7 +415,7 @@ QList QgsGeoPackageProviderConnection::executeGdalSqlPrivate( cons } } - results.push_back( row ); + results.rows.push_back( row ); } GDALDatasetReleaseResultSet( hDS.get(), ogrLayer ); } diff --git a/src/core/providers/ogr/qgsgeopackageproviderconnection.h b/src/core/providers/ogr/qgsgeopackageproviderconnection.h index dfda6783df3f..fd51c1dd8d3a 100644 --- a/src/core/providers/ogr/qgsgeopackageproviderconnection.h +++ b/src/core/providers/ogr/qgsgeopackageproviderconnection.h @@ -38,7 +38,7 @@ class QgsGeoPackageProviderConnection : public QgsAbstractDatabaseProviderConnec 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> executeSql( const QString &sql, QgsFeedback *feedback = nullptr ) const override; + QueryResult executeSqlWithNames( const QString &sql, QgsFeedback *feedback = nullptr ) const override; void vacuum( const QString &schema, const QString &name ) const override; void createSpatialIndex( const QString &schema, const QString &name, const QgsAbstractDatabaseProviderConnection::SpatialIndexOptions &options = QgsAbstractDatabaseProviderConnection::SpatialIndexOptions() ) const override; bool spatialIndexExists( const QString &schema, const QString &name, const QString &geometryColumn ) const override; @@ -52,7 +52,8 @@ class QgsGeoPackageProviderConnection : public QgsAbstractDatabaseProviderConnec void setDefaultCapabilities(); //! Use GDAL to execute SQL - QList executeGdalSqlPrivate( const QString &sql, QgsFeedback *feedback = nullptr ) const; + QueryResult executeGdalSqlPrivate( const QString &sql, QgsFeedback *feedback = nullptr ) const; + }; diff --git a/src/core/qgsabstractdatabaseproviderconnection.cpp b/src/core/qgsabstractdatabaseproviderconnection.cpp index 973105edb001..b2c9ff8c5322 100644 --- a/src/core/qgsabstractdatabaseproviderconnection.cpp +++ b/src/core/qgsabstractdatabaseproviderconnection.cpp @@ -133,12 +133,19 @@ void QgsAbstractDatabaseProviderConnection::renameSchema( const QString &, const checkCapability( Capability::RenameSchema ); } -QList> QgsAbstractDatabaseProviderConnection::executeSql( const QString &, QgsFeedback * ) const +QList> QgsAbstractDatabaseProviderConnection::executeSql( const QString &sql, QgsFeedback *feedback ) const +{ + return executeSqlWithNames( sql, feedback ).rows; +} + + +QgsAbstractDatabaseProviderConnection::QueryResult QgsAbstractDatabaseProviderConnection::executeSqlWithNames( const QString &, QgsFeedback * ) const { checkCapability( Capability::ExecuteSql ); - return QList>(); + return QueryResult(); } + void QgsAbstractDatabaseProviderConnection::vacuum( const QString &, const QString & ) const { checkCapability( Capability::Vacuum ); diff --git a/src/core/qgsabstractdatabaseproviderconnection.h b/src/core/qgsabstractdatabaseproviderconnection.h index 377958c3ebd3..b5fbfff5e47c 100644 --- a/src/core/qgsabstractdatabaseproviderconnection.h +++ b/src/core/qgsabstractdatabaseproviderconnection.h @@ -67,6 +67,27 @@ class CORE_EXPORT QgsAbstractDatabaseProviderConnection : public QgsAbstractProv Q_DECLARE_FLAGS( TableFlags, TableFlag ) Q_FLAG( TableFlags ) + /** + * The QueryResult class represents the result of a query executed by executeSqlWithNames() + * + * It encapsulates the result rows and a list of the column names. + * The query result may be empty in case the query returns nothing. + * \since QGIS 3.18 + */ + struct CORE_EXPORT QueryResult + { +#ifdef SIP_RUN + SIP_PYOBJECT __repr__(); + % MethodCode + QString str = QStringLiteral( "" ).arg( sipCpp->rows.size() ); + sipRes = PyUnicode_FromString( str.toUtf8().constData() ); + % End +#endif + QStringList columnns; + QList> rows; + }; + + /** * The TableProperty class represents a database table or view. * @@ -460,6 +481,14 @@ class CORE_EXPORT QgsAbstractDatabaseProviderConnection : public QgsAbstractProv */ virtual QList> executeSql( const QString &sql, QgsFeedback *feedback = nullptr ) const SIP_THROW( QgsProviderConnectionException ); + /** + * Executes raw \a sql and returns the (possibly empty) query results, optionally \a feedback can be provided. + * Raises a QgsProviderConnectionException if any errors are encountered. + * \throws QgsProviderConnectionException + * \since QGIS 3.18 + */ + virtual QueryResult executeSqlWithNames( const QString &sql, QgsFeedback *feedback = nullptr ) 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. diff --git a/src/providers/mssql/qgsmssqlproviderconnection.cpp b/src/providers/mssql/qgsmssqlproviderconnection.cpp index 432274f06429..eedea706ffc3 100644 --- a/src/providers/mssql/qgsmssqlproviderconnection.cpp +++ b/src/providers/mssql/qgsmssqlproviderconnection.cpp @@ -15,6 +15,7 @@ ***************************************************************************/ #include +#include #include "qgsmssqlproviderconnection.h" #include "qgsmssqlconnection.h" @@ -213,15 +214,15 @@ void QgsMssqlProviderConnection::dropSchema( const QString &schemaName, bool fo .arg( QgsMssqlProvider::quotedIdentifier( schemaName ) ) ); } -QList QgsMssqlProviderConnection::executeSql( const QString &sql, QgsFeedback *feedback ) const +QgsAbstractDatabaseProviderConnection::QueryResult QgsMssqlProviderConnection::executeSqlWithNames( const QString &sql, QgsFeedback *feedback ) const { checkCapability( Capability::ExecuteSql ); return executeSqlPrivate( sql, true, feedback ); } -QList QgsMssqlProviderConnection::executeSqlPrivate( const QString &sql, bool resolveTypes, QgsFeedback *feedback ) const +QgsAbstractDatabaseProviderConnection::QueryResult QgsMssqlProviderConnection::executeSqlPrivate( const QString &sql, bool resolveTypes, QgsFeedback *feedback ) const { - QList results; + QgsAbstractDatabaseProviderConnection::QueryResult results; if ( feedback && feedback->isCanceled() ) { @@ -264,6 +265,10 @@ QList QgsMssqlProviderConnection::executeSqlPrivate( const QString { const QSqlRecord rec { q.record() }; const int numCols { rec.count() }; + for ( int idx = 0; idx < numCols; ++idx ) + { + results.columnns.push_back( rec.field( idx ).name() ); + } while ( q.next() && ( ! feedback || ! feedback->isCanceled() ) ) { QVariantList row; @@ -278,7 +283,7 @@ QList QgsMssqlProviderConnection::executeSqlPrivate( const QString row.push_back( q.value( col ).toString() ); } } - results.push_back( row ); + results.rows.push_back( row ); } } @@ -360,7 +365,7 @@ QList QgsMssqlProviderConnection::tab .arg( QgsMssqlProvider::quotedValue( schema ) ); } - const QList results { executeSqlPrivate( query, false ) }; + const QList results { executeSqlPrivate( query, false ).rows }; for ( const auto &row : results ) { Q_ASSERT( row.count( ) == 6 ); @@ -394,7 +399,7 @@ QList QgsMssqlProviderConnection::tab // This may fail for invalid geometries try { - const auto geomColResults { executeSqlPrivate( geomColSql ) }; + const auto geomColResults { executeSqlPrivate( geomColSql ).rows }; for ( const auto &row : geomColResults ) { table.addGeometryColumnType( QgsWkbTypes::parseType( row[0].toString() ), @@ -443,7 +448,7 @@ QStringList QgsMssqlProviderConnection::schemas( ) const WHERE u.issqluser = 1 AND u.name NOT IN ('sys', 'guest', 'INFORMATION_SCHEMA') )raw" )}; - const QList result { executeSqlPrivate( sql, false ) }; + const QList result { executeSqlPrivate( sql, false ).rows }; for ( const auto &row : result ) { if ( row.size() > 0 ) diff --git a/src/providers/mssql/qgsmssqlproviderconnection.h b/src/providers/mssql/qgsmssqlproviderconnection.h index 11d62812e46b..e28a345e1c54 100644 --- a/src/providers/mssql/qgsmssqlproviderconnection.h +++ b/src/providers/mssql/qgsmssqlproviderconnection.h @@ -41,7 +41,7 @@ class QgsMssqlProviderConnection : public QgsAbstractDatabaseProviderConnection void dropVectorTable( const QString &schema, const QString &name ) const override; void createSchema( const QString &name ) const override; void dropSchema( const QString &name, bool force = false ) const override; - QList executeSql( const QString &sql, QgsFeedback *feedback ) const override; + QgsAbstractDatabaseProviderConnection::QueryResult executeSqlWithNames( const QString &sql, QgsFeedback *feedback ) const override; QList tables( const QString &schema, const TableFlags &flags = TableFlags() ) const override; QStringList schemas( ) const override; @@ -52,7 +52,7 @@ class QgsMssqlProviderConnection : public QgsAbstractDatabaseProviderConnection private: - QList executeSqlPrivate( const QString &sql, bool resolveTypes = true, QgsFeedback *feedback = nullptr ) const; + QgsAbstractDatabaseProviderConnection::QueryResult executeSqlPrivate( const QString &sql, bool resolveTypes = true, QgsFeedback *feedback = nullptr ) const; void setDefaultCapabilities(); void dropTablePrivate( const QString &schema, const QString &name ) const; void renameTablePrivate( const QString &schema, const QString &name, const QString &newName ) const; diff --git a/src/providers/postgres/qgspostgresproviderconnection.cpp b/src/providers/postgres/qgspostgresproviderconnection.cpp index 3966d641f91a..8ec2813d508d 100644 --- a/src/providers/postgres/qgspostgresproviderconnection.cpp +++ b/src/providers/postgres/qgspostgresproviderconnection.cpp @@ -192,15 +192,21 @@ void QgsPostgresProviderConnection::renameSchema( const QString &name, const QSt .arg( QgsPostgresConn::quotedIdentifier( newName ) ) ); } -QList QgsPostgresProviderConnection::executeSql( const QString &sql, QgsFeedback *feedback ) const +QgsAbstractDatabaseProviderConnection::QueryResult QgsPostgresProviderConnection::executeSqlWithNames( const QString &sql, QgsFeedback *feedback ) const { checkCapability( Capability::ExecuteSql ); - return executeSqlPrivate( sql, true, feedback ); + return executeSqlPrivateWithNames( sql, true, feedback ); } QList QgsPostgresProviderConnection::executeSqlPrivate( const QString &sql, bool resolveTypes, QgsFeedback *feedback, std::shared_ptr pgconn ) const { - QList results; + QStringList columnNames; + return executeSqlPrivateWithNames( sql, resolveTypes, feedback, pgconn ).rows; +} + +QgsAbstractDatabaseProviderConnection::QueryResult QgsPostgresProviderConnection::executeSqlPrivateWithNames( const QString &sql, bool resolveTypes, QgsFeedback *feedback, std::shared_ptr pgconn ) const +{ + QueryResult results; // Check feedback first! if ( feedback && feedback->isCanceled() ) @@ -257,8 +263,16 @@ QList QgsPostgresProviderConnection::executeSqlPrivate( const QStr .arg( err ); } } + if ( res.PQntuples() > 0 ) { + + // Get column names + for ( int rowIdx = 0; rowIdx < res.PQnfields(); rowIdx++ ) + { + results.columnns.push_back( res.PQfname( rowIdx ) ); + } + // Try to convert value types at least for basic simple types that can be directly mapped to Python QMap typeMap; if ( resolveTypes ) @@ -355,7 +369,7 @@ QList QgsPostgresProviderConnection::executeSqlPrivate( const QStr row.push_back( res.PQgetvalue( rowIdx, colIdx ) ); } } - results.push_back( row ); + results.rows.push_back( row ); } } if ( ! errCause.isEmpty() ) @@ -693,7 +707,7 @@ QList QgsPostgresProviderConnection::nativeTy QgsFields QgsPostgresProviderConnection::fields( const QString &schema, const QString &tableName ) const { - // Try the base implementation first and fall back to a more complex approch for the + // Try the base implementation first and fall back to a more complex approach for the // few PG-specific corner cases that do not work with the base implementation. try { diff --git a/src/providers/postgres/qgspostgresproviderconnection.h b/src/providers/postgres/qgspostgresproviderconnection.h index 3c147687ed55..1aecc97a4359 100644 --- a/src/providers/postgres/qgspostgresproviderconnection.h +++ b/src/providers/postgres/qgspostgresproviderconnection.h @@ -46,7 +46,7 @@ class QgsPostgresProviderConnection : public QgsAbstractDatabaseProviderConnecti 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 executeSql( const QString &sql, QgsFeedback *feedback = nullptr ) const override; + QgsAbstractDatabaseProviderConnection::QueryResult executeSqlWithNames( const QString &sql, QgsFeedback *feedback = nullptr ) const override; void vacuum( const QString &schema, const QString &name ) const override; void createSpatialIndex( const QString &schema, const QString &name, const QgsAbstractDatabaseProviderConnection::SpatialIndexOptions &options = QgsAbstractDatabaseProviderConnection::SpatialIndexOptions() ) const override; bool spatialIndexExists( const QString &schema, const QString &name, const QString &geometryColumn ) const override; @@ -62,6 +62,7 @@ class QgsPostgresProviderConnection : public QgsAbstractDatabaseProviderConnecti private: QList executeSqlPrivate( const QString &sql, bool resolveTypes = true, QgsFeedback *feedback = nullptr, std::shared_ptr< class QgsPoolPostgresConn > pgconn = nullptr ) const; + QgsAbstractDatabaseProviderConnection::QueryResult executeSqlPrivateWithNames( const QString &sql, bool resolveTypes = true, QgsFeedback *feedback = nullptr, std::shared_ptr< class QgsPoolPostgresConn > pgconn = nullptr ) const; void setDefaultCapabilities(); void dropTablePrivate( const QString &schema, const QString &name ) const; void renameTablePrivate( const QString &schema, const QString &name, const QString &newName ) const; diff --git a/src/providers/spatialite/qgsspatialiteproviderconnection.cpp b/src/providers/spatialite/qgsspatialiteproviderconnection.cpp index 1893c9770294..2777f4f1be43 100644 --- a/src/providers/spatialite/qgsspatialiteproviderconnection.cpp +++ b/src/providers/spatialite/qgsspatialiteproviderconnection.cpp @@ -181,7 +181,7 @@ void QgsSpatiaLiteProviderConnection::renameVectorTable( const QString &schema, } } -QList> QgsSpatiaLiteProviderConnection::executeSql( const QString &sql, QgsFeedback *feedback ) const +QgsAbstractDatabaseProviderConnection::QueryResult QgsSpatiaLiteProviderConnection::executeSqlWithNames( const QString &sql, QgsFeedback *feedback ) const { checkCapability( Capability::ExecuteSql ); return executeSqlPrivate( sql, feedback ); @@ -241,7 +241,7 @@ bool QgsSpatiaLiteProviderConnection::spatialIndexExists( const QString &schema, } const QList res = executeSqlPrivate( QStringLiteral( "SELECT spatial_index_enabled FROM geometry_columns WHERE lower(f_table_name) = lower(%1) AND lower(f_geometry_column) = lower(%2)" ) .arg( QgsSqliteUtils::quotedString( name ), - QgsSqliteUtils::quotedString( geometryColumn ) ) ); + QgsSqliteUtils::quotedString( geometryColumn ) ) ).rows; return !res.isEmpty() && !res.at( 0 ).isEmpty() && res.at( 0 ).at( 0 ).toInt() == 1; } @@ -294,7 +294,7 @@ QList QgsSpatiaLiteProviderConne // Need to store it here because provider (and underlying gaia library) returns views as spatial table if they have geometries QStringList viewNames; - for ( const auto &tn : executeSqlPrivate( QStringLiteral( "SELECT name FROM sqlite_master WHERE type = 'view'" ) ) ) + for ( const auto &tn : executeSqlPrivate( QStringLiteral( "SELECT name FROM sqlite_master WHERE type = 'view'" ) ).rows ) { viewNames.push_back( tn.first().toString() ); } @@ -302,7 +302,7 @@ QList QgsSpatiaLiteProviderConne // Another weirdness: table names are converted to lowercase when out of spatialite gaia functions, let's get them back to their real case here, // may need LAUNDER on open, but let's try to make it consistent with how GPKG works. QgsStringMap tableNotLowercaseNames; - for ( const auto &tn : executeSqlPrivate( QStringLiteral( "SELECT name FROM sqlite_master WHERE LOWER(name) != name" ) ) ) + for ( const auto &tn : executeSqlPrivate( QStringLiteral( "SELECT name FROM sqlite_master WHERE LOWER(name) != name" ) ).rows ) { const QString tName { tn.first().toString() }; tableNotLowercaseNames.insert( tName.toLower(), tName ); @@ -395,9 +395,9 @@ void QgsSpatiaLiteProviderConnection::setDefaultCapabilities() }; } -QList QgsSpatiaLiteProviderConnection::executeSqlPrivate( const QString &sql, QgsFeedback *feedback ) const +QgsAbstractDatabaseProviderConnection::QueryResult QgsSpatiaLiteProviderConnection::executeSqlPrivate( const QString &sql, QgsFeedback *feedback ) const { - QList results; + QgsAbstractDatabaseProviderConnection::QueryResult results; if ( feedback && feedback->isCanceled() ) { @@ -433,6 +433,10 @@ QList QgsSpatiaLiteProviderConnection::executeSqlPrivate( const QS if ( fields.isEmpty() ) { fields = QgsOgrUtils::readOgrFields( fet.get(), QTextCodec::codecForName( "UTF-8" ) ); + for ( const auto &f : qgis::as_const( fields ) ) + { + results.columnns.push_back( f.name() ); + } } if ( ! fields.isEmpty() ) { @@ -451,7 +455,7 @@ QList QgsSpatiaLiteProviderConnection::executeSqlPrivate( const QS } } - results.push_back( row ); + results.rows.push_back( row ); } GDALDatasetReleaseResultSet( hDS.get(), ogrLayer ); } diff --git a/src/providers/spatialite/qgsspatialiteproviderconnection.h b/src/providers/spatialite/qgsspatialiteproviderconnection.h index c6f6cffd1f0b..9eca70f72d87 100644 --- a/src/providers/spatialite/qgsspatialiteproviderconnection.h +++ b/src/providers/spatialite/qgsspatialiteproviderconnection.h @@ -38,7 +38,7 @@ class QgsSpatiaLiteProviderConnection : public QgsAbstractDatabaseProviderConnec void createVectorTable( const QString &schema, const QString &name, const QgsFields &fields, QgsWkbTypes::Type wkbType, const QgsCoordinateReferenceSystem &srs, bool overwrite, const QMap *options ) const override; void dropVectorTable( const QString &schema, const QString &name ) const override; void renameVectorTable( const QString &schema, const QString &name, const QString &newName ) const override; - QList> executeSql( const QString &sql, QgsFeedback *feedback = nullptr ) const override; + QgsAbstractDatabaseProviderConnection::QueryResult executeSqlWithNames( const QString &sql, QgsFeedback *feedback = nullptr ) const override; void vacuum( const QString &schema, const QString &name ) const override; void createSpatialIndex( const QString &schema, const QString &name, const QgsAbstractDatabaseProviderConnection::SpatialIndexOptions &options = QgsAbstractDatabaseProviderConnection::SpatialIndexOptions() ) const override; bool spatialIndexExists( const QString &schema, const QString &name, const QString &geometryColumn ) const override; @@ -52,7 +52,7 @@ class QgsSpatiaLiteProviderConnection : public QgsAbstractDatabaseProviderConnec void setDefaultCapabilities(); //! Use GDAL to execute SQL - QList executeSqlPrivate( const QString &sql, QgsFeedback *feedback = nullptr ) const; + QgsAbstractDatabaseProviderConnection::QueryResult executeSqlPrivate( const QString &sql, QgsFeedback *feedback = nullptr ) const; //! Executes SQL directly using sqlite3 -- avoids the extra consistency checks which GDAL requires when opening a spatialite database bool executeSqlDirect( const QString &sql ) const; diff --git a/tests/src/python/test_qgsproviderconnection_base.py b/tests/src/python/test_qgsproviderconnection_base.py index 87a95d88c534..d0eb8e2e90ec 100644 --- a/tests/src/python/test_qgsproviderconnection_base.py +++ b/tests/src/python/test_qgsproviderconnection_base.py @@ -227,8 +227,15 @@ def _test_operations(self, md, conn): self.assertEqual(res, []) sql = "SELECT string_t, long_t, double_t, integer_t, date_t, datetime_t FROM %s" % table res = conn.executeSql(sql) + # GPKG and spatialite have no type for time self.assertEqual(res, [['QGIS Rocks - \U0001f604', 666, 1.234, 1234, QtCore.QDate(2019, 7, 8) if not self.treat_date_as_string() else '2019-07-08', QtCore.QDateTime(2019, 7, 8, 12, 0, 12)]]) + + # Test column names + res = conn.executeSqlWithNames(sql) + self.assertEqual(res.rows, [['QGIS Rocks - \U0001f604', 666, 1.234, 1234, QtCore.QDate(2019, 7, 8) if not self.treat_date_as_string() else '2019-07-08', QtCore.QDateTime(2019, 7, 8, 12, 0, 12)]]) + self.assertEqual(res.columns, ['string_t', 'long_t', 'double_t', 'integer_t', 'date_t', 'datetime_t']) + sql = "SELECT time_t FROM %s" % table res = conn.executeSql(sql) From a277cc63324ae52be86bfbc1f67e74a3e0f0e6ea Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 21 Dec 2020 10:43:51 +0100 Subject: [PATCH 2/4] QgsAbstractDatabaseProviderConnection::QueryResult proper encapsulation and rename to execSql --- ...sabstractdatabaseproviderconnection.sip.in | 35 ++++++++++++--- .../ogr/qgsgeopackageproviderconnection.cpp | 8 ++-- .../ogr/qgsgeopackageproviderconnection.h | 2 +- .../qgsabstractdatabaseproviderconnection.cpp | 24 +++++++++- .../qgsabstractdatabaseproviderconnection.h | 44 +++++++++++++++---- .../mssql/qgsmssqlproviderconnection.cpp | 12 ++--- .../mssql/qgsmssqlproviderconnection.h | 2 +- .../qgspostgresproviderconnection.cpp | 8 ++-- .../postgres/qgspostgresproviderconnection.h | 2 +- .../qgsspatialiteproviderconnection.cpp | 12 ++--- .../qgsspatialiteproviderconnection.h | 2 +- .../python/test_qgsproviderconnection_base.py | 6 +-- 12 files changed, 113 insertions(+), 44 deletions(-) diff --git a/python/core/auto_generated/qgsabstractdatabaseproviderconnection.sip.in b/python/core/auto_generated/qgsabstractdatabaseproviderconnection.sip.in index 319dc233deee..8e0778158ca1 100644 --- a/python/core/auto_generated/qgsabstractdatabaseproviderconnection.sip.in +++ b/python/core/auto_generated/qgsabstractdatabaseproviderconnection.sip.in @@ -47,13 +47,32 @@ is not supported or cannot be performed without errors. struct QueryResult { - SIP_PYOBJECT __repr__(); + SIP_PYOBJECT __repr__(); %MethodCode - QString str = QStringLiteral( "" ).arg( sipCpp->rows.size() ); - sipRes = PyUnicode_FromString( str.toUtf8().constData() ); + QString str = QStringLiteral( "" ).arg( sipCpp->rows().size() ); + sipRes = PyUnicode_FromString( str.toUtf8().constData() ); +%End + + QStringList columns() const; +%Docstring +Returns the column names %End - QStringList columnns; - QList> rows; + + QList > rows() const; +%Docstring +Returns the results rows +%End + + void appendColumn( const QString &columnName ); +%Docstring +Appends ``columnName`` to the list of column names. +%End + + void appendRow( const QList &row ); +%Docstring +Appends ``row`` to the results. +%End + }; @@ -445,14 +464,18 @@ Raises a QgsProviderConnectionException if any errors are encountered. Executes raw ``sql`` and returns the (possibly empty) list of results in a multi-dimensional array, optionally ``feedback`` can be provided. Raises a QgsProviderConnectionException if any errors are encountered. +.. seealso:: :py:func:`execSql` + :raises :: py:class:`QgsProviderConnectionException` %End - virtual QueryResult executeSqlWithNames( const QString &sql, QgsFeedback *feedback = 0 ) const throw( QgsProviderConnectionException ); + virtual QueryResult execSql( const QString &sql, QgsFeedback *feedback = 0 ) const throw( QgsProviderConnectionException ); %Docstring Executes raw ``sql`` and returns the (possibly empty) query results, optionally ``feedback`` can be provided. Raises a QgsProviderConnectionException if any errors are encountered. +.. seealso:: :py:func:`executeSql` + :raises :: py:class:`QgsProviderConnectionException` .. versionadded:: 3.18 diff --git a/src/core/providers/ogr/qgsgeopackageproviderconnection.cpp b/src/core/providers/ogr/qgsgeopackageproviderconnection.cpp index 0cb7fcdd4a56..0a48f535f1ed 100644 --- a/src/core/providers/ogr/qgsgeopackageproviderconnection.cpp +++ b/src/core/providers/ogr/qgsgeopackageproviderconnection.cpp @@ -164,7 +164,7 @@ void QgsGeoPackageProviderConnection::renameVectorTable( const QString &schema, } } -QgsAbstractDatabaseProviderConnection::QueryResult QgsGeoPackageProviderConnection::executeSqlWithNames( const QString &sql, QgsFeedback *feedback ) const +QgsAbstractDatabaseProviderConnection::QueryResult QgsGeoPackageProviderConnection::execSql( const QString &sql, QgsFeedback *feedback ) const { checkCapability( Capability::ExecuteSql ); return executeGdalSqlPrivate( sql, feedback ); @@ -221,7 +221,7 @@ bool QgsGeoPackageProviderConnection::spatialIndexExists( const QString &schema, QgsMessageLog::logMessage( QStringLiteral( "Schema is not supported by GPKG, ignoring" ), QStringLiteral( "OGR" ), Qgis::Info ); } const auto res { executeGdalSqlPrivate( QStringLiteral( "SELECT HasSpatialIndex(%1, %2)" ).arg( QgsSqliteUtils::quotedString( name ), - QgsSqliteUtils::quotedString( geometryColumn ) ) ).rows }; + QgsSqliteUtils::quotedString( geometryColumn ) ) ).rows() }; return !res.isEmpty() && !res.at( 0 ).isEmpty() && res.at( 0 ).at( 0 ).toBool(); } @@ -394,7 +394,7 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsGeoPackageProviderConnecti fields = QgsOgrUtils::readOgrFields( fet.get(), QTextCodec::codecForName( "UTF-8" ) ); for ( const auto &f : qgis::as_const( fields ) ) { - results.columnns.push_back( f.name() ); + results.appendColumn( f.name() ); } } @@ -415,7 +415,7 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsGeoPackageProviderConnecti } } - results.rows.push_back( row ); + results.appendRow( row ); } GDALDatasetReleaseResultSet( hDS.get(), ogrLayer ); } diff --git a/src/core/providers/ogr/qgsgeopackageproviderconnection.h b/src/core/providers/ogr/qgsgeopackageproviderconnection.h index fd51c1dd8d3a..bdd77432748c 100644 --- a/src/core/providers/ogr/qgsgeopackageproviderconnection.h +++ b/src/core/providers/ogr/qgsgeopackageproviderconnection.h @@ -38,7 +38,7 @@ class QgsGeoPackageProviderConnection : public QgsAbstractDatabaseProviderConnec 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; - QueryResult executeSqlWithNames( const QString &sql, QgsFeedback *feedback = nullptr ) const override; + QueryResult execSql( const QString &sql, QgsFeedback *feedback = nullptr ) const override; void vacuum( const QString &schema, const QString &name ) const override; void createSpatialIndex( const QString &schema, const QString &name, const QgsAbstractDatabaseProviderConnection::SpatialIndexOptions &options = QgsAbstractDatabaseProviderConnection::SpatialIndexOptions() ) const override; bool spatialIndexExists( const QString &schema, const QString &name, const QString &geometryColumn ) const override; diff --git a/src/core/qgsabstractdatabaseproviderconnection.cpp b/src/core/qgsabstractdatabaseproviderconnection.cpp index b2c9ff8c5322..75143317084a 100644 --- a/src/core/qgsabstractdatabaseproviderconnection.cpp +++ b/src/core/qgsabstractdatabaseproviderconnection.cpp @@ -135,11 +135,11 @@ void QgsAbstractDatabaseProviderConnection::renameSchema( const QString &, const QList> QgsAbstractDatabaseProviderConnection::executeSql( const QString &sql, QgsFeedback *feedback ) const { - return executeSqlWithNames( sql, feedback ).rows; + return execSql( sql, feedback ).rows(); } -QgsAbstractDatabaseProviderConnection::QueryResult QgsAbstractDatabaseProviderConnection::executeSqlWithNames( const QString &, QgsFeedback * ) const +QgsAbstractDatabaseProviderConnection::QueryResult QgsAbstractDatabaseProviderConnection::execSql( const QString &, QgsFeedback * ) const { checkCapability( Capability::ExecuteSql ); return QueryResult(); @@ -434,3 +434,23 @@ void QgsAbstractDatabaseProviderConnection::TableProperty::setSchema( const QStr mSchema = schema; } + +QStringList QgsAbstractDatabaseProviderConnection::QueryResult::columns() const +{ + return mColumns; +} + +QList > QgsAbstractDatabaseProviderConnection::QueryResult::rows() const +{ + return mRows; +} + +void QgsAbstractDatabaseProviderConnection::QueryResult::appendColumn( const QString &columnName ) +{ + mColumns.push_back( columnName ); +} + +void QgsAbstractDatabaseProviderConnection::QueryResult::appendRow( const QList &row ) +{ + mRows.push_back( row ); +} diff --git a/src/core/qgsabstractdatabaseproviderconnection.h b/src/core/qgsabstractdatabaseproviderconnection.h index b5fbfff5e47c..150e345672f5 100644 --- a/src/core/qgsabstractdatabaseproviderconnection.h +++ b/src/core/qgsabstractdatabaseproviderconnection.h @@ -68,7 +68,7 @@ class CORE_EXPORT QgsAbstractDatabaseProviderConnection : public QgsAbstractProv Q_FLAG( TableFlags ) /** - * The QueryResult class represents the result of a query executed by executeSqlWithNames() + * The QueryResult class represents the result of a query executed by execSql() * * It encapsulates the result rows and a list of the column names. * The query result may be empty in case the query returns nothing. @@ -77,14 +77,38 @@ class CORE_EXPORT QgsAbstractDatabaseProviderConnection : public QgsAbstractProv struct CORE_EXPORT QueryResult { #ifdef SIP_RUN - SIP_PYOBJECT __repr__(); - % MethodCode - QString str = QStringLiteral( "" ).arg( sipCpp->rows.size() ); - sipRes = PyUnicode_FromString( str.toUtf8().constData() ); - % End + SIP_PYOBJECT __repr__(); + % MethodCode + QString str = QStringLiteral( "" ).arg( sipCpp->rows().size() ); + sipRes = PyUnicode_FromString( str.toUtf8().constData() ); + % End #endif - QStringList columnns; - QList> rows; + + /** + * Returns the column names + */ + QStringList columns() const; + + /** + *Returns the results rows + */ + QList > rows() const; + + /** + * Appends \a columnName to the list of column names. + */ + void appendColumn( const QString &columnName ); + + /** + * Appends \a row to the results. + */ + void appendRow( const QList &row ); + + private: + + QStringList mColumns; + QList> mRows; + }; @@ -477,6 +501,7 @@ class CORE_EXPORT QgsAbstractDatabaseProviderConnection : public QgsAbstractProv /** * Executes raw \a sql and returns the (possibly empty) list of results in a multi-dimensional array, optionally \a feedback can be provided. * Raises a QgsProviderConnectionException if any errors are encountered. + * \see execSql() * \throws QgsProviderConnectionException */ virtual QList> executeSql( const QString &sql, QgsFeedback *feedback = nullptr ) const SIP_THROW( QgsProviderConnectionException ); @@ -484,10 +509,11 @@ class CORE_EXPORT QgsAbstractDatabaseProviderConnection : public QgsAbstractProv /** * Executes raw \a sql and returns the (possibly empty) query results, optionally \a feedback can be provided. * Raises a QgsProviderConnectionException if any errors are encountered. + * \see executeSql() * \throws QgsProviderConnectionException * \since QGIS 3.18 */ - virtual QueryResult executeSqlWithNames( const QString &sql, QgsFeedback *feedback = nullptr ) const SIP_THROW( QgsProviderConnectionException ); + virtual QueryResult execSql( const QString &sql, QgsFeedback *feedback = nullptr ) const SIP_THROW( QgsProviderConnectionException ); /** * Vacuum the database table with given \a schema and \a name (schema is ignored if not supported by the backend). diff --git a/src/providers/mssql/qgsmssqlproviderconnection.cpp b/src/providers/mssql/qgsmssqlproviderconnection.cpp index eedea706ffc3..0c7cae0ffe9a 100644 --- a/src/providers/mssql/qgsmssqlproviderconnection.cpp +++ b/src/providers/mssql/qgsmssqlproviderconnection.cpp @@ -214,7 +214,7 @@ void QgsMssqlProviderConnection::dropSchema( const QString &schemaName, bool fo .arg( QgsMssqlProvider::quotedIdentifier( schemaName ) ) ); } -QgsAbstractDatabaseProviderConnection::QueryResult QgsMssqlProviderConnection::executeSqlWithNames( const QString &sql, QgsFeedback *feedback ) const +QgsAbstractDatabaseProviderConnection::QueryResult QgsMssqlProviderConnection::execSql( const QString &sql, QgsFeedback *feedback ) const { checkCapability( Capability::ExecuteSql ); return executeSqlPrivate( sql, true, feedback ); @@ -267,7 +267,7 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsMssqlProviderConnection::e const int numCols { rec.count() }; for ( int idx = 0; idx < numCols; ++idx ) { - results.columnns.push_back( rec.field( idx ).name() ); + results.appendColumn( rec.field( idx ).name() ); } while ( q.next() && ( ! feedback || ! feedback->isCanceled() ) ) { @@ -283,7 +283,7 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsMssqlProviderConnection::e row.push_back( q.value( col ).toString() ); } } - results.rows.push_back( row ); + results.appendRow( row ); } } @@ -365,7 +365,7 @@ QList QgsMssqlProviderConnection::tab .arg( QgsMssqlProvider::quotedValue( schema ) ); } - const QList results { executeSqlPrivate( query, false ).rows }; + const QList results { executeSqlPrivate( query, false ).rows() }; for ( const auto &row : results ) { Q_ASSERT( row.count( ) == 6 ); @@ -399,7 +399,7 @@ QList QgsMssqlProviderConnection::tab // This may fail for invalid geometries try { - const auto geomColResults { executeSqlPrivate( geomColSql ).rows }; + const auto geomColResults { executeSqlPrivate( geomColSql ).rows() }; for ( const auto &row : geomColResults ) { table.addGeometryColumnType( QgsWkbTypes::parseType( row[0].toString() ), @@ -448,7 +448,7 @@ QStringList QgsMssqlProviderConnection::schemas( ) const WHERE u.issqluser = 1 AND u.name NOT IN ('sys', 'guest', 'INFORMATION_SCHEMA') )raw" )}; - const QList result { executeSqlPrivate( sql, false ).rows }; + const QList result { executeSqlPrivate( sql, false ).rows() }; for ( const auto &row : result ) { if ( row.size() > 0 ) diff --git a/src/providers/mssql/qgsmssqlproviderconnection.h b/src/providers/mssql/qgsmssqlproviderconnection.h index e28a345e1c54..0eed6e92d3c0 100644 --- a/src/providers/mssql/qgsmssqlproviderconnection.h +++ b/src/providers/mssql/qgsmssqlproviderconnection.h @@ -41,7 +41,7 @@ class QgsMssqlProviderConnection : public QgsAbstractDatabaseProviderConnection void dropVectorTable( const QString &schema, const QString &name ) const override; void createSchema( const QString &name ) const override; void dropSchema( const QString &name, bool force = false ) const override; - QgsAbstractDatabaseProviderConnection::QueryResult executeSqlWithNames( const QString &sql, QgsFeedback *feedback ) const override; + QgsAbstractDatabaseProviderConnection::QueryResult execSql( const QString &sql, QgsFeedback *feedback ) const override; QList tables( const QString &schema, const TableFlags &flags = TableFlags() ) const override; QStringList schemas( ) const override; diff --git a/src/providers/postgres/qgspostgresproviderconnection.cpp b/src/providers/postgres/qgspostgresproviderconnection.cpp index 8ec2813d508d..f646b36b9979 100644 --- a/src/providers/postgres/qgspostgresproviderconnection.cpp +++ b/src/providers/postgres/qgspostgresproviderconnection.cpp @@ -192,7 +192,7 @@ void QgsPostgresProviderConnection::renameSchema( const QString &name, const QSt .arg( QgsPostgresConn::quotedIdentifier( newName ) ) ); } -QgsAbstractDatabaseProviderConnection::QueryResult QgsPostgresProviderConnection::executeSqlWithNames( const QString &sql, QgsFeedback *feedback ) const +QgsAbstractDatabaseProviderConnection::QueryResult QgsPostgresProviderConnection::execSql( const QString &sql, QgsFeedback *feedback ) const { checkCapability( Capability::ExecuteSql ); return executeSqlPrivateWithNames( sql, true, feedback ); @@ -201,7 +201,7 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsPostgresProviderConnection QList QgsPostgresProviderConnection::executeSqlPrivate( const QString &sql, bool resolveTypes, QgsFeedback *feedback, std::shared_ptr pgconn ) const { QStringList columnNames; - return executeSqlPrivateWithNames( sql, resolveTypes, feedback, pgconn ).rows; + return executeSqlPrivateWithNames( sql, resolveTypes, feedback, pgconn ).rows(); } QgsAbstractDatabaseProviderConnection::QueryResult QgsPostgresProviderConnection::executeSqlPrivateWithNames( const QString &sql, bool resolveTypes, QgsFeedback *feedback, std::shared_ptr pgconn ) const @@ -270,7 +270,7 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsPostgresProviderConnection // Get column names for ( int rowIdx = 0; rowIdx < res.PQnfields(); rowIdx++ ) { - results.columnns.push_back( res.PQfname( rowIdx ) ); + results.appendColumn( res.PQfname( rowIdx ) ); } // Try to convert value types at least for basic simple types that can be directly mapped to Python @@ -369,7 +369,7 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsPostgresProviderConnection row.push_back( res.PQgetvalue( rowIdx, colIdx ) ); } } - results.rows.push_back( row ); + results.appendRow( row ); } } if ( ! errCause.isEmpty() ) diff --git a/src/providers/postgres/qgspostgresproviderconnection.h b/src/providers/postgres/qgspostgresproviderconnection.h index 1aecc97a4359..8ff78d9dffbb 100644 --- a/src/providers/postgres/qgspostgresproviderconnection.h +++ b/src/providers/postgres/qgspostgresproviderconnection.h @@ -46,7 +46,7 @@ class QgsPostgresProviderConnection : public QgsAbstractDatabaseProviderConnecti 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; - QgsAbstractDatabaseProviderConnection::QueryResult executeSqlWithNames( const QString &sql, QgsFeedback *feedback = nullptr ) const override; + QgsAbstractDatabaseProviderConnection::QueryResult execSql( const QString &sql, QgsFeedback *feedback = nullptr ) const override; void vacuum( const QString &schema, const QString &name ) const override; void createSpatialIndex( const QString &schema, const QString &name, const QgsAbstractDatabaseProviderConnection::SpatialIndexOptions &options = QgsAbstractDatabaseProviderConnection::SpatialIndexOptions() ) const override; bool spatialIndexExists( const QString &schema, const QString &name, const QString &geometryColumn ) const override; diff --git a/src/providers/spatialite/qgsspatialiteproviderconnection.cpp b/src/providers/spatialite/qgsspatialiteproviderconnection.cpp index 2777f4f1be43..bbbbf3d235a4 100644 --- a/src/providers/spatialite/qgsspatialiteproviderconnection.cpp +++ b/src/providers/spatialite/qgsspatialiteproviderconnection.cpp @@ -181,7 +181,7 @@ void QgsSpatiaLiteProviderConnection::renameVectorTable( const QString &schema, } } -QgsAbstractDatabaseProviderConnection::QueryResult QgsSpatiaLiteProviderConnection::executeSqlWithNames( const QString &sql, QgsFeedback *feedback ) const +QgsAbstractDatabaseProviderConnection::QueryResult QgsSpatiaLiteProviderConnection::execSql( const QString &sql, QgsFeedback *feedback ) const { checkCapability( Capability::ExecuteSql ); return executeSqlPrivate( sql, feedback ); @@ -241,7 +241,7 @@ bool QgsSpatiaLiteProviderConnection::spatialIndexExists( const QString &schema, } const QList res = executeSqlPrivate( QStringLiteral( "SELECT spatial_index_enabled FROM geometry_columns WHERE lower(f_table_name) = lower(%1) AND lower(f_geometry_column) = lower(%2)" ) .arg( QgsSqliteUtils::quotedString( name ), - QgsSqliteUtils::quotedString( geometryColumn ) ) ).rows; + QgsSqliteUtils::quotedString( geometryColumn ) ) ).rows(); return !res.isEmpty() && !res.at( 0 ).isEmpty() && res.at( 0 ).at( 0 ).toInt() == 1; } @@ -294,7 +294,7 @@ QList QgsSpatiaLiteProviderConne // Need to store it here because provider (and underlying gaia library) returns views as spatial table if they have geometries QStringList viewNames; - for ( const auto &tn : executeSqlPrivate( QStringLiteral( "SELECT name FROM sqlite_master WHERE type = 'view'" ) ).rows ) + for ( const auto &tn : executeSqlPrivate( QStringLiteral( "SELECT name FROM sqlite_master WHERE type = 'view'" ) ).rows() ) { viewNames.push_back( tn.first().toString() ); } @@ -302,7 +302,7 @@ QList QgsSpatiaLiteProviderConne // Another weirdness: table names are converted to lowercase when out of spatialite gaia functions, let's get them back to their real case here, // may need LAUNDER on open, but let's try to make it consistent with how GPKG works. QgsStringMap tableNotLowercaseNames; - for ( const auto &tn : executeSqlPrivate( QStringLiteral( "SELECT name FROM sqlite_master WHERE LOWER(name) != name" ) ).rows ) + for ( const auto &tn : executeSqlPrivate( QStringLiteral( "SELECT name FROM sqlite_master WHERE LOWER(name) != name" ) ).rows() ) { const QString tName { tn.first().toString() }; tableNotLowercaseNames.insert( tName.toLower(), tName ); @@ -435,7 +435,7 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsSpatiaLiteProviderConnecti fields = QgsOgrUtils::readOgrFields( fet.get(), QTextCodec::codecForName( "UTF-8" ) ); for ( const auto &f : qgis::as_const( fields ) ) { - results.columnns.push_back( f.name() ); + results.appendColumn( f.name() ); } } if ( ! fields.isEmpty() ) @@ -455,7 +455,7 @@ QgsAbstractDatabaseProviderConnection::QueryResult QgsSpatiaLiteProviderConnecti } } - results.rows.push_back( row ); + results.appendRow( row ); } GDALDatasetReleaseResultSet( hDS.get(), ogrLayer ); } diff --git a/src/providers/spatialite/qgsspatialiteproviderconnection.h b/src/providers/spatialite/qgsspatialiteproviderconnection.h index 9eca70f72d87..cc8064810510 100644 --- a/src/providers/spatialite/qgsspatialiteproviderconnection.h +++ b/src/providers/spatialite/qgsspatialiteproviderconnection.h @@ -38,7 +38,7 @@ class QgsSpatiaLiteProviderConnection : public QgsAbstractDatabaseProviderConnec void createVectorTable( const QString &schema, const QString &name, const QgsFields &fields, QgsWkbTypes::Type wkbType, const QgsCoordinateReferenceSystem &srs, bool overwrite, const QMap *options ) const override; void dropVectorTable( const QString &schema, const QString &name ) const override; void renameVectorTable( const QString &schema, const QString &name, const QString &newName ) const override; - QgsAbstractDatabaseProviderConnection::QueryResult executeSqlWithNames( const QString &sql, QgsFeedback *feedback = nullptr ) const override; + QgsAbstractDatabaseProviderConnection::QueryResult execSql( const QString &sql, QgsFeedback *feedback = nullptr ) const override; void vacuum( const QString &schema, const QString &name ) const override; void createSpatialIndex( const QString &schema, const QString &name, const QgsAbstractDatabaseProviderConnection::SpatialIndexOptions &options = QgsAbstractDatabaseProviderConnection::SpatialIndexOptions() ) const override; bool spatialIndexExists( const QString &schema, const QString &name, const QString &geometryColumn ) const override; diff --git a/tests/src/python/test_qgsproviderconnection_base.py b/tests/src/python/test_qgsproviderconnection_base.py index d0eb8e2e90ec..b8d5148be5b6 100644 --- a/tests/src/python/test_qgsproviderconnection_base.py +++ b/tests/src/python/test_qgsproviderconnection_base.py @@ -232,9 +232,9 @@ def _test_operations(self, md, conn): self.assertEqual(res, [['QGIS Rocks - \U0001f604', 666, 1.234, 1234, QtCore.QDate(2019, 7, 8) if not self.treat_date_as_string() else '2019-07-08', QtCore.QDateTime(2019, 7, 8, 12, 0, 12)]]) # Test column names - res = conn.executeSqlWithNames(sql) - self.assertEqual(res.rows, [['QGIS Rocks - \U0001f604', 666, 1.234, 1234, QtCore.QDate(2019, 7, 8) if not self.treat_date_as_string() else '2019-07-08', QtCore.QDateTime(2019, 7, 8, 12, 0, 12)]]) - self.assertEqual(res.columns, ['string_t', 'long_t', 'double_t', 'integer_t', 'date_t', 'datetime_t']) + res = conn.execSql(sql) + self.assertEqual(res.rows(), [['QGIS Rocks - \U0001f604', 666, 1.234, 1234, QtCore.QDate(2019, 7, 8) if not self.treat_date_as_string() else '2019-07-08', QtCore.QDateTime(2019, 7, 8, 12, 0, 12)]]) + self.assertEqual(res.columns(), ['string_t', 'long_t', 'double_t', 'integer_t', 'date_t', 'datetime_t']) sql = "SELECT time_t FROM %s" % table res = conn.executeSql(sql) From 3a2b8f889a2c367109f74de503c29e4ba5e361c0 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 21 Dec 2020 11:36:47 +0100 Subject: [PATCH 3/4] Optimize column information --- .../db_plugins/postgis/connector.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/python/plugins/db_manager/db_plugins/postgis/connector.py b/python/plugins/db_manager/db_plugins/postgis/connector.py index f8531b4d651c..e5c0cef1dff0 100644 --- a/python/plugins/db_manager/db_plugins/postgis/connector.py +++ b/python/plugins/db_manager/db_plugins/postgis/connector.py @@ -104,16 +104,29 @@ def _execute(self, sql=None): return self._debug("execute called with sql " + self.sql) try: - self.result = self._toStrResultSet(self.connection.executeSql(self.sql, feedback=self.feedback)) + result = self.connection.execSql(self.sql, feedback=self.feedback) + self._description = [] # reset description + self.result = self._toStrResultSet(result.rows()) + for c in result.columns(): + self._description.append([ + c, # name + '', # type_code + -1, # display_size + -1, # internal_size + -1, # precision + None, # scale + True # null_ok + ]) + except QgsProviderConnectionException as e: + self._description = None raise DbError(e, self.sql) self._debug("execute returned " + str(len(self.result)) + " rows") self.cursor = 0 - self._description = None # reset description - @property def description(self): + """Returns columns description, it should be already set by _execute""" if self._description is None: From 4a7978307e6384731008c1c3c6e0db772aa45e42 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 21 Dec 2020 11:36:54 +0100 Subject: [PATCH 4/4] Rename private method to execSqlPrivate --- src/providers/postgres/qgspostgresproviderconnection.cpp | 6 +++--- src/providers/postgres/qgspostgresproviderconnection.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/providers/postgres/qgspostgresproviderconnection.cpp b/src/providers/postgres/qgspostgresproviderconnection.cpp index f646b36b9979..ca32c1f8df07 100644 --- a/src/providers/postgres/qgspostgresproviderconnection.cpp +++ b/src/providers/postgres/qgspostgresproviderconnection.cpp @@ -195,16 +195,16 @@ void QgsPostgresProviderConnection::renameSchema( const QString &name, const QSt QgsAbstractDatabaseProviderConnection::QueryResult QgsPostgresProviderConnection::execSql( const QString &sql, QgsFeedback *feedback ) const { checkCapability( Capability::ExecuteSql ); - return executeSqlPrivateWithNames( sql, true, feedback ); + return execSqlPrivate( sql, true, feedback ); } QList QgsPostgresProviderConnection::executeSqlPrivate( const QString &sql, bool resolveTypes, QgsFeedback *feedback, std::shared_ptr pgconn ) const { QStringList columnNames; - return executeSqlPrivateWithNames( sql, resolveTypes, feedback, pgconn ).rows(); + return execSqlPrivate( sql, resolveTypes, feedback, pgconn ).rows(); } -QgsAbstractDatabaseProviderConnection::QueryResult QgsPostgresProviderConnection::executeSqlPrivateWithNames( const QString &sql, bool resolveTypes, QgsFeedback *feedback, std::shared_ptr pgconn ) const +QgsAbstractDatabaseProviderConnection::QueryResult QgsPostgresProviderConnection::execSqlPrivate( const QString &sql, bool resolveTypes, QgsFeedback *feedback, std::shared_ptr pgconn ) const { QueryResult results; diff --git a/src/providers/postgres/qgspostgresproviderconnection.h b/src/providers/postgres/qgspostgresproviderconnection.h index 8ff78d9dffbb..21575548e123 100644 --- a/src/providers/postgres/qgspostgresproviderconnection.h +++ b/src/providers/postgres/qgspostgresproviderconnection.h @@ -62,7 +62,7 @@ class QgsPostgresProviderConnection : public QgsAbstractDatabaseProviderConnecti private: QList executeSqlPrivate( const QString &sql, bool resolveTypes = true, QgsFeedback *feedback = nullptr, std::shared_ptr< class QgsPoolPostgresConn > pgconn = nullptr ) const; - QgsAbstractDatabaseProviderConnection::QueryResult executeSqlPrivateWithNames( const QString &sql, bool resolveTypes = true, QgsFeedback *feedback = nullptr, std::shared_ptr< class QgsPoolPostgresConn > pgconn = nullptr ) const; + QgsAbstractDatabaseProviderConnection::QueryResult execSqlPrivate( const QString &sql, bool resolveTypes = true, QgsFeedback *feedback = nullptr, std::shared_ptr< class QgsPoolPostgresConn > pgconn = nullptr ) const; void setDefaultCapabilities(); void dropTablePrivate( const QString &schema, const QString &name ) const; void renameTablePrivate( const QString &schema, const QString &name, const QString &newName ) const;