Mercurial > hg > orthanc-databases
changeset 20:aacb651833f5
merge
author | am@osimis.io |
---|---|
date | Tue, 10 Jul 2018 11:08:19 +0200 |
parents | 38e23471d132 (current diff) c7c54993a92e (diff) |
children | 2e5d2c69d4f9 |
files | PostgreSQL/Plugins/PostgreSQLIndex.cpp |
diffstat | 20 files changed, 526 insertions(+), 330 deletions(-) [+] |
line wrap: on
line diff
--- a/Framework/Common/DatabaseManager.cpp Tue Jul 10 11:05:37 2018 +0200 +++ b/Framework/Common/DatabaseManager.cpp Tue Jul 10 11:08:19 2018 +0200 @@ -307,8 +307,9 @@ const char* sql) : lock_(manager.mutex_), manager_(manager), + database_(manager_.GetDatabase()), location_(location), - transaction_(manager.GetTransaction()) + transaction_(manager_.GetTransaction()) { Setup(sql); } @@ -319,6 +320,7 @@ const char* sql) : lock_(manager_.mutex_), manager_(transaction.GetManager()), + database_(manager_.GetDatabase()), location_(location), transaction_(manager_.GetTransaction()) {
--- a/Framework/Common/DatabaseManager.h Tue Jul 10 11:05:37 2018 +0200 +++ b/Framework/Common/DatabaseManager.h Tue Jul 10 11:08:19 2018 +0200 @@ -123,6 +123,7 @@ private: boost::recursive_mutex::scoped_lock lock_; DatabaseManager& manager_; + IDatabase& database_; StatementLocation location_; ITransaction& transaction_; IPrecompiledStatement* statement_; @@ -142,6 +143,11 @@ Transaction& transaction, const char* sql); + IDatabase& GetDatabase() + { + return database_; + } + void SetReadOnly(bool readOnly); void SetParameterType(const std::string& parameter,
--- a/Framework/MySQL/MySQLDatabase.cpp Tue Jul 10 11:05:37 2018 +0200 +++ b/Framework/MySQL/MySQLDatabase.cpp Tue Jul 10 11:08:19 2018 +0200 @@ -145,38 +145,127 @@ Close(); throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); } + } - if (parameters_.HasLock()) - { - try - { - Query query("SELECT GET_LOCK('Lock', 0);", false); - MySQLStatement statement(*this, query); - MySQLTransaction t(*this); - Dictionary args; - - std::auto_ptr<IResult> result(t.Execute(statement, args)); + namespace + { + class ResultWrapper : public boost::noncopyable + { + private: + MYSQL_RES *result_; - if (result->IsDone() || - result->GetField(0).GetType() != ValueType_Integer64 || - dynamic_cast<const Integer64Value&>(result->GetField(0)).GetValue() != 1) + public: + ResultWrapper(MySQLDatabase& mysql, + const std::string& sql) : + result_(NULL) + { + if (mysql_real_query(mysql.GetObject(), sql.c_str(), sql.size())) { + mysql.LogError(); throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); } - t.Commit(); + result_ = mysql_use_result(mysql.GetObject()); + if (result_ == NULL) + { + mysql.LogError(); + throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); + } } - catch (Orthanc::OrthancException&) + + ~ResultWrapper() + { + if (result_ != NULL) + { + mysql_free_result(result_); + result_ = NULL; + } + } + + MYSQL_RES *GetObject() { - LOG(ERROR) << "The MySQL database is locked by another instance of Orthanc"; - Close(); - throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); + return result_; } + }; + } + + + bool MySQLDatabase::LookupGlobalStringVariable(std::string& value, + const std::string& variable) + { + ResultWrapper result(*this, "SELECT @@global." + variable); + + MYSQL_ROW row = mysql_fetch_row(result.GetObject()); + if (mysql_errno(mysql_) == 0 && + row && + row[0]) + { + value = std::string(row[0]); + return true; + } + else + { + return false; } } + bool MySQLDatabase::LookupGlobalIntegerVariable(int64_t& value, + const std::string& variable) + { + std::string s; + + if (LookupGlobalStringVariable(s, variable)) + { + try + { + value = boost::lexical_cast<int64_t>(s); + return true; + } + catch (boost::bad_lexical_cast&) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); + } + } + else + { + return false; + } + } + + + void MySQLDatabase::AdvisoryLock(int32_t lock) + { + try + { + Query query("SELECT GET_LOCK('Lock" + + boost::lexical_cast<std::string>(lock) + "', 0);", false); + MySQLStatement statement(*this, query); + + MySQLTransaction t(*this); + Dictionary args; + + std::auto_ptr<IResult> result(t.Execute(statement, args)); + + if (result->IsDone() || + result->GetField(0).GetType() != ValueType_Integer64 || + dynamic_cast<const Integer64Value&>(result->GetField(0)).GetValue() != 1) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); + } + + t.Commit(); + } + catch (Orthanc::OrthancException&) + { + LOG(ERROR) << "The MySQL database is locked by another instance of Orthanc"; + Close(); + throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); + } + } + + bool MySQLDatabase::DoesTableExist(MySQLTransaction& transaction, const std::string& name) { @@ -212,7 +301,8 @@ } - void MySQLDatabase::Execute(const std::string& sql) + void MySQLDatabase::Execute(const std::string& sql, + bool arobaseSeparator) { if (mysql_ == NULL) { @@ -231,8 +321,11 @@ if (!s.empty()) { - // Replace the escape character "@" by a semicolon - std::replace(s.begin(), s.end(), '@', ';'); + if (arobaseSeparator) + { + // Replace the escape character "@" by a semicolon + std::replace(s.begin(), s.end(), '@', ';'); + } LOG(TRACE) << "MySQL: " << s; CheckErrorCode(mysql_query(mysql_, s.c_str()));
--- a/Framework/MySQL/MySQLDatabase.h Tue Jul 10 11:05:37 2018 +0200 +++ b/Framework/MySQL/MySQLDatabase.h Tue Jul 10 11:08:19 2018 +0200 @@ -58,7 +58,16 @@ void Open(); - void Execute(const std::string& sql); + bool LookupGlobalStringVariable(std::string& value, + const std::string& variable); + + bool LookupGlobalIntegerVariable(int64_t& value, + const std::string& variable); + + void AdvisoryLock(int32_t lock); + + void Execute(const std::string& sql, + bool arobaseSeparator); bool DoesTableExist(MySQLTransaction& transaction, const std::string& name);
--- a/Framework/MySQL/MySQLResult.cpp Tue Jul 10 11:05:37 2018 +0200 +++ b/Framework/MySQL/MySQLResult.cpp Tue Jul 10 11:08:19 2018 +0200 @@ -36,9 +36,11 @@ if (code == 1) { unsigned int error = mysql_errno(database_.GetObject()); + if (error == 0) { - // This case can occur if the SQL request is not a SELECT + // This case occurs in requests without a result (e.g. if the + // SQL request is not a SELECT) done_ = true; } else if (error == CR_SERVER_GONE_ERROR ||
--- a/Framework/MySQL/MySQLTransaction.cpp Tue Jul 10 11:05:37 2018 +0200 +++ b/Framework/MySQL/MySQLTransaction.cpp Tue Jul 10 11:08:19 2018 +0200 @@ -35,7 +35,7 @@ readOnly_(true), active_(false) { - db_.Execute("START TRANSACTION"); + db_.Execute("START TRANSACTION", false); active_ = true; } @@ -48,7 +48,7 @@ try { - db_.Execute("ROLLBACK"); + db_.Execute("ROLLBACK", false); } catch (Orthanc::OrthancException&) { @@ -61,7 +61,7 @@ { if (active_) { - db_.Execute("ROLLBACK"); + db_.Execute("ROLLBACK", false); active_ = false; readOnly_ = true; } @@ -77,7 +77,7 @@ { if (active_) { - db_.Execute("COMMIT"); + db_.Execute("COMMIT", false); active_ = false; readOnly_ = true; }
--- a/Framework/Plugins/OrthancCppDatabasePlugin.h Tue Jul 10 11:05:37 2018 +0200 +++ b/Framework/Plugins/OrthancCppDatabasePlugin.h Tue Jul 10 11:08:19 2018 +0200 @@ -1504,7 +1504,7 @@ if (performanceWarning) { - OrthancPluginLogWarning(context, "Performance warning: The database plugin was compiled " + OrthancPluginLogWarning(context, "Performance warning: The database index plugin was compiled " "against an old version of the Orthanc SDK, consider upgrading"); }
--- a/Framework/Plugins/StorageBackend.cpp Tue Jul 10 11:05:37 2018 +0200 +++ b/Framework/Plugins/StorageBackend.cpp Tue Jul 10 11:08:19 2018 +0200 @@ -25,6 +25,9 @@ # error HAS_ORTHANC_EXCEPTION must be set to 1 #endif +#include "../../Framework/Common/BinaryStringValue.h" +#include "../../Framework/Common/FileValue.h" + #include <Core/OrthancException.h> @@ -111,6 +114,99 @@ } + void StorageBackend::Create(DatabaseManager::Transaction& transaction, + const std::string& uuid, + const void* content, + size_t size, + OrthancPluginContentType type) + { + DatabaseManager::CachedStatement statement( + STATEMENT_FROM_HERE, GetManager(), + "INSERT INTO StorageArea VALUES (${uuid}, ${content}, ${type})"); + + statement.SetParameterType("uuid", ValueType_Utf8String); + statement.SetParameterType("content", ValueType_File); + statement.SetParameterType("type", ValueType_Integer64); + + Dictionary args; + args.SetUtf8Value("uuid", uuid); + args.SetFileValue("content", content, size); + args.SetIntegerValue("type", type); + + statement.Execute(args); + } + + + void StorageBackend::Read(void*& content, + size_t& size, + DatabaseManager::Transaction& transaction, + const std::string& uuid, + OrthancPluginContentType type) + { + DatabaseManager::CachedStatement statement( + STATEMENT_FROM_HERE, GetManager(), + "SELECT content FROM StorageArea WHERE uuid=${uuid} AND type=${type}"); + + statement.SetParameterType("uuid", ValueType_Utf8String); + statement.SetParameterType("type", ValueType_Integer64); + + Dictionary args; + args.SetUtf8Value("uuid", uuid); + args.SetIntegerValue("type", type); + + statement.Execute(args); + + if (statement.IsDone()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); + } + else if (statement.GetResultFieldsCount() != 1) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); + } + else + { + const IValue& value = statement.GetResultField(0); + + switch (value.GetType()) + { + case ValueType_File: + ReadFromString(content, size, + dynamic_cast<const FileValue&>(value).GetContent()); + break; + + case ValueType_BinaryString: + ReadFromString(content, size, + dynamic_cast<const BinaryStringValue&>(value).GetContent()); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); + } + } + } + + + void StorageBackend::Remove(DatabaseManager::Transaction& transaction, + const std::string& uuid, + OrthancPluginContentType type) + { + DatabaseManager::CachedStatement statement( + STATEMENT_FROM_HERE, GetManager(), + "DELETE FROM StorageArea WHERE uuid=${uuid} AND type=${type}"); + + statement.SetParameterType("uuid", ValueType_Utf8String); + statement.SetParameterType("type", ValueType_Integer64); + + Dictionary args; + args.SetUtf8Value("uuid", uuid); + args.SetIntegerValue("type", type); + + statement.Execute(args); + } + + + static OrthancPluginContext* context_ = NULL; static std::auto_ptr<StorageBackend> backend_; @@ -182,6 +278,7 @@ { context_ = context; backend_.reset(backend); + backend_->GetManager().Open(); OrthancPluginRegisterStorageArea(context_, StorageCreate, StorageRead, StorageRemove); }
--- a/Framework/Plugins/StorageBackend.h Tue Jul 10 11:05:37 2018 +0200 +++ b/Framework/Plugins/StorageBackend.h Tue Jul 10 11:08:19 2018 +0200 @@ -56,17 +56,17 @@ const std::string& uuid, const void* content, size_t size, - OrthancPluginContentType type) = 0; + OrthancPluginContentType type); virtual void Read(void*& content, size_t& size, DatabaseManager::Transaction& transaction, const std::string& uuid, - OrthancPluginContentType type) = 0; + OrthancPluginContentType type); virtual void Remove(DatabaseManager::Transaction& transaction, const std::string& uuid, - OrthancPluginContentType type) = 0; + OrthancPluginContentType type); static void Register(OrthancPluginContext* context, StorageBackend* backend); // Takes ownership
--- a/MySQL/CMakeLists.txt Tue Jul 10 11:05:37 2018 +0200 +++ b/MySQL/CMakeLists.txt Tue Jul 10 11:08:19 2018 +0200 @@ -30,6 +30,7 @@ ) add_library(OrthancMySQLStorage SHARED + Plugins/MySQLStorageArea.cpp Plugins/StoragePlugin.cpp ${DATABASES_SOURCES} ${AUTOGENERATED_SOURCES} @@ -63,7 +64,9 @@ add_executable(UnitTests Plugins/MySQLIndex.cpp + Plugins/MySQLStorageArea.cpp UnitTests/UnitTestsMain.cpp + ${DATABASES_SOURCES} ${GOOGLE_TEST_SOURCES} ${AUTOGENERATED_SOURCES}
--- a/MySQL/Plugins/MySQLIndex.cpp Tue Jul 10 11:05:37 2018 +0200 +++ b/MySQL/Plugins/MySQLIndex.cpp Tue Jul 10 11:08:19 2018 +0200 @@ -82,39 +82,40 @@ db.Open(); MySQLTransaction t(db); - db.Execute("DROP DATABASE IF EXISTS " + database); - db.Execute("CREATE DATABASE " + database); + db.Execute("DROP DATABASE IF EXISTS " + database, false); + db.Execute("CREATE DATABASE " + database, false); t.Commit(); } std::auto_ptr<MySQLDatabase> db(new MySQLDatabase(parameters_)); db->Open(); - db->Execute("ALTER DATABASE " + parameters_.GetDatabase() + - " CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"); - db->Execute("SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE"); + + db->Execute("SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE", false); + if (parameters_.HasLock()) + { + db->AdvisoryLock(42 /* some arbitrary constant */); + } + { MySQLTransaction t(*db); + db->Execute("ALTER DATABASE " + parameters_.GetDatabase() + + " CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci", false); + if (!db->DoesTableExist(t, "Resources")) { std::string query; Orthanc::EmbeddedResources::GetFileResource (query, Orthanc::EmbeddedResources::MYSQL_PREPARE_INDEX); - db->Execute(query); + db->Execute(query, true); SetGlobalIntegerProperty(*db, t, Orthanc::GlobalProperty_DatabaseSchemaVersion, expectedVersion); SetGlobalIntegerProperty(*db, t, Orthanc::GlobalProperty_DatabasePatchLevel, 1); } - t.Commit(); - } - - { - MySQLTransaction t(*db); - if (!db->DoesTableExist(t, "Resources")) { LOG(ERROR) << "Corrupted MySQL database"; @@ -142,7 +143,7 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); } - t.Rollback(); + t.Commit(); } return db.release();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MySQL/Plugins/MySQLStorageArea.cpp Tue Jul 10 11:08:19 2018 +0200 @@ -0,0 +1,87 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "MySQLStorageArea.h" + +#include "../../Framework/MySQL/MySQLDatabase.h" +#include "../../Framework/MySQL/MySQLTransaction.h" + +#include <Core/Logging.h> + +#include <boost/math/special_functions/round.hpp> + + +namespace OrthancDatabases +{ + IDatabase* MySQLStorageArea::OpenInternal() + { + std::auto_ptr<MySQLDatabase> db(new MySQLDatabase(parameters_)); + + db->Open(); + + if (parameters_.HasLock()) + { + db->AdvisoryLock(43 /* some arbitrary constant */); + } + + { + MySQLTransaction t(*db); + + int64_t size; + if (db->LookupGlobalIntegerVariable(size, "max_allowed_packet")) + { + int mb = boost::math::iround(static_cast<double>(size) / + static_cast<double>(1024 * 1024)); + LOG(WARNING) << "Your MySQL server cannot " + << "store DICOM files larger than " << mb << "MB"; + LOG(WARNING) << " => Consider increasing \"max_allowed_packet\" " + << "in \"my.cnf\" if this limit is insufficient for your use"; + } + else + { + LOG(WARNING) << "Unable to auto-detect the maximum size of DICOM " + << "files that can be stored in this MySQL server"; + } + + if (clearAll_) + { + db->Execute("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); + + t.Commit(); + } + + return db.release(); + } + + + MySQLStorageArea::MySQLStorageArea(const MySQLParameters& parameters) : + StorageBackend(new Factory(*this)), + parameters_(parameters), + clearAll_(false) + { + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MySQL/Plugins/MySQLStorageArea.h Tue Jul 10 11:08:19 2018 +0200 @@ -0,0 +1,69 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../../Framework/Plugins/StorageBackend.h" +#include "../../Framework/MySQL/MySQLParameters.h" + + +namespace OrthancDatabases +{ + class MySQLStorageArea : public StorageBackend + { + private: + class Factory : public IDatabaseFactory + { + private: + MySQLStorageArea& that_; + + public: + Factory(MySQLStorageArea& that) : + that_(that) + { + } + + virtual Dialect GetDialect() const + { + return Dialect_MySQL; + } + + virtual IDatabase* Open() + { + return that_.OpenInternal(); + } + }; + + OrthancPluginContext* context_; + MySQLParameters parameters_; + bool clearAll_; + + IDatabase* OpenInternal(); + + public: + MySQLStorageArea(const MySQLParameters& parameters); + + void SetClearAll(bool clear) + { + clearAll_ = clear; + } + }; +}
--- a/MySQL/Plugins/PrepareIndex.sql Tue Jul 10 11:05:37 2018 +0200 +++ b/MySQL/Plugins/PrepareIndex.sql Tue Jul 10 11:08:19 2018 +0200 @@ -3,9 +3,6 @@ value TEXT ); --- Set GlobalProperty_DatabaseSchemaVersion -INSERT INTO GlobalProperties VALUES (1, '6'); - CREATE TABLE Resources( internalId BIGINT NOT NULL AUTO_INCREMENT, resourceType INTEGER NOT NULL,
--- a/MySQL/Plugins/StoragePlugin.cpp Tue Jul 10 11:05:37 2018 +0200 +++ b/MySQL/Plugins/StoragePlugin.cpp Tue Jul 10 11:08:19 2018 +0200 @@ -19,10 +19,9 @@ **/ +#include "MySQLStorageArea.h" #include "../../Framework/MySQL/MySQLDatabase.h" -#include "../../Framework/Plugins/StorageBackend.h" -#include <Plugins/Samples/Common/OrthancPluginCppWrapper.h> #include <Core/Logging.h> @@ -80,8 +79,9 @@ try { - // TODO - //OrthancDatabases::StorageBackend::Register(); + OrthancDatabases::MySQLParameters parameters(mysql); + OrthancDatabases::StorageBackend::Register + (context, new OrthancDatabases::MySQLStorageArea(parameters)); } catch (Orthanc::OrthancException& e) {
--- a/MySQL/UnitTests/UnitTestsMain.cpp Tue Jul 10 11:05:37 2018 +0200 +++ b/MySQL/UnitTests/UnitTestsMain.cpp Tue Jul 10 11:08:19 2018 +0200 @@ -20,11 +20,15 @@ #include "../Plugins/MySQLIndex.h" +#include "../Plugins/MySQLStorageArea.h" OrthancDatabases::MySQLParameters globalParameters_; +#include "../../Framework/Common/Integer64Value.h" +#include "../../Framework/MySQL/MySQLDatabase.h" +#include "../../Framework/MySQL/MySQLResult.h" +#include "../../Framework/MySQL/MySQLStatement.h" #include "../../Framework/Plugins/IndexUnitTests.h" -#include "../../Framework/MySQL/MySQLDatabase.h" #include <Core/Logging.h> @@ -56,21 +60,108 @@ } +static int64_t CountFiles(OrthancDatabases::MySQLDatabase& db) +{ + OrthancDatabases::Query query("SELECT COUNT(*) FROM StorageArea", true); + OrthancDatabases::MySQLStatement s(db, query); + OrthancDatabases::MySQLTransaction t(db); + OrthancDatabases::Dictionary d; + std::auto_ptr<OrthancDatabases::IResult> result(s.Execute(t, d)); + return dynamic_cast<const OrthancDatabases::Integer64Value&>(result->GetField(0)).GetValue(); +} + + +TEST(MySQL, StorageArea) +{ + OrthancDatabases::MySQLStorageArea storageArea(globalParameters_); + storageArea.SetClearAll(true); + + { + OrthancDatabases::DatabaseManager::Transaction transaction(storageArea.GetManager()); + OrthancDatabases::MySQLDatabase& db = + dynamic_cast<OrthancDatabases::MySQLDatabase&>(transaction.GetDatabase()); + + ASSERT_EQ(0, CountFiles(db)); + + for (int i = 0; i < 10; i++) + { + std::string uuid = boost::lexical_cast<std::string>(i); + std::string value = "Value " + boost::lexical_cast<std::string>(i * 2); + storageArea.Create(transaction, uuid, value.c_str(), value.size(), OrthancPluginContentType_Unknown); + } + + std::string tmp; + ASSERT_THROW(storageArea.ReadToString(tmp, transaction, "nope", OrthancPluginContentType_Unknown), + Orthanc::OrthancException); + + ASSERT_EQ(10, CountFiles(db)); + storageArea.Remove(transaction, "5", OrthancPluginContentType_Unknown); + + ASSERT_EQ(9, CountFiles(db)); + + for (int i = 0; i < 10; i++) + { + std::string uuid = boost::lexical_cast<std::string>(i); + std::string expected = "Value " + boost::lexical_cast<std::string>(i * 2); + std::string content; + + if (i == 5) + { + ASSERT_THROW(storageArea.ReadToString(content, transaction, uuid, OrthancPluginContentType_Unknown), + Orthanc::OrthancException); + } + else + { + storageArea.ReadToString(content, transaction, uuid, OrthancPluginContentType_Unknown); + ASSERT_EQ(expected, content); + } + } + + for (int i = 0; i < 10; i++) + { + storageArea.Remove(transaction, boost::lexical_cast<std::string>(i), + OrthancPluginContentType_Unknown); + } + + ASSERT_EQ(0, CountFiles(db)); + + transaction.Commit(); + } +} + + int main(int argc, char **argv) { if (argc < 5) { - std::cerr << "Usage: " << argv[0] << " <socket> <username> <password> <database>" + std::cerr << "Usage (UNIX): " << argv[0] << " <socket> <username> <password> <database>" + << std::endl + << "Usage (Windows): " << argv[0] << " <host> <port> <username> <password> <database>" << std::endl << std::endl - << "Example: " << argv[0] << " /var/run/mysqld/mysqld.sock root root orthanctest" + << "Example (UNIX): " << argv[0] << " /var/run/mysqld/mysqld.sock root root orthanctest" + << std::endl + << "Example (Windows): " << argv[0] << " localhost 3306 root root orthanctest" << std::endl << std::endl; return -1; } - globalParameters_.SetUnixSocket(argv[1]); - globalParameters_.SetUsername(argv[2]); - globalParameters_.SetPassword(argv[3]); - globalParameters_.SetDatabase(argv[4]); + if (argc == 5) + { + // UNIX + globalParameters_.SetUnixSocket(argv[1]); + globalParameters_.SetUsername(argv[2]); + globalParameters_.SetPassword(argv[3]); + globalParameters_.SetDatabase(argv[4]); + } + else + { + // Windows + globalParameters_.SetHost(argv[1]); + globalParameters_.SetPort(boost::lexical_cast<unsigned int>(argv[2])); + globalParameters_.SetUsername(argv[3]); + globalParameters_.SetPassword(argv[4]); + globalParameters_.SetDatabase(argv[5]); + } ::testing::InitGoogleTest(&argc, argv); Orthanc::Logging::Initialize();
--- a/PostgreSQL/Plugins/PostgreSQLIndex.cpp Tue Jul 10 11:05:37 2018 +0200 +++ b/PostgreSQL/Plugins/PostgreSQLIndex.cpp Tue Jul 10 11:08:19 2018 +0200 @@ -129,7 +129,10 @@ **/ try { - LOG(WARNING) << "Trying to enable trigram matching on the PostgreSQL database to speed up wildcard searches. This may take several minutes"; // we've observed 9 minutes on DB with 100000 studies + // We've observed 9 minutes on DB with 100000 studies + LOG(WARNING) << "Trying to enable trigram matching on the PostgreSQL database " + << "to speed up wildcard searches. This may take several minutes"; + db->Execute( "CREATE EXTENSION pg_trgm; " "CREATE INDEX DicomIdentifiersIndexValues2 ON DicomIdentifiers USING gin(value gin_trgm_ops);"); @@ -139,8 +142,10 @@ } catch (Orthanc::OrthancException&) { - LOG(WARNING) << "Performance warning: Your PostgreSQL server does not support trigram matching"; - LOG(WARNING) << "-> Consider installing the \"pg_trgm\" extension on the PostgreSQL server, e.g. on Debian: sudo apt install postgresql-contrib"; + LOG(WARNING) << "Performance warning: Your PostgreSQL server does " + << "not support trigram matching"; + LOG(WARNING) << "-> Consider installing the \"pg_trgm\" extension on the " + << "PostgreSQL server, e.g. on Debian: sudo apt install postgresql-contrib"; } }
--- a/PostgreSQL/Plugins/PostgreSQLStorageArea.cpp Tue Jul 10 11:05:37 2018 +0200 +++ b/PostgreSQL/Plugins/PostgreSQLStorageArea.cpp Tue Jul 10 11:08:19 2018 +0200 @@ -21,7 +21,6 @@ #include "PostgreSQLStorageArea.h" -#include "../../Framework/Common/FileValue.h" #include "../../Framework/PostgreSQL/PostgreSQLTransaction.h" #include <Plugins/Samples/Common/OrthancPluginCppWrapper.h> @@ -74,82 +73,4 @@ clearAll_(false) { } - - - void PostgreSQLStorageArea::Create(DatabaseManager::Transaction& transaction, - const std::string& uuid, - const void* content, - size_t size, - OrthancPluginContentType type) - { - DatabaseManager::CachedStatement statement( - STATEMENT_FROM_HERE, GetManager(), - "INSERT INTO StorageArea VALUES (${uuid}, ${content}, ${type})"); - - statement.SetParameterType("uuid", ValueType_Utf8String); - statement.SetParameterType("content", ValueType_File); - statement.SetParameterType("type", ValueType_Integer64); - - Dictionary args; - args.SetUtf8Value("uuid", uuid); - args.SetFileValue("content", content, size); - args.SetIntegerValue("type", type); - - statement.Execute(args); - } - - - void PostgreSQLStorageArea::Read(void*& content, - size_t& size, - DatabaseManager::Transaction& transaction, - const std::string& uuid, - OrthancPluginContentType type) - { - DatabaseManager::CachedStatement statement( - STATEMENT_FROM_HERE, GetManager(), - "SELECT content FROM StorageArea WHERE uuid=${uuid} AND type=${type}"); - - statement.SetParameterType("uuid", ValueType_Utf8String); - statement.SetParameterType("type", ValueType_Integer64); - - Dictionary args; - args.SetUtf8Value("uuid", uuid); - args.SetIntegerValue("type", type); - - statement.Execute(args); - - if (statement.IsDone()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); - } - else if (statement.GetResultFieldsCount() != 1 || - statement.GetResultField(0).GetType() != ValueType_File) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); - } - else - { - const FileValue& value = dynamic_cast<const FileValue&>(statement.GetResultField(0)); - ReadFromString(content, size, value.GetContent()); - } - } - - - void PostgreSQLStorageArea::Remove(DatabaseManager::Transaction& transaction, - const std::string& uuid, - OrthancPluginContentType type) - { - DatabaseManager::CachedStatement statement( - STATEMENT_FROM_HERE, GetManager(), - "DELETE FROM StorageArea WHERE uuid=${uuid} AND type=${type}"); - - statement.SetParameterType("uuid", ValueType_Utf8String); - statement.SetParameterType("type", ValueType_Integer64); - - Dictionary args; - args.SetUtf8Value("uuid", uuid); - args.SetIntegerValue("type", type); - - statement.Execute(args); - } }
--- a/PostgreSQL/Plugins/PostgreSQLStorageArea.h Tue Jul 10 11:05:37 2018 +0200 +++ b/PostgreSQL/Plugins/PostgreSQLStorageArea.h Tue Jul 10 11:08:19 2018 +0200 @@ -64,21 +64,5 @@ { clearAll_ = clear; } - - virtual void Create(DatabaseManager::Transaction& transaction, - const std::string& uuid, - const void* content, - size_t size, - OrthancPluginContentType type); - - virtual void Read(void*& content, - size_t& size, - DatabaseManager::Transaction& transaction, - const std::string& uuid, - OrthancPluginContentType type); - - virtual void Remove(DatabaseManager::Transaction& transaction, - const std::string& uuid, - OrthancPluginContentType type); }; }
--- a/PostgreSQL/Plugins/StoragePlugin.cpp Tue Jul 10 11:05:37 2018 +0200 +++ b/PostgreSQL/Plugins/StoragePlugin.cpp Tue Jul 10 11:08:19 2018 +0200 @@ -20,180 +20,9 @@ #include "../../Framework/Plugins/StorageBackend.h" - -#include "../../Framework/Common/FileValue.h" -#include "../../Framework/PostgreSQL/PostgreSQLDatabase.h" -#include "../../Framework/PostgreSQL/PostgreSQLLargeObject.h" -#include "../../Framework/PostgreSQL/PostgreSQLTransaction.h" - -#include <Plugins/Samples/Common/OrthancPluginCppWrapper.h> -#include <Core/Logging.h> - - -namespace OrthancDatabases -{ - class PostgreSQLStorageArea : public StorageBackend - { - private: - class Factory : public IDatabaseFactory - { - private: - PostgreSQLStorageArea& that_; - - public: - Factory(PostgreSQLStorageArea& that) : - that_(that) - { - } - - virtual Dialect GetDialect() const - { - return Dialect_PostgreSQL; - } - - virtual IDatabase* Open() - { - return that_.OpenInternal(); - } - }; - - OrthancPluginContext* context_; - PostgreSQLParameters parameters_; - bool clearAll_; - - IDatabase* OpenInternal() - { - std::auto_ptr<PostgreSQLDatabase> db(new PostgreSQLDatabase(parameters_)); - - db->Open(); - - if (parameters_.HasLock()) - { - db->AdvisoryLock(43 /* some arbitrary constant */); - } - - if (clearAll_) - { - db->ClearAll(); - } - - { - PostgreSQLTransaction t(*db); - - 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)"); - - // 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);"); - } - - t.Commit(); - } - - return db.release(); - } - - public: - PostgreSQLStorageArea(const PostgreSQLParameters& parameters) : - StorageBackend(new Factory(*this)), - parameters_(parameters), - clearAll_(false) - { - } +#include "PostgreSQLStorageArea.h" - void SetClearAll(bool clear) - { - clearAll_ = clear; - } - - - virtual void Create(DatabaseManager::Transaction& transaction, - const std::string& uuid, - const void* content, - size_t size, - OrthancPluginContentType type) - { - std::auto_ptr<FileValue> file(new FileValue(content, size)); - - { - DatabaseManager::CachedStatement statement( - STATEMENT_FROM_HERE, GetManager(), - "INSERT INTO StorageArea VALUES (${uuid}, ${content}, ${type})"); - - statement.SetParameterType("uuid", ValueType_Utf8String); - statement.SetParameterType("content", ValueType_File); - statement.SetParameterType("type", ValueType_Integer64); - - Dictionary args; - args.SetUtf8Value("uuid", uuid); - args.SetValue("content", file.release()); - args.SetIntegerValue("type", type); - - statement.Execute(args); - } - } - - - virtual void Read(void*& content, - size_t& size, - DatabaseManager::Transaction& transaction, - const std::string& uuid, - OrthancPluginContentType type) - { - DatabaseManager::CachedStatement statement( - STATEMENT_FROM_HERE, GetManager(), - "SELECT content FROM StorageArea WHERE uuid=$1 AND type=$2"); - - statement.SetParameterType("uuid", ValueType_Utf8String); - statement.SetParameterType("type", ValueType_Integer64); - - Dictionary args; - args.SetUtf8Value("uuid", uuid); - args.SetIntegerValue("type", type); - - statement.Execute(args); - - if (statement.IsDone()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); - } - else if (statement.GetResultFieldsCount() != 1 || - statement.GetResultField(0).GetType() != ValueType_File) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); - } - else - { - const FileValue& value = dynamic_cast<const FileValue&>(statement.GetResultField(0)); - ReadFromString(content, size, value.GetContent()); - } - } - - - virtual void Remove(DatabaseManager::Transaction& transaction, - const std::string& uuid, - OrthancPluginContentType type) - { - DatabaseManager::CachedStatement statement( - STATEMENT_FROM_HERE, GetManager(), - "DELETE FROM StorageArea WHERE uuid=${uuid} AND type=${type}"); - - statement.SetParameterType("uuid", ValueType_Utf8String); - statement.SetParameterType("type", ValueType_Integer64); - - Dictionary args; - args.SetUtf8Value("uuid", uuid); - args.SetIntegerValue("type", type); - - statement.Execute(args); - } - }; -} +#include <Core/Logging.h> static bool DisplayPerformanceWarning()