OPP/News for September 2005


Oracle PL/SQL Programming, 4th Edition Available!

On August 23, 2005, O'Reilly Media started shipping the 4th Edition of Oracle PL/SQL Programming to the bookstores. Regarding this book, I have good news and bad news. First, the good news: it is the best edition ever. We fixed another batch of the seemingly endless minor typos and other such mistakes. We added coverage of all new PL/SQL features up through and including Oracle Database 10g Release 2. We also put in entirely new chapters on security, globalization/localization, and PL/SQL IO (file, email, internet and screen). And now the bad news: the page count including index is 1,171. Edition 3's page count was "only" 989. Sigh. I really wanted to keep the page count under 1,000. But I do believe the added value of that new count will more than outweigh the additional weight. So order your copy now!

I am just back from a week in Scandinavia and the Netherlands, doing full-day seminars for Quest. Now I am home for a whole week before heading out to Oracle World. I will be presenting on Sunday and Monday, with expert panels on Monday and Tuesday. I will be doing book signings at the Quest booth and at the conference bookstore. So hopefully if you are coming, I will see you there!

Hey, don't forget!

>> OOW is great, but it covers all Oracle technologies and actually pays relatively little attention to PL/SQL. I invite you to join me on November 2-3, 2005 for a two-day PL/SQL-only conference: Oracle PL/SQL Programming 2005

>> Help influence the future of the PL/SQL language by visiting and contributing to the 21st Century PL/SQL Idea Collector.

>> Check out Qnxo, www.qnxo.com, which you can use to quickly generate a PL/SQL code library customized to your own database objects. We just released version 1.3, which includes Codebar, a very cool "coding toolbar" that you can use from your favorite editor to instantly access your Qnxo-generated code library. Qnxo and Codebar will totally transform the way you write PL/SQL applications! And we are working hard now on adding groundbreaking unit testing features: you will be able to specify the test cases through the Qnxo Unit Tester (Qut) interface and then generate utPLSQL-compatible unit test code.

Tip of the Month: Confirming Invoker Rights State

Oracle8i Database introduced the AUTHID clause for procedures, functions and packages. When set to AUTHID DEFINER (the default), then your program runs under "definer rights." This means that any references to data objects (such as tables and views) are resolved at compile time, based on the directly granted privileges of the definer or owner of the program. Roles are ignored. If, on the other hand, you set the clause to AUTHID CURRENT_USER, then any references to data objects are resolved at run time, based on the privileges of the currently-connected schema. And (the DBAs have got to love this) role-based privileges are now applied.

Invoker rights comes in very handy when your application architecture requires that you have multiple schemas with the same table structures, but you don't want to maintain multiple copies of your code base. It is also extremely important to use in all stored programs that contain dynamic SQL -- if you want to make sure that the dynamically contructed and executed SQL statement runs in the currently connected schema.

Suppose for example that I have created a utility that reads the contents of a file and then executes it as a DDL statement. In fact, I have created such a program and it is described (with link) in the Download section. The header of the program looks like this:

PROCEDURE exec_ddl_from_file (
dir_in IN VARCHAR2
, file_in IN VARCHAR2
)
IS ...

That is, you pass in the location and name of the file, and I execute its contents for you. For example, you can use this program to compile programs from file. Now suppose I want all my developers to be able to use this handy utility. I could do the following:

GRANT EXECUTE ON exec_ddl_from_file TO PUBLIC
/
CREATE PUBLIC SYNONYM exec_ddl_from_file FOR exec_ddl_from_file
/

So everyone can have at it! Now suppose that SCOTT is the owner of exec_ddl_from_file. If the HR schema then runs this program to execute a file that contains a "CREATE TABLE MY_TABLE", HR will create this table in the SCOTT schema. Not exactly what was desired. All I have to do, though, is add AUTHID CURRENT_USER to header and then HR will create the table in her own schema, not that of SCOTT:

PROCEDURE exec_ddl_from_file (
dir_in IN VARCHAR2
, file_in IN VARCHAR2
) AUTHID CURRENT_USER
IS ...

So that's the idea behind the AUTHID clause and invoker rights. There is, unfortunately, another complication that lies at the heart of this month's tip: if definer rights program DEFPROG (with AUTHID DEFINER) calls an invoker rights program INVPROG (with AUTHID CURRENT_USER), then the invoker rights program will be run with "current user" set to the definer/owner of DEFPROG. That is, the currently-connected schema will be ignored. (Check out the invdefinv.sql script in the Download section for an example of this behavior).

There is, in other words, no way to guarantee that your invoker rights program will indeed be run under the invoker's privileges. It depends on the call stack and the AUTHID setting of previous programs in the stack. Too bad....

In the case of a program like exec_ddl_from_file, I really don't want it to even run if it is not running under the authority of the currently-connected schema. Fortunately, there is a way to to this. I can take advantage the SYS_CONTEXT built-in to obtain information about my "user environment" by specifying the "USERENV" context. I can then ask for the name of the session user and compare it to that of the current user. If they are the same, I can proceed. If not, then I terminate my program. Here is an example of applying this logic:

CREATE OR REPLACE PROCEDURE exec_ddl_from_file (
dir_in IN VARCHAR2
, file_in IN VARCHAR2
)
AUTHID CURRENT_USER
IS
FUNCTION invoker_rights_mode RETURN BOOLEAN
IS
-- Original idea from Solomon Yakobson
v_retval NUMBER;
BEGIN
RETURN SYS_CONTEXT ( 'USERENV', 'SESSION_USER' ) =
SYS_CONTEXT ( 'USERENV', 'CURRENT_USER' );
END;
...
BEGIN
IF ( NOT invoker_rights_mode )
THEN
raise_application_error
( -20999
, 'Exec DDL from file must run under invoker privileges!'
);
END IF;

Useful Code of the Month: Exec DDL from File

The exec_ddl_from_file program will execute the contents of your file as a DDL statement. This means that you may not use any bind variables, nor may you return information from the statement that is executed dynamically. But you can use this program to read in a file (using UTL_FILE) that contains, say, the CREATE OR REPLACE PACKAGE definition of a package, and create that package. Hopefully you will find such a utility handy, but I wrote it mostly to demonstrate how you can use DBMS_SQL.PARSE to execute arbitrarily large dynamic SQL strings. EXECUTE IMMEDIATE of Native Dynamic SQL fame is limited to 32K characters. Not so with DBMS_SQL. So here are the files in the download:

exec_ddl_from_file.sql - creates a procedure that executes the contents of a file. You will need to have UTL_FILE configured to get this program to run properly.

exec_ddl_from_file2.sql - creates a procedure that executes the contents of a file and includes the logic required to verify that you are indeed running under invoker rights. You will need to have UTL_FILE configured to get this program to run properly.

invdefinv.sql - a script that demonstrates how when a definer rights program calls an invoker rights program, the current user for that invoker rights program is set to the owner of the definer rights program. It also shows the usefulness of the DBMS_UTILITY.FORMAT_CALL_STACK, which reveals the PL/SQL call stack.

Click here to download the zip of these files.

Upcoming public trainings

As mentioned above, I will be at Oracle Open World the week of September 19 and the week of October 4 I will be over in the United Kingdom (trainings sponsored by Quest). Hope to see you at one of those!

Copyright © 2003-2005 PL/Solutions. All Rights Reserved.