From eef73e3eca2959768563d2dbb95a591c68b1d807 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 30 Nov 2020 14:00:46 +0100 Subject: [PATCH] Database: add metadata with the version number of the database layout and check it in the code This is aimed at detecting running a PROJ version against a proj.db that is not meant to work with it. This happens sometimes in complex setups mixing PROJ versions. Hopefully this will help spotting the issue earlier. --- data/sql/metadata.sql | 11 +++++++ src/iso19111/factory.cpp | 64 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/data/sql/metadata.sql b/data/sql/metadata.sql index 3c21636cb5..adcee50793 100644 --- a/data/sql/metadata.sql +++ b/data/sql/metadata.sql @@ -1,2 +1,13 @@ +-- Version of the database structure. +-- The major number indicates an incompatible change (e.g. table or column +-- removed or renamed). +-- The minor number is incremented if a backward compatible change done, that +-- is the new database can still work with an older PROJ version. +-- When updating those numbers, the DATABASE_LAYOUT_VERSION_MAJOR and +-- 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', 0); + INSERT INTO "metadata" VALUES('EPSG.VERSION', 'v10.007'); INSERT INTO "metadata" VALUES('EPSG.DATE', '2020-11-18'); diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index 7c9eac00e7..2a03fd4e3d 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -93,6 +93,12 @@ namespace io { #define GEOG_3D_SINGLE_QUOTED "'geographic 3D'" #define GEOCENTRIC_SINGLE_QUOTED "'geocentric'" +// See data/sql/metadata.sql for the semantics of those constants +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 = 0; + // --------------------------------------------------------------------------- struct SQLValues { @@ -277,6 +283,8 @@ struct DatabaseContext::Private { lru11::Cache> cacheAliasNames_{ CACHE_SIZE}; + void checkDatabaseLayout(); + static void insertIntoCache(LRUCacheOfObjects &cache, const std::string &code, const util::BaseObjectPtr &obj); @@ -571,6 +579,61 @@ void DatabaseContext::Private::open(const std::string &databasePath, // --------------------------------------------------------------------------- +void DatabaseContext::Private::checkDatabaseLayout() { + auto res = run("SELECT key, value FROM metadata WHERE key IN " + "('DATABASE.LAYOUT.VERSION.MAJOR', " + "'DATABASE.LAYOUT.VERSION.MINOR')"); + if (res.size() != 2) { + // The database layout of PROJ 7.2 that shipped with EPSG v10.003 is + // at the time of writing still compatible of the one we support. + static_assert( + // cppcheck-suppress knownConditionTrueFalse + DATABASE_LAYOUT_VERSION_MAJOR == 1 && + // cppcheck-suppress knownConditionTrueFalse + DATABASE_LAYOUT_VERSION_MINOR == 0, + "remove that assertion and below lines next time we upgrade " + "database structure"); + res = run("SELECT 1 FROM metadata WHERE key = 'EPSG.VERSION' AND " + "value = 'v10.003'"); + if (!res.empty()) { + return; + } + + throw FactoryException( + databasePath_ + + " lacks DATABASE.LAYOUT.VERSION.MAJOR / " + "DATABASE.LAYOUT.VERSION.MINOR " + "metadata. It comes from another PROJ installation."); + } + int nMajor = 0; + int nMinor = 0; + for (const auto &row : res) { + if (row[0] == "DATABASE.LAYOUT.VERSION.MAJOR") { + nMajor = atoi(row[1].c_str()); + } else if (row[0] == "DATABASE.LAYOUT.VERSION.MINOR") { + nMinor = atoi(row[1].c_str()); + } + } + if (nMajor != DATABASE_LAYOUT_VERSION_MAJOR) { + throw FactoryException(databasePath_ + + " contains DATABASE.LAYOUT.VERSION.MAJOR = " + + toString(nMajor) + " whereas " + + toString(DATABASE_LAYOUT_VERSION_MAJOR) + + " is expected. " + "It comes from another PROJ installation."); + } + if (nMinor < DATABASE_LAYOUT_VERSION_MINOR) { + throw FactoryException(databasePath_ + + " contains DATABASE.LAYOUT.VERSION.MINOR = " + + toString(nMinor) + " whereas a number >= " + + toString(DATABASE_LAYOUT_VERSION_MINOR) + + " is expected. " + "It comes from another PROJ installation."); + } +} + +// --------------------------------------------------------------------------- + void DatabaseContext::Private::setHandle(sqlite3 *sqlite_handle) { assert(sqlite_handle); @@ -888,6 +951,7 @@ DatabaseContext::create(const std::string &databasePath, if (!auxiliaryDatabasePaths.empty()) { dbCtx->getPrivate()->attachExtraDatabases(auxiliaryDatabasePaths); } + dbCtx->getPrivate()->checkDatabaseLayout(); return dbCtx; }