Using Oracle Database 10g PL/SQL New Features
Using Oracle Database 10g
PL/SQL New Features
In this tutorial, you learn about the new PL/SQL features that
are introduced in Oracle Database 10g.
Approximately 30 minutes
Topics
This tutorial covers the following topics:
Place the cursor over this icon to load and view all
the screenshots for this tutorial. (Caution: Because this action loads all screenshots
simultaneously, response time may be slow depending on your Internet connection.)
Note: Alternatively, you can place the cursor
over each individual icon in the following steps to load and view only the screenshot
associated with that step.
In Oracle Database 10g,
several enhancements are available in the PL/SQL language. In this overview
section, you cover the following topics:
Bulk Binding
Enhancements
Oracle Database 10g
extends the enhancements that were introduced in Oracle9i
in the area of bulk binding. The SAVE
EXCEPTIONS syntax was introduced in Oracle9i
to capture exceptions while bulk inserting (deleting or updating) rows. Although
this feature enables the data manipulation language (DML) to continue (saving any exceptions in the SQL%BULK_EXCEPTIONS
collection), the performance of the operation is greatly affected. In a scenario
in which the collection being processed is sparse due to the application logic
involved, this would be an unnecessary overhead. In Oracle Database 10g,
you can overcome these issues with the new INDICES
OF and VALUES
OF features.
Back to List
Debugging PL/SQL with JDeveloper
Starting with version 9.0.3, JDeveloper supports PL/SQL debugging.
This is achieved with the Java Debugging Wire Protocol (JDWP). With Oracle9i,
release 2 (and later versions), the debugger uses the industry standard, whereas
for earlier releases (Oracle8i
and Oracle9i, release
1), the DBMS_DEBUG package is
used. The debugging UI gives you the ability to single step, step into, and
step over PL/SQL objects as well as the ability to set PL/SQL expressions in
the Watch and Inspector windows, set conditional breakpoints, and view collection
data.
Back to List
PL/SQL Compiler
Enhancements
The PL/SQL compiler is substantially improved in Oracle Database
10g. The back-end code
generator is reengineered. Also, the new parameter PLSQL_OPTIMIZE_LEVEL
has been introduced to speed up the execution of PL/SQL code. This is in addition
to native compilation (which has been available since Oracle9i).
The PLSQL_OPTIMIZE_LEVEL parameter
default is currently "2".
Back to List
Back to Topic List
Before starting this tutorial, you should have:
Back to Topic List
The following example illustrates the use of the INDICES
OF and VALUES
OF keywords for bulk binding
in PL/SQL.
The INDICES
OF keyword can be used in a scenario
in which a collection of records (which is dense) is validated programmatically
and invalid records (those not meeting the specified criterion) are removed
from the collection. This results in a sparse collection of valid elements that
must then be bulk inserted into a table. By using the INDICES
OF keyword, the exceptions for
the missing records are not generated.
The VALUES
OF keyword can be used in a scenario
in which a collection of records (sparse or dense) must be copied to one or
more collection variables, based on some condition whereby certain records
may or may not be copied, and then inserted into a table. This can be efficiently
done by using the "VALUES
OF" syntax and using a pointer
array whose elements are pointers to the selected records within the original
collection. This reduces the need to create multiple copies of data. Exception
handling is done per pointer record, which means that if two or more pointer
records are pointing to the same "original" data, any exceptions that
are reported indicate the iteration number (pointer element number).
This example uses the EMPLOYEES
table to build a collection of employees that is deliberately sparse.
| 1. |
You must first create a NEW_EMPLOYEES
table from the existing EMPLOYEES
table (in the hr schema)
and create a unique index on the EMPLOYEE_ID
column. This generates an exception when inserting a duplicate record.
Execute the following commands from a terminal window:
cd /home/oracle/wkdir
sqlplus hr/hr
@setup.sql
The setup.sql
script contains the following:
drop table new_employees;
create table new_employees as select * from employees where 1=2;
create unique index new_employees_employee_id
on new_employees (employee_id);

|
| 2. |
You next create the P_BULK_BIND
package. This package contains various procedures that create the sparse
collection and bulk insert the records into the NEW_EMPLOYEES
table using three different approaches.
@cr_p.sql
Click cr_p.sql
to see what is contained in this script.

|
| 3. |
To see how bulk binding works in Oracle Database 10g,
we first look at how it was done in Oracle9i. The FORALL
statement uses the SAVE EXCEPTIONS
syntax to suppress exceptions raised due to missing records, which are
later printed in the EXCEPTION
block. Execute the following commands:
set serverout on size 100000
exec p_bulk_bind.Bulk_Insert_Pre_10g (i_make_sparse => true)

-- this code is located in the Bulk_Insert_Pre_10g procedure
...
forall j in g_emp_recs.First()..g_emp_recs.Last()
save exceptions
insert into new_employees values g_emp_recs(j);
exception when bulk_errors then
for j in 1..sql%bulk_exceptions.Count()
loop
Dbms_Output.Put_Line ( 'Error from element #' ||
To_Char(sql%bulk_exceptions(j).error_index) || ': ' ||
Sqlerrm(SQL%bulk_exceptions(j).error_code) );
end loop;
...
From the output, you see that each missing record has
generated an exception (ORA-22160).
As mentioned earlier, even though the remaining records are processed
via the SAVE EXCEPTIONS
syntax, this results in a performance degradation if the number of deleted
records is fairly high.
|
| 4. |
The next step is to use the Oracle Database 10g
INDICES OF
syntax to ignore the missing records and thereby obtain much better performance.
Execute the following commands:
exec p_bulk_bind.Bulk_Insert_With_Indices_Of -
(i_cause_exception => false)
In this case, the Boolean input value is used to ignore
a block of code that is responsible for creating an exception condition
(as you will see in the next step).

-- this code is located in the Bulk_Insert_With_Indices_Of
-- procedure
...
forall j in indices of g_emp_recs
save exceptions
insert into new_employees values g_emp_recs(j);
...
This time there are no exceptions raised because of the
missing records.
|
| 5. |
The preceding procedure can also be run with the input
parameter set to true.
This causes an exception while inserting that must be trapped (by SAVE
EXCEPTIONS) and later processed. The condition causing the exception
is simulated via a procedure call, which nulls the EMAIL
columns for the employees with the last name of "Ernst" and
"Urman." Because the EMAIL
column has a NOT NULL restriction,
this causes an exception when the two corresponding records are inserted.
The exceptions are later processed from the SQL%BULK_EXCEPTIONS
collection. Execute the following commands:
exec p_bulk_bind.Bulk_Insert_With_Indices_Of -
(i_cause_exception => true)

-- this code is located in the exception area
-- in the Bulk_Insert_With_Indices_Of procedure
...
forall j in indices of g_emp_recs
-- between g_emp_recs.First() and g_emp_recs.Last()
-- optional
save exceptions
insert into new_employees values g_emp_recs(j);
exception when bulk_errors then
declare
v_iteration pls_integer;
n pls_integer;
k pls_integer;
begin
for j in 1..sql%bulk_exceptions.Count()
loop
v_iteration := SQL%bulk_exceptions(j).error_index;
Dbms_Output.Put_Line (
'Error on the ' || To_Char(v_iteration)
|| 'th iteration' );
...
Note: The error is reported for the iteration
number. This is new to the exception handling in Oracle Database 10g.
|
| 6. |
Another approach is to use the VALUES
OF clause to create a second
collection of pointers to the elements of the original array. Execute
the following commands:
exec p_bulk_bind.Bulk_Insert_With_Values_Of -
(i_null_email => true, i_violate_pk => false)
The Point_To_Sparse
procedure is used to create a collection of pointers based on the same
logic as before. That is, it points to the elements with the first occurrence
of each letter in the last name. It is called from the Bulk_Insert_With_Values_Of
procedure.

-- this code is located in the Bulk_Insert_With_Values_Of
-- procedure
...
forall j in values of g_values_of_tab
save exceptions
insert into new_employees values g_emp_recs(j);
...
The result is the same as in step 5: An exception is
raised for the fifth and nineteenth iterations.
|
| 7. |
To see the effect of reporting the iteration number
(or pointer element number) as opposed to the index of the erroneous record,
you can run the preceding procedure with i_violate_pk
=> true. To simulate the error condition, the Cause_Exception_For_Values_Of
procedure is used to insert a duplicate index entry from the bound collection
(G_EMP_RECS) into the pointer
collection (G_VALUES_OF_TAB).
Because of this, the unique key constraint (created in step 1) is violated
and the exception is raised upon bulk insert into the NEW_EMPLOYEES
table. Execute the following commands:
exec p_bulk_bind.Bulk_Insert_With_Values_Of -
(i_null_email => false, i_violate_pk => true)

-- this code is located in the Bulk_Insert_With_Values_Of
-- procedure
...
exception when bulk_errors then
declare
v_iteration pls_integer;
begin
for j in 1..sql%bulk_exceptions.Count()
loop
v_iteration := SQL%bulk_exceptions(j).error_index;
Dbms_Output.Put_Line (
'Error on the ' || To_Char(v_iteration) ||
'th iteration' );
-- Find the index of the offending element
-- from the iteration number
Dbms_Output.Put_Line (
'last_name for error element: ' ||
g_emp_recs(g_values_of_tab(v_iteration)).last_name );
Dbms_Output.Put_Line (
'Error was: ' ||
Sqlerrm(SQL%bulk_exceptions(j).error_code) );
end loop;
end;
...
|
Back to Topic List
Before you can use the PL/SQL debug capability in JDeveloper,
you must make sure that the HR user has the appropriate privileges. Do the following:
| 1. |
From your SQL*Plus session, execute the following commands:
sqlplus system/oracle
grant debug any procedure to hr;
grant debug connect session to hr;

|
Back to Topic List
To access PL/SQL, you must first create a database connection.
Perform the following steps:
| 1. |
Click the JDeveloper icon that you created in your panel.
|
| 2. |
Click the Connections tab in the Connections
- Navigator window. Then right-click Database and select New
Database Connection.

|
| 3. |
On the Welcome page, click Next.

|
| 4. |
Enter o10g1
for the connection name, and then click Next.

|
| 5. |
Enter hr
as both the username and the password. Then click Next.

|
| 6. |
Enter raclinux1.us.oracle.com
for Host Name, 1521
for JDBC Port, and
O10G1 for SID. Then click Next.

|
| 7. |
Test your connection by clicking Test Connection.

|
| 8. |
When you see the "Success!"
message, click Finish.

|
| 9. |
Your connection has been created.

|
Back to Topic List
Using the JDeveloper PL/SQL Debugger, you now run the PL/SQL
package P_BULK_BIND (which you
used previously). Perform the following steps:
| 1. |
From the Connections - Navigator window, select Connections > Database >
o10g1 >
HR >
Packages and then right-click
the P_BULK_BIND package body. Select Open to view the PL/SQL
code.

|
| 2. |
You must compile this package body. Right-click the
P_BULK_BIND package body again and select Compile.

|
| 3. |
If the toggle numbers before each line of code is not
yet displayed, right-click in the Code Editor margin and select Toggle
Line Numbers.

|
| 4. |
To set the breakpoint, scroll down to the statement
forall j
in indices
of g_emp_recs
(at approximately line 171) and click the line number.

|
| 5. |
To start debugging, right-click in the Code Editor window
and select Debug.

|
| 6. |
The Debug PL/SQL window appears, which displays the
three global procedures within the package. Select the BULK_INSERT_WITH_INDICES_OF
procedure. This procedure takes one input parameter, which is the Boolean
flag I_CAUSE_EXCEPTION.
The value for this parameter is set to TRUE.
In the PL/SQL block area, change
I_CAUSE_EXCEPTION := NULL;
to
I_CAUSE_EXCEPTION := TRUE;
Then click OK.

|
| 7. |
The debugger executes the statements up to the first
breakpoint. At this point, the breakpoint statement to be executed next
is highlighted. The Stack window displays the procedure being executed.
Click the Data tab to display the variables and their values in
the current procedure.

|
| 8. |
Expand Package body variables of P_BULK_BIND.
You see two variables: G_EMP_RECS
and G_VALUES_OF_TAB.
G_EMP_RECS is the PL/SQL table used by the application to bulk
insert data into the NEW_EMPLOYEES
table.

|
| 9. |
Expand G_EMP_RECS
> _values
> [25]
> _value.
Notice that _value shows
the actual record values and that the EMAIL
column is null for this record. To observe the effect, click the Resume
button (green button on the main toolbar).

|
| 10. |
The procedure is now executed to the end. The Log window
displays program output, at the end of which are the error messages for
the two records for which insert failed.

|
| 11. |
To debug the problem, you can start the debugger with
a conditional breakpoint. The goal is to halt execution in the exception
block for the erroneous record. For this, set a breakpoint at the line
if n = v_iteration. Click
line number 194.

|
| 12. |
Select View > Debugger > BreakPoints.

|
| 13. |
Right-click the breakpoint $Oracle.PackageBody.HR.P_BULK_BIND.pls
194. Then select Edit.

|
| 14. |
Click the Conditions tab.

|
| 15. |
In the Condition field, enter n
= v_iteration. Only when this condition is evaluated to true
does the debugger stop at the breakpoint. Then click OK.

|
| 16. |
Start the debugger again. Right-click in the Code Editor
window and select Debug.

|
| 17. |
In the Debug PL/SQL window, select the BULK_INSERT_WITH_INDICES_OF
procedure. I_CAUSE_EXCEPTION
should still be set to TRUE.
Click OK.

|
| 18. |
Execution halts when the erroneous record is reached
(while processing the exceptions). Expand G_EMP_RECS
> _values
> [25]
> _value.
Notice that _value shows
the actual record values and that the EMAIL
column is null for this record. Right-click the EMAIL column and select Modify Value.

|
| 19. |
Enter BERNST as the New Value and click OK.

|
| 20. |
Click Resume.

|
| 21. |
The second breakpoint is reached. Click Resume.

|
| 22. |
The results are displayed. Notice that the record with
the last name ERNST is no longer an error because the EMAIL column is not
null any longer.

|
Back to Topic List
In this tutorial, you've learned how to:
 |
Examine the bulk binding feature |
 |
Debug a PL/SQL package body with JDeveloper |
Back to Topic List
 |
To learn more about
Oracle Database 10g, refer to additional OBEs on the OTN
Web site. |
 |
To ask a question about this OBE tutorial,
post a query on the OBE
Discussion Forum. |
Back to Topic List
Place the cursor on this icon to hide all screenshots.
|