Mercurial > hg > orthanc-databases
changeset 745:54bdb6c3bbba
Added 'Schema' configuration
| author | Alain Mazy <am@orthanc.team> |
|---|---|
| date | Wed, 08 Oct 2025 12:10:38 +0200 |
| parents | 55a827c8913a |
| children | fc41dc504073 |
| files | Framework/Common/ITransaction.h Framework/PostgreSQL/PostgreSQLDatabase.cpp Framework/PostgreSQL/PostgreSQLDatabase.h Framework/PostgreSQL/PostgreSQLParameters.cpp Framework/PostgreSQL/PostgreSQLParameters.h Framework/PostgreSQL/PostgreSQLResult.cpp Framework/PostgreSQL/PostgreSQLStatement.cpp Framework/PostgreSQL/PostgreSQLStatement.h Framework/PostgreSQL/PostgreSQLTransaction.h PostgreSQL/NEWS PostgreSQL/Plugins/PostgreSQLIndex.cpp PostgreSQL/Plugins/SQL/PrepareIndex.sql |
| diffstat | 12 files changed, 100 insertions(+), 22 deletions(-) [+] |
line wrap: on
line diff
--- a/Framework/Common/ITransaction.h Fri Oct 03 18:57:04 2025 +0200 +++ b/Framework/Common/ITransaction.h Wed Oct 08 12:10:38 2025 +0200 @@ -50,6 +50,8 @@ virtual bool DoesTableExist(const std::string& name) = 0; + virtual bool DoesSchemaExist(const std::string& name) = 0; + virtual bool DoesIndexExist(const std::string& name) = 0; virtual bool DoesTriggerExist(const std::string& name) = 0; // Only for MySQL
--- a/Framework/PostgreSQL/PostgreSQLDatabase.cpp Fri Oct 03 18:57:04 2025 +0200 +++ b/Framework/PostgreSQL/PostgreSQLDatabase.cpp Wed Oct 08 12:10:38 2025 +0200 @@ -111,6 +111,10 @@ LOG(ERROR) << "PostgreSQL error: " << message; throw Orthanc::OrthancException(Orthanc::ErrorCode_DatabaseUnavailable); } + + // select the schema each time we open a connection + PostgreSQLStatement setSchema(*this, "SET search_path TO " + parameters_.GetSchema()); + setSchema.Run(); } @@ -189,22 +193,38 @@ } } - - bool PostgreSQLDatabase::DoesTableExist(const std::string& name) + bool PostgreSQLDatabase::DoesSchemaExist(const std::string& schema) { - std::string lower; - Orthanc::Toolbox::ToLowerCase(lower, name); + PostgreSQLStatement statement(*this, + "SELECT schema_name " + "FROM information_schema.schemata " + "WHERE schema_name = $1"); + + statement.DeclareInputName(0); + statement.BindString(0, schema); + + PostgreSQLResult result(statement); + return !result.IsDone(); + } + + bool PostgreSQLDatabase::DoesTableExist(const std::string& tableName) + { + std::string lowerTableName; + Orthanc::Toolbox::ToLowerCase(lowerTableName, tableName); // http://stackoverflow.com/a/24089729/881731 PostgreSQLStatement statement(*this, "SELECT 1 FROM pg_catalog.pg_class c " "JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace " - "WHERE n.nspname = 'public' AND c.relkind='r' " - "AND c.relname=$1"); + "WHERE n.nspname = $1 AND c.relkind='r' " + "AND c.relname=$2"); - statement.DeclareInputString(0); - statement.BindString(0, lower); + statement.DeclareInputName(0); + statement.BindString(0, parameters_.GetSchema()); + + statement.DeclareInputString(1); + statement.BindString(1, lowerTableName); PostgreSQLResult result(statement); return !result.IsDone(); @@ -220,11 +240,14 @@ PostgreSQLStatement statement(*this, "SELECT 1 FROM pg_catalog.pg_class c " "JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace " - "WHERE n.nspname = 'public' AND c.relkind='i' " - "AND c.relname=$1"); + "WHERE n.nspname = $1 AND c.relkind='i' " + "AND c.relname=$2"); - statement.DeclareInputString(0); - statement.BindString(0, lower); + statement.DeclareInputName(0); + statement.BindString(0, parameters_.GetSchema()); + + statement.DeclareInputString(1); + statement.BindString(1, lower); PostgreSQLResult result(statement); return !result.IsDone(); @@ -246,7 +269,7 @@ statement.DeclareInputString(1); statement.DeclareInputString(2); - statement.BindString(0, "public" /* schema */); + statement.BindString(0, parameters_.GetSchema()); statement.BindString(1, lowerTable); statement.BindString(2, lowerColumn); @@ -263,11 +286,11 @@ ExecuteMultiLines("SELECT lo_unlink(loid) FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) as loids;"); // http://stackoverflow.com/a/21247009/881731 - ExecuteMultiLines("DROP SCHEMA public CASCADE;"); - ExecuteMultiLines("CREATE SCHEMA public;"); - ExecuteMultiLines("GRANT ALL ON SCHEMA public TO postgres;"); - ExecuteMultiLines("GRANT ALL ON SCHEMA public TO public;"); - ExecuteMultiLines("COMMENT ON SCHEMA public IS 'standard public schema';"); + ExecuteMultiLines("DROP SCHEMA " + parameters_.GetSchema() + " CASCADE;"); + ExecuteMultiLines("CREATE SCHEMA " + parameters_.GetSchema() + ";"); + ExecuteMultiLines("GRANT ALL ON SCHEMA " + parameters_.GetSchema() + " TO postgres;"); + ExecuteMultiLines("GRANT ALL ON SCHEMA " + parameters_.GetSchema() + " TO public;"); + ExecuteMultiLines("COMMENT ON SCHEMA " + parameters_.GetSchema() + " IS 'standard orthanc schema';"); transaction.Commit(); } @@ -310,6 +333,11 @@ return db_.DoesTableExist(name.c_str()); } + virtual bool DoesSchemaExist(const std::string& name) ORTHANC_OVERRIDE + { + return db_.DoesSchemaExist(name.c_str()); + } + virtual bool DoesIndexExist(const std::string& name) ORTHANC_OVERRIDE { return db_.DoesIndexExist(name.c_str());
--- a/Framework/PostgreSQL/PostgreSQLDatabase.h Fri Oct 03 18:57:04 2025 +0200 +++ b/Framework/PostgreSQL/PostgreSQLDatabase.h Wed Oct 08 12:10:38 2025 +0200 @@ -81,6 +81,8 @@ bool DoesColumnExist(const std::string& tableName, const std::string& columnName); + bool DoesSchemaExist(const std::string& name); + void ClearAll(); // Only for unit tests! virtual Dialect GetDialect() const ORTHANC_OVERRIDE
--- a/Framework/PostgreSQL/PostgreSQLParameters.cpp Fri Oct 03 18:57:04 2025 +0200 +++ b/Framework/PostgreSQL/PostgreSQLParameters.cpp Wed Oct 08 12:10:38 2025 +0200 @@ -25,6 +25,7 @@ #include <Logging.h> #include <OrthancException.h> +#include <Toolbox.h> #include <boost/lexical_cast.hpp> @@ -98,6 +99,8 @@ lock_ = configuration.GetBooleanValue("Lock", true); // Use locking by default + SetSchema(configuration.GetStringValue("Schema", "public")); + isVerboseEnabled_ = configuration.GetBooleanValue("EnableVerboseLogs", false); allowInconsistentChildCounts_ = configuration.GetBooleanValue("AllowInconsistentChildCounts", false); @@ -202,6 +205,11 @@ database_ = database; } + void PostgreSQLParameters::SetSchema(const std::string& schema) + { + Orthanc::Toolbox::ToLowerCase(schema_, schema); + } + const std::string PostgreSQLParameters::GetReadWriteTransactionStatement() const { switch (isolationMode_)
--- a/Framework/PostgreSQL/PostgreSQLParameters.h Fri Oct 03 18:57:04 2025 +0200 +++ b/Framework/PostgreSQL/PostgreSQLParameters.h Wed Oct 08 12:10:38 2025 +0200 @@ -45,6 +45,7 @@ std::string username_; std::string password_; std::string database_; + std::string schema_; std::string uri_; bool ssl_; bool lock_; @@ -104,6 +105,13 @@ return database_; } + void SetSchema(const std::string& schema); + + const std::string& GetSchema() const + { + return schema_; + } + void SetSsl(bool ssl) { ssl_ = ssl;
--- a/Framework/PostgreSQL/PostgreSQLResult.cpp Fri Oct 03 18:57:04 2025 +0200 +++ b/Framework/PostgreSQL/PostgreSQLResult.cpp Wed Oct 08 12:10:38 2025 +0200 @@ -175,7 +175,7 @@ CheckColumn(column, 0); Oid oid = PQftype(reinterpret_cast<PGresult*>(result_), column); - if (oid != TEXTOID && oid != VARCHAROID) + if (oid != TEXTOID && oid != VARCHAROID && oid != NAMEOID) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadParameterType); }
--- a/Framework/PostgreSQL/PostgreSQLStatement.cpp Fri Oct 03 18:57:04 2025 +0200 +++ b/Framework/PostgreSQL/PostgreSQLStatement.cpp Wed Oct 08 12:10:38 2025 +0200 @@ -223,7 +223,7 @@ } oids_[param] = type; - binary_[param] = (type == TEXTOID || type == OIDOID) ? 0 : 1; + binary_[param] = (type == TEXTOID || type == OIDOID ||type == NAMEOID) ? 0 : 1; } @@ -245,6 +245,12 @@ } + void PostgreSQLStatement::DeclareInputName(unsigned int param) + { + DeclareInputInternal(param, NAMEOID); + } + + void PostgreSQLStatement::DeclareInputBinary(unsigned int param) { DeclareInputInternal(param, BYTEAOID); @@ -464,6 +470,7 @@ switch (oids_[param]) { case TEXTOID: + case NAMEOID: { std::string s = value + '\0'; // Make sure that there is an end-of-string character inputs_->SetItem(param, s.c_str(), s.size());
--- a/Framework/PostgreSQL/PostgreSQLStatement.h Fri Oct 03 18:57:04 2025 +0200 +++ b/Framework/PostgreSQL/PostgreSQLStatement.h Wed Oct 08 12:10:38 2025 +0200 @@ -80,6 +80,8 @@ void DeclareInputString(unsigned int param); + void DeclareInputName(unsigned int param); + void DeclareInputBinary(unsigned int param); void DeclareInputLargeObject(unsigned int param);
--- a/Framework/PostgreSQL/PostgreSQLTransaction.h Fri Oct 03 18:57:04 2025 +0200 +++ b/Framework/PostgreSQL/PostgreSQLTransaction.h Wed Oct 08 12:10:38 2025 +0200 @@ -67,6 +67,11 @@ return database_.DoesTableExist(name.c_str()); } + virtual bool DoesSchemaExist(const std::string& name) ORTHANC_OVERRIDE + { + return database_.DoesSchemaExist(name.c_str()); + } + virtual bool DoesIndexExist(const std::string& name) ORTHANC_OVERRIDE { return database_.DoesIndexExist(name.c_str());
--- a/PostgreSQL/NEWS Fri Oct 03 18:57:04 2025 +0200 +++ b/PostgreSQL/NEWS Wed Oct 08 12:10:38 2025 +0200 @@ -10,6 +10,16 @@ Minimal Postgresql Server version: 9 Optimal Postgresql Server version: 11+ +Changes: +* New configuration "Schema" (default value: 'public') to allow Orthanc + to use another schema. Note that, if you are not using the default 'public' + value, you must create the schema manually before starting Orthanc and you + must make sure that the user has access to it. + Note that Orthanc might try to install the pg_trgm extension in the 'public' + schema so you should make sure that the user also has access to the 'public' + schema or that you install the pg_trgm extension manually in the public schema. + The plugin now calls 'SET search_path TO $schema' when opening a new connection + to the DB. Maintenance: * Now verifying the DatabasePatchLevel (revision) in another transaction than
--- a/PostgreSQL/Plugins/PostgreSQLIndex.cpp Fri Oct 03 18:57:04 2025 +0200 +++ b/PostgreSQL/Plugins/PostgreSQLIndex.cpp Wed Oct 08 12:10:38 2025 +0200 @@ -125,6 +125,12 @@ DatabaseManager::Transaction t(manager, TransactionType_ReadWrite); bool hasAppliedAnUpgrade = false; + if (!t.GetDatabaseTransaction().DoesSchemaExist(parameters_.GetSchema())) + { + LOG(ERROR) << "The schema '" << parameters_.GetSchema() << "' does not exist. If you are not using the 'public' schema, you must create the schema manually before starting Orthanc."; + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + if (!t.GetDatabaseTransaction().DoesTableExist("Resources")) { LOG(WARNING) << "PostgreSQL is creating the database schema";
--- a/PostgreSQL/Plugins/SQL/PrepareIndex.sql Fri Oct 03 18:57:04 2025 +0200 +++ b/PostgreSQL/Plugins/SQL/PrepareIndex.sql Wed Oct 08 12:10:38 2025 +0200 @@ -145,8 +145,8 @@ DO $body$ begin IF EXISTS (SELECT 1 FROM pg_available_extensions WHERE name='pg_trgm') THEN - CREATE EXTENSION IF NOT EXISTS pg_trgm; - CREATE INDEX IF NOT EXISTS DicomIdentifiersIndexValues2 ON DicomIdentifiers USING gin(value gin_trgm_ops); + CREATE EXTENSION IF NOT EXISTS pg_trgm WITH SCHEMA public; + CREATE INDEX IF NOT EXISTS DicomIdentifiersIndexValues2 ON DicomIdentifiers USING gin(value public.gin_trgm_ops); ELSE RAISE NOTICE 'pg_trgm extension is not available on you system'; END IF;
