800 likes | 1.06k Views
Lecture 5 Triggers PL/SQL – advanced. Oracle Database Administration. Triggers - introduction. Triggers is a piece of code executed when specified action occurs, for example: user inserts row into a table user deletes something from a table user logs in
E N D
Lecture 5 Triggers PL/SQL – advanced Oracle Database Administration
Triggers - introduction • Triggers is a piece of code executed when specified action occurs, for example: • user inserts row into a table • user deletes something from a table • user logs in • Triggers cannot be executed as a result of SELECT statement
Triggers • Triggers are often used to: • automatically populate table columns, for example generate primary key identifier from a sequence • automatically update related tables, for example: update parent table when records are inserted into the child table • guarantee that specific operation is performed, for example: automatically create records in the history tables
Triggers • Do not use triggers to duplicate built-in features: • for relations use Foreign Keys • to check if single record data is valid use NOT NULL and CHECK constraints • for access control use GRANT and REVOKE
Types of triggers • DML triggers on tables • UPDATE, DELETE, INSERT • INSTEAD OF triggers on views • System triggers on: • DATABASE - triggers fire for each event for each user • SCHEMA - triggers fire for each event for specific user
System triggers • System triggers can be created for the following events: • DDL statements - CREATE, ALTER, DROP • Database operations: • SERVERERROR • LOGON • LOGOFF • STARTUP • SHUTDOWN
System triggers • Example system trigger: CREATE OR REPLACE TRIGGER On_Logon AFTER LOGON ON USER_NAME.Schema BEGIN Do_Something; END;
System triggers • Example system trigger: CREATE TRIGGER log_errors AFTER SERVERERROR ON DATABASE BEGIN IF (IS_SERVERERROR (1017)) THEN ... ELSE ... END IF; END;
DML triggers - options • BEFORE/AFTER - trigger can fire before the operation or after the operation • Trigger can fire one time (statement trigger) or multiple times (row trigger) • Row trigger can have when condition • Row triggers can access new and old row values • Trigger on update can have column list
Before/After triggers • Use Before triggers to: • modify values that are about to be inserted/updated • Use After triggers to: • access newly inserted/updated values (e.g. using foreign keys) • Before triggers are slightly faster than After triggers
Example statement trigger CREATE OR REPLACE TRIGGER trg_1 BEFORE DELETE OR INSERT OR UPDATE ON test1 BEGIN IF INSERTING THEN INSERT INTO statement_log(log) VALUES ('inserting to test1'); ELSIF DELETING THEN INSERT INTO statement_log(log) VALUES ('deleting from test1'); ELSE INSERT INTO statement_log(log) VALUES ('updating test1'); END IF; END;
Example row triggers CREATE TRIGGER order_insert BEFORE INSERT ON orders FOR EACH ROW BEGIN INSERT INTO order_history(hist_id, type, id, order_value) VALUES (hist_seq.nextval, 'insert', :new.id, :new.order_value); END; CREATE TRIGGER order_update BEFORE UPDATE ON orders FOR EACH ROW BEGIN INSERT INTO order_history(hist_id, type, id, order_value) VALUES (hist_seq.nextval, 'update', :new.id, :new.order_value); END; CREATE TRIGGER order_update BEFORE DELETE ON orders FOR EACH ROW BEGIN INSERT INTO order_history(hist_id, type, id, order_value) VALUES (hist_seq.nextval, 'update',:old.id, :old.order_value); END;
Row triggers • Insert trigger has access to new values only • Delete trigger has access to old values only. New values are null and cannot be modified • Update trigger has access to new and old values. • new values can be modified in the Before trigger only • old and new values are available in both Before and After trigger • if a new value is modified in a Before trigger, modified value is visible in the After trigger
Example triggers CREATE TRIGGER expensive_order BEFORE UPDATE ON orders FOR EACH ROW WHEN (new.order_value > 100000 AND old.order_value < 100000) BEGIN ... END; CREATE TRIGGER value_change BEFORE UPDATE OF order_value ON orders FOR EACH ROW BEGIN ... END;
Instead of triggers • Instead of trigger is used for views which are not updateable • View is not updateable if it contains: • set operator (union, intersect etc.) • distinct operator • aggregate function (sum, max, count, etc.) • group by, order by, connect by, start with • subquery in a select list • joins with some exceptions
Instead of triggers • Example Instead of trigger definition: CREATE OR REPLACE TRIGGER trigger_name INSTEAD OF INSERT ON view_name REFERENCING NEW AS n FOR EACH ROW DECLARE rowcnt number; BEGIN SELECT COUNT(*) FROM .... ...
Triggers and transactions • Unless autonomous transactions are used: • trigger executes in the context of the current transaction (the transaction that executed the statement which caused the trigger to fire) • if a transaction is rolled back, trigger results are also rolled back • if a trigger raises an exception, the statement fails and statement-level rollback occurs • trigger cannot use transaction control statements (rollback, commit, savepoint)
Enabling/disabling triggers • Triggers can be in enabled and disabled state • Disabled triggers do not execute • Triggers are created enabled unless the DISABLE clause is used • Commands to enable/disable triggers: ALTER TRIGGER trigger_name ENABLE; ALTER TRIGGER trigger_name DISABLE; ALTER TABLE table_name ENABLE ALL TRIGGERS;
PL/SQL packages • Package is a group of: • functions • procedures • variables • cursors • type declarations • Package consists of two parts: • package specification • package body
Package specification • Package specification contains declarations of public objects: functions, procedures etc. • Only public objects can be accessed from outside the package • Package specification does not contain any code, just declarations • Package specification is created using the CREATE PACKAGEcommand
Example package specification CREATE PACKAGE pack1 IS PROCEDURE p1(param1 IN NUMBER); FUNCTION f1 RETURN VARCHAR2; var1 INTEGER; CURSOR c1 IS SELECT * FROM TEST; END;
Accessing package objects BEGIN pack1.p1(0); result := pack1.f1; pack1.var1 := 1; FOR rec IN pack1.c1 LOOP ... END LOOP; END;
Package body • Package body contains implementation of objects defined in the package specification • Package body is created using the CREATE PACKAGE BODYcommand • Package body must include implementation of all functions and procedures declared in the specification • Package body may define private functions, that will be accessible only from the package body
Example package body CREATE PACKAGE BODY pack1 IS PROCEDURE p1(param1 IN NUMBER) IS BEGIN p2; -- call private procedure END; FUNCTION f1 RETURN VARCHAR2 IS BEGIN ... END; PROCEDURE p2 IS BEGIN ... END; END;
RECORD type • RECORD type: • similar to C structure – contains multiple variables • must be defined as TYPE – RECORD declaration creates new type that can be later used for declaring variable of that type • RECORD can be declared: • in PACKAGE specification • in declaration part of PL/SQL block
RECORD type in a package CREATE PACKAGE record_package IS TYPE DeptRec IS RECORD ( dept_id dept.deptno%TYPE, dept_name VARCHAR2(14) DEFAULT ‘ABC’, dept_loc VARCHAR2(13) ); END;
RECORD type in declaration DECLARE TYPE DeptRec IS RECORD ( dept_id dept.deptno%TYPE, dept_name VARCHAR2(14), dept_loc VARCHAR2(13) ); -- type declaration recordVar DeptRec; -- variable -- declaration
RECORD type • RECORD members: • can have default values • can have NOT NULL constraint • are accessed by "." operator: recordVar.member • RECORD variables: • can be used as function/procedure parameters, function result • can be used as collection elements • cannot be stored in database (table column cannot have type RECORD)
RECORD type • Each table has predefined record for all table columns: DECLARE tableRec TABLE1%ROWTYPE; -- type record • RECORD can be used in SELECT INTO statement: SELECT * INTO tableRec FROM TABLE1 where ID = 1;
RECORD type • RECORD can be used in UPDATE statement: UPDATE TABLE1 SET ROW = tableRec where ID = 1; • RECORD can be used in INSERT statement: INSERT INTO TABLE1 VALUES tableRec;
PL/SQL exceptions • PL/SQL supports exceptions • Exceptions are thrown (raised): • as a result of executing SQL statement • as a result of calling predefined PL/SQL function procedure or package • manually by the user • Catching exceptions: • Exceptions can be caught in PL/SQL block • Uncaught exceptions are propagated to the caller
PL/SQL exceptions • Exceptions and transactions: • exception in SQL statement rolls back current statement, not the entire transaction • exception thrown from PL/SQL does not cause rollback
PL/SQL exceptions • Predefined exceptions: • NO_DATA_FOUND – select into statement • TOO_MANY_ROWS – select into statement • DUP_VAL_ON_INDEX – unique index violated • INVALID_NUMBER – text cannot be converted into number (e.g. TO_NUMBER)
User exceptions • User can create custom exceptions: DECLARE myError EXCEPTION; BEGIN IF ... THEN RAISE myError; END IF; EXCEPTION WHEN myError THEN ROLLBACK; RAISE; END;
Handling Oracle errors • Oracle reports errors as "ORA-xxxxx": ERROR at line 1: ORA-01403: no data found • Some exceptions have PL/SQL names, like NO_DATA_FOUND, TOO_MANY_ROWS • To catch exception without PL/SQL name: • find Oracle error code for that exception • declare symbolic name for that exception • catch that exception in the EXCEPTION block
Handling Oracle errors • For example: deadlock exception has error code ORA-00060: ERROR at line 1: ORA-00060: deadlock detected while waiting for resource • To declare that exception, PRAGMA directive must be used with error code: -60 DECLARE deadlock_detected EXCEPTION; PRAGMA EXCEPTION_INIT( deadlock_detected, -60);
Handling Oracle errors DECLARE deadlock_detected EXCEPTION; PRAGMA EXCEPTION_INIT( deadlock_detected, -60); BEGIN ... -- Some operation that -- causes an ORA-00060 error EXCEPTION WHEN deadlock_detected THEN -- handle the error END;
Custom error messages • application can raise custom errors with custom error messages: raise_application_error( error_number, message[, {TRUE | FALSE}]); • error_number should be in range -20000 .. -20999 • error message can be up to 2048 characters
Accessing error information • Exception handler has access to SQLCODE and SQLERRM functions • SQLCODE contains Oracle error number • SQLERRM contains error message • Example: WHEN OTHERS THEN IF SQLCODE = -60 THEN -- deadlock detected ELSE -- other error DBMS_OUTPUT.PUT_LINE(SQLCODE || ' ' || SQLERRM); END IF END;
Dynamic SQL • PL/SQL enables execution of dynamic sql (SQL unknown at compilation time) • Dynamic SQL can be executed using: • EXECUTE IMMEDIATE command • OPEN FOR, FETCH, CLOSE statements • DBMS_SQL package
EXECUTE IMMEDIATE • Example: EXECUTE IMMEDIATE 'DELETE FROM ' || table_name; EXECUTE IMMEDIATE 'CREATE TABLE test(id NUMBER)'; • EXECUTE IMMEDIATE • executes SQL command as text • SQL command can be dynamically built at run time
EXECUTE IMMEDIATE • EXECUTE IMMEDIATE does not have access to PL/SQL variables: DECLARE v INTEGER; BEGIN EXECUTE IMMEDIATE 'DELETE FROM test WHERE id = v'; -- Run time error END;
EXECUTE IMMEDIATE • EXECUTE IMMEDIATE can execute: • any DML statement • DDL statements, session control statements, system control statements • can use bind variables and return results DECLARE sql_code VARCHAR2(100) := 'UPDATE table1 SET col1 = :val'; value1 NUMBER := 10; BEGIN EXECUTE IMMEDIATE sql_code USING value1; END;
DDL in PL/SQL BEGIN EXECUTE IMMEDIATE 'CREATE TABLE TAB1(ID NUMBER)'; EXECUTE IMMEDIATE 'INSERT INTO TAB1(ID) VALUES (1)'; INSERT INTO TAB1(ID) VALUES (2) – error -- table TAB1 does not exist when the code -- is compiled END;
Example usage CREATE FUNCTION count_rows( table_name VARCHAR2) RETURN NUMBER CNT NUMBER; IS BEGIN EXECUTE IMMEDIATE 'SELECT COUNT(*) INTO :cnt FROM ' || table_name INTO CNT; RETURN CNT; END;
Example usage CREATE PROCEDURE delete_from( table_name VARCHAR2, id NUMBER) IS BEGIN EXECUTE IMMEDIATE 'DELETE FROM ' || table_name || ' WHERE id = :id' USING ID; END;
CURSOR variables • CURSOR variables are variables that contain reference to cursors (pointers to cursors) • CURSOR variables can be returned from functions and passed to other programming languages, for example: • Java or C++ program calls PL/SQL procedure • PL/SQL procedure opens cursor • Cursor is returned back to Java or C++ (to the client) • The client reads cursor data, like it does with normal SELECT statements • CURSOR variables can also be passed between PL/SQL functions
CURSOR variables • Using CURSOR variable requires: • declaring CURSOR TYPE • declaring CURSOR variable • opening CURSOR • CURSOR must be closed when it is no longer required • Cursor type can be weak or strong • Structure of the strong cursor is known at compile time (number and types of columns) • Weak cursor can be opened for SQL statement returning any set of columns
CURSOR type • Declaring generic cursor type: DECLARE -- weak cursor type TYPE GenericCurTyp IS REF CURSOR; BEGIN • Declaring strong cursor type: DECLARE TYPE TYPE EmpCurTyp IS REF CURSOR RETURN employees%ROWTYPE; • strong cursor type can only be used with queries that return declared type
CURSOR variable • Cursor variable can be declared in DECLARE block: DECLARE cursor_var GenericCurTyp; • Can be used as function parameter: CREATE PROCEDURE proc1 ( emp_cv IN OUT EmpCurTyp) IS ... • Can be returned from a function: CREATE FUNCTION func1 RETURN GenericCurTyp IS ...