Date post: | 27-Jan-2015 |
Category: |
Data & Analytics |
Upload: | scott-wesley |
View: | 112 times |
Download: | 5 times |
SAGE Computing ServicesCustomised Oracle Training Workshops and
Consulting
Creative Conditional Compilation … and “raising the bar” with your PL/SQL
Scott WesleySystems Consultant
The example everybody’s seen…FUNCTION qty_booked(p_resource IN VARCHAR2 ,p_date IN DATE) RETURN NUMBER$IF dbms_db_version.ver_le_10 $THEN $ELSE RESULT_CACHE $END IS li_total PLS_INTEGER := 0;BEGIN SELECT SUM(b.qty) INTO li_total FROM bookings b, events e WHERE p_date BETWEEN e.start_date AND e.end_date AND b.resource = p_resource; RETURN li_totalEND qty_booked;
The example everybody’s seen…FUNCTION qty_booked(p_resource IN VARCHAR2FUNCTION qty_booked(p_resource IN VARCHAR2 ,p_date IN DATE) ,p_date IN DATE) RETURN NUMBERRETURN NUMBER$IF dbms_db_version.ver_le_10 $THEN $ELSE RESULT_CACHE $END ISIS li_total PLS_INTEGER := 0;li_total PLS_INTEGER := 0;BEGINBEGIN SELECT SUM(b.qty)SELECT SUM(b.qty) INTO li_totalINTO li_total FROM bookings b, events eFROM bookings b, events e WHERE p_date BETWEEN e.start_date AND e.end_dateWHERE p_date BETWEEN e.start_date AND e.end_date AND b.resource = p_resource;AND b.resource = p_resource; RETURN li_totalRETURN li_totalEND qty_booked;END qty_booked;
• PL/SQL User’s Guide & Reference10g Release 2– Fundamentals of the PL/SQL Language
• Conditional Compilation
Availability
• 11g• 10g Release 2
– Enabled out of the box
• 10.1.0.4 – Once patched, enabled by default– Disable using “_parameter”
• 9.2.0.6 – Once patched, disabled by default– Enable using “_parameter”
Catch 22
INDICES OF
Catch 22
ConditionalCompilation
Patch
INDICES OF
Facilitates removal of unnecessary code at compile time
PerformanceReadability
Accuracy Testing
It's cool!
Selection Directives
$IF boolean_static_expression $THEN text
[ $ELSIF boolean_static_expression $THEN text ]
[ $ELSE text ]
$ENDInquiry Directives
DBMS_OUTPUT.PUT_LINE($$PLSQL_LINE);
ALTER SESSION SET PLSQL_CCFLAGS='max_sentence:100';IF sentence > $$max_sentence THEN
Error Directives
$IF $$PLSQL_OPTIMIZE_LEVEL != 2
$THEN
$ERROR 'intensive_program must be compiled with maximum optimisation'
$END
$END
Semantics
First demo
cc1acc1bcc1ccc1d
Inquiry Directives
<< anon >>BEGIN DBMS_OUTPUT.PUT_LINE('Unit:'||$$PLSQL_UNIT); DBMS_OUTPUT.PUT_LINE('Line:'||$$PLSQL_LINE);END anon;/
Unit: Line:4
> CREATE OR REPLACE PROCEDURE sw_test ISBEGIN DBMS_OUTPUT.PUT_LINE('Unit:'||$$PLSQL_UNIT); DBMS_OUTPUT.PUT_LINE('Line:'||$$PLSQL_LINE);END sw_test;/
Procedure created.
> exec sw_test
Unit:SW_TESTLine:4
ALTER SESSION SET PLSQL_CCFLAGS = 'max_sentence:100';
Session altered.
> BEGIN
IF p_sentence > $$max_sentence THEN
DBMS_OUTPUT.PUT_LINE('Parole Available');
ELSE
DBMS_OUTPUT.PUT_LINE('Life');
END IF;
END;
/
Life
ALTER SYSTEM SET PLSQL_CCFLAGS =
'VARCHAR2_SIZE:100, DEF_APP_ERR:-20001';
DECLARE
lc_variable_chr VARCHAR2($$VARCHAR2_SIZE);
e_def_app_err EXCEPTION;
PRAGMA EXCEPTION_INIT (e_def_app_err, $$DEF_APP_ERR);
BEGIN
--> rest of your code
END anon;
/
Reuse Settings
ALTER SESSION SET PLSQL_CCFLAGS = 'MY_PI:314';
CREATE OR REPLACE PROCEDURE universe_alpha IS
BEGIN
DBMS_OUTPUT.PUT_LINE('Alpha pi = '||$$my_pi/100);
END;
CREATE OR REPLACE PROCEDURE universe_gamma IS
BEGIN
DBMS_OUTPUT.PUT_LINE('Gamma pi = '||$$my_pi/100);
END;
CREATE OR REPLACE PROCEDURE universe_oz IS
BEGIN
DBMS_OUTPUT.PUT_LINE('Oz pi = '||$$my_pi/100);
END;ALTER PROCEDURE universe_alpha COMPILE
PLSQL_CCFLAGS = 'MY_PI:289'
REUSE SETTINGS; ALTER PROCEDURE universe_gamma COMPILE
PLSQL_CCFLAGS = 'MY_PI:423'
REUSE SETTINGS;
> BEGIN
universe_alpha;
universe_gamma;
universe_oz;
END;
/
Alpha pi = 2.89
Gamma pi = 4.23
Oz pi = 3.14
Some actual examples?
Using new version code today
$IF dbms_db_version.ver_le_10 $THEN
-- version 10 and earlier code
$ELSIF dbms_db_version.ver_le_11 $THEN
-- version 11 code
$ELSE
-- version 12 and later code
$END
10.1 vs 10.2 dbms_output
CREATE OR REPLACE PROCEDURE sw_debug (p_text IN VARCHAR2) IS
$IF $$sw_debug_on $THEN l_text VARCHAR2(32767); $ENDBEGIN $IF $$sw_debug_on $THEN -- Let’s provide debugging info $IF dbms_db_version.ver_le_10_1 $THEN -- We have to truncate for <= 10.1 l_text := SUBSTR(p_text, 1 ,200); $ELSE l_text := p_text; $END DBMS_OUTPUT.PUT_LINE(p_text); $ELSE -- No debugging NULL; $ENDEND sw_debug;
CREATE OR REPLACE PROCEDURE sw_debug (p_text IN CREATE OR REPLACE PROCEDURE sw_debug (p_text IN VARCHAR2) ISVARCHAR2) IS
$IF $$sw_debug_on $THEN l_text VARCHAR2(32767); $ENDBEGINBEGIN $IF $$sw_debug_on $THEN -- Let’s provide debugging info $IF dbms_db_version.ver_le_10_1 $THEN -- We have to truncate for <= 10.1 l_text := SUBSTR(p_text, 1 ,200); $ELSE l_text := p_text; $END DBMS_OUTPUT.PUT_LINE(p_text); $ELSE$ELSE -- No debugging-- No debugging NULL;NULL; $END$ENDEND sw_debug;END sw_debug;
CREATE OR REPLACE PROCEDURE sw_debug (p_text IN CREATE OR REPLACE PROCEDURE sw_debug (p_text IN VARCHAR2) ISVARCHAR2) IS
$IF $$sw_debug_on $THEN$IF $$sw_debug_on $THEN l_text VARCHAR2(32767);l_text VARCHAR2(32767); $END$ENDBEGINBEGIN $IF $$sw_debug_on $THEN$IF $$sw_debug_on $THEN -- Let’s provide debugging info-- Let’s provide debugging info $IF dbms_db_version.ver_le_10_1 $THEN -- We have to truncate for <= 10.1 l_text := SUBSTR(p_text, 1 ,255); $ELSE$ELSE l_text := p_text;l_text := p_text; $END$END DBMS_OUTPUT.PUT_LINE(p_text);DBMS_OUTPUT.PUT_LINE(p_text); $ELSE$ELSE -- No debugging-- No debugging NULL;NULL; $END$ENDEND sw_debug;END sw_debug;
10g vs 11g result_cache
FUNCTION quantity_ordered
(p_item_id IN items.item_id%TYPE)
RETURN NUMBER
$IF dbms_version.ver_le_10 $THEN
-- nothing
$ELSE
RESULT_CACHE
$END
IS
BEGIN
...
9i vs 10g Bulk Insert
CREATE OR REPLACE PACKAGE BODY sw_bulk_insert IS PROCEDURE sw_insert (sw_tab IN t_sw_tab) IS BEGIN $IF dbms_db_version.ver_le_9 $THEN DECLARE l_dense t_sw_tab; ln_index PLS_INTEGER := sw_tab.FIRST; BEGIN << dense_loop >> WHILE (l_index IS NOT NULL) LOOP l_dense(l_dense.COUNT + 1) := sw_tab(l_index); l_index := sw_tab.NEXT(l_index); END LOOP dense_loop;
FORALL i IN 1..l_dense.COUNT INSERT INTO sw_table VALUES l_dense(i); END; $ELSE FORALL i IN INDICES OF sw_tab INSERT INTO sw_table VALUES sw_tab(i); $END END sw_insert;END sw_bulk_insert;
CREATE OR REPLACE PACKAGE BODY sw_bulk_insert IS PROCEDURE sw_insert (sw_tab IN t_sw_tab) IS BEGIN $IF dbms_db_version.ver_le_9 $THEN DECLARE l_dense t_sw_tab; ln_index PLS_INTEGER := sw_tab.FIRST; BEGIN << dense_loop >> WHILE (l_index IS NOT NULL) LOOP l_dense(l_dense.COUNT + 1) := sw_tab(l_index); l_index := sw_tab.NEXT(l_index); END LOOP dense_loop;
FORALL i IN 1..l_dense.COUNT INSERT INTO sw_table VALUES l_dense(i); END; $ELSE$ELSE FORALL i IN INDICES OF sw_tabFORALL i IN INDICES OF sw_tab INSERT INTO sw_tableINSERT INTO sw_table VALUES sw_tab(i);VALUES sw_tab(i); $END$END END sw_insert;END sw_bulk_insert;
CREATE OR REPLACE PACKAGE BODY sw_bulk_insert IS PROCEDURE sw_insert (sw_tab IN t_sw_tab) IS BEGIN $IF dbms_db_version.ver_le_9 $THEN$IF dbms_db_version.ver_le_9 $THEN DECLAREDECLARE l_dense t_sw_tab;l_dense t_sw_tab; ln_index PLS_INTEGER := sw_tab.FIRST;ln_index PLS_INTEGER := sw_tab.FIRST; BEGINBEGIN << dense_loop >><< dense_loop >> WHILE (l_index IS NOT NULL) LOOPWHILE (l_index IS NOT NULL) LOOP l_dense(l_dense.COUNT + 1) := sw_tab(l_index);l_dense(l_dense.COUNT + 1) := sw_tab(l_index); l_index := sw_tab.NEXT(l_index);l_index := sw_tab.NEXT(l_index); END LOOP dense_loop;END LOOP dense_loop;
FORALL i IN 1..l_dense.COUNTFORALL i IN 1..l_dense.COUNT INSERT INTO sw_table VALUES l_dense(i);INSERT INTO sw_table VALUES l_dense(i); END;END; $ELSE FORALL i IN INDICES OF sw_tab INSERT INTO sw_table VALUES sw_tab(i); $END END sw_insert;END sw_bulk_insert;
Paradigm Examples
Latent debugging code
CREATE OR REPLACE PACKAGE pkg_debug IS
debug_flag CONSTANT BOOLEAN := FALSE;
END pkg_debug;
/
CREATE OR REPLACE PROCEDURE sw_proc IS
BEGIN
$IF pkg_debug.debug_flag $THEN
dbms_output.put_line ('Debug=T');
$ELSE
dbms_output.put_line ('Debug=F');
$END
END sw_proc;
/
Assertions
“Assertions should be used to document logically impossible situations —
Development tool
Testing Aid
In-line Documentation
if the ‘impossible’ occurs, then something fundamental is clearly wrong.
This is distinct from error handling.”
Run-time Cost
Latent Assertions
“The removal of assertions from production code is almost always done automatically.
It usually is done via conditional compilation.”
$IF $$asserting OR CC_assertion.asserting$THEN IF p_param != c_pi*r*r THEN raise_application_error(.. END IF;$END
-- individual program unit-- entire application
Testing subprograms only in package body
CREATE PACKAGE universe IS
PROCEDURE create_sun;
PROCEDURE create_planets;
-- CC test procedure
PROCEDURE test_orbit;
END universe;
CREATE PACKAGE BODY universe IS
-- Private
PROCEDURE orbit IS .. END;
-- Public
PROCEDURE create_sun IS .. END;
PROCEDURE create_planets IS .. END;
-- Testers
PROCEDURE test_orbit IS
BEGIN
$IF $$testing $THEN
orbit;
$ELSE
RAISE program_error;
$END
END test_orbit;
END universe;
CREATE PACKAGE universe IS
PROCEDURE create_sun;
PROCEDURE create_planets;
-- CC test sequence
PROCEDURE test_run;
END universe;
CREATE PACKAGE BODY universe IS
-- Private
PROCEDURE orbit IS .. END;
-- Public
PROCEDURE create_sun IS .. END;
PROCEDURE create_planets IS .. END;
-- Test sequence
PROCEDURE test_run IS
BEGIN
$IF $$testing $THEN
create_sun;
create_planets;
orbit;
$ELSE
RAISE program_error;
$END
END test_run;
END universe;
Mock objects
FUNCTION get_emp(p_emp_id IN emp.emp_id%TYPE)
RETURN t_emp IS
l_emp t_emp;
BEGIN
$IF $$mock_emp $THEN
l_emp.emp_name := 'Scott';
..
RETURN l_emp;
$ELSE
SELECT *
FROM emp
INTO l_emp
WHERE emp_id = p_emp_id;
RETURN l_emp;
$END
END get_emp;
Comparing competing implementationsduring prototyping
PROCEDURE xyz IS
$IF $$alternative = 1 $THEN
-- varray declaration
$$ELSIF $$alternative = 2 $THEN
-- nested table declaration
$END
BEGIN
$IF $$alternative = 1 $THEN
-- simple varray solution
$$ELSIF $$alternative = 2 $THEN
-- elegant nested table solution
$END
END xyz;
PROCEDURE xyz ISPROCEDURE xyz IS
$IF $$alternative = 1 $THEN$IF $$alternative = 1 $THEN
-- varray declaration -- varray declaration
$$ELSIF $$alternative = 2 $THEN$$ELSIF $$alternative = 2 $THEN
-- nested table declaration-- nested table declaration
$END$END
BEGINBEGIN
$IF $$alternative = 1 $THEN$IF $$alternative = 1 $THEN
-- simple varray solution -- simple varray solution
$$ELSIF $$alternative = 2 $THEN$$ELSIF $$alternative = 2 $THEN
-- elegant nested table solution-- elegant nested table solution
$END$END
END xyz;END xyz;
$IF $$alternative = 1 $THEN
-- first verbose solution that came to mind
$$ELSIF $$alternative = 2 $THEN
-- some crazy idea you came up with at 3am you need to try out
$END
Component Based Installation
PACKAGE BODY core ISPROCEDURE execute_component(p_choice IN VARCHAR2) ISBEGIN CASE p_choice -- Base is always installed. WHEN 'base' THEN base.main(); $IF CC_licence.cheap_installed $THEN WHEN 'cheap' THEN cheap.main(); $END ... $IF CC_licence.pricey_installed $THEN WHEN 'pricey' THEN pricey.main(); $END END CASE;EXCEPTION WHEN case_not_found THEN dbms_output.put_line('Component '||p_choice||' is
not installed.');END execute_component;END core;
Get It Right with the Error Directive
$IF $$PLSQL_OPTIMIZE_LEVEL != 2
$THEN
$ERROR 'intensive_program must be compiled with maximum optimisation'
$END
$END
BEGIN
...
/*
*
* Note to self: Must remember to finish this bit
*
*/
...
END;
BEGIN
...
-- Jacko: this doesn’t work, fix before moving to prod!
...
END;
1 CREATE PROCEDURE process_court_outcome IS
2 BEGIN
3 IF lr_victim.age > 18 THEN
4 send_to_prison(lr_victim);
5 ELSE
6 $ERROR
7 'Waiting for business to advise '||
8 $$PLSQL_UNIT||' line: '||$$PLSQL_LINE
9 $END
10 END IF;
11 END process_court_outcome;
12 /
Warning: Procedure created with compilation errors.
SQL> sho err
LINE/COL ERROR
-------- ----------------------------------------
6/6 PLS-00179: $ERROR: Waiting for business to
advise PROCESS_COURT_OUTCOME line: 8
Post-processed Source
Demo: cc2
Smaller, Faster Run-Time Code Is in Your Future
Good Practices
Inquiry directives have null values for normal behaviour
$IF $$cc_flag = 'x' $THEN cc_code;$END
Choose restriction type carefully
$IF $$debug_onOR module_y.cc_debug $THEN sw_debug('Error'); $END
but there’s always this...
SQL> define debug=/*
begin
&debug
select 'conditionally'
from dual;
-- */
select 'always'
from dual;
end;
/
SAGE Computing ServicesCustomised Oracle Training Workshops and
Consulting
Questions and Answers?
Presentations are available from our website:http://www.sagecomputing.com.au
[email protected]@sagecomputing.com.auhttp://triangle-circle-square.blogspot.com