Skip to content

Commit 4da0413

Browse files
authored
Merge pull request #9890 from qgis/backport-9873-to-release-3_6
[Backport release-3_6] [postgresql] Fix checkPrimaryKeyUnicity option
2 parents 551a617 + d1e0396 commit 4da0413

File tree

9 files changed

+130
-40
lines changed

9 files changed

+130
-40
lines changed

src/app/qgsprojectproperties.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,8 @@ void QgsProjectProperties::apply()
989989
// Set the project title
990990
QgsProject::instance()->setTitle( title() );
991991
QgsProject::instance()->setPresetHomePath( QDir::fromNativeSeparators( mProjectHomeLineEdit->text() ) );
992+
993+
// DB-related options
992994
QgsProject::instance()->setAutoTransaction( mAutoTransaction->isChecked() );
993995
QgsProject::instance()->setEvaluateDefaultValues( mEvaluateDefaultValues->isChecked() );
994996
QgsProject::instance()->setTrustLayerMetadata( mTrustProjectCheckBox->isChecked() );

src/core/qgsvectorlayer.cpp

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1559,25 +1559,26 @@ QString QgsVectorLayer::loadDefaultStyle( bool &resultFlag )
15591559

15601560
bool QgsVectorLayer::setDataProvider( QString const &provider, const QgsDataProvider::ProviderOptions &options )
15611561
{
1562-
mProviderKey = provider; // XXX is this necessary? Usually already set
1562+
mProviderKey = provider;
1563+
delete mDataProvider;
15631564

1564-
// primary key unicity is tested at construction time, so it has to be set
1565-
// before initializing postgres provider
1566-
QString checkUnicityKey = QStringLiteral( "checkPrimaryKeyUnicity" );
1567-
QString dataSource = mDataSource;
1565+
// For Postgres provider primary key unicity is tested at construction time,
1566+
// so it has to be set before initializing the provider,
1567+
// this manipulation is necessary to preserve default behavior when
1568+
// "trust layer metadata" project level option is set and checkPrimaryKeyUnicity
1569+
// was not explicitly passed in the uri
15681570
if ( provider.compare( QLatin1String( "postgres" ) ) == 0 )
15691571
{
1570-
QgsDataSourceUri uri( dataSource );
1571-
1572-
if ( uri.hasParam( checkUnicityKey ) )
1573-
uri.removeParam( checkUnicityKey );
1574-
1575-
uri.setParam( checkUnicityKey, mReadExtentFromXml ? "0" : "1" );
1576-
dataSource = uri.uri( false );
1572+
const QString checkUnicityKey { QStringLiteral( "checkPrimaryKeyUnicity" ) };
1573+
QgsDataSourceUri uri( mDataSource );
1574+
if ( ! uri.hasParam( checkUnicityKey ) )
1575+
{
1576+
uri.setParam( checkUnicityKey, mReadExtentFromXml ? "0" : "1" );
1577+
mDataSource = uri.uri( false );
1578+
}
15771579
}
15781580

1579-
delete mDataProvider;
1580-
mDataProvider = qobject_cast<QgsVectorDataProvider *>( QgsProviderRegistry::instance()->createProvider( provider, dataSource, options ) );
1581+
mDataProvider = qobject_cast<QgsVectorDataProvider *>( QgsProviderRegistry::instance()->createProvider( provider, mDataSource, options ) );
15811582
if ( !mDataProvider )
15821583
{
15831584
mValid = false;
@@ -1635,15 +1636,7 @@ bool QgsVectorLayer::setDataProvider( QString const &provider, const QgsDataProv
16351636
if ( !lName.isEmpty() )
16361637
setName( lName );
16371638
}
1638-
16391639
QgsDebugMsgLevel( QStringLiteral( "Beautified layer name %1" ).arg( name() ), 3 );
1640-
1641-
// deal with unnecessary schema qualification to make v.in.ogr happy
1642-
// and remove unnecessary key
1643-
QgsDataSourceUri dataProviderUri( mDataProvider->dataSourceUri() );
1644-
if ( dataProviderUri.hasParam( checkUnicityKey ) )
1645-
dataProviderUri.removeParam( checkUnicityKey );
1646-
mDataSource = dataProviderUri.uri( false );
16471640
}
16481641
else if ( mProviderKey == QLatin1String( "osm" ) )
16491642
{

src/providers/postgres/qgspgtablemodel.cpp

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "qgslogger.h"
2121
#include "qgsapplication.h"
2222
#include "qgssettings.h"
23+
#include "qgsproject.h"
2324

2425
#include <climits>
2526

@@ -35,8 +36,11 @@ QgsPgTableModel::QgsPgTableModel()
3536
headerLabels << tr( "SRID" );
3637
headerLabels << tr( "Feature id" );
3738
headerLabels << tr( "Select at id" );
39+
headerLabels << tr( "Check PK unicity" );
3840
headerLabels << tr( "Sql" );
3941
setHorizontalHeaderLabels( headerLabels );
42+
setHeaderData( Columns::DbtmSelectAtId, Qt::Orientation::Horizontal, tr( "Disable 'Fast Access to Features at ID' capability to force keeping the attribute table in memory (e.g. in case of expensive views)." ), Qt::ToolTipRole );
43+
setHeaderData( Columns::DbtmCheckPkUnicity, Qt::Orientation::Horizontal, tr( "Enable check for primary key unicity when loading views and materialized views. This option can make loading of large datasets significantly slower." ), Qt::ToolTipRole );
4044
}
4145

4246
void QgsPgTableModel::addTableEntry( const QgsPostgresLayerProperty &layerProperty )
@@ -120,7 +124,25 @@ void QgsPgTableModel::addTableEntry( const QgsPostgresLayerProperty &layerProper
120124
QStandardItem *selItem = new QStandardItem( QString() );
121125
selItem->setFlags( selItem->flags() | Qt::ItemIsUserCheckable );
122126
selItem->setCheckState( Qt::Checked );
123-
selItem->setToolTip( tr( "Disable 'Fast Access to Features at ID' capability to force keeping the attribute table in memory (e.g. in case of expensive views)." ) );
127+
selItem->setToolTip( headerData( Columns::DbtmSelectAtId, Qt::Orientation::Horizontal, Qt::ToolTipRole ).toString() );
128+
129+
QStandardItem *checkPkUnicityItem = new QStandardItem( QString() );
130+
checkPkUnicityItem->setFlags( checkPkUnicityItem->flags() | Qt::ItemIsUserCheckable );
131+
132+
// Legacy: default value is determined by project option to trust layer's metadata
133+
// TODO: remove this default from QGIS 4 and leave default value to false?
134+
// checkPkUnicity has only effect on views and materialized views, so we can safely disable it
135+
if ( layerProperty.isView || layerProperty.isMaterializedView )
136+
{
137+
checkPkUnicityItem->setCheckState( QgsProject::instance( )->trustLayerMetadata() ? Qt::CheckState::Unchecked : Qt::CheckState::Checked );
138+
checkPkUnicityItem->setToolTip( headerData( Columns::DbtmCheckPkUnicity, Qt::Orientation::Horizontal, Qt::ToolTipRole ).toString() );
139+
}
140+
else
141+
{
142+
checkPkUnicityItem->setCheckState( Qt::CheckState::Unchecked );
143+
checkPkUnicityItem->setFlags( checkPkUnicityItem->flags() & ~ Qt::ItemIsEnabled );
144+
checkPkUnicityItem->setToolTip( tr( "This option is only available for views and materialized views." ) );
145+
}
124146

125147
QStandardItem *sqlItem = new QStandardItem( layerProperty.sql );
126148

@@ -135,6 +157,7 @@ void QgsPgTableModel::addTableEntry( const QgsPostgresLayerProperty &layerProper
135157
childItemList << sridItem;
136158
childItemList << pkItem;
137159
childItemList << selItem;
160+
childItemList << checkPkUnicityItem;
138161
childItemList << sqlItem;
139162

140163
Q_FOREACH ( QStandardItem *item, childItemList )
@@ -144,7 +167,7 @@ void QgsPgTableModel::addTableEntry( const QgsPostgresLayerProperty &layerProper
144167
else
145168
item->setFlags( item->flags() & ~Qt::ItemIsSelectable );
146169

147-
if ( tip.isEmpty() )
170+
if ( tip.isEmpty() && item != checkPkUnicityItem && item != selItem )
148171
{
149172
item->setToolTip( QString() );
150173
}
@@ -374,6 +397,7 @@ QString QgsPgTableModel::layerURI( const QModelIndex &index, const QString &conn
374397

375398
bool selectAtId = itemFromIndex( index.sibling( index.row(), DbtmSelectAtId ) )->checkState() == Qt::Checked;
376399
QString sql = index.sibling( index.row(), DbtmSql ).data( Qt::DisplayRole ).toString();
400+
bool checkPrimaryKeyUnicity = itemFromIndex( index.sibling( index.row(), DbtmCheckPkUnicity ) )->checkState() == Qt::Checked;
377401

378402
QgsDataSourceUri uri( connInfo );
379403

@@ -390,6 +414,7 @@ QString QgsPgTableModel::layerURI( const QModelIndex &index, const QString &conn
390414
uri.setWkbType( wkbType );
391415
uri.setSrid( srid );
392416
uri.disableSelectAtId( !selectAtId );
417+
uri.setParam( QStringLiteral( "checkPrimaryKeyUnicity" ), checkPrimaryKeyUnicity ? QLatin1Literal( "1" ) : QLatin1Literal( "0" ) );
393418

394419
QgsDebugMsg( QStringLiteral( "returning uri %1" ).arg( uri.uri( false ) ) );
395420
return uri.uri( false );

src/providers/postgres/qgspgtablemodel.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class QgsPgTableModel : public QStandardItemModel
5353
DbtmSrid,
5454
DbtmPkCol,
5555
DbtmSelectAtId,
56+
DbtmCheckPkUnicity,
5657
DbtmSql,
5758
DbtmColumns
5859
};

src/providers/postgres/qgspostgresconn.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ struct QgsPostgresLayerProperty
8383
bool isMaterializedView = false;
8484
QString tableComment;
8585

86-
8786
// TODO: rename this !
8887
int size() const { Q_ASSERT( types.size() == srids.size() ); return types.size(); }
8988

src/providers/postgres/qgspostgresprovider.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ QgsPostgresProvider::QgsPostgresProvider( QString const &uri, const ProviderOpti
108108

109109
if ( mUri.hasParam( QStringLiteral( "checkPrimaryKeyUnicity" ) ) )
110110
{
111+
111112
if ( mUri.param( QStringLiteral( "checkPrimaryKeyUnicity" ) ).compare( QLatin1String( "0" ) ) == 0 )
112113
{
113114
mCheckPrimaryKeyUnicity = false;
@@ -1312,7 +1313,7 @@ bool QgsPostgresProvider::determinePrimaryKey()
13121313

13131314
QStringList log;
13141315

1315-
// no primary or unique indizes found
1316+
// no primary or unique indices found
13161317
if ( res.PQntuples() == 0 )
13171318
{
13181319
QgsDebugMsg( QStringLiteral( "Relation has no primary key -- investigating alternatives" ) );
@@ -1568,7 +1569,6 @@ bool QgsPostgresProvider::uniqueData( const QString &quotedColNames )
15681569
pushError( unique.PQresultErrorMessage() );
15691570
return false;
15701571
}
1571-
15721572
return unique.PQntuples() == 1 && unique.PQgetvalue( 0, 0 ).startsWith( 't' );
15731573
}
15741574

src/ui/qgsprojectpropertiesbase.ui

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@
236236
</sizepolicy>
237237
</property>
238238
<property name="currentIndex">
239-
<number>8</number>
239+
<number>4</number>
240240
</property>
241241
<widget class="QWidget" name="mProjOptsGeneral">
242242
<layout class="QVBoxLayout" name="verticalLayout_6">
@@ -265,8 +265,8 @@
265265
<rect>
266266
<x>0</x>
267267
<y>0</y>
268-
<width>590</width>
269-
<height>777</height>
268+
<width>537</width>
269+
<height>795</height>
270270
</rect>
271271
</property>
272272
<layout class="QVBoxLayout" name="verticalLayout_8">
@@ -863,8 +863,8 @@
863863
<rect>
864864
<x>0</x>
865865
<y>0</y>
866-
<width>603</width>
867-
<height>161</height>
866+
<width>546</width>
867+
<height>164</height>
868868
</rect>
869869
</property>
870870
<layout class="QVBoxLayout" name="verticalLayout_7">
@@ -938,8 +938,8 @@
938938
<rect>
939939
<x>0</x>
940940
<y>0</y>
941-
<width>290</width>
942-
<height>543</height>
941+
<width>269</width>
942+
<height>553</height>
943943
</rect>
944944
</property>
945945
<layout class="QVBoxLayout" name="verticalLayout_12">
@@ -1378,7 +1378,7 @@
13781378
<item row="3" column="0">
13791379
<widget class="QCheckBox" name="mTrustProjectCheckBox">
13801380
<property name="toolTip">
1381-
<string>Speed up project loading by skipping data checks. Useful in qgis server context or project with huge database views or materialized views.</string>
1381+
<string>Speed up project loading by skipping data checks in PostgreSQL layers. Useful in QGIS server context or project with huge database views or materialized views.</string>
13821382
</property>
13831383
<property name="text">
13841384
<string>Trust project when data source has no metadata</string>
@@ -1514,8 +1514,8 @@
15141514
<rect>
15151515
<x>0</x>
15161516
<y>0</y>
1517-
<width>178</width>
1518-
<height>54</height>
1517+
<width>167</width>
1518+
<height>55</height>
15191519
</rect>
15201520
</property>
15211521
<layout class="QVBoxLayout" name="verticalLayout_17">
@@ -1575,9 +1575,9 @@
15751575
<property name="geometry">
15761576
<rect>
15771577
<x>0</x>
1578-
<y>-975</y>
1579-
<width>671</width>
1580-
<height>2682</height>
1578+
<y>0</y>
1579+
<width>598</width>
1580+
<height>2732</height>
15811581
</rect>
15821582
</property>
15831583
<layout class="QVBoxLayout" name="verticalLayout_13">

tests/src/python/test_provider_postgres.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,37 @@ def testReadExtentOnTable(self):
10971097

10981098
self.assertEqual(vl2.extent(), originalExtent)
10991099

1100+
def testCheckPkUnicityOnView(self):
1101+
# vector layer based on view
1102+
1103+
# This is valid
1104+
vl0 = QgsVectorLayer(self.dbconn + ' checkPrimaryKeyUnicity=\'0\' sslmode=disable key=\'pk\' srid=0 type=POINT table="qgis_test"."b21839_pk_unicity_view" (geom) sql=', 'test', 'postgres')
1105+
self.assertTrue(vl0.isValid())
1106+
1107+
geom = vl0.getFeature(1).geometry().asWkt()
1108+
1109+
# This is NOT valid
1110+
vl0 = QgsVectorLayer(self.dbconn + ' checkPrimaryKeyUnicity=\'1\' sslmode=disable key=\'an_int\' srid=0 type=POINT table="qgis_test"."b21839_pk_unicity_view" (geom) sql=', 'test', 'postgres')
1111+
self.assertFalse(vl0.isValid())
1112+
1113+
# This is NOT valid because the default is to check unicity
1114+
vl0 = QgsVectorLayer(self.dbconn + ' sslmode=disable key=\'an_int\' srid=0 type=POINT table="qgis_test"."b21839_pk_unicity_view" (geom) sql=', 'test', 'postgres')
1115+
self.assertFalse(vl0.isValid())
1116+
1117+
# This is valid because the readExtentFromXml option is set
1118+
options = QgsVectorLayer.LayerOptions(True, True) # loadDefaultStyle, readExtentFromXml
1119+
vl0 = QgsVectorLayer(self.dbconn + ' sslmode=disable key=\'an_int\' srid=0 type=POINT table="qgis_test"."b21839_pk_unicity_view" (geom) sql=', 'test', 'postgres', options)
1120+
self.assertTrue(vl0.isValid())
1121+
1122+
# Valid because a_unique_int is unique and default is to check unicity
1123+
vl0 = QgsVectorLayer(self.dbconn + ' sslmode=disable key=\'a_unique_int\' srid=0 type=POINT table="qgis_test"."b21839_pk_unicity_view" (geom) sql=', 'test', 'postgres')
1124+
self.assertEqual(vl0.getFeature(1).geometry().asWkt(), geom)
1125+
1126+
# Valid because a_unique_int is unique
1127+
vl0 = QgsVectorLayer(self.dbconn + ' checkPrimaryKeyUnicity=\'1\' sslmode=disable key=\'a_unique_int\' srid=0 type=POINT table="qgis_test"."b21839_pk_unicity_view" (geom) sql=', 'test', 'postgres')
1128+
self.assertTrue(vl0.isValid())
1129+
self.assertEqual(vl0.getFeature(1).geometry().asWkt(), geom)
1130+
11001131
def testNotify(self):
11011132
vl0 = QgsVectorLayer(self.dbconn + ' sslmode=disable key=\'pk\' srid=4326 type=POLYGON table="qgis_test"."some_poly_data" (geom) sql=', 'test', 'postgres')
11021133
vl0.dataProvider().setListening(True)

tests/testdata/provider/testdata_pg.sql

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,4 +534,43 @@ INSERT INTO qgis_test.check_constraints VALUES (
534534
1, -- id
535535
4, -- a
536536
3 -- b
537+
);
538+
539+
540+
---------------------------------------------
541+
--
542+
-- Table and view for tests on checkPrimaryKeyUnicity
543+
--
544+
545+
DROP TABLE IF EXISTS qgis_test.b21839_pk_unicity CASCADE;
546+
CREATE TABLE qgis_test.b21839_pk_unicity
547+
(
548+
pk serial NOT NULL,
549+
an_int integer NOT NULL,
550+
a_unique_int integer NOT NULL,
551+
geom geometry(Point),
552+
CONSTRAINT b21839_pk_unicity_pkey PRIMARY KEY (pk)
537553
)
554+
WITH (
555+
OIDS=FALSE
556+
);
557+
558+
559+
INSERT INTO qgis_test.b21839_pk_unicity(
560+
pk, an_int, a_unique_int , geom)
561+
VALUES (1, 1, 1, ST_GeomFromText('point( 1 1)'));
562+
563+
564+
INSERT INTO qgis_test.b21839_pk_unicity(
565+
pk, an_int, a_unique_int, geom)
566+
VALUES (2, 1, 2, ST_GeomFromText('point( 1 3)'));
567+
568+
569+
570+
CREATE OR REPLACE VIEW qgis_test.b21839_pk_unicity_view AS
571+
SELECT b21839_pk_unicity.pk,
572+
b21839_pk_unicity.an_int,
573+
b21839_pk_unicity.a_unique_int,
574+
b21839_pk_unicity.geom
575+
FROM qgis_test.b21839_pk_unicity;
576+

0 commit comments

Comments
 (0)