Security Advisory |
The DBMS_PROFILER package provides an interface for profiling PL/SQL application code and identifying performance bottlenecks. It can also be used to collect and persistently store profiler data for use in performing comparisons.
From a security standpoint what needs to be considered is whether this package could be used by attacker to gain insights into weaknesses and potential targets which we believe to be the case.
The fact that a package that is clearly not intended for end-user use has EXECUTE granted to PUBLIC makes it all the more of a concern. |
|
Recommended Security Rules |
NEVER
- Let any user or schema without documented justification or escalated privileges gain access to this package by revoking EXECUTE from PUBLIC
WITH GREAT CARE
- Identify legitimate requirements for access to this package and grant EXECUTE explicitly to only justified schemas
- Query the data dictionary after EXECUTE has been revoked from PUBLIC to verify the equivalence created is the equivalence approved by IT management and your CISO
CAUTIONS
- Some usage may be in the form of dynamic SQL so carefully verify usage requirements in source code as well as in DBA_DEPENDENCIES
|
|
DBMS_PROFILER Package Information |
AUTHID |
CURRENT_USER |
Constants |
Name |
Data Type |
Value |
Return values from DBMS_PROFILER functions |
error_io |
BINARY_INTEGER |
2 |
error_param |
BINARY_INTEGER |
1 |
error_version |
BINARY_INTEGER |
-1 |
major_version |
BINARY_INTEGER |
2 |
minor_version |
BINARY_INTEGER |
0 |
success |
BINARY_INTEGER |
0 |
|
Dependencies |
|
Documented |
Yes |
Exceptions |
Error Code |
Reason |
ORA-06528 |
profiler_error: Parameter or I/O error. If this error is returned ... run proftab.sql for the user? |
ORA-06529 |
version_mismatch: Incorrect profiler version for database |
|
First Available |
8.1.5 |
Security Model |
Owned by SYS with EXECUTE granted to PUBLIC |
Source |
{ORACLE_HOME}/rdbms/admin/dbmspbp.sql |
Subprograms |
|
|
FLUSH_DATA |
Flushes the Profiler buffer to the Profiler tables
Overload 1 |
dbms_profiler.flush_data RETURN BINARY_INTEGER; |
SELECT dbms_profiler.flush_data
FROM dual;
FLUSH_DATA
----------
0 |
Overload 2 |
dbms_profiler.flush_data; |
exec dbms_profiler.flush_data;
PL/SQL procedure successfully completed. |
|
GET_VERSION |
Returns the Profiler API version |
dbms_profiler.get_version(
major OUT BINARY_INTEGER,
minor OUT BINARY_INTEGER); |
set serveroutput on
DECLARE
majver BINARY_INTEGER;
minver BINARY_INTEGER;
BEGIN
dbms_profiler.get_version(majver, minver);
dbms_output.put_line('Major: ' || TO_CHAR(majver));
dbms_output.put_line('Minor: ' || TO_CHAR(minver));
END;
/
Major: 2
Minor: 0
PL/SQL procedure successfully completed. |
|
INTERNAL_VERSION_CHECK |
Returns the Profiler package version for compatibility verification |
dbms_profiler.internal_version_check RETURN BINARY_INTEGER; |
SELECT dbms_profiler.internal_version_check
FROM dual;
INTERNAL_VERSION_CHECK
----------------------
0 |
|
PAUSE_PROFILER |
Pauses Profiler data collection
Overload 1 |
dbms_profiler.pause_profiler RETURN BINARY_INTEGER; |
DECLARE
i PLS_INTEGER;
BEGIN
i := dbms_profiler.flush_data;
i := dbms_profiler.pause_profiler;
i := dbms_profiler.resume_profiler;
END;
/ |
Overload 2 |
dbms_profiler.pause_profiler; |
SELECT dbms_profiler.pause_profiler
FROM dual;
PAUSE_PROFILER
--------------
0 |
|
RESUME_PROFILER |
Restarts Profiler data collection
Overload 1 |
dbms_profiler.resume_profiler RETURN BINARY_INTEGER; |
See PAUSE_PROFILER Demo Above |
Overload 2 |
dbms_profiler.resume_profiler; |
SELECT dbms_profiler.resume_profiler
FROM dual;
RESUME_PROFILER
---------------
0 |
|
ROLLUP_RUN |
Roll up and calculate the total time usage for all units that have been part of a run |
dbms_profiler.rollup_run(run_number IN NUMBER); |
CREATE OR REPLACE PROCEDURE proc1 AUTHID DEFINER IS
vd VARCHAR2(5);
BEGIN
FOR i IN 1..100
LOOP
SELECT dummy INTO vd FROM dual;
END LOOP;
END proc1;
/
-- failure to run proftab.sql for the user
DECLARE
v_run NUMBER;
BEGIN
dbms_profiler.start_profiler(SYSDATE, 'test1', v_run);
proc1;
dbms_profiler.stop_profiler;
dbms_profiler.rollup_run(v_run);
END;
/
BEGIN dbms_profiler.start_profiler; END;
*
ERROR at line 1:
ORA-06528: Error executing PL/SQL profiler
ORA-06512: at "SYS.DBMS_PROFILER", line 132
ORA-06512: at "SYS.DBMS_PROFILER", line 141
ORA-06512: at line 1
-- run proftab
@?/rdbms/admin/proftab.sql
-- after running proftab
DECLARE
v_run NUMBER;
BEGIN
dbms_profiler.start_profiler(SYSDATE, 'test1', v_run);
proc1;
dbms_profiler.stop_profiler;
dbms_profiler.rollup_run(v_run);
END;
/
PL/SQL procedure successfully completed. |
|
ROLLUP_UNIT |
Roll up and calculate the total time usage for a specific unit that has been part of a run |
dbms_profiler.rollup_unit(
run_number IN NUMBER,
unit IN NUMBER);
-- executes the following code
UPDATE plsql_profiler_units
SET total_time = (
SELECT SUM(total_time)
FROM plsql_profiler_data
WHERE runid = run_number
AND unit_number = unit); |
SELECT *
FROM plsql_profiler_units;
exec dbms_profiler.rollup_unit(8, 3); |
|
START_PROFILER |
Start Profiler data collection in the current session
Overload 1 |
dbms_profiler.start_profiler(
run_comment IN VARCHAR2 := SYSDATE,
run_comment1 IN VARCHAR2 := '',
run_number OUT BINARY_INTEGER)
RETURN BINARY_INTEGER; |
TBD |
Overload 2 |
dbms_profiler.start_profiler(
run_comment IN VARCHAR2 := SYSDATE,
run_comment1 IN VARCHAR2 := '',
run_number OUT BINARY_INTEGER); |
TBD |
Overload 3 |
dbms_profiler.start_profiler(
run_comment IN VARCHAR2 := SYSDATE,
run_comment1 IN VARCHAR2 := '')
RETURN BINARY_INTEGER; |
TBD |
Overload 4 |
dbms_profiler.start_profiler(
un_comment IN VARCHAR2 := SYSDATE,
run_comment1 IN VARCHAR2 := ''); |
See Demo Below |
|
STOP_PROFILER |
Stop Profiling
Overload 1 |
dbms_profiler.stop_profiler RETURN BINARY_INTEGER; |
DECLARE
x PLS_INTEGER;
BEGIN
x := dbms_profiler.stop_profiler;
dbms_output.put_line(TO_CHAR(x));
END;
/ |
Overload 2 |
dbms_profiler.stop_profiler; |
See Demo Below |
|
Demo Preparatory Steps |
Preparation as SYS |
conn sys@pdbdev as sysdba
GRANT create procedure TO uwclass;
GRANT create sequence TO uwclass;
GRANT create view TO uwclass;
@?\rdbms\admin\profload.sql |
Preparation as UWCLASS |
conn uwclass/uwclass@pdbdev
@?\rdbms\admin\proftab.sql |
Procedure To Empty Profiler Tables Between Runs |
conn uwclass/uwclass@pdbdev
CREATE OR REPLACE PROCEDURE profreset AUTHID DEFINER IS
BEGIN
DELETE FROM plsql_profiler_data;
DELETE FROM plsql_profiler_units;
DELETE FROM plsql_profiler_runs;
COMMIT;
END profreset;
/ |
DBMS_Profiler Report. Save in /home/oracle or an equivalent location |
Download ASCII File |
Load Demo File. Save in /home/oracle or an equivalent location |
Download ASCII File |
|
Demo 1: Preparation of Demo For Testing |
Comma To Table Procedure Demo Tables |
conn uwclass/uwclass@pdbdev
CREATE TABLE sources_import (
sourceno VARCHAR2(10),
sizeno VARCHAR2(10),
status VARCHAR2(10),
latitude VARCHAR2(10),
longitude VARCHAR2(10),
testfor VARCHAR2(15));
CREATE GLOBAL TEMPORARY TABLE gtt_c2t (
readline VARCHAR2(200))
ON COMMIT DELETE ROWS; |
Create the load_sources_import procedure |
conn uwclass/uwclass@pdbdev
CREATE OR REPLACE PROCEDURE load_sources_import AUTHID DEFINER IS
ProcName VARCHAR2(30) := 'load_sources_import';
MyErrm VARCHAR2(250);
vFileName VARCHAR2(30) := 'sources.txt';
vLoc VARCHAR2(20) := 'CTEMP';
v_InHandle utl_file.file_type;
vNewLine VARCHAR2(100);
vLineNo PLS_INTEGER;
Comma1 PLS_INTEGER;
Comma2 PLS_INTEGER;
Comma3 PLS_INTEGER;
Comma4 PLS_INTEGER;
Comma5 PLS_INTEGER;
Fld1 sources_import.sourceno%TYPE;
Fld2 sources_import.sizeno%TYPE;
Fld3 sources_import.status%TYPE;
Fld4 sources_import.latitude%TYPE;
Fld5 sources_import.longitude%TYPE;
Fld6 sources_import.testfor%TYPE;
NoFileToLoad EXCEPTION;
BEGIN
BEGIN
v_InHandle := utl_file.fopen(vLoc, vFileName, 'r');
vLineNo := 1;
LOOP
BEGIN
utl_file.get_line(v_InHandle, vNewLine);
EXCEPTION
WHEN NO_DATA_FOUND THEN
EXIT;
END;
IF vLineNo > 1 THEN
vNewLine := TRANSLATE(vNewLine, 'A''', 'A');
Comma1 := INSTR(vNewLine, ',', 1,1);
Comma2 := INSTR(vNewLine, ',', 1,2);
Comma3 := INSTR(vNewLine, ',', 1,3);
Comma4 := INSTR(vNewLine, ',', 1,4);
Comma5 := INSTR(vNewLine, ',', 1,5);
Fld1 := SUBSTR(vNewLine,1,Comma1-1);
Fld2 := SUBSTR(vNewLine, Comma1+1, Comma2-Comma1-1);
Fld3 := SUBSTR(vNewLine, Comma2+1, Comma3-Comma2-1);
Fld4 := SUBSTR(vNewLine, Comma3+1, Comma4-Comma3-1);
Fld5 := SUBSTR(vNewLine, Comma4+1, Comma5-Comma4-1);
Fld6 := SUBSTR(vNewLine,Comma5+1);
INSERT INTO sources_import
(sourceno, sizeno, status, latitude, longitude, testfor)
VALUES
(Fld1, Fld2, Fld3, Fld4, Fld5, Fld6);
ELSE
vLineNo := 2;
END IF;
END LOOP;
COMMIT;
utl_file.fclose(v_InHandle);
EXCEPTION
WHEN utl_file.invalid_mode THEN
RAISE_APPLICATION_ERROR (-20051, 'Invalid Option');
WHEN utl_file.invalid_path THEN
RAISE_APPLICATION_ERROR (-20052, 'Invalid Path');
WHEN utl_file.invalid_filehandle THEN
RAISE_APPLICATION_ERROR (-20053, 'Invalid Filehandle');
WHEN utl_file.invalid_operation THEN
RAISE_APPLICATION_ERROR (-20054, 'Invalid operation');
WHEN utl_file.read_error THEN
RAISE_APPLICATION_ERROR (-20055, 'Read Error');
WHEN utl_file.internal_error THEN
RAISE_APPLICATION_ERROR (-20057, 'Internal Error');
WHEN OTHERS THEN
RAISE;
END;
EXCEPTION
WHEN NoFileToLoad THEN
dbms_output.put_line('No File To Load Was Found');
WHEN OTHERS THEN
MyErrm := SQLERRM;
dbms_output.put_line(MyErrm);
END load_sources_import;
/ |
Create the Comma To Table Procedure |
conn uwclass/uwclass@pdbdev
CREATE OR REPLACE PROCEDURE c2t_demo AUTHID DEFINER IS
my_table dbms_utility.uncl_array;
cnt BINARY_INTEGER;
c_string VARCHAR2(250);
CURSOR t_cur IS
SELECT readline
FROM gtt_c2t;
t_rec t_cur%ROWTYPE;
BEGIN
OPEN t_cur;
LOOP
FETCH t_cur INTO t_rec;
EXIT WHEN t_cur%NOTFOUND;
-- move the value from the cursor to the VARCHAR2 variable
c_string := t_rec.readline;
-- use the built-in package to break it up
dbms_utility.comma_to_table(c_string, cnt, my_table);
-- use TRANSLATE to remove the single and double quotes
my_table(1) := TRANSLATE(my_table(1), '1"''', '1');
my_table(2) := TRANSLATE(my_table(2), '1"''', '1');
my_table(3) := TRANSLATE(my_table(3), '1"''', '1');
my_table(4) := TRANSLATE(my_table(4), '1"''', '1');
my_table(5) := TRANSLATE(my_table(5), '1"''', '1');
my_table(6) := TRANSLATE(my_table(6), '1"''', '1');
INSERT INTO sources_import
(sourceno, sizeno, status,
latitude, longitude, testfor)
VALUES
(my_table(1), my_table(2), my_table(3),
my_table(4), my_table(5), my_table(6));
END LOOP;
COMMIT;
CLOSE t_cur;
END c2t_demo;
/ |
First Procedure To Load Intermediary Table And Replace Single Quotes With Double Quotes |
conn uwclass/uwclass@pdbdev
CREATE OR REPLACE PROCEDURE load_c2t_test AUTHID DEFINER IS
vProcName VARCHAR2(30) := 'load_t2c_test';
ErrMsg VARCHAR2(250);
vFileName VARCHAR2(30) := 'sources.txt';
vLoc VARCHAR2(20) := 'CTEMP';
vNewLine VARCHAR2(65);
vFirstLine PLS_INTEGER := 0;
StartTime PLS_INTEGER;
vInHandle utl_file.file_type;
BEGIN
StartTime := dbms_utility.get_time;
vInHandle := utl_file.fopen(vLoc, vFileName, 'r');
LOOP
BEGIN
utl_file.get_line(vInHandle, vNewLine);
EXCEPTION
WHEN NO_DATA_FOUND THEN
EXIT;
END;
-- find location of the delimiting commas
BEGIN
IF vFirstLine <> 1 THEN
INSERT INTO gtt_c2t
(readline)
VALUES
(vNewLine);
END IF;
EXCEPTION
WHEN OTHERS THEN
RAISE;
END;
END LOOP;
-- close the text file
utl_file.fclose(vInHandle);
DELETE FROM gtt_c2t
WHERE readline LIKE '%SOURCENO%';
UPDATE gtt_c2t
SET readline = TRANSLATE(readline, 'A''', 'A"');
c2t_demo; -- 2nd procedure that parses record
EXCEPTION
WHEN utl_file.invalid_mode THEN
RAISE_APPLICATION_ERROR(-20051, 'Invalid Option');
WHEN utl_file.invalid_path THEN
RAISE_APPLICATION_ERROR(-20052, 'Invalid Path');
WHEN utl_file.invalid_filehandle THEN
RAISE_APPLICATION_ERROR(-20053, 'Invalid Filehandle');
WHEN utl_file.invalid_operation THEN
RAISE_APPLICATION_ERROR(-20054, 'Invalid operation');
WHEN utl_file.read_error THEN
RAISE_APPLICATION_ERROR(-20055, 'Read Error');
WHEN utl_file.internal_error THEN
RAISE_APPLICATION_ERROR(-20057, 'Internal Error');
WHEN OTHERS THEN
RAISE;
END load_c2t_test;
/ |
Procedure utilizing exteral table array processing |
conn uwclass/uwclass@pdbdev
CREATE TABLE ext_tab (
sourceno CHAR(5),
sizeno CHAR(6),
status CHAR(3),
latitude CHAR(10),
longitude CHAR(11),
testfor CHAR(17))
ORGANIZATION EXTERNAL
(TYPE oracle_loader
DEFAULT DIRECTORY ctemp
ACCESS PARAMETERS
(FIELDS TERMINATED BY ','
MISSING FIELD VALUES ARE NULL
(sourceno, sizeno, status, latitude, longitude, testfor))
LOCATION ('sources.txt'))
PARALLEL
REJECT LIMIT 10;
CREATE OR REPLACE PROCEDURE array_load AUTHID DEFINER IS
CURSOR acur IS
SELECT TRANSLATE(sourceno, 'A''', 'A'),
TRANSLATE(sizeno, 'A''', 'A'),
TRANSLATE(status, 'A''', 'A'),
TRANSLATE(latitude, 'A''', 'A'),
TRANSLATE(longitude, 'A''', 'A'),
TRANSLATE(testfor, 'A''', 'A')
FROM ext_tab;
TYPE profarray IS TABLE OF sources_import%ROWTYPE;
l_data profarray;
BEGIN
OPEN acur;
FETCH acur BULK COLLECT INTO l_data;
FORALL i IN 1..l_data.COUNT
INSERT INTO sources_import VALUES l_data(i);
COMMIT;
CLOSE acur;
EXCEPTION
WHEN OTHERS THEN
RAISE;
END array_load;
/ |
|
Procedure blending UTL_FILE and array processing |
conn uwclass/uwclass@pdbdev
CREATE OR REPLACE PROCEDURE blended AUTHID DEFINER IS
vFileName VARCHAR2(30) := 'sources.txt';
vLoc VARCHAR2(20) := 'CTEMP';
v_InHandle utl_file.file_type;
vNewLine VARCHAR2(100);
vLineNo PLS_INTEGER;
c1 PLS_INTEGER;
c2 PLS_INTEGER;
c3 PLS_INTEGER;
c4 PLS_INTEGER;
c5 PLS_INTEGER;
TYPE profarray IS TABLE OF sources_import%ROWTYPE
INDEX BY BINARY_INTEGER;
l_data profarray;
BEGIN
v_InHandle := utl_file.fopen(vLoc, vFileName, 'r');
vLineNo := 1;
LOOP
BEGIN
utl_file.get_line(v_InHandle, vNewLine);
EXCEPTION
WHEN NO_DATA_FOUND THEN
EXIT;
END;
vNewLine := TRANSLATE(vNewLine, 'A''', 'A');
c1 := INSTR(vNewLine, ',', 1,1);
c2 := INSTR(vNewLine, ',', 1,2);
c3 := INSTR(vNewLine, ',', 1,3);
c4 := INSTR(vNewLine, ',', 1,4);
c5 := INSTR(vNewLine, ',', 1,5);
l_data(vLineNo).sourceno := SUBSTR(vNewLine,1,c1-1);
l_data(vLineNo).sizeno := SUBSTR(vNewLine,c1+1,c2-c1-1);
l_data(vLineNo).status := SUBSTR(vNewLine,c2+1,c3-c2-1);
l_data(vLineNo).latitude := SUBSTR(vNewLine,c3+1,c4-c3-1);
l_data(vLineNo).longitude := SUBSTR(vNewLine,c4+1,c5-c4-1);
l_data(vLineNo).testfor := SUBSTR(vNewLine,c5+1);
vLineNo := vLineNo+1;
END LOOP;
utl_file.fclose(v_InHandle);
FORALL i IN 1..l_data.COUNT
INSERT INTO sources_import VALUES l_data(i);
DELETE FROM sources_import WHERE sourceno = 'SOURCENO';
COMMIT;
EXCEPTION
WHEN OTHERS THEN
RAISE;
END blended;
/ |
|
Profiling Demo |
Profiler Run |
conn uwclass/uwclass@pdbdev
set serveroutput on
-- BEGIN RUN 1: BASIC CURSOR LOOP
-- clean out the profiler tables
exec profreset
PL/SQL procedure successfully completed.
-- clean out the test tables
TRUNCATE TABLE sources_import;
Table truncated.
-- run the procedure to put it into memory
exec load_sources_import;
PL/SQL procedure successfully completed.
SELECT *
FROM sources_import;
No rows selected.
-- truncate the table before starting again
TRUNCATE TABLE sources_import;
Table truncated.
-- start the profiler
exec dbms_profiler.start_profiler('A')
-- run the procedure
exec load_sources_import
PL/SQL procedure successfully completed.
-- stop the profiler
exec dbms_profiler.stop_profiler;
-- get the report
@c:\temp\profiler.sql
-- save the report as run1.txt
-- examine the profiler tables
set linesize 121
-- view raw profiler data
SELECT runid, unit_number, line#, total_occur, total_time,
min_time, max_time
FROM plsql_profiler_data;
SELECT runid, related_run, run_owner, run_date, run_comment,
run_total_time, run_system_info
FROM plsql_profiler_runs;
col unit_type format a20
col unit_name format a25
SELECT runid, unit_number, unit_type, unit_owner, unit_name,
unit_timestamp, total_time
FROM plsql_profiler_units;
SELECT dump(unit_timestamp)
FROM plsql_profiler_units;
-- BEGIN RUN 2: COMMA_TO_TABLE
-- clean out the profiler tables
exec profreset
-- clean out the test tables
TRUNCATE TABLE sources_import;
-- run the procedure to put it into memory
exec load_c2t_test;
SELECT * FROM sources_import;
-- clean out the test tables
TRUNCATE TABLE sources_import;
-- start the profiler
exec dbms_profiler.start_profiler('B')
-- run the procedure
exec load_c2t_test
-- stop the profiler
exec dbms_profiler.stop_profiler;
-- get the report
@c:\temp\profsum.sql
-- save the report as run2.txt
-- BEGIN RUN 3: ARRAY PROCESSING
-- clean out the profiler tables
exec profreset
-- clean out the test tables
TRUNCATE TABLE sources_import;
-- run the procedure to put it into memory
exec array_load;
SELECT * FROM sources_import;
-- truncate the table before starting again
TRUNCATE TABLE sources_import;
-- start the profiler
exec dbms_profiler.start_profiler('C')
-- run the procedure
exec array_load
-- stop the profiler
exec dbms_profiler.stop_profiler;
-- get the report
@c:\temp\profsum.sql
-- save the report as run3.txt
-- BEGIN RUN 4: BLENDED PROCESSING
-- clean out the profiler tables
exec profreset
-- clean out the test tables
TRUNCATE TABLE sources_import;
-- run the procedure to put it into memory
exec blended;
SELECT * FROM sources_import;
-- truncate the table before starting again
TRUNCATE TABLE sources_import;
-- start the profiler
exec dbms_profiler.start_profiler('D')
-- run the procedure
exec blended
-- stop the profiler
exec dbms_profiler.stop_profiler;
-- get the report
@c:\temp\profsum.sql
-- save the report as run4.txt |
|
Demo 2 Procedure |
Demo Tables and Data |
conn uwclass/uwclass@pdbdev
CREATE TABLE parent (
part_num NUMBER,
part_name VARCHAR2(15));
CREATE TABLE child AS
SELECT *
FROM parent;
DECLARE
k parent.part_name%TYPE := 'Transducer';
BEGIN
FOR i IN 1 .. 200000
LOOP
SELECT DECODE(k, 'Transducer', 'Rectifier',
'Rectifier', 'Capacitor',
'Capacitor', 'Knob',
'Knob', 'Chassis',
'Chassis', 'Transducer')
INTO k
FROM dual;
INSERT INTO parent VALUES (i, k);
END LOOP;
COMMIT;
END;
/ |
Demo Procedures |
conn uwclass/uwclass@pdbdev
CREATE OR REPLACE PROCEDURE slow_way AUTHID CURRENT_USER IS
BEGIN
FOR r IN (SELECT * FROM parent) LOOP
r.part_num := r.part_num * 10;
INSERT INTO child
VALUES
(r.part_num, r.part_name);
END LOOP;
COMMIT;
END slow_way;
/
CREATE OR REPLACE PROCEDURE fast_way AUTHID CURRENT_USER IS
TYPE myarray IS TABLE OF parent%ROWTYPE;
l_data myarray;
CURSOR r IS
SELECT part_num, part_name
FROM parent;
BatchSize CONSTANT POSITIVE := 1000;
BEGIN
OPEN r;
LOOP
FETCH r BULK COLLECT INTO l_data LIMIT BatchSize;
FOR j IN 1 .. l_data.COUNT LOOP
l_data(j).part_num := l_data(j).part_num * 10;
END LOOP;
FORALL i IN 1..l_data.COUNT
INSERT INTO child VALUES l_data(i);
EXIT WHEN l_data.COUNT < BatchSize;
END LOOP;
COMMIT;
CLOSE r;
END fast_way;
/ |
Profiler Script #1 |
conn uwclass/uwclass@pdbdev
exec dbms_profiler.start_profiler('SLOW');
exec slow_way;
exec dbms_profiler.stop_profiler;
exec dbms_profiler.start_profiler('FAST');
exec fast_way;
exec dbms_profiler.stop_profiler; |
Eval SLOW Run |
conn uwclass/uwclass@pdbdev
SELECT runid, unit_name, unit_owner
FROM plsql_profiler_units
ORDER BY 1;
col text format a60
SELECT ds.line, p.total_occur, p.total_time Msec, ds.text
FROM dba_source ds, (
SELECT ppu.unit_owner, ppu.unit_name, ppu.unit_type, ppd.line#, ppd.total_occur,
ppd.total_time/1000000 total_time
FROM plsql_profiler_data ppd, plsql_profiler_units ppu
WHERE ppu.runid = 3
AND ppu.runid = ppd.runid
AND ppu.unit_number = ppd.unit_number) p
WHERE ds.owner = p.unit_owner(+)
AND ds.name = p.unit_name(+)
AND ds.type = p.unit_type(+)
AND ds.line = p.line#(+)
AND ds.name = 'SLOW_WAY'
AND ds.owner = 'UWCLASS'
ORDER BY 1;
LINE TOTAL_OCCUR MSEC TEXT
------- ----------- -------------- -----------------------------------------
1 0 .011535 PROCEDURE slow_way AUTHID CURRENT_USER IS
2 BEGIN
3 2002 430.588729 FOR r IN (SELECT * FROM parent) LOOP
4 200000 115.299009 r.part_num := r.part_num * 10;
5 200000 17889.007 INSERT INTO child
6 VALUES
7 (r.part_num, r.part_name);
8 END LOOP;
9 1 .83708 COMMIT;
10 1 .012322 END slow_way;
10 rows selected. |
Evaluate FAST Run |
conn uwclass/uwclass@pdbdev
SELECT runid, unit_name, unit_owner
FROM plsql_profiler_units
ORDER BY 1;
col text format a60
SELECT ds.line, p.total_occur, p.total_time Msec, ds.text
FROM dba_source ds, (
SELECT ppu.unit_owner, ppu.unit_name, ppu.unit_type, ppd.line#, ppd.total_occur,
ppd.total_time/1000000 total_time
FROM plsql_profiler_data ppd, plsql_profiler_units ppu
WHERE ppu.runid = 4
AND ppu.runid = ppd.runid
AND ppu.unit_number = ppd.unit_number) p
WHERE ds.owner = p.unit_owner(+)
AND ds.name = p.unit_name(+)
AND ds.type = p.unit_type(+)
AND ds.line = p.line#(+)
AND ds.name = 'FAST_WAY'
AND ds.owner = 'UWCLASS'
ORDER BY 1;
LINE TOTAL_OCCUR MSEC TEXT
------- ----------- -------------- -----------------------------------------
1 1 .132864 PROCEDURE fast_way AUTHID CURRENT_USER IS
2 TYPE myarray IS TABLE OF parent%ROWTYPE;
3 l_data myarray;
4 0 .00304 CURSOR r IS
5 1 28.317165 SELECT part_num, part_name
6 0 .011255 FROM parent;
7 1 .001778 BatchSize CONSTANT POSITIVE := 1000;
8 BEGIN
9 1 .002619 OPEN r;
10 201 0 LOOP
11 201 808.792131 FETCH r BULK COLLECT INTO l_data LIMIT BatchSize;
12 200201 118.087805 FOR j IN 1 .. l_data.COUNT LOOP
13 200000 350.545577 l_data(j).part_num := l_data(j).part_num * 10;
14 END LOOP;
15 201 1772.08412 FORALL i IN 1..l_data.COUNT
16 INSERT INTO child VALUES l_data(i);
17 200 .459526 EXIT WHEN l_data.COUNT < BatchSize;
18 END LOOP;
19 1 3.923959 COMMIT;
20 1 .148873 CLOSE r;
21 1 .099324 END fast_way;
21 rows selected. |