X-Git-Url: https://git.stg.codes/stg.git/blobdiff_plain/8c6fa3fbaccc22127280bf77a48fab5a3ee0716e..46b0747592074017ff0ea4b33d4a7194235886e5:/libs/ibpp/transaction.cpp diff --git a/libs/ibpp/transaction.cpp b/libs/ibpp/transaction.cpp new file mode 100644 index 00000000..47eb61c7 --- /dev/null +++ b/libs/ibpp/transaction.cpp @@ -0,0 +1,409 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// File : $Id: transaction.cpp,v 1.1 2007/05/05 17:00:43 faust Exp $ +// Subject : IBPP, Database class implementation +// +/////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright 2000-2006 T.I.P. Group S.A. and the IBPP Team (www.ibpp.org) +// +// The contents of this file are subject to the IBPP License (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at http://www.ibpp.org or in the 'license.txt' +// file which must have been distributed along with this file. +// +// This software, distributed under the License, is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations +// under the License. +// +/////////////////////////////////////////////////////////////////////////////// +// +// COMMENTS +// * Tabulations should be set every four characters when editing this file. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifdef _MSC_VER +#pragma warning(disable: 4786 4996) +#ifndef _DEBUG +#pragma warning(disable: 4702) +#endif +#endif + +#include "_ibpp.h" + +#ifdef HAS_HDRSTOP +#pragma hdrstop +#endif + +#include + +using namespace ibpp_internals; + +// (((((((( OBJECT INTERFACE IMPLEMENTATION )))))))) + +void TransactionImpl::AttachDatabase(IBPP::Database db, + IBPP::TAM am, IBPP::TIL il, IBPP::TLR lr, IBPP::TFF flags) +{ + if (db.intf() == 0) + throw LogicExceptionImpl("Transaction::AttachDatabase", + _("Can't attach an unbound Database.")); + + AttachDatabaseImpl(dynamic_cast(db.intf()), am, il, lr, flags); +} + +void TransactionImpl::DetachDatabase(IBPP::Database db) +{ + if (db.intf() == 0) + throw LogicExceptionImpl("Transaction::DetachDatabase", + _("Can't detach an unbound Database.")); + + DetachDatabaseImpl(dynamic_cast(db.intf())); +} + +void TransactionImpl::AddReservation(IBPP::Database db, + const std::string& table, IBPP::TTR tr) +{ + if (mHandle != 0) + throw LogicExceptionImpl("Transaction::AddReservation", + _("Can't add table reservation if Transaction started.")); + if (db.intf() == 0) + throw LogicExceptionImpl("Transaction::AddReservation", + _("Can't add table reservation on an unbound Database.")); + + // Find the TPB associated with this database + std::vector::iterator pos = + std::find(mDatabases.begin(), mDatabases.end(), dynamic_cast(db.intf())); + if (pos != mDatabases.end()) + { + size_t index = pos - mDatabases.begin(); + TPB* tpb = mTPBs[index]; + + // Now add the reservations to the TPB + switch (tr) + { + case IBPP::trSharedWrite : + tpb->Insert(isc_tpb_lock_write); + tpb->Insert(table); + tpb->Insert(isc_tpb_shared); + break; + case IBPP::trSharedRead : + tpb->Insert(isc_tpb_lock_read); + tpb->Insert(table); + tpb->Insert(isc_tpb_shared); + break; + case IBPP::trProtectedWrite : + tpb->Insert(isc_tpb_lock_write); + tpb->Insert(table); + tpb->Insert(isc_tpb_protected); + break; + case IBPP::trProtectedRead : + tpb->Insert(isc_tpb_lock_read); + tpb->Insert(table); + tpb->Insert(isc_tpb_protected); + break; + /*default : + throw LogicExceptionImpl("Transaction::AddReservation", + _("Illegal TTR value detected."));*/ + } + } + else throw LogicExceptionImpl("Transaction::AddReservation", + _("The database connection you specified is not attached to this transaction.")); +} + +void TransactionImpl::Start() +{ + if (mHandle != 0) return; // Already started anyway + + if (mDatabases.empty()) + throw LogicExceptionImpl("Transaction::Start", _("No Database is attached.")); + + struct ISC_TEB + { + ISC_LONG* db_ptr; + ISC_LONG tpb_len; + char* tpb_ptr; + } * teb = new ISC_TEB[mDatabases.size()]; + + unsigned i; + for (i = 0; i < mDatabases.size(); i++) + { + if (mDatabases[i]->GetHandle() == 0) + { + // All Databases must be connected to Start the transaction ! + delete [] teb; + throw LogicExceptionImpl("Transaction::Start", + _("All attached Database should have been connected.")); + } + teb[i].db_ptr = (ISC_LONG*) mDatabases[i]->GetHandlePtr(); + teb[i].tpb_len = mTPBs[i]->Size(); + teb[i].tpb_ptr = mTPBs[i]->Self(); + } + + IBS status; + (*gds.Call()->m_start_multiple)(status.Self(), &mHandle, (short)mDatabases.size(), teb); + delete [] teb; + if (status.Errors()) + { + mHandle = 0; // Should be, but better be sure... + throw SQLExceptionImpl(status, "Transaction::Start"); + } +} + +void TransactionImpl::Commit() +{ + if (mHandle == 0) + throw LogicExceptionImpl("Transaction::Commit", _("Transaction is not started.")); + + IBS status; + + (*gds.Call()->m_commit_transaction)(status.Self(), &mHandle); + if (status.Errors()) + throw SQLExceptionImpl(status, "Transaction::Commit"); + mHandle = 0; // Should be, better be sure +} + +void TransactionImpl::CommitRetain() +{ + if (mHandle == 0) + throw LogicExceptionImpl("Transaction::CommitRetain", _("Transaction is not started.")); + + IBS status; + + (*gds.Call()->m_commit_retaining)(status.Self(), &mHandle); + if (status.Errors()) + throw SQLExceptionImpl(status, "Transaction::CommitRetain"); +} + +void TransactionImpl::Rollback() +{ + if (mHandle == 0) return; // Transaction not started anyway + + IBS status; + + (*gds.Call()->m_rollback_transaction)(status.Self(), &mHandle); + if (status.Errors()) + throw SQLExceptionImpl(status, "Transaction::Rollback"); + mHandle = 0; // Should be, better be sure +} + +void TransactionImpl::RollbackRetain() +{ + if (mHandle == 0) + throw LogicExceptionImpl("Transaction::RollbackRetain", _("Transaction is not started.")); + + IBS status; + + (*gds.Call()->m_rollback_retaining)(status.Self(), &mHandle); + if (status.Errors()) + throw SQLExceptionImpl(status, "Transaction::RollbackRetain"); +} + +IBPP::ITransaction* TransactionImpl::AddRef() +{ + ASSERTION(mRefCount >= 0); + ++mRefCount; + return this; +} + +void TransactionImpl::Release() +{ + // Release cannot throw, except in DEBUG builds on assertion + ASSERTION(mRefCount >= 0); + --mRefCount; + try { if (mRefCount <= 0) delete this; } + catch (...) { } +} + +// (((((((( OBJECT INTERNAL METHODS )))))))) + +void TransactionImpl::Init() +{ + mHandle = 0; + mDatabases.clear(); + mTPBs.clear(); + mStatements.clear(); + mBlobs.clear(); + mArrays.clear(); +} + +void TransactionImpl::AttachStatementImpl(StatementImpl* st) +{ + if (st == 0) + throw LogicExceptionImpl("Transaction::AttachStatement", + _("Can't attach a 0 Statement object.")); + + mStatements.push_back(st); +} + +void TransactionImpl::DetachStatementImpl(StatementImpl* st) +{ + if (st == 0) + throw LogicExceptionImpl("Transaction::DetachStatement", + _("Can't detach a 0 Statement object.")); + + mStatements.erase(std::find(mStatements.begin(), mStatements.end(), st)); +} + +void TransactionImpl::AttachBlobImpl(BlobImpl* bb) +{ + if (bb == 0) + throw LogicExceptionImpl("Transaction::AttachBlob", + _("Can't attach a 0 BlobImpl object.")); + + mBlobs.push_back(bb); +} + +void TransactionImpl::DetachBlobImpl(BlobImpl* bb) +{ + if (bb == 0) + throw LogicExceptionImpl("Transaction::DetachBlob", + _("Can't detach a 0 BlobImpl object.")); + + mBlobs.erase(std::find(mBlobs.begin(), mBlobs.end(), bb)); +} + +void TransactionImpl::AttachArrayImpl(ArrayImpl* ar) +{ + if (ar == 0) + throw LogicExceptionImpl("Transaction::AttachArray", + _("Can't attach a 0 ArrayImpl object.")); + + mArrays.push_back(ar); +} + +void TransactionImpl::DetachArrayImpl(ArrayImpl* ar) +{ + if (ar == 0) + throw LogicExceptionImpl("Transaction::DetachArray", + _("Can't detach a 0 ArrayImpl object.")); + + mArrays.erase(std::find(mArrays.begin(), mArrays.end(), ar)); +} + +void TransactionImpl::AttachDatabaseImpl(DatabaseImpl* dbi, + IBPP::TAM am, IBPP::TIL il, IBPP::TLR lr, IBPP::TFF flags) +{ + if (mHandle != 0) + throw LogicExceptionImpl("Transaction::AttachDatabase", + _("Can't attach a Database if Transaction started.")); + if (dbi == 0) + throw LogicExceptionImpl("Transaction::AttachDatabase", + _("Can't attach a null Database.")); + + mDatabases.push_back(dbi); + + // Prepare a new TPB + TPB* tpb = new TPB; + if (am == IBPP::amRead) tpb->Insert(isc_tpb_read); + else tpb->Insert(isc_tpb_write); + + switch (il) + { + case IBPP::ilConsistency : tpb->Insert(isc_tpb_consistency); break; + case IBPP::ilReadDirty : tpb->Insert(isc_tpb_read_committed); + tpb->Insert(isc_tpb_rec_version); break; + case IBPP::ilReadCommitted : tpb->Insert(isc_tpb_read_committed); + tpb->Insert(isc_tpb_no_rec_version); break; + case IBPP::ilConcurrency : tpb->Insert(isc_tpb_concurrency); break; + } + + if (lr == IBPP::lrNoWait) tpb->Insert(isc_tpb_nowait); + else tpb->Insert(isc_tpb_wait); + + if (flags & IBPP::tfIgnoreLimbo) tpb->Insert(isc_tpb_ignore_limbo); + if (flags & IBPP::tfAutoCommit) tpb->Insert(isc_tpb_autocommit); + if (flags & IBPP::tfNoAutoUndo) tpb->Insert(isc_tpb_no_auto_undo); + + mTPBs.push_back(tpb); + + // Signals the Database object that it has been attached to the Transaction + dbi->AttachTransactionImpl(this); +} + +void TransactionImpl::DetachDatabaseImpl(DatabaseImpl* dbi) +{ + if (mHandle != 0) + throw LogicExceptionImpl("Transaction::DetachDatabase", + _("Can't detach a Database if Transaction started.")); + if (dbi == 0) + throw LogicExceptionImpl("Transaction::DetachDatabase", + _("Can't detach a null Database.")); + + std::vector::iterator pos = + std::find(mDatabases.begin(), mDatabases.end(), dbi); + if (pos != mDatabases.end()) + { + size_t index = pos - mDatabases.begin(); + TPB* tpb = mTPBs[index]; + mDatabases.erase(pos); + mTPBs.erase(mTPBs.begin()+index); + delete tpb; + } + + // Signals the Database object that it has been detached from the Transaction + dbi->DetachTransactionImpl(this); +} + +TransactionImpl::TransactionImpl(DatabaseImpl* db, + IBPP::TAM am, IBPP::TIL il, IBPP::TLR lr, IBPP::TFF flags) + : mRefCount(0) +{ + Init(); + AttachDatabaseImpl(db, am, il, lr, flags); +} + +TransactionImpl::~TransactionImpl() +{ + // Rollback the transaction if it was Started + try { if (Started()) Rollback(); } + catch (...) { } + + // Let's detach cleanly all Blobs from this Transaction. + // No Blob object can still maintain pointers to this + // Transaction which is disappearing. + // + // We use a reverse traversal of the array to avoid loops. + // The array shrinks on each loop (mBbCount decreases). + // And during the deletion, there is a packing of the array through a + // copy of elements from the end to the beginning of the array. + try { + while (mBlobs.size() > 0) + mBlobs.back()->DetachTransactionImpl(); + } catch (...) { } + + // Let's detach cleanly all Arrays from this Transaction. + // No Array object can still maintain pointers to this + // Transaction which is disappearing. + try { + while (mArrays.size() > 0) + mArrays.back()->DetachTransactionImpl(); + } catch (...) { } + + // Let's detach cleanly all Statements from this Transaction. + // No Statement object can still maintain pointers to this + // Transaction which is disappearing. + try { + while (mStatements.size() > 0) + mStatements.back()->DetachTransactionImpl(); + } catch (...) { } + + // Very important : let's detach cleanly all Databases from this + // Transaction. No Database object can still maintain pointers to this + // Transaction which is disappearing. + try { + while (mDatabases.size() > 0) + { + size_t i = mDatabases.size()-1; + DetachDatabaseImpl(mDatabases[i]); // <-- remove link to database from mTPBs + // array and destroy TPB object + // Fixed : Maxim Abrashkin on 12 Jun 2002 + //mDatabases.back()->DetachTransaction(this); + } + } catch (...) { } +} + +// +// EOF +//