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:
parent
1bd0536829
commit
cb461faeb7
105
src/jrd/met.epp
105
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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user