Upload
caryn-salas
View
103
Download
3
Embed Size (px)
DESCRIPTION
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 - PowerPoint PPT Presentation
Citation preview
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 triggerCREATE 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 triggersCREATE 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 triggersCREATE 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 PACKAGE command
Example package specificationCREATE PACKAGE pack1 IS
PROCEDURE p1(param1 IN NUMBER);
FUNCTION f1 RETURN VARCHAR2;
var1 INTEGER;
CURSOR c1 IS SELECT * FROM TEST;
END;
Accessing package objectsBEGIN
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 BODY command
• 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 bodyCREATE 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 packageCREATE 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 declarationDECLARE
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 errorsDECLARE 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 resultsDECLARE
sql_code VARCHAR2(100) :=
'UPDATE table1 SET col1 = :val';
value1 NUMBER := 10;
BEGIN
EXECUTE IMMEDIATE sql_code USING value1;
END;
DDL in PL/SQLBEGIN
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 usageCREATE 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 usageCREATE 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 ...
Opening cursorDECLARE
cursor_var GenericCurTyp;
BEGIN
IF .... THEN
OPEN cursor_var FOR
SELECT * FROM table1;
ELSE
OPEN cursor_var FOR
'SELECT * FROM ' || tableName ||
' WHERE ID = :p' USING id;
END IF;
FETCH cursor_var INTO rec;
CLOSE cursor_var;
END;
Opening cursorCREATE FUNCTION selectFunc(tableName IN VARCHAR2) RETURN GenericCurTyp
DECLARE
cursor_var GenericCurTyp;
BEGIN
OPEN cursor_var FOR
'SELECT * FROM ' || tableName;
RETURN cursor_var;
END;
• Caller must close the returned cursor: cursorVar := selectFunc('some_table');
CLOSE cursorVar;
PL/SQL collection types• PL/SQL does not support regular
collections: arrays, lists, hash maps etc.
• PL/SQL supports three types of collections:– index-by tables– nested tables– varrays
index-by tables• Declaration:DECLARE
TYPE tab IS TABLE OF VARCHAR2(100)
INDEX BY BINARY-INTEGER
• Characteristics:– similar to hash-tables in other languages– index-by table can store any number of elements– can be indexed by number or character type– cannot be stored in a database
Using index-by tablesDECLARE
TYPE tab IS TABLE OF VARCHAR2(100)
INDEX BY BINARY-INTEGER
var tab;
BEGIN
var(1) := 'First item';
var(-100) := 'item before first';
var(100) := 'last item';
var(10000) := 'item after last';
IF var.exists(20) THEN ... END IF;
IF var.first = -100 THEN ... END IF;
END;
VARRAYs• Declaration:DECLARE
TYPE varray_type IS VARRAY(50)
OF INTEGER;
• Characteristics:– array with variable size up to the specified limit– dense array (index starts at 1)– similar to normal array in other languages– can be stored in a database– must be constructed before use (initially is NULL)
Using VARRAYsDECLARE
TYPE varray_type IS VARRAY(50)
OF INTEGER;
var varray_type;
BEGIN
var(1) := 10; -- ERROR – var IS NULL
var := varray_type();
var.extend; -- add element
var(1) := 10; -- ok
var := varray_type(10, 20, 30) – ok
var.extend(51) -- ERROR – limit is 50
END;
Using VARRAYs in SQLCREATE TABLE tab1 (
id NUMBER PRIMARY KEY,
name VARCHAR2(100),
varray_col varray_type );
INSERT INTO tab1 VALUES (1, 'some name',
varray_type(10, 20, 30, 40, -100));
DECLARE
var varray_type(10, -100, 20, -100);
BEGIN
update tab1 set
varray_col = var WHERE id = 1;
END;
Nested tables• Declaration:CREATE TYPE nested_type
AS TABLE OF VARCHAR(1000);
• Characteristics:– array with no size limit– initially dense, can become sparse when elements are
removed– can be stored in a database– must be constructed before use (initially is NULL)
Using nested tablesDECLARE
TYPE nested_type
IS TABLE OF VARCHAR(1000);
var := nested_type();
BEGIN
var.extend(100);
var(1) := 'first element';
var(2) := 'second element';
IF var(3) IS NULL THEN ... END IF
IF var(101) IS NULL -– ERROR
END IF;
END IF;
Using nested tables in SQLCREATE TABLE tab2 (
ID NUMBER PRIMARY KEY,
NAME VARCHAR2(100),
nested_col nested_type);
INSERT INTO tab2 VALUES (1, 'name',
nested_type('val1', 'val2', 'val3'));
Using collection methods• The following methods exist for collections:
• EXISTS – checks if index exists• COUNT – number of objects in the collection
• LIMIT – maximum number of elements in a collection (VARRAY-s)
• FIRST and LAST – lowest and highest index• PRIOR and NEXT – previous next element in the collection (useful in index-by tables)
• EXTEND – adds new element (VARRAY-s and nested tables)
• TRIM – remove elements from the end• DELETE – delete elements
FORALL statement• bulk-bind – faster than normal FOR statement:DECLARE
TYPE NumList IS VARRAY(10) OF NUMBER;
depts NumList :=
NumList(20,30,50,55,57,60,70,75,90,92);
BEGIN
FORALL j IN 4..7
UPDATE emp SET sal = sal * 1.10
WHERE deptno = depts(j);
END;
BULK COLLECT• SELECT INTO nested table:DECLARE
TYPE NumTab IS TABLE OF emp.empno%TYPE;
TYPE NameTab IS TABLE OF emp.ename%TYPE;
enums NumTab; -- no need to initialize
names NameTab;
BEGIN
SELECT empno, ename BULK COLLECT INTO
enums, names FROM emp;
...
END;
BULK COLLECT• FETCH into nested tables:DECLARE
TYPE NameList IS TABLE OF emp.ename%TYPE;
TYPE SalList IS TABLE OF emp.sal%TYPE;
CURSOR c1 IS SELECT ename, sal FROM emp WHERE sal > 1000;
names NameList;
sals SalList;
BEGIN
OPEN c1;
FETCH c1 BULK COLLECT INTO names, sals;
END;
BULK COLLECT• FETCH into nested table of type record:DECLARE
TYPE DeptRecTab IS TABLE OF dept%ROWTYPE;
dept_recs DeptRecTab;
CURSOR c1 IS
SELECT deptno, dname, loc FROM dept WHERE deptno > 10;
BEGIN
OPEN c1;
FETCH c1 BULK COLLECT INTO dept_recs
LIMIT 200;
END;
PL/SQL security• By default PL/SQL ignores roles, it only
sees privileges granted directly
• To access table from some other schema:– grant direct access to it, e.g.
• GRANT select ON schema.name TO some_user;
– define the procedure with invoker rights
Invoker rights• To create procedure with invoker rights:CREATE OR REPLACE PROCEDURE test
AUTHID CURRENT_USER IS
BEGIN
...
END;
Invoker rights• AUTHID is specified in the header of a program unit. The
same cannot be specified for individual programs or methods within a package or object type.
• Definer rights will always be used to resolve any external references when compiling a new routine.
• For an invoker rights routine referred in a view or a database trigger, the owner of these objects is always considered as the invoker, and not the user triggering it.
Standard PL/SQL packages• DBMS_JOB – handles database jobs
• DBMS_LOB – handle BLOB and CLOB types
• DBMS_MVIEW – manage materialized views
• DBMS_OUTPUT – print messages to console
• DBMS_RANDOM – generate random numbers
• UTL_FILE – access files from PL/SQL programs
• UTL_HTTP – make HTTP requests
• UTL_SMTP – send email from PL/SQL
• UTL_TCP – make TCP/IP connections
DBMS_JOBDBMS_JOB.SUBMIT(
JOB OUT BINARY_INTEGER,
WHAT IN VARCHAR2,
NEXT_DATE IN DATE DEFAULT SYSDATE,
INTERVAL IN VARCHAR2 DEFAULT 'NULL',
NO_PARSE IN BOOLEAN DEFAULT FALSE,
INSTANCE IN BINARY_INTEGER DEFAULT
ANY_INSTANCE,
FORCE IN BOOLEAN DEFAULT FALSE);
DBMS_JOB.RUN(
JOB IN BINARY_INTEGER,
FORCE IN BOOLEAN DEFAULT FALSE);
DBMS_LOBFunctions:
OPEN – open specified LOB
READ – read values from LOB
WRITE – write to LOB
GETLENGTH – get current size of the LOB
CLOSE – close LOB
CREATETEMPORARY – create temporary LOB
FREETEMPORARY – release temporary LOB
SUBSTR – return part of the LOB
DBMS_MVIEW• Package for managing materialized views
• Main functions:
• REFRESH – refreshes single materialized view
• REFRESH_ALL_MVIEWS
DBMS_OUTPUT• Writes output that can be viewed on the console
• Useful for debugging PL/SQL code
• Main functions:
• PUT_LINE – write one line of text to console
• NEWLINE – write end of line character
• PUT – write text without end of line character
• Note: there are limits on the size of the output buffer. Large texts may be truncated
DBMS_RANDOM• Package for generating random numbers
• Main functions:
• INITIALIZE
• TERMINATE
• RANDOM – generate random number
• Possible uses:SELECT * FROM tab1 ORDER BY
DBMS_RANDOM.RANDOM;
UTL_FILE• Functions for accessing files from the database
• Special privileges are required to access files
• Functions are similar to C stdio library:
• FOPEN
• FCLOSE
• PUT
• PUT_RAW
• GET_LINE
• GET_RAW