diff --git a/python/plugins/db_manager/db_manager.py b/python/plugins/db_manager/db_manager.py index ee597028dbc..f1539964621 100644 --- a/python/plugins/db_manager/db_manager.py +++ b/python/plugins/db_manager/db_manager.py @@ -88,6 +88,8 @@ class DBManager(QMainWindow): with OverrideCursor(Qt.WaitCursor): try: self.reloadButtons() + # Force-reload information on the layer + self.info.setDirty() # clear preview, this will delete the layer in preview tab self.preview.loadPreview(None) self.refreshTabs() diff --git a/python/plugins/db_manager/db_plugins/gpkg/connector.py b/python/plugins/db_manager/db_plugins/gpkg/connector.py index 3fe36d67bd2..a73af66ed37 100644 --- a/python/plugins/db_manager/db_plugins/gpkg/connector.py +++ b/python/plugins/db_manager/db_plugins/gpkg/connector.py @@ -30,6 +30,8 @@ from ..connector import DBConnector from ..plugin import ConnectionError, DbError, Table from qgis.utils import spatialite_connect +from qgis.core import QgsApplication + import sqlite3 from osgeo import gdal, ogr, osr @@ -590,28 +592,26 @@ class GPKGDBConnector(DBConnector): self._execute_and_commit(sql) def renameTable(self, table, new_table): - """ rename a table """ + """Renames the table - if self.isRasterTable(table): - return False + :param table: tuple with schema and table names + :type table: tuple (str, str) + :param new_table: new table name + :type new_table: str + :return: true on success + :rtype: bool + """ - _, tablename = self.getSchemaTableName(table) - if new_table == tablename: - return True - - if tablename.find('"') >= 0: - tablename = self.quoteId(tablename) - if new_table.find('"') >= 0: - new_table = self.quoteId(new_table) - - gdal.ErrorReset() - self.gdal_ds.ExecuteSQL('ALTER TABLE %s RENAME TO %s' % (tablename, new_table)) - if gdal.GetLastErrorMsg() != '': - return False + table_name = table[1] + provider = [p for p in QgsApplication.dataItemProviderRegistry().providers() if p.name() == 'OGR'][0] + collection_item = provider.createDataItem(self.dbname, None) + data_item = [c for c in collection_item.createChildren() if c.name() == table_name][0] + result = data_item.rename(new_table) # we need to reopen after renaming since OGR doesn't update its # internal state - self._opendb() - return True + if result: + self._opendb() + return result def moveTable(self, table, new_table, new_schema=None): return self.renameTable(table, new_table) diff --git a/python/plugins/db_manager/db_plugins/plugin.py b/python/plugins/db_manager/db_plugins/plugin.py index b5a081f9bb2..8ec34f1ce8e 100644 --- a/python/plugins/db_manager/db_plugins/plugin.py +++ b/python/plugins/db_manager/db_plugins/plugin.py @@ -594,6 +594,7 @@ class Schema(DbItemObject): ret = self.database().connector.renameSchema(self.name, new_name) if ret is not False: self.name = new_name + # FIXME: refresh triggers self.refresh() return ret @@ -652,6 +653,9 @@ class Table(DbItemObject): ret = self.database().connector.renameTable((self.schemaName(), self.name), new_name) if ret is not False: self.name = new_name + self._triggers = None + self._rules = None + self._constraints = None self.refresh() return ret diff --git a/python/plugins/db_manager/layer_preview.py b/python/plugins/db_manager/layer_preview.py index 419d787763a..9b9ea8c87b4 100644 --- a/python/plugins/db_manager/layer_preview.py +++ b/python/plugins/db_manager/layer_preview.py @@ -40,7 +40,7 @@ class LayerPreview(QgsMapCanvas): self.item = None self.dirty = False - self.currentLayer = None + self.currentLayerId = None # reuse settings from QGIS settings = QgsSettings() @@ -118,9 +118,9 @@ class LayerPreview(QgsMapCanvas): vl = None # remove old layer (if any) and set new - if self.currentLayer: - if not QgsProject.instance().layerTreeRoot().findLayer(self.currentLayer.id()): - QgsProject.instance().removeMapLayers([self.currentLayer.id()]) + if self.currentLayerId: + if not QgsProject.instance().layerTreeRoot().findLayer(self.currentLayerId): + QgsProject.instance().removeMapLayers([self.currentLayerId]) if vl and vl.isValid(): self.setLayers([vl]) @@ -129,7 +129,7 @@ class LayerPreview(QgsMapCanvas): else: self.setLayers([]) - self.currentLayer = vl + self.currentLayerId = vl.id() self.freeze(False) super().refresh() diff --git a/src/providers/ogr/CMakeLists.txt b/src/providers/ogr/CMakeLists.txt index bb3f8345b0b..ccd08b1c7f4 100644 --- a/src/providers/ogr/CMakeLists.txt +++ b/src/providers/ogr/CMakeLists.txt @@ -64,18 +64,26 @@ INCLUDE_DIRECTORIES(SYSTEM ADD_LIBRARY(ogrprovider MODULE ${OGR_SRCS} ${OGR_MOC_SRCS}) +ADD_LIBRARY(ogrprovider_a STATIC ${OGR_SRCS} ${OGR_MOC_SRCS}) TARGET_LINK_LIBRARIES(ogrprovider qgis_core ) +TARGET_LINK_LIBRARIES(ogrprovider_a + qgis_core +) IF (WITH_GUI) TARGET_LINK_LIBRARIES (ogrprovider qgis_gui ) + TARGET_LINK_LIBRARIES (ogrprovider_a + qgis_gui + ) ENDIF () + IF (MSVC) #needed for linking to gdal which needs odbc SET(TARGET_LINK_LIBRARIES ${TARGET_LINK_LIBRARIE} odbc32 odbccp32) diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index fb07b9c9d2b..4ff99a472b1 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -41,6 +41,7 @@ #include "qgstaskmanager.h" #include "qgsproviderregistry.h" #include "qgsproxyprogresstask.h" +#include "qgssqliteutils.h" QGISEXTERN bool deleteLayer( const QString &uri, const QString &errCause ); @@ -887,12 +888,23 @@ bool QgsGeoPackageVectorLayerItem::rename( const QString &name ) GDALDatasetH hDS = GDALOpenEx( filePath.toUtf8().constData(), GDAL_OF_VECTOR | GDAL_OF_UPDATE, nullptr, nullptr, nullptr ); if ( hDS ) { - QString sql( QStringLiteral( "ALTER TABLE \"%1\" RENAME TO \"%2\"" ).arg( oldName, name ) ); + QString sql( QStringLiteral( "ALTER TABLE %1 RENAME TO %2" ) + .arg( QgsSqliteUtils::quotedIdentifier( oldName ), + QgsSqliteUtils::quotedIdentifier( name ) ) ); OGRLayerH ogrLayer( GDALDatasetExecuteSQL( hDS, sql.toUtf8().constData(), nullptr, nullptr ) ); if ( ogrLayer ) GDALDatasetReleaseResultSet( hDS, ogrLayer ); - GDALClose( hDS ); errCause = CPLGetLastErrorMsg( ); + if ( errCause.isEmpty() ) + { + sql = QStringLiteral( "UPDATE layer_styles SET f_table_name = %2 WHERE f_table_name = %1" ) + .arg( QgsSqliteUtils::quotedString( oldName ), + QgsSqliteUtils::quotedString( name ) ); + ogrLayer = GDALDatasetExecuteSQL( hDS, sql.toUtf8().constData(), nullptr, nullptr ); + if ( ogrLayer ) + GDALDatasetReleaseResultSet( hDS, ogrLayer ); + } + GDALClose( hDS ); } else { diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp index c0adcbe473e..7855e2dc9c6 100644 --- a/src/providers/ogr/qgsogrprovider.cpp +++ b/src/providers/ogr/qgsogrprovider.cpp @@ -6574,4 +6574,3 @@ QGISEXTERN QgsTransaction *createTransaction( const QString &connString ) return new QgsOgrTransaction( connString, ds ); } - diff --git a/tests/src/providers/CMakeLists.txt b/tests/src/providers/CMakeLists.txt index 404bb18b530..2d55c0901ac 100644 --- a/tests/src/providers/CMakeLists.txt +++ b/tests/src/providers/CMakeLists.txt @@ -17,6 +17,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src/providers/postgres ${CMAKE_SOURCE_DIR}/src/providers/arcgisrest ${CMAKE_SOURCE_DIR}/src/providers/mdal + ${CMAKE_SOURCE_DIR}/src/providers/ogr ${CMAKE_SOURCE_DIR}/src/test ${CMAKE_BINARY_DIR}/src/core ) @@ -74,6 +75,7 @@ ADD_QGIS_TEST(gdalprovidertest testqgsgdalprovider.cpp) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") ADD_QGIS_TEST(ogrprovidertest testqgsogrprovider.cpp) +TARGET_LINK_LIBRARIES(qgis_ogrprovidertest ogrprovider_a) ADD_QGIS_TEST(wmscapabilitiestest testqgswmscapabilities.cpp) diff --git a/tests/src/providers/testqgsogrprovider.cpp b/tests/src/providers/testqgsogrprovider.cpp index ebf9bfeedde..5a529fb4b3a 100644 --- a/tests/src/providers/testqgsogrprovider.cpp +++ b/tests/src/providers/testqgsogrprovider.cpp @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include @@ -47,6 +49,8 @@ class TestQgsOgrProvider : public QObject void setupProxy(); void decodeUri(); void testThread(); + //! Test GPKG data items rename + void testGpkgDataItemRename(); private: QString mTestDataDir; @@ -213,6 +217,37 @@ void TestQgsOgrProvider::testThread() } +void TestQgsOgrProvider::testGpkgDataItemRename() +{ + QTemporaryFile f( QStringLiteral( "qgis-XXXXXX.gpkg" ) ); + f.open(); + f.close(); + QString fileName { f.fileName( ) }; + f.remove(); + QVERIFY( QFile::copy( QStringLiteral( "%1/provider/bug_21227-rename-styles.gpkg" ).arg( mTestDataDir ), fileName ) ); + QgsGeoPackageVectorLayerItem item( nullptr, + QStringLiteral( "Layer 1" ), + QStringLiteral( "gpkg:/%1|layername=layer 1" ) + .arg( fileName ), + QStringLiteral( "%1|layername=layer 1" ).arg( fileName ), + QgsLayerItem::LayerType::TableLayer ); + item.rename( "layer 3" ); + // Check that the style is still available + QgsVectorLayer metadataLayer( QStringLiteral( "/%1|layername=layer_styles" ).arg( fileName ) ); + QVERIFY( metadataLayer.isValid() ); + QgsFeature feature; + QgsFeatureIterator it = metadataLayer.getFeatures( QgsFeatureRequest( QgsExpression( QStringLiteral( "\"f_table_name\" = 'layer 3'" ) ) ) ); + QVERIFY( it.nextFeature( feature ) ); + QVERIFY( feature.isValid() ); + QCOMPARE( feature.attribute( QStringLiteral( "styleName" ) ).toString(), QString( "style for layer 1" ) ); + it = metadataLayer.getFeatures( QgsFeatureRequest( QgsExpression( QStringLiteral( "\"f_table_name\" = 'layer 1' " ) ) ) ); + QVERIFY( !it.nextFeature( feature ) ); + it = metadataLayer.getFeatures( QgsFeatureRequest( QgsExpression( QStringLiteral( "\"f_table_name\" = 'layer 2' " ) ) ) ); + QVERIFY( it.nextFeature( feature ) ); + QVERIFY( feature.isValid() ); + QCOMPARE( feature.attribute( QStringLiteral( "styleName" ) ).toString(), QString( "style for layer 2" ) ); +} + QGSTEST_MAIN( TestQgsOgrProvider ) #include "testqgsogrprovider.moc" diff --git a/tests/src/python/test_provider_ogr_gpkg.py b/tests/src/python/test_provider_ogr_gpkg.py index a82bb0cc3bc..3e4abde6459 100644 --- a/tests/src/python/test_provider_ogr_gpkg.py +++ b/tests/src/python/test_provider_ogr_gpkg.py @@ -1296,8 +1296,8 @@ class TestPyQgsOGRProviderGpkg(unittest.TestCase): def test_quote_identifier(self): """Regression #21100""" - tmpfile = os.path.join(self.basetestpath, 'bug21100-wierd_field_names.gpkg') # spellok - shutil.copy(os.path.join(unitTestDataPath(''), 'bug21100-wierd_field_names.gpkg'), tmpfile) # spellok + tmpfile = os.path.join(self.basetestpath, 'bug_21100-wierd_field_names.gpkg') # spellok + shutil.copy(os.path.join(unitTestDataPath(''), 'bug_21100-wierd_field_names.gpkg'), tmpfile) # spellok vl = QgsVectorLayer('{}|layerid=0'.format(tmpfile), 'foo', 'ogr') self.assertTrue(vl.isValid()) for i in range(1, len(vl.fields())): diff --git a/tests/testdata/bug21100-wierd_field_names.gpkg b/tests/testdata/bug_21100-wierd_field_names.gpkg similarity index 100% rename from tests/testdata/bug21100-wierd_field_names.gpkg rename to tests/testdata/bug_21100-wierd_field_names.gpkg diff --git a/tests/testdata/provider/bug_21227-rename-styles.gpkg b/tests/testdata/provider/bug_21227-rename-styles.gpkg new file mode 100644 index 00000000000..1c4bea247a7 Binary files /dev/null and b/tests/testdata/provider/bug_21227-rename-styles.gpkg differ