Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #40663 from elpaso/connections-api-executeSql-with…
…-column-names

Connections API: execSql, return rows and column names
  • Loading branch information
elpaso committed Dec 21, 2020
2 parents ddcb1c8 + 4a79783 commit 5a2cfe5
Show file tree
Hide file tree
Showing 13 changed files with 213 additions and 37 deletions.
Expand Up @@ -45,6 +45,37 @@ is not supported or cannot be performed without errors.
typedef QFlags<QgsAbstractDatabaseProviderConnection::TableFlag> TableFlags;


struct QueryResult
{
SIP_PYOBJECT __repr__();
%MethodCode
QString str = QStringLiteral( "<QgsAbstractDatabaseProviderConnection.QueryResult: %1 rows>" ).arg( sipCpp->rows().size() );
sipRes = PyUnicode_FromString( str.toUtf8().constData() );
%End

QStringList columns() const;
%Docstring
Returns the column names
%End

QList<QList<QVariant> > 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<QVariant> &row );
%Docstring
Appends ``row`` to the results.
%End

};


struct TableProperty
{

Expand Down Expand Up @@ -433,7 +464,21 @@ 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 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
%End

virtual void vacuum( const QString &schema, const QString &name ) const throw( QgsProviderConnectionException );
Expand Down
19 changes: 16 additions & 3 deletions python/plugins/db_manager/db_plugins/postgis/connector.py
Expand Up @@ -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:

Expand Down
16 changes: 10 additions & 6 deletions src/core/providers/ogr/qgsgeopackageproviderconnection.cpp
Expand Up @@ -164,7 +164,7 @@ void QgsGeoPackageProviderConnection::renameVectorTable( const QString &schema,
}
}

QList<QList<QVariant>> QgsGeoPackageProviderConnection::executeSql( const QString &sql, QgsFeedback *feedback ) const
QgsAbstractDatabaseProviderConnection::QueryResult QgsGeoPackageProviderConnection::execSql( const QString &sql, QgsFeedback *feedback ) const
{
checkCapability( Capability::ExecuteSql );
return executeGdalSqlPrivate( sql, feedback );
Expand Down Expand Up @@ -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<QVariantList> 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();
}

Expand Down Expand Up @@ -354,9 +354,9 @@ void QgsGeoPackageProviderConnection::setDefaultCapabilities()
};
}

QList<QVariantList> QgsGeoPackageProviderConnection::executeGdalSqlPrivate( const QString &sql, QgsFeedback *feedback ) const
QgsAbstractDatabaseProviderConnection::QueryResult QgsGeoPackageProviderConnection::executeGdalSqlPrivate( const QString &sql, QgsFeedback *feedback ) const
{
QList<QVariantList> results;
QgsAbstractDatabaseProviderConnection::QueryResult results;

if ( feedback && feedback->isCanceled() )
{
Expand Down Expand Up @@ -392,6 +392,10 @@ QList<QVariantList> QgsGeoPackageProviderConnection::executeGdalSqlPrivate( cons
if ( fields.isEmpty() )
{
fields = QgsOgrUtils::readOgrFields( fet.get(), QTextCodec::codecForName( "UTF-8" ) );
for ( const auto &f : qgis::as_const( fields ) )
{
results.appendColumn( f.name() );
}
}

if ( ! fields.isEmpty() )
Expand All @@ -411,7 +415,7 @@ QList<QVariantList> QgsGeoPackageProviderConnection::executeGdalSqlPrivate( cons
}
}

results.push_back( row );
results.appendRow( row );
}
GDALDatasetReleaseResultSet( hDS.get(), ogrLayer );
}
Expand Down
5 changes: 3 additions & 2 deletions src/core/providers/ogr/qgsgeopackageproviderconnection.h
Expand Up @@ -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<QList<QVariant>> executeSql( 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;
Expand All @@ -52,7 +52,8 @@ class QgsGeoPackageProviderConnection : public QgsAbstractDatabaseProviderConnec

void setDefaultCapabilities();
//! Use GDAL to execute SQL
QList<QVariantList> executeGdalSqlPrivate( const QString &sql, QgsFeedback *feedback = nullptr ) const;
QueryResult executeGdalSqlPrivate( const QString &sql, QgsFeedback *feedback = nullptr ) const;


};

Expand Down
31 changes: 29 additions & 2 deletions src/core/qgsabstractdatabaseproviderconnection.cpp
Expand Up @@ -133,12 +133,19 @@ void QgsAbstractDatabaseProviderConnection::renameSchema( const QString &, const
checkCapability( Capability::RenameSchema );
}

QList<QList<QVariant>> QgsAbstractDatabaseProviderConnection::executeSql( const QString &, QgsFeedback * ) const
QList<QList<QVariant>> QgsAbstractDatabaseProviderConnection::executeSql( const QString &sql, QgsFeedback *feedback ) const
{
return execSql( sql, feedback ).rows();
}


QgsAbstractDatabaseProviderConnection::QueryResult QgsAbstractDatabaseProviderConnection::execSql( const QString &, QgsFeedback * ) const
{
checkCapability( Capability::ExecuteSql );
return QList<QList<QVariant>>();
return QueryResult();
}


void QgsAbstractDatabaseProviderConnection::vacuum( const QString &, const QString & ) const
{
checkCapability( Capability::Vacuum );
Expand Down Expand Up @@ -427,3 +434,23 @@ void QgsAbstractDatabaseProviderConnection::TableProperty::setSchema( const QStr
mSchema = schema;
}


QStringList QgsAbstractDatabaseProviderConnection::QueryResult::columns() const
{
return mColumns;
}

QList<QList<QVariant> > QgsAbstractDatabaseProviderConnection::QueryResult::rows() const
{
return mRows;
}

void QgsAbstractDatabaseProviderConnection::QueryResult::appendColumn( const QString &columnName )
{
mColumns.push_back( columnName );
}

void QgsAbstractDatabaseProviderConnection::QueryResult::appendRow( const QList<QVariant> &row )
{
mRows.push_back( row );
}
55 changes: 55 additions & 0 deletions src/core/qgsabstractdatabaseproviderconnection.h
Expand Up @@ -67,6 +67,51 @@ 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 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.
* \since QGIS 3.18
*/
struct CORE_EXPORT QueryResult
{
#ifdef SIP_RUN
SIP_PYOBJECT __repr__();
% MethodCode
QString str = QStringLiteral( "<QgsAbstractDatabaseProviderConnection.QueryResult: %1 rows>" ).arg( sipCpp->rows().size() );
sipRes = PyUnicode_FromString( str.toUtf8().constData() );
% End
#endif

/**
* Returns the column names
*/
QStringList columns() const;

/**
*Returns the results rows
*/
QList<QList<QVariant> > 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<QVariant> &row );

private:

QStringList mColumns;
QList<QList<QVariant>> mRows;

};


/**
* The TableProperty class represents a database table or view.
*
Expand Down Expand Up @@ -456,10 +501,20 @@ 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<QList<QVariant>> 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.
* \see executeSql()
* \throws QgsProviderConnectionException
* \since QGIS 3.18
*/
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).
* Raises a QgsProviderConnectionException if any errors are encountered.
Expand Down
19 changes: 12 additions & 7 deletions src/providers/mssql/qgsmssqlproviderconnection.cpp
Expand Up @@ -15,6 +15,7 @@
***************************************************************************/

#include <QSqlRecord>
#include <QSqlField>

#include "qgsmssqlproviderconnection.h"
#include "qgsmssqlconnection.h"
Expand Down Expand Up @@ -213,15 +214,15 @@ void QgsMssqlProviderConnection::dropSchema( const QString &schemaName, bool fo
.arg( QgsMssqlProvider::quotedIdentifier( schemaName ) ) );
}

QList<QVariantList> QgsMssqlProviderConnection::executeSql( const QString &sql, QgsFeedback *feedback ) const
QgsAbstractDatabaseProviderConnection::QueryResult QgsMssqlProviderConnection::execSql( const QString &sql, QgsFeedback *feedback ) const
{
checkCapability( Capability::ExecuteSql );
return executeSqlPrivate( sql, true, feedback );
}

QList<QVariantList> QgsMssqlProviderConnection::executeSqlPrivate( const QString &sql, bool resolveTypes, QgsFeedback *feedback ) const
QgsAbstractDatabaseProviderConnection::QueryResult QgsMssqlProviderConnection::executeSqlPrivate( const QString &sql, bool resolveTypes, QgsFeedback *feedback ) const
{
QList<QVariantList> results;
QgsAbstractDatabaseProviderConnection::QueryResult results;

if ( feedback && feedback->isCanceled() )
{
Expand Down Expand Up @@ -264,6 +265,10 @@ QList<QVariantList> QgsMssqlProviderConnection::executeSqlPrivate( const QString
{
const QSqlRecord rec { q.record() };
const int numCols { rec.count() };
for ( int idx = 0; idx < numCols; ++idx )
{
results.appendColumn( rec.field( idx ).name() );
}
while ( q.next() && ( ! feedback || ! feedback->isCanceled() ) )
{
QVariantList row;
Expand All @@ -278,7 +283,7 @@ QList<QVariantList> QgsMssqlProviderConnection::executeSqlPrivate( const QString
row.push_back( q.value( col ).toString() );
}
}
results.push_back( row );
results.appendRow( row );
}
}

Expand Down Expand Up @@ -360,7 +365,7 @@ QList<QgsMssqlProviderConnection::TableProperty> QgsMssqlProviderConnection::tab
.arg( QgsMssqlProvider::quotedValue( schema ) );
}

const QList<QVariantList> results { executeSqlPrivate( query, false ) };
const QList<QVariantList> results { executeSqlPrivate( query, false ).rows() };
for ( const auto &row : results )
{
Q_ASSERT( row.count( ) == 6 );
Expand Down Expand Up @@ -394,7 +399,7 @@ QList<QgsMssqlProviderConnection::TableProperty> 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() ),
Expand Down Expand Up @@ -443,7 +448,7 @@ QStringList QgsMssqlProviderConnection::schemas( ) const
WHERE u.issqluser = 1
AND u.name NOT IN ('sys', 'guest', 'INFORMATION_SCHEMA')
)raw" )};
const QList<QVariantList> result { executeSqlPrivate( sql, false ) };
const QList<QVariantList> result { executeSqlPrivate( sql, false ).rows() };
for ( const auto &row : result )
{
if ( row.size() > 0 )
Expand Down
4 changes: 2 additions & 2 deletions src/providers/mssql/qgsmssqlproviderconnection.h
Expand Up @@ -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<QVariantList> executeSql( const QString &sql, QgsFeedback *feedback ) const override;
QgsAbstractDatabaseProviderConnection::QueryResult execSql( const QString &sql, QgsFeedback *feedback ) const override;
QList<QgsAbstractDatabaseProviderConnection::TableProperty> tables( const QString &schema,
const TableFlags &flags = TableFlags() ) const override;
QStringList schemas( ) const override;
Expand All @@ -52,7 +52,7 @@ class QgsMssqlProviderConnection : public QgsAbstractDatabaseProviderConnection

private:

QList<QVariantList> 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;
Expand Down

0 comments on commit 5a2cfe5

Please sign in to comment.