changeset 807:c86377097e82 pg-next-1099

merged default -> pg-next-1099
author Alain Mazy <am@orthanc.team>
date Tue, 14 Apr 2026 12:41:50 +0200
parents 412bede39cdb (current diff) b492e1535737 (diff)
children 99598fd743f5
files PostgreSQL/NEWS
diffstat 44 files changed, 497 insertions(+), 324 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Common/DatabaseManager.cpp	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/Common/DatabaseManager.cpp	Tue Apr 14 12:41:50 2026 +0200
@@ -102,16 +102,16 @@
     
     std::unique_ptr<IPrecompiledStatement> statement(GetDatabase().Compile(query));
       
-    IPrecompiledStatement* tmp = statement.get();
-    if (tmp == NULL)
+    if (statement.get() == NULL)
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
     }
-
-    assert(cachedStatements_.find(statementId) == cachedStatements_.end());
-    cachedStatements_[statementId] = statement.release();
-
-    return *tmp;
+    else
+    {
+      assert(cachedStatements_.find(statementId) == cachedStatements_.end());
+      cachedStatements_[statementId] = statement.release();
+      return *cachedStatements_[statementId];
+    }
   }
 
     
--- a/Framework/Common/DatabaseManager.h	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/Common/DatabaseManager.h	Tue Apr 14 12:41:50 2026 +0200
@@ -269,7 +269,7 @@
                           const std::string& sql,
                           const Query::Parameters& parametersTypes);
 
-      virtual ~StandaloneStatement();
+      virtual ~StandaloneStatement() ORTHANC_OVERRIDE;
 
       void Execute()
       {
--- a/Framework/Common/ImplicitTransaction.h	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/Common/ImplicitTransaction.h	Tue Apr 14 12:41:50 2026 +0200
@@ -53,7 +53,7 @@
   public:
     ImplicitTransaction();
 
-    virtual ~ImplicitTransaction();
+    virtual ~ImplicitTransaction() ORTHANC_OVERRIDE;
     
     virtual bool IsImplicit() const ORTHANC_OVERRIDE
     {
--- a/Framework/Common/ResultBase.h	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/Common/ResultBase.h	Tue Apr 14 12:41:50 2026 +0200
@@ -50,7 +50,7 @@
     void SetFieldsCount(size_t count);
     
   public:
-    virtual ~ResultBase()
+    virtual ~ResultBase() ORTHANC_OVERRIDE
     {
       ClearFields();
     }
--- a/Framework/MySQL/MySQLDatabase.cpp	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/MySQL/MySQLDatabase.cpp	Tue Apr 14 12:41:50 2026 +0200
@@ -163,7 +163,7 @@
     {
       // Fallback to TCP connection if no UNIX socket is provided
       unsigned int protocol = MYSQL_PROTOCOL_TCP;
-      mysql_options(mysql_, MYSQL_OPT_PROTOCOL, (unsigned int *) &protocol);
+      mysql_options(mysql_, MYSQL_OPT_PROTOCOL, reinterpret_cast<const void*>(&protocol));
     }
 
     if (parameters_.IsSsl())
@@ -172,15 +172,15 @@
       {
 #if (MYSQL_VERSION_ID > 50110 && MYSQL_VERSION_ID < 80000)  // Removed in MySQL client 8.0
         my_bool verifyCert = 1;
-        mysql_options(mysql_, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (void *) &verifyCert);
+        mysql_options(mysql_, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, reinterpret_cast<const void*>(&verifyCert));
 #endif
         
-        mysql_options(mysql_, MYSQL_OPT_SSL_CA, (void *)(parameters_.GetSslCaCertificates()));
+        mysql_options(mysql_, MYSQL_OPT_SSL_CA, reinterpret_cast<const void*>(parameters_.GetSslCaCertificates()));
       }
 
 #if (MYSQL_VERSION_ID > 50110 && MYSQL_VERSION_ID < 80000)  // Removed in MySQL client 8.0
       my_bool enforceTls = 1;
-      mysql_options(mysql_, MYSQL_OPT_SSL_ENFORCE, (void *) &enforceTls);
+      mysql_options(mysql_, MYSQL_OPT_SSL_ENFORCE, reinterpret_cast<const void*>(&enforceTls));
 #endif
     }
 
@@ -402,7 +402,7 @@
   }
 
 
-  bool MySQLDatabase::DoesTableExist(MySQLTransaction& transaction,
+  bool MySQLDatabase::DoesTableExist(const MySQLTransaction& transaction,
                                      const std::string& name)
   {
     if (mysql_ == NULL)
@@ -434,7 +434,7 @@
   }
 
 
-  bool MySQLDatabase::DoesDatabaseExist(MySQLTransaction& transaction,
+  bool MySQLDatabase::DoesDatabaseExist(const MySQLTransaction& transaction,
                                         const std::string& name)
   {
     if (mysql_ == NULL)
@@ -464,7 +464,7 @@
   }
 
 
-  bool MySQLDatabase::DoesTriggerExist(MySQLTransaction& transaction,
+  bool MySQLDatabase::DoesTriggerExist(const MySQLTransaction& transaction,
                                        const std::string& name)
   {
     if (mysql_ == NULL)
--- a/Framework/MySQL/MySQLDatabase.h	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/MySQL/MySQLDatabase.h	Tue Apr 14 12:41:50 2026 +0200
@@ -54,7 +54,7 @@
   public:
     explicit MySQLDatabase(const MySQLParameters& parameters);
 
-    virtual ~MySQLDatabase();
+    virtual ~MySQLDatabase() ORTHANC_OVERRIDE;
 
     void LogError();
 
@@ -88,13 +88,13 @@
     void ExecuteMultiLines(const std::string& sql,
                            bool arobaseSeparator);
 
-    bool DoesTableExist(MySQLTransaction& transaction,
+    bool DoesTableExist(const MySQLTransaction& transaction,
                         const std::string& name);
 
-    bool DoesDatabaseExist(MySQLTransaction& transaction,
+    bool DoesDatabaseExist(const MySQLTransaction& transaction,
                            const std::string& name);
 
-    bool DoesTriggerExist(MySQLTransaction& transaction,
+    bool DoesTriggerExist(const MySQLTransaction& transaction,
                           const std::string& name);
 
     virtual Dialect GetDialect() const ORTHANC_OVERRIDE
--- a/Framework/MySQL/MySQLStatement.cpp	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/MySQL/MySQLStatement.cpp	Tue Apr 14 12:41:50 2026 +0200
@@ -295,7 +295,7 @@
     MYSQL_RES*              metadata_;
       
   public:
-    ResultMetadata(MySQLDatabase& db,
+    ResultMetadata(const MySQLDatabase& db,
                    MySQLStatement& statement) :
       metadata_(NULL)
     {
@@ -405,7 +405,7 @@
     if (query.IsReadOnly())
     {
       unsigned long type = (unsigned long) CURSOR_TYPE_READ_ONLY;
-      mysql_stmt_attr_set(statement_, STMT_ATTR_CURSOR_TYPE, (void*) &type);
+      mysql_stmt_attr_set(statement_, STMT_ATTR_CURSOR_TYPE, reinterpret_cast<void*>(&type));
     }
   }
 
@@ -450,7 +450,7 @@
   }
 
 
-  IResult* MySQLStatement::Execute(ITransaction& transaction,
+  IResult* MySQLStatement::Execute(const ITransaction& transaction,
                                    const Dictionary& parameters)
   {
     std::list<long long int>  int64Parameters;
@@ -554,7 +554,7 @@
   }
 
 
-  void MySQLStatement::ExecuteWithoutResult(ITransaction& transaction,
+  void MySQLStatement::ExecuteWithoutResult(const ITransaction& transaction,
                                             const Dictionary& parameters)
   {
     std::unique_ptr<IResult> dummy(Execute(transaction, parameters));
--- a/Framework/MySQL/MySQLStatement.h	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/MySQL/MySQLStatement.h	Tue Apr 14 12:41:50 2026 +0200
@@ -50,7 +50,7 @@
     MySQLStatement(MySQLDatabase& db,
                    const Query& query);
 
-    virtual ~MySQLStatement();
+    virtual ~MySQLStatement() ORTHANC_OVERRIDE;
 
     MYSQL_STMT* GetObject();
 
@@ -61,10 +61,10 @@
 
     IValue* FetchResultField(size_t i);
 
-    IResult* Execute(ITransaction& transaction,
+    IResult* Execute(const ITransaction& transaction,
                      const Dictionary& parameters);
 
-    void ExecuteWithoutResult(ITransaction& transaction,
+    void ExecuteWithoutResult(const ITransaction& transaction,
                               const Dictionary& parameters);
   };
 }
--- a/Framework/MySQL/MySQLTransaction.h	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/MySQL/MySQLTransaction.h	Tue Apr 14 12:41:50 2026 +0200
@@ -42,7 +42,7 @@
     explicit MySQLTransaction(MySQLDatabase& db,
                               TransactionType type);
 
-    virtual ~MySQLTransaction();
+    virtual ~MySQLTransaction() ORTHANC_OVERRIDE;
 
     virtual bool IsImplicit() const ORTHANC_OVERRIDE
     {
--- a/Framework/Odbc/OdbcDatabase.h	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/Odbc/OdbcDatabase.h	Tue Apr 14 12:41:50 2026 +0200
@@ -48,7 +48,7 @@
     OdbcDatabase(OdbcEnvironment& environment,
                  const std::string& connectionString);
 
-    virtual ~OdbcDatabase();
+    virtual ~OdbcDatabase() ORTHANC_OVERRIDE;
 
     SQLHDBC GetHandle()
     {
--- a/Framework/Odbc/OdbcEnvironment.cpp	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/Odbc/OdbcEnvironment.cpp	Tue Apr 14 12:41:50 2026 +0200
@@ -44,7 +44,7 @@
     }
       
     /* We want ODBC 3 support */
-    if (!SQL_SUCCEEDED(SQLSetEnvAttr(handle_, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0)))
+    if (!SQL_SUCCEEDED(SQLSetEnvAttr(handle_, SQL_ATTR_ODBC_VERSION, reinterpret_cast<void*>(SQL_OV_ODBC3), 0)))
     {
       SQLFreeHandle(SQL_HANDLE_ENV, handle_);
       throw Orthanc::OrthancException(Orthanc::ErrorCode_Database,
--- a/Framework/Odbc/OdbcPreparedStatement.cpp	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/Odbc/OdbcPreparedStatement.cpp	Tue Apr 14 12:41:50 2026 +0200
@@ -31,6 +31,7 @@
 #include <Logging.h>
 #include <OrthancException.h>
 
+#include <cassert>
 #include <sqlext.h>
 
 
--- a/Framework/Odbc/OdbcResult.h	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/Odbc/OdbcResult.h	Tue Apr 14 12:41:50 2026 +0200
@@ -59,7 +59,7 @@
     OdbcResult(OdbcStatement& statement,
                Dialect dialect);
     
-    virtual ~OdbcResult();
+    virtual ~OdbcResult() ORTHANC_OVERRIDE;
       
     virtual void SetExpectedType(size_t field,
                                  ValueType type) ORTHANC_OVERRIDE;
--- a/Framework/Plugins/DatabaseBackendAdapterV3.cpp	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/Plugins/DatabaseBackendAdapterV3.cpp	Tue Apr 14 12:41:50 2026 +0200
@@ -645,7 +645,7 @@
     std::unique_ptr<Output>                          output_;
     
   public:
-    Transaction(IndexConnectionsPool& pool) :
+    explicit Transaction(IndexConnectionsPool& pool) :
       pool_(pool),
       accessor_(new IndexConnectionsPool::Accessor(pool)),
       output_(new Output)
@@ -677,7 +677,7 @@
                                                  uint32_t* target /* out */)
   {
     assert(target != NULL);
-    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<const DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
     return that.GetOutput().ReadAnswersCount(*target);
   }
 
@@ -687,7 +687,7 @@
                                                      uint32_t index)
   {
     assert(target != NULL);
-    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<const DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
     return that.GetOutput().ReadAnswerAttachment(*target, index);
   }
 
@@ -697,7 +697,7 @@
                                                  uint32_t index)
   {
     assert(target != NULL);
-    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<const DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
     return that.GetOutput().ReadAnswerChange(*target, index);
   }
 
@@ -711,7 +711,7 @@
     assert(group != NULL);
     assert(element != NULL);
     assert(value != NULL);
-    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<const DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
     return that.GetOutput().ReadAnswerDicomTag(*group, *element, *value, index);
   }
 
@@ -721,7 +721,7 @@
                                                            uint32_t index)
   {
     assert(target != NULL);
-    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<const DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
     return that.GetOutput().ReadAnswerExportedResource(*target, index);
   }
 
@@ -731,7 +731,7 @@
                                                 uint32_t index)
   {
     assert(target != NULL);
-    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<const DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
     return that.GetOutput().ReadAnswerInt32(*target, index);
   }
 
@@ -741,7 +741,7 @@
                                                 uint32_t index)
   {
     assert(target != NULL);
-    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<const DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
     return that.GetOutput().ReadAnswerInt64(*target, index);
   }
 
@@ -751,7 +751,7 @@
                                                            uint32_t index)
   {
     assert(target != NULL);
-    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<const DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
     return that.GetOutput().ReadAnswerMatchingResource(*target, index);
   }
 
@@ -763,7 +763,7 @@
   {
     assert(metadata != NULL);
     assert(value != NULL);
-    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<const DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
     return that.GetOutput().ReadAnswerMetadata(*metadata, *value, index);
   }
 
@@ -773,7 +773,7 @@
                                                  uint32_t index)
   {
     assert(target != NULL);
-    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<const DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
     return that.GetOutput().ReadAnswerString(*target, index);
   }
 
@@ -782,7 +782,7 @@
                                                 uint32_t* target /* out */)
   {
     assert(target != NULL);
-    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<const DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
     return that.GetOutput().ReadEventsCount(*target);
   }
 
@@ -792,7 +792,7 @@
                                           uint32_t index)
   {
     assert(event != NULL);
-    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<const DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction& that = *reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
     return that.GetOutput().ReadEvent(*event, index);
   }
 
@@ -944,7 +944,7 @@
   
   static OrthancPluginErrorCode Rollback(OrthancPluginDatabaseTransaction* transaction)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -959,7 +959,7 @@
   static OrthancPluginErrorCode Commit(OrthancPluginDatabaseTransaction* transaction,
                                        int64_t fileSizeDelta /* TODO - not used? */)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -976,7 +976,7 @@
                                               const OrthancPluginAttachment* attachment,
                                               int64_t revision)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -990,7 +990,7 @@
   
   static OrthancPluginErrorCode ClearChanges(OrthancPluginDatabaseTransaction* transaction)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1004,7 +1004,7 @@
   
   static OrthancPluginErrorCode ClearExportedResources(OrthancPluginDatabaseTransaction* transaction)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1019,7 +1019,7 @@
   static OrthancPluginErrorCode ClearMainDicomTags(OrthancPluginDatabaseTransaction* transaction,
                                                    int64_t resourceId)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1038,7 +1038,7 @@
                                                const char* hashSeries,
                                                const char* hashInstance)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1063,7 +1063,7 @@
                                                  int64_t id,
                                                  int32_t contentType)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1079,7 +1079,7 @@
                                                int64_t id,
                                                int32_t metadataType)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1094,7 +1094,7 @@
   static OrthancPluginErrorCode DeleteResource(OrthancPluginDatabaseTransaction* transaction,
                                                int64_t id)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1109,7 +1109,7 @@
   static OrthancPluginErrorCode GetAllMetadata(OrthancPluginDatabaseTransaction* transaction,
                                                int64_t id)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1132,7 +1132,7 @@
   static OrthancPluginErrorCode GetAllPublicIds(OrthancPluginDatabaseTransaction* transaction,
                                                 OrthancPluginResourceType resourceType)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1153,7 +1153,7 @@
                                                          uint64_t since,
                                                          uint64_t limit)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1174,7 +1174,7 @@
                                            int64_t since,
                                            uint32_t maxResults)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1193,7 +1193,7 @@
   static OrthancPluginErrorCode GetChildrenInternalId(OrthancPluginDatabaseTransaction* transaction,
                                                       int64_t id)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1213,7 +1213,7 @@
                                                     int64_t resourceId,
                                                     int32_t metadata)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1232,7 +1232,7 @@
   static OrthancPluginErrorCode GetChildrenPublicId(OrthancPluginDatabaseTransaction* transaction,
                                                     int64_t id)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1253,7 +1253,7 @@
                                                      int64_t since,
                                                      uint32_t maxResults)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1271,7 +1271,7 @@
   
   static OrthancPluginErrorCode GetLastChange(OrthancPluginDatabaseTransaction* transaction)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1286,7 +1286,7 @@
   static OrthancPluginErrorCode GetLastChangeIndex(OrthancPluginDatabaseTransaction* transaction,
                                                    int64_t* target)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1300,7 +1300,7 @@
   
   static OrthancPluginErrorCode GetLastExportedResource(OrthancPluginDatabaseTransaction* transaction)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1315,7 +1315,7 @@
   static OrthancPluginErrorCode GetMainDicomTags(OrthancPluginDatabaseTransaction* transaction,
                                                  int64_t id)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1330,7 +1330,7 @@
   static OrthancPluginErrorCode GetPublicId(OrthancPluginDatabaseTransaction* transaction,
                                             int64_t id)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1346,7 +1346,7 @@
                                                   uint64_t* target /* out */,
                                                   OrthancPluginResourceType resourceType)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1362,7 +1362,7 @@
                                                 OrthancPluginResourceType* target /* out */,
                                                 uint64_t resourceId)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1377,7 +1377,7 @@
   static OrthancPluginErrorCode GetTotalCompressedSize(OrthancPluginDatabaseTransaction* transaction,
                                                        uint64_t* target /* out */)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1392,7 +1392,7 @@
   static OrthancPluginErrorCode GetTotalUncompressedSize(OrthancPluginDatabaseTransaction* transaction,
                                                          uint64_t* target /* out */)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1408,7 +1408,7 @@
                                                 uint8_t* target,
                                                 uint64_t threshold)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1425,7 +1425,7 @@
                                                    uint8_t* target,
                                                    int64_t resourceId)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1442,7 +1442,7 @@
                                                    uint8_t* target,
                                                    int64_t resourceId)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1458,7 +1458,7 @@
   static OrthancPluginErrorCode ListAvailableAttachments(OrthancPluginDatabaseTransaction* transaction,
                                                          int64_t resourceId)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1479,7 +1479,7 @@
                                           OrthancPluginResourceType resourceType,
                                           const char* date)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1501,7 +1501,7 @@
                                                     const char* seriesInstanceUid,
                                                     const char* sopInstanceUid)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1519,7 +1519,7 @@
                                                  int64_t resourceId,
                                                  int32_t contentType)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1535,7 +1535,7 @@
                                                      const char* serverIdentifier,
                                                      int32_t property)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1558,7 +1558,7 @@
                                                int64_t id,
                                                int32_t metadata)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1581,7 +1581,7 @@
                                              int64_t* parentId /* out */,
                                              int64_t id)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1608,7 +1608,7 @@
                                                OrthancPluginResourceType* type /* out */,
                                                const char* publicId)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1636,7 +1636,7 @@
                                                 uint32_t limit,
                                                 uint8_t requestSomeInstanceId)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1664,7 +1664,7 @@
                                                         OrthancPluginResourceType* type /* out */,
                                                         const char* publicId)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1695,7 +1695,7 @@
                                                        uint8_t* patientAvailable,
                                                        int64_t* patientId)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1721,7 +1721,7 @@
                                                         int64_t* patientId,
                                                         int64_t patientIdToAvoid)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1747,7 +1747,7 @@
                                                   int32_t property,
                                                   const char* value)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1765,7 +1765,7 @@
                                             const char* value,
                                             int64_t revision)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1781,7 +1781,7 @@
                                                     int64_t id,
                                                     uint8_t isProtected)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
@@ -1801,7 +1801,7 @@
                                                     uint32_t countMetadata,
                                                     const OrthancPluginResourcesContentMetadata* metadata)
   {
-    DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
+    const DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
 
     try
     {
--- a/Framework/Plugins/DatabaseBackendAdapterV4.cpp	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/Plugins/DatabaseBackendAdapterV4.cpp	Tue Apr 14 12:41:50 2026 +0200
@@ -48,7 +48,9 @@
 namespace OrthancDatabases
 {
   static bool isBackendInUse_ = false;  // Only for sanity checks
-  static BaseIndexConnectionsPool* connectionPool_ = NULL;  // Only for the AuditLogHandler
+
+  // The AuditLogHandler necessitates the plugin to manage the resources associated with the pool of connections
+  static std::unique_ptr<BaseIndexConnectionsPool> connectionPool_;
 
   static Orthanc::DatabasePluginMessages::ResourceType Convert(OrthancPluginResourceType resourceType)
   {
@@ -129,63 +131,63 @@
     }
     
   public:
-    Output(Orthanc::DatabasePluginMessages::DeleteAttachment::Response& deleteAttachment)
+    explicit Output(Orthanc::DatabasePluginMessages::DeleteAttachment::Response& deleteAttachment)
     {
       Clear();
       deleteAttachment_ = &deleteAttachment;
     }
     
-    Output(Orthanc::DatabasePluginMessages::DeleteResource::Response& deleteResource)
+    explicit Output(Orthanc::DatabasePluginMessages::DeleteResource::Response& deleteResource)
     {
       Clear();
       deleteResource_ = &deleteResource;
     }
     
-    Output(Orthanc::DatabasePluginMessages::GetChanges::Response& getChanges)
+    explicit Output(Orthanc::DatabasePluginMessages::GetChanges::Response& getChanges)
     {
       Clear();
       getChanges_ = &getChanges;
     }
 
 #if ORTHANC_PLUGINS_HAS_CHANGES_EXTENDED == 1
-    Output(Orthanc::DatabasePluginMessages::GetChangesExtended::Response& getChangesExtended)
+    explicit Output(Orthanc::DatabasePluginMessages::GetChangesExtended::Response& getChangesExtended)
     {
       Clear();
       getChangesExtended_ = &getChangesExtended;
     }
 #endif
 
-    Output(Orthanc::DatabasePluginMessages::GetExportedResources::Response& getExportedResources)
+    explicit Output(Orthanc::DatabasePluginMessages::GetExportedResources::Response& getExportedResources)
     {
       Clear();
       getExportedResources_ = &getExportedResources;
     }
     
-    Output(Orthanc::DatabasePluginMessages::GetLastChange::Response& getLastChange)
+    explicit Output(Orthanc::DatabasePluginMessages::GetLastChange::Response& getLastChange)
     {
       Clear();
       getLastChange_ = &getLastChange;
     }
     
-    Output(Orthanc::DatabasePluginMessages::GetLastExportedResource::Response& getLastExportedResource)
+    explicit Output(Orthanc::DatabasePluginMessages::GetLastExportedResource::Response& getLastExportedResource)
     {
       Clear();
       getLastExportedResource_ = &getLastExportedResource;
     }
     
-    Output(Orthanc::DatabasePluginMessages::GetMainDicomTags::Response& getMainDicomTags)
+    explicit Output(Orthanc::DatabasePluginMessages::GetMainDicomTags::Response& getMainDicomTags)
     {
       Clear();
       getMainDicomTags_ = &getMainDicomTags;
     }
     
-    Output(Orthanc::DatabasePluginMessages::LookupAttachment::Response& lookupAttachment)
+    explicit Output(Orthanc::DatabasePluginMessages::LookupAttachment::Response& lookupAttachment)
     {
       Clear();
       lookupAttachment_ = &lookupAttachment;
     }
     
-    Output(Orthanc::DatabasePluginMessages::LookupResources::Response& lookupResources)
+    explicit Output(Orthanc::DatabasePluginMessages::LookupResources::Response& lookupResources)
     {
       Clear();
       lookupResources_ = &lookupResources;
@@ -581,9 +583,26 @@
       countValues += constraint.values().size();
     }
 
+    std::vector<size_t> valuesIndex;
+    valuesIndex.resize(request.lookup().size());
+
     std::vector<const char*> values;
     values.reserve(countValues);
 
+    for (int i = 0; i < request.lookup().size(); i++)
+    {
+      valuesIndex[i] = values.size();
+
+      const Orthanc::DatabasePluginMessages::DatabaseConstraint& constraint = request.lookup(i);
+
+      for (int j = 0; j < constraint.values().size(); j++)
+      {
+        values.push_back(constraint.values(j).c_str());
+      }
+    }
+
+    assert(values.size() == countValues);
+
     DatabaseConstraints lookup;
 
     for (int i = 0; i < request.lookup().size(); i++)
@@ -638,13 +657,7 @@
       }
       else
       {
-        c.values = &values[values.size()];
-            
-        for (int j = 0; j < constraint.values().size(); j++)
-        {
-          assert(values.size() < countValues);
-          values.push_back(constraint.values(j).c_str());
-        }
+        c.values = &values[valuesIndex[i]];
       }
 
       lookup.AddConstraint(new DatabaseConstraint(c));
@@ -1454,6 +1467,20 @@
                                             const void* requestData,
                                             uint64_t requestSize)
   {
+    if (rawPool == NULL ||
+        connectionPool_.get() == NULL ||
+        rawPool != connectionPool_.get())
+    {
+      LOG(ERROR) << "Internal error: Incorrect state for the pool of connections";
+      return OrthancPluginErrorCode_InternalError;
+    }
+
+    if (!isBackendInUse_)
+    {
+      LOG(ERROR) << "More than one index backend was registered, internal error";
+      return OrthancPluginErrorCode_InternalError;
+    }
+
     Orthanc::DatabasePluginMessages::Request request;
     if (!request.ParseFromArray(requestData, requestSize))
     {
@@ -1461,14 +1488,6 @@
       return OrthancPluginErrorCode_InternalError;
     }
 
-    if (rawPool == NULL)
-    {
-      LOG(ERROR) << "Received a NULL pointer from the database";
-      return OrthancPluginErrorCode_InternalError;
-    }
-
-    BaseIndexConnectionsPool& pool = *reinterpret_cast<BaseIndexConnectionsPool*>(rawPool);
-
     try
     {
       Orthanc::DatabasePluginMessages::Response response;
@@ -1476,12 +1495,12 @@
       switch (request.type())
       {
         case Orthanc::DatabasePluginMessages::REQUEST_DATABASE:
-          ProcessDatabaseOperation(*response.mutable_database_response(), request.database_request(), pool);
+          ProcessDatabaseOperation(*response.mutable_database_response(), request.database_request(), *connectionPool_);
           break;
           
         case Orthanc::DatabasePluginMessages::REQUEST_TRANSACTION:
         {
-          BaseIndexConnectionsPool::Accessor& transaction = *reinterpret_cast<BaseIndexConnectionsPool::Accessor*>(request.transaction_request().transaction());
+          const BaseIndexConnectionsPool::Accessor& transaction = *reinterpret_cast<const BaseIndexConnectionsPool::Accessor*>(request.transaction_request().transaction());
           ProcessTransactionOperation(*response.mutable_transaction_response(), request.transaction_request(),
                                       transaction.GetBackend(), transaction.GetManager());
           break;
@@ -1498,7 +1517,7 @@
         throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Cannot serialize to protobuf");
       }
 
-      if (OrthancPluginCreateMemoryBuffer64(pool.GetContext(), serializedResponse, s.size()) != OrthancPluginErrorCode_Success)
+      if (OrthancPluginCreateMemoryBuffer64(connectionPool_->GetContext(), serializedResponse, s.size()) != OrthancPluginErrorCode_Success)
       {
         throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory, "Cannot allocate a memory buffer");
       }
@@ -1537,26 +1556,23 @@
 
   static void FinalizeBackend(void* rawPool)
   {
-    if (rawPool != NULL)
+    if (rawPool == NULL ||
+        connectionPool_.get() == NULL ||
+        connectionPool_.get() != rawPool)
     {
-      BaseIndexConnectionsPool* pool = reinterpret_cast<BaseIndexConnectionsPool*>(rawPool);
-      
-      if (isBackendInUse_)
-      {
-        isBackendInUse_ = false;
-        connectionPool_ = NULL;
-      }
-      else
-      {
-        LOG(ERROR) << "More than one index backend was registered, internal error";
-      }
+      LOG(ERROR) << "Internal error: Incorrect state for the pool of connections";
+    }
 
-      delete pool;
+    if (isBackendInUse_)
+    {
+      isBackendInUse_ = false;
     }
     else
     {
-      LOG(ERROR) << "Received a null pointer from the Orthanc core, internal error";
+      LOG(ERROR) << "More than one index backend was registered, internal error";
     }
+
+    connectionPool_.reset(NULL);
   }
 
 
@@ -1569,7 +1585,7 @@
                                          uint32_t                  logDataSize)
   {
     if (!isBackendInUse_ ||
-        connectionPool_ == NULL)
+        connectionPool_.get() == NULL)
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
@@ -1785,29 +1801,28 @@
                                           unsigned int maxDatabaseRetries,
                                           unsigned int housekeepingDelaySeconds)
   {
-    std::unique_ptr<BaseIndexConnectionsPool> pool;
-
+    // The "connectionPool_" takes the ownership of the backend
     if (useDynamicConnectionPool)
     {
-      pool.reset(new DynamicIndexConnectionsPool(backend, countConnections, housekeepingDelaySeconds));
+      connectionPool_.reset(new DynamicIndexConnectionsPool(backend, countConnections, housekeepingDelaySeconds));
     }
     else
     {
-      pool.reset(new IndexConnectionsPool(backend, countConnections, housekeepingDelaySeconds));
+      connectionPool_.reset(new IndexConnectionsPool(backend, countConnections, housekeepingDelaySeconds));
     }
     
     if (isBackendInUse_)
     {
+      connectionPool_.reset();  // This implies "delete backend"
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
 
     OrthancPluginContext* context = backend->GetContext();
-    connectionPool_ = pool.get(); // we need to keep a pointer on the connectionPool for the static Audit log handler
  
-    if (OrthancPluginRegisterDatabaseBackendV4(context, pool.release(), maxDatabaseRetries,
+    if (OrthancPluginRegisterDatabaseBackendV4(context, connectionPool_.get(), maxDatabaseRetries,
                                                CallBackend, FinalizeBackend) != OrthancPluginErrorCode_Success)
     {
-      delete backend;
+      connectionPool_.reset();  // This implies "delete backend"
       throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Unable to register the database backend");
     }
 
--- a/Framework/Plugins/DynamicIndexConnectionsPool.h	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/Plugins/DynamicIndexConnectionsPool.h	Tue Apr 14 12:41:50 2026 +0200
@@ -59,7 +59,7 @@
                                 size_t maxConnectionsCount,
                                 unsigned int houseKeepingDelaySeconds);
 
-    virtual ~DynamicIndexConnectionsPool();
+    virtual ~DynamicIndexConnectionsPool() ORTHANC_OVERRIDE;
 
     virtual void OpenConnections(bool hasIdentifierTags,
                                  const std::list<IdentifierTag>& identifierTags) ORTHANC_OVERRIDE;
--- a/Framework/Plugins/ISqlLookupFormatter.cpp	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/Plugins/ISqlLookupFormatter.cpp	Tue Apr 14 12:41:50 2026 +0200
@@ -135,7 +135,7 @@
         }
         else
         {
-          comparison = "lower(" + tag + ".value) " + op + " lower(" + parameter + ")";
+          comparison = formatter.FormatLower(tag + ".value") + op + formatter.FormatLower(parameter);
         }
 
         break;
@@ -158,7 +158,7 @@
           }
           else
           {
-            comparison += "lower(" + parameter + ")";
+            comparison += formatter.FormatLower(parameter);
           }
         }
 
@@ -168,7 +168,7 @@
         }
         else
         {
-          comparison = "lower(" +  tag + ".value) IN (" + comparison + ")";
+          comparison = formatter.FormatLower(tag + ".value") + " IN (" + comparison + ")";
         }
 
         break;
@@ -188,57 +188,11 @@
         }
         else
         {
-          std::string escaped;
-          escaped.reserve(value.size());
-
-          for (size_t i = 0; i < value.size(); i++)
-          {
-            if (value[i] == '*')
-            {
-              escaped += "%";
-            }
-            else if (value[i] == '?')
-            {
-              escaped += "_";
-            }
-            else if (value[i] == '%')
-            {
-              escaped += "\\%";
-            }
-            else if (value[i] == '_')
-            {
-              escaped += "\\_";
-            }
-            else if (value[i] == '\\')
-            {
-              escaped += "\\\\";
-            }
-            else if (escapeBrackets && value[i] == '[')
-            {
-              escaped += "\\[";
-            }
-            else if (escapeBrackets && value[i] == ']')
-            {
-              escaped += "\\]";
-            }
-            else
-            {
-              escaped += value[i];
-            }
-          }
-
+          std::string escaped = formatter.FormatWildcardsForLike(value);
           std::string parameter = formatter.GenerateParameter(escaped);
-
-          if (isCaseSensitive)
-          {
-            comparison = (tag + ".value LIKE " + parameter + " " +
-                          formatter.FormatWildcardEscape());
-          }
-          else
-          {
-            comparison = ("lower(" + tag + ".value) LIKE lower(" +
-                          parameter + ") " + formatter.FormatWildcardEscape());
-          }
+          comparison = formatter.FormatLike(isCaseSensitive,
+                                            tag + ".value",
+                                            parameter);
         }
 
         break;
@@ -553,7 +507,7 @@
         }
         else
         {
-          comparison = " AND lower(value) " + op + " lower(" + parameter + ")";
+          comparison = " AND " + formatter.FormatLower("value") + op + formatter.FormatLower(parameter);
         }
 
         break;
@@ -572,7 +526,7 @@
           }
           else
           {
-            comparisonValues.push_back("lower(" + parameter + ")");
+            comparisonValues.push_back(formatter.FormatLower(parameter));
           }
         }
 
@@ -584,7 +538,7 @@
         }
         else
         {
-          comparison = " AND lower(value) IN (" + values + ")";
+          comparison = " AND " + formatter.FormatLower("value") + " IN (" + values + ")";
         }
 
         break;
@@ -604,55 +558,11 @@
         }
         else
         {
-          std::string escaped;
-          escaped.reserve(value.size());
-
-          for (size_t i = 0; i < value.size(); i++)
-          {
-            if (value[i] == '*')
-            {
-              escaped += "%";
-            }
-            else if (value[i] == '?')
-            {
-              escaped += "_";
-            }
-            else if (value[i] == '%')
-            {
-              escaped += "\\%";
-            }
-            else if (value[i] == '_')
-            {
-              escaped += "\\_";
-            }
-            else if (value[i] == '\\')
-            {
-              escaped += "\\\\";
-            }
-            else if (escapeBrackets && value[i] == '[')
-            {
-              escaped += "\\[";
-            }
-            else if (escapeBrackets && value[i] == ']')
-            {
-              escaped += "\\]";
-            }
-            else
-            {
-              escaped += value[i];
-            }
-          }
-
+          std::string escaped = formatter.FormatWildcardsForLike(value);
           std::string parameter = formatter.GenerateParameter(escaped);
-
-          if (constraint.IsCaseSensitive())
-          {
-            comparison = " AND value LIKE " + parameter + " " + formatter.FormatWildcardEscape();
-          }
-          else
-          {
-            comparison = " AND lower(value) LIKE lower(" + parameter + ") " + formatter.FormatWildcardEscape();
-          }
+          comparison = " AND " + formatter.FormatLike(constraint.IsCaseSensitive(),
+                                                      "value",
+                                                      parameter);
         }
 
         break;
@@ -811,6 +721,10 @@
       where.push_back("(SELECT COUNT(1) FROM Labels AS selectedLabels WHERE selectedLabels.id = " + FormatLevel(queryLevel) +
                       ".internalId AND selectedLabels.label IN (" + Join(formattedLabels, "", ", ") + ")) " + condition);
     }
+    else if (labelsConstraint == LabelsConstraint_None) // from 1.12.11, 'None' with an empty labels list means "list all resources without any labels"
+    {
+      where.push_back("(SELECT COUNT(1) FROM Labels WHERE id = " + FormatLevel(queryLevel) + ".internalId) = 0");
+    }
 
     sql += joins + Join(where, " WHERE ", " AND ");
 
@@ -893,7 +807,7 @@
     assert(upperLevel <= queryLevel &&
            queryLevel <= lowerLevel);
 
-    std::string ordering;
+    std::string orderingSql;
     std::string orderingJoins;
 
     if (request.ordering_size() > 0)
@@ -955,22 +869,22 @@
 
       if (formatter.SupportsNullsLast())
       {
-        ordering = "ROW_NUMBER() OVER (ORDER BY " + orderByFieldsString + " NULLS LAST) AS rowNumber";
+        orderingSql = "ROW_NUMBER() OVER (ORDER BY " + orderByFieldsString + " NULLS LAST) AS rowNumber";
       }
       else
       {
-        ordering = "ROW_NUMBER() OVER (ORDER BY " + orderByFieldsString + ") AS rowNumber";
+        orderingSql = "ROW_NUMBER() OVER (ORDER BY " + orderByFieldsString + ") AS rowNumber";
       }
     }
     else
     {
-      ordering = "ROW_NUMBER() OVER (ORDER BY " + strQueryLevel + ".publicId) AS rowNumber";  // we need a default ordering in order to make default queries repeatable when using since&limit
+      orderingSql = "ROW_NUMBER() OVER (ORDER BY " + strQueryLevel + ".publicId) AS rowNumber";  // we need a default ordering in order to make default queries repeatable when using since&limit
     }
 
     sql = ("SELECT " +
            strQueryLevel + ".publicId, " +
            strQueryLevel + ".internalId, " +
-           ordering +
+           orderingSql +
            " FROM Resources AS " + strQueryLevel);
 
 
@@ -1126,6 +1040,10 @@
       where.push_back("(SELECT COUNT(1) FROM Labels AS selectedLabels WHERE selectedLabels.id = " + strQueryLevel +
                       ".internalId AND selectedLabels.label IN (" + Join(formattedLabels, "", ", ") + ")) " + condition);
     }
+    else if (request.labels_constraint() == Orthanc::DatabasePluginMessages::LABELS_CONSTRAINT_NONE) // from 1.12.11, 'None' with an empty labels list means "list all resources without any labels"
+    {
+      where.push_back("(SELECT COUNT(1) FROM Labels WHERE id = " + FormatLevel(queryLevel) + ".internalId) = 0");
+    }
 
     sql += joins + orderingJoins + Join(where, " WHERE ", " AND ");
 
@@ -1245,6 +1163,11 @@
                                         ") AS temp "
                                  " WHERE labelsCount " + condition + ")");
     }
+    else if (labelsConstraint == LabelsConstraint_None) // from 1.12.11, 'None' with an empty labels list means "list all resources without any labels"
+    {
+      sql += (" AND (SELECT COUNT(1) FROM Labels WHERE id = internalId) = 0");
+    }
+
 
     if (limit != 0)
     {
--- a/Framework/Plugins/ISqlLookupFormatter.h	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/Plugins/ISqlLookupFormatter.h	Tue Apr 14 12:41:50 2026 +0200
@@ -67,6 +67,12 @@
 
     virtual std::string FormatNull(const char* type) = 0;
 
+    virtual std::string FormatLike(bool isCaseSensitive, const std::string& a, const std::string& b) = 0;
+
+    virtual std::string FormatWildcardsForLike(const std::string& value) = 0;
+
+    virtual std::string FormatLower(const std::string& value) = 0;
+
     /**
      * Whether to escape '[' and ']', which is only needed for
      * MSSQL. New in Orthanc 1.10.0, from the following changeset:
--- a/Framework/Plugins/IndexBackend.cpp	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/Plugins/IndexBackend.cpp	Tue Apr 14 12:41:50 2026 +0200
@@ -34,6 +34,10 @@
 #include <OrthancException.h>
 #include <Toolbox.h>
 
+#if ORTHANC_FRAMEWORK_VERSION_IS_ABOVE(1, 12, 11)
+#  include <ElapsedTimer.h>
+#endif
+
 #include <boost/algorithm/string/join.hpp>
 
 
@@ -48,10 +52,15 @@
   }
 
 
-  static std::string ConvertWildcardToLike(const std::string& query)
+  static std::string ConvertWildcardToLike(const std::string& query, Dialect dialect)
   {
     std::string s = query;
 
+    if (dialect == Dialect_SQLite)
+    {
+      return s; // we are actually using GLOB that keeps the 'Unix' like wildcards
+    }
+
     for (size_t i = 0; i < s.size(); i++)
     {
       if (s[i] == '*')
@@ -158,7 +167,7 @@
 
   void IndexBackend::ReadChangesInternal(IDatabaseBackendOutput& output,
                                          bool& done,
-                                         DatabaseManager& manager,
+                                         const DatabaseManager& manager,
                                          DatabaseManager::CachedStatement& statement,
                                          const Dictionary& args,
                                          uint32_t limit,
@@ -1394,25 +1403,25 @@
       case OrthancPluginIdentifierConstraint_Equal:
         header += "d.value = ${value}";
         statement.reset(new DatabaseManager::CachedStatement(
-                          STATEMENT_FROM_HERE, manager, header.c_str()));
+                          STATEMENT_FROM_HERE, manager, header));
         break;
         
       case OrthancPluginIdentifierConstraint_SmallerOrEqual:
         header += "d.value <= ${value}";
         statement.reset(new DatabaseManager::CachedStatement(
-                          STATEMENT_FROM_HERE, manager, header.c_str()));
+                          STATEMENT_FROM_HERE, manager, header));
         break;
         
       case OrthancPluginIdentifierConstraint_GreaterOrEqual:
         header += "d.value >= ${value}";
         statement.reset(new DatabaseManager::CachedStatement(
-                          STATEMENT_FROM_HERE, manager, header.c_str()));
+                          STATEMENT_FROM_HERE, manager, header));
         break;
         
       case OrthancPluginIdentifierConstraint_Wildcard:
         header += "d.value LIKE ${value}";
         statement.reset(new DatabaseManager::CachedStatement(
-                          STATEMENT_FROM_HERE, manager, header.c_str()));
+                          STATEMENT_FROM_HERE, manager, header));
         break;
         
       default:
@@ -1432,7 +1441,7 @@
 
     if (constraint == OrthancPluginIdentifierConstraint_Wildcard)
     {
-      args.SetUtf8Value("value", ConvertWildcardToLike(value));
+      args.SetUtf8Value("value", ConvertWildcardToLike(value, manager.GetDialect()));
     }
     else
     {
@@ -2226,6 +2235,114 @@
       }
     }
 
+    virtual std::string FormatLower(const std::string& value)
+    {
+      switch (dialect_)
+      {
+        case Dialect_SQLite:
+        {
+          return " lower_with_accents(" + value + ") ";
+        };
+        default:
+          return " lower(" + value + ") ";
+      }
+    }
+
+    virtual std::string FormatWildcardsForLike(const std::string& value)
+    {
+      bool escapeBrackets = IsEscapeBrackets();
+      std::string escaped;
+      escaped.reserve(value.size());
+
+      switch (dialect_)
+      {
+        case Dialect_SQLite:
+        {
+          escaped = value;  // SQLite uses GLOB instead of LIKE -> no need for escaping and we keep the 'Unix' like wildcards
+        }; break;
+        default:
+          for (size_t i = 0; i < value.size(); i++)
+          {
+            if (value[i] == '*')
+            {
+              escaped += "%";
+            }
+            else if (value[i] == '?')
+            {
+              escaped += "_";
+            }
+            else if (value[i] == '%')
+            {
+              escaped += "\\%";
+            }
+            else if (value[i] == '_')
+            {
+              escaped += "\\_";
+            }
+            else if (value[i] == '\\')
+            {
+              escaped += "\\\\";
+            }
+            else if (escapeBrackets && value[i] == '[')
+            {
+              escaped += "\\[";
+            }
+            else if (escapeBrackets && value[i] == ']')
+            {
+              escaped += "\\]";
+            }
+            else
+            {
+              escaped += value[i];
+            }
+          }
+      }
+      return escaped;
+    }
+
+
+    virtual std::string FormatLike(bool isCaseSensitive, const std::string& a, const std::string& b)
+    {
+      switch (dialect_)
+      {
+        case Dialect_MySQL: // LIKE is case insensitive by default !
+        {
+          if (isCaseSensitive)
+          {
+            return a + " LIKE BINARY " + b + " " + FormatWildcardEscape();
+          }
+          else
+          {
+            return a + " LIKE " + b + " " + FormatWildcardEscape();
+          }
+        }; break;
+        case Dialect_MSSQL:
+        case Dialect_PostgreSQL: // LIKE is case sensitive by default !
+        {
+          if (isCaseSensitive)
+          {
+            return a + " LIKE " + b + " " + FormatWildcardEscape();
+          }
+          else
+          {
+            return "lower(" + a + ") LIKE lower(" + b + ") " + FormatWildcardEscape();
+          }
+        }; break;
+        case Dialect_SQLite:
+        {
+          if (isCaseSensitive)
+          {
+            return a + " GLOB " + b + " "; // + FormatWildcardEscape();
+          }
+          else
+          {
+            return "lower_with_accents(" + a + ") GLOB lower_with_accents(" + b + ") "; // + FormatWildcardEscape();
+          }
+        }; break;
+        default:
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+      }
+    }
 
     virtual std::string FormatLimits(uint64_t since, uint64_t count)
     {
@@ -2356,9 +2473,8 @@
     ISqlLookupFormatter::GetLookupLevels(lowerLevel, upperLevel,  queryLevel, lookup);
 
     std::string sql;
-    bool enableNewStudyCode = true;
-
-    if (enableNewStudyCode && lowerLevel == queryLevel && upperLevel == queryLevel)
+
+    if (lowerLevel == queryLevel && upperLevel == queryLevel)
     {
       ISqlLookupFormatter::ApplySingleLevel(sql, formatter, lookup, queryLevel, labels, labelsConstraint, limit);
 
@@ -3133,7 +3249,11 @@
     {
       DatabaseManager::StandaloneStatement statement(manager, "SELECT 1");
 
+#if ORTHANC_FRAMEWORK_VERSION_IS_ABOVE(1, 12, 11)
+      Orthanc::ElapsedTimer timer;
+#else
       Orthanc::Toolbox::ElapsedTimer timer;
+#endif
 
       statement.ExecuteWithoutResult();
 
--- a/Framework/Plugins/IndexBackend.h	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/Plugins/IndexBackend.h	Tue Apr 14 12:41:50 2026 +0200
@@ -73,7 +73,7 @@
   private:
     void ReadChangesInternal(IDatabaseBackendOutput& output,
                              bool& done,
-                             DatabaseManager& manager,
+                             const DatabaseManager& manager,
                              DatabaseManager::CachedStatement& statement,
                              const Dictionary& args,
                              uint32_t limit,
@@ -577,7 +577,7 @@
                                                         const std::list<IdentifierTag>& identifierTags);
 
 #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1
-    ISqlLookupFormatter* CreateLookupFormatter(Dialect dialect);
+    static ISqlLookupFormatter* CreateLookupFormatter(Dialect dialect);
 #endif
   };
 }
--- a/Framework/Plugins/IndexConnectionsPool.h	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/Plugins/IndexConnectionsPool.h	Tue Apr 14 12:41:50 2026 +0200
@@ -62,7 +62,7 @@
                          size_t countConnections,
                          unsigned int houseKeepingDelaySeconds);
 
-    virtual ~IndexConnectionsPool();
+    virtual ~IndexConnectionsPool() ORTHANC_OVERRIDE;
 
     virtual void OpenConnections(bool hasIdentifierTags,
                                  const std::list<IdentifierTag>& identifierTags) ORTHANC_OVERRIDE;
--- a/Framework/Plugins/IndexUnitTests.h	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/Plugins/IndexUnitTests.h	Tue Apr 14 12:41:50 2026 +0200
@@ -359,16 +359,16 @@
     CheckBlob(blob);
   }
 
-  std::string s;
-  ASSERT_TRUE(db.LookupGlobalProperty(s, *manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion));
-  ASSERT_EQ("6", s);
+  std::string a;
+  ASSERT_TRUE(db.LookupGlobalProperty(a, *manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion));
+  ASSERT_EQ("6", a);
 
   db.SetGlobalProperty(*manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseInternal9, "Hello");
-  ASSERT_TRUE(db.LookupGlobalProperty(s, *manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseInternal9));
-  ASSERT_EQ("Hello", s);
+  ASSERT_TRUE(db.LookupGlobalProperty(a, *manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseInternal9));
+  ASSERT_EQ("Hello", a);
   db.SetGlobalProperty(*manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseInternal9, "HelloWorld");
-  ASSERT_TRUE(db.LookupGlobalProperty(s, *manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseInternal9));
-  ASSERT_EQ("HelloWorld", s);
+  ASSERT_TRUE(db.LookupGlobalProperty(a, *manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseInternal9));
+  ASSERT_EQ("HelloWorld", a);
 
   ASSERT_EQ(0u, db.GetAllResourcesCount(*manager));
   ASSERT_EQ(0u, db.GetResourcesCount(*manager, OrthancPluginResourceType_Patient));
@@ -408,9 +408,9 @@
   ASSERT_EQ(1u, db.GetResourcesCount(*manager, OrthancPluginResourceType_Study));
   ASSERT_EQ(2u, db.GetResourcesCount(*manager, OrthancPluginResourceType_Series));
 
-  ASSERT_FALSE(db.GetParentPublicId(s, *manager, studyId));
-  ASSERT_TRUE(db.GetParentPublicId(s, *manager, seriesId));  ASSERT_EQ("study", s);
-  ASSERT_TRUE(db.GetParentPublicId(s, *manager, series2Id));  ASSERT_EQ("study", s);
+  ASSERT_FALSE(db.GetParentPublicId(a, *manager, studyId));
+  ASSERT_TRUE(db.GetParentPublicId(a, *manager, seriesId));  ASSERT_EQ("study", a);
+  ASSERT_TRUE(db.GetParentPublicId(a, *manager, series2Id));  ASSERT_EQ("study", a);
 
   std::list<std::string> children;
   db.GetChildren(children, *manager, studyId);
@@ -449,9 +449,9 @@
   db.SetMetadata(*manager, studyId, Orthanc::MetadataType_ModifiedFrom, "modified", 42);
   db.SetMetadata(*manager, studyId, Orthanc::MetadataType_LastUpdate, "update2", 43);
   int64_t revision = -1;
-  ASSERT_FALSE(db.LookupMetadata(s, revision, *manager, seriesId, Orthanc::MetadataType_LastUpdate));
-  ASSERT_TRUE(db.LookupMetadata(s, revision, *manager, studyId, Orthanc::MetadataType_LastUpdate));
-  ASSERT_EQ("update2", s);
+  ASSERT_FALSE(db.LookupMetadata(a, revision, *manager, seriesId, Orthanc::MetadataType_LastUpdate));
+  ASSERT_TRUE(db.LookupMetadata(a, revision, *manager, studyId, Orthanc::MetadataType_LastUpdate));
+  ASSERT_EQ("update2", a);
 
 #if HAS_REVISIONS == 1
   ASSERT_EQ(43, revision);
@@ -460,8 +460,8 @@
 #endif
 
   db.SetMetadata(*manager, studyId, Orthanc::MetadataType_LastUpdate, reinterpret_cast<const char*>(UTF8), 44);
-  ASSERT_TRUE(db.LookupMetadata(s, revision, *manager, studyId, Orthanc::MetadataType_LastUpdate));
-  ASSERT_STREQ(reinterpret_cast<const char*>(UTF8), s.c_str());
+  ASSERT_TRUE(db.LookupMetadata(a, revision, *manager, studyId, Orthanc::MetadataType_LastUpdate));
+  ASSERT_STREQ(reinterpret_cast<const char*>(UTF8), a.c_str());
 
 #if HAS_REVISIONS == 1
   ASSERT_EQ(44, revision);
@@ -496,11 +496,11 @@
   db.ListAvailableMetadata(md, *manager, seriesId);
   ASSERT_EQ(0u, md.size());
 
-  ASSERT_TRUE(db.LookupMetadata(s, revision, *manager, studyId, Orthanc::MetadataType_LastUpdate));
+  ASSERT_TRUE(db.LookupMetadata(a, revision, *manager, studyId, Orthanc::MetadataType_LastUpdate));
   db.DeleteMetadata(*manager, studyId, Orthanc::MetadataType_LastUpdate);
-  ASSERT_FALSE(db.LookupMetadata(s, revision, *manager, studyId, Orthanc::MetadataType_LastUpdate));
+  ASSERT_FALSE(db.LookupMetadata(a, revision, *manager, studyId, Orthanc::MetadataType_LastUpdate));
   db.DeleteMetadata(*manager, seriesId, Orthanc::MetadataType_LastUpdate);
-  ASSERT_FALSE(db.LookupMetadata(s, revision, *manager, studyId, Orthanc::MetadataType_LastUpdate));
+  ASSERT_FALSE(db.LookupMetadata(a, revision, *manager, studyId, Orthanc::MetadataType_LastUpdate));
 
   db.ListAvailableMetadata(md, *manager, studyId);
   ASSERT_EQ(1u, md.size());
@@ -786,12 +786,12 @@
     // column in "ServerProperties" is "TEXT" instead of "LONGTEXT"
     db.SetGlobalProperty(*manager, "some-server", Orthanc::GlobalProperty_DatabaseInternal8, longProperty.c_str());
 
-    ASSERT_TRUE(db.LookupGlobalProperty(s, *manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseInternal8));
-    ASSERT_EQ(longProperty, s);
+    ASSERT_TRUE(db.LookupGlobalProperty(a, *manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseInternal8));
+    ASSERT_EQ(longProperty, a);
 
-    s.clear();
-    ASSERT_TRUE(db.LookupGlobalProperty(s, *manager, "some-server", Orthanc::GlobalProperty_DatabaseInternal8));
-    ASSERT_EQ(longProperty, s);
+    a.clear();
+    ASSERT_TRUE(db.LookupGlobalProperty(a, *manager, "some-server", Orthanc::GlobalProperty_DatabaseInternal8));
+    ASSERT_EQ(longProperty, a);
   }
 
   for (size_t level = 0; level < 4; level++)
@@ -1069,7 +1069,7 @@
 #if ORTHANC_PLUGINS_HAS_RESERVE_QUEUE_VALUE == 1
   {
     std::string value;
-    uint64_t valueIdA, valueIdB, valueIdC, valueIdD, valueIdE, valueIdFail;
+    uint64_t valueIdA, valueIdB, valueIdC, valueIdD, valueIdE;
 
     {
       manager->StartTransaction(TransactionType_ReadWrite);
@@ -1111,6 +1111,8 @@
       ASSERT_EQ("d", value);
       ASSERT_TRUE(db.ReserveQueueValue(value, valueIdC, *manager, "test", false, 1));
       ASSERT_EQ("c", value);
+
+      uint64_t valueIdFail;
       ASSERT_FALSE(db.ReserveQueueValue(value, valueIdFail, *manager, "test", false, 1));
 
       manager->CommitTransaction();
--- a/Framework/Plugins/PluginInitialization.cpp	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/Plugins/PluginInitialization.cpp	Tue Apr 14 12:41:50 2026 +0200
@@ -155,7 +155,7 @@
                                std::string(isIndex ? "index" : "storage area") +
                                " into a " + dbms + " database");
     
-    OrthancPlugins::SetDescription(pluginName, description.c_str());
+    OrthancPlugins::SetDescription(pluginName, description);
 
     return true;
   }
--- a/Framework/Plugins/StorageBackend.cpp	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/Plugins/StorageBackend.cpp	Tue Apr 14 12:41:50 2026 +0200
@@ -330,7 +330,7 @@
       bool                         success_;
       
     public:
-      Visitor(OrthancPluginMemoryBuffer64* target) :
+      explicit Visitor(OrthancPluginMemoryBuffer64* target) :
         target_(target),
         success_(false)
       {
@@ -406,7 +406,7 @@
       bool                         success_;
       
     public:
-      Visitor(OrthancPluginMemoryBuffer64* target) :
+      explicit Visitor(OrthancPluginMemoryBuffer64* target) :
         target_(target),
         success_(false)
       {
@@ -520,7 +520,7 @@
       {
       }
 
-      ~Visitor()
+      virtual ~Visitor() ORTHANC_OVERRIDE
       {
         if (data_ != NULL /* this condition is invalidated by "Release()" */ &&
             *data_ != NULL)
@@ -792,7 +792,7 @@
         operation.Execute(*accessor);
         return;  // Success
       }
-      catch (Orthanc::OrthancException& e)
+      catch (const Orthanc::OrthancException& e)
       {
 #if ORTHANC_FRAMEWORK_VERSION_IS_ABOVE(1, 9, 2)
         if (e.GetErrorCode() == Orthanc::ErrorCode_DatabaseCannotSerialize)
--- a/Framework/PostgreSQL/PostgreSQLDatabase.h	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/PostgreSQL/PostgreSQLDatabase.h	Tue Apr 14 12:41:50 2026 +0200
@@ -57,7 +57,7 @@
     {
     }
 
-    ~PostgreSQLDatabase();
+    virtual ~PostgreSQLDatabase() ORTHANC_OVERRIDE;
 
     void Open();
 
--- a/Framework/PostgreSQL/PostgreSQLResult.cpp	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/PostgreSQL/PostgreSQLResult.cpp	Tue Apr 14 12:41:50 2026 +0200
@@ -211,7 +211,7 @@
     Oid oid;
     assert(PQfsize(reinterpret_cast<PGresult*>(result_), column) == sizeof(oid));
 
-    oid = *(const Oid*) PQgetvalue(reinterpret_cast<PGresult*>(result_), position_, column);
+    oid = *reinterpret_cast<const Oid*>(PQgetvalue(reinterpret_cast<PGresult*>(result_), position_, column));
     oid = ntohl(oid);
 
     return boost::lexical_cast<std::string>(oid);
--- a/Framework/PostgreSQL/PostgreSQLStatement.cpp	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/PostgreSQL/PostgreSQLStatement.cpp	Tue Apr 14 12:41:50 2026 +0200
@@ -537,7 +537,7 @@
   };
 
 
-  IResult* PostgreSQLStatement::Execute(ITransaction& transaction,
+  IResult* PostgreSQLStatement::Execute(const ITransaction& transaction,
                                         const Dictionary& parameters)
   {
     for (size_t i = 0; i < formatter_.GetParametersCount(); i++)
@@ -589,7 +589,7 @@
   }
 
 
-  void PostgreSQLStatement::ExecuteWithoutResult(ITransaction& transaction,
+  void PostgreSQLStatement::ExecuteWithoutResult(const ITransaction& transaction,
                                                  const Dictionary& parameters)
   {
     std::unique_ptr<IResult> dummy(Execute(transaction, parameters));
--- a/Framework/PostgreSQL/PostgreSQLStatement.h	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/PostgreSQL/PostgreSQLStatement.h	Tue Apr 14 12:41:50 2026 +0200
@@ -72,7 +72,7 @@
     PostgreSQLStatement(PostgreSQLDatabase& database,
                         const Query& query);
 
-    ~PostgreSQLStatement();
+    virtual ~PostgreSQLStatement() ORTHANC_OVERRIDE;
     
     void DeclareInputInteger(unsigned int param);
     
@@ -103,10 +103,10 @@
       return database_;
     }
 
-    IResult* Execute(ITransaction& transaction,
+    IResult* Execute(const ITransaction& transaction,
                      const Dictionary& parameters);
 
-    void ExecuteWithoutResult(ITransaction& transaction,
+    void ExecuteWithoutResult(const ITransaction& transaction,
                               const Dictionary& parameters);
   };
 }
--- a/Framework/PostgreSQL/PostgreSQLTransaction.h	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/PostgreSQL/PostgreSQLTransaction.h	Tue Apr 14 12:41:50 2026 +0200
@@ -45,7 +45,7 @@
     explicit PostgreSQLTransaction(PostgreSQLDatabase& database,
                                    TransactionType type);
 
-    ~PostgreSQLTransaction();
+    virtual ~PostgreSQLTransaction() ORTHANC_OVERRIDE;
 
     virtual bool IsImplicit() const ORTHANC_OVERRIDE
     {
--- a/Framework/SQLite/SQLiteDatabase.cpp	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/SQLite/SQLiteDatabase.cpp	Tue Apr 14 12:41:50 2026 +0200
@@ -28,6 +28,7 @@
 #include "../Common/ImplicitTransaction.h"
 
 #include <OrthancException.h>
+#include <Toolbox.h>
 
 namespace OrthancDatabases
 {
@@ -114,4 +115,44 @@
         throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
     }
   }
+
+  class LowerWithAccents : public Orthanc::SQLite::IScalarFunction
+  {
+  public:
+    LowerWithAccents()
+    {
+    }
+
+    virtual const char* GetName() const ORTHANC_OVERRIDE
+    {
+      return "lower_with_accents";
+    }
+
+    virtual unsigned int GetCardinality() const ORTHANC_OVERRIDE
+    {
+      return 1;
+    }
+
+    virtual void Compute(Orthanc::SQLite::FunctionContext& context) ORTHANC_OVERRIDE
+    {
+      std::string source = context.GetStringValue(0);
+      std::string modified = Orthanc::Toolbox::ToLowerCaseWithAccents(source);
+
+      context.SetStringResult(modified);
+    }
+
+  };
+
+  void SQLiteDatabase::OpenInMemory()
+  {
+    connection_.OpenInMemory();
+    connection_.Register(new LowerWithAccents());
+  }
+
+  void SQLiteDatabase::Open(const std::string& path)
+  {
+    connection_.Open(path);
+    connection_.Register(new LowerWithAccents());
+  }
+
 }
--- a/Framework/SQLite/SQLiteDatabase.h	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/SQLite/SQLiteDatabase.h	Tue Apr 14 12:41:50 2026 +0200
@@ -39,15 +39,9 @@
     Orthanc::SQLite::Connection  connection_;
     
   public:
-    void OpenInMemory()
-    {
-      connection_.OpenInMemory();
-    }
+    void OpenInMemory();
 
-    void Open(const std::string& path)
-    {
-      connection_.Open(path);
-    }
+    void Open(const std::string& path);
     
     Orthanc::SQLite::Connection& GetObject()
     {
--- a/Framework/SQLite/SQLiteStatement.cpp	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/SQLite/SQLiteStatement.cpp	Tue Apr 14 12:41:50 2026 +0200
@@ -105,7 +105,7 @@
   }
 
   
-  IResult* SQLiteStatement::Execute(ITransaction& transaction,
+  IResult* SQLiteStatement::Execute(const ITransaction& transaction,
                                     const Dictionary& parameters)
   {
     BindParameters(parameters);
@@ -113,7 +113,7 @@
   }
 
 
-  void SQLiteStatement::ExecuteWithoutResult(ITransaction& transaction,
+  void SQLiteStatement::ExecuteWithoutResult(const ITransaction& transaction,
                                              const Dictionary& parameters)
   {
     BindParameters(parameters);
--- a/Framework/SQLite/SQLiteStatement.h	Fri Jan 02 13:05:05 2026 +0100
+++ b/Framework/SQLite/SQLiteStatement.h	Tue Apr 14 12:41:50 2026 +0200
@@ -52,10 +52,10 @@
 
     Orthanc::SQLite::Statement& GetObject();
 
-    IResult* Execute(ITransaction& transaction,
+    IResult* Execute(const ITransaction& transaction,
                      const Dictionary& parameters);
 
-    void ExecuteWithoutResult(ITransaction& transaction,
+    void ExecuteWithoutResult(const ITransaction& transaction,
                               const Dictionary& parameters);
   };
 }
--- a/MySQL/Plugins/MySQLStorageArea.cpp	Fri Jan 02 13:05:05 2026 +0100
+++ b/MySQL/Plugins/MySQLStorageArea.cpp	Tue Apr 14 12:41:50 2026 +0200
@@ -28,11 +28,10 @@
 #include "../../Framework/MySQL/MySQLTransaction.h"
 #include "MySQLDefinitions.h"
 
-#include <Compatibility.h>  // For std::unique_ptr<>
+#include <Compatibility.h>      // For std::unique_ptr<>
+#include <CompatibilityMath.h>  // For Orthanc::Math::iround()
 #include <Logging.h>
 
-#include <boost/math/special_functions/round.hpp>
-
 
 namespace OrthancDatabases
 {
@@ -47,8 +46,8 @@
       int64_t size;
       if (db.LookupGlobalIntegerVariable(size, "max_allowed_packet"))
       {
-        int mb = boost::math::iround(static_cast<double>(size) /
-                                     static_cast<double>(1024 * 1024));
+        int mb = Orthanc::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\" "
--- a/PostgreSQL/CMakeLists.txt	Fri Jan 02 13:05:05 2026 +0100
+++ b/PostgreSQL/CMakeLists.txt	Tue Apr 14 12:41:50 2026 +0200
@@ -43,7 +43,7 @@
   set(ORTHANC_FRAMEWORK_VERSION "mainline")
   set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg")
 else()
-  set(ORTHANC_FRAMEWORK_VERSION "e0979326ac53")  # while waiting for 1.12.11
+  set(ORTHANC_FRAMEWORK_VERSION "e0979326ac53")  # while waiting for 1.12.11 for Orthanc::Toolbox::ToLowerCaseWithAccents
   set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web")
 endif()
 
--- a/PostgreSQL/NEWS	Fri Jan 02 13:05:05 2026 +0100
+++ b/PostgreSQL/NEWS	Tue Apr 14 12:41:50 2026 +0200
@@ -13,6 +13,14 @@
   2025-12-05 08:04:24.133 UTC [73] STATEMENT:  SELECT * FROM DeleteResource($1)
 
 
+Release 10.1 (2026-04-14)
+=========================
+
+Changes:
+* In tools/find, filtering against "LabelsConstraint": "None" with an empty "Labels" list
+  now returns all resources that do not have any labels attached instead of returning all resources.
+
+
 Release 10.0 (2025-12-02)
 =========================
 
--- a/PostgreSQL/Plugins/PostgreSQLIndex.cpp	Fri Jan 02 13:05:05 2026 +0100
+++ b/PostgreSQL/Plugins/PostgreSQLIndex.cpp	Tue Apr 14 12:41:50 2026 +0200
@@ -69,7 +69,8 @@
     return PostgreSQLDatabase::CreateDatabaseFactory(parameters_);
   }
 
-  void PostgreSQLIndex::ApplyPrepareIndex(DatabaseManager::Transaction& t, DatabaseManager& manager)
+  void PostgreSQLIndex::ApplyPrepareIndex(DatabaseManager::Transaction& t,
+                                          const DatabaseManager& manager)
   {
     std::string query;
 
--- a/PostgreSQL/Plugins/PostgreSQLIndex.h	Fri Jan 02 13:05:05 2026 +0100
+++ b/PostgreSQL/Plugins/PostgreSQLIndex.h	Tue Apr 14 12:41:50 2026 +0200
@@ -49,7 +49,8 @@
       return true;
     }
 
-    void ApplyPrepareIndex(DatabaseManager::Transaction& t, DatabaseManager& manager);
+    void ApplyPrepareIndex(DatabaseManager::Transaction& t,
+                           const DatabaseManager& manager);
 
   public:
     PostgreSQLIndex(OrthancPluginContext* context,
--- a/Resources/CMake/DatabasesFrameworkConfiguration.cmake	Fri Jan 02 13:05:05 2026 +0100
+++ b/Resources/CMake/DatabasesFrameworkConfiguration.cmake	Tue Apr 14 12:41:50 2026 +0200
@@ -26,6 +26,7 @@
 
 if (ENABLE_SQLITE_BACKEND)
   set(ENABLE_SQLITE ON)
+  set(ENABLE_LOCALE ON)      # iconv is needed for lower_with_accents
 endif()
 
 if (ENABLE_POSTGRESQL_BACKEND)
--- a/Resources/Orthanc/CMake/DownloadOrthancFramework.cmake	Fri Jan 02 13:05:05 2026 +0100
+++ b/Resources/Orthanc/CMake/DownloadOrthancFramework.cmake	Tue Apr 14 12:41:50 2026 +0200
@@ -231,6 +231,11 @@
         # for BlockingSharedMessageQueue.WaitEmpty()
         set(ORTHANC_FRAMEWORK_PRE_RELEASE ON)
         set(ORTHANC_FRAMEWORK_MD5 "c037cd2ddbe1b65b431692855483161b")
+      elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "56eb61c86f93")
+        # OE2 1.11.0 (framework post-1.12.10)
+        # for HttpClient that returns the answer body in case of HTTP error
+        set(ORTHANC_FRAMEWORK_PRE_RELEASE ON)
+        set(ORTHANC_FRAMEWORK_MD5 "665f8aa70d7c5091bc20da37cf664910")
       endif()
     endif()
   endif()
--- a/Resources/Orthanc/Plugins/OrthancPluginCppWrapper.cpp	Fri Jan 02 13:05:05 2026 +0100
+++ b/Resources/Orthanc/Plugins/OrthancPluginCppWrapper.cpp	Tue Apr 14 12:41:50 2026 +0200
@@ -1544,11 +1544,25 @@
 
 #endif /* HAS_ORTHANC_PLUGIN_FIND_MATCHER == 1 */
 
+  static void CheckAnswerSizeIsLessThan4GB(const std::string& answer)
+  {
+    if (answer.size() > static_cast<size_t>(std::numeric_limits<uint32_t>::max()))
+    {
+  #if HAS_ORTHANC_EXCEPTION == 1
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, "Cannot send HTTP response larger than 4GB");
+  #else
+      ORTHANC_PLUGINS_LOG_ERROR("Cannot send HTTP response larger than 4GB");
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);          
+  #endif
+    }
+  }
+
   void AnswerJson(const Json::Value& value,
                   OrthancPluginRestOutput* output)
   {
     std::string bodyString;
-    WriteStyledJson(bodyString, value);    
+    WriteStyledJson(bodyString, value);
+    CheckAnswerSizeIsLessThan4GB(bodyString);    
     OrthancPluginAnswerBuffer(GetGlobalContext(), output, bodyString.c_str(), bodyString.size(), "application/json");
   }
 
@@ -1556,6 +1570,7 @@
                     const char* mimeType,
                     OrthancPluginRestOutput* output)
   {
+    CheckAnswerSizeIsLessThan4GB(answer);
     OrthancPluginAnswerBuffer(GetGlobalContext(), output, answer.c_str(), answer.size(), mimeType);
   }
 
@@ -1564,6 +1579,26 @@
     OrthancPluginSendHttpStatusCode(GetGlobalContext(), output, httpError);
   }
 
+  void AnswerHttpError(uint16_t httpError,
+                       OrthancPluginRestOutput* output,
+                       const std::string& answer,
+                       const char* mimeType)
+  {
+    CheckAnswerSizeIsLessThan4GB(answer);
+
+    OrthancPluginSetHttpHeader(GetGlobalContext(),
+                               output,
+                               "content-type",
+                               mimeType);
+                               
+    OrthancPluginSendHttpStatus(GetGlobalContext(),
+                                output,
+                                httpError,
+                                answer.c_str(),
+                                static_cast<uint32_t>(answer.size()));
+  }
+
+
   void AnswerMethodNotAllowed(OrthancPluginRestOutput *output, const char* allowedMethods)
   {
     OrthancPluginSendMethodNotAllowed(GetGlobalContext(), output, allowedMethods);
--- a/Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h	Fri Jan 02 13:05:05 2026 +0100
+++ b/Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h	Tue Apr 14 12:41:50 2026 +0200
@@ -690,6 +690,11 @@
   void AnswerHttpError(uint16_t httpError,
                        OrthancPluginRestOutput* output);
 
+  void AnswerHttpError(uint16_t httpError,
+                       OrthancPluginRestOutput* output,
+                       const std::string& answer,
+                       const char* mimeType);
+
   void AnswerMethodNotAllowed(OrthancPluginRestOutput* output, const char* allowedMethods);
 
 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 0)
@@ -1025,7 +1030,20 @@
     OrthancPluginSetMetricsValue(GetGlobalContext(), name,
                                  value, OrthancPluginMetricsType_Default);
   }
+#endif
 
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 1)
+  inline void SetMetricsValue(const char* name,
+                              int64_t value)
+  {
+    OrthancPluginSetMetricsIntegerValue(GetGlobalContext(), name,
+                                        value, OrthancPluginMetricsType_Default);
+  }
+#endif
+
+
+#if HAS_ORTHANC_PLUGIN_METRICS == 1
   class MetricsTimer : public boost::noncopyable
   {
   private:
--- a/SQLite/CMakeLists.txt	Fri Jan 02 13:05:05 2026 +0100
+++ b/SQLite/CMakeLists.txt	Tue Apr 14 12:41:50 2026 +0200
@@ -43,7 +43,7 @@
   set(ORTHANC_FRAMEWORK_VERSION "mainline")
   set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg")
 else()
-  set(ORTHANC_FRAMEWORK_VERSION "1.12.8")
+  set(ORTHANC_FRAMEWORK_VERSION "e0979326ac53")  # while waiting for 1.12.11 for Orthanc::Toolbox::ToLowerCaseWithAccents
   set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web")
 endif()
 
--- a/SQLite/Plugins/IndexPlugin.cpp	Fri Jan 02 13:05:05 2026 +0100
+++ b/SQLite/Plugins/IndexPlugin.cpp	Tue Apr 14 12:41:50 2026 +0200
@@ -25,6 +25,7 @@
 #include "../../Framework/Plugins/PluginInitialization.h"
 
 #include <Logging.h>
+#include <Toolbox.h>
 
 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 0)
 #  include <google/protobuf/any.h>
@@ -42,6 +43,8 @@
     GOOGLE_PROTOBUF_VERIFY_VERSION;
 #endif
 
+    Orthanc::Toolbox::InitializeGlobalLocale(NULL);
+
     if (!OrthancDatabases::InitializePlugin(context, ORTHANC_PLUGIN_NAME, "SQLite", true))
     {
       return -1;