You are on page 1of 84

Managing Code in the Database

Data Dictionary Views for PL/SQL Programmers When you CREATE OR REPLACE a PL/SQL program, the source code for that program is stored in the database itself and exposed through a wide range of data dictionary views. - USER_DEPENDENCIES The dependencies to and from objects you own. This view is mostly used by Oracle to mark objects INVALID when necessary - USER_ERRORS The current set of errors for all stored objects you own. This view is accessed by the SHOW ERRORS SQL*Plus command - USER_OBJECTS The objects you own. You can, for instance, use this view to see if an object is marked INVALID - USER_OBJECT_SIZE The size of the objects you own. Actually, this view will show you the source, parsed, and compile sizes for your code. Use it to identify the large programs in your environment, good candidates for pinning into the SGA.

- USER_PLSQL_OBJECT_SETTINGS (10g Release 1) Information about the characteristics of a PL/SQL object that can be modified through the ALTER and SET DDL commands, such as the optimization level, debug settings, and more. - USER_PROCEDURES Information about stored programs - USER_SOURCE The text source code for all objects you own (procedures, functions, packages and triggers) - USER_TRIGGERS and USER_TRIG_COLUMNS The database triggers you own, and any columns identified with the triggers.

- USER_ARGUMENTS (similar to dbms_describe API) The arguments (parameters) in all the procedures and functions in your schema.

Debugging PLSQL code DBMS_TRACE

Tracing PL/SQL: Steps


1 2 3

Enable specific program units for trace data collection.

Use dbms_trace. set_plsql_trace to identify a trace level.

Start tracing by running your PL/SQL code.

Read and interpret the trace information.

Use dbms_trace. clear_plsql_trace to stop tracing data.

Debugging PLSQL Code

The DBMS_TRACE built-in package provides programs to start and stop PL/SQL tracing in a session.
When tracing is turned on, the engine collects data as the program executes. The data is then written out to the Oracle Server trace file. Installing DBMS_TRACE To determine whether DBMS_TRACE is present, connect to SYS (or another account with SYSDBA privileges) and execute this command: BEGIN DBMS_TRACE.CLEAR_PLSQL_TRACE; END;

If error is listed then run following scripts $ORACLE_HOME/rdbms/admin/dbmspbt.sql $ORACLE_HOME/rdbms/admin/prvtpbt.plb

DBMS_TRACE programs The following programs are available in the DBMS_TRACE package: SET_PLSQL_TRACE Starts PL/SQL tracing in the current session
CLEAR_PLSQL_TRACE Stops the dumping of trace data for that session PLSQL_TRACE_VERSION Gets the major and minor version numbers of the DBMS_TRACE package

To trace execution of your PL/SQL code, you must first start the trace with a call to:
DBMS_TRACE.SET_PLSQL_TRACE (trace_level INTEGER); where trace_level is one of the following values: Constants that determine which elements of your PL/SQL program will be traced: DBMS_TRACE.trace_all_calls constant INTEGER := 1; DBMS_TRACE.trace_enabled_calls constant INTEGER := 2; DBMS_TRACE.trace_all_exceptions constant INTEGER := 4; DBMS_TRACE.trace_enabled_exceptions constant INTEGER := 8; DBMS_TRACE.trace_all_sql constant INTEGER := 32; DBMS_TRACE.trace_enabled_sql constant INTEGER := 64; DBMS_TRACE.trace_all_lines constant INTEGER := 128; DBMS_TRACE.trace_enabled_lines constant INTEGER := 256; Constants that control the tracing process: DBMS_TRACE.trace_stop constant INTEGER := 16384; DBMS_TRACE.trace_pause constant INTEGER := 4096; DBMS_TRACE.trace_resume constant INTEGER := 8192; DBMS_TRACE.trace_limit constant INTEGER := 16;

To turn on tracing from all programs executed in your session, issue this call:
DBMS_TRACE.SET_PLSQL_TRACE (DBMS_TRACE.trace_all_calls);

To turn on tracing for all exceptions raised during the session, issue this call:
DBMS_TRACE.SET_PLSQL_TRACE(DBMS_TRACE.trace_all_exceptions);

You then run your code. When you are done, you stop the trace session by calling:
DBMS_TRACE.CLEAR_PLSQL_TRACE;

You can then examine the contents of the trace file. The names of these files are generated by Oracle; you will usually look at the modification dates to figure out which file to examine. Note that you cannot use PL/SQL tracing with the shared server

The trace files produced by DBMS_TRACE can get really big. You can focus the output by enabling only specific programs for trace data collection. Note that you cannot use this approach with remote procedure calls. To enable a specific program for tracing, you can alter the session to enable any programs that are created or replaced in the session. To take this approach, issue this command:
ALTER SESSION SET PLSQL_DEBUG=TRUE;

If you dont want to alter your entire session, you can recompile a specific program unit in debug mode as follows (not applicable to anonymous blocks):
ALTER [PROCEDURE | FUNCTION | PACKAGE BODY] program_name COMPILE DEBUG;

After you have enabled the programs in which youre interested, the following call will initiate tracing just for those program units:
DBMS_TRACE.SET_PLSQL_TRACE(DBMS_TRACE.trace_enabled_calls);

You can also restrict the trace information to only those exceptions raised within enabled programs with this call:
DBMS_TRACE.SET_PLSQL_TRACE (DBMS_TRACE.trace_enabled_exceptions);

If you request tracing for all programs or exceptions and also request tracing only for enabled programs or exceptions, the request for all takes precedence.

Pausing and resuming the trace process

The SET_PLSQL_TRACE procedure can do more than just determine which information will be traced. You can also request that the tracing process be paused and resumed. The following statement, for example, requests that no information be gathered until tracing is resumed:
DBMS_TRACE.SET_PLSQL_TRACE(DBMS_TRACE.trace_pause);

DBMS_TRACE will write a record to the trace file to show when tracing was paused and/or resumed. Use the DBMS_TRACE.trace_limit constant to request that only the last 8,192 trace events of a run be preserved. This approach helps ensure that you can turn tracing on without overwhelming the database with trace activity. When the trace session ends, only the last 8,192 records are saved.

Example - 1 grant all on plsql_trace_events to public; --using sys account DELETE FROM SYS.PLSQL_TRACE_EVENTS; BEGIN DBMS_TRACE.CLEAR_PLSQL_TRACE; END; / commit; ALTER SESSION SET PLSQL_DEBUG=TRUE;

create or replace procedure disp_emp( p_empno in emp.empno%type) is v_ename emp.ename%type; begin select ename into v_ename from emp where empno=p_empno; dbms_output.put_line('Name:'||v_ename); exception when no_data_found then dbms_output.put_line(SQLERRM); end; /

ALTER PROCEDURE disp_emp COMPILE DEBUG; EXEC DBMS_TRACE.SET_PLSQL_TRACE (DBMS_TRACE.trace_all_lines); exec disp_emp(7566); exec disp_emp(9999); EXEC DBMS_TRACE.CLEAR_PLSQL_TRACE; SELECT TO_CHAR(EVENT_TIME,'DD MON YYYY HH24:MI:SS')|| '|' || EVENT_UNIT_OWNER||'.'||EVENT_UNIT||'('||EVENT_LINE||')-'|| EVENT_COMMENT||'|'||CALLSTACK||'|'||ERRORSTACK PLSQL_EXECUTION FROM SYS.PLSQL_TRACE_EVENTS; -- Output in next slide

SYS.DBMS_TRACE(21)-New line executed|| SYS.DBMS_TRACE(75)-New line executed|| SYS.DBMS_TRACE(76)-New line executed|| SYS.DBMS_TRACE(81)-New line executed|| .<anonymous>(1)-New line executed|| .()-PL/SQL Virtual Machine stopped|| .<anonymous>(0)-PL/SQL Virtual Machine started|| .<anonymous>(1)-New line executed|| .<anonymous>(1)-New line executed|| SCOTT.DISP_EMP(1)-New line executed|| SCOTT.DISP_EMP(5)-New line executed|| SCOTT.DISP_EMP(6)-New line executed|| SCOTT.DISP_EMP(7)-New line executed|| SYS.DBMS_OUTPUT(111)-New line executed|| SYS.DBMS_OUTPUT(115)-New line executed|| SCOTT.DISP_EMP(11)-New line executed|| .<anonymous>(1)-New line executed|| .()-PL/SQL Virtual Machine stopped|| .<anonymous>(0)-PL/SQL Virtual Machine started|| .<anonymous>(1)-New line executed|| .<anonymous>(1)-New line executed|| SCOTT.DISP_EMP(1)-New line executed|| SCOTT.DISP_EMP(5)-New line executed|| SCOTT.DISP_EMP(6)-New line executed|| SCOTT.DISP_EMP(8)-New line executed|| SCOTT.DISP_EMP(9)-New line executed|| SCOTT.DISP_EMP(10)-New line executed|| SYS.DBMS_OUTPUT(111)-New line executed|| SYS.DBMS_OUTPUT(115)-New line executed|| SCOTT.DISP_EMP(11)-New line executed|| .<anonymous>(1)-New line executed|| .()-PL/SQL Virtual Machine stopped|| .<anonymous>(0)-PL/SQL Virtual Machine started|| .<anonymous>(1)-New line executed|| .<anonymous>(1)-New line executed|| SYS.DBMS_TRACE(94)-New line executed|| SYS.DBMS_TRACE(72)-New line executed|| SYS.DBMS_TRACE(66)-New line executed|| SYS.DBMS_TRACE(12)-New line executed|| SYS.DBMS_TRACE(66)-New line executed|| SYS.DBMS_TRACE(67)-New line executed|| SYS.DBMS_TRACE(72)-New line executed|| SYS.DBMS_TRACE(75)-New line executed|| SYS.DBMS_TRACE(21)-New line executed|| .()-PL/SQL trace stopped|| .()-PL/SQL Trace Tool started|| .()-Trace flags changed|----- PL/SQL Call Stack -----

Example - 2 DELETE SYS.PLSQL_TRACE_EVENTS; ALTER SESSION SET PLSQL_DEBUG=TRUE; create or replace procedure test_PAR is begin FOR I IN 1..3 LOOP DBMS_OUTPUT.PUT_LINE( 'THIS IS '||I ); END LOOP; end; / ALTER PROCEDURE TEST_PAR COMPILE DEBUG; EXEC DBMS_TRACE.SET_PLSQL_TRACE (DBMS_TRACE.trace_enabled_lines ); EXEC TEST_PAR; EXEC DBMS_TRACE.CLEAR_PLSQL_TRACE;

SELECT TO_CHAR(EVENT_TIME,'DD MON YYYY HH24:MI:SS')|| '|' || EVENT_UNIT_OWNER||'.'||EVENT_UNIT||'('||EVENT_LINE||')-'|| EVENT_COMMENT||'|'||CALLSTACK||'|'||ERRORSTACK PLSQL_EXECUTION FROM SYS.PLSQL_TRACE_EVENTS;
SYS.TEST_PAR(1)-New line SYS.TEST_PAR(2)-New line SYS.TEST_PAR(3)-New line SYS.TEST_PAR(4)-New line .()-Some NODEBUG events SYS.TEST_PAR(3)-New line SYS.TEST_PAR(4)-New line .()-Some NODEBUG events SYS.TEST_PAR(3)-New line SYS.TEST_PAR(4)-New line .()-Some NODEBUG events SYS.TEST_PAR(3)-New line SYS.TEST_PAR(5)-New line SYS.TEST_PAR(6)-New line executed|| executed|| executed|| executed|| skipped|| executed|| executed|| skipped|| executed|| executed|| skipped|| executed|| executed|| executed||

PL/SQL profiler

plsql_optimize_level

Oracle has provided a parameter to control how much optimization we wish our PL/SQL modules to undergo.
There are three levels of optimization, as follows.

level 0: no optimization (simulates previous releases of Oracle);


level 1: minor optimization (some code re-write, removing unnecessary assignments or computations); level 2: default optimization (major code re-write and relocation during compilation for more significant performance improvements). level 2 is the default SQL> show parameter plsql_optimize_level plsql_optimize_level integer 2

Consider an example SQL> set timing on SQL> DECLARE 2 c NUMBER := 1; 3 pi NUMBER := 3.142; 4 r NUMBER := DBMS_RANDOM.RANDOM; 5 BEGIN 6 FOR i IN 1 .. 1000000 LOOP 7 /* Use a random, variable calculation... */ 8 c := pi*(r**2) + (MOD(r,c)*pi+i); 9 END LOOP; 10 END; 11 / PL/SQL procedure successfully completed. Elapsed: 00:00:01.15 In 9i it almost takes twice the time

Let us make code much simpler SQL> DECLARE 2 c NUMBER; 3 BEGIN 4 FOR i IN 1 .. 1000000 LOOP 5 /* Use simple assigning */ 6 c := 1; 7 END LOOP; 8 END; 9 / PL/SQL procedure successfully completed. Elapsed: 00:00:00.04 Note: since less code, less time consumed. If plsql_optimize_level is set to 0 or 1, time consumed would be much more

Let us try the same with optimizer level at 1 SQL> alter session set plsql_optimize_level=1; SQL> DECLARE 2 c NUMBER; 3 BEGIN 4 FOR i IN 1 .. 1000000 LOOP 5 /* Use simple assigning */ 6 c := 1; 7 END LOOP; 8 END; 9 /

PL/SQL procedure successfully completed.


Elapsed: 00:00:00.09 Note: Much delayed. Same would happen with previous example

pl/sql optimization and cursor-for-loops

set timing on;


alter session set timed_statistics=true; alter session set sql_trace=true;

alter session set plsql_optimize_level=0;


DECLARE i PLS_INTEGER := 0; BEGIN FOR r IN ( SELECT * FROM dba_source level_0 ) LOOP i := i + 1; END LOOP; END; / Elapsed: 00:00:25.14

alter session set plsql_optimize_level=2; DECLARE i PLS_INTEGER := 0; BEGIN FOR r IN ( SELECT * FROM dba_source level_2 ) LOOP i := i + 1; END LOOP; END; / Elapsed: 00:00:06.21 alter session set timed_statistics=false; alter session set sql_trace=false; select a.spid from v$process a,v$session b where a.addr=b.paddr and b.audsid=userenv('sessionid');

4276

Now generate readable file using tkprof

C:\>tkprof orcl_ora_4276.trc outfile sys=no

The statistics for level 2 demonstrate that Oracle is fetching in batches of 100. The CPU time (largely due to the reduced context switching) has dropped significantly.

Analyzing Performance of PL/SQL Code Before you can tune your application, you need to figure out what is running slowly and where you should focus your efforts. To answer these questions, Oracle offers a number of built-in utilities. Here are the most useful: DBMS_PROFILER This built-in package allows you to turn on execution profiling in a session. Then, when you run your code, Oracle uses tables to keep track of detailed information about how long each line in your code took to execute. You can then run queries on these tables. DBMS_UTILITY.GET_TIME Use this built-in function to calculate the elapsed time of your code down to the hundredth of a second. This make it possible to time exactly how long a certain operation took to run. In 10g, you can also call DBMS_UTILITY.GET_CPU_TIME to calculate elapsed CPU time.

To use dbms_profiler package run PROFTAB.SQL (C:\app\Administrator\product\11.1.0\db_1\RDBMS\ADMIN) file for each user

The above script creates following tables


PLSQL_PROFILER_DATA PLSQL_PROFILER_RUNS PLSQL_PROFILER_UNITS

Run following program:


DECLARE i PLS_INTEGER := 0; BEGIN DBMS_PROFILER.START_PROFILER('OPTIMIZE LEVEL 2'); FOR r IN ( SELECT * FROM dba_source level_2 ) LOOP i := i + 1; END LOOP; DBMS_PROFILER.STOP_PROFILER(); END;

To view the report use following statements set echo on alter session set nls_timestamp_format = 'dd/mm/yyyy hh24:mi:ss.ff3'; alter session set nls_date_format = 'dd/mm/yyyy hh24:mi:ss'; col run_comment format a20 col unit_name format a14 col text format a40 col total_time format 999999999999999999999 select r.run_comment,u.unit_name,d.line#,d.total_occur,d.total_time from plsql_profiler_runs r inner join plsql_profiler_data d on ( r.runid = d.runid ) inner join plsql_profiler_units u on ( d.runid = u.runid and d.unit_number = u.unit_number) where d.total_occur > 0 order by d.runid, u.unit_name, d.line#; RUN_COMMENT UNIT_NAME LINE# TOTAL_OCCUR TOTAL_TIME -------------------------------------------------------------------------------------------------------OPTIMIZE LEVEL 2 <anonymous> 5 5943 1638699088850 OPTIMIZE LEVEL 2 <anonymous> 6 594003 45951865390 OPTIMIZE LEVEL 2 <anonymous> 8 1 1902476

dbms_utility.get_cpu_time (10g feature)

DBMS_UTILITY.GET_TIME and DBMS_UTILITY.GET_CPU_TIME can be useful for determining elapsed time and CPU time (respectively) consumed by a PL/SQL program.
The DBMS_UTILITY.GET_CPU_TIME function returns the CPU time in 100ths of a second. The DBMS_UTILITY.GET_CPU_TIME function was introduced with Oracle 10g Release 1.

DBMS_UTILITY.GET_TIME is available in Oracle9i.


SQL> select count(*) from dba_source; COUNT(*) ---------594003

DECLARE i PLS_INTEGER := 0; elapsed_start number:=dbms_utility.get_time; cpu_start number:=dbms_utility.get_cpu_time; BEGIN FOR r IN ( SELECT * FROM dba_source level_2 ) LOOP i := i + 1; END LOOP; /*how much time did the LOOP take */ dbms_output.put_line(round((dbms_utility.get_time elapsed_start)/100,2)||' elapsed second.'); dbms_output.put_line(round((dbms_utility.get_cpu_time cpu_start)/100,2)||' cpu seconds'); END; / 5.44 elapsed second. 2.01 cpu seconds

Brief recommendations to improve the performance - Use the most aggressive compiler optimization level possible Oracle Database 10g Release 1 introduced an optimizing compiler for PL/SQL programs. The default optimization level of 2 takes the most aggressive approach possible in terms of transforming your code to make it run faster. - Use BULK COLLECT when querying multiple rows The BULK COLLECT statement retrieves multiple rows of data through either an implicit or an explicit query with a single round trip to and from the database. BULK COLLECT reduces the number of context switches between the PL/SQL and SQL engines and thereby reduces the overhead of retrieving data. - Use FORALL when modifying multiple rows As with BULK COLLECT, FORALL greatly reduces context switching between the PL/SQL and SQL engines, but this time for updates, inserts, and deletes. You can expect to see an order of magnitude (or greater) improvement in performance for multiple-row DML execution with FORALL.

- Use the NOCOPY hint when passing large structures The NOCOPY parameter hint requests that the PL/SQL runtime engine pass an IN OUT argument by reference rather than by value. This can speed up the performance of your programs, because by-reference arguments are not copied within the program unit. When you pass large, complex structures like collections, records, or objects, this copy step can be expensive. -Use PLS_INTEGER for intensive integer computations When you declare an integer variable as PLS_INTEGER, it will use less memory than INTEGER and rely on machine arithmetic to get the job done more efficiently especially in a program that requires intensive integer computations. -Group together related programs in a package Whenever you reference any single element in a package for the first time in your session, the entire package is cached in the shared memory pool. Any other calls to programs in the package require no additional disk I/O, thereby improving the performance of calling those programs.

Virtual Private Database

Fine-Grained Access Control: Overview Fine-grained access control enables you to build applications that enforce security rules (or policies) at a low level of granularity. For example, you can use it to restrict customers who access the Oracle server to see only their own account, physicians to see only the records of their own patients, or managers to see only the records of employees who work for them. When you use fine-grained access control, you create security policy functions attached to the table or view on which you based your application. When a user enters a data manipulation language (DML) statement on that object, the Oracle server dynamically modifies the users statementtransparently to the userso that the statement implements the correct access control. Fine-grained access is also known as a virtual private database (VPD), because it implements row-level security, essentially giving users access to their own private database. Fine-grained means at the individual row level.

Virtual Private Database (VPD)


Virtual Private Database is also known as fine grained access control (FGAC). It allows to define which rows users may have access to. The policies can be applied to SELECT, INSERT, UPDATE, DELETE, and INDEX statements. The index affects CREATE INDEX and ALTER INDEX DDL commands. Whenever a user directly or indirectly accesses a protected table, view, or synonym, the Row Level Security (RLS) engine is transparently invoked, the PL/SQL function registered will execute, and the SQL statement will be modified and executed.

How Fine-Grained Access Works To implement a virtual private database so that account managers can see only their own customers, you must do the following: 1. 2. 3. Create a function to add a WHERE clause identifying a selection criterion to a users SQL statement. Have the user (the account manager) enter a SQL statement. Implement the security policy through the function that you created. The Oracle server calls the function automatically. Dynamically modify the users statement through the function. Execute the dynamically modified statement.

4. 5.

Example Already we have EMP & DEPT tables in SCOTT schema. Now let us create department_secrets table and insert sample rows. create table department_secrets ( deptno references dept, secret varchar2(50) ); insert into department_secrets values (10, 'Accounts Secret #1'); insert into department_secrets values (10, 'Accounts Secret #2'); insert into department_secrets values (20, 'Research Secret #1'); insert into department_secrets values (20, 'Research Secret #2'); insert into department_secrets values (30, 'Sales Secret #1'); insert into department_secrets values (30, 'Sales Secret #2');

For any employee, it must be possible to see all secrets of his department, but no secret of another department. In order to make that happen, we need to create a package, a trigger, and set a policy. Creating package --package spec follows create or replace package pkg_vpd as g_deptno dept.deptno%type; procedure set_dep_id(p_deptno emp.deptno%type); function predicate (obj_schema varchar2, obj_name varchar2) return varchar2; end pkg_vpd; /

--package body follows create or replace package body pkg_vpd as procedure set_dep_id(p_deptno emp.deptno%type) is begin g_deptno := p_deptno; end set_dep_id;

function predicate (obj_schema varchar2, obj_name varchar2) return varchar2 is begin return 'deptno = ' || g_deptno; end predicate;
end pkg_vpd; /

Trigger Code:
Grant administer database trigger to public; --privilege required for schemas

create or replace trigger trg_vpd after logon on database declare v_deptno emp.deptno%type; begin select deptno into v_deptno from emp where upper(ename)=upper(user); pkg_vpd.set_dep_id(v_deptno); end; /

Policy Settings
grant execute on dbms_rls to public

begin dbms_rls.add_policy ( object_schema=>user, object_name=>'department_secrets', policy_name=>'department_security', function_schema=>user, policy_function=>'pkg_vpd.predicate', statement_types=>'select,update,delete'); end; /

Using system account create following user account & grant privileges create user allen identified by allen; grant create session to allen;

grant select on scott.department_secrets to allen;


Using allen username

select * from scott.department_secrets; 30 Sales Secret #1 30 Sales Secret #2 Using scott schema
select * from department_secrets; 20 Research Secret #1 20 Research Secret #2

For dropping policy begin dbms_rls.drop_policy ( object_schema=>user, object_name=>'department_secrets', policy_name=>'department_security'); end; /

Conditional Compilation (10gR2)


Conditional compilation allows PL/SQL code to be tailored to specific environments by selectively altering the source code based on compiler directives. using conditional compilation you can customize a PL/SQL application to:
Utilize the latest functionality with the latest database release and disable the new features to run the application against an older release of the database Activate debugging or tracing functionality in the development environment and hide that functionality in the application while it runs at a production site

Compiler flags are identified by the "$$" prefix, while conditional control is provided by the $IF-$THEN-$ELSE syntax. $IF boolean_static_expression $THEN text

[ $ELSIF boolean_static_expression $THEN text ]


[ $ELSE text ] $END As an example, let's assume that all application debugging is performed by calling a procedure called DEBUG.

Conditional compilation can be used to provide an on/off switch for the debug, as well as influencing the debug information that is produced.

Simple Example: create or replace procedure get_ver is begin $IF DBMS_DB_VERSION.VER_LE_10_2 $THEN dbms_output.put_line('Use code <= 10.2'); $ELSIF DBMS_DB_VERSION.VER_LE_11 $THEN dbms_output.put_line('Use code > 10.2'); $END end; / exec get_ver Use code > 10.2

The PRINT_POST_PROCESSED_SOURCE procedure of the DBMS_PREPROCESSOR package displays the post-processed source: BEGIN DBMS_PREPROCESSOR.print_post_processed_source ( object_type => 'PROCEDURE', schema_name => 'KRISHNA', object_name => 'GET_VER'); END; / procedure get_ver is begin dbms_output.put_line('Use code >= 10.2'); end; To know current db version exec dbms_output.put_line(DBMS_DB_VERSION.VERSION); 11

Example CREATE OR REPLACE PROCEDURE Cond_Plsql IS CURSOR c1 IS SELECT * FROM emp; BEGIN $If $$Debug_Flag $Then dbms_Output.Put_Line( 'Now fetching emp record' ); $End OPEN c1; $If $$Debug_Flag $Then dbms_Output.Put_Line( 'Now updating emp salary' ); $End -- Logic TO UPDATE emp Salary CLOSE c1; dbms_Output.Put_Line('Finished updating emp salary'); END; /

exec cond_plsql

Finished updating emp salary


BEGIN DBMS_PREPROCESSOR.print_post_processed_source ( object_type => 'PROCEDURE', schema_name => 'KRISHNA', object_name => 'COND_PLSQL'); END; / PROCEDURE Cond_Plsql IS CURSOR c1 IS SELECT * FROM emp; BEGIN OPEN c1; -- Logic TO UPDATE emp Salary CLOSE c1; dbms_Output.Put_Line('Finished updating emp salary'); END;

alter session set plsql_ccflags = 'debug_flag:true'; alter procedure Cond_Plsql compile; EXEC COND_PLSQL Now fetching emp record Now updating emp salary Finished updating emp salary BEGIN DBMS_PREPROCESSOR.print_post_processed_source ( object_type => 'PROCEDURE', schema_name => 'KRISHNA', object_name => 'COND_PLSQL'); END; /

o/p PROCEDURE Cond_Plsql IS CURSOR c1 IS SELECT * FROM emp; BEGIN dbms_Output.Put_Line( 'Now fetching emp record' ); OPEN c1; dbms_Output.Put_Line( 'Now updating emp salary' ); -- Logic TO UPDATE emp Salary CLOSE c1; dbms_Output.Put_Line('Finished updating emp salary'); END;

PLSQL compiler warning mechanism


As of 10g, Oracle have introduced a series of compiler options which can be listed by querying the [DBA | ALL | USER]_PLSQL_OBJECT_SETTINGS view. PLSQL_WARNINGS Oracle's 10g PL/SQL compiler can now give a series of warnings about any PL/SQL code (subprograms only) being compiled. There are three different "classes" of warnings : INFORMATIONAL - Such as unreachable code, etc. PERFORMANCE - Checking mismatches in datatypes etc. SEVERE - Problems which may cause unexpected behaviour, such as incorrect aliasing etc. You can specify ALL to turn them all on or off. Note: on my system default was DISABLE:ALL

Setting plsql warning flag ALTER SESSION SET PLSQL_WARNINGS='ENABLE:SEVERE', 'DISABLE:PERFORMANCE'; Example: alter session set plsql_warnings='ENABLE:ALL'; show parameter plsql_warnings plsql_warnings string ENABLE:ALL

CREATE OR REPLACE PROCEDURE p IS BEGIN IF 1=2 THEN dbms_output.put_line('Cannot reach this line'); END IF; END p; / SP2-0804: Procedure created with compilation warnings

SQL> show err Errors for PROCEDURE P: 5/5 PLW-06002: Unreachable code

Example
CREATE TABLE t ( a VARCHAR2(10) ); CREATE OR REPLACE PROCEDURE p IS BEGIN INSERT INTO t VALUES ( 10 ); END p; / SP2-0804: Procedure created with compilation warnings SHOW ERR 4/26 PLW-07202: bind type would result in conversion away from column type

Oracle11g Rel 1 Result Cache Concepts

Systems with large amounts of memory can take advantage of the result cache to improve response times of repetitive queries. The result cache stores the results of SQL queries and PL/SQL functions in an area called Result Cache Memory in the shared pool. When these queries and functions are executed repeatedly, the results are retrieved directly from the cache memory. This results in a faster response time. The cached results stored become invalid when data in the dependent database objects is modified.

SQL> show user USER is "SYS" SQL> set serveroutput on SQL> execute dbms_result_cache.memory_report Result Cache Memory Report [Parameters] Block Size = 1K bytes Maximum Cache Size = 2112K bytes (2112 blocks) Maximum Result Size = 105K bytes (105 blocks) [Memory] Total Memory = 5140 bytes [0.003% of the Shared Pool] ... Fixed Memory = 5140 bytes [0.003% of the Shared Pool] ... Dynamic Memory = 0 bytes [0.000% of the Shared Pool] PL/SQL procedure successfully completed.

SQL> col name format a30 SQL> select name, value from v$result_cache_statistics; NAME -----------------------------Block Size (Bytes) Block Count Maximum Block Count Current Result Size Maximum (Blocks) Create Count Success Create Count Failure Find Count Invalidation Count Delete Count Invalid Delete Count Valid VALUE ---------1024 2112 0 105 0 0 0 0 0 0

RESULT_CACHE_MODE The RESULT_CACHE_MODE initialization parameter determines the SQL query result cache behavior. The possible initialization parameter values are MANUAL and FORCE. When set to MANUAL, you must specify, by using the result_cache hint, that a particular result is using the cache. When set to FORCE, all results use the cache, if possible. You can use the no_result_cache hint to bypass the cache when using the FORCE mode.

Query Result Cache

Oracle 11g allows the results of SQL queries to be cached in the SGA and reused to improve performance. Example

CREATE TABLE qrc_tab ( id NUMBER ); INSERT INTO qrc_tab VALUES (1); INSERT INTO qrc_tab VALUES (2); INSERT INTO qrc_tab VALUES (3); INSERT INTO qrc_tab VALUES (4); INSERT INTO qrc_tab VALUES (5);

CREATE OR REPLACE FUNCTION slow_function( p_id IN qrc_tab.id%TYPE) RETURN qrc_tab.id%TYPE DETERMINISTIC AS BEGIN DBMS_LOCK.sleep(1); RETURN p_id; END; / SET TIMING ON The function contains a one second sleep so we can easily detect if it has been executed by checking the elapsed time of the query.

Next, we query the test table using the slow function and check out the elapsed time. Each run takes approximately five seconds, one second sleep for each row queried. SELECT slow_function(id) FROM qrc_tab; o/p 5 rows selected. Elapsed: 00:00:05.15

Adding the RESULT_CACHE hint to the query tells the server to attempt to retrieve the information from the result cache. If the information is not present, it will cache the results of the query provided there is enough room in the result cache. Since we have no cached results, we would expect the first run to take approximately five seconds, but subsequent runs to be much quicker. SELECT /*+ result_cache */ slow_function(id) FROM qrc_tab;

o/p
Elapsed: 00:00:05.20

SELECT /*+ result_cache */ slow_function(id) FROM qrc_tab; o/p Elapsed: 00:00:00.15

The default action of the result cache is controlled by the RESULT_CACHE_MODE parameter. When it is set to MANUAL, the RESULT_CACHE hint must be used for a query to access the result cache.

SHOW PARAMETER RESULT_CACHE_MODE


NAME TYPE VALUE ------------------------------------ ----------- -----------------------------result_cache_mode string MANUAL If we set the RESULT_CACHE_MODE parameter to FORCE, the result cache is used by default, but we can bypass it using the NO_RESULT_CACHE hint. ALTER SESSION SET RESULT_CACHE_MODE=FORCE;

SELECT slow_function(id) FROM qrc_tab; o/p Elapsed: 00:00:00.14 SELECT /*+ no_result_cache */ slow_function(id) FROM qrc_tab; o/p Elapsed: 00:00:05.14

Deterministic Functions
A function is called deterministic if it returns the same result value whenever it is called with the same values for its arguments.
The following function is such a function: create or replace function betwn_str( p_str IN varchar2, start_pos IN integer, end_pos IN integer) RETURN varchar2 DETERMINISTIC IS begin RETURN substr(p_str,start_pos,end_posstart_pos+1); end; / SELECT betwn_str('abcdef',3,5) FROM dual; BETWN_STR('ABCDEF',3,5) ---------------------------------------------------------cde

If deterministic is omitted in the code, function would be reexecuted every time it is invoked.

PL/SQL Function Result Cache

Function Result Cache


PL/SQL function result cache provides a simple way to boost the performance of PL/SQL functions by saving the results of function calls for specific combinations of input parameters in the SGA.
These results can be reused by any session calling the same function with the same parameters.

This can result in a significant performance boost when functions are called for each row in an SQL query, or within a loop in PL/SQL.

Enabling a function to use the function result cache is as simple as adding the RESULT_CACHE clause, and optionally the RELIES_ON clause. CREATE TABLE res_cache_test_tab ( id NUMBER, value NUMBER ); BEGIN FOR i IN 1 .. 10 LOOP INSERT INTO res_cache_test_tab VALUES (i, i*10); END LOOP; COMMIT; END; /

The following function returns the VALUE from the test table for the specified ID. It also includes a call to the DBMS_LOCK.SLEEP procedure to slow down the function. CREATE OR REPLACE FUNCTION get_value (p_in IN NUMBER) RETURN NUMBER RESULT_CACHE AS l_value res_cache_test_tab.value%TYPE; BEGIN SELECT value INTO l_value FROM res_cache_test_tab WHERE id = p_in; DBMS_LOCK.sleep(1); RETURN l_value; END get_value; /

The following procedure tests the performance of the get_value function by making calls to it in two loops and measuring the elapsed time for each run.
CREATE OR REPLACE PROCEDURE run_test AS l_start NUMBER; l_loops NUMBER := 10; l_value res_cache_test_tab.value%TYPE; BEGIN l_start := DBMS_UTILITY.get_time; FOR i IN 1 .. l_loops LOOP l_value := get_value(i); END LOOP; DBMS_OUTPUT.put_line('First Loop: ' || (DBMS_UTILITY.get_time - l_start) || ' hsecs'); l_start := DBMS_UTILITY.get_time; FOR i IN 1 .. l_loops LOOP l_value := get_value(i); END LOOP; DBMS_OUTPUT.put_line('Second Loop: ' || (DBMS_UTILITY.get_time - l_start) || ' hsecs'); END run_test; /

Running the procedure gives the following results. SET SERVEROUTPUT ON EXEC run_test; First Loop: 1003 hsecs Second Loop: 0 hsecs The first loop takes approximately 10 seconds, 1 second per function call, while the second is almost instantaneous. If we run the test code again in a new session we can see that elapsed time remains the same. CONN test/test SET SERVEROUTPUT ON EXEC run_test; First Loop: 0 hsecs Second Loop: 0 hsecs

If we alter the contents of the table, we still get the fast elapsed time, indicating the existing values are still being used. UPDATE res_cache_test_tab SET value = value * 10; COMMIT; EXEC run_test; First Loop: 0 hsecs Second Loop: 0 hsecs This represents a potential data integrity problem. The optional RELIES_ON clause is used to specify dependent tables and views so the result cache can be invalidated if the dependent objects are modified.

CREATE OR REPLACE FUNCTION get_value (p_in IN NUMBER) RETURN NUMBER RESULT_CACHE RELIES_ON (res_cache_test_tab) AS -- same as earlier code After recreating the function, we see a normal first-time run, followed by a run using the cached results. EXEC run_test; First Loop: 1001 hsecs Second Loop: 0 hsecs

Next, we update the base table and perform another run. UPDATE res_cache_test_tab SET value = value * 10; COMMIT; EXEC run_test; First Loop: 1002 hsecs Second Loop: 0 hsecs The elapsed times show the function results cache was invalidated by the table update.

DBMS_RESULT_CACHE The DBMS_RESULT_CACHE package provides a PL/SQL API for result cache management. The STATUS function displays the current status of the result cache. SQL> SELECT DBMS_RESULT_CACHE.status FROM dual;

STATUS ----------------------------------------------------ENABLED

The MEMORY_REPORT procedure displays information about memory usage of the result cache. SQL> EXEC DBMS_RESULT_CACHE.memory_report(detailed => true);
Result Cache Memory Report [Parameters] Block Size = 1K bytes Maximum Cache Size = 1056K bytes (1056 blocks) Maximum Result Size = 52K bytes (52 blocks) [Memory] Total Memory = 97160 bytes [0.048% of the Shared Pool] ... Fixed Memory = 5132 bytes [0.003% of the Shared Pool] ....... Cache Mgr = 108 bytes ....... Memory Mgr = 124 bytes ....... Bloom Fltr = 2K bytes ....... State Objs = 2852 bytes ... Dynamic Memory = 92028 bytes [0.046% of the Shared Pool] ....... Overhead = 59260 bytes ........... Hash Table = 32K bytes (4K buckets) ........... Chunk Ptrs = 12K bytes (3K slots) ........... Chunk Maps = 12K bytes ........... Miscellaneous = 1916 bytes ....... Cache Memory = 32K bytes (32 blocks) ........... Unused Memory = 0 blocks ........... Used Memory = 32 blocks ............... Dependencies = 2 blocks (2 count) ............... Results = 30 blocks ................... PLSQL = 10 blocks (10 count) ................... Invalid = 20 blocks (20 count) PL/SQL procedure successfully completed.

The INVALIDATE procedure invalidates all result-set objects for a specific object, specified using an OWNER and NAME (OBJECT_NAME) or OBJECT_ID from the %_OBJECTS views. SQL> EXEC DBMS_RESULT_CACHE.invalidate('TEST', 'GET_VALUE'); The FLUSH procedure remove all objects from the result cache and optionally release all memory and clear existing cache statistics. SQL> exec DBMS_RESULT_CACHE.flush;