Professional Documents
Culture Documents
Steven Feuerstein
PL/SQL Evangelist, Quest Software
steven.feuerstein@quest.com
www.oracleplsqlprogramming.com
Copyright 2000-2006 Steven Feuerstein - Page 1 www.quest.com
Ten Years Writing Ten Books
on the Oracle PL/SQL Language
Important principles...
Assume everything will change.
Aim for a single point of definition (a SPOD).
Top four tips....
Drink lots of water.
Write tiny chunks of code.
Stop writing so much SQL.
Stop guessing and start testing.
Copyright 2000-2006 Steven Feuerstein - Page 5
Drink lots of water!
– Test your code using unit testing tools. Quest Code Tester
– Packaged variables
– DBMS_SESSION programs: free_unused_user_memory,
reset_package and modify_package_state
Copyright 2000-2006 Steven Feuerstein - Page 11
> Unit test PL/SQL programs or....
Six
Simple
Steps
to
Unit Testing
Happiness
Embarrassing
Expensive
Deadly
betwnstr.sf
betwnstr.tst
Copyright 2000-2006 Steven Feuerstein - Page 24
Problems with Typical Testing
betwnstr... IS
RETURN VARCHAR2 DETERMINISTIC
Finally! BEGIN
RETURN ( SUBSTR (
string_in
, start_in
, end_in - start_in + 1 )
);
betwnstr1.sf
END;
Copyright 2000-2006 Steven Feuerstein - Page 34
Test, debug, fix, test, debug, fix, test, debug...
DECLARE
Declare a TYPE employees_aat IS TABLE OF employees%ROWTYPE
collection of INDEX BY BINARY_INTEGER;
records to hold
l_employees employees_aat;
the queried data.
BEGIN
SELECT *
BULK COLLECT INTO l_employees
Use BULK FROM employees;
COLLECT to
retrieve all rows. FOR indx IN 1 .. l_employees.COUNT
LOOP
process_employee (l_employees(indx));
Iterate through the END LOOP;
collection END;
contents with a
loop.
bulkcoll.sql
Copyright 2000-2006 Steven Feuerstein - Page 43
Limit the number of rows returned by
BULK COLLECT
CREATE OR REPLACE PROCEDURE bulk_with_limit
(deptno_in IN dept.deptno%TYPE)
IS Use the LIMIT clause with the
CURSOR emps_in_dept_cur IS INTO to manage the amount
SELECT *
FROM emp
of memory used with the
WHERE deptno = deptno_in; BULK COLLECT operation.
multirows.sql
employees_cp.pkb
Dependent
Existing
Data structure programs Re-compile
code base
changes marked invalid code
valid
invalid
CLOSE company_pkg.allrows;
Fetching into a record
means writing
rec company_pkg.allrows%ROWTYPE; less code.
r BEGIN
I OPEN company_pkg.allrows;
FETCH company_pkg.allrows INTO rec;
g If the cursor select list
changes, it doesn't
h IF rec.name = ‘ACME’ THEN ...
necessarily affect your
t CLOSE company_pkg.allrows;
code.
Deliberate
– The code architecture itself deliberately relies on an
exception. Example: UTL_FILE.GET_LINE exec_ddl_from_file.sql
Unfortunate get_nextline.sf
Unexpected
– A "hard" error that indicates a problem within the
application. Example: Primary key lookup raises
TOO_MANY ROWS fullname.pkb
Copyright 2000-2006 Steven Feuerstein - Page 68
PL/SQL error management features
Defining exceptions
Raising exceptions
Handing exceptions
Exceptions and DML
RAISE_APPLICATION_ERROR
(num binary_integer, msg varchar2,
keeperrorstack boolean default FALSE);
valerr.pkg
Copyright 2000-2006 Steven Feuerstein - Page 74 valerr2.pkg
Handling Exceptions
afterservererror.sql
Copyright 2000-2006 Steven Feuerstein - Page 78
Exceptions and DML
qd_error.erd
Copyright 2000-2006 Steven Feuerstein - Page 83
qd_runtime.pkb
Hard to avoid code repetition in handlers
PACKAGE errpkg
IS
PROCEDURE raise (err_in IN PLS_INTEGER);
Generic Raises PROCEDURE raise (err_in in VARCHAR2);
PROCEDURE record_and_stop (
err_in IN PLS_INTEGER := SQLCODE
,msg_in IN VARCHAR2 := NULL);
Record
and Stop
PROCEDURE record_and_continue (
err_in IN PLS_INTEGER := SQLCODE
,msg_in IN VARCHAR2 := NULL);
Record
and Continue END errpkg;
errpkg.pkg
Copyright 2000-2006 Steven Feuerstein - Page 85
Invoking standard handlers
EXCEPTION
WHEN NO_DATA_FOUND
THEN
errpkg.record_and_continue (
SQLCODE,
' No company for id ' || TO_CHAR (v_id));
WHEN OTHERS
THEN The developer simply
errpkg.record_and_stop; describes
END; the desired action.
Copyright 2000-2006 Steven Feuerstein - Page 86
Specifying the error
PACKAGE errnums
IS Give your
en_general_error CONSTANT NUMBER := -20000;
exc_general_error EXCEPTION; error numbers
PRAGMA EXCEPTION_INIT
(exc_general_error, -20000); names and
en_must_be_18 CONSTANT NUMBER := -20001; associate
exc_must_be_18 EXCEPTION;
PRAGMA EXCEPTION_INIT them with
(exc_must_be_18, -20001);
named
en_sal_too_low CONSTANT NUMBER := -20002;
exc_sal_too_low EXCEPTION; exceptions.
PRAGMA EXCEPTION_INIT
(exc_sal_too_low , -20002);
But don't write this
max_error_used CONSTANT NUMBER := -20002; code manually!
BEGIN
IF employee_rp.is_to_young (:new.hire_date)
THEN
RAISE_APPLICATION_ERROR (
-20175, 'You must be at least 18 years old!');
END IF;
BEGIN
IF employee_rp.is_to_young (:new.hire_date)
THEN
qd_runtime.raise_error (
'EMPLOYEE-TOO-YOUNG'
, name1_in => 'LAST_NAME'
, value1_in => :new.last_name);
END IF;
END labels
– For program units, loops, nested blocks end_labels.sql
SUBTYPEs
– Create application-specific datatypes!
plsql_limits.pks
explimpl.pkg
Named notation
– Sometimes the extra typing is worth it! namednot.sql
BEGIN /* main */
The code is now FOR emp_rec IN emps_in_dept_cur
easier to read and LOOP
IF analysis.caseload (emp_rec.emp_id) <
maintain analysis.avg_cases (department_in)
THEN
You can more assign_next_open_case (emp_rec.emp_id, case#);
schedule_case
easily identify (case#, next_appointment (case#));
areas for END IF;
END LOOP
improvement END assign_workload;
Check out my series
locmod.sp on the OverloadCheck
Copyright 2000-2006 Steven Feuerstein - Page 101 10g_indices_of.sql utility on OTN
Challenges of local modules
Requires discipline.
– Always be on the lookout for opportunities to refactor.
Need to read from the bottom, up.
– Takes some getting used to.
Your IDE needs to reveal the internal
structure of the program.
Sometimes can feel like a "wild goose
chase".
– Where is the darned thing actually implemented?
Copyright 2000-2006 Steven Feuerstein - Page 102
Acknowledgements and Resources