Skip to content
Permalink
Browse files

Rework handling of primary keys in HANA

  • Loading branch information
mrylov committed Dec 23, 2020
1 parent ed26d18 commit 32a938dd748d1262ef2d2aba306ce461a6fa8a6d
@@ -11,6 +11,7 @@ SET (HANA_SRCS
qgshanadriver.cpp
qgshanaexpressioncompiler.cpp
qgshanafeatureiterator.cpp
qgshanaprimarykeys.cpp
qgshanaprovider.cpp
qgshanaproviderconnection.cpp
qgshanaresultset.cpp
@@ -552,7 +552,7 @@ void QgsHanaConnection::readLayerInfo( QgsHanaLayerProperty &layerProperty )
{
layerProperty.srid = getColumnSrid( layerProperty.schemaName, layerProperty.tableName, layerProperty.geometryColName );
layerProperty.type = getColumnGeometryType( layerProperty.schemaName, layerProperty.tableName, layerProperty.geometryColName );
layerProperty.pkCols = getLayerPrimaryeKeys( layerProperty );
layerProperty.pkCols = getPrimaryeKeys( layerProperty );
}

QVector<QgsHanaSchemaProperty> QgsHanaConnection::getSchemas( const QString &ownerName )
@@ -584,60 +584,75 @@ QVector<QgsHanaSchemaProperty> QgsHanaConnection::getSchemas( const QString &own
return list;
}

QStringList QgsHanaConnection::getLayerPrimaryeKeys( const QgsHanaLayerProperty &layerProperty )
QPair<QString, QMap<QString, short>> QgsHanaConnection::getLayerPrimaryeKey( const QString &schemaName, const QString &tableName )
{
QStringList ret;

try
{
DatabaseMetaDataUnicodeRef dbmd = mConnection->getDatabaseMetaDataUnicode();
ResultSetRef rsPrimaryKeys = dbmd->getPrimaryKeys( nullptr,
QgsHanaUtils::toUtf16( layerProperty.schemaName ),
QgsHanaUtils::toUtf16( layerProperty.tableName ) );
size_t numColumns = 0;
QStringList intColumns;
QgsHanaUtils::toUtf16( schemaName ),
QgsHanaUtils::toUtf16( tableName ) );
QString keyName;
QMap<QString, short> keyColumns;
while ( rsPrimaryKeys->next() )
{
QString clmName = QgsHanaUtils::toQString( rsPrimaryKeys->getNString( 4 /*COLUMN_NAME*/ ) );
if ( keyName.isEmpty() )
keyName = QgsHanaUtils::toQString( rsPrimaryKeys->getNString( 6 /*PK_NAME*/ ) );
ResultSetRef rsColumns = dbmd->getColumns( nullptr,
QgsHanaUtils::toUtf16( layerProperty.schemaName ),
QgsHanaUtils::toUtf16( layerProperty.tableName ),
QgsHanaUtils::toUtf16( schemaName ),
QgsHanaUtils::toUtf16( tableName ),
QgsHanaUtils::toUtf16( clmName ) );
while ( rsColumns->next() )
{
Short dataType = rsColumns->getShort( 5 );
short dt = *dataType;
if ( dt == SQLDataTypes::TinyInt || dt == SQLDataTypes::SmallInt ||
dt == SQLDataTypes::Integer || dt == SQLDataTypes::BigInt )
intColumns << clmName;
++numColumns;
Short dataType = rsColumns->getShort( 5 /*DATA_TYPE*/ );
keyColumns.insert( clmName, *dataType );
}
rsColumns->close();
}
rsPrimaryKeys->close();

if ( numColumns == 1 )
{
if ( !intColumns.empty() )
ret << intColumns[0];
}
else if ( numColumns > 0 )
{
QgsDebugMsg( QStringLiteral( "Table %1.%2 has %3 columns in its primary key %4" ).arg(
layerProperty.schemaName,
layerProperty.tableName,
QString::number( numColumns ),
keyName ) );
}
return qMakePair( keyName, keyColumns );
}
catch ( const Exception &ex )
{
throw QgsHanaException( ex.what() );
}
}

// The current implementation allows to return only primary keys that contain
// only one column of integer type.
QStringList QgsHanaConnection::getPrimaryeKeys( const QgsHanaLayerProperty &layerProperty )
{
QStringList ret;
size_t numColumns = 0;
QStringList intColumns;
const QPair<QString, QMap<QString, short>> primaryKeys = getLayerPrimaryeKey( layerProperty.schemaName, layerProperty.tableName );
const QMap<QString, short> &columns = primaryKeys.second;
for ( auto it = columns.constBegin(); it != columns.constEnd(); ++it )
{
const QString &clmName = it.key();
short clmType = it.value();

if ( clmType == SQLDataTypes::TinyInt || clmType == SQLDataTypes::SmallInt ||
clmType == SQLDataTypes::Integer || clmType == SQLDataTypes::BigInt )
intColumns << clmName;
++numColumns;
}

if ( numColumns == 1 )
{
if ( !intColumns.empty() )
ret << intColumns[0];
}
else if ( numColumns > 0 )
{
QgsDebugMsg( QStringLiteral( "Table %1.%2 has %3 columns in its primary key %4" ).arg(
layerProperty.schemaName,
layerProperty.tableName,
QString::number( numColumns ),
primaryKeys.first ) );
}
return ret;
}

@@ -24,6 +24,8 @@
#include "qgslogger.h"
#include "qgsvectordataprovider.h"

#include <QMap>

#include "odbc/Forwards.h"

class QgsField;
@@ -64,6 +66,7 @@ class QgsHanaConnection : public QObject
bool userTablesOnly = true );
void readLayerInfo( QgsHanaLayerProperty &layerProperty );
QVector<QgsHanaSchemaProperty> getSchemas( const QString &ownerName );
QPair<QString, QMap<QString, short>> getLayerPrimaryeKey( const QString &schemaName, const QString &tableName );
QgsWkbTypes::Type getColumnGeometryType( const QString &schemaName, const QString &tableName, const QString &columnName );
QString getColumnDataType( const QString &schemaName, const QString &tableName, const QString &columnName );
int getColumnSrid( const QString &schemaName, const QString &tableName, const QString &columnName );
@@ -78,7 +81,7 @@ class QgsHanaConnection : public QObject
private:
QgsHanaConnection( odbc::ConnectionRef connection, const QgsDataSourceUri &uri );

QStringList getLayerPrimaryeKeys( const QgsHanaLayerProperty &layerProperty );
QStringList getPrimaryeKeys( const QgsHanaLayerProperty &layerProperty );

odbc::PreparedStatementRef createPreparedStatement( const QString &sql, const QVariantList &args );

@@ -21,6 +21,7 @@
#include "qgshanaexception.h"
#include "qgshanaexpressioncompiler.h"
#include "qgshanafeatureiterator.h"
#include "qgshanaprimarykeys.h"
#include "qgshanaprovider.h"
#include "qgshanacrsutils.h"
#include "qgshanautils.h"
@@ -65,8 +66,6 @@ QgsHanaFeatureIterator::QgsHanaFeatureIterator(
: QgsAbstractFeatureIteratorFromSource<QgsHanaFeatureSource>( source, ownSource, request )
, mDatabaseVersion( source->mDatabaseVersion )
, mConnection( source->mUri )
, mSrsExtent( source->mSrsExtent )
, mFidColumn( source->mFidColumn )
{
if ( mConnection.isNull() )
{
@@ -149,7 +148,7 @@ bool QgsHanaFeatureIterator::fetchFeature( QgsFeature &feature )
unsigned short paramIndex = 1;

// Read feature id
if ( !mFidColumn.isEmpty() )
if ( !mSource->mPrimaryKeyAttrs.isEmpty() )
{
QVariant id = mResultSet->getValue( paramIndex );
feature.setId( id.toLongLong() );
@@ -238,8 +237,8 @@ QString QgsHanaFeatureIterator::buildSqlQuery( const QgsFeatureRequest &request
bool limitAtProvider = ( request.limit() >= 0 );

QgsRectangle filterRect = mFilterRect;
if ( !mSrsExtent.isEmpty() )
filterRect = mSrsExtent.intersect( filterRect );
if ( !mSource->mSrsExtent.isEmpty() )
filterRect = mSource->mSrsExtent.intersect( filterRect );

if ( !filterRect.isFinite() )
QgsMessageLog::logMessage( QObject::tr( "Infinite filter rectangle specified" ), QObject::tr( "HANA" ) );
@@ -307,17 +306,20 @@ QString QgsHanaFeatureIterator::buildSqlQuery( const QgsFeatureRequest &request

QStringList sqlFields;
// Add feature id column
if ( !mFidColumn.isEmpty() )
sqlFields.push_back( QgsHanaUtils::quotedIdentifier( mFidColumn ) );
for ( const int &idx : mSource->mPrimaryKeyAttrs )
{
QString fieldName = mSource->mFields.at( idx ).name();
sqlFields.push_back( QgsHanaUtils::quotedIdentifier( fieldName ) );
}

for ( int i : attrIds )
for ( int idx : attrIds )
{
QString fieldName = mSource->mFields.at( i ).name();
if ( mFidColumn != fieldName )
{
mAttributesToFetch.append( i );
sqlFields.push_back( QgsHanaUtils::quotedIdentifier( fieldName ) );
}
if ( mSource->mPrimaryKeyAttrs.contains( idx ) )
continue;

QString fieldName = mSource->mFields.at( idx ).name();
mAttributesToFetch.append( idx );
sqlFields.push_back( QgsHanaUtils::quotedIdentifier( fieldName ) );
}

mHasAttributes = !mAttributesToFetch.isEmpty();
@@ -340,22 +342,19 @@ QString QgsHanaFeatureIterator::buildSqlQuery( const QgsFeatureRequest &request
sqlFilter.push_back( mSource->mQueryWhereClause );

// Set fid filter
if ( !mFidColumn.isEmpty() )
if ( !mSource->mPrimaryKeyAttrs.isEmpty() )
{
if ( request.filterType() == QgsFeatureRequest::FilterFid )
{
QString inClause = QStringLiteral( " %1 = %2" ).arg(
QgsHanaUtils::quotedIdentifier( mFidColumn ), FID_TO_STRING( request.filterFid() ) );
sqlFilter.push_back( inClause );
QString fidWhereClause = QgsHanaPrimaryKeyUtils::buildWhereClause( request.filterFid(), mSource->mFields, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, *mSource->mPrimaryKeyCntx );
if ( !fidWhereClause.isEmpty() )
sqlFilter.push_back( fidWhereClause );
}
else if ( request.filterType() == QgsFeatureRequest::FilterFids && !mRequest.filterFids().isEmpty() )
{
QStringList fids;
for ( QgsFeatureId featureId : mRequest.filterFids() )
fids.push_back( FID_TO_STRING( featureId ) );

QString inClause = QStringLiteral( "%1 IN (%2)" ).arg( QgsHanaUtils::quotedIdentifier( mFidColumn ), fids.join( ',' ) );
sqlFilter.push_back( inClause );
QString fidWhereClause = QgsHanaPrimaryKeyUtils::buildWhereClause( request.filterFids(), mSource->mFields, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, *mSource->mPrimaryKeyCntx );
if ( !fidWhereClause.isEmpty() )
sqlFilter.push_back( fidWhereClause );
}
}

@@ -439,7 +438,9 @@ QgsHanaFeatureSource::QgsHanaFeatureSource( const QgsHanaProvider *p )
, mUri( p->mUri )
, mSchemaName( p->mSchemaName )
, mTableName( p->mTableName )
, mFidColumn( p->mFidColumn )
, mPrimaryKeyType( p->mPrimaryKeyType )
, mPrimaryKeyAttrs( p->mPrimaryKeyAttrs )
, mPrimaryKeyCntx( p->mPrimaryKeyCntx )
, mFields( p->mAttributeFields )
, mFieldInfos( p->mFieldInfos )
, mGeometryColumn( p->mGeometryColumn )
@@ -19,6 +19,7 @@

#include "qgsfeatureiterator.h"
#include "qgshanaconnectionpool.h"
#include "qgshanaprimarykeys.h"
#include "qgshanaprovider.h"
#include "qgshanaresultset.h"

@@ -40,7 +41,9 @@ class QgsHanaFeatureSource : public QgsAbstractFeatureSource
QgsDataSourceUri mUri;
QString mSchemaName;
QString mTableName;
QString mFidColumn;
QgsHanaPrimaryKeyType mPrimaryKeyType = QgsHanaPrimaryKeyType::PktUnknown;
QList<int> mPrimaryKeyAttrs;
std::shared_ptr<QgsHanaPrimaryKeyContext> mPrimaryKeyCntx;
QgsFields mFields;
QVector<FieldInfo> mFieldInfos;
QString mGeometryColumn;
@@ -86,9 +89,7 @@ class QgsHanaFeatureIterator : public QgsAbstractFeatureIteratorFromSource<QgsHa
QString mSqlQuery;
QVariantList mSqlQueryParams;
QgsRectangle mFilterRect;
const QgsRectangle mSrsExtent;
QgsAttributeList mAttributesToFetch;
const QString mFidColumn;
QgsCoordinateTransform mTransform;
bool mHasAttributes = false;
bool mHasGeometryColumn = false;

0 comments on commit 32a938d

Please sign in to comment.
You can’t perform that action at this time.