comparison Framework/Common/DatabaseManager.cpp @ 23:b2ff1cd2907a

handling of implicit transactions in DatabaseManager
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 12 Jul 2018 10:44:17 +0200
parents c7c54993a92e
children 2fb9cd42af14
comparison
equal deleted inserted replaced
22:1e9bad493475 23:b2ff1cd2907a
154 154
155 ITransaction& DatabaseManager::GetTransaction() 155 ITransaction& DatabaseManager::GetTransaction()
156 { 156 {
157 if (transaction_.get() == NULL) 157 if (transaction_.get() == NULL)
158 { 158 {
159 LOG(TRACE) << "Automatically creating a database transaction"; 159 LOG(TRACE) << "Automatically creating an implicit database transaction";
160 160
161 try 161 try
162 { 162 {
163 transaction_.reset(GetDatabase().CreateTransaction()); 163 transaction_.reset(GetDatabase().CreateTransaction(true));
164 } 164 }
165 catch (Orthanc::OrthancException& e) 165 catch (Orthanc::OrthancException& e)
166 { 166 {
167 CloseIfUnavailable(e.GetErrorCode()); 167 CloseIfUnavailable(e.GetErrorCode());
168 throw; 168 throw;
169 } 169 }
170 } 170 }
171 171
172 assert(transaction_.get() != NULL); 172 assert(transaction_.get() != NULL);
173 return *transaction_; 173 return *transaction_;
174 }
175
176
177 void DatabaseManager::ReleaseImplicitTransaction()
178 {
179 if (transaction_.get() != NULL &&
180 transaction_->IsImplicit())
181 {
182 LOG(TRACE) << "Committing an implicit database transaction";
183
184 try
185 {
186 transaction_->Commit();
187 transaction_.reset(NULL);
188 }
189 catch (Orthanc::OrthancException& e)
190 {
191 // Don't throw the exception, as we are in CachedStatement destructor
192 LOG(ERROR) << "Error while committing an implicit database transaction: " << e.What();
193 }
194 }
174 } 195 }
175 196
176 197
177 DatabaseManager::DatabaseManager(IDatabaseFactory* factory) : // Takes ownership 198 DatabaseManager::DatabaseManager(IDatabaseFactory* factory) : // Takes ownership
178 factory_(factory) 199 factory_(factory)
192 213
193 try 214 try
194 { 215 {
195 if (transaction_.get() != NULL) 216 if (transaction_.get() != NULL)
196 { 217 {
197 #if 0 218 LOG(ERROR) << "Cannot start another transaction while there is an uncommitted transaction";
198 // TODO: This should be the right implementation 219 throw Orthanc::OrthancException(Orthanc::ErrorCode_Database);
199 if (transaction_->IsReadOnly()) 220 }
200 { 221
201 LOG(TRACE) << "Rollback of an uncommitted read-only transaction to start another transaction"; 222 transaction_.reset(GetDatabase().CreateTransaction(false));
202 transaction_->Rollback(); 223 }
203 transaction_.reset(NULL); 224 catch (Orthanc::OrthancException& e)
204 } 225 {
205 else 226 CloseIfUnavailable(e.GetErrorCode());
206 { 227 throw;
207 LOG(ERROR) << "Cannot rollback an uncommitted write transaction to start another transaction"; 228 }
208 throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); 229 }
209 } 230
210 #else 231
211 LOG(INFO) << "Committing an uncommitted transaction to start another transaction"; 232 void DatabaseManager::CommitTransaction()
233 {
234 boost::recursive_mutex::scoped_lock lock(mutex_);
235
236 if (transaction_.get() == NULL)
237 {
238 LOG(ERROR) << "Cannot commit a non-existing transaction";
239 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
240 }
241 else
242 {
243 try
244 {
212 transaction_->Commit(); 245 transaction_->Commit();
213 transaction_.reset(NULL); 246 transaction_.reset(NULL);
214 #endif
215 }
216
217 transaction_.reset(GetDatabase().CreateTransaction());
218 }
219 catch (Orthanc::OrthancException& e)
220 {
221 CloseIfUnavailable(e.GetErrorCode());
222 throw;
223 }
224 }
225
226
227 void DatabaseManager::CommitTransaction()
228 {
229 boost::recursive_mutex::scoped_lock lock(mutex_);
230
231 if (transaction_.get() == NULL)
232 {
233 LOG(ERROR) << "Cannot commit a non-existing transaction";
234 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
235 }
236 else
237 {
238 try
239 {
240 transaction_->Commit();
241 transaction_.reset(NULL);
242 } 247 }
243 catch (Orthanc::OrthancException& e) 248 catch (Orthanc::OrthancException& e)
244 { 249 {
245 CloseIfUnavailable(e.GetErrorCode()); 250 CloseIfUnavailable(e.GetErrorCode());
246 throw; 251 throw;
300 << location_.GetFile() << ":" << location_.GetLine(); 305 << location_.GetFile() << ":" << location_.GetLine();
301 } 306 }
302 } 307 }
303 308
304 309
310 DatabaseManager::Transaction::Transaction(DatabaseManager& manager) :
311 lock_(manager.mutex_),
312 manager_(manager),
313 database_(manager.GetDatabase()),
314 committed_(false)
315 {
316 manager_.StartTransaction();
317 }
318
319
320 DatabaseManager::Transaction::~Transaction()
321 {
322 if (!committed_)
323 {
324 try
325 {
326 manager_.RollbackTransaction();
327 }
328 catch (Orthanc::OrthancException& e)
329 {
330 // Don't rethrow the exception as we are in a destructor
331 LOG(ERROR) << "Uncatched error during some transaction rollback: " << e.What();
332 }
333 }
334 }
335
336
337 void DatabaseManager::Transaction::Commit()
338 {
339 if (committed_)
340 {
341 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
342 }
343 else
344 {
345 manager_.CommitTransaction();
346 committed_ = true;
347 }
348 }
349
350
305 DatabaseManager::CachedStatement::CachedStatement(const StatementLocation& location, 351 DatabaseManager::CachedStatement::CachedStatement(const StatementLocation& location,
306 DatabaseManager& manager, 352 DatabaseManager& manager,
307 const char* sql) : 353 const char* sql) :
308 lock_(manager.mutex_), 354 lock_(manager.mutex_),
309 manager_(manager), 355 manager_(manager),
325 transaction_(manager_.GetTransaction()) 371 transaction_(manager_.GetTransaction())
326 { 372 {
327 Setup(sql); 373 Setup(sql);
328 } 374 }
329 375
376
377 DatabaseManager::CachedStatement::~CachedStatement()
378 {
379 manager_.ReleaseImplicitTransaction();
380 }
381
330 382
331 void DatabaseManager::CachedStatement::SetReadOnly(bool readOnly) 383 void DatabaseManager::CachedStatement::SetReadOnly(bool readOnly)
332 { 384 {
333 if (query_.get() != NULL) 385 if (query_.get() != NULL)
334 { 386 {