Skip to content

Commit

Permalink
qgspostgresprovider: Fix primary key retrieval with lowercase option
Browse files Browse the repository at this point in the history
The `lowercaseFieldNames` allows to transform the column names to
lowercase. If this option is enabled, then the requested primary
key (`primaryKey`) is in lowercase but the `fields` are
not. Therefore, the requested primary key will never be found.

This issues is fixed by converting the field names to lowercase when
looking for the primary key if the option is enabled.

Closes: qgis#55856
  • Loading branch information
ptitjano committed Feb 7, 2024
1 parent b6c7889 commit dc3d145
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 1 deletion.
4 changes: 3 additions & 1 deletion src/providers/postgres/qgspostgresprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4609,13 +4609,15 @@ Qgis::VectorExportResult QgsPostgresProvider::createEmptyLayer( const QString &u
{
pkList = parseUriKey( primaryKey );
const auto constPkList = pkList;
const bool lowercaseFieldNames = options && options->value( QStringLiteral( "lowercaseFieldNames" ), false ).toBool();
for ( const QString &col : constPkList )
{
// search for the passed field
QString type;
for ( int fldIdx = 0; fldIdx < fields.count(); ++fldIdx )
{
if ( fields[fldIdx].name() == col )
const QString fieldName = lowercaseFieldNames ? fields[fldIdx].name().toLower() : fields[fldIdx].name();
if ( fieldName == col )
{
// found, get the field type
QgsField fld = fields[fldIdx];
Expand Down
44 changes: 44 additions & 0 deletions tests/src/python/test_provider_postgres.py
Original file line number Diff line number Diff line change
Expand Up @@ -3266,6 +3266,50 @@ def testGeographyAddFeature(self):
self.assertTrue(dp.addFeature(f))
self.assertEqual(vl.featureCount(), 1)

# See: https://github.com/qgis/QGIS/issues/55856
def testPktLowerCase(self):
# check that primary key creation correctly works
# when exporting a vector layer to postgresql with
# lowercaseFieldNames option set to True

# create an empty vector layer
pk_key = "DEP"
input_uri = f"NoGeometry?crs=&field={pk_key}:string(255,0)&field=REG:string(255,0)&field=Number:integer(10,0)"
layer = QgsVectorLayer(input_uri, "lowercase", "memory")
self.assertTrue(layer.isValid())

# export the vector layer to postgresql with lowercase field names
self.execSQLCommand('DROP TABLE IF EXISTS qgis_test.pk_lowercase')
output_uri = f'{self.dbconn} table="qgis_test"."pk_lowercase" key=\'{pk_key.lower()}\''
err = QgsVectorLayerExporter.exportLayer(layer, output_uri, "postgres", layer.crs(), False, {'lowercaseFieldNames': True})
self.assertEqual(err[0], QgsVectorLayerExporter.ExportError.NoError,
f'unexpected import error {err}')

# retrieve the columns and type and check them
cur = self.con.cursor()
sql_cols = (
"SELECT column_name, data_type FROM information_schema.columns "
"WHERE table_name = 'pk_lowercase' AND table_schema = 'qgis_test';"
)
cur.execute(sql_cols)
expected_cols = [
('dep', 'character varying'),
('reg', 'character varying'),
('number', 'integer')]
self.assertEqual(cur.fetchall(), expected_cols)

# Retrieve the primary key and check its name and type
sql_pk = (
"SELECT a.attname, format_type(a.atttypid, a.atttypmod) AS data_type "
"FROM pg_index i "
"JOIN pg_attribute a ON a.attrelid = i.indrelid "
"AND a.attnum = ANY(i.indkey) "
"WHERE i.indrelid = 'qgis_test.pk_lowercase'::regclass "
"AND i.indisprimary;"
)
cur.execute(sql_pk)
self.assertEqual(cur.fetchall(), [('dep', 'character varying')])


class TestPyQgsPostgresProviderCompoundKey(QgisTestCase, ProviderTestCase):

Expand Down

0 comments on commit dc3d145

Please sign in to comment.