Oracle Database Application Developer's Guide - Fundamentals b14251
Oracle Database Application Developer's Guide - Fundamentals b14251
November 2005
Oracle Database Application Developers Guide - Fundamentals, 10g Release 2 (10.2) B14251-01 Copyright 1996, 2005, Oracle. All rights reserved. Primary Author: Lance Ashdown D. Adams, M. Cowan, R. Moran, J. Melnick, E. Paapanen, J. Russell, R. Strohm
Contributing Authors:
Contributors: D. Alpern, G. Arora, C. Barclay, D. Bronnikov, T. Chang, M. Davidson, G. Doherty, D. Elson, A. Ganesh, M. Hartstein, J. Huang, N. Jain, R. Jenkins Jr., S. Kotsovolos, S. Kumar, C. Lei, D. Lorentz, V. Moore, J. Muller, R. Murthy, R. Pang, B. Sinha, S. Vemuri, W. Wang, D. Wong, A. Yalamanchi, Q. Yu The Programs (which include both the software and documentation) contain proprietary information; they are provided under a license agreement containing restrictions on use and disclosure and are also protected by copyright, patent, and other intellectual and industrial property laws. Reverse engineering, disassembly, or decompilation of the Programs, except to the extent required to obtain interoperability with other independently created software or as specified by law, is prohibited. The information contained in this document is subject to change without notice. If you find any problems in the documentation, please report them to us in writing. This document is not warranted to be error-free. Except as may be expressly permitted in your license agreement for these Programs, no part of these Programs may be reproduced or transmitted in any form or by any means, electronic or mechanical, for any purpose. If the Programs are delivered to the United States Government or anyone licensing or using the Programs on behalf of the United States 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, use, duplication, disclosure, modification, and adaptation of the Programs, including documentation and technical data, shall be subject to the licensing restrictions set forth in the applicable Oracle license agreement, and, to the extent applicable, the additional rights set forth in FAR 52.227-19, Commercial Computer SoftwareRestricted Rights (June 1987). Oracle Corporation, 500 Oracle Parkway, Redwood City, CA 94065 The Programs are not intended for use in any nuclear, aviation, mass transit, medical, or other inherently dangerous applications. It shall be the licensee's responsibility to take all appropriate fail-safe, backup, redundancy and other measures to ensure the safe use of such applications if the Programs are used for such purposes, and we disclaim liability for any damages caused by such use of the Programs. Oracle, JD Edwards, PeopleSoft, and Retek are registered trademarks of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. The Programs may provide links to Web sites and access to content, products, and services from third parties. Oracle is not responsible for the availability of, or any content provided on, third-party Web sites. You bear all risks associated with the use of such content. If you choose to purchase any products or services from a third party, the relationship is directly between you and the third party. Oracle is not responsible for: (a) the quality of third-party products or services; or (b) fulfilling any of the terms of the agreement with the third party, including delivery of products or services and warranty obligations related to purchased products or services. Oracle is not responsible for any loss or damage of any sort that you may incur from dealing with any third party.
Contents
Preface ............................................................................................................................................................... xix
Audience..................................................................................................................................................... Documentation Accessibility ................................................................................................................... Related Documents ................................................................................................................................... Conventions ............................................................................................................................................... xix xix xx xxi
iii
Overview of Oracle SQLJ ............................................................................................................... Benefits of SQLJ........................................................................................................................ Comparing SQLJ with JDBC .................................................................................................. SQLJ Stored Procedures in the Server................................................................................... Overview of Oracle JPublisher...................................................................................................... Overview of Java Stored Procedures............................................................................................ Overview of Oracle Database Web Services ............................................................................... Oracle Database as a Web Service Provider ........................................................................ Overview of Writing Procedures and Functions in Java........................................................... Overview of Writing Database Triggers in Java ................................................................. Why Use Java for Stored Procedures and Triggers?........................................................... Overview of Pro*C/C++ ....................................................................................................................... Implementing a Pro*C/C++ Application.................................................................................... Highlights of Pro*C/C++ Features............................................................................................... Overview of Pro*COBOL .................................................................................................................... How You Implement a Pro*COBOL Application ...................................................................... Highlights of Pro*COBOL Features ............................................................................................. Overview of OCI and OCCI................................................................................................................ Advantages of OCI.......................................................................................................................... Parts of the OCI ............................................................................................................................... Procedural and Non-Procedural Elements ................................................................................. Building an OCI Application......................................................................................................... Overview of Oracle Data Provider for .NET (ODP.NET) .............................................................. Using ODP.NET in a Simple Application ................................................................................... Overview of Oracle Objects for OLE (OO4O)................................................................................. OO4O Automation Server ............................................................................................................. OO4O Object Model ....................................................................................................................... OraSession................................................................................................................................. OraServer .................................................................................................................................. OraDatabase.............................................................................................................................. OraDynaset ............................................................................................................................... OraField..................................................................................................................................... OraMetaData and OraMDAttribute...................................................................................... OraParameters and OraParameter ........................................................................................ OraParamArray........................................................................................................................ OraSQLStmt.............................................................................................................................. OraAQ ....................................................................................................................................... OraAQMsg................................................................................................................................ OraAQAgent............................................................................................................................. Support for Oracle LOB and Object Datatypes........................................................................... OraBLOB and OraCLOB ......................................................................................................... OraBFILE................................................................................................................................... Oracle Data Control ........................................................................................................................ Oracle Objects for OLE C++ Class Library.................................................................................. Additional Sources of Information ............................................................................................... Choosing a Programming Environment ........................................................................................... Choosing Whether to Use OCI or a Precompiler .......................................................................
1-11 1-12 1-12 1-13 1-13 1-13 1-14 1-14 1-15 1-15 1-15 1-15 1-16 1-17 1-18 1-18 1-19 1-19 1-20 1-20 1-21 1-21 1-22 1-22 1-23 1-24 1-25 1-25 1-25 1-26 1-26 1-26 1-27 1-27 1-27 1-27 1-27 1-28 1-28 1-28 1-29 1-29 1-29 1-29 1-29 1-30 1-30
iv
Using Built-In Packages and Libraries......................................................................................... Java Compared to PL/SQL............................................................................................................ PL/SQL Is Optimized for Database Access ......................................................................... PL/SQL Is Integrated with the Database ............................................................................. Both Java and PL/SQL Have Object-Oriented Features .................................................... Java Is Used for Open Distributed Applications.................................................................
Part I 2
Choosing an Isolation Level for Transactions...................................................................... Application Tips for Transactions ................................................................................................ Autonomous Transactions ................................................................................................................... Examples of Autonomous Transactions ...................................................................................... Entering a Buy Order .............................................................................................................. Example: Making a Bank Withdrawal .................................................................................. Defining Autonomous Transactions ............................................................................................ Restrictions on Autonomous Transactions.................................................................................. Resuming Execution After a Storage Error Condition................................................................... What Operations Can Be Resumed After an Error Condition?................................................ Limitations on Resuming Operations After an Error Condition ............................................. Writing an Application to Handle Suspended Storage Allocation.......................................... Example of Resumable Storage Allocation .................................................................................
2-20 2-20 2-20 2-22 2-23 2-23 2-26 2-27 2-27 2-27 2-27 2-28 2-28
vi
Importing and Exporting Datetime Types .................................................................................. Representing Specialized Data........................................................................................................... Representing Geographic Data ..................................................................................................... Representing Multimedia Data ..................................................................................................... Representing Large Amounts of Data.......................................................................................... Using RAW and LONG RAW Datatypes............................................................................. Representing Searchable Text ....................................................................................................... Representing XML .......................................................................................................................... Representing Dynamically Typed Data....................................................................................... Representing Data with ANSI/ISO, DB2, and SQL/DS Datatypes ....................................... Representing Conditional Expressions as Data .............................................................................. Identifying Rows by Address ............................................................................................................ Querying the ROWID Pseudocolumn ......................................................................................... Accessing the ROWID Datatype ................................................................................................... Restricted ROWID ................................................................................................................... Extended ROWID .................................................................................................................... External Binary ROWID.......................................................................................................... Accessing the UROWID Datatype................................................................................................ How Oracle Database Converts Datatypes ..................................................................................... Datatype Conversion During Assignments ................................................................................ Datatype Conversion During Expression Evaluation ..............................................................
3-15 3-16 3-16 3-16 3-16 3-17 3-18 3-18 3-19 3-21 3-22 3-23 3-24 3-24 3-24 3-24 3-25 3-25 3-25 3-25 3-26
vii
Creating Indexes: Basic Examples ........................................................................................................ When to Use Domain Indexes ............................................................................................................... When to Use Function-Based Indexes .................................................................................................. Advantages of Function-Based Indexes.......................................................................................... Examples of Function-Based Indexes.............................................................................................. Example: Function-Based Index for Case-Insensitive Searches ........................................... Example: Precomputing Arithmetic Expressions with a Function-Based Index............... Example: Function-Based Index for Language-Dependent Sorting.................................... Restrictions for Function-Based Indexes.........................................................................................
viii
Enabling and Disabling Existing Integrity Constraints............................................................. Enabling Existing Constraints ............................................................................................... Disabling Existing Constraints .............................................................................................. Tip: Using the Data Dictionary to Find Constraints ........................................................... Guidelines for Enabling and Disabling Key Integrity Constraints.......................................... Fixing Constraint Exceptions ....................................................................................................... Altering Integrity Constraints ............................................................................................................ Renaming Integrity Constraints.................................................................................................... Dropping Integrity Constraints.......................................................................................................... Managing FOREIGN KEY Integrity Constraints ........................................................................... Datatypes and Names for Foreign Key Columns....................................................................... Limit on Columns in Composite Foreign Keys .......................................................................... Foreign Key References Primary Key by Default....................................................................... Privileges Required to Create FOREIGN KEY Integrity Constraints...................................... Choosing How Foreign Keys Enforce Referential Integrity .................................................... Viewing Definitions of Integrity Constraints ................................................................................ Examples of Defining Integrity Constraints................................................................................
6-16 6-16 6-16 6-17 6-17 6-17 6-17 6-18 6-19 6-19 6-19 6-19 6-20 6-20 6-20 6-21 6-21
Part II 7
ix
Controlling Remote Dependencies .............................................................................................. Dependency Resolution ......................................................................................................... Suggestions for Managing Dependencies ........................................................................... Cursor Variables ................................................................................................................................... Declaring and Opening Cursor Variables .................................................................................. Examples of Cursor Variables ...................................................................................................... Fetching Data ........................................................................................................................... Implementing Variant Records ............................................................................................. Handling PL/SQL Compile-Time Errors ......................................................................................... Handling Run-Time PL/SQL Errors .................................................................................................. Declaring Exceptions and Exception Handling Routines ........................................................ Unhandled Exceptions .................................................................................................................. Handling Errors in Distributed Queries ..................................................................................... Handling Errors in Remote Procedures ...................................................................................... Debugging Stored Procedures ............................................................................................................ Calling Stored Procedures ................................................................................................................... A Procedure or Trigger Calling Another Procedure.................................................................. Interactively Calling Procedures From Oracle Database Tools ............................................... Calling Procedures within 3GL Applications ............................................................................ Name Resolution When Calling Procedures .............................................................................. Privileges Required to Execute a Procedure .............................................................................. Specifying Values for Procedure Arguments ............................................................................. Calling Remote Procedures ................................................................................................................ Remote Procedure Calls and Parameter Values ......................................................................... Referencing Remote Objects .......................................................................................................... Synonyms for Procedures and Packages .................................................................................... Calling Stored Functions from SQL Expressions ........................................................................... Using PL/SQL Functions .............................................................................................................. Syntax for SQL Calling a PL/SQL Function ............................................................................... Naming Conventions ..................................................................................................................... Name Precedence .................................................................................................................... Arguments ............................................................................................................................... Using Default Values .............................................................................................................. Privileges .................................................................................................................................. Requirements for Calling PL/SQL Functions from SQL Expressions .................................... Controlling Side Effects ................................................................................................................. Restrictions................................................................................................................................ Declaring a Function ............................................................................................................... Parallel Query and Parallel DML ......................................................................................... PRAGMA RESTRICT_REFERENCES for Backward Compatibility ............................. Serially Reusable PL/SQL Packages ........................................................................................... Package States .......................................................................................................................... Why Serially Reusable Packages? ......................................................................................... Syntax of Serially Reusable Packages ................................................................................... Semantics of Serially Reusable Packages.............................................................................. Examples of Serially Reusable Packages .............................................................................. Returning Large Amounts of Data from a Function.......................................................................
7-20 7-21 7-22 7-22 7-22 7-23 7-23 7-24 7-24 7-26 7-27 7-28 7-28 7-28 7-29 7-31 7-32 7-32 7-33 7-33 7-33 7-34 7-34 7-35 7-35 7-36 7-36 7-37 7-37 7-37 7-38 7-39 7-39 7-39 7-39 7-40 7-40 7-41 7-42 7-43 7-46 7-47 7-47 7-47 7-47 7-48 7-51
Coding Triggers
Designing Triggers................................................................................................................................... Creating Triggers ..................................................................................................................................... Types of Triggers ............................................................................................................................... Overview of System Events....................................................................................................... Getting the Attributes of System Events ................................................................................. 9-1 9-2 9-3 9-3 9-3
xi
Naming Triggers ............................................................................................................................... 9-3 When Is the Trigger Fired? .............................................................................................................. 9-3 Do Import and SQL*Loader Fire Triggers?............................................................................. 9-4 How Column Lists Affect UPDATE Triggers ........................................................................ 9-4 Controlling When a Trigger Is Fired (BEFORE and AFTER Options) ...................................... 9-4 Ordering of Triggers ......................................................................................................................... 9-5 Modifying Complex Views (INSTEAD OF Triggers) ................................................................... 9-6 Views that Require INSTEAD OF Triggers............................................................................. 9-6 INSTEAD OF Trigger Example................................................................................................. 9-7 Object Views and INSTEAD OF Triggers .............................................................................. 9-8 Triggers on Nested Table View Columns ............................................................................... 9-8 Firing Triggers One or Many Times (FOR EACH ROW Option) .............................................. 9-9 Firing Triggers Based on Conditions (WHEN Clause) ............................................................. 9-10 Coding the Trigger Body ..................................................................................................................... 9-10 Accessing Column Values in Row Triggers ............................................................................... 9-12 Example: Modifying LOB Columns with a Trigger............................................................ 9-12 INSTEAD OF Triggers on Nested Table View Columns ................................................... 9-13 Avoiding Name Conflicts with Triggers (REFERENCING Option) ............................... 9-13 Detecting the DML Operation That Fired a Trigger ........................................................... 9-14 Error Conditions and Exceptions in the Trigger Body ...................................................... 9-14 Triggers on Object Tables............................................................................................................... 9-14 Triggers and Handling Remote Exceptions ............................................................................... 9-15 Restrictions on Creating Triggers ................................................................................................ 9-16 Who Is the Trigger User? ............................................................................................................... 9-19 Privileges Needed to Work with Triggers .................................................................................. 9-19 Compiling Triggers .............................................................................................................................. 9-20 Dependencies for Triggers ............................................................................................................ 9-20 Recompiling Triggers .................................................................................................................... 9-20 Modifying Triggers .............................................................................................................................. 9-21 Debugging Triggers ....................................................................................................................... 9-21 Enabling and Disabling Triggers ....................................................................................................... 9-21 Enabling Triggers ........................................................................................................................... 9-21 Disabling Triggers .......................................................................................................................... 9-21 Viewing Information About Triggers ............................................................................................... 9-22 Examples of Trigger Applications ..................................................................................................... 9-23 Responding to System Events through Triggers ............................................................................ 9-37 How Events Are Published Through Triggers ........................................................................... 9-37 Publication Context......................................................................................................................... 9-38 Error Handling ................................................................................................................................ 9-38 Execution Model.............................................................................................................................. 9-38 Event Attribute Functions.............................................................................................................. 9-38 List of Database Events .................................................................................................................. 9-41 System Events........................................................................................................................... 9-41 Client Events............................................................................................................................. 9-42
10
xii
Application Development Features.............................................................................................. Database Administration Features ............................................................................................... Database Administration Tasks Before Using Flashback Features ............................................. Using Flashback Query (SELECT ... AS OF) ................................................................................... Examining Past Data: Example ..................................................................................................... Tips for Using Flashback Query ................................................................................................... Using the DBMS_FLASHBACK Package......................................................................................... Using ORA_ROWSCN......................................................................................................................... Using Flashback Version Query ........................................................................................................ Using Flashback Transaction Query................................................................................................ Flashback Transaction Query and Flashback Version Query: Example ............................... Flashback Tips ..................................................................................................................................... Flashback Tips Performance..................................................................................................... Flashback Tips General .............................................................................................................
10-2 10-2 10-3 10-4 10-5 10-5 10-6 10-7 10-8 10-10 10-10 10-12 10-12 10-13
11
12
xiii
Specifying the Scripting Language........................................................................................ Returning Data to the Client .................................................................................................. Handling Script Errors ............................................................................................................ Accepting User Input...................................................................................................................... Naming the PL/SQL Stored Procedure....................................................................................... Including the Contents of Other Files .......................................................................................... Declaring Global Variables in a PSP Script ................................................................................. Specifying Executable Statements in a PSP Script...................................................................... Substituting an Expression Result in a PSP Script ................................................................... Quoting and Escaping Strings in a PSP Script .......................................................................... Including Comments in a PSP Script ......................................................................................... Loading a PL/SQL Server Page into the Database ........................................................................ Querying PSP Source Code ......................................................................................................... Executing a PL/SQL Server Page Through a URL......................................................................... Examples of PL/SQL Server Pages ................................................................................................... Setup for PL/SQL Server Pages Examples................................................................................ Printing the Sample Table with a Loop ..................................................................................... Allowing a User Selection............................................................................................................ Using an HTML Form to Call a PL/SQL Server Page............................................................. Including JavaScript in a PSP File ....................................................................................... Debugging PL/SQL Server Page Problems .................................................................................... Putting PL/SQL Server Pages into Production ..............................................................................
12-5 12-5 12-7 12-7 12-8 12-8 12-9 12-9 12-10 12-11 12-11 12-12 12-13 12-14 12-15 12-15 12-16 12-17 12-18 12-19 12-20 12-21
13
xiv
Troubleshooting................................................................................................................................... 13-18
Part III 14
xv
How the Database Server Calls External C Procedures .......................................................... Handling Errors and Exceptions in Multi-Language Programs................................................. Using Service Procedures with External C Procedures................................................................ Doing Callbacks with External C Procedures................................................................................ Object Support for OCI Callbacks............................................................................................... Restrictions on Callbacks ............................................................................................................. Debugging External Procedures ................................................................................................. Using Package DEBUG_EXTPROC .................................................................................... Demo Program .............................................................................................................................. Guidelines for External C Procedures........................................................................................ Restrictions on External C Procedures.......................................................................................
14-28 14-29 14-29 14-35 14-37 14-37 14-38 14-38 14-39 14-39 14-40
15
xvi
Global Uniqueness of XIDs in Real Application Clusters................................................ SQL-Based XA Restrictions.......................................................................................................... Rollbacks and Commits ........................................................................................................ DDL Statements ..................................................................................................................... Session State............................................................................................................................ EXEC SQL ............................................................................................................................... Miscellaneous Restrictions...........................................................................................................
16
Index
xvii
xviii
Preface
The Oracle Database Application Developer's Guide - Fundamentals describes basic application development features of Oracle Database 10g. Information in this guide applies to features that work the same on all supported platforms, and does not include system-specific information.
Audience
Oracle Database Application Developer's Guide - Fundamentals is intended for programmers developing new applications or converting existing applications to run in the Oracle Database environment. This book will also be valuable to systems analysts, project managers, and others interested in the development of database applications. To use this document, you need a working knowledge of application programming, and that you are acquainted with using the Structured Query Language (SQL) to access information in relational database systems. Some sections of this guide assume a familiar with object-oriented programming.
Documentation Accessibility
Our goal is to make Oracle products, services, and supporting documentation accessible, with good usability, to the disabled community. To that end, our documentation includes features that make information available to users of assistive technology. This documentation is available in HTML format, and contains markup to facilitate access by the disabled community. Accessibility standards will continue to evolve over time, and Oracle is actively engaged with other market-leading technology vendors to address technical obstacles so that our documentation can be accessible to all of our customers. For more information, visit the Oracle Accessibility Program Web site at http://www.oracle.com/accessibility/ Accessibility of Code Examples in Documentation Screen readers may not always correctly read the code examples in this document. The conventions for writing code require that closing braces should appear on an otherwise empty line; however, some screen readers may not always read a line of text that consists solely of a bracket or brace.
xix
Accessibility of Links to External Web Sites in Documentation This documentation may contain links to Web sites of other companies or organizations that Oracle does not own or control. Oracle neither evaluates nor makes any representations regarding the accessibility of these Web sites. TTY Access to Oracle Support Services Oracle provides dedicated Text Telephone (TTY) access to Oracle Support Services within the United States of America 24 hours a day, seven days a week. For TTY support, call 800.446.2398.
Related Documents
For more information, see these Oracle resources:
Oracle Database PL/SQL User's Guide and Reference to learn PL/SQL and to get a complete description of the PL/SQL high-level programming language, which is Oracle's procedural extension to SQL. Oracle Call Interface Programmer's Guide and Oracle C++ Call Interface Programmer's Guide to learn about the Oracle Call Interface (OCI). You can use the OCI to build third-generation language (3GL) applications that access the Oracle Database. Oracle Database Security Guide to learn about security features of the database that application developers and database administrators need to be aware of. The Oracle documentation for the Pro* series of precompilers, which allow you to embed SQL and PL/SQL in your programs. If you write 3GL application programs in C, C++, COBOL, or FORTRAN that incorporate embedded SQL, then refer to the corresponding precompiler manual. For example, if you program in C or C++, then refer to the Pro*C/C++ Programmer's Guide. Oracle JDeveloper 10g is an Integrated Development Environment (IDE) for building applications and Web services using the latest industry standards for Java, XML, and SQL. You can access the JDeveloper documentation at the following product page: http://www.oracle.com/technology/products/jdev
Oracle Database SQL Reference and Oracle Database Administrator's Guide for SQL information. Oracle Database Concepts for basic Oracle Database concepts Oracle XML Developer's Kit Programmer's Guide and Oracle XML DB Developer's Guide for developing applications that manipulate XML data.
Many of the examples in this book use the sample schemas, which are installed by default when you select the Basic Installation option with an Oracle Database installation. Refer to Oracle Database Sample Schemas for information on how these schemas were created and how you can use them yourself. Printed documentation is available for sale in the Oracle Store at
http://oraclestore.oracle.com/
To download free release notes, installation documentation, white papers, or other collateral, please visit the Oracle Technology Network (OTN). You must register online before using OTN; registration is free and can be done at
http://www.oracle.com/technology/membership/
xx
If you already have a username and password for OTN, then you can go directly to the documentation section of the OTN Web site at
http://www.oracle.com/technology/documentation/
Oracle JDeveloper 10g: Empowering J2EE Development by Harshad Oak. Apress, 2004. Oracle JDeveloper 10g Handbook by Avrom Faderman, Peter Koletzke, and Paul Dorsey. Oracle Press, 2004. Oracle PL/SQL Tips and Techniques by Joseph C. Trezzo. Oracle Press, 1999. Oracle PL/SQL Programming by Steven Feuerstein. 3rd Edition. O'Reilly & Associates, 2002. Oracle PL/SQL Developer's Workbook by Steven Feuerstein. O'Reilly & Associates, 2000. Oracle PL/SQL Best Practices by Steven Feuerstein. O'Reilly & Associates, 2001.
Conventions
The following text conventions are used in this document:
Convention boldface italic monospace Meaning Boldface type indicates graphical user interface elements associated with an action, or terms defined in text or the glossary. Italic type indicates book titles, emphasis, or placeholder variables for which you supply particular values. Monospace type indicates commands within a paragraph, URLs, code in examples, text that appears on the screen, or text that you enter.
xxi
xxii
Oracle Database 10g Release 2 (10.2) New Features Oracle Database 10g Release 1 (10.1) New Features
Regular expression enhancements This release adds SQL support for common Perl-based extensions that are not included but do not conflict with the POSIX standard.
See Also: Chapter 4, "Using Regular Expressions in Oracle Database" for more information
Unicode datatype literal enhancement You can avoid data loss if the database character set cannot represent all characters in the client character set.
See Also:
Database Change Notification Database Change Notification enables client applications to receive notifications when the result set of a registered query changes. For example, if the client registers a query of the hr.employees table, and if a user adds an employee, then the application can receive a database change notification when a new row is added to the table. A new query of hr.employees returns the changed result set. Database Change Notification supports both mid-tier caches and server-side stored procedures. For example, a stored procedure that performs DML on registered tables can automatically send change notifications to a mid-tier application, which can keep cached data up to date.
See Also: Chapter 13, "Developing Applications with Database Change Notification"
xxiii
Asynchronous Commit Oracle Database enables you to change the handling of commit redo depending on the needs of your application. You can change the default COMMIT options so that the application does not need to wait for Oracle Database to write data to the online redo logs.
See Also:
Automatic Undo Retention Enhancement This feature provides maximum retention for the fixed-size undo tablespace, thereby improving flashback capability as well as other statistic collection queries in which the query length is unknown.
See Also: "Database Administration Tasks Before Using Flashback Features" on page 10-3
Failover Improvements for Real Application Clusters (RAC) Distributed Transactions Distributed Transactions in a RAC environment detect failures and start the failover and failback processes automatically.
See Also:
page 15-19
XML DB Web Services Enables direct access to the Oracle database through a native Web Service. Developers can write and deploy web services that can query the database with SQL or XQuery or execute stored procedures. You can access web services through the Oracle XML DB listener.
See Also:
Regular Expression Support A set of SQL functions introduced in this release let you perform queries and manipulate string data by means of regular expressions. Refer to Chapter 4, "Using Regular Expressions in Oracle Database" for more information.
Oracle Expression Filter Oracle Expression Filter lets you store conditional expressions in a column that you can use in the WHERE clause of a database query. Refer to "Representing Conditional Expressions as Data" on page 3-22 for more information.
See Also:
Native floating-point datatypes Column datatypes BINARY_FLOAT and BINARY_DOUBLE are introduced in this release. These datatypes provide an alternative to using the Oracle NUMBER datatype, with the following benefits:
xxiv
More efficient use of storage resources Faster arithmetic operations Support for numerical algorithms specified in the IEEE 754 Standard
Support for native floating-point datatypes in bind and fetch operations is provided for the following client interfaces:
Terabyte-Size Large Object (LOB) support This release provides support for terabyte-size LOB values (from 8 to 128 terabytes) in the following programmatic environments:
You can store and manipulate LOB (BLOB, CLOB, and NCLOB) datatypes larger than 4GB.
See Also:
Oracle Database Application Developer's Guide - Large Objects Oracle Call Interface Programmer's Guide
Flashback This release has new and enhanced flashback features. You can now do the following:
Query the transaction history of a row. Obtain the SQL undo syntax for a row and perform row-level flashback operations. Perform remote queries of past data.
See Also:
Oracle Data Provider for .NET Oracle Data Provider for .NET (ODP.NET) is a new programmatic environment that implements a data provider for Oracle Database. It uses APIs native to Oracle Database to offer fast and reliable access from any .NET application to database features and data. ODP.NET also uses and inherits classes and interfaces available in the Microsoft .NET Framework Class Library.
See Also:
xxvi
1
Orientation to Oracle Programmatic Environments
This chapter contains these topics:
Overview of Oracle Application Development Overview of PL/SQL Overview of Java Support Built Into the Database Overview of Pro*C/C++ Overview of Pro*COBOL Overview of OCI and OCCI Overview of Oracle Data Provider for .NET (ODP.NET) Overview of Oracle Objects for OLE (OO4O) Choosing a Programming Environment
Client/Server Model
In a traditional client/server program, the code of your application runs on a machine other than the database server. Database calls are transmitted from this client machine 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 machine. 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.
Server-Side Coding
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 procedures that are called 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 procedures callable through a Web server, you can construct a Web-based user interface that performs the same functions as a client/server application.
1-1
Overview of PL/SQL
User Interface
The interface that your application displays to end users depends on the technology behind the application as well as the needs of the users themselves. Experienced users can enter SQL commands 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 by means of HTML and Java.
Overview of PL/SQL
This section contains the following topics:
What Is PL/SQL?
PL/SQL is Oracle's procedural extension to SQL, the standard database access language. It is an advanced 4GL (fourth-generation programming language), which means that it is an application-specific language. PL/SQL and SQL have built-in treatment of the relational database domain. In PL/SQL, you can manipulate data with SQL statements and control program flow with procedural constructs such as loops. You can also do the following:
1-2 Oracle Database Application Developers Guide - Fundamentals
Overview of PL/SQL
Declare constants and variables Define procedures and functions Use collections and object types Trap runtime errors
Applications written in any of the Oracle programmatic interfaces can call PL/SQL stored procedures and send blocks of PL/SQL code to Oracle Database for execution. 3GL applications can access PL/SQL scalar and composite datatypes through host variables and implicit datatype conversion. A 3GL language is easier than assembler language for a human to understand and includes features such as named variables. Unlike 4GL, it is not specific to an application domain. Example 11 provides an example of a simple PL/SQL procedure. The procedure debit_account withdraws money from a bank account. It accepts an account number and an amount of money as parameters. It uses the account number to retrieve the account balance from the database, then computes the new balance. If this new balance is less than zero, then the procedure jumps to an error routine; otherwise, it updates the bank account.
Example 11 Simple PL/SQL Example PROCEDURE debit_account (p_acct_id INTEGER, p_debit_amount REAL) IS v_old_balance REAL; v_new_balance REAL; e_overdrawn EXCEPTION; BEGIN SELECT bal INTO v_old_balance FROM accts WHERE acct_no = p_acct_id; v_new_balance := v_old_balance - p_debit_amount; IF v_new_balance < 0 THEN RAISE e_overdrawn; ELSE UPDATE accts SET bal = v_new_balance WHERE acct_no = p_acct_id; END IF; COMMIT; EXCEPTION WHEN e_overdrawn THEN -- handle the error END debit_account;
See Also:
Oracle Database PL/SQL User's Guide and Reference Oracle Database SQL Reference
Advantages of PL/SQL
PL/SQL is a portable, high-performance transaction processing language with the following advantages:
Overview of PL/SQL
High Performance
If your application is database intensive, then you can use PL/SQL blocks to group SQL statements before sending them to Oracle Database for execution. This coding strategy can drastically reduce the communication overhead between your application and Oracle Database. PL/SQL stored procedures are compiled once and stored in executable form, so procedure calls are quick and efficient. A single call can start a compute-intensive stored procedure, reducing network traffic and improving round-trip response times. Executable code is automatically cached and shared among users, lowering memory requirements and invocation overhead.
High Productivity
PL/SQL adds procedural capabilities such as Oracle Forms and Oracle Reports. For example, you can use an entire PL/SQL block in an Oracle Forms trigger instead of multiple trigger steps, macros, or user exits. PL/SQL is the same in all environments. As soon as you master PL/SQL with one Oracle tool, you can transfer your knowledge to others, and so multiply the productivity gains. For example, scripts written with one tool can be used by other tools.
Scalability
PL/SQL stored procedures increase scalability by centralizing application processing on the server. Automatic dependency tracking helps you develop scalable applications.
Overview of PL/SQL
The shared memory facilities of the shared server enable Oracle Database to support many thousands of concurrent users on a single node. For more scalability, you can use the Oracle Connection Manager to multiplex network connections.
Manageability
After being validated, you can use a PL/SQL stored procedure in any number of applications. If its definition changes, then only the procedure is affected, not the applications that call it. This simplifies maintenance and enhancement. Also, maintaining a procedure on the Oracle Database is easier than maintaining copies on various client machines.
Portability
Applications written in PL/SQL can run on any operating system and hardware platform on which Oracle Database runs. You can write portable program libraries and reuse them in different environments.
Security
PL/SQL stored procedures enable you to divide application logic between the client and the server, which prevents client applications from manipulating sensitive Oracle Database data. Database triggers written in PL/SQL can prevent applications from making specified updates and can audit user queries. You can restrict access to Oracle Database data by allowing users to manipulate it only through stored procedures that have a restricted set of privileges. For example, you can grant users access to a procedure that updates a table but not grant them access to the table itself.
1-5
See Also: Oracle Database Security Guide for details on database security features
Built-In Packages
A package is an encapsulated collection of related program objects stored together in the database. Program objects are procedures, functions, variables, constants, cursors, and exceptions. The following packages are especially useful in application development for Oracle Database:
DBMS_PIPE is used to communicate between sessions. DBMS_ALERT is used to broadcast alerts to users. DBMS_LOCK and DBMS_TRANSACTION are used for lock and transaction management. DBMS_AQ is used for Advanced Queuing. DBMS_LOB is used to manipulate large objects. DBMS_ROWID is used for employing ROWID values. UTL_RAW is the RAW facility. UTL_REF is for work with REF values.
DBMS_SESSION is for session management by DBAs. DBMS_SPACE and DBMS_SHARED_POOL provide space information and reserve shared pool resources. DBMS_JOB is used to schedule jobs in the server.
Translates a URL passed by a browser client Calls an Oracle Database stored procedure with the parameters in the URL Returns output (typically HTML) to the client
See Also: Chapter 11, "Developing Applications with the PL/SQL Web Toolkit" to learn how to use PL/SQL in Web development
and SQLJ, and provides server-side JDBC and SQLJ drivers that allow data-intensive Java code to run within the database. This section contains the following topics:
Overview of Oracle JVM Overview of Oracle Extensions to JDBC Overview of Oracle SQLJ Overview of Oracle JPublisher Overview of Java Stored Procedures Overview of Oracle Database Web Services Overview of Writing Procedures and Functions in Java
See Also:
Oracle Database Concepts for background information about Java and how the database supports it Oracle Database Java Developer's Guide Oracle Database JDBC Developer's Guide and Reference Oracle Database JPublisher User's Guide
Java applications have the same session isolation and data integrity as SQL operations. There is no need to 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 invoked. Java class invocation is secured and controlled through database authentication and authorization, Java 2 security, and invoker's or definer's rights.
1-7
Type 1. A JDBC-ODBC bridge. Software must be installed on client systems. Type 2. Native methods (calls C or C++) and Java methods. Software must be installed on the client. Type 3. Pure Java. The client uses sockets to call middleware on the server. Type 4. The most pure Java solution. Talks directly to the database by using Java sockets.
JDBC is based on the X/Open SQL Call Level Interface, and complies with the SQL92 Entry Level standard. 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. Oracle's implementations of JDBC drivers are described in the following sections. Oracle Database support of and extensions to various levels of the JDBC standard are described in "Oracle Database Extensions to JDBC Standards" on page 1-9.
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.
Support for Oracle datatypes 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 of the highlights include datasources, JTA, and distributed transactions. Oracle Database supports the following features from the JDBC 3.0 standard:
Support for JDK 1.4. Toggling between local and global transactions. Transaction savepoints. Reuse of prepared statements by connection pools.
1-9
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", "hr"); // 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, and database information as well as row prefetching and execution batching. To use the OCI driver in this code, replace the Connection statement with the following, where MyHostString is an entry in the tnsnames.ora file:
Connection conn = DriverManager.getConnection ("jdbc:oracle:oci8:@MyHostString", "hr", "hr");
If you are creating an applet, then the getConnection() and registerDriver() strings will be different.
See Also: Oracle Database JDBC Developer's Guide and Reference for more information on JDBC
The term "SQLJ," when used in this manual, refers to the Oracle SQLJ implementation, including Oracle SQLJ extensions.
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 the following 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 procedures, functions, and triggers which execute in the Oracle JVM. SQLJ is integrated with JDeveloper. Source-level debugging support for SQLJ is available in JDeveloper. The following 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 Sample SQLJ code available on the Oracle Technology Network Web site: http://www.oracle.com/technology/
Benefits of SQLJ
Oracle SQLJ extensions to Java allow rapid development and easy maintenance of applications that perform database operations through embedded SQL. In particular, Oracle SQLJ does the following:
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 will be 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 type extensions. Datatypes supported include: LOB datatypes, ROWID, REF CURSOR, VARRAY, nested table, user-defined object types, RAW, and NUMBER.
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 allows type-checking on calls. JDBC passes values to and from SQL without compile-time type checking. SQLJ programs allow 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 procedures and functions. For example, the following JDBC excerpt requires a generic call to a stored procedure or function, in this case fun, to have the following syntax. (This example shows SQL92 and Oracle JDBC syntaxes. Both are allowed.)
prepStmt.prepareCall("{call fun(?,?)}"); //stored procedure SQL92 prepStmt.prepareCall("{? = call fun(?,?)}"); //stored function SQL92 prepStmt.prepareCall("begin fun(:1,:2);end;"); //stored procedure Oracle prepStmt.prepareCall("begin :1 := fun(:2,:3);end;");//stored func Oracle
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 object types and collections. Java and PL/SQL stored procedures can be used interchangeably.
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.
See Also: Oracle Database JPublisher User's Guide for more information on using stored procedures with Oracle SQLJ
Java stored procedures interface with SQL by using a similar execution model as PL/SQL.
See Also:
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 perform invocation 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, in which case you may know everything about it beforehand, or dynamically, in which case you can discover its operations and transport endpoints on the fly.
Exposes stored procedures independently of the language in which the procedures are written Exposes SQL Queries and XQuery
Overview of Pro*C/C++
SQL CALL statements Embedded SQL CALL statements PL/SQL blocks, subprograms, and packages DML statements (INSERT, UPDATE, DELETE, and SELECT) Oracle development tools such as OCI, Pro*C/C++, and Oracle Developer Oracle Java interfaces such as JDBC, SQLJ statements, CORBA, and Enterprise Java Beans Method calls from object types
Stored procedures and triggers are compiled once, are easy to use and maintain, and require less memory and computing overhead. Network bottlenecks are avoided, and response time is improved. Distributed applications are easier to build and use. Computation-bound procedures run faster in the server. Data access can be controlled by letting users have only stored procedures and triggers that execute with their definer's privileges instead of invoker's rights. PL/SQL and Java stored procedures can call each other. Java in the server follows the Java language specification and can use the SQLJ standard, so that databases other than Oracle Database are also supported. Stored procedures and triggers can be reused in different applications as well as different geographic sites.
Overview of Pro*C/C++
The Pro*C/C++ precompiler is a software tool that allows the programmer to embed SQL statements in a C or C++ source file. Pro*C/C++ reads the source file as input and outputs a C or C++ source file that replaces the embedded SQL statements with Oracle runtime library calls and is then compiled by the C or C++ compiler. When there are errors found during the precompilation or the subsequent compilation, modify your precompiler input file and re-run the two steps.
Overview of Pro*C/C++
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 (C) variable. The returned values of data and indicators (set when the data value is NULL or character columns have been 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. You use the actual names of columns and tables in embedded SQL. Use the default precompiler option values, or you can enter values which 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 in-line inside your source code with a special statement that begins with EXEC ORACLE. If there are no errors found, you can then compile, link, and execute 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++ allows you the freedom to design your own user interfaces and to add database access to existing applications. Before writing your embedded SQL statements, you may want to test interactive versions of the SQL in SQL*Plus. You then make only minor changes to start testing your embedded SQL application.
Overview of Pro*C/C++
You can write your application in either C or C++. 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 call functions or procedures 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, as well as at runtime. You can call stored PL/SQL and Java subprograms. Modules written in COBOL or in C can be called from Pro*C/C++. External C procedures in shared libraries are callable by your program. You can conditionally precompile sections of your code so that they can execute 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 datatypes and C language datatypes. 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 allows 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) will map the object types and named collection types in your database to structures and headers that you will then include in your source. Two kinds of collection types, nested tables and VARRAY, are supported with a set of SQL statements that allow 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 execute 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 to optimize the performance of Pro*C/C++ application. The connection pool option is not enabled by default.
Overview of Pro*COBOL
Overview of Pro*COBOL
The Pro*COBOL precompiler is a software tool that allows the programmer to embed SQL statements in a COBOL source code file. Pro*COBOL reads the source file as input and outputs a COBOL source file that replaces the embedded SQL statements with Oracle Database runtime library calls, and is then compiled by the COBOL compiler. When there are errors found during the precompilation or the subsequent compilation, modify your precompiler input file and re-run the two steps.
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 have been 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. You 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 in-line inside your source code with a special statement that begins with EXEC ORACLE. If there are no errors found, you can then compile, link, and execute 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 allows 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 may want to test interactive versions of the SQL in SQL*Plus. You then make only minor changes to start testing your embedded SQL application.
You can call stored PL/SQL or Java subprograms. You can improve performance by embedding PL/SQL blocks. These blocks can call PL/SQL functions or procedures written by you or provided in Oracle Database packages. Precompiler options allow 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, as well as at runtime. You can conditionally precompile sections of your code so that they can execute 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 allows users to input variable values and statement syntax.
See Also:
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 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 runtime. This eliminates the need to embed SQL or PL/SQL within 3GL programs.
See Also:
Oracle Call Interface Programmer's Guide Oracle C++ Call Interface Programmer's Guide Oracle Streams Advanced Queuing User's Guide and Reference Oracle Database Globalization Support Guide Oracle Database Data Cartridge Developer's Guide
Advantages of OCI
OCI provides 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 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 array INSERTs, UPDATEs, and DELETEs. Ability to associate a commit request with an execute 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 handles. The server connection in nonblocking mode means that control returns to the OCI code when a call is still executing or could not complete.
OCI relational functions, for managing database access and processing SQL statements OCI navigational functions, for manipulating objects retrieved from an Oracle Database OCI datatype mapping and manipulation functions, for manipulating data attributes of Oracle types OCI external procedure functions, for writing C callbacks from PL/SQL
In a non-procedural language program, the set of data to be operated on is specified, but what operations will be performed and how the operations are to be carried out is not specified. The non-procedural 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 non-procedural and procedural language elements in an OCI program provides easy access to Oracle Database in a structured programming environment. The OCI supports all SQL data definition, data manipulation, query, and transaction control facilities that are available through Oracle Database. For example, an OCI 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 that will be supplied by the application. You can alternatively 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. The OCI also provides facilities for accessing and manipulating objects in Oracle Database.
Source Files
Object Files
OCI Library
Host Linker
Application
Object Server
Note:
To properly link your OCI programs, it may be necessary on some platforms to include other libraries, in addition to the OCI library. Check your Oracle platform-specific documentation for further information about extra libraries that may be required.
{ con = new OracleConnection(); con.ConnectionString = "User Id=hr;Password=hr;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(); } }
OO4O "In-Process" Automation Server Oracle Data Control Oracle Objects for OLE C++ Class Library
COM/DCOM OO4O In-Process Automation Server Oracle Client Libraries (OCI, CORE, NLS)
Oracle Database
Support for execution of PL/SQL and Java stored procedures, and PL/SQL anonymous blocks. This includes support for Oracle datatypes used as parameters to stored procedures, including PL/SQL cursors. Refer to "Support for Oracle LOB and Object Datatypes" on page 1-28. 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 object-relational and LOB datatypes.
Full support for Advanced Queuing. Support for array inserts and updates. Support for Microsoft Transaction Server (MTS).
OraMetaData
OraMDAttribute
OraSQLStmt
OraAQ
OraAQMsg
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. The following 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 invoking 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 of Oracle object types. Attributes of schema objects can be retrieved using the Describe method of the OraDatabase interface. In releases prior to Oracle8i, an OraDatabase object is created by invoking the OpenDatabase method of an OraSession interface. The Oracle Net alias, user name, and password are passed as arguments to this method. In Oracle8i and later, invocation of 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 may be started as Read-Write (default), Serializable, or Read-only. Transaction control methods include:
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 may 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 may 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 may not yet 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 new record is being added (using AddNew). Other attempts to assign data to a field's Value property results in an error.
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 a subscript that is not in the collection results in the return of a NULL OraMDAttribute object.
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 re-parsing the SQL statement. The OraSQLStmt object can be used later to execute 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 invoking 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, refer to Oracle Streams Advanced Queuing User's Guide and Reference.
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, refer to Oracle Streams Advanced Queuing User's Guide and Reference.
OraAQAgent
The OraAQAgent object represents a message recipient and is only valid for queues that allow multiple consumers. It is a child of OraAQMsg. An OraAQAgent object can be instantiated by invoking the AQAgent method. For example:
Set agent = qMsg.AQAgent(name)
An OraAQAgent object can also be instantiated by invoking the AddRecipient method. For example:
Set agent = qMsg.AddRecipient(name, address, protocol).
OraCLOB
OraBFILE
OraBFILE
The OraBFile interface in Oracle Objects for OLE provides methods for performing operations on large database objects of datatype BFILE. BFILE objects are large binary data objects stored in operating system files outside of the database tablespaces.
Oracle Objects for OLE Help Oracle Objects for OLE C++ Class Library Help
To view examples of how to use Oracle Objects for OLE, refer to the samples located in the ORACLE_HOME\OO4O directory of the Oracle Database installation. Additional OO4O examples can be found in the following Oracle publications:
Oracle Streams Advanced Queuing User's Guide and Reference Oracle Database PL/SQL Packages and Types Reference
Review the preceding overviews and the manuals for each environment. Read the platform-specific manual that explains which compilers are approved for use with your platforms. If a particular language does not provide a feature you need, remember that PL/SQL and Java stored procedures can both be called from code written in any of the languages in this chapter. Stored procedures include triggers and object type methods. External procedures written in C can be called from OCI, Java, PL/SQL or SQL. The external procedure itself can call back into the database using either SQL, OCI, or Pro*C (but not C++).
Pro*COBOL does not support object types or collection types, while Pro*C/C++ does. SQLJ does not support dynamic SQL the way that JDBC does.
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 allows 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 allows 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 datatypes. OCI calls can be embedded in a Pro*C/C++ application.
distributed CORBA and EJB clients. The following table shows PL/SQL packages and their Java equivalents:
Table 11 PL/SQL and Java Equivalent Software Java Equivalent Call package with SQLJ or JDBC. JDBC has this functionality. Schedule a job that has a Java Stored procedure. Call with SQLJ or JDBC. Use JavaMail. Use subclass oracle.aurora.rdbms.OracleDBMSOutputStream or Java stored procedure DBMS_JAVA.SET_STREAMS. Call with SQLJ or JDBC. Use JDBC to execute an ALTER SESSION statement. Call with SQLJ or JDBC. Use JDBC. Use JDBC to execute an ALTER SESSION statement. Call with SQLJ or JDBC. Grant the JAVAUSERPRIV privilege and then use Java I/O entry points.
Part I
SQL for Application Developers
This part contains the following chapters:
Chapter 2, "SQL Processing for Application Developers" Chapter 3, "Using SQL Datatypes in Application Development" Chapter 4, "Using Regular Expressions in Oracle Database" Chapter 5, "Using Indexes in Application Development" Chapter 6, "Maintaining Data Integrity in Application Development"
2
SQL Processing for Application Developers
This chapter describes how Oracle Database processes SQL statements. Before reading this chapter you should read the section "SQL Processing" in Oracle Database Concepts. Topics include the following:
Grouping Operations into Transactions Ensuring Repeatable Reads with Read-Only Transactions Using Cursors within Applications Locking Data Explicitly About User Locks Using Serializable Transactions for Concurrency Control Autonomous Transactions Resuming Execution After a Storage Error Condition
Deciding How to Group Operations in Transactions Improving Transaction Performance Committing Transactions Rolling Back Transactions Defining Transaction Savepoints
Transactions must be defined properly so that work is accomplished in logical units and data is kept consistent. Data in all referenced tables should be in a consistent state before the transaction begins and after it ends. Transactions should consist of only the SQL statements or PL/SQL blocks that comprise one consistent change to the data.
SQL Processing for Application Developers 2-1
For example, suppose that you write a Web application that enables users to transfer funds between accounts. The transaction should include the debit to one account, which is executed by one SQL statement, and the credit to another account, which is executed by a second SQL statement. Both statements should fail or succeed together as a unit of work; the credit should not be committed without the debit. Other non-related actions, such as a new deposit to one account, should not be included in the same transaction.
Use the SET TRANSACTION command with the USE ROLLBACK SEGMENT clause to explicitly assign a transaction to a rollback segment. This technique can eliminate the need to allocate additional extents dynamically, which can reduce system performance. Note that this clause is relevant and valid only if you use rollback segments for undo. If you use automatic undo management, then Oracle Database ignores this clause. Establish standards for writing SQL statements so that you can take advantage of shared SQL areas. Oracle Database recognizes identical SQL statements and allows them to share memory areas. This reduces memory usage on the database server and increases system throughput. Use the ANALYZE command to collect statistics that can be used by Oracle Database to implement a cost-based approach to SQL statement optimization. You can supply additional "hints" to the optimizer as needed. Call the DBMS_APPLICATION_INFO.SET_ACTION procedure before beginning a transaction to register and name a transaction for later use when measuring performance across an application. You should specify which type of activity a transaction performs so that the system tuners can later see which transactions are taking up the most system resources. Increase user productivity and query efficiency by including user-written PL/SQL functions in SQL expressions as described in "Calling Stored Functions from SQL Expressions" on page 7-36. Create explicit cursors when writing a PL/SQL application. Reduce frequency of parsing and improve performance in precompiler programs by increasing the number of cursors with MAX_OPEN_CURSORS. Use the SET TRANSACTION command with the ISOLATION LEVEL set to SERIALIZABLE to get ANSI/ISO serializable transactions.
See Also:
"How Serializable Transactions Interact" on page 2-15 "Using Cursors within Applications" on page 2-6 Oracle Database Concepts for more information about transaction tuning features
Committing Transactions
To commit a transaction, use the COMMIT statement. The following two statements are equivalent and commit the current transaction:
The COMMIT statements lets you include the COMMENT parameter along with a comment that provides information about the transaction being committed. This option is useful for including information about the origin of the transaction when you commit distributed transactions:
COMMIT COMMENT 'Dallas/Accts_pay/Trans_type 10B';
The options in the COMMIT statement override the current settings in the initialization parameter. Table 21 describes redo persistence options that you can set in either location.
Table 21 Option WAIT NOWAIT IMMEDIATE BATCH Initialization Parameter and COMMIT Options for Managing Commit Redo Specifies that . . . The commit does not return as successful until the redo corresponding to the commit is persisted in the online redo logs (default). The commit should return to the application without waiting for the redo to be written to the online redo logs. The log writer process should write the redo for the commit immediately (default). In other words, this option forces a disk I/O. Oracle Database should buffer the redo. The log writer process is permitted to write the redo to disk in its own time.
The following example shows how to set the commit behavior to BATCH and NOWAIT in the initialization parameter file:
COMMIT_WRITE = BATCH, NOWAIT
You can change the commit behavior at the system level by executing ALTER SYSTEM as in the following example:
ALTER SYSTEM SET COMMIT_WRITE = BATCH, NOWAIT
2-3
After the initialization parameter is set, a COMMIT statement with no options conforms to the options specified in the parameter. Alternatively, you can override the current initialization parameter setting by specifying options directly on the COMMIT statement as in the following example:
COMMIT WRITE BATCH NOWAIT
In either case, your application specifies that log writer does not have to write the redo for the commit immediately to the online redo logs and should not wait for confirmation that the redo has been written to disk.
Note:
You cannot change the default IMMEDIATE and WAIT behavior for distributed transactions.
If your application uses OCI, then you can modify redo behavior by setting the following flags in the OCITransCommit() function within your application:
Note that the specification of the NOWAIT and BATCH options allows a small window of vulnerability in which Oracle Database can roll back a transaction that your application view as committed. Your application must be able to tolerate the following scenarios:
The database host crashes, which causes the database to lose redo that was buffered but not yet written to the online redo logs. A file I/O problem prevents log writer from writing buffered redo to disk. If the redo logs are not multiplexed, then the commit is lost.
See Also:
Oracle Database SQL Reference for information on the COMMIT statement Oracle Call Interface Programmer's Guide for information about the OCITransCommit() function
The WORK option of the ROLLBACK command has no function. To roll back to a savepoint defined in the current transaction, use the TO option of the ROLLBACK command. For example, either of the following statements rolls back the current transaction to the savepoint named POINT1:
SAVEPOINT Point1; ...
If you create a second savepoint with the same identifier as an earlier savepoint, the earlier savepoint is erased. After creating a savepoint, you can roll back to the savepoint. There is no limit on the number of active savepoints for each session. An active savepoint is one that has been specified since the last commit or rollback.
INSERT INTO...; Second DML statement of this transaction SAVEPOINT c; UPDATE...; ROLLBACK TO c; ROLLBACK TO b; ROLLBACK TO c; Third savepoint of this transaction Third DML statement of this transaction. UPDATE statement is rolled back, savepoint C remains defined INSERT statement is rolled back, savepoint C is lost, savepoint B remains defined ORA-01086 error; savepoint C no longer defined
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.
statement-level read consistency is used to provide transaction-level read consistency; all queries return information with respect to the system change number (SCN) determined when the read-only transaction begins. Because no data locks are acquired, other transactions can query and update data being queried concurrently by a read-only transaction. Long-running queries sometimes fail because undo information required for consistent read operations is no longer available. This happens when committed undo blocks are overwritten by active transactions. Automatic undo management provides a way to explicitly control when undo space can be reused; that is, how long undo information is retained. Your database administrator can specify a retention period by using the parameter UNDO_RETENTION.
See Also: Oracle Database Administrator's Guide for information on long-running queries and resumable space allocation
For example, if UNDO_RETENTION is set to 30 minutes, then all committed undo information in the system is retained for at least 30 minutes. This ensures that all queries running for 30 minutes or less, under usual circumstances, do not encounter the OER error, "snapshot too old." A read-only transaction is started with a SET TRANSACTION statement that includes the READ ONLY option. For example:
SET TRANSACTION READ ONLY;
The SET TRANSACTION statement must be the first statement of a new transaction; if any DML statements (including queries) or other non-DDL statements (such as SET ROLE) precede a SET TRANSACTION READ ONLY statement, an error is returned. Once a SET TRANSACTION READ ONLY statement successfully executes, only SELECT (without a FOR UPDATE clause), COMMIT, ROLLBACK, or non-DML statements (such as SET ROLE, ALTER SYSTEM, LOCK TABLE) are allowed in the transaction. Otherwise, an error is returned. A COMMIT, ROLLBACK, or DDL statement terminates the read-only transaction; a DDL statement causes an implicit commit of the read-only transaction and commits in its own transaction.
Each cursor requires virtual memory, so a session's total number of cursors is limited by the memory available to that process. A systemwide limit of cursors for each session is set by the initialization parameter named OPEN_CURSORS found in the parameter file (such as INIT.ORA).
See Also:
Explicitly creating cursors for precompiler programs can offer some advantages in tuning those applications. For example, increasing the number of cursors can often reduce the frequency of parsing and improve performance. If you know how many cursors may be required at a given time, then you can make sure you can open that many simultaneously.
The V$SQL_PLAN view contains the execution plan information for each child cursor loaded in the library cache. The V$SQL_PLAN_STATISTICS view provides execution statistics at the row source level for each child cursor. The V$SQL_PLAN_STATISTICS_ALL view contains memory 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.
See Also:
Closing Cursors
Closing a cursor means that the information currently in the associated private area is lost and its memory is deallocated. Once a cursor is opened, it is not closed until one of the following events occurs:
The user program terminates its connection to the server. If the user program is an OCI program or precompiler application, then it explicitly closes any open cursor during the execution of that program. (However, when this program terminates, any cursors remaining open are implicitly closed.)
2-7
Cancelling Cursors
Cancelling a cursor frees resources from the current fetch.The information currently in the associated private area is lost but the cursor remains open, parsed, and associated with its bind variables.
Note:
See Also: Oracle Call Interface Programmer's Guide for more information about cancelling cursors
You want transaction-level read consistency or "repeatable reads"where transactions query a consistent set of data for the duration of the transaction, knowing that the data has not been changed by any other transactions. This level of consistency can be achieved by using explicit locking, read-only transactions, serializable transactions, or overriding default locking for the system. A transaction requires exclusive access to a resource. To proceed with its statements, the transaction with exclusive access to a resource does not have to wait for other transactions to complete.
The automatic locking mechanisms can be overridden at the transaction level. Transactions including the following SQL commands override Oracle Database's default locking:
LOCK TABLE SELECT, including the FOR UPDATE clause SET TRANSACTION with the READ ONLY or ISOLATION LEVEL SERIALIZABLE options
Locks acquired by these statements are released after the transaction is committed or rolled back. The following sections describe each option available for overriding the default locking of Oracle Database. The initialization parameter DML_LOCKS determines the maximum number of DML locks allowed.
See Also:
Although the default value is usually enough, you might need to increase it if you use additional manual locks.
Caution: If you override the default locking of Oracle Database at any level, be sure that the overriding locking procedures operate correctly: Ensure that data integrity is guaranteed, data concurrency is acceptable, and deadlocks are not possible or are appropriately handled.
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.
You can also indicate if you do or do not want to wait to acquire the lock. If you specify the NOWAIT option, then you only acquire the table lock if it is immediately available. Otherwise an error is returned to notify that the lock is not available at this time. In this case, you can attempt to lock the resource at a later time. If NOWAIT is omitted, then the transaction does not proceed until the requested table lock is acquired. If the wait for a table lock is excessive, then you might want to cancel the lock operation and retry at a later time; you can code this logic into your applications.
ROW SHARE and ROW EXCLUSIVE table locks offer the highest degree of concurrency. You might use these locks if:
Your transaction needs to prevent another transaction from acquiring an intervening share, share row, or exclusive table lock for a table before the table can be updated in your transaction. 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 needs to prevent a table from being altered or dropped before the table can be modified later in your transaction.
SHARE 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 locks on the table either commit or roll back. Other transactions may acquire concurrent SHARE table locks on the same table, also allowing them the option of transaction-level read consistency.
2-9
Caution: Your transaction may or may 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.
For example, assume that two tables, EMP_TAB and BUDGET_TAB, require a consistent set of data in a third table, DEPT_TAB. For a given department number, you want to update the information in both of these tables, and ensure that no new members are added to the department between these two transactions. Although this scenario is quite rare, it can be accommodated by locking the DEPT_TAB table in SHARE MODE, as shown in the following example. Because the DEPT_TAB table is rarely updated, locking it probably does not cause many other transactions to wait long.
Note:
You may need to set up data structures similar to the following for certain examples to work:
CREATE TABLE dept_tab( deptno NUMBER(2) NOT NULL, dname VARCHAR2(14), loc VARCHAR2(13)); CREATE TABLE emp_tab ( empno NUMBER(4) NOT NULL, ename VARCHAR2(10), job VARCHAR2(9), mgr NUMBER(4), hiredate DATE, sal NUMBER(7,2), comm NUMBER(7,2), deptno NUMBER(2)); CREATE TABLE Budget_tab ( totsal NUMBER(7,2), deptno NUMBER(2) NOT NULL);
LOCK TABLE Dept_tab IN SHARE MODE; UPDATE Emp_tab SET sal = sal * 1.1 WHERE deptno IN (SELECT deptno FROM Dept_tab WHERE loc = 'DALLAS'); UPDATE Budget_tab SET Totsal = Totsal * 1.1 WHERE Deptno IN (SELECT Deptno FROM Dept_tab WHERE Loc = 'DALLAS'); COMMIT; /* This releases the lock */
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 behavior.
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.
Privileges Required
You can automatically acquire any type of table lock on tables in your schema. To acquire a table lock on a table in another schema, you must have the LOCK ANY TABLE system privilege or any object privilege (for example, SELECT or UPDATE) for the table.
Oracle Database SQL Reference for information on the SET TRANSACTION statement Oracle Database SQL Reference for information on the ALTER SESSION statements
The settings for these parameters should be changed only when an instance is shut down. If multiple instances are accessing a single database, then all instances should use the same setting for these parameters.
The return set for a SELECT... FOR UPDATE may 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 new 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 operations when rows are locked in a non-sequential order. To prevent such deadlocks, design your application so that any concurrent DML on the table does not affect the return set of the query. If this is not feasible, you may want to serialize queries in your application.
When acquiring row locks with SELECT... FOR UPDATE, you can specify the NOWAIT option to indicate that you are not willing to wait to acquire the lock. If you cannot acquire then lock immediately, an error is returned to signal that the lock is not possible at this time. You can try to lock the row again later. By default, the transaction waits until the requested row lock is acquired. If the wait for a row lock is too long, you can code logic into your application to cancel the lock operation and try again later.
Provide exclusive access to a device, such as a terminal Provide application-level enforcement of read locks Detect when a lock is released and cleanup after the application Synchronize applications and enforce sequential processing
Isolation Level
Not possible Possible Not possible Not possible Not possible Not possible
A transaction can read uncommitted data changed by another transaction. A transaction rereads data committed by another transaction and sees the new data. A transaction can execute a query again, and discover new rows inserted by another committed transaction.
The behavior of Oracle Database with respect to these isolation levels is summarized in Table 25.
Table 25 ANSI Isolation Levels and Oracle Database Description
Isolation Level
READ UNCOMMITTED Oracle Database never permits "dirty reads." Although some other database products use this undesirable technique to improve thoughput, it is not required for high throughput with Oracle Database. READ COMMITTED Oracle Database meets the READ COMMITTED isolation standard. This is the default mode for all Oracle Database applications. Because an Oracle Database query only sees data that was committed at the beginning of the query (the snapshot time), Oracle Database actually offers more consistency than is required by the ANSI/ISO SQL92 standards for READ COMMITTED isolation. Oracle Database does not normally support this isolation level, except as provided by SERIALIZABLE. Oracle Database does not normally support this isolation level, except as provided by SERIALIZABLE.
Commit the work executed to that point Execute additional, different, statements, perhaps after rolling back to a prior savepoint in the transaction Roll back the entire transaction and try it again
Oracle Database stores control information in each data block to manage access by concurrent transactions. To use the SERIALIZABLE isolation level, you must use the INITRANS clause of the CREATE TABLE or ALTER TABLE command to set aside storage for this control information. To use serializable mode, INITRANS must be set to at least 3.
TRANSACTION A
(arbitrary) begin work update row 2 in block 1 Issue update "too recent" for B to see
TRANSACTION B (serializable)
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE read row 1 in block 1
insert row 4
commit
B can see its own changes but not the committed changes of transaction A.
re-read updated row 1 in block 1 search for row 4 (not found) read old row 2 in block 1
Failure on attempt to update row updated and committed since transaction B began TIME
Oracle Database Reference for the complete syntax of the SET TRANSACTION and ALTER SESSION commands
SERIALIZABLE, then you must use the ALTER TABLE command to set INITRANS to at least 3. This parameter causes Oracle Database to allocate sufficient storage in each block to record the history of recent transactions that accessed the block. Higher values should be used for tables that will undergo many transactions updating the same blocks.
TRANSACTION A
TRANSACTION B
delete parent
commit work
commit work
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. This scenario leaves a child row in the database with no corresponding parent row. This result occurs even if both A and B are SERIALIZABLE transactions, because neither transaction prevents the other from making changes in the data it reads to check consistency.
As this example shows, sometimes you must take steps to ensure that the data read by one transaction is not concurrently written by another. This requires a greater degree of transaction isolation than defined by SQL92 SERIALIZABLE mode.
Transaction A can use SELECT FOR UPDATE to query and lock the parent row and thereby prevent transaction B from deleting the row. Transaction B can prevent Transaction A from gaining access to the parent row by reversing the order of its processing steps. Transaction B first deletes the parent row, and then rolls back if its subsequent query detects the presence of corresponding rows in the child table.
Referential integrity can also be enforced in Oracle Database using database triggers, instead of a separate query as in Transaction A. For example, an INSERT into the child table can fire a BEFORE INSERT row-level trigger to check for the corresponding parent row. The trigger queries the parent table using SELECT FOR UPDATE, ensuring that parent row (if it exists) remains in the database for the duration of the transaction inserting the child row. If the corresponding parent row does not exist, the trigger rejects the insert of the child row. SQL statements issued by a database trigger execute in the context of the SQL statement that caused the trigger to fire. All SQL statements executed within a trigger see the database in the same state as the triggering statement. Thus, in a READ COMMITTED transaction, the SQL statements in a trigger see the database as of the beginning of the triggering statement execution, and in a transaction executing in SERIALIZABLE mode, the SQL statements see the database as of the beginning of the transaction. In either case, the use of SELECT FOR UPDATE by the trigger correctly enforces referential integrity.
A collection of database tables (or any set of data) A sequence of reads of rows in those tables The set of transactions committed at any moment
An operation (a query or a transaction) is transaction set consistent if its read operations all return data written by the same set of committed transactions. When an operation is not transaction set consistent, some reads reflect the changes of one set of transactions, and other reads reflect changes made by other transactions. Such an operation sees the database in a state that reflects no single set of committed transactions.
Oracle Database transactions executing in READ COMMITTED mode are transaction-set consistent on an individual-statement basis, because all rows read by a query must be committed before the query begins. Oracle Database transactions executing in SERIALIZABLE mode are transaction set consistent on an individual-transaction basis, because all statements in a SERIALIZABLE transaction execute on an image of the database as of the beginning of the transaction. In other database systems, a single query run in READ COMMITTED mode provides results that are not transaction set consistent. The query is not transaction set consistent, because it may see only a subset of the changes made by another transaction. For example, a join of a master table with a detail table could see a master record inserted by another transaction, but not the corresponding details inserted by that transaction, or vice versa. The READ COMMITTED mode avoids this problem, and so provides a greater degree of consistency than read-locking systems. In read-locking systems, at the cost of preventing concurrent updates, SQL92 REPEATABLE READ isolation provides transaction set consistency at the statement level, but not at the transaction level. The absence of phantom protection means two queries issued by the same transaction can see data committed by different sets of other transactions. Only the throughput-limiting and deadlock-susceptible SERIALIZABLE mode in these systems provides transaction set consistency at the transaction level.
Autonomous Transactions
When you get this error, roll back the current transaction and execute it again. The transaction gets a new transaction snapshot, and the operation is likely to succeed. To minimize the performance overhead of rolling back transactions and executing them again, try to put DML statements that might conflict with other concurrent transactions near the beginning of your transaction.
Autonomous Transactions
This section gives a brief overview of autonomous transactions and what you can do with them.
See Also: Oracle Database PL/SQL User's Guide and Reference and Chapter 9, "Coding Triggers" for detailed information on autonomous transactions
Autonomous Transactions
At times, you may want to commit or roll back some changes to a table independently of a primary transaction's final outcome. For example, in a stock purchase transaction, you may want to commit a customer's information regardless of whether the overall stock purchase actually goes through. Or, while running that same transaction, you may want to log error messages to a debug table even if the overall transaction rolls back. Autonomous transactions allow you to do such tasks. An autonomous transaction (AT) is an independent transaction started by another transaction, the main transaction (MT). It lets you suspend the main transaction, do SQL operations, commit or roll back those operations, then resume the main transaction. An autonomous transaction executes within an autonomous scope. An autonomous scope is a routine you mark with the pragma (compiler directive) AUTONOMOUS_TRANSACTION. The pragma instructs the PL/SQL compiler to mark a routine as autonomous (independent). In this context, the term routine includes:
Top-level (not nested) anonymous PL/SQL blocks Local, standalone, and packaged functions and procedures Methods of a SQL object type PL/SQL triggers
Figure 23 shows how control flows from the main routine (MT) to an autonomous routine (AT) and back again. As you can see, the autonomous routine can commit more than one transaction (AT1 and AT2) before control returns to the main routine.
Figure 23 Transaction Control Flow
Main Routine
PROCEDURE proc1 IS emp_id NUMBER; BEGIN emp_id := 7788; INSERT ... SELECT ... proc2; DELETE ... COMMIT; END;
Autonomous Routine
PROCEDURE proc2 IS PRAGMA AUTON... dept_id NUMBER; BEGIN dept_id := 20; UPDATE ... INSERT ... UPDATE ... COMMIT; INSERT ... INSERT ... COMMIT; END;
MT begins
MT ends
When you enter the executable section of an autonomous routine, the main routine suspends. When you exit the routine, the main routine resumes. COMMIT and ROLLBACK end the active autonomous transaction but do not exit the autonomous routine. As Figure 23 shows, when one transaction ends, the next SQL statement begins another transaction. A few more characteristics of autonomous transactions:
The changes autonomous transactions effect do not depend on the state or the eventual disposition of the main transaction. For example: An autonomous transaction does not see any changes made by the main transaction.
Autonomous Transactions
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. This means that users can access the updated information without having to wait for the main transaction to commit. Autonomous transactions can start other autonomous transactions.
Figure 24 illustrates some of the possible sequences autonomous transactions can follow.
Figure 24 Possible Sequences of Autonomous Transactions
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. At Scope 1 commits or rolls back Tx1.1, than ends. MTx resumes. MTx invokes AT Scope 2. MT suspends, passing control to AT Scope 2 which, initially, is performing queries. AT Scope 2 then begins Tx2.1 by, say, doing an update. AT Scope 2 commits or rolls back Tx2.1. Later, AT Scope 2 begins a second transaction, Tx2.2, then commits or rolls it back. AT Scope 2 performs a few queries, then ends, passing control back to MTx. MTx invokes AT Scope 3. MTx suspends, AT Scope 3 begins. AT Scope 3 begins Tx3.1 which, in turn, invokes AT Scope 4. Tx3.1 suspends, AT Scope 4 begins. AT Scope 4 begins Tx4.1, commits or rolls it back, then ends. AT Scope 3 resumes. AT Scope 3 commits or rolls back Tx3.1, then ends. MTx resumes. Finally, MT Scope commits or rolls back MTx, then ends.
MT Scope MTx
AT Scope 1
AT Scope 2
AT Scope 3
AT Scope 4
Tx1.1 MTx
Tx2.1
Tx2.2
MTx
Tx3.1
Tx4.1
Tx3.1 MTx
Autonomous Transactions
As these examples illustrate, there are four possible outcomes that can occur when you use autonomous and main transactions. Table 27 presents these possible outcomes. As you can see, there is no dependency between the outcome of an autonomous transaction and that of a main transaction.
Table 27 Possible Transaction Outcomes
Autonomous Transaction Main Transaction Commits Commits Rolls back Rolls back Commits Rolls back Commits Rolls back
MT Scope
AT Scope
ATx
Scenario 1: There are sufficient funds to cover the withdrawal and therefore the bank releases the funds Scenario 2: There are insufficient funds to cover the withdrawal, but the customer has overdraft protection. The bank therefore releases the funds. Scenario 3: There are insufficient funds to cover the withdrawal, the customer does not have overdraft protection, and the bank therefore withholds the requested funds.
Scenario 1
There are sufficient funds to cover the withdrawal and therefore the bank releases the funds. This is illustrated by Figure 26.
SQL Processing for Application Developers 2-23
Autonomous Transactions
MT Scope MTx
AT Scope 1
AT Scope 2
Tx1.1 inserts the transaction ID into the audit table and commits.
Tx1.1
MTx
Tx2.1, updates the audit table using the transaction ID generated above, then commits.
Tx2.1
MTx
Scenario 2
There are insufficient funds to cover the withdrawal, but the customer has overdraft protection. The bank therefore releases the funds. This is illustrated by Figure 27.
Autonomous Transactions
MT Scope MTx
AT Scope 1
AT Scope 2
Tx1.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. Tx2.1, updates the audit table.
MTx
Tx2.1
MTx
Scenario 3
There are insufficient funds to cover the withdrawal, the customer does not have overdraft protection, and the bank therefore withholds the requested funds. This is illustrated by Figure 28.
Autonomous Transactions
MT Scope MTx
AT Scope 1
AT Scope 2
Tx1.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. Tx2.1, updates the audit table.
MTx
Tx2.1
MTx Scope rolls back MTx, denying the release of funds. MT Scope ends.
MTx
This section is provided here to round out your general understanding of autonomous transactions. For a more thorough understanding of autonomous transactions, refer to Oracle Database PL/SQL User's Guide and Reference.
To define autonomous transactions, you use the pragma (compiler directive) AUTONOMOUS_TRANSACTION. The pragma instructs the PL/SQL compiler to mark the procedure, function, or PL/SQL block as autonomous (independent). You can code the pragma anywhere in the declarative section of a procedure, function, or PL/SQL block. But, for readability, code the pragma at the top of the section. The syntax follows:
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN --add appropriate code END; -- add additional functions and packages... END Banking;
You cannot use the pragma to mark all subprograms in a package (or all methods in an object type) as autonomous. Only individual routines can be marked autonomous. For example, the following pragma is illegal:
CREATE OR REPLACE PACKAGE Banking AS PRAGMA AUTONOMOUS_TRANSACTION; -- illegal FUNCTION Balance (Acct_id INTEGER) RETURN REAL; END Banking;
You cannot execute a PIPE ROW statement in your autonomous routine while your autonomous transaction is open. You must close the autonomous transaction before executing the PIPE ROW statement. This is normally accomplished by committing or rolling back the autonomous transaction before executing the PIPE ROW statement.
See Also:
Out of space errors, such as ORA-01653. Space limit errors, such as ORA-01628. Space quota errors, such as ORA-01536.
segments, or the maximum number of extents while creating an index or a table. Use locally managed tablespaces and automatic undo management in combination with this feature.
Parse the error message with the DBMS_RESUMABLE.SPACE_ERROR_INFO function. For details about this function, refer to Oracle Database PL/SQL Packages and Types Reference. Set a new timeout value with the SET_TIMEOUT procedure.
Within the body of the trigger, you can perform any notifications, such as sending a mail message to alert an operator to the space problem. Alternatively, the DBA can periodically check for suspended statements using the data dictionary views DBA_RESUMABLE, USER_RESUMABLE, and V$_SESSION_WAIT. When the space condition is corrected (usually by the DBA), the suspended statement automatically resumes execution. If it is not corrected before the timeout period expires, the operation causes a SERVERERROR exception. To reduce the chance of out-of-space errors within the trigger itself, you must declare it as an autonomous transaction so that it uses a rollback segment in the SYSTEM tablespace. If the trigger encounters a deadlock condition because of locks held by the suspended statement, the trigger is aborted and your application receives the original error condition, as if it was never suspended. If the trigger encounters an out-of-space condition, the trigger and the suspended statement are rolled back. You can prevent the rollback through an exception handler in the trigger, and just wait for the statement to be resumed.
See Also: Oracle Database Reference for details on the DBA_RESUMABLE, USER_RESUMABLE, and V$_SESSION_WAIT data dictionary views
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 sys.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 := 'Subject: 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;
3
Using SQL Datatypes in Application Development
This chapter discusses how to use SQL datatypes in database applications. Topics include the following:
Representing Data with SQL Datatypes: Overview Representing Character Data Representing Numeric Data Representing Datetime Data Representing Specialized Data Representing Conditional Expressions as Data Identifying Rows by Address How Oracle Database Converts Datatypes
See Also:
Oracle Database Application Developer's Guide - Object-Relational Features for information about more complex types, such as object types, varrays, and nested tables Oracle Database Application Developer's Guide - Large Objects for information about LOB datatypes Oracle Database PL/SQL User's Guide and Reference to learn about the PL/SQL datatypes. Many SQL datatypes are the same or similar in PL/SQL.
3-1
Oracle built-in datatypes, which include datatypes for characters, numbers, dates and times (known as datetime datatypes), raw data, large objects (LOBs), and row addresses (ROWIDs). ANSI datatypes and datatypes from the IBM products SQL/DS and DB2, which are usable in SQL statements that create tables and clusters User-defined types, which use Oracle built-in datatypes and other user-defined datatypes as the building blocks of object types that model the structure and behavior of data in applications Oracle-supplied types, which are SQL-based interfaces for defining new types
The Oracle precompilers recognize other datatypes in embedded SQL programs. These datatypes are called external datatypes and are associated with host variables. You should not confuse Oracle Database built-in datatypes and user-defined types with external datatypes.
See Also:
Oracle Database SQL Reference for complete reference information on the SQL datatypes Pro*COBOL Programmer's Guide and Pro*C/C++ Programmer's Guide for information on external datatypes, including how Oracle converts between them and built-in or user-defined types Oracle Database Concepts to learn about Oracle built-in datatypes
Representing Character Data: Overview Specifying Column Lengths as Bytes or Characters Choosing Between the CHAR and VARCHAR2 Datatypes Using Character Literals in SQL Statements
CHAR and NCHAR datatypes store fixed-length character literals. VARCHAR2 and NVARCHAR2 datatypes store variable-length character literals. NCHAR and NVARCHAR2 datatypes store Unicode character data only. CLOB and NCLOB datatypes store single-byte and multibyte character strings of up to (4 gigabytes - 1) * (the value obtained from DBMS_LOB.GETCHUNKSIZE). The LONG datatype stores variable-length character strings containing up to two gigabytes, but with many restrictions. This datatype is provided only for backward compatibility with existing applications. In general, new applications should use CLOB and NCLOB datatypes to store large amounts of character data, and BLOB and BFILE to store large amounts of binary data.
See Also:
Oracle Database Application Developer's Guide - Large Objects for information on LOB datatypes (including CLOB and NCLOB datatypes) and migration from LONG to LOB datatypes Oracle Database SQL Reference for restrictions on LONG datatypes
name VARCHAR2(32 CHAR) The name column contains data in the database character set. If the database character set allows multibyte characters, then the 32 characters can be stored as more than 32 bytes.
biography NVARCHAR2(2000) The biography column can represent 2000 characters in any Unicode-representable language. The encoding depends on the national character set, but the column can contain multibyte values even if the database character set is single-byte.
comment VARCHAR2(2000) The representation of comment as 2000 bytes or characters depends on 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 there generally is no such correspondence. A character might consist of one or more bytes, depending upon 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:
Space usage To store data more efficiently, use the VARCHAR2 datatype. The CHAR datatype blank-pads and stores trailing blanks up to a fixed column length for all column values, whereas the VARCHAR2 datatype does not add extra blanks.
Using SQL Datatypes in Application Development 3-3
Comparison semantics Use the CHAR datatype when you require ANSI compatibility in comparison semantics (when trailing blanks are not important in string comparisons). Use the VARCHAR2 when trailing blanks are important in string comparisons.
Future compatibility The CHAR and VARCHAR2 datatypes are fully supported. At this time, the VARCHAR datatype automatically corresponds to the VARCHAR2 datatype and is reserved for future use.
When an application interfaces with Oracle Database, there is a character set on the client and server side. Oracle Database uses the NLS_LANGUAGE parameter to automatically convert CHAR, VARCHAR2, and LONG data from the database character set to the character set defined for the user session, if these are different. In the section "Datatype Comparison Rules," Oracle Database SQL Reference explains the comparison semantics that Oracle Database uses to compare character data. Because Oracle Database blank-pads values stored in CHAR columns but not in VARCHAR2 columns, a value stored in a VARCHAR2 column can take up less space than the same value in a CHAR column. For this reason, a full table scan on a large table containing VARCHAR2 columns may 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 may be able to improve performance by storing data in VARCHAR2 rather than in CHAR columns. Performance is not the only factor to consider when deciding which datatype to use. Oracle Database uses different semantics to compare values of each datatype. You might choose one datatype over the other if your application is sensitive to the differences between these semantics. For example, if you want Oracle Database to ignore trailing blanks when comparing character values, then you must store these values in CHAR columns.
See Also:
Oracle Database SQL Reference for more information on comparison semantics for these datatypes
Character literals with the 'text' notation, as in the literals 'users01.dbf' and 'Muthu''s computer'. National character literals with the N'text' or n'text' notation, where N or n specifies the literal using the national character set. For example, N'rsum' is a National character literal. Oracle Database translates N-quoted text into the national character set by way of the database character set. If client-side characters do not have corresponding encoding in the database character set, then Oracle Database converts them into question marks. To avoid the potential loss of data during the text literal conversion, set the environment variable $ORA_NCHAR_LITERAL_REPLACE to TRUE. This setting transparently replaces the N'text' internally and preserves the text literal for SQL processing.
The UNISTR function provides support for Unicode character literals by enabling you to specify the Unicode encoding value of characters in the string, as in UNISTR('\1234'). This technique is useful, for example, when inserting data into
NCHAR columns. Because every character has a corresponding Unicode encoding, the client application can safely send character data to the server without data loss.
See Also:
Oracle Database Globalization Support Guide to learn about national character sets Oracle Database SQL Reference to learn about character literals
What Are the Numeric Datatypes? Using Floating-Point Number Formats Using Comparison Operators for Native Floating-Point Datatypes Performing Arithmetic Operations with Native Floating-Point Datatypes Using Conversion Functions with Native Floating-Point Datatypes Client Interfaces for Native Floating-Point Datatypes
Use the NUMBER datatype to store real numbers in a fixed-point or floating-point format. Numbers using this datatype are guaranteed to be portable among different Oracle Database platforms, and offer up to 38 decimal digits of precision. You can store positive and negative numbers of magnitude 1 x 10-130 through 9.99 x10125, as well as zero, in a NUMBER column.
3-5
The BINARY_FLOAT and BINARY_DOUBLE datatypes store floating-point data in the 32-bit IEEE 754 format and the double precision 64-bit IEEE 754 format respectively. Compared to the Oracle NUMBER datatype, arithmetic operations on floating-point data are usually faster for BINARY_FLOAT and BINARY_DOUBLE. Also, high-precision values require less space when stored as BINARY_FLOAT and BINARY_DOUBLE. In client interfaces supported by Oracle Database, the native instruction set supplied by the hardware vendor performs arithmetic operations on BINARY_FLOAT and BINARY_DOUBLE datatypes. The term native floating-point datatypes refers to datatypes including BINARY_FLOAT and BINARY_DOUBLE and to all implementations of these types in supported client interfaces. The floating-point number system is a common way of representing and manipulating numeric values in computer systems. A floating-point number is characterized by the following components:
A floating-point value is the signed product of its significand and the base raised to the power of its exponent, as shown in the formula in Example 31.
Example 31 Components of a Floating-Point Number (-1)sign
.
significand
base
exponent
For example, the number 4.31 can be represented in the following expression:
(-1)0
.
431
10
-2
0 is the binary-valued sign 431 is the significant 10 is the base -2 is the exponent
See Also:
Oracle Database Concepts for information about the internal format for the NUMBER datatype
Oracle Database SQL Reference for more information about the BINARY_FLOAT and BINARY_DOUBLE datatypes formats
values that a format can represent. A floating-point number that uses more precision than available with a given format is rounded. A floating-point number can be represented in a binary system (one that uses base 2), as in the IEEE 754 standard, or in a decimal system (one that uses base 10), such as Oracle NUMBER. The base affects many properties of the format, including how a numeric value is rounded. For a decimal floating-point number format like Oracle NUMBER, rounding is done to the nearest decimal place (for example. 1000, 10, or 0.01). The IEEE 754 formats use a binary format for floating-point values and round numbers to the nearest binary place (for example: 1024, 512, or 1/64). The native floating-point datatypes supported by the database round to the nearest binary place, so they are not satisfactory for applications that require decimal rounding. Use the Oracle NUMBER datatype for applications in which decimal rounding is required on floating-point data.
The leading bit of the significand, b0, must be set (1), except for subnormal numbers (explained later). Consequently, the leading bit is not actually stored, so the formats provide N bits of precision although only N-1 bits are stored.
Note:
The IEEE 754 specification also defines extended single-precision and extended double-precision formats, which are not supported by Oracle Database.
3-7
The storage parameters for the formats are described in Table 33. The in-memory formats for single-precision and double-precision datatypes are specified by IEEE 754.
Table 33 Datatype Single-precision Double-precision Summary of Binary Format Storage Parameters Sign bits 1 1 Exponent bits 8 11 Significand bits 24 (23 stored) 53 (52 stored) Total bits 32 64
A significand is normalized when the leading bit of the significand is set. IEEE 754 defines denormal or subnormal values as numbers that are too small to be represented with an implied leading set bit in the significand. The number is too small because its exponent would be too large if its significand were normalized to have an implied leading bit set. IEEE 754 formats support subnormal values. Subnormal values preserve the following property: if: x - y == 0.0 (using floating-point subtraction) then: x == y Table 34 shows the range and precision of the required formats in the IEEE 754 standard and those of Oracle NUMBER. Range limits are expressed here in terms of positive numbers; they also apply to the absolute value of a negative number. (The notation "number e exponent" used here stands for number multiplied by 10 raised to the exponent power: number . 10 exponent.)
Table 34 Range and Precision Max positive normal number Min positive normal number Max positive subnormal number Min positive subnormal number Precision (decimal digits)
1
Range and Precision of IEEE 754 formats Single-precision 32-bit1 3.40282347e+38 1.17549435e-38 1.17549421e-38 1.40129846e-45 6-9 Double-precision 64-bit1 Oracle NUMBER Datatype
1.7976931348623157e+308 < 1.0e126 2.2250738585072014e-308 1.0e-130 2.2250738585072009e-308 not applicable 4.9406564584124654e-324 not applicable 15 - 17 38 - 40
These numbers are quoted from the IEEE Numerical Computation Guide.
See Also:
Oracle Database SQL Reference, section "Numeric Literals", for information about literal representation of numeric values Oracle Database SQL Reference for more information about floating-point formats
NaN represent results of operations that are undefined. Many bit patterns in IEEE 754 represent NaN. Bit patterns can represent NaN with and without the sign bit set. IEEE 754 distinguishes between signalling NaNs and quiet NaNs. IEEE 754 specifies behavior for when exceptions are enabled and disabled. Oracle Database does not allow exceptions to be enabled; the database behavior is that specified by IEEE 754 for when exceptions are disabled. In particular, Oracle Database makes no distinction between signalling and quiet NaNs. Programmers who use OCI can retrieve NaN values from Oracle Database; whether a retrieved NaN value is signalling or quiet depends on the client platform and beyond the control of Oracle Database. IEEE 754 does not define the bit pattern for either type of NaN. Positive infinity, negative infinity, positive zero, and negative zero are each represented by a specific bit pattern. Ignoring signs, there are the following classes of values, with each of the classes except for NaN greater than the one preceding it in the list:
In IEEE 754, NaN is unordered with other classes of special values and with itself. Behavior of Special Values for Native Floating-Point Datatypes When used with the database, special values of native floating-point datatypes behave as follows:
All NaNs are quiet. IEEE 754 exceptions are not raised. NaN is ordered as follows: All non-NaN < NaN Any NaN == any other NaN
-0 is converted to +0. All NaNs are converted to the same bit pattern.
See Also: "Using Comparison Operators for Native Floating-Point Datatypes" on page 3-9 for more information on NaN compared to other values
3-9
Greater than Greater than or equal to Less than Less than or equal to Unordered
Comparisons ignore the sign of zero (-0 is equal to, not less than, +0). In Oracle Database, NaN is equal to itself. NaN is greater than everything except itself. That is, NaN == NaN and NaN > x, unless x is NaN.
See Also: "Behavior of Special Values for Native Floating-Point Datatypes" on page 3-9 for more information on comparison results, ordering, and other behaviors of special values
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 require such behavior. The standard allows for the result of operations, including arithmetic, to be delivered to a destination that uses a range greater than that used by the operands to the operation. You can compute the result of a double-precision multiplication at an extended double-precision destination. When this is done, 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. This occurrence may result in a double-rounding error in which the least significant bit of the result is incorrect. This state of affairs can only occur for double-precision multiplication and division on hardware that implements the IA-32 and IA-64 instruction set architecture. Thus, with the exception of this case, arithmetic for these datatypes 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.
TO_BINARY_DOUBLE, which converts float to double, decimal (string) to double, and float or double to integer-valued double TO_BINARY_FLOAT, which converts double to float, decimal (string) to float, and float or double to integer-valued float TO_CHAR, which converts float or double to decimal (string) TO_NUMBER, which converts a float, double, or string to a number
Oracle Database can raise exceptions during conversion. The IEEE 754 specification defines the following exceptions:
Oracle Database does not raise these exceptions for native floating-point datatypes. Generally, situations that would raise an exception produce the values described in Table 35.
Table 35 Exception Underflow Overflow Invalid Operation Divide by Zero Inexact Values Resulting from Exceptions Value 0 -INF, +INF NaN -INF, +INF, NaN Any value rounding was performed
Representing Datetime Data: Overview Manipulating the Date Format Manipulating the Time Format Performing Date Arithmetic Converting Between Datetime Types Importing and Exporting Datetime Types
DATE TIMESTAMP TIMESTAMP WITH TIME ZONE TIMESTAMP WITH LOCAL TIME ZONE
To change on an instance-wide basis, use the NLS_DATE_FORMAT parameter. To change during a session, use the ALTER SESSION statement.
To enter dates that are not in the current default date format, use the TO_DATE function with a format mask. For example:
SELECT TO_CHAR(TO_DATE('27-OCT-98', 'DD-MON-RR') ,'YYYY') "Year" FROM DUAL;
Be careful when using a date format such as DD-MON-YY. The YY indicates the year in the current century. For example, 31-DEC-92 is December 31, 2092, not 1992 as
you might expect. If you want to indicate years in any century other than the current one, use a different format mask, such as the default RR.
See Also:
Oracle Database Concepts for information about Julian dates. Oracle Database Julian dates might not be compatible with Julian dates generated by other date algorithms.
By default, the time in a DATE column is 12:00:00 A.M. (midnight) if no time portion is entered or if the DATE is truncated. In a time-only entry, the date portion defaults to the first day of the current month. To enter the time portion of a date, use the TO_DATE function with a format mask indicating the time portion, as shown in Example 33.
Example 33 Indicating Time with the TO_DATE Function -- create test table CREATE TABLE birthdays ( Bname VARCHAR2(20), Bday DATE ); -- insert a row INSERT INTO birthdays (bname, bday) VALUES ( 'ANNIE', TO_DATE('13-NOV-92 10:56 A.M.','DD-MON-YY HH:MI A.M.') );
ADD_MONTHS function, which returns the date plus the specified number of months.
SYSDATE function, which returns the current date and time set for the operating system on which the database resides. SYSTIMESTAMP function, which returns the system date, including fractional seconds and time zone, of the system on which the database resides. TRUNC function, which when applied to a DATE value, trims off the time portion so that it represents the very beginning of the day (the stroke of midnight). By truncating two DATE values and comparing them, you can determine whether they refer to the same day. You can also use TRUNC along with a GROUP BY clause to produce daily totals. Arithmetic operators such as + and -. For example, SYSDATE-7 refers to 7 days before the current system date. INTERVAL datatypes, which enable you to represent constants when performing date arithmetic rather than performing your own calculations. For example, you can add or subtract INTERVAL constants from DATE values or subtract two DATE values and compare the result to an INTERVAL. Comparison operators such as >, <, =, and BETWEEN.
EXTRACT, which extracts and returns the value of a specified datetime field from a datetime or interval value expression NUMTODSINTERVAL, which converts a NUMBER or expression that can be implicitly converted to a NUMBER value to an INTERVAL DAY TO SECOND literal NUMTOYMINTERVAL, which converts a NUMBER or expression that can be implicitly converted to a NUMBER value to an INTERVAL YEAR TO MONTH literal TO_DATE, which converts character data to a DATE datatype TO_CHAR, which converts DATE data to character data TO_DSINTERVAL, which converts a character string to an INTERVAL DAY TO SECOND value TO_TIMESTAMP, which converts character data to a value of TIMESTAMP datatype TO_TIMESTAMP_TZ, which converts character data to a value of TIMESTAMP WITH TIME ZONE datatype TO_YMINTERVAL, which converts a character string to an INTERVAL YEAR TO MONTH type
See Also: function
Representing Geographic Data Representing Multimedia Data Representing Large Amounts of Data Representing Searchable Text Representing XML Representing Dynamically Typed Data Representing Data with ANSI/ISO, DB2, and SQL/DS Datatypes
Extracting metadata and attributes from multimedia data Retrieving and managing multimedia data from Oracle interMedia, Web servers, file systems, and other servers Performing manipulation operations on image data
See Also: Oracle interMedia Referenceto learn about the interMedia types
Table 36
Large Object Datatypes Description Represents large amounts of binary data such as images, video, or other multimedia data.
Character large object Represents large amounts of character data. CLOB types are stored by using the database character set. Note that the database stores a CLOB up to 4,000 bytes inline as a VARCHAR2. If the CLOB exceeds this length, then the database moves the CLOB out of line. National character set Represents large amounts of character data in National large objects Character Set format. External large object Stores objects in the operating system's file system, outside of the database files or tablespace. Note that the BFILE type is read-only; other LOB types are read/write. BFILE objects are also sometimes referred to as external LOBs.
NCLOB BFILE
An instance of type BLOB, CLOB, or NCLOB can exist as either a persistent LOB instance or a temporary LOB instance. Persistent and temporary instances differ as follows:
A temporary LOB instance is declared in the scope of your application. A persistent LOB instance is created and stored in the database.
With the exception of declaring, freeing, creating, and committing, operations on persistent and temporary LOB instances are performed the same way.
See Also: Oracle Database Application Developer's Guide - Large Objects for more details on using LOBs in applications
See Also:
See Oracle Database Application Developer's Guide - Large Objects for information about the BLOB and BFILE datatypes See the Oracle Database SQL Reference for restrictions on LONG and LONG RAW datatypes
Representing XML
If you have information stored as files in XML format, or if you want to take an object type and store it as XML, then you can use the XMLType built-in type. XMLType columns store their data as CLOBs. You can take an existing CLOB, VARCHAR2, or any object type, and call the XMLType constructor to turn it into an XML object. When an XML object is inside the database, you can use queries to traverse it (using the XML XPath notation) and extract all or part of its data. You can also produce XML output from existing relational data and split XML documents across relational tables and columns. You can use the following packages to transfer XML data into and out of relational tables:
DBMS_XMLQUERY, which provides database-to-XMLType functionality DBMS_XMLGEN, which converts the results of a SQL query to a canonical XML format DBMS_XMLSAVE, which provides XML to database-type functionality
EXTRACT, which applies a VARCHAR2 XPath string and returns an XMLType instance containing an XML fragment SYS_XMLAGG, which aggregates all of the XML documents or fragments represented by an expression and produces a single XML document SYS_XMLGEN, which takes an expression that evaluates to a particular row and column of the database, and returns an instance of type XMLType containing an XML document UPDATEXML, which takes as arguments an XMLType instance and an XPath-value pair and returns an XMLType instance with the updated value XMLAGG, which takes a collection of XML fragments and returns an aggregated XML document XMLCOLATTVAL, which creates an XML fragment and then expands the resulting XML so that each XML fragment has the name column with the attribute name
XMLCONCAT, which takes as input a series of XMLType instances, concatenates the series of elements for each row, and returns the concatenated series XMLELEMENT, which takes an element name for identifier, an optional collection of attributes for the element, and arguments that make up the content of the element XMLFOREST, which converts each of its argument parameters to XML, and then returns an XML fragment that is the concatenation of these converted arguments XMLSEQUENCE, which either takes as input an XMLType instance and returns a varray of the top-level nodes in the XMLType, or takes as input a REFCURSOR instance, with an optional instance of the XMLFormat object, and returns as an XMLSequence type an XML document for each row of the cursor XMLTRANSFORM, which takes as arguments an XMLType instance and an XSL style sheet, applies the style sheet to the instance, and returns an XMLType
See Also:
Oracle XML DB Developer's Guide for details about the XMLType datatype Oracle XML Developer's Kit Programmer's Guide for information about client-side programming with XML Oracle Database SQL Reference for information about XML functions
SYS.ANYDATA.ConvertObject(Employee_type(5555, 'john'))); COMMIT; 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; /* The typecode is a number that signifies what type is 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 */ ); /* Now we compare the typecode against constants from DBMS_TYPES to see what kind of data we have, and decide how to display it. */ CASE v_typecode WHEN DBMS_TYPES.TYPECODE_NUMBER THEN IF v_type IS NOT NULL -- This condition should never happen, but we check just in case. THEN 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(); -- An object type's name is qualified with the schema name. IF v_typename NOT IN ( 'HR.EMPLOYEE_TYPE' ) -- If we encounter any object type besides EMPLOYEE_TYPE, raise an exception. 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; /
The query and procedure in Example 34 produce output like that shown in Example 35.
Example 35 Sample Output for Example 34 SQL> SELECT t.data.gettypename() AS "Type Name" FROM mytab t; Type Name -------------------------------------------------------------------------------SYS.NUMBER HR.EMPLOYEE_TYPE SQL> EXEC p; 1: NUMBER = 5 2: user-defined type = HR.EMPLOYEE_TYPE ( 5555, john )
You can access the same features through the OCI interface by using the OCIType, OCIAnyData, and OCIAnyDataSet interfaces.
See Also:
Oracle Database PL/SQL Packages and Types Reference for details about the DBMS_TYPES package Oracle Database Application Developer's Guide - Object-Relational Features for information and examples using the ANYDATA, ANYDATASET, and ANYTYPE types Oracle Call Interface Programmer's Guide for details about the OCI interfaces
ANSI SQL Datatype CHARACTER (n), CHAR (n) NUMERIC (p,s), DECIMAL (p,s), DEC (p,s) INTEGER, INT, SMALLINT FLOAT (p) REAL DOUBLE PRECISION CHARACTER VARYING(n), CHAR VARYING(n) TIMESTAMP TIMESTAMP WITH TIME ZONE
DB2 or SQL/DS Datatype CHARACTER (n) VARCHAR (n) LONG VARCHAR DECIMAL (p,s) INTEGER, SMALLINT FLOAT (p) DATE TIMESTAMP
The datatypes TIME, GRAPHIC, VARGRAPHIC, and LONG VARGRAPHIC of IBM products SQL/DS and DB2 have no corresponding Oracle datatype, and they cannot be used.
Create a table traders holds data for a stock trading account holder:
CREATE TABLE traders ( name VARCHAR2(50), email VARCHAR2(50), interest VARCHAR2(50) );
2.
Create the user-defined datatype ticker 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 );
3.
Use the following PL/SQL block to create an attribute set ticker based on the ticker datatype:
BEGIN DBMS_EXPFIL.CREATE_ATTRIBUTE_SET( attr_set => 'ticker', from_type => 'YES' ); END;
4.
Associate the attribute set with the expression set stored in the database column trader.interest as follows:
BEGIN DBMS_EXPFIL.ASSIGN_ATTRIBUTE_SET (attr_set => 'ticker', expr_tab => 'traders', expr_col => 'interest'); END;
The preceding code places a constraint on the interest column that ensures the column stores valid conditional expressions.
5.
Populate the table with trader names, email addresses and conditional expressions that represents a stock the trader is interested in at a particular price:
INSERT INTO traders (name, email, interest) VALUES ('Vishu', '[email protected]', 'symbol = ''ABC'' AND price > 25');
6.
Use the EVALUATE operator to identify the conditional expressions that evaluate to TRUE for a given data item. For example, the following query returns traders who are interested in a given 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;
To speed up this type of query, you can optionally create an Oracle Expression Filter index on the interest column.
See Also: Oracle Database Application Developer's Guide - Rules Manager and Expression Filter for details on Oracle Expression Filter
They are the fastest way to access a single row. They can show you how the rows in a table are stored. They are unique identifiers for rows in a table.
See Also:
Oracle Database Concepts for general information about the ROWID pseudocolumn and the ROWID datatype Oracle Database SQL Reference to learn about the ROWID pseudocolumn
Note: Although you can use the ROWID pseudocolumn in the SELECT and WHERE clause of a query, these pseudocolumn values are not actually stored in the database. You cannot insert, update, or delete a value of the ROWID pseudocolumn.
Restricted ROWID
Internally, the ROWID is a structure that holds information that the database server needs to access a row. The restricted internal ROWID is 6 bytes on most platforms. Each restricted rowid includes the following data:
The restricted ROWID pseudocolumn is returned to client applications in the form of an 18-character string with a hexadecimal encoding of the datablock, row, and datafile components of the ROWID.
Extended ROWID
The extended ROWID datatype includes the data in the restricted rowid plus a data object number. The data object number is an identification number assigned to every database segment. The extended internal ROWID is 10 bytes on most platforms. Data in an extended ROWID pseudocolumn is returned to the client application in the form of an 18-character string (for example, "AAAA8mAALAAAAQkAAA"), which represents a base 64 encoding of the components of the extended ROWID in a four-piece format, OOOOOOFFFBBBBBBRRR. Extended rowids are not available directly. You can use a supplied package, DBMS_ROWID, to interpret extended rowid contents. The package functions extract and provide information that would be available directly from a restricted rowid as well as information specific to extended rowids.
See Also:
Oracle Database PL/SQL Packages and Types Reference for information about the DBMS_ROWID package
conversion
variable := expression The datatype of expression must be either the same as, or convertible to, the datatype of variable. For example, Oracle Database automatically converts the data provided in the following assignment within the body of a stored procedure:
VAR1 := 0;
INSERT INTO Table1_tab VALUES (expression1, expression2, ...) The datatypes of expression1, expression2, and so on, must be either the same as, or convertible to, the datatypes of the corresponding columns in Table1_tab. For example, Oracle Database automatically converts the data provided in the following INSERT statement for Table1_tab:
INSERT INTO Table1_tab VALUES ( ' 19 ' );
UPDATE Table1_tab SET column = expression The datatype of expression must be either the same as, or convertible to, the datatype of column. For example, Oracle Database automatically converts the data provided in the following UPDATE statement issued against Table1_tab:
UPDATE Table1_tab SET col1 = ' 30 ' ;
SELECT column INTO variable FROM Table1_tab The datatype of column must be either the same as, or convertible to, the datatype of variable. For example, Oracle Database automatically converts data selected from the table before assigning it to the variable in the following statement:
SELECT Col1 INTO Var1 FROM Table1_tab WHERE Col1 = 30;
Character to NUMBER conversions succeed only if the character string represents a valid number. Character to DATE conversions succeed only if the character string satisfies the session default format, which is specified by the initialization parameter NLS_DATE_FORMAT. Some common types of expressions follow:
commission + '500'
In general, Oracle Database uses the rule for expression evaluation when a datatype conversion is needed in places not covered by the rule for assignment conversions. In assignments of the form:
variable := expression
Oracle Database first evaluates expression using the conversion rules for expressions; expression can be as simple or complex as desired. If it succeeds, then the evaluation of expression results in a single value and datatype. Then, Oracle Database tries to assign this value to the target variable using the conversion rules for assignments.
4
Using Regular Expressions in Oracle Database
This chapter introduces regular expression support for Oracle Database. This chapter covers the following topics:
Using Regular Expressions with Oracle Database: Overview Regular Expression Metacharacters in Oracle Database Using Regular Expressions in SQL Statements: Scenarios
See Also:
Oracle Database SQL Reference for information about Oracle Database SQL functions for regular expressions Oracle Database Globalization Support Guide for details on 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
What Are Regular Expressions? How Are Oracle Database Regular Expressions Useful? Oracle Database Implementation of Regular Expressions Oracle Database Support for the POSIX Regular Expression Standard
Metacharacters, which are operators that specify search algorithms Literals, which are the characters for which you are searching
A regular expression can specify complex patterns of character sequences. For example, the following regular expression searches for the literals f or ht, the t literal, the p literal optionally followed by the s literal, and finally the colon (:) literal:
(f|ht)tps?:
The parentheses are metacharacters that group a series of pattern elements to a single element; the pipe symbol (|) matches one of the alternatives in the group. The question mark (?) is a metacharacter indicating that the preceding pattern, in this case the s character, is optional. Thus, the preceding regular expression matches the http:, https:, ftp:, and ftps: strings.
By centralizing match logic in Oracle 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 DNAs and proteins. Previously, finding a match for a protein sequence such as [AG].{4}GK[ST] would be handled in the middle tier. The SQL regular expression functions move the processing logic closer to the data, thereby providing a more efficient solution. Prior to Oracle Database 10g, developers often coded data validation logic on the client, requiring the same validation logic to be duplicated for multiple clients. Using server-side regular expressions to enforce constraints solves this problem. The built-in SQL and PL/SQL regular expression functions and conditions make string manipulations more powerful and less cumbersome than in previous releases of Oracle Database.
Table 41 (Cont.) SQL Regular Expression Functions and Conditions SQL Element Category Description Searches for a pattern in a character column and replaces each occurrence of that pattern with the specified string. The following function puts a space after each character in the country_name column: REGEXP_REPLACE(country_name, '(.)', '\1 ') REGEXP_INSTR Function Searches a string for a given occurrence of a regular expression pattern and returns an integer indicating the position in the string where the match is found. You specify which occurrence you want to find and the start position. For example, the following performs a boolean test for a valid email address in the email column: REGEXP_INSTR(email, '\w+@\w+(\.\w+)+') > 0 REGEXP_SUBSTR Function Returns the substring matching the regular expression pattern that you specify. The following function uses the x flag to match the first string by ignoring spaces in the regular expression: REGEXP_SUBSTR('oracle', 'o r a c l e', 1, 1, 'x')
REGEXP_REPLACE Function
A string literal in a REGEXP function or condition conforms to the rules of SQL text literals. By default, regular expressions must be enclosed in single quotes. If your regular expression includes the single quote character, then enter two single quotation marks to represent one single quotation mark within the expression. This technique ensures that the entire expression is interpreted by the SQL function and improves the readability of your code. You can also use the q-quote syntax to define your own character to terminate a text literal. For example, you could delimit your regular expression with the pound sign (#) and then use a single quote within the expression.
Note:
If your expression comes from a column or a bind variable, then the same rules for quoting do not apply.
See Also:
Oracle Database SQL Reference for syntax, descriptions, and examples of the REGEXP functions and conditions Oracle Database SQL Reference for a discussion of character literals
IEEE Portable Operating System Interface (POSIX) standard draft 1003.2/D11.2 Unicode Regular Expression Guidelines of the Unicode Consortium
Oracle Database follows the exact syntax and matching semantics for these operators as defined in the POSIX standard for matching ASCII (English language) data. You can find the POSIX standard draft at the following URL:
http://www.opengroup.org/onlinepubs/007908799/xbd/re.html
Extends the matching capabilities for multilingual data beyond what is specified in the POSIX standard.
Adds support for the common Perl regular expression extensions that are not included in the POSIX standard but do not conflict with it. Oracle Database provides built-in support for some of the most heavily used Perl regular expression operators, for example, character class shortcuts, the non-greedy modifier, and so on.
Oracle Database supports a set of common metacharacters used in regular expressions. The behavior of supported metacharacters and related features is described in "Regular Expression Metacharacters in Oracle Database" on page 4-4.
Note:
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 the regular expression syntax is supported and the behavior is what you expect.
POSIX Metacharacters in Oracle Database Regular Expressions Regular Expression Operator Multilingual Enhancements Perl-Influenced Extensions in Oracle Regular Expressions
Matches any character in the database character set. The expression a.b matches the If the n flag is set, it matches the newline character. strings abb, acb, and adb, but does The newline is recognized as the linefeed character not match acc. (\x0a) on UNIX and Windows or the carriage return character (\x0d) on Macintosh platforms. Note: In the POSIX standard, this operator matches any English character except NULL and the newline character.
One or More Plus Quantifier Zero or One Question Mark Quantifier Zero or More Star Quantifier
Matches one or more occurrences of the preceding subexpression. Matches zero or one occurrence of the preceding subexpression. Matches zero or more occurrences of the preceding subexpression. By default, a quantifier match is greedy because it matches as many times as possible while still allowing the rest of the match to succeed. Matches exactly m occurrences of the preceding subexpression. Matches at least m occurrences of the preceding subexpression.
The expression a+ matches the strings a, aa, and aaa, but does not match bbb. The expression ab?c matches the strings abc and ac, but does not match abbc. The expression ab*c matches the strings ac, abc, and abbc, but does not match abb.
{m} {m,}
The expression a{3} matches the strings aaa, but does not match aa. The expression a{3,} matches the strings aaa and aaaa, but does not match aa.
Example The expression a{3,5} matches the strings aaa, aaaa, and aaaaa, but does not match aa. The expression [abc] matches the first character in the strings all, bill, and cold, but does not match any characters in doll.
[ ... ]
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 values ranges; the semantics of the range expression are independent of character set. In Oracle Database, the linguistic range is determined by the NLS_SORT initialization parameter. [^ ... ] Non-Matching Character List Matches any single character not in the list within the brackets. Characters not in the non-matching character list are returned as a match. Refer to the description of the Matching Character List operator for an account of metacharacters allowed in the character list. The expression [^abc] matches the character d in the string abcdef, but not the character a, b, or c. The expression [^abc]+ matches the sequence def in the string abcdef, but not a, b, or c. The expression [^a-i] excludes any character between a and i from the search result. This expression matches the character j in the string hij, but does not match any characters in the string abcdefghi. | ( ... ) Or Subexpression or Grouping Matches one of the alternatives. Treats the expression within parentheses as a unit. The subexpression can be a string of literals or a complex expression containing operators. The expression a|b matches character a or character b. The expression (abc)?def matches the optional string abc, followed by def. Thus, the expression matches abcdefghi and def, but does not match ghi. The expression (abc|def)xy\1 matches the strings abcxyabc and defxydef, but does not match abcxydef or abcxy.
\n
Backreference
Matches the nth preceding subexpression, that is, whatever is grouped within parentheses, where n is an integer from 1 to 9. The parentheses cause an expression to be remembered; a backreference refers to it. A backreference counts subexpressions from left to right, starting with the opening parenthesis of each preceding subexpression. The expression is invalid if the source string contains fewer than n subexpressions preceding the \n.
A backreference enables you to search for a repeated string without knowing the actual string ahead of time. For example, the expression ^(.*)\1$ matches a line Oracle supports the backreference expression in the consisting of two adjacent instances of the same string. regular expression pattern and the replacement string of the REGEXP_REPLACE function. The expression \+ searches for the plus character (+). It matches the plus character in the string abc+def, but does not match abcdef.
Escape Character
Treats the subsequent metacharacter in the expression as a literal. Use a backslash (\) to search for a character that is normally treated as a metacharacter. Use consecutive backslashes (\\) to match the backslash literal itself.
Beginning of Line Matches the beginning of a string (default). In Anchor multiline mode, it matches the beginning of any line within the source string. End of Line Anchor POSIX Character Class Matches the end of a string (default). In multiline mode, it matches the beginning of any line within the source string. Matches any character belonging to the specified POSIX character class. You can use this operator to search for characters with specific formatting such as uppercase characters, or you can search for special characters such as digits or punctuation characters. The full set of POSIX character classes is supported. 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 may not be the same in all languages. Oracle supports the character classes in Table 43 based on character class definitions in Globalization classification data.
[:class:]
[.element.]
POSIX Collating Specifies a collating element to use in the regular Element Operator expression. The element must be a defined collating element in the current locale. Use any collating element defined in the locale, including single-character and multicharacter elements. The NLS_SORT initialization parameter determines supported collation elements. This operator lets you use a multicharacter collating element in cases where only one character would otherwise be 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.]] searches for the collating element ch and matches ch in string chabc, but does not match cdefg. The expression [a-[.ch.]] specifies the range a to ch.
Matches all characters that are members of the same The expression [[=n=]] searches for characters equivalent to n in a character equivalence class in the current locale as Spanish locale. It matches both N the specified character. and in the string El Nio. The character equivalence class must occur within a character list, so the character equivalence class is always nested within the brackets for the character list in the regular expression. Usage of character equivalents depends on how canonical rules are defined for your database locale. Refer to the Oracle Database Globalization Support Guide for more information on linguistic sorting and string searching.
See Also:
Oracle Database SQL Reference for syntax, descriptions, and examples of the REGEXP functions and conditions
The first column lists the supported operators. The second column indicates whether the POSIX standard for Basic Regular Expression (BRE) defines the operator.
The third column indicates whether the POSIX standard for Extended Regular Expression (ERE) defines the operator. The fourth column indicates whether the Oracle Database implementation extends the operator's semantics for handling multilingual data.
Oracle Database lets you enter multibyte characters directly, if you have a direct input method, or use functions to compose the multibyte characters. You cannot use the Unicode hexadecimal encoding value of the form \xxxx. Oracle evaluates the characters based on the byte values used to encode the character, not the graphical representation of the character.
Table 43 Operator \ * + ? | ^ $ . [ ] ( ) {m} {m,} {m,n} \n [..] [::] [==] POSIX and Multilingual Operator Relationships POSIX BRE syntax Yes Yes ---Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes POSIX ERE Syntax Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Multilingual Enhancement -----Yes Yes Yes Yes ----Yes Yes Yes Yes
Table 44
Perl-Influenced Extensions in Oracle Regular Expressions Example The expression ^\(\d{3}\) \d{3}-\d{4}$ matches (650) 555-1212 but does not match 650-555-1212. The expression \w\d\D matches b2b and b2_ but does not match b22. The expression \w+@\w+(\.\w+)+ matches the string [email protected] but not the string jdoe@company.
Reg. Exp. Matches . . . \d A digit character. It is equivalent to the POSIX class [[:digit:]]. A non-digit character. It is equivalent to the POSIX class [^[:digit:]]. A word character, which is defined as an alphanumeric or underscore (_) character. It is equivalent to the POSIX class [[:alnum:]_]. Note that if you do not want to include the underscore character, you can use the POSIX class [[:alnum:]]. A non-word character. It is equivalent to the POSIX class [^[:alnum:]_]. A whitespace character. It is equivalent to the POSIX class [[:space:]]. A non-whitespace character. It is equivalent to the POSIX class [^[:space:]]. Only at the beginning of a string. In multi-line mode, that is, when embedded newline characters in a string are considered the termination of a line, \A does not match the beginning of each line. Only at the end of string or before a newline ending a string. In multi-line mode, that is, when embedded newline characters in a string are considered the termination of a line, \Z does not match the end of each line. Only at the end of a string.
\D \w
\W \s \S \A
The expression \w+\W\s\w+ matches the string to: bill but not the string to bill. The expression \(\w\s\w\s\) matches the string (a b ) but not the string (ab). The expression \(\w\S\w\S\) matches the string (abde) but not the string (a b d e). The expression \AL matches only the first L character in the string Line1\nLine2\n, regardless of whether the search is in single-line or multi-line mode.
\Z
In the expression \s\Z, the \s matches the last space in the string L i n e \n, regardless of whether the search is in single-line or multi-line mode.
\z
In the expression \s\z, the \s matches the newline in the string L i n e \n, regardless of whether the search is in single-line or multi-line mode.
*?
The expression \w*?x\w is "non-greedy" and so The preceding pattern element 0 or more times (non-greedy). Note that this quantifier matches abxc in the string abxcxd. The expression matches the empty string whenever possible. \w*x\w is "greedy" and so matches abxcxd in the string abxcxd. The expression \w*?x\w also matches the string xa. The preceding pattern element 1 or more times (non-greedy). The expression \w+?x\w is "non-greedy" and so matches abxc in the string abxcxd. The expression \w+x\w is "greedy" and so matches abxcxd in the string abxcxd. The expression \w+?x\w does not match the string xa, but does match the string axa.
+?
??
The expression a??aa is "non-greedy" and matches aa The preceding pattern element 0 or 1 time in the string aaaa. The expression a?aa is "greedy" (non-greedy). Note that this quantifier matches the empty string whenever possible. and so matches aaa in the string aaaa. The preceding pattern element exactly n times (non-greedy). In this case {n}? is equivalent to {n}. The preceding pattern element at least n times (non-greedy). The expression (a|aa){2}? matches aa in the string aaaa. The expression a{2,}? is "non-greedy" and matches aa in the string aaaaa. The expression a{2,} is "greedy" and so matches aaaaa.
{n}?
{n,}?
{n,m}?
At least n but not more than m times The expression a{2,4}? is "non-greedy" and matches (non-greedy). Note that {0,m}? matches the aa in the string aaaaa. The expression a{2,4} is empty string whenever possible. "greedy" and so matches aaaa.
The Oracle Database regular expression functions and conditions support the pattern matching modifiers described in Table 45.
Table 45 Pattern Matching Modifiers Example The following regular expression returns AbCd: REGEXP_SUBSTR('AbCd', 'abcd', 1, 1, 'i') c Specifies case-sensitive matching. The following regular expression fails to match: REGEXP_SUBSTR('AbCd', 'abcd', 1, 1, 'c') n Allows the period (.), which by default does not match newlines, to match the newline character. Performs the search in multi-line mode. The metacharacter ^ and $ signify the start and end, respectively, of any line anywhere in the source string, rather than only at the start or end of the entire source string. Ignores whitespace characters in the regular expression. By default, whitespace characters match themselves. The following regular expression matches the string, but would not match if the n flag were not specified: REGEXP_SUBSTR('a'||CHR(10)||'d', 'a.d', 1, 1, 'n') The following regular expression returns ac: REGEXP_SUBSTR('ab'||CHR(10)||'ac', '^a.', 1, 2, 'm')
The following regular expression returns abcd: REGEXP_SUBSTR('abcd', 'a b c d', 1, 1, 'x')
Using an Integrity Constraint to Enforce a Phone Number Format Using Back References to Reposition Characters
Table 46 (Cont.) Explanation of the Regular Expression Elements in Example 41 Regular Expression Element \( Matches . . . A left parenthesis. The backward slash (\) is an escape character that indicates that the left parenthesis following it is a literal rather than a grouping expression. Exactly three digits. A right parenthesis. The backward slash (\) is an escape character that indicates that the right parenthesis following it is a literal rather than a grouping expression. A space character. Exactly three digits. A hyphen. Exactly four digits. The end of the string.
\d{3} \)
Example 42 shows a SQL script that attempts to insert seven phone numbers into the contacts table. Only the first two INSERT statements use a format that conforms to the p_number_format constraint; the remaining statements generate check constraint errors.
Example 42 insert_contacts.sql -- first two statements use valid phone number format INSERT INTO contacts (p_number) VALUES( '(650) 555-5555' ); INSERT INTO contacts (p_number) VALUES( '(215) 555-3427' ); -- remaining statements generate check contraint errors INSERT INTO contacts (p_number) VALUES( '650 555-5555' ); INSERT INTO contacts (p_number) VALUES( '650 555 5555' ); INSERT INTO contacts (p_number) VALUES( '650-555-5555' ); INSERT INTO contacts (p_number) VALUES( '(650)555-5555' ); INSERT INTO contacts (p_number) VALUES( ' (650) 555-5555' ); /
-- populate table with data INSERT INTO famous_people VALUES ('John Quincy Adams'); INSERT INTO famous_people VALUES ('Harry S. Truman'); INSERT INTO famous_people VALUES ('John Adams'); INSERT INTO famous_people VALUES (' John Quincy Adams'); INSERT INTO famous_people VALUES ('John_Quincy_Adams'); COMMIT;
Example 44 shows a query that repositions names in the format "first middle last" to the format "last, first middle". It ignores names not in the format "first middle last".
Example 44 Using Back References to Reposition Characters SELECT names "names", REGEXP_REPLACE(names, '^(\S+)\s(\S+)\s(\S+)$', '\3, \1 \2') AS "names after regexp" FROM famous_people;
Example 45 shows the result set of the query in Example 44. The regular expression matched only the first two rows.
Example 45 Result Set of Regular Expression Query names -----------------------------names after regexp -----------------------------John Quincy Adams Adams, John Quincy
Harry S. Truman Truman, Harry S. John Adams John Adams John Quincy Adams John Quincy Adams John_Quincy_Adams John_Quincy_Adams
5
Using Indexes in Application Development
This chapter discusses considerations for using the different types of indexes in an application. The topics include:
Guidelines for Application-Specific Indexes Creating Indexes: Basic Examples When to Use Domain Indexes When to Use Function-Based Indexes
See Also:
Oracle Database Administrator's Guide for information about creating and managing indexes Oracle Database Performance Tuning Guide for detailed information about using indexes Oracle Database SQL Reference for the syntax of commands to work with indexes Oracle Database Administrator's Guide for information on creating hash clusters to improve performance, as an alternative to indexing
The column is queried frequently. A referential integrity constraint exists on the column. A UNIQUE key integrity constraint exists on the column.
You can create an index on any column; however, if the column is not used in any of these situations, creating an index on the column does not increase performance and the index takes up resources unnecessarily. Although the database creates an index for you on a column with an integrity constraint, explicitly creating an index on such a column is recommended. You can use the following techniques to determine which columns are best candidates for indexing:
5-1
Use the EXPLAIN PLAN feature to show a theoretical execution plan of a given query statement. Use the V$SQL_PLAN view to determine the actual execution plan used for a given query statement.
Sometimes, if an index is not being used by default and it would be most efficient to use that index, you can use a query hint so that the index is used. The following sections explain how to create, alter, and drop indexes using SQL commands, and give guidelines for managing indexes.
See Also: Oracle Database Performance Tuning Guide for information on using the V$SQL_PLAN view, the EXPLAIN PLAN statement, query hints, and measuring the performance benefits of indexes
Create a new temporary tablespace using the CREATE TABLESPACE command. Use the TEMPORARY TABLESPACE option of the ALTER USER command to make this your new temporary tablespace. Create the index using the CREATE INDEX command. Drop this tablespace using the DROP TABLESPACE command. Then use the ALTER USER command to reset your temporary tablespace to your original temporary tablespace.
Under certain conditions, you can load data into a table with the SQL*Loader "direct path load", and an index can be created as data is loaded.
See Also:
Create an index if you frequently want to retrieve less than about 15% of the rows in a large table. This threshold percentage varies greatly, however, according to the relative speed of a table scan and how clustered the row data is about the index key. The faster the table scan, the lower the percentage; the more clustered the row data, the higher the percentage. Index columns that are used for joins to improve join performance.
Primary and unique keys automatically have indexes, but you might want to create an index on a foreign key; see Chapter 6, "Maintaining Data Integrity in Application Development" for more information. Small tables do not require indexes; if a query is taking too long, then the table might have grown from small to large.
Some columns are strong candidates for indexing. Columns with one or more of the following characteristics are good candidates for indexing:
Values are unique in the column, or there are few duplicates. There is a wide range of values (good for regular indexes). There is a small range of values (good for bitmap indexes). The column contains many nulls, but queries often select all rows having a value. In this case, a comparison that matches all the non-null values, such as:
WHERE COL_X >= -9.99 *power(10,125)
is preferable to
WHERE COL_X IS NOT NULL
This is because the first uses an index on COL_X (assuming that COL_X is a numeric column). Columns with the following characteristics are less suitable for indexing:
There are many nulls in the column and you do not search on the non-null values.
LONG and LONG RAW columns cannot be indexed. The size of a single index entry cannot exceed roughly one-half (minus some overhead) of the available space in the data block. Consult with the database administrator for assistance in determining the space required by an index.
5-3
Table VENDOR_PARTS
VEND ID 1012 1012 1012 1010 1010 1220 1012 1292 PART NO 10440 10441 457 10440 457 08300 08300 457 UNIT COST .25 .39 4.95 .27 5.10 1.33 1.19 5.28
Assume that there are five vendors, and each vendor has about 1000 parts. Suppose that the VENDOR_PARTS table is commonly queried by SQL statements such as the following:
SELECT * FROM vendor_parts WHERE part_no = 457 AND vendor_id = 1012;
To increase the performance of such queries, you might create a composite index putting the most selective column first; that is, the column with the most values:
CREATE INDEX ind_vendor_id ON vendor_parts (part_no, vendor_id);
Composite indexes speed up queries that use the leading portion of the index. So in this example, queries with WHERE clauses using only the PART_NO column also note a performance gain. Because there are only five distinct values, placing a separate index on VENDOR_ID would serve no purpose.
It does not speed up queries. The table might be very small, or there might be many rows in the table but very few index entries. The queries in your applications do not use the index. The index must be dropped before being rebuilt.
When you drop an index, all extents of the index's segment are returned to the containing tablespace and become available for other objects in the tablespace. Use the SQL command DROP INDEX to drop an index. For example, the following statement drops a specific named index:
DROP INDEX Emp_ename;
If you drop a table, then all associated indexes are dropped. To drop an index, the index must be contained in your schema or you must have the DROP ANY INDEX system privilege.
In this example, several storage settings are explicitly specified for the index:
CREATE INDEX emp_ename ON emp_tab(ename) TABLESPACE users STORAGE (INITIAL 20K NEXT 20k PCTINCREASE 75) PCTFREE 0 COMPUTE STATISTICS;
In this example, the index applies to two columns, to speed up queries that test either the first column or both columns:
CREATE INDEX emp_ename ON emp_tab(ename, empno) COMPUTE STATISTICS;
In this example, the query is going to sort on the function UPPER(ENAME). An index on the ENAME column itself would not speed up this operation, and it might be slow to call the function for each result row. A function-based index precomputes the result of the function for each column value, speeding up queries that use the function for searching or sorting:
CREATE INDEX emp_upper_ename ON emp_tab(UPPER(ename)) COMPUTE STATISTICS;
5-5
The index is more effective if you gather statistics for the table or schema, using the procedures in the DBMS_STATS package. The index cannot contain any null values. Either make sure the appropriate columns contain no null values, or use the NVL function in the index expression to substitute some other value for nulls.
The expression indexed by a function-based index can be an arithmetic expression or an expression that contains a PL/SQL function, package function, C callout, or SQL function. Function-based indexes also support linguistic sorts based on collation keys, efficient linguistic collation of SQL statements, and case-insensitive sorts. Like other indexes, function-based indexes improve query performance. For example, if you need to access a computationally complex expression often, then you can store it in an index. Then when you need to access the expression, it is already computed. You can find a detailed description of the advantages of function-based indexes in "Advantages of Function-Based Indexes" on page 5-6. Function-based indexes have all of the same properties as indexes on columns. Unlike indexes on columns which can be used by both cost-based and rule-based optimization, however, function-based indexes can be used by only by cost-based optimization. Other restrictions on function-based indexes are described in "Restrictions for Function-Based Indexes" on page 5-8.
See Also:
Oracle Database Concepts Oracle Database Administrator's Guide for information on creating function-based indexes
Increase the number of situations where the optimizer can perform a range scan instead of a full table scan. For example, consider the expression in this WHERE clause:
CREATE INDEX Idx ON Example_tab(Column_a + Column_b); SELECT * FROM Example_tab WHERE Column_a + Column_b < 10;
The optimizer can use a range scan for this query because the index is built on (column_a + column_b). Range scans typically produce fast response times if the predicate selects less than 15% of the rows of a large table. The optimizer can estimate how many rows are selected by expressions more accurately if the expressions are materialized in a function-based index. (Expressions of function-based indexes are represented as virtual columns and ANALYZE can build histograms on such columns.)
Precompute the value of a computationally intensive function and store it in the index. An index can store computationally intensive expression that you access often. When you need to access a value, it is already computed, greatly improving query execution performance. Create indexes on object columns and REF columns. Methods that describe objects can be used as functions on which to build indexes. For example, you can use the MAP method to build indexes on an object type column. Create more powerful sorts. You can perform case-insensitive sorts with the UPPER and LOWER functions, descending order sorts with the DESC keyword, and linguistic-based sorts with the NLSSORT function.
Note: Oracle Database sorts columns with the DESC keyword in descending order. Such indexes are treated as function-based indexes. Descending indexes cannot be bitmapped or reverse, and cannot be used in bitmapped optimizations. To get the DESC functionality prior to Oracle Database version 8, remove the DESC keyword from the CREATE INDEX statement.
Another function-based index calls the object method distance_from_equator for each city in the table. The method is applied to the object column Reg_Obj. A query could use this index to quickly find cities that are more than 1000 miles from the equator:
CREATE INDEX Distance_index ON Weatherdata_tab (Distance_from_equator (Reg_obj)); SELECT * FROM Weatherdata_tab WHERE (Distance_from_equator (Reg_Obj)) > '1000';
Another index stores the temperature delta and the maximum temperature. The result of the delta is sorted in descending order. A query could use this index to quickly find table rows where the temperature delta is less than 20 and the maximum temperature is greater than 75.
CREATE INDEX compare_index ON Weatherdata_tab ((Maxtemp - Mintemp) DESC, Maxtemp); SELECT * FROM Weatherdata_tab WHERE ((Maxtemp - Mintemp) < '20' AND Maxtemp > '75');
5-7
The SELECT command uses the function-based index on UPPER(e_name) to return all of the employees with name like :KEYCOL.
SELECT * FROM Emp_tab WHERE UPPER(Ename) like :KEYCOL;
The SELECT statement can either use index range scan (since the expression is a prefix of index IDX) or index fast full scan (which may be preferable if the index has specified a high parallel degree).
SELECT a FROM Fbi_tab WHERE A + B * (C - 1) < 100;
The SELECT statement selects all of the contents of the table and orders it by NAME. The rows are ordered using the German collation sequence. The Globalization Support parameters are not needed in the SELECT statement, because in a German session, NLS_SORT is set to German and NLS_COMP is set to ANSI.
SELECT * FROM Nls_tab WHERE Name IS NOT NULL ORDER BY Name;
Only cost-based optimization can use function-based indexes. Remember to call DBMS_STATS.GATHER_TABLE_STATISTICS or DBMS_STATS.GATHER_ SCHEMA_STATISTICS, for the function-based index to be effective. Any top-level or package-level PL/SQL functions that are used in the index expression must be declared as DETERMINISTIC. That is, they always return the same result given the same input, for example, the UPPER function. You must ensure that the subprogram really is deterministic, because Oracle Database does not check that the assertion is true. The following semantic rules demonstrate how to use the keyword DETERMINISTIC:
You can declare a top level subprogram as DETERMINISTIC. You can declare a PACKAGE level subprogram as DETERMINISTIC in the PACKAGE specification but not in the PACKAGE BODY. Errors are raised if DETERMINISTIC is used inside a PACKAGE BODY.
You can declare a private subprogram (declared inside another subprogram or a PACKAGE BODY) as DETERMINISTIC. A DETERMINISTIC subprogram can call another subprogram whether the called program is declared as DETERMINISTIC or not.
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. Expressions in a function-based index cannot contain any aggregate functions. The expressions should reference only columns in a row in the table. You must analyze the table or index before the index is used. Bitmap optimizations cannot use descending indexes. Function-based indexes are not used when OR-expansion is done. The index function cannot be marked NOT NULL. To avoid a full table scan, you must ensure that the query cannot fetch null values. Function-based indexes cannot use expressions that return VARCHAR2 or RAW data types of unknown length from PL/SQL functions. A workaround is to limit the size of the function's output by indexing a substring of known length:
-- INITIALS() might return 1 letter, 2 letters, 3 letters, and so on. -- We limit the return value to 10 characters for purposes of the index. CREATE INDEX func_substr_index ON emp_tab(substr(initials(ename),1,10); -- Call SUBSTR both when creating the index and when referencing -- the function in queries. SELECT SUBSTR(initials(ename),1,10) FROM emp_tab;
See Also: Oracle Database SQL Reference for an account of CREATE FUNCTION restrictions
5-9
6
Maintaining Data Integrity in Application Development
This chapter explains how to enforce the business rules associated with your database and prevent the entry of invalid information into tables by using integrity constraints. Topics include the following:
Overview of Integrity Constraints Enforcing Referential Integrity with Constraints Managing Constraints That Have Associated Indexes Guidelines for Indexing Foreign Keys About Referential Integrity in a Distributed Database When to Use CHECK Integrity Constraints Examples of Defining Integrity Constraints Enabling and Disabling Integrity Constraints Altering Integrity Constraints Dropping Integrity Constraints Managing FOREIGN KEY Integrity Constraints Viewing Definitions of Integrity Constraints
6-1
Then, create a rule that every department listed in the employee table must match one of the values in the department table:
ALTER TABLE Emp_tab ADD FOREIGN KEY (Deptno) REFERENCES Dept_tab(Deptno);
When you add a new employee record to the table, Oracle Database automatically checks that its department number appears in the department table. To enforce this rule without integrity constraints, you can use a trigger to query the department table and test that each new employee's department is valid. But this method is less reliable than the integrity constraint. SELECT in Oracle Database uses "consistent read", so the query might miss uncommitted changes from other transactions.
Constraints use existing indexes where possible, rather than creating new ones. Unique and primary keys can use non-unique as well as unique indexes. They can even use just the first few columns of non-unique indexes. At most one unique or primary key can use each non-unique index. The column orders in the index and the constraint do not need to match. If you need to check whether an index is used by a constraint, for example when you want to drop the index, the object number of the index used by a unique or primary key constraint is stored in CDEF$.ENABLED for that constraint. It is not shown in any catalog view.
You should almost always index foreign keys; the database does not do this for you automatically.
NOT NULL integrity constraints. However, an employee name might be required from the very beginning, and you can enforce this rule with a NOT NULL integrity constraint. NOT NULL constraints are often combined with other types of integrity constraints to further restrict the values that can exist in specific columns of a table. Use the combination of NOT NULL and UNIQUE key integrity constraints to force the input of values in the UNIQUE key; this combination of data integrity rules eliminates the possibility that any new row' data will ever attempt to conflict with an existing row's data. Because Oracle Database indexes do not store keys that are all null, if you want to allow index-only scans of the table or some other operation that requires indexing all rows, you must put a NOT NULL constraint on at least one indexed column.
See Also:
Table EMPLOYEES
ID 100 101 102 103 LNAME King Kochhar De Hann Hunold JOB AD_PRES AD_VP AD_VP IT_PROG MGR 100 100 102 HIREDATE 17JUN87 21SEP89 13JAN93 03JAN90 SAL 24000 17000 17000 9000 COMM DEPTNO 90 90 90 60
NOT NULL Constraint (no row may contain a null value for this column)
Absence of NOT NULL Constraint (any row can contain a null for this column)
Depending upon your business rules, you might use default values to represent zero or false, or leave the default values as NULL to signify an unknown value. Defaults are also useful when you use a view to make a subset of a table's columns visible. For example, you might allow users to insert rows through a view. The base table might also have a column named INSERTER, not included in the definition of the
6-3
view, to log the user that inserts each row. To record the user name automatically, define a default value that calls the USER function:
CREATE TABLE ( value1 value2 inserter ); audit_trail NUMBER, VARCHAR2(32), VARCHAR2(30) DEFAULT USER
Default values cannot include expressions that refer to a sequence, PL/SQL function, column, LEVEL, ROWNUM, or PRIOR. The datatype of a default literal or expression must match or be convertible to the column datatype. Sometimes the default value is the result of a SQL function. For example, a call to SYS_CONTEXT can set a different default value depending on conditions such as the user name. To be used as a default value, a SQL function must have parameters that are all literals, cannot reference any columns, and cannot call any other functions. If you do not explicitly define a default value for a column, the default for the column is implicitly set to NULL. You can use the keyword DEFAULT within an INSERT statement instead of a literal value, and the corresponding default value is inserted.
Whenever practical, use a column containing a sequence number. It is a simple way to satisfy all the other guidelines. Choose a column whose data values are unique, because the purpose of a primary key is to uniquely identify each row of the table. Choose a column whose data values are never changed. A primary key value is only used to identify a row in the table, and its data should never be used for any other purpose. Therefore, primary key values should rarely or never be changed. Choose a column that does not contain any nulls. A PRIMARY KEY constraint, by definition, does not allow any row to contain a null in any column that is part of the primary key. Choose a column that is short and numeric. Short primary keys are easy to type. You can use sequence numbers to easily generate numeric primary keys.
Minimize your use of composite primary keys. Although composite primary keys are allowed, they do not satisfy all of the other recommendations. For example, composite primary key values are long and cannot be assigned by sequence numbers.
Table DEPARTMENTS
DEPID 10 20 30 40 DNAME Administration Marketing Purchasing Human Resources LOC 1700 1800 1700 2400
UNIQUE Key Constraint (no row may duplicate a value in the constraint's column)
INSERT INTO 50 MARKETING 1700 This row violates the UNIQUE key constraint, because "MARKETING" is already present in another row; therefore, it is not allowed in the table. This row is allowed because a null value is entered for the DNAME column; however, if a NOT NULL constraint is also defined on the DNAME column, this row is not allowed.
60
2400
Note:
You cannot have identical values in the non-null columns of a composite UNIQUE key constraint (UNIQUE key constraints allow NULL values).
An employee social security number (the primary key might be the employee number) A truck license plate number (the primary key might be the truck number) A customer phone number, consisting of the two columns AREA_CODE and LOCAL_PHONE (the primary key might be the customer number) A department name and location (the primary key might be the department number)
views, which helps performance with materialized views and other data warehousing features. Such constraints are always declared with the DISABLE keyword, and you cannot use the VALIDATE keyword. The constraints are never enforced, and there is no associated index.
See Also: Oracle Database Data Warehousing Guide for information on query rewrite, materialized views, and the performance reasons for declaring constraints on views
"Defining Relationships Between Parent and Child Tables" on page 6-8 for information on defining additional integrity constraints, including the foreign key
Figure 63 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. Foreign keys can be comprised of multiple columns. Such a composite foreign key must reference a composite primary or unique key of the exact same structure, with the same number of columns and the same datatypes. Because composite primary and unique keys are limited to 32 columns, a composite foreign key is also limited to 32 columns.
Table DEPARTMENTS
DEPID 10 20 30 40 DNAME Administration Marketing Purchasing Human Resources LOC 1700 1800 1700 2400
Foreign Key (values in dependent table must match a value in unique key or primary key of referenced table)
Table EMPLOYEES
ID 100 101 102 103 LNAME King Kochhar De Hann Hunold JOB AD_PRES AD_VP AD_VP IT_PROG MGR 100 100 102 HIREDATE 17JUN87 21SEP89 13JAN93 03JAN90 SAL 24000 17000 17000 9000 COMM DEPTNO 90 90 90 60
This row violates the referential constraint because "50" is not present in the referenced table's primary key; therefore, the row is not allowed in the table.
556
CRICKET
PU_CLERK
31OCT96
5000
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.
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
Maintaining Data Integrity in Application Development 6-7
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 Chapter 9, "Coding Triggers".
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 63, "Tables with Referential Integrity Constraints" 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 can be used to illustrate 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 should 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)
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.
UNIQUE and NOT NULL Constraints on the Foreign 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.
The SET CONSTRAINTS setting lasts for the duration of the transaction, or until another SET CONSTRAINTS statement resets the mode.
See Also:
Tables are snapshots. Some tables contain a large amount of data being manipulated by another application, which may or may not return the data in the same order. Update cascade operations on foreign keys.
Ensure Constraints Are Created Deferrable After you have identified and selected the appropriate tables, make sure their FOREIGN, UNIQUE and PRIMARY key constraints are created deferrable. You can do so by issuing statements similar to the following:
CREATE TABLE dept ( deptno NUMBER PRIMARY KEY, dname VARCHAR2 (30) ); CREATE TABLE emp ( empno NUMBER, ename VARCHAR2 (30), deptno NUMBER REFERENCES (dept), CONSTRAINT epk PRIMARY KEY (empno) DEFERRABLE, CONSTRAINT efk FOREIGN KEY (deptno) REFERENCES (dept.deptno) DEFERRABLE); INSERT INTO dept VALUES (10, 'Accounting'); INSERT INTO dept VALUES (20, 'SALES'); INSERT INTO emp VALUES (1, 'Corleone', 10); INSERT INTO emp VALUES (2, 'Costanza', 20); COMMIT;
6-9
SET CONSTRAINT efk DEFERRED; UPDATE dept SET deptno = deptno + 10 WHERE deptno = 20; SELECT * from emp ORDER BY deptno; EMPNO ENAME DEPTNO ------------------ ------1 Corleone 10 2 Costanza 20 UPDATE emp SET deptno = deptno + 10 WHERE deptno = 20; SELECT * FROM emp ORDER BY deptno; EMPNO ----1 2 COMMIT; ENAME -------------Corleone Costanza DEPTNO ------10 30
Set All Constraints Deferred Within the application that manipulates the data, you must set all constraints deferred before you begin processing any data. Use the following DML statement to set all constraints deferred:
SET CONSTRAINTS ALL DEFERRED;
Note: The SET CONSTRAINTS statement applies only to the current transaction. The defaults specified when you create a constraint remain as long as the constraint exists. The ALTER SESSION SET CONSTRAINTS statement applies for the current session only.
Check the Commit (Optional) You can check for constraint violations before committing by issuing the SET CONSTRAINTS ALL IMMEDIATE statement just before issuing the COMMIT. If there are any problems with a constraint, this statement will fail and the constraint causing the error will be identified. If you commit while constraints are violated, the transaction will be rolled back and you will receive an error message.
Minimizing Space and Time Overhead for Indexes Associated with Constraints
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 (because, for example, it would take a long time to re-create it), you can specify the KEEP INDEX clause on the DROP command for the constraint. 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 non-unique indexes.
To reuse existing indexes when creating unique and primary key constraints, you can include USING INDEX in the constraint clause. For example:
CREATE TABLE b ( b1 INTEGER, b2 INTEGER, CONSTRAINT unique1 (b1, b2) USING INDEX (CREATE UNIQUE INDEX b_index on b(b1, b2), CONSTRAINT unique2 (b1, b2) USING INDEX b_index );
Oracle Database Concepts for information on locking mechanisms involving indexes and keys
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 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.
A CHECK constraint on employee salaries so that no salary value is greater than 10000.
Maintaining Data Integrity in Application Development 6-11
A CHECK constraint on department locations so that only the locations "BOSTON", "NEW YORK", and "DALLAS" are allowed. A CHECK constraint on the salary and commissions columns to prevent the commission from being larger than the salary.
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, PRIOR, or ROWNUM.
See Also:
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 or not 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 integrity 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 operators AND and OR in Oracle Database SQL Reference
Therefore, NOT NULL integrity constraints for a single column can, in practice, be written in two forms: using the NOT NULL constraint or a CHECK constraint. For ease of use, you should always choose to define NOT NULL integrity constraints, instead of CHECK constraints with the IS NOT NULL condition. In the case where a composite key can allow only all nulls or all values, you must use a CHECK integrity constraint. For example, the following expression of a CHECK integrity 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))
You cannot create a validated constraint on a table if the table already contains any rows that would violate the constraint.
"Viewing Definitions of Integrity Constraints" on page 6-21 for examples of data dictionary views
An integrity constraint represents an assertion about the data in a database. This assertion is always true when the constraint is enabled. The assertion may or may not be true when the constraint is disabled, because data that violates the integrity constraint can be in the database.
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
Enabling Constraints
When you define an integrity constraint in a CREATE TABLE or ALTER TABLE statement, Oracle Database automatically enables the constraint by default. For code clarity, you can explicitly enable the constraint by including the ENABLE clause in its definition. Use this technique when creating tables that start off empty, and are populated a row at a time by individual transactions. In such cases, you want to ensure that data is consistent at all times, and the performance overhead of each DML operation is small. The following CREATE TABLE and ALTER TABLE statements both define and enable integrity constraints:
CREATE TABLE Emp_tab ( Empno NUMBER(5) PRIMARY KEY); ALTER TABLE Emp_tab ADD PRIMARY KEY (Empno);
An ALTER TABLE statement that tries to enable an integrity constraint will fail if any existing rows of the table violate the integrity constraint. The statement is rolled back and the constraint definition is not stored and not enabled.
See Also: "Fixing Constraint Exceptions" on page 6-17 for more information about rows that violate integrity constraints
Use this technique when creating tables that will be loaded with large amounts of data before anybody else accesses them, particularly if you need to cleanse data after loading it, or need to fill empty columns with sequence numbers or parent/child relationships. An ALTER TABLE statement that defines and disables an integrity constraints never fails, because its rule is not enforced.
Enable a disabled constraint, using the ENABLE clause. Disable an enabled constraint, using the DISABLE clause.
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" on page 6-17 for more information about rows that violate integrity constraints
Oracle Database SQL Reference for information on the parameters you can modify
CREATE TABLE X1_tab (A1 NUMBER CONSTRAINT Y_cnstrt NOT NULL DEFERRABLE INITIALLY DEFERRED NORELY DISABLE); ALTER TABLE X1_tab ADD CONSTRAINT One_cnstrt UNIQUE(A1) DEFERRABLE INITIALLY IMMEDIATE RELY USING INDEX PCTFREE = 30 ENABLE VALIDATE; ALTER TABLE X1_tab MODIFY UNIQUE(A1) INITIALLY DEFERRED NORELY USING INDEX PCTFREE = 40 ENABLE NOVALIDATE; ALTER TABLE X1_tab MODIFY CONSTRAINT One_cnstrt INITIALLY IMMEDIATE RELY;
dbms_output.put_line('The primary key for ' || upper('&table_name.') || ' is: ' || constraint_name); execute immediate 'alter table &table_name. rename constraint ' || constraint_name || ' to &new_constraint.'; end; /
When dropping UNIQUE, PRIMARY KEY, and FOREIGN KEY integrity constraints, you should be aware of several important issues and prerequisites. UNIQUE and PRIMARY KEY constraints are usually managed by the database administrator.
See Also: Oracle Database Administrator's Guide and "Managing FOREIGN KEY Integrity Constraints" on page 6-19
FOREIGN KEY integrity constraints cannot be enabled if the constraint of the referenced primary or unique key is not present or not enabled.
The 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. The 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
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);
You can query these views to find the names of constraints, what columns they affect, and other information to help you manage constraints.
See Also:
FROM User_constraints;
Considering the example statements at the beginning of this section, a list similar to this is returned:
CONSTRAINT_NAME --------------SYS_C00275 DNAME_UKEY LOC_CHECK1 SYS_C00278 SYS_C00279 SYS_C00280 MGR_FKEY DEPT_FKEY C P U C C C P R R TABLE_NAME ---------DEPT_TAB DEPT_TAB DEPT_TAB EMP_TAB EMP_TAB EMP_TAB EMP_TAB EMP_TAB R_CONSTRAINT_NAME -----------------
SYS_C00280 SYS_C00275
Some constraint names are user specified (such as DNAME_UKEY), while others are system specified (such as SYS_C00275). Each constraint type is denoted with a different character in the CONSTRAINT_ TYPE column. The following table summarizes the characters used for each constraint type.
Character P U R
Note:
An additional constraint type is indicated by the character "V" in the CONSTRAINT_TYPE column. This constraint type corresponds to constraints created using the WITH CHECK OPTION for views.
Example 2: Distinguishing NOT NULL Constraints from CHECK Constraints In the previous example, several constraints are listed with a constraint type of C. To distinguish which constraints are NOT NULL constraints and which are CHECK constraints in the EMP_TAB and DEPT_TAB tables, submit the following query:
SELECT Constraint_name, Search_condition FROM User_constraints WHERE (Table_name = 'DEPT_TAB' OR Table_name = 'EMP_TAB') AND Constraint_type = 'C';
Considering the example CREATE TABLE statements at the beginning of this section, a list similar to this is returned:
CONSTRAINT_NAME --------------LOC_CHECK1 SYS_C00278 SYS_C00279 SEARCH_CONDITION ---------------------------------------loc IN ('NEW YORK', 'BOSTON', 'CHICAGO') ENAME IS NOT NULL DEPTNO IS NOT NULL
Notice that the following are explicitly listed in the SEARCH_CONDITION column:
The following query lists all columns that constitute the constraints defined on all tables accessible to you, the user:
Considering the example statements at the beginning of this section, a list similar to this is returned:
CONSTRAINT_NAME --------------DEPT_FKEY DNAME_UKEY DNAME_UKEY LOC_CHECK1 MGR_FKEY SYS_C00275 SYS_C00278 SYS_C00279 SYS_C00280 TABLE_NAME ----------EMP_TAB DEPT_TAB DEPT_TAB DEPT_TAB EMP_TAB DEPT_TAB EMP_TAB EMP_TAB EMP_TAB COLUMN_NAME --------------DEPTNO DNAME LOC LOC MGR DEPTNO ENAME DEPTNO EMPNO
Part II
PL/SQL for Application Developers
This part contains the following chapters:
Chapter 7, "Coding PL/SQL Procedures and Packages" Chapter 8, "Coding Dynamic SQL" Chapter 9, "Coding Triggers" Chapter 10, "Developing Flashback Applications" Chapter 11, "Developing Applications with the PL/SQL Web Toolkit" Chapter 12, "Developing PL/SQL Server Pages" Chapter 13, "Developing Applications with Database Change Notification"
7
Coding PL/SQL Procedures and Packages
This chapter describes some of the procedural capabilities of Oracle Database for application development, including:
Overview of PL/SQL Program Units Compiling PL/SQL Procedures for Native Execution Remote Dependencies Cursor Variables Handling PL/SQL Compile-Time Errors Handling Run-Time PL/SQL Errors Debugging Stored Procedures Calling Stored Procedures Calling Remote Procedures Calling Stored Functions from SQL Expressions Returning Large Amounts of Data from a Function Coding Your Own Aggregate Functions
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 in place of 3GL programs that use embedded SQL or Oracle Call Interface (OCI). PL/SQL program units include:
Anonymous Blocks Stored Program Units (Procedures, Functions, and Packages) Triggers
See Also:
Oracle Database PL/SQL User's Guide and Reference for syntax and examples of operations on PL/SQL packages Oracle Database PL/SQL Packages and Types Reference for information about the PL/SQL packages that come with Oracle Database
Anonymous Blocks
An anonymous block is a PL/SQL program 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 called 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. The following short example of a PL/SQL anonymous block prints the names of all employees in department 20 in the hr.employees table by using the DBMS_OUTPUT package:
DECLARE Last_name Cursor VARCHAR2(10); c1 IS SELECT last_name FROM employees WHERE department_id = 20;
BEGIN OPEN c1; LOOP FETCH c1 INTO Last_name; EXIT WHEN c1%NOTFOUND; DBMS_OUTPUT.PUT_LINE(Last_name); END LOOP; END; /
Note:
If you test this block using SQL*Plus, then enter the statement SET SERVEROUTPUT ON so that output using the DBMS_OUTPUT procedures (for example, PUT_LINE) is activated. Also, end the example with a slash (/) to activate it.
Exceptions let you handle Oracle Database error conditions within PL/SQL program logic. This allows your application to prevent the server from issuing an error that could cause the client application to end. The following anonymous block handles the
predefined Oracle Database exception NO_DATA_FOUND (which would result in an ORA-01403 error if not handled):
DECLARE Emp_number INTEGER := 9999; Emp_name VARCHAR2(10); BEGIN SELECT Ename INTO Emp_name FROM Emp_tab WHERE Empno = Emp_number; -- no such 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;
You can also define your own exceptions, declare them in the declaration part of a block, and define them in the exception part of the block. An example follows:
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 Ename INTO Emp_name FROM Emp_tab WHERE Empno = 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;
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 call stored procedures or to open cursor variables.
See Also:
Oracle Database PL/SQL Packages and Types Reference for complete information about the DBMS_OUTPUT package Oracle Database PL/SQL User's Guide and Reference and "Handling Run-Time PL/SQL Errors" on page 7-26 "Cursor Variables" on page 7-22
Has a name. Can take parameters, and can return values. Is stored in the data dictionary. Can be called by many users.
Note:
The term stored procedure is sometimes used generically for both stored procedures and stored functions. The only difference between procedures and functions is that functions always return a single value to the caller, while procedures do not return a value to the caller.
If you plan to call a stored procedure using a stub generated by SQL*Module, then the stored procedure name must also be a legal identifier in the calling host 3GL language, such as Ada or C.
PROCEDURE Get_emp_names (Dept_num IN NUMBER) IS Emp_name VARCHAR2(10); CURSOR c1 (Depno NUMBER) IS SELECT Ename FROM Emp_tab WHERE deptno = Depno; 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;
In this stored procedure example, the department number is an input parameter which is used when the parameterized cursor c1 is opened. The formal parameters of a procedure have three major attributes, described in Table 71.
Table 71 Attributes of Procedure Parameters
Parameter Attribute Description Name Mode This must be a legal PL/SQL identifier. 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.
Table 71 (Cont.) Attributes of Procedure Parameters Parameter Attribute Description Datatype This is a standard PL/SQL datatype.
Parameter Modes Parameter modes define the behavior of formal parameters. The three parameter modes, IN (the default), OUT, and IN OUT, can be used with any subprogram. However, avoid using the OUT and IN OUT modes with functions. The purpose of a function is to take no arguments and return a single value. It is poor programming practice to have a function return multiple values. Also, functions should be free from side effects, which change the values of variables not local to the subprogram. Table 72 summarizes the information about parameter modes.
Table 72 IN The default. Passes values to a subprogram. Parameter Modes OUT Must be specified. Returns values to the caller. IN OUT Must be specified. Passes initial values to a subprogram; returns updated values to the caller. Formal parameter acts like an initialized variable. Formal parameter should be assigned a value. Actual parameter must be a variable.
Formal parameter acts like a Formal parameter acts like an constant. uninitialized variable. Formal parameter cannot be Formal parameter cannot be assigned a value. used in an expression; must be assigned a value. Actual parameter can be a Actual parameter must be a constant, initialized variable, variable. literal, or expression.
See Also: Oracle Database PL/SQL User's Guide and Reference for details about parameter modes
Parameter Datatypes The datatype of a formal parameter consists of one of the following:
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 Get_emp_names procedure specification in "Parameters for Procedures and Functions" on page 7-4 could be written as the following:
PROCEDURE Get_emp_names(Dept_num IN Emp_tab.Deptno%TYPE)
This has the Dept_num parameter take the same datatype as the Deptno column in the Emp_tab 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, then it is not necessary to change the application code. If the Get_emp_names procedure is part of a package, then you can use previously-declared public (package) variables to constrain a parameter datatype. 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 following example defines the Get_emp_rec procedure, which returns all the columns of the Emp_tab table in a PL/SQL record for the given empno:
Caution:
PROCEDURE Get_emp_rec (Emp_number IN Emp_tab.Empno%TYPE, Emp_ret OUT Emp_tab%ROWTYPE) IS BEGIN SELECT Empno, Ename, Job, Mgr, Hiredate, Sal, Comm, Deptno INTO Emp_ret FROM Emp_tab WHERE Empno = Emp_number; END;
BEGIN Get_emp_rec(7499, Emp_row); DBMS_OUTPUT.PUT(Emp_row.Ename DBMS_OUTPUT.PUT(' ' DBMS_OUTPUT.PUT(' ' DBMS_OUTPUT.PUT(' ' DBMS_OUTPUT.NEW_LINE; END;
Stored functions can also return values that are declared using %ROWTYPE. For example:
FUNCTION Get_emp_rec (Dept_num IN Emp_tab.Deptno%TYPE) RETURN Emp_tab%ROWTYPE IS ...
Tables and Records You can pass PL/SQL tables as parameters to stored procedures and functions. You can also pass tables of records as parameters.
Note:
When passing a user defined type, such as a PL/SQL table or record to a remote procedure, to make PL/SQL use the same definition so that the type checker can verify the source, you must create a redundant loop back DBLINK so that when the PL/SQL compiles, both sources pull from the same location.
Default Parameter Values Parameters can take default values. Use the DEFAULT keyword or the assignment operator to give a parameter a default value. For example, the specification for the Get_emp_names procedure could be written as the following:
PROCEDURE Get_emp_names (Dept_num IN NUMBER DEFAULT 20) IS ...
or
PROCEDURE Get_emp_names (Dept_num IN NUMBER := 20) IS ...
When a parameter takes a default value, it can be omitted from the actual parameter list when you call the procedure. When you do specify the parameter value on the call, it overrides the default 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 procedure. In fact, it is an error to use it.
For example, to use the example in "%TYPE and %ROWTYPE Attributes" on page 7-5, create a text (source) file called get_emp.sql containing the following code:
CREATE PROCEDURE Get_emp_rec (Emp_number IN Emp_tab.Empno%TYPE, Emp_ret OUT Emp_tab%ROWTYPE) AS BEGIN SELECT Empno, Ename, Job, Mgr, Hiredate, Sal, Comm, Deptno INTO Emp_ret FROM Emp_tab WHERE Empno = Emp_number; END; /
Then, using an interactive tool such as SQL*Plus, load the text file containing the procedure by entering the following statement:
SQL> @get_emp
This loads the procedure into the current schema from the get_emp.sql file (.sql is the default file extension). Note the slash (/) at the end of the code. This is not part of the code; it just activates the loading of the procedure. Use the CREATE [OR REPLACE] FUNCTION... statement to store functions.
Caution: When developing a new procedure, it is usually much more convenient to use the CREATE OR REPLACE PROCEDURE statement. This replaces any previous version of that procedure in the same schema with the newer version, but note that this is done without warning.
You can use either the keyword IS or AS after the procedure parameter list.
See Also:
Oracle Database Reference for the complete syntax of the CREATE PROCEDURE and CREATE FUNCTION statements
Privileges to Create Procedures and Functions To create a standalone procedure or function, or package specification or body, you must meet the following prerequisites:
You must have the CREATE PROCEDURE system privilege to create a procedure or package in your schema, or the CREATE ANY PROCEDURE system privilege to create a procedure or package in another user's schema.
Note:
To create without errors (to compile the procedure or package successfully) requires the following additional privileges: The owner of the procedure 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 procedure or package change, then the procedure must be reauthenticated before it is run. If a necessary privilege to a referenced object is revoked from the owner of the procedure or package, then the procedure cannot be run. The EXECUTE privilege on a procedure gives a user the right to run a procedure owned by another user. Privileged users run the procedure under the security domain of the owner of the procedure. Therefore, users never need to be granted the privileges to the objects referenced by a procedure. This allows for more disciplined and efficient security strategies with database applications and their users. Furthermore, all procedures 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 procedures and packages.
Note:
Package creation requires a sort. So the user creating the package should be able to create a sort segment in the temporary tablespace with which the user is associated. "Privileges Required to Execute a Procedure" on page 7-33
See Also:
PACKAGE BODY, and DROP PACKAGE, respectively. A DROP PACKAGE statement drops both the specification and body of a package. The following statement drops the Old_sal_raise procedure in your schema:
DROP PROCEDURE Old_sal_raise;
Privileges to Drop Procedures and Functions To drop a procedure, function, or package, the procedure or package must be in your schema, or you must have the DROP ANY PROCEDURE privilege. An individual procedure within a package cannot be dropped; the containing package specification and body must be re-created without the procedures to be dropped.
External Procedures
A PL/SQL procedure executing on an Oracle Database instance can call an external procedure written in a 3GL. The 3GL procedure runs in a separate address space from that of the database.
See Also:
Chapter 14, "Calling External Procedures" for information about external procedures
PL/SQL Packages
A package is an encapsulated collection of related program objects (for example, procedures, functions, variables, constants, cursors, and exceptions) stored together in the database. Using packages is an alternative to creating procedures and functions as standalone schema objects. Packages have many advantages over standalone procedures and functions. 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 procedures and functions in the package. Let you overload procedures or functions. Overloading a procedure means creating multiple procedures with the same name in the same package, each taking arguments of different number or datatype.
See Also:
Oracle Database PL/SQL User's Guide and 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 the objects declared in the specification, as well as private objects that are not visible to applications outside the package. Example of a PL/SQL Package Specification and Body The following example shows a package specification for a package named Employee_management. The package contains one stored function and two stored procedures. The body for this package defines the function and the procedures:
CREATE PACKAGE BODY Employee_management AS FUNCTION Hire_emp (Name VARCHAR2, Job VARCHAR2, Coding PL/SQL Procedures and Packages 7-9
Mgr NUMBER, Hiredate DATE, Sal NUMBER, Comm NUMBER, Deptno NUMBER) RETURN NUMBER IS New_empno NUMBER(10); -----This function accepts all arguments for the fields in the employee table except for the employee number. A value for this field is supplied by a sequence. The function returns the sequence number generated by the call to this function. BEGIN SELECT Emp_sequence.NEXTVAL INTO New_empno FROM dual; INSERT INTO Emp_tab VALUES (New_empno, Name, Job, Mgr, Hiredate, Sal, Comm, Deptno); RETURN (New_empno); END Hire_emp; PROCEDURE fire_emp(emp_id IN NUMBER) AS -- This procedure deletes the employee with an employee -- number that corresponds to the argument Emp_id. If -- no employee is found, then an exception is raised. BEGIN DELETE FROM Emp_tab WHERE Empno = 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) AS -----This procedure accepts two arguments. Emp_id is a number that corresponds to an employee number. SAL_INCR is the amount by which to increase the employee's salary. If employee exists, then update salary with increase.
BEGIN UPDATE Emp_tab SET Sal = Sal + Sal_incr WHERE Empno = 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;
Note:
If you want to try this example, then first create the sequence number Emp_sequence. Do this with the following SQL*Plus statement:
SQL> CREATE SEQUENCE Emp_sequence > START WITH 8000 INCREMENT BY 10;
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 procedures and functions declared in the package specification. You can also define private, or local, package procedures, functions, and variables in a package body. These objects can only be accessed by other procedures and functions 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 would then be the following:
CREATE OR REPLACE PACKAGE Package_name AS ...
and
CREATE OR REPLACE PACKAGE BODY Package_name AS ...
Procedures and functions declared in the package specification. Definitions of cursors declared in the package specification. Local procedures and functions, not declared in the package specification. Local variables.
Procedures, functions, cursors, and variables that are declared in the package specification are global. They can be called, 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, make sure that each procedure that you define in the body has the same parameters, by name, datatype, 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 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 procedure or function.
Coding PL/SQL Procedures and Packages 7-11
See Also:
"Privileges to Create Procedures and Functions" on page 7-8 "Privileges to Drop Procedures and Functions" on page 7-9
The second time a session makes such a package call, the package is reinstantiated for the session without error.
Note:
For optimal performance, Oracle Database returns this error message only onceeach time the package state is discarded. If you handle this error in your application, ensure that your error handling strategy can accurately handle this error. For example, when a procedure in one package calls a procedure in another package, your application should be aware that the session state is lost for both packages.
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.
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 the following:
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 if you should 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, refer to the Oracle Database PL/SQL User's Guide and Reference.
Without the bulk bind, PL/SQL sends a SQL statement to the SQL engine for each employee that is updated, leading to context switches that hurt performance.
If you have a set of rows prepared in a PL/SQL table, you can bulk-insert or bulk-update the data using a loop like:
FORALL i in Emp_Data.FIRST..Emp_Data.LAST INSERT INTO Emp_tab VALUES(Emp_Data(i));
SELECT Statements that Reference Collections The BULK COLLECT INTO clause can improve the performance of queries that reference collections. For example, the following PL/SQL block queries multiple values into PL/SQL tables, both with and without bulk binds:
-- Find all employees whose manager's ID number is 7698. DECLARE TYPE Var_tab IS TABLE OF VARCHAR2(20) INDEX BY BINARY_INTEGER; Empno VAR_TAB; Ename VAR_TAB; Counter NUMBER; CURSOR C IS SELECT Empno, Ename FROM Emp_tab WHERE Mgr = 7698; BEGIN -- Efficient method, using a bulk bind SELECT Empno, Ename BULK COLLECT INTO Empno, Ename FROM Emp_Tab WHERE Mgr = 7698; -- Slower method, assigning each collection element within a loop. counter := 1; FOR rec IN C LOOP Empno(Counter) := rec.Empno; Ename(Counter) := rec.Ename; Counter := Counter + 1; END LOOP; END;
You can use BULK COLLECT INTO with tables of scalar values, or tables of %TYPE values. Without the bulk bind, PL/SQL sends a SQL statement to the SQL engine for each employee that is selected, leading to context switches that hurt performance. FOR Loops that Reference Collections and the Returning Into Clause You can use the FORALL keyword along with the BULK COLLECT INTO keywords to improve the performance of FOR loops that reference collections and return DML. For example, the following PL/SQL block updates the Emp_tab table by computing bonuses for a collection of employees; then it returns the bonuses in a column called Bonlist. The actions are performed both with and without using bulk binds:
DECLARE TYPE Emplist IS VARRAY(100) OF NUMBER; Empids EMPLIST := EMPLIST(7369, 7499, 7521, 7566, 7654, 7698); TYPE Bonlist IS TABLE OF Emp_tab.sal%TYPE; Bonlist_inst BONLIST; BEGIN Bonlist_inst := BONLIST(1,2,3,4,5); FORALL i IN Empids.FIRST..empIDs.LAST
Remote Dependencies
UPDATE Emp_tab SET Bonus = 0.1 * Sal WHERE Empno = Empids(i) RETURNING Sal BULK COLLECT INTO Bonlist; FOR i IN Empids.FIRST..Empids.LAST LOOP UPDATE Emp_tab Set Bonus = 0.1 * sal WHERE Empno = Empids(i) RETURNING Sal INTO BONLIST(i); END LOOP; END;
Without the bulk bind, PL/SQL sends a SQL statement to the SQL engine for each employee that is updated, leading to context switches that hurt performance.
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 User's Guide and Reference for details on PL/SQL native compilation Oracle Database Java Developer's Guide for details on Java native compilation
Remote Dependencies
Dependencies among PL/SQL program units can be handled in two ways:
Timestamps Signatures
Remote Dependencies
Timestamps
If timestamps are used to handle dependencies among PL/SQL program units, then whenever you alter a program unit or a relevant schema object, all of its dependent units are marked as invalid and must be recompiled before they can be run. Each program unit carries a timestamp that is set by the server when the unit is created or recompiled. Figure 71 demonstrates this graphically. Procedures P1 and P2 call stored procedure P3. Stored procedure P3 references table T1. In this example, each of the procedures is dependent on table T1. P3 depends upon T1 directly, while P1 and P2 depend upon T1 indirectly.
Figure 71
P1
Dependency Relationships
P3 T1
P2
If P3 is altered, then P1 and P2 are marked as invalid immediately, if they are on the same server as P3. The compiled states of P1 and P2 contain records of the timestamp of P3. Therefore, if the procedure P3 is altered and recompiled, then the timestamp on P3 no longer matches the value that was recorded for P3 during the compilation of P1 and P2. If P1 and P2 are on a client system, or on another Oracle Database instance in a distributed environment, then the timestamp information is used to mark them as invalid at runtime.
Signatures
To alleviate some of the problems with the timestamp-only dependency model, Oracle Database provides the additional capability of remote dependencies using signatures.
Remote Dependencies
The signature capability affects only remote dependencies. Local (same server) dependencies are not affected, as recompilation is always possible in this environment. A signature is associated with each compiled stored program unit. It identifies the unit using the following criteria:
The name of the unit (the package, procedure, or function name). The types of each of the parameters of the subprogram. The modes of the parameters (IN, OUT, IN OUT). The number of parameters. The type of the return value for a function.
The user has control over whether signatures or timestamps govern remote dependencies.
See Also:
When the signature dependency model is used, a dependency on a remote program unit causes an invalidation of the dependent unit if the dependent unit contains a call to a subprogram in the parent unit, and if the signature of this subprogram has been changed in an incompatible manner. For example, consider a procedure get_emp_name stored on a server in Boston (BOSTON_SERVER). The procedure is defined as the following:
Note:
You may need to set up data structures, similar to the following, for certain examples to work:
CONNECT system/manager CREATE PUBLIC DATABASE LINK boston_server USING 'inst1_alias'; CONNECT scott/tiger
CREATE OR REPLACE PROCEDURE get_emp_name ( emp_number IN NUMBER, hire_date OUT VARCHAR2, emp_name OUT VARCHAR2) AS BEGIN SELECT ename, to_char(hiredate, 'DD-MON-YY') INTO emp_name, hire_date FROM emp WHERE empno = emp_number; END;
When get_emp_name is compiled on BOSTON_SERVER, its signature, as well as its timestamp, is recorded. Suppose that on another server in California, some PL/SQL code calls get_emp_name identifying it using a DBlink called BOSTON_SERVER, as follows:
CREATE OR REPLACE PROCEDURE print_ename (emp_number IN NUMBER) AS hire_date VARCHAR2(12); ename VARCHAR2(10); BEGIN get_emp_name@BOSTON_SERVER(emp_number, hire_date, ename); dbms_output.put_line(ename); dbms_output.put_line(hire_date); END;
Remote Dependencies
When this California server code is compiled, the following actions take place:
A connection is made to the Boston server. The signature of get_emp_name is transferred to the California server. The signature is recorded in the compiled state of print_ename.
At runtime, during the remote procedure call from the California server to the Boston server, the recorded signature of get_emp_name that was saved in the compiled state of print_ename gets sent to the Boston server, regardless of whether or not there were any changes. If the timestamp dependency mode is in effect, then a mismatch in timestamps causes an error status to be returned to the calling procedure. However, if the signature mode is in effect, then any mismatch in timestamps is ignored, and the recorded signature of get_emp_name in the compiled state of Print_ename on the California server is compared with the current signature of get_emp_name on the Boston server. If they match, then the call succeeds. If they do not match, then an error status is returned to the print_name procedure. Note that the get_emp_name procedure on the Boston server could have been changed. Or, its timestamp could be different from that recorded in the print_name procedure on the California server, possibly due to the installation of a new release of the server. As long as the signature remote dependency mode is in effect on the California server, a timestamp mismatch does not cause an error when get_emp_name is called.
Note: DETERMINISTIC, PARALLEL_ENABLE, and purity information do not show in the signature mode. Optimizations based on these settings are not automatically reconsidered if a function on a remote system is redefined with different settings. This may lead to incorrect query results when calls to the remote function occur, even indirectly, in a SQL statement, or if the remote function is used, even indirectly, in a function-based index.
Remote Dependencies
Date types: DATE, TIMESTAMP, TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH LOCAL TIME ZONE, INTERVAL YEAR TO MONTH, INTERVAL DAY TO SECOND Modes Changing to or from an explicit specification of the default parameter mode IN does not change the signature of a subprogram. For example, changing between:
PROCEDURE P1 (Param1 NUMBER); PROCEDURE P1 (Param1 IN NUMBER);
does not change the signature. Any other change of parameter mode does change the signature. Default Parameter Values Changing the specification of a default parameter value does not change the signature. For example, procedure P1 has the same signature in the following two examples:
PROCEDURE P1 (Param1 IN NUMBER := 100); PROCEDURE P1 (Param1 IN NUMBER := 200);
An application developer who requires that callers get the new default value must recompile the called procedure, but no signature-based invalidation occurs when a default parameter value assignment is changed.
The specification of the procedure has not changed, so its signature has not changed. But if the procedure specification is changed to the following:
CREATE OR REPLACE PROCEDURE Get_emp_name ( Emp_number IN NUMBER, Hire_date OUT DATE, Emp_name OUT VARCHAR2) AS
And if the body is changed accordingly, then the signature changes, because the parameter Hire_date has a different datatype. However, if the name of that parameter changes to When_hired, and the datatype remains VARCHAR2, and the mode remains OUT, the signature does not change. Changing the name of a formal parameter does not change the signature of the unit. Consider the following example:
CREATE OR REPLACE PACKAGE Emp_package AS TYPE Emp_data_type IS RECORD ( Emp_number NUMBER,
Remote Dependencies
Hire_date VARCHAR2(12), Emp_name VARCHAR2(10)); PROCEDURE Get_emp_data (Emp_data IN OUT Emp_data_type); END; CREATE OR REPLACE PACKAGE BODY Emp_package AS PROCEDURE Get_emp_data (Emp_data IN OUT Emp_data_type) IS BEGIN SELECT Empno, Ename, TO_CHAR(Hiredate, 'DD/MON/YY') INTO Emp_data FROM Emp_tab WHERE Empno = Emp_data.Emp_number; END; END;
If the package specification is changed so that the record's field names are changed, but the types remain the same, then this does not affect the signature. For example, the following package specification has the same signature as the previous package specification example:
CREATE OR REPLACE PACKAGE Emp_package AS TYPE Emp_data_type IS RECORD ( Emp_num NUMBER, -- was Emp_number Hire_dat VARCHAR2(12), -- was Hire_date Empname VARCHAR2(10)); -- was Emp_name PROCEDURE Get_emp_data (Emp_data IN OUT Emp_data_type); END;
Changing the name of the type of a parameter does not cause a change in the signature if the type remains the same as before. For example, the following package specification for Emp_package is the same as the first one:
CREATE OR REPLACE PACKAGE Emp_package AS TYPE Emp_data_record_type IS RECORD ( Emp_number NUMBER, Hire_date VARCHAR2(12), Emp_name VARCHAR2(10)); PROCEDURE Get_emp_data (Emp_data IN OUT Emp_data_record_type); END;
Then only timestamps are used to resolve dependencies (if this is not explicitly overridden dynamically).
Then signatures are used to resolve dependencies (if this not explicitly overridden dynamically).
Remote Dependencies
You can alter the mode dynamically by using the DDL statements. For example, this example alters the dependency model for the current session:
ALTER SESSION SET REMOTE_DEPENDENCIES_MODE = {SIGNATURE | TIMESTAMP} Thise example alters the dependency model 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 DDL statements, then 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 timestamp dependency model. When you use REMOTE_DEPENDENCIES_MODE=SIGNATURE:
If you change the default 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 default value is used. In this case, because invalidation/recompilation does not automatically occur, the old default value is used. If you want to see the new default values, then you must recompile the calling procedure manually. If you add a new overloaded procedure in a package (a new 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 timestamp mode, then this rebinding does not happen under the signature mode, because the local procedure does not get invalidated. You must recompile the local procedure manually to achieve the new rebinding. If the types of parameters of an existing packaged 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.
Dependency Resolution
When REMOTE_DEPENDENCIES_MODE = TIMESTAMP (the default value), dependencies among program units are handled by comparing timestamps at runtime. If the timestamp of a called remote procedure does not match the timestamp 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 timestamp dependency mode, 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 timestamp in the calling unit is first compared to the current timestamp in the called remote unit. If they match, then the call proceeds. If the timestamps do not match, then the signature of the called remote subprogram, as recorded in the calling subprogram, is compared with the current signature of the called subprogram. If they do not match (using the criteria described in the section "When Does a Signature Change?" on page 7-18), then an error is returned to the calling session.
Cursor Variables
Server-side PL/SQL users can set the parameter to TIMESTAMP (or let it default to that) to get the timestamp dependency mode. Server-side PL/SQL users can choose to use the signature dependency mode if they have a distributed system and they want to avoid possible unnecessary recompilations. Client-side PL/SQL users should set the parameter to SIGNATURE. This allows: Installation of new applications at client sites, without the need to recompile procedures. Ability to upgrade the server, without encountering timestamp mismatches.
When using signature mode on the server side, add new procedures to the end of the procedure (or function) declarations in a package specification. Adding a new procedure in the middle of the list of declarations can cause unnecessary invalidation and recompilation of dependent procedures.
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 procedures and functions. A cursor variable can also refer to different cursors in its lifetime. Some additional advantages of cursor variables include:
Encapsulation Queries are centralized in the stored procedure that opens the cursor variable. Ease of maintenance If you need to change the cursor, then you only need to make the change in one place: the stored procedure. There is no need to change each application. Convenient security The user of the application is the username used when the application connects to the server. The user must have EXECUTE permission on the stored procedure that opens the cursor. But, the user does not need to have READ permission on the tables used in the query. This capability can be used to limit access to the columns in the table, as well as access to other stored procedures.
See Also: Oracle Database PL/SQL User's Guide and Reference for details on cursor variables
Cursor Variables
Pro*C/C++ Programmer's Guide Pro*COBOL Programmer's Guide Oracle Call Interface Programmer's Guide Oracle SQL*Module for Ada Programmer's Guide
Fetching Data
The following package defines a PL/SQL cursor variable type Emp_val_cv_type, and two procedures. The first procedure, Open_emp_cv, opens the cursor variable using a bind variable in the WHERE clause. The second procedure, Fetch_emp_data, fetches rows from the Emp_tab table using the cursor variable.
CREATE OR REPLACE PACKAGE Emp_data AS TYPE Emp_val_cv_type IS REF CURSOR RETURN Emp_tab%ROWTYPE; PROCEDURE Open_emp_cv (Emp_cv IN OUT Emp_val_cv_type, Dept_number IN INTEGER); PROCEDURE Fetch_emp_data (emp_cv IN Emp_val_cv_type, emp_row OUT Emp_tab%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 INTEGER) IS BEGIN OPEN emp_cv FOR SELECT * FROM Emp_tab WHERE deptno = dept_number; END open_emp_cv; PROCEDURE Fetch_emp_data (Emp_cv IN Emp_val_cv_type, Emp_row OUT Emp_tab%ROWTYPE) IS BEGIN FETCH Emp_cv INTO Emp_row; END Fetch_emp_data; END Emp_data;
The following example shows how to call the Emp_data package procedures from a PL/SQL block:
DECLARE -- declare a cursor variable Emp_curs Emp_data.Emp_val_cv_type; Dept_number Dept_tab.Deptno%TYPE; Emp_row Emp_tab%ROWTYPE; BEGIN Dept_number := 20; -- open the cursor using a variable Emp_data.Open_emp_cv(Emp_curs, Dept_number); -- fetch the data and display it LOOP Emp_data.Fetch_emp_data(Emp_curs, Emp_row); EXIT WHEN Emp_curs%NOTFOUND; DBMS_OUTPUT.PUT(Emp_row.Ename || ' '); DBMS_OUTPUT.PUT_LINE(Emp_row.Sal); END LOOP;
END;
You can call the Open_cv procedure to open the cursor variable and point it to either a query on the Emp_tab table or the Dept_tab table. The following PL/SQL block shows how to fetch using the cursor variable, and then use the ROWTYPE_MISMATCH predefined exception to handle either fetch:
DECLARE Emp_rec Emp_tab%ROWTYPE; Dept_rec Dept_tab%ROWTYPE; Cv Emp_dept_data.CV_TYPE; BEGIN Emp_dept_data.open_cv(Cv, 1); -- Open Cv For Emp_tab Fetch Fetch cv INTO Dept_rec; -- but fetch into Dept_tab record -- which raises ROWTYPE_MISMATCH DBMS_OUTPUT.PUT(Dept_rec.Deptno); DBMS_OUTPUT.PUT_LINE(' ' || Dept_rec.Loc); EXCEPTION WHEN ROWTYPE_MISMATCH THEN BEGIN DBMS_OUTPUT.PUT_LINE ('Row type mismatch, fetching Emp_tab data...'); FETCH Cv INTO Emp_rec; DBMS_OUTPUT.PUT(Emp_rec.Deptno); DBMS_OUTPUT.PUT_LINE(' ' || Emp_rec.Ename); END;
SQL> @proc1
And, if there are one or more errors in the code, then you receive a notice such as the following:
MGR-00072: Warning: Procedure proc1 created with compilation errors
In this case, use the SHOW ERRORS statement in SQL*Plus to get a list of the errors that were found. SHOW ERRORS with no argument lists the errors from the most recent compilation. You can qualify SHOW ERRORS using the name of a procedure, function, package, or package body:
SQL> SHOW ERRORS PROC1 SQL> SHOW ERRORS PROCEDURE PROC1
See Also:
SQL*Plus User's Guide and Reference for complete information about the SHOW ERRORS statement
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
Assume that you want to create a simple procedure that deletes records from the employee table using SQL*Plus:
CREATE OR REPLACE PROCEDURE Fire_emp(Emp_id NUMBER) AS BEGIN DELETE FROM Emp_tab WHER Empno = Emp_id; END /
Notice that the CREATE PROCEDURE statement has two errors: the DELETE statement has an error (the E is absent from WHERE), and the semicolon is missing after END. After the CREATE PROCEDURE statement is entered and an error is returned, a SHOW ERRORS statement returns the following lines:
SHOW ERRORS; ERRORS FOR PROCEDURE Fire_emp: LINE/COL ERROR -------------- -------------------------------------------3/27 PL/SQL-00103: Encountered the symbol "EMPNO" wh. . . 5/0 PL/SQL-00103: Encountered the symbol "END" when . . . 2 rows selected.
Notice that each line and column number where errors were found is listed by the SHOW ERRORS statement. Alternatively, you can query the following data dictionary views to list errors when using any tool or application:
The error text associated with the compilation of a procedure is updated when the procedure is replaced, and it is deleted when the procedure is dropped.
Coding PL/SQL Procedures and Packages 7-25
Original source code can be retrieved from the data dictionary using the following views: ALL_SOURCE, USER_SOURCE, and DBA_SOURCE.
See Also:
Oracle Database Reference for more information about these data dictionary views
This procedure stops procedure execution, rolls back any effects of the procedure, 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. Error number -20000 should be used 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 Kbytes or less (longer messages are ignored). Keep_error_stack can be TRUE if you want to add the error to any already on the stack, or FALSE if you want to replace the existing errors. By default, this option is FALSE.
Note:
Some of the Oracle Database packages, such as DBMS_OUTPUT, DBMS_DESCRIBE, and DBMS_ALERT, use application error numbers in the range -20000 to -20005. Refer to 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, the following exception handler selects the string for the associated user-defined error message and calls 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); ...
See Also: "Handling Errors in Remote Procedures" on page 7-28 for information on exception handling when calling remote procedures
The following section includes an example of passing a user-specified error number from a trigger to a procedure.
Enter a RAISE statement that names the appropriate exception. A RAISE statement stops the execution of the procedure, and control passes to an exception handler (if any). Call 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 72 on page 7-27 illustrates the following:
An exception and associated exception handler in a procedure 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 calling environment (in this case, a procedure), and how that application can define an exception that corresponds to the user-specified error number
Declare a user-defined exception in a procedure or package body (private exceptions), or in the specification of a package (public exceptions). Define an exception handler in the body of a procedure (standalone or package).
Figure 72 Exceptions and User-Defined Errors
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;
Table EMP
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;
Unhandled Exceptions
In database PL/SQL program 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 program units propagate back to client-side applications that call the containing program unit. In such an application, only the application program unit call 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 program units are propagated back to database applications, then the database PL/SQL code should be modified to handle the exceptions. Your application can also trap for unhandled exceptions when calling database program units and handle such errors appropriately.
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 and ORA-02015. Application exceptions, which are generated using the RAISE_APPLICATION_ERROR() procedure.
When using local procedures, all of these messages can be trapped by writing an exception handler, such as shown in the following example:
EXCEPTION WHEN ZERO_DIVIDE THEN /* ...handle the exception */
Notice that 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, as shown in the following 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 calling a remote procedure, exceptions are also handled by creating a local exception handler. The remote procedure must return an error number to the local calling procedure, which then handles the exception, as shown in the previous example. Because PL/SQL user-defined exceptions always return ORA-06510 to the local procedure, these exceptions cannot be handled. All other remote exceptions can be handled in the same manner as local exceptions.
Adding extra output statements to verify execution progress and check data values at certain points within the procedure. Running a separate debugger to analyze execution in greater detail.
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/technology/.
DBMS_OUTPUT Package
You can also debug stored procedures and triggers using the DBMS_OUTPUT supplied package. Put PUT and PUT_LINE statements in your code to output the value of variables and expressions to your terminal.
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 execute code lines step by step, the debugging connection must be granted DEBUG privilege on the relevant code
Caution: The DEBUG privilege effectively allows a debugging session to do anything that the procedure 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. The PLSQL_DEBUG parameter and the DEBUG keyword on commands such as ALTER PACKAGE can be used to control whether the PL/SQL compiler includes debug information in its results. If it does not, variables will not be accessible, and neither stepping nor breakpoints will stop on code lines. The PL/SQL compiler will never generate debug information for code that has been obfuscated using the PL/SQL wrap utility.
See Also: Oracle Database PL/SQL User's Guide and Reference, "Obfuscating PL/SQL Source Code"
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. Refer to 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.
Oracle Procedure Builder Developer's Guide Oracle Database PL/SQL Packages and Types Reference for more information about the DBMS_DEBUG and DBMS_OUTPUT packages and associated privileges The Oracle JDeveloper documentation for information on using package DBMS_DEBUG_JDWP Oracle Database SQL Reference for more details on privileges The PL/SQL page at http://www.oracle.com/technology/ for information about writing low-level debug code
You may need to set up data structures, similar to the following, for certain examples to work:
CREATE TABLE Emp_tab ( Empno NUMBER(4) NOT NULL, Ename VARCHAR2(10), Job VARCHAR2(9), Mgr NUMBER(4), Hiredate DATE, Sal NUMBER(7,2), Comm NUMBER(7,2), Deptno NUMBER(2)); CREATE OR REPLACE PROCEDURE fire_emp1(Emp_id NUMBER) AS BEGIN DELETE FROM Emp_tab WHERE Empno = Emp_id; END; VARIABLE Empnum NUMBER;
A procedure can be interactively called by a user using an Oracle Database tool. A procedure can be explicitly called within an application, such as a SQL*Forms or a precompiler application. A stored function can be called from a SQL statement in a manner similar to calling a built-in SQL function, such as LENGTH or ROUND.
This section includes some common examples of calling procedures from within these environments.
See Also:
page 7-36
This line calls the Sal_raise procedure. Emp_id is a variable within the context of the procedure. Recursive procedure calls are allowed within PL/SQL: A procedure can call itself.
Note:
Interactive tools, such as SQL*Plus, require you to follow these lines with a slash (/) to run the PL/SQL block.
An easier way to run a block is to use the SQL*Plus statement EXECUTE, which wraps BEGIN and END statements around the code you enter. For example:
EXECUTE Sal_raise(7369, 200);
Some interactive tools allow session variables to be created. For example, when using SQL*Plus, the following statement creates a session variable:
VARIABLE Assigned_empno NUMBER
After defined, any session variable can be used for the duration of the session. For example, you might run a function and capture the return value using a session variable:
EXECUTE :Assigned_empno := Hire_emp('JSMITH', 'President', 1032, SYSDATE, 5000, NULL, 10); PRINT Assigned_empno; ASSIGNED_EMPNO -------------2893
See Also:
SQL*Plus User's Guide and Reference Your tools documentation for information about performing similar operations using your development tool
In this case, :Empno is a host (bind) variable within the context of the application. To run a procedure within the code of a precompiler application, you must use the EXEC call interface. For example, the following statement calls the Fire_emp procedure in the code of a precompiler application:
EXEC SQL EXECUTE BEGIN Fire_emp1(:Empnum); END; END-EXEC;
See Also: For information about calling PL/SQL procedures from within 3GL applications:
Oracle Call Interface Programmer's Guide Pro*C/C++ Programmer's Guide Oracle SQL*Module for Ada Programmer's Guide
You must have the EXECUTE privilege for the standalone procedure or package containing the procedure, or you must have the EXECUTE ANY PROCEDURE system privilege. If you are executing a remote procedure, then 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 call. For example:1
EXECUTE Jward.Fire_emp (1043); EXECUTE Jward.Hire_fire.Fire_emp (1043);
If the procedure is a definer's-rights procedure, then it runs with the privileges of the procedure owner. The owner must have all the necessary object privileges for any referenced objects. If the procedure is an invoker's-rights procedure, then it runs with your privileges (as the invoker). In this case, you also need privileges on all referenced objects; that is, all objects accessed by the procedure through external references that are resolved in your schema. You may hold these privileges directly or through a role. Roles are enabled unless an invoker's-rights procedure is called directly or indirectly by a definer's-rights procedure.
List the values in the order the arguments appear in the procedure declaration. Specify the argument names and corresponding values, in any order.
For example, these statements each call the procedure Sal_raise to increase the salary of employee number 7369 by 500:
Sal_raise(7369, 500); Sal_raise(Sal_incr=>500, Emp_id=>7369); Sal_raise(7369, Sal_incr=>500);
The first statement identifies the argument values by listing them in the order in which they appear in the procedure specification. The second statement identifies the argument values by name and in an order different from that of the procedure specification. If you use argument names, then you can list the arguments in any order. The third statement identifies the argument values using a combination of these methods. If you use a combination of order and argument names, then values identified in order must precede values identified by name. If you used the DEFAULT option to define default values for IN parameters to a subprogram (see the Oracle Database PL/SQL User's Guide and Reference),then you can pass different numbers of actual parameters to the first subprogram, accepting or overriding the default values as you please. If an actual value is not passed, then the corresponding default value is used. If you want to assign a value to an argument that occurs after an omitted argument (for which the corresponding default is used), then you must explicitly designate the name of the argument, as well as its value.
You may need to set up the following data structures for certain examples to work: CONNECT SYS/password AS SYSDBA;CREATE USER Jward IDENTIFIED BY Jward;GRANT CREATE ANY PACKAGE TO Jward;GRANT CREATE SESSION TO Jward;GRANT EXECUTE ANY PROCEDURE TO Jward;CONNECT Scott/Tiger
EXECUTE fire_emp1@boston_server(1043);
See Also: "Handling Errors in Remote Procedures" on page 7-28 for information on exception handling when calling remote procedures
The following list explains how to properly call remote procedures, depending on the calling environment.
Remote procedures (standalone and packaged) can be called from within a procedure, an OCI application, or a precompiler application by specifying the remote procedure name, a database link, and the arguments for the remote procedure.
CREATE OR REPLACE PROCEDURE local_procedure(arg IN NUMBER) AS BEGIN fire_emp1@boston_server(arg); END;
In the previous example, you could create a synonym for FIRE_EMP1@BOSTON_SERVER. This would enable you to call the remote procedure from an Oracle Database tool application, such as a SQL*Forms application, as well from within a procedure, OCI application, or precompiler application.
CREATE SYNONYM synonym1 for fire_emp1@boston_server; CREATE OR REPLACE PROCEDURE local_procedure(arg IN NUMBER) AS BEGIN synonym1(arg); END;
If you do not want to use a synonym, then you could write a local cover procedure to call the remote procedure.
DECLARE arg NUMBER; BEGIN local_procedure(arg); END;
Caution: Unlike stored procedures, which use compile-time binding, runtime binding is used when referencing remote procedures. The user account to which you connect depends on the database link.
All calls to remotely stored procedures are assumed to perform updates; therefore, this type of referencing always requires two-phase commit of that transaction (even if the remote procedure is read-only). Furthermore, if a transaction that includes a remote procedure call is rolled back, then the work done by the remote procedure is also rolled back. A procedure called remotely can usually execute a COMMIT, ROLLBACK, or SAVEPOINT statement, the same as a local procedure. However, there are some differences in behavior:
If the transaction was originated by a non-Oracle database, as may be the case in XA applications, these operations are not allowed in the remote procedure. After doing one of these operations, the remote procedure cannot start any distributed transactions of its own. If the remote procedure does not commit or roll back its work, the commit is done implicitly when the database link is closed. In the meantime, further calls to the remote procedure are not allowed because it is still considered to be performing a transaction.
A distributed update modifies data on two or more databases. A distributed update is possible using a procedure 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 procedures that perform distributed updates. Pay special attention when using a local procedure that calls a remote procedure. If a timestamp mismatch is found during execution of the local procedure, then the remote procedure is not run, and the local procedure is invalidated.
Hide the identity of the name and owner of a procedure or package. Provide location transparency for remotely stored procedures (standalone or within a package).
When a privileged user needs to call a procedure, an associated synonym can be used. Because the procedures defined within a package are not individual objects (the package is the object), synonyms cannot be created for individual procedures within a package.
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 would otherwise need to be evaluated by the application. Manipulate character strings to represent special datatypes (for example, latitude, longitude, or temperature). Provide parallel query execution: If the query is parallelized, then SQL statements in your PL/SQL function may also be run in parallel (using the parallel query option).
The select list of the SELECT statement. The condition of the WHERE and HAVING clause. The CONNECT BY, START WITH, ORDER BY, and GROUP BY clauses. The VALUES clause of the INSERT statement. The SET clause of the UPDATE statement.
You cannot call stored PL/SQL functions from a CHECK constraint clause of a CREATE or ALTER TABLE statement or use them to specify a default value for a column. These situations require an unchanging definition.
Note:
Unlike functions, which are called as part of an expression, procedures are called as statements. Therefore, PL/SQL procedures are not directly callable from SQL statements. However, functions called from a PL/SQL statement or referenced in a SQL expression can call a PL/SQL procedure.
For example, to reference a function you created that is called My_func, in the My_funcs_pkg package, in the Scott schema, that takes two numeric parameters, you could call the following:
SELECT Scott.My_funcs_pkg.My_func(10,20) FROM dual;
Naming Conventions
If only one of the optional schema or package names is given, then the first identifier can be either a schema name or a package name. For example, to determine whether
Payroll in the reference Payroll.Tax_rate is a schema or package name, Oracle Database proceeds as follows:
Oracle Database first checks for the Payroll package in the current schema. If the PAYROLL package is found in the current schema, then Oracle Database looks for a Tax_rate function in the Payroll package. If a Tax_rate function is not found in the Payroll package, then an error message is returned. If a Payroll package is not found, then Oracle Database looks for a schema named Payroll that contains a top-level Tax_rate function. If the Tax_rate function is not found in the Payroll schema, then an error message is returned.
You can also refer to a stored top-level function using any synonym that you have defined for it.
Name Precedence
In SQL statements, the names of database columns take precedence over the names of functions with no parameters. For example, if schema Scott creates the following two objects:
CREATE TABLE Emp_tab(New_sal NUMBER ...); CREATE FUNCTION New_sal RETURN NUMBER IS ...;
Then, in the following two statements, the reference to New_sal refers to the column Emp_tab.New_sal:
SELECT New_sal FROM Emp_tab; SELECT Emp_tab.New_sal FROM Emp_tab;
Example of Calling a PL/SQL Function from SQL For example, to call the Tax_rate PL/SQL function from schema Scott, run it against the Ss_no and sal columns in Tax_table, and place the results in the variable Income_tax, specify the following:
Note:
You may need to set up data structures similar to the following for certain examples to work:
CREATE TABLE Tax_table ( Ss_no NUMBER, Sal NUMBER); CREATE OR REPLACE FUNCTION tax_rate (ssn IN NUMBER, salary IN NUMBER) RETURN NUMBER IS sal_out NUMBER; BEGIN sal_out := salary * 1.1; END;
DECLARE Tax_id NUMBER; Income_tax NUMBER; BEGIN SELECT scott.tax_rate (Ss_no, Sal) INTO Income_tax FROM Tax_table
Arguments
To pass any number of arguments to a function, supply the arguments within the parentheses. You must use positional notation; named notation is not supported. For functions that do not accept arguments, use ().
When calling Gross_pay from a procedural statement, you can always accept the default value of St_hrs. This is because you can use named notation, which lets you skip parameters. For example:
IF Gross_pay(Eenum, Ot_hrs => Otime) > Pay_limit THEN ...
However, when calling Gross_pay from a SQL expression, you cannot accept the default value of St_hrs, unless you accept the default value of Ot_hrs. This is because you cannot use named notation.
Privileges
To call a PL/SQL function from SQL, you must either own or have EXECUTE privileges on the function. 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.
It must be a stored function, not a function defined within a PL/SQL block or subprogram. It must be a row function, not a column (group) function; in other words, it cannot take an entire column of data as its argument. All its formal parameters must be IN parameters; none can be an OUT or IN OUT parameter. The datatypes of its formal parameters must be Oracle built-in types, such as CHAR, DATE, or NUMBER, not PL/SQL types, such as BOOLEAN, RECORD, or TABLE. Its return type (the datatype of its result value) must be an Oracle built-in type.
Coding PL/SQL Procedures and Packages 7-39
For example, the following stored function meets the basic requirements:
Note:
You may need to set up the following data structures for certain examples to work:
CREATE FUNCTION Gross_pay (Emp_id IN NUMBER, St_hrs IN NUMBER DEFAULT 40, Ot_hrs IN NUMBER DEFAULT 0) RETURN NUMBER AS 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;
Restrictions
When a SQL statement is run, checks are made to see if it is logically embedded within the execution of an already running SQL statement. This occurs if the statement is run from a trigger or from a subprogram that was in turn called from the already running SQL statement. In these cases, further checks occur to determine if the new SQL statement is safe in the specific context. The following restrictions are enforced on subprograms:
A subprogram called from a query or DML statement may not end the current transaction, create or rollback to a savepoint, or ALTER the system or session. A subprogram called from a query (SELECT) statement or from a parallelized DML statement may not execute a DML statement or otherwise modify the database. A subprogram called from a DML statement may not 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 called 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 already running statement. PL/SQL's autonomous transactions provide one escape (see "Autonomous Transactions" on page 2-20). Another escape is available using Oracle Call Interface (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 the following:
Functions that need not be called 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 FUNCTION F1 (P1 NUMBER) RETURN NUMBER DETERMINISTIC IS BEGIN RETURN P1 * 2; END;
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
You should 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 calls to functions that are marked DETERMINISTIC without any other action being required. The following 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 calling the function when it is possible to do so. Functions that fall in the following categories should typically be DETERMINISTIC:
Functions used in a WHERE, ORDER BY, or GROUP BY clause Functions that MAP or ORDER methods of a SQL type Functions that in any other way help determine whether or where a row should appear in a result set
Oracle Database cannot require that you should explicitly declare functions in the preceding categories as DETERMINISTIC without breaking existing applications, but the use of the keyword might be a wise choice of style within your application. Keep the following points in mind when you create DETERMINISTIC functions:
The database cannot recognize if the behavior of the function is indeed deterministic. If the DETERMINISTIC keyword is applied to a function whose behavior 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 SQL Reference for an account of CREATE FUNCTION restrictions
For DML statements in Oracle Database versions prior to 8.1.5, the parallelization optimization looked to see if a function was noted as having all four of RNDS, WNDS,
7-42 Oracle Database Application Developers Guide - Fundamentals
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" on page 7-41; it is placed after the return value type in a declaration of the function, as in:
CREATE FUNCTION F1 (P1 NUMBER) RETURN NUMBER PARALLEL_ENABLE IS BEGIN RETURN P1 * 2; END;
A PL/SQL function defined with CREATE FUNCTION may 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 calls 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 execute a DML statement; it is subject to the same restrictions that are enforced on functions that are run inside a query (SELECT) statement.
See Also:
It is impossible or impractical to edit existing code to remove RESTRICT_REFERENCES completely. If you do not remove it from a subprogram S1 that depends on another subprogram S2, then RESTRICT_REFERENCES might also be needed in S2, so that S1 will compile. Replacing RESTRICT_REFERENCES in existing code with hints parallel-enable and deterministic would negatively affect the behavior of new, dependent code. Use RESTRICT_REFERENCES to preserve the behavior 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 wholly new application. If you use the pragma RESTRICT_REFERENCES, place it in a package specification, not in a package body. It must follow the declaration of a subprogram (function or
procedure), but it need not follow immediately. Only one pragma can reference a given subprogram declaration.
Note:
The pragma RESTRICT_REFERENCES applies to both functions and procedures. Purity levels are important for functions, but also for procedures that are called by functions.
Where:
Keyword Description WNDS RNDS WNPS RNPS TRUST The subprogram writes no database state (does not modify database tables). The subprogram reads no database state (does not query database tables). The subprogram writes no package state (does not change the values of packaged variables). The subprogram reads no package state (does not reference the values of packaged variables). The other restrictions listed in the pragma are not enforced; they are simply assumed to be true. This allows easy calling 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 the following example, 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. That way, the PL/SQL compiler never rejects the subprogram unnecessarily.
Note:
You may need to set up the following data structures for certain examples here to work:
CREATE TABLE Accts ( Yrs NUMBER, Amt NUMBER, Acctno NUMBER, Rte NUMBER);
CREATE PACKAGE Finance AS -- package specification 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, --package body
Rate IN NUMBER) RETURN NUMBER IS BEGIN RETURN Amount * POWER((Rate / 100) + 1, Years); END Compound; -- no pragma in package body END Finance;
-- function call
Using the Keyword TRUST The keyword TRUST in the RESTRICT_REFERENCES syntax allows easy calling from functions that have RESTRICT_REFERENCES declarations to those that do not. When TRUST is present, the restrictions listed in the pragma are not actually enforced, but rather are simply assumed to be true. When calling from a section of code that is using pragmas to one that is not, there are two likely usage styles. One is to place a pragma on the routine to be called, for example on a "call specification" for a Java method. Then, calls from PL/SQL to this method will complain if the method is less restricted than the calling subprogram. For example:
CREATE OR REPLACE PACKAGE P1 IS FUNCTION F1 (P1 NUMBER) RETURN NUMBER IS LANGUAGE JAVA NAME 'CLASS1.METHODNAME(int) return int'; PRAGMA RESTRICT_REFERENCES(F1,WNDS,TRUST); FUNCTION F2 (P1 NUMBER) RETURN NUMBER; PRAGMA RESTRICT_REFERENCES(F2,WNDS); END; CREATE OR REPLACE PACKAGE BODY P1 IS FUNCTION F2 (P1 NUMBER) RETURN NUMBER IS BEGIN RETURN F1(P1); END; END;
Here, F2 can call F1, as F1 has been declared to be WNDS. The other approach is to mark only the caller, which may then make a call to any subprogram without complaint. For example:
CREATE OR REPLACE PACKAGE P1a IS FUNCTION F1 (P1 NUMBER) RETURN NUMBER IS LANGUAGE JAVA NAME 'CLASS1.METHODNAME(int) return int'; FUNCTION F2 (P1 NUMBER) RETURN NUMBER; PRAGMA RESTRICT_REFERENCES(F2,WNDS,TRUST); END; CREATE OR REPLACE PACKAGE BODY P1a IS FUNCTION F2 (P1 NUMBER) RETURN NUMBER IS BEGIN RETURN F1(P1);
END; END;
Here, F2 can call F1 because while F2 is promised to be WNDS (because TRUST is specified), the body of F2 is not actually examined to determine if it truly satisfies the WNDS restriction. Because F2 is not examined, its call to F1 is allowed, even though there is no PRAGMA RESTRICT_REFERENCES for F1. 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 or not the statements explicitly read database states. The following INSERT violates RNDS if it is executed dynamically, but it does not violate RNDS if it is executed statically.
INSERT INTO my_table values(3, 'SCOTT');
The following UPDATE always violates RNDS statically and dynamically, because it explicitly reads the column name of my_table.
UPDATE my_table SET id=777 WHERE name='SCOTT';
Overloading Packaged PL/SQL Functions PL/SQL lets you overload packaged (but not standalone) functions: You can use the same name for different functions if their formal parameters differ in number, order, or datatype family. However, a RESTRICT_REFERENCES pragma can apply to only one function declaration. Therefore, a pragma that references the name of overloaded functions always applies to the nearest preceding function declaration. In this example, the pragma applies to the second declaration of valid:
CREATE PACKAGE Tests AS FUNCTION Valid (x NUMBER) RETURN CHAR; FUNCTION Valid (x DATE) RETURN CHAR; PRAGMA RESTRICT_REFERENCES (valid, WNDS); END;
Package States
The state of a nonreusable package (one not marked SERIALLY_REUSABLE) persists for the lifetime of a session. A package state includes global variables, cursors, and so on. The state of a serially reusable package persists only for the lifetime of a call to the server. On a subsequent call to the server, if a reference is made to the serially reusable package, then Oracle Database creates a new instantiation of the serially reusable package and initializes all the global variables to NULL or to the default values provided. Any changes made to the serially reusable package state in the previous calls to the server are not visible.
Note: Creating a new instantiation of a serially reusable package on a call to the server does not necessarily imply that Oracle Database allocates memory or configures the instantiation object. Oracle Database looks for an available instantiation work area (which is allocated and configured) for this package in a least-recently used (LRU) pool in the SGA.
At the end of the call to the server, this work area is returned back to the LRU pool. The reason for keeping the pool in the SGA is that the work area can be reused across users who have requests for the same package.
A package specification can be marked serially reusable, whether or not it has a corresponding package body. If the package has a body, then the body must have the serially reusable pragma, if its corresponding specification has the pragma; it cannot have the serially reusable pragma unless the specification also has the pragma.
Its package variables are meant for use only within the work boundaries, which correspond to calls to the server (either OCI call boundaries or PL/SQL RPC calls to the server).
Note:
If the application programmer makes a mistake and depends on a package variable that is set in a previous unit of work, then the application program can fail. PL/SQL cannot check for such cases.
A pool of package instantiations is kept, and whenever a "unit of work" needs this package, one of the instantiations is "reused", as follows: The package variables are reinitialized (for example, if the package variables have default values, then those values are reinitialized). The initialization code in the package body is run again.
At the "end work" boundary, cleanup is done. If any cursors were left open, then they are silently closed. Some non-reusable secondary memory is freed (such as memory for collection variables or long VARCHAR2s). This package instantiation is returned back to the pool of reusable instantiations kept for this package.
Serially reusable packages cannot be accessed from database triggers or other PL/SQL subprograms that are called from SQL statements. If you try, then Oracle Database generates an error.
Suppose your Enterprise Manager (or SQL*Plus) application issues the following:
CONNECT Scott/Tiger # first CALL to server BEGIN Sr_pkg.N := 10; END; # second CALL to server BEGIN DBMS_OUTPUT.PUT_LINE(Sr_pkg.N); END;
Note:
If the package had not had the pragma SERIALLY_REUSABLE, the program would have printed '10'.
Example 2: How Package Variables Act Across Call Boundaries This example has both a package specification and package body, which are serially reusable.
CONNECT Scott/Tiger DROP PACKAGE Sr_pkg; CREATE OR REPLACE PACKAGE Sr_pkg IS PRAGMA SERIALLY_REUSABLE; TYPE Str_table_type IS TABLE OF VARCHAR2(200) INDEX BY BINARY_INTEGER; Num NUMBER := 10; Str VARCHAR2(200) := 'default-init-str'; Str_tab STR_TABLE_TYPE; PROCEDURE Print_pkg; PROCEDURE Init_and_print_pkg(N NUMBER, V VARCHAR2); END Sr_pkg; CREATE OR REPLACE PACKAGE BODY Sr_pkg IS -- the body is required to have the pragma because the -- specification of this package has the pragma PRAGMA SERIALLY_REUSABLE; PROCEDURE Print_pkg IS BEGIN DBMS_OUTPUT.PUT_LINE('num: ' || Sr_pkg.Num); DBMS_OUTPUT.PUT_LINE('str: ' || Sr_pkg.Str); DBMS_OUTPUT.PUT_LINE('number of table elems: ' || Sr_pkg.Str_tab.Count); FOR i IN 1..Sr_pkg.Str_tab.Count LOOP DBMS_OUTPUT.PUT_LINE(Sr_pkg.Str_tab(i)); END LOOP; END; PROCEDURE Init_and_print_pkg(N NUMBER, V VARCHAR2) IS BEGIN -- init the package globals Sr_pkg.Num := N; Sr_pkg.Str := V; FOR i IN 1..n LOOP Sr_pkg.Str_tab(i) := V || ' ' || i; END LOOP; -- print the package Print_pkg; END; END Sr_pkg; SET SERVEROUTPUT ON; Rem SR package access in a CALL: BEGIN -- initialize and print the package DBMS_OUTPUT.PUT_LINE('Initing and printing pkg state..'); Sr_pkg.Init_and_print_pkg(4, 'abracadabra'); -- print it in the same call to the server. -- we should see the initialized values. DBMS_OUTPUT.PUT_LINE('Printing package state in the same CALL...'); Sr_pkg.Print_pkg; END;
Initing and printing pkg state.. num: 4 str: abracadabra number of table elems: 4 abracadabra 1 abracadabra 2 abracadabra 3 abracadabra 4 Printing package state in the same CALL... num: 4 str: abracadabra number of table elems: 4 abracadabra 1 abracadabra 2 abracadabra 3 abracadabra 4 REM SR package access in subsequent CALL: BEGIN -- print the package in the next call to the server. -- We should that the package state is reset to the initial (default) values. DBMS_OUTPUT.PUT_LINE('Printing package state in the next CALL...'); Sr_pkg.Print_pkg; END; Statement processed. Printing package state in the next CALL... num: 10 str: default-init-str number of table elems: 0
Example 3: Open Cursors in Serially Reusable Packages at Call Boundaries This example demonstrates that any open cursors in serially reusable packages get closed automatically at the end of a work boundary (which is a call). Also, in a new call, these cursors need to be opened again.
REM REM REM REM REM REM For serially reusable pkg: At the end work boundaries (which is currently the OCI call boundary) all open cursors will be closed. Because the cursor is closed - every time we fetch we will start at the first row again.
CONNECT Scott/Tiger DROP PACKAGE Sr_pkg; DROP TABLE People; CREATE TABLE People (Name VARCHAR2(20)); INSERT INTO People VALUES ('ET'); INSERT INTO People VALUES ('RAMBO'); CREATE OR REPLACE PACKAGE Sr_pkg IS PRAGMA SERIALLY_REUSABLE; CURSOR C IS SELECT Name FROM People; END Sr_pkg; SQL> SET SERVEROUTPUT ON; SQL> CREATE OR REPLACE PROCEDURE Fetch_from_cursor IS Name VARCHAR2(200); BEGIN IF (Sr_pkg.C%ISOPEN) THEN DBMS_OUTPUT.PUT_LINE('cursor is already open.'); ELSE
DBMS_OUTPUT.PUT_LINE('cursor is closed; opening now.'); OPEN Sr_pkg.C; END IF; -- fetching from cursor. FETCH sr_pkg.C INTO name; DBMS_OUTPUT.PUT_LINE('fetched: ' || Name); FETCH Sr_pkg.C INTO name; DBMS_OUTPUT.PUT_LINE('fetched: ' || Name); -- Oops forgot to close the cursor (Sr_pkg.C). -- But, because it is a Serially Reusable pkg's cursor, -- it will be closed at the end of this CALL to the server. END; EXECUTE fetch_from_cursor; cursor is closed; opening now. fetched: ET fetched: RAMBO
The producer function uses the PIPELINED keyword in its declaration. The producer function uses an OUT parameter that is a record, corresponding to a row in the result set. As each output record is completed, it is sent to the consumer function using the PIPE ROW keyword. The producer function ends with a RETURN statement that does not specify any return value. The consumer function or SQL statement uses the TABLE keyword to treat the resulting rows like a regular table.
For example:
CREATE FUNCTION StockPivot(p refcur_pkg.refcur_t) RETURN TickerTypeSet PIPELINED IS out_rec TickerType := TickerType(NULL,NULL,NULL); in_rec p%ROWTYPE; BEGIN LOOP -- Function accepts multiple rows through a REF CURSOR argument. FETCH p INTO in_rec; EXIT WHEN p%NOTFOUND; -- Return value is a record type that matches the table definition. out_rec.ticker := in_rec.Ticker; out_rec.PriceType := 'O'; out_rec.price := in_rec.OpenPrice; -- Once a result row is ready, we send it back to the calling program, -- and continue processing. PIPE ROW(out_rec); Coding PL/SQL Procedures and Packages 7-51
-- This function outputs twice as many rows as it receives as input. out_rec.PriceType := 'C'; out_rec.Price := in_rec.ClosePrice; PIPE ROW(out_rec); END LOOP; CLOSE p; -- The function ends with a RETURN statement that does not specify any value. RETURN; END; / -- Here we use the result of this function in a SQL query. SELECT * FROM TABLE(StockPivot(CURSOR(SELECT * FROM StockTable))); -- Here we use the result of this function in a PL/SQL block. DECLARE total NUMBER := 0; price_type VARCHAR2(1); BEGIN FOR item IN (SELECT * FROM TABLE(StockPivot(CURSOR(SELECT * FROM StockTable)))) LOOP -- Access the values of each output row. -- We know the column names based on the declaration of the output type. -- This computation is just for illustration. total := total + item.price; price_type := item.price_type; END LOOP; END; /
Code the member functions. In particular, ODCIAggregateIterate accumulates the result as it is called once for each row that is processed. Store any intermediate results using the attributes of the object type. Create the aggregate function, and associate it with the new object type. Call the aggregate function from SQL queries, DML statements, or other places that you might use the built-in aggregates. You can include typical options such as DISTINCT and ALL in the calls to the aggregate function.
See Also: Oracle Database Data Cartridge Developer's Guide for details of this process and the requirements for the member functions
8
Coding Dynamic SQL
This chapter covers the following topics:
What Is Dynamic SQL? Why Use Dynamic SQL? Developing with Native Dynamic SQL: Scenario Choosing Between Native Dynamic SQL and the DBMS_SQL Package Programming with Dynamic SQL Avoiding SQL Injection in PL/SQL
8-1
Oracle Database enables you to implement dynamic SQL in a PL/SQL application in the following ways:
Using native dynamic SQL, which involves placing dynamic SQL statements directly into PL/SQL blocks Calling procedures in the DBMS_SQL package
Although this chapter discusses PL/SQL support for dynamic SQL, you can call dynamic SQL from other languages:
If you use C/C++, you can call dynamic SQL with the Oracle Call Interface (OCI), or you can use the Pro*C/C++ precompiler to add dynamic SQL extensions to your C code. If you use COBOL, you can use the Pro*COBOL precompiler to add dynamic SQL extensions to your COBOL code. If you use Java, you can develop applications that use dynamic SQL with JDBC.
If you have a program that uses OCI, Pro*C/C++, or Pro*COBOL to execute dynamic SQL, consider switching to native dynamic SQL inside PL/SQL stored procedures and functions. The network round-trips required to perform dynamic SQL operations from client-side applications might hurt performance. Stored procedures can reside on the server, eliminating network overhead. You can call the PL/SQL stored procedures and stored functions from the OCI, Pro*C/C++, or Pro*COBOL application.
See Also: Oracle Database PL/SQL Packages and Types Reference for details about the DBMS_SQL package. To learn about calling Oracle Database stored procedures and stored functions from languages other than PL/SQL, consult the following resources:
Oracle Call Interface Programmer's Guide Pro*C/C++ Programmer's Guide Pro*COBOL Programmer's Guide
Successful compilation verifies that the SQL statements reference valid database objects and that the necessary privileges are in place to access the objects. Performance of static SQL is generally better than dynamic SQL.
Despite these advantages, static SQL has limitations that can be overcome with dynamic SQL, as in the following cases:
You do not know the full text of the SQL statements that must be executed in a PL/SQL procedure. These SQL statements may depend on user input or on processing work performed by the program. You want to execute DDL statements and other SQL statements that are not supported in purely static SQL programs. You want to write a program that can handle changes in data definitions without the need to recompile. Dynamic SQL is more flexible than static SQL because it enables you to write reusable code that can be adapted for different environments.
As a general rule you should use dynamic SQL only if you cannot use static SQL to accomplish your goals, or if using static SQL is too cumbersome. The following sections describe typical situations in which you should use dynamic SQL:
Executing DDL and SCL Statements in PL/SQL Executing Dynamic Queries Referencing Database Objects that Do Not Exist at Compilation Optimizing Execution Dynamically Executing Dynamic PL/SQL Blocks Performing Dynamic Operations Using Invoker's Rights
Data definition language (DDL) statements such as CREATE, DROP, GRANT, and REVOKE Session control language (SCL) statements such as ALTER SESSION and SET ROLE The TABLE clause in the SELECT statement
The following native dynamic SQL example uses a SELECT statement with the TABLE clause.
Example 81 Using SELECT . . . TABLE in Dynamic SQL -- Create an object t_emp and a datatype t_emplist as a table of type t_emp CREATE TYPE t_emp AS OBJECT (id NUMBER, name VARCHAR2(20)) / CREATE TYPE t_emplist AS TABLE OF t_emp / -- Create a table with a nested table of type t_emplist CREATE TABLE dept_new (id NUMBER, emps t_emplist) NESTED TABLE emps STORE AS emp_table; -- Populate the dept_new table with data INSERT INTO dept_new VALUES ( 10, t_emplist ( t_emp(1, 'SCOTT'), t_emp(2, 'BRUCE') ) ); -- Write a PL/SQL block that queries table dept_new and nested table emps -- SELECT ... FROM ... TABLE is not allowed in static SQL in PL/SQL DECLARE v_deptid NUMBER; v_ename VARCHAR2(20); BEGIN EXECUTE IMMEDIATE 'SELECT d.id, e.name FROM dept_new d, TABLE(d.emps) e WHERE e.id = 1' INTO v_deptid, v_ename; END; /
8-3
See Also:
Oracle Database SQL Reference for information about DDL and SCL statements
Allow users to input or choose query search or sorting criteria at runtime Allow users to input or choose optimizer hints at run time Query a database where the data definitions of tables are constantly changing Query a database where new tables are created often
See Also:
"Querying with Dynamic SQL: Example" on page 8-14 and "Developing with Native Dynamic SQL: Scenario" on page 8-7
In Example 83, the user can pass values such as the following for p_hint:
p_hint = '/*+ ALL_ROWS */' p_hint = '/*+ FIRST_ROWS */' p_hint = '/*+ CHOOSE */'
See Also: Oracle Database Performance Tuning Guide to learn more about using hints
8-5
CREATE OR REPLACE PROCEDURE event_handler_2 (p_handle NUMBER) IS BEGIN -- process event 2 RETURN; END; / CREATE OR REPLACE PROCEDURE event_handler_3 (p_handle NUMBER) IS BEGIN -- process event 3 RETURN; END; / CREATE OR REPLACE PROCEDURE event_dispatcher (p_event_num NUMBER, p_handle NUMBER) IS BEGIN IF (p_event_num = 1) THEN EVENT_HANDLER_1(p_handle); ELSIF (p_event_num = 2) THEN EVENT_HANDLER_2(p_handle); ELSIF (p_event_num = 3) THEN EVENT_HANDLER_3(p_handle); END IF; END; /
By using native dynamic SQL, you can write a smaller, more flexible event dispatcher as shown in the following example.
Example 85 Event Dispatching with Native Dynamic SQL CREATE OR REPLACE PROCEDURE event_dispatcher (p_event_num NUMBER, p_handle NUMBER) IS BEGIN EXECUTE IMMEDIATE 'BEGIN EVENT_HANDLER_' || TO_CHAR(p_event_num) || '(:1); END;' USING p_handle; END; /
Execute DDL and DML operations. Execute single row and multiple row queries.
The database in this scenario is used for human resources. A master table named offices contains the list of all company locations. The offices table has the following definition:
Column Name LOCATION Null? NOT_NULL Type VARCHAR2(200)
Multiple emp_location tables contain the employee information, where location is the name of city where the office is located. For example, a table named emp_houston contains employee information for the company's Houston office, whereas a table named emp_boston contains employee information for the company's Boston office. Each emp_location table has the following definition:
Column Name EMPNO ENAME JOB SAL DEPTNO Null? NOT_NULL NOT_NULL NOT_NULL NOT_NULL NOT_NULL Type NUMBER(4) VARCHAR2(10) VARCHAR2(9) NUMBER(7,2) NUMBER(2)
The following sections describe various native dynamic SQL operations that can be performed on the data in the hr database.
SHOW ERRORS;
The following procedure uses the same concatenation technique to drop a table.
Example 88 Dropping a Table with Native Dynamic SQL CREATE OR REPLACE PROCEDURE drop_location (p_loc VARCHAR2) IS BEGIN -- Drop the employee table for location 'p_loc', for example, emp_detroit EXECUTE IMMEDIATE 'DROP TABLE ' || 'emp_' || p_loc; -- Remove location from master table DELETE FROM offices WHERE location = p_loc; END; / SHOW ERRORS;
v_num_of_employees NUMBER; BEGIN -- Use concatenation to form the table name in the SELECT statement v_query_str := 'SELECT COUNT(*) FROM emp_' || p_loc || ' WHERE job = :1'; -- Execute the query and put the result row in a variable EXECUTE IMMEDIATE v_query_str INTO v_num_of_employees USING p_job; RETURN v_num_of_employees; END; / SHOW ERRORS;
CREATE OR REPLACE PROCEDURE list_employees (p_loc VARCHAR2, p_job VARCHAR2) IS TYPE cur_typ IS REF CURSOR; -- Define a cursor variable v_emp_cursor cur_typ; v_query_str VARCHAR2(1000); v_emp_name VARCHAR2(20); v_emp_num NUMBER; BEGIN -- Use concatenation to form the SELECT statement v_query_str := 'SELECT ename, empno FROM emp_' || p_loc || ' WHERE job = :g_job_title'; -- Open a cursor variable for the query OPEN v_emp_cursor FOR v_query_str USING p_job; -- Loop through each row to find employees who perform the specified job LOOP -- Fetch the employee name and ID into variables FETCH v_emp_cursor INTO v_emp_name, v_emp_num; EXIT WHEN v_emp_cursor%NOTFOUND; -- Process row here END LOOP; CLOSE v_emp_cursor; END; / SHOW ERRORS;
8-9
Native dynamic SQL enables you to place dynamic SQL statements directly into PL/SQL code. These dynamic statements include the following:
Queries and DML statements PL/SQL anonymous blocks DDL statements Transaction control statements Session control statements
To process most native dynamic SQL statements, use the EXECUTE IMMEDIATE statement. To process a multi-row SELECT statement, use OPEN-FOR, FETCH, and CLOSE statements.
Note: To use native dynamic SQL, you must set the COMPATIBLE initialization parameter to 8.1.0 or higher.
As an alternative to native dynamic SQL, the DBMS_SQL package offers a PL/SQL API to execute dynamic SQL statements. For example, the DBMS_SQL package contains procedures to do the following:
Programs that use the DBMS_SQL package make calls to this package to perform dynamic SQL operations.
See Also:
Oracle Database PL/SQL User's Guide and Reference to learn about native dynamic SQL Oracle Database PL/SQL Packages and Types Reference to learn about the DBMS_SQL package Oracle Database Upgrade Guide to learn about the COMPATIBLE initialization parameter
Native Dynamic SQL is Easy to Use Native Dynamic SQL is Faster than DBMS_SQL Native Dynamic SQL Supports User-Defined Types Native Dynamic SQL Supports Fetching into Records
With the DBMS_SQL package you must call many procedures and functions in a strict sequence, which means that even simple operations require extensive code. You can avoid this complexity by using native dynamic SQL instead. Table 81 illustrates the difference in the amount of code required to perform the same operation with the DBMS_SQL package and native dynamic SQL.
Table 81 Code Comparison of DBMS_SQL Package and Native Dynamic SQL Native Dynamic SQL CREATE OR REPLACE PROCEDURE insert_into_table (p_table_name VARCHAR2, p_deptnumber NUMBER, p_deptname VARCHAR2, p_location VARCHAR2) IS v_stmt_str VARCHAR2(200);
DBMS_SQL Package CREATE OR REPLACE PROCEDURE insert_into_table (p_table_name VARCHAR2, p_deptnumber NUMBER, p_deptname VARCHAR2, p_location VARCHAR2) IS v_cur_hdl INTEGER; v_stmt_str VARCHAR2(200); v_rows_processed BINARY_INTEGER; BEGIN v_stmt_str := 'INSERT INTO ' || p_table_name || ' VALUES (:g_deptno, :g_dname, :g_loc)'; v_cur_hdl := DBMS_SQL.OPEN_CURSOR; DBMS_SQL.PARSE(v_cur_hdl, v_stmt_str, DBMS_SQL.NATIVE); DBMS_SQL.BIND_VARIABLE (v_cur_hdl,':g_deptno', p_deptnumber); DBMS_SQL.BIND_VARIABLE (v_cur_hdl, ':g_dname', p_deptname); DBMS_SQL.BIND_VARIABLE (v_cur_hdl, ':g_loc', p_location); v_rows_processed := DBMS_SQL.EXECUTE(v_cur_hdl); DBMS_SQL.CLOSE_CURSOR(v_cur_hdl); END; /
BEGIN v_stmt_str := 'INSERT INTO ' || p_table_name || ' VALUES (:g_deptno, :g_dname, :g_loc)'; EXECUTE IMMEDIATE v_stmt_str USING p_deptnumber, p_deptname, p_location;
END; /
In Example 811 the native dynamic SQL code uses a parameter instead of a bind variable to construct the SQL statement.
Example 811 Using Native Dynamic SQL Without Bind Variables
CREATE OR REPLACE PROCEDURE del_dept (p_department_id departments.department_id%TYPE) IS BEGIN EXECUTE IMMEDIATE 'DELETE FROM departments WHERE department_id = ' || TO_CHAR(p_department_id); END; / SHOW ERRORS;
For each distinct p_department_id parameter, the procedure creates a new cursor, which causes resource contention and poor performance. Instead, you can construct the SQL statement by using a bind variable, as shown in Example 812.
Example 812 Using Native Dynamic SQL with Bind Variables
CREATE OR REPLACE PROCEDURE del_dept (p_department_id departments.department_id%TYPE) IS BEGIN EXECUTE IMMEDIATE 'DELETE FROM departments WHERE department_id = :1' USING p_department_id; END; / SHOW ERRORS;
In Example 812 the same cursor is reused for different values of the bind my_deptno, which improves performance and scalability.
BEGIN v_stmt_str := 'SELECT * FROM emp WHERE job = :1'; -- in a multi-row query OPEN v_emp_cursor FOR v_stmt_str USING 'MANAGER'; LOOP FETCH v_emp_cursor INTO emp_record; EXIT WHEN v_emp_cursor%NOTFOUND; END LOOP; CLOSE v_emp_cursor; -- in a single-row query EXECUTE IMMEDIATE v_stmt_str INTO emp_record USING 'PRESIDENT'; END; /
DBMS_SQL is Supported in Client-Side Programs DBMS_SQL Supports Statements with Unknown Number of Inputs or Outputs DBMS_SQL Supports SQL Statements Larger than 32 KB DBMS_SQL Lets You Reuse SQL Statements
Native dynamic SQL prepares a SQL statement each time the statement is used, which typically involves parsing, optimization, and plan generation. Although the extra prepare operations incur a small performance penalty, the decrease in speed is typically outweighed by the performance benefits of native dynamic SQL.
In general, the native dynamic SQL code is more readable and compact, which can improve developer productivity.
This example queries for employees with the job description SALESMAN in the job column of the emp table. Table 82 shows sample code that accomplishes this query using the DBMS_SQL package and native dynamic SQL.
Table 82
Querying Using the DBMS_SQL Package and Native Dynamic SQL Native Dynamic SQL Query Operation DECLARE TYPE EmpCurTyp IS REF CURSOR; v_emp_cursor EmpCurTyp; v_stmt_str VARCHAR2(200); v_name VARCHAR2(20); v_salary NUMBER; BEGIN v_stmt_str := 'SELECT ename, sal FROM emp WHERE job = :1'; OPEN v_emp_cursor FOR v_stmt_str USING 'SALESMAN'; LOOP FETCH v_emp_cursor INTO v_name, v_salary; EXIT WHEN v_emp_cursor%NOTFOUND; -- Process data END LOOP; CLOSE v_emp_cursor;
DBMS_SQL Query Operation DECLARE v_stmt_str VARCHAR2(200); v_cur_hdl INT; v_rows_processed INT; v_name VARCHAR2(10); v_salary INT; BEGIN v_cur_hdl := DBMS_SQL.OPEN_CURSOR; -- open cursor v_stmt_str := 'SELECT ename, sal FROM emp WHERE job = :g_jobname'; DBMS_SQL.PARSE(v_cur_hdl,v_stmt_str,DBMS_SQL.NATIVE); -- Supply binds (bind by name) DBMS_SQL.BIND_VARIABLE(v_cur_hdl, 'g_jobname', 'SALESMAN'); -- Describe defines DBMS_SQL.DEFINE_COLUMN(v_cur_hdl, 1, v_name, 200); DBMS_SQL.DEFINE_COLUMN(v_cur_hdl, 2, v_salary); -- Execute v_rows_processed := DBMS_SQL.EXECUTE(v_cur_hdl); LOOP -- Fetch a row IF DBMS_SQL.FETCH_ROWS(v_cur_hdl) > 0 THEN -- Fetch columns from the row DBMS_SQL.COLUMN_VALUE(v_cur_hdl, 1, v_name); DBMS_SQL.COLUMN_VALUE(v_cur_hdl, 2, v_salary); -- Process ELSE EXIT; END IF; END LOOP; DBMS_SQL.CLOSE_CURSOR(v_cur_hdl); -- close cursor END; /
END; /
This example inserts a new row for which the column values are in the PL/SQL variables deptnumber, deptname, and location. Table 83 shows sample code that accomplishes this task with the DBMS_SQL package and native dynamic SQL.
Table 83
DML Operation Using the DBMS_SQL Package and Native Dynamic SQL Native Dynamic SQL DML Operation DECLARE v_stmt_str v_deptnumber v_deptname v_location VARCHAR2(200); NUMBER := 99; VARCHAR2(20); VARCHAR2(10);
DBMS_SQL DML Operation DECLARE v_stmt_str VARCHAR2(200); v_cur_hdl NUMBER; v_deptnumber NUMBER := 99; v_deptname VARCHAR2(20); v_location VARCHAR2(10); v_rows_processed NUMBER; BEGIN v_stmt_str := 'INSERT INTO dept VALUES (:g_deptno, :g_dname, :g_loc)'; v_cur_hdl := DBMS_SQL.OPEN_CURSOR; DBMS_SQL.PARSE(v_cur_hdl, v_stmt_str, DBMS_SQL.NATIVE); -- Supply binds DBMS_SQL.BIND_VARIABLE (v_cur_hdl, ':g_deptno', v_deptnumber); DBMS_SQL.BIND_VARIABLE (v_cur_hdl, ':g_dname', v_deptname); DBMS_SQL.BIND_VARIABLE (v_cur_hdl, ':g_loc', v_location); v_rows_processed := DBMS_SQL.EXECUTE(v_cur_hdl); DBMS_SQL.CLOSE_CURSOR(v_cur_hdl); END; /
BEGIN v_stmt_str := 'INSERT INTO dept VALUES (:g_deptno, :g_dname, :g_loc)'; EXECUTE IMMEDIATE v_stmt_str USING v_deptnumber, v_deptname, v_location;
END; /
Table 84 shows sample code that accomplishes this operation using both the DBMS_SQL package and native dynamic SQL.
Table 84
DML Returning Operation Using the DBMS_SQL Package and Native Dynamic SQL Native Dynamic SQL DML Returning Operation DECLARE deptname_array v_stmt_str v_location v_deptnumber v_deptname DBMS_SQL.VARCHAR2_TABLE; VARCHAR2(200); VARCHAR2(20); NUMBER := 10; VARCHAR2(20);
DBMS_SQL DML Returning Operation DECLARE deptname_array DBMS_SQL.VARCHAR2_TABLE; v_cur_hdl INT; v_stmt_str VARCHAR2(200); v_location VARCHAR2(20); v_deptnumber NUMBER := 10; v_rows_procsd NUMBER; BEGIN v_stmt_str := 'UPDATE dept SET loc = :g_newloc WHERE deptno = :g_deptno RETURNING dname INTO :g_dname'; v_cur_hdl := DBMS_SQL.OPEN_CURSOR; DBMS_SQL.PARSE (v_cur_hdl, v_stmt_str, DBMS_SQL.NATIVE); -- Supply binds DBMS_SQL.BIND_VARIABLE (v_cur_hdl, ':g_newloc', v_location); DBMS_SQL.BIND_VARIABLE (v_cur_hdl, ':g_deptno', v_deptnumber); DBMS_SQL.BIND_ARRAY (v_cur_hdl, ':g_dname', deptname_array); -- Execute cursor v_rows_procsd := DBMS_SQL.EXECUTE(v_cur_hdl); -- Get RETURNING column into OUT bind array DBMS_SQL.VARIABLE_VALUE (v_cur_hdl, ':g_dname', deptname_array); DBMS_SQL.CLOSE_CURSOR(v_cur_hdl); END; /
BEGIN v_stmt_str := 'UPDATE dept SET loc = :g_newloc WHERE deptno = :g_deptno RETURNING dname INTO :g_dname'; EXECUTE IMMEDIATE v_stmt_str USING v_location, v_deptnumber, OUT v_deptname;
END; /
The following sections describe these techniques. To try the examples in your sample database, run the script in Example 814.
Example 814
CONNECT hr/hr SET SERVEROUTPUT ON SET LINESIZE 150 SET ECHO OFF DROP TABLE user_pwd; CREATE TABLE user_pwd( username VARCHAR2(100), password VARCHAR2(100) ); INSERT INTO user_pwd VALUES('whitehat', 'secret'); DROP TABLE delemp; CREATE TABLE delemp AS SELECT * FROM employees; COMMIT;
Statement Modification
SQL modification involves deliberately altering a dynamic SQL statement so that it executes in a way unintended by the application developer. Typically, the user retrieves unauthorized data by changing the WHERE clause of a query or by inserting a UNION ALL clause. The classic example of this technique is bypassing password authentication by making a WHERE clause always TRUE. Suppose a Web form prompts a user to enter a username and password. When the user clicks Submit, the form invokes a PL/SQL stored procedure that concatenates the username and password entered in the form to build a dynamic SQL statement. The procedure executes the query to authenticate the user. Example 815 illustrates an authentication procedure that you can test in SQL*Plus. The ckpwd procedure uses concatenation to build a SQL query of the user_pwd table. If the user enters a username and password stored in the table, then the execution of the query retrieves a single row and the user is authenticated. The ckpwd procedure also displays the concatenated query so that you can see which query is executed.
Example 815 ckpwd Procedure
CREATE OR REPLACE PROCEDURE ckpwd (p_user IN VARCHAR2, p_pass IN VARCHAR2) IS v_query VARCHAR2(100); v_output NUMBER; BEGIN v_query := q'{SELECT COUNT(*) FROM user_pwd }' || q'{WHERE username = '}' || p_user || q'{' AND password = '}' || p_pass || q'{'}'; DBMS_OUTPUT.PUT_LINE(CHR(10)||'Built the following query:'||CHR(10)||v_query); EXECUTE IMMEDIATE v_query INTO v_output; IF v_output = 1 THEN DBMS_OUTPUT.PUT_LINE(CHR(10)||p_user||' is authenticated'); ELSE DBMS_OUTPUT.PUT_LINE(CHR(10)||'access denied'); END IF; END; /
Suppose that the user whitehat enters the password secret in a Web form. You can simulate this scenario by invoking the code shown in Example 816 in SQL*Plus to authenticate whitehat (sample output included).
Example 816
BEGIN ckpwd ( p_user => q'{whitehat}', p_pass => q'{secret}' ); END; / Built the following query: SELECT COUNT(*) FROM user_pwd WHERE username = 'whitehat' AND password = 'secret' whitehat is authenticated
A malicious user could exploit the concatenation vulnerability and enter the username x in the Web-based form and the text shown in Example 817 as a password.
Example 817 x' OR 'x' = 'x Password String Entered in Form
You can simulate this scenario by executing the code in Example 818 in SQL*Plus (sample output included).
Example 818 Performing Statement Modification with the ckpwd Procedure
BEGIN ckpwd ( p_user => q'{x}', p_pass => q'{x' OR 'x' = 'x}' ); END; / Built the following query: SELECT COUNT(*) FROM user_pwd WHERE username = 'x' AND password = 'x' OR 'x' = 'x' x is authenticated
By using the cleverly constructed password in Example 817, you alter the concatenated SQL statement so that the OR condition always returns TRUE. Thus, the query of the user_pwd table always succeeds no matter which username is entered.
Statement Injection
Statement injection occurs when a user appends one or more new SQL statements to a dynamically generated SQL statement. Anonymous PL/SQL blocks are vulnerable to this technique. Suppose a Web form prompts a user to enter a username and password. When the user clicks Submit, the form invokes a PL/SQL stored procedure that concatenates the username and password entered in the form into an anonymous PL/SQL block. The procedure then executes the anonymous block to authenticate the user. Example 819 illustrates an authentication procedure that you can test in SQL*Plus. The call_ckpwd procedure uses concatenation to build an anonymous block that invokes the ckpwd procedure from Example 815. If the user enters a username and password stored in the user_pwd table, then the execution of the block retrieves a
Coding Dynamic SQL 8-19
single row and the user is authenticated. The call_ckpwd procedure also prints the concatenated text so that you can see which block is executed.
Example 819 call_ckpwd Procedure
CREATE OR REPLACE PROCEDURE call_ckpwd (p_user IN VARCHAR2, p_pass IN VARCHAR2) IS v_block VARCHAR2(100); BEGIN v_block := q'{BEGIN ckpwd( '}' || p_user || q'{' , '}' || p_pass || q'{' ); END; }'; DBMS_OUTPUT.PUT_LINE(CHR(10)|| 'Built the following anonymous block:'||CHR(10)||v_block); EXECUTE IMMEDIATE v_block; END; /
Suppose that the user whitehat enters the password secret in a Web-based form. You can simulate this scenario by invoking the call_ckpwd procedure shown in Example 820 in SQL*Plus (sample output included).
Example 820 Authenticating a User with the call_ckpwd Procedure
BEGIN call_ckpwd ( p_user => q'{whitehat}', p_pass => q'{secret}' ); END; / Built the following anonymous block: BEGIN ckpwd( 'whitehat' , 'secret' ); END; Built the following query: SELECT COUNT(*) FROM user_pwd WHERE username = 'whitehat' AND password = 'secret' whitehat is authenticated
If whitehat turns bad, then he could enter the string shown in Example 821 as the password in a Web form.
Example 821 Bogus Password Entered in Form
You can simulate this technique by invoking the call_ckpwd procedure shown in Example 822 in SQL*Plus (sample output included).
Example 822 Performing Statement Injection with the call_ckpwd Procedure BEGIN call_ckpwd ( p_user => q'{whitehat}',
p_pass => q'{secret'); DELETE FROM hr.delemp WHERE UPPER('x') = UPPER('x}' ); END; / Built the following anonymous block: BEGIN ckpwd( 'whitehat' , 'secret' ); DELETE FROM hr.delemp WHERE UPPER('x') = UPPER('x'); END; Built the following query: SELECT COUNT(*) FROM user_pwd WHERE username = 'whitehat' AND password = 'secret' whitehat is authenticated
The bogus password in Example 821 causes the system to authenticate whitehat and silently execute the injected DELETE statement. A query of the delemp table shows that the injected statement silently removed all rows from the table:
SELECT * FROM delemp; no rows selected
Using Bind Variables to Guard Against SQL Injection Using Validation Checks to Guard Against SQL Injection
CREATE OR REPLACE PROCEDURE ckpwd_bind (p_user IN VARCHAR2, p_pass IN VARCHAR2) IS v_query VARCHAR2(100); v_output NUMBER; BEGIN v_query := q'{SELECT COUNT(*) FROM user_pwd WHERE username = :1 AND password = :2}'; DBMS_OUTPUT.PUT_LINE(CHR(10)||'Built the following query:'||CHR(10)||v_query); EXECUTE IMMEDIATE v_query INTO v_output USING p_user, p_pass; IF v_output = 1 THEN DBMS_OUTPUT.PUT_LINE(CHR(10)||p_user||' is authenticated'); ELSE DBMS_OUTPUT.PUT_LINE(CHR(10)||'access denied'); END IF; END;
If the user tries to pass the bogus password shown in Example 817 to the ckpwd_bind procedure, then the technique fails to authenticate the user. You can execute the block shown in Example 824 in SQL*Plus to test the revised version of the code (sample output included).
Example 824 Preventing Statement Modification with the ckpwd_bind Procedure
BEGIN ckpwd_bind ( p_user => q'{x}', p_pass => q'{x' OR 'x' = 'x}' ); END; / Built the following query: SELECT COUNT(*) FROM user_pwd WHERE username = :1 AND password = :2 access denied
The same binding technique fixes the vulnerable call_ckpwd procedure shown in Example 819. By using bind variables exclusively in your code, you avoid concatenating SQL statements and thereby prevent malicious users from altering or injecting additional statements. Oracle database uses the value of the bind variable exclusively and does not interpret its contents in any way. This technique is the most effective way to prevent SQL injection in PL/SQL programs.
9
Coding Triggers
Triggers are procedures that are stored in the database and are implicitly run, or fired, when something happens. Traditionally, triggers supported the execution of a PL/SQL block when an INSERT, UPDATE, or DELETE occurred on a table or view. Triggers support system and other data events on DATABASE and SCHEMA. Oracle Database also supports the execution of PL/SQL or Java procedures. This chapter discusses DML triggers, INSTEAD OF triggers, and system triggers (triggers on DATABASE and SCHEMA). Topics include:
Designing Triggers Creating Triggers Coding the Trigger Body Compiling Triggers Modifying Triggers Enabling and Disabling Triggers Viewing Information About Triggers Examples of Trigger Applications Responding to System Events through Triggers
Designing Triggers
Use the following guidelines when designing your triggers:
Use triggers to guarantee that when a specific operation is performed, related actions are performed. Do not define triggers that duplicate features already built into Oracle Database. For example, do not define triggers to reject bad data if you can do the same checking through declarative integrity constraints. Limit the size of triggers. If the logic for your trigger requires much more than 60 lines of PL/SQL code, it is better to include most of the code in a stored procedure and call the procedure from the trigger. Use triggers only for centralized, global operations that should be fired for the triggering statement, regardless of which user or database application issues the statement.
Coding Triggers
9-1
Creating Triggers
Do not create recursive triggers. For example, creating an AFTER UPDATE statement trigger on the Emp_tab table that itself issues an UPDATE statement on Emp_tab, causes the trigger to fire recursively until it has run out of memory. Use triggers on DATABASE judiciously. They are executed for every user every time the event occurs on which the trigger is created.
Creating Triggers
Triggers are created using the CREATE TRIGGER statement. This statement can be used with any interactive tool, such as SQL*Plus or Enterprise Manager. When using an interactive tool, a single slash (/) on the last line is necessary to activate the CREATE TRIGGER statement. The following statement creates a trigger for the Emp_tab table.
CREATE OR REPLACE TRIGGER Print_salary_changes BEFORE DELETE OR INSERT OR UPDATE ON Emp_tab FOR EACH ROW WHEN (new.Empno > 0) DECLARE sal_diff number; BEGIN sal_diff := :new.sal - :old.sal; dbms_output.put('Old salary: ' || :old.sal); dbms_output.put(' New salary: ' || :new.sal); dbms_output.put_line(' Difference ' || sal_diff); END; /
The trigger is fired when DML operations (INSERT, UPDATE, and DELETE statements) are performed on the table. You can choose what combination of operations should fire the trigger. Because the trigger uses the BEFORE keyword, it can access the new values before they go into the table, and can change the values if there is an easily-corrected error by assigning to :NEW.column_name. You might use the AFTER keyword if you want the trigger to query or change the same table, because triggers can only do that after the initial changes are applied and the table is back in a consistent state. Because the trigger uses the FOR EACH ROW clause, it might be executed multiple times, such as when updating or deleting multiple rows. You might omit this clause if you just want to record the fact that the operation occurred, but not examine the data for each row. Once the trigger is created, entering the following SQL statement:
UPDATE Emp_tab SET sal = sal + 500.00 WHERE deptno = 10;
fires the trigger once for each row that is updated, in each case printing the new salary, old salary, and the difference. The CREATE (or CREATE OR REPLACE) statement fails if any errors exist in the PL/SQL block.
Note:
The following sections use this example to illustrate the way that parts of a trigger are specified.
9-2 Oracle Database Application Developers Guide - Fundamentals
Creating Triggers
See Also: "Examples of Trigger Applications" on page 9-23 for more realistic examples of CREATE TRIGGER statements
Types of Triggers
A trigger is either a stored PL/SQL block or a PL/SQL, C, or Java procedure associated with a table, view, schema, or the database itself. Oracle Database automatically executes a trigger when a specified event takes place, which may be in the form of a system event or a DML statement being issued against the table. Triggers can be:
DML triggers on tables. INSTEAD OF triggers on views. System triggers on DATABASE or SCHEMA: With DATABASE, triggers fire for each event for all users; with SCHEMA, triggers fire for each event for that specific user.
See Also:
DML statements (DELETE, INSERT, UPDATE) DDL statements (CREATE, ALTER, DROP) Database operations (SERVERERROR, LOGON, LOGOFF, STARTUP, SHUTDOWN)
Naming Triggers
Trigger names must be unique with respect to other triggers in the same schema. Trigger names do not need to be unique with respect to other schema objects, such as tables, views, and procedures. For example, a table and a trigger can have the same name (however, to avoid confusion, this is not recommended).
The SQL statement or the system event, database event, or DDL event that fires the trigger body. The options include DELETE, INSERT, and UPDATE. One, two, or all three of these options can be included in the triggering statement specification.
Coding Triggers
9-3
Creating Triggers
The table, view, DATABASE, or SCHEMA associated with the trigger. Exactly one table or view can be specified in the triggering statement. If the INSTEAD OF option is used, then the triggering statement may only specify a view; conversely, if a view is specified in the triggering statement, then only the INSTEAD OF option may be used.
Note:
For example, the PRINT_SALARY_CHANGES trigger fires after any DELETE, INSERT, or UPDATE on the Emp_tab table. Any of the following statements trigger the PRINT_SALARY_CHANGES trigger given in the previous example:
DELETE INSERT INSERT UPDATE FROM Emp_tab; INTO Emp_tab VALUES ( ... ); INTO Emp_tab SELECT ... FROM ... ; Emp_tab SET ... ;
If IGNORE=N (default) and the table already exists, then import does not change the table and no existing triggers fire. If the table does not exist, then import creates and loads it before any triggers are defined, so again no triggers fire. If IGNORE=Y, then import loads rows into existing tables. Any existing triggers fire, and indexes are updated to account for the imported data.
Notes:
You cannot specify a column list for UPDATE with INSTEAD OF triggers. If the column specified in the UPDATE OF clause is an object column, then the trigger is also fired if any of the attributes of the object are modified. You cannot specify UPDATE OF clauses on collection columns.
Creating Triggers
a CREATE TRIGGER statement, the BEFORE or AFTER option is specified just before the triggering statement. For example, the PRINT_SALARY_CHANGES trigger in the previous example is a BEFORE trigger. In general, you use BEFORE or AFTER triggers to achieve the following results:
Use BEFORE row triggers to modify the row before the row data is written to disk. Use AFTER row triggers to obtain, and perform operations, using the row ID. BEFORE row triggers are slightly more efficient than AFTER row triggers. With AFTER row triggers, affected data blocks must be read (logical read, not physical read) once for the trigger and then again for the triggering statement. Alternatively, with BEFORE row triggers, the data blocks must be read only once for both the triggering statement and the trigger.
Note:
Ordering of Triggers
A relational database does not guarantee the order of rows processed by a SQL statement. Therefore, do not create triggers that depend on the order in which rows are processed. For example, do not assign a value to a global package variable in a row trigger if the current value of the global variable is dependent on the row being processed by the row trigger. Also, if global package variables are updated within a trigger, then it is best to initialize those variables in a BEFORE statement trigger. When a statement in a trigger body causes another trigger to be fired, the triggers are said to be cascading. Oracle Database allows up to 32 triggers to cascade at any one time. However, you can effectively limit the number of trigger cascades using the initialization parameter OPEN_CURSORS, because a cursor must be opened for every execution of a trigger.
Oracle Database Concepts for more information on the firing order of triggers
Each subsequent trigger sees the changes made by the previously fired triggers. Each trigger can see the old and new values. The old values are the original values, and the
Coding Triggers
9-5
Creating Triggers
new values are the current values, as set by the most recently fired UPDATE or INSERT trigger. To ensure that multiple triggered actions occur in a specific order, you must consolidate these actions into a single trigger (for example, by having the trigger call a series of procedures).
Note:
The INSTEAD OF option can only be used for triggers created over views. The BEFORE and AFTER options cannot be used for triggers created over views. The CHECK option for views is not enforced when inserts or updates to the view are done using INSTEAD OF triggers. The INSTEAD OF trigger body must enforce the check.
A set operator A DISTINCT operator An aggregate or analytic function A GROUP BY, ORDER BY, MODEL, CONNECT BY, or START WITH clause A collection expression in a SELECT list A subquery in a SELECT list A subquery designated WITH READ ONLY
Creating Triggers
If a view contains pseudocolumns or expressions, then you can only update the view with an UPDATE statement that does not refer to any of the pseudocolumns or expressions.
You may need to set up the following data structures for this example to work:
Project_tab ( NUMBER, NUMBER, NUMBER); Emp_tab ( NUMBER NOT NULL, VARCHAR2(10), VARCHAR2(9), NUMBER(4), DATE, NUMBER(7,2), NUMBER(7,2), NUMBER(2) NOT NULL); Dept_tab ( NUMBER(2) NOT NULL, VARCHAR2(14), VARCHAR2(13), NUMBER, NUMBER);
CREATE TABLE Prj_level Projno Resp_dept CREATE TABLE Empno Ename Job Mgr Hiredate Sal Comm Deptno CREATE TABLE Deptno Dname Loc Mgr_no Dept_type
The following example shows an INSTEAD OF trigger for inserting rows into the MANAGER_INFO view.
CREATE OR REPLACE VIEW manager_info AS SELECT e.ename, e.empno, d.dept_type, d.deptno, p.prj_level, p.projno FROM Emp_tab e, Dept_tab d, Project_tab p WHERE e.empno = d.mgr_no AND d.deptno = p.resp_dept; CREATE OR REPLACE TRIGGER manager_info_insert INSTEAD OF INSERT ON manager_info REFERENCING NEW AS n -- new manager information FOR EACH ROW DECLARE rowcnt number; BEGIN SELECT COUNT(*) INTO rowcnt FROM Emp_tab WHERE empno = :n.empno; IF rowcnt = 0 THEN INSERT INTO Emp_tab (empno,ename) VALUES (:n.empno, :n.ename); ELSE UPDATE Emp_tab SET Emp_tab.ename = :n.ename WHERE Emp_tab.empno = :n.empno; END IF; SELECT COUNT(*) INTO rowcnt FROM Dept_tab WHERE deptno = :n.deptno; Coding Triggers 9-7
Creating Triggers
IF rowcnt = 0 THEN INSERT INTO Dept_tab (deptno, dept_type) VALUES(:n.deptno, :n.dept_type); ELSE UPDATE Dept_tab SET Dept_tab.dept_type = :n.dept_type WHERE Dept_tab.deptno = :n.deptno; END IF; SELECT COUNT(*) INTO rowcnt FROM Project_tab WHERE Project_tab.projno = :n.projno; IF rowcnt = 0 THEN INSERT INTO Project_tab (projno, prj_level) VALUES(:n.projno, :n.prj_level); ELSE UPDATE Project_tab SET Project_tab.prj_level = :n.prj_level WHERE Project_tab.projno = :n.projno; END IF; END;
The actions shown for rows being inserted into the MANAGER_INFO view first test to see if appropriate rows already exist in the base tables from which MANAGER_INFO is derived. The actions then insert new rows or update existing rows, as appropriate. Similar triggers can specify appropriate actions for UPDATE and DELETE.
To modify an object materialized by an object view in the client-side object cache and flush it back to the persistent store, you must specify INSTEAD OF triggers, unless the object view is modifiable. If the object is read only, then it is not necessary to define triggers to pin it.
These triggers:
Can only be defined over nested table columns in views. Fire only when the nested table elements are modified using the THE() or TABLE() clauses. They do not fire when a DML statement is performed on the view.
For example, consider a department view that contains a nested table of employees.
CREATE OR REPLACE VIEW Dept_view AS SELECT d.Deptno, d.Dept_type, d.Dept_name, CAST (MULTISET ( SELECT e.Empno, e.Empname, e.Salary) FROM Emp_tab e
Creating Triggers
The CAST (MULTISET..) operator creates a multi-set of employees for each department. If you want to modify the emplist column, which is the nested table of employees, then you can define an INSTEAD OF trigger over the column to handle the operation. The following example shows how an insert trigger might be written:
CREATE OR REPLACE TRIGGER Dept_emplist_tr INSTEAD OF INSERT ON NESTED TABLE Emplist OF Dept_view REFERENCING NEW AS Employee PARENT AS Department FOR EACH ROW BEGIN -- The insert on the nested table is translated to an insert on the base table: INSERT INTO Emp_tab VALUES ( :Employee.Empno, :Employee.Empname,:Employee.Salary, :Department.Deptno); END;
Any INSERT into the nested table fires the trigger, and the Emp_tab table is filled with the correct values. For example:
INSERT INTO TABLE (SELECT d.Emplist FROM Dept_view d WHERE Deptno = 10) VALUES (1001, 'John Glenn', 10000);
The :department.deptno correlation variable in this example would have a value of 10.
You may need to set up the following data structures for certain examples to work:
CREATE TABLE Emp_log ( Emp_id NUMBER, Log_date DATE, New_salary NUMBER, Action VARCHAR2(20));
CREATE OR REPLACE TRIGGER Log_salary_increase AFTER UPDATE ON Emp_tab FOR EACH ROW WHEN (new.Sal > 1000) BEGIN INSERT INTO Emp_log (Emp_id, Log_date, New_salary, Action) VALUES (:new.Empno, SYSDATE, :new.SAL, 'NEW SAL'); END;
Coding Triggers
9-9
If there are five employees in department 20, then the trigger fires five times when this statement is entered, because five rows are affected. The following trigger fires only once for each UPDATE of the Emp_tab table:
CREATE OR REPLACE TRIGGER Log_emp_update AFTER UPDATE ON Emp_tab BEGIN INSERT INTO Emp_log (Log_date, Action) VALUES (SYSDATE, 'Emp_tab COMMISSIONS CHANGED'); END;
See Also:
The statement level triggers are useful for performing validation checks for the entire statement.
If included, then the expression in the WHEN clause is evaluated for each row that the trigger affects. If the expression evaluates to TRUE for a row, then the trigger body is fired on behalf of that row. However, if the expression evaluates to FALSE or NOT TRUE for a row (unknown, as with nulls), then the trigger body is not fired for that row. The evaluation of the WHEN clause does not have an effect on the execution of the triggering SQL statement (in other words, the triggering statement is not rolled back if the expression in a WHEN clause evaluates to FALSE). For example, in the PRINT_SALARY_CHANGES trigger, the trigger body is not run if the new value of Empno is zero, NULL, or negative. In more realistic examples, you might test if one column value is less than another. The expression in a WHEN clause of a row trigger can include correlation names, which are explained later. The expression in a WHEN clause must be a SQL expression, and it cannot include a subquery. You cannot use a PL/SQL expression (including user-defined functions) in the WHEN clause.
Note:
The trigger body for row triggers has some special constructs that can be included in the code of the PL/SQL block: correlation names and the REFERENCEING option, and the conditional predicates INSERTING, DELETING, and UPDATING.
Note:
The INSERTING, DELETING, and UPDATING conditional predicates cannot be used for the CALL procedures; they can only be used in a PL/SQL block.
You may need to set up data structures similar to the following for certain examples to work:
CONNECT system/manager GRANT ADMINISTER DATABASE TRIGGER TO scott; CONNECT scott/tiger CREATE TABLE audit_table ( seq number, user_at VARCHAR2(10), time_now DATE, term VARCHAR2(10), job VARCHAR2(10), proc VARCHAR2(10), enum NUMBER);
CREATE OR REPLACE PROCEDURE foo (c VARCHAR2) AS BEGIN INSERT INTO Audit_table (user_at) VALUES(c); END; CREATE OR REPLACE TRIGGER logontrig AFTER LOGON ON DATABASE -- Just call an existing procedure. The ORA_LOGIN_USER is a function -- that returns information about the event that fired the trigger. CALL foo (ora_login_user) /
Although triggers are declared using PL/SQL, they can call procedures in other languages, such as Java:
CREATE OR REPLACE PROCEDURE Before_delete (Id IN NUMBER, Ename VARCHAR2) IS language Java name 'thjvTriggers.beforeDelete (oracle.sql.NUMBER, oracle.sql.CHAR)'; CREATE OR REPLACE TRIGGER Pre_del_trigger BEFORE DELETE ON Tab FOR EACH ROW CALL Before_delete (:old.Id, :old.Ename) /
public state void beforeDelete (NUMBER old_id, CHAR old_name) Throws SQLException, CoreException { Connection conn = JDBCConnection.defaultConnection(); Statement stmt = conn.CreateStatement(); String sql = "insert into logtab values ("+ old_id.intValue() +", '"+ old_ename.toString() + ", BEFORE DELETE'); stmt.executeUpdate (sql); stmt.close(); return; } }
A trigger fired by an INSERT statement has meaningful access to new column values only. Because the row is being created by the INSERT, the old values are null. A trigger fired by an UPDATE statement has access to both old and new column values for both BEFORE and AFTER row triggers. A trigger fired by a DELETE statement has meaningful access to :old column values only. Because the row no longer exists after the row is deleted, the :new values are NULL. However, you cannot modify :new values because ORA-4084 is raised if you try to modify :new values.
The new column values are referenced using the new qualifier before the column name, while the old column values are referenced using the old qualifier before the column name. For example, if the triggering statement is associated with the Emp_tab table (with the columns SAL, COMM, and so on), then you can include statements in the trigger body. For example:
IF :new.Sal > 10000 ... IF :new.Sal < :old.Sal ...
Old and new values are available in both BEFORE and AFTER row triggers. A new column value can be assigned in a BEFORE row trigger, but not in an AFTER row trigger (because the triggering statement takes effect before an AFTER row trigger is fired). If a BEFORE row trigger changes the value of new.column, then an AFTER row trigger fired by the same statement sees the change assigned by the BEFORE row trigger. Correlation names can also be used in the Boolean expression of a WHEN clause. A colon (:) must precede the old and new qualifiers when they are used in a trigger body, but a colon is not allowed when using the qualifiers in the WHEN clause or the REFERENCING option.
drop table tab1; create table tab1 (c1 clob); insert into tab1 values ('<h1>HTML Document Fragment</h1><p>Some text.'); create or replace trigger trg1 before update on tab1 for each row begin dbms_output.put_line('Old value of CLOB column: '||:OLD.c1); dbms_output.put_line('Proposed new value of CLOB column: '||:NEW.c1); -- Previously, we couldn't change the new value for a LOB. -- Now, we can replace it, or construct a new value using SUBSTR, INSTR... -- operations for a CLOB, or DBMS_LOB calls for a BLOB. :NEW.c1 := :NEW.c1 || to_clob('<hr><p>Standard footer paragraph.'); dbms_output.put_line('Final value of CLOB column: '||:NEW.c1); end; / set serveroutput on; update tab1 set c1 = '<h1>Different Document Fragment</h1><p>Different text.'; select * from tab1;
You may need to set up the following data structures for certain examples to work:
CREATE OR REPLACE TRIGGER Print_salary_changes BEFORE UPDATE ON new REFERENCING new AS Newest FOR EACH ROW BEGIN :Newest.Field2 := TO_CHAR (:newest.field1); END;
Notice that the new qualifier is renamed to newest using the REFERENCING option, and it is then used in the trigger body.
The first condition evaluates to TRUE only if the statement that fired the trigger is an INSERT statement; the second condition evaluates to TRUE only if the statement that fired the trigger is an UPDATE statement. In an UPDATE trigger, a column name can be specified with an UPDATING conditional predicate to determine if the named column is being updated. For example, assume a trigger is defined as the following:
CREATE OR REPLACE TRIGGER ... ... UPDATE OF Sal, Comm ON Emp_tab ... BEGIN ... IF UPDATING ('SAL') THEN ... END IF; END;
The code in the THEN clause runs only if the triggering UPDATE statement updates the SAL column. This way, the trigger can minimize its overhead when the column of interest is not being changed.
DML statement. The trigger causes the old and new values of the object t in tbl to be written in tbl_history when tbl is updated. These old and new values are :OLD.OBJECT_VALUE and :NEW.OBJECT_VALUE. An update of the table tbl is done (each value of n is increased by 1). A select from the history table to check that the trigger works is then shown at the end of the example:
CREATE OR REPLACE TYPE t AS OBJECT (n NUMBER, m NUMBER) / CREATE TABLE tbl OF t / BEGIN FOR j IN 1..5 LOOP INSERT INTO tbl VALUES (t(j, 0)); END LOOP; END; / CREATE TABLE tbl_history ( d DATE, old_obj t, new_obj t) / CREATE OR REPLACE TRIGGER Tbl_Trg AFTER UPDATE ON tbl FOR EACH ROW BEGIN INSERT INTO tbl_history (d, old_obj, new_obj) VALUES (SYSDATE, :OLD.OBJECT_VALUE, :NEW.OBJECT_VALUE); END Tbl_Trg; / -------------------------------------------------------------------------------UPDATE tbl SET tbl.n = tbl.n+1 / BEGIN FOR j IN (SELECT d, old_obj, new_obj FROM tbl_history) LOOP Dbms_Output.Put_Line ( j.d|| ' -- old: '||j.old_obj.n||' '||j.old_obj.m|| ' -- new: '||j.new_obj.n||' '||j.new_obj.m); END LOOP; END; /
The result of the select shows that the values of the column n have been all increased by 1. The value of m remains 0. The output of the select is:
23-MAY-05 23-MAY-05 23-MAY-05 23-MAY-05 23-MAY-05 -----old: old: old: old: old: 1 2 3 4 5 0 0 0 0 0 -----new: new: new: new: new: 2 3 4 5 6 0 0 0 0 0
A trigger is compiled when it is created. Thus, if a remote site is unavailable when the trigger must compile, then Oracle Database cannot validate the statement accessing the remote database, and the compilation fails. The previous example exception statement cannot run, because the trigger does not complete compilation. Because stored procedures are stored in a compiled form, the work-around for the previous example is as follows:
CREATE OR REPLACE TRIGGER Example AFTER INSERT ON Emp_tab FOR EACH ROW BEGIN Insert_row_proc; END; CREATE OR REPLACE PROCEDURE Insert_row_proc AS BEGIN INSERT INTO Emp_tab@Remote VALUES ('x'); EXCEPTION WHEN OTHERS THEN INSERT INTO Emp_log VALUES ('x'); END;
The trigger in this example compiles successfully and calls the stored procedure, which already has a validated statement for accessing the remote database; thus, when the remote INSERT statement fails because the link is down, the exception is caught.
A procedure called by a trigger cannot run the previous transaction control statements, because the procedure runs within the context of the trigger body.
Statements inside a trigger can reference remote schema objects. However, pay special attention when calling remote procedures from within a local trigger. If a timestamp or signature mismatch is found during execution of the trigger, then the remote procedure is not run, and the trigger is invalidated.
A SQL statement within a trigger can insert data into a column of LONG or LONG RAW datatype. If data from a LONG or LONG RAW column can be converted to a constrained datatype (such as CHAR and VARCHAR2), then a LONG or LONG RAW column can be referenced in a SQL statement within a trigger. The maximum length for these datatypes is 32000 bytes. Variables cannot be declared using the LONG or LONG RAW datatypes. :NEW and :PARENT cannot be used with LONG or LONG RAW columns.
An error is returned because the table is mutating when the row is deleted:
ORA-04091: table SCOTT.Emp_tab is mutating, trigger/function may not see it
If you delete the line "FOR EACH ROW" from the trigger, it becomes a statement trigger which is not subject to this restriction, and the trigger. If you need to update a mutating table, you could bypass these restrictions by using a temporary table, a PL/SQL table, or a package variable. For example, in place of a single AFTER row trigger that updates the original table, resulting in a mutating table
Coding Triggers 9-17
error, you might use two triggersan AFTER row trigger that updates a temporary table, and an AFTER statement trigger that updates the original table with the values from the temporary table. Declarative integrity constraints are checked at various times with respect to row triggers.
See Also:
Oracle Database Concepts for information about the interaction of triggers and integrity constraints
Because declarative referential integrity constraints are not supported between tables on different nodes of a distributed database, the mutating table restrictions do not apply to triggers that access remote nodes. These restrictions are also not enforced among tables in the same database that are connected by loop-back database links. A loop-back database link makes a local table appear remote by defining an Oracle Net path back to the database that contains the link. Restrictions on Mutating Tables Relaxed The mutating error, discussed earlier in this section, still prevents the trigger from reading or modifying the table that the parent statement is modifying. However, starting in Oracle Database Release 8.1, a delete against the parent table causes before/after statement triggers to be fired once. That way, you can create triggers (just not row triggers) to read and modify the parent and child tables. This allows most foreign key constraint actions to be implemented through their obvious after-row trigger, providing the constraint is not self-referential. Update cascade, update set null, update set default, delete set default, inserting a missing parent, and maintaining a count of children can all be implemented easily. For example, this is an implementation of update cascade:
create table p create table f create trigger update f set end; / (p1 number constraint ppk primary key); (f1 number constraint ffk references p); pt after update on p for each row begin f1 = :new.p1 where f1 = :old.p1;
This implementation requires care for multirow updates. For example, if a table p has three rows with the values (1), (2), (3), and table f also has three rows with the values (1), (2), (3), then the following statement updates p correctly but causes problems when the trigger updates f:
update p set p1 = p1+1;
The statement first updates (1) to (2) in p, and the trigger updates (1) to (2) in f, leaving two rows of value (2) in f. Then the statement updates (2) to (3) in p, and the trigger updates both rows of value (2) to (3) in f. Finally, the statement updates (3) to (4) in p, and the trigger updates all three rows in f from (3) to (4). The relationship of the data in p and f is lost. To avoid this problem, you must forbid multirow updates to p that change the primary key and reuse existing primary key values. It could also be solved by tracking which foreign key values have already been updated, then modifying the trigger so that no row is updated twice. That is the only problem with this technique for foreign key updates. The trigger cannot miss rows that have been changed but not committed by another transaction, because the foreign key constraint guarantees that no matching foreign key rows are locked before the after-row trigger is called.
9-18 Oracle Database Application Developers Guide - Fundamentals
Then, trigger my_trigger is not fired after the creation of my_trigger. Oracle Database does not fire a trigger that is not committed. Foreign Function Callouts All restrictions on foreign function callouts also apply.
Own the table specified in the triggering statement, or Have the ALTER privilege for the table in the triggering statement, or Have the ALTER ANY TABLE system privilege
To create a trigger in another user's schema, or to reference a table in another schema from a trigger in your schema, you must have the CREATE ANY TRIGGER system privilege. With this privilege, the trigger can be created in any schema and can be associated with any user's table. In addition, the user creating the trigger must also have EXECUTE privilege on the referenced procedures, functions, or packages. To create a trigger on DATABASE, you must have the ADMINISTER DATABASE TRIGGER privilege. If this privilege is later revoked, then you can drop the trigger, but not alter it. The object privileges to the schema objects referenced in the trigger body must be granted to the trigger owner explicitly (not through a role). The statements in the trigger body operate under the privilege domain of the trigger owner, not the privilege domain of the user issuing the triggering statement. This is similar to the privilege model for stored procedures.
Compiling Triggers
Compiling Triggers
Triggers are similar to PL/SQL anonymous blocks with the addition of the :new and :old capabilities, but their compilation is different. A PL/SQL anonymous block is compiled each time it is loaded into memory. Compilation involves three stages:
1. 2. 3.
Syntax checking: PL/SQL syntax is checked, and a parse tree is generated. Semantic checking: Type checking and further processing on the parse tree. Code generation: The pcode is generated.
Triggers, in contrast, are fully compiled when the CREATE TRIGGER statement is entered, and the pcode is stored in the data dictionary. Hence, firing the trigger no longer requires the opening of a shared cursor to run the trigger action. Instead, the trigger is executed directly. If errors occur during the compilation of a trigger, then the trigger is still created. If a DML statement fires this trigger, then the DML statement fails. (Runtime that trigger errors always cause the DML statement to fail.) You can use the SHOW ERRORS statement in SQL*Plus or Enterprise Manager to see any compilation errors when you create a trigger, or you can SELECT the errors from the USER_ERRORS view.
Triggers may depend on other functions or packages. If the function or package specified in the trigger is dropped, then the trigger is marked invalid. An attempt is made to validate the trigger on occurrence of the event. If the trigger cannot be validated successfully, then it is marked VALID WITH ERRORS, and the event fails.
Note:
There is an exception for STARTUP events: STARTUP events succeed even if the trigger fails. There are also exceptions for SHUTDOWN events and for LOGON events if you login as SYSTEM. Because the DBMS_AQ package is used to enqueue a message, dependency between triggers and queues cannot be maintained.
Recompiling Triggers
Use the ALTER TRIGGER statement to recompile a trigger manually. For example, the following statement recompiles the PRINT_SALARY_CHANGES trigger:
ALTER TRIGGER Print_salary_changes COMPILE;
To recompile a trigger, you must own the trigger or have the ALTER ANY TRIGGER system privilege.
Modifying Triggers
Like a stored procedure, a trigger cannot be explicitly altered: It must be replaced with a new definition. (The ALTER TRIGGER statement is used only to recompile, enable, or disable a trigger.) When replacing a trigger, you must include the OR REPLACE option in the CREATE TRIGGER statement. The OR REPLACE option is provided to allow a new version of an existing trigger to replace the older version, without affecting any grants made for the original version of the trigger. Alternatively, the trigger can be dropped using the DROP TRIGGER statement, and you can rerun the CREATE TRIGGER statement. To drop a trigger, the trigger must be in your schema, or you must have the DROP ANY TRIGGER system privilege.
Debugging Triggers
You can debug a trigger using the same facilities available for stored procedures.
See Also:
Enabling Triggers
By default, a trigger is automatically enabled when it is created; however, it can later be disabled. After you have completed the task that required the trigger to be disabled, re-enable the trigger, so that it fires when appropriate. Enable a disabled trigger using the ALTER TRIGGER statement with the ENABLE option. To enable the disabled trigger named REORDER of the INVENTORY table, enter the following statement:
ALTER TRIGGER Reorder ENABLE;
All triggers defined for a specific table can be enabled with one statement using the ALTER TABLE statement with the ENABLE clause with the ALL TRIGGERS option. For example, to enable all triggers defined for the INVENTORY table, enter the following statement:
ALTER TABLE Inventory ENABLE ALL TRIGGERS;
Disabling Triggers
You might temporarily disable a trigger if:
An object it references is not available. You need to perform a large data load, and you want it to proceed quickly without firing triggers. You are reloading data.
By default, triggers are enabled when first created. Disable a trigger using the ALTER TRIGGER statement with the DISABLE option. For example, to disable the trigger named REORDER of the INVENTORY table, enter the following statement:
ALTER TRIGGER Reorder DISABLE;
All triggers associated with a table can be disabled with one statement using the ALTER TABLE statement with the DISABLE clause and the ALL TRIGGERS option. For example, to disable all triggers defined for the INVENTORY table, enter the following statement:
ALTER TABLE Inventory DISABLE ALL TRIGGERS;
The new column, BASE_OBJECT_TYPE, specifies whether the trigger is based on DATABASE, SCHEMA, table, or view. The old column, TABLE_NAME, is null if the base object is not table or view. The column ACTION_TYPE specifies whether the trigger is a call type trigger or a PL/SQL trigger. The column TRIGGER_TYPE includes two additional values: BEFORE EVENT and AFTER EVENT, applicable only to system events. The column TRIGGERING_EVENT includes all system and DML events.
See Also:
Oracle Database Reference for a complete description of these data dictionary views
For example, assume the following statement was used to create the REORDER trigger:
Caution: You may need to set up data structures for certain examples to work:
CREATE OR REPLACE TRIGGER Reorder AFTER UPDATE OF Parts_on_hand ON Inventory FOR EACH ROW WHEN(new.Parts_on_hand < new.Reorder_point) DECLARE x NUMBER; BEGIN SELECT COUNT(*) INTO x
FROM Pending_orders WHERE Part_no = :new.Part_no; IF x = 0 THEN INSERT INTO Pending_orders VALUES (:new.Part_no, :new.Reorder_quantity, sysdate); END IF; END;
The following two queries return information about the REORDER trigger:
SELECT Trigger_type, Triggering_event, Table_name FROM USER_TRIGGERS WHERE Trigger_name = 'REORDER'; TYPE TRIGGERING_STATEMENT TABLE_NAME ---------------- -------------------------- -----------AFTER EACH ROW UPDATE INVENTORY SELECT Trigger_body FROM USER_TRIGGERS WHERE Trigger_name = 'REORDER'; TRIGGER_BODY -------------------------------------------DECLARE x NUMBER; BEGIN SELECT COUNT(*) INTO x FROM Pending_orders WHERE Part_no = :new.Part_no; IF x = 0 THEN INSERT INTO Pending_orders VALUES (:new.Part_no, :new.Reorder_quantity, sysdate); END IF; END;
Provide sophisticated auditing Prevent invalid transactions Enforce referential integrity (either those actions not supported by declarative integrity constraints or across nodes in a distributed database) Enforce complex business rules Enforce complex security authorizations Provide transparent event logging Automatically generate derived column values Enable building complex views that are updatable Track system events
This section provides an example of each of these trigger applications. These examples are not meant to be used exactly as written: They are provided to assist you in designing your own triggers.
Centralized Audit Trail Declarative Method Auditing Options can be Audited Session and Execution time Auditing
When using triggers to provide sophisticated auditing, AFTER triggers are normally used. By using AFTER triggers, auditing information is recorded after the triggering statement is subjected to any applicable integrity constraints, preventing cases where the audit processing is carried out unnecessarily for statements that generate exceptions to integrity constraints. Choosing between AFTER row and AFTER statement triggers depends on the information being audited. For example, row triggers provide value-based auditing for each table row. Triggers can also require the user to supply a "reason code" for
issuing the audited SQL statement, which can be useful in both row and statement-level auditing situations. The following example demonstrates a trigger that audits modifications to the Emp_tab table for each row. It requires that a "reason code" be stored in a global package variable before the update. This shows how triggers can be used to provide value-based auditing and how to use public package variables.
Note:
You may need to set up the following data structures for the examples to work:
CREATE OR REPLACE PACKAGE Auditpackage AS Reason VARCHAR2(10); PROCEDURE Set_reason(Reason VARCHAR2); END; CREATE TABLE Emp99 ( Empno NOT NULL NUMBER(4), Ename VARCHAR2(10), Job VARCHAR2(9), Mgr NUMBER(4), Hiredate DATE, Sal NUMBER(7,2), Comm NUMBER(7,2), Deptno NUMBER(2), Bonus NUMBER, Ssn NUMBER, Job_classification NUMBER); CREATE TABLE Audit_employee ( Oldssn NUMBER, Oldname VARCHAR2(10), Oldjob VARCHAR2(2), Oldsal NUMBER, Newssn NUMBER, Newname VARCHAR2(10), Newjob VARCHAR2(2), Newsal NUMBER, Reason VARCHAR2(10), User1 VARCHAR2(10), Systemdate DATE);
CREATE OR REPLACE TRIGGER Audit_employee AFTER INSERT OR DELETE OR UPDATE ON Emp99 FOR EACH ROW BEGIN /* AUDITPACKAGE is a package with a public package variable REASON. REASON could be set by the application by a command such as EXECUTE AUDITPACKAGE.SET_REASON(reason_string). Note that a package variable has state for the duration of a session and that each session has a separate copy of all package variables. */ IF Auditpackage.Reason IS NULL THEN Raise_application_error(-20201, 'Must specify reason' || ' with AUDITPACKAGE.SET_REASON(Reason_string)'); END IF; /* If the preceding conditional evaluates to TRUE, the
user-specified error number and message is raised, the trigger stops execution, and the effects of the triggering statement are rolled back. Otherwise, a new row is inserted into the predefined auditing table named AUDIT_EMPLOYEE containing the existing and new values of the Emp_tab table and the reason code defined by the REASON variable of AUDITPACKAGE. Note that the "old" values are NULL if triggering statement is an INSERT and the "new" values are NULL if the triggering statement is a DELETE. */ INSERT INTO Audit_employee VALUES (:old.Ssn, :old.Ename, :old.Job_classification, :old.Sal, :new.Ssn, :new.Ename, :new.Job_classification, :new.Sal, auditpackage.Reason, User, Sysdate ); END;
Optionally, you can also set the reason code back to NULL if you wanted to force the reason code to be set for every update. The following simple AFTER statement trigger sets the reason code back to NULL after the triggering statement is run:
CREATE OR REPLACE TRIGGER Audit_employee_reset AFTER INSERT OR DELETE OR UPDATE ON Emp_tab BEGIN auditpackage.set_reason(NULL); END;
Notice that the previous two triggers are both fired by the same type of SQL statement. However, the AFTER row trigger is fired once for each row of the table affected by the triggering statement, while the AFTER statement trigger is fired only once after the triggering statement execution is completed. This next trigger also uses triggers to do auditing. It tracks changes made to the Emp_tab table and stores this information in AUDIT_TABLE and AUDIT_TABLE_VALUES.
Note:
You may need to set up the following data structures for the example to work:
CREATE TABLE Audit_table ( Seq NUMBER, User_at VARCHAR2(10), Time_now DATE, Term VARCHAR2(10), Job VARCHAR2(10), Proc VARCHAR2(10), enum NUMBER); CREATE SEQUENCE Audit_seq; CREATE TABLE Audit_table_values ( Seq NUMBER, Dept NUMBER, Dept1 NUMBER, Dept2 NUMBER);
CREATE OR REPLACE TRIGGER Audit_emp AFTER INSERT OR UPDATE OR DELETE ON Emp_tab FOR EACH ROW DECLARE Time_now DATE;
Terminal CHAR(10); BEGIN -- get current time, and the terminal of the user: Time_now := SYSDATE; Terminal := USERENV('TERMINAL'); -- record new employee primary key IF INSERTING THEN INSERT INTO Audit_table VALUES (Audit_seq.NEXTVAL, User, Time_now, Terminal, 'Emp_tab', 'INSERT', :new.Empno); -- record primary key of the deleted row: ELSIF DELETING THEN INSERT INTO Audit_table VALUES (Audit_seq.NEXTVAL, User, Time_now, Terminal, 'Emp_tab', 'DELETE', :old.Empno); -- for updates, record the primary key -- of the row being updated: ELSE INSERT INTO Audit_table VALUES (audit_seq.NEXTVAL, User, Time_now, Terminal, 'Emp_tab', 'UPDATE', :old.Empno); -- and for SAL and DEPTNO, record old and new values: IF UPDATING ('SAL') THEN INSERT INTO Audit_table_values VALUES (Audit_seq.CURRVAL, 'SAL', :old.Sal, :new.Sal); ELSIF UPDATING ('DEPTNO') THEN INSERT INTO Audit_table_values VALUES (Audit_seq.CURRVAL, 'DEPTNO', :old.Deptno, :new.DEPTNO); END IF; END IF; END;
Triggers constrain what a transaction can do. A trigger does not apply to data loaded before the definition of the trigger; therefore, it is not known if all data in a table conforms to the rules established by an associated trigger. Although triggers can be written to enforce many of the same rules supported by Oracle Database's declarative integrity constraint features, triggers should only be used to enforce complex business rules that cannot be defined using standard integrity constraints. The declarative integrity constraint features provided with Oracle Database offer the following advantages when compared to constraints defined by triggers: Centralized integrity checks. All points of data access must adhere to the global set of rules defined by the integrity constraints corresponding to each schema object.
Declarative method. Constraints defined using the standard integrity constraint features are much easier to write and are less prone to errors, when compared with comparable constraints defined by triggers. While most aspects of data integrity can be defined and enforced using declarative integrity constraints, triggers can be used to enforce complex business constraints not definable using declarative integrity constraints. For example, triggers can be used to enforce:
UPDATE SET NULL, and UPDATE and DELETE SET DEFAULT referential actions. Referential integrity when the parent and child tables are on different nodes of a distributed database. Complex check constraints not definable using the expressions allowed in a CHECK constraint.
A trigger must be defined for the child table that guarantees values inserted or updated in the foreign key correspond to values in the parent key. One or more triggers must be defined for the parent table. These triggers guarantee the desired referential action (RESTRICT, CASCADE, or SET NULL) for values in the foreign key when values are updated or deleted in the parent key. No action is required for inserts into the parent table (no dependent foreign keys exist).
The following sections provide examples of the triggers necessary to enforce referential integrity. The Emp_tab and Dept_tab table relationship is used in these examples. Several of the triggers include statements that lock rows (SELECT... FOR UPDATE). This operation is necessary to maintain concurrency as the rows are being processed.
Foreign Key Trigger for Child Table The following trigger guarantees that before an INSERT or UPDATE statement affects a foreign key value, the corresponding value exists in the parent key. The mutating table exception included in the following example allows this trigger to be used with the UPDATE_SET_DEFAULT and UPDATE_CASCADE triggers. This exception can be removed if this trigger is used alone.
CREATE OR REPLACE TRIGGER Emp_dept_check BEFORE INSERT OR UPDATE OF Deptno ON Emp_tab FOR EACH ROW WHEN (new.Deptno IS NOT NULL) -- Before a row is inserted, or DEPTNO is updated in the Emp_tab -- table, fire this trigger to verify that the new foreign -- key value (DEPTNO) is present in the Dept_tab table.
DECLARE Dummy INTEGER; -- to be used for cursor fetch Invalid_department EXCEPTION; Valid_department EXCEPTION; Mutating_table EXCEPTION; PRAGMA EXCEPTION_INIT (Mutating_table, -4091); -- Cursor used to verify parent key value exists. -- present, lock parent key's row so it can't be -- deleted by another transaction until this -- transaction is committed or rolled back. CURSOR Dummy_cursor (Dn NUMBER) IS SELECT Deptno FROM Dept_tab WHERE Deptno = Dn FOR UPDATE OF Deptno; BEGIN OPEN Dummy_cursor (:new.Deptno); FETCH Dummy_cursor INTO Dummy; ---IF If
Verify parent key. If not found, raise user-specified error number and message. If found, close cursor before allowing triggering statement to complete: Dummy_cursor%NOTFOUND THEN RAISE Invalid_department; ELSE RAISE valid_department; END IF; CLOSE Dummy_cursor; EXCEPTION WHEN Invalid_department THEN CLOSE Dummy_cursor; Raise_application_error(-20000, 'Invalid Department' || ' Number' || TO_CHAR(:new.deptno)); WHEN Valid_department THEN CLOSE Dummy_cursor; WHEN Mutating_table THEN NULL; END;
The following trigger is defined on the DEPT_TAB table to enforce the UPDATE and DELETE RESTRICT referential action on the primary key of the DEPT_TAB table:
UPDATE and DELETE RESTRICT Trigger for Parent Table
CREATE OR REPLACE TRIGGER Dept_restrict BEFORE DELETE OR UPDATE OF Deptno ON Dept_tab FOR EACH ROW -- Before a row is deleted from Dept_tab or the primary key -- (DEPTNO) of Dept_tab is updated, check for dependent -- foreign key values in Emp_tab; rollback if any are found. DECLARE Dummy INTEGER; -- to be used for cursor fetch Employees_present EXCEPTION; employees_not_present EXCEPTION; -- Cursor used to check for dependent foreign key values. CURSOR Dummy_cursor (Dn NUMBER) IS SELECT Deptno FROM Emp_tab WHERE Deptno = Dn; BEGIN
OPEN Dummy_cursor (:old.Deptno); FETCH Dummy_cursor INTO Dummy; -- If dependent foreign key is found, raise user-specified -- error number and message. If not found, close cursor -- before allowing triggering statement to complete. IF Dummy_cursor%FOUND THEN RAISE Employees_present; -- dependent rows exist ELSE RAISE Employees_not_present; -- no dependent rows END IF; CLOSE Dummy_cursor; EXCEPTION WHEN Employees_present THEN CLOSE Dummy_cursor; Raise_application_error(-20001, 'Employees Present in' || ' Department ' || TO_CHAR(:old.DEPTNO)); WHEN Employees_not_present THEN CLOSE Dummy_cursor; END;
Caution:
This trigger does not work with self-referential tables (tables with both the primary/unique key and the foreign key). Also, this trigger does not allow triggers to cycle (such as, A fires B fires A).
UPDATE and DELETE SET NULL Triggers for Parent Table: Example The following trigger is defined on the DEPT_TAB table to enforce the UPDATE and DELETE SET NULL referential action on the primary key of the DEPT_TAB table:
CREATE OR REPLACE TRIGGER Dept_set_null AFTER DELETE OR UPDATE OF Deptno ON Dept_tab FOR EACH ROW -- Before a row is deleted from Dept_tab or the primary key -- (DEPTNO) of Dept_tab is updated, set all corresponding -- dependent foreign key values in Emp_tab to NULL: BEGIN IF UPDATING AND :OLD.Deptno != :NEW.Deptno OR DELETING THEN UPDATE Emp_tab SET Emp_tab.Deptno = NULL WHERE Emp_tab.Deptno = :old.Deptno; END IF; END;
DELETE Cascade Trigger for Parent Table: Example The following trigger on the DEPT_TAB table enforces the DELETE CASCADE referential action on the primary key of the DEPT_TAB table:
CREATE OR REPLACE TRIGGER Dept_del_cascade AFTER DELETE ON Dept_tab FOR EACH ROW -- Before a row is deleted from Dept_tab, delete all -- rows from the Emp_tab table whose DEPTNO is the same as -- the DEPTNO being deleted from the Dept_tab table: BEGIN DELETE FROM Emp_tab WHERE Emp_tab.Deptno = :old.Deptno; END;
Note:
Typically, the code for DELETE CASCADE is combined with the code for UPDATE SET NULL or UPDATE SET DEFAULT to account for both updates and deletes.
The following trigger ensures that if a department number is updated in the Dept_tab table, then this change is propagated to dependent foreign keys in the Emp_tab table:
-- Generate a sequence number to be used as a flag for -- determining if an update has occurred on a column: CREATE SEQUENCE Update_sequence INCREMENT BY 1 MAXVALUE 5000 CYCLE; CREATE OR REPLACE PACKAGE Integritypackage AS Updateseq NUMBER; END Integritypackage; CREATE OR REPLACE PACKAGE BODY Integritypackage AS END Integritypackage; -- create flag col: ALTER TABLE Emp_tab ADD Update_id NUMBER; CREATE OR REPLACE TRIGGER Dept_cascade1 BEFORE UPDATE OF Deptno ON Dept_tab DECLARE Dummy NUMBER; -- Before updating the Dept_tab table (this is a statement -- trigger), generate a new sequence number and assign -- it to the public variable UPDATESEQ of a user-defined -- package named INTEGRITYPACKAGE: BEGIN SELECT Update_sequence.NEXTVAL INTO Dummy FROM dual; Integritypackage.Updateseq := Dummy; END; CREATE OR REPLACE TRIGGER Dept_cascade2 AFTER DELETE OR UPDATE OF Deptno ON Dept_tab FOR EACH ROW -- For each department number in Dept_tab that is updated, -- cascade the update to dependent foreign keys in the -- Emp_tab table. Only cascade the update if the child row -- has not already been updated by this trigger: BEGIN IF UPDATING THEN UPDATE Emp_tab SET Deptno = :new.Deptno, Update_id = Integritypackage.Updateseq --from 1st WHERE Emp_tab.Deptno = :old.Deptno AND Update_id IS NULL; /* only NULL if not updated by the 3rd trigger fired by this same triggering statement */ END IF; IF DELETING THEN -- Before a row is deleted from Dept_tab, delete all
-- rows from the Emp_tab table whose DEPTNO is the same as -- the DEPTNO being deleted from the Dept_tab table: DELETE FROM Emp_tab WHERE Emp_tab.Deptno = :old.Deptno; END IF; END; CREATE OR REPLACE TRIGGER Dept_cascade3 AFTER UPDATE OF Deptno ON Dept_tab BEGIN UPDATE Emp_tab SET Update_id = NULL WHERE Update_id = Integritypackage.Updateseq; END;
Note:
Because this trigger updates the Emp_tab table, the Emp_dept_check trigger, if enabled, is also fired. The resulting mutating table error is trapped by the Emp_dept_check trigger. You should carefully test any triggers that require error trapping to succeed to ensure that they always work properly in your environment.
Trigger for Complex Check Constraints: Example Triggers can enforce integrity rules other than referential integrity. For example, this trigger performs a complex check before allowing the triggering statement to run.
Note:
You may need to set up the following data structures for the example to work:
NUMBER, NUMBER, NUMBER, NUMBER)
CREATE OR REPLACE TRIGGER Salary_check BEFORE INSERT OR UPDATE OF Sal, Job ON Emp99 FOR EACH ROW DECLARE Minsal NUMBER; Maxsal NUMBER; Salary_out_of_range EXCEPTION; BEGIN /* Retrieve the minimum and maximum salary for the employee's new job classification from the SALGRADE table into MINSAL and MAXSAL: */ SELECT Minsal, Maxsal INTO Minsal, Maxsal FROM Salgrade WHERE Job_classification = :new.Job;
/* If the employee's new salary is less than or greater than the job classification's limits, the exception is raised. The exception message is returned and the pending INSERT or UPDATE statement that fired the trigger is rolled back:*/ IF (:new.Sal < Minsal OR :new.Sal > Maxsal) THEN
RAISE Salary_out_of_range; END IF; EXCEPTION WHEN Salary_out_of_range THEN Raise_application_error (-20300, 'Salary '||TO_CHAR(:new.Sal)||' out of range for ' ||'job classification '||:new.Job ||' for employee '||:new.Ename); WHEN NO_DATA_FOUND THEN Raise_application_error(-20322, 'Invalid Job Classification ' ||:new.Job_classification); END;
Complex Security Authorizations and Triggers: Example Triggers are commonly used to enforce complex security authorizations for table data. Only use triggers to enforce complex security authorizations that cannot be defined using the database security features provided with Oracle Database. For example, a trigger can prohibit updates to salary data of the Emp_tab table during weekends, holidays, and non-working hours. When using a trigger to enforce a complex security authorization, it is best to use a BEFORE statement trigger. Using a BEFORE statement trigger has these benefits:
The security check is done before the triggering statement is allowed to run, so that no wasted work is done by an unauthorized statement. The security check is performed only once for the triggering statement, not for each row affected by the triggering statement.
You may need to set up the following data structures for the example to work:
CREATE OR REPLACE TRIGGER Emp_permit_changes BEFORE INSERT OR DELETE OR UPDATE ON Emp99 DECLARE Dummy INTEGER; Not_on_weekends EXCEPTION; Not_on_holidays EXCEPTION; Non_working_hours EXCEPTION; BEGIN /* check for weekends: */ IF (TO_CHAR(Sysdate, 'DY') = 'SAT' OR TO_CHAR(Sysdate, 'DY') = 'SUN') THEN RAISE Not_on_weekends; END IF; /* check for company holidays:*/ SELECT COUNT(*) INTO Dummy FROM Company_holidays WHERE TRUNC(Day) = TRUNC(Sysdate); /* TRUNC gets rid of time parts of dates: */ IF dummy > 0 THEN RAISE Not_on_holidays; END IF; /* Check for work hours (8am to 6pm): */ IF (TO_CHAR(Sysdate, 'HH24') < 8 OR
TO_CHAR(Sysdate, 'HH24') > 18) THEN RAISE Non_working_hours; END IF; EXCEPTION WHEN Not_on_weekends THEN Raise_application_error(-20324,'May not change ' ||'employee table during the weekend'); WHEN Not_on_holidays THEN Raise_application_error(-20325,'May not change ' ||'employee table during a holiday'); WHEN Non_working_hours THEN Raise_application_error(-20326,'May not change ' ||'Emp_tab table during non-working hours'); END;
See Also: Oracle Database Security Guide for details on database security features
Transparent Event Logging and Triggers Triggers are very useful when you want to transparently perform a related change in the database following certain events. The REORDER trigger example shows a trigger that reorders parts as necessary when certain conditions are met. (In other words, a triggering statement is entered, and the PARTS_ON_HAND value is less than the REORDER_POINT value.) Derived Column Values and Triggers: Example Triggers can derive column values automatically, based upon a value provided by an INSERT or UPDATE statement. This type of trigger is useful to force values in specific columns that depend on the values of other columns in the same row. BEFORE row triggers are necessary to complete this type of operation for the following reasons:
The dependent values must be derived before the INSERT or UPDATE occurs, so that the triggering statement can use the derived values. The trigger must fire for each row affected by the triggering INSERT or UPDATE statement.
The following example illustrates how a trigger can be used to derive new column values for a table whenever a row is inserted or updated.
Note:
You may need to set up the following data structures for the example to work:
CREATE OR REPLACE TRIGGER Derived BEFORE INSERT OR UPDATE OF Ename ON Emp99 /* Before updating the ENAME field, derive the values for the UPPERNAME and SOUNDEXNAME fields. Users should be restricted from updating these fields directly: */ FOR EACH ROW BEGIN :new.Uppername := UPPER(:new.Ename);
Building Complex Updatable Views Using Triggers: Example Views are an excellent mechanism to provide logical windows over table data. However, when the view query gets complex, the system implicitly cannot translate the DML on the view into those on the underlying tables. INSTEAD OF triggers help solve this problem. These triggers can be defined over views, and they fire instead of the actual DML. Consider a library system where books are arranged under their respective titles. The library consists of a collection of book type objects. The following example explains the schema.
CREATE OR REPLACE TYPE Book_t AS OBJECT ( Booknum NUMBER, Title VARCHAR2(20), Author VARCHAR2(20), Available CHAR(1) ); CREATE OR REPLACE TYPE Book_list_t AS TABLE OF Book_t;
You can define a complex view over these tables to create a logical view of the library with sections and a collection of books in each section.
CREATE OR REPLACE VIEW Library_view AS SELECT i.Section, CAST (MULTISET ( SELECT b.Booknum, b.Title, b.Author, b.Available FROM Book_table b WHERE b.Section = i.Section) AS Book_list_t) BOOKLIST FROM Library_table i;
Make this view updatable by defining an INSTEAD OF trigger over the view.
CREATE OR REPLACE TRIGGER Library_trigger INSTEAD OF INSERT ON Library_view FOR EACH ROW Bookvar BOOK_T; i INTEGER; BEGIN INSERT INTO Library_table VALUES (:NEW.Section); FOR i IN 1..:NEW.Booklist.COUNT LOOP
Bookvar := Booklist(i); INSERT INTO book_table VALUES ( Bookvar.booknum, :NEW.Section, Bookvar.Title, Bookvar.Author, bookvar.Available); END LOOP; END; /
The library_view is an updatable view, and any INSERTs on the view are handled by the trigger that gets fired automatically. For example:
INSERT INTO Library_view VALUES ('History', book_list_t(book_t(121330, 'Alexander', 'Mirth', 'Y');
Similarly, you can also define triggers on the nested table booklist to handle modification of the nested table element. Tracking System Events Using Triggers
Fine-Grained Access Control Using Triggers: Example System triggers can be used to set application context. Application context is a relatively new feature that enhances your ability to implement fine-grained access control. Application context is a secure session cache, and it can be used to store session-specific attributes.
In the example that follows, procedure set_ctx sets the application context based on the user profile. The trigger setexpensectx ensures that the context is set for every user.
CONNECT secdemo/secdemo CREATE OR REPLACE CONTEXT Expenses_reporting USING Secdemo.Exprep_ctx; REM ================================================================= REM Creation of the package which implements the context: REM ================================================================= CREATE OR REPLACE PACKAGE Exprep_ctx AS PROCEDURE Set_ctx; END; SHOW ERRORS CREATE OR REPLACE PACKAGE BODY Exprep_ctx IS PROCEDURE Set_ctx IS Empnum NUMBER; Countrec NUMBER; Cc NUMBER; Role VARCHAR2(20); BEGIN -- SET emp_number: SELECT Employee_id INTO Empnum FROM Employee WHERE Last_name = SYS_CONTEXT('userenv', 'session_user'); DBMS_SESSION.SET_CONTEXT('expenses_reporting','emp_number', Empnum); -- SET ROLE: SELECT COUNT (*) INTO Countrec FROM Cost_center WHERE Manager_id=Empnum; IF (countrec > 0) THEN DBMS_SESSION.SET_CONTEXT('expenses_reporting','exp_role','MANAGER');
ELSE DBMS_SESSION.SET_CONTEXT('expenses_reporting','exp_role','EMPLOYEE'); END IF; -- SET cc_number: SELECT Cost_center_id INTO Cc FROM Employee WHERE Last_name = SYS_CONTEXT('userenv','session_user'); DBMS_SESSION.SET_CONTEXT(expenses_reporting','cc_number',Cc); END; END;
CALL Syntax
CREATE OR REPLACE TRIGGER Secdemo.Setexpseetx AFTER LOGON ON DATABASE CALL Secdemo.Exprep_etx.Set_otx
Infrastructure for publish/subscribe, by making the database an active publisher of events. Integration of data cartridges in the server. The system events publication can be used to notify cartridges of state changes in the server. Integration of fine-grained access control in the server.
By creating a trigger, you can specify a procedure that runs when an event occurs. DML events are supported on tables, and system events are supported on DATABASE and SCHEMA. You can turn notification on and off by enabling and disabling the trigger using the ALTER TRIGGER statement. This feature is integrated with the Advanced Queueing engine. Publish/subscribe applications use the DBMS_AQ.ENQUEUE() procedure, and other applications such as cartridges use callouts.
See Also:
Oracle Database SQL Reference Oracle Streams Advanced Queuing User's Guide and Reference for details on how to subscribe to published events
When an event occurs, the database fires all triggers that are enabled on that event, with some exceptions:
If the trigger is actually the target of the triggering event, it is not fired. For example, a trigger for all DROP events is not fired when it is dropped itself. If a trigger has been modified but not committed within the same transaction as the firing event. For example, recursive DDL within a system trigger might modify a trigger, which prevents the modified trigger from being fired by events within the same transaction.
You can create more than one trigger on an object. When an event fires more than one trigger, the firing order is not defined and so you should not rely on the triggers being fired in a particular order.
Publication Context
When an event is published, certain runtime context and attributes, as specified in the parameter list, are passed to the callout procedure. A set of functions called event attribute functions are provided.
See Also: "Event Attribute Functions" on page 9-38 for information on event-specific attributes
For each supported system event, you can identify and predefine event-specific attributes for the event. You can choose the parameter list to be any of these attributes, along with other simple expressions. For callouts, these are passed as IN arguments.
Error Handling
Return status from publication callout functions for all events are ignored. For example, with SHUTDOWN events, the database cannot do anything with the return status.
See Also: "List of Database Events" on page 9-41 for details on return status
Execution Model
Traditionally, triggers execute as the definer of the trigger. The trigger action of an event is executed as the definer of the action (as the definer of the package or function in callouts, or as owner of the trigger in queues). Because the owner of the trigger must have EXECUTE privileges on the underlying queues, packages, or procedure, this behavior is consistent.
Note:
To make these attributes available, you must first run the CATPROC.SQL script. The trigger dictionary object maintains metadata about events that will be published and their corresponding attributes. In earlier releases, these functions were accessed through the SYS package. We recommend you use these public synonyms whose names begin with ora_.
Table 92
Attribute
ora_client_ip_address
ora_database_name
VARCHAR2(50)
Database name.
ora_des_encrypted_password
VARCHAR2
The DES-encrypted password of the user being created or altered. Name of the dictionary object on which the DDL operation occurred. Return the list of object names of objects being modified in the event. Owner of the dictionary object on which the DDL operation occurred.
ora_dict_obj_name
VARCHAR(30)
BINARY_INTEGER
VARCHAR(30)
BINARY_INTEGER
Returns the list of IF (ora_sysevent='ASSOCIATE STATISTICS') object owners of objects THEN number_of_modified_objects := being modified in the ora_dict_obj_owner_list(owner_list); event. END IF; Type of the dictionary object on which the DDL operation occurred. Returns the grantees of a grant event in the OUT parameter; returns the number of grantees in the return value. Instance number. INSERT INTO event_table VALUES ('This object is a ' || ora_dict_obj_type); IF (ora_sysevent = 'GRANT') THEN number_of_users=ora_grantee(user_list); END IF;
VARCHAR(20)
BINARY_INTEGER
ora_instance_num
NUMBER
ora_is_creating_nested_table
BOOLEAN
BOOLEAN
Returns true if the specified column is dropped. Returns TRUE if given error is on error stack, FALSE otherwise. Login user name. In an INSTEAD OF trigger for CREATE TABLE, the position within the SQL text where you could insert a PARTITION clause.
ora_is_servererror
BOOLEAN
ora_login_user ora_partition_pos
VARCHAR2(30) BINARY_INTEGER
BINARY_INTEGER
Returns the list of IF (ora_sysevent = 'GRANT' OR privileges being ora_sysevent = 'REVOKE') THEN granted by the grantee number_of_privileges := or the list of privileges ora_privilege_list(priv_list); revoked from the END IF; revokees in the OUT parameter; returns the number of privileges in the return value. Returns the revokees of IF (ora_sysevent = 'REVOKE') THEN a revoke event in the number_of_users := ora_revokee(user_list); OUT parameter; returns the number of revokees in the return value. Given a position (1 for top of stack), it returns the error number at that position on error stack Returns the total number of error messages on the error stack. Given a position (1 for top of stack), it returns the error message at that position on error stack INSERT INTO event_table VALUES ('top stack error ' || ora_server_error(1));
BINARY_INTEGER
ora_server_error
NUMBER
ora_server_error_depth
BINARY_INTEGER
n := ora_server_error_depth; -- This value is used with other functions -- such as ora_server_error INSERT INTO event_table VALUES ('top stack error message' || ora_server_error_msg(1));
VARCHAR2
VARCHAR2
-- For example, the second %s in a -- message: "Expected %s, found %s" param := ora_server_error_param(1,2);
BINARY_INTEGER
sql_text ora_name_list_t; v_stmt VARCHAR2(2000); ... n := ora_sql_txt(sql_text); FOR i IN 1..n LOOP v_stmt := v_stmt || sql_text(i); END LOOP; INSERT INTO event_table VALUES ('text of triggering statement: ' || v_stmt); INSERT INTO event_table VALUES (ora_sysevent);
ora_sysevent
VARCHAR2(20)
ora_with_grant_option
BOOLEAN
IF (ora_sysevent = 'GRANT' and ora_with_grant_option = TRUE) THEN INSERT INTO event_table VALUES ('with grant option'); END IF;
space_error_info (error_number OUT NUMBER, error_type OUT VARCHAR2, object_owner OUT VARCHAR2, table_space_name OUT VARCHAR2, object_name OUT VARCHAR2, sub_object_name OUT VARCHAR2)
BOOLEAN
Returns true if the error IF (space_error_info(eno,typ,owner,ts,obj, is related to an subobj) = TRUE) THEN out-of-space condition, DBMS_OUTPUT.PUT_LINE('The object '|| obj and fills in the OUT || ' owned by ' || owner || parameters with ' has run out of space.'); information about the END IF; object that caused the error.
System Events
System events are related to entire instances or schemas, not individual tables or rows. Triggers created on startup and shutdown events must be associated with the database instance. Triggers created on error and suspend events can be associated with either the database instance or a particular schema. Table 93 contains a list of system manager events.
Table 93
Event STARTUP
SHUTDOWN
Just before the server starts the shutdown of an instance. This lets the cartridge shutdown completely. For abnormal instance shutdown, this event may not be fired.
None allowed
DB_ROLE_CHANGE When the database is opened for the first time after a role change. SERVERERROR When the error eno occurs. If no condition is given, then this event fires whenever an error occurs. The trigger does not fire on ORA-1034, ORA-1403, ORA-1422, ORA-1423, and ORA-4030 because they are not true errors or are too serious to continue processing. It also fails to fire on ORA-18 and ORA-20 because a process is not available to connect to the database to record the error.
None allowed
Starts a separate transaction and commits it after firing the triggers. Starts a separate transaction and commits it after firing the triggers.
ora_sysevent ora_login_user ora_instance_num ora_database_name ora_sysevent ora_login_user ora_instance_num ora_database_name ora_server_error ora_is_servererror space_error_info
Client Events
Client events are the events related to user logon/logoff, DML, and DDL operations. For example:
CREATE OR REPLACE TRIGGER On_Logon AFTER LOGON ON The_user.Schema BEGIN Do_Something; END;
The LOGON and LOGOFF events allow simple conditions on UID and USER. All other events allow simple conditions on the type and name of the object, as well as functions like UID and USER. The LOGON event starts a separate transaction and commits it after firing the triggers. All other events fire the triggers in the existing user transaction. The LOGON and LOGOFF events can operate on any objects. For all other events, the corresponding trigger cannot perform any DDL operations, such as DROP and ALTER, on the object that caused the event to be generated. The DDL allowed inside these triggers is altering, creating, or dropping a table, creating a trigger, and compile operations. If an event trigger becomes the target of a DDL operation (such as CREATE TRIGGER), it cannot be fired later during the same transaction Table 94 contains a list of client events.
9-42 Oracle Database Application Developers Guide - Fundamentals
Table 94
Event BEFORE ALTER AFTER ALTER
Client Events
When Fired? When a catalog object is altered. Attribute Functions ora_sysevent ora_login_user ora_instance_num ora_database_name ora_dict_obj_type ora_dict_obj_name ora_dict_obj_owner ora_des_encrypted_password (for ALTER USER events) ora_is_alter_column (for ALTER TABLE events) ora_is_drop_column (for ALTER TABLE events) ora_sysevent ora_login_user ora_instance_num ora_database_name ora_dict_obj_type ora_dict_obj_name ora_dict_obj_owner ora_sysevent ora_login_user ora_instance_num ora_database_name ora_dict_obj_name ora_dict_obj_type ora_dict_obj_owner ora_sysevent ora_login_user ora_instance_num ora_database_name ora_dict_obj_name ora_dict_obj_type ora_dict_obj_owner ora_dict_obj_name_list ora_dict_obj_owner_list ora_sysevent ora_login_user ora_instance_num ora_database_name ora_sysevent ora_login_user ora_instance_num ora_database_name ora_dict_obj_name ora_dict_obj_type ora_dict_obj_owner ora_sysevent ora_login_user ora_instance_num ora_database_name ora_dict_obj_type ora_dict_obj_name ora_dict_obj_owner ora_is_creating_nested_table (for CREATE TABLE events)
BEFORE AUDIT AFTER AUDIT BEFORE NOAUDIT AFTER NOAUDIT BEFORE COMMENT AFTER COMMENT
BEFORE LOGOFF
AFTER LOGON
AFTER SUSPEND
After a SQL statement is suspended because of an out-of-space condition. The trigger should correct the condition so the statement can be resumed.
10
Developing Flashback Applications
This chapter discusses the following flashback topics:
Overview of Flashback Features Database Administration Tasks Before Using Flashback Features Using Flashback Query (SELECT ... AS OF) Using the DBMS_FLASHBACK Package Using ORA_ROWSCN Using Flashback Version Query Using Flashback Transaction Query Flashback Tips
See Also:
Oracle Database Backup and Recovery Advanced User's Guide and Oracle Database Administrator's Guide for information on flashback features designed for database administration tasks such as Oracle Flashback Database and Oracle Flashback Table Oracle Database SQL Reference for the syntax of SQL extensions for flashback features
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.
Flashback features use the Automatic Undo Management 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 executes an UPDATE statement to change a salary from 1000 to 1100, then Oracle would store the value 1000 in the undo data.
Undo data is persistent and survives a database shutdown. By using flashback features, you can employ undo data to query past data or recover from logical corruptions. Besides using it in flashback operations, Oracle Database uses undo data to perform the following actions:
Roll back active transactions Recover terminated transactions by using database or process recovery Provide read consistency for SQL queries
See Also:
Oracle Database Concepts for more information about flashback features and automatic undo management
Oracle Flashback Query You can use this feature to retrieve data for a time in the past that you specify using the AS OF clause of the SELECT statement.
Oracle Flashback Version Query You can use this feature to retrieve metadata and historical data for a specific time interval. For example, you can view all the rows of a table that ever existed during a given time interval. Metadata about the different versions of rows includes start and end time, type of change operation, and identity of the transaction that created the row version. You use the VERSIONS BETWEEN clause of the SELECT statement to create a Flashback Version Query.
Oracle Flashback Transaction Query You can use this feature to retrieve metadata and historical data for a given transaction or for all transactions within a given time interval. You can also obtain the SQL code to undo the changes to particular rows affected by a transaction. Typically, you use Flashback Transaction Query in conjunction with a Flashback Version Query that provides the transaction IDs for the rows of interest. To perform a Flashback Transaction Query, select from the FLASHBACK_TRANSACTION_QUERY view.
DBMS_FLASHBACK package You can use this feature to set the internal Oracle clock to a time in the past so that you can examine data current at that time.
DBMS_FLASHBACK package Flashback Query Flashback Version Query Flashback Transaction Query
Typically, you use the following flashback features only in database administration:
You can use this feature to recover a table to its state at a previous point in time. You can restore table data while the database is on line, undoing changes to only the specified table.
Oracle Flashback Drop You can use this feature to recover a dropped table. This reverses the effects of a DROP TABLE statement.
Oracle Flashback Database You can 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.
Flashback Database, Flashback Table, and Flashback Drop are primarily data recovery mechanisms and are therefore documented elsewhere. The other flashback features, while valuable in data recovery scenarios, are useful for application development. They are the focus of this chapter.
See Also:
Oracle Database Backup and Recovery Advanced User's Guide Oracle Database Administrator's Guide to learn about the Flashback Drop feature Oracle Database Administrator's Guide to learn about the Flashback Table feature Oracle Database Administrator's Guide to learn about Automatic Undo Management
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. Calculating the space requirements is usually performed by a database administrator. You can find the calculation formula in the Oracle Database Administrator's Guide. Enable Automatic Undo Management, as explained in Oracle Database Administrator's Guide. In particular, you must set the following database initialization parameters: UNDO_MANAGEMENT UNDO_TABLESPACE
Note that for an undo tablespace with a fixed size, Oracle Database automatically performs the following actions: Tunes the system to give the best possible undo retention for the undo tablespace.
For an automatically extensible undo tablespace, Oracle Database retains undo data longer than the longest query duration as well as 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.
Specify the RETENTION GUARANTEE clause for the undo tablespace to ensure that unexpired undo is not discarded. Setting UNDO_RETENTION is not, by itself, a strict guarantee. If the system is under space pressure, then Oracle can overwrite unexpired undo with freshly generated undo. Specifying RETENTION GUARANTEE prevents this behavior. Grant flashback privileges to users, roles, or applications that need to use flashback features as follows: For the DBMS_FLASHBACK package, grant the EXECUTE privilege on DBMS_FLASHBACK to provide access to the features in this package. For Flashback Query and Flashback Version Query, grant FLASHBACK and SELECT privileges on specific objects to be accessed during queries or grant the FLASHBACK ANY TABLE privilege to allow queries on all tables. For Flashback Transaction Query, grant the SELECT ANY TRANSACTION privilege. For Execution of undo SQL code, grant SELECT, UPDATE, DELETE, and INSERT privileges for specific tables, as appropriate, to permit execution of undo SQL code retrieved by a Flashback Transaction Query.
To use the Flashback Transaction Query feature in Oracle Database 10g, the database must be running with version 10.0 compatibility, and must have supplemental logging turned on with the following SQL statement: ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;
To enable flashback operations on specific LOB columns of a table, use the ALTER TABLE command 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 Backup and Recovery Advanced User's Guide and Oracle Database Administrator's Guide to learn about DBA tasks such as setting up automatic undo management and granting privileges Oracle Database Application Developer's Guide - Large Objects to learn about LOB storage and the RETENTION parameter
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 some time in the past. For example, you might 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 could verify the account balance of a certain day. Simplifying application design, by removing the need to store some kinds of temporal data. By using a Flashback Query, you can 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.
See Also:
Oracle Database SQL Reference for details on the syntax of the SELECT... AS OF statement
SELECT * FROM employees AS OF TIMESTAMP TO_TIMESTAMP('2004-04-04 09:30:00', 'YYYY-MM-DD HH:MI:SS') WHERE last_name = 'Chung';
The update in Example 102 restores Chung's information to the employees table:
Example 102 Reinserting a Row After a 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');
You can specify or omit the AS OF clause for each table and specify different times for different tables. Use an AS OF clause in a query to perform DDL operations (such as creating and truncating tables) or DML operations (such as inserting and deleting) in the same session as the query. To use the results of a 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. When choosing whether to use a timestamp or an SCN in Flashback Query, remember that Oracle Database uses SCNs internally and maps these to
timestamps at a granularity of 3 seconds. If a possible 3-second error (maximum) is important to a Flashback Query in your application, then use an SCN instead of a timestamp. Refer to "Flashback Tips General".
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, then 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 a Flashback Query with a CREATE TABLE AS SELECT or INSERT INTO TABLE SELECT statement. For example, the following 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)) -- SYSTIMESTAMP refers to the time zone of the database host environment MINUS SELECT * FROM employees);
Call DBMS_FLASHBACK.ENABLE_AT_TIME or DBMS_FLASHBACK.ENABLE_AT_SYSTEM_CHANGE_NUMBER to turn back the clock to a specified time in the past. Afterwards all queries retrieve data that was current at the specified time. Perform normal queries, that is, without any special flashback-feature syntax such as AS OF. The database is automatically queried at the specified past time. Perform only queries; do not try to perform DDL or DML operations. Call DBMS_FLASHBACK.DISABLE to return to the present. You must call DISABLE before calling ENABLE again for a different time. You cannot nest ENABLE /DISABLE pairs.
2.
3.
You can use a cursor to store the results of queries. To do this, open the cursor before calling DBMS_FLASHBACK.DISABLE. After storing the results and then calling DISABLE, you can do the following:
Perform INSERT or UPDATE operations to modify the current database state by using the stored results from the past.
Using ORA_ROWSCN
Compare current data with the past data. After calling 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 call DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER at any time to obtain the current System Change Number (SCN). Note that the current SCN is always returned; this takes no account of previous calls to DBMS_FLASHBACK.ENABLE*.
See Also:
Oracle Database PL/SQL Packages and Types Reference for details about the DBMS_FLASHBACK package Oracle Database Reference and Oracle Database Backup and Recovery Reference for information about SCNs
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, that is, the latest COMMIT operation for the row. For example:
SELECT ora_rowscn, last_name, salary FROM employees WHERE employee_id = 7788; ORA_ROWSCN ---------202553 NAME ---Fudd SALARY -----3000
The latest COMMIT operation for the row took place at approximately SCN 202553. You can use function SCN_TO_TIMESTAMP to convert an SCN, like ORA_ROWSCN, to the corresponding TIMESTAMP value. ORA_SCN is in fact a conservative upper bound of the latest commit time: the actual commit SCN can be somewhat earlier. ORA_SCN is more precise (closer to the actual commit SCN) for a row-dependent table (created using CREATE TABLE with the ROWDEPENDENCIES clause). Noteworthy uses of ORA_ROWSCN in application development include concurrency control and client cache invalidation. To see how you might use it in concurrency control, consider the following scenario. Your application examines a row of data, and records the corresponding ORA_ROWSCN as 202553. Later, the application needs to update the row, but only if its record of the data is still accurate. That is, this particular update operation depends, logically, on the row not having been changed. The operation is therefore made conditional on the ORA_ROWSCN being still 202553. Here is an equivalent interactive command:
UPDATE employees SET salary = salary + 100 WHERE employee_id = 7788 AND ora_rowscn = 202553; 0 rows updated.
The conditional update fails in this case, because the ORA_ROWSCN is no longer 202553. This means that some user or another application changed the row and performed a COMMIT more recently than the recorded ORA_ROWSCN. 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. Here is an interactive equivalent:
SQL> UPDATE employees SET salary = salary + 100 WHERE empno = 7788 AND ora_rowscn = 415639; 1 row updated. SQL> COMMIT; Commit complete. SQL> SELECT ora_rowscn, name, salary FROM employees WHERE empno = 7788; ORA_ROWSCN ---------465461 NAME ---Fudd SALARY -----3100
The SCN corresponding to the new COMMIT is 465461. Besides using ORA_ROWSCN in an UPDATE statement WHERE clause, you can use it in a DELETE statement WHERE clause or the AS OF clause of a Flashback Query.
See Also:
where start and end are expressions representing the start and end of the time interval to be queried, respectively. The interval is closed at both ends: the upper and lower limits specified (start and end) are both included in the time interval. The Flashback Version Query returns a table with a row for each version of the row that existed at any time during the time interval you specify. Each row in the table includes pseudocolumns of metadata about the row version, described in Table 101. This information can reveal when and how a particular change (perhaps erroneous) occurred to your database.
Table 101
Starting System Change Number (SCN) or TIMESTAMP when the row version was created. This identifies the time when the data first VERSIONS_STARTTIME took on the values reflected in the row version. You can use this to identify the past target time for a Flashback Table or Flashback Query operation. If this is NULL, then the row version was created before the lower time bound of the query BETWEEN clause. VERSIONS_ENDSCN VERSIONS_ENDTIME SCN or TIMESTAMP when the row version expired. This identifies the row expiration time. If this is NULL, then either the row version was still current at the time of the query or the row corresponds to a DELETE operation. Identifier of the transaction that created the row version.
VERSIONS_XID
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. Note: For user updates of an index key, a Flashback Version Query may 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, the following output indicates that the salary was 10243 from September 9, 2002, included, to November 25, 2003, not included.
VERSIONS_START_TIME ------------------09-SEP-2003 VERSIONS_END_TIME ----------------25-NOV-2003 SALARY -----10243
Pseudocolumn VERSIONS_XID provides a unique identifier for the transaction that put the data in that state. You can use this value in connection with a Flashback Transaction Query to locate metadata about this transaction in the FLASHBACK_TRANSACTION_QUERY view, including the SQL required to undo the row change and the user responsible for the change see "Using Flashback Transaction Query" on page 10-10.
See Also:
Oracle Database SQL Reference for information on the Flashback Version Query pseudocolumns and the syntax of the VERSIONS clause
As an example, the following 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 to undo the operation:
SELECT xid, operation, start_scn,commit_scn, logon_user, undo_sql FROM flashback_transaction_query WHERE xid = HEXTORAW('000200030000002D');
As another example, the following query uses a 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'));
At this point, emp and dept have one row each. In terms of row versions, each table has one version of one row. Next, suppose that an erroneous transaction deletes employee id 111 from table emp:
UPDATE emp SET salary = salary + 100 WHERE empno = 111; INSERT INTO dept VALUES (20, 'Finance'); DELETE FROM emp WHERE empno = 111; COMMIT;
Subsequently, a new transaction reinserts employee id 111 with a new employee name into the emp table.
INSERT INTO emp VALUES (111, 'Tom', 777); UPDATE emp SET salary = salary + 100 WHERE empno = 111; UPDATE emp SET salary = salary + 50 WHERE empno = 111; COMMIT;
At this point, the DBA detects the application error and needs to diagnose the problem. The DBA issues the following query to retrieve versions of the rows in the emp table that correspond to empno 111. The query uses Flashback Version Query pseudocolumns.
connect dba_name/password SELECT versions_xid XID, versions_startscn START_SCN, versions_endscn END_SCN, versions_operation OPERATION, empname, salary FROM hr.emp VERSIONS BETWEEN SCN MINVALUE AND MAXVALUE where empno = 111; XID ---------------0004000700000058 000200030000002D 000200030000002E 3 rows selected START_SCN END_SCN ---------- --------113855 113564 112670 113564 OPERATION ---------I D I EMPNAME ---------Tom Mike Mike SALARY ---------927 555 555
The results table reads chronologically, from bottom to top. The third row corresponds to the version of the row in emp that was originally inserted in the table when the table was created. The second row corresponds to the row in emp that was deleted by the erroneous transaction. The first row corresponds to the version of the row in emp that was reinserted with a new employee name. The DBA identifies transaction 000200030000002D as the erroneous transaction and issues the following Flashback Transaction Query to audit all changes made by this transaction:
SELECT xid, start_scn START, commit_scn COMMIT, operation OP, logon_user USER, undo_sql FROM flashback_transaction_query WHERE xid = HEXTORAW('000200030000002D');
XID START COMMIT OP USER UNDO_SQL ---------------- ---------- ------------------------------000200030000002D 195243 195244 DELETE HR insert into "HR"."EMP" ("EMPNO","EMPNAME","SALARY") values ('111','Mike','655'); 000200030000002D 195243 195244 INSERT where ROWID = 'AAAKD4AABAAAJ3BAAB'; HR delete from "HR"."DEPT"
000200030000002D 195243 195244 UPDATE HR update "HR"."EMP" set "SALARY" = '555' where ROWID = 'AAAKD2AABAAAJ29AAA'; 000200030000002D 4 rows selected 195243 113565 BEGIN HR
The rightmost column (undo_sql) contains the SQL code that will undo the corresponding change operation. The DBA can execute this code to undo the changes
10-11
Flashback Tips
made by that transaction. The USER column (logon_user) shows the user responsible for the transaction. A DBA might also be interested in knowing all changes made in a certain time window. In our scenario, the DBA performs the following query to view the details of all transactions that executed since the erroneous transaction identified earlier (including the erroneous transaction itself):
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'); XID ---------------0004000700000058 0004000700000058 0004000700000058 000200030000002D 000200030000002D 000200030000002D 6 rows selected START_SCN --------195245 195245 195245 195243 195243 195243 COMMIT_SCN ---------195246 195246 195246 195244 195244 195244 OPERATION --------UPDATE UPDATE INSERT DELETE INSERT UPDATE TABLE_NAME ---------EMP EMP EMP EMP DEPT EMP TABLE_OWNER ----------HR HR HR HR HR HR
Flashback Tips
The following tips and restrictions apply to using flashback features.
For better performance, generate statistics on all tables involved in a Flashback Query by using the DBMS_STATS package, and keep the statistics current. Flashback Query always uses the cost-based optimizer, which relies on these statistics. The performance of a query into the past depends on how much undo data must be accessed. For better performance, use queries to select small sets of past data using indexes, not to scan entire tables. If you must do a full table scan, consider adding 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 already 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 features essentially CPU bound. Use index structures for Flashback Version Query: the database keeps undo data for index changes as well as data changes. Performance of index lookup-based Flashback Version Query is an order of magnitude faster than the full table scans that are otherwise needed. In a Flashback Transaction Query, the type of the xid column is RAW(8). To take advantage of the index built on the xid column, use the HEXTORAW conversion function: HEXTORAW(xid). Flashback Query against a materialized view does not take advantage of query rewrite optimizations.
See Also:
Flashback Tips
Use the DBMS_FLASHBACK package or other flashback features? Use ENABLE/DISABLE calls to the DBMS_FLASHBACK package around SQL code that you do not control, or when you want to use the same past time for several consecutive queries. Use Flashback Query, Flashback Version Query, or Flashback Transaction Query for SQL that you write, for convenience. A 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. You can compute or retrieve a past time to use in a query by using a function return value as a timestamp or SCN argument. For example, you can perform date and time calculations by adding or subtracting an INTERVAL value to the value of the SYSTIMESTAMP function. You can query locally or remotely (Flashback Query, Flashback Version Query, or Flashback Transaction Query). for example here is a remote Flashback Query:
(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 is done using the current session settings, such as national language and character set, not the settings that were in effect at the time being queried. Some DDLs that alter the structure of a table, such as drop/modify column, move table, drop partition, and truncate table/partition, invalidate any existing undo data for the table. It is not possible to retrieve data from a point earlier than the time such DDLs were executed. Trying such a query results in error ORA-1466. This restriction does not apply to DDL operations that alter the storage attributes of a table, such as PCTFREE, INITRANS, and MAXTRANS. Use an SCN to query past data at a precise time. If you use a timestamp, the actual time queried might be up to 3 seconds earlier than the time you specify. Internally, Oracle Database uses SCNs; these are mapped to timestamps at a granularity of every 3 seconds. For example, assume that the SCN values 1000 and 1005 are mapped to the times 8:41 and 8:46 AM respectively. A query for a time between 8:41:00 and 8:45:59 AM is mapped to SCN 1000; a Flashback Query for 8:46 AM is mapped to SCN 1005. Due to this time-to-SCN mapping, if you specify a time that is slightly after a DDL operation (such as a table creation) the database might actually use an SCN that is just before the DDL operation. This can result in error ORA-1466.
You cannot retrieve past data from a V$ view in the data dictionary. Performing a query on such a view always returns the current data. You can, however, perform queries on past data in other views of the data dictionary, such as USER_TABLES.
10-13
Flashback Tips
11
Developing Applications with the PL/SQL Web Toolkit
Java is not the only language that can do network operations and produce dynamic Web content. PL/SQL has a number of features that you can use to make your database available on the Web and make back-office data accessible on the intranet. This chapter discusses the following topics:
Developing PL/SQL Web Applications: Overview Using the mod_plsql Gateway Generating HTML Output with PL/SQL Passing Parameters to a PL/SQL Web Application Performing Network Operations within PL/SQL Stored Procedures
11-1
Stored Procedure
4 2
Web Browser
3
PL/SQL Web Toolkit
5 1
Web Server
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. The HTTP server invokes a stored procedure 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 procedure. The stored procedure calls 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. The subprograms pass the dynamically generated page to the Web server. The Web server delivers the page to the client.
3.
4. 5.
Obtain information about an HTTP request Generate HTTP headers such as content-type and mime-type Set browser cookies Generate HTML pages
Commonly Used Packages in the PL/SQL Web Toolkit Description of Contents Function versions of the procedures 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 need to nest function calls. Procedures that generate HTML tags. For instance, the procedure htp.anchor generates the HTML anchor tag, <A>. Functions and procedures 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.
HTP OWA_CACHE
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. The authorize function used by cookies. 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_CUSTOM OWA_IMAGE
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 have been changed in the meantime by another user. OWA_PATTERN OWA_SEC OWA_TEXT OWA_UTIL Subprograms that perform string matching and string manipulation with regular expressions. Subprograms used by the PL/SQL gateway for authenticating requests. Subprograms used by package OWA_PATTERN for manipulating strings. You can also use them directly. The following 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 datatype.
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
11-3
See Also:
Oracle Application Server mod_plsql User's Guide to learn how to configure and use mod_plsql Oracle HTTP Server Administrator's Guide to obtain mod_plsql reference material
-----
-- 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 the text and tags together. Example 112 illustrates this technique.
Example 112 Displaying HTML Tags with HTP.PRINT
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 12, "Developing PL/SQL Server Pages" 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 do the following:
Simplify the processing involved in displaying tables Store persistent data (cookies) Work with CGI protocol internals
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 procedure 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 procedure. Typically, you would include separate links on your Web page for all the choices that the user might want.
Passing List and Dropdown List Parameters from an HTML Form Passing Radio Button and Checkbox Parameters from an HTML Form Passing Entry Field Parameters from an HTML Form Passing Hidden Parameters from an HTML Form Uploading a File from an HTML Form Submitting a Completed HTML Form Handling Missing Input from an HTML Form Maintaining State Information Between Web Pages
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
11-5
motion of selecting an item from the drop-down list, allowing them to make selections quickly as they gain experience. Example 113 shows a simple drop-down list.
Example 113 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>
dynamic HTML or Java, 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 once a length limit is reached. You might silently remove spaces and dashes from a credit card number if the stored procedure 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 procedures 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. For example, the following procedure accepts two strings as input. The first time it is called, the user sees a simple form prompting for the input values. When the user submits the information, the same procedure is called again to check if the input is correct. If the input is OK, the procedure processes it. If not, the procedure prompts for new input, filling in the original values for the user.
-- Store a name and associated zip code in the database. CREATE OR REPLACE PROCEDURE associate_name_with_zipcode ( name VARCHAR2 DEFAULT NULL, zip VARCHAR2 DEFAULT NULL ) AS booktitle VARCHAR2(256); BEGIN -- Both entry fields must contain a value. The zip code must be 6 characters. -- (In a real program you would perform more extensive checking.) IF name IS NOT NULL AND zip IS NOT NULL AND length(zip) = 6 THEN store_name_and_zipcode(name, zip); htp.print('<p>The person ' || name || ' has the zip code ' || zip || '.'); -- If the input was OK, we stop here and the user does not see the form again. RETURN; END IF; -- If some data was entered, but it is not correct, show the 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 re-enter the data. Fill in all fields, and use a 6-digit zip code.</b>'); END IF; -- If the user has not entered any data, or entered bad data, prompt for -- input values. -- Make the form call the same procedure to check the input values. htp.formOpen( 'scott.associate_name_with_zipcode', 'GET'); htp.print('<p>Enter your name:</td>'); htp.print('<td valign=center><input type=text name=name value="' || name ||
11-7
'">'); 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; / SHOW ERRORS;
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 procedures 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.
Use a DEFAULT clause in all parameter declarations, to prevent an exception when the stored procedure is called with a missing form parameter. You can set the default to zero for numeric values (when that makes sense), and use DEFAULT NULL when you want to check whether or not the user actually specifies a value. Before using an input parameter value that has a DEFAULT NULL declaration, check if it is null. Make the procedure 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 procedure again, directly from the results page. For example, you could include a link that calls the same stored procedure with an additional parameter, or display the original form with its values filled in as part of the output.
11-9
Sending E-Mail from PL/SQL Getting a Host Name or Address from PL/SQL Working with TCP/IP Connections from PL/SQL Retrieving the Contents of an HTTP URL from PL/SQL Working with Tables, Image Maps, Cookies, and CGI Variables from PL/SQL
Control the details of the HTTP session, including header lines, cookies, redirects, proxy servers, IDs and passwords for protected sites, and CGI parameters through the GET or POST methods. Speed up multiple accesses to the same Web site using HTTP 1.1 persistent connections. Construct and interpret URLs for use with UTL_HTTP through the ESCAPE and UNESCAPE functions in the UTL_URL package.
Typically, developers have used Java or Perl to perform these operations; this package lets you do them with PL/SQL.
CREATE OR REPLACE PROCEDURE show_url ( url IN VARCHAR2, username IN VARCHAR2 DEFAULT NULL, password IN VARCHAR2 DEFAULT 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 don't need the proxy host. utl_http.set_proxy('proxy.my-company.com', 'corp.my-company.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 ourselves. Some sites serve special pages for particular browsers. utl_http.set_header(req, 'User-Agent', 'Mozilla/4.0'); -- Specify a user ID and password for pages that require them. IF (username IS NOT NULL) THEN utl_http.set_authentication(req, username, password); END IF;
BEGIN -- Start receiving the HTML text. resp := utl_http.get_response(req); -- Show the status codes and reason phrase of the 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 the page is password protected, and we 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/password 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/password 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; -- The 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; -- Keep reading lines until no more 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; END;
/ SET serveroutput ON -- The following URLs illustrate the use of this procedure, -- but these pages do not actually exist. To test, substitute -- URLs from your own Web server. exec show_url('http://www.oracle.com/no-such-page.html') exec show_url('http://www.oracle.com/protected-page.html') exec show_url('http://www.oracle.com/protected-page.html', 'scott', 'tiger')
Working with Tables, Image Maps, Cookies, and CGI Variables from PL/SQL
Packages for all of these functions are supplied with Oracle8i and higher. You use these packages in combination with the mod_plsql plug-in of Oracle HTTP Server (OHS). You 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 combine other typical Web operations with a PL/SQL program. Documentation for these packages is not part of the database documentation library. The location of the documentation depends on the particular application server you are running. To get started with these packages, look at their procedure names and parameters using the SQL*Plus DESCRIBE command:
DESCRIBE HTP; DESCRIBE HTF; DESCRIBE OWA_UTIL;
12
Developing PL/SQL Server Pages
This section contains the following topics:
PL/SQL Server Pages: Overview Writing a PL/SQL Server Page Loading a PL/SQL Server Page into the Database Executing a PL/SQL Server Page Through a URL Examples of PL/SQL Server Pages Debugging PL/SQL Server Page Problems Putting PL/SQL Server Pages into Production
What Are PL/SQL Server Pages and Why Use Them? Prerequisites for Developing and Deploying PL/SQL Server Pages PSP and the HTP Package PSP and Other Scripting Solutions
<%@ 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; %> Developing PL/SQL Server Pages 12-1
<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 this script into an Oracle database with the loadpsp command-line utility. The following example loads this server page into the hr schema, replacing the show_employees procedure if it already exists:
loadpsp -replace -user hr/hr simple.psp
Browser users can execute 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 the following advantages:
For developers familiar with PL/SQL, the server pages are the easiest way to create professional Web pages that included database-generated content. You can develop Web pages as normal and then embed PL/SQL code in the HTML. PSP 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 PSP 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 machine.
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 an Oracle 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 "PL/SQL Web Toolkit" on page 11-2, the gateway makes use of the PL/SQL Web Toolkit.
See Also:
By writing an HTML page with embedded PL/SQL code and compiling it as a PL/SQL server page. You may call procedures from the PL/SQL Web Toolkit, but not to generate every line of HTML output. By writing a complete stored procedure that produces HTML by calling the HTP and OWA_* packages in the PL/SQL Web Toolkit. This technique is described in "Generating HTML Output with PL/SQL" on page 11-4.
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 if you want to include dynamic content or make it the front end of a database application, then use PSP. If you have a large body of PL/SQL code that produces formatted output, then you may find it more convenient to produce HTML tags by changing your print statements to call 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 PSP. If you use authoring tools that produce PL/SQL code, then it might be less convenient to use PSP.
Java server pages are loosely analogous to PSP pages; Java servlets are analogous to PL/SQL packages. PSP uses the same script tag syntax as JSP to make it easy to switch back and forth. PSP uses 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. In many cases, you can get the same results by using the corresponding PSP features.
In the simplest case, it is an HTML file. Compiling it as a PL/SQL server page produces a stored procedure that outputs exactly the same HTML file. In the most complex case, it is a PL/SQL procedure 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 PSP directives and declarations is usually not significant. It becomes significant only when another file is included. For ease of maintenance, it is recommended that you place the directives and declarations together near the beginning of the file. Table 121 lists the PSP elements and directs you to the section that discusses how to use them. The section "Quoting and Escaping Strings in a PSP Script" on page 12-11 describes how to quote strings that are used in various PSP elements.
Table 121
PSP Element <%@ page ... %>
PSP Elements
Name Page Directive Specifies . . . Characteristics of the PL/SQL server page. Section "Specifying Basic Server Page Characteristics" on page 12-5
<%@ parameter ... %> Parameter Directive The name, and optionally the type and "Accepting User Input" on page 12-7 default, for each parameter expected by the PSP stored procedure. <%@ plsql ... %> <%@ include ... %> <%! ... %> Procedure Directive The name of the stored procedure produced by the PSP file. Include Directive Declaration Block The name of a file to be included at a specific point in the PSP file. The declaration for a set of PL/SQL variables that are visible throughout the page, not just within the next BEGIN/END block. A set of PL/SQL statements to be executed when the procedure is run. "Naming the PL/SQL Stored Procedure" on page 12-8 "Including the Contents of Other Files" on page 12-8 "Declaring Global Variables in a PSP Script" on page 12-9
A single PL/SQL expression, such as a "Substituting an Expression Result in string, arithmetic expression, function a PSP Script" on page 12-10 call, or combination of these. A comment in a PSP script. "Including Comments in a PSP Script" on page 12-11
Comment
Note:
If you are familiar with dynamic HTML and want to start coding right away, you can jump forward to"Examples of PL/SQL Server Pages" on page 12-15.
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 exactly the same name in both the errorPage directive and in the loadpsp command, including any relative path name such as ../include/.
The following code shows the syntax of the page directive (note that the attribute names contentType and errorPage are case-sensitive):
<%@ page [language="PL/SQL"] [contentType="content type string"] charset="encoding" [errorPage="file.psp"] %>
This directive is for compatibility with other scripting environments. Example 121 shows an example of a simple PL/SQL server page that includes the language directive.
Returning HTML Returning XML, Text, or Other Document Types Returning Pages Containing Different Character Sets
Returning HTML The PL/SQL parts of a PL/SQL server page are enclosed within special delimiters. All other content is passed along verbatimincluding any whitespaceto the browser. To display text or HTML tags, write it as you would a typical Web page. You do not need to call any output functions. As illustration, the server page in Example 121 returns the HTML page shown in Example 122, except that it includes the table rows for the queried employees.
Example 122 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 the following code fragment from Example 121:
<% 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, or 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 the following directive:
<%@ page contentType="MIMEtype" %>
The attribute name is case-sensitive, so be sure to capitalize it as contentType. Insert text/html, text/xml, text/plain, image/jpeg, or some other MIME type that the browser or other client program recognizes. Users may have to configure their browsers to recognize some MIME types. The following shows an example of a directive for an Excel spreadsheet:
<%@ 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 the following 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 may 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.
HTML syntax errors. Any errors in HTML markup are handled by the browser. The loadpsp utility does not check for them. PL/SQL syntax errors. If you make a syntax error in the PL/SQL code, the loadpsp utility stops and displays the line number, column number, and a brief message. You must fix the error before continuing. Note that any previous version of the stored procedure 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, then load the final stored procedure 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 bring up a special PL/SQL server page. Use the errorPage attribute (note that 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 procedure does not receive any parameters, so to determine the cause of the error, it can call 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 procedure.
The following example shows a directive that specifies errors.psp as the page to run when errors are encountered:
<%@ page language="PL/SQL" contentType="text/html" errorPage="errors.psp" %>
Example 129 shows an example of a script that includes the parameter directive. By default, parameters are of type VARCHAR2. To use a different type, include a type="PL/SQL type" attribute within the directive, as in the following example:
<%@ plsql parameter="p_employee_id" type="NUMBER" %>
To set a default value, so that the parameter becomes optional, include a default="expression" attribute in the directive. The values for this attribute are substituted directly into a PL/SQL statement, so any strings must be single-quoted, and you can use special values such as null, as in the following example:
<%@ 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 calling your page as the action of an HTML form. Your page receives the input as parameters to a PL/SQL stored procedure. For example, assume that you change the first few lines of Example 121 to include a parameter directive as follows, and then load it into the database:
<%@ page language="PL/SQL" %> <%@ page contentType="text/html" %>
<%@ plsql parameter="p_employee_id" default="null" type="NUMBER" %> <%@ plsql procedure="show_employees" %> <%! CURSOR emp_cursor IS SELECT last_name, first_name FROM hr.employees WHERE employee_id = p_employee_id ORDER BY last_name; %>
If the PL/SQL gateway were configured so that you could execute procedures by calling http://www.host.com/pls/proc_name, where proc_name is the name of a procedure, then you could pass 200 for parameter p_employee_id as follows:
http://www.host.com/pls/show_employees?p_employee_id=200
Example 121 includes the following directive, which gives the stored procedure the name show_employees:
<%@ plsql procedure="show_employees" %>
Thus, you could name the file empnames.psp or anything else that ends with *.psp, but the procedure is created as show_employees. Note that it is the name of the procedure, not the name of the PSP script, that you include in the URL.
The included file must have an extension other than .psp. You must specify exactly 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 more than one place in a page. The following example includes an HTML footer:
You can use any names and extensions for the included files. For example, you could 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.
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 121 includes the following 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" on page 12-9. These declarations are only visible to the following BEGIN/END block.
Note:
To make things easier to maintain, keep all your directives and declarations together near the beginning of a PL/SQL server page.
This element typically spans multiple lines, with individual PL/SQL statements ended by semicolons. The statements can include complete blocks, as in the following example, which calls 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. The following code from Example 1210 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 packaged procedures, constants, and types from PSP scripts, the PSP scripts can only produce standalone procedures, not packages.
Note that you do not need to end the PL/SQL expression with a semicolon. Example 121 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 the following 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 whitespace and requires that you quote literal strings. Note that you can use concatenation by using the twin pipe symbol (||) as you would in PL/SQL. The following directive shows an example of concatenation:
<%= 'The employee last name is ' || emp_record.last_name %>
You can also nest single-quoted strings inside single quotes. In this case, you must escape the nested single quotes 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 %>
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 the following example:
-- Comment in PL/SQL code
Example 123 shows a fragment of a PSP file with the three types of comments.
Example 123
<p>Today we introduce our new model XP-10. <%-This is the project with code name "Secret Project". Users viewing the HTML page will 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 will 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 will 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; %>
To create procedures with CREATE OR REPLACE syntax, use the -replace flag. When you load a PSP file, the loader performs the following actions:
1. 2.
Logs on to the database with the specified user name, password, and net service name Creates the stored procedures in the user schema
Include the names of all the include files before the names of the PL/SQL server pages. Also include the name of the file specified in the errorPage attribute of the page directive. These filenames on the loadpsp command line must match exactly the names specified within the PSP include and page directives, including any relative path name such as ../include/. Example 124 shows a sample PSP load command.
Example 124 Loading a PSP
The stored procedure is created in the database orcl. The database is accessed as user hr with password hr, 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.
If you log on to the database as user hr, then you can execute the following query in SQL*Plus to view the source code of the PSP:
SET HEADING OFF SELECT TEXT FROM USER_SOURCE WHERE NAME = 'SHOW_EMPLOYEES' ORDER BY LINE;
Sample output is shown in Example 125. Note that the code generated by loadpsp is different from the code in the source file. The loadpsp utility has added extra code, mainly calls to the HTP package, to the PSP code. The HTP package generates the HTML tags for the web page.
Example 125 Output from Query of USER_SOURCE AS
PROCEDURE show_employees
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(' <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;
For example, the following URL includes a p_lname and p_fname parameter:
http://www.host.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, the following URL specifies a procedure name but does not pass parameters:
http://www.host.com/pls/show_employees
The METHOD=GET format is more convenient for debugging and allows visitors to pass exactly 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 should 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 called this way.
Setup for PL/SQL Server Pages Examples Printing the Sample Table with a Loop Allowing a User Selection Using an HTML Form to Call a PL/SQL Server Page
Youn have set up mod_plsql as described in "Using the mod_plsql Gateway" on page 11-3. You have created a DAD for static authentication of the oe user. You can access PL/SQL stored procedures created in the oe schema through the following URL, where proc_name is the name of a stored procedure: http://www.host.com/pls/proc_name
For debugging purposes, you can display the complete contents of an SQL table. You can do this with a single call to OWA_UTIL.TABLEPRINT as illustrated in Example 126. In subsequent iterations, we use other techniques to gain more control over the presentation.
Example 126
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>
<%@ 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 128 shows a more sophisticated variation of Example 127 in which formatting is added to the HTML to improve the presentation.
Example 128 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>
Making it accept a minimum price, and present only the items that are more expensive. (Your customers' buying criteria may vary.) Setting the default minimum price to 100 units of the appropriate currency. Later, we see how to allow the user to pick a minimum price.
show_product_partial.psp
Example 129
<%@ 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 129 into the database, you can pass a parameter to the show_product_partial procedure through a URL. The following example specifies a minimum price of 250:
http://www.host.com/pls/show_product_partial?p_minprice=250
This technique of filtering results is fine for some applications, such as search results, in which users might worry about being overwhelmed by choices. But in a retail situation, you might want to use the alternative technique illustrated in Example 1210 so that customers can still choose to purchase other items. Note the following features of this example:
Instead of filtering the results through a WHERE clause, we can retrieve the entire result set and then take different actions for different returned rows.
We can change the HTML to highlight the output that meets their criteria. In this case, we use the background color for an HTML table row. We could also insert a special icon, increase the font size, or use some other technique to call attention to the most important rows. We can present the results in an HTML table.
Example 1210 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>
<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>
</A> <BR> Price = <BIG><%= item.list_price %></BIG><BR> <% END LOOP; %> </UL> </BODY> </HTML>
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. Make sure you use semicolons to terminate lines where required. If a value must be quoted, quote it. You might need to enclose a single-quoted value (needed by PL/SQL) inside double quotes (needed by PSP). 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 may get an error that the file is not found. In this case, note the following: Make 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 new 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 new file name. When you get one file-not-found error, make sure to request the latest version of the page the next time. The error page might be cached by the browser. You may 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. Note the following tips: To determine exactly what is being passed to your page, use METHOD=GET in the calling form so that the parameters are visible in the URL. Make sure that the form or CGI program that calls 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. Make sure 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. Make sure 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 calling 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 will need to locate the error through exception handlers and by printing debug output.
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. Generally, if you set one color (such as the foreground text color), you should 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 may 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 routine can detect incorrect data and prompt the user to correct it before they press the Submit button and make a call to 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. Note the following 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 should 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.
13
Developing Applications with Database Change Notification
This section contains the following topics:
What Is Database Change Notification? Using Database Change Notification in the Middle Tier Registering Queries for Database Change Notification Querying Change Notification Registrations Interpreting a Database Change Notification Configuring Database Change Notification: Scenario Best Practices Troubleshooting
Names of the modified objects. For example, the notification can specify that the hr.employees table was changed.
13-1
The type of change. For example, the message specifies whether the change was caused by an INSERT, UPDATE, DELETE, ALTER TABLE, or DROP TABLE. The ROWIDs of the changed rows and the type of DML that changed them. Global events such as STARTUP and SHUTDOWN (consistent only). In a Real Applications Cluster, the database delivers a notification when the first instance on the database starts or the last instance shuts down.
The notification contains only metadata about the changed rows or objects rather than the changed data itself. For example, the database does not notify the client that a monthly salary increased from 5000 to 6000. To obtain more recent values for the changed objects or rows, the client must query the database based on the information contained in the notification. Database Change Notification is useful for an application that caches query result sets on mostly read-only objects in the mid-tier to avoid network round trips to the database. Such an application can create a registration on the queries it is interested in caching using the change notification service. On changes to objects referenced inside those queries, the database publishes a change notification when the underlying transaction commits. In response to the notification, the application can refresh its cache by re-executing the queries. For example, the users of a Web forum application may not need to view new content as soon as it is inserted into the back-end database. Such an application is intrinsically tolerant of slightly out-of-date data, and hence can benefit from caching in the mid-tier. Database change notification is of help in this scenario in keeping the cache updated with the back-end database.
Oracle Database
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 out of date or "stale" when a transaction modifies the data and commits, thereby putting the application at risk of accessing incorrect results. If the application uses Database Change Notification, then Oracle 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 132 illustrates the process by which middle-tier Web clients can receive change and process notifications.
13-3
1 2
Data Dictionary Registration through OCI or PL/SQL
Oracle Database
User Objects
8 4 9
Client Application Invalidation Queue JOBQ Process user DML
5 7
Client notification
6
PL/SQL
In this example, let's assume that the application has cached the result set of a query on HR.EMPLOYEES. The developer creates a registration for the query on HR.EMPLOYEES using the Change Notification PL/SQL Interface. In addition, he creates a stored PL/SQL procedure to process notifications and supplies the server-side PL/SQL procedure as the notification handler. The database populates the registration information in the data dictionary. A user modifies one of the registered objects with DML statements and commits the transaction. For example, a user updates a row in the hr.employees table on the back-end database. The data for hr.employees cached in the middle tier is now stale. Oracle Database adds a message that describes the change to an internal queue. A JOBQ background process is notified of a new change notification message. The JOBQ process executes 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 callback procedure determines how the notification is handled. Inside the server-side PL/SQL procedure, the developer can implement logic to notify the mid-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. The client application in the middle tier queries the back-end database to retrieve the data in the changed row. The client application updates the cache with the new data.
2. 3.
4. 5. 6.
7.
8. 9.
The above steps are applicable to registrations created through PL/SQL. In the case of registrations created via the OCI interface, the application uses the OCISubscriptionRegister interface to create a registration and specifies a client side C callback as the notification handler. After registration, an event thread is spawned on the client side program in the process. When a transaction changes any of the registered objects and commits, the EMON process of the RDBMS sends the notification to the event thread. The C callback specified by the application is then executed in the context of the event thread.
Note:
Privileges What Is a Database Change Registration? Supported Query Types Registration Properties Drop Table Interfaces for Database Change Registration Creating a PL/SQL Stored Procedure as the Change Notification Recipient Registering Queries for Change Notification Through PL/SQL
Privileges
In order to create a registration for change notification, the user is required to have the CHANGE NOTIFICATION system privilege. In addition the user is required to have SELECT privileges on all objects to be registered. Note that if the SELECT privilege on an object was granted at the time of registration creation but lost subsequently (due to a revoke), then the registration will be purged and a notification to that effect will be published.
Create the notification recipient for the queries that you want to register. The recipient can be one of the following:
13-5
PL/SQL stored procedure, as described in "Creating a PL/SQL Stored Procedure as the Change Notification Recipient" on page 13-8 OCI callback function, as described in Oracle Call Interface Programmer's Guide
2.
Create an query registration for a specified notification recipient, as described in "Registering Queries for Change Notification Through PL/SQL" on page 13-9. You can perform this registration by executing SQL queries. After the SQL execution the registration is complete.
Note:
You must be connected as a non-SYS user and should NOT be in the middle of an uncommitted transaction in order to be able to create a registration. The dml_locks init.ora parameter must have a nonzero value in order to be able to successfully create registrations and receive notifications. The default value of dml_locks is nonzero, therefore this requirement is automatically fulfilled if the application does not configure the dml_locks parameter explicitly.
Change Notification Registrations are persistent by default and survive until the application explicitly unregisters them. After the registration has been successfully created, the Oracle Database notifies client applications in response to any changes to objects referred to in the registered queries, when the underlying transaction commits. Notifications are generated as a result of DML operations like INSERT, UPDATE, and DELETE (on transaction commit) and DDL operations like ALTER and DROP. The notification includes information on the names of the objects changed, the Transaction-Id of the transaction that made the change and the TYPE of operation (INSERT, UPDATE or DELETE).
Note:
If multiple registered objects were modified in a single transaction, then the application will receive one notification for every modified object when the transaction commits.
Queries on fixed tables or fixed views. Queries with dblinks inside them Queries over materialized views
Registration Properties
Oracle Database supports the following options for an object registration:
1.
2. 3. 4.
Timeout option: Specification of a registration expiration after a time interval. ROWIDs option: ROWIDs of changed rows are part of the notification ROWID option. Reliable Notification option: By default, notifications are generated in shared memory. If this option is chosen, notifications are generated in a persistent database queue. The notifications are enqueued atomically with the transaction that changes a registered object. Since the notifications are persistent in the database, if an instance crashes after generating a notification, they can be delivered when it restarts subsequently OR by a surviving instance of the cluster if running RAC. (Note: there is a trade-off involved here between performance of notifications and reliability. Since there are CPU and I/O costs when generating reliable notifications, it is recommended to choose the default in memory option if better notification performance is desired). Operations filter: Ability to be notified of PARTICULAR operations (for example notifications only for INSERT AND UPDATE). Transaction Lag: Specification of a count between successive notifications.
5. 6.
If the ROWID option is chosen, then ROWIDs of changed rows are published as part of the notification. The ROWIDs are published in the external string format. From the ROWID information in the notification, the application should be subsequently able to retrieve the contents of the changed rows by performing a query of the form "SELECT * from table_name_from_notification where ROWID = rowid_from_notification". The length of the ROWID is 18 character bytes in the case of regular heap tables. In the case of Index Organized Tables (IOTs), the length of the ROWID depends on the size of the primary key and therefore could be larger than 18 bytes. The ROWID notifications are hierarchically summarized. If enough memory is not available on the server side to hold ROWIDs, then the notification might be rolled up into a FULL-TABLE-NOTIFICATION (a special flag in the notification descriptor is reserved for this purpose). When such a notification is received, the application must conservatively assume that the entire table (that is, all rows) may have been modified. ROWIDs are not part of such a notification. ROWIDs may be rolled-up if the total shared memory consumption due to ROWIDs is too large (exceeds 1% of the dynamic shared pool size), OR if too many rows were modified in a single registered object within a transaction (more than 80 approximately) OR if the total length of the logical ROWIDs of modified rows for an IOT is too large (exceeds. 1800 bytes approximately.).
Drop Table
When a table is dropped, a DROP NOTIFICATION is published. Any registrations on the dropped table will implicitly remove interest from that object (since it does not exist anymore). If those registrations have interest in other objects as well, then the registrations will continue to exist and DML transactions on those other objects will continue to result in notifications on commit. Even if the dropped table is the only object of interest for a particular registration, we still preserve the registration. The user that created that registration can use the registration to add more objects/queries subsequently. A registration is based on the version and definition of an object at the time the query was registered. If an object was dropped, registrations on the object will lose interest on the object forever. Subsequently, even if a different object was created with a matching name and in the same schema, then the newly created object is a new object for the purposes of existing Database Change Notification Registrations, that is, any changes to this newly created object (with the matching schema/name) will not result in notifications for those registrations that existed on the prior version of the object.
13-7
The JOBQ process passes the CHNF$_DESC object (notification descriptor), whose attributes describe the details of the change, to the callback procedure. For example, the object contains the transaction ID, the type of change that occurred, the tables that were modified, and so forth. The callback procedure can then send this data to a mid-tier client application for further processing.
Note:
The JOB_QUEUE_PROCESSES initialization parameter specifies the maximum number of processes that can be created for the execution of jobs. You must set it to a nonzero value to receive PL/SQL notifications because the specified callback procedure is executed inside a job queue process.
See Also:
"Interpreting a Database Change Notification" on page 13-13 for an explanation of the SYS.CHNF$_DESC type "Creating a PL/SQL Callback Procedure" on page 13-15 for an example of PL/SQL callback procedure
Create a CHNF$_REG_INFO object that specifies the name of the callback procedure and other metadata concerning the notification. Create or update a query registration by executing a program unit in the DBMS_CHANGE_NOTIFICATION package and then execute the queries that you want to register.
QOSFLAGS
QOS_RELIABLE, which specifies that notifications persist in the database and survive instance failure. If an instance fails in a Real Applications Cluster, then surviving instances can deliver any queued notification messages. By default, the database buffers change notification messages in shared memory (that is, the messages are not recorded to persistent storage) for better performance. QOS_DEREG_NFY, which specifies that the database should unregister the registration on the first notification. QOS_ROWIDS, which specifies that the notification should include information about the modified ROWIDs.
It is possible to specify a combination of the above options using bitwise OR for example, (dbms_change_notification.QOS_RELIABLE + dbms_change_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 unregisters it. Note: You can combine the TIMEOUT option with the QOS_DEREG_NFY option in the QOSFLAGS attribute.
13-9
OPERATIONS_FILTER Filters messages based on types of SQL statement. You can specify the following constants in the DBMS_CHANGE_NOTIFICATION package:
ALL_OPERATIONS notifies on all changes INSERTOP notifies on inserts UPDATEOP notifies on updates DELETEOP notifies on deletes
You can specify a combination of operations with a bitwise OR. For example, you can perform addition as follows: DBMS_CHANGE_NOTIFICATION.INSERTOP + DBMS_CHANGE_NOTIFICATION.DELETEOP. TRANSACTION_LAG 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. Oracle Database tracks intervening changes at an object granularity and bundles the changes along with the notification. Thus, the client does not lose intervening changes. Note1: Most applications that need to be notified of changes to an object on transaction commit without further deferral would be expected to chose 0 transaction lag. A non-zero transaction lag is useful only if an application wishes to implement some 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 periodicity in time. Otherwise, notifications maybe deferred indefinitely till the lag is satisfied. Note2: 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.
Suppose that you want to invoke the procedure hr.dcn_callback whenever a registered object changes. In Example 131, you create a CHNF$_REG_INFO object that specifies that hr.dcn_callback should receive change notifications. Note that to create the object you must have EXECUTE privileges on the DBMS_CHANGE_NOTIFICATION package.
Example 131 Creating a CHNF$_REG_INFO Object
DECLARE v_cn_addr SYS.CHNF$_REG_INFO; BEGIN -- create the object v_cn_addr := SYS.CHNF$_REG_INFO ( 'hr.dcn_callback', -- name of PL/SQL callback procedure DBMS_CHANGE_NOTIFICATION.QOS_ROWIDS, -- include rowids of modified objects 0, -- registration persists until unregistered 0, -- notify on all types of DML 0 -- notify immediately (no transaction lag) ); -- ... register objects ... END; /
For an example of an object registration, suppose that the client requires notification whenever a row changes in the hr.employees table. Example 132 shows an anonymous PL/SQL block that registers this table with the hr.dcn_callback procedure. Note that you must have been granted the CHANGE NOTIFICATION privilege to execute this block.
Example 132 Registering the Employees Table for Change Notifications SYS.CHNF$_REG_INFO; NUMBER; hr.employees.manager_id%TYPE;
13-11
v_cn_recip := SYS.CHNF$_REG_INFO('hr.dcn_callback', DBMS_CHANGE_NOTIFICATION.QOS_ROWIDS, 0, 0, 0); -- begin the registration boundary v_regid := DBMS_CHANGE_NOTIFICATION.NEW_REG_START(v_cn_recip); SELECT employee_id INTO v_employee_id FROM hr.employees -- register the employees object WHERE ROWNUM < 2; -- write the query so that it returns a single row -- end the registration boundary DBMS_CHANGE_NOTIFICATION.REG_END; DBMS_OUTPUT.PUT_LINE('the registration id for this query is '||v_regid); END; /
In Example 132, the program registers the object itself, that is, the hr.employees table. The WHERE clause restricts the query to the first employee in the result set to avoid generating an error because the fetch returns multiple rows. The DBMS_CHANGE_NOTIFICATION package registers the object itself, which means that any change to the tableregardless of whether the change is to the row returned by the registered querygenerates a notification.
See Also: "Configuring Database Change Notification: Scenario" on page 13-14
DECLARE v_department_id hr.departments.department_id%TYPE; BEGIN -- begin registration boundary DBMS_CHANGE_NOTIFICATION.ENABLE_REG(reg_id); SELECT department_id INTO v_department_id FROM hr.departments WHERE ROWNUM < 2; -- register this query -- end registration boundary DBMS_CHANGE_NOTIFICATION.REG_END; END; /
DBA_CHANGE_NOTIFICATION_REGS USER_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, you can do the following from SQL*Plus:
13-12 Oracle Database Application Developers Guide - Fundamentals
See Also:
EVENT_NONE EVENT_STARTUP (Instance startup) EVENT_SHUTDOWN (Instance shutdown - last instance shutdown in the case of RAC) EVENT_SHUTDOWN_ANY (Any instance shutdown in the case of RAC) EVENT_DEREG (Registration has been removed) EVENT_OBJCHANGE (Change to a registered table)
NUMTABLES
TABLE_DESC_ARRAY A varray of table change descriptors of type CHNF$_TDESC, which is described in Table 134. Each table descriptor corresponds to a table that was modified.
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 NUMROWS
ROW_DESC_ARRAY A varray of row descriptors of type CHNF$_RDESC, which is described in Table 135. If ALL_ROWS was set in the opflags, then the desc_array member is NULL.
1.
Implement a mid-tier HTTP listener that listens for notifications and updates the mid-tier cache in response to a notification of a change to the hr.employees and hr.departments tables Create a server-side PL/SQL stored procedure to process the change notifications, as described in "Creating a PL/SQL Callback Procedure" on page 13-15 Register the hr.employees and hr.departments tables, as described in "Registering the Query" on page 13-16
2. 3.
After you complete these steps, the server-side PL/SQL procedure defined in step 2 executes in response to changes to hr.employees or hr.departments. The callback procedure notifies the Web application of the tables changed. In response to the notification, the application refreshes the cache by querying the back-end database.
You may want to create database tables to hold the record of notification events received:
connect hr/hr; Rem Create a table to record notification events CREATE table nfevents(regid number, event_type number); Rem Create a table to record changes to registered tables create table nftablechanges( regid number, table_name varchar2(100), table_operation number); Rem Create a table to record rowids of changed rows. create table nfrowchanges( regid number, table_name varchar2(100), row_id varchar2(2000));
CREATE OR REPLACE PROCEDURE chnf_callback(ntfnds IN SYS.CHNF$_DESC) IS regid NUMBER; tbname VARCHAR2(60); event_type NUMBER; numtables NUMBER; operation_type NUMBER; numrows NUMBER; row_id VARCHAR2(2000); BEGIN regid := ntfnds.registration_id;
13-15
numtables := ntfnds.numtables; event_type := ntfnds.event_type; insert into nfevents values(regid, event_type); IF (event_type = DBMS_CHANGE_NOTIFICATION.EVENT_OBJCHANGE) THEN FOR i IN 1..numtables LOOP tbname := ntfnds.table_desc_array(i).table_name; operation_type := ntfnds.table_desc_array(I). Opflags; insert into nftablechanges values(regid, tbname, operation_type); /* Send the table name and operation_type to client side listener using UTL_HTTP */ /* If interested in the rowids, obtain them as follows */ IF (bitand(operation_type, dbms_change_notification.ALL_ROWS) = 0) THEN numrows := ntfnds.table_desc_array(i).numrows; else numrows :=0; /* ROWID INFO NOT AVAILABLE */ END IF; /* The body of the loop is not executed when numrows is ZERO */ FOR j IN 1..numrows LOOP Row_id := ntfnds.table_desc_array(i).row_desc_array(j).row_id; insert into nfrowchanges values(regid, tbname, Row_id); /* optionally Send out row_ids to client side listener using UTL_HTTP */ END LOOP; END LOOP; END IF; commit; END; /
CREATE OR REPLACE PROCEDURE hr.table_reg IS v_regds SYS.CHNF$_REG_INFO; v_regid NUMBER; v_employee_id NUMBER; v_department_id NUMBER; BEGIN v_regds := SYS.CHNF$_REG_INFO ('hr.chnf_callback', DBMS_CHANGE_NOTIFICATION.QOS_ROWIDS, 0, 0, 0); v_regid := DBMS_CHANGE_NOTIFICATION.NEW_REG_START(v_regds); SELECT employee_id INTO v_employee_id FROM hr.employees -- register employees object WHERE ROWNUM < 2; -- return a single row to avoid multiple fetch error
Best Practices
SELECT department_id INTO v_department_id FROM hr.departments -- register departments object WHERE ROWNUM < 2; -- return a single row to avoid multiple fetch error DBMS_CHANGE_NOTIFICATION.REG_END; END; / EXEC hr.table_reg
You can view the newly created registration by issuing the following query:
SQL> select regid, table_name from user_change_notification_regs; REGID TABLE_NAME ---------- ------------------------------------------------------------16 HR.EMPLOYEES 16 HR.DEPARTMENTS
Once the registration is created as shown above, the server side PL/SQL procedure chnf_callback, as described above, is executed in response to any committed changes to the HR.EMPLOYEES or HR.DEPARTMENTS tables. As an example, let us assume that the following update is performed on the employees table:
UPDATE employees SET salary=salary*1.05 WHERE employee_id=203; COMMIT;
Once the notification is processed, you will find rows which might look like the following in the nfevents, nftablechanges, and nfrowchanges tables:
SQL> select * from nfevents; REGID EVENT_TYPE ---------- ---------20045 6 SQL> select * from nftablechanges; REGID TABLE_NAME TABLE_OPERATION ------------------------------------------20045 HR.EMPLOYEES 4 SQL> select * from nfrowchanges; REGID TABLE_NAME ROW_ID --------------------------------------------20045 HR.EMPLOYEES AAAKB/AABAAAJ8zAAF
Best Practices
For best performance of change notification, the following guidelines are presented. Registered objects are few and mostly read-only and that modifications to those objects are the exception rather than the rule. If the object is extremely volatile, then it will cause a large number of invalidation notifications to be sent, and potentially a lot of storage in the invalidation queue on the server. If there are frequent and a large number of notifications, it can slow down OLTP throughput due to the overhead of generating the notifications. It is also a good idea to keep the number of duplicate registrations on any given object low (ideally one) in order to avoid the same notification message being replicated to multiple recipients.
13-17
Troubleshooting
Troubleshooting
If you have created a registration and seem to not receive notifications or you are unable to create a registration, the following is a list of things to check for.
1.
Is the job_queue_processes parameter set to a nonzero value? This parameter needs to be configured to a nonzero value in order to receive PL/SQL notifications via the handler. Are the registrations being created as a NON-SYS user? If you are attempting DML changes on the registered object, are you committing the transaction? Note that the notifications are transactional and will be generated when the transaction commits. To check that the registrations on the objects have been successfully created in the database, you can query from the USER_CHANGE_NOTIFICATION_REGS or DBA_CHANGE_NOTIFICATION_REGS views. For example, to view all registrations and the registered objects for the current user, you can issue the following select:
SELECT regid, table_name FROM user_change_notification_regs;
2. 3.
4.
5.
It maybe possible that there are run-time errors during the execution of the PL/SQL callback due to implementation errors in the callback. If so, they would be logged to the trace file of the JOBQ process that attempts to execute the procedure. The trace file would be usually named <ORACLE_SID>_j*_<PID>.trc. For example, if the ORACLE_SID is 'dbs1' and the process id of the JOBQ process is 12483, the trace file might be named 'dbs1_j000_12483.trc'. If there are run-time errors, then it will be reported to the JOBQ trace file. For example, let's say a registration is created with 'chnf_callback' as the notification handler and registration id 100. Let's say the 'chnf_callback' stored procedure was not DEFINED in the database. Then the JOBQ trace file might contain a message of the form:
*************************************************************************** Runtime 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 ****************************************************************************
6.
If you are running into errors during the execution of the callback, consider creating a very simple version of the callback as shown below to verify that you are actually receiving notifications. The callback can be gradually evolved to add more application logic. For example, if the user is HR then you might consider creating a very simple version of the notification handler as follows:
Rem create a table in the HR schema to hold a count of number of notifications received. Create table nfcount(cnt number); Insert into nfcount values(0); Commit;
Troubleshooting
CREATE OR REPLACE PROCEDURE chnf_callback (ntfnds IN SYS.CHNF$_DESC) IS BEGIN update nfcount set cnt = cnt+1; commit; END; /
The simple procedure created increments the count column of a table and commits. To verify that notifications are being published, you can query from the table nfcount to see if the cnt column is indeed going up when a change is made to a registered object and the transaction committed.
7.
There maybe a time lag between the commit of a transaction and the notification received by the end user.
13-19
Troubleshooting
Part III
Advanced Topics for Application Developers
This part deals with application development scenarios that are either used by only a small minority of developers or of general interest but involve sophisticated technologies. This part contains:
Chapter 14, "Calling External Procedures" Chapter 15, "Developing Applications with Oracle XA" Chapter 16, "Developing Applications on the Publish-Subscribe Model"
14
Calling External Procedures
In situations where a particular language does not provide the features you need, or when you want to reuse existing code written in another language, you can use code written in some other language by calling external procedures. This chapter discusses the following topics:
Overview of Multi-Language Programs What Is an External Procedure? Overview of The Call Specification for External Procedures Loading External Procedures Publishing External Procedures Publishing Java Class Methods Publishing External C Procedures Locations of Call Specifications Passing Parameters to External C Procedures with Call Specifications Executing External Procedures with the CALL Statement Handling Errors and Exceptions in Multi-Language Programs Using Service Procedures with External C Procedures Doing Callbacks with External C Procedures
PL/SQL, as described in the Oracle Database PL/SQL User's Guide and Reference C, by means of the Oracle Call Interface (OCI), as described in the Oracle Call Interface Programmer's Guide C or C++, by means of the Pro*C/C++ precompiler, as described in the Pro*C/C++ Programmer's Guide COBOL, by means of the Pro*COBOL precompiler, as described in the Pro*COBOL Programmer's Guide Visual Basic, by means of Oracle Objects for OLE (OO4O), as described in Oracle Objects for OLE Developer's Guide.
Java, by means of the JDBC Application Programmers Interface (API). Refer to Oracle Database Java Developer's Guide.
How should 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 may narrow depending on how your application needs to 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. The need for portability, together with the need for security, may influence you to select Java.
Most significantly, from the point of view of performance, you should note that 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 machine 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 may be a number of situations in which you may need to implement your application in more than one language. For instance, the introduction of Java running within the address space of the server suggest that you may 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 allow you to write C function calls as PL/SQL bodies. These C functions 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 still be usable by SQL or PL/SQL, as long as your procedure is callable by C. Therefore, if you have a candidate C++ procedure, you would use a C++ extern "C" statement in that procedure to make it callable by C. This means that 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.
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 execute 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
To support legacy applications, call specifications also allow you to publish with the AS EXTERNAL clause. For new application development, however, using the AS LANGUAGE clause is recommended.
Dispatching the appropriate C or Java target procedure Datatype conversions Parameter mode mappings Automatic memory allocation and cleanup Purity constraints to be specified, where necessary, for packaged 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 already-existing program as an external procedure, load, publish, and then call it.
statement, the Java Virtual Machine (JVM)] library manager loads Java binaries (.class files) and resources from local BFILEs or LOB columns into RDBMS libunits. Suppose a compiled Java class is stored in the following operating system file:
/home/java/bin/Agent.class
Creating a class libunit in schema scott from file Agent.class requires two steps: First, create a directory object on the server's file system. The name of the directory object is an alias for the directory path leading to Agent.class. To create the directory object, you must grant user scott the CREATE ANY DIRECTORY privilege, then execute the CREATE DIRECTORY statement, as follows:
CONNECT System/Manager GRANT CREATE ANY DIRECTORY TO Scott IDENTIFIED BY Tiger; CONNECT Scott/Tiger CREATE DIRECTORY Bfile_dir AS '/home/java/bin';
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.
Note:
This feature is available only on platforms that support dynamically linked libraries (DLLs) or dynamically loadable shared libraries such as Solaris .so libraries. The external procedure agent can call procedures in any library that complies with the calling standard used. The supported calling standard is C. Refer to "CALLING STANDARD" on page 14-10 for more information on the calling standard sub clause used with external procedures in PL/SQL. Define the C procedures using either the 1) K & R-style prototypes or 2) the ISO/ANSI prototypes without numeric datatypes that are less than full width (such as float, short, char). Other datatypes work in ISO/ANSI prototypes if they do not change size under "default argument promotions."
/* Supported K & R */ void C_findRoot(x) float x; ... /* Supported ISO/ANSI */ void C_findRoot(double x) ... /* Not supported ISO/ANSI */ void C_findRoot(float x) ...
Step 1 Set Up the Environment Your database administrator must perform the following tasks to configure your database to use external procedures that are written in C, or can be called from C applications:
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 when the database is started. 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 the external procedure agent. 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.
Determine whether the agent for your external procedure will run in dedicated mode (the default mode) or multithreaded mode. In dedicated mode, one "dedicated" agent is launched for each user. In multithreaded mode, a single multithreaded agent is launched. The multithreaded agent handles calls using different threads for different users. In a configuration where many users will call the external procedures, using a multithreaded agent is recommended to conserve system resources. If the agent will run in dedicated mode, additional configuration of the agent process is not necessary. If the agent will run in multithreaded mode, your database administrator must configure the database system to start the agent in multithreaded mode (as a multithreaded agent). This is done using the agent control utility agtctl. For example, start the agent using the agent control utility startup command:
agtctl startup extproc agent_sid
where agent_sid is the system identifier which this agent will service. An entry for this system identifier is typically added as an entry in the file tnsnames.ora. Details on the agent control utility are in the Oracle Database Heterogeneous Connectivity Administrator's Guide.
Note:
If you use a multithreaded agent, the library you call must be thread safeto avoid errors such as a corrupt 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 141 illustrates the architecture of the multithreaded external procedure 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, however, be serviced by different task threads. For example, task thread 1 could handle the request from user session 1 at one time, and handle the request from user session 2 at another time.
Task Thread 1
Task Thread 2
Task Thread 3
DLLs
See Also:
Oracle Database Heterogeneous Connectivity Administrator's Guide for details on configuring the agent process for the external procedure to use multithreaded mode, architecture, and for additional details on multithreaded agents Oracle Database Administrator's Guide. for details on managing processes for external procedures
Step 2 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 may grant you CREATE ANY LIBRARY privileges, in which case you can create your own alias libraries using the following syntax:
CREATE LIBRARY [schema_name.]library_name {IS | AS} 'file_path' [AGENT 'agent_link'];
It is recommended that you specify the full path to the DLL, rather than just the DLL name. In the following 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 the following example, the agent specified by the name agent_link is used to run any external procedure in the library C_Utils. 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.
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';
For security reasons, EXTPROC, by default, will only load DLLs that are in directory $ORACLE_HOME/bin or $ORACLE_HOME/lib. Also, only local sessionsthat is, Oracle Database client processes that are running on the same machineare allowed to connect to EXTPROC. To load DLLs from other directories, the environment variable EXTPROC_DLLS should be set. The value for this environment variable is a colon- separated (:) list of DLL names qualified with the complete path. For example:
EXTPROC_DLLS=/private1/home/scott/dll/myDll.so:/private1/home/scott/dll/newDll.so
The preferred method to set this environment variable is through the ENVS parameter in the file listener.ora. Refer to the Oracle Net manual for more information on the EXTPROC feature. Note the following:
On a Windows system, you would specify the path using a drive letter and backslash characters (\) in the path. This technique is not applicable for VMS systems, where the ENVS section of listener.ora is not supported.
Step 3 Publish the External Procedure You find or write a new external C procedure, then add it to the DLL. When the procedure is in the DLL, you publish it using the call specification mechanism described in the following section.
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, for running the procedure on a different machine.
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}
Note:
Oracle Database uses a PL/SQL variant of the ANSI SQL92 External Procedure, but replaces the ANSI keyword AS EXTERNAL with this call specification syntax. This new syntax, first introduced for Java class methods, has been extended to C procedures.
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]}
Note:
Unlike Java, C does not understand SQL types; therefore, the syntax is more intricate
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 quotes, 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 quotes, 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 subprogram.
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 will be 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 datatypes 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 should run this procedure. This is intended for situations where external procedure agents are run 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 invoked 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 runtime 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.
in Calthough they map one-to-one with Java classes, whereas DLLs can contain more than one procedure. The NAME-clause string uniquely identifies the Java method. The PL/SQL function or procedure and Java must correspond with regard to 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 the following 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); } }
The following 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';
Standalone PL/SQL Procedures and Functions PL/SQL Package Specifications PL/SQL Package Bodies Object Type Specifications Object Type Bodies
Note: Under Oracle Database version 8.0, AS EXTERNAL call specifications could not be placed in package or type bodies.
14-11
We have already shown an example of call specification located in a standalone PL/SQL function. Here are some examples showing some of the other locations.
Note:
In the following examples, the AUTHID and SQL_NAME_RESOLVE clauses may or may not be required to fully stipulate a call specification. See the Oracle Database PL/SQL User's Guide and Reference and the Oracle Database SQL Reference for more information.
You may need to set up the following data structures for certain examples to work:
CONN SYS/CHANGE_ON_INSTALL AS SYSDBA; GRANT CREATE ANY LIBRARY TO scott; CONNECT scott/tiger 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 BINARY_INTEGER, y VARCHAR2, z DATE) AS LANGUAGE C NAME "C_demoExternal" LIBRARY SomeLib WITH CONTEXT
PROCEDURE C_InSpec_proc (x BINARY_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 BINARY_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 BINARY_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 BINARY_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 BINARY_INTEGER, y VARCHAR2, z DATE) IS LANGUAGE JAVA NAME 'pkg1.class4.J_InBody_meth(int,java.lang.String,java.sql.Date)'; END;
The available set of PL/SQL datatypes does not correspond one-to-one with the set of C datatypes. 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.
In the following sections, you learn how to specify a parameter list that deals with these circumstances.
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.
Specifying Datatypes
Do not pass parameters to an external procedure directly. Instead, pass them to the PL/SQL subprogram that published the external procedure. Therefore, you must specify PL/SQL datatypes for the parameters. PL/SQL datatypes map to default external datatypes, as shown in Table 141.
Table 141 Parameter Datatype Mappings Supported External Types Default External Type [UNSIGNED] CHAR [UNSIGNED] SHORT [UNSIGNED] INT [UNSIGNED] LONG SB1, SB2, SB4 UB1, UB2, UB4 SIZE_T [UNSIGNED] CHAR [UNSIGNED] SHORT [UNSIGNED] INT [UNSIGNED] LONG SB1, SB2, SB4 UB1, UB2, UB4 SIZE_T FLOAT DOUBLE STRING OCISTRING INT
UNSIGNED INT
FLOAT REAL DOUBLE PRECISION CHAR CHARACTER LONG NCHAR NVARCHAR2 ROWID VARCHAR VARCHAR2 LONG RAW RAW BFILE BLOB CLOB NCLOB
RAW OCILOBLOCATOR
14-15
Table 141 (Cont.) Parameter Datatype Mappings PL/SQL Datatype NUMBER DEC1 DECIMAL1 INT1 INTEGER1 NUMERIC1 SMALLINT1 DATE TIMESTAMP TIMESTAMP WITH TIME ZONE TIMESTAMP WITH LOCAL TIME ZONE INTERVAL DAY TO SECOND INTERVAL YEAR TO MONTH composite object types: ADTs composite object types: collections (varrays, nested tables)
1
OCIDATE OCIDateTime
OCIDATE OCIDateTime
This PL/SQL type will only compile if you use AS EXTERNAL in your callspec.
External Datatype If Mode is IN or Corresponding to RETURN, Specify in C Prototype... PL/SL Type CHAR UNSIGNED CHAR SHORT UNSIGNED SHORT INT UNSIGNED INT LONG UNSIGNED LONG CHAR UNSIGNED CHAR SHORT UNSIGNED SHORT INT UNSIGNED INT char unsigned char short unsigned short int unsigned int long unsigned long char unsigned char short unsigned short int unsigned int
Table 142 (Cont.) External Datatype Mappings External Datatype If Mode is IN or Corresponding to RETURN, Specify in PL/SL Type C Prototype... LONG UNSIGNED LONG SIZE_T SB1 UB1 SB2 UB2 SB4 UB4 FLOAT DOUBLE STRING RAW OCILOBLOCATOR OCINUMBER OCISTRING OCIRAW OCIDATE OCICOLL long unsigned long size_t sb1 ub1 sb2 ub2 sb4 ub4 float double char * unsigned char * OCILobLocator * OCINumber * OCIString * OCIRaw * OCIDate * OCIColl * or OCIArray * or OCITable * OCIType * OCIType * dvoid* dvoid* If Mode is IN by Reference or RETURN by Reference, Specify in C Prototype... long * unsigned long * size_t * sb1 * ub1 * sb2 * ub2 * sb4 * ub4 * float * double * char * unsigned char * OCILobLocator ** OCINumber * OCIString * OCIRaw * OCIDate * OCIColl ** or OCIArray ** or OCITable ** OCIType * OCIType * dvoid* dvoid* If Mode is IN OUT or OUT, Specify in C Prototype... long * unsigned long * size_t * sb1 * ub1 * sb2 * ub2 * sb4 * ub4 * float * double * char * unsigned char * OCILobLocator ** OCINumber * OCIString * OCIRaw * OCIDate * OCIColl ** or OCIArray ** or OCITable ** OCIType * OCIType * dvoid* dvoid**
Composite object 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 datatype, but must use the datatypes generated by Oracle Database's Object Type Translator (OTT). The optional TDO argument for INDICATOR, and for composite objects, in general, has the C datatype, OCIType *. OCICOLL for REF and collection arguments is optional and only exists for the sake of completeness. You cannot map REFs or collections onto any other datatype and vice versa.
14-17
All IN and RETURN arguments of external types not on this list, all IN OUT arguments, and all OUT arguments are passed by reference.
You may need to set up the following data structures for certain examples to work:
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 datatype (which maps to the default external datatype). 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 the following:
Nondefault external datatypes 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)
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 may specify the RETURN parameter, but it must be in the last position. If RETURN is not specified, the default external type is used.
Specifying Properties
You can also use the PARAMETERS clause to pass additional information about PL/SQL formal parameters and function results to an external procedure. Do this by specifying one or more of the following properties:
INDICATOR [{STRUCT | TDO}] LENGTH DURATION MAXLEN CHARSETID CHARSETFORM SELF
Table 143 shows the allowed and the default external datatypes, PL/SQL datatypes, and PL/SQL parameter modes allowed for a given property. Notice that MAXLEN (used to specify data returned from C back to PL/SQL) cannot be applied to an IN parameter.
Table 143 Property INDICATOR Properties and Datatypes Allowed External Types (C) SHORT Default External Type (C) SHORT Allowed PL/SQL Types all scalars Allowed Default PL/SQL PL/SQL Modes Passing Method IN IN OUT OUT RETURN BY BY BY BY VALUE REFERENCE REFERENCE REFERENCE
14-19
Table 143 (Cont.) Properties and Datatypes Property LENGTH Allowed External Types (C) [UNSIGNED] SHORT [UNSIGNED] INT [UNSIGNED] LONG [UNSIGNED] SHORT [UNSIGNED] INT [UNSIGNED] LONG Default External Type (C) INT Allowed PL/SQL Types CHAR LONG RAW RAW VARCHAR2 CHAR LONG RAW RAW VARCHAR2 CHAR CLOB VARCHAR2 Allowed Default PL/SQL PL/SQL Modes Passing Method IN IN OUT OUT RETURN IN OUT OUT RETURN IN IN OUT OUT RETURN BY BY BY BY VALUE REFERENCE REFERENCE REFERENCE
MAXLEN
INT
UNSIGNED INT
In the following example, the PARAMETERS clause specifies properties for the PL/SQL formal parameters and function result:
CREATE OR REPLACE FUNCTION plsToCparse_func x IN BINARY_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 y, -- stores value of y y LENGTH, -- stores current length y MAXLEN, -- stores maximum length RETURN INDICATOR, RETURN); (
x of y of y
A K&R prototype is needed because the indicator variable x_ind must be of datatype short and short should not be used in ISO/ANSI prototypes. The additional parameters in the C prototype correspond to the INDICATOR (for x), LENGTH (of y), and MAXLEN (of y), as well as the INDICATOR for the function result in the PARAMETERS clause. The parameter RETURN corresponds to the C function identifier, which stores the result value.
INDICATOR
An INDICATOR is a parameter whose value indicates whether or not 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
14-20 Oracle Database Application Developers Guide - Fundamentals
signal the server that a returned value is actually a NULL, and should be treated accordingly. In such cases, you can use the property INDICATOR to associate an indicator with a formal parameter. If the PL/SQL subprogram 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,
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.
14-21
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:
Oracle Call Interface Programmer's Guide and the Oracle Database Globalization Support Guide for more information about using national language data with the OCI
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.
Using SELF
SELF is the always-present argument of an object type's member function or 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 further a table of this object type. The user would eventually like to determine the age of each Person object in this table.
Note:
You may need to set up data structures similar to the following for certain examples to work:
CONNECT system/manager GRANT CONNECT,RESOURCE,CREATE LIBRARY TO scott IDENTIFIED BY tiger; CONNECT scott/tiger CREATE OR REPLACE LIBRARY agelib UNTRUSTED IS '/tmp/scott1.so';.
This example is only for Solaris; other libraries and include paths might be needed for other platforms. 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) );
Normally, the member function would be implemented in PL/SQL, but for this example, we make it an external procedure. To realize this, 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;
Notice that the calcAge_func member function does not take any arguments, but only returns a number. A member function is always invoked 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 ('SCOTT', TO_DATE('14-MAY-85')); INSERT INTO Person_tab VALUES ('TIGER', TO_DATE('22-DEC-71'));
The following 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;
14-23
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); }
PARAMETERS ( x BY REFERENCE);
This is rather than the default, which would be used when there is no PARAMETERS clause:
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 will be passed to the external procedure. For example, if you write the following PL/SQL function:
CREATE OR REPLACE FUNCTION getNum_func ( x IN REAL) RETURN BINARY_INTEGER AS LANGUAGE C LIBRARY c_utils NAME "C_getNum" WITH CONTEXT PARAMETERS ( CONTEXT, x BY REFERENCE, RETURN INDICATOR);
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.
14-25
Anonymous blocks Standalone and packaged subprograms Methods of an object type Database triggers SQL statements (calls to packaged functions only).
Any PL/SQL block or subprogram executing 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 142 shows how Oracle Database and external procedures interact.
Figure 142 Oracle Database and External Procedures
Oracle Database Disk Storage Oracle Server Process Execution PL/SQL Interpreter PL/SQL Subprogram Java Method Java Virtual Machine SQL Engine External Process Execution DLL
External C Process
Managing Permissions
Note:
You may need to set up the following data structures for certain examples to work:
CONNECT system/manager GRANT CREATE ANY DIRECTORY to scott; CONNECT scott/tiger CREATE OR REPLACE DIRECTORY bfile_dir AS '/tmp'; CREATE OR REPLACE JAVA RESOURCE NAMED "appImages" USING BFILE (bfile_dir,'bfile_audio');
To call external procedures, a user must have the EXECUTE privilege on the call specification and on any resources used by the procedure. In SQL*Plus, you can use the GRANT and REVOKE data control statements to manage permissions. For example:
GRANT EXECUTE ON plsToJ_demoExternal_proc TO Public; REVOKE EXECUTE ON plsToJ_demoExternal_proc FROM Public; GRANT EXECUTE ON JAVA RESOURCE "appImages" TO Public; GRANT EXECUTE ON plsToJ_demoExternal_proc TO Scott; REVOKE EXECUTE ON plsToJ_demoExternal_proc FROM Scott;
See Also:
This is essentially the same as executing 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 we 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;
14-27
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.
Call J_calcFactorial:
CALL J_calcFactorial (:x) INTO :y; PRINT y
The result:
Y -----120
Although some DLL caching takes place, there is no guarantee that your DLL will 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 killed. Consequently, you incur the cost of launching the agent only once, no matter how many calls you make. Still, you should call an external procedure only when the computational benefits outweigh the cost.
Here, we call PL/SQL function plsCallsCdivisor_func, which we 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 BINARY_INTEGER; a BINARY_INTEGER; b BINARY_INTEGER; CALL plsCallsCdivisor_func(a, b); IF g IN (2,4,8) THEN ...
Note:
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. The external procedure does not need to (and should not) call the C function free() to free memory allocated by this service routine as this is handled automatically.
Note:
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:
14-29
Note:
You may need to set up data structures similar to the following for certain examples to work:
CONNECT system/manager DROP USER y CASCADE; GRANT CONNECT,RESOURCE,CREATE LIBRARY TO y IDENTIFIED BY y; CONNECT y/y CREATE LIBRARY stringlib AS '/private/varora/ilmswork/Cexamples/john2.so';
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 the following example shows, C_ concat uses OCIExtProcAllocCallMemory() to allocate memory for the result string:
#include #include #include #include #include <stdio.h> <stdlib.h> <string.h> <oci.h> <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; } }
14-31
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 * (*)(dvoid (dvoid * (*)(dvoid (void (*)(dvoid *,
(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 *) "samp", (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)); /* ------- subroutine called here-----------------------*/ printf ("calling concat...\n"); concat(ctx, str1, str1_i, str2, str2_i, ret_i, ret_l); return 0;
14-33
} #endif
OCIExtProcRaiseExcp
This service routine raises a predefined exception, which must have a valid Oracle Database error number in the range 1..32767. 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 BINARY_INTEGER, divisor IN BINARY_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 the following 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 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 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, we published external procedure plsTo_divide_proc. In the following 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 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; }
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 need to establish a new connection to the database, you cannot use these handles; instead, you must create your own.
14-35
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, refer to "Demo Program" on page 14-39.
Note:
Callbacks are not necessarily a same-session phenomenon; you may execute an SQL statement in a different session through OCIlogon.
An external C procedure executing on Oracle Database can call a service routine to obtain OCI environment and service handles. With the OCI, you can use callbacks to execute 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 the following script:
CREATE TABLE Emp_tab (empno NUMBER(10)) CREATE PROCEDURE plsToC_insertIntoEmpTab_proc ( empno BINARY_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 do not need to include oci.h; instead, just include ociextp.h.
Restrictions on Callbacks
With callbacks, the following SQL commands and OCI procedures are not supported:
Transaction control commands such as COMMIT Data definition commands such as CREATE The following object-oriented OCI procedures:
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
14-37
Also, with OCI procedure OCIHandleAlloc, the following handle types are not supported:
OCI_HTYPE_SERVER OCI_HTYPE_SESSION OCI_HTYPE_SVCCTX OCI_HTYPE_TRANS
This error, which means that agent extproc terminated abnormally because the external procedure caused a core dump. To avoid errors when declaring C prototype parameters, refer to the preceding tables.
Demo Program
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/TIGER account, which must have CREATE LIBRARY privileges.
Threading: In the non-threaded configuration of the agent process, there is only one function active at a time. However, in the case of multithreaded agents, multiple functions can be active at the same time. In that case, it is possible that two or more functions would try to access the global variable concurrently, with unsuccessful results. DLL caching: Global variables are also used to store data that is intended to persist beyond the lifetime of a function. For example, consider two functions func1() and func2() trying to pass data to each other. Because of the DLL caching feature, it is possible that after func1()'s completion, the DLL will be unloaded, which results in all global variables losing their values. When func2() is executed, the DLL is reloaded, and all globals are initialized to 0, which will be inconsistent with their values at the completion of func1().
Template makefile in the RDBMS subdirectory /public for help creating a dynamic link library
Guidelines for Call Specifications and CALL Statements 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.
14-39
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, make sure that the datatypes 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.
This feature is available only on platforms that support DLLs. Only C procedures and procedures callable from C code are 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.
15
Developing Applications with Oracle XA
This chapter describes 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 more than one database. The chapter includes the following topics:
X/Open Distributed Transaction Processing (DTP) Oracle XA Library Interface Subroutines Developing and Installing XA Applications Troubleshooting XA Applications XA Issues and Restrictions
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 on library linking filenames. README for changes, bugs, and restrictions in the Oracle XA library for your platform.
Oracle Database
DTP Terminology
This section introduces you to key terminology in distributed transaction processing. 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 an "all-or-none" semantics across distributed RMs. Branch A branch is a unit of work contained within one RM. Multiple branches make up one global transaction. In the case of 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 an "all-or-none" semantics across distributed RMs.
An external TM is a middle-tier component that resides outside of 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) vendor. 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 subroutines, which are described in "XA Library Subroutines" on page 15-5, 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 may 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. 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.
2.
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 OCI program. The AP operates on the RM's resource through its native interface, for example, SQL. TX Interface An application program starts and completes all transaction control operations through the TM by means of 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.
The naming conventions for the TX interface and associated subroutines are vendor-specific. For example, the tx_open call may be referred to as tp_open on your system. In some cases, the calls may be implicit, for example, at the entry to a transactional RPC. Refer to the documentation supplied with the transaction processing monitor for details.
Note:
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 executes 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.
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 Close string Open string Libraries The Oracle Database resource manager name within the xa_switch_t structure is Oracle_XA. The close string used by xa_close() is ignored and can be null. The format of the open string used by xa_open() is described in detail in "Defining the xa_open() String" on page 15-7. 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 may 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.
Typically, the opening of the resource results from the AP's call to tx_open. Some TMs may 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 may occur when the AP calls tx_close or when the application terminates. The TM instructs the RMs to perform several other tasks, which include the following:
Starting a new transaction and associating it with an ID Rolling back a transaction Preparing and committing a transaction
XA Library Subroutines
XA Library subroutines are described in Table 152.
Table 152 XA Library Subroutines Description Connects to the RM. Disconnects from the RM. Starts a new transaction and associates it with the given transaction ID (XID), or associates the process with an existing transaction. Disassociates the process from the given XID.
xa_rollback() Rolls back the transaction associated with the given XID. xa_prepare() xa_commit() xa_recover() xa_forget() Prepares the transaction associated with the given XID. This is the first phase of the two-phase commit protocol. Commits the transaction associated with the given XID. This is the second phase of the two-phase commit protocol. Retrieves a list of prepared, heuristically committed, or heuristically rolled back transactions. Forgets the heuristically completed transaction associated with the given XID.
In general, the AP does not need to worry about the subroutines in Table 152 except to understand the role played by the xa_open() string.
Additional Functions in the XA Interface for Oracle Database Description 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. 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 execute 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 command 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.
Responsibilities of the DBA or System Administrator Responsibilities of the Application Developer Defining the xa_open() String Interfacing XA with Precompilers and OCI Managing Transaction Control with XA Migrating Precompiler or OCI Applications to TPM Applications Managing XA Library Thread Safety
Define the open string, with help from the application developer. This task is described in "Defining the xa_open() String" on page 15-7. Make sure the DBA_PENDING_TRANSACTIONS view exists and grant the SELECT privilege to the view for all Oracle users specified in the xa_open() string. In Oracle Database version 7 client applications, all Oracle Database accounts used by Oracle XA library must have the SELECT privilege on the V$XATRANS$ view. This view should 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 should be aware that a TPM system starts the process that connects to Oracle Database. Refer to your TPM documentation to determine what environment exists for the process and what user ID it will have. Be sure 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 will write the XA trace file.
See Also: "Defining the xa_open() String" on page 15-7 for information on 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. You should perform this task before starting any TPM servers.
Define the open string with help from the DBA or system administrator, as explained in "Defining the xa_open() String" on page 15-7. Develop the applications. Observe special restrictions on transaction-oriented SQL statements for precompilers.
See Also: "Interfacing XA with Precompilers and OCI" on page 15-10
3.
Syntax of the xa_open() String Required Fields for the xa_open() String Optional Fields for the xa_open() String
ORACLE_XA{+required_fields...} [+optional_fields...]
The following sections 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. Their values may or may not be case-sensitive depending on the platform. There is no way to use the plus character (+) as part of the actual information string.
Acc=P/user/password
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 aborts the transaction. For example, SesTM=15 indicates that the session idle time limit is 15 seconds. For example, if the TPM uses remote procedure 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.
Table 155
Syntax Element
NoLocal= true | false Specifies whether local transactions are allowed. The default value is false. If the application needs to disallow local transactions, then set the value to true. DB=db_name Indicates the name used by Oracle Database precompilers to identify the database. For example, DB=payroll indicates that the database name is payroll and that the application server program will use 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) should omit the DB=db_name clause in the open string. Applications that use explicitly named databases should indicate that database name in their DB=db_name field. Oracle Database Version 7 OCI programs need to 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 need to 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 execute 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 may 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 machine where the Oracle XA library error and tracing information should 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. Specifies whether the application is initialized in object mode. The default value is false. If the application needs to use certain API calls that require object mode, such as OCIRawAssignBytes(), then set the value to true.
MaxCur=maximum_#_of_o Specifies the number of cursors to be allocated when the pen_cursors database is opened. It serves the same purpose as the precompiler option maxopencursors. For example, MaxCur=5 indicates that the precompiler should try to keep five open cursors cached. Note that 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 should 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 needs to access more than one Oracle Database instance. 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.
Table 155 (Cont.) Optional Fields in the xa_open() String Syntax Element Loose_Coupling=true | false Description 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. However, if branches are landed on different RAC instances when running Oracle Real Application Clusters, they are loosely coupled even if the value is set to false. Refer to "Managing Transaction Branches in XA Applications" on page 15-19 for more information.
SesWt=session_wait_li Specifies the number of seconds Oracle Database waits for a mit 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 Precompilers with the Oracle XA Library Using OCI with the Oracle XA Library
Using Precompilers with the Default Database Using Precompilers with a Named Database
The following examples use the precompiler Pro*C/C++. Using Precompilers with the Default Database To interface to a precompiler with the default database, make certain 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. The following is an example of an open string identifying a default Pro*C/C++ connection.
ORACLE_XA+SqlNet=maildb+ACC=P/scott/tiger +SesTM=10+LogDir=/usr/local/logs
Note that the DB=db_name is absent, indicating an empty database ID string. The syntax of a SQL statement would be:
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 may include the default database as well as 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. You would configure the following open strings in the transaction manager:
Example 152 Sample Open String Configuration
Note that there is no DB=db_name field in the last open string in Example 152. In the application server program, you would 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, you would enter statements similar to the following:
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 the following 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 the ones created through xa_open(). Any work performed on non-XA connections would be outside the global transaction and would have to be committed separately.
15-11
done through the TPM. The applications can execute the function xaoSvcCtx() to obtain the service context structure when they need to access the resource manager. In applications that need to 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 should call the function xaoSvcCtx() with the correct arguments to obtain the correct service context.
See Also:
Oracle Call Interface Programmer's Guide for more information about using the OCISvcCtx handle
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" on page 15-13 use such a client/server model. A service is a logical unit of work, which in the case of 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 executes 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" on page 15-13, you can encode transaction control statements within either the client or the server. To have more than one process 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, the examples that follow 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.
/***** Client: *****/ tpm_service("ServiceName"); /***** Server: *****/ ServiceName() { <get service specific data> tx_begin(); 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> }
/***** 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> }
15-13
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, be sure 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. Refer to 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(). 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:
3.
The preceding is only true for global rather than local transactions. You should 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, you should use release_cursor=no. Use release_cursor=yes only when you are certain that a statement will be executed only once.
Table 157 lists the TPM functions that replace regular Oracle Database commands when migrating precompiler or OCI applications to TPM applications.
Table 157 TPM Replacement Commands TPM Functions tx_open (possibly implicit) tx_begin Service that executes the SQL tx_commit tx_rollback tx_close (possibly implicit)
Regular Oracle Database Commands CONNECT user/password implicit start of transaction SQL COMMIT ROLLBACK disconnect
Troubleshooting XA Applications
In Oracle Database, each thread that accesses the database must have its own connection.
Restrictions on Threading in XA
The following restrictions apply when using threads:
Any Pro* or OCI code that executes as part of the application server process on the transaction monitor cannot be threaded unless the transaction monitor is explicitly told when each new 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.
Troubleshooting XA Applications
This section discusses how to find information in case of problems or system failure. It also discusses trace files and recovery of pending transactions. This section contains the following topics:
Accessing XA Trace Files Managing In-Doubt or Pending Transactions Using SYS Account Tables to Monitor XA Transactions
15-15
Troubleshooting XA Applications
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.
0x1, which enables you to trace the entry and exit to each procedure 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 non-public XA library routines. 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.
Troubleshooting XA Applications
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.
The LogDir directory specified in the open string. If you do not specify LogDir in the open string, then the Oracle XA application attempts to create the trace file in the following directory (if the Oracle home is accessible):
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.
It is locking data that is required by other transactions. It is not resolved in a reasonable amount of time.
Refer to the TPM documentation for more information about overriding in-doubt transactions in such circumstances and about how to decide whether the in-doubt transaction should be committed or rolled back.
For transactions generated by Oracle XA applications, the following 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, you could use the following SQL statement to obtain more information about in-doubt transactions generated by Oracle XA applications.
15-17
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.
Using Database Links in XA Applications Managing Transaction Branches in XA Applications Using XA with Oracle Real Application Clusters SQL-Based XA Restrictions Miscellaneous 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.
Assuming that 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 is free to use the database link connection provided the user that created the connection is the same as the user who 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 is not applicable to XA applications.
Two Phase Commit Read-only optimization [prepare for all branches, commit for last branch] Serialization Database call
Managing Transaction Branches on Oracle Real Application Clusters (RAC) Managing Instance Recovery in Real Application Clusters Global Uniqueness of XIDs in Real Application Clusters
15-19
In the previous sequence, Oracle Database returns an error because Node 2 should not resume a branch that is physically located on a different node (Node 1). The way to achieve tight coupling in RAC is to use DTP services, that is, services whose cardinality (one) ensures that all tightly-coupled branches land on the same instancewhether or not load balancing is enabled. Mid-tier components address Oracle Database by means of a common logical database service name that maps to a single RAC instance at any point in time. An intermediate name resolver for the database service hides the physical characteristics of the database instance. DTP services enable all participants of a tightly-coupled global transaction to create branches on one instance. For example, when you use a DTP service, the following sequence of actions occurs on the same instance:
1. 2. 3. 4. 5. 6. 7.
xa_start() SQL operations xa_end() (SUSPEND) xa_start() (RESUME) SQL operations xa_prepare() 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 a RAC cluster to balance the load of many distributed XA transactions, thereby maximizing application throughput.
See Also: Oracle Database Oracle Clusterware and Oracle Real Application Clusters Administration and Deployment Guide to learn how to manage distributed transactions in a Real Application Clusters configuration.
Note: In releases subsequent to Oracle Database 9i Release 2, xa_recover() is required to wait for distributed DML to complete on remote sites.
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 failover 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 command, 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 mid-tier TM involvement to determine the state of transactions prepared by other instances.
See Also: Oracle Database Oracle Clusterware and Oracle Real Application Clusters Administration and Deployment Guide to learn how to manage instance recovery
SQL-Based XA Restrictions
This section describes restrictions concerning the following SQL operations:
15-21
OCITransCommit() or the Version 7 equivalent ocom(). For example, use tx_commit() or tx_rollback() to end a global transaction.
DDL Statements
Because a DDL SQL statement, such as CREATE TABLE, implies an implicit commit, the Oracle XA application cannot execute any DDL SQL statements.
Session State
Oracle Database does not guarantee that session state will be 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 executes as part of the same global transaction may 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 command 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
Note the following restrictions:
Oracle Database does not support association migration (a means whereby a transaction manager may 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.
See Also:
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 non-shared client library. These link restrictions are applicable only when using XA's dynamic registration (Oracle XA switch xaoswd).
16
Developing Applications on 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. Topics in this chapter include:
Introduction to Publish-Subscribe
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 has been 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 161 illustrates publish and subscribe functionality.
16-1
Publish-Subscribe Architecture
Subscriptions Topic Publisher Agent receive notification/ message Subject, Channel subscribe register
Subscriber Agent
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 runtime, 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 the following features to support database-enabled publish-subscribe messaging:
Database Events Database events support declarative definitions for publishing database events, detection, and run-time 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.
See Also: "Responding to System Events through Triggers" on page 9-37
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.
See Also:
Client Notification Client notifications support asynchronous delivery of messages to interested subscribers. This enables database clients to register interest in certain queues, and it enables these clients to receive notifications when publications on such queues occur.
Publish-Subscribe Concepts
Asynchronous delivery of messages to database clients is in contrast to the traditional polling techniques used to retrieve information.
See Also:
Publish-Subscribe Concepts
This section describes various concepts related to publish-subscribe. queue A queue is an entity that supports the notion of named subjects of interest. Queues can be characterized as: non-persistent queue (lightweight queue) The underlying queue infrastructure pushes the messages published to connected clients in a lightweight, at-best-once, manner. persistent queue Queues serve as durable containers for messages. Messages are delivered in a deferred and reliable mode. agent Publishers and subscribers are internally represented as agents. There is a distinction between an agent and a client. 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. 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. There could be several clients acting on behalf of a single agent. Also, 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.
16-3
Publish-Subscribe Concepts
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 should be done, and a callback, indicating how there delivery should 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 needs to notify all interested clients, it posts the message to all registered clients. receive a message A subscriber may receive messages through any of the following mechanisms:
A client process acting on behalf of the subscriber specifies a callback using the registration mechanism. The posting mechanism then asynchronously invokes the callback when a message matches the subscriber's subscription. The message content may be passed to the callback function (non-persistent queues only). A client process acting on behalf of the subscriber specifies a callback using the registration mechanism. The posting mechanism then asynchronously invokes the callback function, but without the full message content. This serves as a notification to the client, which subsequently retrieves the message content in a pull fashion (persistent queues only). A client process acting on behalf of the subscriber simply retrieves messages from the queue in a periodic, or some other appropriate, manner. While the messages are deferred, there is no asynchronous delivery to the end-client.
You may need to set up data structures, similar to the following, for certain examples to work:
CONNECT system/manager DROP USER pubsub CASCADE; CREATE USER pubsub IDENTIFIED BY pubsub; GRANT CONNECT, RESOURCE TO pubsub; GRANT EXECUTE ON DBMS_AQ to pubsub; GRANT EXECUTE ON DBMS_AQADM to pubsub; GRANT AQ_ADMINISTRATOR_ROLE TO pubsub; CONNECT pubsub/pubsub
Scenario: This example shows how system events, client notification, and AQ work together to implement publish-subscribe.
Create under the user schema, pubsub, with all objects necessary to support a publish-subscribe mechanism. In this particular code, the Agent snoop subscribe to messages that are published at logon events. Note that the user pubsub needs AQ_ADMINISTRATOR_ROLE privileges to use AQ functionality.
Rem -----------------------------------------------------REM create queue table for persistent multiple consumers: Rem -----------------------------------------------------CONNECT pubsub/pubsub; 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 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; /
16-5
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 Msg_prop Enq_msgid Userdata DBMS_AQ.Enqueue_options_t; DBMS_AQ.Message_properties_t; RAW(16); 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 = ''SCOTT'' '); 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; /
After subscriptions are created, the next step is for the client to register for notification using callback functions. This is done using the Oracle Call Interface (OCI). The following code performs necessary steps for registration. The initial
steps of allocating and initializing session handles are omitted here for sake of clarity.
ub4 namespace = OCI_SUBSCR_NAMESPACE_AQ; /* callback function for notification of logon of user 'scott' 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 Scott 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; Developing Applications on the Publish-Subscribe Model 16-7
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 SCOTT logs on to the database, the client is notified, and the call back function notifySnoop is called.
Index
Symbols
%ROWTYPE attribute, 7-5 used in stored functions, 7-6 %TYPE attribute, 7-5 asynchronous commit feature using to manage redo behavior, 2-3 attributes %rowtype, PL/SQL, 1-4 %type, PL/SQL, 1-4 auditing triggers and, 9-24 autonomous routine, 2-21 autonomous scope definition, 2-21 autonomous transactions, 2-20, 2-26 AUTONOMOUS_TRANSACTION pragma, 2-21
A
Active Data Object translating to PSP, 12-3 Active Server Pages translating to PSP, 12-3 AFTER SUSPEND event handling suspended storage allocation, 2-28 AFTER triggers auditing and, 9-24, 9-26 correlation names and, 9-12 specifying, 9-4 agent definition, 16-3 ALL_ERRORS view debugging stored procedures, 7-25 ALL_SOURCE view, 7-26 ALTER SESSION statement SERIALIZABLE clause, 2-16 ALTER TABLE statement defining integrity constraints, 6-14 DISABLE ALL TRIGGERS clause, 9-22 DISABLE CONSTRAINT clause, 6-16 DROP CONSTRAINT clause, 6-19 ENABLE ALL TRIGGERS clause, 9-21 ENABLE CONSTRAINT clause, 6-16 INITRANS parameter, 2-16 RETENTION option, for flashback, 10-4 ALTER TRIGGER statement DISABLE clause, 9-22 ENABLE clause, 9-21 anonymous PL/SQL blocks about, 7-2 compared to triggers, 7-15 AnyData datatype, 3-19 AnyDataSet datatype, 3-19 AnyType datatype, 3-19 applications calling stored procedures and packages, 7-33 unhandled exceptions in, 7-28
B
BEFORE triggers complex security authorizations, 9-33 correlation names and, 9-12 derived column values, 9-34 specifying, 9-4 BFILE datatype, 3-17 BINARY_DOUBLE datatype, 3-5 BINARY_FLOAT datatype, 3-5 binding, bulk definition, 7-13 BLOB datatype, 3-17 body of package definition, 7-9 Boolean expressions, 3-27 bulk binding definition, 7-13 bulk binds, 7-12 DML statements, 7-13 FOR loops, 7-14 SELECT statements, 7-14 usage, 7-13 BY REF phrase, 14-24 BYTE qualifier for column lengths, 3-3
C
call specifications, 14-3 to 14-40 callbacks, 14-36 to 14-37 canceling a cursor, 2-8 CATPROC.SQL script, 9-39 century
Index-1
date format masks, 3-13 CGI variables, 11-13 CHAR qualifier for column lengths, 3-3 character data, 3-2 Character Large Object, 3-17 character literals quoting, 3-4 quoting with Q, 3-5 using in SQL statements, 3-4 CHARSETFORM property, 14-21 CHARSETID property, 14-21 CHECK constraint triggers and, 9-28, 9-32 check constraints how to use, 6-11 CHNF$_DESC type, 13-13 CHNF$_REG_INFO type using in database change registrations, 13-9 CHNF$_TDESC type, 13-14 client definition, 16-3 client events, 9-42 CLOB datatype, 3-17 column type attribute, PL/SQL, 1-4 columns accessing in triggers, 9-12 default values, 6-4, 7-37 generating derived values with triggers, 9-34 listing in an UPDATE trigger, 9-4, 9-14 multiple foreign key constraints, 6-9 number of CHECK constraints limit, 6-12 specifying length in bytes or characters, 3-3 COMMIT statement, 2-2 managing redo behavior, 2-3 COMMIT_WRITE initialization parameter, 2-3 compile-time errors, 7-24 compiling PL/SQL procedures to native code, 7-15 composite keys foreign, 6-6 restricting nulls in, 6-13 concurrency, 2-14 conditional expressions in WHERE clause, 3-22 conditional predicates trigger bodies, 9-10, 9-14 connection pooling, 1-17 consistency read-only transactions, 2-5 constraining tables, 9-17 constraints See integrity constraints context switches reducing with bulk binds, 7-12 converting data, 3-25 ANSI datatypes, 3-21 expression evaluation, 3-26 SQL/DS and DB2 datatypes, 3-22 cookies, 11-13 correlation names, 9-10 NEW, 9-12
OLD, 9-12 REFERENCING option and, 9-13 when preceded by a colon, 9-12 CREATE INDEX statement, 5-5 CREATE PACKAGE BODY statement, 7-11 CREATE PACKAGE statement, 7-11 CREATE TABLE statement defining integrity constraints, 6-13 INITRANS parameter in, 2-16 CREATE TRIGGER statement, 9-2 REFERENCING option, 9-13 cursor and cursor variable definitions, 2-6 cursors, 2-6 canceling, 2-8 closing, 2-7 declaring and opening cursor variables, 7-22 maximum number of, 2-6 pointers to, 7-22 private SQL areas and, 2-6
D
data conversion See converting data data dictionary compile-time errors, 7-25 integrity constraints in, 6-21 procedure source code, 7-26 data recovery using flashback features, 10-2 Database Change Notification creating a PL/SQL callback procedure, 13-8 interfaces, 13-8 interpreting notifications, 13-13 overview, 13-1 querying registrations, 13-12 registering database objects, 13-9 registrations, 13-5 scenario, 13-14 database links using in Oracle XA applications, 15-18 datatypes, 3-1 ANSI/ISO, 3-21 BINARY_DOUBLE, 3-5 BINARY_FLOAT, 3-5 CHAR, 3-2 column length, 3-3 character, 3-2 column lengths for character types, 3-3 conversion, 3-25 DB2, 3-21 LONG, 3-2 MDSYS.SDO_GEOMETRY, 3-16 native floating point, 3-6 native floating-point IEEE 754 exceptions not raised, 3-9 NCHAR, 3-2 numeric, 3-5 NVARCHAR2, 3-2 ROWID, 3-24
Index-2
SQL/DS, 3-21 VARCHAR2, 3-2 column length, 3-3 date and time data representing, 3-12 date arithmetic, 3-26 functions for, 3-14 DATE datatype, 3-12 DB_ROLE_CHANGE system manager event, 9-42 DB2 datatypes, 3-21 DBA_2PC_NEIGHBORS view, 15-17 DBA_2PC_PENDING view, 15-17 DBA_CHANGE_NOTIFICATION_REGS initialization parameter, 13-12 DBA_ERRORS view debugging stored procedures, 7-25 DBA_PENDING_TRANSACTIONS view, 15-17 DBA_SOURCE view, 7-26 DBMS_CHANGE_NOTIFICATION package using to create database change notifications, 13-11 DBMS_FLASHBACK package, 10-6 DBMS_LOB.GETCHUNKSIZE, 3-2 DBMS_LOCK package, 2-13 DBMS_RESUMABLE package handling suspended storage allocation, 2-28 DBMS_SQL package advantages of, 8-13 client-side programs, 8-13 DESCRIBE, 8-13 differences with native dynamic SQL, 8-9 DBMS_STATS package and Flashback Query, 10-12 DBMS_TYPES package, 3-19 DBMS_XMLGEN package, 3-18 DBMS_XMLQUERY package, 3-18 DBMS_XMLSAVE package, 3-18 DDL statements package state and, 7-12 DEBUG_EXTPROC package, 14-38 debugging stored procedures, 7-29 triggers, 9-21 dedicated external procedure agents, 14-5 default parameters in stored functions, 7-39 definer's-rights procedure, 7-34 DELETE statement column values and triggers, 9-12 data consistency, 2-8 triggers for referential integrity, 9-29, 9-30 denormal floating-point numbers, 3-8 dependencies among PL/SQL library objects, 7-15 in stored triggers, 9-20 schema objects trigger management, 9-17 timestamp model, 7-16 DESC function, 5-7 DETERMINISTIC keyword, 7-41 dictionary_obj_owner event attribute, 9-39 dictionary_obj_owner_list event attribute, 9-39
dictionary_obj_type event attribute, 9-39 disabled integrity constraint definition, 6-14 disabled trigger definition, 9-21 disabling integrity constraints, 6-15 triggers, 9-21 distributed databases referential integrity and, 6-11 remote stored procedures, 7-34, 7-35 triggers and, 9-17 distributed queries flashback features, 10-13 handling errors, 7-28 distributed transaction processing, 15-1 managing branches, 15-19 resource manager (RM), 15-2 transaction manager (TM), 15-2 transaction processing monitor (TPM), 15-3 TX interface, 15-3 distributed transactions branches, 15-2 managing in RAC, 15-19 managing with singleton services, 15-20 distributed update definition, 7-36 DML_LOCKS parameter, 2-8 double datatype, native in C and C++, 3-12 DROP INDEX statement, 5-4 DROP TRIGGER statement, 9-21 dropping indexes, 5-4 integrity constraints, 6-19 packages, 7-8 procedures, 7-8 triggers, 9-21 dynamic SQL application development languages, 8-1 invoker's rights, 8-6 invoking PL/SQL blocks, 8-5 optimization, 8-5 queries, 8-4 scenario, 8-7 usage, 8-2 dynamically typed data representing, 3-19
E
e-mail sending from PL/SQL, 11-10 embedded SQL, 7-2 enabled integrity constraint definition, 6-14 enabled trigger definition, 9-21 enabling integrity constraints, 6-15 triggers, 9-21
Index-3
errors application errors raised by Oracle Database packages, 7-26 remote procedures, 7-28 user-defined, 7-26, 7-27 event attribute functions, 9-38 event notification, 16-3 event publication, 9-37 to 9-38 triggering, 9-37 events attribute, 9-38 client, 9-42 resource manager, 9-41 tracking, 9-36 exception handlers in PL/SQL, 7-2 exception to a constraint, 6-15 exceptions during trigger execution, 9-14 effects on applications, 7-28 remote procedures, 7-28 unhandled, 7-28 exclusive locks LOCK TABLE statement, 2-11 explicit locks, 2-8 expression filtering, 3-22 expressions, conditional in WHERE clause, 3-22 extended ROWID format, 3-24 external LOB definition, 3-17 external procedure, 14-2 DBA tasks required, 14-5 DEBUG_EXTPROC package, 14-38 debugging, 14-38 definition, 14-2 maximum number of parameters, 14-40 passing parameters to C, 14-14 specifying datatypes, 14-15 extproc process, 14-5, 14-28
foreign key constraints defining, 6-20 enabling, 6-15, 6-19 NOT NULL constraint and, 6-8 one-to-many relationship, 6-8 one-to-n relationships, 6-8 UNIQUE key constraint and, 6-8 format masks TO_DATE function, 3-13 full-text search using Oracle9i Text, 3-18
G
geographic coordinate data representing, 3-16 global entity definition, 7-11 grantee event attribute, 9-39
H
HTML retrieving from PL/SQL, 11-11 HTP and HTF packages, 11-13 HTTP URLs, 11-11
I
IBM datatypes, 3-21 IEEE 754 standard for floating-point numbers, 3-6 image maps, 11-13 IN OUT parameter mode, 7-5 IN parameter mode, 7-5 indexes creating, 5-5 dropping, 5-4 function-based, 5-6 guidelines, 5-2 order of columns, 5-3 privileges, 5-5 SQL*Loader and, 5-2 temporary segments and, 5-2 when to create, 5-2 INDICATOR property, 14-20 -INF and +INF, 3-8 infinity values, 3-8 initialization parameters DML_LOCKS, 2-8 OPEN_CURSORS, 2-7 REMOTE_DEPENDENCIES_MODE, 7-20 INITRANS parameter, 2-16 INSERT statement column values and triggers, 9-12 read consistency, 2-8 instance recovery managing in RAC, 15-20 instance_num event attribute, 9-39 INSTEAD OF triggers, 9-6 on nested table view columns, 9-13 integrity constraints
F
features new, xxiii features, new, xxiii firing of triggers, 9-1 flashback features, 10-1 performance, 10-12 flashback privileges, 10-4 Flashback Query, 10-4 DBMS_STATS package, 10-12 Flashback Transaction Query, 10-10 Flashback Version Query, 10-8 FLASHBACK_TRANSACTION_QUERY view, 10-10 float datatype, native in C and C++, 3-12 floating-point numbers, 3-5 FOR EACH ROW clause, 9-9 FORALL statement using, 7-12
Index-4
CHECK, 6-11 composite UNIQUE keys, 6-5 defining, 6-13 disabling, 6-15, 6-16 dropping, 6-19 enabling, 6-14 examples, 6-2 exceptions to, 6-17 listing definitions of, 6-21 naming, 6-14 NOT NULL, 6-2 performance considerations, 6-2 PRIMARY KEY, 6-4 privileges required for creating, 6-14 renaming, 6-18 triggers vs., 9-1, 9-27 UNIQUE, 6-5 violations, 6-15 when to use, 6-1 interactive block execution, 7-32 INTERVAL DAY TO SECOND datatype, 3-12 INTERVAL YEAR TO MONTH datatype, 3-12 invoker's-rights procedure, 7-34 is_alter_column event attribute, 9-39 ISOLATION LEVEL changing, 2-16 SERIALIZABLE, 2-16
library units remote dependencies, 7-15 loadjava utility, 1-13 LOB datatypes, 1-29, 3-16 external definition, 3-17 support in OO4O, 1-28 use in triggers, 9-12 LOCK TABLE statement, 2-9 locks explicit, 2-8 LOCK TABLE statement, 2-9 privileges for manual acquirement, user, 2-13 UTLLOCKT.SQL script, 2-14 LONG datatype, 3-2 use in triggers, 9-17 LOWER function, 5-7
2-11
M
mail sending from PL/SQL, 11-10 main transaction definition, 2-21 manual locks, 2-8 LOCK TABLE statement, 2-9 match full rule for NULL values, 6-7 match partial rule for NULL values, 6-7 MDSYS.SDO_GEOMETRY datatype, 3-16 memory scalability, 7-46 modes parameter, 7-5 mutating table definition, 9-17 mutating tables trigger restrictions, 9-17
J
Java calling methods through call specifications, 14-3 compared to PL/SQL, 1-31 generating wrapper classes with JPublisher, 1-13 JDBC overview, 1-8 loading into the database, 14-3 SQLJ overview, 1-11 Java Server Pages translating to PSP, 12-3 Javascript translating to PSP, 12-3 JDBC See Oracle JDBC JScript translating to PSP, 12-3
N
National Character Set Large Object, 3-17 native dynamic SQL advantages of, 8-10 differences with DBMS_SQL package, 8-9 fetching into records, 8-12 performance, 8-11 user-defined types, 8-12 native execution of PL/SQL procedures, 7-15 native float and native double datatypes in C and C++, 3-12 native floating-point datatypes, 3-6 arithmetic, rounding behavior, 3-10 IEEE 754 exceptions not raised, 3-9 infinity values, 3-8 NCHAR datatype, 3-2 NCLOB datatype, 3-17 negative infinity value, 3-8 NEW correlation name, 9-12
K
keys foreign, 6-19 unique composite, 6-5
L
large data representing with LOBs, 3-16 Large Objects (LOBs), 3-16 libraries, 1-30
Index-5
new features, xxiii NLS_DATE_FORMAT parameter, 3-13 NLSSORT order, and indexes, 5-7 normalization of floating-point numbers, 3-8 NOT NULL constraint CHECK constraint and, 6-13 data integrity, 6-15 when to use, 6-2 notification, event, 16-3 NOWAIT option, 2-9 NUMBER datatype, 3-5 numeric datatypes, 3-5 NVARCHAR2 datatype, 3-2
O
OAS, 11-13 obfuscating PL/SQL code, 7-30 object columns, indexes on, 5-7 object support in OO4O, 1-28 OBJECT_VALUE pseudocolumn, 9-14 OCCI overview, 1-19 OCI, 7-2 applications, 7-3 cancelling cursors, 2-8 closing cursors, 2-7 comparison with precompilers, 1-30 overview, 1-19 OCIEnv function, 15-6 OCISvcCtx function, 15-6 OCITransCommit function, 2-4 OLD correlation name, 9-12 one-to-many relationship with foreign keys, 6-8 one-to-one relationship with foreign keys, 6-8 OO4O See Oracle Objects for OLE open string for Oracle XA, 15-7 OPEN_CURSORS parameter, 2-7 OR REPLACE clause for creating packages, 7-11 ora_dictionary_obj_owner event attribute, 9-39 ora_dictionary_obj_owner_list event attribute, 9-39 ora_dictionary_obj_type event attribute, 9-39 ora_grantee event attribute, 9-39 ora_instance_num event attribute, 9-39 ora_is_alter_column event, 9-39 ora_is_creating_nested_table event attribute, 9-40 ora_is_drop_column event attribute, 9-40 ora_is_servererror event attribute, 9-40 ora_login_user event attribute, 9-40 ora_privileges event attribute, 9-40 ora_revokee event attribute, 9-40 ORA_ROWSCN pseudocolumn, 10-7, 10-8 ora_server_error event attribute, 9-40 ora_sysevent event attribute, 9-40 ora_with_grant_option event attribute, 9-41 ORA-21301 error, fixing, 15-9
OraAQ object, 1-27 OraAQAgent object, 1-28 OraAQMsg object, 1-28 OraBFILE object, 1-29 OraBLOB object, 1-29 Oracle Application Server (OAS), 11-13 Oracle Call Interface See OCI Oracle Data Control (ODC), 1-29 Oracle Data Provider for .NET overview, 1-22 Oracle JDBC definition, 1-8 example, 1-10 OCI driver, 1-8 Oracle Database extensions, 1-9 server driver, 1-9 stored procedures, 1-15 thin driver, 1-8 Oracle JDeveloper definition, 1-12 Oracle JPublisher definition, 1-13 Oracle Objects for OLE automation server, 1-24 C++ Class Library, 1-29 LOB and object support, 1-28 object model, 1-25 overview, 1-23 Oracle Real Application Clusters managing instance recovery with Oracle XA library, 15-20 managing XA transaction branches, 15-19 using XA library, 15-19 Oracle SQLJ advantages over JDBC, 1-12 definition, 1-11 design, 1-12 in the server, 1-13 stored programs, 1-13 Oracle XA applications developing and installing, 15-6 managing transaction branches, 15-19 troubleshooting, 15-15 using database links, 15-18 Oracle XA library, 15-3 dynamic and static registration, 15-4 extension functions, 15-5 issues and restrictions, 15-18 managing instance recovery in RAC, 15-20 managing thread safety, 15-15 managing transaction control, 15-12 managing transactions in RAC, 15-19 monitoring XA transactions, 15-17 precompilers and OCI, 15-10 SQL-based restrictions, 15-21 trace files, 15-16 TX interface, 15-3 using with RAC, 15-19 xa_close subroutine, 15-5
Index-6
xa_commit subroutine, 15-5 xa_end subroutine, 15-5 xa_forget, 15-5 xa_open subroutine, 15-5 xa_prepare subroutine, 15-5 xa_recover, 15-5 xa_rollback subroutine, 15-5 xa_start subroutine, 15-5 OraCLOB object, 1-29 OraDatabase object, 1-26 OraDynaset object, 1-26 OraField object, 1-26 OraMDAttribute object, 1-27 OraMetaData object, 1-27 OraParamArray object, 1-27 OraParameter object, 1-27 OraServer object, 1-25 OraSession object, 1-25 OraSQLStmt object, 1-27 OUT parameter mode, 7-5 overloading packaged functions, 7-46 procedures and functions, definition, 7-9 using RESTRICT_REFERENCES, 7-46 OWA* packages, 11-13
P
package definition, 7-9 package body, 7-9 package specification, 7-9 packages, 1-30 creating, 7-11 DEBUG_EXTPROC, 14-38 dropping, 7-8 in PL/SQL, 7-9 naming of, 7-12 Oracle Database, 7-12 privileges for execution, 7-33 privileges required to create, 7-11 privileges required to create procedures in, 7-8 serially reusable packages, 7-46 session state and, 7-12 synonyms, 7-36 UTL_TCP, 11-10 where documented, 7-12 parallel server distributed locks, 2-8 PARALLEL_ENABLE keyword, 7-41 parameters default values, 7-7 with stored functions, 7-39 modes, 7-5 parse tree, 9-20 pcode when generated for triggers, 9-20 performance index column order, 5-3 native dynamic SQL, 8-11
permanent and temporary LOB instances, 3-17 PL/SQL, 7-1 anonymous blocks, 7-2 calling remote stored procedures, 7-35 compared to Java, 1-31 cursor variables, 7-22 dependencies among library units, 7-15 exception handlers, 7-2 functions arguments, 7-39 overloading, 7-46 parameter default values, 7-39 purity level, 7-46 RESTRICT_REFERENCES pragma, 7-43 using, 7-37 invoking with dynamic SQL, 8-5 objects, 1-5 overview, 1-2 packages, 7-9 program units, 7-1 RAISE statement, 7-27 serially reusable packages, 7-46 server pages, 12-14 tables, 7-6 of records, 7-6 trigger bodies, 9-10, 9-12 user-defined errors, 7-27 Web toolkit, 11-13 PLSQL_COMPILER_FLAGS initialization parameter, 7-15 positive infinity value, 3-8 POSIX Oracle support for regular expressions, 4-3 POSIX standard metacharacters in Oracle regular expressions, 4-4 posting, message definition, 16-4 pragma, 2-26 RESTRICT_REFERENCES, 7-43 SERIALLY_REUSABLE pragma, 7-46, 7-47 precompilers, 7-33 applications, 7-3 calling stored procedures and packages, 7-33 compared to OCI, 1-30 PRIMARY KEY constraints choosing a primary key, 6-4 disabling, 6-16 enabling, 6-15 multiple columns in, 6-5 UNIQUE key constraint vs., 6-5 private SQL areas cursors and, 2-6 privileges creating integrity constraints, 6-14 creating triggers, 9-19 dropping triggers, 9-21 flashback, 10-4 index creation, 5-5 manually acquiring locks, 2-11 recompiling triggers, 9-21
Index-7
stored procedure execution, 7-33 triggers, 9-19 Pro*C/C++ overview of application development, 1-16 Pro*COBOL overview of application development, 1-18 procedure external definition, 14-2 procedures called by triggers, 9-17 external, 14-2 program units in PL/SQL, 7-1 property CHARSETFORM, 14-21 CHARSETID, 14-21 INDICATOR, 14-20 pseudocolumns modifying views, 9-7 publish-subscribe, 16-1 to 16-5 purity of stored function, definition, 7-40
Q
queries dynamic, 8-4 errors in distributed queries, 7-28
R
RAISE statement, 7-27 RAISE_APPLICATION_ERROR procedure, 7-26 remote procedures, 7-28 raising exceptions triggers, 9-14 read-only transactions, 2-5 recovery, data, using flashback features, 10-2 REF column indexes on, 5-7 REFERENCING option, 9-13 referential integrity distributed databases and, 6-11 one-to-many relationship, 6-8 one-to-one relationship, 6-8 privileges required to create foreign keys, 6-20 self-referential constraints, 9-30 triggers and, 9-28 to 9-31 REGEXP_INSTR function description, 4-3 REGEXP_LIKE condition description, 4-2 REGEXP_REPLACE function description, 4-3 REGEXP_SUBSTR function description, 4-3 regular expressions operators, multilingual enhancements, 4-6 Oracle support for POSIX standard, 4-3 overview, 4-1 Perl-influenced extensions in Oracle
Database, 4-7 SQL functions and conditions, 4-2 regular expressions, Oracle implementation, 4-2 remote dependencies, 7-15 signatures, 7-16 specifying timestamps or signatures, 7-20 remote exception handling, 7-28, 9-15 remote queries flashback features, 10-13 REMOTE_DEPENDENCIES_MODE parameter, 7-20 repeatable reads, 2-5, 2-8 resource manager events, 9-41 resource manager (RM), 15-2 RESTRICT_REFERENCES pragma, 7-43 syntax for, 7-44 restrictions system triggers, 9-19 resumable storage allocation, 2-27 definition, 2-27 examples, 2-28 RETENTION GUARANTEE clause for undo tablespace, 10-4 reusable packages, 7-46 RNDS argument, 7-44 RNPS argument, 7-44 ROLLBACK statement, 2-4 rolling back transactions to savepoints, 2-4 routine autonomous scope definition, 2-21 external definition, 14-2 routines external, 14-2 service, 14-29 row locking manual, 2-12 row triggers defining, 9-9 REFERENCING option, 9-13 timing, 9-4 UPDATE statements and, 9-4, 9-14 ROWID datatype extended ROWID format, 3-24 rows violating integrity constraints, 6-15 rowtype attribute, PL/SQL, 1-4 ROWTYPE_MISMATCH exception, 7-24 RS locks LOCK TABLE statement, 2-9 run-time error handling, 7-26 RX locks LOCK TABLE statement, 2-9
S
S locks
Index-8
LOCK TABLE statement, 2-9 SAVEPOINT statement, 2-5 savepoints maximum number of, 2-5 rolling back to, 2-4 scalability serially reusable packages, 7-46 scope autonomous definition, 2-21 scrollable cursors, 1-17 search data representing, 3-18 SELECT statement AS OF clause, 10-4 FOR UPDATE clause, 2-12 read consistency, 2-8 VERSIONS BETWEEN...AND clause, 10-8 SERIALIZABLE option for ISOLATION LEVEL, 2-16 serializable transactions, 2-14 serially reusable PL/SQL packages, 7-46 SERIALLY_REUSABLE pragma, 7-47 service routine, 14-29 sessions package state and, 7-12 SET TRANSACTION statement, 2-6 ISOLATION LEVEL clause, 2-16 SERIALIZABLE, 2-16 share locks (S) LOCK TABLE statement, 2-9 share row exclusive locks (SRX) LOCK TABLE statement, 2-11 side effects, subprogram, 7-5, 7-40 signatures PL/SQL library unit dependencies, 7-15 to manage remote dependencies, 7-16 singleton services using to manage distribution transactions, 15-20 SORT_AREA_SIZE parameter index creation and, 5-2 sorting with function-based indexes, 5-6 specification part of package, definition, 7-9 SQL functions regular expressions, 4-2 SQL statements in trigger bodies, 9-12, 9-16 not allowed in triggers, 9-16 SQL*Loader indexes and, 5-2 SQL*Module applications, 7-3 SQL*Plus anonymous blocks, 7-3 compile-time errors, 7-24 invoking stored procedures, 7-32 loading a procedure, 7-7 SHOW ERRORS command, 7-25 SQL/DS datatypes, 3-21
SRX locks LOCK Table statement, 2-11 state package definition, 7-47 session, package objects, 7-12 Web application definition, 11-9 stateful and stateless user interfaces, definitions, statement triggers conditional code for statements, 9-14 row evaluation order, 9-5 specifying SQL statement, 9-3 timing, 9-4 trigger evaluation order, 9-5 UPDATE statements and, 9-4, 9-14 valid SQL statements, 9-16 storage allocation errors resuming execution after, 2-27 stored functions, 7-3 creating, 7-7 restrictions, 7-37 stored procedure, definition, 7-4 stored procedures, 7-3 argument values, 7-34 creating, 7-7 distributed query creation, 7-28 exceptions, 7-26, 7-27 invoking, 7-31 names of, 7-4 overloading names of, 7-9 parameter default values, 7-7 privileges, 7-33 remote, 7-34 remote objects and, 7-35 storing, 7-7 synonyms, 7-36 subnormal floating-point numbers, 3-8 synonyms stored procedures and packages, 7-36 SYS_XMLAGG function, 3-18 SYS_XMLGEN function, 3-18 system events attributes, 9-38 client, 9-42 resource manager, 9-41 tracking, 9-36
1-2
T
table, mutating definition, 9-17 tables constraining, 9-17 in PL/SQL, 7-6 mutating, 9-17 TCP/IP, 11-11 temporary and permanent LOB instances, temporary segments
3-17
Index-9
index creation and, 5-2 text search using Oracle9i Text, 3-18 thread safety managing with Oracle XA library, 15-15 time and date data representing, 3-12 TIMESTAMP datatype, 3-12 TIMESTAMP WITH LOCAL TIME ZONE datatype, 3-12 TIMESTAMP WITH TIME ZONE datatype, 3-12 timestamps PL/SQL library unit dependencies, 7-15 TO_DATE function, 3-13 trace files produced by Oracle XA library, 15-16 tracking system events, 9-36 transaction manager (TM), 15-2 transaction processing monitor (TPM), 15-3 transaction set consistency definition, 2-18 transaction, main definition, 2-21 transactions autonomous, 2-20, 2-26 committing, 2-3 read-only, 2-6 serializable, 2-14 SET TRANSACTION statement, 2-6 trigger disabled definition, 9-21 enabled definition, 9-21 triggering statement definition, 9-3 triggers about, 7-15 accessing column values, 9-12 AFTER, 9-4, 9-12, 9-24, 9-26 auditing with, 9-24, 9-25 BEFORE, 9-4, 9-12, 9-33, 9-34 body, 9-10, 9-14, 9-16 check constraints, 9-32, 9-33 client events, 9-42 column list in UPDATE, 9-4, 9-14 compiled, 9-20 conditional predicates, 9-10, 9-14 creating, 9-2, 9-16, 9-19 data access restrictions, 9-33 debugging, 9-21 designing, 9-1 disabling, 9-21 distributed query creation, 7-28 enabling, 9-21 error conditions and exceptions, 9-14 events, 9-3 examples, 9-23 to 9-34 firing, 9-1 FOR EACH ROW clause, 9-9
generating derived column values, 9-34 illegal SQL statements, 9-16 INSTEAD OF triggers, 9-6 integrity constraints vs., 9-1, 9-27 listing information about, 9-22 modifying, 9-21 multiple same type, 9-5 mutating tables and, 9-17 naming, 9-3 package variables and, 9-5 privileges, 9-19 to drop, 9-21 procedures and, 9-17 recompiling, 9-20 REFERENCING option, 9-13 referential integrity and, 9-28 to 9-31 remote dependencies and, 9-17 remote exceptions, 9-15 resource manager events, 9-41 restrictions, 9-10, 9-16 row, 9-9 row evaluation order, 9-5 scan order, 9-5 stored, 9-20 system triggers, 9-3 on DATABASE, 9-3 on SCHEMA, 9-3 trigger evaluation order, 9-5 use of LONG and LONG RAW datatypes, 9-17 username reported in, 9-19 WHEN clause, 9-10 triggers on object tables, 9-14 TRUST keyword, 7-45 two-phase commit protocol using with Oracle XA library, 15-3 TX interface, 15-3 type attribute, PL/SQL, 1-4
U
undo data, 10-1 unhandled exceptions, 7-28 UNINSTR function support for Unicode character literals, 3-4 UNIQUE key constraints combining with NOT NULL constraint, 6-4 composite keys and nulls, 6-5 disabling, 6-16 enabling, 6-15 PRIMARY KEY constraint vs., 6-5 when to use, 6-5 updatable view definition, 9-6 UPDATE statement column values and triggers, 9-12 data consistency, 2-8 triggers and, 9-4, 9-14 triggers for referential integrity, 9-29, 9-30 update, distributed definition, 7-36
Index-10
UPPER function, 5-7 URLs, 11-11 USER function, 6-3 user locks, requesting, 2-13 USER_CHANGE_NOTIFICATION_REGS initialization parameter, 13-12 USER_ERRORS view debugging stored procedures, 7-25 USER_SOURCE view, 7-26 user-defined errors, 7-26, 7-27 usernames as reported in a trigger, 9-19 UTL_HTTP package, 11-11 UTL_INADDR package, 11-10 UTL_SMTP package, 11-10 UTL_TCP package, 11-10 UTLLOCKT.SQL script, 2-14
xa_open subroutine, 15-5 xa_prepare subroutine, 15-5 xa_recover subroutine, 15-5 xa_rollback subroutine, 15-5 xa_start subroutine, 15-5 xaosterr function, 15-6 XML searching with Oracle9i Text, 3-18 XML data representing, 3-18 X/Open distributed transaction processing architecture, 15-1
V
V$GLOBAL_TRANSACTIONS view, 15-17 VARCHAR2 datatype, 3-2 column length, 3-3 VBScript translating to PSP, 12-3 VERSIONS_ENDSCN pseudocolumn, 10-9 VERSIONS_ENDTIME pseudocolumn, 10-9 VERSIONS_OPERATION pseudocolumn, 10-9 VERSIONS_STARTSCN pseudocolumn, 10-9 VERSIONS_STARTTIME pseudocolumn, 10-9 VERSIONS_XID pseudocolumn, 10-9 views containing expressions, 9-7 FLASHBACK_TRANSACTION_QUERY, 10-10 inherently modifiable, 9-6 modifiable, 9-6 pseudocolumns, 9-7
W
web services overview, 1-14 using Oracle Database as a service provider, 1-14 WHEN clause, 9-10 cannot contain PL/SQL expressions, 9-10 correlation names, 9-12 examples, 9-2, 9-9, 9-28 EXCEPTION examples, 9-15, 9-28, 9-32, 9-33 WITH CONTEXT clause, 14-25 WNDS argument, 7-44 WNPS argument, 7-44
X
X locks LOCK TABLE statement, 2-11 xa_close subroutine, 15-5 xa_commit subroutine, 15-5 xa_end subroutine, 15-5 xa_forget subroutine, 15-5 xa_open string, 15-7 Index-11
Index-12