# HG changeset patch # User Sebastien Jodogne # Date 1617901744 -7200 # Node ID 35598014f140c95b7c4ead8c840dda6ea6b460d8 # Parent d1d2edbbe6fb30c79ffdecb6d3590b442e52d559 refactoring to remove GlobalProperties.cpp diff -r d1d2edbbe6fb -r 35598014f140 Framework/Common/DatabaseManager.h --- a/Framework/Common/DatabaseManager.h Thu Apr 08 12:00:01 2021 +0200 +++ b/Framework/Common/DatabaseManager.h Thu Apr 08 19:09:04 2021 +0200 @@ -36,6 +36,9 @@ * WARNING: In PostgreSQL releases <= 3.3 and in MySQL releases <= * 3.0, this class was protected by a mutex. It is now assumed that * locking must be implemented at a higher level. + * + * This class maintains a list of precompiled statements. At any + * time, this class handles 0 or 1 active transaction. **/ class DatabaseManager : public boost::noncopyable { @@ -47,11 +50,6 @@ CachedStatements cachedStatements_; Dialect dialect_; - IDatabase& GetDatabase() - { - return *database_; - } - void CloseIfUnavailable(Orthanc::ErrorCode e); IPrecompiledStatement* LookupCachedStatement(const StatementLocation& location) const; @@ -71,6 +69,11 @@ Close(); } + IDatabase& GetDatabase() + { + return *database_; + } + Dialect GetDialect() const { return dialect_; @@ -110,6 +113,21 @@ { return database_; } + + bool DoesTableExist(const std::string& name) + { + return manager_.GetTransaction().DoesTableExist(name); + } + + bool DoesTriggerExist(const std::string& name) + { + return manager_.GetTransaction().DoesTriggerExist(name); + } + + void ExecuteMultiLines(const std::string& sql) + { + manager_.GetTransaction().ExecuteMultiLines(sql); + } }; diff -r d1d2edbbe6fb -r 35598014f140 Framework/Common/ITransaction.h --- a/Framework/Common/ITransaction.h Thu Apr 08 12:00:01 2021 +0200 +++ b/Framework/Common/ITransaction.h Thu Apr 08 19:09:04 2021 +0200 @@ -45,5 +45,11 @@ virtual void ExecuteWithoutResult(IPrecompiledStatement& statement, const Dictionary& parameters) = 0; + + virtual bool DoesTableExist(const std::string& name) = 0; + + virtual bool DoesTriggerExist(const std::string& name) = 0; // Only for MySQL + + virtual void ExecuteMultiLines(const std::string& query) = 0; }; } diff -r d1d2edbbe6fb -r 35598014f140 Framework/MySQL/MySQLDatabase.cpp --- a/Framework/MySQL/MySQLDatabase.cpp Thu Apr 08 12:00:01 2021 +0200 +++ b/Framework/MySQL/MySQLDatabase.cpp Thu Apr 08 19:09:04 2021 +0200 @@ -224,8 +224,8 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); } - db.Execute("DROP DATABASE " + database, false); - db.Execute("CREATE DATABASE " + database, false); + db.ExecuteMultiLines("DROP DATABASE " + database, false); + db.ExecuteMultiLines("CREATE DATABASE " + database, false); t.Commit(); } } @@ -478,8 +478,8 @@ } - void MySQLDatabase::Execute(const std::string& sql, - bool arobaseSeparator) + void MySQLDatabase::ExecuteMultiLines(const std::string& sql, + bool arobaseSeparator) { if (mysql_ == NULL) { @@ -527,6 +527,9 @@ { class MySQLImplicitTransaction : public ImplicitTransaction { + private: + MySQLDatabase& db_; + protected: virtual IResult* ExecuteInternal(IPrecompiledStatement& statement, const Dictionary& parameters) @@ -539,6 +542,27 @@ { dynamic_cast(statement).ExecuteWithoutResult(*this, parameters); } + + public: + MySQLImplicitTransaction(MySQLDatabase& db) : + db_(db) + { + } + + virtual bool DoesTableExist(const std::string& name) ORTHANC_OVERRIDE + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "An explicit transaction is needed"); + } + + virtual bool DoesTriggerExist(const std::string& name) ORTHANC_OVERRIDE + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "An explicit transaction is needed"); + } + + virtual void ExecuteMultiLines(const std::string& query) ORTHANC_OVERRIDE + { + db_.ExecuteMultiLines(query, false /* don't deal with arobases */); + } }; } @@ -553,7 +577,7 @@ switch (type) { case TransactionType_Implicit: - return new MySQLImplicitTransaction; + return new MySQLImplicitTransaction(*this); case TransactionType_ReadOnly: case TransactionType_ReadWrite: @@ -635,7 +659,7 @@ { std::unique_ptr db(new MySQLDatabase(parameters_)); db->Open(); - db->Execute("SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE", false); + db->ExecuteMultiLines("SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE", false); return db.release(); } diff -r d1d2edbbe6fb -r 35598014f140 Framework/MySQL/MySQLDatabase.h --- a/Framework/MySQL/MySQLDatabase.h Thu Apr 08 12:00:01 2021 +0200 +++ b/Framework/MySQL/MySQLDatabase.h Thu Apr 08 19:09:04 2021 +0200 @@ -79,8 +79,8 @@ void AdvisoryLock(const std::string& lock); - void Execute(const std::string& sql, - bool arobaseSeparator); + void ExecuteMultiLines(const std::string& sql, + bool arobaseSeparator); bool DoesTableExist(MySQLTransaction& transaction, const std::string& name); diff -r d1d2edbbe6fb -r 35598014f140 Framework/MySQL/MySQLTransaction.cpp --- a/Framework/MySQL/MySQLTransaction.cpp Thu Apr 08 12:00:01 2021 +0200 +++ b/Framework/MySQL/MySQLTransaction.cpp Thu Apr 08 19:09:04 2021 +0200 @@ -39,11 +39,11 @@ switch (type) { case TransactionType_ReadWrite: - db_.Execute("START TRANSACTION READ WRITE", false); + db_.ExecuteMultiLines("START TRANSACTION READ WRITE", false); break; case TransactionType_ReadOnly: - db_.Execute("START TRANSACTION READ ONLY", false); + db_.ExecuteMultiLines("START TRANSACTION READ ONLY", false); break; default: @@ -62,7 +62,7 @@ try { - db_.Execute("ROLLBACK", false); + db_.ExecuteMultiLines("ROLLBACK", false); } catch (Orthanc::OrthancException&) { @@ -76,7 +76,7 @@ { if (active_) { - db_.Execute("ROLLBACK", false); + db_.ExecuteMultiLines("ROLLBACK", false); active_ = false; } else @@ -91,7 +91,7 @@ { if (active_) { - db_.Execute("COMMIT", false); + db_.ExecuteMultiLines("COMMIT", false); active_ = false; } else diff -r d1d2edbbe6fb -r 35598014f140 Framework/MySQL/MySQLTransaction.h --- a/Framework/MySQL/MySQLTransaction.h Thu Apr 08 12:00:01 2021 +0200 +++ b/Framework/MySQL/MySQLTransaction.h Thu Apr 08 19:09:04 2021 +0200 @@ -56,5 +56,20 @@ virtual void ExecuteWithoutResult(IPrecompiledStatement& transaction, const Dictionary& parameters) ORTHANC_OVERRIDE; + + virtual bool DoesTableExist(const std::string& name) + { + return db_.DoesTableExist(*this, name); + } + + virtual bool DoesTriggerExist(const std::string& name) + { + return db_.DoesTriggerExist(*this, name); + } + + virtual void ExecuteMultiLines(const std::string& query) + { + db_.ExecuteMultiLines(query, false /* don't deal with arobases */); + } }; } diff -r d1d2edbbe6fb -r 35598014f140 Framework/Plugins/DatabaseBackendAdapterV3.cpp --- a/Framework/Plugins/DatabaseBackendAdapterV3.cpp Thu Apr 08 12:00:01 2021 +0200 +++ b/Framework/Plugins/DatabaseBackendAdapterV3.cpp Thu Apr 08 19:09:04 2021 +0200 @@ -151,11 +151,12 @@ if (connections_.size() == 0) { assert(backend_.get() != NULL); - - std::unique_ptr database(backend_->OpenDatabaseConnection()); - backend_->ConfigureDatabase(*database); - connections_.push_back(new DatabaseManager(database.release())); + { + std::unique_ptr manager(new DatabaseManager(backend_->OpenDatabaseConnection())); + backend_->ConfigureDatabase(*manager); + connections_.push_back(manager.release()); + } for (size_t i = 1; i < countConnections_; i++) { diff -r d1d2edbbe6fb -r 35598014f140 Framework/Plugins/GlobalProperties.h --- a/Framework/Plugins/GlobalProperties.h Thu Apr 08 12:00:01 2021 +0200 +++ b/Framework/Plugins/GlobalProperties.h Thu Apr 08 19:09:04 2021 +0200 @@ -21,8 +21,6 @@ #pragma once -#include "../Common/DatabaseManager.h" - #define MISSING_SERVER_IDENTIFIER "" @@ -58,41 +56,3 @@ GlobalProperty_DatabaseInternal9 = 19 }; } - - -namespace OrthancDatabases -{ - bool LookupGlobalProperty(std::string& target /* out */, - IDatabase& db, - ITransaction& transaction, - const std::string& serverIdentifier, - Orthanc::GlobalProperty property); - - bool LookupGlobalProperty(std::string& target /* out */, - DatabaseManager& manager, - const std::string& serverIdentifier, - Orthanc::GlobalProperty property); - - void SetGlobalProperty(IDatabase& db, - ITransaction& transaction, - const std::string& serverIdentifier, - Orthanc::GlobalProperty property, - const std::string& utf8); - - void SetGlobalProperty(DatabaseManager& manager, - const std::string& serverIdentifier, - Orthanc::GlobalProperty property, - const std::string& utf8); - - bool LookupGlobalIntegerProperty(int& target, - IDatabase& db, - ITransaction& transaction, - const std::string& serverIdentifier, - Orthanc::GlobalProperty property); - - void SetGlobalIntegerProperty(IDatabase& db, - ITransaction& transaction, - const std::string& serverIdentifier, - Orthanc::GlobalProperty property, - int value); -} diff -r d1d2edbbe6fb -r 35598014f140 Framework/Plugins/IDatabaseBackend.h --- a/Framework/Plugins/IDatabaseBackend.h Thu Apr 08 12:00:01 2021 +0200 +++ b/Framework/Plugins/IDatabaseBackend.h Thu Apr 08 19:09:04 2021 +0200 @@ -42,7 +42,7 @@ virtual IDatabase* OpenDatabaseConnection() = 0; // This function is invoked once, even if multiple connections are open - virtual void ConfigureDatabase(IDatabase& database) = 0; + virtual void ConfigureDatabase(DatabaseManager& database) = 0; virtual void SetOutputFactory(IDatabaseBackendOutput::IFactory* factory) = 0; @@ -213,7 +213,7 @@ virtual void SetGlobalProperty(DatabaseManager& manager, const char* serverIdentifier, int32_t property, - const char* value) = 0; + const char* utf8) = 0; virtual void SetMainDicomTag(DatabaseManager& manager, int64_t id, diff -r d1d2edbbe6fb -r 35598014f140 Framework/Plugins/IndexBackend.cpp --- a/Framework/Plugins/IndexBackend.cpp Thu Apr 08 12:00:01 2021 +0200 +++ b/Framework/Plugins/IndexBackend.cpp Thu Apr 08 19:09:04 2021 +0200 @@ -1068,8 +1068,41 @@ const char* serverIdentifier, int32_t property) { - return ::OrthancDatabases::LookupGlobalProperty(target, manager, serverIdentifier, - static_cast(property)); + DatabaseManager::CachedStatement statement( + STATEMENT_FROM_HERE, manager, + "SELECT value FROM GlobalProperties WHERE property=${property}"); + + statement.SetReadOnly(true); + statement.SetParameterType("property", ValueType_Integer64); + + Dictionary args; + args.SetIntegerValue("property", property); + + statement.Execute(args); + statement.SetResultFieldType(0, ValueType_Utf8String); + + if (statement.IsDone()) + { + return false; + } + else + { + ValueType type = statement.GetResultField(0).GetType(); + + if (type == ValueType_Null) + { + return false; + } + else if (type != ValueType_Utf8String) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); + } + else + { + target = dynamic_cast(statement.GetResultField(0)).GetContent(); + return true; + } + } } @@ -1332,10 +1365,53 @@ void IndexBackend::SetGlobalProperty(DatabaseManager& manager, const char* serverIdentifier, int32_t property, - const char* value) + const char* utf8) { - return ::OrthancDatabases::SetGlobalProperty( - manager, serverIdentifier, static_cast(property), value); + if (manager.GetDialect() == Dialect_SQLite) + { + DatabaseManager::CachedStatement statement( + STATEMENT_FROM_HERE, manager, + "INSERT OR REPLACE INTO GlobalProperties VALUES (${property}, ${value})"); + + statement.SetParameterType("property", ValueType_Integer64); + statement.SetParameterType("value", ValueType_Utf8String); + + Dictionary args; + args.SetIntegerValue("property", static_cast(property)); + args.SetUtf8Value("value", utf8); + + statement.Execute(args); + } + else + { + { + DatabaseManager::CachedStatement statement( + STATEMENT_FROM_HERE, manager, + "DELETE FROM GlobalProperties WHERE property=${property}"); + + statement.SetParameterType("property", ValueType_Integer64); + + Dictionary args; + args.SetIntegerValue("property", property); + + statement.Execute(args); + } + + { + DatabaseManager::CachedStatement statement( + STATEMENT_FROM_HERE, manager, + "INSERT INTO GlobalProperties VALUES (${property}, ${value})"); + + statement.SetParameterType("property", ValueType_Integer64); + statement.SetParameterType("value", ValueType_Utf8String); + + Dictionary args; + args.SetIntegerValue("property", static_cast(property)); + args.SetUtf8Value("value", utf8); + + statement.Execute(args); + } + } } @@ -2304,6 +2380,43 @@ } + bool IndexBackend::LookupGlobalIntegerProperty(int& target, + DatabaseManager& manager, + const char* serverIdentifier, + int32_t property) + { + std::string value; + + if (LookupGlobalProperty(value, manager, serverIdentifier, property)) + { + try + { + target = boost::lexical_cast(value); + return true; + } + catch (boost::bad_lexical_cast&) + { + LOG(ERROR) << "Corrupted PostgreSQL database"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); + } + } + else + { + return false; + } + } + + + void IndexBackend::SetGlobalIntegerProperty(DatabaseManager& manager, + const char* serverIdentifier, + int32_t property, + int value) + { + std::string s = boost::lexical_cast(value); + SetGlobalProperty(manager, serverIdentifier, property, s.c_str()); + } + + void IndexBackend::Finalize() { OrthancDatabases::DatabaseBackendAdapterV2::Finalize(); @@ -2318,8 +2431,8 @@ DatabaseManager* IndexBackend::CreateSingleDatabaseManager(IDatabaseBackend& backend) { - std::unique_ptr database(backend.OpenDatabaseConnection()); - backend.ConfigureDatabase(*database); - return new DatabaseManager(database.release()); + std::unique_ptr manager(new DatabaseManager(backend.OpenDatabaseConnection())); + backend.ConfigureDatabase(*manager); + return manager.release(); } } diff -r d1d2edbbe6fb -r 35598014f140 Framework/Plugins/IndexBackend.h --- a/Framework/Plugins/IndexBackend.h Thu Apr 08 12:00:01 2021 +0200 +++ b/Framework/Plugins/IndexBackend.h Thu Apr 08 19:09:04 2021 +0200 @@ -88,7 +88,7 @@ uint32_t maxResults); public: - IndexBackend(OrthancPluginContext* context); + explicit IndexBackend(OrthancPluginContext* context); virtual OrthancPluginContext* GetContext() ORTHANC_OVERRIDE { @@ -254,7 +254,7 @@ virtual void SetGlobalProperty(DatabaseManager& manager, const char* serverIdentifier, int32_t property, - const char* value) ORTHANC_OVERRIDE; + const char* utf8) ORTHANC_OVERRIDE; virtual void SetMainDicomTag(DatabaseManager& manager, int64_t id, @@ -381,6 +381,16 @@ const char* hashSeries, const char* hashInstance); + bool LookupGlobalIntegerProperty(int& target /*out*/, + DatabaseManager& manager, + const char* serverIdentifier, + int32_t property); + + void SetGlobalIntegerProperty(DatabaseManager& manager, + const char* serverIdentifier, + int32_t property, + int value); + /** * "maxDatabaseRetries" is to handle * "OrthancPluginErrorCode_DatabaseCannotSerialize" if there is a diff -r d1d2edbbe6fb -r 35598014f140 Framework/PostgreSQL/PostgreSQLDatabase.cpp --- a/Framework/PostgreSQL/PostgreSQLDatabase.cpp Thu Apr 08 12:00:01 2021 +0200 +++ b/Framework/PostgreSQL/PostgreSQLDatabase.cpp Thu Apr 08 19:09:04 2021 +0200 @@ -155,7 +155,7 @@ } - void PostgreSQLDatabase::Execute(const std::string& sql) + void PostgreSQLDatabase::ExecuteMultiLines(const std::string& sql) { LOG(TRACE) << "PostgreSQL: " << sql; Open(); @@ -210,14 +210,14 @@ PostgreSQLTransaction transaction(*this, TransactionType_ReadWrite); // Remove all the large objects - Execute("SELECT lo_unlink(loid) FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) as loids;"); + ExecuteMultiLines("SELECT lo_unlink(loid) FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) as loids;"); // http://stackoverflow.com/a/21247009/881731 - Execute("DROP SCHEMA public CASCADE;"); - Execute("CREATE SCHEMA public;"); - Execute("GRANT ALL ON SCHEMA public TO postgres;"); - Execute("GRANT ALL ON SCHEMA public TO public;"); - Execute("COMMENT ON SCHEMA public IS 'standard public schema';"); + 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';"); transaction.Commit(); } @@ -233,6 +233,9 @@ { class PostgreSQLImplicitTransaction : public ImplicitTransaction { + private: + PostgreSQLDatabase& db_; + protected: virtual IResult* ExecuteInternal(IPrecompiledStatement& statement, const Dictionary& parameters) @@ -245,6 +248,27 @@ { dynamic_cast(statement).ExecuteWithoutResult(*this, parameters); } + + public: + PostgreSQLImplicitTransaction(PostgreSQLDatabase& db) : + db_(db) + { + } + + virtual bool DoesTableExist(const std::string& name) ORTHANC_OVERRIDE + { + return db_.DoesTableExist(name.c_str()); + } + + virtual bool DoesTriggerExist(const std::string& name) ORTHANC_OVERRIDE + { + return false; + } + + virtual void ExecuteMultiLines(const std::string& query) ORTHANC_OVERRIDE + { + db_.ExecuteMultiLines(query); + } }; } @@ -254,7 +278,7 @@ switch (type) { case TransactionType_Implicit: - return new PostgreSQLImplicitTransaction; + return new PostgreSQLImplicitTransaction(*this); case TransactionType_ReadWrite: case TransactionType_ReadOnly: diff -r d1d2edbbe6fb -r 35598014f140 Framework/PostgreSQL/PostgreSQLDatabase.h --- a/Framework/PostgreSQL/PostgreSQLDatabase.h Thu Apr 08 12:00:01 2021 +0200 +++ b/Framework/PostgreSQL/PostgreSQLDatabase.h Thu Apr 08 19:09:04 2021 +0200 @@ -62,7 +62,7 @@ void AdvisoryLock(int32_t lock); - void Execute(const std::string& sql); + void ExecuteMultiLines(const std::string& sql); bool DoesTableExist(const char* name); diff -r d1d2edbbe6fb -r 35598014f140 Framework/PostgreSQL/PostgreSQLStatement.cpp --- a/Framework/PostgreSQL/PostgreSQLStatement.cpp Thu Apr 08 12:00:01 2021 +0200 +++ b/Framework/PostgreSQL/PostgreSQLStatement.cpp Thu Apr 08 19:09:04 2021 +0200 @@ -201,7 +201,7 @@ // "Although there is no libpq function for deleting a // prepared statement, the SQL DEALLOCATE statement can be // used for that purpose." - database_.Execute("DEALLOCATE \"" + id_ + "\""); + database_.ExecuteMultiLines("DEALLOCATE \"" + id_ + "\""); } id_.clear(); diff -r d1d2edbbe6fb -r 35598014f140 Framework/PostgreSQL/PostgreSQLTransaction.cpp --- a/Framework/PostgreSQL/PostgreSQLTransaction.cpp Thu Apr 08 12:00:01 2021 +0200 +++ b/Framework/PostgreSQL/PostgreSQLTransaction.cpp Thu Apr 08 19:09:04 2021 +0200 @@ -46,7 +46,7 @@ try { - database_.Execute("ABORT"); + database_.ExecuteMultiLines("ABORT"); } catch (Orthanc::OrthancException&) { @@ -64,16 +64,16 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } - database_.Execute("BEGIN"); + database_.ExecuteMultiLines("BEGIN"); switch (type) { case TransactionType_ReadWrite: - database_.Execute("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ WRITE"); + database_.ExecuteMultiLines("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ WRITE"); break; case TransactionType_ReadOnly: - database_.Execute("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY"); + database_.ExecuteMultiLines("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY"); break; default: @@ -93,7 +93,7 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } - database_.Execute("ABORT"); + database_.ExecuteMultiLines("ABORT"); isOpen_ = false; } @@ -107,7 +107,7 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } - database_.Execute("COMMIT"); + database_.ExecuteMultiLines("COMMIT"); isOpen_ = false; } diff -r d1d2edbbe6fb -r 35598014f140 Framework/PostgreSQL/PostgreSQLTransaction.h --- a/Framework/PostgreSQL/PostgreSQLTransaction.h Thu Apr 08 12:00:01 2021 +0200 +++ b/Framework/PostgreSQL/PostgreSQLTransaction.h Thu Apr 08 19:09:04 2021 +0200 @@ -59,5 +59,20 @@ virtual void ExecuteWithoutResult(IPrecompiledStatement& statement, const Dictionary& parameters) ORTHANC_OVERRIDE; + + virtual bool DoesTableExist(const std::string& name) ORTHANC_OVERRIDE + { + return database_.DoesTableExist(name.c_str()); + } + + virtual bool DoesTriggerExist(const std::string& name) ORTHANC_OVERRIDE + { + return false; + } + + virtual void ExecuteMultiLines(const std::string& query) ORTHANC_OVERRIDE + { + database_.ExecuteMultiLines(query); + } }; } diff -r d1d2edbbe6fb -r 35598014f140 Framework/SQLite/SQLiteDatabase.cpp --- a/Framework/SQLite/SQLiteDatabase.cpp Thu Apr 08 12:00:01 2021 +0200 +++ b/Framework/SQLite/SQLiteDatabase.cpp Thu Apr 08 19:09:04 2021 +0200 @@ -48,6 +48,9 @@ { class SQLiteImplicitTransaction : public ImplicitTransaction { + private: + SQLiteDatabase& db_; + protected: virtual IResult* ExecuteInternal(IPrecompiledStatement& statement, const Dictionary& parameters) ORTHANC_OVERRIDE @@ -60,6 +63,27 @@ { dynamic_cast(statement).ExecuteWithoutResult(*this, parameters); } + + public: + SQLiteImplicitTransaction(SQLiteDatabase& db) : + db_(db) + { + } + + virtual bool DoesTableExist(const std::string& name) ORTHANC_OVERRIDE + { + return db_.GetObject().DoesTableExist(name.c_str()); + } + + virtual bool DoesTriggerExist(const std::string& name) ORTHANC_OVERRIDE + { + return false; + } + + virtual void ExecuteMultiLines(const std::string& query) ORTHANC_OVERRIDE + { + db_.GetObject().Execute(query); + } }; } @@ -68,7 +92,7 @@ switch (type) { case TransactionType_Implicit: - return new SQLiteImplicitTransaction; + return new SQLiteImplicitTransaction(*this); case TransactionType_ReadOnly: case TransactionType_ReadWrite: diff -r d1d2edbbe6fb -r 35598014f140 Framework/SQLite/SQLiteDatabase.h --- a/Framework/SQLite/SQLiteDatabase.h Thu Apr 08 12:00:01 2021 +0200 +++ b/Framework/SQLite/SQLiteDatabase.h Thu Apr 08 19:09:04 2021 +0200 @@ -53,11 +53,6 @@ } void Execute(const std::string& sql); - - bool DoesTableExist(const std::string& table) - { - return connection_.DoesTableExist(table.c_str()); - } int64_t GetLastInsertRowId() const { diff -r d1d2edbbe6fb -r 35598014f140 Framework/SQLite/SQLiteTransaction.cpp --- a/Framework/SQLite/SQLiteTransaction.cpp Thu Apr 08 12:00:01 2021 +0200 +++ b/Framework/SQLite/SQLiteTransaction.cpp Thu Apr 08 19:09:04 2021 +0200 @@ -30,6 +30,7 @@ namespace OrthancDatabases { SQLiteTransaction::SQLiteTransaction(SQLiteDatabase& database) : + database_(database), transaction_(database.GetObject()) { transaction_.Begin(); diff -r d1d2edbbe6fb -r 35598014f140 Framework/SQLite/SQLiteTransaction.h --- a/Framework/SQLite/SQLiteTransaction.h Thu Apr 08 12:00:01 2021 +0200 +++ b/Framework/SQLite/SQLiteTransaction.h Thu Apr 08 19:09:04 2021 +0200 @@ -35,6 +35,7 @@ class SQLiteTransaction : public ITransaction { private: + SQLiteDatabase& database_; Orthanc::SQLite::Transaction transaction_; public: @@ -60,5 +61,20 @@ virtual void ExecuteWithoutResult(IPrecompiledStatement& statement, const Dictionary& parameters) ORTHANC_OVERRIDE; + + virtual bool DoesTableExist(const std::string& name) ORTHANC_OVERRIDE + { + return database_.GetObject().DoesTableExist(name.c_str()); + } + + virtual bool DoesTriggerExist(const std::string& name) ORTHANC_OVERRIDE + { + return false; + } + + virtual void ExecuteMultiLines(const std::string& query) ORTHANC_OVERRIDE + { + database_.GetObject().Execute(query); + } }; } diff -r d1d2edbbe6fb -r 35598014f140 MySQL/Plugins/MySQLIndex.cpp --- a/MySQL/Plugins/MySQLIndex.cpp Thu Apr 08 12:00:01 2021 +0200 +++ b/MySQL/Plugins/MySQLIndex.cpp Thu Apr 08 19:09:04 2021 +0200 @@ -62,10 +62,8 @@ } - void MySQLIndex::ConfigureDatabase(IDatabase& database) + void MySQLIndex::ConfigureDatabase(DatabaseManager& manager) { - MySQLDatabase& db = dynamic_cast(database); - uint32_t expectedVersion = 6; if (GetContext()) // "GetContext()" can possibly be NULL in the unit tests @@ -92,6 +90,8 @@ MySQLDatabase::ClearDatabase(parameters_); } + MySQLDatabase& db = dynamic_cast(manager.GetDatabase()); + { MySQLDatabase::TransientAdvisoryLock lock(db, MYSQL_LOCK_DATABASE_SETUP); @@ -110,19 +110,21 @@ * https://groups.google.com/d/msg/orthanc-users/OCFFkm1qm0k/Mbroy8VWAQAJ **/ { - MySQLTransaction t(db, TransactionType_ReadWrite); + DatabaseManager::Transaction t(manager, TransactionType_ReadWrite); - db.Execute("ALTER DATABASE " + parameters_.GetDatabase() + - " CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci", false); + t.ExecuteMultiLines("ALTER DATABASE " + parameters_.GetDatabase() + + " CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"); // This is the first table to be created - if (!db.DoesTableExist(t, "GlobalProperties")) + if (!t.DoesTableExist("GlobalProperties")) { std::string query; Orthanc::EmbeddedResources::GetFileResource (query, Orthanc::EmbeddedResources::MYSQL_PREPARE_INDEX); - db.Execute(query, true); + + // Need to escape arobases: Don't use "t.ExecuteMultiLines()" here + db.ExecuteMultiLines(query, true); } t.Commit(); @@ -139,25 +141,25 @@ int version = 0; { - MySQLTransaction t(db, TransactionType_ReadWrite); + DatabaseManager::Transaction t(manager, TransactionType_ReadWrite); // This is the last table to be created - if (!db.DoesTableExist(t, "PatientRecyclingOrder")) + if (!t.DoesTableExist("PatientRecyclingOrder")) { LOG(ERROR) << "Corrupted MySQL database"; throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } // This is the last item to be created - if (!db.DoesTriggerExist(t, "PatientAdded")) + if (!t.DoesTriggerExist("PatientAdded")) { ThrowCannotCreateTrigger(); } - if (!LookupGlobalIntegerProperty(version, db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion)) + if (!LookupGlobalIntegerProperty(version, manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion)) { - SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion, expectedVersion); - SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, 1); + SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion, expectedVersion); + SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, 1); version = expectedVersion; } @@ -173,12 +175,12 @@ int revision = 0; { - MySQLTransaction t(db, TransactionType_ReadWrite); + DatabaseManager::Transaction t(manager, TransactionType_ReadWrite); - if (!LookupGlobalIntegerProperty(revision, db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel)) + if (!LookupGlobalIntegerProperty(revision, manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel)) { revision = 1; - SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision); + SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision); } t.Commit(); @@ -186,72 +188,76 @@ if (revision == 1) { - MySQLTransaction t(db, TransactionType_ReadWrite); + DatabaseManager::Transaction t(manager, TransactionType_ReadWrite); // The serialization of jobs as a global property can lead to // very long values => switch to the LONGTEXT type that can // store up to 4GB: // https://stackoverflow.com/a/13932834/881731 - db.Execute("ALTER TABLE GlobalProperties MODIFY value LONGTEXT", false); + t.ExecuteMultiLines("ALTER TABLE GlobalProperties MODIFY value LONGTEXT"); revision = 2; - SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision); + SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision); t.Commit(); } if (revision == 2) { - MySQLTransaction t(db, TransactionType_ReadWrite); + DatabaseManager::Transaction t(manager, TransactionType_ReadWrite); // Install the "GetLastChangeIndex" extension std::string query; Orthanc::EmbeddedResources::GetFileResource (query, Orthanc::EmbeddedResources::MYSQL_GET_LAST_CHANGE_INDEX); - db.Execute(query, true); - if (!db.DoesTriggerExist(t, "ChangeAdded")) + // Need to escape arobases: Don't use "t.ExecuteMultiLines()" here + db.ExecuteMultiLines(query, true); + + if (!t.DoesTriggerExist("ChangeAdded")) { ThrowCannotCreateTrigger(); } revision = 3; - SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision); + SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision); t.Commit(); } if (revision == 3) { - MySQLTransaction t(db, TransactionType_ReadWrite); + DatabaseManager::Transaction t(manager, TransactionType_ReadWrite); // Reconfiguration of "Metadata" from TEXT type (up to 64KB) // to the LONGTEXT type (up to 4GB). This might be important // for applications such as the Osimis Web viewer that stores // large amount of metadata. // http://book.orthanc-server.com/faq/features.html#central-registry-of-metadata-and-attachments - db.Execute("ALTER TABLE Metadata MODIFY value LONGTEXT", false); + t.ExecuteMultiLines("ALTER TABLE Metadata MODIFY value LONGTEXT"); revision = 4; - SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision); + SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision); t.Commit(); } if (revision == 4) { - MySQLTransaction t(db, TransactionType_ReadWrite); + DatabaseManager::Transaction t(manager, TransactionType_ReadWrite); // Install the "CreateInstance" extension std::string query; Orthanc::EmbeddedResources::GetFileResource (query, Orthanc::EmbeddedResources::MYSQL_CREATE_INSTANCE); - db.Execute(query, true); + + // Need to escape arobases: Don't use "t.ExecuteMultiLines()" here + db.ExecuteMultiLines(query, true); revision = 5; - SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision); + SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision); t.Commit(); } diff -r d1d2edbbe6fb -r 35598014f140 MySQL/Plugins/MySQLIndex.h --- a/MySQL/Plugins/MySQLIndex.h Thu Apr 08 12:00:01 2021 +0200 +++ b/MySQL/Plugins/MySQLIndex.h Thu Apr 08 19:09:04 2021 +0200 @@ -43,7 +43,7 @@ virtual IDatabase* OpenDatabaseConnection() ORTHANC_OVERRIDE; - virtual void ConfigureDatabase(IDatabase& database) ORTHANC_OVERRIDE; + virtual void ConfigureDatabase(DatabaseManager& database) ORTHANC_OVERRIDE; virtual int64_t CreateResource(DatabaseManager& manager, const char* publicId, diff -r d1d2edbbe6fb -r 35598014f140 MySQL/Plugins/MySQLStorageArea.cpp --- a/MySQL/Plugins/MySQLStorageArea.cpp Thu Apr 08 12:00:01 2021 +0200 +++ b/MySQL/Plugins/MySQLStorageArea.cpp Thu Apr 08 19:09:04 2021 +0200 @@ -59,13 +59,13 @@ if (clearAll) { - db.Execute("DROP TABLE IF EXISTS StorageArea", false); + db.ExecuteMultiLines("DROP TABLE IF EXISTS StorageArea", false); } - db.Execute("CREATE TABLE IF NOT EXISTS StorageArea(" - "uuid VARCHAR(64) NOT NULL PRIMARY KEY," - "content LONGBLOB NOT NULL," - "type INTEGER NOT NULL)", false); + db.ExecuteMultiLines("CREATE TABLE IF NOT EXISTS StorageArea(" + "uuid VARCHAR(64) NOT NULL PRIMARY KEY," + "content LONGBLOB NOT NULL," + "type INTEGER NOT NULL)", false); t.Commit(); } diff -r d1d2edbbe6fb -r 35598014f140 PostgreSQL/Plugins/PostgreSQLIndex.cpp --- a/PostgreSQL/Plugins/PostgreSQLIndex.cpp Thu Apr 08 12:00:01 2021 +0200 +++ b/PostgreSQL/Plugins/PostgreSQLIndex.cpp Thu Apr 08 19:09:04 2021 +0200 @@ -60,7 +60,7 @@ } - void PostgreSQLIndex::ConfigureDatabase(IDatabase& database) + void PostgreSQLIndex::ConfigureDatabase(DatabaseManager& manager) { uint32_t expectedVersion = 6; @@ -78,7 +78,7 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin); } - PostgreSQLDatabase& db = dynamic_cast(database); + PostgreSQLDatabase& db = dynamic_cast(manager.GetDatabase()); if (parameters_.HasLock()) { @@ -94,29 +94,29 @@ } { - PostgreSQLTransaction t(db, TransactionType_ReadWrite); + DatabaseManager::Transaction t(manager, TransactionType_ReadWrite); - if (!db.DoesTableExist("Resources")) + if (!t.DoesTableExist("Resources")) { std::string query; Orthanc::EmbeddedResources::GetFileResource (query, Orthanc::EmbeddedResources::POSTGRESQL_PREPARE_INDEX); - db.Execute(query); + t.ExecuteMultiLines(query); - SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion, expectedVersion); - SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, 1); - SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_HasTrigramIndex, 0); + SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion, expectedVersion); + SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, 1); + SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_HasTrigramIndex, 0); } - if (!db.DoesTableExist("Resources")) + if (!t.DoesTableExist("Resources")) { LOG(ERROR) << "Corrupted PostgreSQL database"; throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } int version = 0; - if (!LookupGlobalIntegerProperty(version, db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion) || + if (!LookupGlobalIntegerProperty(version, manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion) || version != 6) { LOG(ERROR) << "PostgreSQL plugin is incompatible with database schema version: " << version; @@ -124,10 +124,10 @@ } int revision; - if (!LookupGlobalIntegerProperty(revision, db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel)) + if (!LookupGlobalIntegerProperty(revision, manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel)) { revision = 1; - SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision); + SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision); } if (revision != 1) @@ -140,10 +140,10 @@ } { - PostgreSQLTransaction t(db, TransactionType_ReadWrite); + DatabaseManager::Transaction t(manager, TransactionType_ReadWrite); int hasTrigram = 0; - if (!LookupGlobalIntegerProperty(hasTrigram, db, t, MISSING_SERVER_IDENTIFIER, + if (!LookupGlobalIntegerProperty(hasTrigram, manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_HasTrigramIndex) || hasTrigram != 1) { @@ -163,11 +163,11 @@ LOG(WARNING) << "Trying to enable trigram matching on the PostgreSQL database " << "to speed up wildcard searches. This may take several minutes"; - db.Execute( + t.ExecuteMultiLines( "CREATE EXTENSION IF NOT EXISTS pg_trgm; " "CREATE INDEX DicomIdentifiersIndexValues2 ON DicomIdentifiers USING gin(value gin_trgm_ops);"); - SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_HasTrigramIndex, 1); + SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_HasTrigramIndex, 1); LOG(WARNING) << "Trigram index has been created"; t.Commit(); @@ -187,10 +187,10 @@ } { - PostgreSQLTransaction t(db, TransactionType_ReadWrite); + DatabaseManager::Transaction t(manager, TransactionType_ReadWrite); int property = 0; - if (!LookupGlobalIntegerProperty(property, db, t, MISSING_SERVER_IDENTIFIER, + if (!LookupGlobalIntegerProperty(property, manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_HasCreateInstance) || property != 2) { @@ -199,20 +199,20 @@ if (property == 1) { // Drop older, experimental versions of this extension - db.Execute("DROP FUNCTION CreateInstance(" - "IN patient TEXT, IN study TEXT, IN series TEXT, in instance TEXT)"); + t.ExecuteMultiLines("DROP FUNCTION CreateInstance(" + "IN patient TEXT, IN study TEXT, IN series TEXT, in instance TEXT)"); } std::string query; Orthanc::EmbeddedResources::GetFileResource (query, Orthanc::EmbeddedResources::POSTGRESQL_CREATE_INSTANCE); - db.Execute(query); + t.ExecuteMultiLines(query); - SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_HasCreateInstance, 2); + SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_HasCreateInstance, 2); } - if (!LookupGlobalIntegerProperty(property, db, t, MISSING_SERVER_IDENTIFIER, + if (!LookupGlobalIntegerProperty(property, manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_GetTotalSizeIsFast) || property != 1) { @@ -221,16 +221,16 @@ std::string query; Orthanc::EmbeddedResources::GetFileResource (query, Orthanc::EmbeddedResources::POSTGRESQL_FAST_TOTAL_SIZE); - db.Execute(query); + t.ExecuteMultiLines(query); - SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_GetTotalSizeIsFast, 1); + SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_GetTotalSizeIsFast, 1); } // Installing this extension requires the "GlobalIntegers" table // created by the "FastTotalSize" extension property = 0; - if (!LookupGlobalIntegerProperty(property, db, t, MISSING_SERVER_IDENTIFIER, + if (!LookupGlobalIntegerProperty(property, manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_HasFastCountResources) || property != 1) { @@ -239,16 +239,16 @@ std::string query; Orthanc::EmbeddedResources::GetFileResource (query, Orthanc::EmbeddedResources::POSTGRESQL_FAST_COUNT_RESOURCES); - db.Execute(query); + t.ExecuteMultiLines(query); - SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_HasFastCountResources, 1); + SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_HasFastCountResources, 1); } // Installing this extension requires the "GlobalIntegers" table // created by the "GetLastChangeIndex" extension property = 0; - if (!LookupGlobalIntegerProperty(property, db, t, MISSING_SERVER_IDENTIFIER, + if (!LookupGlobalIntegerProperty(property, manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_GetLastChangeIndex) || property != 1) { @@ -257,9 +257,23 @@ std::string query; Orthanc::EmbeddedResources::GetFileResource (query, Orthanc::EmbeddedResources::POSTGRESQL_GET_LAST_CHANGE_INDEX); - db.Execute(query); + t.ExecuteMultiLines(query); + + SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_GetLastChangeIndex, 1); + } + + t.Commit(); + } + - SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_GetLastChangeIndex, 1); + { + // New in release 4.0 to deal with multiple writers + DatabaseManager::Transaction t(manager, TransactionType_ReadWrite); + + if (!t.DoesTableExist("ServerProperties")) + { + t.ExecuteMultiLines("CREATE TABLE ServerProperties(server VARCHAR(64) NOT NULL, " + "property INTEGER, value TEXT, PRIMARY KEY(server, property))"); } t.Commit(); diff -r d1d2edbbe6fb -r 35598014f140 PostgreSQL/Plugins/PostgreSQLIndex.h --- a/PostgreSQL/Plugins/PostgreSQLIndex.h Thu Apr 08 12:00:01 2021 +0200 +++ b/PostgreSQL/Plugins/PostgreSQLIndex.h Thu Apr 08 19:09:04 2021 +0200 @@ -43,7 +43,7 @@ virtual IDatabase* OpenDatabaseConnection() ORTHANC_OVERRIDE; - virtual void ConfigureDatabase(IDatabase& database) ORTHANC_OVERRIDE; + virtual void ConfigureDatabase(DatabaseManager& manager) ORTHANC_OVERRIDE; virtual int64_t CreateResource(DatabaseManager& manager, const char* publicId, diff -r d1d2edbbe6fb -r 35598014f140 PostgreSQL/Plugins/PostgreSQLStorageArea.cpp --- a/PostgreSQL/Plugins/PostgreSQLStorageArea.cpp Thu Apr 08 12:00:01 2021 +0200 +++ b/PostgreSQL/Plugins/PostgreSQLStorageArea.cpp Thu Apr 08 19:09:04 2021 +0200 @@ -53,14 +53,14 @@ if (!db.DoesTableExist("StorageArea")) { - db.Execute("CREATE TABLE IF NOT EXISTS StorageArea(" - "uuid VARCHAR NOT NULL PRIMARY KEY," - "content OID NOT NULL," - "type INTEGER NOT NULL)"); + db.ExecuteMultiLines("CREATE TABLE IF NOT EXISTS StorageArea(" + "uuid VARCHAR NOT NULL PRIMARY KEY," + "content OID NOT NULL," + "type INTEGER NOT NULL)"); // Automatically remove the large objects associated with the table - db.Execute("CREATE OR REPLACE RULE StorageAreaDelete AS ON DELETE " - "TO StorageArea DO SELECT lo_unlink(old.content);"); + db.ExecuteMultiLines("CREATE OR REPLACE RULE StorageAreaDelete AS ON DELETE " + "TO StorageArea DO SELECT lo_unlink(old.content);"); } t.Commit(); diff -r d1d2edbbe6fb -r 35598014f140 PostgreSQL/UnitTests/PostgreSQLTests.cpp --- a/PostgreSQL/UnitTests/PostgreSQLTests.cpp Thu Apr 08 12:00:01 2021 +0200 +++ b/PostgreSQL/UnitTests/PostgreSQLTests.cpp Thu Apr 08 19:09:04 2021 +0200 @@ -86,7 +86,7 @@ std::unique_ptr pg(CreateTestDatabase()); ASSERT_FALSE(pg->DoesTableExist("Test")); - pg->Execute("CREATE TABLE Test(name INTEGER, value BIGINT)"); + pg->ExecuteMultiLines("CREATE TABLE Test(name INTEGER, value BIGINT)"); ASSERT_TRUE(pg->DoesTableExist("Test")); PostgreSQLStatement s(*pg, "INSERT INTO Test VALUES ($1,$2)"); @@ -158,7 +158,7 @@ { std::unique_ptr pg(CreateTestDatabase()); - pg->Execute("CREATE TABLE Test(name INTEGER, value VARCHAR(40))"); + pg->ExecuteMultiLines("CREATE TABLE Test(name INTEGER, value VARCHAR(40))"); PostgreSQLStatement s(*pg, "INSERT INTO Test VALUES ($1,$2)"); s.DeclareInputInteger(0); @@ -204,7 +204,7 @@ { std::unique_ptr pg(CreateTestDatabase()); - pg->Execute("CREATE TABLE Test(name INTEGER, value INTEGER)"); + pg->ExecuteMultiLines("CREATE TABLE Test(name INTEGER, value INTEGER)"); { PostgreSQLStatement s(*pg, "INSERT INTO Test VALUES ($1,$2)"); @@ -283,10 +283,10 @@ std::unique_ptr pg(CreateTestDatabase()); ASSERT_EQ(0, CountLargeObjects(*pg)); - pg->Execute("CREATE TABLE Test(name VARCHAR, value OID)"); + pg->ExecuteMultiLines("CREATE TABLE Test(name VARCHAR, value OID)"); // Automatically remove the large objects associated with the table - pg->Execute("CREATE RULE TestDelete AS ON DELETE TO Test DO SELECT lo_unlink(old.value);"); + pg->ExecuteMultiLines("CREATE RULE TestDelete AS ON DELETE TO Test DO SELECT lo_unlink(old.value);"); { PostgreSQLStatement s(*pg, "INSERT INTO Test VALUES ($1,$2)"); diff -r d1d2edbbe6fb -r 35598014f140 Resources/CMake/DatabasesPluginConfiguration.cmake --- a/Resources/CMake/DatabasesPluginConfiguration.cmake Thu Apr 08 12:00:01 2021 +0200 +++ b/Resources/CMake/DatabasesPluginConfiguration.cmake Thu Apr 08 19:09:04 2021 +0200 @@ -77,7 +77,6 @@ ${ORTHANC_CORE_SOURCES} ${ORTHANC_DATABASES_ROOT}/Framework/Plugins/DatabaseBackendAdapterV2.cpp ${ORTHANC_DATABASES_ROOT}/Framework/Plugins/DatabaseBackendAdapterV3.cpp - ${ORTHANC_DATABASES_ROOT}/Framework/Plugins/GlobalProperties.cpp ${ORTHANC_DATABASES_ROOT}/Framework/Plugins/IndexBackend.cpp ${ORTHANC_DATABASES_ROOT}/Framework/Plugins/StorageBackend.cpp ${ORTHANC_DATABASES_ROOT}/Resources/Orthanc/Databases/DatabaseConstraint.cpp diff -r d1d2edbbe6fb -r 35598014f140 SQLite/Plugins/SQLiteIndex.cpp --- a/SQLite/Plugins/SQLiteIndex.cpp Thu Apr 08 12:00:01 2021 +0200 +++ b/SQLite/Plugins/SQLiteIndex.cpp Thu Apr 08 19:09:04 2021 +0200 @@ -47,14 +47,25 @@ db->Open(path_); } + db->Execute("PRAGMA ENCODING=\"UTF-8\";"); + + if (fast_) + { + // Performance tuning of SQLite with PRAGMAs + // http://www.sqlite.org/pragma.html + db->Execute("PRAGMA SYNCHRONOUS=NORMAL;"); + db->Execute("PRAGMA JOURNAL_MODE=WAL;"); + db->Execute("PRAGMA LOCKING_MODE=EXCLUSIVE;"); + db->Execute("PRAGMA WAL_AUTOCHECKPOINT=1000;"); + //db->Execute("PRAGMA TEMP_STORE=memory"); + } + return db.release(); } - void SQLiteIndex::ConfigureDatabase(IDatabase& database) + void SQLiteIndex::ConfigureDatabase(DatabaseManager& manager) { - SQLiteDatabase& db = dynamic_cast(database); - uint32_t expectedVersion = 6; if (GetContext()) // "GetContext()" can possibly be NULL in the unit tests @@ -72,47 +83,35 @@ } { - SQLiteTransaction t(db); + DatabaseManager::Transaction t(manager, TransactionType_ReadWrite); - if (!db.DoesTableExist("Resources")) + if (!t.DoesTableExist("Resources")) { std::string query; Orthanc::EmbeddedResources::GetFileResource (query, Orthanc::EmbeddedResources::SQLITE_PREPARE_INDEX); - db.Execute(query); - - SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion, expectedVersion); - SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, 1); - } + + t.ExecuteMultiLines(query); + + SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion, expectedVersion); + SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, 1); + } t.Commit(); } - db.Execute("PRAGMA ENCODING=\"UTF-8\";"); + { + DatabaseManager::Transaction t(manager, TransactionType_ReadWrite); - if (fast_) - { - // Performance tuning of SQLite with PRAGMAs - // http://www.sqlite.org/pragma.html - db.Execute("PRAGMA SYNCHRONOUS=NORMAL;"); - db.Execute("PRAGMA JOURNAL_MODE=WAL;"); - db.Execute("PRAGMA LOCKING_MODE=EXCLUSIVE;"); - db.Execute("PRAGMA WAL_AUTOCHECKPOINT=1000;"); - //db.Execute("PRAGMA TEMP_STORE=memory"); - } - - { - SQLiteTransaction t(db); - - if (!db.DoesTableExist("Resources")) + if (!t.DoesTableExist("Resources")) { LOG(ERROR) << "Corrupted SQLite database"; throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } int version = 0; - if (!LookupGlobalIntegerProperty(version, db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion) || + if (!LookupGlobalIntegerProperty(version, manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion) || version != 6) { LOG(ERROR) << "SQLite plugin is incompatible with database schema version: " << version; @@ -120,10 +119,10 @@ } int revision; - if (!LookupGlobalIntegerProperty(revision, db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel)) + if (!LookupGlobalIntegerProperty(revision, manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel)) { revision = 1; - SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision); + SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision); } if (revision != 1) diff -r d1d2edbbe6fb -r 35598014f140 SQLite/Plugins/SQLiteIndex.h --- a/SQLite/Plugins/SQLiteIndex.h Thu Apr 08 12:00:01 2021 +0200 +++ b/SQLite/Plugins/SQLiteIndex.h Thu Apr 08 19:09:04 2021 +0200 @@ -44,7 +44,7 @@ virtual IDatabase* OpenDatabaseConnection() ORTHANC_OVERRIDE; - virtual void ConfigureDatabase(IDatabase& database) ORTHANC_OVERRIDE; + virtual void ConfigureDatabase(DatabaseManager& manager) ORTHANC_OVERRIDE; virtual int64_t CreateResource(DatabaseManager& manager, const char* publicId, diff -r d1d2edbbe6fb -r 35598014f140 SQLite/UnitTests/UnitTestsMain.cpp --- a/SQLite/UnitTests/UnitTestsMain.cpp Thu Apr 08 12:00:01 2021 +0200 +++ b/SQLite/UnitTests/UnitTestsMain.cpp Thu Apr 08 19:09:04 2021 +0200 @@ -65,8 +65,8 @@ OrthancDatabases::SQLiteDatabase db; db.OpenInMemory(); - ASSERT_FALSE(db.DoesTableExist("test")); - ASSERT_FALSE(db.DoesTableExist("test2")); + ASSERT_FALSE(db.GetObject().DoesTableExist("test")); + ASSERT_FALSE(db.GetObject().DoesTableExist("test2")); { std::unique_ptr t(db.CreateTransaction(OrthancDatabases::TransactionType_ReadWrite)); @@ -101,8 +101,8 @@ t->ExecuteWithoutResult(*s, args); } - ASSERT_TRUE(db.DoesTableExist("test")); - ASSERT_TRUE(db.DoesTableExist("test2")); + ASSERT_TRUE(db.GetObject().DoesTableExist("test")); + ASSERT_TRUE(db.GetObject().DoesTableExist("test2")); }