tra.cpp (Firebird-3.0.2.32703-0.tar.bz2) | : | tra.cpp (Firebird-3.0.4.33054-0.tar.bz2) | ||
---|---|---|---|---|
skipping to change at line 63 | skipping to change at line 63 | |||
#include "../common/isc_proto.h" | #include "../common/isc_proto.h" | |||
#include "../jrd/lck_proto.h" | #include "../jrd/lck_proto.h" | |||
#include "../jrd/met_proto.h" | #include "../jrd/met_proto.h" | |||
#include "../jrd/mov_proto.h" | #include "../jrd/mov_proto.h" | |||
#include "../jrd/pag_proto.h" | #include "../jrd/pag_proto.h" | |||
#include "../jrd/rlck_proto.h" | #include "../jrd/rlck_proto.h" | |||
#include "../jrd/tpc_proto.h" | #include "../jrd/tpc_proto.h" | |||
#include "../jrd/tra_proto.h" | #include "../jrd/tra_proto.h" | |||
#include "../jrd/vio_proto.h" | #include "../jrd/vio_proto.h" | |||
#include "../jrd/jrd_proto.h" | #include "../jrd/jrd_proto.h" | |||
#include "../jrd/scl_proto.h" | ||||
#include "../common/classes/ClumpletWriter.h" | #include "../common/classes/ClumpletWriter.h" | |||
#include "../common/classes/TriState.h" | #include "../common/classes/TriState.h" | |||
#include "../common/utils_proto.h" | #include "../common/utils_proto.h" | |||
#include "../lock/lock_proto.h" | #include "../lock/lock_proto.h" | |||
#include "../dsql/dsql.h" | #include "../dsql/dsql.h" | |||
#include "../dsql/dsql_proto.h" | #include "../dsql/dsql_proto.h" | |||
#include "../common/StatusArg.h" | #include "../common/StatusArg.h" | |||
#include "../jrd/trace/TraceManager.h" | #include "../jrd/trace/TraceManager.h" | |||
#include "../jrd/trace/TraceJrdHelpers.h" | #include "../jrd/trace/TraceJrdHelpers.h" | |||
#include "../jrd/Function.h" | #include "../jrd/Function.h" | |||
#include "../jrd/Collation.h" | #include "../jrd/Collation.h" | |||
#include "../jrd/Mapping.h" | #include "../jrd/Mapping.h" | |||
#include "../jrd/DbCreators.h" | #include "../jrd/DbCreators.h" | |||
#include "../common/os/fbsyslog.h" | ||||
const int DYN_MSG_FAC = 8; | const int DYN_MSG_FAC = 8; | |||
using namespace Jrd; | using namespace Jrd; | |||
using namespace Ods; | using namespace Ods; | |||
using namespace Firebird; | using namespace Firebird; | |||
typedef Firebird::GenericMap<Firebird::Pair<Firebird::NonPooled<USHORT, UCHAR> > > RelationLockTypeMap; | typedef Firebird::GenericMap<Firebird::Pair<Firebird::NonPooled<USHORT, UCHAR> > > RelationLockTypeMap; | |||
#ifdef SUPERSERVER_V2 | #ifdef SUPERSERVER_V2 | |||
skipping to change at line 97 | skipping to change at line 99 | |||
#else | #else | |||
static header_page* bump_transaction_id(thread_db*, WIN*, bool); | static header_page* bump_transaction_id(thread_db*, WIN*, bool); | |||
#endif | #endif | |||
static void retain_context(thread_db* tdbb, jrd_tra* transaction, bool commit, i nt state); | static void retain_context(thread_db* tdbb, jrd_tra* transaction, bool commit, i nt state); | |||
static void expand_view_lock(thread_db* tdbb, jrd_tra*, jrd_rel*, UCHAR lock_typ e, | static void expand_view_lock(thread_db* tdbb, jrd_tra*, jrd_rel*, UCHAR lock_typ e, | |||
const char* option_name, RelationLockTypeMap& lockmap, const int level); | const char* option_name, RelationLockTypeMap& lockmap, const int level); | |||
static tx_inv_page* fetch_inventory_page(thread_db*, WIN* window, ULONG sequence , USHORT lock_level); | static tx_inv_page* fetch_inventory_page(thread_db*, WIN* window, ULONG sequence , USHORT lock_level); | |||
static const char* get_lockname_v3(const UCHAR lock); | static const char* get_lockname_v3(const UCHAR lock); | |||
static ULONG inventory_page(thread_db*, ULONG); | static ULONG inventory_page(thread_db*, ULONG); | |||
static int limbo_transaction(thread_db*, TraNumber id); | static int limbo_transaction(thread_db*, TraNumber id); | |||
static void link_transaction(thread_db*, jrd_tra*); | ||||
static void restart_requests(thread_db*, jrd_tra*); | static void restart_requests(thread_db*, jrd_tra*); | |||
static void start_sweeper(thread_db*); | static void start_sweeper(thread_db*); | |||
static THREAD_ENTRY_DECLARE sweep_database(THREAD_ENTRY_PARAM); | static THREAD_ENTRY_DECLARE sweep_database(THREAD_ENTRY_PARAM); | |||
static void transaction_flush(thread_db* tdbb, USHORT flush_flag, TraNumber tra_ number); | static void transaction_flush(thread_db* tdbb, USHORT flush_flag, TraNumber tra_ number); | |||
static void transaction_options(thread_db*, jrd_tra*, const UCHAR*, USHORT); | static void transaction_options(thread_db*, jrd_tra*, const UCHAR*, USHORT); | |||
static void transaction_start(thread_db* tdbb, jrd_tra* temp); | static void transaction_start(thread_db* tdbb, jrd_tra* temp); | |||
static const UCHAR sweep_tpb[] = | static const UCHAR sweep_tpb[] = | |||
{ | { | |||
isc_tpb_version1, isc_tpb_read, | isc_tpb_version1, isc_tpb_read, | |||
skipping to change at line 247 | skipping to change at line 248 | |||
const ULONG last = ceiling / trans_per_tip; | const ULONG last = ceiling / trans_per_tip; | |||
ULONG number = active % trans_per_tip; | ULONG number = active % trans_per_tip; | |||
TraNumber limbo = 0; | TraNumber limbo = 0; | |||
for (ULONG sequence = active / trans_per_tip; sequence <= last; sequence+ +, number = 0) | for (ULONG sequence = active / trans_per_tip; sequence <= last; sequence+ +, number = 0) | |||
{ | { | |||
window.win_page = inventory_page(tdbb, sequence); | window.win_page = inventory_page(tdbb, sequence); | |||
tx_inv_page* tip = (tx_inv_page*) CCH_FETCH(tdbb, &window, LCK_wr ite, pag_transactions); | tx_inv_page* tip = (tx_inv_page*) CCH_FETCH(tdbb, &window, LCK_wr ite, pag_transactions); | |||
TraNumber max = ceiling - (TraNumber) sequence * trans_per_tip; | TraNumber max = ceiling - (TraNumber) sequence * trans_per_tip; | |||
if (max > trans_per_tip) | if (max >= trans_per_tip) | |||
max = trans_per_tip - 1; | max = trans_per_tip - 1; | |||
for (; number <= max; number++) | for (; number <= max; number++) | |||
{ | { | |||
const ULONG trans_offset = TRANS_OFFSET(number); | const ULONG trans_offset = TRANS_OFFSET(number); | |||
UCHAR* byte = tip->tip_transactions + trans_offset; | UCHAR* byte = tip->tip_transactions + trans_offset; | |||
const USHORT shift = TRANS_SHIFT(number); | const USHORT shift = TRANS_SHIFT(number); | |||
const int state = (*byte >> shift) & TRA_MASK; | const int state = (*byte >> shift) & TRA_MASK; | |||
if (state == tra_limbo && limbo == 0) | if (state == tra_limbo && limbo == 0) | |||
limbo = (TraNumber) sequence * trans_per_tip + nu mber; | limbo = (TraNumber) sequence * trans_per_tip + nu mber; | |||
else if (state == tra_active) | else if (state == tra_active) | |||
skipping to change at line 809 | skipping to change at line 810 | |||
if (!dbb || dbb->dbb_flags & DBB_read_only || dbb->dbb_flags & DBB_new || | if (!dbb || dbb->dbb_flags & DBB_read_only || dbb->dbb_flags & DBB_new || | |||
dbb->dbb_oldest_transaction == 0) | dbb->dbb_oldest_transaction == 0) | |||
{ | { | |||
return; | return; | |||
} | } | |||
WIN window(HEADER_PAGE_NUMBER); | WIN window(HEADER_PAGE_NUMBER); | |||
header_page* header = (header_page*)CCH_FETCH(tdbb, &window, LCK_write, p ag_header); | header_page* header = (header_page*)CCH_FETCH(tdbb, &window, LCK_write, p ag_header); | |||
if (dbb->dbb_oldest_active > header->hdr_oldest_active || | const TraNumber next_transaction = Ods::getNT(header); | |||
dbb->dbb_oldest_transaction > header->hdr_oldest_transaction || | const TraNumber oldest_transaction = Ods::getOIT(header); | |||
dbb->dbb_oldest_snapshot > header->hdr_oldest_snapshot) | const TraNumber oldest_active = Ods::getOAT(header); | |||
const TraNumber oldest_snapshot = Ods::getOST(header); | ||||
fb_assert(dbb->dbb_next_transaction <= next_transaction); | ||||
if (dbb->dbb_oldest_active > oldest_active || | ||||
dbb->dbb_oldest_transaction > oldest_transaction || | ||||
dbb->dbb_oldest_snapshot > oldest_snapshot || | ||||
dbb->dbb_next_transaction > next_transaction) | ||||
{ | { | |||
CCH_MARK_MUST_WRITE(tdbb, &window); | CCH_MARK_MUST_WRITE(tdbb, &window); | |||
if (dbb->dbb_oldest_active > header->hdr_oldest_active) | if (dbb->dbb_oldest_active > oldest_active) | |||
header->hdr_oldest_active = dbb->dbb_oldest_active; | Ods::writeOAT(header, dbb->dbb_oldest_active); | |||
if (dbb->dbb_oldest_transaction > header->hdr_oldest_transaction) | if (dbb->dbb_oldest_transaction > oldest_transaction) | |||
header->hdr_oldest_transaction = dbb->dbb_oldest_transact | Ods::writeOIT(header, dbb->dbb_oldest_transaction); | |||
ion; | ||||
if (dbb->dbb_oldest_snapshot > header->hdr_oldest_snapshot) | if (dbb->dbb_oldest_snapshot > oldest_snapshot) | |||
header->hdr_oldest_snapshot = dbb->dbb_oldest_snapshot; | Ods::writeOST(header, dbb->dbb_oldest_snapshot); | |||
if (dbb->dbb_next_transaction > next_transaction) | ||||
Ods::writeNT(header, dbb->dbb_next_transaction); | ||||
} | } | |||
CCH_RELEASE(tdbb, &window); | CCH_RELEASE(tdbb, &window); | |||
} | } | |||
void TRA_post_resources(thread_db* tdbb, jrd_tra* transaction, ResourceList& res ources) | void TRA_post_resources(thread_db* tdbb, jrd_tra* transaction, ResourceList& res ources) | |||
{ | { | |||
/************************************** | /************************************** | |||
* | * | |||
* T R A _ p o s t _ r e s o u r c e s | * T R A _ p o s t _ r e s o u r c e s | |||
skipping to change at line 1115 | skipping to change at line 1127 | |||
ERR_post(Arg::Gds(isc_no_recon) << | ERR_post(Arg::Gds(isc_no_recon) << | |||
Arg::Gds(isc_tra_state) << Arg::Num(number) << A rg::Str(text)); | Arg::Gds(isc_tra_state) << Arg::Num(number) << A rg::Str(text)); | |||
} | } | |||
MemoryPool* const pool = attachment->createPool(); | MemoryPool* const pool = attachment->createPool(); | |||
Jrd::ContextPoolHolder context(tdbb, pool); | Jrd::ContextPoolHolder context(tdbb, pool); | |||
jrd_tra* const trans = jrd_tra::create(pool, attachment, NULL); | jrd_tra* const trans = jrd_tra::create(pool, attachment, NULL); | |||
trans->tra_number = number; | trans->tra_number = number; | |||
trans->tra_flags |= TRA_prepared | TRA_reconnected | TRA_write; | trans->tra_flags |= TRA_prepared | TRA_reconnected | TRA_write; | |||
link_transaction(tdbb, trans); | trans->linkToAttachment(attachment); | |||
return trans; | return trans; | |||
} | } | |||
void TRA_release_transaction(thread_db* tdbb, jrd_tra* transaction, Jrd::TraceTr ansactionEnd* trace) | void TRA_release_transaction(thread_db* tdbb, jrd_tra* transaction, Jrd::TraceTr ansactionEnd* trace) | |||
{ | { | |||
/************************************** | /************************************** | |||
* | * | |||
* T R A _ r e l e a s e _ t r a n s a c t i o n | * T R A _ r e l e a s e _ t r a n s a c t i o n | |||
* | * | |||
skipping to change at line 1210 | skipping to change at line 1222 | |||
jrd_rel* relation = rels[i]; | jrd_rel* relation = rels[i]; | |||
if (relation && (relation->rel_flags & REL_temp_tran)) | if (relation && (relation->rel_flags & REL_temp_tran)) | |||
relation->delPages(tdbb, transaction->tra_number) ; | relation->delPages(tdbb, transaction->tra_number) ; | |||
} | } | |||
} // end scope | } // end scope | |||
// Release the locks associated with the transaction | // Release the locks associated with the transaction | |||
if (transaction->tra_alter_db_lock) | ||||
LCK_release(tdbb, transaction->tra_alter_db_lock); | ||||
vec<Lock*>* vector = transaction->tra_relation_locks; | vec<Lock*>* vector = transaction->tra_relation_locks; | |||
if (vector) | if (vector) | |||
{ | { | |||
vec<Lock*>::iterator lock = vector->begin(); | vec<Lock*>::iterator lock = vector->begin(); | |||
for (ULONG i = 0; i < vector->count(); ++i, ++lock) | for (ULONG i = 0; i < vector->count(); ++i, ++lock) | |||
{ | { | |||
if (*lock) | if (*lock) | |||
LCK_release(tdbb, *lock); | LCK_release(tdbb, *lock); | |||
} | } | |||
} | } | |||
skipping to change at line 1238 | skipping to change at line 1253 | |||
delete transaction->tra_commit_sub_trans; | delete transaction->tra_commit_sub_trans; | |||
if (transaction->tra_flags & TRA_precommitted) | if (transaction->tra_flags & TRA_precommitted) | |||
TRA_precommited(tdbb, transaction->tra_number, 0); | TRA_precommited(tdbb, transaction->tra_number, 0); | |||
if (trace) | if (trace) | |||
trace->finish(ITracePlugin::RESULT_SUCCESS); | trace->finish(ITracePlugin::RESULT_SUCCESS); | |||
// Unlink the transaction from the attachment block | // Unlink the transaction from the attachment block | |||
for (jrd_tra** ptr = &attachment->att_transactions; *ptr; ptr = &(*ptr)-> | transaction->unlinkFromAttachment(); | |||
tra_next) | ||||
{ | ||||
if (*ptr == transaction) | ||||
{ | ||||
*ptr = transaction->tra_next; | ||||
break; | ||||
} | ||||
} | ||||
// Release transaction's under-modification-rpb list | // Release transaction's under-modification-rpb list | |||
delete transaction->tra_rpblist; | delete transaction->tra_rpblist; | |||
// Release the database snapshot, if any | // Release the database snapshot, if any | |||
delete transaction->tra_mon_snapshot; | delete transaction->tra_mon_snapshot; | |||
// Close all open DSQL cursors | // Close all open DSQL cursors | |||
while (transaction->tra_open_cursors.hasData()) | while (transaction->tra_open_cursors.hasData()) | |||
DsqlCursor::close(tdbb, transaction->tra_open_cursors.pop()); | DsqlCursor::close(tdbb, transaction->tra_open_cursors.pop()); | |||
// Release the transaction and its pool | // Release the transaction and its pool | |||
tdbb->setTransaction(NULL); | tdbb->setTransaction(NULL); | |||
JTransaction* jTra = transaction->getInterface(); | JTransaction* jTra = transaction->getInterface(true); // ASF: maybe it' s better to pass false? | |||
if (jTra) | if (jTra) | |||
{ | { | |||
jTra->setHandle(NULL); | jTra->setHandle(NULL); | |||
} | } | |||
jrd_tra::destroy(attachment, transaction); | jrd_tra::destroy(attachment, transaction); | |||
} | } | |||
void TRA_rollback(thread_db* tdbb, jrd_tra* transaction, const bool retaining_fl ag, | void TRA_rollback(thread_db* tdbb, jrd_tra* transaction, const bool retaining_fl ag, | |||
const bool force_flag) | const bool force_flag) | |||
{ | { | |||
skipping to change at line 1762 | skipping to change at line 1770 | |||
Jrd::Attachment* const attachment = tdbb->getAttachment(); | Jrd::Attachment* const attachment = tdbb->getAttachment(); | |||
jrd_tra* const tdbb_old_trans = tdbb->getTransaction(); | jrd_tra* const tdbb_old_trans = tdbb->getTransaction(); | |||
jrd_tra* transaction = NULL; | jrd_tra* transaction = NULL; | |||
// Clean up the temporary locks we've gotten in case anything goes wrong | // Clean up the temporary locks we've gotten in case anything goes wrong | |||
try { | try { | |||
// Identify ourselves as a sweeper thread. This accomplishes two goals: | // Identify ourselves as a sweeper thread. This accomplishes two | |||
// 1) Sweep transaction is started "precommitted" and | goals: | |||
// 2) Execution is throttled in JRD_reschedule() by | // 1) Sweep transaction is started "precommitted" and | |||
// yielding the processor when our quantum expires. | // 2) Execution is throttled in JRD_reschedule() by | |||
// yielding the processor when our quantum expires. | ||||
tdbb->tdbb_flags |= TDBB_sweeper; | ||||
TraceSweepEvent traceSweep(tdbb); | ||||
// Start a transaction, if necessary, to perform the sweep. | ||||
// Save the transaction's oldest snapshot as it is refreshed | ||||
// during the course of the database sweep. Since it is used | ||||
// below to advance the OIT we must save it before it changes. | ||||
transaction = TRA_start(tdbb, sizeof(sweep_tpb), sweep_tpb); | ||||
TraNumber transaction_oldest_active = transaction->tra_oldest_active; | ||||
tdbb->setTransaction(transaction); | ||||
// The garbage collector runs asynchronously with respect to | ||||
// our database sweep. This isn't good enough since we must | ||||
// be absolutely certain that all dead transactions have been | ||||
// swept from the database before advancing the OIT. Turn off | ||||
// the "notify garbage collector" flag for the attachment and | ||||
// synchronously perform the garbage collection ourselves. | ||||
attachment->att_flags &= ~ATT_notify_gc; | ||||
if (VIO_sweep(tdbb, transaction, &traceSweep)) | ||||
{ | ||||
// At this point, we know that no record versions belonging to de | ||||
ad | ||||
// transactions remain anymore. However, there may still be limbo | ||||
// transactions, so we need to find the oldest one between tra_ol | ||||
dest and tra_top. | ||||
// As our transaction is read-committed (see sweep_tpb), we have | ||||
to scan | ||||
// the global TIP cache. | ||||
int oldest_state = 0; | ||||
const TraNumber oldest_limbo = | ||||
TPC_find_states(tdbb, transaction->tra_oldest, transactio | ||||
n->tra_top - 1, | ||||
1 << tra_limbo, oldest_st | ||||
ate); | ||||
const TraNumber active = oldest_limbo ? oldest_limbo : transactio | ||||
n->tra_top; | ||||
// Flush page buffers to insure that no dangling records from | ||||
// dead transactions are left on-disk. This must be done before | ||||
// the OIT is advanced and the header page is written to disk. | ||||
// If the header page was written before flushing the page buffer | ||||
s | ||||
// and there was a server crash, the dead records would appear | ||||
// committed since their TID would now be less than the OIT recor | ||||
ded | ||||
// in the database. | ||||
CCH_flush(tdbb, FLUSH_SWEEP, 0); | tdbb->tdbb_flags |= TDBB_sweeper; | |||
WIN window(HEADER_PAGE_NUMBER); | TraceSweepEvent traceSweep(tdbb); | |||
header_page* const header = (header_page*) CCH_FETCH(tdbb, &windo | ||||
w, LCK_write, pag_header); | // Start a transaction, if necessary, to perform the sweep. | |||
// Save the transaction's oldest snapshot as it is refreshed | ||||
// during the course of the database sweep. Since it is used | ||||
// below to advance the OIT we must save it before it changes. | ||||
transaction = TRA_start(tdbb, sizeof(sweep_tpb), sweep_tpb); | ||||
if (Ods::getOIT(header) < --transaction_oldest_active) | TraNumber transaction_oldest_active = transaction->tra_oldest_act | |||
ive; | ||||
tdbb->setTransaction(transaction); | ||||
// The garbage collector runs asynchronously with respect to | ||||
// our database sweep. This isn't good enough since we must | ||||
// be absolutely certain that all dead transactions have been | ||||
// swept from the database before advancing the OIT. Turn off | ||||
// the "notify garbage collector" flag for the attachment and | ||||
// synchronously perform the garbage collection ourselves. | ||||
attachment->att_flags &= ~ATT_notify_gc; | ||||
if (VIO_sweep(tdbb, transaction, &traceSweep)) | ||||
{ | { | |||
CCH_MARK_MUST_WRITE(tdbb, &window); | // At this point, we know that no record versions belongi | |||
Ods::writeOIT(header, MIN(active, transaction_oldest_acti | ng to dead | |||
ve)); | // transactions remain anymore. However, there may still | |||
} | be limbo | |||
// transactions, so we need to find the oldest one betwee | ||||
n tra_oldest and tra_top. | ||||
// As our transaction is read-committed (see sweep_tpb), | ||||
we have to scan | ||||
// the global TIP cache. | ||||
traceSweep.update(header); | int oldest_state = 0; | |||
const TraNumber oldest_limbo = | ||||
TPC_find_states(tdbb, transaction->tra_oldest, tr | ||||
ansaction->tra_top - 1, | ||||
1 << tra_limbo, o | ||||
ldest_state); | ||||
CCH_RELEASE(tdbb, &window); | const TraNumber active = oldest_limbo ? oldest_limbo : tr ansaction->tra_top; | |||
traceSweep.finish(); | // Flush page buffers to insure that no dangling records | |||
} | from | |||
// dead transactions are left on-disk. This must be done | ||||
before | ||||
// the OIT is advanced and the header page is written to | ||||
disk. | ||||
// If the header page was written before flushing the pag | ||||
e buffers | ||||
// and there was a server crash, the dead records would a | ||||
ppear | ||||
// committed since their TID would now be less than the O | ||||
IT recorded | ||||
// in the database. | ||||
TRA_commit(tdbb, transaction, false); | CCH_flush(tdbb, FLUSH_SWEEP, 0); | |||
tdbb->tdbb_flags &= ~TDBB_sweeper; | WIN window(HEADER_PAGE_NUMBER); | |||
tdbb->setTransaction(tdbb_old_trans); | header_page* const header = (header_page*) CCH_FETCH(tdbb | |||
dbb->clearSweepFlags(tdbb); | , &window, LCK_write, pag_header); | |||
} // try | ||||
if (Ods::getOIT(header) < --transaction_oldest_active) | ||||
{ | ||||
CCH_MARK_MUST_WRITE(tdbb, &window); | ||||
Ods::writeOIT(header, MIN(active, transaction_old | ||||
est_active)); | ||||
} | ||||
traceSweep.update(header); | ||||
CCH_RELEASE(tdbb, &window); | ||||
traceSweep.finish(); | ||||
} | ||||
TRA_commit(tdbb, transaction, false); | ||||
tdbb->tdbb_flags &= ~TDBB_sweeper; | ||||
tdbb->setTransaction(tdbb_old_trans); | ||||
dbb->clearSweepFlags(tdbb); | ||||
} | ||||
catch (const Firebird::Exception& ex) | catch (const Firebird::Exception& ex) | |||
{ | { | |||
iscLogException("Error during sweep:", ex); | PathName message = "Error during sweep of "; | |||
message += dbb->dbb_database_name; | ||||
message += ':'; | ||||
iscLogException(message.c_str(), ex); | ||||
ex.stuffException(tdbb->tdbb_status_vector); | ex.stuffException(tdbb->tdbb_status_vector); | |||
if (transaction) | if (transaction) | |||
{ | { | |||
try | try | |||
{ | { | |||
TRA_commit(tdbb, transaction, false); | TRA_commit(tdbb, transaction, false); | |||
} | } | |||
catch (const Firebird::Exception& ex2) | catch (const Firebird::Exception& ex2) | |||
skipping to change at line 2334 | skipping to change at line 2345 | |||
const ULONG trans_offset = TRANS_OFFSET(number); | const ULONG trans_offset = TRANS_OFFSET(number); | |||
const UCHAR* byte = tip->tip_transactions + trans_offset; | const UCHAR* byte = tip->tip_transactions + trans_offset; | |||
const USHORT shift = TRANS_SHIFT(number); | const USHORT shift = TRANS_SHIFT(number); | |||
const int state = (*byte >> shift) & TRA_MASK; | const int state = (*byte >> shift) & TRA_MASK; | |||
CCH_RELEASE(tdbb, &window); | CCH_RELEASE(tdbb, &window); | |||
return state; | return state; | |||
} | } | |||
static void link_transaction(thread_db* tdbb, jrd_tra* transaction) | void jrd_tra::unlinkFromAttachment() | |||
{ | ||||
for (jrd_tra** ptr = &tra_attachment->att_transactions; *ptr; ptr = &(*pt | ||||
r)->tra_next) | ||||
{ | ||||
if (*ptr == this) | ||||
{ | ||||
*ptr = tra_next; | ||||
return; | ||||
} | ||||
} | ||||
tra_abort("transaction to unlink is missing in the attachment"); | ||||
} | ||||
void jrd_tra::linkToAttachment(Attachment* attachment) | ||||
{ | { | |||
/************************************** | /************************************** | |||
* | * | |||
* l i n k _ t r a n s a c t i o n | * l i n k _ t r a n s a c t i o n | |||
* | * | |||
************************************** | ************************************** | |||
* | * | |||
* Functional description | * Functional description | |||
* Link transaction block into database attachment. | * Link transaction block into database attachment. | |||
* | * | |||
**************************************/ | **************************************/ | |||
SET_TDBB(tdbb); | tra_next = attachment->att_transactions; | |||
attachment->att_transactions = this; | ||||
} | ||||
Jrd::Attachment* attachment = tdbb->getAttachment(); | void jrd_tra::tra_abort(const char* reason) | |||
transaction->tra_next = attachment->att_transactions; | { | |||
attachment->att_transactions = transaction; | string buff; | |||
buff.printf("Failure working with transactions list: %s", reason); | ||||
Syslog::Record(Syslog::Error, buff.c_str()); | ||||
gds__log(buff.c_str()); | ||||
#ifdef DEV_BUILD | ||||
abort(); | ||||
#endif | ||||
} | } | |||
static void restart_requests(thread_db* tdbb, jrd_tra* trans) | static void restart_requests(thread_db* tdbb, jrd_tra* trans) | |||
{ | { | |||
/************************************** | /************************************** | |||
* | * | |||
* r e s t a r t _ r e q u e s t s | * r e s t a r t _ r e q u e s t s | |||
* | * | |||
************************************** | ************************************** | |||
* | * | |||
skipping to change at line 3160 | skipping to change at line 3193 | |||
#endif // SUPERSERVER_V2 | #endif // SUPERSERVER_V2 | |||
// Allocate pool and transactions block. Since, by policy, | // Allocate pool and transactions block. Since, by policy, | |||
// all transactions older than the oldest are either committed | // all transactions older than the oldest are either committed | |||
// or cleaned up, they can be all considered as committed. To | // or cleaned up, they can be all considered as committed. To | |||
// make everything simpler, round down the oldest to a multiple | // make everything simpler, round down the oldest to a multiple | |||
// of four, which puts the transaction on a byte boundary. | // of four, which puts the transaction on a byte boundary. | |||
TraNumber base = oldest & ~TRA_MASK; | TraNumber base = oldest & ~TRA_MASK; | |||
const ULONG top = (dbb->dbb_flags & DBB_read_only) ? | const TraNumber top = (dbb->dbb_flags & DBB_read_only) ? | |||
dbb->dbb_next_transaction : number; | dbb->dbb_next_transaction : number; | |||
if (!(trans->tra_flags & TRA_read_committed) && (top >= oldest)) | if (!(trans->tra_flags & TRA_read_committed) && (top >= oldest)) | |||
{ | { | |||
const FB_SIZE_T length = (top + 1 - base + TRA_MASK) / 4; | const FB_SIZE_T length = (top + 1 - base + TRA_MASK) / 4; | |||
trans->tra_transactions.resize(length); | trans->tra_transactions.resize(length); | |||
} | } | |||
trans->tra_number = number; | trans->tra_number = number; | |||
trans->tra_top = top; | trans->tra_top = top; | |||
skipping to change at line 3199 | skipping to change at line 3232 | |||
#ifndef SUPERSERVER_V2 | #ifndef SUPERSERVER_V2 | |||
if (!dbb->readOnly()) | if (!dbb->readOnly()) | |||
CCH_RELEASE(tdbb, &window); | CCH_RELEASE(tdbb, &window); | |||
#endif | #endif | |||
ERR_post(Arg::Gds(isc_lock_conflict)); | ERR_post(Arg::Gds(isc_lock_conflict)); | |||
} | } | |||
// Link the transaction to the attachment block before releasing | // Link the transaction to the attachment block before releasing | |||
// header page for handling signals. | // header page for handling signals. | |||
link_transaction(tdbb, trans); | trans->linkToAttachment(attachment); | |||
try | ||||
{ | ||||
#ifndef SUPERSERVER_V2 | #ifndef SUPERSERVER_V2 | |||
if (!dbb->readOnly()) | if (!dbb->readOnly()) | |||
CCH_RELEASE(tdbb, &window); | CCH_RELEASE(tdbb, &window); | |||
#endif | #endif | |||
if (dbb->readOnly()) | if (dbb->readOnly()) | |||
{ | { | |||
// Set transaction flags to TRA_precommitted, TRA_readonly | // Set transaction flags to TRA_precommitted, TRA_readonl | |||
trans->tra_flags |= (TRA_readonly | TRA_precommitted); | y | |||
} | trans->tra_flags |= (TRA_readonly | TRA_precommitted); | |||
} | ||||
// Next, take a snapshot of all transactions between the oldest interesti | ||||
ng | ||||
// transaction and the current. Don't bother to get a snapshot for | ||||
// read-committed transactions; they use the snapshot off the dbb block | ||||
// since they need to know what is currently committed. | ||||
if (trans->tra_flags & TRA_read_committed) | // Next, take a snapshot of all transactions between the oldest i | |||
TPC_initialize_tpc(tdbb, top); | nteresting | |||
else if (top > base) | // transaction and the current. Don't bother to get a snapshot f | |||
TRA_get_inventory(tdbb, trans->tra_transactions.begin(), base, to | or | |||
p); | // read-committed transactions; they use the snapshot off the dbb | |||
block | ||||
// Next task is to find the oldest active transaction on the system. Thi | // since they need to know what is currently committed. | |||
s | ||||
// is needed for garbage collection. Things are made ever so slightly | ||||
// more complicated by the fact that existing transaction may have oldest | ||||
// actives older than they are. | ||||
Lock temp_lock(tdbb, sizeof(TraNumber), LCK_tra, trans); | ||||
trans->tra_oldest_active = number; | ||||
base = oldest & ~TRA_MASK; | ||||
oldest_active = number; | ||||
bool cleanup = !(number % TRA_ACTIVE_CLEANUP); | ||||
int oldest_state; | ||||
for (; active < top; active++) | ||||
{ | ||||
if (trans->tra_flags & TRA_read_committed) | if (trans->tra_flags & TRA_read_committed) | |||
TPC_initialize_tpc(tdbb, top); | ||||
else if (top > base) | ||||
TRA_get_inventory(tdbb, trans->tra_transactions.begin(), | ||||
base, top); | ||||
// Next task is to find the oldest active transaction on the syst | ||||
em. This | ||||
// is needed for garbage collection. Things are made ever so sli | ||||
ghtly | ||||
// more complicated by the fact that existing transaction may hav | ||||
e oldest | ||||
// actives older than they are. | ||||
Lock temp_lock(tdbb, sizeof(TraNumber), LCK_tra, trans); | ||||
trans->tra_oldest_active = number; | ||||
base = oldest & ~TRA_MASK; | ||||
oldest_active = number; | ||||
bool cleanup = !(number % TRA_ACTIVE_CLEANUP); | ||||
int oldest_state; | ||||
for (; active < top; active++) | ||||
{ | { | |||
const ULONG mask = (1 << tra_active); | if (trans->tra_flags & TRA_read_committed) | |||
active = TPC_find_states(tdbb, active, top, mask, oldest_ | ||||
state); | ||||
if (!active) | ||||
{ | { | |||
active = number; | const ULONG mask = (1 << tra_active); | |||
break; | active = TPC_find_states(tdbb, active, top, mask, | |||
oldest_state); | ||||
if (!active) | ||||
{ | ||||
active = number; | ||||
break; | ||||
} | ||||
fb_assert(oldest_state == tra_active); | ||||
} | ||||
else | ||||
{ | ||||
const ULONG byte = TRANS_OFFSET(active - base); | ||||
const USHORT shift = TRANS_SHIFT(active); | ||||
oldest_state = (trans->tra_transactions[byte] >> | ||||
shift) & TRA_MASK; | ||||
} | } | |||
fb_assert(oldest_state == tra_active); | ||||
} | ||||
else | ||||
{ | ||||
const ULONG byte = TRANS_OFFSET(active - base); | ||||
const USHORT shift = TRANS_SHIFT(active); | ||||
oldest_state = (trans->tra_transactions[byte] >> shift) & | ||||
TRA_MASK; | ||||
} | ||||
if (oldest_state == tra_active) | if (oldest_state == tra_active) | |||
{ | ||||
temp_lock.lck_key.lck_long = active; | ||||
TraNumber data = LCK_read_data(tdbb, &temp_lock); | ||||
if (!data) | ||||
{ | { | |||
if (cleanup) | temp_lock.lck_key.lck_long = active; | |||
TraNumber data = LCK_read_data(tdbb, &temp_lock); | ||||
if (!data) | ||||
{ | { | |||
if (TRA_wait(tdbb, trans, active, jrd_tra | if (cleanup) | |||
::tra_no_wait) == tra_committed) | { | |||
cleanup = false; | if (TRA_wait(tdbb, trans, active, | |||
continue; | jrd_tra::tra_no_wait) == tra_committed) | |||
} | cleanup = false; | |||
continue; | ||||
} | ||||
data = active; | data = active; | |||
} | } | |||
oldest_active = MIN(oldest_active, active); | oldest_active = MIN(oldest_active, active); | |||
// Find the oldest record version that cannot be garbage | // Find the oldest record version that cannot be | |||
collected yet | garbage collected yet | |||
// by taking the minimum of all all versions needed by al | // by taking the minimum of all all versions need | |||
l active transactions. | ed by all active transactions. | |||
if (data < trans->tra_oldest_active) | if (data < trans->tra_oldest_active) | |||
trans->tra_oldest_active = data; | trans->tra_oldest_active = data; | |||
// If the lock data for any active transaction matches a | // If the lock data for any active transaction ma | |||
previously | tches a previously | |||
// computed value then there is no need to continue. Ther | // computed value then there is no need to contin | |||
e can't be | ue. There can't be | |||
// an older lock data in the remaining active transaction | // an older lock data in the remaining active tra | |||
s. | nsactions. | |||
if (trans->tra_oldest_active == oldest_snapshot) | if (trans->tra_oldest_active == oldest_snapshot) | |||
break; | break; | |||
// Query the minimum lock data for all active transaction | // Query the minimum lock data for all active tra | |||
locks. | nsaction locks. | |||
// This will be the oldest active snapshot used for regul | // This will be the oldest active snapshot used f | |||
ating garbage collection. | or regulating garbage collection. | |||
data = LCK_query_data(tdbb, LCK_tra, LCK_MIN); | data = LCK_query_data(tdbb, LCK_tra, LCK_MIN); | |||
if (data && data < trans->tra_oldest_active) | if (data && data < trans->tra_oldest_active) | |||
trans->tra_oldest_active = data; | trans->tra_oldest_active = data; | |||
break; | break; | |||
} | ||||
} | } | |||
} | ||||
// Calculate attachment-local oldest active and oldest snapshot numbers | // Calculate attachment-local oldest active and oldest snapshot n | |||
// looking at current attachment's transactions only. Calculated values | umbers | |||
// are used to determine garbage collection threshold for attachment-loca | // looking at current attachment's transactions only. Calculated | |||
l | values | |||
// data such as temporary tables (GTT's). | // are used to determine garbage collection threshold for attachm | |||
ent-local | ||||
// data such as temporary tables (GTT's). | ||||
trans->tra_att_oldest_active = number; | trans->tra_att_oldest_active = number; | |||
TraNumber att_oldest_active = number; | TraNumber att_oldest_active = number; | |||
TraNumber att_oldest_snapshot = number; | TraNumber att_oldest_snapshot = number; | |||
for (jrd_tra* tx_att = attachment->att_transactions; tx_att; tx_att = tx_ | for (jrd_tra* tx_att = attachment->att_transactions; tx_att; tx_a | |||
att->tra_next) | tt = tx_att->tra_next) | |||
{ | { | |||
att_oldest_active = MIN(att_oldest_active, tx_att->tra_number); | att_oldest_active = MIN(att_oldest_active, tx_att->tra_nu | |||
att_oldest_snapshot = MIN(att_oldest_snapshot, tx_att->tra_att_ol | mber); | |||
dest_active); | att_oldest_snapshot = MIN(att_oldest_snapshot, tx_att->tr | |||
} | a_att_oldest_active); | |||
} | ||||
trans->tra_att_oldest_active = (trans->tra_flags & TRA_read_committed) ? number : att_oldest_active; | trans->tra_att_oldest_active = (trans->tra_flags & TRA_read_commi tted) ? number : att_oldest_active; | |||
if (attachment->att_oldest_snapshot < att_oldest_snapshot) | if (attachment->att_oldest_snapshot < att_oldest_snapshot) | |||
attachment->att_oldest_snapshot = att_oldest_snapshot; | attachment->att_oldest_snapshot = att_oldest_snapshot; | |||
// Put the TID of the oldest active transaction (just calculated) | // Put the TID of the oldest active transaction (just calculated) | |||
// in the new transaction's lock. | // in the new transaction's lock. | |||
// hvlad: for read-committed transaction put tra_number to prevent | // hvlad: for read-committed transaction put tra_number to preven | |||
// unnecessary blocking of garbage collection by read-committed | t | |||
// transactions | // unnecessary blocking of garbage collection by read-committed | |||
// transactions | ||||
const TraNumber lck_data = (trans->tra_flags & TRA_read_committed) ? numb er : oldest_active; | const TraNumber lck_data = (trans->tra_flags & TRA_read_committed ) ? number : oldest_active; | |||
//fb_assert(sizeof(lock->lck_data) == sizeof(lck_data)); | //fb_assert(sizeof(lock->lck_data) == sizeof(lck_data)); | |||
if (lock->lck_data != (SLONG) lck_data) | if (lock->lck_data != (SLONG) lck_data) | |||
LCK_write_data(tdbb, lock, lck_data); | LCK_write_data(tdbb, lock, lck_data); | |||
// Finally, scan transactions looking for the oldest interesting transact | // Finally, scan transactions looking for the oldest interesting | |||
ion -- the oldest | transaction -- the oldest | |||
// non-commited transaction. This will not be updated immediately, but s | // non-commited transaction. This will not be updated immediatel | |||
aved until the | y, but saved until the | |||
// next update access to the header page | // next update access to the header page | |||
oldest_state = tra_committed; | oldest_state = tra_committed; | |||
for (oldest = trans->tra_oldest; oldest < top; oldest++) | for (oldest = trans->tra_oldest; oldest < top; oldest++) | |||
{ | ||||
if (trans->tra_flags & TRA_read_committed) | ||||
{ | { | |||
const ULONG mask = ~((1 << tra_committed) | (1 << tra_pre | if (trans->tra_flags & TRA_read_committed) | |||
committed)); | ||||
oldest = TPC_find_states(tdbb, trans->tra_oldest, top, ma | ||||
sk, oldest_state); | ||||
if (!oldest) | ||||
{ | { | |||
oldest = top; | const ULONG mask = ~((1 << tra_committed) | (1 << | |||
break; | tra_precommitted)); | |||
oldest = TPC_find_states(tdbb, trans->tra_oldest, | ||||
top, mask, oldest_state); | ||||
if (!oldest) | ||||
{ | ||||
oldest = top; | ||||
break; | ||||
} | ||||
fb_assert(oldest_state != tra_committed && oldest | ||||
_state != tra_precommitted); | ||||
} | } | |||
fb_assert(oldest_state != tra_committed && oldest_state ! | else | |||
= tra_precommitted); | { | |||
} | const ULONG byte = TRANS_OFFSET(oldest - base); | |||
else | const USHORT shift = TRANS_SHIFT(oldest); | |||
{ | oldest_state = (trans->tra_transactions[byte] >> | |||
const ULONG byte = TRANS_OFFSET(oldest - base); | shift) & TRA_MASK; | |||
const USHORT shift = TRANS_SHIFT(oldest); | } | |||
oldest_state = (trans->tra_transactions[byte] >> shift) & | ||||
TRA_MASK; | if (oldest_state != tra_committed && oldest_state != tra_ | |||
precommitted) | ||||
break; | ||||
} | } | |||
if (oldest_state != tra_committed && oldest_state != tra_precommi | if (oldest >= top && dbb->dbb_flags & DBB_read_only) | |||
tted) | oldest = number; | |||
break; | ||||
} | ||||
if (oldest >= top && dbb->dbb_flags & DBB_read_only) | if (--oldest > dbb->dbb_oldest_transaction) | |||
oldest = number; | dbb->dbb_oldest_transaction = oldest; | |||
if (--oldest > dbb->dbb_oldest_transaction) | if (oldest_active > dbb->dbb_oldest_active) | |||
dbb->dbb_oldest_transaction = oldest; | dbb->dbb_oldest_active = oldest_active; | |||
if (oldest_active > dbb->dbb_oldest_active) | if (trans->tra_oldest_active > dbb->dbb_oldest_snapshot) | |||
dbb->dbb_oldest_active = oldest_active; | { | |||
dbb->dbb_oldest_snapshot = trans->tra_oldest_active; | ||||
if (trans->tra_oldest_active > dbb->dbb_oldest_snapshot) | if (!(dbb->dbb_flags & DBB_gc_active) && (dbb->dbb_flags | |||
{ | & DBB_gc_background)) | |||
dbb->dbb_oldest_snapshot = trans->tra_oldest_active; | { | |||
dbb->dbb_flags |= DBB_gc_pending; | ||||
dbb->dbb_gc_sem.release(); | ||||
} | ||||
} | ||||
// If the transaction block is getting out of hand, force a sweep | ||||
if (!(dbb->dbb_flags & DBB_gc_active) && (dbb->dbb_flags & DBB_gc | if (dbb->dbb_sweep_interval && | |||
_background)) | (trans->tra_oldest_active > oldest) && | |||
(trans->tra_oldest_active - oldest > dbb->dbb_sweep_inter | ||||
val) && | ||||
oldest_state != tra_limbo) | ||||
{ | { | |||
dbb->dbb_flags |= DBB_gc_pending; | start_sweeper(tdbb); | |||
dbb->dbb_gc_sem.release(); | ||||
} | } | |||
} | ||||
// If the transaction block is getting out of hand, force a sweep | // Start a 'transaction-level' savepoint, unless this is the | |||
// system transaction, or unless the transactions doesn't want | ||||
// a savepoint to be started. This savepoint will be used to | ||||
// undo the transaction if it rolls back. | ||||
if (dbb->dbb_sweep_interval && | if (trans != attachment->getSysTransaction() && !(trans->tra_flag | |||
(trans->tra_oldest_active > oldest) && | s & TRA_no_auto_undo)) | |||
(trans->tra_oldest_active - oldest > dbb->dbb_sweep_interval) && | { | |||
oldest_state != tra_limbo) | VIO_start_save_point(tdbb, trans); | |||
{ | trans->tra_save_point->sav_flags |= SAV_trans_level; | |||
start_sweeper(tdbb); | } | |||
} | ||||
// Start a 'transaction-level' savepoint, unless this is the | ||||
// system transaction, or unless the transactions doesn't want | ||||
// a savepoint to be started. This savepoint will be used to | ||||
// undo the transaction if it rolls back. | ||||
if (trans != attachment->getSysTransaction() && !(trans->tra_flags & TRA_ | // if the user asked us to restart all requests in this attachmen | |||
no_auto_undo)) | t, | |||
{ | // do so now using the new transaction | |||
VIO_start_save_point(tdbb, trans); | ||||
trans->tra_save_point->sav_flags |= SAV_trans_level; | ||||
} | ||||
// if the user asked us to restart all requests in this attachment, | if (trans->tra_flags & TRA_restart_requests) | |||
// do so now using the new transaction | restart_requests(tdbb, trans); | |||
if (trans->tra_flags & TRA_restart_requests) | // If the transaction is read-only and read committed, it can be | |||
restart_requests(tdbb, trans); | // precommitted because it can't modify any records and doesn't | |||
// need a snapshot preserved. This transaction type can run | ||||
// forever without impacting garbage collection or causing | ||||
// transaction bitmap growth. | ||||
// If the transaction is read-only and read committed, it can be | if ((trans->tra_flags & TRA_readonly) && (trans->tra_flags & TRA_ | |||
// precommitted because it can't modify any records and doesn't | read_committed)) | |||
// need a snapshot preserved. This transaction type can run | { | |||
// forever without impacting garbage collection or causing | TRA_set_state(tdbb, trans, trans->tra_number, tra_committ | |||
// transaction bitmap growth. | ed); | |||
LCK_release(tdbb, lock); | ||||
if ((trans->tra_flags & TRA_readonly) && (trans->tra_flags & TRA_read_com | lock->lck_type = LCK_tra_pc; // note, LCK_tra_ | |||
mitted)) | pc belongs to the same owner as LCK_tra | |||
{ | lock->lck_data = 0; | |||
TRA_set_state(tdbb, trans, trans->tra_number, tra_committed); | if (!LCK_lock(tdbb, lock, LCK_write, LCK_WAIT)) | |||
LCK_release(tdbb, lock); | ERR_post(Arg::Gds(isc_lock_conflict)); | |||
lock->lck_type = LCK_tra_pc; // note, LCK_tra_pc belon | trans->tra_flags |= TRA_precommitted; | |||
gs to the same owner as LCK_tra | } | |||
lock->lck_data = 0; | ||||
if (!LCK_lock(tdbb, lock, LCK_write, LCK_WAIT)) | ||||
ERR_post(Arg::Gds(isc_lock_conflict)); | ||||
trans->tra_flags |= TRA_precommitted; | if (trans->tra_flags & TRA_precommitted) | |||
TRA_precommited(tdbb, 0, trans->tra_number); | ||||
} | ||||
catch (const Firebird::Exception&) | ||||
{ | ||||
trans->unlinkFromAttachment(); | ||||
throw; | ||||
} | } | |||
if (trans->tra_flags & TRA_precommitted) | ||||
TRA_precommited(tdbb, 0, trans->tra_number); | ||||
} | } | |||
jrd_tra::~jrd_tra() | jrd_tra::~jrd_tra() | |||
{ | { | |||
while (tra_undo_records.hasData()) | while (tra_undo_records.hasData()) | |||
delete tra_undo_records.pop(); | delete tra_undo_records.pop(); | |||
delete tra_undo_space; | delete tra_undo_space; | |||
delete tra_user_management; | delete tra_user_management; | |||
delete tra_mapping_list; | delete tra_mapping_list; | |||
skipping to change at line 3457 | skipping to change at line 3499 | |||
tra_interface->setHandle(NULL); | tra_interface->setHandle(NULL); | |||
tra_interface->release(); | tra_interface->release(); | |||
} | } | |||
if (tra_autonomous_pool) | if (tra_autonomous_pool) | |||
MemoryPool::deletePool(tra_autonomous_pool); | MemoryPool::deletePool(tra_autonomous_pool); | |||
delete tra_sec_db_context; | delete tra_sec_db_context; | |||
} | } | |||
JTransaction* jrd_tra::getInterface() | JTransaction* jrd_tra::getInterface(bool create) | |||
{ | { | |||
if (!tra_interface) | if (!tra_interface && create) | |||
{ | { | |||
tra_flags |= TRA_own_interface; | tra_flags |= TRA_own_interface; | |||
tra_interface = FB_NEW JTransaction(this, tra_attachment->getStab le()); | tra_interface = FB_NEW JTransaction(this, tra_attachment->getStab le()); | |||
tra_interface->addRef(); | tra_interface->addRef(); | |||
} | } | |||
return tra_interface; | return tra_interface; | |||
} | } | |||
void jrd_tra::setInterface(JTransaction* jt) | void jrd_tra::setInterface(JTransaction* jt) | |||
skipping to change at line 3537 | skipping to change at line 3579 | |||
void jrd_tra::releaseAutonomousPool(MemoryPool* toRelease) | void jrd_tra::releaseAutonomousPool(MemoryPool* toRelease) | |||
{ | { | |||
fb_assert(tra_autonomous_pool == toRelease); | fb_assert(tra_autonomous_pool == toRelease); | |||
if (++tra_autonomous_cnt > TRA_AUTONOMOUS_PER_POOL) | if (++tra_autonomous_cnt > TRA_AUTONOMOUS_PER_POOL) | |||
{ | { | |||
MemoryPool::deletePool(tra_autonomous_pool); | MemoryPool::deletePool(tra_autonomous_pool); | |||
tra_autonomous_pool = NULL; | tra_autonomous_pool = NULL; | |||
} | } | |||
} | } | |||
void jrd_tra::checkBlob(thread_db* tdbb, const bid* blob_id, bool punt) | ||||
{ | ||||
USHORT rel_id = blob_id->bid_internal.bid_relation_id; | ||||
if ((tra_attachment->isGbak()) || | ||||
tra_attachment->att_flags & ATT_garbage_collector || | ||||
tra_attachment->att_user->locksmith() || | ||||
rel_id == 0) | ||||
{ | ||||
return; | ||||
} | ||||
if (!tra_blobs->locate(blob_id->bid_temp_id()) && | ||||
!tra_fetched_blobs.locate(*blob_id)) | ||||
{ | ||||
vec<jrd_rel*>* vector = tra_attachment->att_relations; | ||||
jrd_rel* blb_relation; | ||||
if (rel_id < vector->count() && (blb_relation = (*vector)[rel_id] | ||||
)) | ||||
{ | ||||
if (blb_relation->rel_security_name.isEmpty()) | ||||
MET_scan_relation(tdbb, blb_relation); | ||||
SecurityClass* s_class = SCL_get_class(tdbb, blb_relation | ||||
->rel_security_name.c_str()); | ||||
if (!s_class) | ||||
return; | ||||
switch (s_class->scl_blb_access) | ||||
{ | ||||
case SecurityClass::BA_UNKNOWN: | ||||
// Relation has not been checked for access right | ||||
s | ||||
try | ||||
{ | ||||
ThreadStatusGuard status_vector(tdbb); | ||||
SCL_check_access(tdbb, s_class, 0, 0, NUL | ||||
L, SCL_select, SCL_object_table, false, | ||||
blb_relation->rel_name); | ||||
s_class->scl_blb_access = SecurityClass:: | ||||
BA_SUCCESS; | ||||
} | ||||
catch (const Exception& ex) | ||||
{ | ||||
StaticStatusVector status; | ||||
ex.stuffException(status); | ||||
if (status[1] != isc_no_priv) | ||||
throw; | ||||
// We don't have access to this relation | ||||
s_class->scl_blb_access = SecurityClass:: | ||||
BA_FAILURE; | ||||
if (punt) | ||||
throw; | ||||
// but someone else has (SP, view) | ||||
// store Blob ID as allowed in this trans | ||||
action | ||||
tra_fetched_blobs.add(*blob_id); | ||||
} | ||||
break; | ||||
case SecurityClass::BA_FAILURE: | ||||
// Relation has been checked earlier and check wa | ||||
s failed | ||||
if (punt) | ||||
ERR_post(Arg::Gds(isc_no_priv) << Arg::St | ||||
r("SELECT") << | ||||
Arg::Str("TABLE") << | ||||
Arg::Str(blb_relation->rel_name)) | ||||
; | ||||
else | ||||
tra_fetched_blobs.add(*blob_id); | ||||
break; | ||||
case SecurityClass::BA_SUCCESS: | ||||
// do nothing | ||||
break; | ||||
default: | ||||
fb_assert(false); | ||||
} | ||||
} | ||||
} | ||||
} | ||||
/// class TraceSweepEvent | /// class TraceSweepEvent | |||
TraceSweepEvent::TraceSweepEvent(thread_db* tdbb) | TraceSweepEvent::TraceSweepEvent(thread_db* tdbb) | |||
{ | { | |||
m_tdbb = tdbb; | m_tdbb = tdbb; | |||
WIN window(HEADER_PAGE_NUMBER); | WIN window(HEADER_PAGE_NUMBER); | |||
Ods::header_page* header = (Ods::header_page*) CCH_FETCH(m_tdbb, &window, LCK_read, pag_header); | Ods::header_page* header = (Ods::header_page*) CCH_FETCH(m_tdbb, &window, LCK_read, pag_header); | |||
m_sweep_info.update(header); | m_sweep_info.update(header); | |||
End of changes. 83 change blocks. | ||||
307 lines changed or deleted | 453 lines changed or added |