1 /**
2  * Orthanc - A Lightweight, RESTful DICOM Store
3  * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4  * Department, University Hospital of Liege, Belgium
5  * Copyright (C) 2017-2021 Osimis S.A., Belgium
6  *
7  * This program is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU Affero General Public License
9  * as published by the Free Software Foundation, either version 3 of
10  * the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program. If not, see <http://www.gnu.org/licenses/>.
19  **/
20 
21 
22 #include "PostgreSQLTransaction.h"
23 
24 #include "PostgreSQLStatement.h"
25 
26 #include <Compatibility.h>  // For std::unique_ptr<>
27 #include <Logging.h>
28 #include <OrthancException.h>
29 
30 namespace OrthancDatabases
31 {
PostgreSQLTransaction(PostgreSQLDatabase & database,TransactionType type)32   PostgreSQLTransaction::PostgreSQLTransaction(PostgreSQLDatabase& database,
33                                                TransactionType type) :
34     database_(database),
35     isOpen_(false)
36   {
37     Begin(type);
38   }
39 
40 
~PostgreSQLTransaction()41   PostgreSQLTransaction::~PostgreSQLTransaction()
42   {
43     if (isOpen_)
44     {
45       LOG(INFO) << "PostgreSQL: An active PostgreSQL transaction was dismissed";
46 
47       try
48       {
49         database_.ExecuteMultiLines("ABORT");
50       }
51       catch (Orthanc::OrthancException&)
52       {
53         // Ignore possible exceptions due to connection loss
54       }
55     }
56   }
57 
58 
Begin(TransactionType type)59   void PostgreSQLTransaction::Begin(TransactionType type)
60   {
61     if (isOpen_)
62     {
63       LOG(ERROR) << "PostgreSQL: Beginning a transaction twice!";
64       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
65     }
66 
67     database_.ExecuteMultiLines("BEGIN");
68 
69     switch (type)
70     {
71       case TransactionType_ReadWrite:
72         database_.ExecuteMultiLines("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ WRITE");
73         break;
74 
75       case TransactionType_ReadOnly:
76         database_.ExecuteMultiLines("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY");
77         break;
78 
79       default:
80         throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
81     }
82 
83     isOpen_ = true;
84   }
85 
86 
Rollback()87   void PostgreSQLTransaction::Rollback()
88   {
89     if (!isOpen_)
90     {
91       LOG(ERROR) << "PostgreSQL: Attempting to rollback a nonexistent transaction. "
92                  << "Did you remember to call Begin()?";
93       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
94     }
95 
96     database_.ExecuteMultiLines("ABORT");
97     isOpen_ = false;
98   }
99 
100 
Commit()101   void PostgreSQLTransaction::Commit()
102   {
103     if (!isOpen_)
104     {
105       LOG(ERROR) << "PostgreSQL: Attempting to roll back a nonexistent transaction. "
106                  << "Did you remember to call Begin()?";
107       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
108     }
109 
110     database_.ExecuteMultiLines("COMMIT");
111     isOpen_ = false;
112   }
113 
114 
Execute(IPrecompiledStatement & statement,const Dictionary & parameters)115   IResult* PostgreSQLTransaction::Execute(IPrecompiledStatement& statement,
116                                           const Dictionary& parameters)
117   {
118     return dynamic_cast<PostgreSQLStatement&>(statement).Execute(*this, parameters);
119   }
120 
121 
ExecuteWithoutResult(IPrecompiledStatement & statement,const Dictionary & parameters)122   void PostgreSQLTransaction::ExecuteWithoutResult(IPrecompiledStatement& statement,
123                                                    const Dictionary& parameters)
124   {
125     dynamic_cast<PostgreSQLStatement&>(statement).ExecuteWithoutResult(*this, parameters);
126   }
127 }
128