From cb461faeb71b2a9111965a8d15737ef4986d041f Mon Sep 17 00:00:00 2001 From: hvlad Date: Tue, 22 Mar 2011 10:32:27 +0000 Subject: [PATCH] Improvement CORE-3399 : Allow write operations to temporary tables in read only transactions --- src/jrd/met.epp | 105 ++++++++++++++++++++++++++++------------------- src/jrd/rlck.cpp | 16 +++++++- src/jrd/vio.cpp | 53 ++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 45 deletions(-) diff --git a/src/jrd/met.epp b/src/jrd/met.epp index 7f93fd9d01..1de082af14 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -1602,7 +1602,9 @@ void MET_load_trigger(thread_db* tdbb, // No need to load table triggers for ReadOnly databases, // since INSERT/DELETE/UPDATE statements are not going to be allowed - if (dbb->dbb_flags & DBB_read_only) + // hvlad: GTT with ON COMMIT DELETE ROWS clause is writable + + if ((dbb->dbb_flags & DBB_read_only) && !(relation->rel_flags & REL_temp_tran)) return; } @@ -2790,11 +2792,10 @@ void MET_parse_sys_trigger(thread_db* tdbb, jrd_rel* relation) // No need to load triggers for ReadOnly databases, since // INSERT/DELETE/UPDATE statements are not going to be allowed + // hvlad: GTT with ON COMMIT DELETE ROWS clause is writable - if (dbb->dbb_flags & DBB_read_only) - { + if ((dbb->dbb_flags & DBB_read_only) && !(relation->rel_flags & REL_temp_tran)) return; - } relation->rel_flags |= REL_sys_trigs_being_loaded; @@ -3632,6 +3633,48 @@ void MET_scan_relation(thread_db* tdbb, jrd_rel* relation) triggers[itr] = NULL; } + rel_t relType = rel_persistent; + + if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_11_1) + { + jrd_req* sub_request = CMP_find_request(tdbb, irq_r_type, IRQ_REQUESTS); + + FOR(REQUEST_HANDLE sub_request) + REL IN RDB$RELATIONS WITH REL.RDB$RELATION_ID EQ relation->rel_id + + if (!REQUEST(irq_r_type)) + REQUEST(irq_r_type) = sub_request; + + if (!REL.RDB$RELATION_TYPE.NULL) + { + relType = (rel_t) REL.RDB$RELATION_TYPE; + } + + END_FOR; + + if (!REQUEST(irq_r_type)) + REQUEST(irq_r_type) = sub_request; + } + + switch (relType) + { + case rel_persistent: + case rel_external: + case rel_view: + break; + case rel_virtual: + relation->rel_flags |= REL_virtual; + break; + case rel_global_temp_preserve: + relation->rel_flags |= REL_temp_conn; + break; + case rel_global_temp_delete: + relation->rel_flags |= REL_temp_tran; + break; + default: + fb_assert(false); + } + /* Since this can be called recursively, find an inactive clone of the request */ request = CMP_find_request(tdbb, irq_r_fields, IRQ_REQUESTS); @@ -3849,46 +3892,22 @@ void MET_scan_relation(thread_db* tdbb, jrd_rel* relation) delete csb; - if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_11_1) + switch (relType) { - jrd_req* sub_request = CMP_find_request(tdbb, irq_r_type, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE sub_request) - REL IN RDB$RELATIONS WITH REL.RDB$RELATION_ID EQ relation->rel_id - - if (!REQUEST(irq_r_type)) - REQUEST(irq_r_type) = sub_request; - - if (!REL.RDB$RELATION_TYPE.NULL) - { - switch (REL.RDB$RELATION_TYPE) - { - case rel_persistent: - break; - case rel_external: - fb_assert(relation->rel_file); - break; - case rel_view: - fb_assert(relation->rel_view_rse); - break; - case rel_virtual: - relation->rel_flags |= REL_virtual; - break; - case rel_global_temp_preserve: - relation->rel_flags |= REL_temp_conn; - break; - case rel_global_temp_delete: - relation->rel_flags |= REL_temp_tran; - break; - default: - fb_assert(false); - } - } - - END_FOR; - - if (!REQUEST(irq_r_type)) - REQUEST(irq_r_type) = sub_request; + case rel_persistent: + break; + case rel_external: + fb_assert(relation->rel_file); + break; + case rel_view: + fb_assert(relation->rel_view_rse); + break; + case rel_virtual: + case rel_global_temp_preserve: + case rel_global_temp_delete: + break; + default: + fb_assert(false); } /* release any triggers in case of a rescan, but not if the rescan diff --git a/src/jrd/rlck.cpp b/src/jrd/rlck.cpp index 510c32091f..178b77570e 100644 --- a/src/jrd/rlck.cpp +++ b/src/jrd/rlck.cpp @@ -55,10 +55,22 @@ Lock* RLCK_reserve_relation(thread_db* tdbb, if (transaction->tra_flags & TRA_system) return NULL; - if (write_flag && (tdbb->getDatabase()->dbb_flags & DBB_read_only)) + + // hvlad: virtual relations always writable, all kind of GTT's are writable + // at read-only transactions at read-write databases, GTT's with ON COMMIT + // DELETE ROWS clause is writable at read-only databases. + + if (write_flag && (tdbb->getDatabase()->dbb_flags & DBB_read_only) && + !relation->isVirtual() && !(relation->rel_flags & REL_temp_tran)) + { ERR_post(Arg::Gds(isc_read_only_database)); - if (write_flag && (transaction->tra_flags & TRA_readonly)) + } + + if (write_flag && (transaction->tra_flags & TRA_readonly) && + !relation->isVirtual() && !relation->isTemporary()) + { ERR_post(Arg::Gds(isc_read_only_trans)); + } Lock* lock = RLCK_transaction_relation_lock(tdbb, transaction, relation); diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index ca9940c6cd..c938693c11 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -94,6 +94,7 @@ using namespace Firebird; static void check_class(thread_db*, jrd_tra*, record_param*, record_param*, USHORT); static void check_control(thread_db*); static bool check_user(thread_db*, const dsc*); +static int check_precommitted(const jrd_tra*, const record_param*); static void check_rel_field_class(thread_db*, record_param*, SecurityClass::flags_t, jrd_tra*); static void delete_record(thread_db*, record_param*, SLONG, MemoryPool*); static UCHAR* delete_tail(thread_db*, record_param*, SLONG, UCHAR*, const UCHAR*); @@ -628,6 +629,9 @@ bool VIO_chase_record_version(thread_db* tdbb, record_param* rpb, if (state == tra_active) state = tra_limbo; } + if (state == tra_precommitted) + state = check_precommitted(transaction, rpb); + // If the transaction is a read committed and chooses the no version // option, wait for reads also! @@ -670,6 +674,10 @@ bool VIO_chase_record_version(thread_db* tdbb, record_param* rpb, CCH_RELEASE(tdbb, &rpb->getWindow(tdbb)); state = TRA_wait(tdbb, transaction, rpb->rpb_transaction_nr, jrd_tra::tra_wait); + + if (state == tra_precommitted) + state = check_precommitted(transaction, rpb); + if (state == tra_active) ERR_post(Arg::Gds(isc_deadlock)); @@ -1633,6 +1641,9 @@ bool VIO_garbage_collect(thread_db* tdbb, record_param* rpb, const jrd_tra* tran } } + if (state == tra_precommitted) + state = check_precommitted(transaction, rpb); + if (state == tra_dead) { CCH_RELEASE(tdbb, &rpb->getWindow(tdbb)); @@ -1898,6 +1909,9 @@ bool VIO_get_current(thread_db* tdbb, } } + if (state == tra_precommitted) + state = check_precommitted(transaction, rpb); + switch (state) { case tra_committed: @@ -1925,6 +1939,8 @@ bool VIO_get_current(thread_db* tdbb, if (!(rpb->rpb_flags & rpb_gc_active)) { state = TRA_wait(tdbb, transaction, rpb->rpb_transaction_nr, jrd_tra::tra_wait); + if (state == tra_precommitted) + state = check_precommitted(transaction, rpb); } else { @@ -3332,6 +3348,40 @@ bool VIO_writelock(thread_db* tdbb, record_param* org_rpb, RecordSource* rsb, jr } +static int check_precommitted(const jrd_tra* transaction, const record_param* rpb) +{ +/********************************************* + * + * c h e c k _ p r e c o m m i t t e d + * + ********************************************* + * + * Functional description + * Check if precommitted transaction which created given record version is + * current transaction or it is a still active and belongs to the current + * attachment. This is needed to detect visibility of records modified by + * temporary tables in read-only transactions. + * + **************************************/ + if (!(rpb->rpb_flags & rpb_gc_active) && rpb->rpb_relation->isTemporary()) + { + if (transaction->tra_number == rpb->rpb_transaction_nr) { + return tra_us; + } + else + { + const jrd_tra* tx = transaction->tra_attachment->att_transactions; + for (; tx; tx = tx->tra_next) + if (tx->tra_number == rpb->rpb_transaction_nr) + { + return tra_active; + } + } + } + return tra_precommitted; +} + + static void check_rel_field_class(thread_db* tdbb, record_param* rpb, SecurityClass::flags_t flags, @@ -4571,6 +4621,9 @@ static int prepare_update( thread_db* tdbb, } } + if (state == tra_precommitted) + state = check_precommitted(transaction, rpb); + switch (state) { case tra_committed: