diff --git a/src/providers/postgres/qgspostgresprovider.cpp b/src/providers/postgres/qgspostgresprovider.cpp index fc2a6b779c08..37427d28a7a4 100644 --- a/src/providers/postgres/qgspostgresprovider.cpp +++ b/src/providers/postgres/qgspostgresprovider.cpp @@ -1111,6 +1111,31 @@ bool QgsPostgresProvider::loadFields() } mAttrPalIndexName.insert( i, fieldName ); + // If this is an identity field with constraints and there is no default, let's look for a sequence: + // we might have a default value created by a sequence named __seq + if ( ! identityMap[tableoid ][ attnum ].isEmpty() + && notNullMap[tableoid][ attnum ] + && uniqueMap[tableoid][attnum] + && defValMap[tableoid][attnum].isEmpty() ) + { + const QString seqName { mTableName + '_' + fieldName + QStringLiteral( "_seq" ) }; + const QString seqSql { QStringLiteral( "SELECT c.oid " + " FROM pg_class c " + " LEFT JOIN pg_namespace n " + " ON ( n.oid = c.relnamespace ) " + " WHERE c.relkind = 'S' " + " AND c.relname = %1 " + " AND n.nspname = %2" ) + .arg( quotedValue( seqName ) ) + .arg( quotedValue( mSchemaName ) ) + }; + QgsPostgresResult seqResult( connectionRO()->PQexec( seqSql ) ); + if ( seqResult.PQntuples() == 1 ) + { + defValMap[tableoid][attnum] = QStringLiteral( "nextval(%1::regclass)" ).arg( quotedIdentifier( seqName ) ); + } + } + mDefaultValues.insert( mAttributeFields.size(), defValMap[tableoid][attnum] ); QgsField newField = QgsField( fieldName, fieldType, fieldTypeName, fieldSize, fieldPrec, fieldComment, fieldSubType ); diff --git a/tests/src/python/test_provider_postgres.py b/tests/src/python/test_provider_postgres.py index c063a7db9391..3ea154e0fb64 100644 --- a/tests/src/python/test_provider_postgres.py +++ b/tests/src/python/test_provider_postgres.py @@ -1420,6 +1420,25 @@ def testFeatureCountEstimatedOnView(self): self.assertTrue(vl.isValid()) self.assertTrue(self.source.featureCount() > 0) + def testIdentityPk(self): + """Test a table with identity pk, see GH #29560""" + + vl = QgsVectorLayer(self.dbconn + ' sslmode=disable key=\'gid\' srid=4326 type=POLYGON table="qgis_test"."b29560"(geom) sql=', 'testb29560', 'postgres') + self.assertTrue(vl.isValid()) + + feature = QgsFeature(vl.fields()) + geom = QgsGeometry.fromWkt('POLYGON EMPTY') + feature.setGeometry(geom) + self.assertTrue(vl.dataProvider().addFeature(feature)) + + del(vl) + + # Verify + vl = QgsVectorLayer(self.dbconn + ' sslmode=disable key=\'gid\' srid=4326 type=POLYGON table="qgis_test"."b29560"(geom) sql=', 'testb29560', 'postgres') + self.assertTrue(vl.isValid()) + feature = next(vl.getFeatures()) + self.assertIsNotNone(feature.id()) + class TestPyQgsPostgresProviderCompoundKey(unittest.TestCase, ProviderTestCase): diff --git a/tests/testdata/provider/testdata_pg.sql b/tests/testdata/provider/testdata_pg.sql index 755f546d5601..96c3efe023ae 100644 --- a/tests/testdata/provider/testdata_pg.sql +++ b/tests/testdata/provider/testdata_pg.sql @@ -671,3 +671,18 @@ CREATE VIEW qgis_test.b31799_test_view_ctid AS (SELECT ctid, geom, random() FROM CREATE VIEW qgis_test.b32523 AS SELECT pk, random() FROM qgis_test.some_poly_data; + +---------------------------------------------- +-- +-- IDENTITY pk +-- See https://github.com/qgis/QGIS/issues/29560 +-- + +CREATE TABLE qgis_test.b29560 ( + gid int8 NOT NULL GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + geom geometry(polygon) +); + +INSERT INTO qgis_test.b29560 (geom) +VALUES ('POLYGON EMPTY'::geometry); +