Skip to content
Permalink
Browse files

Rework handling of primary keys in HANA (part 2)

  • Loading branch information
mrylov committed Dec 23, 2020
1 parent 32a938d commit 531998fdac51699f468df78df4802544df341024
@@ -620,7 +620,7 @@ QPair<QString, QMap<QString, short>> QgsHanaConnection::getLayerPrimaryeKey( con
}
}

// The current implementation allows to return only primary keys that contain
// The current implementation allows returning only primary keys that contain
// only one column of integer type.
QStringList QgsHanaConnection::getPrimaryeKeys( const QgsHanaLayerProperty &layerProperty )
{
@@ -24,6 +24,12 @@
class QgsHanaException final : public QException
{
public:
explicit QgsHanaException( const QString &what ) noexcept
: mMessage( QgsHanaUtils::formatErrorMessage( what.toStdString().c_str() ).toStdString() )
{
QgsDebugMsg( what );
}

explicit QgsHanaException( const char *what ) noexcept
: mMessage( QgsHanaUtils::formatErrorMessage( what ).toStdString() )
{
@@ -148,18 +148,54 @@ bool QgsHanaFeatureIterator::fetchFeature( QgsFeature &feature )
unsigned short paramIndex = 1;

// Read feature id
QgsFeatureId fid = 0;
bool subsetOfAttributes = mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes;
QgsAttributeList fetchAttributes = mRequest.subsetOfAttributes();

if ( !mSource->mPrimaryKeyAttrs.isEmpty() )
{
QVariant id = mResultSet->getValue( paramIndex );
feature.setId( id.toLongLong() );
feature.setAttribute( 0, id );
++paramIndex;
}
else
{
feature.setId( 0u );
switch ( mSource->mPrimaryKeyType )
{
case QgsHanaPrimaryKeyType::PktInt:
{
QVariant v = mResultSet->getValue( paramIndex );
if ( !subsetOfAttributes || fetchAttributes.contains( mSource->mPrimaryKeyAttrs[ 0 ] ) )
feature.setAttribute( 0, v );
fid = QgsHanaPrimaryKeyUtils::intToFid( v.toInt() );
++paramIndex;
}
break;
case QgsHanaPrimaryKeyType::PktInt64:
{
int idx = mSource->mPrimaryKeyAttrs.at( 0 );
QVariant v = mResultSet->getValue( paramIndex );
if ( !subsetOfAttributes || fetchAttributes.contains( idx ) )
feature.setAttribute( idx, v );
fid = mSource->mPrimaryKeyCntx->lookupFid( QVariantList( { v} ) );
++paramIndex;
}
break;
case QgsHanaPrimaryKeyType::PktFidMap:
{
QVariantList pkValues;
for ( int idx : qgis::as_const( mSource->mPrimaryKeyAttrs ) )
{
QVariant v = mResultSet->getValue( paramIndex );
pkValues << v;
if ( !subsetOfAttributes || fetchAttributes.contains( idx ) )
feature.setAttribute( idx, v );
paramIndex++;
}
fid = mSource->mPrimaryKeyCntx->lookupFid( pkValues );
}
break;
case QgsHanaPrimaryKeyType::PktUnknown:
break;
}
}

feature.setId( fid );

// Read attributes
if ( mHasAttributes )
{
@@ -306,7 +342,7 @@ QString QgsHanaFeatureIterator::buildSqlQuery( const QgsFeatureRequest &request

QStringList sqlFields;
// Add feature id column
for ( const int &idx : mSource->mPrimaryKeyAttrs )
for ( int idx : qgis::as_const( mSource->mPrimaryKeyAttrs ) )
{
QString fieldName = mSource->mFields.at( idx ).name();
sqlFields.push_back( QgsHanaUtils::quotedIdentifier( fieldName ) );
@@ -347,14 +383,16 @@ QString QgsHanaFeatureIterator::buildSqlQuery( const QgsFeatureRequest &request
if ( request.filterType() == QgsFeatureRequest::FilterFid )
{
QString fidWhereClause = QgsHanaPrimaryKeyUtils::buildWhereClause( request.filterFid(), mSource->mFields, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, *mSource->mPrimaryKeyCntx );
if ( !fidWhereClause.isEmpty() )
sqlFilter.push_back( fidWhereClause );
if ( fidWhereClause.isEmpty() )
throw QgsHanaException( QStringLiteral( "Key values for feature %1 not found." ).arg( request.filterFid() ) );
sqlFilter.push_back( fidWhereClause );
}
else if ( request.filterType() == QgsFeatureRequest::FilterFids && !mRequest.filterFids().isEmpty() )
{
QString fidWhereClause = QgsHanaPrimaryKeyUtils::buildWhereClause( request.filterFids(), mSource->mFields, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, *mSource->mPrimaryKeyCntx );
if ( !fidWhereClause.isEmpty() )
sqlFilter.push_back( fidWhereClause );
QString fidsWhereClause = QgsHanaPrimaryKeyUtils::buildWhereClause( request.filterFids(), mSource->mFields, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, *mSource->mPrimaryKeyCntx );
if ( fidsWhereClause.isEmpty() )
throw QgsHanaException( QStringLiteral( "Key values for features not found." ) );
sqlFilter.push_back( fidsWhereClause );
}
}

@@ -1,3 +1,19 @@
/***************************************************************************
qgshanaprimarykeys.cpp
--------------------------------------
Date : 23-12-2020
Copyright : (C) SAP SE
Author : Maxim Rylov
***************************************************************************/

/***************************************************************************
*
* 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 "qgshanaprimarykeys.h"
#include "qgshanautils.h"
#include "qgslogger.h"
@@ -20,7 +36,7 @@ namespace

static qint32 fid_to_int32pk( qint64 x )
{
return x <= ( ( INT32PK_OFFSET ) / 2.0 ) ? x : -( INT32PK_OFFSET - x );
return x <= ( ( INT32PK_OFFSET ) / 2 ) ? x : -( INT32PK_OFFSET - x );
}

QStringList parseUriKey( const QString &key )
@@ -155,14 +171,14 @@ QPair<QgsHanaPrimaryKeyType, QList<int>> QgsHanaPrimaryKeyUtils::determinePrimar
return determinePrimaryKeyFromColumns( parseUriKey( primaryKey ), fields );
}

int QgsHanaPrimaryKeyUtils::fidToInt(QgsFeatureId id)
int QgsHanaPrimaryKeyUtils::fidToInt( QgsFeatureId id )
{
return fid_to_int32pk(id);
return fid_to_int32pk( id );
}

QgsFeatureId QgsHanaPrimaryKeyUtils::intToFid(int id)
QgsFeatureId QgsHanaPrimaryKeyUtils::intToFid( int id )
{
return int32pk_to_fid(id);
return int32pk_to_fid( id );
}

QgsHanaPrimaryKeyType QgsHanaPrimaryKeyUtils::getPrimaryKeyType( const QgsField &field )
@@ -179,23 +195,27 @@ QgsHanaPrimaryKeyType QgsHanaPrimaryKeyUtils::getPrimaryKeyType( const QgsField
}

QString QgsHanaPrimaryKeyUtils::buildWhereClause( const QgsFields &fields, QgsHanaPrimaryKeyType pkType,
const QList<int> &pkAttrs )
const QList<int> &pkAttrs )
{
switch ( pkType )
switch ( pkType )
{
case PktInt:
case PktInt64:
{
case PktInt:
case PktInt64:
{
QString columnName = fields.at( pkAttrs[0] ).name() ;
return QStringLiteral( "%1 = ?" ).arg( QgsHanaUtils::quotedIdentifier( columnName ) );
}
case PktFidMap:
// TODO
return QString();
case PktUnknown:
return QString();
QString columnName = fields.at( pkAttrs[0] ).name() ;
return QStringLiteral( "%1=?" ).arg( QgsHanaUtils::quotedIdentifier( columnName ) );
}
return QString(); //avoid warning
case PktFidMap:
{
QList<QString> conditions;
for ( int idx : pkAttrs )
conditions << QStringLiteral( "%1=?" ).arg( QgsHanaUtils::quotedIdentifier( fields[idx].name() ) );
return conditions.join( QStringLiteral( ") AND (" ) );
}
case PktUnknown:
return QString();
}
return QString(); //avoid warning
}

QString QgsHanaPrimaryKeyUtils::buildWhereClause( QgsFeatureId featureId, const QgsFields &fields, QgsHanaPrimaryKeyType pkType,
@@ -206,43 +226,35 @@ QString QgsHanaPrimaryKeyUtils::buildWhereClause( QgsFeatureId featureId, const
case PktInt:
{
Q_ASSERT( pkAttrs.size() == 1 );
QString fieldName = fields.at( pkAttrs[0] ).name();
QString fieldName = fields[pkAttrs[0]].name();
return QStringLiteral( "%1=%2" ).arg( QgsHanaUtils::quotedIdentifier( fieldName ) ).arg( fidToInt( featureId ) );
}
case PktInt64:
{
QString whereClause;
Q_ASSERT( pkAttrs.size() == 1 );
QVariantList pkVals = primaryKeyCntx.lookupKey( featureId );
if ( !pkVals.isEmpty() )
{
QgsField fld = fields.at( pkAttrs[0] );
whereClause = QgsHanaUtils::quotedIdentifier( fld.name() );
if ( !pkVals[0].isNull() )
whereClause += '=' + pkVals[0].toString();
else
whereClause += QLatin1String( " IS NULL" );
}
QVariantList pkValues = primaryKeyCntx.lookupKey( featureId );
Q_ASSERT( pkValues.size() == 1 );
if ( pkValues.isEmpty() )
return QString();

return whereClause;
const QgsField &fld = fields.at( pkAttrs[0] );
return QStringLiteral( "%1=%2" ).arg( QgsHanaUtils::quotedIdentifier( fld.name() ), pkValues[0].toString() );
}
case PktFidMap:
{
QVariantList pkVals = primaryKeyCntx.lookupKey( featureId );
if ( !pkVals.isEmpty() )
{
Q_ASSERT( pkVals.size() == pkAttrs.size() );

// TODO
QVariantList pkValues = primaryKeyCntx.lookupKey( featureId );
Q_ASSERT( pkValues.size() == pkAttrs.size() );
if ( pkValues.isEmpty() )
return QString();
}
else

QStringList conditions;
for ( int i = 0; i < pkAttrs.size(); i++ )
{
QgsDebugMsg( QStringLiteral( "FAILURE: Key values for feature %1 not found." ).arg( featureId ) );
return QStringLiteral( "NULL" );
const QgsField &fld = fields.at( pkAttrs[i] );
conditions << QStringLiteral( "%1=%2" ).arg( QgsHanaUtils::quotedIdentifier( fld.name() ), pkValues[i].toString() );
}
return conditions.join( QStringLiteral( ") AND (" ) );
}

case PktUnknown:
return QString();
}
@@ -251,7 +263,7 @@ QString QgsHanaPrimaryKeyUtils::buildWhereClause( QgsFeatureId featureId, const
}

QString QgsHanaPrimaryKeyUtils::buildWhereClause( const QgsFeatureIds &featureIds, const QgsFields &fields, QgsHanaPrimaryKeyType pkType,
const QList<int> &pkAttrs, QgsHanaPrimaryKeyContext & primaryKeyCntx)
const QList<int> &pkAttrs, QgsHanaPrimaryKeyContext &primaryKeyCntx )
{
if ( featureIds.isEmpty() )
return QString();
@@ -265,21 +277,32 @@ QString QgsHanaPrimaryKeyUtils::buildWhereClause( const QgsFeatureIds &featureId
for ( QgsFeatureId featureId : featureIds )
{
if ( pkType == PktInt )
fids.push_back( QString::number( fidToInt( featureId ) ) );
fids << QString::number( fidToInt( featureId ) );
else
{
QVariantList pkVals = primaryKeyCntx.lookupKey( featureId );
if ( !pkVals.isEmpty() )
fids.push_back( pkVals[0].toString() );
QVariantList pkValues = primaryKeyCntx.lookupKey( featureId );
if ( pkValues.isEmpty() )
return QString();

fids << pkValues[0].toString();
}
}

QString columnName = fields.at( pkAttrs[0] ).name();
return QStringLiteral( "%1 IN (%2)" ).arg( QgsHanaUtils::quotedIdentifier( columnName ), fids.join( ',' ) );
const QgsField &fld = fields.at( pkAttrs[0] );
return QStringLiteral( "%1 IN (%2)" ).arg( QgsHanaUtils::quotedIdentifier( fld.name() ), fids.join( ',' ) );
}
case PktFidMap:
// TODO
return QString();
{
QStringList whereClauses;
for ( QgsFeatureId featureId : featureIds )
{
const QString fidWhereClause = buildWhereClause( featureId, fields, pkType, pkAttrs, primaryKeyCntx );
if ( fidWhereClause.isEmpty() )
return QString();
whereClauses << fidWhereClause;
}
return whereClauses.join( QStringLiteral( " OR " ) ).prepend( '(' ).append( ')' );
}
case PktUnknown:
return QString();
}
@@ -1,3 +1,19 @@
/***************************************************************************
qgshanaprimarykeys.h
--------------------------------------
Date : 23-12-2020
Copyright : (C) SAP SE
Author : Maxim Rylov
***************************************************************************/

/***************************************************************************
*
* 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 QGSHANAPRIMARYKEYS_H
#define QGSHANAPRIMARYKEYS_H

@@ -31,25 +47,25 @@ class QgsHanaPrimaryKeyContext

QgsFeatureId mFidCounter = 0; // next feature id if map is used
QMap<QVariantList, QgsFeatureId> mKeyToFid; // map key values to feature id
QMap<QgsFeatureId, QVariantList> mFidToKey; // map feature back to fea
QMap<QgsFeatureId, QVariantList> mFidToKey; // map feature id back to key values
};

class QgsHanaPrimaryKeyUtils
{
public:
QgsHanaPrimaryKeyUtils() = delete;

static QPair<QgsHanaPrimaryKeyType, QList<int>> determinePrimaryKeyFromColumns(const QStringList& columnNames, const QgsFields &fields);
static QPair<QgsHanaPrimaryKeyType, QList<int>> determinePrimaryKeyFromUriKeyColumn(const QString& primaryKey, const QgsFields &fields);
static int fidToInt(QgsFeatureId id);
static QgsFeatureId intToFid(int id);
static QPair<QgsHanaPrimaryKeyType, QList<int>> determinePrimaryKeyFromColumns( const QStringList &columnNames, const QgsFields &fields );
static QPair<QgsHanaPrimaryKeyType, QList<int>> determinePrimaryKeyFromUriKeyColumn( const QString &primaryKey, const QgsFields &fields );
static int fidToInt( QgsFeatureId id );
static QgsFeatureId intToFid( int id );
static QgsHanaPrimaryKeyType getPrimaryKeyType( const QgsField &field );
static QString buildWhereClause( const QgsFields &fields, QgsHanaPrimaryKeyType pkType,
const QList<int> &pkAttrs );
const QList<int> &pkAttrs );
static QString buildWhereClause( QgsFeatureId featureId, const QgsFields &fields, QgsHanaPrimaryKeyType pkType,
const QList<int> &pkAttrs, QgsHanaPrimaryKeyContext &primaryKeyCntx );
static QString buildWhereClause( const QgsFeatureIds& featureIds, const QgsFields &fields, QgsHanaPrimaryKeyType pkType,
const QList<int> &pkAttrs, QgsHanaPrimaryKeyContext &primaryKeyCntx );
const QList<int> &pkAttrs, QgsHanaPrimaryKeyContext &primaryKeyCntx );
static QString buildWhereClause( const QgsFeatureIds &featureIds, const QgsFields &fields, QgsHanaPrimaryKeyType pkType,
const QList<int> &pkAttrs, QgsHanaPrimaryKeyContext &primaryKeyCntx );
};


0 comments on commit 531998f

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