From 23dfb1de57bfa701f4ed2a9fe1b334497732c3ba Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Wed, 22 Nov 2017 13:02:15 +0100 Subject: [PATCH 1/2] [bugfix] Browser panel D&D a layer onto a postgresql connection tree node icon fails without notice Fixes #17518 by defaulting to "public" if no schema is given --- src/providers/postgres/qgspostgresprovider.cpp | 2 +- tests/src/python/test_provider_postgres.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/providers/postgres/qgspostgresprovider.cpp b/src/providers/postgres/qgspostgresprovider.cpp index 8380c4d1033..2a8f25dfd6b 100644 --- a/src/providers/postgres/qgspostgresprovider.cpp +++ b/src/providers/postgres/qgspostgresprovider.cpp @@ -3696,7 +3696,7 @@ QgsVectorLayerExporter::ExportError QgsPostgresProvider::createEmptyLayer( const { // populate members from the uri structure QgsDataSourceUri dsUri( uri ); - QString schemaName = dsUri.schema(); + QString schemaName = dsUri.schema().isEmpty() ? QStringLiteral( "public" ) : dsUri.schema(); QString tableName = dsUri.table(); QString geometryColumn = dsUri.geometryColumn(); diff --git a/tests/src/python/test_provider_postgres.py b/tests/src/python/test_provider_postgres.py index 2dcca63029d..97c131d1ccc 100644 --- a/tests/src/python/test_provider_postgres.py +++ b/tests/src/python/test_provider_postgres.py @@ -1,10 +1,14 @@ # -*- coding: utf-8 -*- """QGIS Unit tests for the postgres provider. +Note: to prepare the DB, you need to run the sql files specified in +tests/testdata/provider/testdata_pg.sh + .. note:: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. + """ from builtins import next __author__ = 'Matthias Kuhn' @@ -765,6 +769,20 @@ class TestPyQgsPostgresProvider(unittest.TestCase, ProviderTestCase): testKey(lyr, '"f1","F2","f3"', ['f1', 'F2', 'f3']) testKey(lyr, None, ['id']) + # See https://issues.qgis.org/issues/17518 + def testImportWithoutSchema(self): + self.execSQLCommand('DROP TABLE IF EXISTS b17518 CASCADE') + uri = 'point?field=f1:int' + uri += '&field=F2:double(6,4)' + uri += '&field=f3:string(20)' + lyr = QgsVectorLayer(uri, "x", "memory") + self.assertTrue(lyr.isValid()) + + uri = "%s sslmode=disable table=\"b17518\" (geom) sql" % self.dbconn + err = QgsVectorLayerExporter.exportLayer(lyr, uri, "postgres", lyr.crs()) + olyr = QgsVectorLayer(uri, "y", "postgres") + self.assertTrue(olyr.isValid()) + def testStyle(self): self.execSQLCommand('DROP TABLE IF EXISTS layer_styles CASCADE') From 9f56878d888334a86734872f20f32961c7cca027 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Wed, 22 Nov 2017 17:22:14 +0100 Subject: [PATCH 2/2] [bugfix] Add some more logic to get the schema name if empty --- .../postgres/qgspostgresprovider.cpp | 17 +++++++- tests/src/python/test_provider_postgres.py | 39 ++++++++++++++----- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/providers/postgres/qgspostgresprovider.cpp b/src/providers/postgres/qgspostgresprovider.cpp index 2a8f25dfd6b..61f1cb97e48 100644 --- a/src/providers/postgres/qgspostgresprovider.cpp +++ b/src/providers/postgres/qgspostgresprovider.cpp @@ -3696,7 +3696,8 @@ QgsVectorLayerExporter::ExportError QgsPostgresProvider::createEmptyLayer( const { // populate members from the uri structure QgsDataSourceUri dsUri( uri ); - QString schemaName = dsUri.schema().isEmpty() ? QStringLiteral( "public" ) : dsUri.schema(); + + QString schemaName = dsUri.schema(); QString tableName = dsUri.table(); QString geometryColumn = dsUri.geometryColumn(); @@ -3791,6 +3792,20 @@ QgsVectorLayerExporter::ExportError QgsPostgresProvider::createEmptyLayer( const { conn->PQexecNR( QStringLiteral( "BEGIN" ) ); + // We want a valid schema name ... + if ( schemaName.isEmpty() ) + { + QString sql = QString( "SELECT current_schema" ); + QgsPostgresResult result( conn->PQexec( sql ) ); + if ( result.PQresultStatus() != PGRES_TUPLES_OK ) + throw PGException( result ); + schemaName = result.PQgetvalue( 0, 0 ); + if ( schemaName.isEmpty() ) + { + schemaName = QStringLiteral( "public" ); + } + } + QString sql = QString( "SELECT 1" " FROM pg_class AS cls JOIN pg_namespace AS nsp" " ON nsp.oid=cls.relnamespace " diff --git a/tests/src/python/test_provider_postgres.py b/tests/src/python/test_provider_postgres.py index 97c131d1ccc..031bdf36b98 100644 --- a/tests/src/python/test_provider_postgres.py +++ b/tests/src/python/test_provider_postgres.py @@ -771,17 +771,36 @@ class TestPyQgsPostgresProvider(unittest.TestCase, ProviderTestCase): # See https://issues.qgis.org/issues/17518 def testImportWithoutSchema(self): - self.execSQLCommand('DROP TABLE IF EXISTS b17518 CASCADE') - uri = 'point?field=f1:int' - uri += '&field=F2:double(6,4)' - uri += '&field=f3:string(20)' - lyr = QgsVectorLayer(uri, "x", "memory") - self.assertTrue(lyr.isValid()) - uri = "%s sslmode=disable table=\"b17518\" (geom) sql" % self.dbconn - err = QgsVectorLayerExporter.exportLayer(lyr, uri, "postgres", lyr.crs()) - olyr = QgsVectorLayer(uri, "y", "postgres") - self.assertTrue(olyr.isValid()) + def _test(table, schema=None): + self.execSQLCommand('DROP TABLE IF EXISTS %s CASCADE' % table) + uri = 'point?field=f1:int' + uri += '&field=F2:double(6,4)' + uri += '&field=f3:string(20)' + lyr = QgsVectorLayer(uri, "x", "memory") + self.assertTrue(lyr.isValid()) + + table = ("%s" % table) if schema is None else ("\"%s\".\"%s\"" % (schema, table)) + dest_uri = "%s sslmode=disable table=%s (geom) sql" % (self.dbconn, table) + err = QgsVectorLayerExporter.exportLayer(lyr, dest_uri, "postgres", lyr.crs()) + olyr = QgsVectorLayer(dest_uri, "y", "postgres") + self.assertTrue(olyr.isValid(), "Failed URI: %s" % dest_uri) + + # Test bug 17518 + _test('b17518') + + # Test fully qualified table (with schema) + _test("b17518", "qgis_test") + + # Test empty schema + _test("b17518", "") + + # Test public schema + _test("b17518", "public") + + # Test fully qualified table (with wrong schema) + with self.assertRaises(AssertionError): + _test("b17518", "qgis_test_wrong") def testStyle(self): self.execSQLCommand('DROP TABLE IF EXISTS layer_styles CASCADE')