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

Merge pull request #8202 from FirebirdSQL/work/system-triggers

Reimplement system triggers in C++/GDML code
This commit is contained in:
Adriano dos Santos Fernandes 2024-08-05 07:44:44 -03:00 committed by GitHub
commit 16da398675
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 1609 additions and 2424 deletions

View File

@ -31,6 +31,7 @@
<ClCompile Include="..\..\..\gen\jrd\ini.cpp" />
<ClCompile Include="..\..\..\gen\jrd\met.cpp" />
<ClCompile Include="..\..\..\gen\jrd\scl.cpp" />
<ClCompile Include="..\..\..\gen\jrd\SystemTriggers.cpp" />
<ClCompile Include="..\..\..\gen\utilities\gstat\dba.cpp" />
<ClCompile Include="..\..\..\src\dsql\AggNodes.cpp" />
<ClCompile Include="..\..\..\src\dsql\BlrDebugWriter.cpp" />
@ -350,6 +351,7 @@
<ClInclude Include="..\..\..\src\jrd\sys-packages\SqlPackage.h" />
<ClInclude Include="..\..\..\src\jrd\SysFunction.h" />
<ClInclude Include="..\..\..\src\jrd\SystemPackages.h" />
<ClInclude Include="..\..\..\src\jrd\SystemTriggers.h" />
<ClInclude Include="..\..\..\src\jrd\TempSpace.h" />
<ClInclude Include="..\..\..\src\jrd\TimeZone.h" />
<ClInclude Include="..\..\..\src\jrd\tpc_proto.h" />
@ -390,6 +392,7 @@
<None Include="..\..\..\src\jrd\ini.epp" />
<None Include="..\..\..\src\jrd\met.epp" />
<None Include="..\..\..\src\jrd\scl.epp" />
<None Include="..\..\..\src\jrd\SystemTriggers.epp" />
<None Include="..\..\..\src\utilities\gstat\dba.epp" />
</ItemGroup>
<PropertyGroup Label="Globals">

View File

@ -423,6 +423,9 @@
<ClCompile Include="..\..\..\gen\jrd\scl.cpp">
<Filter>JRD files\GPRE cpp</Filter>
</ClCompile>
<ClCompile Include="..\..\..\gen\jrd\SystemTriggers.cpp">
<Filter>JRD files\GPRE cpp</Filter>
</ClCompile>
<ClCompile Include="..\..\..\gen\jrd\dfw.cpp">
<Filter>JRD files\GPRE cpp</Filter>
</ClCompile>
@ -1085,6 +1088,9 @@
<ClInclude Include="..\..\..\src\jrd\SystemPackages.h">
<Filter>Header files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\jrd\SystemTriggers.h">
<Filter>Header files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\jrd\Coercion.h">
<Filter>Header files</Filter>
</ClInclude>
@ -1139,6 +1145,9 @@
<None Include="..\..\..\src\jrd\scl.epp">
<Filter>JRD files\GPRE files</Filter>
</None>
<None Include="..\..\..\src\jrd\SystemTriggers.epp">
<Filter>JRD files\GPRE files</Filter>
</None>
<None Include="..\..\..\src\utilities\gstat\dba.epp">
<Filter>Services</Filter>
</None>
@ -1146,4 +1155,4 @@
<Filter>DSQL</Filter>
</None>
</ItemGroup>
</Project>
</Project>

View File

@ -65,7 +65,7 @@ goto :EOF
@for %%i in (alice_meta) do @call :PREPROCESS alice %%i
@for %%i in (metd, DdlNodes, PackageNodes) do @call :PREPROCESS dsql %%i -gds_cxx
@for %%i in (gpre_meta) do @call :PREPROCESS gpre/std %%i
@for %%i in (dfw, dpm, dyn_util, fun, grant, ini, met, scl, Function) do @call :PREPROCESS jrd %%i -gds_cxx
@for %%i in (dfw, dpm, dyn_util, fun, grant, ini, met, scl, Function, SystemTriggers) do @call :PREPROCESS jrd %%i -gds_cxx
@for %%i in (stats) do @call :PREPROCESS utilities %%i
@goto :EOF
@ -78,7 +78,7 @@ goto :EOF
@for %%i in (metd) do @call :PREPROCESS dsql %%i -gds_cxx
@for %%i in (DdlNodes, PackageNodes) do @call :PREPROCESS dsql %%i -gds_cxx
@for %%i in (gpre_meta) do @call :PREPROCESS gpre/std %%i
@for %%i in (dfw, dpm, dyn_util, fun, grant, ini, met, scl, Function) do @call :PREPROCESS jrd %%i -gds_cxx
@for %%i in (dfw, dpm, dyn_util, fun, grant, ini, met, scl, Function, SystemTriggers) do @call :PREPROCESS jrd %%i -gds_cxx
@for %%i in (extract, isql, show) do @call :PREPROCESS isql %%i -ocxx
@for %%i in (dba) do @call :PREPROCESS utilities/gstat %%i
@for %%i in (stats) do @call :PREPROCESS utilities %%i

View File

@ -2781,7 +2781,7 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger
spPreTriggers.release();
// Handle post operation trigger.
if (relation->rel_post_erase && whichTrig != PRE_TRIG)
if ((relation->rel_post_erase || relation->isSystem()) && whichTrig != PRE_TRIG)
{
EXE_execute_triggers(tdbb, &relation->rel_post_erase, rpb, NULL, TRIGGER_DELETE, POST_TRIG);
}
@ -7848,7 +7848,7 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, Request* request, WhichTrigg
newRpb->rpb_number = orgRpb->rpb_number;
newRpb->rpb_number.setValid(true);
if (relation->rel_post_modify && whichTrig != PRE_TRIG)
if ((relation->rel_post_modify || relation->isSystem()) && whichTrig != PRE_TRIG)
{
EXE_execute_triggers(tdbb, &relation->rel_post_modify, orgRpb, newRpb,
TRIGGER_UPDATE, POST_TRIG);
@ -8871,7 +8871,7 @@ const StmtNode* StoreNode::store(thread_db* tdbb, Request* request, WhichTrigger
{
SavepointChangeMarker scMarker(transaction);
if (relation && relation->rel_pre_store && whichTrig != POST_TRIG)
if (relation && (relation->rel_pre_store || relation->isSystem()) && whichTrig != POST_TRIG)
{
EXE_execute_triggers(tdbb, &relation->rel_pre_store, NULL, rpb,
TRIGGER_INSERT, PRE_TRIG);
@ -8904,7 +8904,8 @@ const StmtNode* StoreNode::store(thread_db* tdbb, Request* request, WhichTrigger
rpb->rpb_number.setValid(true);
if (relation && relation->rel_post_store && whichTrig != PRE_TRIG)
if (relation && (relation->rel_post_store || relation->isSystem()) &&
relation->rel_post_store && whichTrig != PRE_TRIG)
{
EXE_execute_triggers(tdbb, &relation->rel_post_store, NULL, rpb,
TRIGGER_INSERT, POST_TRIG);
@ -11660,9 +11661,10 @@ static void preModifyEraseTriggers(thread_db* tdbb, TrigVector** trigs,
FB_NEW_POOL(*tdbb->getTransaction()->tra_pool) traRpbList(*tdbb->getTransaction()->tra_pool);
}
const auto relation = rpb->rpb_relation;
const int rpblevel = tdbb->getTransaction()->tra_rpblist->PushRpb(rpb);
if (*trigs && whichTrig != StmtNode::POST_TRIG)
if ((*trigs || relation->isSystem()) && whichTrig != StmtNode::POST_TRIG)
{
try
{

View File

@ -265,6 +265,7 @@ Jrd::Attachment::Attachment(MemoryPool* pool, Database* dbb, JProvider* provider
att_generators(*pool),
att_internal(*pool),
att_dyn_req(*pool),
att_internal_cached_statements(*pool),
att_dec_status(DecimalStatus::DEFAULT),
att_charsets(*pool),
att_charset_ids(*pool),
@ -681,9 +682,15 @@ Request* Jrd::Attachment::findSystemRequest(thread_db* tdbb, USHORT id, USHORT w
//Database::CheckoutLockGuard guard(this, dbb_cmp_clone_mutex);
fb_assert(which == IRQ_REQUESTS || which == DYN_REQUESTS);
fb_assert(which == IRQ_REQUESTS || which == DYN_REQUESTS || which == CACHED_REQUESTS);
Statement* statement = (which == IRQ_REQUESTS ? att_internal[id] : att_dyn_req[id]);
if (which == CACHED_REQUESTS && id >= att_internal_cached_statements.getCount())
att_internal_cached_statements.grow(id + 1);
Statement* statement =
which == IRQ_REQUESTS ? att_internal[id] :
which == DYN_REQUESTS ? att_dyn_req[id] :
att_internal_cached_statements[id];
if (!statement)
return NULL;

View File

@ -666,6 +666,7 @@ public:
Firebird::Array<Statement*> att_internal; // internal statements
Firebird::Array<Statement*> att_dyn_req; // internal dyn statements
Firebird::Array<Statement*> att_internal_cached_statements; // internal cached statements
Firebird::ICryptKeyCallback* att_crypt_callback; // callback for DB crypt
Firebird::DecimalStatus att_dec_status; // error handling and rounding

View File

@ -626,8 +626,6 @@ RelationSourceNode* RelationSourceNode::parse(thread_db* tdbb, CompilerScratch*
{
MET_scan_relation(tdbb, node->relation);
}
else if (node->relation->rel_flags & REL_sys_triggers)
MET_parse_sys_trigger(tdbb, node->relation);
// generate a stream for the relation reference, assuming it is a real reference

View File

@ -393,11 +393,9 @@ const ULONG REL_deleted = 0x0004; // Relation known gonzo
const ULONG REL_get_dependencies = 0x0008; // New relation needs dependencies during scan
const ULONG REL_check_existence = 0x0010; // Existence lock released pending drop of relation
const ULONG REL_blocking = 0x0020; // Blocking someone from dropping relation
const ULONG REL_sys_triggers = 0x0040; // The relation has system triggers to compile
const ULONG REL_sql_relation = 0x0080; // Relation defined as sql table
const ULONG REL_check_partners = 0x0100; // Rescan primary dependencies and foreign references
const ULONG REL_being_scanned = 0x0200; // relation scan in progress
const ULONG REL_sys_trigs_being_loaded = 0x0400; // System triggers being loaded
const ULONG REL_deleting = 0x0800; // relation delete in progress
const ULONG REL_temp_tran = 0x1000; // relation is a GTT delete rows
const ULONG REL_temp_conn = 0x2000; // relation is a GTT preserve rows

1383
src/jrd/SystemTriggers.epp Normal file

File diff suppressed because it is too large Load Diff

43
src/jrd/SystemTriggers.h Normal file
View File

@ -0,0 +1,43 @@
/*
* The contents of this file are subject to the Initial
* Developer's Public License Version 1.0 (the "License");
* you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
*
* Software distributed under the License is distributed AS IS,
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
* See the License for the specific language governing rights
* and limitations under the License.
*
* The Original Code was created by Adriano dos Santos Fernandes
* for the Firebird Open Source RDBMS project.
*
* Copyright (c) 2024 Adriano dos Santos Fernandes <adrianosf@gmail.com>
* and all contributors signed below.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*/
#ifndef JRD_SYSTEM_TRIGGERS_H
#define JRD_SYSTEM_TRIGGERS_H
#include "firebird.h"
namespace Jrd
{
class jrd_rel;
class thread_db;
class Record;
} // namespace Jrd
namespace Jrd::SystemTriggers
{
void executeBeforeDeleteTriggers(thread_db* tdbb, jrd_rel* relation, Record* record);
void executeAfterDeleteTriggers(thread_db* tdbb, jrd_rel* relation, Record* record);
void executeBeforeInsertTriggers(thread_db* tdbb, jrd_rel* relation, Record* record);
void executeBeforeUpdateTriggers(thread_db* tdbb, jrd_rel* relation, Record* orgRecord, Record* newRecord);
} // namespace Jrd::SystemTriggers
#endif // JRD_SYSTEM_TRIGGERS_H

View File

@ -6304,9 +6304,7 @@ static bool make_version(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_
// We have just loaded the triggers onto the local vector triggers.
// It's now time to place them at their rightful place inside the relation block.
if (!(relation->rel_flags & REL_sys_trigs_being_loaded))
relation->replaceTriggers(tdbb, triggers);
relation->replaceTriggers(tdbb, triggers);
// in case somebody changed the view definition or a computed
// field, reset the dependencies by deleting the current ones

View File

@ -71,6 +71,7 @@
#include "../jrd/intl.h"
#include "../jrd/sbm.h"
#include "../jrd/blb.h"
#include "../jrd/SystemTriggers.h"
#include "firebird/impl/blr.h"
#include "../dsql/ExprNodes.h"
#include "../dsql/StmtNodes.h"
@ -1294,18 +1295,57 @@ void EXE_execute_triggers(thread_db* tdbb,
* if any blow up.
*
**************************************/
SET_TDBB(tdbb);
const auto dbb = tdbb->getDatabase();
const auto old_rec = old_rpb ? old_rpb->rpb_record : nullptr;
const auto new_rec = new_rpb ? new_rpb->rpb_record : nullptr;
if (!(dbb->dbb_flags & DBB_creating) && (old_rpb || new_rpb))
{
if (const auto relation = old_rpb ? old_rpb->rpb_relation : new_rpb->rpb_relation;
relation->rel_flags & REL_system)
{
switch (which_trig)
{
case StmtNode::PRE_TRIG:
{
switch (trigger_action)
{
case TriggerAction::TRIGGER_DELETE:
SystemTriggers::executeBeforeDeleteTriggers(tdbb, relation, old_rec);
break;
case TriggerAction::TRIGGER_UPDATE:
SystemTriggers::executeBeforeUpdateTriggers(tdbb, relation, old_rec, new_rec);
break;
case TriggerAction::TRIGGER_INSERT:
SystemTriggers::executeBeforeInsertTriggers(tdbb, relation, new_rec);
break;
}
break;
}
case StmtNode::POST_TRIG:
switch (trigger_action)
{
case TriggerAction::TRIGGER_DELETE:
SystemTriggers::executeAfterDeleteTriggers(tdbb, relation, old_rec);
break;
}
break;
}
}
}
if (!*triggers || (*triggers)->isEmpty())
return;
SET_TDBB(tdbb);
Request* const request = tdbb->getRequest();
jrd_tra* const transaction = request ? request->req_transaction : tdbb->getTransaction();
RefPtr<TrigVector> vector(*triggers);
Record* const old_rec = old_rpb ? old_rpb->rpb_record : NULL;
Record* const new_rec = new_rpb ? new_rpb->rpb_record : NULL;
AutoPtr<Record> null_rec;
const bool is_db_trigger = (!old_rec && !new_rec);
@ -1868,15 +1908,6 @@ static void trigger_failure(thread_db* tdbb, Request* trigger)
MET_trigger_msg(tdbb, msg, trigger->getStatement()->triggerName, trigger->req_label);
if (msg.hasData())
{
if (trigger->getStatement()->flags & Statement::FLAG_SYS_TRIGGER)
{
ISC_STATUS code = PAR_symbol_to_gdscode(msg);
if (code)
{
ERR_post(Arg::Gds(isc_integ_fail) << Arg::Num(trigger->req_label) <<
Arg::Gds(code));
}
}
ERR_post(Arg::Gds(isc_integ_fail) << Arg::Num(trigger->req_label) <<
Arg::Gds(isc_random) << Arg::Str(msg));
}
@ -1897,8 +1928,15 @@ void AutoCacheRequest::cacheRequest()
thread_db* tdbb = JRD_get_thread_data();
Attachment* att = tdbb->getAttachment();
Statement** stmt = which == IRQ_REQUESTS ? &att->att_internal[id] :
which == DYN_REQUESTS ? &att->att_dyn_req[id] : nullptr;
if (which == CACHED_REQUESTS && id >= att->att_internal_cached_statements.getCount())
att->att_internal_cached_statements.grow(id + 1);
Statement** stmt =
which == IRQ_REQUESTS ? &att->att_internal[id] :
which == DYN_REQUESTS ? &att->att_dyn_req[id] :
which == CACHED_REQUESTS ? &att->att_internal_cached_statements[id] :
nullptr;
if (!stmt)
{
fb_assert(false);

View File

@ -25,6 +25,7 @@
#define JRD_EXE_PROTO_H
#include "../jrd/cmp_proto.h"
#include <atomic>
namespace Jrd {
class Request;
@ -58,6 +59,29 @@ void EXE_unwind(Jrd::thread_db*, Jrd::Request*);
namespace Jrd
{
class CachedRequestId
{
public:
CachedRequestId()
: id(generator++)
{
fb_assert(id <= MAX_USHORT);
}
CachedRequestId(const CachedRequestId&) = delete;
CachedRequestId& operator=(const CachedRequestId&) = delete;
public:
USHORT getId() const
{
return id;
}
private:
unsigned id;
static inline std::atomic_uint generator;
};
// ASF: To make this class MT-safe in SS for v3, it should be AutoCacheRequest::release job to
// inform CMP that the request is available for subsequent usage.
class AutoCacheRequest
@ -70,6 +94,13 @@ namespace Jrd
{
}
AutoCacheRequest(thread_db* tdbb, const CachedRequestId& cachedRequestId)
: id(cachedRequestId.getId()),
which(CACHED_REQUESTS),
request(tdbb->getAttachment()->findSystemRequest(tdbb, id, which))
{
}
AutoCacheRequest()
: id(0),
which(0),
@ -92,6 +123,15 @@ namespace Jrd
request = tdbb->getAttachment()->findSystemRequest(tdbb, id, which);
}
void reset(thread_db* tdbb, const CachedRequestId& cachedRequestId)
{
release();
id = cachedRequestId.getId();
which = CACHED_REQUESTS;
request = tdbb->getAttachment()->findSystemRequest(tdbb, id, which);
}
void compile(thread_db* tdbb, const UCHAR* blr, ULONG blrLength)
{
if (request)

View File

@ -79,134 +79,6 @@ namespace
jrd_vtof(name.c_str(), field, sizeof(field));
}
// This is the table used in defining triggers; note that
// RDB$TRIGGER_0 was first changed to RDB$TRIGGER_7 to make it easier to
// upgrade a database to support field-level grant. It has since been
// changed to RDB$TRIGGER_9 to handle SQL security on relations whose
// name is > 27 characters
const jrd_trg triggers[] =
{
{ "RDB$TRIGGER_1", (UCHAR) nam_user_privileges,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_MODIFY, sizeof(trigger3), trigger3,
0, ODS_8_0 },
{ "RDB$TRIGGER_8", (UCHAR) nam_user_privileges,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_ERASE, sizeof(trigger2), trigger2,
0, ODS_8_0 },
{ "RDB$TRIGGER_9", (UCHAR) nam_user_privileges,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_STORE, sizeof(trigger1), trigger1,
0, ODS_8_0 },
{ "RDB$TRIGGER_2", (UCHAR) nam_trgs,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_MODIFY, sizeof(trigger4), trigger4,
0, ODS_8_0 },
{ "RDB$TRIGGER_3", (UCHAR) nam_trgs,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_ERASE, sizeof(trigger4), trigger4,
0, ODS_8_0 },
{ "RDB$TRIGGER_26", (UCHAR) nam_rel_constr,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_STORE, sizeof(trigger26), trigger26,
0, ODS_8_0 },
{ "RDB$TRIGGER_25", (UCHAR) nam_rel_constr,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_MODIFY, sizeof(trigger25),
trigger25, 0, ODS_8_0 },
{ "RDB$TRIGGER_10", (UCHAR) nam_rel_constr,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_ERASE, sizeof(trigger10), trigger10,
0, ODS_8_0 },
{ "RDB$TRIGGER_11", (UCHAR) nam_rel_constr,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.POST_ERASE, sizeof(trigger11),
trigger11, 0, ODS_8_0 },
{ "RDB$TRIGGER_12", (UCHAR) nam_ref_constr,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_STORE, sizeof(trigger12), trigger12,
0, ODS_8_0 },
{ "RDB$TRIGGER_13", (UCHAR) nam_ref_constr,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_MODIFY, sizeof(trigger13),
trigger13, 0, ODS_8_0 },
{ "RDB$TRIGGER_14", (UCHAR) nam_chk_constr,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_MODIFY, sizeof(trigger14),
trigger14, 0, ODS_8_0 },
{ "RDB$TRIGGER_15", (UCHAR) nam_chk_constr,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_ERASE, sizeof(trigger15), trigger15,
0, ODS_8_0 },
{ "RDB$TRIGGER_16", (UCHAR) nam_chk_constr,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.POST_ERASE, sizeof(trigger16),
trigger16, 0, ODS_8_0 },
{ "RDB$TRIGGER_17", (UCHAR) nam_i_segments,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_ERASE, sizeof(trigger17), trigger17,
0, ODS_8_0 },
{ "RDB$TRIGGER_18", (UCHAR) nam_i_segments,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_MODIFY, sizeof(trigger18),
trigger18, 0, ODS_8_0 },
{ "RDB$TRIGGER_19", (UCHAR) nam_indices,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_ERASE, sizeof(trigger19), trigger19,
0, ODS_8_0 },
{ "RDB$TRIGGER_20", (UCHAR) nam_indices,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_MODIFY, sizeof(trigger20),
trigger20, 0, ODS_8_0 },
{ "RDB$TRIGGER_21", (UCHAR) nam_trgs,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_ERASE, sizeof(trigger21), trigger21,
0, ODS_8_0 },
{ "RDB$TRIGGER_22", (UCHAR) nam_trgs,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_MODIFY, sizeof(trigger22),
trigger22, 0, ODS_8_0 },
{ "RDB$TRIGGER_23", (UCHAR) nam_r_fields,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_ERASE, sizeof(trigger23), trigger23,
0, ODS_8_0 },
{ "RDB$TRIGGER_24", (UCHAR) nam_r_fields,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_MODIFY, sizeof(trigger24),
trigger24, 0, ODS_8_0 },
{ "RDB$TRIGGER_27", (UCHAR) nam_r_fields,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.POST_ERASE, sizeof(trigger27),
trigger27, 0, ODS_8_0 },
{ "RDB$TRIGGER_34", (UCHAR) nam_rel_constr,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.POST_ERASE, sizeof(trigger34),
trigger34, TRG_ignore_perm, ODS_8_1 },
{ "RDB$TRIGGER_35", (UCHAR) nam_chk_constr,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.POST_ERASE, sizeof(trigger35),
trigger35, TRG_ignore_perm, ODS_8_1 },
{ "RDB$TRIGGER_36", (UCHAR) nam_fields,
RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_MODIFY, sizeof(trigger36),
trigger36, 0, ODS_11_0 },
{ 0, 0, 0, 0, 0, 0 }
};
// this table is used in defining messages for system triggers
const trigger_msg trigger_messages[] =
{
{ "RDB$TRIGGER_9", 0, "grant_obj_notfound", ODS_8_0 },
{ "RDB$TRIGGER_9", 1, "grant_fld_notfound", ODS_8_0 },
{ "RDB$TRIGGER_9", 2, "grant_nopriv", ODS_8_0 },
{ "RDB$TRIGGER_9", 3, "nonsql_security_rel", ODS_8_0 },
{ "RDB$TRIGGER_9", 4, "nonsql_security_fld", ODS_8_0 },
{ "RDB$TRIGGER_9", 5, "grant_nopriv_on_base", ODS_8_0 },
{ "RDB$TRIGGER_1", 0, "existing_priv_mod", ODS_8_0 },
{ "RDB$TRIGGER_2", 0, "systrig_update", ODS_8_0 },
{ "RDB$TRIGGER_3", 0, "systrig_update", ODS_8_0 },
{ "RDB$TRIGGER_24", 1, "cnstrnt_fld_rename", ODS_8_0 },
{ "RDB$TRIGGER_23", 1, "cnstrnt_fld_del", ODS_8_0 },
{ "RDB$TRIGGER_22", 1, "check_trig_update", ODS_8_0 },
{ "RDB$TRIGGER_21", 1, "check_trig_del", ODS_8_0 },
{ "RDB$TRIGGER_20", 1, "integ_index_mod", ODS_8_0 },
{ "RDB$TRIGGER_20", 2, "integ_index_deactivate", ODS_8_0 },
{ "RDB$TRIGGER_20", 3, "integ_deactivate_primary", ODS_8_0 },
{ "RDB$TRIGGER_19", 1, "integ_index_del", ODS_8_0 },
{ "RDB$TRIGGER_18", 1, "integ_index_seg_mod", ODS_8_0 },
{ "RDB$TRIGGER_17", 1, "integ_index_seg_del", ODS_8_0 },
{ "RDB$TRIGGER_15", 1, "check_cnstrnt_del", ODS_8_0 },
{ "RDB$TRIGGER_14", 1, "check_cnstrnt_update", ODS_8_0 },
{ "RDB$TRIGGER_13", 1, "ref_cnstrnt_update", ODS_8_0 },
{ "RDB$TRIGGER_12", 1, "ref_cnstrnt_notfound", ODS_8_0 },
{ "RDB$TRIGGER_12", 2, "foreign_key_notfound", ODS_8_0 },
{ "RDB$TRIGGER_10", 1, "primary_key_ref", ODS_8_0 },
{ "RDB$TRIGGER_10", 2, "primary_key_notnull", ODS_8_0 },
{ "RDB$TRIGGER_25", 1, "rel_cnstrnt_update", ODS_8_0 },
{ "RDB$TRIGGER_26", 1, "constaint_on_view", ODS_8_0 },
{ "RDB$TRIGGER_26", 2, "invld_cnstrnt_type", ODS_8_0 },
{ "RDB$TRIGGER_26", 3, "primary_key_exists", ODS_8_0 },
{ "RDB$TRIGGER_24", 2, "integ_index_seg_mod", ODS_11_0 },
{ "RDB$TRIGGER_36", 1, "integ_index_seg_mod", ODS_11_0 },
{ 0, 0, 0, 0 }
};
unsigned getLatestFormat(thread_db* tdbb, int relId, int maxFieldId)
{
const auto relation = MET_relation(tdbb, relId);
@ -730,11 +602,9 @@ static void store_generator(thread_db*, const gen*, AutoRequest&, NonRelationSec
static void store_global_field(thread_db*, const gfld*, AutoRequest&, NonRelationSecurity&);
static void store_indices(thread_db*, USHORT = 0);
static void store_intlnames(thread_db*, NonRelationSecurity&);
static void store_message(thread_db*, const trigger_msg*, AutoRequest&);
static void store_relation(thread_db*, int, const char*, int, int, AutoRequest&, RelationSecurity&);
static void store_relation_field(thread_db*, int, const char*, const char*, const char*, int, AutoRequest&);
static void store_packages(thread_db*, NonRelationSecurity&, USHORT = 0);
static void store_trigger(thread_db*, const jrd_trg*, AutoRequest&);
//
@ -779,6 +649,8 @@ void INI_format(thread_db* tdbb, const string& charset)
for (const int* relfld = relfields; relfld[RFLD_R_NAME]; relfld = fld + 1)
{
const bool isVirtual = (relfld[RFLD_R_TYPE] == rel_virtual);
bool needsRdbRuntime = false;
int fieldId = 0;
for (fld = relfld + RFLD_RPT; fld[RFLD_F_NAME]; fld += RFLD_F_LENGTH)
@ -790,6 +662,9 @@ void INI_format(thread_db* tdbb, const string& charset)
const auto globalName = names[gfield->gfld_name];
const auto updateFlag = fld[RFLD_F_UPDATE];
if (!isVirtual && (gfield->gfld_dflt_blr || !gfield->gfld_nullable))
needsRdbRuntime = true;
store_relation_field(tdbb, fieldId, relName, fieldName, globalName,
updateFlag, handle2);
++fieldId;
@ -800,6 +675,13 @@ void INI_format(thread_db* tdbb, const string& charset)
const auto relType = relfld[RFLD_R_TYPE];
store_relation(tdbb, relId, relName, fieldId, relType, handle, relSec);
if (needsRdbRuntime)
{
dsc desc;
desc.makeText(static_cast<USHORT>(strlen(relName)), CS_METADATA, (UCHAR*) relName);
DFW_post_work(transaction, dfw_update_format, &desc, 0);
}
}
}
@ -896,20 +778,6 @@ void INI_format(thread_db* tdbb, const string& charset)
// Adjust the value of the hidden generator RDB$GENERATORS
DPM_gen_id(tdbb, 0, true, FB_NELEM(generators) - 1);
// Store system-defined triggers
handle.reset();
for (const jrd_trg* trigger = triggers; trigger->trg_relation; ++trigger)
store_trigger(tdbb, trigger, handle);
// Store trigger messages to go with triggers
handle.reset();
for (const trigger_msg* message = trigger_messages; message->trigmsg_name; ++message)
store_message(tdbb, message, handle);
// Create system packages
// Reset nonRelSec for package permissions, it should be its last usage in this function
@ -952,22 +820,6 @@ void INI_format(thread_db* tdbb, const string& charset)
}
//
// Return the trigger flags for a system trigger
//
USHORT INI_get_trig_flags(const MetaName& triggerName)
{
for (const jrd_trg* trig = triggers; trig->trg_length > 0; trig++)
{
if (triggerName == trig->trg_name)
return trig->trg_flags;
}
return 0;
}
//
// Initialize in-memory meta data
//
@ -989,17 +841,6 @@ void INI_init(thread_db* tdbb)
relation->rel_flags |= MET_get_rel_flags_from_TYPE(relfld[RFLD_R_TYPE]);
relation->rel_name = names[relfld[RFLD_R_NAME]];
// Set a flag if their is a trigger on the relation. Later we may need to compile it.
for (const jrd_trg* trigger = triggers; trigger->trg_relation; trigger++)
{
if (relation->rel_name == names[trigger->trg_relation])
{
relation->rel_flags |= REL_sys_triggers;
break;
}
}
HalfStaticArray<const char*, 64> fieldNames;
for (fld = relfld + RFLD_RPT; fld[RFLD_F_NAME]; fld += RFLD_F_LENGTH)
{
@ -1102,6 +943,30 @@ void INI_init(thread_db* tdbb)
}
// Rescan system relations that have fields with default values.
void INI_init_sys_relations(thread_db* tdbb)
{
const int* fld;
for (const int* relfld = relfields; relfld[RFLD_R_NAME]; relfld = fld + 1)
{
jrd_rel* relation = MET_relation(tdbb, relfld[RFLD_R_ID]);
bool needsRdbRuntime = false;
for (fld = relfld + RFLD_RPT; fld[RFLD_F_NAME]; fld += RFLD_F_LENGTH)
{
const auto* const gfield = &gfields[fld[RFLD_F_ID]];
if (!relation->isVirtual() && (gfield->gfld_dflt_blr || !gfield->gfld_nullable))
needsRdbRuntime = true;
}
if (needsRdbRuntime)
MET_scan_relation(tdbb, relation);
}
}
//
// Load system objects into DSQL metadata cache
//
@ -1373,26 +1238,6 @@ void INI_upgrade(thread_db* tdbb)
context = "indices";
store_indices(tdbb, odsVersion);
// Create new system triggers and their trigger messages
context = "triggers";
handle.reset();
for (const jrd_trg* trigger = triggers; trigger->trg_relation; ++trigger)
{
if (trigger->trg_ods_version > odsVersion)
store_trigger(tdbb, trigger, handle);
}
context = "trigger messages";
handle.reset();
for (const trigger_msg* message = trigger_messages; message->trigmsg_name; ++message)
{
if (message->trg_ods_version > odsVersion)
store_message(tdbb, message, handle);
}
// Create new system generators
context = "generators";
@ -1944,22 +1789,6 @@ static void store_intlnames(thread_db* tdbb, NonRelationSecurity& security)
}
static void store_message(thread_db* tdbb, const trigger_msg* message, AutoRequest& handle)
{
const auto attachment = tdbb->getAttachment();
const auto transaction = tdbb->getTransaction();
STORE(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction)
X IN RDB$TRIGGER_MESSAGES
{
PAD(message->trigmsg_name, X.RDB$TRIGGER_NAME);
X.RDB$MESSAGE_NUMBER = message->trigmsg_number;
PAD(message->trigmsg_text, X.RDB$MESSAGE);
}
END_STORE
}
static void store_relation(thread_db* tdbb,
int relId,
const char* relName,
@ -2226,36 +2055,3 @@ static void store_packages(thread_db* tdbb, NonRelationSecurity& security, USHOR
}
}
}
static void store_trigger(thread_db* tdbb, const jrd_trg* trigger, AutoRequest& handle)
{
const auto attachment = tdbb->getAttachment();
const auto transaction = tdbb->getTransaction();
// Indicate that the relation format needs revising
const auto triggerName = names[trigger->trg_relation];
dsc desc;
desc.makeText(static_cast<USHORT>(strlen(triggerName)), CS_METADATA,
(UCHAR*) triggerName);
DFW_post_work(transaction, dfw_update_format, &desc, 0);
// Store the trigger
STORE(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction)
X IN RDB$TRIGGERS
{
PAD(trigger->trg_name, X.RDB$TRIGGER_NAME);
PAD(names[trigger->trg_relation], X.RDB$RELATION_NAME);
X.RDB$TRIGGER_SEQUENCE = 0;
X.RDB$SYSTEM_FLAG = RDB_system;
X.RDB$SYSTEM_FLAG.NULL = FALSE;
X.RDB$TRIGGER_TYPE = trigger->trg_type;
X.RDB$FLAGS = trigger->trg_flags;
attachment->storeBinaryBlob(tdbb, transaction, &X.RDB$TRIGGER_BLR,
ByteChunk(trigger->trg_blr, trigger->trg_length));
}
END_STORE
}

View File

@ -30,8 +30,8 @@ namespace Jrd {
}
void INI_format(Jrd::thread_db*, const Firebird::string&);
USHORT INI_get_trig_flags(const Jrd::MetaName&);
void INI_init(Jrd::thread_db*);
void INI_init_sys_relations(Jrd::thread_db*);
void INI_init_dsql(Jrd::thread_db*, Jrd::dsql_dbb* database);
Firebird::string INI_owner_privileges();
void INI_upgrade(Jrd::thread_db*);

View File

@ -54,7 +54,6 @@ enum irq_type_t
irq_l_funct_blr, // lookup function BLR and debug info
irq_l_args, // lookup function arguments
irq_s_triggers, // scan triggers
irq_s_triggers2, // scan triggers
irq_s_msgs, // search for message
irq_grant1, // process grant option
irq_grant2, // process grant option

View File

@ -955,15 +955,7 @@ void Trigger::release(thread_db* tdbb)
{
extTrigger.reset();
// dimitr: We should never release triggers created by MET_parse_sys_trigger().
// System triggers do have BLR, but it's not stored inside the trigger object.
// However, triggers backing RI constraints are also marked as system,
// but they are loaded in a regular way and their BLR is present here.
// This is why we cannot simply check for sysTrigger, sigh.
const bool sysTableTrigger = (blr.isEmpty() && engine.isEmpty());
if (sysTableTrigger || !statement || statement->isActive() || releaseInProgress)
if (!statement || statement->isActive() || releaseInProgress)
return;
AutoSetRestore<bool> autoProgressFlag(&releaseInProgress, true);
@ -1904,6 +1896,8 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch
PAG_attachment_id(tdbb);
INI_init_sys_relations(tdbb);
bool cleanupTransactions = false;
if (!options.dpb_verify && CCH_exclusive(tdbb, LCK_PW, LCK_NO_WAIT, NULL))

View File

@ -205,8 +205,10 @@ private:
//
// Flags to indicate normal internal requests vs. dyn internal requests
//
// IRQ_REQUESTS and DYN_REQUESTS are deprecated
const int IRQ_REQUESTS = 1;
const int DYN_REQUESTS = 2;
const int CACHED_REQUESTS = 3;
// Procedure block

View File

@ -1928,9 +1928,6 @@ void MET_load_trigger(thread_db* tdbb,
if (relation)
{
if (relation->rel_flags & REL_sys_trigs_being_loaded)
return;
// No need to load table 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
@ -3213,111 +3210,6 @@ DmlNode* MET_parse_blob(thread_db* tdbb,
}
void MET_parse_sys_trigger(thread_db* tdbb, jrd_rel* relation)
{
/**************************************
*
* M E T _ p a r s e _ s y s _ t r i g g e r
*
**************************************
*
* Functional description
* Parse the blr for a system relation's triggers.
*
**************************************/
SET_TDBB(tdbb);
Attachment* attachment = tdbb->getAttachment();
Database* dbb = tdbb->getDatabase();
relation->rel_flags &= ~REL_sys_triggers;
// Release any triggers in case of a rescan
relation->releaseTriggers(tdbb, true);
// 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->readOnly() && !(relation->rel_flags & REL_temp_tran))
return;
relation->rel_flags |= REL_sys_trigs_being_loaded;
AutoCacheRequest request(tdbb, irq_s_triggers2, IRQ_REQUESTS);
FOR (REQUEST_HANDLE request)
TRG IN RDB$TRIGGERS
WITH TRG.RDB$RELATION_NAME = relation->rel_name.c_str()
AND TRG.RDB$SYSTEM_FLAG = 1
{
const FB_UINT64 type = TRG.RDB$TRIGGER_TYPE;
const USHORT trig_flags = TRG.RDB$FLAGS;
const TEXT* name = TRG.RDB$TRIGGER_NAME;
TrigVector** ptr;
switch (type)
{
case TRIGGER_PRE_STORE:
ptr = &relation->rel_pre_store;
break;
case TRIGGER_POST_STORE:
ptr = &relation->rel_post_store;
break;
case TRIGGER_PRE_MODIFY:
ptr = &relation->rel_pre_modify;
break;
case TRIGGER_POST_MODIFY:
ptr = &relation->rel_post_modify;
break;
case TRIGGER_PRE_ERASE:
ptr = &relation->rel_pre_erase;
break;
case TRIGGER_POST_ERASE:
ptr = &relation->rel_post_erase;
break;
default:
ptr = NULL;
break;
}
if (ptr)
{
blb* blob = blb::open(tdbb, attachment->getSysTransaction(), &TRG.RDB$TRIGGER_BLR);
ULONG length = blob->blb_length + 10;
HalfStaticArray<UCHAR, 128> blr;
length = blob->BLB_get_data(tdbb, blr.getBuffer(length), length);
USHORT par_flags = (USHORT) ((trig_flags & TRG_ignore_perm) ? csb_ignore_perm : 0);
if (type & 1)
par_flags |= csb_pre_trigger;
else
par_flags |= csb_post_trigger;
Statement* statement = NULL;
{
Jrd::ContextPoolHolder context(tdbb, attachment->createPool());
PAR_blr(tdbb, relation, blr.begin(), length, NULL, NULL, &statement, true, par_flags);
}
statement->triggerName = name;
statement->flags |= Statement::FLAG_SYS_TRIGGER | Statement::FLAG_INTERNAL;
if (trig_flags & TRG_ignore_perm)
statement->flags |= Statement::FLAG_IGNORE_PERM;
save_trigger_data(tdbb, ptr, relation, statement, NULL, NULL, NULL, type, true, 0, "",
"", NULL, TriState());
}
}
END_FOR
relation->rel_flags &= ~REL_sys_trigs_being_loaded;
}
void MET_post_existence(thread_db* tdbb, jrd_rel* relation)
{
/**************************************
@ -3968,7 +3860,6 @@ void MET_scan_relation(thread_db* tdbb, jrd_rel* relation)
Database* dbb = tdbb->getDatabase();
Jrd::ContextPoolHolder context(tdbb, attachment->att_pool);
bool dependencies = false;
bool sys_triggers = false;
blb* blob = NULL;
@ -3985,8 +3876,7 @@ void MET_scan_relation(thread_db* tdbb, jrd_rel* relation)
relation->rel_flags |= REL_being_scanned;
dependencies = (relation->rel_flags & REL_get_dependencies) ? true : false;
sys_triggers = (relation->rel_flags & REL_sys_triggers) ? true : false;
relation->rel_flags &= ~(REL_get_dependencies | REL_sys_triggers);
relation->rel_flags &= ~REL_get_dependencies;
for (USHORT itr = 0; itr < TRIGGER_MAX; ++itr)
triggers[itr] = NULL;
@ -4274,17 +4164,7 @@ void MET_scan_relation(thread_db* tdbb, jrd_rel* relation)
// We have just loaded the triggers onto the local vector triggers.
// It's now time to place them at their rightful place inside the relation block.
if (!(relation->rel_flags & REL_sys_trigs_being_loaded))
{
// if we are scanning a system relation during loading the system
// triggers, (during parsing its blr actually), we must not release the
// existing system triggers; because we have already set the
// relation->rel_flag to not have REL_sys_trig, so these
// system triggers will not get loaded again. This fixes bug 8149.
relation->replaceTriggers(tdbb, triggers);
}
relation->replaceTriggers(tdbb, triggers);
LCK_lock(tdbb, relation->rel_rescan_lock, LCK_SR, LCK_WAIT);
relation->rel_flags &= ~REL_being_scanned;
@ -4298,9 +4178,6 @@ void MET_scan_relation(thread_db* tdbb, jrd_rel* relation)
if (dependencies) {
relation->rel_flags |= REL_get_dependencies;
}
if (sys_triggers) {
relation->rel_flags |= REL_sys_triggers;
}
if (blob)
blob->BLB_close(tdbb);
@ -5305,7 +5182,7 @@ void MET_store_dependencies(thread_db* tdbb,
SET_TDBB(tdbb);
const Trigger* t = 0;
const Trigger* t = nullptr;
const bool checkTableScope =
(dependency_type == obj_computed) ||
(dependency_type == obj_trigger) && (dep_rel != 0) &&
@ -5538,11 +5415,6 @@ static bool verify_TRG_ignore_perm(thread_db* tdbb, const MetaName& trig_name)
SET_TDBB(tdbb);
Attachment* attachment = tdbb->getAttachment();
// See if this is a system trigger, with the flag set as TRG_ignore_perm
if (INI_get_trig_flags(trig_name) & TRG_ignore_perm)
return true;
// See if this is a RI trigger
AutoCacheRequest request(tdbb, irq_c_trg_perm, IRQ_REQUESTS);

View File

@ -118,7 +118,6 @@ Jrd::jrd_rel* MET_lookup_relation(Jrd::thread_db*, const Jrd::MetaName&);
Jrd::jrd_rel* MET_lookup_relation_id(Jrd::thread_db*, SLONG, bool);
Jrd::DmlNode* MET_parse_blob(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::bid*, Jrd::CompilerScratch**,
Jrd::Statement**, bool, bool);
void MET_parse_sys_trigger(Jrd::thread_db*, Jrd::jrd_rel*);
void MET_post_existence(Jrd::thread_db*, Jrd::jrd_rel*);
void MET_prepare(Jrd::thread_db*, Jrd::jrd_tra*, USHORT, const UCHAR*);
Jrd::jrd_prc* MET_procedure(Jrd::thread_db*, USHORT, bool, USHORT);

View File

@ -242,6 +242,15 @@ int MOV_get_string(Jrd::thread_db* tdbb, const dsc* desc, UCHAR** address, vary*
}
void MOV_get_string(Jrd::thread_db* tdbb, const dsc* desc, string& str)
{
VaryStr<MAX_SQL_IDENTIFIER_SIZE> temp;
const char* strPtr = NULL;
const auto len = MOV_make_string(tdbb, desc, ttype_metadata, &strPtr, &temp, MAX_SQL_IDENTIFIER_SIZE);
str.assign(strPtr, len);
}
GDS_DATE MOV_get_sql_date(const dsc* desc)
{
/**************************************

View File

@ -42,6 +42,7 @@ SQUAD MOV_get_quad(Jrd::thread_db*, const dsc*, SSHORT);
SINT64 MOV_get_int64(Jrd::thread_db*, const dsc*, SSHORT);
int MOV_get_string_ptr(Jrd::thread_db*, const dsc*, USHORT*, UCHAR**, vary*, USHORT);
int MOV_get_string(Jrd::thread_db*, const dsc*, UCHAR**, vary*, USHORT);
void MOV_get_string(Jrd::thread_db* tdbb, const dsc* desc, Firebird::string& str);
GDS_DATE MOV_get_sql_date(const dsc*);
GDS_TIME MOV_get_sql_time(const dsc*);
ISC_TIME_TZ MOV_get_sql_time_tz(const dsc*);

File diff suppressed because it is too large Load Diff

View File

@ -3363,6 +3363,7 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j
case rel_roles:
case rel_ccon:
case rel_pub_tables:
case rel_priv:
protect_system_table_delupd(tdbb, relation, "UPDATE");
break;