The Concise Expert Guide to PL/SQL (preview) - CX Guide

12 downloads 131 Views 190KB Size Report
It aims to be concise by focusing on the aspects of PL/SQL which are the most useful ... Those taking Oracle 11g Advanced PL/SQL certification exam 1Z0-146.
This document is a preview from The Concise Expert Guide to PL/SQL .

Table of Contents Introduction Interacting with PL/SQL Compilation and Execution Language structures Datatypes Interaction with the database Stored Subprograms Functions Pipelined Functions Packages Triggers Runtime error handling Dynamic SQL LOB handling Collections Performance and Tuning PL/SQL debug, profiling and tracing DDL generation with DBMS_METADATA External Procedures PL/SQL Security The PL/SQL Gateway Oracle Objects Conclusion

Introduction This title of this book has been chosen carefully. It aims to be concise by focusing on the aspects of PL/SQL which are the most useful in practice, giving short and clear examples along the way. The book includes all major aspects of PL/SQL on Oracle 11g, starting from the basics, right through to the most advanced features of the language. This will enable you to become a PL/SQL expert. With PL/SQL, as with many other programming languages, there's usually more than one way to achieve the desired result. By knowing what features are available and learning how to use them effectively, this book will guide you towards taking full advantage of the power and speed of Oracle, which will help maximise your return on investment in the database. Who should read this book The people who will benefit most from this book are: • • • •

PL/SQL developers who wish to learn and use the advanced features of the language. Those taking Oracle 11g Advanced PL/SQL certification exam 1Z0-146. This book includes all items on the curriculum for 1Z0-146. People who support systems written using PL/SQL. Anyone tasked with Oracle application tuning and improving efficiency.

For those who are completely new to Oracle and PL/SQL, and who are looking for a more basic guide, I can recommend the Apress book "Beginning PL/SQL From Novice to Professional", written by Donald J. Bales. What you will need If you have access to an Oracle database, you will be able to run the demonstration code examples in this book. With 11g, all the samples will work. Most of the samples will run against earlier versions of Oracle as well, for although the language is continually being extended, the base functionality remains the same. Code examples are shown in the following typeface: BEGIN NULL; END; The code examples don't use line numbers, which means that all the code can be directly copied and pasted. In some cases, for the sake of clarity, skeleton or cut-down code samples have been used instead. Where database tables and other objects are important to illustrate an example, a SQL script to create them is included. All code samples can also be downloaded separately from the book's web site www.cxguide.net/ cegp. How the book is organized Chapters 1-6 cover the core functionality of PL/SQL, practicalities in different environments, what happens at runtime, and how to use SQL inside PL/SQL.

Most PL/SQL is delivered in the form of stored subprograms, either as standalone procedures and functions, or as logical groups in packages. These structures, including database triggers which are also most commonly written in a similar way, are the subject of chapters 7-11. The book then moves on to the details of exception handling and dynamic SQL. Chapter 14 covers LOB handling with the DBMS_LOB package. Chapter 15 describes PL/SQL's three built-in collection datatypes: VARRAYs, nested tables and associative arrays. Chapter 16 follows on by from this by focusing in detail on performance tuning techniques including bulk query and bulk DML, and then also on more recent Oracle features including native compilation and the result cache. Chapter 17 describes various debug, profiling and tracing utilities, including the Trace API, the Profiler API and - new in 11g - the Hierarchical profiler and PL/Scope. Chapter 18 de-mystifies the very powerful but complex metadata generation capabilities of the DBMS_METADATA package. Using external procedures as described in chapter 19 enables you to invoke software in other languages via PL/SQL. Security aspects of PL/SQL are covered in chapter 20, including how to create and use application contexts, establish a virtual private database (VPD), and how to prevent SQL injection attacks. Chapter 21 explains how the PL/SQL Gateway can turn the Oracle database into a web server, to deliver content straight from tables into a browser. The final chapter introduces PL/SQL's object oriented capabilities, with which you can write user-defined types which can inherit capabilities down a class hierarchy. About the author Ian Funnell is a consultant in Sydney, Australia, specializing in data warehousing. He has delivered solutions to many industries on most major database platforms including Oracle, SQL Server, DB2 and Teradata, and is also an expert in Java, Linux and open systems. Other than being an Oracle Certified Professional, the author has no past or present affiliation with Oracle Corporation. Copyright notice Copyright © 2011 Ian Clive Funnell All rights reserved. No part of the contents of this book may be reproduced or transmitted in any form or by any means without the prior written permission of the copyright owner. You may not copy, reproduce, distribute, publish, display, perform, modify, create derivative works, transmit, or in any way exploit the contents of this book, nor may you distribute any part over any network, including a local area network, sell or offer it for sale, or use such content to construct any kind of database. Disclaimer of warranty The information and software samples contained in this book are provided on an "as is" basis, without any express, statutory, or implied warranty. Although every care has been taken in the preparation of this book, neither the author, the publisher, Amazon, Oracle Corporation, nor its resellers or distributors shall have any liability to any person or entity with respect to any loss or damage caused or alleged to be caused directly or indirectly by the information or the software samples contained in this book or on the associated website www.cxguide.net. The example data, company, organizations, products, domain names, email addresses, people, places and events depicted in this book are fictitious. No association with any real company,

organization, product, domain name, email address, person, place or event is intended or should be inferred. Trademarked names may appear in this book. Rather than use a trademark symbol with every occurrence of a trademarked name, I use the names only in an editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the trademark. This book expresses the author's personal opinions. PL/SQL and Oracle Procedural Language / Structured Query Language is Oracle's proprietary 3GL extension to the SQL database language. This means that you can use procedural constructs such as conditional logic, variables, loops and subroutines, and also seamlessly incorporate ordinary SQL statements in PL/SQL code. PL/SQL is versatile, has been extensively optimised, and executes very quickly. It is the perfect language for controlling data movement within Oracle, for example when creating data warehouse ELT tasks, or when implementing business logic for an OLTP application. Using PL/SQL adds absolutely minimal overhead to the execution of SQL against the database. Some databases work best in set-based mode, and others are fundamentally designed to handle data row by row. PL/SQL helps Oracle span these two modes of operation. Version 1 of PL/SQL was introduced in Oracle 6, as part of Oracle Forms v3. Since Oracle 8, the PL/SQL version number has been kept in step with the database. PL/SQL's main way of interacting with the world is by issuing SQL statements, and much of this book is about how to do that as efficiently as possible. In order to work with PL/SQL you will need a way to interact with it, and that's the subject of the first chapter.

1 Interacting with PL/SQL This first chapter is about how to get started with writing PL/SQL code, and how to execute PL/ SQL that's already stored in the database. Oracle provides two basic ways to write and execute PL/SQL programs: SQL*Plus and SQL Developer. SQL*Plus is installed on all operating systems as part of the Oracle client, and you may have the choice of using a command line version or a window based version. SQL Developer is a standalone product which you can download, and which allows you to execute SQL statements in a more graphical environment. It goes far beyond SQL*Plus in scope, including some advanced functionality specifically aimed at PL/SQL developers. While developing and testing code you'll naturally want to get some feedback about what happens during execution, and so the last part of this chapter covers the various I/O capabilities that allow you to do this in PL/SQL. PL/SQL is the same regardless of what program or utility is used to write or execute it: for example there is no special version of PL/SQL for SQL*Plus, nor for any specific operating system. SQL*Plus and SQL Developer have no monopoly other than the guarantee that they will always be available on your Oracle installation. Pretty much any product that allows you to send SQL statements to the database is suitable for writing and using PL/SQL. As described in chapter 3 PL/SQL executes inside the Oracle database, so while it may look different in different environments, behind the scenes the same code will always do the same thing.

Using PL/SQL in SQL *Plus This section introduces some fundamental concepts and techniques by providing demonstrations and explanations in the context of SQL*Plus. If you have not used PL/SQL at all before this will also be important background material before moving on to looking at SQL Developer. The starting point is the simplest fragment of PL/SQL code that can be executed, which is known as an anonymous block. By introducing these first, this chapter will prepare you for the many code examples in this book, most of which are provided in the format of anonymous blocks. This section will then move on to describe how to launch PL/SQL code that's already been created, how to provide and retrieve data by using parameters and how to deal with function return values in SQL*Plus. The basic syntax for creating your own PL/SQL subprograms will be described, and finally a note on some security features that can be used in SQL*Plus. But first, in order to use PL/SQL from SQL*Plus, and before going any further in this chapter, you need to log on to the database in the ordinary way, and arrive at the SQL> prompt.

Anonymous blocks An "anonymous block" is a long name for a simple, un-named piece of PL/SQL code that you can write and use immediately. PL/SQL is fundamentally composed of blocks as chapter 4 will describe in detail, and this is one obvious feature which immediately differentiates PL/SQL code from SQL. SQL*Plus was originally intended for entering SQL statements interactively via the command line, but there are two keywords from PL/SQL's lexicon which cause SQL*Plus to enter "PL/SQL mode" and interpret the commands you supply as PL/SQL code rather than as SQL. In this mode a semicolon does not terminate the statement as it would with plain SQL: instead you must enter either: • •

a single dot (.) - to terminate the block without executing it a forward slash (/) - to terminate the block and also execute it

The first of these two keywords is BEGIN, which enables you to enter the simplest possible anonymous block: BEGIN NULL; END; . The final single dot (which must be completely on its own on a new line) causes SQL*Plus to terminate the PL/SQL code entry without executing it. SQL*Plus should silently accept the above sequence of statements, and the text you entered will remain in the command buffer just as any other SQL*Plus command normally does. If you want to now execute the block you've just typed in, type a single forward slash: / You should see the following happy message: PL/SQL procedure successfully completed. The forward slash causes SQL*Plus to terminate the block and also execute it, just as it does for ordinary SQL statements. You can repeat the execution by typing the forward slash again.

Technically you can also use 'RUN' but that SQL*Plus command is easier to get mixed up with the PL/SQL code, and is not recommended. The SQL*Plus LIST command can also be used to display the contents of the command buffer which in this case is a PL/SQL block. You can of course enter the PL/SQL anonymous block and execute it at the same time, just as you would normally do with an ordinary SQL statement, like this: BEGIN NULL; END; / The second of the two PL/SQL keywords which are meaningful to SQL*Plus is DECLARE. This keyword also signifies the starting point of an anonymous block - this time a slightly more complex one which includes the declaration of a PL/SQL variable. In the following example a numeric variable is declared: DECLARE v_val NUMBER; BEGIN NULL; END; / Other than the two new lines at the beginning, the rest of the anonymous block is the same as the earlier example and it behaves the same way in SQL*Plus, . This book includes many code samples like the anonymous blocks shown above, and for the sake of readability the forward slash is always assumed to be present, rather than being shown explicitly. Of course normally you don't want to enter PL/SQL code by typing it all in every time! Instead it is most common to store the code in a text file held in an operating system directory, and then use SQL*Plus @ command to run the statements in the file. This works the same as for just entering SQL statements, and you can also use the @@ or START commands which differ only in minor details concerning how they search for nested scripts.

Executing PL/SQL subprograms Most PL/SQL is not just entered once, executed immediately and then forgotten: it's held inside the database for repeated execution as a "stored subprogram". These objects are the main subject matter of chapters 7, 8, 9 and 10, but for now you need to know that to execute them from SQL*Plus, you should use the EXECUTE command. For example to turn on SQL trace for your current session you can execute one of Oracle's built-in stored subprograms: EXECUTE DBMS_SESSION.SESSION_TRACE_ENABLE or alternatively use the SQL*Plus shorthand, which is just EXEC: EXEC DBMS_SESSION.SESSION_TRACE_ENABLE You should again see SQL*Plus report PL/SQL procedure successfully completed. If you receive a PLS-00201 error instead then it's possible that •

you don't have the privilege to execute this subprogram - see chapter 7 on subprogram security for more information, or



you have mis-spelled the name of the code you're trying to call

PL/SQL code naming follows Oracle's standard for other objects, including prefixed schema names and database links named with an @ (more on this subject in chapter 6). EXECUTE is a SQL*Plus command and under the covers it takes the remainder of the line and builds its own anonymous block with your text inside it (including a semicolon statement terminator if you omitted it). So the trace commands shown above would actually result in the following PL/SQL being executed. BEGIN DBMS_SESSION.SESSION_TRACE_ENABLE; END; You could of course type in exactly that anonymous block instead, and it would work just the same.

Parameters Most PL/SQL subprograms require that you supply information at runtime, in the form of one or more parameters. You supply these in brackets following the name of the subprogram, for example as follows: EXEC DBMS_STATS.GATHER_TABLE_STATS(USER, 'MYTABLE') As the above example shows, you can make use of the functions such as USER which Oracle provide as part of the SQL language. Furthermore you are not restricted to plain SQL datatypes either: PL/SQL has many additional datatypes, described in chapter 5, such as Boolean, which can also be suppled as subprogram parameters - for example like this: EXEC DBMS_SESSION.SET_SQL_TRACE(true) In general if you omit the parameters or use the wrong number or datatypes the command will fail with a PLS-00306 error. Chapter 7 will describe some more sophisticated options that you can use for dealing with subprogram parameters.

Bind variables It's very common for subprograms to use parameters to send data back out to the caller. In this case it doesn't make sense to supply literal values as the parameters because the subprogram can't overwrite literal values with the data it wants to send back out. If you try to do this the subprogram call will fail with a PLS-00363 error. For example: EXEC DBMS_UTILITY.DB_VERSION('x', 'y') The above will fail with error PLS-00363: expression 'x' cannot be used as an assignment target. So to receive data back out from a subprogram parameter in SQL*Plus you need to use a variable instead of a literal value. To set up variables in SQL*Plus use the VARIABLE command. For example: VARIABLE v_version VARCHAR2(30) VARIABLE v_compat VARCHAR2(30) Having created the variables to hold the output values you can now call the procedure. The only thing to remember is that you must prefix the names of the variables with a colon so that SQL*Plus deals with them correctly as variables that it knows about. EXEC DBMS_UTILITY.DB_VERSION(:v_version, :v_compat) To display the values in SQL*Plus you can then do: PRINT

Or PRINT v_version Technically variables used in this way are known as "bind variables" and when SQL*Plus takes the value returned by the procedure and uses it to set the variable the process is known as "binding". There is much more on this subject in chapter 6. You should always remember that using bind variables is one of the best ways to get good performance from Oracle, especially when you write code which performs the same action over and over again just with different values each time. You can of course also use bind variables to supply data in to PL/SQL from SQL*Plus. Again you first need to set up the variable in SQL*Plus, for example like this: VARIABLE x NUMBER SQL*Plus does not allow you to initialize a variable while it is being declared. To assign a value you actually have to use a small piece of PL/SQL to do it, for example: EXEC :x := 100 Confusingly, SQL*Plus allows you to execute a SQL command which looks like it should assign the bind variable, but actually doesn't: SELECT 678 INTO :x FROM dual; The above statement has no effect at all on the bind variable X. You need to execute it in PL/SQL for it to work - for example as: EXEC SELECT 678 INTO :x FROM dual; Or (actually the same thing): BEGIN SELECT 678 INTO :x FROM dual; END; Having assigned a value to the SQL*Plus variable, you can now use it to supply the value to your PL/SQL subprogram parameter, for example: EXEC DBMS_RANDOM.SEED(:x) If you must use SQL*Plus variables as bind variables you'll be restricted to just the SQL datatypes. PL/SQL datatypes like Boolean are not permitted, and using an illegal type typically produces ORA-06550 and PLS-00382 errors in SQL*Plus. To take advantage of the full set of PL/SQL datatypes it's better to use an anonymous block which includes a declaration section, like this simple example: DECLARE v_flag BOOLEAN := TRUE; BEGIN DBMS_SESSION.SET_SQL_TRACE(v_flag); END; As well as subprogram parameters, SQL*Plus variables are often needed to store values returned from functions, as the next section will explain.

Functions PL/SQL makes a very clear distinction between procedures and functions. Functions return a value to the caller: procedures don't.

In fact PL/SQL functions can return values to the caller in all sorts of interesting ways as you will learn in chapters 9 and 15, but this chapter will concentrate on the more familiar end of the scale first. To use a simple example, the built-in function DBMS_RANDOM.VALUE returns a random number. But if you just invoke it with: EXEC DBMS_RANDOM.VALUE .. the call will fail with a PLS-00306 error "wrong number or types of arguments". It fails because VALUE is a function not a procedure, and you have to provide Oracle with somewhere to put the random number it will return. In this next example another SQL*Plus bind variable will be used to hold the returned number: VARIABLE n NUMBER EXEC :n := DBMS_RANDOM.VALUE Because PL/SQL is so tightly integrated with the Oracle database you can also use a PL/SQL function in a SQL statement like this: SELECT DBMS_RANDOM.VALUE FROM dual; You can also write your own "user-defined" functions ("UDFs") and do exactly the same thing, as chapter 8 will explain.

Writing stored PL/SQL So far this chapter has described how to use anonymous blocks, and how to execute stored subprograms that are already available inside the database. Much of this book is about creating your own stored subprograms, and this is also easy to do in SQL*Plus. A stored subprogram can be either a procedure (see chapter 7) or a function (see chapter 8). In either case they look rather like an anonymous block with a name. You create stored subprograms using the familiar SQL CREATE command, and Oracle have extended its syntax to encompass PL/ SQL. Entering the CREATE command in SQL*Plus causes it to change to PL/SQL mode in the same way as BEGIN and DECLARE do, as described earlier. You can then type in and create an entire stored subprogram via the command line; for example the simplest possible procedure looks like this. CREATE PROCEDURE myproc IS BEGIN NULL; END; SQL*Plus should respond tersely with: Procedure created. Note that the whole thing is actually one big SQL CREATE statement, and that apart from the name "myproc" on the first line the syntax is the same as an ordinary anonymous block. If you want to also declare variables you don't need to type a DECLARE line: the CREATE PROCEDURE acts as a substitute. Note that the above CREATE command only created (and compiled) the procedure in the database, and did not execute it. As long as the creation was successful you can now execute the procedure with an EXEC command: EXEC myproc

To create a stored function the steps are almost exactly the same, only the syntax is slightly different. The simplest possible function is structured like this: CREATE FUNCTION myfunc RETURN NUMBER IS BEGIN RETURN 99; END; SQL*Plus should confirm that the function was created cleanly: Function created. The new function returns a (hardcoded) value, and to call it you need to use either a bind variable as explained earlier, like this: VARIABLE v NUMBER EXEC :v := myfunc .. or else call it from a SQL statement: SELECT myfunc FROM dual; There are different SQL CREATE commands for creating the different types of PL/SQL in the database. They are: • • • • • •

CREATE FUNCTION (see chapter 8) CREATE LIBRARY (see chapter 19) CREATE PACKAGE [BODY] (see chapter 10) CREATE PROCEDURE (see chapter 7) CREATE TRIGGER (see chapter 11) CREATE TYPE (see chapters15 and 22)

All the CREATE commands work in the same way when SQL*Plus is executing a script, in "batch mode". The scripts can be externally version-controlled, and this is the usual way to store and distribute PL/SQL source code.

Storage and Compilation When you create stored PL/SQL, the database first stores the code in the data dictionary, and then subsequently also tries to compile it. If the compilation fails, the stored program is still created. It then exists within the database with an invalid status, and anything which tries to call it will fail with a runtime error. This is described fully in chapter 3. You cannot CREATE a stored PL/SQL subprogram if it already exists. For this reason it is most common to use CREATE commands with the "CREATE OR REPLACE" syntax, which causes Oracle to create the stored subprogram if it doesn't already exist or to update it if it does. An example of this syntax is: CREATE OR REPLACE PROCEDURE myproc ... The versatile SQL*Plus command DESCRIBE can be used to view stored subprogram definitions, for example: DESCRIBE myproc The output is like a summary of the statement that was originally used to CREATE the subprogram. DESCRIBE also works with whole packages but not unfortunately with individual packaged subprograms - for example you can describe DBMS_RANDOM and view all the subprograms

within this built-in package, but if you try to just describe DBMS_RANDOM.VALUE for example, it fails with an ORA-04043. If you're only human and your PL/SQL code doesn't compile cleanly first time, then you can view the compilation errors in SQL*Plus using the "SHOW" command. Most commonly you will need to just display the errors from the most recent compilation attempt, which you can do easily with: SHOW ERRORS Alternatively you can also name a specific stored subprogram to check for errors. This syntax works only if it has previously been compiled within the current SQL*Plus session. SHOW ERRORS PROCEDURE myproc In both cases, if there were no errors (or if myproc doesn't exist), then SQL*Plus just displays: No errors. The next chapter includes some additional information on locating compilation errors.

SQL*Plus security For security reasons you may need to disable the use of PL/SQL from SQL*Plus, which an administrator can do by adding records into the PRODUCT_USER_PROFILE table. For example, to prevent the use of anonymous blocks by any user, you can disable the BEGIN and DECLARE commands like this: INSERT INTO product_user_profile (product, userid, attribute, char_value) VALUES ('SQL*Plus','%','BEGIN','DISABLED'); INSERT INTO product_user_profile (product, userid, attribute, char_value) VALUES ('SQL*Plus','%','DECLARE','DISABLED'); Now all users are prevented from entering anonymous blocks via SQL*Plus, and will receive an SP2-0544 error instead. The mechanism works by simply disabling the SQL*Plus commands BEGIN and DECLARE, which prevents SQL*Plus from entering PL/SQL mode. Similarly, to prevent a user from launching any PL/SQL via SQL*Plus: INSERT INTO product_user_profile (product, userid, attribute, char_value) VALUES ('SQL*Plus','%','RUN','DISABLED'); INSERT INTO product_user_profile (product, userid, attribute, char_value) VALUES ('SQL*Plus','%','EXECUTE','DISABLED'); As before this works by simply disabling the RUN and EXECUTE SQL*Plus commands. To remove the restrictions imposed previously, an administrator can remove the records from SYSTEM's product_user_profile table, for example with: DELETE FROM system.product_user_profile WHERE userid = 'USER1'; Note that you must explicitly name the SYSTEM user in the above statement.

Using PL/SQL in SQL Developer Oracle's SQL Developer is a graphical interface to the Oracle database, providing a convenient way to perform various programming and administrative tasks. It can be thought of as a graphical version of SQL*Plus with extra features, and in particular the PL/SQL debugging facilities are excellent. SQL Developer is written in Java, so no installation is necessary except copying the files into a new directory on the target server or PC. This book is based on SQL Developer version 3.0 but almost everything described here is identical to how it appears in SQL Developer version 2.x.

Anonymous blocks in SQL Developer Anonymous blocks in SQL Developer are exactly the same as those in SQL*Plus - remember that PL/SQL is the same regardless of how it's being accessed. If you haven't used PL/SQL before then read the earlier section on anonymous blocks in SQL*Plus before continuing. After connecting to a database in the normal way you can type PL/SQL code straight into a SQL Worksheet. For example the simplest possible anonymous block: BEGIN NULL; END; You don't need forward-slash or dot terminators to tell SQL Developer where the PL/SQL code begins and ends, although to maintain compatibility with SQL*Plus you can leave them in the code if they are already there. To execute the code in the SQL Worksheet you press F9, or CTRLENTER, or the big green Run button. You can use F5 instead but if you have more than one SQL statement in the window then you need to first highlight just the code that you want to run. SQL Developer should open a Script Output pane and display: anonymous block completed SQL Developer creates its own anonymous blocks internally too, as you will see in the next section.

Managing PL/SQL subprograms SQL Developer lists all the top-level schema objects in the Connections pane on the left-hand side of its display, and PL/SQL objects are among them. There are separate lists for Packages, Procedures, Functions, Triggers and Types. To manage a PL/SQL object you must first locate it in the Connections pane, and then right-click to view the following options: • • • •

run edit (which opens a new window in the detail pane) compile / compile debug profile (using the Hierarchical Profiler DBMS_HPROF)

If you left- or double-click a PL/SQL object, SQL Developer will open a new editor pane alongside the SQL Worksheet containing the source code for the object. Editing code When SQL Developer opens a PL/SQL editor pane it initially shows the current source code for the object, as a CREATE OR REPLACE statement. You can edit the code right there if you want, or else copy and paste into a SQL Worksheet and make the changes there. From the code editor you can also use the small icons at the top to compile, run and debug the code.

Compiling code Code is compiled when you execute a CREATE statement and also when you use the "compile" icon at the top of SQL Developer's code editor pane. You can also reach this option by rightclicking the chosen object in the Connections pane. When SQL Developer compiles a stored subprogram, it captures any compilation errors and makes them available in the Compiler panel of the Log window. There is much more information on compilation errors in the next chapter, but from SQL Developer there is a convenient shortcut in the compilation errors window - when you double click on one of the errors it re-positions the cursor on the source code line which caused the error. If there are no compilation errors SQL Developer will open its Messages log pane and display Compiled You are now ready to run your code. Running code To run a PL/SQL stored subprogram from SQL Developer, you can highlight it in the Connections pane and choose "Run" from the right-click popup menu. Alternatively, you can left- or doubleclick to open the source code editor, and then use the editor's own "run" icon. SQL Developer then opens a "Run PL/SQL" dialog containing an anonymous block which allows you to supply any required parameters. The anonymous block looks very much like the SQL*Plus example shown earlier in this chapter, using PL/SQL variables to hold the subprogram parameters and (if it's a function) the returned value. You can edit the parameters in the generated anonymous block as required, then press OK. The parameters are cached so they will be re-used the next time you run this stored subprogram. The dialog box still pops up if there are no parameters, and can be left empty. The stored subprogram runs, and when finished the log window opens automatically. If you have selected a packaged subprogram, the "Run PL/SQL" dialog allows you to choose which subprogram to run from the package (see chapter 10 for much more details on PL/SQL packages). Writing new stored subprograms There are two common ways to do this in SQL Developer. • •

Use a SQL Worksheet and just type the CREATE statement and the code. Use a wizard.

Option 1 is exactly the same as creating new stored subprograms in SQL*Plus as described earlier in this chapter. To use the SQL Developer wizard, go to the Connections pane on the left, highlight the type of object you want to create (i.e. Procedure / Function / Package) node, and select New from the right-click context menu. This opens a wizard to create the new object, inside which you can conveniently also see the code that will be generated. When you press OK or Save the newly generated code will appear in a new editor pane. The new object isn't created until you compile the object in this pane. To create a package (see chapter 10) you need to first create the package specification, which you can do using the wizard as described in the previous paragraph (although you still have to type in the subprogram declarations that you want to create). After it has been compiled the package should appear under the object navigator. Locate it, then right-click the package name, and choose "Create

Body" to fill the implementation details of the package body. This opens a package body editor with conveniently pre-filled stubs for all the subprograms that you defined in the specification.

SQL Developer's PL/SQL reports SQL Developer includes many built-in reports, some of which are specifically concerned with PL/ SQL. The reports are listed in the "Reports" tab next to "Connections" in the navigation pane on the left-hand side. To access them you can either use the menu View / Reports, or else double-click the report folder icon, which is initially collapsed. The built-in PL/SQL reports are listed under "Data Dictionary Reports" and then "PL/SQL", and they include: • • •

Program Unit Arguments - which queries ALL_ARGUMENTS to show subprogram parameters. Search Source Code - which queries ALL_SOURCE for specified string. Unit Line Counts - which queries ALL_SOURCE for a quick summary of the number of lines in all the user's PL/SQL source code.

When running the reports, you can also view the SQL to see which data dictionary tables underpin the query. This is a good way to develop an understanding of the structure of the data dictionary. Later in this book chapter 7 contains much more information on the PL/SQL metadata contained in the data dictionary.

Using SQL Developer to debug PL/SQL This is a very powerful capability for use during PL/SQL development, which among other things enables you to execute PL/SQL code line by line to check exactly what happens at runtime. SQL Developer will automatically open new code editor windows and flick between them if one subprogram calls another while it's running. When debugging code it's easiest to do everything from within a single SQL Developer session. However if necessary you can initiate a debug run from a completely separate database session even on a different host. This technique will discussed at the end of the section. There are several prerequisites for using SQL Developer's debug capabilities: •







The system privileges DEBUG CONNECT SESSION and DEBUG ANY PROCEDURE are needed, otherwise the debugger just fails to launch, with an ORA-01031 "insufficient privileges" error. An additional object privilege DEBUG (or EXECUTE) is needed in order to view/modify variables, manage breakpoints, and execute lines of code. Note that the DEBUG privilege is equivalent to enabling anything that the subprogram which is being debugged could have done in its original code. The PL/SQL must have been previously compiled in debug mode. If this was not done the debugger just silently runs the code to completion without stopping at any breakpoints, exactly as it would do when running normally. SQL Developer attempts to communicate with the database server using a non standard port so you will need to ensure that the communication isn't blocked by a firewall.

Internally SQL Developer uses the built-in package DBMS_DEBUG to manage debug sessions. You will not need to use DBMS_DEBUG manually.

Debugging within SQL Developer First of all you must go to the Connections pane and locate the subprogram to be debugged, then click to open it in a code editor.

Ensure that the code has been compiled in debug mode. You can do this in the code editor pane by selecting the "Compile for Debug" icon, which is also an option when you right click a subprogram in the Connections pane. Code compiled in debug mode is highlighted in the tree with a green icon. You must also set at least one breakpoint in the code, and when the debug run is started execution will halt immediately before the first breakpoint. To set breakpoints in the code editor window, either click in the left margin or press F5. Having done these two things you may now start the debug run. In the code editor press the Debug icon (which looks like a ladybird), and after supplying parameters the code will begin to run. The Run Manager window should appear and show execution in progress, and the currently executing line should be highlighted in blue. Once running in this mode the execution will automatically stop at every breakpoint, just before the line of code is executed. The "Debugging - Log" tab will be available, and contains the debug toolbar and messages. The controls here are: • • • • • • • •

Find Execution Point - go to the next line to be executed (e.g. the current breakpoint). Step Over - execute the statement, and if it's a subprogram call run the subprogram without stopping. Step Into - execute the statement, and if it's a subprogram call then step into the first line of the subprogram and stop again. Step Out - useful if within a subprogram: finishes it and resumes at the next statement Step to End of Method - go straight to last statement of current subprogram Resume - resume execution Pause - force stop during PL/SQL execution, even if no breakpoint set Terminate - quit the session

During a debug run you can also view variables in the code, by choosing the View / Debugger / Data menu. This opens up the Data tab in the debugger window. It's not just restricted to simple variables: a really useful feature is that it's possible to expand records (and tables of records - see chapter 15), and view all the data in those complex structures. You can choose to define a "watch" expression (e.g. j+1) in the "Debugging - Log" under "watches". The watch expression must be based on variables in the program. The "Smart Data" tab is the same as the "Data" tab, except it only shows data items which have recently been altered.

Debugging a different session This is very similar to debugging completely within SQL Developer except that the code to be debugged is launched from a different session. Typically this is required when you can't launch the code from SQL Developer itself. So in this mode the code is running in a remote session (perhaps via SQL*Plus on a different host), while the execution in debug mode is controlled graphically by SQL Developer in exactly the same way as described before. Some preparation is necessary for this, to allow the two sessions to communicate. First, in SQL Developer, set up a debug listener by right-clicking the connection and choosing the "Remote Debug" option. In the "Listen for JPDA" window which pops up you must set the port (default 4000), the timeout (default 0 meaning no timeout) and the local address, which can be an IP address. Be careful to get this right if your host has more than one network adapter or is running virtual machines. After doing this, you should be able to see the "debug listener" waiting in SQL Developer's Run Manager.

As before, the code must have been compiled in debug mode, and be sure to set at least one breakpoint or else the debugger will just launch and run to completion without stopping! To launch the code from the remote session you must do three things: • • •

Use DBMS_DEBUG_JDWP to connect to the SQL Developer instance which is listening for connections. Run the code Use DBMS_DEBUG_JDWP to disconnect the debug session.

These can be accomplished with a script such as this, which connects to SQL Developer running on a host named host1, and listening on port 4000: EXEC DBMS_DEBUG_JDWP.CONNECT_TCP('host1',4000) EXEC my_subprogram EXEC DBMS_DEBUG_JDWP.DISCONNECT If everything is working correctly, the open Run Manager should detect the remote session connecting and show this immediately. Once the PL/SQL (my_subprogram in the above example) has been started, SQL Developer will fully control execution and will allow you to step through the lines as the code runs. Again you may need to check the firewall policy if the connections aren't being made correctly.

PL/SQL I/O This chapter has covered a lot of ground already, but there has been no "Hello, world" example yet. There's a good reason for this glaring omission, and it's to do with the concepts of "standard input" and "standard output". Most software that you interact with has some kind of visual or sensory interface - usually a window or a terminal on a screen. This is what makes the software interactive, and is why for example SQL*Plus and SQL Developer are good tools for writing and debugging PL/SQL, because you can get immediate feedback from the command line or the graphical interface. In contrast to this, PL/SQL is a database language, designed to execute inside Oracle where there might be no associated screen or terminal. For example PL/SQL code can be invoked from a scheduler, by a database trigger, or as part of an invisible batch process. In these cases there is no screen that could be used to display the output from a "print" statement, and no keyboard that could be used to capture input from a user. As a consequence of this possible diversity, PL/SQL does not include any direct way to "print" information synchronously to a terminal. However, as you will see in the very next section DBMS_OUTPUT is a commonly used built-in feature which does almost exactly the same thing. DBMS_OUTPUT is just one of a number of APIs supplied by Oracle which enable PL/SQL to perform I/O in different ways. These APIs include: • • • • •

DBMS_OUTPUT (which can print text to a screen; most suitable for environments like SQL*Plus and SQL Developer) UTL_FILE - for reading and writing files DBMS_PIPE (for opening a programmatic pipe between processes) HTF and HTP (for serving HTML) UTL_HTTP (retrieving web pages) and UTL_SMTP (email)

PL/SQL can also perform I/O via stored subprogram parameters and with function return values. But during code development, probably the simplest and the most common technique is to use DBMS_OUTPUT to provide basic capabilities such as printing log and debug messages.

DBMS_OUTPUT There is no concept of a "screen" in PL/SQL itself, so writing text to an output device is a two-stage process. When you call DBMS_OUTPUT from a subprogram the text is first sent to a buffer in SGA memory. When the PL/SQL program has finished the caller resumes the thread of control, and can then retrieve the text from the SGA and display it on whatever output device it happens to be using. The interface for retrieving text from the SGA buffer is DBMS_OUTPUT.GET_LINES, and in fact this is one extra thing that the SQL*Plus EXEC command does automatically on your behalf. SQL*Plus does always have an associated terminal since it's designed as an interactive program, so after running the PL/SQL code you specify it also does a DBMS_OUTPUT.GET_LINES(...) .. in an anonymous block at the end. Splitting the output into two phases in this way has a couple of important consequences for DBMS_OUTPUT: •

The call to GET_LINES can only be performed after the PL/SQL job has finished, so you will not see any output at all from a long-running program until the whole program has finished.



There is no way to "flush" the output programmatically. DBMS_OUTPUT is therefore not a good way to try and monitor long-running PL/SQL programs during execution. The SGA buffer is session-specific, and sessions cannot share output. If a session ends before its DBMS_OUTPUT buffer has been read, then any text in the buffer is simply lost as the buffer is returned to free space. This is what happens when there is no screen associated with a session: any calls to DBMS_OUTPUT silently have no effect.

Different software can render the DBMS_OUTPUT text in whatever way it chooses. For SQL*Plus the text just goes to the terminal's standard output and so can become mixed up with everything else that gets printed. Graphical tools such as SQL Developer often render the DBMS_OUTPUT text into sub-windows or panes within their own display. There are also some quirks that can cause errors from an unexpected angle at runtime. In Oracle 9 the maximum allowable line length was just 255 characters, and has been increased since. So, when trying to output long lines using an Oracle 9 client version of SQL*Plus from a later database version, it is possible for DBMS_OUTPUT.PUT_LINE to fail with the error ORA-06502: PL/SQL: numeric or value error: host bind array too small. One extra thing to remember is that you always need to enable DBMS_OUTPUT in your client tool, otherwise calls to the I/O procedures are silently ignored. Enabling DBMS_OUTPUT To enable DBMS_OUTPUT in command line environments like SQL*Plus and SQL Developer you just need to enter the statement SET

SERVEROUTPUT

ON

This statement activates the memory buffer, which has an unlimited size with memory only being allocated when it's actually needed. In non command line environments you can call: DBMS_OUTPUT.ENABLE For backwards compatibility with earlier releases of Oracle you can also supply a buffer size parameter. This helps ensure that scripts written a long time ago continue to work, but it's not necessary in 11g. Formatting and writing DBMS_OUTPUT The basic way to write a line of text is to call PUT_LINE, which writes your text plus a line terminator, or to call PUT, which writes a partial line. Examples are: DBMS_OUTPUT.PUT_LINE('Hello, world'); DBMS_OUTPUT.PUT('Part of a line'); You can add an end-of-line manually with DBMS_OUTPUT.NEW_LINE; If the buffer size or single line length is exceeded, the error ORU-10027: buffer overflow / ORU-10028: Line length overflow is generated. When switching on DBMS_OUTPUT there are various options for wrapping text or words around line length limits: SET SERVEROUTPUT ON FORMAT WRAP SET SERVEROUTPUT ON FORMAT WORD_WRAPPED Or you can also truncate text at the line limit if you want:

SET SERVEROUTPUT ON FORMAT TRUNCATED Reading from DBMS_OUTPUT Remember that SQL*Plus and SQL Developer automatically call GET_LINES after executing every SQL statement or anonymous PL/SQL block, so if you're using these tools you won't ever have to manually read from DBMS_OUTPUT. But if you do need to do it, you can also retrieve lines from the buffer using GET_LINE (which uses the built-in DBMS_OUTPUT.CHARARR datatype) and GET_LINES (which uses the DBMS_OUTPUT_LINESARRAY datatype). DBMS_OUTPUT always strips leading spaces from lines of text, which can be annoying if you're trying to produce output which includes indentation. Unfortunately there is no built-in equivalent to "printf", so you have to do all non-character formatting yourself using format masks with the TO_CHAR function. If you have a need to produce carefully formatted output you would probably be better off using UTL_FILE which offers more control, and which is described next.

UTL_FILE As its name suggests, Oracle provide the built-in package UTL_FILE to enable you to perform I/O on operating system files. Like any other PL/SQL subprogram, UTL_FILE runs inside the Oracle database, and so it's the database which actually interacts with the operating system to perform the file I/O on behalf of your code. UTL_FILE can therefore only act on files which are accessible from the server where Oracle is running. If you're connecting to Oracle from a different machine across a network, UTL_FILE can not read or write files on your client machine. I/O security In versions since Oracle 10g the DIRECTORY feature has replaced the UTL_FILE_DIR database parameter, which was previously used to list all the writeable directories. All access to files is via Oracle DIRECTORY objects, which have special READ and WRITE object privileges that can be granted. Whenever you quote a directory name it must exactly match the DIRECTORY_NAME in ALL_DIRECTORIES, otherwise the error ORA-29280: invalid directory path occurs. Normally this means you must supply literal directory names in uppercase. The UTL_FILE API The UTL_FILE API is similar to the standard I/O library functions in the C language and contains all the stored procedures and functions that you can use to perform I/O. UTL_FILE makes use of a file datatype called UTL_FILE.FILE_TYPE. You can declare a file variable like this: v_file UTL_FILE.FILE_TYPE; To open a file for writing, Three basic I/O modes are available: "text", "Unicode" and "binary" (also sometimes called "raw"). The mode is denoted by a character string literal with only a small set of allowable values: 'r' (read-only), 'w' (write) or 'a' (append) for text and Unicode, or the equivalent binary modes 'rb', 'wb' and 'ab'. For text and binary mode you need to use UTL_FILE.FOPEN, and for Unicode mode you must use FOPEN_NCHAR. In text mode the output is written in the database character set. If you try to write Unicode data in text mode the runtime error ORA-29298 "character set mismatch" will occur. The three available modes can be summarised as follows: •

Text, using FOPEN 'r/w/a', and then calling any of PUT, PUT_LINE or PUTF

• •

Binary, using FOPEN 'rb/rw/ra', and then calling PUT_RAW Unicode, using FOPEN_NCHAR 'r/w/a', and then calling any of PUT_NCHAR, PUT_LINE_NCHAR or PUTF_NCHAR

A character mode example is: v_file := UTL_FILE.FOPEN('DIR_NAME', 'filename', 'w'); A binary mode example is: v_file := UTL_FILE.FOPEN('DIR_NAME', 'filename', 'wb'); The default buffer size is 1024, but you can change the default value, up to a maximum of 32767, as follows: v_file := UTL_FILE.FOPEN('DIR_NAME', 'filename', 'wb', 32767); Writing output data For text, some limited string formatting is available, along the lines of the C library printf function. Only the markers %s and \n are allowed, up to a maximum of five parameters. Writing a string can be done for example like this: UTL_FILE.PUTF(fOut, '%s\n', v_app_data); To writing a binary or raw buffer, in wb mode, you would call: UTL_FILE.PUT_RAW(fOut, rbuf); Unlike DBMS_OUTPUT you can flush the output buffer at any time, which causes it to be physically written to the output file: UTL_FILE.FFLUSH(fOut); The following fragment is an example writing Unicode characters, which includes an embedded Unicode character referenced by its code point number: fOut := UTL_FILE.FOPEN_NCHAR('XFER', 'utf.txt', 'w'); UTL_FILE.PUT_NCHAR(fOut, n'abc£'); UTL_FILE.PUT_NCHAR(fOut, UNISTR('abc\0061abc')); Files should always be closed after use, like this: UTL_FILE.FCLOSE(f_file); Buffering in text / binary mode All UTL_FILE I/O is normally buffered, so it's possible to receive a WRITE_ERROR exception on output if the buffer is not flushed soon enough. Buffering behaves slightly differently between files that were opened in "text" mode and in "binary" mode. In "binary" mode (wb), you are only allowed to call PUT_RAW. The error ORA-29283 INVALID_OPERATION is thrown if you attempt to write any character data using the other functions in the package. Also you must call UTL_FILE.FFLUSH to periodically flush the buffer to the output file. In "text" mode (w), PUT_RAW does work but UTL_FILE.FFLUSH silently does nothing unless there happens to be a newline in the RAW buffer. This means that a WRITE_ERROR will soon occur if you try to write more than one buffer full of data. I/O Exceptions Exception handling is fully described in chapter 12 and UTL_FILE exception handling follows the standard patterns described in that chapter. The UTL_FILE package contains some named

exceptions which are specific to I/O and which can help you to more precisely handle different types of error. So typically an exception handler for I/O problems will look something like the following: EXCEPTION WHEN UTL_FILE.INVALID_PATH THEN ... WHEN UTL_FILE.INVALID_MODE THEN ... WHEN UTL_FILE.INVALID_FILEHANDLE THEN ... WHEN UTL_FILE.INVALID_OPERATION THEN ... WHEN UTL_FILE.INTERNAL_ERROR THEN ... WHEN OTHERS THEN ... general purpose error handler

PL/SQL in other environments When you need to invoke a PL/SQL subprogram from a product other than SQL*Plus or SQL Developer you will probably need to use the SQL statement "CALL" rather than the SQL*Plus command "EXECUTE". EXECUTE is a SQL*Plus helper command which creates an anonymous block for you, and also calls DBMS_OUTPUT.GET_LINES to retrieve DBMS_OUTPUT text when the invoked subprogram has finished. CALL is a real SQL statement that can be used in any Oracle client - including SQL*Plus and SQL Developer of course. As well as running stored PL/SQL subprograms, CALL is useful in dynamic SQL as you will see in chapter 13. When using CALL the syntax is very similar to the examples shown earlier in this chapter which use EXECUTE. There are some minor differences though: CALL always requires brackets even if there are no parameters. And being a SQL command, it also requires a semicolon terminator. A couple of examples are: CALL my_procedure(179, 1000); CALL other_proc(); You can also use CALL to launch stored functions, subject to the same rules described earlier concerning functions in SQL*Plus. Functions return a value to the caller and you need somewhere to hold this returned value. In SQL*Plus you would use a bind variable like this: VARIABLE v_rc NUMBER CALL my_func() INTO :v_rc; In other languages bind variables may not always be represented by a colon; in Java for example you must use a ? character instead. But in any case you should always try to use bind variables regardless of what environment you're using to invoke PL/SQL. In summary, you may not be developing PL/SQL in other environments, but you can certainly call subprograms from other languages and environments.

Calling subprograms from Java To call functions or procedures you will need to use a JDBC CallableStatement. In terms of the CallableStatement the only syntactical difference between calling a procedure and calling a function is that for a function its return value must be captured. You can call a function using a CallableStatement syntax like this... ? = call func_name .. whereas for a procedure you can just use: call proc_name A fragment of Java code for calling a numeric function named MY_FUNC, which requires a number of parameters could look like this: java.sql.CallableStatement csFunc; java.sql.Connection conn; csFunc = conn.prepareCall("{? = call my_func(0,0,?,?,'Info',TO_NUMBER(NULL),?,?)}" csFunc.clearParameters(); csFunc.registerOutParameter(1, java.sql.Types.INTEGER); csFunc.setString(2, );

... csFunc.execute(); = csFunc.getInt(1); csFunc.close(); Note that the first "out" parameter which is bound to a Java bind variable is the function return code itself. Similarly, a fragment of Java code to call the procedure MY_PROC could look like this: java.sql.CallableStatement csFunc; java.sql.Connection conn; java.sql.CallableStatement csProc; csProc = conn.prepareCall("{call my_proc('Text',?,SYSDATE,?,?)}"); csProc.clearParameters(); csProc.setString(1, ); ... csProc.registerOutParameter(2, java.sql.Types.VARCHAR); csProc.registerOutParameter(3, java.sql.Types.VARCHAR); csProc.execute(); = csProc.getString(2); = csProc.getString(3); csProc.close(); Note in both the examples that the parameters are associated with Java bind variables by order, and are numbered sequentially starting at 1. The return value from a function is in position 1.

Calling subprograms from Pro *C In Pro*C you can use an anonymous block to encompass whatever PL/SQL code is needed. You just need to surround the PL/SQL block with "EXEC SQL EXECUTE" and "END-EXEC". Standard indicator variables can be added for null handling if necessary. A fragment of code to invoke a packaged function could look something like this: EXEC SQL EXECUTE BEGIN :v_week_end_date:i_week_end_date := TO_CHAR(my_package.get_financial_date( TO_DATE(:v_run_date:i_run_date, 'DDMMYYYY'), 'WEEK_END'), 'DDMMYYYY'); END; END-EXEC; In the above example the bind variables v_week_end_date and v_run_date are signified using colon notation.

2 Compilation and Execution PL/SQL code must be compiled before it can be executed. The Oracle database includes a PL/SQL compiler which is invoked automatically whenever you submit source code to the database. The compiler is also activated automatically when certain changes are made to objects in the database, as this chapter will describe. You'll learn in detail here exactly what the compiler does, and what happens when PL/SQL code is executed at runtime. This chapter will then describe how to interact with the compiler and locate errors The various conditional compilation techniques available in PL/SQL round off the chapter. The PL/SQL compiler converts source code into a special bytecode which is designed to be executed on a virtual machine (or "PVM") within the database server. In fact there are actually two stages to PL/SQL compilation: • •

the compiler "front-end" generates "DIANA" code (Descriptive Intermediate Attributed Notation for Ada). the compiler "back end" uses the DIANA to generate "machine-readable" Mcode for the PL/ SQL virtual machine.

The DIANA contains information about the interface to the subprogram and is used in dependency tracking. It's also used by the wrap utility as you'll see in chapter 20. The Mcode is read by the PVM, which translates it again into the appropriate functions in the Oracle libraries, which are executed ultimately by the operating system. In "native" compilation the PVM is actually removed from the picture altogether, and the PL/SQL compiler directly generates operating system-specific executable code instead of Mcode. This removes one layer of abstraction at runtime and can make PL/SQL execute faster. Oracle 11g brings many simplifications to native compilation over earlier releases and chapter 16 describes how to take advantage of this feature. SQL is very tightly integrated with PL/SQL in many ways, but not so in terms of compilation. SQL statements embedded within PL/SQL remain intact in the bytecode, and execute exactly as they would do outside of PL/SQL. In this way PL/SQL is able to take full advantage of the database's ordinary SQL handling capabilities, as chapter 6 will describe more fully. Most of the examples shown so far have been anonymous blocks, as in fact are most of the examples throughout this book. In terms of compilation anonymous blocks are actually quite a special case because they are always compiled once and then (in the absence of errors) just executed immediately, once only. When you execute an anonymous block a second time it's just compiled again from scratch. For stored PL/SQL code things are a lot more interesting! This chapter will first look at runtime considerations for stored PL/SQL, before moving on to describe how PL/SQL code can become invalidated and recompiled both manually and automatically.

Runtime execution and memory usage PL/SQL code runs in a virtual machine located within the database server. Some versions of Oracle Forms contain a PL/SQL engine to run PL/SQL code on the client, but that's beyond the scope of this book. When a piece of PL/SQL code is executed for the first time the bytecode is retrieved and placed into the library cache memory area in the shared pool. From there it can be accessed much more quickly the second time it's called - provided of course that it hasn't been aged out meanwhile. Chapter 10 contains some useful guidelines on using memory efficiently when developing packages. Memory used by PL/SQL at runtime to store variables etc is held in PGA memory, and is subject to the server-wide limit imposed by the database parameter PGA_AGGREGATE_TARGET. It's actually a soft (i.e. breakable) limit, and you can keep on allocating more and more PGA memory, for example by extending a collection inside a loop. But at some point the operating system will start paging and everything will slow down dramatically. Eventually the operating system will generate an out-of-memory error which will just halt the execution of the PL/SQL. PL/SQL's use of dynamically allocated process memory depends on whether the Oracle database has been configured in dedicated-server or shared-server mode. Dedicated-Server mode In dedicated-shared server configuration, whenever a database session is created by a logon, a "shadow" process is created on the server. PGA memory is private to this shadow process. Memory associated with executing a SQL statement or PL/SQL block is called "CGA" (call global area), and is allocated from the PGA. As soon as the call completes, Oracle frees the CGA memory. Memory for holding the session state and for private SQL and PL/SQL areas (e.g. package variables and constants) is stored in UGA memory, and is also allocated from the PGA. UGA memory remains in use across calls. Shared-Server mode In shared-server mode, a single "shadow" process handles multiple sessions. CGA memory is allocated from the shadow process as before, but UGA is part of the SGA.

Parallelism and threading Within PL/SQL there is no multithreading : there is always only one thread of control. A single stored subprogram can be executed concurrently by more than one database session, but no data is shared between the sessions. This even applies to global package variables so you don't have to take any special steps to make code thread-safe. More on this subject is in the "session state" part of chapter 10. Of course you can invoke PL/SQL from a language which does allow multithreading such as Java, and you can even run the same subprogram simultaneously from multiple different threads. But in these cases it's the host language which is handling the multithreading, not PL/SQL. When a pipelined function is executed it does introduce a kind of parallelism because as soon as the pipelined function returns its first record, the caller receives it and can begin processing. Not only is this an excellent way to take advantage of multi CPU hardware, it also means you don't have to physically materialize an intermediate result set. Pipelined functions are described fully in chapter 9. As you will see in chapter 8 you can write your own PL/SQL functions which can be used in SQL code, and which blend in seamlessly with ordinary SQL. There is one special compilation option

which you need to use so that your own functions can be used in parallel under the control of the SQL engine.

Recursion PL/SQL allows recursive subprogram calls - where a subprogram invokes itself. This can be very useful for example to navigate a hierarchy or to improve the precision of a calculation. As with any recursive programmatic construct you need to be careful to avoid infinite recursion, by including a "terminating" condition which doesn't call itself. The following example does this crudely just for demonstration, but the "maximum depth" technique illustrated can be useful during development and debugging. CREATE OR REPLACE PROCEDURE recursive_example(p_depth IN INTEGER) IS x CHAR(3) := 'abc'; BEGIN IF p_depth > 1000 THEN RETURN; -- Prevent infinite recursion ELSE recursive_example(p_depth + 1); END IF; END; The p_depth parameter is just used to put a fixed limit on the recursion depth, in case of any logic errors. If circular dependencies are created using dynamic SQL (e.g. where procedure P1 calls P2, and P2 calls P1), the runtime engine automatically kills the statement at 50 recursive SQL levels, with an ORA-00036 error.

Stored PL/SQL and validity When an anonymous block is submitted to Oracle it is compiled and immediately executed. The bytecode is transient and is lost. In contrast, when a stored subprogram is submitted to Oracle using a CREATE statement, it is compiled and then both the source code and the bytecode are stored alongside each other inside the database. When you later come to execute the stored subprogram Oracle retrieves the bytecode and executes it in the virtual machine. One major consequence of the separation between compilation and execution is that stored PL/SQL can be valid or invalid. For example, if a PL/SQL subprogram is sent to Oracle, but contains syntax errors or references to non-existent database objects, it is still created and is still stored in the data dictionary - although it's flagged as invalid and can't be executed. Similarly, if a PL/SQL subprogram exists happily in the database and internally makes use of a table, there is nothing to stop the table from being dropped, and thereby invalidating the subprogram. There is no PL/SQL equivalent to the "schema binding" which is available in some database languages, and which is specifically designed to prevent you from dropping database objects upon which stored code depends. Instead of schema binding, the 11g database elegantly, silently and automatically tries to recompile objects if they are invalid, or have become invalid for any reason. If the automatic recompilation is successful, you won't even know that the subprogram ever was invalid. This removes a huge potential administrative burden of having to create objects in exactly the right order every time, and allows you complete flexibility as code and database structures evolve over time. Consider the following sequence of statements: CREATE OR REPLACE PROCEDURE p IS BEGIN INSERT INTO t VALUES (1); END; Now the procedure has been created, but with a compilation error because the table T does not yet exist. You'll receive a PLS-00905 error if you try to use it. You can go on to create the table T, perhaps like this: CREATE TABLE t (col1 NUMBER); At this point you can execute the procedure, without having to recompile it manually! EXEC p Oracle finds that the procedure is invalid, and automatically tries to recompile it. The table it needs exists now so the compilation succeeds, and the execution just works. Exactly the same thing will happen if you drop and recreate the table. In fact Oracle's automated dependency tracking goes far beyond this simple example, and extends to cover hierarchies of subprograms, in which one subprogram depends upon another. This is described fully in chapter 7.

Of course if the background recompilation fails then the execution will also fail, with a runtime PLS-00905 error. In this case you'll have to find and analyze the compilation error yourself, using the techniques that you'll learn in the next section.

Using the PL/SQL compiler The behaviour of the PL/SQL compiler can be altered in various ways by changing a number of compiler options. These compiler options are controlled by session and database parameters and are described fully later in this section. The default compiler settings are taken from database parameters so you won't normally need to adjust them unless you have a specific reason for doing so. Code compilation occurs when you submit a PL/SQL subprogram to the database using a CREATE statement as shown in chapter 2, or when you use an ALTER .. COMPILE command as this chapter will demonstrate. Oracle itself will try to automatically recompile objects if they are invoked while invalid. But regardless of what caused a PL/SQL subprogram to be compiled, if the compilation failed for any reason you will need to locate and understand the compiler error, which is the subject of the next section.

Compilation errors Whenever a piece of PL/SQL is compiled, any errors which occur are reported against a line number in the source code. Numbering starts at 1 and completely blank lines are also counted. For example consider the following anonymous block which contains the error of trying to use an unknown variable. DECLARE v_value PLS_INTEGER; BEGIN v_value := 0; -- Next line is deliberately blank v_value := 1 + x_value; -- This is an error DBMS_OUTPUT.PUT_LINE('Value is ' || TO_CHAR(v_value)); END; If you try to run the code above, Oracle will return a compilation error stack which contains the following lines: ERROR at line 7: ORA-06550: line 7, column 14: PLS-00201: identifier 'X_VALUE' must be declared ORA-06550: line 6, column 3: PL/SQL: Statement ignored The first three lines indicate that the primary source of the error was at line 7 - namely that the identifier is unknown. The last two lines in the error stack indicate that the line was part of a multiline statement beginning at line 6. This secondary information is most useful in tracking down cases where BEGIN and END statements have not been nested properly. The same rules apply when finding compilation errors from subprograms stored in the database. The compilation error stack for stored subprograms is held in the data dictionary tables USER_ERRORS, ALL_ERRORS and DBA_ERRORS. The contents are managed automatically whenever a subprogram or package is compiled - either explicitly or implicitly. So for example, to list all compilation errors on subprogram named PROC1 you could issue the following query:

SELECT TO_CHAR(line)||'/'|| TO_CHAR(position) AS "Line/Col", text AS error FROM user_errors WHERE name = 'PROC1'; To fetch the source code around the line where a compilation error was reported you can use a data dictionary query on USER_SOURCE (or ALL_SOURCE, or DBA_SOURCE) like the following: SELECT line||' '||text FROM user_source WHERE name = 'MY_PLSQL' AND line BETWEEN 6 AND 7 ORDER BY line; If a subprogram becomes invalidated (for example if a table it uses is dropped) then its STATUS in USER_OBJECTS immediately becomes INVALID. But no rows are created in USER_ERRORS yet, until either the subprogram is compiled explicitly, or someone tries to use it, causing an implicit compilation. When an attempt is made to run the subprogram, as you will see in chapter 7 Oracle first automatically tries to recompile it. If the recompilation fails the caller receives the runtime error PLS-00905 (object ... is invalid), and the real underlying error is stored in USER_ERRORS for that procedure. If you're using SQL*Plus to develop PL/SQL, you can also display the most recently-issued DDL (for example a "CREATE PROCEDURE" statement) with the "LIST" or "L" command, which also conveniently adds line numbers to the whole statement.

Changing compiler options The PL/SQL compiler allows you some influence over the way it does its job. You can change the optimization level, toggle debug mode on or off, and also choose exactly how compilation warnings should be reported. The compilation settings are determined by database parameters which you can alter in your session if required. These database parameters are described individually in this section of the chapter, and the full list is: • • • • •

PLSQL_OPTIMIZE_LEVEL (numeric) : 3=top, 2=normal/default, 1=debug. PLSQL_DEBUG (Boolean) : TRUE or FALSE PLSQL_CODE_TYPE (string) : INTERPRETED or NATIVE PLSQL_WARNINGS (string) : 'K:V,K:V' to specify what warnings should be displayed NLS_LENGTH_SEMANTICS (string): BYTE or CHAR

To alter the compilation parameters in your database session you must use an ALTER SESSION statement, for example: ALTER SESSION SET plsql_optimize_level=3; Multiple parameters can be modified at once like this: ALTER SESSION SET PLSQL_WARNINGS='ENABLE:ALL' PLSQL_CODE_TYPE='NATIVE' PLSQL_DEBUG=FALSE PLSQL_OPTIMIZE_LEVEL=3; The unusual syntax (space-separated) is necessary because some individual parameters can themselves have multiple parameters, which are comma-separated.

Remember that PL/SQL subprograms are stored in the database in both source code and bytecode (compiled) form. To recompile a subprogram with the compilation parameters as defined in your current session you can either recreate it with a CREATE OR REPLACE statement, or else use an ALTER command such as: ALTER PROCEDURE p COMPILE; The above command recompiles the stored procedure named P that already exists in the database. Similarly, for a function named F: ALTER FUNCTION f COMPILE;

.................................................. End of preview ..................................................

The Concise Expert Guide to PL/SQL is available on Amazon at the following URL. http://www.amazon.com/dp/B005G9U8KW