77
Oracle Database Administration Lecture 5 Triggers PL/SQL – advanced

Oracle Database Administration

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

Oracle Database Administration

Lecture 5

Triggers

PL/SQL – advanced

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

Other packages• UTL_HTTP

• Functions for accessing HTTP servers (also using SSL)

• UTL_SMTP

• Functions for sending email from PL/SQL (low level package)

• UTL_TCP

• Functions for connecting to servers using TCP/IP