Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
fixes #20872 : Manage postgres multidimensionnal array
  • Loading branch information
troopa81 authored and nyalldawson committed Mar 11, 2019
1 parent 4f9c03d commit 2dbaa18
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 14 deletions.
11 changes: 10 additions & 1 deletion src/providers/postgres/qgspostgresconn.cpp
Expand Up @@ -1009,7 +1009,16 @@ static QString quotedList( const QVariantList &list )
{
ret += QLatin1String( "," );
}
ret.append( doubleQuotedMapValue( i->toString() ) );

QString inner = i->toString();
if ( inner.startsWith( '{' ) )
{
ret.append( inner );
}
else
{
ret.append( doubleQuotedMapValue( i->toString() ) );
}
}
return "E'{" + ret + "}'";
}
Expand Down
66 changes: 53 additions & 13 deletions src/providers/postgres/qgspostgresprovider.cpp
Expand Up @@ -4214,7 +4214,7 @@ static void jumpSpace( const QString &txt, int &i )
++i;
}

static QString getNextString( const QString &txt, int &i, const QString &sep )
QString QgsPostgresProvider::getNextString( const QString &txt, int &i, const QString &sep )
{
jumpSpace( txt, i );
QString cur = txt.mid( i );
Expand All @@ -4223,14 +4223,14 @@ static QString getNextString( const QString &txt, int &i, const QString &sep )
QRegExp stringRe( "^\"((?:\\\\.|[^\"\\\\])*)\".*" );
if ( !stringRe.exactMatch( cur ) )
{
QgsLogger::warning( "Cannot find end of double quoted string: " + txt );
QgsMessageLog::logMessage( tr( "Cannot find end of double quoted string: %1" ).arg( txt ), tr( "PostGIS" ) );
return QString();
}
i += stringRe.cap( 1 ).length() + 2;
jumpSpace( txt, i );
if ( !txt.midRef( i ).startsWith( sep ) && i < txt.length() )
{
QgsLogger::warning( "Cannot find separator: " + txt.mid( i ) );
QgsMessageLog::logMessage( tr( "Cannot find separator: %1" ).arg( txt.mid( i ) ), tr( "PostGIS" ) );
return QString();
}
i += sep.length();
Expand All @@ -4249,7 +4249,7 @@ static QString getNextString( const QString &txt, int &i, const QString &sep )
}
}

static QVariant parseHstore( const QString &txt )
QVariant QgsPostgresProvider::parseHstore( const QString &txt )
{
QVariantMap result;
int i = 0;
Expand All @@ -4259,7 +4259,7 @@ static QVariant parseHstore( const QString &txt )
QString value = getNextString( txt, i, QStringLiteral( "," ) );
if ( key.isNull() || value.isNull() )
{
QgsLogger::warning( "Error parsing hstore: " + txt );
QgsMessageLog::logMessage( tr( "Error parsing hstore: %1" ).arg( txt ), tr( "PostGIS" ) );
break;
}
result.insert( key, value );
Expand All @@ -4268,7 +4268,7 @@ static QVariant parseHstore( const QString &txt )
return result;
}

static QVariant parseJson( const QString &txt )
QVariant QgsPostgresProvider::parseJson( const QString &txt )
{
QVariant result;
QJsonDocument jsonResponse = QJsonDocument::fromJson( txt.toUtf8() );
Expand All @@ -4277,7 +4277,7 @@ static QVariant parseJson( const QString &txt )
return result;
}

static QVariant parseOtherArray( const QString &txt, QVariant::Type subType, const QString &typeName )
QVariant QgsPostgresProvider::parseOtherArray( const QString &txt, QVariant::Type subType, const QString &typeName )
{
int i = 0;
QVariantList result;
Expand All @@ -4286,15 +4286,15 @@ static QVariant parseOtherArray( const QString &txt, QVariant::Type subType, con
const QString value = getNextString( txt, i, QStringLiteral( "," ) );
if ( value.isNull() )
{
QgsLogger::warning( "Error parsing array: " + txt );
QgsMessageLog::logMessage( tr( "Error parsing array: %1" ).arg( txt ), tr( "PostGIS" ) );
break;
}
result.append( QgsPostgresProvider::convertValue( subType, QVariant::Invalid, value, typeName ) );
}
return result;
}

static QVariant parseStringArray( const QString &txt )
QVariant QgsPostgresProvider::parseStringArray( const QString &txt )
{
int i = 0;
QStringList result;
Expand All @@ -4303,24 +4303,64 @@ static QVariant parseStringArray( const QString &txt )
const QString value = getNextString( txt, i, QStringLiteral( "," ) );
if ( value.isNull() )
{
QgsLogger::warning( "Error parsing array: " + txt );
QgsMessageLog::logMessage( tr( "Error parsing array: %1" ).arg( txt ), tr( "PostGIS" ) );
break;
}
result.append( value );
}
return result;
}

static QVariant parseArray( const QString &txt, QVariant::Type type, QVariant::Type subType, const QString &typeName )
QVariant QgsPostgresProvider::parseMultidimensionalArray( const QString &txt )
{
QStringList result;
if ( !txt.startsWith( '{' ) || !txt.endsWith( '}' ) )
{
QgsMessageLog::logMessage( tr( "Error parsing array, missing curly braces: %1" ).arg( txt ), tr( "PostGIS" ) );
return result;
}

QStringList values;
QString text = txt;
while ( !text.isEmpty() )
{
bool escaped = false;
int openedBrackets = 1;
int i = 0;
while ( i < text.length() && openedBrackets > 0 )
{
++i;

if ( text.at( i ) == '}' && !escaped ) openedBrackets--;
else if ( text.at( i ) == '{' && !escaped ) openedBrackets++;

escaped = !escaped ? text.at( i ) == '\\' : false;
}

values.append( text.left( ++i ) );
i = text.indexOf( ',', i );
i = i > 0 ? text.indexOf( '{', i ) : -1;
if ( i == -1 )
break;

text = text.mid( i );
}
return values;

}

QVariant QgsPostgresProvider::parseArray( const QString &txt, QVariant::Type type, QVariant::Type subType, const QString &typeName )
{
if ( !txt.startsWith( '{' ) || !txt.endsWith( '}' ) )
{
if ( !txt.isEmpty() )
QgsLogger::warning( "Error parsing array, missing curly braces: " + txt );
QgsMessageLog::logMessage( tr( "Error parsing array, missing curly braces: %1" ).arg( txt ), tr( "PostGIS" ) );
return QVariant( type );
}
QString inner = txt.mid( 1, txt.length() - 2 );
if ( type == QVariant::StringList )
if ( ( type == QVariant::StringList || type == QVariant::List ) && inner.startsWith( "{" ) )
return parseMultidimensionalArray( inner );
else if ( type == QVariant::StringList )
return parseStringArray( inner );
else
return parseOtherArray( inner, subType, typeName );
Expand Down
10 changes: 10 additions & 0 deletions src/providers/postgres/qgspostgresprovider.h
Expand Up @@ -264,6 +264,16 @@ class QgsPostgresProvider : public QgsVectorDataProvider

QString geomParam( int offset ) const;


static QString getNextString( const QString &txt, int &i, const QString &sep );
static QVariant parseHstore( const QString &txt );
static QVariant parseJson( const QString &txt );
static QVariant parseOtherArray( const QString &txt, QVariant::Type subType, const QString &typeName );
static QVariant parseStringArray( const QString &txt );
static QVariant parseMultidimensionalArray( const QString &txt );
static QVariant parseArray( const QString &txt, QVariant::Type type, QVariant::Type subType, const QString &typeName );


/**
* Gets parametrized primary key clause
* \param offset specifies offset to use for the pk value parameter
Expand Down
8 changes: 8 additions & 0 deletions tests/src/providers/testqgspostgresconn.cpp
Expand Up @@ -54,6 +54,14 @@ class TestQgsPostgresConn: public QObject
QCOMPARE( actual, QString( "E'{\"1\",\"-5\"}'" ) );
}

void quotedValue2DimArray()
{
QStringList list;
list << QStringLiteral( "{\"hello foo\",b}" ) << QStringLiteral( "{c,\"hello bar\"}" );
const QString actual = QgsPostgresConn::quotedValue( list );
QCOMPARE( actual, QString( "E'{{\"hello foo\",b},{c,\"hello bar\"}}'" ) );
}

};

QGSTEST_MAIN( TestQgsPostgresConn )
Expand Down
23 changes: 23 additions & 0 deletions tests/src/providers/testqgspostgresprovider.cpp
Expand Up @@ -78,6 +78,29 @@ class TestQgsPostgresProvider: public QObject
qDebug() << "actual: " << decoded;
QCOMPARE( decoded.toList(), expected );
}

void decode2DimensionArray()
{
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::StringList, QVariant::String, QStringLiteral( "{{foo,\"escape bracket \\}\"},{\"escape bracket and backslash \\\\\\}\",\"hello bar\"}}" ), QStringLiteral( "_text" ) );
QCOMPARE( decoded.type(), QVariant::StringList );

QVariantList expected;
expected << QVariant( "{foo,\"escape bracket \\}\"}" ) << QVariant( "{\"escape bracket and backslash \\\\\\}\",\"hello bar\"}" );
qDebug() << "actual: " << decoded;
QCOMPARE( decoded.toList(), expected );
}

void decode3DimensionArray()
{
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::StringList, QVariant::String, QStringLiteral( "{{{0,1},{1,2}},{{3,4},{5,6}}}" ), QStringLiteral( "_integer" ) );
QCOMPARE( decoded.type(), QVariant::StringList );

QVariantList expected;
expected << QVariant( "{{0,1},{1,2}}" ) << QVariant( "{{3,4},{5,6}}" );
qDebug() << "actual: " << decoded;
QCOMPARE( decoded.toList(), expected );
}

void decodeJsonList()
{
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::Map, QVariant::String, QStringLiteral( "[1,2,3]" ), QStringLiteral( "json" ) );
Expand Down

0 comments on commit 2dbaa18

Please sign in to comment.