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