encapsulating bulk pl/sql exceptions

Upload: balajismith

Post on 14-Apr-2018

235 views

Category:

Documents


0 download

TRANSCRIPT

  • 7/29/2019 encapsulating bulk pl/sql exceptions

    1/22

    Search

    Home

    Art icles

    11g New Fea tures

    10g New Features

    9i New Features

    8i New Features

    Miscellaneous

    Utilities

    Links

    Subscribe

    Disclaimer

    oracle-developer.net

    encapsulating bulk pl/sql exceptions

    One of the features of bulk PL/SQL processing is the SAVE EXCEPTIONS extension to FORALL. This clause has been available

    since Oracle 9i and instructs Oracle to skip any exceptions it might encounter during a FORALL DML operation. It enables us to

    continue processing a batch of data to completion rather than fail the entire statement. For example, we might be inserting 500

    records using FORALL and if 1 record raises an exception, the remaining 499 rows will be successfully loaded. The bad row will

    be "set aside" for post-processing.

    Most systems have an error-logging mechanism to write details of processing failures to files, tables or even queues. Most

    commonly this mechanism comprises a centralised error package and error-logging table, which will typically contain informationsuch as timestamp, business date, failing package/procedure/function, some details on the nature of the exception (such as

    SQLERRM) and the keys to the source data that caused the exception (where applicable). Exception handling in these cases is

    quite simple: each procedure makes a single call to the error package, which in turns writes the exception details to the

    table/file/queue and optionally raises it.

    One of the "features" of the SAVE EXCEPTIONS clause, however, is that the exception handling is quite code-intensive (i.e. we

    need to write quite a few lines of code to process the exceptions data). An example of this can be found in this oracle-

    developer.net article on 9i bulk PL/SQL features. It therefore makes sense for us to try to encapsulate this processing in an

    error-logging package and this article will suggest two methods for this, using the following Oracle features:

    ANYDATA; an d

    type substitution ("polymorphism").

    It is assumed that readers are familiar with these features of Oracle. For some background on these techniques, begin by

    rea din g the oracle-de veloper.net articles on ANYDATA an d type enhancements in 9i (in particular the section on type

    polymorphism).

    The examples in this article have been tested on 9i Release 2 (9.2) and should also work on any version o f 10g.

    PDFmyURL.com

    http://www.oracle-developer.net/disclaimer.phphttp://www.oracle-developer.net/links.phphttp://www.oracle-developer.net/utilities.phphttp://www.oracle-developer.net/misc.phphttp://www.oracle-developer.net/8i.phphttp://www.oracle-developer.net/10g.phphttp://www.oracle-developer.net/display.php?id=218http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://www.oracle-developer.net/display.php?id=219http://www.oracle-developer.net/display.php?id=218http://www.oracle-developer.net/display.php?id=201http://www.oaktable.net/membershttp://apex.oracle.com/pls/otn/f?p=19297:4:2680396891503574::NO:4:P4_ID:274http://www.oracle-developer.net/disclaimer.phphttp://www.oracle-developer.net/feed.xmlhttp://www.oracle-developer.net/links.phphttp://www.oracle-developer.net/utilities.phphttp://www.oracle-developer.net/misc.phphttp://www.oracle-developer.net/8i.phphttp://www.oracle-developer.net/9i.phphttp://www.oracle-developer.net/10g.phphttp://www.oracle-developer.net/11g.phphttp://www.oracle-developer.net/index.php
  • 7/29/2019 encapsulating bulk pl/sql exceptions

    2/22

    setup: a simple error logger

    As state d, we will be en cap sul atin g FO RALL .. SAVE EXCEPTIONS ha nd lin g i n a n e rro r pack ag e. We will star t by b ui ld ing th e e rro r

    logging application that we wish to extend to include the new bulk handler. Like most systems, this package will record

    processing exceptions in an errors table. To keep this as simple as possible, we will exclude the rollback/raise management that

    such an error package should ideally encapsulate. We will begin by creating a simple error logging table (note that keys,

    constraints, indexes etc are deliberately ignored a s they add nothing to the examples).

    SQL> CREATETABLE errors

    2 ( package_owner VARCHAR2(30)

    3 , package_name VARCHAR2(30)

    4 , procedure_nameVARCHAR2(30)

    5 , action VARCHAR2(100)

    6 , business_date DATE

    7 , business_key VARCHAR2(1000)

    8 , error_ts TIMESTAMP

    9 , error_msg VARCHAR2(4000)

    10 );

    Table created.

    Now we can create the simple error pa ckage. At this stage it doesn't handle bu lk exceptions.

    SQL> CREATEPACKAGE errorAS

    2 PROCEDURElog( p_owner IN errors.package_owner%TYPE,

    3 p_package IN errors.package_name%TYPE,

    4 p_procedure IN errors.procedure_name%TYPE,

    5 p_action IN errors.action%TYPE,

    6 p_business_date IN errors.business_date%TYPE,

    7 p_business_key IN errors.business_key%TYPEDEFAULTNULL,

    8 p_error INVARCHAR2DEFAULTNULL );

    9 END;

    10 /

    Package created.

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01
  • 7/29/2019 encapsulating bulk pl/sql exceptions

    3/22

    So far we have a single procedure to log a single exception. The implementation of this logging procedure will typically be

    constructed as follows.

    SQL> CREATEPACKAGEBODY errorAS

    2

    3 PROCEDURElog( p_owner IN errors.package_owner%TYPE,

    4 p_package IN errors.package_name%TYPE,

    5 p_procedure IN errors.procedure_name%TYPE,

    6 p_action IN errors.action%TYPE,

    7 p_business_date IN errors.business_date%TYPE,

    8 p_business_key IN errors.business_key%TYPEDEFAULTNULL,

    9 p_error INVARCHAR2DEFAULTNULL ) IS

    10

    11 v_error errors.error_msg%TYPE :=NVL(p_error,SQLERRM);

    12

    13 PRAGMAAUTONOMOUS_TRANSACTION;

    14

    15 BEGIN

    16

    17 INSERTINTO errors

    18 ( package_owner, package_name, procedure_name, action,

    19 business_date, business_key, error_ts, error_msg )

    20 VALUES

    21 ( p_owner, p_package, p_procedure, p_action,

    22 p_business_date, p_business_key, SYSTIMESTAMP, v_error );

    23

    24 COMMIT;

    25

    26 ENDlog;

    2728 END error;

    29 /

    Package body created.

    Before we move onto the main subject of this article, we can see how the error logger might typically be used in a "traditional"

    PL/SQL data processing routine. Note that the following anonymous block is an approximation of a daily processing procedure.

    Elements such as business date will usually be passed as parameters.

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01
  • 7/29/2019 encapsulating bulk pl/sql exceptions

    4/22

    SQL> DECLARE

    2

    3 v_package errors.package_name%TYPE := 'ANON. BLOCK';

    4 v_procedure errors.procedure_name%TYPE := 'ANON. BLOCK';

    5 v_action errors.action%TYPE;

    6 v_business_date errors.business_date%TYPE := TRUNC(SYSDATE)-1;

    7 v_business_key errors.business_key%TYPE;

    8

    9 BEGIN

    10

    11 v_action := 'Process source data';

    12 FORr IN ( SELECT object_id AS key_attr1

    13 , object_nameAS key_attr2

    14 , object_typeAS data_attr1

    15 FROM user_objects )

    16 LOOP

    17

    18 v_action := 'Assign business key';

    19 v_business_key := r.key_attr1 || ',' || r.key_attr2;

    20

    21 v_action := 'Transformation and business rules';

    22 r.data_attr1 := RPAD('Oops',4000); -- USER,

    29 p_package => v_package,30 p_procedure => v_procedure,

    31 p_action => v_action,

    32 p_business_date => v_business_date,

    33 p_business_key => v_business_key );

    34

    35 ROLLBACK; --

  • 7/29/2019 encapsulating bulk pl/sql exceptions

    5/22

    38 /

    DECLARE

    *

    ERROR at line 1:

    ORA-06502: PL/SQL: numeric or value error: character string buffer too small

    ORA-06512: at line 36

    We can see that the exception handling is simplified by the error package, especially if it encapsulates ROLLBACK and RAISE

    logic as well (which typically it should though for simplicity we've ignored it for this article). The aim is to hand-off al l exception

    handling mechanics to the error package.

    Finally, using Tom Kyte's print_table procedure to make the format easier to read, we can see our single logged exception as

    follows.

    SQL> exec print_table('SELECT * FROM errors');

    PACKAGE_OWNER : SCOTT

    PACKAGE_NAME : ANON. BLOCK

    PROCEDURE_NAME : ANON. BLOCK

    ACTION : Transformation and business rules

    BUSINESS_DATE : 25-jul-2007 00:00:00

    BUSINESS_KEY : 33944,ANOTHER_SUBTYPE_OT

    ERROR_TS : 26-JUL-07 18.24.37.640000

    ERROR_MSG : ORA-06502: PL/SQL: numeric or value error: character string buffer too small

    -----------------

    PL/SQL procedure successfully completed.

    Now that we have some context (i.e. we have an error logging package in place), we can move on to how we might log errors in

    bulk.

    encapsulating bulk exceptions

    Using the error logging framework that we ha ve created above, we could quite simply decide to manage exception log ging in

    every proce ssing rou tine we write. We would n eed to cod e a loo p through SQL%BULK_EXCEPTIONS, determine the bad b usiness

    data and make associated calls to the ERROR.LOG procedure to record the exceptional data. However, this would be a lot of

    repetition across multiple procedures and, as stated in the introduction, this can be code-intensive. It is far better, therefore, to

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pages.citebite.com/i9q5m2c1fflg
  • 7/29/2019 encapsulating bulk pl/sql exceptions

    6/22

    encapsulate this extension of exception handling , as we shall now see.

    There are two primary elements we need to consider for this encapsulation. First, we need to process the

    SQL%BULK_EXCEPTIONS pseudo-array that Oracle populates following an exception with a FORALL statement (with or without

    SAVE EXCEPTIONS). Second, we use the metadata in this pseudo-array to determine the locations of the exceptional business

    data and the exceptions themselves. This means that an encapsulated error logge r will need to accept an array of business data

    in any format . It is this req uirement that lea ds us to the two Oracle fea tures describ ed in the introdu ction; namely ANYDATA and

    po lymorp hism. We will b eg in with ANYDATA.

    encapsulating with anydata

    ANYDATA is a bu ilt- in type tha t ha s be en ava ila bl e sin ce Oracle 9i . We can use ANYDATA as a con taine r to store an y form of

    structured data for which we have a named SQL type (either built-in or user-defined). We can exploit this feature to encapsulate

    generic collection handling (such as that required for bulk exceptions).

    We will add the following components to our existing error logging application:

    a gen eric collection type o f VARCHAR2(4000 ) to be used in the implementation;

    a gen eric collection type o f NUMBER to be used i n the implementation; and

    an o verload ed ERROR.LOG proced ure to a ccept an ANYDATA parameter (of the busine ss data we were proce ssing a t the

    time of failure).

    We will begin by creating the generic collection types that will "assist" with the encapsulation as follows.

    SQL> CREATEORREPLACETYPE varchar2_nttASTABLEOFVARCHAR2(4000);

    2 /

    Type created.

    SQL> CREATEORREPLACETYPE number_nttASTABLEOFNUMBER;

    2 /

    Type created.

    It will become clear how these are used when we add our encapsulation to the ERROR package. First we will add an overloaded

    LOG procedure to the package specification. For brevity, the original LOG procedure is removed from the output (though it still

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01
  • 7/29/2019 encapsulating bulk pl/sql exceptions

    7/22

    exists of course).

    SQL> CREATEORREPLACEPACKAGE errorAS

    2

    3 PROCEDURElog( ...snip...

    10

    11 bulk_exceptions EXCEPTION;

    12 PRAGMAEXCEPTION_INIT(bulk_exceptions, -24381);

    13

    14 PROCEDURElog( p_owner IN errors.package_owner%TYPE,

    15 p_package IN errors.package_name%TYPE,

    16 p_procedure IN errors.procedure_name%TYPE,

    17 p_action IN errors.action%TYPE,

    18 p_business_date IN errors.business_date%TYPE,

    19 p_business_data INANYDATA );

    20

    21 END;

    22 /

    Package created.

    This overloaded LOG procedure takes similar parameters to the original, single-row version, with the key difference being that we

    now have the abili ty to pa ss in a co llection of bu siness da ta via the ANYDATA type. This busine ss data will be held in the

    collection that we are processing when FORALL .. SAVE EXCEPTIONS is invoked (i.e. when we hit an exception). We have also

    encapsulated the exception that Oracle raises when this happens (ORA-24381).

    We can now implement the overloaded LOG procedure by re-creating the package body as follows. Again, the original LOG

    proced ure is removed from the output for brevity.

    SQL> CREATEORREPLACEPACKAGEBODY errorAS

    2

    3 PROCEDURElog( ...snip...

    27

    28 PROCEDURElog( p_owner IN errors.package_owner%TYPE,

    29 p_package IN errors.package_name%TYPE,

    30 p_procedure IN errors.procedure_name%TYPE,

    31 p_action IN errors.action%TYPE,

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01
  • 7/29/2019 encapsulating bulk pl/sql exceptions

    8/22

    32 p_business_date IN errors.business_date%TYPE,

    33 p_business_data INANYDATA ) IS

    34

    35 v_plsql VARCHAR2(1024); --

  • 7/29/2019 encapsulating bulk pl/sql exceptions

    9/22

    71 || ' o.EXTEND; '

    72 || ' o(o.LAST) := c(x(i)).print_key; '

    73 || ' END LOOP; '

    74 || ' :b3 := o; '

    75 || 'END; ';

    76

    77 /*

    78 || Execute the PL/SQL string to return the bad data keys...

    79 */80 EXECUTEIMMEDIATE v_plsql USINGIN p_business_data,

    81 IN nt_error_indices,

    82 OUT nt_business_keys;

    83

    84

    85 /*

    86 || Now we can log the errors...

    87 */

    88 FORi IN 1 .. SQL%BULK_EXCEPTIONS.COUNTLOOP

    89

    90 error.log( p_owner => p_owner,

    91 p_package => p_package,

    92 p_procedure => p_procedure,

    93 p_action => p_action,

    94 p_business_date => p_business_date,

    95 p_business_key => nt_business_keys(i),

    96 p_error => SQLERRM(-1*SQL%BULK_EXCEPTIONS(i).error_code) );

    97

    98 ENDLOOP;

    99

    100 ENDlog;

    101

    102 END error;

    103 /

    Package body created.

    Note the following elements of the bulk exceptions wrapper above:

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01
  • 7/29/2019 encapsulating bulk pl/sql exceptions

    10/22

    Line 4 6: we determine the n ame of the collection type that defines the data "i nside" the ANYDATA instance. We will need

    to use this in a dynamic PL/SQL block to access the ANYDATA;

    Lines 51-54 : we set-aside the offsets of the elements in the business data collection that raised exceptions. This is

    becau se the Native Dyna mic PL/SQL block that follows can not reference SQL%BULK_EXCEPTIONS in this context an d will

    need some way of identifying these inde x values;

    Lines 6 2-75: we build a Native Dynamic PL/SQL block to retrieve the exception al bu siness ke ys from the ANYDATA

    instance. We start by retrieving the ANYDATA instance i nto a va riable of the correct un derlying collection type. Using the

    error index collection we built earlier, the dynamic block loops through the business collection and derives the keys of the"bad" business data. These keys are added to a collection to be pa ssed as an OUT bind variable;

    Lines 72: we ensure that all collection types used in FORALL .. SAVE EXCEPTIONS constructs have a PRINT_KEY()

    member function to simlify the retrieval of busine ss key values;

    Lines 80-82: we execute the dynamic PL/SQL block and receive a collection of business keys for "bad" records as an

    OUT parameter; and

    Lines 88-98: we process the BULK_EXCEPTIONS pseu do- array an d invo ke the orig inal ERROR.LOG proced ure for each

    exception we encounter, including all of the critical information we have extracted (including business keys).

    We now have an extension to our error logger/handler that enables us to work with bulk PL/SQL and capture the exceptionswithout having to repeatedly code a complicated exception block. To test this, we will build a dummy customer table and load it

    with bulk PL/SQL, ensurin g we have so me "bad " da ta. We will begin by creating a CUSTOMERS table as follows.

    SQL> CREATETABLE customers

    2 ( customer_id NUMBERPRIMARYKEY

    3 , first_name VARCHAR2(30)

    4 , last_name VARCHAR2(50)

    5 , start_date DATE

    6 );

    Table created.

    One of the pre- requisites o f being a ble to pa ss around collections o f records with ANYDATA is that we use SQL object and

    collection types (i.e. types creates using the CREATE TYPE... syntax). Most developers will be familiar with using PL/SQL types

    (records and associative arrays that are declared in a package or procedure) for array processing. It is not much of a diversion

    to use objects and collections and, in some cases, using the SQL types provides greater flexibility than the PL/SQL-only type

    structures. Given this, we will now crea te a customer obje ct to define a " record" of CUSTOMERS source d ata as follows.

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01
  • 7/29/2019 encapsulating bulk pl/sql exceptions

    11/22

    SQL> CREATETYPE customer_otASOBJECT

    2 ( customer_id NUMBER

    3 , first_name VARCHAR2(30)

    4 , last_name VARCHAR2(50)

    5 , start_date DATE

    6 ,MEMBERFUNCTION print_key RETURNVARCHAR2

    7 );

    8 /

    Type created.

    SQL> CREATETYPEBODY customer_otAS

    2 MEMBERFUNCTION print_key RETURNVARCHAR2IS

    3 BEGIN

    4 RETURNTO_CHAR(SELF.customer_id);

    5 END;

    6 END;

    7 /

    Type body created.

    Note that we have included a member function named PRINT_KEY. This is for convenience. Remember that the dynamic PL/SQL

    block in the error logger will invoke this to extract the business keys of the exceptional data. To work with multiple records of

    customer data, we must create a collection type o f the customer "reco rd", which we do as follo ws.

    SQL> CREATETYPE customer_nttASTABLEOF customer_ot;

    2 /

    Type created.

    We now ha ve the elements we req uire to test the encap sulated FORALL .. SAVE EXCEPTIONS ha ndler. The following anon ymous

    block represents a batch load of CUSTOMERS. We will fetch the source data first and then manufacture two duplicate records to

    ensure our subsequent FORALL .. SAVE EXCEPTIONS construct hits some exceptions. Note that we would usually expect this

    load to include some complex transformations between the fetch and load stages (else we would b e using bulk SQL and not

    PL/SQL), but these are assumed a nd o mitted for b revity.

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01
  • 7/29/2019 encapsulating bulk pl/sql exceptions

    12/22

    SQL> DECLARE

    2

    3 v_package errors.package_name%TYPE := 'ANYDATA_ENCAPSULATION';

    4 v_procedure errors.procedure_name%TYPE := 'CUSTOMER_LOAD_EG';

    5 v_action errors.action%TYPE;

    6 v_business_date errors.business_date%TYPE := TRUNC(SYSDATE)-1;

    7 nt_customer_data customer_ntt := customer_ntt();

    8

    9 BEGIN10

    11 v_action := 'Fetch source data';

    12 SELECT customer_ot(

    13 object_id, --

  • 7/29/2019 encapsulating bulk pl/sql exceptions

    13/22

    40 WHEN error.bulk_exceptions THEN

    41 error.log( p_owner => USER,

    42 p_package => v_package,

    43 p_procedure => v_procedure,

    44 p_action => v_action,

    45 p_business_date => v_business_date,

    46 p_business_data =>ANYDATA.convertCollection(nt_customer_data) );

    47

    48 ROLLBACK; --

  • 7/29/2019 encapsulating bulk pl/sql exceptions

    14/22

    SQL> exec print_table('SELECT * FROM errors');

    PACKAGE_OWNER : SCOTT

    PACKAGE_NAME : ANYDATA_ENCAPSULATION

    PROCEDURE_NAME : CUSTOMER_LOAD_EG

    ACTION : Customer load

    BUSINESS_DATE : 31-jul-2007 00:00:00

    BUSINESS_KEY : 17286

    ERROR_TS : 01-AUG-07 18.25.11.859000ERROR_MSG : ORA-00001: unique constraint (.) violated

    -----------------

    PACKAGE_OWNER : SCOTT

    PACKAGE_NAME : ANYDATA_ENCAPSULATION

    PROCEDURE_NAME : CUSTOMER_LOAD_EG

    ACTION : Customer load

    BUSINESS_DATE : 31-jul-2007 00:00:00

    BUSINESS_KEY : 7559

    ERROR_TS : 01-AUG-07 18.25.11.859000

    ERROR_MSG : ORA-00001: unique constraint (.) violated

    -----------------

    PL/SQL procedure successfully completed.

    To summarise, therefore, the key elements of the encapsulation of FORALL .. SAVE EXCEPTIONS with ANYDATA are as follows:

    each business loa d package requires an ad ditional object type and a collection type of this object. The object type

    represents a single record of data that is to be loaded to the target table. The collection is simply an array of this record

    structure. The obje ct type contains a PRINT_KEY member function to simplify access to the b usine ss data that we wish to

    log (in our example we have just printed the record's primary key);

    any e xceptions we e ncoun ter during FORALL bulk processin g are p assed o ff to the ERROR.LOG API as an instance o f

    ANYDATA; an d

    the ERROR.LOG proced ure is overloa ded to accep t a SYS.ANYDATA parameter. This para meter stores a co llection of the

    business data that was being loaded at the time of hitting the exception(s). This business data can be of any structure, as

    defined by relevant unde rlying object and collection types.

    encapsulating with t ype substit utio n

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01
  • 7/29/2019 encapsulating bulk pl/sql exceptions

    15/22

    In the previous section, we saw that ANYDATA enables us to encapsulate the processing of the SQL%BULK_EXCEPTIONS

    pseudo-array and the related business data. One of the drawbacks of the ANYDATA method is that it requires dynamic PL/SQL

    that will be generated and executed every time the error logger is called. An alternative to this method is to use type substitution

    (known as po lymorphism) and we will now build an exa mple of how we might implement this.

    Briefly, polymorphism enables us to create a hierarchy of types and use any of the subtypes wherever their respective

    supertypes are expected. We can take advantage of this to build a data-loading framework that utilises a single supertype as a

    consistent parameter type and yet uses underlying types of different structures for specific load targets. In relation to our

    encap sulated FORALL .. SAVE EXCEPTIONS han dler, we will create a sin gle " gene ric" loa d type a s follows.

    SQL> CREATETYPE bulk_load_otASOBJECT

    2 ( generic_idNUMBER

    3 ,MEMBERFUNCTION print_key RETURNVARCHAR2

    4 )NOTFINALNOTINSTANTIABLE;

    5 /

    Type created.

    Note how we define this type as being NOT FINAL and NOT INSTANTIABLE. The former means we have not yet completed the

    implementation of the type hierarchy (of which BULK_LOAD_OT is a supertype) and the latter means that we will not be able to

    directly use this type for variables in our PL/SQL programs. We create this type with a single ID attribute that will be inherited by

    all subtypes and also a PRINT_KEY member function, as before, that will print the current value of the business key contained

    within the data structure.

    As we ha ve a membe r fu ncti on , we must al so ha ve a type bo dy. Ofte n with po lymo rph ism, we migh t e xclud e a type bo dy in the

    supertype and instead include an overriding function in every subtype. For simplicity, we will create one function in the

    BULK_LOAD_OT supertype a s follows and allo w all subtypes to inhe rit and use this function.

    SQL> CREATETYPEBODY bulk_load_otAS2 MEMBERFUNCTION print_key RETURNVARCHAR2IS

    3 BEGIN

    4 RETURNTO_CHAR(SELF.generic_id);

    5 END;

    6 END;

    7 /

    Type body created.

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01
  • 7/29/2019 encapsulating bulk pl/sql exceptions

    16/22

    Finally, because we are dealing with collections of data and not single records, we will create a collection type based on our

    BULK_LOAD_OT obje ct.

    SQL> CREATETYPE bulk_load_nttASTABLEOF bulk_load_ot;

    2 /

    Type created.

    We now have the elements we require to implement another version of the ERROR.LOG procedure. The ERROR package

    specification is as follows (the origin al LOG procedure d efinition is cut for brevity).

    SQL> CREATEORREPLACEPACKAGE errorAS

    2

    3 PROCEDURElog( ...snip...

    10

    11 bulk_exceptions EXCEPTION;

    12 PRAGMAEXCEPTION_INIT(bulk_exceptions, -24381);

    13

    14 PROCEDURElog( p_owner IN errors.package_owner%TYPE,

    15 p_package IN errors.package_name%TYPE,

    16 p_procedure IN errors.procedure_name%TYPE,

    17 p_action IN errors.action%TYPE,

    18 p_business_date IN errors.business_date%TYPE,

    19 p_business_data IN bulk_load_ntt );

    20

    21 END;

    22 /

    Package created.

    The specification differs from the ANYDATA version only by the type of the p_business_data parameter. This time, we use the

    BULK_LOAD_NTT type for the busine ss data, which is a collection type ba sed on BULK_LOAD_OT. As we will see later, this mean s

    that the collection ca n contain data of an y subtype in a type hie rarchy created unde r BULK_LOAD_OT. Before we can se e this, we

    must create the package body for our new LOG overload as follows.

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01
  • 7/29/2019 encapsulating bulk pl/sql exceptions

    17/22

    SQL> CREATEORREPLACEPACKAGEBODY errorAS

    2

    3 PROCEDURElog( ...snip...

    27

    28 PROCEDURElog( p_owner IN errors.package_owner%TYPE,

    29 p_package IN errors.package_name%TYPE,

    30 p_procedure IN errors.procedure_name%TYPE,

    31 p_action IN errors.action%TYPE,

    32 p_business_date IN errors.business_date%TYPE,

    33 p_business_data IN bulk_load_ntt ) IS

    34

    35 v_error_index PLS_INTEGER;

    36 v_error_code PLS_INTEGER;

    37

    38 BEGIN

    39

    40 /*

    41 || Simply log the errors...

    42 */

    43 FORi IN 1 .. SQL%BULK_EXCEPTIONS.COUNTLOOP44

    45 v_error_index := SQL%BULK_EXCEPTIONS(i).error_index;

    46 v_error_code := SQL%BULK_EXCEPTIONS(i).error_code;

    47

    48 error.log( p_owner => p_owner,

    49 p_package => p_package,

    50 p_procedure => p_procedure,

    51 p_action => p_action,

    52 p_business_date => p_business_date,

    53 p_business_key => p_business_data(v_error_index).print_key(),

    54 p_error => SQLERRM(-1 * v_error_code) );

    55

    56 ENDLOOP;

    57

    58 ENDlog;

    59

    60 END error;

    61 /

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01
  • 7/29/2019 encapsulating bulk pl/sql exceptions

    18/22

    Package body created.

    We can see immediately that this implementation is far simpler than the ANYDATA example from earlier. We are working with a

    known col lection type (BULK_LOAD_NTT) and we a lso kno w that each " record" in this col lection will ha ve a PRINT_KEY member

    function. These two factors make the processing of the SQL%BULK_EXCEPTIONS pseudo-array and the probing of the business

    data collection much easier. The resulting code is therefore short, simple and self-explanatory.

    We are now ready to test our implementation. We can see that the ERROR.LOG encapsulation expects an instance of

    BULK_LOAD_NTT. By using type substitution, we can pass in a collection of any subtype that is defined under BULK_LOAD_OT.

    Using o ur CUSTOMERS example from earlier, we will now cre ate a CUSTOMER_OT subtype u nder BULK_LOAD_OT as follows.

    SQL> CREATETYPE customer_ot UNDERbulk_load_ot

    2 ( first_name VARCHAR2(30)

    3 , last_name VARCHAR2(50)

    4 , start_date DATE

    5 );

    6 /

    Type created.

    This subtype inherits the GENERIC_ID attribute and PRINT_KEY member function from the BULK_LOAD_OT supertype. It

    represe nts a "record" of customer-specific data, yet can be u sed whereve r a BULK_LOAD_OT record is exp ected. We will test this

    using the same exa mple that we used to demonstrate the ANYDATA method. In other words, we will bulk fetch some " source" data,

    manufacture two exceptions and load the CUSTOMERS table using FORALL .. SAVE EXCEPTIONS.

    SQL> DECLARE

    2

    3 v_package errors.package_name%TYPE := 'SUBTYPE_ENCAPSULATION';

    4 v_procedure errors.procedure_name%TYPE := 'CUSTOMER_LOAD_EG';5 v_action errors.action%TYPE;

    6 v_business_date errors.business_date%TYPE := TRUNC(SYSDATE)-1;

    7 nt_customer_data bulk_load_ntt := bulk_load_ntt();

    8

    9 BEGIN

    10

    11 v_action := 'Fetch source data';

    12 SELECT customer_ot(

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01
  • 7/29/2019 encapsulating bulk pl/sql exceptions

    19/22

    13 object_id, -- v_action,

    45 p_business_date => v_business_date,

    46 p_business_data => nt_customer_data );

    47

    48 ROLLBACK; --

  • 7/29/2019 encapsulating bulk pl/sql exceptions

    20/22

    ERROR:

    ORA-24381: error(s) in array DML

    ORA-06512: at line 49

    Warning: PL/SQL compilation errors.

    As befo re, ou r two exc ep tion s g en erate d the Ora cle err or we exp ecte d. The re are a few smal l d iffer en ces b etwe en this example

    and the ANYDATA example, most no tably the following:

    Line 7: we are fetching into and load ing from a colle ction of the BULK_LOAD_NTT type, rather than a sp ecific customer

    data collection;

    Line 12: we use the CUSTOMER_OT constructor to conve rt the source da ta columns into the co rrect format for bulk

    fetching in to the bu siness d ata colle ction. Because CUSTOMER_OT is a sub type of BULK_LOAD_OT, it can be used in the

    BULK_LOAD_NTT collection i nstance (this is the be nefit of type substitution);

    Line 37: becau se we ha ve sub stituted o ur CUSTOMER_OT data in to a BULK_LOAD_NTT collection , Oracle now co nsiders

    each element in o ur colle ction to be o f the BULK_LOAD_OT structure. We must tell Oracle that we have in fact loaded the

    collection with multiple in stances of CUSTOMER_OT instead (u sing type su bstitution). We do this when we acce ss the data

    by usin g the TREAT function to "do wncast" the da ta to its correct subtype. Quite simply, we have con verted the type b oth

    on the "way in" a nd on the " way out" of the collection.

    Finally, we can confirm that our encapsulation works by checking the ERRORS table (this was truncated before the previous

    example was executed).

    SQL> exec print_table('SELECT * FROM errors');

    PACKAGE_OWNER : SCOTT

    PACKAGE_NAME : SUBTYPE_ENCAPSULATION

    PROCEDURE_NAME : CUSTOMER_LOAD_EG

    ACTION : Customer load

    BUSINESS_DATE : 31-jul-2007 00:00:00

    BUSINESS_KEY : 17286

    ERROR_TS : 01-AUG-07 18.27.49.625000

    ERROR_MSG : ORA-00001: unique constraint (.) violated

    -----------------

    PACKAGE_OWNER : SCOTT

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01
  • 7/29/2019 encapsulating bulk pl/sql exceptions

    21/22

    PACKAGE_NAME : SUBTYPE_ENCAPSULATION

    PROCEDURE_NAME : CUSTOMER_LOAD_EG

    ACTION : Customer load

    BUSINESS_DATE : 31-jul-2007 00:00:00

    BUSINESS_KEY : 7559

    ERROR_TS : 01-AUG-07 18.27.49.625000

    ERROR_MSG : ORA-00001: unique constraint (.) violated

    -----------------

    PL/SQL procedure successfully completed.

    In summary, the key elemen ts of the type substitution method for encap sulating b ulk excep tions are listed be low.

    we have a single "top-level" generic object type and collection type of this object. This single supertype is used to define

    gen eric parameters where the in coming data might be o f different structures. The object type has a sing le ID attribute to

    be use d by subtypes a s appro priate and also a PRINT_KEY member function for simplifying acce ss to the business da ta;

    each business loa d package requires its own object type to be created as a subtype of the single supe rtype. Each

    subtype represents a single record of data that is to be loaded to the target table and replaces the use of a PL/SQL

    record. The sub types can op tionally ove rride the PRINT_KEY member function if they need to access data other than tha t

    contained in the single ID attribute;

    each business loa d package uses its own object type to structure each record b ut stores these in a variable of the

    generic collection type. This is made possible by type substitution and enables a single data type to be p assed to generic

    APIs;

    any e xceptiona l busin ess data e ncoun tered du ring the FORALL processing is passed off to the ERROR.LOG API in a

    generic collection; and

    the ERROR.LOG procedu re is ove rloade d to acce pt a BULK_LOAD_NTT parameter. This parameter stores a collection of

    the business data that was being loaded at the time of hitting the exception(s). This business data can be of any subtype

    structure in the ove rall type hie rarchy that exists unde r the BULK_LOAD_OT supertype. In our e xample, we load ed

    customer data via this mecha nism, but this could eq ually b e of any o ther data format (ACCOUNTS, SALES and so o n) as

    required.

    summary

    We have seen two methods for encapsulating SQL%BULK_EXCEPTIONS and logging the underlying business information during

    exceptions handling. We have managed to avoid lengthy and repetitive exception-handling in each load process we write.

    Instead, we hand off our busin ess data to a ge neric utility that does this for us.

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01
  • 7/29/2019 encapsulating bulk pl/sql exceptions

    22/22

    The ANYDATA method en able s us to p ass any format of data (defined as colle ctions of SQL obje ct types) to the API, wherea s type

    substitution limits us to whatever we define in the object type hierarchy. The latter method, however, is much simpler to work with

    and the resulting implementation is much cleaner. Both methods require that we divert away from PL/SQL records and arrays and

    instead u se SQL object types and collections in their place . As stated, however, the SQL types can provide much greater flexibili ty

    and scope than their PL/SQL-only counterparts.

    further reading

    For more information on FORALL .. SAVE EXCEPTIONS, ANYDATA and type substitution, follow the links provided earlier in this

    article. For a general discussion of object-relational features in Oracle, see the online Application Developer's Guide -

    Object-Relational Features.

    source code

    The source code for the examples in this article can be do wnloaded from here .

    Adrian Billingto n, July 2007

    Back to Top

    oracle-de velope r.net 2002-2012 copyright Adrian Billington all rights rese rved | orig inal template by SmallPark | last upd ated 02 April 2012

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://smallpark.org/http://www.oracle-developer.net/display.php?id=419#http://www.oracle-developer.net/content/code/419.ziphttp://docs.oracle.com/cd/B19306_01/appdev.102/b14260/toc.htm