Skip to content

Commit be17000

Browse files
authoredNov 24, 2021
Merge pull request #45127 from m-kuhn/refGeomPg
[postgis] Expose secondary gometry columns as referenced geometries
2 parents c42e74c + 6d5d30b commit be17000

File tree

9 files changed

+246
-84
lines changed

9 files changed

+246
-84
lines changed
 

‎.github/workflows/macos-build.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@ jobs:
5858
# Qt caching
5959
- name: Cache Qt
6060
id: cache-qt
61-
uses: pat-s/always-upload-cache@v2.1.5
61+
uses: actions/cache@v2.1.6
6262
with:
6363
path: ${{ env.DEPS_CACHE_DIR }}/Qt/${{ env.QT_VERSION }}
64-
key: mac-qt-v4-${{ env.QT_VERSION }}
64+
key: mac-qt-${{ env.QT_VERSION }}
6565

6666
- name: Restore Qt
6767
if: steps.cache-qt.outputs.cache-hit == 'true'
@@ -81,10 +81,10 @@ jobs:
8181
# QGIS-deps caching
8282
- name: Cache qgis-deps
8383
id: cache-deps
84-
uses: pat-s/always-upload-cache@v2.1.5
84+
uses: actions/cache@v2.1.6
8585
with:
8686
path: ${{ env.DEPS_CACHE_DIR }}/QGIS/qgis-deps-${{ env.QGIS_DEPS_VERSION }}.${{ env.QGIS_DEPS_PATCH_VERSION }}
87-
key: mac-qgis-deps-v4-${{ env.QGIS_DEPS_VERSION }}.${{ env.QGIS_DEPS_PATCH_VERSION }}
87+
key: mac-qgis-deps-${{ env.QGIS_DEPS_VERSION }}.${{ env.QGIS_DEPS_PATCH_VERSION }}
8888

8989
- name: Restore qgis-deps
9090
if: steps.cache-deps.outputs.cache-hit == 'true'

‎doc/api_break.dox

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,23 @@ remove them nor change their semantics. Existing code should keep working when t
1010
to another minor version (e.g. from 2.0 to 2.2), so all extensions of existing classes should be done in a manner that
1111
third party developers do not need to adjust their code to work properly with newer QGIS releases.
1212

13-
Sometimes, however, we may need to break the API as a result of some code changes. These cases should be only exceptions
14-
and they should happen only after consideration and agreement of the development team. Backwards incompatible changes
15-
with too big impact should be deferred to a major version release.
13+
Sometimes, however, we need to break the API as a result of code changes. These cases are exceptions
14+
and they happen only after consideration and agreement of the development team.
15+
Backwards incompatible changes with large impact are postponed to the next major release and tracked in
16+
https://github.com/qgis/qgis4.0_api/issues
17+
18+
This page maintains a list of incompatible changes that happened in previous releases.
19+
20+
QGIS 3.24 {#qgis_api_break_3_24}
21+
=========
22+
23+
Additional geometry attributes
24+
------------------------------
25+
26+
- If a postgis layer has more than one geometry, the additional geometry attributes are exposed as
27+
QgsReferencedGeometry. Previously, they were exposed as EWKT strings. EWKT strings are still supported
28+
for inserting and updating features.
1629

17-
This page tries to maintain a list with incompatible changes that happened in previous releases.
1830

1931
QGIS 3.22 {#qgis_api_break_3_22}
2032
=========

‎src/core/qgsfield.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "qgis.h"
2020
#include "qgsapplication.h"
2121
#include "qgssettings.h"
22+
#include "qgsreferencedgeometry.h"
2223

2324
#include <QDataStream>
2425
#include <QIcon>
@@ -258,6 +259,23 @@ QString QgsField::displayString( const QVariant &v ) const
258259
return QgsApplication::nullRepresentation();
259260
}
260261

262+
if ( v.userType() == QMetaType::type( "QgsReferencedGeometry" ) )
263+
{
264+
QgsReferencedGeometry geom = qvariant_cast<QgsReferencedGeometry>( v );
265+
if ( geom.isNull() )
266+
return QgsApplication::nullRepresentation();
267+
else
268+
{
269+
QString wkt = geom.asWkt();
270+
if ( wkt.length() >= 1050 )
271+
{
272+
wkt = wkt.left( 999 ) + QChar( 0x2026 );
273+
}
274+
QString formattedText = QStringLiteral( "%1 [%2]" ).arg( wkt, geom.crs().userFriendlyIdentifier() );
275+
return formattedText;
276+
}
277+
}
278+
261279
// Special treatment for numeric types if group separator is set or decimalPoint is not a dot
262280
if ( d->type == QVariant::Double )
263281
{

‎src/providers/postgres/qgspostgresfeatureiterator.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -875,7 +875,7 @@ bool QgsPostgresFeatureIterator::getFeature( QgsPostgresResult &queryResult, int
875875
int idx = mSource->mPrimaryKeyAttrs.at( 0 );
876876
QgsField fld = mSource->mFields.at( idx );
877877

878-
QVariant v = QgsPostgresProvider::convertValue( fld.type(), fld.subType(), QString::number( mConn->getBinaryInt( queryResult, row, col ) ), fld.typeName() );
878+
QVariant v = QgsPostgresProvider::convertValue( fld.type(), fld.subType(), QString::number( mConn->getBinaryInt( queryResult, row, col ) ), fld.typeName(), mConn );
879879
pkVal << v;
880880

881881
if ( !subsetOfAttributes || fetchAttributes.contains( idx ) )
@@ -900,11 +900,11 @@ bool QgsPostgresFeatureIterator::getFeature( QgsPostgresResult &queryResult, int
900900

901901
if ( fld.type() == QVariant::LongLong )
902902
{
903-
v = QgsPostgresProvider::convertValue( fld.type(), fld.subType(), QString::number( mConn->getBinaryInt( queryResult, row, col ) ), fld.typeName() );
903+
v = QgsPostgresProvider::convertValue( fld.type(), fld.subType(), QString::number( mConn->getBinaryInt( queryResult, row, col ) ), fld.typeName(), mConn );
904904
}
905905
else
906906
{
907-
v = QgsPostgresProvider::convertValue( fld.type(), fld.subType(), queryResult.PQgetvalue( row, col ), fld.typeName() );
907+
v = QgsPostgresProvider::convertValue( fld.type(), fld.subType(), queryResult.PQgetvalue( row, col ), fld.typeName(), mConn );
908908
}
909909
primaryKeyVals << v;
910910

@@ -986,13 +986,13 @@ void QgsPostgresFeatureIterator::getFeatureAttribute( int idx, QgsPostgresResult
986986
}
987987
else
988988
{
989-
v = QgsPostgresProvider::convertValue( fld.type(), fld.subType(), QString::number( mConn->getBinaryInt( queryResult, row, col ) ), fld.typeName() );
989+
v = QgsPostgresProvider::convertValue( fld.type(), fld.subType(), QString::number( mConn->getBinaryInt( queryResult, row, col ) ), fld.typeName(), mConn );
990990
}
991991
break;
992992
}
993993
default:
994994
{
995-
v = QgsPostgresProvider::convertValue( fld.type(), fld.subType(), queryResult.PQgetvalue( row, col ), fld.typeName() );
995+
v = QgsPostgresProvider::convertValue( fld.type(), fld.subType(), queryResult.PQgetvalue( row, col ), fld.typeName(), mConn );
996996
break;
997997
}
998998
}

‎src/providers/postgres/qgspostgresprovider.cpp

Lines changed: 129 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,106 @@ void QgsPostgresProvider::setTransaction( QgsTransaction *transaction )
354354
mTransaction = static_cast<QgsPostgresTransaction *>( transaction );
355355
}
356356

357+
QgsReferencedGeometry QgsPostgresProvider::fromEwkt( const QString &ewkt, QgsPostgresConn *conn )
358+
{
359+
thread_local const QRegularExpression regularExpressionSRID( "^SRID=(\\d+);" );
360+
361+
QRegularExpressionMatch regularExpressionMatch = regularExpressionSRID.match( ewkt );
362+
if ( !regularExpressionMatch.hasMatch() )
363+
return QgsReferencedGeometry();
364+
365+
QString wkt = ewkt.mid( regularExpressionMatch.captured( 0 ).size() );
366+
int srid = regularExpressionMatch.captured( 1 ).toInt();
367+
368+
369+
QgsGeometry geom = QgsGeometry::fromWkt( wkt );
370+
return QgsReferencedGeometry( geom, sridToCrs( srid, conn ) );
371+
}
372+
373+
QString QgsPostgresProvider::toEwkt( const QgsReferencedGeometry &geom, QgsPostgresConn *conn )
374+
{
375+
if ( !geom.isNull() )
376+
return QStringLiteral( "SRID=%1;%2" ).arg( QString::number( crsToSrid( geom.crs(), conn ) ), geom.asWkt() );
377+
else
378+
return QString();
379+
}
380+
381+
QString QgsPostgresProvider::geomAttrToString( const QVariant &attr, QgsPostgresConn *conn )
382+
{
383+
if ( attr.type() == QVariant::String )
384+
return attr.toString();
385+
else
386+
return toEwkt( attr.value<QgsReferencedGeometry>(), conn );
387+
}
388+
389+
static QMutex sMutex;
390+
static QMap<int, QgsCoordinateReferenceSystem> sCrsCache;
391+
392+
int QgsPostgresProvider::crsToSrid( const QgsCoordinateReferenceSystem &crs, QgsPostgresConn *conn )
393+
{
394+
QMutexLocker locker( &sMutex );
395+
int srid = sCrsCache.key( crs );
396+
397+
if ( srid > -1 )
398+
return srid;
399+
else
400+
{
401+
if ( conn )
402+
{
403+
QStringList authParts = crs.authid().split( ':' );
404+
if ( authParts.size() != 2 )
405+
return -1;
406+
const QString authName = authParts.first();
407+
const QString authId = authParts.last();
408+
QgsPostgresResult result( conn->PQexec( QStringLiteral( "SELECT srid FROM spatial_ref_sys WHERE auth_name='%1' AND auth_srid=%2" ).arg( authName, authId ) ) );
409+
410+
if ( result.PQresultStatus() == PGRES_TUPLES_OK )
411+
{
412+
int srid = result.PQgetvalue( 0, 0 ).toInt();
413+
sCrsCache.insert( srid, crs );
414+
return srid;
415+
}
416+
}
417+
}
418+
419+
return -1;
420+
}
421+
422+
QgsCoordinateReferenceSystem QgsPostgresProvider::sridToCrs( int srid, QgsPostgresConn *conn )
423+
{
424+
QgsCoordinateReferenceSystem crs;
425+
426+
QMutexLocker locker( &sMutex );
427+
if ( sCrsCache.contains( srid ) )
428+
crs = sCrsCache.value( srid );
429+
else
430+
{
431+
if ( conn )
432+
{
433+
QgsPostgresResult result( conn->PQexec( QStringLiteral( "SELECT auth_name, auth_srid, srtext, proj4text FROM spatial_ref_sys WHERE srid=%1" ).arg( srid ) ) );
434+
if ( result.PQresultStatus() == PGRES_TUPLES_OK )
435+
{
436+
const QString authName = result.PQgetvalue( 0, 0 );
437+
const QString authSRID = result.PQgetvalue( 0, 1 );
438+
const QString srText = result.PQgetvalue( 0, 2 );
439+
bool ok = false;
440+
if ( authName == QLatin1String( "EPSG" ) || authName == QLatin1String( "ESRI" ) )
441+
{
442+
ok = crs.createFromUserInput( authName + ':' + authSRID );
443+
}
444+
if ( !ok && !srText.isEmpty() )
445+
{
446+
ok = crs.createFromUserInput( srText );
447+
}
448+
if ( !ok )
449+
crs = QgsCoordinateReferenceSystem::fromProj( result.PQgetvalue( 0, 3 ) );
450+
sCrsCache.insert( srid, crs );
451+
}
452+
}
453+
}
454+
return crs;
455+
}
456+
357457
void QgsPostgresProvider::disconnectDb()
358458
{
359459
if ( mConnectionRO )
@@ -1078,7 +1178,6 @@ bool QgsPostgresProvider::loadFields()
10781178
}
10791179
else if ( fieldTypeName == QLatin1String( "text" ) ||
10801180
fieldTypeName == QLatin1String( "citext" ) ||
1081-
fieldTypeName == QLatin1String( "geometry" ) ||
10821181
fieldTypeName == QLatin1String( "geography" ) ||
10831182
fieldTypeName == QLatin1String( "inet" ) ||
10841183
fieldTypeName == QLatin1String( "cidr" ) ||
@@ -1094,6 +1193,11 @@ bool QgsPostgresProvider::loadFields()
10941193
fieldType = QVariant::String;
10951194
fieldSize = -1;
10961195
}
1196+
else if ( fieldTypeName == QLatin1String( "geometry" ) )
1197+
{
1198+
fieldType = QVariant::UserType;
1199+
fieldSize = -1;
1200+
}
10971201
else if ( fieldTypeName == QLatin1String( "bpchar" ) )
10981202
{
10991203
// although postgres internally uses "bpchar", this is exposed to users as character in postgres
@@ -2448,10 +2552,11 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist, Flags flags )
24482552
}
24492553
else if ( fieldTypeName == QLatin1String( "geometry" ) )
24502554
{
2555+
QString val = geomAttrToString( v, connectionRO() );
24512556
values += QStringLiteral( "%1%2(%3)" )
24522557
.arg( delim,
24532558
connectionRO()->majorVersion() < 2 ? "geomfromewkt" : "st_geomfromewkt",
2454-
quotedValue( v.toString() ) );
2559+
quotedValue( val ) );
24552560
}
24562561
else if ( fieldTypeName == QLatin1String( "geography" ) )
24572562
{
@@ -3054,9 +3159,11 @@ bool QgsPostgresProvider::changeAttributeValues( const QgsChangedAttributesMap &
30543159
}
30553160
else if ( fld.typeName() == QLatin1String( "geometry" ) )
30563161
{
3162+
QString val = geomAttrToString( siter.value(), connectionRO() );
3163+
30573164
sql += QStringLiteral( "%1(%2)" )
30583165
.arg( connectionRO()->majorVersion() < 2 ? "geomfromewkt" : "st_geomfromewkt",
3059-
quotedValue( siter->toString() ) );
3166+
quotedValue( val ) );
30603167
}
30613168
else if ( fld.typeName() == QLatin1String( "geography" ) )
30623169
{
@@ -3418,9 +3525,10 @@ bool QgsPostgresProvider::changeFeatures( const QgsChangedAttributesMap &attr_ma
34183525

34193526
if ( fld.typeName() == QLatin1String( "geometry" ) )
34203527
{
3528+
QString val = geomAttrToString( siter.value(), connectionRO() ) ;
34213529
sql += QStringLiteral( "%1(%2)" )
34223530
.arg( connectionRO()->majorVersion() < 2 ? "geomfromewkt" : "st_geomfromewkt",
3423-
quotedValue( siter->toString() ) );
3531+
quotedValue( val ) );
34243532
}
34253533
else if ( fld.typeName() == QLatin1String( "geography" ) )
34263534
{
@@ -4693,40 +4801,8 @@ QgsCoordinateReferenceSystem QgsPostgresProvider::crs() const
46934801
QgsCoordinateReferenceSystem srs;
46944802
int srid = mRequestedSrid.isEmpty() ? mDetectedSrid.toInt() : mRequestedSrid.toInt();
46954803

4696-
{
4697-
static QMutex sMutex;
4698-
QMutexLocker locker( &sMutex );
4699-
static QMap<int, QgsCoordinateReferenceSystem> sCrsCache;
4700-
if ( sCrsCache.contains( srid ) )
4701-
srs = sCrsCache.value( srid );
4702-
else
4703-
{
4704-
QgsPostgresConn *conn = connectionRO();
4705-
if ( conn )
4706-
{
4707-
QgsPostgresResult result( conn->PQexec( QStringLiteral( "SELECT auth_name, auth_srid, srtext, proj4text FROM spatial_ref_sys WHERE srid=%1" ).arg( srid ) ) );
4708-
if ( result.PQresultStatus() == PGRES_TUPLES_OK )
4709-
{
4710-
const QString authName = result.PQgetvalue( 0, 0 );
4711-
const QString authSRID = result.PQgetvalue( 0, 1 );
4712-
const QString srText = result.PQgetvalue( 0, 2 );
4713-
bool ok = false;
4714-
if ( authName == QLatin1String( "EPSG" ) || authName == QLatin1String( "ESRI" ) )
4715-
{
4716-
ok = srs.createFromUserInput( authName + ':' + authSRID );
4717-
}
4718-
if ( !ok && !srText.isEmpty() )
4719-
{
4720-
ok = srs.createFromUserInput( srText );
4721-
}
4722-
if ( !ok )
4723-
srs = QgsCoordinateReferenceSystem::fromProj( result.PQgetvalue( 0, 3 ) );
4724-
sCrsCache.insert( srid, srs );
4725-
}
4726-
}
4727-
}
4728-
}
4729-
return srs;
4804+
return sridToCrs( srid, connectionRO() );
4805+
47304806
}
47314807

47324808
QString QgsPostgresProvider::subsetString() const
@@ -4847,7 +4923,7 @@ QVariant QgsPostgresProvider::parseJson( const QString &txt )
48474923
return QgsJsonUtils::parseJson( txt );
48484924
}
48494925

4850-
QVariant QgsPostgresProvider::parseOtherArray( const QString &txt, QVariant::Type subType, const QString &typeName )
4926+
QVariant QgsPostgresProvider::parseOtherArray( const QString &txt, QVariant::Type subType, const QString &typeName, QgsPostgresConn *conn )
48514927
{
48524928
int i = 0;
48534929
QVariantList result;
@@ -4859,7 +4935,7 @@ QVariant QgsPostgresProvider::parseOtherArray( const QString &txt, QVariant::Typ
48594935
QgsMessageLog::logMessage( tr( "Error parsing array: %1" ).arg( txt ), tr( "PostGIS" ) );
48604936
break;
48614937
}
4862-
result.append( QgsPostgresProvider::convertValue( subType, QVariant::Invalid, value, typeName ) );
4938+
result.append( convertValue( subType, QVariant::Invalid, value, typeName, conn ) );
48634939
}
48644940
return result;
48654941
}
@@ -4919,7 +4995,7 @@ QVariant QgsPostgresProvider::parseMultidimensionalArray( const QString &txt )
49194995

49204996
}
49214997

4922-
QVariant QgsPostgresProvider::parseArray( const QString &txt, QVariant::Type type, QVariant::Type subType, const QString &typeName )
4998+
QVariant QgsPostgresProvider::parseArray( const QString &txt, QVariant::Type type, QVariant::Type subType, const QString &typeName, QgsPostgresConn *conn )
49234999
{
49245000
if ( !txt.startsWith( '{' ) || !txt.endsWith( '}' ) )
49255001
{
@@ -4933,10 +5009,15 @@ QVariant QgsPostgresProvider::parseArray( const QString &txt, QVariant::Type typ
49335009
else if ( type == QVariant::StringList )
49345010
return parseStringArray( inner );
49355011
else
4936-
return parseOtherArray( inner, subType, typeName );
5012+
return parseOtherArray( inner, subType, typeName, conn );
5013+
}
5014+
5015+
QVariant QgsPostgresProvider::convertValue( QVariant::Type type, QVariant::Type subType, const QString &value, const QString &typeName ) const
5016+
{
5017+
return convertValue( type, subType, value, typeName, connectionRO() );
49375018
}
49385019

4939-
QVariant QgsPostgresProvider::convertValue( QVariant::Type type, QVariant::Type subType, const QString &value, const QString &typeName )
5020+
QVariant QgsPostgresProvider::convertValue( QVariant::Type type, QVariant::Type subType, const QString &value, const QString &typeName, QgsPostgresConn *conn )
49405021
{
49415022
QVariant result;
49425023
switch ( type )
@@ -4949,7 +5030,7 @@ QVariant QgsPostgresProvider::convertValue( QVariant::Type type, QVariant::Type
49495030
break;
49505031
case QVariant::StringList:
49515032
case QVariant::List:
4952-
result = parseArray( value, type, subType, typeName );
5033+
result = parseArray( value, type, subType, typeName, conn );
49535034
break;
49545035
case QVariant::Bool:
49555036
if ( value == QChar( 't' ) )
@@ -4959,6 +5040,10 @@ QVariant QgsPostgresProvider::convertValue( QVariant::Type type, QVariant::Type
49595040
else
49605041
result = QVariant( type );
49615042
break;
5043+
case QVariant::UserType:
5044+
result = fromEwkt( value, conn );
5045+
break;
5046+
49625047
default:
49635048
result = value;
49645049
if ( !result.convert( type ) || value.isNull() )

‎src/providers/postgres/qgspostgresprovider.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "qgspostgresconn.h"
2424
#include "qgsfields.h"
2525
#include "qgsprovidermetadata.h"
26+
#include "qgsreferencedgeometry.h"
2627
#include <memory>
2728

2829
class QgsFeature;
@@ -218,7 +219,8 @@ class QgsPostgresProvider final: public QgsVectorDataProvider
218219
* \param value the value to convert
219220
* \returns a QVariant of the given type or a null QVariant
220221
*/
221-
static QVariant convertValue( QVariant::Type type, QVariant::Type subType, const QString &value, const QString &typeName );
222+
QVariant convertValue( QVariant::Type type, QVariant::Type subType, const QString &value, const QString &typeName ) const;
223+
static QVariant convertValue( QVariant::Type type, QVariant::Type subType, const QString &value, const QString &typeName, QgsPostgresConn *conn );
222224

223225
QList<QgsRelation> discoverRelations( const QgsVectorLayer *self, const QList<QgsVectorLayer *> &layers ) const override;
224226
QgsAttrPalIndexNameHash palAttributeIndexNames() const override;
@@ -274,10 +276,10 @@ class QgsPostgresProvider final: public QgsVectorDataProvider
274276
static QString getNextString( const QString &txt, int &i, const QString &sep );
275277
static QVariant parseHstore( const QString &txt );
276278
static QVariant parseJson( const QString &txt );
277-
static QVariant parseOtherArray( const QString &txt, QVariant::Type subType, const QString &typeName );
279+
static QVariant parseOtherArray( const QString &txt, QVariant::Type subType, const QString &typeName, QgsPostgresConn *conn );
278280
static QVariant parseStringArray( const QString &txt );
279281
static QVariant parseMultidimensionalArray( const QString &txt );
280-
static QVariant parseArray( const QString &txt, QVariant::Type type, QVariant::Type subType, const QString &typeName );
282+
static QVariant parseArray( const QString &txt, QVariant::Type type, QVariant::Type subType, const QString &typeName, QgsPostgresConn *conn );
281283

282284

283285
/**
@@ -500,6 +502,13 @@ class QgsPostgresProvider final: public QgsVectorDataProvider
500502
QgsLayerMetadata mLayerMetadata;
501503

502504
std::unique_ptr< QgsPostgresListener > mListener;
505+
506+
static QgsReferencedGeometry fromEwkt( const QString &ewkt, QgsPostgresConn *conn );
507+
static QString toEwkt( const QgsReferencedGeometry &geom, QgsPostgresConn *conn );
508+
static QString geomAttrToString( const QVariant &attr, QgsPostgresConn *conn );
509+
static int crsToSrid( const QgsCoordinateReferenceSystem &crs, QgsPostgresConn *conn );
510+
static QgsCoordinateReferenceSystem sridToCrs( int srsId, QgsPostgresConn *conn );
511+
503512
};
504513

505514

‎tests/src/gui/testqgslistwidget.cpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ class TestQgsListWidget : public QObject
145145
{
146146
//create pg layers
147147
QgsVectorLayer *vl_array_int = new QgsVectorLayer( QStringLiteral( "%1 sslmode=disable key=\"pk\" table=\"qgis_test\".\"array_tbl\" sql=" ).arg( dbConn ), QStringLiteral( "json" ), QStringLiteral( "postgres" ) );
148+
149+
connect( vl_array_int, &QgsVectorLayer::raiseError, this, []( const QString & msg ) { qWarning() << msg; } );
148150
QVERIFY( vl_array_int->isValid( ) );
149151

150152
QgsListWidgetWrapper w_array_int( vl_array_int, vl_array_int->fields().indexOf( QLatin1String( "location" ) ), nullptr, nullptr );
@@ -161,10 +163,8 @@ class TestQgsListWidget : public QObject
161163
new_rec_997.setAttribute( 0, QVariant( 997 ) );
162164
vl_array_int->addFeature( new_rec_997, QgsFeatureSink::RollBackOnErrors );
163165
vl_array_int->commitChanges( false );
164-
bool success = vl_array_int->changeAttributeValue( 997, 1, w_array_int.value(), QVariant(), false );
165-
QVERIFY( success );
166-
success = vl_array_int->commitChanges( false );
167-
QVERIFY( success );
166+
QVERIFY( vl_array_int->changeAttributeValue( 997, 1, w_array_int.value(), QVariant(), false ) );
167+
QVERIFY( vl_array_int->commitChanges( false ) );
168168

169169
w_array_int.setFeature( vl_array_int->getFeature( 997 ) );
170170
QCOMPARE( widget->list( ), QList<QVariant>( ) << 100 );
@@ -208,10 +208,8 @@ class TestQgsListWidget : public QObject
208208
new_rec_997_str.setAttribute( 0, QVariant( 997 ) );
209209
vl_array_str->addFeature( new_rec_997_str, QgsFeatureSink::RollBackOnErrors );
210210
vl_array_str->commitChanges( false );
211-
success = vl_array_str->changeAttributeValue( 997, 1, w_array_str.value(), QVariant(), false );
212-
QVERIFY( success );
213-
success = vl_array_str->commitChanges( false );
214-
QVERIFY( success );
211+
QVERIFY( vl_array_str->changeAttributeValue( 997, 1, w_array_str.value(), QVariant(), false ) );
212+
QVERIFY( vl_array_str->commitChanges( false ) );
215213

216214
w_array_str.setFeature( vl_array_str->getFeature( 997 ) );
217215
QCOMPARE( widget->list( ), QList<QVariant>( ) << QStringLiteral( "10\"0" ) );

‎tests/src/providers/testqgspostgresprovider.cpp

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class TestQgsPostgresProvider: public QObject
4747

4848
void TestQgsPostgresProvider::decodeHstore()
4949
{
50-
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::Map, QVariant::String, QStringLiteral( "\"1\"=>\"2\", \"a\"=>\"b, \\\"c'\", \"backslash\"=>\"\\\\\"" ), QStringLiteral( "hstore" ) );
50+
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::Map, QVariant::String, QStringLiteral( "\"1\"=>\"2\", \"a\"=>\"b, \\\"c'\", \"backslash\"=>\"\\\\\"" ), QStringLiteral( "hstore" ), nullptr );
5151
QCOMPARE( decoded.type(), QVariant::Map );
5252

5353
QVariantMap expected;
@@ -60,7 +60,7 @@ void TestQgsPostgresProvider::decodeHstore()
6060

6161
void TestQgsPostgresProvider::decodeHstoreNoQuote()
6262
{
63-
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::Map, QVariant::String, QStringLiteral( "1=>2, a=>b c" ), QStringLiteral( "hstore" ) );
63+
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::Map, QVariant::String, QStringLiteral( "1=>2, a=>b c" ), QStringLiteral( "hstore" ), nullptr );
6464
QCOMPARE( decoded.type(), QVariant::Map );
6565

6666
QVariantMap expected;
@@ -72,7 +72,7 @@ void TestQgsPostgresProvider::decodeHstoreNoQuote()
7272

7373
void TestQgsPostgresProvider::decodeArray2StringList()
7474
{
75-
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::StringList, QVariant::String, QStringLiteral( "{\"1\",\"2\", \"a\\\\1\" , \"\\\\\",\"b, \\\"c'\"}" ), QStringLiteral( "hstore" ) );
75+
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::StringList, QVariant::String, QStringLiteral( "{\"1\",\"2\", \"a\\\\1\" , \"\\\\\",\"b, \\\"c'\"}" ), QStringLiteral( "hstore" ), nullptr );
7676
QCOMPARE( decoded.type(), QVariant::StringList );
7777

7878
QStringList expected;
@@ -83,7 +83,7 @@ void TestQgsPostgresProvider::decodeArray2StringList()
8383

8484
void TestQgsPostgresProvider::decodeArray2StringListNoQuote()
8585
{
86-
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::StringList, QVariant::String, QStringLiteral( "{1,2, a ,b, c}" ), QStringLiteral( "hstore" ) );
86+
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::StringList, QVariant::String, QStringLiteral( "{1,2, a ,b, c}" ), QStringLiteral( "hstore" ), nullptr );
8787
QCOMPARE( decoded.type(), QVariant::StringList );
8888

8989
QStringList expected;
@@ -94,7 +94,7 @@ void TestQgsPostgresProvider::decodeArray2StringListNoQuote()
9494

9595
void TestQgsPostgresProvider::decodeArray2IntList()
9696
{
97-
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::StringList, QVariant::String, QStringLiteral( "{1, 2, 3,-5,10}" ), QStringLiteral( "hstore" ) );
97+
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::StringList, QVariant::String, QStringLiteral( "{1, 2, 3,-5,10}" ), QStringLiteral( "hstore" ), nullptr );
9898
QCOMPARE( decoded.type(), QVariant::StringList );
9999

100100
QVariantList expected;
@@ -105,7 +105,7 @@ void TestQgsPostgresProvider::decodeArray2IntList()
105105

106106
void TestQgsPostgresProvider::decode2DimensionArray()
107107
{
108-
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::StringList, QVariant::String, QStringLiteral( "{{foo,\"escape bracket \\}\"},{\"escape bracket and backslash \\\\\\}\",\"hello bar\"}}" ), QStringLiteral( "_text" ) );
108+
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::StringList, QVariant::String, QStringLiteral( "{{foo,\"escape bracket \\}\"},{\"escape bracket and backslash \\\\\\}\",\"hello bar\"}}" ), QStringLiteral( "_text" ), nullptr );
109109
QCOMPARE( decoded.type(), QVariant::StringList );
110110

111111
QVariantList expected;
@@ -116,7 +116,7 @@ void TestQgsPostgresProvider::decode2DimensionArray()
116116

117117
void TestQgsPostgresProvider::decode3DimensionArray()
118118
{
119-
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::StringList, QVariant::String, QStringLiteral( "{{{0,1},{1,2}},{{3,4},{5,6}}}" ), QStringLiteral( "_integer" ) );
119+
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::StringList, QVariant::String, QStringLiteral( "{{{0,1},{1,2}},{{3,4},{5,6}}}" ), QStringLiteral( "_integer" ), nullptr );
120120
QCOMPARE( decoded.type(), QVariant::StringList );
121121

122122
QVariantList expected;
@@ -127,7 +127,7 @@ void TestQgsPostgresProvider::decode3DimensionArray()
127127

128128
void TestQgsPostgresProvider::decodeJsonList()
129129
{
130-
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::Map, QVariant::String, QStringLiteral( "[1,2,3]" ), QStringLiteral( "json" ) );
130+
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::Map, QVariant::String, QStringLiteral( "[1,2,3]" ), QStringLiteral( "json" ), nullptr );
131131
QCOMPARE( decoded.type(), QVariant::List );
132132

133133
QVariantList expected;
@@ -140,7 +140,7 @@ void TestQgsPostgresProvider::decodeJsonList()
140140

141141
void TestQgsPostgresProvider::decodeJsonbList()
142142
{
143-
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::Map, QVariant::String, QStringLiteral( "[1,2,3]" ), QStringLiteral( "jsonb" ) );
143+
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::Map, QVariant::String, QStringLiteral( "[1,2,3]" ), QStringLiteral( "jsonb" ), nullptr );
144144
QCOMPARE( decoded.type(), QVariant::List );
145145

146146
QVariantList expected;
@@ -153,7 +153,7 @@ void TestQgsPostgresProvider::decodeJsonbList()
153153

154154
void TestQgsPostgresProvider::decodeJsonMap()
155155
{
156-
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::Map, QVariant::String, QStringLiteral( "{\"a\":1,\"b\":2}" ), QStringLiteral( "json" ) );
156+
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::Map, QVariant::String, QStringLiteral( "{\"a\":1,\"b\":2}" ), QStringLiteral( "json" ), nullptr );
157157
QCOMPARE( decoded.type(), QVariant::Map );
158158

159159
QVariantMap expected;
@@ -165,7 +165,7 @@ void TestQgsPostgresProvider::decodeJsonMap()
165165

166166
void TestQgsPostgresProvider::decodeJsonbMap()
167167
{
168-
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::Map, QVariant::String, QStringLiteral( "{\"a\":1,\"b\":2}" ), QStringLiteral( "jsonb" ) );
168+
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::Map, QVariant::String, QStringLiteral( "{\"a\":1,\"b\":2}" ), QStringLiteral( "jsonb" ), nullptr );
169169
QCOMPARE( decoded.type(), QVariant::Map );
170170

171171
QVariantMap expected;
@@ -180,19 +180,19 @@ void TestQgsPostgresProvider::testDecodeDateTimes()
180180

181181
QVariant decoded;
182182

183-
decoded = QgsPostgresProvider::convertValue( QVariant::DateTime, QVariant::Invalid, QStringLiteral( "2020-06-08 18:30:35.496438+02" ), QStringLiteral( "timestamptz" ) );
183+
decoded = QgsPostgresProvider::convertValue( QVariant::DateTime, QVariant::Invalid, QStringLiteral( "2020-06-08 18:30:35.496438+02" ), QStringLiteral( "timestamptz" ), nullptr );
184184
QCOMPARE( decoded.type(), QVariant::DateTime );
185185

186-
decoded = QgsPostgresProvider::convertValue( QVariant::Time, QVariant::Invalid, QStringLiteral( "18:29:27.569401+02" ), QStringLiteral( "timetz" ) );
186+
decoded = QgsPostgresProvider::convertValue( QVariant::Time, QVariant::Invalid, QStringLiteral( "18:29:27.569401+02" ), QStringLiteral( "timetz" ), nullptr );
187187
QCOMPARE( decoded.type(), QVariant::Time );
188188

189-
decoded = QgsPostgresProvider::convertValue( QVariant::Date, QVariant::Invalid, QStringLiteral( "2020-06-08" ), QStringLiteral( "date" ) );
189+
decoded = QgsPostgresProvider::convertValue( QVariant::Date, QVariant::Invalid, QStringLiteral( "2020-06-08" ), QStringLiteral( "date" ), nullptr );
190190
QCOMPARE( decoded.type(), QVariant::Date );
191191

192-
decoded = QgsPostgresProvider::convertValue( QVariant::DateTime, QVariant::Invalid, QStringLiteral( "2020-06-08 18:30:35.496438" ), QStringLiteral( "timestamp" ) );
192+
decoded = QgsPostgresProvider::convertValue( QVariant::DateTime, QVariant::Invalid, QStringLiteral( "2020-06-08 18:30:35.496438" ), QStringLiteral( "timestamp" ), nullptr );
193193
QCOMPARE( decoded.type(), QVariant::DateTime );
194194

195-
decoded = QgsPostgresProvider::convertValue( QVariant::Time, QVariant::Invalid, QStringLiteral( "18:29:27.569401" ), QStringLiteral( "time" ) );
195+
decoded = QgsPostgresProvider::convertValue( QVariant::Time, QVariant::Invalid, QStringLiteral( "18:29:27.569401" ), QStringLiteral( "time" ), nullptr );
196196
QCOMPARE( decoded.type(), QVariant::Time );
197197

198198
}

‎tests/src/python/test_provider_postgres.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
QgsTransactionGroup,
4242
QgsReadWriteContext,
4343
QgsRectangle,
44+
QgsReferencedGeometry,
4445
QgsDefaultValue,
4546
QgsCoordinateReferenceSystem,
4647
QgsProject,
@@ -3509,6 +3510,45 @@ def testExportPkGuessLogic(self):
35093510

35103511
self.assertEqual(exported_layer.fields().names(), ['id', 'name', 'author'])
35113512

3513+
def testEwkt(self):
3514+
vl = QgsVectorLayer(f'{self.dbconn} table="qgis_test"."someData" sql=', "someData", "postgres")
3515+
tg = QgsTransactionGroup()
3516+
tg.addLayer(vl)
3517+
3518+
feature = next(vl.getFeatures())
3519+
# make sure we get a QgsReferenceGeometry and not "just" a string
3520+
self.assertEqual(feature['geom'].crs().authid(), 'EPSG:4326')
3521+
3522+
vl.startEditing()
3523+
3524+
# Layer accepts a referenced geometry
3525+
feature['geom'] = QgsReferencedGeometry(QgsGeometry.fromWkt('POINT(70 70)'), QgsCoordinateReferenceSystem.fromEpsgId(4326))
3526+
self.assertTrue(vl.updateFeature(feature))
3527+
3528+
# Layer will accept null geometry
3529+
feature['geom'] = QgsReferencedGeometry()
3530+
self.assertTrue(vl.updateFeature(feature))
3531+
3532+
# Layer will not accept invalid crs
3533+
feature['geom'] = QgsReferencedGeometry(QgsGeometry.fromWkt('POINT(1 1)'), QgsCoordinateReferenceSystem())
3534+
self.assertFalse(vl.updateFeature(feature))
3535+
3536+
# EWKT strings are accepted too
3537+
feature['geom'] = 'SRID=4326;Point (71 78)'
3538+
self.assertTrue(vl.updateFeature(feature))
3539+
3540+
# addFeature
3541+
feature['pk'] = 8
3542+
self.assertTrue(vl.addFeature(feature))
3543+
3544+
# changeAttributeValue
3545+
geom = QgsReferencedGeometry(QgsGeometry.fromWkt('POINT(3 3)'), QgsCoordinateReferenceSystem.fromEpsgId(4326))
3546+
3547+
feature['pk'] = 8
3548+
self.assertTrue(vl.changeAttributeValue(8, 8, geom))
3549+
self.assertEqual(vl.getFeature(8)['geom'].asWkt(), geom.asWkt())
3550+
self.assertEqual(vl.getFeature(8)['geom'].crs(), geom.crs())
3551+
35123552

35133553
if __name__ == '__main__':
35143554
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.