END - MiNI PW

advertisement
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 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 PACKAGE command
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 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 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
dept_id
dept_name
dept_loc
);
END;
IS RECORD (
dept.deptno%TYPE,
VARCHAR2(14) DEFAULT ‘ABC’,
VARCHAR2(13)
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 ...
Opening cursor
DECLARE
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 cursor
CREATE 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 tables
DECLARE
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 VARRAYs
DECLARE
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 SQL
CREATE 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 tables
DECLARE
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 SQL
CREATE 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_JOB
DBMS_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_LOB
Functions:
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
Download