PK |%Aoa,mimetypeapplication/epub+zipPK|%AiTunesMetadata.plist\ artistName Oracle Corporation book-info cover-image-hash 207909234 cover-image-path OEBPS/dcommon/oracle-logo.jpg package-file-hash 873349154 publisher-unique-id E25518-05 unique-id 619800653 genre Oracle Documentation itemName Oracle® Database Advanced Application Developer's Guide, 11g Release 2 (11.2) releaseDate 2012-04-30T10:06:40Z year 2012 PK:@a\PK|%AMETA-INF/container.xml PKYuPK|%AOEBPS/adfns_dependencies.htm Schema Object Dependency

18 Schema Object Dependency

If the definition of object A references object B, then A depends on B. This chapter explains dependencies among schema objects, and how Oracle Database automatically tracks and manages these dependencies. Because of this automatic dependency management, A never uses an obsolete version of B, and you almost never have to explicitly recompile A after you change B.

Topics:

Overview of Schema Object Dependencies

Some types of schema objects can reference other objects in their definitions. For example, a view is defined by a query that references tables or other views, and the body of a subprogram can include SQL statements that reference other objects. If the definition of object A references object B, then A is a dependent object (of B) and B is a referenced object (of A).

Example 18-1 shows how to display the dependent and referenced object types in your database (if you are logged in as DBA).

Example 18-1 Displaying Dependent and Referenced Object Types

Display dependent object types:

SELECT DISTINCT TYPE
FROM DBA_DEPENDENCIES
ORDER BY TYPE;

Result:

TYPE
------------------
DIMENSION
EVALUATION CONTXT
FUNCTION
INDEX
INDEXTYPE
JAVA CLASS
JAVA DATA
MATERIALIZED VIEW
OPERATOR
PACKAGE
PACKAGE BODY
 
TYPE
------------------
PROCEDURE
RULE
RULE SET
SYNONYM
TABLE
TRIGGER
TYPE
TYPE BODY
UNDEFINED
VIEW
XML SCHEMA
 
22 rows selected.

Display referenced object types:

SELECT DISTINCT REFERENCED_TYPE
FROM DBA_DEPENDENCIES
ORDER BY REFERENCED_TYPE;

Result:

REFERENCED_TYPE
------------------
EVALUATION CONTXT
FUNCTION
INDEX
INDEXTYPE
JAVA CLASS
LIBRARY
NON-EXISTENT
OPERATOR
PACKAGE
PROCEDURE
SEQUENCE

REFERENCED_TYPE
------------------
SYNONYM
TABLE
TYPE
VIEW
XML SCHEMA

16 rows selected.

If you alter the definition of a referenced object, dependent objects might not continue to function without error, depending on the type of alteration. For example, if you drop a table, no view based on the dropped table is usable.

As an example of a schema object change that invalidates some dependents but not others, consider the two views in Example 18-2, which are based on the HR.EMPLOYEES table.

Example 18-2 creates two views from the EMPLOYEES table: SIXFIGURES, which selects all columns in the table, and COMMISSIONED, which does not include the EMAIL column. As the example shows, changing the EMAIL column invalidates SIXFIGURES, but not COMMISSIONED.

Example 18-2 Schema Object Change that Invalidates Some Dependents

CREATE OR REPLACE VIEW sixfigures AS
SELECT * FROM employees
WHERE salary >= 100000;

CREATE OR REPLACE VIEW commissioned AS
SELECT first_name, last_name, commission_pct
FROM employees
WHERE commission_pct > 0.00;
 

SQL*Plus formatting command:

COLUMN object_name FORMAT A16

Query:

SELECT object_name, status
FROM user_objects
WHERE object_type = 'VIEW'
ORDER BY object_name;

Result:

OBJECT_NAME      STATUS
---------------- -------
COMMISSIONED     VALID
SIXFIGURES       VALID

Lengthen EMAIL column of EMPLOYEES table:

ALTER TABLE employees MODIFY email VARCHAR2(100);

Query:

SELECT object_name, status
FROM user_objects
WHERE object_type = 'VIEW'
ORDER BY object_name;

Result:

OBJECT_NAME      STATUS
---------------- -------
COMMISSIONED     INVALID
SIXFIGURES       VALID

A view depends on every object referenced in its query. The view in Example 18-3, depends on the tables employees and departments.

Example 18-3 View that Depends on Multiple Objects

CREATE OR REPLACE VIEW v AS
  SELECT last_name, first_name, department_name
  FROM employees e, departments d
  WHERE e.department_id = d.department_id
  ORDER BY last_name;

Notes:

  • CREATE statements automatically update all dependencies.

  • Dynamic SQL statements do not create dependencies. For example, this statement does not create a dependency on tab1:

    EXECUTE IMMEDIATE 'SELECT * FROM tab1'
    

Querying Object Dependencies

The static data dictionary views USER_DEPENDENCIES, ALL_DEPENDENCIES, and DBA_DEPENDENCIES describe dependencies between database objects.

The utldtree.sql SQL script creates the view DEPTREE, which contains information on the object dependency tree, and the view IDEPTREE, a presorted, pretty-print version of DEPTREE.


See Also:

Oracle Database Reference for more information about the DEPTREE, IDEPTREE, and utldtree.sql script

Object Status

Every database object has a status value described in Table 18-1.

Table 18-1 Database Object Status

StatusMeaning

Valid

The object was successfully compiled, using the current definition in the data dictionary.

Compiled with errors

The most recent attempt to compile the object produced errors.

Invalid

The object is marked invalid because an object that it references has changed. (Only a dependent object can be invalid.)

Unauthorized

An access privilege on a referenced object was revoked. (Only a dependent object can be unauthorized.)



Note:

The static data dictionary views USER_OBJECTS, ALL_OBJECTS, and DBA_OBJECTS do not distinguish between "Compiled with errors," "Invalid," and "Unauthorized"—they describe all of these as INVALID.

Invalidation of Dependent Objects

If object A depends on object B, which depends on object C, then A is a direct dependent of B, B is a direct dependent of C, and A is an indirect dependent of C.

Direct dependents are invalidated only by changes to the referenced object that affect them (changes to the signature of the referenced object).

Indirect dependents can be invalidated by changes to the reference object that do not affect them. If a change to C invalidates B, it invalidates A (and all other direct and indirect dependents of B). This is called cascading invalidation.

With coarse-grained invalidation, a data definition language (DDL) statement that changes a referenced object invalidates all of its dependents.

With fine-grained invalidation, a DDL statement that changes a referenced object invalidates only dependents for which either of these statements is true:

For example, if view v selects columns c1 and c2 from table t, a DDL statement that changes only column c3 of t does not invalidate v.

The DDL statement CREATE OR REPLACE object has no effect under these conditions:

The operations in the left column of Table 18-2 cause fine-grained invalidation, except in the cases in the right column. The cases in the right column, and all operations not listed in Table 18-2, cause coarse-grained invalidation.

Table 18-2 Operations that Cause Fine-Grained Invalidation

OperationExceptions

ALTER TABLE table ADD column

  • Dependent object (except a view) uses SELECT * on table.

  • Dependent object uses table%rowtype.

  • Dependent object performs INSERT on table without specifying column list.

  • Dependent object references table in query that contains a join.

  • Dependent object references table in query that references a PL/SQL variable.

ALTER TABLE table {MODIFY|RENAME|DROP|SET UNUSED} column

ALTER TABLE table DROP CONSTRAINT not_null_constraint

  • Dependent object directly references column.

  • Dependent object uses SELECT * on table.

  • Dependent object uses table%ROWTYPE.

  • Dependent object performs INSERT on table without specifying column list.

  • Dependent object is a trigger that depends on an entire row (that is, it does not specify a column in its definition).

  • Dependent object is a trigger that depends on a column to the right of the dropped column.

CREATE OR REPLACE VIEW view

Online Table Redefinition (DBMS_REDEFINITION)

Column lists of new and old definitions differ, and at least one of these is true:

  • Dependent object references column that is modified or dropped in new view or table definition.

  • Dependent object uses view%rowtype or table%rowtype.

  • Dependent object performs INSERT on view or table without specifying column list.

  • New view definition introduces new columns, and dependent object references view or table in query that contains a join.

  • New view definition introduces new columns, and dependent object references view or table in query that references a PL/SQL variable.

  • Dependent object references view or table in RELIES ON clause.

CREATE OR REPLACE SYNONYM synonym

  • New and old synonym targets differ, and one is not a table.

  • Both old and new synonym targets are tables, and the tables have different column lists or different privilege grants.

  • Both old and new synonym targets are tables, and dependent object is a view that references a column that participates in a unique index on the old target but not in a unique index on the new target.

DROP INDEX

  • The index is a function-based index and the dependent object is a trigger that depends either on an entire row or on a column that was added to table after a function-based index was created.

  • The index is a unique index, the dependent object is a view, and the view references a column participating in the unique index.

CREATE OR REPLACE {PROCEDURE|FUNCTION}

Call signature changes. Call signature is the parameter list (order, names, and types of parameters), return type, purityFoot 1 , determinism, parallelism, pipelining, and (if the procedure or function is implemented in C or Java) implementation properties.

CREATE OR REPLACE PACKAGE

  • Dependent object references a dropped or renamed package item.

  • Dependent object references a package procedure or function whose call signature or entry-point numberFoot 2 , changed.

    If referenced procedure or function has multiple overload candidates, dependent object is invalidated if any overload candidate's call signature or entry point number changed, or if a candidate was added or dropped.

  • Dependent object references a package cursor whose call signature, rowtype, or entry point number changed.

  • Dependent object references a package type or subtype whose definition changed.

  • Dependent object references a package variable or constant whose name, data type, initial value, or offset number changed.

  • Package purity1 changed.


Footnote 1 Purity refers to a set of rules for preventing side effects (such as unexpected data changes) when invoking PL/SQL functions within SQL queries. Package purity refers to the purity of the code in the package initialization block.

Footnote 2 The entry-point number of a procedure or function is determined by its location in the PL/SQL package code. A procedure or function added to the end of a PL/SQL package is given a new entry-point number.


Note:

A dependent object that is invalidated by an operation in Table 18-2 appears in the static data dictionary views *_OBJECTS and *_OBJECTS_AE only after an attempt to reference it (either during compilation or execution) or after invoking one of these subprograms:

Topics:

Session State and Referenced Packages

Each session that references a package construct has its own instantiation of that package, including a persistent state of any public and private variables, cursors, and constants. All of a session's package instantiations, including state, can be lost if any of the session's instantiated packages are subsequently invalidated and revalidated.


See Also:


Security Authorization

When a data manipulation language (DML) object or system privilege is granted to, or revoked from, a user or PUBLIC, Oracle Database invalidates all the owner's dependent objects, to verify that an owner of a dependent object continues to have the necessary privileges for all referenced objects.

Guidelines for Reducing Invalidation

To reduce invalidation of dependent objects, follow these guidelines:

Add Items to End of Package

When adding items to a package, add them to the end of the package. This preserves the entry point numbers of existing top-level package items, preventing their invalidation.

For example, consider this package:

CREATE OR REPLACE PACKAGE pkg1 IS
  FUNCTION get_var RETURN VARCHAR2;
END;
/

Adding an item to the end of pkg1, as follows, does not invalidate dependents that reference the get_var function:

CREATE OR REPLACE PACKAGE pkg1 IS
  FUNCTION get_var RETURN VARCHAR2;
  PROCEDURE set_var (v VARCHAR2);
END;
/

Inserting an item between the get_var function and the set_var procedure, as follows, invalidates dependents that reference the set_var function:

CREATE OR REPLACE PACKAGE pkg1 IS
  FUNCTION get_var RETURN VARCHAR2;
  PROCEDURE assert_var (v VARCHAR2);
  PROCEDURE set_var (v VARCHAR2);
END;
/

Reference Each Table Through a View

Reference tables indirectly, using views, enabling you to:

  • Add columns to the table without invalidating dependent views or dependent PL/SQL objects

  • Modify or delete columns not referenced by the view without invalidating dependent objects

The statement CREATE OR REPLACE VIEW does not invalidate an existing view or its dependents if the new ROWTYPE matches the old ROWTYPE.

Object Revalidation

An object that is not valid when it is referenced must be validated before it can be used. Validation occurs automatically when an object is referenced; it does not require explicit user action.

If an object is not valid, its status is either compiled with errors, unauthorized, or invalid. For definitions of these terms, see Table 18-1.

Topics:

Revalidation of Objects that Compiled with Errors

The compiler cannot automatically revalidate an object that compiled with errors. The compiler recompiles the object, and if it recompiles without errors, it is revalidated; otherwise, it remains invalid.

Revalidation of Unauthorized Objects

The compiler checks whether the unauthorized object has access privileges to all of its referenced objects. If so, the compiler revalidates the unauthorized object without recompiling it. If not, the compiler issues appropriate error messages.

Revalidation of Invalid SQL Objects

The SQL compiler recompiles the invalid object. If the object recompiles without errors, it is revalidated; otherwise, it remains invalid.

Revalidation of Invalid PL/SQL Objects

For an invalid PL/SQL program unit (procedure, function, or package), the PL/SQL compiler checks whether any referenced object changed in a way that affects the invalid object. If so, the compiler recompiles the invalid object. If the object recompiles without errors, it is revalidated; otherwise, it remains invalid. If not, the compiler revalidates the invalid object without recompiling it.

Name Resolution in Schema Scope

Object names referenced in SQL statements have one or more pieces. Pieces are separated by periods—for example, hr.employees.department_id has three pieces.

Oracle Database uses this procedure to try to resolve an object name:

  1. Try to qualify the first piece of the object name.

    If the object name has only one piece, then that piece is the first piece. Otherwise, the first piece is the piece to the left of the leftmost period; for example, in hr.employees.department_id, the first piece is hr.

    The procedure for trying to qualify the first piece is:

    1. If the object name is a table name that appears in the FROM clause of a SELECT statement, and the object name has multiple pieces, go to step d. Otherwise, go to step b.

    2. Search the current schema for an object whose name matches the first piece.

      If found, go to step 2. Otherwise, go to step c.

    3. Search for a public synonym that matches the first piece.

      If found, go to step 2. Otherwise, go to step d.

    4. Search for a schema whose name matches the first piece.

      If found, and if the object name has a second piece, go to step e. Otherwise, return an error—the object name cannot be qualified.

    5. Search the schema found at step d for a SQL function whose name matches the second piece of the object name.

      If found, the schema redefined that SQL function. The object name resolves to the original SQL function, not to the schema-defined function of the same name. Go to step 2.

      If not found, return an error—the object name cannot be qualified.

  2. A schema object has been qualified. Any remaining pieces of the object name must match a valid part of this schema object.

    For example, if the object name is hr.employees.department_id, hr is qualified as a schema. If employees is qualified as a table, department_id must correspond to a column of that table. If employees is qualified as a package, department_id must correspond to a public constant, variable, procedure, or function of that package.

Because of how Oracle Database resolves references, an object can depend on the nonexistence of other objects. This situation occurs when the dependent object uses a reference that would be interpreted differently if another object were present.


See Also:


Local Dependency Management

Local dependency management occurs when Oracle Database manages dependencies among the objects in a single database. For example, a statement in a procedure can reference a table in the same database.

Remote Dependency Management

Remote dependency management occurs when Oracle Database manages dependencies in distributed environments across a network. For example, an Oracle Forms trigger can depend on a schema object in the database. In a distributed database, a local view can reference a remote table.

Oracle Database also manages distributed database dependencies. For example, an Oracle Forms application might contain a trigger that references a table. The database system must account for dependencies among such objects. Oracle Database uses different mechanisms to manage remote dependencies, depending on the objects involved.

Topics:

Dependencies Among Local and Remote Database Procedures

Dependencies among stored procedures (including functions, packages, and triggers) in a distributed database system are managed using either time-stamp checking or signature checking (see "Time-Stamp Dependency Mode" and "RPC-Signature Dependency Mode").

The dynamic initialization parameter REMOTE_DEPENDENCIES_MODE determines whether time stamps or signatures govern remote dependencies.

Dependencies Among Other Remote Objects

Oracle Database does not manage dependencies among remote schema objects other than local-procedure-to-remote-procedure dependencies.

For example, assume that a local view is created and defined by a query that references a remote table. Also assume that a local procedure includes a SQL statement that references the same remote table. Later, the definition of the table is altered.

Therefore, the local view and procedure are never invalidated, even if the view or procedure is used after the table is altered, and even if the view or procedure now returns errors when used. In this case, the view or procedure must be altered manually so that errors are not returned. In such cases, lack of dependency management is preferable to unnecessary recompilations of dependent objects.

Dependencies of Applications

Code in database applications can reference objects in the connected database. For example, Oracle Call Interface (OCI) and precompiler applications can submit anonymous PL/SQL blocks. Triggers in Oracle Forms applications can reference a schema object.

Such applications are dependent on the schema objects they reference. Dependency management techniques vary, depending on the development environment. Oracle Database does not automatically track application dependencies.


See Also:

Manuals for your application development tools and your operating system for more information about managing the remote dependencies within database applications

Remote Procedure Call (RPC) Dependency Management

Remote procedure call (RPC) dependency management occurs when a local stored procedure calls a remote procedure in a distributed database system. The dynamic initialization parameter REMOTE_DEPENDENCIES_MODE controls the dependency mode. The choice is either time-stamp dependency mode or RPC-signature dependency mode.

Topics:

Time-Stamp Dependency Mode

Whenever a procedure is compiled, its time stamp is recorded in the data dictionary. The time stamp shows when the procedure was created, altered, or replaced.

A compiled procedure contains information about each remote procedure that it calls, including the schema, package name, procedure name, and time stamp of the remote procedure.

In time-stamp dependency mode, when a local stored procedure calls a remote procedure, Oracle Database compares the time stamp that the local procedure has for the remote procedure to the current time stamp of the remote procedure. If the two timestamps match, both the local and remote procedures run. Neither is recompiled.

If the two timestamps do not match, the local procedure is invalidated and an error is returned to the calling environment. All other local procedures that depend on the remote procedure with the new time stamp are also invalidated.

Time stamp comparison occurs when a statement in the body of the local procedure calls the remote procedure. Therefore, statements in the local procedure that precede the invalid call might run successfully. Statements after the invalid call do not run. The local procedure must be recompiled.

If DML statements precede the invalid call, they roll back only if they and the invalid call are in the same PL/SQL block. For example, the UPDATE statement rolls back in this code:

BEGIN
  UPDATE table SET ...
  invalid_proc;
  COMMIT;
END;

But the UPDATE statement does not roll back in this code:

UPDATE table SET ...
EXECUTE invalid_proc;
COMMIT;

The disadvantages of time-stamp dependency mode are:

  • Dependent objects across the network are often recompiled unnecessarily, degrading performance.

  • If the client-side application uses PL/SQL, this mode can cause situations that prevent the application from running on the client side.

    An example of such an application is Oracle Forms. During installation, you must recompile the client-side PL/SQL procedures that Oracle Forms uses at the client site. Also, if a client-side procedure depends on a server procedure, and if the server procedure changes or is automatically recompiled, you must recompile the client-side PL/SQL procedure. However, no PL/SQL compiler is available on the client. Therefore, the developer of the client application must distribute new versions of the application to all customers.

RPC-Signature Dependency Mode

Oracle Database provides RPC signatures to handle remote dependencies. RPC signatures do not affect local dependencies, because recompilation is always possible in the local environment.

An RPC signature is associated with each compiled stored program unit. It identifies the unit by these characteristics:

  • Name

  • Number of parameters

  • Data type class of each parameter

  • Mode of each parameter

  • Data type class of return value (for a function)

An RPC signature changes only when at least one of the preceding characteristics changes.


Note:

An RPC signature does not include DETERMINISTIC, PARALLEL_ENABLE, or purity information. If these settings change for a function on remote system, optimizations based on them are not automatically reconsidered. Therefore, calling the remote function in a SQL statement or using it in a function-based index might cause incorrect query results.

A compiled program unit contains the RPC signature of each remote procedure that it calls (and the schema, package name, procedure name, and time stamp of the remote procedure).

In RPC-signature dependency mode, when a local program unit calls a subprogram in a remote program unit, the database ignores time-stamp mismatches and compares the RPC signature that the local unit has for the remote subprogram to the current RPC signature of the remote subprogram. If the RPC signatures match, the call succeeds; otherwise, the database returns an error to the local unit, and the local unit is invalidated.

For example, suppose that this procedure, get_emp_name, is stored on a server in Boston (BOSTON_SERVER):

CREATE OR REPLACE PROCEDURE get_emp_name (
  emp_number  IN  NUMBER,
  hiredate    OUT VARCHAR2,
  emp_name    OUT VARCHAR2) AS
BEGIN
  SELECT last_name, TO_CHAR(hire_date, 'DD-MON-YY')
  INTO emp_name, hiredate
  FROM employees
  WHERE employee_id = emp_number;
END;
/

When get_emp_name is compiled on BOSTON_SERVER, Oracle Database records both its RPC signature and its time stamp.

Suppose that this PL/SQL procedure, print_name, which calls get_emp_name, is on a server in California:

CREATE OR REPLACE PROCEDURE print_ename (emp_number IN NUMBER) AS
  hiredate  VARCHAR2(12);
  ename     VARCHAR2(10);
BEGIN
  get_emp_name@BOSTON_SERVER(emp_number, hiredate, ename);
  dbms_output.put_line(ename);
  dbms_output.put_line(hiredate);
END;
/

When print_name is compiled on the California server, the database connects to the Boston server, sends the RPC signature of get_emp_name to the California server, and records the RPC signature of get_emp_name in the compiled state of print_ename.

At run time, when print_name calls get_emp_name, the database sends the RPC signature of get_emp_name that was recorded in the compiled state of print_ename to the Boston server. If the recorded RPC signature matches the current RPC signature of get_emp_name on the Boston server, the call succeeds; otherwise, the database returns an error to print_name, which is invalidated.

Topics:

Changing Names and Default Values of Parameters

Changing the name or default value of a subprogram parameter does not change the RPC signature of the subprogram. For example, procedure P1 has the same RPC signature in these two examples:

PROCEDURE P1 (Param1 IN NUMBER := 100);
PROCEDURE P1 (Param2 IN NUMBER := 200);

However, if your application requires that callers get the new default value, you must recompile the called procedure.

Changing Specification of Parameter Mode IN

Because the subprogram parameter mode IN is the default, you can specify it either implicitly or explicitly. Changing its specification from implicit to explicit, or the reverse, does not change the RPC signature of the subprogram. For example, procedure P1 has the same RPC signature in these two examples:

PROCEDURE P1 (Param1 NUMBER);    -- implicit specification
PROCEDURE P1 (Param1 IN NUMBER);  -- explicit specification

Changing Subprogram Body

Changing the body of a subprogram does not change the RPC signature of the subprogram.

Example 18-4 changes only the body of the procedure get_hire_date; therefore, it does not change the RPC signature of get_hire_date.

Example 18-4 Changing Body of Procedure get_hire_date

CREATE OR REPLACE PROCEDURE get_hire_date (
  emp_number  IN  NUMBER,
  hiredate    OUT VARCHAR2,
  emp_name    OUT VARCHAR2) AS
BEGIN
  SELECT last_name, TO_CHAR(hire_date, 'DD-MON-YY')
  INTO emp_name, hiredate
  FROM employees
  WHERE employee_id = emp_number;
END;
/

CREATE OR REPLACE PROCEDURE get_hire_date (
  emp_number  IN  NUMBER,
  hiredate    OUT VARCHAR2,
  emp_name    OUT VARCHAR2) AS
BEGIN
  -- Change date format model
  SELECT last_name, TO_CHAR(hire_date, 'DD/MON/YYYY')
  INTO emp_name, hiredate
  FROM employees
  WHERE employee_id = emp_number;
END;
/

Changing Data Type Classes of Parameters

Changing the data type of a parameter to another data type in the same class does not change the RPC signature, but changing the data type to a data type in another class does.

Table 18-3 lists the data type classes and the data types that comprise them. Data types not listed in Table 18-3, such as NCHAR, do not belong to a data type class. Changing their type always changes the RPC signature.

Table 18-3 Data Type Classes

Data Type ClassData Types in Class

Character

CHAR
CHARACTER

VARCHAR

VARCHAR
VARCHAR2
STRING
LONG
ROWID

Raw

RAW
LONG RAW

Integer

BINARY_INTEGER
PLS_INTEGER
SIMPLE_INTEGER
BOOLEAN
NATURAL
NATURALN
POSITIVE
POSITIVEN

Number

NUMBER
INT
INTEGER
SMALLINT
DEC
DECIMAL
REAL
FLOAT
NUMERIC
DOUBLE PRECISION

Datetime

DATE
TIMESTAMP
TIMESTAMP WITH TIME ZONE
TIMESTAMP WITH LOCAL TIME ZONE
INTERVAL YEAR TO MONTH
INTERVAL DAY TO SECOND

Example 18-5 changes the data type of the parameter hiredate from VARCHAR2 to DATE. VARCHAR2 and DATE are not in the same data type class, so the RPC signature of the procedure get_hire_date changes.

Example 18-5 Changing Data Type Class of get_hire_date Parameter

CREATE OR REPLACE PROCEDURE get_hire_date (
  emp_number  IN  NUMBER,
  hiredate    OUT DATE,
  emp_name    OUT VARCHAR2) AS
BEGIN
  SELECT last_name, TO_CHAR(hire_date, 'DD/MON/YYYY')
  INTO emp_name, hiredate
  FROM employees
  WHERE employee_id = emp_number;
END;
/

Changing Package Types

Changing the name of a package type, or the names of its internal components, does not change the RPC signature of the package.

Example 18-6 defines a record type, emp_data_type, inside the package emp_package. Next, it changes the names of the record fields, but not their types. Finally, it changes the name of the type, but not its characteristics. The RPC signature of the package does not change.

Example 18-6 Changing Names of Fields in Package Record Type

CREATE OR REPLACE PACKAGE emp_package AS
  TYPE emp_data_type IS RECORD (
    emp_number  NUMBER,
    hiredate    VARCHAR2(12),
    emp_name    VARCHAR2(10)
  );
  PROCEDURE get_emp_data (
    emp_data  IN OUT emp_data_type
  );
END;
/

CREATE OR REPLACE PACKAGE emp_package AS
  TYPE emp_data_type IS RECORD (
    emp_num   NUMBER,
    hire_dat  VARCHAR2(12),
    empname   VARCHAR2(10)
  );
  PROCEDURE get_emp_data (
    emp_data  IN OUT emp_data_type
  );
END;
/

CREATE OR REPLACE PACKAGE emp_package AS
  TYPE emp_data_record_type IS RECORD (
    emp_num   NUMBER,
    hire_dat  VARCHAR2(12),
    empname   VARCHAR2(10)
  );
  PROCEDURE get_emp_data (
    emp_data  IN OUT emp_data_record_type
  );
END;
/

Controlling Dependency Mode

The dynamic initialization parameter REMOTE_DEPENDENCIES_MODE controls the dependency mode. If the initialization parameter file contains this specification, then only time stamps are used to resolve dependencies (if this is not explicitly overridden dynamically):

REMOTE_DEPENDENCIES_MODE = TIMESTAMP

If the initialization parameter file contains this parameter specification, then RPC signatures are used to resolve dependencies (if this not explicitly overridden dynamically):

REMOTE_DEPENDENCIES_MODE = SIGNATURE

You can alter the mode dynamically by using the DDL statements. For example, this example alters the dependency mode for the current session:

ALTER SESSION SET REMOTE_DEPENDENCIES_MODE = {SIGNATURE | TIMESTAMP}

This example alters the dependency mode systemwide after startup:

ALTER SYSTEM SET REMOTE_DEPENDENCIES_MODE = {SIGNATURE | TIMESTAMP}

If the REMOTE_DEPENDENCIES_MODE parameter is not specified, either in the init.ora parameter file or using the ALTER SESSION or ALTER SYSTEM statements, TIMESTAMP is the default value. Therefore, unless you explicitly use the REMOTE_DEPENDENCIES_MODE parameter, or the appropriate DDL statement, your server is operating using the time-stamp dependency mode.

When you use REMOTE_DEPENDENCIES_MODE=SIGNATURE:

  • If you change the initial value of a parameter of a remote procedure, then the local procedure calling the remote procedure is not invalidated. If the call to the remote procedure does not supply the parameter, then the initial value is used. In this case, because invalidation and recompilation does not automatically occur, the old initial value is used. To see the new initial values, recompile the calling procedure manually.

  • If you add an overloaded procedure in a package (a procedure with the same name as an existing one), then local procedures that call the remote procedure are not invalidated. If it turns out that this overloading results in a rebinding of existing calls from the local procedure under the time-stamp mode, then this rebinding does not happen under the RPC signature mode, because the local procedure does not get invalidated. You must recompile the local procedure manually to achieve the rebinding.

  • If the types of parameters of an existing package procedure are changed so that the new types have the same shape as the old ones, then the local calling procedure is not invalidated or recompiled automatically. You must recompile the calling procedure manually to get the semantics of the new type.

Topics:

Dependency Resolution

When REMOTE_DEPENDENCIES_MODE = TIMESTAMP (the default value), dependencies among program units are handled by comparing time stamps at run time. If the time stamp of a called remote procedure does not match the time stamp of the called procedure, then the calling (dependent) unit is invalidated and must be recompiled. In this case, if there is no local PL/SQL compiler, then the calling application cannot proceed.

In the time-stamp dependency mode, RPC signatures are not compared. If there is a local PL/SQL compiler, then recompilation happens automatically when the calling procedure is run.

When REMOTE_DEPENDENCIES_MODE = SIGNATURE, the recorded time stamp in the calling unit is first compared to the current time stamp in the called remote unit. If they match, then the call proceeds. If the time stamps do not match, then the RPC signature of the called remote subprogram, as recorded in the calling subprogram, is compared with the current RPC signature of the called subprogram. If they do not match (using the criteria described in the section "Changing Data Type Classes of Parameters"), then an error is returned to the calling session.

Suggestions for Managing Dependencies

Follow these guidelines for setting the REMOTE_DEPENDENCIES_MODE parameter:

  • Server-side PL/SQL users can set the parameter to TIMESTAMP (or let it default to that) to get the time-stamp dependency mode.

  • Server-side PL/SQL users can use RPC-signature dependency mode if they have a distributed system and they want to avoid possible unnecessary recompilations.

  • Client-side PL/SQL users must set the parameter to SIGNATURE. This allows:

    • Installation of applications at client sites without recompiling procedures.

    • Ability to upgrade the server, without encountering time stamp mismatches.

  • When using RPC signature mode on the server side, add procedures to the end of the procedure (or function) declarations in a package specification. Adding a procedure in the middle of the list of declarations can cause unnecessary invalidation and recompilation of dependent procedures.

Shared SQL Dependency Management

In addition to managing dependencies among schema objects, Oracle Database also manages dependencies of each shared SQL area in the shared pool. If a table, view, synonym, or sequence is created, altered, or dropped, or a procedure or package specification is recompiled, all dependent shared SQL areas are invalidated. At a subsequent execution of the cursor that corresponds to an invalidated shared SQL area, Oracle Database reparses the SQL statement to regenerate the shared SQL area.

PK*~PK|%AOEBPS/cover.htmO Cover

Oracle Corporation

PK[pTOPK|%AOEBPS/whatsnew.htmU~ What's New in Application Development?

What's New in Application Development?

This topic briefly describes the new Oracle Database features that this book documents and provides links to more information.

Topics:

Oracle Database 11g Release 2 (11.2.0.2) Feature

Edition Attribute of Database Service

Before Release 11.2.0.2, you could not specify your initial session edition when using a database service to connect to Oracle Database. If you wanted to use Edition-Based Redefinition for hot rollover, where some database clients use the pre-upgrade edition while others use the post-upgrade edition, then you had to change the client code.

As of Release 11.2.0.2, you can specify the initial session edition as an attribute of a database service, which makes it easier to ensure that each session uses the desired edition during hot rollover. For more information, see "Your Initial Session Edition".

As of Release 11.2.0.2, each *_SERVICES static data dictionary view has an EDITION column that shows the default initial session edition. For more information, see "Displaying Information About Editions, Editioning Views, and Crossedition Triggers".

Oracle Database 11g Release 2 Features

The Oracle Database features for Oracle Database 11g Release 2 are:

Flashback Transaction Foreign Key Dependency Tracking

Flashback Transaction (the DBMS_FLASHBACK.TRANSACTION_BACKOUT procedure) with the CASCADE option rolls back a transaction and its dependent transactions while the database remains online.

Before Oracle Database 11g Release 2, Flashback Transaction did not track foreign key dependencies. Therefore, if you tried to use Flashback Transaction with the CASCADE option to roll back a transaction that had foreign key dependencies, you could get a foreign key violation error. The workaround was to include the foreign-key-dependent transactions in the list of transactions to roll back.

As of Oracle Database 11g Release 2, when using Flashback Transaction with the CASCADE option, you do not have to include any dependent transactions in the list of transactions to be rolled back.

Foreign key dependency tracking for Flashback Transaction requires that you enable foreign key supplemental logging. For instructions, see "Configuring Your Database for Flashback Transaction". For information about Flashback Transaction, see "Using Flashback Transaction".

Fine-Grained Invalidation for Triggers

The Oracle Database 11g Release 1 feature "Fine-Grained Invalidation" has been extended to triggers.

Edition-Based Redefinition

Edition-based redefinition enables you to upgrade the database component of an application while it is in use, thereby minimizing or eliminating down time.

To upgrade an application while it is in use, you copy the database objects that comprise the application and redefine the copied objects in isolation. Your changes do not affect users of the application—they continue to run the unchanged application. When you are sure that your changes are correct, you make the upgraded application available to all users.

Using edition-based redefinition means using one or more of its component features. The features you use, and the down time, depend on these factors:

You always use the edition feature to copy the database objects and redefine the copied objects in isolation.

If you change the structure of one or more tables, you also use the feature editioning views.

If other users must be able to change data in the tables while you are changing their structure, you also use forward crossedition triggers. If the pre- and post-upgrade applications will be in ordinary use at the same time (hot rollover), you also use reverse crossedition triggers. Crossedition triggers are not a permanent part of the application—you drop them when all users are using the post-upgrade application.

For more information, see Chapter 19, "Edition-Based Redefinition."

APPLYING_CROSSEDITION_TRIGGER Function

The body of a forward crossedition trigger must handle data transformation collisions. If your collision-handling strategy depends on why the trigger is running, you can determine the reason with the function APPLYING_CROSSEDITION_TRIGGER, which is defined in the package DBMS_STANDARD.

For more information, see "Handling Data Transformation Collisions".

IGNORE_ROW_ON_DUPKEY_INDEX Hint

When a statement of the form INSERT INTO target subquery runs, a unique key for some rows to be inserted might collide with existing rows. Suppose that your application must ignore such collisions and insert the rows that do not collide with existing rows.

Before Oracle Database 11g Release 2, you had to write a PL/SQL program which, in a block with a NULL handler for the DUP_VAL_ON_INDEX exception, selected the source rows and then inserted them, one at a time, into the target.

As of Oracle Database 11g Release 2, you do not have to write a PL/SQL program. You can use the IGNORE_ROW_ON_DUPKEY_INDEX hint in an INSERT statement, which is easier to write and runs much faster. This hint is especially helpful when implementing crossedition triggers.

For more information, see "Handling Data Transformation Collisions".

CHANGE_DUPKEY_ERROR_INDEX Hint

When an INSERT or UPDATE statement runs, a unique key might collide with existing rows.

Before Oracle Database 11g Release 2, the collision caused error ORA-00001. You could tell that a collision had occurred, but you could not tell where.

As of Oracle Database 11g Release 2, you can use the CHANGE_DUPKEY_ERROR_INDEX hint in an INSERT or UPDATE statement, specifying that when a unique key violation occurs for a specified index or set of columns, ORA-38911 is reported instead of ORA-00001. This hint is especially helpful when implementing crossedition triggers.

For more information, see "Handling Data Transformation Collisions".

DBMS_PARALLEL_EXECUTE Package

The DBMS_PARALLEL_EXECUTE package enables you to incrementally update the data in a large table in parallel, in two high-level steps:

  1. Group sets of rows in the table into smaller chunks.

  2. Apply the desired UPDATE statement to the chunks in parallel, committing each time you have finished processing a chunk.

This technique improves performance, reduces rollback space consumption, and reduces the number of row locks held. The DBMS_PARALLEL_EXECUTE package is recommended whenever you are updating a lot of data; for example, when you are applying forward crossedition triggers.

For more information, see "Transforming Data from Pre- to Post-Upgrade Representation".

Internet Protocol version 6 (IPv6) Support

Internet Protocol version 6 (IPv6) supports a much larger address space than IPv4 does. An IPv6 address has 128 bits, while an IPv4 address has only 32 bits.

Applications that use network addresses might need small changes, and recompilation, to accommodate IPv6 addresses. For more information, see "Performing Network Operations in PL/SQL Subprograms".

The agent control utility, agtctl, which starts a multithreaded extproc agent, now accepts IPv6 addresses. For more information, see "Configuration Parameters for Multithreaded extproc Agent Control".


See Also:

Oracle Database Net Services Administrator's Guide for detailed information about IPv6 support in Oracle Database

Oracle Database 11g Release 1 Features

The application development features for Oracle Database 11g Release 1 are:

WAIT Option for Data Definition Language (DDL) Statements

DDL statements require exclusive locks on internal structures. If these locks are unavailable when a DDL statement is issued, the DDL statement fails, though it might have succeeded if it had been issued subseconds later. The WAIT option of the SQL statement LOCK TABLE enables a DDL statement to wait for its locks for a specified period before failing.

For more information, see "Choosing a Locking Strategy".

Binary XML Support for Oracle XML Database

Binary XML is a third way to represent an XML document. Binary XML complements, rather than replaces, the existing object-relational storage and CLOB storage representations. Binary XML has two significant benefits:

As with other storage mechanisms, the details of binary XML storage are transparent to you. You continue to use XMLType and its associated methods and operators.

For more information, see "Representing XML Data".

Metadata for SQL Operators and Functions

Metadata for SQL operators and functions is accessible through dynamic performance (V$) views. Third-party tools can leverage SQL functions without maintaining their metadata in the application layer.

For more information, see "Metadata for SQL Operators and Functions".

Enhancements to Regular Expression SQL Functions

The regular expression SQL functions REGEXP_INSTR and REGEXP_SUBSTR have increased functionality. A new regular expression SQL function, REGEXP_COUNT, returns the number of times a pattern appears in a string. These functions act the same in SQL and PL/SQL.

For more information, see "Oracle SQL Support for Regular Expressions".

Invisible Indexes

An invisible index is maintained by Oracle Database for every data manipulation language (DML) statement, but is ignored by the optimizer unless you explicitly set the parameter OPTIMIZER_USE_INVISIBLE_INDEXES to TRUE on a session or system level.

Making an index invisible is an alternative to making it unusable or dropping it. Using invisible indexes, you can:

For more information, see Oracle Database Administrator's Guide.

PL/SQL Function Result Cache

Before Oracle Database 11g Release 1, if you wanted your PL/SQL application to cache the results of a function, you had to design and code the cache and cache-management subprograms. If multiple sessions ran your application, each session had to have its own copy of the cache and cache-management subprograms. Sometimes each session had to perform the same expensive computations.

As of Oracle Database 11g Release 1, PL/SQL provides a function result cache. Because the function result cache is stored in a shared global area (SGA), it is available to any session that runs your application.

For more information, see "PL/SQL Function Result Cache".

Sequences in PL/SQL Expressions

The pseudocolumns CURRVAL and NEXTVAL make writing PL/SQL source code easier for you and improve runtime performance and scalability. You can use sequence_name.CURRVAL and sequence_name.NEXTVAL wherever you can use a NUMBER expression.

See Example 6-6.

PL/Scope

PL/Scope is a compiler-driven tool that collects and organizes data about user-defined identifiers from PL/SQL source code. Because PL/Scope is a compiler-driven tool, you use it through interactive development environments (such as SQL Developer and JDeveloper), rather than directly.

PL/Scope enables the development of powerful and effective PL/Scope source code browsers that increase PL/SQL developer productivity by minimizing time spent browsing and understanding source code.

For a detailed description of PL/Scope, see Chapter 7, "Using PL/Scope."

PL/SQL Hierarchical Profiler

Nonhierarchical (flat) profilers record the time that a program spends within each subprogram—the function time or self time of each subprogram. Function time is helpful, but often inadequate. For example, it is helpful to know that a program spends 40% of its time in the subprogram INSERT_ORDER, but it is more helpful to know which subprograms call INSERT_ORDER often and the total time the program spends under INSERT_ORDER (including its descendent subprograms). Hierarchical profilers provide such information.

The PL/SQL hierarchical profiler does this:

Each subprogram-level summary in the dynamic execution profile includes information such as:

You can browse the generated HTML reports in any browser. The browser's navigational capabilities, combined with well chosen links, provide a powerful way to analyze performance of large applications, improve application performance, and lower development costs.

For a detailed description of PL/SQL hierarchical profiler, see Chapter 8, "Using the PL/SQL Hierarchical Profiler."

Query Result Change Notification

Before Oracle Database 11g Release 1, Continuous Query Notification (CQN) published only object change notifications, which result from DML or DDL changes to the objects associated with registered the queries.

As of Oracle Database 11g Release 1, CQN can also publish query result change notifications, which result from DML or DDL changes to the result set associated with the registered queries. New static data dictionary views enable you to see which queries are registered for result-set-change notifications (see "Querying CQN Registrations").

For more information, see Chapter 11, "Using Continuous Query Notification (CQN)."

Flashback Transaction

The DBMS_FLASHBACK.TRANSACTION_BACKOUT procedure rolls back a transaction and its dependent transactions while the database remains online. This recovery operation uses undo data to create and run the compensating transactions that return the affected data to its original state.

For more information, see "Using Flashback Transaction".

Flashback Data Archive (Oracle Total Recall)

A Flashback Data Archive provides the ability to store and track transactional changes to a record over its lifetime. It is no longer necessary to build this intelligence into the application. A Flashback Data Archive is useful for compliance with record stage policies and audit reports.

For more information, see "Using Flashback Data Archive (Oracle Total Recall)".

XA API Available Within PL/SQL

The XA interface functionality that supports transactions involving multiple resource managers, such as databases and queues, is now available within PL/SQL. You can use PL/SQL to switch and share transactions across SQL*Plus sessions and across processes.

For more information, see "Using the DBMS_XA Package".

Support for XA/JTA in Oracle Real Application Clusters (Oracle RAC) Environment

An XA transaction now spans Oracle RAC instances by default, enabling any application that uses XA to take full advantage of the Oracle RAC environment, enhancing the availability and scalability of the application.

For more information, see "Using Oracle XA with Oracle Real Application Clusters (Oracle RAC)".

Identity Code Package

The Identity Code Package provides tools to store, retrieve, encode, decode, and translate between various product or identity codes, including Electronic Product Code (EPC), in Oracle Database. The Identity Code Package provides new data types, metadata tables and views, and PL/SQL packages for storing EPC standard RFID tags or new types of RFID tags in a user table.

The Identity Code Package enables Oracle Database to recognize EPC coding schemes, to support efficient storage and component-level retrieval of EPC data, and to meet the EPCglobal Tag Data Translation 1.0 (TDT) standard that defines how to decode, encode, and translate between various EPC RFID tag representations.

The Identity Code Package also provides an extensible framework that enables you to use pre-existing coding schemes with applications that are not included in the EPC standard and adapt Oracle Database both to these older systems and to evolving identity codes that might become part of a future EPC standard.

The Identity Code Package also lets you create your own identity codes by first registering the new encoding category, registering the new encoding type, and then registering the new components associated with each new encoding type.

For more information, see Chapter 17, "Using the Identity Code Package."

Enhanced Online Index Creation and Rebuilding

Online index creation and rebuilding no longer requires a DML-blocking lock.

Before Oracle Database 11g Release 1, online index creation and rebuilding required a very short-term DML-blocking lock at the end of the rebuilding. The DML-blocking lock could cause a spike in the number of waiting DML operations, and therefore a short drop and spike of system usage. This system usage anomaly could trigger operating system alarm levels.

Embedded PL/SQL Gateway

The PL/SQL gateway enables a user-written PL/SQL subprogram to be invoked in response to a URL with parameters derived from an HTTP request. mod_plsql is a form of the gateway that exists as a plug-in to the Oracle HTTP Server. Now the PL/SQL gateway is also embedded in the database itself. The embedded PL/SQL gateway uses the internal Oracle XML Database Listener and does not depend on the Oracle HTTP Server. You configure the embedded version of the gateway with the DBMS_EPG package.

For more information, see "Using Embedded PL/SQL Gateway".

Oracle Database Spawns Multithreaded extproc Agent Directly by Default

When an application calls an external C procedure, either Oracle Database or Oracle Listener starts the external procedure agent, extproc.

Before Oracle Database 11g Release 1, Oracle Listener spawned the multithreaded extproc agent, and you defined environment variables for extproc in the file listener.ora.

As of Oracle Database 11g Release 1, by default, Oracle Database spawns extproc directly, eliminating the risk that Oracle Listener might spawn extproc unexpectedly. This default configuration is recommended for maximum security. If you use it, you define environment variables for extproc in the file extproc.ora.

For more information, including situations in which you cannot use the default configuration, see "Loading External Procedures".

Fine-Grained Invalidation

Before Oracle Database 11g Release 1, a DDL statement that changed a referenced object invalidated all of its dependents.

As of Oracle Database 11g Release 1, a DDL statement that changes a referenced object invalidates only the dependents for which either of these statements is true:

For example, if view v selects columns c1 and c2 from table t, a DDL statement that changes only column c3 of t does not invalidate v.

For more information, see "Invalidation of Dependent Objects".

PKhCZ~U~PK|%AOEBPS/adfns_constraints.htm Maintaining Data Integrity in Database Applications

5 Maintaining Data Integrity in Database Applications

In a database application, maintaining data integrity means ensuring that the data in the tables that the application manipulates conform to the appropriate business rules. A business rule specifies a condition or relationship that must always be true or must always be false. For example, a business rule might be that no employee can have a salary over $100,000 or that every employee in the EMPLOYEES table must belong to a department in the DEPARTMENTS table. Business rules vary from company to company, because each company defines its own policies about salaries, employee numbers, inventory tracking, and so on.

As explained in Oracle Database Concepts, there are several ways to ensure data integrity, and the one to use whenever possible is the integrity constraint (or constraint).

This chapter supplements this information:


Note:

This chapter applies to only to constraints on tables. Constraints on views do not help maintain data integrity or have associated indexes. Instead, they enable query rewrites on queries involving views, thereby improving performance when using materialized views and other data warehousing features.

For more information about constraints on views, see Oracle Database SQL Language Reference.

For information about using constraints in data warehouses, see Oracle Database Data Warehousing Guide.


Topics:

Enforcing Business Rules with Constraints

Whenever possible, enforce business rules with constraints. In addition to the advantages explained in Oracle Database Concepts, constraints have the advantage of speed: Oracle Database can check that all the data in a table obeys a constraint faster than application code can do the equivalent checking.

Example 5-1 creates a table of departments, a table of employees, a constraint to enforce the rule that all values in the department table are unique, and a constraint to enforce the rule that every employee must work for a valid department.

Example 5-1 Enforcing Business Rules with Constraints

Create table of departments:

DROP TABLE dept_tab;
CREATE TABLE dept_tab (
  deptname VARCHAR2(20),
  deptno   INTEGER
);
 

Create table of employees:

DROP TABLE emp_tab;
CREATE TABLE emp_tab (
  empname VARCHAR2(80),
  empno   INTEGER,
  deptno  INTEGER
);

Create constraint to enforce rule that all values in department table are unique:

ALTER TABLE dept_tab ADD PRIMARY KEY (deptno);

Create constraint to enforce rule that every employee must work for a valid department:

ALTER TABLE emp_tab ADD FOREIGN KEY (deptno) REFERENCES dept_tab(deptno);

Now, whenever you insert an employee record into emp_tab, Oracle Database checks that its deptno value appears in dept_tab.

Suppose that instead of using a constraint to enforce the rule that every employee must work for a valid department, you use a trigger that queries dept_tab to check that it contains the deptno value of the employee record to be inserted into emp_tab. Because the query uses consistent read (CR), it might miss uncommitted changes from other transactions. For more information about using triggers to enforce business rules, see Oracle Database Concepts.


See Also:

Oracle Database SQL Language Reference for syntactic and semantic information about constraints

Enforcing Business Rules with Both Constraints and Application Code

Enforcing business rules with both constraints and application code is recommended when application code can determine that data values are invalid without querying tables. The application code can provide immediate feedback to the user and reduce the load on the database by preventing attempts to insert invalid data into tables.

For Example 5-2, assume that Example 5-1 was run and then this column was added to the table emp_tab:

empgender VARCHAR2(1)

The only valid values for empgender are 'M' and 'F'. When someone tries to insert a row into emp_tab or update the value of emp_tab.empgender, application code can determine whether the new value for emp_tab.empgender is valid without querying a table. If the value is invalid, the application code can notify the user instead of trying to insert the invalid value, as in Example 5-2.

Example 5-2 Enforcing Business Rules with Both Constraints and Application Code

CREATE OR REPLACE PROCEDURE add_employee (
  e_name   emp_tab.empname%TYPE,
  e_gender emp_tab.empgender%TYPE,
  e_number emp_tab.empno%TYPE,
  e_dept   emp_tab.deptno%TYPE
) AUTHID DEFINER IS
BEGIN
  IF UPPER(e_gender) IN ('M','F') THEN
    INSERT INTO emp_tab VALUES (e_name, e_gender, e_number, e_dept);
  ELSE
    DBMS_OUTPUT.PUT_LINE('Gender must be M or F.');
  END IF;
END;
/
 
BEGIN
  add_employee ('Smith', 'H', 356, 20);
END;
/

Result:

Gender must be M or F.

Creating Indexes for Use with Constraints

When a unique or primary key constraint is enabled, Oracle Database creates an index automatically, but Oracle recommends that you create these indexes explicitly. If you want to use an index with a foreign key constraint, then you must create the index explicitly. For information about creating indexes explicitly, see Oracle Database Administrator's Guide or Oracle Database SQL Language Reference.

When a constraint can use an existing index, Oracle Database does not create an index for that constraint. Note that:


See Also:


When to Use NOT NULL Constraints

By default, a column can contain a NULL value. To ensure that the column never contains a NULL value, use the NOT NULL constraint (described in Oracle Database SQL Language Reference).

Use a NOT NULL constraint in both of these situations:

Example 5-3 uses the SQL*Plus command DESCRIBE to show which columns of the DEPARTMENTS table have NOT NULL constraints, and then shows what happens if you try to insert NULL values in columns that have NOT NULL constraints.

Example 5-3 Inserting NULL Values into Columns with NOT NULL Constraints

DESCRIBE DEPARTMENTS;

Result:

 Name                                      Null?    Type
 ----------------------------------------- -------- ------------
 
 DEPARTMENT_ID                             NOT NULL NUMBER(4)
 DEPARTMENT_NAME                           NOT NULL VARCHAR2(30)
 MANAGER_ID                                         NUMBER(6)
 LOCATION_ID                                        NUMBER(4)

Try to insert NULL into DEPARTMENT_ID column:

INSERT INTO DEPARTMENTS (
 DEPARTMENT_ID, DEPARTMENT_NAME, MANAGER_ID, LOCATION_ID
)
VALUES (NULL, 'Sales', 200, 1700);

Result:

VALUES (NULL, 'Sales', 200, 1700)
        *
ERROR at line 4:
ORA-01400: cannot insert NULL into ("HR"."DEPARTMENTS"."DEPARTMENT_ID")

Omitting a value for a column that cannot be NULL is the same as assigning it the value NULL:

INSERT INTO DEPARTMENTS (
  DEPARTMENT_NAME, MANAGER_ID, LOCATION_ID
)
VALUES ('Sales', 200, 1700);

Result:

INSERT INTO DEPARTMENTS (
*
ERROR at line 1:
ORA-01400: cannot insert NULL into ("HR"."DEPARTMENTS"."DEPARTMENT_ID")

You can prevent the preceding error by giving DEPARTMENT_ID a non-NULL default value. For more information, see "When to Use Default Column Values".

You can combine NOT NULL constraints with other constraints to further restrict the values allowed in specific columns. For example, the combination of NOT NULL and UNIQUE constraints forces the input of values in the UNIQUE key, eliminating the possibility that data in a new conflicts with data in an existing row. For more information, see "UNIQUE and NOT NULL Constraints on the Foreign Key".

When to Use Default Column Values

When an INSERT statement (described in Oracle Database SQL Language Reference) does not specify a value for a specific column, that column receives its default value. By default, that default value is NULL. You can change the default value when you define the column (with the CREATE TABLE statement, described in Oracle Database SQL Language Reference) or when you alter the column (with the ALTER TABLE statement, described in Oracle Database SQL Language Reference).


Note:

Giving a column a non-NULL default value does not ensure that the value of the column will never have the value NULL, as the NOT NULL constraint does. For information about the NOT NULL constraint, see "When to Use NOT NULL Constraints".

Use a default column value in these situations:

Choosing a Primary Key for a Table (PRIMARY KEY Constraint)

The primary key of a table uniquely identifies each row and ensures that no duplicate rows exist (and typically, this is its only purpose). Therefore, a primary key value cannot be NULL.

A table can have at most one primary key, but that key can have multiple columns (that is, it can be a composite key). To designate a primary key, use the PRIMARY KEY constraint.

Whenever practical, choose as the primary key a single column whose values are generated by a sequence. For information about sequences, see Oracle Database SQL Language Reference.

The second-best choice for a primary key is a single column whose values are all of the following:

Minimize your use of composite primary keys, whose values are long and cannot be generated by a sequence.


See Also:


When to Use UNIQUE Constraints

Use a UNIQUE constraint (which designates a unique key) on any column or combination of columns (except the primary key) where duplicate non-NULL values are not allowed. For example:

Unique KeyPrimary Key
Employee Social Security NumberEmployee number
Truck license plate numberTruck number
Customer phone number (country code column, area code column, and local phone number column)Customer number
Department name column and location columnDepartment number

Figure 5-1 shows a table with a UNIQUE constraint, a row that violates the constraint, and a row that satisfies it.

Figure 5-1 Rows That Violate and Satisfy a UNIQUE Constraint

Table with a UNIQUE Constraint
Description of "Figure 5-1 Rows That Violate and Satisfy a UNIQUE Constraint"


See Also:


Enforcing Referential Integrity with FOREIGN KEY Constraints

When two tables share one or more columns, you use can use a FOREIGN KEY constraint to enforce referential integrity—that is, to ensure that the shared columns always have the same values in both tables.


Note:

A FOREIGN KEY constraint is also called a referential integrity constraint, and its CONSTRAINT_TYPE is R in the static data dictionary views *_CONSTRAINTS.

Designate one table as the referenced or parent table and the other as the dependent or child table. In the parent table, define either a PRIMARY KEY or UNIQUE constraint on the shared columns. In the child table, define a FOREIGN KEY constraint on the shared columns. The shared columns now comprise a foreign key. Defining additional constraints on the foreign key affects the parent-child relationship—for details, see "Defining Relationships Between Parent and Child Tables".

Figure 5-2 shows a foreign key defined on the department number. It guarantees that every value in this column must match a value in the primary key of the department table. This constraint prevents erroneous department numbers from getting into the employee table.

Figure 5-2 shows parent and child tables that share one column, a row that violates the FOREIGN KEY constraint, and a row that satisfies it.

Figure 5-2 Rows That Violate and Satisfy a FOREIGN KEY Constraint

Tables with FOREIGN KEY Constraints
Description of "Figure 5-2 Rows That Violate and Satisfy a FOREIGN KEY Constraint"

Topics:


See Also:


FOREIGN KEY Constraints and NULL Values

Foreign keys allow key values that are all NULL, even if there are no matching PRIMARY or UNIQUE keys.

  • By default (without any NOT NULL or CHECK clauses), the FOREIGN KEY constraint enforces the match none rule for composite foreign keys in the ANSI/ISO standard.

  • To enforce the match full rule for NULL values in composite foreign keys, which requires that all components of the key be NULL or all be non-null, define a CHECK constraint that allows only all nulls or all non-nulls in the composite foreign key. For example, with a composite key comprised of columns A, B, and C:

    CHECK ((A IS NULL AND B IS NULL AND C IS NULL) OR
           (A IS NOT NULL AND B IS NOT NULL AND C IS NOT NULL))
    
  • In general, it is not possible to use declarative referential integrity to enforce the match partial rule for NULL values in composite foreign keys, which requires the non-null portions of the key to appear in the corresponding portions in the primary or unique key of a single row in the referenced table. You can often use triggers to handle this case, as described in Oracle Database PL/SQL Language Reference.

Defining Relationships Between Parent and Child Tables

Several relationships between parent and child tables can be determined by the other types of constraints defined on the foreign key in the child table.

No Constraints on the Foreign Key When no other constraints are defined on the foreign key, any number of rows in the child table can reference the same parent key value. This model allows nulls in the foreign key.

This model establishes a one-to-many relationship between the parent and foreign keys that allows undetermined values (nulls) in the foreign key. An example of such a relationship is shown in Figure 5-2 between the employee and department tables. Each department (parent key) has many employees (foreign key), and some employees might not be in a department (nulls in the foreign key).

NOT NULL Constraint on the Foreign Key When nulls are not allowed in a foreign key, each row in the child table must explicitly reference a value in the parent key because nulls are not allowed in the foreign key.

Any number of rows in the child table can reference the same parent key value, so this model establishes a one-to-many relationship between the parent and foreign keys. However, each row in the child table must have a reference to a parent key value; the absence of a value (a null) in the foreign key is not allowed. The same example in the previous section illustrates such a relationship. However, in this case, employees must have a reference to a specific department.

UNIQUE Constraint on the Foreign Key When a UNIQUE constraint is defined on the foreign key, only one row in the child table can reference a given parent key value. This model allows nulls in the foreign key.

This model establishes a one-to-one relationship between the parent and foreign keys that allows undetermined values (nulls) in the foreign key. For example, assume that the employee table had a column named MEMBERNO, referring to an employee membership number in the company insurance plan. Also, a table named INSURANCE has a primary key named MEMBERNO, and other columns of the table keep respective information relating to an employee insurance policy. The MEMBERNO in the employee table must be both a foreign key and a unique key:

  • To enforce referential integrity rules between the EMP_TAB and INSURANCE tables (the FOREIGN KEY constraint)

  • To guarantee that each employee has a unique membership number (the UNIQUE key constraint)

UNIQUE and NOT NULL Constraints on the Foreign Key When both UNIQUE and NOT NULL constraints are defined on the foreign key, only one row in the child table can reference a given parent key value, and because NULL values are not allowed in the foreign key, each row in the child table must explicitly reference a value in the parent key.

This model establishes a one-to-one relationship between the parent and foreign keys that does not allow undetermined values (nulls) in the foreign key. If you expand the previous example by adding a NOT NULL constraint on the MEMBERNO column of the employee table, in addition to guaranteeing that each employee has a unique membership number, you also ensure that no undetermined values (nulls) are allowed in the MEMBERNO column of the employee table.

Rules for Multiple FOREIGN KEY Constraints

Oracle Database allows a column to be referenced by multiple FOREIGN KEY constraints; there is no limit on the number of dependent keys. This situation might be present if a single column is part of two different composite foreign keys.

Deferring Constraint Checks

When Oracle Database checks a constraint, it signals an error if the constraint is not satisfied. To defer checking constraints until the end of the current transaction, use the SET CONSTRAINTS statement.


Note:

You cannot use the SET CONSTRAINTS statement inside a trigger.

When deferring constraint checks:

  • Select appropriate data.

    You might want to defer constraint checks on UNIQUE and FOREIGN keys if the data you are working with has any of these characteristics:

    • Tables are snapshots.

    • Some tables contain a large amount of data being manipulated by another application, which might not return the data in the same order.

  • Update cascade operations on foreign keys.

  • Ensure that constraints are deferrable.

    After identifying the appropriate tables, ensure that their FOREIGN, UNIQUE and PRIMARY key constraints are created DEFERRABLE.

  • Within the application that manipulates the data, set all constraints deferred before you begin processing any data, as follows:

    SET CONSTRAINTS ALL DEFERRED;
    
  • (Optional) Check for constraint violations immediately before committing the transaction.

    Immediately before the COMMIT statement, run the SET CONSTRAINTS ALL IMMEDIATE statement. If there are any problems with a constraint, this statement fails, and identifies the constraint that caused the error. If you commit while constraints are violated, the transaction rolls back and you get an error message.

In Example 5-4, the PRIMARY and FOREIGN keys of the table emp are created DEFERRABLE and then deferred.

Example 5-4 Deferring Constraint Checks

DROP TABLE dept; 
CREATE TABLE dept (
  deptno NUMBER PRIMARY KEY,
  dname  VARCHAR2 (30)
);
 
DROP TABLE emp; 
CREATE TABLE emp (
  empno  NUMBER,
  ename  VARCHAR2(30),
  deptno NUMBER,
  CONSTRAINT pk_emp_empno PRIMARY KEY (empno) DEFERRABLE,
  CONSTRAINT fk_emp_deptno FOREIGN KEY (deptno) REFERENCES dept(deptno) DEFERRABLE
);
 
INSERT INTO dept (deptno, dname) VALUES (10, 'Accounting');
INSERT INTO dept (deptno, dname) VALUES (20, 'SALES');
 
INSERT INTO emp (empno, ename, deptno) VALUES (1, 'Corleone', 10);
INSERT INTO emp (empno, ename, deptno) VALUES (2, 'Costanza', 20);
COMMIT;
 
SET CONSTRAINTS ALL DEFERRED;
 
UPDATE dept
SET deptno = deptno + 10
WHERE deptno = 20;
 

Query:

SELECT * from dept
ORDER BY deptno;
 

Result:

    DEPTNO DNAME
---------- ------------------------------
        10 Accounting
        30 SALES
 
2 rows selected.
 

Update:

UPDATE emp
SET deptno = deptno + 10
WHERE deptno = 20;
 

Result:

1 row updated.
 

Query:

SELECT * from emp
ORDER BY deptno;
 

Result:

     EMPNO ENAME                              DEPTNO
---------- ------------------------------ ----------
         1 Corleone                               10
         2 Costanza                               30
 
2 rows selected.

The SET CONSTRAINTS applies only to the current transaction, and its setting lasts for the duration of the transaction, or until another SET CONSTRAINTS statement resets the mode. The ALTER SESSION SET CONSTRAINTS statement applies only for the current session. The defaults specified when you create a constraint remain while the constraint exists.


See Also:

Oracle Database SQL Language Reference for more information about the SET CONSTRAINTS statement

Minimizing Space and Time Overhead for Indexes Associated with Constraints

When you create a UNIQUE or PRIMARY key, Oracle Database checks to see if an existing index enforces uniqueness for the constraint. If there is no such index, the database creates one.

When Oracle Database uses a unique index to enforce a constraint, and constraints associated with the unique index are dropped or disabled, the index is dropped. To preserve the statistics associated with the index (which would take a long time to re-create), specify the KEEP INDEX clause on the DROP CONSTRAINT statement.

While enabled foreign keys reference a PRIMARY or UNIQUE key, you cannot disable or drop the PRIMARY or UNIQUE key constraint or the index.


Note:

UNIQUE and PRIMARY keys with deferrable constraints must all use nonunique indexes.

To use existing indexes when creating unique and primary key constraints, include USING INDEX in the CONSTRAINT clause. For details and examples, see Oracle Database SQL Language Reference.

Guidelines for Indexing Foreign Keys

Index foreign keys unless the matching unique or primary key is never updated or deleted.


See Also:

Oracle Database Concepts for more information about indexing foreign keys

Referential Integrity in a Distributed Database

The declaration of a referential constraint cannot specify a foreign key that references a primary or unique key of a remote table.

However, you can maintain parent/child table relationships across nodes using triggers.


See Also:

Oracle Database PL/SQL Language Reference for more information about triggers that enforce referential integrity


Note:

If you decide to define referential integrity across the nodes of a distributed database using triggers, be aware that network failures can make both the parent table and the child table inaccessible.

For example, assume that the child table is in the SALES database, and the parent table is in the HQ database.

If the network connection between the two databases fails, then some data manipulation language (DML) statements against the child table (those that insert rows or update a foreign key value) cannot proceed, because the referential integrity triggers must have access to the parent table in the HQ database.


When to Use CHECK Constraints

Use CHECK constraints when you must enforce integrity rules based on logical expressions, such as comparisons. Never use CHECK constraints when any of the other types of constraints can provide the necessary checking.

Examples of CHECK constraints include:

Restrictions on CHECK Constraints

A CHECK constraint requires that a condition be true or unknown for every row of the table. If a statement causes the condition to evaluate to false, then the statement is rolled back. The condition of a CHECK constraint has these limitations:

  • The condition must be a boolean expression that can be evaluated using the values in the row being inserted or updated.

  • The condition cannot contain subqueries or sequences.

  • The condition cannot include the SYSDATE, UID, USER, or USERENV SQL functions.

  • The condition cannot contain the pseudocolumns LEVEL or ROWNUM.

  • The condition cannot contain the PRIOR operator.

  • The condition cannot contain a user-defined function.


See Also:


Designing CHECK Constraints

When using CHECK constraints, remember that a CHECK constraint is violated only if the condition evaluates to false; true and unknown values (such as comparisons with nulls) do not violate a check condition. Ensure that any CHECK constraint that you define is specific enough to enforce the rule.

For example, consider this CHECK constraint:

CHECK (Sal > 0 OR Comm >= 0)

At first glance, this rule may be interpreted as "do not allow a row in the employee table unless the employee salary is greater than zero or the employee commission is greater than or equal to zero." But if a row is inserted with a null salary, that row does not violate the CHECK constraint, regardless of whether the commission value is valid, because the entire check condition is evaluated as unknown. In this case, you can prevent such violations by placing NOT NULL constraints on both the SAL and COMM columns.


Note:

If you are not sure when unknown values result in NULL conditions, review the truth tables for the logical conditions in Oracle Database SQL Language Reference

Rules for Multiple CHECK Constraints

A single column can have multiple CHECK constraints that reference the column in its definition. There is no limit to the number of CHECK constraints that can be defined that reference a column.

The order in which the constraints are evaluated is not defined, so be careful not to rely on the order or to define multiple constraints that conflict with each other.

Choosing Between CHECK and NOT NULL Constraints

According to the ANSI/ISO standard, a NOT NULL constraint is an example of a CHECK constraint, where the condition is:

CHECK (column_name IS NOT NULL)

Therefore, you can write NOT NULL constraints for a single column using either a NOT NULL constraint or a CHECK constraint. The NOT NULL constraint is easier to use than the CHECK constraint.

In the case where a composite key can allow only all nulls or all values, you must use a CHECK constraint. For example, this CHECK constraint allows a key value in the composite key made up of columns C1 and C2 to contain either all nulls or all values:

CHECK ((C1 IS NULL AND C2 IS NULL) OR (C1 IS NOT NULL AND C2 IS NOT NULL))

Examples of Defining Constraints

Example 5-5 and Example 5-6 show how to create simple constraints during the prototype phase of your database design. In these examples, each constraint is given a name. Naming the constraints prevents the database from creating multiple copies of the same constraint, with different system-generated names, if the data definition language (DDL) statement runs multiple times.

Example 5-5 creates tables and their constraints at the same time, using the CREATE TABLE statement.

Example 5-5 Defining Constraints with the CREATE TABLE Statement

DROP TABLE DeptTab;
CREATE TABLE DeptTab (
  Deptno  NUMBER(3) CONSTRAINT pk_DeptTab_Deptno PRIMARY KEY,
  Dname   VARCHAR2(15),
  Loc     VARCHAR2(15),
  CONSTRAINT u_DeptTab_Dname_Loc UNIQUE (Dname, Loc),
  CONSTRAINT c_DeptTab_Loc
    CHECK (Loc IN ('NEW YORK', 'BOSTON', 'CHICAGO')));
 
DROP TABLE EmpTab;
CREATE TABLE EmpTab (
  Empno    NUMBER(5) CONSTRAINT pk_EmpTab_Empno PRIMARY KEY,
  Ename    VARCHAR2(15) NOT NULL,
  Job      VARCHAR2(10),
  Mgr      NUMBER(5) CONSTRAINT r_EmpTab_Mgr REFERENCES EmpTab,
  Hiredate DATE,
  Sal      NUMBER(7,2),
  Comm     NUMBER(5,2),
  Deptno   NUMBER(3) NOT NULL
  CONSTRAINT r_EmpTab_DeptTab REFERENCES DeptTab ON DELETE CASCADE);

Example 5-6 creates constraints for existing tables, using the ALTER TABLE statement.

You cannot create a validated constraint on a table if the table contains rows that violate the constraint.

Example 5-6 Defining Constraints with the ALTER TABLE Statement

-- Create tables without constraints:

DROP TABLE DeptTab; 
CREATE TABLE DeptTab (
  Deptno  NUMBER(3),
  Dname   VARCHAR2(15),
  Loc     VARCHAR2(15)
);
 
DROP TABLE EmpTab; 
CREATE TABLE EmpTab (
  Empno    NUMBER(5),
  Ename    VARCHAR2(15),
  Job      VARCHAR2(10),
  Mgr      NUMBER(5),
  Hiredate DATE,
  Sal      NUMBER(7,2),
  Comm     NUMBER(5,2),
  Deptno   NUMBER(3)
);
 
--Define constraints with the ALTER TABLE statement:

ALTER TABLE DeptTab
ADD CONSTRAINT pk_DeptTab_Deptno PRIMARY KEY (Deptno);
 
ALTER TABLE EmpTab
ADD CONSTRAINT fk_DeptTab_Deptno
FOREIGN KEY (Deptno) REFERENCES DeptTab;
 
ALTER TABLE EmpTab MODIFY (Ename VARCHAR2(15) NOT NULL);

See Also:

Oracle Database Administrator's Guide for information about creating and maintaining constraints for a large production database

Privileges Needed to Define Constraints

The creator of a constraint must have the ability to create tables (the CREATE TABLE or CREATE ANY TABLE system privilege), or the ability to alter the table (the ALTER object privilege for the table or the ALTER ANY TABLE system privilege) with the constraint. Additionally, UNIQUE and PRIMARY KEY constraints require that the owner of the table have either a quota for the tablespace that contains the associated index or the UNLIMITED TABLESPACE system privilege. FOREIGN KEY constraints also require some additional privileges.

Naming Constraints

Assign names to constraints NOT NULL, UNIQUE, PRIMARY KEY, FOREIGN KEY, and CHECK using the CONSTRAINT option of the constraint clause. This name must be unique among the constraints that you own. If you do not specify a constraint name, one is assigned automatically by Oracle Database.

Choosing your own name makes error messages for constraint violations more understandable, and prevents the creation of duplicate constraints with different names if the SQL statements are run more than once.

See the previous examples of the CREATE TABLE and ALTER TABLE statements for examples of the CONSTRAINT option of the constraint clause. The name of each constraint is included with other information about the constraint in the data dictionary.


See Also:

"Viewing Information About Constraints" for examples of static data dictionary views

Enabling and Disabling Constraints

This section explains the mechanisms and procedures for manually enabling and disabling constraints.

enabled constraint. When a constraint is enabled, the corresponding rule is enforced on the data values in the associated columns. The definition of the constraint is stored in the data dictionary.

disabled constraint. When a constraint is disabled, the corresponding rule is not enforced. The definition of the constraint is still stored in the data dictionary.

An integrity constraint represents an assertion about the data in a database. This assertion is always true when the constraint is enabled. The assertion might not be true when the constraint is disabled, because data that violates the integrity constraint can be in the database.

Topics:

Why Disable Constraints?

During day-to-day operations, keep constraints enabled. In certain situations, temporarily disabling the constraints of a table makes sense for performance reasons. For example:

  • When loading large amounts of data into a table using SQL*Loader

  • When performing batch operations that make massive changes to a table (such as changing each employee number by adding 1000 to the existing number)

  • When importing or exporting one table at a time

Temporarily turning off constraints can speed up these operations.

Creating Enabled Constraints (Default)

When you define an integrity constraint (using either CREATE TABLE or ALTER TABLE), Oracle Database enables the constraint by default. For code clarity, you can explicitly enable the constraint by including the ENABLE clause in its definition, as in Example 5-7.

Example 5-7 Creating Enabled Constraints

/* Use CREATE TABLE statement to create enabled constraint
   (ENABLE keyword is optional): */
 
DROP TABLE t1; 
CREATE TABLE t1 (Empno NUMBER(5) PRIMARY KEY ENABLE);
 
/* Create table without constraint
   and then use ALTER TABLE statement to add enabled constraint
   (ENABLE keyword is optional): */

DROP TABLE t2;
CREATE TABLE t2 (Empno NUMBER(5));
 
ALTER TABLE t2 ADD PRIMARY KEY (Empno) ENABLE;

Include the ENABLE clause when defining a constraint for a table to be populated a row at a time by individual transactions. This ensures that data is always consistent, and reduces the performance overhead of each DML statement.

An ALTER TABLE statement that tries to enable an integrity constraint fails if an existing row of the table violates the integrity constraint. The statement rolls back and the constraint definition is neither stored nor enabled.


See Also:

"Fixing Constraint Exceptions" for more information about rows that violate constraints

Creating Disabled Constraints

You define and disable an integrity constraint (using either CREATE TABLE or ALTER TABLE), by including the DISABLE clause in its definition, as in Example 5-8.

Example 5-8 Creating Disabled Constraints

/* Use CREATE TABLE statement to create disabled constraint */
 
DROP TABLE t1; 
CREATE TABLE t1 (Empno NUMBER(5) PRIMARY KEY DISABLE);
 
/* Create table without constraint
   and then use ALTER TABLE statement to add disabled constraint */
 
DROP TABLE t2; 
CREATE TABLE t2 (Empno NUMBER(5));
 
ALTER TABLE t2 ADD PRIMARY KEY (Empno) DISABLE;

Include the DISABLE clause when defining a constraint for a table to have large amounts of data inserted before anybody else accesses it, particularly if you must cleanse data after inserting it, or must fill empty columns with sequence numbers or parent/child relationships.

An ALTER TABLE statement that defines and disables a constraint never fails, because its rule is not enforced.

Enabling Existing Constraints

After you have cleansed the data and filled the empty columns, you can enable constraints that were disabled during data insertion.

To enable an existing constraint, use the ALTER TABLE statement with the ENABLE clause, as in Example 5-9.

Example 5-9 Enabling Existing Constraints

-- Create table with disabled constraints:
 
DROP TABLE DeptTab;
CREATE TABLE DeptTab (
  Deptno  NUMBER(3) PRIMARY KEY DISABLE,
  Dname   VARCHAR2(15),
  Loc     VARCHAR2(15),
  CONSTRAINT uk_DeptTab_Dname_Loc UNIQUE (Dname, Loc) DISABLE,
  CONSTRAINT c_DeptTab_Loc
  CHECK (Loc IN ('NEW YORK', 'BOSTON', 'CHICAGO')) DISABLE
);
 
-- Enable constraints:
 
 ALTER TABLE DeptTab
ENABLE PRIMARY KEY
ENABLE CONSTRAINT uk_DeptTab_Dname_Loc
ENABLE CONSTRAINT c_DeptTab_Loc;

An ALTER TABLE statement that attempts to enable an integrity constraint fails if any of the table rows violate the integrity constraint. The statement is rolled back and the constraint is not enabled.


See Also:

"Fixing Constraint Exceptions" for more information about rows that violate constraints

Disabling Existing Constraints

If you must perform a large insert or update when a table contains data, you can temporarily disable constraints to improve performance of the bulk operation.

To disable an existing constraint, use the ALTER TABLE statement with the DISABLE clause, as in Example 5-10.

Example 5-10 Disabling Existing Constraints

-- Create table with enabled constraints:
 
DROP TABLE DeptTab; 
CREATE TABLE DeptTab (
  Deptno  NUMBER(3) PRIMARY KEY ENABLE,
  Dname   VARCHAR2(15),
  Loc     VARCHAR2(15),
  CONSTRAINT uk_DeptTab_Dname_Loc UNIQUE (Dname, Loc) ENABLE,
  CONSTRAINT c_DeptTab_Loc
  CHECK (Loc IN ('NEW YORK', 'BOSTON', 'CHICAGO')) ENABLE
);
 
-- Disable constraints:
 
ALTER TABLE DeptTab
DISABLE PRIMARY KEY
DISABLE CONSTRAINT uk_DeptTab_Dname_Loc
DISABLE CONSTRAINT c_DeptTab_Loc;

Guidelines for Enabling and Disabling Key Constraints

When enabling or disabling UNIQUE, PRIMARY KEY, and FOREIGN KEY constraints, be aware of several important issues and prerequisites. UNIQUE key and PRIMARY KEY constraints are usually managed by the database administrator.

Fixing Constraint Exceptions

If a row of a table disobeys an integrity constraint, then this row is in violation of the constraint and is called an exception to the constraint. If any exceptions exist, then the constraint cannot be enabled. The rows that violate the constraint must be updated or deleted before the constraint can be enabled.

You can identify exceptions for a specific integrity constraint as you try to enable the constraint.


See Also:

"Fixing Constraint Exceptions" for more information about this procedure

When you try to create or enable a constraint, and the statement fails because integrity constraint exceptions exist, the statement is rolled back. You cannot enable the constraint until all exceptions are either updated or deleted. To determine which rows violate the integrity constraint, include the EXCEPTIONS option in the ENABLE clause of a CREATE TABLE or ALTER TABLE statement.


See Also:

Oracle Database Administrator's Guide for more information about responding to constraint exceptions

Modifying Constraints

Starting with Oracle8i, you can modify an existing constraint with the MODIFY CONSTRAINT clause, as in Example 5-11.


See Also:

Oracle Database SQL Language Reference for information about the parameters you can modify

Example 5-11 Modifying Constraints

/* Create & then modify a CHECK constraint: */
 
DROP TABLE X1Tab;
CREATE TABLE X1Tab (
  a1 NUMBER
  CONSTRAINT c_X1Tab_a1 CHECK (a1>3)
  DEFERRABLE DISABLE
);
 
ALTER TABLE X1Tab
MODIFY CONSTRAINT c_X1Tab_a1 ENABLE;
 
ALTER TABLE X1Tab
MODIFY CONSTRAINT c_X1Tab_a1 RELY;
 
ALTER TABLE X1Tab
MODIFY CONSTRAINT c_X1Tab_a1 INITIALLY DEFERRED;
 
ALTER TABLE X1Tab
MODIFY CONSTRAINT c_X1Tab_a1 ENABLE NOVALIDATE;
 
/* Create & then modify a PRIMARY KEY constraint: */
 
DROP TABLE t1; 
CREATE TABLE t1 (a1 INT, b1 INT);
 
ALTER TABLE t1
ADD CONSTRAINT pk_t1_a1 PRIMARY KEY(a1) DISABLE;
 
ALTER TABLE t1
MODIFY PRIMARY KEY INITIALLY IMMEDIATE
USING INDEX PCTFREE = 30 ENABLE NOVALIDATE;
 
ALTER TABLE t1
MODIFY PRIMARY KEY ENABLE NOVALIDATE;

Renaming Constraints

One property of a constraint that you can modify is its name. Situations in which you would rename a constraint include:

Example 5-12 shows how to find the system-generated name of a constraint and change it.

Example 5-12 Renaming a Constraint

DROP TABLE T;
CREATE TABLE T (
  C1 NUMBER PRIMARY KEY,
  C2 NUMBER
);
 

Query:

SELECT CONSTRAINT_NAME FROM USER_CONSTRAINTS
WHERE TABLE_NAME = 'T'
AND CONSTRAINT_TYPE = 'P';
 

Result (system-generated name of constraint name varies):

CONSTRAINT_NAME
------------------------------
SYS_C0013059
 
1 row selected.
 

Rename constraint from name reported in preceding query to T_C1_PK:

ALTER TABLE T
RENAME CONSTRAINT SYS_C0013059
TO T_C1_PK;
 

Query:

SELECT CONSTRAINT_NAME FROM USER_CONSTRAINTS
WHERE TABLE_NAME = 'T'
AND CONSTRAINT_TYPE = 'P';
 

Result:

CONSTRAINT_NAME
------------------------------
T_C1_PK
 
1 row selected.

Dropping Constraints

You can drop a constraint using the DROP clause of the ALTER TABLE statement. Situations in which you would drop a constraint include:

To drop a constraint and all other integrity constraints that depend on it, specify CASCADE.

Example 5-13 Dropping Constraints

-- Create table with constraints:

DROP TABLE DeptTab; 
CREATE TABLE DeptTab (
  Deptno  NUMBER(3) PRIMARY KEY,
  Dname   VARCHAR2(15),
  Loc     VARCHAR2(15),
  CONSTRAINT uk_DeptTab_Dname_Loc UNIQUE (Dname, Loc),
  CONSTRAINT c_DeptTab_Loc
  CHECK (Loc IN ('NEW YORK', 'BOSTON', 'CHICAGO'))
);
 
-- Drop constraints:
 
ALTER TABLE DeptTab
DROP PRIMARY KEY
DROP CONSTRAINT uk_DeptTab_Dname_Loc
DROP CONSTRAINT c_DeptTab_Loc;

When dropping UNIQUE, PRIMARY KEY, and FOREIGN KEY constraints, be aware of several important issues and prerequisites. UNIQUE and PRIMARY KEY constraints are usually managed by the database administrator.


See Also:


Managing FOREIGN KEY Constraints

FOREIGN KEY constraints enforce relationships between columns in different tables. Therefore, they cannot be enabled if the constraint of the referenced primary or unique key is not present or not enabled.

Data Types and Names for Foreign Key Columns

You must use the same data type for corresponding columns in the dependent and referenced tables. The column names need not match.

Limit on Columns in Composite Foreign Keys

Because foreign keys reference primary and unique keys of the parent table, and PRIMARY KEY and UNIQUE key constraints are enforced using indexes, composite foreign keys are limited to 32 columns.

Foreign Key References Primary Key by Default

If the column list is not included in the REFERENCES option when defining a FOREIGN KEY constraint (single column or composite), then Oracle Database assumes that you intend to reference the primary key of the specified table. Alternatively, you can explicitly specify the column(s) to reference in the parent table within parentheses. Oracle Database automatically checks to verify that this column list references a primary or unique key of the parent table. If it does not, then an informative error is returned.

Privileges Required to Create FOREIGN KEY Constraints

To create a FOREIGN KEY constraint, the creator of the constraint must have privileged access to the parent and child tables.

  • Parent Table The creator of the referential integrity constraint must own the parent table or have REFERENCES object privileges on the columns that constitute the parent key of the parent table.

  • Child Table The creator of the referential integrity constraint must have the ability to create tables (that is, the CREATE TABLE or CREATE ANY TABLE system privilege) or the ability to alter the child table (that is, the ALTER object privilege for the child table or the ALTER ANY TABLE system privilege).

In both cases, necessary privileges cannot be obtained through a role; they must be explicitly granted to the creator of the constraint.

These restrictions allow:

  • The owner of the child table to explicitly decide which constraints are enforced and which other users can create constraints

  • The owner of the parent table to explicitly decide if foreign keys can depend on the primary and unique keys in her tables

Choosing How Foreign Keys Enforce Referential Integrity

Oracle Database allows different types of referential integrity actions to be enforced, as specified with the definition of a FOREIGN KEY constraint:

  • Prevent Delete or Update of Parent Key The default setting prevents the deletion or update of a parent key if there is a row in the child table that references the key. For example:

    CREATE TABLE Emp_tab (
    FOREIGN KEY (Deptno) REFERENCES Dept_tab);
     
    
  • Delete Child Rows When Parent Key Deleted The ON DELETE CASCADE action allows parent key data that is referenced from the child table to be deleted, but not updated. When data in the parent key is deleted, all rows in the child table that depend on the deleted parent key values are also deleted. To specify this referential action, include the ON DELETE CASCADE option in the definition of the FOREIGN KEY constraint. For example:

    CREATE TABLE Emp_tab (
    FOREIGN KEY (Deptno) REFERENCES Dept_tab
    ON DELETE CASCADE);
    
  • Set Foreign Keys to Null When Parent Key Deleted The ON DELETE SET NULL action allows data that references the parent key to be deleted, but not updated. When referenced data in the parent key is deleted, all rows in the child table that depend on those parent key values have their foreign keys set to null. To specify this referential action, include the ON DELETE SET NULL option in the definition of the FOREIGN KEY constraint. For example:

    CREATE TABLE Emp_tab (
    FOREIGN KEY (Deptno) REFERENCES Dept_tab  
    ON DELETE SET NULL); 
    

Viewing Information About Constraints

To find the names of constraints, what columns they affect, and other information to help you manage them, query the static data dictionary views *_CONSTRAINTS and *_CONS_COLUMNS, as in Example 5-14.


See Also:

Oracle Database Reference for information about *_CONSTRAINTS and *_CONS_COLUMNS

Example 5-14 Viewing Information About Constraints

DROP TABLE DeptTab;
CREATE TABLE DeptTab (
  Deptno  NUMBER(3) PRIMARY KEY,
  Dname   VARCHAR2(15),
  Loc     VARCHAR2(15),
  CONSTRAINT uk_DeptTab_Dname_Loc UNIQUE (Dname, Loc),
  CONSTRAINT c_DeptTab_Loc
  CHECK (Loc IN ('NEW YORK', 'BOSTON', 'CHICAGO'))
);
 
DROP TABLE EmpTab; 
CREATE TABLE EmpTab (
  Empno    NUMBER(5) PRIMARY KEY,
  Ename    VARCHAR2(15) NOT NULL,
  Job      VARCHAR2(10),
  Mgr      NUMBER(5) CONSTRAINT r_EmpTab_Mgr
             REFERENCES EmpTab ON DELETE CASCADE,
  Hiredate DATE,
  Sal      NUMBER(7,2),
  Comm     NUMBER(5,2),
  Deptno   NUMBER(3) NOT NULL
  CONSTRAINT r_EmpTab_Deptno REFERENCES DeptTab
);
 
-- Format columns (optional):
 
COLUMN CONSTRAINT_NAME   FORMAT A20;
COLUMN CONSTRAINT_TYPE   FORMAT A4 HEADING 'TYPE';
COLUMN TABLE_NAME        FORMAT A10;
COLUMN R_CONSTRAINT_NAME FORMAT A17;
COLUMN SEARCH_CONDITION  FORMAT A40;
COLUMN COLUMN_NAME       FORMAT A12;

List accessible constraints in DeptTab and EmpTab:

SELECT CONSTRAINT_NAME, CONSTRAINT_TYPE, TABLE_NAME, R_CONSTRAINT_NAME
FROM USER_CONSTRAINTS
WHERE (TABLE_NAME = 'DEPTTAB' OR TABLE_NAME = 'EMPTAB')
ORDER BY CONSTRAINT_NAME;
 

Result:

CONSTRAINT_NAME      TYPE TABLE_NAME R_CONSTRAINT_NAME
-------------------- ---- ---------- -----------------
C_DEPTTAB_LOC        C    DEPTTAB
R_EMPTAB_DEPTNO      R    EMPTAB     SYS_C006286
R_EMPTAB_MGR         R    EMPTAB     SYS_C006290
SYS_C006286          P    DEPTTAB
SYS_C006288          C    EMPTAB
SYS_C006289          C    EMPTAB
SYS_C006290          P    EMPTAB
UK_DEPTTAB_DNAME_LOC U    DEPTTAB
 
8 rows selected.
 

Distinguish between NOT NULL and CHECK constraints in DeptTab and EmpTab:

SELECT CONSTRAINT_NAME, SEARCH_CONDITION
FROM USER_CONSTRAINTS
WHERE (TABLE_NAME = 'DEPTTAB' OR TABLE_NAME = 'EMPTAB')
AND CONSTRAINT_TYPE = 'C'
ORDER BY CONSTRAINT_NAME;
 

Result:

CONSTRAINT_NAME      SEARCH_CONDITION
-------------------- ----------------------------------------
C_DEPTTAB_LOC        Loc IN ('NEW YORK', 'BOSTON', 'CHICAGO')
SYS_C006288          "ENAME" IS NOT NULL
SYS_C006289          "DEPTNO" IS NOT NULL
 
3 rows selected.
 

For DeptTab and EmpTab, list columns that constitute constraints:

SELECT CONSTRAINT_NAME, TABLE_NAME, COLUMN_NAME
FROM USER_CONS_COLUMNS
WHERE (TABLE_NAME = 'DEPTTAB' OR TABLE_NAME = 'EMPTAB')
ORDER BY CONSTRAINT_NAME;
 

Result:

CONSTRAINT_NAME      TABLE_NAME COLUMN_NAME
-------------------- ---------- ------------
C_DEPTTAB_LOC        DEPTTAB    LOC
R_EMPTAB_DEPTNO      EMPTAB     DEPTNO
R_EMPTAB_MGR         EMPTAB     MGR
SYS_C006286          DEPTTAB    DEPTNO
SYS_C006288          EMPTAB     ENAME
SYS_C006289          EMPTAB     DEPTNO
SYS_C006290          EMPTAB     EMPNO
UK_DEPTTAB_DNAME_LOC DEPTTAB    LOC
UK_DEPTTAB_DNAME_LOC DEPTTAB    DNAME
 
9 rows selected.

Note that:

These constraints are explicitly listed in the SEARCH_CONDITION column:

PK7cmmPK|%AOEBPS/title.htm Oracle Database Advanced Application Developer's Guide, 11g Release 2 (11.2)

Oracle® Database

Advanced Application Developer's Guide

11g Release 2 (11.2)

E25518-05

April 2012


Oracle Database Advanced Application Developer's Guide, 11g Release 2 (11.2)

E25518-05

Copyright © 1996, 2012, Oracle and/or its affiliates. All rights reserved.

Primary Author: Sheila Moore

Contributing Authors: D. Adams, L. Ashdown, M. Cowan, T. Kyte, J. Melnick, R. Moran, E. Paapanen, J. Russell, R. Strohm, R. Ward

Contributors:  D. Alpern, G. Arora, C. Barclay, D. Bronnikov, T. Chang, L. Chen, B. Cheng, M. Davidson, R. Day, R. Decker, G. Doherty, D. Elson, A. Ganesh, M. Hartstein, Y. Hu, J. Huang, C. Iyer, N. Jain, R. Jenkins Jr., S. Kotsovolos, V. Krishnaswamy, S. Kumar, C. Lei, B. Llewellyn, D. Lorentz, V. Moore, K. Muthukkaruppan, V. Moore, J. Muller, R. Murthy, R. Pang, B. Sinha, S. Vemuri, W. Wang, D. Wong, A. Yalamanchi, Q. Yu

This software and related documentation are provided under a license agreement containing restrictions on use and disclosure and are protected by intellectual property laws. Except as expressly permitted in your license agreement or allowed by law, you may not use, copy, reproduce, translate, broadcast, modify, license, transmit, distribute, exhibit, perform, publish, or display any part, in any form, or by any means. Reverse engineering, disassembly, or decompilation of this software, unless required by law for interoperability, is prohibited.

The information contained herein is subject to change without notice and is not warranted to be error-free. If you find any errors, please report them to us in writing.

If this is software or related documentation that is delivered to the U.S. Government or anyone licensing it on behalf of the U.S. Government, the following notice is applicable:

U.S. GOVERNMENT RIGHTS Programs, software, databases, and related documentation and technical data delivered to U.S. Government customers are "commercial computer software" or "commercial technical data" pursuant to the applicable Federal Acquisition Regulation and agency-specific supplemental regulations. As such, the use, duplication, disclosure, modification, and adaptation shall be subject to the restrictions and license terms set forth in the applicable Government contract, and, to the extent applicable by the terms of the Government contract, the additional rights set forth in FAR 52.227-19, Commercial Computer Software License (December 2007). Oracle America, Inc., 500 Oracle Parkway, Redwood City, CA 94065.

This software or hardware is developed for general use in a variety of information management applications. It is not developed or intended for use in any inherently dangerous applications, including applications that may create a risk of personal injury. If you use this software or hardware in dangerous applications, then you shall be responsible to take all appropriate fail-safe, backup, redundancy, and other measures to ensure its safe use. Oracle Corporation and its affiliates disclaim any liability for any damages caused by use of this software or hardware in dangerous applications.

Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners.

Intel and Intel Xeon are trademarks or registered trademarks of Intel Corporation. All SPARC trademarks are used under license and are trademarks or registered trademarks of SPARC International, Inc. AMD, Opteron, the AMD logo, and the AMD Opteron logo are trademarks or registered trademarks of Advanced Micro Devices. UNIX is a registered trademark of The Open Group.

This software or hardware and documentation may provide access to or information on content, products, and services from third parties. Oracle Corporation and its affiliates are not responsible for and expressly disclaim all warranties of any kind with respect to third-party content, products, and services. Oracle Corporation and its affiliates will not be responsible for any loss, costs, or damages incurred due to your access to or use of third-party content, products, or services.

PK PK|%AOEBPS/adfns_publish.htm!Tޫ Developing Applications with the Publish-Subscribe Model

16 Developing Applications with the Publish-Subscribe Model

This chapter explains how to develop applications on the publish-subscribe model.

Topics:

Introduction to the Publish-Subscribe Model

Because the database is the most significant resource of information within the enterprise, Oracle created a publish-subscribe solution for enterprise information delivery and messaging to complement this role.

Networking technologies and products enable a high degree of connectivity across a large number of computers, applications, and users. In these environments, it is important to provide asynchronous communications for the class of distributed systems that operate in a loosely-coupled and autonomous fashion, and which require operational immunity from network failures. This requirement is filled by various middleware products that are characterized as messaging, message-oriented middleware (MOM), message queuing, or publish-subscribe.

Applications that communicate through a publish and subscribe paradigm require the sending applications (publishers) to publish messages without explicitly specifying recipients or having knowledge of intended recipients. Similarly, receiving applications (subscribers) must receive only those messages that the subscriber has registered an interest in.

This decoupling between senders and recipients is usually accomplished by an intervening entity between the publisher and the subscriber, which serves as a level of indirection. This intervening entity is a queue that represents a subject or channel. Figure 16-1 illustrates publish and subscribe functionality.

Figure 16-1 Oracle Publish-Subscribe Functionality

Oracle Publish-Subscribe Functionality
Description of "Figure 16-1 Oracle Publish-Subscribe Functionality"

A subscriber subscribes to a queue by expressing interest in messages enqueued to that queue and by using a subject- or content-based rule as a filter. This results in a set of rule-based subscriptions associated with a given queue.

At run time, publishers post messages to various queues. The queue (in other words, the delivery mechanisms of the underlying infrastructure) then delivers messages that match the various subscriptions to the appropriate subscribers.

Publish-Subscribe Architecture

Oracle Database includes these features to support database-enabled publish-subscribe messaging:

Database Events

Database events support declarative definitions for publishing database events, detection, and runtime publication of such events. This feature enables active publication of information to end-users in an event-driven manner, to complement the traditional pull-oriented approaches to accessing information.

Oracle Advanced Queuing

Oracle Advanced Queuing (AQ) supports a queue-based publish-subscribe paradigm. Database queues serve as a durable store for messages, along with capabilities to allow publish and subscribe based on queues. A rules-engine and subscription service dynamically route messages to recipients based on expressed interest. This allows decoupling of addressing between senders and receivers to complement the existing explicit sender-receiver message addressing.

Client Notification

Client notifications support asynchronous delivery of messages to interested subscribers, enabling database clients to register interest in certain queues, and it enables these clients to receive notifications when publications on such queues occur. Asynchronous delivery of messages to database clients is in contrast to the traditional polling techniques used to retrieve information.

Publish-Subscribe Concepts

queue

A queue is an entity that supports the notion of named subjects of interest. Queues can be characterized as persistent or nonpersistent (lightweight).

A persistent queue serves as a durable container for messages. Messages are delivered in a deferred and reliable mode.

The underlying infrastructure of a nonpersistent, or lightweight, queue pushes the messages published to connected clients in a lightweight, at-best-once, manner.

agent

Publishers and subscribers are internally represented as agents.

An agent is a persistent logical subscribing entity that expresses interest in a queue through a subscription. An agent has properties, such as an associated subscription, an address, and a delivery mode for messages. In this context, an agent is an electronic proxy for a publisher or subscriber.

client

A client is a transient physical entity. The attributes of a client include the physical process where the client programs run, the node name, and the client application logic. Several clients can act on behalf of a single agent. The same client, if authorized, can act on behalf of multiple agents.

rule on a queue

A rule on a queue is specified as a conditional expression using a predefined set of operators on the message format attributes or on the message header attributes. Each queue has an associated message content format that describes the structure of the messages represented by that queue. The message format may be unstructured (RAW) or it may have a well-defined structure (ADT). This allows both subject- or content-based subscriptions.

subscriber

Subscribers (agents) may specify subscriptions on a queue using a rule. Subscribers are durable and are stored in a catalog.

database event publication framework

The database represents a significant source for publishing information. An event framework is proposed to allow declarative definition of database event publication. As these pre-defined events occur, the framework detects and publishes such events. This allows active delivery of information to end-users in an event-driven manner as part of the publish-subscribe capability.

registration

Registration is the process of associated delivery information by a given client, acting on behalf of an agent. There is an important distinction between the subscription and registration related to the agent/client separation.

Subscription indicates an interest in a particular queue by an agent. It does not specify where and how delivery must occur. Delivery information is a physical property that is associated with a client, and it is a transient manifestation of the logical agent (the subscriber). A specific client process acting on behalf of an agent registers delivery information by associating a host and port, indicating where the delivery is to be done, and a callback, indicating how there delivery is to be done.

publishing a message

Publishers publish messages to queues by using the appropriate queuing interfaces. The interfaces may depend on which model the queue is implemented on. For example, an enqueue call represents the publishing of a message.

rules engine

When a message is posted or published to a given queue, a rules engine extracts the set of candidate rules from all rules defined on that queue that match the published message.

subscription services

Corresponding to the list of candidate rules on a given queue, the set of subscribers that match the candidate rules can be evaluated. In turn, the set of agents corresponding to this subscription list can be determined and notified.

posting

The queue notifies all registered clients of the appropriate published messages. This concept is called posting. When the queue must notify all interested clients, it posts the message to all registered clients.

receiving a message

A subscriber may receive messages through any of these mechanisms:

Examples of a Publish-Subscribe Mechanism

This example shows how database events, client notification, and AQ work to implement publish-subscribe.

Rem ------------------------------------------------------
REM create queue table for persistent multiple consumers:
Rem ------------------------------------------------------

Rem  Create or replace a queue table
BEGIN
DBMS_AQADM.CREATE_QUEUE_TABLE(
   Queue_table        =>  'Pubsub.Raw_msg_table', 
   Multiple_consumers =>   TRUE,
   Queue_payload_type =>  'RAW',
   Compatible         =>  '8.1');
END;
/
Rem ------------------------------------------------------
Rem  Create a persistent queue for publishing messages:
Rem ------------------------------------------------------

Rem  Create a queue for logon events
BEGIN
   DBMS_AQADM.CREATE_QUEUE(
         Queue_name     =>   'Pubsub.Logon',
         Queue_table    =>   'Pubsub.Raw_msg_table',
         Comment        =>   'Q for error triggers');
END;
/

Rem ------------------------------------------------------
Rem  Start the queue:
Rem ------------------------------------------------------

BEGIN
   DBMS_AQADM.START_QUEUE('pubsub.logon');
END;
/

Rem ------------------------------------------------------
Rem  define new_enqueue for convenience:
Rem ------------------------------------------------------

CREATE OR REPLACE PROCEDURE New_enqueue(
               Queue_name      IN VARCHAR2,
               Payload         IN RAW ,
               Correlation     IN VARCHAR2 := NULL,
               Exception_queue IN VARCHAR2 := NULL)
AS

Enq_ct     DBMS_AQ.Enqueue_options_t;
Msg_prop   DBMS_AQ.Message_properties_t;
Enq_msgid  RAW(16);
Userdata   RAW(1000);
 
BEGIN
   Msg_prop.Exception_queue := Exception_queue;
   Msg_prop.Correlation := Correlation;
   Userdata := Payload;

DBMS_AQ.ENQUEUE(Queue_name, Enq_ct, Msg_prop, Userdata, Enq_msgid);
END;
/

Rem ------------------------------------------------------
Rem  add subscriber with rule based on current user name, 
Rem  using correlation_id
Rem ------------------------------------------------------

 
DECLARE
Subscriber Sys.Aq$_agent;
BEGIN
   Subscriber := sys.aq$_agent('SNOOP', NULL, NULL);
DBMS_AQADM.ADD_SUBSCRIBER(
    Queue_name         => 'Pubsub.logon',
    Subscriber         => subscriber,
    Rule               => 'CORRID = ''HR'' ');
END;
/

Rem ------------------------------------------------------
Rem  create a trigger on logon on database:
Rem ------------------------------------------------------


Rem  create trigger on after logon:
CREATE OR REPLACE TRIGGER pubsub.Systrig2
   AFTER LOGON
   ON DATABASE
   BEGIN
      New_enqueue('Pubsub.Logon', HEXTORAW('9999'), Dbms_standard.login_user);
   END;
/
ub4 namespace = OCI_SUBSCR_NAMESPACE_AQ;

/* callback function for notification of logon of user 'HR' on database: */

ub4 notifySnoop(ctx, subscrhp, pay, payl, desc, mode)
dvoid *ctx;
OCISubscription *subscrhp;
dvoid *pay;
ub4 payl;
dvoid *desc;
ub4 mode;
{
    printf("Notification : User HR Logged on\n");
}

int main()
{
    OCISession *authp = (OCISession *) 0;
    OCISubscription *subscrhpSnoop = (OCISubscription *)0;

    /*****************************************************
       Initialize OCI Process/Environment
       Initialize Server Contexts
       Connect to Server
       Set Service Context
    ******************************************************/

    /* Registration Code Begins */

    /* Each call to initSubscriptionHn allocates 
           and Initialises a Registration Handle */

   
    initSubscriptionHn(    &subscrhpSnoop,    /* subscription handle */
        "ADMIN:PUBSUB.SNOOP", /* subscription name */ 
                  /* <agent_name>:<queue_name> */
        (dvoid*)notifySnoop); /* callback function */

     /*****************************************************
       The Client Process does not need a live Session for Callbacks
       End Session and Detach from Server
     ******************************************************/

    OCISessionEnd ( svchp,  errhp, authp, (ub4) OCI_DEFAULT);

    /* detach from server */
    OCIServerDetach( srvhp, errhp, OCI_DEFAULT);

    while (1)     /* wait for callback */
        sleep(1);

}

void initSubscriptionHn (subscrhp,
subscriptionName,
func)

OCISubscription **subscrhp;
char* subscriptionName;
dvoid * func;
{

    /* allocate subscription handle: */

    (void) OCIHandleAlloc((dvoid *) envhp, (dvoid **)subscrhp, 
        (ub4) OCI_HTYPE_SUBSCRIPTION,
        (size_t) 0, (dvoid **) 0);

    /* set subscription name in handle: */

    (void) OCIAttrSet((dvoid *) *subscrhp, (ub4) OCI_HTYPE_SUBSCRIPTION,
        (dvoid *) subscriptionName, 
        (ub4) strlen((char *)subscriptionName),
        (ub4) OCI_ATTR_SUBSCR_NAME, errhp);

    /* set callback function in handle: */

    (void) OCIAttrSet((dvoid *) *subscrhp, (ub4) OCI_HTYPE_SUBSCRIPTION,
        (dvoid *) func, (ub4) 0,
        (ub4) OCI_ATTR_SUBSCR_CALLBACK, errhp);

    (void) OCIAttrSet((dvoid *) *subscrhp, (ub4) OCI_HTYPE_SUBSCRIPTION,
        (dvoid *) 0, (ub4) 0,
        (ub4) OCI_ATTR_SUBSCR_CTX, errhp);

    /* set namespace in handle: */

    (void) OCIAttrSet((dvoid *) *subscrhp, (ub4) OCI_HTYPE_SUBSCRIPTION,
        (dvoid *) &namespace, (ub4) 0,
        (ub4) OCI_ATTR_SUBSCR_NAMESPACE, errhp);

    checkerr(errhp, OCISubscriptionRegister(svchp, subscrhp, 1, errhp,

        OCI_DEFAULT));
}

If user HR logs on to the database, the client is notified, and the call back function notifySnoop is invoked.

PKwۇ&T!TPK|%A OEBPS/loe.htm.3 List of Examples

List of Examples

PK33.3PK|%AOEBPS/adfns_xa.htm Developing Applications with Oracle XA

15 Developing Applications with Oracle XA

This chapter explains how to use the Oracle XA library. Typically, you use this library in applications that work with transaction monitors. The XA features are most useful in applications in which transactions interact with multiple databases.

Topics:


See Also:

  • X/Open CAE Specification - Distributed Transaction Processing: The XA Specification, X/Open Document Number XO/CAE/91/300, for an overview of XA, including basic architecture. Access at http://www.opengroup.org/pubs/catalog/c193.htm.

  • Oracle Call Interface Programmer's Guide for background and reference information about the Oracle XA library

  • The Oracle Database platform-specific documentation for information about library linking filenames

  • README for changes, bugs, and restrictions in the Oracle XA library for your platform


X/Open Distributed Transaction Processing (DTP)

The X/Open Distributed Transaction Processing (DTP) architecture defines a standard architecture or interface that enables multiple application programs (APs) to share resources provided by multiple, and possibly different, resource managers (RMs). It coordinates the work between APs and RMs into global transactions.

The Oracle XA library conforms to the X/Open software architecture's XA interface specification. The Oracle XA library is an external interface that enables a client-side transaction manager (TM) that is not an Oracle client-side TM to coordinate global transactions, thereby allowing inclusion of database RMs that are not Oracle Database RMs in distributed transactions. For example, a client application can manage an Oracle Database transaction and a transaction in an NTFS file system as a single, global transaction.

Figure 15-1 illustrates a possible X/Open DTP model.

Figure 15-1 Possible DTP Model

Possible DTP Model
Description of "Figure 15-1 Possible DTP Model"

Topics:

DTP Terminology

Resource Manager (RM)

A resource manager controls a shared, recoverable resource that can be returned to a consistent state after a failure. Examples are relational databases, transactional queues, and transactional file systems. Oracle Database is an RM and uses its online redo log and undo segments to return to a consistent state after a failure.

Distributed Transaction

A distributed transaction, also called a global transaction, is a client transaction that involves updates to multiple distributed resources and requires "all-or-none" semantics across distributed RMs.

Branch

A branch is a unit of work contained within one RM. Multiple branches comprise a global transaction. For Oracle Database, each branch maps to a local transaction inside the database server.

Transaction Manager (TM)

A transaction manager provides an API for specifying the boundaries of the transaction and manages commit and recovery. The TM implements a two-phase commit engine to provide "all-or-none" semantics across distributed RMs.

An external TM is a middle-tier component that resides outside Oracle Database. Normally, the database is its own internal TM. Using a standards-based TM enables Oracle Database to cooperate with other heterogeneous RMs in a single transaction.

Transaction Processing Monitor (TPM)

A TM is usually provided by a transaction processing monitor (TPM), such as:

  • Oracle Tuxedo

  • IBM Transarc Encina

  • IBM CICS

A TPM coordinates the flow of transaction requests between the client processes that issue requests and the back-end servers that process them. Basically, a TPM coordinates transactions that require the services of several different types of back-end processes, such as application servers and RMs distributed over a network.

The TPM synchronizes any commits or rollbacks required to complete a distributed transaction. The TM portion of the TPM is responsible for controlling when distributed commits and rollbacks take place. Thus, if a distributed application program takes advantage of a TPM, then the TM portion of the TPM is responsible for controlling the two-phase commit protocol. The RMs enable the TMs to perform this task.

Because the TM controls distributed commits or rollbacks, it must communicate directly with Oracle Database (or any other RM) through the XA interface. It uses Oracle XA library subprograms, which are described in "Oracle XA Library Subprograms", to tell Oracle Database how to process the transaction, based on its knowledge of all RMs in the transaction.

Two-Phase Commit Protocol

The Oracle XA library interface follows the two-phase commit protocol. The sequence of events is as follows:

  1. In the prepare phase, the TM asks each RM to guarantee that it can commit any part of the transaction. If this is possible, then the RM records its prepared state and replies affirmatively to the TM. If it is not possible, then the RM might roll back any work, reply negatively to the TM, and forget about the transaction. The protocol allows the application, or any RM, to roll back the transaction unilaterally until the prepare phase completes.

  2. In phase two, the TM records the commit decision and issues a commit or rollback to all RMs participating in the transaction. TM can issue a commit for an RM only if all RMs have replied affirmatively to phase one.

Application Program (AP)

An application program defines transaction boundaries and specifies actions that constitute a transaction. For example, an AP can be a precompiler or Oracle Call Interface (OCI) program. The AP operates on the RM resource through its native interface, for example, SQL.

TX Interface

An application program starts and completes all transaction control operations through the TM through an interface called TX. The AP does not directly use the XA interface. APs are not aware of branches that fork in the middle-tier: application threads do not explicitly join, leave, suspend, and resume branch work, instead the TM portion of the transaction processing monitor manages the branches of a global transaction for APs. Ultimately, APs call the TM to commit all-or-none.


Note:

The naming conventions for the TX interface and associated subprograms are vendor-specific. For example, the tx_open call might be referred to as tp_open on your system. In some cases, the calls might be implicit, for example, at the entry to a transactional RPC. See the documentation supplied with the transaction processing monitor for details.

Tight and Loose Coupling

Application threads are tightly coupled if the RM considers them as a single entity for all isolation semantic purposes. Tightly coupled branches must see changes in each other. Furthermore, an external client must either see all changes of a tightly coupled set or none of the changes. If application threads are not tightly coupled, then they are loosely coupled.

Dynamic and Static Registration

Oracle Database supports both dynamic and static registration. In dynamic registration, the RM runs an application callback before starting any work. In static registration, you must call xa_start for each RM before starting any work, even if some RMs are not involved.

Required Public Information

As a resource manager, Oracle Database must publish the information described in Table 15-1.

Table 15-1 Required XA Features Published by Oracle Database

XA FeatureOracle Database Details

xa_switch_t structures

The Oracle Database xa_switch_t structure name is xaosw for static registration and xaoswd for dynamic registration. These structures contain entry points and other information for the resource manager.

xa_switch_t resource manager

The Oracle Database resource manager name within the xa_switch_t structure is Oracle_XA.

Close string

The close string used by xa_close is ignored and can be null.

Open string

For the description of the format of the open string that xa_open uses, see "Defining the xa_open String".

Libraries

Libraries needed to link applications using Oracle XA have platform-specific names. The procedure is similar to linking an ordinary precompiler or OCI program except that you might have to link any TPM-specific libraries.

If you are not using sqllib, then link with $ORACLE_HOME/rdbms/lib/xaonsl.o or $ORACLE_HOME/rdbms/lib32/xaonsl.o (for 32 bit application on 64 bit platforms).

Requirements

None. The functionality to support XA is part of both Standard Edition and Enterprise Edition.


Oracle XA Library Subprograms

The Oracle XA library subprograms enable a TM to tell Oracle Database how to process transactions. Generally, the TM must open the resource by using xa_open. Typically, the opening of the resource results from the AP call to tx_open. Some TMs might call xa_open implicitly when the application begins.

Similarly, there is a close (using xa_close) that occurs when the application is finished with the resource. The close might occur when the AP calls tx_close or when the application terminates.

The TM instructs the RMs to perform several other tasks, which include:

Topics:

Oracle XA Library Subprograms

XA Library subprograms are described in Table 15-2.

Table 15-2 XA Library Subprograms

XA SubprogramDescription

xa_open

Connects to the RM.

xa_close

Disconnects from the RM.

xa_start

Starts a transaction and associates it with the given transaction ID (XID), or associates the process with an existing transaction.

xa_end

Disassociates the process from the given XID.

xa_rollback

Rolls back the transaction associated with the given XID.

xa_prepare

Prepares the transaction associated with the given XID. This is the first phase of the two-phase commit protocol.

xa_commit

Commits the transaction associated with the given XID. This is the second phase of the two-phase commit protocol.

xa_recover

Retrieves a list of prepared, heuristically committed, or heuristically rolled back transactions.

xa_forget

Forgets the heuristically completed transaction associated with the given XID.


In general, the AP need not worry about the subprograms in Table 15-2 except to understand the role played by the xa_open string.

Oracle XA Interface Extensions

Oracle Database's XA interface includes some additional functions, which are described in Table 15-3.

Table 15-3 Oracle XA Interface Extensions

FunctionDescription

OCISvcCtx *xaoSvcCtx(text *dbname)

Returns the OCI service handle for a given XA connection. The dbname parameter must be the same as the DB parameter passed in the xa_open string. OCI applications can use this routing instead of the sqlld2 calls to obtain the connection handle. Hence, OCI applications need not link with the sqllib library. The service handle can be converted to the Version 7 OCI logon data area (LDA) by using OCISvcCtxToLda [Version 8 OCI]. Client applications must remember to convert the Version 7 LDA to a service handle by using OCILdaToSvcCtx after completing the OCI calls.

OCIEnv *xaoEnv(text *dbname)

Returns the OCI environment handle for a given XA connection. The dbname parameter must be the same as the DB parameter passed in the xa_open string.

int xaosterr(OCISvcCtx *SvcCtx,sb4 error)

Converts an Oracle Database error code to an XA error code (only applicable to dynamic registration). The first parameter is the service handle used to run the work in the database. The second parameter is the error code that was returned from Oracle Database. Use this function to determine if the error returned from an OCI statement was caused because the xa_start failed. The function returns XA_OK if the error was not generated by the XA module or a valid XA error if the error was generated by the XA module.


Developing and Installing XA Applications

This section explains how to develop and install Oracle XA applications:

DBA or System Administrator Responsibilities

The responsibilities of the DBA or system administrator are as follows:

  1. Define the open string, with help from the application developer. For details, see "Defining the xa_open String".

  2. Ensure that the static data dictionary view DBA_PENDING_TRANSACTIONS exists and grant the SELECT privilege to the view for all Oracle users specified in the xa_open string.

    Grant FORCE TRANSACTION privilege to the Oracle user who might commit or roll back pending (in-doubt) transactions that he or she created, using the command COMMIT FORCE local_tran_id or ROLLBACK FORCE local_tran_id.

    Grant FORCE ANY TRANSACTION privilege to the Oracle user who might commit or roll back XA transactions created by other users. For example, if user A might commit or roll back a transaction that was created by user B, user A must have FORCE ANY TRANSACTION privilege.

    In Oracle Database version 7 client applications, all Oracle Database accounts used by Oracle XA library must have the SELECT privilege on the dynamic performance view V$XATRANS$. This view must have been created during the XA library installation. If necessary, you can manually create the view by running the SQL script xaview.sql as Oracle Database user SYS.


    See Also:

    Your Oracle Database platform-specific documentation for the location of the catxpend.sql script

  3. Using the open string information, install the RM into the TPM configuration. Follow the TPM vendor instructions.

    The DBA or system administrator must be aware that a TPM system starts the process that connects to Oracle Database. See your TPM documentation to determine what environment exists for the process and what user ID it must have. Ensure that correct values are set for $ORACLE_HOME and $ORACLE_SID in this environment.

  4. Grant the user ID write permission to the directory in which the system is to write the XA trace file.


    See Also:

    "Defining the xa_open String" for information about how to specify an Oracle System Identifier (SID) or a trace directory that is different from the defaults

  5. Start the relevant database instances to bring Oracle XA applications on-line. Perform this task before starting any TPM servers.

Application Developer Responsibilities

The responsibilities of the application developer are as follows:

  1. Define the open string with help from the DBA or system administrator, as explained in "Defining the xa_open String".

  2. Develop the applications.

    Observe special restrictions on transaction-oriented SQL statements for precompilers.

  3. Link the application according to TPM vendor instructions.

Defining the xa_open String

The open string is used by the transaction monitor to open the database. The maximum number of characters in an open string is 256.

Topics:

Syntax of the xa_open String

You can define an open string with the syntax shown in Example 15-1.

Example 15-1 xa_open String

ORACLE_XA{+required_fields...} [+optional_fields...]

These strings shows sample parameter settings:

ORACLE_XA+DB=MANAGERS+SqlNet=SID1+ACC=P/username/password
  +SesTM=10+LogDir=/usr/local/xalog
ORACLE_XA+DB=PAYROLL+SqlNet=SID2+ACC=P/username/password
  +SesTM=10+LogDir=/usr/local/xalog
ORACLE_XA+SqlNet=SID3+ACC=P/username/password
  +SesTM=10+LogDir=/usr/local/xalog

These topics describe valid parameters for the required_fields and optional_fields placeholders:




Note:

  • You can enter the required fields and optional fields in any order when constructing the open string.

  • All field names are case insensitive. Whether their values are case-sensitive depends on the platform.

  • There is no way to use the plus character (+) as part of the actual information string.


Required Fields for the xa_open String

The required_fields placeholder in Example 15-1 refers to any of the name-value pairs described in Table 15-4.

Table 15-4 Required Fields of xa_open string

Syntax ElementDescription

Acc=P//

Specifies that no explicit user or password information is provided and that the operating system authentication form is used. For more information see Oracle Database Administrator's Guide.

Acc=P/user/password

Specifies the user name and password for a valid Oracle Database account. As described in "DBA or System Administrator Responsibilities", ensure that HR has the SELECT privilege on the DBA_PENDING_TRANSACTIONS table.

SesTm=session_time_limit

Specifies the maximum number of seconds allowed in a transaction between one service and the next, or between a service and the commit or rollback of the transaction, before the system terminates the transaction. For example, SesTM=15 indicates that the session idle time limit is 15 seconds.

For example, if the TPM uses remote subprogram calls between the client and the servers, then SesTM applies to the time between the completion of one RPC and the initiation of the next RPC, or the tx_commit, or the tx_rollback.

The value of 0 indicates no limit. Entering a value of 0 is strongly discouraged. It might tie up resources for a long time if something goes wrong. Also, if a child process has SesTM=0, then the SesTM setting is not effective after the parent process is terminated.


Optional Fields for the xa_open String

The optional_fields placeholder in Example 15-1 refers to any of the name-value pairs described in Table 15-5.

Table 15-5 Optional Fields in the xa_open String

Syntax ElementDescription

NoLocal= true | false

Specifies whether local transactions are allowed. The default value is false. If the application must disallow local transactions, then set the value to true.

DB=db_name

Specifies the name used by Oracle Database precompilers to identify the database. For example, DB=payroll specifies that the database name is payroll and that the application server program uses that name in AT clauses.

Application programs that use only the default database for the Oracle Database precompiler (that is, they do not use the AT clause in their SQL statements) must omit the DB=db_name clause in the open string. Applications that use explicitly named databases must indicate that database name in their DB=db_name field. Oracle Database Version 7 OCI programs must call the sqlld2 function to obtain the correct context for logon data area (Lda_Def), which is the equivalent of an OCI service context. Version 8 and higher OCI programs must call the xaoSvcCtx function to get the OCISvcCtx service context.

The db_name is not the SID and is not used to locate the database to be opened. Rather, it correlates the database opened by this open string with the name used in the application program to run SQL statements. The SID is set from either the environment variable ORACLE_SID of the TPM application server or the SID given in the Oracle Net clause in the open string. The Oracle Net clause is described later in this section.Some TPM vendors provide a way to name a group of servers that use the same open string. You might find it convenient to choose the same name both for that purpose and for db_name.

LogDir=log_dir

Specifies the path name on the local system where the Oracle XA library error and tracing information is to be logged. The default is $ORACLE_HOME/rdbms/log if ORACLE_HOME is set; otherwise, it specifies the current directory. For example, LogDir=/xa_trace indicates that the logging information is located under the /xa_trace directory. Ensure that the directory exists and the application server can write to it.

Objects= true | false

Specifies whether the application is initialized in object mode. The default value is false. If the application must use certain API calls that require object mode, such as OCIRawAssignBytes, then set the value to true.

MaxCur=maximum_#_of_open_cursors

Specifies the number of cursors to be allocated when the database is opened. It serves the same purpose as the precompiler option maxopencursors. For example, MaxCur=5 indicates that the precompiler tries to keep five open cursors cached. This parameter overrides the precompiler option maxopencursors that you might have specified in your source code or at compile time.

SqlNet=db_link

Specifies the Oracle Net database link to use to log on to the system. This string must be an entry in tnsnames.ora. For example, the string SqlNet=inst1_disp might connect to a shared server at instance 1 if so defined in tnsnames.ora.

You can use the SqlNet parameter to specify the ORACLE_SID in cases where you cannot control the server environment variable. You must also use it when the server must access multiple Oracle Database instances. To use the Oracle Net string without actually accessing a remote database, use the Pipe driver. For example, specify SqlNet=localsid1, where localsid1 is an alias defined in the tnsnames.ora file.

Loose_Coupling=true | false

Specifies whether locks are shared. Oracle Database transaction branches within the same global transaction can be coupled tightly or loosely. If branches are loosely coupled, then they do not share locks. Set the value to true for loosely coupled branches. If branches are tightly coupled, then they share locks. Set the value to false for tightly coupled branches. The default value is false.

SesWt=session_wait_limit

Specifies the number of seconds Oracle Database waits for a transaction branch that is being used by another session before XA_RETRY is returned. The default value is 60 seconds.

Threads=true | false

Specifies whether the application is multithreaded. The default value is false. If the application is multithreaded, then the setting is true.


Using Oracle XA with Precompilers

When used in an Oracle XA application, cursors are valid only for the duration of the transaction. Explicit cursors must be opened after the transaction begins, and closed before the commit or rollback.

You have these options when interfacing with precompilers:

The examples in this topic use the precompiler Pro*C/C++.

Using Precompilers with the Default Database

To interface to a precompiler with the default database, ensure that the DB=db_name field used in the open string is not present. The absence of this field indicates the default connection. Only one default connection is allowed for each process.

This is an example of an open string identifying a default Pro*C/C++ connection:

ORACLE_XA+SqlNet=maildb+ACC=P/username/password
  +SesTM=10+LogDir=/usr/local/logs

The DB=db_name is absent, indicating an empty database ID string.

The syntax of a SQL statement is:

EXEC SQL UPDATE Emp_tab SET Sal = Sal*1.5;

Using Precompilers with a Named Database

To interface to a precompiler with a named database, include the DB=db_name field in the open string. Any database you refer to must reference the same db_name you specified in the corresponding open string.

An application might include the default database and one or more named databases. For example, suppose you want to update an employee's salary in one database, his department number (DEPTNO) in another, and his manager in a third database. Configure the open strings in the transaction manager as shown in Example 15-2.

Example 15-2 Sample Open String Configuration

ORACLE_XA+DB=MANAGERS+SqlNet=SID1+ACC=P/username/password
  +SesTM=10+LogDir=/usr/local/xalog
ORACLE_XA+DB=PAYROLL+SqlNet=SID2+ACC=P/username/password
  +SesTM=10+LogDir=/usr/local/xalog
ORACLE_XA+SqlNet=SID3+ACC=P/username/password
  +SesTM=10+LogDir=/usr/local/xalog

There is no DB=db_name field in the last open string in Example 15-2.

In the application server program, enter declarations such as:

EXEC SQL DECLARE PAYROLL DATABASE;
EXEC SQL DECLARE MANAGERS DATABASE;

Again, the default connection (corresponding to the third open string that does not contain the DB field) needs no declaration.

When doing the update, enter statements similar to these:

EXEC SQL AT PAYROLL UPDATE Emp_Tab SET Sal=4500 WHERE Empno=7788;
EXEC SQL AT MANAGERS UPDATE Emp_Tab SET Mgr=7566 WHERE Empno=7788;
EXEC SQL UPDATE Emp_Tab SET Deptno=30 WHERE Empno=7788;

There is no AT clause in the last statement because it is referring to the default database.

In Oracle Database precompilers release 1.5.3 or later, you can use a character host variable in the AT clause, as this example shows:

EXEC SQL BEGIN DECLARE SECTION;
  DB_NAME1 CHARACTER(10);
  DB_NAME2 CHARACTER(10);
EXEC SQL END DECLARE SECTION;
    ...
SET DB_NAME1 = 'PAYROLL'
SET DB_NAME2 = 'MANAGERS'
    ...
EXEC SQL AT :DB_NAME1 UPDATE...
EXEC SQL AT :DB_NAME2 UPDATE...

Caution:

Do not have XA applications create connections other than those created through xa_open. Work performed on non-XA connections is outside the global transaction and must be committed separately.

Using Oracle XA with OCI

Oracle Call Interface applications that use the Oracle XA library must not call OCISessionBegin to log on to the resource manager. Rather, the logon must be done through the TPM. The applications can run the function xaoSvcCtx to obtain the service context structure when they must access the resource manager.

In applications that must pass the environment handle to OCI functions, you can also call xaoEnv to find that handle.

Because an application server can have multiple concurrent open Oracle Database resource managers, it must call the function xaoSvcCtx with the correct arguments to obtain the correct service context.

Managing Transaction Control with Oracle XA

When you use the XA library, transactions are not controlled by the SQL statements that commit or roll back transactions. Rather, they are controlled by an API accepted by the TM that starts and stops transactions. You call the API that is provided by the transaction manager, including the TX interface listed in Table 15-6, but not the XA Library Subprograms listed in Table 15-2.

The TMs typically control the transactions through the XA interface. This interface includes the functions described in Table 15-2.

Table 15-6 TX Interface Functions

TX FunctionDescription

tx_open

Logs into the resource manager(s)

tx_close

Logs out of the resource manager(s)

tx_begin

Starts a transaction

tx_commit

Commits a transaction

tx_rollback

Rolls back the transaction


Most TPM applications use a client/server architecture in which an application client requests services and an application server provides them. The examples shown in "Examples of Precompiler Applications" use such a client/server model. A service is a logical unit of work that, for Oracle Database as the resource manager, comprises a set of SQL statements that perform a related unit of work.

For example, when a service named "credit" receives an account number and the amount to be credited, it runs SQL statements to update information in certain tables in the database. In addition, a service might request other services. For example, a "transfer fund" service might request services from a "credit" and "debit" service.

Typically, application clients request services from the application servers to perform tasks within a transaction. For some TPM systems, however, the application client itself can offer its own local services. As shown in "Examples of Precompiler Applications", you can encode transaction control statements within either the client or the server.

To have multiple processes participating in the same transaction, the TPM provides a communication API that enables transaction information to flow between the participating processes. Examples of communications APIs include RPC, pseudo-RPC functions, and send/receive functions.

Because the leading vendors support different communication functions, these examples use the communication pseudo-function tpm_service to generalize the communications API.

X/Open includes several alternative methods for providing communication functions in their preliminary specification. At least one of these alternatives is supported by each of the leading TPM vendors.

Examples of Precompiler Applications

These examples illustrate precompiler applications. Assume that the application server has logged onto the RMs system, in a TPM-specific manner. Example 15-3 shows a transaction started by an application server.

Example 15-3 Transaction Started by an Application Server

/***** Client: *****/
tpm_service("ServiceName");              /*Request Service*/

/***** Server: *****/
ServiceName()
{
  <get service specific data>
  tx_begin();                             /* Begin transaction boundary */
  EXEC SQL UPDATE ...;

  /* This application server temporarily becomes */
  /* a client and requests another service. */

  tpm_service("AnotherService");
  tx_commit();                             /* Commit the transaction */
  <return service status back to the client>
}

Example 15-4 shows a transaction started by an application client.

Example 15-4 Transaction Started by an Application Client

/***** Client: *****/
tx_begin();                            /* Begin transaction boundary */
tpm_service("Service1");
tpm_service("Service2");
tx_commit();                           /* Commit the transaction */

/***** Server: *****/
Service1()
{
  <get service specific data>
  EXEC SQL UPDATE ...;
  <return service status back to the client>
}
Service2()
{
  <get service specific data>
  EXEC SQL UPDATE ...;
  ...
  <return service status back to client>
}

Migrating Precompiler or OCI Applications to TPM Applications

To migrate existing precompiler or OCI applications to a TPM application that uses the Oracle XA library, you must:

  1. Reorganize the application into a framework of "services" so that application clients request services from application servers. Some TPMs require the application to use the tx_open and tx_close functions, whereas other TPMs do the logon and logoff implicitly.

    If you do not specify the SqlNet parameter in your open string, then the application uses the default Oracle Net driver. Thus, ensure that the application server is brought up with the ORACLE_HOME and ORACLE_SID environment variables properly defined. This is accomplished in a TPM-specific fashion. See your TPM vendor documentation for instructions on how to accomplish this.

  2. Ensure that the application replaces the regular connect and disconnect statements. For example, replace the connect statements EXEC SQL CONNECT (for precompilers) or OCISessionBegin, OCIServerAttach, and OCIEnvCreate (for OCI) with tx_open. Replace the disconnect statements EXEC SQL COMMIT/ROLLBACK WORK RELEASE (for precompilers) or OCISessionEnd/OCIServerDetach (for OCI) with tx_close.

  3. Ensure that the application replaces the regular commit or rollback statements for any global transactions and begins the transaction explicitly.

    For example, replace the COMMIT/ROLLBACK statements EXEC SQL COMMIT/ROLLBACK WORK (for precompilers), or OCITransCommit/OCITransRollback (for OCI) with tx_commit/tx_rollback and start the transaction by calling tx_begin.


    Note:

    The preceding is only true for global rather than local transactions. Commit or roll back local transactions with the Oracle API.

  4. Ensure that the application resets the fetch state before ending a transaction. In general, use release_cursor=no. Use release_cursor=yes only when you are certain that a statement will run only once.

Table 15-7 lists the TPM functions that replace regular Oracle Database statements when migrating precompiler or OCI applications to TPM applications.

Table 15-7 TPM Replacement Statements

Regular Oracle Database StatementsTPM Functions

CONNECTuser/password

tx_open (possibly implicit)

implicit start of transaction

tx_begin

SQL

Service that runs the SQL

COMMIT

tx_commit

ROLLBACK

tx_rollback

disconnect

tx_close (possibly implicit)


Managing Oracle XA Library Thread Safety

If you use a transaction monitor that supports threads, then the Oracle XA library enables you to write applications that are thread-safe. Nevertheless, keep certain issues in mind.

A thread of control (or thread) refers to the set of connections to resource managers. In an nonthreaded system, each process is considered a thread of control because each process has its own set of connections to RMs and maintains its own independent resource manager table. In a threaded system, each thread has an autonomous set of connections to RMs and each thread maintains a private RM table. This private table must be allocated for each thread and de-allocated when the thread terminates, even if the termination is abnormal.


Note:

In Oracle Database, each thread that accesses the database must have its own connection.

Topics:

Specifying Threading in the Open String

The xa_open string provides the clause Threads=. You must specify this clause as true to enable the use of threads by the TM. The default is false. In most cases, the TM creates the threads; the application does not know when a thread is created. Therefore, it is advisable to allocate a service context on the stack within each service that is written for a TM application. Before doing any Oracle Database-related calls in that service, you must call the xaoSvcCtx function to retrieve the initialized OCI service context. You can then use this context for OCI calls within the service.

Restrictions on Threading in Oracle XA

These restrictions apply when using threads:

  • Any Pro* or OCI code that runs as part of the application server process on the transaction monitor cannot be threaded unless the transaction monitor is explicitly told when each application thread is started. This is typically accomplished by using a special C compiler provided by the TM vendor.

  • The Pro* statements EXEC SQL ALLOCATE and EXEC SQL USE are not supported. Therefore, when threading is enabled, you cannot use embedded SQL statements across non-XA connections.

  • If one thread in a process connects to Oracle Database through XA, then all other threads in the process that connect to Oracle Database must also connect through XA. You cannot connect through EXEC SQL CONNECT in one thread and through xa_open in another thread.

Using the DBMS_XA Package

PL/SQL applications can use the Oracle XA library with the DBMS_XA package. For information about this package, see Oracle Database PL/SQL Packages and Types Reference.

In Example 15-5, one PL/SQL session starts a transaction but does not commit it, a second session resumes the transaction, and a third session commits the transaction. All three sessions are connected to the HR schema.

Example 15-5 Using the DBMS_XA Package

REM Session 1 starts a transaction and does some work.
DECLARE
  rc  PLS_INTEGER;
  oer PLS_INTEGER;
  xae EXCEPTION;
BEGIN
  rc  := DBMS_XA.XA_START(DBMS_XA_XID(123), DBMS_XA.TMNOFLAGS);

  IF rc!=DBMS_XA.XA_OK THEN
    oer := DBMS_XA.XA_GETLASTOER();
    DBMS_OUTPUT.PUT_LINE('ORA-' || oer || ' occurred, XA_START failed');
    RAISE xae;
  ELSE DBMS_OUTPUT.PUT_LINE('XA_START(new xid=123)     OK');
  END IF;

  UPDATE employees SET salary=salary*1.1 WHERE employee_id = 100;
  rc  := DBMS_XA.XA_END(DBMS_XA_XID(123), DBMS_XA.TMSUSPEND);

  IF rc!=DBMS_XA.XA_OK THEN
    oer := DBMS_XA.XA_GETLASTOER();
    DBMS_OUTPUT.PUT_LINE('ORA-' || oer || ' occurred, XA_END failed');
    RAISE xae;
  ELSE DBMS_OUTPUT.PUT_LINE('XA_END(suspend xid=123)   OK');
  END IF;

  EXCEPTION
    WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE
       ('XA error('||rc||') occurred, rolling back the transaction ...');
      rc := DBMS_XA.XA_END(DBMS_XA_XID(123), DBMS_XA.TMSUCCESS);
      rc := DBMS_XA.XA_ROLLBACK(DBMS_XA_XID(123));

      IF rc != DBMS_XA.XA_OK THEN
        oer := DBMS_XA.XA_GETLASTOER();
        DBMS_OUTPUT.PUT_LINE('XA-'||rc||', ORA-' || oer ||
         ' XA_ROLLBACK does not return XA_OK');
        raise_application_error(-20001, 'ORA-'||oer||
         ' error in rolling back a failed transaction');
      END IF;

      raise_application_error(-20002, 'ORA-'||oer||
       ' error in transaction processing, transaction rolled back');
END;
/
SHOW ERRORS
DISCONNECT

REM Session 2 resumes the transaction and does some work.
DECLARE
  rc  PLS_INTEGER;
  oer PLS_INTEGER;
  s   NUMBER;
  xae EXCEPTION;
BEGIN
  rc  := DBMS_XA.XA_START(DBMS_XA_XID(123), DBMS_XA.TMRESUME);

  IF rc!=DBMS_XA.XA_OK THEN
    oer := DBMS_XA.XA_GETLASTOER();
    DBMS_OUTPUT.PUT_LINE('ORA-' || oer || ' occurred, xa_start failed');
    RAISE xae;
  ELSE DBMS_OUTPUT.PUT_LINE('XA_START(resume xid=123)  OK');
  END IF;

  SELECT salary INTO s FROM employees WHERE employee_id = 100;
  DBMS_OUTPUT.PUT_LINE('employee_id = 100, salary = ' || s);
  rc  := DBMS_XA.XA_END(DBMS_XA_XID(123), DBMS_XA.TMSUCCESS);

  IF rc!=DBMS_XA.XA_OK THEN
    oer := DBMS_XA.XA_GETLASTOER();
    DBMS_OUTPUT.PUT_LINE('ORA-' || oer || ' occurred, XA_END failed');
    RAISE xae;
  ELSE DBMS_OUTPUT.PUT_LINE('XA_END(detach xid=123)    OK');
  END IF;

  EXCEPTION
    WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE
       ('XA error('||rc||') occurred, rolling back the transaction ...');
      rc := DBMS_XA.XA_END(DBMS_XA_XID(123), DBMS_XA.TMSUCCESS);
      rc := DBMS_XA.XA_ROLLBACK(DBMS_XA_XID(123));

      IF rc != DBMS_XA.XA_OK THEN
        oer := DBMS_XA.XA_GETLASTOER();
        DBMS_OUTPUT.PUT_LINE('XA-'||rc||', ORA-' || oer ||
         ' XA_ROLLBACK does not return XA_OK');
        raise_application_error(-20001, 'ORA-'||oer||
         ' error in rolling back a failed transaction');
      END IF;

      raise_application_error(-20002, 'ORA-'||oer||
       ' error in transaction processing, transaction rolled back');
END;
/
SHOW ERRORS
DISCONNECT

REM Session 3 commits the transaction.
DECLARE
  rc  PLS_INTEGER;
  oer PLS_INTEGER;
  xae EXCEPTION;
BEGIN
  rc  := DBMS_XA.XA_COMMIT(DBMS_XA_XID(123), TRUE);

  IF rc!=DBMS_XA.XA_OK THEN
    oer := DBMS_XA.XA_GETLASTOER();
    DBMS_OUTPUT.PUT_LINE('ORA-' || oer || ' occurred, XA_COMMIT failed');
    RAISE xae;
  ELSE DBMS_OUTPUT.PUT_LINE('XA_COMMIT(commit xid=123)  OK');
  END IF;

  EXCEPTION
    WHEN xae THEN
      DBMS_OUTPUT.PUT_LINE
       ('XA error('||rc||') occurred, rolling back the transaction ...');
      rc := DBMS_XA.XA_ROLLBACK(DBMS_XA_XID(123));

      IF rc != DBMS_XA.XA_OK THEN
        oer := DBMS_XA.XA_GETLASTOER();
        DBMS_OUTPUT.PUT_LINE('XA-'||rc||', ORA-' || oer ||
         ' XA_ROLLBACK does not return XA_OK');
        raise_application_error(-20001, 'ORA-'||oer||
         ' error in rolling back a failed transaction');
      END IF;

      raise_application_error(-20002, 'ORA-'||oer||
       ' error in transaction processing, transaction rolled back');
END;
/
SHOW ERRORS
DISCONNECT
QUIT

Troubleshooting XA Applications

Topics:

Accessing Oracle XA Trace Files

The Oracle XA library logs any error and tracing information to its trace file. This information is useful in supplementing the XA error codes. For example, it can indicate whether an xa_open failure is caused by an incorrect open string, failure to find the Oracle Database instance, or a logon authorization failure.

The name of the trace file is xa_db_namedate.trc, where db_name is the database name specified in the open string field DB=db_name, and date is the date when the information is logged to the trace file. If you do not specify DB=db_name in the open string, then it automatically defaults to NULL.

For example, xa_NULL06022005.trc indicates a trace file that was created on June 2, 2005. Its DB field was not specified in the open string when the resource manager was opened. The filename xa_Finance12152004.trc indicates a trace file was created on December 15, 2004. Its DB field was specified as "Finance" in the open string when the resource manager was opened.


Note:

Multiple Oracle XA library resource managers with the same DB field and LogDir field in their open strings log all trace information that occurs on the same day to the same trace file.

Suppose that a trace file contains these contents:

1032.12345.2:  ORA-01017:  invalid username/password;  logon denied
1032.12345.2:  xaolgn:  XAER_INVAL;  logon denied

Table 15-8 explains the meaning of each element.

Table 15-8 Sample Trace File Contents

StringDescription

1032

The time when the information is logged.

12345

The process ID (PID).

2

Resource manager ID

xaolgn

Name of module

XAER_INVAL

Error returned as specified in the XA standard

ORA-01017

Oracle Database information that was returned


Topics:

xa_open String DbgFl

Normally, the XA trace file is opened only if an error is detected. The xa_open string DbgFl provides a tracing facility to record additional detail about the XA library. By default, its value is zero. You can set it to any combination of these values:

  • 0x1, which enables you to trace the entry and exit to each subprogram in the XA interface. This value can be useful in seeing exactly which XA calls the TP Monitor is making and which transaction identifier it is generating.

  • 0x2, which enables you to trace the entry to and exit from other nonpublic XA library programs. This is generally of use only to Oracle Database developers.

  • 0x4, which enables you to trace various other "interesting" calls made by the XA library, such as specific calls to the OCI. This is generally of use only to Oracle Database developers.


Note:

The flags are independent bits of an ub4, so to obtain printout from two or more flags, you must set a combined value of the flags.

Trace File Locations

The XA application determines a location for the trace file according to this algorithm:

  1. The LogDir directory specified in the open string.

  2. If you do not specify LogDir in the open string, then the Oracle XA application attempts to create the trace file in this directory (if the Oracle home is accessible):

    • %ORACLE_HOME%\rdbms\trace on Windows

    • $ORACLE_HOME/rdbms/log on Linux and UNIX

  3. If the Oracle XA application cannot determine where the Oracle home is located, then the application creates the trace file in the current working directory.

Managing In-Doubt or Pending Oracle XA Transactions

In-doubt or pending transactions are transactions that were prepared but not committed to the database. In general, the TM provided by the TPM system resolves any failure and recovery of in-doubt or pending transactions. The DBA might have to override an in-doubt transaction if these situations occur:

  • It is locking data that is required by other transactions.

  • It is not resolved in a reasonable amount of time.

See the TPM documentation for more information about overriding in-doubt transactions in such circumstances and about how to decide whether to commit or roll back the in-doubt transaction.

Using SYS Account Tables to Monitor Oracle XA Transactions

These views under the Oracle Database SYS account contain transactions generated by regular Oracle Database applications and Oracle XA applications:

  • DBA_PENDING_TRANSACTIONS

  • V$GLOBAL_TRANSACTION

  • DBA_2PC_PENDING

  • DBA_2PC_NEIGHBORS

For transactions generated by Oracle XA applications, this column information applies specifically to the DBA_2PC_NEIGHBORS table:

  • The DBID column is always xa_orcl

  • The DBUSER_OWNER column is always db_namexa.oracle.com

Remember that the db_name is always specified as DB=db_name in the open string. If you do not specify this field in the open string, then the value of this column is NULLxa.oracle.com for transactions generated by Oracle XA applications.

For example, this SQL statement provide more information about in-doubt transactions generated by Oracle XA applications:

SELECT * 
FROM DBA_2PC_PENDING p, DBA_2PC_NEIGHBORS n
WHERE p.LOCAL_TRAN_ID = n.LOCAL_TRAN_ID
AND n.DBID = 'xa_orcl';

Alternatively, if you know the format ID used by the transaction processing monitor, then you can use DBA_PENDING_TRANSACTIONS or V$GLOBAL_TRANSACTION. Whereas DBA_PENDING_TRANSACTIONS gives a list of prepared transactions, V$GLOBAL_TRANSACTION provides a list of all active global transactions.

Oracle XA Issues and Restrictions

Topics:

Using Database Links in Oracle XA Applications

Oracle XA applications can access other Oracle Database instances through database links with these restrictions:

  • They must use the shared server configuration.

    The transaction processing monitors (TPMs) use shared servers to open the connection to an Oracle Database A. Then the operating system network connection required for the database link is opened by the dispatcher instead of a dedicated server process. This allows different services or threads to operate on the transaction.

    If this restriction is not satisfied, then when you use database links within an XA transaction, it creates an operating system network connection between the dedicated server process and the other Oracle Database B. Because this network connection cannot be moved from one dedicated server process to another, you cannot detach from this dedicated server process of database A. Then when you access the database B through a database link, you receive an ORA-24777 error.

  • The other database being accessed must be another Oracle Database.

If these restrictions are satisfied, Oracle Database allows such links and propagates the transaction protocol (prepare, rollback, and commit) to the other Oracle Database instances.

If using the shared server configuration is not possible, then access the remote database through the Pro*C/C++ application by using EXEC SQL AT syntax.

The init.ora parameter OPEN_LINKS_PER_INSTANCE specifies the number of open database link connections that can be migrated. These dblink connections are used by XA transactions so that the connections are cached after a transaction is committed. Another transaction can use the database link connection if the user who created the connection also created the transaction. This parameter is different from the init.ora parameter OPEN_LINKS, which specifies the maximum number of concurrent open connections (including database links) to remote databases in one session. The OPEN_LINKS parameter does not apply to XA applications.

Managing Transaction Branches in Oracle XA Applications

Oracle Database transaction branches within the same global transaction can be coupled tightly or loosely. If the transaction branches are tightly coupled, then they share locks. Consequently, pre-COMMIT updates in one transaction branch are visible in other branches that belong to the same global transaction. In loosely coupled transaction branches, the branches do not share locks and do not see updates in other branches.

In a tightly coupled branch, Oracle Database obtains the DX lock before running any statement. Because the system does not obtain a lock before running the statement, loosely coupled transaction branches result in greater concurrency. The disadvantage is that all transaction branches must go through the two phases of commit, that is, the system cannot use XA one-phase optimization.

Table 15-9 summarizes the trade-offs between tightly coupled branches and loosely coupled branches.

Table 15-9 Tightly and Loosely Coupled Transaction Branches

AttributeTightly Coupled BranchesLoosely Coupled Branches

Two Phase Commit

Read-only optimization

[prepare for all branches, commit for last branch]

Two phases

[prepare and commit for all branches]

Serialization

Database call

None


Using Oracle XA with Oracle Real Application Clusters (Oracle RAC)

As of Oracle Database 11g Release 1, an XA transaction can span Oracle RAC instances, allowing any application that uses XA to take full advantage of the Oracle RAC environment, enhancing the availability and scalability of the application.


Note:

External procedure callouts combined with distributed transactions is not supported.

Topics:

GLOBAL_TXN_PROCESSES Initialization Parameter

The initialization parameter GLOBAL_TXN_PROCESSES specifies the initial number of GTXn background processes for each Oracle RAC instance. Its default value is 1.

Leave this parameter at its default value clusterwide if distributed transactions might span multiple Oracle RAC instances. This allows the units of work performed across these Oracle RAC instances to share resources and act as a single transaction (that is, the units of work are tightly coupled). It also allows 2PC requests to be sent to any node in the cluster.


See Also:

Oracle Database Reference for more information about GLOBAL_TXN_PROCESSES

Managing Transaction Branches on Oracle RAC


Note:

This topic applies if either of the following is true:
  • The initialization parameter GLOBAL_TXN_PROCESSES is not at its default value in the initialization file of every Oracle RAC instance.

  • The Oracle XA application resumes or joins previously detached branches of a transaction.


Oracle Database permits different instances to operate on different transaction branches in Oracle RAC. For example, Node 1 can operate on branch A while Node 2 operates on branch B. Before Oracle Database 11g Release 1, if transaction branches were on different instances, then they were loosely coupled and did not share locks. In this case, Oracle Database treated different units of work in different application threads as separate entities that did not share resources.

A different case is when multiple instances operate on a single transaction branch. For example, assume that a single transaction lands on Node 1 and Node 2 as follows:

Node 1

  1. xa_start

  2. SQL operations

  3. xa_end (SUSPEND)

Node 2

  1. xa_start (RESUME)

  2. xa_prepare

  3. xa_commit

  4. xa_end

In the immediately preceding sequence, Oracle Database returns an error because Node 2 must not resume a branch that is physically located on a different node (Node 1).

Before Oracle Database 11g Release 1, the way to achieve tight coupling in Oracle RAC was to use Distributed Transaction Processing (DTP) services, that is, services whose cardinality (one) ensured that all tightly-coupled branches landed on the same instance—regardless of whether load balancing was enabled. Middle-tier components addressed Oracle Database through a common logical database service name that mapped to a single Oracle RAC instance at any point in time. An intermediate name resolver for the database service hid the physical characteristics of the database instance. DTP services enabled all participants of a tightly-coupled global transaction to create branches on one instance.

As of Oracle Database 11g Release 1, the DTP service is no longer required to support XA transactions with tightly coupled branches. By default, tightly coupled branches that land on different Oracle RAC instances remain tightly coupled; that is, they share locks and resources across Oracle RAC instances.

For example, when you use a DTP service, this sequence of actions occurs on the same instance:

  1. xa_start

  2. SQL operations

  3. xa_end (SUSPEND)

  4. xa_start (RESUME)

  5. SQL operations

  6. xa_prepare

  7. xa_commit or xa_rollback

Moreover, multiple tightly-coupled branches land on the same instance if each addresses the Oracle RM with the same DTP service.

To leverage all instances in the cluster, create multiple DTP services, with one or more on each node that hosts distributed transactions. All branches of a global distributed transaction exist on the same instance. Thus, you can leverage all instances and nodes of an Oracle RAC cluster to balance the load of many distributed XA transactions, thereby maximizing application throughput.


See Also:

Oracle Real Application Clusters Administration and Deployment Guide to learn how to manage distributed transactions in a Real Application Clusters configuration

Managing Instance Recovery in Oracle RAC with DTP Services

Before Oracle Database 10g Release 2, TM was responsible for detecting failure and triggering failover and failback in Oracle RAC. To ensure that information about in-doubt transactions was propagated to DBA_2PC_PENDING, TM had to call xa_recover before resolving the in-doubt transactions. If an instance failed, then the XA client library could not fail over to another instance until it had run theSYS.DBMS_XA.DIST_TXN_SYNC procedure to ensure that the undo segments of the failed instance were recovered. As of Oracle Database 10g Release 2, there is no such requirement to call xa_recover in cases where the TM has enough information about in-flight transactions.


Note:

As of Oracle Database 9g Release 2, xa_recover is required to wait for distributed data manipulation language (DML) statements to complete on remote sites.

Using DTP services in Oracle RAC has these benefits:

  • Automates instance failure detection.

  • Automates instance failover and failback. When an instance fails, the DTP service hosted on this instance fails over to another instance. The fail%over forces clients to reconnect; nevertheless, the logical names for the service remain the same. Failover is automatic and does not require an administrator intervention. The administrator can induce failback by a service relocate statement, but all failback-related recovery is automatically handled within the database server.

  • Enables Oracle Database rather than the client to drive instance recovery. The database does not require middle-tier TM involvement to determine the state of transactions prepared by other instances.


See Also:


Global Uniqueness of XIDs in Oracle RAC

Before Oracle Database 11g Release 1, Oracle RAC database cannot determine whether a given XID is unique for XA transactions throughout the cluster.

For example, suppose that there is an XID Fmt(x).Tx(1).Br(1) on Oracle RAC instance 1 and another XID Fmt(x).Tx(1).Br(1) on Oracle RAC instance 2. Each of these can start a branch and run SQL even though the XID is not unique across Oracle RAC instances.

As of Oracle Database 11g Release 1, Oracle RAC database detects the duplicate XIDs across Oracle RAC instances and prevents a branch with a duplicate XID from starting.


See Also:

Oracle Real Application Clusters Administration and Deployment Guide for information about services and distributed transaction processing in Oracle RAC

Tight and Loose Coupling

Oracle Database transaction branches within the same global transaction can be coupled either tightly or loosely (for details, see "Managing Transaction Branches in Oracle XA Applications"). Ordinarily, coupling type is determined by the value of the Loose_Coupling field of the xa_open string (see Table 15-5). However, if branches are landed on different Oracle RAC instances when running Oracle RAC, they are loosely coupled even if Loose_Coupling=false.


See Also:

Oracle Real Application Clusters Administration and Deployment Guide for information about services and distributed transaction processing in Oracle RAC

SQL-Based Oracle XA Restrictions

This section describes restrictions concerning these SQL operations:

Rollbacks and Commits

Because the transaction manager is responsible for coordinating and monitoring the progress of the global transaction, the application must not contain any Oracle Database-specific statement that independently rolls back or commits a global transaction. However, you can use rollbacks and commits in a local transaction.

Do not use EXEC SQL ROLLBACK WORK for precompiler applications when you are in the middle of a global transaction. Similarly, an OCI application must not run OCITransRollback, or the Version 7 equivalent orol. You can roll back a global transaction by calling tx_rollback.

Similarly, a precompiler application must not have the EXEC SQL COMMIT WORK statement in the middle of a global transaction. An OCI application must not run OCITransCommit or the Version 7 equivalent ocom. For example, use tx_commit or tx_rollback to end a global transaction.

DDL Statements

Because a data definition language (DDL) statement, such as CREATE TABLE, implies an implicit commit, the Oracle XA application cannot run any DDL statements.

Session State

Oracle Database does not guarantee that session state is valid between TPM services. For example, if a TPM service updates a session variable (such as a global package variable), then another TPM service that runs as part of the same global transaction might not see the change. Use savepoints only within a TPM service. The application must not refer to a savepoint that was created in another TPM service. Similarly, an application must not attempt to fetch from a cursor that was executed in another TPM service.

EXEC SQL

Do not use the EXEC SQL statement to connect or disconnect. That is, do not use EXEC SQL CONNECT, EXEC SQL COMMIT WORK RELEASE or EXEC SQL ROLLBACK WORK RELEASE.

Miscellaneous Restrictions

  • You cannot use both Oracle XA and a gateway in the same session.

  • Oracle Database does not support association migration (a means whereby a transaction manager might resume a suspended branch association in another branch).

  • The optional XA feature asynchronous XA calls is not supported.

  • Set the TRANSACTIONS initialization parameter to the expected number of concurrent global transactions. The initialization parameter OPEN_LINKS_PER_INSTANCE specifies the number of open database link connections that can be migrated. These database link connections are used by XA transactions so that the connections are cached after a transaction is committed.

  • The maximum number of xa_open calls for each thread is 32.

  • When building an XA application based on TP-monitor, ensure that the TP-monitors libraries (that define the symbols ax_reg and ax_unreg) are placed in the link line before Oracle Database's client shared library. If your platform does not support shared libraries or if your linker is not sensitive to ordering of libraries in the link line, use Oracle Database's nonshared client library. These link restrictions are applicable only when using XA's dynamic registration (Oracle XA switch xaoswd).

PK /PK|%AOEBPS/adfns_environments.htm Choosing a Programming Environment

13 Choosing a Programming Environment

To choose a programming environment for a development project, read:

Sometimes the choice of programming environment is obvious, for example:

If no programming language provides all the features you need, you can use multiple programming languages, because:

For more information about multilanguage programming, see Chapter 14, "Developing Applications with Multiple Programming Languages."

Topics:

Overview of Application Architecture

In this topic, application architecture refers to the computing environment in which a database application connects to an Oracle Database.

Topics:


See Also:

Oracle Database Concepts for more information about application architecture

Client/Server Architecture

In a traditional client/server program, your application code runs on a client system; that is, a system other than the database server. Database calls are transmitted from the client system to the database server. Data is transmitted from the client to the server for insert and update operations and returned from the server to the client for query operations. The data is processed on the client system. Client/server programs are typically written by using precompilers, whereas SQL statements are embedded within the code of another language such as C, C++, or COBOL.


See Also:

Oracle Database Concepts for more information about client/server architecture

Server-Side Programming

You can develop application logic that resides entirely inside the database by using triggers that are executed automatically when changes occur in the database or stored subprograms that are invoked explicitly. Off-loading the work from your application lets you reuse code that performs verification and cleanup and control database operations from a variety of clients. For example, by making stored subprograms invocable through a web server, you can construct a web-based user interface that performs the same functions as a client/server application.


See Also:

Oracle Database Concepts for more information about server-side programming

Two-Tier and Three-Tier Architecture

Client/server computing is often referred to as a two-tier model: your application communicates directly with the database server. In the three-tier model, a separate application server processes the requests. The application server might be a basic web server, or might perform advanced functions like caching and load-balancing. Increasing the processing power of this middle tier lets you lessen the resources needed by client systems, resulting in a thin client configuration in which the client system might need only a web browser or other means of sending requests over the TCP/IP or HTTP protocols.


See Also:

Oracle Database Concepts for more information about multitier architecture

Overview of the Program Interface

The program interface is the software layer between a database application and Oracle Database. The program interface:

  • Provides a security barrier, preventing destructive access to the SGA by client user processes

  • Acts as a communication mechanism, formatting information requests, passing data, and trapping and returning errors

  • Converts and translates data, particularly between different types of computers or to external user program data types

The Oracle code acts as a server, performing database tasks on behalf of an application (a client), such as fetching rows from data blocks. The program interface consists of several parts, provided by both Oracle Database software and operating system-specific software.


See Also:

Oracle Database Concepts for more information about the program interface

Topics:

User Interface

The user interface is what your application displays to end users. It depends on the technology behind the application and the needs of the users themselves. Experienced users can enter SQL statements that are passed on to the database. Novice users can be shown a graphical user interface that uses the graphics libraries of the client system (such as Windows or X-Windows). Any of these traditional user interfaces can also be provided in a web browser through HTML and Java.

Stateful and Stateless User Interfaces

In traditional client/server applications, the application can keep a record of user actions and use this information over the course of one or more sessions. For example, past choices can be presented in a menu so that they do not have to be entered again. When the application can save information in this way, the application is considered stateful.

Web or thin-client applications that are stateless are easier to develop. Stateless applications gather all the required information, process it using the database, and then start over with the next user. This is a popular way to process single-screen requests such as customer registration.

There are many ways to add stateful action to web applications that are stateless by default. For example, an entry form on one web page can pass information to subsequent web pages, enabling you to construct a wizard-like interface that remembers user choices through several different steps. You can use cookies to store small items of information about the client system, and retrieve them when the user returns to a web site. You can use servlets to keep a database session open and store variables between requests from the same client.

Overview of PL/SQL

PL/SQL, the Oracle procedural extension of SQL, is a completely portable, high-performance transaction-processing language. PL/SQL lets you manipulate data with SQL statements; control program flow with conditional selection and loops; declare constants and variables; define subprograms; define types, subtypes, and ADTs and declare variables of those types; and trap runtime errors.

Applications written in any of the Oracle Database programmatic interfaces can invoke PL/SQL stored subprograms and send blocks of PL/SQL code to Oracle Database for execution. Third-generation language (3GL) applications can access PL/SQL scalar and composite data types through host variables and implicit data type conversion. A 3GL language is easier than assembler language for a human to understand and includes features such as named variables. Unlike a fourth-generation language (4GL), it is not specific to an application domain.

You can use PL/SQL to develop stored procedures that can be invoked by a web client.


See Also:


Overview of Oracle Database Java Support

This section provides an overview of Oracle Database features that support Java applications. The database includes the core JDK libraries such as java.lang, java.io, and so on. The database supports client-side Java standards such as JDBC and SQLJ, and provides server-side JDBC and SQLJ drivers that enable data-intensive Java code to run within the database.

Topics:

Overview of Oracle JVM

Oracle JVM, the Java Virtual Machine provided with the Oracle Database, is compliant with the J2SE version 1.5.x specification and supports the database session architecture.

Any database session can activate a dedicated JVM. All sessions share the same JVM code and statics; however, private states for any given session are held, and subsequently garbage collected, in an individual session space.

This design provides these benefits:

  • Java applications have the same session isolation and data integrity as SQL operations.

  • You need not run Java in a separate process for data integrity.

  • Oracle JVM is a robust JVM with a small memory footprint.

  • The JVM has the same linear Symmetric Multiprocessing (SMP) scalability as the database and can support thousands of concurrent Java sessions.

Oracle JVM works consistently with every platform supported by Oracle Database. Java applications that you develop with Oracle JVM can easily be ported to any supported platform.

Oracle JVM includes a deployment-time native compiler that enables Java code to be compiled once, stored in executable form, shared among users, and invoked more quickly and efficiently.

Security features of the database are also available with Oracle JVM. Java classes must be loaded in a database schema (by using Oracle JDeveloper, a third-party IDE, SQL*Plus, or the loadjava utility) before they can be called. Java class calls are secured and controlled through database authentication and authorization, Java 2 security, and invoker's rights (IR) or definer's rights (DR).


See Also:

Oracle Database Concepts for additional general information about Oracle JVM

Overview of Oracle JDBC

Java Database Connectivity (JDBC) is an Applications Programming Interface (API) that enables Java to send SQL statements to an object-relational database such as Oracle Database.

Oracle Database includes these extensions to the JDBC 1.22 standard:

  • Support for Oracle data types

  • Performance enhancement by row prefetching

  • Performance enhancement by execution batching

  • Specification of query column types to save round-trips

  • Control of DatabaseMetaData calls

Oracle Database supports all APIs from the JDBC 2.0 standard, including the core APIs, optional packages, and numerous extensions. Some highlights include datasources, JTA, and distributed transactions.

Oracle Database supports these features from the JDBC 3.0 standard:

  • Support for JDK 1.5.

  • Toggling between local and global transactions.

  • Transaction savepoints.

  • Reuse of prepared statements by connection pools.


Note:

JDBC code and SQLJ code interoperate. For more information, see "Comparing Oracle JDBC and Oracle SQLJ".)

Topics:


See Also:

Oracle Database Concepts for additional general information about Java support in Oracle Database

Oracle JDBC Drivers

The JDBC standard defines four types of JDBC drivers:

TypeDescription
1A JDBC-ODBC bridge. Software must be installed on client systems.
2Native methods (calls C or C++) and Java methods. Software must be installed on the client.
3Pure Java. The client uses sockets to call middleware on the server.
4The most pure Java solution. Talks directly to the database by using Java sockets.

JDBC is based on Part 3 of the SQL standard, "Call-Level Interface."

You can use JDBC to do dynamic SQL. In dynamic SQL, the embedded SQL statement to be executed is not known before the application is run and requires input to build the statement.

The drivers that are implemented by Oracle have extensions to the capabilities in the JDBC standard that was defined by Sun Microsystems.

Topics:


See Also:


JDBC Thin Driver

The JDBC thin driver is a Type 4 (100% pure Java) driver that uses Java sockets to connect directly to a database server. It has its own implementation of a Two-Task Common (TTC), a lightweight implementation of TCP/IP from Oracle Net. It is written entirely in Java and is therefore platform-independent.

The thin driver does not require Oracle software on the client side. It does need a TCP/IP listener on the server side. Use this driver in Java applets that are downloaded into a web browser or in applications for which you do not want to install Oracle client software. The thin driver is self-contained, but it opens a Java socket, and thus can only run in a browser that supports sockets.

JDBC OCI Driver

The JDBC OCI driver is a Type  2 JDBC driver. It makes calls to OCI written in C to interact with Oracle Database, thus using native and Java methods.

The OCI driver provides access to more features than the thin driver, such as Transparent Application Fail-Over, advanced security, and advanced LOB manipulation.

The OCI driver provides the highest compatibility between different Oracle Database versions. It also supports all installed Oracle Net adapters, including IPC, named pipes, TCP/IP, and IPX/SPX.

Because it uses native methods (a combination of Java and C) the OCI driver is platform-specific. It requires a client installation of version Oracle8i or later including Oracle Net, OCI libraries, CORE libraries, and all other dependent files. The OCI driver usually runs faster than the thin driver.

The OCI driver is not appropriate for Java applets, because it uses a C library that is platform-specific and cannot be downloaded into a web browser. It is usable in J2EE components running in middle-tier application servers, such as Oracle Application Server. Oracle Application Server provides middleware services and tools that support access between applications and browsers.

JDBC Server-Side Internal Driver

The JDBC server-side internal driver is a Type 2 driver that runs inside the database server, reducing the number of round-trips needed to access large amounts of data. The driver, the Java server VM, the database, the Java native compiler (which speeds execution by as much as 10 times), and the SQL engine all run within the same address space.

This driver provides server-side support for any Java program used in the database. You can also call PL/SQL stored subprograms and triggers.

The server driver fully supports the same features and extensions as the client-side drivers.

Sample JDBC 2.0 Program

This example shows the recommended technique for looking up a data source using JNDI in JDBC 2.0:

// import the JDBC packages 
import java.sql.*; 
import javax.sql.*; 
import oracle.jdbc.pool.*; 
...
   InitialContext ictx = new InitialContext(); 
   DataSource ds = (DataSource)ictx.lookup("jdbc/OracleDS"); 
   Connection conn = ds.getConnection(); 
   Statement stmt = conn.createStatement(); 
   ResultSet rs = stmt.executeQuery("SELECT last_name FROM employees"); 
   while ( rs.next() ) { 
   out.println( rs.getString("ename") + "<br>"); 
   } 
conn.close(); 

Sample Pre-2.0 JDBC Program

This source code registers an Oracle JDBC thin driver, connects to the database, creates a Statement object, runs a query, and processes the result set.

The SELECT statement retrieves and lists the contents of the last_name column of the hr.employees table.

import java.sql.*
import java.math.*
import java.io.*
import java.awt.*

class JdbcTest { 
  public static void main (String args []) throws SQLException { 
    // Load Oracle driver
    DriverManager.registerDriver (new oracle.jdbc.OracleDriver());
     
    // Connect to the local database
    Connection conn = 
      DriverManager.getConnection ("jdbc:oracle:thin:@myhost:1521:orcl", 
                                   "hr", "password");

    // Query the employee names 
    Statement stmt = conn.createStatement (); 
    ResultSet rset = stmt.executeQuery ("SELECT last_name FROM employees");

    // Print the name out 
    while (rset.next ())
      System.out.println (rset.getString (1));
    // Close the result set, statement, and the connection
    rset.close();
    stmt.close();
    conn.close();
  } 
} 

One Oracle Database extension to the JDBC drivers is a form of the getConnection() method that uses a Properties object. The Properties object lets you specify user, password, database information, row prefetching, and execution batching.

To use the OCI driver in this code, replace the Connection statement with this code, where MyHostString is an entry in the tnsnames.ora file:

Connection conn = DriverManager.getConnection ("jdbc:oracle:oci8:@MyHostString",
    "hr", "password");

If you are creating an applet, then the getConnection() and registerDriver() strings are different.

Overview of Oracle SQLJ


Note:

In this document, SQLJ refers to Oracle SQLJ and its extensions.

SQLJ is an ANSI SQL-1999 standard for embedding SQL statements in Java source code. SQLJ provides a simpler alternative to JDBC for both client-side and server-side SQL data access from Java.

A SQLJ source file contains Java source with embedded SQL statements. Oracle SQLJ supports dynamic and static SQL. Support for dynamic SQL is an Oracle extension to the SQLJ standard.

Oracle Database provides a translator and a run time driver to support SQLJ. The SQLJ translator is 100% pure Java and is portable to any JVM that is compliant with JDK version 1.1 or higher.

The Oracle SQLJ translator performs these tasks:

  • Translates SQLJ source to Java code with calls to the SQLJ run time driver. The SQLJ translator converts the source code to pure Java source code and can check the syntax and semantics of static SQL statements against a database schema and verify the type compatibility of host variables with SQL types.

  • Compiles the generated Java code with the Java compiler.

  • (Optional) Creates profiles for the target database. SQLJ generates "profile" files with customization specific to Oracle Database.

Oracle Database supports SQLJ stored subprograms and triggers that run in the Oracle JVM. SQLJ is integrated with JDeveloper. Source-level debugging support for SQLJ is available in JDeveloper.

This is an example of a simple SQLJ executable statement, which returns one value because employee_id is unique in the employee table:

String name;
#sql  { SELECT first_name INTO :name FROM employees WHERE employee_id=112 };
System.out.println("Name is " + name + ", employee number = " + employee_id);

Each host variable (or qualified name or complex Java host expression) included in a SQL expression is preceded by a colon (:). Other SQLJ statements declare Java types. For example, you can declare an iterator (a construct related to a database cursor) for queries that retrieve many values, as follows:

#sql iterator EmpIter (String EmpNam, int EmpNumb);

See Also:

Oracle Database JPublisher User's Guide for more examples and details about Oracle SQLJ syntax

Topics:


See Also:

Oracle Database Concepts for additional general information about SQLJ

Benefits of SQLJ

Oracle SQLJ extensions to Java enable rapid development and easy maintenance of applications that perform database operations through embedded SQL.

In particular, Oracle SQLJ does this:

  • Provides a concise, legible mechanism for database access from static SQL. Most SQL in applications is static. SQLJ provides more concise and less error-prone static SQL constructs than JDBC does.

  • Provides an SQL Checker module for verification of syntax and semantics at translate time.

  • Provides flexible deployment configurations, which makes it possible to implement SQLJ on the client, server, or middle tier.

  • Supports a software standard. SQLJ is an effort of a group of vendors and is supported by all of them. Applications can access multiple database vendors.

  • Provides source code portability. Executables can be used with all of the vendor DBMSs if the code does not rely on any vendor-specific features.

  • Enforces a uniform programming style for the clients and the servers.

  • Integrates the SQLJ translator with Oracle JDeveloper, a graphical IDE that provides SQLJ translation, Java compilation, profile customizing, and debugging at the source code level, all in one step.

  • Includes Oracle Database type extensions.

SQLJ Stored Subprograms in the Server

SQLJ applications can be stored and executed in the server by using these techniques:

  • Translate, compile, and customize the SQLJ source code on a client and load the generated classes and resources into the server with the loadjava utility. The classes are typically stored in a Java archive (.jar) file.

  • Load the SQLJ source code into the server, also using loadjava, where it is translated and compiled by the server's embedded translator.

Comparing Oracle JDBC and Oracle SQLJ

JDBC code and SQLJ code interoperate, enabling dynamic SQL statements in JDBC to be used with both static and dynamic SQL statements in SQLJ. A SQLJ iterator class corresponds to the JDBC result set.

Some differences between JDBC and SQLJ are:

  • JDBC provides a complete dynamic SQL interface from Java to databases. It gives developers full control over database operations. SQLJ simplifies Java database programming to improve development productivity.

  • JDBC provides fine-grained control of the execution of dynamic SQL from Java, whereas SQLJ provides a higher-level binding to SQL operations in a specific database schema.

  • SQLJ source code is more concise than equivalent JDBC source code.

  • SQLJ uses database connections to type-check static SQL code. JDBC, being a completely dynamic API, does not.

  • SQLJ provides strong typing of query outputs and return parameters and provides type-checking on calls. JDBC passes values to and from SQL without compile-time type checking.

  • SQLJ programs enable direct embedding of Java bind expressions within SQL statements. JDBC requires a separate get or set statement for each bind variable and specifies the binding by position number.

  • SQLJ provides simplified rules for calling SQL stored subprograms.

    For example, the following four examples show, on successive lines, how to call a stored procedure or a stored function using either JDBC escape syntax or Oracle JDBC syntax:

    prepStmt.prepareCall("{call fun(?,?)}");       //stored proc. JDBC esc.
    prepStmt.prepareCall("{? = call fun(?,?)}");   //stored func. JDBC esc.
    prepStmt.prepareCall("begin fun(:1,:2);end;"); //stored proc. Oracle
    prepStmt.prepareCall("begin :1 := fun(:2,:3);end;"); //stored func. Oracle
    

    The SQLJ equivalent is:

    #sql {call fun(param_list) };  //Stored procedure
    // Declare x
    ...
    #sql x = {VALUES(fun(param_list)) };  // Stored function
    // where VALUES is the SQL construct
    

These benefits are common to SQLJ and JDBC:

  • SQLJ source files can contain JDBC calls. SQLJ and JDBC are interoperable.

  • Oracle JPublisher generates custom Java classes to be used in your SQLJ or JDBC application for mappings to Oracle Database ADTs and collections.

  • PL/SQL and Java stored subprograms can be used interchangeably.

Overview of Oracle JPublisher

Oracle JPublisher is a code generator that automates the process of creating database-centric Java classes by hand. Oracle JPublisher is a client-side utility and is built into the database system. You can run Oracle JPublisher from the command line or directly from the Oracle JDeveloper IDE.

Oracle JPublisher inspects PL/SQL packages and database object types such as ADTs, VARRAY types, and nested table types, and then generates a Java class that is a wrapper around the PL/SQL package with corresponding fields and methods.

The generated Java class can be incorporated and used by Java clients or J2EE components to exchange and transfer database object type instances to and from the database transparently.


See Also:


Overview of Java Stored Subprograms

Java stored subprograms enable you to implement programs that run in the database server and are independent of programs that run in the middle tier. Structuring applications in this way reduces complexity and increases reuse, security, performance, and scalability.

For example, you can create a Java stored subprogram that performs operations that require data persistence and a separate program to perform presentation or business logic operations.

Java stored subprograms interface with SQL using an execution model similar to that of PL/SQL.


See Also:


Overview of Oracle Database Web Services

Web services represent a distributed computing paradigm for Java application development that is an alternative to earlier Java protocols such as JDBC, and which enable applications to interact through the XML and web protocols. For example, an electronics parts vendor can provide a web-based programmatic interface to its suppliers for inventory management. The vendor can invoke a web service as part of a program and automatically order stock based on the data returned.

The key technologies used in web services are:

  • Web Services Description Language (WSDL), which is a standard format for creating an XML document. WSDL describes what a web service can do, where it resides, and how to invoke it. Specifically, it describes the operations and parameters, including parameter types, provided by a web service. In addition, a WSDL document describes the location, the transport protocol, and the invocation style for the web service.

  • Simple Object Access Protocol (SOAP) messaging, which is an XML-based message protocol used by web services. SOAP does not prescribe a specific transport mechanism such as HTTP, FTP, SMTP, or JMS; however, most web services accept messages that use HTTP or HTTPS.

  • Universal Description, Discovery, and Integration (UDDI) business registry, which is a directory that lists web services on the internet. The UDDI registry is often compared to a telephone directory, listing unique identifiers (white pages), business categories (yellow pages), and instructions for binding to a service protocol (green pages).

Web services can use a variety of techniques and protocols. For example:

  • Dispatching can occur in a synchronous (typical) or asynchronous manner.

  • You can invoke a web service in an RPC-style operation in which arguments are sent and a response returned, or in a message style such as a one-way SOAP document exchange.

  • You can use different encoding rules: literal or encoded.

You can invoke a web service statically, when you might know everything about it beforehand, or dynamically, in which case you can discover its operations and transport endpoints while using it.

Oracle Database can function as either a web service provider or as a web service consumer. When used as a provider, the database enables sharing and disconnected access to stored subprograms, data, metadata, and other database resources such as the queuing and messaging systems.

As a web service provider, Oracle Database provides a disconnected and heterogeneous environment that:

  • Exposes stored subprograms independently of the language in which the subprograms are written

  • Exposes SQL Queries and XQuery


See Also:

Oracle Database Concepts for additional general information about Oracle Database as a web service provider

Choosing PL/SQL or Java

PL/SQL and Java interoperate in the server. You can run a PL/SQL package from Java or wrap a PL/SQL class with a Java wrapper so that it can be invoked from distributed CORBA and Enterprise Java Beans clients.

Table 13-1 shows PL/SQL packages and their Java equivalents.

Table 13-1 PL/SQL Packages and Their Java Equivalents

PL/SQL PackageJava Equivalent

DBMS_ALERT

Call package with SQLJ or JDBC.

DBMS_DDL

JDBC has this functionality.

DBMS_JOB

Schedule a job that has a Java stored subprogram.

DBMS_LOCK

Call with SQLJ or JDBC.

DBMS_MAIL

Use JavaMail.

DBMS_OUTPUT

Use subclass oracle.aurora.rdbms.OracleDBMSOutputStream or Java stored subprogram DBMS_JAVA.SET_STREAMS.

DBMS_PIPE

Call with SQLJ or JDBC.

DBMS_SESSION

Use JDBC to run an ALTER SESSION statement.

DBMS_SNAPSHOT

Call with SQLJ or JDBC.

DBMS_SQL

Use JDBC.

DBMS_TRANSACTION

Use JDBC to run an ALTER SESSION statement.

DBMS_UTILITY

Call with SQLJ or JDBC.

UTL_FILE

Grant the JAVAUSERPRIV privilege and then use Java I/O entry points.


Topics:

Similarities of PL/SQL and Java

Both PL/SQL and Java provide packages and libraries.

Both PL/SQL and Java have object-oriented features:

  • Both have inheritance.

  • PL/SQL has type evolution, the ability to change methods and attributes of a type while preserving subtypes and table data that use the type.

  • Java has polymorphism and component models for developing distributed systems.

PL/SQL Advantages Over Java

As an extension of SQL, PL/SQL supports all SQL data types, data encapsulation, information hiding, overloading, and exception-handling. Therefore:

  • SQL data types are easier to use in PL/SQL than in Java.

  • SQL operations are faster with PL/SQL than with Java, especially when a large amount of data is involved, when mostly database access is done, or when bulk operations are used.

    Some advanced PL/SQL capabilities are not available for Java in Oracle9i (for example, autonomous transactions and the dblink facility for remote databases).

Code development is usually faster in PL/SQL than in Java.

Java Advantages Over PL/SQL

Java is used for open distributed applications, and many Java-based development tools are available throughout the industry. Java has a richer type system than PL/SQL. Java can use CORBA (which can have many different computer languages in its clients) and Enterprise Java Beans. PL/SQL packages can be invoked from CORBA or Enterprise Java Beans clients. You can run XML tools, the Internet File System, or JavaMail from Java.

Overview of Precompilers

Client/server programs are typically written using precompilers, which are programming tools that let you embed SQL statements in high-level programs written in languages such as C, C++, or COBOL. Because the client application hosts the SQL statements, it is called a host program, and the language in which it is written is called the host language.

A precompiler accepts the host program as input, translates the embedded SQL statements into standard database runtime library calls, and generates a source program that you can compile, link, and run in the usual way.

Topics:


See Also:

Oracle Database Concepts for additional general information about Oracle precompilers

Overview of the Pro*C/C++ Precompiler

For the Pro*C/C++ precompiler, the host language is either C or C++. Some features of the Pro*C/C++ precompiler are:

  • You can write multithreaded programs if your platform supports a threads package. Concurrent connections are supported in either single-threaded or multithreaded applications.

  • You can improve performance by embedding PL/SQL blocks. These blocks can invoke subprograms in Java or PL/SQL that are written by you or provided in Oracle Database packages.

  • Using precompiler options, you can check the syntax and semantics of your SQL or PL/SQL statements during precompilation, and at run time.

  • You can invoke stored PL/SQL and Java subprograms. Modules written in COBOL or in C can be invoked from Pro*C/C++. External C subprograms in shared libraries can be invoked by your program.

  • You can conditionally precompile sections of your code so that they can run in different environments.

  • You can use arrays, or structures, or arrays of structures as host and indicator variables in your code to improve performance.

  • You can deal with errors and warnings so that data integrity is guaranteed. As a programmer, you control how errors are handled.

  • Your program can convert between internal data types and C language data types.

  • The Oracle Call Interface (OCI) and Oracle C++ Call Interface (OCCI), lower-level C and C++ interfaces, are available for use in your precompiler source.

  • Pro*C/C++ supports dynamic SQL, a technique that enables users to input variable values and statement syntax.

  • Pro*C/C++ can use special SQL statements to manipulate tables containing user-defined object types. An Object Type Translator (OTT) maps the ADTs and named collection types in your database to structures and headers that you include in your source.

  • Two kinds of collection types, nested tables and VARRAY, are supported with a set of SQL statements that give you a high degree of control over data.

  • Large Objects are accessed by another set of SQL statements.

  • A new ANSI SQL standard for dynamic SQL is supported for new applications, so that you can run SQL statements with a varying number of host variables. An older technique for dynamic SQL is still usable by pre-existing applications.

  • Globalization support lets you use multibyte characters and UCS2 Unicode data.

  • Using scrollable cursors, you can move backward and forward through a result set. For example, you can fetch the last row of the result set, or jump forward or backward to an absolute or relative position within the result set.

  • A connection pool is a group of physical connections to a database that can be shared by several named connections. Enabling the connection pool option can help optimize the performance of Pro*C/C++ application. The connection pool option is not enabled by default.


See Also:

Pro*C/C++ Precompiler Programmer's Guide for complete information about the Pro*C/C++ precompiler

Example 13-1 is a code fragment from a C source program that queries the table employees in the schema hr.

Example 13-1 Pro*C/C++ Application

...
#define  UNAME_LEN   10
...
int   emp_number;
/* Define a host structure for the output values of a SELECT statement. */
/* No declare section needed if precompiler option MODE=ORACLE          */
struct {
    VARCHAR  last_name[UNAME_LEN];
    float    salary;
    float    commission_pct;
} emprec;
/* Define an indicator structure to correspond to the host output structure. */
struct {
    short emp_name_ind;
    short sal_ind;
    short comm_ind;
} emprec_ind;
...
/* Select columns last_name, salary, and commission_pct given the user's input 
/* for employee_id. */
    EXEC SQL SELECT last_name, salary, commission_pct
        INTO :emprec INDICATOR :emprec_ind
        FROM employees
        WHERE employee_id = :emp_number;
...

The embedded SELECT statement differs slightly from the interactive (SQL*Plus) SELECT statement. Every embedded SQL statement begins with EXEC SQL. The colon (:) precedes every host (C) variable. The returned values of data and indicators (set when the data value is NULL or character columns were truncated) can be stored in structs (such as in the preceding code fragment), in arrays, or in arrays of structs. Multiple result set values are handled very simply in a manner that resembles the case shown, where there is only one result, because of the unique employee number. Use the actual names of columns and tables in embedded SQL.

Either use the default precompiler option values or enter values that give you control over the use of resources, how errors are reported, the formatting of output, and how cursors (which correspond to a particular connection or SQL statement) are managed. Cursors are used when there are multiple result set values.

Enter the options either in a configuration file, on the command line, or inline inside your source code with a special statement that begins with EXEC ORACLE. If there are no errors found, you can compile, link, and run the output source file, like any other C program that you write.

Use the precompiler to create server database access from clients that can be on many different platforms. Pro*C/C++ gives you the freedom to design your own user interfaces and to add database access to existing applications.

Before writing your embedded SQL statements, you can test interactive versions of the SQL in SQL*Plus and then make minor changes to start testing your embedded SQL application.

Overview of the Pro*COBOL Precompiler

For the Pro*COBOL precompiler, the host language is COBOL. Some features of the Pro*COBOL precompiler are:

  • You can invoke stored PL/SQL or Java subprograms. You can improve performance by embedding PL/SQL blocks. These blocks can invoke PL/SQL subprograms written by you or provided in Oracle Database packages.

  • Precompiler options enable you to define how cursors, errors, syntax-checking, file formats, and so on, are handled.

  • Using precompiler options, you can check the syntax and semantics of your SQL or PL/SQL statements during precompilation, and at run time.

  • You can conditionally precompile sections of your code so that they can run in different environments.

  • Use tables, or group items, or tables of group items as host and indicator variables in your code to improve performance.

  • You can program how errors and warnings are handled, so that data integrity is guaranteed.

  • Pro*COBOL supports dynamic SQL, a technique that enables users to input variable values and statement syntax.


    See Also:

    Pro*COBOL Programmer's Guide for complete information about the Pro*COBOL precompiler

Example 13-2 is a code fragment from a COBOL source program that queries the table employees in the schema hr.

Example 13-2 Pro*COBOL Application

...
 WORKING-STORAGE SECTION.
*
* DEFINE HOST INPUT AND OUTPUT HOST AND INDICATOR VARIABLES.
* NO DECLARE SECTION NEEDED IF MODE=ORACLE.
*
 01  EMP-REC-VARS.
     05  EMP-NAME    PIC X(10) VARYING.
     05  EMP-NUMBER  PIC S9(4) COMP VALUE ZERO.
     05  SALARY      PIC S9(5)V99 COMP-3 VALUE ZERO.
     05  COMMISSION  PIC S9(5)V99 COMP-3 VALUE ZERO.
     05  COMM-IND    PIC S9(4) COMP VALUE ZERO.
...
 PROCEDURE DIVISION.
...
     EXEC SQL
         SELECT last_name, salary, commission_pct
         INTO :EMP-NAME, :SALARY, :COMMISSION:COMM-IND
         FROM employees
         WHERE employee_id = :EMP-NUMBER
     END-EXEC.
...

The embedded SELECT statement is only slightly different from an interactive (SQL*Plus) SELECT statement. Every embedded SQL statement begins with EXEC SQL. The colon (:) precedes every host (COBOL) variable. The SQL statement is terminated by END-EXEC. The returned values of data and indicators (set when the data value is NULL or character columns were truncated) can be stored in group items (such as in the preceding code fragment), in tables, or in tables of group items. Multiple result set values are handled very simply in a manner that resembles the case shown, where there is only one result, given the unique employee number. Use the actual names of columns and tables in embedded SQL.

Use the default precompiler option values, or enter values that give you control over the use of resources, how errors are reported, the formatting of output, and how cursors are managed (cursors correspond to a particular connection or SQL statement).

Enter the options in a configuration file, on the command line, or inline inside your source code with a special statement that begins with EXEC ORACLE. If there are no errors found, you can compile, link, and run the output source file, like any other COBOL program that you write.

Use the precompiler to create server database access from clients that can be on many different platforms. Pro*COBOL gives you the freedom to design your own user interfaces and to add database access to existing COBOL applications.

The embedded SQL statements available conform to an ANSI standard, so that you can access data from many databases in a program, including remote servers networked through Oracle Net.

Before writing your embedded SQL statements, you can test interactive versions of the SQL in SQL*Plus and then make minor changes to start testing your embedded SQL application.

Overview of OCI and OCCI

The Oracle Call Interface (OCI) and Oracle C++ Call Interface (OCCI) are application programming interfaces (APIs) that enable you to create applications that use native subprogram invocations of a third-generation language to access Oracle Database and control all phases of SQL statement execution. These APIs provide:

  • Improved performance and scalability through the efficient use of system memory and network connectivity

  • Consistent interfaces for dynamic session and transaction management in a two-tier client/server or multitier environment

  • N-tiered authentication

  • Comprehensive support for application development using Oracle Database objects

  • Access to external databases

  • Ability to develop applications that service an increasing number of users and requests without additional hardware investments

OCI lets you manipulate data and schemas in a database using a host programming language, such as C. OCCI is an object-oriented interface suitable for use with C++. These APIs provide a library of standard database access and retrieval functions in the form of a dynamic runtime library (OCILIB) that can be linked in an application at run time. You need not embed SQL or PL/SQL within 3GL programs.

Topics:

Advantages of OCI and OCCI

OCI and OCCI provide significant advantages over other methods of accessing Oracle Database:

  • More fine-grained control over all aspects of the application design.

  • High degree of control over program execution.

  • Use of familiar 3GL programming techniques and application development tools such as browsers and debuggers.

  • Support of dynamic SQL, method 4.

  • Availability on the broadest range of platforms of all the Oracle Database programmatic interfaces.

  • Dynamic bind and define using callbacks.

  • Describe functionality to expose layers of server metadata.

  • Asynchronous event notification for registered client applications.

  • Enhanced array data manipulation language (DML) capability for arrays.

  • Ability to associate a commit request with an run to reduce round-trips.

  • Optimization for queries using transparent prefetch buffers to reduce round-trips.

  • Thread safety, so you do not have to implement mutual exclusion (mutex) locks on OCI and OCCI handles.

  • The server connection in nonblocking mode means that control returns to the OCI or OCCI code when a call is still running or cannot complete.

OCI and OCCI Functions

Both OCI and OCCI have four kinds of functions:

Kind of FunctionPurpose
RelationalTo manage database access and process SQL statements
NavigationalTo manipulate objects retrieved from the database
Database mapping and manipulationTo manipulate data attributes of Oracle Database types
External subprogramTo write C callbacks from PL/SQL

Procedural and Nonprocedural Elements of OCI and OCCI Applications

OCI and OCCI enable you to develop applications that combine the nonprocedural data access power of SQL with the procedural capabilities of most programming languages, including C and C++. Procedural and nonprocedural languages have these characteristics:

  • In a nonprocedural language program, the set of data to be operated on is specified, but what operations are performed and how the operations are to be carried out is not specified. The nonprocedural nature of SQL makes it an easy language to learn and to use to perform database transactions. It is also the standard language used to access and manipulate data in modern relational and object-relational database systems.

  • In a procedural language program, the execution of most statements depends on previous or subsequent statements and on control structures, such as loops or conditional branches, which are not available in SQL. The procedural nature of these languages makes them more complex than SQL, but it also makes them very flexible and powerful.

The combination of both nonprocedural and procedural language elements in an OCI or OCCI program provides easy access to Oracle Database in a structured programming environment.

OCI and OCCI support all SQL data definition, data manipulation, query, and transaction control facilities that are available through Oracle Database. For example, an OCI or OCCI program can run a query against Oracle Database. The queries can require the program to supply data to the database using input (bind) variables, as follows:

SELECT name FROM employees WHERE empno = :empnumber

In the preceding SQL statement, :empnumber is a placeholder for a value to be supplied by the application.

Alternatively, you can use PL/SQL, Oracle's procedural extension to SQL. The applications you develop can be more powerful and flexible than applications written in SQL alone. OCI and OCCI also provide facilities for accessing and manipulating objects in Oracle Database.

Building an OCI or OCCI Application

As Figure 13-1 shows, you compile and link an OCI or OCCI program in the same way that you compile and link a nondatabase application. There is no need for a separate preprocessing or precompilation step.

Figure 13-1 The OCI or OCCI Development Process

The OCI or OCCI Development Process
Description of "Figure 13-1 The OCI or OCCI Development Process"


Note:

To properly link your OCI and OCCI programs, it might be necessary on some platforms to include other libraries, in addition to the OCI and OCCI libraries. Check your Oracle platform-specific documentation for further information about extra libraries that might be required.

Choosing a Precompiler or OCI

Precompiler applications typically contain less code than equivalent OCI applications, which can help productivity.

Some situations require detailed control of the database and are suited for OCI applications (either pure OCI or a precompiler application with embedded OCI calls):

  • OCI provides more detailed control over multiplexing and migrating sessions.

  • OCI provides dynamic bind and define using callbacks that can be used for any arbitrary structure, including lists.

  • OCI has many calls to handle metadata.

  • OCI enables asynchronous event notifications to be received by a client application. It provides a means for clients to generate notifications for propagation to other clients.

  • OCI enables DML statements to use arrays to complete as many iterations as possible before returning any error messages.

  • OCI calls for special purposes include Advanced Queuing, globalization support, Data Cartridges, and support of the date and time data types.

  • OCI calls can be embedded in a Pro*C/C++ application.

Overview of Oracle Data Provider for .NET (ODP.NET)

Oracle Data Provider for .NET (ODP.NET) is an implementation of a data provider for Oracle Database.

ODP.NET uses APIs native to Oracle Database to offer fast and reliable access from any .NET application to database features and data. It also uses and inherits classes and interfaces available in the Microsoft .NET Framework Class Library.

For programmers using Oracle Provider for OLE DB, ADO (ActiveX Data Objects) provides an automation layer that exposes an easy programming model. ADO.NET provides a similar programming model, but without the automation layer, for better performance. More importantly, the ADO.NET model enables native providers such as ODP.NET to expose specific features and data types specific to Oracle Database.

This is a simple C# application that connects to Oracle Database and displays its version number before disconnecting:

using System; 
using Oracle.DataAccess.Client; 

class Example 
{ 
  OracleConnection con; 

  void Connect() 
  { 
    con = new OracleConnection(); 
    con.ConnectionString = "User Id=hr;Password=password;Data Source=oracle"; 
    con.Open(); 
    Console.WriteLine("Connected to Oracle" + con.ServerVersion); 
  } 

  void Close() 
  { 
    con.Close(); 
    con.Dispose(); 
  } 
  
  static void Main() 
  { 
    Example example = new Example(); 
    example.Connect(); 
    example.Close(); 
  } 
}

Note:

Additional samples are provided in directory ORACLE_BASE\ORACLE_HOME\ODP.NET\Samples.

Overview of OraOLEDB

Oracle Provider for OLE DB (OraOLEDB) is an OLE DB data provider that offers high performance and efficient access to Oracle data by OLE DB consumers. In general, this developer's guide assumes that you are using OraOLEDB through OLE DB or ADO.

Overview of Oracle Objects for OLE (OO4O)

Oracle Objects for OLE (OO4O) is a product designed to provide easy access to data stored in Oracle Database with any programming or scripting language that supports the Microsoft COM Automation and ActiveX technology. This includes Visual Basic, Visual C++, Visual Basic For Applications (VBA), IIS Active Server Pages (VBScript and JavaScript), and others.

See the OO4O online help for detailed information about using OO4O.

Oracle Objects for OLE consists of these software layers:

  • OO4O "In-Process" Automation Server

  • Oracle Data Control

  • Oracle Objects for OLE C++ Class Library

Figure 13-2 illustrates the OO4O software components.

Figure 13-2 Software Layers

Software Layers
Description of "Figure 13-2 Software Layers"

Topics:

OO4O Automation Server

The OO4O Automation Server is a set of COM Automation objects for connecting to Oracle Database, running SQL statements and PL/SQL blocks, and accessing the results.

Unlike other COM-based database connectivity APIs, such as Microsoft ADO, the OO4O Automation Server was developed specifically for use with Oracle Database.

It provides an optimized API for accessing features that are unique to Oracle Database and are otherwise cumbersome or inefficient to use from ODBC or OLE database-specific components.

OO4O provides key features for accessing Oracle Database efficiently and easily in environments ranging from the typical two-tier client/server applications, such as those developed in Visual Basic or Excel, to application servers deployed in multitiered application server environments such as web server applications in Microsoft Internet Information Server (IIS) or Microsoft Transaction Server (MTS).

Features include:

  • Support for execution of PL/SQL and Java stored subprograms, and PL/SQL anonymous blocks. This includes support for Oracle Database data types used as parameters to stored subprograms, including PL/SQL cursors. See "Support for Oracle LOB and Object Data Types".

  • Support for scrollable and updatable cursors for easy and efficient access to result sets of queries.

  • Thread-safe objects and Connection Pool Management Facility for developing efficient web server applications.

  • Full support for Oracle Database object-relational and LOB data types.

  • Full support for Advanced Queuing.

  • Support for array inserts and updates.

  • Support for Microsoft Transaction Server (MTS).

OO4O Object Model

The Oracle Objects for OLE object model is illustrated in Figure 13-3.

Figure 13-3 Objects and Their Relations

Objects and Their Relations
Description of "Figure 13-3 Objects and Their Relations "

Topics:

OraSession

An OraSession object manages collections of OraDatabase, OraConnection, and OraDynaset objects used within an application.

Typically, a single OraSession object is created for each application, but you can create named OraSession objects for shared use within and between applications.

The OraSession object is the top-most object for an application. It is the only object created by the CreateObject VB/VBA API and not by an Oracle Objects for OLE method. This code fragment shows how to create an OraSession object:

Dim OraSession as Object
Set OraSession = CreateObject("OracleInProcServer.XOraSession")

OraServer

OraServer represents a physical network connection to Oracle Database.

The OraServer interface is introduced to expose the connection-multiplexing feature provided in the Oracle Call Interface. After an OraServer object is created, multiple user sessions (OraDatabase) can be attached to it by calling the OpenDatabase method. This feature is particularly useful for application components, such as Internet Information Server (IIS), that use Oracle Objects for OLE in n-tier distributed environments.

The use of connection multiplexing when accessing Oracle Database with a large number of user sessions active can help reduce server processing and resource requirements while improving server scalability.

OraServer is used to share a single connection across multiple OraDatabase objects (multiplexing), whereas each OraDatabase obtained from an OraSession has its own physical connection.

OraDatabase

An OraDatabase interface adds additional methods for controlling transactions and creating interfaces representing Oracle Database object types. Attributes of schema objects can be retrieved using the Describe method of the OraDatabase interface.

Before Oracle Database 8g Release 1, an OraDatabase object is created by calling the OpenDatabase method of an OraSession interface. The Oracle Net alias, user name, and password are passed as arguments to this method. As of Oracle Database 8g Release 1, calling this method results in implicit creation of an OraServer object.

An OraDatabase object can also be created using the OpenDatabase method of the OraServer interface.

Transaction control methods are available at the OraDatabase (user session) level. Transactions might be started as Read-Write (default), Serializable, or Read-only. Transaction control methods include:

  • BeginTrans

  • CommitTrans

  • RollbackTrans

For example:

UserSession.BeginTrans(OO4O_TXN_READ_WRITE) 
UserSession.ExecuteSQL("delete emp where empno = 1234") 
UserSession.CommitTrans

OraDynaset

An OraDynaset object permits browsing and updating of data created from a SQL SELECT statement.

The OraDynaset object can be thought of as a cursor, although in actuality several real cursors might be used to implement the semantics of OraDynaset. An OraDynaset object automatically maintains a local cache of data fetched from the server and transparently implements scrollable cursors within the browse data. Large queries might require significant local disk space; application developers are encouraged to refine queries to limit disk usage.

OraField

An OraField object represents a single column or data item within a row of a dynaset.

If the current row is being updated, then the OraField object represents the currently updated value, although the value might not have been committed to the database.

Assignment to the Value property of a field is permitted only if a record is being edited (using Edit) or a record is being added (using AddNew). Other attempts to assign data to a field's Value property results in an error.

OraMetaData and OraMDAttribute

An OraMetaData object is a collection of OraMDAttribute objects that represent the description information about a particular schema object in the database.

The OraMetaData object can be visualized as a table with three columns:

  • Metadata Attribute Name

  • Metadata Attribute Value

  • Flag specifying whether the Value is another OraMetaData object

The OraMDAttribute objects contained in the OraMetaData object can be accessed by subscripting using ordinal integers or by using the name of the property. Referencing an index that is not in the collection results in the return of a NULL OraMDAttribute object.

OraParameter and OraParameters

An OraParameter object represents a bind variable in a SQL statement or PL/SQL block.

OraParameter objects are created, accessed, and removed indirectly through the OraParameters collection of an OraDatabase object. Each parameter has an identifying name and an associated value. You can automatically bind a parameter to SQL and PL/SQL statements of other objects (as noted in the object descriptions), by using the parameter name as a placeholder in the SQL or PL/SQL statement. Such use of parameters can simplify dynamic queries and increase program performance.

OraParamArray

An OraParamArray object represents an array-type bind variable in a SQL statement or PL/SQL block, as opposed to a scalar-type bind variable represented by the OraParameter object.

OraParamArray objects are created, accessed, and removed indirectly through the OraParameters collection of an OraDatabase object. Each OraParamArray object has an identifying name and an associated value.

OraSQLStmt

An OraSQLStmt object represents a single SQL statement. Use the CreateSQL method to create an OraSQLStmt object from an OraDatabase object.

During create and refresh, OraSQLStmt objects automatically bind all relevant, enabled input parameters to the specified SQL statement, using the parameter names as placeholders in the SQL statement. This can improve the performance of SQL statement execution without reparsing the SQL statement.

The OraSQLStmt object can be used later to run the same query using a different value for the :SALARY placeholder. This is done as follows (updateStmt is the OraSQLStmt object here):

OraDatabase.Parameters("SALARY").value = 200000
updateStmt.Parameters("ENAME").value = "KING"
updateStmt.Refresh

OraAQ

An OraAQ object is instantiated by calling the CreateAQ method of the OraDatabase interface. It represents a queue that is present in the database.

Oracle Objects for OLE provides interfaces for accessing Oracle Advanced Queuing (AQ) feature. It makes AQ accessible from popular COM-based development environments such as Visual Basic. For a detailed description of Oracle Advanced Queuing, see Oracle Streams Advanced Queuing User's Guide.

OraAQMsg

The OraAQMsg object encapsulates the message to be enqueued or dequeued. The message can be of any user-defined or raw type.

For a detailed description of Oracle Advanced Queuing, see Oracle Streams Advanced Queuing User's Guide.

OraAQAgent

The OraAQAgent object represents a message recipient and is only valid for queues that support multiple consumers. It is a child of OraAQMsg.

An OraAQAgent object can be instantiated by calling the AQAgent method. For example:

Set agent = qMsg.AQAgent(name)

An OraAQAgent object can also be instantiated by calling the AddRecipient method. For example:

Set agent = qMsg.AddRecipient(name, address, protocol).

Support for Oracle LOB and Object Data Types

Oracle Objects for OLE (OO4O) provides full support for accessing and manipulating instances of object data types and LOBs in Oracle Database. Figure 13-4 ill_ustrates the data types supported by OO4O.

Instances of these types can be fetched from the database or passed as input or output variables to SQL statements and PL/SQL blocks, including stored subprograms. All instances are mapped to COM Automation Interfaces that provide methods for dynamic attribute access and manipulation.

Figure 13-4 Supported Oracle Database Data Types

Supported Oracle Database Data Types
Description of "Figure 13-4 Supported Oracle Database Data Types"

Topics:

OraBLOB and OraCLOB

The OraBlob and OraClob interfaces in Oracle Objects for OLE provide methods for performing operations on large database objects of data type BLOB, CLOB, and NCLOB. BLOB, CLOB, and NCLOB data types are also referred to here as LOB data types.

LOB data is accessed using Read and the CopyToFile methods.

LOB data is modified using Write, Append, Erase, Trim, Copy, CopyFromFile, and CopyFromBFile methods. Before modifying the content of a LOB column in a row, a row lock must be obtained. If the LOB column is a field of an OraDynaset, object, then the lock is obtained by calling the Edit method.

OraBFILE

The OraBFile interface in Oracle Objects for OLE provides methods for performing operations on large database objects of data type BFILE.

BFILE objects are large binary data objects stored in operating system files outside of the database tablespaces.

Oracle Data Control

Oracle Data Control (ODC) is an ActiveX Control that is designed to simplify the exchange of data between Oracle Database and visual controls such edit, text, list, and grid controls in Visual Basic and other development tools that support custom controls.

ODC acts as an agent to handle the flow of information from Oracle Database and a visual data-aware control, such as a grid control, that is bound to it. The data control manages various user interface (UI) tasks such as displaying and editing data. It also runs and manages the results of database queries.

Oracle Data Control is compatible with the Microsoft data control included with Visual Basic. If you are familiar with the Visual Basic data control, learning to use Oracle Data Control is quick and easy. Communication between data-aware controls and a Data Control is governed by a protocol that Microsoft specified.

Oracle Objects for OLE C++ Class Library

Oracle Objects for OLE (OO4O) C++ Class Library is a collection of C++ classes that provide programmatic access to the Oracle Object Server. Although the class library is implemented using OLE Automation, neither the OLE development kit nor any OLE development knowledge is necessary to use it. This library helps C++ developers avoid the chore of writing COM client code for accessing the OO4O interfaces.


See Also:


PKe,s_PK|%AOEBPS/adfns_regexp.htm Using Regular Expressions in Database Applications

3 Using Regular Expressions in Database Applications

This chapter describes regular expressions and explains how to use them in database applications.

Topics:


See Also:

  • Oracle Database Globalization Support Guide for information about using SQL regular expression functions in a multilingual environment

  • Oracle Regular Expressions Pocket Reference by Jonathan Gennick, O'Reilly & Associates

  • Mastering Regular Expressions by Jeffrey E. F. Friedl, O'Reilly & Associates


Overview of Regular Expressions

A regular expression specifies a search pattern, using metacharacters (which are, or belong to, operators) and character literals (described in Oracle Database SQL Language Reference).

The search pattern can be complex. For example, this regular expression matches any string that begins with either f or ht, followed by tp, optionally followed by s, followed by the colon (:):

(f|ht)tps?:

The metacharacters (which are also operators) in the preceding example are the parentheses, the pipe symbol (|), and the question mark (?). The character literals are f, ht, tp, s, and the colon (:).

Parentheses group multiple pattern elements into a single element. The pipe symbol (|) indicates a choice between the elements on either side of it, f and ht. The question mark (?) indicates that the preceding element, s, is optional. Thus, the preceding regular expression matches these strings:

  • http:

  • https:

  • ftp:

  • ftps:

Regular expressions are a powerful text-processing component of the programming languages Java and PERL. For example, a PERL script can read the contents of each HTML file in a directory into a single string variable and then use a regular expression to search that string for URLs. This robust pattern-matching functionality is one reason that many application developers use PERL.

Oracle SQL Support for Regular Expressions

Oracle SQL support for regular expressions lets application developers implement complex pattern-matching logic in the database, which is useful for these reasons:

  • By centralizing pattern-matching logic in the database, you avoid intensive string processing of SQL results sets by middle-tier applications.

    For example, life science customers often rely on PERL to do pattern analysis on bioinformatics data stored in huge databases of DNA and proteins. Previously, finding a match for a protein sequence such as [AG].{4}GK[ST] was handled in the middle tier. The SQL regular expression functions move the processing logic closer to the data, thereby providing a more efficient solution.

  • By using server-side regular expressions to enforce constraints, you avoid duplicating validation logic on multiple clients.

Oracle SQL supports regular expressions with the pattern-matching condition and functions summarized in Table 3-1. Each pattern matcher searches a given string for a given pattern (described with a regular expression), and each has the pattern-matching options described in Table 3-2. The functions have additional options (for example, the character position at which to start searching the string for the pattern). For details, see Oracle Database SQL Language Reference.

Table 3-1 Oracle SQL Pattern-Matching Condition and Functions

NameDescription

REGEXP_LIKE

Condition that can appear in the WHERE clause of a query, causing the query to return rows that match the given pattern.

Example: This WHERE clause identifies employees with the first name of Steven or Stephen:

WHERE REGEXP_LIKE((hr.employees.first_name, '^Ste(v|ph)en$')

REGEXP_COUNT

Function that returns the number of times the given pattern appears in the given string.

Example: This function invocation returns the number of times that e (but not E) appears in the string 'Albert Einstein', starting at character position 7:

REGEXP_COUNT('Albert Einstein', 'e', 7, 'c')

(The returned value is 1, because the c option specifies case-sensitive matching.)

REGEXP_INSTR

Function that returns an integer that indicates the starting position of the given pattern in the given string. Alternatively, the integer can indicate the position immediately following the end of the pattern.

Example: This function invocation returns the starting position of the first valid email address in the column hr.employees.email:

REGEXP_INSTR(hr.employees.email, '\w+@\w+(\.\w+)+')

If the returned value is greater than zero, then the column contains a valid email address.

REGEXP_REPLACE

Function that returns the string that results from replacing occurrences of the given pattern in the given string with a replacement string.

Example: This function invocation puts a space after each character in the column hr.countries.country_name:

REGEXP_REPLACE(hr.countries.country_name, '(.)', '\1 ')

REGEXP_SUBSTR

Function that is like REGEXP_INSTR except that instead of returning the starting position of the given pattern in the given string, it returns the matching substring itself.

Example: This function invocation returns 'Oracle' because the x option ignores the spaces in the pattern:

REGEXP_SUBSTR('Oracle 2010', 'O r a c l e', 1, 1, 'x')

Table 3-2 describes the pattern-matching options that are available to each pattern matcher in Table 3-1.

Table 3-2 Pattern-Matching Options for Oracle SQL Pattern-Matching Condition and Functions

OptionDescriptionExample

i

Specifies case-insensitive matching.

This function invocation returns 3:

REGEXP_COUNT('Albert Einstein', 'e', 'i')

c

Specifies case-sensitive matching.

This function invocation returns 2:

REGEXP_COUNT('Albert Einstein', 'e', 'c')

n

Allows the Dot operator (.) to match the newline character, which is not the default (see Table 3-3).

In this function invocation, the string and search pattern match only because the n option is specified:

REGEXP_SUBSTR('a'||CHR(10)||'d', 'a.d', 1, 1, 'n')

m

Specifies multiline mode, where a newline character inside a string terminates a line. The string can contain multiple lines.

Multiline mode affects POSIX operators Beginning-of-Line Anchor (^) and End-of-Line Anchor ($) (described in Table 3-3) but not PERL-influenced operators \A, \Z, and \z (described in Table 3-5).

This function invocation returns ac:

REGEXP_SUBSTR('ab'||CHR(10)||'ac', '^a.', 1, 2, 'm')

x

Ignores whitespace characters in the search pattern. By default, whitespace characters match themselves.

This function invocation returns abcd:

REGEXP_SUBSTR('abcd', 'a b c d', 1, 1, 'x')

Oracle SQL and POSIX Regular Expression Standard

Oracle SQL implementation of regular expressions conforms to these standards:

Oracle SQL extends regular expression support beyond the POSIX standard in these ways:

Operators in Oracle SQL Regular Expressions

Oracle SQL supports a set of common operators (composed of metacharacters) used in regular expressions.


Caution:

The interpretation of metacharacters differs between tools that support regular expressions. If you are porting regular expressions from another environment to Oracle Database, ensure that Oracle SQL supports their syntax and interprets them as you expect.

Topics:

POSIX Operators in Oracle SQL Regular Expressions

Table 3-3 summarizes the POSIX operators defined in the POSIX standard Extended Regular Expression (ERE) syntax. Oracle SQL follows the exact syntax and matching semantics for these operators as defined in the POSIX standard for matching ASCII (English language) data. Any differences in action between Oracle SQL and the POSIX standard are noted in the Description column.

Table 3-3 POSIX Operators in Oracle SQL Regular Expressions

Operator SyntaxNamesDescriptionExamples

.

Any Character

Dot

Matches any character in the database character set, including the newline character if you specify matching option n (see Table 3-2).

The Linux, UNIX, and Windows platforms recognize the newline character as the linefeed character (\x0a).

The Macintosh platforms recognize the newline character as the carriage return character (\x0d).

Note: In the POSIX standard, this operator matches any English character except NULL and the newline character.

The expression a.b matches the strings abb, acb, and adb, but does not match acc.

+


One or More

Plus Quantifier

Matches one or more occurrences of the preceding subexpression (greedyFoot 1 ).

The expression a+ matches the strings a, aa, and aaa, but does not match ba or ab.

*


Zero or More

Star Quantifier

Matches zero or more occurrences of the preceding subexpression (greedyFootref 1).

The expression ab*c matches the strings ac, abc, and abbc, but does not match abb or bbc.

?

Zero or One

Question Mark Quantifier

Matches zero or one occurrences of the preceding subexpression (greedyFootref 1).

The expression ab?c matches the strings abc and ac, but does not match abbc or adc.

{m}

Interval

Exact Count

Matches exactly m occurrences of the preceding subexpression.

The expression a{3} matches the string aaa, but does not match aa.

{m,}

Interval

At-Least Count

Matches at least m occurrences of the preceding subexpression (greedyFootref 1).

The expression a{3,} matches the strings aaa and aaaa, but does not match aa.

{m,n}

Interval

Between Count

Matches at least m but not more than n occurrences of the preceding subexpression (greedyFootref 1).

The expression a{3,5} matches the strings aaa, aaaa, and aaaaa, but does not match aa or aaaaaa.

[char...]

Matching Character List

Matches any single character in the list within the brackets. In the list, all operators except these are treated as literals:

  • Range operator: -

  • POSIX character class: [: :]

  • POSIX collation element: [. .]

  • POSIX character equivalence class: [= =]

A dash (-) is a literal when it occurs first or last in the list, or as an ending range point in a range expression, as in [#--]. A right bracket (]) is treated as a literal if it occurs first in the list.

Note: In the POSIX standard, a range includes all collation elements between the start and end of the range in the linguistic definition of the current locale. Thus, ranges are linguistic rather than byte value ranges; the semantics of the range expression are independent of the character set. In Oracle Database, the linguistic range is determined by the NLS_SORT initialization parameter.

The expression [abc] matches the first character in the strings all, bill, and cold, but does not match any characters in doll.

[^char...]

Nonmatching Character List

Matches any single character not in the list within the brackets.

For information about operators and ranges in the character list, see the description of the Matching Character List operator.

The expression [^abc]def matches the string xdef, but not adef, bdef, or cdef.

The expression [^a-i]x matches the string jx, but does not match ax, fx, or ix.

[alt1 |alt2]

Or

Matches either alternative.

The expression a|b matches the character a or b.

(expr)

Subexpression

Grouping

Treats the expression within the parentheses as a unit. The expression can be a string or a complex expression containing operators.

You can refer to a subexpression in a back reference.

The expression (abc)?def matches the strings abcdef and def, but does not match abcdefg or xdef.

\n

Back Reference

Matches the nth preceding subexpression, where n is an integer from 1 through 9. A back reference counts subexpressions from left to right, starting with the opening parenthesis of each preceding subexpression. The expression is invalid if fewer than n subexpressions precede \n.

A back reference lets you search for a repeated string without knowing what it is.

For the REGEXP_REPLACE function, Oracle SQL supports back references in both the regular expression pattern and the replacement string.

The expression (abc|def)xy\1 matches the strings abcxyabc and defxydef, but does not match abcxydef or abcxy.

The expression ^(.*)\1$ matches a line consisting of two adjacent instances of the same string.

\


Escape Character

Treats the subsequent character as a literal.

A backslash (\) lets you search for a character that would otherwise be treated as a metacharacter. Use consecutive backslashes (\\) to match the backslash literal itself.

The expression abc\+def matches the string abc+def, but does not match abcdef or abccdef.

^

Beginning-of-Line Anchor

Default mode: Matches the beginning of a string.

Multiline mode:Foot 2  Matches the beginning of any line the source string.

The expression ^def matches the substring def in the string defghi but not in the string abcdef.

$


End-of-Line Anchor

Default mode: Matches the end of a string.

Multiline mode:Footref 2 Matches the end of any line the source string.

The expression def$ matches the substring def in the string abcdef but not in the string defghi.

[:class:]

POSIX Character Class

Matches any character in the specified POSIX character class (such as uppercase characters, digits, or punctuation characters).

Note: In English regular expressions, range expressions often indicate a character class. For example, [a-z] indicates any lowercase character. This convention is not useful in multilingual environments, where the first and last character of a given character class might not be the same in all languages.

The expression [:upper:]+, which specifies one or more consecutive uppercase characters, matches the substring DEF in the string abcDEFghi, but does not match any substring in abcdefghi.

[.element.]

POSIX Collating Element Operator

Specifies a collating element defined in the current locale. The NLS_SORT initialization parameter determines the supported collation elements.

This syntax lets you use a multicharacter collating element where otherwise only single-character collating elements are allowed. For example, you can ensure that the collating element ch, when defined in a locale such as Traditional Spanish, is treated as one character in operations that depend on the ordering of characters.

The expression [.ch.], which specifies the collating element ch, matches ch in the string chabc, but does not match any substring in cdefg.

The expression [a-[.ch.]] specifies the range from a through ch.

[=char=]

POSIX Character Equivalence Class

Matches all characters that belong to the same POSIX character equivalence class as the specified character, in the current locale.

This syntax must appear within a character list; that is, it must be nested within the brackets for a character list.

Character equivalents depend on how canonical rules are defined for your database locale. For details, see Oracle Database Globalization Support Guide.

The expression [[=n=]], which specifies characters equivalent to n in a Spanish locale, matches both N and ñ in the string El Niño.


Footnote 1 A greedy operator matches as many occurrences as possible while allowing the rest of the match to succeed. To make the operator nongreedy, follow it with the nongreedy modifier (?) (see Table 3-5).

Footnote 2 Specify multiline mode with the pattern-matching option m, described in Table 3-2.

Oracle SQL Multilingual Extensions to POSIX Standard

When applied to multilingual data, Oracle SQL POSIX operators extend beyond the matching capabilities specified in the POSIX standard.

Table 3-4 shows, for each POSIX operator, which POSIX standards define its syntax and whether Oracle SQL extends its semantics for handling multilingual data. The POSIX standards are Basic Regular Expression (BRE) and Extended Regular Expression (ERE).

Table 3-4 POSIX Operators and Multilingual Operator Relationships

OperatorPOSIX BRE SyntaxPOSIX ERE SyntaxMultilingual Enhancement

\


Yes

Yes

--

*


Yes

Yes

--

+


--

Yes

--

?

--

Yes

--

|


--

Yes

--

^

Yes

Yes

Yes

$


Yes

Yes

Yes

.

Yes

Yes

Yes

[ ]

Yes

Yes

Yes

( )

Yes

Yes

--

{m}

Yes

Yes

--

{m,}

Yes

Yes

--

{m,n}

Yes

Yes

--

\n

Yes

Yes

Yes

[..]

Yes

Yes

Yes

[::]

Yes

Yes

Yes

[==]


Yes

Yes

Yes


Multilingual data might have multibyte characters. Oracle Database lets you enter multibyte characters directly (if you have a direct input method) or use functions to compose them. You cannot use the Unicode hexadecimal encoding value of the form \xxxx. Oracle Database evaluates the characters based on the byte values used to encode the character, not the graphical representation of the character.

Oracle SQL PERL-Influenced Extensions to POSIX Standard

Oracle SQL supports some commonly used PERL regular expression operators that are not included in the POSIX standard but do not conflict with it.

Table 3-5 summarizes the PERL-influenced operators that Oracle SQL supports.


Caution:

PERL character class matching is based on the locale model of the operating system, whereas Oracle SQL regular expressions are based on the language-specific data of the database. In general, you cannot expect a regular expression involving locale data to produce the same results in PERL and Oracle SQL.

Table 3-5 PERL-Influenced Operators in Oracle SQL Regular Expressions

Operator SyntaxDescriptionExamples

\d

Matches a digit character.

Equivalent to POSIX expression [[:digit:]].

The expression ^\(\d{3}\) \d{3}-\d{4}$ matches (650) 555-0100 but does not match 650-555-0100.

\D

Matches a nondigit character.

Equivalent to POSIX expression [^[:digit:]].

The expression \w\d\D matches b2b and b2_ but does not match b22.

\w

Matches a word character (that is, an alphanumeric or underscore (_) character).

Equivalent to POSIX expression [[:alnum:]_].

The expression \w+@\w+(\.\w+)+ matches the string jdoe@company.co.uk but does not match jdoe@company.

\W

Matches a nonword character.

Equivalent to POSIX expression [^[:alnum:]_].

The expression \w+\W\s\w+ matches the string to: bill but does not match to bill.

\s

Matches a whitespace character.

Equivalent to POSIX expression [[:space:]].

The expression \(\w\s\w\s\) matches the string (a b ) but does not match (ab) or (a,b.).

\S

Matches a nonwhitespace character.

Equivalent to POSIX expression [^[:space:]].

The expression \(\w\S\w\S\) matches the strings (abde) and (a,b.) but does not match (a b d e).

\A

Matches the beginning of a string, in either single-line or multiline mode.

Not equivalent to POSIX operator ^.

The expression \AL matches only the first L in the string Line1\nLine2\n (where \n is the newline character), in either single-line or multiline mode.

\Z

Matches the end of a string, in either single-line or multiline mode.

Not equivalent to POSIX operator $.

The expression \s\Z matches the last space in the string L i n e \n (where \n is the newline character), in either single-line or multiline mode.

\z

Matches the end of a string, in either single-line or multiline mode.

Not equivalent to POSIX operator $.

The expression \s\z matches the newline character (\n) in the string L i n e \n, in either single-line or multiline mode.

+?

Matches one or more occurrences of the preceding subexpression (nongreedyFoot 1 ).

The expression \w+?x\w matches abxc in the string abxcxd (and the greedy expression \w+x\w matches abxcxd).

*?

Matches zero or more occurrences of the preceding subexpression (nongreedyFootref 1). Matches the empty string whenever possible.

The expression \w*?x\w matches xa in the string xaxbxc (and the greedy expression \w*x\w matches xaxbxc.

??

Matches zero or one occurrences of the preceding subexpression (nongreedyFootref 1). Matches the empty string whenever possible.

The expression a??aa matches aa in the string aaaa (and the greedy expression a?aa matches aaa).

{m}?

Matches exactly m occurrences of the preceding subexpression (nongreedyFootref 1).

The expression (a|aa){2}? matches aa in the string aaaa (and the greedy expression (a|aa){2} matches aaaa.

Both the expression b{2}? and the greedy expression b{2} match bb in the string bbbb.

{m,}?

Matches at least m occurrences of the preceding subexpression (nongreedyFootref 1).

The expression a{2,}? matches aa in the string aaaaa (and the greedy expression a{2,} matches aaaaa.

{m,n}?

Matches at least m but not more than n occurrences of the preceding subexpression (nongreedyFootref 1). {0,n}? matches the empty string whenever possible.

The expression a{2,4}? matches aa in the string aaaaa (and the greedy expression a{2,4} matches aaaa.


Footnote 1 A nongreedy operator matches as few occurrences as possible while allowing the rest of the match to succeed. To make the operator greedy, omit the nongreedy modifier (?).

Using Regular Expressions in SQL Statements: Scenarios

Scenarios:

Using a Constraint to Enforce a Phone Number Format

Regular expressions are useful for enforcing constraints—for example, to ensure that phone numbers are entered into the database in a standard format. Example 3-1 creates a contacts table and adds a CHECK constraint to the p_number column to enforce this format model:

(XXX) XXX-XXXX

Example 3-1 Enforcing a Phone Number Format with Regular Expressions

DROP TABLE contacts;
CREATE TABLE contacts (
  l_name    VARCHAR2(30),
  p_number  VARCHAR2(30)
  CONSTRAINT c_contacts_pnf
  CHECK (REGEXP_LIKE (p_number, '^\(\d{3}\) \d{3}-\d{4}$'))
);

Table 3-6 explains the elements of the regular expression.

Table 3-6 Explanation of the Regular Expression Elements in Example 3-1

Regular Expression ElementMatches . . .

^

The beginning of the string.

\(

A left parenthesis. The backslash (\) is an escape character that indicates that the left parenthesis after it is a literal rather than a subexpression delimiter.

\d{3}

Exactly three digits.

\)

A right parenthesis. The backslash (\) is an escape character that indicates that the right parenthesis after it is a literal rather than a subexpression delimiter.

space character

A space character.

\d{3}

Exactly three digits.

-

A hyphen.

\d{4}

Exactly four digits.

$


The end of the string.


Example 3-2 shows some statements that correctly and incorrectly insert phone numbers into the contacts table.

Example 3-2 Inserting Phone Numbers in Correct and Incorrect Formats

These are correct:

INSERT INTO contacts (p_number) VALUES('(650) 555-0100');
INSERT INTO contacts (p_number) VALUES('(215) 555-0100');
 

These generate CHECK constraint errors:

INSERT INTO contacts (p_number) VALUES('650 555-0100');
INSERT INTO contacts (p_number) VALUES('650 555 0100');
INSERT INTO contacts (p_number) VALUES('650-555-0100');
INSERT INTO contacts (p_number) VALUES('(650)555-0100');
INSERT INTO contacts (p_number) VALUES(' (650) 555-0100');

Using Back References to Reposition Characters

A back reference (described in Table 3-3) stores the referenced subexpression in a temporary buffer. Therefore, you can use back references to reposition characters, as in Example 3-3. For an explanation of the elements of the regular expression in Example 3-3, see Table 3-7.

Example 3-3 Using Back References to Reposition Characters

Create table and populate it with names in different formats:

DROP TABLE famous_people;
CREATE TABLE famous_people (names VARCHAR2(20));
INSERT INTO famous_people (names) VALUES ('John Quincy Adams');
INSERT INTO famous_people (names) VALUES ('Harry S. Truman');
INSERT INTO famous_people (names) VALUES ('John Adams');
INSERT INTO famous_people (names) VALUES (' John Quincy Adams');
INSERT INTO famous_people (names) VALUES ('John_Quincy_Adams');

SQL*Plus formatting command:

COLUMN "names after regexp" FORMAT A20

For each name in the table whose format is "first middle last", use back references to reposition characters so that the format becomes "last, first middle":

SELECT names "names",
  REGEXP_REPLACE(names, '^(\S+)\s(\S+)\s(\S+)$', '\3, \1 \2')
    AS "names after regexp"
FROM famous_people
ORDER BY "names";
 

Result:

names                names after regexp
-------------------- --------------------
 John Quincy Adams    John Quincy Adams
Harry S. Truman      Truman, Harry S.
John Adams           John Adams
John Quincy Adams    Adams, John Quincy
John_Quincy_Adams    John_Quincy_Adams
 
5 rows selected.

Table 3-7 explains the elements of the regular expression.

Table 3-7 Explanation of the Regular Expression Elements in Example 3-3

Regular Expression ElementDescription

^

Matches the beginning of the string.

$


Matches the end of the string.

(\S+)

Matches one or more nonspace characters. The parentheses are not escaped so they function as a grouping expression.

\s

Matches a whitespace character.

\1

Substitutes the first subexpression, that is, the first group of parentheses in the matching pattern.

\2

Substitutes the second subexpression, that is, the second group of parentheses in the matching pattern.

\3

Substitutes the third subexpression, that is, the third group of parentheses in the matching pattern.

,

Inserts a comma character.


PKPK|%AOEBPS/preface.htm5! Preface

Preface

Oracle Database Advanced Application Developer's Guide explains topics that experienced application developers reference repeatedly. Information in this guide applies to features that work the same on all supported platforms, and does not include system-specific information.

Preface Topics:

Audience

Oracle Database Advanced Application Developer's Guide is intended for application developers who are either developing applications or converting applications to run in the Oracle Database environment. This guide is also valuable to anyone who is interested in the development of database applications, such as systems analysts and project managers.

To use this document effectively, you need a working knowledge of:

Documentation Accessibility

For information about Oracle's commitment to accessibility, visit the Oracle Accessibility Program website at http://www.oracle.com/pls/topic/lookup?ctx=acc&id=docacc.

Access to Oracle Support

Oracle customers have access to electronic support through My Oracle Support. For information, visit http://www.oracle.com/pls/topic/lookup?ctx=acc&id=info or visit http://www.oracle.com/pls/topic/lookup?ctx=acc&id=trs if you are hearing impaired.

Related Documents

For more information, see these documents in the Oracle Database 11g Release 2 documentation set:

See also:

Conventions

This document uses these text conventions:

ConventionMeaning
boldfaceBoldface type indicates graphical user interface elements associated with an action, or terms defined in text or the glossary.
italicItalic type indicates book titles, emphasis, or placeholder variables for which you supply particular values.
monospaceMonospace type indicates commands within a paragraph, URLs, code in examples, text that appears on the screen, or text that you enter.

Also:

PKda:!5!PK|%AOEBPS/adfns_packages.htm Coding PL/SQL Subprograms and Packages

6 Coding PL/SQL Subprograms and Packages

This chapter describes some procedural capabilities of Oracle Database for application development.

Topics:


See Also:


Overview of PL/SQL Units

PL/SQL is a modern, block-structured programming language. It provides several features that make developing powerful database applications very convenient. For example, PL/SQL provides procedural constructs, such as loops and conditional statements, that are not available in standard SQL.

You can directly enter SQL data manipulation language (DML) statements inside PL/SQL blocks, and you can use subprograms supplied by Oracle to perform data definition language (DDL) statements.

PL/SQL code runs on the server, so using PL/SQL lets you centralize significant parts of your database applications for increased maintainability and security. It also enables you to achieve a significant reduction of network overhead in client/server applications.


Note:

Some Oracle tools, such as Oracle Forms, contain a PL/SQL engine that lets you run PL/SQL locally.

You can even use PL/SQL for some database applications instead of 3GL programs that use embedded SQL or Oracle Call Interface (OCI).

PL/SQL units include:

Anonymous Blocks

An anonymous block is a PL/SQL unit that has no name. An anonymous block consists of an optional declarative part, an executable part, and one or more optional exception handlers.

The declarative part declares PL/SQL variables, exceptions, and cursors. The executable part contains PL/SQL code and SQL statements, and can contain nested blocks.

Exception handlers contain code that is invoked when the exception is raised, either as a predefined PL/SQL exception (such as NO_DATA_FOUND or ZERO_DIVIDE) or as an exception that you define.

Anonymous blocks are usually used interactively from a tool, such as SQL*Plus, or in a precompiler, OCI, or SQL*Module application. They are usually used to invoke stored subprograms or to open cursor variables.

The anonymous block in Example 6-1 uses the DBMS_OUTPUT package to print the names of all employees in the HR.EMPLOYEES table who are in department 20.

Example 6-1 Anonymous Block

DECLARE
  last_name  VARCHAR2(10);
  cursor     c1 IS
               SELECT LAST_NAME
               FROM EMPLOYEES
               WHERE DEPARTMENT_ID = 20
               ORDER BY LAST_NAME;
BEGIN
  OPEN c1;
  LOOP
    FETCH c1 INTO last_name;
    EXIT WHEN c1%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE(last_name);
  END LOOP;
END;
/

Result:

Fay
Hartstein

Exceptions let you handle Oracle Database error conditions with PL/SQL program logic, enabling your application to prevent the server from issuing an error that can cause the client application to end. The anonymous block in Example 6-2 handles the predefined Oracle Database exception NO_DATA_FOUND (which results in ORA-01403 if not handled).

Example 6-2 Anonymous Block with Exception Handler for Predefined Error

DECLARE
  Emp_number  INTEGER := 9999
  Emp_name    VARCHAR2(10);
BEGIN
  SELECT LAST_NAME INTO Emp_name
    FROM EMPLOYEES
      WHERE EMPLOYEE_ID = Emp_number;
  DBMS_OUTPUT.PUT_LINE('Employee name is ' || Emp_name);
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    DBMS_OUTPUT.PUT_LINE('No such employee: ' || Emp_number);
END;
/

Result:

No such employee: 9999

You can also define your own exceptions; that is, you can declare them in the declaration part of a block and define them in the exception part of the block, as in Example 6-3.

Example 6-3 Anonymous Block with Exception Handler for User-Defined Exception

DECLARE
  Emp_name            VARCHAR2(10);
  Emp_number          INTEGER;
  Empno_out_of_range  EXCEPTION;
BEGIN
  Emp_number := 10001;
  IF Emp_number > 9999 OR Emp_number < 1000 THEN
    RAISE Empno_out_of_range;
  ELSE
    SELECT LAST_NAME INTO Emp_name
    FROM EMPLOYEES
    WHERE EMPLOYEE_ID = Emp_number;
    DBMS_OUTPUT.PUT_LINE('Employee name is ' || Emp_name);
 END IF;
EXCEPTION
  WHEN Empno_out_of_range THEN
    DBMS_OUTPUT.PUT_LINE('Employee number ' || Emp_number || 
      ' is out of range.');
END;
/

Result:

Employee number 10001 is out of range.

Stored PL/SQL Units

A stored PL/SQL unit is a subprogram (procedure or function) or package that:

  • Has a name.

  • Can take parameters, and can return values.

  • Is stored in the data dictionary.

  • Can be invoked by many users.

If a subprogram belongs to a package, it is called a package subprogram; if not, it is called a standalone subprogram.

Topics:

Naming Subprograms

Because a subprogram is stored in the database, it must be named. This distinguishes it from other stored subprograms and makes it possible for applications to invoke it. Each publicly-visible subprogram in a schema must have a unique name, and the name must be a legal PL/SQL identifier.


Note:

If you plan to invoke a stored subprogram using a stub generated by SQL*Module, then the stored subprogram name must also be a legal identifier in the invoking host 3GL language, such as Ada or C.

Subprogram Parameters

Stored subprograms can take parameters. In the procedure in Example 6-4, the department number is an input parameter that is used when the parameterized cursor c1 is opened.

Example 6-4 Stored Procedure with Parameters

CREATE OR REPLACE PROCEDURE get_emp_names (
  dept_num IN NUMBER
)
IS
  emp_name  VARCHAR2(10);
  CURSOR    c1 (dept_num NUMBER) IS
                SELECT LAST_NAME FROM EMPLOYEES
                WHERE DEPARTMENT_ID = dept_num;
BEGIN
  OPEN c1(dept_num);
  LOOP
    FETCH c1 INTO emp_name;
    EXIT WHEN C1%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE(emp_name);
  END LOOP;
  CLOSE c1;
END;
/

The formal parameters of a subprogram have three major attributes, described in Table 6-1.

Table 6-1 Attributes of Subprogram Parameters

Parameter AttributeDescription

Name

This must be a legal PL/SQL identifier.

Mode

This indicates whether the parameter is an input-only parameter (IN), an output-only parameter (OUT), or is both an input and an output parameter (IN OUT). If the mode is not specified, then IN is assumed.

Data Type

This is a standard PL/SQL data type.


Topics:

Parameter Modes

Parameter modes define the action of formal parameters. You can use the three parameter modes, IN (the default), OUT, and IN OUT, with any subprogram. Avoid using the OUT and IN OUT modes with functions. Good programming practice dictates that a function returns a single value and does not change the values of variables that are not local to the subprogram.


See Also:

Oracle Database PL/SQL Language Reference for details about parameter modes

Parameter Data Types

The data type of a formal parameter consists of one of these:

  • An unconstrained type name, such as NUMBER or VARCHAR2.

  • A type that is constrained using the %TYPE or %ROWTYPE attributes.


    Note:

    Numerically constrained types such as NUMBER(2) or VARCHAR2(20) are not allowed in a parameter list.

%TYPE and %ROWTYPE Attributes

Use the type attributes %TYPE and %ROWTYPE to constrain the parameter. For example, the procedure heading in Example 6-4 can be written as follows:

PROCEDURE get_emp_names(dept_num IN EMPLOYEES.DEPARTMENT_ID%TYPE)

This gives the dept_num parameter the same data type as the DEPARTMENT_ID column in the EMPLOYEES table. The column and table must be available when a declaration using %TYPE (or %ROWTYPE) is elaborated.

Using %TYPE is recommended, because if the type of the column in the table changes, it is not necessary to change the application code.

If the get_emp_names procedure is part of a package, you can use previously-declared public (package) variables to constrain its parameter data types. For example:

dept_number  NUMBER(2);
...
PROCEDURE get_emp_names(dept_num IN dept_number%TYPE);

Use the %ROWTYPE attribute to create a record that contains all the columns of the specified table. The procedure in Example 6-5 returns all the columns of the EMPLOYEES table in a PL/SQL record for the given employee ID.

Example 6-5 %TYPE and %ROWTYPE Attributes

CREATE OR REPLACE PROCEDURE get_emp_rec (
  emp_number  IN  EMPLOYEES.EMPLOYEE_ID%TYPE,
  emp_info    OUT EMPLOYEES%ROWTYPE
)
IS
BEGIN
  SELECT * INTO emp_info
  FROM EMPLOYEES
  WHERE EMPLOYEE_ID = emp_number;
END;
/
 

Invoke procedure from PL/SQL block:

DECLARE
  emp_row  EMPLOYEES%ROWTYPE;
BEGIN
  get_emp_rec(206, emp_row);
  DBMS_OUTPUT.PUT('EMPLOYEE_ID: ' || emp_row.EMPLOYEE_ID);
  DBMS_OUTPUT.NEW_LINE;
  DBMS_OUTPUT.PUT('FIRST_NAME: ' || emp_row.FIRST_NAME);
  DBMS_OUTPUT.NEW_LINE;
  DBMS_OUTPUT.PUT('LAST_NAME: ' || emp_row.LAST_NAME);
  DBMS_OUTPUT.NEW_LINE;
  DBMS_OUTPUT.PUT('EMAIL: ' || emp_row.EMAIL);
  DBMS_OUTPUT.NEW_LINE;
  DBMS_OUTPUT.PUT('PHONE_NUMBER: ' || emp_row.PHONE_NUMBER);
  DBMS_OUTPUT.NEW_LINE;
  DBMS_OUTPUT.PUT('HIRE_DATE: ' || emp_row.HIRE_DATE);
  DBMS_OUTPUT.NEW_LINE;
  DBMS_OUTPUT.PUT('JOB_ID: ' || emp_row.JOB_ID);
  DBMS_OUTPUT.NEW_LINE;
  DBMS_OUTPUT.PUT('SALARY: ' || emp_row.SALARY);
  DBMS_OUTPUT.NEW_LINE;
  DBMS_OUTPUT.PUT('COMMISSION_PCT: ' || emp_row.COMMISSION_PCT);
  DBMS_OUTPUT.NEW_LINE;
  DBMS_OUTPUT.PUT('MANAGER_ID: ' || emp_row.MANAGER_ID);
  DBMS_OUTPUT.NEW_LINE;
  DBMS_OUTPUT.PUT('DEPARTMENT_ID: ' || emp_row.DEPARTMENT_ID);
  DBMS_OUTPUT.NEW_LINE;
END;
/

Result:

EMPLOYEE_ID: 206
FIRST_NAME: William
LAST_NAME: Gietz
EMAIL: WGIETZ
PHONE_NUMBER: 515.123.8181
HIRE_DATE: 07-JUN-02
JOB_ID: AC_ACCOUNT
SALARY: 8300
COMMISSION_PCT:
MANAGER_ID: 205
DEPARTMENT_ID: 110

Stored functions can return values that are declared using %ROWTYPE. For example:

FUNCTION get_emp_rec (dept_num IN EMPLOYEES.DEPARTMENT_ID%TYPE)
   RETURN EMPLOYEES%ROWTYPE IS ...
Passing Composite Variables as Parameters

You can pass PL/SQL composite variables (collections and records) as parameters to stored subprograms.

If the subprogram is remote, you must create a redundant loop-back DBLINK, so that when the remote subprogram compiles, the type checker that verifies the source uses the same definition of the user-defined composite variable type as the invoker uses.

Initial Parameter Values

Parameters can take initial values. Use either the assignment operator or the DEFAULT keyword to give a parameter an initial value. For example, these are equivalent:

PROCEDURE Get_emp_names (Dept_num IN NUMBER := 20) IS ...
PROCEDURE Get_emp_names (Dept_num IN NUMBER DEFAULT) IS ...

When a parameter takes an initial value, it can be omitted from the actual parameter list when you invoke the subprogram. When you do specify the parameter value on the invocation, it overrides the initial value.


Note:

Unlike in an anonymous PL/SQL block, you do not use the keyword DECLARE before the declarations of variables, cursors, and exceptions in a stored subprogram. In fact, it is an error to use it.

Creating Subprograms

Use a text editor to write the subprogram. Then, using an interactive tool such as SQL*Plus, load the text file containing the procedure by entering:

@get_emp

This loads the procedure into the current schema from the get_emp.sql file (.sql is the default file extension). The slash (/) after the code is not part of the code, it only activates the loading of the procedure.


Caution:

When developing a subprogram, it is usually preferable to use the statement CREATE OR REPLACE PROCEDURE or CREATE OR REPLACE FUNCTION. This statement replaces any previous version of that subprogram in the same schema with the newer version, but without warning.

You can use either the keyword IS or AS after the subprogram parameter list.


See Also:


Privileges Needed

To create a subprogram, a package specification, or a package body, you must meet these prerequisites:

  • You must have the CREATE PROCEDURE system privilege to create a subprogram or package in your schema, or the CREATE ANY PROCEDURE system privilege to create a subprogram or package in another user's schema. In either case, the package body must be created in the same schema as the package.


    Note:

    To create without errors (to compile the subprogram or package successfully) requires these additional privileges:
    • The owner of the subprogram or package must be explicitly granted the necessary object privileges for all objects referenced within the body of the code.

    • The owner cannot obtain required privileges through roles.


If the privileges of the owner of a subprogram or package change, then the subprogram must be reauthenticated before it is run. If a necessary privilege to a referenced object is revoked from the owner of the subprogram or package, then the subprogram cannot be run.

The EXECUTE privilege on a subprogram gives a user the right to run a subprogram owned by another user. Privileged users run the subprogram under the security domain of the owner of the subprogram. Therefore, users need not be granted the privileges to the objects referenced by a subprogram. This allows for more disciplined and efficient security strategies with database applications and their users. Furthermore, all subprograms and packages are stored in the data dictionary (in the SYSTEM tablespace). No quota controls the amount of space available to a user who creates subprograms and packages.


Note:

Package creation requires a sort. The user creating the package must be able to create a sort segment in the temporary tablespace with which the user is associated.

Altering Subprograms

To alter a subprogram, you must first drop it using the DROP PROCEDURE or DROP FUNCTION statement, then re-create it using the CREATE PROCEDURE or CREATE FUNCTION statement. Alternatively, use the CREATE OR REPLACE PROCEDURE or CREATE OR REPLACE FUNCTION statement, which first drops the subprogram if it exists, then re-creates it as specified.


Caution:

The subprogram is dropped without warning.

Dropping Subprograms and Packages

A standalone subprogram, a standalone function, a package body, or an entire package can be dropped using the SQL statements DROP PROCEDURE, DROP FUNCTION, DROP PACKAGE BODY, and DROP PACKAGE, respectively. A DROP PACKAGE statement drops both the specification and body of a package.

This statement drops the Old_sal_raise procedure in your schema:

DROP PROCEDURE Old_sal_raise;

Privileges Needed

To drop a subprogram or package, the subprogram or package must be in your schema, or you must have the DROP ANY PROCEDURE privilege. An individual subprogram within a package cannot be dropped; the containing package specification and body must be re-created without the subprograms to be dropped.

External Subprograms

A PL/SQL subprogram running on an Oracle Database instance can invoke an external subprogram written in a third-generation language (3GL). The 3GL subprogram runs in a separate address space from that of the database.


See Also:

Chapter 14, "Developing Applications with Multiple Programming Languages," for information about external subprograms

PL/SQL Function Result Cache

Using the PL/SQL function result cache can save significant space and time. Each time a result-cached PL/SQL function is invoked with different parameter values, those parameters and their result are stored in the cache. Subsequently, when the same function is invoked with the same parameter values, the result is retrieved from the cache, instead of being recomputed. Because the cache is stored in a shared global area (SGA), it is available to any session that runs your application.

If a database object that was used to compute a cached result is updated, the cached result becomes invalid and must be recomputed.

The best candidates for result-caching are functions that are invoked frequently but depend on information that changes infrequently or never.

For more information about the PL/SQL function result cache, see Oracle Database PL/SQL Language Reference.

PL/SQL Packages

A package is a collection of related program objects (for example, subprogram, variables, constants, cursors, and exceptions) stored as a unit in the database.

Using packages is an alternative to creating subprograms as standalone schema objects. Packages have many advantages over standalone subprograms. For example, they:

  • Let you organize your application development more efficiently.

  • Let you grant privileges more efficiently.

  • Let you modify package objects without recompiling dependent schema objects.

  • Enable Oracle Database to read multiple package objects into memory at once.

  • Can contain global variables and cursors that are available to all subprograms in the package.

  • Let you overload subprograms. Overloading a subprogram means creating multiple subprograms with the same name in the same package, each taking arguments of different number or data type.


    See Also:

    Oracle Database PL/SQL Language Reference for more information about subprogram name overloading

The specification part of a package declares the public types, variables, constants, and subprograms that are visible outside the immediate scope of the package. The body of a package defines both the objects declared in the specification and private objects that are not visible to applications outside the package.

Example 6-6 creates a package that contains one stored function and two stored procedures, and then invokes a procedure.

Example 6-6 Creating PL/SQL Package and Invoking Package Subprogram

-- Sequence that package function needs:
 
CREATE SEQUENCE emp_sequence
START WITH 8000
INCREMENT BY 10;
 
-- Package specification:
 
CREATE or REPLACE PACKAGE employee_management IS
  FUNCTION hire_emp (
  firstname  VARCHAR2,
  lastname   VARCHAR2,
  email      VARCHAR2,
  phone      VARCHAR2,
  hiredate   DATE,
  job        VARCHAR2,
  sal        NUMBER,
  comm       NUMBER,
  mgr        NUMBER,
  deptno     NUMBER
) RETURN NUMBER;
 
 PROCEDURE fire_emp(
    emp_id IN NUMBER
 );
 
 PROCEDURE sal_raise (
    emp_id IN NUMBER,
    sal_incr IN NUMBER
 );
END employee_management;
/
 
-- Package body:
 
CREATE or REPLACE PACKAGE BODY employee_management IS
  FUNCTION hire_emp (
    firstname  VARCHAR2,
    lastname   VARCHAR2,
    email      VARCHAR2,
    phone      VARCHAR2,
    hiredate   DATE,
    job        VARCHAR2,
    sal        NUMBER,
    comm       NUMBER,
    mgr        NUMBER,
    deptno     NUMBER
  ) RETURN NUMBER
 IS
   new_empno  NUMBER(10);
 BEGIN
   new_empno := emp_sequence.NEXTVAL;
 
    INSERT INTO EMPLOYEES (
      employee_id,
      first_name,
      last_name,
      email,
      phone_number,
      hire_date,
      job_id,
      salary,
      commission_pct,
      manager_id,
      department_id
    )
    VALUES (
      new_empno,
      firstname,
      lastname,
      email,
      phone,
      hiredate,  
      job,
      sal, 
      comm,
      mgr,
      deptno
    );

   RETURN (new_empno);
 END hire_emp;
 
 PROCEDURE fire_emp (
   emp_id IN NUMBER
 ) IS
 BEGIN
   DELETE FROM EMPLOYEES
   WHERE EMPLOYEE_ID = emp_id;
 
   IF SQL%NOTFOUND THEN
     raise_application_error(
       -20011,
       'Invalid Employee Number: ' || TO_CHAR(Emp_id)
     );
   END IF;
 END fire_emp;
 
 PROCEDURE sal_raise (
    emp_id IN NUMBER,
    sal_incr IN NUMBER
  ) IS
  BEGIN
    UPDATE EMPLOYEES
    SET SALARY = SALARY + sal_incr
    WHERE EMPLOYEE_ID = emp_id;
 
    IF SQL%NOTFOUND THEN
      raise_application_error(
        -20011,
        'Invalid Employee Number: ' || TO_CHAR(Emp_id)
      );
    END IF;
  END sal_raise;
END employee_management;
/

Invoke package procedures:

DECLARE
  empno  NUMBER(6);
  sal    NUMBER(6);
  temp   NUMBER(6);
BEGIN
  empno := employee_management.hire_emp(
            'John',
            'Doe',
            'john.doe@company.com',
            '555-0100',
            '20-SEP-07',
            'ST_CLERK',
            2500,
            0,
            100,
            20);
 
  DBMS_OUTPUT.PUT_LINE('New employee ID is ' || TO_CHAR(empno));
END;
/

PL/SQL Object Size Limits

The size limit for PL/SQL stored database objects such as subprograms, triggers, and packages is the size of the Descriptive Intermediate Attributed Notation for Ada (DIANA) code in the shared pool in bytes. The Linux and UNIX limit on the size of the flattened DIANA/code size is 64K but the limit might be 32K on desktop platforms.

The most closely related number that a user can access is the PARSED_SIZE in the static data dictionary view *_OBJECT_SIZE. That gives the size of the DIANA in bytes as stored in the SYS.IDL_xxx$ tables. This is not the size in the shared pool. The size of the DIANA part of PL/SQL code (used during compilation) is significantly larger in the shared pool than it is in the system table.

Creating Packages

Each part of a package is created with a different statement. Create the package specification using the CREATE PACKAGE statement. The CREATE PACKAGE statement declares public package objects.

To create a package body, use the CREATE PACKAGE BODY statement. The CREATE PACKAGE BODY statement defines the procedural code of the public subprograms declared in the package specification.

You can also define private, or local, package subprograms, and variables in a package body. These objects can only be accessed by other subprograms in the body of the same package. They are not visible to external users, regardless of the privileges they hold.

It is often more convenient to add the OR REPLACE clause in the CREATE PACKAGE or CREATE PACKAGE BODY statements when you are first developing your application. The effect of this option is to drop the package or the package body without warning. The CREATE statements are:

CREATE OR REPLACE PACKAGE Package_name AS ...

and

CREATE OR REPLACE PACKAGE BODY Package_name AS ...
Creating Package Objects

The body of a package can contain:

  • Subprograms declared in the package specification.

  • Definitions of cursors declared in the package specification.

  • Local subprograms, not declared in the package specification.

  • Local variables.

Subprograms, cursors, and variables that are declared in the package specification are global. They can be invoked, or used, by external users that have EXECUTE permission for the package or that have EXECUTE ANY PROCEDURE privileges.

When you create the package body, ensure that each subprogram that you define in the body has the same parameters, by name, data type, and mode, as the declaration in the package specification. For functions in the package body, the parameters and the return type must agree in name and type.

Privileges to Needed to Create or Drop Packages

The privileges required to create or drop a package specification or package body are the same as those required to create or drop a standalone subprogram. See "Creating Subprograms" and "Dropping Subprograms and Packages".

Naming Packages and Package Objects

The names of a package and all public objects in the package must be unique within a given schema. The package specification and its body must have the same name. All package constructs must have unique names within the scope of the package, unless overloading of subprogram names is desired.

Package Invalidations and Session State

Each session that references a package object has its own instance of the corresponding package, including persistent state for any public and private variables, cursors, and constants. If any of the session's instantiated packages (specification or body) are invalidated, then all package instances in the session are invalidated and recompiled. Therefore, the session state is lost for all package instances in the session.

When a package in a given session is invalidated, the session receives ORA-04068 the first time it attempts to use any object of the invalid package instance. The second time a session makes such a package call, the package is reinstantiated for the session without error. However, if you handle this error in your application, be aware of the following:

  • For optimal performance, Oracle Database returns this error message only when the package state is discarded. When a subprogram in one package invokes a subprogram in another package, the session state is lost for both packages.

  • If a server session traps ORA-04068, then ORA-04068 is not raised for the client session. Therefore, when the client session attempts to use an object in the package, the package is not reinstantiated. To reinstantiate the package, the client session must either reconnect to the database or recompile the package.

In Example 6-7, the RAISE statement raises the current exception, ORA-04068, which is the cause of the exception being handled, ORA-06508. ORA-04068 is not trapped.

Example 6-7 Raising ORA-04068

PROCEDURE p IS
  package_exception EXCEPTION;
  PRAGMA EXCEPTION_INIT (package_exception, -6508);
BEGIN
 ...
EXCEPTION
  WHEN package_exception THEN
    RAISE;
END;
/

In Example 6-8, the RAISE statement raises the exception ORA-20001 in response to ORA-06508, instead of the current exception, ORA-04068. ORA-04068 is trapped. When this happens, the ORA-04068 error is masked, which stops the package from being reinstantiated.

Example 6-8 Trapping ORA-04068

PROCEDURE p IS
  package_exception EXCEPTION;
  other_exception   EXCEPTION;
  PRAGMA EXCEPTION_INIT (package_exception, -6508);
  PRAGMA EXCEPTION_INIT (other_exception, -20001);
BEGIN
 ...
EXCEPTION
  WHEN package_exception THEN
    ...
    RAISE other_exception;
END;
/

In most production environments, DDL operations that can cause invalidations are usually performed during inactive working hours; therefore, this situation might not be a problem for end-user applications. However, if package invalidations are common in your system during working hours, then you might want to code your applications to handle this error when package calls are made.

Packages Supplied with Oracle Database

There are many packages provided with Oracle Database, either to extend the functionality of the database or to give PL/SQL access to SQL features. You can invoke these packages from your application.


See Also:

Oracle Database PL/SQL Packages and Types Reference for an overview of these Oracle Database packages

Overview of Bulk Binding

Oracle Database uses two engines to run PL/SQL blocks and subprograms. The PL/SQL engine runs procedural statements, while the SQL engine runs SQL statements. During execution, every SQL statement causes a context switch between the two engines, resulting in performance overhead.

Performance can be improved substantially by minimizing the number of context switches required to run a particular block or subprogram. When a SQL statement runs inside a loop that uses collection elements as bind variables, the large number of context switches required by the block can cause poor performance. Collections include:

  • Varrays

  • Nested tables

  • Index-by tables

  • Host arrays

Binding is the assignment of values to PL/SQL variables in SQL statements. Bulk binding is binding an entire collection at once. Bulk binds pass the entire collection back and forth between the two engines in a single operation.

Typically, using bulk binds improves performance for SQL statements that affect four or more database rows. The more rows affected by a SQL statement, the greater the performance gain from bulk binds.


Note:

This section provides an overview of bulk binds to help you decide whether to use them in your PL/SQL applications. For detailed information about using bulk binds, including ways to handle exceptions that occur in the middle of a bulk bind operation, see Oracle Database PL/SQL Language Reference.

Parallel DML statements are disabled with bulk binds.


When to Use Bulk Binds

Consider using bulk binds to improve the performance of:

DML Statements that Reference Collections

A bulk bind, which uses the FORALL keyword, can improve the performance of INSERT, UPDATE, or DELETE statements that reference collection elements.

The PL/SQL block in Example 6-9 increases the salary for employees whose manager's ID number is 7902, 7698, or 7839, with and without bulk binds. Without bulk bind, PL/SQL sends a SQL statement to the SQL engine for each updated employee, leading to context switches that slow performance.

Example 6-9 DML Statements that Reference Collections

DECLARE
  TYPE numlist IS VARRAY (100) OF NUMBER;
  id NUMLIST := NUMLIST(7902, 7698, 7839);
BEGIN
  -- Efficient method, using bulk bind:
  
  FORALL i IN id.FIRST..id.LAST
  UPDATE EMPLOYEES
  SET SALARY = 1.1 * SALARY
  WHERE MANAGER_ID = id(i);
 
 -- Slower method:
 
 FOR i IN id.FIRST..id.LAST LOOP
    UPDATE EMPLOYEES
    SET SALARY = 1.1 * SALARY
    WHERE MANAGER_ID = id(i);
 END LOOP;
END;
/
SELECT Statements that Reference Collections

The BULK COLLECT INTO clause can improve the performance of queries that reference collections. You can use BULK COLLECT INTO with tables of scalar values, or tables of %TYPE values.

The PL/SQL block in Example 6-10 queries multiple values into PL/SQL tables, with and without bulk binds. Without bulk bind, PL/SQL sends a SQL statement to the SQL engine for each selected employee, leading to context switches that slow performance.

Example 6-10 SELECT Statements that Reference Collections

DECLARE
  TYPE var_tab IS TABLE OF VARCHAR2(20)
  INDEX BY PLS_INTEGER;
  
  empno    VAR_TAB;
  ename    VAR_TAB;
  counter  NUMBER;
  
  CURSOR c IS
    SELECT EMPLOYEE_ID, LAST_NAME
    FROM EMPLOYEES
    WHERE MANAGER_ID = 7698;
BEGIN
 -- Efficient method, using bulk bind:
 
 SELECT EMPLOYEE_ID, LAST_NAME BULK COLLECT
 INTO empno, ename
 FROM EMPLOYEES
 WHERE MANAGER_ID = 7698;
 
 -- Slower method:
 
 counter := 1;
 
 FOR rec IN c LOOP
    empno(counter) := rec.EMPLOYEE_ID;
    ename(counter) := rec.LAST_NAME;
    counter := counter + 1;
 END LOOP;
END;
/
FOR Loops that Reference Collections and Return DML

You can use the FORALL keyword with the BULK COLLECT INTO keywords to improve the performance of FOR loops that reference collections and return DML.

The PL/SQL block in Example 6-11 updates the EMPLOYEES table by computing bonuses for a collection of employees. Then it returns the bonuses in a column called bonus_list_inst. The actions are performed with and without bulk binds. Without bulk bind, PL/SQL sends a SQL statement to the SQL engine for each updated employee, leading to context switches that slow performance.

Example 6-11 FOR Loops that Reference Collections and Return DML

DECLARE
  TYPE emp_list IS VARRAY(100) OF EMPLOYEES.EMPLOYEE_ID%TYPE;
  empids emp_list := emp_list(182, 187, 193, 200, 204, 206);
  
  TYPE bonus_list IS TABLE OF EMPLOYEES.SALARY%TYPE;
  bonus_list_inst  bonus_list;
  
BEGIN
  -- Efficient method, using bulk bind:
 
 FORALL i IN empids.FIRST..empids.LAST
 UPDATE EMPLOYEES
 SET SALARY = 0.1 * SALARY
 WHERE EMPLOYEE_ID = empids(i)
 RETURNING SALARY BULK COLLECT INTO bonus_list_inst;
 
 -- Slower method:
 
 FOR i IN empids.FIRST..empids.LAST LOOP
   UPDATE EMPLOYEES
   SET SALARY = 0.1 * SALARY
   WHERE EMPLOYEE_ID = empids(i)
   RETURNING SALARY INTO bonus_list_inst(i);
 END LOOP;
END;
/

Triggers

A trigger is a special kind of PL/SQL anonymous block. You can define triggers to fire before or after SQL statements, either on a statement level or for each row that is affected. You can also define INSTEAD OF triggers or system triggers (triggers on DATABASE and SCHEMA).


See Also:

Oracle Database PL/SQL Language Reference for more information about triggers

Compiling PL/SQL Subprograms for Native Execution

You can speed up PL/SQL subprograms by compiling them into native code residing in shared libraries.

You can use native compilation with both the supplied packages and the subprograms you write yourself. Subprograms compiled this way work in all server environments, such as the shared server configuration (formerly known as multithreaded server) and Oracle Real Application Clusters (Oracle RAC).

This technique is most effective for computation-intensive subprograms that do not spend much time running SQL, because it can do little to speed up SQL statements invoked from these subprograms.

With Java, you can use the ncomp tool to compile your own packages and classes.


See Also:


Cursor Variables

A cursor is a static object; a cursor variable is a pointer to a cursor. Because cursor variables are pointers, they can be passed and returned as parameters to subprograms. A cursor variable can also refer to different cursors in its lifetime.

Additional advantages of cursor variables include:

  • Encapsulation

    Queries are centralized in the stored subprogram that opens the cursor variable.

  • Easy maintenance

    If you must change the cursor, then you only make the change in the stored subprogram, not in each application.

  • Convenient security

    The user of the application is the user name used when the application connects to the server. The user must have EXECUTE permission on the stored subprogram that opens the cursor. But, the user need not have READ permission on the tables used in the query. Use this capability to limit access to the columns in the table and access to other stored subprograms.


See Also:

Oracle Database PL/SQL Language Reference for more information about cursor variables

Topics:

Declaring and Opening Cursor Variables

Memory is usually allocated for a cursor variable in the client application using the appropriate ALLOCATE statement. In Pro*C, use the EXEC SQL ALLOCATE cursor_name statement. In OCI, use the Cursor Data Area.

You can also use cursor variables in applications that run entirely in a single server session. You can declare cursor variables in PL/SQL subprograms, open them, and use them as parameters for other PL/SQL subprograms.

Examples of Cursor Variables

This section has these examples of cursor variable usage in PL/SQL:


See Also:

For additional cursor variable examples that use programmatic interfaces:

Example 6-12 creates a package that defines a PL/SQL cursor variable type and two procedures, and then invokes the procedures from a PL/SQL block. The first procedure opens a cursor variable using a bind variable in the WHERE clause. The second procedure uses a cursor variable to fetch rows from the EMPLOYEES table.

Example 6-12 Fetching Data with Cursor Variable

CREATE OR REPLACE PACKAGE emp_data AS
  TYPE emp_val_cv_type IS REF CURSOR
  RETURN EMPLOYEES%ROWTYPE;
  
  PROCEDURE open_emp_cv (
    emp_cv       IN OUT emp_val_cv_type,
    dept_number  IN     EMPLOYEES.DEPARTMENT_ID%TYPE
  );
 
 PROCEDURE fetch_emp_data (
   emp_cv   IN  emp_val_cv_type,
   emp_row  OUT EMPLOYEES%ROWTYPE
 );
END emp_data;
/
CREATE OR REPLACE PACKAGE BODY emp_data AS
  PROCEDURE open_emp_cv (
    emp_cv       IN OUT emp_val_cv_type,
    dept_number  IN     EMPLOYEES.DEPARTMENT_ID%TYPE
  )
  IS
  BEGIN
    OPEN emp_cv FOR
    SELECT * FROM EMPLOYEES
    WHERE DEPARTMENT_ID = dept_number
    ORDER BY last_name;
  END open_emp_cv;
  
  PROCEDURE fetch_emp_data (
    emp_cv   IN  emp_val_cv_type,
    emp_row  OUT EMPLOYEES%ROWTYPE
  )
  IS
  BEGIN
    FETCH emp_cv INTO emp_row;
  END fetch_emp_data;
END emp_data;
/
 

Invoke package procedures:

DECLARE
  emp_curs     emp_data.emp_val_cv_type;
  dept_number  EMPLOYEES.DEPARTMENT_ID%TYPE;
  emp_row      EMPLOYEES%ROWTYPE;
  
BEGIN
  dept_number := 20;
  
  -- Open cursor, using variable:
 
 emp_data.open_emp_cv(emp_curs, dept_number);
 
 -- Fetch and display data:
 
 LOOP
   emp_data.fetch_emp_data(emp_curs, emp_row);
   EXIT WHEN emp_curs%NOTFOUND;
   DBMS_OUTPUT.PUT(emp_row.LAST_NAME || '  ');
   DBMS_OUTPUT.PUT_LINE(emp_row.SALARY);
 END LOOP;
END;
/

In Example 6-13, the procedure opens a cursor variable for either the EMPLOYEES table or the DEPARTMENTS table, depending on the value of the parameter discrim. The anonymous block invokes the procedure to open the cursor variable for the EMPLOYEES table, but fetches from the DEPARTMENTS table, which raises the predefined exception ROWTYPE_MISMATCH.

Example 6-13 Cursor Variable with Discriminator

CREATE OR REPLACE PACKAGE emp_dept_data AS
  TYPE cv_type IS REF CURSOR;
  
  PROCEDURE open_cv (
    cv       IN OUT cv_type,
    discrim  IN     POSITIVE
  );
  END emp_dept_data;
/
 
CREATE OR REPLACE PACKAGE BODY emp_dept_data AS
  PROCEDURE open_cv (
    cv      IN OUT cv_type,
    discrim IN     POSITIVE) IS
  BEGIN
    IF discrim = 1 THEN
    OPEN cv FOR
      SELECT * FROM EMPLOYEES ORDER BY employee_id;
    ELSIF discrim = 2 THEN
      OPEN cv FOR
        SELECT * FROM DEPARTMENTS ORDER BY department_id;
    END IF;
  END open_cv;
END emp_dept_data;
/

Invoke procedure open_cv from anonymous block:

DECLARE
  emp_rec   EMPLOYEES%ROWTYPE;
  dept_rec  DEPARTMENTS%ROWTYPE;
  cv        Emp_dept_data.CV_TYPE;
BEGIN
  emp_dept_data.open_cv(cv, 1);  -- Open cv for EMPLOYEES fetch.
  FETCH cv INTO dept_rec;        -- Fetch from DEPARTMENTS.
  DBMS_OUTPUT.PUT(dept_rec.DEPARTMENT_ID);
  DBMS_OUTPUT.PUT_LINE('  ' || dept_rec.LOCATION_ID);
EXCEPTION
  WHEN ROWTYPE_MISMATCH THEN
     BEGIN
       DBMS_OUTPUT.PUT_LINE
         ('Row type mismatch, fetching EMPLOYEES data ...');
       FETCH cv INTO emp_rec;
       DBMS_OUTPUT.PUT(emp_rec.DEPARTMENT_ID);
       DBMS_OUTPUT.PUT_LINE('  ' || emp_rec.LAST_NAME);
     END;
END;
/

Result:

Row type mismatch, fetching EMPLOYEES data ...
90  King

Handling PL/SQL Compile-Time Errors

To list compile-time errors, query the static data dictionary view *_ERRORS. From these views, you can retrieve original source code. The error text associated with the compilation of a subprogram is updated when the subprogram is replaced, and it is deleted when the subprogram is dropped.

SQL*Plus issues a warning message for compile-time errors, but for more information about them, you must use the command SHOW ERRORS.


Note:

Before issuing the SHOW ERRORS statement, use the SET LINESIZE statement to get long lines on output. The value 132 is usually a good choice. For example:
SET LINESIZE 132

Example 6-14 has two compile-time errors: WHER should be WHERE, and END should be followed by a semicolon. SHOW ERRORS shows the line, column, and description of each error.

Example 6-14 Compile-Time Errors

CREATE OR REPLACE PROCEDURE fire_emp (
  emp_id NUMBER
) AS
BEGIN
  DELETE FROM EMPLOYEES
  WHER EMPLOYEE_ID = Emp_id;
END
/
 

Result:

Warning: Procedure created with compilation errors.
 

Command:

SHOW ERRORS;

Result:

Errors for PROCEDURE FIRE_EMP:
 
LINE/COL ERROR
-------- -----------------------------------------------------------------
5/3      PL/SQL: SQL Statement ignored
6/8    PL/SQL: ORA-00933: SQL command not properly ended
7/3    PLS-00103: Encountered the symbol "end-of-file" when expecting
         one of the following:
         ; <an identifier> <a double-quoted delimited-identifier>
         current delete exists prior <a single-quoted SQL string>
         The symbol ";" was substituted for "end-of-file" to continue.

See Also:


Handling Runtime PL/SQL Errors

Oracle Database allows user-defined errors in PL/SQL code to be handled so that user-specified error numbers and messages are returned to the client application, which can handle the error.

User-specified error messages are returned using the RAISE_APPLICATION_ERROR procedure. For example:

RAISE_APPLICATION_ERROR(error_number, 'text', keep_error_stack)

This procedure stops subprogram execution, rolls back any effects of the subprogram, and returns a user-specified error number and message (unless the error is trapped by an exception handler). error_number must be in the range of -20000 to -20999.

Use error number -20000 as a generic number for messages where it is important to relay information to the user, but having a unique error number is not required. Text must be a character expression, 2 KB or less (longer messages are ignored). To add the error to errors on the stack, set Keep_error_stack to TRUE; to replace the existing errors, set it to FALSE (the default).


Note:

Some Oracle Database packages, such as DBMS_OUTPUT, DBMS_DESCRIBE, and DBMS_ALERT, use application error numbers in the range -20000 to -20005. See the descriptions of these packages for more information.

The RAISE_APPLICATION_ERROR procedure is often used in exception handlers or in the logic of PL/SQL code. For example, this exception handler selects the string for the associated user-defined error message and invokes the RAISE_APPLICATION_ERROR procedure:

...
WHEN NO_DATA_FOUND THEN
   SELECT Error_string INTO Message
   FROM Error_table,
   V$NLS_PARAMETERS V
   WHERE Error_number = -20101 AND Lang = v.value AND
      v.parameter = "NLS_LANGUAGE";
   Raise_application_error(-20101, Message);
...

Topics:

Declaring Exceptions and Exception Handlers

User-defined exceptions are explicitly defined and raised within the PL/SQL block, to process errors specific to the application. When an exception is raised, the usual execution of the PL/SQL block stops, and an exception handler is invoked. Specific exception handlers can be written to handle any internal or user-defined exception.

Application code can check for a condition that requires special attention using an IF statement. If there is an error condition, then two options are available:

  • Enter a RAISE statement that names the appropriate exception. A RAISE statement stops the execution of the subprogram, and control passes to an exception handler (if any).

  • Invoke the RAISE_APPLICATION_ERROR procedure to return a user-specified error number and message.

You can also define an exception handler to handle user-specified error messages. For example, Figure 6-1 shows:

  • An exception and associated exception handler in a subprogram

  • A conditional statement that checks for an error (such as transferring funds not available) and enters a user-specified error number and message within a trigger

  • How user-specified error numbers are returned to the invoking environment (in this case, a subprogram), and how that application can define an exception that corresponds to the user-specified error number

Declare a user-defined exception in a subprogram or package body (private exceptions), or in the specification of a package (public exceptions). Define an exception handler in the body of a subprogram (standalone or package).

Figure 6-1 Exceptions and User-Defined Errors

Exceptions and User-Defined Errors
Description of "Figure 6-1 Exceptions and User-Defined Errors"

Unhandled Exceptions

In database PL/SQL units, an unhandled user-error condition or internal error condition that is not trapped by an appropriate exception handler causes the implicit rollback of the program unit. If the program unit includes a COMMIT statement before the point at which the unhandled exception is observed, then the implicit rollback of the program unit can only be completed back to the previous COMMIT.

Additionally, unhandled exceptions in database-stored PL/SQL units propagate back to client-side applications that invoke the containing program unit. In such an application, only the application program unit invocation is rolled back (not the entire application program unit), because it is submitted to the database as a SQL statement.

If unhandled exceptions in database PL/SQL units are propagated back to database applications, modify the database PL/SQL code to handle the exceptions. Your application can also trap for unhandled exceptions when invoking database program units and handle such errors appropriately.

Handling Errors in Distributed Queries

You can use a trigger or a stored subprogram to create a distributed query. This distributed query is decomposed by the local Oracle Database instance into a corresponding number of remote queries, which are sent to the remote nodes for execution. The remote nodes run the queries and send the results back to the local node. The local node then performs any necessary post-processing and returns the results to the user or application.

If a portion of a distributed statement fails, possibly from a constraint violation, then Oracle Database returns ORA-02055. Subsequent statements, or subprogram invocations, return ORA-02067 until a rollback or a rollback to savepoint is entered.

Design your application to check for any returned error messages that indicates that a portion of the distributed update has failed. If you detect a failure, rollback the entire transaction (or rollback to a savepoint) before allowing the application to proceed.

Handling Errors in Remote Subprograms

When a subprogram is run locally or at a remote location, these types of exceptions can occur:

  • PL/SQL user-defined exceptions, which must be declared using the keyword EXCEPTION

  • PL/SQL predefined exceptions, such as NO_DATA_FOUND

  • SQL errors, such as ORA-00900

  • Application exceptions, which are generated using the RAISE_APPLICATION_ERROR procedure.

When using local subprograms, all of these messages can be trapped by writing an exception handler, such as:

EXCEPTION
    WHEN ZERO_DIVIDE THEN
    /* Handle the exception */

The WHEN clause requires an exception name. If the exception that is raised does not have a name, such as those generated with RAISE_APPLICATION_ERROR, then one can be assigned using PRAGMA_EXCEPTION_INIT. For example:

DECLARE
    ...
    Null_salary EXCEPTION;
    PRAGMA EXCEPTION_INIT(Null_salary, -20101);
BEGIN
    ...
    RAISE_APPLICATION_ERROR(-20101, 'salary is missing');
    ...
EXCEPTION
    WHEN Null_salary THEN
        ...

When invoking a remote subprogram, exceptions are also handled by creating a local exception handler. The remote subprogram must return an error number to the local invoking subprogram, which then handles the exception, as shown in the previous example. Because PL/SQL user-defined exceptions always return ORA-06510 to the local subprogram, these exceptions cannot be handled. All other remote exceptions can be handled in the same manner as local exceptions.

Debugging Stored Subprograms

Compiling a stored subprogram involves fixing any syntax errors in the code. You might need to do additional debugging to ensure that the subprogram works correctly, performs well, and recovers from errors. Such debugging might involve:

  • Adding extra output statements to verify execution progress and check data values at certain points within the subprogram.

  • Running a separate debugger to analyze execution in greater detail.

Topics:

PL/Scope

PL/Scope is a compiler-driven tool that collects and organizes data about user-defined identifiers from PL/SQL source code. Because PL/Scope is a compiler-driven tool, you use it through interactive development environments (such as SQL Developer and JDeveloper), rather than directly.

PL/Scope enables the development of powerful and effective PL/Scope source code browsers that increase PL/SQL developer productivity by minimizing time spent browsing and understanding source code.

For more information about PL/Scope, see Chapter 7, "Using PL/Scope."

PL/SQL Hierarchical Profiler

The PL/SQL hierarchical profiler reports the dynamic execution profile of your PL/SQL program, organized by subprogram calls. It accounts for SQL and PL/SQL execution times separately. Each subprogram-level summary in the dynamic execution profile includes information such as number of calls to the subprogram, time spent in the subprogram itself, time spent in the subprogram's subtree (that is, in its descendent subprograms), and detailed parent-children information.

You can browse the generated HTML reports in any browser. The browser's navigational capabilities, combined with well chosen links, provide a powerful way to analyze performance of large applications, improve application performance, and lower development costs.

For a detailed description of PL/SQL hierarchical profiler, see Chapter 8, "Using the PL/SQL Hierarchical Profiler."

Oracle JDeveloper

Recent releases of Oracle JDeveloper have extensive features for debugging PL/SQL, Java, and multi-language programs. You can get Oracle JDeveloper as part of various Oracle product suites. Often, a more recent release is available as a download at:

http://www.oracle.com/technetwork/developer-tools/jdev/downloads/index.html

DBMS_OUTPUT Package

You can also debug stored subprograms and triggers using the Oracle package DBMS_OUTPUT. Put PUT and PUT_LINE statements in your code to output the value of variables and expressions to your terminal.

Privileges for Debugging PL/SQL and Java Stored Subprograms

Starting with Oracle Database 10g, a new privilege model applies to debugging PL/SQL and Java code running within the database. This model applies whether you are using Oracle JDeveloper, Oracle Developer, or any of the various third-party PL/SQL or Java development environments, and it affects both the DBMS_DEBUG and DBMS_DEBUG_JDWP APIs.

For a session to connect to a debugger, the effective user at the time of the connect operation must have the DEBUG CONNECT SESSION system privilege. This effective user might be the owner of a DR subprogram involved in making the connect call.

When a debugger becomes connected to a session, the session login user and the enabled session-level roles are fixed as the privilege environment for that debugging connection. Any DEBUG or EXECUTE privileges needed for debugging must be granted to that combination of user and roles.

  • To be able to display and change Java public variables or variables declared in a PL/SQL package specification, the debugging connection must be granted either EXECUTE or DEBUG privilege on the relevant code.

  • To be able to either display and change private variables or breakpoint and run code lines step by step, the debugging connection must be granted DEBUG privilege on the relevant code


    Caution:

    The DEBUG privilege allows a debugging session to do anything that the subprogram being debugged could have done if that action had been included in its code.

In addition to these privilege requirements, the ability to stop on individual code lines and debugger access to variables are allowed only in code compiled with debug information generated. Use the PL/SQL compilation parameter PLSQL_DEBUG and the DEBUG keyword on statements such as ALTER PACKAGE to control whether the PL/SQL compiler includes debug information in its results. If not, variables are not accessible, and neither stepping nor breakpoints stop on code lines. The PL/SQL compiler never generates debug information for code hidden with the PL/SQL wrap utility.


See Also:

Oracle Database PL/SQL Language Reference, for information about the wrap utility

The DEBUG ANY PROCEDURE system privilege is equivalent to the DEBUG privilege granted on all objects in the database. Objects owned by SYS are included if the value of the O7_DICTIONARY_ACCESSIBILITY parameter is TRUE.

A debug role mechanism is available to carry privileges needed for debugging that are not normally enabled in the session. See the documentation on the DBMS_DEBUG and DBMS_DEBUG_JDWP packages for details on how to specify a debug role and any necessary related password.

The JAVADEBUGPRIV role carries the DEBUG CONNECT SESSION and DEBUG ANY PROCEDURE privileges. Grant it only with the care those privileges warrant.


Caution:

Granting DEBUG ANY PROCEDURE privilege, or granting DEBUG privilege on any object owned by SYS, means granting complete rights to the database.

Writing Low-Level Debugging Code

If you are writing code for part of a debugger, you might need to use packages such as DBMS_DEBUG_JDWP or DBMS_DEBUG.

DBMS_DEBUG_JDWP Package

The DBMS_DEBUG_JDWP package, provided starting with Oracle Database 9g Release 2, provides a framework for multi-language debugging that is expected to supersede the DBMS_DEBUG package over time. It is especially useful for programs that combine PL/SQL and Java.

DBMS_DEBUG Package

The DBMS_DEBUG package, provided starting with Oracle8i, implements server-side debuggers and provides a way to debug server-side PL/SQL units. Several of the debuggers available, such as Oracle Procedure Builder and various third-party vendor solutions, use this API.


See Also:


Invoking Stored Subprograms

Stored PL/SQL subprograms can be invoked from many different environments. For example:

  • Interactively, using an Oracle Database tool

  • From the body of another subprogram

  • From within an application (such as a SQL*Forms or a precompiler)

  • From the body of a trigger

Stored PL/SQL functions (but not procedures) can also be invoked from within SQL statements. For details, see "Invoking Stored PL/SQL Functions from SQL Statements".

Topics:


See Also:


Privileges Required to Invoke a Subprogram

You do not need privileges to invoke:

  • Standalone subprograms that you own

  • Subprograms in packages that you own

  • Public standalone subprograms

  • Subprograms in public packages

To invoke a standalone or package subprogram owned by another user:

  • You must have the EXECUTE privilege for the standalone subprogram or for the package containing the subprogram, or you must have the EXECUTE ANY PROCEDURE system privilege.

  • When running a remote subprogram, you must be granted the EXECUTE privilege or EXECUTE ANY PROCEDURE system privilege directly, not through a role.

  • You must include the name of the owner in the invocation. For example:

    EXECUTE jdoe.Fire_emp (1043);
    EXECUTE jdoe.Hire_fire.Fire_emp (1043);
    
  • If the subprogram is a definer's-rights (DR) subprogram, then it runs with the privileges of the owner. The owner must have all the necessary object privileges for any referenced objects.

  • If the subprogram is an invoker's-rights (IR) subprogram, then it runs with your privileges. You must have all the necessary object privileges for any referenced objects; that is, all objects accessed by the subprogram through external references that are resolved in your schema. You can hold these privileges either directly or through a role. Roles are enabled unless an IR subprogram is invoked directly or indirectly by a DR subprogram.

Invoking a Subprogram Interactively from Oracle Tools

You can invoke a subprogram interactively from an Oracle Database tool, such as SQL*Plus. Example 6-15 uses SQL*Plus to create a procedure and then invokes it in two different ways.

Example 6-15 Invoking a Subprogram Interactively with SQL*Plus

CREATE OR REPLACE PROCEDURE salary_raise (
  employee  EMPLOYEES.EMPLOYEE_ID%TYPE,
  increase  EMPLOYEES.SALARY%TYPE
)
IS
BEGIN
  UPDATE EMPLOYEES
  SET SALARY = SALARY + increase
  WHERE EMPLOYEE_ID = employee;
END;
/
 

Invoke procedure from within PL/SQL block:

BEGIN
  salary_raise(205, 200);
END;
/
 

Result:

PL/SQL procedure successfully completed.
 

Invoke procedure with EXECUTE statement:

EXECUTE salary_raise(205, 200);
 

Result:

PL/SQL procedure successfully completed.

Some interactive tools allow you to create session variables, which you can use for the duration of the session. Using SQL*Plus, Example 6-16 creates, uses, and prints a session variable.

Example 6-16 Creating and Using a Session Variable with SQL*Plus

-- Create function for later use:

CREATE OR REPLACE FUNCTION get_job_id (
  emp_id  EMPLOYEES.EMPLOYEE_ID%TYPE
) RETURN EMPLOYEES.JOB_ID%TYPE
IS
  job_id  EMPLOYEES.JOB_ID%TYPE;
BEGIN
  SELECT JOB_ID INTO job_id
  FROM EMPLOYEES
  WHERE EMPLOYEE_ID = emp_id;
 
  RETURN job_id;
END;
/
-- Create session variable:
 
VARIABLE job VARCHAR2(10);
 
-- Run function and store returned value in session variable:
 
EXECUTE :job := get_job_id(204);
 
PL/SQL procedure successfully completed.
 

SQL*Plus command:

PRINT job;
 

Result:

JOB
--------------------------------
PR_REP

See Also:

  • SQL*Plus User's Guide and Reference for information about the EXECUTE command

  • Your tools documentation for information about performing similar operations using your development tool


Invoking a Subprogram from Another Subprogram

A subprogram or a trigger can invoke another stored subprogram. In Example 6-17, the procedure print_mgr_name invokes the procedure print_emp_name.

Recursive subprogram invocations are allowed (that is, a subprogram can invoke itself).

Example 6-17 Invoking a Subprogram from Within Another Subprogram

-- Create procedure that takes employee's ID and prints employee's name:
 
CREATE OR REPLACE PROCEDURE print_emp_name (
  emp_id  EMPLOYEES.EMPLOYEE_ID%TYPE
)
IS
  fname  EMPLOYEES.FIRST_NAME%TYPE;
  lname  EMPLOYEES.LAST_NAME%TYPE;
BEGIN
  SELECT FIRST_NAME, LAST_NAME
  INTO fname, lname
  FROM EMPLOYEES
  WHERE EMPLOYEE_ID = emp_id;
 
  DBMS_OUTPUT.PUT_LINE (
    'Employee #' || emp_id || ':  ' || fname || ' ' || lname
  );
END;
/
 
-- Create procedure that takes employee's ID and prints manager's name:
 
CREATE OR REPLACE PROCEDURE print_mgr_name (
  emp_id  EMPLOYEES.EMPLOYEE_ID%TYPE
)
IS
  mgr_id  EMPLOYEES.MANAGER_ID%TYPE;
BEGIN
  SELECT MANAGER_ID
  INTO mgr_id
  FROM EMPLOYEES
  WHERE EMPLOYEE_ID = emp_id;
 
 DBMS_OUTPUT.PUT_LINE (
   'Manager of employee #' || emp_id || ' is:  '
 );
 
 print_emp_name(mgr_id);
END;
/
 

Invoke procedures:

BEGIN
  print_emp_name(200);
  print_mgr_name(200);
END;
/
 

Result:

Employee #200:  Jennifer Whalen
Manager of employee #200 is:
Employee #101:  Neena Kochhar

Invoking a Subprogram from a 3GL Application

A 3GL database application, such as a precompiler or an OCI application, can invoke a subprogram from within its own code.

Assume that the procedure Fire_emp1 was created as follows:

CREATE OR REPLACE PROCEDURE fire_emp1 (Emp_id NUMBER) AS
  BEGIN
    DELETE FROM Emp_tab WHERE Empno = Emp_id;
  END;

To run a subprogram within the code of a precompiler application, you must use the EXEC call interface. For example, this statement invokes the Fire_emp procedure in the code of a precompiler application:

EXEC SQL EXECUTE
  BEGIN
    Fire_emp1(:Empnum);
  END;
END-EXEC;

See Also:

Oracle Call Interface Programmer's Guide for information about invoking PL/SQL subprograms from within 3GL applications

Invoking Remote Subprograms

Remote subprograms (standalone and package) can be invoked from within a subprogram, OCI application, or precompiler by specifying the remote subprogram name, a database link, and the parameters for the remote subprogram.

For example, this SQL*Plus statement invokes the procedure fire_emp1, which is located in the database and referenced by the local database link named boston_server:

EXECUTE fire_emp1@boston_server(1043);

You must specify values for all remote subprogram parameters, even if there are defaults. You cannot access remote package variables and constants.


Caution:

  • Remote subprogram invocations use runtime binding. The user account to which you connect depends on the database link. (Stored subprograms use compile-time binding.)

  • If a local subprogram invokes a remote subprogram, and a time stamp mismatch is found during execution of the local subprogram, then the remote subprogram is not run, and the local subprogram is invalidated.


Topics:


See Also:

"Handling Errors in Remote Subprograms" for information about exception handling when invoking remote subprograms

Synonyms for Remote Subprograms

You can create a synonym for a remote subprogram name and database link, and then use the synonym to invoke the subprogram. For example:

CREATE SYNONYM synonym1 for fire_emp1@boston_server;

EXECUTE synonym1(1043);
/

The synonym enables you to invoke the remote subprogram from an Oracle Database tool application, such as a SQL*Forms application, as well from within a subprogram, OCI application, or precompiler.

Synonyms provide both data independence and location transparency. Synonyms permit applications to function without modification regardless of which user owns the object and regardless of which database holds the object. However, synonyms are not a substitute for privileges on database objects. Appropriate privileges must be granted to a user before the user can use the synonym.

Because subprograms defined within a package are not individual objects (the package is the object), synonyms cannot be created for individual subprograms within a package.

If you do not want to use a synonym, you can create a local subprogram to invoke the remote subprogram. For example:

CREATE OR REPLACE PROCEDURE local_procedure
  (arg IN NUMBER)
AS
BEGIN
  fire_emp1@boston_server(arg);
END;
/
DECLARE
  arg NUMBER;
BEGIN
  local_procedure(arg);
END;
/

See Also:


Committing Transactions

All invocations to remotely stored subprograms are assumed to perform updates; therefore, this type of referencing always requires two-phase commit of that transaction (even if the remote subprogram is read-only). Furthermore, if a transaction that includes a remote subprogram invocation is rolled back, then the work done by the remote subprogram is also rolled back.

A subprogram invoked remotely can usually run a COMMIT, ROLLBACK, or SAVEPOINT statement, the same as a local subprogram. However, there are some differences in action:

  • If the transaction was originated by a database that is not an Oracle database, as might be the case in XA applications, these operations are not allowed in the remote subprogram.

  • After doing one of these operations, the remote subprogram cannot start any distributed transactions of its own.

  • If the remote subprogram does not commit or roll back its work, the commit is done implicitly when the database link is closed. In the meantime, further invocations to the remote subprogram are not allowed because it is still considered to be performing a transaction.

A distributed transaction modifies data on two or more databases. A distributed transaction is possible using a subprogram that includes two or more remote updates that access data on different databases. Statements in the construct are sent to the remote databases, and the execution of the construct succeeds or fails as a unit. If part of a distributed update fails and part succeeds, then a rollback (of the entire transaction or to a savepoint) is required to proceed. Consider this when creating subprograms that perform distributed updates.

Invoking Stored PL/SQL Functions from SQL Statements


Caution:

Because SQL is a declarative language, rather than an imperative (or procedural) one, you cannot know how many times a function invoked from a SQL statement will run—even if the function is written in PL/SQL, an imperative language.

If your application requires that a function be executed a certain number of times, do not invoke that function from a SQL statement. Use a cursor instead.

For example, if your application requires that a function be called for each selected row, then open a cursor, select rows from the cursor, and call the function for each row. This guarantees that the number of calls to the function is the number of rows fetched from the cursor.


To be invoked from a SQL statement, a stored PL/SQL function must be declared either at schema level or in a package specification.

These SQL statements can invoke stored PL/SQL functions:

  • INSERT

  • UPDATE

  • DELETE

  • SELECT

  • CALL

    (CALL can also invoke a stored PL/SQL procedure.)

To invoke a PL/SQL subprogram from SQL, you must either own or have EXECUTE privileges on the subprogram. To select from a view defined with a PL/SQL function, you must have SELECT privileges on the view. No separate EXECUTE privileges are necessary to select from the view.

For general information about invoking subprograms, including passing parameters, see Oracle Database PL/SQL Language Reference.

Topics:

Why Invoke Stored PL/SQL Subprograms from SQL Statements?

Invoking PL/SQL subprograms in SQL statements can:

  • Increase user productivity by extending SQL

    Expressiveness of the SQL statement increases where activities are too complex, too awkward, or unavailable with SQL.

  • Increase query efficiency

    Functions used in the WHERE clause of a query can filter data using criteria that must otherwise be evaluated by the application.

  • Manipulate character strings to represent special data types (for example, latitude, longitude, or temperature).

  • Provide parallel query execution

    If the query is parallelized, then SQL statements in your PL/SQL subprogram might also be run in parallel (using the parallel query option).

Where PL/SQL Functions Can Appear in SQL Statements

A PL/SQL function can appear in a SQL statement wherever a SQL function or an expression can appear in a SQL statement. For example:

  • Select list of the SELECT statement

  • Condition of the WHERE or HAVING clause

  • CONNECT BY, START WITH, ORDER BY, or GROUP BY clause

  • VALUES clause of the INSERT statement

  • SET clause of the UPDATE statement

A PL/SQL table function (which returns a collection of rows) can appear in a SELECT statement instead of:

  • Column name in the SELECT list

  • Table name in the FROM clause

A PL/SQL function cannot appear in these contexts, which require unchanging definitions:

  • CHECK constraint clause of a CREATE or ALTER TABLE statement

  • Default value specification for a column

When PL/SQL Functions Can Appear in SQL Expressions

To be invoked from a SQL expression, a PL/SQL function must satisfy these requirements:

  • It must be a row function, not a column (group) function; that is, its argument cannot be an entire column.

  • Its formal parameters must be IN parameters, not OUT or IN OUT parameters.

  • Its formal parameters and its return value (if any) must have Oracle built-in data types (such as CHAR, DATE, or NUMBER), not PL/SQL data types (such as BOOLEAN, RECORD, or TABLE).

    There is an exception to this rule: A formal parameter can have a PL/SQL data type if the corresponding actual parameter is implicitly converted to the data type of the formal parameter (as in Example 6-19).

The function in Example 6-18 satisfies the preceding requirements.

Example 6-18 PL/SQL Function in SQL Expression (Follows Rules)

DROP TABLE payroll;  -- in case it exists
CREATE TABLE payroll (
  srate  NUMBER,
  orate  NUMBER,
  acctno NUMBER
);
 
CREATE OR REPLACE FUNCTION gross_pay (
  emp_id  IN NUMBER,
  st_hrs  IN NUMBER := 40,
  ot_hrs  IN NUMBER := 0
) RETURN NUMBER
IS
  st_rate  NUMBER;
  ot_rate  NUMBER;
BEGIN
  SELECT srate, orate
  INTO st_rate, ot_rate
  FROM payroll
  WHERE acctno = emp_id;
 
 RETURN st_hrs * st_rate + ot_hrs * ot_rate;
END gross_pay;
/

In Example 6-19, the SQL statement CALL invokes the PL/SQL function f1, whose formal parameter and return value have PL/SQL data type PLS_INTEGER. The CALL statement succeeds because the actual parameter, 2, is implicitly converted to the data type PLS_INTEGER. If the actual parameter had a value outside the range of PLS_INTEGER, the CALL statement would fail.

Example 6-19 PL/SQL Function in SQL Expression (Exception to Rule)

CREATE OR REPLACE FUNCTION f1 (
  b IN PLS_INTEGER
) RETURN PLS_INTEGER
IS
BEGIN
  RETURN
    CASE
      WHEN b > 0  THEN  1
      WHEN b <= 0 THEN -1
      ELSE NULL
    END;
END f1;
/
 
VARIABLE x NUMBER;
CALL f1(b=>2) INTO :x;
PRINT x;
 

Result:

         X
----------
         1

Controlling Side Effects

The purity of a stored subprogram refers to the side effects of that subprogram on database tables or package variables. Side effects can prevent the parallelization of a query, yield order-dependent (and therefore, indeterminate) results, or require that package state be maintained across user sessions. Various side effects are not allowed when a function is invoked from a SQL query or DML statement.

Before Oracle Database 8g Release 1, Oracle Database leveraged the PL/SQL compiler to enforce restrictions during the compilation of a stored subprogram or a SQL statement. As of Oracle Database 8g Release 1, the compile-time restrictions were relaxed, and a smaller set of restrictions are enforced during execution.

This change provides uniform support for stored subprograms written in PL/SQL, Java, and C, and it allows programmers the most flexibility possible.

Topics:

Restrictions

When a new SQL statement is run, checks are made to see if it is logically embedded within the execution of a running SQL statement. This occurs if the statement is run from a trigger or from a subprogram that was in turn invoked from the running SQL statement. In these cases, further checks determine if the new SQL statement is safe in the specific context.

These restrictions are enforced on subprograms:

  • A subprogram invoked from a query (SELECT statement) or DML statement cannot end the current transaction, create or rollback to a savepoint, or ALTER the system or session.

  • A subprogram invoked from a query or parallelized DML statement cannot run a DML statement or otherwise modify the database.

  • A subprogram invoked from a DML statement cannot read or modify the particular table being modified by that DML statement.

These restrictions apply regardless of what mechanism is used to run the SQL statement inside the subprogram or trigger. For example:

  • They apply to a SQL statement invoked from PL/SQL, whether embedded directly in a subprogram or trigger body, run using the native dynamic mechanism (EXECUTE IMMEDIATE), or run using the DBMS_SQL package.

  • They apply to statements embedded in Java with SQLJ syntax or run using JDBC.

  • They apply to statements run with OCI using the callback context from within an "external" C function.

You can avoid these restrictions if the execution of the new SQL statement is not logically embedded in the context of the running statement. PL/SQL autonomous transactions provide one escape (see "Autonomous Transactions" ). Another escape is available using OCI from an external C function, if you create a new connection rather than using the handle available from the OCIExtProcContext argument.

Declaring a Function

You can use the keywords DETERMINISTIC and PARALLEL_ENABLE in the syntax for declaring a function. These are optimization hints that inform the query optimizer and other software components about:

  • Functions that need not be invoked redundantly

  • Functions permitted within a parallelized query or parallelized DML statement

Only functions that are DETERMINISTIC are allowed in function-based indexes and in certain snapshots and materialized views.

A deterministic function depends solely on the values passed into it as arguments and does not reference or modify the contents of package variables or the database or have other side-effects. Such a function produces the same result value for any combination of argument values passed into it.

You place the DETERMINISTIC keyword after the return value type in a declaration of the function. For example:

CREATE OR REPLACE FUNCTION f1 (
  p1 NUMBER
) RETURN NUMBER DETERMINISTIC
IS
BEGIN
  RETURN p1 * 2;
END;
/

You might place this keyword in these places:

  • On a function defined in a CREATE FUNCTION statement

  • In a function declaration in a CREATE PACKAGE statement

  • On a method declaration in a CREATE TYPE statement

Do not repeat the keyword on the function or method body in a CREATE PACKAGE BODY or CREATE TYPE BODY statement.

Certain performance optimizations occur on invocations of functions that are marked DETERMINISTIC without any other action being required. These features require that any function used with them be declared DETERMINISTIC:

  • Any user-defined function used in a function-based index.

  • Any function used in a materialized view, if that view is to qualify for Fast Refresh or is marked ENABLE QUERY REWRITE.

The preceding functions features attempt to use previously calculated results rather than invoking the function when it is possible to do so.

It is good programming practice to make functions that fall into these categories DETERMINISTIC:

  • Functions used in a WHERE, ORDER BY, or GROUP BY clause

  • Functions that MAP or ORDER methods of a SQL type

  • Functions that help determine whether or where a row appears in a result set

Keep these points in mind when you create DETERMINISTIC functions:

  • The database cannot recognize if the action of the function is indeed deterministic. If the DETERMINISTIC keyword is applied to a function whose action is not truly deterministic, then the result of queries involving that function is unpredictable.

  • If you change the semantics of a DETERMINISTIC function and recompile it, then existing function-based indexes and materialized views report results for the prior version of the function. Thus, if you change the semantics of a function, you must manually rebuild any dependent function-based indexes and materialized views.


See Also:

Oracle Database PL/SQL Language Reference for CREATE FUNCTION restrictions

Parallel Query and Parallel DML

Oracle Database's parallel execution feature divides the work of running a SQL statement across multiple processes. Functions invoked from a SQL statement that is run in parallel might have a separate copy run in each of these processes, with each copy invoked for only the subset of rows that are handled by that process.

Each process has its own copy of package variables. When parallel execution begins, these are initialized based on the information in the package specification and body as if a user is logging into the system; the values in package variables are not copied from the original login session. And changes made to package variables are not automatically propagated between the various sessions or back to the original session. Java STATIC class attributes are similarly initialized and modified independently in each process. Because a function can use package (or Java STATIC) variables to accumulate some value across the various rows it encounters, Oracle Database cannot assume that it is safe to parallelize the execution of all user-defined functions.

For SELECT statements in Oracle Database versions before 8.1.5, the parallel query optimization allowed functions noted as both RNPS and WNPS in a PRAGMA RESTRICT_REFERENCES declaration to run in parallel. Functions defined with CREATE FUNCTION statements had their code implicitly examined to determine if they were pure enough; parallelized execution might occur even though a pragma cannot be specified on these functions.

For DML statements in Oracle Database versions before 8.1.5, the parallelization optimization looked to see if a function was noted as having all four of RNDS, WNDS, RNPS and WNPS specified in a PRAGMA RESTRICT_REFERENCES declaration; those functions that were marked as neither reading nor writing to either the database or package variables could run in parallel. Again, those functions defined with a CREATE FUNCTION statement had their code implicitly examined to determine if they were actually pure enough; parallelized execution might occur even though a pragma cannot be specified on these functions.

Oracle Database versions 8.1.5 and later continue to parallelize those functions that earlier versions recognize as parallelizable. The PARALLEL_ENABLE keyword is the preferred way to mark your code as safe for parallel execution. This keyword is syntactically similar to DETERMINISTIC as described in "Declaring a Function"; it is placed after the return value type in a declaration of the function, as in:

CREATE OR REPLACE FUNCTION f1 (
  p1 NUMBER
) RETURN NUMBER PARALLEL_ENABLE
IS
BEGIN
  RETURN p1 * 2;
END;
/

A PL/SQL function defined with CREATE FUNCTION might still be run in parallel without any explicit declaration that it is safe to do so, if the system can determine that it neither reads nor writes package variables nor invokes any function that might do so. A Java method or C function is never seen by the system as safe to run in parallel, unless the programmer explicitly indicates PARALLEL_ENABLE on the call specification, or provides a PRAGMA RESTRICT_REFERENCES indicating that the function is sufficiently pure.

An additional runtime restriction is imposed on functions run in parallel as part of a parallelized DML statement. Such a function is not permitted to in turn run a DML statement; it is subject to the same restrictions that are enforced on fun8$ctions that are run inside a query (SELECT) statement.


See Also:

Restrictions

PRAGMA RESTRICT_REFERENCES for Backward Compatibility

In Oracle Database versions before 8.1.5 (Oracle8i), programmers used PRAGMA RESTRICT_REFERENCES to assert the purity level of a subprogram. In subsequent versions, use the hints PARALLEL_ENABLE and DETERMINISTIC, instead, to communicate subprogram purity to Oracle Database.

You can remove PRAGMA RESTRICT_REFERENCES from your code. However, this pragma remains available for backward compatibility in these situations:

  • When it is impossible or impractical to edit existing code to completely remove PRAGMA RESTRICT_REFERENCES.

    For example, if subprogram S1 depends on subprogram S2, and you do not remove the pragma from S1, then you might need the pragma in S2 to compile S1.

  • When replacing PRAGMA RESTRICT_REFERENCES in existing code with hints PARALLEL_ENABLE and DETERMINISTIC would negatively affect the action of new, dependent code. (Use PRAGMA RESTRICT_REFERENCES to preserve the action of the existing code.)

An existing PL/SQL application can thus continue using the pragma even on new functionality, to ease integration with the existing code. Do not use the pragma in a new application.

If you use PRAGMA RESTRICT_REFERENCES, place it in a package specification, not in a package body. It must follow the declaration of a subprogram, but it need not follow immediately. Only one pragma can reference a given subprogram declaration.

To code the PRAGMA RESTRICT_REFERENCES, use this syntax:

PRAGMA RESTRICT_REFERENCES ( 
    Function_name, WNDS [, WNPS] [, RNDS] [, RNPS] [, TRUST] ); 

Where:

OptionDescription
WNDSThe subprogram writes no database state (does not modify database tables).
RNDSThe subprogram reads no database state (does not query database tables).
WNPSThe subprogram writes no package state (does not change the values of package variables).
RNPSThe subprogram reads no package state (does not reference the values of package variables)
TRUSTThe other restrictions listed in the pragma are not enforced; they are simply assumed to be true. This allows easy invocation from functions that have RESTRICT_REFERENCES declarations to those that do not.

You can pass the arguments in any order. If any SQL statement inside the subprogram body violates a rule, then you get an error when the statement is parsed.

In Example 6-20, the function compound_ neither reads nor writes database or package state; therefore, you can assert the maximum purity level. Always assert the highest purity level that a subprogram allows, so that the PL/SQL compiler never rejects the subprogram unnecessarily.

Example 6-20 PRAGMA RESTRICT_REFERENCES

DROP TABLE accounts; -- in case it exists
CREATE TABLE accounts (
  acctno   INTEGER,
  balance  NUMBER
);
 
INSERT INTO accounts (acctno, balance)
VALUES (12345, 1000.00);
 
CREATE OR REPLACE PACKAGE finance AS
  FUNCTION compound_ (
    years  IN NUMBER,
    amount IN NUMBER,
    rate   IN NUMBER
   ) RETURN NUMBER;
  PRAGMA RESTRICT_REFERENCES (compound_, WNDS, WNPS, RNDS, RNPS);
END finance;
/
CREATE PACKAGE BODY finance AS
  FUNCTION compound_ (
    years  IN NUMBER,
    amount IN NUMBER,
    rate   IN NUMBER
   ) RETURN NUMBER
   IS
   BEGIN
     RETURN amount * POWER((rate / 100) + 1, years);
   END compound_;
  -- No pragma in package body
END finance;
/
DECLARE
  interest NUMBER;
BEGIN
  SELECT finance.compound_(5, 1000, 6)
  INTO interest
  FROM accounts
  WHERE acctno = 12345;
END;
/

Topics:

Using the Keyword TRUST

When PRAGMA RESTRICT REFERENCES includes the keyword TRUST, the restrictions listed in the pragma are assumed to be true, and not enforced.

When you invoke a subprogram that is in a section of code that does not use pragmas (such as a Java method), from a section of PL/SQL code that does use pragmas, specify PRAGMA RESTRICT REFERENCES with TRUST for either the invoked subprogram or the invoking subprogram.

In both Example 6-21 and Example 6-22, the PL/SQL function f invokes the Java procedure java_sleep. In Example 6-21, this is possible because java_sleep is declared to be WNDS with TRUST. In Example 6-22, it is possible because f is declared to be WNDS with TRUST, which allows it to invoke any subprogram.

Example 6-21 PRAGMA RESTRICT REFERENCES with TRUST on Invokee

CREATE OR REPLACE PACKAGE p IS
  PROCEDURE java_sleep (milli_seconds IN NUMBER)
  AS LANGUAGE JAVA NAME 'java.lang.Thread.sleep(long)';
  PRAGMA RESTRICT_REFERENCES(java_sleep,WNDS,TRUST);
  
  FUNCTION f (n NUMBER) RETURN NUMBER;
END p;
/
CREATE OR REPLACE PACKAGE BODY p IS
  FUNCTION f (
    n NUMBER
   ) RETURN NUMBER
   IS
   BEGIN
     java_sleep(n);
     RETURN n;
   END f;
END p;
/

Example 6-22 PRAGMA RESTRICT REFERENCES with TRUST on Invoker

CREATE OR REPLACE PACKAGE p IS
  PROCEDURE java_sleep (milli_seconds IN NUMBER)
  AS LANGUAGE JAVA NAME 'java.lang.Thread.sleep(long)';
  
  FUNCTION f (n NUMBER) RETURN NUMBER;
  PRAGMA RESTRICT_REFERENCES(f,WNDS,TRUST);
END p;
/
CREATE OR REPLACE PACKAGE BODY p IS
  FUNCTION f (
    n NUMBER
  ) RETURN NUMBER
  IS
  BEGIN
    java_sleep(n);
     RETURN n;
  END f;
END p;
/
Differences between Static and Dynamic SQL Statements

Static INSERT, UPDATE, and DELETE statements do not violate RNDS if these statements do not explicitly read any database states, such as columns of a table. However, dynamic INSERT, UPDATE, and DELETE statements always violate RNDS, regardless of whether the statements explicitly read database states.

This INSERT statement violates RNDS if it is executed dynamically, but it does not violate RNDS if it is executed statically.

INSERT INTO my_table values(3, 'BOB'); 

This UPDATE statement always violates RNDS statically and dynamically, because it explicitly reads the column name of my_table.

UPDATE my_table SET id=777 WHERE name='BOB';
Overloading Package Functions

If a subprogram is overloaded, PRAGMA RESTRICT_REFERENCES applies only to the most recently declared version.

In Example 6-23, the pragma applies to the second declaration of valid.

Example 6-23 Overloaded Package Function with PRAGMA RESTRICT_REFERENCES

CREATE OR REPLACE PACKAGE tests AS
  FUNCTION valid (x NUMBER) RETURN CHAR;
  FUNCTION valid (x DATE) RETURN CHAR;
  PRAGMA RESTRICT_REFERENCES (valid, WNDS);
END;
/

Returning Large Amounts of Data from a Function

In a data warehousing environment, you might use PL/SQL functions to transform large amounts of data. Perhaps the data is passed through a series of transformations, each performed by a different function. PL/SQL table functions let you perform such transformations without significant memory overhead or the need to store the data in tables between each transformation stage. These functions can accept and return multiple rows, can return rows as they are ready rather than all at once, and can be parallelized.


See Also:

Oracle Database PL/SQL Language Reference for more information about performing multiple transformations with pipelined table functions

Coding Your Own Aggregate Functions

To analyze a set of rows and compute a result value, you can code your own aggregate function that works the same as a SQL aggregate function like SUM:

  • Define an ADT that defines these member functions:

    • ODCIAggregateInitialize

    • ODCIAggregateIterate

    • ODCIAggregateMerge

    • ODCIAggregateTerminate

  • Code the member functions. In particular, ODCIAggregateIterate accumulates the result as it is invoked for each row that is processed. Store any intermediate results using the attributes of the ADT.

  • Create the aggregate function, and associate it with the ADT.

  • Call the aggregate function from SQL queries, DML statements, or other places that you might use the SQL aggregate functions. You can include typical options such as DISTINCT and ALL in the invocation of the aggregate function.


See Also:

Oracle Database Data Cartridge Developer's Guide for more information about user-defined aggregate functions

PKE88PK|%AOEBPS/index.htm Index

Index

A  B  C  D  E  F  G  H  I  J  K  L  M  N  O  P  Q  R  S  T  U  V  W  X  Y 

Symbols

%ROWTYPE attribute, 6.1.2.2.3
%TYPE attribute, 6.1.2.2.3

Numerics

32-bit IEEE 754 format, 2.3.2.1
3GL (third-generation language), 6.7.4
64-bit IEEE 754 format, 2.3.2.1

A

Abstract Data Type (ADT)
editions and, 19.1.1.2
resetting evolved, 19.1.1.3
Active Server Pages (ASP), 10.4
actual object, 19.1.3
actualization, 19.1.3
schema object dependency and, 19.1.3.2
address of row
See rowid
ADT
editions and, 19.1.1.2
resetting evolved, 19.1.1.3
AFTER SUSPEND trigger, 1.9.2.1
agent, 16.3
Agent Control Utility (agtctl)
commands
in shell mode, A.3.3
in single-line mode, A.3.2
list of, A.3.1
extproc administration and, A.3
extproc architecture and, A.2
aggregate function, 6.11
altering application online
See edition-based redefinition
ancestor edition, 19.1.2
anonymous block, 6.1.1
ANSI data type, 2.5.7
ANYDATA data type, 2.5.6
ANYDATASET data type, 2.5.6
AP (application program), 15.1.1
application architecture, 13.1
application domain index, 4.3
application program (AP), 15.1.1
application SQL, 19.3.3.2.3
APPLYING_CROSSEDITION_TRIGGER function, 19.3.4.1.1
AQ (Oracle Advanced Queuing), 16.2.2
archive
See Flashback Data Archive
ARGn data type, 2.9.1
arithmetic operation
with datetime data type, 2.4.4
with native floating-point data type, 2.3.4
ASP (Active Server Pages), 10.4
assignment
data type conversion during, 2.8.1, 2.8.1
reported by PL/Scope, 7.6
attribute
%ROWTYPE, 6.1.2.2.3
%TYPE, 6.1.2.2.3
Java STATIC class, 6.9.4.3
auditing policy, editioning view and, 19.5.1
Automatic Undo Management system, 12.1
autonomous transaction, 1.8
trigger as, 1.9.2.1

B

backward compatibility
LONG and LONG RAW data types for, 2.5.3.2
RESTRICT_REFERENCES pragma for, 6.9.4.4
BATCH commit redo option, 1.2.4
BFILE data type, 2.5.3.1
binary format, 2.3.2.1
binary internal ROWID format, 2.7.2.3
binary large object (BLOB) data type, 2.5.3.1
binary number, 2.3.2
BINARY_DOUBLE data type, 2.3
BINARY_FLOAT data type, 2.3
BLOB data type, 2.5.3.1
block, anonymous, 6.1.1
branch, 15.1.1
built-in function
See SQL function
bulk binding, 6.1.2.14
when to use, 6.1.2.15
business rule, 5

C

C external subprogram
callback with, 14.13
global variable in, 14.13.6
interface between PL/SQL and, 14.5.2
invoking, 14.10.4
loading, 14.4.2
passing parameter to, 14.9
publishing, 14.7
running, 14.10
service routine and, 14.12
See also external subprogram
C++ Class Library, 13.11.5
call specification
for external subprogram, 14.3
location of, 14.8
CALL statement, 14.10
calling subprogram
See invoking subprogram
cascading invalidation, 18.4
CHANGE_DUPKEY_ERROR_INDEX hint, 19.3.4.1.1
CHAR data type
compared to VARCHAR2 data type, 2.2.2
specifying length of, 2.2.1
values stored, 2.2
character data type class, 18.10.2.4
character data types, 2.2
character large object (CLOB) data type, 2.5.3.1
CHECK constraint
compared to NOT NULL constraint, 5.12.4
designing, 5.12.2
multiple, 5.12.3
naming, 5.13.2
restrictions on, 5.12.1
when to use, 5.12
client notification, 16.2.3
client/server architecture, 13.1.1
CLOB data type, 2.5.3.1
coarse-grained invalidation, 18.4
collection
edition and, 19.1.1.2
referenced by DML statement, 6.1.2.15.1
referenced by FOR loop, 6.1.2.15.3
referenced by SELECT statement, 6.1.2.15.2
column
default value for
when to use, 5.5
multiple foreign key constraints on, 5.8.3
specifying length of, 2.2.1
commit redo management, 1.2.4
COMMIT statement, 1.2.3
committing transaction, 1.2.3
comparison operator, 2.3.3
compile-time error, handling
for multilanguage program, 14.11
concurrency
serializable transaction for, 1.7
under explicit locking, 1.5.5
conditional expression represented as data, 2.6
connection pool, 13.6.1
constraint, 5
altering, 5.15
CHECK
See CHECK constraint
compared to trigger, 5.1
crossedition trigger and
collisions, 19.3.4.1.1
dropping, 19.3.6
deferring checking of, 5.8.4
disabling
effect of, 5.14
existing, 5.14.5, 5.14.5
new, 5.14.3, 5.14.3
reasons for, 5.14.1
dropping, 5.17, 5.17
editioning view and, 19.2.6
enabling
effect of, 5.14
existing, 5.14.4
new, 5.14.2
exception to, 5.14.7
FOREIGN KEY
See FOREIGN KEY constraint
minimizing overhead of, 5.9
naming, 5.13.2
NOT NULL
See NOT NULL constraint
on view, 5
PRIMARY KEY
See PRIMARY KEY constraint
privileges needed for defining, 5.13.1
referential integrity
See FOREIGN KEY constraint
renaming, 5.16
UNIQUE
See UNIQUE constraint
viewing definition of, 5.19
violating, 5.14.7
Continuous Query Notification (CQN), 11
converting data types
See data type conversion
copying on change, 19.1.3
coupling, 15.1.1
CQ_NOTIFICATION$_DESCRIPTOR object, 11.8.1
CQ_NOTIFICATION$_QUERY object, 11.8.3
CQ_NOTIFICATION$_REG_INFO object, 11.6.6.2
CQ_NOTIFICATION$_ROW object, 11.8.4
CQ_NOTIFICATION$_TABLE object, 11.8.2
CQN (Continuous Query Notification), 11
CREATE OR REPLACE optimization, 18.4
actualization and, 19.1.3
crossedition trigger, 19.3
creating, 19.3.4
displaying information about, 19.4
dropping, 19.3.6
execution of, 19.3.3.4
forward, 19.3.1
interaction with editions, 19.3.3
read-only editioning view and, 19.2.1
read-write editioning view and, 19.2.1
reverse, 19.3.2
sharing child cursor and, 19.4
crossedition trigger SQL
forward, 19.3.3.2.1
reverse, 19.3.3.2.2
cross-session PL/SQL function result cache, 6.1.2.7
current date and time, displaying, 2.4.1
current edition, 19.1.6
cursor, 1.4
canceling, 1.4.5
closing, 1.4.4
crossedition trigger and, 19.4
number in session, 1.4.1
Oracle XA application and, 15.3.4
rerunning statement with, 1.4.2
schema object dependency and, 18.11
scrollable, 1.4.3
See also cursor variable
cursor variable, 6.3
declaring, 6.3.1
examples of, 6.3.2
opening, 6.3.1

D

data definition language statement
See DDL statement
data integrity, 5
See also constraint
data type, 2.1
ANSI, 2.5.7
DB2, 2.5.7
dynamic, 2.5.6
external, 2.1
family of, 2.9.3
for character data, 2.2
for datetime data, 2.4
for geographic data, 2.5.1
for large amount of data, 2.5.3
for multimedia data, 2.5.2
for numeric data, 2.3
for spatial data, 2.5.1
for XML data, 2.5.5
object, 13.11.3
of formal subprogram parameter, 6.1.2.2.2
SQL/DS, 2.5.7
data type class, 18.10.2.4
data type conversion, 2.8
of ANSI and IBM data types, 2.5.7
of datetime data types, 2.4.5
of native floating-point data types, 2.3.5
date
default format for, 2.4.1
default value for, 2.4.3
displaying
current, 2.4.1
in nondefault format, 2.4.2
inserting in nondefault format, 2.4.2
See also datetime data types
datetime data type class, 18.10.2.4
datetime data types, 2.4
arithmetic operations with, 2.4.4
conversion functions for, 2.4.5
importing, exporting, and comparing, 2.4.6
DB2 data type, 2.5.7
DBMS_DEBUG package, 6.6.8
DBMS_DEBUG_JDWP package, 6.6.7
DBMS_FLASHBACK package, 12.8
DBMS_FLASHBACK.TRANSACTION_BACKOUT procedure, 12.9
DBMS_HPROF package, 8.2
DBMS_LOCK package, 1.6
DBMS_OUTPUT package, 6.6.4
DBMS_PARALLEL_EXECUTE package, 19.3.5
DBMS_STATS package, 12.12
DBMS_TYPES package, 2.5.6
DBMS_XA package, 15.3.10
DDL statement
Flashback Data Archive and, 12.10.6
ineffective, 18.4
Oracle XA and, 15.5.4.2
processing, 1.1
that generates notification, 11.3.2
deadlock, undetected, 1.6
debugging
PL/SQL Server Pages, 10.10
subprograms, 6.6
decimal number, 2.3.2
default column value, 5.5
default subprogram parameter value, 6.1.2.2.5
deferring constraint checks, 5.8.4
definer’s-rights subprogram, 6.7.1
denormal floating-point number, 2.3.2.1
dependency mode, 18.10
dependent object
See schema object dependency
dependent transaction, 12.9.1
descendent edition, 19.1.2
DETERMINISTIC function, 6.9.4.2
function-based index and, 4.4.2
RPC signature and, 18.10.2
dirty read, 1.7.1
disabling constraint
effect of, 5.14
existing, 5.14.5
new, 5.14.3
reasons for, 5.14.1
dispatcher thread, A.2
distributed database
FOREIGN KEY constraint and, 5.11
remote dependency management and, 18.9
distributed query, runtime error in, 6.5.3
distributed transaction, 15.1.1
how it works, 6.8.2
DLL (dynamic link library), 14.2
DML statement
bulk binding for, 6.1.2.15.1
parallel, 6.9.4.3
that references collection, 6.1.2.15.1
DML_LOCKS initialization parameter, 1.5, 1.5
domain index, 4.3
double-precision IEEE 754 format, 2.3.2.1
drivers, Oracle JDBC, 13.4.2.1
DTP (X/Open Distributed Transaction architecture), 15.1
dynamic link library (DLL), 14.2
dynamic registration, 15.1.1
dynamic SQL statement, 6.9.4.4.2
dynamically typed data, 2.5.6

E

edition, 19.1
ancestor, 19.1.2
creating, 19.1.2
crossedition triggers and, 19.3.3
current, 19.1.6
descendent, 19.1.2
displaying information about, 19.4
enabling for a user, 19.1.1.3
leaf, 19.1.2
making available
to all users, 19.1.5
to some users, 19.1.4
ora$base, 19.1, 19.1.2
retiring, 19.1.7
root, 19.1.2
session, 19.1.6
visibility of trigger in, 19.3.3.1
editionable schema object type, 19.1.1.1
edition-based redefinition, 19
editioned object, 19.1.1
editioning view, 19.2
auditing policy and, 19.5.1
changing base table of, 19.2.5
changing write-ability of, 19.2.3
covering table with, 19.5.1
creating, 19.2.1
displaying information about, 19.4
partition-extended name for, 19.2.2
preparing application for, 19.5.1
read-only, 19.2.1
read-write, 19.2.1
replacing, 19.2.4
SQL optimizer hint and, 19.2.7
editions-enabled user, 19.1.1.3
Electronic Product Code (EPC), 17.7.2.1
embedded PL/SQL gateway, 9.2.1.2
how to use, 9.4
enabling constraint
effect of, 5.14
existing, 5.14.4
new, 5.14.2
enabling editions for a user, 19.1.1.3
encoding scheme, adding, 17.3.4
environment, programming, 13
EPC (Electronic Product Code), 17.7.2.1
error handling
compile-time, 6.4
for multilanguage program, 14.11
runtime
See runtime error handling
exception
IEEE 754 standard
not raised, 2.3.2.2
raised during conversion, 2.3.5
in multilanguage program, 14.11
to constraint, 5.14.7
unhandled, 6.5.2
user-defined, 6.5.1
See also runtime error handling
EXCLUSIVE MODE option of LOCK TABLE statement, 1.5.2.4
EXPR data type, 2.9.2
expression
conditional, represented as data, 2.6
evaluation of, during data type conversion, 2.8.2
index built on
See function-based index
regular
See regular expression
expression directive in PSP script, 10.5.7
extended internal ROWID format, 2.7.2.2
external binary internal ROWID format, 2.7.2.3
external data type, 2.1
external large object (BFILE) data type, 2.5.3.1
external subprogram, 14.2
call specification for, 14.3
debugging, 14.13.4
loading, 14.4
publishing, 14.5
external transaction manager, 15.1.1
extproc agent, A

F

families of data types, 2.9.3, 2.9.3
fine-grained auditing (FGA) policy, editioning view and, 19.5.1
fine-grained invalidation, 18.4
firing order of triggers, 19.3.3.3
FIXED_DATE initialization parameter, 2.4.1
Flashback Data Archive, 12.10
Flashback Transaction, 12.9
FLASHBACK_TRANSACTION_QUERY view, 12.5
floating-point data type, 2.3
range and precision of, 2.3.2.1
See also native floating-point data type
floating-point number
components of, 2.3.1
denormal, 2.3.2.1
format of, 2.3.2
rounding, 2.3.2
subnormal, 2.3.2.1
FOR loop
bulk binding for, 6.1.2.15.3
that references collection, 6.1.2.15.3
FORCE option of ALTER USER statement, 19.1.1.3
FOREIGN KEY constraint, 5.8
distributed databases and, 5.11
dropping, 5.17
editioned view and, 19.1.1.2
enabling, 5.18
Flashback Transaction and, 12.9.1
indexing, 5.10
multiple, 5.8.3
naming, 5.13.2
NOT NULL constraint on, 5.8.2, 5.8.2
NULL value and, 5.8.1
privileges needed to create, 5.18.4
referential integrity enforced by, 5.18.5
UNIQUE constraint on, 5.8.2, 5.8.2
without other constraints, 5.8.2
foreign key dependency, 12.9.1
foreign rowid, 2.7.3
formal subprogram parameter, data type of, 6.1.2.2.2
forward crossedition trigger, 19.3.1
forward crossedition trigger SQL, 19.3.3.2.1
function
aggregate, 6.11
built-in
See SQL function
controlling side effects of, 6.9.4
DETERMINISTIC, 6.9.4.2
function-based index and, 4.4.2
RPC signature and, 18.10.2
invoking from SQL statement, 6.9
MGD_ID ADT, 17.3.3
OCI or OCCI, 13.7.2
PARALLEL_ENABLE, 6.9.4.2, 6.9.4.2
RPC signature and, 18.10.2
purity of, 6.9.4
RPC signature and, 18.10.2
result-cached, 6.1.2.7
returning large amount of data from, 6.10
SQL
See SQL function
See also subprogram
function result cache, 6.1.2.7
function-based index, 4.4
editioned function and, 19.1.1.2
examples of, 4.4.3
optimizer and, 4.4, 4.4.1

G

Geographic Information System (GIS) data, 2.5.1
global transaction, 15.1.1
global variable, in C external subprogram, 14.13.6
greedy operator in regular expression, 3.4.1
group commit, 1.2.4

H

hierarchical profiler, 8
host language, 13.6
host program, 13.6
hot rollover, 19
HTML syntax error in PSP script, 10.5.1.3

I

IA-32 and IA-64 instruction set architecture, 2.3.4
IBM CICS, 15.1.1
IBM Transarc Encina, 15.1.1
Identity Code Package, 17
IEEE 754 format, 2.3
See Also native floating-point data type
IEEE 754 standard
exception
not raised, 2.3.2.2
raised during conversion, 2.3.5
OCI support for datatypes of, 2.3.6.1
special values supported by, 2.3.2.2
IGNORE_ROW_ON_DUPKEY_INDEX hint, 19.3.4.1.1
IMMEDIATE commit redo option, 1.2.4
IN OUT subprogram parameter mode, 6.1.2.2.1
IN subprogram parameter mode, 6.1.2.2.1
independent transaction
See autonomous transaction
index, 4
domain, 4.3
edition-based redefinition and, 19.2.6
function-based
See function-based index
on MGD_ID column, 17.3.2
infinity, 2.3.2.2, 2.3.2.2
inherited object, 19.1.3
dropping, 19.1.3.1
initialization parameter
DML_LOCKS, 1.5
FIXED_DATE, 2.4.1
NLS_DATE_FORMAT, 2.4.1
OPEN_CURSORS, 1.4.1
integer data type class, 18.10.2.4
integrity constraint
See constraint
integrity of data, 5
interface
between PL/SQL and C, 14.5.2
between PL/SQL and Java, 14.5.1
OraDatabase, 13.11.2.3
program, 13.2
TX, 15.1.1
user, 13.2.1
stateful or stateless, 13.2.2
See also Oracle Call Interface
invalidation
cascading, 18.4
coarse-grained, 18.4
fine-grained, 18.4
of dependent object, 18.4
of package, 6.1.2.12
invoker’s-rights subprogram, 6.7.1
invoking subprogram, 6.7
from 3GL application, 6.7.4
from subprogram, 6.7.3
from trigger, 6.7.3
interactively from Oracle Database tools, 6.7.2
through embedded PL/SQL gateway, 9.4.4
isolation level
See transaction isolation level

J

Java class method
calling, 14.10.3
interface between PL/SQL and, 14.5.1
loading, 14.4.1
publishing, 14.6
See also external subprogram
Java Database Connectivity
See Oracle JDBC
Java language
compared to PL/SQL, 13.5
Oracle Database support for, 13.4
STATIC class attribute of, 6.9.4.3
Java Server Pages (JSP), 10.4
Java Virtual Machine
See Oracle JVM
JavaScript, 10.9.5
JDBC
See Oracle JDBC
JSP (Java Server Pages), 10.4
JVM
See Oracle JVM

K

key
foreign
See FOREIGN KEY constraint
primary
See PRIMARY KEY constraint
unique
See UNIQUE constraint

L

Large Object (LOB), 2.5.3.1
Oracle Objects for OLE support for, 13.11.3
leaf edition, 19.1.2
LGWR (log writer process), 1.2.4, 1.2.4
libunit, 14.2
lightweight queue, 16.3
loadpsp utility, 10.6
LOB
See Large Object (LOB)
LOCK TABLE statement, 1.5.2
SELECT FOR UPDATE statement with, 1.5.5
locking row explicitly, 1.5.4
locking table
explicitly, 1.5
implicitly, 1.5.3
log writer process (LGWR), 1.2.4
logical rowid, 2.7.3
LONG and LONG RAW data types, 2.5.3.2
loose coupling, 15.1.1

M

main transaction, 1.8
maximum availability of table, 19.2.1
metacharacter in regular expression, 3.1
metadata for SQL operator or function, 2.9
MGD_ID ADT, 17.1
MGD_ID database ADT function, 17.3.3
mod_plsql module, 9.2.1.1
mode
agtctl command, A.3
dependency, 18.10
lock, 1.5.2
serialized
See serializable transaction
subprogram parameter, 6.1.2.2.1
MODIFY CONSTRAINT clause of ALTER TABLE statement, 5.15
modifying
See altering
monitor thread, A.2
multilanguage program, 14.1
error or exception in, 14.11
multiline mode, 3.2
multilingual data, 3.4.2
multimedia data, 2.5.2
multithreaded extproc agent, A

N

name resolution, 18.7
editions and, 19.1.3.2
NaN (not a number), 2.3.2.2
national character large object (NCLOB) data type, 2.5.3.1
native execution, compiling subprogram for, 6.2
native floating-point data type, 2.3
arithmetic operation with, 2.3.4
binary format for, 2.3.2.1
clients that support, 2.3.6
comparison operator for, 2.3.3
conversion functions for, 2.3.5
special values for, 2.3.2.2
NCHAR data type
specifying length of, 2.2.1
values stored, 2.2
NCLOB data type, 2.5.3.1
negative infinity, 2.3.2.2
negative zero, 2.3.2.2
new features, Preface
NLS_DATE_FORMAT initialization parameter, 2.4.1
noneditionable schema object type, 19.1.1.1
noneditioned object, 19.1.1
nongreedy operator in regular expression, 3.4.3
nonpersistent queue, 16.3
normalized significand, 2.3.2.1
NOT NULL constraint
compared to CHECK constraint, 5.12.4
naming, 5.13.2
on FOREIGN KEY constraint, 5.8.2, 5.8.2
when to use, 5.4
NOWAIT commit redo option, 1.2.4
NOWAIT option of LOCK TABLE statement, 1.5.2
NULL value
FOREIGN KEY constraint and, 5.8.1
function-based index and, 4.4
indexing and, 5.4
number
binary, 2.3.2
decimal, 2.3.2
rounding, 2.3.2
NUMBER data type, 2.3
number data type class, 18.10.2.4
numeric data types, 2.3
NVARCHAR2 data type
specifying length of, 2.2.1
values stored, 2.2

O

object
actual, 19.1.3
dependent
See schema object dependency
editioned, 19.1.1
inherited, 19.1.3
dropping, 19.1.3.1
large
See Large Object (LOB)
noneditioned, 19.1.1
potentially editioned, 19.1.1
referenced
See schema object dependency
size limit for PL/SQL stored, 6.1.2.9
object change notification, 11.1
object data type, 13.11.3
object type
See schema object type
OCCI
See Oracle C++ Call Interface
OCI
See Oracle Call Interface
OCIAnyData and OCIAnyDataSet interfaces, 2.5.6
ODC (Oracle Data Control), 13.11.4
ODP.NET, 13.9
online application upgrade
See edition-based redefinition
OO4O
See Oracle Objects for OLE
OPEN_CURSORS initialization parameter, 1.4.1
operator
comparison, 2.3.3
in regular expression, 3.4
greedy, 3.4.1
nongreedy, 3.4.3
metadata for, 2.9
relational, 2.3.3
optimizer
editioning view and, 19.2.7
function-based index and, 4.4, 4.4.1
RPC signature and, 18.10.2
ora$base edition, 19.1, 19.1.2
ORA_ROWSCN pseudocolumn, 12.7
Oracle Advanced Queuing (AQ), 16.2.2
Oracle C++ Call Interface, 13.7
building application with, 13.7.4
kinds of functions in, 13.7.2
procedural and nonprocedural elements of, 13.7.3
Oracle Call Interface, 13.7
building application with, 13.7.4
commit redo action in, 1.2.4
compared to precompiler, 13.8
kinds of functions in, 13.7.2
procedural and nonprocedural elements of, 13.7.3
with Oracle XA, 15.3.5
Oracle Data Control (ODC), 13.11.4
Oracle Data Provider for .NET, 13.9
Oracle data type
See data type
Oracle Database package, 6.1.2.13
for writing low-level debugging code, 6.6.6
runtime error raised by, 6.5
Oracle Expression Filter, 2.6
Oracle Flashback Query, 12.3
Oracle Flashback Technology, 12.1
application development features, 12.1.1
configuring database for, 12.2
database administration features, 12.1.2
performance guidelines for, 12.12
Oracle Flashback Transaction Query, 12.5
Oracle Flashback Version Query, 12.4
Oracle JDBC, 13.4.2
compared to Oracle SQLJ, 13.4.4
sample program
2.0, 13.4.2.2
pre-2.0, 13.4.2.3
Oracle JDeveloper, 6.6.3
Oracle SQLJ and, 13.4.3.1
Oracle JPublisher, 13.4.5
Oracle JVM, 13.4.1
Oracle Lock Management services, 1.6
Oracle Multimedia, 2.5.2
Oracle Objects for OLE, 13.11
Automation Server, 13.11.1
C++ Class Library, 13.11.5
object data type support, 13.11.3
object model, 13.11.2
Oracle RAC and Oracle XA, 15.5.3
Oracle SQLJ, 13.4.3
compared to Oracle JDBC, 13.4.4
Oracle JDeveloper and, 13.4.3.1
Oracle Text, 2.5.4, 2.5.4
Oracle Total Recall, 12.10
Oracle Tuxedo, 15.1.1
Oracle Virtual Private Database (VPD) policy, editioning view and, 19.5.1
Oracle XA
Oracle RAC and, 15.5.3
subprograms, 15.2
when to use, 15
OUT subprogram parameter mode, 6.1.2.2.1
out-of-space error, 1.9
overloaded subprogram, 6.1.2.8

P

package, 6.1.2.8
advantages of, 6.1.2.8
body of, 6.1.2.8
creating, 6.1.2.10
invalidation of, 6.1.2.12
naming, 6.1.2.11
Oracle Database, 6.1.2.13
for writing low-level debugging code, 6.6.6
runtime error raised by, 6.5
privileges needed to create, 6.1.2.10.2
privileges needed to drop, 6.1.2.10.2
session state and, 18.4.1
size limit for, 6.1.2.9
specification of, 6.1.2.8
synonym for, 6.8.1
package invalidation and, 6.1.2.12
package subprogram, 6.1.2
parallel DML statement, 6.9.4.3
parallel query, 6.9.4.3
PARALLEL_ENABLE function, 6.9.4.2
RPC signature and, 18.10.2
parameter
initialization
See initialization parameter
subprogram
See subprogram parameter
parameter mode, 6.1.2.2.1
partition-extended editioning view name, 19.2.2
persistent LOB instance, 2.5.3.1
persistent queue, 16.3
phantom read, 1.7.1
PL/Scope tool, 7
plshprof utility, 8.5
PL/SQL function result cache, 6.1.2.7
PL/SQL gateway, 9.2.1
PL/SQL hierarchical profiler, 8
PL/SQL language, 13.3
compared to Java, 13.5
PL/SQL object, CREATE OR REPLACE and, 18.4
PL/SQL Server Pages
characteristics of, 10.5.1
elements of, 10.5
loading, 10.6
script error in, 10.5.1.3
PL/SQL unit, 6.1
stored, 6.1.2
PL/SQL Web Toolkit, 9.2.2
pool, connection, 13.6.1
positive infinity, 2.3.2.2
positive zero, 2.3.2.2
POSIX standard for regular expressions
operators defined in, 3.4.1
Oracle SQL and, 3.3
Oracle SQL multilingual extensions to, 3.4.2
Oracle SQL PERL-influenced extensions to, 3.4.3
potentially editioned object, 19.1.1
precompiler, 13.6
compared to Oracle Call Interface, 13.8
Oracle XA and, 15.3.4
PRIMARY KEY constraint, 5.6
dropping, 5.17
Flashback Transaction and, 12.9.1
naming, 5.13.2
primary key dependency, 12.9.1
privileges
for creating package, 6.1.2.3
for creating subprogram, 6.1.2.3
for debugging subprogram, 6.6.5
for defining constraint, 5.13.1
for dropping package, 6.1.2.5
for dropping packages, 6.1.2.10.2
for dropping subprogram, 6.1.2.5
for Oracle Flashback Technology, 12.2.5
for running subprogram, 6.7.1
revoked, object dependency and, 18.4.2
Pro*C/C++ precompiler, 13.6.1
native floating-point data type support in, 2.3.6.3
Pro*COBOL precompiler, 13.6.2
procedure
PL/SQL Server Pages and, 10.5.3
See also subprogram
product code, 17.7.2
profiler, 8
program interface, 13.2
programming environment, 13
PSP
See PL/SQL Server Pages
public information, required, 15.1.2
publish-subscribe model, 16
purity of function, 6.9.4
RPC signature and, 18.10.2

Q

quality-of-service flag, 11.6.6.2
query
parallel, 6.9.4.3
registering for Continuous Query Notification, 11.6
runtime error in distributed, 6.5.3
query result change notification, 11.2
queue, 16.3

R

Radio Frequency Identification (RFID) technology, 17.7.1
RAISE statement, 6.5.1
RAW data type, 2.5.3.2
raw data type class, 18.10.2.4
READ COMMITTED transaction isolation level
compared to SERIALIZABLE, 1.7.4.2
in Oracle Database, 1.7.1
transaction interactions with, 1.7.1
read consistency
statement-level, 1.3
transaction-level, 1.3
locking tables explicitly for, 1.5
read-only transaction for, 1.3
read lock, 1.7.3
READ UNCOMMITTED transaction isolation level
in Oracle Database, 1.7.1
transaction interactions with, 1.7.1
read-only editioning view, 19.2.1
read-only transaction, 1.3
read-write editioning view, 19.2.1
redefinition, edition-based, 19
redo information for transaction, 1.2.4
redo management, 1.2.4
referenced object
See schema object dependency
referential integrity
serializable transactions and, 1.7.3
trigger for enforcing, 1.7.3
referential integrity constraint
See FOREIGN KEY constraint
REGEXP_COUNT function, 3.2
REGEXP_INSTR function, 3.2
REGEXP_LIKE condition, 3.2
REGEXP_REPLACE function, 3.2
back reference operator in, 3.4.1
REGEXP_SUBSTR function, 3.2
registration
dynamic, 15.1.1
for Continuous Query Notification, 11.6
in publish-subscribe model, 16.3
static, 15.1.1
regular expression, 3
in Oracle SQL, 3.2
in SQL statement, 3.5
metacharacter in, 3.1
POSIX standard and
See POSIX standard for regular expressions
Unicode and, 3.3
relational operator, 2.3.3
remote dependency management, 18.9
remote procedure call dependency management, 18.10
repeatable read, 1.3
read-only transaction for, 1.3
REPEATABLE READ transaction isolation level
in Oracle Database, 1.7.1
locking tables explicitly for, 1.5
transaction interactions with, 1.7.1
required public information, 15.1.2
rerunning SQL statement, 1.4.2
resource manager (RM), 15.1.1
RESTRICT_REFERENCES pragma
for backward compatibility, 6.9.4.4
overloaded functions and, 6.9.4.4.3
static and dynamic SQL statements and, 6.9.4.4.2
restricted internal ROWID format, 2.7.2.1
result cache, 6.1.2.7
resumable storage allocation, 1.9
RETENTION GUARANTEE clause for undo tablespace, 12.2.1
RETENTION option of ALTER TABLE statement, 12.2.4
reverse crossedition trigger, 19.3.2
reverse crossedition trigger SQL, 19.3.3.2.2
RFID (Radio Frequency Identification) technology, 17.7.1
RM (resource manager), 15.1.1
ROLLBACK statement, 1.2.5
rolling back transaction, 1.2.5
root edition, 19.1.2
roundiing floating-point numbers, 2.3.2
routine
See subprogram
row
address of
See rowid
locking explicitly, 1.5.4
ROW EXCLUSIVE MODE option of LOCK TABLE statement, 1.5.2.1, 1.5.2.1
ROW SHARE MODE option of LOCK TABLE statement, 1.5.2.1
rowid, 2.7
foreign, 2.7.3
logical, 2.7.3
universal (urowid), 2.7.3
ROWID data type, 2.7.2
ROWID pseudocolumn, 2.7
CQN and, 11.6.2.3
See also rowid
ROWTYPE_MISMATCH exception, 6.3.2
RPC dependency management, 18.10
RPC-signature dependency mode, 18.10.2
RR datetime format element, 2.4.1
rule on queue, 16.3
rules engine, 16.3
runtime error handling, 6.5
for distributed query, 6.5.3
for PL/SQL Server Pages (PSP) script, 10.5.1.3
for remote subprogram, 6.5.4
for storage allocation error, 1.9
for user-defined exception, 6.5.1
See also exception

S

SAVEPOINT statement, 1.2.6, 1.2.6
schema object dependency, 18
actualization and, 19.1.3.2
in distributed database, 18.9
invalidation and, 18.4
on nonexistence of other objects, 18.7
revoked privileges and, 18.4.2
shared pool and, 18.11
schema object type
editionable, 19.1.1.1
noneditionable, 19.1.1.1
scrollable cursor, 1.4.3
searchable text, 2.5.4
SELECT FOR UPDATE statement, 1.5.4
LOCK TABLE statement with, 1.5.5
referential integrity and
inside trigger, 1.7.3
outside trigger, 1.7.3
SELECT statement
bulk binding for, 6.1.2.15.2
referencing collection with, 6.1.2.15.2
with AS OF clause, 12.3
with FOR UPDATE clause
See SELECT FOR UPDATE statement
with VERSIONS BETWEEN clause, 12.4
semi-available table, 19.2.1
serendipitous change, 19.3.5
data transformation collisions and, 19.3.4.1.1
identifying, 19.3.4.1.1
serializable transaction
for concurrency control, 1.7
interaction with, 1.7.1
referential integrity and, 1.7.3
SERIALIZABLE transaction isolation level
compared to READ COMMITTED, 1.7.4.2
in Oracle Database, 1.7.1
transaction interactions with, 1.7.1
See also serializable transaction
server-side programming, 13.1.2
service routine, C external subprogram and, 14.12
session edition, 19.1.6
session state, 18.4.1
session variable, 6.7.2
SET CONSTRAINTS statement, 5.8.4
SET TRANSACTION statement with READ ONLY option, 1.3
SHARE MODE option of LOCK TABLE statement, 1.5.2.2
SHARE ROW EXCLUSIVE MODE option of LOCK TABLE statement, 1.5.2.3
shared SQL area, 1.1
side effects of function, controlling, 6.9.4
signature checking, 18.9.1
single-precision IEEE 754 format, 2.3.2.1
spatial data, 2.5.1
SQL area, shared, 1.1
SQL data type
See data type
SQL function
display type of, 2.9.2
for data type conversion, 2.4.5
metadata for, 2.9
SQL optimizer hint and editioning view, 19.2.7
SQL statement
application, 19.3.3.2.3
crossedition trigger
forward, 19.3.3.2.1
reverse, 19.3.3.2.2
dynamic, 6.9.4.4.2
invoking PL/SQL function from, 6.9
processing
DDL statement, 1.1
stages of, 1.1
system management statement, 1.1, 1.1
rerunning, 1.4.2
static, 6.9.4.4.2
SQL/DS data type, 2.5.7
SQLJ
See Oracle SQLJ
SQLT_BDOUBLE data type, 2.3.6.1
SQLT_BFLOAT data type, 2.3.6.1
standalone subprogram, 6.1.2
state
session, 18.4.1
user interface and, 13.2.2
web application and, 9.6.8
statement
See SQL statement
statement-level read consistency, 1.3
static registration, 15.1.1
static SQL statement, 6.9.4.4.2
static variable, in C external subprogram, 14.13.7
statistics
for application, 8
for identifier, 7
storage allocation error, 1.9
stored PL/SQL unit, 6.1.2
subnormal floating-point number, 2.3.2.1
subprogram
compiling for native execution, 6.2
creating, 6.1.2.3
definer’s-rights, 6.7.1
editioned, 19.1.1.2
exception-handling, 6.5.1
external
See external subprogram
invoker’s-rights, 6.7.1
invoking
See invoking subprogram
naming, 6.1.2.1
Oracle XA, 15.2
overloaded, 6.1.2.8
package, 6.1.2
parameter of
See subprogram parameter
privileges needed to debug, 6.6.5
privileges needed to run, 6.7.1
remote, 6.5.4
size limit for, 6.1.2.9
standalone, 6.1.2
synonym for, 6.8.1
See also function and procedure
subprogram parameter, 6.1.2.2
composite variable as, 6.1.2.2.4
data type of formal, 6.1.2.2.2
default value of, 6.1.2.2.5
mode of, 6.1.2.2.1
subscriber, 16.3
subscription services, 16.3
synonym
CREATE OR REPLACE and, 18.4
for package, 6.8.1
for subprogram, 6.8.1
public, for editioned object, 19.1.1.2
SYSDATE function, 2.4.1
system management statement, 1.1, 1.1

T

table
locking
choosing strategy for, 1.5.2
explicitly, 1.5
implicitly, 1.5.3
with maximum availability, 19.2.1
with semi-availability, 19.2.1
Tag Data Translation Markup Language Schema, 17.1
task thread, A.2
temporary LOB instance, 2.5.3.1
thin client configuration, 13.1.3
third-generation language (3GL), 6.7.4
thread
dispatcher, A.2
monitor, A.2
Oracle XA library, 15.3.9
task, A.2
three-tier architecture, 13.1.3
tight coupling, 15.1.1
time
default format for, 2.4.3
default value for, 2.4.3
displaying
current, 2.4.1
in nondefault format, 2.4.3
inserting in nondefault format, 2.4.3
See also datetime data types
time stamp checking, 18.9.1
time-stamp dependency mode, 18.10.1
TM (transaction manager), 15.1.1
TPM (transaction processing monitor), 15.1.1
transaction
autonomous, 1.8
trigger as, 1.9.2.1
choosing isolation level for, 1.7.4.2
committing, 1.2.3
dependent, 12.9.1
distributed, 15.1.1
how it works, 6.8.2
global, 15.1.1
grouping operations into, 1.2.1
improving performance of, 1.2.2
main, 1.8, 1.8
read-only, 1.3
redo entry for, 1.2.4
rolling back, 1.2.5
savepoints for, 1.2.6
serializable
See serializable transaction
statements in, 1.2.6
transaction interaction
kinds of, 1.7.1
serializable transaction and, 1.7.1
transaction isolation level and, 1.7.1
transaction isolation level
choosing, 1.7.4.2
setting, 1.7.2
transaction interaction and, 1.7.1
transaction manager (TM), 15.1.1
transaction processing monitor (TPM), 15.1.1
transaction set consistency, 1.7.4.1
transaction-level read consistency, 1.3
locking tables explicitly for, 1.5
read-only transaction for, 1.3
transform, 19.3.1
applying, 19.3.5
trigger, 6.1.2.16
AFTER SUSPEND, 1.9.2.1
as autonomous transaction, 1.9.2.1
compared to constraint, 5.1
crossedition
See crossedition trigger
enforcing referential integrity with, 1.7.3
in edition
firing order of, 19.3.3.3
visibility of, 19.3.3.1
what kind can fire, 19.3.3.2
invoking subprogram from, 6.7.3
size limit for, 6.1.2.9
TRUST keyword in RESTRICT_REFERENCES pragma, 6.9.4.4.1
two-phase commit protocol, 15.1.1
two-tier architecture, 13.1.3
TX interface, 15.1.1
type attribute, 6.1.2.2.3

U

undetected deadlock, 1.6
undo data, 12.1
UNDO_RETENTION parameter, 1.3
undoing transaction, 1.2.5
unhandled exception, 6.5.2
Unicode
character literals and, 2.2.1
regular expressions and, 3.3
UNIQUE constraint
crossedition trigger and, 19.3.4.1.1
dropping, 5.17
naming, 5.13.2
on FOREIGN KEY constraint, 5.8.2, 5.8.2
when to use, 5.7
universal rowid (urowid), 2.7.3
unrepeatable read, 1.7.1
upgrading applications online
See edition-based redefinition
UROWID data type, 2.7.3
user interface, 13.2.1
stateful and stateless, 13.2.2
user lock, 1.6
user-defined exception, 6.5.1
user-defined type, as subprogram parameter, 6.1.2.2.4
UTLLOCKT.SQL script, 1.6.2

V

VARCHAR data type class, 18.10.2.4
VARCHAR2 data type
compared to CHAR data type, 2.2.2
specifying length of, 2.2.1, 2.2.1
values stored, 2.2
variable
cursor
See cursor variable
in C external subprogram
global, 14.13.6
static, 14.13.7
VERSIONS_ENDSCN pseudocolumn, 12.4
VERSIONS_ENDTIME pseudocolumn, 12.4
VERSIONS_OPERATION pseudocolumn, 12.4
VERSIONS_STARTSCN pseudocolumn, 12.4
VERSIONS_STARTTIME pseudocolumn, 12.4
VERSIONS_XID pseudocolumn, 12.4
view
constraint on, 5
editioned
FOREIGN KEY constraint and, 19.1.1.2
materialized view and, 19.1.1.2
editioning
See editioning view
VPD policy, editioning view and, 19.5.1

W

WAIT commit redo option, 1.2.4
WAIT option of LOCK TABLE statement, 1.5.2, 1.5.2
web application, 9.1
implementing, 9.2
state and, 9.6.8
web page
See also PL/SQL Server Pages
web services, 13.4.7
web toolkit
See PL/SQL Web Toolkit
WORK option of ROLLBACK statement, 1.2.5
wrap utility, debugging and, 6.6.5
write-ability of editioning view, 19.2.3
write-after-write dependency, 12.9.1

X

xa_open string, 15.3.3
XMLType data type, 2.5.5
X/Open Distributed Transaction architecture, 15.1
X/Open Distributed Transaction Processing (DTP) architecture, 15.1

Y

YY datetime format element, 2.4.2
PKjtt`PK|%AOEBPS/adfns_idcode.htm Using the Identity Code Package

17 Using the Identity Code Package

The Identity Code Package is a feature in the Oracle Database that offers tools and techniques to store, retrieve, encode, decode, and translate between various product or identity codes, including Electronic Product Code (EPC), in an Oracle Database. The Identity Code Package provides data types, metadata tables and views, and PL/SQL packages for storing EPC standard RFID tags or new types of RFID tags in a user table.

The Identity Code Package empowers Oracle Database with the knowledge to recognize EPC coding schemes, support efficient storage and component level retrieval of EPC data, and comply with the EPCglobal Tag Data Translation 1.0 (TDT) standard that defines how to decode, encode, and translate between various EPC RFID tag representations.

The Identity Code Package also provides an extensible framework that allows developers to use pre-existing coding schemes with their applications that are not included in the EPC standard and make the Oracle Database adaptable to these older systems and to any evolving identity codes that may some day be part of a future EPC standard.

The Identity Code Package also lets developers create their own identity codes by first registering the encoding category, registering the encoding type, and then registering the components associated with each encoding type.

Topics.

Identity Concepts

A database object MGD_ID is defined that lets users use EPC standard identity codes and use their own existing identity codes. See "Electronic Product Code (EPC) Concepts" for a brief description of EPC concepts. The MGD_ID object serves as the base code object to which belong certain categories, or types of the RFID tag, such as the EPC category, NASA category, and many other categories. Each category has a set of tag schemes or documents that define tag representation structures and their components. For the EPC category, the metadata needed to define encoding schemes (SGTIN-64, SGTIN-96, GID-96, and so forth) representing different encoding types (defined in the EPC standard v1.1) is loaded by default into the database. Users can define encoding their own categories and schemes as shown in Figure 17-1 and load these into the database as well.

Figure 17-1 RFID Code Categories and Their Schemes

RFID Code Categories and Their Schemes
Description of "Figure 17-1 RFID Code Categories and Their Schemes"

An MGD_ID object contains two attributes, a category_id and a list of components consisting of name-value pairs. When MGD_ID objects are stored, the tag representation must be parsed into these component name-value pairs upon object creation.

EPC standard version 1.1 defines one General Identifier type (GID) that is independent of any known, existing code schemes, five Domain Identifier types that are based on EAN.UCC specifications, and the identity type United States Department of Defense (USDOD). The five EAN.UCC based identity types are the serialized global trade identification number (SGTIN), the serial shipping container code (SSCC), the serialized global location number (SGLN), the global returnable asset identifier (GRAI) and the global individual asset identifier (GIAI).

Except GID, which has only one bit-level encoding, all the other identity types each have two encodings depending on their length: 64-bit and 96-bit. So in total there are thirteen different standard encodings for EPC tags. In addition, tags can be encoded in representations other than binary, such as the tag URI and pure identity representations.

Each EPC encoding has its own structure and organization, see Table 17-1. The EPC encoding structure field names relate to the names in the parameter_list parameter name-value pairs in the Identity Code Package API. For example, for SGTIN-64, the structure field names are Filter Value, Company Prefix Index, Item Reference, and Serial Number.

Table 17-1 General Structure of EPC Encodings

Encoding NameHeader Length in bitsField Names (parameter_list name-value pairs) and (length in bits)

GID-96

8

General Manager Number (8), Object Class (24), Serial Number (36)

SGTIN-64

2

Filter Value (3), Company Prefix Index (14), Item Reference 20), Serial Number (25)

SGTIN-96

8

Filter Value (3), Partition (3), Company Prefix (20-40), Item Reference (24-4), Serial Number (38)

SSCC-64

8

Filter Value (3), Company Prefix Index (14), Serial Reference (39)

SSCC-96

8

Filter Value (3), Partition (3), Company Prefix (20-40), Serial Reference (38-18), Unallocated (24)

SGLN-64

8

Filter Value (3), Company Prefix Index (14), Location Reference (20), Serial Number (19)

SGLN-96

8

Filter Value (3), Partition (3), Company Prefix (20-40), Location Reference (21-1), Serial Number (41)

GRAI-64

8

Filter Value (3), Company Prefix Index (14), Asset Type (20), Serial Number (19)

GRAI-96

8

Filter Value (3), Partition (3), Company Prefix (20-40), Asset Type (24-4), Serial Number (38)

GIAI-64

8

Filter Value (3), Company Prefix Index (14), Individual Asset Reference (39)

GIAI-96

8

Filter Value (3), Partition (3), Company Prefix (20-40), Individual Asset Reference (62-42)

USDOD-64

8

Filter Value (2), Government Managed Identifier (30), Serial Number (24)

USDOD-96

8

Filter Value (4), Government Managed Identifier (48), Serial Number (36)


EPCglobal defines eleven tag schemes (GID-96, SGTIN-64, SGTIN-96, and so forth). Each of these schemes has various representations; today, the most often used are BINARY, TAG_URI, and PURE_IDENTITY. For example, information in an SGTIN-64 can be represented in these ways:

BINARY: 1001100000000000001000001110110001000010000011111110011000110010
PURE_IDENTITY:  urn:epc:id:sgtin:0037000.030241.1041970
TAG_URI: urn:epc:tag:sgtin-64:3.0037000.030241.1041970
LEGACY: gtin=00037000302414;serial=1041970
ONS_HOSTNAME: 030241.0037000.sgtin.id.example.com

Some representations contain all information about the tag (BINARY and TAG_URI), while other representations contain only partial information (PURE_IDENTITY). It is therefore possible to translate a tag from its TAG_URI to its PURE_IDENTITY representation, but it is not possible to translate in the other direction without more information being provided, namely the filter value must be supplied.

EPCglobal released a Tag Data Translation 1.0 (TDT) standard that defines how to decode, encode, and translate between various EPC RFID tag representations. Decoding refers to parsing a given representation into field/value pairs, and encoding refers to reconstructing representations from these fields. Translating refers to decoding one representation and instantly encoding it into another.TDT defines this information using a set of XML files, each referred to as a scheme. For example, the SGTIN-64 scheme defines how to decode, encode, and translate between various SGTIN-64 representations, such as binary and pure identity. For details about the EPCglobal TDT schema, see the EPCglobal Tag Data Translation specification.

A key feature of the TDT specification is its ability to define any EPC scheme using the same XML schema. This approach creates a standard way of defining EPC metadata that RFID applications can then use to write their parsers, encoders, and translators. When the application is written according to the TDT specification, it must be able to update its set of EPC tag schemes and modify its action according to the metadata.

The Oracle Database metadata structure is similar, but not identical to the TDT standard. To fit the EPCglobal TDT specification, the Oracle RFID package must be able to ingest any TDT compatible scheme and seamlessly translate it into the generic Oracle Database defined metadata. See the EPC_TO_ORACLE Function in Table 17-4 for more information.

Reconstructing tag representation from fields, or in other words, encoding tag data into predefined representations is easily accomplished using the MGD_ID.format function. Likewise, the decoding of tag representations into MGD_ID objects and then encoding these objects into tag representations is also easily accomplished using the MGDID.translate function. See the FORMAT Member Function and the TRANSLATE Static Function in Table 17-3 for more information.

Because the EPCglobal TDT standard is powerful and highly extensible, the Oracle RFID standard metadata is a close relative of the TDT specification. See "Oracle Database Tag Data Translation Schema" for the actual Oracle Database TDT XML schema. Developers can refer to this Oracle Database TDT XML schema to define their own tag structures.

Figure 17-2 shows the Oracle Database Tag Data Translation Markup Language Schema diagram.

Figure 17-2 Oracle Database Tag Data Translation Markup Language Schema

Oracle Database Tag Data Translation Markup Language Schema
Description of "Figure 17-2 Oracle Database Tag Data Translation Markup Language Schema"

The top level element in a tag data translation xml is 'scheme'. Each scheme defines various tag encoding representations, or levels. SGTIN-64 and GID-96 are examples of tag encoding schemes, and BINARY or PURE_IDENTITY are examples of levels within these schemes. Each level has a set of options that define how to parse various representations into fields, and rules that define how to derive values for fields that require additional work, such as an external table lookup or the concatenation of other parsed out fields. See the EPCGlobal Tag Translator Specification for more information.

What is the Identity Code Package?

The Identity Code Package provides an extensible framework that supports the current RFID tags with the standard family of EPC bit encodings for the supported encoding types and new and evolving tag encodings that are not included in the current EPC standard.

The Identity Code Package defines these ADTs:

  • MGD_ID -- defines these (see MGD_ID ADT in Table 17-2 for more information):

    • Two attributes, category_id and components.

    • Four MGD_ID constructor functions for constructing identity code type objects to represent RFID tags.

    • A set of member subprograms for operating on these ADTs.

    "Using the Identity Code Package" describes how to use these ADTs and member functions.

    "Identity Code Package Types" and "DBMS_MGD_ID_UTL Package" briefly describe the reference information for these ADTs along with a set of utility subprograms. See Oracle Database PL/SQL Packages and Types Reference for detailed reference information.

  • MGD_ID_COMPONENT — defines two attributes, comp_name, which identifies the name of the component and comp_value, which identifies the components value.

  • MGD_ID_COMPONENT_VARRAY — defines an array type that can store up to 128 elements of MGD_IDCOMPONENT type, which is used in two constructor functions for creating an identity code type object with a list of components.

The Identity Code Package supports EPC spec v1.1 by supplying the predefined EPC_ENCODING_CATEGORY encoding_category attribute definition with its bit-encoding structures for the supported encoding types. This information is stored as meta information in the supplied encoding metadata views, MGD_USR_ID_CATEGORY, MGD_USR_ID_SCHEME, the read-only views MGD_ID_CATEGORY, MGD_ID_SCHEME, and their underlying tables: MGD_ID_CATEGORY_TAB, MGD_ID_SCHEME_TAB, MGD_ID_XML_VALIDATOR. See these topics and files for more information:

  • "Electronic Product Code (EPC) Concepts" describes the EPC spec v1.1 product code and its family of coding schemes.

  • "Identity Code Metadata Tables and Views" describes the structure of the identity code meta tables and views and how metadata are used by the Identity Code Package to interpret the various RFID tags.

  • The mgdmeta.sql file describes the meta table data for the EPC_ENCODING_CATEGORY categories and each of its specific encoding schemes.

After storing many thousands of RFID tags into the column of MGD_ID column type of your user table, you can improve query performance by creating an index on this column. See these topics for more information:

The Identity Code Package provides a utility package that consists of various utility subprograms. See this topic for more information:

  • "Identity Code Package Types" and "DBMS_MGD_ID_UTL Package" describes each of the member subprograms. A proxy utility sets and removes proxy information. A metadata utility gets a category ID, refreshes a tag scheme for a category, removes a tag scheme for a category, and validates a tag scheme. A conversion utility translates standard EPCglobal Tag Data Translation (TDT) files into Oracle Database TDT files.

The Identity Code Package is extensible and lets you create your own identity code types for your new or evolving RFID tags. You can define your identity code types, catagory_id attribute values, and components structures for your own encoding types. See these topics for more information:

  • "Creating a Category of Identity Codes" describes how to create your own identity codes by first registering the encoding category, and then registering the schemes associated to the encoding category.

  • "Identity Code Metadata Tables and Views" describes the structure of the identity code meta tables and views and how to register meta information by storing it in the supplied metadata tables and views.

Using the Identity Code Package

Topics:

Storing RFID Tags in Oracle Database Using MGD_ID ADT

Topics:

Creating a Table with MGD_ID Column Type and Storing EPC Tag Encodings in the Column

You can create tables using MGD_ID as the column type to represent RFID tags, for example:

Example 1. Using the MGD_ID column type:

CREATE TABLE Warehouse_info (
             Code          MGD_ID,
             Arrival_time  TIMESTAMP, 
             Location      VARCHAR2(256);
             ...); 

SQL*Plus command:

describe warehouse_info;

Result:

Name                                      Null?    Type
----------------------------------------- -------- ----------------------------
CODE                                      NOT NULL MGDSYS.MGD_ID
ARRIVAL_TIME                                       TIMESTAMP(6)
LOCATION                                           VARCHAR2(256)

Constructing MGD_ID Objects to Represent RFID Tags

There are several ways to construct MGD_ID objects:

Constructing an MGD_ID Object (SGTIN-64) Passing in the Category ID and a List of Components

If a RFID tag complies to the EPC standard, an MGD_ID object can be created using its category ID and a list of components. For example:

call DBMS_MGD_ID_UTL.set_proxy('www-proxy.us.oracle.com', '80');
call DBMS_MGD_ID_UTL.refresh_category('1');
select MGD_ID ('1', 
               MGD_ID_COMPONENT_VARRAY(
               MGD_ID_COMPONENT('companyprefix','0037000'),
               MGD_ID_COMPONENT('itemref','030241'),
               MGD_ID_COMPONENT('serial','1041970'),
               MGD_ID_COMPONENT('schemes','SGTIN-64')
              )
             ) from DUAL;
call DBMS_MGD_ID_UTL.remove_proxy();

@constructor11.sql
.
.
.
MGD_ID ('1', MGD_ID_COMPONENT_VARRAY
        (MGD_ID_COMPONENT('companyprefix', '0037000'),
        MGD_ID_COMPONENT('itemref', '030241'), 
        MGD_ID_COMPONENT('serial', '1041970'),
        MGD_ID_COMPONENT('schemes', 'SGTIN-64')))
.
.
.
Constructing an MGD_ID object (SGTIN-64) and Passing in the Category ID, the Tag Identifier, and the List of Additional Required Parameters

Use this constructor when there is a list of additional parameters required to create the MGD_ID object. For example:

call DBMS_MGD_ID_UTL.set_proxy('www-proxy.us.oracle.com', '80');
call DBMS_MGD_ID_UTL.refresh_category('1');
select MGD_ID('1', 
              'urn:epc:id:sgtin:0037000.030241.1041970', 
              'filter=3;scheme=SGTIN-64') from DUAL;
call DBMS_MGD_ID_UTL.remove_proxy();


@constructor22.sql
.
.
.
MGD_ID('1', MGD_ID_COMPONENT_VARRAY(MGD_ID_COMPONENT('filter', '3'), 
       MGD_ID_COMPONENT('schemes', 'SGTIN-64'), 
       MGD_ID_COMPONENT('companyprefixlength', '7'), 
       MGD_ID_COMPONENT('companyprefix', '0037000'), 
       MGD_ID_COMPONENT('scheme', 'SGTIN-64'), 
       MGD_ID_COMPONENT('serial', '1041970'), 
       MGD_ID_COMPONENT('itemref', '030241')))
.
.
.
Constructing an MGD_ID object (SGTIN-64) and Passing in the Category Name, Category Version (if null, then the latest version is used), and a List of Components

Use this constructor when a category version must be specified along with a category ID and a list of components. For example:

call DBMS_MGD_ID_UTL.set_proxy('www-proxy.us.oracle.com', '80');
call DBMS_MGD_ID_UTL.refresh_category
  (DBMS_MGD_ID_UTL.get_category_id('EPC', NULL));
select MGD_ID('EPC', NULL, 
              MGD_ID_COMPONENT_VARRAY(
              MGD_ID_COMPONENT('companyprefix','0037000'),
              MGD_ID_COMPONENT('itemref','030241'),
              MGD_ID_COMPONENT('serial','1041970'),
              MGD_ID_COMPONENT('schemes','SGTIN-64')
             )
            ) from DUAL;
call DBMS_MGD_ID_UTL.remove_proxy();

@constructor33.sql
.
.
.
MGD_ID('1', MGD_ID_COMPONENT_VARRAY
             (MGD_ID_COMPONENT('companyprefix', '0037000'),
              MGD_ID_COMPONENT('itemref', '030241'), 
              MGD_ID_COMPONENT('serial', '1041970'),
              MGD_ID_COMPONENT('schemes', 'SGTIN-64')
             )
       )
.
.
.
Constructing an MGD_ID object (SGTIN-64) and Passing in the Category Name and Category Version, the Tag Identifier, and the List of Additional Required Parameters

Use this constructor when the category version and an additional list of parameters is required.

call DBMS_MGD_ID_UTL.set_proxy('www-proxy.us.oracle.com', '80');
call DBMS_MGD_ID_UTL.refresh_category
  (DBMS_MGD_ID_UTL.get_category_id('EPC', NULL));
select MGD_ID('EPC', NULL,
              'urn:epc:id:sgtin:0037000.030241.1041970', 
              'filter=3;scheme=SGTIN-64') from DUAL;
call DBMS_MGD_ID_UTL.remove_proxy();

@constructor44.sql
.
.
.
MGD_ID('1', MGD_ID_COMPONENT_VARRAY
       (MGD_ID_COMPONENT('filter', '3'),
        MGD_ID_COMPONENT('schemes', 'SGTIN-64'), 
        MGD_ID_COMPONENT('companyprefixlength', '7'), 
        MGD_ID_COMPONENT('companyprefix', '0037000'), 
        MGD_ID_COMPONENT('scheme', 'SGTIN-64'), 
        MGD_ID_COMPONENT('serial', '1041970'), 
        MGD_ID_COMPONENT('itemref', '030241')
       )
      )
.
.
.

Inserting an MGD_ID Object into a Database Table

This example shows how to populate the WAREHOUSE_INFO table by inserting each MGD_ID object into the table along with the additional column values:

call DBMS_MGD_ID_UTL.set_proxy('www-proxy.us.oracle.com', '80');

call DBMS_MGD_ID_UTL.refresh_category
  (DBMS_MGD_ID_UTL.get_category_id('EPC', NULL));

INSERT INTO WAREHOUSE_INFO (code, arrival_time, location)
   values (MGDSYS.MGD_ID ('EPC',
                          NULL,
                          'urn:epc:id:sgtin:0037000.030241.1041970',
                          null
                         ),
           SYSDATE,
           'SHELF_123');

INSERT INTO WAREHOUSE_INFO (code, arrival_time, location)
  values (MGDSYS.MGD_ID ('EPC',
                         NULL,
                         'urn:epc:id:sgtin:0037000.053021.1012353',
                         null
                        ),
          SYSDATE,
          'SHELF_456');
INSERT INTO WAREHOUSE_INFO (code, arrival_time, location)
  values (MGDSYS.MGD_ID ('EPC',
                         NULL,
                         'urn:epc:id:sgtin:0037000.020140.10174832',
                         null
                        ),
          SYSDATE,
          'SHELF_1034');

COMMITT;
call DBMS_MGD_ID_UTL.remove_proxy();

Querying MGD_ID Column Type

There are three ways to query on MGD_ID column type.

  • Query the MGD_ID column type. Find all items with item reference 030241.

    SELECT location, wi.code.get_component('itemref') as itemref, 
                     wi.code.get_component('serial') as serial 
    FROM warehouse_info wi WHERE wi.code.get_component('itemref') = '030241';
    
    LOCATION       |ITEMREF   |SERIAL
    ---------------|----------|----------
    SHELF_123      |030241    |1041970
    
  • Query using the member functions of the MGD_ID ADT. Select the pure identity representations of all RFID tags in the table.

    SELECT wi.code.format(null,'PURE_IDENTITY')
       as PURE_IDENTITY FROM warehouse_info wi;
    
    PURE_IDENTITY
    -------------------------------------------------------------------------------
    urn:epc:id:sgtin:0037000.030241.1041970
    urn:epc:id:gid:0037000.053021.1012353
    urn:epc:id:sgtin:0037000.020140.10174832
    

    See "Using the get_component Function with the MGD_ID Object" for more information and see Table 17-3 for a list of member functions.

Building a Function-Based Index Using the Member Functions of the MGD_ID Column Type

You can improve the performance of queries based on a certain component of the RFID tags by creating a function-based index that uses the get_component member function or its variation convenience functions. For example:

CREATE INDEX warehouseinfo_idx2  
  on warehouse_info(code.get_component('itemref'));

You can also improve the performance of queries based on a certain component of the RFID tags by creating a bitmap function based index that uses the get_component member function or its variation convenience functions. For example:

CREATE BITMAP INDEX warehouseinfo_idx3  
  on warehouse_info(code.get_component('serial'));

Using MGD_ID ADT Functions

The MGD_ID ADT contains member subprograms that operate on these ADTs. See Table 17-2 for MGD_ID_COMPONENT, MGD_ID_COMPONENT_VARRAY, MGD_ID ADT reference information. See the mgdtyp.sql file for the MGD_ID ADT definition and its member subprograms.

Topics:

Using the get_component Function with the MGD_ID Object

The get_component function is defined as follows:

MEMBER FUNCTION get_component(component_name IN VARCHAR2)
   RETURN VARCHAR2 DETERMINISTIC,

Each component in a identity code has a name. It is defined when the code type is registered. See "Defining a Category of Identity Codes and Adding Encoding Schemes to an Existing Category" for more information about how to create a identity code type.

The get_component function takes the name of the component, component_name as a parameter, uses the metadata registered in the metadata table to analyze the identity code, and returns the component with the name component_name.

The get_component function can be used in a SQL query. For example, find the current location of the coded item for the component named itemref; or, in other words find all items with the item reference of 03024. Because the code tag has encoded the "itemref" as a component, you can use this SQL query:

SELECT location,
       w.code.get_component('itemref') as itemref,
       w.code.get_component('serial')  as serial
FROM   warehouse_info w
       WHERE  w.code.get_component('itemref')  = '030241';

LOCATION       |ITEMREF   |SERIAL
---------------|----------|----------
SHELF_123      |030241    |1041970

See Table 17-3 for a list of other member functions.

Parsing Tag Data from Standard Representations

RFID readers read the bit strings stored in the tags. The tag data and other information, such as the reader ID and the time stamp, first go through an edge server to be processed, normalized, and preliminarily filtered. Then, in many application scenarios, the information must be persistently stored and later on be retrieved. The Oracle Database understands the code structures representations of various EPC tags as described in Table 17-1 because these code representation schemes defined in the EPC Standard are pre-registered. This gives the Oracle Database the ability to understand all the EPC code schemes and parse various tag representations into fields. Users can also register their own coding structures for the identity codes that use other encoding technologies. In this way the system is extensible.

As mentioned in "Identity Concepts", each of the EPCGlobal tag schemes (GID-96, SGTIN-64, SGTIN-96, and so forth) has various representations with the most often used ones being BINARY, TAG_URI, and PURE_IDENTITY.

Some representations contain all the information about the tag (BINARY and TAG_URI), while representations contain only partial information (PURE_IDENTITY). It is therefore possible to translate a tag from it's TAG_URI to it's PURE_IDENTITY representation, but it is not possible to translate in the other direction (PURE_IDENTITY to TAG_URI) without supplying more information, namely the filter value.

One MGD_ID constructor takes in four fields, the category name (such as EPC), the category version, the tag identifier (for EPC, the identifier must be in a representation previously described), and a parameter list for any additional parameters that may be required to parse the tag representation. For example, this code creates an MGD_ID object from its BINARY representation.

SELECT MGD_ID 
   ('EPC',
    null,
    '1001100000000000001000001110110001000010000011111110011000110010',
    null
   )
   AS NEW_RFID_CODE FROM DUAL;

NEW_RFID_CODE(CATEGORY_ID, COMPONENTS(NAME, VALUE))
--------------------------------------------------------------------------------
MGD_ID ('1', 
        MGD_ID_COMPONENT_VARRAY(MGD_ID_COMPONENT('filter', '3'), 
        MGD_ID_COMPONENT('schemes', 'SGTIN-64'), 
        MGD_ID_COMPONENT('companyprefixlength', '7'), 
        MGD_ID_COMPONENT('companyprefix', '0037000'), 
        MGD_ID_COMPONENT('companyprefixindex', '1'), 
        MGD_ID_COMPONENT('serial', '1041970'), 
        MGD_ID_COMPONENT('itemref', '030241')
       )
      )

For example, an identical object can be created if the call is done with the TAG_URI representation of the tag as follows with the addition of the value of the filter value:

SELECT MGD_ID ('EPC',
                null,
                'urn:epc:tag:sgtin-64:3.0037000.030241.1041970',
                null
              )
  as NEW_RFID_CODE FROM DUAL;

NEW_RFID_CODE(CATEGORY_ID, COMPONENTS(NAME, VALUE))
--------------------------------------------------------------------------------
MGD_ID ('1', 
        MGD_ID_COMPONENT_VARRAY (
         ( MGD_ID_COMPONENT('filter', '3'), 
           MGD_ID_COMPONENT('schemes', 'SGTIN-64'), 
           MGD_ID_COMPONENT('companyprefixlength', '7'), 
           MGD_ID_COMPONENT('companyprefix', '0037000'), 
           MGD_ID_COMPONENT('serial', '1041970'), 
           MGD_ID_COMPONENT('itemref', '030241')
         )
       )

Reconstructing Tag Representations from Fields

Another useful feature of the Identity Code package is the ability to encode tag data into predefined representations. For example, a warehouse wants to send certain inventory to a retailer, but first it wants to send an invoice that tells the retailer what inventory to expect. The invoice can be a list of pure identity URIs that the warehouse intends to send. If all the inventory in the WAREHOUSE_INFO table is to be sent, this example constructs the desired URIs:

SELECT wi.code.format (null,'PURE_IDENTITY') 
  as PURE_IDENTITY FROM warehouse_info wi;

PURE_IDENTITY
--------------------------------------------------------------------------------
urn:epc:id:sgtin:0037000.030241.1041970
urn:epc:id:gid:0037000.053021.1012353
urn:epc:id:sgtin:0037000.020140.10174832

Translating Between Tag Representations

The Identity Code package can decode tag representations into MGD_ID objects and encode these objects into tag representations. These two steps can be combined into one step using the MGD_ID.translate function. Static translation allows for the conversion of an RFID tag from one representation to another. For example:

SELECT MGD_ID.translate ('EPC',
                         null,
                         'urn:epc:id:sgtin:0037000.030241.1041970',
                         'filter=3;scheme=SGTIN-64',
                         'BINARY'
                        )
  as BINARY FROM DUAL;

BINARY
--------------------------------------------------------------------------------
1001100000000000001000001110110001000010000011111110011000110010

In this example, the binary representation contains more information than the pure identity representation. Specifically, it also contains the filter value and in this case the scheme value must also be specified to distinguish SGTIN-64 from SGTIN-96. Thus, the function call must provide the missing filter parameter information and specify the scheme name in order for translation call to succeed.

Defining a Category of Identity Codes and Adding Encoding Schemes to an Existing Category

Topics:

Creating a Category of Identity Codes

Because the EPCglobal TDT standard is powerful and highly extensible, the Oracle Database RFID standard metadata is a close relative of the TDT specification. Thus, the Identity Code package is extensible: You can create your own categories and tag structures using generic metadata. To create a category of identity codes, use the DBMS_MGD_ID_UTIL.create_category function.

For example, suppose you want to create a category called MGD_SAMPLE_CATEGORY, which has two types of tags, a CONTRACTOR_TAG and an EMPLOYEE_TAG. This category and its two metadata schemes might be used within a company that must grant different access privileges to people who are full time employees from those who are contractors, and thus require that their security software be able to identify quickly between the two badge types at an RFID reader. This script creates a category named 'MGD_SAMPLE_CATEGORY', with a 1.0 category version, having an agency name as Oracle, with a URI as http://www.oracle.com/mgd/sample. See "Adding Two Metadata Schemes to a Newly Created Category" for an example.

Adding Two Metadata Schemes to a Newly Created Category

Next, create an CONTRACTOR_TAG metadata scheme such as:

<?xml version="1.0" encoding="UTF-8"?>
<TagDataTranslation version="0.04" date="2005-04-18T16:05:00Z" 
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema" 
                    xmlns="oracle.mgd.idcode">
 <scheme name="CONTRACTOR_TAG" optionKey="1" xmlns="">
  <level type="URI" prefixMatch="mycompany.contractor.">
   <option optionKey="1" pattern="mycompany.contractor.([0-9]*).([0-9]*)" 
           grammar="''mycompany.contractor.'' contractorID ''.'' divisionID">
    <field seq="1" characterSet="[0-9]*" name="contractorID"/>
    <field seq="2" characterSet="[0-9]*" name="divisionID"/>
   </option>
  </level>
  <level type="BINARY" prefixMatch="11">
   <option optionKey="1" pattern="11([01]{7})([01]{6})" 
           grammar="''11'' contractorID divisionID ">
    <field seq="1" characterSet="[01]*" name="contractorID"/>
    <field seq="2" characterSet="[01]*" name="divisionID"/>
   </option>
  </level>
 </scheme>
</TagDataTranslation>

The CONTRACTOR_TAG scheme contains two encoding levels, or ways in which the tag can be represented. The first level is URI and the second level is BINARY. The URI representation starts with the prefix "mycompany.contractor." and is then followed by two numeric fields separated by a period. The names of the two fields are contractorID and divisionID. The pattern field in the option tag defines the parsing structure of the tag URI representation, and the grammar field defines how to reconstruct the URI representation. The BINARY representation can be understood in a similar fashion. This representation starts with the prefix "01" and is then followed by the same two fields, contractorID and divisionID, this time, in their respective binary formats. Given this XML metadata structure, contractor tags can now be decoded from their URI and BINARY representations and the resulting fields can be re-encoded into one of these representations.

The EMPLOYEE_TAG scheme is defined in a similar fashion and is shown as follows.

<?xml version="1.0" encoding="UTF-8"?>
<TagDataTranslation version="0.04" date="2005-04-18T16:05:00Z" 
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema" 
                    xmlns="oracle.mgd.idcode">
 <scheme name="EMPLOYEE_TAG" optionKey="1" xmlns="">
  <level type="URI" prefixMatch="mycompany.employee.">
   <option optionKey="1" pattern="mycompany.employee.([0-9]*).([0-9]*)" 
           grammar="''mycompany.employee.'' employeeID ''.'' divisionID">
    <field seq="1" characterSet="[0-9]*" name="employeeID"/>
    <field seq="2" characterSet="[0-9]*" name="divisionID"/>
   </option>
  </level>
  <level type="BINARY" prefixMatch="01">
   <option optionKey="1" pattern="01([01]{7})([01]{6})" 
           grammar="''01'' employeeID divisionID ">
    <field seq="1" characterSet="[01]*" name="employeeID"/>
    <field seq="2" characterSet="[01]*" name="divisionID"/>
   </option>
  </level>
 </scheme>
</TagDataTranslation>;

To add these schemes to the category ID previously created, use the DBMS_MGD_ID_UTIL.add_scheme function.

This script creates the MGD_SAMPLE_CATEGORY category, adds a contractor scheme and an employee scheme to the MGD_SAMPLE_CATEGORY category, validates the MGD_SAMPLE_CATEGORY scheme, tests the tag translation of the contractor scheme and the employee scheme, then removes the contractor scheme, tests the tag translation of the contractor scheme and this returns the expected exception for the removed contractor scheme, tests the tag translation of the employee scheme and this returns the expected values, then removes the MGD_SAMPLE_CATEGORY category:

--contents of add_scheme2.sql
SET LINESIZE 160
CALL DBMS_MGD_ID_UTL.set_proxy('www-proxy.us.oracle.com', '80');
---------------------------------------------------------------------
---CREATE CATEGORY, ADD_SCHEME, REMOVE_SCHEME, REMOVE_CATEGORY-------
---------------------------------------------------------------------
DECLARE
  amt          NUMBER;
  buf          VARCHAR2(32767);
  pos          NUMBER;
  tdt_xml      CLOB;
  validate_tdtxml VARCHAR2(1042);
  category_id  VARCHAR2(256);
BEGIN
  -- remove the testing category if it exists
  DBMS_MGD_ID_UTL.remove_category('MGD_SAMPLE_CATEGORY', '1.0');
  -- create the testing category 'MGD_SAMPLE_CATEGORY', version 1.0
  category_id := DBMS_MGD_ID_UTL.CREATE_CATEGORY('MGD_SAMPLE_CATEGORY', '1.0', 'Oracle', 
'http://www.oracle.com/mgd/sample');
  -- add contractor scheme to the category
  DBMS_LOB.CREATETEMPORARY(tdt_xml, true);
  DBMS_LOB.OPEN(tdt_xml, DBMS_LOB.LOB_READWRITE);
 
  buf := '<?xml version="1.0" encoding="UTF-8"?>
<TagDataTranslation version="0.04" date="2005-04-18T16:05:00Z" 
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema" 
                    xmlns="oracle.mgd.idcode">
 <scheme name="CONTRACTOR_TAG" optionKey="1" xmlns="">
  <level type="URI" prefixMatch="mycompany.contractor.">
   <option optionKey="1" pattern="mycompany.contractor.([0-9]*).([0-9]*)" 
           grammar="''mycompany.contractor.'' contractorID ''.'' divisionID">
    <field seq="1" characterSet="[0-9]*" name="contractorID"/>
    <field seq="2" characterSet="[0-9]*" name="divisionID"/>
   </option>
  </level>
  <level type="BINARY" prefixMatch="11">
   <option optionKey="1" pattern="11([01]{7})([01]{6})" 
           grammar="''11'' contractorID divisionID ">
    <field seq="1" characterSet="[01]*" name="contractorID"/>
    <field seq="2" characterSet="[01]*" name="divisionID"/>
   </option>
  </level>
 </scheme>
</TagDataTranslation>';

  amt := length(buf);
  pos := 1;
  DBMS_LOB.WRITE(tdt_xml, amt, pos, buf);
  DBMS_LOB.CLOSE(tdt_xml);

  DBMS_MGD_ID_UTL.ADD_SCHEME(category_id, tdt_xml);

  -- add employee scheme to the category
  DBMS_LOB.CREATETEMPORARY(tdt_xml, true);
  DBMS_LOB.OPEN(tdt_xml, DBMS_LOB.LOB_READWRITE);
 
  buf := '<?xml version="1.0" encoding="UTF-8"?>
<TagDataTranslation version="0.04" date="2005-04-18T16:05:00Z" 
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema" 
                    xmlns="oracle.mgd.idcode">
 <scheme name="EMPLOYEE_TAG" optionKey="1" xmlns="">
  <level type="URI" prefixMatch="mycompany.employee.">
   <option optionKey="1" pattern="mycompany.employee.([0-9]*).([0-9]*)" 
           grammar="''mycompany.employee.'' employeeID ''.'' divisionID">
    <field seq="1" characterSet="[0-9]*" name="employeeID"/>
    <field seq="2" characterSet="[0-9]*" name="divisionID"/>
   </option>
  </level>
  <level type="BINARY" prefixMatch="01">
   <option optionKey="1" pattern="01([01]{7})([01]{6})" 
           grammar="''01'' employeeID divisionID ">
    <field seq="1" characterSet="[01]*" name="employeeID"/>
    <field seq="2" characterSet="[01]*" name="divisionID"/>
   </option>
  </level>
 </scheme>
</TagDataTranslation>';

  amt := length(buf);
  pos := 1;
  DBMS_LOB.WRITE(tdt_xml, amt, pos, buf);
  DBMS_LOB.CLOSE(tdt_xml);
  DBMS_MGD_ID_UTL.ADD_SCHEME(category_id, tdt_xml);

  -- validate the scheme
  dbms_output.put_line('Validate the MGD_SAMPLE_CATEGORY Scheme');
  validate_tdtxml := DBMS_MGD_ID_UTL.validate_scheme(tdt_xml);
  dbms_output.put_line(validate_tdtxml);
  dbms_output.put_line('Length of scheme xml is: '||DBMS_LOB.GETLENGTH(tdt_xml));

  -- test tag translation of contractor scheme
  dbms_output.put_line(
    mgd_id.translate('MGD_SAMPLE_CATEGORY', NULL, 
                     'mycompany.contractor.123.45', 
                     NULL, 'BINARY'));

  dbms_output.put_line(
    mgd_id.translate('MGD_SAMPLE_CATEGORY', NULL, 
                     '111111011101101',
                     NULL, 'URI'));

  -- test tag translation of employee scheme
  dbms_output.put_line(
    mgd_id.translate('MGD_SAMPLE_CATEGORY', NULL, 
                     'mycompany.employee.123.45', 
                     NULL, 'BINARY'));

  dbms_output.put_line(
    mgd_id.translate('MGD_SAMPLE_CATEGORY', NULL, 
                     '011111011101101',
                     NULL, 'URI'));

  DBMS_MGD_ID_UTL.REMOVE_SCHEME(category_id, 'CONTRACTOR_TAG');

  -- Test tag translation of contractor scheme. Doesn't work any more.
  BEGIN
    dbms_output.put_line(
      mgd_id.translate('MGD_SAMPLE_CATEGORY', NULL, 
                       'mycompany.contractor.123.45', 
                       NULL, 'BINARY'));

    dbms_output.put_line(
      mgd_id.translate('MGD_SAMPLE_CATEGORY', NULL, 
                       '111111011101101',
                       NULL, 'URI'));
  EXCEPTION 
    WHEN others THEN
      dbms_output.put_line('Contractor tag translation failed: '||SQLERRM);
  END;

  -- Test tag translation of employee scheme. Still works.
  BEGIN
    dbms_output.put_line(
      mgd_id.translate('MGD_SAMPLE_CATEGORY', NULL, 
                       'mycompany.employee.123.45', 
                       NULL, 'BINARY'));
    dbms_output.put_line(
      mgd_id.translate('MGD_SAMPLE_CATEGORY', NULL, 
                       '011111011101101',
                       NULL, 'URI'));
  EXCEPTION 
    WHEN others THEN
      dbms_output.put_line('Employee tag translation failed: '||SQLERRM);
  END;

  -- remove the testing category, which also removes all the associated schemes
  DBMS_MGD_ID_UTL.remove_category('MGD_SAMPLE_CATEGORY', '1.0');
END;
/
SHOW ERRORS;
call DBMS_MGD_ID_UTL.remove_proxy();

@add_scheme3.sql
.
.
.
Validate the MGD_SAMPLE_CATEGORY Scheme
EMPLOYEE_TAG;URI,BINARY;divisionID,employeeID
Length of scheme xml is: 933
111111011101101
mycompany.contractor.123.45
011111011101101
mycompany.employee.123.45
Contractor tag translation failed: ORA-55203: Tag data translation level not found
ORA-06512: at "MGDSYS.DBMS_MGD_ID_UTL", line 54
ORA-06512: at "MGDSYS.MGD_ID", line 242
ORA-29532: Java call terminated by uncaught Java
exception: oracle.mgd.idcode.exceptions.TDTLevelNotFound: Matching level not
found for any configured scheme
011111011101101
mycompany.employee.123.45
.
.
.

Identity Code Package Types

Table 17-2 describes the Identity Code Package ADTs.

Table 17-2 Identity Code Package ADTs

ADT NameDescription

MGD_ID_COMPONENT ADT

A data type that specifies the name and value pair attributes that define a component.

MGD_ID_COMPONENT_VARRAY ADT

A data type that specifies a list of up to 128 components as name-value attribute pairs used in two constructor functions for creating an identity code type object.

MGD_ID ADT

Represents an identity code type that specifies the category identifier for the code category for this identity code and its list of components.


Table 17-3 describes the subprograms in the MGD_ID ADT.

All the values and names passed to the subprograms defined in the MGD_ID ADT are case-insensitive unless otherwise noted. To preserve case, enclose values in double quotation marks.

Table 17-3 MGD_ID ADT Subprograms

SubprogramDescription

MGD_ID Constructor Function

Creates an identity code type object, MGD_ID, and returns self.

FORMAT Member Function

Returns a representation of an identity code given an MGD_ID component.

GET_COMPONENT Member Function

Returns the value of an MGD_ID component.

TO_STRING Member Function

Concatenates the category_id parameter value with the components name-value attribute pair.

TRANSLATE Static Function

Translates one MGD_ID representation of an identity code into a different MGD_ID representation.


DBMS_MGD_ID_UTL Package

Table 17-4 describes the Utility subprograms in the DBMS_MGD_ID_UTL package.

All the values and names passed to the subprograms defined in the MGD_ID ADT are case-insensitive unless otherwise noted. To preserve case, enclose values in double quotation marks.

Table 17-4 DBMS_MGD_ID_UTL Package Utility Subprograms

SubprogramDescription

ADD_SCHEME Procedure

Adds a tag data translation scheme to an existing category.

CREATE_CATEGORY Function

Creates a category or a version of a category.

EPC_TO_ORACLE Function

Converts the EPCglobal tag data translation (TDT) XML to Oracle Database tag data translation XML.

GET_CATEGORY_ID Function

Returns the category ID given the category name and the category version.

GET_COMPONENTS Function

Returns all relevant separated component names separated by semicolon (';') for the specified scheme.

GET_ENCODINGS Function

Returns a list of semicolon (';') separated encodings (formats) for the specified scheme.

GET_JAVA_LOGGING_LEVEL Function

Returns an integer representing the current Java trace logging level.

GET_PLSQL_LOGGING_LEVEL Function

Returns an integer representing the current PL/SQL trace logging level.

GET_SCHEME_NAMES Function

Returns a list of semicolon (';') separated scheme names for the specified category.

GET_TDT_XML Function

Returns the Oracle Database tag data translation XML for the specified scheme.

GET_VALIDATOR Function

Returns the Oracle Database tag data translation schema.

REFRESH_CATEGORY Function

Refreshes the metadata information about the Java stack for the specified category.

REMOVE_CATEORY Function

Removes a category including all the related TDT XML.

REMOVE_PROXY Procedure

Unsets the host and port of the proxy server.

REMOVE_SCHEME Procedure

Removes the tag scheme for a category.

SET_JAVA_LOGGING_LEVEL Procedure

Sets the Java logging level.

SET_PLSQL_LOGGING_LEVEL Procedure

Sets the PL/SQL tracing logging level.

SET_PROXY Procedure

Sets the host and port of the proxy server for Internet access.

VALIDATE_SCHEME Function

Validates the input tag data translation XML against the Oracle Database tag data translation schema.


Identity Code Metadata Tables and Views

This topic describes the structure of identity code metadata tables and views and explains how the metadata are used by the Identity Code Package to interpret the various RFID tags. The creation of these meta tables, views, and triggers is done automatically during the Identity Code Package installation.

Encoding metadata views are used to store encoding categories and schemes. Application developers can insert the meta information of their own identity codes into these views. The MGD_ID ADT is designed to understand the encodings if the metadata for the encodings are stored in the meta tables. If an application developer only uses the encodings defined in the EPC specification v1.1, the developer does not have to worry about the meta tables because product codes specified in EPC spec v1.1 are predefined.

There are two encoding metadata views.

  • user_mgd_id_category — this view is used to store the encoding category information defined by the session user.

  • user_mgd_id_scheme — this view is used to store the encoding type information defined by the session user.

In addition, these read-only views are defined for a user to query the system predefined encoding metadata and the metadata defined by the user:

  • mgd_id_category — this view is used to query the encoding category information defined by the system or the session user

  • mgd_id_scheme — this view is used to query the encoding type information defined by the system or the session user.

The underlying metadata tables for the preceding views are:

  • mgd_id_xml_validator

  • mgd_id_category_tab

  • mgd_id_scheme_tab

Users other than the Identity Code Package system users cannot operate on these tables. Users must not use the metadata tables directly. They must use the read only views and the metadata functions described in the DBMS_MGD_ID_UTL package.


See Also:

Oracle Database PL/SQL Packages and Types Reference for information about the DBMS_MGD_ID_UTL package

Metadata View Definitions

Table 17-5, Table 17-6, Table 17-7, and Table 17-8 describe the metadata view definitions for the MGD_ID_CATEGORY, USER_ID_CATEGORY, MGD_ID_SCHME, and USER_MGD_ID_SCHME respectively as defined in the mgdview.sql file.

Table 17-5 Definition and Description of the MGD_ID_CATEGORY Metadata View

Column NameData TypeDescription

CATEGORY_ID

NUMBER(4)

Category identifier

CATEGORY_NAME

VARCHAR2(256)

Category name

AGENCY

VARCHAR2(256)

Organization that defined the category

VERSION

VARCHAR2(256)

Category version

URI

VARCHAR2(256)

URI that describes the category


Table 17-6 Definition and Description of the USER_MGD_ID_CATEGORY Metadata View

Column NameData TypeDescription

CATEGORY_ID

NUMBER(4)

Category identifier

CATEGORY_NAME

VARCHAR2(256)

Category name

AGENCY

VARCHAR2(256)

Organization that defined the category

VERSION

VARCHAR2(256)

Category version

URI

VARCHAR2(256)

URI that describes the category


Table 17-7 Definition and Description of the MGD_ID_SCHEME Metadata View

Column NameData TypeDescription

CATEGORY_ID

NUMBER(4)

Category identifier

TYPE_NAME

VARCHAR2(256)

Encoding scheme name, for example, SGTIN-96, GID-96, and so forth

TDT_XML

CLOB

Tag data translation XML for this encoding scheme

ENCODINGS

VARCHAR2(256)

Encodings separated by a comma (,), for example, LEGACY, TAG_ENCODING, PURE_IDENTITY, BINARY (for SGTIN-96)

COMPONENTS

VARCHAR2(1024)

Relevant component names, extracted from each level and then combined. Each is separated by a comma (,). For example, objectclass, generalmanager, serial (for GID-96)


Table 17-8 Definition and Description of the USER_MGD_ID_SCHEME Metadata View

Column NameData TypeDescription

CATEGORY_ID

NUMBER(4)

Category identifier

TYPE_NAME

VARCHAR2(256)

Encoding scheme name, for example, SGTIN-96, GID-96, and so forth

TDT_XML

CLOB

Tag data translation XML for this encoding scheme

ENCODINGS

VARCHAR2(256)

Encodings separated by a comma (,), for example, LEGACY, TAG_ENCODING, PURE_IDENTITY, BINARY (for SGTIN-96)

COMPONENTS

VARCHAR2(1024)

Relevant component names, extracted from each level and then combined. Each is separated by a comma (,). For example, objectclass, generalmanager, serial (for GID-96)


Electronic Product Code (EPC) Concepts

Topics:

RFID Technology and EPC v1.1 Coding Schemes

Radio Frequency Identification (RFID) technology continues to gain momentum with suppliers, distributors, manufacturers, and retailers for its ability to eliminate line-of-site processes and automate critical supply chain transactions. Electronic Product Code (EPC), an identification scheme for universally identifying objects using RFID tags and other means, is gaining widespread acceptance as an emerging standard. Its capabilities enable companies to reduce warehouse and distribution costs through improved inventory control and extended supply chain visibility.

The standardized EPC Identifier is a metacoding scheme designed to support the needs of various industries. Therefore, the EPC represents a family of coding schemes and a means to make them unique across all possible EPC-compliant tags. EPC Version 1.1 includes these specific coding schemes:

  • General Identifier (GID)

  • Serialized version of the EAN.UCC Global Trade Item Number (GTIN)

  • EAN.UCC Serial Shipping Container Code (SSCC)

  • EAN.UCC Global Location Number (GLN)

  • EAN.UCC Global Returnable Asset Identifier (GRAI)

  • EAN.UCC Global Individual Asset Identifier (GIAI)

RFID applications require the storage of a large volume of EPC data into a database. The efficient use of EPC data also requires that the database recognizes the different coding schemes of EPC data.

EPC is an emerging standard. It does not cover all the numbering schemes used in the various industries and is itself still evolving (the changes from EPC version 1.0 to EPC version 1.1 are significant).

Identity Code Package empowers the Oracle Database with the knowledge to recognize EPC coding schemes. It makes the Oracle Database a database system that not only provides efficient storage and component level retrieval for EPC data, but also has features to support EPC data encoding and decoding, and conversion between bit encoding and URI encoding.

Identity Code Package provides an extensible framework that allows developers to define their own coding schemes that are not included in the EPC standard. This extensibility feature also makes the Oracle Database adaptable to the evolving future EPC standard.

This chapter describes the requirement of storing, retrieving, encoding and decoding various product codes, including EPC, in an Oracle Database and shows how the Identity Code Package solution meets all these requirements by providing data types, metadata tables, and PL/SQL packages for these purposes.

Product Code Concepts and Their Current Use

This topic describes these product codes:

Electronic Product Code (EPC)

The Electronic Product Code™ (EPC™) is an identification scheme for universally identifying physical objects using Radio Frequency Identification (RFID) tags and other means. The standardized EPC data consists of an EPC (or EPC Identifier) that uniquely identifies an individual object, and an optional Filter Value when judged to be necessary to enable effective and efficient reading of the EPC tags. In addition to this standardized data, certain classes of EPC tags allow user-defined data.

The EPC Identifier is a meta-coding scheme designed to support the needs of various industries by accommodating both existing coding schemes where possible and defining schemes where necessary. The various coding schemes are referred to as Domain Identifiers, to indicate that they provide object identification within certain domains such as a particular industry or group of industries. As such, EPC represents a family of coding schemes (or "namespaces") and a means to make them unique across all possible EPC-compliant tags.

The EPCGlobal EPC Data Standards Version 1.1 defines the abstract content of the Electronic Product Code, and its concrete realization in the form of RFID tags, Internet URIs, and other representations. In EPC Version 1.1, the specific coding schemes include a General Identifier (GID), a serialized version of the EAN.UCC Global Trade Item Number (GTIN®), the EAN.UCC Serial Shipping Container Code (SSCC®), the EAN.UCC Global Location Number (GLN®), the EAN.UCC Global Returnable Asset Identifier (GRAI®), and the EAN.UCC Global Individual Asset Identifier (GIAI®).

EPC Pure Identity

The EPC pure identity is the identity associated with a specific physical or logical entity, independent of any particular encoding vehicle such as an RF tag, bar code or database field. As such, a pure identity is an abstract name or number used to identify an entity. A pure identity consists of the information required to uniquely identify a specific entity, and no more.

EPC Encoding

EPC encoding is a pure identity with more information, such as filter value, rendered into a specific syntax (typically consisting of value fields of specific sizes). A given pure identity might have several possible encodings, such as a Barcode Encoding, various Tag Encodings, and various URI Encodings. Encodings may also incorporate additional data besides the identity (such as the Filter Value used in some encodings), in which case the encoding scheme specifies what additional data it can hold.

For example, the Serial Shipping Container Code (SSCC) format as defined by the EAN.UCC System is an example of a pure identity. An SSCC encoded into the EPC- SSCC 96-bit format is an example of an encoding.

EPC Tag Bit-Level Encoding

EPC encoding on a tag is a string of bits, consisting of a tiered, variable length header followed by a series of numeric fields whose overall length, structure, and function are completely determined by the header value.

EPC Identity URI

The EPC identity URI is a representation of a pure identity as a Uniform Resource Identifier (URI).

EPC Tag URI Encoding

The EPC tag URI encoding represents a specific EPC tag bit-level encoding, for example, urn:epc:tag:sgtin-64:3.0652642.800031.400.

EPC Encoding Procedure

The EPC encoding procedure is used to generate an EPC tag bit-level encoding using various information.

EPC Decoding Procedure

The EPC decoding procedure is used to convert an EPC tag bit-level encoding to an EAN.UCC code.

Global Trade Identification Number (GTIN) and Serializable Global Trade Identification Number (SGTIN)

A Global Trade Identification Number (GTIN) is used for the unique identification of trade items worldwide within the EAN.UCC system. The Serialized Global Trade Identification Number (SGTIN) is an identity type in EPC standard version1.1. It is based on the EAN.UCC GTIN code defined in the General EAN.UCC Specifications [GenSpec5.0]. A GTIN identifies a particular class of object, such as a particular kind of product or SKU. The combination of GTIN and a unique serial number is called a Serialized GTIN (SGTIN).

Serial Shipping Container Code (SSCC)

The Serial Shipping Container Code (SSCC) is defined by the General EAN.UCC Specifications [GenSpec5.0]. The unique identification of logistics units is achieved in the EAN.UCC system by the use of the SSCC. The SSCC is intended for assignment to individual objects.

Global Location Number (GLN) and Serializable Global Location Number (SGLN)

The Global Location Number (GLN) is defined by the General EAN.UCC Specifications [GenSpec5.0]. A GLN can represent either a discrete, unique physical location such as a dock door or a warehouse slot, or an aggregate physical location such as an entire warehouse. In addition, a GLN can represent a logical entity such as an organization that performs a business function (for example, placing an order). The combination of GLN and a unique serial number is called a Serialized GLN (SGLN). However, until the EAN.UCC community determines the appropriate way to extend GLN, the serial number field is reserved and must not be used.

Global Returnable Asset Identifier (GRAI)

A returnable asset is a reusable package or transport equipment of a certain value. Global Returnable Asset Identifier is (GRAI) is defined by the General EAN.UCC Specifications [GenSpec5.0] for the unique identification of a returnable asset.

Global Individual Asset Identifier (GIAI)

The Global Individual Asset Identifier (GIAI) is defined by the General EAN.UCC Specifications [GenSpec5.0]. Unlike the GTIN, the GIAI is intended for assignment to individual objects. Global Individual Asset Identifier (GIAI) is used to uniquely identify an entity that is part of the fixed inventory of a company. The GIAI identifies any fixed asset of an organization.

RFID EPC Network

The RFID EPC network is used to identify, track and locate assets. Physical objects are identified by a unique RFID enabled EPC.

Oracle Database Tag Data Translation Schema

The Oracle Database Tag Data Translation Schema is closely related to the EPCglobal TDT schema, however it is not exact. The Oracle Database TDT is shown as follows:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema targetNamespace="oracle.mgd.idcode" 
   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
     xmlns:tdt="oracle.mgd.idcode" elementFormDefault="qualified" 
       attributeFormDefault="unqualified" version="1.0">

 <xsd:simpleType name="InputFormatList">
  <xsd:restriction base="xsd:string">
   <xsd:enumeration value="BINARY"/>
   <xsd:enumeration value="STRING"/>
  </xsd:restriction>
 </xsd:simpleType>

 <xsd:simpleType name="LevelTypeList">
  <xsd:restriction base="xsd:string">
  </xsd:restriction>
 </xsd:simpleType>
 <xsd:simpleType name="SchemeNameList">
Z  <xsd:restriction base="xsd:string">
  </xsd:restriction>
 </xsd:simpleType>

 <xsd:simpleType name="ModeList">
  <xsd:restriction base="xsd:string">
   <xsd:enumeration value="EXTRACT"/>
   <xsd:enumeration value="FORMAT"/>
  </xsd:restriction>
 </xsd:simpleType>

 <xsd:simpleType name="CompactionMethodList">
  <xsd:restriction base="xsd:string">
   <xsd:enumeration value="32-bit"/>
   <xsd:enumeration value="16-bit"/>
   <xsd:enumeration value="8-bit"/>
   <xsd:enumeration value="7-bit"/>
   <xsd:enumeration value="6-bit"/>
   <xsd:enumeration value="5-bit"/>
  </xsd:restriction>
 </xsd:simpleType>

 <xsd:simpleType name="PadDirectionList">
  <xsd:restriction base="xsd:string">
   <xsd:enumeration value="LEFT"/>
   <xsd:enumeration value="RIGHT"/>
  </xsd:restriction>
 </xsd:simpleType>

 <xsd:complexType name="Field">
  <xsd:attribute name="seq" type="xsd:integer" use="required"/>
  <xsd:attribute name="name" type="xsd:string" use="required"/>
  <xsd:attribute name="bitLength" type="xsd:integer"/>
  <xsd:attribute name="characterSet" type="xsd:string" use="required"/>
  <xsd:attribute name="compaction" type="tdt:CompactionMethodList"/>
  <xsd:attribute name="compression" type="xsd:string"/>
  <xsd:attribute name="padChar" type="xsd:string"/>
  <xsd:attribute name="padDir" type="tdt:PadDirectionList"/>
  <xsd:attribute name="decimalMinimum" type="xsd:long"/>
  <xsd:attribute name="decimalMaximum" type="xsd:long"/>
  <xsd:attribute name="length" type="xsd:integer"/>
 </xsd:complexType>

 <xsd:complexType name="Option">
  <xsd:sequence>
   <xsd:element name="field" type="tdt:Field" maxOccurs="unbounded"/>
  </xsd:sequence>
  <xsd:attribute name="optionKey" type="xsd:string" use="required"/>
  <xsd:attribute name="pattern" type="xsd:string"/>
  <xsd:attribute name="grammar" type="xsd:string" use="required"/>
 </xsd:complexType>

 <xsd:complexType name="Rule">
  <xsd:attribute name="type" type="tdt:ModeList" use="required"/>
  <xsd:attribute name="inputFormat" type="tdt:InputFormatList" use="required"/>
  <xsd:attribute name="seq" type="xsd:integer" use="required"/>
  <xsd:attribute name="newFieldName" type="xsd:string" use="required"/>
  <xsd:attribute name="characterSet" type="xsd:string" use="required"/>
  <xsd:attribute name="padChar" type="xsd:string"/>
  <xsd:attribute name="padDir" type="tdt:PadDirectionList"/>
  <xsd:attribute name="decimalMinimum" type="xsd:long"/>
  <xsd:attribute name="decimalMaximum" type="xsd:long"/>
  <xsd:attribute name="length" type="xsd:string"/>
  <xsd:attribute name="function" type="xsd:string" use="required"/>
  <xsd:attribute name="tableURI" type="xsd:string"/>
  <xsd:attribute name="tableParams" type="xsd:string"/>
  <xsd:attribute name="tableXPath" type="xsd:string"/>
  <xsd:attribute name="tableSQL" type="xsd:string"/>
 </xsd:complexType>

 <xsd:complexType name="Level">
  <xsd:sequence>
   <xsd:element name="option" type="tdt:Option" minOccurs="1" 
     maxOccurs="unbounded"/>
   <xsd:element name="rule" type="tdt:Rule" minOccurs="0" 
     maxOccurs="unbounded"/>
  </xsd:sequence>
  <xsd:attribute name="type" type="tdt:LevelTypeList" use="required"/>
  <xsd:attribute name="prefixMatch" type="xsd:string"/>
  <xsd:attribute name="requiredParsingParameters" type="xsd:string"/>
  <xsd:attribute name="requiredFormattingParameters" type="xsd:string"/>
 </xsd:complexType>

 <xsd:complexType name="Scheme">
  <xsd:sequence>
   <xsd:element name="level" type="tdt:Level" minOccurs="4" maxOccurs="5"/>
  </xsd:sequence>
  <xsd:attribute name="name" type="tdt:SchemeNameList" use="required"/>
  <xsd:attribute name="optionKey" type="xsd:string" use="required"/>
 </xsd:complexType>
 <xsd:complexType name="TagDataTranslation">
  <xsd:sequence>
   <xsd:element name="scheme" type="tdt:Scheme" maxOccurs="unbounded"/>
  </xsd:sequence>
  <xsd:attribute name="version" type="xsd:string" use="required"/>
  <xsd:attribute name="date" type="xsd:dateTime" use="required"/>
 </xsd:complexType>
 <xsd:element name="TagDataTranslation" type="tdt:TagDataTranslation"/>
</xsd:schema>
PKGnZPK|%AOEBPS/img/adfns062.gifeGIF87a;?**?***UU?UUU????**?*******?*******U*U?*U*U*U**?*****?*****?*****?***UU?UUUU*U*?U*U*U*UUUU?UUUUUUUU?UUUUU?UUUUU?UUUUU?UUU?**?***UU?UUU?????**?***UU?UUU?????**?***UU?UUU?ժժ?ժժժ???**?***UU?UUU?????***UUU,; H*\ȰÇ#JHŋ3jȱǏ CIɓ(S| ? 4xaB 6tbD)Vxcƅ H*\ȰÇO@ DPB >QD-^Ę1a>$XA .dA"D!B"D!B|A"D"D!B"D!B"D"D!BhP?!B"D!B"D!Bt/_~!B"DA"D!B"D!B|"D!"D!B"D!B"D˗D!BѠ>!B"D!B"D!B0_| B"D"D!B"D!B"D"D "? 4xaB 6tbD)VxcƄQFӨQF5jԨQFQFӨQF5jԨQFQFӨQF5jԨQFQFӨQF5jԨQFQFӨQF5jԨQFQFӨQF5jԨQFQFӨQF5jԨQFQF1_>O|!/|4JQF ˗F5:!| ~˗O>˗/_~˗O?~O`?˗O`|!D!B"D!B!B"D!B"4G_?!@~̇ ~??!D/|70߿|o`O|!$o`>"D!B"D!!B"D!B|/>}˗/_/?~o_|_ӧϟ?~O'_|ӧϟ?~#8|o_}˗/_/˷/?}_|˗/___|ȯ| 7? H*\ȰÅÇ>l_|o`G`>_|߿7P ?| O@~ 0@_߿(_/߿/߿/߿| /?~'߾#/_~$H0A8`A&TaC Ç>|P @7P? _>_|7P?'0߿|O|#O`'_/70߿|70|aÇ>L/_~>|Æ!/'p_|˗O_߾|/>}˗/}P_|˗/'p_|ܗ/>/|߿/_}_>_|/_>~8p_}/_}O_>~׏|O߿˗߿|O@ DPB *̗/_>|aC}>|Ç!0ÇÇ>|0_|>|Ç {ÇܗOC{Ç>|ÇÇ>|P>|p |O@ DPB >QD˗E-ZDϟE-ZhѢE-Zh`|YhѢEYhѢE-ZhѢE ˗E-ZDϟE*˗|X_>YhѢE-Z0_|,Zh"B},Za>/_|/߿}/_|˗/> O O_'p "Lp!ÆB(`|M8qĉ?3a>/b?~O߿|'_|70@~/|'N8qĉ ˗ĉ'NHP'_}ۗ߿|ӧ/|7p_|/_>}ۗo_|˗/_#ȏ|70@~o`>8qĉ'N/_~'N8q"A}&70| ߾|߿| O` `>/߿|/߿߿'p|߿|o`|G_>_8`A&TaC!FH0_|&N8qDM$o`>_>W0|wP?_~O`>~;o`o_|w0_|o߿|oĉ'N8q|8qĉ 7Q`|뗏߿|'_|/_|˗/|˧_O߾|3`>O?~7P ?O }_'p "Lp!ÆB(_|M8qĉ8qD&N8?~'N8qĉ˗ĉ'NHP'N,/'N8qĉ'N80_|&N8qDM8 |O@ DPB >QD-.̗/_/^8P/^xŋ/^xa|]xŁ]xŋ "|w0_3/D/?`.^4/_~/^xq>˗O?~>}˗/>}o>} ˗/}o_|˗/_>}˧|7?~ӗ? O_|_|˗|?$X|;x,O`>'|O`?;x ̗/_?$XA .daC}'_'p?_> _| _/_>O }o`o`>o ? П?k؏?~ `>#o`>#C=̗/_>|aC}'ϟ3_|O`>O ?}70|߿| ߿||'_}ۗO_?~߾|O@/}/_|˗o|8p'0|O` o@} HAw|O@ DPB 6|/_~˗_>˗/_|߿|70_|+O`>__ +`O ۗ`/A ߿|߿|_˗/_>~O`>`> ,X`A~˧? ̗/ ,X` H*\P? 'P_۷o`o ?O ?70߿|'>'_/|W0| ׏߿|#` Ϡ>~ /߿|70|'C O|`> c`> 4xaB O@ DPB1D/_>~'0_|/_}O_|w|o@~@}?˷| o_|˷|˗?~#ϟ|/ O |/_|˗/_>~O/_>}˗/O> ̗/>'p_|/_?}#H A'p @},/_ ,X`AO ? 4x| 0a„ &L0a„ &Lx_„ &L0_„&L0~0}ӗ`|%L0aA~̗0a„ KP?$XA .dC%NHp_>}+VП|˗_E;>~o_|o} '0@}ӗ|/_>GP+V`>'p "LpA <0… :L|{p!|;߿|>_>ϟ|߿}O`>~{Ç>|Ç>|Ç ˗Ç w`>߿|_'P`>O`>o`|o@}8`A&TaC!F8bE1fL/_~`o`> O`>O`>'p8P?$XA .dC%NXE˗Ƈ;`>_> O` '0|o>~Ϡ>˗/_?~˧|/_|/_>5jԨQF5*̗/_?w`>߿|'߿(0|ӧO`>/_}˗|7p|7>~ 70| H*\ȰÇ#JHŋcD~aĈQ> ˗߿| ̇0|ˇ#F1bĈ#FcD~aĈQ> _/__>1bĈ#F1bD/_~##| 0| ̷o| '_>1bĈ#F1bD/_~#/b>wQ?˗? O} /߾'p "Lp!ÆB(q"Ŋ/R̗/_?wP?~|O>~O | ܗ_>/_>È#F1bĈ#FcD~| '0|߿|߾}+߾}틨F1bĈ#F1b/_~#`> '0| '0|_ o`aĈ#F1bĈ#F1"|0@/߿_(_O| 7p <0… :|1ĉ+Z1c|_>~ O ? _>>~ӷ|ӷ|ӨQF5jt/|4jԈ1_|4>䗏|˗|˧O| ϟ|/>}/_~/_|˗/_>}'_|O@~O(@}>}? 4x_|/?˗/_>}˧|Ӈ!B"D!|"D!B"Dh0_|"D?~!D!B"B ܷ_#o |/@~ /@o߿|CX?!$ϟ?~'0|'_CX?!B?ˇ!B"D!B!B㗏B"D!B!DX0|G0|߿|!0_|_O@/}/_|˗/| '_8P?~߾|ӗ/_~ӗO|8|7p?~߾|ӗ/_~7p_|  <0… ̗/_?a|СC9/߿|/ǯ?~ '0?}O`O@~/߿|/߿| _|#(`>/߿|/߿߿'p "Lp!Æ ϡÆsX0߿|:TϟÃ/| /| 70| ׏>70A}_ ~O`>O~ gP?_~O` aA}_o`> O`?:t|s!|˗ϟ@~_ۗ}sP> ˗/߾(@ O O'_>}/߾/?˷_>}/|O>O? O߿ ˷_>}/|_>}˷ A ۗ/>/|/_|ۗ/?}'p "Lp!ÆϡÆ3o?'0|'0_>СC:DϡCsС|СC:/_~6䗏|O`O`sP>:tСÃsСs!}9tСC̗/_?`O`O`sP>:tСC:tСC:tСCaC~ԷO`O`O`/,h B'p "Lp!ÆB(q"Ŋ/b̘0_|4>䗏A_|_?}P?5jԨQF5j/_~OcAP?5jԨQF5j/_~Oc~󧑡>˗/}o_|˗/_>}˧|7?~ӗ? H*\ȰÇ#JHʼnwQ"|. w@}?O?} /@ /|o߿|]xŋ/^xqb|]/#S_|O`>O ?}70|߿|/^xŋ/^/_~% | Է/ '?}ۗ/?O߾|'_|ۗ?}%/a_>/_}'0|/ŋ]tE]tEMP>8`A䗏A~߿|o|o?/߿|/?~߿|%_|'} _ o@~/,h „ 2l!Ĉ'Rhqb|]/? '0|O`O`>O` '0|)!? 䗯? @?7?~ۗo?~8`A&TaC!F8bED~O@?}/߿|__@/߿8> H*\ȰÇ#JHŋ3&̗/_?gP?'0@~ /@}'0| '0?}/|)OF5jԨQF-˗Ƈ3O_|o`>'П| '0߿| ϟ| '0|QF5jԨQFOC~ihQ?5jԨQF5j/_~OFi/D~`|3/F5jԨQ|_>~5Zb>˗_>~/_|/_>$X| ;x 'p |o`>}'? 4xaB 6tbD oā8qbD}&O`a?~ka?~`>O`>o`'N8qĉ oā8qbD}&˗/| ߿|'_} ܗ߿|/_˗Ϡ?~߾|ӗ/_~7p_|%߿|/_oĉ'N8q|8q |&NQ߿| _>o`>ȏ`> 7_>(_/߿/߿/70|70߿~'p "Lp!ÆB(Qa|M8_>~'ND_>_>%70|G0_|/A}_o`> /ao`7qĉ'N8Qa|M8_>~'ND/O}?˗/~˧o |˗߿|o_|/_}_>_|/_>~/O? ܗo?~8`A&TaC!F0_|&N/'Foĉ'.7qĉ'N8qD7q@~M81>'N8Q|&N8qĉ'N1_|&N/'Foĉ'p ?$XA .dC%NX1a|Y/?-"ϢE-ZhѢE-ZH0_|,N䗏EgѢE-ZhѢE-Z$/_~'ϢEhѢE'_>-ZhѢŃgq"|,ZP? ˗|_|˗/_>}˧?OO_8P?~'p "L80| *TPB *TPB ˗B 㗏B *T> *,o`O`/߿|˗0|_>~ۧp`?~S8П?̧PB *TPB *T0a|)TP?~)TPB P‚_/_G0? ߿|`>˧_>~O_|O_O|/_>OB *TPB *B P>8`A䗏_„ &L0@}&L0|GP| ̗/_ 70߿~ '0|(_/߿'p}O`70| H*\ȰÇ#JD/_~'䗏ĉ#7bo@}_>̷0|O }3|/_O`>~oĉ'N8q"|8q |&NQ˗߾|/_|O@~?~'p}/߾/>G|˧_O?˧O߾|/_|˗,h „ 2l!Ĉ ˗ĉ7qĈM8qĉM8qĉ'N8`|M8_>~'Nĉ'N8p_>}'N8qĉ'N,/_~'䗏ĉ#7qĉ'&O@~ H*\ȰÇ#J0_|(R/?):"E)RH"E)RHa|Q_>~)RtE).G|so|3/_~HQ|1_> W|H|(RP? '0_/_}/_>~˧_|-GQa>O@$XР|4<80A˗߿|ϟ>}˗/?/_|ۧ/˧|/_|˗O}gp`> ˗/_? 4h | 4hРA 4hP> 4hР'p_|O`Ϡ> O@~/߿|߿|_| H |G0@ ߿'p|'p __ ?O`/@˷/|o| Gp |߿|_> A? 4xaA~%L0a„  /a„ o O`>o`0@}_o`> O`G0@}'_O'П>P`~o`>~?O`'П;_|O`|O`| 0_|&L |&L0a„ 0a„ '0_>O @O@ ܗ/>/|/_|ۗ/?}$Hp | ̗/_>~˗O|/_>~ ܗO|˗o?'p_|˗/}Gp`>~/_|˷߾|/_>}/?߾|뗏߿|ܗ/>O߿}o_|ۗ/?} G>? 4xaA~%L0a„  /a„ &L0a̗0! &L0a„ K0a„ &L`~C/_~ &LX_>~ &L0a„K0a„ &L0}%L0a„ &L0!}%L0a„ &L0|K0a‚K0a„ &,h „ 2l!Ĉ'Rh"ƌ OC~ihQ?5jԨQF5j/_~OFiԨQF5jԨQ|_>~5ZOF5jԨQF-˗ƇӨѢ>˗/>}˗_|iԨQF5jԨb|i|/?-0@~ /}4jԨQF5jh1_|4>䗏FK/__ӨQF5jԨQ|_>~5Z/a|/,h „ 2l!Ĉ'Rh"F"|2fĨ_|'_'_ƌ3f̘1cƌ3̗/_1#F}O}㗏} <0… :|1+Z`|e/1Q?~3f̘1cƌ3fX0_|2B䗏_ƌ˘1cƌ3f̘1cƌ /#D~ëQ3f̘1cƌ3f̘0_|2B䗏_ƌ˘1cƌ3f̘1cƌ /#D~ëQ3f̘1cƌ3f̘0_|2B䗏_ƌ˘1cƌ3f̘1cƌ /#D~ëQ3f̘1cƌ3f̘0_|2B䗏_ƌ˘1cƌ3f̘1cƌ ,h ‚K0a„ &,h „ 2l!Ĉ'Rh"ƌ OC~ihQ?5jԨQF5j/_~OFiԨQF5jԨQ|_>~5ZOF5jԨQF-˗ƇӨѢ>5jԨQF5jh1_|4>䗏FӨQF5jԨQF!|4jϟF5jԨQF5Z̗/_?QE}4jԨQF5jԨb|i|/?-QF5jԨQFOC~ih> H*\ȰÇ#JHŋ3&̗/_?QE} H*\ȰÇ#JHŋ36 < |&L0a„ O@ DPB >QD-^Ęa?$XA0a„ &Lx|,h „ 2l!'Rh"ƌ'p ",/?$XA .dC%NXE5nؑ"|~=zѣG=zѣǂѣG=zѣG=䗏G=zѣG=z |~=zѣG=zѣǂѣG7'p "Lp!ÆB(q"Ŋg!,h „ 2l!Ĉ P <0… :|1ĉ+B̗/ņ'p "Lp!ÆB>I(QD%J(QĆQ?%J(QD'QD%J(QDObD}I(QD%J$J(QD%J(QbC~I/_>%J(QD-'QD%J(QDObD}I(QD%JQD!bC}YhѢEEgѢE-ZhѢłP_|-ZhbD~EgѢE-ZhѢłP_|-ZhbDYhѢE-Zh |,6ԗ/E-Zha>-ZhѢE-䗏ņhѢE->gѢE-ZhѢłP_|-ZhB~, gѢE-ZhѢłP_|-ZhѢB~,gѢE-ZhѢłP_|-ZhѢŇ,Z",",  ~O@ D`>)TPB *TPB~)T|*TPB *TPaA *O|/@@~ /@~ O!~P@~+| OB *TP„SPaA})TPB *TPB &̧`ȯ|g0_| Ϡ?~߾|ӗ/_~˗?}o_|˗/_~ӗ|/__})TPB *T(! '_|70_|'P /߿| '0|O|ۗO`> 7_O@ DPB 䗏ÇÇ>|{o`> 'p߾_A} _`~o`>~?O`'П{Ç䗏ÇA} H*\Ȱa>~ HKH_|˧O`>߾|/_}'P |?˷߿|_>~8_>~/_|˷߾|/_>}/?߾|뗏߿|8`A&TaCaA}=|hP>|P{C{Ç>|P!|>,/_ Ça|>|p_>} Ç>|aB~=|XP_|Ç>ǯÇ=|C ? 4xaB 6tbDQ_|%'QD'Q|%J(QD%J(!|$Fԗ/DI(QD}I(0D%J(QD%Jl/?'Q>%J(Q_|% 'QD%J(QDObD}IϟD%Jԗ/D1/_|˗/>~ } ׏|8_|/_|˗o@~˧`> ̧?˗o?}˗? 4xaB 6t@~EO_|# /bĈ#ԗ/_Ĉ570߿|7P}O| w0}o`>'0߿| g>70_~1bĈ#F/˗/bDE1bĂ`_|˗O`>ȯ߿|%70߿|G0_|/_|/?/_>}ۗo_/bĈ#Fd/˗/bDE1bĂ 'p /?}'0|O` 70߿|'߿|70?}#_+`>/߿|/,h „ 2l0!|>,/_ Ç˗C_#o@~߿|'߾O`o` '߾gP?_~{Ç&䗏ÇA}>|C}=|0C~׏|߿/(?˗o?~ @ O Oo_}#8p_|/߿|˧,h „ 2l0!|>,/_ Ç˗C>|Ç{Ç6䗏ÇA}O@~ /0@ ϟ? 4xP_|$XA .dÃ"D!䗏D"A}˧0_| +/_/| 2ԗ/DA"D!B"D"ā> #/_~˧O`>W0_/_> ̗"C}Ah0D!B"D!B | B/_> 0_>'0߾o_>#0@ o'? 4xP_|$XA .dC%NX"|,6ԗ/E)̗`?~ '0_'0_>̗O?} ̗/_>},ԗ/EOo~ /_|ۧO}/? _|/_}/_>$XA .dCObD}Iϟ|/|_>}G0_/_>It/_>ko>~/@O>~'ПKO| ϟ?(QD%J/?'Q> #_|o߿|| /|'ѡ|$Ja|#_>̗/@`|%'_|Ǐ߿~%J(QD'1|$JO!|O_}ӧO''P'? Hˇ!B!,>/_~7p_|3O`>_>?$XA .dCObD}IϟD%Jԗ/D5G>'P_o } /a>'߾OD%J(Q"|$Fԗ/DI(QD}I(0C~/_}'080߿_|'0?~O> H*\ȰÇ#&䗏Ĉ(Q?%J/_>(QD%J(QD '1|$JOD%˗OD$J(QD%J(QbC~I/_>(QD(Q`>%J(QD%Jؐ_>~#˗OD}$J(Q|$JOD%J(QD%6䗏Ĉ(Q?%J/_>(QD%J(QD '1|$JOD%˗OD$J(QD%J(QbC~I/_>$XA!D!B"DP|"D`>˗/_?~˧|/_|/_>$XA .dC%N|/?G>Hq|(J0| ䷏@~ 70E)RH"EG|(B珢|(R/_>5̗/_>'0|/E)RH"EG|(B珢|(R/_>5'߾O |_(RH"E)R,/?G>GD}Qao߾O`|)RH"E)䗏ŇQ@ HC!B ԗ/Ḃ |`>o߿}˷| H*\ȰÇ#J_>~˗"D}(*"E(1E)RH"E)䗏ŇQ? HQ|(JG"E)RH"EG|(B珢|(R/_>QH"E)RHA~Q|/_>0?%˗|)RH"E)Rx_>~˗"D}(*"E(1EQ$/ň3/A~ '1_o|(RL/?G>GDO@ D(0_„˗? O@$H G A |#H A#80A? >8_>~70>} 䗏A ,/_> 4hP? 4ϟA 4hРA3hРA3hР@~ 4h_gРA3/ ȯG0A W0|O`O`Ϡ3hРAgРA gРA3hРA 4HP_| 4hР| 480|,o>}O`>}˧o|O|˗?go`//_>/>}˗O}ϟ>}˗/˧o`>/?#Hp`˗/| ߿| $_>~ $H AG A $HP?8`A;x 'p +X` W`/_>~ '_|/} |70?7_/ 7_ O@~/}/߿ ߿|ǯ'0| 70|70߿~$H?~$H A ԗ/A $H A} P /|˗ϟ|ǯ|WPW| '0| 엯`W` ԗ/_ ,X> ,X`> ,X` /_ ,X| ,Xp_|˗o_/_>}/_>}O|_|˗o 0_} >/߿O?(߿}O ?}?'p'p?ӗ@#H|뗏߿|'P| 䗏A ,/_> 4hP? 4ϟA 4hРA3hРA3hРA gp`?$XA .dС~>|0!|>,/_ a>>l/_{aB~{Ç>|Ä{|>4Ç=||>DÇ>|Ç>| |>,/_ a>>l/_{Ç>|Ç>|Ç>|ÇÂ{Р>Æ{aC#/,h|'|ӗO |ӗO?Ӈ?} ԗ/_~˗|7_|O_O |˗/_?w| ԗO>ӧ߿}O_>} W_?#`>Chp|,hp>$XC!B!D`>"D!BC!B"D`ۗ``>'p?'0| 70#o_}#80|O`>(߿(0|o|70|/|8p H Hˇ!BC|"D!Bˇ!B!D|` '0__?}70 '0|O߿|`'П> >Gp?#!B"DР|"D>"4B"DP|"D`>",/_|˧O| ܗ/>˧O_>~/_|˗/׏_>_|˗/_>~˷_|˷߿|/_|˷O`|O`| /_?O| ܗ/_>'p "Lp!A}1dP? ǐ!C ǐ!C2dȐ!C cȐ!C "ǐB~ 2dP|2dCcȐ!CcȐ| 2dȐ} ܗOC 2dȐ!C 2dȐaA}1dP? ǐ!C ǐ!C2dȐA <0B ? 4xaB 6t@}E(P 1A}EH0_Ĉ#F1bĈ#F1bC}E(P 1A}EH0_Ĉ#F1bĈ#F1bC}E(P 1A}EH0_ĈE,/_ W_B~̗/bĂ #/bDE/DEԗ/_Ĉ0#ԗ/_ĈEH0@~˧a W0|EX0_|E0|+|"F_ĆE|"F$0,h B_?~K/a„W0_BKX?#؏?~+྄0!~` 7p_} ˗/a„ K0|&L0aBK0aB&L`|/߾|˗|˗_?}/_>}˗O_ /_|/_| /ӗO?}o_}˗O|%4/_|ۧ/_B}O_>}ۗ/_~˗?`>'߿/>}8p`> ԗ/A $H A}$H AO@ DP@}-\` *O|/| 7_0@ O` '0~ O@~}߿|߿| o/߿| 80| 7_o_}8_>/|7p˗? 4x>"4B"DP|"D`>"D80|_~o`>~_a| O?} O`|+O` /|w0_| 'C/_ 70?| '0|O߿|!!B}!D!A}"Dh0?"D@}!D|"DP ?/߿|˧߿O߿}O> 80˗o|o_| 7p_|_>}ӧ_>}o_| G_> ܗ/߾OA_| ܗ/߾ӗ|_|˗/_>~#8> ԗ/A $H A}$H A'p "L|.\X0… .̷a +o… W0… [p…[XP_| .… .\p@}-\` .T/o [p…p}-\p… ˷p-\p…  ԗ/… O@ DPB >QD-^L/_> ÈD}at#F1bĈ#Fˇ>1b/_>aĈ#F1bĈ|02#Fˇa>˗O|/_|˗/?~  <0… :|1ĉ˗C}*VXѠ|*FW`_>%70@}+VXbŊ+>ԗ/_ŇUXA}U"| ߿| ̗0|WbŊ+VXB}U|_Ŋ+ԗ/_ň*O|/| O_Ŋ+VXbŊW>+VhP_|#H0| _>%70~+VXbŊ+2ԗ/_ŇUXA}U@~ /߾?˗/?~#? 4xaB 6tbD)2ԗ/_ŇUXA}UbʼnUXbŊ+VP_|WbŊW1b+VXbŊ+VP_|П <0… ˗? 4x| H*\ȰÇ#JHŋ ˇ!?}O@ DPB 8`A  <0… :|1ĉ+ZP_|!O@ DPB 8`A  <0… :|1ĉ+ZP_|!O@ DPB O@ D/,h „ 2l!Ĉ'RhbB}aĈ#ƅad!|ӧ/?˗/>1bĈ#F!˗#F12ao`ۇ#F1bĈC}aĈ#Fk/__È#F1bĈq|0bĈ#F5O|/|1bĈ#F1>ԗ/F1b| /~o~1bĈ#F12ԗ/F1bO?10@/? 4xaB 6tbD)V/_>-ZhѠ?~泘?-ZhѢE-*ԗ/E-Zha>-ZhѢE-Z/_>-Zh|-ZhѢE-ZXQ_|-ZhѢŇ,ZhѢE-Zh|,ZhѢEYϢE-ZhѢE+˗ϢE-Zlϟ|-ZhѢE-ZXQ_|-ZhѢŇ,ZhѢE-Zh|,ZhѢEYhѢE-ZhbE}YhѢEhѢE-Zh"*H_| H*\ȰÇ1_Ĉ#F1bĈ#F1bC}E1bĈ#>b#F1bĈ#F1bĆ1bĈ#Fb#F1bĈ#F1bĆ1bĈ#F1_Ĉ#F1bĈ#F1bC}E1bĈ#F/bĈ#F1bĈ#F|"F1bĈ#ܷ0_Ĉ#F1bĈ#F1bC}E1bĈ#F$O"F1bĈ#F1bĈ˗/bĈ#F1~ E1bĈEQDEQDE@O@ DPB >b>%J(QD%J(QC}I(QD%J 4xaB 6tbD)Vx!,h „ 2l!ĈO@ DPB >QD-^D <0… :|1D8`A&TaC!F8bE'p "Lp!ÆB(q"Ŋ/b̨q#ǎ? )r$ɒ&OLr%;;PKlKeePK|%AOEBPS/img/adfns059.gifRTGIF87a?**?***UU?UUU????**?*******?*******U*U?*U*U*U**?*****?*****?*****?***UU?UUUU*U*?U*U*U*UUUU?UUUUUUUU?UUUUU?UUUUU?UUUUU?UUU?**?***UU?UUU?????**?***UU?UUU?????**?***UU?UUU?ժժ?ժժժ???**?***UU?UUU?????***UUU, H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ } ? 4xaB 'p 8`A&TP?$Xp`?$XA .T <0… :|| H*\0,8_>$XA .D,8P_>$XA .D,h „ 2l!ąE1bA"/bĈ >~#Fh0#F1bąE1bA"/bĈ >~#Fh0#F1bąE1bA"/bĈ >~#Fh0#F1bąE1bA"/bĈ >~#Fh0#F1bąE1bA"/bĈ >~#Fh0#F1bąE1bA"/bĈ >~#Fh0#F1bD ,h „ A2dȐ!C14C 2d(0? 2dȐ!C 2C 2d_>cȐ!C Р>~ 2dȐ|2dȐ!C 2dp>~ 2dȐ?!C 2C1dȐ!CcȐ!C 2dȐ!Á1dȐ!Cch_> 2dP>~ ǐ!C  !C 2dȐ!C ǐ!C 󗏡A2dȐ!C14C 2d(0? 2dȐ!C 2 ?$XA . 2dȐ!C 2d8P?_|'P   <| ˗p|O@~7,h@~@}/@} ߿O@ /߾O@ DPB >P?/_ԗ?~E(_c/_~+/_񋨰߾| O?70_/ /E1bĈ#.a|˗o`|ȯ?~/_}˧O|5/?ӗo| 7_'_|O}O| O?70_ |/>7p_|'0_E1bĈ#.H?$H0_}70_>?~׏_>~o߿|/W`,_>~ ̗|_>~㗏߿}/߾}ǯ`W`,_>~ ̗|_>~߾}o߿}ǯ| ? 4xaB 6tB}˧ϟ|W?}˗O /|/C"o!|'0_>˗_|˗o`˗O| [_-|g?}˗O|O`|/~)/bĈ#Fq>~㷏_|#|#/| ̗/|c/_-O} O@˗/ /|˗/ 8P>~8p>~0@@ ̗/_ /|ӗ/_>}7p@'p "Lp!ÆB\|/||/}ۧ_>~O~ _>/| |/}ۧ_>~O~-/?}O |+/ӗ߿|O߿|O? 엏|"F1bĈ0_~70_˷?}/_>}ӗ/?_S/@˗ |O_|˗O˗ϟ?}O|7_~8_|/_}˧߿|ϟ| 0@ 80?$XA .dC b_拨Pqa 1bĈ#F\_ĈE,/_E0_DE_Ĉ E\_Ĉ#F1B}"F|1bĈO#F4_Ĉ#F1B}O@ DP?$(_ `A$XA O@$H@}> 4xa„8p`> H*\ȰÇ@~,h „'p A,Xp| 'p "LǏ AG~ H&O@ DPB >P#F,/_E1A}"/bĈ 1bĈ#FP#F,/_E1A}"/bĈ 1bĈ#FP#F,/_E1A}"/bĈ 1bĈ#FP#F,/_E1A}"/bĈ 1bĈ#FP#F,/_E1A}"/bĈ 1bĈ#FP#F,/_E1A}"/bĈ 1bĈ#Fh!O@ DPƒ14/C 2d(P?cȐ!C ǐ!C 2dȐ!CO@'p O@  > 4xaB  ԗ> 4xaB  <0… :|qa?$X |,h`A} HA H A$XA .T?  ? 4xaB 'p "Lp!ÆB0? (QD%J(QD%J(QDȐ>%J(QD%J(QD%JH0? (QD%J(QD%J(QDȐ>%J(QD%J(QD%JH0? (QD%J(QD%J(QDȐ>%J(QD%J(QD%JH0? ԗ|ӷ>%J(QD%J(QD%JH0? ̗o`>I(QD%J(QD%J$DID@'p`>}70@~7p?~8`A&TaC!F8bE1fԸ`>o_A~8rȑ#G9rȑ| o| OB~8rȑ#G9rȑ| O| oB~8rȑ#G9rȑ| /߿| ̷/qȑ#G9rȑ#GO_/?}Ǒ#G9rȑ#G%Q ?}9rȑ#G9rQb>Ǒ#G9rȑ#G%Q ?}9rȑ#G9rQb> HO@ DPB >QD-^ĘQFqOG9rȑ#G9rGqȑ#G9rȑ#GqOG9r#G9rGqȑ#G9rȑ#GqOG9rȑ#G9rGqȑ#G9rȑ#GqOG9rȑ#G9rGqȑ#G9rȑ#GqOG9rȑ#G9rGqȑ#G9rȑ#GqOG9rȑ#G9rG'p "Lp!ÆB(q"Ŋ/b̨q#|8 䧏#|8rȑ#G9rH1?1_|8rȑ#G9r(1?`>'p "Lp!ÆB(q"Ŋ/bĘ_Ƃe(`> ? 4x?$XA .dC%NXEa4`> 4xaBP <80?$XA .dC%NXEa4O@,h „80߾'p ? 4xaB 6tbD)VxQa> Ӈ#A ?~8`A <0… :|1ĉ+Z0?È`|#OƃaĈ#F1bĈb> Ӈ#|A}0bĈ#F1bh1?È`?a<F1bĈ#F-O@ O,h „ ӷpaB}8`A&TaC!F8bE'p  <0B ԗ/_|3>~ H*\ȰÇ#JHŋ3j8Wqȑ#G9rȑ#Lj7_ӗ?}Ǒ#G9rȑ#G#W0|`qȑ#G9rȑ#LjG_>}wP?9rȑ#G9r˷o`>Ǒ#G9rȑ#G#W0|o`>Ǒ#G9rȑ#G#W0@~'0|Ǐ#G9rȑ#G9F䧏@}8rȑ#GqGq@'p ? 4xaB 6tbD)VxcFq>~9rȑ#G9r1"?}Ǒ#G9rȑ#G#q>~9rȑ#G9r1"?}Ǒ#G9rȑ#G#q>~9rȑ#G9r1"?}Ǒ#G9rȑ#G#q>~9rȑ#G9r1"?}Ǒ#G9rȑ#G#q>~9rȑ#G9r1"?}Ǒ#G9rȑ#G#q>~9rȑ#G9r1"?} <0… :|1ĉ+Z1ƍ8P?9rȑ#G9r>ȑ#G9rȑ#G8P?9rȑ#G9r>ȑ#G9rȑ#G8P?9rȑ#G9r>ȑ#G9rȑ#G8P?9rȑ#G9r>ȑ#G9rȑ#G8P?9rȑ#G9r>ȑ#G9rȑ#G8P?9rȑ#G9r>O@ DPB >QD-^ĘQƁqG9rȑ#G9rOǁqȑ#G9rȑ#LjqG9rȑ#G9rOǁqȑ#G9rȑ#LjqG9rȑ#G9rOǁqȑ#G9rȑ#LjqG9rȑ#G9rOǁqȑ#G9rȑ#LjqG9rȑ#G9rOǁqȑ#G9rȑ#LjqG9rȑ#G9rOǁ'p "Lp!ÆB(q"Ŋ/b̨q@~8Ǐ#G9rȑ#G9F䧏@}8rȑ#G9rȑcD~8Ǐ#G9rȑ#G9F䧏@}8rȑ#G9rȑcD~8Ǐ#G9rȑ#G"?}Ǒ#G9rȑ#LjǏE~8Ǐ#G9rȑ#G H(p_|O@ D ?} *,h „ 2l!Ĉ'RhbB~ H ?$XA ӧP)TPB *TP|˧`>'p A+X` `'p "Lp!A~2A0߿| /D!C}O} H0 ~O_|˗߿}O`'P}7P_>}'_|˧O|O | Ǐ|/_?/?>~˗/_>~!B4/?"D!Bǯ?~ GP?}/߿|o}۷}뷏_>?~߿|O`>/?~| O`>' ?$X!D>~˗B"Dh_|"$/>"D!B"D!|/|g0|ӷϟ|_'|g0߿|O`>_>~ /? '0߿|C!!D>~!B/?˷!B"D!B"D_o`70߿|/|/? /߿߿/߿~_/߿ _>~@8`A Ӈ!ƒ!D!B /?ۇ!B"D!B"D(0?~70A}Է|70}_@~o`>_>`@~@~_>'?"OB!B"D8_|oB"D!B"D|o`맯?}|/_~O`>˗|˗|/?~_>~/߿ӗ//_>~/>}O_|8`Aw0_>˗/_>} !B"DH_|o,h „ 2l!Ĉ'RhD~'0_>cES/>1bĈ#F1b~1Z/?È#F1bĈ#F̷_}#_>È|oF1b|/"?~g0D~ÈQ`>;߾} G_>}È|oF1bĨ0|1_|#E~O|oB}0b_|˷#F1F}ӗ/O_>}_|/_>ۗ?}/O߾|ۗ_>?}ۗ//o/O_>}|'p "`'0|;B"D!B/>"D!B"D!B_|/| /}_>/| '0߿|o?~o|o@~˷o?>~|_>"D ?}ӗ_>O!D!B"/_|"D!B"D!B/|O`>/@'|/߿/߿/߿|˧o?/| o`>뗏@~ O`>? 4x!?}"<B"D!B!B"D!B"Dp`o_>~ '0|+0/߿/߿/߿_߿_߿| /O |O@ }/|'0߿~}70|/@}ӷ|O?+O`>B!BC!B"D(_}!B"D!B"D| /_|˧O|O_|/_>~ '0˧O߿|/>}̗߿O_|O_|˗߿O_|O|ϟ|'p "!BC!B"D_}? 4xaB 6tbD)Vx"?}#~/?1bĈ#F1bLOƃax_}#F1VG0|wcob>Ӈ>~1Zo_BÈ#F w0| G0| ˷0|ax>Èb|˗F1b\߿|O_>}߾| Ǐ|_>~/_? />}ۗ_o|߿O_>}˧_>~/?$XA ӧP)TPBۧp|)TPB *TPB~O߿| _}'0?Ǐ߿|O`> `>/߾}?~߿|O`>/|)TPA~*TO? *T`|/? *TPB *Lo_}_>70|o_}/| G0| ̗o`3_>'0| ̗'p "L(> OB *4/>OB *TPB O?~'0|_?}_?}Ǐ_> `>/|Ǐ`O?~#O`> ? 4xa)T>~ *TPa~)L/_> *TPB *TP__>ӷ||O_|'0|'0߿|_#_>_>O`>}PBSP?}*TPBS|*TPB *T!?}_>~ O_>}/_>}|˧O#O`>~ /|ӧ|#_>0>}߿|/?}O@ DP ?} *B *TH?  <0… :|1ĉ+Z萟>È#F1bĈ#F1VA}0bĈ#F1bĈ#FxP?1bĈ#F/1B~0b|OƃaĈ#F1b#|![_>Ӈ>~1bĈ#F˗߿|ӗ/?}/?/'P?~|/_>o?˗ϟ@}/@}˗?}ӇcC~0#F1bĈ@~_߾߾󗯟|߿|/_/߿|߿}׏߿~߿_߿|? 4xaB[0>~ .\p… .\p… .Loӷ| '0|'| 7_+o`뗏@~70|_ .\x p… .\p… .\p„ _}/| '`>@맏|o`|_/߿~_8`A&Tx p… .\p… .\p„ /߿~ /?}'0|' /70@}>~߿|/|/|-\pƒ-\P .\p… .\p… &/_~ӧ_|ϟ| '0|O`/>}˗@'0O`ӧO`ӧ߿|[p…[0>~ H*\ȰÇ#JHŋ3j8>ȑ#G9rȑ#G8P?9rȑ#G9r>ȑ#G9r̘| 㗏#GqG9rȑc| qa>{#E~8Ǐ#G9rxQ|߾|˧|/o_|ۗA~o_>~㗏?}/_>/_>׏|/_?7P_>}珣C~8Ǐ#GqG?}o?/߿/߿۷}/߿/߿˷o?~/߾/ϟ>_۷} H ӗ0aB'p "Lp!ÆB(q"ń'0|O | /_Ϡ| ̗o`>@~ӷA o| G0߿|UO_EUXbŊ+V0|_ 70| 짏| ̗o``>߾|맏| _~/|o`>$XAK0!A}8`A&TaC!F8b|ۗO}>~'0|_>?O߾70>~|/_|_뗏`bE~**ǯbŊ+VXbń/@/?O_|ϟ| '0@~_>˧OAO_|/_|˗O>~/_>}ӗo_>ӗ/_髨P+VXbŊ(,h „ 2lП?:t>СC:tC:LϡC:tX_|9tp!?}ϡC:tСC:t0C s0C:\OCsСC:tСC&/Csx0C:\OCsСC:tСC"ԗO_>~ /?'P|ӗo?}˗ϟ@}?~O_>}o>/_?O |9tСC~: ?$XA .dC%N0߿|/@~O`~?}۷}߿|O`'~O|'0?~XA~**ǯbŊ+VXbń_>~O`/߿| 70߿| _ G0߿}'0߿|O +䧯B}*VXbŊ+VL_>'P/߿_ ߿߿|>~o@/߿~_o@ H*\8> ǐ!C 2dȐ!C 2dȐ| O? |o߿|ۗ}?~/O? 䧏߿|'0߿|@~2dȐaB~2<C 2dȐ!C 2dȐ!C'0?7_|O_}˷_O`>} '0|㗏?}+?~_ϟ|ǐ!C ǐA}2dȐ!C 1C 1C 1C P> 7p'p "Lp!Æ?}>|Ç>|Ç+a>|C~>Ç>|Ç>|Ç>|C=|O>|Ç>|Ç>|Ç{>~>|Ç>|Ç>|Ç ?}>|Ç>|Ç>|Ç=|Ç>|Ç>|Ç>4OÇ{Ç>|Ç>|Ç>|h,h „ 2l!Ĉ'Rh"ƌ7䧏@}8rȑ#G9rȑcD~8Ǐ#G9rȑ#G9F䧏@}8rȑ#G9rȑcD~8Ǐ#G9rȑ#G9F䧏@}8rȑ#G9rȑcD~8Ǐ#G9rȑ#G9F䧏@}8rȑ#G9rȑcD~8Ǐ#G9rȑ#G9F䧏@}8rȑ#G9rȑcD~8Ǐ#G9rȑ#G9F䧏@}8rȑ#G9rȑcD~8? 4xaB 6tbD)VxcFq>~9rȱ ?~9rȑ#G 8P?9rX_|8rl`>'P | o/,h`/B"D!BӇ!ƒ'p "Lp!ÆBL`>_>$X'p '߾'0A}!'0@}/||?}/|/_>"D!B!BO@ DPB >P|_>$X!D`+`>?}?} /@~o||!D!B"D_>$X'p "Lp!ÆBL0? 4xp ?}"o`'0_>~ӷϟ| /?"D!B" ? 4x?$XA .dC 'P> <8>G0?!̇0| 70߿~'`>//߿ ? 4xaB 6tbD)Vx_|#/E]Do~ۗ0|?~_>O?`@~.^xŋ/^x_| w!?}ȏ߿~˗/| Gp_>}ӗo?}/_>}˷O_|˗/_??}xŋ/^x"~7P_|3/>Exŋ/^xŋ+a|OA~.^D <0… :|1ĉ+Z| | 'П|˘1cƌ3f̘1cƌ3`>7_>ӗQb>+/cƌ3f̘1cƌW0A~ |(1|W0_ƌ3f̘1cƌ36`>/ӗ |'0}O_>}_|ۗo|/cƌ3f̘1cƌW0||/@O`O`ϟ߾}?$XA .dC%NXE+o |ۗ/_| wO`_>/|'_ƌ3f̘1cƌ3"/A~2짏| /|G0߿~/|8,h „ 2l!Ĉ'Rh"ƃe4O_Ɓ_ӷ|G0߿|/˧@~2f1cƌ3f̘| ӗq |/>}˧O||/_}ӗ/2f̘1cƌ3f̘Ѡ| ӗ1cƌ3f̘1cƌ3f4/_Fe̘1cƌ3f̘1cƌ ˗ ?}3f̘1cƌ3f̘1cFe4O_ƌ3f̘1cƌ3f̘Ѡ| ӗ1cƌ3f̘1cFeQF/,h@~8`A&TaC!F8bE1fԸq|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?} H*\ȰÇ#JHŋ3j8_>ȑ#G9rȑ#G8>9rȑ#G9r_>ȑ#G9rȑ#G8>9rȑ#G9r_>ȑ#G9rȑ#G8>9rȑ#G9r_>ȑ#G9rȑ#G8>9rȑ#G9r_>ȑ#G9rȑ#G8>9rȑ#G9r_>ȑ#G9rȑ#G8>$XA .dC%NXE5n/ǁqȑ#G9rȑ#LjqOG9rȑ#G9r/ǁqȑ#G9rȑ#LjqOG9rȑ#G9r/ǁqȑ#G9rȑ#LjqOG9rȑ#G9r/ǁqȑ#G9rȑ#LjqOG9rȑ#G9r/ǁq#G9rȑ#LjqOG9rȑ#G9r/ǁqȑ#G9rȑ#LjqO,h „ 2l!Ĉ'Rh"ƌ7@~8rȑ#G9rȑcD8䧏#G9rȑ#G9F@~8rȑ#G9rȑcD8䧏#G9rȑ#G9F@~8rȑ#G9rȑcD8䧏#G9rȑ#G9F@~8rȑ#G9rȑcD8䧏#G9rȑ#G9F@~8rȑ#G9rȑcD8䧏#G9rȑ#G9F@~8rȑ#G9rȑcD8? 4xaB 6tbD)VxcFq ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1| <0… :|1ĉ+Z1ƍ8>9rȑ#G9r_>ȑ#G9rȑ#G8>9rȑ#G9r_>ȑ#G9rȑ#G8>9rȑ#G9r_>ȑ#G9rȑ#G8>9rȑ#G9r_>ȑ#G9rȑ#G8>9rȑ#DŽqȑ#GqOG9rȑA~ȑ#G8>9rȑ#ǂ(? 4xaB 6tB":/bĈ#F1bĈ8`A `>'p "Lp!ÆBT/_DEd@~'P?$X`> 480A3hp`>/>  <|,h`A~8`A&TaC!FP <80>$X ԗo`>C|!,!| 7P_"D ?}"~/?'0@}/|`>}!BӇ!ƒ 0@ H*\ȰÇ#JH1~70A}Ǐ_>O ??~_>O`~'_| '0_|w0~70ʼnQ\_}H"E).엯E0߿}70A/?O ӷϟ|o/? O`>o`>}%o?} Gq"?}wp_>)RH"C}QOE_>(_>~ |7p ?}߾| ߾|߿| ߿__o_>~ /?}70A $H $H A <0… :|1bB}IOć_` _>}| ̧@~ 70|'?/|ӷ|;?}Oā̗O|˗OC}$J(QD%ӗ~I| 7G0?'0?}/>}˗?}/_|/|ǯ`'0|˧O|` 70DO ̗O`> ? 4xaB 6tbąs/>%'QD%J|O|ӗO`ۗ>~%J(QD׏a|$J(D%J(!?}o_>A}$J(QD%ԗ~I(QD%J(?}۷_>˧>~%J(QD a|$J/_|$J$OD%JDO|O`>CD%J(QD3/>_>~%'QD%"`߿|ۗϟ@ <0… :|1"D}OD |ۗ/'_|˗ϟ@}/|˗ϟD%J(/|OA}$J(QD%"ԗ~IXp>~_}/_'@~߿|_>(QD'>~%J(QDׯ_>}%O_~70߿|'|O`|ӷϟD%JH> (QD%J@ <Р?G0| O@ _/߿ <0… :,OÇ{Ç>|~{a?}O`>}/'? _>/?~=|Ç ?}>|Ç>|h_>}{!|O_|˗߿|O_|ӗ/?/>}˗߿|>|Á=|O>|ÇO@}=|a>|Ç {>~>|Ç>$/>{!|>|C=PB <8P?$XA .dC˧Ϡ|$J(QD%J8> (QD%JX_>}'QD%J(QDIlD%J(QDS/_?%J(QD%ObC}$J(QD%OC}I(QD%J(> (QD%J(_>}'QD%J(QbD~$6OD%J(Q~AԗD%J(QDP?%J(QD铨/_?%J(QD'>~%J(QD"_>} H`A}'p "Lp!ÆB(q"A~(.Ǐ"E)RH_>}׏"E)RHbC~(.Ǐ"E)RH>QH"E)RtOŅQH"E)RH"E)RH?}G"E)RH"E)RH"Q\E)RH"E)RH"EGq>~)RH"E)RH"E)ROŅQH"E)RH"E)RH?}G"E)RH"E)RH"Q\E)RH"E)RDEQDEO,h@}8`A&TaC!F8bE1fԸq ?}Ǒ#G9rȑ#G#q>~9rȑ#G9r1"| HO@ DPB >QD-^ĘQƁ8`A O@ DPB >QD-^ĘQF=~RH%MDRJ-]SL5mIR̙3gΜ/WQ_~9s̙3'~Mԗ_Μ9s)_>}3gΜ9sOC}̙3gΜ6P_~9s̙3'~-ԗ_Μ9s̙_|/gΜ9s9_>}3gΜ9s,/˙3gΜ9sO@}̙3gΜ9˧|r̙3gΜ ˗_Μ9s̙a|r̙3gΜ ̷/>$XA .dC%NXE5nHP_~ױcǎ;vرcǎ; ԗ~uرcǎ;vرcǎ+/;vرcǎ;vQ_~ױcǎ;vرcǎ3/a|:vرcǎ;vرE}-O_ǎ;vرcǎ;v/_رcǎ;vرcǎ_>};vرcǎ;v؏1|"˧cǎ;vرcǎ;>ԗ~uرcǎ;vرcdž/;vرcǎ:ꨣ_?$XР~8`A&TaC!F8bE1fԸcGA9dI'QTeK;;PKX}RRPK|%AOEBPS/img/adfns090.gifCEGIF87a.Z?**?***UU?UUU????**?*******?*******U*U?*U*U*U**?*****?*****?*****?***UU?UUUU*U*?U*U*U*UUUU?UUUUUUUU?UUUUU?UUUUU?UUUUU?UUU?**?***UU?UUU?????**?***UU?UUU?????**?***UU?UUU?ժժ?ժժժ???**?***UU?UUU?????***UUU,.Z H*\ȰÇ#JHŋ3jȱǏ CI$B$XA .d? 4xaB 6tbD) > 4xaB *O@ DPB >QDXbEUXbŊ+V(П+VO_Ŋ+VXbŊXbEUXbŊ+V(П+VO_Ŋ+VXbŊXbEUXbŊ+V(П+VO_Ŋ+VXbŊXbEUXbŊ+V(П>ǯ|*NbŊ+VXbEA7P_?˷D*VXbŊ+VO|/_~_| O>Ǐ?~>~? O,h „ 2l!Ĉ'RO|O`>'0_Aϟ>/@~XbŊ+VXQ?}G_>Wp߾|O` џ+VXbŊ1@Ǐ_>Ǐ_ _>~>~@8`A8`A&TaC!F8@ ߿|'0߿|G_ӗ?}_>'UXbŊ+V(П>O |/?~o>~O?˗O>~/_>~XbŊ+VXQ?}+V(П+VXbŊWbŊXbŊ+VXQ?}0BW?}+VXbŊ+ A~%pU,O_Ŋ+VXbŊh0| /o?˗ϟ ,O,h „ 2l!Ĉ'RO_ł}O`~/@~WѠ?}+VXbŊ+ b}/@ /߿}o`|*bŊ+VXbEU,0@'p`>|/|8`AO@ DPB >QDH_'0|㧏?}OU,O_Ŋ+VXbŊHpӗ/_?'0?}HП+VXbŊWbŊXbŊ+VXQ?}+V(П+VXbŊWbŊXbŊ+VXQ?} ˗_E*"bŊ+VXbEUO}["B*VXbŊ+VO_Ł 'p_|ۗ?}}/? O,h „ 2l!Ĉ'RO_Ł >'0|o|o_EUXbŊ+V(П70| '0߿| O> WbŊ+VX@*Gp߿| '0|70|ǯ@*VXbŊ+VO_Ł }O`>o?o`XbŊ+VXQ?} ˗o@'0|?}|(П+VXbEU$@'p "Lp!C5lذaÆ 6lذaÆ a 6DO_Æ 6lذaÆ 6lذ!A6lذaC5lذaÆ 6lذaÆ aÆ 6DO_Æ 6lذaÆ 6lذ!A6lذaC5lذaÆ 6lذaÆ aÆ 6DO_Æ 6lذaÆ 6lذ!A6lذaC5lذaÆ 6lذaÆ aÆ 6DO_Æ 6lذaÆ 6lذ!A6lذaC5lذaÆ 6lذaÆ ?} H*\?} 6lذaÆ 6lذaÆkذaÆ װaÆ 6lذaÆ 6lH|,h „ 2T? 4xaB 6tbD)V8P_>/^xŋ/^xQ_>/^xŋ/^xQ_>/^xŋ/^xQ_>/^xŋ/^xQ_>/^xŋ/^xQ_>/^xŋ/^xQ_>/^xŋ/^xQ_>/^xŋ/^xQ_>/^xŋ/^xQ_>/^xŋ/^xQ_>/^xŋ/^xQ_>/^xŋ/^xQ_>/^xŋ/^xQ_>/^xC$XA .d? P <a &<? P <0… O| H*\0a>$HП>$XA .d>$HP?$XA .dP?  <0… kϟ 6l?} װaÆ "` 6l0akذaÆ > 6lذ!B} kذaÆ kϟ 6l?} װaÆ "` 6l0akذaÆ > 6lذ!B} kذaÆ kϟ 6l?} װaÆ "` 6l0akذaÆ > װa| ` 6l0akذaÆ > oG A#H A | H*\0akذaÆ > o`>˧߾| Է/_?} /_?}'p_|ۗ_CװaÆ &?} 6lذ!B!A'0|۷}󗯟|_>>א>5lذaÆ 5O_Æ 6lПkHp '0| '0|_>O`>` 6l0akذaÆ >8`A __/߿>~_߿|O@W0,h „ 2L?6lذaC5_Á _ O߾ '0|/߿|ӷ| א>5D/_>~ ˗| *?} ˗O)̗/_>~ ?}o`?}ӧO`>~O`>/_>}/?'0| W0_à ǯ!A~/_Æ 5O_Á _Á| > 6lذ!B} kxP_?/_~˗ϟ?}O@ 3hP?} ׏`}_|˗߿|` /_/_~ ϠA'p "Lp!C`> ;` /߾5O_C ?o?O O|O`kOǯak>5<a3|'P> kϟ/_A_|+`> G_kO_| װA} kx0}˗O|W0}맏|_}54?8`A}/~/_A߿O߿~_? `A,Xp~ ܗ/?//_>'_|O_| ,8P ,X> W0_A _ W`A $O_'П~ |_>}G0}_>_ W A} ,80_O`>~'0O`'|o_W0_ /> +8_|/_>}0_~? $ϠA4H߾|O |O_|/_>~˗@~_>~A3hP> g0| /| '0߿|O`3hР@} O@ DPB 5O_Æ 6lПkX0} '0| '0|`>_}'p +? 4xaB &?} 6lذ!BaA~O߾ '0߿| Է|O?| W0_Æ 6l0_5lذaÆkO˷O?}O`'П| /?'0?װ>5\`װa| װ}k_ kO 6l>5\` 6?6O}˷0_Æ5_Æ 6lP.@ۗo@}/_?'_|ӗ/8p|(П>$XРA}/_>|o_|;x`A~?߿|_>;x|wA >'0|o|,O˗/_>~'P?$Xp`>/_|˗/>`> W0߿|W_>@~gРA4(П> `O`>O`> OAد|O| gР o|_WP 4h0_| /~'0|(߿~O@ w?}4O`>o`> 7P߾ ϟ>_|;/| _>GP<80_|o߿|/|O ?;x|wA }O`>o?;x`AoO`>Ϡ>_>~|O? _?}+˗O߿|̗o߿'P>~?}'0_?$XР|w'П| '0|O_|;x`Ao`>'`>'p? _>~|_>߿~/_˧o`>? 4xaB &?} 6lذ!Bo`>o|O`> _>0@|'p|#/,h „ 2L?6lذaC5|70~3_> ̧@~3/@}/߿|` 6l0akذaÆ >o`|/_A Ǐ߿~'?}_߿7p߿8_(0,h „ 2L?6lذaC5@~װaA~ +_| 6lذa| װaÆ "?}k8_Æ5lȯ>5lذaÆ 5O_Æ 6lПkذaÆ W0_Æ 6l0_5lذaÆkO 6l>5lذaÆ 5O_Æ 6lПkذaÆ W0_Æ 6l0_5lذaÆkO 6l>5lذaÆ 5O_Æ 6l?} H> H*\>5lذaÆ 5O_Æ 6lПkذaÆ W0_Æ 6l0_5lذaÆkO 6l>5lذaÆ 5O_Æ 6lПkذaÆ W0_Æ 6l0_5lذaÆkO 6l>5lذaÆ 5O_Æ 6lПkذaÆ W0@~,h „ 2L?  ? 4xaB "ܧ? Է ? 4xaB "?8`Aԗ/a„ 'p H0a„'p A8`A̧/a„ 'p "LHP_> *TPB ӗϟB *TPBSPB *T(P_> *TPB ӗϟB *TPBSPB *T(P_> *TPB ӗϟB *TPBSPB *T(P_> *TPB ӗϟB *TPBSPB *T(P_> *TPB ӗϟB *TPBSPB *T(P_> *TPB ӗϟB *TPBSPB *T(P_> *B )B |8`A&TaÄsСCϡC:t8P_>:tСCsСCϡC:t8P_>:tСCsСCϡC:t8P_>:tСCsСCϡC:t8P_>:tСCsСCϡC:t8P_>:tСCsСCϡC:t8P_>:tСCsA$XA0a„ &L0aBK0a„ &L0~%L0A > 4xaB 6tbD)VxQb>Ǐ,h „ 2l!Ĉ'RhD}#F1bĈ#F#ȏF1bĈ#F1 ?~1bĈ#F1bhP?aĈ#F1bĈA}#F1bĈ#F#ȏF1^ܗ/?O`~_|#/_>~˧|ˇ#F#ȏF1ZO>~/ ԗo '>O`|7P_>1bA~0bĈѢ~׏`}`߿߿߿O߿O@ DPB 2 ?~>|Æ 3_>~O ?`>?~>|Ç #ȏÇ>|a> ߾O`>+`>_>|!C}Ç>l__~ O`>#O|/Ç>|>{Ç6O }'П~㧏߿| 7p_? ䷯_>~'0?~Ç>d }?$XA .dСC~o_| _>/_|˗o@o` Ç>dA~>|Ç>|Ç>|P?=|Ç>|Ç>|0>{Ç>|Ç>|aB}Ç>|Ç>|ÄÇ>|Ç>|Ç #ȏÇ>|Ç>|ÇG>|Ç>|Ç& } H*\ȰÇ#JHŋ߿~ H*\ȰÇ1bĈ#F? 4xaB 6tbD}E1bĈ#F1bĈ#FT/#F1bĈ#F1bĈ 1bĈ#F1bĈ#FQ|"F1bĈ#F1bĈ#*ԗ_Ĉ#F1bĈ#F1bD1bĈ#F1bĈ#FP_>#F1bĈ#F1bĈ/bĈ#F1bĈ#F1B}E1bĈ#F1bĈ#FT/#F1bĈ#F1bĈ 1bĈ#F1bĈ#FQ|"F1"("("("җ,h „ 2l!Ĉ'RhE}aĈ#F1b|,h „ 2T? 4xaB 6tbD) 0 <0… O@ DPB >QD'p "Lp!C8`A&TaC!F8@ H*\P>$XA .dC%N(|,h „ 2T? 4xaB 6tbD) 0 <8߿ ?}?"DhP,h „ 2l!Ĉ'R`> 4xPӗ?? OB O@ DPB >QD'p o@}O_~ B O@ DPB >QD'p /~",? 4xaB 6tbD) 0  4x0?}߿O"DXP,h „ 2l!Ĉ'R`> 4x0߾o?G_} oB O@ DPB >QD'p  ?} ?"DhP,h „ 2l!Ĉ'R`> 4xaB * <0… :|1ĉO@ DPB 'p "Lp!ÆB(q"E8`A4߿4hРA  <0… :|1ĉO@g3hРA ? 4xaB 6tbD) 0 O?~+/ ?'߿A'p "Lp!ÆB(q"E8`A /˧߿|_~O߿}_>}4HP,h „ 2l!Ĉ'R`> ?/??}Ϡ8`A&TaC!F8@ H߿+O?o_߿~  <0… :|1ĉO@'+O`?}O} _?4HP,h „ 2l!Ĉ'R`> ࿁/>˧߿?}OA'p "Lp!ÆB(q"E8`A&TB} H*\ȰÇ#JHQ?$XA .dP,h „ 2l!Ĉ'R`> 4xaB * <0… :|1ĉO@ A} 4o? $? 4xaB 6tbD) 0 >4h? 4(P,h „ 2l!Ĉ'R`> ߿~ ߿o?~O |g@} H*\ȰÇ#JHQ?$X0o?/_~_O࿁/_~A'p "Lp!ÆB(q"E8`|o_ ߿O?'/߿ ? 4xaB 6tbD) 0 O~˗_| ̗/߿|/__|/|/_|8`A} H*\ȰÇ#JHQ?$XP߿}O_>~| 'O࿁/_? W`A} H*\ȰÇ#JHQ?$X߿_??O?'?} ?} W`A} H*\ȰÇ#JHQ?$XA .dP,h „ 2l!Ĉ'R`> 4xaB * <0… :|1ĉO@ DPB 'p "Lp!ÆB(q"E8`A?"DР>$XA .dC%N(|,h 4x߿?O?~O} 4x߿>}o߾>}o O@ DPB >QD'p ߿_/?!B'p "Lp!ÆB(q"E8`A?~߿w'p "Lp!ÆB(q"E8`AӗO? /߿ӗ?w'p "Lp!ÆB(q"E8`A_|O? ?~!D>$XA .dC%N(|,h „ 2T? 4xaB 6tbD) 0 <0… O@ DPB >QD'p "Lp!C8`A&TaC!F8@ H*\P>$XA .dC%N8? 4x|%L0A$XA .dC%NX@}]xŋ/^xE}]xŋ/^xE}]xŋ/^xE}]xŋ/^xE}]xŋ/^xE}]xŋ/^xE}]xŋ/^xE}]xŋ/^xE}]xŋ/^xE}]xŋ/^xE}]xŋ/^xE}]xŋ/^xE}]xŋ/^xq @,h ƒK0aƒ H*\ȰÇ#JHQ| H*\P!,h „ 2l!Ĉ'RO_Ŋ+ bŊ+VXbEUX@*VXbŊ+VO_Ŋ+ bŊ+VXbE9ܗ/>-ܗ/?hП+VXbŊ0_?O!?}bUXbŊ+V(П>O>~O | _| 7߿|/?O_>} XbŊ+VXQ?} '0?o?O _>`'p 'p "Lp!ÆB(q"E1g0|o?ȯ`>O ??}+VXbŊ+ ~_?} |߿|_/߿ <0… :|1ĉǰ_>~'0߿|O>O ?o߿|'?}+VXbŊ+ }70?~ϟ| O_| ˗o|ӗ/?C*VXbŊ+VO_Ŋ+ bŊ+VXbEUX@*VXbŊ+VO_Ŋ+ bŊ+VXbEU`$П+VXbŊWq`*V\O_Ŋ+VXQEU$@'p +_} Ǐ?~˗?wA'p "Lp!ÆB(q"EU`۷_>~_>~'߿|WQ?}+VXbŊ+ |70߿|o`|oXbŊ+VXQ?}+_O?~0ۗ,hP?} H*\ȰÇ#JHQ?}+_>O`>o`>}o`XbŊ+VXQ?}˗/˗o|˧O|ӗ/ׯ@*VXbŊ+VO_Ŋ+ bŊ+VXbEUX@*VXbŊ+VO_Ŋ+ bŊ+VXbE5'p_| ܗ/˗}'`>7P?$X?} H*\ȰÇ#JHQ?} ~O>~!O>~/'0_E*VXbŊ+VO}#o?}~׏`}'0_E*VXbŊ+VOC}/|g0߿|?}+VXbŊ+ a `> 3|o@ H?8`A&TaC!F8@A#O`>AO|XbŊ+VXQ?} 뗏@O }!O }'П~ O`UXbŊ+V(П>ۗ/~˷o`> ˗o@O`>'P 8 A8`A&TaC!F8@G_ŇWbŊ+VX@د~XbŊ+VXQ?}70|˗߿~*.bŊ+VXbEU\/||髸П+VXbŊWqa>~`ǯB*VXbŊ+VO_Ņ/|o`?}WbŊ+VX@*.3؏|櫨П+VXbŊWqa>? O`>$X'p "Lp!ÆB(q"EU\O|#O~П+VXbŊWqa ̗/_>}˗o~**bŊ+VXbEUP_ŅUXbŊ+V(ПП+VXbŊWbŊXbŊ+VXQ?}+V(П+VXbŊP <0… 'p "Lp!ÆB(q"Ł H/a„ 'p "Lp!ÆB(q"Ŋŋ/^xŋ/ŋ/^xŋ/ŋ/^xŋ/ŋ/^xŋ/ŋ/^xŋ/ŋ/^xŋ/ŋ/^xŋ/ŋ/^xŋ/ŋ/^xŋ/ŋ/^xŋ/ŋ/^xŋ/ŋ/^xŋǏ}˗O>}ŋ/^xŋ/O_|ȏ?~Ǐ?~˗O߾~8`A&TaC!F8bE˗/>~U׏|wŋ/^xŃ˷ń˗/^xŋ//>~#ۗ//^xŋ)˗˷ŋ/^xŋEwŋ/^x"~x1|.^xŋ/^L/ŋ/^xłxb|.^xŋ/^/_'ŋ/^xE}]X_~/^xŋ+w <0… :|1ĉ!WbńXbŊ+VXѡ>}˗O*_Ŋ+VXbŊ 80_?A~*VXbŊ+V\/#o?}˗@~|Uo_Ŋ+VXbŊ 80|O`O`>~H0+VXbŊW`>#/߿| ̗@~ WbŊ+VX"B}*׏?}맏|O@ HA'p "Lp!ÆB(q"Ń*엏@/ӷO`>_EUXbŊ+Vh˗O_?˗O>~O`>W`?}+VXbŊ+ǯbŊUXbŊ+VhP_+RbŊ+VXbłUX>+VXbŊ+ob|񓘯D}*VXbŊ+V,|CaXbŊ+VX>~ O |ӗO?~o_}?}_|8`'p "Lp!ÆB(q"łMG0??} } /'D*VXbŊ+V4/|O`> 70@O`>~M䧯bŊ+VXbŃI7ПO`O`/~O@ H@}8`A&TaC!F8}$߿|'0߿|'0_O ?XbŊ+VX1a>˗@~_>~ӧ?}/_>}˗/_?'Q߾+VXbŊWbŇUXbŊ+V0+:ׯbŊ+VXbŅX"C}*VXbŊ+Vl/+*ܧbŊ+VXbŇX"}*VXbŊ+V/+ԧbŊ+VXbʼn <0…[p… .\p… .\p…[p… p… .\p… .\p… o… ԗ… .\p… .\p… .,/> .\P |.\p… .\p… .\paB~[p„p… .\p… .\p… ۗO_ .,/> .\p… .\p… .\paA~pBo… .\p… .\p… .Lo_|-\AۗO?$XA .dC%NX"~ł˗O/^xŋ/*o_|~˗O~/^xŋ/^؏>} P˗O ~8`A&TaC!F8bE _?~(? 4xaB 6tbD)VxcF9vd;;PK ӿCCPK|%AOEBPS/img/adfns057.gifC3GIF87a@?**?***UU?UUU????**?*******?*******U*U?*U*U*U**?*****?*****?*****?***UU?UUUU*U*?U*U*U*UUUU?UUUUUUUU?UUUUU?UUUUU?UUUUU?UUU?**?***UU?UUU?????**?***UU?UUU?????**?***UU?UUU?ժժ?ժժժ???**?***UU?UUU?????***UUU,@ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cD@~'P? 4X0<80 O@ DPB >QD ԗo`>ł(2/E'p "LpB$Xp?$XA .T߿|_|˗ϟ@}/_>ۗo@~/?}ӷ/o|O | o_~/_~ӷ/?$X`A}8`A&T| H@8`A&Ta>~_/ϟ@~˷o?O| ߿}߾}_>~}߿|o|/?_>~'p  w/|,/߿߿߿|ۗ߿߿| ߿|/߿~/߿|O@ 'O?_O` o`>?O`~˷?}O_}O`׏`>~O_|O`̧O|? 4XP~~ H*\x_>cȐ!C G0C1Do@}c0ac>~ 2dȐ?!C 2@}|ӷ/| /?}/_>`>}O }#}/_>>~˧/_>/|˗Å1dȐ!Cch_> 2dP>~?}_ϟ>_۷}`>/߿O?} _>~} /?o/| 8p7p_|'P   <| ˗Р|O@~7,h /_A} /@˗o`>;߾o| O`@~ ̧o?/?&,_‚/_ԗ?~%L0| ˗ | ̗|/a„ G0߿|/߿| /~/_|O`>ϟ#_| '߾|(_o_>~߿| ? 4O_|#/@~˗@~_>}A$/˷o`|ȯ?~/_}˧O|}'0|㧏߿| /_| | /|#O`>w`|˗o`|˷|/߾_>~ w A߾|+p_|/?o߾o߾| G0|˧O|/_~ӧ?}_>'0|_>ȏ߿~O`ϟ|70߾|#߿|ӗ/ _O_|+؏|'߿|O`| ̗`A,Xp| |W_?~/@ '0|/_+X` ,XP ?$XA .dp>~㷏_|#|#/| ̗/|c/C9O} O@˗/|O`|˗O ? 4xaƒ*TPB O!|/||/}ۧ_>~O~ ˧| O |+/ӗ߿|O|ӧ@}Ǐ`> S80B #OB O!|/| ˗/߾˷O|O_|˗)D/ ߿| ˗/߾˷߾|O_|˗‚̧P| S0|)TP!H?$XA SP| ˧PB )THP?/||Ǐ߿}/_>7?}ӗO߿}#/_~_|ۗo߿|OB  OB SP| ˧PB )THP?۷/|׏}/㗏_>G0߿|߾}O|O`>۷/|)TPB)TPB ˧| *TPB70|/}߿/߿'P`_o|'0|? 4xaƒ)$П <0@$(_ ` H O@#o`| /|'0@/O`o`?}/߿~'0@? 4xaB)`> 4xa|8@ ,8_O@ D0>$HP?/߿~Ǐ}O}O ?/|ӷ|/|/|ۗO?,X` ,O?$XA .˗߿|˧O?}`?}/_>} Ǐ_|/_}ӗ/?2d>~ 2dȐ?!C 2Á1dȐ!C 2dP? 2d| ǐ!C  Ǐ~1dȐ!C 2dP? 2d| ǐ!C  Ǐ!C 2dȐ!C ǐ!C 󗏡A2dȐ!C1dȐ!Cb!bh O@ DPƒ14/C 2d(P? 2dȐ!C 2C 2d_>cȐ!C ǐ!C 2dȐ!CcȐ!C Р| 2dȐ@}2dȐ!C 2dp>$XA .T 0 <0… 'p "Lp!ÆB\П <0… 8`A H*\? 4xaB 6tbD)VxcF9vdH#I4yeJ+YtfL3iִygN;y@~'P? A#H A8`A&TaC!F8bE/| 擘_]xŋ/^(0 7߾ O߾|'0߿}~?}/|/_>O`>˧o_|ۗ߿}o_~/_> ϟ|]xŋ/ǯ?~ GP?}_o߾O?~?~߿|o?} '0|O`>~>󗯟|o߿|?~8`A&TaC!F81߿}70A/|/|/?`@~O`> '0|O`>_>oĉ'N8qD_ G0߿| /߿|O? ߾|/߿|7P/߿/߿߿|/߿|/߿/?$XA .dC%N>~ GP_|'0@~ӷ|O?`@~_>'0}O`>}'0|ӷ|&N8qĉ'N_>ȏ߿~O`/_~ O_|˗/_??}/_>}ۗO>˗|ӗ/_??}/>}˗ĉ'N8qĉ'N8qĉ&h&|8`A&TaC!F8bE1J̗O3f/c}`>'p "Lp!ÆB/_|EaEذ_?+/bĈ#F1@?$X8`AӧO߾|ӗ/'_|߾| Է/|ۗ/?_| _~ }o߾ >}+O|#篠| /߿|O`o| 70? ߿~O@ DPB >Q |?} HC| 짏| '߾|/߿|˗o`>#o_>~O` ߿7PO@ DPB >Q`|#OĆIL_>_>/ӷO`ӷ| 7| ̧o?_>~#_|OD%J(qa| '>~O |O`׏_|/_~O_|o|/_~O`>}ӗ/7$J(QDCOĆI(QD'QD%J(!?}'QD  O@ DPB >Q"A~O|P'N8qĉ'N8q"?}G0C}&"/_>~wp? _`>@~ H*\ȰÇ#Ja>}/߿}'1a~' ̗O ?~I/߾ _>~%J(QDw_> 70|'1a/O|˗?~o/_'P߾| `>_|˗?}/_>%J(QD;O߿}/ILO?/߾}?}_?}Ǐ/?_>~|߿| O?/߾}?}? 4xaB 6tbC~'P_} 8P? $H`?~7_/?#8p_}O`>O`>>O_~70߿|',h „ 2l!Ćo`~ ̷/?EL@70߿|8߿| |'0@/߿o@ /o |O@ DPB >Ȑ> ԷO`>ӧ>~7_>}ӷ|o>~|O`>߿| o>~o`| ̧o?>}1bĈ#FȐ񋨐_|/_~_>}/_>~`>~O`>'0|O|˗o?O_|ӗo?1bĈ#FؐQa>"FQ`#F(" O@ B"$@~"D!B'p "Lp!ÆBX> (QD%J(QD%>ObC}$&1_|˧`>@~ H*_|˗?}/_>˗߿|/_>˷O |ӷ/?O_|.\p… .\?} &o‚ۧ/@~/|/ǯ?~ gp>~_>_~?~}_>?}߿|'|? 4xaB 6t!?}qa /|'0};؏| o`O O`o`|O`ӷD!BA~ >|(_/߿|o_>~ /?}70 /|7`>߾|/߿/߿߿|/߿~O@ DPB >lOćA\?~ O /|;?}`?}ӷO`>}/'?_| /'|ӷ|"D!B,OćA\/_}?}_>˗߿|O` ˗/߾ϟ|˧/߾o|/>}˗߿|O_|?}/_~A"D!>$X!DaAChḂ!‚8`A&TaC!F,OĆI_|9'Q|(QD%JtOĆI(QD%J(QD'>~%J(QD%J(QćIlD%J(QD%J(!?}'QD%J(QD%J|OĆI(QD%J(QD'>~%J(QD%J(QćIlD%J(QD%J(!?}'QD%J(QD%J|OĆI(QD%J(QD'>~ H*\ȰÇ#JHŋ xP?1bĈ#F1VA}0O>  W0,h „ 2l!ĂEt_ą+/|9'0_D#`"F1bĈ ѡ>~`>`>ӗo߿}/_~/_>˗ϟ@}/_?G_|߾| />/_~˷O`1bĈ#FO_DET/߿}ϟ?}?} /@~o|| O`>۷|G`_>~_'p "Lp!BC} ._?} | 70|/?`>@~O`| /|o`"D!B!?}q!|_?O`O?~'p߿|/߿O?~/߿|_?}߿ ? 4xaB 6t!?}Qa|o~/|˷|/| | '0?}'0 _>}'0߿||o߿| "D!B ?}Q>~O ?~| ̧/>_|o|/_~O`o |o_>}˧O||/_}ӗ/?!B"DA|P?$XA %? 4xaB 6tbD)J䧯B}*VOa+VXbŊ%WQ>~+VXbŊ+VX!?}Wa>*VL ?~ XbŊ+*䧯B}*>'0_E*:Wq`+VXbC~**ǯbA}?~_~_>ӗ/?}˗ϟ@}ӗ@~ۗ?}˗ϟ@}ۗ|˗|/_>ǯbŊ+VO_EU,ȏ߿|0߿|/߿/߿|?~۷}/_?__}_>~󗯟|/߿|۷}/,h „ 2l!DEt_Ąۗ_O`O`/| '0߿|`/| '0|O`| '0_Ĉ#F1B~":/|_o`o_>~O`>0@'P`/| '0|/߿|O`>8`A&TaC!*/C}"*ԗ?}O`>'0߿|o`/| O?+_>ӷ| '0|/߿|_>1bĈ#FTO_DETO?߿| /_~ӧ|o| '0|˗߿|'0|O`>|㗏߿|O`>1bĈ#FDO_DE1bĈ#F1bĈ#j ~8`A <0… :|1ĉ+Z>È#F1bĈcE~0#F1bĈ#FxP?1bĈ#F1VA}0bĈ#F1bX>È#F1bĈcE~0#F1bĈ#FxP?1bĈ#F1VA}0bĈ#F1:FxP?1bĈ#F ˗FxP?1bĈE$X/_|'p "Lx> OB *TPB *TP„8`A Ϡ?$XA ӧP)Tx0_O@ H}o|O |'p "Lp!Æ?~#ϟ㷏_>'߿|}۷}o?} /߾}O`>'||=| ?}Ǐ |{B"C_o}'߿|O3_@/|_>~/|Ç{>~C{h_| O?} W0?`> /?}ۗ߿߿O߿|/߿ _~__o@ HOBSPB ˷OaBSP|/|7| O?o?_>_>}˗?@~O` OB ӧP)TPBۧ|)Tp` 70@~'_|ӗ/_>˗|˗_|˗O>~O_>}˧Oӗ/??}SPBO˗O|˗OAO@ DPc8_|2dȐ!C 2dȐ!C w0_> p>~ 2d|/? 2dȐ!C 2dȐ!Â̧ϟ?}/߿}ǐ!C ˷o|1dȐ!C 2dȐ!C `}'0|;C 2T/߾ǐ!C1ȏ߿| 1`> 1\!C;߾} G_>}cȐ!C w_|2dP`>1/a>#O`> 'p A W` $O_A/|Wp>~ H*\hp_}!C>~/?~?} ܗ_>7P_>}|ӗO@}/_GП|/˗ϟ@~ӗO?~/| 2O|O`>wP? 2d߾|˗C ߾۷| '0?߿|/_>#O`~'0| _>~#?~_>}|۷| Hw0?}o |;_„ &L0}/ &$o`/?;O`o`>}'0|`>O ?@~'߿|/? ̗0a„K0!A}&L0a„ ˧/_> &LX0| 7`> /| ߾|O`~'0| O@o_>~8_> `>? 4x@~&LHP &L0aB0a„/߿|/>~`>}70| ̗?#O`>@~'| O>@~/a„ ӗ0aB%L0a„ /_} &LhП|'П|ӗ/?#O`O_>}˗|_||ϟ|/_~_>ϟ|?}/>} Hӗ0aB%L0a„  /}%L0a„ K0a„ &L0a„ /a„K0a„ &/_>ۗ0a„ &/a„ &L0a„ &L8 /a„ &L_|˷/a„ KP` &'0|%L0_„ &Lh /a„ &L_| ˷/a„ K0a„ 3` K0a„ ӗ0aB%L0a„ ˗}%L0@~/?ۗ| O_| '0@}/_~|'0?~o_}/?/_'p_>~8`A&TX p… /?[p!|'|/|㷏`>/?}_} /| //@~_>~۷| .\ ?} &o… ./_> ˷o…˗@~ '0|3O`>/|/߿| _O '0߿|-\p‚-\P .\|-/߾  `>__߿_Ǐ_>~_~/߿8Ǐ_o`>$XA o„-\p…|.\/߿| _>#|O`>/@~| ̗?߾_|/|/… .,O… [p… o}-\0߿|O`>#_|ܗO>/>}_|'0|/_}ӗ/˗O>~O_>}.\paA~.L… .L/|8`O@ DPB "Ç ?}>| |ܗÇ>|Ç>,OÇ{Ç>|Ç>|!?}Ç>|`{0߿| =̗0C>tOÇ{Ç>_=l_;_>dOÇ{Ç>/|/_>/_>/_>/_>/_>O_|ۗo?~/_>o|䗯߿߿/ 

~O`o|ӧO`_ ߿|ȏ`>?}/&Lp ?} &$_„ &L0a„ &4_>'|__>~/|˧o?/߿|˧_| /?O`>/| &L8 ,h „ 2l| `>o@_(߿߿|_}_Ǐ_(0?}O` ߿__ 

''|/||/|`>_ /߿}O` o`>?O` &O_„ 0a„ &L0a„ O`O`_O`Ǐ`O`A}/>}˗߿|/|˗| _|_'p "O_„  <0… :|1ĉ+Z>È#F1bĈcE~0#F1bĈ#FxP?1bĈa>1bh>È#FaD#F1"A}0bĈ@}|ӷ/| /?}/_>#F'Ӈ>~1bĈq`O`/'0?~/߿|O`>~aĈD~0#F1/_A} __|_'p "Lp!Æ?}>|ÇO`?}O`>O?~+_| '0Ç>|P!?}Ç>|p`_o`|O>O߾ '0Ç>|P!?}Ç>|p`>~˗O>~/_|˧O?/|O`>{Ç"=|Ç>|Ç>|Ç>|Ç>C=?} HO@ DPB >QD-^LOƃaĈ#F1bĈ"?}#F1bĈ#Ɗa<F1bĈ#F+O@ ,? 4xaB 6tbD)VxQ!@,h`A$XA .dC%NXE5nG!E:ϟ~F9rȑ#b|F9rȑ#Ob|F9rȑ##b|F9rȑ#'a|F9rȑ#+a|F9rȑ#/Oa|F9rȑ#3a|F9rȑ#7`|F9rȑ#;`|F9rȑ#?O`|F9rȑ#C_}#G9rȑ#˷oȑ#G9r~9rȑ#G/}O@ DPB >QD-^ذ_}1cƌ3f̘1cƌ GP_>3f̘1cƌ3f̘_}1cƌ3f̘1cƌۇP_>3f̘1cƌ3fX_} 1cƌ3f̘1cƌP_>3f̘1cƌ3f_}1cƌ3f̘1cF탨/3f̘1cƌ3ZoD}e̘1cƌ3f̘b|(/cƌ3f̘1cƌgQ_3f̘1cƌ3J#3f̘1cƌ3f(2(2(2(2( ;;PKvCCPK|%AOEBPS/img/adfns108.gifR GIF89a???999@@@///```OOO000 ooo___rrrరpppPPP***UUUֺȻ:::GGG>>>...ݵ<<<!,pH,ȤQk:ШtJZvzI&L.wynsx`8~Dzh3!1qwh7en7[fGI  GCG G8FBB 9 C  cB[ʨEE 0ۆA?WP0ݱNޅG@8(Dήk݄8X"0g)i'^/ @h@Er`*B0`NByZh֜xs D6CA'|5GRL0 \S5Q dБ'i@*RdpaN@T20x Cc-' 2UϜRAf(,j눯I 7ƇNhx8 =]*` 1a Jv3> *Ӄ_9VG>FO=QDB(RNEᆮ00rD P(*<:Š4H ㏝DLYc%J<2dRq$HN>G[HeVFeh>)p)g U E rnDyQx`F$@O\CDG>QrpS Ndւmmz~k]vT@uJ2DŽkP̫ot>. O ۰D i_+0aA< lr. D0/<@-=mW$Kl-!.ԸH-թX-֨`p}%,פ+ڡ+xWWs|,1ZuI,u5 5d}+D$$ nDb 5ḬAI9h$yؠQdPzI8I{: [Y~@`g@e-s{x?agD$D8l^C闽C}xE%79>Mus6(S&.@F Ǽc\ACMp,8zP%"F(Sj"' 0T '.0Bj p#&a-)8@ C QKDlD%JMA%uOrBF7.A&?ݬlօ {8G0 yԣ<@#x<rD sE}4pREFd5l X(P(|B?% 7& 좨=Gh8)jOg,PU_z9G84B g&* vz1> :Z "ʐC>]A@ 迦1+ VFBE*n58%7uw5N^fUVUR3(Vb؄j:L8,& ja i_5>ajjD s`p.׶WUhu悸1`P] d 0 Եn T'VH"v"90: 'U$G Rȯ~,<`z!NI;E@&L s ΰp]&- GL8 HCp0qh 8f3dȱx, `2#"JV6Љ&89P1}\e'"C$3 Bw, Vc`nC*oC?| !ŏ$> <'S! P " h`' PtcF5хLdMI4vR8uüv)f3x;^07x}!%oL wogcXў}q(~| K̰ i8 [ qz&(@Y:l?B J}ҋr#律 X-ր-/A=z W ib|tfʟr0 P=@`gT69sSPI`zt?c}n"FZ2 xę*G ݘ?̷orP>Pk^a<*y?kx!(O ^/w~qG qG?7<J܏o/ wp 78#F0EhB8!P\En8>rgB?E] EESP&>\,&wRN`ULT2?E aMpMp_:r$PYMI@hv%\N]N M8xI``Zr]a cXph jІnp0t(M@wv|Ї~v0xd _UՈm\QRXJ0Q<[Qe]\NJ^0JZY9W5u[XՋ\ K02P]z(UhD3X8ߥ.淍./:AbKߢK-hXe880R^ Lpgwg|JpcF$ Wgf F2z0 ~vY3;4%!#hs>! ,ɏg`07;G1ntI0H>Y CHC@H`Y I'Hnb9);EqCoْ?FPm l jy "_yu9縗sٗ:9-<x)2Qy^9-B9YK,$6CٙY#.p-XL.iY꒚;PKW R PK|%AOEBPS/img/adfns079.gif9cGIF89a@@@???___///OOOooo``` 000ppp֐333PPPñ;;;vvv999,,,--- 555777888>>>XXX...===wwwxxxFFF666uuuqqqZZZ***JJJ444iii<<TA1*@7t% 0 2)xY䜺-IENVD2dPфEi ~4T2Nh`D4)*by祪ىDyRgD7ȠW"*h7ׂ -@$'v:ѧ p@XjDWB3:a )˾w؞k}ѶD:(2+*AސBݬضv(})1Q7 1@ A5x|3M{I  &ʗ.לw|6Ltyo bE7 DNpF}CHrfp#v7 vD{*wКLjw`ls'mxsc9[oLKmK/@HYMp<=fOčYk[?lP|7/tA/>}DxK Y}u\_˶AX872 4V=0F7b+h6u#\Rmph@5 N4J9 UWn8`% ph8B J@ W>np ݠ!1>`.I)H*ZX"7s@:n o8 eC1pełC74 =o h7e oHHAfD@5m/'7q Vƫ.y"R-bdű4e [P E@'ww98%&8; }BkJt96QDִ7BV 5?FR, +_ucLk]nҙ !y$02Lm\&8ZΎ=,ǝLa>/Q%Os4gP具3͘Dq&׷eEe PhG"01SBJMNzg)7EC]dj)iSѴ0HvAE_JmX8Ba^8Af0)8*MԢ JA iHkn`ü0ޓfg:-b6ED{]л!xL:qћ N-vnufm"O+nE8L_:#N-K68H# (YgNgyGQ&/4O40ڍt`/Z )- c;o8ycc8YnZb?2 kl'Ƿ1]6BɊ #&J@9ٚw+cYWZ[/o*jlK:j> L7S'Ρ3d4z3 ?&Ђch :ZtrݙGC?F5cK'ٴeZRԨNWVZw` WָεwkXX㩊MbN6Th&Nl%/ LѦδWY!KQ> 7Nm5VhZd 21ixrNyo Vw؝o(UDkU {4s+8p5wiEZ%4 }TE<=rfg~?~ynPōנ `LlqUnEY>=E_gQT~;m>_D ZSDfQa"0 \`":&4`"< LлDK$Z#l3 ^%yM~l|yGYo z-v$O)A7_@: vxw{#"Vo1p{$>e ?;~{Y{xYzzYz{~57wG}g|~5ѷ{7ǁ1|W|Ge:WGw8X3PG (1WGF 0PG3P8؄уuăBHAeGIEX^ńNXS(:x?D`qxb([df9y>(vm8rH^؅w8t5eX3suxsofqrhfo(fh3nXfX3mgX21%` "wvgge)tԱs+G 2Uxg/4'sa]/qBg/%qaF8,޲`QXh٘-16ݸt22 /"#"03h-%wr1!.*P2/8B42p=1Qvs3B,b%PR!5""ii - &qk,2%2LQ Tȋe!qCI`Nw;:5_95sG@8CYp7lŭۑ@eCG?}c= >>7@<\F̖̊21 5Sʓ5wVUf3T8tEtTGCbd:EeH@:G ƶM =eHˤL4ߣ͔UXCijW c$&ծQaO#KC+DwS+AQ^Dz4T5- 3YZ ԛبLw=eFeDUHJlDI,!jU8L؝6Z-R5b8;rG`45!@ȿɡS>ֆS=,`MC^c9Y|L6t:P`MF?t!q) >'ߐv9Elƶٜ,ܬ&~(h,:3~5N7cQٞ'G81j?O 2bAÌ˱<6,q S`s˺QBߏ`ky5R35g1vc"r^,Bx}4Y冾Nja_IT8RIr7^BĀǓ7Bč|<\`~LwX?8??>ZyL>ԨOσs 6"WpXџE_(Q GLO8@hn:QFTM Aܺiড78,X[B5nG!E~ H)CFyP `27:j2rf;v占#G2)coh!b\$JPXHHO d%EGf{TY'=aR#%Lyʏ,MT,T+}JXB s4z pLd&Sdf3LhFS,Gh)(["KMpS$g9yNtS7~rIL6%(h1^_Vi:u?eSjU?%̂TOAweP!"j+BL CvXeTch>i&)N:lsh%_꥘l}"o2mi Zm,ZmljJm*m"n(ZJnkJrn«+opU@XTY}eEP!np hPHAŝ@iR/0ܢzfm+[,)XJP{*7 ڳ{-r ~wKgenwy6- nEDxS' 4-H~@ no jЂf2(`|y^VU@|'хCLNE7) $/:o6W*P;^}}{x] oT͊E7D5;"3cy~}_Zo΋fO7n=uwY]z'@ML?{#>=Ӻ2n9n? H + : (2*@:'I@=x QQ8D8HA8ړ*੬2:ϰt83B 86*BP,Dv{'u5\6%Ԫ89l: =B ~zH>9щc; {KM2CPɮIB΁MnRNp:YD;o T7JtF T*l(,7< |%-OϏ*PWqP,PPP:PKP\P 1=OP*Q&e?oSxSSC=UԚdFGe,/B$)TLJ%KsC~TRPQ]ՠ TTXMYuZ%W9U?U_ Sab-VUHVYVfg 7lVmVnVoVp Wq%^qRu`5W=VXWyEvuW:0W~W2WIq,=X=AX{%FbbuXD)B%U،]ՍퟏՐ--I={%ed>%-'`&h:^;c ;c3c;`' XHbK!5''&KdLdMdX&(cc.RAع(X- MeYLd&bD1A3eZ.fc-X@IeM`a>fkXނ/p$(ghnJftNK&&p-3d3} /n`Pg}gv CY^bւ-ggey>z\|NhNmnh:yhqm bhm4m'蒎cfhy>&i6ffhfa^M,`ajd&1.>jsj~Hi^i6%N䷶7*iSkpk&Pfe6ϱ 'X.i6~E-pgf5xnkm-m x3Xmu^[E@ؖ٦1gm Bp Vme)%`ƂmmV n cp '0eȄ@W hlVn/aneqnn HdxH~ohP:hU؁ ,HF܎_'p ZЙW&wU(p 75pi>lFo (pGSЉ&Ȅ%Pju-hnXp7  oqmq#"W&8f/c:;r?_9^fI6\h .rq1m߃ ǒgg"?8pXx\ K(q,65xhG/Dp4em5Ps9R?T_8 $0P(B[8@?K #u^u`.0/DpdfgN?O'r7ivT_lvnvpW#`t_vx]_wt|t0qKqxOvP?xi'uS_oxvwwwywwa}=9ނo7ujkvyxuyw?wW~zwyzy'z|Gz_Fhzx@̓ީvWwxz'|'v8xf dlgˇ̷}x'}?g}o x0 ~eĻE 0?ݧ{||~|7}/o hr(Eȍn [nf"ƌ7Zȍ#Ȑ"G,i$ʔ*W @(PC(b|@$D.\X $1aB GXb:N@@A$xc|\`B 0?zH``A# |rK<ďvp =j3Т]”I&N<}J(RLBJ*V\K,Zl•K.^|P<:0ux cDžv{dќ/o|bάy3Ξ?-z4ҦONz5V]}XeZm\uݕ^2sh ,fQCd^xYvyg!)zjVlͶm ovp  r %H%D@(!AuQC$$aRZ|}W[ۀ\1x܃!Dcf$hH@Mu)0dzijf_lm&`op `ra bDq,Ŗh: gMjrEx'B2(k窻nz c 7s7ch74l;kzZ+kذKMVC]P1 @8KJp3䶷<)Pna&=Px4~,f4RSmO7^pw޵;p%yD>;֎qw|Ǽ?T0<^Qd9YkqG'ңw, E@:EPtC H@B2SAG`QKWR1M^e>! '!F6li47p)V= p(tcC8D "DqRH9 $D{C (&ƋYxxVr)h `p b i(-Bđ\;Jĸ2 LC:ElQl5p.yu޸I9Etܩj4-~jGtՏ8y Ⱥ6FfhLlh$ec)m <#d{|h~y;i_p*`5HyFj&0I=;Bl!- 2tbaH$43x@H&rI"kp <]n|D ,p7b5 PK%*ca`\]$Y#J%!VYTrvٶV`WN xZVTMzbFMiܺ)[{N~S2k|܃*I6t>?}´qkw{iFucn]{RxMf۳n`3`֓ ߹U0 <0B .V'_ԉ "S4]ܮ`dK{Qy۫= z>L]耟|MGw(+|iڟz @]yk_;TWNpo;M㑞n^MT K_AA2d S P\1^a_Q `e y_U Uu^7l<)%  4`TJ!7û؎Ji`!!J$$  XS, pC)$ Z aݡ]l d!^am !ව  ; b'N dvB])aE^$6! Bbb -.jA$*S/8w8$,,3"4F4R#bd/:"7`*Ёw -P&b&@6$ 4C#7؀?f9K:RAA@&+x܂+« 6Jd!l wAaGKHH$, 6$OXA,=J"(-p*w8AY @O^ F뤥Z%[[%\ƥ\eg%-\@D'`9 &aNΦy\*p& YMbbvg~&hyh&i],]L/d\BԦmf hA6#]Pd2 ,@ P@ @i6s>'t~i \؀wt]܍ g%@rNg;PKԦx99PK|%AOEBPS/img/adfns092.gifRSGIF87a?**?***UU?UUU????**?*******?*******U*U?*U*U*U**?*****?*****?*****?***UU?UUUU*U*?U*U*U*UUUU?UUUUUUUU?UUUUU?UUUUU?UUUUU?UUU?**?***UU?UUU?????**?***UU?UUU?????**?***UU?UUU?ժժ?ժժժ???**?***UU?UUU?????***UUU, H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϒ8`A&TaC H*\ȰÇ#JHŋ3jH_|8`A&TaC~ <0… :|1ĉ+Z1ƍȑcC}8rȑ#G9rȑ#Gqѡ|9rȑ#G9rȑ@}8rP_?9rȑ#G9r_>9r|G9rȑ#G9r/G!Ǒ#G9rȑ#G9Ǒ#Gqȑ#G9rȑ#G-ܗ/> ˗|%Ǒ#G9rȑ#G9˧0_>S/_|Ǒ#G9rȑ#G7K/_?__|߾|/_}˗o߿/߾˗,h|)ӗ? 4xaB 6tbD)VxcFK/|ۗ/>~_>~o߿|/?~۷_~(_>Q)O@  <0… :|1ĉ+Z1ƅG0_/~/>˧?'0߿|o`7OT>~ H'p "Lp!ÆB(q"Ŋ/b̨qa//?˗O_|/_}˗o߿|_6ԗO|6˷qƍ7nܸqƍK/>o`?'P_?~o|/70Ƃo|7nܸqƍ7nܸqa>˧|ۧO |O_|ӗ/>˧߿|˧߿|m4ȏFmܸqƍ7nܸqƅ8`A&TaÄ9t_>:tСC:tСC:tСC 9tСC?:tСC:tСC:tСC2СC:ȏCsH@~,h „ 2l8,h „ 2l!Ĉ'R1E-2A ˧o@~,h „ 2lO_|8`A&TaC!F8bŇYhB},!|,Zh|,ZhE-Z8P?gѢEx_YhѢB~,ZhѢE-Z8_˗oE-"BhѢŅYhѢE-Zh>}7o^|,ZX_~_>-ZdϟE-ZhѢE ˗/yP <0… 'p@}8`Aˇ ?}"D!B"DH0?$XA .dC%NXQb}? 4xaB "O@ 4/B!D!B"D!A}8`A&TaC!F8bE˓OE-Z\/C~,Zh>-ZhѢE-ZD/_}-Zh|S/_>}˗/?h>-ZhѢE-ZLOE-Zl/C~'p_> /h>-ZhѢE-ZLOE-Zl/C~O|| ̗O`>}|/_?'_|˗o}XP?-ZhѢE-&ϢE-60 #/|_> ̗ 7P?/߾'0?_>~ $H@}8`A&TaC!F8bEYhѢņ'p A'p|'0~/|/|ۗO`>o_>/>$H AO@ DPB >QD)gѢE?/>/|/>~o| ̗_} /_}˗OEYhѢE-Zh1!?}-Zh|C/>o` ̗|/|O`>~_|/>~HP?-ZhѢE-&ϢE-6!} ˗O_˷O|'P_}| ԧ/_>ۧ?}ϟ|'p   <0… :|1ĉ+RϢE-6!?}-ZP?-ZhѢE-&ϢE-6!?}-ZP?-ZhѢE-&ϢE-6a?}-Z0?-ZhѢE-&!@,h „ 2l8? `A'p "Lp!Æ 9tСC:tСC:tOCP <0… ˗O_?$_ +X` <0…cȐ!C 2dȐ!C 2dȐ!C1̷C 2dp!|p>~ ˗/> 2dp> 2dȐ!C 2dȐ!C 2dO}2dȐ!C ӧ_>P!|yǏ!C /C 2dȐ!C 2dȐ!C 2O|2dȐ!C | 08 |͛/_ ,X` 'p}8`A&TaC!F8bE-gѢE ˇ_> H`A}͛OA 4hРA8`A&TaC!F8bES/ŋ/"o>_(ʢwŋ/^xŋ/ӧ_/^D_|D~wŋ/^xŋ/O?}/^P?+/ʼn]xŋ/^xŋSO}p_|]XQ߾G_xŋ/^xŋӧП> ܗϟB}㗏ŋ'P}_>$XAO@ DPB >QD-^Ę>%ܗ@/?~/@~˗|'0'0_|'JT|#/_ƈe̘1cƌ3f̘1cF~8`>} 80_>ǯ?}/_}ۗϟ|ׯ_ۗϟ|׏ A $H?D͛'*߾}o_>$XAO@ DPB >QD-^Ę@~O̗`| o_>˗_>}o`~˧O`|$H A /yD˧/@}'p "4/,h „ 2l!Ĉ'Rh"ƌ K/?o`}o`|˗O߿|_>}/F)/?4ZOF5jԨQF3ӗ_>}/|/O~ / _>'P?8`A&/,h „ 2l!Ĉ'Rh"ƌ So_| 7P_}˗/߾O`> /_>~io߿|h_>5jԨQF5jOF-/?4ZOF5jԨQF3˧QFϟ> ? 4xaB 6tϟ?$XA .dC%N/E)R_Q_'p} H*\Ȱ@ԗ/_?$XAO@ DPB ? 4xaB cȐ!C 2$߿|cȐ!Cԧ!C 2dȐ@~1d_>~1dȐ!C /_? 2d0? 2dȐ!Áϟ> 2aA2dȐ!C ܧ!C1dȐ!C !C *Ǐ!C 2dP`|!C cX> 2dȐ!C׏!Ã1dȐ!C ԗC 2D/_? 2dȐ!C?} 2d/Â1dȐ!C 2,_>  <0… "/_?:d/_>}8`A&TaC}'p "L8_>SPB *TP|PaB}*TPB &짯_> *TPa>} H*\Ȱ~?~8`A&/ƒ)TPB *Tp`>P!B}*TPB &_|*TPBSPB *TX>}SPB)<ϟ}P`|SP@ *TxP~S> *TPB O|*TPB'p@$XA .d(`>_~ HOA}'p_> _>~ *OB ˧_)Tx0?O@ / GP`>$XA O}*TPB'p "Lp!Æ(_>$XA ˧>'_}_|'0 _>~/_~˷O|ӗ/? */|Oƒ%̗O|˗O!|)TP!B/B *T_}8`A&TaC <0!A8`8P`|_'0_>ϟ|_'0?_>~'p "D/yo@,hР> (p_~/_|O`|/_>~ ܗ/| H?~)TPB O| H*\`|'p "Lh_>$XP> (0_>7}'0_>'0|ۗO`>}˧O`|'p "D/߼y'p #/߿|_>˧O`߿|O_>$XA )TPB *TPB *TP„)<B} O߿|O`|O>~ />~ۗ/߾|˧_> *$/yǏBC/|؏߾|+_>}_|)TP!B/? *TPB *TPB *T_>C/>o` ̗|˗ϟ|_ ̗O?˧PBӗ/_)TxP@ (P_>/|/ӗ/_>}˗o`>$XA )TPB *TPB *TP„)<_BO`'0_|̗o`˷O?ϟ| ԗϟB O|S>'p_~ _>O?o_>SPB> *TPB *TPB *T0| PB)TPB /?~*<_BO`'0_ӗ/?˗o> *DO? 4xaB 6tbD)&/>WbņXbŇ>+VXbŊ+&/>X|Ǐ_EUXCO+VXbŊQ_+V0?WѠ>+VПbŊ+VXbńEǯbŊ+"_U4_Ŋ+>?~XbŊ+VX1|XbŊ/?~*ǯbŊoUXbŊ+V 8`A'p "Lp!Æϟ=d/Ç>|0_Ç>|Ç>L/Á{ÇO ?}{0_>| |Ç>|Ç>|_P <0… :ԗ/>?$XAП <0… /,h „ 2l!Ĉ'RL/~8`A&TaC_O@ /@},h „ 2l(p_>$XA .dC%N_bŊ)/?UT_Ŋ+2?*VXbŊ+VT/_8`A&TaC_>$XAO@} H*\P>̗߿'p "Lp!ÆB(q"EUO_Ŋ+V4o_ņ'p "Lp!Æ?}8`A&TaC!F8B*ԗo@~,h „ 2lP|8`AB"D!B ӧ? 4xaB 6tbD)."A'P>$XA .dP> ,h 'p "Lp!Æ'p "Lp!ÆB(q"EUXbŊ+*bŊXbŊ+VXQ|+VXbŊ+VXbŊ+VXbŅUXbŊ+VXbŊ+VXbŊWbŊ+VXbŊ+VXbŊ+V\/_Ŋ+VXbŊ+VXbŊ+VXq|+VXbŊ+VXbŊ+VXbŅUXbŊ+VXbŊ+VXbŊWbŊ+VXbŊ+VXbŊ+V\/E H*\ȰÁ8`A&TaC!F8bE1f_ <0… :ԗ/_?$XA'p "Lp!ÆO@ DPB >QD哘o_Ŋ+V,/ ? 4xaB 6o_~ H*\ȰÇ#JH|XbŊWa~+Vذ_+VXbŊ!߾+Vx_|**䧯bŊׯbŊ+VXbEEǯbŊ+"/_EUXC~XbŊ+VX|XbŊӗ_EUX~XbŊ+VX|XbŊ/_XbŇ˗_Ŋ+VXbŊ 勨ϟ}0_>ˇ_|_Ŋ맯_>$XA} _|˗A 7|17_WbŊ+VXB"p_~/_|O`|˗O|ӗ/'0_>/__|*F/_>}WѠ> //_>@~o`>O߿}XbŊ+VXQ!@8`8P`|_'0_}o_>o ̗O`|O`G A $H_|ɗ?~ $H@}ܗ@/?~//|ӗo?}ӗ/O`>~ 70?}ۗ/? ?~'p "Lp!ÆB(q"E'p O@̗_'0_>}/>7}'0_'0~#H A $_y˧/_>$XA (0_>ǯ?}/߾/|o_>_/߾߾|G A/?$XA .dC%N_!ԗϟ@70>~o|ӗ/_>} ̷_> ̧_} 70>~*Bo޼yӧ ? 4hP?/|؏߾|O_|/@~_O`>70|/AǏ߿|8`A&TaC!F8bB"_>}/|/|@}_|/_'0|_(C/?o`} ̗>~ ̗O`>}(p߿|߿|_|O@} HP?},h „ 2l!Ĉ'RL/_D}ۗ/~ ԗo>/_O|˷O|'P_}|U/_|ǯA}˧O|_|㗏_|/_/߿| 70|/ O?1?~XbŊ+VX1|XbŊ?~ ߾|o| ̗_~ ̗/_>~/_?}ӗ߿| ԧ`|O߿'O?~ <0… :|1ĉQ+VPW`>+V_bŊ+VXbńEbŊ+"_U4_Ŋ+>?~XbŊ+VX1|XbŊ?~ WbŊǏ?}*VXbŊ+VL/_}*VX"|Ǐ_EUX~O+VXbŊ߾+Vx_ǯ}*VX!}_Ŋ+VXbŊ O@ <0… :4߿~!?}>|aB}/>|Ç>|0|Ç>|8߾|ǏÆ=|Ç߿|>|Ç>|Ä=$`>'p "Lp!Æ8P>?$X~O@ DPB ۗ߿? 4xaB 6tbD)&b|8`A&TaC /@}8`A08`A&T>̧/?$XA .dC%N_П <0… :o_~ <(_~"D!B"DO'p "Lp!ÆB(q"EU/>O@ DPB 'P>}/,h ~O@ DPB ӗo߿'p "Lp!ÆB(q"EU/+VXP߾ O@ DPB >~ H*\ȰÇ#JHQ|O@~,h „ 2lp|8`A!B"D!B <0… :|1ĉW`?$XA .dС~ Hӗ/_>$XA .d0_|8`A&TaC!F8"C*VXbŊ'p "Lp!Æ8`A&TaC!F8C*VXbŊ+VXbŊ+VXbŊ XbŊ+VXbŊ+VXbŊ+.bŊ+VXbŊ+VXbŊ+V_+VXbŊ+VXbŊ+VXB*VXbŊ+VXbŊ+VXbŊ XbŊ+VXbŊ+VXbŊ+.bŊ+VXbŊ+VXbŊ+V_+VXb'p "Lp!ÆO@ DPB >QDXbŊ+RO?~ H*\Ȱ!?~O@ DPB >QDXbŊ+RׯbŊWbŊ+VX"D*VXbŊXbŇXbŊ+VXѡ|+VXbʼnUXC~XbŊ+VX|+VXbʼnUXC~XbŊ+VX|+VXbʼnUXCbŊ+VXbŅUXbŊ'WbŊo_+VXbŊWbŊ+Vϟ}0_|_Ņo߾+V*"? 4xaB 6tbĆ%̗O|'0>ItO߿}(QD%J(QDI(QD%J$} o߿|/_? ̗| /?~_>}/_>~o_|O@/D%J(QD%OD%J( @, ̗`|O?_|O`>~/|/}_߿|$H _> H*\ȰÇ#JH1|+VXbŁ8`|8P`|o`?~ ̗/_>}/|ۗO`>O|O|#H AǏ߿|8`A&TaC!F8bB*VXbŊsB} O߿|O`|O`'0| ̧/_|O_|WQ?}_Ŋ+VXbŊ XbŊ+ϡ>'p_~ _>_>/| 'P?O?WQ?}_Ŋ+VXbŊ XbŊ+ϡ> ˗O_?˷O| ˷O|ۧO`>O_|/_?}/,hP?},h „ 2l!Ĉ'RL/_Ŋ+VXq|XbŇ>+VXbŊ+&bŊ+V8_>UXCO+VXbŊG@~,h „ 2lp`?$X| ? 4xaB 6DO?~СC:tСC:L/ <0… :ԗ/_?$Xp| ? 4xaB 6DO?~СC:tСC:L/C9tСC@СC:o?~СC:tСC:L/Á9tСCp|sСC>~sСC:tСC&@~:tСCs8_>sСC'P9tСC:tСCp>~:tСCs(_>ϟ:tB/_?,h „ 2l!Ĉ'RL/_D}*VX"| |,h „ 2lHP>_> H*\ȰÇ#JH1|XbŊ/_DUXbC}WbŊ+VXB"WbŊ_>O@ DPB ˷߿~8`A&TaC!F8B"p_|1̗/_>}*2W~Ab}( ? 4xaB  ܧO>}>~ H*\ȰÇ#JHQ|K/}-̗O`>}XQ|{/~UX"}*VXbŊ+V\/_D}O|| ̗O`|O`~˗|㗏}/_>'0_|˷A~DSoC*˗/@},h „ 2l/_}8`A&TaC!F8B8`A'p|/~O`|O? _>~ ܗϟ|/?ǯ߿|G A ܗo޼yy8`A HA H*\Ȱ!A$XA .dC%N_>$XP> (0_>7}˗/>ۗO`'0| ̧O`|˧O`| ̧? $Hp |ӧ? ? 4xaB 6tbD)VxcƈE|˧_'0_'0|O`>ӗ/_>}˧/_|o_|4˗/O|˧QF5jԨQF Q?/_/| /|O`>@}@}o|4/_>~ӨQF5jԨQF勨_BO`'0_'0@}O`>ӗ/?0_>~ϟ>'p !B8`A&TaC!F8bE1f/_D}4jԨ1>ӨQF5jԨQF勨F5&|5jԨQF5jԨ|ӨQƄAO~ ? 4xaB @'p "Lp!ÆB(q"ʼnEǯbŊ+"_>O@ DPB ˷? 4xaB 6tbD)F/"?}+VX`|{O_Ŋ+2O_Ŋ+VXbŊ哘_Ŋ+V4o_DWbŊXbŊ+VX|WbŊۗO|XbŇXbŊ+VXѡ|'p@$XA .dP | ? /AO@ DPB ӧoC:tСC:t谡|'p "Lp!Æ'p w>}˗_ņǯ+VXbŊWbŊ+V/C}O|| ̗O`|O`~˗|㗏|_/_~C_>$XA .dC%N_+VX@8 A}8P`|_'0_|O?/o___>#H?}? 4xaB 6tbD)*bŊ+V8|, ̗`| o_>˧߿~o`?~ '0|/_?؏߾|㗏 AǏ߿|8`A&TaC!F8bB*VXbŊC/?o`} ̗`} 70>~O` ߿|߿|o>}8@/?$XA .dC%N_+VXD}˧O|_|+_| _>O|'0|O`|5?~XbŊ+VX1|+VXbʼn%/_>}/>}+/>} ԗo>O |/|ۧo`|Ǐ?}*VXbŊ+VL/_Ŋ+VXq>+VCO+VXbŊWbŊ+V_Ŋ'|,h „ 2l!Ĉ'RL/_Ŋ+VXq>+V?}_Ŋ+VXbŊ XbŊ+NbŊUXbŊ+V_+VXD}*VX!?}_Ŋ+VXbŊ XbŊ+N䷯bŊoUXbŊ+V_+VXD*VXa>_Ŋ+VXbŊ XbŊ+Rԧ_Ŋ+.O@}WbŊ+VXbB*VXbŊP <0…  ̗o߿,h „ 2l!Ĉ'RL/_Ŋ+VX?~'P`?$XA .d?~߿? 4xaB 6tbD)*bŊ+VXQ|UX"B_+VXbŊWbŊ+V8|,h „ 2lX߿O@ DPB >QD XbŊ+V$O_Ŋ+6_Ŋ+VXbŊ XbŊ+V,/>П <0… 'p ?}'p "Lp!ÆB(q"ŅUXbŊ+o@},h „ 2l?$XA .dC%N_+VXbŊ+VXbŊ+VXB*VXbŊ+VXbŊ+VXbŊ XbŊ+VXbŊ+VXbŊ+.bŊ+VXbŊ+VXbŊ+V_+VXbŊ+VXbŊ+VXB*VXbŊ+VXbŊ+VXbŊ ؏?  <0…  O}'p "Lp!ÆB(q"Ŋ/b̨|˷ ? 4xaB 6t(P_~ H*\ȰÇ#JHŋ3j\/|6nܸQ|6nܸqƍ7nܸqB"˷qƍqƍ7nܸqƍ7n$/_~7nܸqƍ7nܸ|۸qƂ۸qƍ7nܸqƍ勨ƍ7o_~7nܸqƍ7nܸѠ|۸qƂӗƍ7nܸqƍ7n,/_D}6nܸ>˷qƍ7nܸqƍ Q?|/_>}Gp_>` ,Xp>? 4xaB 6tbD)VxcF勨_|O|_|O`|_>~1̷>oƍ7nܸqƍ7?  ܗ@/?~/@~'_}/@~_>~|'p ",ϟ|O@ DPB >QD-^ĘQ#A H A (0_>ǯ?}/_}˗`|/_} 70_?'0,h ƒ?$XA .dC%NXE5/>#/߿|ۗO`?~/|G?}o`>o#D}Ǐƍ7nܸqƍ7n$/_D}O|o|/|ۗO|70_O`>6B_mܸqƍ7nܸqFEa|__>/'0_/O`>6B_mܸqƍ7nܸqF? ϟ˧|ۧO |o_|0@ |/߿ӗ,h ‚?$XA .dC%NXE5/>۸Q>oƍ7nܸqƍ7/>7nX0qƍ7nܸqƍ Q_7n,ϟ|۸qƍ7nܸqƍ勸ƍ7_mܸqƍ7nܸqFEoƍ _mܸqƍ7nܸqFIoƍmܸqƍ7nܸqFI䗏7j/@~qƍ7nܸqƍ 7|,h „ 2l?$XA .dC%NXE5b|  <0… O@O>~ H*\ȰÇ#JHŋ3j$/_}O@ DPB /_O@ DPB >QD-^ĘQ#A,P <0… :߿'p "Lp!ÆB(q"Ŋ/b̨|qƍqƍ7nܸqƍ ˇ_|۷O>$XA .dP>}ӧ/_>~ H*\ȰÇ#JHŋ3j4/F H*\ȰC8`A&TaC!F8bE1fԈ_7nܸqƍ7nܸqƍ˷qƍ7nܸqƍ7nܸq#B6nܸqƍ7nܸqƍ7nD/ƍ7nܸqƍ7nܸqƍ۸qƍ7nܸqƍ7nܸ|7nܸqƍ7nܸqƍ7"oƍ7nܸqƍ7nܸqFmܸqƍ7nܸqƍ7n܈_0 H*\Ȱ@Է,h „ 2l!Ĉ'Rh"ƌ'_}8`A&TaC˷? 4x} H*\Ȱ!}8`A&TaC!F8bD$WbŊ Wa|XbŃׯbŊ+VXbEE䗯bŊ+엏_EUXbC*VXbŊ+V/_D~*VXA+V|_Ŋ+VXbŊ勨_Ŋ+VD_>WbŊXbŊ+VX|XbŊۗo_EUX~*VXbŊ+V|/_D}*VX"|ӷA}*VX?}+VXbŊ+>/> Hp_|$H|ۗ/> H*\hP?cȐ> 2dȐ!CcȐ!C 2dȐ!C 2d0| 0_>c/_~o_> 2dP!|CS/_>}/_}'0_>ǐ!CcȐ!C 2dȐ!C 2d0| p_~/_|o`>} ܗ@1dȐ!Ä˗/?}2_|!| ̗߿}˗߿~1dȐ?2dȐ!C 2dȐ!C 1PB ?G0_|/| <0…5o^|cp>'_}_|ӗo} o߿|/@~/>}2dOC 2dȐ!C 2dȐ!C 'p 'p|/~/?˗`| H*\(p_yO@ $?G0_|/|/_}'0?| $H AO@ DPB >QDP_>/|0@_}'p "Lp@~͛/}8`AO@̗_'_/|˧ϟ|ϟ|$H A ? 4xaB 6tbD)Vl~ ܗ߿||_>}ϢEۗ/_Y,B} O߿|O |/O }o}/,h B8`A&TaC!F8bņ%/_>}/>}/_˧O?-ܗO_|XP?/_/? _|˗߿|/?~/YtOE-ZhѢEgq?-_Y,_BO`_o_|/_|O_|/|gѢE-ZhѢEYhѢŁ?8D_>-",", }8`A&TaC?~ÆC=|Ç>|ÇÇ>|0?>>|aB>|Ç>|Ç Ç>~>|aB~>|Ç>|Ç Ç>|aB~>|Ç>|Ç Ç>4߿~a|>|a|>|Ç>|Ç ˷@,h „ 2l_?~o?~8`A;xQD!ӗO|'p "Lp!Æ8P߾~?~ H? 4xaB 6O_> H*\ȰÇ#JHbDYhѢ}g!} H*\Ȱ}8`A&TaC!F8bE <0… :ܧ/_> <0… :|1ĉ+Z1ƍO@ DPB  ߿~8`A&TaC!F8bE1fԸq`|8r䘑_~9rȑ#G9rQb?} (> 4xaB 6/_>}˧,h „ 2l!Ĉ'Rh"ƌ7"O@ DPB  O@ DPB >QD-^ĘQF=~RH ;;PKU>RRPK|%AOEBPS/img/adfns091.gifQGGIF87a?**?***UU?UUU????**?*******?*******U*U?*U*U*U**?*****?*****?*****?***UU?UUUU*U*?U*U*U*UUUU?UUUUUUUU?UUUUU?UUUUU?UUUUU?UUU?**?***UU?UUU?????**?***UU?UUU?????**?***UU?UUU?ժժ?ժժժ???**?***UU?UUU?????***UUU, H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ П <0…  O@ DH? 4xaB 6 <0… :|1bC~ 8> 4xaB O8`A 08`A&T@? 4xaB 6tbĄ(QD'a|$J(QbAI(QD%F'QD0?%JP>%J(QćI(QĄId/D%JL/>%J(QDI(QĄIdOD%JL/߾|%J(QD (QD Ȑ>%J0|$J(QD'QD'!}%J(Qa>'QD%J0?%JП> (QD o>%J(QD)0_>}8}70_| W` ,/_| ,X@~ ܗ/> /@O`+X` ϟ8`A&TaC!F_|oa|'0D$J/߾< ܗ_C~o`(>OD%J@},H ܗ@/?~/_?_>} ܗ/?O_|$H A e޼y'p  '_}'_|ӗo?}ӗ/|'0|O_|˗A $HP>? 4xaB 6tС| H@}8P`|_/_> ̷_}ۗϟ|/_$H A ̗g޼y'p  #/߿| _>ۗO`_>˷O`/?$H AO?}8`A&TaC>#/߿|ۗ_|O`|˧O`| '0DS6/O~0_>G}_~ '0| ̗O`O| 6ϟA"D!?}O|o|'_>} ̗_˗O_|A0_|AdȏB} O߿|O |߿߿|O_? 4x>!B"D!B"DР|a|__>}'0?O?_>~!D!CaA~˧O|#_|'_o`>˷O`>O>~CaA}ǏB"D!B"DA"˧|ۧO}7P|ϟ|/_}!BCA~/_>}#P_}?|/߿?~O_>? 4xP>!B"D!B"DР|!B ̇!B"!BC!B"D!B?"D!B"D!B!DO?"D/>"D?}"4ȏB"D!Bϟ`> 4xaB 6,? 4x|!B"D!BӇ!B!D!B"D!|Ǐ_AC!B"DaAC!A"B"D!B!BC!B"D!B!D!B"D|"D8_>!B"D!BCA"D!B"Dp|ǏA"D!B"Dp`?}"/BC!B"D!!D`>"D!B!}? 4xaB 6D/Æ9$oC:tp_> ϡC:d/Ǐ ?}:tСC6aA}O@ DPB ˧? 4x|'p "Lp!Æ߿}߿'p "Lp!Æ s_>'p "Lp!Æ'p " ? 4xaB 6߿߿ O@ DPB 琡|:tСC:ܗC:t_}#ȏ}`>O@  <0!|*4/B *TPB *T ?P <0… 'p@~@ 80_>8p|8_'p "L(П|)TX_> *TPB *TP~8`A&TaC o@ (p_~/_|O`|/_>~ ܗ/| H ̗/|8? /,h „ 2l!Ĉ? 4xaB 6O> ȏ/_|˗O߿|/| Ho޼yǏ߾| ˧PB *TPB *Do>O@ DPB8P>~ ?#/߿|ۗO`|˧O`|˗o`>$XA 7o޼<˧| H*\ȰÇ#JHE!ԗϟ@G0>~G0߿|˧_ w"|)˷|xŋ/^xB~˧O|#_|#>~߾|w?}W_>]xŋ/^xQ!?~ ˗O_?˷O|O_|/_}.^/>Iŋ/^xŋ xEO|/^xŋ/^Tȏŋ/"Ϡ|xŋ]tE]@'p "Lp!Æ 3/C9tСC:tСC:t>:tС|sH_>:tСC:tСCСC:/AaAO@$XA .d`>? 4x ?}8> 4xaB  O@}'p 'p "Lp!Æ;/C9$/>~ H*\Ȱ@~O@ $O>~ H*\Ȱ~O@O@ DPB | `~:ta~:$oC:t0_?O@ DPB ?G A#H 'p "Lp!Æsp`>:t?~1Ǐ@}O@ DPB8?$/A A <0 "ϡC9tСC˗oC:tСA@}:tСCsP ?}:tСCsСC:4/C9ϟC:t?} ϡC:t(0}9tСC!AϡC:tϟ>СC:}9tСC!AO|c0@8P|8`A&T/B S/_>}/@'0B*Tx0~SPB *TPaA"OA}'p_> '0_>)TP‚OB%̗O|/_?KO| *<ϟ?}SPB *TP!A"OA}O|| ̗O`||)TPˣ/? p_~/_|o`>}ӗ/?}/?}˧o`˗߿}O@ ϟ?}C!B"D!Bˇ@8`8P`|_'0_>ۗϟ|O@ DPa(˗> 4h0?/|O ߾| '0~ ̷_} /}Ǐ A $O? <0… :L/Á'p O@̗_'`>˧?'p "L0_y 4 ̗`|o_>߿| 70|o`'0_> $H?}Ǐ,h „ 2l0|P>'_>} ̷_> ԧ_>}/Ç˧l^|2|˧_>'`>߿|_'߿|/߿  ~:tС9t(_>:t?9tСC;/C9OC:t>~СC:O?G_>:t~琠| СC6ϡC9tСCO?~СC:/A!~O@ DPB ۗ,h AO@ DPB o߿߿O@ DPB ;/C9,菟>} P <0… 'p>}'p ܗO|'p "Lp!C o_O? 4xaB 6LϟAϡC:tСC9tСC A~:tСC3/C9tСC:t|'p "Lp!Æ߿|O ܗ/> /_|$H A #H A A$H@8`A&TaC!F/@~,h „ 2l8߿O@~/} 8p`|O@ $!B˗O_A"/,h „ 2l!Ĉ'QD ׯ ?~'_}'_|'0_'0_|O`>~O|ӗ/'0_|3OT}/D%J(Q} (> 4xaB  O@'p ?~ ̗o|O ̗|/|_>_߿|/8p7o޾| oԗ? 4xaB 6tbD H*\Ȱ@ȏA̗_>'0_|׏߾| o_>_>}O`>} A 7o޼(o_>$Xp| H*\ȰÇ#JHE!ԗϟ@G0>~W0>~o| '0߿|˧_>˗O߿|'*O>~/ŋ/^xŋㇰ_>}/|/_|o` '0|߿|߿|˗_A$wŋ/^xE%/_>}#/>}+/>} ԗo>?}/?'0| '_/^xŋ]@'p "Lp!Æ 3/C9tСC:tСC:t?:tС| 琠|:tСC:tСC.ϡC:t(0?9$/C:tСC:tСC sСC w_>sСC:tСC:tp|:tС!AO>O@ DPB8P>~8`Aӧ`>8`A&T@Է,h „ 2lx߾9$/CП <0…  /_?$XAП <0…  />$(p>$XA .dР|琠| СC6ϡC9tСCc/>$XA .dp|8P| $(_> O,h „ 2lP?sСC㗏C'P>$XA .d(>O@$H@$H>~ H*\Ȱ!B~:/C:t|9tСC!AϡC:t>СC:_>:tСC9$/Á9tСCС@~:tСCۗoC:t!A@}:tСCsP ?~:tСCۧC:t@|˗O?˷|970CsP ?~ (߿| Ho`}!Da>ˇ!B"D!B!B!DO?/?'P_>Ch0Bԗ/_>~"$ȏ̇!B O`~!D>!B"D!B"/BC>'_}_|O?}˗o| ܗ/?O_|/_}˗Bˣ,J>"ȏ'p_|O|˗@O_|//_~/߿ۗ/˷? ϟ'p "Lp!Æp?$X?/|˗@~۷_>_>~߿|㗏߿}_> $o߼y8`AO _߿|/߿߿|/o>'0|_>~o_'p "Lp!Æp| H@}8P`|o`?~˗|O` ̗/_>_|70A ̷o޼y'p  #/߿|/| ̧O`| /AO`?~ '0| ̗_>}$HP>? 4xaB 6t_{(P?//?} _|'0߿|˧/_/| |yE!C~W0߿|˧_>O_|70߿|70>~o`|˧_}kϟ?}{Ç&@ a|__>}/'0?}/70߿}ۧ_ԗ/_>~0_?}O`> _>'P/߿~?~}O@?~ H*\ȰÄ=/C%/_>}/>}˗o?_>ӗ/˷O|/_>}~>/!|?}߿|ϟ>߿|@/>} />}?}7p?~ H*\ȰÄ=/C=|Ç {>|Pa>Ç>|_{(P>|0?}Ç*ϟ)O@$XA .d(?8@ ,8_ ? 4xaB 6DOCsСC ϟ`> 4xaB 6,?G A#H 'p "Lp!ÆsP ?}:tСCСC*OB}:tСCsP|:tС?~sСCw_>s8>:t?}:ϡC:tǏ|:tС!AϡC:l/CsСCϡC:t_9$/CП <0…  /_?$XAП <0…  /߾߿ <0… &Ϡ| 簠?~'p @},h „ 20 } P <0… 'p}?}>$XA .d0a>sH_>:tСCСC6ܗO~sСC Ϡ| СC:tС <0… ӗO߿? Hp_|$H_|G A /A $H@/A ? 4xaB 6tbā'p "Lp!Æ_> o ܗ'0>$XРA~;x˗?O@ <0… :|1"|I(QĂ+ȏ} o߿| /_? ̗| /?~_>}O_}/_|߾|S/_y8P ,8_>$XA .dC O@ H*\P | ?8P`|/~O`|O? _>~ ߾|/~_|7p͛7/O8p@8`A&TaC!FDП <0…  O$/|#؏߾|˗O'0~O`>_~ 7}/A7o>} $H| H*\ȰÇ#JHE!ԗϟ@G0>~W0>~o| '0@ __}O>~ H|ǯ`  <0… :|1ĉ+ZTȏ~ ܗ߿|O`|O`'0|󗯟@~o` /?@.^xŋ/^˧|ۧO`|ۧO`'0|o|o| ̗>wŋ/^xE]xa>~8_/^xŋ/*ŋ 僘Ł]xŋ/^xQ!?}/^l/b>wŋ/^xE)P?} H*\Ȱa|~ ? 4xaB @'p "Lp!ÆB\/_Ĉ#Ft/_DE/_>$XA .dP`|8`A&TaC!*/bĈ#6ܷ/B"ӧ_Ĉ#FTO#F1bĄE1bD_E1bDE1bĈ#&ܗO>$XA .dؐ߾|'p w@~!B"D!BO@ DPB >QD-R/>/^xП/^xŋ/R/>/^xП/^xŋ/R/b>/^x_/^xŋ/R/> ˧|70_o`|Xџ/^xŋ/R/> / ̷O`|/? ̷"E.^xŋ/^H_!ܗ@/?~/@}7p_~'0_>]Oŋ/^xŋ) ?  ̗`|O?'P O o@~ H <0… :|1ĉ+Z/@},8 ̗`| o_> ̷O`|#/|  ̗o|˧O`|Hџ/^xŋ/R/>'p_~ _> ̗O`|'p_~/E]xŋ/^x|Ko_| 7P_}'P o@}7P?$XAO@ DPB >QD-R/b>/^x_/^xŋ/R/>/^xП/^xŋ/R/>/^xП/^xŋ/R/>~/^x/^xŋ/R/"}/^xp/^xŋ/R/|/^x0/^xŋ/RO>}.^xq?}.^xŋ/^X_O@ DPB ˷? 4xaB 6tbD)Vh_'P`?$XA .d@~ <0… :|1ĉ+Z_>1bĈ#F1bĈ#ƄaĈ#F1bĈ#F1&#F1bĈ#F1bĈ1|1bĈ#F1bĈ#F È#F1bĈ#F1bL/F1bĈ#F1bĈcB0bĈ#F1bĈ#Fˇ#F1bĈ#F1bĘ_>1bĈ#F1bĈ#ƄaĈ#F1bĈ#F1&#F1bĈ#F1bĈ1|1bĈ#F1bĈ#F O@ DPB >QD-^ĘQƇU ?'p "Lp!C(?$XA .dC%NXMܗ/@},h „ 2l(0_} H*\ȰÇ#JHEIԧŋ/Oŋ/^xŋ+_/^<ŋ/^xŋ)߾/^<ŋ/^xŋ)Q/^ /?$/> ̗`|'0_>'p " ԧ"E.^xŋ/^H_>$XP> (0_>ǯ?}/ ̗`|(,h ƒ'p "Lp!ÆB(q"Ŋ)P #/߿|ۗ_|70_> ̗O`} H <0… :|1ĉ+Z/_D}O|o|'П>/?/|.Rŋ/^xŋ勨~ ܗ߿|_|'0_/_? ̗"E.^xŋ/^H_%/_>}/>}˗o? >o@ H <0… :|1ĉ+Z/_|.^x|/^xŋ/^/_D}.^x?}/^xŋ/^/_D}.^x?}/^xŋ/^/_D}.^x ?}/^xŋ/^/_D~.^x>~/^xŋ/^/_D.^x`>/^xŋ/^/D}]x@]xŋ/^x|P <0…  ̗o,h 2l!Ĉ'RhѢ|O~ H*\_? ? 4xaB 6tbD)Vx|1bĈ#F1bĈ#F È#F1bĈ#F1bL/F1bĈ#F1bĈcB0bĈ#F1bĈ#Fˇ#F1bĈ#F1bĘ_>1bĈ#F1bĈ#ƄaĈ#F1bĈ#F1&#F1bĈ#F1bĈ1|1bĈ#F1bĈ#F È#F1bĈ#F1bL/F1bĈ#F1bĈcB0bĈ#F1bĈ#F <0… :|1ĉ+Z1ƍ؏?  <0… O@~'p "Lp!ÆB(q"Ŋ/'_|8`A&TaC <0… :|1ĉ+Z/|.^x|/^xŋ/^/_~.^x`~/^xŋ/^/_D~.^x ?~/^xŋ/^/_D}.^x ?}/^xŋ/^/_D}.^x?}/^xŋ/^/_D}.^x?}/^xŋ/^/_|˗O?˷o |O 'P ?$XAO@ DPB >QD-R/> / ̷O`|/_|.^ŋ/^xŋ勨} o߿|/_? ԧO`|/_|.^ŋ/^xŋ勨|/~O |o`>}@ ? 4xA8`A&TaC!F8bEO@O@̗_'0_> ̗o`|#/_>$XA  <0… :|1ĉ+Z/@~,(P?/?o`} ̗O`|/|˗? 4xa'p "Lp!ÆB(q"Ŋ)Q?/_/|'0_ ̗`|/wŋ/^xEE/}'0@}0@? @8`A? 4xaB 6tbD)VH_]xA.^xŋ/^H_]xA.^xŋ/^H_]xA.^xŋ/^H_]x~.^xŋ/^H_]xA~.^xŋ/^H_]x}.^xŋ/^H_>]xbA.^xŋ/^X_>wŋŋ/^xŋO|,h „ 2lHP?$XA .dC%NXѢE.'P ,h „ 2П@ H*\ȰÇ#JHŋÈ#F1bĈ#F1bL/F1bĈ#F1bĈcB0bĈ#F1bĈ#Fˇ#F1bĈ#F1bĘ_>1bĈ#F1bĈ#ƄaĈ#F1bĈ#F1&#F1bĈ#F1bĈ1|1bĈ#F1bĈ#F È#F1bĈ#F1bL/F1bĈ#F1bĈcB0bĈ#F1bĈ#Fˇ#F1bĈ#F1bĘ_>$XA .dC%NXE5n|/E H*\ȰÇ#& <0… :|1ĉ囨/} H*\ȰÇ#ܧ/>$XA .dC%N/|(RH"E Ǐ"E)RH"EE"E)RHѠ|)RH"E)/"?~)RH"EH"E)RH|H"E)R<E)RH"E勨E)RHA}(RH"E)R,/_D}(RH"EG"E)RHbA"_G|?(FEQH"E)RX_!|H0?cO | HKh0_„  <0… :|1ĉ勨_|'_|o`W_|˗@~O`'P_|˗}_>'_|˗|_~˗O_>/_|G"E)RHbA H A 8p_ _>o_>/ G '0A~۷O`>߾|/~o|O?|G_߿|o_>_} ? 4xaB 6tbD!'p OO`?~ '0| ̗o`| G_?~ '0|O`>˧O`|O?O`?~ '}#80|O`|/_>}$(?$XA .dC%NP?o| 70_>}70_+o| G0_ϟ|˗/ ۗ/߾| /|_ӧ|O_|ۗ/߾UXbŊ+VP?_| | o|'0_|O`>O|>~ }ӗ߿|/|_˗ϟ|O?/? 4xaB 6tbD):Oa|˷O|˷O?}+/_>}+/>} Wp_| ԗ_>/_>~/_>}˗o@}O|wP_>O_|ӗ/?/!?~+VXbŊ+:bŊ+VT| WbŊ+VX|*VXbńW ?~+VXbŊ+:bŊ+VL询}*VXbŊ+Vt_Ŋ+VX>~+VXbŊ+:,h „ 2l!Ĉ(QD%J(QDI(QD%J,ϟD%J(QD%&'QD%J( |%J(QD%Jp_~%J(QDOD%J(QDO H*\ȰÇ'p@}8`A&TaC!F8bE$X5A .dC'p "Lp!ÆB(q"Ŋ/b̨q#ǎ;;PK5gVGQGPK|%AOEBPS/img/adfns039.gif gGIF87aX?**?***UU?UUU????**?*******?*******U*U?*U*U*U**?*****?*****?*****?***UU?UUUU*U*?U*U*U*UUUU?UUUUUUUU?UUUUU?UUUUU?UUUUU?UUU?**?***UU?UUU?????**?***UU?UUU?????**?***UU?UUU?ժժ?ժժժ???**?***UU?UUU?????***UUU,X H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӈ8`A&TaC!F(,h „ 2l!Ĉ'Rh"ƌ7r/@,h „ 2l!Ĉ! <0… :|1ĉ+Z1ƍױcǎ;6ǯcǎ;vرcǎ;v/_ǎ;vP;vرcǎ;vq|;vرcC}رcǎ;vرcǎױcǎ;6ԗ/߾;vرcǎ;v|;vرcC}رcǎ;vرcǎױcǎ;6ԗ/߾;vرcǎ;v|;vرcC}k/|'0_ ܗ/>/@ ߿˧? o_|4hP?3(,h „ 2l!Ĉ'Rh?0bĈ#F˷a|/|'0_>/_> ̗a|/>Qg1F1bĈ#Fˇ#F1b/_} /߿|˗_|'_}/|/o_> ܗ@˗o|_|˗߿|/_?/_?}È#F1bĈB0bĈ#F˷?~̗O`|`|߿ O߿߾|/?O ߾}/߿|/|_>_8`A&TaC!F8bEˇ#F1b/_}ۧ_? ̗_|/_̗_|/|O|/| 70|˧?؏߾| 70߿|1bĈ#F1bl/F1bĈq|Oa|/?~˗|ۗO߿|'0_>˧O`>}o>}O?} '0|˗o߿| ̷_>_>1bĈ#F1j 8P |'p|/_> /_| 0@o߿ ߾oOཱྀ? ߿}O@ Dh?}ۗ|/_/߿~/_O|_>~߿~#/>>} o|o` '0|'p "Lp!ÆB(q"Ŋ/a>SOa>'0@o@~o ?} /?~ ԗ߿|/_?_> ~|5/_}/߿|ۗ/>}ӗ/߾ ߿/?@/_}˗O?/_>}_'0|ӗ,h „ 2l!Ĉ'RhѢ| /@70߾|G߾|-7>O`>ӧo| '0߿|/_>O`|O`#~篠|]t|.^xŋ/^xŅ!'0?~o`|W0|ۧ0| G0@~/|O`ۧo| G0|O`˷ӷwE]xE]tE]tE1| H_>˗o`>~7_,`>G0_| O`~˗O_/_|˗`>W0_~W`ۧo_ ˗o,h „  <0… :|1ĉ+Z1B#o?~ o`O@ 80@>O@}_~ o|O`>߿| o~8pO߾~8p|'p "Lp!ÆB(q"Ŋ/b̨q#-G0@~ ̧O ?} ~| g_>o |'߿|'0@7_~菟>}Yԗ/>:v$a:vرcǎ;v/_|/_>}˗/_>} O@?˗/_}'p}o} O_>o o}|ӷO> Hp?~W`˗o_O@ DP| [H0,h „ 2l!Ĉ'RhE0#FO? ˗o_}O|Ǐ߾|_>~#o_~/_~| '0@}?_|˧|/_?1bĈ#F1N#F1b؏>}aT/_} _`>~?~`>?}_?~/| >~߾'0|?? 4xaB 6tbD)Vx|1bĈb}ѡ|5'0| W0߿|߾|'0|> '0| /߿| 7_>'A0bĈ#F1b8_>1b(_}Q|5'0| W0߿| ǯ>~ O`O>O`>_#O?~0@O?~8`A&TaC!F8bEˇ#FO߾~/˗o_| O߾O߾ ԗ?} O`O |_>'0/?}|O ?/F1bĈ#F'ˇ#FO~1ԗ/߾?}+?}O? O`ӧ|ӗO>'0|?}/>}O_|˗O>~ H*\ȰÇ#JHŋÈ#Fӧ_?˗o_C~0b/|1bĈ#F1bĈ|1bĈQ`?~B}s#F#F1bĈ#F1"#F-O?1>ԗ/>1bĈ#F1bĈ#ƋaFӷ?P_|0bĈ#F1bĈ#F/ˇ?}0b\o}a<ȏFۇ1| a/|1bĈ#F1b(_>Aӷ,h B~"DР|!D0B!B Cp`>8`A&TaC!F8bE˗P_|ۗ/_|˗/_O_|-篠|/?~˗/@}o_|k>}aȏ@~#/_}˗O_D}cϟ|O`/_>ӷ/?_~ |ۗ/?ϟ|ӗO?}˗@~|/_>1bĈ#F1b/|_?~/| ܷO> O}O`}/_>oǯ,h BG>'_˷/~O|'0?~+_>~'0_'A~˷o?/߿|'0|O`'0| <0… :|1ĉ+Z_>/_70|)`_'0} ̗/A#F~#`|˷a O`@~'0_| /?/| O> G_>O`È#F1bĈE_/_} 70B} O`/_|_>O ?}È ?~ /|,/>$8P߿|/| `>_@|_߿|/߿_맏|߿|߿~O@ DPB >QD-^L/|o`O} 3O}/?}#ƅ'0?~۷O@~˷!?}_>/?}G0߿|'0|@}ӷ| | / /| 70F1bĈ#F+˗P_|0@| WP_| ԗ/?}/_|˗o> O@ DP!A~O ߾8`AgР|˗O>~ۗOǯ`+߿|/_>~˗?}O`O`>~/>}O`> /_~ H*\ȰÇ#JHŋ È>1bȏFۇ#F1bĈ#F1bx_>#Fat/_}1bĈ#F1bĈ#FÈ>1bȏFۇ#F1bĈ#F1bx_>#Fat/_}1bĈ#F1bĈ#F3П <~ &L0a„ ܷ/a„8p|$H A$H~8`A&TaC!F8bE1fԈ_>8`A̗/a„ &L0aB%LP ԗ/> $H0A ? 4xaB 6tbD)VxcFhQ7>oB}k/_>O`o_~o_>~۸qƍ7nܸqƍhQ7>oB}co?o| '0?'0ӷqƍ7nܸqƍ˷Ѣ>7n|ȏƅ0@_>O`>oƍ7nܸqƍ7oE}6n ˷a>_>O`>0,h „ 2l!Ĉ'Rh"ƌ˷Ѣ>7n|ȏƅP>/|O`>O`6nܸqƍ7nܸqA#/{/B˷|+؏`| ԗA}-ԗ/>ϟ| o_>}O`>دƍ7nܸqƍ7nԗ/>"Dx,h „ 2l!Ĉ'Rh"ƌ0|mϟA`>˷/?~o|߿}o ӧ?o  ߾ۧ/ Ǐ|O`#Hp|'p "Lp!ÆB(q"Ŋ/b̨q#170|3/_| /| 70| /? o_};O`'0B~ 3o`>o|uرcǎ;vرcǎco>u /_>o`>O`>70?;O`>~O`>g}ϟ?} ˷cǎ;vرcǎ;v/}_|:W0_>ۗ/?}_>ǯ_>~/_|˗O߿_|/>}˗߿߾|뗏߿|8?~˗A~o|$8P_|8`A&TaC!F8bE1fԸ?:Rcǎ P_|:vرcǎ;vرcu_ǎ1|uرcǎ;vرcǎHQ;&ǯcB}رcǎ;vرcǎ˧_|篣>/_GG_|珠|˗o_ǎ;vرcǎ;v_>߿}+O@1̷0_ǎ_>'poB}رcǎ;vرcǎ_>$80|,X`AO`|+(_ `A+X` $ȏ_|O`>` <0… :|1ĉ+Z1ƍǐ_?ϟ|ϟ>}˧O|/_>~'_}/_>}:"ǯ`>'0| ̷P_|:vرcǎ;vرc-o`>:g0?~ۗo_O |?O`#H A $(?_>O`>˗o,h „ 2l!Ĉ'Rh"ƌ7r/B󧯣> ̗ϟ|w0||W0A} ϟӷP_|:vرcǎ;vرc)̗/>Q/߾|/_|_>}˷?~/_|/1!?~˗ϟ@/_~o|uرcǎ;vرcǎHQuLȏ_DŽױcǎ;vرcGu? <0!A}*TPa|)T!?~ *D/_} H*\ȰÇ#JHŋ3j|)ױcDŽuL/_};vرcǎ;v?:Rcǎ P_|:vرcǎ;vرc-̗@}u#|˗/@}ԗB}رcǎ;vرcǎ˧П|>˗O:v<ȏ~'}߿} ˷cǎ;vرcǎ;v/_| Q?˧_+_~uLȏ_A8?O`>˗o,h 2l!Ĉ'Rh"ƌ7r/B '0_G} O|/>}˗O_'П>}ӗO@~ o|˗O+`?O`ױcǎ;vرcǎ;a>ϟ|O`+O`ۗO`|70| q ?~70| ̷P_|:vرcǎ;vرc5>u``> /?_| H+`7p?ԗ/>$XA .dC%NXE5n_>'_|:W_|˧O'_|_>}ӧ?}/_|/q ?~˗A~o|-ԗ/߾;vرcǎ;v|)ױc|1|uرcǎ;vرcǎHQׯ@~:&ԗ/߾;vرcǎ;v|)ױcDŽuL/_};vرcǎ;vر?7P|8`A!B"D!B / ԗA}!/_} H*\ȰÇ#JHŋ3j| 'p:W_>/_|:bo ?O>~ ߾ױcǎ;vرcǎ;o!> w~3؏K?~)`>O`[/_};vرcǎ;vر?O`̗/_ӗ/߾|o_ӗ`/_߾|O_>o_|/_?ӧ? G~O`#Hp|'p "Lp!ÆB(q"Ŋ/b̨q#)ԗ/_>ϟ| '0_/| w0_|˗O?~o`O`˧|)Ǐ`?˗O`>o|uرcǎ;vرcǎkO>u`>/߿|'0|>}_>~O`GP?Sȏ@ ϟӷP_|:vرcǎ;vc1̗O|O@ _A~߾|ۗo_>}_>+/˗?}O_|˧O|/_}˗?}C(W0_>7_|ԗ/>$XA .dC%NXE5n_رcB~:&ԗ/߾;vرcǎ;v|)ױcDŽuL/_};vرcǎ;vر?:Rcǎ P_|:vرcǎ;vرcu_ǎ1|uرcǎ;vرcǎHQ;&ǯcB}رcǎ;vرcǎב>;vLȏ_DŽױcǎ;vرcǎ;#E}:vؑ|uرcǎ;vرcǎ_>$XA .dC%Bԗ/߾'N8qĉ'N8qĉ'N_'N8qĉ˷oĉ'N8qĉ'N8qĉ'"oĉ'N8q"D}8qĉ'N8qĉ'N8qĉ8qĉ'NQ_|&N8qĉ'N8qĉ'N8q"B&N8qĉ'Bԗ/߾'N8qĉ'N8qĉ'N_'N8qĉ˷oĉ'N8qĉ'N8qĉ'"oĉ'N8q"D}8qĉ&h&h&h&h|,h „ 2l!Ĉ' <0… :|1ĉ+Z1ƍ'P |,h „ 2l!ĈO@ DPB >QD-^ĘQF8`A&TaC!Fp,h „ 2l!Ĉ'Rh"ƌ7r#Ȑ"G,i$ʔ*Wl%̘2gҬi&Μ-өSN:uԩ3b|ԩSN:u_}өSN:uԩa|˗ON:uԩS΅#/_>:uԩSN:˷Ϡ|tԩSN:uөSN:uԩ`|˗ON:uԩS΁c/_>:uԩSN:˷ϡ|tԩSN:uoDԩSN:u/>өSN:餓N:P|8`O@ DPB >QD-^ĘQF=~a|,˗OH"E)RH"E)rc|0˗OH"E)RH"E)2c|4˗OH"E)RH"E)b|8˗OH"E)RH"E)b|<˗OH"E)RH"E)rb|@˗OH"E)RH"E)2b|D˗OH"E)RH"E)a|D /H"E)RH"E)Rd~$/_>"E)RH"E)Rȅ8 A H ?$8P,h 2l!Ĉ'Rh"ƌ7r#ȍ8@ H ?$H,h „ 2l!Ĉ'Rh"ƌ7r#H8|8`A~'p A H*\ȰÇ#JHŋ3jȱǏ C!|D)RH"E)RH"E !|D)RH"E)RH"E !|D)RH"E)RH"E !|D)RH"E)RH"E !|D)RH"E)RH"E !|D)RH"E)RH"E !|D)RH"E)RH"E !|D)RH"E)RH"E !|D)RH"E)RH"E !?'p "Lp!ÆB(q"Ŋ/b̨q#ǎ? 91)RH"E)RH"E)0)RH"E)RH"E)0)RH"E)RH"E)0)RH"E)RH"E)0)RH"E)RH"E)0)RH"E)RH"E)0)RH"E)RH"E)0)RH"E)RH"E)0CO`~/_>~(߿(,h „ 2l!Ĉ'Rh"ƌ7r#H=|_~'0|/dȐ!C 2dȐ!C 2a>0| /~3`B 2dȐ!C 2dȐ!_> /| o`!C 2dȐ!C 2dȄ=|_>O |7P`O@ DPB >QD-^ĘQF=~0CO`>o|7B 2dȐ!C 2dȐ!_> '_| O`>70_Ȑ!C 2dȐ!C 2d|a>_| O@~_O@ DPB >QD-^ĘQF=~0 2dȐ!C 2dȐ!C 91 2dȐ!C 2dȐ!C 91CO`~O@,h „ 2l!Ĉ'Rh"ƌ7r#Ȋ=|o`> _Ȑ!C 2dȐ!C 2$|a>o`/dȐ!C 2dȐ!C b>0|o` 2dȐ!C 2dȐ!CC~!'0߿~o`>B 2dȐ!C 2dȐ!)_> 'P_| ׏?}!C 2dȐ!C 2dȐ{/?/_O?}B 2dH!RH!RH!D@'p +80|} <0… :|1ĉ+Z1ƍ;zb>2dȐ!C 2dȐ!C rb>2dȐ!C 2dȐ!C rb>2dȐ!C 2dȐ!C rb>2dȐ!C 2dȐ!C rb>2dȐ!C 2dȐ!C rb>П <0… 'p "Lp!ÆB(q"Ŋ/b̨q#ǃ8`A&T!A H ?$XA .d,h „ 2l!Ĉ'Rh"ƌ7r4O@,h „ 2 O@ DPBO@ DPB >QD-^ĘQFرcǎױcǎ;vرcǎ;䧯cǎ;vT_ǎ;vرcǎ;vh;vQ>~;vرcǎ;vرA~:vرcGuرcǎ;vرcǎرcǎױcǎ;vرcǎ;䧯cǎ;vT/_~;vرcǎ;vر#A~:vرcGױcǎ;vرcǎ;䧯cǎ;vT/_~;vرcǎ;vر#A~:vرcG? 4xaB 6tbD)VxcF9䧯cǎ;vT/_~;vرcǎ;vر#A~:vرcGP|?'p w0B CX0|O`O`>'P <0!| *TPB *TPB *4OB *TPB *TPBO|)TPa|)'0_~G0| )TP| SPB *TPB *T ?} ˗ϟ@})TPB *TP˗/A} ԗB}Sh0|ϟ|Ǐ߿}߿|7p?/_> /_?}/珠|㗯@~O|?7_>~߾~ /_>O}/_>@}/>$XA .dC%>Oa7>}o>}˗O_?~˗|0O_7P,h O`'pӗ/__>o|߾˷>_}'0?|} _ G0|_>3_>K ߾ |o|?~?O`>~  <0… :|1D)W0|#/} '_o |G0|7ĉ70| G@~˗o'p` '0}'0|/߿|߿|o?@~7p| /?7_>/| _ O@O߿|_߿__'P| '0| H*\ȰÇ#JO}/|#_>'0@~Ǐ`| `/ĉ70| G?o80_} O`O?~O`| >~`>/? '0|(߿__߿߿?|_@O`>/|/߿| '0,h „ 2l!Ĉ!_1?~ ܗ/_>'0~`>_|7q|'0|)ԗ/_?O`>~'P߾ ԗ?} O`>}'0߿|/?}o`>~@~/_>O>}'_>>~/? O߾ '0?~'0|/| 7qĉ'N8"?} ?8`AO ߿| 70| '_}C!BGp?OBa>_|'П| |ϟ| _|ϟ|/_~O`Ǐ?}'0@~o_|O_| O@~߿| ߿|ϟ>'߿~?~߿|/8`A&TaC!Fؐ>'_|&Bԗ?}'0_>~ /0@ ߿}˷| ܷ? 4x}7A/_~ ˗_„ &L0a„ &L` /a„ " <0… :|1ĆM8qĉ'Nl/_~'N8qĉ7q~'N8qĉ7qĉ'N8|M8qĉ'N8qĉ'N8qąM8qĉ'Nl/_~'N8qĉ'N8qĉ'N8q!?}'N8qĉ˗|'̗_~#@ _o@ /߿/߿? o8pO@ D`> ˧PB *TPB *DOB *TPB *TPBO!| *T80_ ԗo |O|a>70߿|70_> S0B ̧PBSPB *TPB "OB *TPB *TPB말`} /?_|?}/_>O@}o`>o|'0?~G?O_|/_Ǐ?~_|ۗ_߿˷?}/|o_>~߿ۗ˗@}/_>/|˗ϟ@}'p "Lp!ÆBDO_Ĉ#F1bĈa>~O ϟ>_>/@~||_>#O`˗b|7П>?~O|/߿~G0_|} '0+_>~O`>o߾ '0_'|"F1bĈ1bĈ#F1C}so`|o/߿߿?> ߿|@߿_?}O@O> '0߿|ȏ| '0߿}_> O>O`>#/߿|/|70| /?O@ DPB >#F1bĈ˗|8_맏|/߿o@ /?}'`>?~_>O@O~#/A/O`'P~/߿_> 70߿|(߿~(߿|/O`/|70| `> <0… :|Q!?}#F1bĈ#:ԗ/_?O?'0_>_| O?>~o>~ O`> ̇0| /_|ӗ/a>~ |'P?_O | ߾'?~'`#?}O`>'"F1bĈ1bĈ#F1C}s/_}˗?؏_|/>}˗?O` _>o? ߿|߿|_?}˗/_ӧ߿| /_>~˧O?70?˷O`>|/_>~ '0? |/|˗|?}? 4xaB 6tB~"F1bĈ#Ft/_~#F1bĈ# `#F1bĈ#F#F1bĈ˗_Ĉ#F1bĈg0_Ĉ#F1bĈ#FDO_Ĉ#F1bĈ/bĈ#F1bĈ#F1bĈ#FQ ?}#F1bĈ#:ԗ/_#F1bĈ#F1bĈ#F1bDE1bĈ#FP_|"F4/"A~"w0_Ĉx0_| E4`#F1bĈ#BP,h „ 2l!Ĉ˗D(0D&&̷0߿| 惘O`>+oĉ'N8qĆM8qĉ'Nl/_~o_|/ԗO߿|/_>_|˧߾| Ǐ|!/>ӗ/?'p_|˧ | /߿}߿|_|/_A~Ǐ|㗯@~#/'0߾|/_>'N8qĉ'"? 4xaB 6tbD a>~o?~O߿| _ '0 '0߿}'0?~C_>o߾ GП>o߾_}O 㗯`W0|/@~O`>۷_>~8qĉ'N8!?}'N8qĉ˗| ߾~w0|O ?'| '0߿|C__8|˗o`ۗ_ /|O` '0߿|(0 70߿|  <0… :|1ĉH"E)RHP_|o`>Ǐ| `>@߿o@ /߿|o_>~ /|짏|˗o`>Ǐ? ߿(0߿| O`>'P>~߿ ? 4xaB 6tbDG"E)RH|9'P߾ ԗ?}O` _> _O` ` o`>o?#?O߾ ԗ?}'?}W0߿|'0||/|/>~H"E)R>)RH"Ea>O ?}`>~ ϟ| 'П| ϟ| ?}/˗/_ ϟ| o |/߿|O ?}O_|ӗO'0@'0?7P`>~ӧ?}ϟ|  <0… :|1ĉH"E)RHP_|(.G"B~)RH"E)RH"E H"E)RHP_|(.G"~)RH"E)RH"E H"E)RHP_|(RH"E)RH"E)RHQ!?})RH"E ˗E)RH"E)RH"E)*䧏"E)RH"A}H"E)RH"E)RH"EQH"E)R$/_~)RH"E)RH"E)R>)RHEQD@ <0… :|1ĉ+Z1ƍ ױcǎ;*ԗ/_;vرcǎ;vؑ ?};vرB}رcǎ;vرcǎ ױcǎ;*ԗ/_;vرcǎ;vؑ ?};vرB}رcǎ;vرcǎ ױcǎ;*ԗ/_;vرcǎ;vؑ ?};vرB}رcǎ;vرcǎ ױcǎ;*ԗ/_(߿| ? 4xP`> ̇|!D!B| <0… :|1ĉ H"E)RHP_| #"E80_|)RO`(RH"E)ROE)RH"E1|ϟ|Ǐ߿}߿|G?}#/_~ o_|//W0߾|˗ϟ@~/_~ ϟ| /WP߾|O`>'0?~O`>˗_A~"E)RH"D~/_ <0… :|_|GP_>P_| #߿|>۷}o?}߾ '0|?~|_۷_>_ /|o?~#O|_>o`?~O`>|̗?~ B"D!B ?} 7p B"Do?~o}#>7p@ o| ̧o 70?/@7p@/|/߿|/|/?o?}'(0| '0|@O` 7P`>}'p "Lp!ÆB(q"B~G0|)RH|70| ̧P_| #O`>ۗ| '0~+_} 짏| ̗o`>Ǐ?_> 7`>O~/||_?}O|'0| _O`>0@/߿~O@ DPB >QD)/_> G"E)6ׯ`O`>1| o`>_>㧯_|?O߾ ԗ?}'}+?~ϟ}|'`>/|䧏߿|'0|@~70E)RH"E)_| G"E)2o| '0|)ԗ/_?_˗/_?}'p`>7_|O`>' /_>~ӧo@}O_|ӗ/_ӧ߾|/_~˗ӧ/>} 7P`>˧O˧O?7P`> <0… :|1ĉS|"E)R\a'p?˗E)RH"E)RH"E)*o|/_?)RHqa|̗o| /_?G"E)RH"E)RH"E H"E)RHP_|(RH"E)RH"E)RHQ!?})RH"E ˗E)RH"E)RH"E)*䧏"E)RH"A}HQaE0_| o`> ߿`>'p ,X| ,X`Ǐ,h „ 2l!Ĉ#7qĉ'N8|MTo|='0|/|70߾ 7_ M0ĉ'N8qDM8qĉ'Nl/_~O}ӗO?~o_>~˧߾| Ǐ@}/_?W0@O`O@}K/_>O`>o_~o_>~O|_>~/_>|  ?O_>}? 4xaB 6tbD 8qĉ'NP_| '|| |_>'0_'_|/o?Ǐ_>_}'0| _~ _ /߿}_>>~|O`'N8qĉ 8qĉ'NP_| '| /?@~+_30߿/'P`>_> ߿/߿߿|/߿_'p|70?/߿|߾~ '0AGp| H*\ȰÇ#JLOĉ'N8qĆ`>_(߿| ߿| _80|O@߿~(߿|/߿8P`>O`>O`>#80| '0~0@@O#O?~8`A&TaC!F'N8qĉ >~O`>''}+}`>@~߿|_>~ ̇P>/|O`>O`o?~?~O ?%/|7qĉ'N81!?}'N8qĉ˗D'0|/_>~ /_>~ӧ`O`>߿|˗˗/?o_>'P '?}߿}/߿_@O_|_>o`>˗| _˧O?$XA .dC%"oĉ'N8qbC}8qĉ'NloD&N8qĉ'N  <`%/|'0_| ,X| $`+X|+X | ,O`>$XA .dC%Nd|,h „ 2l!ĈO@'0߿}70_A_>+@~/_>?}/>}/_?ۗ?}ϟ|/_?˗ϟ}#/||o_>~߾|ܗ/?GP_>}O |'p "Lp!ÆB(qbA$XA .dC%> ߿|o`_`/߾}߿|o|O|_> ?~'0|߾}o?~'| _ o?~>W0|/@~ H*\ȰÇ#JHŋ3j(1߿~`O߿|'0_| o`>o?+/| '0_|_>70| /?O`>'_O`> O` Ǒ#G9rȑ#G9'P_|#O| /߿| W0| '0|O`Ǐ_>O`ۗ~'0߿~o`0@/߿o߿|_'P`>0@8`A&TaC!F8bE1fԸ1b>/?`>@~+}_>o`>?O`> o`O`>~ӷ|/?}/| O?/A}'0| '0?}Ǒ#G9rȑ#G97_>˧| ?}'0_|˗/_>}˗?}O`O`>~˗O>~߿|߿˗/_˗O>~˗/_߿ӧ?}/߿_ϟ|?}/߿(0O_|8`A&TaC!F8bE1fԸcGA9dI'QTeK/aƔ9fM7qԹgO?:hQG&UZ1_| o`> o |(,` ` ,X0_ ,80| ,80,h „ 2l!Ĉ'Rh"ƌ7rTo~o |7P_>(O`-'0|;vرcǎ;vر|?o`>}/!|ۗ/?O}O}ԗO߿|ϟ|Ǐ߾|_>~#ϟ|Է/?O_>}O`>˗?}o_>~:vرcǎ;vرcG_> >~ ?~/߾}_>/_?/A/|㷏|o?~Oo|o?#O| ̷o?/߾'߾}O@ DPB >QD-^ĘQF '0߿~߿~'P|__߿|O@O`>}_o_˧o? /|/|}_>~? 4xaB 6tbD)VxcF9*7П>O |>/ G0|'0| '0| O`>ۗO`>~G_G0| '0~'0|_?}OO@ DPB >QD-^ĘQF O`>/@}OCo|O`'0| '0| '0 o?~`>_>_>ӷ|O_ o?:vرcǎ;vرcGo`o`?}O`> ߿ O~/>}/߿_'P`>~ '0|(0?' _| O`O`>~˧O/_~/@O_>}O@ DPB >QD-^ĘQF=bǏ?~Ǐ?~G8`A&TaC!F8bE1fԸcGA9dI'QTeK/aƔ9fM7qԹgO?:hQG;;PK:Ng gPK|%AOEBPS/img/adfns056.gifGIF87aJH?**?***UU?UUU????**?*******?*******U*U?*U*U*U**?*****?*****?*****?***UU?UUUU*U*?U*U*U*UUUU?UUUUUUUU?UUUUU?UUUUU?UUUUU?UUU?**?***UU?UUU?????**?***UU?UUU?????**?***UU?UUU?ժժ?ժժժ???**?***UU?UUU?????***UUU,JH H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCM)e>ԨQ7o|1P <0… 8~ H*\> 4xaB O@8`A&TP!?$8@},h „ *O| o_~/_~ӷ/ӧO߾|ӗ/_|߿}o_>~˗?~/_>˷|'p ԧ ? 4xaB  <0…O@O@ DPB'p 'p "LpA}8@}8`A&T| /߿}_~O`/@ϟ>/߿|O|_>o|?~/߾}?}  "D!BCH_>"D!BCX0?"D!BCXP?"D!B+߿~O`>/|W_o_> '0|'0@o`>~!D?}"D!BaA~"D!B !A"D!B a|"D!BaA}"D!B |__?}_'P`Ǐ_o_>~/@70| o_>~70|(,h A}"D!BaA~"D!B !A"D!B a|"D!BaA}"D!B`|O`>_`_o`|˧o| ̧o?o O߾ |!B!D!B"/‚!D!B"B!D!B"‚!D!B"O‚!D!B"`߿/߿_|/߿(0|˧O|/_~ӧo|_>'0|˗?}/_~˷ϟ|'p !B"D?!B"D|!B"D@}!B"D?~!B"D?"D!B"D|"D>~"D!Bˇ ?}"D!B󇐠|"D!Bㇰ`>"D!BӇ>~"D!Bˇ!B"D!BC!BC!B"D_>C!B"D(0?C!B"D(P? <0…cO? 2d| 2dȐ!C 2dHP? 2d| ǐ!C  珡| 2dȐ@}!C 2O1dȐ!C3_~`>o ?$XР~ ;xP`;x| w}#`/@?}/_>߿|ۗӷ/??}/_@/'?}8p <0…c> 2dP`> cȐ!C _> 2dȐ?~Ǐ!C 2/_A}|ܧ?o߾ >}O|/@~O`˗?~߿| W0?O|_>c(0_>O|o@~ H˗ ?} ˗O> ߿}'p  p`> ˇ|O@~7,h |A$/_(߿O@ /߾;hP7P|߾8`A| o?}g?}/| O /? '0߿|`>O o|'0BCh0_~70_>/?"$/‚!$/_~+/_C~!B!$/_~+/_C~˧?}@~#/A}㗏B_}Ӈ>~˗| |"DH_| 8_> O?}GП#o`>'P_ ߿/߿ۗ߿/߿߿|/_?}/߿|O@ gР|/|7_'_|O}3h| Ϡ|/_|_|˗o|/_>~ӗ`| Ϡ|/_|_|˗o|/_>~/4/?˷`|ȯ?~/_}˧O| | Ϡ,O_}+/@~O | />W_>~ `>䧏߿|߿_|O߾?~߿/?}_߿/߿˧߿_/_'p 3hP`|˗o`|˷|/߾_>~ g?~ ߾|+/_}㗏|o|۷/?g`> g|/_}|/˷_}_AA3H_>~̗|_>~㗏߿}/߾}Ǐ`}OA3H_>~̗|/?߾}/߾}߿?G>'0|˗/߾ϟ| ̧/>'_?ϟ|ӗo_>_|_>˗_|_>$Hp>~ />/˗_|˗o`˗O|$/AG@~O`|/_>~ /| ̗/> $H_>_~#(?}˗O /|0_oǏ A#HP |70_>ӗ/|/| ̗/>/_?G $(_/A˗/_>70߿|˧O`| ǯA $H | G  <0…c(0_>~70_> ̗/_> /|˗/>1OB @ o`| ̗o`>˗O_| o8p@(`>'?/_|70߿|˧/_|o8p 8|߿߿ϟ'p@~$O?'P O@˗o|O`|˗O@}o@ ? 4xa>!~O~ ˇ ?}O |+/ӗ߿|O|ӧ@} C80?C(_~/_A~O|/_>} O`|a|󗯟@~G_>ӧ/>}O>~/?!O‚!/_? 0|߾|/?'P_8p| H K0_O@ H_~0@8`A&,B/_/_}˗o}/|ӗ/?S8> ߿| ˗/߾˷߾|O_|˗| P| /_/_}˗o}/|ӗ/(?G $_/˷?}/_>}ӗ/?GP_|OAG?~O`|˗/>'p_|˧/_>Ǐ | | $H A$H`} A/| H"OB SP| ӧPB)Tx0?SPB*<)TPa| O@}*T`> GP_>}/_~?}/_~O_>}˗ϟ@};O@}o?7P_ϟ> *,B *̧P@OB S`> ˧PB)TxP?SP‚*|O,h „SPB O@~*TPBS(_> *TP!A}OB *T8>SPB ` /|W__| '0|/|0߿}70A)TPA}O@ DP /_+X? 4xaB H_> `A$XA 'p@}$/?'p "L?G $? 4xaB8P|O`?}O`>O?~#/߿|O`>O߿|Ǐ |/| <0?}0 <0a>} HP| 䧯 A~ H"#H| 0 <0aB} 8P?  H*O$H>~ O@ DPa> _>'0'0?~_ /߿|O`>>~ 7P_| O#_>$XA PB */)TPB P| *TPB)ϟB *Tp ?} PB */_| />} _|˗O>~_>~˗|W0?O ?~` 70@~ӧPBSPB O@~*TPBS(_> *TP!A}OB *T8>SPB OB PB *T>~ *TP? ? 4xaB _> 2dP>~ ǐ!C 㧏?}2dȐ!1dȐa~ 2dȐ!C1dȐ!Cc> 2dP`> cȐ!C _> 2dȐ?~Ǐ!C 2/C 2dȐ!C Ǐ!C 2/1dȐ!Cc/C 2d(P?cȐ!C >~ 2dȐ? cX0C_> 2dȐ@}2dȐ!1OC 2d(0?1dȐ! }8|8`A&T ?} ǐ!C | | 1!C ǐ!C ?~2dȐ!C1!C 21dȐ!CcO? 2d|ӷ/}/_>|/_>/|˗@}O}/'0|/_>߾| Է/_?}ۗ/?O_|Ǐ!C 2/1dȐ!Cc/C b!? ? 4xaB >~ 2dȐ? ߿|o߾ O?~?~߿|ȏ`>?~O| /߿}'0o|/߾}O`㷏aA} H*\? 0 <0… 'p A8`A&TP!?$8`> 4xaB O@'p "LpB /|/|/?o| /?'0߿|70 '0߿| '0|O`> '0|7pO@ P <80>$XР@$H? 4xaB 'p A H*\? O@ DP… Hp @,h „ .O| O߿| '0?}(߿~_@߿|o_>~>~߿|߿|O߿| _߿~O@ D/B <0… :|1ĉ+Z1ƍ '0?~ӷ|O?o`>@~_o`>_|ӷ| ̧o?O`>}'0߿~#B8䧏#G9rȑ#G9f/|O_|ϟ|/_~O`Ǐ`>/_|'_|/>}/_~ O_|O`>'p_>}O`?$XA!D ?} H*\ȰÇ#JHŋ3jȱǏ /!C 2dȐ!C 2dȋ}/dȐ!C 2dȐ!C | 2dȐ!C 2dHB670_?(,h „ w0_>ӗ/_| <0… :|1ĉ+Z1ƍ q$~˷`>w0_> 琟>9rȑ#G9rQ_>}/_~?}/_~O_>}˗ϟ@}O_|˗߿}O_}ཱྀ+#Ģϟ?}/߿}Ǒ#G9rȑ#G3?߿|o?}|˷o?/|o?o|/߿(P@7_>$XA ./|ۗo`w>$XA .dC%NXE5n\_'0?}_/| 7P> o`>'_|O|92`>~70@~!䧏#G9rȑ#G9f/~/|ǯ>~W0߿|O`>㗏`0@/߿_>$XA ./|o`CO,h „ 2l!Ĉ'Rh"ƌ7./|/| ̗?'0߿|_>o`>}ӷ|/?_ Ǒ#C/߿| ̷/qȑ#G9rȑ#nj/_>}|/>}˗߿|ӗ/_?/_|˗?}O_}ǯ o`>w0?}O |;OG9rȑ#G9rȑ#BO@O@ DPBkذ ?} H*\ȰÇ#JHŋ3jȱc|y8_>ѣG=zѣǎ/#?!o |(,h ƒO>  <_>? 4xaB 6tbD)VxcF_>~a}| W0DžqOG9rȑ#G9r̘o`~߾|ӧ/>O|;O@}o?ӗ/O_|ӗo?~o_>~?};_8.@~8rȑ#G9rȑc}o?o߿|/@~ `>~o`#?~_>0_߿˷o?~>>~O@ D8_ ? 4xaB 6tbD)VxcF/_|_o`>/?O`o`>~ '0_>~#B8䧏#G9rȑ#G9bo|_ ߿|?ϟ#/|_ _/߿ ߿8`A/a„O@ DPB >QD-^ĘQF O>/߿|ۧ@~#`o`#o`|/?'0}o` o`> 8>9rȑ#G9r䘑_|/_~_>}/_>~ _'0|O@o_>}˗/_O_|ϟ|/_~ o`?}q\/ǁqȑ#G9r#8!'p 4hРA 3hРA 4hРA3hРAO@ DPB >QD-^ĘQF (1G8>9rȑ#G9rȑ#G9N@~8rȑ#G9rȑcFǑ_|EǑ#ǁqOG9rȑ#G9r̘?i䗯`> Ǒ#ǁqOG9rȑ#G9r̘o`~߾|ӧ/>GP|˷O | ܗۗ?'P_>}O |qh_>ȑ#G9rȑ#G߿o߾>~? ߿}߾}_>~} _  <0… c ?} 2T/>$XA .dC%NXE!/_|_`O _>`>@~5jOcA~4R/_>~5jԨQF5jԘџ#o`'p? 7P` /߿/߿//߿ ? 4xaB ǐA~2d|O@ DPB >QD-^ĘQa?}ӷO`>}/' >}߿~O`O`>'?3˧ ?} H0G A $? 4xaB 6tbD)Vx"|O_|˗߿|O_| +/_}˗?/߿|/_~O`#Fx|,h „ ӷ? 4xP | H*\ȰÇ#JHŋ!xП?2f̘| ӗ?>~ HO@ DPB >QD-^1_ƃ/cƌ˗ ?}+˗/_>}X_3f̘1cƌ3f̘1cƌ˗ ?}+˗Oe,/_ƌ3f̘1cƌ3fLϡ> $? 4xaB *aÂ5l|װ!A6lذaÆ 6lذaÆ 6lذaÆ64| ;/_Æ 6l_>$X'p "L>~ "? 4xaB 6tbD)VxѢ|_|ۗ?_|˗ϟ|ۗ/?g0@~O|ˇ#F8`A O@ DP?}˗/_>}g_>$XA .dC%NXŋϟ>/߿|O|/߿|>g0@}̇0F1bĈq>~!`?}È#F1bĈ#ƅ+/߿|ӷ_A_ '0| |aĈ#FW0@~O_3/F1bĈ#F1./~/|ǯ>~W0߿|O`>G0|1bĈ#ƅ70@~?#(_>$XA .dC%NXŋ?/?}_>}'0| /|aĈ#FW0@~ w0A0bĈ#F1bĈqa>~˗O>~/_|˧O?/|O`> ȏ߿~`>0bĈ#F+o`|;Ϡ|1bĈ#F1bĈ#F1bĈ#F} o_> 70A0bĈ#F1bĈ#F1bĈ#F? >} 70'p "Lp!ÆB(q"Ŋ/b̨q#ǎ?ĨGB 2dȐ!C 2dȐ!1_!C 2dȐ!C 2dHy/dȐ!C 2dȐ!C >~ 2dȐ!C 2dȐ!CĨGB 2dȐ!C 2dȐ!1_!C 2dȐ!C 2dB~B6ϣ|!C 2dȐ!C 2dȃ>~ 2dȐ!C 2dȐ!C/_|B6ϣ|!C 2dȐ!C $?$X ?$XA PB8`A&TaC!F8bE1fԸcG'p ϟ>P <0|8`A <0… :|1ĉ+Z_~˗_Ɓ2"̗`2Z/A}O|8`A&`> 4x ,h „ 2l!Ĉ'Rh"F~ G0_(_> a4_>xP(,h „@,hP ?~8`A&TaC!F8bE1O|د?~/_>˷O |ԧ/_A~/_>O_>~O_>~˧ϟ|ܗ/}_>Q"?}Wp_|aO_~ۇ#F1bĈ#F|O }۷}|G߾>_>_?~߾o߾ /_> < ?} &$}%L0a‚K_|8`A&TaC!F8bE1_?}O_~70߿|'| o`>O`>_+o` o`>ӷ_Ɖe4B2f/_˘1cƌ3f̘1cƂ _?/߿߿'P` o`>O`>_>70| __}'p "4O_„ 0a„ "ԗ_BO@ DPB >QD-^X_7'0}?~_|O߾ '0| '0߿|70@}/_|/|/cD~'P_>}˧>~36ԗ_~e̘1cƌ3f̘1}/˷?}/_~˷?}3?}/_~ '0| /߿|˗@/_|˗|˗/_/| ,h „ oa|8`A&TaC!F8bE1fdOF5 `>}'0@~wP?/a|4jԨQF5jԨQc>5j(/|>~5:ԗ~iԨQF5jԨQF5jH/|ӇP?ׯ`|4jԨQF5jԘ1|'P?Ǐ@~ G A $ A >  <!?}˧_>˷>~ &L0a‚/>$XA .dC%NXE =̗0_B~ ˇ0|co@}h/|>~3Nԗ߿~e̘1cƌ3f̘1c|/_~_'߾ӧ/_A}ԗO߿|/_~Ǐ | Է/ϟ|̧o| ?}w0?}o |;_ƌۗo_ƌ3f̘1cƌ3:>}o?`# | (0|O`>7_>/߾}?~ ǯ?~7P?} H ӗ0aB%L0a„ ˧? 4xaB 6tbD)Vx| _`#`>o| 70?/|0߿}G0@eO_FeH_|e̘1cƌ3f̘1|/߿|w0|;a>맏| 7`>߾|80?}7_O@ DX /a„ &Lh_} <0… :|1ĉ+Za>_ /߿} _>CO`>/| O ? ̧o?``˘Qb|/cƌ3f̘1cƌ˗o|O_|`>o`>`>~ /_>}'0@'0|ӧ|` G0@~ӗ!?} 1c~ԗ_ƌ3f̘1cƌ3f̨@8`A&TaC{>~>|h_}Ç>|Ç>|Ç>tÇ>|xÇۗP_~>|Ç>|Ç>|Ç>|C=|O>$/߾{Ç>|Ç>|Ç>|Ç=|~5ԗÇ>|Ç>|ÇǏ|/_> O߾|'_|˗Ç>|aA~'p !B"Dh_}? 4xaB 6tbD)Vx|_>O`_ ?~2f̘1#?} 1~Eԗ_ƌ3f̘1cƌ3/?o`>O` o3f(˘Qa|&/cƌ3f̘1cƌ 0@ۗ߿|߿|7P~O@ DPB >,OćAqa| ԗD!B"D!B"DO ?_| '0?}70D!B!?}"ąA~˗߿}O`>ӗ/?/D!Bq!?} <0… :|1ĉ+Z1ƍ;z"?} 2dȐ!C 2dȐ!COG}B 2dȐ!C 2dȐ!/Q!C 2dȐ!C 2dȋ}/dȐ!C 2dȐ!C "?} 2dȐ!C 2dȐ!COG}B 2dȐ!C 2dȐ!/Q!C 2dȐ!C 2dȋ}/dȐ!C 2dȐ!C "?} 2dȐ!C 2dȐ!COG}B 2dȐ!C 2dȐ!/? 4xp>~ H*\ȰÇ#JHŋ3jȱǏ /Q!C 2dȐ!C 2dȋ}/dȐ!C 2dȐ!C "?} 2dȐ!C 2|˧`>'p| $/A O> 8`A`~0@8`AӇ!ƒ'p "Lp!ÆB(q"Ŋ/b̨q|70&˷`_Fw0߾ ?}Ǒ#G9rȑ#G37P_ϟۗ߿}/_>|/_>W0_|/_>O>}/? w0 ?}Ǒ#G9rȑ#G3?~#ϟ>/߿~۷o?|}|߿o߾?}?#80~70A $H@~$H AO@ DPB >QD-^ĘQƅ_Ǐ`߾~O O>O|ӗ_ '0|3/__8䧏@}8rȑ#G9rȑc|/|#_>_|o_>~7P?O_o@oO?} G A $(> $H@}8`A&TaC!F8bE1fԸqao`>#_>}>~ ||/}_>㗏@~ۧ0?~70GqG9rȑ#G9r̘| 7 /|O_|ϟ|/_~O|˗o?O_|ӗo?}#/_|+| ?}Ǒ#G9rȑ#G9# (,h „ 䧏!Ã1dȐ!C 2dȐaB'p "Lp!ÆB(q"Ŋ/b̘/cƉe4_ƌ3f8_|e̘1cƌ3f1_F2J̗"?} 1cƌ30@8`A&TaC!F8bE ]H1|]OE]xŋ'p`> H*\ȰÇ#JH?}O`>O|˗ϟ@}/|˗ϟA}/_~ϟ|O߾|OA~/_>|/_> ԗO߾|U/@},h@$XA .dC <0… :|1ĉ߾}| _}/>~ ̗?~o߿|߾}o?o|_>~O`>o_ň8`A8p_?$XA .dC'p`~'p "Lp!ÆB(q"EO`>} 70_>~ '0_a˧o?_>o` '0|/|̗o`+VXbŊ!O~ <0… :|1ĉۗ߿|/'p߿|ۗ>~//߿|70|O`>_/_8`A&TaC!F8"DGP+VXbŊ__>O ? ̧o?`>|70߿| O ԷO`>}'0|O߾ G0}XbŊ+VX| WbŊ+VXCo_>}˗߿|O_|ϟ|/_~̧/߾ӧo_|˗/_?}O_|˗|'0|˗|ۧ/_~ H*\ȰÇ#JH"A},ZhѢE-ZbA~Y,ϟ}-ZhѢE-ZP?-ZhѢE)X| ˗/?-ZhѢE-ZtE-ZhѢE-ZhѢE-ZhѢE hѢE-Zh`~'P  4/_> +`70|$? 4xaB 6tbD)V~/_?˗߿|;} />~O` ?gѢE-ZhbC},ZhѢE-Z,/߿}+O?/߾}?} 0@~O߿|0}߿| /߿|_O`׏Ao,h „ 2l!Ĉ'R(P?-ZhѢE > /_|O` g_~/߿| '0_`_'0߿|`>}YhѢE-ZP?-ZhѢE 'P?O_o@oO?~ '0_ 7p| '0߿|Ǐ_>_o_>~ H*\ȰÇ#JH@},ZhѢE-Z$/@}#O~O߾ |7߾㧯߿| ̧o| w0߿| /߿|O``>~YhѢE-ZP?-ZhѢE 7 ˗/߾ϟ| ̧/>G_|| '0?}O`>O`>㗏_|_> G0|hѢE-Zh>8`A&TaC!F8bE]ł wŋ/^xbC}.^xŋ/^X0ŋ]$/_.^xŋ/^lŋ/^xŋ/^xŋ/^xń]xŋ/^?.2wŋ/^xŋ xŋ/^x1a/^xŋ/^xѢ>~/^xŋ%ӗo?~_|˗?}ӷ/_?}O߾|O}O`'P?~˗?? 4xaB 6tbD)Vx>~1bĈ#FO| __>~󗯟|_o}O`>~߿| /aĈ#F1bĈ>~1bĈ#F@~ /| '0|'0߿| 70|'|'?}aĈ#F1bĈ>~1bĈ#F0@/߿|/߿߿|_߿߿| _߾|8`A&TaC!F8bE#F1bĈ1a>'o?O`>O`o}_| _>0bĈ#F1bP?1bĈ#F ˷ϟ| '0?/| /߿|/߾|˧/}/_>~ O_|˗/_0bĈ#F1bĸP?1bĈ#F aϟ}aĈ#F1bĈcD}0bĈ#F1J| H ˗/8`A&TaC!F8bE1/cƌ3f̘1cƌ3f̘1cƌ3f̘P3f̘1cƌ3f̘1cƌ3f̘1cB}2f̘1cƌ3f̘1cƌ3f̘1cƌ ˘1cƌ3f̘1cƌ3f̘1cƌ3&/cƌ3f̘1cƌ3f̘1cƌ3f̘P3f̘1cƌ3f̘1cƌ3f̘1cB}2f̘1cƌ3f̘1cƌ3f̘1cƌ ˘1cƌ3f̘1cƌ3f̘1cƌ3&/cƌ3f̘1cƌ3f̘1cƌ3(? 4xaB 6tbD)VxcF9vdȋ)RH"E)RH"E4H"Ey1_?(߿O@ /$A8`A&TaC!F8bE H| O@ DPB >QDW0_|ǯ"AG0|UXbŊ+VX"|8`A <0… :|1ĉ`_|˗ϟ@}/_>o|㗯?~o_>~˷O |ۗ>ۗ?WbŊ+VXbłUL/_Ŋ+VXbŊǏ`'߾>ӧ/@~'0_O`>~߾} o߿~߿|o_Ŋ+VXbŊ W1|+VXbŊ+> /_|O`>O ?O| ̗@~ 'P`>'_> /| <0… :|1ĉ+"B,ZhѢE-2O߿ϟ߿|@o>'0@/߿/߿߿|߿|ۗ,h „ 2l!Ĉ'RX0?hѢE-Z_ G'0}?~ o`} '0>~O`>O?/| |-ZhѢE-Bo|WP_>} 3/E-ZhѢŅ o`˷?}/_~˷ϟ|/_|O`>'0|˷?}/߾|'0?gѢE-Zh"| ;`_?} /,h „ 2l!Ĉ'R0E ϢE-ZhѢE+O |O`3/E-ZhѢEh1a|hѢE-Zh"| _>+Ϡ|-ZhѢE-ZhѢE-ZhѢEW0@~ _|hѢE-ZP|O@~88`A ̇0,h „ 2l!Ĉ'Rha> o}g_/^xŋ?}3|/^xŋ/^_|_> g_/^xŋ ?} GP|˷o_? ӗ/o_? /}}ۗ>'P߾|.^xŋ/^\_|O߾|o`>]xŋ/^,o> 3`>~ϟ>`?~ o?~>/߾}O|? 4xaB 6tbD)VTϟEYhѢE-Zto|`>o+/|+o`_> g_>hѢE-Zhb>gѢE-Zha> ܷa>/~맏|g0| '0߿|o`?}O`>-ZhѢE+g|-ZhѢE_%g0 /@~Է|/| P/߿| H*\ȰÇ#JH|,"ϢE-ZhѢ|O|o`>'0?˗/_?~O`> />}˗| O_}˗O>~ gѢE-Zhb|,"ϢE-ZhѢE|YO>-ZhѢE-6"B,ZhѢE-Z/_o`?ϢE-ZhѢE _>-ZhѢE /_?(߿O@ /$? 4xaB 6tbD)2bB*VXbŊ+Vt`>+`~W|0_Ŋ+VXbŊ _+VXbŊ O|/@~ӗO?~C_ د?~/_>˷|O`>o_|ۗ|ۗ|ӗ/|UXbŊ+VX0 XbŊ+VX1|߾}O|O Ǐ`?~˷o?O| (P`>/߾}ϟ|ϟ|_| <0… :|1ĉ W1|+VXbŊ+_ G_'}G0~o| 70? o` '0|'0|˧o+VXbŊW1|+VXbŊ+_ >~`>'p |_߿_/߿ ߿'P` '0|'0|߾|8`A&TaC!F8b| H'p "Lp!ÆB(q"|?~/߿|/?}W_ G'0}?~ o`}O߾ '0| '0|"E)RH"Ň H|  <0… :|1ĉӧ_>}/>}˧O|˗߿|O|˗o?O_|ӗo?}#/_|ӗ/_?'0|'0|˷/_|(RH"E)RP?)RH"E)*G"Ł(RH"E)RH"ńQH"E)RHQa>)G"E)RH"E)&Ǐ"E)RH"E)RH"E)RH"E)Ǐ"E)RH"E /G  0@(_| <0… :|1ĉ+ZXP?1bĈ#F /a;/? ˇ#F1bĈ#ƈaĈ#F1bL/_>7P?~'0߿|`}/_?/? o?>}0bĈ#F1bQ?1bĈ#Fo߿|o?/>}o?`+`>1bĈ#F1F#F1bĈ#|W0߿| _>}'p`/@7p|7_~O@ DPB >QD-^,F1bĈ#F`_G0|/_|G0?!̇#F1bĈ#ƈaĈ#F1bDo?+}_>~|O>|߾0bĈ#F1bQ?1bĈ#F˗߿|/_~/_|˗o|O_|`>맏_| 70|1bĈ#F1bl }8`A&TaC!F8bE1fԸcGAH"E)RH"E)RH)RH"E)RH"E4H"E)RH"E)RH)RH"E)RH"E4H"E)RH"E)RH)RH"E)RH"E4H"E)RH"E)RH)RH"E)RH"E4H"E)RH"E)RH)RH"E)RH"E4H"E)RH"E)RHO@ DPB >QD-^ĘQF=~>~"E)Rd|`>o ?$XРA~#A8`A&TaC!F8bExŋ/^(1|W0_|@G0ŋ/^xŋ 'p 3hp ,h „ 2l!Ĉ'RG_|˧|Ǐ|W0~˗߿}O_} ?~̷/>o_>~o_>~ |QH"E)RHq`} HO@ DPB >QD'0|_>~|O }۷}O_>#߿}o߾ /o@H"E)RH|(*"E)RH"E '0߿|>~#`?~7_@~G0| o`>O>H"E)RH|(*"E)RH"EǏ_>'P| (`>/߿?/߿߿߿}80@߿|߿|߿? 4xaB 6tbD)6bB*VXbŊ+V70 O?;/@}#O~O߾ |7߾O?/| o`bŊ+VXbņUL/_Ŋ+VXbŊ O>~O ߿O@~ϟ|ӗ/_?O_|/_|˗o?O_}O`>7_|O@ DPB >QD /_|ԗO?G_+VXbŊ+W1b bŊ+VXbŊ +`|/?~WbŊ+VXb*FWQa|XbŊ+VXb| /߿}o ?}bŊ+VXbŊ+VXbŊ+VXbE70|g_>UXbŊ+VX1aE0@(_|/?$XA .dC%NH1˧o | 珠|+VXbŊ+VL` U|/?*VXbŊ+V_|'p_>W_+VXbŊ˗?~/_>O} ܗ_?~o_>~˗?~/_>o_|ۗ_|O?~ O`+VXbŊ)W0||g_+VXbŊo@~o?o߿o߾_߿|_>߿|/߿|߾?ϟ>? 4xaB 6tbD)R`>ӧo_|o_|bŊ+VX"E} O |O`>`>/|W__| '0_|7_~WbŊ+VX"|*&bŊ+VXb~O߿|/߿|߿>~_ǯ>~@70| 80 ܷo}8`A&TaC!F8bYD/E-ZhѢŅO ?}ӷ| O߾'0߿|/| ̗?'0߿|_>Ko~g>-ZhѢEg|-ZhѢE˗/_?O_|˗|/>}|O`׏_|/_~O_|O`>>~'0|ϢE-ZhѢŃO@ /,h „ 2l!Ĉ'RhD~ È#F1bĸ0?È#F1bĈ#F0#F1bĈqa> ˇ#F1bĈ#F1bĈ#F1bA0bĈ#F)x1ƃK`>0bĈ#F1A0bĈ#F%#0/|Ȧ#F1bĈ`> ˇ#F1bQ_>}'_|ۗA~/_>O_>~O_>~˗O_|˗>~˗߿|ӗ//ۗo߿|_?$XA .dC%N0 XbŊ+VX1߿|/@~o }۷}󗯟|󗯟|˗?~> ߾}_|G0߿|o?}|WbŊ+VX|*&bŊ+VXb| /?o` '0|'0|˧o`>_>}/| _bŊ+VXbŅ? 4xP| H*\ȰÇ#JH1߿|O@߿/߿߿|߿|/߿|߿߿|߿|/߿|߿߿>~@O@ DPB >QD 'p  <0… :|1ĉ'0?}/|O߾ '0| '0|o}`>_ G0߿|o_>o|Ǐ"E)RH"E H|  <0… :|1ĉ'0?'0|˗?}O`>O`>ۗ/_~˗| /_~/_|@}/_>}ӗ/_"E)RH"EH"E)RH"E)RH"E)RH"EH"E)RH"E)RH"E)RH"EH"E)RH"E)RH"E)RH"EH"E)RH"E)RH"E)RHEQD@'p "Lp!ÆB(q"Ŋ/b̨q#ǎ? yQ?"E)RH"E)RH)RH"E)RH"EiP?"E)RH"E)RH)RH"E)RH"EiP?"E)RH"EOH"#'RH"E)RH'RH)RH"E)RH˗OH"#'RH"E)RHO@$XA .dC1bĈ#F1bĈ`>o ?$XРA~;x ?~1?$XA .dC%N1|7_o_|ӗo?~#ȏ|˷O |/_>}O>~O_>~ | ԗO?~߿|W"?}'p|'p "Lp!ÆB(q"Ŋ/b̨q@G0_}o?o|ӗO ?O?~|o|/_?o@#|_}qtOǁԗ/_|9r_?ȑ#G9B_?}O_~70|Ϡ|/?_O`>_>'?/Gq_Aȑ#GW_>}9rGq@ ߿|߿ ߿|_ ߿O|8_߿|/߿߾|O?~/߿ǯ>~8`A/a„;/_„ &L0a„ &LР|%L_>} H*\ȰÇ#JHQ`|`?}ӷO`>}'0~>}O`O`>|/|?}ׯbE~**ǯbŊ+VH_|"˧bŊ+VXbŁ o`˷?}/_~˷ϟ|/_|˷?}O`O`>_| />}O_|_w0_>˗/_>8@}8`A&TaC!B/1bĈ#F1bĈH0_Ĉ#FH ̗O`E1bĈ#F/1bĈ#F1bĈH0_Ĉ#FH/|/A}"F1bĈ#/1bĈ#F1bĈ#F1bĈ w0>~`>~/bĈ#F1@K/#F1bĈ#F1_D"Bb# `>~'0A~!/bĈ#(" OO@ DPB >QD-/E.^(/|ۇP/^xq|Oŋ/^xņ/|O>~/篠|O | Է/?'P߾|/?gP|˗?}/_>ӷ/?wq"?}˗_>ۗA}.^xE7_>}/^xŋ_/@~_>>_߿||//߿|ϟ>o_>_>~۷_| H w0?}o |;_„ &L0a„ &L0aB_>} H*\ȰÇ#JHqa_>~ '0_>~ӷ| /?o`>~ '0_a˧o?_>bE~**ǯbŊ+V_|XbŊ+VXa|߿/?/|(߿|@Oo_>~ o`~߾|ۗ߿| /| H ӗ0aB%L0a„ &L0a„ &D/_>} H*\ȰÇ#JH}_|߿|'?~/|߿| O ? ̧o?`>|70߿| OUO_EUXbŊ˗/+VXbŊӗO_ϟ| '0?}/_|#_>ӗ/?O_|ϟ|/_~̧/߾ӧo_|˗/_?}X>$X'p "Lp!ÆBx_>}'QD%J(QD I\OD擘П?$J4OĆI(QD%>O@(QD%J(QD$.'Q"~IL/_|$J4OĆI(QD%:OA(QD%J(QD 0|O|O@ DPBkذ>~ 6lذaÆ 6l_>}aÆ 6lذaÆ 6lذa|)̷o|5lذaÆkذ>~ 6lذaÆ 6l/>װaÆ 6lذaÆ 6l_|˗?}/ۗ/?ۗo@~/_>o|˗|G0@~װaÆ װaA}6lذaÆ 6lȰ_>} aÆ 6lذaÆ 6l!}̷o?/߿7Lȳo߾߿۷_O|oǏ_ O,h „ .䧏!Ã1dȐ!C 2dȐ~1/? 2dȐ!C 2dȐ!C o`O`o`˗o+_/? 2dȰ ?} Ǐ!C 2dȐ!C ˧@cȐ!C 2dȐ!C 2d0|'0|70| O?~O_| G0?}G0|2dȐ!Â1dxP? 2dȐ!C 2D/>? 4xaB 6tbD)ԷO`>}/| O߾ 70_>}>~ӷ|㧏߿| /_ŊWQ>~+VXbE/_>+VXbŊ+ /_~_>/?'0@}/_>}˗߿|ϟ|_>ȏ߿~XB~**ǯbŊ+V8_>}bŊ+VXbŊ+VXbD~**ǯbŊ+V8|8`A <0… :|1ĉ+Z1Ƌm$ƍ7nܸqƍ7nܸqƍ7.o#A}6nܸqƍ7nܸqƍ7nܸq!?} qƍ7nܸqƍ7nܸqƍ HP7nܸqƍ7nܸqƍ7n\OFmܸqƍ7nܸqƍ7nܸqB~6oƍ7nܸqƍ7nܸqƍӷ>~7nܸqƍ mܸqƍ7nܸ1#?} qƍ7nP_~7nܸqƍ7nOFmܸFmF /_>}8`A&TaC!F8bE1J/A}2f̘1cƌ3ԗ/_|e̘1cƌ3f̘1#D~2Է? 4xaB 6tbD)J08`A&TaC!F8bE1>> 4xp |,h „ 2l!Ĉ'Rhq",h „ 2l!Ĉ'RhbA H'p "Lp!ÆB(q"E8} HO@ DPB :o |( G A$H |O@? 4h?~ H*\ȰÇ#JHŋ!˗/_~X_3fo@}H0D~W0_O?2f̘1cƌ3f̘|/cA2f̘qb>}o }}O`/?˗?W0|7_o_|ӗo?~#ȏ|3f̘1cƌ3fP?e,/_ƌ3Nǯ?~ GP?}_o߾O?~?|߿| O?/߾}?} 7p~8`A&TaC!F8bE1^/cA2f̘qbo`>#_>__>~ӷϟ}G0~o| 70? ۧ/cƌ3f̘1cƌ/㗱|3f81?}70_|O`>o`0@ۗ߿ ߿ϟ߿߿|(߿O_?$XA .dC%NXE/_|ԗOA˘1cƉ_뗏`O_'?~ o>~o`| ̧o?_>~ȏ|2f̘1cƌ3f̘Ѣ>~}3/_ƌ3N_>ȏ߿~O`/_~ O_|˗/_ o`˷?}/_~˷ϟ|ԗ/2f̘1cƌ2(2 }(0@~Ǐ?~ӗo@8`A&TaC!F8|+VXbŊ+VXbŊ+o`>~ 䗯_|XbŊ+VX_+VXbŊ+VXbE7_>}W0A*VX|˧`>'p "D/a|O@7,hР?~8`A&TaC!F8bE12ǯ`o` ˗1cƌ/|-k/> _>~O_ƌ3f̘1cƌ3.ǯ`O߿~Ϡ|3f81 7߾ /_> ϟ|ӧ/߾'P߾|/?}C_> د?~/_>˷|O`3f̘1cƌ3f\_|O|o`>e̘1|/| ϟ_>߾ӧ/@~߾}}>~ O?/߾}?} 7p~8`A&TaC!F8bE1./cA2f̘qbo`>oo'|70}/_|O` g>}3f̘1cƌ3f̸P˘1cƉ_ G߾|O`}7`>__}'p? _> o|70|(߿O_?$XA .dC%NXE X_3f?}~_70߿|O ? O߾`|`?}ӷO`>}'0@~1cƌ3f̘1cƌ X_3f| 7 /_~ӧ|O_}O`>ӗO>/}'0A~ϟ|ӗ/_?O_| ˗_ƌ3f̘1c2h!O@ /,h „ 2l!ĈIDOD$J(QD%J(QD%J$DI(QD%6'a?(QD%J(QD%J(>~'QD%J(QD%J(QD%J(QD%2O"C$J(Qą (QD%J(QD%J(QD%O"C$J(Qą$J(QD%J(QD%J(QD_>%J(qa}/?'?}/_>%J(QD%J(QDI$DI$QB <(_>"D!B"D`>~'߿}'0߿|} H*\ȰÇ#JHŋ3jܸQ?ȑ#ǃ'߿| /|ȑ#G9rȑ#GQ|9rx0@ ߿/߿ۗ,h „ 2l!Ĉ'Rh"ƌ7nǏ@8r`>'?}'0߿|#G9rȑ#G9rlGqȑA}/_>~˷/| /_|ȑ#G9rȑ#GQ|9rџ?8rȑ#G9rȑ#Gq/G9F/_>9rȑ#G9rȑF}8 #G9rȑ#G9rȑ#G(_>9r#G9rȑ#G9RǏ@8rȑ#GqGqGqGQ>~ HO@ DPB >QD-^ĘQF=~Q? 2dȐ!C 2dȐ!Cbϣ|!C 2dȐ!C 2dȐ/_ȇB 2dȐ!C 2dȐ/_H 2dȐ!C 2dȐ!Cϣ|!˗/߾!C 2dȐ!C 2dA}<ҡ?  <0… :|1ĉ+Z1ƍ;z(P_>$X`AO@ DP>̷> 4x ?$XA .dC%NXE5nqc?$XР} P <0@$ <8P>$X 0@7,hР?~ ;xP`'P? Ǐ'p "Lp!ÆB(q"Ŋ/b̨qF _>$X!D | +?!D?}#!B;O?  <0… :|1ĉ+Z1ƍ˗/>qOG+`/'P|˗A~#o_}߾| O߾|˗ϟ|O?~ԧ/G9rȑ#G9r_| @~8>~ܧ?o߾ >};O|/@~O`˗?~ ϟ>Ǒ#G9rȑ#G92_A8䧏}߿'p`?~7_@~ۧo|'_O`>} (0@ O'p "Lp!ÆB(q"Ŋ/b̨q#GuO_G ߿|߿ ߿|_ ߿߿o|_߾|807P_'p "Lp!ÆB(q"Ŋ/b̨q#G ԗ/_|WA~87P7_>}ӷ|/? O`~o>~_>| /?~g0_ǎ;vرcǎ;vh_Gqo`?}/_}˗?}O_}ǯ|ԗ/߾ϟ| /|`>| G0_ǎ;vرcǎ;vh_/߿}ۗO? (?} H*O߿|ϣG=zѣG=zѣGG_>}`yѣG=zѣG=z_˷|# ?} +`><'1|O@ H*\ȰÇ#JHŋ3jq|̷/ ?}cb Io_|;vرcǎ;vرA /?}O`q_>~/_?˗߿|;} ԗO߿|Ǐ?}3}O`'0?˗ϟ?}+_:vرcǎ;vرcGuOG~O߿|߾_/| /?g0?߾_߾>~? 4xaB 6tbD)VxcF9@~8o_O`|'0| G0|70|'0|70|O ?}_?}uرcǎ;vرcǎ(Ǐ߿| ̗`>O`>_3_>_|/(߿'p "Lp!ÆB(q"Ŋ/b̨q#GuO_G}_>ӷO`;_>_| '0| '>~/|O ?~ o>~uرcǎ;vرcǎ(O`>/_~ /_| G0/|G0߿| ?}O`>/_|7:vرcǎ;vرcGuO_nj:ױcǎ;vرcǎ;v@'p !B"4!B'p "Lp!ÆB(q"Ŋ/b̨q#ǎ>=zѣG=zѣG?}˗G O@ O@ DX0_‚ 0@8`A&TaC!F8bE1fԸ|_~1_ϟql!| ȑ#G9rȑ#Gq ?}د?~/_>˷O |/_|O?~ԧ/_A/|>} /?o_>~ӗ/+#G9rȑ#G9r/ǁqO?/߾}?}ȏ }%7P?}3`>O`|_~?~/߾}?~8_ ? 4xaB 6tbD)VxcF!q ?}/_|_o}%7_>~g0_A} '0}/?/|pǑ#G9rȑ#G9 @~87_| /@'p|o~#(߾|_}7`>߾|8`>/,h „ 2l!Ĉ'Rh"ƌ7B@~87_>}ӷ|o>~3/aǏ| +o`o`O?'0}o` o`>9rȑ#G9rQ||O_|˗߿|O_|`>| 70_Ao_>}˗߿|O_|ϟ|/_~ o`?}qȑ#G9rȑ#G | HC!B!D!Ḃ!8`A&TaC!F8bE1fԸ@: 䧯c|;&̷_ǎ;vرcǎ;v(_/_|8G0|;vرcǎ;vرcǎ(|رcǎ;vرcǎ;v/_Gu7_o_|ӗo@~+_>'_|O?~?}رcǎ;vرcǎ;N@~:ϟ@~˷o?O?~#o߿|o|o߾/>$XA .dC%NXE5nq|?}/| ̗@~`_O`ѣG=zѣG=?~87_| /@/߿߿߿|ۗ,h „ 2l!Ĉ'Rh"ƌ7r8_>O~O߾} `>O?_~˷/߿| _|ѣG=zѣG=z/yϣ@ѣG=zѣG=F?~!,`>'p? <0… :|1ĉ+Z1ƍ Q ?}˷`|H0| ױcǎ;vرcǎ;@~:/_| |ۗ/?_| 㗏`} /'?}/_> ?}WP|;vرcǎ;vرA: 䧯|#>} |O`~'_A~#߿}o߾ /o|?~O8`A&TaC!F8bE1fԸA: 䧯>~#`?~7_@~`/|'0}/ w0_ǎ;vرcǎ;vh_0@߿?߿߿O|߿__|7p`>$XA .dC%NXE5nh_/@}#O~O߾ |w0|ۧ@~O`70|W0|;vرcǎ;vرA: 䧏>~ |O_|˗|/_>~ԗ/߾ϟ| /|`>맏`>cǎ;vرcǎ;v/_GuH0_G ױ!;vرcǎ;vq @8`AӇ!B"4!B˗B"? 4xaB 6tbD)VxcF9@~:rQ`3b:vرcǎ;vرcǃuO_ǎu/|A̗0߿|;vرcǎ;vرA: 䧯#|ۗ//?/?˧/_>>~߾|/?}#O_>}˷O | /WP_>}'_|ۗ_ǎ;vرcǎ;v/_Gq|_>0߿_˗?~o?߿۷}˗?~/߿|߿}/?~/?} /߿|/@~? 4xaB 6tbD)VxcF)q ?} o`O`>_>}70߿| 70߿|`o| />'0߿|O`>9rȑ#G9rѠ|1|'0| '0|/`>_G0߿| O?~+|߿| H*\ȰÇ#JHŋ3jH_>o|_>O`>|/߿| Է|o`>#?~/?}>~'0?}/|9rȑ#G9rȑA8䧏?}/_~ '0| '0߿|o?}`>_˗|/_}˧O?/|/|˗|qȑ#GqGqG/,h@~"D!B"D!BO@ DPB >QD-^ĘQF(;vtدcǎ;vرcǎ;vt/_Guرcǎ;vرcǎ;v_0|O@7,h?~8`A&TaC!F8bE1fԸcGyOdž˷`>FѣG=zѣG=b?~:˗?}/|˗|g0~߾| ԧ/>7?Ǐ_|ӗ/o_~/_>˗ϟG=zѣG=zd/qO o߾}|}۷}|짏`>~߿|/߿_~? 4xaB 6tbD)VxcF1q ?}_>}>/_ '0| W>}O O`>/?#G9rȑ#G9rd/ǁqO@/߿o_>~ 0@?/o |߿_o_>~߿|o@? 4xaB 6tbD)VxcF1q ?}_>_+/@}+O~ ̧o?>}'? O?_ '0| |9rȑ#G9rȑB8䧏?}O`ӧ|o>~ |/_~O`>}/_>~Ǐ`|/>} '0?}/_|ȑ#G9rȑ#8!O@ OB"D!BO@ DPB >QD-^ĘQF5?}=vѣG=zѣG=z\/yѣG=zѣG=z_>ѣG=zѣG=zq|ѣG=zѣG=zB<ϣG=zѣG=zѣDžyOG=zѣG=zѣG >=zѣG=zѣNj$XA П <8p>$XA .dC%NXE5nq#|,h „ 2l@} H`>o ?$XРA~ ?} .oƒO@ DPB >QD-^0`>8`A&O}}O ?}˗}ۗ//_> H*\ȰÇ#JHŋ ?}˗/cF/_>o? ϟ@~˷o?O|o }۷}󗯟|󗯟|˗?~>o߾/_>˘1cƌ3f̘1cFe,O}2f/>>~#`?~7_@~70@O`>O`>ӷ_| W0߿|/>'p "Lp!ÆB(q"Ŋ/bd_Ƃep_}/cB ߿|߿ ߿|_ ߿߿/߿߿|߿|/߿|߿߿| __}'p "Lp!ÆB(q"Ŋ/bT@}˧| 3O_ƌ _|2&o>~o`| ̧o?_>~ȏ|'0}O`>O`>70@}'0_|/߿|/cƌ3f̘1cƌW0A~G>ep_} /}70_A~ϟ|ӗ/_?O_| ˗@O_|O`O`˗/_ӗ/_'P`_˗,h „ 2l!Ĉ'Rh"F'_3O_ƌۧ_|2f/cƌ3f̘1cƌ3f̈1ϟ@~g!˷|eX1_ƌ3f̘1cƌ3f̘c> O}g#˷Ϡ|e̘1cƌ3f̘1cƌ3fh1˷ 3O_ƌG_|2>0A} ?7p?~? 4x`>/?} ? 4xaB 6tbD)Vx| o_>_ ӗ1} /=̗0AW_>x0|G0_ƌ3f̘1c3J`>ӧ_|70A~2f/>1߾|˗@~~ ?} GP|/|˗ϟ@}#/_>߾|/_>/|O_ƌ3f̘1cƌ3./cA~2f/_|2J߿}}>o>GP`> _>~߿|O`>~_㷏|/| <0… :|1ĉ+Za> ӗ1cE81|o;o|ۧ`>/?o`>~ '0_a`˘1cƌ3f̘1cFe,O_ƌ˧/߾ ̧|O|o~!'0@/߿7P߿|ۗϟ7_>~ H*\ȰÇ#JHŋ ?}3N/1/?}>~ /?~W0| ̧@~o>~O`>}|㧏߿| /_ƌ3f̘1cƌ32/cA~2f/_>ۗ|˗O>~_> ȏ߿~˗߿| G0/_>~ 7П| '0?}/_|#|'e̘1cƌeQFe@'p  !B"Dp|ܗoB"D!BO@ DPB >QD-^ĘQƂqOG |8rHG9rȑ#G9rGq_|˷#G9rȑ#G9rb>Ǒ#B[/>9rȑ#G9rȑ#GqOG|8rȑ#G9rȑ#G+Q ?}9/xP?~ϟ|'0?}ӗ@~/_>9rȑ#G9rȑ#|8 䧏#ǂq_} Ǐ_}_>|} H*\ȰÇ#JHŋ3j(1?ȑ|Mܗoǂ'?} '0߿| /?Ǒ#G9rȑ#G9珣@~8r$/H0? ߾|/߿|/߿ۗ,h „ 2l!Ĉ'Rh"ƌ7J珣@~8r$E~8O?_'0|ȏ|9rȑ#G9rȑ#|8 䧏#GO_|˗/_/|/_>~_>9rȑ#G9rQ`>Ǒ#G9rȑ#G9rȑ#GqOG9rȑ#Gȑ#G9r$Gqȑ#G9rȱ!|8rȑ#G Q ?}9rȑ#G9r\`>8`A&TaC!F8|*&䧯bŊ+VXbŊ+"P QD-^,Oƃ0@8`A&TaC!F8bE~XbŊ+VXbŊ 髨P?˗O_Ŋ+VXbŊW_|*VXbŊ+VX"C~**Ǐ?}UXbŊ+VX_>}WbŊ+VXbŊw0_>˗/_>} 3o_Ŋ+VXbŊ˧Ob|*VXbŊ+VXC~'0_>WbŊ+VXbE/߾+VXbŊ+V/|/A}*VXbŊ+O$/>$XA .dà /_7_>} ߿O@ D0a> *T/|>~ H*\ȰÇ#JH"~1oE-ZOb7P_ _>~-.gbA~o|OB},ZhѢE-Z(_>} gѢE'+} /_O` ԗo`>_|˗?}/_>߾| Է/_?} Է/_?}/>7p_|bB~O|oB},ZhѢE-Z8_>}gѢE)+_>O߿߾'p|/|ϟ@~˷o?O?~o?o|/|/߿|}o|$H A#80߿|`},h „ 2l!Ĉ'R_>}gѢE+˗/|/߿};؏| o`O 70| '0|/?}70|w0?}o |;E-ZhѢE ˧`|,ZhѢ|'0߿~/߿|ˇ0?}70_A˗`0߿߿|/߿_߾|߿|O@ ,OB <0… :|1ĉ+FO~YhѢE/|/߿|㗏|/|/}_O? ԷO`>}'0| '0||/|g1>~-ZhѢE-Z_>~߿߿| HCA}8`A&TaC!F8bʼngѢE-Z$/@~#hѢłYLE-ZhѢE˗OE-Zhq`|1E-bB},ZhѢE-Zx_}hѢE-ZhѢE 鳘P?-ZhѢE-o~YhѢE ˇ0|/?} ? W` W` ,X`A~ ,X`'p "Lp!ÆB(q"ŊG_>}-Zhb|!1|G0_|Y\ϢŅYLE-ZhѢE ˷`|,ZhbE}˗ϟ|ӗ/'_|˗߿|O@}o? ˧_>~ /G_|ۗ}/_>-bB},ZhѢE-Z8_}gѢE)o?/ ߾} ߾O|Ǐ_?}'p||_ '0_| <0aA~*TO?$XA .dC%NXa|˧ϢE-N/|˧o/߿|˧|/|w0|O @~ /|h!?}gѢE-ZhѢc/>-Z(1߿| /߿~o`~/_|O?}G0|_ _߿|? 4xa‚)T>~ H*\ȰÇ#JH~9OE-Z}_>~ }}o_>`>@~ 7P?'0?}o`> P <80?$XA .dC%NXQa| ˧ϢE-Bϟ| /|/_}˧O?/|O`>/|˗߿| /_>~ ϟ|˗/_,Z$ ? 4x?$XA .dC%NX1a|$˧ϢE-Zh!?-Zd/?hѢE-Zh~QOE-Zh"~-Z_|(˗ϟE-ZhѢEg_>~-ZhѢE-Zhq|I/?-ZhѢE- gQ?-ZhѢE-ZX_| ˗ϟE-ZhѢE-ZhѢE-Zhņ_|,ZhѢE-ZhѢE-ZhѢE->/?gѢE-ZhѢE,",",", O@ <0… :|1ĉ+Z1ƍ;z2$BC/_>"E)RH"E)RHϠ|)RH"E)RH"Ev/?'RH"E)RH"E_|˗ϟH"E)RH"E)RdH/?"E)RH"E)RH˗/?"E)RH"E)RHOH"E)RH"E)R@OH"E)RH"E)R?/?"E)RH"E)H"|/_> H*\ȰÇ#JHŋ3jȱǏ C:/?'RH"E)RH"E_| ˗ϟH"E)RH"E)RFC/_>"E)RH"E)RHO|)RH"E)RH"E^/?'RH"E)RH"EY_|˗ϟH"E)RH"E)RD/_>"E)RH"E)RHO|)RH"E)RH"E>/?'RH"$H"$H"$H/?$X?'p "Lp!ÆB(q"Ŋ/b̨q#ǎ? )r$ɒ&OLr%˖._Œ)s&͚6oZ ;;PK7cA7PK|%AOEBPS/img/adfns089.gifFGIF87ak4?**?***UU?UUU????**?*******?*******U*U?*U*U*U**?*****?*****?*****?***UU?UUUU*U*?U*U*U*UUUU?UUUUUUUU?UUUUU?UUUUU?UUUUU?UUU?**?***UU?UUU?????**?***UU?UUU?????**?***UU?UUU?ժժ?ժժժ???**?***UU?UUU?????***UUU,k4 H*\ȰÇ#JHŋ3jȱǏ CnO@  <0… :|1ĉ+Z1A8`AO@ DPB >QD-^ĘѠ?}ӨQF5jԨQƉiOF5jԨQF'ӧq`~4jԨQF5jԨ1?}OF5jԨQF#ӧq`~4jԨQF5jԨ1?}'p /_~ H*\ȰÇ#JHŋ3O|iԨQ~ H*\ȰÇ#JHq>} H0'p "Lp!ÆO@ DPB >QD O@ _?!D!B"D?}8`A&TaC!F8B*"ׯB}*VX>~+VXbŊ+* @,h߿O@ DPB СC:tСC:TO@ H~ׯ ǯ_~ H*\Ȱ@}:tСC:tСC H>$XC_~"D!B"DhP?$XA .dC%N> ~:t@!A /߿| H*\0_BO@ _>8P>~O@ DPB СC|:a>:th0_B:ϟ?ϡC:TC:t80~˗ϟ| Ǐ?~/_>/?~O_|:t0a> O,h|'Pׯ,h „ 2l8P?:tp>} ܷ} '0_> 3_>~?~:t0a> O,h|!ǯ_~"D!B"4B"D!Bӗ_O`>O ˧O`}"D!BCϟ>B}!B"D!B!D!B"DhПO`> |߿| |o_>~ H*T/?}'p /? (P~'p "Lp!ÆsСC7_>}'0߿~o| `>O ?sСÄ|!ǯ_~:tСB}:tСÁ˷?}/>} /_~ǯ`>/_>~˗C&G?O@ ϟ?!B"D!BO@ DPB >QD'P>} HP@ǯ_? H*\Ȱ@}:tСC:tСC|!ǯ?:tСB}:tСC:tСC|!ϟ?:tСB}:tСC:tСC7@},h߿,h „ 2l8P?:tСC:ta9t/?ϟC:tP?:tСC:ta> O,h|'P,h „ 2l8P?:tСC:ta> O,h|!ϟ?"D!B"4,h „ 2l!Ĉ'R̗П> P_?QHC}(RH"E)RT/?}~H"EQH"E)R0_B(&B}G"EO@ DPB >QD8p?$X'p@} <0…  <0… :T/_}>||`? _(߿? 4xaB 6tbD)˗E)2G?'p A׏ A8P~O@ DPB >QDG"E 1O@ 8A'p@} <0… :|1ĉ"Ec HA (P_?'p "Lp!ÆB(q"E~H"E(&ϟ?"E)RH"EG"E QL?E)RH"E "E# ?}O@ϟ?$XA .dC%N/_>)R0A$XР@~,80_?'p "Lp!ÆB(q"E~H"E(&ׯ?$X?'p "Lp!ÆB(q"E~H"E(&? 4(? 4xaB 6tbD) /+VD"B}8`A8`A&TaC!F8@~XbE*"? , <0… :|1ĉ ˗_Ŋ+"W~UXbŊ+VXQ |UX"| H? 4xaB 6tbD)V/_>-ZHП?$X~8`A&TaC!F8bEgѢE8`A'p "Lp!ÆB(q"ŊϢE 'p  'p "Lp!ÆB(q"ŊϢE 'p  'p "Lp!ÆB(q"Ŋ ϢE-ZhѢE-Zh |YhѢE-ZhѢE-08`A&TaC!F8bE1f(`>O@ DPB >QD-^ĘQ@ (? 4xaB 6tbD)VxcF'p ?$XA .dC%NXE50 H*\ȰÇ#JHŋ3j4/_|mܸqƍ7nܸqƃ˷qƍ7nܸqƍ˗ƍ7nܸqƍ7nL/ƍ7nܸqƍ7nTȯƍ7nܸqƍ7nܸqƍ7n? 4xaB 6tbD)V <0… :|P|,h „ 2l!Ĉ'R(,h „ 2l!B} B"D!B"ăA"D!"D!B"D!"D!BQ?!B"D!B`>!B"DA"D!B"D"D!B"D!B"D!B<D!B>~!B"D!B| B"D"D!B"D"D!BD!B"D!Bx0?!B"D} B0|}`8`A&T0 .\p… o… G0B [p‚ o.\p|.\p… .\xP .\80|?}ӗO|O} ܗ>/_~O |o ?'P߾|'P|| Ǐ… .… .\p… p… #߾}?~+?} _O`>߾}/@~ o}'0?'0_/| ? 4xaB[p… .\pA}.\p@߿߿(0O`O`70߿|(0O`>_O`>$XA o… .\p…[p…/|_Ǐ_>o`>맏| `>_/߿߿|/߿o@'p "Lp`> .\p… .<… .`+`>O`O`_>~#o@~_>O`>_>}/… .… .\p… p… #?}/_|G0_|˗O>~ ϟ| ܗO>~ϟ|/_>~˷O?}O`>ӗo|ӗ/?.\p|8`A&TaC"D!O> |!B0?!B"D} B"Da|Q`>!BTD!B>~!B"D!B| B"D"D!B"D"D!BD!B"D!Bx0?!B"D} B"D!B"ăA"D!"D!B"D!"D!BQ?!B"D!B`>!B"DO@ DPB >QDXbŊWbŊ+VXb|*VXbE'p "Lp!ÆB(q"Ŋ'p "Lp!Æ"P <0… :T/_>|B H*\ȰÇ#JHbC}hѢE-ZhѢE-Z-ZhѢE-ZhѢEϢE-ZhѢE-Zh|YhѢE-ZhѢE-ԗ/?-ZhѢE-ZhѢŃgѢE-ZhѢE-ZxP_|,ZhѢE-ZhѢE˗ϟE-ZhѢE-ZhA}hѢE-ZhѢE-Z-ZhѢE-ZhѢEϢE-ZhѢE-Zh>O@ DPB >QD-^ĘQ#B}۸qƍ7nܸqƍoƍ7nܸqƍ7"ԗ/7nܸqƍ7n܈P_|6nܸqƍ7nܸq#B}۸qƍ7nܸqƍoƍ7nܸqƍ7"ԗ/7nܸqƍ7nH|O@ DPB >QD-^ĘQ@? 4xaB 6tbD)VxcF'p@$XA .d0?$X H*\ȰÇ#JH!| <0… &? 4x_>$XA .dC%Nؐ|'p "Lp!Æ s_>:tСC:tСC  <0… *ϡ:tСC:tСC˗/C:t萠?}ׯC:tСC:ta|9tСC |9tСC:tСC˗ϡC:tXП>ϡC:tСC:tP_?:taAO@ _~8`A&TaC!F8bE1f$OƁO H*\Ȱ~ H*\ȰÇ#JHq>} HPO@'p "LpaA/@},h „ 2l!Ĉ'RTO,h|!!8`A&Tp`> (P?$XA .dC%NПPUX`>XbŊ+VXQ?}'p /_~ (P? ,h „  >~ 2dȐ!C 2dȐ!C !ÂC_~ !C ">~ 2dȐ!C 2dȐ!C 'p }8`A>~Ch0?"D!B!? 4xaB 6tbD)O'p ǯ@}O~'p A'p "Lp|ǐ!C 2dȐ!C 2d`1dX0_?ׯC1dȐ!C!Ǐ!C 2dȐ!C 2dȐ| cȰ`~ׯ_?cȐ!CCC 2dȐ!C 2dȐ!C7@},h߿ׯA <0…CC 2d|`>(_'p "Lp| cȰ`>ׯ_?S/_>}˗|'0CCC 2dȰ`~/à C!C 2,/?} П_~8 | O`|'p +80_ _'p "Lp!C_} _|˗?}W|?}kذaÆ P@'p ׯ@~O~'p A~7߿|'0߾|Ǐ_|o_? Wp>~ H*\Pa>o߾}`O 㷯aÆ ˗ >~k/? ` ߿}O|o߿~ P 6lPa>_|o? ˗/O kذaÆ%aÁC_~a>3`_> 70߿} P 6lP~o` /(0@'p <0…7@},h߿ׯAG~G_>_?}o> A}8`A&T~ ߿|/>~ӷO`> +O`>}o` 6l/_B6ϟ?ׯ|O?}'߾ +O`>_>o | P 6lp}'P_|/_>~˗|+O`>_|5lذa'P>} H'pׯ?$H0˗O_?'0_|/@}|/| @}8`A&TaC:tСCO~8`A ?_?8 | ,X` ,H_+8P?$XA .dP|:tC%ϡC?СCw0?9tСC:tСC%ϡC?СC CC:tСC:t0_BO@ _>8P~#H|8`A&TP`>cȐ!C 2dȐ!C 2dh0_B2,ϟ?珡|2dȐ!|ǐ!C 2dȐ!C 2d`> O,h|'P$,h „  >~ 2dȐ!C 2dȐ!C #П@'p B}`>"D!BC,h „ 2l!Ĉ'R̗П> P_?9"ECE)RH"E %b|!ϟ?H"|G"E)RH| O@ O  ϟ? ,h „  | H*\ȰÇ#JH1P g? (P_?$HP`>$XA .  <0… :T/_}>||P'p ~`? Oϟ?$H ? 4xa|!D ,h „ 2laA~"D 1O@ A'p@} <0aA}SPB *TPB /? *TPB)8p`? O@? 4xa‚PB *TPB *4/_> *TPB S0~)D?*TP|)TPB *TPB ˗ϟB *TPB*L? ϟ? *T/_> *TPB *TPBPB *TP`> ϟBOB ˗ϟB *TPB *T |)TPB * @},hP߿? ? 4hРA˗,h „ 2l!ĈOD% 'q>~'p ? 4xa‚PB *TPB *4/_> *TPB S0~ H Hԗ/? *TPB *TPA~SPB *T(0B O@ O@ D|)TPB *TPB ˗ϟB *TPB*L@,h? 4xa„PB *TPB *4/_> *TPB S0~ H A$XA ӗ/ .\p… .\P |-\p… ̷pA}[p… ˗… .\p… .\(_|.\p… 'p ? 4xaB ˗C 2dȐ!C 2/? 2dȐB$XA H*\P_|2dȐ!C 2dȐ!|1dȐ!C 'p 'p "LpB}cȐ!C 2dȐ!Cǐ!C 2TП , <0… ˗_Æ 6lذaÆ ˗_Æ 6lذaÆ 6lؐ|5lذaÆ 6lذ!|5lذaÆ 6lذaÆ ˗_Æ 6lذaÆ ˗_Æ 6lذaÆ 6lؐ|5lذaÆ 6l0a?$ <0… :|1ĉ"E)RP|  <0… :|1ĉ"E)R| O@ DPB >QăG"E)Rt`>'p "Lp!ÆB(qA}H"E):0 H*\ȰÇ#JP_|(RH"E˗/?)RH"E"E)R1_|(RH"E)Nԗ/?)RH"}H"E)R8Q_|(RH"EH"E)RHQ_|(RH"EH"E)RHQ_|(RH"E)RH"E)Nԗ/?8`A&TaC!F8bŁ H*\|5l>$XA .dC%NXQ?$XA .dhP_|6lO 6lذaÆ 6lذaÆ kذaÆ ˗_Æ װaÆ 6lذaÆ 6lPa> 6lذA}kذ?}6lذaÆ 6lذaÆ *aÆ 64/_> 6_Æ 6lذaÆ 6lذaC5lذaÆaÆkذaÆ 6lذaÆ 6l0 6lР|5l>~ 6lذaÆ 6lذaÆ װaÆ ԗ/ ǯaÆ 6lذaÆjjH!O@ DPBaÆkذaÆ 6lذaÆ 6l0?5lذaÂװa5lذaÆ `kذaÆ *Oa|5lذaÂװa5lذaÆ 0_| 5lذaÆ _|kذaÆaÆkذaÆ #O|/_># '0?˗ϟ@}5lذaÆ 3O|O@ DPBǐ!C1dȐ!C #߾}?~+`'0?}O`>~ 2dȐ!Â`>'p "Lpa|O@ DHP &L0a„ 8_oO|O`>@~'p "Lp!C 0 <0… O@ DHP &L0a„ /|_ O`>0@O@ DPB #`> 4xaB  <>~ &L0a„ #}o` +_>>~/a„ &L0a| O@ H*\ȰÄ=|ÇO_|˗/_̗/_>}'0|ӗ/?>||˗/_>>|Ä=|Ç>|Ç p_|>|Ç {Ç>|Ço?}>|Ç {Ç>|ÇÇ>|>~>|Ç>|C=|Ç"Ç>|Ç>|h0>|C=|Ç>|ÇP> H*\ȰÇ"D!B"D"D!BD!B"D!Bx0?!B"D} B"D!B"ăA"D!"D!B"D!"D!BQ>$XA .dC%NX1?$XA .dC8`A&TaC Ç>T? 4xaB 6tbD)Vl/_>-ZhѢE-ZhѢEϢE-ZhѢE-Zh|YhѢE-ZhѢE-ԗ/?-ZhѢE-ZhѢŃgѢE-Z_?~0ӷ?8`A&TaC!*ԗ/#F1bĈO|ӧ`> <0… :||E1bĈ#F߾|ǯO_|1bĈ#FO_|"F1bĈ#F,O_|?~˷_Ĉ#F|E1bĈ#F(П|1a?~1bĈ#Fl/_>#F1bĈ#˗/ۗ/߾#F1B}1bĈ#Fq_|"F8p_|"F1bĈ /bĈ#F1~1A1bĈ#"ԗ/#FQDE@O@ DPA~-\p… .\|-\p… .\p… ӷp… ˧o… .\p…o… .\p… .\/… .T/ .\p… ԗ/ .\p… .\pa>~ .\pa}.\p… .\(P_|.\p… .\pB[p… p… .\pBp… .\p… *ԗo… ./_? .\p… ./_> .\p… .\h_~ H*\/_? .\p… "08`A&TaC!FD/>%./_?$J(QD 8p,h „ 2l!Ĉ˷OD ˧ϟ?%J(QB~ (? 4xaB 6tbĄ˷ODӷO?$J(Qą ,h „ 2l!Ĉ /_>%J$/>OD%J\`>8`A&TaC!FTO@(Q~o>I(QD (,h „ 2l!Ĉ #/_>}$Jt/_>}O?$J(QD˗OD%J(Q@~ ˗/?%"/_}(p H*\Ȱ"D!B"D!/_|AX?}П <0… :|_|!B"D!oa}˧_?O_|O@ <0… :|(P_?!B"D _?~(P>~OO|? ܧo O@ DPB >QD'p "LX> o>$XP>}O@ 7?O@ DPB >QDO@ D ?} *O@ H? $O@ ? 4xaB 6tbD)VbŃUh? $O@ ? 4xaB 6tbD)VbŃUh? $O@ ? 4xaB 6tbD)VbŃUh? $O@ ? 4xaB 6tbD)VbŃUh? $O@ ? 4xaB 6tbD)VbŃUh? $O@ ? 4xaB 6tbD)VbŃUh? $O@ ? 4xaB 6tbD)VbŃUh? $O@ ? 4xaB 6tbD)VbŃUh? $O@ ? 4xaB 6s(0C:tp`>:9tСC СÃ9t @,Hp ,h „ 2l/|˷O>} @~/_~߿}o_>~:tС|:tx>O@ 7?O@ DPB |/_/?~O| ̷o?/_Æ 6l05T_B~ܗ/>װ@,8p ,h „ 2l߿~__'0|O` 6lؐa> ˗|;O_C ܗ߿| O@7?O@ DPB (߿|/߿/߿|O?~/߿8`A&T!|O@$XР@ @ 8p/߾7P}/_>~П ۧo  <0… 7P_>_ ̗?O߾ '0_Æ 6l080@,hP |?~8`̗_}߾|/?O@7?O@ DPB O߿|O_}ϟ|˗O>~ϟ| '0_Æ 6l0'p "?~8`̗_> />'߿| H}'P>~ H*\Ȱ|9tСC: ?$XA? /?O`|˧/_|#? O@ ? 4xaB 6<`>:tСC08`A80߾ O}_}O~_>$Xp>}(P?$XA .dC%NX1(? 4(`>'P ?}8p?O>}?}O_|? O@ ? 4xaB 6tbD)VO?}(0_|䧯b| 8`A O@}8`A&TaC!F8b|ӗ|!䧯"|'p @'p "Lp!ÆB(q"Ŋ9WQ`?Uh? $O@ ? 4xaB 6tbD)VbŃUh? $O@ ? 4xaB 6tbD)VbŃUh? $O@ ? 4xaB 6tbD)VbŃMܗ/_>~'p @'p "Lp!ÆB(q"ŊUx>/8`A O@}8`A&TaC!F8b|8`A&$OB ߾| /߾|/_>~П ۧo O@ DPB >QD'p "LX>˷߿|_>߿|'p  <0… :|1ĉ+Zؐ˗_>}o`~˧O`|'p  <0… :|1ĉ+Zؐ> ̗/|o}˧/_|O@7? 4xaB 6tbD)Vx!?}_>} _O?П ܧO ,h „ 2l!Ĉ'RhC~&˗/߾O`> /_>~'p  <0… :|1ĉ+Z萟>'p  <0… :|1ĉ+Z>'p O@ DPB >QD-^OF8`'p "Lp!ÆB(q"Ŋ/B# H}8`A&TaC!F8bE!Ӈ?$Xp>} H*\ȰÇ#JHŋÈ@,8p>$XA .dC%NXEa? 'p>$XA .dC%NXEa? 'p>$XA .dC%NXEa? '@}8`A&TaC!F8bE##@O?'p "Lp!ÆB(q"Ŋ/F̗#F$( <0… :|1ĉ+Z_>}/'p Ao,h „ 2l!Ĉ'Rh~aП'_} H*\ȰÇ#JHŋ#E~O@ DPB >QD-^Ĩ/~%?߾|aĈ#F1bĈ~?~ۇ#F1bĈ#F˗o? ?~#F1bĈ#F-ۗ/_>}aL_?~˷F1bĈ#F1b?}˧_?O_|#F1bĈ#F׏>} O@~O>,h „ 2l!Ĉ'Rh"ƌׯ}0ӷ?8`A&TaC!F8bE1fdП O@ DPB >QD-^ĘQF=~RH%MDRJ-] ;;PK1wFFPK|%AOEBPS/img/adfns104.gifCOGIF89aIII333E=;_G|||zzz'''tttzt,,,JEFMJKXXX<<>>822!, H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ jh<4GYtʑЫXjݺ^HSգtzyݻxiSQ/ q]̸{ CYSKtH( ϠC?0̒0먜G˞M;(#A Isȓ")`\2k߂ fy۽8_ϾX˟O?Ͽ,:`I%LB<Bd.h'5xN_(bGVH~8,ZC z}1hxBH3W#@Y<ޏA&"BQ!QOB)%\*}Nt?[vi~;8#pbYfxvËꝀCYyg"Ǥ{'dhw%2 qaI8À1:(f%FDתCxQ+{Bd'&*î&D;˲@W넧*Ő,zk?BJ)8,itD{@F0*&M3?Ws$j+Fv2aо1@{ИܣA7[2{ XguZ`dԲ?'NCNpttdza\CG;cu:0mǍÐ@ SAsDs^EAw>99袏κ 륷? S{l=cB@xLx.<4OK?;}Co@s?<)ݐOS}g?=зkMLc*Rx`3J%lp CxDD! G@XV1P~\ u 2@4pH*0 k XȆ"0q0!pbXxƦ>Ј8|0¥*j?*dlDػz(92ĊDŽȂ%xN$ I!X9F-rDII!RPp8ID:Ў)bCRf+J4QeLpG ("oELajA| k\K3愍S"KHc[@"c&B)Q9`'!YrvҜs<ӈC[apV*JT@t0+t:OVF0(r`LA%g]5D #D,LٴFzH`jA0!DN+ޏ@{-¬" dgI,7#rEVݙzf&gl-ewrR+"*Ȼn5匜 ?q啓L)k-Fq7&NՖ掤 L;qivgyϗs] hG +.4D3ъ T: tV,bz&ܥ?ݑPmԤވTY=xٛ]U=kL VHPZ 0 B]@ʲ|1 P8F末M'r[O*:VF;$&A쵞2xJ NpX :xĩ푁_졂 S'e(V0xC S=v=0&0' fPjO9ҷ|a t|r]o#X I4"'Y9-vwoSm2hxL-;y]y%y=_w2kcz  vM/ @{=lsĩat=O/;_Cl)ŏ},(H r,%:?$*y h~E?#\R=O6WB^jYppX1XyӢ AA2ْVF2Vc3(w<Ňt~0Հ.G/wWGVOtC24l50إ"{&iU-GCx#(*0l,E0E)m_+>68XX]za4M8opu.m'Qx24qWM{"[8'/c}B'/2MXG~7bN SpC]/13LYX3"Pv-@qOS UMD$;~:OXqd#g|pn5[8tC1p $/|s7ŷc1%P5+v8So6x*oM0Lz'(0EtC.x.[QQH/;9vH "9!X7*cRȒ04Պ8ڥX@VR!%QmR9TYZΧ)\ٕb^92Qh 0I)(rlR|L8(-1fEYyZEV[I&S`aeP/1GF>1~/M_l)XІp Pp.ڂ~A@ M9Y)y2wY[}A,^kCT~-)A)"h`bN&8mcxHЏIٟќጃUxTiFxp Aa1H"ڙ4)з:١̩8!2d  o1vd5r2hM)r4:If7) 顨lG.88"T3`Cזe5Ru]~ZK @kuK)* ' +͇bvRDfh(ERU7?4#-%& !j R8,V3'tTBVy0Z]GƯA3cVե1( ;6K1XE-A^AQsT-Ҷbo/' ` 0` Ѐ `;0q1`ƣv`1<1eeYHu+ y<)7TH V,ۯẆӜ;~R٥k&2|LԺ 3)[5Ƚыia×#*} ~iI(CGfJh- 2twuL-qyJ9}]V)+*rܸ$ *'lV+{`5Š F2$5ql \= ̧!֙ZؐO ;7!@؞v"w 0 ` pŕuQ s$$v@:ۏ? MlM-}")1ϡ}r`Nj `׍Rs/yҼ1!qؑ]+z.  !uAګگ۳]TھQ(Ɖ ^1R:1*Pܐ╚7ᬎsHJ~j)7& @4gMJ̻b>lúh~ u-(nV>a a+/!,y3&r^ tm\同d|xfpL Dێx! ؞Nؠ p艞ؤ~?>XZ@HjwIpv > yݨE{Ҟ@6X^5v'{ߞҀ:0 {` ^zUz./3 bp`Ҽ> rlMI O>_46*b^0 NkÑ`*.i1Z ?[Xq>C^k wK?N x]@+oX!KWa#D_D9LpY|,_  . ~.H,uG&oy  d,p \u@U/ ?b޽9 A_om.WA .d!%2&>8cB Klqbϗ/Dqʘ;.Hϧ<$"I*5j4QBg@ҤÅK,Ytb$I=:ed[t-h'Ayw/A\ &\aĉ#D-^RP @RI"SI$J,]”I1޼FQw$J,}kZz+Yj3r;Ϲy+d}o_V\< $-PCoC]G#gMsd~TkQ?mava=*r~ VJ$LvdKhV/}Bl5آ2Hf%'Z05Y5 |7Py 5QEuRI)}_Lt`|Uj#:ulfc% -j,|f{@^M_+ 9fd ;EwdtAG叐*$%- ;';BK%S$qg )#@7g?g`MmHT8B*ӑAHIiX_tQ!AAgnUpUK Q`NV?*BL*4>m״0lVnBDSi̝1YB́+$4K0au(!ۊmsݞ/]#CUO1 _88k2dD-]$"#E6;"iI % OgM=aYG6i -HA:W ѯ?NS 0I}Qs_&a)r8"e~i$u'hٜW(0Q"Z6-O0J.=$[=SmR{!0] @\ e6es 1ˀSg̏S cNij ?\f$8J` B6IFxj>T{pDMb5j:])o~1KǑ]3wف S:A=?6#}?F.7!Qe# Of5HDbfm <(5p @`:ML#Y թ[/UN?kLM?Xq4 X D4 64rX!E89l/ t%."@f5 M<B=yЇ^Gݺ3{^ތWPk}"C,}(A a0611s38"ιJvO]v ur0 Qu {yÃ?Z.K/RX >H ];V  ok@9_`" h&15LSӼ3?@?Zs.?\C8;?A06 p;%0_@PAZ2{ {%(…A*0ȁhWH+'@`,`BV0&Y3Ï7(dAcDD@ÑQ3.HΙN*)(\pz1*L D\ĉA+GHl2E` Co*CL$ 3/_ez3:#K74YhE( X:C".lIdAb| c2ѐ .U4E>"ZZZZ"/UP@ -V`'X[C@T A\FpO(GIp;JM+`)SՀE\aDܿ@lX PH C[;c+=#\?h@VLY4կx}ˍZ|ZнLMflVЃ}2X),=X(E()RF;N]2Z\(+_썋ICE:hZ(Ւ/4pY_._#۩6@^+[F`k ?a Ta] IЂ3γz? H拎 50 \u\/\b 3 B\aªtTș|PA&+v -b2 '01fR~䪽T 9׆B.ŕ?꽺Bd9d5VdG8ǭB-d㥄9LfM6%dI dW՛RAeUj޳I fܴ;8zbAKmfnl^Le`冀Ub _~+):HN\^bH)K%02Hc@u.b/tNBic$~cFhvE|^3~#PP.i>R%qNՖfgh$6NnggXn:~EX0=jtDAsΘriQj똢%ݜv8vfI6E8(jHj.k-n0#ꃰ[a#&J~䞞{4>x0kN_e1lj ~ fdve9~B l[C)mĎ\\1~`mCU7鬾H84 6(-( ̗vlMmoxЫfm{vo0n5ȃkfxbnwnfh&kΚPo+ o^?vk.@(HP}fVl\afXdyhg5"Z?0'^@qqqq$0,0 &^XegG~TXU2i*s2/s3/s]*! g$_^&k'ch~1C؄+,_ f"w.7?@\@6WC7&쭖j=4+/>0-ǎ.K'[GH#q`vz_0%hm@A wb.t `urWX "Ȅ+ȁKE%v674 ( 3w}W]`ie/W/PR^ P8sMw.x 8 @:a'5 /8:d z׋2hoyy7U"?nohJ1EHz #h5ckߔy!`A0#T@ }FneH< ȅche%8j8 "^>pޮp 7 58;:;4,)^K;#5(7ȃ>0"×_hB8_8 AА"R7fOOB''?Ww꿣~TWO!!pTy~b~w]vc獱 Og „ 2l!ăh"ƌ7rH"'b8ǔ* \eDa&–5w g|L"F>SE.a5@Trݩc$0Ӯ.ӪU+mMiwoܹ!vwJ!\wx\=]n>򊒱TS?k_x/d^l%>,>N4﯂ 8 T?FfX jQ@]Pv.K=!FOdbաעE"Lȑ"O@l"UPa/"鐌-XQH;r$DP1AddŒ)BMz$/Q9PEUEhhx߅G'UkHJ&YXAxvGj?aRYŤxj蘚)5 DY>g _^*i膲"G+|ޘZRNjH{Qtabl:d ;Ket C(UR8n|ꗮCBP6Qi meԕ+^F;pw B(")f\"xQ\93=3>cE\?A+40wDZvNY?YFE>avnPVqdWV_-6ՕQ`-ak_6SlF88N?Ll0K>ںd} kL"U_oi~:Ĉ oʬo"a@?9㵺vT8RyYqd' e];.H"9]!>O!`őUH݅|҇.(]J6b@}>B ږ'X 9l+7r U xQ %2?Mn`leң֧**4 )x#U2d 5}X4EAp0B@E RqFR-If "\8(F:۟A#}O: !GBۉt=j;Nԙx!DQX bG0jERRl>`]JWd2=_G$T @YUjj(1j &i?LYL&*2IVB#۩pS2X Q 2EL xNvkfXO2}]DD| Z(BRL'^J-]"_Q,hDTͮ.6 -)64(e8HT}7 L#БaZH?3H{&Q$3mKMG"Jn4*s 3&A4fYdցGEC5U] ּ>57Dj\H{?=z+XDMdq V R b'Yv CJ ԂjV4Ck{l-@ BځXvV] %`ƥH-zluY늒uSxs1`Te^')S;I!B)oU}Pq~0#, S0whb`nU eXx{`($X 5$61mLF)R7!0܌g:xqiܓڎIבd !ɢh!v|q'*Kn?ψy'R2G,c#r%d6.NqiDZY| D\# ,xvʄs Mmf3lӻ qrC:l+))%)ZBUJHå?5AƂІ?Q^̀*}M@Y|y] ^JeD]ybm~ACZ]ƐRnu f&!<1A\Q܆]A a l<_B) BR=YH~!tB~A`rD*l)Ѵ 6Ta?PAmR_Y_b$|F衣_@XtvEѠ Y0A) 6u"?'j"bEL,n$K D߼1At"]`[[h./Z/^D7nDdE$$B#:6D!T"NKā0v7@8Z8f,Ï:2 ;G%%7cc@F@b*ӪZHHc _M5LJt І=B?X ?dEP,݂۰M]a\Qv %L,dC̚E4gJ E<#FXeX4EuI٣^&pR">Zb^z$*h(*"㥄h$iMl\1[%*y_ѝl(^m"eb5d>YlAS '&$F(&\0a,ZZń!2̝㨥I褍$dBDA%vQ,S܈\{Cfߋ"#ҡR4z4&mԤI"qBdra&j'`u 4*_j'!\QHh$dV!4FTl"|B){e!|є` B_miVF$h$Uѥݝ&E!\z%G fdf4BȩB,9*))bjjGD'4Kj(2JlEBĆֈ*`BޠA4_)A &Ht Fh[ (W8&?\H>eh"9^V酶8J*kB C3:RT"+dgꁲxdLWڛCpfhV)F-UF+LFH-F-Hf+BJ0lݤ;"?\gK棇 @j$(/E+$30(xE( 38-X,܂$Ǝ['ż>."0Mm6F-:-Cm2:Dåm]0$!>elnREG:DMRZim-Y-CmφښBZ2ٚmm{i[px&lfgnjriATcg*f*~aFoT,>A=T/B A  <9HAHB [0fo mxt/nbn)CMܐ<]SZY`gY&l1"Hoo0H{P |0 |!-@|Al&|0*Ђ   w s~.ʄ K[AA>ΆFjh@<qg6Ρ^?4ۑ"P@?/P؎U/&,: "4A0NҠ WhBA-2+ߒ9D8rCrBH25,w*Qp2ѝr*_,r}aX%q(ǩ'b&-n.n򲣀b9ȒYs1(j@j)IF3?@F@<[;7M7#j:1=4@38󧃹26k=hTƱv1|S_@6_*FMa*b5g (,-GtJ#+GX)sJO Ik;40Gt2C__5``6an tw.!D`\"23MN"DcGؖfi5!%HHġ4]CXZ74nS@Hq xq7r'r/7s7s37쳑J* V_,C(A1e:,d+.D\f5nKEky+y3^lo|eD|0b]EwFB0"^@byklk7n7Z5^^[~8KougZ>.8w&[#DxzCP-(y[85 |;^#}Cء5>_Ĉ8G9 ˯+qL瑧?X#)\nE@ $dHRw8yB'Ȍ9: Z{'ĕ#/zc/89~y*vM49h"2Ay$3Nc$%:w[ߣB48z3NQB"t7%,9C,9qwl-}o +7GD]4zz먐PC+vY;/#)3v Dl_CPq]D*\cD4j;GՑDl|{GSsD@Ftx/n^ˎY|#wq4w#>=F`^}$\>Ӂ8F >GnD8$?'/?7@O?W[QiW }~g)@83>sA:C~?jﳙ?D?A\4xaB &bD#xc9Jt1cG#I舎&Ut{C0$\pΤt'>0ziRK6u*4zuavpD!'@vcqu N=ԔU=Hz[@)dHbŋ7vrdɓ@,lW `#8H[xBjٳI qwnݹiwxpI~ryr˙7W.AөWwN-p'-mDxx;\$շ_MܽO=vx0AbCe0 U#N]KqDQq5i'."= 6?1@Ū-r5#- F)A˷ڸ6RN,F|PT"6{c>RLԤ;݊&|؂ 0|]39>~c!+0Y#!?l"(x^ qWOha668hl&0't&) ڟ$ab] ,mwV#(4DY |`,Ѻƴr2n6b;UW*~O!"iп/}=ݡtS/H'\ (k# %jM'ڰﲐfkk=|l*ҲDn#bJ)X=a ³??1Ȕ # Ir@9?1K@yWπ|K}Bd`XX ~XbC@nn44a$M5Q  ,F71eZ0-oTpXMi`y+ ^j-rTp8 bVἀ$lP\#D̊<"GjU?rfOSI`: HF%,6`HZ.,G,סr-NxIDG8YNr"CiNwF paR~FGԊgaLzFGf&c B 2II `]dY?*F褮b}@B#9݁ECBo0= 8$lQSe<%[$(1nP)"VShy% T I`&3_\OJ!#в,؟^$6A)U, numRX/  %qt9eSfVU%R'Ni뀜*u FFK!&[ EJ#m[[DOp[]ĈT@Tjʹ#tEҵrUrFke+Zȼ/Ղ^ ێВ~XPw*/ qx'Zl_„QM)˰[0!,+Νy'⠌-STސ\d#EfTJ2kcHX"F@?LpG@Q 3D^+c&1ˢ, TVs,l^Oް"w-Q|37P W\ T(s|>,bPC bp̂+"[x(<w,Ywu@2!]Īpsgc:θsiФ.oq5v}"!V#ENނ5&©,48Jcë.ג/j&B*I5HK!8 3-f ,EDU@ BTL$   C(\@" 0"0" yBpp6@2*EF0"lcOlCPd$1"v`O bP"@qpXVa 2q[+LB?n""$p=PQiC,*` r(P p `C?LAt *nlmHp0-n4v@ x2JgK` 5X$,brq $%MHh\DZ115r1#=r#A##I2$M# "2T,@}O߿o`'ۗ/~'p`o@~߿8|'p "Lp!ÆBP|GP> //}7P | o_| 0 }O O@ ? 4xaB O|?}@}߾|߿O@O ߿8|'p "Lp!ÆBP|Gp'߿_>O #(?}_?'P߿'p | <0… G0_/_|O`|o|#/>#/|˗~/_~;/_| 6lذaÆ 6l0_> /|/_~/|˗/} /|_|˗߿|/|˧aÆ 6/|˗O |70_>}'0_ 䗏|o ?~#/߿}O߿|/A~aÆ 6lPC 5C߿O/ϟO_> ?~O /_O?'?/>$XA .d(0_> ߿뗏߿ Է߿/ O@̗_|/_>˗@}? 4xaB 6tbćO|/~'_>~O}O_>O ̗`|#(0__|'p`>$XA .d80_> ߿?'߿ O ̗`|#(0_ӗ_|'0_>$XA .dCG`>'|'? O@̗_|/_˗@? 4xaB  ̗`||O O@0߿O`'@8,h „ 2l!Ĉ#/} 0 /> /_>8?߿ ?? /> /@'p "Lp!C̗o |O@~ ߾|'߿ϟ| @ 08`A&TaC!Ft/|˧O>Oo߿o@~?'߿'߿O'p`| <0… G0_/@ ̗o`|'pO߿O O|˗? 4xaB 6tbD̗o |7_|/_> ?'🿁'߿8`>O@ DPB#/~Ǐ`|7_|˗/߾#/?˧_|7|/@~䗏|5lذaÆ 6lذ|7_|G0_> /@˷O |?}W0_> /_|7_|8,h „ 2l!Ĉ'Rh"ƌ7r#Ȑ"G,i$ʔ*Wl%̘2gҬi&Μ:-ӷsΝ;wd/_;w"̷0|UoΝ1哘a>3 ?~;woa,۹s'~m1|iP'0_|˧o_'0_| >~;w|/?'p_|>'0@}˗/?~_>} ܗ/?Y0@}_˷_'0~߿~8`A&TaC!F8|/߾}_>~| 7_>}O`}߾|+V(1~o`> '0|ۗO`70_Ŋ+VXbŊO?/߿| o_>/~O`|˧O`|UXbE/| ̗O`>o| ܧO*VXbŊ+Vt/?˷/|o`>'0|ӗ|O`|˧/_|O@ DPB >/|o`>~O`>/|O|!B"D!Bb|'0_>}/|O`>o` _}O~"D!.| 7P| 'P_>˷O|oD!B"D!B/߾˧ϟ?}o`>'0|˗/?}O_|O_|"D!B\DD!B" " O@ DPa|.\p… .\(_ ̗o`?} .\p… .\p… .\x0 .\8 .\p… p‚[p… .\p… .\pB-\p8`A&TaC!F8bE1fԸcGA9dI'QTeK/aƔ9fM7qԹgO?:hQG&UiSOF:jUWfպ5%@,H? 4xaB 6tbD)VxcF9v4`>  <0… :|1ĉ+Z1ƍ;!|=>P'p 'p "Lp!ÆBh? 4xaB 6tbD{/ĉ'*Q|,h „ 2l!Ĉ8`A&TaC!FhPM8qB}'p "Lp!ÆBh? 4xaB 6tbD{/ĉ'*Q|,h „ 2l!Ĉ8`A˗@ o@7P_~߿7߿߿O@o@}߿8`A&4‚)TPB ԧ>$XA .dC 'p _>~| o |70|O |'p?~˗o`~;x } >~o` _> ,h~`>/7`>߿~׏|'p|GP`/,h „SX_> *TPB0 <0… :|1A$XРA/ 70@~'P/?/߿߿/߿߿?}'p "LhP?S0B*DOB} O@ DPB > ,h`?}|o?}߿|7P ䷏`O?}/_˧|O@ DР>~ ˧0a}/?'@}#߿|/_>/?ԧ>`> 4xaBS(_)TPA}P!A$XA .dC%!|O_>_>/A~O߾}|IQ|0 <0)/?*T?S ,h „ 2l!Ĉ _'߿| '0|/߿|o`>}$ꋨ`>߿~O࿁@?_>~?}o~>~߿~/_ ?'~ 8p'p /_|˗O?'P|߿_~@ O@߾? 4xaB[H_ O@߿|/O?~o`>~8p@}8P>O?}O/_70߿_|_>O`7p㗯_|ׯ|O߾~/߿}`> 4x0,h`>߿|_>+`> ߿|+`>/| #߾~? +Ϡ> g0~3_Ǐ`>3_W0_|o|~ǯӗ߿| o`?}O_~˷o_~/_?菟@ӗ?ۗ'p O@ 7?}3_맏`>3_W0_A _7P8` 4hРA48_> ,/_| 4hРA 3h>/>ӧ߿_?@/?߿~ϟ@O_?'P߿~ O|,h ,h` O_?W`>/߿߿/߿O|8>80/_>$XAKh_ &L0a„%D`> 4x! &L0a„ &Lh? 4x0߿~/?`|`O_˗`ӷo`>/_| 4x! &L0a„ &Lh? 4x0@~'߾|/_|O| /_>}/_|`>/_ |8?}  <>~ ˗0aBK0@ӇP_B8`A&TaC!F4 <0… :|P|O@  Ϡ4h_>ӗo}/_~_| ߾|_>~ȏ|'p 8`A&TaC!F< <0… :|xp|,h@~ H@} /A_~۷|O` W0?~|3h>> ߿~O@ DPB > <0… :|x|,h@~ H@} /A_ G_>'_| ߾~W_~3h>A '_?8`A&TaC'p ˗/? @߿/`>߿߿|7P?$X~ ? $/_>~ H@} /A_ >~ `>_/߿Ǐ߿$H?} ?}O} O?/ ߿~O`'p "Lp!Æ8`A/ O|70_|/߾O`>_'0|OGp#O珟| ?'p "Lp!Æ8`Ao`>'0@'0_|/߿}o| 7pO |/߿ ? 4xaB>~ ˧P!| *TP?} O|/_?| O ?'p "Lp!Æ8`A/߿| O`> _> '(߿|߿|? 4xaBoA}o… .\PB80߿/?(P_|/_?˗O?'_>}O@ DPB 'p  7_ O|߿|`|o`?}w ,h |0@߿|߿70(po`>}O@8`A&T/_>~ 㧰 | *W0B˧PaA} O@ DPB > ,h „ 2l!Ĉ/>~?}+o_}ۗ/?/? ӗOA}'p "Lp!ÆBh? 4xaB 6tbāQI_>~߾}߾_xP_D} H*\ȰÇ#O@ DPB >q|E!|O`/| />I$XA .dC 'p "L/B̧_>̗B ˗ƒ),/„`70߿|Ӈ0B)<`> 4xaB 6tbD H˧PA~"G0˧P>~ ˧0a+?~ӷ|_̧PaA} O@ DPB > ,h>~O | ܗ_/_>?~ӗ ?W0߾|/_>_ԧ//w?~,_> ˗o|?}O` ߿|o_}~~7p`>(0|_7p| H!!}˧O_|˗@~w0| WP_|O_|ӗ/_3 ˗;x|~ӨQF5jԨQF5j$/_>~{/F5jԨQF5jԨQ#A񋨏C~4jԨQF5jԨQF ˗_D}˧QF5jԨQF5jH_|"_>5jԨQF5jԨQFQiԨQF5jԨQF5/=OF5jԨQF5jԨ|E!|5jԨQF5jԨQF/>~ӨQFiFiFiF/_>~ H@} /,h „ 2l!Ĉ'Rh"ƌ7rt/_>~{/_ǎ;vرcǎ;v_|"_;vرcǎ;vѡ|E!|;vرcǎ;vرC񋨏C~:vرcǎ;vرcGQuرcǎ;vرcǎ/>~رcǎ;vرcǎ˗_D}ױcǎ;vرcǎ;:/=䗯cǎ;vرcǎ;vt/_>~{/_ǎ;vرcǎ;v|'p 3hp | H*\ȰÇ#JHŋ8@$XA .dC>~"D!B"D!BP@~ <0… :|h_| !|!B"D!B"D"D!BL/_>~_>!Ba?$XA .dC 'p 'p "Lp!ÆB/_>~{/_Ĉ#F(P|,h „ 2l!Ĉ8`8`A&TaC!/=/bĈ#F`> 4xaB 6tbD H@} H*\ȰÇ˗_D}1bĈ# 0 <0… :|1A$Xp>$XA .dC/>~1bĈO@ DPB > ,8P,h „ 2l!QE1bĈ'p`>}O? $H A G A $H?$XA  O@O@ DPB >_|"_#FQ>ϟ@O@ DH &L0a„ "O@O@ DPB >_|"_#FQ>o@}߿}O?~˷?|'߿߾|'߾/ /ϟ0 <0aA$Xp> gРA g0A 3h0A 48_| ,AO@ DPB O_>~˧|O/_~ /_ >~/|ۗ?$XA 'p 4h`>4hРA4X0_| 38_> /? g@~8`A&TaÆ8߿'}o|ϟ7?7p? o߿}o` 0 <0aA$Xp> 'P|/@~/GP?~>ǏA}O߾| ۗo|O |_3h@3h>~ ? 4xaB 6l`>'P?}O~ 7_߿7oO~o` 0 <0aA$Xp> '0__ '0?~#߿}g_>/_|/߾}o?Ϡ| /_>~ Ϡ'p "Lp!Æ 'p'>˷/_o~O࿁ 珟@8~O7P?O@ D ,8Po`|O` G0|߾|`+o` /@gP`> /? g@~8`A&TaÆ80>'>ϟ~'?}O GP࿁O G0?O@ D ,8Po`|맏|O@_/~O /|_|G_>~G A˗A Ǐ AO@ DPB O@ 4O"D!B"D!B$Xp>O`>ӷ|/||/@}|'0_|/}o|| /_>~ Ϡ'p "Lp!Æ 'p ?}"D!B"D!B$Xp>ӧO`>}/>}˧Oӗ/?ӗ/_?Ϡ|'0_A}ϟ|/_~ g_> /? g@~8`A&TaÆ8`A&TaC!F4 g| H*\ȰÇ?}"D!"0 <0… :|1A$Xp>O@ DPB >/_>~_>!B>$XA .dC 'p 4h| ϠA4h@~4hРAg`A} /,h „ 2lP|,h B Kh_„ &L0a„8` 4hp`> gP`>4hР@~ 4hРg`A} /,h „ 2lP|,h B Kh_„ &L0a„8` Ǐ| /_?W0|˷O| /_/_>㗯>~>ǏA}O߾| 4hp|4XP? <0… 60O  ?~ ?O?7߿˧ϟ>O` ?O?_0 <0@$Xp>O  o߾'0|o?~'0|߾}+߾}Ǐ߿|3/|ϠA˗A3hp | H*\ȰaC} >}O?/?_>~/ۗO߿~_7?~_߾|'p "L(? |ȏ| W0| ̗o`> /?`o_}3/| gРAϠ48_>$XA .dذ>?}O?ϟ_/߿ Aӗ?'߿ o߿}/߿8`A&P O@_O| 70_Ǐ_>'P|@O`>~7p|O`>$XРA;xp>~_|' ߿_/߿o߿7O_'p "L(? |_>o? /|ۧo?_>'?}W0?}'P_3/| gРAϠ48_>$XA .dذ>OO?菟@O߿~_~/?~߿?~O}㗯?'p "L(? |˗?~ϟ|ܗO>ϟ|/>}O_|ӗOӗ/_?Ϡ|'0A /? g@~8`A&TaÆ8P_? '@}7P?O`˧߿˧߿?}/>(߿ o_`> 4xaB H@} 4h| H*\ȰAs8P?sСC0 <0B.\p… 'p 4hРA'p "Lp!Æ@}ϡC:thP|,h „ ۷p… .\X? ? 4xaB 6t?񋨏C~"F1bD8`A&TaC!F4 gp` g ?$XA .dР|99tСC O@ DPB > ,8P 3h0'p "Lp!Æ@}ϡC:thP|,h „ 2l!Ĉ8` ̷/>˗ϟ@~~ <0… /?s_>:tСA} Hp?$XA .dC 8` ߾}߾_  <0… /?s_>:tСA} Hp?$XA .dC 8` 70߿|o3(0,h „ 2lh_|?~:tСC'p`>~߿oۗo? ?0 <0… :, gp`70߿|gP`>$XA .dР|99tСC O@~/߿~/_>?}O8`A&TaC8` }_ /߿}  <0… /?s_>:tСA} @}O>~ 7_O`> 4xaB 6tX? @}ϟ|/_~ g_>$XA .dР|99tСC O O?' ˷߿8`A&TaC8`8`A&TaC!/=/bĈ#F`>ӗ| '_}/?}珟@|,h „ 2l ,8P,h „ 2l!QE1bĈ'p>OO࿁߿|/>0 <0… :$  <0… :||E!|#F1@} H !B"D!B"D(? ? 4xaB 6t?񋨏C~"F1bD8`A ϟ>"D!B"D@$Xp>$XA .dC/>~1bĈO@ DPB > ,8P,h „ 2l!QE1bĈ'p "Lp!ÆBh? P'p "Lp!Æ/?{/D!BP|,h „ 2l!Ĉ8`A&TaC!F/_>~{/D%Jt`> 4xaB 6tbD H*\ȰÇ#/=OD%: <0… :|1A$XA .dC˗_D}'QD%J(QD%J(QD%J4/_>~{/D%J(QD%J(QD%J(Ѡ|E!|%J(QD%J(QD%J(QDQI(QD%J(QD%J(QD ˗_D}'QD%J(QD%J(QD%J4/_>~{/D%J(QD%J(QD%J(Ѡ|E!|%J(QD%J(QD%J(QD/?$XP>~ ? 4xaB 6tbD)VxcF9:/=䗯cǎ;vرcǎ;vt/_>~{/_ǎ;vرcǎ;v_|"_;vرcǎ;vѡ|E!|;vرcǎ;vرC񋨏C~:vرcǎ;vرcGQuرcǎ;vرcǎ/>~رcǎ;vرcǎ˗_D}ױcǎ;vرcǎ;:/=䗯cǎ;vرcǎ;vt_|8`A48_>$XA .dC%NXE5n_|"_;vرcǎ;vѡ|E!|;vرcǎ;vرC񋨏C~:vرcǎ;vرcGQuرcǎ;vرcǎ/>~cǎ;vرcǎ˗_D}ױcLj8`A&TaC!F4 <0… :|1@񋨏C~$J(QC} H*\ȰÇ#O@ DPB >q|E!|%J(ѡ>$XA .dC 'p "Lp!ÆB8_|"_>%JP|,h „ 2l!Ĉ8`A&TaC!F/_>~{/D%Jt`> 4xaB 6tbD H*\ȰÇ#/=OD%:0 <0… :|1A$XA .dC˗_D}'Qć HP ,hP>$XA .dC 'p "Lp!ÆB8_|"_>%>O@'p O@ DPB > ,h „ 2l!Ĉ/>~(QD$0 <0… :|1A$XA .dC˗_D}'QDI,`> 4xaB 6tbD H*\ȰÇ#/=ODXP|߿~$HP> $H A#(|,h „ 2 <0… :|1@C~$J(>'p7pO@ D8 &L0a„8`A&TaC!F/_>~{/D%"'>o@}_~߿}/_ ?o  _|`> 4xaB 'p "Lp!ÆB8_|"_>%JDObA} >}O?O?~_?~O?}O /߿~O8`A&T ,h „ 2l!Ĉ/>~(QD$0 o~ϟ'߿?}/?'_0 <0…8`A&TaC!F/_>~{/D%"'>O@}' ߿'߿߿_ O||,h „ O@ DPB >q|E!|%JPĂ8p?O?~/߾ O}~/~O_?'>$XA .< <0… :|1@C~$'QCXP|߿~߿ϟ@?O_~ӗO? ?}?~ H*\? 4xaB 6tbāQIh0Ą!'>$XA%L0a„ &L0A$XA .dC˗_D}'ϟ|/?ӗOA}˗?}CObA} HK0a„ &L0aB H*\ȰÇ#/=O|_>O ? W0}W߾XP|,h „ 2l!Ĉ8`A&TaC!F/_>~{/}'|/|/߿|>$XР@$XA .dC'p "Lp!ÆB8_|"_>'0~O@/o`>Ǐ˗'p 'p "Lp!ÆBx? 4xaB 6tbāQI_>O?70_|/@}a>$0G` $(PA |,h „ 2l? 4xaB 6tbāQI_>˗/_?'0|O_|_XP|A$H $(_ H*\Ȱ!B$XA .dC˗_D}'QDI,`>O?~ ߿}O?~?}7'_~O /O@ DPB H*\ȰÇ#/=ODXP|_뗏߿|'߾| ?~o+X}㗯_?~?$XA .T <0… :|1@񋨏C$J(>'p A'> ߿O࿂ o}?$XA .T <0… :|1@񋨏C$J(>'p A'> ߿O࿂ o|>$XA .T <0… :|1@񋨏C$J(>'p AO?}o?/_',o`?~O@ DP… H*\ȰÇ#/=ODXP|߿'_>o??} '_ϟ||,h „ .O@ DPB >q|E|%JPĂ8 H*\ȰÇ'p "Lp!ÆB8_|"_>%JDObA} HP?$XA .dC 8`A&TaC!F/_>~{/D%"'>$XA .dC 'p "Lp!ÆB8_|"_>%JDObA} H*\ȰÇ#O@ DPB >q|E|%JPĂ8`A&TaC!F4 <0… :|1@񋨏C$J ?  `> 4xaB 6tbD H*\ȰÇ#/=OD8@$XР@} H*\ȰÇ#O@ DPB >q|E|%J(ѡ>$XA .dC 'p "Lp!ÆB8_|"_>%JP|,h „ 2l!Ĉ8`A&TaC!F/_>~{/D%Jt`> 4xaB 6tbD H*\ȰÇ#/=OD%:0 <0… :|1A$XA .dC˗_D}'QDO@ DPB > ,h „ 2l!Ĉ/>~(QD'p "Lp!ÆBh? 4xaB 6tbāQI(QD8`A&TaC!F4 <0… :|1@񋨏C$J(QD%J(QD%J(QD/>~(QD%J(QD%J(QD%/=OD%J(QD%J(QD%Jh_|"_>%J(QD%J(QD%J(QA񋨏C$J(QD%J(QD%J(QD/>~(QD%J(QD%J(QD%/=OD%J(QD%J(QD%Jh@ Ϡ'p "Lp!ÆB(q"Ŋ/b̨q#GQuرcǎ;vرcǎ/>~رcǎ;vرcǎ˗_D}ױcǎ;vرcǎ;:/=cǎ;vرcǎ;vt/_>~{/_ǎ;vرcǎ;v_|"_;vرcǎ;vѡ|E|;vرcǎ;vرC񋨏C:vرcǎ;vرcGQuرcǎ;vرcǎ˗,(P? <0… :|1ĉ+Z1ƍ˗_D}ױcǎ;vرcǎ;:/=cǎ;vرcǎ;vt/_>~{/_ǎ;vرcǎ;v_|"_;vرcǎ;vѡ|E|;vرcǎ;vرC񋨏C:vرcǎ;VP'p "Lp!Æ/?{/D!B? 4xaB 6tbă H@} H*\ȰÇ˗_D}1bĈ# 0 <0… :|1A$Xp>$XA .dC/>~1bĈO@ DPB > ,8P,h „ 2l!QE1bĈ'p "Lp!ÆBh? ? 4xaB 6t?񋨏C"F1bD8`A&TaC!F4  <0… :||E|#F1@} H*\ȰÇ#O@O@ DPB >_|"_#FQ>$XA .dC 'p 'p "Lp!ÆB/_>~{/_Ĉ#F(P|,h „ 2l!Ĉ8`8`A&TaC!/=/bĈ#F`> 4xaB 6tbD H@} 480A3hРA~# |4Ϡ@~ /_>~ Ϡ'p "Lp!Æ 'p "Lp!ÆBh? ϠA3H0| ,/? `>/A˗A3hp| H*\ȰaC} H*\ȰÇ#O@38P?~'_|/}߾|Ǐ߿}߿|'?o_>~˷O`_|@}4hp|4XP? <0… 60 <0… :|1A$Xp>O  o߾ ܷ}_>۷}o?}#_>~߾}>~'0?}4hp|4XP? <0… 60 <0… :|1A$Xp>O ?70| ̗o`>}|O`/|O? 48_| ,AO@ DPB O@ DPB > ,8P'P~/߿/߿'߿/߿߿|߿O| 7P`70_7p`8p| 8p@ 8p@8`A&TaÆ8`A&TaC!F4 gp` /ӷ|/|`>O|7>'0_|/}ӷO`>W0A˗A3hp| H*\ȰaC} H*\ȰÇ#O@380?_|/>}˗|ӧ`>O ?}#/_|O`'0?}/_~ W0A˗A3hp| H*\ȰaC} ?$H A $O$ A8`A&T!,8P,h „ 2l!QE1bĈ'p`0 <࿁0߿ &L0aB H@} H*\ȰÇ˗_D}1bĈ# 0'߿@߿O`#߿>'ϟO $H A   <0… :||E|#F1@} ? />~/߿}o}O?>}/߾'_>}ۗ߿|/_>}$H A  gРA 3(? 4X? 4hРAg`A} /,h „ 2lP|O ?7_ 7 '߿O| 'p~/߿'߿|G A $Hp ,8PA ,Ϡ| 4h`| 4hРAϠ48_>$XA .dذ>/߿/?O?o|߿?} ׏?o|_}G A $HP ,8PA| /_?/_>׏|˗ϟ@} Ǐ߿}߿|GP| 4hРAϠ48_>$XA .dذ>_} 7_>ǯ?~O?O?/?~O_~/?~ '_}/?}珟@A $H A H@} (0'??~/_o߾ Ǐ`>~o?~OA~O@ D0|)<‚)TPB *THP|߿~ O ?} ' '@o _?__? o߿?~ϟ> $H A8`8P}'Ao#/߿| G_>o_}o| H&/?SX_> *TPB O@ DPB C'p o_>~ 诟>~߿| O`70|o`>Ǐ@}? 4xaB>~ ˧PB *TP!A} H*\Ȱ!C~СÄ H@}O?_>'0߿|O߾ W0@}'P_3/A 4hРA3h>~ ? 4xaB 6l`> 4xaB 6tbD H@}˗/_?_|O?}O`ӗ/_? O_|_>4hРA /? g@8`A&TaÆ8`A&TaC!F4  <0… :||E|#F1@} H*\ȰÇ#O@O@ DPB >_|"_#FQ> A$O?'p "Lp!Æ'p 'p "Lp!ÆB/_>~{/_Ĉ#F(P|,h!D!B"D!B8` g A~ 4H0߿~ 4hРA 4h A3h>~ ? 4xaB 6l`>߿'|@O?0 <0… :4 gp`~ '0A ϠA 4hРA /? g@8`A&TaÆ80?/߾ӗ_~`> 4xaB 6tX? }O|ӗO|˗ϟ| Է/?'? 4hРA 48_| ,AO@ DPB O|O} '?~OO H*\Ȱ H@}߿|o|۷} '0_>4hРA 4hРg`A} /,h „ 2lP|'O?/?_`> 4xaB 6tX? | /| /| '0| 70A 4hРA /_>~ Ϡ'p "Lp!Æ 'p` 'p߿ǯ珟@8`A&TaC8` '0| '0| '0| /|4hРA 4hРg`A} /,h „ 2lP|'/>ӗ@ӗ_>}0 <0… :4 gp`>o?}_| O߾ gРA 4hРA˗A3hp| H*\ȰaC} H*\ȰÇ#O@38P|/_~?}/>} ϟ| ϠA 4hРA /? g@8`A&TaÆ8`A&TaC!F4 gp ~ 4hР 4hРA 4h A3h>~ ? 4xaB 6l`> 4xaB 6tbD H@} 3hРA gРA 4hРA ˗A3hp| H*\ȰaC} H*\ȰÇ#O@O@ DPB >_|"_#FQ>$XA .dC 'p 'p "Lp!ÆB/_>~{/_Ĉ#F(P|,h „ 2l!Ĉ8`8`A&TaC!/=/bĈ#F`> 4xaB 6tbD H@} H*\ȰÇ˗_D}1bĈ#  <0… :|1A$Xp>$XA .dC/>~1bĈ#F1bĈ#FT/bĈ#F|E|#F1bĈ#F1bĈ E1bĈ#/=/bĈ#F1bĈ#FQ#F1bQE1bĈ#F1bĈ#*P'p "Lp!Æ/?{/D!B"D!B"D!B"D?}"D!B"D!B"D!B"AOA"D!B"D!B"D!BH_| |!B"D!B"D!B"D!/?{/D!B"D!B"D!B"D?}"D!B"D!B"D!B"AOO@ DPB >QD-^ĘQF/>~رcǎ;vرcǎ˗_D}ױcǎ;vرcǎ;:/=cǎ;vرcǎ;vt/_>~{/_ǎ;vرcǎ;v_|"_;vرcǎ;vѡ|E|;vرcǎ;vرC񋨏C:vرcǎ;vرcGQuرcǎ;vرcǎ/>~رcǎ;vرcǎ/?$XP>~ ? 4xaB 6tbD)VxcF9:/=cǎ;vرcǎ;vt/_>~{/_ǎ;vرcǎ;v_|"_;vرcǎ;vѡ|E|;vرcǎ;vرC񋨏C:vرcǎ;vرcGQu1>$XA .dC 'p "Lp!ÆB8_|"_>%JP|,h „ 2l!Ĉ8`A&TaC!F/_>~{/D%Jt`> 4xaB 6tbD H*\ȰÇ#/=OD%:0 <0… :|1A$XA .dC˗_D}'QDO@ DPB > ,h „ 2l!Ĉ/>~(QD'p`A$H?$XA !' &L ,h „ 2l!Ĉ/>~(QD'p`A$H?$XA  g… O@ DPB >q|E|%J(ѡ>|߿}O_~#߿߾|' ϟO O?~ _?`> 4x!B$XA .dC˗_D}'Q~ H0?$XA'p`@O߿~ӧo__>}ۗ??}OO?} O~ /߿~O8`AO@ DPB >q|E|%J<ϟD'p`}? o O|' ϟO|'O? ?'p "< <0… :|1@񋨏C$Jx? O|?'?#> 7?o_?_|?$XA8`A&TaC!F/_>~{/D'QbA} ?~'_}_>7?}OO࿁ӗ?ǯO_o|,h ƒ H*\ȰÇ#/=OD(>߾_?>#߿'_>o?O?o|O_O|,h B H*\ȰÇ#/=OD(>$XA '߿ .\pB H*\ȰÇ#/=OD(>$XA ?} .\p…8`A&TaC!F/_>~{/D'QbA} H*\ȰÇ#O@ DPB >q|E|%J<ϟD'p "Lp!ÆBh? 4xaB 6tbāQI0C~$J,`> 4xaB 6tbD H*\ȰÇ#/=OD'QbA} OA$H A ̧o? $HP 0 <0B$XA .dC˗_D}'|ۗ/?/?/?ӗ? O|$ A $H` $H $H|,h „ 8`A&TaC!F/_>~{/D~o?/_?/_?O!?%0ӗ߿|߿}o@~߿}/'߿>70>ۗ߿8`A&D <0… :|1@񋨏C$/| '0| '0߿| (>O ^GO?O?}/o__~ۗ?׏?O| 0 <0B$XA .dC˗_D}'1| '0| '0|)> 4xaAO7}o?'p~O࿁7_ 珟> O?'|,h „ 8`A&TaC!F/_>~{/D}ӷ| '0| '0߿| (>O7o?/߿~G0߿7_ ߿~ O?'|,h „ 8`A&TaC!F/_>~{/DO_|O`>O`%OĂ80? o?ǯ㗯|ǯOO?$XA 'p "Lp!ÆB8_|"_>%OĂ80ϟ>o 7_>}/_?O`o|OA'@>$XA 'p "Lp!ÆB8_|"_>%OĂ8`A&TaC!F4 <0… :|1@񋨏C$Jx? O@ DPB > ,h „ 2l!Ĉ/>~(QA~$J,`> 4xaB 6tbD H*\ȰÇ#/=OD(>$XA .dC 'p "Lp!ÆB8_|"_>%OĂ80?  A0 <0… :, <0… :|1@񋨏C$Jx? O|$H A$`> 4xaB 6tX? 4xaB 6tbāQI( ?%0o` ϟ ߿}߿}߿`> 4xaB 6D <0… :|1@񋨏C$Jx? O|/_|'߾|O| ?}>$XA .d!,h „ 2l!Ĉ/>~(QA~$J,`>_ 7|_ H*\ȰB$XA .dC˗_D}'QăIXP|'߿|_'߿O@ DPB 'p "Lp!ÆB8_|"_>O@'p ",`>_| /?}O࿁'_}珟@O@ DPB 'p "Lp!ÆB8_|"_>'p A H O|/>?o? ??8`A&TaC H*\ȰÇ#/=OD%:0 '?$XA .dC H*\ȰÇ#/=OD%:0 ?} H*\ȰÇ8`A&TaC!F/_>~{/D%Jt`> 4xaB 6tbD H*\ȰÇ#/=OD%:0 <0… :|1A$XA .dC˗_D}'QDO@ DPB > ,h „ 2l!Ĉ/>~(QD'p "Lp!ÆBh? 4xaB 6tbāQI(QD8`A&TaC!F4 <0… :|1@񋨏C$J(QD%J(QD%J(QD/>~(QD%J(QD%J(QD%/=OD%J(QD%J(QD%Jh_|"_>%J(QD%J(QD%J(QA񋨏C$J(QD%J(QD%J(QD/>~(QD%J(QD%J(QD%/=OD%J(QD%J(QD%Jh@ Ϡ'p "Lp!ÆB(q"Ŋ/b̨q#GQuرcǎ;vرcǎ/>~رcǎ;vرcǎ˗_D}ױcǎ;vرcǎ;:/=cǎ;vرcǎ;vt/_>~{/_ǎ;vرcǎ;v_|"_;vرcǎ;vѡ|E|;vرcǎ;vرC񋨏C:vرcǎ;vرcGQuرcǎ;vرcǎ˗,(P? <0… :|1ĉ+Z1ƍ˗_D}ױcǎ;vرcǎ;:/=cǎ;vرcǎ;vt/_>~{/_ǎ;vرcǎ;v_|"_;vرcǎ;vѡ|E|;vرcǎ;vرC񋨏C:vرcǎ;vرcGQuرcǎ;vرcǎ/>~رcǎ;vرcǎ˗_D}ױcLj H*\ȰÇ#O@ DPB >q|E|%J(ѡ>$XA .dC 'p "Lp!ÆB8_|"_>%JP|,h „ 2l!Ĉ8`A&TaC!F/_>~{/D%Jt`> 4xaB 6tbD H*\ȰÇ#/=OD%:0 <0… :|1A$XA .dC˗_D}'QDO@ DPB > ,h „ 2l!Ĉ/>~(QD'p "Lp!ÆBh? 4xaB 6tbāQI(QD8`A&TaC!F4 <0… :|1@񋨏C$J(QC} H*\ȰÇ#O@ DPB >q|E|%J(ѡ>$XA .dC 'p "Lp!ÆB8_|"_>%JP|,h „ 2l!Ĉ8` HP ,h „ 2lAOA"D'p`A$H?$XA .dСB$Xp>$XA .dC/>~1bĈO|˷?$H  H*\ȰC H@} H*\ȰÇ˗_D}1bĈ# 0㗏߿O|?~ۗ߿>/_ _?$XA .d8? ? 4xaB 6t?񋨏C"F1bD80߿O| _|@~/߿~/@>}O߿~ H*\P ,8P,h „ 2l!QE1bĈ'p`}? o O|' ϟO|,h „ 2  <0… :||E|#F1@} ?~'_?~o_o'_o?߿_?$XA .d(? ? 4xaB 6t?񋨏C"F1bD80߿O| ߿| o>˷ /_ _?'p "Lp!C H@} H*\ȰÇ˗_D}1bĈ# 0o߿O|?~_? 'p߿|O|,h „ 2  <0… :||E|#F1@} H*$ϠAϠ48_>$XA .dذ>$XA ?} .\p…8` 4h0A 4hР?}3h` gp | /_>~ Ϡ'p "Lp!Æ 'p "Lp!ÆBh? | ԧ/>'_|˧|Ǐ߿}߿|7? o_>~˷O`'_|_A}4hp|4XP? <0… 60 <0… :|1A$Xp> '0__ '0?~#߿}g_?}_۷_}߾OA 48_| ,AO@ DPB O@ DPB > ,8Po`|O` G0|߾|O_| W0|_Ϡ| /_>~ Ϡ'p "Lp!Æ 'p ˧`W` $O>$XA .d> ?O` >~ `>/߿߿|߿O_/߿(0|_7p|8p@8p8p'p "Lp!Æ 'p W`|W` ,|,h „ 2 gp` }_>>~O}/?~O`~'0_|/}o|| /_>~ Ϡ'p "Lp!Æ 'p`>~?}?~G0??~o?~O?~o|߿~ H*\x? }O_}˧O?~_>~Ǐ`>O ?}3/_>~'0_A}ϟ|/_~ g_> /? g@8`A&TaÆ80| 7_~/?~ǯ?>~O}O}o?ӧ_?$XA .4 g| H*\ȰÇ?}"D!"0O࿁'߿| /'P_O?O࿁?} H*\x? Ϡ8`A&TaC˗="D!BD`>߿#(?˗/|/_˗_|/߿|˗_|O`?$XA .4  <0… :||E|#F1@} _}/?~`>} | ' 'O H*\h? ? 4xaB 6t?񋨏C"F1bD80 7߿}'| o@}'߿ ' '?8`A&T ,8P,h „ 2l!QE1bĈ'p "Lp!ÆBh? ? 4xaB 6t?񋨏C"F1bD8`A&TaC!F4  <0… :||E|#F1@} H*\ȰÇ#O@O@ DPB >_|"_#FQ>$XA .dC 'p 'p "Lp!ÆB/_>~{/_Ĉ#F(P|,h „ 2l!Ĉ8`8`A&TaC!/=/bĈ#F`> 4xaB 6tbD H@} H*\ȰÇ˗_D}1bĈ# 0 <0… :|1A$Xp>$XA .dC/>~1bĈO@ DPB > ,8P,h „ 2l!QE1bĈ'p "Lp!ÆBh? > O@ DPB >4/_>~_>!B>$XA .dC 'p "Lp!ÆB8_|"_>%JP|,h „ 2l!Ĉ8`A&TaC!F/_>~{/D%Jt`> 4xaB 6tbD H*\ȰÇ#/=OD%:0 <0… :|1A$XA .dC˗_D}'QDO@ DPB > ,h „ 2l!Ĉ/>~(QD%J(QD%J(QD%/=OD%J(QD%J(QD%Jh_|"_>%J(QD%J(QD%J(QA񋨏C$J(QD%J(QD%J(QD/>~(QD%J(QD%J(QD%/=OD%J(QD%J(QD%Jh_|"_>%J(QD%J(QD%J(QA˗,(P? <0… :|1ĉ+Z1ƍ˗_D}ױcǎ;vرcǎ;:/=cǎ;vرcǎ;vt/_>~{/_ǎ;vرcǎ;v_|"_;vرcǎ;vѡ|E|;vرcǎ;vرC񋨏C:vرcǎ;vرcGQuرcǎ;vرcǎ/>~رcǎ;vرcǎ˗_D}ױcǎ;vرcǎ;:/_>~ H@} /,h „ 2l!Ĉ'Rh"ƌ7rt/_>~{/_ǎ;vرcǎ;v_|"_;vرcǎ;vѡ|E|;vرcǎ;vرC񋨏C:vرcǎ;vرcGQuرcǎ;vرcǎ/>~رcǎ;vرcǎ˗_D}ױcǎ;vر"@} <0… :|h_| |!B"D!B"D"D!BL/_>~_>!B!@,h „ 2l!Ĉ8`8`A&TaC!/=/bĈ#F`> 4xaB 6tbD H@} H*\ȰÇ˗_D}1bĈ# 0 <0… :|1A$Xp> 4h`> gРA 4hp ? /_>~ Ϡ'p "Lp!Æ 'p "Lp!ÆBh? ϠA $ϠA`| 4(_> /_>~ Ϡ'p "Lp!Æ 'p "Lp!ÆBh? @}˗ϟA}˗ϟ@~/| 'P|/@~/'p_>~ o_|/ԧ/A /? g@8`A&TaÆ8`A&TaC!F4 gp`>~ g0?~O|/_| >~?} _>_߾˷>4hР@3h>~ ? 4xaB 6l`>_?$XA OB SPB8` /? @~o`o`|O` /|70?/ 4hP|4XP? <0… 60|,h „)TP *TP ,8P'P7P~/߿'P`>/|/|(_O| '0~8_>$X`A;xp>~'П?O  ߿}O` ?|G0/߿?/ ߿}_~߿~ $(? |_|_>o? /|ۧo?_>'?}W0?}'P_3/A /? g@8`A&TaÆ80˗/_>~/|o_>~˗>}O?~_|˗_|_|㗯_?~ _/_|o__>}/߿~/?}O@'p ?}3?}/_>}ӗO'0|ӗOӧ߿|˗?W0?'ϠA˗A3hp| H*\ȰaC} ?O>'/?O 7?~7߾_O> o~O_#H A H@} 4hРA4hРA 4hРAg`A} /,h „ 2lP|O#_?~~߿?߿7_O_` '_}G A8` 4hРA 3hРA 4hРAϠ48_>$XA .dذ>/?~O࿁'?珟@O ~|߾>˷ /_ _?$H`>$Xp>$`>38,h „ 2D/_>~ P| 6lذaÆ80˗/_>}˗߿~/_O?} O?(@?}ϟ| ̧߿'_>o?O?8p@$Xp> W0A/,h „ 2D/_>~ P| 6lذaÆ8`A&TaC Ć H@}o_>~˷O`'_|A}'p "Lp!Cא>~ װaÆ 6l`> 4xaB 6t ? |`>~o߾|g_>$XA .d_|ԧ@6lذaÆ 'p "Lp!ÆBh? | W0|_| H*\|5$O_C5lذaÆ O@ DPB > ,8P`70߿|g_>$XA .d_|ԧ@6lذaÆ 'p` G A̧o?$XA O>)TPa>$Xp>'0_|/}o|`>$XA .d_|ԧ@6lذaÆ 'p` G Ao?$XA O *T? | WP_|/_~˗| <0… ˗_C5/_Æ 6lذ>? ?}o_>~?}/?~?~O  ?~ ?~ ߿ӗ߿?$XA8`8`A&TaC!/=/b#F`>_?| G0߿/_'?~_~ۧ|ۧ|O?_0 < ,8P,h „ 2l!QE1bĈ'p`o| 7߿ G0߿ ? O| ' ' ' H 'p 'p "Lp!ÆB/_>~{/_Ĉ#F(P|'?7O?o?O?O?? 0 < @~,80,h|C!B܇@~",_~!!7_~'?}ӗ>O`O?O࿁?}OO@ DX? Ϡ@_|˗?}/7P_>}O>~o_|//_}O|˧o|˗߿| Է/?'?g`A} /,h „ 2lP|,h „ 2l!Ĉ8` } ϟ>߾_>>o߾_}_/߿}/_|o߿| _O`?}Ϡ48_>$XA .dذ>$XA .dC 'p o? G__>o`> o`ۗ_ W0|70߿|70߿| '0߿| G_| ,OAO@ DPB O@ DPB > ,8P0@/߿|O| _(0߿| ǯ>~˗/_>~O`o| /| /| /_>~8P>}8_>$XA .dذ>$XA .dC 'p o`>}?o| G0?}'0_|/@}| '0?}/_|/߿|O`o>g`A} /,h „ 2lP|,h „ 2l!Ĉ8` /_~|O?}O`>ӗ/_? O_|_>'0|˗߿|O_|˧O|/>}#/_>~ ԧϠ'p "Lp!Æ 'p ,X` OA} ,X`A+X A},X` H@} H*\ ~ 6,|5$O_C5lذaÆ O@,X` _ ,X`A ,XP ,X`A H@} H*\ᾆ W_|ԧ@6laÆ 'p`>~߿oۗo? ?G0o߿|' _?'߿O?7߿~ $H@$Xp>$XA .dC/>}1bĈO@~/߿~/_>?}O/_~ O?~˧'>}O߿|o__~ۗ߿|,h ,8PA g`> 4hP |g` g ?˗A3hp| H*\ȰaC} @}O>~ 7_Oo`o ǯ??}/߿ '_o࿁˧>$X`A$Xp> `|4h?g |4@~ /_>~ ԧϠ'p "Lp!Æ 'p'o|߿߿~/߿ 7_?~O~(O࿁O~ H H@}Ǐ_|˗}+/_>ۗ_/_>?~'0A~ǯ`} o_|˗߿|+O_>˗A3hp| H*\ȰaC} |O?'_>ǯ/_? O࿁'_|'_~O࿁O 8p O@380?~O|/}'0߾}+߿}gP`>/_|/߾}o?Ϡ| /? g@8`A&TaÆ8P O?'_>}O߿7p ?˧߿|O ?} 7߿o(? |ȏ| G0|`>o_}3/_?`70߿|˧ϟA /_>~ ԧϠ'p "Lp!Æ 'p '?"D!B"D@$Xp>O@_߿|O| '0~(p߾ '0@/| 8p7p7pO@ DPB O@ $?"D!B"D!B8` O?_󗏠}O߾'P߾ ԗ?}g_>+?~ӷ|_Ϡg`A} /,h „ 2lP|,h „ 2l!Ĉ8` ϟ|/>}˧OA'0?}+O?}O? ˗/_>| O_|ӗ/_3/AϠ48_>$XA .dذ>$XA .dC 'p 'p "Lp!ÆB/_>~{/_Ĉ#F(P|,h „ 2l!Ĉ8`8`A&TaC!/=/bĈ#F`> 4xaB 6tbD H@} H*\ȰÇ˗_D}1bĈ# P <0… :|1A$Xp>$XA .dC/>}1bĈ#F1bĈ#FT/bĈ#F|Eԧ|#F1bĈ#F1bĈ E1bĈ#/=/bĈ#F1bĈ#FQ#F1bQE1bĈ#F1bĈ#*O@8`A&TaC ˗="D!B"D!B"D!B"D>}"D!B"D!B"D!B|AOC B"D!B"D!B"D!B$/_>~_>!B"D!B"D!B"D ˗="D!B"D!B"D!B"D>}"D!B"D!B"D!B|AOC'p "Lp!ÆB(q"Ŋ/b̨q#GQuرcǎ;vرcǎ/>}رcǎ;vرcǎ˗_D}ױcǎ;vرcǎ;:/=cǎ;vرcǎ;vt/_>~{/_ǎ;vرcǎ;v_|"_;vرcǎ;vѡ|Eԧ|;vرcǎ;vرCOC:vرcǎ;vرcGQuرcǎ;vرcǎ˗,(P> <0… :|1ĉ+Z1ƍ˗_D}ױcǎ;vرcǎ;:/=cǎ;vرcǎ;vt/_>~{/_ǎ;vرcǎ;v_|"_;vرcǎ;vѡ|Eԧ|;vرcǎ;vرCOC:v? 4xaB 6tbă H*\ȰÇ#/=OD%:0 <0… :|1A$XA .dC˗_D}'QDO@ DPB > ,h „ 2l!Ĉ/>}(QD'p "Lp!ÆBh? 4xaB 6tbāQI(QD8`A&TaC!F4 O@8`A&TaC ˗="D!BD`> 4xaB 6tbD H@} H,h „ 2lAOA"D'p@ 0O@ DP@} WP_„ C/A$Xp>$XA .dC/>}1bĈO$H0?$XA 0%LP?O@O@ DPB >_|O@O@O@'p "Lp!Æ8pO?O}|'߿}?}O?~O?~ >ӗ߿7?~o_>?'p 'p "Lp!ÆB/_>~O@O@O@O@ DPB 'p_>}O࿁㷯?}|/˧ Oۧ|_?'?~#o?ׯ?~/OO@O@ DPB >_| 1bA~E1bD8pO?O? o7|'_ '##o?O| A$Xp>$XA .dCǯ`|"F/߾#FxP|~ ' ' G0߿~ '/?OO7o?'_ A$Xp>$XA .dC|"F/_Ĉ#FD`>G_}O?}O?~|7|'o_ '?} G0?'?}/_O| A$Xp>$XA .dC`>}#ۗ_Ĉ#FD`>G߿~O O>#߿7|'?}?~ ''?}ӗ߿_?O_>'߿~߿ gРA4hРA`>g|3hР@3_>~ 4hp`|8`A&TaÁ8`A&Ta װ!B$Xp> Ϡ| 4h_4H0_| gP | /_>~ϠA ̧? 4xaB 6$`> 4xaB 6װ "O@3H0|˷O| /_/_>/_>?~'0_A~ǯ`} o_|˗ϟ|#O_>8?~O$H A <0… 0 <0… :|1A$Xp> '0__ '0?~+߾}Ǐ߿|3(0_|`>~o߾|g_>'p A8p|$H A <0… 0 <0… :|1A$Xp> '0|#/|ȯ`o_}3/_`70߿| ̧ϟ 0 O@#H A˷? 4xaB 64`> 4xaS8B)T?S(B$Xp> '0|O?~0@/߿|_>~@ /߿| o` /8_O@'p ,X`A'p "Lp!Æ'p "L8 S0? "7B'p $_|o}?@~o?~3(0߿|}_ '0߿} 3hРA3hРO@ DPB O߿~_>~7_?~o_>?~A~߿?'߿?}ӗ߿?'p $/>}˷?}/_>}ϟ|ϟ| |˗/_| WP_|/_~˗|gРA ϠA <0… "0|?}o|/__>}>}/O}| 㗯_|/߿/?O`_> g | H*\ȰÇ/b|"F1C} 8o?# /߿ǯ?'߿#_ ߿'P_O?O?O~7A8`8`8`A&TaC! ̧/"}E1bć8p? _?G0߿_ ߿~ 7_O?O?O } /_>~ H ? ? 'P߿|O@7p_/8_>$XA Oa~)TPB *TO|'?~}O_>~|_O?~O_~˷O_~/?~_ 7 '?}#HP ,8PA o|| $O` $ϠA480A 48_> ̧? 4xaB 6\`>?/߿~|O_?ӧ߿?'@}'߿' 'ӗ?}/#HP ,8PA /|7_?/_> /߾|˗@~~} ܗ_/_>O?}3hРA3h| H*\Ȱ!C} HK0a„ &L0a„8` g_ '0_}`>~O`>o>o??}_㗯`>~O`>? 4_} 䗯,h „ 2lP|,h |&L0a„ &L0!A$Xp>˗O}'0|ӗO`>o| />/| W0|O`>gРAϠO@ DPB O@ DPB > ,8PA 'P߿_>__/߿|㧏~/| G_> __>$XA!,/B"D!B"`> 4xaB 6tbD H@} +/@}`?}3/?}'0|o_>o|Ǐ`>O``> _>ϠA ̧`A~'p "Lp!Æ'p "Lp!Bh?  |O߿|˗O?~'0_|˗|ӗ/߾ӧ?}O`>ӧ_>ӗOӗ/?70? 4X_>~? 4xaB 6tO|,`+X` '__,X`  gРA gРA 4hРAgРA ۗϟA'p "Lp!ÆO@ D8_„ 0W_„ & gРA gРA 4hРAϠA OO@ DPB  0O?/ ?ӗ߾O?~߿~O >o?~#@'p?> A'p 'p "Lp!ÆB_>~'QD%O/??~O߾|'߾|O߿~/ۗ㗯_? O뗯?}_>˧7pO@O@ DPB >Q!|'QD%0O?O| ࿁珟>?/߿ 7G0?O o~ A'p 'p "Lp!ÆBP_I(QD'p}G#o࿁'_| G?O| G A8`8`A&TaC!F\/߾(QDO@ /?O_o o?O/㗯?#O߿~o__} A'p 'p "Lp!ÆBȐ_>~I(QD'p@}/?߿~|o 7>ӗ߿~_?'߿ 7A 0@/>?ϟ 8p8`8`A&TaC!Fl/_>%J(>$XA .dÁ!,8P,h „ 2l!Ĉ(QD O@ DPB >? >O@O@ DPB >QD-^,`> 4xaB 6tbD H@} H*\ȰÇ#JHŋ'p "Lp!ÆBh? ? O@ DPB >QD-F0 <0… :|1A$Xp`?$? 4xaB 6tbD)VQ|,h „ 2l!Ĉ8`A&TaC!F8bE1O@ DPB > ,h „ 2l!Ĉ'Rh"ƌ7r#Ȑ"G,i$ʔ*Wl鲡> '?@8`A&TaC!F8bE1fԸc ̗O`|˗O`|=zѣG=z#|'0_>~'0_>=zѣG=zb|/߾0@8`A&TaC!F8bE1fԸc ̗O`|˗O`|=zѣG=z#|'0_>~'0_>=zѣG=zb|/|/G=zѣG=zH1_ ̗_~ O@} H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣHV ;;PKPK|%AOEBPS/img/adfns109.gif_=GIF89a@{|N=>'ٹu򫱲999rvw9;;rrrvk<ى铚b UXY*,,0\]:..v^ Z⩫kllD抌X;5MM1dghGIJם֠;/ ```@@@bKXP-q,(JC%xDSϼi 000ްСˆ.4"lj8bɏCJLII(/ScK"aĹgG?oڜ9ǝE화ҠDT tӪ: хcYc=;!g,۶v=Eڻ{7npbÏ7V\Ǔ!g|f|1w8CY7͋O|z秢W?Nvݭ;/uۙ7?}yݧ~{??{} wW 28 ) Inuv ($h(,0(4ֈqڨ<@)D!L6PF)8Xf\`" 0@Ø#~I5t)tig 28Þ)b ީ袌6ꨇyf0 `&+0iæѠJP*rz@ rJ h>Y++b* `uZ(i gЭ*XʡbA4"R@A bAH/q4С%[*x` ˫wyvcyL'+LBJ >L @Ʈ}lH;:,@F;C i]K\`R2A,le`BMk"0֪"0 \ȩj)C\vWLe'G${ >+)GF{Ϊ{B.3~&0%{vX&پ+6*gr*G/>Og_w p J\q[zRqP.JuSSO*G &-a ΥZAMO0 .=8 W.%R"F j 0fPbX'0 *h@=mPN4X"08 h~b")$8SҦ6J|* 8GbUVL) $+{ $IPa Afq\ϊ _8BZA ~W>YnXb AUjL%@蔪,uԷҭ̥.>84R IbhƔ f:Ќ4%SDȼfx 8Ir^&61uM\#NwvC ?}=h?5n+hB'JQ.$FߩxV *Ђ$ҖVT0L*iTKw:Q4ehJq1~T #Oә#ьZɣ?Г< U=*p3|ԩe1Ӫ8<*` 'oPEaϪ޼*Ϧu٤:OE+TVyUZijk_[ٟ,8+O\lcϠVaMj[z6j Wܲuo`U6t f@jzU6Vecek^^6nǶkxX`Z[6G [;P^Qu=/g0|^-5-0tZԲ' ؞ *^$թX|>w SΡW 8 ~*,P&zO(ߤ[QԨ9X]7=3Ҭ楾Y6[(:yw(:f\ =>Ήhn(C?)}'K_v9]DGzm1_R7 j]4dt@]%X Mb>6Ї2Et5Mj[P%: +D@r67vYHC3Va1}&{O'ox'z 7-D09U4`f\K\H#oL"@Ё.= HOEȼ4oΤj'Q $ 8P権# 쫙2ȺN$?=;mv}{2sY@DG$G x/8g%xCp"owϼ7yk/UPe EzӏG4{O=EO2_~=[~>  ~H~y_蟿O}~hGxm sh H{zb#HL]G ,2#h-(98؃}CBFX q AHJ(LhN؄IR0QTxVXZȅ_(a1]LD e8chAȁЅhjld[(|~h{x}x8Xщ8Xxh>0p8{BA@4>8XxȘ8X'x؋8ؘڸ؍8yBx(8ȋx@X9Y{ҎٌBi9YLjzȐ{"9$[`{RH@Y' Y2949[ MP-YMELB5YFyx9;i=i?2HYVyBIX;fi)0eyg hlٖvɖr)toy{ }qiuz~9ɘyo{ {B9阋əɗ陇I))@ $MyٚٛɛR :=P1iɜYIy Y鉞ᙝٝ)iɞ to(Iii'빠ڠy Z*ڝŷt !j&Z"ʠ$ja!!b 0Z r4ʣb9j'e 2z 2?±u@A RڣѡWw+HSQ^ʥ[@f.3gjiezmJoʦjqJEku {Jnw sj}Jr ʨZ| tuxjJ[רzꨠʪfZ$ ]@|ৣʩzڪ꫹f6wkpw7:ocsv-4:#FzqhƬzq@=R@7lsqgz2_zܪ#'C"r/vE3u!"wp110Z#'c)c1*5x3 GE,s>\&ư;zF#'ǂ7.aCa'_,9u$."m%;g沨SW:;#7+$#-2`1u{?kKt.BV  nKXQx6{*"K(2&B*f&,R(".t9 ,R&5gfvΔv5m3a'#Ǜ003;s2"rrd 0u)4.@ 8|f,99 G±’ 10P4{mCñru5v$[;,=>,;.3;SQKAⴿd!{U+)HW@',gk;_&F*#1$k(6P^a]|ӯd 9f^T1(/"rvp+"D-"mB;R *R4:xsC] (CL=~LCK")k7b*PG@  -'k*b0CyNB:c:WȲCl'<"*s.G`,Pɢ/ H,íXRD~RHJ^RpHOoFWᡦG$`@,b^YS[%ndnFcs~k.Sm|~U4x0n0n0G{n }nGp0pIOI}0% P>O^I1^ ꡮ,-0m`0p~P'C0dn4]pC nNRKKL>GI0(oGpLN(` ^q®>mNOՎn@2#/> @JێN@ J oK0(I?e~tLD~JP$MhU j?zBco, I?j/~og(nN>_@.騀JooF쪾jNNO > (lG`C]~2 - J.PrJ TI . PjBIPm#*ULTI)UdK0XsHCTGTX1͟(*A0zE5C4e  x FLaŎ @UTU=2yaδiR5E_MΌURTwa(Q3'PFpU7^$jxTE&KO&YzEҠanT %(tj6 jJ NAXq/z-\W>" {&]&MG`و"y'ѣ2B@'9/[dhEd &(#@b62. >B)oCCn$KLST#J2[q}$ 3F]F ET DE#dI%d.NG'QJ.r,T .dRL,3HRC4yr$G+N4դs3P=Ul `?K3l4L@!IRM8 S4DII5rS)OUXm5-OQUTX3uV!̕GYyJ=.Xa=[OvMV/IDdkIVeMjn;ܠ^W+vsWe,moh}tq?ݠԭ=0GzgnȗOy-Mͭ7 :P`|70hw| 0|{YHOcI x@ ))ԡ( h`'~ A1Umg/IP$UA f &\B3…ЀCTb09Tf4, bXE+dX$I+r1`"B54"'hFVm :BBt"XF1l#!iALF` G $"/YHF~򑘴d'CiJH2L%*IIWYHsKZĖIvZ&-c_.Lcʱ&5dϼf3}o"5Mb3<'9iNba!d4YOu=YM (;׉N|4; P sš($JUXE+h-I*&Pa!`H1ZRtD0`z B Nuҡ4&jQT5L< ӦJU/?URZMjYհubZۊӬkj@{+F׾R|+`[]^RPmbJY ke;{YƮB jC"vml_kخ- Y-VŽ-ss[ʕluyQW%oy{t!ThzK"BWo>@ |`'X f28 .hUq_ gXA2L$w#&qM|bBV3la X$*4 bx;q}|^ 50c$Ʊ*td(GY ry[6\`.Yc/|efپG&YsN7b5$tc>Zy05gBy>󟝼hHGڿv1mhIю~r=iRD>1oӨfE=j,ʭWMLUGM`w]^׫^]l]s8KZ[貛-dv5mbs;ȖHvmic6p떶}kk׻~7 0w t 7p[8}/)nqVx1Qߒ0){ xf`kN@,D ̤yY|2m=B'ǭGF)3]J:כ^u/[y.w=__;M[.˻eă GxW7HG̗=k/z^=m{q;+ EF?|87žĈG7W!/GO?+<>o?SI2꓾dd@*L@@@t#>z,0 rP,!"Uh"l'h!kA!zA[A!4A4B4u#Bd˜@$,i DQ%D!".d ,BBC98tC`%|9rC2 B3\4LÔXC6,";$8 C0 z U1 n1CBLGX-tDU@ GvG V`FXA?;mƅTDpTDqBr,Gy4-l f,BLTl_Fi “GV@CSnloVlŊ6HQdD⠓GŐdO)"d)(IAEoDF4I<ǎ||_Ǔ@aF (Vɒ쀓l" PIUw$~\ '(IPL *t"P4K<1k!DxlGDM&{ }GH|[̶K,aFDd J Ff *>'^ ^c;b8F,~:F^3cP=^3^Ic&_M̀ȀEVmQdu^L L^*nx̀P^P~; (O~_GdAa0CYe `.`]>1Հ 0 x eV p_흘^5 (^ X oTdWT`̀5XTf\ e 0HVIc HTgfaLh(h;Vc>6_]uK 0p^doF>efEgHv)`hUUAt!niXLf}^ \DݍnP(؀̀ XfQn-].xkVd6kjfދ^ov fgQfRv^ (u6l]]0݅~lUl}a = Nheuюmf\bޱN6kf.mE HlnmC^Nn \΀]Vn>⹶dnfUoed .瞾ibU~gl+VgIhh-ni풶}^؀6hAb bPcvNgVn}ߍO^.'g!u^^f` S6j ku6LU?h>BN~D'lmTtEoɞpR&g݀&Dn^ 8tqv.K_k;?q^5'oV2?nQs2fAWsmO]Vg /)`&ހS6_nr+s~kg=i@7gxc0oVn^{6]}ܮ&hgvO_t}\6yflkhnހzW{_O-&^qv_r6i"Nwruu1r^b6lvqpu^w~޼vvc7owk.rk}.=peip? qd{.&zkv]Հ`5w=_WS{- .gn</?kNosgOf^Y^?p?&y~G(mWxg|B5'j.3_s?xkmLWl&`O~Ftsf}q i5GrVtff&x dmW'^֎e`v_m|^]VgsgtT*؂„ R ĈT (@6lb*B@d1*M8$Hdpi`A w'Р k p [ʄ Sj%Ԉ#|37vTBV4S(+SrxqCM pEUdK&$xl *@X|*E5%i}#gJDBT6CMD4G,+\1CiEAL]~ ,DwRT9dhZ=|N5u* @D`b`* $Bb BV[%X.eG/Q$wo)ȕn48 @I K"[P1h #b)@/LȕE㐮F\e" 0#'FƤ'Wc\tB7f$if*/t$. a|*|ݗ_UJ:)r{ V)N#@ )ҕ?fYzzA*֢ST kӥ¥ɖe)l*ƶ*gPV[> OȖ;l4JlK뵲{/݂o )鹙6aljK鼪r©oi(!'jHA)BBd1>듺ʲ,^ P>Qi%䬕+ݾ*KsY÷*(a$!\ĒCV OR oA!F&dp6FmX۞dӛV*ۍT6vW_Jk^LjUGR C]jz@:I )Z^'4teS&=t zmUtQ !V P5SZ_ƁSbo<οlυOZSC_ko h}^ivKdRES @&ۊc D)jR8(Nv OrGMzI+'3OR裈V$]i M! NTf !RQ.UF (A@EtXxera`X*L{X/\ {@ǝ0eSD#ɱaK<dѓ[. aT^ #|*6AD|*Z.(Y<_JxT@$\BPt ܔ iZNE.!<# %5I&u!1 \@Tx9$DNjM43ZiDN"Ṟ*bD %@_JzjU@7s`ȖbČ(2^[@M8Xs$p,-QLttb(`ˑ6`\򔘬iG³w~+YpV-۪Twqr"!Ig!Uqѳ0dVPLnE$Icu43XA6(~R̭N$+$8&އI) O5ql`I#LU'I* C%`tɆUjAKu|8m#9cj%_ 6Әy`kb"LLeP WBMcu%j.ł}ny7%ݟP7%vݻrWdݫ|;pR͠^OE9v`Y/B0*oO`+9N+ta"ta@4# X4xQ׃&4; 헧CL3Lژ'" ܣdt8>p}2VrCx^wN2O4&NsOc]Lgxy`g9ݱBB ΂IB PVXI7[jQhzNl*RGzl۩h|bziN'JGIwȉu7:;o u9ZMz!׶3S {'Ŷ(g[-p5ۇTV. 7xu pVVȹ ['%X_{n!1!'$T36="yvK߹2r6<8.kj;ӹBx.r]^+Eu~Cn\"NWҕ*g١WirNKt}E'yR/؀ Tn'ZӮdβ.xv8;BT},! \ϸU;DPBOug|{kn沪i7GyEeަݎd~K(Q33C D⽏ȴBs@s?0 V*$-o{<GR ==x4XNa^$CUSL<$Ov1ZGyJO7_N  F Vi QLDH)]ONRHP|nm`oPT`! VY`O$,YU`LQ>EF0q<H_~ܷ% R\x9r9!WIJݏ@)>Ɨ4! & O!,2y+RQ,ƺ`]SD^D c%1 -#oL!f4ڐ&򚥈DY:0""ETRx0Β!.2.N BIpqS>"?T n1i_Aօ>b4N#b]IddP27j`Fr@G6miq, DS:mS7Fۖ$T*dMMBZ@CO8KuIFUqĢ0FG0!fݢɵ`"^%xF"PF u5"[%U@FY`ID)=PPM)͛0P QZUV^S&_x4If6dy]cB$ HM|VgUVoMn@9iј&UImjɋkGB^H9VoU`iR2 fc`hfpqd"DeYޜ%H8Yd?Nfw*ڿUXU#B\'{{z2e{wzez'+Ч|ytg?痽狽怊g^z!gmdngXjYRhyj}v?zb_|Z5bhy(*(1(s9(SΨ{jg |(x萵(#h(f"`izJx)i(T)֩)ŝ))K+uD*Ri.j*n'@Lj$^*fn*vjj$<*+xN|*~fB6*A|ƪbXOtj*.:%D[<+FN+V.%\(n+vOh+n+D +\++^_Rʫv++VĿB,:'|,ll+-D6D^,&Nl,2ĆPɲkng*Ĭ,֬,lΎ,C,ТA--$>4-V,.ln-v~j-؎-ٖٞ-۶۾--݂֭\e B,ʈ(+mn.m.:B,nBZnBPnJnbނnrzn*jnn2...nVn. o"o./*o2@oJ/:/^/}do>6noNor)xooob///ޯoo/;Cp/#p+?0BrCz0׀p0  L 0}pp  p 0p pp0 q'<1 p q1p[cqk1sq{q1q/q@;PKd=_=PK|%AOEBPS/img/adfns054.gifGGIF87aK?**?***UU?UUU????**?*******?*******U*U?*U*U*U**?*****?*****?*****?***UU?UUUU*U*?U*U*U*UUUU?UUUUUUUU?UUUUU?UUUUU?UUUUU?UUU?**?***UU?UUU?????**?***UU?UUU?????**?***UU?UUU?ժժ?ժժժ???**?***UU?UUU?????***UUU,K H*\ȰÇ#JHŋ3jȱǏ CIɓ(CO@ DPB O@ DPB >QD-^Ę|,h „ 2l,h „ 2l!Ĉ'Rh"ƌ8`A&TaC8`A&TaC!F8bE1f0 <0… " <0… :|1ĉ+Z1?$XA .d!?$XA .dC%NXE'p "Lp!Æ'p "Lp!ÆB(q"Ŋ/b`> 4xaB 6D? 4xaB 6tbD)VxcF H*\Ȱ!B~ H*\ȰÇ#JHŋ3O@ DPB O@ DPB >QD-^Ę|,h „ 2l,h „ 2l!Ĉ'Rh"ƌ8`A&TaC8`A&TaC!F8bE1f0 <0… " <0… :|1ĉ+Z1?$XA .d!?$XA .dC%NXE'p Ao࿂ ,X`,X`'p "Lp!ÆB(q"Ŋ/b`> O~,X` ` ? 4xaB 6tbD)VxcF HP/߾ ߿?O` _O@ DPB >QD-^Ę|@˗>}˗?~˗_|O?~۷>}˗?~|O?~|? 4xaB 6tbD)VxcF HP#?}'߿| /?,8,h „ 2l!Ĉ'Rh"ƌ8Gp_ o?߿ϟ?~/߿?_'p "Lp!ÆB(q"Ŋ/b`> O@O?}O} >_~?'߿O@ DPB >QD-^Ę|@˧ϟ@˧ϟ~'?}˗_| O@'߿|OO@ DPB >QD-^Ę|,h!B  <0… :|1ĉ+Z1?$X"B"? 4xaB 6tbD)VxcF H&ԧPB'p "Lp!ÆB(q"Ŋ/b`> 4xaB 6D? 4xaB 6tbD)VxcF H*\Ȱ!B~ H*\ȰÇ#JHŋ3O@ w 3O?? 4xaB 6tbD)VxcF HA}/O~O?}+8,h „ 2l!Ĉ'Rh"ƌ87 O? /߿}O _|'㧯 A~ H*\ȰÇ#JHŋ3O@ '??/?O?} 'p_? 4xaB 6tbD)VxcF HP࿁㗯? ?'߾|ӗ?7_OOO@ DPB >QD-^Ę|o|O> ?~ ?}O?  <0… :|1ĉ+Z1?$X"D!BO@ DPB >QD-^Ę|,h"D!B'p "Lp!ÆB(q"Ŋ/b`> 4xaB 6D? 4xaB 6tbD)VxcF H*\Ȱ!B~ H*\ȰÇ#JHŋ3O@,X A} H*4? 4xaB 6tbD)VxcF HK0a„ O@ DPB >QD-^Ę|O?~߿~O}` ,X`'p "Lp!ÆB(q"Ŋ/b`> '߾|O߿~_0 <0B8`A&TaC!F8bE1f0o|ӗ?7|,h „ O@ DPB >QD-^Ę|O࿁_/߿8`A&Th,h „ 2l!Ĉ'Rh"ƌ8 ǯo_'p "L ?$XA .dC%NXE'p AO?~ O_ ,X` O@ DPB >QD-^Ę|,h „ 2l,h „ 2l!Ĉ'Rh"ƌ8`A&TaC8`A&TaC!F8bE1f0 <0… " <0… :|1ĉ+Z1?$XA .d!?$XA .dC%NXE'p "Lp!Æ'p "Lp!ÆB(q"Ŋ/b`> 4xaB 6D? ,`>#(P˗| ̗o|(_|/_>}O|߾'߿'p@~O@ DP80?'P | ̗|˗/_o?O>O߿O 'P~ HA H*\Ȱ!B~ H`A} O@~߿ۗ?~>~O @}? 4xaB O|߿@}߾|߿ ߿GP@O|8`8`A&TaC8`A#/@̗/_'0_> ̷`| ܗO@~̗`|O`|˗/_?/_|#/|˗~/_~3/@~~/_̗`|G_>}ӗO`|/~ ?o_ӗ_|#/߿| ̗_|/뗏,h „ ̗ |?}?'߿}߿뗏߿8`|/A˗_>~#(`>'p 'p "Lp!Æ'p G`>'|'? O@̗_|/_˗| <0…#0OO_O@~'p |/_>/?}GP | O@ Dh_| &L0!,hp`|/>8?'?'|'߿?'~O?'?'p?$XA ./|˷O ?O O@ 0 ?O '߿O O|˗? 4xaBӷp… ./|˧O>Oo߿o@~?'߿'߿O'p> <0…#/@~ 0  ߾|'߿ϟ| @̗o|8`A&T_>} .\p̗o |7_|/_> ?'🿁'߿80_O@ DPB̗o |7_|/_> ?'🿁'߿8`>O@ DP!B-\p… G0_/?˗o |ۗ/_}G0_>˗O_̗o ?}70_+/?[p… G0_/?˗o |ۗ/_}G0_>˗O_̗o ?}70_308`A&T_>} H*\ȰÇ#JHŋ3j1|:vرcǎ;vرcLjرcǎ;vرcǎ#˧cǎ;vرcǎ;v/;vرcǎ;v1|:vرcǎ;vرcLjرcǎ;vرcǎ#˧cǎ;vرcǎ;v/;vرcǎ;v1|:vرcǎ;vرcLjرcǎ;vرcǎ#˧cǎ;vرcǎ;v/;vرcǎ;v1|:vرcǎuQGuQD˧? 4xaB 6tbD)VxcF9FO_ǎ;vرcǎ;v_>};vرcǎ;vرcDuرcǎ;vرcǎױcǎ;vرcǎ;FO_ǎ;vرcǎ;v_>};vرcǎ;vرcDuرcǎ;vرcǎױcǎ;vرcǎ;FO_ǎ;vرcǎ;v_>};vرcǎ;vرcDuرcǎ;vرcǎױcǎ;vرcǎ;FO_ǎ;vر:ꨣ: 'p "Lp!ÆB(q"Ŋ/b̨q#Ljرcǎ;vرcǎ#˧cǎ;vرcǎ;v/;vرcǎ;v1|:vرcǎ;vرcLjرcǎ;vرcǎ#˧cǎ;vرcǎ;v/;vرcǎ;v1|:vرcǎ;vرcLjرcǎ;vرcǎ#˧cǎ;vرcǎ;v/;vرcǎ;v1|:vرcǎ;vرcLjرcǎ;vQGuQG/>$XA .dC%NXE5n_>};vرcǎ;vرcDuرcǎ;vرcǎױcǎ;vرcǎ;FO_ǎ;vرcǎ;v_>};vرcǎ;vرcDu` '1~:v0| G0_ǁ&g1߿~:NO_ǎ80_~Ø;&̷0|uد_|]'P_ljQ>~O | ܗ_'_||ӷ/@~߿O_>}/_>O?~˗ϟ|˗?~8`A&Tp>~O | ܗ_'0|߾|Ǐ߿}߿|ۧ/_> Է/?O_>}o_|O} ܗ_>1dp|2dȐ!C'?o?~ O??~߿|70߿|O>?~?~ǐ!C Ǐ_>'0߿}G߾ _۷|o?_}> '0> o߾ '0|o?~ <0@)TPB @~o`/|_>+o`'@ O>ӷ| *TPƒ'A`_> W0|߾|`>o`> o`>O`OB ˧OB *Th0? O?~_> 70_?}O |_'P``>߿//'p "Lp|(_?}߿/_> 7P`>_?}/ '0߿| 7P`O`>o`8`A&/> *TP|_>o? ߾|'0@}O`> _> ~o`O> *TP|_>o?'0|'0}+O}/?~`>o?+}_| O߾ ܧPBӧPB *4?}/_>}ӗO_|O?}O`>?}/ӗ/__>~_~ H*\0?_|/>}˗|'0?}+O?}O?˗/_?}|˗}O`ӧO`~ 2\/> 2dȐ!C2DȯC cȐ!C 2dȐ!Ä1dP> 2OC 2dȐ!| &ǐ!2dȐ!C 2dP> *ǐ!CO@ DPB >QD-^ĘQFױcǎ;vرcǎ;FO_ǎ;vرcǎ;v_>};vرcǎ;vرcDuرcǎ;vرcǎױcǎ;vرcǎ;FO_ǎ;vرcǎ;v_>};vرcǎ;vرcDuرcǎ;vرcǎױcǎ;vرcǎ;FO_ǎ;vرcǎ;v_>};vرcǎ;vرcDuرcǎ;vرcǎױcǎ;v쨣:ꨣ"_>} H*\ȰÇ#JHŋ3j1|:vرcǎ;vرcLjرcǎ;vرcǎ#˧cǎ;vرcǎ;v/;vرcǎ;v1|:vرcǎ;vرcLjرcǎ;vرcǎ#˧cǎ;vرcǎ;v/;vرcǎ;v1|:vرcǎ;vرcLjرcǎ;vرcǎ#˧cǎ;vرcǎ;v/;vرcǎ;v1|:vرcǎuQGuQD˧? 4xaB 6tbD)VxcF9FO_ǎ;vرcǎ;v_>};vرcǎ;vرcDuرcǎ;vرcǎױcǎ;vرcǎ;FO_ǎ;vرcǎ;v_>};vرcǎ;vرcDuرcǎ;vرcǎױcǎ;vرcǎ;FO_ǎ;vرcǎ;v_>};vرcǎ;vرcDuرcǎ;vرcǎױcǎ;vرcǎ;FO_ǎ;vر:ꨣ: 'p "Lp!ÆB(q"Ŋ/b̨q#Ljرcǎ;vرcǎ#˧cG:W0߿|]g_;*̇0_ǎ;vt/Qa>+b~uQa>0_Duر|:rԗ/_~ӷ/| Ǐ|˗߿|_>WP?~O߾O ?~;v4/|O`O_>}篠>} /_?/?|8 A <0… O@,h „ 'p`?$(0߿|?~O`>~|O`_߾˷>>'p "Lp!Æ/?~_>/@~?} Ǐ_>/| O@$XP`} H*\x|,h „ O@'0?} O ?#o_+_o_o8`A&Ta@~'|ȯ`'0߿|O`>'p  'p "Lp!B$XA O~ HP`o_>~ _߿| O`>o`70|O`>70,h „ 2l/|(߿o@߿/߿ _? `>8`AO@ DPB >l/_|O`O ? GP>O`__>㧯| 7pD!Bdo?'߿||ۗ?@~O`˗| B"D b/_|˗|O_|O`ӗOӗ/_?oO`~!B?}O_|/_>~O_|(0|˗O>~/_>~ '0ϟ|8|8`A&TaC!F8bC~XbŊUX_>}+VXbŊ+:w_Ŋ+VbŊXbŊ+VXbŊ+VXbŁXbŊ+VXbŊ+VXbŁXbŊ+VXbŊ+VXbŁXbŊ+VXbŊ+VXbŁXbŊ+VXbŊ+VXbŁXbŊ+VXbŊ+VXbŁXbŊ+VXbŊ+VXbŁXbŊ+VXbŊ+VXbŁ <0… :|1ĉ+Z1ƍ#˧cǎ;vرcǎ;v/;vرcǎ;v1|:vرcǎ;vرcLjرcǎ;vرcǎ#˧cǎ;vرcǎ;v/;vرcǎ;v1|:vرcǎ;vرcLjرcǎ;vرcǎ#˧cǎ;vرcǎ;v/;vرcǎ;v1|:vرcǎ;vرcLjرcǎ;vرcǎ#˧cǎ;vQGuQGE|8`A&TaC!F8bE1fԸcDuرcǎ;vرcǎױcǎ;vرcǎ;FO_ǎ;vرcǎ;v_>};vرcǎ;vرcDuرcǎ;vرcǎױcǎ;vرcǎ;FO_ǎ;vرcǎ;v_>};vرcǎ;vرcDuرcǎ;vرcǎױcǎ;vرcǎ;FO_ǎ;vرcǎ;v_>};vرcǎ;vرcDuرcǎ;ꨣ:ꨣO,h „ 2l!Ĉ'Rh"ƌ7r/;vرcǎ;v1|:vرcǎ;vرcLjرcǎ;vرcǎ#˧cǎ;vرcǎ;v/;vرcǎ;v1|:vرcǎ;vرcLjرcǎ;vرcǎ#˧cǎ;vرcǎ;v/;vرcǎ;v1|:vرcǃ0رcǎaב|:vرcDŽ,رcǎYב|:vرA~`|ӷ/_?}o_~|߿|o@/?8`A&TaC ߾| Է/_?} Է/_?} /?~O>~C{Ç>|}۷}󗯟|󗯟|Go߾ _>|Ç /߾}ϟ|ϟ|_>˷O`>~aB=|Ç>|/@O`>O`>#o_}o`>Çz!?߿|/߿_߿ۗ_߿|? 4x@%L0a„ &L0a„ G0| '0| '0߿|맏O`>~&L0a„ &L0a>O`>O`>+_?}70|ӗ0a„ӗ0a„ &L0a„ &LxP>/| '0| /_A}}O`%L0a„ &L0!B}ӷ| '0| '0߿|O_O`>0a„ <0… :|?}/_~ '0| '0߿|_>O`>1bĈӗ/_?'0| '0|Ǐ ?}O_|O` ˧/bĈ#F1b#F1bĈ#F|"F1bĈ#F1bĈ#F1bĈ˧/bĈ#F1bĈ#F1bĈ#F|"F1bĈ#F1bĈ#F1bĈ˧/bĈ#F1bĈ#F1bĈ#FC˧? 4xaB 6tbD)VxcF9FO_ǎ;vرcǎ;v_>};vرcǎ;vرcDuرcǎ;vرcǎױcǎ;vcǎ;FO_ǎ;vرcǎ;v_>};vرcǎ;vرcDuرcǎ;vرcǎױcǎ;vرcǎ;FO_ǎ;vرcǎ;v_>};vرcǎ;vرcDuرcǎ;vرcǎױcǎ;vرcǎ;FO_ǎ;vر:ꨣ: 'p "Lp!ÆB(q"Ŋ/b̨q#Ljرcǎ;vرcǎ#˧c;vرcǎ;v/;vرcǎ;v1|:vرcǎ;vرcLjرcǎ;vرcǎ#˧cǎ;vرcǎ;v/;vرcǎ;v1|:vرcǎ;vرcLjرcǎ;vرcǎ#˧cǎ;vرcǎ;v/;vرcǎ;v1|:vرcǎ;vرcLjرcǎ;vQGuQG/>$XA .dC%NXE5n_>};vرcǎ;vرcDuرcǎ;vرcǎױcǎ;vرcǎ;FO_ǎ;vرcǎ;v_>};vرcǎ;vرcDuرcǎ;vرcǎױcǎ;vرcǎ;FO_ǎ;vرcǎ;v_>};vرcǎ;vH@~,h „ ܗ/C8`A&TaC!F8bE1f`> 4xaB 6D? 4xaB 6tbD)VxcF~ H*\Ȱ!B H*\ȰÇ#JHŋ3O@ DPB O@ DPB >QD-^Ę|,h „ 2l,h „ 2l!Ĉ'Rh"ƌ8`A&TaC8`A&TaC!F8bE1f0 <0 " <0… :|1ĉ+Z1#?$XA .d?$XA .dC%NXE'p "Lp!Æ'p "Lp!ÆB(q"Ŋ/b`> 4xaB 6D? 4xaB 6tbD)VxcF~ H*\Ȱ!B H*\ȰÇ#JHŋ3O@ DPB O@ DPB >QD-^Ę|,h „ 2l,h „ 2l!Ĉ'Rh"ƌ8@} W` ,XP࿂ ,X0?$XA .dC%NXE'p '_ ,X`A ,X`|8`A&TaC!F8bE1f0~ׯ o?'߾ Ǐ`߿~O ,8,h „ 2l!Ĉ'Rh"ƌ8@}W>} _>/_}O~?~>}O߿~  <0… :|1ĉ+Z1#?$(߿|#?}_O Ǐ? _+Xp?$XA .dC%NXE'p A /_|/_|/߿|@˗?˗O`|˗_˷~ HP?$XA .dC%NXE'p |@~7?}O_>~'߾|O~?~珟@_'p "Lp!ÆB(q"Ŋ/b`> o?}߿|/>|/>˧߿,(,h „ 2l!Ĉ'Rh"ƌ8`ACp?~"DP?$XA .dC%NXE'p "D@ H*\ȰÇ#JHŋ3O@ D0> *$? 4xaB 6tbD)VxcF~ H*\Ȱ!B H*\ȰÇ#JHŋ3O@ DPB O@ DPB >QD-^Ę|,h H*\ȰÇ#JHŋ3O@ w 3O࿃ <0… :|1ĉ+Z1#?$(_?7/_O?~O?~߿߿~ O@ DPB >QD-^Ę|o_>~㗯_? _?O˧~/߿}`_8`A&TaC!F8bE1f0߿+O O/?o| '>?} O@ DPB >QD-^Ę|o? 'ׯ?~|`O?ׯ@ H*\ȰÇ#JHŋ3O@ | W?} '?| /߿+/~+8,h „ 2l!Ĉ'Rh"ƌ8?}7 'P O>o|#/>Wp?$XA .dC%NXE'p !B"D8,h „ 2l!Ĉ'Rh"ƌ8`AC!B"? 4xaB 6tbD)VxcF~ H*\Ȱ!B H*\ȰÇ#JH3O@ DPB O@ DPB >QD-^Ę|,h „ 2l,h „ 2l!Ĉ'Rh"ƌ8`| gP> 4hРA O@ DPB >QD-^Ę|,| gРA 4h`A H*\ȰÇ#JHŋ3O@_~O`|W` ,X`8`A&TaC!F8bE1f0/_ӧ|O?~ ,X` ? 4xaB 6tbD)VxcF~ H0߿'߿|'߿|'߿ ,X`  <0… :|1ĉ+Z1#?$߿|? ߿ ,X` ,(,h „ 2l!Ĉ'Rh"ƌ8`~??'?,X` ,XP?$XA .dC%NXE'p AO O O>+X` ,X@ H*\ȰÇ#JHŋ3O@ DPB O@ DPB >QD-^Ę|,h „ 2l,h „ 2l!Ĉ'Rh"ƌ8`A&TaC8`A&TaC!F8bE1f0 <0… " <0… :|1ĉ+Z1#?$XA .d?$XA .dC%NXE'p "Lp!Æ'p "Lp!ÆB(q"Ŋ/b`> 4xaB 6D? 4xaB 6tbD)VxcF~ H*\Ȱ!B H*\ȰÇ#JHŋ3O@ DPB O@ DPB >QD-^Ę|,h „ 2l,h „ 2l!Ĉ'Rh"ƌ8`A&TaC H*\ȰÇ#JHŋ3jȱǏ CIɓ(G;;PKe~GGPK|%AOEBPS/img/adfns055.gifRDGIF89a???}}}@@@[[[KKK|||tttvvv򔔔훛𱱱~~~CCCԁ⫫zzzυ>>>BBBwwwŵ似xxxAAA{{{qqq___ޜbbbmmmHHH媪ʇDDDaaaFFFXXX===jjj///RRRrrrաTTT\\\ooonnnӏ^^^sssMMMSSSyyyggglll888kkkYYYuuuQQQ;;;iiiPPP]]]***```JJJLLL<<<000dddWWWVVVppphhhGGG:::222UUU444cccNNN...OOOfffEEE111999333)))666'''$$$---IIIeee555### ,,,777%%%ZZZ((( !!! +++ """&&&  !, H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲeo)^)N@dPJ f\4b2s&4NPX(eHSö>,*XWBmJn]vLJ} ,9 7Ek2X Rv68S# r(3sgdy^J`|b]>EEx ' * "Ī)1B:iF {!P?=EQP=gH(X Eh TlFSx\6#Ph[فA١@OA@l!] :0WPUHP%݋ E*C;uSCp?t4Ba f2FOc?psMpH] J pQ5N| %O(7̅0*(i1cN1?<=9PEuC?o\7D?~5OᏭԵ?KS1c %O0N<ɄN"L]GG:u}O*ui=f-u!(*0Ad>D!H*=.$O19d!A9?rXq ,O\@L(?[D"n`RЅoo68t}Gl`_ F.1eQJc`?t -@4&kB>8MZ)7yX?^ r26!e1YF@B0у8P,s`p:o葏~El0bP8@q2؊@FtLj"|(q넑u@8 p%}_/;V?yFBg(&  xh 'd v9#"\a *dC-a B3ô ?c(H PF,r.)X,([?@FTAgqܩ2^? AZBhG.X\ ,Ie(x~H (@%eChX7!s–_եXK'`B vU<>qL#B+C6F/ U#G` {t!0A" l Ƞ R lb昇d0G>qgpsh?j1$2 `O԰9 ,~A,!2D eă1 5n0pp, ר$VAvu3` l @; C F?;DJT 1d@ @Ġ hY"Ђ.X"*Z-` h2BoHC+D!>?( A00 bpBZaW(b4pJ6PC8.1n8\"%фA 6 0MBD.rbIPA?a" C8eB P w?qL@a L.Zhc{: .cD")D(NO;񐏼'O[ϼ7{GOқOWzgOϽw;s? zOO~]GЏO[ϾodOs~Z?'~$w?_'qxG7 y ya~#׀xQ,X hy-6(3Xy5@=Hy?X!ׁJG8yI TQ(yS a^[y]8'h()x+؆iHNPp0z(#pGm` 0 Zzp50@(p %?p ; < Q Poq(dž H[P)f&0 _l] P (  0@@ <X upaF0p2 P? %QVPJF sH"HH&FPB\?s@&YpH {$*$NBVl&0C p e@Blɐ -IBwP I$`03 00NQ1`&NP!| 1%)pDe0 !  qǐ<UD FQ/0+p .}s "@5` pgAf P  !NQw1bPa 0#! mt e | - 0##  ΀ e Ag u-3 J   \+_2 (h Sp W@adp00a,3RSpp`CiPBaIUl0vQk e0xqٓN Fi3?N5Qh4iAh91@pq9Qj$I{ aZjhC& ?fp>*X   0v*6* `4 `pU B <wPI!m P`Z:q& ᭆ P P@@ @]PjR瘎؎.b=P0,P9C  ,qǶ p  N i i0NR@x:A?@.JPdA` 3W5G{SP Ӑ 4m ) !B$@ ϋs;0ҭ9ڄvHxZ#ۚ`ZPh> R @  ^ G` 'p P .P RP31xp>n*Z¸/P1"26 xp u+Tп: s .`)\+,ihkqpQ 0f.p ա#Hܪ^P8z,xgBx+(K2` `[ѩ m@.*Z ; T@: R ?!Lepj*`Fx=)z#@*WvE 5xь@ր 1W } hZ0 }0 P{ ְ!: 0 Pq b K$b N@ǰ d,0 smҋᗀyR`.%u?)yxJ( ,0MxxB@7:0p=x텿=k=&Q؀ \`PX F6t@[F`RP@./@[v`tNZȑXpFM l`VP  ;P ` C\ }@ z,pZ@ [p c B}xŝ>`.0 ʆb`I@ro@p@] QPJq? p+VVWV pl+ɒIı8@ =0>ؑ+ z@qPT ^swuz; 4ƀJ \ ZEcne !t03E @ `p4 ! l5nx7K@ PhP * /0$ ̰ L $f`  b ~ (n ` 0 nD P$ m0   >i ~˽6eZ 4\`be^PU  ) s  0,5p w^x<B.0 Q 0. >c` pV[' ׮y@M3P`8p 9cK #,0#㔵 -1ߚ|1v)O Z v3 0` @:40ODU/ZJz0nYNJAO0m k`_@ pp И aA .d/ %NXE`ĨRv)@򏌏 -y'Ysp3=5ZYIǿ41vpNE\s?0 h8 h[q ,8w"G,h!M63̾4 Ĵ&JS+*Qx9?1FQs. 81& #A&(.Qv+>o+"Xmt"'!909 j tU]Tݙ%>,?paƞ A1l$(\``daBG a&mpCbNl!) BdS8>PqLIJ !m脚,*@D1H4Q5b86v('ɍCxsL#"T:sM☁ bsN<>#3'FހeD @ ";3p7`'FHXGbФ\ HtrI$'N^)2׃lV :@K7 u";9Y*I 'bAJQcf~8gp/ vc|CQ"bp.@ T0H_2dY Y 781a%5? 2d]}W<˜4bvY{6( 0 "D0>zP6"kO؂* ^m$R tP_86 ㆗A&: )g)p!.@o  ;X}f3יg͟sЏL~&nf@By&Kc>x%Rύ9(#C\4>_H$`a 2A @D ! @T聆IE``pԛX 6p V߉ 2 Ap &-d4E2P50 `@AePO Bt IxRUt 2=-G9-^ = 0Q\XJ80 K `,HAW%~J H@ !tW0!:,sA YkF'B7E,UCPUtZ [ x >r,Pe)b(ЛB 1X"@ ,("FHB1kQ!PL@\ hX8R Gp> Vh-t7wVJo(P nyΟ5\Ch7x, RӃE7OQc9(R d~aﺮNʶnj& 14lhLP ReDK"C cu?w}!4,S,A &]` zp@y|TATL 02@ DZ:8B2"L>R+U]X*@ @)hp\O0px94P4Pd@x+;LO\OlO|OĀ6@QOO$JB S L 膜1Opp%en@;PȀ ( Bp)X |0bJXoŔi5 5 @'ـ O1΅%#0Ih5!BP+p`Gap_3Axl.Fdn$H@9Xh.%p=Dx:pTKTLTMTNL%,NH ,US@;@ pHPFHPYPz-s Jۂcgb0(XY Ix8QTH% *" x+"45 qxL)JTT;MJCspN8]x_UP*҄_n` 8(=f1TxVԔV P 81hn ?0?:KlCr"S Crhn 4\6|CDKpQ(9C=LB 5שFx2L=X4*`PU8)p Byx H5i52VpDXxY+;X&:2D@xWיsJ Ёp(XUuxX !Hyh(pB:! TUY(3a(\0@o8ܷ\덋 pF0 <[_JB(607 h؂=.ЂGȄ (m{( `Wˡݐ` %1114,豽Z6Ɓñ8 a=6 Hj.29E1 :(L%p[ (E gІ9 Q@b(ͮ;M5Ybi$ I` :PI x0EE_`K07l<@6Hxc$pPPȄQSdH]>CU188  C]v0j6gx0 (ۊ; B0A$(J`e-C) bH 70 Rxւ 6$ @!)(a5bH73ppn0jD(` E<=Np IFob?0%1!P;0ʓlc4^HUp3g`eqe' §I~DiY3Y䷨/XB>X57(`K5< טC :+"` H2%hPYҵA6'x()^(eXd8gP]eI+0Q|$bh x3HnG& )H؅4 x9;0a.0kp~[p-8c,y=/KpX `pHX 6x2&m> 0&(2hgǣKBhnHR5"1P(S蘡2\H7 ?9g`p(  Vpq&Ph8d ([8kPJ8~+HœO?oO NOC/1SHF ]%<8V]H5 VX-bȆpg ^X.V:@Ew;(EuO X8C F]hg_%X6[8&`% EO-wL pF8SuH; 0c/tp{9@f@vp1h 7@8rg3h0N |*@vvp2qQH(Wv~88Ypzn=@ X@J Fi {!wx`8Ă QX&`RX P >ȄXdP'7&ȄL>+X0HVDI@j=&B;MHCz}Whr oׂt(}VM q!\O1Rh=M {25 ߨ/h9Ѓ,-H!BLK&I4:PO:U^0,h?P^p2RH(V7Lǐ"G,ir'Wl%L 4p b_mqPT7H w ۸,-[(2H%3@wbfaV9$r: aGs՘/ά9x# J5 :@`"4:lQ1B q֢U{8Z# 85ɼgK6nsId 7 8mЏi4Ky:x<ӆӒoot B>Be֤C,B)P'3/2@*@L+M7"W"`!2A@@ A0A 5P0I~cl2k#Rye_'b A,F?AأII@]e@KDlڔf^\,( Ls+b&E$ʨz hIZR90 P 0L?x@;]1AF8WulllAtQ]42A8`, 6xăQ0F 8l<` LBNhDQ Kh@A AtW8A2H_ #,@9P?JpNp]wCw)E:\`/ Ќo   }KC%XA`؈ _A@ JL`4Ą0$p5``]p?dH4 Y|qc hd19$T%J H;Q:f~RX)LȀ0] ^ \: T }/#_μB&4 alAp2ـ p1*ӁvrpxC'p[܆Xl2Re*8l?ЀM$ `1X Oy:U>%_s.Єv%EF/h& 8?(!zlO0` }354 nmA ]^P ,q, r*#w4aJN`'4G .xEzPA.8 ,t@o Ӣ4K. kt1 ^ \!&v( `a Ѡ毥laAIj0Td/Ё9$X# 34Bn%qЅ(ybxB"2 $pV ,@ !$P rG?Y?Aa|%t!H4?0T`xu8.3)]/&I.`dKo:C xa 'h6WAhAP?=Sճ=c/٫^)I&~}ў ?̀@DA @CA!P@8 0 @,?L3)A$U5 |fL 0DLAKH%r*@B.!6>!ŽC),Ȃ! E͝ C B yYvA$LDl_ "!!.G,B#<#>"$F$N"%Vcu@1* VaI\@̀8@!A]@lAHH@WAܖ <=W @YKDܳ|?)LAxHt@\-h1H "@#%3, B-J!$B X5X<3C2PBH @3AH,Hcx` 23 B([.| ($EBK FPcF E Fd`4$|7UDAz$LFZH^ФF1nfd ,?X j`BQB NOJO*@3l8DB腴-@@. ,, dBWdeXX>KY"!??PX@4PD?TXT/H dCcBBfddJee&* C \$Dhqi"3 ʌ-yeA4B"%!+C4P(A/B"5,MvA08 A!,$3G,d&A % hPp@|1@tFdfFrAt,BBD`p`š #ʅ!lfg60΄B&lDt@l@DR~0؀#td$l`@A4)LRYA [%\%]F"$^"D1fa&br@(| D ?vOė.K9Rw0@<Le d18~ J1 P Q%R*jDe8%T& %hAFLVVrm@1~@*cN٪xPLADЪD B*D'թ X@۞VI+(* biAjP0.ЯrRF lE.n5Ö~λ )dCPB69lAtB{/4X=t%0:`p@>@ ("DC8DBC.Cd,C+? tDŐDZ ̪xm-rDnF̬"bI>A08.2lڮx&,@@> فCҕr, Mv nanC^ -|<: A -C.y8 )wB?" B7@'ȋ P "ÖKo` /4X^, .L&@2|(L?(xJ%D *#f.FIj&w2$J0r$-$B< A ;A B9=B|,Ā,-Z# dAA( @ t+`4b D`#5;X'+=5JCl7 ;pDA0hHpX J,(A* lE@4*| @1rDŽ7Ԑ@"*08C%< Sԇ<d H&N2~HԶ~O /|" b"@ 9(؄ Q((\R3Št 4`%dS Z"%(!G(u]!ת%<` >Jn)(G. H[2)Wq1ğ!X;Xؑق¹a..ȷ9Ήǒj*baЀ@D *A z $["#lA$_6$ ]$(`Xgg~B g )dg&:) [#aqqc:dkMQ{E`b'A0?6 @m(Y >Ё! b I bHB 2!T!!@ TD#IT"ڀQ@]QbdFqE A8D(0hȆB\`Xǿ-#f_0FH>0|H <$)YI! D IO~ғI ҋEH?HȢ/$AFEH@4F iRN6sZ"WsH&RT`ЁQZua?n9.0IL3 !0! XO 5OM{15P, rl5H b jQL|C@ ,D!<_J:. UH W`%~$`,A "JE 0EH(1"ҡD|&N SӬݔriBPDNv |+*B4N@ `Fܠ F&d@ ]l4yu5Ю7#]!QB}GP ԧtPMuS*Uwa%1FuDY/b: % зc۲. _1AZ"jW:BPQDC ÂZlЀТF *cĂ+zC6# P@-!:#۸byd ?@`d # !x4S*=SP |!p^'{'ZJa`0 9% a t B` Ta ?, "ؠ !f$ E( (lb=n>S?"> 8ANtL(`Lls?#4 hƀl .D@8 @C TB[!F0` 3ST `[v6AFI\>IxNApv@vG >́ 1'b!;4BIZ`'q` 8x ` t |>a~:jNt:` pMrra6` arX@^b `XTܴ!bN ,!Q b@l`ET"*aAjS!! x``@@ae$a6z) ~ @ X! l"B1TC H^@Ԡ *a .eSVeWe[V!!!T@D!0 USY ^hAB:^@`HpVAa @Zk"?v|x AZ#Aj h I A>u_Z v`8 zHlaX8n*t@>pp@M.ij/7s7v  `N^" pWNUwQ|b$ r@*@`$@yAsTޠ {I{r 2" &$ >@  2aAw xAԠ & (V Hb" 2!JXQ8!XԈuN=uXm=Xe.;8Ƙӌ X&؍LX0)T<A 0 ِ6;PKRRPK|%AOEBPS/img/adfns065.gifM7GIF87a?**?***UU?UUU????**?*******?*******U*U?*U*U*U**?*****?*****?*****?***UU?UUUU*U*?U*U*U*UUUU?UUUUUUUU?UUUUU?UUUUU?UUUUU?UUU?**?***UU?UUU?????**?***UU?UUU?????**?***UU?UUU?ժժ?ժժժ???**?***UU?UUU?????***UUU, H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲH$XA .dذ ,h „ 2l!|,h „ 2lX,h „ 2l!D H*\ȰaA~ H*\ȰÅ'p "Lp!ÆO@ DPB >0#FP#Fa>#F>~#F1bĈ1bĈ1bĈ1bĈ1bĈ#F0#FP#Fa>#F>~#F1bĈ1bĈ1bĈ1bĈ1bĈ#F0#FP#Fa>#F>~#F1bĈ1bĈ1bĈ!|O@8`A&B *TPB *T0? *TPBSPB *TP|*T/_>!̧PBSPB *TPB PB *Tp>~ *TPB *4ϟB '0_>O| /> OB *TPB *TϟB *TP'p "Lp!Æ <80_> 70߿|/˷!B <0… :|a>#F>$XA .daC H(߿_'_>}'p ? 4xaB 6t|"F1"B}8`A&TaC O@ /@}_˗O߿~ۇ!BO@ DPB >0#FP#Fa>'P>_ ׏>E_Ĉ#F1|"F1"B}"F1bDEt/}˗oӗ/˧_ĈE1bĈ#:/bĈ#"/bĈ#Ft_Ĉ#FD_Ĉ#F1|"F1"B}"F1bDE1bDE1bĈ#:/bĈ#"/bĈ#Ft_Ĉ#FD_Ĉ#F1|"F1"B}"F("r|8`A&TaC9tСC:t0?:t0>~:tСCsСCСC:tС|:tСÄ9tСCϡC:LC:tСC sСCСC:tH0?:t0>~:tСC*ϡC:LC:t!|:tСÄ9tСC:t0?:t0>~:tСCO@ DPB  <0… :|a>#F>~#F1C$XA .dذ`?$XA .dC1bĈ1bĈ#F1bĈ#F1bĈ1bĈ1bĈ#F1bĈ#F1bĈ1bĈ1bĈ#F1bĈ#F1bĈ1bĈ1bĈ#F1bĈ#F1bĈ1bĈ1bĈ#F1bĈ#F1bĈ1bĈ1bĈ#F1bĈ#F1bĈ1bĈ1bĈ#F1bĈ#F1bĈ1bĈ1bĈ"("("("( (?$XA .dP>~:tСC:tСC:tСCСC&ϡC:tСC:tСC:ta|:tСÄ9tСC:tСC:tСC:lϟC:tP?:tСC:tСC:tСC sСCСC:tСC:tСC:ta>:taB}:tСC:tСC:tСC6ϡC:LC:tСC:tСCr!rȡ? 4xaB 6C:tСC:tСC:tСÆ9tСC sСC:tСC:tСC:t0?:t0>~:tСC:tСC:tСCСC&ϡC:t @~,h „ 2lX,h „ 2l!DE1bDE1bĈ'p "Lp!ÆO@ DPB >0#FP#Fa>#F>~#F1bĈ1bĈ1bĈ1bĈ1bĈ#F0#FP#Fa>#F>~#F1bĈ1bĈ1bĈ1bĈ1bĈ#F0#FP#Fa>#F>~#F1bĈ1bĈ1bĈp_|%`Q#F1bDE1bD'p "Lp!ÆP_~0Ç=|hP>|Ç{ÇÇ>\_|/|˧O | /?O`>~ ԗ/?}˷o`>~/_}{Ç>|0>|p} H*\ȰÅ'p}O`/߾˷_>~_/>/ |oA <0… :|a>#F>$XA .daC H?}o`>/>~'П| '0_>/| />~$(P?$XA .dC1bĈO@ DPB .?70_O`|˗o_|O`> '0߿|o`˷ AO@ DPB >0#FP#Fa> _>}/|o|/|߾|'0| ̗>~#F1bĈ1bĈ1bĈ_| />}˗ϟ?}O_|O`>}/|˧ϟ|ӗ/> 1bĈ#Ft_Ĉ#FD_Ĉ#F01>~#F1bĈ1bĈ1bĈ? 4xaB*Tp>~ *TPB *TP|*TPB OB *TPBSPB *T8P? *TPB *TPa> *TPB PB *TPB)TPB *B *TPB *T0? *TPBSPB *TP|*TPB OB *TPB *TϟB *TP)TPB *T`> *TPB PB *TPB *,h „ 2l(P?:tСC9tСC sСC:tPa>:taB}:tСC СC&ϡC:tСC9tСC sСC:$@,h „ 2l(P?$XA .dC1bĈ1bĈ'p "Lp!Æ'p "Lp!ÆBt_Ĉ#FD_Ĉ#F1bĈ#F1bĈ#F4_Ĉ#FD_Ĉ#F1bĈ#F1bĈ#F4_Ĉ#FD_Ĉ#F1bĈ#F1bĈ#F4_Ĉ#FD_Ĉ#F1bĈ#F1bĈ#F4_Ĉ#FD_Ĉ#F1bĈ#F1bĈ#F4_Ĉ#FD_Ĉ#F1bĈ#F1bĈ#F4_Ć($` ,X>~ H*\ȰÇ#JHŋ3jdƂmXP7nܸqƍ7n0#/_}˧o>>~7nܸqƍ7nܸa> 7_>~o߿|/?m<ƍ7nܸqƍ7ndƂ ̗o`'0߿|ۈP7nܸqƍ7n0/| ̗O`m\ƍ7R71|7nܸqFm,/}ۧ_>~|>8`A&TaCh0D!B"D!BDܗ/>˗ϟ|˗oD"D!/_> '0߾|ӗ/?O?~ۗo@~"D!B"DA0D"D!} '0߾_>~ ߾} "D!B"D a>!"D!B$oo`>} 7߿| 70_>~A"D!" R|8`A&TaC9tСC/o`/_| 7`>'p "Lp!ÆB(q|(RH>~ QH"E_|o'0߿| } "E)RH"QHbC}Ǐ"E)/_~ӧO|/_~˗߿| ԗ/߾Ǐ"E)RH"EQHbC}˗/?)RH"E)RH"E H"ņ0@8`A&TaC!F8bE1fԘϟF7| ϟ>}ӧ߿ϟ>}ӧ߿ϟ>}ӧ߿ϟ>}ӧ߿ϟ>}ӧ߿ϟ>}ӧ߿ϟ>}ӧ߿ϟ>}ӧ߿ϟ>}ӧ߿ϟ>}ӧ߿ϟ>}8`A&TaC!F0'N?8pO ϟ@~ ??'?'P߿O @ @}??~'߿OO ϟ@~ ?? 4xaB 6tbD8qĈ 0'߿OO ϟ@~ ??'?'P߿O @ @}??~'߿OO ϟ@~O@ DPB >Q|&N81>~'p ?$XA .dC˧ObD$XA .dذ?$XA PB *Tp>~˗/? *TPB *TPBSPaA$XA .dذ ?$XA PB *Tp>~˗B *TPB *TPA)T |,h „ 2lX,h „SPB *T8PSPB *TPB *TPB H*\ȰaA~ HOB *TP@}*TPB *TPB "ϟB 'p "Lp!Æ'p "LX0? *TPBSPB *TPB *T0_>} *,0 <0…  <0a|*TPB OB *TPB *TP!|)T |,h „ 2lX,h B /a„ &L0a%L0a„ &L0a„ &L ?~&L`> 4(߿;w O@ D(0> K0a„ &Lp>~ H*\ȰÇ#JH |,hP;;x'p "/_}K0a„ &Lp>~ H*\ȰÇ#JH |,hPo|7@/?~?~wA8`A O} ,h „ 2l(P?:tСC:T/>'p O߿ _| '_>~O?~wA8`A O|/?$XA .dP>~:tСC:t0_>}O@ ?/? 'O_ H*\Ȱ@}:tСC:tPa|:<0 '?߿/?~:tСCsСC8`A3߿~>˧߿'_>}' O@ D(`>'p`> H*\Ȱ@}:tСC | ܗӗ/?}/?sȐ~O@ DPB O@ D(0_|0a„ &L0@}&L0a„ &L0a>~߾}_>| /a„K0!B$XA .dذ ?$XA0 &L0a„ 0a„ &L0a„ O }@~%L_|&L`> 4xaB 6,? 4x@ 0a„ &L0@}&L0a„ &L0a> /߿_}o@'p 'p "Lp!Æ'p "LX0? *TPBSϟB *TPB O ?_>~OB *40 <0…  <0a|*TPB /!|*TPB *,O?}O_}/_|O_|S?*TX`> 4xaB 6,? 4xa‚)TPB *A~PB *TPB ܧPBӧP‚ H*\ȰaA~ HOB *TP@} O@~ H*\ȰÄÇaA$XA .dذ ?$XA PB *Tp>~'p~߿~O~߿~O~߿~O~߿~O~߿~O~߿~O~߿~O~߿~O~߿~O~ HA~4hР H*\ȰaA~ HOB *TP@}0'P߿O @ @}??~'߿OO ϟ@~ ??'?'P߿O ?$XA O@ DPB O@ D`> *TPB '|@}??~'߿OO ϟ@~ ??'?'P߿O @ @}? <0… :|1ĉ H"ņ08`A&TaC!&̗O_1bĈ#Fa>#F>~˗/#F1bĄ/_>}#F1bĈ#2/bĈ#"?}1bĈ#FLϟ1bĈ1bĈ1bĈП>~#F1bĈ#F1?~E$/bĈ# /bĈ#"/bĈ#F1"AE1bD/?}O}ӗO_Ĉ#F_Ĉ#FD_Ĉ#F1bD~ H*\8_۷_|O`>~ ǐ!C 2C 2d0>~ 2dȐ!C 2d_|̗OC 2L߿~_>~ '0| 2dȐ!Á1dȐ!C ǐ!C 2dȐ!C ׏|1dȐ!Ä /߿(߿|/,h „ 2L_Æ 6lP>~ *װ!|| kذaÆװaÆ 7P?}O?'0| 6lذa|6lذaÆkذaC k(0_C6lذaÆ 6loӗo_O`>װaÆ "aÆ 6l(P_|˗ϟ@~o?˗_A}/_>?}_|˗?}o_|ۗ@6l>}8`3hРA 4hР  <0… .ϡC:LO ?~_>_} W0>O`>~O| ̷o?/@~:\/>СC ˗/?:t!|:tСÄ9O`>~_>~ /߿}ȯ`>O`>/@O`| '0}:\/>СC:tСCsСC_> @ _7P_߿|/߿>~?_? 4xaB ׏!C 2dȐ!C 2d(0? 2dȐaB}'P?>~?}'?o?O`>/ '0}O`2dȐ!C 2dȐ!C 2dȐ!|2dȐ!C c80@O_|ӗ/?ϟ|'0?'0ϟ|˗O>~ϟ| '0߿} 2dh~ 2dȐ!C 2dȐ!C cȐ!b? 4xaB 6tbD铘/>%J(QD'QD'QD%J0_>}'QD%J(Q|$J(Q@}$J(QDOb|$J(QD%JϟD%JD%J(QD%J(QD%OD%OD%JD0 <0…  <0… :|a>#F>~O_>~?}/_>O| /#&O@ DPB O@ DPB >0#FP_ />o߿~'_Ĉ'p "Lp!Æ'p "Lp!ÆBt_Ĉ#FD| /?O} /|/bĈ 8`A&TaÂ8`A&TaC!:/bĈ#"a>0@ۗ߿~맏|/߿'p "L |,h „ 2lX,h „ 2l!DE1bD='0| ̗?_ /bĈ8`A&TaÂ8`A&TaC!:/bĈ#"a>O_|˗/_?/_>}ӗo_/bD)O@ DPB O@ DPB >0#FP'p_Ĉ˧B$X`38AϠA  <0… :|a>#F>~#./_>#F/_|O@ / gРA 4hРA~ H*\ȰÇ1bĈ1bĈ# 0@(`> $@~O?~ _?'߾ '߾ ? 4xaB 6t|"F1"B}8`A&TaC o߿$Hp`'_?}o__~/?~$H`>~ H*\ȰÇ1bĈO@ DPB *a}/߿_@} ''߿|" <0… :|a>#F>$XA .da~=$OO?߿~7߿!?$XA .dC1bĈ1bĈO@~7pO_>}/~O_O?~珟@8p  <0… :|a>#F>~#F1@ |,X߿ _?O>ӧ߿O?˧߿3h`A~ H*\ȰÇ1bĈ1bĈ# /> H*\ȰaA~ H*\ȰÇ1bĈ1bĈ# O!|,h „ 2lX,h „ 2l!DE1bDE1bĈ8`A&TaÂ8`A&TaC!:/bĈ#"/bĈ#Ft0 <0…  <0… :|a>#F>~#F1C$XA .dذ ?$XA .dC1bĈ1bĈ'p "Lp!Æ'p "Lp!ÆBt_Ĉ#FD_Ĉ#F`> 4xaB 6,? 4xaB 6t|"F1"B}"F1bD H*\ȰaA~ H*\ȰÇ <0…  ? 4xaB 6t`> 4xaB 6,? 4xaB 6tC$XA .dذ`?$XA .dB$XA .dذ`?$XA .dC%NXE5nG!E$YI)UdK1eΤY&M0_|o`>nޤ͛7g_>~Ṁ> 1͛3W1͛7;د|˷/>˗ϟ@~ӗOA}_~} Է/?'0͛3Gp_>~˗ϟ@}/7orܧ?߿}/@~/߿|_ //߿| H*\ȰÇ#>~/| _>$J(QD%J|؏|_@~ /| G_O` '0D%J(Q"}'0| '0|I(QD%JП '0߿|`>O| 7>O?~O`>'P8`A&TaC!F<0@/߿8`A&TaC!F8q`?}_|o߿|ϟ}ˇ> '0߿|/| O?G"E)R/@}_>}/|QH"E)Rt/_|˧O?}?}O_|/A~O`ӧ_>?}"E)R8q/߾|O`>G"E)RH"|QH"E)RП?(RH"E)O,,h „ 2l!Ĉ'R/_> 1H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\1 ;;PKfUR7M7PK|%AOEBPS/img/adfns040.giftpGIF89av???@@@000Ϡ___ OOOooo```pppPPP///!,v'dihlp,tmx|pH,Ȥrl:ШtJZجvz0A(*;D`P"XD ~/< o9%}(l oqn  $#n"4"  ҉n""uk"(`60P%a; as2jȱ{e|8 q S~@CBD$dxt.v(|ȳϟh7GaE.*' ʵWIBE(6 1uyP?Hv6˷/2t" H@^Y/˛ϠCf S^Z! [˞Mvbͻ12e.x8 ͼs-( `xT>0AX  @ (|v-T"T@ѱ@ރFx#PFi7]ZdbvPLBHf|tT7@6" q:M&!x@oRGB:2^H# H0򑘔!,9̤([0h`eC9V_xJW򕑬Cp򖷄%_ӕB0LHge <Ĥ4Tt8] P sH 0tsŒi4vrA*Qp )6@6B %8Ɣ4qi\M0 Sh< ⳗp)>ц~`)c 2N Ш<@H04S*TSt QA/*UR5ժDJϲf5 7d@PѨHW=Ŗ%cM@̌V@QJUSЬ0"he x@h +Ύ#TjiKOi`(|H} r!=(MQ; %":ՊEh<]m6#@NZ͊2P5na΃Ee(yʁ VgF;\{FAM8]&VryI(^ְiL) rW+YG4[,\Z?Āw7ݼ6=0`LoT)1`( vgyor<j`'G K|Y,>r8KD~)D@mc8>#7Ix^V(tayR3j T3'+'A{Ó.o5-i! 2pLrYd1%GЁr k=@Y`ԨJml؆Q/mQY~Uv>&f,}Wú4(ۻDi1U5 Uj;'@;ꦪ"#ƅ7u5#^j.hjV4Be 瓓0CW rA&jnﰡx R>ҙ.=@Dԩ;`Ⱥևuz+/Ϡ]AΓkg;sFB6a\!N+pb?'.Ml@? `qOKyGsob3Cu(=3@ϓ@8u*o|ϤbWgYGv\#!S5oTَk$;Pj땾.6?рؑwA_q@b0ǕP`,P@ŀ%xR% Pmy3GU$0e `Z8xgtW V"umuaQFEahVwYXQ^bMex`P 5x (yKT}XhN$feZń!BP؃^p׀8pX0ZeM5B8bf.XANLhaj6n6؉a,eQGw,/ҤQ0V`Q#`m0wbQO*KON&mR8fZqUbx??1&O&|50H/ڈw03=P=qC.(8R;pAX,O ِ |9Yy\i:&W7o8R&y(*,ْ.02y} an"9N y:ѓ?@i(BDFH8Д=R.XyV]Z>B7:0d#fih$gHzu |9Ylٖ2NgjMtg ݨy*g`)KfY7vW-З~ ![$ЈGLv$f| qxTdP^O6`GwF O\t&%@oޤO)x]-(ev8 r^foרsf"x| 6zVabƧYRUsj v}\jNUM~Wcy3vYysi)Bf{Yig/G#yQߕP/Qh%@D|Sƙc5ZǖaJu5"/$:e 4pDϗf,\3# i?J#V5bU&wGV1 v^` FaEd%Iwdqz2*^FyI4 xZ:Z)i8Z/yaI5` i{fYgY GVLIVН:bPwTM]uKdwJ\Z]VO- vw6Q'> ~$)0w&1@QET*5`V K[X ;걼NeOC?}tEG.{lPR9A8MxoZ8%cL iRhܕfniH{7Z"XyZXKKX+ʪ5zUk kGkk!K gGKJ^QQ'IL[r^(a{8& ? (p;)\fڤkzGy;:DWKXٺ[*`45O OlQc[4c Ӻg"ཧzE2NW4wl{9Kx$yKOJ}FM)QJlwi듁| ]/v©I +* nmj; 9o PTyQfFJ̦w27paI-MLi"؋ţYSfiʳGP20df<\n\=22P/q5@Fpz# &ryXyV)@2 |yOUK|Ň<,tGa?ś泮{y(c{Ig3{w_yh{ljʝ (UT\П"^cH뷳jYkE8*^˺{uƞGOaȫ*1G0BNj g~͔Щ^Ky"p5*bFoغ,p M'1 s*Lˌ{ L\UT"̦=p!"}噙JӀρY 2P =5/}tp X0ǢQRɼ[3ܦNfk[ӆW,a;[Pa $W$&! ArD'03 1Aa6]1N ̮殇Úh֫4c\}Pp&˖.0`af$9ap}*apP%+ ဒ *LAjޤ ..@B.jITbͪuk% XxB T!AK 2HxG+W ! ͜R_D9a!‡i?:a)ɞyLŚoҦO>YC'm^ :Ygネ,[.n8h]^v!+ih!3{9VGϣ?` Jpp(adȐQ1麁d$B\T0uq}Ecԥuhwp gz6UX' xTAlRp4k0p/Pr b HXDLXRkE v5\Hn(C`sx_ΈVszsT`W0\T 5yvD@GzB qJz9gbnPff%ݚtJfT$Қe@ZjԪ$Ъ4iOb+Vh4, d00wF1£'ϧ&[ 2pP;y*ܰ=0Q PD  YV K b!3^q/7`}|ksy|T<0.B?0i 쁇:02@3֊]ÒDNAvHлBMȈhFkdМV00ŏCeq-qiXWu l <1vtBB.{^&EDrL& C'd`l #HM͓$FG}A *MY":1Dލ5}iqt9{F WpqS 0DŽM6@cϔ4Rwu(\ᗺ8lÖl8N0)>[FH5GEAB%Fas5}@` l`cTH3Gs鋁 ;`>vWD @}C.-0{1Y8vHb$%+iKb2$'19Xr;e'KiS24t)7z$ D IZrx9F4xM6ҥїXKSA(X\   5Ӆ 2jls*\';w3'=!_2A~6e (EȎ ԧz9~^n W;\WNP\EGՓB\)K[җ4>kjӛ:)O{ӟ5B*:"5J]*S9B5RU؆ pkZݪZb,Q(W@ hdTַ7G584$0G9Ey!wy8 J%G@5/ b&-/M– pZoWVb c4 0z1@,C烂`^5<^.Q (CQ>dq򳐶>ՐQHt sq̞.XH2b\wf(d`%dP` @Z۱i$mWFF۷rO,F7\d̼0"LeGr'$wmr7\p v@0K LY,E(IJ H !{z-nDy@` `  }.жhYUL wH8fDq 8l]PhUԤmY  YOЗYHMںM!x@_ \Gie FA__( _LoZ0{mL2yʎEYH>ڑ5q2 }biH$X&1VXG^>De[:6ɢ0e\qIEDĭW^zRoQfnq.B'"DF,R.!'/Gf8n=Z߱}igZ#ig>h/mQe knUBL!MַL,DG0 EA|? Tc &xm lmʝ^Mcp0m(w.qEuNږM)ʧwu Py.Wr_yN[D>@t܍ڌi%)) V2uex'"ĨBɀkZb & TIO%i{i2@ch>iRNiSB,*Ƌzj K*|2jjr)wH]jjNjI*j? afk*+ⲞGBn)"*~r~UHv)JkbЗMZak?kp+k")9V^[kB,ЫËQ5z,Pi>0«~lzkZ>l,el@vmʡ%+B-KUFe)C,Z-֊m,\mю k a-Xj[kڦ|n n"n*2n:BnJ"nRrnzP.>Lnnnnb$Tq,no o"o*2o:.K;qBojro6KB mH؜b</,(僭t@! x/͜`FMpLw@/)i0T9/x }FGs&@pG0j00L>t @  . BL/ D@>o\ >$ 2p Sh HRh SKo> $wlsT}w pˏo T2W @h1  )r}q?hA4 `h2"q''ڿ81%oIJhr  <%U,1?P8h O*,G-7 L)m@.5L,4t@t@SLH$ys@ 4.p|lL6w3737W 1(16q(y@< 3W#Ct0x-E/@ ssg4$I0MJ; t6/2| D1(s88B3EE @(OO#pGC<7@r67<`.X,8@ Z@gtS5W'KN׀.3gPC#5P t4M@2{[ ,uW4Ir \0> ldh/uF5^ Urf1 ``S u^oLtt5CL6\o0c7ec@/\;  q[&`wRjD׬(CVlmGmwrς`EuWv@g4)tedzT8{GG6W~7@v5dPfs7A+IaGpc56z@Mwyw0􂛸}۸wO8(evxlTumFtRKXF8J  :'9w¥g&;m 90,}=K>e_4(1+hQ|4?dy74@'wfollO- P Zt+xEc$-hRhZsSm>&E[*ShS K"pHHMabSЁ4QBfe'g&qp%(H!K[k{0W6X vi* Q+Ъ{"wU,3e] s.ݫ\_1!CC8FҜ@-u@0h(c3F$vTJz!A4e)_r". a0z13A,<=t>>/aeL4 ѯhPRoLI8I)Q챵b6l/aP;A" SAގl뭏&`SM m)E%BwX00ՔDp &和QvDh>Cv /JMAlsp _8$:fI<bF .W#uG"|F]<9itE \J8V%!y'(f'X( iLK:--g䝨qT[݅B"a60/sT %|C\\ӊWn8^>t牅T=7`a#^J@=NZƘNduVUX. X gLS7RC,(0T!2`@ezr_ b}ve5F3 P?`_p`+ F0>i Z2\iF>ҙ Yr٭(Pg_XcT=,YҨA+@m+ .sMXͨءeA!PlpFf ~n%~`lmqUgqFjBg܅K&7aza'M3Ӭ@3sK,; ~“ƼXb lAz/ .Р? n* 5p+a YCN04`:P6; qD,b<0#*qLl0'JqT,"&4V_3T9zGGiQcMk=q^5C˓A^mXV(PI#  4MukA w.֍gҫH 2#Hh)2yx!"hP*]͘Tmz|-p}F 83aU?vn+T'EfrZvKD̮k ~$jg%s~zu[x ӤO)XW޾?g3%uQ }CVb2z_/O|?)5ۈ>$3_J(!~4p;WI~_uyzu'MEGPnUvtF*2Qd pw\' T6A[Bzα|X|YG*p&krx &zw)W#=x1G8T!^2pzēD ˀ}s/o`#1'Vs>ksS6VSÅt p`{7чj_M' @bu)@h+ױ#lU ؈xcwUs8#Xprl37fĢ31P# `,l Z \!@k#6`hLHeaLp%qg0G Ŋ>0-gX`>Xa>/Hv8p;N Tx'p8 Gp\֨Vcd#+"!5w"h`r&_h}n8cw {WVuS,!8>Bdxk5 bXw{/1uR7Xa?wfk`' TB`m-sHĠ0=c6)<jJ%1 jr\|N^%rPHvw#9@71^,҅酌׆u4 Z~U6#[%2s:a*v֨a!gGhͶ90Bv^by5b¹јW"X6i8>`;jW3H[Y@[,[`oo,[YReS %z4w? Zş0@!̗G0YyUk ǡWǜ { JW:v 1gop},ࢂ0lt[?:h .XE)kF}5"}GƣSŌSE9Qģ4Ȟ$եԦs03AY0ZqWM~*|_u'wZW%W>U C*!3N*L V'cBEJH< Z2Wi!H0))馓UYu>X))y S#vЕu,]` 8 [@]E)tXu00\\ʎ@]ɮY`WƩUz[@MgY@xGJ g׈g:>džIFd"q#mzj ymfB0d5WP>9rXm1-`=˗p{(lpt(qߙrи똀;Z(q avl*qk\wK@@L&$״ja潜t׉J1'y  Q,穵k~H#tM :.T9+Ru u 8Jj GO2v2hvvŝsy\,y m6 $F8z@k2i=,1C z0HI{ x8VE̳rj󦁺|]'/Jyܫ_ cl|N~ Pi~!ʒʂ~lb@LP} 5Sɞ ˛L4jyuJgL󒛯"r zsp'`|(7T@{ i7A;Tp-.00l!zjk- a|\!ٕڬ#˜z.pIL:0090[808a¤kW`D 0s8H`A20Kt69P`';+G)-g45s1 8xHS6 81[Ij5B,a/=*Mdcn#*6Y  X,Oq% f  $`bd=!сͨ56kƷ,\kDVHs3R9M++h; h׀ tQ hRA<5AuȖr%۹湄ͻ1%,M:lNaQbm@e["6 !]20&e&6) M~da+" Ķ5 (|_Aͯǯ/"Fzex `d]T]xf6!8ڗGRT%gPUfT 9L`#@d*ed f cQ 2 R6` =a)-o-ӓ0 1+Y X .ƴ*V9PZ2M0x@+EW dEb!}M wLî}k @= mq0<@0W24|?&3\Va2q6N\_4:Ke)8 a#5sM"ϰ 2=rtF#3Y }Œ# BS,~%0%P` p%pQoGVYoEO_] C_bZ/+jvͯWy8:?Sj& @ Q?` @`Pk%coE _`pop>Vʣ\ a[U|#T_?/]_^pL___Au/GԑSÏ #Y'+ 3zE'?(Rz>2()18 |d,Gã ZuPM aP`aKbcd$d%ʠebO&֎R(@ ځˆEEFC@* EEYYӀBYAG 5'd67%78"yf:'(ӇȫԇCHLEA?3Dk78pfՅ[YTc7"D9 IdK bЈ& wFE8Ɖ; x P|@MƼj / p!J"jrÐ-ܸ85kWlP4%:qXı1Ȓ'Sf2͎7s3hCL4ԪWY!6V"Ӭw83IP8ҧS! 1%-Ǧ IS&>3@iOT: 8 e=E) tTI(bX EePU]/Pa`PDn\!0#5x#9#=#A 9$Ey$I*$M:$NqX(U,Ƀ|]2KzL+`aJO0rEu y'}' :(z(*(:(J:)6SB# "x89Đ حB.p~qlJ bD! U3@  oLlUeAEc\*(?8%Oϲ<$l>R"Dq֩f.⁺@Ch |Y 0lId]DXB2_@ |)2p~B%d#1r#'SPLPFQq 92rFBm"KP!QI Uf:Z`,*cHA,r$V *҈ ?0㋤dEH )"L &d1|hԶH tF,))K%+3;P+Ҏ!6t00Y8Fu`jVHUQHvwaֶZ$:b7^⃀nM "%* ZS1ZflLs@jGkrEYBx[@!&<a4cbؓ'tqL\U2mi}"t/|2 q"hʒӑ\+gP/Y5j'Ρ΃.tȳfxXu^8JhㄲM]u8׷ޒ'Q7nK3S>mOxp]}v̰ĖH8_&)ns\<8 Xȓrt $Vh|1DD"*JšFgBO`Ȏ ExdR}3a``ZHHB `a>DpdPSt@8Ɔ8P! tz>p RgŏQ ,vA eL'N  D&M|H0P%?pADFPŲ \_C  ʲ`4UVU%'̒Xr` Ԡ:y_\\L"r  aZ.Ϭ!j! P!*a ^ 5F .ԠQBxi]$$>:" f"LA۵M T_s!M X3~@q"D&zl <"/`<E!׈A `1c%YE}NUtM#3H0^vb9.0@C4# љ(^ʤ!U=~OHD:@#":$ c8ܠDŽ_܁LaP|KqX `cB028Bd P(&CL T@gETO MRտJ”/B%Ѱd0K<$T`+PEiYdLe+,D,$/$ R S ǼA&ĚLD}$V8\' eZba (E(ajQeRVfB aan&h^gg4"Uf'\fafk*@m@k&dy܆7Y-Zb]޴G* Z$߹ggʦvV čaU5qJ9@0c52A|)LԚuEV ~ef>(%{c,sN00BQЛ.up'iz rQ(wAG^$Ib4GXPL"Hyh@iLJ,'5u!ϑhrq[O.tADBeѨPpAx 0yvSlz8s!fq0LQZ+"bqb,"Er2жRჴB.DlE43PH+LjE?-1`˾lzkEEl3thnI  ?j̖f~'⬬59u ,(QbFiZGbÊӧ<DA^>ZR2Ȕ) n$Th5vdqIh>Őڬ A[Ì.drh[Oi3V2j4nm$4rKZP3uCv`O1cj@,@@4AA4B'B/4C7C'4>DO4EWE_4FgFo4GFBémV@UVe>?CUBMtM@O4P5bҰ!5E%:? @? H4T_5 TT5VuVObN[bZx5X(pu ̡ 95[[w)Bb]֎7!&S5Xu 䧬LDr'i*m6B^;6VC6 ()N+\R*h"VT;Bi6T P6|\ph) =sSn:v ,-nsvcKSSB4 7Ȥ4AYPmh?dJDy2z 2SvwK~qvQcf]8_qo8w8|8װQYZcx<@D@@a @_L`,「?@Ak@Phy xi_]4 V*`B /'I|)gGW-(r)bb(,(( IyK7Cu Y1(}gZB0y z{≯,4F Z$70(ZP/ sי)@Ty~BwX >{~?~fDW};@4zW݃e8}"&Z?e~FV<@(@@3!BAD!6(pE00 d08p 49 sբ"]\v`)Gh$a@Cњ0PGfdَqť矀*[Ix(bV裐F*順N af馜v)mAjꩨꘕN*무֚Dk믐 ,k&ʲPlF+_J ^lv+n-ᖫyn+B֫j۶+qol G,q g-X BuVgHlMk`mvjKlc-k-ox ]www{߄LJKxGnyZw.訫[zS׮{w;o|oZӚ7/g؛Zg=O5,@~H4QF&E@y@M+p6g2  @G` @HE:PT  ;2 D <W)B&C`a \P,!xhpЃ S@A*POA eXOu0+`Ѐ,1jJTԴcT跚1RZ T43'(@T8r"8ь4|MY&pAJR9oE$AM@YAcܠw.yPQ 2IkC$ tV B *C = jb?-; B.;f @tdI:Q Yd@"5.h)L "%C@ 7=XQNB,VP+`Fyj(ha0D lMCDj6@$ٯ>YD`Ȥ*dANAؔ፳f1TPh)_`A ]+:QR $ )@;5|  qȷYȭ)#y ;b@Jl.F׾F\_65% 䏜`yc , lERAVpCeP'F*sh/xasÊO"6469n Q!E%:F9 go`1Ɛ48LaojYLq I^V2`SGA(Sѡ <SIٳ Q*B`@gEa`Dp VT )TU)a >hg?[OM> {QY$"7.mJ@f? SU`̨}』0hl?euFpҔLB48R0LMoqMNx6 gDɁsqT< W ) c.Nrl;a$8;r_q nY7i8 ?hM jeT`P B6Eq.YI;EPdIcrSk02ydv<ݒ, ř\M%~UoO}MЦ@Q;@se@ꗟT|?^CDhRK+wxn%LB%$fx?zg?l7}W1؄,gp!H0q@aB*yBp0X)o@(qhbB6r,e"(@*0$9 M @%j6 !'ͤoFktIw`0p_KȄ0 :v $`SPRabw p~S / -R!1l` 0h@So.(tRmQxaz6uLǗ}G'o~mTq  烺Xy Će$+ SA `:}R׀< 0QXL!~ GƸ 16%e+Q|!.ᆺs%y@w{h|tDhȎ QZ6Ͷ`}{𷈞0HR`p  ^VұҐYP19rF$Ir~$EE8nHM?RMbp$nO9P@"$k92f`yo'5G'&orb'iM}k}l ss3iC|ٗ~9Yy٘9i0Ԃ(ٙ9Yyٚ9Yy HӲ 9Yyșy+<fYyؙڹٜٝ~ `pzK`i !Y깞ٞ9Yyٟ::Bg$QWM ⹃ԑ&0|efд*TY`~g uxMxa}:Bt**,QZw9`e>uHJ)F{AJS }&טlV`¸^ )M#jaS{Avr_Gu6` W' -^|lrz$pCSy`&%IxasnڪqwvezcB rN ԠtpmG?qҤs\v@:s@w~JT&8 I"$ !w`7F: hk7($ۺ@xWuJ%8yжxxEzZh2 @Br?B `m'ghG Ej,{|&.![$e&xb ڲDZA1`AɇqC[-#WO$fC?%;* jǵr;js"]zM&_p00Ps;[s(YY& u`J0˵< U@йX #ϖKK$WySf#`M`g Ltt釗 ۅAK)ZIMF FB%h RhF IrvvR꫈;Urי[ 8oݻϑ@v`hK Brv)#KŷL |$܌ 'AZV]Gw, +0Zfx ')_KzOxG Zfl}y/ vD\ě`I[JL= őLR`qǔ ר+_DpGwPmEnSnZwD0Qيsk`khD' Z~d@oeh]^(j̱{1S}0,;zl,w^{zAY[@Gu+1\M֦П#Ǖ^WABu`$꦳z=# _ngڌjfAܜ/kj#N/LŞy{A0ʨf*$O8L.N6ݞys@ #N¢<8n$j_ݔ^7д=O4v郐!N7 S1Fx@ʴ-ؔjtN ٩LCP ]2qu255$HbX'ڷKۓ$m00j,%$ۼg%Gq!'ѹhf%O>jvƥӶQ.H8~=*廓 -ϊMXT _o/`,`g9#Ƿ́ݼaᩏp9̩J5}n"> jO'ovm6ީ@ H+;CK׸lt 'sdg ߇qGTAQJ@CY<4Js45xb-1l>5~9n ŁP|XX|0xؘ |d(\ H8(PX88hjuD}P`*P8xYH(\f #'+/aHT (^Hh5-UB?:VPf\j!.9 P`* _(Me8>2 (p "Lp*m&As0l)C#GhӒT `g;T\s'Ϟ> fջd^J D㇤*ucvb D{buW!+@n+w.2Ap Y3*G>Ts% pD J2qATB.ҦONKb)qjzv(kO?MO\rP5U \+%QMѓe@[֯cϮ.46APÏ/;PKgeyptpPK|%AOEBPS/img/adfns060.gifW)GIF87a?**?***UU?UUU????**?*******?*******U*U?*U*U*U**?*****?*****?*****?***UU?UUUU*U*?U*U*U*UUUU?UUUUUUUU?UUUUU?UUUUU?UUUUU?UUU?**?***UU?UUU?????**?***UU?UUU?????**?***UU?UUU?ժժ?ժժժ???**?***UU?UUU?????***UUU, H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ } ? 4xaB 'p 8`A&TP?$Xp`?$XA .T <0… :|| H*\0,8_>$XA .D,8P_>$XA .D,h „ 2l!ąE1bA"/bĈ >~#Fh0#F1bąE1bA"/bĈ >~#Fh0#F1bąE1bA"/bĈ >~#Fh0#F1bąE1bA"/bĈ >~#Fh0#F1bąE1bA"/bĈ >~#Fh0#F1bąE1bA"/bĈ >~#Fh0#F1bD ,h „ A2dȐ!C14C 2d(0? 2dȐ!C 2C 2d_>cȐ!C Р>~ 2dȐ|2dȐ!C 2dp>~ 2dȐ?!C 2C1dȐ!CcȐ!C 2dȐ!Á1dȐ!Cch_> 2dP>~ ǐ!C  !C 2dȐ!C ǐ!C 󗏡A2dȐ!C14C 2d(0? 2dȐ!C 2 ?$XA . 2dȐ!C 2d8P?_|'P   <| ˗p|O@~7,h@~@}/@} ߿O@ /߾O@ DPB >P?/_ԗ?~E(_c/_~+/_񋨰߾| O?70_/ /E1bĈ#.a|˗o`|ȯ?~/_}˧O|5/?ӗo| 7_'_|O}O| O?70_ |/>7p_|'0_E1bĈ#.H?$H0_}70_>?~׏_>~o߿|/W`,_>~ ̗|_>~㗏߿}/߾}ǯ`W`,_>~ ̗|_>~߾}o߿}ǯ| ? 4xaB 6tB}˧ϟ|W?}˗O /|/C"o!|'0_>˗_|˗o`˗O| [_-|g?}˗O|O`|/~)/bĈ#Fq>~㷏_|#|#/| ̗/|c/_-O} O@˗/ /|˗/ 8P>~8p>~0@@ ̗/_ /|ӗ/_>}7p@'p "Lp!ÆB\|/||/}ۧ_>~O~ _>/| |/}ۧ_>~O~-/?}O |+/ӗ߿|O߿|O? 엏|"F1bĈ0_~70_˷?}/_>}ӗ/?_S/@˗ |O_|˗O˗ϟ?}O|7_~8_|/_}˧߿|ϟ| 0@ 80?$XA .dC b_拨Pqa 1bĈ#F\_ĈE,/_E0_DE_Ĉ E\_Ĉ#F1B}"F|1bĈO#F4_Ĉ#F1B}O@ DP?$(_ `A$XA O@$H@}> 4xa„8p`> H*\ȰÇ@~,h „'p A,Xp| 'p "LǏ AG~ H&O@ DPB >P#F,/_E1A}"/bĈ 1bĈ#FP#F,/_E1A}"/bĈ 1bĈ#FP#F,/_E1A}"/bĈ 1bĈ#FP#F,/_E1A}"/bĈ 1bĈ#FP#F,/_E1A}"/bĈ 1bĈ#FP#F,/_E1A}"/bĈ 1bĈ#Fh!O@ DPƒ14/C 2d(P?cȐ!C ǐ!C 2dȐ!CO@'p O@  > 4xaB  ԗ> 4xaB  <0… :|qa?$X |,h`A} HA H A$XA .T?  ? 4xaB 'p "Lp!ÆB0? (QD%J(QD%J(QDȐ>%J(QD%J(QD%JH0? (QD%J(QD%J(QDȐ>%J(QD%J(QD%JH0? (QD%J(QD%J(QDȐ>%J(QD%J(QD%JH0? ԗ|ӷ>%J(QD%J(QD%JH0? ̗o`>I(QD%J(QD%J$DID@'p`>}70@~7p?~8`A&TaC!F8bE1fԸ`>o_A~8rȑ#G9rȑ| o| OB~8rȑ#G9rȑ| O| oB~8rȑ#G9rȑ| /߿| ̷/qȑ#G9rȑ#GO_/?}Ǒ#G9rȑ#G%Q ?}9rȑ#G9rQb>Ǒ#G9rȑ#G%Q ?}9rȑ#G9rQb> HO@ DPB >QD-^ĘQFqOG9rȑ#G9rGqȑ#G9rȑ#GqOG9r#G9rGqȑ#G9rȑ#GqOG9rȑ#G9rGqȑ#G9rȑ#GqOG9rȑ#G9rGqȑ#G9rȑ#GqOG9rȑ#G9rGqȑ#G9rȑ#GqOG9rȑ#G9rG'p "Lp!ÆB(q"Ŋ/b̨q#|8 䧏#|8rȑ#G9rH1?1_|8rȑ#G9r(1?`>'p "Lp!ÆB(q"Ŋ/bĘ_Ƃe(`> ? 4x?$XA .dC%NXEa4`> 4xaBP <80?$XA .dC%NXEa4O@,h „80߾'p ? 4xaB 6tbD)VxQa> Ӈ#A ?~8`A <0… :|1ĉ+Z0?È`|#OƃaĈ#F1bĈb> Ӈ#|A}0bĈ#F1bh1?È`?a<F1bĈ#F-O@ O,h „ ӷpaB}8`A&TaC!F8bE'p  <0B ԗ/_|3>~ H*\ȰÇ#JHŋ3j8Wqȑ#G9rȑ#Lj7_ӗ?}Ǒ#G9rȑ#G#W0|`qȑ#G9rȑ#LjG_>}wP?9rȑ#G9r˷o`>Ǒ#G9rȑ#G#W0|o`>Ǒ#G9rȑ#G#W0@~'0|Ǐ#G9rȑ#G9F䧏@}8rȑ#GqGq@'p ? 4xaB 6tbD)VxcFq>~9rȑ#G9r1"?}Ǒ#G9rȑ#G#q>~9rȑ#G9r1"?}Ǒ#G9rȑ#G#q>~9rȑ#G9r1"?}Ǒ#G9rȑ#G#q>~9rȑ#G9r1"?}Ǒ#G9rȑ#G#q>~9rȑ#G9r1"?}Ǒ#G9rȑ#G#q>~9rȑ#G9r1"?} <0… :|1ĉ+Z1ƍ8P?9rȑ#G9r>ȑ#G9rȑ#G8P?9rȑ#G9r>ȑ#G9rȑ#G8P?9rȑ#G9r>ȑ#G9rȑ#G8P?9rȑ#G9r>ȑ#G9rȑ#G8P?9rȑ#G9r>ȑ#G9rȑ#G8P?9rȑ#G9r>O@ DPB >QD-^ĘQƁqG9rȑ#G9rOǁqȑ#G9rȑ#LjqG9rȑ#G9rOǁqȑ#G9rȑ#LjqG9rȑ#G9rOǁqȑ#G9rȑ#LjqG9rȑ#G9rOǁqȑ#G9rȑ#LjqG9rȑ#G9rOǁqȑ#G9rȑ#LjqG9rȑ#G9rOǁ'p "Lp!ÆB(q"Ŋ/b̨q@~8Ǐ#G9rȑ#G9F䧏@}8rȑ#G9rȑcD~8Ǐ#G9rȑ#G9F䧏@}8rȑ#G9rȑcD~8Ǐ#G9rȑ#G"?}Ǒ#G9rȑ#LjǏE~8Ǐ#G9rȑ#G H(p_|O@ D ?} *,h „ 2l!Ĉ'RhbB~ H ?$XA ӧP)TPB *TP|˧`>'p A+X` `'p "Lp!A~2A0߿| /D!C}O} H0 ~O_|˗߿}O`'P}7P_>}'_|˧O|O | Ǐ|/_?/?>~˗/_>~!B4/?"D!Bǯ?~ GP?}/߿|o}۷}뷏_>?~߿|O`>/?~| O`>' ?$X!D>~˗B"Dh_|"$/>"D!B"D!|/|g0|ӷϟ|_'|g0߿|O`>_>~ /? '0߿|C!!D>~!B/?˷!B"D!B"D_o`70߿|/|/? /߿߿/߿~_/߿ _>~@8`A Ӈ!ƒ!D!B /?ۇ!B"D!B"D(0?~70A}Է|70}_@~o`>_>`@~@~_>'?"OB!B"D8_|oB"D!B"D|o`맯?}|/_~O`>˗|˗|/?~_>~/߿ӗ//_>~/>}O_|8`Aw0_>˗/_>} !B"DH_|o,h „ 2l!Ĉ'RhD~'0_>cES/>1bĈ#F1b~1Z/?È#F1bĈ#F̷_}#_>È|oF1b|/"?~g0D~ÈQ`>;߾} G_>}È|oF1bĨ0|1_|#E~O|oB}0b_|˷#F1F}ӗ/O_>}_|/_>ۗ?}/O߾|ۗ_>?}ۗ//o/O_>}|'p "`'0|;B"D!B/>"D!B"D!B_|/| /}_>/| '0߿|o?~o|o@~˷o?>~|_>"D ?}ӗ_>O!D!B"/_|"D!B"D!B/|O`>/@'|/߿/߿/߿|˧o?/| o`>뗏@~ O`>? 4x!?}"<B"D!B!B"D!B"Dp`o_>~ '0|+0/߿/߿/߿_߿_߿| /O |O@ }/|'0߿~}70|/@}ӷ|O?+O`>B!BC!B"D(_}!B"D!B"D| /_|˧O|O_|/_>~ '0˧O߿|/>}̗߿O_|O_|˗߿O_|O|ϟ|'p "!BC!B"D_}? 4xaB 6tbD)Vx"?}#~/?1bĈ#F1bLOƃax_}#F1VG0|wcob>Ӈ>~1Zo_BÈ#F w0| G0| ˷0|ax>Èb|˗F1b\߿|O_>}߾| Ǐ|_>~/_? />}ۗ_o|߿O_>}˧_>~/?$XA ӧP)TPBۧp|)TPB *TPB~O߿| _}'0?Ǐ߿|O`> `>/߾}?~߿|O`>/|)TPA~*TO? *T`|/? *TPB *Lo_}_>70|o_}/| G0| ̗o`3_>'0| ̗'p "L(> OB *4/>OB *TPB O?~'0|_?}_?}Ǐ_> `>/|Ǐ`O?~#O`> ? 4xa)T>~ *TPa~)L/_> *TPB *TP__>ӷ||O_|'0|'0߿|_#_>_>O`>}PBSP?}*TPBS|*TPB *T!?}_>~ O_>}/_>}|˧O#O`>~ /|ӧ|#_>0>}߿|/?}O@ DP ?} *B *TH?  <0… :|1ĉ+Z萟>È#F1bĈ#F1VA}0bĈ#F1bĈ#FxP?1bĈ#F1 ̇a>1A}0bĈ#F1J̇`>̇#Fa<F1bĈ#Ɓ/|O_>}O}| Ǐ}/_>?}o_>~O_>}|O`cB~0#F1bĈ@~_߾߾󗯟|߿|o?~o?~//߾(0__?~O ?$XA  o„-\p… .\p… .\0aO>70| ̗@~70_'?}'0߿|W0|߾~ p… ӷpaB}.\p… .\p… .\0_|/O`>'p/߿/߿//߿|/>~ /?  <0-\P .\p… .\p… &ԷO`o`_>O?GP߾_ | O߾W0|O ?}' .\ p… .\p… .\p„߾|˗/_ӗ/_?ϟ| GП|ӗ/_|O`_>~ /_>} ?}[p…O@ ,h „ 2l!Ĉ'Rh"ƌ7䧏@}8rȑ#G9rȑcD~8Ǐ#G9rȑ#G9F䧏@}8rȑ#G#[ȏG8Ǒ@~8Ǐ#G9r1b囘a>=!?}Ǒ#G9r/_> _|o_>~Ǐ_|˗/>O?~O_>}|˧'P|˗@}/|˗?~/>1!@'p ? 4xaB 6tbD)"ܷ}뷏_>o?~O|W0_o?/@~_>o|O|o߾ } '0߿|W"?}WbŊ+VX"|o_|_#/|70__>~_> '0|O`| Էϟ|}*J䧯B}*VXbŊ+VDo`0@߿>~_> 70_|'P`> '0|/|o~O@o_>~ Hӗ0aB'p "Lp!ÆB(q"E?}'_>_>+?~ӷ|O ?}O`>}'0|/|_O ?70_EUT_Ŋ+VXbŊ˗|ӗ/?ӗO>˗O>~Ǐ`>}ϟ| ϟ|ӗ/_O_|o |/|ӧo|/_>~/_~!O,h@}8`A&TaC!F8bE]xE]Dŋ/^xŋxŋ黈P/^xŋb|/ wŁ]Dŋ/^xE{/a>.^Lʼn]Dŋ/^xE7P_>}/_GP_>}7P_>}O |/_?O|o_|ӗo?}_|_|.^lOE]xŋ/^\O| O|/۷}߿|o?} >~_>~O`~O`>|'p "L ?} &o… .\p… .\p… G0|70_|#O`>'A/|70|o|O .\H p… .\p… .\p„㗏`>/_ 70| `>O?~?/߿|_ _~/߿'p "Lp ?} &o… .\p… .\p… /|_>}W0}#O`>'?O`>}ӷ|o_/߿|… .O… [p… .\p… .\paB~o`>˧O>}+o|_>ӗ/ӧ߿|O_}/_~˷_˧O|˗… .$O…  <0… :|1ĉ+O>0Eg1>~-ZhѢE-B/_>!#a>->bB},ZhѢE-ZhѢE-bB},ZhѢE-ZLϢE-ZOńYhѢE-Z0E-Z8> hѢE-ZHq?˗߿| '_|,ZhѢEYLE-ZhѢEϟ>O` gѢE-"bB},ZhѢE-Vo__?~ H*\ȰÇ ѡ>~#F1bĈ#FP߿|/߿| `>'p "Lp!ÆBO_DE1bĈ#F1C~|_ /bĈ#Fq ?}1bĈ#F1bĈ/_>}/>}˗|#F1bĈP#F1bĈ#F1bĈ#F ?}1bĈ#F1bĈ#F1bĈ#/C}"F1bĈ#F1bĈ#F1băEt_Ĉ#F1bĈ#F1bĈ#Fx1bĈ#F1bĈ#FQDEQDO,h@}8`A&TaC!F8bE1fԸq ?}Ǒ#G9rȑ#G#q>~9rȑ#G9r1"?}Ǒ#G9rȑ#G#q>~9rȑ#G9r1"?}Ǒ#G9rȑ#G#q>~9rȑ#G9r1"?}Ǒ#G9rȑ#G#q>~9rȑ#G9r1"?}Ǒ#G Ǒ#G9r䨐>ȑ#G׏#dž(˗8p'p  ;x C|!D!B"DP ?}"<,h „ 2l!Ą(PO@ O,h|O?~ԧ/| ԧ/߾'_|˧_|ӗ//_?!B"D?~"DxP>$XA .dC 'p`>}O@ OBϟ CO`>ӧo?~'0| //_'B"D!BO@ /@},h „ 2l!Ą (P?8`AӇ!ƒo| CO`>|'?}/|!B"D!8`A O@ DPB >|| HC|o| '0|맏|8߿~߿|@8`A&TaC!F8bE˗/?]DOE_} !_>o?_>O` ŋ/^xŋϠ|wa>맏_| 70|ӧO`>}ӗOӧ߿||#_>ӗ//^xŋ/"|˗/>GA~.^DOa/^xŋ/^_E'p | H*\ȰÇ#JHŋW0@~O|w3f̘1cƌ3f̘1A #_># ?}%a2f̘1cƌ3f̘|O@~wG0|e̘1cƌ3f̘1cC #/>+ ?} ˗߿| ܗ_>'P_>}_} _|2f̘1cƌ3f̘|̷/~ '0A~2O`o?~`>/߿|/|O@ DPB >QD-^Ĉ_O}/|h_o`>#| /_>~e̘1cƌ3f̘1#B2/~/| '0߿|맏| `>'p "Lp!ÆB(q"Ŋ/b~ϟ| /˗O>~'0?}/cƌ3f̘1cƌ ˗ ?}3f̘1cƌ3f̘1cFe4O_ƌ3f̘1cƌ3f̘Ѡ| ӗ1cƌ3f̘1cƌ3f4/_Fe̘1cƌ3f̘1cƌ ˗ ?}3f̘1cƌ3fQFed@'p ? 4xaB 6tbD)VxcFq ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1| <0… :|1ĉ+Z1ƍ8>9rȑ#G9r_>ȑ#G9rȑ#G8>9rȑ#G9r_>ȑ#G9rȑ#G8>9rȑ#G9r_>ȑ#G9rȑ#G8>9rȑ#G9r_>ȑ#G9rȑ#G8>9rȑ#G9r_>ȑ#G9rȑ#G8>9rȑ#G9r_>O@ DPB >QD-^ĘQƁqOG9rȑ#G9r/ǁqȑ#G9rȑ#LjqOG9rȑ#G9r/ǁqȑ#G9rȑ#LjqOG9rȑ#G9r/ǁqȑ#G9rȑ#LjqOG9rȑ#G9r/ǁqȑ#G9rȑ#LjqOG9rȑ#G9r/ǁqȑ#G9rȑ#LjqOG9rȑ#G9r/ǁ'p "Lp!ÆB(q"Ŋ/b̨q@8䧏#G9rȑ#G9F@~8rȑ#G9rȑcD8䧏#G9rȑ#G9F@~8rȑ#G9rȑcD8䧏#G9rȑ#G9F@~8rȑ#G9rȑcD8䧏#G9rȑ#G9F@~8rȑ#G9rȑcD8䧏#G9rȑ#G9F@~8rȑ#G9rȑcD8䧏#G9rȑ#G9F@~8`A&TaC!F8bE1fԸq|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?} H*\ȰÇ#JHŋ3j8_>ȑ#G9rȑ#G8>9rȑ#G9r_>ȑ#G9rȑ#G8>9rȑ#G9r_>ȑ#G9rL/G9r4/ǁqȑ#G9/_>9rh_>ȑ#G9r,`>8`A&TaC!*/C~"F1bĈ#FX? 4x80,h „ 2l!DEtO_D O|O@Ǐ A $Hp` A@~O@ H0 $XA .dC'p ̧? 4x_}|!B|;o@}C!B Ӈ!ƒ08`A&TaC!.O@'p 'p ˧o| ?} |ۗ/?_|߾|/?}#o_}|_? ԗ`>!B!B`>8`A&TaC!F8b>~o`>#O?/߾}?} G0_o|O|_ ?~ϟ> Gq>~˗"E)R_~׏|/|W?}/| O ?_g0|7߿|o?}7_>~)䧏B}G"E)Rd/_?0?}70_|ϟ˗߿/߿0@߿|/߿|߿/_?} O| >/'p "L> OB *TPB *TP|"OB O#_ O>/|O ? Է|o`>/'0߿|#?}#o~*T ?}/|ӧP>~ *TPB *TPB`|*TH0?o ?~/_|ӗ/_?'0|˗|˗߿|˗/_?_|ۧ/_~߿߿|?~/,h ƒ̗O`|%4_„ &L0a„ &L0a„뗰`|&L0a„ K0a„ &,/a„ w0>o |;_„ &L0a„ &L0a„p`|&L0a„ K0a„ &,/a„ w0>~`>~/a„ &L0a„ &L0aBK_>} H*\ȰÇ#J/|ӇP?)RH"ŃC/>哘/b>Q\ȏ@~H ?}˧_>˷>~)RH"Eg_>}) Ga( Gqa> H ?}˗_>ۗA}(RH"E׏`|(Rܗ_'P߾|'0?~߾|ԗO_>~ /?/?˗ϟ@~/_>|ӗO@߿|ۗ߿}O_|8`A `'0@~wP &L0a„ &L0a„ O`|&L}O ?߾//_||Ǐ_>O  ̗?~o}`>߿|_>K0aB%L>~ &L0a„ &L0a„ &ԗ_|&L`O`>߿| ̗o`_>~/?O ?ӷ߿߿߿|߿|/߿/?}'p "$O_„ 0a„ &L0a„ &L0aӗ0a„ O@߿}_/O` ߿ /߿~(߿߿|߿|/߿߿~O@ D( /a„ &L0a„ &L0a„/a„ _>}/|'0߿|_>~>~O?_>~'?O`>/|_K0aB%L>~ &L0a„ &L0a„ &O|&L?}/_>~ 70?ϟ|/|˗|˗||ۗ/_~̗߿߿|߿}/߿˧O?? 4x!?}"<,h „ 2l!Ĉ'P_~%J$/_B(QD%ObC}$J(QD%OA}I(Q`|˗/?%J(Q@~$6OD%J(Q~ԗD/> ?'p ",/a„ &L0aB%L>~ &L0a„ &L0a„ OB}%L0|70|/a„ K0a„ &L!?} &$_„ &L0a„ &L0a„ӗ|&L_>}`/'P|˗A~o_>~ϟ|%L0a„ &L8 /a„ &L0a„ &L0aK8P_~ &L_>} |o߿ӧ/߿/߿_/'p "Lp!Æsp>~:tСC:T/>s`o`/@o`>~ O ?o`>}9tСC sp>~:tСC:L/>С|/|/_>o |o@_o_>~ H*\Ȱ@~:C:tСC˧!A}9tX0?~70_~˧o|O`>'? _>_>~9tСC sp>~:tСC:!|o`>˷?}/_~˷ϟ| /_>~ ϟ|˗/_:tСÃ9t8P?:tСCOCsСC:tСC@}:tСC9C? 48p~!B"D!B"D!B"ĆA|D!B"D!B"D!B!?}"D!B"D!B"D!BlOćA"D!B"D!B"D>~!B"D!B"D!B"ĆA|D!B"D!B"D!B!?}"D!B"D!B"D!BlOćA"D!B"D!B"D>$X'p "Lp!ÆB(q"Ŋ/b̨q@~8Ǐ#G9rȑ#G9F ? 4xp`>~ H*\ȰÇ#JHŋ3j8@},h`~ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8IwQ9s̙e|*/gΜ9s/˙3gΜ9e˧/|r̙3gN{/_9s̙f|/gΜ9s/˙3gΜ9sO_B}̙3gΜ9˧|r̙3gΜWP_~9s̙3g΃/_9s̙3g~ӗ_Μ9s̙sa|˙3gΜ9s6̗/_Μ9s̙> <0… :|1ĉ+Z1ƍ _|:vرcǎ;vرcG/;vرcǎ;vQ_~ױcǎ;vرcǎ7`|:vرcǎ;vرcF}%O_ǎ;vرcǎ;v/_رcǎ;vرcǎװ_>};vرcǎ;vq|˧cǎ;vرcǎ;Fԗ_~uرcǎ;vرcLJ/;vرcǎ;vP_~ױcǎ;vQGuC د,h „ 2l!Ĉ'Rh"ƌ7r#Ȑ"G,i$ʔ*Wl)1 ;;PKa WWPK|%AOEBPS/img/adfns022.gif{>>sssqqqxxxkkkJJJ~~~hhhuuujjjTTTyyyþcccZZZ%%%ɥ***...UUU555rrr{{{[[[EEE]]]RRRiii+++===444YYY)))BBBFFFȉmmmKKKwwwfOy¨Okqh\uuhDDDkxefy\uuqbbb\\\||cfLBϬťNNNaaaSSS9AAAf6bfI333fffyyr36̀}fVoolIV~~x!, H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛ ;2(A>ɴӧPWVC(Lt( /DZ]˶[\Y/?FP YD "8)CYK/pO˘3kf!A b:nrp!TNXڱ!.tH)%h:p@f F_X50NJӽ+WTo[j |pvhN?!XSue t(Ur D("KTB^9h]#v[n ^WEhcSߩL6(fw"N?#U?UEh&Cx]`APp|矀*蠄j衈&袌6裐F*餔Vj饘f馜v  Pꩨ@@JPBk+,2{ନzJ) d p+ڒ:.+./[/[ n[@2?ޢknJ1ۛ//!1'lLn?'mS|s⬳ϫ,B̫Gjͻ%mԴN-5W*1?- ]ԏ憽`Jp?mtmvM7z* ߂v/~Q㎧ y@3-QbS6PWTÙ99ؕ%Q;Q[QVIqwI&?iUhU@iP]W6b^#uW8@U"v[醁U~d}@aWUOXe`[ٸ$mMc`?\dו)G:hScה4BZ4G"# AB0 R: 5c:O 2 'rq" Qe@.PĻaT !pOug`qTS#46$e tXi&v^ijfG+HC3 OrD[wk"ԕw29/nFӦ{yڗ-~M"QCLx= M_P-j&L [pj &€ġrW#j&@810~,ɯchHN&' 1!`'{KA"d+Ārf'dmRfx?R9Y~s)o~vJ#A 9]@юf3)Eә9 ԡv=`RSUmɤjZ|t#k_'yFXlcTVe",ƆF<=m:ֳvvaawdv# fp43s6k: )!cH#5Gmo7N5a~H!P?TjH! 0놧7݅fC 4Af q79!oLPHҗ;=h7 ]%.z{`{ASWG cf6nXx{Ѐ[t\v{1R,ZkR@[63?4kIV+ (h$Xy=F@\ "wٓDit@Q< ? ag@5z?9C!㈑9ӑ(vY`GvPو94Xt\a9ٙi)ꘙYkI ˜/{cU`iɒٙ~)fHII)dIu"IYrY y ~Yu酡yaɓ)sL#'9QY򉜽^؞}Ǡ؝T(@w9$ơ;Fbb/:.,,1h88©F'~Z*!#ڤ%:i jh)ah'mb,ZQ$  D5CM [r+F3@UФ$JOYR-!0d{Xsr!Q_tW9"3 A BZy  \Q"ZGKE bĥʗ O;R>#uu?_%ia,":Nq\5%Y7@F@uR:G̱=rzW1~*Zk@QnKuaLC4t?LfBZeA座Pe  =jɮqAZ"yc mFJtMjܷEgs .LNrsJd2! 2=jQ#^Z? O^v~r0 &q ]_~^x@POQrx^.&^}Iwc~YHh n q~.gP @P7NIf)nnoy)4Q!rI U ~ f fp0 7^8A<`^P}Z}9c`*ɽ P)0 FJ/K``d`^x`2nbP iduqsN70/\<n^5.`;< _{FH~c|].x@A;Qn#b5B"뎅3wcݼX1A[/_|dpkH_U4ż~ɻ (0"L&P*_Q&`Q_'*/X_-I1`0T$XA~-pC%NXQG!EvJ?UBt "+eΤ?9tOABssM}AJ,+7kUa"oaT0Y2-B aPM" v}hsΡYT?H3ñ 8.T[֮_n&j' 4i)p!cJ-#G^Y&o:]hWb[fnOG`I +i|Obje?SG*z`2L8S`znl&~3r n@ qf+A ;:N s )F ~TgDXSÙ(= >a.? -hDZ pA*!x`_1 Ʉ& qNɇp8LAlJDĸ19kN'|.=дF~PaPB JYϕl%☲J@#^ C ȵxqb#I !hzK풘"'Bዿ4L gvkvq7e@0=Mtei&plb j=@6#;DA IA "ra?ZH)Ze Uz dF#n@ x@&P\Iv1@&e˸?LyB='E`np` 4A^ C9^H:0d&؀ ,a;̀[Qi+U*X.[^#, S03<_9.jD2&&0ZC!Il. h\튋n9@IċgYCg1nygIhB. ih/tbDXpUK"50 8 4#:Puxd䤵drp FTqrtemGY\H&`aZ'1/c(11\(PA3-Y+LJ 0P#j,OFUƱtۗYv ꩒ B PU42:hP0+DI4#+s~T$R5T%y#Y*5 f Ah!B QiQ~0]85Tsz4:Ra2hU&J<sE$?4}HA l Q( r[6r?+81Trry%Hi@dy\iG'@JP˪BPaVFM?ЈR`>E:VEmzkWA K~o/.xJ a N>a*S:MB*ߊ7Nd$v@9p!0GV1(n5A|=01^Ȏ+c "6]+jZDrpAoՄd LYY%? 9UAD0qHc,%!I`!=#* & k@&7 _ .7/PJpb@L&8!=r"p@jvldMc.C\/ƦD30tS3@,T /:@ HZ h^d&v `sK11 JfT& ڈ(G`IH<@_6N3/$`_.6r&p"pj0V[`?i9'˧4L!,c!v-b@"f%!lMd_˖ɛr:`޿V0Ĝ拯F;5@yxNG11L sE?p?[ wǛ w:d% 3f3XR=C4w͢\}_"4yb3^T _-W?7ꖄ꺮ڮnc0_? 4Fh7C\P5X4u۷~㽔#+>~[; # ,yAAA|{|ҵI?[< 9&lB'|'6A*¿ڶ0-!"8:>H<Q1YX7 *[@c;UA!8A A1˂ ?+DIA>:+DM2!™sSH5ķH6@],hFԼDODfl hAr|GsDB*W 'X$'Y*&Xpj 2`ǛЀ{H8ě[$H)8)lsTtLulpƹhW*H@"Hu@+#U]COъ( bO@=D=H@#K` ip88@P7hV@ Gp TDd83EDE(LAҕPGՖҙ0 N4@Q]Nl8 MMho x} 5*/gefXQ"G'VK;M\q-UX=QPP Hx7A@L\#ǔ" ȕhWk _sQ{ -Zh@ZuċZ܁V,s0W帀ZhZ0;X ,36 S0SY@C8X(8M(v 2@mDݢ ҚHXc{#>G[òZڙZYTM]iӽ,;ب?[jҕ`ɦ˼jI؅ BЂ8>< @gQtu]]B)`T*#kq cψ83ޅ͌X "܍`I~aC^Uފ,n⒃}Zctp%b;aqe:5csh(~zbHGrY{Q Z8R `U fe݊]3aՋ5wvˌgghh1t^HXg|n@R'_6>Ȉ0H6F4pl hue~)GNH6(>b}Lb輷I(G 7ݘ7^ 7``{ZeNRXVu[mh{2 &Æfh#G ~;W2qtQ0Ngg]Ծ*afm}efk0Hr޲=["ҦQubѥL¬.W U׽e@F.ԖmEL^`e1znIǞkd%I`:1)Ί/YHH+&2V&h_p.vuǍflf~J8 F|0sҷ1pm=hkgs4G~f̽hI s:<3`ŸϊA3%|g2VvYu⻎VHOVIx(b(af͊06 nJ~̓~8uh thA_8,w cpbCmeFzwX }o[q?oT&sUkB~;QhnE8q1W@48(`uo `Mn x `? U 2w\vx^M (p}fp_pxWkxheÄV0 nP QS"?oy"_a&z%c'*/r/-WȎ#9^wg“vFI?BHϳW{s1( ;g+聳8 3<.z H\_FVwc nL& !ذS'Mx!/<ʔ)\D~2 G6B#FP`?. Uf=z5좵Mr10 3o9t -W XAܫ%Ĕ*EզMt몑`ȀdqDyUiPA :G6aOAHTz!!~u^W\MGl(`@!FJ B'5PJ "WG: .0Now%YjnՓ6eH!:7ډ&@qiSk(:̹h^5ٖeRh!)(gJAk(lU XR~?vP*hk(f(dqQp*|ЫʪS:m1:G2B RXdfq-pָ,\Tʪd!qlnUB"$>@,ph{.H* KWbyzKLa*T]uLS2;+[ "a`gm2W gTB6zB#4Ӷ{" *<7Z+`€@s~63qϟxw?6g:ާ16HCP>9cKX 31hL6~"]BvS1;b!`#[s8(ԡQDu!&y9&x"()RQu5\69i.p/Vo~I ?鰍Dy7ұr3sF]v#EN 1:A?d'Y/zvEB$Ke2 CـYldЅ.}:e*PR eHCHfΕ"_KX`2>5ґ{!F:4b̉A!Ă28'\xV4EfӍ+'NlSbPRxj \]GДst \`~Bç,xA\(FH+(CuhY!qhe)P[ GjJŏp@RT DHu a-PBH.+PaW^*sBes[奚` ֵu4*Gh W #]cUSJ~"981JkWXmv%Ca.B)"WZlfrصFӳhCNV{=-lٔZΕI-nW4[ڲV-puj֫ZMԐQov"[SRJnH/y{fҼDzVӄo|ǻ->,`T k~;6 Pr),b#(v`WIސ\NXD$G&&rLA 1sx׭0$]wGpҀ;lN~2|J|D0ۈd(Q2<*c!oޕyn&h,(9+Nt/y[_q>5mbغ [f7{NM/[[ ml5# r96 {wmێ.oۙ ag0d6z;!i!Ԋ&R.\3)9JNe* `>Nެ9ԝFv |!= Q )03unf@5@l,C D$CW(Ϟ~5 -hSnzǟN|)&s)okY3>}׽V 2Þ'?ν{oOSLnfV=X~pއ9,r{վ \1FX@p@+$ 4?UٳEA]ځFՁL OMn`h_"Hi Y(, ȃ!a_ ᱠ  *`_IAV` L-/5/i%_ZeB BR6^&afRj"PNR.]WN_!!@+ql $nMXa"i"'"-6"!*!).'b&*QVNT/"00H,"-֢"`#J23꽔 !5P66 JdPc*`8Wc4hc7V;.8Z>Q??R9>DAV! (P%NX GvG~D"R$Qi-$ʤX K$K <䱨d$LM֊MSL椳aN$[$P-"QQfR&%iDe$<6T> UU Vlte}e%H #YfY6ZJ>%jeMԥ]!^^NZM%,bn GaFP*bS2Ic\.^ QZeN<#4F#gBg~&Dfg)fnP&G%jbǐ^SfV^alriZ^m&Efn6Ifm%cp~n@&iB@rVs_oupzkZtvg|np'[|yF bU.BڄqcgVAqM}6Tvg'Ug2 ((P*"'.n'gl_ŧ|* }25:lgzkJp)'^uZBHΌD(Մfjhd戺̑r{"Xh[hgVܧ62dbmnY)OiHi&̓ni&6iBi_R)[iF)r&N!")N X$ĨF*čBjj3t*^hHjĄte4*aN$lS(@jVĩb}?GF+̈́r**8NRѴVB+uζNiEVѸ6k^ke_m^ͫ«ijۣkg`$>Z+†RW."U-*ĞbVe^ʤ`Ȋ<Ȏ,ɪlʪ,,Ƭ˺͞,B,A--ъǒH=-"ԺBՒ@֦H`mzrm"vNm~m ۲ګ-ܖ-ޖ-ߺ"lA`mn%n6..nB D?\nf.jZX.@4nzD?xnu .®.֮ޮ.nn D./&2A(/B?HR/p;PK앣k<{<PK|%AOEBPS/img/adfns100.gifRGIF87a g?**?***UU?UUU????**?*******?*******U*U?*U*U*U**?*****?*****?*****?***UU?UUUU*U*?U*U*U*UUUU?UUUUUUUU?UUUUU?UUUUU?UUUUU?UUU?**?***UU?UUU?????**?***UU?UUU?????**?***UU?UUU?ժժ?ժժժ???**?***UU?UUU?????***UUU, g H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜIb|嫸/_|(/|*˗/?jĘ/|(|h'0_>/5Y'0_'p_|뗏`|/_>~˷O | '_|/_ ̗O | ܗ/?/@~˗?~/_} /߾ׯfM ̗_>ۗϟ|׏|o_>/|o߿|o߾? $/|/߾}_>~˗o?/|/?_>~?~8`A&TaC /߿|O|;؏|'0_'0>~/| `|O?/߿|ӗ/ ̗/>~ϟ|O`>|Ç /߿~ۗO_|'0_|/_>}߾|/_}˗o`{H0_> /_}˗O߿||ӗ/_>}˷_}_| 70Ç>|p|˧߾?}O@~O>~O`| ̗_O߿| 8p/ ̗O?7P |˧>~'0_>~/߿}ۧO`8`A&TaC ˗o?ϟ| w_|/_>~˗O?_>_/_}˗O?'0A~'П|ӗ/>˧߿|˧ϟ|'p "Lp!ÆB(q"Ŋ/b̨q#ǎ? )r$ɒ&OLrA~,M˗oK,Y/˒OK,YO_>%/K,Y|Xdɒ`>'ǒ%K4/?,Y4e~dɒ|,KO@$XA .dC%NXE5nG!E$YI)UdK1eΤY~ H*L P <0B8`A&TaC O@ DP}8`A'p "L`>~ H*\ȰC=|?ÇÇ>dOÇ|>|>~>|C=|?ÇÇ>dOÇ|>|>~>|C=|?ÇÇ>dOÇ|>|>~>|C=/_>} | p|{(0Â=|ÇP`~_{h0/?$X_> ? 4xaB 6tȐ#o?}/_?/?~{/C=~Ǐ_|˗ϟ|>~>|C=`O`_>~{/C=`'0?~'0|=Ç>|Ȑ/_A_|{/C=`/@'C}>|Ç {O_?'>~W`>'p A,X| ,O_?>~W|O@O@ DPB 2~ ߿|/?}O?9|뗏@/ӷO`>ϟC}>|Ç {(߾|O |O_|/_>~{h0˗O_?˗O>~O`>P>|!C~>|| Ç{Ç2Ç`>>|O>|!C~>|| Ç{Ç2>$XP|3hРAgР4X_|4hРAO@ DPB 2|C=4CÇ{Ç2|/_>ϟ|O>~`>د?~>/?sÇ>|!?}ϟ@~߿~/@~s/C=O?O`>~뷏_>P>|!C~O_~ '0|O ?`>/_|O`?~O@ <0… :dO 0 _>$X_ _7|?}(߿| H?}8`A&TaC {'>~| _?}O ? 䧏?}/=|ÇP |O_|#?'0C@~ϟ| 70O`{Ç2Ç`>>|O>|!C~>|| Ç{Ç2Ç`>>|O>|!C~>|| H| 4hРA ,h „ 2l!?}>|_{=|ÇÇ{h0>Ç>|!?}>|_{=|ÇÇ{h0>Ç>|!?} H*4/,h0?$XA ? 4xaB 6tP|,h „ 'p 8`A&TP,h „ 2l!?}>|_{=|ÇÇ{h0>Ç>|!?}>|_{=|ÇÇ{h0>Ç>|!?}>|_{=|ÇÇ{h0>Ç>|!?}>|_{=|ÇÇ{h0>Ç>|!?}>|_{C,h „ 2l!?}>|_{=|ÇÇ{h0>Ç>|!?}>|_{=|ÇPa>˗/{h0 '_|=DÇ>|!?}#O`~| Pa>_>~Ç>dOCo`~`>#O`=<Ç>|!?}#O>}=L/C=T? ܧ8p@}8`A&TaC {`>׏|8`A;x`>$0O_~ H'p "Lp!ÆPa>7_{h0 ϟ=<Ç>|!?}#_?}{x_{0|/Ã=|ÇPa>˗/߾{h0 '_|=DÇ>|!?}>|_{=|ÇÇ{h0>Ç>|!?}>|_{=|ÇÇ{h0>Ç>C O,h „ ˷Pa> .\Р>~ .\p… o… o|.\pA}.\p… .~ H*\ȰC=|?ÇÇ>dOÇ|>|>~>|C'p "L`>$X`>$XA ? 4xaB 6t@~,/_>} ,X_| ,? 4 ˗/A ܗ/>O@ DPB >l/_>~!B"D!B"D"D! OD!B"D˗D!B/_>!B"D!/?!B(P_|!B"D!B/_>~!BQ| */?!B"DB~A`?~ */?!B"DB}A`?~ *O_?!B"DB}A| B"D!B8_| *ԗ/D ˗"D!B" h ? 4XP_|Q"Bx>}'>OD8qĉ'B/'N_|&N8qĉoĉ ˗oA8qĉ'B/'N4/_7qĉG~'P~7P ~?'P~7P ~??ׯ(߿(?ׯ(߿(?ׯ˗'P ׯ_?'P7P ׯ_?'P7P ׯ_?'P߿(_~(`?(_~(`??~@@ ߿ @@ ߿ @@(?ׯ(߿(?ׯ(߿(?ׯ(߿ ? П@ ϟ?'P 'P  o@8?O_|'p? П@ П@ ϟ?'P 'P   7p @'p @'p? П@ د?O@ DPǐ@}1dȐaA~1d(П>}2dȐ!~ ׯC 2L/_>~  ԗ/C ԗ/CǏ!C 2ϟ@1dȐ!Äǐ@}1dȐaA}1d(_|2dȐ!C1dȐ!CǏ!CcȐ!ÂcȐ!C 2dȐ!C ˗Cǐ!Cǐ!C 2d0|.ǐBcȐ!C Ǐ!C 2dȐaB~c0C Ǐ!C 2T }'p "Lp!Æӗo~'p_|/_@{Çp|=|C3/}O__~ÂÇ˗B{C_?󗯟|˧O`|Âpa?~>|>}ӧÇ2ϟ@O}_ ԗ߿ ~˗OĈ(QD '_~/_ӗ?}o`>Ȑ?~Ǐ?/_|'QbB˗/?~I'p "Lp~ 珡@cȐB} 8? ,0'p "L`>O@ O8`A&TP? ˗O? .0'p 'p@$XA O@8` (P,h „ 2l!D80,h| <0@̷? 4`>O@ DPB˗O 6`>'p O@$XA O@$XР@ <0… '0 6$`>8`A(,h „(p,hP ? O@ DP…̗O`|'_|˗}/_}˷|ǐ!|cȐ!Á˗!˷!C .o~ _|_>~㗏|/˷_>cp_|2/_>~ 2dH0_|2/_| 2dȐa~`|/|˗O ̗_>} ̧? ǐ~1dȐa~1d80_>~ 2dȐa~`|'0_>'P`>}߾|/_} HA~}˗o?ϟ|/_>}8`A&TaC!F8~U? 4xaB 8`A H*L <0… :l`> 4xaB 8`A H*L? 4xaB 6tȐ>/C=|?}>|Ç {=4ÇÇ>|Ȑ>/C=|?}>|?>|>/C=|?}>|П?C=|?ÇÅ_"Ç`>>|O.ϟ@=|!?}>|_{=|pa~Ç{=40?$XA o… ϟ@-\paB~˗/.,oA*`|o‚o… … .O|[0| ˷Pa> o|-<… .\p…;o?}ӗ/?'_|_|㗯@~-T|?}O_}|˗߿|/_>GP .\p… w0|}O?/?~_~?~߿'p 3/|}?} ߾_ '0'p "Lp!Æw0|`/߿| '0߿|| w0|ӷ| O`O` '0A}>|Ç ;o?}/?맏|g0|(߿| /80O߿|_/| O` _>? 4xaBp… ;o~O`>|ӷO`>@~+/B7p__>|ӷO`@~3… .ׯ?[p„̗/_>~˗/_?_|ϟ| '0?'0_A*`||O_}˧Oӗ/_O_|3… .ׯ?[p„-\| H˗0|&$/a„ 0a„  ϟ@%L0aB%LX0_„ /a%LH0_„  /a„ &د?K0a„KO|? 4(0A~w|0@'p +/_>w<د<(0A~4o`G_}4o`>~ Ǐ?~/_~㗏 ?~,h „ 2l!?} _>'?o?~S/C570__>'0߿}ˇ0_B}>|Ç k`>@~o``> '0|ȏ| g_~ {Ç2䧯a>|맏|/G@$H|$/| '`>Ǐ_?8p@}8`A&TxП .LOBO` /ӷ|)o| 70| O ?}#o-\p ׯ… &o| '0?_|/>})o| 70| /_>~˧O?G_|%o… ϟ@-\paB~.\pA*o… o… ϟ@-\paB~.\pA*o… 2 }8`A&T_?p… [p…[0 .\hP .\_?p… [p…[0 .\hP .\_? .\p ?} .\Р| p… p… .\pA~.\pA*o… o… .\pƒ-\pB-T… .4… .\p…[p…[0 .\hP .\p… P <0ƒ8`A8`A&Tx0?$XA … & ˗O_ /_ O@ 'p gРA˧Ϡ8`A&Tϟ?p… / o… o‚[p… O~.\pp… .,؏ .\p…'_~ .\p@[p… ԗ/… .\p_ .\p|-\p… ˗o… .\p~ … .\8_|.\p…˷p!A[p… o… .D/_>~ Ǐ… &O…o… ZhZh2 O@ ԗ/ 2,/_> ˗C 2dȐ!C /? ˗!C ˗!Cǐ!C !C &/? ˗!C ˗!ÁcȐ!C _? 2d_|2O> 2,O> 2C 1d@ϟ@$XA ./_>~ 2dȐ!C 2dȐ!Ã_? 2d_|2dȐ!CcȐ!C 24د?cȐ!C Ǐ!C 2T/_> ӧC 2dد?cȐ!C Ǐ!C 2T/_> ˗C 2dد? 2dP|1dȐ!C ǐ@cȐ!C 2dȐ!Âǐ@}1dȐ!C 2dȐ!C 2d|1d(P_| 2dX>} 2dȐ!C 2dȐ!B />8`˗/?4hРA /_|gР H,h „ 2l!D H,8|O@ D|O@ O8`A&T?2dȐA~ (p,XP|  <0?} (,H|O@ DP_? 2dx`>'p 'p`>} H 08`A80_?$XA .د?cȐ!C'p>$X?} ? 4xa8,h|O@ DPB_? 2d`>8`A'P>$XA 'P>$XР} ? 4xaB O~2dȐ!~Ǐ!}!C ˗/_> ˗/> 2d_?ǐ!C ˗/C˷!C ˗OC˗C 2\ϟ? 2dP|1d(p_| 2dX_|2/> 2dȐ!C 24/CcȐ!C1d8_> 2dȐ!C 2<C1dȐA~  <0… :|1ĉ+Z1F8`A&T? `> 4xaB 'p 8`A&T,hp~? 4xaBO@П <0ƒ'p 'p "L`> H ~ ׯA3hРA 4h| ϠA 4hРA4_> 4hРAgР_?gРA 4h? ,/A 4hРA3h| 4hРA ϠA'_~ ϠA 4hР4X_> 4hРAg? 4hРA ϟAO~ OA 4hРA3h| 4hРA Ϡ4hРAdA,H_?3hP ?} 4hРA Ϡ4hРA 4(0?gРA 4hР| 4ϟ? ϠA 4hР4X_> 4hРAg? 4hРA ϟA 4X> 0@8| ,8_ `'P?$(0_ `,X> +X`,X` 㧯`+X` /_W`,X0_ `,X| W`+X` O_ 7_|ӗ/W`,XP| ,(0@~O_|_ W@ ,XP`ϟ| _ ` W A~ ,XP`> /O_ `+X`A'0߿|߿|'p 3h| $O?}}gР4h?gР@~ 4H0_Ao`>} 48_> gРA O>gР4_> W_#O? ϠA'_~ ϠA 짏|_>~ 48_> gРAǏ_>'0?} ϟA3h |/~> ϠA'_~ ϠA/߿|? ,/A3h |/?~ o? Ϡ4h`>AgР_?gРA˗O>~_> 4(_> gРA˗O>~O`> 4(0?gРA˗O>~O`> 4(0? ϟ@4(> 4hРAg`A 4hРA ϟA3hРA 4hP`> ׯ? P,h „ ˷Р| .\`> ˷p… pB)0@O@ w<8_'P| HA 4/(߿| HA |}~ Ǐ?~/_~㗏 ?~A4`>~ Ǐ| /_?G?|O`>~ ϟ>_ ߿|/@~o߾`| /__>'?o?~ 짯`| ,X? $O_'0|ȏ| WP`+X@ $o`>@~70_+X0W| '0߿|#/߿|o+X A ׯ_+Xp`>|맏|/G@$H| o`>0@O'P}o/|O@_??G A'_~ OA#O`>'|O߾ A$H?$(0| 'P?_>}G> A#H| '0?}?_> ׏ $H~ ׯAG O`|O_>}G@$H| o`>O_|˗O}G_|$(0? 0`>O_|˗O>~ӧ|,_ O~ $O,h „ ˷Р| .\`> ˷p… a~o@~.\pAo… o@.\p|"B-\pB-4/… . .DO… .4/B-\pƒ-/… .<… ӷp… ˷Р| .\`> ˷p… pB-\pB-4`>$XA o@.\p|._[p…[h_ .\x0[p…[_?p ?} .\Р| ˷p… P| .\`> O~ ? 4xaBO@ <0ƒ'p 'p "L`> H ~ ׯA'p  ̗/B'p 'p gРA3h?$8@},h „ O@ O~ 4hРAgРA 4h`|4hР@~3hРA 4hП>} 4hРAo? 4hР3hРA 4h`| 4hP |4hРA 4/_>~ 4hРA3hРA /> 4hРA ˧ϠA˗ϟA 4hРA <0… 2̗OC&ǯCsСC:tС|9tСC СC:tСCsСC˗ϟC:t!B:t80_>}:tСB~sСC:@9tp`|:tСCСCϡC'_~:/>:Lȏ_? ϡC ˗CO~:t80_>}:t0_>}:tСAs!~ ׯC˧ϡCO,h |'p "Lp!ÆO~'P7P ׯ_?'P˧o`? ׯ_~ O@o@ ׯ_~ O@O`|(_~(`?߾| O@o@ ߿ O@o@ ߿ O@o@ ߿ O@o~ 8`?(_~(`?/_>}'P~7P ~?'P~7P ~?@@ ߿O ׯ_?'P7P ׯ_?'P7P ׯ_?'P7P ׯ_?o  7p @_|O@8?8?8П?O@/_>} @7p @'p @'p? П@ o_?ϟ(?(?o@8`A&4/_> *TPB *TP!BSPB *Tp`|)TPB *O> *TP|)TPB *T8_|)TPB */_> *TP?*TPB *,/_|)TPB */? *TPB *TP„˗ϟB *TPaB~SPB *TPB *T/_|)TP„SР?~*TPB *TPB  /_|*TP!|)Th?~ H*4O~ .\p… ˗/ .\(0_>} / .\XP_|.\p… .ԗ/_> .\_|.$/_> .\|-\p… .\O_|-\p|-\H_|.\pA.\p… ./_|.\P>}.$O> .\P` .\p… ˗/ .\p… .T/_ .\p… ˗/ .\p!A~[p…˷p… .\pB? 4xaB  / 6$/ 6lذaÆ˗_Æ 6l_|6lذaÆ 6lذ!C}װaÆ ˗_Æ aÆ 6lذB}װaÄkذaÆ ˗/ 6lذaÆ ˗_Æ ˧a~kذ!C}5lذaÆ 6l/_|6l8П?ϟ? o_|װaC6lذaÆ 6/_|6l(p| O@ H*\ȰÇ#Jd/_|&N08`8P>$XAۗ0a„ &L0a„ &/_|&L ?? 408`A"̗/ &L0a„ &L0@}0a„80,h? <a|&L0a„ &L0a„˗_„ 0@8`A8? 4xa*TPB *TPBOB (? 48_|;xO@ DPB >P_|EH_|ED/_|#/#F1bą/bD勘_|"F}#F1bĈ ˗_ĈQ|#&ԧ/bĈ#F|q 1bĈ#F1b}1bĈ#/bĈ#F1"}1bĈ#/#F1bĈ˗_D H'p />$XA .dC ˗/_? O@ ,,h~8`A&TaC!FO"|(QD%JHp_|IDϟćId/>%J(QD˗ă 0 <? 4X,h „ 2l!Ĉ˗D ? 4x | HA H*\ȰÇ#J,/_|&o ?}7|&N8qĉ'ܗ/_~7/_|&N8qĉ'ܗ/_~7sAM8qĉ'N~OA~ H8|O@ DPB >Q}1oC O@ H*\ȰÇ#Jt/_| 7>Mt/} ,h „ 2l!Ĉ˗/߾ 䧏 ?}g`>O@ DPB >QC~0oC O@'p "Lp!ÆB(a|k@~7ѡ|'P ~ H*\ȰÇ#J/_|7>Mt/_|7qĉ'N8q"~W_|G+oĉ'N8qĄ˷o}OA~&:oĉ'N8qD˷߾| 7>Mt/ĉ'N8qĉ'p`~߿?~8`A  <0… :|1ĉ'p ߿?} HO@ DPB >Qĉ8߿?}O,h`A8`A&TaC!F8Q>߿? 4x| H*\ȰÇ#Jq| ߿|O߿O` 8p?8`A&TaC!F81"?} O߿?}O߿|8p'p "Lp!ÆB(q"E~(߿? _ O_|? 4xaB 6tbD)/_~OA~ #_>UXbŊ+Vh_? 䧏 ?}/_|g_+VXbŊ7>7П +o_>~XbŊ+VXQa>#O|`>#/_Ŋ+VXbŊ OA~ ˗/?˧_|/_~XbŊ+VXQa}ӧ ?}WbŊ+VX}ӗOA~*20,h „ 2l!Ĉ'R|OA~*2bŊ+VXbŇ䧯"C*VXbŊ+V|OA~*2bŊ+VXbŇ䧯"C*VXbŊ+V|/?~W|+VXbŊ+>P@'p ? 4xaB 6tbD)V䧯"C*VXbŊ+VO_EUXbŊ+VX XbŊ+VX"?}WbŊ+VXbE~*2bŊ+VXbŊ8`A'p "Lp!ÆB(q"Ŋ8`AO@ DPB >QD-^ĘQF=~RH%MDRJ-]SL5;;PKjRRPK|%AOEBPS/img/adfns063.gifPGIF87a?**?***UU?UUU????**?*******?*******U*U?*U*U*U**?*****?*****?*****?***UU?UUUU*U*?U*U*U*UUUU?UUUUUUUU?UUUUU?UUUUU?UUUUU?UUU?**?***UU?UUU?????**?***UU?UUU?????**?***UU?UUU?ժժ?ժժժ???**?***UU?UUU?????***UUU, H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ /_>}$[/_|CoСC̗O|"[/?!̇0СCŹ/_?_'_|/_>~O@~/_>'0|˗@~/_>~C:Td|__>~߾|G0_> _>/|'P?/>,h „ 2la}Q`"D!BD0@8? $HP`>˧,h „#/߿|ۗ_| />̗O`|ۗO`>'0|/|O|*TPB *T`|`>/? *TPB ˧_> #/|*T|˧_/_ӗ/_>}O|O`} '0_>'߾|ӗ/_>} *TPB *T8p_~/_|O | 'p_|7_'`~/_~8`A&Tpa|/|ۗ/?_|˗|/|/>'p_|˗o@~c(_>}/|/O?̗|/|O`>~_|/>~cȐ!C 2dp`|__>~߾|7p_|/?ǯ_|/?ǯC 2dX`>O>}/?ǯ߿|߿'߿|߾}߾|/?_} H}'0@}O_|O_| ˗/'P_>'P| ԗo˧ϟ?}O@ DPB >$/|؏߾|O` ̗`?~O||/߿|!B0_>/|/| o_>˗/> ̗o`|O|ϟ|"D!B"D '_>} ̷_/||˗/ۧ߿|˧_>!B|/A '0|o`/|3O`|G0|߾|/_} H*\ȰÇ#JHQa|_O_>>~/O~ / _+B̗Ϡ>}'P?70|O`>'p_>~ӗ߿|}O`|+VXbŊ+./_>}/>}˗o|˗_A~ϟ| G0_>O`+BO@}/_?}'߿߿ӗo@7p_|ӗ/߾?}O_|'p "Lp!ÆB(q"Ŋ/b̨q#ǎ? )r$ɒ&OLa|ao+W\rʕ/ |[r˧ϟI  ,O`} />O`|/_>70_|O`|/_>~ H*\ȰÇ'0 a#F̗/b|#F/||o}o`o|_>ӗo_>1bĈ#Fۗ/?'0|O@~_|"F/|O?W?}_>߿|o_/_>} <0… :|`|/~o|/?_}oA />}_>~߿|_㗏߿}| HO@ӧO߾|/?߿|/?~۷߿| <0a|'0~/|70_>70|O`˗/> *TPB *Tp`|/|˗O ̗_>} ̧/ ̗/_>O`>70|)TP/|˗/@ '0|O`*TPa|'0?~/?}|/ 70߿~o_>}PB *TPB //| O_|/_}˗o /_>˗O_|O`>70|'p "Lh0_>O_|W0| /_OB ˗/ӗ/>@~7P_>O`'_|ӗ/? *TPB *T`| ܗ߿}/?}o|/| ӗO?}/| o_>~o`> *T/_>ӧ@}/O`>>} ̧PB *TP@SPB *B )B //_>}˗o?ϟ|/_>}O@}뗯?}O ܧ?}o@}߿| HO@}/?}O ܧ?}o@}߿| H*\Ȱ!C~ <0… :|1ĉ+Z1ƍ;z2ȑ$K<2ʕ,[| 3̙4kڼ3Ν<{ <0…  <0@$XA .dذ?$XA *O@g A$XA .dС| H*\Ȱ|8`A&> 4xaB 6,h „O>} H*O>} <0… ϡC:LC СC&ϡÃӧ`> ԧO} П8` 8@~ӧO|$O~ H*\Ȑ`> 6lذaC5l0a> 6lذaC5l?}8p>}ϟ?$XA .?~0? 4xaB ǐ!C 2LC ǐ!C 2LÄO|,h „ 2d?~'p`>}'p "Lp|2dȐ!C cȐ!|2dȐ!C cH>Ǐ? H*\Ȱۧ`>O@ DP!|.\p… p…[p… .,˗/}-\p… .\?} ? 4xaB[p… .,… o… .\>~ ˗/>~.\p… .\߾|ӷp… p… .\XP .$… .\paA}˗/߾~ .\p… .\h߾|[p„-\p… o… p… .\XPo… .\p… /_~ .\x0 .\p‚-\p!|.\p… G0_>}.\p… .\paA~[p (?$XA .dP>~:4ϟC:tPsСC:tp|:t0?:t0>~:4ϟC:tP9tСC:tؐ_>"/!~,h „ O@ Dp`> 8`A&T?$|'p "Lp!ÆB/_ĈK`? 4xaB O@ Dp`> 'p "LpA$@}O@ DPB >ذ_|"F_B~ H*\X?$,h „K? 4xaB |O@ DPB >|b> 'p "Lpa~8>~ H/!?$XA .,دo>~˧?$XA .dC˗/߾E18`A&T`? HP?$XA @,h „ ? 7P?˗o_? H*\ȰÇ ӗ/_|"D%П ˗/_>}gp}7p_| ̗ϠAO@'p "L808`A&T`~ HP`> <0… :|}˗O_?AQ`> 'p O| ӗ}˗?}˗ϠAO@'p "L808`A&T`~ HP$8p> Է_? H*\ȰÁ0'p A8`A&,_B~ H|/_7p} O| 48?$,h „K? 4xaB Aۧ`>Ǐ? H*\Ȱ?ӧ`> W` ,X`> O@_|+߾|/_>˗ϠAO@'p "L808`A&T`~ HP$H 0Ǐ_?8`A&Tp?O>o_?$XР| 4hРA38@,/_|/_}ӗ/߾|/_> ?  <0|O@ DP‚'p A}G A ׯ>}8@}@ (? 4X?O@}O@? 4x|"D|П ˗`|ׯ|߾|gР'p A}8`A&_B~ HA˷o`|;xA'p A}G A $H`~0 <0Bׯ_?$XAK0a„K? /_|/_?O~/AO@ Dp`> 'p  _}+/? 7P?$XA  O@8`A8P ,h „ ˷p…K? /_A ϟ?篟>7P`?$XP`? HP?$XA @,h`|/_|~ H*\ȰÇ#1"|O@ DP‚'p A}8`A&_B~ HA ̗O`|;xA'p A} <0… :|1|#B/!?/A$H A$H A'p A}8`A&_B~ HA ̧O`|;xA'p A} <0… :|1|#B/!?/A$H A$H A'p A}8`A&_B~ HAۗ| wO@ ? 4xaB 6tbD"F_B~ (0_>O_|˗o|˷//_}o_|㗏 A'p A}8`A&_B~ HA˷o |7P`?$X ~8>~O@ DPB >_!@ ̗O`>~O|/>~o|ׯ_}ۗ_|Ǐ A'p A}8`A&_B~ H*\X_?$@}8`A&TaC!F/bD%Пo`>'0_>/|/|/| ?  <0|O@ DP‚'p A} <0… :|1"|#B/!?/|'0|o`> '0|_>}/AO@'p "L808`A&T`~ HP'p "Lp!ÆB/_ĈK?'0| ԷO>~ ǯ_>} ԷO>~ӷO>~$H?$,h „K? 4xaB ,h „ 2l!ĈE18P`| O_~˷O`O`>˷O|˷O`> O@ Dp`> 'p "Lpa~8>~O@ DPB >_!@,h |"Dh?$,h „K? 4xaB ,h „ 2l!ĈE18`A C!B'p A}8`A&_B~ H*\X_?$@}8`A&TaC!F/bD%П <0…O@'p "L808`A&T`~ HP'p "Lp!ÆB/_ĈK? 4xaB O@ Dp`> 'p "Lpa~8>~O@ DPB >_! ? 4xaB O@'p "L80 H*\xo>~ H*\ȰÇ#1"|"F1"B}"Ft_Ĉ#FD@}"F1bĈ#1"|"F1"B}"Ft_Ĉ#FD@}"F1bĈ#1"|"F1"B}"Ft_Ĉ#FD@}"F1bĈ#1"|"F1"B}"Ft_Ĉ#FD@}"F1bĈ#1"|"F1"B}"Ft_Ĉ#FD@}"F1bĈ#1"|"F1"B}"Ft_Ĉ#FD@}"F1bĈE| HOB *TP@}*Ta> *TPB 7P? *TPB *TP@~*TPa> *TPB PBSPB *T8P)D? 4xaB 6t? 䗯` ,X`|8`A&TaC9t`>:taB} |,h „ 2l?$H_ ,X`O@ DPB СC9tСC C9tСC!|:DϟC:tP?ϡC:L@}ϡC:t`>s!|:tСÄ9t`} H*\Ȱ|,80?$XA .d!|Â=|Ç{@$XA ˗OB >~ H|8`A&TaC s/Ç `? 4xaB O@'p "Lp!ÆСÄ9$ϟC:ta|СC% <0…O@'p "Lp!ÆСÄ9$ϟC:ta|СC%П <0…O@'p "Lp!ÆСÄ9$|_|ۗ/_>~˧ϟ|˗/:t0?9ta> 'p "Lpa~8>~ H*\Ȱ|9t0>~ w0_>˗>}㗏|ۗ߿| |s!|СC%П <0…O@'p "Lp!ÆСÄ9$|/_>7_/߾̗o ~ '0|O|/߾ӗ_~˗|㗏|СC%П#H AO|#H|O@ DPB ˧ϡC sH0/߿|/_} ̗`|3/_} 70|/߾ ǯ|'Pǯ?}|CϟC~:t08P`| $H_|G| ~8>~ H*\Ȱ|9t0>~ w0_|/}ӗ/̗_|ӗ/O`| ̗O`> '0| o_>a>s!|O@ /? ̷_|˷| /?}70@~~8>~ H*\Ȱ|9t0>~ w0_ ?//_> ̗_/߿|O`| 70߿|O`>CϟC~:t08P`||_|O>O`>_|? 4xaB 6/>&!|W0_|˧|˗ |˧_>|/>>}/߿|O`>CϟC~:t08P`|ۗO`>~o_>󗏟|'0|ۗO`>O@'p "Lp!ÆСÄ9$| 'P߿ ԧ߿}O߿ />߿}@}|߿|O@$HP | G A G|?/|O}o| '0@O`>} 7p~8>~ H*\Ȱ|9t0>~ aB p?9dϟC~˗a|!/!? ̗_|O |_70|?~/|?  <0… ̗OC`>:t0Á!|w_|9l/_| @˗/?70_>7p |o?}˗o>'0O@'p "Lp!ÆСÄ9$ϟC:ta|W|'p  'p?O@ DP‚'p A}8`A&TaÁsaB}ϡC:t`>O|O@ O|П <0…O@'p "Lp!ÆСÄ9$ϟC:ta|'p|,h „'p A/_ +/_ ? 4xaB 6/>&!|:tСC _8`A_> 8@$/?+X` ,X`? HP?$XA .dp`|:tP?sСC:,ϟC~ O@$XРA߿'p@~ H`|˗O | ܗ/>O>߾|,X_? HP?$XA .dp`|:tP?sСC:,ϟC~ ӗ/_|˗/>%П ˷߾|/}_>}O`>~ۗ_| ,? 4xaB 6/>&!| H*\ȰÆ'p AO_| ,XP`|_A8 |ӗ/@~/~O`>˧O`| ?  <0… ̗OC @~,h „ 2l!,/_,X`A$_A8 |ۗ/|/_|O`˗/ O@ DPB ˧ϡC sСC:t!|:D_B~ H`|˗O`>ӗ|'0| ̧o@} ,? 4xaB 6/>&ϡC:tСÆ9ta> 'p A/_>篟| O`>|,X_? HP?$XA .dp`|:tP?:tСCСC%П <0…O@'p "Lp!C Hp,h „SPB *TPB OB @,h „ ?  <0… O HOB *TPB *T8_> *T_B~ H*\X?$,h „ 2l0'p "L8P? *TPB *TP@~*TPa> 'p "Lpa~8>~ H*\Ȱa? <0!A}*TPB *TPBSPB%O@ DPƒ8>~ H*\Ȱ?} ? 4xaB)TPB *TPB ˧PB SPB *T8P? *TPB'P ,h „SPB *TPB OB PB *Tp>~ *TPB ˗/_? *TB8`A&TaC8`A~ ,X` ? 4xaB 6C:t_|:tP?'p "Lp!Æ W` ,X`> H*\Ȱ@}:tСCsaB}ϡC:t`>s!|:tСÄ9tСC 9tP>~ СC:tX0?9ta>:taB}:tСC:DC9tСC!|:DϟC:tP?:tСC`>:tСCs/CСC&ϡC8`A&TaÂ,80?$XA .d!|Â=|Ç{|8`A&TaCO?$Xp`> H*\ȰC9Ç{ÇÁ=|ÇÁ=t/C/_{/C9Ç{ÇÁ=|ÇÁ=t/C/_{/C9Ç{ÇÁ=|ÇÁ=t/@~/|˗_|/}/O | ܗ/|_Ç=@ <0|*TPB o>~ Pa|/߿~̗o߿}ۗϟ| ̷|o߾}ӗOƒ)$/B */!@~,h „ O@ Dp`> *TPB 7P?S0a|ۗO`~ o_/_ ̗/|/| ̧P|OB @,h „ ?  <0|*TPB o>~ PA}'0>~ۧ|+/}O_|ϟ|/_*<ϟB)TP|O@ DP‚'p A}8`A&ϟB *TP O!|*4/߿|/~_|㗏_|_ o_>~ۗ߿}*<ϟB)TP|O@ DP‚'p A}8`A&ϟB *TP O!|*4/_|˷O|˷O_|/?O_>O|/_}*<ϟB)TP|O@ ܗ/_}˧|~S0? *TPB O!A~*TPa> 'p ߾|ϟ|wO@'p "L80? *TPBB)TPB *T`> ˧PB K? 480_O`|wO@'p "L80? *TPBB)TPB *T`> ˧PB K? 48p_|/|wO@'p "L80 H*\xo>~ $(0?$XA .d!|Â%П $o|G0_>~ H/!~ H*\H_?$@}$HP`> H*\ȰC9ÇK? 48_}˗O@ Dp`> 'p "Lpa~8>~#H A'p "Lp!Æ_/!?$X@~O|˗|{a|O@ ܗ/_}˗O}(@,h`? HP?$XA @O} A $/A˧AO@ Ǐ A <0… :dϟC~>|X08`A 0a‚'p A}8`A&_B~ 80_>#H A#/>$8_?$@}$H?$XA .dC H | ,X` W ?$XA .,? 4xa%П#/_?~˗}_>O |GP_>~G~8>~#H A H*\ȰÇ H | ,X` W ?$XA .,Ǐ $H A 8p |O>}o_~o?ϟ?}ϟ?'p O@ ? 4xaB 6tbD~"F_B~ H@ П +X`#8_>} $H@~$/?O/_~ ̧O`| 70@~`| $دo>~ H*\ȰÇ#1"|O@gРA 48?$˧ A /_| 8p`|˗o`>˗O| '0>~#/_?#HP`~ HP'p "Lp!ÆB/_ĈK? /_|_>} '0߾|ۗ/> ? GP_|#H AO~ 'p|ӧ/>'P?O`>G_>}#Hp`~ HP'p "Lp!ÆB/_ĈK? 0@_>ӗϟ|_}˷? $O> ,`>_> 8@O@}㗏?}'p?ϟ?ӗo>(|8p~8>~O@ DPB >_!@,80_o`>/>4h_? HP?~ H/?? 4xaB ,h „ 2l!ĈE18`70| '0|˗OA0 <>O8`A&T`~ HP'p "Lp!ÆB/_ĈK? /_|߾| 70}3hР~8>~'p`>$X`A} ߿O8`A&T`~ HP'p "Lp!ÆB/_ĈK? 0@_>_>/_?}'p O@ԗ/_|$H A(7p@~ H`|W` ,X`~ HP'p "Lp!ÆB/_ĈK? 4xp`?!D~8>~ӗ/> $HP |#80?O@O_ ,X`'p A} <0… :|1"|#B/!?$X!B#8_>} $H@~$/?O@/|ۗ/'p_|˗O}+X~8>~O@ DPB >_!@,h „ ? G? $H@  8`|˗_}O>뗏߾|/_~ׯ_>} ? 7P?$XA .dCb> 'p "LpaA~8>~ H/!?$X0_|O`|˗O`? ̗_>} ̧_ ? 7P?$XA .dCb> 8`A&T?$,h „K? ̗`> /_>˗O?~珟|,X_?$@}8`A&TaC!F/bDE1bDE08`| /?ӗ|>}ۧO`| ? 7P?$XA .dCb>#F>~#:/!?$X0_˗O_?˷_?}O_|ӗ/~ ? 7P?$XA .dCb>#F>~#:/!?$XA .,دo>~ H*\ȰÇ#1"|"F1"B}"Ft_B~ H*\X_?$@}8`A&TaC!F/bDE1bDE08`A&T`~ HP'p "Lp!ÆB/_Ĉ1bĈa> O@ DPB'p A} <0… :|1"|#B/bĈ#"/bD%O@ DPƒ8>~O@ DPB >_!1bĈ1|"F1"B}1bĈ#F_!1bĈ1|"F1"B}1bĈ#F_!1bĈ1|"F1"B}1bĈ#F_!1bĈ1|"F1"B}1bĈ#F_!1bĈ1|"F1"B}1bĈ#F_!1bĈ1|"F1"B}1bĈ#F("O@ D`> *TPB PBSPB *T8P)TPB *TPB ˧PB SPB *T8P? *DϟB *TP OB *TPB *T8_> *TϟB *TP)TP!|*TPB o>~ *TPB *TP)TP|*TPB OB PB *Tp>~SPB *TPB ܗOB PB *TH? 4xa)TPB *}*TPB *TPBSPB H*\ȰaA~ HO@ DPB ߿|8`A&TaC!>엏_Ĉ8`A&Ta HO@ DPB 'P`|'p "Lp!ÆBd/߾#F1bĈ#F1bĈ ˷/bĈ#F1a|E1bĈ#F1bĈ#Fd/_>~#F1bĈ˗_Ĉ#F1bĈ#F1bć/bĈ#F_|E1bĈ#F1bĈ#F(_|1bĈ#./_}"F1bĈ#F1bĈ#F4o_|1bĈ#O_|E1bĈ#F1bĈ#FȰ߾|/bĈ#Fto_|1bĈ#FQDEQDEQD?~˧_?$XA .dС@˗/߾~>|Ç>|Ç>|?˗/>~=|Çۧ/_|{Ç>|Ç>|ÇO_|ϟ>|_?~(p? H*\ȰÇ#JHŋ3^׏>Ǐ? H*\H_?~'P}'p "Lp!ÆB(q"Ŋ/b̨~O@}ϟ?$XA ׏?}8P>~'p "Lp!ÆB(q"Ŋ/b̨?0ӧ ?8p  O@o>}8p>} <0… :|1ĉ+Z1ƍ Ǐ>}'p ԧO>~? 4xaB 6tbD)VxcF9O@,(? 4xaB 6tbD)VxcF9vdH#I4yc@;;PKѸRPPPK|%AOEBPS/img/adfns058.gifRGIF87ao?**?***UU?UUU????**?*******?*******U*U?*U*U*U**?*****?*****?*****?***UU?UUUU*U*?U*U*U*UUUU?UUUUUUUU?UUUUU?UUUUU?UUUUU?UUU?**?***UU?UUU?????**?***UU?UUU?????**?***UU?UUU?ժժ?ժժժ???**?***UU?UUU?????***UUU,o H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8s:o |(,h „ 2l!Ĉ'Rh"ƌ7b̷o|qt#G'p "LpB$X @~,h „ *  <0… (0 7߾ ܗ>O߾|˗ϟ@}˗|˗?7_|8`A&T| H*\0,8_>$XA .D,8P_>$XA .D߿?~߿߿}__߿|/߿|߿|?}O@ DPB1dȐ!Cch_> 2dP>~ ǐ!C  `o`>o` /|O`>/?| 2dP>~ 2dȐ?!C 2C1dȐ!C#>`>~ `>/߿>~o@߿~ <0…cȐ!C Р| 2dȐ@}Ǐ!C 2|/|7PO ?@~?@~ '01dȐ!C1dȐ!Cch_> 2dP>~ ǐ!C  ` 70@~'П|/_>~ ϟ| _|ϟ|/_~˧O? 2dP? 2d| ǐ!C  ǏA}2dC P> HA  <0… :dÇ6@>|С>~{_|=|ÇÇ {8_>tÁ=||>|Ç2Çp|>|P{C=|0|˗Ç>LÇ6@>|С>~Ç#C '0@}>|!B}>||Ç{8P>t@}|ӷ/?~_|߾|o_>~߿|? 4xaB ǯaÆ "aA6lذaB}ǯaÆ &`O`/'0?~/߿|O`>~'0| 6lذaA}6lذ!BaÆ &ǯaA}6lذa|| O>o70|'0| 6lذaA}˗?0@7,h %L/_`>o@~ H!BCH_|'P ? 4x_|`Ǐ_o_>~_70|'0@'0'0?~_ /߿|O`>o~~/_>~ Hp| /_˷o`|ȯ?~/_}˧O| O_W`,(0|W0_/'0|˗@~W`>'_|'0?/_>}ӗ/_/_~ G0|'0,h „ 2_/_ܗ/>~_~o߿|۷/?1aAo_ܗ/>~_~o߿|۷/?̷P[/}Wp_|/?o߾˷߾|W_} kذaÆ 6lذ!A}˗O?} ̗`?~/@_>'0_>5,/B~O`|ӗ/|/| ̗`>}@(P8P |7p`?~/@ /|'0_7p'p "Lp!ÆB\|/|70_|70߿|˧/_|| (p߿(П7_|'0_|˧o|'P'P?˗o`|'0_˗O@~ 8P`> H*\ȰÇ0_>~70_>/>}o_>~|C"O|'0_/>}o_>~|_| O?/| |/|/?O~0#F1bą9̗_~ ̗ |O_|˗Oϟ|5/?7_~3/_|ӗ/>_>O_|807p7p ߿|/_}˗o߿}/|/_>~'P ? 4xaB 6tB}"F/bA"/bD"*/?}"F\/|"F1bĈ1"| |#2Q>~1|1bĈ#FP#F,/_E1A}"/bĈ 1bĈ#FP8`A&? `+X> 4xaB8>~ $(P?O@ D0a?,h „ 2l!ą5 <0!~ HP| /_ H"#H A$@~,h „ 'p|8`A&TaC!./bĈ |#FhP1bDE1bĈ#./bĈ |#FhP1bDE1bĈ#./bĈ |#FhP1bDE1bĈ#./bĈ |#FhP1bDE1bĈ#./bĈ |#FhP1bDE1bĈ#./bĈ |#FhP1bDE1bĈ#Z }8`A&T| ǐ!C  ǏA}2dȐ!C1dȐ!C 2dȐ@}8` H8`A'p O@ DPB'p O@ DPB'p "Lp!ÆB\ ? $0 O"C~$J(QD%J(QD%J(Q"|$2OD%J(QD%J(QD%O"C~$J(QD%J(QD%J(Q"|$2OD%J(QD%J(QD%O"C~$J(QD%J(QD%J(Q"|$2OD%J(QD%J(QD%`|/_|-OD%J(QD%J(QD%`|!?}%J(QD%J(QDI$DP> O? '_ >$XA .dC%NXE5n$ϟ|ۗo`w>9rȑ#G9r(1?/|Ӈ>9rȑ#G9r(1?/|ۇ>9rȑ#G9r(1?/| ?}9rȑ#G9rQb>ӗ_Oqȑ#G9rȑ#GqOG9rȑ#G9rGqȑ#G9rȑ#GqOG9rȑ#G9r,h@~8`A&TaC!F8bE1fԸ`>Ǒ#G9rȑ#G%Q ?}9rȑ#G9rQb>Ǒ#G9rȑ#G%Q ?}9rȑ#G9rQb>Ǒ#G9rȑ#G%Q ?}9rȑ#G9rQb>Ǒ#G9rȑ#G%Q ?}9rȑ#G9rQb>Ǒ#G9rȑ#G%Q ?}9rȑ#G9rQb>Ǒ#G9rȑ#G%Q ?} H*\ȰÇ#JHŋ3jH0?1?9rȑ#G9R珣@~8b̗/_?9rȑ#G9J珣@~8bO@ H*\ȰÇ#JHŋ1 ?}3 O@}O@ ,? 4xaB 6tbD)VxQa> O@ DPA}'p ? 4xaB 6tP> ȏ| $H A'p "L80B *ϟB O@ D |>$X!D!B"D!B C_>!Dp`> ̇`>"̇!B!BC!B 'P <8P?"D!B"Da|O?}ԧ/A~ϟ|/?˧ϟ| ԗO߿|/_>ӧO߾|ӗ/'_|߾| Է/?"DB Ӈ!B̗/>CA}"D!B"D!B O|/o| _o`>_>?߿|o?}o?o߾߿| H 0a%L0aƒ3O_„ 0a„ &L0a„ /?3_>'0?} O` G0߿|O`+/߿|/_/a„ 0a%L0aƒӗ0aB%L0a„ &L0aBG0|'0߿~O@߿~_@_/߿|߾|/߿| _| '0,h B8`A <0B-\P .\p… 7P_|g0߿|O?'0?~ '0|| | |}/}O` O@ ,? 4xaB/_|gA}.\p… .\H0@~70|/|ϟ| '0?70O_|O |O`׏_|/_~O_|o… .\p… W0_~ wP?$XA .dC%NXE5nO_|/?}ӗ>~9rȑ#G9r1"?}/? ;G9./_|W1|=W0|9r ?}O|Ǐ#Go~ )'0|w0_|qȑ#ǃG_}wP?9r\_O߾|˧߾| ԗO߿|/_>_>/?~/>~o_}/@/_~߿}O@ DPB >ۗϟ| wP#F1"|G0߿|߾}#O`~'AO`_ O`>۷_>~O|߾}1bĈ#F\O_|O| wP#F1"|G0߿| /| '0_>~_>_ /|˗@~O`1bĈ#F\O_DE1bĈ /߿߿|߿|/߿/߿ ߿_߿|@/|O@Ǐ_/߿'p "Lp!ÆB\O_DE1bĈ o~'0|/| ̧@~_O߾/_|O`>O? | O߾E1bĈ#./C}"F1bĈ˗/_>~'0|˗|ϟ|/>}ӧ?}_>~˗O>~˷O`>7_|'0?1bĈ#F1bĈ#FQDEQDEQDEQDO,h@}8`A&TaC!F8bE1fԸq ?}Ǒ#G9rȑ#G#q>~9rqa>9rȑ#ǂqG9rl#G9rq ?}Ǒ#G ߾|ӷ/_?} Է/_?} /_>}ȑ#G9j䧏@}8rQ!}۷}󗯟|󗯟|˗?~8rȑ#G8P?9rTo|'0| '0|#G9rȑ>ȑ#G_߿|/߿_ۗ,h „ 2l!Ĉ'RdO_EUXbň ̧o?O`>O` WbŊ+VX"C~**ǯbŊ+F/_~_>O`>o_|WbŊ+VXB~**ǯbŊ+VXbŊ+VXbŁUT_Ŋ+VXbŊ+VXbŊ髨P+VXbŊ+VXbŊWQ>~+VXbŊ+VXbŊ+䧯B}*VXbŊ+VXbŊ+V>$X'p "Lp!ÆB(q"Ŋ/b̨q@~8Ǐ#G9rȑ#G9F䧏@}8rȑ#G9rȑcD~8Ǐ#G9rȑ#G9F䧏@}8rȑ#G9rȑcD~8Ǐ#G9rȑ#G9F䧏@}8rȑ#G9rȑcD~8Ǐ#G9rȑ#G9F䧏@}8rȑ#G9rȑcD~8Ǐ#G9rȑ#G9F䧏@}8rȑ#G9rȑcD~8Ǐ#G9rȑ#G9F䧏@}8`A&TaC!F8bE1fԸq ?}Ǒ#G9rȑ#G#q>~9rȑ#G9r1"?}Ǒ#G9rȑ#G#q>~9rȑ#G9r1"?}Ǒ#G9rȑ#G#q>~9rȑ#G9r1"?}Ǒ#G9rȑ#G#q>~9rȑ#G9r1"?}Ǒ#G9rȑ#G#q>~9rȑ#G9r1"?}Ǒ#G9rȑ#ljx>? 4xaB 6tbD)VxcF1?~2/cƌ3f̘1cƌ 8`A 'P| <0A~*TO?$XA .dC%NXń8`A oA~ HOBSPB *TP O|O@ `> Ϡ gРA 4hР4hРO@ DPA} HO@ DPB >o@}h0|='0_| AbA~ >O>П <0} H'p "Lp!Æ̧o| ?}| ߿}/_~/_>o|㗯?~+o_}O`O߾|˗?~#/_>"DA|@˗D ~ B"D|O~O|˷o?~'0߿|/'0_ȯ`>~'0| /_>~o?߿| HCA} ӗ/?"DР|!DH_}"D!B"D!B_Ǐ` O` G_'} O`|+o`> 7_o|o`>C!B!D>~!B/?˷!B"D!B"D_o`/߿~'0߿|O?~0@߿߿_Ǐ_Ǐ_/߿ ߿|_'p !BC!B"/_>˷!B"D!B"DP`o`>#OO`/߿|/?}70| ̧@~_>/'0?}O ?/|!D@~"DxP?"D!`|"D!B"D!B/| _?}/_>}/?}/_>}O_|˗|_>@/_>}/_>}?}/_>~˗|'p `|ӗ/_|!B"D|!$/>$XA .dC%NXE̗O`|=#Ɗ_}1bĈ#F1bx/|/A}0b/_>ۇ#F1bĈ#F;o~ G0|E3/>1b1F1b/|ӇP?1˗~aĈ#Fh1F1B`>'0~!#FO`|0bĈ#D}㗯@~#/_~@~/_>/?}/>$XA .dСA~/߿|̷/=|CoÇ>|Á/?~O|/}۷} /{Ç`'0@~wP>|/_|>|Ç/|Ϡ| o| '0߿| Ç>|!| `>O?~߿|߿__>$XA .dA~>Ç>o_|>|Ç/|ϟ|O>O߾ _Ç>|xÇ_|>|Ç /|˗@~ϟ|ӗ/_?'p_>}O`>~>|Ç{>~>|ذ_}Ç>|Ç>|Ç {>~>|Ȱ_}Ç>|Ç>|Ç>$X!D!BoAO@ DPB >QD-^Oƃah_} #F1bĈ#FӇ>~1VoBÈ#F1bĈ#Fa<F_|0bĈ#F1bĈ ?}~=/?1bĈ#F1b$Oƃa(_}#F1bĈ#FӇ>~1FoDÈ#F1bĈ#Fa<FW_|1bĈ#F1b(>È?~È#F1b#0 ~8`A <0… :|1ĉ+Z1ƍ8P?9rȑ#G9r>ȑ#G9rȑ#G8P?9rȑ#G9r>ȑ#G9rȑ#G8P?9rȑ#G9r>ȑ#G9rȑ#G8P?9rȑ#G9r>ȑ#G9rȑ#G8P?9rȑ#G9r>ȑ#G9rȑ#G8P?9rȑ#G9r>O@ DPB >QD-^ĘQƁqG9rȑ#G9rOǁqȑ#G9rȑ#LjqG9rȑ#G9rOǁqȑ#G9rȑ#LjqG9rȑ#G9rOǁqȑ#G9rȑ#LjqG9rȑ#G9rOǁqȑ#G9rȑ#LjqG9rȑ#G9rOǁqȑ#G9rȑ#LjqG9rȑ#G9rOǁ'p "Lp!ÆB(q"Ŋ/b̨q@~8Ǐ#G9rȑ#G9F䧏@}8rȑ#G9rȑcD~8Ǐ#G9rȑ#G9F䧏@}8rȑcA~8rȑ#Gq>~9rȱ |qP|O@~88_>$X`|4O`wwO@ DPB >_|+ ˇ!#`"D ?}"<,h „ 2l!Ą8߿'p ̧? 4xp`>?ӗa>ӗo߿}/_~/_>˗@}㗯@~#/_~ o_>~ӗO@}/_'0?~C!!D>} H*\ȰÇO| <8>7P?}̇0|O߾O`>_>#_~O '0|O`O|o߿| !BO@ /@},h „ 2l!Ą (p?8`AӇ!ƒo| CO`>|'?}/|Ϡ| ̗o`>#| /_>~!D? H'p "Lp!ÆBL`>_>$X!D`> ܷa>o`Ǐ_> _}_o@Ǐ_'߿/߿O?~_O@ DPB >QD˗O?UTO_E_} !_>o?_>O` O`>_|O`?߾_Ŋ+VXbŊϠ|WQa>맏_| 70|ӧO`>}ӗOӧ߿||#_>ӗ/ӧ߿}/>}o`>~ӧ|/?}O@ DPB >QDC/@}˧Ϡ|wSbŊ+VXbŊ+V/_|O~W"~ UXbŊ+VXbŊ+o |O`>;O_Ŋ+VXbŊ+VXbŊ+`>~ 엏`UOb {O`|XbŊ+VXD #/# ?}+6'0_|拘O`UXbŊ+VXQ|o}w/?}} ܗ>/|˗ϟǏ?~o_>~˗?~/_>o_|ۗ@ϟ>$XA .dC%NXQ|̷/~ '0A~,'0_>_>~۷_| _ G0߿|'0߿|?~}_}'0`>-ZhѢE-`ӧo_|'0A~,'0}O`#O`>'|_>篠| /߿|O`>#ϢE-ZhѢE峘> }/߿__O@/߿_o_>~_> /_o| ? 4xaB 6tbD)V/ńY<_|_>_>>~_>_>/ӷO`ӷ| G0|hѢE-ZhA,&}/_~'0?G0O_|O |O`׏_|/_~O_|`>ϢE-ZhѢE峘>'pE-ZhѢE-Zh 8`AӇ!B ˗/?$XA .dC%NXE ˧ ?}5jԨQF5jԨQcD4OƉ80F5jԨQFX>SOa>ӨQF5jԨ| ӧq|/?'_|Ǐ_|˧|ۗ_/߾|˗>/?_~}iԨQF5j/Ƃio߿~O`>~O '0|o߾O |o߾}_}?߿|_>~'p "Lp!ÆB(q"Ŋw!?} /?_>~G_>'_/|߾~ w0|O xŋ/^x?."b|8߿|7P|/߿ ߿|O?~_O| `>/,h „ 2l!Ĉ'Rh|w>~O ? O??@~`>?~/?~O ? '0?}O`/^xŋ/"B~./߾|O`>ӗ/˗O>~/_>~ӧ |/_}˗|ӗ/?/|˗߿|]xŋ/^x|w?]xQ /^xE]tE <8>/_>"D!B'p "Lp!ÆB(q"Ŋ/w!?}/.wŋ/^xŋ/"B~.^lŋ/^xŋ/Z"B~./O߾|'P߾|'|ŋ/^xŋ/F"B~.|_>O`>O`oŋ/^xŋ/^/E],o|'0| '0|ŋ/^xŋ/N"B~.G0| '0|/߿o_>~ H*\ȰÇ#JHŋ! ?}'0}O`>O`>70_ƌ3f̘1cƌ3B/A~2/_~_>O`>o_|˗1cƌ3f̘1cƌh3f̘1cƌ3f̘1A2/cƌ3f̘1cƌ3fh_˘1cƌ3f̘1cƌ3/A~2f̘1cƌ3f̘1cƌh3f̘1cƌ3f̘1A2/cƌ3f̘1cƌeQF/,h@~8`A&TaC!F8bE1fԸq|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?}9rȑ#G9r1|Ǒ#G9rȑ#G#q ?} H*\ȰÇ#JHŋ3j8_>ȑ#G9rȑ#G8>9rȑ#G9r_>ȑ#G9rȑ#G8>9rȑ#G9r_>ȑ#G9rȑ#G8>9rȑ#G9r_>ȑ#G9rȑ#G8>9rȑ#G9r_>ȑ#G9rȑ#G8>9rȑ#G9r_>ȑ#G9rȑ#G8>$XA .dC%NXE5n/ǁqȑ#G9rȑ#LjqOG9rȑ#G9r/ǁqȑ#G9rȑ#LjqOG9rȑ#G9r/ǁqȑ#G9rȑ#LjqOG9rȑ#G9r/ǁqȑ#G9rȑ#LjqOG9rȑ#G9r/ǁqȑ#G9rȑ#LjqOG9rȑ#G9r/ǁqȑ#G9rȑ#LjqO,h „ 2l!Ĉ'Rh"ƌ7@~8rȑ#G9rȑcD8䧏#G9rȑ#G9F@~8rȑ#G9rȑcD8䧏#G9rȑ#G9F@~8rȑ#G9rȑcD8䧏#G9rȑ#G9F@~8rȑ#G9rȑcD8䧏#G9rȑ#G9F@~8rȑ#G9rȑcD8䧏#G9rȑ#G9F@~8rȑ#GǑ#G q @'p "Lp!ÆB(q"ŊWbŊ+RB~*VXbŊ+V`>8`A&TaC!*/C~"F1bĈ#FX? 4x80,h „ 2l!DEtO_D O|O@3hРA4?3h0_O@ HO@ ,O|,h „ 2l!Ĉ8`A !D!|70B"OB|O@ DPB >>   ˗@}㗯@~/o_>~?}3O@}C!BCA}O@$XA .dC%N_>/@~'?} //_'| /_`>~o`>Gq>~˗"E)R_~׏|/|G0߿|/?˧o? O>'0߿|#O`>_ao`>Gq>~H"E)2ԗ~Qd@}߿| 'P맏|o@߿___߿|ۗϟO@ Dh /a„ &L0a„ &L0aB}%L/ >~ GP_|O ?'|o`>}o`>>~O`'0?}|㧏߿|%L0A~'P_>}˧/@}&L0a„ &L0a„ &/_ӗ0aƒ'0|O|˗||ۗ/_~|#_>ӗ/?ܗOϟ|/_~_߿8`A`|/A}&L0a„ &L0a„ &/_ <0… :|1ĉ;O? 7_Ǐ"E)RH|˧"E)RHD~ǯ߾|/QH"E)ԗ~QH"E)RO|O`>CE)RHA}!OEǏ"|)RH"?}˧_>˷>~)RH"Eg_>}'_>~'H"E;_| G0߾|G"E)RP_~Gb/_>>}/_>/_>ۗ߿}O_|8`A&TaC;| 7_Ç>|Ç'_>}.ܧ?o߾>~ 7_>/߾}?~>|Ç{>~>|Ç>4/_~=|ذ?}/| ̗@~O`> O>>|Ã=|O>|Çԗ/7_| /@@Oo_>~ H*\ȰÂ=|O>|Ç/_7_>}ӷ|o>~@~O߾70Ç>| ?}Ç>|Çӧ/_˗/߾ϟ|˧/߾o?}O`>_|Ç>OÇ{Ç>|a~ ԗÇ{Ç>|Ç>|C#/_2Ç=C%?} HO@ DPB >a| OD%J(QDP?%J(QDӇP_~%J(QD%JOĆI(QD%J$/>(QD%J(Q?~$6OD%J(Q~1ԗD%J(QDP?%J(QDP_~%J(QD%FObC}$J(QD%OD}I(QD%J> (QD%J/>(QD%J(!?}'QD%J(" ԗ,h „ 2l!Ĉ'䧏B}(RH"EGQ|(RH"E)6䧏B}(RH"EHpE)RH"EQ\E)RH"E)RH"EGq>~)RH"E)RH"E)ROŅQH"E)RH"E)RH?}G"E)RH"E)RH"Q\E)RH"E)RH"EGq>~)RH"E)RH"E)ROŅQH"E)RH"EQDEQ@'p ? 4xaB 6tbD)VxcFq>~9rȑ#G9r1"?}Ǒ#G9rȑ#G# <80?$XA .dC%NXE5n> 4x`?$XA .dC%NXE5nG!E$YI)UdK1eΤYM$黨O_Μ9s_>}3gΜ9sOD}̙3gΜ2Q_>9s̙3'M=ԗ_Μ9si_>} 3gΜ9sOB}̙3gΜ9˧/|r̙3gΜwP_>9s̙3g΂+/9s̙3A ԗ_Μ9s̙3|/gΜ9s̹_|̙3gΜ9˗/gΜ9sP|O@ DPB >QD-^ĘQF_>};vرcǎ;vر@} O_ǎ;vرcǎ;v/رcǎ;vرcǎw_>};vرcǎ;v1|˧cǎ;vرcǎ;^ԗBuرcǎ;vرcNJk/;vرcǎ;v8Q_>ױcǎ;vرcǎ#/|:vرcǎ;vرC}MO_ǎ;vرcǎ;vl/رcǎ;v쨣:!O@ ? 4xaB 6tbD)VxcF9vdH#I4yeJ+Y;;PK VSRPK|%AOEBPS/img/adfns102.gif )GIF89aҘTQRgeeurs745,)*>;;>:;gdeKHIuvvyz|{|~mnoqrrvvyGCB}~8560,-}{|CABebbƷMMMrsuKYZY===srs;8:kjljhjedfIFGaabޮsuuopq老yyyA>?xwy:68;;;ZWXZWWQNOȿ~㧩ՃWTULIIusu0--䭬uss֟咓ع\Z[̭YVWVSUί\[]ϘopofffXUVϜklmsqsghhUVVGHH˴=9:˺}~۷wrsphUUVWXXͲGDFECDIGHPMN?@?z||RPQpqrxvxWVWMJLonp|}|ᓔbN垝zz^_`Ѻ`]^ggiVXWϾ_sB?Aqnofeg# !, H*\ȰÇ#JHŋ ÑG>^B௤ɓ'E4a2ʜI͛8s`0v/H´S~YSԤyӳׯ`Ê{s.^E&K-ps+)ET߿ POP/sNV fI5,B̹oTT Rjf԰c˞M8ùY $.KLȓ+,:7c-eO43Z^νoEe[ogDF8')@}rXuWFV\XGdFّ fG%_8Fb0xPDy2Ispb@fQsqZ:iE}>"~oO=xhGr$yhVQk&OYǛtB@bYٕKz񣞀ŇnSslg:h@&9Fj)F$qiOh妠JDbT*C\ظNeLz櫴 ?Ԫsxkh5ϜĖj?&{sl|1-NZ鵋*޴2:h6+-oza-3e[)hGLKfj/\.e 0V4, /O.0@ P$IP2ks)@!NikLLιͼGK?q.2s uN 4\w@qa3j`ig@L,e4A[-AգˑX.x| TsLifA?^iAR양b9t›'kzb_8ګ:A.Pw{Akpf_td:xnK+W=BrtFo*9#7~Bfda鋹elL!vHJ<0`CTA{!BӿI P=ݤAx2a?3BG8&BAY =ΰ35u(C3aMnR" wG@K1:,tc(e8%2@y#f2I\VT1LihǺL)h,Q"'~ōoL{" *'T&I/B>li!Df~jrJ2M&9IOk+KrbenK_W~` 'ļ1dk̉3hd쪦5kMtisڢ Nn,EYt" ;sŗ>O|΁X''ɓl-)MBІ:4K@ τd+ˉD'*"XQx =HsВ;IwD-u0a$`T9qPS9w:ԢHŖO-RF6BuSs՛(5"xV| *QS&p\JWHxͫ^׾ )@ 8\QVJV󱐍l? :R?" NJ氶N2?aեlg+ ?ʀ0imP?M*A5mX2 t~™ {ad҆4n Kj6Du|ar׾+EP67O?6% DZ;Mmy :#Hz)ஐ4NNj ;Dqoa@kHЦ& 7u Rbd)pX\-ܥЀ`B6W]{b ׄ@]oVfTU֚}MX\4'mD8kR,X@ M>Y\Dž:91)ΚX[T?$.oY @@ &BfO-ڪlK!6G 8gl'k Zh?&2Y+ .9]Y5 iP-CMwʠpg4%MOu. opuY=GqDU+f;N/$N']+Nd{rXGȵ,Ő`L:ܻDD5UN; 9JPeT߹y4\ivs کśN5_jÌ1g"5+dǛVaI;6 q=c[%lopv]rǹ=3N?1/n7zԨ8-G3FI3znɷQN)u:tOs8Yxŧ1Xӳ֊:A(1V8(>K4ؘS.w4HI(BJX76q苩wDQR1 x=ǐNJnA!C鐉!}{6()?*F,9.9(3ᑹsfFh6>)@5)Ք(!X"aR|"`ISk`b9dYfY!X!m-i~9 X0/ |+$ QqH[w%A*sxAK)M`pb 9*g:(or{pI95%Ij.o)1O?MPVUٜйcM,4!%4xXSUy9Ĺ(Iqι)ird')21F4+䘛k%j9Ui ih4!B95:IRp z& ,ނ#sϨ3<Hfu&⩕DHQi(+ڢLƝ&QH…|Pyv{-y&o/hpKs4LʢNmSA(^*w$f)*&jڦ1ze/DzAWZpƘ[HR'$&3(+yt*d5Y(z /PZ}'N8E1JD3h9Fë8pqrYVXѬD4(JM ׺TSQ'apxujyuBʜ8jrj6 ](B"Ag 򺰾*DAUq`9ڠ k rImފ*tگ3 1 ap%ai4Rq̸9Ѳ Es)6{͙%%. %f]GC˱RI Pv P@M.[ 4g U Pp+Kn:@۶Pp^ p Uйg5u똺 [ u+ yˤV@ K5Q؛ڻ۽;Ἕ#i|ҪN۔kk<~Ic0[{K К *I65J zMyۼK,ADN9ǰ(*,.0‰0`tpeɚ [Yݨ bHJ4;Ǚ`ۜ^_\|J Fd\1ğCQTV|f<ǒQ1* bz|~ǀ,h|j Ȋb6X^|ɘx6t,zG¼ Ǐ +\ߡ@ ko,gG+c(,OJPʾP 4`  0ܢ|h<jD9C{@+~̶ܷ^'͟14)l2&CC%ް̦m,u,l~!E[DMo`  ]Dʫʯ˳Hb&O(v01+4ǜ|*a`O| ,'p8+R>aw1TF}l wys*΄,k&?SOOc=C68 RFGd*&A(eNaR?Upo]%,Ld}hX<`^&^So`!iE=ڤ}I A( =9Ut%%ac=l4j"f]!-?=.+\Ea-ף>2* 1ҦA:x >Gm:$Uޒ}Sg:Cu9f{+qR`PcZ$@s~7nұN.]Ͼ8!cFb_sܡ(OS1׭a2R(߅uy7d]-`Ug-#V# d58'p%*e_c-}8Z I=*{.{o˨)~󊨵]IKNxup`X^42 bͮ3#Gs^^Fme"&o>~ ތAUQttc4lv^say'osHK<} }®O7 o~wHQ8mYpuHjɵLU3d+qVQd[=F\: _82@\o+uq`|?է5PteosZ.sZFiZЊ=(aotq0/cn[9c7Rnqbg&\ $XЋ- c/KA3NXŊ+G!E$_)Udٲ 5Ur9u'H;OP1π-YC0,xcRJZϏ.lp@,fih.Ɖe89kjLȱa$Ode5k2lr3qٞgEiFtE'}8ecvΩ9ڍm%樜e:5s =?pDcF5ᅴ'^ػcďcvwr[X+ro n(B#b@ԞnC-@cΟTZl̲ERobʘ?H o kL,6m,6p+L7(Cǣ8+4{2c5.l(#30%sΔ`TңCjoO;ãP q433$ړ &:P*8JX(F(Jzm;h =b;>2Pv1&Kz/4b46|Ԫ xhȢ*VQt=;VP WvuW~U2PaU$Vc;r-e;BcgRJKðx(SRp.|;7"udWwwף? 5_]JVl隙5B8j&6&И!ϐ'=ymw'DGGp "3*Kec 5Ysܣ/+w")bs.2fo\V%qkX ߴ0U\ޓ]sW;B)8 |I] .jvs}w2<8Wd\ Bb S9o(44}C{|/dр|7?/:]1Kd H984 e*@XF wArCg~ߩ1hx@4TEQAgmʰgAn/ E:XDDV#lI =k:nǑyXrBHV?\,wk\c>ڱQwnlD&d qCG@R$/)o(UCٸ2 @+640CqEǾqй" ,rĉp(0<2j(3ŜTQ k0Cf6<?.iKhREܗ@ Kr;u8p(5D$Y,e;Zb- f@2YeGe[ @6 iA[; ȸDAg?OT CvLy>kiI@0DThE-JQCT2Kz CI1!8(s@N]RT2%e s9+XfY4q*It@H-p=,iUz՘Ԧ8N&n4["'*GQ3Ug(Uks؋&Vט"C %A-*J7?Pj+Lp: s=JPB|%I 5Vemk]Z6C`=+U cJV +gQ;P$z@" pt(#.X$#<(v |N`]{lsllgNRXͨo \, 7 + t0#aAT% E/5Vx`*ܠ R+@,NX9Je\ ?~r\R,g9i=F an"X$ljS`1qoU"$vpT@ EX &8G,}iLa'@6'm9HY;2aC':`| ~2S[e.@kH D~v=kg{v8BrB ]z!!| x'^7tr;e@=I \"ܶ3L稃5D<;XDFlDG|DHDI| {C&z BrB7+D؋NJ@ȀGXEYEZE[EZKD݇Um,V̘[֏6ݨIESf*x`t0*Ek&lU:Yyn%W0'0W~`EXU*S&m2VźWUsT%ءLX=XRE%Ёv U]k"9ׇWUWXXY?%Ք:W i$^}ZZ(Z>*l3EI9; Y[ݎ78H.RIJuk9ɭ[ݓ:P>h3iN4kI [=\ =[8PVՂ6R0[ĥZ܁ܝ[.V lIٓ( 5;&&/P6m%7܉IqZYޥxښ=-}GX 75.(\ 꺭M ߐ0%=Pݍ*MCN1S]-$]`& _ڹ V7ȏa(S]]aw3=P`߭.a ba)"`5M`!nb'VC,ݼ78G /'b/b0c1c2.c3>c^4nc2+6Guc:.7Q= /m-]X=n%?y:^Ý+Vy),xICNVIHfdP[2e*`)OԃraeTPeՃ=䥈1ރta]cZ\n.0e ^aP㭃f&fk&Wj.:+ jhfuf.jfuT=(vV*J5>^g~gg;PKE) )PK|%AOEBPS/img/adfns101.gif|VGIF89a>;;usukklLIIξ~}~0--䧩ZZZfdfYWYĮDBDWTU,()չž̕ڻ433geeTQRۢussPMÓtvwRQPQyyzyz|򥧩ub`b𽿿TRTsrszwwvwxNLN}{|IEFrrtӅ856ւdceQNOJIJ`^_@>?xwyopr~^^`A?AZWW{|}popʑstvihj֬򺹹{VTMqno󧦜Ҁ~yphVVWwx{`_anmoRRSXUVLJKXUUȷ?@?dabSTTIGH;89opoՌ}yzONP߲FGGᇇOPOZXX778湹302_`_# !, H*\ȰÇ#JHŋ3jxє4)ɓ'y#Ǘ0cʜI͛8s% JTHM(ԟ"UyիXjʵO!%9` LDuZNh3[JO˷߿T)ڵ%0Q=4y ˘3_t#:UV'ƒ$ y2װc˞0*MM;vAr;;塄ȓ+i P@^LPEC)! oO~W=n#9_hF8Qt&9hZCg bXI@@^hw((H( N4cGQEdJ([yHdE,PF))!5i'Xv%O<$&tkVtR @'xfTXT'2VWJQ&摨(fԉYI*^?jrzc꫰Nt8Z뮖F+U k^3ʱuFH;i6%'""TVa.T?[iACAl0B\P 7a pP0a 6\Kr:vK<$K|Y|%|$1C-G#n^b+sRKuNaq5_22AfmmUc1u,,rK?y_-]vSs=n JxRjJAqOnl?GDΩBtJ:Ahp ǎ6$Z:A8Q ]G(͟|utߚ( q}+T~J ? .r?AJ _4= a(Mɠ7 Z ! GrNӃA*_B {D8̡w76BٌP"#ɩ(4Lp&q.zч) basD9hdcQ9ѡF/? cSF?M15DD2549 =D(քx$o=/i## 2-[ТH3M Zx,JtXIKj5YLcLY|2. &0i$qdVXyC\򙱄AkiK*n9M\Pͧ,Tf_ d@4X\s[::R;-P P j0V<P~IhЀ:g)OhFQu HG2#8lLS,WRR ԑP( CE)<Ӟť3AHT'P1MujW:V&4`[4j5>"sdS*֭XR`;QjFtu}}jI-"Xͬf7{L,Ӭ7DY5h,y_(Q>2h>:WtX\& }bԁV~طQ:x|brLe+3aW8;.?¤dA+5< :YVl;a0u\)AFJ$8গ{(u+n%qzU)1SiFTy'0\BNvf<)Ք:'㏘G(n|/swT&=8-@wX'ȶQLB)g鈊ٓ#L;ĘL'E)E|UArQ($PNu@.4Xͳ "̽q[T9llxsP&Lw)a렶8<-/]oyYI]H"emĭ)>mo6whDo`pь:eMl༌ 4R~ Ma `JKqpX?P~xN[`~;Ø`r痭FdFUdP3PxVA *Bf|9@T $j{>k0Ʊ 1|kaNtʆ|ud" "4!,W0"iy"xx٧x9[;UZ%|1Fw,Me&HWf yqAÂcfT$f!xFDNkSksvA|Gvg"0tkumד m2lHEp es[j_uSwsE831x9X:b5H2HAu1b7jA bURt*Xun#th -"p,xss3}ͷH'`: b\SBxVXEQ]x}Lb(1 tэBGD:u7PheE2h}]ACH(FQ3Ruf ِ'L9(E/؊vb-`nX{- d%_{hA| 1<%BQdS5(z2U i,~hh'W)ij+u܁f (WA< 7z+  aqa7!O(F:Qd@)2/$})fkz 9KšX AA}i'ӗsF@tIyYMRw`? E Yz+{́upV TRys֠=f' 91!D;tIDu'( $@K *+{Vr`;f:a?9UIɠQ"tIŤx",)=;5{XE] c% 0ک*M/:g*0^J)x6aV0ngSh2!qʪy櫺r饀:]JdDgqUeam%AF_:I De3I(~ŪjdZZuIHIYٴ0(<购˳`:=ɫ6a(G!y' Jj8e P>3T [;5!@!Ṯ Ѯ!PYu{k* 6{8+r˔^80&KtEŔ($\ZA 2e$ TK` p iPU+ e8svDh%%>B^0p & 9@6@Q@ Q] Ѱ P S0 2PU,<{`'A0a6h ZZ:ȸ̣ )|ȝ C4V[0c?PK*gpS6jҫhgy1PHП'ٮDYX$SזWF% &|)"͆/$6Wv}xz|u,0#N*x僙0\"H^  b‚ {,{L~ڢ=ڤms=.CfB& w#ׁl?]ڸ۹}DF1&-Ys~YhB;Gu8W m  ,s]=- 4_q ᨰ ⟰ nN)~*,>%`6a<>>ہMݿ V;h%3>6P-0^+.6n8˳y=d^f~hjfNA~ gtSNW.4~.X(S^7p-^`n/ y0^~阞^)CNB#_Ѱ|A?iM1&yM{NN^%剾̽>.ypʾ-p@r ;#;f^ii ^3NV>|廎賭迾=0^  chQHqC"g0 $z0>v#C!zN..K0 vB?DOuD*ϥ"R*NQXgMHGdf&O HN.ni6[ N ~?_}?N,tHUBr~" 3AJ=0 {QkSqAv'e#܌?.NVq뒿33@ ?_ȟ/BslID?&Ғ?Z\lGyP?c؆gjo2n/±yZ`"XA .dؐ $N8N&@Uر$4y$YI)UD%T'I&ԤibMO9% $NFiYrҒuNf1OXeEOjJ6O]=[YaDz䓣$ b*LF Yaĉ/fذS+E9p ߦ}5o 5hHSy:ʂ$=C#ZVY8ǧUR:mܸt.ָ[iҥ{"^^xu{$wկW$ HL@&< ϓXOݠͤس*@ Xz]&0] !0 !C?0D 51 k/N%qL@S {UnSz%RB%Hj4@dFfL0쨓ԠQM8⒤sN.N21[<:~QH;Ө;CS= tSN;SPCuT^LA#? DIHMTו&]ҎqǒUvYf;$x,U`7E!_adDPG]uAq< 0b0p Szx` &L5xa&SZ3Q]DxBoH(29o7)?Sb**(I4Ѥܕ@Xz`Qxhv@Sy `&8naW0A8"niHajj6p"7%bZ@u$P&2~9HXcQrK9[0^M͹QfFKE+HBqnsؙcdXks?Tp >,)2VyJv垢LaZXƅX&[-y0C9{V?ͩtbsS78M 9'  i:1EF=Nb㿝mϑ a!n"4P@~vA uDU<1M;a:#r{`HN|G!bx 66jB I_+Z٢x=‰$Sl<&}YH1!.-Ғd(9FI~rvY"gHFb'(i_/b6͉AMf)؈4e&QɧAT Ӥf5yMl^sգH.EƙDpH Df !xHJ) f Lell8< D/2)04BhE-zщna'.;t2H7 Dx⟈I x".^+u &VxR}Tÿ&b3dA*N<ƒ`l svIE@7"VNmɭ.¡;ƾjpMdvFSO5$eHjt4otJqK=8' ޏ<|ldn+4?) S?'Yvbb<v0}4HWD CDvWe?M,@ 8;qݑzyGLOfnesJ菚-l1V]v~7 lr(=\p2VxBgG[Ob {S$g &9f8Rd$Ft'؏* w2Std(AlTPhb:I(cpaХ@ h˾c=s=#ك#$y@U ߓوk<%'R 4/,Ј*@!Bs R @ш>!3z>I%QMA1`pLThԇ*8B#L `0#Q@<=.$*0D+3'tmx89ğa,lĵC&BD<9A$=@CL@BQL,*EDB@-TGLHAWXBC0:i& mFnFoFp Gm\Q-9EMUqKKbˀ˽ʔD䓴Ss$K K,MӤIǜȌźsxM،`|T"X% @ J$&, 3 °lůJ̚NĈLM9YMf;`mf6niE  X6ȅHP%H0Ki<tHͅP)]`W/`=Cٰ [M{]St n4Ԙ4K1X[9 U:|]?8`']ȃy<] ^5 Hhb @"8XUJ f\,J[#M_]_m_}_0\\=Lu 0YhYP]5 ;N28^BXb >`)(8Õ0  `f!_=J~{`%qvHhSǨ P. ^}ϫae`oU h`(4h0c)bިͅ kHާ0 c;c<йaQưb4;_(4[EX0I= 6[a8Q_ܡSƀHJ$%3%̓ 6QE 0wyP0F'@hsH+=fF i=.{_dB[ɏ3K_Eޙ$t&[1hT\̡ux4/s3(06HZ5"IX)j_/:3Ѐ ;5%"Y <3&ziid(h^`LPTW`hƥY讜h&ESYh?V`nQfٚN kf0{>P~ )0^N3^h@gV@׶³dLj,-km6e,Pp/K'Su5:EN ^ p╈"$s0@^ȅY>o\=YqoTB[*pqǐq_ž&%_r&or'g@hZn2Hs 3h^8`jMV>"Gq:_ !O  ogHA&pi@p(ht/'=h^{@VԮns;/eNuU_6<Auj*T]P hhV DK45O8OQ4o 4)JYtZGV/}]7un$u06O5ivQPGfJCm,F/nӉXvsH Ke$F0 y{t|vqݰ󮋧]Sk瞓iEoKx6}u0vH'8p&cuujh% YN6ס7yQq +S!Qsb() VlR˨82P.z0V,0.MD W~:Q4}wvݶ dߐ}PP|;PЀ[d_ Cȿ#|ȃXNT@P38}ZHOXMw,h „ 1Ԥp$I'RLɟAjOR%Xɓ@ $^BTiDF0dS:$^_ȍFi(@Q*t:hO:G8Ѯ_jScyt-ܸrҍ)a~ Q;E)buhQ#@E_(UD@m$9P??xdFknS(+SV9֕D'O=x|w?2Jڂ$P(?GJL垫^r홊gC"b̝@(^ :(\ ~0/rQ4P &!_ҧ;Ѩe9A*YmhTH6J,n$jAfH<-7Ʉ4QK5vYPMUEp= \ 49(, I2H0pq1FG |5YLA#Q/j] 3D!+@H.HZ,1'm&%{SU#NᡙR_%6e ݕ(Ac ( 8N&ߔh={=Ɉ*i~^wbӅ9@Q#&:PIi %1Y^GHB 3 >Y6(B  8! SF@7<($#kaR1(&B`OqN\] R* E(`ͤG53*  @"@O4dQDg}%Z <(HN0d?E>&?8 ( $ɐ;pD%W@x&2rQ`G"8#klSLIB%(2 IFBTɳ`↘U?'\d! 9oNMd [@ x' @ jXQƀ@(G&SH,*q^M!Hd(DRpGUs&} .ZC@$a1b@d@;)GSFԥ2U"UBQPˡ(EUE"}b H n3Մ l)m75?W 44+zVDV#U,_eS)>" I{`ga梶b ^Z+X¨~Pa*XS69z#-p+-.$kV=iZ5(lae!CXwIqS"JNնPehm+lQdeA\,xҟ{l~C{S17 ^& U|KG&6c,+70&.w +w Hb)N)^2X\&18ظe;Rgl>oaM yEFD<2%CIMWJ!+AZԐB &]3Lm0$P0^'(k <Τbn"$d!kY j 80 'ED@(e-# .A4XS|ݓOTk Ҍ6}O@ 1'\6 C( bD! AD 4` lنrg)-Zq`@uK~,o} 4~;/;!C'^g| ?䐋<P@$b ta.KD>IUSlvoMK&(Nlh֠)h#{^b+ew͵RD)(4C?tuRW1CVT-y,iOMEw^݂oƋƓ3w {=nOrH/??QWPG}UT? YalI4> FN V^ hBmX9^4, N mF՗Ŕ  2l^l}`Յ46!L!V^!fN!EE<!`? aa)Ʌ <  JLN   "!b Ch!."#6"@ n`V%^"~!`(b#\!C!<& 0D"֔ݠ-ޢj rXaE)!Ȩ".P++#ʢMb-24Vy`~0.12c2.43- 9#;`5zY/j#7v}cL@+#??#@c@@A?:d#FD& ;L(-e)%yD$.ifʀ铊E)$JM *4"'* \cdb@\Jꌢ)UPPD晅z6Dd/ AHJ@j\( <*]Aⱪc2DSD?q't$< D3@A`6j A:%lA*JmG}XGHꬺDR?t@)$AcҺvdOxˎ,2C%ֆ*?8Tg2o}L|J' K*\0}42 w#C2 wEyh2%p8@22',3A0(3o+4l/r/C6w\ ?3)4G8i9X>'i;;!Y@ p< -#NBMƱ8֟Ko@Vf캧#'B3}\)@?'H=&?|tHIt7γ4E4tTs|JhZ2|BO_rL6tAM3SgFV(p_}f?,1 ?luLXmxM+tNۖ)@BH¹tJ\]^_1Zo>['@1}pOU|.1~3 J6>=c7MMRt\bEZɚC|>#CM\5ф9~AH4EPt°\j冉/t%#㛉a}sq[?\E4FɾIH T ~}hv? 4xL >o@F &|(JO,ҽ "̴ ;T("ȁ#F鄤8e03ltǁtG X V0 ҖL5,с@N2yۨ:˲[9Un`\iԓዴ`VbtdM?%HlgD4@ȤX5 @Z`&D\uS?%5ߖ͗_,`LuY["IDe2̄LR`qE1bb*ixAz1wHmn҂u9e+Sܴ^Uɖ JiSYj} 4HNeFyAYqDI%uՠ p0DB]-Ҩp&.S:כeNAH4*J fH pÿNW{HNA0??zQ>7Tҍ?ӁK=ՏV<:ek%6 6r DYC;oʟ_.yizlZ,n1M; ,) @Fy?旲lk݈WySDNHW s>o :bPֻV"@H"Q"lVM\֕İBm[ ehQւ?(NS &,vrG?pH-Fo|@0,׺IrXl&)Xܢ5(HYJST*Y€U2/@sILBJ% 0YLͰTf1<ޒR}4Ir&0m609)d䒚5{Mo݄2iH :O7x@ ZP M(KfP>t ,ȟ"Q|)?93&K$faxC")UJYRF;ZSSS*HhҙAP7u^8JҧDTz^UKJ"FOeJLZVr0Vʧjv+1WՄnBVjYZWwZW@*;Pw 8 }h(2@]7XS`,c%6KZXƮcq(V;[P&pp؀ QЂ,@ AE`ݤVm,QXZ*p07Њ`@jW끵zB^b4Q^'?X8eЭb2ޭ xt6Y9TB5a"^ذjS0h/>}9`Xu\d#IVd A8lԑN\JGb^ja7e)[ yq0|yK]aݬ:8q!笮 붆4 7ρ4<M:\"`IJ[ڻ;ט}tiMf3vr]}F]kcԸ~٬=t:dMe NB,3+S馴D{cr5zb3b@`'`{| 1-n;lJ90Qq_9q$408lEMu9M\jf!J`a .^ 9y'`[se|ܷNQN:qv ֹuĹta&2@Hhɻt(f63YCmނ|w7x R]H*aQRFFt ŽyJ*V-7J7ÒIވ-ĩSy{S@$e0/G=#=K[aD8--OD}},u|sCCB0( F1!??G AEFcg^9H}  t+:b Jd.1C J3PJ)R(>B` &ԞK-JE 6!Z #9sCM'MK"9?bTC2!454+0p#V'M,$YNBu`Eb V, 4O#nbc.2ZcZXf ddFXHW(b#B9earhQhuD{W7V@OjYdvMβVXsMdl{U"cfHvFR59M^dgE@6'6me6,e<%[>lRNbFRlpq;0&;'=m"J&)V-W82WʲvA ``+TMYWshBAp7SZ@ xK8AxDwwo< ( |K6y y/Q 7 Z!K` x`|d}7w~|~K &727}}CV{xxgah~ v0C ~E &fk؆} J@wx)gB@<y]hB`I+ @  *xh؋XBAǘ8X@؈/En%I`! w9ט ِנ޸lGl瘎%% Y7\y `"|GU'YBb{;yƀkg@*}#y!Z pSYA{aY D9AB d&!|!swB@:ٞyvz9:J7 ZxE> 9CH[az7` J` D~}a !y)zs(~yAB  F9-@[e6\ AB@hYzO9]8 KZKZ֡lZ%kJY"U Njjԑ`gv`zJY7र[=ŮM".u/)3b@ۦZQgʹ(lL;{yaw{۷,jj*Q!B0LAl8fۻ[Ǜۼ{b,HLE/Ý;i?I@콑J h\bC\?<~C*1ZPVhz`Ud4PhX]PoܦhJ(c mT`5; ALn*|oLB\Ab\,p:a$L XQ輣DAQ\B>| }]F=91>`%<f| ]y==CABP(a(e;]_4k0ԗ#XP4s)Ca DY\=l q]ٟ<x.^bPN-İ5֢iHx|ۉٵ p*)]m$5A Using Indexes in Database Applications

4 Using Indexes in Database Applications

Indexes are optional structures, associated with tables and clusters, which allow SQL queries to execute more quickly. Just as the index in this document helps you locate information faster than if there were no index, an Oracle Database index provides a faster access path to table data. You can use indexes without rewriting any queries. Your results are the same, but you see them more quickly.

This chapter supplements this information:

Topics:

Guidelines for Managing Indexes

Here is a summary of the guidelines for managing indexes. For details, see Oracle Database Administrator's Guide:

  • Create indexes after inserting table data

  • Index the correct tables and columns

  • Order index columns for performance

  • Limit the number of indexes for each table

  • Drop indexes that are no longer needed

  • Understand deferred segment creation

  • Estimate index size and set storage parameters

  • Specify the tablespace for each index

  • Consider parallelizing index creation

  • Consider creating indexes with NOLOGGING

  • Understand when to use unusable or invisible indexes

  • Consider costs and benefits of coalescing or rebuilding indexes

  • Consider cost before disabling or dropping constraints

When to Use Domain Indexes

A domain index (also called an application domain index) is a customized index specific to an application that was implemented using a data cartridge (for example, a search engine or geographic information system). For information about domain indexes, see Oracle Database Concepts.


See Also:

Oracle Database Data Cartridge Developer's Guide for conceptual background to help you decide when to build domain indexes

When to Use Function-Based Indexes

A function-based index computes the value of an expression that involves one or more columns and stores it in the index. The index expression can be an arithmetic expression or an expression that contains a SQL function, PL/SQL function, package function, or C callout. Function-based indexes also support linguistic sorts based on collation keys, efficient linguistic collation of SQL statements, and case-insensitive sorts.

A function-based index improves the performance of queries that use the index expression (especially if the expression is computationally intensive). However, the database must also evaluate the index expression to process statements that do not use it.

The optimizer can use function-based indexes only for cost-based optimization, while it can use indexes on columns for both cost-based and rule-based optimization.


Note:

  • A function-based index cannot contain the value NULL. Therefore, either ensure that no column involved in the index expression can contain NULL or use the NVL function in the index expression to substitute another value for NULL. (For information about NVL, see Oracle Database SQL Language Reference.)

  • Oracle Database treats descending indexes as if they were function-based indexes (for more information, see Oracle Database SQL Language Reference).


Topics:


See Also:


Advantages of Function-Based Indexes

A function-based index has these advantages:

  • A function-based index increases the number of situations where the database can perform an index range scan (described in Oracle Database Concepts) instead of a full index scan (described in Oracle Database Concepts).

    An index range scan typically has a fast response time when the WHERE clause selects fewer than 15% of the rows of a large table. The optimizer can more accurately estimate how many rows an expression selects if the expression is materialized in a function-based index.

    Oracle Database represents the index expression as a virtual column, on which the ANALYZE statement (described in Oracle Database SQL Language Reference) can build a histogram.

  • A function-based index precomputes and stores the value of an expression.

    Queries can get the value of the expression from the index instead of computing it. The more queries that need the value, and the more computationally intensive the index expression, the more the index improves application performance. (See Example 4-1.)

  • You can create a function-based index on an object column or REF column.

    The index expression can be the invocation of a method that returns an object type. For more information, see Oracle Database Object-Relational Developer's Guide. (See Example 4-2 and the example in Oracle Database SQL Language Reference.)

  • A function-based index lets you perform more powerful sorts.

    The index expression can invoke the SQL functions UPPER and LOWER for case-insensitive sorts (as in Example 4-3) and the SQL function NLSSORT for linguistic-based sorts (as in Example 4-4).

Disadvantages of Function-Based Indexes

A function-based index has these disadvantages:

  • The optimizer can use a function-based index only for cost-based optimization, not for rule-based optimization.

    The cost-based optimizer uses statistics stored in the dictionary. To gather statistics for a function-based index, invoke either DBMS_STATS.GATHER_TABLE_STATS (described in Oracle Database PL/SQL Packages and Types Reference) or DBMS_STATS.GATHER_SCHEMA_STATS (described in Oracle Database PL/SQL Packages and Types Reference).

  • The database does not use a function-based index until you analyze the index itself and the table on which it is defined.

    To analyze the index and the table on which the index is defined, invoke either DBMS_STATS.GATHER_TABLE_STATS or DBMS_STATS.GATHER_SCHEMA_STATS.

  • The database does not use function-based indexes when doing OR expansion.

  • You must ensure that any schema-level or package-level PL/SQL function that the index expression invokes is deterministic (that is, that the function always return the same result for the same input).

    You must declare the function as DETERMINISTIC, but because Oracle Database does not check this assertion, you must ensure that the function really is deterministic.

    If you change the semantics of a DETERMINISTIC function and recompile it, then you must manually rebuild any dependent function-based indexes and materialized views. Otherwise, they report results for the prior version of the function.

  • If the index expression is a function invocation, then the function return type cannot be constrained.

    Because you cannot constrain the function return type with NOT NULL, you must ensure that the query that uses the index cannot fetch NULL values. Otherwise, the database performs a full table scan.

  • The index expression cannot invoke an aggregate function (described in Oracle Database SQL Language Reference).

  • A bitmapped function-based index cannot be a descending index (see Oracle Database SQL Language Reference).

  • The data type of the index expression cannot be VARCHAR2, RAW, LONGRAW, or a PL/SQL data type of unknown length.

    That is, you cannot index an expression of unknown length. However, you can index a known-length substring of that expression. For example:

    CREATE OR REPLACE FUNCTION initials (
      name  IN VARCHAR2
    ) RETURN VARCHAR2
      DETERMINISTIC
    IS
    BEGIN
      RETURN('A. J.');
    END;
    /
    
    /* Invoke SUBSTR both when creating index and when referencing
       function in queries. */
     
    CREATE INDEX func_substr_index ON
    EMPLOYEES(SUBSTR(initials(FIRST_NAME),1,10));
     
    SELECT SUBSTR(initials(FIRST_NAME),1,10) FROM EMPLOYEES;
    

See Also:


Examples of Function-Based Indexes

Examples:


Note:

Example 4-1 and Example 4-2 use composite indexes (indexes on multiple table columns), described in Oracle Database Concepts.

Example 4-1 creates a table with columns a, b, and c; creates an index on the table, and then queries the table. The index is a composite index on three columns: a virtual column that represents the expression a+b*(c-1), column a, and column b. The query uses the indexed expression in its WHERE clause; therefore, it can use an index range scan instead of a full index scan.

Example 4-1 Function-Based Index for Precomputing Arithmetic Expression

Create table on which to create index:

DROP TABLE Fbi_tab;
CREATE TABLE Fbi_tab (
  a INTEGER, 
  b INTEGER, 
  c INTEGER
);

Create index:

CREATE INDEX Idx ON Fbi_tab (a+b*(c-1), a, b);

This query can use an index range scan instead of a full index scan:

SELECT a FROM Fbi_tab WHERE a+b*(c-1) < 100;

See Also:

Oracle Database Concepts for information about fast full index scans

In Example 4-2, assume that the object type Reg_obj has been defined, and that it stores information about a city. The example creates a table whose first column has type Reg_obj, a deterministic function with a parameter of type Reg_obj, and two function-based indexes that invoke the function. The first query uses the first index to quickly find cities further than 1000 miles from the equator. The second query uses the second index (which is composite) to quickly find cities where the temperature delta is less than 20 and the maximum temperature is greater than 75. (The table is not populated for the example, so the queries return no rows.)

Example 4-2 Function-Based Indexes on Object Column

Create table with object column:

DROP TABLE Weatherdata_tab;
CREATE TABLE Weatherdata_tab (
  Reg_obj INTEGER,
  Maxtemp INTEGER,
  Mintemp INTEGER
);
 

Create deterministic function with parameter of type Reg_obj:

CREATE OR REPLACE FUNCTION Distance_from_equator (
  Reg_obj IN INTEGER
) RETURN INTEGER
  DETERMINISTIC
IS
BEGIN
  RETURN(3000);
END;
/
 

Create first function-based index:

CREATE INDEX Distance_index
ON Weatherdata_tab (Distance_from_equator (Reg_obj));
 

Use index expression in query:

SELECT * FROM Weatherdata_tab
WHERE (Distance_from_equator (Reg_Obj)) > '1000';
 

Result:

no rows selected
 

Create second function-based (and composite) index:

CREATE INDEX Compare_index
ON Weatherdata_tab ((Maxtemp - Mintemp) DESC, Maxtemp);
 

Use index expression and indexed column in query:

SELECT * FROM Weatherdata_tab
WHERE ((Maxtemp - Mintemp) < '20' AND Maxtemp > '75');
 

Result:

no rows selected

Example 4-3 creates an index that allows faster case-insensitive searches in the EMPLOYEES table and then uses the index expression in a query.

Example 4-3 Function-Based Index for Faster Case-Insensitive Searches

Create index:

CREATE INDEX emp_lastname ON EMPLOYEES (UPPER(LAST_NAME));

Use index expression in query:

SELECT first_name, last_name
FROM EMPLOYEES
WHERE UPPER(LAST_NAME) LIKE 'J%S_N';

Result:

FIRST_NAME           LAST_NAME
-------------------- -------------------------
Charles              Johnson
 
1 row selected.

Example 4-4 creates a table with one column, NAME, and a function-based index to sort that column using the collation sequence GERMAN, and then selects all columns of the table, ordering them by NAME. Because the query can use the index, the query is faster. (Assume that the query is run in a German session, where NLS_SORT is set to GERMAN and NLS_COMP is set to ANSI. Otherwise, the query would have to specify the values of these Globalization Support parameters.)

Example 4-4 Function-Based Index for Language-Dependent Sorting

Create table on which to create index:

DROP TABLE nls_tab;
CREATE TABLE nls_tab (NAME VARCHAR2(80));

Create index:

CREATE INDEX nls_index
  ON nls_tab (NLSSORT(NAME, 'NLS_SORT = GERMAN'));

Select all table columns, ordered by NAME:

SELECT * FROM nls_tab
WHERE NAME IS NOT NULL
ORDER BY NAME;
PKU/IdDdPK|%AOEBPS/adfns_web.htm Developing PL/SQL Web Applications

9 Developing PL/SQL Web Applications

This chapter explains how to develop PL/SQL web applications, which let you make your database available on the intranet.

Topics:

Overview of PL/SQL Web Applications

Typically, a web application written in PL/SQL is a set of stored subprograms that interact with web browsers through HTTP. A set of interlinked, dynamically generated HTML pages forms the user interface of a web application.

The program flow of a PL/SQL web application is similar to that in a CGI PERL script. Developers often use CGI scripts to produce web pages dynamically, but such scripts are often not optimal for accessing the database. Delivering web content with PL/SQL stored subprograms provides the power and flexibility of database processing. For example, you can use data manipulation language (DML) statements, dynamic SQL statements, and cursors. You also eliminate the process overhead of forking a new CGI process to handle each HTTP request.

Figure 9-1 illustrates the generic process for a PL/SQL web application.

Figure 9-1 PL/SQL Web Application

PL/SQL Web Application
Description of "Figure 9-1 PL/SQL Web Application"

Implementing PL/SQL Web Applications

You can implement a web browser-based application entirely in PL/SQL with these Oracle Database components:

PL/SQL Gateway

The PL/SQL gateway enables a web browser to invoke a PL/SQL stored subprogram through an HTTP listener. The gateway is a platform on which PL/SQL users develop and deploy PL/SQL web applications.

mod_plsql

mod_plsql is one implementation of the PL/SQL gateway. The module is a plug-in of Oracle HTTP Server and enables web browsers to invoke PL/SQL stored subprograms. Oracle HTTP Server is a component of both Oracle Application Server and the database.

The mod_plsql plug-in enables you to use PL/SQL stored subprograms to process HTTP requests and generate responses. In this context, an HTTP request is a URL that includes parameter values to be passed to a stored subprogram. PL/SQL gateway translates the URL, invokes the stored subprogram with the parameters, and returns output (typically HTML) to the client.

Some advantages of using mod_plsql over the embedded form of the PL/SQL gateway are:

  • You can run it in a firewall environment in which the Oracle HTTP Server runs on a firewall-facing host while the database is hosted behind a firewall. You cannot use this configuration with the embedded gateway.

  • The embedded gateway does not support mod_plsql features such as dynamic HTML caching, system monitoring, and logging in the Common Log Format.

Embedded PL/SQL Gateway

You can use an embedded version of the PL/SQL gateway that runs in the XML DB HTTP Listener in the database. It provides the core features of mod_plsql in the database but does not require the Oracle HTTP Server. You configure the embedded PL/SQL gateway with the DBMS_EPG package in the PL/SQL Web Toolkit.

Some advantages of using the embedded gateway over mod_plsql are as follows:

  • You can invoke PL/SQL web applications such as Application Express without installing Oracle HTTP Server, thereby simplifying installation, configuration, and administration of PL/SQL based web applications.

  • You use the same configuration approach that is used to deliver content from Oracle XML DB in response to FTP and HTTP requests.

PL/SQL Web Toolkit

This set of PL/SQL packages is a generic interface that enables you to use stored subprograms invoked by mod_plsql at run time.

In response to a browser request, a PL/SQL subprogram updates or retrieves data from Oracle Database according to the user input. It then generates an HTTP response to the browser, typically in the form of a file download or HTML to be displayed. The PL/SQL Web Toolkit API enables stored subprograms to perform actions such as:

  • Obtain information about an HTTP request

  • Generate HTTP headers such as content-type and mime-type

  • Set browser cookies

  • Generate HTML pages

Table 9-1 describes commonly used PL/SQL Web Toolkit packages.

Table 9-1 Commonly Used Packages in the PL/SQL Web Toolkit

PackageDescription of Contents

HTF

Function versions of the subprograms in the htp package. The function versions do not directly generate output in a web page. Instead, they pass their output as return values to the statements that invoke them. Use these functions when you must nest function calls.

HTP

Subprograms that generate HTML tags. For example, the procedure htp.anchor generates the HTML anchor tag, <A>.

OWA_CACHE

Subprograms that enable the PL/SQL gateway cache feature to improve performance of your PL/SQL web application.

You can use this package to enable expires-based and validation-based caching with the PL/SQL gateway file system.

OWA_COOKIE

Subprograms that send and retrieve HTTP cookies to and from a client web browser. Cookies are strings a browser uses to maintain state between HTTP calls. State can be maintained throughout a client session or longer if a cookie expiration date is included.

OWA_CUSTOM

The authorize function used by cookies.

OWA_IMAGE

Subprograms that obtain the coordinates where a user clicked an image. Use this package when you have an image map whose destination links invoke a PL/SQL gateway.

OWA_OPT_LOCK

Subprograms that impose database optimistic locking strategies to prevent lost updates. Lost updates can otherwise occur if a user selects, and then attempts to update, a row whose values were changed in the meantime by another user.

OWA_PATTERN

Subprograms that perform string matching and string manipulation with regular expressions.

OWA_SEC

Subprograms used by the PL/SQL gateway for authenticating requests.

OWA_TEXT

Subprograms used by package OWA_PATTERN for manipulating strings. You can also use them directly.

OWA_UTIL

These types of utility subprograms:

  • Dynamic SQL utilities to produce pages with dynamically generated SQL code.

  • HTML utilities to retrieve the values of CGI environment variables and perform URL redirects.

  • Date utilities for correct date-handling. Date values are simple strings in HTML, but must be properly treated as an Oracle Database data type.

WPG_DOCLOAD

Subprograms that download documents from a document repository that you define using the DAD configuration.



See Also:

Oracle Database PL/SQL Packages and Types Reference for syntax, descriptions, and examples for the PL/SQL Web Toolkit packages

Using mod_plsql Gateway to Map Client Requests to a PL/SQL Web Application

As explained in detail in the Oracle HTTP Server mod_plsql User's Guide, mod_plsql maps web client requests to PL/SQL stored subprograms over HTTP. See this documentation for instructions.


See Also:


Using Embedded PL/SQL Gateway

The embedded gateway functions very similar to the mod_plsql gateway. Before using the embedded version of the gateway, familiarize yourself with the Oracle HTTP Server mod_plsql User's Guide. Much of the information is the same or similar.

Topics:

How Embedded PL/SQL Gateway Processes Client Requests

Figure 9-2 illustrates the process by which the embedded gateway handles client HTTP requests.

Figure 9-2 Processing Client Requests with Embedded PL/SQL Gateway

Processing Client Requests with Embedded PL/SQL Gateway
Description of "Figure 9-2 Processing Client Requests with Embedded PL/SQL Gateway"

The explanation of the steps in Figure 9-2 is as follows:

  1. The Oracle XML DB HTTP Listener receives a request from a client browser to request to invoke a PL/SQL subprogram. The subprogram can either be written directly in PL/SQL or indirectly generated when a PL/SQL Server Page is uploaded to the database and compiled.

  2. The XML DB HTTP Listener routes the request to the embedded PL/SQL gateway as specified in its virtual-path mapping configuration.

  3. The embedded gateway uses the HTTP request information and the gateway configuration to determine which database account to use for authentication.

  4. The embedded gateway prepares the call parameters and invokes the PL/SQL subprogram in the application.

  5. The PL/SQL subprogram generates an HTML page out of relational data and the PL/SQL Web Toolkit accessed from the database.

  6. The application sends the page to the embedded gateway.

  7. The embedded gateway sends the page to the XML DB HTTP Listener.

  8. The XML DB HTTP Listener sends the page to the client browser.

Unlike mod_plsql, the embedded gateway processes HTTP requests with the Oracle XML DB Listener. This listener is the same server-side process as the Oracle Net Listener and supports Oracle Net Services, HTTP, and FTP.

Configure general HTTP listener settings through the XML DB interface (for instructions, see Oracle XML DB Developer's Guide). Configure the HTTP listener either by using Oracle Enterprise Manager or by editing the xdbconfig.xml file. Use the DBMS_EPG package for all embedded PL/SQL gateway configuration, for example, creating or setting attributes for a DAD.

Installing Embedded PL/SQL Gateway

The embedded gateway requires these components:

  • XML DB HTTP Listener

  • PL/SQL Web Toolkit

The embedded PL/SQL gateway is installed as part of Oracle XML DB. If you are using a preconfigured database created during an installation or by the Database Configuration Assistant (DBCA), then Oracle XML DB is installed and configured. For information about manually adding Oracle XML DB to an existing database, see Oracle XML DB Developer's Guide.

The PL/SQL Web Toolkit is part of the standard installation of the database, so no supplementary installation is necessary.

Configuring Embedded PL/SQL Gateway

You configure mod_plsql by editing the Oracle HTTP Server configuration files. Because the embedded gateway is installed as part of the Oracle XML DB HTTP Listener, you manage the embedded gateway as a servlet through the Oracle XML DB servlet management interface.

The configuration interface to the embedded gateway is the PL/SQL package DBMS_EPG. This package modifies the underlying xdbconfig.xml configuration file that XML DB uses. The default values of the embedded gateway configuration parameters are sufficient for most users.

Topics:

Configuring Embedded PL/SQL Gateway: Overview

As in mod_plsql, each request for a PL/SQL stored subprogram is associated with a Database Access Descriptor (DAD). A DAD is a set of configuration values used for database access. A DAD specifies information such as:

  • The database account to use for authentication

  • The subprogram to use for uploading and downloading documents

In the embedded PL/SQL gateway, a DAD is represented as a servlet in the XML DB HTTP Listener configuration. Each DAD attribute maps to an XML element in the configuration file xdbconfig.xml. The value of the DAD attribute corresponds to the element content. For example, the database-username DAD attribute corresponds to the <database-username> XML element; if the value of the DAD attribute is HR it corresponds to <database-username>HR<database-username>. DAD attribute names are case-sensitive.

Use the DBMS_EPG package to perform these embedded PL/SQL gateway configurations:

  1. Create a DAD with the DBMS_EPG.CREATE_DAD procedure.

  2. Set DAD attributes with the DBMS_EPG.SET_DAD_ATTRIBUTE procedure.

    All DAD attributes are optional. If you do not specify an attribute, it has its initial value.

Table 9-2 lists the embedded PL/SQL gateway attributes and the corresponding mod_plsql DAD parameters. Enumeration values in the "Legal Values" column are case-sensitive.

Table 9-2 Mapping Between mod_plsql and Embedded PL/SQL Gateway DAD Attributes

mod_plsql DAD AttributeEmbedded PL/SQL Gateway DAD AttributeMultiple OccurrencesLegal Values

PlsqlAfterProcedure

after-procedure

No

String

PlsqlAlwaysDescribeProcedure

always-describe-procedure

No

Enumeration of On, Off

PlsqlAuthenticationMode

authentication-mode

No

Enumeration of Basic, SingleSignOn, GlobalOwa, CustomOwa, PerPackageOwa

PlsqlBeforeProcedure

before-procedure

No

String

PlsqlBindBucketLengths

bind-bucket-lengths

Yes

Unsigned integer

PlsqlBindBucketWidths

bind-bucket-widths

Yes

Unsigned integer

PlsqlCGIEnvironmentList

cgi-environment-list

Yes

String

PlsqlCompatibilityMode

compatibility-mode

No

Unsigned integer

PlsqlDatabaseUsername

database-username

No

String

PlsqlDefaultPage

default-page

No

String

PlsqlDocumentPath

document-path

No

String

PlsqlDocumentProcedure

document-procedure

No

String

PlsqlDocumentTablename

document-table-name

No

String

PlsqlErrorStyle

error-style

No

Enumeration of ApacheStyle, ModplsqlStyle, DebugStyle

PlsqlExclusionList

exclusion-list

Yes

String

PlsqlFetchBufferSize

fetch-buffer-size

No

Unsigned integer

PlsqlInfoLogging

info-logging

No

Enumeration of InfoDebug

PlsqlInputFilterEnable

input-filter-enable

No

String

PlsqlMaxRequestsPerSession

max-requests-per-session

No

Unsigned integer

PlsqlNLSLanguage

nls-language

No

String

PlsqlOWADebugEnable

owa-debug-enable

No

Enumeration of On, Off

PlsqlPathAlias

path-alias

No

String

PlsqlPathAliasProcedure

path-alias-procedure

No

String

PlsqlRequestValidationFunction

request-validation-function

No

String

PlsqlSessionCookieName

session-cookie-name

No

String

PlsqlSessionStateManagement

session-state-management

No

Enumeration of StatelessWithResetPackageState, StatelessWithFastRestPackageState, StatelessWithPreservePackageState

PlsqlTransferMode

transfer-mode

No

Enumeration of Char, Raw

PlsqlUploadAsLongRaw

upload-as-long-raw

No

String


The default values of the DAD attributes are sufficient for most users of the embedded gateway. mod_plsql users do not need these attributes:

  • PlsqlDatabasePassword

  • PlsqlDatabaseConnectString (because the embedded gateway does not support logon to external databases)

Like the DAD attributes, the global configuration parameters are optional. Table 9-3 describes the DBMS_EPG global attributes and the corresponding mod_plsql global parameters.

Table 9-3 Mapping Between mod_plsql and Embedded PL/SQL Gateway Global Attributes

mod_plsql DAD AttributeEmbedded PL/SQL Gateway DAD AttributeMultiple OccurrencesLegal Values

PlsqlLogLevel

log-level

No

Unsigned integer

PlsqlMaxParameters

max-parameters

No

Unsigned integer



See Also:


Configuring User Authentication for Embedded PL/SQL Gateway

Because it uses the XML DB authentication schemes, the embedded gateway handles database authentication differently from mod_plsql. In particular, it does not store database passwords in a DAD.


Note:

To serve a PL/SQL web application on the Internet but maintain the database behind a firewall, do not use the embedded PL/SQL gateway to run the application; use mod_plsql.

Use the DBMS_EPG package to configure database authentication.

Topics:

Configuring Static Authentication with DBMS_EPG

Static authentication is for the mod_plsql user who stores database user names and passwords in the DAD so that the browser user is not required to enter database authentication information.

To configure static authentication, follow these steps:

  1. Log on to the database as an XML DB administrator (that is, a user with the XDBADMIN role assigned).

  2. Create the DAD. For example, this procedure creates a DAD invoked HR_DAD and maps the virtual path to /hrweb/:

    EXEC DBMS_EPG.CREATE_DAD('HR_DAD', '/hrweb/*');
    
  3. For this step, you need the ALTER ANY USER system privilege. Set the DAD attribute database-username to the database account whose privileges must be used by the DAD. For example, this procedure specifies that the DAD named HR_DAD has the privileges of the HR account:

    EXEC DBMS_EPG.SET_DAD_ATTRIBUTE('HR_DAD', 'database-username', 'HR');
    

    The DAD attribute database-username is case-sensitive.

  4. Assign the DAD the privileges of the database user specified in the previous step. This authorization enables end users to invoke procedures and access document tables through the embedded PL/SQL gateway with the privileges of the authorized account. For example:

    EXEC DBMS_EPG.AUTHORIZE_DAD('HR_DAD', 'HR');
    

    Alternatively, you can log off as the user with XDBADMIN privileges, log on as the database user whose privileges must be used by the DAD, and then use this command to assign these privileges to the DAD:

    EXEC DBMS_EPG.AUTHORIZE_DAD('HR_DAD');
    

    Note:

    Multiple users can authorize the same DAD. The database-username attribute setting of the DAD determines which user's privileges to use.

Unlike mod_plsql, the embedded gateway connects to the database as the special user ANONYMOUS, but accesses database objects with the user privileges assigned to the DAD. The database rejects access if the browser user attempts to connect explicitly with the HTTP Authorization header.


Note:

The account ANONYMOUS is locked after XML DB installation. To use static authentication with the embedded PL/SQL gateway, first unlock this account.

Configuring Dynamic Authentication with DBMS_EPG

Dynamic authentication is for the mod_plsql user who does not store database user names and passwords in the DAD.

In dynamic authentication, a database user does not have to authorize the embedded gateway to use its privileges to access database objects. Instead, browser users must supply the database authentication information through the HTTP Basic Authentication scheme.

The action of the embedded gateway depends on whether the database-username attribute is set for the DAD. If the attribute is not set, then the embedded gateway connects to the database as the user supplied by the browser client. If the attribute is set, then the database restricts access to the user specified in the database-username attribute.

To set up dynamic authentication, follow these steps:

  1. Log on to the database as a an XML DB administrator (that is, a user with the XDBADMIN role).

  2. Create the DAD. For example, this procedure creates a DAD invoked DYNAMIC_DAD and maps the virtual path to /hrweb/:

    EXEC DBMS_EPG.CREATE_DAD('DYNAMIC_DAD', '/hrweb/*');
    
  3. Optionally, set the DAD attribute database-username to the database account whose privileges must be used by the DAD. The browser prompts the user to enter the username and password for this account when accessing the DAD. For example, this procedure specifies that the DAD named DYNAMIC_DAD has the privileges of the HR account:

    EXEC DBMS_EPG.SET_DAD_ATTRIBUTE('DYNAMIC_DAD', 'database-username', 'HR');
    

    The attribute database-username is case-sensitive.


WARNING:

Passwords sent through the HTTP Basic Authentication scheme are not encrypted. Configure the embedded gateway to use the HTTPS protocol to protect the passwords sent by the browser clients.


Configuring Anonymous Authentication with DBMS_EPG

Anonymous authentication is for the mod_plsql user who creates a special DAD database user for database logon, but stores the application procedures and document tables in a different schema and grants access to the procedures and document tables to PUBLIC.

To set up anonymous authentication, follow these steps:

  1. Log on to the database as an XML DB administrator, that is, a user with the XDBADMIN role assigned.

  2. Create the DAD. For example, this procedure creates a DAD invoked HR_DAD and maps the virtual path to /hrweb/:

    EXEC DBMS_EPG.CREATE_DAD('HR_DAD', '/hrweb/*');
    
  3. Set the DAD attribute database-username to ANONYMOUS. For example:

    EXEC DBMS_EPG.SET_DAD_ATTRIBUTE('HR_DAD', 'database-username', 'ANONYMOUS');
    

    Both database-username and ANONYMOUS are case-sensitive.

    You need not authorize the embedded gateway to use ANONYMOUS privileges to access database objects, because ANONYMOUS has no system privileges and owns no database objects.

Determining the Authentication Mode of a DAD

If you know the name of a DAD, then the authentication mode for this DAD depends on these factors:

  • Does the DAD exist?

  • Is the database-username attribute for the DAD set?

  • Is the DAD authorized to use the privilege of the database-username user?

  • Is the database-username attribute the one that the user authorized to use the DAD?

Table 9-4 shows how the answers to the preceding questions determine the authentication mode.

Table 9-4 Authentication Possibilities for a DAD

DAD Exists?database-username set?User authorized?Mode

Yes

Yes

Yes

Static

Yes

Yes

No

Dynamic restricted

Yes

No

Does not matter

Dynamic

Yes

Yes (to ANONYMOUS)

Does not matter

Anonymous

No



N/A


For example, assume that you create a DAD named MY_DAD. If the database-username attribute for MY_DAD is set to HR, but the HR user does not authorize MY_DAD, then the authentication mode for MY_DAD is dynamic and restricted. A browser user who attempts to run a PL/SQL subprogram through MY_DAD is prompted to enter the HR database username and password.

The DBA_EPG_DAD_AUTHORIZATION view shows which users have authorized use of a DAD. The DAD_NAME column displays the name of the DAD; the USERNAME column displays the user whose privileges are assigned to the DAD. The DAD authorized might not exist.


See Also:

Oracle Database Reference for more information about the DBA_EPG_DAD_AUTHORIZATION view

Creating and Configuring DADs: Examples

Example 9-1 does this:

  • Creates a DAD with static authentication for database user HR and assigns it the privileges of the HR account, which then authorizes it.

  • Creates a DAD with dynamic authentication that is not restricted to any user.

  • Creates a DAD with dynamic authentication that is restricted to the HR account.

Example 9-1 Creating and Configuring DADs

------------------------------------------------------------------------
--- DAD with static authentication
------------------------------------------------------------------------

CONNECT SYSTEM AS SYSDBA
PASSWORD: password
EXEC DBMS_EPG.CREATE_DAD('Static_Auth_DAD', '/static/*');
EXEC DBMS_EPG.SET_DAD_ATTRIBUTE('Static_Auth_DAD', 'database-username', 'HR');
GRANT EXECUTE ON DBMS_EPG TO HR;

-- Authorization
CONNECT HR
PASSWORD: password
EXEC DBMS_EPG.AUTHORIZE_DAD('Static_Auth_DAD');

------------------------------------------------------------------------
-- DAD with dynamic authentication
------------------------------------------------------------------------

CONNECT SYSTEM AS SYSDBA
PASSWORD: password
EXEC DBMS_EPG.CREATE_DAD('Dynamic_Auth_DAD', '/dynamic/*');

-------------------------------------------------------------------------
-- DAD with dynamic authentication restricted
-------------------------------------------------------------------------

EXEC DBMS_EPG.CREATE_DAD('Dynamic_Auth_DAD_Restricted', '/dynamic/*');
EXEC DBMS_EPG.SET_DAD_ATTRIBUTE
  ('Dynamic_Auth_DAD_Restricted', 'database-username', 'HR');

The creation and authorization of a DAD are independent; therefore you can:

  • Authorize a DAD that does not exist (it can be created later)

  • Authorize a DAD for which you are not the user (however, the authorization does not take effect until the DAD database-user attribute is changed to your username)

Example 9-2 creates a DAD with static authentication for database user HR and assigns it the privileges of the HR account. Then:

  • Instead of authorizing that DAD, the database user HR authorizes a nonexistent DAD.

    Although the user might have done this by mistake, no error occurs, because the nonexistent DAD might be created later.

  • The database user OE authorizes the DAD (whose database-user attribute is set to HR.

    No error occurs, but the authorization does not take effect until the DAD database-user attribute is changed to OE.

Example 9-2 Authorizing DADs to be Created or Changed Later

REM Create DAD with static authentication for database user HR

CONNECT SYSTEM AS SYSDBA
PASSWORD: password
EXEC DBMS_EPG.CREATE_DAD('Static_Auth_DAD', '/static/*');
EXEC DBMS_EPG.SET_DAD_ATTRIBUTE('Static_Auth_DAD', 'database-username', 'HR');
GRANT EXECUTE ON DBMS_EPG TO HR;

REM Database user HR authorizes DAD that does not exist

CONNECT HR
PASSWORD: password
EXEC DBMS_EPG.AUTHORIZE_DAD('Static_Auth_DAD_Typo');

REM Database user OE authorizes DAD with database-username 'HR'

CONNECT OE
PASSWORD: password
EXEC DBMS_EPG.AUTHORIZE_DAD('Static_Auth_DAD');
Determining the Authentication Mode for a DAD: Example

Example 9-3 creates a PL/SQL procedure, show_dad_auth_status, which accepts the name of a DAD and reports its authentication mode. If the specified DAD does not exist, the procedure exits with an error.

Example 9-3 Determining the Authentication Mode for a DAD

CREATE OR REPLACE PROCEDURE show_dad_auth_status (p_dadname VARCHAR2) IS
  v_daduser VARCHAR2(32);
  v_cnt     PLS_INTEGER;
BEGIN
  -- Determine DAD user
  v_daduser := DBMS_EPG.GET_DAD_ATTRIBUTE(p_dadname, 'database-username');

  -- Determine whether DAD authorization exists for DAD user
  SELECT COUNT(*)
    INTO v_cnt
      FROM DBA_EPG_DAD_AUTHORIZATION da
        WHERE da.DAD_NAME = p_dadname
          AND da.USERNAME = v_daduser;

  -- If DAD authorization exists for DAD user, authentication mode is static
  IF (v_cnt > 0) THEN
    DBMS_OUTPUT.PUT_LINE (
      '''' || p_dadname ||
      ''' is set up for static authentication for user ''' ||
      v_daduser || '''.');
    RETURN;
  END IF;

  -- If no DAD authorization exists for DAD user, authentication mode is dynamic

  -- Determine whether dynamic authentication is restricted to particular user
  IF (v_daduser IS NOT NULL) THEN
    DBMS_OUTPUT.PUT_LINE (
      '''' || p_dadname ||
      ''' is set up for dynamic authentication for user ''' ||
      v_daduser || ''' only.');
  ELSE
    DBMS_OUTPUT.PUT_LINE (
      '''' || p_dadname ||
      ''' is set up for dynamic authentication for any user.');
  END IF;
END;
/

Assume that you have run the script in Example 9-1 to create and configure various DADs. The output is:

SET SERVEROUTPUT ON;
BEGIN
  show_dad_auth_status('Static_Auth_DAD');
END;
/
'Static_Auth_DAD' is set up for static authentication for user 'HR'.
Determining the Authentication Mode for All DADs: Example

The anonymous block in Example 9-4 reports the authentication modes of all registered DADs. It invokes the show_dad_auth_status procedure from Example 9-3.

Example 9-4 Showing the Authentication Mode for All DADs

DECLARE
  v_dad_names DBMS_EPG.VARCHAR2_TABLE;
BEGIN
  DBMS_OUTPUT.PUT_LINE
    ('---------- Authorization Status for All DADs ----------');
  DBMS_EPG.GET_DAD_LIST(v_dad_names);

  FOR i IN 1..v_dad_names.count LOOP
    show_dad_auth_status(v_dad_names(i));
  END LOOP;
END;
/

If you have run the script in Example 9-1 to create and configure various DADs, the output of Example 9-4 is:

---------- Authorization Status for All DADs ----------
'Static_Auth_DAD' is set up for static auth for user 'HR'.
'Dynamic_Auth_DAD' is set up for dynamic auth for any user.
'Dynamic_Auth_DAD_Restricted' is set up for dynamic auth for user 'HR' only.
Showing DAD Authorizations that Are Not in Effect: Example

The anonymous block in Example 9-5 reports DAD authorizations that are not in effect. A DAD authorization is not in effect in either of these situations:

  • The user who authorizes the DAD is not the user specified by the database-username attribute of the DAD

  • The user authorizes a DAD that does not exist

Example 9-5 Showing DAD Authorizations that Are Not in Effect

DECLARE
  v_dad_names DBMS_EPG.VARCHAR2_TABLE;
  v_dad_user  VARCHAR2(32);
  v_dad_found BOOLEAN;
BEGIN 
  DBMS_OUTPUT.PUT_LINE
    ('---------- DAD Authorizations Not in Effect ----------');
  DBMS_EPG.GET_DAD_LIST(v_dad_names);

  FOR r IN (SELECT * FROM DBA_EPG_DAD_AUTHORIZATION) LOOP  -- Outer loop
    v_dad_found := FALSE;
    FOR i IN 1..v_dad_names.count LOOP  -- Inner loop
      IF (r.DAD_NAME = v_dad_names(i)) THEN
        v_dad_user :=
          DBMS_EPG.GET_DAD_ATTRIBUTE(r.DAD_NAME, 'database-username');

        -- Is database-username the user for whom DAD is authorized?
        IF (r.USERNAME <> v_dad_user) THEN
          DBMS_OUTPUT.PUT_LINE (
            'DAD authorization of ''' || r.dad_name ||
            ''' by user ''' || r.username || '''' ||
            ' is not in effect because DAD user is ' ||
            '''' || v_dad_user || '''.');
        END IF;
        v_dad_found := TRUE;
        EXIT;  -- Inner loop
      END IF;
    END LOOP;  -- Inner loop

    -- Does DAD exist?
    IF (NOT v_dad_found) THEN
      DBMS_OUTPUT.PUT_LINE (
       'DAD authorization of ''' || r.dad_name ||
       ''' by user ''' || r.username ||
       ''' is not in effect because the DAD does not exist.');
    END IF;
  END LOOP;  -- Outer loop
END;
/

If you have run the script in Example 9-2 to create and configure various DADs, the output of Example 9-5 (reformatted to fit on the page) is:

---------- DAD Authorizations Not in Effect ----------
DAD authorization of 'Static_Auth_DAD' by user 'OE' is not in effect
  because DAD user is 'HR'.
DAD authorization of 'Static_Auth_DAD_Typo' by user 'HR' is not in effect
  because DAD does not exist.
Examining Embedded PL/SQL Gateway Configuration

When you are connected to the database as a user with system privileges, this script helps you examine the configuration of the embedded PL/SQL gateway:

$ORACLE_HOME/rdbms/admin/epgstat.sql

Example 9-6 shows the output of the epgstat.sql script for Example 9-1 when the ANONYMOUS account is locked.

Example 9-6 epgstat.sql Script Output for Example 9-1

Command to run script:

@$ORACLE_HOME/rdbms/admin/epgstat.sql

Result:

+--------------------------------------+
| XDB protocol ports:                  |
|  XDB is listening for the protocol   |
|  when the protocol port is nonzero.  |
+--------------------------------------+

HTTP Port FTP Port
--------- --------
        0        0

1 row selected.

+---------------------------+
| DAD virtual-path mappings |
+---------------------------+

Virtual Path                     DAD Name
-------------------------------- --------------------------------
/dynamic/*                       Dynamic_Auth_DAD_Restricted
/static/*                        Static_Auth_DAD

2 rows selected.

+----------------+
| DAD attributes |
+----------------+

DAD Name        DAD Param             DAD Value
------------    --------------------- ----------------------------------------
Dynamic_Auth    database-username     HR
_DAD_Restric
ted

Static_Auth_    database-username     HR
DAD


2 rows selected.

+---------------------------------------------------+
| DAD authorization:                                |
|  To use static authentication of a user in a DAD, |
|  the DAD must be authorized for the user.         |
+---------------------------------------------------+

DAD Name                         User Name
-------------------------------- --------------------------------
Static_Auth_DAD                  HR
                                 OE
Static_Auth_DAD_Typo             HR

3 rows selected.

+----------------------------+
| DAD authentication schemes |
+----------------------------+

DAD Name             User Name                        Auth Scheme
-------------------- -------------------------------- ------------------
Dynamic_Auth_DAD                                      Dynamic
Dynamic_Auth_DAD_Res HR                               Dynamic Restricted
tricted

Static_Auth_DAD      HR                               Static

3 rows selected.

+--------------------------------------------------------+
| ANONYMOUS user status:                                 |
|  To use static or anonymous authentication in any DAD, |
|  the ANONYMOUS account must be unlocked.               |
+--------------------------------------------------------+

Database User   Status
--------------- --------------------
ANONYMOUS       EXPIRED & LOCKED

1 row selected.

+-------------------------------------------------------------------+
| ANONYMOUS access to XDB repository:                               |
|  To allow public access to XDB repository without authentication, |
|  ANONYMOUS access to the repository must be allowed.              |
+-------------------------------------------------------------------+

Allow repository anonymous access?
----------------------------------
false

1 row selected.

Invoking PL/SQL Stored Subprograms Through Embedded PL/SQL Gateway

The basic steps for invoking PL/SQL subprograms through the embedded PL/SQL gateway are the same as for the mod_plsql gateway. See Oracle HTTP Server mod_plsql User's Guide for instructions. You must adapt the mod_plsql instructions slightly for use with the embedded gateway. For example, invoke the embedded gateway in a browser by entering the URL in this format:

protocol://hostname[:port]/virt-path/[[!][schema.][package.]proc_name[?query_str]]

The placeholder virt-path stands for the virtual path that you configured in DBMS_EPG.CREATE_DAD. The mod_plsql documentation uses DAD_location instead of virt-path.

These topics documented in Oracle HTTP Server mod_plsql User's Guide apply equally to the embedded gateway:

  • Transaction mode

  • Supported data types

  • Parameter-passing scheme

  • File upload and download support

  • Path-aliasing

  • Common Gateway Interface (CGI) environment variables

Securing Application Access with Embedded PL/SQL Gateway

The embedded gateway shares the same protection mechanism with mod_plsql. See Oracle HTTP Server mod_plsql User's Guide for instructions.

Restrictions in Embedded PL/SQL Gateway

The mod_plsql restrictions documented in the first chapter of Oracle HTTP Server mod_plsql User's Guide apply equally to the embedded gateway. In addition, the embedded version of the gateway does not support these features:

Using Embedded PL/SQL Gateway: Scenario

This section illustrates how to write a simple application that queries the hr.employees table and delivers HTML output to a web browser through the PL/SQL gateway. It assumes that you have both XML DB and the sample schemas installed.

To write and run the program follow these steps:

  1. Log on to the database as a user with ALTER USER privileges and ensure that the database account ANONYMOUS is unlocked. The ANONYMOUS account, which is locked by default, is required for static authentication. If the account is locked, then use this SQL statement to unlock it:

    ALTER USER anonymous ACCOUNT UNLOCK;
    
  2. Log on to the database as an XML DB administrator, that is, a user with the XDBADMIN role.

    To determine which users and roles were granted the XDADMIN role, query the data dictionary:

    SELECT *
    FROM DBA_ROLE_PRIVS
    WHERE GRANTED_ROLE = 'XDBADMIN';
    
  3. Create the DAD. For example, this procedure creates a DAD invoked HR_DAD and maps the virtual path to /plsql/:

    EXEC DBMS_EPG.CREATE_DAD('HR_DAD', '/plsql/*');
    
  4. Set the DAD attribute database-username to the database user whose privileges must be used by the DAD. For example, this procedure specifies that the DAD HR_DAD accesses database objects with the privileges of user HR:

    EXEC DBMS_EPG.SET_DAD_ATTRIBUTE('HR_DAD', 'database-username', 'HR');
    

    The attribute database-username is case-sensitive.

  5. Grant EXECUTE privilege to the database user whose privileges must be used by the DAD (so that he or she can authorize the DAD). For example:

    GRANT EXECUTE ON DBMS_EPG TO HR;
    
  6. Log off as the XML DB administrator and log on to the database as the database user whose privileges must be used by the DAD (for example, HR).

  7. Authorize the embedded PL/SQL gateway to invoke procedures and access document tables through the DAD. For example:

    EXEC DBMS_EPG.AUTHORIZE_DAD('HR_DAD');
    
  8. Create a sample PL/SQL stored procedure invoked print_employees. This program creates an HTML page that includes the result set of a query of hr.employees:

    CREATE OR REPLACE PROCEDURE print_employees IS
      CURSOR emp_cursor IS
        SELECT last_name, first_name
          FROM hr.employees
            ORDER BY last_name;
    BEGIN
      HTP.PRINT('<html>');
      HTP.PRINT('<head>');
      HTP.PRINT('<meta http-equiv="Content-Type" content="text/html">');
      HTP.PRINT('<title>List of Employees</title>');
      HTP.PRINT('</head>'); 
      HTP.PRINT('<body TEXT="#000000" BGCOLOR="#FFFFFF">');
      HTP.PRINT('<h1>List of Employees</h1>');
      HTP.PRINT('<table width="40%" border="1">');
      HTP.PRINT('<tr>');
      HTP.PRINT('<th align="left">Last Name</th>');
      HTP.PRINT('<th align="left">First Name</th>');
      HTP.PRINT('</tr>');
      FOR emp_record IN emp_cursor LOOP
        HTP.PRINT('<tr>');
        HTP.PRINT('<td>' || emp_record.last_name  || '</td>');
        HTP.PRINT('<td>' || emp_record.first_name || '</td>');
      END LOOP;
      HTP.PRINT('</table>');
      HTP.PRINT('</body>');
      HTP.PRINT('</html>');
    END;
    /
    
  9. Ensure that the Oracle Net listener can accept HTTP requests. You can determine the status of the listener on Linux and UNIX by running this command at the system prompt:

    lsnrctl status | grep HTTP
    

    Output (reformatted from a single line to multiple lines from page size constraints):

    (DESCRIPTION=
      (ADDRESS=(PROTOCOL=tcp)(HOST=example.com)(PORT=8080))
      (Presentation=HTTP)
      (Session=RAW)
    )
    

    If you do not see the HTTP service started, then you can add these lines to your initialization parameter file (replacing listener_name with the name of your Oracle Net local listener), then restart the database and the listener:

    dispatchers="(PROTOCOL=TCP)"
    local_listener=listener_name
    
  10. Run the print_employees program from your web browser. For example, you can use this URL, replacing host with the name of your host computer and port with the value of the PORT parameter in the previous step:

    http://host:port/plsql/print_employees
    

    For example, if your host is test.com and your HTTP port is 8080, then enter:

    http://example.com:8080/plsql/print_employees
    

    The web browser returns an HTML page with a table that includes the first and last name of every employee in the hr.employees table.

Generating HTML Output with PL/SQL

Traditionally, PL/SQL web applications use function calls to generate each HTML tag for output. These functions are part of the PL/SQL Web Toolkit packages that come with Oracle Database. Example 9-7 shows how to generate a simple HTML page by calling the HTP functions that correspond to each HTML tag.

Example 9-7 Using HTP Functions to Generate HTML Tags

CREATE OR REPLACE PROCEDURE html_page IS
BEGIN
  HTP.HTMLOPEN;                            -- generates <HTML>
  HTP.HEADOPEN;                            -- generates <HEAD>
  HTP.TITLE('Title');                      -- generates <TITLE>Hello</TITLE>
  HTP.HEADCLOSE;                           -- generates </HTML> 

  -- generates <BODY TEXT="#000000" BGCOLOR="#FFFFFF">
  HTP.BODYOPEN( cattributes => 'TEXT="#000000" BGCOLOR="#FFFFFF"');

  -- generates <H1>Heading in the HTML File</H1>
  HTP.HEADER(1, 'Heading in the HTML File');

  HTP.PARA;                                 -- generates <P>        
  HTP.PRINT('Some text in the HTML file.'); 
  HTP.BODYCLOSE;                            -- generates </BODY>
  HTP.HTMLCLOSE;                            -- generates </HTML>
END;
/

An alternative to making function calls that correspond to each tag is to use the HTP.PRINT function to print both text and tags. Example 9-8 illustrates this technique.

Example 9-8 Using HTP.PRINT to Generate HTML Tags

CREATE OR REPLACE PROCEDURE html_page2 IS
BEGIN
  HTP.PRINT('<html>');
  HTP.PRINT('<head>');
  HTP.PRINT('<meta http-equiv="Content-Type" content="text/html">');
  HTP.PRINT('<title>Title of the HTML File</title>');
  HTP.PRINT('</head>');
  HTP.PRINT('<body TEXT="#000000" BGCOLOR="#FFFFFF">');
  HTP.PRINT('<h1>Heading in the HTML File</h1>');
  HTP.PRINT('<p>Some text in the HTML file.');
  HTP.PRINT('</body>');
  HTP.PRINT('</html>');
END;
/

Chapter 10, "Developing PL/SQL Server Pages (PSP)," describes an additional method for delivering using PL/SQL to generate HTML content. PL/SQL server pages enables you to build on your knowledge of HTML tags and avoid learning a new set of function calls. In an application written as a set of PL/SQL server pages, you can still use functions from the PL/SQL Web Toolkit to:

  • Simplify the processing involved in displaying tables

  • Store persistent data (cookies)

  • Work with CGI protocol internals

Passing Parameters to PL/SQL Web Applications

To be useful in a wide variety of situations, a web application must be interactive enough to allow user choices. To keep the attention of impatient web surfers, streamline the interaction so that users can specify these choices very simply, without excessive decision-making or data entry.

The main methods of passing parameters to PL/SQL web applications are:

  • Using HTML form tags. The user fills in a form on one web page, and all the data and choices are transmitted to a stored subprogram when the user clicks the Submit button on the page.

  • Hard-coded in the URL. The user clicks on a link, and a set of predefined parameters are transmitted to a stored subprogram. Typically, you include separate links on your web page for all the choices that the user might want.

Topics:

Passing List and Dropdown-List Parameters from an HTML Form

List boxes and drop-down lists are implemented with the HTML tag <SELECT>.

Use a list box for a large number of choices or to allow multiple selections. List boxes are good for showing items in alphabetical order so that users can find an item quickly without reading all the choices.

Use a drop-down list in these situations:

  • There are a small number of choices

  • Screen space is limited.

  • Choices are in an unusual order.

The drop-down captures the attention of first-time users and makes them read the items. If you keep the choices and order consistent, then users can memorize the motion of selecting an item from the drop-down list, allowing them to make selections quickly as they gain experience. Example 9-9 shows a simple drop-down list.

Example 9-9 HTML Drop-Down List

<form>
<select name="seasons">
<option value="winter">Winter
<option value="spring">Spring
<option value="summer">Summer
<option value="fall">Fall
</select>

Passing Option and Check Box Parameters from an HTML Form

Options pass either a null value (if none of the options in a group is checked), or the value specified on the option that is checked.

To specify a default value for a set of options, you can include the CHECKED attribute in anINPUT tag, or include a DEFAULT clause on the parameter within the stored subprogram. When setting up a group of options, be sure to include a choice that indicates "no preference", because after selecting a option, the user can select a different one, but cannot clear the selection completely. For example, include a "Don't Care" or "Don't Know" selection along with "Yes" and "No" choices, in case someone makes a selection and then realizes it was wrong.

Check boxes need special handling, because your stored subprogram might receive a null value, a single value, or multiple values:

All the check boxes with the same NAME attribute comprise a check box group. If none of the check boxes in a group is checked, the stored subprogram receives a null value for the corresponding parameter.

If one check box in a group is checked, the stored subprogram receives a single VARCHAR2 parameter.

If multiple check boxes in a group are checked, the stored subprogram receives a parameter with the PL/SQL type TABLE OF VARCHAR2. You must declare a type like TABLE OF VARCHAR2, or use a predefined one like OWA_UTIL.IDENT_ARR. To retrieve the values, use a loop:

CREATE OR REPLACE PROCEDURE handle_checkboxes (
  checkboxes owa_util.ident_arr
) AS
BEGIN
  FOR i IN 1..checkboxes.count
  LOOP
    htp.print('<p>Check Box value: ' || checkboxes(i));
  END LOOP;
END;
/

Passing Entry-Field Parameters from an HTML Form

Entry fields require the most validation, because a user might enter data in the wrong format, out of range, and so on. If possible, validate the data on the client side using a client-side JavaScript function, and format it correctly for the user or prompt them to enter it again.

For example:

  • You might prevent the user from entering alphabetic characters in a numeric entry field, or from entering characters after reaching a length limit.

  • You might silently remove spaces and dashes from a credit card number if the stored subprogram expects the value in that format.

  • You might inform the user immediately when they type a number that is too large, so that they can retype it.

Because you cannot always rely on such validation to succeed, code the stored subprograms to deal with these cases anyway. Rather than forcing the user to use the Back button when they enter wrong data, display a single page with an error message and the original form with all the other values filled in.

For sensitive information such as passwords, a special form of the entry field, <INPUT TYPE=PASSWORD>, hides the text as it is typed in.

The procedure in Example 9-10 accepts two strings as input. The first time the procedure is invoked, the user sees a simple form prompting for the input values. When the user submits the information, the same procedure is invoked again to check if the input is correct. If the input is OK, the procedure processes it. If not, the procedure prompts for input, filling in the original values for the user.

Example 9-10 Passing Entry-Field Parameters from an HTML Form

DROP TABLE name_zip_table;
CREATE TABLE name_zip_table (
  name     VARCHAR2(100),
  zipcode  NUMBER
);

-- Store a name and associated zip code in the database.
CREATE OR REPLACE PROCEDURE associate_name_with_zipcode
  (name VARCHAR2 := NULL,
   zip  VARCHAR2 := NULL)
AS
BEGIN
  -- Each entry field must contain a value. Zip code must be 6 characters.
  -- (In a real program you perform more extensive checking.)

  IF name IS NOT NULL AND zip IS NOT NULL AND length(zip) = 6 THEN
    INSERT INTO name_zip_table (name, zipcode) VALUES (name, zip);
    HTP.PRINT('<p>The person ' || name || ' has the zip code ' || zip || '.');

    -- If input was OK, stop here. User does not see form again.
    RETURN;
  END IF;

  -- If user entered incomplete or incorrect data, show error message.

  IF (name IS NULL AND zip IS NOT NULL)
    OR (name IS NOT NULL AND zip IS NULL)
      OR (zip IS NOT NULL AND length(zip) != 6)
  THEN
    HTP.PRINT('<p><b>Please reenter data. Fill all fields,
               and use 6-digit zip code.</b>');
  END IF;

  -- If user entered no data or incorrect data, show error message
  -- & make form invoke same procedure to check input values.

  HTP.FORMOPEN('HR.associate_name_with_zipcode', 'GET');
  HTP.PRINT('<p>Enter your name:</td>');
  HTP.PRINT
   ('<td valign=center><input type=text name=name value="' || name || '">');
  HTP.PRINT('<p>Enter your zip code:</td>');
  HTP.PRINT
    ('<td valign=center><input type=text name=zip value="' || zip || '">');
  HTP.FORMSUBMIT(NULL, 'Submit');
  HTP.FORMCLOSE;
END;
/

Passing Hidden Parameters from an HTML Form

One technique for passing information through a sequence of stored subprograms, without requiring the user to specify the same choices each time, is to include hidden parameters in the form that invokes a stored subprogram. The first stored subprogram places information, such as a user name, into the HTML form that it generates. The value of the hidden parameter is passed to the next stored subprogram, as if the user had entered it through a option or entry field.

Other techniques for passing information from one stored subprogram to another include:

  • Sending a "cookie" containing the persistent information to the browser. The browser then sends this same information back to the server when accessing other web pages from the same site. Cookies are set and retrieved through the HTTP headers that are transferred between the browser and the web server before the HTML text of each web page.

  • Storing the information in the database itself, where later stored subprograms can retrieve it. This technique involves some extra overhead on the database server, and you must still find a way to keep track of each user as multiple users access the server at the same time.

Uploading a File from an HTML Form

You can use an HTML form to choose a file on a client system, and transfer it to the server. A stored subprogram can insert the file into the database as a CLOB, BLOB, or other type that can hold large amounts of data.

The PL/SQL Web Toolkit and the PL/SQL gateway have the notion of a "document table" that holds uploaded files.


See Also:

mod_plsql User's Guide

Submitting a Completed HTML Form

By default, an HTML form must have a Submit button, which transmits the data from the form to a stored subprogram or CGI program. You can label this button with text of your choice, such as "Search", "Register", and so on.

You can have multiple forms on the same page, each with its own form elements and Submit button. You can even have forms consisting entirely of hidden parameters, where the user makes no choice other than clicking the button.

Using JavaScript or other scripting languages, you can eliminate the Submit button and have the form submitted in response to some other action, such as selecting from a drop-down list. This technique is best when the user only makes a single selection, and the confirmation step of the Submit button is not essential.

Handling Missing Input from an HTML Form

When an HTML form is submitted, your stored subprogram receives null parameters for any form elements that are not filled in. For example, null parameters can result from an empty entry field, a set of check boxes, options, or list items with none checked, or a VALUE parameter of "" (empty quotation marks).

Regardless of any validation you do on the client side, always code stored subprograms to handle the possibility that some parameters are null:

  • Specify an initial value in all parameter declarations, to prevent an exception when the stored subprogram is invoked with a missing form parameter. You can set the initial value to zero for numeric values (when that makes sense), and to NULL when you want to check whether the user actually specifies a value.

  • Before using an input parameter value that has the initial value NULL, check if it is null.

  • Make the subprogram generate sensible results even when not all input parameters are specified. You might leave some sections out of a report, or display a text string or image in a report to indicate where parameters were not specified.

  • Provide a way to fill in the missing values and run the stored subprogram again, directly from the results page. For example, include a link that invokes the same stored subprogram with an additional parameter, or display the original form with its values filled in as part of the output.

Maintaining State Information Between Web Pages

Web applications are particularly concerned with the idea of state, the set of data that is current at a particular moment in time. It is easy to lose state information when switching from one web page to another, which might result in asking the user to make the same choices over and over.

You can pass state information between dynamic web pages using HTML forms. The information is passed as a set of name-value pairs, which are turned into stored subprogram parameters for you.

If the user has to make multiple selections, or one selection from many choices, or it is important to avoid an accidental selection, use an HTML form. After the user makes and reviews all the choices, they confirm the choices with the Submit button. Subsequent pages can use forms with hidden parameters (<INPUT TYPE=HIDDEN> tags) to pass these choices from one page to the next.

If the user is only considering one or two choices, or the decision points are scattered throughout the web page, you can save the user from hunting around for the Submit button by representing actions as hyperlinks and including any necessary name-value pairs in the query string (the part after the ? within a URL).

An alternative way to main state information is to use Oracle Application Server and its mod_ose module. This approach lets you store state information in package variables that remain available as a user moves around a web site.


See Also:

The Oracle Application Server documentation set at:
http://www.oracle.com/technetwork/indexes/documentation/index.html

Performing Network Operations in PL/SQL Subprograms

Oracle provides packages that allow PL/SQL subprograms to perform these network operations:

Internet Protocol version 6 (IPv6) Support

As of Oracle Database 11g Release 2, PL/SQL network utility packages support IPv6 addresses. The package interfaces have not changed: Any interface parameter that expects a network host accepts an IPv6 address in string form, and any interface that returns an IP address can return an IPv6 address.

However, applications that use network addresses might need small changes, and recompilation, to accommodate IPv6 addresses. An IPv6 address has 128 bits, while an IPv4 address has only 32 bits. In a URL, an IPv6 address must be enclosed in brackets. For example:

http://[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]/

See Also:


Sending Email from PL/SQL

Using the UTL_SMTP package, a PL/SQL subprogram can send email, as in Example 9-11.

Example 9-11 Sending Email from PL/SQL

CREATE OR REPLACE PROCEDURE send_test_message
IS
  mailhost   VARCHAR2(64) := 'mailhost.example.com';
  sender     VARCHAR2(64) := 'me@example.com';
  recipient  VARCHAR2(64) := 'you@example.com';
  mail_conn  UTL_SMTP.CONNECTION;
BEGIN
  mail_conn := UTL_SMTP.OPEN_CONNECTION(mailhost, 25); -- 25 is the port
  UTL_SMTP.HELO(mail_conn, mailhost);
  UTL_SMTP.MAIL(mail_conn, sender);
  UTL_SMTP.RCPT(mail_conn, recipient);
 
  UTL_SMTP.OPEN_DATA(mail_conn);
  UTL_SMTP.WRITE_DATA(mail_conn, 'This is a test message.' || chr(13));
  UTL_SMTP.WRITE_DATA(mail_conn, 'This is line 2.' || chr(13));
  UTL_SMTP.CLOSE_DATA(mail_conn);
 
  /* If message were in single string, open_data(), write_data(),
     and close_data() could be in a single call to data(). */
 
  UTL_SMTP.QUIT(mail_conn);
EXCEPTION
  WHEN OTHERS THEN
   -- Insert error-handling code here
   RAISE;
END;
/

See Also:

Oracle Database PL/SQL Packages and Types Reference for detailed information about the UTL_SMTP package

Getting a Host Name or Address from PL/SQL

Using the UTL_INADDR package, a PL/SQL subprogram can determine the host name of the local system or the IP address of a given host name.


See Also:

Oracle Database PL/SQL Packages and Types Reference for detailed information about the UTL_INADDR package

Using TCP/IP Connections from PL/SQL

Using the UTL_TCP package, a PL/SQL subprogram can open TCP/IP connections to systems on the network, and read or write to the corresponding sockets.


See Also:

Oracle Database PL/SQL Packages and Types Reference for detailed information about the UTL_TCP package

Retrieving HTTP URL Contents from PL/SQL

Using the UTL_HTTP package, a PL/SQL subprogram can:

  • Retrieve the contents of an HTTP URL

    The contents are usually in the form of HTML-tagged text, but might be any kind of file that can be downloaded from a web server (for example, plain text or a JPEG image).

  • Control HTTP session details (such as headers, cookies, redirects, proxy servers, IDs and passwords for protected sites, and CGI parameters)

  • Speed up multiple accesses to the same web site, using HTTP 1.1 persistent connections

A PL/SQL subprogram can construct and interpret URLs for use with the UTL_HTTP package by using the functions UTL_URL.ESCAPE and UTL_URL.UNESCAPE.

The PL/SQL procedure in Example 9-12 uses the UTL_HTTP package to retrieve the contents of an HTTP URL.

Example 9-12 Retrieving HTTP URL Contents from PL/SQL

CREATE OR REPLACE PROCEDURE show_url
  (url      IN VARCHAR2,
  username IN VARCHAR2 := NULL,
  password IN VARCHAR2 := NULL)
AS
  req        UTL_HTTP.REQ;
  resp       UTL_HTTP.RESP;
  name_      VARCHAR2(256);
  value_     VARCHAR2(1024);
  data_      VARCHAR2(255);
  my_scheme  VARCHAR2(256);
  my_realm   VARCHAR2(256);
  my_proxy   BOOLEAN;
BEGIN
  -- When going through a firewall, pass requests through this host.
  -- Specify sites inside the firewall that do not need the proxy host.
 
  UTL_HTTP.SET_PROXY('proxy.example.com', 'corp.example.com');
 
  -- Ask UTL_HTTP not to raise an exception for 4xx and 5xx status codes,
  -- rather than just returning the text of the error page.
 
  UTL_HTTP.SET_RESPONSE_ERROR_CHECK(FALSE);
 
  -- Begin retrieving this web page.
 req := UTL_HTTP.BEGIN_REQUEST(url);
 
  -- Identify yourself.
  --  Some sites serve special pages for particular browsers.
  UTL_HTTP.SET_HEADER(req, 'User-Agent', 'Mozilla/4.0');
 
  -- Specify user ID and password for pages that require them.
  IF (username IS NOT NULL) THEN
    UTL_HTTP.SET_AUTHENTICATION(req, username, password);
  END IF;
 
  -- Start receiving the HTML text.
  resp := UTL_HTTP.GET_RESPONSE(req);
 
  -- Show status codes and reason phrase of response.
  DBMS_OUTPUT.PUT_LINE('HTTP response status code: ' || resp.status_code);
  DBMS_OUTPUT.PUT_LINE
    ('HTTP response reason phrase: ' || resp.reason_phrase);
 
  -- Look for client-side error and report it.
  IF (resp.status_code >= 400) AND (resp.status_code <= 499) THEN
 
  -- Detect whether page is password protected
  -- and you didn't supply the right authorization.
 
  IF (resp.status_code = UTL_HTTP.HTTP_UNAUTHORIZED) THEN
  UTL_HTTP.GET_AUTHENTICATION(resp, my_scheme, my_realm, my_proxy);
  IF (my_proxy) THEN
    DBMS_OUTPUT.PUT_LINE('Web proxy server is protected.');
    DBMS_OUTPUT.PUT('Please supply the required ' || my_scheme ||
      ' authentication username for realm ' || my_realm ||
      ' for the proxy server.');
  ELSE
    DBMS_OUTPUT.PUT_LINE('Web page ' || url || ' is protected.');
    DBMS_OUTPUT.PUT('Please supplied the required ' || my_scheme ||
      ' authentication username for realm ' || my_realm ||
      ' for the web page.');
  END IF;
  ELSE
    DBMS_OUTPUT.PUT_LINE('Check the URL.');
  END IF;
 
  UTL_HTTP.END_RESPONSE(resp);
    RETURN;
 
  -- Look for server-side error and report it.
  ELSIF (resp.status_code >= 500) AND (resp.status_code <= 599) THEN
    DBMS_OUTPUT.PUT_LINE('Check if the web site is up.');
    UTL_HTTP.END_RESPONSE(resp);
    RETURN;
  END IF;
 
  -- HTTP header lines contain information about cookies, character sets,
  -- and other data that client and server can use to customize each
  -- session.
 
  FOR i IN 1..UTL_HTTP.GET_HEADER_COUNT(resp) LOOP
    UTL_HTTP.GET_HEADER(resp, i, name_, value_);
    DBMS_OUTPUT.PUT_LINE(name_ || ': ' || value_);
  END LOOP;
 
  -- Read lines until none are left and an exception is raised.
  LOOP
    UTL_HTTP.READ_LINE(resp, value_);
    DBMS_OUTPUT.PUT_LINE(value_);
  END LOOP;
EXCEPTION
  WHEN UTL_HTTP.END_OF_BODY THEN
  UTL_HTTP.END_RESPONSE(resp);
END;
/

This block shows examples of calls to the procedure in Example 9-12, but the URLs are for nonexistent pages. Substitute URLs from your own web server.

BEGIN
  show_url('http://www.oracle.com/no-such-page.html');
  show_url('http://www.oracle.com/protected-page.html');
  show_url
    ('http://www.oracle.com/protected-page.html','username','password');
END;
/

See Also:


Using Tables, Image Maps, Cookies, and CGI Variables from PL/SQL

Using packages supplied by Oracle, and the mod_plsql plug-in of Oracle HTTP Server (OHS), a PL/SQL subprogram can format the results of a query in an HTML table, produce an image map, set and get HTTP cookies, check the values of CGI variables, and perform other typical web operations.

Documentation for these packages is not part of the database documentation library. The location of the documentation depends on your application server. To get started with these packages, look at their subprogram names and parameters using the SQL*Plus DESCRIBE statement:

DESCRIBE HTP;
DESCRIBE HTF;
DESCRIBE OWA_UTIL;
PK3ͲPK|%AOEBPS/adfns_sqlproc.htm SQL Processing for Application Developers

1 SQL Processing for Application Developers

This chapter explains what application developers must know about how Oracle Database processes SQL statements. Before reading this chapter, read the basic information about SQL processing in Oracle Database Concepts.

Topics:

Description of SQL Statement Processing

This topic explains what happens during each stage of processing the execution of a SQL statement, using a DML statement as an example.

Assume that you are using a Pro*C program to increase the salary for all employees in a department. The program has connected to Oracle Database and you are connected to the HR schema, which owns the employees table. You can embed this SQL statement in your program:

EXECUTE SQL UPDATE employees SET salary = 1.10 * salary
  WHERE department_id = :department_id;

The program provides a value for the bind variable placeholder :department_id, which the SQL statement uses when it runs.

Stages of SQL Statement Processing


Note:

DML statements use all stages. Transaction management, session management, and system management SQL statements use only stages 2 and 8.

  1. Open or create a cursor.

    A program interface call opens or creates a cursor, in expectation of a SQL statement. Most applications create the cursor implicitly (automatically). Precompiler programs can create the cursor either implicitly or explicitly.

  2. Parse the statement.

    The user process passes the SQL statement to Oracle Database, which loads a parsed representation of the statement into the shared SQL area. Oracle Database can catch many errors during parsing.


    Note:

    For a DDL statement, parsing includes data dictionary lookup and execution.


    See Also:


  3. Determine if the statement is a query.

  4. If the statement is a query, describe its results.


    Note:

    This stage is necessary only if the characteristics of the result are unknown; for example, when a user enters the query interactively.

    Oracle Database determines the characteristics (data types, lengths, and names) of the result.

  5. If the statement is a query, define its output.

    You specify the location, size, and data type of variables defined to receive each fetched value. These variables are called define variables. Oracle Database performs data type conversion if necessary.


    See Also:

    Oracle Database Concepts for information about the DEFINE stage

  6. Bind any variables.

    Oracle Database has determined the meaning of the SQL statement but does not have enough information to run it. Oracle Database needs values for any bind variable placeholders in the statement. In the example, Oracle Database needs a value for :department_id. The process of obtaining these values is called binding variables.

    A program must specify the location (memory address) of the value. End users of applications may be unaware that they are specifying values for bind variable placeholders, because the Oracle Database utility can prompt them for the values.

    Because the program specifies the location of the value (that is, binds by reference), it need not rebind the variable before rerunning the statement, even if the value changes. Each time Oracle Database runs the statement, it gets the value of the variable from its address.

    You must also specify a data type and length for each value (unless they are implied or defaulted) if Oracle Database must perform data type conversion.


    See Also:

    For more information about specifying a data type and length for a value:

  7. (Optional) Parallelize the statement.

    Oracle Database can parallelize queries and some data definition language (DDL) operations (for example, index creation, creating a table with a subquery, and operations on partitions). Parallelization causes multiple server processes to perform the work of the SQL statement so that it can complete faster.

  8. Run the statement.

    Oracle Database runs the statement. If the statement is a query or an INSERT statement, the database locks no rows, because no data is changing. If the statement is an UPDATE or DELETE statement, the database locks all rows that the statement affects, until the next COMMIT, ROLLBACK, or SAVEPOINT for the transaction, thereby ensuring data integrity.

    For some statements, you can specify multiple executions to be performed. This is called array processing. Given n number of executions, the bind and define locations are assumed to be the beginning of an array of size n.

  9. If the statement is a query, fetch its rows.

    Oracle Database selects rows and, if the query has an ORDER BY clause, orders the rows. Each successive fetch retrieves another row of the result set, until the last row has been fetched.

  10. Close the cursor.

    Oracle Database closes the cursor.


Note:

To rerun a transaction management, session management, or system management SQL statement, use another EXECUTE statement.

Shared SQL Areas

Oracle Database automatically detects when applications send similar SQL statements to the database. The SQL area used to process the first occurrence of the statement is shared—that is, used for processing subsequent occurrences of that same statement. Therefore, only one shared SQL area exists for a unique statement. Because shared SQL areas are shared memory areas, any Oracle Database process can use a shared SQL area. The sharing of SQL areas reduces memory use on the database server, thereby increasing system throughput.

In determining whether statements are similar or identical, Oracle Database compares both SQL statements issued directly by users and applications and recursive SQL statements issued internally by DDL statements.


See Also:

Oracle Database Performance Tuning Guide for more information about shared SQL

Grouping Operations into Transactions

Topics:


See Also:

Oracle Database Concepts for basic information about transactions

Deciding How to Group Operations in Transactions

Typically, deciding how to group operations in transactions is the concern of application designers who use programming interfaces to Oracle Database. When deciding how to group transactions:

  • Define transactions such that work is accomplished in logical units and data remains consistent.

  • Ensure that data in all referenced tables is in a consistent state before the transaction begins and after it ends.

  • Ensure that each transaction consists only of the SQL statements or PL/SQL blocks that comprise one consistent change to the data.

For example, suppose that you write a web application that enables users to transfer funds between accounts. The transaction must include the debit to one account, executed by one SQL statement, and the credit to another account, executed by another SQL statement. Both statements must fail or succeed as a unit of work; one statement must not be committed without the other. Do not include unrelated actions, such as a deposit to one account, in the transaction.

Improving Transaction Performance

As an application developer, you must try to improve performance. Consider using these performance enhancement techniques when designing and writing your application:

  • For each transaction:

    1. If you can use a single SQL statement, then do so.

    2. If you cannot use a single SQL statement but you can use PL/SQL, then use as little PL/SQL as possible.

      For information about PL/SQL, see Part II, "PL/SQL for Application Developers".

    3. If you cannot use PL/SQL (because it cannot do what you must do; for example, send email), then use Java.

    4. If you cannot use Java (for example, if it is too slow) or you have existing third-generation language (3GL) code, then use an external C subprogram.

    For information about using Java and C in your application, see Chapter 14, "Developing Applications with Multiple Programming Languages."

  • Establish standards for writing SQL statements so that you can take advantage of shared SQL areas.

    Oracle Database recognizes identical SQL statements and enables them to share memory areas, reducing memory usage on the database server and increasing system throughput.

  • Collect statistics that Oracle Database can use to implement a cost-based approach to SQL statement optimization, and use additional hints to the optimizer as needed.

    To collect most statistics, use the DBMS_STATS package, which lets you collect statistics in parallel, collect global statistics for partitioned objects, and fine-tune your statistics collection in other ways. For more information about this package, see Oracle Database PL/SQL Packages and Types Reference.

    To collect statistics unrelated to the cost-based optimizer (such as information about free list blocks), use the SQL statement ANALYZE. For more information about this statement, see Oracle Database SQL Language Reference.

    For more information about hints, see Oracle Database SQL Language Reference.

  • Before beginning a transaction, invoke DBMS_APPLICATION_INFO procedures to record the name of the transaction in the database for later use when tracking its performance with Oracle Trace and the SQL trace facility. For information about the DBMS_APPLICATION_INFO package, see Oracle Database PL/SQL Packages and Types Reference.

  • Increase user productivity and query efficiency by including user-written PL/SQL functions in SQL expressions. For details, see "Invoking Stored PL/SQL Functions from SQL Statements".


See Also:

Oracle Database Concepts for more information about transaction management

Committing Transactions

To commit a transaction, use the COMMIT statement. These two statements are equivalent and commit the current transaction:

COMMIT WORK;
COMMIT;

The COMMIT statement lets you include the COMMENT parameter with a comment that provides information about the transaction being committed. A comment is useful for including information about the origin of the transaction when you commit distributed transactions:

COMMIT COMMENT 'Dallas/Accts_pay/Trans_type 10B';

See Also:

Oracle Database SQL Language Reference for information about the COMMIT statement

Managing Commit Redo Action

When a transaction updates Oracle Database, it generates a corresponding redo entry. Oracle Database buffers the redo entry to the redo log until the transaction completes. When the transaction commits, the log writer process (LGWR) writes redo records to disk for the buffered redo entries of all changes in the transaction. By default, Oracle Database writes the redo entries to disk before the call returns to the client. This action causes a latency in the commit, because the application must wait for the redo entries to be persistent on disk.

Oracle Database lets you change the handling of commit redo to fit the needs of your application. If your application requires very high transaction throughput and you are willing to trade commit durability for lower commit latency, then you can change the default COMMIT options so that the application need not wait for the database to write data to the online redo logs.

Table 1-1 describes the COMMIT options.


Caution:

With the NOWAIT option, a failure that occurs after the commit message is received, but before the redo log records are written, can falsely indicate to a transaction that its changes are persistent.

Table 1-1 COMMIT Statement Options

OptionEffect

WAIT(default)

Ensures that the COMMIT statement returns only after the corresponding redo information is persistent in the online redo log. When the client receives a successful return from this COMMIT statement, the transaction has been committed to durable media.

A failure that occurs after a successful write to the log might prevent the success message from returning to the client, in which case the client cannot tell whether the transaction committed.

NOWAIT(alternative to WAIT)

The COMMIT statement returns to the client regardless of whether the write to the redo log has completed. This behavior can increase transaction throughput.

BATCH(alternative to IMMEDIATE)

Buffers the redo information to the redo log with concurrently running transactions. After collecting sufficient redo information, initiates a disk write to the redo log. This behavior is called group commit, because it writes redo information for multiple transactions to the log in a single I/O operation.

IMMEDIATE(default)

LGWR writes the transaction redo information to the log. Because this operation option forces a disk I/O, it can reduce transaction throughput.


To change the COMMIT options, use either the COMMIT statement (described in Oracle Database SQL Language Reference) or the appropriate initialization parameter. For information about initialization parameters, see Oracle Database Reference.


Note:

You cannot change the default IMMEDIATE and WAIT action for distributed transactions.

If your application uses Oracle Call Interface (OCI), then you can modify redo action by setting these flags in the OCITransCommit function in your application:

  • OCI_TRANS_WRITEWAIT

  • OCI_TRANS_WRITENOWAIT

  • OCI_TRANS_WRITEBATCH

  • OCI_TRANS_WRITEIMMED


Caution:

OCI_TRANS_WRITENOWAIT can cause silent transaction loss with shutdown termination, startup force, and any instance or node failure. On an Oracle RAC system, asynchronously committed changes might not be immediately available to read on other instances.


See Also:

Oracle Call Interface Programmer's Guide for information about the OCITransCommit function

The specification of the NOWAIT and BATCH options has a small window of vulnerability in which Oracle Database can roll back a transaction that your application views as committed. Your application must be able to tolerate these scenarios:

  • The database host fails, which causes the database to lose redo entries that were buffered but not yet written to the online redo logs.

  • A file I/O problem prevents LGWR from writing buffered redo entries to disk. If the redo logs are not multiplexed, then the commit is lost.

Rolling Back Transactions

To roll back an entire transaction, or to roll back part of a transaction to a savepoint, use the ROLLBACK statement. For example, either of these statements rolls back the entire current transaction:

ROLLBACK WORK;
ROLLBACK;

The WORK option of the ROLLBACK statement has no function.

To roll back to a savepoint defined in the current transaction, use the TO option of the ROLLBACK statement. For example, either of these statements rolls back the current transaction to the savepoint named POINT1:

SAVEPOINT Point1;
...
ROLLBACK TO SAVEPOINT Point1;
ROLLBACK TO Point1;

Defining Transaction Savepoints

To define a savepoint in a transaction, use the SAVEPOINT statement. This statement creates the savepoint named ADD_EMP1 in the current transaction:

SAVEPOINT Add_emp1;

Creating a savepoint with the same identifier as an earlier savepoint deletes the earlier savepoint. After creating a savepoint, you can roll back to it.

An active savepoint is one that was created after the last COMMIT or ROLLBACK statement. The number of active savepoints for each session is unlimited.

Table 1-2 shows a series of SQL statements that illustrates the use of COMMIT, SAVEPOINT, and ROLLBACK statements within a transaction.

Table 1-2 Use of COMMIT, SAVEPOINT, and ROLLBACK

SQL StatementResults

SAVEPOINT a;

First savepoint of this transaction

DELETE...;

First DML statement of this transaction

SAVEPOINT b;

Second savepoint of this transaction

INSERT INTO...;

Second DML statement of this transaction

SAVEPOINT c;

Third savepoint of this transaction

UPDATE...;

Third DML statement of this transaction

ROLLBACK TO c;

UPDATE statement is rolled back, savepoint C remains defined.

ROLLBACK TO b;

INSERT statement is rolled back, savepoint C is lost, savepoint B remains defined.

ROLLBACK TO c;

ORA-01086

INSERT INTO...;

New DML statement in this transaction

COMMIT;

Commits all actions performed by the first DML statement (the DELETE statement) and the last DML statement (the second INSERT statement).

All other statements (the second and the third statements) of the transaction were rolled back before the COMMIT. The savepoint A is no longer active.


Ensuring Repeatable Reads with Read-Only Transactions

By default, Oracle Database guarantees statement-level read consistency, but not transaction-level read consistency. With statement-level read consistency, queries in a statement produce consistent data for the duration of the statement, not reflecting changes by other statements. With transaction-level read consistency (repeatable reads), queries in the transaction produce consistent data for the duration of the transaction, not reflecting changes by other transactions.

To ensure transaction-level read consistency for a transaction that does not include DML statements, specify that the transaction is read-only. The queries in a read-only transaction see only changes committed before the transaction began, so query results are consistent for the duration of the transaction.

A read-only transaction provides transaction-level read consistency without acquiring additional data locks. Therefore, while the read-only transaction is querying data, other transactions can query and update the same data.

A read-only transaction begins with this statement:

SET TRANSACTION READ ONLY [ NAME string ];

Only DDL statements can precede the SET TRANSACTION READ ONLY statement. After the SET TRANSACTION READ ONLY statement successfully runs, the transaction can include only SELECT (without FOR UPDATE), COMMIT, ROLLBACK, or non-DML statements (such as SET ROLE, ALTER SYSTEM, and LOCK TABLE). A COMMIT, ROLLBACK, or DDL statement ends the read-only transaction.


See Also:

Oracle Database SQL Language Reference for more information about the SET TRANSACTION statement

Long-running queries sometimes fail because undo information required for consistent read (CR) operations is no longer available. This situation occurs when active transactions overwrite committed undo blocks.

Automatic undo management lets your database administrator (DBA) explicitly control how long the database retains undo information, using the parameter UNDO_RETENTION. For example, if UNDO_RETENTION is set to 30 minutes, then the database retains all committed undo information for at least 30 minutes, ensuring that all queries running for 30 minutes or less do not encounter the OER error "snapshot too old."


See Also:

Oracle Database Administrator's Guide for information about long-running queries and resumable space allocation

Using Cursors

PL/SQL implicitly declares a cursor for all SQL data manipulation statements, including queries that return only one row. For queries that return multiple rows, you can explicitly declare a cursor to process the rows individually.

You can think of a cursor as a name for a specific private SQL area. A PL/SQL cursor variable lets you retrieve multiple rows from a stored subprogram. You can pass cursor variables as parameters in your 3GL application. For more information about cursor variables, see Oracle Database PL/SQL Language Reference.

Most Oracle Database users rely on the automatic cursor handling of the database utilities, but programmatic interfaces offer application designers more control over cursors. In application development, a cursor is a named resource available to a program, which can be specifically used for parsing SQL statements embedded within the application.

Topics:

How Many Cursors Can a Session Have?

The number of cursors that a session can have open simultaneously is determined by:

  • The amount of memory available to the session

  • The value of the initialization parameter OPEN_CURSORS, described in Oracle Database Reference

Explicitly creating cursors for precompiler programs has advantages in tuning those applications. For example, increasing the number of cursors can reduce the frequency of parsing and improve performance. If you know how many cursors might be required at a given time, you can open that many cursors simultaneously.

Using a Cursor to Reexecute a Statement

After each stage of execution, the cursor retains enough information about the SQL statement to reexecute the statement without starting over, if no other SQL statement was associated with that cursor. The statement can be reexecuted without including the parse stage.

By opening several cursors, the parsed representation of several SQL statements can be saved. Repeated execution of the same SQL statements can thus begin at the describe, define, bind, or run step, saving the repeated cost of opening cursors and parsing.

To understand the performance characteristics of a cursor, the DBA can use the V$SQL dynamic performance view to display the text of the associated query (see Oracle Database Reference). Because the results of EXPLAIN PLAN for the original query might differ from the way the database actually processes the query, the DBA can get more precise information by examining these dynamic performance views:

ViewDescription
V$SQL_PLANExecution plan information for each child cursor loaded in the library cache. For more information, see Oracle Database Reference.
V$SQL_PLAN_STATISTICSExecution statistics at the row source level for each child cursor. For more information, see Oracle Database Reference.
V$SQL_PLAN_STATISTICS_ALLMemory usage statistics for row sources that use SQL memory (sort or hash-join). This view concatenates information in V$SQL_PLAN with execution statistics from V$SQL_PLAN_STATISTICS and V$SQL_WORKAREA. For more information, see Oracle Database Reference.

Scrollable Cursors

Execution of a cursor puts the results of the query into a set of rows called the result set. A scrollable cursor is one whose result rows need not be fetched in forward sequential order. For a scrollable cursor, interfaces exist to fetch previously fetched rows, to fetch the nth row in the result set, and to fetch the nth row from the current position in the result set.


See Also:

Oracle Call Interface Programmer's Guide for more information about using scrollable cursors in OCI

Closing a Cursor

Closing a cursor makes its information inaccessible and deallocates the memory that it uses. After a cursor opens, it does not close until the user program terminates its connection to the server.

An OCI program or precompiler application that follows good programming practice explicitly closes open cursors during program execution. If the program terminates without closing an open cursor, then that cursor closes implicitly.

Canceling a Cursor

Canceling a cursor frees resources from the current fetch.The information in the associated private area is lost but the cursor remains open, parsed, and associated with its bind variables.


Note:

You cannot cancel cursors using Pro*C/C++ or PL/SQL.


See Also:

Oracle Call Interface Programmer's Guide for information about canceling a cursor with the OCIStmtFetch2 statement

Locking Tables Explicitly

Oracle Database has default locking mechanisms that ensure data concurrency, data integrity, and statement-level read consistency. However, you can override these mechanisms by locking tables explicitly. Locking tables explicitly is useful in situations such as these:

To override default locking at the transaction level, use any of these SQL statements:

Locks acquired by these statements are released after the transaction is committed or rolled back.


See Also:

Oracle Database SQL Language Reference for information about the ISOLATION_LEVEL parameter of the ALTER SESSION statement

The initialization parameter DML_LOCKS (described in Oracle Database Reference) determines the maximum number of DML locks. Although its default value is usually enough, you might need to increase it if you use explicit locks.


Caution:

If you override the default locking of Oracle Database at any level, ensure that data integrity is guaranteed, data concurrency is acceptable, and deadlocks are either impossible or appropriately handled.

Topics:

Privileges Required to Acquire Table Locks

No special privileges are required to acquire any type of table lock on a table in your own schema. To acquire a table lock on a table in another schema, you must have either the LOCK ANY TABLE system privilege or any object privilege (for example, SELECT or UPDATE) for the table.

Choosing a Locking Strategy

A transaction explicitly acquires the specified table locks when a LOCK TABLE statement is executed. A LOCK TABLE statement explicitly overrides default locking. When a LOCK TABLE statement is issued on a view, the underlying base tables are locked. This statement acquires exclusive table locks for the employees and departments tables on behalf of the containing transaction:

LOCK TABLE employees, departments
   IN EXCLUSIVE MODE NOWAIT;

You can specify several tables or views to lock in the same mode; however, only a single lock mode can be specified for each LOCK TABLE statement.


Note:

When a table is locked, all rows of the table are locked. No other user can modify the table. For information about locking individual rows, see "Explicitly Acquiring Row Locks".

In the LOCK TABLE statement, you can also indicate how long you want to wait for the table lock:

  • If you do not want to wait, specify either NOWAIT or WAIT 0.

    You acquire the table lock only if it is immediately available; otherwise, an error notifies you that the lock is not available now.

  • To wait up to n seconds to acquire the table lock, specify WAIT n, where n is greater than 0 and less than or equal to 100000.

    If the table lock is still unavailable after n seconds, an error notifies you that the lock is not available now.

  • To wait indefinitely to acquire the lock, specify neither NOWAIT nor WAIT.

    The database waits indefinitely until the table is available, locks it, and returns control to you. When the database is running DDL statements concurrently with DML statements, a timeout or deadlock can sometimes result. The database detects such timeouts and deadlocks and returns an error.

Topics:


See Also:

Oracle Database SQL Language Reference for LOCK TABLE statement syntax

When to Lock with ROW SHARE MODE and ROW EXCLUSIVE MODE

ROW SHARE MODE and ROW EXCLUSIVE MODE table locks offer the highest degree of concurrency. You might use these locks if:

  • Your transaction must prevent another transaction from acquiring an intervening share, share row, or exclusive table lock for a table before your transaction can update that table.

    If another transaction acquires an intervening share, share row, or exclusive table lock, no other transactions can update the table until the locking transaction commits or rolls back.

  • Your transaction must prevent a table from being altered or dropped before your transaction can modify that table.

When to Lock with SHARE MODE

SHARE MODE table locks are rather restrictive data locks. You might use these locks if:

  • Your transaction only queries the table, and requires a consistent set of the table data for the duration of the transaction.

  • You can hold up other transactions that try to update the locked table, until all transactions that hold SHARE MODE locks on the table either commit or roll back.

  • Other transactions might acquire concurrent SHARE MODE table locks on the same table, also giving them the option of transaction-level read consistency.


    Caution:

    Your transaction might not update the table later in the same transaction. However, if multiple transactions concurrently hold share table locks for the same table, no transaction can update the table (even if row locks are held as the result of a SELECT FOR UPDATE statement). Therefore, if concurrent share table locks on the same table are common, updates cannot proceed and deadlocks are common. In this case, use share row exclusive or exclusive table locks instead.

Scenario: Tables employees and budget_tab require a consistent set of data in a third table, departments. For a given department number, you want to update the information in employees and budget_tab, and ensure that no members are added to the department between these two transactions.

Solution: Lock the departments table in SHARE MODE, as shown in Example 1-1. Because the departments table is rarely updated, locking it probably does not cause many other transactions to wait long.

Example 1-1 LOCK TABLE with SHARE MODE

-- Create and populate table:
 
DROP TABLE budget_tab;
CREATE TABLE budget_tab (
  sal     NUMBER(8,2),
  deptno  NUMBER(4)
);
 
INSERT INTO budget_tab (sal, deptno)
  SELECT salary, department_id
  FROM employees;
 
-- Lock departments and update employees and budget_tab:
 
LOCK TABLE departments IN SHARE MODE;
 
UPDATE employees
  SET salary = salary * 1.1
  WHERE department_id IN
    (SELECT department_id FROM departments WHERE location_id = 1700);
 
UPDATE budget_tab
SET sal = sal * 1.1
WHERE deptno IN
  (SELECT department_id FROM departments WHERE location_id = 1700);
 
COMMIT;  -- COMMIT releases lock

When to Lock with SHARE ROW EXCLUSIVE MODE

You might use a SHARE ROW EXCLUSIVE MODE table lock if:

  • Your transaction requires both transaction-level read consistency for the specified table and the ability to update the locked table.

  • You do not care if other transactions acquire explicit row locks (using SELECT FOR UPDATE), which might make UPDATE and INSERT statements in the locking transaction wait and might cause deadlocks.

  • You only want a single transaction to have this action.

When to Lock with EXCLUSIVE MODE

You might use an EXCLUSIVE MODE table if:

  • Your transaction requires immediate update access to the locked table. When your transaction holds an exclusive table lock, other transactions cannot lock specific rows in the locked table.

  • Your transaction also ensures transaction-level read consistency for the locked table until the transaction is committed or rolled back.

  • You are not concerned about low levels of data concurrency, making transactions that request exclusive table locks wait in line to update the table sequentially.

Letting Oracle Database Control Table Locking

If you let Oracle Database control table locking, your application needs less programming logic, but also has less control than if you manage the table locks yourself.

Issuing the statement SET TRANSACTION ISOLATION LEVEL SERIALIZABLE or ALTER SESSION ISOLATION LEVEL SERIALIZABLE preserves ANSI serializability without changing the underlying locking protocol. This technique gives concurrent access to the table while providing ANSI serializability. Getting table locks greatly reduces concurrency.


See Also:


Change the settings for these parameters only when an instance is shut down. If multiple instances are accessing a single database, then all instances must use the same setting for these parameters.

Explicitly Acquiring Row Locks

You can override default locking with a SELECT statement that includes the FOR UPDATE clause. This statement acquires exclusive row locks for selected rows (as an UPDATE statement does), in anticipation of updating the selected rows in a subsequent statement.

You can use a SELECT FOR UPDATE statement to lock a row without actually changing it. For example, several triggers in Oracle Database PL/SQL Language Reference show how to implement referential integrity. In the EMP_DEPT_CHECK trigger, the row that contains the referenced parent key value is locked to guarantee that it remains for the duration of the transaction; if the parent key is updated or deleted, referential integrity is violated.

SELECT FOR UPDATE statements are often used by interactive programs that enable a user to modify fields of one or more specific rows (which might take some time); row locks are acquired so that only a single interactive program user is updating the rows at any given time.

If a SELECT FOR UPDATE statement is used when defining a cursor, the rows in the return set are locked when the cursor is opened (before the first fetch) rather than being locked as they are fetched from the cursor. Locks are only released when the transaction that opened the cursor is committed or rolled back, not when the cursor is closed.

Each row in the return set of a SELECT FOR UPDATE statement is locked individually; the SELECT FOR UPDATE statement waits until the other transaction releases the conflicting row lock. If a SELECT FOR UPDATE statement locks many rows in a table, and if the table experiences a lot of update activity, it might be faster to acquire an EXCLUSIVE table lock instead.


Note:

The return set for a SELECT FOR UPDATE might change while the query is running; for example, if columns selected by the query are updated or rows are deleted after the query started. When this happens, SELECT FOR UPDATE acquires locks on the rows that did not change, gets a read-consistent snapshot of the table using these locks, and then restarts the query to acquire the remaining locks.

This can cause a deadlock between sessions querying the table concurrently with DML statements when rows are locked in a nonsequential order. To prevent such deadlocks, design your application so that concurrent DML statements on the table do not affect the return set of the query. If this is not feasible, you might want to serialize queries in your application.


By default, the SELECT FOR UPDATE statement waits until the requested row lock is acquired. To change this behavior, use the NOWAIT, WAIT, or SKIP LOCKED clause of the SELECT FOR UPDATE statement. For information about these clauses, see Oracle Database SQL Language Reference.

Examples of Concurrency Under Explicit Locking

Table 1-3 shows how Oracle Database maintains data concurrency, integrity, and consistency when the LOCK TABLE statement and the SELECT statement with the FOR UPDATE clause are used. For brevity, the message text for ORA-00054 ("resource busy and acquire with NOWAIT specified") is not included. User-entered text is bold.


Note:

In tables compressed with Hybrid Columnar Compression (HCC), DML statements lock compression units rather than rows. HCC, a feature of certain Oracle storage systems, is described in Oracle Database Concepts.

Table 1-3 Examples of Concurrency Under Explicit Locking

Transaction 1Time PointTransaction 2
LOCK TABLE hr.departments
IN ROW SHARE MODE;

Statement processed.

1



2

DROP TABLE hr.departments;

DROP TABLE hr.departments
* 
ORA-00054

(Exclusive DDL lock not possible because Transaction 1 has table locked.)


3

LOCK TABLE hr.departments
IN EXCLUSIVE MODE
NOWAIT;
 
ORA-00054

4

SELECT location_id
FROM hr.departments
WHERE department_id = 20
FOR UPDATE OF location_id;
 
LOCATION_ID
-----------
DALLAS
 
1 row selected.
UPDATE hr.departments
SET location_id = 'NEW YORK'
WHERE department_id = 20;

(Waits because Transaction 2 locked same rows.)

5



6

ROLLBACK;

(Releases row locks.)

1 row processed.

ROLLBACK;

7


LOCK TABLE hr.departments
IN ROW EXCLUSIVE MODE;
 
Statement processed.

8



9

LOCK TABLE hr.departments
IN EXCLUSIVE MODE
NOWAIT;
 
ORA-00054

10

LOCK TABLE hr.departments
IN SHARE ROW EXCLUSIVE MODE
NOWAIT;
 
ORA-00054

11

LOCK TABLE hr.departments
IN SHARE ROW EXCLUSIVE MODE
NOWAIT;
 
ORA-00054

12

UPDATE hr.departments
SET location_id = 'NEW YORK'
WHERE department_id = 20;
 
1 row processed.

13

ROLLBACK;
SELECT location_id
FROM hr.departments
WHERE department_id = 20
FOR UPDATE OF location_id;
 
LOCATION_ID
-----------
DALLAS
 
1 row selected.

14



15

UPDATE hr.departments
SET location_id = 'NEW YORK'
WHERE department_id = 20;
 
1 row processed.

(Waits because Transaction 1 locked same rows.)

ROLLBACK;

16



17

1 row processed.

(Conflicting locks were released.)

ROLLBACK;
LOCK TABLE hr.departments
IN ROW SHARE MODE
 
Statement processed.

18



19

LOCK TABLE hr.departments
IN EXCLUSIVE MODE
NOWAIT;
 
ORA-00054

20

LOCK TABLE hr.departments
IN SHARE ROW EXCLUSIVE MODE
NOWAIT;
 
ORA-00054

21

LOCK TABLE hr.departments
IN SHARE MODE;
 
Statement processed.

22

SELECT location_id
FROM hr.departments
WHERE department_id = 20;
 
LOCATION_ID
-----------
DALLAS
 
1 row selected.

23

SELECT location_id
FROM hr.departments
WHERE department_id = 20
FOR UPDATE OF location_id;
 
LOCATION_ID
-----------
DALLAS
 
1 row selected.

24

UPDATE hr.departments
SET location_id = 'NEW YORK'
WHERE department_id = 20;

(Waits because Transaction 1 has conflicting table lock.)

ROLLBACK;

25



26

1 row processed.

(Conflicting table lock released.)

ROLLBACK;
LOCK TABLE hr.departments
IN SHARE ROW EXCLUSIVE MODE;
 
Statement processed.

27



28

LOCK TABLE hr.departments
IN EXCLUSIVE MODE
NOWAIT;
 
ORA-00054

29

LOCK TABLE hr.departments
IN SHARE ROW EXCLUSIVE MODE
NOWAIT;
 
ORA-00054

30

LOCK TABLE hr.departments
IN SHARE MODE
NOWAIT;
 
ORA-00054

31

LOCK TABLE hr.departments
IN ROW EXCLUSIVE MODE
NOWAIT;
 
ORA-00054

32

LOCK TABLE hr.departments
IN SHARE MODE
NOWAIT;
 
ORA-00054

33

SELECT location_id
FROM hr.departments
WHERE department_id = 20;
 
LOCATION_ID
-----------
DALLAS
 
1 row selected.

34

SELECT location_id
FROM hr.departments
WHERE department_id = 20
FOR UPDATE OF location_id;
 
LOCATION_ID
-----------
DALLAS
 
1 row selected.

35

UPDATE hr.departments
SET location_id = 'NEW YORK'
WHERE department_id = 20;

(Waits because Transaction 1 has conflicting table lock.)

UPDATE hr.departments
SET location_id = 'NEW YORK'
WHERE department_id = 20;

(Waits because Transaction 2 locked same rows.)

36

(Deadlock.)

Cancel operation.

ROLLBACK;

37



38

1 row processed.
LOCK TABLE hr.departments
IN EXCLUSIVE MODE;

39



40

LOCK TABLE hr.departments
IN EXCLUSIVE MODE;

ORA-00054

41

LOCK TABLE hr.departments
IN ROW EXCLUSIVE MODE
NOWAIT;

ORA-00054

42

LOCK TABLE hr.departments
IN SHARE MODE;

ORA-00054

43

LOCK TABLE hr.departments
IN ROW EXCLUSIVE MODE
NOWAIT;

ORA-00054

44

LOCK TABLE hr.departments
IN ROW SHARE MODE
NOWAIT;

ORA-00054

45

SELECT location_id
FROM hr.departments
WHERE department_id = 20;
 
LOCATION_ID
-----------
DALLAS
 
1 row selected.

46

SELECT location_id
FROM hr.departments
WHERE department_id = 20
FOR UPDATE OF location_id;

(Waits because Transaction 1 has conflicting table lock.)

UPDATE hr.departments
SET department_id = 30
WHERE department_id = 20;
 
1 row processed.

47


COMMIT;

48



49

0 rows selected.

(Transaction 1 released conflicting lock.)

SET TRANSACTION READ ONLY;

50


SELECT location_id
FROM hr.departments
WHERE department_id = 10;
 
LOCATION_ID
-----------
BOSTON

51



52

UPDATE hr.departments
SET location_id = 'NEW YORK'
WHERE department_id = 10;
 
1 row processed.
SELECT location_id
FROM hr.departments
WHERE department_id = 10;
 
LOCATION_ID
-----------
BOSTON

(Transaction 1 does not see uncommitted data.)

53



54

COMMIT;
SELECT location_id
FROM hr.departments
WHERE department_id = 10;
 
LOCATION_ID
-----------
BOSTON

(Same result even after Transaction 2 commits.)

55


COMMIT;

56


SELECT location_id
FROM hr.departments
WHERE department_id = 10;
 
LOCATION_ID
-----------
NEW YORK

(Sees committed data.)

57



Using Oracle Lock Management Services (User Locks)

Your applications can use Oracle Lock Management services (user locks) by invoking subprograms the DBMS_LOCK package. An application can request a lock of a specific mode, give it a unique name (recognizable in another subprogram in the same or another instance), change the lock mode, and release it. Because a reserved user lock is an Oracle Database lock, it has all the features of a database lock, such as deadlock detection. Ensure that any user locks used in distributed transactions are released upon COMMIT, otherwise an undetected deadlock can occur.


See Also:

Oracle Database PL/SQL Packages and Types Reference for detailed information about the DBMS_LOCK package

Topics:

When to Use User Locks

User locks can help:

  • Provide exclusive access to a device, such as a terminal

  • Provide application-level enforcement of read locks

  • Detect when a lock is released and clean up after the application

  • Synchronize applications and enforce sequential processing

Example 1-2 shows how the Pro*COBOL precompiler uses locks to ensure that there are no conflicts when multiple people must access a single device.

Example 1-2 How the Pro*COBOL Precompiler Uses Locks

****************************************************************** 
* Print Check                                                    * 
* Any cashier may issue a refund to a customer returning goods.  * 
* Refunds under $50 are given in cash, more than $50 by check.   * 
* This code prints the check. One printer is opened by all       * 
* the cashiers to avoid the overhead of opening and closing it   * 
* for every check, meaning that lines of output from multiple    * 
* cashiers can become interleaved if you do not ensure exclusive * 
* access to the printer. The DBMS_LOCK package is used to        * 
* ensure exclusive access.                                       * 
****************************************************************** 
CHECK-PRINT 
*    Get the lock "handle" for the printer lock. 
   MOVE "CHECKPRINT" TO LOCKNAME-ARR. 
   MOVE 10 TO LOCKNAME-LEN. 
   EXEC SQL EXECUTE 
      BEGIN DBMS_LOCK.ALLOCATE_UNIQUE ( :LOCKNAME, :LOCKHANDLE ); 
      END; END-EXEC. 
*   Lock the printer in exclusive mode (default mode).
   EXEC SQL EXECUTE 
      BEGIN DBMS_LOCK.REQUEST ( :LOCKHANDLE ); 
      END; END-EXEC. 
*   You now have exclusive use of the printer, print the check. 
  ... 
*   Unlock the printer so other people can use it 
EXEC SQL EXECUTE 
      BEGIN DBMS_LOCK.RELEASE ( :LOCKHANDLE ); 
      END; END-EXEC.

Viewing and Monitoring Locks

Table 1-4 describes the Oracle Database facilities that display locking information for ongoing transactions within an instance.

Table 1-4 Ways to Display Locking Information

ToolDescription

Oracle Enterprise Manager Database Control

The Database Locks page shows user locks, all database locks, or locks that are blocking other users or applications. For more information, see Oracle Database 2 Day + Real Application Clusters Guide.

Performance Monitoring Data Dictionary Views

See Oracle Database Administrator's Guide.

UTLLOCKT.SQL

The UTLLOCKT.SQL script displays a simple character lock wait-for graph in tree structured fashion. Using any SQL tool (such as SQL*Plus) to run the script, it prints the sessions in the system that are waiting for locks and the corresponding blocking locks. The location of this script file is operating system dependent. (You must have run the CATBLOCK.SQL script before using UTLLOCKT.SQL.)


Using Serializable Transactions for Concurrency Control

By default, Oracle Database permits concurrently running transactions to modify, add, or delete rows in the same table, and in the same data block. When transaction A changes a table, the changes are invisible to concurrently running transactions until transaction A commits them. If transaction A tries to update or delete a row that transaction B has locked (by issuing a DML or SELECT FOR UPDATE statement), then the DML statement that A issued waits until B either commits or rolls back the transaction. This concurrency model, which provides higher concurrency and thus better performance, is appropriate for most applications.

However, some rare applications require serializable transactions. Serializable transactions run concurrently in serialized mode. In serialized mode, concurrent transactions can make only the database changes that they could make if they were running serially (that is, one at a time). If a serialized transaction tries to change data that another transaction changed after the serialized transaction began, then error ORA-08177 occurs.

When a serializable transaction fails with ORA-08177, the application can take any of these actions:

  • Commit the work executed to that point.

  • Run additional, different, statements, perhaps after rolling back to a prior savepoint in the transaction.

  • Roll back the transaction and then rerun it.

    The transaction gets a transaction snapshot and the operation is likely to succeed.


    Tip:

    To minimize the performance overhead of rolling back and re running transactions, put DML statements that might conflict with concurrent transactions near the beginning of the transaction.


Note:

Serializable transactions do not work with deferred segment creation or interval partitioning. Trying to insert data into an empty table with no segment created, or into a partition of an interval partitioned table that does not yet have a segment, causes an error.

Topics:

Transaction Interaction and Isolation Level

The ANSI/ISO SQL standard defines three kinds of transaction interaction:

Transaction InteractionDefinition
Dirty readTransaction A reads uncommitted changes made by transaction B.
Unrepeatable readTransaction A reads data, transaction B changes the data and commits the changes, and transaction A rereads the data and sees the changes.
Phantom readTransaction A runs a query, transaction B inserts new rows and commits the change, and transaction A repeats the query and sees the new rows.

The kinds of interactions that a transaction can have is determined by its isolation level. The ANSI/ISO SQL standard defines four transaction isolation levels. Table 1-5 shows what kind of interactions are possible at each isolation level.

Table 1-5 ANSI/ISO SQL Isolation Levels and Possible Transaction Interactions

Isolation LevelDirty ReadUnrepeatable ReadPhantom Read

READ UNCOMMITTED

Possible

Possible

Possible

READ COMMITTED

Not possible

Possible

Possible

REPEATABLE READ

Not possible

Not possible

Possible

SERIALIZABLE

Not possible

Not possible

Not possible


Table 1-6 shows which ANSI/ISO SQL transaction isolation levels Oracle Database provides.

Table 1-6 ANSI/ISO SQL Isolation Levels Provided by Oracle Database

Isolation LevelProvided by Oracle Database

READ UNCOMMITTED

No. Oracle Database never permits "dirty reads." Some other database products use this undesirable technique to improve thoughput, but it is not required for high throughput with Oracle Database.

READ COMMITTED

Yes, by default. In fact, because an Oracle Database query sees only data that was committed at the beginning of the query (the snapshot time), Oracle Database offers more consistency than the ANSI/ISO SQL standard for READ COMMITTED isolation requires.

REPEATABLE READ

Yes, if you set the transaction isolation level to SERIALIZABLE.

SERIALIZABLE

Yes, if you set the transaction isolation level to SERIALIZABLE.


Figure 1-1 shows how an arbitrary transaction (that is, one that is either SERIALIZABLE or READ COMMITTED) interacts with a serializable transaction.

Figure 1-1 Interaction Between Serializable Transaction and Another Transaction

Time Line for Two Transactions
Description of "Figure 1-1 Interaction Between Serializable Transaction and Another Transaction"

Setting Isolation Levels

To set the transaction isolation level for every transaction in your session, use the ALTER SESSION statement, described in Oracle Database SQL Language Reference.

To set the transaction isolation level for a specific transaction, use the ISOLATION LEVEL clause of the SET TRANSACTION statement. The SET TRANSACTION statement, described in Oracle Database SQL Language Reference, must be the first statement in the transaction.


Note:

If you set the transaction isolation level to SERIALIZABLE, then you must use the ALTER TABLE statement to set the INITRANS parameter to at least 3. Use higher values for tables for which many transactions update the same blocks. For more information about INITRANS, see Oracle Database SQL Language Reference.

Serializable Transactions and Referential Integrity

Because Oracle Database does not use read locks, even in SERIALIZABLE transactions, data read by one transaction can be overwritten by another. Therefore, transactions that perform database consistency checks at the application level must not assume that the data they read does not change during the transaction (even though such changes are invisible to the transaction). Code your application-level consistency checks carefully, even when using SERIALIZABLE transactions.

In Figure 1-2, transactions A and B (which are either READ COMMITTED or SERIALIZABLE) perform application-level checks to maintain the referential integrity of the parent/child relationship between two tables. Transaction A queries the parent table to check that it has a row with a specific primary key value before inserting corresponding child rows into the child table. Transaction B queries the child table to check that no child rows exist for a specific primary key value before deleting the corresponding parent row from the parent table. Both transactions assume (but do not ensure) that the data they read does not change before the transaction completes.

Figure 1-2 Referential Integrity Check

Referential Integrity Check
Description of "Figure 1-2 Referential Integrity Check"

The query by transaction A does not prevent transaction B from deleting the parent row, and the query by transaction B does not prevent transaction A from inserting child rows. Therefore, this can happen:

  1. Transaction A queries the parent table and finds the specified parent row.

  2. Transaction B queries the child table and finds no child rows for the specified parent row.

  3. Having found the specified parent row, transaction A inserts the corresponding child rows into the child table.

  4. Having found no child rows for the specified parent row, transaction B deletes the specified parent row from the parent table.

    Now the child rows that transaction A inserted in step 3 have no parent row.

The preceding result can occur even if both A and B are SERIALIZABLE transactions, because neither transaction prevents the other from changing the data that it reads to check consistency.

Ensuring that data queried by one transaction is not concurrently changed or deleted by another requires more transaction isolation than the ANSI/ISO SQL standard SERIALIZABLE isolation level provides. However, in Oracle Database:

  • Transaction A can use a SELECT FOR UPDATE statement to query and lock the parent row, thereby preventing transaction B from deleting it.

    For information about the FOR UPDATE clause of the SELECT statement, see Oracle Database SQL Language Reference.

  • Transaction B can prevent transaction A from finding the parent row (thereby preventing A from inserting the child rows) by reversing the order of its processing steps. That is, transaction B can:

    1. Delete the parent row.

    2. Query the child table.

    3. If the deleted parent row has child rows in the child table, then roll back the deletion of the parent row.

Alternatively, you can enforce referential integrity with a trigger. Instead of having transaction A query the parent table, define on the child table a row-level BEFORE INSERT trigger that does this:

  • Queries the parent table with a SELECT FOR UPDATE statement, thereby ensuring that if the parent row exists, then it remains in the database for the duration of the transaction that inserts the child rows.

  • Rejects the insertion of the child rows if the parent row does not exist.


See Also:

Oracle Database PL/SQL Language Reference for more information about using triggers to maintain referential integrity between parent and child tables

A trigger runs SQL statements in the context of the triggering statement (that is, the triggering and triggered statements see the database in the same state). Therefore, if a READ COMMITTED transaction runs the triggering statement, then the triggered statements see the database as it was when the triggering statement began to execute. If a SERIALIZABLE transaction runs the triggering statement, then the triggered statements see the database as it was at the beginning of the transaction. In either case, using SELECT FOR UPDATE in the trigger correctly enforces referential integrity.

READ COMMITTED and SERIALIZABLE Isolation Levels

Oracle Database provides two transaction isolation levels, READ COMMITTED and SERIALIZABLE. Both levels provide a high degree of consistency and concurrency, reduce contention, and are designed for real-world applications. This topic compares them and explains how to choose between them.

Topics:

Transaction Set Consistency Differences

An operation (query or transaction) is transaction set consistent if all of its read operations return data written by the same set of committed transactions. When an operation is not transaction set consistent, some of its read operations reflect the changes of one set of transactions and others reflect the changes of other sets of transactions; that is, the operation sees the database in a state that reflects no single set of committed transactions.

Topics:

Oracle Database

Oracle Database transactions with READ COMMITTED isolation level are transaction set consistent on an individual-statement basis, because all rows that a query reads must be committed before the query begins.

Oracle Database transactions with SERIALIZABLE isolation level are transaction set consistent on an individual-transaction basis, because all statements in a SERIALIZABLE transaction run on an image of the database as it was at the beginning of the transaction.

Other Database Systems

In other database systems, a single query with READ UNCOMMITTED isolation level is not transaction set consistent, because it might see only a subset of the changes made by another transaction. For example, a join of a master table with a detail table can see a master record inserted by another transaction, but not the corresponding details inserted by that transaction (or the reverse). READ COMMITTED isolation level avoids this problem, providing more consistency than read-locking systems do.

In read-locking systems, at the cost of preventing concurrent updates, the REPEATABLE READ isolation level provides transaction set consistency at the statement level, but not at the transaction level. Due to the absence of phantom read protection, two queries in the same transaction can see data committed by different sets of transactions. In these systems, only the throughput-limiting and deadlock-susceptible SERIALIZABLE isolation level provides transaction set consistency at the transaction level.

Choosing Transaction Isolation Levels

The choice of transaction isolation level depends on performance and consistency needs and application coding requirements. There is a trade-off between concurrency (transaction throughput) and consistency. Consider the application and workload when choosing isolation levels for its transactions. Different transactions can have different isolation levels.

For environments with many concurrent users rapidly submitting transactions, consider expected transaction arrival rate, response time demands, and required degree of consistency.

READ COMMITTED isolation can provide considerably more concurrency with a somewhat increased risk of inconsistent results (from unrepeatable and phantom reads) for some transactions.

SERIALIZABLE isolation provides somewhat more consistency (by protecting against phantoms and unrepeatable reads), which might be important where a read/write transaction runs a query more than once. However, SERIALIZABLE isolation requires applications to check for the "cannot serialize access" error, and this checking can significantly reduce throughput in an environment with many concurrent transactions accessing the same data for update.

As explained in "Serializable Transactions and Referential Integrity" reads do not block writes in either READ COMMITTED or SERIALIZABLE transactions.

Table 1-7 summarizes the similarities and differences between READ COMMITTED and SERIALIZABLE transactions.

Table 1-7 Comparison of READ COMMITTED and SERIALIZABLE Transactions

OperationREAD COMMITTEDSERIALIZABLE

Dirty write

Not Possible

Not Possible

Dirty read

Not Possible

Not Possible

Unrepeatable read

Possible

Not Possible

Phantom read

Possible

Not Possible

Compliant with ANSI/ISO SQL 92

Yes

Yes

Read snapshot time

Statement

Transaction

Transaction set consistency

Statement level

Transaction level

Row-level locking

Yes

Yes

Readers block writers

No

No

Writers block readers

No

No

Different-row writers block writers

No

No

Same-row writers block writers

Yes

Yes

Waits for blocking transaction

Yes

Yes

Subject to "cannot serialize access" error

No

Yes

Error after blocking transaction terminates

No

No

Error after blocking transaction commits

No

Yes


Autonomous Transactions

An autonomous transaction (AT) is an independent transaction started by another transaction, the main transaction (MT). An autonomous transaction lets you suspend the main transaction, do SQL operations, commit or roll back those operations, and then resume the main transaction.

For example, in a stock purchase transaction, you might want to commit customer information regardless of whether the purchase succeeds. Or, you might want to log error messages to a debug table even if the transaction rolls back. Autonomous transactions enable you to do such tasks.

An autonomous transaction runs within an autonomous scope; that is, within the scope of an autonomous routine—a routine that you mark with the AUTONOMOUS_TRANSACTION pragma. For the definition of routine in this context, see Oracle Database PL/SQL Language Reference.

Figure 1-3 shows how control flows from the main transaction (MT) to an autonomous transaction (AT) and back again. As you can see, the autonomous transaction can commit multiple transactions (AT1 and AT2) before control returns to the main transaction.

Figure 1-3 Transaction Control Flow

Transaction Control Flow
Description of "Figure 1-3 Transaction Control Flow"

When you enter the executable section of an autonomous transaction, the main transaction suspends. When you exit the transaction, the main transaction resumes. COMMIT and ROLLBACK end the active autonomous transaction but do not exit the autonomous transaction. As Figure 1-3 shows, when one transaction ends, the next SQL statement begins another transaction.

More characteristics of autonomous transactions:

  • The changes an autonomous transaction effects do not depend on the state or the eventual disposition of the main transaction. For example:

    • An autonomous transaction does not see changes made by the main transaction.

    • When an autonomous transaction commits or rolls back, it does not affect the outcome of the main transaction.

  • The changes an autonomous transaction effects are visible to other transactions as soon as that autonomous transaction commits. Therefore, users can access the updated information without having to wait for the main transaction to commit.

  • Autonomous transactions can start other autonomous transactions.

Figure 1-4 shows some possible sequences that autonomous transactions can follow.

Figure 1-4 Possible Sequences of Autonomous Transactions

Possible Sequences of Autonomous Transactions
Description of "Figure 1-4 Possible Sequences of Autonomous Transactions"

Topics:


See Also:

Oracle Database PL/SQL Language Reference for detailed information about autonomous transactions

Examples of Autonomous Transactions

As these examples show, there are four possible outcomes when you use autonomous and main transactions (see Table 1-8). There is no dependency between the outcome of an autonomous transaction and that of a main transaction.

Table 1-8 Possible Transaction Outcomes

Autonomous TransactionMain Transaction

Commits

Commits

Commits

Rolls back

Rolls back

Commits

Rolls back

Rolls back


Ordering a Product

Figure 1-5 shows an example of a customer ordering a product. The customer information (such as name, address, phone) is committed to a customer information table—even though the sale does not go through.

Figure 1-5 Example: A Buy Order

Example: A Buy Order
Description of "Figure 1-5 Example: A Buy Order"

Withdrawing Money from a Bank Account

In this example, a customer tries to withdraw money from a bank account. In the process, a main transaction invokes one of two autonomous transaction scopes (AT Scope 1 or AT Scope 2).

The possible scenarios for this transaction are:

Scenario 1: Sufficient Funds

There are sufficient funds to cover the withdrawal, so the bank releases the funds (see Figure 1-6).

Figure 1-6 Bank Withdrawal—Sufficient Funds

Sufficient Funds
Description of "Figure 1-6 Bank Withdrawal—Sufficient Funds"

Scenario 2: Insufficient Funds with Overdraft Protection

There are insufficient funds to cover the withdrawal, but the customer has overdraft protection, so the bank releases the funds (see Figure 1-7).

Figure 1-7 Bank Withdrawal—Insufficient Funds with Overdraft Protection

Insufficient Funds with Overdraft Protection
Description of "Figure 1-7 Bank Withdrawal—Insufficient Funds with Overdraft Protection"

Scenario 3: Insufficient Funds Without Overdraft Protection

There are insufficient funds to cover the withdrawal and the customer does not have overdraft protection, so the bank withholds the requested funds (see Figure 1-8).

Figure 1-8 Bank Withdrawal—Insufficient Funds Without Overdraft Protection

Insufficient Funds Without Overdraft Protection
Description of "Figure 1-8 Bank Withdrawal—Insufficient Funds Without Overdraft Protection"

Defining Autonomous Transactions

To define an autonomous transaction, use PRAGMA AUTONOMOUS_TRANSACTION, which instructs the PL/SQL compiler to mark the subprogram as autonomous.

In Example 1-3, the function balance is autonomous.

Example 1-3 Marking a Package Subprogram as Autonomous

-- Create table for package to use:
 
DROP TABLE accounts;
CREATE TABLE accounts (account INTEGER, balance REAL);
 
-- Create package:
 
CREATE OR REPLACE PACKAGE banking AS
  FUNCTION balance (acct_id INTEGER) RETURN REAL;
  -- Additional functions and packages
END banking;
/
CREATE OR REPLACE PACKAGE BODY banking AS
  FUNCTION balance (acct_id INTEGER) RETURN REAL IS
    PRAGMA AUTONOMOUS_TRANSACTION;
    my_bal  REAL;
  BEGIN
    SELECT balance INTO my_bal FROM accounts WHERE account=acct_id;
    RETURN my_bal;
  END;
  -- Additional functions and packages
END banking;
/

See Also:

Oracle Database PL/SQL Language Reference for more information about PRAGMA AUTONOMOUS_TRANSACTION

Resuming Execution After Storage Allocation Errors

When a long-running transaction is interrupted by a storage allocation error, the application can suspend the statement that encountered the problem, correct the problem, and then resume executing the statement. This capability, called resumable storage allocation, avoids time-consuming rollbacks. It also makes it unnecessary to split the operation into smaller pieces and write code to track its progress.


See Also:

Oracle Database Administrator's Guide for more information about resumable storage allocation

Topics:

What Operations Have Resumable Storage Allocation?

Queries, DML statements, and some DDL statements have resumable storage allocation after these kinds of errors:

  • Out-of-space errors, such as ORA-01653.

  • Space-limit errors, such as ORA-01628.

  • Space-quota errors, such as ORA-01536.

Resumable storage allocation is possible whether the operation is performed directly by a SQL statement or within SQL*Loader, a stored subprogram, an anonymous PL/SQL block, or an OCI call such as OCIStmtExecute.

In dictionary-managed tablespaces, you cannot resume an index- or table-creating operation that encounters the limit for rollback segments or the maximum number of extents. You must use locally managed tablespaces and automatic undo management in combination with resumable storage allocation.

Handling Suspended Storage Allocation

When a statement in an application is suspended because of a storage allocation error, the application does not receive an error code. Therefore, either the application must use an AFTER SUSPEND trigger or the DBA must periodically check for suspended statements.

After the problem is corrected (usually by the DBA), the suspended statement automatically resumes execution. If the timeout period expires before the problem is corrected, then the statement raises a SERVERERROR exception.

Topics:

Using an AFTER SUSPEND Trigger in the Application

In the application, an AFTER SUSPEND trigger can get information about the problem by invoking subprograms in the DBMS_RESUMABLE package (described in Oracle Database PL/SQL Packages and Types Reference). Then the trigger can send the information to an operator, using email (for example).

To reduce the chance of out-of-space errors within the trigger itself, declare the trigger as an autonomous transaction. As an autonomous transaction, the trigger uses a rollback segment in the SYSTEM tablespace. If the trigger encounters a deadlock condition because of locks held by the suspended statement, then the trigger terminates and the application receives the original error code, as if the statement were never suspended. If the trigger encounters an out-of-space condition, then both the trigger and the suspended statement are rolled back. To prevent rollback, use an exception handler in the trigger to wait for the statement to resume.

For general information about triggers, see Oracle Database PL/SQL Language Reference.

The trigger in Example 1-4 handles storage errors within the database. For some kinds of errors, the trigger terminates the statement and alerts the DBA, using e-mail. For other errors, which might be temporary, the trigger specifies that the statement waits for eight hours before resuming, expecting the storage problem to be fixed by then. To run this example, you must connect to the database as SYSDBA.

Example 1-4 AFTER SUSPEND Trigger Handles Suspended Storage Allocation

-- Create table used by trigger body
 
DROP TABLE rbs_error;
CREATE TABLE rbs_error (
  SQL_TEXT VARCHAR2(64),
  ERROR_MSG VARCHAR2(64),
  SUSPEND_TIME VARCHAR2(64)
);
 
-- Resumable Storage Allocation

CREATE OR REPLACE TRIGGER suspend_example
  AFTER SUSPEND
  ON DATABASE
DECLARE
  cur_sid           NUMBER;
  cur_inst          NUMBER;
  err_type          VARCHAR2(64);
  object_owner      VARCHAR2(64);
  object_type       VARCHAR2(64);
  table_space_name  VARCHAR2(64);
  object_name       VARCHAR2(64);
  sub_object_name   VARCHAR2(64);
  msg_body          VARCHAR2(64);
  ret_value         BOOLEAN;
  error_txt         VARCHAR2(64);
  mail_conn         UTL_SMTP.CONNECTION;
BEGIN
 SELECT DISTINCT(SID) INTO cur_sid FROM V$MYSTAT;
 cur_inst := USERENV('instance');
 ret_value := DBMS_RESUMABLE.SPACE_ERROR_INFO
              (err_type,
              object_owner,
              object_type,
              table_space_name,
              object_name,
              sub_object_name);
 IF object_type = 'ROLLBACK SEGMENT' THEN
   INSERT INTO rbs_error
     (SELECT SQL_TEXT, ERROR_MSG, SUSPEND_TIME
      FROM DBA_RESUMABLE
      WHERE SESSION_ID = cur_sid
      AND INSTANCE_ID = cur_inst);

    SELECT ERROR_MSG INTO error_txt
    FROM DBA_RESUMABLE
    WHERE SESSION_ID = cur_sid
    AND INSTANCE_ID = cur_inst;

    msg_body :=
     'Space error occurred: Space limit reached for rollback segment '
     || object_name || ' on ' || to_char(SYSDATE, 'Month dd, YYYY, HH:MIam')
     || '. Error message was: ' || error_txt;

    mail_conn := UTL_SMTP.OPEN_CONNECTION('localhost', 25);
    UTL_SMTP.HELO(mail_conn, 'localhost');
    UTL_SMTP.MAIL(mail_conn, 'sender@localhost');
    UTL_SMTP.RCPT(mail_conn, 'recipient@localhost');
    UTL_SMTP.DATA(mail_conn, msg_body);
    UTL_SMTP.QUIT(mail_conn);
    DBMS_RESUMABLE.ABORT(cur_sid);
  ELSE
    DBMS_RESUMABLE.SET_TIMEOUT(3600*8);
  END IF;
  COMMIT;
END;
/

Checking for Suspended Statements

If the application does not use an AFTER SUSPEND trigger, then the DBA must periodically check for suspended statements, using the static data dictionary view DBA_RESUMABLE (described in Oracle Database Reference).

The DBA can get additional information from the dynamic performance view V$_SESSION_WAIT (described in Oracle Database Reference).

PKҰt_[_PK|%AOEBPS/img_text/adfns109.htmU Description of the illustration adfns109.eps

This graphic shows the MGD_ID base code object as the uppermost box, the RFID code categories beneath it (labeled as boxes EPC, NASA, and others), then beneath these code category boxes, the respective scheme boxes. So beneath the EPC category box are the scheme boxes labeled SGTIN-64, SGTIN-96, and GID-96 and beneath the NASA category box are the scheme boxes labeled NASA-T1 and NASA-T2. Each level of boxes in the hierarchy is connected with downward pointing arrows to indicate their relationship.

PK~PK|%AOEBPS/img_text/adfns058.htm Description of the illustration adfns058.eps
  1. MTx generates a transaction ID.

  2. Tx1.1 inserts the transaction ID into the audit table and commits.

  3. MTx validates the balance on the account.

  4. Tx2.1 updates the audit table using the transaction ID generated earlier, then commits.

  5. MTx releases the funds. MT Scope ends.

PKy`PK|%AOEBPS/img_text/adfns065.htmA Description of the illustration adfns065.eps

This figure shows 5 boxes with 6 lines connecting them:

  1. An arrow from a Publisher (Agent) box to a Topic (Subject Channel) box

  2. A "receive notification/message" arrow from the Topic to a Subscriber (Agent) box

  3. A "register" arrow from the Subscriber to the Topic

  4. A "subscribe" arrow from the Subscriber to the Topic

  5. A line between the Topic box and a Subscriptions box

  6. A line between the Topic box and a Rules box

PK,.!PK|%AOEBPS/img_text/adfns062.htm5 Description of the illustration adfns062.eps

This figure shows an exception and an associated exception handler. A trigger uses a conditional statement that checks for an error condition and raises an application error. The user-defined error code and message are returned to the error handler.

PROCEDURE fire_emp (empid NUMBER) IS
  invalid_empid EXCEPTION
  PRAGMA EXCEPTION_INIT (invalid_empid, -20101);
BEGIN
  DELETE FROM emp WHERE empno = empid;
EXCEPTION
  WHEN invlid_empid THEN
   INSERT INTO emp_audit
    VALUES (empid, 'Fired before probation ended');
END;

This program returns the error number 20101 to the invoking environment (the preceding program).

TRIGGER emp_probation
BEFORE DELETE ON emp
FOR EACH ROW
BEGIN
  IF (sysdate - :old.hiredate) < 30 THEN
    raise_application_error(20101,
     'Employee' || old.ename || ' on probation')
  END IF;
END;
PK!:5PK|%AOEBPS/img_text/adfns057.htm] Description of the illustration adfns057.eps
  1. MT Scope begins the main transaction, MTx inserts the buy order into a table.

  2. MTx invokes the autonomous transaction scope (AT Scope). When AT Scope begins, MT Scope suspends.

  3. ATx updates the audit table with customer information.

  4. MTx seeks to validate the order, finds that the selected item is unavailable, and therefore rolls back the main transaction.

PK* b]PK|%AOEBPS/img_text/adfns104.htmE Description of the illustration adfns104.eps

This graphic shows how a middle-tier application uses Continuous Query Notification. The steps of the process are described in the following text.

PK4JEPK|%AOEBPS/img_text/adfns059.htm Description of the illustration adfns059.eps
  1. MTx discovers that there are insufficient funds to cover the withdrawal. It finds that the customer has overdraft protection and sets a flag to the appropriate value.

  2. Tx2.1 updates the audit table.

  3. MTx releases the funds.

  4. MT Scope ends.

PKiUPK|%AOEBPS/img_text/adfns091.htmY Description of the illustration adfns091.eps

This illustration shows the OO4O software components (layers).

The first layer contains the Data Aware ActiveX Controls.

The second layer consists of C++ Class Libraries and Oracle Data Control Automation Controllers (VB< Excel, ASP).

The third layer contains the COM/DCOM.

The fourth layer contains the OO40 In-Process Automation Server.

The fifth layer contains the Oracle Client Libraries (OCI, CORE, NLS).

The last layer contains the Oracle Database.

PKPK|%AOEBPS/img_text/adfns101.htmZ Description of the illustration adfns101.eps

This graphic depicts an Oracle Database that uses the embedded PL/SQL gateway to process client requests. The steps of the process are described in the following text.

PKډ_ZPK|%AOEBPS/img_text/adfns056.htm% Description of the illustration adfns056.eps

A main transaction scope (MT Scope) begins the main transaction, MTx. MTx invokes the first autonomous transaction scope (AT Scope1). MTx suspends. AT Scope 1 begins the transaction Tx1.1.

  1. AT Scope 1 commits or rolls back Tx1.1, then ends MTx resumes.

  2. MTx invokes AT Scope 2. MT suspends, passing control to AT Scope 2, which initially is performing queries.

  3. AT Scope 2 then begins Tx2.1 by, say, doing an update. AT Scope 2 commits or rolls back Tx2.1.

  4. Later, AT Scope 2 begins a second transaction, Tx2.2, then commits or rolls it back.

  5. AT Scope 2 performs a few queries, then ends, passing control back to MTx.

  6. MTx invokes AT Scope 3. MTx suspends, AT Scope 3 begins.

  7. AT Scope 3 begins Tx3.1, which in turn, invokes AT Scope 4. Tx3.1 suspends, AT Scope 4 begins.

  8. AT Scope 4 begins Tx4.1, commits or rolls it back, then ends. AT Scope 3 resumes.

  9. AT Scope 3 commits or rolls back Tx3.1, then ends. MTx resumes.

  10. MT Scope commits or rolls back MTx, then ends.

PKn5*%PK|%AOEBPS/img_text/adfns063.htmu Description of the illustration adfns063.eps

This figure shows:

  • A cylinder representing Oracle Database disk storage, containing a PL/SQL subprogram and a Java method

  • A box representing an Oracle Server process execution, containing the PL/SQL Interpreter, the Java Virtual Machine, and the SQL Engine

  • Two boxes representing an External Process Execution: one containing DLL and the other containing an external C process

Interactions are shown using arrows between these objects: a double arrow between the database cylinder and the Oracle Server Process Execution box, a double arrow between this box and the External C Process box, and an arrow directed from the DLL box to the External C Process box.

PK| PK|%AOEBPS/img_text/adfns079.htm| Description of the illustration adfns079.eps

This figure shows 6 boxes with 7 lines connecting them:

  1. A "TX Interface" double arrow between a Transaction Manager box and an Application Program box

  2. An "XA Interface" double arrow between the Transaction Manager and a Resource Manager box

  3. A double arrow between the Application Program and the Resource Manager

  4. An "XA Interface" double arrow between the Transaction Manager and a Resource box

  5. A "Native Interface" double arrow between the Application Program and the Resource

  6. An arrow from the Resource to a box called Other

  7. A line between the Resource Manager and an Oracle (Database) box

PK_PK|%AOEBPS/img_text/adfns089.htm Description of the illustration adfns089.eps

This illustration shows the Source Files being processed by the Host Language Compiler, which produces object files.

The object files are next linked by the host linker, with the OCI Library, which outputs the application executable.

The application can then communicate with the Oracle Server.

PK,PK|%AOEBPS/img_text/adfns053.htmt Description of the illustration adfns053.eps

This illustration shows a time line for two transactions, Transaction A (arbitrary) and Transaction B (serializable).

Transaction A issues an update that is too recent for Transaction B to see. Transaction B changes the other row in the same block, and sees its own changes. Transaction A creates a possible "phantom row." Uncommitted changes are invisible to Transaction B, so Transaction A commits, making changes visible to transactions that begin later.

Transaction B makes changes after Transaction A commits. Transaction B can see its own changes, but not the committed changes of Transaction A, so the attempt to update the row fails, after which Transaction B rolls back and retries.

PK<~ytPK|%AOEBPS/img_text/adfns054.htm3 Description of the illustration adfns054.eps

This illustration shows Transaction A and Transaction B. Transaction A has a read operation followed by an insert operation. Transaction B has a read operation followed by a delete operation. The read issued by Transaction A does not prevent Transaction B from deleting the parent row, and Transaction B's query for child rows does not prevent Transaction A from inserting child rows.

PK7I%83PK|%AOEBPS/img_text/adfns060.htm Description of the illustration adfns060.eps
  1. MTx discovers that there are insufficient funds to cover the withdrawal. It finds that the customer does not have overdraft protection and sets a flag to the appropriate value.

  2. Tx2.1 updates the audit table.

  3. MTx Scope rolls back MTx, denying the releases of funds.

  4. MT Scope ends.

PKhb|PK|%AOEBPS/img_text/adfns090.htmY Description of the illustration adfns090.eps

This illustration shows the OO4O software components (layers).

The first layer contains the Data Aware ActiveX Controls.

The second layer consists of C++ Class Libraries and Oracle Data Control Automation Controllers (VB< Excel, ASP).

The third layer contains the COM/DCOM.

The fourth layer contains the OO40 In-Process Automation Server.

The fifth layer contains the Oracle Client Libraries (OCI, CORE, NLS).

The last layer contains the Oracle Database.

PK^֫PK|%AOEBPS/img_text/adfns100.htm) Description of the illustration adfns100.eps

This figure shows that user sessions can spawn multiple threads to the multithreaded agent process, to make calls to DLLs. It shows two user sessions as boxes composed of an Oracle Server and an HS. There are arrows from each of the user sessions to dispatcher threads in an agent process - one thread per user session. There are arrows from each of the dispatcher threads to task threads, also within the agent process. One of the task threads is shown handling requests from both user sessions (through both dispatcher threads). There are arrows from each of the task threads to a collection of DLLs (outside the agent process).

PK" .)PK|%AOEBPS/img_text/adfns040.htm Description of the illustration adfns040.eps

Referenced or parent table (DEPTNO is its primary key):

Table DEPARTMENTS

DEPTNO   DNAME       LOC
------   -----       --------
20       RESEARCH    DALLAS
30       SALES       NEW YORK
40       MARKETING   BOSTON

Dependent or child table (DEPTNO is a foreign key; values in dependent table must match a value in unique key or primary key of referenced table):

Table EMPLOYEES

EMPNO  ENAME  JOB       MGR   HIREDATE   SAL       COMM    DEPTNO  COMMENT
-----  -----  --------  ----  ---------  --------  ------  ------  -------
7329   SMITH  CEO       N/A   17-DEC-85  9,000.00  N/A     20
7499   ALLEN  VP-SALES  7329  20-FEB-90  7,500.00  100.00  30
7521   WARD   MANAGER   7499  22-FEB-90  5,000.00  200.00  30
7586   JONES  SALESMAN  7521  02-APR-90  2,975.00  400.00  20
7571   FORD   MANAGER   7499  23-FEB-90  5,000.00  200.00  50      1
7571   FORD   MANAGER   7499  23-FEB-90  5,000.00  200.00  N/A     2
  1. This row violates the referential constraint because "50" is not present in the referenced table's primary key; therefore, the row is now allowed in the table.

  2. This row is allowed in the table because a null value is entered in the DEPTNO column; however, if a not null constraint is also defined for this column, this row is not allowed.

PK{+PK|%AOEBPS/img_text/adfns102.htm  Description of the illustration adfns102.eps

The process includes these steps:

  1. A user visits a Web page, follows a hypertext link, or submits data in a form, which causes the browser to send a HTTP request for a URL to an HTTP server.

  2. The HTTP server calls a stored subprogram on an Oracle Database according to the data encoded in the URL. The data in the URL takes the form of parameters to be passed to the stored subprogram.

  3. The stored subprogram invokes subprograms in the PL/SQL Web Toolkit. Typically, subprograms such as HTP.Print generate Web pages dynamically. A generated Web page varies depending on the database contents and the input parameters.

  4. The subprograms pass the dynamically generated page to the Web server.

  5. The Web server delivers the page to the client.

PKs9 PK|%AOEBPS/img_text/adfns039.htm5 Description of the illustration adfns039.eps
DEPNO   DNAME       LOC
-----   ---------   --------
20      RESEARCH    DALLAS
30      SALES       NEW YORK
40      MARKETING   BOSTON
-       UNIQUE      -

UNIQUE is a constraint that means no row can duplicate a value in the constraint's column.)

DEPNO   DNAME   LOC        COMMENTS
-----   -----   --------   --------
50      SALES   NEW YORK   1
60      N/A     BOSTON     2
  1. This row violates the UNIQUE key constraint, because SALES is already present in another row; therefore, it is not allowed in the table.

  2. This row is allowed because a NULL value is entered for the DNAME column; however, if a NOT NULL constraint is defined on the DNAME column, this row is not allowed.

PKډPK|%AOEBPS/img_text/adfns092.htm Description of the illustration adfns092.eps

This figure shows OO4O objects and their relations. The relations are as follows:

  • From OraServer to OraSession

  • From OraDatabase to both OraServer and OraSession

  • From each of OraDynaset, OraMetaData, OraParameters, OraSQLStmt, and OraAQ to OraDatabase

  • From multiple OraField objects to OraDynaset

  • From multiple OraMDAttribute objects to OraMetaData

  • From multiple OraParameter and OraParamArray objects to OraParameters

  • from OraAQMsg to OraAQ

PK3PK|%AOEBPS/img_text/adfns108.htmM Description of the illustration adfns108.eps

This graphic shows the TDT markup language schema beginning with the upper most box labeled IDCodeTranslator. The text that follows describes this schema.

PKUgRMPK|%AOEBPS/img_text/adfns022.htmC Description of the illustration adfns022.eps

This graphic illustrates an OracleAS Web Cache interacting with Web servers through HTTP and HTTPS, and with Oracle Database through Oracle Net.

PK'JHCPK|%AOEBPS/img_text/adfns055.htm Description of the illustration adfns055.eps

This figure shows how control flows from a main transaction (MT) to an autonomous transaction (AT) and back again.

The autonomous transaction commits two transactions, AT1 and AT2, before control returns to the main transaction. When control enters the executable section of the autonomous transaction (BEGIN), the main transaction suspends. When control exits the autonomous transaction (END), the main transaction resumes. COMMIT and ROLLBACK statements in an autonomous transaction end the active transaction, but they do not end the autonomous transaction.

PKyPK|%AOEBPS/adfns_extproc.htm Multithreaded extproc Agent

A Multithreaded extproc Agent

This appendix explains what the multithreaded extproc agent is, how it contributes to the overall efficiency of a distributed database system, and how to administer it.

Topics:

Why Use the Multithreaded extproc Agent?

This section explains how the multithreaded extproc agent contributes to the efficiency of external procedures.

Topics:

The Challenge of Dedicated Agent Architecture

By default, an extproc agent is started for each user session and the extproc agent process terminates only when the user session ends.

This architecture can consume an unnecessarily large amount of system resources. For example, suppose that several thousand user sessions simultaneously spawn extproc agent processes. Because an extproc agent process is started for each session, several thousand extproc agent processes run concurrently. The extproc agent processes operate regardless of whether each individual extproc agent process is active at the moment. Thus extproc agent processes and open connections can consume a disproportionate amount of system resources. When sessions connect to Oracle Database, this problem is addressed by starting the server in shared server mode. Shared server mode allows database connections to be shared by a small number of server processes.

The Advantage of Multithreading

The Oracle Database shared server architecture assumes that even when several thousand user sessions are open, only a small percentage of these connections are active at any given time. In shared server mode, there is a pool of shared server processes. User sessions connect to dispatcher processes that place the requested tasks in a queue. The tasks are picked up by the first available shared server processes. The number of shared server processes is usually less that the number of user sessions.

The multithreaded extproc agent provides similar functionality for connections to external procedures. The multithreaded extproc agent architecture uses a pool of shared agent threads. The tasks requested by the user sessions are put in a queue and are picked up by the first available multithreaded extproc agent thread. Because only a small percentage of user connections are active at a given moment, using a multithreaded extproc architecture allows more efficient use of system resources.

Multithreaded extproc Agent Architecture

One multithreaded extproc agent must be started for each system identifier (SID) before attempting to connect to the external procedure. This is done using the agent control utility agtctl. This utility is also used to configure the agent and to shut down the agent.

Each Oracle Net listener that is running on a system listens for incoming connection requests for a set of SIDs. If the SID in an incoming Oracle Net connect string is an SID for which the listener is listening, then that listener processes the connection. Further, if a multithreaded extproc agent was started for the SID, then the listener passes the request to that extproc agent.

In the architecture for multithreaded extproc agents, each incoming connection request is processed by different kinds of threads:

  • A single monitor thread. The monitor thread is responsible for:

    • Maintaining communication with the listener

    • Monitoring the load on the process

    • Starting and stopping threads when required

  • Several dispatcher threads. The dispatcher threads are responsible for:

    • Handling communication with the Oracle Database

    • Passing task requests to the task threads

  • Several task threads. The task threads handle requests from the Oracle Database processes.

Figure A-1 illustrates the architecture of the multithreaded extproc agent. User sessions 1 and 2 issue requests for callouts to functions in some DLLs. These requests get serviced through heterogeneous services to the multithreaded extproc agent. These requests get handled by the agent's dispatcher threads, which then pass them on to the task threads. The task thread that is actually handling a request is responsible for loading the respective DLL and calling the function therein.

  • All requests from a user session get handled by the same dispatcher thread. For example, dispatcher 1 handles communication with user session 1, and dispatcher 2 handles communication with user session 2. This is the case for the lifetime of the session.

  • The individual requests can be serviced by different task threads. For example, task thread 1 can handle the request from user session 1, and later handle the request from user session 2.


See Also:

Oracle Database Administrator's Guide. for details on managing processes for external procedures

Figure A-1 Multithreaded extproc Agent Architecture

Multithreaded extproc Agent Architecture
Description of "Figure A-1 Multithreaded extproc Agent Architecture"

These three thread types roughly correspond to the Oracle Database multithreaded server PMON, dispatcher, and shared server processes, respectively.


Note:

All requests from a user session go through the same dispatcher thread, but can be serviced by different task threads. Also, several task threads can use the same connection to the external procedure.

These topics explain each type of thread in more detail:

Monitor Thread

When the agent control utility agtctl starts a multithreaded extproc agent for a SID, agtctl creates the monitor thread. The monitor thread performs these functions:

  • Creates the dispatcher and task threads.

  • Registers the dispatcher threads with all the listeners that are handling connections to this extproc agent. While the dispatcher for this SID is running, the listener does not start a process when it gets an incoming connection. Instead, the listener gives the connection to this same dispatcher.

  • Monitors the other threads and sends load information about the dispatcher threads to all the listener processes handling connections to this extproc agent, enabling listeners to give incoming connections to the least loaded dispatcher.

  • Continues to monitor each of the threads it has created.

Dispatcher Threads

Dispatcher threads perform these functions:

  • Accept incoming connections and task requests from Oracle Database servers.

  • Place incoming requests on a queue for a task thread to pick up.

  • Send results of a request back to the server that issued the request.


    Note:

    After a user session establishes a connection with a dispatcher, all requests from that user session go to the same dispatcher until the end of the user session.

Task Threads

Task threads perform these functions:

  • Pick up requests from a queue.

  • Perform the necessary operations.

  • Place the results on a queue for a dispatcher to pick up.

Administering the Multithreaded extproc Agent

One multithreaded extproc agent must be started for each system identifier (SID) before attempting to connect to the external procedure.

A multithreaded extproc agent is started, stopped, and configured by an agent control utility called agtctl, which works like lsnrctl. However, unlike lsnrctl, which reads a configuration file (listener.ora), agtctl takes configuration information from the command line and writes it to a control file.

Before starting agtctl, ensure that Oracle Listener is running. Then use the agtctl commands to set the agtctl configuration parameters (if you do not want their default values) and to start agtctl, as in Example A-1.

Example A-1 Setting Configuration Parameters and Starting agtctl

agtctl set max_dispatchers  2 ep_agt1
agtctl set tcp_dispatchers  1   ep_agt1
agtctl set max_task_threads 2  ep_agt1
agtctl set max_sessions     5 ep_agt1
agtctl unset listener_address ep_agt1
agtctl set listener_address "(address=(protocol=ipc)(key=extproc))" ep_agt1
agtctl startup extproc ep_agt1

You can use agtctl commands in either single-line command mode or shell mode.

Topics:

Agent Control Utility (agtctl) Commands

You can start and stop agtctl and create and maintain its control file by using the commands shown in Table A-1.

Table A-1 Agent Control Utility (agtctl) Commands

CommandDescription

startup

Starts a multithreaded extproc agent

shutdown

Stops a multithreaded extproc agent

set

Sets a configuration parameter for a multithreaded extproc agent

unset

Causes a parameter to revert to its default value

show

Displays the value of a configuration parameter

delete

Deletes the entry for a particular SID from the control file

exit

Exits shell mode

help

Lists available commands


These commands can be issued in one of two ways:

  • You can issue commands from the UNIX or DOS shell. This mode is called single-line command mode.

  • You can enter agtctl and an AGTCTL> prompt appears. You then can enter commands from within the agtctl shell. This mode is called shell mode.

The syntax and parameters for agtctl commands depend on the mode in which they are issued.


Note:

  • All commands are case-sensitive.

  • The agtctl utility puts its control file in the directory specified by either one of two environment variables, AGTCTL_ADMIN or TNS_ADMIN. Ensure that at least one of these environment variables is set and that it specifies a directory to which the agent has access.

  • If the multithreaded extproc agent requires that an environment variable be set, or if the ENVS parameter was used when configuring the listener.ora entry for the agent working in dedicated mode, then all required environment variables must be set in the UNIX or DOS shell that runs the agtctl utility.


Using agtctl in Single-Line Command Mode

This section describes the use of agtctl commands. They are presented in single-line command mode.

Setting Configuration Parameters for a Multithreaded extproc Agent

Set the configuration parameters for a multithreaded extproc agent before you start the agent. If a configuration parameter is not specifically set, a default value is used. Configuration parameters and their default values are shown in Table A-2.

Use the set command to set multithreaded extproc agent configuration parameters.

Syntax

agtctl set parameter parameter_value agent_sid

parameter is the parameter that you are setting.

parameter_value is the value being assigned to that parameter.

agent_sid is the SID that this agent services. This must be specified for single-line command mode.

Example

agtctl set max_dispatchers 5 salesDB

Starting a Multithreaded extproc Agent

Use the startup command to start a multithreaded extproc agent.

Syntax

agtctl startup extproc agent_sid

agent_sid is the SID that this multithreaded extproc agent services. This must be specified for single-line command mode.

Example

agtctl startup extproc salesDB

Shutting Down a Multithreaded extproc Agent

Use the shutdown command to stop a multithreaded extproc agent. There are three forms of shutdown:

  • Normal (default)

    agtctl asks the multithreaded extproc agent to terminate itself gracefully. All sessions complete their current operations and then shut down.

  • Immediate

    agtctl tells the multithreaded extproc agent to terminate immediately. The agent exits immediately regardless of the state of current sessions.

  • Abort

    Without talking to the multithreaded extproc agent, agtctl issues a system call to stop it.

Syntax

agtctl shutdown [immediate|abort] agent_sid

agent_sid is the SID that the multithreaded extproc agent services. It must be specified for single-line command mode.

Example

agtctl shutdown immediate salesDB

Examining the Value of Configuration Parameters

To examine the value of a configuration parameter, use the show command.

Syntax

agtctl show parameter agent_sid

parameter is the parameter that you are examining.

agent_sid is the SID that this multithreaded extproc agent services. This must be specified for single-line command mode.

Example

agtctl show max_dispatchers salesDB

Resetting a Configuration Parameter to Its Default Value

You can reset a configuration parameter to its default value using the unset command.

Syntax

agtctl unset parameter agent_sid

parameter is the parameter that you are resetting (or changing).

agent_sid is the SID that this multithreaded extproc agent services. It must be specified for single-line command mode.

Example

agtctl unset max_dispatchers salesDB

Deleting an Entry for a Specific SID from the Control File

The delete command deletes the entry for the specified SID from the control file.

Syntax

agtctl delete agent_sid

agent_sid is the SID entry to delete.

Example

agtctl delete salesDB

Requesting Help

Use the help command to view a list of available commands for agtctl or to see the syntax for a particular command.

Syntax

agtctl help [command]

command is the name of the command whose syntax you want to view. The default is all agtctl commands.

Example

agtctl help set

Using Shell Mode Commands

In shell mode, start agtctl by entering:

agtctl

This results in the prompt AGTCTL>. Thereafter, because you are issuing commands from within the agtctl shell, you need not prefix the command string with agtctl.

Set the name of the agent SID by entering:

AGTCTL> set agent_sid agent_sid

All subsequent commands are assumed to be for the specified SID until the agent_sid value is changed. Unlike single-line command mode, you do not specify agent_sid in the command string.

You can set the language for error messages as follows:

AGTCTL> set language language

The commands themselves are the same as those for the single-line command mode. To exit shell mode, enter exit.

The following examples use shell mode commands.

Example: Setting a Configuration Parameter

This example sets a value for the shutdown_address configuration parameter.

AGTCTL> set shutdown_address (address=(protocol=ipc)(key=oraDBsalesDB))

Example: Starting a Multithreaded extproc Agent

This example starts a multithreaded extproc agent.

AGTCTL> startup extproc

Configuration Parameters for Multithreaded extproc Agent Control

Table A-2 describes and gives the defaults of the configuration parameters for the agent control utility.

Table A-2 Configuration Parameters for agtctl

ParameterDescriptionDefault Value

max_dispatchers

Maximum number of dispatchers

1

tcp_dispatchers

Number of dispatchers listening on TCP (the rest are using IPC)

0

max_task_threads

Maximum number of task threads

2

max_sessions

Maximum number of sessions for each task thread

5

listener_address

Address on which the listener is listening (needed for registration)

(ADDRESS_LIST=
    (ADDRESS=
        (PROTOCOL=IPC)
        (KEY=PNPKEY))
    (ADDRESS=
        (PROTOCOL=IPC)
        (KEY=listener_sid))
    (ADDRESS=
        (PROTOCOL=TCP)
        (HOST=127.0.0.1)
        (PORT=1521)))

Note: listener_sid is the IPC key of the address, on the Oracle Database, on which the listener is listening.

shutdown_address

Address the agent uses to communicate with the listener. This is the address on which the agent listens for all communication, including shutdown messages from agtctl.

(ADDRESS=
    (PROTOCOL=IPC)
    (KEY=listener_sid || agent_sid))
(ADDRESS=
    (PROTOCOL=TCP)
    (HOST=127.0.0.1)
    (PORT=1521))

Notes:

  • agent_sid is the SID of the multithreaded extproc agent.

  • [

    || indicates that listener_sid and agent_sid are concatenated into one string.


max_dispatchers, tcp_dispatchers, max_task_threads, and max_sessions

To improve performance, you might need to change the values of some or all of the parameters max_dispatchers, tcp_dispatchers, max_task_threads, and max_sessions.

You can calculate the optimum values of max_dispatchers, tcp_dispatchers, max_task_threads with these formulas:

max_dispatchers = CEIL(x/y)
tcp_dispatchers = CEIL(x_tcpip/y)
max_task_threads = CEIL(x/max_sessions)

Where:

  • CEIL is a SQL function that returns the smallest integer greater than or equal to its argument.

  • x is the maximum number of sessions that can be connected to extproc concurrently.

  • y is the maximum number of connections that the system can support for each dispatcher.

  • x_tcpip is the maximum number of sessions that can be connected to extproc concurrently by TCP/IP.

    (x - x_tcpip is the maximum number of sessions that can be connected to extproc concurrently by IPC.)

There is no formula for computing the optimum value of max_sessions, which affects max_task_threads.

You must fine-tune these parameter settings, based on the capability of your hardware, and ensure that the concurrent threads do not exhaust your operating system.

The value of max_dispatchers must be at least 1 (which is the default).

Example

Suppose:

  • The maximum number of sessions that can be connected to extproc concurrently (x) is 650.

  • The maximum number of sessions that can be connected to extproc concurrently by TCP/IP (x_tcpip) is 400.

    (The maximum number of sessions that can be connected to extproc concurrently by IPC is 650-400=250.)

  • The maximum number of connections that the system can support for each dispatcher (y) is 100.

  • The maximum number of sessions for each task thread (max_sessions) is 20.

The optimum values for these parameters are:

max_dispatchers = CEIL(650/100) = CEIL(6.5)  = 7
tcp_dispatchers  = CEIL(400/100) = CEIL(4)    = 4
max_task_threads = CEIL(650/20)  = CEIL(32.5) = 33

That is, optimally:

  • The maximum number of dispatchers is seven.

  • Four of the seven dispatchers are listening on TCP/IP, and the remaining three are listening on IPC.

  • The maximum number of task threads is 33.

listener_address and shutdown_address

The values of the configuration parameters listener_address and shutdown_address are specified with ADDRESS, as shown in both Table A-2 and Example A-1. Within ADDRESS, you can specify the parameter HOST, which can be either an IPv6 or IPv4 address or a host name. If HOST is a host name, then these values of the optional ADDRESS parameter IP are relevant:

IP ValueMeaning
FIRSTListen on the first IP address returned by the DNS resolution of the host name.
V4_ONLYListen only on the IPv4 interfaces in the system.
V6_ONLYListen only on the IPv6 interfaces in the system.

For example, this value of listener_address or shutdown_address restricts it to IPv6 interfaces:

"(ADDRESS=(PROTOCOL=tcp)(HOST=sales)(PORT=1521)(IP=V6_ONLY))"

See Also:

Oracle Database Net Services Administrator's Guide for detailed information about IPv6 support in Oracle Database

PK"*PK|%AOEBPS/adfns_flashback.htm Using Oracle Flashback Technology

12 Using Oracle Flashback Technology

This chapter explains how to use Oracle Flashback Technology in database applications.

Topics:

Overview of Oracle Flashback Technology

Oracle Flashback Technology is a group of Oracle Database features that let you view past states of database objects or to return database objects to a previous state without using point-in-time media recovery.

With flashback features, you can:

  • Perform queries that return past data

  • Perform queries that return metadata that shows a detailed history of changes to the database

  • Recover tables or rows to a previous point in time

  • Automatically track and archive transactional data changes

  • Roll back a transaction and its dependent transactions while the database remains online

Oracle Flashback features use the Automatic Undo Management (AUM) system to obtain metadata and historical data for transactions. They rely on undo data, which are records of the effects of individual transactions. For example, if a user runs an UPDATE statement to change a salary from 1000 to 1100, then Oracle Database stores the value 1000 in the undo data.

Undo data is persistent and survives a database shutdown. By using flashback features, you can use undo data to query past data or recover from logical damage. Besides using it in flashback features, Oracle Database uses undo data to perform these actions:

  • Roll back active transactions

  • Recover terminated transactions by using database or process recovery

  • Provide read consistency for SQL queries

Topics:

For additional general information about flashback features, see Oracle Database Concepts

Application Development Features

In application development, you can use these flashback features to report historical data or undo erroneous changes. (You can also use these features interactively as a database user or administrator.)

Oracle Flashback Query

Use this feature to retrieve data for an earlier time that you specify with the AS OF clause of the SELECT statement. For more information, see "Using Oracle Flashback Query (SELECT AS OF)".

Oracle Flashback Version Query

Use this feature to retrieve metadata and historical data for a specific time interval (for example, to view all the rows of a table that ever existed during a given time interval). Metadata for each row version includes start and end time, type of change operation, and identity of the transaction that created the row version. To create an Oracle Flashback Version Query, use the VERSIONS BETWEEN clause of the SELECT statement. For more information, see "Using Oracle Flashback Version Query".

Oracle Flashback Transaction Query

Use this feature to retrieve metadata and historical data for a given transaction or for all transactions in a given time interval. To perform an Oracle Flashback Transaction Query, select from the static data dictionary view FLASHBACK_TRANSACTION_QUERY. For more information, see "Using Oracle Flashback Transaction Query".

Typically, you use Oracle Flashback Transaction Query with an Oracle Flashback Version Query that provides the transaction IDs for the rows of interest (see "Using Oracle Flashback Transaction Query with Oracle Flashback Version Query").

DBMS_FLASHBACK Package

Use this feature to set the internal Oracle Database clock to an earlier time so that you can examine data that was current at that time, or to roll back a transaction and its dependent transactions while the database remains online (see Flashback Transaction). For more information, see "Using DBMS_FLASHBACK Package".

Flashback Transaction

Use Flashback Transaction to roll back a transaction and its dependent transactions while the database remains online. This recovery operation uses undo data to create and run the corresponding compensating transactions that return the affected data to its original state. (Flashback Transaction is part of DBMS_FLASHBACK package.) For more information, see "Using DBMS_FLASHBACK Package".

Flashback Data Archive (Oracle Total Recall)

Use Flashback Data Archive to automatically track and archive both regular queries and Oracle Flashback Query, ensuring SQL-level access to the versions of database objects without getting a snapshot-too-old error. For more information, see "Using Flashback Data Archive (Oracle Total Recall)".

Database Administration Features

These flashback features are primarily for data recovery. Typically, you use these features only as a database administrator.

This chapter focuses on the "Application Development Features". For more information about the database administration features, see Oracle Database Administrator's Guide and the Oracle Database Backup and Recovery User's Guide.

Oracle Flashback Table

Use this feature to restore a table to its state at a previous point in time. You can restore a table while the database is on line, undoing changes to only the specified table.

Oracle Flashback Drop

Use this feature to recover a dropped table. This feature reverses the effects of a DROP TABLE statement.

Oracle Flashback Database

Use this feature to quickly return the database to an earlier point in time, by undoing all of the changes that have taken place since then. This is fast, because you do not have to restore database backups.

Configuring Your Database for Oracle Flashback Technology

Before you can use flashback features in your application, you or your database administrator must perform the configuration tasks described in these topics:

Configuring Your Database for Automatic Undo Management

To configure your database for Automatic Undo Management (AUM), you or your database administrator must:

  • Create an undo tablespace with enough space to keep the required data for flashback operations.

    The more often users update the data, the more space is required. The database administrator usually calculates the space requirement.

  • Enable AUM, as explained in Oracle Database Administrator's Guide. Set these database initialization parameters:

    • UNDO_MANAGEMENT

    • UNDO_TABLESPACE

    • UNDO_RETENTION

    For a fixed-size undo tablespace, Oracle Database automatically tunes the system to give the undo tablespace the best possible undo retention.

    For an automatically extensible undo tablespace, Oracle Database retains undo data longer than the longest query duration and the low threshold of undo retention specified by the UNDO_RETENTION parameter.


    Note:

    You can query V$UNDOSTAT.TUNED_UNDORETENTION to determine the amount of time for which undo is retained for the current undo tablespace. For more information about V$UNDOSTAT, see Oracle Database Reference.

    Setting UNDO_RETENTION does not guarantee that unexpired undo data is not discarded. If the system needs more space, Oracle Database can overwrite unexpired undo with more recently generated undo data.

  • Specify the RETENTION GUARANTEE clause for the undo tablespace to ensure that unexpired undo data is not discarded.


See Also:

Oracle Database Administrator's Guide for more information about creating an undo tablespace and enabling AUM

Configuring Your Database for Oracle Flashback Transaction Query

To configure your database for the Oracle Flashback Transaction Query feature, you or your database administrator must:

  • Ensure that Oracle Database is running with version 10.0 compatibility.

  • Enable supplemental logging:

    ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;
    

Configuring Your Database for Flashback Transaction

To configure your database for the Flashback Transaction feature, you or your database administrator must:

  • With the database mounted but not open, enable ARCHIVELOG:

    ALTER DATABASE ARCHIVELOG;
    
  • Open at least one archive log:

    ALTER SYSTEM ARCHIVE LOG CURRENT;
    
  • If not done, enable minimal and primary key supplemental logging:

    ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;
    ALTER DATABASE ADD SUPPLEMENTAL LOG DATA (PRIMARY KEY) COLUMNS;
    
  • If you want to track foreign key dependencies, enable foreign key supplemental logging:

    ALTER DATABASE ADD SUPPLEMENTAL LOG DATA (FOREIGN KEY) COLUMNS;
    

Note:

If you have very many foreign key constraints, enabling foreign key supplemental logging might not be worth the performance penalty.

Enabling Oracle Flashback Operations on Specific LOB Columns

To enable flashback operations on specific LOB columns of a table, use the ALTER TABLE statement with the RETENTION option.

Because undo data for LOB columns can be voluminous, you must define which LOB columns to use with flashback operations.


See Also:

Oracle Database SecureFiles and Large Objects Developer's Guide to learn about LOB storage and the RETENTION parameter

Granting Necessary Privileges

You or your database administrator must grant privileges to users, roles, or applications that must use these flashback features. For information about the GRANT statement, see Oracle Database SQL Language Reference.

For Oracle Flashback Query and Oracle Flashback Version Query

To allow access to specific objects during queries, grant FLASHBACK and SELECT privileges on those objects.

To allow queries on all tables, grant the FLASHBACK ANY TABLE privilege.

For Oracle Flashback Transaction Query

Grant the SELECT ANY TRANSACTION privilege.

To allow execution of undo SQL code retrieved by an Oracle Flashback Transaction Query, grant SELECT, UPDATE, DELETE, and INSERT privileges for specific tables.

For DBMS_FLASHBACK Package

To allow access to the features in the DBMS_FLASHBACK package, grant the EXECUTE privilege on DBMS_FLASHBACK.

For Flashback Data Archive (Oracle Total Recall)

To allow a specific user to enable Flashback Data Archive on tables, using a specific Flashback Data Archive, grant the FLASHBACK ARCHIVE object privilege on that Flashback Data Archive to that user. To grant the FLASHBACK ARCHIVE object privilege, you must either be logged on as SYSDBA or have FLASHBACK ARCHIVE ADMINISTER system privilege.

To allow execution of these statements, grant the FLASHBACK ARCHIVE ADMINISTER system privilege:

  • CREATE FLASHBACK ARCHIVE

  • ALTER FLASHBACK ARCHIVE

  • DROP FLASHBACK ARCHIVE

To grant the FLASHBACK ARCHIVE ADMINISTER system privilege, you must be logged on as SYSDBA.

To create a default Flashback Data Archive, using either the CREATE FLASHBACK ARCHIVE or ALTER FLASHBACK ARCHIVE statement, you must be logged on as SYSDBA.

To disable Flashback Data Archive for a table that has been enabled for Flashback Data Archive, you must either be logged on as SYSDBA or have the FLASHBACK ARCHIVE ADMINISTER system privilege.

Using Oracle Flashback Query (SELECT AS OF)

To use Oracle Flashback Query, use a SELECT statement with an AS OF clause. Oracle Flashback Query retrieves data as it existed at an earlier time. The query explicitly references a past time through a time stamp or System Change Number (SCN). It returns committed data that was current at that point in time.

Uses of Oracle Flashback Query include:

  • Recovering lost data or undoing incorrect, committed changes.

    For example, if you mistakenly delete or update rows, and then commit them, you can immediately undo the mistake.

  • Comparing current data with the corresponding data at an earlier time.

    For example, you can run a daily report that shows the change in data from yesterday. You can compare individual rows of table data or find intersections or unions of sets of rows.

  • Checking the state of transactional data at a particular time.

    For example, you can verify the account balance of a certain day.

  • Simplifying application design by removing the need to store some kinds of temporal data.

    Oracle Flashback Query lets you retrieve past data directly from the database.

  • Applying packaged applications, such as report generation tools, to past data.

  • Providing self-service error correction for an application, thereby enabling users to undo and correct their errors.

Topics:

For more information about the SELECT AS OF statement, see Oracle Database SQL Language Reference.

Example of Examining and Restoring Past Data

Suppose that you discover at 12:30 PM that the row for employee Chung was deleted from the employees table, and you know that at 9:30AM the data for Chung was correctly stored in the database. You can use Oracle Flashback Query to examine the contents of the table at 9:30 AM to find out what data was lost. If appropriate, you can restore the lost data.

Example 12-1 retrieves the state of the record for Chung at 9:30AM, April 4, 2004:

Example 12-1 Retrieving a Lost Row with Oracle Flashback Query

SELECT * FROM employees
AS OF TIMESTAMP
TO_TIMESTAMP('2004-04-04 09:30:00', 'YYYY-MM-DD HH:MI:SS')
WHERE last_name = 'Chung';

Example 12-2 restores Chung's information to the employees table:

Example 12-2 Restoring a Lost Row After Oracle Flashback Query

INSERT INTO employees (
  SELECT * FROM employees
  AS OF TIMESTAMP
  TO_TIMESTAMP('2004-04-04 09:30:00', 'YYYY-MM-DD HH:MI:SS')
  WHERE last_name = 'Chung'
);

Guidelines for Oracle Flashback Query

  • You can specify or omit the AS OF clause for each table and specify different times for different tables.


    Note:

    If a table is a Flashback Data Archive and you specify a time for it that is earlier than its creation time, the query returns zero rows for that table, rather than causing an error. (For information about Flashback Data Archives, see "Using Flashback Data Archive (Oracle Total Recall)".)

  • You can use the AS OF clause in queries to perform data definition language (DDL) operations (such as creating and truncating tables) or data manipulation language (DML) statements (such as INSERT and DELETE) in the same session as Oracle Flashback Query.

  • To use the result of Oracle Flashback Query in a DDL or DML statement that affects the current state of the database, use an AS OF clause inside an INSERT or CREATE TABLE AS SELECT statement.

  • If a possible 3-second error (maximum) is important to Oracle Flashback Query in your application, use an SCN instead of a time stamp. See "General Guidelines for Oracle Flashback Technology".

  • You can create a view that refers to past data by using the AS OF clause in the SELECT statement that defines the view.

    If you specify a relative time by subtracting from the current time on the database host, the past time is recalculated for each query. For example:

    CREATE VIEW hour_ago AS
      SELECT * FROM employees
        AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '60' MINUTE);
    

    SYSTIMESTAMP refers to the time zone of the database host environment.

  • You can use the AS OF clause in self-joins, or in set operations such as INTERSECT and MINUS, to extract or compare data from two different times.

    You can store the results by preceding Oracle Flashback Query with a CREATE TABLE AS SELECT or INSERT INTO TABLE SELECT statement. For example, this query reinserts into table employees the rows that existed an hour ago:

    INSERT INTO employees
        (SELECT * FROM employees
         AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '60' MINUTE)
        )
        MINUS SELECT * FROM employees);
    

    SYSTIMESTAMP refers to the time zone of the database host environment.

Using Oracle Flashback Version Query

Use Oracle Flashback Version Query to retrieve the different versions of specific rows that existed during a given time interval. A row version is created whenever a COMMIT statement is executed.

Specify Oracle Flashback Version Query using the VERSIONS BETWEEN clause of the SELECT statement. The syntax is:

VERSIONS {BETWEEN {SCN | TIMESTAMP} start AND end}

where start and end are expressions representing the start and end, respectively, of the time interval to be queried. The time interval includes (start and end).

Oracle Flashback Version Query returns a table with a row for each version of the row that existed at any time during the specified time interval. Each row in the table includes pseudocolumns of metadata about the row version, described in Table 12-1. This information can reveal when and how a particular change (perhaps erroneous) occurred to your database.

Table 12-1 Oracle Flashback Version Query Row Data Pseudocolumns

Pseudocolumn NameDescription

VERSIONS_STARTSCN

VERSIONS_STARTTIME

Starting System Change Number (SCN) or TIMESTAMP when the row version was created. This pseudocolumn identifies the time when the data first had the values reflected in the row version. Use this pseudocolumn to identify the past target time for Oracle Flashback Table or Oracle Flashback Query.

If this pseudocolumn is NULL, then the row version was created before start.

VERSIONS_ENDSCN

VERSIONS_ENDTIME

SCN or TIMESTAMP when the row version expired.

If this pseudocolumn is NULL, then either the row version was current at the time of the query or the row corresponds to a DELETE operation.

VERSIONS_XID

Identifier of the transaction that created the row version.

VERSIONS_OPERATION

Operation performed by the transaction: I for insertion, D for deletion, or U for update. The version is that of the row that was inserted, deleted, or updated; that is, the row after an INSERT operation, the row before a DELETE operation, or the row affected by an UPDATE operation.

For user updates of an index key, Oracle Flashback Version Query might treat an UPDATE operation as two operations, DELETE plus INSERT, represented as two version rows with a D followed by an I VERSIONS_OPERATION.


A given row version is valid starting at its time VERSIONS_START* up to, but not including, its time VERSIONS_END*. That is, it is valid for any time t such that VERSIONS_START* <= t < VERSIONS_END*. For example, this output indicates that the salary was 10243 from September 9, 2002, included, to November 25, 2003, excluded.

VERSIONS_START_TIME     VERSIONS_END_TIME     SALARY
-------------------     -----------------     ------
09-SEP-2003             25-NOV-2003           10243

Here is a typical use of Oracle Flashback Version Query:

SELECT versions_startscn, versions_starttime,
       versions_endscn, versions_endtime,
       versions_xid, versions_operation,
       last_name, salary
  FROM employees
  VERSIONS BETWEEN TIMESTAMP
      TO_TIMESTAMP('2008-12-18 14:00:00', 'YYYY-MM-DD HH24:MI:SS')
  AND TO_TIMESTAMP('2008-12-18 17:00:00', 'YYYY-MM-DD HH24:MI:SS')
  WHERE first_name = 'John';

You can use VERSIONS_XID with Oracle Flashback Transaction Query to locate this transaction's metadata, including the SQL required to undo the row change and the user responsible for the change—see "Using Oracle Flashback Transaction Query".


See Also:

Oracle Database SQL Language Reference for information about Oracle Flashback Version Query pseudocolumns and the syntax of the VERSIONS clause

Using Oracle Flashback Transaction Query

Use Oracle Flashback Transaction Query to retrieve metadata and historical data for a given transaction or for all transactions in a given time interval. Oracle Flashback Transaction Query queries the static data dictionary view FLASHBACK_TRANSACTION_QUERY, whose columns are described in Oracle Database Reference.

The column UNDO_SQL shows the SQL code that is the is the logical opposite of the DML operation performed by the transaction. You can usually use this code to reverse the logical steps taken during the transaction. However, there are cases where the SQL_UNDO code is not the exact opposite of the original transaction. For example, a SQL_UNDO INSERT operation might not insert a row back in a table at the same ROWID from which it was deleted.

This statement queries the FLASHBACK_TRANSACTION_QUERY view for transaction information, including the transaction ID, the operation, the operation start and end SCNs, the user responsible for the operation, and the SQL code that shows the logical opposite of the operation:

SELECT xid, operation, start_scn, commit_scn, logon_user, undo_sql
FROM flashback_transaction_query
WHERE xid = HEXTORAW('000200030000002D');

This statement uses Oracle Flashback Version Query as a subquery to associate each row version with the LOGON_USER responsible for the row data change:

SELECT xid, logon_user
FROM flashback_transaction_query
WHERE xid IN (
  SELECT versions_xid FROM employees VERSIONS BETWEEN TIMESTAMP
  TO_TIMESTAMP('2003-07-18 14:00:00', 'YYYY-MM-DD HH24:MI:SS') AND
  TO_TIMESTAMP('2003-07-18 17:00:00', 'YYYY-MM-DD HH24:MI:SS')
);

Note:

If you query FLASHBACK_TRANSACTION_QUERY without specifying XID in the WHERE clause, the query scans many unrelated rows, degrading performance.


See Also:


Using Oracle Flashback Transaction Query with Oracle Flashback Version Query

In this example, a database administrator does this:

DROP TABLE emp;
CREATE TABLE emp (
  empno   NUMBER PRIMARY KEY,
  empname VARCHAR2(16),
  salary  NUMBER
);
INSERT INTO emp (empno, empname, salary) VALUES (111, 'Mike', 555);
COMMIT;

DROP TABLE dept;
CREATE TABLE dept (
  deptno   NUMBER,
  deptname VARCHAR2(32)
);
INSERT INTO dept (deptno, deptname) VALUES (10, 'Accounting');
COMMIT;

Now emp and dept have one row each. In terms of row versions, each table has one version of one row. Suppose that an erroneous transaction deletes empno 111 from table emp:

UPDATE emp SET salary = salary + 100 WHERE empno = 111;
INSERT INTO dept (deptno, deptname) VALUES (20, 'Finance');
DELETE FROM emp WHERE empno = 111;
COMMIT;

Next, a transaction reinserts empno 111 into the emp table with a new employee name:

INSERT INTO emp (empno, empname, salary) VALUES (111, 'Tom', 777);
UPDATE emp SET salary = salary + 100 WHERE empno = 111;
UPDATE emp SET salary = salary + 50 WHERE empno = 111;
COMMIT;

The database administrator detects the application error and must diagnose the problem. The database administrator issues this query to retrieve versions of the rows in the emp table that correspond to empno 111. The query uses Oracle Flashback Version Query pseudocolumns:

SELECT versions_xid XID, versions_startscn START_SCN,
  versions_endscn END_SCN, versions_operation OPERATION,
  empname, salary
FROM emp
VERSIONS BETWEEN SCN MINVALUE AND MAXVALUE
WHERE empno = 111;

Results are similar to:

XID               START_SCN    END_SCN O EMPNAME              SALARY
---------------- ---------- ---------- - ---------------- ----------
09001100B2200000   10093466            I Tom                     927
030002002B210000   10093459            D Mike                    555
0800120096200000   10093375   10093459 I Mike                    555
 
3 rows selected.

The results table rows are in descending chronological order. The third row corresponds to the version of the row in the table emp that was inserted in the table when the table was created. The second row corresponds to the row in emp that the erroneous transaction deleted. The first row corresponds to the version of the row in emp that was reinserted with a new employee name.

The database administrator identifies transaction 030002002B210000 as the erroneous transaction and uses Oracle Flashback Transaction Query to audit all changes made by this transaction:

SELECT  xid, start_scn, commit_scn, operation, logon_user, undo_sql
FROM flashback_transaction_query
WHERE xid = HEXTORAW('000200030000002D');

Results are similar to:

XID               START_SCN COMMIT_SCN OPERATION LOGON_USER
---------------- ---------- ---------- --------- ------------------------------
UNDO_SQL
--------------------------------------------------------------------------------
 
030002002B210000   10093452   10093459 DELETE    HR
insert into "HR"."EMP"("EMPNO","EMPNAME","SALARY") values ('111','Mike','655');
 
030002002B210000   10093452   10093459 INSERT    HR
delete from "HR"."DEPT" where ROWID = 'AAATjuAAEAAAAJrAAB';
 
030002002B210000   10093452   10093459 UPDATE    HR
update "HR"."EMP" set "SALARY" = '555' where ROWID = 'AAATjsAAEAAAAJ7AAA';
 
030002002B210000   10093452   10093459 BEGIN     HR
 
 
4 rows selected.

To make the result of the next query easier to read, the database administrator uses these SQL*Plus commands:

COLUMN operation FORMAT A9
COLUMN table_name FORMAT A10
COLUMN table_owner FORMAT A11

To see the details of the erroneous transaction and all subsequent transactions, the database administrator performs this query:

SELECT xid, start_scn, commit_scn, operation, table_name, table_owner
FROM flashback_transaction_query
WHERE table_owner = 'HR'
AND start_timestamp >=
  TO_TIMESTAMP ('2002-04-16 11:00:00','YYYY-MM-DD HH:MI:SS');

Results are similar to:

XID               START_SCN COMMIT_SCN OPERATION TABLE_NAME TABLE_OWNER
---------------- ---------- ---------- --------- ---------- -----------
02000E0074200000   10093435   10093446 INSERT    DEPT       HR
030002002B210000   10093452   10093459 DELETE    EMP        HR
030002002B210000   10093452   10093459 INSERT    DEPT       HR
030002002B210000   10093452   10093459 UPDATE    EMP        HR
0800120096200000   10093374   10093375 INSERT    EMP        HR
09001100B2200000   10093462   10093466 UPDATE    EMP        HR
09001100B2200000   10093462   10093466 UPDATE    EMP        HR
09001100B2200000   10093462   10093466 INSERT    EMP        HR
 
8 rows selected.

Note:

Because the preceding query does not specify the XID in the WHERE clause, it scans many unrelated rows, degrading performance.

Using ORA_ROWSCN

ORA_ROWSCN is a pseudocolumn of any table that is not fixed or external. It represents the SCN of the most recent change to a given row in the current session; that is, the most recent COMMIT operation for the row in the current session. For example:

SELECT ora_rowscn, last_name, salary
FROM employees
WHERE employee_id = 200;

Result is similar to:

ORA_ROWSCN LAST_NAME                     SALARY
---------- ------------------------- ----------
    884320 Whalen                          2800

The most recent COMMIT operation for the row in the current session took place at approximately SCN 9371092. To convert an SCN to the corresponding TIMESTAMP value, use the function SCN_TO_TIMESTAMP (documented in Oracle Database SQL Language Reference).

ORA_ROWSCN is a conservative upper bound of the latest commit time—the actual commit SCN can be somewhat earlier. ORA_ROWSCN is more precise (closer to the actual commit SCN) for a row-dependent table (created using CREATE TABLE with the ROWDEPENDENCIES clause). For more information about ORA_ROWSCN and ROWDEPENDENCIES, see Oracle Database SQL Language Reference.


Note:

ORA_ROWSCN is not supported for Flashback Query. Instead, use the version query pseudocolumns, which are provided explicitly for Flashback Query. For information about these pseudocolumns, see Oracle Database SQL Language Reference.

Topics:

Scenario: Package Subprogram Might Change Row

Your application examines a row of data and records the corresponding ORA_ROWSCN as 202553. Then, your application invokes a package subprogram, whose implementation details you cannot see, which might or might not change the same row (and commit the change). Later, your application must update the row only if the package subprogram did not change it. Make the operation conditional—update the row only if ORA_ROWSCN is still 202553, as in this equivalent interactive statement:

UPDATE employees
SET salary = salary + 100
WHERE employee_id = 200
AND ora_rowscn = 202553;

If the package subprogram changed the row, then ORA_ROWSCN is no longer 9371092, and the update fails.

Your application queries again to obtain the new row data and ORA_ROWSCN. Suppose that the ORA_ROWSCN is now 415639. The application tries the conditional update again, using the new ORA_ROWSCN. This time, the update succeeds, and it is committed. An interactive equivalent is:

UPDATE employees
SET salary = salary + 100
WHERE employee_id = 7788
AND ora_rowscn = 415639;

ORA_ROWSCN and Tables with Virtual Private Database (VPD)

When a VPD policy is added to a table, it is no longer possible to select the ORA_ROWSCN pseudocolumn. However, because ORA_ROWSCN is available inside the policy function, you can:

  1. Create a function that returns a row SCN, as in Example 12-3.

  2. In the policy predicate function, add a predicate that stores the row SCN in the context that the function uses while processing rows. For example:

    ||' AND f_ora_rowscn('||object_name||'.ora_rowscn) = 1'
    
  3. Use the function to fetch the row. For example:

    SELECT t.*, get_rowscn(t.rowid) "ORA_ROWSCN" FROM test_table t;
    

Note:

To run Example 12-3, you need CREATE ANY CONTEXT system privilege.

Example 12-3 Function that Can Return Row SCN from Table that has VPD

-- Create context that function uses while processing rows:
 
CREATE OR REPLACE FUNCTION f_ora_rowscn
 (rowscn IN NUMBER)
 RETURN NUMBER
AS
BEGIN
  DBMS_SESSION.SET_CONTEXT('STORE_ROWSCN','ROWSCN',rowscn);
  RETURN 1;
END;
/
 
CREATE CONTEXT store_rowscn USING f_ora_rowscn;
 
-- Create function that returns row SCN for each row:
 
CREATE OR REPLACE FUNCTION get_rowscn
  (r IN ROWID)
  RETURN VARCHAR2
AS
BEGIN 
  RETURN sys_context('STORE_ROWSCN','ROWSCN');
END;
/

Using DBMS_FLASHBACK Package

The DBMS_FLASHBACK package provides the same functionality as Oracle Flashback Query, but Oracle Flashback Query is sometimes more convenient.

The DBMS_FLASHBACK package acts as a time machine: you can turn back the clock, perform normal queries as if you were at that earlier time, and then return to the present. Because you can use the DBMS_FLASHBACK package to perform queries on past data without special clauses such as AS OF or VERSIONS BETWEEN, you can reuse existing PL/SQL code to query the database at earlier times.

You must have the EXECUTE privilege on the DBMS_FLASHBACK package.

To use the DBMS_FLASHBACK package in your PL/SQL code:

  1. Specify a past time by invoking either DBMS_FLASHBACK.ENABLE_AT_TIME or DBMS_FLASHBACK.ENABLE_AT_SYSTEM_CHANGE_NUMBER.

  2. Perform regular queries (that is, queries without special flashback-feature syntax such as AS OF). Do not perform DDL or DML operations.

    The database is queried at the specified past time.

  3. Return to the present by invoking DBMS_FLASHBACK.DISABLE.

    You must invoke DBMS_FLASHBACK.DISABLE before invoking DBMS_FLASHBACK.ENABLE_AT_TIME or DBMS_FLASHBACK.ENABLE_AT_SYSTEM_CHANGE_NUMBER again. You cannot nest enable/disable pairs.

To use a cursor to store the results of queries, open the cursor before invoking DBMS_FLASHBACK.DISABLE. After storing the results and invoking DBMS_FLASHBACK.DISABLE, you can:

  • Perform INSERT or UPDATE operations to modify the current database state by using the stored results from the past.

  • Compare current data with the past data. After invoking DBMS_FLASHBACK.DISABLE, open a second cursor. Fetch from the first cursor to retrieve past data; fetch from the second cursor to retrieve current data. You can store the past data in a temporary table and then use set operators such as MINUS or UNION to contrast or combine the past and current data.

You can invoke DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER at any time to get the current System Change Number (SCN). DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER always returns the current SCN regardless of previous invocations of DBMS_FLASHBACK.ENABLE.


See Also:


Using Flashback Transaction

The DBMS_FLASHBACK.TRANSACTION_BACKOUT procedure rolls back a transaction and its dependent transactions while the database remains online. This recovery operation uses undo data to create and run the compensating transactions that return the affected data to its original state.

The transactions being rolled back are subject to these restrictions:

  • They cannot have performed DDL operations that changed the logical structure of database tables.

  • They cannot use Large Object (LOB) Data Types:

    • BFILE

    • BLOB

    • CLOB

    • NCLOB

  • They cannot use features that LogMiner does not support.

    The features that LogMiner supports depends on the value of the COMPATIBLE initialization parameter for the database that is rolling back the transaction. The default value is the release number of the most recent major release.

    Flashback Transaction inherits SQL data type support from LogMiner. Therefore, if LogMiner fails due to an unsupported SQL data type in a the transaction, Flashback Transaction fails too.

    Some data types, though supported by LogMiner, do not generate undo information as part of operations that modify columns of such types. Therefore, Flashback Transaction does not support tables containing these data types. These include tables with BLOB, CLOB and XML type.


See Also:


Topics:

Dependent Transactions

In the context of Flashback Transaction, transaction 2 can depend on transaction 1 in any of these ways:

  • Write-after-write dependency

    Transaction 1 changes a row of a table, and later transaction 2 changes the same row.

  • Primary key dependency

    A table has a primary key constraint on column c. In a row of the table, column c has the value v. Transaction 1 deletes that row, and later transaction 2 inserts a row into the same table, assigning the value v to column c.

  • Foreign key dependency

    In table b, column b1 has a foreign key constraint on column a1 of table a. Transaction 1 changes a value in a1, and later transaction 2 changes a value in b1.

TRANSACTION_BACKOUT Parameters

The parameters of the TRANSACTION_BACKOUT procedure are:

  • Number of transactions to be backed out

  • List of transactions to be backed out, identified either by name or by XID

  • Time hint, if you identify transactions by name

    Specify a time that is earlier than any transaction started.

  • Backout option from Table 12-2

For the syntax of the TRANSACTION_BACKOUT procedure and detailed parameter descriptions, see Oracle Database PL/SQL Packages and Types Reference.

Table 12-2 Flashback TRANSACTION_BACKOUT Options

OptionDescription

CASCADE

Backs out specified transactions and all dependent transactions in a post-order fashion (that is, children are backed out before parents are backed out).

Without CASCADE, if any dependent transaction is not specified, an error occurs.

NOCASCADE

Default. Backs out specified transactions, which are expected to have no dependent transactions. First dependent transactions causes an error and appears in *_FLASHBACK_TRANSACTION_REPORT.

NOCASCADE_FORCE

Backs out specified transactions, ignoring dependent transactions. Server runs undo SQL statements for specified transactions in reverse order of commit times.

If no constraints break and you are satisfied with the result, you can commit the changes; otherwise, you can roll them back.

NONCONFLICT_ONLY

Backs out changes to nonconflicting rows of the specified transactions. Database remains consistent, but transaction atomicity is lost.


TRANSACTION_BACKOUT analyzes the transactional dependencies, performs DML operations, and generates reports. TRANSACTION_BACKOUT does not commit the DML operations that it performs as part of transaction backout, but it holds all the required locks on rows and tables in the right form, preventing other dependencies from entering the system. To make the transaction backout permanent, you must explicitly commit the transaction.

TRANSACTION_BACKOUT Reports

To see the reports that TRANSACTION_BACKOUT generates, query the static data dictionary views *_FLASHBACK_TXN_STATE and *_FLASHBACK_TXN_REPORT.

*_FLASHBACK_TXN_STATE

The static data dictionary view *_FLASHBACK_TXN_STATE shows whether a transaction is active or backed out. If a transaction appears in this view, it is backed out.

*_FLASHBACK_TXN_STATE is maintained atomically for compensating transactions. If a compensating transaction is backed out, all changes that it made are also backed out, and *_FLASHBACK_TXN_STATE reflects this. For example, if compensating transaction ct backs out transactions t1 and t2, then t1 and t2 appear in *_FLASHBACK_TXN_STATE. If ct itself is later backed out, the effects of t1 and t2 are reinstated, and t1 and t2 disappear from *_FLASHBACK_TXN_STATE.


See Also:

Oracle Database Reference for more information about *_FLASHBACK_TXN_STATE

*_FLASHBACK_TXN_REPORT

The static data dictionary view *_FLASHBACK_TXN_REPORT provides a detailed report for each backed-out transaction.


See Also:

Oracle Database Reference for more information about *_FLASHBACK_TXN_REPORT

Using Flashback Data Archive (Oracle Total Recall)

A Flashback Data Archive provides the ability to track and store transactional changes to a table over its lifetime. A Flashback Data Archive is useful for compliance with record stage policies and audit reports.

A Flashback Data Archive consists of one or more tablespaces or parts thereof. You can have multiple Flashback Data Archives. If you are logged on as SYSDBA, you can specify a default Flashback Data Archive for the system. A Flashback Data Archive is configured with retention time. Data archived in the Flashback Data Archive is retained for the retention time.

By default, flashback archiving is off for any table. You can enable flashback archiving for a table if all of these conditions are true:

  • You have the FLASHBACK ARCHIVE object privilege on the Flashback Data Archive to use for that table.

  • The table is neither nested, clustered, temporary, remote, or external.

  • The table contains neither LONG nor nested columns.

After flashback archiving is enabled for a table, you can disable it only if you either have the FLASHBACK ARCHIVE ADMINISTER system privilege or you are logged on as SYSDBA.

When choosing a Flashback Data Archive for a specific table, consider the data retention requirements for the table and the retention times of the Flashback Data Archives on which you have the FLASHBACK ARCHIVE object privilege.

Topics:


See Also:

http://www.oracle.com/database/total-recall.html for more information about Oracle Total Recall

Creating a Flashback Data Archive

Create a Flashback Data Archive with the CREATE FLASHBACK ARCHIVE statement, specifying:

  • Name of the Flashback Data Archive

  • Name of the first tablespace of the Flashback Data Archive

  • (Optional) Maximum amount of space that the Flashback Data Archive can use in the first tablespace

    The default is unlimited. Unless your space quota on the first tablespace is also unlimited, you must specify this value; otherwise, error ORA-55621 occurs.

  • Retention time (number of days that Flashback Data Archive data for the table is guaranteed to be stored)

If you are logged on as SYSDBA, you can also specify that this is the default Flashback Data Archive for the system. If you omit this option, you can still make this Flashback Data Archive the default later (see "Specifying the Default Flashback Data Archive").

Examples

  • Create a default Flashback Data Archive named fla1 that uses up to 10 G of tablespace tbs1, whose data are retained for one year:

    CREATE FLASHBACK ARCHIVE DEFAULT fla1 TABLESPACE tbs1
      QUOTA 10G RETENTION 1 YEAR;
    
  • Create a Flashback Data Archive named fla2 that uses tablespace tbs2, whose data are retained for two years:

    CREATE FLASHBACK ARCHIVE fla2 TABLESPACE tbs2 RETENTION 2 YEAR;
    

For more information about the CREATE FLASHBACK ARCHIVE statement, see Oracle Database SQL Language Reference.

Altering a Flashback Data Archive

With the ALTER FLASHBACK ARCHIVE statement, you can:

  • Change the retention time of a Flashback Data Archive

  • Purge some or all of its data

  • Add, modify, and remove tablespaces


    Note:

    Removing all tablespaces of a Flashback Data Archive causes an error.

If you are logged on as SYSDBA, you can also use the ALTER FLASHBACK ARCHIVE statement to make a specific file the default Flashback Data Archive for the system.

Examples

  • Make Flashback Data Archive fla1 the default Flashback Data Archive:

    ALTER FLASHBACK ARCHIVE fla1 SET DEFAULT;
    
  • To Flashback Data Archive fla1, add up to 5 G of tablespace tbs3:

    ALTER FLASHBACK ARCHIVE fla1 ADD TABLESPACE tbs3 QUOTA 5G;
    
  • To Flashback Data Archive fla1, add as much of tablespace tbs4 as needed:

    ALTER FLASHBACK ARCHIVE fla1 ADD TABLESPACE tbs4;
    
  • Change the maximum space that Flashback Data Archive fla1 can use in tablespace tbs3 to 20 G:

    ALTER FLASHBACK ARCHIVE fla1 MODIFY TABLESPACE tbs3 QUOTA 20G;
    
  • Allow Flashback Data Archive fla1 to use as much of tablespace tbs1 as needed:

    ALTER FLASHBACK ARCHIVE fla1 MODIFY TABLESPACE tbs1;
    
  • Change the retention time for Flashback Data Archive fla1 to two years:

    ALTER FLASHBACK ARCHIVE fla1 MODIFY RETENTION 2 YEAR;
    
  • Remove tablespace tbs2 from Flashback Data Archive fla1:

    ALTER FLASHBACK ARCHIVE fla1 REMOVE TABLESPACE tbs2;
    

    (Tablespace tbs2 is not dropped.)

  • Purge all historical data from Flashback Data Archive fla1:

    ALTER FLASHBACK ARCHIVE fla1 PURGE ALL;
    
  • Purge all historical data older than one day from Flashback Data Archive fla1:

    ALTER FLASHBACK ARCHIVE fla1
      PURGE BEFORE TIMESTAMP (SYSTIMESTAMP - INTERVAL '1' DAY);
    
  • Purge all historical data older than SCN 728969 from Flashback Data Archive fla1:

    ALTER FLASHBACK ARCHIVE fla1 PURGE BEFORE SCN 728969;
    

For more information about the ALTER FLASHBACK ARCHIVE statement, see Oracle Database SQL Language Reference.

Dropping a Flashback Data Archive

Drop a Flashback Data Archive with the DROP FLASHBACK ARCHIVE statement. Dropping a Flashback Data Archive deletes its historical data, but does not drop its tablespaces.

Example

Remove Flashback Data Archive fla1 and all its historical data, but not its tablespaces:

DROP FLASHBACK ARCHIVE fla1;

For more information about the DROP FLASHBACK ARCHIVE statement, see Oracle Database SQL Language Reference.

Specifying the Default Flashback Data Archive

By default, the system has no default Flashback Data Archive. If you are logged on as SYSDBA, you can specify default Flashback Data Archive in either of these ways:

  • Specify the name of an existing Flashback Data Archive in the SET DEFAULT clause of the ALTER FLASHBACK ARCHIVE statement. For example:

    ALTER FLASHBACK ARCHIVE fla1 SET DEFAULT;
    

    If fla1 does not exist, an error occurs.

  • Include DEFAULT in the CREATE FLASHBACK ARCHIVE statement when you create a Flashback Data Archive. For example:

    CREATE FLASHBACK ARCHIVE DEFAULT fla2 TABLESPACE tbs1
      QUOTA 10G RETENTION 1 YEAR;
    

The default Flashback Data Archive for the system is the default Flashback Data Archive for every user who does not have his or her own default Flashback Data Archive.


See Also:


Enabling and Disabling Flashback Data Archive

By default, flashback archiving is disabled for any table. You can enable flashback archiving for a table if you have the FLASHBACK ARCHIVE object privilege on the Flashback Data Archive to use for that table.

To enable flashback archiving for a table, include the FLASHBACK ARCHIVE clause in either the CREATE TABLE or ALTER TABLE statement. In the FLASHBACK ARCHIVE clause, you can specify the Flashback Data Archive where the historical data for the table are stored. The default is the default Flashback Data Archive for the system. If you specify a nonexistent Flashback Data Archive, an error occurs.

If you enable flashback archiving for a table, but AUM is disabled, error ORA-55614 occurs when you try to modify the table.

If a table has flashback archiving enabled, and you try to enable it again with a different Flashback Data Archive, an error occurs.

After flashback archiving is enabled for a table, you can disable it only if you either have the FLASHBACK ARCHIVE ADMINISTER system privilege or you are logged on as SYSDBA. To disable flashback archiving for a table, specify NO FLASHBACK ARCHIVE in the ALTER TABLE statement. (It is unnecessary to specify NO FLASHBACK ARCHIVE in the CREATE TABLE statement, because that is the default.)


See Also:

Oracle Database SQL Language Reference for more information about the FLASHBACK ARCHIVE clause of the CREATE TABLE statement, including restrictions on its use

Examples

  • Create table employee and store the historical data in the default Flashback Data Archive:

    CREATE TABLE employee (EMPNO NUMBER(4) NOT NULL, ENAME VARCHAR2(10),
      JOB VARCHAR2(9), MGR NUMBER(4)) FLASHBACK ARCHIVE;
    
  • Create table employee and store the historical data in the Flashback Data Archive fla1:

    CREATE TABLE employee (EMPNO NUMBER(4) NOT NULL, ENAME VARCHAR2(10),
      JOB VARCHAR2(9), MGR NUMBER(4)) FLASHBACK ARCHIVE fla1;
    
  • Enable flashback archiving for the table employee and store the historical data in the default Flashback Data Archive:

    ALTER TABLE employee FLASHBACK ARCHIVE;
    
  • Enable flashback archiving for the table employee and store the historical data in the Flashback Data Archive fla1:

    ALTER TABLE employee FLASHBACK ARCHIVE fla1;
    
  • Disable flashback archiving for the table employee:

    ALTER TABLE employee NO FLASHBACK ARCHIVE;
    

DDL Statements on Tables Enabled for Flashback Data Archive

Flashback Data Archive supports many DDL statements, including some that alter the table definition or move data. For example:

  • ALTER TABLE statement that does any of the following:

    • Adds, drops, renames, or modifies a column

    • Adds, drops, or renames a constraint

    • Drops or truncates a partition or subpartition operation

  • TRUNCATE TABLE statement

  • RENAME statement that renames a table

Some DDL statements cause error ORA-55610 when used on a table enabled for Flashback Data Archive. For example:

  • ALTER TABLE statement that includes an UPGRADE TABLE clause, with or without an INCLUDING DATA clause

  • ALTER TABLE statement that moves or exchanges a partition or subpartition operation

  • DROP TABLE statement

If you must use unsupported DDL statements on a table enabled for Flashback Data Archive, use the DBMS_FLASHBACK_ARCHIVE.DISASSOCIATE_FBA procedure to disassociate the base table from its Flashback Data Archive. To reassociate the Flashback Data Archive with the base table afterward, use the DBMS_FLASHBACK_ARCHIVE.REASSOCIATE_FBA procedure.


See Also:


Viewing Flashback Data Archive Data

Table 12-3 lists and briefly describes the static data dictionary views that you can query for information about Flashback Data Archive files.

Table 12-3 Static Data Dictionary Views for Flashback Data Archive Files

ViewDescription

*_FLASHBACK_ARCHIVE

Displays information about Flashback Data Archive files.

*_FLASHBACK_ARCHIVE_TS

Displays tablespaces of Flashback Data Archive files.

*_FLASHBACK_ARCHIVE_TABLES

Displays information about tables that are enabled for Data Flashback Archive files.



See Also:


Flashback Data Archive Scenarios

Scenario: Using Flashback Data Archive to Enforce Digital Shredding

Your company wants to "shred" (delete) historical data changes to the Taxes table after ten years. When you create the Flashback Data Archive for Taxes, you specify a retention time of ten years:

CREATE FLASHBACK ARCHIVE taxes_archive TABLESPACE tbs1 RETENTION 10 YEAR;

When history data from transactions on Taxes exceeds the age of ten years, it is purged. (The Taxes table itself, and history data from transactions less than ten years old, are not purged.)

Scenario: Using Flashback Data Archive to Access Historical Data

You want to be able to retrieve the inventory of all items at the beginning of the year from the table inventory, and to be able to retrieve the stock price for each symbol in your portfolio at the close of business on any specified day of the year from the table stock_data.

Create a default Flashback Data Archive named fla1 that uses up to 10 G of tablespace tbs1, whose data are retained for five years (you must be logged on as SYSDBA):

CREATE FLASHBACK ARCHIVE DEFAULT fla1 TABLESPACE tbs1
  QUOTA 10G RETENTION 5 YEAR;

Enable Flashback Data Archive for the tables inventory and stock_data, and store the historical data in the default Flashback Data Archive:

ALTER TABLE inventory FLASHBACK ARCHIVE;
ALTER TABLE stock_data FLASHBACK ARCHIVE;

To retrieve the inventory of all items at the beginning of the year 2007, use this query:

SELECT product_number, product_name, count FROM inventory AS OF
  TIMESTAMP TO_TIMESTAMP ('2007-01-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS');

To retrieve the stock price for each symbol in your portfolio at the close of business on July 23, 2007, use this query:

SELECT symbol, stock_price FROM stock_data AS OF
  TIMESTAMP TO_TIMESTAMP ('2007-07-23 16:00:00', 'YYYY-MM-DD HH24:MI:SS')
  WHERE symbol IN my_portfolio;

Scenario: Using Flashback Data Archive to Generate Reports

You want users to be able to generate reports from the table investments, for data stored in the past five years.

Create a default Flashback Data Archive named fla2 that uses up to 20 G of tablespace tbs1, whose data are retained for five years (you must be logged on as SYSDBA):

CREATE FLASHBACK ARCHIVE DEFAULT fla2 TABLESPACE tbs1
  QUOTA 20G RETENTION 5 YEAR;

Enable Flashback Data Archive for the table investments, and store the historical data in the default Flashback Data Archive:

ALTER TABLE investments FLASHBACK ARCHIVE;

Lisa wants a report on the performance of her investments at the close of business on December 31, 2006. She uses this query:

SELECT * FROM investments AS OF
  TIMESTAMP TO_TIMESTAMP ('2006-12-31 16:00:00', 'YYYY-MM-DD HH24:MI:SS')
  WHERE name = 'LISA';

Scenario: Using Flashback Data Archive for Auditing

A medical insurance company must audit a medical clinic. The medical insurance company has its claims in the table Billings, and creates a default Flashback Data Archive named fla4 that uses up to 100 G of tablespace tbs1, whose data are retained for 10 years:

CREATE FLASHBACK ARCHIVE DEFAULT fla4 TABLESPACE tbs1
  QUOTA 100G RETENTION 10 YEAR;

The company enables Flashback Data Archive for the table Billings, and stores the historical data in the default Flashback Data Archive:

ALTER TABLE Billings FLASHBACK ARCHIVE;

On May 1, 2007, clients were charged the wrong amounts for some diagnoses and tests. To see the records as of May 1, 2007, the company uses this query:

SELECT date_billed, amount_billed, patient_name, claim_Id,
  test_costs, diagnosis FROM Billings AS OF TIMESTAMP
  TO_TIMESTAMP('2007-05-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS');

Scenario: Using Flashback Data Archive to Recover Data

An end user recovers from erroneous transactions that were previously committed in the database. The undo data for the erroneous transactions is no longer available, but because the required historical information is available in the Flashback Data Archive, Flashback Query works seamlessly.

Lisa manages a software development group whose product sales are doing well. On November 3, 2007, she decides to give all her level-three employees who have more than two years of experience a salary increase of 10% and a promotion to level four. Lisa asks her HR representative, Bob, to make the changes.

Using the HR web application, Bob updates the employee table to give Lisa's level-three employees a 10% raise and a promotion to level four. Then Bob finishes his work for the day and leaves for home, unaware that he omitted the requirement of two years of experience in his transaction. A few days later, Lisa checks to see if Bob has done the updates and finds that everyone in the group was given a raise! She calls Bob immediately and asks him to correct the error.

At first, Bob thinks he cannot return the employee table to its prior state without going to the backups. Then he remembers that the employee table has Flashback Data Archive enabled.

First, he verifies that no other transaction modified the employee table after his: The commit time stamp from the transaction query corresponds to Bob's transaction, two days ago.

Next, Bob uses these statements to return the employee table to the way it was before his erroneous change:

DELETE EMPLOYEE WHERE MANAGER = 'LISA JOHNSON';
INSERT INTO EMPLOYEE
  SELECT * FROM EMPLOYEE
    AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '2' DAY)
      WHERE MANAGER = 'LISA JOHNSON';

Bob then reexecutes the update that Lisa had requested.

General Guidelines for Oracle Flashback Technology

  • Use the DBMS_FLASHBACK.ENABLE and DBMS_FLASHBACK.DISABLE procedures around SQL code that you do not control, or when you want to use the same past time for several consecutive queries.

  • Use Oracle Flashback Query, Oracle Flashback Version Query, or Oracle Flashback Transaction Query for SQL code that you write, for convenience. An Oracle Flashback Query, for example, is flexible enough to do comparisons and store results in a single query.

  • To obtain an SCN to use later with a flashback feature, use DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER.

  • To compute or retrieve a past time to use in a query, use a function return value as a time stamp or SCN argument. For example, add or subtract an INTERVAL value to the value of the SYSTIMESTAMP function.

  • Use Oracle Flashback Query, Oracle Flashback Version Query, and Oracle Flashback Transaction Query locally or remotely. An example of a remote Oracle Flashback Query is:

    (SELECT * FROM employees@some_remote_host AS OF 
        TIMESTAMP (SYSTIMESTAMP - INTERVAL '60' MINUTE);
    
  • To ensure database consistency, always perform a COMMIT or ROLLBACK operation before querying past data.

  • Remember that all flashback processing uses the current session settings, such as national language and character set, not the settings that were in effect at the time being queried.

  • Remember that DDLs that alter the structure of a table (such as drop/modify column, move table, drop partition, truncate table/partition, and add constraint) invalidate any existing undo data for the table. If you try to retrieve data from a time before such a DDL executed, error ORA-01466 occurs. DDL operations that alter the storage attributes of a table (such as PCTFREE, INITRANS, and MAXTRANS) do not invalidate undo data.

  • To query past data at a precise time, use an SCN. If you use a time stamp, the actual time queried might be up to 3 seconds earlier than the time you specify. Oracle Database uses SCNs internally and maps them to time stamps at a granularity of 3 seconds.

    For example, suppose that the SCN values 1000 and 1005 are mapped to the time stamps 8:41 AM and 8:46 AM, respectively. A query for a time between 8:41:00 and 8:45:59 AM is mapped to SCN 1000; an Oracle Flashback Query for 8:46 AM is mapped to SCN 1005. Therefore, if you specify a time that is slightly after a DDL operation (such as a table creation) Oracle Database might use an SCN that is just before the DDL operation, causing error ORA-01466.

  • You cannot retrieve past data from a dynamic performance (V$) view. A query on such a view always returns current data.

  • You can perform queries on past data in static data dictionary views, such as *_TABLES.

Performance Guidelines for Oracle Flashback Technology

  • Use the DBMS_STATS package to generate statistics for all tables involved in an Oracle Flashback Query. Keep the statistics current. Oracle Flashback Query uses the cost-based optimizer, which relies on these statistics.

  • Minimize the amount of undo data that must be accessed. Use queries to select small sets of past data using indexes, not to scan entire tables. If you must scan a full table, add a parallel hint to the query.

    The performance cost in I/O is the cost of paging in data and undo blocks that are not in the buffer cache. The performance cost in CPU use is the cost of applying undo information to affected data blocks. When operating on changes in the recent past, flashback operations are CPU-bound.

  • For Oracle Flashback Version Query, use index structures. Oracle Database keeps undo data for index changes and data changes. Performance of index lookup-based Oracle Flashback Version Query is an order of magnitude faster than the full table scans that are otherwise needed.

  • In an Oracle Flashback Transaction Query, the xid column is of the type RAW(8). To take advantage of the index built on the xid column, use the HEXTORAW conversion function: HEXTORAW(xid).

  • A Oracle Flashback Query against a materialized view does not take advantage of query rewrite optimization.

PKҸ .PK|%A OEBPS/toc.ncxP Oracle® Database Advanced Application Developer's Guide, 11g Release 2 (11.2) Cover Table of Contents List of Examples List of Figures List of Tables Oracle Database Advanced Application Developer's Guide, 11g Release 2 (11.2) Preface What's New in Application Development? SQL for Application Developers SQL Processing for Application Developers Using SQL Data Types in Database Applications Using Regular Expressions in Database Applications Using Indexes in Database Applications Maintaining Data Integrity in Database Applications PL/SQL for Application Developers Coding PL/SQL Subprograms and Packages Using PL/Scope Using the PL/SQL Hierarchical Profiler Developing PL/SQL Web Applications Developing PL/SQL Server Pages (PSP) Using Continuous Query Notification (CQN) Advanced Topics for Application Developers Using Oracle Flashback Technology Choosing a Programming Environment Developing Applications with Multiple Programming Languages Developing Applications with Oracle XA Developing Applications with the Publish-Subscribe Model Using the Identity Code Package Schema Object Dependency Edition-Based Redefinition Multithreaded extproc Agent Index Copyright PKnܴPK|%AOEBPS/adfns_profiler.htm Using the PL/SQL Hierarchical Profiler

8 Using the PL/SQL Hierarchical Profiler

You can use the PL/SQL hierarchical profiler to identify bottlenecks and performance-tuning opportunities in PL/SQL applications.

The profiler reports the dynamic execution profile of a PL/SQL program organized by function calls, and accounts for SQL and PL/SQL execution times separately. No special source or compile-time preparation is required; any PL/SQL program can be profiled.

This chapter describes the PL/SQL hierarchical profiler and explains how to use it to collect and analyze profile data for a PL/SQL program.

Topics:

Overview of PL/SQL Hierarchical Profiler

Nonhierarchical (flat) profilers record the time that a program spends within each subprogram—the function time or self time of each subprogram. Function time is helpful, but often inadequate. For example, it is helpful to know that a program spends 40% of its time in the subprogram INSERT_ORDER, but it is more helpful to know which subprograms call INSERT_ORDER often and the total time the program spends under INSERT_ORDER (including its descendant subprograms). Hierarchical profilers provide such information.

The PL/SQL hierarchical profiler:

  • Reports the dynamic execution profile of your PL/SQL program, organized by subprogram calls

  • Accounts for SQL and PL/SQL execution times separately

  • Requires no special source or compile-time preparation

  • Stores results in database tables (hierarchical profiler tables) for custom report generation by integrated development environment (IDE) tools (such as SQL Developer and third-party tools)

  • Provides subprogram-level execution summary information, such as:

    • Number of calls to the subprogram

    • Time spent in the subprogram itself (function time or self time)

    • Time spent in the subprogram itself and in its descendent subprograms (subtree time)

    • Detailed parent-children information, for example:

      • All callers of a given subprogram (parents)

      • All subprograms that a given subprogram called (children)

      • How much time was spent in subprogram x when called from y

      • How many calls to subprogram x were from y

The PL/SQL hierarchical profiler is implemented by the DBMS_HPROF package and has two components:

  • Data collection

    The data collection component is an intrinsic part of the PL/SQL Virtual Machine. The DBMS_HPROF package provides APIs to turn hierarchical profiling on and off and write the raw profiler output to a file.

  • Analyzer

    The analyzer component processes the raw profiler output and stores the results in hierarchical profiler tables.


    Note:

    To generate simple HTML reports directly from raw profiler output, without using the Analyzer, you can use the plshprof command-line utility.

Collecting Profile Data

To collect profile data from your PL/SQL program for the PL/SQL hierarchical profiler, follow these steps:

  1. Ensure that you have these privileges:

    • EXECUTE privilege on the DBMS_HPROF package

    • WRITE privilege on the directory that you specify when you call DBMS_HPROF.START_PROFILING

  2. Use the DBMS_HPROF.START_PROFILING PL/SQL API to start hierarchical profiler data collection in a session.

  3. Run your PL/SQL program long enough to get adequate code coverage.

    To get the most accurate measurements of elapsed time, avoid unrelated activity on the system on which your PL/SQL program is running.

  4. Use the DBMS_HPROF.STOP_PROFILING PL/SQL API to stop hierarchical profiler data collection.

For more information about DBMS_HPROF.START_PROFILING and DBMS_HPROF.STOP_PROFILING, see Oracle Database PL/SQL Packages and Types Reference.

Consider this PL/SQL procedure, test:

CREATE OR REPLACE PROCEDURE test IS
  n NUMBER;

  PROCEDURE foo IS
  BEGIN
    SELECT COUNT(*) INTO n FROM EMPLOYEES;
  END foo;

BEGIN  -- test
  FOR i IN 1..3 LOOP
    foo;
  END LOOP;
END test;
/

The SQL script in Example 8-1 profiles the execution of the PL/SQL procedure test.

Example 8-1 Profiling a PL/SQL Procedure

BEGIN
  /* Start profiling.
     Write raw profiler output to file test.trc in a directory
     that is mapped to directory object PLSHPROF_DIR
     (see note after example). */

  DBMS_HPROF.START_PROFILING('PLSHPROF_DIR', 'test.trc');
END;
/
-- Run procedure to be profiled
BEGIN
  test;
END;
/
BEGIN
  -- Stop profiling
  DBMS_HPROF.STOP_PROFILING;
END;
/

Note:

A directory object is an alias for a file system path name. For example, if you are connected to the database AS SYSDBA, this CREATE DIRECTORY statement creates the directory object PLSHPROF_DIR and maps it to the file system directory /private/plshprof/results:
CREATE DIRECTORY PLSHPROF_DIR as '/private/plshprof/results';

To run the SQL script in Example 8-1, you must have READ and WRITE privileges on both PLSHPROF_DIR and the directory to which it is mapped. if you are connected to the database AS SYSDBA, this GRANT statement grants READ and WRITE privileges on PLSHPROF_DIR to HR:

GRANT READ, WRITE ON DIRECTORY PLSHPROF_DIR TO HR;

For more information about using directory objects, see Oracle Database SecureFiles and Large Objects Developer's Guide.


Understanding Raw Profiler Output

Raw profiler output is intended to be processed by the analyzer component of the PL/SQL hierarchical profiler. However, even without such processing, it provides a simple function-level trace of the program. This topic explains how to understand raw profiler output.


Note:

The raw profiler format shown in this chapter is intended only to illustrate conceptual features of raw profiler output. Format specifics are subject to change at each Oracle Database release.

The SQL script in Example 8-1 wrote this raw profiler output to the file test.trc:

P#V PLSHPROF Internal Version 1.0
P#! PL/SQL Timer Started
P#C PLSQL."".""."__plsql_vm"
P#X 2
P#C PLSQL."".""."__anonymous_block"
P#X 50
P#C PLSQL."HR"."TEST"::7."TEST"#980980e97e42f8ec #1
P#X 3
P#C PLSQL."HR"."TEST"::7."TEST.FOO"#980980e97e42f8ec #4
P#X 35
P#C SQL."HR"."TEST"::7."__static_sql_exec_line6" #6
P#X 206
P#R
P#X 26
P#R
P#X 2
P#C PLSQL."HR"."TEST"::7."TEST.FOO"#980980e97e42f8ec #4
P#X 4
P#C SQL."HR"."TEST"::7."__static_sql_exec_line6" #6
P#X 80
P#R
P#X 3
P#R
P#X 0
P#C PLSQL."HR"."TEST"::7."TEST.FOO"#980980e97e42f8ec #4
P#X 3
P#C SQL."HR"."TEST"::7."__static_sql_exec_line6" #6
P#X 69
P#R
P#X 3
P#R
P#X 1
P#R
P#X 1
P#R
P#X 3
P#R
P#C PLSQL."".""."__plsql_vm"
P#X 3
P#C PLSQL."".""."__anonymous_block"
P#X 44
P#C PLSQL."SYS"."DBMS_HPROF"::11."STOP_PROFILING"#980980e97e42f8ec #53
P#R
P#R
P#R
P#! PL/SQL Timer Stopped

Table 8-1 Raw Profiler Output File Indicators

IndicatorMeaning

P#V

PLSHPROF banner with version number

P#C

Call to a subprogram (call event)

P#R

Return from a subprogram (return event)

P#X

Elapsed time between preceding and following events

P#!

Comment


Call events (P#C) and return events (P#R) are always properly nested (like matched parentheses). If an unhandled exception causes a called subprogram to exit, the profiler still reports a matching return event.

Each call event (P#C) entry in the raw profiler output includes this information:

  • Namespace to which the called subprogram belongs

    See "Namespaces of Tracked Subprograms".

  • Name of the PL/SQL module in which the called subprogram is defined

  • Type of the PL/SQL module in which the called subprogram is defined

  • Name of the called subprogram

    This name might be one of the "Special Function Names".

  • Hexadecimal value that represents an MD5 hash of the signature of the called subprogram

    The DBMS_HPROF.analyze PL/SQL API (described in "Analyzing Profile Data") stores the hash value in a hierarchical profiler table, which allows both you and DBMS_HPROF.analyze to distinguish between overloaded subprograms (subprograms with the same name).

  • Line number at which the called subprogram is defined in the PL/SQL module

    PL/SQL hierarchical profiler does not measure time spent at individual lines within modules, but you can use line numbers to identify the source locations of subprograms in the module (as IDE/Tools do) and to distinguish between overloaded subprograms.

For example, consider this entry in the preceding example of raw profiler output:

P#C PLSQL."HR"."TEST"::7."TEST.FOO"#980980e97e42f8ec #4

The components of the preceding entry have these meanings:

ComponentMeaning
PLSQLPLSQL is the namespace to which the called subprogram belongs.
"HR"."TEST"HR.TEST is the name of the PL/SQL module in which the called subprogram is defined.
77 is the internal enumerator for the module type of HR.TEST. Examples of module types are procedure, package, and package body.
"TEST.FOO"TEST.FOO is the name of the called subprogram.
#980980e97e42f8ec#980980e97e42f8ec is a hexadecimal value that represents an MD5 hash of the signature of TEST.FOO.
#44 is the line number in the PL/SQL module HR.TEST at which TEST.FOO is defined.


Note:

When a subprogram is inlined, it is not reported in the profiler output. For information about subprogram inlining, see Oracle Database PL/SQL Language Reference.

When a call to a DETERMINISTIC function is "optimized away," it is not reported in the profiler output. For information about DETERMINISTIC functions, see Oracle Database PL/SQL Language Reference.


Namespaces of Tracked Subprograms

The subprograms that the PL/SQL hierarchical profiler tracks are classified into the namespaces PLSQL and SQL, as follows:

  • Namespace PLSQL includes:

    • PL/SQL subprogram calls

    • PL/SQL triggers

    • PL/SQL anonymous blocks

    • Remote subprogram calls

    • Package initialization blocks

  • Namespace SQL includes SQL statements executed from PL/SQL, such as queries, data manipulation language (DML) statements, data definition language (DDL) statements, and native dynamic SQL statements

Special Function Names

PL/SQL hierarchical profiler tracks certain operations as if they were functions with the names and namespaces shown in Table 8-2.

Table 8-2 Function Names of Operations that the PL/SQL Hierarchical Profiler Tracks

Tracked OperationFunction NameNamespace

Call to PL/SQL Virtual Machine

__plsql_vm

PL/SQL

PL/SQL anonymous block

__anonymous_block

PL/SQL

Package initialization block

__pkg_init

PL/SQL

Static SQL statement at line line#

__static_sql_exec_lineline#

SQL

Dynamic SQL statement at line line#

__dyn_sql_exec_lineline#

SQL

SQL FETCH statement at line line#

__sql_fetch_lineline#

SQL


Analyzing Profile Data

The analyzer component of the PL/SQL hierarchical profiler, DBMS_HPROF.analyze, processes the raw profiler output and stores the results in the hierarchical database tables described in Table 8-3.

Table 8-3 PL/SQL Hierarchical Profiler Database Tables

TableDescription

DBMSHP_RUNS

Top-level information for this run of DBMS_HPROF.analyze. For column descriptions, see Table 8-4.

DBMSHP_FUNCTION_INFO

Information for each subprogram profiled in this run of DBMS_HPROF.analyze. For column descriptions, see Table 8-5.

DBMSHP_PARENT_CHILD_INFO

Parent-child information for each subprogram profiled in this run of DBMS_HPROF.analyze. For column descriptions, see Table 8-6.


Topics:


Note:

To generate simple HTML reports directly from raw profiler output, without using the Analyzer, you can use the plshprof command-line utility. For details, see "plshprof Utility".

Creating Hierarchical Profiler Tables

To create the hierarchical profiler tables in Table 8-3 and the other data structures required for persistently storing profile data, follow these steps:

  1. Run the script dbmshptab.sql (located in the directory rdbms/admin).

    This script creates both the hierarchical profiler tables in Table 8-3 and the other data structures required for persistently storing profile data.


    Note:

    Running the script dbmshptab.sql drops any previously created hierarchical profiler tables.

  2. Ensure that you have these privileges:

    • EXECUTE privilege on the DBMS_HPROF package

    • READ privilege on the directory that DBMS_HPROF.analyze specifies

  3. Use the PL/SQL API DBMS_HPROF.analyze to analyze a single raw profiler output file and store the results in hierarchical profiler tables.

    (For an example of a raw profiler output file, see test.trc in "Understanding Raw Profiler Output".)

    For more information about DBMS_HPROF.analyze, see Oracle Database PL/SQL Packages and Types Reference.

  4. Use the hierarchical profiler tables to generate custom reports.

The anonymous block in Example 8-2:

  • Invokes the function DBMS_HPROF.analyze function, which:

    • Analyzes the profile data in the raw profiler output file test.trc (from "Understanding Raw Profiler Output"), which is in the directory that is mapped to the directory object PLSHPROF_DIR, and stores the data in the hierarchical profiler tables in Table 8-3.

    • Returns a unique identifier that you can use to query the hierarchical profiler tables in Table 8-3. (By querying these hierarchical profiler tables, you can produce customized reports.)

  • Stores the unique identifier in the variable runid, which it prints.

Example 8-2 Invoking DBMS_HPROF.analyze

DECLARE
  runid NUMBER;
BEGIN
  runid := DBMS_HPROF.analyze(LOCATION=>'PLSHPROF_DIR',
                              FILENAME=>'test.trc');
  DBMS_OUTPUT.PUT_LINE('runid = ' || runid);
END;
/

Understanding Hierarchical Profiler Tables

These topics explain how to use the hierarchical profiler tables in Table 8-3:

Hierarchical Profiler Database Table Columns

Table 8-4 describes the columns of the hierarchical profiler table DBMSHP_RUNS, which contains one row of top-level information for each run of DBMS_HPROF.analyze.

The primary key for the hierarchical profiler table DBMSHP_RUNS is RUNID.

Table 8-4 DBMSHP_RUNS Table Columns

Column NameColumn Data TypeColumn Contents

RUNID

NUMBER PRIMARY KEY

Unique identifier for this run of DBMS_HPROF.analyze, generated from DBMSHP_RUNNUMBER sequence.

RUN_TIMESTAMP

TIMESTAMP

Time stamp for this run of DBMS_HPROF.analyze.

RUN_COMMENT

VARCHAR2(2047)

Comment that you provided for this run of DBMS_HPROF.analyze.

TOTAL_ELAPSED_TIME

INTEGER

Total elapsed time for this run of DBMS_HPROF.analyze.


Table 8-5 describes the columns of the hierarchical profiler table DBMSHP_FUNCTION_INFO, which contains one row of information for each subprogram profiled in this run of DBMS_HPROF.analyze. If a subprogram is overloaded, Table 8-5 has a row for each variation of that subprogram. Each variation has its own LINE# and HASH (see "Distinguishing Between Overloaded Subprograms").

The primary key for the hierarchical profiler table DBMSHP_FUNCTION_INFO is RUNID, SYMBOLID.

Table 8-5 DBMSHP_FUNCTION_INFO Table Columns

Column NameColumn Data TypeColumn Contents

RUNID

NUMBER

References RUNID column of DBMSHP_RUNS table. For a description of that column, see Table 8-4.

SYMBOLID

NUMBER

Symbol identifier for subprogram (unique for this run of DBMS_HPROF.analyze).

OWNER

VARCHAR2(32)

Owner of module in which each subprogram is defined (for example, SYS or HR).

MODULE

VARCHAR2(2047)

Module in which subprogram is defined (for example, DBMS_LOB, UTL_HTTP, or MY_PACKAGE).

TYPE

VARCHAR2(32)

Type of module in which subprogram is defined (for example, PACKAGE, PACKAGE_BODY, or PROCEDURE).

FUNCTION

VARCHAR2(4000)

Name of subprogram (not necessarily a function) (for example, INSERT_ORDER, PROCESS_ITEMS, or TEST).

This name might be one of the "Special Function Names".

For subprogram B defined within subprogram A, this name is A.B.

A recursive call to function X is denoted X@n, where n is the recursion depth. For example, X@1 is the first recursive call to X.

LINE#

NUMBER

Line number in OWNER.MODULE at which FUNCTION is defined.

HASH

RAW(32)

Hash code for signature of subprogram (unique for this run of DBMS_HPROF.analyze).

NAMESPACE

VARCHAR2(32)

Namespace of subprogram. For details, see "Namespaces of Tracked Subprograms".

SUBTREE_ELAPSED_TIME

INTEGER

Elapsed time, in microseconds, for subprogram, including time spent in descendant subprograms.

FUNCTION_ELAPSED_TIME

INTEGER

Elapsed time, in microseconds, for subprogram, excluding time spent in descendant subprograms.

CALLS

INTEGER

Number of calls to subprogram.


Table 8-6 describes the columns of the hierarchical profiler table DBMSHP_PARENT_CHILD_INFO, which contains one row of parent-child information for each unique parent-child subprogram combination profiled in this run of DBMS_HPROF.analyze.

Table 8-6 DBMSHP_PARENT_CHILD_INFO Table Columns

Column NameColumn Data TypeColumn Contents

RUNID

NUMBER

References RUNID column of DBMSHP_FUNCTION_INFO table. For a description of that column, see Table 8-5.

PARENTSYMID

NUMBER

Parent symbol ID.

RUNID, PARENTSYMID references DBMSHP_FUNCTION_INFO(RUNID, SYMBOLID).

CHILDSYMID

VARCHAR2(32)

Child symbol ID.

RUNID, CHILDSYMID references DBMSHP_FUNCTION_INFO(RUNID, SYMBOLID).

SUBTREE_ELAPSED_TIME

INTEGER

Elapsed time, in microseconds, for RUNID, CHILDSYMID when called from RUNID, PARENTSYMID, including time spent in descendant subprograms.

FUNCTION_ELAPSED_TIME

INTEGER

Elapsed time, in microseconds, for RUNID, CHILDSYMID when called from RUNID, PARENTSYMID, excluding time spent in descendant subprograms.

CALLS

INTEGER

Number of calls to RUNID, CHILDSYMID from RUNID, PARENTSYMID.


Distinguishing Between Overloaded Subprograms

Overloaded subprograms are different subprograms with the same name (see Oracle Database PL/SQL Language Reference).

Suppose that a program declares three subprograms named compute—the first at line 50, the second at line 76, and the third at line 100. In the DBMSHP_FUNCTION_INFO table, each of these subprograms has compute in the FUNCTION column. To distinguish between the three subprograms, use either the LINE# column (which has 50 for the first subprogram, 76 for the second, and 100 for the third) or the HASH column (which has a unique value for each subprogram).

In the profile data for two different runs, the LINE# and HASH values for a function might differ. The LINE# value of a function changes if you insert or delete lines before the function definition. The HASH value changes only if the signature of the function changes; for example, if you change the parameter list.

Hierarchical Profiler Tables for Sample PL/SQL Procedure

The hierarchical profiler tables for the PL/SQL procedure test in "Collecting Profile Data" are shown in Example 8-3 through Example 8-5.

Example 8-3 DBMSHP_RUNS Table for Sample PL/SQL Procedure

RUNID  RUN_TIMESTAMP                 TOTAL_ELAPSED_TIME  RUN_COMMENT
1      10-APR-06 12.01.56.766743 PM  2637                 First run of TEST

Example 8-4 DBMSHP_FUNCTION_INFO Table for Sample PL/SQL Procedure

RUNID  SYMBOLID  OWNER  MODULE      TYPE          NAMESPACE  FUNCTION
1      1                                          PLSQL      __anonymous_block
1      2                                          PLSQL      __plsql_vm
1      3         HR     TEST        PROCEDURE     PLSQL      TEST
1      4         HR     TEST        PROCEDURE     PLSQL      TEST.FOO
1      5         SYS    DBMS_HPROF  PACKAGE_BODY  PLSQL      STOP_PROFILING
1      6         HR     TEST        PROCEDURE     SQL        __static_sql_exec_line5

LINE#  CALLS  HASH              SUBTREE_ELAPSED_TIME  FUNCTION_ELAPSED_TIME
0      2      980980E97E42F8EC  2554                  342
0      2      980980E97E42F8EC  2637                  83
1      1      980980E97E42F8EC  2212                  28
3      3      980980E97E42F8EC  2184                  126
57     1      980980E97E42F8EC  0                     0
5      3      980980E97E42F8EC  1998                  1998

Example 8-5 DBMSHP_PARENT_CHILD_INFO Table for Sample PL/SQL Procedure

RUNID  PARENTSYMID  CHILDSYMID  SUBTREE_ELAPSED_TIME  FUNCTION_ELAPSED_TIME  CALLS
1      2            1           2554                   342                   2
1      1            3           2212                   28                    1
1      3            4           2184                   126                   3
1      1            5           0                      0                     1
1      4            6           1998                   1998                  3

Consider the third row of the table DBMSHP_PARENT_CHILD_INFO (Example 8-5). The RUNID column shows that this row corresponds to the first run. The columns PARENTSYMID and CHILDSYMID show that the symbol IDs of the parent (caller) and child (called subprogram) are 3 and 4, respectively. The table DBMSHP_FUNCTION_INFO (Example 8-4) shows that for the first run, the symbol IDs 3 and 4 correspond to procedures TEST and TEST.FOO, respectively. Therefore, the information in this row is about calls from the procedure TEST to the procedure FOO (defined within TEST) in the module HR.TEST. This row shows that, when called from the procedure TEST, the function time for the procedure FOO is 126 microseconds, and the time spent in the FOO subtree (including descendants) is 2184 microseconds.

Examples of Calls to DBMS_HPROF.analyze with Options

For an example of a call to DBMS_HPROF.analyze without options, see Example 8-2.

Example 8-6 creates a package, creates a procedure that invokes subprograms in the package, profiles the procedure, and uses DBMS_HRPROF.analyze to analyze the raw profiler output file. The raw profiler output file is in the directory corresponding to the PLSHPROF_DIR directory object.

Example 8-6 Invoking DBMS_HPROF.analyze with Options

-- Create package

CREATE OR REPLACE PACKAGE pkg IS
  PROCEDURE myproc (n IN out NUMBER);
  FUNCTION myfunc (v VARCHAR2) RETURN VARCHAR2;
  FUNCTION myfunc (n PLS_INTEGER) RETURN PLS_INTEGER;
END pkg;
/
CREATE OR REPLACE PACKAGE BODY pkg IS
  PROCEDURE myproc (n IN OUT NUMBER) IS
  BEGIN
    n := n + 5;
  END;

  FUNCTION myfunc (v VARCHAR2) RETURN VARCHAR2 IS
    n NUMBER;
  BEGIN
    n := LENGTH(v);
    myproc(n);
    IF n > 20 THEN
      RETURN SUBSTR(v, 1, 20);
    ELSE
      RETURN v || '...';
    END IF;
  END;

  FUNCTION myfunc (n PLS_INTEGER) RETURN PLS_INTEGER IS
    i PLS_INTEGER;
    PROCEDURE myproc (n IN out PLS_INTEGER) IS
    BEGIN
      n := n + 1;
    END;
  BEGIN
    i := n;
    myproc(i);
    RETURN i;
  END;
END pkg;
/

-- Create procedure that invokes package subprograms

CREATE OR REPLACE PROCEDURE test2 IS
  x NUMBER := 5;
  y VARCHAR2(32767);
BEGIN
  pkg.myproc(x);
  y := pkg.myfunc('hello');
END;

-- Profile test2

BEGIN
  DBMS_HPROF.START_PROFILING('PLSHPROF_DIR', 'test2.trc');
END;
/
BEGIN
  test2;
END;
/
BEGIN
  DBMS_HPROF.STOP_PROFILING;
END;
/
-- If not done, create hierarchical profiler tables
-- (see "Creating Hierarchical Profiler Tables".)

-- Call DBMS_HPROF.analyze with options

DECLARE
  runid NUMBER;
BEGIN
  -- Analyze only subtrees rooted at trace entry "HR"."PKG"."MYPROC"

  runid := DBMS_HPROF.analyze('PLSHPROF_DIR', 'test2.trc',
                              trace => '"HR"."PKG"."MYPROC"');

  -- Analyze up to 20 calls to subtrees rooted at trace entry
  -- "HR"."PKG"."MYFUNC".  Because "HR"."PKG"."MYFUNC" is overloaded,
  -- both overloads are considered for analysis.

  runid := DBMS_HPROF.analyze('PLSHPROF_DIR', 'test2.trc',
                              collect => 20,
                              trace => '"HR"."PKG"."MYFUNC"');

  -- Analyze second call to PL/SQL virtual machine

  runid := DBMS_HPROF.analyze('PLSHPROF_DIR', 'test2.trc',
                              skip => 1, collect => 1,
                              trace => '""."".""__plsql_vm"');
END;
/

plshprof Utility

The plshprof command-line utility (located in the directory $ORACLE_HOME/bin/) generates simple HTML reports from either one or two raw profiler output files. (For an example of a raw profiler output file, see test.trc in "Collecting Profile Data".)

You can browse the generated HTML reports in any browser. The browser's navigational capabilities, combined with well chosen links, provide a powerful way to analyze performance of large applications, improve application performance, and lower development costs.

Topics:

plshprof Options

The command to run the plshprof utility is:

plshprof [option...] profiler_output_filename_1 profiler_output_filename_2

Each option is one of these:

OptionDescriptionDefault
-skip countSkips first count calls. Use only with -trace symbol.0
-collect countCollects information for count calls. Use only with -trace symbol.1
-output filenameSpecifies name of output filesymbol.html or tracefile1.html
-summaryPrints only elapsed timeNone
-trace symbolSpecifies function name of tree rootNot applicable

Suppose that your raw profiler output file, test.trc, is in the current directory. You want to analyze and generate HTML reports, and you want the root file of the HTML report to be named report.html. Use this command (% is the prompt):

% plshprof -output report test.trc

HTML Report from a Single Raw Profiler Output File

To generate a PL/SQL hierarchical profiler HTML report from a single raw profiler output file, use these commands:

% cd target_directory
% plshprof -output html_root_filename profiler_output_filename

target_directory is the directory in which you want the HTML files to be created.

html_root_filename is the name of the root HTML file to be created.

profiler_output_filename is the name of a raw profiler output file.

The preceding plshprof command generates a set of HTML files. Start browsing them from html_root_filename.html.

Topics:

First Page of Report

The first page of an HTML report from a single raw profiler output file includes summary information and hyperlinks to other pages of the report.

Sample First Page

PL/SQL Elapsed Time (microsecs) Analysis

2831 microsecs (elapsed time) & 12 function calls

The PL/SQL Hierarchical Profiler produces a collection of reports that present information derived from the profiler output log in a variety of formats. These reports have been found to be the most generally useful as starting points for browsing:

  • Function Elapsed Time (microsecs) Data sorted by Total Subtree Elapsed Time (microsecs)

  • Function Elapsed Time (microsecs) Data sorted by Total Function Elapsed Time (microsecs)

In addition, the following reports are also available:

  • Function Elapsed Time (microsecs) Data sorted by Function Name

  • Function Elapsed Time (microsecs) Data sorted by Total Descendants Elapsed Time (microsecs)

  • Function Elapsed Time (microsecs) Data sorted by Total Function Call Count

  • Function Elapsed Time (microsecs) Data sorted by Mean Subtree Elapsed Time (microsecs)

  • Function Elapsed Time (microsecs) Data sorted by Mean Function Elapsed Time (microsecs)

  • Function Elapsed Time (microsecs) Data sorted by Mean Descendants Elapsed Time (microsecs)

  • Module Elapsed Time (microsecs) Data sorted by Total Function Elapsed Time (microsecs)

  • Module Elapsed Time (microsecs) Data sorted by Module Name

  • Module Elapsed Time (microsecs) Data sorted by Total Function Call Count

  • Namespace Elapsed Time (microsecs) Data sorted by Total Function Elapsed Time (microsecs)

  • Namespace Elapsed Time (microsecs) Data sorted by Namespace

  • Namespace Elapsed Time (microsecs) Data sorted by Total Function Call Count

  • Parents and Children Elapsed Time (microsecs) Data

Function-Level Reports

The function-level reports provide a flat view of the profile information. Each function-level report includes this information for each function:

  • Function time (time spent in the function itself, also called "self time")

  • Descendants time (time spent in the descendants of the function)

  • Subtree time (time spent in the subtree of the function—function time plus descendants time)

  • Number of calls to the function

  • Function name

    The function name is hyperlinked to the Parents and Children Report for the function.

Each function-level report is sorted on a particular attribute; for example, function time or subtree time.

This sample report is sorted in descending order of the total subtree elapsed time for the function, which is why information in the Subtree and Ind% columns is in bold type:

Sample Report

Function Elapsed Time (microsecs) Data sorted by Total Subtree Elapsed Time (microsecs)

2831 microsecs (elapsed time) & 12 function calls

SubtreeInd%FunctionDescendantInd%CallsInd%Function Name
2831100%93273896.7%216.7%__plsq_vm
273896.7%310242885.8%216.7%__anonymous_block
242885.8%15241385.2%18.3%HR.TEST.TEST (Line 1)
241385.2%435197869.9%325.0%HR.TEST.TEST.FOO (Line 3)
197869.9%197800.0%325.0%HR.TEST.__static_sql_exec_line5 (Line 5)
00.0%000.0%18.3%SYS.DBMS_HPROF.STOP_PROFILING (Line 53)

Module-Level Reports

Each module-level report includes this information for each module (for example, package or type):

  • Module time (time spent in the module—sum of the function times of all functions in the module)

  • Number of calls to functions in the module

Each module-level report is sorted on a particular attribute; for example, module time or module name.

This sample report is sorted by module time, which is why information in the Module, Ind%, and Cum% columns is in bold type:

Sample Report

Module Elapsed Time (microsecs) Data sorted by Total Function Elapsed Time (microsecs)

166878 microsecs (elapsed time) & 1099 function calls

ModuleInd%Cum%CallsInd%Module Name
8493250.9%50.9%60.5%HR.P
6774940.6%91.5%21619.7%SYS.DBMS_LOB
133408.0%99.5%66060.1%SYS.UTL_FILE
8390.5%100%21419.5%SYS.UTL_RAW
180.0%100%20.2%HR.UTILS
00.0%100%10.1%SYS.DBMS_HPROF

Namespace-Level Reports

Each namespace-level report includes this information for each namespace:

  • Namespace time (time spent in the namespace—sum of the function times of all functions in the namespace)

  • Number of calls to functions in the namespace

Each namespace-level report is sorted on a particular attribute; for example, namespace time or number of calls to functions in the namespace.

This sample report is sorted by function time, which is why information in the Function, Ind%, and Cum% columns is in bold type:

Sample Report

Namespace Elapsed Time (microsecs) Data sorted by Total Function Elapsed Time (microsecs)

166878 microsecs (elapsed time) & 1099 function calls

FunctionInd%Cum%CallsInd%Namespace
9365956.1%56.1%109599.6%PLSQL
7321943.9%100%40.4%SQL

Parents and Children Report for a Function

For each function tracked by the profiler, the Parents and Children Report provides information about parents (functions that call it) and children (functions that it calls). For each parent, the report gives the function's execution profile (subtree time, function time, descendants time, and number of calls). For each child, the report gives the execution profile for the child when called from this function (but not when called from other functions).

The execution profile for a function includes the same information for that function as a function-level report includes for each function (for details, see "Function-Level Reports").

This Sample Report is a fragment of a Parents and Children Report that corresponds to a function named HR.P.UPLOAD. The first row has this summary information:

  • There are two calls to the function HR.P.UPLOAD.

  • The total subtree time for the function is 166,860 microseconds—11,713 microseconds (7.0%) in the function itself and 155,147 microseconds (93.0%) in its descendants.

After the row "Parents" are the execution profile rows for the two parents of HR.P.UPLOAD, which are HR.UTILS.COPY_IMAGE and HR.UTILS.COPY_FILE.

The first parent execution profile row, for HR.UTILS.COPY_IMAGE, shows:

  • HR.UTILS.COPY_IMAGE calls HR.P.UPLOAD once, which is 50% of the number of calls to HR.P.UPLOAD.

  • The subtree time for HR.P.UPLOAD when called from HR.UTILS.COPY_IMAGE is 106,325 microseconds, which is 63.7% of the total subtree time for HR.P.UPLOAD.

  • The function time for HR.P.UPLOAD when called from HR.UTILS.COPY_IMAGE is 6,434 microseconds, which is 54.9% of the total function time for HR.P.UPLOAD.

After the row "Children" are the execution profile rows for the children of HR.P.UPLOAD when called from HR.P.UPLOAD.

The third child execution profile row, for SYS.UTL_FILE.GET_RAW, shows:

  • HR.P.UPLOAD calls SYS.UTL_FILE.GET_RAW 216 times.

  • The subtree time, function time and descendants time for SYS.UTL_FILE.GET_RAW when called from HR.P.UPLOAD are 12,487 microseconds, 3,969 microseconds, and 8,518 microseconds, respectively.

  • Of the total descendants time for HR.P.UPLOAD (155,147 microseconds), the child SYS.UTL_FILE.GET_RAW is responsible for 12,487 microsecs (8.0%).

Sample Report

HR.P.UPLOAD (Line 3)

SubtreeInd%FunctionInd%DescendantInd%CallsInd%Function Name
166860100%117137.0%15514793.0%20.2%HR.P.UPLOAD (Line 3)
Parents:







10632563.7%643454.9%9989164.4%150.0%HR.UTILS.COPY_IMAGE (Line 3)
6053536.3%527945.1%5525635.6%150.0%HR.UTILS.COPY_FILE (Line 8))
Children:







7181846.3%71818100%0N/A2100%HR.P.__static_sql_exec_line38 (Line 38)
6764943.6%67649100%0N/A214100%SYS.DBMS_LOB.WRITEAPPEND (Line 926)
124878.0%3969100%8518100%216100%SYS.UTL_FILE.GET_RAW (Line 1089)
14010.9%1401100%0N/A2100%HR.P.__static_sql_exec_line39 (Line 39)
8390.5%839100%0N/A214100%SYS.UTL_FILE.GET_RAW (Line 246)
7400.5%73100%667100%2100%SYS.UTL_FILE.FOPEN (Line 422)
1130.1%11100%102100%2100%SYS.UTL_FILE.FCLOSE (Line 585)
1000.1%100100%0N/A2100%SYS.DBMS_LOB.CREATETEMPORARY (Line 536)

HTML Difference Report from Two Raw Profiler Output Files

To generate a PL/SQL hierarchical profiler HTML difference report from two raw profiler output files, use these commands:

% cd target_directory
% plshprof -output html_root_filename profiler_output_filename_1 profiler_output_filename_2

target_directory is the directory in which you want the HTML files to be created.

html_root_filename is the name of the root HTML file to be created.

profiler_output_filename_1 and profiler_output_filename_2 are the names of raw profiler output files.

The preceding plshprof command generates a set of HTML files. Start browsing them from html_root_filename.html.

Topics:

Difference Report Conventions

Difference reports use these conventions:

  • In a report title, Delta means difference, or change.

  • A positive value indicates that the number increased (regressed) from the first run to the second run.

  • A negative value for a difference indicates that the number decreased (improved) from the first run to the second run.

  • The symbol # after a function name means that the function was called in only one run.

First Page of Difference Report

The first page of an HTML difference report from two raw profiler output files includes summary information and hyperlinks to other pages of the report.

Sample First Page

PL/SQL Elapsed Time (microsecs) Analysis – Summary Page

This analysis finds a net regression of 2709589 microsecs (elapsed time) or 80% (3393719 versus 6103308). Here is a summary of the 7 most important individual function regressions and improvements:

Regressions: 3399382 microsecs (elapsed time)

FunctionRel%Ind%CallsRel%Function Name
2075627+941%61.1%0
HR.P.G (Line 35)
1101384+54.6%32.4%5+55.6%HR.P.H (Line 18)
222371
6.5%1
HR.P.J (Line 10)

Improvements: 689793 microsecs (elapsed time)

FunctionRel%Ind%CallsRel%Function Name
-467051-50.0%67.7%-2-50.0%HR.P.F (Line 25)
-222737
32.3%-1
HR.P.I (Line 2)#
-5-21.7%0.0%0
HR.P.TEST (Line 46)

The PL/SQL Timing Analyzer produces a collection of reports that present information derived from the profiler's output logs in a variety of formats. The following reports have been found to be the most generally useful as starting points for browsing:

  • Function Elapsed Time (microsecs) Data for Performance Regressions

  • Function Elapsed Time (microsecs) Data for Performance Improvements

In addition, the following reports are also available:

  • Function Elapsed Time (microsecs) Data sorted by Function Name

  • Function Elapsed Time (microsecs) Data sorted by Total Subtree Elapsed Time (microsecs) Delta

  • Function Elapsed Time (microsecs) Data sorted by Total Function Elapsed Time (microsecs) Delta

  • Function Elapsed Time (microsecs) Data sorted by Total Descendants Elapsed Time (microsecs) Delta

  • Function Elapsed Time (microsecs) Data sorted by Total Function Call Count Delta

  • Module Elapsed Time (microsecs) Data sorted by Module Name

  • Module Elapsed Time (microsecs) Data sorted by Total Function Elapsed Time (microsecs) Delta

  • Module Elapsed Time (microsecs) Data sorted by Total Function Call Count Delta

  • Namespace Elapsed Time (microsecs) Data sorted by Namespace

  • Namespace Elapsed Time (microsecs) Data sorted by Total Function Elapsed Time (microsecs)

  • Namespace Elapsed Time (microsecs) Data sorted by Total Function Call Count

  • File Elapsed Time (microsecs) Data Comparison with Parents and Children

Function-Level Difference Reports

Each function-level difference report includes, for each function, the change in these values from the first run to the second run:

  • Function time (time spent in the function itself, also called "self time")

  • Descendants time (time spent in the descendants of the function)

  • Subtree time (time spent in the subtree of the function—function time plus descendants time)

  • Number of calls to the function

  • Mean function time

    The mean function time is the function time divided by number of calls to the function.

  • Function name

    The function name is hyperlinked to the Parents and Children Difference Report for the function.

The report in Sample Report 1 shows the difference information for all functions that performed better in the first run than they did in the second run. Note that:

  • For HR.P.G, the function time increased by 2,075,627 microseconds (941%), which accounts for 61.1% of all regressions.

  • For HR.P.H, the function time and number of calls increased by 1,101,384 microseconds (54.6%) and 5 (55.6%), respectively, but the mean function time improved by 1,346 microseconds (-0.6%).

  • HR.P.J was called in only one run.

Sample Report 1

Function Elapsed Time (microsecs) Data for Performance Regressions

SubtreeFunctionRel%Ind%Cum%DescendantCallsRel%Mean FunctionRel%Function Name
40757872075627+941%61.1%61.1%20001600
2075627+941%HR.P.G (Line 35)
11013841101384+54.6%32.4%93.5%05+55.6%-1346-0.6%HR.P.H (Line 18)
222371222371
6.5%100%01


HR.P.J (Line 10)#

The report in Sample Report 2 shows the difference information for all functions that performed better in the second run than they did in the first run.

Sample Report 2

Function Elapsed Time (microsecs) Data for Performance Improvements

SubtreeFunctionRel%Ind%Cum%DescendantCallsRel%Mean FunctionRel%Function Name
-1365827-467051-50.0%67.7%67.7%-898776-2-50.0%-320.0%HR.P.F (Line 25)
-222737-222737
32.3%100%0-1


HR.P.I (Line 2)
2709589-5-21.7%0.0%100%27095940
-5-20.8HR.P.TEST (Line 46)#

The report in Sample Report 3 summarizes the difference information for all functions.

Sample Report 3

Function Elapsed Time (microsecs) Data sorted by Total Function Call Count Delta

SubtreeFunctionRel%Ind%DescendantCallsRel%Mean FunctionRel%Function Name
11013841101384+54.6%32.4%05+55.6%-1346-0.6%HR.P.H (Line 18)
-1365827-467051+50.0%67.7%-898776-2-50.0%-32-0.0%HR.P.F (Line 25)
-222377-222377
32.3%0-1


HR.P.I (Line 2)#
222371222371
6.5%01


HR.P.J(Line 10)#
40757872075627+941%61.1%20001600
2075627+941%HR.P.G (Line 35)
2709589-5-21.7%0.0%27095940
-5-20.8%HR.P.TEST (Line 46)
00

00


SYS.DBMS_HPROF.STOP_PROFILING (Line 53)

Module-Level Difference Reports

Each module-level report includes, for each module, the change in these values from the first run to the second run:

  • Module time (time spent in the module—sum of the function times of all functions in the module)

  • Number of calls to functions in the module

Sample Report

Module Elapsed Time (microsecs) Data sorted by Total Function Elapsed Time (microsecs) Delta

ModuleCallsModule Name
27095893HR.P
00SYS.DBMS_HPROF

Namespace-Level Difference Reports

Each namespace-level report includes, for each namespace, the change in these values from the first run to the second run:

  • Namespace time (time spent in the namespace—sum of the function times of all functions in the namespace)

  • Number of calls to functions in the namespace

Sample Report

Namespace Elapsed Time (microsecs) Data sorted by Namespace

FunctionCallsNamespace
27095893PLSQL

Parents and Children Difference Report for a Function

The Parents and Children Difference Report for a function shows changes in the execution profiles of these from the first run to the second run:

  • Parents (functions that call the function)

  • Children (functions that the function calls)

    Execution profiles for children include only information from when this function calls them, not for when other functions call them.

The execution profile for a function includes this information:

  • Function time (time spent in the function itself, also called "self time")

  • Descendants time (time spent in the descendants of the function)

  • Subtree time (time spent in the subtree of the function—function time plus descendants time)

  • Number of calls to the function

  • Function name

The sample report is a fragment of a Parents and Children Difference Report that corresponds to a function named HR.P.X.

The first row, a summary of the difference between the first and second runs, shows regression: function time increased by 1,094,099 microseconds (probably because the function was called five more times).

The "Parents" rows show that HR.P.G called HR.P.X nine more times in the second run than it did in the first run, while HR.P.F called it four fewer times.

The "Children" rows show that HR.P.X called each child five more times in the second run than it did in the first run.

Sample Report

HR.P.X (Line 11)

SubtreeFunctionDescendantCallsFunction Name
3322196109409922280975HR.P.X (Line 11)
Parents:



6037490199316940443219HR.P.G (Line 38)
-2715294-899070-1816224-4HR.P.F (Line 28)
Children:



1125489112548905HR.P.J (Line 10)
1102608110260805HR.P.I (Line 2)

The Parents and Children Difference Report for a function is accompanied by a Function Comparison Report, which shows the execution profile of the function for the first and second runs and the difference between them. This example is the Function Comparison Report for the function HR.P.X:

Sample Report

Elapsed Time (microsecs) for HR.P.X (Line 11) (20.1% of total regression)

HR.P.X (Line 11)First TraceInd%Second TraceInd%DiffDiff%
Function Elapsed Time (microsecs)199950926.9%309360824.9%1094099+54.7%
Descendants Elapsed Time (microsecs)409594355.1%632404050.9%2228097+54.4%
Subtree Elapsed Time (microsecs)609545281.9%941764875.7%3322196+54.5%
Function Calls925.0%1428.6%5+55.6%
Mean Function Elapsed Time (microsecs)222167.7
220972.0
-1195.7-0.5%
Mean Descendants Elapsed Time (microsecs)455104.8
451717.1
-3387.6-0.7%
Mean Subtree Elapsed Time (microsecs)677272.4
672689.1
-4583.3-0.7%

PK((PK|%AOEBPS/adfns_editions.htm Edition-Based Redefinition

19 Edition-Based Redefinition

Edition-based redefinition enables you to upgrade the database component of an application while it is in use, thereby minimizing or eliminating down time.

To upgrade an application while it is in use, you copy the database objects that comprise the application and redefine the copied objects in isolation. Your changes do not affect users of the application—they continue to run the unchanged application. When you are sure that your changes are correct, you make the upgraded application available to all users.

Using edition-based redefinition means using one or more of its component features. The features you use, and the down time, depend on these factors:

  • What kind of database objects you redefine

  • How available the database objects must be to users while you are redefining them

  • Whether you make the upgraded application available to some users while others continue to use the older version of the application

You always use the edition feature to copy the database objects and redefine the copied objects in isolation; that is why the procedure that this chapter describes for upgrading applications online is called edition-based redefinition.

If the object type of every object you will redefine is editionable (defined in "Editionable and Noneditionable Schema Object Types"), the edition is the only feature you use.

Table is not an editionable type. If you change the structure of one or more tables, you also use the editioning view feature.

If other users must be able to change data in the tables while you are changing their structure, you also use forward crossedition triggers. If the pre- and post-upgrade applications will be in ordinary use at the same time (hot rollover), you also use reverse crossedition triggers. Crossedition triggers are not a permanent part of the application—you drop them when all users are using the post-upgrade application.

Topics:

Editions

Editions are nonschema objects; as such, they do not have owners. Editions are created in a single namespace, and multiple editions can coexist in the database.

The database must have at least one edition. Every newly created or upgraded Oracle Database starts with one edition named ora$base.

Topics:

Editioned and Noneditioned Objects

An editioned object is a schema object that has both an editionable type and an editions-enabled owner. (A schema object that has an editionable type but not an editions-enabled owner is potentially editioned.) An edition can have its own copy of an editioned object, in which case only the copy is visible to the edition.

A noneditioned object is a schema object that has a noneditionable type. An edition cannot have its own copy of a noneditioned object. A noneditioned object is identical in, and visible to, all editions.

An editioned object is uniquely identified by its OBJECT_NAME, OWNER, and EDITION_NAME. A noneditioned object is uniquely identified by its OBJECT_NAME and OWNER—its EDITION_NAME is NULL. (Strictly speaking, the NAMESPACE of an object is also required to uniquely identify the object, but you can ignore this fact, because any statement that references the object implicitly or explicitly specifies its NAMESPACE.)

You can display the OBJECT_NAME, OWNER, and EDITION_NAME of an object with the static data dictionary views *_OBJECTS and *_OBJECTS_AE (described in Table 19-1).

You need not know the EDITION_NAME of an object to refer to that object (and if you do know it, you cannot specify it). The context of the reference implicitly specifies the edition. If the context is a data definition language (DDL) statement, the edition is the current edition of the session that issued the command (for information about the current edition, see "Current Edition and Session Edition"). If the context is source code, the edition is the one in which the object is actual (see "Inherited and Actual Objects").

Topics:

Editionable and Noneditionable Schema Object Types

These schema objects types are editionable:

  • SYNONYM

  • VIEW

  • All PL/SQL object types:

    • FUNCTION

    • LIBRARY

    • PACKAGE and PACKAGE BODY

    • PROCEDURE

    • TRIGGER

    • TYPE and TYPE BODY

All other schema object types are noneditionable. Table is an example of an noneditionable type.

A schema object of an editionable type is editioned if its owner is editions-enabled; otherwise, it is potentially editioned.

A schema object of a noneditionable type is always noneditioned, even if its owner is editions-enabled. A table is an example of an noneditioned object.


Note:

There is one exception to the rules: Although SYNONYM is an editionable type, a public synonym is a noneditioned object.

Rules for Editioned Objects

  • A noneditioned object cannot depend on an editioned object.

    For example:

    • A public synonym cannot refer to an editioned object.

    • A function-based index cannot depend on an editioned function.

    • A materialized view cannot depend on an editioned view.

    • A table cannot have a column of a user-defined data type (collection or ADT) whose owner is editions-enabled.

    • A noneditioned subprogram cannot have a static reference to a subprogram whose owner is editions-enabled.

    For the reason for this rule, see "Actualizing Referenced Objects".

  • An ADT cannot be both editioned and evolved.

    For information about type evolution, see Oracle Database Object-Relational Developer's Guide.

  • An editioned object cannot be the starting or ending point of a FOREIGN KEY constraint.

    The only editioned object that this rule affects is an editioned view. An editioned view can be either an ordinary view or an editioning view.

Enabling Editions for a User

To enable editions for a user, use the ENABLE EDITIONS clause of either the CREATE USER or ALTER USER statement.

The EDITIONS_ENABLED column of the static data dictionary view DBA_USERS or USER_USERS shows which users have editions enabled.

Enabling editions is retroactive and irreversible. When a user is editions-enabled, every editionable-type object that the user has owned or will own is an editioned object. You cannot enable editions for a user who owns a potentially editioned object with noneditioned dependents unless you specify FORCE:

ALTER USER user_name ENABLE EDITIONS FORCE;

The preceding statement enables editions for the specified user and invalidates noneditioned dependents of editioned objects.

FORCE is useful in the following situation: You must editions-enable users A and B. User A owns potentially editioned objects a1 and a2. User B owns potentially editioned objects b1 and b2. Object a1 depends on object b1. Object b2 depends on object a2. Editions-enable users A and B like this:

  1. Using FORCE, enable editions for user A:

    ALTER USER A ENABLE EDITIONS FORCE;
    

    Now a1 and a2 are editioned objects, and b2 (which depends on a2) is invalid.

  2. Enable editions for user B:

    ALTER USER B ENABLE EDITIONS;
    
  3. Recompile b2, using the appropriate ALTER statement with COMPILE. For a PL/SQL object, also specify REUSE SETTINGS.

    For example, if b2 is a procedure, use this statement:

    ALTER PROCEDURE b2 COMPILE REUSE SETTINGS
    

    For information about the ALTER statements for PL/SQL objects, see Oracle Database PL/SQL Language Reference.

    For information about the ALTER statements for SQL objects, see Oracle Database SQL Language Reference.

FORCE is unnecessary in the following situation: You must editions-enable user C, who owns potentially editioned object c1. Object c1 has dependent d1, a potentially editioned object owned by user D. User D owns no potentially editioned objects that have dependents owned by C. If you editions-enable D first, making d1 an editioned object, then you can editions-enable C without violating the rule that a noneditioned object cannot depend on an editioned object.

You cannot enable editions for a user who owns one or more evolved ADTs. Trying to do so causes error ORA-38820. If an ADT has no table dependents, you can use the ALTER TYPE RESET statement to reset its version to 1, so that it is no longer considered to be evolved. (Resetting the version of an ADT to 1 invalidates its dependents.)


See Also:


Creating an Edition

To create an edition, use the SQL statement CREATE EDITION.

You must create the edition as the child of an existing edition. The parent of the first edition created with a CREATE EDITION statement is ora$base. This statement creates the edition e2 as the child of ora$base:

CREATE EDITION e2

(Example 19-1 and others use the preceding statement.)

At Oracle Database 11g Release 2, an edition can have at most one child.

The descendents of an edition are its child, its child's child, and so on. The ancestors of an edition are its parent, its parent's parent, and so on. The root edition has no parent, and a leaf edition has no child.


See Also:

Oracle Database SQL Language Reference for information about the CREATE EDITION statement, including the privileges required to use it

Inherited and Actual Objects

Each database session uses exactly one edition at a time. Upon creation, a child edition inherits from its parent edition all editioned objects in the database that are visible in the parent edition. Each inherited object is visible in the child edition.

An inherited object is copied on change or actualized; that is, when a user of the child edition references an inherited object in a DDL statement (other than DROP), the inherited object is copied and the DDL statement affects only the copy—the actual object. The unchanged object in the parent edition is no longer visible in the child edition.


Note:

When the DDL statement CREATE OR REPLACE object has no effect, it does not actualize object (for details, see "Invalidation of Dependent Objects"). The DDL statement ALTER object COMPILE always actualizes object.

Example 19-1 creates a procedure named hello in the edition ora$base, and then creates the edition e2 as a child of ora$base. When e2 invokes hello, it invokes the inherited procedure. Then e2 changes hello, actualizing it. The procedure hello in the edition ora$base remains unchanged, and is no longer visible in e2. Now when e2 invokes hello, it invokes the actual procedure.

Example 19-1 Inherited and Actual Objects

  1. Create procedure in parent edition:

    CREATE OR REPLACE PROCEDURE hello IS
      BEGIN
        DBMS_OUTPUT.PUT_LINE('Hello, edition 1.');
      END hello;
    /
    
  2. Invoke procedure in parent edition:

    BEGIN hello(); END;
    /
    

    Result:

    Hello, edition 1.
     
    PL/SQL procedure successfully completed.
    
  3. Create child edition:

    CREATE EDITION e2;
    
  4. Use child edition:

    ALTER SESSION SET EDITION = e2;
    

    For information about ALTER SESSION SET EDITION, see "Changing Your Session Edition".

  5. In child edition, invoke procedure:

    BEGIN hello(); END;
    /
    

    Child edition inherits procedure from parent edition. Child edition invokes inherited procedure. Result:

    Hello, edition 1.
     
    PL/SQL procedure successfully completed.
    
  6. Change procedure in child edition:

    CREATE OR REPLACE PROCEDURE hello IS
      BEGIN
        DBMS_OUTPUT.PUT_LINE('Hello, edition 2.');
      END hello;
    /
    

    Child changes only its own copy of procedure. Child's copy is an actual object.

  7. Invoke procedure:

    BEGIN hello(); END;
    /
    

    Child invokes its own copy, the actual procedure:

    Hello, edition 2.
    
    PL/SQL procedure successfully completed.
    
  8. Return to parent:

    ALTER SESSION SET EDITION = ora$base;
    
  9. Invoke procedure and see that it has not changed:

    BEGIN hello(); END;
    /
    

    Result:

    Hello, edition 1.
     
    PL/SQL procedure successfully completed.
    

Topics:

Dropping Inherited Objects

If a user of the child edition drops an inherited object, that object is no longer visible in the child edition, but it continues to be visible in the parent edition.

Example 19-2 creates a procedure named goodbye in the edition ora$base, and then creates edition e2 as a child of ora$base. After e2 drops goodbye, it can no longer invoke it, but ora$base can still invoke it. (For more information about the DROP PROCEDURE statement, including the privileges required to use it, see Oracle Database PL/SQL Language Reference.)

Example 19-2 Dropping an Inherited Object

  1. Create procedure in edition ora$base:

    CREATE OR REPLACE PROCEDURE goodbye IS
      BEGIN
        DBMS_OUTPUT.PUT_LINE('Good-bye!');
      END goodbye;
    /
    
  2. Invoke procedure:

    BEGIN goodbye; END;
    /
    

    Result:

    Good-bye!
     
    PL/SQL procedure successfully completed.
    
  3. Create edition e2 as a child of ora$base:

    CREATE EDITION e2;
    
  4. Use edition e2:

    ALTER SESSION SET EDITION = e2;
    

    ALTER SESSION SET EDITION must be a top-level SQL statement. For more information, see "Changing Your Session Edition".

  5. In e2, invoke procedure:

    BEGIN goodbye; END;
    /
    

    e2 invokes inherited procedure:

    Good-bye!
     
    PL/SQL procedure successfully completed.
    
  6. In e2, drop procedure:

    DROP PROCEDURE goodbye;
    
  7. In e2, try to invoke dropped procedure:

    BEGIN goodbye; END;
    /
    

    Result:

    BEGIN goodbye; END;
          *
    ERROR at line 1:
    ORA-06550: line 1, column 7:
    PLS-00201: identifier 'GOODBYE' must be declared
    ORA-06550: line 1, column 7:
    PL/SQL: Statement ignored
    
  8. Return to parent:

    ALTER SESSION SET EDITION = ora$base;
    
  9. In parent, invoke procedure:

    BEGIN goodbye; END;
    /
    

    Result:

    Good-bye!
     
    PL/SQL procedure successfully completed.
    

Because e2 dropped the procedure goodbye:

  • Its descendents do not inherit the procedure goodbye.

  • No object named goodbye is visible in e2, so e2 can create an object named goodbye of any editionable type. If e2 creates this object, the descendents of e2 inherit that object.

In Example 19-3, e2 creates a function named goodbye and then an edition named e3 as a child of e2. When e3 tries to invoke the procedure goodbye (which e2 dropped), an error occurs, but e3 successfully invokes the function goodbye (which e2 created).

Example 19-3 Creating an Object with the Name of a Dropped Inherited Object

  1. Return to e2:

    ALTER SESSION SET EDITION = e2;
    

    For information about ALTER SESSION SET EDITION, see "Changing Your Session Edition".

  2. In e2, create function named goodbye:

    CREATE OR REPLACE FUNCTION goodbye
      RETURN BOOLEAN
    IS
    BEGIN
      RETURN(TRUE);
    END goodbye;
    /
    
  3. Create edition e3:

    CREATE EDITION e3 AS CHILD OF e2;
    
  4. Use edition e3:

    ALTER SESSION SET EDITION = e3;
    
  5. In e3, try to invoke procedure goodbye:

    BEGIN
      goodbye;
    END;
    /
    

    Result:

      goodbye;
      *
    ERROR at line 2:
    ORA-06550: line 2, column 3:
    PLS-00221: 'GOODBYE' is not a procedure or is undefined
    ORA-06550: line 2, column 3:
    PL/SQL: Statement ignored
    
  6. In e3, invoke function goodbye:

    BEGIN
      IF goodbye THEN
        DBMS_OUTPUT.PUT_LINE('Good-bye!');
      END IF;
    END;
    /
    

    Result:

    Good-bye!
     
    PL/SQL procedure successfully completed.
    

Actualizing Referenced Objects

When a referenced object is actualized in an edition, all of its dependents (direct and indirect) that are not yet actualized in that edition become actualized in that edition in an invalid state. Therefore, an editioned object cannot have dependents that cannot be actualized. In other words, a noneditioned object cannot depend on an editioned object (for examples, see "Rules for Editioned Objects").

When an invalid object is referenced, the database automatically validates it, which requires name resolution. The database looks for the object name first in the current edition, then in the parent edition, and so on.


See Also:

Chapter 18, "Schema Object Dependency," for general information about dependencies among schema objects, including invalidation, revalidation, and name resolution

Making an Edition Available to Some Users

As the creator of the edition, you automatically have the USE privilege WITH GRANT OPTION on it. To grant the USE privilege on the edition to other users, use the SQL statement GRANT USE ON EDITION. For information about the GRANT statement, see Oracle Database SQL Language Reference.

Making an Edition Available to All Users

To make an edition available to all users, either:

  • Grant the USE privilege on the edition to PUBLIC:

    GRANT USE ON EDITION edition_name TO PUBLIC
    

    For information about the GRANT statement, see Oracle Database SQL Language Reference.

  • Make the edition the database default edition:

    ALTER DATABASE DEFAULT EDITION = edition_name
    

    This has the side effect of granting the USE privilege on edition_name to PUBLIC.

    For information about the ALTER DATABASE statement, see Oracle Database SQL Language Reference.

Current Edition and Session Edition

Each database session uses exactly one edition at a time. The edition that a database session is using at any one time is called its current edition. When a database session begins, its current edition is its session edition, which is the edition in which it begins. If you change the session edition, the current edition changes to the same thing. However, there are situations in which the current edition and session edition differ.

Topics:

Your Initial Session Edition

When you connect to the database, you can specify your initial session edition. Your initial session edition can be any edition on which you have the USE privilege. To see the names of the editions that are available to you, use this query:

SELECT EDITION_NAME FROM ALL_EDITIONS;

How you specify your initial session edition at connection time depends on how you connect to the database—see the documentation for your interface.


See Also:


As of Oracle Database 11g Release 2 (11.2.0.2), if you do not specify your session edition at connection time, then:

  • If you use a database service to connect to the database, and an initial session edition was specified for that service, then the initial session edition for the service is your initial session edition.

  • Otherwise, your initial session edition is the database default edition.

As of Release 11.2.0.2, when you create or modify a database service, you can specify its initial session edition.

To create or modify a database service, Oracle recommends using the srvctl add service or srvctl modify service command. To specify the default initial session edition of the service, use the -t option.

Alternatively, you can create or modify a database service with the DBMS_SERVICE.CREATE_SERVICE or DBMS_SERVICE.MODIFY_SERVICE procedure, and specify the default initial session edition of the service with the EDITION attribute.


Note:

As of Oracle Database 11g Release 2, the DBMS_SERVICE.CREATE_SERVICE and DBMS_SERVICE.MODIFY_SERVICE procedures are deprecated in databases managed by Oracle Clusterware and Oracle Restart.


See Also:


Changing Your Session Edition

After connecting to the database, you can change your session edition with the SQL statement ALTER SESSION SET EDITION. You can change your session edition to any edition on which you have the USE privilege. When you change your session edition, your current edition changes to the same thing.

These statements from Example 19-1 and Example 19-2 change the session edition (and current edition) first to e2 and later to ora$base:

ALTER SESSION SET EDITION = e2
...
ALTER SESSION SET EDITION = ora$base

Note:

ALTER SESSION SET EDITION must be a top-level SQL statement. To defer an edition change (in a logon trigger, for example), use the DBMS_SESSION.SET_EDITION_DEFERRED procedure.


See Also:


Displaying the Names of the Current and Session Editions

This statement returns the name of the current edition:

SELECT SYS_CONTEXT('USERENV', 'CURRENT_EDITION_NAME') FROM DUAL;

This statement returns the name of the session edition:

SELECT SYS_CONTEXT('USERENV', 'SESSION_EDITION_NAME') FROM DUAL;

See Also:

Oracle Database SQL Language Reference for more information about the SYS_CONTEXT function

When the Current Edition Might Differ from the Session Edition

The current edition might differ from the session edition in these situations:

Example 19-4 creates a function that returns the names of the session edition and current edition. Then it creates a child edition, which invokes the function twice. The first time, the session edition and current edition are the same. The second time, they are not, because a different edition is passed as a parameter to the DBMS_SQL.PARSE procedure.

Example 19-4 Current Edition Differs from Session Edition

  1. Create function that returns the names of the session edition and current edition:

    CREATE OR REPLACE FUNCTION session_and_current_editions
      RETURN VARCHAR2
    IS
    BEGIN
      RETURN
      'Session: '|| SYS_CONTEXT('USERENV', 'SESSION_EDITION_NAME') ||
      ' / ' ||
      'Current: '|| SYS_CONTEXT('USERENV', 'CURRENT_EDITION_NAME');
    END session_and_current_editions;
    /
    
  2. Create child edition:

    CREATE EDITION e2 AS CHILD OF ora$base;
    
  3. Use child edition:

    ALTER SESSION SET EDITION = e2;
    
  4. Invoke function:

    BEGIN
      DBMS_OUTPUT.PUT_LINE (session_and_current_editions());
    END;
    /
    

    Result:

    Session: E2 / Current: E2
     
    PL/SQL procedure successfully completed.
    
  5. Invoke function again:

    DECLARE
      c     NUMBER := DBMS_SQL.OPEN_CURSOR();
      v     VARCHAR2(200);
      dummy NUMBER;
      stmt  CONSTANT VARCHAR2(32767)
        := 'SELECT session_and_current_editions() FROM DUAL';
    BEGIN
      DBMS_SQL.PARSE (c => c,
                      statement => stmt,
                      language_flag => DBMS_SQL.NATIVE,
                      edition => 'ora$base');
     
      DBMS_SQL.DEFINE_COLUMN (c, 1, v, 200);
      dummy := DBMS_SQL.EXECUTE_AND_FETCH (c, true);
      DBMS_SQL.COLUMN_VALUE (c, 1, v);
      DBMS_SQL.CLOSE_CURSOR(c);
      DBMS_OUTPUT.PUT_LINE (v);
    END;
    /
    

    Result:

    Session: E2 / Current: ORA$BASE
     
    PL/SQL procedure successfully completed.
    

Retiring an Edition

After making a new edition (an upgraded application) available to all users, you want to retire the old edition (the original application), so that no user except SYS can use the retired edition.


Note:

If the old edition is the database default edition, make another edition the database default edition before you retire the old edition:
ALTER DATABASE DEFAULT EDITION = edition_name

For information about the ALTER DATABASE statement, see Oracle Database SQL Language Reference.


To retire an edition, you must revoke the USE privilege on the edition from every grantee. To list the grantees, use this query, where :e is a placeholder for the name of the edition to be dropped:

SELECT GRANTEE, PRIVILEGE
FROM DBA_TAB_PRIVS
WHERE TABLE_NAME = :e
/

For information about the REVOKE statement, see Oracle Database SQL Language Reference.

Dropping an Edition


Note:

If the edition includes crossedition triggers, see "Dropping the Crossedition Triggers" before you drop the edition.

To drop an edition, use the DROP EDITION statement, described in Oracle Database SQL Language Reference. If the edition has actual objects, you must specify the CASCADE clause, which drops the actual objects.

If a DROP EDITION edition CASCADE statement is interrupted before finishing normally (from a power failure, for example), the static data dictionary view *_EDITIONS shows that the value of USABLE for edition is NO. The only operation that you can perform on such an unusable edition is DROP EDITION CASCADE.

You drop an edition in these situations:

  • You want to roll back the application upgrade.

  • (Optional) You have retired the edition.

You can drop an edition only if all of these statements are true:

  • The edition is either the root edition or a leaf edition.

  • If the edition is the root, it has no objects that its descendents inherit. (That is, each object inherited from the root edition was either actualized or dropped.)

  • The edition is not in use (that is, it is not the current edition or session edition of a session).

  • The edition is not the database default edition.

To explicitly actualize an inherited object in the child edition:

  1. Make the child edition your session edition.

    For instructions, see "Changing Your Session Edition".

  2. Recompile the object, using the appropriate ALTER statement with COMPILE. For a PL/SQL object, also specify REUSE SETTINGS.

    For example, this statement actualizes the procedure p1:

    ALTER PROCEDURE p1 COMPILE REUSE SETTINGS
    

    For information about the ALTER statements for PL/SQL objects, see Oracle Database PL/SQL Language Reference.

    For information about the ALTER statements for SQL objects, see Oracle Database SQL Language Reference.


See Also:


Editioning Views

The owner of an editioning view must be editions-enabled before the editioning view is created.

On a noneditioning view, the only type of trigger that you can define is an INSTEAD OF trigger. On an editioning view, you can define every type of trigger that you can define on a table (except crossedition triggers, which are temporary, and INSTEAD OF triggers). Therefore, and because they can be editioned, editioning views let you treat their base tables as if the base tables were editioned. However, you cannot add indexes or constraints to an editioning view; if your upgraded application requires new indexes or constraints, you must add them to the base table.

An editioning view selects a subset of the columns from a single base table and, optionally, provides aliases for them. In providing aliases, the editioning view maps physical column names (used by the base table) to logical column names (used by the application). An editioning view is like an API for a table.

There is no performance penalty for accessing a table through an editioning view, rather than directly. That is, if a SQL SELECT, INSERT, UPDATE, DELETE, or MERGE statement uses one or more editioning views, one or more times, and you replace each editioning view name with the name of its base table and adjust the column names if necessary, performance does not change.

The static data dictionary view *_EDITIONING_VIEWS describes every editioning view in the database that is visible (actual or inherited) in the session edition. *_EDITIONING_VIEWS_AE describes every actual object in every editioning view in the database, in every edition.

Topics:


See Also:

Oracle Database Reference for more information about the static data dictionary views *_EDITIONING_VIEWS and *_EDITIONING_VIEWS_AE.

Creating an Editioning View

To create an editioning view, use the SQL statement CREATE VIEW with the keyword EDITIONING. To make the editioning view read-only, specify WITH READ ONLY; to make it read-write, omit WITH READ ONLY.

If an editioning view is read-only, users of the unchanged application can see the data in the base table, but cannot change it. The base table has semi-availability. Semi-availability is acceptable for applications such as online dictionaries, which users read but do not change. Make the editioning view read-only if you do not define crossedition triggers on the base table.

If an editioning view is read-write, users of the unchanged application can both see and change the data in the base table. The base table has maximum availability. Maximum availability is required for applications such as online stores, where users submit purchase orders. If you define crossedition triggers on the base table, make the editioning view read-write.

Because an editioning view must do no more than select a subset of the columns from the base table and provide aliases for them, the CREATE VIEW statement that creates an editioning view has restrictions. Violating the restrictions causes the creation of the view to fail, even if you specify FORCE.


See Also:

Oracle Database SQL Language Reference for more information about using the CREATE VIEW statement to create editioning views, including the restrictions

Partition-Extended Editioning View Names

An editioning view defined on a partitioned table can have a partition-extended name, with partition and subpartition names that refer to the partitions and subpartitions of the base table.

The data manipulation language (DML) statements that support partition-extended table names also support partition-extended editioning view names. These statements are:

  • DELETE

  • INSERT

  • SELECT

  • UPDATE


See Also:

Oracle Database SQL Language Reference for information about referring to partitioned tables

Changing the 'Write-ability' of an Editioning View

To change an existing editioning view from read-only to read-write, use the SQL statement ALTER VIEW READ WRITE. To change an existing editioning view from read-write to read-only, use the SQL statement ALTER VIEW READ ONLY.


See Also:

Oracle Database SQL Language Reference for more information about the ALTER VIEW statement

Replacing an Editioning View

To replace an editioning view, use the SQL statement CREATE VIEW with the OR REPLACE clause and the keyword EDITIONING.

You can replace an editioning view only with another editioning view. Any triggers defined on the replaced editioning view are retained.

Dropping or Renaming the Base Table

If you drop or rename the base table on which an editioning view is defined, the editioning view is not dropped, but the editioning view and its dependents become invalid. However, any triggers defined on the editioning view remain.

Adding Indexes and Constraints to the Base Table

If your upgraded application requires new indexes or constraints, you must add them to the base table. You cannot add them to the editioning view.

If the new indexes might negatively impact the old edition (the original application), make them invisible. In the crossedition triggers that must use the new indexes, specify them in INDEX hints.

When all users are using only the upgraded application:

  • If the new indexes were used only by the crossedition triggers, drop them.

  • If the new indexes are helpful in the upgraded application, make them visible.


See Also:


SQL Optimizer Index Hints

SQL optimizer index hints are specified in terms of the logical names of the columns participating in the index. Any SQL optimizer index hints specified on an editioning view using logical column names must be mapped to an index on the corresponding physical column in the base table.


See Also:

Oracle Database SQL Language Reference for information about using hints

Crossedition Triggers

The most important difference between crossedition triggers and noncrossedition triggers is how they interact with editions. A crossedition trigger is visible only in the edition in which it is actual, never in a descendent edition. Forward crossedition triggers move data from columns used by the old edition to columns used by the new edition; reverse crossedition triggers do the reverse.

Other important differences are:

  • Crossedition triggers can be ordered with triggers defined on other tables, while noncrossedition triggers can be ordered only with other triggers defined on the same table.

  • Crossedition triggers are temporary—you drop them after you have made the restructured tables available to all users.

Topics:


See Also:

Oracle Database PL/SQL Language Reference for general information about triggers

Forward Crossedition Triggers

The DML changes that you make to the table in the post-upgrade edition are written only to new columns or new tables, never to columns that users of pre-upgrade (ancestor) editions might be reading or writing. However, if the user of an ancestor edition changes the table data, the editioning view that you see must accurately reflect these changes. This is accomplished with forward crossedition triggers.

A forward crossedition trigger defines a transform, which is a rule for transforming an old row to one or more new rows. An old row is a row of data in the pre-upgrade representation. A new row is a row of data in the post-upgrade representation. The name of the trigger refers to the trigger itself and to the transform that the trigger defines.

Reverse Crossedition Triggers

If the pre- and post-upgrade editions will be in ordinary use at the same time (hot rollover), use reverse crossedition triggers to ensure that when users of the post-upgrade edition make changes to the table data, the changes are accurately reflected in the pre-upgrade editions.

Crossedition Trigger Interaction with Editions

The most important difference between crossedition triggers and noncrossedition triggers is how they interact with editions.

In this topic, the current edition is the edition in which the triggering DML statement runs. The current edition might differ from the session edition (for details, see "When the Current Edition Might Differ from the Session Edition").

Which Triggers Are Visible

Editions inherit noncrossedition triggers in the same way that they inherit other editioned objects (see "Inherited and Actual Objects").

Editions do not inherit crossedition triggers. A crossedition trigger might fire in response to a DML statement that another edition runs, but its name is visible only in the edition in which it was created. Therefore, an edition can reuse the name of a crossedition trigger created in an ancestor edition. Reusing the name of a crossedition trigger does not change the conditions under which the older trigger fires.

Crossedition triggers that appear in static data dictionary views are actual objects in the current edition.

What Kind of Triggers Can Fire

What kind of triggers can fire depends on the category of the triggering DML statement. The categories are:


Note:

The APPEND hint on a SQL INSERT statement does not prevent crossedition triggers from firing. For information about the APPEND hint, see Oracle Database SQL Language Reference.

Forward Crossedition Trigger SQL

Forward crossedition trigger SQL is SQL that is executed in either of these ways:

  • Directly from the body of a forward crossedition trigger

    This category includes SQL in an invoked subprogram only if the subprogram is local to the forward crossedition trigger.

  • By invoking the DBMS_SQL.PARSE procedure with a non-NULL value for the apply_crossedition_trigger parameter

    The only valid non-NULL value for the apply_crossedition_trigger parameter is the unqualified name of a forward crossedition trigger. For more information about the DBMS_SQL.PARSE procedure, see Oracle Database PL/SQL Packages and Types Reference.

If a forward crossedition trigger invokes a subprogram in another compilation unit, the SQL in the subprogram is forward crossedition trigger SQL only if it is invoked by the DBMS_SQL.PARSE procedure with a non-NULL value for the apply_crossedition_trigger parameter.

Forward crossedition trigger SQL can fire only triggers that satisfy all of these conditions:

  • They are forward crossedition triggers.

  • They were created either in the current edition or in a descendent of the current edition.

  • They explicitly follow the running forward crossedition trigger.

Reverse Crossedition Trigger SQL

Reverse crossedition trigger SQL is SQL that is executed directly from the body of a reverse crossedition trigger. This category includes SQL in an invoked subprogram only if the subprogram is local to the reverse crossedition trigger.

Reverse crossedition trigger SQL can fire only triggers that satisfy all of these conditions:

  • They are reverse crossedition triggers.

  • They were created either in the current edition or in an ancestor of the current edition.

  • They explicitly precede the running reverse crossedition trigger.

Application SQL

Application SQL is all SQL except crossedition trigger SQL, including these DML statements:

  • Dynamic SQL DML statements coded with the DBMS_SQL package (for information about these statements, see Oracle Database PL/SQL Language Reference).

  • DML statements executed by Java stored procedures and external procedures (even when these procedures are invoked by CALL triggers)

Application SQL fires both noncrossedition and crossedition triggers, according to these rules:

Kind of TriggerConditions Under Which Trigger Can Fire
NoncrosseditionTrigger is both visible and enabled in the current edition.
Forward crosseditionTrigger was created in a descendent of the current edition.
Reverse crosseditionTrigger was created either in the current edition or in an ancestor of the current edition.

Firing Order

For a trigger to fire in response to a specific DML statement, the trigger must:

  • Be the right kind (see "What Kind of Triggers Can Fire")

  • Satisfy the selection criteria (for example, the type of DML statement and the WHEN clause)

  • Be enabled

For the triggers that meet these requirements, firing order depends on:


See Also:

Oracle Database PL/SQL Language Reference for general information about trigger firing order

FOLLOWS and PRECEDES Clauses

When triggers A and B are to be fired at the same timing point, A fires before B fires if either of these is true:

  • A explicitly precedes B.

  • B explicitly follows A.

This rule is independent of conditions such as:

  • Whether the triggers are enabled or disabled

  • Whether the columns specified in the UPDATE OF clause are modified

  • Whether the WHEN clauses are satisfied

  • Whether the triggers are associated with the same kinds of DML statements (INSERT, UPDATE, or DELETE)

  • Whether the triggers have overlapping timing points

The firing order of triggers that do not explicitly follow or precede each other is unpredictable.

Trigger Type and Edition

For each timing point associated with a triggering DML statement, eligible triggers fire in this order. In categories 1 through 3, FOLLOWS relationships apply; in categories 4 and 5, PRECEDES relationships apply.

  1. Noncrossedition triggers

  2. Forward crossedition triggers created in the current edition

  3. Forward crossedition triggers created in descendents of the current edition, in the order that the descendents were created (child, grandchild, and so on)

  4. Reverse crossedition triggers created in the current edition

  5. Reverse crossedition triggers created in the ancestors of the current edition, in the reverse order that the ancestors were created (parent, grandparent, and so on)

Crossedition Trigger Execution

A crossedition trigger runs using the edition in which it was created. Any code that the crossedition trigger calls (including package references, PL/SQL subprogram calls, and SQL statements) also runs in the edition in which the crossedition trigger was created.

If a PL/SQL package is actual in multiple editions, then the package variables and other state are private in each edition, even within a single session. Because each crossedition trigger and the code that it calls run using the edition in which the crossedition trigger was created, the same session can instantiate two or more versions of the package, with the same name.

Creating a Crossedition Trigger

To create a crossedition trigger, you must be editions-enabled (for information about enabling editions for a user, see "Enabling Editions for a User").

Create a crossedition trigger with the SQL statement CREATE TRIGGER, observing these rules:

  • A crossedition trigger must be defined on a table, not a view.

  • A crossedition trigger must be a DML trigger (simple or compound).

    The DML statement in a crossedition trigger body can be either a static SQL statement (described in Oracle Database PL/SQL Language Reference) or a native dynamic SQL statement (described in Oracle Database PL/SQL Language Reference).

  • A crossedition trigger is forward unless you specify REVERSE. (Specifying FORWARD is optional.)

  • The FOLLOWS clause is allowed only when creating a forward crossedition trigger or a noncrossedition trigger. (The FOLLOWS clause indicates that the trigger being created is to fire after the specified triggers fire.)

  • The PRECEDES clause is allowed only when creating a reverse crossedition trigger. (The PRECEDES clause indicates that the trigger being created is to fire before the specified triggers fire.)

  • The triggers specified in the FOLLOWS or PRECEDES clause must exist, but need not be enabled or successfully compiled.

  • Like a noncrossedition trigger, a crossedition trigger is created in the enabled state unless you specify DISABLE. (Specifying ENABLE is optional.)


    Tip:

    Create crossedition triggers in the disabled state, and enable them after you are sure that they compile successfully. If you create them in the enabled state, and they fail to compile, the failure affects users of the existing application.

  • The operation in a crossedition trigger body must be idempotent (that is, performing the operation multiple times is redundant; it does not change the result).


See Also:

Oracle Database PL/SQL Language Reference for more information about using the CREATE TRIGGER statement to create crossedition triggers

Coding the Forward Crossedition Trigger Body

The operation in the body of a forward crossedition trigger must be idempotent, because it is impossible to predict:

  • The context in which the body will first run for an old row.

    The possibilities are:

  • How many times the body will run for each old row.

Topics:

Handling Data Transformation Collisions

If a forward crossedition trigger populates a new table (rather than new columns of a table), its body must handle data transformation collisions.

For example, suppose that a column of the new table has a UNIQUE constraint. A serendipitous change fires the forward crossedition trigger, which inserts a row in the new table. Later, another serendipitous change fires the forward crossedition trigger, or you apply the transform defined by the trigger. The trigger tries to insert a row in the new table, violating the UNIQUE constraint.

If your collision-handling strategy depends on why the trigger is running, you can determine the reason with the function APPLYING_CROSSEDITION_TRIGGER. When called directly from a trigger body, this function returns the BOOLEAN value TRUE if the trigger is running because of a serendipitous change and FALSE if the trigger is running because you are applying the transform. (APPLYING_CROSSEDITION_TRIGGER is defined in the package DBMS_STANDARD. It has no parameters.)

To ignore collisions and insert the rows that do not collide with existing rows, put the IGNORE_ROW_ON_DUPKEY_INDEX hint in the INSERT statement.

If you do not want to ignore such collisions, but want to know where they occur so that you can handle them, put the CHANGE_DUPKEY_ERROR_INDEX hint in the INSERT or UPDATE statement, specifying either an index or set of columns. Then, when a unique key violation occurs for that index or set of columns, ORA-38911 is reported instead of ORA-00001. You can write an exception handler for ORA-38911.


Note:

Although they have the syntax of hints, IGNORE_ROW_ON_DUPKEY_INDEX and CHANGE_DUPKEY_ERROR_INDEX are mandates. The optimizer always uses them.

Example 19-5 creates a crossedition trigger that uses the APPLYING_CROSSEDITION_TRIGGER function and the IGNORE_ROW_ON_DUPKEY_INDEX and CHANGE_DUPKEY_ERROR_INDEX hints to handle data transformation collisions. The trigger transforms old rows in table1 to new rows in table2. The tables were created as follows:

CREATE TABLE table1 (key NUMBER, value VARCHAR2(20));

CREATE TABLE table2 (key NUMBER, value VARCHAR2(20), last_updated TIMESTAMP);
CREATE UNIQUE INDEX i2 on table2(key);

Example 19-5 Crossedition Trigger that Handles Data Transformation Collisions

CREATE OR REPLACE TRIGGER trigger1
  BEFORE INSERT OR UPDATE ON table1
  FOR EACH ROW
  CROSSEDITION
DECLARE
  row_already_present  EXCEPTION;
  PRAGMA EXCEPTION_INIT(row_already_present, -38911);
BEGIN
  IF APPLYING_CROSSEDITION_TRIGGER THEN
    /* Trigger is running because of serendipitous change.
       Insert new row into table2 unless it is already there. */
    INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(table2(key)) */
    INTO table2
    VALUES(:new.key, :new.value, to_date('1900-01-01', 'YYYY-MM-DD'));
  ELSE
    /* Trigger is running because you are applying transform.
       If tranform has not yet inserted new row in table2, insert new row;
       otherwise, update new row. */
    BEGIN
      INSERT /*+ CHANGE_DUPKEY_ERROR_INDEX(table2(key)) */
      INTO table2
      VALUES(:new.key, :new.value, SYSTIMESTAMP);
    EXCEPTION WHEN row_already_present THEN
      UPDATE table2
      SET value = :new.value, last_updated = SYSTIMESTAMP
      WHERE key = :new.key;
    END;
  END IF;
END;
/

See Also:


Handling Changes to Other Tables

If the body of a forward crossedition trigger includes explicit SQL statements that change tables other than the one on which the trigger is defined, and if the rows of those tables do not have a one-to-one correspondence with the rows of the table on which the trigger is defined, then the body code must implement a locking mechanism that correctly handles these situations:

  • Two or more users of ancestor editions simultaneously issue DML statements for the table on which the trigger is defined.

  • At least one user of an ancestor edition issues a DML statement for the table on which the trigger is defined.

Transforming Data from Pre- to Post-Upgrade Representation

After redefining the database objects that comprise the application that you are upgrading (in the new edition), you must transform the application data from its pre-upgrade representation (in the old edition) to its post-upgrade representation (in the new edition). The rules for this transformation are called transforms, and they are defined by forward crossedition triggers. (For general information about forward crossedition triggers, see "Forward Crossedition Triggers".)

Some old rows might have been transformed to new rows by serendipitous changes; that is, by changes that users of the pre-upgrade application made, which fired forward crossedition triggers. However, any rows that were not transformed by serendipitous changes are still in their pre-upgrade representation. To ensure that all old rows are transformed to new rows, you must apply the transforms that you defined on the tables that store the application data.

There are two ways to apply a transform:

  • Fire the trigger that defines the transform on every row of the table, one row at a time.

  • Instead of firing the trigger, run a SQL statement that does what the trigger would do, but faster, and then fire any triggers that follow that trigger.

    This second way is recommended if you have replaced an entire table or created a new table.

For either way of applying the transform, invoke either the DBMS_SQL.PARSE procedure or the subprograms in the DBMS_PARALLEL_EXECUTE package. The latter is recommended if you have a lot of data. The subprograms enable you to incrementally update the data in a large table in parallel, in two high-level steps:

  1. Group sets of rows in the table into smaller chunks.

  2. Apply the desired UPDATE statement to the chunks in parallel, committing each time you have finished processing a chunk.

The advantages are:

  • You lock only one set of rows at a time, for a relatively short time, instead of locking the entire table.

  • You do not lose work that has been done if something fails before the entire operation finishes.

For both the DBMS_SQL.PARSE procedure and the DBMS_PARALLEL_EXECUTE subprograms, the actual parameter values for apply_crossedition_trigger, fire_apply_trigger, and sql_stmt are the same:

  • For apply_crossedition_trigger, specify the name of the forward crossedition trigger that defines the transform to be applied.

  • To fire the trigger on every row of the table, one row at a time:

    • For the value of fire_apply_trigger, specify TRUE.

    • For sql_stmt, supply a SQL statement whose only significant effect is to select the forward crossedition trigger to be fired; for example, an UPDATE statement that sets some column to its own existing value in each row.

  • To run a SQL statement that does what the trigger would do, and then fire any triggers that follow that trigger:

    • For the value of fire_apply_trigger, specify FALSE.

    • For sql_stmt, supply a SQL statement that does what the forward crossedition trigger would do, but faster—for example, a PL/SQL anonymous block that calls one or more PL/SQL subprograms.


See Also:


Preventing Lost Updates

To prevent lost updates when applying a transform, use this procedure:

  1. Enable crossedition triggers.

  2. Wait until pending changes to the affected tables are either committed or rolled back.

    Use the procedure DBMS_UTILITY.WAIT_ON_PENDING_DML, described in Oracle Database PL/SQL Packages and Types Reference.

  3. Apply the transform.


Note:

This scenario, where the forward crossedition trigger changes only the table on which it is defined, is sufficient to illustrate the risk. Suppose that Session One issues an UPDATE statement against the table when the crossedition trigger is not yet enabled; and that Session Two then enables the crossedition trigger and immediately applies the transformation.

A race condition can now occur when both Session One and Session Two will change the same row (row n). Chance determines which session reaches row n first. Both updates succeed, even if the session that reaches row n second must wait until the session that reached it first commits its change and releases its lock.

The problem occurs when Session Two wins the race. Because its SQL statement was compiled after the trigger was enabled, the program that implements the statement also implements the trigger action; therefore, the intended post-upgrade column values are set for row n. Now Session One reaches row n, and because its SQL statement was compiled before the trigger was enabled, the program that implements the statement does not implement the trigger action. Therefore, the values that Session Two set in the post-upgrade columns do not change—they reflect the values that the source columns had before Session One updated row n. That is, the intended side-effect of Session One's update is lost.


Dropping the Crossedition Triggers

To drop a crossedition trigger, use the DROP TRIGGER statement, described in Oracle Database PL/SQL Language Reference. Alternatively, you can drop crossedition triggers by dropping the edition in which they are actual, by using the DROP EDITION statement with the CASCADE clause. For information about dropping editions, see "Dropping an Edition".

You drop crossedition triggers in these situations:

  • You are rolling back the application upgrade (dropping the post-upgrade edition).

    Before dropping the post-upgrade edition, you must disable or drop any constraints on the new columns.

  • You have finished the application upgrade and made the post-upgrade edition available to all users.

    When all sessions are using the post-upgrade edition, you can drop the forward crossedition triggers. However, before dropping the reverse crossedition triggers, you must disable or drop any constraints on the old columns.

To disable or drop constraints, use the ALTER TABLE statement with the DISABLE CONSTRAINT or DROP CONSTRAINT clause. For information about the ALTER TABLE statement, see Oracle Database SQL Language Reference.

Displaying Information About Editions, Editioning Views, and Crossedition Triggers

Table 19-1 and Table 19-2 describe the static data dictionary views that display information about editions and editioning views, respectively. For more information about a view in either table, see Oracle Database Reference.

The static data dictionary views that display information about triggers are described in Oracle Database Reference. Crossedition triggers that appear in static data dictionary views are actual objects in the session edition.

Child cursors cannot be shared if the set of crossedition triggers that might run differs. The dynamic performance views V$SQL_SHARED_CURSOR and GV$SQL_SHARED_CURSOR have a CROSSEDITION_TRIGGER_MISMATCH column that tells whether this is true. For information about V$SQL_SHARED_CURSOR, see Oracle Database Reference.

Table 19-1 *_ Dictionary Views with Edition Information

ViewDescription

*_EDITIONS

Describe every edition in the database.

*_EDITION_COMMENTS

Show the comments associated with every edition in the database.

*_OBJECTS

Describe every object in the database that is visible (actual or inherited) in the session edition.

*_OBJECTS_AE

Describe every actual object in the database, in every edition.

*_ERRORS

Describe every error in the database in the session edition.

*_ERRORS_AE

Describe every error in the database, in every edition.

*_USERS

Describe every user in the database. Useful for showing which users have editions enabled.

*_SERVICES

Describe every service in the database.

As of Oracle Database 11g Release 2 (11.2.0.2), the EDITIONS column shows the default initial session edition.



Note:

*_OBJECTS and *_OBJECTS_AE include dependent objects that are invalidated by operations in Table 18-2 only after one of the following:

Table 19-2 *_ Dictionary Views with Editioning View Information

ViewDescription

*_VIEWS

Describes every view in the database that is visible (actual or inherited) in the session edition, including editioning views.

*_EDITIONING_VIEWS

Describes every editioning view in the database that is visible (actual or inherited) in the session edition. Useful for showing relationships between editioning views and their base tables. Join with *_OBJECTS_AE for additional information.

*_EDITIONING_VIEWS_AE

Describes every actual object in every editioning view in the database, in every edition.

*_EDITIONING_VIEW_COLS

Describes the columns of every editioning view in the database that is visible (actual or inherited) in the session edition. Useful for showing relationships between the columns of editioning views and the table columns to which they map. Join with *_OBJECTS_AE, *_TAB_COL, or both, for additional information.

*_EDITIONING_VIEW_COLS_AE

Describes the columns of every editioning view in the database, in every edition.


Each row of *_EDITIONING_VIEWS matches exactly one row of *_VIEWS, and each row of *_VIEWS that has EDITIONING_VIEW = 'Y' matches exactly one row of *_EDITIONING_VIEWS. Therefore, in this example, the WHERE clause is redundant:

SELECT ...
  FROM DBA_EDITIONING_VIEWS INNER JOIN DBA_VIEWS
  USING (OWNER, VIEW_NAME)
  WHERE EDITIONING_VIEW = 'Y'
  AND ...

The row of *_VIEWS that matches a row of *_EDITIONING_VIEWS has EDITIONING_VIEW = 'Y' by definition. Conversely, no row of *_VIEWS that has EDITIONING_VIEW = 'N' has a counterpart in *_ EDITIONING_VIEWS.

Using Edition-Based Redefinition to Upgrade an Application

To use edition-based redefinition to upgrade your application online, you must first ready your application:

  1. Editions-enable the appropriate users.

    For instructions, see "Enabling Editions for a User".

    The reason for this step is that only editions-enabled users can own editioning views, which you create in the next step.

  2. Prepare your application to use editioning views.

    For instructions, see "Preparing Your Application to Use Editioning Views".

With the editioning views in place, you can use edition-based redefinition to upgrade your application online as often as necessary. For each upgrade:

Topics:

Preparing Your Application to Use Editioning Views

An application that uses one or more tables must cover each table with an editioning view. An editioning view covers a table when all of these statements are true:

  • Every ordinary object in the application references the table only through the editioning view. (An ordinary object is any object except an editioning view or crossedition trigger. Editioning views and crossedition triggers must reference tables.)

  • Application users are granted object privileges only on the editioning view, not on the table.

  • Oracle Virtual Private Database (VPD) policies are attached only to the editioning view, not to the table. (Regular auditing and fine-grained auditing (FGA) policies are attached only to the table.)

When the editioning view is actualized, a copy of the VPD policy is attached to the actualized editioning view. (A policy is uniquely identified by its name and the object to which is it attached.) If the policy function is also actualized, the copy of the policy uses the actualized policy function; otherwise, it uses the original policy function.

The static data dictionary views *_POLICIES, which describe the VPD policies, can have different results in different editions.


See Also:


If an existing application does not use editioning views, prepare it to use them by following this procedure for each table that it uses:

  1. Give the table a new name (so that you can give its current name to its editioning view).

    Oracle recommends choosing a new name that is related to the original name and reflects the change history. For example, if the original table name is Data, the new table name might be Data_1.

  2. (Optional) Give each column of the table a new name.

    Again, Oracle recommends choosing new names that are related to the original names and reflect the change history. For example, Name and Number might be changed to Name_1 and Number_1.

    Any triggers that depend on renamed columns are now invalid. For details, see the entry for ALTER TABLE table RENAME column in Table 18-2.

  3. Create the editioning view, giving it the original name of the table.

    For instructions, see "Creating an Editioning View".

    Because the editioning view has the name that the table had, objects that reference that name now reference the editioning view.

  4. If triggers are defined on the table, drop them, and rerun the code that created them.

    Now the triggers that were defined on the table are defined on the editioning view.

  5. If VPD policies are attached to the table, drop the policies and policy functions and rerun the code that created them.

    Now the VPD policies that were attached to the table are attached to the editioning view.

  6. Revoke all object privileges on the table from all application users.

    To see which application users have which object privileges on the table, use this query:

    SELECT GRANTEE, PRIVILEGE
    FROM DBA_TAB_PRIVS
    WHERE TABLE_NAME='table_name';
    
  7. For every privilege revoked in step 6, grant the same privilege on the editioning view.

  8. Enable editions for users who own private synonyms that refer to the table (for instructions, see "Enabling Editions for a User") and notify those users that they must re-create those synonyms.


Note:

Public synonyms that refer to the table now fail with ORA-00980, and you cannot re-create them on the editioning view (for the reason, see "Actualizing Referenced Objects").

Procedure for Edition-Based Redefinition Using Only Editions

Use this procedure only if the type of every object that you will redefine is editionable (as defined in "Editionable and Noneditionable Schema Object Types"). Table is not an editionable type.

  1. Create a new edition.

    For instructions, see "Creating an Edition".

  2. Make the new edition your session edition.

    For instructions, see "Changing Your Session Edition".

  3. Make the necessary changes to the editioned objects of the application.

  4. Ensure that all objects are valid.

    Query the static data dictionary *_OBJECTS_AE, which describes every actual object in the database, in every edition. If invalid objects remain, recompile them, using any UTL_RECOMP subprogram (described in Oracle Database PL/SQL Packages and Types Reference).

  5. Check that the changes work as intended.

    If so, go to step 6.

    If not, either make further changes (return to step 3) or roll back the application upgrade (for instructions, see "Rolling Back the Application Upgrade").

  6. Make the new edition (the upgraded application) available to all users.

    For instructions, see "Making an Edition Available to All Users".

  7. Retire the old edition (the original application), so that all users except SYS use only the upgraded application.

    For instructions, see "Retiring an Edition".

Example 19-6 shows how to use the preceding procedure to change a very simple PL/SQL procedure.

Example 19-6 Edition-Based Redefinition of Very Simple Procedure

  1. Create PL/SQL procedure for this example:

    CREATE OR REPLACE PROCEDURE hello IS
    BEGIN
      DBMS_OUTPUT.PUT_LINE('Hello, edition 1.');
    END hello;
    /
    
  2. Invoke PL/SQL procedure:

    BEGIN hello(); END;
    /
    

    Result:

    Hello, edition 1.
     
    PL/SQL procedure successfully completed.
    
  3. Do edition-based redefinition of procedure:

    1. Create new edition:

      CREATE EDITION e2 AS CHILD OF ora$base;
      

      Result:

      Edition created.
      
    2. Make new edition your session edition:

      ALTER SESSION SET EDITION = e2;
      

      Result:

      Session altered.
      
    3. Change procedure:

      CREATE OR REPLACE PROCEDURE hello IS
      BEGIN
        DBMS_OUTPUT.PUT_LINE('Hello, edition 2.');
      END hello;
      /
      

      Result:

      Procedure created.
      
    4. Check that change works as intended:

      BEGIN hello(); END;
      /
      

      Result:

      Hello, edition 2.
      PL/SQL procedure successfully completed.
      
    5. Make new edition available to all users (requires system privileges):

      ALTER DATABASE DEFAULT EDITION = e2;
      
    6. Retire old edition (requires system privileges):

      List grantees:

      SELECT GRANTEE, PRIVILEGE
      FROM DBA_TAB_PRIVS
      WHERE TABLE_NAME = UPPER('ora$base')
      /
      

      Result:

      GRANTEE                        PRIVILEGE
      ------------------------------ ---------
      PUBLIC                         USE
       
      1 row selected.
      

      Revoke use on old edition from all grantees:

      REVOKE USE ON EDITION ora$base FROM PUBLIC;
      

Procedure for Edition-Based Redefinition Using Editioning Views

Use this procedure only if you will change the structure of one or more tables, and while you are doing so, other users do not need to be able to change data in those tables.

  1. Create a new edition.

    For instructions, see "Creating an Edition".

  2. Make the new edition your session edition.

    For instructions, see "Changing Your Session Edition".

  3. In the new edition, if the editioning views are read-only, make them read-write.

    For instructions, see "Changing the 'Write-ability' of an Editioning View".

  4. In every edition except the new edition, make the editioning views read-only.

  5. Make the necessary changes to the objects of the application.

  6. Ensure that all objects are valid.

    Query the static data dictionary *_OBJECTS_AE, which describes every actual object in the database, in every edition. If invalid objects remain, recompile them, using any UTL_RECOMP subprogram (described in Oracle Database PL/SQL Packages and Types Reference).

  7. Check that the changes work as intended.

    If so, go to step 8.

    If not, either make further changes (return to step 5) or roll back the application upgrade (for instructions, see "Rolling Back the Application Upgrade").

  8. Make the upgraded application available to all users.

    For instructions, see "Making an Edition Available to All Users".

  9. Retire the old edition (the original application), so that all users except SYS use only the upgraded application.

    For instructions, see "Retiring an Edition".

Procedure for Edition-Based Redefinition Using Crossedition Triggers

Use this procedure only if you will change the structure of one or more tables, and while you are doing so, other users must be able to change data in those tables.

  1. Create a new edition.

    For instructions, see "Creating an Edition".

  2. Make the new edition your session edition.

    For instructions, see "Changing Your Session Edition".

  3. Make the permanent changes to the objects of the application.

    For example, add new columns to the tables and create any new permanent subprograms.

    Objects that depend on objects that you changed might now be invalid. For more information, see Table 18-2.

  4. Ensure that all objects are valid.

    Query the static data dictionary *_OBJECTS_AE, which describes every actual object in the database, in every edition. If invalid objects remain, recompile them, using any UTL_RECOMP subprogram (described in Oracle Database PL/SQL Packages and Types Reference).

  5. Create the temporary objects—the crossedition triggers (in the disabled state) and any subprograms that they need.

    For instructions, see "Creating a Crossedition Trigger".

    You need reverse crossedition triggers only if you do step 10, which is optional.

  6. When the crossedition triggers compile successfully, enable them.

    Use the ALTER TRIGGER statement with the ENABLE option. For information about this statement, see Oracle Database PL/SQL Language Reference.

  7. Wait until pending changes are either committed or rolled back.

    Use the procedure DBMS_UTILITY.WAIT_ON_PENDING_DML, described in Oracle Database PL/SQL Packages and Types Reference.

  8. Apply the transforms.

    For instructions, see "Transforming Data from Pre- to Post-Upgrade Representation".


    Note:

    It is impossible to predict whether this step visits an existing row before a user of an ancestor edition updates, inserts, or deletes data from that row.

  9. Check that the changes work as intended.

    If so, go to step 10.

    If not, either make further changes (return to step 3) or roll back the application upgrade (for instructions, see "Rolling Back the Application Upgrade").

  10. (Optional) Grant the USE privilege on your session edition to the early users of the upgraded application.

    For instructions, see "Making an Edition Available to Some Users".

  11. Make the upgraded application available to all users.

    For instructions, see "Making an Edition Available to All Users".

  12. Disable or drop the constraints and then drop the crossedition triggers.

    For instructions, see P"Dropping the Crossedition Triggers".

  13. Retire the old edition (the original application), so that all users except SYS use only the upgraded application.

    For instructions, see "Retiring an Edition".

Rolling Back the Application Upgrade

To roll back the application upgrade:

  1. Change your session edition to something other than the new edition that you created for the upgrade.

    For instructions, see "Changing Your Session Edition".

  2. Drop the new edition that you created for the upgrade.

    For instructions, see "Dropping an Edition".

  3. If you created new table columns during the upgrade, reclaim the space that they occupy (for instructions, see "Reclaiming Space Occupied by Unused Table Columns").

Reclaiming Space Occupied by Unused Table Columns

If you roll back an upgrade for which you created new table columns,

To reclaim the space that unused columns occupy:

  1. Set the values of the unused columns to NULL.

    To avoid locking out other users while doing this operation, use the DBMS_PARALLEL_EXECUTE procedure (described in Oracle Database PL/SQL Packages and Types Reference).

  2. Set the unused columns to UNUSED.

    Use the ALTER TABLE statement (described in Oracle Database SQL Language Reference) with the SET UNUSED clause (described in Oracle Database SQL Language Reference).

  3. Shrink the table.

    Use the ALTER TABLE statement (described in Oracle Database SQL Language Reference) with the SHRINK SPACE clause (described in Oracle Database SQL Language Reference).

Example: Using Edition-Based Redefinition to Upgrade an Application

This example uses an edition, an editioning view, a forward crossedition trigger, and a reverse crossedition trigger.

Topics:


Note:

Before you can use edition-based redefinition to upgrade an application, you must enable editions for every schema that the application uses. For instructions, see "Enabling Editions for a User".

Existing Application

The existing application—the application to be upgraded—consists of a single table on which a trigger is defined. The application was created as in Example 19-7.

Example 19-7 Creating the Existing Application

  1. Create table:

    CREATE TABLE Contacts(
      ID            NUMBER(6,0) CONSTRAINT Contacts_PK PRIMARY KEY,
      Name          VARCHAR2(47),
      Phone_Number  VARCHAR2(20)
    );
    
  2. Populate table (not shown).

  3. Prepare to create trigger on table:

    ALTER TABLE Contacts ENABLE VALIDATE CONSTRAINT Contacts_PK;
     
    DECLARE Max_ID INTEGER;
    BEGIN
      SELECT MAX(ID) INTO Max_ID FROM Contacts;
      EXECUTE IMMEDIATE '
        CREATE SEQUENCE Contacts_Seq
          START WITH '||To_Char(Max_ID + 1);
    END;
    /
    
  4. Create trigger:

    CREATE TRIGGER Contacts_BI
      BEFORE INSERT ON Contacts FOR EACH ROW
    BEGIN
      :NEW.ID := Contacts_Seq.NEXTVAL;
    END;
    /
    

Example 19-8 shows how the table Contacts looks after being populated with data.

Example 19-8 Viewing Data in Existing Table

Query:

SELECT * FROM Contacts
ORDER BY Name;
 

Result:

        ID NAME                                            PHONE_NUMBER
---------- ----------------------------------------------- --------------------
       174 Abel, Ellen                                     011.44.1644.429267
       166 Ande, Sundar                                    011.44.1346.629268
       130 Atkinson, Mozhe                                 650.124.6234
       105 Austin, David                                   590.423.4569
       204 Baer, Hermann                                   515.123.8888
       116 Baida, Shelli                                   515.127.4563
       167 Banda, Amit                                     011.44.1346.729268
       172 Bates, Elizabeth                                011.44.1343.529268
       192 Bell, Sarah                                     650.501.1876
       151 Bernstein, David                                011.44.1344.345268
       129 Bissot, Laura                                   650.124.5234
       169 Bloom, Harrison                                 011.44.1343.829268
       185 Bull, Alexis                                    650.509.2876
       187 Cabrio, Anthony                                 650.509.4876
       148 Cambrault, Gerald                               011.44.1344.619268
       154 Cambrault, Nanette                              011.44.1344.987668
       110 Chen, John                                      515.124.4269
       ...
       120 Weiss, Matthew                                  650.123.1234
       200 Whalen, Jennifer                                515.123.4444
       149 Zlotkey, Eleni                                  011.44.1344.429018

107 rows selected.

Suppose that you must redefine Contacts, replacing the Name column with the columns First_Name and Last_Name, and adding the column Country_Code. Also suppose that while you are making this structural change, other users must be able to change the data in Contacts.

You need all features of edition-based redefinition: the edition, which is always needed; the editioning view, because you are redefining a table; and crossedition triggers, because other users must be able to change data in the table while you are redefining it.

Preparing the Application to Use Editioning Views

Example 19-9 shows how to create the editioning view from which other users will access the table Contacts while you are redefining it in the new edition.

Example 19-9 Creating an Editioning View for the Existing Table

  1. Give table a new name (so that you can give its current name to editioning view):

    ALTER TABLE Contacts RENAME TO Contacts_Table;
    
  2. (Optional) Give columns of table new names:

    ALTER TABLE Contacts_Table
      RENAME COLUMN Name TO Name_1;
    
    ALTER TABLE Contacts_Table
      RENAME COLUMN Phone_Number TO Phone_Number_1;
    
  3. Create editioning view:

    CREATE OR REPLACE EDITIONING VIEW Contacts AS
      SELECT
        ID                 ID,
        Name_1             Name,
        Phone_Number_1     Phone_Number
      FROM Contacts_Table;
    
  4. Move trigger Contacts_BI from table to editioning view:

    DROP TRIGGER Contacts_BI;
     
    CREATE TRIGGER Contacts_BI
      BEFORE INSERT ON Contacts FOR EACH ROW
    BEGIN
      :NEW.ID := Contacts_Seq.NEXTVAL;
    END;
    /
    

Using Edition-Based Redefinition to Upgrade the Application

Example 19-10 shows how to create an edition in which to upgrade the "Existing Application", make the new edition the session edition, and check that the new edition really is the session edition.

Example 19-10 Creating Edition in Which to Upgrade the Application

  1. Create new edition:

    CREATE EDITION Post_Upgrade AS CHILD OF Ora$Base;
    
  2. Make new edition your session edition:

    ALTER SESSION SET EDITION = Post_Upgrade;
    
  3. Check session edition:

    SELECT
    SYS_CONTEXT('Userenv', 'Current_Edition_Name') "Current_Edition"
    FROM DUAL;
    

    Result:

    Current_Edition
    -----------------------------------------------------------------------------
    POST_UPGRADE
     
    1 row selected.
    

In the Post_Upgrade edition, Example 19-11 shows how to add the new columns to the physical table and recompile the trigger that was invalidated by adding the columns. Then, it shows how to replace the editioning view Contacts so that it selects the columns of the table by their desired logical names.

Example 19-11 Changing the Table and Replacing the Editioning View

  1. Add new columns to physical table:

    ALTER TABLE Contacts_Table ADD (
      First_Name_2     varchar2(20),
      Last_Name_2      varchar2(25),
      Country_Code_2   varchar2(20),
      Phone_Number_2   varchar2(20)
    );
    

    (This is nonblocking DDL.)

  2. Recompile invalidated trigger:

    ALTER TRIGGER Contacts_BI COMPILE REUSE SETTINGS;
    
  3. Replace editioning view so that it selects replacement columns with their desired logical names:

    CREATE OR REPLACE EDITIONING VIEW Contacts AS
      SELECT
        ID                 ID,
        First_Name_2       First_Name,
        Last_Name_2        Last_Name,
        Country_Code_2     Country_Code,
        Phone_Number_2     Phone_Number
      FROM Contacts_Table;
    

In the Post_Upgrade edition, Example 19-12 shows how to create two procedures for the forward crossedition trigger to use, create both the forward and reverse crossedition triggers in the disabled state, and enable them.

Example 19-12 Creating and Enabling the Crossedition Triggers

  1. Create first procedure that forward crossedition trigger uses:

    CREATE OR REPLACE PROCEDURE Set_First_And_Last_Name (
      Name        IN  VARCHAR2,
      First_Name  OUT VARCHAR2,
      Last_Name   OUT VARCHAR2)
    IS
      Comma_Pos NUMBER := INSTR(Name, ',');
    BEGIN
      IF Comma_Pos IS NULL OR Comma_Pos < 2 THEN
        RAISE Program_Error;
      END IF;
     
      Last_Name := SUBSTR(Name, 1, Comma_Pos-1);
      Last_Name := RTRIM(Ltrim(Last_Name));
     
      First_Name := SUBSTR(Name, Comma_Pos+1);
      First_Name := RTRIM(LTRIM(First_Name));
    END Set_First_And_Last_Name;
    /
    
  2. Create second procedure that forward crossedition trigger uses:

    CREATE OR REPLACE PROCEDURE Set_Country_Code_And_Phone_No (
      Phone_Number     IN  VARCHAR2,
      Country_Code     OUT VARCHAR2,
      Phone_Number_V2  OUT VARCHAR2)
    IS
      Char_To_Number_Error EXCEPTION;
      PRAGMA EXCEPTION_INIT(Char_To_Number_Error, -06502);
      Bad_Phone_Number EXCEPTION;
      Nmbr VARCHAR2(30) := REPLACE(Phone_Number, '.', '-');
     
      FUNCTION Is_US_Number(Nmbr IN VARCHAR2)
        RETURN BOOLEAN
      IS
        Len NUMBER := LENGTH(Nmbr);
        Dash_Pos NUMBER := INSTR(Nmbr, '-');
        n PLS_INTEGER;
      BEGIN
        IF Len IS NULL OR Len <> 12 THEN
          RETURN FALSE;
        END IF;
        IF Dash_Pos IS NULL OR Dash_Pos <> 4 THEN
          RETURN FALSE;
        END IF;
        BEGIN
          n := TO_NUMBER(SUBSTR(Nmbr, 1, 3));
        EXCEPTION WHEN Char_To_Number_Error THEN
          RETURN FALSE;
        END;
     
        Dash_Pos := INSTR(Nmbr, '-', 5);
     
        IF Dash_Pos IS NULL OR Dash_Pos <> 8 THEN
          RETURN FALSE;
        END IF;
     
        BEGIN
          n := TO_NUMBER(SUBSTR(Nmbr, 5, 3));
        EXCEPTION WHEN Char_To_Number_Error THEN
          RETURN FALSE;
        END;
     
        BEGIN
          n := TO_NUMBER(SUBSTR(Nmbr, 9));
        EXCEPTION WHEN Char_To_Number_Error THEN
          RETURN FALSE;
        END;
     
        RETURN TRUE;
      END Is_US_Number;
     
    BEGIN
      IF Nmbr LIKE '011-%' THEN
        DECLARE
          Dash_Pos NUMBER := INSTR(Nmbr, '-', 5);
        BEGIN
          Country_Code := '+'|| TO_NUMBER(SUBSTR(Nmbr, 5, Dash_Pos-5));
          Phone_Number_V2 := SUBSTR(Nmbr, Dash_Pos+1);
        EXCEPTION WHEN Char_To_Number_Error THEN
          raise Bad_Phone_Number;
        END;
      ELSIF Is_US_Number(Nmbr) THEN
        Country_Code := '+1';
        Phone_Number_V2 := Nmbr;
      ELSE
        RAISE Bad_Phone_Number;
      END IF;
    EXCEPTION WHEN Bad_Phone_Number THEN
      Country_Code := '+0';
      Phone_Number_V2 := '000-000-0000';
    END Set_Country_Code_And_Phone_No;
    /
    
  3. Create forward crossedition trigger in disabled state:

    CREATE OR REPLACE TRIGGER Contacts_Fwd_Xed
      BEFORE INSERT OR UPDATE ON Contacts_Table
      FOR EACH ROW
      FORWARD CROSSEDITION
      DISABLE
    BEGIN
      Set_First_And_Last_Name(
        :NEW.Name_1,
        :NEW.First_Name_2,
        :NEW.Last_Name_2
      );
      Set_Country_Code_And_Phone_No(
        :NEW.Phone_Number_1,
        :NEW.Country_Code_2,
        :NEW.Phone_Number_2
      );
    END Contacts_Fwd_Xed;
    /
    
  4. Enable forward crossedition trigger:

    ALTER TRIGGER Contacts_Fwd_Xed ENABLE;
    
  5. Create reverse crossedition trigger in disabled state:

    CREATE OR REPLACE TRIGGER Contacts_Rvrs_Xed
      BEFORE INSERT OR UPDATE ON Contacts_Table
      FOR EACH ROW
      REVERSE CROSSEDITION
      DISABLE
    BEGIN
      :NEW.Name_1 := :NEW.Last_Name_2||', '||:NEW.First_Name_2;
      :NEW.Phone_Number_1 :=
      CASE :New.Country_Code_2
        WHEN '+1' THEN
          REPLACE(:NEW.Phone_Number_2, '-', '.')
        ELSE
          '011.'||LTRIM(:NEW.Country_Code_2, '+')||'.'||
          REPLACE(:NEW.Phone_Number_2, '-', '.')
      END;
    END Contacts_Rvrs_Xed;
    /
    
  6. Enable reverse crossedition trigger:

    ALTER TRIGGER Contacts_Rvrs_Xed ENABLE;
    
  7. Wait until pending changes are either committed or rolled back:

    DECLARE
      scn              NUMBER  := NULL;
      timeout CONSTANT INTEGER := NULL;
    BEGIN
      IF NOT DBMS_UTILITY.WAIT_ON_PENDING_DML(Tables  => 'Contacts_Table',
                                              timeout => timeout,
                                              scn     => scn)
      THEN
        RAISE_APPLICATION_ERROR(-20000,
         'Wait_On_Pending_DML() timed out. CETs were enabled before SCN: '||SCN);
      END IF;
    END;
    /
    

    For information about the DBMS_UTILITY.WAIT_ON_PENDING_DML procedure, see Oracle Database PL/SQL Packages and Types Reference.

In the Post_Upgrade edition, Example 19-13 shows how to apply the transforms.

Example 19-13 Applying the Transforms

DECLARE
  c NUMBER := DBMS_SQL.OPEN_CURSOR();
  x NUMBER;
BEGIN
  DBMS_SQL.PARSE(
    c                          => c,
    Language_Flag              => DBMS_SQL.NATIVE,
    Statement                  => 'UPDATE Contacts_Table SET ID = ID',
    Apply_Crossedition_Trigger => 'Contacts_Fwd_Xed'
  );
  x := DBMS_SQL.EXECUTE(c);
  DBMS_SQL.CLOSE_CURSOR(c);
  COMMIT;
END;
/
 

In the Post_Upgrade edition, Example 19-14 shows how to check that the change worked as intended. Compare Example 19-14 to Example 19-8.

Example 19-14 Viewing Data in Changed Table

  1. Format columns for readability:

    COLUMN ID FORMAT 999
    COLUMN Last_Name FORMAT A15
    COLUMN First_Name FORMAT A15
    COLUMN Country_Code FORMAT A12
    COLUMN Phone_Number FORMAT A12
    
  2. Query:

    SELECT * FROM Contacts
    ORDER BY Last_Name;
    

    Result:

      ID FIRST_NAME      LAST_NAME       COUNTRY_CODE PHONE_NUMBER
    ---- --------------- --------------- ------------ ------------
     174 Ellen           Abel            +44          1644-429267
     166 Sundar          Ande            +44          1346-629268
     130 Mozhe           Atkinson        +1           650-124-6234
     105 David           Austin          +1           590-423-4569
     204 Hermann         Baer            +1           515-123-8888
     116 Shelli          Baida           +1           515-127-4563
     167 Amit            Banda           +44          1346-729268
     172 Elizabeth       Bates           +44          1343-529268
     192 Sarah           Bell            +1           650-501-1876
     151 David           Bernstein       +44          1344-345268
     129 Laura           Bissot          +1           650-124-5234
     169 Harrison        Bloom           +44          1343-829268
     185 Alexis          Bull            +1           650-509-2876
     187 Anthony         Cabrio          +1           650-509-4876
     154 Nanette         Cambrault       +44          1344-987668
     148 Gerald          Cambrault       +44          1344-619268
     110 John            Chen            +1           515-124-4269
           ...
     120 Matthew         Weiss           +1           650-123-1234
     200 Jennifer        Whalen          +1           515-123-4444
     149 Eleni           Zlotkey         +44          1344-429018
     
    107 rows selected.
    

If the change worked as intended, you can now follow steps 10 through 13 of the "Procedure for Edition-Based Redefinition Using Crossedition Triggers".

PK6PPPK|%AOEBPS/adfns_cqn.htm Using Continuous Query Notification (CQN)

11 Using Continuous Query Notification (CQN)

Continuous Query Notification (CQN) allows an application to register queries with the database for either object change notification (the default) or query result change notification. An object referenced by a registered query is a registered object.

If a query is registered for object change notification (OCN), the database notifies the application whenever a transaction changes an object that the query references and commits, regardless of whether the query result changed.

If a query is registered for query result change notification (QRCN), the database notifies the application whenever a transaction changes the result of the query and commits.

A CQN registration associates a list of one or more queries with a notification type (OCN or QRCN) and a notification handler. To create a CQN registration, you can use either the PL/SQL interface or Oracle Call Interface (OCI). If you use the PL/SQL interface, the notification handler is a server-side PL/SQL stored procedure; if you use OCI, the notification handler is a client-side C callback procedure.

This chapter explains general CQN concepts and explains how to use the PL/SQL CQN interface. For information about using OCI for CQN, see Oracle Call Interface Programmer's Guide.

Topics:


Note:

The terms OCN and QRCN refer to both the notification type and the notification itself: An application registers a query for OCN, and the database sends the application an OCN; an application registers a query for QRCN, and the database sends the application a QRCN.

Object Change Notification (OCN)

If an application registers a query for object change notification (OCN), the database sends the application an OCN whenever a transaction changes an object associated with the query and commits, regardless of whether the result of the query changed.

For example, if an application registers the query in Example 11-1 for OCN, and a user commits a transaction that changes the EMPLOYEES table, the database sends the application an OCN, even if the changed row or rows did not satisfy the query predicate (for example, if DEPARTMENT_ID = 5).

Example 11-1 Query to be Registered for Change Notification

SELECT EMPLOYEE_ID, SALARY
FROM EMPLOYEES
WHERE DEPARTMENT_ID = 10;

Query Result Change Notification (QRCN)


Note:

For QRCN support, the COMPATIBLE initialization parameter of the database must be at least 11.0.0, and Automatic Undo Management (AUM) must be enabled (as it is by default).

For information about the COMPATIBLE initialization parameter, see Oracle Database Administrator's Guide.

For information about AUM, see Oracle Database Administrator's Guide.


If an application registers a query for query result change notification (QRCN), the database sends the application a QRCN whenever a transaction changes the result of the query and commits.

For example, if an application registers the query in Example 11-1 for QRCN, the database sends the application a QRCN only if the query result set changes; that is, if one of these data manipulation language (DML) statements commits:

  • An INSERT or DELETE of a row that satisfies the query predicate (DEPARTMENT_ID = 10).

  • An UPDATE to the EMPLOYEE_ID or SALARY column of a row that satisfied the query predicate (DEPARTMENT_ID = 10).

  • An UPDATE to the DEPARTMENT_ID column of a row that changed its value from 10 to a value other than 10, causing the row to be deleted from the result set.

  • An UPDATE to the DEPARTMENT_ID column of a row that changed its value to 10 from a value other than 10, causing the row to be added to the result set.

The default notification type is OCN. For QRCN, specify QOS_QUERY in the QOSFLAGS attribute of the CQ_NOTIFICATION$_REG_INFO object.

With QRCN, you have a choice of guaranteed mode (the default) or best-effort mode.

Topics:

Guaranteed Mode

In guaranteed mode, there are no false positives: the database sends the application a QRCN only when the query result set is guaranteed to have changed.

For example, suppose that an application registered the query in Example 11-1 for QRCN, that employee 201 is in department 10, and that these statements are executed:

UPDATE EMPLOYEES
SET SALARY = SALARY + 10
WHERE EMPLOYEE_ID = 201;

UPDATE EMPLOYEES
SET SALARY = SALARY - 10
WHERE EMPLOYEE_ID = 201;

COMMIT;

Each UPDATE statement in the preceding transaction changes the query result set, but together they have no effect on the query result set; therefore, the database does not send the application a QRCN for the transaction.

For guaranteed mode, specify QOS_QUERY, but not QOS_BEST_EFFORT, in the QOSFLAGS attribute of the CQ_NOTIFICATION$_REG_INFO object.

Some queries are too complex for QRCN in guaranteed mode. For the characteristics of queries that can be registered in guaranteed mode, see "Queries that Can Be Registered for QRCN in Guaranteed Mode".

Best-Effort Mode

Some queries that are too complex for guaranteed mode can be registered for QRCN in best-effort mode, in which CQN creates and registers simpler versions of them.

For example, the query in Example 11-2 is too complex for QRCN in guaranteed mode because it contains the aggregate function SUM.

Example 11-2 Query Too Complex for QRCN in Guaranteed Mode

SELECT SUM(SALARY)
FROM EMPLOYEES
WHERE DEPARTMENT_ID = 20;

In best-effort mode, CQN registers this simpler version of the query in Example 11-2:

SELECT SALARY
FROM EMPLOYEES
WHERE DEPARTMENT_ID = 20;

Whenever the result of the original query changes, the result of its simpler version also changes; therefore, no notifications are lost from the simplification. However, the simplification might cause false positives, because the result of the simpler version can change when the result of the original query does not.

In best-effort mode, the database:

  • Minimizes the OLTP response overhead that is from notification-related processing, as follows:

    • For a single-table query, the database determines whether the query result has changed by which columns changed and which predicates the changed rows satisfied.

    • For a multiple-table query (a join), the database uses the primary-key/foreign-key constraint relationships between the tables to determine whether the query result has changed.

  • Sends the application a QRCN whenever a DML statement changes the query result set, even if a subsequent DML statement nullifies the change made by the first DML statement.

The overhead minimization of best-effort mode infrequently causes false positives, even for queries that CQN does not simplify. For example, consider the query in Example 11-1 and the transaction in "Guaranteed Mode". In best-effort mode, CQN does not simplify the query, but the transaction generates a false positive.

Some types of queries are so simplified that invalidations are generated at object level; that is, whenever any object referenced in those queries changes. Examples of such queries are those that use unsupported column types or include subqueries. The solution to this problem is to rewrite the original queries.

For example, the query in Example 11-3 is too complex for QRCN in guaranteed mode because it includes a subquery.

Example 11-3 Query Whose Simplified Version Invalidates Objects

SELECT SALARY
FROM EMPLOYEES
WHERE DEPARTMENT_ID IN (
  SELECT DEPARTMENT_ID
  FROM DEPARTMENTS
  WHERE LOCATION_ID = 1700
);

In best-effort mode, CQN simplifies the query in Example 11-3 to this:

SELECT * FROM EMPLOYEES, DEPARTMENTS;

The simplified query can cause objects to be invalidated. However, if you rewrite the original query as follows, you can register it in either guaranteed mode or best-effort mode:

SELECT SALARY
FROM EMPLOYEES, DEPARTMENTS
WHERE EMPLOYEES.DEPARTMENT_ID = DEPARTMENTS.DEPARTMENT_ID
  AND DEPARTMENTS.LOCATION_ID = 1700;

Queries that can be registered only in best-effort mode are described in "Queries that Can Be Registered for QRCN Only in Best-Effort Mode".

The default for QRCN mode is guaranteed mode. For best-effort mode, specify QOS_BEST_EFFORT in the QOSFLAGS attribute of the CQ_NOTIFICATION$_REG_INFO object.

Events that Generate Notifications

These events generate notifications:

Committed DML Transactions

When the notification type is OCN, any DML transaction that changes one or more registered objects generates one notification for each object when it commits.

When the notification type is QRCN, any DML transaction that changes the result of one or more registered queries generates a notification when it commits. The notification includes the query IDs of the queries whose results changed.

For either notification type, the notification includes:

  • Name of each changed table

  • Operation type (INSERT, UPDATE, or DELETE)

  • ROWID of each changed row, if the registration was created with the ROWID option and the number of modified rows was not too large. For more information, see "ROWID Option".

Committed DDL Statements

For both OCN and QRCN, these data definition language (DDL) statements, when committed, generate notifications:

  • ALTER TABLE

  • TRUNCATE TABLE

  • FLASHBACK TABLE

  • DROP TABLE


Note:

When the notification type is OCN, a committed DROP TABLE statement generates a DROP NOTIFICATION.

Any OCN registrations of queries on the dropped table become disassociated from that table (which no longer exists), but the registrations themselves continue to exist. If any of these registrations are associated with objects other than the dropped table, committed changes to those other objects continue to generate notifications. Registrations associated only with the dropped table also continue to exist, and their creator can add queries (and their referenced objects) to them.

An OCN registration is based on the version and definition of an object at the time the query was registered. If an object is dropped, registrations on that object are disassociated from it forever. If an object is created with the same name, and in the same schema, as the dropped object, the created object is not associated with OCN registrations that were associated with the dropped object.


When the notification type is QRCN:

  • The notification includes:

    • Query IDs of the queries whose results have changed

    • Name of the modified table

    • Type of DDL operation

  • Some DDL operations that invalidate registered queries can cause those queries to be deregistered.

    For example, suppose that this query is registered for QRCN:

    SELECT COL1 FROM TEST_TABLE
      WHERE COL2 = 1;
    

    Suppose that TEST_TABLE has this schema:

    (COL1 NUMBER, COL2 NUMBER, COL3 NUMBER)
    

    This DDL statement, when committed, invalidates the query and causes it to be removed from the registration:

    ALTER TABLE DROP COLUMN COL2;
    

Deregistration

For both OCN and QRCN, deregistration—removal of a registration from the database—generates a notification. The reasons that the database removes a registration are:

  • Timeout

    If TIMEOUT is specified with a nonzero value when the queries are registered, the database purges the registration after the specified time interval.

    If QOS_DEREG_NFY is specified when the queries are registered, the database purges the registration after it generates its first notification.

  • Loss of privileges

    If privileges are lost on an object associated with a registered query, and the notification type is OCN, the database purges the registration. (When the notification type is QRCN, the database removes that query from the registration, but does not purge the registration.)

    For privileges needed to register queries, see "Prerequisites for Creating CQN Registrations".

A notification is not generated when a client application performs an explicit deregistration.

Global Events

The global events EVENT_STARTUP and EVENT_SHUTDOWN generate notifications.

In an Oracle RAC environment, these events generate notifications:

  • EVENT_STARTUP when the first instance of the database starts

  • EVENT_SHUTDOWN when the last instance of the database shuts down

  • EVENT_SHUTDOWN_ANY when any instance of the database shuts down

The preceding global events are constants defined in the DBMS_CQ_NOTIFICATION package.


See Also:

Oracle Database PL/SQL Packages and Types Reference for more information about the DBMS_CQ_NOTIFICATION package

Notification Contents

A notification contains some or all of this information:

  • Type of event, which is one of:

    • Startup

    • Object change

    • Query result change

    • Deregistration

    • Shutdown

  • Registration ID of affected registration

  • Names of changed objects

  • If ROWID option was specified, ROWIDs of changed rows

  • If the notification type is QRCN: Query IDs of queries whose results changed

  • If notification resulted from a DML or DDL statement:

    • Array of names of modified tables

    • Operation type (for example, INSERT or UPDATE)

A notification does not contain the changed data itself. For example, the notification does not say that a monthly salary increased from 5000 to 6000. To obtain more recent values for the changed objects or rows or query results, the application must query the database.

Good Candidates for CQN

Good candidates for CQN are applications that cache the result sets of queries on infrequently changed objects in the middle tier, to avoid network round trips to the database. These applications can use CQN to register the queries to be cached. When such an application receives a notification, it can refresh its cache by rerunning the registered queries.

An example of such an application is a web forum. Because its users need not view content as soon as it is inserted into the database, this application can cache information in the middle tier and have CQN tell it when it when to refresh the cache.

Figure 11-1 illustrates a typical scenario in which the database serves data that is cached in the middle tier and then accessed over the Internet.

Figure 11-1 Middle-Tier Caching

Middle-Tier Caching
Description of "Figure 11-1 Middle-Tier Caching"

Applications in the middle tier require rapid access to cached copies of database objects while keeping the cache as current as possible in relation to the database. Cached data becomes obsolete when a transaction modifies the data and commits, thereby putting the application at risk of accessing incorrect results. If the application uses CQN, the database can publish a notification when a change occurs to registered objects with details on what changed. In response to the notification, the application can refresh cached data by fetching it from the back-end database.

Figure 11-2 illustrates the process by which middle-tier Web clients receive and process notifications.

Figure 11-2 Basic Process of Continuous Query Notification (CQN)

Basic Process of Continuous Query Notification (CQN)
Description of "Figure 11-2 Basic Process of Continuous Query Notification (CQN)"

Explanation of steps in Figure 11-2 (if registrations are created using PL/SQL and that the application has cached the result set of a query on HR.EMPLOYEES):

  1. The developer uses PL/SQL to create a CQN registration for the query, which consists of creating a stored PL/SQL procedure to process notifications and then using the PL/SQL CQN interface to create a registration for the query, specifying the PL/SQL procedure as the notification handler.

  2. The database populates the registration information in the data dictionary.

  3. A user updates a row in the HR.EMPLOYEES table in the back-end database and commits the update, causing the query result to change. The data for HR.EMPLOYEES cached in the middle tier is now outdated.

  4. The database adds a message that describes the change to an internal queue.

  5. The database notifies a JOBQ background process of a notification message.

  6. The JOBQ process runs the stored procedure specified by the client application. In this example, JOBQ passes the data to a server-side PL/SQL procedure. The implementation of the PL/SQL notification handler determines how the notification is handled.

  7. Inside the server-side PL/SQL procedure, the developer can implement logic to notify the middle-tier client application of the changes to the registered objects. For example, it notifies the application of the ROWID of the changed row in HR.EMPLOYEES.

  8. The client application in the middle tier queries the back-end database to retrieve the data in the changed row.

  9. The client application updates the cache with the data.

Creating CQN Registrations

A CQN registration associates a list of one or more queries with a notification type and a notification handler.

The notification type is either OCN or QRCN. For information about these types, see "Object Change Notification (OCN)" and "Query Result Change Notification (QRCN)".

To create a CQN registration, you can use either the PL/SQL interface or OCI. If you use the PL/SQL interface, the notification handler is a server-side PL/SQL stored procedure; if you use OCI, the notification handler is a client-side C callback procedure. (This topic explains only the PL/SQL interface. For information about OCI, see Oracle Call Interface Programmer's Guide.)

Once created, a registration is stored in the database. In an Oracle RAC environment, it is visible to all database instances. Transactions that change the query results in any database instance generate notifications.

By default, a registration survives until the application that created it explicitly deregisters it or until the database implicitly purges it (from loss of privileges, for example).

Topics:

PL/SQL CQN Registration Interface

The PL/SQL CQN registration interface is implemented with the DBMS_CQ_NOTIFICATION package. You use the DBMS_CQ_NOTIFICATION.NEW_REG_START function to open a registration block. You specify the registration details, including the notification type and notification handler, as part of the CQ_NOTIFICATION$_REG_INFO object, which is passed as an argument to the NEW_REG_START procedure. Every query that you run while the registration block is open is registered with CQN. If you specified notification type QRCN, the database assigns a query ID to each query. You can retrieve these query IDs with the DBMS_CQ_NOTIFICATION.CQ_NOTIFICATION_QUERYID function. To close the registration block, you use the DBMS_CQ_NOTIFICATION.REG_END function.

For step-by-step instructions, see "Using PL/SQL to Register Queries for CQN".


See Also:

Oracle Database PL/SQL Packages and Types Reference for more information about the DBMS_CQ_NOTIFICATION package

CQN Registration Options

You can change the CQN registration defaults with the options summarized in Table 11-1.

Table 11-1 Continuous Query Notification Registration Options

<td align="left" headers="r6c1-t7 r1c2-t7" rowspan="1" colspan="1">

Deprecated. Use Notification Grouping instead.

OptionDescription

Notification Type

Specifies QRCN (the default is OCN).

QRCN ModeFoot 1 

Specifies best-effort mode (the default is guaranteed mode).

ROWID

Includes the value of the ROWID pseudocolumn for each changed row in the notification.

Operations FilterFoot 2 

Publishes the notification only if the operation type matches the specified filter condition.

Transaction LagFootref 2

Notification Grouping

Specifies how notifications are grouped.

Reliable

Stores notifications in a persistent database queue (instead of in shared memory, the default).

Purge on Notify

Purges the registration after the first notification.

Timeout

Purges the registration after a specified time interval.


Footnote 1 Applies only when notification type is QRCN.

Footnote 2 Applies only when notification type is OCN.

Topics:

Notification Type Option

The notification types are OCN (described in "Object Change Notification (OCN)") and QRCN (described in "Query Result Change Notification (QRCN)").

QRCN Mode (QRCN Notification Type Only)

The QRCN mode option applies only when the notification type is QRCN. Instructions for setting the notification type to QRCN are in "Notification Type Option".

The QRCN modes are guaranteed (described in "Guaranteed Mode") and best-effort (described in "Best-Effort Mode").

The default is guaranteed mode. For best-effort mode, specify QOS_BEST_EFFORT in the QOSFLAGS attribute of the CQ_NOTIFICATION$_REG_INFO object.

ROWID Option

The ROWID option includes the value of the ROWID pseudocolumn (the rowid of the row) for each changed row in the notification. To include the ROWID option of each changed row in the notification, specify QOS_ROWIDS in the QOSFLAGS attribute of the CQ_NOTIFICATION$_REG_INFO object.


Note:

When you update a row in a table compressed with Hybrid Columnar Compression (HCC), the ROWID of the row changes. HCC, a feature of certain Oracle storage systems, is described in Oracle Database Concepts.

From the ROWID information in the notification, the application can retrieve the contents of the changed rows by performing queries of this form:

SELECT * FROM table_name_from_notification
WHERE ROWID = rowid_from_notification;

ROWIDs are published in the external string format. For a regular heap table, the length of a ROWID is 18 character bytes. For an Index Organized Table (IOT), the length of the ROWID depends on the size of the primary key, and might exceed 18 bytes.

If the server does not have enough memory for the ROWIDs, the notification might be "rolled up" into a FULL-TABLE-NOTIFICATION, indicated by a special flag in the notification descriptor. Possible reasons for a FULL-TABLE-NOTIFICATION are:

  • Total shared memory consumption from ROWIDs exceeds 1% of the dynamic shared pool size.

  • Too many rows were changed in a single registered object within a transaction (the upper limit is approximately 80).

  • Total length of the logical ROWIDs of modified rows for an IOT is too large (the upper limit is approximately 1800 bytes).

  • You specified the Notification Grouping option NTFN_GROUPING_TYPE with the value DBMS_CQ_NOTIFICATION.NTFN_GROUPING_TYPE_SUMMARY, described in "Notification Grouping Options".

Because a FULL-TABLE-NOTIFICATION does not include ROWIDs, the application that receives it must assume that the entire table (that is, all rows) might have changed.

Operations Filter Option (OCN Notification Type Only)

The Operations Filter option applies only when the notification type is OCN.

The Operations Filter option enables you to specify the types of operations that generate notifications.

The default is all operations. To specify that only some operations generate notifications, use the OPERATIONS_FILTER attribute of the CQ_NOTIFICATION$_REG_INFO object. With the OPERATIONS_FILTER attribute, specify the type of operation with the constant that represents it, which is defined in the DBMS_CQ_NOTIFICATION package, as follows:

OperationConstant
INSERTDBMS_CQ_NOTIFICATION.INSERTOP
UPDATEDBMS_CQ_NOTIFICATION.UPDATEOP
DELETEDBMS_CQ_NOTIFICATION.DELETEOP
ALTEROPDBMS_CQ_NOTIFICATION.ALTEROP
DROPOPDBMS_CQ_NOTIFICATION.DROPOP
UNKNOWNOPDBMS_CQ_NOTIFICATION.UNKNOWNOP
All (default)DBMS_CQ_NOTIFICATION.ALL_OPERATIONS

To specify multiple operations, use bitwise OR. For example:

DBMS_CQ_NOTIFICATION.INSERTOP + DBMS_CQ_NOTIFICATION.DELETEOP

OPERATIONS_FILTER has no effect if you also specify QOS_QUERY in the QOSFLAGS attribute, because QOS_QUERY specifies notification type QRCN.


See Also:

Oracle Database PL/SQL Packages and Types Reference for more information about the DBMS_CQ_NOTIFICATION package

Transaction Lag Option (OCN Notification Type Only)

The Transaction Lag option applies only when the notification type is OCN.


Note:

This option is deprecated. To implement flow-of-control notifications, use "Notification Grouping Options".

The Transaction Lag option specifies the number of transactions by which the client application can lag behind the database. If the number is 0, every transaction that changes a registered object results in a notification. If the number is 5, every fifth transaction that changes a registered object results in a notification. The database tracks intervening changes at object granularity and includes them in the notification, so that the client does not lose them.

A transaction lag greater than 0 is useful only if an application implements flow-of-control notifications. Ensure that the application generates notifications frequently enough to satisfy the lag, so that they are not deferred indefinitely.

If you specify TRANSACTION_LAG, then notifications do not include ROWIDs, even if you also specified QOS_ROWIDS.

Notification Grouping Options

By default, notifications are generated immediately after the event that causes them.

Notification Grouping options, which are attributes of the CQ_NOTIFICATION$_REG_INFO object, are:

AttributeDescription
NTFN_GROUPING_CLASSSpecifies the class by which to group notifications. The only allowed values are DBMS_CQ_NOTIFICATION.NTFN_GROUPING_CLASS_TIME, which groups notifications by time, and zero, which is the default (notifications are generated immediately after the event that causes them).
NTFN_GROUPING_VALUESpecifies the time interval that defines the group, in seconds. For example, if this value is 900, notifications generated in the same 15-minute interval are grouped.
NTFN_GROUPING_TYPESpecifies the type of grouping, which is either of:
  • DBMS_CQ_NOTIFICATION.NTFN_GROUPING_TYPE_SUMMARY: All notifications in the group are summarized into a single notification.

    Note: The single notification does not include ROWIDs, even if you specified the ROWID option.

  • DBMS_CQ_NOTIFICATION.NTFN_GROUPING_TYPE_LAST: Only the last notification in the group is published and the earlier ones discarded.

NTFN_GROUPING_START_TIMESpecifies when to start generating notifications. If specified as NULL, it defaults to the current system-generated time.
NTFN_GROUPING_REPEAT_COUNTSpecifies how many times to repeat the notification. Set to DBMS_CQ_NOTIFICATION.NTFN_GROUPING_FOREVER to receive notifications for the life of the registration. To receive at most n notifications during the life of the registration, set to n.


Note:

Notifications generated by timeouts, loss of privileges, and global events might be published before the specified grouping interval expires. If they are, any pending grouped notifications are also published before the interval expires.

Reliable Option

By default, a CQN registration is stored in shared memory. To store it in a persistent database queue instead—that is, to generate reliable notifications—specify QOS_RELIABLE in the QOSFLAGS attribute of the CQ_NOTIFICATION$_REG_INFO object.

The advantage of reliable notifications is that if the database fails after generating them, it can still deliver them after it restarts. In an Oracle RAC environment, a surviving database instance can deliver them.

The disadvantage of reliable notifications is that they have higher CPU and I/O costs than default notifications do.

Purge-on-Notify and Timeout Options

By default, a CQN registration survives until the application that created it explicitly deregisters it or until the database implicitly purges it (from loss of privileges, for example).

To purge the registration after it generates its first notification, specify QOS_DEREG_NFY in the QOSFLAGS attribute of the CQ_NOTIFICATION$_REG_INFO object.

To purge the registration after n seconds, specify n in the TIMEOUT attribute of the CQ_NOTIFICATION$_REG_INFO object.

You can use the Purge-on-Notify and Timeout options together.

Prerequisites for Creating CQN Registrations

These are prerequisites for creating CQN registrations:

  • You must have these privileges:

    • EXECUTE privilege on the DBMS_CQ_NOTIFICATION package, whose subprograms you use to create a registration

    • CHANGE NOTIFICATION system privilege

    • SELECT privileges on all objects to be registered

    Loss of privileges on an object associated with a registered query generates a notification—see "Deregistration".

  • You must be connected as a non-SYS user.

  • You must not be in the middle of an uncommitted transaction.

  • The dml_locks init.ora parameter must have a nonzero value (as its default value does).

    (This is also a prerequisite for receiving notifications.)


Note:

For QRCN support, the COMPATIBLE setting of the database must be at least 11.0.0.

Queries that Can Be Registered for Object Change Notification (OCN)

Most queries can be registered for OCN, including those executed as part of stored procedures and REF cursors.

Queries that cannot be registered for OCN are:

  • Queries on fixed tables or fixed views

  • Queries on user views

  • Queries that contain database links (dblinks)

  • Queries over materialized views


Note:

You can use synonyms in OCN registrations, but not in QRCN registrations.

Queries that Can Be Registered for Query Result Change Notification (QRCN)

Some queries can be registered for QRCN in guaranteed mode, some can be registered for QRCN only in best-effort mode, and some cannot be registered for QRCN in either mode. (For information about modes, see "Guaranteed Mode" and "Best-Effort Mode".)

Topics:

Queries that Can Be Registered for QRCN in Guaranteed Mode

To be registered for QRCN in guaranteed mode, a query must conform to these rules:

  • Every column that it references is either a NUMBER data type or a VARCHAR2 data type.

  • Arithmetic operators in column expressions are limited to these binary operators, and their operands are columns with numeric data types:

    • + (addition)

    • - (subtraction, not unary minus)

    • * (multiplication)

    • / (division)

  • Comparison operators in the predicate are limited to:

    • < (less than)

    • <= (less than or equal to)

    • = (equal to)

    • >= (greater than or equal to)

    • > (greater than)

    • <> or != (not equal to)

    • IS NULL

    • IS NOT NULL

  • Boolean operators in the predicate are limited to AND, OR, and NOT.

  • The query contains no aggregate functions (such as SUM, COUNT, AVERAGE, MIN, and MAX).

    For a list of SQL aggregate functions, see Oracle Database SQL Language Reference.

Guaranteed mode supports most queries on single tables and some inner equijoins, such as:

SELECT SALARY FROM EMPLOYEES, DEPARTMENTS
  WHERE EMPLOYEES.DEPARTMENT_ID = DEPARTMENTS.DEPARTMENT_ID
    AND DEPARTMENTS.LOCATION_ID = 1700;

Notes:

  • Sometimes the query optimizer uses an execution plan that makes a query incompatible for guaranteed mode (for example, OR-expansion). For information about the query optimizer, see Oracle Database Performance Tuning Guide.

  • Queries that can be registered in guaranteed mode can also be registered in best-effort mode, but results might differ, because best-effort mode can cause false positives even for queries that CQN does not simplify. For details, see "Best-Effort Mode".


Queries that Can Be Registered for QRCN Only in Best-Effort Mode

A query that does any of the following can be registered for QRCN only in best-effort mode, and its simplified version generates notifications at object granularity:

  • Refers to columns that have encryption enabled

  • Has more than 10 items of the same type in the SELECT list

  • Has expressions that include any of these:

    • String functions (such as SUBSTR, LTRIM, and RTRIM)

    • Arithmetic functions (such as TRUNC, ABS, and SQRT)

      For a list of SQL functions, see Oracle Database SQL Language Reference.

    • Pattern-matching conditions LIKE and REGEXP_LIKE

    • EXISTS or NOT EXISTS condition

  • Has disjunctions involving predicates defined on columns from different tables. For example:

    SELECT EMPLOYEE_ID, DEPARTMENT_ID
      FROM EMPLOYEES, DEPARTMENTS
        WHERE EMPLOYEES.EMPLOYEE_ID = 10
          OR DEPARTMENTS.DEPARTMENT_ID = 'IT';
    
  • Has user rowid access. For example:

    SELECT DEPARTMENT_ID
      FROM DEPARTMENTS
        WHERE ROWID = 'AAANkdAABAAALinAAF';
    
  • Has any join other than an inner join

  • Has an execution plan that involves any of these:

    • Bitmap join, domain, or function-based indexes

    • UNION ALL or CONCATENATION

      (Either in the query itself, or as the result of an OR-expansion execution plan chosen by the query optimizer.)

    • ORDER BY or GROUP BY

      (Either in the query itself, or as the result of a SORT operation with an ORDER BY option in the execution plan chosen by the query optimizer.)

    • Partitioned index-organized table (IOT) with overflow segment

    • Clustered objects

    • Parallel execution

Queries that Cannot Be Registered for QRCN in Either Mode

A query that refers to any of the following cannot be registered for QRCN in either guaranteed or best-effort mode:

  • Views

  • Tables that are fixed, remote, or have Virtual Private Database (VPD) policies enabled

  • DUAL (in the SELECT list)

  • Synonyms

  • Calls to user-defined PL/SQL subprograms

  • Operators not listed in "Queries that Can Be Registered for QRCN in Guaranteed Mode"

  • The aggregate function COUNT

    (Other aggregate functions are allowed in best-effort mode, but not in guaranteed mode.)

  • Application contexts; for example:

    SELECT SALARY FROM EMPLOYEES
    WHERE USER = SYS_CONTEXT('USERENV', 'SESSION_USER');
    
  • SYSDATE, SYSTIMESTAMP, or CURRENT TIMESTAMP

Also, a query that the query optimizer has rewritten using a materialized view cannot be registered for QRCN. For information about the query optimizer, see Oracle Database Performance Tuning Guide.

Using PL/SQL to Register Queries for CQN

To use PL/SQL to create a CQN registration, follow these steps:

  1. Create a stored PL/SQL procedure to serve as the notification handler.

  2. Create a CQ_NOTIFICATION$_REG_INFO object that specifies the name of the notification handler, the notification type, and other attributes of the registration.

  3. In your client application, use the DBMS_CQ_NOTIFICATION.NEW_REG_START function to open a registration block.

  4. Run the queries to register. (Do not run DML or DDL operations.)

  5. Close the registration block, using the DBMS_CQ_NOTIFICATION.REG_END function.

Topics:


See Also:

Oracle Database PL/SQL Packages and Types Reference for more information about the CQ_NOTIFICATION$_REG_INFO object and the functions NEW_REG_START and REG_END, all of which are defined in the DBMS_CQ_NOTIFICATION package

Creating a PL/SQL Notification Handler

The PL/SQL stored procedure that you create to serve as the notification handler must have this signature:

PROCEDURE schema_name.proc_name(ntfnds IN CQ_NOTIFICATION$_DESCRIPTOR)

In the preceding signature, schema_name is the name of the database schema, proc_name is the name of the stored procedure, and ntfnds is the notification descriptor.

The notification descriptor is a CQ_NOTIFICATION$_DESCRIPTOR object, whose attributes describe the details of the change (transaction ID, type of change, queries affected, tables modified, and so on).

The JOBQ process passes the notification descriptor, ntfnds, to the notification handler, proc_name, which handles the notification according to its application requirements. (This is step 6 in Figure 11-2.)


Note:

The notification handler runs inside a job queue process. The JOB_QUEUE_PROCESSES initialization parameter specifies the maximum number of processes that can be created for the execution of jobs. You must set JOB_QUEUE_PROCESSES to a nonzero value to receive PL/SQL notifications.

Creating a CQ_NOTIFICATION$_REG_INFO Object

An object of type CQ_NOTIFICATION$_REG_INFO specifies the notification handler that the database runs when a registered objects changes. In SQL*Plus, you can view its type attributes by running this statement:

DESC CQ_NOTIFICATION$_REG_INFO

Table 11-2 describes the attributes of SYS.CQ_NOTIFICATION$_REG_INFO.

Table 11-2 Attributes of CQ_NOTIFICATION$_REG_INFO

AttributeDescription

CALLBACK

Specifies the name of the PL/SQL procedure to be executed when a notification is generated (a notification handler). You must specify the name in the form schema_name.procedure_name, for example, hr.dcn_callback.

QOSFLAGS

Specifies one or more quality-of-service flags, which are constants in the DBMS_CQ_NOTIFICATION package. For their names and descriptions, see Table 11-3.

To specify multiple quality-of-service flags, use bitwise OR. For example: DBMS_CQ_NOTIFICATION.QOS_RELIABLE + DBMS_CQ_NOTIFICATION.QOS_ROWIDS

TIMEOUT

Specifies the timeout period for registrations. If set to a nonzero value, it specifies the time in seconds after which the database purges the registration. If 0 or NULL, then the registration persists until the client explicitly deregisters it.

Can be combined with the QOSFLAGS attribute with its QOS_DEREG_NFY flag.

OPERATIONS_FILTER

Applies only to OCN (described in "Object Change Notification (OCN)"). Has no effect if you specify the QOS_FLAGS attribute with its QOS_QUERY flag.

Filters messages based on types of SQL statement. You can specify these constants in the DBMS_CQ_NOTIFICATION package:

  • ALL_OPERATIONS notifies on all changes

  • INSERTOP notifies on inserts

  • UPDATEOP notifies on updates

  • DELETEOP notifies on deletes

  • ALTEROP notifies on ALTER TABLE operations

  • DROPOP notifies on DROP TABLE operations

  • UNKNOWNOP notifies on unknown operations

You can specify a combination of operations with a bitwise OR. For example: DBMS_CQ_NOTIFICATION.INSERTOP + DBMS_CQ_NOTIFICATION.DELETEOP.

TRANSACTION_LAG

Deprecated. To implement flow-of-control notifications, use the NTFN_GROUPING_* attributes.

Applies only to OCN (described in "Object Change Notification (OCN)"). Has no effect if you specify the QOS_FLAGS attribute with its QOS_QUERY flag.

Specifies the number of transactions or database changes by which the client can lag behind the database. If 0, then the client receives an invalidation message as soon as it is generated. If 5, then every fifth transaction that changes a registered object results in a notification. The database tracks intervening changes at an object granularity and bundles the changes along with the notification. Thus, the client does not lose intervening changes.

Most applications that must be notified of changes to an object on transaction commit without further deferral are expected to chose 0 transaction lag. A nonzero transaction lag is useful only if an application implements flow control on notifications. When using nonzero transaction lag, it is recommended that the application workload has the property that notifications are generated at a reasonable frequency. Otherwise, notifications might be deferred indefinitely till the lag is satisfied.

If you specify TRANSACTION_LAG, then the ROWID level granularity is not available in the notification messages even if you specified QOS_ROWIDS during registration.

NTFN_GROUPING_CLASS

Specifies the class by which to group notifications. The only allowed value is DBMS_CQ_NOTIFICATION.NTFN_GROUPING_CLASS_TIME, which groups notifications by time.

NTFN_GROUPING_VALUE

Specifies the time interval that defines the group, in seconds. For example, if this value is 900, notifications generated in the same 15-minute interval are grouped.

NTFN_GROUPING_TYPE

Specifies either of these types of grouping:

  • DBMS_CQ_NOTIFICATION.NTFN_GROUPING_TYPE_SUMMARY: All notifications in the group are summarized into a single notification.

  • DBMS_CQ_NOTIFICATION.NTFN_GROUPING_TYPE_LAST: Only the last notification in the group is published and the earlier ones discarded.

NTFN_GROUPING_START_TIME

Specifies when to start generating notifications. If specified as NULL, it defaults to the current system-generated time.

NTFN_GROUPING_REPEAT_COUNT

Specifies how many times to repeat the notification. Set to DBMS_CQ_NOTIFICATION.NTFN_GROUPING_FOREVER to receive notifications for the life of the registration. To receive at most n notifications during the life of the registration, set to n.


The quality-of-service flags in Table 11-3 are constants in the DBMS_CQ_NOTIFICATION package. You can specify them with the QOS_FLAGS attribute of CQ_NOTIFICATION$_REG_INFO (see Table 11-2).

Table 11-3 Quality-of-Service Flags

FlagDescription

QOS_DEREG_NFY

Purges the registration after the first notification.

QOS_RELIABLE

Stores notifications in a persistent database queue.

In an Oracle RAC environment, if a database instance fails, surviving database instances can deliver any queued notification messages.

Default: Notifications are stored in shared memory, which performs more efficiently.

QOS_ROWIDS

Includes the ROWID of each changed row in the notification.

QOS_QUERY

Registers queries for QRCN, described in Query Result Change Notification (QRCN).

If a query cannot be registered for QRCN, an error is generated at registration time, unless you also specify QOS_BEST_EFFORT.

Default: Queries are registered for OCN, described in "Object Change Notification (OCN)"

QOS_BEST_EFFORT

Used with QOS_QUERY. Registers simplified versions of queries that are too complex for query result change evaluation; in other words, registers queries for QRCN in best-effort mode, described in "Best-Effort Mode".

To see which queries were simplified, query the static data dictionary view DBA_CQ_NOTIFICATION_QUERIES or USER_CQ_NOTIFICATION_QUERIES. These views give the QUERYID and the text of each registered query.

Default: Queries are registered for QRCN in guaranteed mode, described in "Guaranteed Mode"


Suppose that you must invoke the procedure HR.dcn_callback whenever a registered object changes. In Example 11-4, you create a CQ_NOTIFICATION$_REG_INFO object that specifies that HR.dcn_callback receives notifications. To create the object you must have EXECUTE privileges on the DBMS_CQ_NOTIFICATION package.

Example 11-4 Creating a CQ_NOTIFICATION$_REG_INFO Object

DECLARE
  v_cn_addr CQ_NOTIFICATION$_REG_INFO;

BEGIN
  -- Create object:

  v_cn_addr := CQ_NOTIFICATION$_REG_INFO (
    'HR.dcn_callback',                 -- PL/SQL notification handler
    DBMS_CQ_NOTIFICATION.QOS_QUERY     -- notification type QRCN
    + DBMS_CQ_NOTIFICATION.QOS_ROWIDS, -- include rowids of changed objects
    0,                          -- registration persists until unregistered
    0,                          -- notify on all operations
    0                           -- notify immediately
    );

  -- Register queries: ...
END;
/

Identifying Individual Queries in a Notification

Any query in a registered list of queries can cause a continuous query notification. To know when a certain query causes a notification, use the DBMS_CQ_NOTIFICATION.CQ_NOTIFICATION_QUERYID function in the SELECT list of that query. For example:

SELECT EMPLOYEE_ID, SALARY, DBMS_CQ_NOTIFICATION.CQ_NOTIFICATION_QUERYID
FROM EMPLOYEES
WHERE DEPARTMENT_ID = 10;

Result:

EMPLOYEE_ID     SALARY CQ_NOTIFICATION_QUERYID
----------- ---------- -----------------------
        200       2800                       0
 
1 row selected.

When that query causes a notification, the notification includes the query ID.

Adding Queries to an Existing Registration

To add queries to an existing registration, follow these steps:

  1. Retrieve the registration ID of the existing registration.

    You can retrieve it from either saved output or a query of *_CHANGE_NOTIFICATION_REGS.

  2. Open the existing registration by calling the procedure DBMS_CQ_NOTIFICATION.ENABLE_REG with the registration ID as the parameter.

  3. Run the queries to register. (Do not run DML or DDL operations.)

  4. Close the registration, using the DBMS_CQ_NOTIFICATION.REG_END function.

Example 11-5 adds a query to an existing registration whose registration ID is 21.

Example 11-5 Adding a Query to an Existing Registration

DECLARE
  v_cursor SYS_REFCURSOR;

BEGIN
  -- Open existing registration
  DBMS_CQ_NOTIFICATION.ENABLE_REG(21);
  OPEN v_cursor FOR
    -- Run query to be registered
    SELECT DEPARTMENT_ID
      FROM HR.DEPARTMENTS;  -- register this query
  CLOSE v_cursor;
  -- Close registration
  DBMS_CQ_NOTIFICATION.REG_END;
END;
/

Best Practices for CQN Registrations

For best CQN performance, follow these registration guidelines:

  • Register few queries—preferably those that reference objects that rarely change.

    Extremely volatile registered objects cause numerous notifications, whose overhead slows OLTP throughput.

  • Minimize the number of duplicate registrations of any given object, to avoid replicating a notification message for multiple recipients.

Troubleshooting CQN Registrations

If you are unable to create a registration, or if you have created a registration but are not receiving the notifications that you expected, the problem might be one of these:

  • The JOB_QUEUE_PROCESSES parameter is not set to a nonzero value.

    This prevents you from receiving PL/SQL notifications through the notification handler.

  • You were connected as a SYS user when you created the registrations.

    You must be connected as a non-SYS user to create CQN registrations.

  • You changed a registered object, but did not commit the transaction.

    Notifications are generated only when the transaction commits.

  • The registrations were not successfully created in the database.

    To check, query the static data dictionary view *_CHANGE_NOTIFICATION_REGS. For example, this statement displays all registrations and registered objects for the current user:

    SELECT REGID, TABLE_NAME FROM USER_CHANGE_NOTIFICATION_REGS;
    
  • Runtime errors occurred during the execution of the notification handler.

    If so, they were logged to the trace file of the JOBQ process that tried to run the procedure. The name of the trace file usually has this form:

    ORACLE_SID_jnumber_PID.trc
    

    For example, if the ORACLE_SID is dbs1 and the process ID (PID) of the JOBQ process is 12483, the name of the trace file is usually dbs1_j000_12483.trc.

    Suppose that a registration is created with 'chnf_callback' as the notification handler and registration ID 100. Suppose that 'chnf_callback' was not defined in the database. Then the JOBQ trace file might contain a message of the form:

    ****************************************************************************
       Run-time error during execution of PL/SQL cbk chnf_callback for reg CHNF100.
       Error in PLSQL notification of msgid:
       Queue :
       Consumer Name :
       PLSQL function :chnf_callback
       Exception Occured, Error msg:
       ORA-00604: error occurred at recursive SQL level 2
       ORA-06550: line 1, column 7: 
       PLS-00201: identifier 'CHNF_CALLBACK' must be declared
       ORA-06550: line 1, column 7:
       PL/SQL: Statement ignored
    ****************************************************************************
    

    If runtime errors occurred during the execution of the notification handler, create a very simple version of the notification handler to verify that you are actually receiving notifications, and then gradually add application logic.

    An example of a very simple notification handler is:

    REM Create table in HR schema to hold count of notifications received.
    CREATE TABLE nfcount(cnt NUMBER);
    INSERT INTO nfcount (cnt) VALUES(0);
    COMMIT;
    CREATE OR REPLACE PROCEDURE chnf_callback
      (ntfnds IN CQ_NOTIFICATION$_DESCRIPTOR)
    IS
    BEGIN
      UPDATE nfcount SET cnt = cnt+1;
      COMMIT;
    END;
    /
    
  • There is a time lag between the commit of a transaction and the notification received by the end user.

Querying CQN Registrations

To see top-level information about all registrations, including their QOS options, query the static data dictionary view *_CHANGE_NOTIFICATION_REGS.

For example, you can obtain the registration ID for a client and the list of objects for which it receives notifications. To view registration IDs and table names for HR, use this query:

SELECT regid, table_name FROM USER_CHANGE_NOTIFICATION_REGS;

To see which queries are registered for QRCN, query the static data dictionary view USER_CQ_NOTIFICATION_QUERIES or DBA_CQ_NOTIFICATION_QUERIES. These views include information about any bind values that the queries use. In these views, bind values in the original query are included in the query text as constants. The query text is equivalent, but maybe not identical, to the original query that was registered.


See Also:

Oracle Database Reference for more information about the static data dictionary views USER_CHANGE_NOTIFICATION_REGS and DBA_CQ_NOTIFICATION_QUERIES

Interpreting Notifications

When a transaction commits, the database determines whether registered objects were modified in the transaction. If so, it runs the notification handler specified in the registration.

Topics:

Interpreting a CQ_NOTIFICATION$_DESCRIPTOR Object

When a CQN registration generates a notification, the database passes a CQ_NOTIFICATION$_DESCRIPTOR object to the notification handler. The notification handler can find the details of the database change in the attributes of the CQ_NOTIFICATION$_DESCRIPTOR object.

In SQL*Plus, you can list these attributes by connecting as SYS and running this statement:

DESC CQ_NOTIFICATION$_DESCRIPTOR

Table 11-4 summarizes the attributes of CQ_NOTIFICATION$_DESCRIPTOR.

Table 11-4 Attributes of CQ_NOTIFICATION$_DESCRIPTOR

AttributeDescription

REGISTRATION_ID

The registration ID that was returned during registration.

TRANSACTION_ID

The ID for the transaction that made the change.

DBNAME

The name of the database in which the notification was generated.

EVENT_TYPE

The database event that triggers a notification. For example, the attribute can contain these constants, which correspond to different database events:

  • EVENT_NONE

  • EVENT_STARTUP (Instance startup)

  • EVENT_SHUTDOWN (Instance shutdown - last instance shutdown for Oracle RAC)

  • EVENT_SHUTDOWN_ANY (Any instance shutdown for Oracle RAC)

  • EVENT_DEREG (Registration was removed)

  • EVENT_OBJCHANGE (Change to a registered table)

  • EVENT_QUERYCHANGE (Change to a registered result set)

NUMTABLES

The number of tables that were modified.

TABLE_DESC_ARRAY

This field is present only for OCN registrations. For QRCN registrations, it is NULL.

If EVENT_TYPE is EVENT_OBJCHANGE]: a VARRAY of table change descriptors of type CQ_NOTIFICATION$_TABLE, each of which corresponds to a changed table. For attributes of CQ_NOTIFICATION$_TABLE, see Table 11-5.

Otherwise: NULL.

QUERY_DESC_ARRAY

This field is present only for QRCN registrations. For OCN registrations, it is NULL.

If EVENT_TYPE is EVENT_QUERYCHANGE]: a VARRAY of result set change descriptors of type CQ_NOTIFICATION$_QUERY, each of which corresponds to a changed result set. For attributes of CQ_NOTIFICATION$_QUERY, see Table 11-6.

Otherwise: NULL.


Interpreting a CQ_NOTIFICATION$_TABLE Object

The CQ_NOTIFICATION$_DESCRIPTOR type contains an attribute called TABLE_DESC_ARRAY, which holds a VARRAY of table descriptors of type CQ_NOTIFICATION$_TABLE.

In SQL*Plus, you can list these attributes by connecting as SYS and running this statement:

DESC CQ_NOTIFICATION$_TABLE

Table 11-5 summarizes the attributes of CQ_NOTIFICATION$_TABLE.

Table 11-5 Attributes of CQ_NOTIFICATION$_TABLE

AttributeSpecifies . . .

OPFLAGS

The type of operation performed on the modified table. For example, the attribute can contain these constants, which correspond to different database operations:

  • ALL_ROWS signifies that either the entire table is modified, as in a DELETE *, or row-level granularity of information is not requested or not available in the notification, and the recipient must assume that the entire table has changed

  • UPDATEOP signifies an update

  • DELETEOP signifies a deletion

  • ALTEROP signifies an ALTER TABLE

  • DROPOP signifies a DROP TABLE

  • UNKNOWNOP signifies an unknown operation

TABLE_NAME

The name of the modified table.

NUMROWS

The number of modified rows.

ROW_DESC_ARRAY

A VARRAY of row descriptors of type CQ_NOTIFICATION$_ROW, which Table 11-7 describes. If ALL_ROWS was set in the opflags, then the ROW_DESC_ARRAY member is NULL.


Interpreting a CQ_NOTIFICATION$_QUERY Object

The CQ_NOTIFICATION$_DESCRIPTOR type contains an attribute called QUERY_DESC_ARRAY, which holds a VARRAY of result set change descriptors of type CQ_NOTIFICATION$_QUERY.

In SQL*Plus, you can list these attributes by connecting as SYS and running this statement:

DESC CQ_NOTIFICATION$_QUERY

Table 11-6 summarizes the attributes of CQ_NOTIFICATION$_QUERY.

Table 11-6 Attributes of CQ_NOTIFICATION$_QUERY

AttributeSpecifies . . .

QUERYID

Query ID of the changed query.

QUERYOP

Operation that changed the query (either EVENT_QUERYCHANGE or EVENT_DEREG).

TABLE_DESC_ARRAY

A VARRAY of table change descriptors of type CQ_NOTIFICATION$_TABLE, each of which corresponds to a changed table that caused a change in the result set. For attributes of CQ_NOTIFICATION$_TABLE, see Table 11-5.


Interpreting a CQ_NOTIFICATION$_ROW Object

If the ROWID option was specified during registration, the CQ_NOTIFICATION$_TABLE type has a ROW_DESC_ARRAY attribute, a VARRAY of type CQ_NOTIFICATION$_ROW that contains the ROWIDs for the changed rows. If ALL_ROWS was set in the OPFLAGS field of the CQ_NOTIFICATION$_TABLE object, then ROWID information is not available.

Table 11-7 summarizes the attributes of CQ_NOTIFICATION$_ROW.

Table 11-7 Attributes of CQ_NOTIFICATION$_ROW

AttributeSpecifies . . .

OPFLAGS

The type of operation performed on the modified table. See the description of OPFLAGS in Table 11-5.

ROW_ID

The ROWID of the changed row.


Deleting Registrations

To delete a registration, call the procedure DBMS_CQ_NOTIFICATION.DEREGISTER with the registration ID as the parameter. For example, this statement deregisters the registration whose registration ID is 21:

DBMS_CQ_NOTIFICATION.DEREGISTER(21);

Only the user who created the registration or the SYS user can deregister it.

Configuring CQN: Scenario

In this scenario, you are a developer who manages a Web application that provides employee data: name, location, phone number, and so on. The application, which runs on Oracle Application Server, is heavily used and processes frequent queries of the HR.EMPLOYEES and HR.DEPARTMENTS tables in the back-end database. Because these tables change relatively infrequently, the application can improve performance by caching the query results. Caching avoids a round trip to the back-end database and server-side execution latency.

You can use the DBMS_CQ_NOTIFICATION package to register queries based on HR.EMPLOYEES and HR.DEPARTMENTS tables. To configure CQN, you follow these steps:

  1. Create a server-side PL/SQL stored procedure to process the notifications, as instructed in "Creating a PL/SQL Notification Handler".

  2. Register the queries on the HR.EMPLOYEES and HR.DEPARTMENTS tables for QRCN, as instructed in "Registering the Queries".

After you complete these steps, any committed change to the result of a query registered in step 2 causes the notification handler created in step 1 to notify the Web application of the change, whereupon the Web application refreshes the cache by querying the back-end database.

Topics:

Creating a PL/SQL Notification Handler

Create a a server-side stored PL/SQL procedure to process notifications as follows:

  1. Connect to the database AS SYSDBA.

  2. Grant the required privileges to HR:

    GRANT EXECUTE ON DBMS_CQ_NOTIFICATION TO HR;
    GRANT CHANGE NOTIFICATION TO HR;
    
  3. Enable the JOB_QUEUE_PROCESSES parameter to receive notifications:

    ALTER SYSTEM SET "JOB_QUEUE_PROCESSES"=4;
    
  4. Connect to the database as a non-SYS user (such as HR).

  5. Create database tables to hold records of notification events received:

    -- Create table to record notification events.
    DROP TABLE nfevents;
    CREATE TABLE nfevents (
      regid      NUMBER,
      event_type NUMBER
    );
    
    -- Create table to record notification queries:
    DROP TABLE nfqueries;
    CREATE TABLE nfqueries (
      qid NUMBER,
      qop NUMBER
    );
    
    -- Create table to record changes to registered tables:
    DROP TABLE nftablechanges;
    CREATE TABLE nftablechanges (
      qid             NUMBER,
      table_name      VARCHAR2(100),
      table_operation NUMBER
    );
    
    -- Create table to record ROWIDs of changed rows:
    DROP TABLE nfrowchanges;
    CREATE TABLE nfrowchanges (
      qid        NUMBER,
      table_name VARCHAR2(100),
      row_id     VARCHAR2(2000)
    );
    
  6. Create the procedure HR.chnf_callback, as shown in Example 11-6.

Example 11-6 Creating Server-Side PL/SQL Notification Handler

CREATE OR REPLACE PROCEDURE chnf_callback (
  ntfnds IN CQ_NOTIFICATION$_DESCRIPTOR
)
IS
  regid           NUMBER;
  tbname          VARCHAR2(60);
  event_type      NUMBER;
  numtables       NUMBER;
  operation_type  NUMBER;
  numrows         NUMBER;
  row_id          VARCHAR2(2000);
  numqueries      NUMBER;
  qid             NUMBER;
  qop             NUMBER;

BEGIN
  regid := ntfnds.registration_id;
  event_type := ntfnds.event_type;

  INSERT INTO nfevents (regid, event_type)
  VALUES (chnf_callback.regid, chnf_callback.event_type);

  numqueries :=0;

  IF (event_type = DBMS_CQ_NOTIFICATION.EVENT_QUERYCHANGE) THEN
    numqueries := ntfnds.query_desc_array.count;

    FOR i IN 1..numqueries LOOP  -- loop over queries
      qid := ntfnds.query_desc_array(i).queryid;
      qop := ntfnds.query_desc_array(i).queryop;

      INSERT INTO nfqueries (qid, qop)
      VALUES(chnf_callback.qid, chnf_callback.qop);

      numtables := 0;
      numtables := ntfnds.query_desc_array(i).table_desc_array.count;

      FOR j IN 1..numtables LOOP  -- loop over tables
        tbname :=
          ntfnds.query_desc_array(i).table_desc_array(j).table_name;
        operation_type :=
          ntfnds.query_desc_array(i).table_desc_array(j).Opflags;

        INSERT INTO nftablechanges (qid, table_name, table_operation) 
        VALUES (
          chnf_callback.qid,
          tbname,
          operation_type
        );

        IF (bitand(operation_type, DBMS_CQ_NOTIFICATION.ALL_ROWS) = 0) THEN
          numrows := ntfnds.query_desc_array(i).table_desc_array(j).numrows;
        ELSE
          numrows :=0;  -- ROWID info not available
        END IF;

        -- Body of loop does not run when numrows is zero.
        FOR k IN 1..numrows LOOP  -- loop over rows
          Row_id :=
 ntfnds.query_desc_array(i).table_desc_array(j).row_desc_array(k).row_id;

          INSERT INTO nfrowchanges (qid, table_name, row_id)
          VALUES (chnf_callback.qid, tbname, chnf_callback.Row_id);

        END LOOP;  -- loop over rows
      END LOOP;  -- loop over tables
    END LOOP;  -- loop over queries
  END IF;
  COMMIT;
END;
/

Registering the Queries

After creating the notification handler, you register the queries for which you want to receive notifications, specifying HR.chnf_callback as the notification handler, as in Example 11-7.

Example 11-7 Registering a Query

DECLARE
  reginfo   CQ_NOTIFICATION$_REG_INFO;
  mgr_id    NUMBER;
  dept_id   NUMBER;
  v_cursor  SYS_REFCURSOR;
  regid     NUMBER;

BEGIN
  /* Register two queries for QRNC: */
  /* 1. Construct registration information.
        chnf_callback is name of notification handler.
        QOS_QUERY specifies result-set-change notifications. */

  reginfo := cq_notification$_reg_info (
    'chnf_callback',
    DBMS_CQ_NOTIFICATION.QOS_QUERY,
    0, 0, 0
  );

  /* 2. Create registration. */

  regid := DBMS_CQ_NOTIFICATION.new_reg_start(reginfo);

  OPEN v_cursor FOR
    SELECT dbms_cq_notification.CQ_NOTIFICATION_QUERYID, manager_id
    FROM HR.EMPLOYEES
    WHERE employee_id = 7902;
  CLOSE v_cursor;

  OPEN v_cursor FOR
    SELECT dbms_cq_notification.CQ_NOTIFICATION_QUERYID, department_id
    FROM HR.departments
    WHERE department_name = 'IT';
  CLOSE v_cursor;

  DBMS_CQ_NOTIFICATION.reg_end;
END;
/

View the newly created registration:

SELECT queryid, regid, TO_CHAR(querytext)
FROM user_cq_notification_queries;

Result is similar to:

QUERYID REGID                               TO_CHAR(QUERYTEXT)
------- ----- ------------------------------------------------
     22    41 SELECT HR.DEPARTMENTS.DEPARTMENT_ID
                FROM HR.DEPARTMENTS
                  WHERE HR.DEPARTMENTS.DEPARTMENT_NAME  = 'IT'

     21    41 SELECT HR.EMPLOYEES.MANAGER_ID
                FROM HR.EMPLOYEES
                  WHERE HR.EMPLOYEES.EMPLOYEE_ID  = 7902

Run this transaction, which changes the result of the query with QUERYID 22:

UPDATE DEPARTMENTS
SET DEPARTMENT_NAME = 'FINANCE'
WHERE department_name = 'IT';

The notification procedure chnf_callback (which you created in Example 11-6) runs.

Query the table in which notification events are recorded:

SELECT * FROM nfevents;

Result is similar to:

REGID EVENT_TYPE
----- ----------
   61          7

EVENT_TYPE 7 corresponds to EVENT_QUERYCHANGE (query result change).

Query the table in which changes to registered tables are recorded:

SELECT * FROM nftablechanges;

Result is similar to:

REGID     TABLE_NAME TABLE_OPERATION
----- -------------- ---------------
   42 HR.DEPARTMENTS               4

TABLE_OPERATION 4 corresponds to UPDATEOP (update operation).

Query the table in which ROWIDs of changed rows are recorded:

SELECT * FROM nfrowchanges;

Result is similar to:

REGID     TABLE_NAME              ROWID
----- -------------- ------------------
   61 HR.DEPARTMENTS AAANkdAABAAALinAAF
PKNIyePK|%AOEBPS/content.opfd< Oracle® Database Advanced Application Developer's Guide, 11g Release 2 (11.2) en-US E25518-05 Oracle Corporation Oracle Corporation Oracle® Database Advanced Application Developer's Guide, 11g Release 2 (11.2) 2012-04-30T10:06:40Z Explains topics that experienced application developers reference repeatedly. PKi<d<PK|%AOEBPS/adfns_sqltypes.htm Using SQL Data Types in Database Applications

2 Using SQL Data Types in Database Applications

This chapter explains how to use SQL data types in database applications.

Topics:


See Also:


Overview of SQL Data Types

A data type associates fixed properties with the values that can be inserted in table columns or passed as parameters to subprograms. These properties cause Oracle Database to treat values of different data types differently. For example, Oracle Database can add values of NUMBER data type, but cannot add values of RAW data type.

Oracle Database provides many data types and several categories for user-defined types that can be used as data types.

The Oracle precompilers recognize other data types in embedded SQL programs. These data types are called external data types and are associated with host variables. Do not confuse external data types with Oracle built-in, Oracle-supplied, and user-defined data types.


See Also:


Representing Character Data

Table 2-1 summarizes the SQL data types that store alphanumeric data.

Table 2-1 SQL Character Data Types

Data TypesValues Stored

CHAR

Fixed-length character literals

NCHAR

Fixed-length Unicode character literals

VARCHAR2

Variable-length character literals

NVARCHAR2

Variable-length Unicode character literals

CLOB

Single-byte and multibyte character strings of up to (4 gigabytes - 1) * (the value obtained from DBMS_LOB.GETCHUNKSIZE)

NCLOB

Single-byte and multibyte Unicode character strings of up to (4 gigabytes - 1) * (the value obtained from DBMS_LOB.GETCHUNKSIZE)

LONG

Variable-length character data of up to 2 gigabytes - 1. Provided only for backward compatibility.


For a client/server application, if the character set on the client side differs from the character set on the server side, then Oracle Database automatically converts CHAR, VARCHAR2, and LONG data from the database character set (determined by the NLS_LANGUAGE parameter) to the character set defined for the user session.

Topics:


See Also:


Specifying Column Lengths as Bytes or Characters

You can specify the lengths of CHAR and VARCHAR2 columns as either bytes or characters. The lengths of NCHAR and NVARCHAR2 columns are always specified in characters, making them ideal for storing Unicode character literals, where a character might consist of multiple bytes. This table shows some column length specifications and their meanings:

Column Length SpecificationMeaning
id VARCHAR2(32 BYTE)The id column contains up to 32 single-byte characters.
name VARCHAR2(32 CHAR)The name column contains up to 32 characters of the database character set. If the database character set includes multibyte characters, then the 32 characters can occupy more than 32 bytes.
biography NVARCHAR2(2000)The biography column contains up to 2000 characters of any Unicode-representable language. The encoding depends on the national character set. The column can contain multibyte values even if the database character set is single-byte.
comment VARCHAR2(2000)The comment column contains up to 2000 bytes or characters, depending on the value of the initialization parameter NLS_LENGTH_SEMANTICS.

When using a multibyte database character encoding scheme, consider carefully the space required for tables with character columns. If the database character encoding scheme is single-byte, then the number of bytes and the number of characters in a column is the same. If it is multibyte, however, then generally there is no such correspondence. A character might consist of one or more bytes, depending on the specific multibyte encoding scheme and whether shift-in/shift-out control codes are present. To avoid overflowing buffers, specify data as NCHAR or NVARCHAR2 if it might use a Unicode encoding that is different from the database character set.


See Also:


Choosing Between CHAR and VARCHAR2 Data Types

When choosing a data type for a column that stores alphanumeric data in a table, consider:

  • Space usage

    Oracle Database blank-pads values stored in CHAR columns but not values stored in VARCHAR2 columns. Therefore, VARCHAR2 columns use space more efficiently than CHAR columns.

  • Performance

    Because of the blank-padding difference, a full table scan on a large table containing VARCHAR2 columns might read fewer data blocks than a full table scan on a table containing the same data stored in CHAR columns. If your application often performs full table scans on large tables containing character data, then you might be able to improve performance by storing data in VARCHAR2 columns rather than in CHAR columns.

  • Comparison semantics

    When you need ANSI compatibility in comparison semantics, use the CHAR data type. When trailing blanks are important in string comparisons, use the VARCHAR2 data type.


    See Also:

    Oracle Database SQL Language Reference for more information about comparison semantics for these data types

  • Future compatibility

    The CHAR and VARCHAR2 data types are fully supported. Today, the VARCHAR data type automatically corresponds to the VARCHAR2 data type and is reserved for future use.

Representing Numeric Data

The SQL data types that store numeric data are NUMBER, BINARY_FLOAT, and BINARY_DOUBLE.

The NUMBER data type stores real numbers in either a fixed-point or floating-point format. NUMBER offers up to 38 decimal digits of precision. In a NUMBER column, you can store positive and negative numbers of magnitude 1 x 10-130 through 9.99 x10125, and 0. All Oracle Database platforms support NUMBER values.

The BINARY_FLOAT and BINARY_DOUBLE data types store floating-point numbers in the single-precision (32-bit) IEEE 754 format and the double-precision (64-bit) IEEE 754 format, respectively. High-precision values use less space when stored as BINARY_FLOAT and BINARY_DOUBLE than when stored as NUMBER. Arithmetic operations on floating-point numbers are usually faster for BINARY_FLOAT and BINARY_DOUBLE values than for NUMBER values.

In client interfaces that Oracle Database supports, arithmetic operations on BINARY_FLOAT and BINARY_DOUBLE values are performed by the native instruction set that the hardware vendor supplies. The term native floating-point data type includes BINARY_FLOAT and BINARY_DOUBLE data types and all implementations of these types in supported client interfaces.

Native floating-point data types conform substantially with the Institute of Electrical and Electronics Engineers (IEEE) Standard for Binary Floating-Point Arithmetic, IEEE Standard 754-1985 (IEEE754). For details, see Oracle Database SQL Language Reference.

Topics:


See Also:


Floating-Point Number Components

A floating-point number has these components:

  • Binary-valued sign

  • Signed exponent

  • Significand

  • Base

The formula for a floating-point value is:

(-1)sign.significand.baseexponent

For example, the floating-point value 4.31 is represented:

(-1)0.431.10-2

The components of the preceding representation are:

Component NameComponent Value
Sign0
Significand431
Base10
Exponent-2

Floating-Point Number Formats

A floating-point number format specifies how the components of a floating-point number are represented, thereby determining the range and precision of the values that the format can represent. The range is the interval bounded by the smallest and largest values and the precision is the number of significant digits. Both range and precision are finite. If a floating-point number is too precise for a given format, then the number is rounded.

How the number is rounded depends on the base of its format, which can be either decimal or binary. A number stored in decimal format is rounded to the nearest decimal place (for example, 1000, 10, or 0.01). A number stored in binary format is rounded to the nearest binary place (for example, 1024, 512, or 1/64).

NUMBER values are stored in decimal format. For calculations that need decimal rounding, use the NUMBER data type.

Native floating-point values are stored in binary format.

Topics:

Binary Floating-Point Formats

This formula determines the value of a floating-point number that uses a binary format:

(-1)sign 2E (bit0 bit1 bit2 ... bitp-1)

Table 2-2 describes the components of the preceding formula.

Table 2-2 Binary Floating-Point Format Components

ComponentComponent Value

sign

0 or 1

E (exponent)

For single-precision (32-bit) data type, an integer from -126 through 127.

For double-precision (64-bit) data type, an integer from -1022 through 1023.

biti

0 or 1. (The bit sequence represents a number in base 2.)

p (precision)

For single-precision data type, 24.

For double-precision data type, 53.


The leading bit of the significand, b0, must be set (1), except for subnormal numbers (explained later). Therefore, the leading bit is not actually stored, and a binary format provides n bits of precision while storing only n-1 bits. The IEEE 754 standard defines the in-memory formats for single-precision and double-precision data types, which Table 2-3 shows.

Table 2-3 Summary of Binary Format Storage Parameters

Data TypeSign BitExponent BitsSignificand BitsTotal Bits

Single-precision

1

8

24 (23 stored)

32

Double-precision

1

11

53 (52 stored)

64



Note:

Oracle Database does not support the extended single- and double-precision formats that the IEEE 754 standard defines.

A significand whose leading bit is set is called normalized. The IEEE 754 standard defines subnormal numbers (also called denormal numbers) that are too small to represent with normalized significands. If the significand of a subnormal number were normalized, then its exponent would be too large. Subnormal numbers preserve this property: If x-y==0.0 (using floating-point subtraction), then x==y. IEEE 754 formats support subnormal values.

Table 2-4 shows the range and precision of the IEEE 754 single- and double-precision formats and Oracle Database NUMBER. Range limits are expressed as positive numbers, but they also apply to absolute values of negative numbers. (The notation "number e exponent" means number * 10exponent.)

Table 2-4 Range and Precision of Floating-Point Data Types

Range and PrecisionSingle-precision 32-bitFoot 1 Double-precision 64-bit1Oracle Database NUMBER Data Type

Maximum positive normal number

3.40282347e+38

1.7976931348623157e+308

< 1.0e126

Minimum positive normal number

1.17549435e-38

2.2250738585072014e-308

1.0e-130

Maximum positive subnormal number

1.17549421e-38

2.2250738585072009e-308

not applicable

Minimum positive subnormal number

1.40129846e-45

4.9406564584124654e-324

not applicable

Precision (decimal digits)

6 - 9

15 - 17

38 - 40


Footnote 1 These numbers are from the IEEE Numerical Computation Guide.


See Also:


Special Values for Native Floating-Point Formats

The IEEE 754 standard supports the special values shown in Table 2-5.

Table 2-5 Special Values for Native Floating-Point Formats

ValueMeaning

+INF

Positive infinity

-INF

Negative infinity

NaN

Not a number

+0

Positive zero

-0

Negative zero


Each value in Table 2-5 is represented by a specific bit pattern, except NaN. NaN, the result of any undefined operation, is represented by many bit patterns. Some of these bits patterns have the sign bit set and some do not, but the sign bit has no meaning.

The IEEE 754 standard distinguishes between quiet NaNs (which do not raise additional exceptions as they propagate through most operations) and signaling NaNs (which do). The IEEE 754 standard specifies action for when exceptions are enabled and action for when they are disabled.

In Oracle Database, exceptions cannot be enabled. Oracle Database acts as the IEEE 754 standard specifies for when exceptions are disabled. In particular, Oracle Database does not distinguish between quiet and signaling NaNs. You can use Oracle Call Interface (OCI) to retrieve NaN values from Oracle Database, but whether a retrieved NaN value is signaling or quiet depends on the client platform and is beyond the control of Oracle Database.

The IEEE 754 standard defines these classes of special values:

  • Zero

  • Subnormal

  • Normal

  • Infinity

  • NaN

The values in each class in the preceding list are larger than the values in the classes that precede it in the list (ignoring signs), except NaN. NaN is unordered with other classes of special values and with itself.

In Oracle Database:

  • All NaNs are quiet.

  • Any non-NaN value < NaN

  • Any NaN == any other NaN

  • All NaNs are converted to the same bit pattern.

  • -0 is converted to +0.

  • IEEE 754 exceptions are not raised.


See Also:

Oracle Database SQL Language Reference for information about floating-point conditions, which let you determine whether an expression is infinite or is the undefined result of an operation (is not a number or NaN).

Comparison Operators for Native Floating-Point Data Types

Oracle Database defines these comparison operators for native floating-point data types:

  • Equal to

  • Not equal to

  • Greater than

  • Greater than or equal to

  • Less than

  • Less than or equal to

  • Unordered

Comparisons ignore the sign of zero (-0 equals +0).


See Also:

"Special Values for Native Floating-Point Formats" for more information about comparison results, ordering, and other actions of special values

Arithmetic Operations with Native Floating-Point Data Types

Oracle Database defines these arithmetic operators for native floating-point data types:

  • Multiplication

  • Division

  • Addition

  • Subtraction

  • Remainder

  • Square root

You can define the mode used to round the result of the operation. Exceptions can be raised when operations are performed. Exceptions can also be disabled.

Formerly, Java required floating-point arithmetic to be exactly reproducible. IEEE 754 does not have this requirement. Therefore, results of operations (including arithmetic operations) can be delivered to a destination that uses a range greater than the range that the operands of the operation use.

You can compute the result of a double-precision multiplication at an extended double-precision destination, but the result must be rounded as if the destination were single-precision or double-precision. The range of the result (that is, the number of bits used for the exponent) can use the range supported by the wider (extended double-precision) destination; however, this might cause a double-rounding error in which the least significant bit of the result is incorrect.

This situation can occur only for double-precision multiplication and division on hardware that implements the IA-32 and IA-64 instruction set architecture. Therefore, except for this case, arithmetic for these data types is reproducible across platforms. When the result of a computation is NaN, all platforms produce a value for which IS NAN is true. However, all platforms do not have to use the same bit pattern.

Conversion Functions for Floating-Point Data Types

Oracle Database defines functions that convert between floating-point and other data types, including string formats that use decimal precision (but precision might be lost during the conversion). For example:

Oracle Database can raise exceptions during conversion. The IEEE 754 standard defines these exceptions:

  • Invalid

  • Inexact

  • Divide by zero

  • Underflow

  • Overflow

Oracle Database does not raise these exceptions for native floating-point data types. Generally, operations that raise exceptions produce the values described in Table 2-6.

Table 2-6 Values Resulting from Exceptions

ExceptionValue

Underflow

0

Overflow

-INF, +INF

Invalid Operation

NaN

Divide by Zero

-INF, +INF, NaN

Inexact

Any value – rounding was performed


Client Interfaces for Native Floating-Point Data Types

Oracle Database supports native floating-point data types in these client interfaces:

  • SQL

  • PL/SQL

  • Oracle Call Interface (OCI)

  • Oracle C++ Call Interface (OCCI)

  • Pro*C/C++

  • JDBC

Topics:

OCI Native Floating-Point Data Types SQLT_BFLOAT and SQLT_BDOUBLE

The OCI API implements the IEEE 754 single- and double-precision native floating-point data types with the data types SQLT_BFLOAT and SQLT_BDOUBLE, respectively. Conversions between these types and the SQL types BINARY_FLOAT and BINARY_DOUBLE are exact on platforms that implement the IEEE 754 standard for the C data types FLOAT and DOUBLE.

Native Floating-Point Data Types Supported in ADTs

Oracle Database supports the SQL data types BINARY_FLOAT and BINARY_DOUBLE as attributes of ADTs.

Pro*C/C++ Support for Native Floating-Point Data Types

Pro*C/C++ supports the native FLOAT and DOUBLE data types using the column data types BINARY_FLOAT and BINARY_DOUBLE. You can use these data types in the same way that Oracle Database NUMBER data type is used. You can bind FLOAT and DOUBLE to BINARY_FLOAT and BINARY_DOUBLE, respectively, by setting the Pro*C/C++ precompiler command line option NATIVE_TYPES to Y (yes) when you compile your application.

Representing Date and Time Data

Oracle Database stores date and time (datetime) data in its own internal format, in 7-byte fields that correspond to century, year, month, day, hour, minute, and second.

Table 2-7 summarizes the SQL datetime data types. For more information about these data types, see Oracle Database SQL Language Reference.

Table 2-7 SQL Datetime Data Types

Date TypeUsage

DATE

Use to store point-in-time (datetime) values in a table—for example, dates of jobs.

TIMESTAMP

Use to store datetime values that are precise to fractional seconds—for example, times of events that must be compared to determine the order in which they occurred.

TIMESTAMP WITH TIME ZONE

Use to store datetime values that must be gathered or coordinated across geographic regions.

TIMESTAMP WITH LOCAL TIME ZONE

Use to store datetime values when the time zone is insignificant—for example, in an application that schedules teleconferences, where participants each see the start and end times for their own time zone.

Appropriate for two-tier applications in which you want to display dates and times that use the time zone of the client system. Usually inappropriate for three-tier applications, because data displayed in a web browser is formatted according to the time zone of the web server, not the time zone of the browser. The web server is the database client, so its local time is used.

INTERVAL YEAR TO MONTH

Use to store the difference between two datetime values, where only the year and month are significant—for example, to set a reminder for a date 18 months in the future, or check whether 6 months have elapsed since a particular date.

INTERVAL DAY TO SECOND

Use to store the precise difference between two datetime values—for example, to set a reminder for a time 36 hours in the future or to record the time between the start and end of a race. To represent long spans of time with high precision, use a large number of days.


Topics:


See Also:

Oracle Call Interface Programmer's Guide for more information about Oracle Database internal date types

Displaying Current Date and Time

The simplest way to display the current date and time is:

SELECT SYSDATE FROM DUAL

The preceding command displays the current date and time in the default date format, which depends on the initialization parameter NLS_DATE_FORMAT.

The standard Oracle Database default date format is DD-MON-RR. The RR datetime format element lets you store 20th century dates in the 21st century by specifying only the last two digits of the year. For example, in the datetime format DD-MON-YY, 13-NOV-54 refers to the year 1954 in a query issued between 1950 and 2049, but to the year 2054 in a query issued between 2050 and 2099.

To display SYSDATE in a nondefault format, use the TO_CHAR function with a datetime format model.

Example 2-1 uses TO_CHAR with a format model to display SYSDATE in a nondefault format, which includes the qualifier BC or AD. (By default, SYSDATE is displayed without this qualifier.)

Example 2-1 Displaying Current Date and Time in Nondefault Format

SELECT TO_CHAR(SYSDATE, 'DD-MON-YYYY BC') NOW FROM DUAL;
 

Result:

NOW
-----------------------
18-MAR-2009 AD
 
1 row selected.

Tip:

When testing code that uses SYSDATE, it can be helpful to set SYSDATE to a constant. Do this with the initialization parameter FIXED_DATE, described in Oracle Database Reference.


See Also:


Displaying and Inserting Dates in Nondefault Formats

Although Oracle Database always stores dates in the default date format (set by the initialization parameter NLS_DATE_FORMAT), you can display and insert dates in nondefault formats by using the TO_CHAR and TO_DATE functions, respectively, with datetime format models.

Example 2-2 creates a table with a DATE column and inserts into it a date specified in a nondefault format. The date is stored in the default format, as the first SELECT statement shows. The second SELECT statement displays the date in a nondefault format.

Example 2-2 Inserting and Displaying Date in Nondefault Formats

DROP TABLE dates;
CREATE TABLE dates (d DATE);
 
INSERT INTO dates VALUES (TO_DATE('OCT 27, 1998', 'MON DD, YYYY'));
 
SELECT d FROM dates;
 

Result:

D
---------
27-OCT-98
 
1 row selected.

SELECT TO_CHAR(d, 'YYYY-MON-DD') D FROM dates;

Result:

D
--------------------
1998-OCT-27
 
1 row selected.

Caution:

Be careful when using the YY datetime format element, which indicates the year in the current century. For example, in the 21st century, the format DD-MON-YY, 31-DEC-92 is December 31, 2092 (not December 31, 1992, as you might expect). To store 20th century dates in the 21st century by specifying only the last two digits of the year, use the RR datetime format element (the default).


See Also:


Displaying and Inserting Times in Nondefault Formats

Although Oracle Database always stores times in the 24-hour format HH24:MI:SS, you can display and insert times in nondefault formats by using the TO_CHAR and TO_DATE functions, respectively, with datetime format models.

In a DATE column:

  • The default time is 12:00:00 A.M. (midnight).

    The default time applies to any value in the column that has no time portion, either because none was specified or because the value was truncated.

  • The default date is the first day of the current month.

    The default date applies to any value in the column that has no date portion, because none was specified.

Example 2-3 creates a table with a DATE column and inserts into it three dates specified in nondefault formats—one with both date and time portions, one with no time portion, and one with no date portion. The first SELECT statement shows the current date. The second SELECT statement displays the three dates in a nondefault format that includes both date and time portions.

Example 2-3 Inserting and Displaying Dates and Times in Nondefault Formats

DROP TABLE birthdays;
CREATE TABLE birthdays (name VARCHAR2(20), day DATE);
 
INSERT INTO birthdays (name, day)
VALUES ('Annie',
        TO_DATE('13-NOV-92 10:56 A.M.','DD-MON-RR HH:MI A.M.')
       );
 
INSERT INTO birthdays (name, day)
VALUES ('Bobby',
        TO_DATE('5-APR-02','DD-MON-RR')
       );
 
INSERT INTO birthdays (name, day)
VALUES ('Cindy',
        TO_DATE('8:25 P.M.','HH:MI A.M.')
       );
 

Display current date:

SELECT SYSDATE FROM DUAL;
 

Result:

SYSDATE
---------
05-NOV-10
 
1 row selected.

Display both date and time portions of stored datetime values:

SELECT name,
       TO_CHAR(day, 'Mon DD, RRRR') DAY,
       TO_CHAR(day, 'HH:MI A.M.') TIME
FROM birthdays;
 

Result:

NAME                 DAY                   TIME
-------------------- --------------------- ----------
Annie                Nov 13, 1992          10:56 A.M.
Bobby                Apr 05, 2002          12:00 A.M.
Cindy                Nov 01, 2010          08:25 P.M.
 
3 rows selected.

Arithmetic Operations with Datetime Data Types

You can perform arithmetic operations on datetime values. The results of such operations are determined by the rules in Oracle Database SQL Language Reference.

SQL has many datetime functions that you can use in datetime expressions. For example, the function ADD_MONTHS returns the date that is a specified number of months from a specified date. For the complete list of datetime functions, see Oracle Database SQL Language Reference.

Conversion Functions for Datetime Data Types

Table 2-8 summarizes the SQL functions that convert to or from datetime data types.

Table 2-8 SQL Conversion Functions for Datetime Data Types

FunctionConverts ...To ...

NUMTODSINTERVAL

NUMBER

INTERVAL DAY TO SECOND

NUMTOYMINTERVAL

NUMBER

INTERVAL DAY TO MONTH

TO_CHAR

DATE

TIMESTAMP

TIMESTAMP WITH TIME ZONE

TIMESTAMP WITH LOCAL TIME ZONE

INTERVAL DAY TO SECOND

INTERVAL YEAR TO MONTH

VARCHAR2

TO_DATE

CHAR

VARCHAR2

NCHAR

NVARCHAR2

DATE

TO_DSINTERVAL

CHAR

VARCHAR2

NCHAR

NVARCHAR2

INTERVAL DAY TO SECOND

TO_TIMESTAMP

CHAR

VARCHAR2

NCHAR

NVARCHAR2

TIMESTAMP

TO_TIMESTAMP_TZ

CHAR

VARCHAR2

NCHAR

NVARCHAR2

TIMESTAMP WITH TIME ZONE

TO_YMINTERVAL

CHAR

VARCHAR2

NCHAR

NVARCHAR2

INTERVAL DAY TO MONTH


Importing, Exporting, and Comparing Datetime Types

You can import, export, and compare TIMESTAMP WITH TIME ZONE and TIMESTAMP WITH LOCAL TIME ZONE values without worrying about time zone offsets, because the database stores these values in normalized format.

When importing, exporting, and comparing DATE and TIMESTAMP values, you must adjust them to account for any time zone differences between source and target databases, because the database does not store their time zones.

Representing Specialized Data

Topics:

Representing Geographic Data

To represent Geographic Information System (GIS) or spatial data in the database, you can use Oracle Spatial features, including the type MDSYS.SDO_GEOMETRY. You can store the data in the database by using either an object-relational or a relational model. You can use a set of PL/SQL packages to query and manipulate the data.


See Also:

Oracle Spatial Developer's Guide for information about Oracle Spatial features

Representing Multimedia Data

Oracle Multimedia enables Oracle Database to store, manage, and retrieve images, audio, video, or other heterogeneous media data in an integrated fashion with other enterprise information. Oracle Multimedia extends Oracle Database reliability, availability, and data management to multimedia content in traditional, Internet, electronic commerce, and media-rich applications.

Whether you store such multimedia data inside the database as BLOB or BFILE values, or store it externally on a web server or other kind of server, you can use Oracle Multimedia to access the data using either an object-relational or a relational model, and manipulate and query the data using a set of ADTs.

Oracle Multimedia provides the ORDAudio, ORDDoc, ORDImage, ORDImageSignature, ORDVideo, and SI_StillImage ADTs (including methods) for these purposes:

  • Extracting metadata and attributes from multimedia data

  • Retrieving and managing multimedia data from Oracle Multimedia, web servers, file systems, and other servers

  • Performing manipulation operations on image data


See Also:

Oracle Multimedia Reference for information about Oracle Multimedia

Representing Large Amounts of Data

For representing large amounts of data, Oracle Database provides:

Large Objects (LOBs)

Large Objects (LOBs) are data types that are designed to store large amounts of data (the maximum size of a LOB depends on how your database is configured). Storing data in LOBs enables you to access and manipulate the data efficiently in your application.

Table 2-9 summarizes the LOBs.

Table 2-9 Large Objects (LOBs)

Data TypeDescription

BLOB

Binary large object

Stores any kind of data in binary format.

Typically used for multimedia data such as images, audio, and video.

CLOB

Character large object

Stores string data in the database character set format.

Used for large strings or documents that use the database character set exclusively.

NCLOB

National character large object

Stores string data in National Character Set format.

Used for large strings or documents in the National Character Set.

BFILE

External large object

Stores a binary file outside the database in the host operating system file system. Applications have read-only access to BFILEs.

Used for static data that applications do not manipulate, such as image data.

Any kind of data (that is, any operating system file) can be stored in a BFILE. For example, you can store character data in a BFILE and then load the BFILE data into a CLOB, specifying the character set when loading.


An instance of type BLOB, CLOB, or NCLOB can be either temporary (declared in the scope of your application) or persistent (created and stored in the database).


See Also:


LONG and LONG RAW Data Types


Note:

Oracle supports the LONG and LONG RAW data types for backward compatibility, but strongly recommends that you convert LONG columns to LOB columns and LONG RAW columns to BLOB columns.

LONG columns store variable-length character strings containing up to 2 gigabytes - 1 bytes. LONG columns have many of the characteristics of VARCHAR2 columns. You can use LONG columns to store long text strings. The length of LONG values may be limited by the memory available on your computer. For more information about the LONG data type, including its many restrictions, see Oracle Database SQL Language Reference.

The LONG RAW (and RAW) data types store data that is not to be explicitly converted by Oracle Database when moving data between different systems. These data types are intended for binary data or byte strings. For example, you can use LONG RAW to store graphics, sound, documents, or arrays of binary data, for which the interpretation is dependent on the use. You can index RAW data, but not LONG RAW data. For more information about the RAW and LONG RAW data types, see Oracle Database SQL Language Reference.

Representing Searchable Text

Rather than writing low-level code to do full-text searches, you can use Oracle Text. Oracle Text stores the search data in a special kind of index and lets you query the data with operators and PL/SQL packages. This technology enables you to create your own search engine using data from tables, files, or URLs, and combine the search logic with relational queries. You can also search XML data this way with the XPath notation.


See Also:

Oracle Text Application Developer's Guide for more information about Oracle Text

Representing XML Data

If you have information stored as files in XML format, or want to store an ADT in XML format, then you can use the Oracle-supplied type XMLType.

When you create an XMLType column in a table, you can store the XML data in any of these ways:

  • In a CLOB column

  • As binary XML (stored internally as a CLOB)

  • Object relationally

XMLType has member functions that access, extract, and query the XML data using W3C XPath expressions (see Oracle XML DB Developer's Guide). Also, Oracle provides SQL XML functions that manipulate or return whole or partial XML documents (see Oracle Database SQL Language Reference) and these PL/SQL packages (described in Oracle Database PL/SQL Packages and Types Reference):


See Also:


Representing Dynamically Typed Data

Some languages allow data types to change at run time, and some let a program check the type of a variable. For example, C has the union keyword and the void * pointer, and Java has the typeof operator and wrapper types such as Number.

In Oracle Database, you can create variables and columns that can hold data of any type and test their values to determine their underlying representation. For example, a single table column can have a numeric value in one row, a string value in another row, and an object in another row.

You can use the Oracle-supplied ADT SYS.ANYDATA to represent values of any scalar type or ADT. SYS.ANYDATA has methods that accept scalar values of any type, and turn them back into scalars or objects. Similarly, you can use the Oracle-supplied ADT SYS.ANYDATASET to represent values of any collection type. For more information about these ADTs, see Oracle Database Object-Relational Developer's Guide.

To check and manipulate type information, use the DBMS_TYPES package, as in Example 2-4. For more information about this package, see Oracle Database PL/SQL Packages and Types Reference.

With OCI, use the OCIAnyData and OCIAnyDataSet interfaces, described in Oracle Call Interface Programmer's Guide.

Example 2-4 Accessing Information in a SYS.ANYDATA Column

CREATE OR REPLACE TYPE employee_type AS
  OBJECT (empno NUMBER, ename VARCHAR2(10));
/
 
DROP TABLE mytab;
CREATE TABLE mytab (id NUMBER, data SYS.ANYDATA);
 
INSERT INTO mytab (id, data)
VALUES (1, SYS.ANYDATA.ConvertNumber(5));
 
INSERT INTO mytab (id, data)
VALUES (2, SYS.ANYDATA.ConvertObject(Employee_type(5555, 'john')));
 
CREATE OR REPLACE PROCEDURE p IS
  CURSOR cur IS SELECT id, data FROM mytab;
  v_id                        mytab.id%TYPE;
  v_data                      mytab.data%TYPE;
  v_type                      SYS.ANYTYPE;
  v_typecode                  PLS_INTEGER;
  v_typename                  VARCHAR2(60);
  v_dummy                     PLS_INTEGER;
  v_n                         NUMBER;
  v_employee                  employee_type;
  non_null_anytype_for_NUMBER exception;
  unknown_typename            exception;
BEGIN
  OPEN cur;
  LOOP
    FETCH cur INTO v_id, v_data;
    EXIT WHEN cur%NOTFOUND;
 
    /* typecode signifies type represented by v_data.
       GetType also produces a value of type SYS.ANYTYPE with methods you
       can call to find precision and scale of a number, length of a
       string, and so on. */
 
       v_typecode := v_data.GetType (v_type /* OUT */);
 
    /* Compare typecode to DBMS_TYPES constants to determine type of data
       and decide how to display it. */
  
    CASE v_typecode
      WHEN DBMS_TYPES.TYPECODE_NUMBER THEN
        IF v_type IS NOT NULL THEN  -- This condition should never happen.
          RAISE non_null_anytype_for_NUMBER;
        END IF;
 
      -- For each type, there is a Get method.
      v_dummy := v_data.GetNUMBER (v_n /* OUT */);
      DBMS_OUTPUT.PUT_LINE
        (TO_CHAR(v_id) || ': NUMBER = ' || TO_CHAR(v_n) );
 
       WHEN DBMS_TYPES.TYPECODE_OBJECT THEN
         v_typename := v_data.GetTypeName();
         IF v_typename NOT IN ('HR.EMPLOYEE_TYPE') THEN
           RAISE unknown_typename;
         END IF;
         v_dummy := v_data.GetObject (v_employee /* OUT */);
         DBMS_OUTPUT.PUT_LINE
           (TO_CHAR(v_id) || ': user-defined type = ' || v_typename ||
            ' ( ' || v_employee.empno || ', ' || v_employee.ename || ' )' );
    END CASE;
  END LOOP;
  CLOSE cur;
EXCEPTION
  WHEN non_null_anytype_for_NUMBER THEN
    RAISE_Application_Error (-20000,
      'Paradox: the return AnyType instance FROM GetType ' ||
      'should be NULL for all but user-defined types');
  WHEN unknown_typename THEN
    RAISE_Application_Error( -20000, 'Unknown user-defined type ' ||
      v_typename || ' - program written to handle only HR.EMPLOYEE_TYPE');
END;
/
 
SELECT t.data.gettypename() AS "Type Name" FROM mytab t;
 

Result:

Type Name
--------------------------------------------------------------------------------
SYS.NUMBER
HR.EMPLOYEE_TYPE
 
2 rows selected.

Representing ANSI, DB2, and SQL/DS Data

SQL statements that create tables and clusters can use ANSI data types and data types from the IBM products SQL/DS and DB2 (except those noted after this paragraph). Oracle Database converts the ANSI or IBM data type to the equivalent Oracle data type, records the Oracle data type as the name of the column data type, and stores the column data in the Oracle data type. For conversion details, see Oracle Database SQL Language Reference.


Note:

SQL statements cannot use the SQL/DS and DB2 data types TIME, GRAPHIC, VARGRAPHIC, and LONG VARGRAPHIC, because they have no equivalent Oracle data types.

Representing Conditional Expressions as Data

Oracle Expression Filter (a feature of Rules Manager) enables you to store, index, and evaluate conditional expressions in one or more columns of a database table. Then Oracle Expression Filter compares the stored expressions to incoming data, identifying rows of interest.

Scenario: You created the following table, in which each row holds data for a stock-trading account holder, and you want to define a column that stores information about the stocks in which each trader is interested as a conditional expression.

DROP TABLE traders;
CREATE TABLE traders (
  name     VARCHAR2(10),
  email    VARCHAR2(20),
  interest VARCHAR2(30)
);

Solution:

  1. Create a type with attributes for the trading symbol, limit price, and amount of change in the stock price:

    CREATE OR REPLACE TYPE ticker AS OBJECT (
      symbol VARCHAR2(20),
      price  NUMBER,
      change NUMBER
    );
    /
    
  2. Create an attribute set based on the type ticker:

    BEGIN
      DBMS_EXPFIL.DROP_ATTRIBUTE_SET (attr_set  => 'ticker');
    END;
    /
    BEGIN
      DBMS_EXPFIL.CREATE_ATTRIBUTE_SET
      (attr_set  => 'ticker',
      from_type => 'YES');
    END;
    /
    
  3. Associate the attribute set with the expression set stored in the column trader.interest:

    BEGIN
      DBMS_EXPFIL.ASSIGN_ATTRIBUTE_SET
      (attr_set => 'ticker',
      expr_tab => 'traders',
      expr_col => 'interest');
      END;
    /
    

    The preceding code ensures that the interest column stores valid conditional expressions.

  4. Populate the table with trader names, e-mail addresses, and conditional expressions that represent stocks in which the trader is interested, at specific prices. For example:

    INSERT INTO traders (name, email, interest)
    VALUES ('Vishu', 'vishu@example.com', 'symbol = ''ABC'' AND price > 25');
    
  5. Use the EVALUATE operator to identify the conditional expressions that evaluate to TRUE for a given data item. For example, this query returns traders who are interested in the stock quote (symbol='ABC', price=31, change=5.2):

    SELECT name, email
    FROM traders
    WHERE EVALUATE (
      interest,
      'symbol=>''ABC'',
      price=>31,
      change=>5.2'
    ) = 1;
     
    

    Result:

    NAME       EMAIL
    ---------- --------------------
    Vishu      vishu@example.com
     
    1 row selected.
    

    Tip:

    To speed up the query, create an Oracle Expression Filter index on the interest column.


    See Also:

    Oracle Database Rules Manager and Expression Filter Developer's Guide for information about developing applications using Oracle Expression Filter

Identifying Rows by Address

The fastest way to access a row is by its address, or rowid, which uniquely identifies it. Different rows in the same data block can have the same rowid only if they are in different clustered tables. If a row is larger than one data block, then its rowid identifies its initial row piece.

To see rowids, query the ROWID pseudocolumn, whose value is a string that represents the address of the row. The string has the data type ROWID or UROWID.


Note:

When you update a row in a table compressed with Hybrid Columnar Compression (HCC), the ROWID of the row changes. HCC, a feature of certain Oracle storage systems, is described in Oracle Database Concepts.

Topics:


See Also:

Oracle Database SQL Language Reference for more information about the ROWID pseudocolumn

Querying the ROWID Pseudocolumn

Each table in Oracle Database has a pseudocolumn named ROWID, which can appear in a query in either the SELECT list or WHERE clause.

Example 2-5 creates a table of with a column of the data type ROWID, populates it with rowids by querying the ROWID pseudocolumn inside an INSERT statement, and then displays it. The rowids of the table rows show how they are stored.

Example 2-5 Querying the ROWID Pseudocolumn

DROP TABLE t_tab;  -- in case it exists
CREATE TABLE t_tab (col1 ROWID);
 
INSERT INTO t_tab (col1)
SELECT ROWID
FROM employees
WHERE employee_id > 199;
 

Query:

SELECT employee_id, rowid
FROM employees
WHERE employee_id > 199;
 

ROWID varies, but result is similar to:

EMPLOYEE_ID ROWID
----------- ------------------
        200 AAAPeSAAFAAAABTAAC
        201 AAAPeSAAFAAAABTAAD
        202 AAAPeSAAFAAAABTAAE
        203 AAAPeSAAFAAAABTAAF
        204 AAAPeSAAFAAAABTAAG
        205 AAAPeSAAFAAAABTAAH
        206 AAAPeSAAFAAAABTAAI
 
7 rows selected.
 

Query:

SELECT * FROM t_tab;
 

COL1 varies, but result is similar to:

COL1
------------------
AAAPeSAAFAAAABTAAC
AAAPeSAAFAAAABTAAD
AAAPeSAAFAAAABTAAE
AAAPeSAAFAAAABTAAF
AAAPeSAAFAAAABTAAG
AAAPeSAAFAAAABTAAH
AAAPeSAAFAAAABTAAI
 
7 rows selected.

ROWID Data Type

In heap-organized tables generated by Oracle Database, the values in the ROWID pseudocolumn have the data type ROWID. Internally, this data type is a structure that stores information that the database server needs to access a row. The format of this structure is either restricted, extended, or external binary.


Note:

Creating a column of the type ROWID (like col1 in Example 2-5) does not guarantee that its values will be valid rowids.

Topics:

Restricted Internal ROWID Format

A ROWID structure with the restricted internal format has these components:

  • Data file identifier

  • Block identifier

  • Row identifier

On most platforms, the size of this structure is 6 bytes.

The database server returns a ROWID pseudocolumn value to the client application as an 18-character string with a hexadecimal encoding of each component.

Extended Internal ROWID Format

A ROWID structure with the extended internal format has the same components as the restricted format and a data object number, which identifies a database segment. On most platforms, the size of this structure is 10 bytes.

The database server returns a ROWID pseudocolumn value to the client application as an 18-character string with a base-64 encoding of each component. For example, the string might be "AAAA8mAALAAAAQkAAA", which represents a base-64 encoding of the components of the extended ROWID in a four-piece format, OOOOOOFFFBBBBBBRRR.

To access and interpret the contents of an extended rowid, use the DBMS_ROWID package, described in Oracle Database PL/SQL Packages and Types Reference.

External Binary Internal ROWID Format

Some client applications use a binary internal format for the ROWID structure. For example, OCI and some precompiler applications can map the ROWID data type to a 3GL structure on bind or define calls.

In binary internal format, the ROWID structure is the same size for restricted and extended rowids. For a restricted rowid, the data object number is stored in an unused field.

The format of the extended binary ROWID, expressed as a C struct, is:

struct riddef {
    ub4    ridobjnum; /* data obj#--this field is 
                         unused in restricted ROWIDs */
    ub2    ridfilenum;
    ub1    filler;
    ub4    ridblocknum;
    ub2    ridslotnum;
}

UROWID Data Type

In tables that are foreign (that is, not generated by Oracle Database) or index-organized, the values in the ROWID pseudocolumn have the data type UROWID. This data type stores a universal rowid (urowid).

Urowids for foreign tables (such as DB2 tables accessed through a gateway) are called foreign rowids.

Urowids for index-organized tables (whose rows are stored in index leaves, which can move) are called logical rowids. Oracle Database creates logical rowids based on the primary key of the table. The logical rowids do not change if the primary key does not change.

To store urowids in a table, define a column of data type UROWID for the table and then retrieve the value of the ROWID pseudocolumn into that column.

How Oracle Database Converts Data Types

Generally, you cannot assign a value of one data type to a variable or column of another data type, or create an expression with values of different data types. However, in some cases, Oracle Database accepts data of one type where it expects data of another type and then automatically converts the accepted data to the expected type. This is called implicit data conversion.

Topics:


See Also:

Oracle Database SQL Language Reference for more information about data type conversion

Data Type Conversion During Assignments

For the assignment

variable := expression

if the data type of expression differs from that of variable, then Oracle Database tries to convert the data type of expression to that of variable. For information about when that is possible, see Oracle Database SQL Language Reference.

A character-to-NUMBER conversion succeeds only if the character string represents a valid number. A character-to-DATE conversion succeeds only if the character string satisfies the session default date format. (For information about the default date format, see "Displaying Current Date and Time".)

Examples

Assume that test_package, its public variable var1, and table1_tab are declared as follows:

CREATE OR REPLACE PACKAGE test_package AS
  var1 CHAR(5);
END;
/
 
DROP TABLE table1_tab;
CREATE TABLE table1_tab (col1 NUMBER);

In the assignment

variable := expression

the data type of expression must be either the same as, or implicitly convertible to, the data type of variable. For example, for this assignment, Oracle Database automatically converts zero to the data type of var1, which is CHAR(5):

var1 := 0;

In the statement

INSERT INTO table1_tab (col1) VALUES (expression)

the data type of expression must be either the same as, or implicitly convertible to, the data type of col1. For example, for this statement, Oracle Database automatically converts the string '19' to the data type of col1, which is NUMBER:

INSERT INTO table1_tab (col1) VALUES ('19')

In the statement

UPDATE table1_tab SET column = expression

the data type of expression must be either the same as, or implicitly convertible to, the data type of column. For example, for this statement, Oracle Database automatically converts the string '30' to the data type of col1, which is NUMBER:

UPDATE table1_tab SET col1 = '30';

In the statement

SELECT column INTO variable FROM table1_tab

the data type of column must be either the same as, or convertible to, the data type of variable. For example, for this statement, Oracle Database automatically converts the value selected from col1, which is 30, to the data type of var1, which is CHAR(5):

SELECT col1 INTO var1 FROM table1_tab WHERE col1 = 30;

Data Type Conversion During Expression Evaluation

When evaluating an expression, Oracle Database can perform the same automatic conversions that it does for assignments. The target data type is determined by the context of the expression. For example, if an expression is the operand of an arithmetic operator, then Oracle Database tries to convert the value of the expression to NUMBER; if the expression is the operand of a string function, then Oracle Database tries to convert the value of the expression to VARCHAR2.

For the assignment

variable := expression

Oracle Database first evaluates expression, using the conversion rules for expressions. If the evaluation succeeds, the result is a single value of a single data type, which Oracle Database tries to assign to variable, using the conversion rules for assignments.

Metadata for SQL Operators and Functions

The dynamic performance view V$SQLFN_METADATA contains metadata about SQL operators and functions. For every function in V$SQLFN_METADATA, the dynamic performance view V$SQLFN_ARG_METADATA has one row of metadata about each function argument. If a function argument can be repeated (as in the functions LEAST and GREATEST), then V$SQLFN_ARG_METADATA has only one row for each repeating argument. You can join these two views on the column FUNCID.

These views enable third-party tools to leverage SQL functions without maintaining their metadata in the application layer.

Topics:


See Also:


ARGn Data Type

In the view V$SQLFN_METADATA, the column DATATYPE is the data type of the function (that is, the data type that the function returns). This data type can be an Oracle data type, data type family (see "Data Type Families"), or ARGn. ARGn is the data type of the nth argument of the function. For example:

DISP_TYPE Data Type

In the view V$SQLFN_METADATA, DISP_TYPE is the data type of an argument that can be any expression. An expression is either a single value or a combination of values and SQL functions that has a single value.

Table 2-10 Display Types of SQL Functions

Display TypeDescriptionExample

NORMAL

FUNC(A,B,...)

LEAST(A,B,C)

ARITHMETIC

A FUNC B)

A+B

PARENTHESIS

FUNC()

SYS_GUID()

RELOP

A FUNC B)

A IN B

CASE_LIKE

CASE statement or DECODE decode


NOPAREN

FUNC

SYSDATE


Data Type Families

Often, a SQL function argument can have any data type in a data type family. Table 2-11 shows the data type families and their member data types.

Table 2-11 Data Type Families

FamilyData Types

STRING

CHARACTER


VARCHAR2


CLOB


NCHAR


NVARCHAR2


NCLOB


LONG

NUMERIC

NUMBER


BINARY_FLOAT


BINARY_DOUBLE

DATETYPE

DATE


TIMESTAMP


TIMESTAMP WITH TIME ZONE


TIMESTAMP WITH LOCAL TIME ZONE


INTERVAL YEAR TO MONTH


INTERVAL DAY TO SECOND

BINARY

BLOB


RAW


LONGRAW


PKTYGPK|%A OEBPS/lof.htm  List of Figures PKEk PK|%AOEBPS/dcommon/prodbig.gif GIF87a!!!)))111BBBZZZsss{{ZRRcZZ!!1!91)JB9B9)kkcJJB991ssc絽Zcc!!{祽BZc!9B!c{!)c{9{Z{{cZB1)sJk{{Z{kBsZJ91)Z{!{BcsRsBc{9ZZk甽kBkR!BZ9c)JJc{!))BZks{BcR{JsBk9k)Zck!!BZ1k!ZcRBZcZJkBk1Z9c!R!c9kZRZRBZ9{99!R1{99R{1!1)c1J)1B!BJRkk{ƽ絵ތkk絵RRs{{{{JJsssBBkkk!!9ss{{ZZssccJJZZRRccRRZZ))cBBJJ99JJ!!c11991199Z11!c!!))Z!!!1BRck{)!cJBkZRZ,HP)XRÇEZ֬4jJ0 @ "8pYҴESY3CƊ@*U:lY0_0#  5tX1E: C_xޘeKTV%ȣOΏ9??:a"\fSrğjAsKJ:nOzO=}E1-I)3(QEQEQEQEQEQEQE֝Hza<["2"pO#f8M[RL(,?g93QSZ uy"lx4h`O!LŏʨXZvq& c՚]+: ǵ@+J]tQ]~[[eϸ (]6A&>ܫ~+כzmZ^(<57KsHf妬Ϧmnẁ&F!:-`b\/(tF*Bֳ ~V{WxxfCnMvF=;5_,6%S>}cQQjsOO5=)Ot [W9 /{^tyNg#ЄGsֿ1-4ooTZ?K Gc+oyڙoNuh^iSo5{\ܹ3Yos}$.nQ-~n,-zr~-|K4R"8a{]^;I<ȤL5"EԤP7_j>OoK;*U.at*K[fym3ii^#wcC'IIkIp$󿉵|CtĈpW¹l{9>⪦׺*ͯj.LfGߍԁw] |WW18>w.ӯ! VӃ :#1~ +މ=;5c__b@W@ +^]ևՃ7 n&g2I8Lw7uҭ$"&"b eZ":8)D'%{}5{; w]iu;_dLʳ4R-,2H6>½HLKܹR ~foZKZ࿷1[oZ7׫Z7R¢?«'y?A}C_iG5s_~^ J5?œ tp]X/c'r%eܺA|4ծ-Ե+ْe1M38Ǯ `|Kյ OVڅu;"d56, X5kYR<̭CiطXԮ];Oy)OcWj֩}=܅s۸QZ*<~%뺃ȶp f~Bðzb\ݳzW*y{=[ C/Ak oXCkt_s}{'y?AmCjޓ{ WRV7r. g~Q"7&͹+c<=,dJ1V߁=T)TR՜*N4 ^Bڥ%B+=@fE5ka}ędܤFH^i1k\Sgdk> ֤aOM\_\T)8靠㡮3ģR: jj,pk/K!t,=ϯZ6(((((((49 xn_kLk&f9sK`zx{{y8H 8b4>ÇНE|7v(z/]k7IxM}8!ycZRQ pKVr(RPEr?^}'ðh{x+ՀLW154cK@Ng C)rr9+c:׹b Жf*s^ fKS7^} *{zq_@8# pF~ [VPe(nw0MW=3#kȵz晨cy PpG#W:%drMh]3HH<\]ԁ|_W HHҡb}P>k {ZErxMX@8C&qskLۙOnO^sCk7ql2XCw5VG.S~H8=(s1~cV5z %v|U2QF=NoW]ո?<`~׮}=ӬfԵ,=;"~Iy7K#g{ñJ?5$y` zz@-~m7mG宝Gٱ>G&K#]؃y1$$t>wqjstX.b̐{Wej)Dxfc:8)=$y|L`xV8ߙ~E)HkwW$J0uʟk>6Sgp~;4֌W+חc"=|ř9bc5> *rg {~cj1rnI#G|8v4wĿhFb><^ pJLm[Dl1;Vx5IZ:1*p)إ1ZbAK(1ׅ|S&5{^ KG^5r>;X׻K^? s fk^8O/"J)3K]N)iL?5!ƾq:G_=X- i,vi2N3 |03Qas ! 7}kZU781M,->e;@Qz T(GK(ah(((((((Y[×j2F}o־oYYq $+]%$ v^rϭ`nax,ZEuWSܽ,g%~"MrsrY~Ҿ"Fت;8{ѰxYEfP^;WPwqbB:c?zp<7;SBfZ)dϛ; 7s^>}⍱x?Bix^#hf,*P9S{w[]GF?1Z_nG~]kk)9Sc5Ո<<6J-ϛ}xUi>ux#ţc'{ᛲq?Oo?x&mѱ'#^t)ϲbb0 F«kIVmVsv@}kҡ!ˍUTtxO̧]ORb|2yԵk܊{sPIc_?ħ:Ig)=Z~' "\M2VSSMyLsl⺿U~"C7\hz_ Rs$~? TAi<lO*>U}+'f>7_K N s8g1^CeКÿE ;{+Y\ O5|Y{/o+ LVcO;7Zx-Ek&dpzbӱ+TaB0gNy׭ 3^c T\$⫫?F33?t._Q~Nln:U/Ceb1-im WʸQM+VpafR3d׫é|Aү-q*I P7:y&]hX^Fbtpܩ?|Wu󭏤ʫxJ3ߴm"(uqA}j.+?S wV ~ [B&<^U?rϜ_OH\'.;|.%pw/ZZG'1j(#0UT` Wzw}>_*9m>󑓀F?EL3"zpubzΕ$+0܉&3zڶ+jyr1QE ( ( ( ( ( ( ( (UIdC0EZm+]Y6^![ ԯsmܶ捆?+me+ZE29)B[;я*wGxsK7;5w)}gH~.Ɣx?X\ߚ}A@tQ(:ͧ|Iq(CT?v[sKG+*רqҍck <#Ljα5݈`8cXP6T5i.K!xX*p&ќZǓϘ7 *oƽ:wlຈ:Q5yIEA/2*2jAҐe}k%K$N9R2?7ýKMV!{W9\PA+c4w` Wx=Ze\X{}yXI Ү!aOÎ{]Qx)#D@9E:*NJ}b|Z>_k7:d$z >&Vv󃏽WlR:RqJfGإd9Tm(ҝEtO}1O[xxEYt8,3v bFF )ǙrPNE8=O#V*Cc𹾾&l&cmCh<.P{ʦ&ۣY+Gxs~k5$> ӥPquŽўZt~Tl>Q.g> %k#ú:Kn'&{[yWQGqF}AЅ׮/}<;VYZa$wQg!$;_ $NKS}“_{MY|w7G!"\JtRy+贾d|o/;5jz_6fHwk<ѰJ#]kAȎ J =YNu%dxRwwbEQEQEQEQEQEQEQEQEQE'fLQZ(1F)hQ@X1KEQE-Q@ 1KE3h=iPb(((1GjZ(-ʹRPbR@ 1KE7`bڒyS0(-&)P+ ڎԴP11F)h&:LRmQ@Q@Š(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((?l:ϊw "{{-3j3%{sj~2= 7 ~MڅKrHb|P3 r=Ҁ +Ş/$iu7=q2dԂxn⸷9$l]H #WI񯄴;\[ݚD8C3p&0U9^AnK vI+!I8>5(zqj03Y.X ,@85ߛ8>pq8=} \xmm常8` $Q@$v7zwp]ɝA GX;y_]覮O&4 SPtY.X),@84U=7Vuv K4,$g{@<+uqtiGw3; I@ORմn5MBp%8'ƫ%u6uBJrHRN2@ϸ J(9i[[m$|@񗌯|-(0Y8~[_w׮F?kiZҴeեՕVQȠ78w*z66h)[ELrɜ/$wO hgA}>3;vmwguy|Y`Z *VZ8fWh[$a9S#U(vz\][j3>˘]lNTCU/:5ޛc; u"b# 1g^S Y3h$#`pÃ*=KVtku5 K,S,J[ g{Ee^%tk56v@DrF@b2ϱ'+yc Pƀ5(7zwp]ɝA GV(vz\][j3>˘]lNTCT|YN;;iWwRgd0^#' N'7A%wG`((((((((((((((/CcaSm+3O \\K0E$QDOk?; z a3|$Xwx"}aؑ޶<5g6H{ ;fst+oi sg%Qm`YYSi3E5QHmeFd+oGE,y'~>b'A" IY޻!%d|>EmۼGf3=`4ѰU.pJ!sƧƭf4}+ZkW%bf%Q#ܡe9.qk>ԾxÞ2ԼA&׿gD2xM@uŤ? 4oFx;Ox4b'`㲻)&'Ψ`<Aܓ'o<1^o~5?4{|Bo cP=X6̫:c9Sw##>gİ׈{y-Gڍq$.q3f6$:vS==eJapw5W!?l=$ٳ~3w8 >_ U=ƓJzVUH!Jn_pF|yV?-q&|'_{{ V.] ʻN0[iEK@7 ǧ$!VķS^J@ߴ\9g>Ko#3gr;s\1ͱO{ex|Y`Z㙵"uos=ejPprO̿uE-J8;#(F8 퐶0Iیd{#kKM."o91 8'+،'tEK04bKI5犼Q?c*>G$^nƚ=z'k;٧Ha y<9)oSC%֟6qeUdĬq?wF K C{[($zGp;!m$rN6G? >;n>}1\M"qry5eɫF V | " `j~!ޭ?nݻf83@EPEPEPEPEPEPEPEPEPEPEPEPEPEP?w?e]O羅nn3ҹR_?^ۏ9^ESt7Fk}/OGk ĥHPpϰQ@>\K;v09v̊ 9UQnE [ͶM @Z4y F'&G=? (?YaA{j;&L$OUl#<ÿ GQCҾu$FK&PHñT~UQ@߉|Aq1OA#I Fsu5Ngm4{. V r):aEgbi`}%dٷvw}3?q7z=>d@| 3o+-+9 ^Ai$ 6ZI8r[o#8jjUiw6w X8 AW(t"gydAS׸Z4 h_~E֩/xkc1 rkbxSirh"c=>7>G+|bgk#n巸9 I]H#W?7<]w3"NxUp{]OMtݭ>r+!@>®QEc-J~f"RY]0IT@㝊( ÚGtu ĩV TpHM\ i`?>\1L`/wg=V(~wmdg8l۟qq[ +klvjFPP&v(4ҷ( ;u/=+RDagDe ;G]EW&>\jW owrtX7s 㓒y&J(">V_2xI|}$׍4CUi:ݵ\F626r7{?1W_.HlVo)>PzPKsG7_$| wVŨjvs*([D_MP1<rO ռW4x䍃+sv6?15 y W;rP ?7GdOs}Ͻuր=Šg=ONNMV24I/Tm$m{Z5KkK>)c)wb@06z󦽤] ֵXu|>uʧ0C"*bv8 +g'dQWaxR_E{?|e0*v;W8o/[ָ{u$Saq +tͬk#r$b s$ 3Cִo%92c0b YG9zv{>EwIF>$;p6edՏďA,~"om̺dγE$Dё v;x;KH5or#E0Pz +ό fᛆ?/V\" cx;Xs^௄M.RymݖDe Y[#j)Pw w~CVhc1D7oG簯?kW>,ZT|[ ֵg̳"g; ;"'h(|Msoon667W>xZTּ⫙'ִS:g;#;X;%h(<7]_y7zfu9_RkROUb-Oof?nݻf 8q Р,|I!]+M!AAP *_fB?pߤvt$A:*?ֽ<-fw^ !dGH 1L'++BjvHo./E D%bY AȮ⇌/iq fZXBp 6 `N@";+g=B#yj.4P6Y 1F>Q7ׁ$IZ$4Jd;U*a@(cwW @݉#EbyN(̛oj4am٢ZSƤ/8.Tvu +57FUu n,? F3Wߍ'uYI):Uw7,0®Huw҈QMG,B$K.T:B7 ;w?eVBH܍4EN# xI熿k '}o|,$mtKy}EKN=+7_b^+յ+]vn@%60P7,0$m+I_Ykյ+p[!6(`7 /?5e֡о7#A)S#{E?%5zV],I,#V 2|9ּ˟"d{qqXl@F%T?) )d:GrW>z oC6}.*d\NIO WWĿivޙNWԚԀUX ᕦʞ(i<־ żCa,I`kMaoĺ*m]!uL((((((((((1[j^<]H۽˴b|j*=<==#y+McOwqj(#wlQaUTc-y9(Yxh|Ou+=2@.(nBǜ1S㮥⯇Y^q"b1$ zx+Meƕww=ݵqKWP:Cؐ|4xᵽQ$ȡn Ab=R\=n 4Lj.@;qq3/2ƚ`n1K,lW\"$A g߳ς$u[( (p]=}8D<9x[K]7E9}KcԳ1%AOP~5yoyir7h`\ gFp2Gx_t9}^*>WDAݎI G4Ѿ.ubTVR  V4k=„|9(uPz5eO/B!BaTE$:,1UjEE^oop8*^ |Ms ptQ @|Y`Z2'>4~P߹' ͅ?1`-z%j>0{_ۋycg_((ۜzciwfm͝l'#@#P7zwp]ɝA G\_}FhO!7VQE]͂ =\fT`Grw@O'$Rk4i7AtGOHLU,Iy_u_F/Ħ4/"e cËhlm)dyY3ndfbJC{9g_q,w(#*7FNNI>I|'7^`m>'8"ppYؐ: P'xm|uX-MI$`%$xs^X~+=z2I26BYCpH x PkWmzڦwMW0y~^7on9LW3|aw,٤4B7²G9Z> O>oO/9ٝ㏽zF~ӒHpvB@s,rN94?Nѷ|mCۿG[V}g],}j+3TmL( fCԞxv~.އ$l06 ;P<RAT6FF;xB E 8P{Nş AX,伿AEn:GON( 5LLG߆nQ̗*ę"n Aktavje#̹G#,@9zƍ~ӒHpvB@s,rN95rx!(T)$r(eu#x 1@'| w9/귶N@;XyYVS3ȭ 1?~ [3\lcg%O &@Ld# /g]I<2Q1 Q03sגzq]k[Ѭ<"XFI' $85zW1-QȨ00u1_Ks3K7}y֥# l`sIpz ռG4I#C+ q~ ?&4Iƍ{\ !z3k7ۼzrAoۦ6G((8珇3a꺭X$jXTAS3p1Oo^} vryA0 9Fp1@>7߇CZFaQeFIOl:7.ݤh,-i,U&A8gQ|Fqmwtx 208O]˽I㵺ٽ`muqATv ='MY'c۴k=#$(g '>! ]_GTRD c  pNXv#Aeom!A\xCŷK}\2$ln8x< <-njbHb$H~r2IvXtٴo~6FE$dA]Dž>xCWK}߲rmln<h'cRxc_8u}"6N[TqنG$O_[)>Ѧ:{CuunP#are8G# v5~ o- $B޸@Wa?^ ӚEݦy3(,8PI4/ _7wv:>oZ^[{dgRNqoBH㧃xMռW4xWR2"<G9P9xKm7Zи}JaЫ)OQGכ:6{ftG IqWӶ0pzpA"7>S~Wš3K7*+Yģ8'ӂ?63@m-t;K@),YRĖ=I<:YV}g],}j+3TmL( fCԞ:J(89=kLA곪+-9@e|݌&~F-|RmzKkM>D+8MIYr.]AP(zݽwmަR8fdỲa8a#QEQEQEQEV?<-xN\]r<׏=uEQEW&ǐlc{.S0h2m0A$F0HҾ:.6buo$qn\H_N3PQ\_?C Ɗ;}BWuFd((((((((((((( A>:/zQ vdo>삇B1ox^?Hd wk ! @E%ʼ9Uscgyawݬ4 H8a~yCI\.? 5}cK$7ҭ.6MqxHkcmO /5?5wRyC#' ? jZjv]e.cfE|b0}Oh|i 󤪬 Fއk)A\y>)7Y3uHOʏ$?ṬQĝn#k鶓kd\ g q Ox#kVs+R#v* Uuoſfh%@ԌhxJ׀guVqe}2"U2&KWsMLoO_[E/Gx8fU2XtzQEC²訫+g'dQPWmCۿG^^InQtyZ襬^1i_uVOPHg-?3 [yZ襯??4W*~^^'( ~IW}kn .$,>͹ !Q¿RXx;}0lD#B<[+H׸A<7V\[J6 dGk_ۘLؓGʱO`8=FG4נWmCۿG@M3Z̟duC cgPzV=WҹI>PF ~ ׁ|=qqi-I$ĤJyqz2?o)y¾!Gfts;y`&bq@OY+ZyVicPON2B88y'?ut΁4,k l1qZ5X=jM9*_qҾ\=3sgpa9A@<5xtxUVD++T lo J oQwRĺ|CԞ%&bHȅ8%c$8%O@,{j<+ 41¨''!Cbf::g@XYScYK6OsA5bx8e_ |CivYMJl0  <_O:Wǫiz?u ٕr]Y 67dž{Oi1>_QKпa#OB:!1{pԡ.k_'Im)2!i X88>(\$<-cv"Mn[?,||͓t}xkHg>:ywrNkO|"񯆴{I㥊Cq VTf H +Ǿ3Ķ Dq2,W, qwC3a-lỵ}.L`, m9͌3;˯y1&i Y]7?$iF FJ%kA+:7O_Y8u6^G(Ilc+~YanW9;rNzG/,[??`n<vr9j(<~oo7xKzkq%[$ nWBT y9-X:&w&ezQzᑰ>anW9;rNzOUҬu.LM0Ar`xZS>!k:V[(bwm)"N:`sײQ@_OTd-[ZH./oHU~b\~>74hVtM5[[c A-Oz$䳿ɍ #`2|^Ey>Ě&K 'UX;$X4#x @یxNuo x;m.dUwB#;Fx6gp9nV~i#ukXϷ̋{&r@N~ ^is[e$ƋnI.f-)I+ڲ D|Ie: vq|Y~ 3%zf#Z-vRX,z xt5+?1ϱm"vz( Lwm"v߱N3&{x|o·iYgP9UOlWYEW74+יpH,@ܮ qs^Ey[z姈5m{Uuh(/&;6%`3XKgOjp`C|!3G#y`{(%s}qνU=Ii?7 ;< O붹|+D]s޻ (}IJCχw-*⦳  H%UE'qJ(/ }[ŷ|/aqvqGۇL}ֻ ((((((((((((( sY{߾[HW:*X3 W/P.v|KsMFVaX3m sev'K-%+oSDV,V"Ę䍱s CkoQH8Ppb8^oɩqn&ۑU2/,8= ?:xH8JZ+ 92`pcֽ"?6=Ŀ!qw^,g đcڌH]G}rV3$906@`6Xi wB,aK!# =A/د#K[TEb1kl` g6mºFpѴVPђT RF@82n\k>Tѿ{b/BO3*z%x?p~XۇLAeX9$W>@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@?>wiT|N>Q'<}}׺lK/.q d8ڏ>.Z/k1-@Wڀ; P'Tۊs ?J _9>*`ų~Xy$k5[jvzu林+yd![p`r9\uC7͸+khqz(vr@6<k',t~>gs<#2)Qϥy>|MiO"]-IeّBLnO#h/uMg\=u0{*J3Ý͗?/W==#תi1CXŪM[ƷR0(Q=PPů:MoXC۲pp~^x?|P{'ͦito?%{QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQENIt->{s$04͒r }jQEQEQEQETs ռG4I#C+ qcagYgaiy rI8Q$5b((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((PKόxxPK|%AOEBPS/dcommon/contbig.gif`GIF87a!!!111999BBBJJJRRRccckkksss{{{skk{{ZRRRJJƽ{sZRJRJB91)kcZB9)sskZRJ1޽ƽ{{ssskkkcƵZZRccZRRJJJB{BB9991ssckkZccR))!RRB!!JJ1))99!11ƌ)1R)k֔)s1RZJR{BJs9R1J!11J1J9k{csZk!1J!)cBR9J1B)91B!cRs{!)s!){1B!k!s!{ksksckckZc9B)1!)!)BJ9B1919έƌ!!)JJcZZ{!!!1RR{JJsBBkJJ{!!9BB{1!!J9)!!Z!!c1!!kR!!s9Z!BckJs)19!!c!!ZRZ,H rrxB(Kh" DժuICiи@S z$G3TTʖ&7!f b`D 0!A  k,>SO[!\ *_t  Exr%*_}!#U #4 & ֩3|b]L ]t b+Da&R_2lEٱZ`aC)/яmvUkS r(-iPE Vv_{z GLt\2s!F A#葡JY r|AA,hB}q|B`du }00(䡆<pb,G+oB C0p/x$…– ]7 @2HFc ) @AD \0 LHG',(A` `@SC)_" PH`}Y+_|1.K8pAKMA @?3҄$[JPA)+NH I ,@8G0/@R T,`pF8Ѓ)$^$ DDTDlA@ s;PKPK|%AOEBPS/dcommon/darbbook.cssPKPK|%A!OEBPS/dcommon/O_signature_clr.JPG"(JFIF``C    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (?O '~MQ$Vz;OlJi8L%\]UFjޙ%ԯS;rA]5ފ<׈]j7Ouyq$z'TQuw7Ŀ KX߁M2=S'TQt?.5w'97;~pq=" ~k?`'9q6 E|yayM^Om'fkC&<5x' ?A?Zx'jß={=SßM gVC.5+Hd֪xc^)Җufz{Cީ|D Vkznq|+Xa+{50rx{|OG.OϞ~f/ xxX[2H )c+#jpUOZYX\=SG ߨC|K@;_߆'e?LT?]:?>w ڔ`D^So~xo[Ӡ3i7B:Q8 Vc-ďoi:FM292~y_*_闱YN\Fr=xZ3鳎OwW_QEzW~c]REeaSM}}Hӏ4&.E]u=gMѠ+mF`rNn$w9gMa꺢nTuhf2Xv>އ a(Û6߭?<=>z'TQuw7Ŀ KX߁M2=S'TQt?.5Kko\.8S$TOX߀Gw?Zx汴X)C7~.i6(Щ=+4{mGӭ¸-]&'t_kV*I<1)4thtIsqpQJ+> \m^[aJ5)ny:4o&QEnyAEPEEss 72,PDۢ׃K W{Wjr+wگ iM/;pd?~&?@;7E4gv8 $l'z'TQuw7Ŀ Gֱ=ɿ&G?. iR(5W*$|?w᫼gkmIbHe/_t>tg%y.l}N5[]+Mk0ĠeHdPrsst'UiC,y8`V%9ZIia|ܪvi מYG,o}+kk{YbyIeb*sAtի82zWoEK5z*o-eo;n(P u-I)4Š(HQEQEQEQEhz(X/Đ?}Bk˩ ݏrk0]4>8XzV? }6$}d^F>nU K ?Bտk_9׾x~w'ߞ  uDŽtL ؈5c-E/"|_Oo.IH쐍=i*Iw5(ںw?t5s.)+tQ2dUt5Vĺ.jZ"@IRrZƅY4ߡ_;}ų(KyQf1Aǵt?sZg+?F5_oQR&Dg߿]6FuRD u>ڿxl7?IT8'shj^=.=J1rj1Wl$얲cPx;E,p$֟ˏkw qg"45(ǛkV/=+ũ)bYl~K#˝J_כ5&\F'I#8/|wʾ_Xj Q:os^T1.M_|TO.;?_  jF?g N 8nA2F%i =qW,G=5OU u8]Rq?wr'˻S+۾.ܼ 87Q^elo/T*?L|ۚ<%<,/v_OKs B5f/29n0=zqQq(ª=VX@*J(э(f5qJN_EVǞQEOuoѕOuoa5}gO?:߂8Wא|cڽ~]N&O( (<]>͠@VQ=^~U ̴m&\խ5i:}|}r~9՝f}_>'vVֲ$~^f30^in{\_.O F8to}?${φ|#x^#^n~w=~k~?'KRtO.㌡h![3Zu*ٷճ(ԟ]z_/W1(ԟ]v~g|Yq<ז0 ; b8֮s,w9\?uEyStKaª@\,)) (!EPEPEPEPEPzѧts{v>C/"N6`d*J2gGӧWqBq_1ZuΓ\X]r?=Ey88Mp&pKtO-"wR2 K^-Z< \c>V0^@O7x2WFjs<׻kZ(<Т(OFw/6$1[:ޯԯ#q~4|,LVPem=@=YLUxӃV}AUbcUB.Ds5*kٸAeG>PJxt͝ b88?*$~@ׯD VkraiJs}Q.20x&mXξ,Z]“A-J#`+-E/"<]\a'tZGy.(|lދ~gMK OZdxDŽU9T6ϯ^<Ϡt5CZ]].t۫S=s`ڳ%8iVK:nqe+#<.T6U>zWoy3^I {F?J~=G}k)K$$;$de8*G Uӟ4Ocºw}|]4=ݣ\x$ʠms?q^ipw\"ȿPs^Z Q_0GڼU.t}ROM[G#]8wٞ ӫ87}Cgw vHȩBM55vof =A_٭`Ygx[6 P,5}>蚊(0(+?>+?> k|TuXq6_ +szk :u_ Z߶Ak_U}Jc2u/1[_»ݸG41-bሬ۴}}Eȹפ_c?5gi @cL\L<68hF_Ih>X4K7UТ sMj =J7CKo>Օ5s:߀t ~ηaٿ?|gdL8+gG%o?x`دOqȱwc¨&TW_V_aI=dpG!wu۞սZ1yL50$(l3(:~'ַo A}a3N*[0ǭ HKQV}G@֜$ 9of$ArNqUOgË05#m?D)^_h//5_/<?4}Jį+GkpG4"$ r| >S4Ђ"S 1%R:ȝ 8;PKPz PK|%AOEBPS/dcommon/feedback.gif7GIF89a'%(hp|fdx?AN5:dfeDGHɾTdQc`g*6DC\?ؘ||{;=E6JUՄfeA= >@,4`H.|`a (Q 9:&[|ځ,4p Y&BDb,!2@, $wPA'ܠǃ@CO~/d.`I @8ArHx9H75j L 3B/` P#qD*s 3A:3,H70P,R@ p!(F oԥ D;"0 ,6QBRɄHhI@@VDLCk8@NBBL2&pClA?DAk%$`I2 #Q+l7 "=&dL&PRSLIP)PɼirqМ'N8[_}w;PK-PK|%AOEBPS/dcommon/booklist.gifGIF89a1޵֥΄kZ{Jk1Rs!BZ)B),@I9Z͓Ca % Dz8Ȁ0FZЌ0P !x8!eL8aWȠFD(~@p+rMS|ӛR$ v "Z:]ZJJEc{*=AP  BiA ']j4$*   & 9q sMiO?jQ = , YFg4.778c&$c%9;PKː5PK|%AOEBPS/dcommon/cpyr.htm1 Oracle Legal Notices

Oracle Legal Notices

Copyright Notice

Copyright © 1994-2012, Oracle and/or its affiliates. All rights reserved.

Trademark Notice

Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners.

Intel and Intel Xeon are trademarks or registered trademarks of Intel Corporation. All SPARC trademarks are used under license and are trademarks or registered trademarks of SPARC International, Inc. AMD, Opteron, the AMD logo, and the AMD Opteron logo are trademarks or registered trademarks of Advanced Micro Devices. UNIX is a registered trademark of The Open Group.

License Restrictions Warranty/Consequential Damages Disclaimer

This software and related documentation are provided under a license agreement containing restrictions on use and disclosure and are protected by intellectual property laws. Except as expressly permitted in your license agreement or allowed by law, you may not use, copy, reproduce, translate, broadcast, modify, license, transmit, distribute, exhibit, perform, publish, or display any part, in any form, or by any means. Reverse engineering, disassembly, or decompilation of this software, unless required by law for interoperability, is prohibited.

Warranty Disclaimer

The information contained herein is subject to change without notice and is not warranted to be error-free. If you find any errors, please report them to us in writing.

Restricted Rights Notice

If this is software or related documentation that is delivered to the U.S. Government or anyone licensing it on behalf of the U.S. Government, the following notice is applicable:

U.S. GOVERNMENT RIGHTS Programs, software, databases, and related documentation and technical data delivered to U.S. Government customers are "commercial computer software" or "commercial technical data" pursuant to the applicable Federal Acquisition Regulation and agency-specific supplemental regulations. As such, the use, duplication, disclosure, modification, and adaptation shall be subject to the restrictions and license terms set forth in the applicable Government contract, and, to the extent applicable by the terms of the Government contract, the additional rights set forth in FAR 52.227-19, Commercial Computer Software License (December 2007). Oracle America, Inc., 500 Oracle Parkway, Redwood City, CA 94065.

Hazardous Applications Notice

This software or hardware is developed for general use in a variety of information management applications. It is not developed or intended for use in any inherently dangerous applications, including applications that may create a risk of personal injury. If you use this software or hardware in dangerous applications, then you shall be responsible to take all appropriate fail-safe, backup, redundancy, and other measures to ensure its safe use. Oracle Corporation and its affiliates disclaim any liability for any damages caused by use of this software or hardware in dangerous applications.

Third-Party Content, Products, and Services Disclaimer

This software or hardware and documentation may provide access to or information on content, products, and services from third parties. Oracle Corporation and its affiliates are not responsible for and expressly disclaim all warranties of any kind with respect to third-party content, products, and services. Oracle Corporation and its affiliates will not be responsible for any loss, costs, or damages incurred due to your access to or use of third-party content, products, or services.

Alpha and Beta Draft Documentation Notice

If this document is in prerelease status:

This documentation is in prerelease status and is intended for demonstration and preliminary use only. It may not be specific to the hardware on which you are using the software. Oracle Corporation and its affiliates are not responsible for and expressly disclaim all warranties of any kind with respect to this documentation and will not be responsible for any loss, costs, or damages incurred due to the use of this documentation.

Oracle Logo

PKN61PK|%AOEBPS/dcommon/masterix.gif.GIF89a1ޜΌscJk1Rs!Bc1J),@IS@0"1 Ѿb$b08PbL,acr B@(fDn Jx11+\%1 p { display: none; } /* Class Selectors */ .ProductTitle { font-family: sans-serif; } .BookTitle { font-family: sans-serif; } .VersionNumber { font-family: sans-serif; } .PrintDate { font-family: sans-serif; font-size: small; } .PartNumber { font-family: sans-serif; font-size: small; } PKeӺ1,PK|%AOEBPS/dcommon/larrow.gif#GIF87a絵ƌֵƽ{{ss֜ƔZZ{{{{ZZssZZccJJJJRRBBJJJJ991111))!!{,@pH,Ȥrl:ШtpHc`  өb[.64ꑈ53=Z]'yuLG*)g^!8C?-6(29K"Ĩ0Яl;U+K9^u2,@@ (\Ȱ Ë $P`lj 8x I$4H *(@͉0dа8tA  DсSP v"TUH PhP"Y1bxDǕ̧_=$I /& .)+ 60D)bB~=0#'& *D+l1MG CL1&+D`.1qVG ( "D2QL,p.;u. |r$p+5qBNl<TzB"\9e0u )@D,¹ 2@C~KU 'L6a9 /;<`P!D#Tal6XTYhn[p]݅ 7}B a&AƮe{EɲƮiEp#G}D#xTIzGFǂEc^q}) Y# (tۮNeGL*@/%UB:&k0{ &SdDnBQ^("@q #` @1B4i@ aNȅ@[\B >e007V[N(vpyFe Gb/&|aHZj@""~ӎ)t ? $ EQ.սJ$C,l]A `8A o B C?8cyA @Nz|`:`~7-G|yQ AqA6OzPbZ`>~#8=./edGA2nrBYR@ W h'j4p'!k 00 MT RNF6̙ m` (7%ꑀ;PKl-OJPK|%AOEBPS/dcommon/index.gifGIF89a1޵ΥΥ{sc{BZs,@IM" AD B0 3.R~[D"0, ]ШpRNC  /& H&[%7TM/`vS+-+ q D go@" 4o'Uxcxcc&k/ qp zUm(UHDDJBGMԃ;PK(PK|%AOEBPS/dcommon/bookbig.gif +GIF89a$!!!)))111999BBBJJJRRRZZZccckkksss{{{skkB991)))!!B11))1!JB9B9!!cZ9ƭƽssk{ZZRccZRRJJJBBB9c!!ν)1)k{s絽ƌkssֽZccJRRBJJ{9BB)11)99!!))11!!k!JZ!)RcJccBcs)1c)JZ!BR!)BZ)99J!Rk9!c11B)Z{)9Bkc1kB9BZ!Z{9Rs)Jkksk9kB1s1Jk9Rƥc{k9s)Z{1k91)s1Rk)Jc1J!))BZ!1k{csc{)19B!)Bcsc{ksc{kZs!RkJkJkքc{9Zks{ck9R)Bks9R9R1J!)Z1B!)c)9)99BR19kksBBJcc{ccBBZ))9kk!!199c11ZBB{9!!R!!Z!!c))!!kR!!s!!BcksRZ1c9B)R91c1)Z!R9B9k1)RcZ{)!1B9JB9B)!)J9B!& Imported from GIF image: bookbig.gif,$!!!)))111999BBBJJJRRRZZZccckkksss{{{skkB991)))!!B11))1!JB9B9!!cZ9ƭƽssk{ZZRccZRRJJJBBB9c!!ν)1)k{s絽ƌkssֽZccJRRBJJ{9BB)11)99!!))11!!k!JZ!)RcJccBcs)1c)JZ!BR!)BZ)99J!Rk9!c11B)Z{)9Bkc1kB9BZ!Z{9Rs)Jkksk9kB1s1Jk9Rƥc{k9s)Z{1k91)s1Rk)Jc1J!))BZ!1k{csc{)19B!)Bcsc{ksc{kZs!RkJkJkքc{9Zks{ck9R)Bks9R9R1J!)Z1B!)c)9)99BR19kksBBJcc{ccBBZ))9kk!!199c11ZBB{9!!R!!Z!!c))!!kR!!s!!BcksRZ1c9B)R91c1)Z!R9B9k1)RcZ{)!1B9JB9B)!)J9BH`\Ȑ:pظа"A6DBH,V@Dڹ'G"v Æ ܥ;n;!;>xAܽ[G.\rQC wr}BŊQ A9ᾑ#5Y0VȒj0l-GqF>ZpM rb ;=.ސW-WѻWo ha!}~ْ ; t 53 :\ 4PcD,0 4*_l0K3-`l.j!c Aa|2L4/1C`@@md;(H*80L0L(h*҇҆o#N84pC (xO@ A)J6rVlF r  fry†$r_pl5xhA+@A=F rGU a 1х4s&H Bdzt x#H%Rr (Ѐ7P`#Rщ'x" #0`@~i `HA'Tk?3!$`-A@1l"P LhʖRG&8A`0DcBH sq@AXB4@&yQhPAppxCQ(rBW00@DP1E?@lP1%T` 0 WB~nQ@;PKGC PK|%AOEBPS/dcommon/rarrow.gif/GIF87a絵ƌֵƽ{{ss֜ƔZZ{{{{ZZssZZccJJJJRRBBJJJJ991111))!!{,@pH,Ȥrl:ШLlԸ NCqWEd)#34vwwpN|0yhX!'+-[F 'n5 H $/14w3% C .90" qF 7&E "D mnB|,c96) I @0BW{ᢦdN p!5"D`0 T 0-]ʜ$;PKJV^PK|%AOEBPS/dcommon/mix.gifkGIF89aZZZBBBJJJkkk999sss!!!111cccֽ{{{RRR)))猌ƭ{s{sks!,@@pH,B$ 8 t:<8 *'ntPP DQ@rIBJLNPTVEMOQUWfj^!  hhG H  kCúk_a Ǥ^ h`B BeH mm  #F` I lpǎ,p B J\Y!T\(dǏ!Gdˆ R53ټ R;iʲ)G=@-xn.4Y BuU(*BL0PX v`[D! | >!/;xP` (Jj"M6 ;PK枰pkPK|%AOEBPS/dcommon/doccd_epub.jsM /* Copyright 2006, 2012, Oracle and/or its affiliates. All rights reserved. Author: Robert Crews Version: 2012.3.17 */ function addLoadEvent(func) { var oldOnload = window.onload; if (typeof(window.onload) != "function") window.onload = func; else window.onload = function() { oldOnload(); func(); } } function compactLists() { var lists = []; var ul = document.getElementsByTagName("ul"); for (var i = 0; i < ul.length; i++) lists.push(ul[i]); var ol = document.getElementsByTagName("ol"); for (var i = 0; i < ol.length; i++) lists.push(ol[i]); for (var i = 0; i < lists.length; i++) { var collapsible = true, c = []; var li = lists[i].getElementsByTagName("li"); for (var j = 0; j < li.length; j++) { var p = li[j].getElementsByTagName("p"); if (p.length > 1) collapsible = false; for (var k = 0; k < p.length; k++) { if ( getTextContent(p[k]).split(" ").length > 12 ) collapsible = false; c.push(p[k]); } } if (collapsible) { for (var j = 0; j < c.length; j++) { c[j].style.margin = "0"; } } } function getTextContent(e) { if (e.textContent) return e.textContent; if (e.innerText) return e.innerText; } } addLoadEvent(compactLists); function processIndex() { try { if (!/\/index.htm(?:|#.*)$/.test(window.location.href)) return false; } catch(e) {} var shortcut = []; lastPrefix = ""; var dd = document.getElementsByTagName("dd"); for (var i = 0; i < dd.length; i++) { if (dd[i].className != 'l1ix') continue; var prefix = getTextContent(dd[i]).substring(0, 2).toUpperCase(); if (!prefix.match(/^([A-Z0-9]{2})/)) continue; if (prefix == lastPrefix) continue; dd[i].id = prefix; var s = document.createElement("a"); s.href = "#" + prefix; s.appendChild(document.createTextNode(prefix)); shortcut.push(s); lastPrefix = prefix; } var h2 = document.getElementsByTagName("h2"); for (var i = 0; i < h2.length; i++) { var nav = document.createElement("div"); nav.style.position = "relative"; nav.style.top = "-1.5ex"; nav.style.left = "1.5em"; nav.style.width = "90%"; while (shortcut[0] && shortcut[0].toString().charAt(shortcut[0].toString().length - 2) == getTextContent(h2[i])) { nav.appendChild(shortcut.shift()); nav.appendChild(document.createTextNode("\u00A0 ")); } h2[i].parentNode.insertBefore(nav, h2[i].nextSibling); } function getTextContent(e) { if (e.textContent) return e.textContent; if (e.innerText) return e.innerText; } } addLoadEvent(processIndex); PKo"nR M PK|%AOEBPS/dcommon/toc.gifGIF89a1ΥΥ{c{Z{JkJk1Rk,@IK% 0| eJB,K-1i']Bt9dz0&pZ1o'q(؟dQ=3S SZC8db f&3v2@VPsuk2Gsiw`"IzE%< C !.hC IQ 3o?39T ҍ;PKv I PK|%AOEBPS/dcommon/topnav.gifGIF89a1ֽ筽ޭƔkZZk{Bc{,@ ) l)-'KR$&84 SI) XF P8te NRtHPp;Q%Q@'#rR4P fSQ o0MX[) v + `i9gda/&L9i*1$#"%+ ( E' n7Ȇ(,҅(L@(Q$\x 8=6 'נ9tJ&"[Epljt p#ѣHb :f F`A =l|;&9lDP2ncH R `qtp!dȐYH›+?$4mBA9 i@@ ]@ꃤFxAD*^Ŵ#,(ε  $H}F.xf,BD Z;PK1FAPK|%AOEBPS/dcommon/bp_layout.css# @charset "utf-8"; /* bp_layout.css Copyright 2007, Oracle and/or its affiliates. All rights reserved. */ body { margin: 0ex; padding: 0ex; } h1 { display: none; } #FOOTER { border-top: #0d4988 solid 10px; background-color: inherit; color: #e4edf3; clear: both; } #FOOTER p { font-size: 80%; margin-top: 0em; margin-left: 1em; } #FOOTER a { background-color: inherit; color: gray; } #LEFTCOLUMN { float: left; width: 50%; } #RIGHTCOLUMN { float: right; width: 50%; clear: right; /* IE hack */ } #LEFTCOLUMN div.portlet { margin-left: 2ex; margin-right: 1ex; } #RIGHTCOLUMN div.portlet { margin-left: 1ex; margin-right: 2ex; } div.portlet { margin: 2ex 1ex; padding-left: 0.5em; padding-right: 0.5em; border: 1px #bcc solid; background-color: #f6f6ff; color: black; } div.portlet h2 { margin-top: 0.5ex; margin-bottom: 0ex; font-size: 110%; } div.portlet p { margin-top: 0ex; } div.portlet ul { list-style-type: none; padding-left: 0em; margin-left: 0em; /* IE Hack */ } div.portlet li { text-align: right; } div.portlet li cite { font-style: normal; float: left; } div.portlet li a { margin: 0px 0.2ex; padding: 0px 0.2ex; font-size: 95%; } #NAME { margin: 0em; padding: 0em; position: relative; top: 0.6ex; left: 10px; width: 80%; } #PRODUCT { font-size: 180%; } #LIBRARY { color: #0b3d73; background: inherit; font-size: 180%; font-family: serif; } #RELEASE { position: absolute; top: 28px; font-size: 80%; font-weight: bold; } #TOOLS { list-style-type: none; position: absolute; top: 1ex; right: 2em; margin: 0em; padding: 0em; background: inherit; color: black; } #TOOLS a { background: inherit; color: black; } #NAV { float: left; width: 96%; margin: 3ex 0em 0ex 0em; padding: 2ex 0em 0ex 4%; /* Avoiding horizontal scroll bars. */ list-style-type: none; background: transparent url(../gifs/nav_bg.gif) repeat-x bottom; } #NAV li { float: left; margin: 0ex 0.1em 0ex 0em; padding: 0ex 0em 0ex 0em; } #NAV li a { display: block; margin: 0em; padding: 3px 0.7em; border-top: 1px solid gray; border-right: 1px solid gray; border-bottom: none; border-left: 1px solid gray; background-color: #a6b3c8; color: #333; } #SUBNAV { float: right; width: 96%; margin: 0ex 0em 0ex 0em; padding: 0.1ex 4% 0.2ex 0em; /* Avoiding horizontal scroll bars. */ list-style-type: none; background-color: #0d4988; color: #e4edf3; } #SUBNAV li { float: right; } #SUBNAV li a { display: block; margin: 0em; padding: 0ex 0.5em; background-color: inherit; color: #e4edf3; } #SIMPLESEARCH { position: absolute; top: 5ex; right: 1em; } #CONTENT { clear: both; } #NAV a:hover, #PORTAL_1 #OVERVIEW a, #PORTAL_2 #OVERVIEW a, #PORTAL_3 #OVERVIEW a, #PORTAL_4 #ADMINISTRATION a, #PORTAL_5 #DEVELOPMENT a, #PORTAL_6 #DEVELOPMENT a, #PORTAL_7 #DEVELOPMENT a, #PORTAL_11 #INSTALLATION a, #PORTAL_15 #ADMINISTRATION a, #PORTAL_16 #ADMINISTRATION a { background-color: #0d4988; color: #e4edf3; padding-bottom: 4px; border-color: gray; } #SUBNAV a:hover, #PORTAL_2 #SEARCH a, #PORTAL_3 #BOOKS a, #PORTAL_6 #WAREHOUSING a, #PORTAL_7 #UNSTRUCTURED a, #PORTAL_15 #INTEGRATION a, #PORTAL_16 #GRID a { position: relative; top: 2px; background-color: white; color: #0a4e89; } PK3( # PK|%AOEBPS/dcommon/bookicon.gif:GIF87a!!!)))111999BBBJJJRRRZZZccckkksss{{{ޭ{{ZRRcZZRJJJBB)!!skRB9{sν{skskcZRJ1)!֭ƽ{ZZRccZJJBBB999111)JJ9BB1ZZB!!ﭵBJJ9BB!!))Jk{)1!)BRZJ{BsR!RRJsJ!J{s!JsBkks{RsB{J{c1RBs1ZB{9BJ9JZ!1BJRRs!9R!!9Z9!1)J19JJRk19R1Z)!1B9R1RB!)J!J1R)J119!9J91!9BkksBBJ119BBR!))9!!!JB1JJ!)19BJRZckތ1)1J9B,H*\hp >"p`ƒFF "a"E|ժOC&xCRz OBtX>XE*O>tdqAJ +,WxP!CYpQ HQzDHP)T njJM2ꔀJ2T0d#+I:<жk 'ꤱF AB @@nh Wz' H|-7f\A#yNR5 /PM09u UjćT|q~Yq@&0YZAPa`EzI /$AD Al!AAal 2H@$ PVAB&c*ؠ p @% p-`@b`uBa l&`3Ap8槖X~ vX$Eh`.JhAepA\"Bl, :Hk;PKx[?:PK|%AOEBPS/dcommon/conticon.gif^GIF87a!!!)))111999BBBJJJRRRZZZccckkksss{{{ZRR޽{{ssskkkcccZ991ccRZZBBJJZck)19ZcsBJZ19J!k{k)Z1RZs1!B)!J91{k{)J!B!B911)k{cs!1s!9)s!9!B!k)k1c!)Z!R{9BJcckZZcBBJ99B119{{!!)BBRBBZ!))999R99Z!!999c1!9!)19B1)!B9R,  oua\h2SYPa aowwxYi 9SwyyxxyYSd $'^qYȵYvh ч,/?g{н.J5fe{ڶyY#%/}‚e,Z|pAܠ `KYx,ĉ&@iX9|`p ]lR1khٜ'E 6ÅB0J;t X b RP(*MÄ!2cLhPC <0Ⴁ  $4!B 6lHC%<1e H 4p" L`P!/,m*1F`#D0D^!AO@..(``_؅QWK>_*OY0J@pw'tVh;PKp*c^PK|%AOEBPS/dcommon/blafdoc.cssL@charset "utf-8"; /* Copyright 2002, 2011, Oracle and/or its affiliates. All rights reserved. Author: Robert Crews Version: 2011.10.7 */ body { font-family: Tahoma, sans-serif; /* line-height: 125%; */ color: black; background-color: white; font-size: small; } * html body { /* http://www.info.com.ph/~etan/w3pantheon/style/modifiedsbmh.html */ font-size: x-small; /* for IE5.x/win */ f\ont-size: small; /* for other IE versions */ } h1 { font-size: 165%; font-weight: bold; border-bottom: 1px solid #ddd; width: 100%; } h2 { font-size: 152%; font-weight: bold; } h3 { font-size: 139%; font-weight: bold; } h4 { font-size: 126%; font-weight: bold; } h5 { font-size: 113%; font-weight: bold; display: inline; } h6 { font-size: 100%; font-weight: bold; font-style: italic; display: inline; } a:link { color: #039; background: inherit; } a:visited { color: #72007C; background: inherit; } a:hover { text-decoration: underline; } a img, img[usemap] { border-style: none; } code, pre, samp, tt { font-family: monospace; font-size: 110%; } caption { text-align: center; font-weight: bold; width: auto; } dt { font-weight: bold; } table { font-size: small; /* for ICEBrowser */ } td { vertical-align: top; } th { font-weight: bold; text-align: left; vertical-align: bottom; } ol ol { list-style-type: lower-alpha; } ol ol ol { list-style-type: lower-roman; } td p:first-child, td pre:first-child { margin-top: 0px; margin-bottom: 0px; } table.table-border { border-collapse: collapse; border-top: 1px solid #ccc; border-left: 1px solid #ccc; } table.table-border th { padding: 0.5ex 0.25em; color: black; background-color: #f7f7ea; border-right: 1px solid #ccc; border-bottom: 1px solid #ccc; } table.table-border td { padding: 0.5ex 0.25em; border-right: 1px solid #ccc; border-bottom: 1px solid #ccc; } span.gui-object, span.gui-object-action { font-weight: bold; } span.gui-object-title { } p.horizontal-rule { width: 100%; border: solid #cc9; border-width: 0px 0px 1px 0px; margin-bottom: 4ex; } div.zz-skip-header { display: none; } td.zz-nav-header-cell { text-align: left; font-size: 95%; width: 99%; color: black; background: inherit; font-weight: normal; vertical-align: top; margin-top: 0ex; padding-top: 0ex; } a.zz-nav-header-link { font-size: 95%; } td.zz-nav-button-cell { white-space: nowrap; text-align: center; width: 1%; vertical-align: top; padding-left: 4px; padding-right: 4px; margin-top: 0ex; padding-top: 0ex; } a.zz-nav-button-link { font-size: 90%; } div.zz-nav-footer-menu { width: 100%; text-align: center; margin-top: 2ex; margin-bottom: 4ex; } p.zz-legal-notice, a.zz-legal-notice-link { font-size: 85%; /* display: none; */ /* Uncomment to hide legal notice */ } /*************************************/ /* Begin DARB Formats */ /*************************************/ .bold, .codeinlinebold, .syntaxinlinebold, .term, .glossterm, .seghead, .glossaryterm, .keyword, .msg, .msgexplankw, .msgactionkw, .notep1, .xreftitlebold { font-weight: bold; } .italic, .codeinlineitalic, .syntaxinlineitalic, .variable, .xreftitleitalic { font-style: italic; } .bolditalic, .codeinlineboldital, .syntaxinlineboldital, .titleinfigure, .titleinexample, .titleintable, .titleinequation, .xreftitleboldital { font-weight: bold; font-style: italic; } .itemizedlisttitle, .orderedlisttitle, .segmentedlisttitle, .variablelisttitle { font-weight: bold; } .bridgehead, .titleinrefsubsect3 { font-weight: bold; } .titleinrefsubsect { font-size: 126%; font-weight: bold; } .titleinrefsubsect2 { font-size: 113%; font-weight: bold; } .subhead1 { display: block; font-size: 139%; font-weight: bold; } .subhead2 { display: block; font-weight: bold; } .subhead3 { font-weight: bold; } .underline { text-decoration: underline; } .superscript { vertical-align: super; } .subscript { vertical-align: sub; } .listofeft { border: none; } .betadraft, .alphabetanotice, .revenuerecognitionnotice { color: #e00; background: inherit; } .betadraftsubtitle { text-align: center; font-weight: bold; color: #e00; background: inherit; } .comment { color: #080; background: inherit; font-weight: bold; } .copyrightlogo { text-align: center; font-size: 85%; } .tocsubheader { list-style-type: none; } table.icons td { padding-left: 6px; padding-right: 6px; } .l1ix dd, dd dl.l2ix, dd dl.l3ix { margin-top: 0ex; margin-bottom: 0ex; } div.infoboxnote, div.infoboxnotewarn, div.infoboxnotealso { margin-top: 4ex; margin-right: 10%; margin-left: 10%; margin-bottom: 4ex; padding: 0.25em; border-top: 1pt solid gray; border-bottom: 1pt solid gray; } p.notep1 { margin-top: 0px; margin-bottom: 0px; } .tahiti-highlight-example { background: #ff9; text-decoration: inherit; } .tahiti-highlight-search { background: #9cf; text-decoration: inherit; } .tahiti-sidebar-heading { font-size: 110%; margin-bottom: 0px; padding-bottom: 0px; } /*************************************/ /* End DARB Formats */ /*************************************/ @media all { /* * * { line-height: 120%; } */ dd { margin-bottom: 2ex; } dl:first-child { margin-top: 2ex; } } @media print { body { font-size: 11pt; padding: 0px !important; } a:link, a:visited { color: black; background: inherit; } code, pre, samp, tt { font-size: 10pt; } #nav, #search_this_book, #comment_form, #comment_announcement, #flipNav, .noprint { display: none !important; } body#left-nav-present { overflow: visible !important; } } PKʍPK|%AOEBPS/dcommon/rightnav.gif&GIF89a1ֽ筽ޭƔkZZk{Bc{,@ ) l)- $CҠҀ ! D1 #:aS( c4B0 AC8 ְ9!%MLj Z * ctypJBa H t>#Sb(clhUԂ̗4DztSԙ9ZQҀEPEPEPEPEPEPEPM=iԍP Gii c*yF 1׆@\&o!QY00_rlgV;)DGhCq7~..p&1c:u֫{fI>fJL$}BBP?JRWc<^j+χ5b[hֿ- 5_j?POkeQ^hֿ1L^ H ?Qi?z?+_xɔŪ\썽O]χ>)xxV/s)e6MI7*ߊޛv֗2J,;~E4yi3[nI`Ѱe9@zXF*W +]7QJ$$=&`a۾?]N T䏟'X)Ɣkf:j |>NBWzYx0t!* _KkoTZ?K Gc+UyڹgNuh^iSo5{\ܹ3Yos}.>if FqR5\/TӮ#]HS0DKu{($"2xִ{SBJ8=}Y=.|Tsц2UЫ%.InaegKo z ݎ3ֹxxwM&2S%';+I',kW&-"_¿_ Vq^ܫ6pfT2RV A^6RKetto^[{w\jPZ@ޢN4/XN#\42j\(z'j =~-I#:q[Eh|X:sp* bifp$TspZ-}NM*B-bb&*xUr#*$M|QWY ~p~- fTED6O.#$m+t$˙H"Gk=t9r娮Y? CzE[/*-{c*[w~o_?%ƔxZ:/5𨴟q}/]22p qD\H"K]ZMKR&\C3zĽ[PJm]AS)Ia^km M@dК)fT[ijW*hnu Ͳiw/bkExG£@f?Zu.s0(<`0ֹoxOaDx\zT-^ѧʧ_1+CP/p[w 9~U^[U<[tĽwPv[yzD1W='u$Oeak[^ |Gk2xv#2?¹TkSݕ| rݞ[Vi _Kz*{\c(Ck_܏|?u jVڔ6f t?3nmZ6f%QAjJf9Rq _j7Z-y.pG$Xb]0')[_k;$̭?&"0FOew7 z-cIX岛;$u=\an$ zmrILu uٞ% _1xcUW%dtÀx885Y^gn;}ӭ)場QEQ@Q@Q@Q@Q@Q@!4xPm3w*]b`F_931˜[ן+(> E ly;<;MF-qst+}DH @YKlLmؤciN<|]IU)Lw(8t9FS(=>og<\Z~u_+X1ylsj'eՃ*U3`C!N9Q_WܱhKc93^ua>H ƕGk=8~e#_?{ǀe-[2ٔ7;=&K挑5zsLdx(e8#{1wS+ΝVkXq9>&yஏh$zq^0~/j@:/«Vnce$$uoPp}MC{$-akH@ɫ1O !8R9s5ԦYmϧ'OUṡ5T,!Ԛ+s#1Veo=[)g>#< s)ƽُA^䠮ωFUj(ǩ|N3Jڷ睁ϱuږZYGOTsI<&drav?A^_f׻B$,O__ԿC`it{6>G׈C~&$y؎v1q9Sc1fH[ѽ>,gG'0'@Vw,BO [#>ﱺg5ΒFVD%Yr:O5 Tu+O멃]ی38Ze}R&ѝ_xzc1DXgس;<,_,{ƽY'AS#oF.M#~cBuEx7G+Y)(5q+GCV;qF+CLQ)qEC&6z𿊘z}?&w=+)??&\g{;V??׻xGœdٿ׼-Nc')3K]N)iLTӿCdb7Q^a N sd>Fz[0S^s'Zi 77D}kWus ab~~H(>.fif9,~|Jk;YN3H8Y(t6Q݉k͇_÷Z+2߄&[ +Tr^藺97~c܎=[f1RrBǓ^kEMhxYVm<[џ6| kqbѱ| YA{G8p?\UM7Z66 g1U1igU69 u5Pƪ:VVZC=[@ҹ¨$kSmɳО\vFz~i3^a Osŧυ9Q}_3 όO{/wgoet39 vO2ea;Ύ7$U#?k+Ek&dpzbӱ+TaB0gN{[N7Gי}U7&@?>Fz~E!a@s ?'67XxO*!?qi]֏TQN@tI+\^s8l0)2k!!iW8F$(yOּT.k,/#1:}8uT˾+5=O/`IW G֯b.-<= HOm;~so~hW5+kS8s.zwE| ?4ӿw/K N 9?j(#0UT` Wzw}:_*9m>󑓀F?ELzv=8q:=WgJ`nDr Zе<ֹ](Q@Q@Q@Q@Q@Q@Q@Q@ 'IdC0EYJVcMty_~u+Sw-aO n<[YJgL#6i g5ЖDZ14cʝ!!\/M}/_AYR__>oC? _?7_G#RERW쏞KB}JxGSkǕA pƱơP m]hwB7U$Zq M95"3q1ioATߚ{g.t uu2k=;h#YB= fgS :TdLԃ!44mFK{Hrd^7oz|BVr<{)6AXգV»|>*/hS܏z͆OM=Εq (s|s׊LKQI :9NJ)P+!ʣoAF>+=@I}"x/}۠1aנc¹4emC:>p_xWKX` >R3_S½èųp3޺u3N e یbmͺ<_ mnݮ1Op?Gm)Qb%N585'%Ahs\6yw!"&Ɨ._wk)}GP;Z!#\"< *oƾ\)}N>"լ/~]Lg}pBG X?<zZ#x69S=6) jzx=y9O&>+e!!? ?s~k5Gʏ)?*ce7Ox~k5􇔾Q/e7/Ԑ#3OgNC0] ;_FiRl>Q.g>!%k#ú:Kn'&}?U@\pџPtp)v<{_i}Oվֲ3XIYIx~b<D?(=_JXH=bbi=Oh?_ C_O)}oW쏜? %Ƶ;-RYFi`wۭ{ϖZMtQ$"c_+ԃx1*0b;ԕ݋ESQEQEQEQEQEQEQEQEQEQZ(1F)h1K@XLRE&9P (bf{RӨ&)PEPEPbԴPGKZ(iإbn(:A%S0(-&)P+ ڎԴP11F)h&:LRmQ@Q@Š(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((PKje88PK|%AOEBPS/dcommon/help.gif!GIF89a1εֵ֜֜{kZsBc{,@ )sƠTQ$8(4ʔ%ŌCK$A HP`$h8ŒSd+ɡ\ H@%' 6M HO3SJM /:Zi[7 \( R9r ERI%  N=aq   qƦs *q-n/Sqj D XZ;PKއ{&!PK|%A OEBPS/toc.htm Table of Contents

Contents

List of Examples

List of Figures

List of Tables

Title and Copyright Information

Preface

What's New in Application Development?

Part I SQL for Application Developers

1 SQL Processing for Application Developers

2 Using SQL Data Types in Database Applications

3 Using Regular Expressions in Database Applications

4 Using Indexes in Database Applications

5 Maintaining Data Integrity in Database Applications

Part II PL/SQL for Application Developers

6 Coding PL/SQL Subprograms and Packages

7 Using PL/Scope

8 Using the PL/SQL Hierarchical Profiler

9 Developing PL/SQL Web Applications

10 Developing PL/SQL Server Pages (PSP)

11 Using Continuous Query Notification (CQN)

Part III Advanced Topics for Application Developers

12 Using Oracle Flashback Technology

13 Choosing a Programming Environment

14 Developing Applications with Multiple Programming Languages

15 Developing Applications with Oracle XA

16 Developing Applications with the Publish-Subscribe Model

17 Using the Identity Code Package

18 Schema Object Dependency

19 Edition-Based Redefinition

A Multithreaded extproc Agent

Index

PK"PK|%AOEBPS/adfns_plscope.htm Using PL/Scope

7 Using PL/Scope

PL/Scope is a compiler-driven tool that collects data about identifiers in PL/SQL source code at program-unit compilation time and makes it available in static data dictionary views. The collected data includes information about identifier types, usages (declaration, definition, reference, call, assignment) and the location of each usage in the source code.

PL/Scope enables the development of powerful and effective PL/Scope source code browsers that increase PL/SQL developer productivity by minimizing time spent browsing and understanding source code.

PL/Scope is intended for application developers, and is usually used in the environment of a development database.


Note:

PL/Scope cannot collect data for a PL/SQL unit whose source code is wrapped. For information about wrapping PL/SQL source code, see Oracle Database PL/SQL Language Reference.

Topics:

Specifying Identifier Collection

By default, PL/Scope does not collect data for identifiers in the PL/SQL source program. To have PL/Scope collect data for all identifiers in the PL/SQL source program, including identifiers in package bodies, set the PL/SQL compilation parameter PLSCOPE_SETTINGS to 'IDENTIFIERS:ALL'.


Note:

Collecting all identifiers might generate large amounts of data and slow compile time.

PL/Scope stores the data that it collects in the SYSAUX tablespace. If the SYSAUX tablespace is unavailable, and you compile a program unit with PLSCOPE_SETTINGS='IDENTIFIERS:ALL', PL/Scope does not collect data for the compiled object. The compiler does not issue a warning, but it saves a warning in USER_ERRORS.


See Also:


PL/Scope Identifier Data for STANDARD and DBMS_STANDARD

The packages STANDARD and DBMS_STANDARD declare and define base types, such as VARCHAR2 and NUMBER, and subprograms such as RAISE_APPLICATION_ERROR. If your database has PL/Scope identifier data for these packages, PL/Scope can track your usage of the identifiers that these packages create.

Do You Need STANDARD and DBMS_STANDARD Identifier Data?

You can use PL/Scope without STANDARD and DBMS_STANDARD identifier data. You need this data only if you must know where your code uses the base types or subprograms that these packages create—for example, to know where your code uses the base type BINARY_INTEGER, so that you can substitute PLS_INTEGER.

Does Your Database Have STANDARD and DBMS_STANDARD Identifier Data?

A newly created Oracle Database 11g Release 1 (11.1.0.7), or a database that was upgraded to 11.1.0.7 from Oracle Database 10g Release 2, has PL/Scope identifier data for the packages STANDARD and DBMS_STANDARD. A database that was upgraded to 11.1.0.7 from 11.1.0.6 does not have this data.

To see if your database has this data, use the query in Example 7-1.

Example 7-1 shows what the query returns when the database has PL/Scope identifier data for STANDARD and DBMS_STANDARD.

Example 7-1 Is STANDARD and DBMS_STANDARD PL/Scope Identifier Data Available?

Query:

SELECT UNIQUE OBJECT_NAME
FROM ALL_IDENTIFIERS
WHERE OBJECT_NAME IN ('STANDARD', 'DBMS_STANDARD')
AND OWNER='SYS'
ORDER BY OBJECT_NAME;
 

Result:

OBJECT_NAME
------------------------------
DBMS_STANDARD
STANDARD
 
2 rows selected.

If the query in Example 7-1 selects no rows, then the database does not have PL/Scope identifier data for the packages STANDARD and DBMS_STANDARD. To collect this data, a DBA must recompile the packages STANDARD and DBMS_STANDARD, as explained in "Recompiling STANDARD and DBMS_STANDARD".

Recompiling STANDARD and DBMS_STANDARD

A DBA can use this procedure to recompile the packages STANDARD and DBMS_STANDARD:


Note:

This procedure invalidates and revalidates (by recompiling) every PL/SQL object in the database.

  1. Connect to the database, shut it down, and then start it in UPGRADE mode:

    CONNECT / AS SYSDBA;
    SHUTDOWN IMMEDIATE;
    STARTUP PFILE=parameter_initialization_file UPGRADE;
    
  2. Have PL/Scope collect data for all identifiers in the packages STANDARD and DBMS_STANDARD:

    ALTER SESSION SET PLSCOPE_SETTINGS='IDENTIFIERS:ALL';
    
  3. Invalidate and recompile the database:

    @?/rdbms/admin/utlirp.sql
    

    Now all PL/SQL objects in the database are invalid except STANDARD and DBMS_STANDARD, which were recompiled with PLSCOPE_SETTINGS='IDENTIFIERS:ALL'.

  4. (Optional) Invalidate any other PL/SQL objects to be recompiled with PLSCOPE_SETTINGS='IDENTIFIERS:ALL', using a script similar to this.

    Customize the query on lines 5 through 9 to invalidate only those objects for which you need PL/Scope identifier data. Collecting all identifiers for all objects, as this script does, might generate large amounts of data and slow compile time:

    DECLARE
      TYPE ObjIDArray IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
      ObjIDs  ObjIDArray;
    BEGIN
      SELECT object_id BULK COLLECT INTO ObjIDs
      FROM ALL_OBJECTS
      WHERE object_type IN
        (SELECT DISTINCT TYPE
         FROM ALL_PLSQL_OBJECT_SETTINGS);
      FOR i IN 1..SQL%ROWCOUNT LOOP
        BEGIN
          DBMS_UTILITY.INVALIDATE(ObjIDs(i),
            'PLSCOPE_SETTINGS=IDENTIFIERS:ALL REUSE SETTINGS');
          NULL;
        END;
      END LOOP;
    END;
    /
    

    Notes:

    In the preceding script:
    • Do not substitute ObjIDs.LAST for SQL%ROWCOUNT, because ObjIDs attributes are dependent on a package that is locked by the anonymous block.

    • If your database is large, do not substitute a cursor FOR LOOP for the BULK COLLECT statement, or you will run out of resources.


  5. Shut down the database, and then start it in NORMAL mode:

    SHUTDOWN IMMEDIATE;
    STARTUP PFILE=parameter_initialization_file;
    
  6. For any remaining invalid PL/SQL objects, do either of these:

Running utlrp.sql to Recompile Invalid PL/SQL Objects

If the database was restarted in NORMAL mode (step 5), then a DBA, or a user who has been granted the DBA role, can use this procedure:

  1. Connect to the database as SYS:

    CONNECT / AS SYS;
    
  2. Run the script utlrp.sql:

    @?/rdbms/admin/utlrp.sql
    

    If the script gives you any instructions, follow them, and then run the script again.

    If the script terminates abnormally without giving any instructions, run it again.

How Much Space is PL/Scope Data Using?

PL/Scope stores its data in the SYSAUX tablespace. If you are logged on as SYSDBA, you can use the query in Example 7-2 to display the amount of space that PL/Scope data is using.

Example 7-2 How Much Space is PL/Scope Data Using?

Query:

SELECT SPACE_USAGE_KBYTES
FROM V$SYSAUX_OCCUPANTS
WHERE OCCUPANT_NAME='PL/SCOPE';
 

Result:

SPACE_USAGE_KBYTES
------------------
              1600
 
1 row selected.

For information about managing the SYSAUX tablespace, see Oracle Database Administrator's Guide.

Viewing PL/Scope Data

To view the data that PL/Scope has collected, you can use either:

Static Data Dictionary Views

The static data dictionary views *_IDENTIFIERS display information about PL/Scope identifiers, including their types and usages. For general information about these views, see Oracle Database Reference.

Topics:

Unique Keys

Each row of a *_IDENTIFIERS view represents a unique usage of an identifier in the PL/SQL unit. In each of these views, these are equivalent unique keys within a compilation unit:

  • LINE, COL, and USAGE

  • USAGE_ID

For the usages in the *_IDENTIFIERS views, see "Usages that PL/Scope Reports".


Note:

An identifier that is passed to a subprogram in IN OUT mode has two rows in *_IDENTIFIERS: a REFERENCE usage (corresponding to IN) and an ASSIGNMENT usage (corresponding to OUT).

Context

Context is useful for discovering relationships between usages. Except for top-level schema object declarations and definitions, every usage of an identifier happens within the context of another usage. For example:

  • A local variable declaration happens within the context of a top-level procedure declaration.

  • If an identifier is declared as a variable, such as x VARCHAR2(10), the USAGE_CONTEXT_ID of the VARCHAR2 type reference contains the USAGE_ID of the x declaration, allowing you to associate the variable declaration with its type.

In other words, USAGE_CONTEXT_ID is a reflexive foreign key to USAGE_ID, as Example 7-3 shows.

Example 7-3 USAGE_CONTEXT_ID and USAGE_ID

ALTER SESSION SET PLSCOPE_SETTINGS = 'IDENTIFIERS:ALL';

CREATE OR REPLACE PROCEDURE a (p1 IN BOOLEAN) IS
  v PLS_INTEGER;
BEGIN
  v := 42;
  DBMS_OUTPUT.PUT_LINE(v);
  RAISE_APPLICATION_ERROR (-20000, 'Bad');
EXCEPTION
  WHEN Program_Error THEN NULL;
END a;
/
CREATE OR REPLACE PROCEDURE b (p2 OUT PLS_INTEGER, p3 IN OUT VARCHAR2) IS
  n NUMBER;
  q BOOLEAN := TRUE;
BEGIN
  FOR j IN 1..5 LOOP
    a(q); a(TRUE); a(TRUE);
    IF j > 2 THEN
       GOTO z;
    END IF;
  END LOOP;
<<z>> DECLARE
  d CONSTANT CHAR(1) := 'X';
  BEGIN
    SELECT COUNT(*) INTO n FROM Dual WHERE Dummy = d;
  END z;
END b;
/
WITH v AS (
  SELECT    Line,
            Col,
            INITCAP(NAME) Name,
            LOWER(TYPE)   Type,
            LOWER(USAGE)  Usage,
            USAGE_ID,
            USAGE_CONTEXT_ID
    FROM USER_IDENTIFIERS
      WHERE Object_Name = 'B'
        AND Object_Type = 'PROCEDURE'
)
SELECT RPAD(LPAD(' ', 2*(Level-1)) ||
                 Name, 20, '.')||' '||
                 RPAD(Type, 20)||
                 RPAD(Usage, 20)
                 IDENTIFIER_USAGE_CONTEXTS
  FROM v
  START WITH USAGE_CONTEXT_ID = 0
  CONNECT BY PRIOR USAGE_ID = USAGE_CONTEXT_ID
  ORDER SIBLINGS BY Line, Col
/

Result:

IDENTIFIER_USAGE_CONTEXTS
-------------------------------------------------------------
B................... procedure           declaration
  B................. procedure           definition
    P2.............. formal out          declaration
      Pls_Integer... subtype             reference
    P3.............. formal in out       declaration
      Varchar2...... character datatype  reference
    N............... variable            declaration
      Number........ number datatype     reference
    Q............... variable            declaration
      Q............. variable            assignment
      Boolean....... boolean datatype    reference
    J............... iterator            declaration
      A............. procedure           call
        Q........... variable            reference
      A............. procedure           call
      A............. procedure           call
      J............. iterator            reference
      Z............. label               reference
    Z............... label               declaration
      D............. constant            declaration
        D........... constant            assignment
        Char........ subtype             reference
      N............. variable            assignment
      D............. constant            reference
 
24 rows selected.

Signature

The signature of an identifier is unique, within and across program units. That is, the signature distinguishes the identifier from other identifiers with the same name, whether they are defined in the same program unit or different program units.

For the program unit in Example 7-4, which has two identifiers named p, the static data dictionary view USER_IDENTIFIERS has several rows in which NAME is p, but in these rows, SIGNATURE varies. The rows associated with the outer procedure p have one signature, and the rows associated with the inner procedure p have another signature. If program unit q calls procedure p, the USER_IDENTIFIERS view for q has a row in which NAME is p and SIGNATURE is the signature of the outer procedure p.

Example 7-4 Program Unit with Two Identifiers Named p

CREATE OR REPLACE PROCEDURE p IS
  PROCEDURE p IS
  BEGIN
    DBMS_OUTPUT.PUT_LINE('Inner p');
  END p;
BEGIN
  DBMS_OUTPUT.PUT_LINE('Outer p');
  p();
END p;
/

Demo Tool

$ORACLE_HOME/plsql/demo/plscopedemo.sql is an HTML-based demo implemented as a PL/SQL Web Application using the PL/SQL Web Toolkit. For more information about PL/SQL Web Applications, see "Implementing PL/SQL Web Applications".

SQL Developer

PL/Scope is a feature of SQL Developer. For information about using PL/Scope from SQL Developer, see the SQL Developer online documentation.

Identifier Types that PL/Scope Collects

Table 7-1 shows the identifier types that PL/Scope collects, in alphabetical order. The identifier types in Table 7-1 appear in the TYPE column of the *_IDENTIFIER static data dictionary views, which are described in Oracle Database Reference.


Note:

Identifiers declared in compilation units that were not compiled with PLSCOPE_SETTINGS='IDENTIFIERS:ALL' do not appear in *_IDENTIFIER static data dictionary views.

Table 7-1 Identifier Types that PL/Scope Collects

TYPE Column ValueComment

ASSOCIATIVE ARRAY


CONSTANT


CURSOR


BFILE DATATYPEBLOB DATATYPEBOOLEAN DATATYPECHARACTER DATATYPECLOB DATATYPEDATE DATATYPEINTERVAL DATATYPENUMBER DATATYPETIME DATATYPETIMESTAMP DATATYPE

Each DATATYPE is a base type declared in package STANDARD.

EXCEPTION


FORMAL INFORMAL IN OUTFORMAL OUT


FUNCTION


INDEX TABLE


ITERATOR

An iterator is the index of a FOR loop.

LABEL

A label declaration also acts as a context.

LIBRARY


NESTED TABLE


OBJECT


OPAQUE

Examples of internal opaque types are ANYDATA and XMLType.

PACKAGE


PROCEDURE


RECORD


REFCURSOR


SUBTYPE


SYNONYM

PL/Scope does not resolve the base object name of a synonym. To find the base object name of a synonym, query *_SYNONYMS.

TRIGGER


UROWID


VARRAY


VARIABLE

Can be object attribute, local variable, package variable, or record field.


Usages that PL/Scope Reports

Table 7-2 shows the usages that PL/Scope reports, in alphabetical order. The identifier types in Table 7-2 appear in the USAGE column of the *_IDENTIFIER static data dictionary views, which are described in Oracle Database Reference.

Table 7-2 Usages that PL/Scope Reports

USAGE Column ValueDescription

ASSIGNMENT

An assignment can be made only to an identifier that can have a value, such as a VARIABLE. Examples of assignments are:

  • Using an identifier to the left of an assignment operator

  • Using an identifier in the INTO clause of a FETCH statement

  • Passing an identifier to a subprogram by reference (OUT mode)

  • Using an identifier as the bind variable in the USING clause of an EXECUTE IMMEDIATE statement in either OUT or IN OUT mode

An identifier that is passed to a subprogram in IN OUT mode has both a REF'0ERENCE usage (corresponding to IN) and an ASSIGNMENT usage (corresponding to OUT).

CALL

In the context of PL/Scope, a CALL is an operation that pushes a call onto the call stack; that is:

  • A call to a FUNCTION or PROCEDURE

  • Running or fetching a cursor identifier (a logical call to SQL)

A GOTO statement or raise of an exception is not a CALL, because neither pushes a call onto the call stack.

DECLARATION

A DECLARATION tells the compiler that an identifier exists, and each identifier has exactly one DECLARATION. Each DECLARATION can have an associated data type.

For a loop index declaration, LINE and COL (in *_IDENTIFIERS views) are the line and column of the FOR clause that implicitly declares the loop index.

For a label declaration, LINE and COL are the line and column on which the label appears (and is implicitly declared) within the delimiters << and >>.

DEFINITION

A DEFINITION tells the compiler how to implement or use a previously declared identifier.

Each of these types of identifiers has a DEFINITION:

  • EXCEPTION (can have multiple definitions)

  • FUNCTION

  • OBJECT

  • PACKAGE

  • PROCEDURE

  • TRIGGER

For a top-level identifier only, the DEFINITION and DECLARATION are in the same place.

REFERENCE

A REFERENCE uses an identifier without changing its value. Examples of references are:

  • Raising an exception identifier

  • Using a type identifier in the declaration of a variable or formal parameter

  • Using a variable identifier whose type contains fields to access a field. For example, in myrecordvar.myfield := 1, a reference is made to myrecordvar, and an assignment is made to myfield.

  • Using a cursor for any purpose except FETCH

  • Passing an identifier to a subprogram by value (IN mode)

  • Using an identifier as the bind variable in the USING clause of an EXECUTE IMMEDIATE statement in either IN or IN OUT mode

An identifier that is passed to a subprogram in IN OUT mode has both a REFERENCE usage (corresponding to IN) and an ASSIGNMENT usage (corresponding to OUT).


Sample PL/Scope Session

In this sample session, assume that you are logged in as HR.

  1. Set the session parameter:

    ALTER SESSION SET PLSCOPE_SETTINGS='IDENTIFIERS:ALL';
    
  2. Create this package:

    CREATE OR REPLACE PACKAGE PACK1 IS
      TYPE r1 is RECORD (rf1 VARCHAR2(10));
      FUNCTION F1(fp1 NUMBER) RETURN NUMBER;
      PROCEDURE P1(pp1 VARCHAR2);
    END PACK1;
    /
    CREATE OR REPLACE PACKAGE BODY PACK1 IS
      FUNCTION F1(fp1 NUMBER) RETURN NUMBER IS
        a NUMBER := 10;
      BEGIN
        RETURN a;
      END F1;
      PROCEDURE P1(pp1 VARCHAR2) IS
        pr1 r1;
      BEGIN
        pr1.rf1 := pp1;
      END;
    END PACK1;
    /
    
  3. Verify that PL/Scope collected all identifiers for the package body:

    SELECT PLSCOPE_SETTINGS
    FROM USER_PLSQL_OBJECT_SETTINGS
    WHERE NAME='PACK1' AND TYPE='PACKAGE BODY'
    

    Result:

    PLSCOPE_SETTINGS
    ------------------------------------------------------------------------
    IDENTIFIERS:ALL
    
  4. Display unique identifiers in HR by querying for all DECLARATION usages. For example, to see all unique identifiers with name like %1, use these SQL*Plus formatting commands and this query:

    COLUMN NAME FORMAT A6
    COLUMN SIGNATURE FORMAT A32
    COLUMN TYPE FORMAT A9
    
    SELECT NAME, SIGNATURE, TYPE
    FROM USER_IDENTIFIERS
    WHERE NAME LIKE '%1' AND USAGE='DECLARATION'
    ORDER BY OBJECT_TYPE, USAGE_ID;
    

    Result is similar to:

    NAME   SIGNATURE                        TYPE
    ------ -------------------------------- ---------
    PACK1  41820FA4D5EF6BE707895178D0C5C4EF PACKAGE
    R1     EEBB6849DEE31BC77BF186EBAE5D4E2D RECORD
    RF1    41D70040337349634A7F547BC83517C7 VARIABLE
    F1     4559CF050A5F5C3E5F5FFDD0D9D55EFA FUNCTION
    FP1    CAC3474C112DBEC67AB926978D9A16C1 FORMAL IN
    P1     B7C0576BA4D00C33A65CC0C64CADAB89 PROCEDURE
    PP1    6B74CF95A5B7377A735925DFAA280266 FORMAL IN
    FP1    98EB63B8A4AFEB5EF94D50A20165D6CC FORMAL IN
    PP1    AD89FE0EAE9CE5D6D48AA4684E0D57DF FORMAL IN
    PR1    1B5117F30E8DAE0261A02CAA5E33883F VARIABLE
     
    10 rows selected.
    

    The *_IDENTIFIERS static data dictionary views display only basic type names; for example, the TYPE of a local variable or record field is VARIABLE. To determine the exact type of a VARIABLE, you must use its USAGE_CONTEXT_ID.

  5. Find all local variables:

    COLUMN VARIABLE_NAME FORMAT A13
    COLUMN CONTEXT_NAME FORMAT A12
    
    SELECT a.NAME variable_name,
            b.NAME context_name,
            a.SIGNATURE
    FROM USER_IDENTIFIERS a, USER_IDENTIFIERS b
    WHERE a.USAGE_CONTEXT_ID = b.USAGE_ID
    AND a.TYPE = 'VARIABLE'
    AND a.USAGE = 'DECLARATION'
    AND a.OBJECT_NAME = 'PACK1'
    AND a.OBJECT_NAME = b.OBJECT_NAME
    AND a.OBJECT_TYPE =  b.OBJECT_TYPE
    AND (b.TYPE = 'FUNCTION' or b.TYPE = 'PROCEDURE')
    ORDER BY a.OBJECT_TYPE, a.USAGE_ID;
    

    Result is similar to:

    VARIABLE_NAME CONTEXT_NAME SIGNATURE
    ------------- ------------ --------------------------------
    A             F1           1691C6B3C951FCAA2CBEEB47F85CF128
    PR1           P1           1B5117F30E8DAE0261A02CAA5E33883F
     
    2 rows selected.
    
    
  6. Find all usages performed on the local variable A:

    COLUMN USAGE FORMAT A11
    COLUMN USAGE_ID FORMAT 999
    COLUMN OBJECT_NAME FORMAT A11
    COLUMN OBJECT_TYPE FORMAT A12
    
    SELECT USAGE, USAGE_ID, OBJECT_NAME, OBJECT_TYPE
    FROM USER_IDENTIFIERS
    WHERE SIGNATURE='1691C6B3C951FCAA2CBEEB47F85CF128'  -- signature of A
    ORDER BY OBJECT_TYPE, USAGE_ID;
    

    Result:

    USAGE       USAGE_ID OBJECT_NAME OBJECT_TYPE
    ----------- -------- ----------- ------------
    DECLARATION        6 PACK1       PACKAGE BODY
    ASSIGNMENT         8 PACK1       PACKAGE BODY
    REFERENCE          9 PACK1       PACKAGE BODY
     
    3 rows selected.
    

    The usages performed on the local identifier A are the identifier declaration (USAGE_ID 6), an assignment (USAGE_ID 8), and a reference (USAGE_ID 9).

  7. From the declaration of the local identifier A, find its type:

    COLUMN NAME FORMAT A6
    COLUMN TYPE FORMAT A15
    
    SELECT a.NAME, a.TYPE
    FROM USER_IDENTIFIERS a, USER_IDENTIFIERS b
    WHERE a.USAGE = 'REFERENCE'
    AND a.USAGE_CONTEXT_ID = b.USAGE_ID
    AND b.USAGE = 'DECLARATION'
    AND b.SIGNATURE = '4559CF050A5F5C3E5F5FFDD0D9D55EFA'  -- signature of F1
    AND a.OBJECT_TYPE = b.OBJECT_TYPE
    AND a.OBJECT_NAME = b.OBJECT_NAME;
    

    Result:

    NAME   TYPE
    ------ ---------------
    NUMBER NUMBER DATATYPE
     
    1 row selected.
    

    Note:

    This query produces the output shown only if your database has PL/Scope identifier data for the packages STANDARD and DBMS_STANDARD. For more information, see "PL/Scope Identifier Data for STANDARD and DBMS_STANDARD".

  8. Find out where the assignment to local identifier A occurred:

    SELECT LINE, COL, OBJECT_NAME, OBJECT_TYPE
    FROM USER_IDENTIFIERS
    WHERE SIGNATURE='1691C6B3C951FCAA2CBEEB47F85CF128'  -- signature of A
    AND USAGE='ASSIGNMENT';
    

    Result:

          LINE        COL OBJECT_NAME OBJECT_TYPE
    ---------- ---------- ----------- ------------
             3          5 PACK1       PACKAGE BODY
     
    1 row selected.
    
PK{+٧ϧPK|%AOEBPS/adfns_psp.htm Developing PL/SQL Server Pages (PSP)

10 Developing PL/SQL Server Pages (PSP)

This chapter explains how to develop PL/SQL Server Pages (PSP), which let you include dynamic content in web pages.

Topics:

What Are PL/SQL Server Pages and Why Use Them?

PL/SQL Server Pages (PSP) are server-side scripts that include dynamic content, including the results of SQL queries, inside web pages. You can author the web pages in an HTML authoring tool and insert blocks of PL/SQL code.

Example 10-1 shows a simple PL/SQL server page called simple.psp.

Example 10-1 simple.psp

<%@ page language="PL/SQL" %>
<%@ page contentType="text/html" %>
<%@ plsql procedure="show_employees" %>
<%-- This example displays the last name and first name of every 
     employee in the hr.employees table. --%>
<%!
  CURSOR emp_cursor IS
    SELECT last_name, first_name
      FROM hr.employees
        ORDER BY last_name;
%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html">
<title>List of Employees</title>
</head>
<body TEXT="#000000" BGCOLOR="#FFFFFF">
<h1>List of Employees</h1>
<table width="40%" border="1">
<tr>
<th align="left">Last Name</th>
<th align="left">First Name</th>
</tr>
<%  FOR emp_record IN emp_cursor LOOP %>
  <tr>
  <td> <%= emp_record.last_name %> </td>
  <td> <%= emp_record.first_name %> </td>
  </tr>
<%  END LOOP; %>
</table>
</body>
</html>

You can compile and load a PL/SQL server page into the database with the loadpsp command-line utility. This command loads simple.psp into the hr schema, replacing the show_employees procedure if it exists:

loadpsp -replace simple.psp
Enter Password: password

Browser users can run the show_employees procedure through a URL. An HTML page that displays the last and first names of employees in the hr.employees table is returned to the browser through the PL/SQL gateway.

Deploying content through PL/SQL Server Pages has these advantages:

  • For developers familiar with PL/SQL, the server pages are the easiest way to create professional web pages that include database-generated content. You can develop web pages as you usually do and then embed PL/SQL code in the HTML.

  • PL/SQL Server Pages can be more convenient than using the HTP and HTF packages to write out HTML content line by line.

  • Because processing is performed on the database server, the client browser receives a plain HTML page with no special script tags. You can support all browsers and browser levels equally.

  • Network traffic is efficient because use of PL/SQL Server Pages minimizes the number of database round-trips.

  • You can write content quickly and follow a rapid, iterative development process. You maintain central control of the software, with only a web browser required on the client system.

Prerequisites for Developing and Deploying PL/SQL Server Pages

To develop and deploy PL/SQL server pages, you must meet these prerequisites:

  • To write a PL/SQL server page you need access to a text editor or HTML authoring tool for writing the script. No other development tool is required.

  • To load a PL/SQL server page you need:

    • An account on the database in which to load the server pages.

    • Execution rights to the loadpsp command-line utility, which is located in $ORACLE_HOME/bin.

  • To deploy the server pages you must use mod_plsql. As explained in "Using mod_plsql Gateway to Map Client Requests to a PL/SQL Web Application", the gateway uses the PL/SQL Web Toolkit.

PL/SQL Server Pages and the HTP Package

You can enable browser users to run PL/SQL units through HTTP in these ways:

  • By writing an HTML page with embedded PL/SQL code and compiling it as a PL/SQL server page. You might invoke subprograms from the PL/SQL Web Toolkit, but not to generate every line of HTML output.

  • By writing a complete stored subprogram that produces HTML by invoking the HTP and OWA_* packages in the PL/SQL Web Toolkit. For information about this technique, see "Generating HTML Output with PL/SQL".

Thus, you must choose which technique to use when writing your web application. The key factors in choosing between these techniques are:

  • What source are you using as a starting point?

    • If you have a large body of HTML, and want to include dynamic content or make it the front end of a database application, then use PL/SQL Server Pages.

    • If you have a large body of PL/SQL code that produces formatted output, then you might find it more convenient to produce HTML tags by changing your print statements to invoke the HTP package of the PL/SQL Web Toolkit.

  • What is the fastest and most convenient authoring environment for your group?

    • If most work is done using HTML authoring tools, then use PL/SQL Server Pages.

    • If you use authoring tools that produce PL/SQL code, then it might be less convenient to use PL/SQL Server Pages.

PL/SQL Server Pages and Other Scripting Solutions

Scripting solutions can be client-side or server-side. JavaScript is a very popular client-side scripting languages. PL/SQL Server Pages fully support JavaScript. Because any kind of tags can be passed unchanged to the browser through a PL/SQL server page, you can include JavaScript or other client-side script code in a PL/SQL server page.

Java Server Pages (JSP) and Active Server Pages (ASP) are two of the most popular server-side scripting solutions. Compared to PL/SQL Server Pages:

  • Java server pages are loosely analogous to PL/SQL Server Pages pages; Java servlets are analogous to PL/SQL packages. PL/SQL Server Pages use the same script tag syntax as JSP to make it easy to switch back and forth.

  • PL/SQL Server Pages use syntax that is similar to ASP, although not identical. Typically, you must translate from VBScript or JScript to PL/SQL. The best candidates for migration are pages that use the Active Data Object (ADO) interface to perform database operations.


Note:

You cannot mix PL/SQL server pages with other server-side script features, such as server-side includes. Often, you can get the same results by using the corresponding PL/SQL Server Pages features.

Developing PL/SQL Server Pages

To develop a PL/SQL server page, you can start with an existing web page or with an existing stored subprogram. Either way, with a few additions and changes you can create dynamic web pages that perform database operations and display the results.

The file for a PL/SQL server page must have the extension .psp. It can contain whatever content you choose, with text and tags interspersed with PL/SQL Server Pages directives, declarations, and scriptlets. A server page can take these forms:

  • In the simplest case, it is an HTML file. Compiling it as a PL/SQL server page produces a stored subprogram that outputs the same HTML file.

  • In the most complex case, it is a PL/SQL subprogram that generates all the content of the web page, including the tags for title, body, and headings.

  • In the typical case, it is a mixture of HTML (providing the static parts of the page) and PL/SQL (providing the dynamic content).

The order and placement of the PL/SQL Server Pages directives and declarations is usually not significant. It becomes significant only when another file is included. For ease of maintenance, Oracle recommends that you put the directives and declarations near the beginning of the file.

Table 10-1 lists the PL/SQL Server Pages elements and directs you to the section that explains how to use them. The section "Using Quotation Marks and Escaping Strings in a PSP Script" describes how to use quotation marks in strings that are used in various PL/SQL Server Pages elements.

Table 10-1 PSP Elements

PSP ElementNameSpecifies . . .Section

<%@ page ... %>

Page Directive

Characteristics of the PL/SQL server page.

"Specifying Basic Server Page Characteristics"


<%@ parameter ... %>

Parameter Directive

The name, and optionally the type and default, for each parameter expected by the PSP stored procedure.

"Accepting User Input"


<%@ plsql ... %>

Procedure Directive

The name of the stored procedure produced by the PSP file.

"Naming the PL/SQL Stored Procedure"


<%@ include ... %>

Include Directive

The name of a file to be included at a specific point in the PSP file.

"Including the Contents of Other Files"


<%! ... %>

Declaration Block

The declaration for a set of PL/SQL variables that are visible throughout the page, not just within the next BEGIN/END block.

"Declaring Global Variables in a PSP Script"


<% ... %>

Code Block

A set of PL/SQL statements to be executed when the procedure is run.

"Specifying Executable Statements in a PSP Script"


<%= ... %>

Expression Block

A single PL/SQL expression, such as a string, arithmetic expression, function call, or combination of these.

"Substituting Expression Values in a PSP Script"


<%-- ... --%>

Comment

A comment in a PSP script.

"Including Comments in a PSP Script"




Note:

If you are familiar with dynamic HTML, you can go directly to "Examples of PL/SQL Server Pages".

Topics:

Specifying Basic Server Page Characteristics

Use the <%@ page ... %> directive to specify characteristics of the PL/SQL server page such as:

  • What scripting language it uses.

  • What type of information (MIME type) it produces.

  • What code to run to handle all uncaught exceptions. This might be an HTML file with a friendly message, renamed to a .psp file. You must specify this same file name in the loadpsp command that compiles the main PSP file. You must specify the same name in both the errorPage directive and in the loadpsp command, including any relative path name such as ../include/.

This code shows the syntax of the page directive (the attribute names contentType and errorPage are case-sensitive):

<%@ page
language='PL/SQL'
contentType='content_type_string'
charset='encoding'
errorPage='file.psp'
%>

Topics:

Specifying the Scripting Language

To identify a file as a PL/SQL server page, include this directive somewhere in the file:

<%@ page language="PL/SQL" %>

This directive is for compatibility with other scripting environments. Example 10-1 shows an example of a simple PL/SQL server page that includes the language directive.

Returning Data to the Client Browser

Options:

Returning HTML

The PL/SQL parts of a PL/SQL server page are enclosed within special delimiters. All other content is passed exactly as it is—including any white space—to the browser. To display text or HTML tags, write it as you would write a typical web page. You need not invoke any output functions. As illustration, the server page in Example 10-1 returns the HTML page shown in Example 10-2, except that it includes the table rows for the queried employees.

Example 10-2 Sample Returned HTML Page

<html>
<head>
<meta http-equiv="Content-Type" content="text/html">
<title>List of Employees</title>
</head>
<body TEXT="#000000" BGCOLOR="#FFFFFF">
<h1>List of Employees</h1>
<table width="40%" border="1">
<tr>
<th align="left">Last Name</th>
<th align="left">First Name</th>
</tr>

  <!-- result set of query of hr.employees inserted here -->

</table>
</body>
</html>

Sometimes you might want to display one line of output or another, or change the value of an attribute, based on a condition. You can include control structures and variable substitution inside the PSP delimiters, as shown in this code fragment from Example 10-1:

<%  FOR emp_record IN emp_cursor LOOP %>
  <tr>
  <td> <%= emp_record.last_name %> </td>
  <td> <%= emp_record.first_name %> </td>
  </tr>
<%  END LOOP; %>
Returning XML, Text, and Other Document Types

By default, the PL/SQL gateway transmits files as HTML documents so that the browser interprets the HTML tags. If you want the browser to interpret the document as XML, plain text (with no formatting), or some other document type, then include this directive:

<%@ page contentType="MIMEtype" %>

The attribute name contentType is case-sensitive. Insert text/html, text/xml, text/plain, image/jpeg, or some other MIME type that the browser or other client program recognizes. Users might have to configure their browsers to recognize some MIME types. An example of a directive for an Excel spreadsheet is:

<%@ page contentType="application/vnd.ms-excel" %>

Typically, a PL/SQL server page is intended to be displayed in a web browser. It can also be retrieved and interpreted by a program that can make HTTP requests, such as a a Java or PERL client.

Returning Pages Containing Different Character Sets

By default, the PL/SQL gateway transmits files with the character set defined by the PL/SQL gateway. To convert the data to a different character set for browser display, include this directive:

<%@ page charset="encoding" %>

Specify Shift_JIS, Big5, UTF-8, or another encoding that the client program recognizes.

You must also configure the character set setting in the database accessor descriptor (DAD) of the PL/SQL gateway. Users might have to select the same encoding in their browsers to see the data displayed properly. For example, a database in Japan might have a database character set that uses the EUC encoding, but the web browsers are configured to display Shift_JIS encoding.

Handling Script Errors

When writing PL/SQL server pages, you can get these types of errors:

  • HTML syntax errors

    The browser handles these errors. The loadpsp utility does not check for them.

  • PL/SQL syntax errors

    The loadpsp utility stops and displays the line number, column number, and a brief message. You must fix the error before continuing.

    Any previous version of the stored subprogram can be erased when you attempt to replace it with a script that contains a syntax error. You might want to use one database for prototyping and debugging, and then load the final stored subprogram into a different database for production. You can switch databases using a command-line flag without changing any source code.

  • Runtime errors

    To handle database errors that occur when the script runs, you can include PL/SQL exception-handling code within a PSP file and have any unhandled exceptions start a special PL/SQL server page. Use the errorPage attribute (the name is case-sensitive) of the <%@ page ... %> directive to specify the page name.

    The page for unhandled exceptions is a PL/SQL server page with extension .psp. The error subprogram does not receive any parameters, so to determine the cause of the error, it can invoke the SQLCODE and SQLERRM functions. You can also display a standard HTML page without any scripting when an error occurs, but you must still give it the extension .psp and load it into the database as a stored subprogram.

    This line specifies errors.psp as the page to run when errors are encountered:

    <%@ page language="PL/SQL" contentType="text/html" errorPage="errors.psp" %> 
    

Accepting User Input

To set up parameter passing for a PL/SQL server page, include a directive with this syntax:

<%@ plsql parameter="parameter_name" [type="PL/SQL_type"] [default="value"] %>

The default PL/SQL_type is VARCHAR2. This directive specifies that the parameter p_employee_id is of the type NUMBER:

<%@ plsql parameter="p_employee_id" type="NUMBER" %>

Specifying a default value for a parameter makes the parameter optional. The default value is substituted directly into a PL/SQL statement, so any strings must be enclosed in single quotation marks, and you can use special values such as NULL. This directive specifies that the parameter p_last_name has the default value NULL:

<%@ plsql parameter="p_last_name" default="NULL" %>

User input comes encoded in the URL that retrieves the HTML page. You can generate the URL by hard-coding it in an HTML link, or by invoking your page as the action of an HTML form. Your page receives the input as parameters to a PL/SQL stored subprogram.

Example 10-3 is like Example 10-1, except that it uses a parameter, p_employee_id. If the PL/SQL gateway is configured so that you can run procedures by invoking http://www.host.com/pls/proc_name, where proc_name is the name of a procedure, then you can pass 200 for parameter p_employee_id as follows:

http://www.example.com/pls/show_employees?p_employee_id=200

Example 10-3 simplewithuserinput.psp

<%@ page language="PL/SQL" %>
<%@ page contentType="text/html" %>
<%@ plsql parameter="p_employee_id" default="null" type="NUMBER" %>
<%@ plsql procedure="show_employees" %>
<%-- This example displays the last name and first name of every 
     employee in the hr.employees table. --%>
<%!
  CURSOR emp_cursor IS
  SELECT last_name, first_name
  FROM hr.employees
  WHERE employee_id = p_employee_id
  ORDER BY last_name;
%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html">
<title>List of Employees</title>
</head>
<body TEXT="#000000" BGCOLOR="#FFFFFF">
<h1>List of Employees</h1>
<table width="40%" border="1">
<tr>
<th align="left">Last Name</th>
<th align="left">First Name</th>
</tr>
<%  FOR emp_record IN emp_cursor LOOP %>
  <tr>
  <td> <%= emp_record.last_name %> </td>
  <td> <%= emp_record.first_name %> </td>
  </tr>
<%  END LOOP; %>
</table>
</body>
</html>

Naming the PL/SQL Stored Procedure

Each top-level PL/SQL server page corresponds to a stored procedure within the server. When you load the page with loadpsp, the utility creates a PL/SQL stored procedure. If the server page is name.psp, the default procedure name is name. For example, if the server page is hello_world.psp, then the default procedure name is hello_world.

To specify a procedure name, use this directive, where procname is the name for the procedure:

<%@ plsql procedure="procname" %>

In Example 10-1, this directive gives the stored procedure the name show_employees:

<%@ plsql procedure="show_employees" %>

It is the name of the procedure, not the name of the PSP script, that you include in the URL.

Including the Contents of Other Files

You can set up an include mechanism to pull in the contents of other files, typically containing either static HTML content or more PL/SQL scripting code. Insert this directive at the point where the content of the other file is to appear, replacing filename with the name of the file to be included:

<%@ include file="filename" %>

The included file must have an extension other than .psp. You must specify the same name in both the include directive and in the loadpsp command, including any relative path name such as ../include/.

Because the files are processed when you load the stored procedure into the database, the substitution is performed only once, not whenever the page is served. Therefore, changes to the included files that occur after the page is loaded into the database are not displayed when the procedure is executed.

You can use the include feature to pull in libraries of code, such as a navigation banners, footers, tables of contents, and so forth into multiple files. Alternatively, you can use this feature as a macro capability to include the same section of script code in multiple places in a page. This example includes an HTML footer:

<%@ include file="footer.htm" %>

When you use included files:

  • You can use any names and extensions for the included files. For example, you can include a file called products.txt.

  • If the included files contain PL/SQL scripting code, then they do not need their own set of directives to identify the procedure name, character set, and so on.

  • When specifying the names of files to the loadpsp utility, you must include the names of all included files also. Specify the names of included files before the names of any .psp files.

Declaring Global Variables in a PSP Script

You can use the <%! ... %> directive to define a set of PL/SQL variables that are visible throughout the page, not just within the next BEGIN/END block. This element typically spans multiple lines, with individual PL/SQL variable declarations ended by semicolons. The syntax for this directive is as follows:

<%! PL/SQL declaration;
    [ PL/SQL declaration; ] ... %>

The usual PL/SQL syntax is allowed within the block. The delimiters server as shorthand, enabling you to omit the DECLARE keyword. All declarations are available to the code later in the file. Example 10-1 includes this cursor declaration:

<%!
  CURSOR emp_cursor IS
  SELECT last_name, first_name
  FROM hr.employees
  ORDER BY last_name;
%>

You can specify multiple declaration blocks; internally, they are all merged into a single block when the PSP file is created as a stored procedure.

You can also use explicit DECLARE blocks within the <% ... %> delimiters that are explained in "Specifying Executable Statements in a PSP Script". These declarations are only visible to the BEGIN/END block that follows them.


Note:

To make things easier to maintain, keep all your directives and declarations near the beginning of a PL/SQL server page.

Specifying Executable Statements in a PSP Script

You can use the <% ... %> code block directive to run a set of PL/SQL statements when the stored procedure is run. This code shows the syntax for executable statements:

<% PL/SQL statement;
   [ PL/SQL statement; ] ... %>

This element typically spans multiple lines, with individual PL/SQL statements ended by semicolons. The statements can include complete blocks, as in this example, which invokes the OWA_UTIL.TABLEPRINT procedure:

<% OWA_UTIL.TABLEPRINT(CTABLE => 'hr.employees', CATTRIBUTES => 'border=2', 
   CCOLUMNS => 'last_name,first_name', CCLAUSES => 'WHERE employee_id > 100'); %>

The statements can also be the bracketing parts of IF/THEN/ELSE or BEGIN/END blocks. When a code block is split into multiple directives, you can put HTML or other directives in the middle, and the middle pieces are conditionally executed when the stored procedure is run. This code from Example 10-11 provides an illustration of this technique:

  <% FOR ITEM IN (SELECT product_name, list_price, catalog_url 
                  FROM product_information
                  WHERE list_price IS NOT NULL
                  ORDER BY list_price DESC) LOOP
     IF item.list_price > p_minprice THEN
        v_color := '#CCCCFF';
     ELSE
        v_color := '#CCCCCC';
     END IF;
  %>
  <TR BGCOLOR="<%= v_color %>">
    <TD><A HREF="<%= item.catalog_url %>"><%= item.product_name %></A></TD>
    <TD><BIG><%= item.list_price %></BIG></TD>
  </TR>
  <% END LOOP; %>

All the usual PL/SQL syntax is allowed within the block. The delimiters server as shorthand, letting you omit the DECLARE keyword. All the declarations are available to the code later on in the file.


Note:

To share procedures, constants, and types across different PL/SQL server pages, compile them into a package in the database by using a plain PL/SQL source file. Although you can reference package procedures, constants, and types from PSP scripts, the PSP scripts can only produce standalone procedures, not packages.

Substituting Expression Values in a PSP Script

An expression directive outputs a single PL/SQL expression, such as a string, arithmetic expression, function call, or combination of these things. The result is substituted as a string at that spot in the HTML page that is produced by the stored procedure. The expression result must be a string value or be able to be cast to a string. For any types that cannot be implicitly cast, such as DATE, pass the value to the PL/SQL TO_CHAR function.

The syntax of an expression directive is as follows, where the expression placeholder is replaced by the desired expression:

<%= expression %>

You need not end the PL/SQL expression with a semicolon.

Example 10-1 includes a directive to print the value of a variable in a row of a cursor:

<%= emp_record.last_name %>

Compare the preceding example to the equivalent htp.print call in this example (note especially the semicolon that ends the statement):

<% HTP.PRN (emp_record.last_name); %>

The content within the <%= ... %> delimiters is processed by the HTP.PRN function, which trims leading or trailing white space and requires that you enclose literal strings in single quotation marks.

You can use concatenation by using the twin pipe symbol (||) as in PL/SQL. This directive shows an example of concatenation:

<%= 'The employee last name is ' || emp_record.last_name %>

Using Quotation Marks and Escaping Strings in a PSP Script

PSP attributes use double quotation marks to delimit data. When values specified in PSP attributes are used for PL/SQL operations, they are passed exactly as you specify them in the PSP file. Thus, if PL/SQL requires a string enclosed in single quotation marks, then you must specify the string enclosed in single quotation marks, and enclose the whole thing in double quotation marks.

For example, your PL/SQL procedure might use the string Babe Ruth as the default value for a variable. For the string to be used in PL/SQL, you must enclose it in single quotation marks as 'Babe Ruth'. If you specify this string in the default attribute of a PSP directive, you must enclose it in double quotation marks, like this:

<%@ plsql parameter="in_players" default="'Babe Ruth'" %>

You can also enclose strings that are enclosed in single quotation marks in another set of single quotation marks. In this case, you must escape the inner single quotation marks by specifying the sequence \'. For example:

<%@ plsql parameter="in_players" default="'Walter \'Big Train\' Johnson'" %>

You can include most characters and character sequences in a PSP file without having them changed by the PSP loader. To include the sequence %>, specify the escape sequence %\>. To include the sequence <%, specify the escape sequence <\%. For example:

<%= 'The %\> sequence is used in scripting language: ' || lang_name %>
<%= 'The <\% sequence is used in scripting language: ' || lang_name %>

Including Comments in a PSP Script

To put a comment in the HTML portion of a PL/SQL server page for the benefit of those reading the PSP source code, use this syntax:

<%-- PSP comment text --%>

Comments in the preceding form do not appear in the HTML output from the PSP and also do not appear when you query the PL/SQL source code in USER_OBJECTS.

To create a comment that is visible in the HTML output and in the USER_OBJECTS source, place the comment in the HTML and use the normal HTML comment syntax:

<!-- HTML comment text -->

To include a comment inside a PL/SQL block within a PSP, and to make the comment invisible in the HTML output but visible in USER_OBJECTS, use the normal PL/SQL comment syntax, as in this example:

-- Comment in PL/SQL code

Example 10-4 shows a fragment of a PSP file with the three types of comments.

Example 10-4 Sample Comments in a PSP File

<p>Today we introduce our new model XP-10.
<%--
  This is the project with code name "Secret Project".
  Users viewing the HTML page do not see this PSP script comment.
  The comment is not visible in the USER_OBJECTS source code.
--%>
<!--
  Some pictures of the XP-10.
  Users viewing the HTML page source see this comment.
  The comment is also visible in the USER_OBJECTS source code.
-->
<%
FOR image_file IN (SELECT pathname, width, height, description
                   FROM image_library WHERE model_num = 'XP-10')
-- Comments interspersed with PL/SQL statements.
-- Users viewing the HTML page source do not see these PL/SQL comments.
-- These comments are visible in the USER_OBJECTS source code.
LOOP
%>
<img src="<%= image_file.pathname %>" width=<% image_file.width %>
height=<% image_file.height %> alt="<% image_file.description %>">
<br>
<% END LOOP; %>

Loading PL/SQL Server Pages into the Database

Use the loadpsp utility, which is located in $ORACLE_HOME/bin, to load one or more PSP files into the database as stored procedures. Each .psp file corresponds to one stored procedure. The pages are compiled and loaded in one step, to speed up the development cycle. The syntax of the loadpsp utility is:

loadpsp [-replace] [include_file_name...] [error_file_name] psp_file_name...
Enter Password: password

When you load a PSP file, the loader performs these actions:

  1. Logs on to the database with the specified user name, password, and net service name

  2. Creates the stored procedures in the user schema

-replace creates procedures with CREATE OR REPLACE syntax.

include_file_name is the name of a file that is specified in the PSP include directive.

error_file_name is the name of the file that is specified in the errorPage attribute of the PSP page directive.

psp_file_name is the name of a file that is specified in a PSP page directive.

The filenames on the loadpsp command line must exactly match the names specified in the PSP include and page directives, including any relative path name such as ../include/.

Example 10-5 shows a sample PSP load command.

Example 10-5 Loading PL/SQL Server Pages

loadpsp -replace -user joe/abc123@/db3 banner.inc error.psp display_order.psp

In Example 10-5:

  • The stored procedure is created in the database db3. The database is accessed as user joe with password abc123, both to create the stored procedure and when the stored procedure is executed.

  • banner.inc is a file containing boilerplate text and script code that is included by the .psp file. The inclusion occurs when the PSP is loaded into the database, not when the stored procedure is executed.

  • error.psp is a file containing code, text, or both that is processed when an unhandled exception occurs, to present a friendly page rather than an internal error message.

  • display_order.psp contains the main code and text for the web page. By default, the corresponding stored procedure is named display_order.

Querying PL/SQL Server Page Source Code

The code that loadpsp generates is different from the code in the source file. It has calls to the HTP package, which generates the HTML tags for the web page.

After loading a PSP file, you can see the generated source code by querying the static data dictionary views *_SOURCE. For example, suppose that you load the script in Example 10-1 with this command:

loadpsp -replace -user hr simple.psp
Enter Password: password

If you log on to the database as user hr, you can view the source code of the PSP as shown in Example 10-6.

Example 10-6 Querying PL/SQL Server Page Source Code

Query:

SELECT TEXT
FROM USER_SOURCE
WHERE NAME = 'SHOW_EMPLOYEES'
ORDER BY LINE;
 

Result:

PROCEDURE show_employees  AS
 
  CURSOR emp_cursor IS
  SELECT last_name, first_name
  FROM hr.employees
  ORDER BY last_name;
 
 BEGIN NULL;
owa_util.mime_header('text/html'); htp.prn('
');
htp.prn('
');
htp.prn('
');
htp.prn('
');
htp.prn('
');
htp.prn('
<html>
<head>
<meta http-equiv="Content-Type" content="text/html">
<title>List of Employees</title>
</head>
<body TEXT="#000000" BGCOLOR="#FFFFFF">
<h1>List of Employees</h1>
<table width="40%" border="1">
<tr>
<th align="left">Last Name</th>
<th align="left">First Name</th>
</tr>
');
  FOR emp_record IN emp_cursor LOOP
htp.prn('
  <tr>
  <td> ');
htp.prn( emp_record.last_name );
htp.prn(' </td>
  <td> ');
htp.prn( emp_record.first_name );
htp.prn(' </td>
  </tr>
');
  END LOOP;
htp.prn('
</table>
</body>
</html>
');
 END;

Running PL/SQL Server Pages Through URLs

After the PL/SQL server page is turned into a stored procedure, you can run the procedure by retrieving an HTTP URL through a web browser or other Internet-aware client program. The virtual path in the URL depends on the way the PL/SQL gateway is configured.

The parameters to the stored procedure are passed through either the POST method or the GET method of the HTTP protocol. With the POST method, the parameters are passed directly from an HTML form and are not visible in the URL. With the GET method, the parameters are passed as name-value pairs in the query string of the URL, separated by & characters, with most nonalphanumeric characters in encoded format (such as %20 for a space). You can use the GET method to invoke a PSP page from an HTML form, or you can use a hard-coded HTML link to invoke the stored procedure with a given set of parameters.

Using METHOD=GET, the syntax of the URL looks something like this:

http://sitename/schemaname/procname?parmname1=value1&parmname2=value2

For example, this URL includes a p_lname and p_fname parameter:

http://www.example.com/pls/show_employees?p_lname=Ashdown&p_fname=Lance

Using METHOD=POST, the syntax of the URL does not show the parameters:

http://sitename/schemaname/procname

For example, this URL specifies a procedure name but does not pass parameters:

http://www.example.com/pls/show_employees

The METHOD=GET format is more convenient for debugging and allows visitors to pass the same parameters when they return to the page through a bookmark.

The METHOD=POST format allows a larger volume of parameter data, and is suitable for passing sensitive information that must not be displayed in the URL. (URLs linger on in the browser's history list and in the HTTP headers that are passed to the next-visited page.) It is not practical to bookmark pages that are invoked this way.

Examples of PL/SQL Server Pages

This section shows how you might start with a very simple PL/SQL server page, and produce progressively more complicated versions as you gain more confidence.

As you go through each step, you can follow the instructions in "Loading PL/SQL Server Pages into the Database" and "Running PL/SQL Server Pages Through URLs" to test the examples.

Topics:

Setup for PL/SQL Server Pages Examples

These examples use the PRODUCT_INFORMATION table in the OE schema, which is described as follows:

SQL*Plus command:

DESCRIBE PRODUCT_INFORMATION;

Result:

 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 PRODUCT_ID                                NOT NULL NUMBER(6)
 PRODUCT_NAME                                       VARCHAR2(50)
 PRODUCT_DESCRIPTION                                VARCHAR2(2000)
 CATEGORY_ID                                        NUMBER(2)
 WEIGHT_CLASS                                       NUMBER(1)
 WARRANTY_PERIOD                                    INTERVAL YEAR(2) TO MONTH
 SUPPLIER_ID                                        NUMBER(6)
 PRODUCT_STATUS                                     VARCHAR2(20)
 LIST_PRICE                                         NUMBER(8,2)
 MIN_PRICE                                          NUMBER(8,2)
 CATALOG_URL                                        VARCHAR2(50)

The examples assume:

For debugging purposes, you can display the complete contents of a SQL table with a call to OWA_UTIL.TABLEPRINT, as in Example 10-7. Later examples show other techniques that give more control over the presentation.

Example 10-7 show_prod_simple.psp

<%@ plsql procedure="show_prod_simple" %>
<HTML>
<HEAD><TITLE>Show Contents of product_information (Complete Dump)</TITLE></HEAD>
<BODY>
<% 
DECLARE
  dummy BOOLEAN;
BEGIN
  dummy := OWA_UTIL.TABLEPRINT('oe.product_information','border');
END;
%>
</BODY>
</HTML>

Load the PSP in Example 10-7 at the command line as follows:

loadpsp -replace -user oe/password show_prod_simple.psp
Enter Password: password

Access the PSP through this URL:

http://www.example.com/pls/show_prod_simple

Printing the Sample Table with a Loop

Example 10-7 loops through the items in the product_information table and adjusts the SELECT statement to retrieve only a subset of the rows or columns. This example uses a very simple presentation, a set of list items, to avoid any problems from mismatched or unclosed table tags.

Example 10-8 show_catalog_raw.psp

<%@ plsql procedure="show_prod_raw" %>
<HTML>
<HEAD><TITLE>Show Products (Raw Form)</TITLE></HEAD>
<BODY>
<UL>
<% FOR item IN (SELECT product_name, list_price, catalog_url
                FROM product_information
                WHERE list_price IS NOT NULL
                ORDER BY list_price DESC) LOOP %>
<LI>
Item = <%= item.product_name %><BR>
Price = <%= item.list_price %><BR>
URL = <%= item.catalog_url %><BR>
<% END LOOP; %>
</UL>
</BODY>
</HTML>

Example 10-9 shows a more sophisticated variation of Example 10-8 in which formatting is added to the HTML to improve the presentation.

Example 10-9 show_catalog_pretty.psp

<%@ plsql procedure="show_prod_pretty" %>
<HTML>
<HEAD><TITLE>Show Products (Better Form)</TITLE></HEAD>
<BODY>
<UL>
<% FOR item IN (SELECT product_name, list_price, catalog_url
                FROM product_information
                WHERE list_price IS NOT NULL
                ORDER BY list_price DESC) LOOP %>
<LI>
Item = <A HREF=<%= item.catalog_url %>><%= item.product_name %></A><BR>
Price = <BIG><%= item.list_price %></BIG><BR>
<% END LOOP; %>
</UL>
</BODY>
</HTML>

Allowing a User Selection

In Example 10-7, Example 10-8, and Example 10-9, the HTML page remains the same unless the PRODUCT_INFORMATION table is updated. Example 10-10:

  • Makes the HTML page accept a minimum price, and presents only the items that are more expensive. (Your customers' buying criteria might vary.)

  • Sets the default minimum price to 100 units of the appropriate currency.

Example 10-10 show_product_partial.psp

<%@ plsql procedure="show_product_partial" %>
<%@ plsql parameter="p_minprice" default="100" %>
<HTML>
<HEAD><TITLE>Show Items Greater Than Specified Price</TITLE></HEAD>
<BODY>
<P>This report shows the items whose price is greater than <%= p_minprice %>.
<UL>
<% FOR ITEM IN (SELECT product_name, list_price, catalog_url
                FROM product_information
                WHERE list_price > p_minprice 
                ORDER BY list_price DESC)
   LOOP %>
<LI>
Item = <A HREF="<%= item.catalog_url %>"><%= item.product_name %></A><BR>
Price = <BIG><%= item.list_price %></BIG><BR>
<% END LOOP; %>
</UL>
</BODY>
</HTML>

After loading Example 10-10 into the database, you can pass a parameter to the show_product_partial procedure through a URL. This example specifies a minimum price of 250:

http://www.example.com/pls/show_product_partial?p_minprice=250

Filtering results is appropriate for applications such as search results, where users might be overwhelmed by choices. But in a retail situation, you might want to use the alternative technique illustrated in Example 10-11, so that customers can still choose to purchase other items:

  • Instead of filtering the results through a WHERE clause, retrieve the entire result set and then take different actions for different returned rows.

  • Change the HTML to highlight the output that meets their criteria. Example 10-11 uses the background color for an HTML table row. You can also insert a special icon, increase the font size, or use another technique to call attention to the most important rows.

  • Present the results in an HTML table.

Example 10-11 show_product_highlighed.psp

<%@ plsql procedure="show_product_highlighted" %>
<%@ plsql parameter="p_minprice" default="100" %>
<%! v_color VARCHAR2(7); %>

<HTML>
<HEAD><TITLE>Show Items Greater Than Specified Price</TITLE></HEAD>
<BODY>
<P>This report shows all items, highlighting those whose price is
 greater than <%= p_minprice %>.
<P>
<TABLE BORDER>
  <TR>
    <TH>Product</TH>
    <TH>Price</TH>
  </TR>
  <% FOR ITEM IN (SELECT product_name, list_price, catalog_url 
                  FROM product_information
                  WHERE list_price IS NOT NULL
                  ORDER BY list_price DESC) LOOP
     IF item.list_price > p_minprice THEN
        v_color := '#CCCCFF';
     ELSE
        v_color := '#CCCCCC';
     END IF;
  %>
  <TR BGCOLOR="<%= v_color %>">
    <TD><A HREF="<%= item.catalog_url %>"><%= item.product_name %></A></TD>
    <TD><BIG><%= item.list_price %></BIG></TD>
  </TR>
  <% END LOOP; %>
</TABLE>
</BODY>
</HTML>

Using an HTML Form to Invoke a PL/SQL Server Page

Example 10-12 shows a bare-bones HTT-ML form that allows the user to enter a price. The form invokes the show_product_partial stored procedure illustrated in Example 10-10 and passes it the entered value as the p_minprice parameter.

To avoid coding the entire URL of the stored procedure in the ACTION= attribute of the form, you can make the form a PSP file so that it resides in the same directory as the PSP file that it invokes. Even though this HTML file contains no PL/SQL code, you can give it a .psp extension and load it as a stored procedure into the database. When the product_form stored procedure is executed through a URL, it displays the HTML exactly as it appears in the file.

Example 10-12 product_form.psp

<HTML>
<BODY>
<FORM method="POST" action="show_product_partial">
  <P>Enter the minimum price you want to pay:
  <INPUT type="text" name="p_minprice">
  <INPUT type="submit" value="Submit">
</FORM>
</BODY>
</HTML>

Including JavaScript in a PSP File

To produce an elaborate HTML file, perhaps including dynamic content such as JavaScript, you can simplify the source code by implementing it as a PSP. This technique avoids having to deal with nested quotation marks, escape characters, concatenated literals and variables, and indentation of the embedded content.

Example 10-13 shows a version of Example 10-10 that uses JavaScript to display the order status in the browser status bar when the user moves his or her mouse over the product URL.

Example 10-13 show_product_javascript.psp

<%@ plsql procedure="show_product_javascript" %>
<%@ plsql parameter="p_minprice" default="100" %>
<HTML>
<HEAD>
  <TITLE>Show Items Greater Than Specified Price</TITLE>
 
<SCRIPT language="JavaScript">
<!--hide
 
var text=" ";
 
function overlink (text)
{
  window.status=text;
}
function offlink (text)
{
  window.status=text;
}
 
//-->
</SCRIPT>
 
</HEAD>
<BODY>
<P>This report shows the items whose price is greater than <%= p_minprice %>.
<P>
<UL>
<% FOR ITEM IN (SELECT product_name, list_price, catalog_url, product_status
                FROM product_information
                WHERE list_price > p_minprice 
                ORDER BY list_price DESC)
   LOOP %>
<LI>
Item = 
  <A HREF="<%= item.catalog_url %>" 
  onMouseover="overlink('PRODUCT STATUS: <%= item.product_status %>');return true"
  onMouseout="offlink(' ');return true">
    <%= item.product_name %>
  </A>
<BR>
Price = <BIG><%= item.list_price %></BIG><BR>
<% END LOOP; %>
</UL>
</BODY>
</HTML>

Debugging PL/SQL Server Pages

As you begin experimenting with PL/SQL Server Pages, and as you adapt your first simple pages into more elaborate ones, keep these guidelines in mind when you encounter problems:

  • The first step is to get all the PL/SQL syntax and PSP directive syntax right. If you make a mistake here, the file does not compile.

    • Use semicolons to terminate lines where required.

    • If a value must be quoted, quote it. You might need to enclose a value in single quotation marks (which PL/SQL needs) inside double quotation marks (which PSP needs).

    • Mistakes in the PSP directives are usually reported through PL/SQL syntax messages. Check that your directives use the right syntax, that directives are closed properly, and that you are using the right element (declaration, expression, or code block) depending on what goes inside it.

    • PSP attribute names are case-sensitive. Most are specified in all lowercase; contentType and errorPage must be specified as mixed-case.

  • When using a URL to request a PSP, you might get an error that the file is not found. In this case:

    • Be sure you are requesting the right virtual path, depending on the way the web gateway is configured. Typically, the path includes the host name, optionally a port number, the schema name, and the name of the stored procedure (with no .psp extension).

    • If you use the -replace option when compiling the file, the old version of the stored procedure is erased. So, after a failed compilation, you must fix the error or the page is not available. You might want to test scripts in a separate schema, then load them into the production schema.

    • If you copied the file from another file, remember to change any procedure name directives in the source to match the correct file name.

    • When you get one file-not-found error, request the latest version of the page the next time. The error page might be cached by the browser. You might need to force a page reload in the browser to bypass the cache.

  • When the PSP script is run, and the results come back to the browser, use standard debugging techniques to check for and correct wrong output. The difficult part is to configure the interface between different HTML forms, scripts, and CGI programs so that the right values are passed into your page. The page might return an error because of a parameter mismatch.

    Guidelines:

    • To determine exactly what is being passed to your page, use METHOD=GET in the invoking form so that the parameters are visible in the URL.

    • Ensure that the form or CGI program that invokes your page passes the correct number of parameters, and that the names specified by the NAME= attributes on the form match the parameter names in the PSP file. If the form includes any hidden input fields, or uses the NAME= attribute on the Submit or Reset buttons, then the PSP file must declare equivalent parameters.

    • Ensure that the parameters can be cast from string into the correct PL/SQL types. For example, do not include alphabetic characters if the parameter in the PSP file is declared as a NUMBER.

    • Ensure that the query string of the URL consists of name-value pairs, separated by equals signs, especially if you are passing parameters by constructing a hard-coded link to the page.

    • If you are passing a lot of parameter data, such as large strings, you might exceed the volume that can be passed with METHOD=GET. You can switch to METHOD=POST in the invoking form without changing your PSP file.

    • Although the loadpsp command reports line numbers correctly when there is a syntax error in your source file, line numbers reported for runtime errors refer to a transformed version of the source and do not match the line numbers in the original source. When you encounter errors that produce an error trace instead of the expected web page, you must locate the error through exception handlers and by printing debug output.

Putting PL/SQL Server Pages into Production

Before putting your PSP application into production, consider issues such as usability and download speed:

  • Pages can be rendered faster in the browser if the HEIGHT= and WIDTH= attributes are specified for all images. You might standardize on picture sizes, or store the height and width of images in the database along with the data or URL.

  • For viewers who turn off graphics, or who use alternative browsers that read the text out loud, include a description of significant images using the ALT= attribute. You might store the description in the database along with the image.

  • Although an HTML table provides a good way to display data, a large table can make your application seem slow. Often, the reader sees a blank page until the entire table is downloaded. If the amount of data in an HTML table is large, consider splitting the output into multiple tables.

  • If you set text, font, or background colors, test your application with different combinations of browser color settings:

    • Test what happens if you override just the foreground color in the browser, or just the background color, or both.

    • If you set one color (such as the foreground text color), set all the colors through the <BODY> tag, to avoid hard-to-read combinations like white text on a white background.

    • If you use a background image, specify a similar background color to provide proper contrast for viewers who do not load graphics.

    • If the information conveyed by different colors is crucial, consider using an alternative technique. For example, you might put an icon next to special items in a table. Some users might see your page on a monochrome screen or on browsers that cannot represent different colors.

  • Providing context information prevents users from getting lost. Include a descriptive <TITLE> tag for your page. If the user is partway through a procedure, indicate which step is represented by your page. Provide links to logical points to continue with the procedure, return to a previous step, or cancel the procedure completely. Many pages might use a standard set of links that you embed using the include directive.

  • In any entry fields, users might enter incorrect values. Where possible, use SELECT lists to present a set of choices. Validate any text entered in a field before passing it to SQL. The earlier you can validate, the better; a JavaScript function can detect incorrect data and prompt the user to correct it before they press the Submit button and call the database.

  • Browsers tend to be lenient when displaying incorrect HTML. What looks OK in one browser might look bad or might not display at all in another browser.

    Guidelines:

    • Pay attention to HTML rules for quotation marks, closing tags, and especially for anything to do with tables.

    • Minimize the dependence on tags that are only supported by a single browser. Sometimes you can provide an extra bonus using such tags, but your application must still be usable with other browsers.

    • You can check the validity, and even in some cases the usability, of your HTML for free at many sites on the World Wide Web.

PK4c-T-PK|%AOEBPS/adfns_part_special.htm B Advanced Topics for Application Developers PK PK|%AOEBPS/adfns_part_plsql.htm` PL/SQL for Application Developers

Part II

PL/SQL for Application Developers

This part presents information that application developers need about PL/SQL, the Oracle procedural extension of SQL.

Chapters:


See Also:

Oracle Database PL/SQL Language Reference for a complete description of PL/SQL

PKBe ` PK|%AOEBPS/adfns_externproc.htm Developing Applications with Multiple Programming Languages
PK\N@y@PK|%A OEBPS/lot.htm!\ List of Tables

List of Tables

PK6!!PK|%AOEBPS/adfns_part_sql.htma SQL for Application Developers

Part I

SQL for Application Developers

This part presents information that application developers need about Structured Query Language (SQL), which is used to manage information in an Oracle Database.

Chapters:


See Also:

Oracle Database SQL Language Reference for a complete description of SQL

PK+f a PK |%Aoa,mimetypePK|%A:@a\:iTunesMetadata.plistPK|%AYuMETA-INF/container.xmlPK|%A*~OEBPS/adfns_dependencies.htmPK|%A[pTOOEBPS/cover.htmPK|%AhCZ~U~kOEBPS/whatsnew.htmPK|%A7cmmOEBPS/adfns_constraints.htmPK|%A GOEBPS/title.htmPK|%Awۇ&T!TOEBPS/adfns_publish.htmPK|%A33.3 ZOEBPS/loe.htmPK|%A /iOEBPS/adfns_xa.htmPK|%Ae,s_4OEBPS/adfns_environments.htmPK|%AyOEBPS/adfns_regexp.htmPK|%Ada:!5!OEBPS/preface.htmPK|%AE885OEBPS/adfns_packages.htmPK|%Ajtt`o< OEBPS/index.htmPK|%AGnZ  OEBPS/adfns_idcode.htmPK|%AlKeeҖ OEBPS/img/adfns062.gifPK|%AX}RR OEBPS/img/adfns059.gifPK|%A ӿCCOOEBPS/img/adfns090.gifPK|%AvCCOEBPS/img/adfns057.gifPK|%AW R )OEBPS/img/adfns108.gifPK|%AԦx99OEBPS/img/adfns079.gifPK|%AU>RROEBPS/img/adfns092.gifPK|%A5gVGQGqOEBPS/img/adfns091.gifPK|%A:Ng g8OEBPS/img/adfns039.gifPK|%A7cA7 OEBPS/img/adfns056.gifPK|%A1wFFOEBPS/img/adfns089.gifPK|%AtHOCOA4OEBPS/img/adfns104.gifPK|%A̓OEBPS/img/adfns053.gifPK|%Ad=_=cOEBPS/img/adfns109.gifPK|%Ae~GGOEBPS/img/adfns054.gifPK|%ARROEBPS/img/adfns055.gifPK|%AfUR7M7;OEBPS/img/adfns065.gifPK|%AgeyptprOEBPS/img/adfns040.gifPK|%Aa WWqOEBPS/img/adfns060.gifPK|%A앣k<{<;OEBPS/img/adfns022.gifPK|%AjRRTxOEBPS/img/adfns100.gifPK|%AѸRPPOEBPS/img/adfns063.gifPK|%A VSROEBPS/img/adfns058.gifPK|%AE) )5oOEBPS/img/adfns102.gifPK|%AgTV|VOEBPS/img/adfns101.gifPK|%AU/IdDdLOEBPS/adfns_indexes.htmPK|%A3ͲSOEBPS/adfns_web.htmPK|%AҰt_[_OEBPS/adfns_sqlproc.htmPK|%A~fOEBPS/img_text/adfns109.htmPK|%Ay`kOEBPS/img_text/adfns058.htmPK|%A,.!pOEBPS/img_text/adfns065.htmPK|%A!:5uOEBPS/img_text/adfns062.htmPK|%A* b]{OEBPS/img_text/adfns057.htmPK|%A4JEKOEBPS/img_text/adfns104.htmPK|%AiUރOEBPS/img_text/adfns059.htmPK|%AOEBPS/img_text/adfns091.htmPK|%Aډ_ZOEBPS/img_text/adfns101.htmPK|%An5*%OEBPS/img_text/adfns056.htmPK|%A| !OEBPS/img_text/adfns063.htmPK|%A_OEBPS/img_text/adfns079.htmPK|%A,ʣOEBPS/img_text/adfns089.htmPK|%A<~ytOEBPS/img_text/adfns053.htmPK|%A7I%83OEBPS/img_text/adfns054.htmPK|%Ahb|BOEBPS/img_text/adfns060.htmPK|%A^֫OEBPS/img_text/adfns090.htmPK|%A" .)OEBPS/img_text/adfns100.htmPK|%A{+ OEBPS/img_text/adfns040.htmPK|%As9 hOEBPS/img_text/adfns102.htmPK|%AډOEBPS/img_text/adfns039.htmPK|%A3OEBPS/img_text/adfns092.htmPK|%AUgRMOEBPS/img_text/adfns108.htmPK|%A'JHCOEBPS/img_text/adfns022.htmPK|%Ay=OEBPS/img_text/adfns055.htmPK|%A"*uOEBPS/adfns_extproc.htmPK|%AҸ .hOEBPS/adfns_flashback.htmPK|%Anܴ OEBPS/toc.ncxPK|%A((OEBPS/adfns_profiler.htmPK|%A6PPBOEBPS/adfns_editions.htmPK|%ANIyes!OEBPS/adfns_cqn.htmPK|%Ai<d<-J#OEBPS/content.opfPK|%ATYGՆ#OEBPS/adfns_sqltypes.htmPK|%AEk lj%OEBPS/lof.htmPK|%A_ %OEBPS/dcommon/prodbig.gifPK|%AY@ G%OEBPS/dcommon/doclib.gifPK|%Aόxx%OEBPS/dcommon/oracle-logo.jpgPK|%As&OEBPS/dcommon/contbig.gifPK|%A^&OEBPS/dcommon/darbbook.cssPK|%AMά""!&OEBPS/dcommon/O_signature_clr.JPGPK|%APz A&OEBPS/dcommon/feedbck2.gifPK|%A-.C&OEBPS/dcommon/feedback.gifPK|%Aː5CJ&OEBPS/dcommon/booklist.gifPK|%AN61K&OEBPS/dcommon/cpyr.htmPK|%A!:3. ^&OEBPS/dcommon/masterix.gifPK|%AeӺ1,_&OEBPS/dcommon/doccd.cssPK|%A7 b&OEBPS/dcommon/larrow.gifPK|%A#8d&OEBPS/dcommon/indxicon.gifPK|%AS'"f&OEBPS/dcommon/leftnav.gifPK|%Ahu,h&OEBPS/dcommon/uarrow.gifPK|%Al-OJ,k&OEBPS/dcommon/oracle.gifPK|%A(s&OEBPS/dcommon/index.gifPK|%AGC u&OEBPS/dcommon/bookbig.gifPK|%AJV^'&OEBPS/dcommon/rarrow.gifPK|%A枰pkB&OEBPS/dcommon/mix.gifPK|%Ao"nR M &OEBPS/dcommon/doccd_epub.jsPK|%Av I &OEBPS/dcommon/toc.gifPK|%A r~$ݏ&OEBPS/dcommon/topnav.gifPK|%A1FAG&OEBPS/dcommon/prodicon.gifPK|%A3( # Ք&OEBPS/dcommon/bp_layout.cssPK|%Ax[?:F&OEBPS/dcommon/bookicon.gifPK|%Ap*c^ͧ&OEBPS/dcommon/conticon.gifPK|%Aʍx&OEBPS/dcommon/blafdoc.cssPK|%A+&w&OEBPS/dcommon/rightnav.gifPK|%Aje88&OEBPS/dcommon/oracle-small.JPGPK|%Aއ{&!+&OEBPS/dcommon/help.gifPK|%A" &OEBPS/toc.htmPK|%A{+٧ϧ}(OEBPS/adfns_plscope.htmPK|%A4c-T-(OEBPS/adfns_psp.htmPK|%A ?)OEBPS/adfns_part_special.htmPK|%ABe ` K)OEBPS/adfns_part_plsql.htmPK|%A\N@y@*OEBPS/adfns_externproc.htmPK|%A6!! C,OEBPS/lot.htmPK|%A+f a e,OEBPS/adfns_part_sql.htmPKwwD ap,

14 Developing Applications with Multiple Programming Languages

This chapter explains how you can develop database applications that call external procedures written in other programming languages.

Topics:

Overview of Multilanguage Programs

Oracle Database lets you work in different languages:

How can you choose between these different implementation possibilities? Each of these languages offers different advantages: ease of use, the availability of programmers with specific expertise, the need for portability, and the existence of legacy code are powerful determinants.

The choice might narrow depending on how your application must work with Oracle Database:

  • PL/SQL is a powerful development tool, specialized for SQL transaction processing.

  • Some computation-intensive tasks are executed most efficiently in a lower level language, such as C.

  • For both portability and security, you might select Java.

Most significantly for performance, only PL/SQL and Java methods run within the address space of the server. C/C++ methods are dispatched as external procedures, and run on the server system but outside the address space of the database server. Pro*COBOL and Pro*C/C++ are precompilers, and Visual Basic accesses Oracle Database through the OCI, which is implemented in C.

Taking all these factors into account suggests that there might be situations in which you might need to implement your application in multiple languages. For example, the introduction of Java running within the address space of the server suggest that you might want to import existing Java applications into the database, and then leverage this technology by calling Java functions from PL/SQL and SQL.

PL/SQL external procedures enable you to write C procedure calls as PL/SQL bodies. These C procedures are callable directly from PL/SQL, and from SQL through PL/SQL procedure calls. The database provides a special-purpose interface, the call specification, that lets you call external procedures from other languages. While this service is designed for intercommunication between SQL, PL/SQL, C, and Java, it is accessible from any base language that can call these languages. For example, your procedure can be written in a language other than Java or C, and if C can call your procedure, then SQL or PL/SQL can use it. Therefore, if you have a candidate C++ procedure, use a C++ extern "C" statement in that procedure to make it callable by C.

Therefore, the strengths and capabilities of different languages are available to you, regardless of your programmatic environment. You are not restricted to one language with its inherent limitations. External procedures promote reusability and modularity because you can deploy specific languages for specific purposes.

What Is an External Procedure?

An external procedure is a procedure stored in a dynamic link library (DLL), or libunit for a Java class method. You register the procedure with the base language, and then call it to perform special-purpose processing.

For example, when you work in PL/SQL, the language loads the library dynamically at run time, and then calls the procedure as if it were a PL/SQL procedure. These procedures participate fully in the current transaction and can call back to the database to perform SQL operations.

The procedures are loaded only when necessary, so memory is conserved. Because the decoupling of the call specification from its implementation body means that the procedures can be enhanced without affecting the calling programs.

External procedures let you:

  • Isolate execution of client applications and processes from the database instance to ensure that any problems on the client side do not adversely impact the database.

  • Move computation-bound programs from client to server where they run faster (because they avoid the round-trips of network communication)

  • Interface the database server with external systems and data sources

  • Extend the functionality of the database server itself


Note:

The external library (DLL file) must be statically linked. In other words, it must not reference any external symbols from other external libraries (DLL files). Oracle Database does not resolve such symbols, so they can cause your external procedure to fail.

Overview of Call Specification for External Procedures

You publish external procedures through call specifications, which provide a superset of the AS EXTERNAL function through the AS LANGUAGE clause. AS LANGUAGE call specifications allow the publishing of external C procedures, but also Java class methods.


Note:

To support legacy applications, call specifications also enable you to publish with the AS EXTERNAL clause. For application development, however, using the AS LANGUAGE clause is recommended.

In general, call specifications enable:

  • Dispatching the appropriate C or Java target procedure

  • Data type conversions

  • Parameter mode mappings

  • Automatic memory allocation and cleanup

  • Purity constraints to be specified, where necessary, for package functions called from SQL.

  • Calling Java methods or C procedures from database triggers

  • Location flexibility: you can put AS LANGUAGE call specifications in package or type specifications, or package (or type) bodies to optimize performance and hide implementation details

To use an existing program as an external procedure, load, publish, and then call it.

Loading External Procedures

To make your external C procedures or Java methods available to PL/SQL, you must first load them. The manner of doing this depends upon whether the procedure is written in C or Java.

Topics:

Loading Java Class Methods

One way to load Java programs is to use the CREATE JAVA statement, which you can run interactively from SQL*Plus. When implicitly called by the CREATE JAVA statement, the Java Virtual Machine (JVM)] library manager loads Java binaries (.class files) and resources from local BFILE or LOB columns into RDBMS libunits.

Suppose a compiled Java class is stored in the operating system file /home/java/bin/Agent.class.

Create a class libunit in schema username from file Agent.class as follows:

  1. Connect to the database as SYSTEM and grant the user username the CREATE ANY DIRECTORY privilege.

  2. Connect to the database as username and create a directory object on the server's file system:

    CREATE DIRECTORY Bfile_dir AS '/home/java/bin';
    

    The name of the directory object is an alias for the directory path leading to Agent.class.

  3. Create the class libunit:

    CREATE JAVA CLASS USING BFILE (Bfile_dir, 'Agent.class');
    

    The name of the libunit is derived from the name of the class.

Alternatively, you can use the command-line utility LoadJava. This uploads Java binaries and resources into a system-generated database table, then uses the CREATE JAVA statement to load the Java files into RDBMS libunits. You can upload Java files from file systems, Java IDEs, intranets, or the Internet.

Loading External C Procedures


Note:

You can load external C procedures only on platforms that support either DLLs or dynamically loadable shared libraries (such as Solaris .so libraries).

When an application calls an external C procedure, Oracle Database or Oracle Listener starts the external procedure agent, extproc. Using the network connection established by Oracle Database or Oracle Listener, the application passes this information to extproc:

  • Name of DLL or shared library

  • Name of external procedure

  • Any parameters for the external procedure

Then extproc loads the DLL or the shared library, runs the external procedure, and passes any values that the external procedure returns back to the application. The application and extproc must reside on the same computer.

extproc can call procedures in any library that complies with the calling standard used. For more information about the calling standard, see "CALLING STANDARD".


Note:

The default configuration for external procedures no longer requires a network listener to work with Oracle Database and extproc. Oracle Database now spawns extproc directly, eliminating the risk that Oracle Listener might spawn extproc unexpectedly. This default configuration is recommended for maximum security.

You must change this default configuration, so that Oracle Listener spawns extproc, if you use any of these:

  • A multithreaded extproc agent

  • Oracle Database in shared mode on Windows

  • An AGENT clause in the LIBRARY specification or an AGENT IN clause in the PROCEDURE specification that redirects external procedures to a different extproc agent

Changing the default configuration requires additional network configuration steps.


To configure your database to use external procedures that are written in C, or that can be called from C applications, you or your database administrator must follow these steps:

  1. Define the C Procedures

  2. Set Up the Environment

  3. Identify the DLL

  4. Publish the External Procedures

Define the C Procedures

Define the C procedures using one of these prototypes:

  • Kernighan & Ritchie style prototypes; for example:

    void C_findRoot(x)
     float x;
    ...
    
  • ISO/ANSI prototypes other than numeric data types that are less than full width (such as float, short, char); for example:

    void C_findRoot(double x)
    ...
    
  • Other data types that do not change size under default argument promotions.

    This example changes size under default argument promotions:

    void C_findRoot(float x)
    ...
    

Set Up the Environment

When you use the default configuration for external procedures, Oracle Database spawns extproc directly. You need not make configuration changes for listener.ora and tnsnames.ora. Define the environment variables to be used by external procedures in the file extproc.ora (located at $ORACLE_HOME/hs/admin on UNIX operating systems and at ORACLE_HOME\hs\admin on Windows), using this syntax:

SET name=value (environment_variable_name value)

Set the EXTPROC_DLLS environment variable, which restricts the DLLs that extproc can load, to one of these values:

  • NULL; for example:

    SET EXTPROC_DLLS=
    

    This setting, the default, allows extproc to load only the DLLs that are in directory $ORACLE_HOME/bin or $ORACLE_HOME/lib.

  • ONLY followed by a colon-separated (semicolon-separated on Windows systems) list of DLLs; for example:

    SET EXTPROC_DLLS=ONLY:DLL1:DLL2
    

    This setting allows extproc to load only the DLLs named DLL1 and DLL2. This setting provides maximum security.

  • A colon-separated (semicolon-separated on Windows systems) list of DLLs; for example:

    SET EXTPROC_DLLS=DLL1:DLL2
    

    This setting allows extproc to load the DLLs named DLL1 and DLL2 and the DLLs that are in directory $ORACLE_HOME/bin or $ORACLE_HOME/lib.

  • ANY; for example:

    SET EXTPROC_DLLS=ANY
    

    This setting allows extproc to load any DLL.

To change the default configuration for external procedures and have your extproc agent spawned by Oracle Listener, configure your database to use external procedures that are written in C, or can be called from C applications, as follows:

  1. Set configuration parameters for the agent, named extproc by default, in the configuration files tnsnames.ora and listener.ora. This establishes the connection for the external procedure agent, extproc, when the database is started.

  2. Start a listener process exclusively for external procedures.

    The Listener sets a few required environment variables (such as ORACLE_HOME, ORACLE_SID, and LD_LIBRARY_PATH) for extproc. It can also define specific environment variables in the ENVS section of its listener.ora entry, and these variables are passed to the agent process. Otherwise, it provides the agent with a "clean" environment. The environment variables set for the agent are independent of those set for the client and server. Therefore, external procedures, which run in the agent process, cannot read environment variables set for the client or server processes.


    Note:

    It is possible for you to set and read environment variables themselves by using the standard C procedures setenv and getenv, respectively. Environment variables, set this way, are specific to the agent process, which means that they can be read by all functions executed in that process, but not by any other process running on the same host.

  3. Determine whether the agent for your external procedure is to run in dedicated mode (the default) or multithreaded mode. In dedicated mode, one "dedicated" agent is launched for each session. In multithreaded mode, a single multithreaded extproc agent is launched. The multithreaded extproc agent handles calls using different threads for different users. In a configuration where many users can call the external procedures, using a multithreaded extproc agent is recommended to conserve system resources.

    If the agent is to run in dedicated mode, additional configuration of the agent process is not necessary.

    If the agent is to run in multithreaded mode, your database administrator must configure the database system to start the agent in multithreaded mode (as a multithreaded extproc agent). To do this configuration, use the agent control utility, agtctl. For example, start extproc using this command:

    agtctl startup extproc agent_sid 
    

    where agent_sid is the system identifier that this extproc agent services. An entry for this system identifier is typically added as an entry in the file tnsnames.ora. For more information about using agtctl for extproc administration, see "Administering the Multithreaded extproc Agent".


Note:

  • If you use a multithreaded extproc agent, the library you call must be thread safe—to avoid errors such as a damaged call stack.

  • The database server, the agent process, and the listener process that spawns the agent process must all reside on the same host.

  • By default, the agent process runs on the same database instance as your main application. In situations where reliability is critical, you might want to run the agent process for the external procedure on a separate database instance (still on the same host), so that any problems in the agent do not affect the primary database server. To do so, specify the separate database instance using a database link.


Figure A-1 illustrates the architecture of the multithreaded extproc agent.

Identify the DLL

In this context, a DLL is any dynamically loadable operating-system file that stores external procedures.

For security reasons, your DBA controls access to the DLL. Using the CREATE LIBRARY statement, the DBA creates a schema object called an alias library, which represents the DLL. Then, if you are an authorized user, the DBA grants you EXECUTE privileges on the alias library. Alternatively, the DBA might grant you CREATE ANY LIBRARY privileges, in which case you can create your own alias libraries using this syntax:

CREATE LIBRARY [schema_name.]library_name
  {IS | AS} 'file_path'
  [AGENT 'agent_link'];

Note:

The ANY privileges are very powerful and must not be granted lightly. For more information, see:

It is recommended that you specify the full path to the DLL, rather than just the DLL name. In this example, you create alias library c_utils, which represents DLL utils.so:

CREATE LIBRARY C_utils AS '/DLLs/utils.so';

To allow flexibility in specifying the DLLs, you can specify the root part of the path as an environment variable using the notation ${VAR_NAME}, and set up that variable in the ENVS section of the listener.ora entry.

In this example, the agent specified by the name agent_link is used to run any external procedure in the library C_Utils:

create or replace database link agent_link using 'agent_tns_alias';
create or replace library C_utils is
  '${EP_LIB_HOME}/utils.so' agent 'agent_link';

The environment variable EP_LIB_HOME is expanded by the agent to the appropriate path for that instance, such as /usr/bin/dll. Variable EP_LIB_HOME must be set in the file listener.ora, for the agent to be able to access it.

For security reasons, extproc, by default, loads only DLLs that are in directory $ORACLE_HOME/bin or $ORACLE_HOME/lib. Also, only local sessions—that is, Oracle Database client processes that run on the same system—are allowed to connect to extproc.

To load DLLs from other directories, set the environment variable EXTPROC_DLLS. The value for this environment variable is a colon-separated (semicolon-separated on Windows systems) list of DLL names qualified with the complete path. For example:

EXTPROC_DLLS=/private1/home/johndoe/dll/myDll.so:/private1/home/johndoe/dll/newDll.so

While you can set up environment variables for extproc through the ENVS parameter in the file listener.ora, you can also set up environment variables in the extproc initialization file extproc.ora in directory $ORACLE_HOME/hs/admin. When both extproc.ora and ENVS parameter in listener.ora are used, the environment variables defined in extproc.ora take precedence. See the Oracle Net manual for more information about the EXTPROC feature.


Note:

In extproc.ora on a Windows system, specify the path using a drive letter and using a double backslash (\\) for each backslash in the path. (The first backslash in each double backslash serves as an escape character.)

Publish the External Procedures

You find or write an external C procedure, and add it to the DLL. When the procedure is in the DLL, you publish it using the call specification mechanism described in "Publishing External Procedures".

Publishing External Procedures

Oracle Database can only use external procedures that are published through a call specification, which maps names, parameter types, and return types for your Java class method or C external procedure to their SQL counterparts. It is written like any other PL/SQL stored procedure except that, in its body, instead of declarations and a BEGIN END block, you code the AS LANGUAGE clause.

The AS LANGUAGE clause specifies:

  • Which language the procedure is written in.

  • For a Java method:

    • The signature of the Java method.

  • For a C procedure:

    • The alias library corresponding to the DLL for a C procedure.

    • The name of the C procedure in a DLL.

    • Various options for specifying how parameters are passed.

    • Which parameter (if any) holds the name of the external procedure agent, extproc, for running the procedure on a different system.

You begin the declaration using the normal CREATE OR REPLACE syntax for a procedure, function, package specification, package body, type specification, or type body.

The call specification follows the name and parameter declarations. Its syntax is:

{IS | AS} LANGUAGE {C | JAVA}

This is then followed by either:

NAME  java_string_literal_name

Where java_string_literal_name is the signature of your Java method, or by:

{ LIBRARY library_name [ NAME c_string_literal_name ] |
  [ NAME c_string_literal_name ] LIBRARY library_name }
[ AGENT IN ( argument [, argument]... ) ]
[ WITH CONTEXT ]
[ PARAMETERS (external_parameter[, external_parameter]...) ];

Where library_name is the name of your alias library, c_string_literal_name is the name of your external C procedure, and external_parameter stands for:

{  CONTEXT 
 | SELF [{TDO | property}]
 | {parameter_name | RETURN} [property] [BY REFERENCE] [external_datatype]}

property stands for:

{INDICATOR [{STRUCT | TDO}] | LENGTH | DURATION | MAXLEN | CHARSETID | CHARSETFORM}

Note:

Unlike Java, C does not understand SQL types; therefore, the syntax is more intricate

Topics:

AS LANGUAGE Clause for Java Class Methods

The AS LANGUAGE clause is the interface between PL/SQL and a Java class method.

AS LANGUAGE Clause for External C Procedures

These subclauses tell PL/SQL where to locate the external C procedure, how to call it, and what to pass to it:

Of the preceding subclauses, only LIBRARY is required.

LIBRARY

Specifies a local alias library. (You cannot use a database link to specify a remote library.) The library name is a PL/SQL identifier. Therefore, if you enclose the name in double quotation marks, then it becomes case-sensitive. (By default, the name is stored in upper case.) You must have EXECUTE privileges on the alias library.

NAME

Specifies the external C procedure to be called. If you enclose the procedure name in double quotation marks, then it becomes case-sensitive. (By default, the name is stored in upper case.) If you omit this subclause, then the procedure name defaults to the upper-case name of the PL/SQL procedure.


Note:

The terms LANGUAGE and CALLING STANDARD apply only to the superseded AS EXTERNAL clause.

LANGUAGE

Specifies the third-generation language in which the external procedure was written. If you omit this subclause, then the language name defaults to C.

CALLING STANDARD

Specifies the calling standard under which the external procedure was compiled. The supported calling standard is C. If you omit this subclause, then the calling standard defaults to C.

WITH CONTEXT

Specifies that a context pointer is passed to the external procedure. The context data structure is opaque to the external procedure but is available to service procedures called by the external procedure.

PARAMETERS

Specifies the positions and data types of parameters passed to the external procedure. It can also specify parameter properties, such as current length and maximum length, and the preferred parameter passing method (by value or by reference).

AGENT IN

Specifies which parameter holds the name of the agent process that runs this procedure. This is intended for situations where the external procedure agent, extproc, runs using multiple agent processes, to ensure robustness if the agent process of one external procedure fails. You can pass the name of the agent process (corresponding to the name of a database link), and if tnsnames.ora and listener.ora are set up properly across both instances, the external procedure is called on the other instance. Both instances must be on the same host.

This is similar to the AGENT clause of the CREATE LIBRARY statement; specifying the value at run time through AGENT IN allows greater flexibility.

When the agent name is specified this way, it overrides any agent name declared in the alias library. If no agent name is specified, the default is the extproc agent on the same instance as the calling program.

Publishing Java Class Methods

Java classes and their methods are stored in RDBMS libunits in which you can load Java sources, binaries and resources using the LOADJAVA utility or the CREATEJAVA SQL statements. Libunits can be considered analogous to DLLs written, for example, in C—although they map one-to-one with Java classes, whereas DLLs can contain multiple procedures.

The NAME-clause string uniquely identifies the Java method. The PL/SQL function or procedure and Java must have corresponding parameters. If the Java method takes no parameters, then you must code an empty parameter list for it.

When you load Java classes into the RDBMS, they are not published to SQL automatically. This is because the methods of many Java classes are called only from other Java classes, or take parameters for which there is no appropriate SQL type.

Suppose you want to publish this Java method named J_calcFactorial, which returns the factorial of its argument:

package myRoutines.math;
public class Factorial {
   public static int J_calcFactorial (int n) {
      if (n == 1) return 1;
      else return n * J_calcFactorial(n - 1);
   }
}

This call specification publishes Java method J_calcFactorial as PL/SQL stored function plsToJavaFac_func, using SQL*Plus:

CREATE OR REPLACE FUNCTION Plstojavafac_func (N NUMBER) RETURN NUMBER AS
   LANGUAGE JAVA
   NAME 'myRoutines.math.Factorial.J_calcFactorial(int) return int';

Publishing External C Procedures

In this example, you write a PL/SQL standalone function named plsCallsCdivisor_func that publishes C function Cdivisor_func as an external function:

CREATE OR REPLACE FUNCTION Plscallscdivisor_func (
/* Find greatest common divisor of x and y: */
 x     PLS_INTEGER, 
 y     PLS_INTEGER) 
RETURN PLS_INTEGER 
AS LANGUAGE C
   LIBRARY C_utils
   NAME "Cdivisor_func"; /* Quotation marks preserve case. */

Locations of Call Specifications

For both Java class methods and external C procedures, call specifications can be specified in any of these locations:

  • Standalone PL/SQL procedures

  • PL/SQL Package Specifications

  • PL/SQL Package Bodies

  • ADT Specifications

  • ADT Bodies


    Note:

    In Oracle Database version 8.0, AS EXTERNAL did not allow call specifications in package or type bodies.


See Also:


Examples:


Note:

In these examples, the AUTHID and SQL_NAME_RESOLVE clauses might be required to fully stipulate a call specification.

Example: Locating a Call Specification in a PL/SQL Package

CREATE OR REPLACE PACKAGE Demo_pack 
AUTHID DEFINER 
AS
   PROCEDURE plsToC_demoExternal_proc (x PLS_INTEGER, y VARCHAR2, z DATE) 
   AS LANGUAGE C
      NAME "C_demoExternal"
      LIBRARY SomeLib
      WITH CONTEXT
      PARAMETERS(CONTEXT, x INT, y STRING, z OCIDATE);
END;

Example: Locating a Call Specification in a PL/SQL Package Body

CREATE OR REPLACE PACKAGE Demo_pack 
   AUTHID CURRENT_USER
AS 
   PROCEDURE plsToC_demoExternal_proc(x PLS_INTEGER, y VARCHAR2, z DATE);
END;
 
CREATE OR REPLACE PACKAGE BODY Demo_pack 
   SQL_NAME_RESOLVE CURRENT_USER
AS
   PROCEDURE plsToC_demoExternal_proc (x PLS_INTEGER, y VARCHAR2, z DATE)
   AS LANGUAGE JAVA
      NAME 'pkg1.class4.methodProc1(int,java.lang.String,java.sql.Date)';
END;

Example: Locating a Call Specification in an ADT Specification


Note:

For examples in this topic to work, you must set up this data structure (which requires that you have the privilege CREATE ANY LIBRARY):
CREATE OR REPLACE LIBRARY SOMELIB AS '/tmp/lib.so';

CREATE OR REPLACE TYPE Demo_typ 
AUTHID DEFINER 
AS OBJECT
   (Attribute1   VARCHAR2(2000), SomeLib varchar2(20),
   MEMBER PROCEDURE plsToC_demoExternal_proc (x PLS_INTEGER, y VARCHAR2, z DATE) 
   AS LANGUAGE C
      NAME "C_demoExternal"
      LIBRARY SomeLib
      WITH CONTEXT
    --  PARAMETERS(CONTEXT, x INT, y STRING, z OCIDATE)
      PARAMETERS(CONTEXT, x INT, y STRING, z OCIDATE, SELF)
);

Example: Locating a Call Specification in an ADT Body

CREATE OR REPLACE TYPE Demo_typ 
AUTHID CURRENT_USER 
AS OBJECT
   (attribute1 NUMBER,
   MEMBER PROCEDURE plsToJ_demoExternal_proc (x PLS_INTEGER, y VARCHAR2, z DATE)
);

CREATE OR REPLACE TYPE BODY Demo_typ 
AS
   MEMBER PROCEDURE plsToJ_demoExternal_proc (x PLS_INTEGER, y VARCHAR2, z DATE)
   AS LANGUAGE JAVA
      NAME 'pkg1.class4.J_demoExternal(int,java.lang.String,java.sql.Date)';
END;

Example: Java with AUTHID

Here is an example of a publishing a Java class method in a standalone PL/SQL procedure.

CREATE OR REPLACE PROCEDURE plsToJ_demoExternal_proc (x PLS_INTEGER, y VARCHAR2, z DATE)
   AUTHID CURRENT_USER 
AS LANGUAGE JAVA
   NAME 'pkg1.class4.methodProc1(int,java.lang.String,java.sql.Date)';

Example: C with Optional AUTHID

Here is an example of AS EXTERNAL publishing a C procedure in a standalone PL/SQL program, in which the AUTHID clause is optional. This maintains compatibility with the external procedures of Oracle Database version 8.0.

CREATE OR REPLACE PROCEDURE plsToC_demoExternal_proc (x PLS_INTEGER, y VARCHAR2, z DATE) 
AS 
   EXTERNAL
   LANGUAGE C
   NAME "C_demoExternal"
   LIBRARY SomeLib
   WITH CONTEXT
   PARAMETERS(CONTEXT, x INT, y STRING, z OCIDATE);

Example: Mixing Call Specifications in a Package

CREATE OR REPLACE PACKAGE Demo_pack 
AUTHID DEFINER 
AS 
   PROCEDURE plsToC_InBodyOld_proc (x PLS_INTEGER, y VARCHAR2, z DATE);
   PROCEDURE plsToC_demoExternal_proc (x PLS_INTEGER, y VARCHAR2, z DATE); 
   PROCEDURE plsToC_InBody_proc (x PLS_INTEGER, y VARCHAR2, z DATE);
   PROCEDURE plsToJ_InBody_proc (x PLS_INTEGER, y VARCHAR2, z DATE);

   PROCEDURE plsToJ_InSpec_proc (x PLS_INTEGER, y VARCHAR2, z DATE)
   IS LANGUAGE JAVA
      NAME 'pkg1.class4.J_InSpec_meth(int,java.lang.String,java.sql.Date)';

PROCEDURE C_InSpec_proc (x PLS_INTEGER, y VARCHAR2, z DATE) 
   AS LANGUAGE C
      NAME "C_demoExternal"
      LIBRARY SomeLib
      WITH CONTEXT
      PARAMETERS(CONTEXT, x INT, y STRING, z OCIDATE);
END;

CREATE OR REPLACE PACKAGE BODY Demo_pack 
AS 
PROCEDURE plsToC_InBodyOld_proc (x PLS_INTEGER, y VARCHAR2, z DATE) 
   AS EXTERNAL
      LANGUAGE C
      NAME "C_InBodyOld"
      LIBRARY SomeLib
      WITH CONTEXT
      PARAMETERS(CONTEXT, x INT, y STRING, z OCIDATE); 
PROCEDURE plsToC_demoExternal_proc (x PLS_INTEGER, y VARCHAR2, z DATE) 
   AS LANGUAGE C
      NAME "C_demoExternal"
      LIBRARY SomeLib
      WITH CONTEXT
      PARAMETERS(CONTEXT, x INT, y STRING, z OCIDATE);
   
PROCEDURE plsToC_InBody_proc (x PLS_INTEGER, y VARCHAR2, z DATE) 
   AS LANGUAGE C
      NAME "C_InBody"
      LIBRARY SomeLib
      WITH CONTEXT
      PARAMETERS(CONTEXT, x INT, y STRING, z OCIDATE);
PROCEDURE plsToJ_InBody_proc (x PLS_INTEGER, y VARCHAR2, z DATE)
   IS LANGUAGE JAVA
      NAME 'pkg1.class4.J_InBody_meth(int,java.lang.String,java.sql.Date)';
END;

Passing Parameters to External C Procedures with Call Specifications

Call specifications allows a mapping between PL/SQL and C data types. See Specifying Data Types for data type mappings.

Passing parameters to an external C procedure is complicated by several circumstances:

  • The available set of PL/SQL data types does not correspond one-to-one with the set of C data types.

  • Unlike C, PL/SQL includes the RDBMS concept of nullity. Therefore, PL/SQL parameters can be NULL, whereas C parameters cannot.

  • The external procedure might need the current length or maximum length of CHAR, LONG RAW, RAW, and VARCHAR2 parameters.

  • The external procedure might need character set information about CHAR, VARCHAR2, and CLOB parameters.

  • PL/SQL might need the current length, maximum length, or null status of values returned by the external procedure.


Note:

The maximum number of parameters that you can pass to a C external procedure is 128. However, if you pass float or double parameters by value, then the maximum is less than 128. How much less depends on the number of such parameters and your operating system. To get a rough estimate, count each float or double passed by value as two parameters.

Topics:

Specifying Data Types

Do not pass parameters to an external procedure directly. Instead, pass them to the PL/SQL procedure that published the external procedure, specifying PL/SQL data types for the parameters. PL/SQL data types map to default external data types, as shown in Table 14-1.


Note:

The PL/SQL data types BINARY_INTEGER and PLS_INTEGER are identical. For simplicity, this document uses "PLS_INTEGER" to mean both BINARY_INTEGER and PLS_INTEGER.

Table 14-1 Parameter Data Type Mappings

PL/SQL Data TypeSupported External TypesDefault External Type
BINARY_INTEGER
BOOLEAN
PLS_INTEGER
[UNSIGNED] CHAR
[UNSIGNED] SHORT
[UNSIGNED] INT
[UNSIGNED] LONG
SB1, SB2, SB4
UB1, UB2, UB4
SIZE_T
INT
NATURALFoot 1 
NATURALNFootref 1
POSITIVEFootref 1
POSITIVENFootref 1
SIGNTYPEFootref 1
[UNSIGNED] CHAR
[UNSIGNED] SHORT
[UNSIGNED] INT
[UNSIGNED] LONG
SB1, SB2, SB4
UB1, UB2, UB4
SIZE_T
UNSIGNED INT
FLOAT
REAL
FLOAT
FLOAT
DOUBLE PRECISION
DOUBLE
DOUBLE
CHAR 
CHARACTER
LONG
NCHAR
NVARCHAR2
ROWID
VARCHAR 
VARCHAR2 
STRING
OCISTRING
STRING
LONG RAW 
RAW
RAW
OCIRAW
RAW
BFILE 
BLOB 
CLOB
NCLOB
OCILOBLOCATOR
OCILOBLOCATOR
NUMBER
DECFootref 1
DECIMALFootref 1
INTFootref 1
INTEGERFootref 1
NUMERICFootref 1
SMALLINTFootref 1
OCINUMBER
OCINUMBER
DATE
OCIDATE
OCIDATE
TIMESTAMP
TIMESTAMP WITH TIME ZONE
TIMESTAMP WITH LOCAL TIME ZONE
OCIDateTime
OCIDateTime
INTERVAL DAY TO SECOND
INTERVAL YEAR TO MONTH
OCIInterval
OCIInterval
composite object types: ADTs
dvoid
dvoid
composite object types: collections (varrays, nested tables)
OCICOLL
OCICOLL

Footnote 1 This PL/SQL type compiles only if you use AS EXTERNAL in your call spec.

External Data Type Mappings

Each external data type maps to a C data type, and the data type conversions are performed implicitly. To avoid errors when declaring C prototype parameters, see Table 14-2, which shows the C data type to specify for a given external data type and PL/SQL parameter mode. For example, if the external data type of an OUT parameter is STRING, then specify the data type char * in your C prototype.

Table 14-2 External Data Type Mappings

External Data Type Corresponding to PL/SL TypeIf Mode is IN or RETURN, Specify in C Prototype...If Mode is IN by Reference or RETURN by Reference, Specify in C Prototype...If Mode is IN OUT or OUT, Specify in C Prototype...
CHAR
char
char *
char *
UNSIGNED CHAR
unsigned char
unsigned char *
unsigned char *
SHORT
short
short *
short *
UNSIGNED SHORT
unsigned short
unsigned short *
unsigned short *
INT
int
int *
int *
UNSIGNED INT
unsigned int
unsigned int *
unsigned int *
LONG
long
long *
long *
UNSIGNED LONG
unsigned long
unsigned long *
unsigned long *
CHAR
char
char *
char *
UNSIGNED CHAR
unsigned char
unsigned char *
unsigned char *
SHORT
short
short *
short *
UNSIGNED SHORT
unsigned short
unsigned short *
unsigned short *
INT
int
int *
int *
UNSIGNED INT
unsigned int
unsigned int *
unsigned int *
LONG
long
long *
long *
UNSIGNED LONG
unsigned long
unsigned long *
unsigned long *
SIZE_T
size_t
size_t *
size_t *
SB1
sb1
sb1 *
sb1 *
UB1
ub1
ub1 *
ub1 *
SB2
sb2
sb2 *
sb2 *
UB2
ub2
ub2 *
ub2 *
SB4
sb4
sb4 *
sb4 *
UB4
ub4
ub4 *
ub4 *
FLOAT
float
float *
float *
DOUBLE
double
double *
double *
STRING
char *
char *
char *
RAW
unsigned char *
unsigned char *
unsigned char *
OCILOBLOCATOR
OCILobLocator *
OCILobLocator **
OCILobLocator **
OCINUMBER
OCINumber *
OCINumber *
OCINumber *
OCISTRING
OCIString *
OCIString *
OCIString *
OCIRAW
OCIRaw *
OCIRaw *
OCIRaw *
OCIDATE
OCIDate *
OCIDate *
OCIDate *
OCICOLL
OCIColl * or OCIArray * or OCITable *
OCIColl **
or OCIArray **
or OCITable **
OCIColl ** or OCIArray ** or OCITable **
OCITYPE
OCIType *
OCIType *
OCIType *
TDO
OCIType *
OCIType *
OCIType *
ADT
(final types)
dvoid*
dvoid*
dvoid*
ADT (nonfinal types)
dvoid*
dvoid*
dvoid**

Composite data types are not self describing. Their description is stored in a Type Descriptor Object (TDO). Objects and indicator structs for objects have no predefined OCI data type, but must use the data types generated by Oracle Database's Object Type Translator (OTT). The optional TDO argument for INDICATOR, and for composite objects, in general, has the C data type, OCIType *.

OCICOLL for REF and collection arguments is optional and exists only for completeness. You cannot map a REF or collection type onto any other data type, or any other data type onto a REF or collection type.

Passing Parameters BY VALUE or BY REFERENCE

If you specify BY VALUE, then scalar IN and RETURN arguments are passed by value (which is also the default). Alternatively, you might have them passed by reference by specifying BY REFERENCE.

By default, or if you specify BY REFERENCE, then scalar IN OUT, and OUT arguments are passed by reference. Specifying BY VALUE for IN OUT, and OUT arguments is not supported for C. The usefulness of the BY REFERENCE/VALUE clause is restricted to external data types that are, by default, passed by value. This is true for IN, and RETURN arguments of these external types:

[UNSIGNED] CHAR
[UNSIGNED] SHORT
[UNSIGNED] INT
[UNSIGNED] LONG
SIZE_T
SB1
SB2
SB4
UB1
UB2
UB4
FLOAT
DOUBLE

All IN and RETURN arguments of external types not on this list, all IN OUT arguments, and all OUT arguments are passed by reference.

Declaring Formal Parameters

Generally, the PL/SQL procedure that publishes an external procedure declares a list of formal parameters, as this example shows:


Note:

You might need to set up this data structure for examples in this topic to work:
CREATE LIBRARY MathLib AS '/tmp/math.so';

CREATE OR REPLACE FUNCTION Interp_func (
/* Find the value of y at x degrees using Lagrange interpolation: */ 
   x    IN FLOAT, 
   y    IN FLOAT) 
RETURN FLOAT AS 
   LANGUAGE C
   NAME "Interp_func"
   LIBRARY MathLib;

Each formal parameter declaration specifies a name, parameter mode, and PL/SQL data type (which maps to the default external data type). That might be all the information the external procedure needs. If not, then you can provide more information using the PARAMETERS clause, which lets you specify:

  • Nondefault external data types

  • The current or maximum length of a parameter

  • NULL/NOT NULL indicators for parameters

  • Character set IDs and forms

  • The position of parameters in the list

  • How IN parameters are passed (by value or by reference)

If you decide to use the PARAMETERS clause, keep in mind:

  • For every formal parameter, there must be a corresponding parameter in the PARAMETERS clause.

  • If you include the WITH CONTEXT clause, then you must specify the parameter CONTEXT, which shows the position of the context pointer in the parameter list.

  • If the external procedure is a function, then you might specify the RETURN parameter, but it must be in the last position. If RETURN is not specified, the default external type is used.

Overriding Default Data Type Mapping

In some cases, you can use the PARAMETERS clause to override the default data type mappings. For example, you can remap the PL/SQL data type BOOLEAN from external data type INT to external data type CHAR.

Specifying Properties

You can also use the PARAMETERS clause to pass more information about PL/SQL formal parameters and function results to an external procedure. Do this by specifying one or more of these properties:

INDICATOR [{STRUCT | TDO}]
LENGTH
DURATION
MAXLEN
CHARSETID
CHARSETFORM
SELF

Table 14-3 shows the allowed and the default external data types, PL/SQL data types, and PL/SQL parameter modes allowed for a given property. MAXLEN (used to specify data returned from C back to PL/SQL) cannot be applied to an IN parameter.

Table 14-3 Properties and Data Types

PropertyAllowed External Types (C)Default External Type (C)Allowed PL/SQL TypesAllowed PL/SQL ModesDefault PL/SQL Passing Method
INDICATOR
SHORT
SHORT
all scalars
IN
IN OUT
OUT
RETURN
BY VALUE
BY REFERENCE
BY REFERENCE
BY REFERENCE
LENGTH
[UNSIGNED] SHORT
[UNSIGNED] INT
[UNSIGNED] LONG
INT
CHAR
LONG RAW
RAW
VARCHAR2
IN
IN OUT
OUT
RETURN
BY VALUE
BY REFERENCE
BY REFERENCE
BY REFERENCE
MAXLEN
[UNSIGNED] SHORT
[UNSIGNED] INT
[UNSIGNED] LONG
INT
CHAR
LONG RAW
RAW
VARCHAR2
IN OUT
OUT
RETURN
BY REFERENCE
BY REFERENCE
BY REFERENCE
CHARSETID
CHARSETFORM
UNSIGNED SHORT
UNSIGNED INT
UNSIGNED LONG
UNSIGNED INT
CHAR
CLOB
VARCHAR2
IN
IN OUT
OUT
RETURN
BY VALUE
BY REFERENCE
BY REFERENCE
BY REFERENCE

In this example, the PARAMETERS clause specifies properties for the PL/SQL formal parameters and function result:

CREATE OR REPLACE FUNCTION plsToCparse_func  (
   x   IN PLS_INTEGER,
   Y   IN OUT CHAR) 
RETURN CHAR AS LANGUAGE C
   LIBRARY c_utils 
   NAME "C_parse" 
   PARAMETERS (
      x,            -- stores value of x
      x INDICATOR,  -- stores null status of x 
      y,            -- stores value of y
      y LENGTH,     -- stores current length of y
      y MAXLEN,     -- stores maximum length of y
      RETURN INDICATOR,
      RETURN);

With this PARAMETERS clause, the C prototype becomes:

char  *C_parse( int x, short x_ind, char *y, int *y_len, int *y_maxlen,
  short *retind );

The additional parameters in the C prototype correspond to the INDICATOR (for x), LENGTH (of y), and MAXLEN (of y), and the INDICATOR for the function result in the PARAMETERS clause. The parameter RETURN corresponds to the C function identifier, which stores the result value.

Topics:

INDICATOR

An INDICATOR is a parameter whose value indicates whether another parameter is NULL. PL/SQL does not need indicators, because the RDBMS concept of nullity is built into the language. However, an external procedure might need to know if a parameter or function result is NULL. Also, an external procedure might need to signal the server that a returned value is actually a NULL, and must be treated accordingly.

In such cases, you can use the property INDICATOR to associate an indicator with a formal parameter. If the PL/SQL procedure is a function, then you can also associate an indicator with the function result, as shown earlier.

To check the value of an indicator, you can use the constants OCI_IND_NULL and OCI_IND_NOTNULL. If the indicator equals OCI_IND_NULL, then the associated parameter or function result is NULL. If the indicator equals OCI_IND_NOTNULL, then the parameter or function result is not NULL.

For IN parameters, which are inherently read-only, INDICATOR is passed by value (unless you specify BY REFERENCE) and is read-only (even if you specify BY REFERENCE). For OUT, IN OUT, and RETURN parameters, INDICATOR is passed by reference by default.

The INDICATOR can also have a STRUCT or TDO option. Because specifying INDICATOR as a property of an object is not supported, and because arguments of objects have complete indicator structs instead of INDICATOR scalars, you must specify this by using the STRUCT option. You must use the type descriptor object (TDO) option for composite objects and collections,

LENGTH and MAXLEN

In PL/SQL, there is no standard way to indicate the length of a RAW or string parameter. However, you often want to pass the length of such a parameter to and from an external procedure. Using the properties LENGTH and MAXLEN, you can specify parameters that store the current length and maximum length of a formal parameter.


Note:

With a parameter of type RAW or LONG RAW, you must use the property LENGTH. Also, if that parameter is IN OUT and NULL or OUT and NULL, then you must set the length of the corresponding C parameter to zero.

For IN parameters, LENGTH is passed by value (unless you specify BY REFERENCE) and is read-only. For OUT, IN OUT, and RETURN parameters, LENGTH is passed by reference.

As mentioned earlier, MAXLEN does not apply to IN parameters. For OUT, IN OUT, and RETURN parameters, MAXLEN is passed by reference and is read-only.

CHARSETID and CHARSETFORM

Oracle Database provides globalization support, which lets you process single-byte and multibyte character data and convert between character sets. It also lets your applications run in different language environments.

By default, if the server and agent use the exact same $ORACLE_HOME value, the agent uses the same globalization support settings as the server (including any settings that were specified with ALTER SESSION statements).

If the agent is running in a separate $ORACLE_HOME (even if the same location is specified by two different aliases or symbolic links), the agent uses the same globalization support settings as the server except for the character set; the default character set for the agent is defined by the NLS_LANG and NLS_NCHAR environment settings for the agent.

The properties CHARSETID and CHARSETFORM identify the nondefault character set from which the character data being passed was formed. With CHAR, CLOB, and VARCHAR2 parameters, you can use CHARSETID and CHARSETFORM to pass the character set ID and form to the external procedure.

For IN parameters, CHARSETID and CHARSETFORM are passed by value (unless you specify BY REFERENCE) and are read-only (even if you specify BY REFERENCE). For OUT, IN OUT, and RETURN parameters, CHARSETID and CHARSETFORM are passed by reference and are read-only.

The OCI attribute names for these properties are OCI_ATTR_CHARSET_ID and OCI_ATTR_CHARSET_FORM.


See Also:


Repositioning Parameters

Remember, each formal parameter of the external procedure must have a corresponding parameter in the PARAMETERS clause. Their positions can differ, because PL/SQL associates them by name, not by position. However, the PARAMETERS clause and the C prototype for the external procedure must have the same number of parameters, and they must be in the same order.

SELF

SELF is the always-present argument of an object type's member procedure, namely the object instance itself. In most cases, this argument is implicit and is not listed in the argument list of the PL/SQL procedure. However, SELF must be explicitly specified as an argument of the PARAMETERS clause.

For example, assume that a user wants to create a Person object, consisting of a person's name and date of birth, and then create a table of this object type. The user eventually wants to determine the age of each Person object in this table.

In SQL*Plus, the Person object type can be created by:

CREATE OR REPLACE TYPE Person1_typ AS OBJECT (
  Name_     VARCHAR2(30),
  B_date    DATE,
  MEMBER FUNCTION calcAge_func RETURN NUMBER
);
/

Typically, the member function is implemented in PL/SQL, but in this example it is an external procedure. The body of the member function is declared as follows:

CREATE OR REPLACE TYPE BODY Person1_typ AS
  MEMBER FUNCTION calcAge_func RETURN NUMBER
  AS LANGUAGE C
  NAME "age"
  LIBRARY agelib
  WITH CONTEXT
  PARAMETERS (
    CONTEXT,
    SELF,
    SELF INDICATOR STRUCT,
    SELF TDO,
    RETURN INDICATOR
  );
END;
/

The calcAge_func member function does not take any arguments, but only returns a number. A member function is always called on an instance of the associated object type. The object instance itself always is an implicit argument of the member function. To refer to the implicit argument, the SELF keyword is used. This is incorporated into the external procedure syntax by supporting references to SELF in the parameters clause.

The matching table is created and populated.

CREATE TABLE Person_tab OF Person1_typ;

INSERT INTO Person_tab
VALUES ('BOB', TO_DATE('14-MAY-85'));

INSERT INTO Person_tab
VALUES ('JOHN', TO_DATE('22-DEC-71'));

Finally, retrieve the information of interest from the table.

SELECT p.name, p.b_date, p.calcAge_func() FROM Person_tab p; 

NAME                           B_DATE    P.CALCAGE_ 
------------------------------ --------- ---------- 
BOB                            14-MAY-85          0 
JOHN                           22-DEC-71          0
 

This is sample C code that implements the external member function and the Object-Type-Translator (OTT)-generated struct definitions:

#include <oci.h>

struct PERSON 
{ 
    OCIString   *NAME; 
    OCIDate      B_DATE; 
}; 
typedef struct PERSON PERSON; 
 
struct PERSON_ind 
{ 
    OCIInd    _atomic; 
    OCIInd    NAME; 
    OCIInd    B_DATE; 
}; 
typedef struct PERSON_ind PERSON_ind; 
 
OCINumber *age (ctx, person_obj, person_obj_ind, tdo, ret_ind) 
OCIExtProcContext *ctx; 
PERSON         *person_obj; 
PERSON_ind     *person_obj_ind; 
OCIType        *tdo; 
OCIInd         *ret_ind; 
{ 
    sword      err; 
    text       errbuf[512]; 
    OCIEnv    *envh; 
    OCISvcCtx *svch; 
    OCIError  *errh; 
    OCINumber *age; 
    int        inum = 0;
    sword      status;
  
    /* get OCI Environment */
    err = OCIExtProcGetEnv( ctx, &envh, &svch, &errh ); 

    /* initialize return age to 0 */
    age = (OCINumber *)OCIExtProcAllocCallMemory(ctx, sizeof(OCINumber));
    status = OCINumberFromInt(errh, &inum, sizeof(inum), OCI_NUMBER_SIGNED,
                              age);
    if (status != OCI_SUCCESS)
    {
      OCIExtProcRaiseExcp(ctx, (int)1476);
      return (age);
    }

    /* return NULL if the person object is null or the birthdate is null */
    if ( person_obj_ind->_atomic == OCI_IND_NULL || 
         person_obj_ind->B_DATE  == OCI_IND_NULL ) 
    { 
        *ret_ind = OCI_IND_NULL;
        return (age); 
    } 

    /* The actual implementation to calculate the age is left to the reader,
       but an easy way of doing this is a callback of the form:
            select trunc(months_between(sysdate, person_obj->b_date) / 12) 
            from DUAL;   
    */ 
    *ret_ind = OCI_IND_NOTNULL;
    return (age);
} 

BY REFERENCE

In C, you can pass IN scalar parameters by value (the value of the parameter is passed) or by reference (a pointer to the value is passed). When an external procedure expects a pointer to a scalar, specify BY REFERENCE phrase to pass the parameter by reference:

CREATE OR REPLACE PROCEDURE findRoot_proc (
   x IN DOUBLE PRECISION)
AS LANGUAGE C
   LIBRARY c_utils
   NAME "C_findRoot"
   PARAMETERS (
      x BY REFERENCE);

In this case, the C prototype is:

void C_findRoot(double *x);

The default (used when there is no PARAMETERS clause) is:

void C_findRoot(double x);

WITH CONTEXT

By including the WITH CONTEXT clause, you can give an external procedure access to information about parameters, exceptions, memory allocation, and the user environment. The WITH CONTEXT clause specifies that a context pointer is passed to the external procedure. For example, if you write this PL/SQL function:

CREATE OR REPLACE FUNCTION getNum_func (
   x IN REAL) 
RETURN PLS_INTEGER AS LANGUAGE C
   LIBRARY c_utils
   NAME "C_getNum"
   WITH CONTEXT
   PARAMETERS (
      CONTEXT,
      x BY REFERENCE,
      RETURN INDICATOR);

The C prototype is:

int C_getNum(
   OCIExtProcContext *with_context, 
   float *x, 
   short *retind);

The context data structure is opaque to the external procedure; but, is available to service procedures called by the external procedure.

If you also include the PARAMETERS clause, then you must specify the parameter CONTEXT, which shows the position of the context pointer in the parameter list. If you omit the PARAMETERS clause, then the context pointer is the first parameter passed to the external procedure.

Interlanguage Parameter Mode Mappings

PL/SQL supports the IN, IN OUT, and OUT parameter modes, and the RETURN clause for procedures returning values.

Running External Procedures with CALL Statements

Now that you have published your Java class method or external C procedure, you are ready to call it.

Do not call an external procedure directly. Instead, use the CALL statement to call the PL/SQL procedure that published the external procedure. See "CALL Statement Syntax".

Such calls, which you code in the same manner as a call to a regular PL/SQL procedure, can appear in:

  • Anonymous blocks

  • Standalone and package procedures

  • Methods of an object type

  • Database triggers

  • SQL statements (calls to package functions only).

Any PL/SQL block or procedure running on the server side, or on the client side, (for example, in a tool such as Oracle Forms) can call an external procedure. On the server side, the external procedure runs in a separate process address space, which safeguards your database. Figure 14-1 shows how Oracle Database and external procedures interact.

Figure 14-1 Oracle Database and External Procedures

Oracle Database and External Procedures
Description of "Figure 14-1 Oracle Database and External Procedures"

Topics:

Preconditions for External Procedures

Before calling external procedures, consider the privileges, permissions, and synonyms that exist in the execution environment.

Topics:

Privileges of External Procedures

When external procedures are called through CALL specifications, they run with definer's privileges, rather than invoker privileges.

A program running with invoker privileges is not bound to a particular schema. It runs at the calling site and accesses database items (such as tables and views) with the caller's visibility and permissions. However, a program running with definer's privileges is bound to the schema in which it is defined. It runs at the defining site, in the definer's schema, and accesses database items with the definer's visibility and permissions.

Managing Permissions

To call an external procedure, a user must have the EXECUTE privilege on its call specification. To grant this privilege, use the SQL statement GRANT (described in Oracle Database SQL Language Reference). For example, this statement allows the user johndoe to call the external procedure whose call specification is plsToJ_demoExternal_proc:

GRANT EXECUTE ON plsToJ_demoExternal_proc TO johndoe;

Grant the EXECUTE privilege on a call specification only to users who need to call the procedure.

Creating Synonyms for External Procedures

For convenience, you or your DBA can create synonyms for external procedures using the CREATE PUBLIC SYNONYM statement. In this example, your DBA creates a public synonym, which is accessible to all users. If PUBLIC is not specified, then the synonym is private and accessible only within its schema.

CREATE PUBLIC SYNONYM Rfac FOR johndoe.RecursiveFactorial;

CALL Statement Syntax

Call the external procedure through the SQL CALL statement. You can run the CALL statement interactively from SQL*Plus. The syntax is:

CALL [schema.][{object_type_name | package_name}]procedure_name[@dblink_name]
   [(parameter_list)] [INTO :host_variable][INDICATOR][:indicator_variable];

This is equivalent to running a procedure myproc using a SQL statement of the form "SELECT myproc(...) FROM DUAL," except that the overhead associated with performing the SELECT is not incurred.

For example, here is an anonymous PL/SQL block that uses dynamic SQL to call plsToC_demoExternal_proc, which you published. PL/SQL passes three parameters to the external C procedure C_demoExternal_proc.

DECLARE
   xx NUMBER(4);
   yy VARCHAR2(10);
   zz DATE; 
 BEGIN 
 EXECUTE IMMEDIATE
 'CALL plsToC_demoExternal_proc(:xxx, :yyy, :zzz)' USING xx,yy,zz;
 END;

The semantics of the CALL statement is identical to the that of an equivalent BEGIN END block.


Note:

CALL is the only SQL statement that cannot be put, by itself, in a PL/SQL BEGIN END block. It can be part of an EXECUTE IMMEDIATE statement within a BEGIN END block.

Calling Java Class Methods

To call the J_calcFactorial class method published earlier:

  1. Declare and initialize two SQL*Plus host variables:

    VARIABLE x NUMBER
    VARIABLE y NUMBER
    EXECUTE :x := 5;
    
  2. Call J_calcFactorial:

    CALL J_calcFactorial(:x) INTO :y;
    PRINT y
    

Result:

Y
------
   120

Calling External C Procedures

To call an external C procedure, PL/SQL must find the path of the appropriate DLL. The PL/SQL engine retrieves the path from the data dictionary, based on the library alias from the AS LANGUAGE clause of the procedure declaration.

Next, PL/SQL alerts a Listener process which, in turn, spawns a session-specific agent. By default, this agent is named extproc, although you can specify other names in the listener.ora file. The Listener hands over the connection to the agent, and PL/SQL passes to the agent the name of the DLL, the name of the external procedure, and any parameters.

Then, the agent loads the DLL and runs the external procedure. Also, the agent handles service calls (such as raising an exception) and callbacks to Oracle Database. Finally, the agent passes to PL/SQL any values returned by the external procedure.


Note:

Although some DLL caching takes place, your DLL might not remain in the cache; therefore, do not store global variables in your DLL.

After the external procedure completes, the agent remains active throughout your Oracle Database session; when you log off, the agent is stopped. Consequently, you incur the cost of launching the agent only once, no matter how many calls you make. Still, call an external procedure only when the computational benefits outweigh the cost.

Here, you call PL/SQL function plsCallsCdivisor_func, which you published previously, from an anonymous block. PL/SQL passes the two integer parameters to external function Cdivisor_func, which returns their greatest common divisor.

DECLARE
   g    PLS_INTEGER;
   a    PLS_INTEGER;
   b    PLS_INTEGER;
CALL plsCallsCdivisor_func(a, b); 
IF g IN (2,4,8) THEN ... 

Handling Errors and Exceptions in Multilanguage Programs

The PL/SQL compiler raises compile-time exceptions if an AS EXTERNAL call specification is found in a TYPE or PACKAGE specification.

C programs can raise exceptions through the OCIExtproc functions.

Using Service Routines with External C Procedures

When called from an external procedure, a service routine can raise exceptions, allocate memory, and call OCI handles for callbacks to the server. To use a service routine, you must specify the WITH CONTEXT clause, which lets you pass a context structure to the external procedure. The context structure is declared in header file ociextp.h as follows:

typedef struct OCIExtProcContext OCIExtProcContext;

Note:

ociextp.h is located in $ORACLE_HOME/plsql/public on Linux and UNIX.

Service procedures:

OCIExtProcAllocCallMemory

This service routine allocates n bytes of memory for the duration of the external procedure call. Any memory allocated by the function is freed automatically as soon as control returns to PL/SQL.


Note:

Do not have the external procedure call the C function free to free memory allocated by this service routine, as this is handled automatically.

The C prototype for this function is as follows:

dvoid *OCIExtProcAllocCallMemory(
   OCIExtProcContext *with_context, 
   size_t amount);

The parameters with_context and amount are the context pointer and number of bytes to allocate, respectively. The function returns an untyped pointer to the allocated memory. A return value of zero indicates failure.

In SQL*Plus, suppose you publish external function plsToC_concat_func, as follows:

CREATE OR REPLACE FUNCTION plsToC_concat_func ( 
   str1 IN VARCHAR2,  
   str2 IN VARCHAR2)  
RETURN VARCHAR2 AS LANGUAGE C 
NAME "concat" 
LIBRARY stringlib 
WITH CONTEXT 
PARAMETERS ( 
CONTEXT,  
str1   STRING,  
str1   INDICATOR short,  
str2   STRING,  
str2   INDICATOR short,  
RETURN INDICATOR short,  
RETURN LENGTH short,  
RETURN STRING); 

When called, C_concat concatenates two strings, then returns the result:

select plsToC_concat_func('hello ', 'world') from DUAL; 
PLSTOC_CONCAT_FUNC('HELLO','WORLD') 
-----------------------------------------------------------------------------
hello world

If either string is NULL, the result is also NULL. As this example shows, C_concat uses OCIExtProcAllocCallMemory to allocate memory for the result string:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <oci.h>
#include <ociextp.h>

char *concat(ctx, str1, str1_i, str2, str2_i, ret_i, ret_l) 
OCIExtProcContext *ctx; 
char   *str1; 
short  str1_i; 
char   *str2; 
short  str2_i; 
short  *ret_i; 
short  *ret_l; 
{ 
  char *tmp; 
  short len; 
  /* Check for null inputs. */ 
  if ((str1_i == OCI_IND_NULL) || (str2_i == OCI_IND_NULL)) 
  { 
      *ret_i = (short)OCI_IND_NULL; 
      /* PL/SQL has no notion of a NULL ptr, so return a zero-byte string. */  
      tmp = OCIExtProcAllocCallMemory(ctx, 1);  
      tmp[0] = '\0';  
      return(tmp);  
  } 
  /* Allocate memory for result string, including NULL terminator. */ 
  len = strlen(str1) + strlen(str2); 
  tmp = OCIExtProcAllocCallMemory(ctx, len + 1); 
 
  strcpy(tmp, str1); 
  strcat(tmp, str2); 
 
  /* Set NULL indicator and length. */ 
  *ret_i = (short)OCI_IND_NOTNULL; 
  *ret_l = len; 
  /* Return pointer, which PL/SQL frees later. */ 
  return(tmp); 
} 

#ifdef LATER
static void checkerr (/*_ OCIError *errhp, sword status _*/);

void checkerr(errhp, status)
OCIError *errhp;
sword status;
{
  text errbuf[512];
  sb4 errcode = 0;

  switch (status)
  {
  case OCI_SUCCESS:
    break;
  case OCI_SUCCESS_WITH_INFO:
    (void) printf("Error - OCI_SUCCESS_WITH_INFO\n");
    break;
  case OCI_NEED_DATA:
    (void) printf("Error - OCI_NEED_DATA\n");
    break;
  case OCI_NO_DATA:
    (void) printf("Error - OCI_NODATA\n");
    break;
  case OCI_ERROR:
    (void) OCIErrorGet((dvoid *)errhp, (ub4) 1, (text *) NULL, &errcode,
                        errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR);
    (void) printf("Error - %.*s\n", 512, errbuf);
    break;
  case OCI_INVALID_HANDLE:
    (void) printf("Error - OCI_INVALID_HANDLE\n");
    break;
  case OCI_STILL_EXECUTING:
    (void) printf("Error - OCI_STILL_EXECUTE\n");
    break;
  case OCI_CONTINUE:
    (void) printf("Error - OCI_CONTINUE\n");
    break;
  default:
    break;
  }
}

char *concat(ctx, str1, str1_i, str2, str2_i, ret_i, ret_l)
OCIExtProcContext *ctx;
char   *str1;
short  str1_i;
char   *str2;
short  str2_i;
short  *ret_i;
short  *ret_l;
{
  char *tmp;
  short len;
  /* Check for null inputs. */
  if ((str1_i == OCI_IND_NULL) || (str2_i == OCI_IND_NULL))
  {
      *ret_i = (short)OCI_IND_NULL;
      /* PL/SQL has no notion of a NULL ptr, so return a zero-byte string. */ 
      tmp = OCIExtProcAllocCallMemory(ctx, 1); 
      tmp[0] = '\0'; 
      return(tmp); 
  }
  /* Allocate memory for result string, including NULL terminator. */
  len = strlen(str1) + strlen(str2);
  tmp = OCIExtProcAllocCallMemory(ctx, len + 1);

  strcpy(tmp, str1);
  strcat(tmp, str2);

  /* Set NULL indicator and length. */
  *ret_i = (short)OCI_IND_NOTNULL;
  *ret_l = len;
  /* Return pointer, which PL/SQL frees later. */
  return(tmp);
}

/*======================================================================*/
int main(char *argv, int argc)
{
  OCIExtProcContext *ctx;
  char           *str1;
  short          str1_i;
  char           *str2;
  short          str2_i;
  short          *ret_i;
  short          *ret_l;
  /* OCI Handles */
  OCIEnv        *envhp;
  OCIServer     *srvhp;
  OCISvcCtx     *svchp;
  OCIError      *errhp;
  OCISession    *authp;
  OCIStmt       *stmthp;
  OCILobLocator *clob, *blob;
  OCILobLocator *Lob_loc;

  /* Initialize and Logon */
  (void) OCIInitialize((ub4) OCI_DEFAULT, (dvoid *)0,
                       (dvoid * (*)(dvoid *, size_t)) 0,
                       (dvoid * (*)(dvoid *, dvoid *, size_t))0,
                       (void (*)(dvoid *, dvoid *)) 0 );

  (void) OCIEnvInit( (OCIEnv **) &envhp, 
                    OCI_DEFAULT, (size_t) 0, 
                    (dvoid **) 0 );

  (void) OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &errhp, OCI_HTYPE_ERROR, 
                   (size_t) 0, (dvoid **) 0);

  /* Server contexts */
  (void) OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &srvhp, OCI_HTYPE_SERVER,
                   (size_t) 0, (dvoid **) 0);

  /* Service context */
  (void) OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &svchp, OCI_HTYPE_SVCCTX,
                   (size_t) 0, (dvoid **) 0);

  /* Attach to Oracle Database */
  (void) OCIServerAttach( srvhp, errhp, (text *)"", strlen(""), 0);

  /* Set attribute server context in the service context */
  (void) OCIAttrSet ((dvoid *) svchp, OCI_HTYPE_SVCCTX, 
                     (dvoid *)srvhp, (ub4) 0,
                    OCI_ATTR_SERVER, (OCIError *) errhp);

  (void) OCIHandleAlloc((dvoid *) envhp, 
                        (dvoid **)&authp, (ub4) OCI_HTYPE_SESSION,
                        (size_t) 0, (dvoid **) 0);
 
  (void) OCIAttrSet((dvoid *) authp, (ub4) OCI_HTYPE_SESSION,
                 (dvoid *) "samp", (ub4)4,
                 (ub4) OCI_ATTR_USERNAME, errhp);
 
  (void) OCIAttrSet((dvoid *) authp, (ub4) OCI_HTYPE_SESSION,
                 (dvoid *) "password", (ub4) 4,
                 (ub4) OCI_ATTR_PASSWORD, errhp);

  /* Begin a User Session */
  checkerr(errhp, OCISessionBegin ( svchp,  errhp, authp, OCI_CRED_RDBMS, 
                          (ub4) OCI_DEFAULT));

  (void) OCIAttrSet((dvoid *) svchp, (ub4) OCI_HTYPE_SVCCTX,
                   (dvoid *) authp, (ub4) 0,
                   (ub4) OCI_ATTR_SESSION, errhp);

  /* -----------------------User Logged In------------------------------*/
  printf ("user logged in \n");

  /* allocate a statement handle */
  checkerr(errhp, OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &stmthp,
           OCI_HTYPE_STMT, (size_t) 0, (dvoid **) 0));

  checkerr(errhp, OCIDescriptorAlloc((dvoid *)envhp, (dvoid **) &Lob_loc, 
                                     (ub4) OCI_DTYPE_LOB, 
                                     (size_t) 0, (dvoid **) 0)); 

  /* ------- subprogram called  here-----------------------*/ 
  printf ("calling concat...\n");
  concat(ctx, str1, str1_i, str2, str2_i, ret_i, ret_l);

  return 0;
}

#endif

OCIExtProcRaiseExcp

This service routine raises a predefined exception, which must have a valid Oracle Database error number in the range 1..32,767. After doing any necessary cleanup, your external procedure must return immediately. (No values are assigned to OUT or IN OUT parameters.) The C prototype for this function follows:

int OCIExtProcRaiseExcp(
   OCIExtProcContext *with_context, 
   size_t errnum);

The parameters with_context and error_number are the context pointer and Oracle Database error number. The return values OCIEXTPROC_SUCCESS and OCIEXTPROC_ERROR indicate success or failure.

In SQL*Plus, suppose you publish external procedure plsTo_divide_proc, as follows:

CREATE OR REPLACE PROCEDURE plsTo_divide_proc (
   dividend IN PLS_INTEGER, 
   divisor  IN PLS_INTEGER, 
   result   OUT FLOAT) 
AS LANGUAGE C
   NAME "C_divide"
   LIBRARY MathLib
   WITH CONTEXT
   PARAMETERS (
      CONTEXT, 
      dividend INT, 
      divisor  INT, 
      result   FLOAT);

When called, C_divide finds the quotient of two numbers. As this example shows, if the divisor is zero, C_divide uses OCIExtProcRaiseExcp to raise the predefined exception ZERO_DIVIDE:

void C_divide (ctx, dividend, divisor, result)
OCIExtProcContext *ctx;
int    dividend;
int    divisor;
float  *result;
{
  /* Check for zero divisor. */
  if (divisor == (int)0) 
  {
    /* Raise exception ZERO_DIVIDE, which is Oracle Database error 1476. */
    if (OCIExtProcRaiseExcp(ctx, (int)1476) == OCIEXTPROC_SUCCESS)
    {
      return;
    }
    else
    {
      /* Incorrect parameters were passed. */
      assert(0);
    }
  }
  *result = (float)dividend / (float)divisor;
}

OCIExtProcRaiseExcpWithMsg

This service routine raises a user-defined exception and returns a user-defined error message. The C prototype for this function follows:

int OCIExtProcRaiseExcpWithMsg(
   OCIExtProcContext *with_context, 
   size_t  error_number,
   text   *error_message, 
   size_t  len);

The parameters <y@code>with_context, error_number, and error_message are the context pointer, Oracle Database error number, and error message text. The parameter len stores the length of the error message. If the message is a null-terminated string, then len is zero. The return values OCIEXTPROC_SUCCESS and OCIEXTPROC_ERROR indicate success or failure.

In the previous example, you published external procedure plsTo_divide_proc. In this example, you use a different implementation. With this version, if the divisor is zero, then C_divide uses OCIExtProcRaiseExcpWithMsg to raise a user-defined exception:

void C_divide (ctx, dividend, divisor, result)
OCIExtProcContext *ctx;
int    dividend;
int    divisor;
float  *result;
  /* Check for zero divisor. */
  if (divisor == (int)0) 
  {
    /* Raise a user-defined exception, which is Oracle Database error 20100,
       and return a null-terminated error message. */
    if (OCIExtProcRaiseExcpWithMsg(ctx, (int)20100, 
          "divisor is zero", 0) == OCIEXTPROC_SUCCESS)
    {
      return;
    }
    else
    {
      /*  Incorrect parameters were passed. */
      assert(0);
    }
  }
  *result = dividend / divisor;

}

Doing Callbacks with External C Procedures

To enable callbacks, use the function OCIExtProcGetEnv.

Topics:

OCIExtProcGetEnv

This service routine enables OCI callbacks to the database during an external procedure call. The environment handles obtained by using this function reuse the existing connection to go back to the database. If you must establish a new connection to the database, you cannot use these handles; instead, you must create your own.

The C prototype for this function follows:

sword OCIExtProcGetEnv ( OCIExtProcContext *with_context,
   OCIEnv envh,
   OCISvcCtx svch,
   OCIError errh )

The parameter with_context is the context pointer, and the parameters envh, svch, and errh are the OCI environment, service, and error handles, respectively. The return values OCIEXTPROC_SUCCESS and OCIEXTPROC_ERROR indicate success or failure.

Both external C procedures and Java class methods can call-back to the database to do SQL operations. For a working example, see "Example: Calling an External Procedure" .


Note:

Callbacks are not necessarily a same-session phenomenon; you might run an SQL statement in a different session through OCIlogon.

An external C procedure running on Oracle Database can call a service routine to obtain OCI environment and service handles. With the OCI, you can use callbacks to run SQL statements and PL/SQL subprograms, fetch data, and manipulate LOBs. Callbacks and external procedures operate in the same user session and transaction context, and so have the same user privileges.

In SQL*Plus, suppose you run this script:

CREATE TABLE Emp_tab (empno NUMBER(10))

CREATE PROCEDURE plsToC_insertIntoEmpTab_proc (
   empno PLS_INTEGER)
AS LANGUAGE C
   NAME "C_insertEmpTab"
   LIBRARY insert_lib
   WITH CONTEXT
   PARAMETERS (
      CONTEXT, 
      empno LONG);

Later, you might call service routine OCIExtProcGetEnv from external procedure plsToC_insertIntoEmpTab_proc, as follows:

#include <stdio.h>
#include <stdlib.h>
#include <oratypes.h>
#include <oci.h>   /* includes ociextp.h */
...
void C_insertIntoEmpTab (ctx, empno) 
OCIExtProcContext *ctx; 
long empno; 
{ 
  OCIEnv    *envhp; 
  OCISvcCtx *svchp; 
  OCIError  *errhp; 
  int        err; 
  ... 
  err = OCIExtProcGetEnv(ctx, &envhp, &svchp, &errhp); 
  ... 
}

If you do not use callbacks, you need not include oci.h; instead, just include ociextp.h.

Object Support for OCI Callbacks

To run object-related callbacks from your external procedures, the OCI environment in the extproc agent is fully initialized in object mode. You retrieve handles to this environment with the OCIExtProcGetEnv procedure.

The object runtime environment lets you use static and dynamic object support provided by OCI. To use static support, use the OTT to generate C structs for the appropriate object types, and then use conventional C code to access the object attributes.

For those objects whose types are unknown at external procedure creation time, an alternative, dynamic, way of accessing objects is first to call OCIDescribeAny to obtain attribute and method information about the type. Then, OCIObjectGetAttr and OCIObjectSetAttr can be called to retrieve and set attribute values.

Because the current external procedure model is stateless, OCIExtProcGetEnv must be called in every external procedure that wants to run callbacks, or call OCIExtProc. service routines. After every external procedure call, the callback mechanism is cleaned up and all OCI handles are freed.

Restrictions on Callbacks

With callbacks, this SQL statements and OCI subprograms are not supported:

  • Transaction control statements such as COMMIT

  • Data definition statements such as CREATE

  • These object-oriented OCI subprograms:

    OCIObjectNew 
    OCIObjectPin 
    OCIObjectUnpin 
    OCIObjectPinCountReset 
    OCIObjectLock 
    OCIObjectMarkUpdate 
    OCIObjectUnmark 
    OCIObjectUnmarkByRef 
    OCIObjectAlwaysLatest 
    OCIObjectNotAlwaysLatest 
    OCIObjectMarkDeleteByRef 
    OCIObjectMarkDelete 
    OCIObjectFlush 
    OCIObjectFlushRefresh 
    OCIObjectGetTypeRef 
    OCIObjectGetObjectRef 
    OCIObjectExists 
    OCIObjectIsLocked 
    OCIObjectIsDirtied 
    OCIObjectIsLoaded 
    OCIObjectRefresh 
    OCIObjectPinTable 
    OCIObjectArrayPin 
    OCICacheFlush, 
    OCICacheFlushRefresh, 
    OCICacheRefresh 
    OCICacheUnpin 
    OCICacheFree 
    OCICacheUnmark 
    OCICacheGetObjects 
    OCICacheRegister 
    
  • Polling-mode OCI subprograms such as OCIGetPieceInfo

  • These OCI subprograms:

    OCIEnvInit
    OCIInitialize
    OCIPasswordChange
    OCIServerAttach
    OCIServerDetach
    OCISessionBegin
    OCISessionEnd
    OCISvcCtxToLda
    OCITransCommit
    OCITransDetach
    OCITransRollback
    OCITransStart
    

Also, with OCI subprogram OCIHandleAlloc, these handle types are not supported:

OCI_HTYPE_SERVER 
OCI_HTYPE_SESSION 
OCI_HTYPE_SVCCTX 
OCI_HTYPE_TRANS

Debugging External Procedures

Usually, when an external procedure fails, its prototype is faulty. In other words, the prototype does not match the one generated internally by PL/SQL. This can happen if you specify an incompatible C data type. For example, to pass an OUT parameter of type REAL, you must specify float *. Specifying float, double *, or any other C data type results in a mismatch.

In such cases, you might get:

lost RPC connection to external routine agent 

This error, which means that extproc terminated abnormally because the external procedure caused a core dump. To avoid errors when declaring C prototype parameters, see the preceding tables.

To help you debug external procedures, PL/SQL provides the utility package DEBUG_EXTPROC. To install the package, run the script dbgextp.sql, which you can find in the PL/SQL demo directory. (For the location of the directory, see your Oracle Database Installation or User's Guide.)

To use the package, follow the instructions in dbgextp.sql. Your Oracle Database account must have EXECUTE privileges on the package and CREATE LIBRARY privileges.


Note:

DEBUG_EXTPROC works only on platforms with debuggers that can attach to a running process.

Example: Calling an External Procedure

Also in the PL/SQL demo directory is the script extproc.sql, which demonstrates the calling of an external procedure. The companion file extproc.c contains the C source code for the external procedure.

To run the demo, follow the instructions in extproc.sql. You must use the SCOTT account, which must have CREATE LIBRARY privileges.

Global Variables in External C Procedures

A global variable is declared outside of a function, and its value is shared by all functions of a program. Therefore, in external procedures, all functions in a DLL share the value of the global variable. Global variables are also used to store data that is intended to persist beyond the lifetime of a function. However, Oracle discourages the use of global variables for two reasons:

  • Threading

    In the nonthreaded configuration of the agent process, one function is active at a time. For the multithreaded extproc agent, multiple functions can be active at the same time, and they might try to access the global variable concurrently, with unsuccessful results.

  • DLL caching

    Suppose that function func1 tries to pass data to function func2 by storing the data in a global variable. After func1 completes, the DLL cache might be unloaded, causing all global variables to lose their values. Then, when func2 runs, the DLL is reloaded, and all global variables are initialized to 0.

Static Variables in External C Procedures

There are two types of static variables: external and internal. An external static variable is a special case of a global variable, so its usage is discouraged. Internal static variables are local to a particular function, but remain in existence rather than coming and going each time the function is activated. Therefore, they provide private, permanent storage within a single function. These variables are used to pass on data to subsequent calls to the same function. But, because of the DLL caching feature mentioned previously, the DLL might be unloaded and reloaded between calls, which means that the internal static variable loses its value.


See Also:

Template makefile in the RDBMS subdirectory /public for help creating a dynamic link library

When calling external procedures:

  • Never write to IN parameters or overflow the capacity of OUT parameters. (PL/SQL does no run time checks for these error conditions.)

  • Never read an OUT parameter or a function result.

  • Always assign a value to IN OUT and OUT parameters and to function results. Otherwise, your external procedure will not return successfully.

  • If you include the WITH CONTEXT and PARAMETERS clauses, then you must specify the parameter CONTEXT, which shows the position of the context pointer in the parameter list.

  • If you include the PARAMETERS clause, and if the external procedure is a function, then you must specify the parameter RETURN in the last position.

  • For every formal parameter, there must be a corresponding parameter in the PARAMETERS clause. Also, ensure that the data types of parameters in the PARAMETERS clause are compatible with those in the C prototype, because no implicit conversions are done.

  • With a parameter of type RAW or LONG RAW, you must use the property LENGTH. Also, if that parameter is IN OUT or OUT and null, then you must set the length of the corresponding C parameter to zero.

Restrictions on External C Procedures

These restrictions apply to external procedures:

  • This feature is available only on platforms that support DLLs.

  • Only C procedures and procedures callable from C code are supported.

  • External procedure callouts combined with distributed transactions is not supported.

  • You cannot pass PL/SQL cursor variables or records to an external procedure. For records, use instances of object types instead.

  • In the LIBRARY subclause, you cannot use a database link to specify a remote library.

  • The maximum number of parameters that you can pass to a external procedure is 128. However, if you pass float or double parameters by value, then the maximum is less than 128. How much less depends on the number of such parameters and your operating system. To get a rough estimate, count each float or double passed by value as two parameters.