Not something entirely learned new today - but rather a revisited "something I learned".
When you don't use bind variables revolves around security, specifically the risk of SQL injection.
SQL injection occurs when an application program accepts arbitrary SQL from an untrusted source (think "end user"), blindly adds it to the application's SQL, and executes it. It would be similar to the operating system's taking a couple of lines of C code from a user, compiling it on the fly, and just executing it in the operating system kernel. It would stand a good chance of crashing something.
To understand how it works, suppose that we have an application that asks a user for a username and password. We execute this query using with and without Bind variables.
scott@10GR2> create table t(userid varchar2(10),passwd varchar2(100));
Table created.
scott@10GR2> insert into t values ('tom','top_secret_password');
1 row created.
scott@10GR2> commit;
Commit complete.
scott@10GR2> Create or replace procedure without_bind (p_username in varchar2,p_passwd in varchar2)
2 as
3 l_count number := 0;
4 v_sql varchar2(500);
5 begin
6 v_sql := 'select count(*)
7 from t
8 where userid = '''||p_username ||''' AND passwd = '|| p_passwd ;
9
10 execute immediate v_sql into l_count;
11
12 if l_count > 0 then
13 dbms_output.put_line('Thanks for Correct user name and password');
14 else
15 dbms_output.put_line('Incorrect Credentials');
16 end if;
17
18 end without_bind;
19 /
Procedure created.
scott@10GR2> Create or replace procedure using_bind (p_username in varchar2,p_passwd in varchar2)
2 as
3 l_count number := 0;
4 begin
5
6 execute immediate ' select count(*) from t where userid = :x and passwd = :y ' into l_count using p_username, p_passwd ;
7
8 if l_count > 0 then
9 dbms_output.put_line('Thanks for Correct user name and password');
10 else
11 dbms_output.put_line('Incorrect Credentials');
12 end if;
13
14 end using_bind;
15 /
Procedure created.
scott@10GR2> exec without_bind('tom','''i_dont_know'' or 1 = 1');
Thanks for Correct user name and password
Note the password we just used. It incorporates a little SQL, doesn't it? Since we are just gluing strings together, not binding, the end user can actually type in arbitrary SQL and have it executed! Our application takes this string now and continues on.
Look at that. Apparently, the password 'i_dont_know' or 1 = 1 is our password. But if we use bind variables instead and accept the exact input from the end user, we see this:
scott@10GR2> exec using_bind('tom','''i_dont_know'' or 1 = 1');
Incorrect Credentials
Bind variables add security to your applications.
Monday, May 24, 2010
Thursday, May 13, 2010
Export and Import (DataPump) Over the Network - Schema Refresh.
Here's how to set up an Oracle Data Pump Import, using a network-based approach
system@ORCL> create user test_dpump identified by test_dpump;
User created.
system@ORCL> grant connect ,resource,dba to test_dpump;
Grant succeeded.
system@ORCL>
test_dpump@ORCL> create database link test_dpump_to_longdes connect to LONGDESC identified by password using 'orcl';
Database link created.
test_dpump@ORCL> select * from dual@test_dpump_to_longdes;
D
-
X
test_dpump@ORCL>
longdesc@ORCL> select count(*) from user_tables;
COUNT(*)
----------
136
Script for Schema Refresh from Master Database.
DECLARE
ind NUMBER; -- Loop index number
job_handle number; -- Data Pump job handle
percent_done number := 0; -- Percentage of job complete
job_state varchar2(100) := 'UNDEFINED'; -- Keeps track of job state
le ku$_LogEntry; -- work-in-progress and error messages
js ku$_JobStatus; -- Job status from get_status
jd ku$_JobDesc; -- Job description from get_status
sts ku$_Status; -- Status object returned by get_status
BEGIN
-- Using database link establish connection to pull data from source to destination database.
job_handle := dbms_datapump.open ( operation=>'IMPORT',
job_mode=>'TABLE',
remote_link=>'TEST_DPUMP_TO_LONGDES',
job_name=> 'TEST_DPUMP_REFRESH');
-- Setting the degree of parallelization to 1 for this Refresh operation.
dbms_datapump.set_parallel ( handle =>job_handle,
degree => 1 );
--- Remap Schema objects from source schema Named LONGDESC to destination schema TEST_DPUMP.
dbms_datapump.metadata_remap ( handle => job_handle,name => 'REMAP_SCHEMA',old_value => 'LONGDESC',value => 'TEST_DPUMP' );
-- If a table already exists in the destination schema, don't override (this is default behavior.)
dbms_datapump.set_parameter( handle => job_handle,name => 'TABLE_EXISTS_ACTION',value => 'SKIP');
-- Use statistics (rather than blocks) to estimate time.
dbms_datapump.set_parameter( handle => job_handle, name => 'ESTIMATE', value => 'STATISTICS' );
-- Start the job. An exception is returned if something is not set up properly.
dbms_datapump.start_job (handle => job_handle);
-- The import job should now be running. This loop monitors the job until it completes, displaying progress information if there are problems.
while ( job_state <> 'COMPLETED' and job_state <> 'STOPPED')
loop
dbms_datapump.get_status( job_handle,
DBMS_DATAPUMP.ku$_status_job_error +
DBMS_DATAPUMP.ku$_status_job_status +
DBMS_DATAPUMP.ku$_status_wip ,
-1,
job_state ,
sts );
js := sts.job_status;
-- As the percentage-complete changes in this loop, the new value displays.
if js.percent_done != percent_done then
DBMS_OUTPUT.PUT_LINE('*** Job percent done = ' || to_char(js.percent_done));
percent_done := js.percent_done;
end if;
-- Displays any work-in-progress or error messages received for the job.
if (bitand(sts.mask,dbms_datapump.ku$_status_wip) != 0) then
le := sts.wip;
else
if (bitand(sts.mask,dbms_datapump.ku$_status_job_error) != 0) then
le := sts.error;
else
le := null;
end if;
end if;
if le is not null then
ind := le.FIRST;
while ind is not null
loop
DBMS_OUTPUT.PUT_LINE(le(ind).LogText);
ind := le.NEXT(ind);
end loop;
end if;
end loop; --- End of While loop.
-- When the job finishes, display status before detaching from job.
DBMS_OUTPUT.PUT_LINE('Job has completed');
DBMS_OUTPUT.PUT_LINE('Final job state = ' || job_state);
DBMS_DATAPUMP.DETACH(job_handle);
END;
/
test_dpump@ORCL> select count(*) from user_tables;
COUNT(*)
----------
136
Oracle Data Pump imports are anywhere from 15 to 40 times as fast as with traditional import, even with parallelism set to 1, in part because of Oracle Data Pump's use of direct path (when possible).
- Set up a remote link from a satellite database (database 2) to the master database (database 1).
- Create the import as a SQL script to run weekly from the satellite database to import selected tables from the source system, filtering and transforming as needed
system@ORCL> create user test_dpump identified by test_dpump;
User created.
system@ORCL> grant connect ,resource,dba to test_dpump;
Grant succeeded.
system@ORCL>
test_dpump@ORCL> create database link test_dpump_to_longdes connect to LONGDESC identified by password using 'orcl';
Database link created.
test_dpump@ORCL> select * from dual@test_dpump_to_longdes;
D
-
X
test_dpump@ORCL>
longdesc@ORCL> select count(*) from user_tables;
COUNT(*)
----------
136
Script for Schema Refresh from Master Database.
DECLARE
ind NUMBER; -- Loop index number
job_handle number; -- Data Pump job handle
percent_done number := 0; -- Percentage of job complete
job_state varchar2(100) := 'UNDEFINED'; -- Keeps track of job state
le ku$_LogEntry; -- work-in-progress and error messages
js ku$_JobStatus; -- Job status from get_status
jd ku$_JobDesc; -- Job description from get_status
sts ku$_Status; -- Status object returned by get_status
BEGIN
-- Using database link establish connection to pull data from source to destination database.
job_handle := dbms_datapump.open ( operation=>'IMPORT',
job_mode=>'TABLE',
remote_link=>'TEST_DPUMP_TO_LONGDES',
job_name=> 'TEST_DPUMP_REFRESH');
-- Setting the degree of parallelization to 1 for this Refresh operation.
dbms_datapump.set_parallel ( handle =>job_handle,
degree => 1 );
--- Remap Schema objects from source schema Named LONGDESC to destination schema TEST_DPUMP.
dbms_datapump.metadata_remap ( handle => job_handle,name => 'REMAP_SCHEMA',old_value => 'LONGDESC',value => 'TEST_DPUMP' );
-- If a table already exists in the destination schema, don't override (this is default behavior.)
dbms_datapump.set_parameter( handle => job_handle,name => 'TABLE_EXISTS_ACTION',value => 'SKIP');
-- Use statistics (rather than blocks) to estimate time.
dbms_datapump.set_parameter( handle => job_handle, name => 'ESTIMATE', value => 'STATISTICS' );
-- Start the job. An exception is returned if something is not set up properly.
dbms_datapump.start_job (handle => job_handle);
-- The import job should now be running. This loop monitors the job until it completes, displaying progress information if there are problems.
while ( job_state <> 'COMPLETED' and job_state <> 'STOPPED')
loop
dbms_datapump.get_status( job_handle,
DBMS_DATAPUMP.ku$_status_job_error +
DBMS_DATAPUMP.ku$_status_job_status +
DBMS_DATAPUMP.ku$_status_wip ,
-1,
job_state ,
sts );
js := sts.job_status;
-- As the percentage-complete changes in this loop, the new value displays.
if js.percent_done != percent_done then
DBMS_OUTPUT.PUT_LINE('*** Job percent done = ' || to_char(js.percent_done));
percent_done := js.percent_done;
end if;
-- Displays any work-in-progress or error messages received for the job.
if (bitand(sts.mask,dbms_datapump.ku$_status_wip) != 0) then
le := sts.wip;
else
if (bitand(sts.mask,dbms_datapump.ku$_status_job_error) != 0) then
le := sts.error;
else
le := null;
end if;
end if;
if le is not null then
ind := le.FIRST;
while ind is not null
loop
DBMS_OUTPUT.PUT_LINE(le(ind).LogText);
ind := le.NEXT(ind);
end loop;
end if;
end loop; --- End of While loop.
-- When the job finishes, display status before detaching from job.
DBMS_OUTPUT.PUT_LINE('Job has completed');
DBMS_OUTPUT.PUT_LINE('Final job state = ' || job_state);
DBMS_DATAPUMP.DETACH(job_handle);
END;
/
test_dpump@ORCL> select count(*) from user_tables;
COUNT(*)
----------
136
Oracle Data Pump imports are anywhere from 15 to 40 times as fast as with traditional import, even with parallelism set to 1, in part because of Oracle Data Pump's use of direct path (when possible).
Wednesday, May 12, 2010
dbms_utility.FORMAT_ERROR_BACKTRACE - 10g New Features
When an exception is raised, one of the most important piece of information that a developer would like to know is the line of code that raised that exception, prior to Oracle 10g one way to obtain this information by allowing the exception to go unhandled.
test@9iR2> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.8.0 - Production
PL/SQL Release 9.2.0.8.0 - Production
CORE 9.2.0.8.0 Production
TNS for 32-bit Windows: Version 9.2.0.8.0 - Production
NLSRTL Version 9.2.0.8.0 - Production
test@9iR2> create or replace procedure p1 as
2 begin
3 dbms_output.put_line ('raising error at p1');
4 raise no_data_found;
5 end;
6 /
Procedure created.
test@9iR2> create or replace procedure p2 as
2 begin
3 p1;
4 end;
5 /
Procedure created.
test@9iR2> create or replace procedure p3 as
2 begin
3 p2;
4 end;
5 /
Procedure created.
test@9iR2> exec p3;
raising error at p1
BEGIN p3; END;
*
ERROR at line 1:
ORA-01403: no data found
ORA-06512: at "TEST.P1", line 4
ORA-06512: at "TEST.P2", line 3
ORA-06512: at "TEST.P3", line 3
ORA-06512: at line 1
Now we have the line number, we can zoom right into the code and fix it, on the other hand we got this information by letting the Exception going unhandled. How ever in many application we work to avoid unhandled exception.
Let's see what happen when exception handled in procedure P3.
test@9iR2> create or replace procedure p3 as
2 begin
3 p2;
4 exception
5 when others then
6 dbms_output.put_line (' calling format error stack from P3 ');
7 dbms_output.put_line (dbms_utility.format_error_stack);
8 end;
9 /
Procedure created.
Notice that I call dbms_utility.format_error_stack, it returns the full error message.
test@PWTK52> exec p3;
raising error at p1
calling format error stack from P3
ORA-01403: no data found
PL/SQL procedure successfully completed.
dbms_utility.format_error_stack - does not show the full error stack with line numbers.
In Oracle database 10g, Oracle added format_error_backtrace which can and should be called from exception handler. It displays the call stack at the point where exception was raised.
Let's see what happen when exception handled using dbms_utility.format_error_stack procedure P3.
scott@10gR2> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi
PL/SQL Release 10.2.0.4.0 - Production
CORE 10.2.0.4.0 Production
TNS for Solaris: Version 10.2.0.4.0 - Production
NLSRTL Version 10.2.0.4.0 - Production
scott@10gR2> create or replace procedure p3 as
2 begin
3 p2;
4 exception
5 when others then
6 dbms_output.put_line (' calling format error stack from P3 ');
7 dbms_output.put_line (dbms_utility.FORMAT_ERROR_BACKTRACE);
8 end;
9 /
Procedure created.
And now when I run the procedure P3, I will see the following output.
scott@10gR2> exec p3;
raising error at p1
calling format error stack from P3
ORA-06512: at "scott.P1", line 4
ORA-06512: at "scott.P2", line 3
ORA-06512: at "scott.P3", line 3
PL/SQL procedure successfully completed.
The information that had previously been available only through an unhandled exception is now retrievable from within the PL/SQL code.
test@9iR2> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.8.0 - Production
PL/SQL Release 9.2.0.8.0 - Production
CORE 9.2.0.8.0 Production
TNS for 32-bit Windows: Version 9.2.0.8.0 - Production
NLSRTL Version 9.2.0.8.0 - Production
test@9iR2> create or replace procedure p1 as
2 begin
3 dbms_output.put_line ('raising error at p1');
4 raise no_data_found;
5 end;
6 /
Procedure created.
test@9iR2> create or replace procedure p2 as
2 begin
3 p1;
4 end;
5 /
Procedure created.
test@9iR2> create or replace procedure p3 as
2 begin
3 p2;
4 end;
5 /
Procedure created.
test@9iR2> exec p3;
raising error at p1
BEGIN p3; END;
*
ERROR at line 1:
ORA-01403: no data found
ORA-06512: at "TEST.P1", line 4
ORA-06512: at "TEST.P2", line 3
ORA-06512: at "TEST.P3", line 3
ORA-06512: at line 1
Now we have the line number, we can zoom right into the code and fix it, on the other hand we got this information by letting the Exception going unhandled. How ever in many application we work to avoid unhandled exception.
Let's see what happen when exception handled in procedure P3.
test@9iR2> create or replace procedure p3 as
2 begin
3 p2;
4 exception
5 when others then
6 dbms_output.put_line (' calling format error stack from P3 ');
7 dbms_output.put_line (dbms_utility.format_error_stack);
8 end;
9 /
Procedure created.
Notice that I call dbms_utility.format_error_stack, it returns the full error message.
test@PWTK52> exec p3;
raising error at p1
calling format error stack from P3
ORA-01403: no data found
PL/SQL procedure successfully completed.
dbms_utility.format_error_stack - does not show the full error stack with line numbers.
In Oracle database 10g, Oracle added format_error_backtrace which can and should be called from exception handler. It displays the call stack at the point where exception was raised.
Let's see what happen when exception handled using dbms_utility.format_error_stack procedure P3.
scott@10gR2> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi
PL/SQL Release 10.2.0.4.0 - Production
CORE 10.2.0.4.0 Production
TNS for Solaris: Version 10.2.0.4.0 - Production
NLSRTL Version 10.2.0.4.0 - Production
scott@10gR2> create or replace procedure p3 as
2 begin
3 p2;
4 exception
5 when others then
6 dbms_output.put_line (' calling format error stack from P3 ');
7 dbms_output.put_line (dbms_utility.FORMAT_ERROR_BACKTRACE);
8 end;
9 /
Procedure created.
And now when I run the procedure P3, I will see the following output.
scott@10gR2> exec p3;
raising error at p1
calling format error stack from P3
ORA-06512: at "scott.P1", line 4
ORA-06512: at "scott.P2", line 3
ORA-06512: at "scott.P3", line 3
PL/SQL procedure successfully completed.
The information that had previously been available only through an unhandled exception is now retrievable from within the PL/SQL code.
Tuesday, May 11, 2010
How PL/SQL Optimizes Your Programs
Today, I learned a new 10g Release 2 and above 'trick' that I wasn't aware of. This is pretty cool
You can choose the level of optimization through the plsql_optimization_level setting:
2 Most aggressive, maximum possible code transformations, biggest impact on compile time. [default]
1 Smaller scale change, less impact on compile times
0 Pre-10g compilation without optimization
scott@10GR2> show parameter plsql_optim;
NAME TYPE VALUE
------------------------------------ ----------- -----------
plsql_optimize_level integer 2
scott@10GR2> alter session set plsql_optimize_level=0;
Session altered.
scott@10GR2> create or replace procedure p
2 as
3 l_start_time number ;
4 a positiven := 1;
5 b positiven := 1;
6 c positiven := 1;
7 d positiven := 1;
8 begin
9 l_start_time := dbms_utility.get_cpu_time;
10 for i in 1..1000000
11 loop
12 a := 42;
13 b := a*2;
14 c := a * 5;
15 d := a+b+c;
16 end loop;
17 dbms_output.put_line ( dbms_utility.get_cpu_time - l_start_time);
18 end p;
19 /
Procedure created.
scott@10GR2> begin
2 for i in 1..5
3 loop
4 p;
5 end loop;
6 end;
7 /
39
41
39
39
41
PL/SQL procedure successfully completed.
scott@10GR2> alter session set plsql_optimize_level=1;
Session altered.
scott@10GR2> alter procedure p compile;
Procedure altered.
scott@10GR2> begin
2 for i in 1..5
3 loop
4 p;
5 end loop;
6 end;
7 /
9
8
9
8
8
PL/SQL procedure successfully completed.
scott@10GR2> alter session set plsql_optimize_level=2;
Session altered.
scott@10GR2> alter procedure p compile;
Procedure altered.
scott@10GR2> begin
2 for i in 1..5
3 loop
4 p;
5 end loop;
6 end;
7 /
10
7
10
9
8
PL/SQL procedure successfully completed.
scott@10GR2> SELECT name, type, plsql_optimize_level
2 FROM user_plsql_object_settings
3 WHERE name = 'P'
4 /
NAME TYPE PLSQL_OPTIMIZE_LEVEL
------------------------------ ------------ --------------------
P PROCEDURE 2
You can choose the level of optimization through the plsql_optimization_level setting:
2 Most aggressive, maximum possible code transformations, biggest impact on compile time. [default]
1 Smaller scale change, less impact on compile times
0 Pre-10g compilation without optimization
scott@10GR2> show parameter plsql_optim;
NAME TYPE VALUE
------------------------------------ ----------- -----------
plsql_optimize_level integer 2
scott@10GR2> alter session set plsql_optimize_level=0;
Session altered.
scott@10GR2> create or replace procedure p
2 as
3 l_start_time number ;
4 a positiven := 1;
5 b positiven := 1;
6 c positiven := 1;
7 d positiven := 1;
8 begin
9 l_start_time := dbms_utility.get_cpu_time;
10 for i in 1..1000000
11 loop
12 a := 42;
13 b := a*2;
14 c := a * 5;
15 d := a+b+c;
16 end loop;
17 dbms_output.put_line ( dbms_utility.get_cpu_time - l_start_time);
18 end p;
19 /
Procedure created.
scott@10GR2> begin
2 for i in 1..5
3 loop
4 p;
5 end loop;
6 end;
7 /
39
41
39
39
41
PL/SQL procedure successfully completed.
scott@10GR2> alter session set plsql_optimize_level=1;
Session altered.
scott@10GR2> alter procedure p compile;
Procedure altered.
scott@10GR2> begin
2 for i in 1..5
3 loop
4 p;
5 end loop;
6 end;
7 /
9
8
9
8
8
PL/SQL procedure successfully completed.
scott@10GR2> alter session set plsql_optimize_level=2;
Session altered.
scott@10GR2> alter procedure p compile;
Procedure altered.
scott@10GR2> begin
2 for i in 1..5
3 loop
4 p;
5 end loop;
6 end;
7 /
10
7
10
9
8
PL/SQL procedure successfully completed.
scott@10GR2> SELECT name, type, plsql_optimize_level
2 FROM user_plsql_object_settings
3 WHERE name = 'P'
4 /
NAME TYPE PLSQL_OPTIMIZE_LEVEL
------------------------------ ------------ --------------------
P PROCEDURE 2
Monday, May 10, 2010
Dependency Chain (PKG vs Proc)
Not something entirely learned new today - but rather a revisited "something I learned".
Nice quote from Oracle product documentation about dependency chain
If the body of the referenced package is replaced, then the dependent procedure is not affected. However, if the specification of the referenced package is replaced, then the dependent procedure is invalidated. This is a mechanism for minimizing dependencies among procedures and referenced objects by using packages.
If procedure A calls procedure B and B is "changed", then A is directly affected and must be recompiled. There is no "auto recompile A when B changes" option -- although A will recompile itself automatically the next time it is run.
On the other hand, If I create a package PKG_A with a procedure A and a package PKG_B with a procedure B then PKG_A will be dependent on PKG_B's specification. I can compile and recompile PKG_B's Body as often as I like without affecting PKG_A's state. As long as PKG_B's specification or interface does not change -- dependent objects are not affected by the recompilation of the body.
test@10gR2> create or replace procedure B
2 as
3 begin
4 null;
5 end;
6 /
Procedure created.
test@10gR2> create or replace procedure A
2 as
3 begin
4 b;
5 end;
6 /
Procedure created.
So procedure A calls B. One of the downsides to procedures, in addition to the direct dependency, is that you have to create them in the "right" order if you want everything valid in the database after an install. I had to create B before A. Packages don't have this nuance either -- as shown below
test@10gR2> create or replace package pkg_a as
2 procedure a;
3 end pkg_a;
4 /
Package created.
test@10gR2> create or replace package pkg_b as
2 procedure b;
3 end pkg_b;
4 /
Package created.
Here i have created the package specification, they are not dependent on other and can be created in any order.
test@10gR2> create or replace package body pkg_a as
2 procedure a
3 is
4 begin
5 pkg_b.b;
6 end a;
7 end pkg_a;
8 /
Package body created.
test@10gR2> create or replace package body pkg_b as
2 procedure b
3 is
4 begin
5 null;
6 end b;
7 end pkg_b;
8 /
Package body created.
Then i created the bodied - I can create bodies in any order after the specification (In general) - PKG_A is dependent on Pkg_B specification - not is body. so we don't have the ordering problem.
test@10gR2> select object_type,object_name,status
2 from user_objects
3 where status ='INVALID';
no rows selected
nothing is Invalid.
test@10gR2> alter procedure b compile;
Procedure altered.
test@10gR2> select object_type,object_name,status
2 from user_objects
3 where status ='INVALID';
OBJECT_TYPE OBJECT_NAME STATUS
------------------- ------------------------------ -------
PROCEDURE A INVALID
Standalone procedure A goes invalid, B was changed and A needs to be recompiled.
test@10gR2> alter package pkg_b compile body;
Package body altered.
test@10gR2> select object_type,object_name,status
2 from user_objects
3 where status ='INVALID';
OBJECT_TYPE OBJECT_NAME STATUS
------------------- ------------------------------ -------
PROCEDURE A INVALID
Pkg_A never goes invalid - As long as spec does not change, Pkg_A remains valid.
Don't use standalone procedures! Use packages. Packages break the dependency chain.
Nice quote from Oracle product documentation about dependency chain
If the body of the referenced package is replaced, then the dependent procedure is not affected. However, if the specification of the referenced package is replaced, then the dependent procedure is invalidated. This is a mechanism for minimizing dependencies among procedures and referenced objects by using packages.
If procedure A calls procedure B and B is "changed", then A is directly affected and must be recompiled. There is no "auto recompile A when B changes" option -- although A will recompile itself automatically the next time it is run.
On the other hand, If I create a package PKG_A with a procedure A and a package PKG_B with a procedure B then PKG_A will be dependent on PKG_B's specification. I can compile and recompile PKG_B's Body as often as I like without affecting PKG_A's state. As long as PKG_B's specification or interface does not change -- dependent objects are not affected by the recompilation of the body.
test@10gR2> create or replace procedure B
2 as
3 begin
4 null;
5 end;
6 /
Procedure created.
test@10gR2> create or replace procedure A
2 as
3 begin
4 b;
5 end;
6 /
Procedure created.
So procedure A calls B. One of the downsides to procedures, in addition to the direct dependency, is that you have to create them in the "right" order if you want everything valid in the database after an install. I had to create B before A. Packages don't have this nuance either -- as shown below
test@10gR2> create or replace package pkg_a as
2 procedure a;
3 end pkg_a;
4 /
Package created.
test@10gR2> create or replace package pkg_b as
2 procedure b;
3 end pkg_b;
4 /
Package created.
Here i have created the package specification, they are not dependent on other and can be created in any order.
test@10gR2> create or replace package body pkg_a as
2 procedure a
3 is
4 begin
5 pkg_b.b;
6 end a;
7 end pkg_a;
8 /
Package body created.
test@10gR2> create or replace package body pkg_b as
2 procedure b
3 is
4 begin
5 null;
6 end b;
7 end pkg_b;
8 /
Package body created.
Then i created the bodied - I can create bodies in any order after the specification (In general) - PKG_A is dependent on Pkg_B specification - not is body. so we don't have the ordering problem.
test@10gR2> select object_type,object_name,status
2 from user_objects
3 where status ='INVALID';
no rows selected
nothing is Invalid.
test@10gR2> alter procedure b compile;
Procedure altered.
test@10gR2> select object_type,object_name,status
2 from user_objects
3 where status ='INVALID';
OBJECT_TYPE OBJECT_NAME STATUS
------------------- ------------------------------ -------
PROCEDURE A INVALID
Standalone procedure A goes invalid, B was changed and A needs to be recompiled.
test@10gR2> alter package pkg_b compile body;
Package body altered.
test@10gR2> select object_type,object_name,status
2 from user_objects
3 where status ='INVALID';
OBJECT_TYPE OBJECT_NAME STATUS
------------------- ------------------------------ -------
PROCEDURE A INVALID
Pkg_A never goes invalid - As long as spec does not change, Pkg_A remains valid.
Don't use standalone procedures! Use packages. Packages break the dependency chain.
Script to compile Invalid objects - Tracking dependencies
begin
FOR r IN (SELECT 'ALTER ' || decode(a.object_type, 'PACKAGE BODY', 'PACKAGE', 'TYPE BODY', 'TYPE', a.object_type) || ' ' || a.object_name || decode(a.object_type, 'JAVA CLASS', ' RESOLVE', ' COMPILE') || decode(a.object_type, 'PACKAGE BODY', ' BODY', 'TYPE BODY', 'BODY') text
FROM user_objects a,
(SELECT MAX(LEVEL) dlevel,
object_id
FROM public_dependency START WITH object_id IN
(SELECT object_id
FROM user_objects
WHERE status = 'INVALID'
and object_type <> 'SYNONYM' )
CONNECT BY object_id = PRIOR referenced_object_id
GROUP BY object_id)b
WHERE a.object_id = b.object_id(+)
AND a.status = 'INVALID'
and object_type <> 'SYNONYM'
ORDER BY b.dlevel DESC,a.object_name ASC )
loop
begin
execute immediate r.text;
exception
when others then
dbms_output.put_line (' Exception '||sqlcode||' - '||sqlerrm ||' - ' ||r.text);
end;
end loop;
end;
rajesh@10GR2> begin
2 FOR r IN (SELECT 'ALTER ' || decode(a.object_type, 'PACKAGE BODY', 'PACKAGE', 'TYPE BODY', 'TYPE', a.object_type) || ' ' || a.object_name || decode(a.object_type, 'JAVA CLASS', ' RESOLVE', ' COMPILE') || decode(a.object_type, 'PACKAGE BODY', ' BODY', 'TYPE BODY', 'BODY') text
3 FROM user_objects a,
4 (SELECT MAX(LEVEL) dlevel,
5 object_id
6 FROM public_dependency START WITH object_id IN
7 (SELECT object_id
8 FROM user_objects
9 WHERE status = 'INVALID'
10 and object_type <> 'SYNONYM' )
11 CONNECT BY object_id = PRIOR referenced_object_id
12 GROUP BY object_id)b
13 WHERE a.object_id = b.object_id(+)
14 AND a.status = 'INVALID'
15 and object_type <> 'SYNONYM'
16 ORDER BY b.dlevel DESC,a.object_name ASC )
17 loop
18 begin
19 execute immediate r.text;
20 exception
21 when others then
22 dbms_output.put_line (' Exception '||sqlcode||' - '||sqlerrm ||' - ' ||r.text);
23 end;
24 end loop;
25 end;
26 /
Exception -24344 - ORA-24344: success with compilation error - ALTER FUNCTION EMP_FUNC COMPILE
Exception -24344 - ORA-24344: success with compilation error - ALTER FUNCTION SHOW_DATA COMPILE
PL/SQL procedure successfully completed.
FOR r IN (SELECT 'ALTER ' || decode(a.object_type, 'PACKAGE BODY', 'PACKAGE', 'TYPE BODY', 'TYPE', a.object_type) || ' ' || a.object_name || decode(a.object_type, 'JAVA CLASS', ' RESOLVE', ' COMPILE') || decode(a.object_type, 'PACKAGE BODY', ' BODY', 'TYPE BODY', 'BODY') text
FROM user_objects a,
(SELECT MAX(LEVEL) dlevel,
object_id
FROM public_dependency START WITH object_id IN
(SELECT object_id
FROM user_objects
WHERE status = 'INVALID'
and object_type <> 'SYNONYM' )
CONNECT BY object_id = PRIOR referenced_object_id
GROUP BY object_id)b
WHERE a.object_id = b.object_id(+)
AND a.status = 'INVALID'
and object_type <> 'SYNONYM'
ORDER BY b.dlevel DESC,a.object_name ASC )
loop
begin
execute immediate r.text;
exception
when others then
dbms_output.put_line (' Exception '||sqlcode||' - '||sqlerrm ||' - ' ||r.text);
end;
end loop;
end;
rajesh@10GR2> begin
2 FOR r IN (SELECT 'ALTER ' || decode(a.object_type, 'PACKAGE BODY', 'PACKAGE', 'TYPE BODY', 'TYPE', a.object_type) || ' ' || a.object_name || decode(a.object_type, 'JAVA CLASS', ' RESOLVE', ' COMPILE') || decode(a.object_type, 'PACKAGE BODY', ' BODY', 'TYPE BODY', 'BODY') text
3 FROM user_objects a,
4 (SELECT MAX(LEVEL) dlevel,
5 object_id
6 FROM public_dependency START WITH object_id IN
7 (SELECT object_id
8 FROM user_objects
9 WHERE status = 'INVALID'
10 and object_type <> 'SYNONYM' )
11 CONNECT BY object_id = PRIOR referenced_object_id
12 GROUP BY object_id)b
13 WHERE a.object_id = b.object_id(+)
14 AND a.status = 'INVALID'
15 and object_type <> 'SYNONYM'
16 ORDER BY b.dlevel DESC,a.object_name ASC )
17 loop
18 begin
19 execute immediate r.text;
20 exception
21 when others then
22 dbms_output.put_line (' Exception '||sqlcode||' - '||sqlerrm ||' - ' ||r.text);
23 end;
24 end loop;
25 end;
26 /
Exception -24344 - ORA-24344: success with compilation error - ALTER FUNCTION EMP_FUNC COMPILE
Exception -24344 - ORA-24344: success with compilation error - ALTER FUNCTION SHOW_DATA COMPILE
PL/SQL procedure successfully completed.
Session - Open Cursors
So, something else I learned recently...its about Open cursors in Session.
V$open_cursor – Represents a set of cached cursors that server has. Here is a quick test to demonstrate that.
scott@10GR2> SELECT *
2 FROM v$open_cursor
3 WHERE sql_text LIKE '%CURSOR_TEST%';
no rows selected
scott@10GR2> declare
2 cursor c1 is select * from dual cursor_test;
3 l_rec c1%rowtype;
4 begin
5 open c1;
6 fetch c1 into l_rec;
7 close c1;
8 end;
9 /
PL/SQL procedure successfully completed.
scott@10GR2> SELECT *
2 FROM v$open_cursor
3 WHERE sql_text LIKE '%CURSOR_TEST%'
4 /
no rows selected
scott@10GR2> create or replace procedure test_proc as
2 cursor c1 is select * from dual cursor_test;
3 l_rec c1%rowtype;
4 begin
5 open c1;
6 fetch c1 into l_rec;
7 close c1;
8 end test_proc;
9 /
Procedure created.
scott@10GR2> exec test_proc;
PL/SQL procedure successfully completed.
scott@10GR2> SELECT sid,sql_id,sql_text
2 FROM v$open_cursor
3 WHERE sql_text LIKE '%CURSOR_TEST%';
SID SQL_ID SQL_TEXT
-------- ---------- -----------------------------
159 9y8yp8fh22fxz SELECT * FROM DUAL CURSOR_TEST
Even I closed the cursor Explicitly it’s still shown up in V$open_Cursor (due to set of cached cursors in server).
To get the current open cursors ( Cursor leaking ) across session
scott@10GR2> select sum(a.value), b.name,a.sid
2 from v$sesstat a, v$statname b
3 where a.statistic# = b.statistic#
4 and b.name = 'opened cursors current'
5 group by b.name,a.sid
6 having sum(a.value) > 0
7 /
SUM(A.VALUE) NAME SID
------------ ------------------------------ ----------
32 opened cursors current 140
5 opened cursors current 138
5 opened cursors current 132
4 opened cursors current 162
81 opened cursors current 154
29 opened cursors current 161
11 opened cursors current 134
23 opened cursors current 136
55 opened cursors current 144
25 opened cursors current 149
10 rows selected.
open_cursors - its purely related to Session, say if you set Open_cursor 1000, so there could be 10,000 of them if you have 10 sessions.
V$open_cursor – Represents a set of cached cursors that server has. Here is a quick test to demonstrate that.
scott@10GR2> SELECT *
2 FROM v$open_cursor
3 WHERE sql_text LIKE '%CURSOR_TEST%';
no rows selected
scott@10GR2> declare
2 cursor c1 is select * from dual cursor_test;
3 l_rec c1%rowtype;
4 begin
5 open c1;
6 fetch c1 into l_rec;
7 close c1;
8 end;
9 /
PL/SQL procedure successfully completed.
scott@10GR2> SELECT *
2 FROM v$open_cursor
3 WHERE sql_text LIKE '%CURSOR_TEST%'
4 /
no rows selected
scott@10GR2> create or replace procedure test_proc as
2 cursor c1 is select * from dual cursor_test;
3 l_rec c1%rowtype;
4 begin
5 open c1;
6 fetch c1 into l_rec;
7 close c1;
8 end test_proc;
9 /
Procedure created.
scott@10GR2> exec test_proc;
PL/SQL procedure successfully completed.
scott@10GR2> SELECT sid,sql_id,sql_text
2 FROM v$open_cursor
3 WHERE sql_text LIKE '%CURSOR_TEST%';
SID SQL_ID SQL_TEXT
-------- ---------- -----------------------------
159 9y8yp8fh22fxz SELECT * FROM DUAL CURSOR_TEST
Even I closed the cursor Explicitly it’s still shown up in V$open_Cursor (due to set of cached cursors in server).
To get the current open cursors ( Cursor leaking ) across session
scott@10GR2> select sum(a.value), b.name,a.sid
2 from v$sesstat a, v$statname b
3 where a.statistic# = b.statistic#
4 and b.name = 'opened cursors current'
5 group by b.name,a.sid
6 having sum(a.value) > 0
7 /
SUM(A.VALUE) NAME SID
------------ ------------------------------ ----------
32 opened cursors current 140
5 opened cursors current 138
5 opened cursors current 132
4 opened cursors current 162
81 opened cursors current 154
29 opened cursors current 161
11 opened cursors current 134
23 opened cursors current 136
55 opened cursors current 144
25 opened cursors current 149
10 rows selected.
open_cursors - its purely related to Session, say if you set Open_cursor 1000, so there could be 10,000 of them if you have 10 sessions.
Subscribe to:
Posts (Atom)