1 ///////////////////////////////////////////////////////////////////////////////
\r 
   3 //      File    : $Id: transaction.cpp,v 1.1 2007/05/05 17:00:43 faust Exp $
\r 
   4 //      Subject : IBPP, Database class implementation
\r 
   6 ///////////////////////////////////////////////////////////////////////////////
\r 
   8 //      (C) Copyright 2000-2006 T.I.P. Group S.A. and the IBPP Team (www.ibpp.org)
\r 
  10 //      The contents of this file are subject to the IBPP License (the "License");
\r 
  11 //      you may not use this file except in compliance with the License.  You may
\r 
  12 //      obtain a copy of the License at http://www.ibpp.org or in the 'license.txt'
\r 
  13 //      file which must have been distributed along with this file.
\r 
  15 //      This software, distributed under the License, is distributed on an "AS IS"
\r 
  16 //      basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the
\r 
  17 //      License for the specific language governing rights and limitations
\r 
  18 //      under the License.
\r 
  20 ///////////////////////////////////////////////////////////////////////////////
\r 
  23 //      * Tabulations should be set every four characters when editing this file.
\r 
  25 ///////////////////////////////////////////////////////////////////////////////
\r 
  28 #pragma warning(disable: 4786 4996)
\r 
  30 #pragma warning(disable: 4702)
\r 
  40 #include <algorithm>
\r 
  42 using namespace ibpp_internals;
\r 
  44 //      (((((((( OBJECT INTERFACE IMPLEMENTATION ))))))))
\r 
  46 void TransactionImpl::AttachDatabase(IBPP::Database db,
\r 
  47         IBPP::TAM am, IBPP::TIL il, IBPP::TLR lr, IBPP::TFF flags)
\r 
  50                 throw LogicExceptionImpl("Transaction::AttachDatabase",
\r 
  51                                 _("Can't attach an unbound Database."));
\r 
  53         AttachDatabaseImpl(dynamic_cast<DatabaseImpl*>(db.intf()), am, il, lr, flags);
\r 
  56 void TransactionImpl::DetachDatabase(IBPP::Database db)
\r 
  59                 throw LogicExceptionImpl("Transaction::DetachDatabase",
\r 
  60                                 _("Can't detach an unbound Database."));
\r 
  62         DetachDatabaseImpl(dynamic_cast<DatabaseImpl*>(db.intf()));
\r 
  65 void TransactionImpl::AddReservation(IBPP::Database db,
\r 
  66         const std::string& table, IBPP::TTR tr)
\r 
  69                 throw LogicExceptionImpl("Transaction::AddReservation",
\r 
  70                                 _("Can't add table reservation if Transaction started."));
\r 
  72                 throw LogicExceptionImpl("Transaction::AddReservation",
\r 
  73                                 _("Can't add table reservation on an unbound Database."));
\r 
  75         // Find the TPB associated with this database
\r 
  76         std::vector<DatabaseImpl*>::iterator pos =
\r 
  77                 std::find(mDatabases.begin(), mDatabases.end(), dynamic_cast<DatabaseImpl*>(db.intf()));
\r 
  78         if (pos != mDatabases.end())
\r 
  80                 size_t index = pos - mDatabases.begin();
\r 
  81                 TPB* tpb = mTPBs[index];
\r 
  83                 // Now add the reservations to the TPB
\r 
  86                         case IBPP::trSharedWrite :
\r 
  87                                         tpb->Insert(isc_tpb_lock_write);
\r 
  89                                         tpb->Insert(isc_tpb_shared);
\r 
  91                         case IBPP::trSharedRead :
\r 
  92                                         tpb->Insert(isc_tpb_lock_read);
\r 
  94                                         tpb->Insert(isc_tpb_shared);
\r 
  96                         case IBPP::trProtectedWrite :
\r 
  97                                         tpb->Insert(isc_tpb_lock_write);
\r 
  99                                         tpb->Insert(isc_tpb_protected);
\r 
 101                         case IBPP::trProtectedRead :
\r 
 102                                         tpb->Insert(isc_tpb_lock_read);
\r 
 103                                         tpb->Insert(table);
\r 
 104                                         tpb->Insert(isc_tpb_protected);
\r 
 107                                         throw LogicExceptionImpl("Transaction::AddReservation",
\r 
 108                                                 _("Illegal TTR value detected."));*/
\r 
 111         else throw LogicExceptionImpl("Transaction::AddReservation",
\r 
 112                         _("The database connection you specified is not attached to this transaction."));
\r 
 115 void TransactionImpl::Start()
\r 
 117         if (mHandle != 0) return;       // Already started anyway
\r 
 119         if (mDatabases.empty())
\r 
 120                 throw LogicExceptionImpl("Transaction::Start", _("No Database is attached."));
\r 
 127         } * teb = new ISC_TEB[mDatabases.size()];
\r 
 130         for (i = 0; i < mDatabases.size(); i++)
\r 
 132                 if (mDatabases[i]->GetHandle() == 0)
\r 
 134                         // All Databases must be connected to Start the transaction !
\r 
 136                         throw LogicExceptionImpl("Transaction::Start",
\r 
 137                                         _("All attached Database should have been connected."));
\r 
 139                 teb[i].db_ptr = (ISC_LONG*) mDatabases[i]->GetHandlePtr();
\r 
 140                 teb[i].tpb_len = mTPBs[i]->Size();
\r 
 141                 teb[i].tpb_ptr = mTPBs[i]->Self();
\r 
 145         (*gds.Call()->m_start_multiple)(status.Self(), &mHandle, (short)mDatabases.size(), teb);
\r 
 147         if (status.Errors())
\r 
 149                 mHandle = 0;    // Should be, but better be sure...
\r 
 150                 throw SQLExceptionImpl(status, "Transaction::Start");
\r 
 154 void TransactionImpl::Commit()
\r 
 157                 throw LogicExceptionImpl("Transaction::Commit", _("Transaction is not started."));
\r 
 161         (*gds.Call()->m_commit_transaction)(status.Self(), &mHandle);
\r 
 162         if (status.Errors())
\r 
 163                 throw SQLExceptionImpl(status, "Transaction::Commit");
\r 
 164         mHandle = 0;    // Should be, better be sure
\r 
 167 void TransactionImpl::CommitRetain()
\r 
 170                 throw LogicExceptionImpl("Transaction::CommitRetain", _("Transaction is not started."));
\r 
 174         (*gds.Call()->m_commit_retaining)(status.Self(), &mHandle);
\r 
 175         if (status.Errors())
\r 
 176                 throw SQLExceptionImpl(status, "Transaction::CommitRetain");
\r 
 179 void TransactionImpl::Rollback()
\r 
 181         if (mHandle == 0) return;       // Transaction not started anyway
\r 
 185         (*gds.Call()->m_rollback_transaction)(status.Self(), &mHandle);
\r 
 186         if (status.Errors())
\r 
 187                 throw SQLExceptionImpl(status, "Transaction::Rollback");
\r 
 188         mHandle = 0;    // Should be, better be sure
\r 
 191 void TransactionImpl::RollbackRetain()
\r 
 194                 throw LogicExceptionImpl("Transaction::RollbackRetain", _("Transaction is not started."));
\r 
 198         (*gds.Call()->m_rollback_retaining)(status.Self(), &mHandle);
\r 
 199         if (status.Errors())
\r 
 200                 throw SQLExceptionImpl(status, "Transaction::RollbackRetain");
\r 
 203 IBPP::ITransaction* TransactionImpl::AddRef()
\r 
 205         ASSERTION(mRefCount >= 0);
\r 
 210 void TransactionImpl::Release()
\r 
 212         // Release cannot throw, except in DEBUG builds on assertion
\r 
 213         ASSERTION(mRefCount >= 0);
\r 
 215         try { if (mRefCount <= 0) delete this; }
\r 
 219 //      (((((((( OBJECT INTERNAL METHODS ))))))))
\r 
 221 void TransactionImpl::Init()
\r 
 224         mDatabases.clear();
\r 
 226         mStatements.clear();
\r 
 231 void TransactionImpl::AttachStatementImpl(StatementImpl* st)
\r 
 234                 throw LogicExceptionImpl("Transaction::AttachStatement",
\r 
 235                                         _("Can't attach a 0 Statement object."));
\r 
 237         mStatements.push_back(st);
\r 
 240 void TransactionImpl::DetachStatementImpl(StatementImpl* st)
\r 
 243                 throw LogicExceptionImpl("Transaction::DetachStatement",
\r 
 244                                 _("Can't detach a 0 Statement object."));
\r 
 246         mStatements.erase(std::find(mStatements.begin(), mStatements.end(), st));
\r 
 249 void TransactionImpl::AttachBlobImpl(BlobImpl* bb)
\r 
 252                 throw LogicExceptionImpl("Transaction::AttachBlob",
\r 
 253                                         _("Can't attach a 0 BlobImpl object."));
\r 
 255         mBlobs.push_back(bb);
\r 
 258 void TransactionImpl::DetachBlobImpl(BlobImpl* bb)
\r 
 261                 throw LogicExceptionImpl("Transaction::DetachBlob",
\r 
 262                                 _("Can't detach a 0 BlobImpl object."));
\r 
 264         mBlobs.erase(std::find(mBlobs.begin(), mBlobs.end(), bb));
\r 
 267 void TransactionImpl::AttachArrayImpl(ArrayImpl* ar)
\r 
 270                 throw LogicExceptionImpl("Transaction::AttachArray",
\r 
 271                                         _("Can't attach a 0 ArrayImpl object."));
\r 
 273         mArrays.push_back(ar);
\r 
 276 void TransactionImpl::DetachArrayImpl(ArrayImpl* ar)
\r 
 279                 throw LogicExceptionImpl("Transaction::DetachArray",
\r 
 280                                 _("Can't detach a 0 ArrayImpl object."));
\r 
 282         mArrays.erase(std::find(mArrays.begin(), mArrays.end(), ar));
\r 
 285 void TransactionImpl::AttachDatabaseImpl(DatabaseImpl* dbi,
\r 
 286         IBPP::TAM am, IBPP::TIL il, IBPP::TLR lr, IBPP::TFF flags)
\r 
 289                 throw LogicExceptionImpl("Transaction::AttachDatabase",
\r 
 290                                 _("Can't attach a Database if Transaction started."));
\r 
 292                 throw LogicExceptionImpl("Transaction::AttachDatabase",
\r 
 293                                 _("Can't attach a null Database."));
\r 
 295         mDatabases.push_back(dbi);
\r 
 297         // Prepare a new TPB
\r 
 298         TPB* tpb = new TPB;
\r 
 299     if (am == IBPP::amRead) tpb->Insert(isc_tpb_read);
\r 
 300     else tpb->Insert(isc_tpb_write);
\r 
 304                 case IBPP::ilConsistency :              tpb->Insert(isc_tpb_consistency); break;
\r 
 305                 case IBPP::ilReadDirty :                tpb->Insert(isc_tpb_read_committed);
\r 
 306                                                                 tpb->Insert(isc_tpb_rec_version); break;
\r 
 307                 case IBPP::ilReadCommitted :    tpb->Insert(isc_tpb_read_committed);
\r 
 308                                                                                 tpb->Insert(isc_tpb_no_rec_version); break;
\r 
 309                 case IBPP::ilConcurrency :                                              tpb->Insert(isc_tpb_concurrency); break;
\r 
 312     if (lr == IBPP::lrNoWait) tpb->Insert(isc_tpb_nowait);
\r 
 313     else tpb->Insert(isc_tpb_wait);
\r 
 315         if (flags & IBPP::tfIgnoreLimbo)        tpb->Insert(isc_tpb_ignore_limbo);
\r 
 316         if (flags & IBPP::tfAutoCommit)         tpb->Insert(isc_tpb_autocommit);
\r 
 317         if (flags & IBPP::tfNoAutoUndo)         tpb->Insert(isc_tpb_no_auto_undo);
\r 
 319         mTPBs.push_back(tpb);
\r 
 321         // Signals the Database object that it has been attached to the Transaction
\r 
 322         dbi->AttachTransactionImpl(this);
\r 
 325 void TransactionImpl::DetachDatabaseImpl(DatabaseImpl* dbi)
\r 
 328                 throw LogicExceptionImpl("Transaction::DetachDatabase",
\r 
 329                                 _("Can't detach a Database if Transaction started."));
\r 
 331                 throw LogicExceptionImpl("Transaction::DetachDatabase",
\r 
 332                                 _("Can't detach a null Database."));
\r 
 334         std::vector<DatabaseImpl*>::iterator pos =
\r 
 335                 std::find(mDatabases.begin(), mDatabases.end(), dbi);
\r 
 336         if (pos != mDatabases.end())
\r 
 338                 size_t index = pos - mDatabases.begin();
\r 
 339                 TPB* tpb = mTPBs[index];
\r 
 340                 mDatabases.erase(pos);
\r 
 341                 mTPBs.erase(mTPBs.begin()+index);
\r 
 345         // Signals the Database object that it has been detached from the Transaction
\r 
 346         dbi->DetachTransactionImpl(this);
\r 
 349 TransactionImpl::TransactionImpl(DatabaseImpl* db,
\r 
 350         IBPP::TAM am, IBPP::TIL il, IBPP::TLR lr, IBPP::TFF flags)
\r 
 354         AttachDatabaseImpl(db, am, il, lr, flags);
\r 
 357 TransactionImpl::~TransactionImpl()
\r 
 359         // Rollback the transaction if it was Started
\r 
 360         try { if (Started()) Rollback(); }
\r 
 363         // Let's detach cleanly all Blobs from this Transaction.
\r 
 364         // No Blob object can still maintain pointers to this
\r 
 365         // Transaction which is disappearing.
\r 
 367         // We use a reverse traversal of the array to avoid loops.
\r 
 368         // The array shrinks on each loop (mBbCount decreases).
\r 
 369         // And during the deletion, there is a packing of the array through a
\r 
 370         // copy of elements from the end to the beginning of the array.
\r 
 372                 while (mBlobs.size() > 0)
\r 
 373                         mBlobs.back()->DetachTransactionImpl();
\r 
 376         // Let's detach cleanly all Arrays from this Transaction.
\r 
 377         // No Array object can still maintain pointers to this
\r 
 378         // Transaction which is disappearing.
\r 
 380                 while (mArrays.size() > 0)
\r 
 381                         mArrays.back()->DetachTransactionImpl();
\r 
 384         // Let's detach cleanly all Statements from this Transaction.
\r 
 385         // No Statement object can still maintain pointers to this
\r 
 386         // Transaction which is disappearing.
\r 
 388                 while (mStatements.size() > 0)
\r 
 389                         mStatements.back()->DetachTransactionImpl();
\r 
 392         // Very important : let's detach cleanly all Databases from this
\r 
 393         // Transaction. No Database object can still maintain pointers to this
\r 
 394         // Transaction which is disappearing.
\r 
 396                 while (mDatabases.size() > 0)
\r 
 398                         size_t i = mDatabases.size()-1;
\r 
 399                         DetachDatabaseImpl(mDatabases[i]);      // <-- remove link to database from mTPBs
\r 
 400                                                                                         // array and destroy TPB object
\r 
 401                                                                                         // Fixed : Maxim Abrashkin on 12 Jun 2002
\r 
 402                         //mDatabases.back()->DetachTransaction(this);
\r