8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-24 07:23:03 +01:00

Improvement CORE-3399 : Allow write operations to temporary tables in read only transactions

This commit is contained in:
hvlad 2011-03-22 10:32:27 +00:00
parent 1bd0536829
commit cb461faeb7
3 changed files with 129 additions and 45 deletions

View File

@ -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

View File

@ -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);

View File

@ -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: