Skip to content

Commit

Permalink
Database: add metadata with the version number of the database layout…
Browse files Browse the repository at this point in the history
… 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.
  • Loading branch information
rouault committed Nov 30, 2020
1 parent 36c7001 commit eef73e3
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 0 deletions.
11 changes: 11 additions & 0 deletions data/sql/metadata.sql
Original file line number Diff line number Diff line change
@@ -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');
64 changes: 64 additions & 0 deletions src/iso19111/factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -277,6 +283,8 @@ struct DatabaseContext::Private {
lru11::Cache<std::string, std::list<std::string>> cacheAliasNames_{
CACHE_SIZE};

void checkDatabaseLayout();

static void insertIntoCache(LRUCacheOfObjects &cache,
const std::string &code,
const util::BaseObjectPtr &obj);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -888,6 +951,7 @@ DatabaseContext::create(const std::string &databasePath,
if (!auxiliaryDatabasePaths.empty()) {
dbCtx->getPrivate()->attachExtraDatabases(auxiliaryDatabasePaths);
}
dbCtx->getPrivate()->checkDatabaseLayout();
return dbCtx;
}

Expand Down

0 comments on commit eef73e3

Please sign in to comment.