Skip to content

Database: add constraint for unicity of CRS and operation names #4071

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions data/sql/commit.sql
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ FOR EACH ROW BEGIN
SELECT RAISE(ABORT, 'corrupt definition of authority_list')
WHERE (SELECT 1 FROM authority_list LIMIT 1) = 0;

-- check that the auth_name of all objects in object_view is recorded in builtin_authorities
SELECT RAISE(ABORT, 'One or several authorities referenced in object_view are missing in builtin_authorities')
WHERE EXISTS (
SELECT DISTINCT o.auth_name FROM object_view o WHERE NOT EXISTS (
SELECT 1 FROM builtin_authorities b WHERE o.auth_name = b.auth_name)
);

-- check that a usage is registered for most objects where this is needed
SELECT RAISE(ABORT, 'One or several objects lack a corresponding record in the usage table')
WHERE EXISTS (
Expand Down
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
66 changes: 64 additions & 2 deletions data/sql/proj_db_table_defs.sql
Original file line number Diff line number Diff line change
Expand Up @@ -306,13 +306,33 @@ CREATE TABLE vertical_crs(
CONSTRAINT fk_vertical_crs_datum FOREIGN KEY (datum_auth_name, datum_code) REFERENCES vertical_datum(auth_name, code) ON DELETE CASCADE
) WITHOUT ROWID;

-- Authorities provided by the upstream PROJ
-- This is used to check unicity of object names
CREATE TABLE builtin_authorities(auth_name TEXT NOT NULL PRIMARY KEY) WITHOUT ROWID;
INSERT INTO builtin_authorities VALUES
('EPSG'),
('ESRI'),
('IAU_2015'),
('IGNF'),
('NKG'),
('NRCAN'),
('OGC'),
('PROJ')
;

CREATE TRIGGER vertical_crs_insert_trigger
BEFORE INSERT ON vertical_crs
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 IN (SELECT auth_name FROM builtin_authorities WHERE 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 +743,11 @@ 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 IN (SELECT auth_name FROM builtin_authorities WHERE auth_name != 'IGNF')
);

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 +789,11 @@ 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 IN (SELECT auth_name FROM builtin_authorities WHERE 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 +1056,13 @@ 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 IN (SELECT auth_name FROM builtin_authorities WHERE auth_name != 'IGNF')
AND NEW.name != 'NKG_ETRF00 to [email protected]' -- NKG:P1_2008_EE and NKG:P1_2008_FI have the same name
AND NEW.name != 'NKG_ETRF14 to [email protected]' -- 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 +1133,15 @@ 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 IN (SELECT auth_name FROM builtin_authorities WHERE auth_name != 'IGNF')
AND NEW.name != 'NAD83(CSRS)v2 to NAD83(CSRS)v3 (1)' -- duplicate entry in EPSG
AND NEW.name != 'ETRS89 to ETRS89 + Baltic 1957 height (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 +1312,17 @@ 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 IN (SELECT auth_name FROM builtin_authorities WHERE auth_name != 'IGNF')
AND NEW.name != 'NKG_ETRF14 to [email protected]' -- NKG:PAR_2020_NO and NKG:NKG_ETRF14_ETRF93_2000 have the same name
AND NEW.name != '[email protected] to [email protected]' -- NKG:ETRF96_2000_TO_ETRF96_1997_56 and NKG:EE_2020_INTRAPLATE have the same name
AND NEW.name != '[email protected] to [email protected]' -- NKG:ETRF93_2000_TO_ETRF93_1995 and NKG:NO_2020_INTRAPLATE have the same name
AND NEW.name != '[email protected] to [email protected]' -- NKG:ETRF92_2000_TO_ETRF92_1994 and NKG:DK_2020_INTRAPLATE have the same name
AND NEW.name != '[email protected] to [email protected]' -- NKG:ETRF96_2000_TO_ETRF96_1997 AND NKG:FI_2020_INTRAPLATE have the same name
AND NEW.name != '[email protected] to [email protected]' -- 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 +1368,11 @@ 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 IN (SELECT auth_name FROM builtin_authorities WHERE 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 +1531,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
2 changes: 1 addition & 1 deletion test/cli/testprojinfo_out.dist
Original file line number Diff line number Diff line change
Expand Up @@ -1593,7 +1593,7 @@ CREATE TABLE unit_of_measure(

Testing projinfo --dump-db-structure --output-id HOBU:XXXX EPSG:4326 | tail -n 4
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 geodetic_crs VALUES('HOBU','XXXX','WGS 84','','geographic 2D','EPSG','6422','EPSG','6326',NULL,0);
INSERT INTO usage VALUES('HOBU','USAGE_GEODETIC_CRS_XXXX','geodetic_crs','HOBU','XXXX','EPSG','1262','EPSG','1183');

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