/*- * Copyright 2005-2007 Guram Dukashvili * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ //--------------------------------------------------------------------------- #include //--------------------------------------------------------------------------- namespace fbcpp { //--------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////////////////// //--------------------------------------------------------------------------- TPB::TPBParam TPB::params[] = { { "version1", isc_tpb_version1 }, { "version3", isc_tpb_version3 }, { "consistency", isc_tpb_consistency }, { "concurrency", isc_tpb_concurrency }, { "shared", isc_tpb_shared }, { "protected", isc_tpb_protected }, { "exclusive", isc_tpb_exclusive }, { "wait", isc_tpb_wait }, { "nowait", isc_tpb_nowait }, { "read", isc_tpb_read }, { "write", isc_tpb_write }, { "lock_read", isc_tpb_lock_read }, { "lock_write", isc_tpb_lock_write }, { "verb_time", isc_tpb_verb_time }, { "commit_time", isc_tpb_commit_time }, { "ignore_limbo", isc_tpb_ignore_limbo }, { "read_committed", isc_tpb_read_committed }, { "autocommit", isc_tpb_autocommit }, { "rec_version", isc_tpb_rec_version }, { "no_rec_version", isc_tpb_no_rec_version }, { "restart_requests", isc_tpb_restart_requests }, { "no_auto_undo", isc_tpb_no_auto_undo } }; //--------------------------------------------------------------------------- TPB::TPB() : tpb_(NULL), tpbLen_(0) { } //--------------------------------------------------------------------------- TPB::~TPB() { ksys::xfree(tpb_); } //--------------------------------------------------------------------------- TPB & TPB::clear() { ksys::xfree(tpb_); tpb_ = NULL; tpbLen_ = 0; return *this; } //--------------------------------------------------------------------------- TPB & TPB::writeChar(uintptr_t code) { ksys::xrealloc(tpb_, tpbLen_ + 1); tpb_[tpbLen_++] = char(code); return *this; } //--------------------------------------------------------------------------- intptr_t TPB::writeISCCode(const utf8::String & name) { intptr_t i; for( i = sizeof(params) / sizeof(params[0]) - 1; i >= 0; i-- ){ if( name.strcasecmp(params[i].name_) == 0 ){ ksys::xrealloc(tpb_, tpbLen_ + 1); tpb_[tpbLen_++] = params[i].number; return params[i].number; } } return i; } //--------------------------------------------------------------------------- TPB & TPB::add(const utf8::String & name) { writeISCCode(name); return *this; } //--------------------------------------------------------------------------- TPB & TPB::add(const utf8::String & name, const utf8::String & tableName) { writeISCCode(name); const char * s = tableName.c_str(); do writeChar(*s); while( *s != '\0' ); return *this; } //--------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////////////////// //--------------------------------------------------------------------------- Transaction::Transaction() : handle_(0), startCount_(0), lastRetainingTransaction_(lrtNone) { #ifndef __GNUG__ databases_.ownsObjects(false); #endif dsqlStatements_.ownsObjects(false); tpbs_.ownsObjects(true); } //--------------------------------------------------------------------------- Transaction::~Transaction() { detach(); while( dsqlStatements_.count() > 0 ){ DSQLStatement * dsqlStatement = dsqlStatements_.objectOfIndex(0); dsqlStatement->free(); dsqlStatement->transaction_ = NULL; dsqlStatements_.removeByIndex(0); } } //--------------------------------------------------------------------------- Transaction & Transaction::attach(Database & database) { utf8::String dbKey(utf8::ptr2Str(&database)); tpbs_.add(newObject(),dbKey); try{ #if __GNUG__ databases_.add(&database); #else databases_.add(&database, dbKey); #endif try{ database.transactions_.add(this, utf8::ptr2Str(this)); } catch( ksys::ExceptionSP & ){ #if __GNUG__ databases_.resize(databases_.count() - 1); #else databases_.removeByKey(dbKey); #endif throw; } } catch( ksys::ExceptionSP & ){ tpbs_.removeByKey(dbKey); throw; } return *this; } //--------------------------------------------------------------------------- Transaction & Transaction::detach(Database & database) { while( active() ) rollback(); tpbs_.removeByKey(utf8::ptr2Str(&database)); database.transactions_.removeByObject(this); #if __GNUG__ databases_.remove(databases_.search(&database)); #else databases_.removeByObject(&database); #endif return *this; } //--------------------------------------------------------------------------- Transaction & Transaction::detach() { #if __GNUG__ while( databases_.count() > 0 ) detach(*databases_[0]); #else while( databases_.count() > 0 ) detach(*databases_.objectOfIndex(0)); #endif return *this; } //--------------------------------------------------------------------------- Transaction & Transaction::retainingHelper() { ISC_STATUS_ARRAY status; switch( lastRetainingTransaction_ ){ case lrtNone : break; case lrtCommit : if( api.isc_commit_transaction(status, &handle_) != 0 ) exceptionHandler(newObjectV1C2(status, __PRETTY_FUNCTION__)); break; case lrtRollback : if( api.isc_rollback_transaction(status, &handle_) != 0 ) exceptionHandler(newObjectV1C2(status, __PRETTY_FUNCTION__)); break; } lastRetainingTransaction_ = lrtNone; return *this; } //--------------------------------------------------------------------------- Transaction & Transaction::isolation(const utf8::String & isolation) { isolation_ = isolation; for( intptr_t i = databases_.count() - 1; i >= 0; i-- ){ #if __GNUG__ TPB * tpb = tpbs_.objectOfKey(utf8::ptr2Str(databases_[i])); #else TPB * tpb = tpbs_.objectOfKey(databases_.keyOfIndex(i)); #endif tpb->clear(); } return *this; } //--------------------------------------------------------------------------- Transaction & Transaction::start() { if( !attached() ) newObjectV1C2((ISC_STATUS *) NULL, __PRETTY_FUNCTION__); if( startCount_ == 0 ){ retainingHelper(); // pumping retaining transactions ksys::AutoPtr tebVector; tebVector.alloc(sizeof(ISC_TEB) * databases_.count()); for( intptr_t i = databases_.count() - 1; i >= 0; i-- ){ #if __GNUG__ tebVector[i].db_ptr = &databases_[i]->handle_; TPB * tpb = tpbs_.objectOfKey(utf8::ptr2Str(databases_[i])); #else tebVector[i].db_ptr = &databases_.objectOfIndex(i)->handle_; TPB * tpb = tpbs_.objectOfKey(databases_.keyOfIndex(i)); #endif if( tpb->tpbLen_ == 0 ){ if( isolation_.strcasecmp("REPEATABLE") == 0 ){ tpb->add("version3"); tpb->add("concurrency"); tpb->add("read"); tpb->add("write"); tpb->add("nowait"); } else if( isolation_.strcasecmp("SERIALIZABLE") == 0 ){ tpb->add("version3"); tpb->add("consistency"); tpb->add("read"); tpb->add("write"); tpb->add("wait"); } else if( isolation_.strcasecmp("READ_COMMITTED") == 0 ){ l1: tpb->add("version3"); tpb->add("read_committed"); tpb->add("rec_version"); tpb->add("nowait"); } else if( isolation_.strcasecmp("READ_ONLY_COMMITTED") == 0 ){ tpb->add("version3"); tpb->add("read"); tpb->add("read_committed"); tpb->add("rec_version"); tpb->add("nowait"); } else goto l1; } tebVector[i].tpb_len = tpb->tpbLen_; tebVector[i].tpb_ptr = tpb->tpb_; } ISC_STATUS_ARRAY status; if( api.isc_start_multiple(status, &handle_, (short) databases_.count(), tebVector.ptr()) != 0 ) exceptionHandler(newObjectV1C2(status, __PRETTY_FUNCTION__)); } startCount_++; lastRetainingTransaction_ = lrtNone; return *this; } //--------------------------------------------------------------------------- Transaction & Transaction::prepare() { if( !active() ) exceptionHandler(newObjectV1C2((ISC_STATUS *) NULL, __PRETTY_FUNCTION__)); ISC_STATUS_ARRAY status; if( api.isc_prepare_transaction(status, &handle_) != 0 ) exceptionHandler(newObjectV1C2(status, __PRETTY_FUNCTION__)); return *this; } //--------------------------------------------------------------------------- Transaction & Transaction::commit() { if( !active() ) exceptionHandler(newObjectV1C2((ISC_STATUS *) NULL, __PRETTY_FUNCTION__)); ISC_STATUS_ARRAY status; if( startCount_ == 1 ){ if( api.isc_commit_transaction(status, &handle_) != 0 ) exceptionHandler(newObjectV1C2(status, __PRETTY_FUNCTION__)); startCount_--; } else if( startCount_ == 0 ){ retainingHelper(); } else{ startCount_--; } lastRetainingTransaction_ = lrtNone; return *this; } //--------------------------------------------------------------------------- Transaction & Transaction::commitRetaining() { if( !active() ) exceptionHandler(newObjectV1C2((ISC_STATUS *) NULL, __PRETTY_FUNCTION__)); if( startCount_ == 1 ){ ISC_STATUS_ARRAY status; if( api.isc_commit_retaining(status, &handle_) != 0 ) exceptionHandler(newObjectV1C2(status, __PRETTY_FUNCTION__)); lastRetainingTransaction_ = lrtCommit; startCount_--; } else if( startCount_ == 0 ){ retainingHelper(); } else{ startCount_--; lastRetainingTransaction_ = lrtNone; } return *this; } //--------------------------------------------------------------------------- Transaction & Transaction::rollback() { if( !active() ) exceptionHandler(newObjectV1C2((ISC_STATUS *) NULL, __PRETTY_FUNCTION__)); ISC_STATUS_ARRAY status; if( startCount_ == 1 ){ if( api.isc_rollback_transaction(status, &handle_) != 0 ) exceptionHandler(newObjectV1C2(status, __PRETTY_FUNCTION__)); startCount_--; } else if( startCount_ == 0 ){ retainingHelper(); } else{ startCount_--; } lastRetainingTransaction_ = lrtNone; return *this; } //--------------------------------------------------------------------------- Transaction & Transaction::rollbackRetaining() { if( !active() ) exceptionHandler(newObjectV1C2((ISC_STATUS *) NULL, __PRETTY_FUNCTION__)); if( startCount_ == 1 ){ ISC_STATUS_ARRAY status; if( api.isc_rollback_retaining(status, &handle_) != 0 ) exceptionHandler(newObjectV1C2(status, __PRETTY_FUNCTION__)); lastRetainingTransaction_ = lrtRollback; startCount_--; } else if( startCount_ == 0 ){ retainingHelper(); } else{ startCount_--; lastRetainingTransaction_ = lrtNone; } return *this; } //--------------------------------------------------------------------------- void Transaction::processingException(ksys::Exception * e) { EClientServer * p = dynamic_cast< EClientServer *>(e); if( p != NULL ){ if( p->isFatalError() ){ startCount_ = 0; } else{ if( active() ) rollbackRetaining(); } } } //--------------------------------------------------------------------------- void Transaction::staticExceptionHandler(ksys::Exception * e) { processingException(e); for( intptr_t i = databases_.count() - 1; i >= 0; i-- ) databases_[i]->processingException(e); e->throwSP(); } //--------------------------------------------------------------------------- void Transaction::exceptionHandler(ksys::Exception * e) { staticExceptionHandler(e); } //--------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////////////////// // properties /////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// //--------------------------------------------------------------------------- TPB & Transaction::paramsByDatabase(Database & database) { return *tpbs_.objectOfKey(utf8::ptr2Str(&database)); } //--------------------------------------------------------------------------- } // namespace fbcpp //---------------------------------------------------------------------------