aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2021-03-13 16:29:44 +0100
committerEven Rouault <even.rouault@spatialys.com>2021-03-15 16:16:32 +0100
commit80ef9cee87be3df8eb293e53a91992d6c19178bb (patch)
tree8bd8068795d0f0d3f0708fa546c1eae2b1a83a86 /src
parent7293d657a658c2f2930326246d739fb7802b1115 (diff)
downloadPROJ-80ef9cee87be3df8eb293e53a91992d6c19178bb.tar.gz
PROJ-80ef9cee87be3df8eb293e53a91992d6c19178bb.zip
Add proj_context_get_database_structure() to dump structure of empty valid auxiliary DB
Diffstat (limited to 'src')
-rw-r--r--src/iso19111/c_api.cpp73
-rw-r--r--src/iso19111/factory.cpp100
-rw-r--r--src/proj.h3
3 files changed, 125 insertions, 51 deletions
diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp
index a79b387e..2a655e51 100644
--- a/src/iso19111/c_api.cpp
+++ b/src/iso19111/c_api.cpp
@@ -105,6 +105,28 @@ static void PROJ_NO_INLINE proj_log_debug(PJ_CONTEXT *ctx, const char *function,
// ---------------------------------------------------------------------------
+template <class T> static PROJ_STRING_LIST to_string_list(T &&set) {
+ auto ret = new char *[set.size() + 1];
+ size_t i = 0;
+ for (const auto &str : set) {
+ try {
+ ret[i] = new char[str.size() + 1];
+ } catch (const std::exception &) {
+ while (--i > 0) {
+ delete[] ret[i];
+ }
+ delete[] ret;
+ throw;
+ }
+ std::memcpy(ret[i], str.c_str(), str.size() + 1);
+ i++;
+ }
+ ret[i] = nullptr;
+ return ret;
+}
+
+// ---------------------------------------------------------------------------
+
void proj_context_delete_cpp_context(struct projCppContext *cppContext) {
delete cppContext;
}
@@ -390,6 +412,35 @@ const char *proj_context_get_database_metadata(PJ_CONTEXT *ctx,
// ---------------------------------------------------------------------------
+/** \brief Return the database structure
+ *
+ * Return SQL statements to run to initiate a new valid auxiliary empty
+ * database. It contains definitions of tables, views and triggers, as well
+ * as metadata for the version of the layout of the database.
+ *
+ * @param ctx PROJ context, or NULL for default context
+ * @param options null-terminated list of options, or NULL. None currently.
+ * @return list of SQL statements (to be freed with proj_string_list_destroy()),
+ * or NULL in case of error.
+ * @since 8.1
+ */
+PROJ_STRING_LIST
+proj_context_get_database_structure(PJ_CONTEXT *ctx,
+ const char *const *options) {
+ SANITIZE_CTX(ctx);
+ (void)options;
+ try {
+ auto ret = to_string_list(getDBcontext(ctx)->getDatabaseStructure());
+ ctx->safeAutoCloseDbIfNeeded();
+ return ret;
+ } catch (const std::exception &e) {
+ proj_log_error(ctx, __FUNCTION__, e.what());
+ return nullptr;
+ }
+}
+
+// ---------------------------------------------------------------------------
+
/** \brief Guess the "dialect" of the WKT string.
*
* @param ctx PROJ context, or NULL for default context
@@ -508,28 +559,6 @@ PJ *proj_create(PJ_CONTEXT *ctx, const char *text) {
// ---------------------------------------------------------------------------
-template <class T> static PROJ_STRING_LIST to_string_list(T &&set) {
- auto ret = new char *[set.size() + 1];
- size_t i = 0;
- for (const auto &str : set) {
- try {
- ret[i] = new char[str.size() + 1];
- } catch (const std::exception &) {
- while (--i > 0) {
- delete[] ret[i];
- }
- delete[] ret;
- throw;
- }
- std::memcpy(ret[i], str.c_str(), str.size() + 1);
- i++;
- }
- ret[i] = nullptr;
- return ret;
-}
-
-// ---------------------------------------------------------------------------
-
/** \brief Instantiate an object from a WKT string.
*
* This function calls osgeo::proj::io::WKTParser::createFromWKT()
diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp
index 750e9054..af9736e6 100644
--- a/src/iso19111/factory.cpp
+++ b/src/iso19111/factory.cpp
@@ -274,6 +274,8 @@ struct DatabaseContext::Private {
std::vector<std::string> auxiliaryDatabasePaths_{};
bool close_handle_ = true;
sqlite3 *sqlite_handle_{};
+ int nLayoutVersionMajor_ = 0;
+ int nLayoutVersionMinor_ = 0;
std::map<std::string, sqlite3_stmt *> mapSqlToStatement_{};
PJ_CONTEXT *pjCtxt_ = nullptr;
int recLevel_ = 0;
@@ -305,7 +307,8 @@ struct DatabaseContext::Private {
lru11::Cache<std::string, std::list<std::string>> cacheAliasNames_{
CACHE_SIZE};
- void checkDatabaseLayout();
+ void checkDatabaseLayout(const std::string &path,
+ const std::string &dbNamePrefix);
static void insertIntoCache(LRUCacheOfObjects &cache,
const std::string &code,
@@ -709,10 +712,22 @@ void DatabaseContext::Private::open(const std::string &databasePath,
// ---------------------------------------------------------------------------
-void DatabaseContext::Private::checkDatabaseLayout() {
- auto res = run("SELECT key, value FROM metadata WHERE key IN "
+void DatabaseContext::Private::checkDatabaseLayout(
+ const std::string &path, const std::string &dbNamePrefix) {
+ if (!dbNamePrefix.empty() && run("SELECT 1 FROM " + dbNamePrefix +
+ "sqlite_master WHERE name = 'metadata'")
+ .empty()) {
+ // Accept auxiliary databases without metadata table (sparse DBs)
+ return;
+ }
+ auto res = run("SELECT key, value FROM " + dbNamePrefix +
+ "metadata WHERE key IN "
"('DATABASE.LAYOUT.VERSION.MAJOR', "
"'DATABASE.LAYOUT.VERSION.MINOR')");
+ if (res.empty() && !dbNamePrefix.empty()) {
+ // Accept auxiliary databases without layout metadata.
+ return;
+ }
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.
@@ -730,36 +745,47 @@ void DatabaseContext::Private::checkDatabaseLayout() {
}
throw FactoryException(
- databasePath_ +
- " lacks DATABASE.LAYOUT.VERSION.MAJOR / "
- "DATABASE.LAYOUT.VERSION.MINOR "
- "metadata. It comes from another PROJ installation.");
+ path + " lacks DATABASE.LAYOUT.VERSION.MAJOR / "
+ "DATABASE.LAYOUT.VERSION.MINOR "
+ "metadata. It comes from another PROJ installation.");
}
- int nMajor = 0;
- int nMinor = 0;
+ int major = 0;
+ int minor = 0;
for (const auto &row : res) {
if (row[0] == "DATABASE.LAYOUT.VERSION.MAJOR") {
- nMajor = atoi(row[1].c_str());
+ major = atoi(row[1].c_str());
} else if (row[0] == "DATABASE.LAYOUT.VERSION.MINOR") {
- nMinor = atoi(row[1].c_str());
+ minor = atoi(row[1].c_str());
}
}
- if (nMajor != DATABASE_LAYOUT_VERSION_MAJOR) {
+ if (major != DATABASE_LAYOUT_VERSION_MAJOR) {
throw FactoryException(
- databasePath_ +
- " contains DATABASE.LAYOUT.VERSION.MAJOR = " + toString(nMajor) +
+ path +
+ " contains DATABASE.LAYOUT.VERSION.MAJOR = " + toString(major) +
" whereas " + toString(DATABASE_LAYOUT_VERSION_MAJOR) +
" is expected. "
"It comes from another PROJ installation.");
}
- if (nMinor < DATABASE_LAYOUT_VERSION_MINOR) {
+ if (minor < DATABASE_LAYOUT_VERSION_MINOR) {
throw FactoryException(
- databasePath_ +
- " contains DATABASE.LAYOUT.VERSION.MINOR = " + toString(nMinor) +
+ path +
+ " contains DATABASE.LAYOUT.VERSION.MINOR = " + toString(minor) +
" whereas a number >= " + toString(DATABASE_LAYOUT_VERSION_MINOR) +
" is expected. "
"It comes from another PROJ installation.");
}
+ if (dbNamePrefix.empty()) {
+ nLayoutVersionMajor_ = major;
+ nLayoutVersionMinor_ = minor;
+ } else if (nLayoutVersionMajor_ != major || nLayoutVersionMinor_ != minor) {
+ throw FactoryException(
+ "Auxiliary database " + path +
+ " contains a DATABASE.LAYOUT.VERSION = " + toString(major) + '.' +
+ toString(minor) +
+ " which is different from the one from the main database " +
+ databasePath_ + " which is " + toString(nLayoutVersionMajor_) +
+ '.' + toString(nLayoutVersionMinor_));
+ }
}
// ---------------------------------------------------------------------------
@@ -777,17 +803,28 @@ void DatabaseContext::Private::setHandle(sqlite3 *sqlite_handle) {
// ---------------------------------------------------------------------------
std::vector<std::string> DatabaseContext::Private::getDatabaseStructure() {
- const char *sqls[] = {
- "SELECT sql FROM sqlite_master WHERE type = 'table'",
- "SELECT sql FROM sqlite_master WHERE type = 'view'",
- "SELECT sql FROM sqlite_master WHERE type = 'trigger'"};
+ const std::string dbNamePrefix(auxiliaryDatabasePaths_.empty() &&
+ memoryDbForInsertPath_.empty()
+ ? ""
+ : "db_0.");
+ const auto sqlBegin("SELECT sql||';' FROM " + dbNamePrefix +
+ "sqlite_master WHERE type = ");
+ const char *const objectTypes[] = {"'table'", "'view'", "'trigger'"};
std::vector<std::string> res;
- for (const auto &sql : sqls) {
- auto sqlRes = run(sql);
+ for (const auto &objectType : objectTypes) {
+ const auto sqlRes = run(sqlBegin + objectType);
for (const auto &row : sqlRes) {
res.emplace_back(row[0]);
}
}
+ if (nLayoutVersionMajor_ > 0) {
+ res.emplace_back(
+ "INSERT INTO metadata VALUES('DATABASE.LAYOUT.VERSION.MAJOR'," +
+ toString(nLayoutVersionMajor_) + ");");
+ res.emplace_back(
+ "INSERT INTO metadata VALUES('DATABASE.LAYOUT.VERSION.MINOR'," +
+ toString(nLayoutVersionMinor_) + ");");
+ }
return res;
}
@@ -828,13 +865,16 @@ void DatabaseContext::Private::attachExtraDatabases(
"' AS db_0");
detach_ = true;
int count = 1;
- for (const auto &otherDb : auxiliaryDatabasePaths) {
+ for (const auto &otherDbPath : auxiliaryDatabasePaths) {
+ const auto attachedDbName("db_" + toString(static_cast<int>(count)));
std::string sql = "ATTACH DATABASE '";
- sql += replaceAll(otherDb, "'", "''");
- sql += "' AS db_";
- sql += toString(static_cast<int>(count));
+ sql += replaceAll(otherDbPath, "'", "''");
+ sql += "' AS ";
+ sql += attachedDbName;
count++;
run(sql);
+
+ checkDatabaseLayout(otherDbPath, attachedDbName + '.');
}
for (const auto &pair : tableStructure) {
@@ -2452,11 +2492,11 @@ DatabaseContext::create(const std::string &databasePath,
auto dbCtx = DatabaseContext::nn_make_shared<DatabaseContext>();
auto dbCtxPrivate = dbCtx->getPrivate();
dbCtxPrivate->open(databasePath, ctx);
+ dbCtxPrivate->checkDatabaseLayout(databasePath, std::string());
if (!auxiliaryDatabasePaths.empty()) {
dbCtxPrivate->attachExtraDatabases(auxiliaryDatabasePaths);
dbCtxPrivate->auxiliaryDatabasePaths_ = auxiliaryDatabasePaths;
}
- dbCtxPrivate->checkDatabaseLayout();
dbCtxPrivate->self_ = dbCtx.as_nullable();
return dbCtx;
}
@@ -2527,6 +2567,9 @@ void DatabaseContext::startInsertStatementsSession() {
"stopInsertStatementsSession() is.");
}
+ d->memoryDbForInsertPath_.clear();
+ const auto sqlStatements = getDatabaseStructure();
+
// Create a in-memory temporary sqlite3 database
std::ostringstream buffer;
buffer << "file:temp_db_for_insert_statements_";
@@ -2541,7 +2584,6 @@ void DatabaseContext::startInsertStatementsSession() {
}
// Fill the structure of this database
- const auto sqlStatements = getDatabaseStructure();
for (const auto &sql : sqlStatements) {
if (sqlite3_exec(d->memoryDbHandle_, sql.c_str(), nullptr, nullptr,
nullptr) != SQLITE_OK) {
diff --git a/src/proj.h b/src/proj.h
index 83975123..63821489 100644
--- a/src/proj.h
+++ b/src/proj.h
@@ -1044,6 +1044,9 @@ const char PROJ_DLL *proj_context_get_database_path(PJ_CONTEXT *ctx);
const char PROJ_DLL *proj_context_get_database_metadata(PJ_CONTEXT* ctx,
const char* key);
+PROJ_STRING_LIST PROJ_DLL proj_context_get_database_structure(
+ PJ_CONTEXT* ctx,
+ const char* const *options);
PJ_GUESSED_WKT_DIALECT PROJ_DLL proj_context_guess_wkt_dialect(PJ_CONTEXT *ctx,
const char *wkt);