Skip to content

Commit

Permalink
Database: add constraint for unicity of CRS and operation names
Browse files Browse the repository at this point in the history
More exactly on projected_crs, vertical_crs, compound_crs,
helmert_transformation, grid_transformation, other_transformation and
concatenated_operation tables.

Bump database layout version to 1.4
  • Loading branch information
rouault committed Mar 7, 2024
1 parent d3296b7 commit 670db21
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 20 deletions.
2 changes: 1 addition & 1 deletion data/sql/metadata.sql
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
-- DATABASE_LAYOUT_VERSION_MINOR constants in src/iso19111/factory.cpp must be
-- updated as well.
INSERT INTO "metadata" VALUES('DATABASE.LAYOUT.VERSION.MAJOR', 1);
INSERT INTO "metadata" VALUES('DATABASE.LAYOUT.VERSION.MINOR', 3);
INSERT INTO "metadata" VALUES('DATABASE.LAYOUT.VERSION.MINOR', 4);

INSERT INTO "metadata" VALUES('EPSG.VERSION', 'v11.004');
INSERT INTO "metadata" VALUES('EPSG.DATE', '2024-02-24');
Expand Down
41 changes: 39 additions & 2 deletions data/sql/proj_db_table_defs.sql
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,11 @@ FOR EACH ROW BEGIN
SELECT RAISE(ABORT, 'insert on vertical_crs violates constraint: (auth_name, code) must not already exist in crs_view')
WHERE EXISTS (SELECT 1 FROM crs_view WHERE crs_view.auth_name = NEW.auth_name AND crs_view.code = NEW.code);

SELECT RAISE(ABORT, 'insert on vertical_crs violates constraint: name (of a non-deprecated entry) must not already exist in (a non-deprecated entry of) crs_view')
WHERE EXISTS (SELECT 1 FROM crs_view WHERE crs_view.name = NEW.name AND crs_view.deprecated = 0 AND NEW.deprecated = 0 AND NEW.auth_name != 'IGNF'
AND NOT(NEW.auth_name = 'ESRI' and crs_view.table_name = 'geodetic_crs') -- some ESRI vertical CRS are an ellipsoidal height CRS derived from a geodetic CRS
);

SELECT RAISE(ABORT, 'insert on vertical_crs violates constraint: datum must not be deprecated when vertical_crs is not deprecated')
WHERE EXISTS(SELECT 1 FROM vertical_crs datum WHERE datum.auth_name = NEW.datum_auth_name AND datum.code = NEW.datum_code AND datum.deprecated != 0) AND NEW.deprecated = 0;

Expand Down Expand Up @@ -723,6 +728,9 @@ FOR EACH ROW BEGIN
SELECT RAISE(ABORT, 'insert on projected_crs violates constraint: (auth_name, code) must not already exist in crs_view')
WHERE EXISTS (SELECT 1 FROM crs_view WHERE crs_view.auth_name = NEW.auth_name AND crs_view.code = NEW.code);

SELECT RAISE(ABORT, 'insert on projected_crs violates constraint: name (of a non-deprecated entry) must not already exist in (a non-deprecated entry of) crs_view')
WHERE EXISTS (SELECT 1 FROM crs_view WHERE crs_view.name = NEW.name AND crs_view.deprecated = 0 AND NEW.deprecated = 0 AND NEW.auth_name != 'IGNF' AND NEW.name != 'unknown');

SELECT RAISE(ABORT, 'insert on projected_crs violates constraint: geodetic_crs must not be deprecated when projected_crs is not deprecated')
WHERE EXISTS(SELECT 1 FROM geodetic_crs WHERE geodetic_crs.auth_name = NEW.geodetic_crs_auth_name AND geodetic_crs.code = NEW.geodetic_crs_code AND geodetic_crs.deprecated != 0 AND geodetic_crs.name NOT LIKE 'Unknown datum%' AND geodetic_crs.name NOT LIKE 'Unspecified datum%') AND NEW.deprecated = 0 AND NOT (NEW.auth_name = 'ESRI' AND NEW.geodetic_crs_auth_name != 'ESRI');

Expand Down Expand Up @@ -764,6 +772,9 @@ FOR EACH ROW BEGIN
SELECT RAISE(ABORT, 'insert on compound_crs violates constraint: (auth_name, code) must not already exist in crs_view')
WHERE EXISTS (SELECT 1 FROM crs_view WHERE crs_view.auth_name = NEW.auth_name AND crs_view.code = NEW.code);

SELECT RAISE(ABORT, 'insert on compound_crs violates constraint: name (of a non-deprecated entry) must not already exist in (a non-deprecated entry of) crs_view')
WHERE EXISTS (SELECT 1 FROM crs_view WHERE crs_view.name = NEW.name AND crs_view.deprecated = 0 AND NEW.deprecated = 0 AND NEW.auth_name != 'IGNF');

SELECT RAISE(ABORT, 'insert on compound_crs violates constraint: horiz_crs(auth_name, code) not found')
WHERE NOT EXISTS (SELECT 1 FROM crs_view WHERE crs_view.auth_name = NEW.horiz_crs_auth_name AND crs_view.code = NEW.horiz_crs_code);

Expand Down Expand Up @@ -1026,6 +1037,12 @@ FOR EACH ROW BEGIN
SELECT RAISE(ABORT, 'insert on helmert_transformation violates constraint: (auth_name, code) must not already exist in coordinate_operation_with_conversion_view')
WHERE EXISTS (SELECT 1 FROM coordinate_operation_with_conversion_view covwv WHERE covwv.auth_name = NEW.auth_name AND covwv.code = NEW.code);

SELECT RAISE(ABORT, 'insert on helmert_transformation violates constraint: name (of a non-deprecated entry) must not already exist in (a non-deprecated entry of) coordinate_operation_with_conversion_view')
WHERE EXISTS (SELECT 1 FROM coordinate_operation_with_conversion_view covwv WHERE covwv.name = NEW.name AND covwv.deprecated = 0 AND NEW.deprecated = 0 AND NEW.auth_name != 'IGNF'
AND NEW.name != 'NKG_ETRF00 to ETRF96@2000.0' -- NKG:P1_2008_EE and NKG:P1_2008_FI have the same name
AND NEW.name != 'NKG_ETRF14 to ETRF96@2000.0' -- NKG:PAR_2020_EE and NKG:PAR_2020_FI have the same name
);

SELECT RAISE(ABORT, 'insert on helmert_transformation violates constraint: translation_uom.type must be ''length''')
WHERE (SELECT type FROM unit_of_measure WHERE unit_of_measure.auth_name = NEW.translation_uom_auth_name AND unit_of_measure.code = NEW.translation_uom_code) != 'length';
SELECT RAISE(ABORT, 'insert on helmert_transformation violates constraint: rotation_uom.type must be ''angle''')
Expand Down Expand Up @@ -1096,6 +1113,13 @@ FOR EACH ROW BEGIN
SELECT RAISE(ABORT, 'insert on grid_transformation violates constraint: (auth_name, code) must not already exist in coordinate_operation_with_conversion_view')
WHERE EXISTS (SELECT 1 FROM coordinate_operation_with_conversion_view covwv WHERE covwv.auth_name = NEW.auth_name AND covwv.code = NEW.code);

SELECT RAISE(ABORT, 'insert on grid_transformation violates constraint: name (of a non-deprecated entry) must not already exist in (a non-deprecated entry of) coordinate_operation_with_conversion_view')
WHERE EXISTS (SELECT 1 FROM coordinate_operation_with_conversion_view covwv WHERE covwv.name = NEW.name AND covwv.deprecated = 0 AND NEW.deprecated = 0 AND NEW.auth_name != 'IGNF'
AND NEW.name != 'NAD83(CSRS)v2 to NAD83(CSRS)v3 (1)' -- duplicate entry in EPSG
AND NOT (NEW.description LIKE 'Reversible alternative to%' AND covwv.description NOT LIKE 'Reversible alternative to%')
AND NEW.code NOT LIKE '%_WITH_NAD83CSRSV7_INTERPOLATION'
);

SELECT RAISE(ABORT, 'insert on grid_transformation violates constraint: source_crs(auth_name, code) not found')
WHERE NOT EXISTS (SELECT 1 FROM crs_view WHERE crs_view.auth_name = NEW.source_crs_auth_name AND crs_view.code = NEW.source_crs_code);

Expand Down Expand Up @@ -1266,6 +1290,16 @@ FOR EACH ROW BEGIN
SELECT RAISE(ABORT, 'insert on other_transformation violates constraint: (auth_name, code) must not already exist in coordinate_operation_with_conversion_view')
WHERE EXISTS (SELECT 1 FROM coordinate_operation_with_conversion_view covwv WHERE covwv.auth_name = NEW.auth_name AND covwv.code = NEW.code);

SELECT RAISE(ABORT, 'insert on other_transformation violates constraint: name (of a non-deprecated entry) must not already exist in (a non-deprecated entry of) coordinate_operation_with_conversion_view')
WHERE EXISTS (SELECT 1 FROM coordinate_operation_with_conversion_view covwv WHERE covwv.name = NEW.name AND covwv.deprecated = 0 AND NEW.deprecated = 0 AND NEW.auth_name != 'IGNF'
AND NEW.name != 'NKG_ETRF14 to ETRF93@2000.0' -- NKG:PAR_2020_NO and NKG:NKG_ETRF14_ETRF93_2000 have the same name
AND NEW.name != 'ETRF96@2000.0 to ETRF96@1997.56' -- NKG:ETRF96_2000_TO_ETRF96_1997_56 and NKG:EE_2020_INTRAPLATE have the same name
AND NEW.name != 'ETRF93@2000.0 to ETRF93@1995.0' -- NKG:ETRF93_2000_TO_ETRF93_1995 and NKG:NO_2020_INTRAPLATE have the same name
AND NEW.name != 'ETRF92@2000.0 to ETRF92@1994.704' -- NKG:ETRF92_2000_TO_ETRF92_1994 and NKG:DK_2020_INTRAPLATE have the same name
AND NEW.name != 'ETRF96@2000.0 to ETRF96@1997.0' -- NKG:ETRF96_2000_TO_ETRF96_1997 AND NKG:FI_2020_INTRAPLATE have the same name
AND NEW.name != 'ETRF97@2000.0 to ETRF97@1999.5' -- NKG:ETRF97_2000_TO_ETRF97_1999 and NKG:SE_2020_INTRAPLATE have the same name
);

SELECT RAISE(ABORT, 'insert on other_transformation violates constraint: source_crs(auth_name, code) not found')
WHERE NOT EXISTS (SELECT 1 FROM crs_view WHERE crs_view.auth_name = NEW.source_crs_auth_name AND crs_view.code = NEW.source_crs_code);

Expand Down Expand Up @@ -1311,6 +1345,9 @@ FOR EACH ROW BEGIN
SELECT RAISE(ABORT, 'insert on concatenated_operation violates constraint: (auth_name, code) must not already exist in coordinate_operation_with_conversion_view')
WHERE EXISTS (SELECT 1 FROM coordinate_operation_with_conversion_view covwv WHERE covwv.auth_name = NEW.auth_name AND covwv.code = NEW.code);

SELECT RAISE(ABORT, 'insert on concatenated_operation violates constraint: name (of a non-deprecated entry) must not already exist in (a non-deprecated entry of) coordinate_operation_with_conversion_view')
WHERE EXISTS (SELECT 1 FROM coordinate_operation_with_conversion_view covwv WHERE covwv.name = NEW.name AND covwv.deprecated = 0 AND NEW.deprecated = 0 AND new.auth_name != 'IGNF');

SELECT RAISE(ABORT, 'insert on concatenated_operation violates constraint: source_crs(auth_name, code) not found')
WHERE NOT EXISTS (SELECT 1 FROM crs_view WHERE crs_view.auth_name = NEW.source_crs_auth_name AND crs_view.code = NEW.source_crs_code);

Expand Down Expand Up @@ -1469,8 +1506,8 @@ CREATE VIEW coordinate_operation_view AS
;

CREATE VIEW coordinate_operation_with_conversion_view AS
SELECT auth_name, code, table_name AS type FROM coordinate_operation_view UNION ALL
SELECT auth_name, code, CAST('conversion' AS TEXT) FROM conversion_table;
SELECT auth_name, code, name, description, table_name AS type, deprecated FROM coordinate_operation_view UNION ALL
SELECT auth_name, code, name, description, CAST('conversion' AS TEXT) AS type, deprecated FROM conversion_table;

CREATE VIEW crs_view AS
SELECT CAST('geodetic_crs' AS TEXT) AS table_name, auth_name, code, name, type,
Expand Down
2 changes: 1 addition & 1 deletion src/iso19111/factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ constexpr const char *CS_TYPE_ORDINAL = cs::OrdinalCS::WKT2_TYPE;
constexpr int DATABASE_LAYOUT_VERSION_MAJOR = 1;
// If the code depends on the new additions, then DATABASE_LAYOUT_VERSION_MINOR
// must be incremented.
constexpr int DATABASE_LAYOUT_VERSION_MINOR = 3;
constexpr int DATABASE_LAYOUT_VERSION_MINOR = 4;

constexpr size_t N_MAX_PARAMS = 7;

Expand Down
44 changes: 28 additions & 16 deletions test/unit/test_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1898,7 +1898,7 @@ class FactoryWithTmpDatabase : public ::testing::Test {

ASSERT_TRUE(execute(
"INSERT INTO helmert_transformation "
"VALUES('EPSG','DUMMY_HELMERT','name',NULL,'EPSG','9603','"
"VALUES('EPSG','DUMMY_HELMERT','dummy_helmert',NULL,'EPSG','9603','"
"Geocentric translations (geog2D domain)','EPSG','4326',"
"'EPSG','4326',44.0,-143."
"0,-90.0,-294.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,"
Expand All @@ -1914,7 +1914,8 @@ class FactoryWithTmpDatabase : public ::testing::Test {

ASSERT_TRUE(execute(
"INSERT INTO grid_transformation "
"VALUES('EPSG','DUMMY_GRID_TRANSFORMATION','name',NULL,"
"VALUES('EPSG','DUMMY_GRID_TRANSFORMATION',"
"'dummy_grid_transformation',NULL,"
"'EPSG','9615'"
",'NTv2','EPSG','4326','EPSG','4326',1.0,'EPSG','"
"8656','Latitude and longitude difference "
Expand All @@ -1936,7 +1937,8 @@ class FactoryWithTmpDatabase : public ::testing::Test {

ASSERT_TRUE(execute(
"INSERT INTO other_transformation "
"VALUES('EPSG','DUMMY_OTHER_TRANSFORMATION','name',NULL,"
"VALUES('EPSG','DUMMY_OTHER_TRANSFORMATION',"
"'dummy_other_transformation',NULL,"
"'EPSG','9601','Longitude rotation',"
"'EPSG','4326','EPSG','4326',0.0,'EPSG'"
",'8602','Longitude "
Expand All @@ -1954,7 +1956,8 @@ class FactoryWithTmpDatabase : public ::testing::Test {
<< last_error();

ASSERT_TRUE(execute("INSERT INTO concatenated_operation "
"VALUES('EPSG','DUMMY_CONCATENATED','name',NULL,"
"VALUES('EPSG','DUMMY_CONCATENATED',"
"'dummy_concatenated',NULL,"
"'EPSG','4326','EPSG'"
",'4326',NULL,NULL,0);"))
<< last_error();
Expand Down Expand Up @@ -2381,7 +2384,8 @@ TEST_F(FactoryWithTmpDatabase,
<< last_error();
ASSERT_TRUE(
execute("INSERT INTO other_transformation "
"VALUES('EPSG','4326_TO_OTHER_GEOG_CRS','name',NULL,"
"VALUES('EPSG','4326_TO_OTHER_GEOG_CRS',"
"'4326_to_other_geog_crs',NULL,"
"'EPSG','9601','Longitude rotation',"
"'EPSG','4326','EPSG','OTHER_GEOG_CRS',0.0,'EPSG'"
",'8602','Longitude "
Expand All @@ -2392,7 +2396,8 @@ TEST_F(FactoryWithTmpDatabase,
<< last_error();
ASSERT_TRUE(
execute("INSERT INTO other_transformation "
"VALUES('EPSG','OTHER_GEOG_CRS_TO_4326','name',NULL,"
"VALUES('EPSG','OTHER_GEOG_CRS_TO_4326',"
"'other_geog_crs_to_4326',NULL,"
"'EPSG','9601','Longitude rotation',"
"'EPSG','OTHER_GEOG_CRS','EPSG','4326',0.0,'EPSG'"
",'8602','Longitude "
Expand All @@ -2402,7 +2407,8 @@ TEST_F(FactoryWithTmpDatabase,
"NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);"))
<< last_error();
ASSERT_TRUE(execute("INSERT INTO concatenated_operation "
"VALUES('EPSG','DUMMY_CONCATENATED_2','name',NULL,"
"VALUES('EPSG','DUMMY_CONCATENATED_2',"
"'dummy_concatenated_2',NULL,"
"'EPSG','4326','EPSG'"
",'4326',NULL,NULL,0);"))
<< last_error();
Expand Down Expand Up @@ -2441,7 +2447,7 @@ TEST_F(FactoryWithTmpDatabase,
<< last_error();

ASSERT_TRUE(execute("INSERT INTO projected_crs "
"VALUES('OTHER','OTHER_32631','WGS 84 / UTM zone "
"VALUES('OTHER','OTHER_32631','my WGS 84 / UTM zone "
"31N',NULL,'EPSG','4400','OTHER','OTHER_4326',"
"'EPSG','16031',NULL,0);"))
<< last_error();
Expand Down Expand Up @@ -2478,7 +2484,8 @@ TEST_F(FactoryWithTmpDatabase,

ASSERT_TRUE(execute(
"INSERT INTO grid_transformation "
"VALUES('OTHER','OTHER_GRID_TRANSFORMATION','name',NULL,"
"VALUES('OTHER','OTHER_GRID_TRANSFORMATION',"
"'other_grid_transformation_2',NULL,"
"'EPSG','9615'"
",'NTv2','EPSG','4326','OTHER','OTHER_4326',1.0,'EPSG','"
"8656','Latitude and longitude difference "
Expand Down Expand Up @@ -3035,19 +3042,22 @@ TEST_F(FactoryWithTmpDatabase, custom_projected_crs) {
populateWithFakeEPSG();

ASSERT_TRUE(execute("INSERT INTO projected_crs "
"VALUES('TEST_NS','TEST','my name',NULL,NULL,"
"VALUES('TEST_NS','TEST',"
"'custom_projected_crs',NULL,NULL,"
"NULL,NULL,NULL,NULL,NULL,"
"'+proj=mbt_s +unused_flag',0);"))
<< last_error();

ASSERT_TRUE(execute("INSERT INTO projected_crs "
"VALUES('TEST_NS','TEST_BOUND','my name',NULL,"
"VALUES('TEST_NS','TEST_BOUND',"
"'custom_projected_crs2',NULL,"
"NULL,NULL,NULL,NULL,NULL,NULL,"
"'+proj=mbt_s +unused_flag +towgs84=1,2,3',0);"))
<< last_error();

ASSERT_TRUE(execute("INSERT INTO projected_crs "
"VALUES('TEST_NS','TEST_WRONG','my name',NULL,"
"VALUES('TEST_NS','TEST_WRONG',"
"'custom_projected_crs3',NULL,"
"NULL,NULL,NULL,NULL,NULL,NULL,"
"'+proj=longlat',0);"))
<< last_error();
Expand Down Expand Up @@ -3097,7 +3107,7 @@ TEST_F(FactoryWithTmpDatabase, custom_projected_crs) {
AuthorityFactory::create(DatabaseContext::create(m_ctxt), "TEST_NS");
{
auto crs = factory->createProjectedCRS("TEST");
EXPECT_EQ(*(crs->name()->description()), "my name");
EXPECT_EQ(*(crs->name()->description()), "custom_projected_crs");
EXPECT_EQ(crs->identifiers().size(), 1U);
EXPECT_EQ(crs->derivingConversion()->targetCRS().get(), crs.get());
EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()),
Expand All @@ -3106,7 +3116,7 @@ TEST_F(FactoryWithTmpDatabase, custom_projected_crs) {
}
{
auto crs = factory->createProjectedCRS("TEST_BOUND");
EXPECT_EQ(*(crs->name()->description()), "my name");
EXPECT_EQ(*(crs->name()->description()), "custom_projected_crs2");
EXPECT_EQ(crs->identifiers().size(), 1U);
EXPECT_EQ(crs->derivingConversion()->targetCRS().get(), crs.get());
EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()),
Expand Down Expand Up @@ -3487,7 +3497,8 @@ TEST_F(FactoryWithTmpDatabase,

ASSERT_TRUE(execute(
"INSERT INTO other_transformation "
"VALUES('EPSG','NOOP_TRANSFORMATION_32631','name',NULL,"
"VALUES('EPSG','NOOP_TRANSFORMATION_32631',"
"'NOOP_TRANSFORMATION_32631',NULL,"
"'PROJ','PROJString','+proj=noop',"
"'EPSG','32631','EPSG','32631',0.0,"
"NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,"
Expand All @@ -3506,7 +3517,8 @@ TEST_F(FactoryWithTmpDatabase,

ASSERT_TRUE(execute(
"INSERT INTO other_transformation "
"VALUES('EPSG','NOOP_TRANSFORMATION_4326','name',NULL,"
"VALUES('EPSG','NOOP_TRANSFORMATION_4326',"
"'NOOP_TRANSFORMATION_4326',NULL,"
"'PROJ','PROJString','+proj=noop',"
"'EPSG','4326','EPSG','4326',0.0,"
"NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,"
Expand Down

0 comments on commit 670db21

Please sign in to comment.