Download presentation
Presentation is loading. Please wait.
Published byEvangeline Webb Modified over 9 years ago
1
1 Theory, Practice & Methodology of Relational Database Design and Programming Copyright © Ellis Cohen 2002-2008 Enforcing State & Transition Constraints with Triggers These slides are licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 2.5 License. For more information on how you may use them, please see http://www.openlineconsult.com/db
2
2 © Ellis Cohen 2001-2008 Overview of Lecture Table-Based Constraint Contexts Triggers Statement Triggers AFTER Statement Triggers Implementing Derivation Using AFTER Statement Triggers Application-Based vs Trigger-Based State Constraint Enforcement Two Phase Trigger-Based Enforcement Transactions, Triggers & Deferred Constraints
3
3 © Ellis Cohen 2001-2008 Table-Based Constraint Contexts
4
4 © Ellis Cohen 2001-2008 Relational Transition Constraint FiddleWithDept( :deptno ) RESULTS IN NO Emps& e SATISFIES e.deptno@ = :deptno Constraint Context: Circumstances in which the side effect is to be performed or checked Transition Assertion: Formal description of the side effect
5
5 © Ellis Cohen 2001-2008 Operation-Dependence A specific user operation TerminateEmp( :empno ) A list of specific user operations TerminateEmp( :empno ), DestroyDept( :deptno, :reldept ) Any operation/query/action ANY ACTION Any operation/query/action for some role ANY OPERATION BY DeptMgr Operation-based contexts with exceptions ANY OPERATION EXCEPT (ANY QUERY BY Employee) Table-based UPDATE OF job ON Emps Operation- Dependent Operation- Independent
6
6 © Ellis Cohen 2001-2008 Operation vs Table-Based A specific user operation TerminateEmp( :empno ) A list of specific user operations TerminateEmp( :empno ), DestroyDept( :deptno, :reldept ) Any operation/query/action ANY ACTION Any operation/query/action for some role ANY OPERATION BY DeptMgr Operation-based contexts with exceptions ANY OPERATION EXCEPT (ANY QUERY BY Employee) Table-based UPDATE OF job ON Emps Operation- Based Table- Based
7
7 © Ellis Cohen 2001-2008 Context Examples Identify the context in which an employee can be terminated Operation-Based TerminateEmp( :empno ) DestroyDept( :deptno ) These are the only actions that terminate an employee Table-Based DELETE ON Emps You can tell an employee has been/will be terminated if a tuple has been/will be deleted from the Emps table
8
8 © Ellis Cohen 2001-2008 Table-Based Constraint Contexts DELETE ON Emps INSERT ON Emps UPDATE ON Emps UPDATE OF job, sal ON Emps UPDATE OF job, sal OR DELETE ON Emps These are ways of characterizing the effects of SQL modification operations Specific fields can be listed, but only for UPDATE. Indicates that EITHER the job OR sal is updated
9
9 © Ellis Cohen 2001-2008 Table Constraint Context Exercise Write the table constraint contexts that characterize the cause of these situations: An employee has been terminated DELETE ON Emps A department no longer exists (use Depts) A department is no longer at its previous location. Use Depts( deptno, dname, loc ) There is a change in the number of projects assigned to an employee. Use Asns( pno, empno, hrs )
10
10 © Ellis Cohen 2001-2008 Table Constraint Context Answer A department no longer exists DELETE ON Depts A department is no longer at its previous location. UPDATE OF loc OR DELETE ON Depts A change in the number of projects assigned to an employee UPDATE OF empno OR INSERT OR DELETE ON Asns
11
11 © Ellis Cohen 2001-2008 No UPDATES of Referenced Primary Keys A department no longer exists DELETE ON Depts Why not UPDATE OF deptno OR DELETE ON Depts primary key attribute It is ALMOST ALWAYS a bad idea to allow a primary key attribute (e.g. deptno) to be updated as part of an ordinary user operation, if either the table represents an entity class (e.g. a Dept), or a foreign key references the attribute Does changing the deptno mean that the dept no longer exists?
12
12 © Ellis Cohen 2001-2008 UPDATEs of Bridge Table Keys Asns( pno, empno, hrs ) Can we UPDATE empno in Asns? This implies that we might change empno in Asns (e.g. if we wanted to switch an assignment from one employee to another), even though empno is part of Asns' primary key This is acceptable for bridge tables (they represent relationships, not entity classes), but be careful if any other table has a composite FK reference to Asns
13
13 © Ellis Cohen 2001-2008 Contexts For State Constraints Every department that has a clerk must have a dept manager What's the table constraint context on Emps to detect violations of
14
14 © Ellis Cohen 2001-2008 Another Description Solution Violation will occur if either a)An employee is inserted into Emps (i.e a new clerk) b)An employee is deleted from Emps (i.e. the dept manager) c)An employee changes departments d)An employee changes jobs Every department that has a clerk must have a dept manager UPDATE OF deptno, job OR INSERT OR DELETE ON Emps
15
15 © Ellis Cohen 2001-2008 UPDATE OF deptno, job OR INSERT OR DELETE ON Emps RESULTS IN EACH DeptMgrClerks& WHERE cknt@ > 0 SATISFIES mknt@ > 0 Table-Based Relational Transition Constraint Table-Based Constraint Context Every department that has a clerk must have a dept manager
16
16 © Ellis Cohen 2001-2008 Triggers
17
17 © Ellis Cohen 2001-2008 Triggers Monitor (potential) changes to the database –After the change is done, or before it is attempted, arbitrary code can be executed Enforce Constraints –Primarily state & transition constraints, but can be used for access constraints as well
18
18 © Ellis Cohen 2001-2008 What Can be Monitored? Database Events Startup, Shutdown, Logon, Logoff, Server Errors Data Definition Events Create, Alter, Drop, etc. Data Manipulation Events Insert, Update, Delete
19
19 © Ellis Cohen 2001-2008 Triggers and Constraint Contexts Table-based constraint contexts define the context in which a behavior constraint applies The exact same format is used for the trigger context, the circumstance in which a trigger is run. CREATE OR REPLACE TRIGGER wkday_term BEFORE DELETE ON Emps CALL CheckWeekday
20
20 © Ellis Cohen 2001-2008 Uses of Triggers Enforce business rules that can't be represented by built-in integrity constraints Automatically set ids and derived values Audit changes Automatically retain old versions of data Automatically propagate modifications Build materialized views and replicate changes back to base tables Support smart modification of views
21
21 © Ellis Cohen 2001-2008 Statement Triggers
22
22 © Ellis Cohen 2001-2008 Reject Changes to a Table CREATE OR REPLACE TRIGGER wkday_term BEFORE DELETE ON Emps CALL CheckWeekday Before deleting an employee from Emps, check if it's a weekday (and if not, raise an exception) Checks whenever an employee is being deleted from Emps Especially useful if a user (e.g. the DBA) attempts to delete an employee by using a SQL DELETE command directly instead of by calling TerminateEmp What kind of constraint is being enforced?
23
23 © Ellis Cohen 2001-2008 Reject Column Changes CREATE OR REPLACE TRIGGER wkday_sal BEFORE UPDATE OF sal ON Emps CALL CheckWeekday Before changing an employee's salary, check if it's a weekday (and if not, raise an exception) Note reuse of the checking code
24
24 © Ellis Cohen 2001-2008 CheckWeekday Implementation PROCEDURE CheckWeekday IS BEGIN IF getDayOfWeek() IN [ 'SAT', SUN' ] THEN RAISE_APPLICATION_ERROR( -20086, 'Operation only allowed on weekday' ); END IF; END; FUNCTION getDayOfWeek RETURN varchar IS today varchar(3) BEGIN SELECT to_char(sysdate,'DY') INTO today FROM dual; RETURN today; END;
25
25 © Ellis Cohen 2001-2008 Triggers and Exceptions PROCEDURE CheckWeekday IS BEGIN IF getDayOfWeek() IN [ 'SAT', SUN' ] THEN RAISE_APPLICATION_ERROR( -20086, 'Operation only allowed on weekday' ); END IF; END; An exception propagated out of triggered code acts as if it were raised by the triggering statement BEGIN DELETE FROM Emps WHERE empno = :empno; END; CREATE OR REPLACE TRIGGER wkday_term BEFORE DELETE ON Emps CALL CheckWeekday TerminateEmp( :empno )
26
26 © Ellis Cohen 2001-2008 Inline Trigger Code CREATE OR REPLACE TRIGGER wkday_term BEFORE DELETE ON Emps BEGIN IF getDayOfWeek() IN [ 'SAT', SUN' ] THEN RAISE_APPLICATION_ERROR( -20086, 'Operation only allowed on weekday' ); END IF; END; Trigger code can be inline. Make the choice based on aesthetics & reusability
27
27 © Ellis Cohen 2001-2008 AFTER Statement Triggers
28
28 © Ellis Cohen 2001-2008 BEFORE vs AFTER Triggers AFTER triggers are run AFTER a modification Triggered code sees the DB state AFTER the operation runs. BEFORE triggers are run BEFORE a SQL DML modification operation (INSERT, UPDATE, DELETE, MERGE) Triggered code sees the DB state BEFORE the modification.
29
29 © Ellis Cohen 2001-2008 Statement Triggers & Tuples SQL DML operations which modify tables are INSERT, UPDATE, DELETE & MERGE (which inserts, updates & deletes tuples) Any of these operations can involve multiple tuples. An AFTER statement trigger –runs after all the tuples have been modified (it will even run if a DML operation was executed, but no tuples were actually modified!) –has no inherent way of knowing which tuples (or even how many) were affected by the operation (in Oracle)
30
30 © Ellis Cohen 2001-2008 Enforcing Transition Constraints Transition constraints enforce side- effects, which take place as the result of an operation. AFTER statement triggers are the obvious way to implement the side effect (by correction or rejection). Consider the transition constraint: When any operation makes a change to the projects table, log the user & time the change was made, along with the new total project budget
31
31 © Ellis Cohen 2001-2008 Audit Changes to a Table PROCEDURE LogChange IS sumbudget number(10,2); usr varchar(30); tim date; BEGIN SELECT user, sysdate INTO usr, tim FROM dual; SELECT sum(budget) INTO sumbudget FROM Projs; INSERT INTO ProjLog VALUES( sumbudget, usr, tim ); END; After any change to the Projs table, log which user made the change, the date/time, and the new total project budgets CREATE OR REPLACE TRIGGER audit_proj AFTER INSERT OR UPDATE OR DELETE ON Projs CALL LogChange CREATE TABLE ProjLog ( sumbudget number(10,2), usr varchar(30), tim date ); Requires AFTER Trigger
32
32 © Ellis Cohen 2001-2008 Implementing Derivation Using AFTER Statement Triggers
33
33 © Ellis Cohen 2001-2008 Derivation Exercise CREATE TABLE Depts ( deptnoint primary key, dnamevarchar(12) not null, locvarchar(18), totsalint ) Conceptual Transition Constraint A department's totsal is automatically derived and is the total salary of the employees in the department What kind of trigger is needed? What's the table constraint context?
34
34 © Ellis Cohen 2001-2008 Implementing Derivation What kind of trigger (before/after)? An AFTER trigger What’s the table modification description? INSERT OR DELETE OR UPDATE Of sal, deptno ON Emps Is it possible to tell which employees/depts had their salaries modified? NO What does the trigger do? Recalculates the total salary of every department
35
35 © Ellis Cohen 2001-2008 Derivation Solution CREATE OR REPLACE TRIGGER emp_upd_totsals AFTER INSERT OR DELETE OR UPDATE Of sal, deptno ON Emps BEGIN UPDATE Depts d SET totsal = (SELECT sum(sal) FROM Emps e WHERE e.deptno = d.deptno); END; Sets each department's totsal to be the sum of the sals of the employees who are in that department Note that UPDATE loops through the tuples (restricted by the WHERE clause, if any) This is a correlated UPDATE, using the table alias d Performance DANGER: Even if we only updated the salary of one employee, this requires scanning the entire Emps table
36
36 © Ellis Cohen 2001-2008 Exceptions & AFTER Triggers AFTER triggers are run AFTER a SQL DML modification statement, so the triggered code sees the DB state AFTER the modification. Even if the trigger raises an exception, the modification was ALREADY performed If the trigger raises an exception, the modification is UNDONE (along with any modification made by triggers already fired as part of this statement!) A statement does not complete execution until all triggers are executed. If a trigger raises an exception, the statement fails, is rolled back (since statements always run inside a nested transaction), and re-raises the exception If the re-raised exception is not caught, that can cause rollback of the entire transaction
37
37 © Ellis Cohen 2001-2008 Exceptions in After Trigger Undo CREATE OR REPLACE TRIGGER wkday_term AFTER DELETE ON Emps BEGIN IF getDayOfWeek() IN [ 'SAT', SUN' ] THEN RAISE_APPLICATION_ERROR( -20086, 'Operation only allowed on weekday' ); END IF; END; Still works as an AFTER trigger. If the application error is raised, and not caught any DELETE will be undone.
38
38 © Ellis Cohen 2001-2008 Application-Based vs Trigger-Based State Constraint Enforcement
39
39 © Ellis Cohen 2001-2008 State Constraint & Assertion Every department that has a clerk must have a dept manager Define a manifest view DeptMgrClerks which shows the # of clerks (cknt) & the # of managers (mknt) in each dept EACH DeptMgrClerksView WHERE cknt > 0 SATISFIES mknt > 0
40
40 © Ellis Cohen 2001-2008 Manifest View Define a view that indicates the # of clerks and the # of managers in each dept 1021 2011 3031 4000 deptno cknt mknt DeptMgrClerksView Including depts without employees is optional
41
41 © Ellis Cohen 2001-2008 Manifest View Definition CREATE VIEW DeptMgrClerksView AS SELECT deptno, (SELECT count(*) FROM Emps e WHERE d.deptno = e.deptno AND job = 'CLERK') AS cknt, (SELECT count(*) FROM Emps e WHERE d.deptno = e.deptno AND job = 'DEPTMGR') AS mknt FROM Depts d Define a manifest view DeptMgrClerks which shows the # of clerks (cknt) & the # of managers (mknt) in each dept
42
42 © Ellis Cohen 2001-2008 Application-Based Enforcement 1)Define a procedure PROCEDURE CheckDeptMgrClerks IS BEGIN FOR drec IN ( SELECT deptno FROM DeptMgrClerksView WHERE cknt > 0 AND mknt = 0) LOOP RAISE_APPLICATION_ERROR( -20099, 'Dept with clerks has no manager' ); END LOOP; END; 2) At the end of the code which implements ChangeJob, AddEmp, TerminateEmp, MoveEmp, or DestroyDept call CheckDeptMgrClerks() Raises an exception if any dept with a clerk has no dept mgr
43
43 © Ellis Cohen 2001-2008 Using Application-Based Enforcement BEGIN DELETE FROM Emps WHERE empno = :empno; CheckDeptMgrClerks(); EXCEPTION WHEN OTHERS THEN doerr(); END; TerminateEmp( :empno ) And similarly for ChangeJob, AddEmp, MoveEmp & DestroyDept Operation- independent (i.e. expensive) state constraint enforcement
44
44 © Ellis Cohen 2001-2008 UPDATE OF deptno, job OR INSERT OR DELETE ON Emps RESULTS IN EACH DeptMgrClerks& WHERE cknt@ > 0 SATISFIES mknt@ > 0 Table-Based Relational Transition Constraint Table-Based Constraint Context Every department that has a clerk must have a dept manager
45
45 © Ellis Cohen 2001-2008 Trigger-Based Enforcement CREATE OR REPLACE TRIGGER mgr_clerks AFTER UPDATE OF deptno, job OR INSERT OR DELETE ON Emps CALL CheckDeptMgrClerks Do NOT explicitly call CheckDeptMgrClerks from TerminateEmp, AddEmp, MoveEmp, ChangeJob & DestroyDept. Instead, just define this trigger! The statement trigger can use exactly the same operation-independent (i.e. expensive) state constraint checking procedure
46
46 © Ellis Cohen 2001-2008 Pros & Cons Trigger Advantages Triggers continue to work even when operations are (badly) changed, or when corrections add new side effects to operations Protect against coding errors (e.g. forgot DestroyDept) Protect against incorrect changes by (assistant) DBA's Trigger Disadvantages Triggers can be hard to code & maintain, especially when multiple triggers interact Triggers run immediately when a table is modified – can be problematic with operations that do a sequence of related modifications
47
47 © Ellis Cohen 2001-2008 Two-Phase Trigger-Based Enforcement
48
48 © Ellis Cohen 2001-2008 Two-Phase Trigger-Based Enforcement Sometimes black box enforcement of a transition constraint requires two phases: 1.Collect information at the start of the action (in local variables, package variables, or temporary tables) 2.Use that information at the end of the action to figure out whether there is a violation, and if so, reject or correct. Triggers automatically provide black box enforcement. 1.BEFORE statement triggers collect information 2.AFTER statement triggers use it
49
49 © Ellis Cohen 2001-2008 Calculate Differences Suppose that you wanted to do the following: After any change to the Projs table, log which user made the change, the date/time, and the change in the total project budgets -- use a BEFORE trigger to determine the old sum CREATE OR REPLACE TRIGGER prepare_proj BEFORE INSERT OR UPDATE OR DELETE ON Projs CALL ProjLogPkg.RememberOldSum -- the AFTER trigger uses it to calculate the change CREATE OR REPLACE TRIGGER audit_proj AFTER INSERT OR UPDATE OR DELETE ON Projs CALL ProjLogPkg.LogChange
50
50 © Ellis Cohen 2001-2008 Package Variables Remember Session Information CREATE OR REPLACE PACKAGE BODY ProjLogPkg AS oldsum number; PROCEDURE RememberOldSum IS BEGIN SELECT sum(budget) INTO oldsum FROM Projs; END; PROCEDURE LogChange IS sumbudget number(10,2); usr varchar(30); tim date; BEGIN SELECT user, sysdate INTO usr, tim FROM dual; SELECT sum(budget) INTO sumbudget FROM Projs; INSERT INTO ProjLog VALUES( sumbudget – oldsum, usr, tim ); END; END ProjLogPkg;
51
51 © Ellis Cohen 2001-2008 Statement Trigger Evaluation Order When a DELETE/INSERT/UPDATE/MERGE is executed All BEFORE statement triggers with a matching modification description are executed The statement is executed (modifying one or more tuples) All AFTER Statement triggers with a matching modification description are executed If a trigger can be run as a BEFORE or AFTER trigger, sometimes you can choose a type simply to ensure that they are run before/after other triggers
52
52 © Ellis Cohen 2001-2008 Ordering Similar Triggers There can be multiple BEFORE triggers (and multiple AFTER triggers) on a table. What determines the order in which they are run? As of Oracle 11g, the order can be determined, if necessary. CREATE OR REPLACE TRIGGER trig1 AFTER UPDATE ON SomeTable CALL Procedure1 CREATE OR REPLACE TRIGGER trig2 AFTER UPDATE ON SomeTable FOLLOWS trig1 CALL Procedure2
53
53 © Ellis Cohen 2001-2008 Transactions, Triggers & Deferred Constraints
54
54 © Ellis Cohen 2001-2008 Participation Constraints Implement using a manifest view EACH DeptKntsView SATISFIES eknt > 0 Given CREATE VIEW DeptKntsView AS SELECT deptno, count(empno) AS eknt FROM (Depts NATURAL LEFT JOIN Emps) GROUP BY deptno 103 205 306 400 eknt deptno Consider enforcing: Every dept has at least one employee.
55
55 © Ellis Cohen 2001-2008 Creating a Department Creating a department would have to be done by an action that simultaneously creates a new department and places at least one employee in it. BEGIN -- creates the new dept INSERT INTO Depts VALUES ( :deptno, :dname ); -- moves :empno to that dept UPDATE Emps SET deptno = :deptno WHERE empno = :empno; EXCEPTION WHEN OTHERS THEN doerr(); END; CreateDept( :deptno, :dname, :empno )
56
56 © Ellis Cohen 2001-2008 Application-Based Enforcement BEGIN INSERT INTO Depts VALUES ( :deptno, :dname ); UPDATE Emps SET deptno = :deptno WHERE empno = :empno; CheckDeptKnt(); EXCEPTION WHEN OTHERS THEN doerr(); END; CreateDept( :deptno, :dname, :empno ) PROCEDURE CheckDeptKnts IS BEGIN FOR erec IN (SELECT deptno FROM DeptKntsView WHERE eknt = 0) LOOP RAISE_APPLICATION_ERROR( -20099, 'Dept has no employees' ); END LOOP; END;
57
57 © Ellis Cohen 2001-2008 Trigger-Based Implementation CREATE OR REPLACE TRIGGER emp_check AFTER UPDATE OF deptno OR DELETE ON Emps CALL CheckDeptKnts CREATE OR REPLACE TRIGGER dept_check AFTER INSERT ON Depts CALL CheckDeptKnts This would be OK if we could delay firing of the trigger until the end of the operation/trigger, but we can't. The dept_check trigger will fire immediately after the new dept is added!
58
58 © Ellis Cohen 2001-2008 Can't Create Dept First OOPS! This will cause the dept_check trigger to raise an exception BEGIN INSERT INTO Depts VALUES ( :deptno, :dname ); UPDATE Emps SET deptno = :deptno WHERE empno = :empno; EXCEPTION WHEN OTHERS THEN doerr(); END; CreateDept( :deptno, :dname, :empno )
59
59 © Ellis Cohen 2001-2008 Can't Move Employee First OOPS! This will cause the referential integrity constraint to raise an exception BEGIN UPDATE Emps SET deptno = :deptno WHERE empno = :empno; INSERT INTO Depts VALUES ( :deptno, :dname ); EXCEPTION WHEN OTHERS THEN doerr(); END; CreateDept( :deptno, :dname, :empno )
60
60 © Ellis Cohen 2001-2008 Resolving Conflicting Constraints 1.Use application-based implementation (but it no longer protects against program and DBA errors) 2.Defer referential integrity constraints 3.Use package flags to control trigger code (Note: Cannot disable [and later re-enable] triggers; it disables the trigger for all users, and ends the current transaction)
61
61 © Ellis Cohen 2001-2008 Transactions & Constraints The problem is that constraints and assertions are not meant to hold in the middle of a transaction! We can defer enforcement of the referential integrity constraint by defining the deptno attribute in Emps as deptno int REFERENCES Depts INITIALLY DEFERRED INITIALLY DEFERRED says to defer the constraint checking until the transaction is about to commit
62
62 © Ellis Cohen 2001-2008 Using Deferred Constraints BEGIN UPDATE Emps SET deptno = :deptno WHERE empno = :empno; -- The referential integrity check is deferred INSERT INTO Depts VALUES ( :deptno, :dname ); -- The trigger has no effect, because the employee has already been moved. EXCEPTION WHEN OTHERS THEN doerr(); END; -- Later, as part of transaction commit, the referential integrity constraint will be checked, but will succeed since the dept has been inserted CreateDept( :deptno, :dname, :empno )
63
63 © Ellis Cohen 2001-2008 Protected Trigger CREATE OR REPLACE PACKAGE TestDeptCheck AS reallyCheck boolean := TRUE; END TestDeptCheck; CREATE OR REPLACE TRIGGER dept_check AFTER INSERT ON Depts BEGIN IF TestDeptCheck.reallyCheck THEN CheckDeptKnts(); END IF; END;
64
64 © Ellis Cohen 2001-2008 Controlling the Trigger BEGIN TestDeptCheck.reallyCheck := FALSE; INSERT INTO Depts VALUES ( :deptno, :dname ); UPDATE Emps SET deptno = :deptno WHERE empno = :empno; TestDeptCheck.reallyCheck := TRUE; -- Since the trigger wasn't checking the constraint, -- check the constraint explicitly to be safe CheckDeptKnts(); EXCEPTION WHEN OTHERS THEN TestDeptCheck.reallyCheck := TRUE; doerr(); END; CreateDept( :deptno, :dname, :empno )
65
65 © Ellis Cohen 2001-2008 Transactions, Assertions & Triggers Because not all these "Chicken & Egg" situations can be solved by deferring checking of built-in constraints, it would be useful to have Built-in Assertions that could be deferred until transaction commit. Statement Triggers, whose firing could be deferred until transaction commit. Neither is supported by the SQL standard or by commercial databases.
66
66 © Ellis Cohen 2001-2008 Passing Parameters to Triggers BEGIN TriggerParmPkg.parmval := :parm; UPDATE …; -- The update will cause the trigger to be raised -- which will have access to parmval TriggerParmPkg.parmval := NULL; EXCEPTION WHEN OTHERS THEN TriggerParmPkg.parmval := NULL; doerr(); END; SomeAction( :parm ) This approach can also be used to pass parameter values to triggers. Use this technique very cautiously, since it can negate the advantage of having triggers be operation-independent
67
67 © Ellis Cohen 2001-2008 "Parameterized" Trigger CREATE OR REPLACE TRIGGER some_update AFTER UPDATE ON … CALL DoUpdateTrigger( TriggerParmPkg.parmval ); To limit the degree of operation-dependence, DoUpdateTrigger should generally do something reasonable if TriggerParmPkg.parmval is NULL
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.