1. Trang chủ
  2. » Công Nghệ Thông Tin

Oracle Built−in Packages- P21 pptx

5 434 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 5
Dung lượng 80,67 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

CREATE OR REPLACE PROCEDURE whateverIS v_sql VARCHAR232767; BEGIN construct_sql v_sql; DBMS_SQL.PARSE cur, v_sql, DBMS_SQL.NATIVE; EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE '

Trang 1

v_start := DBMS_UTILITY.GET_TIME;

cursor_id := DBMS_SQL.OPEN_CURSOR;

/*

|| Parse first, outside of loop

*/

DBMS_SQL.PARSE (cursor_id, 'SELECT ', DBMS_SQL.native);

FOR i IN 1 &1

LOOP

/*

|| bind and excecute each loop iteration using host vars

*/

DBMS_SQL.BIND_VARIABLE(cursor_id,'i',i);

exec_stat := DBMS_SQL.EXECUTE(cursor_id);

END LOOP;

DBMS_SQL.CLOSE_CURSOR(cursor_id);

DBMS_OUTPUT.PUT_LINE

('Approach 3: ' || TO_CHAR (DBMS_UTILITY.GET_TIME − v_start));

END;

/

And here are the results from running this script twice:

SQL> @effdsql.tst 10000

Approach 1: 860

Approach 2: 981

Approach 3: 479

2.4.6 Problem−Solving Dynamic SQL Errors

Sometimes the hardest aspect to building and executing dynamic SQL programs is getting the string of

dynamic SQL right You might be combining a list of columns in a query with a list of tables and then a WHERE clause that changes with each execution You have to concatenate that stuff together, getting the commas right, and the ANDs and ORs right, and so on What happens if you get it wrong? Well, let's take the nightmare scenario and work it through

I am building the most complicated PL/SQL application ever It uses dynamic SQL left and right, but that's

OK I am a pro at dynamic SQL I can, in a flash, type OPEN_CURSOR, PARSE, DEFINE_COLUMN, and other commands I know the right sequence, I know how to detect when there are no more rows to fetch, and I

blast through the development phase I also rely on some standard exception−handling programs I have built

that display an error message when encountered

Then the time comes to test my application I build a test script that runs through a lot of my code; I place it in

a file named testall.sql With trembling fingers I start my test:

SQL> @testall

And, to my severe disappointment, here is what shows up on my screen:

ORA−00942: table or view does not exist

ORA−00904: invalid column name

ORA−00921: unexpected end of SQL command

ORA−00936: missing expression

ORA−00911: invalid character

Ugh A whole bunch of error messages, clearly showing that various SQL statements have been constructed

improperly and are causing parse errors −− but which SQL statements are the troublemakers? That is a very

difficult question to answer One way to get at the answer is to place all calls to the PARSE procedure inside

an exception section and then display the string causing the error

Trang 2

CREATE OR REPLACE PROCEDURE whatever

IS

v_sql VARCHAR2(32767);

BEGIN

construct_sql (v_sql);

DBMS_SQL.PARSE (cur, v_sql, DBMS_SQL.NATIVE);

EXCEPTION

WHEN OTHERS

THEN

DBMS_OUTPUT.PUT_LINE ('Error in ' || v_sql);

END;

/

This certainly would have helped explain those earlier error messages The problem with this approach is that

I would need to build this exception section every time I call PARSE I also might be raising exceptions from lines of code other than those containing the call to PARSE How could I distinguish between the errors and the information I should display? Furthermore, I might discover after writing the previous code ten or twenty

times that I need more information, such as the error code I would then have to go back to all those

occurrences and enhance them This is a very tedious, high−maintenance, and generally nonproductive way of doing things

A different and better approach is to provide your own substitute for PARSE that encapsulates, or hides away,

all of these details You don't have to add exception sections in each call to this substitute, because it would come with its own exception section And if you decide you want to do things differently, you just change this one program Doesn't that sound so much better?

Let's go through the steps involved in creating a layer over PARSE that enhance its error−detection

capabilities First, we will build the interface to the underlying DBMS_SQL call That is easy enough:

/* Filename on companion disk: dynsql.spp */*

/* Final version of package */

CREATE OR REPLACE PACKAGE dynsql

IS

PROCEDURE parse

(cur IN INTEGER,

sqlstr IN VARCHAR2,

dbmsmode IN INTEGER := NULL);

END;

/

Why did I bother to put this single procedure inside a package? I always start with packages, because sooner

or later I want to add more related functionality, or I need to take advantage of package features, like

persistent data In this case, I could foresee providing an overloaded parse function, which opens and returns a cursor I also expect to be defining some package data pertaining to error information, which would require a package

Notice that the parse procedure looks just like the DBMS_SQL version, except that the database mode has a default value of NULL (which will translate into DBMS_SQL.NATIVE) This way (a) you do not have to bother with providing a mode, and (b) the default value is not a packaged constant, which could cause

problems for calling this program from within Oracle Developer Release 1

It would be a good idea to compare using DBMS_SQL with dynsql before we even try to implement this package; that will be a validation of the design of the interface So instead of this,

DECLARE

cur PLS_INTEGER := DBMS_SQL.OPEN_CURSOR;

fdbk PLS_INTEGER;

BEGIN

DBMS_SQL.PARSE (cur, 'CREATE INDEX ', DBMS_SQL.NATIVE);

Trang 3

I could use dynsql.parse as follows:

DECLARE

cur PLS_INTEGER := DBMS_SQL.OPEN_CURSOR;

BEGIN

dynsql.parse (cur, 'CREATE INDEX ');

I get to write a little bit less code, but that isn't really the main objective I just want to make sure that I can do whatever I can do with DBMS_SQL (with parse, anyway) through dynsql Now let's build the package body and add some value:

CREATE OR REPLACE PACKAGE BODY dynsql

IS

PROCEDURE parse

(cur IN INTEGER, sqlstr IN VARCHAR2, dbmsmode IN INTEGER := NULL)

IS

BEGIN

DBMS_SQL.PARSE (cur, sqlstr, NVL (dbmsmode, DBMS_SQL.NATIVE));

EXCEPTION

WHEN OTHERS

THEN

DBMS_OUTPUT.PUT_LINE ('Error in ' || sqlstr);

END;

END;

/

With this program installed, I can replace all calls to PARSE with dynsql.parse and then see precisely which dynamic SQL statements are causing me problems As I mentioned earlier, though, I really want to get more information Suppose, for example, that I needed to see the error number (as surely I would), as well as the position in the SQL statement in which the error was detected No problem! I just go to the package body and add a couple lines of code:

CREATE OR REPLACE PACKAGE BODY dynsql

IS

PROCEDURE parse

(cur IN INTEGER, sqlstr IN VARCHAR2, dbmsmode IN INTEGER := NULL)

IS

BEGIN

DBMS_SQL.PARSE (cur, sqlstr, NVL (dbmsmode, DBMS_SQL.NATIVE));

EXCEPTION

WHEN OTHERS

THEN

DBMS_OUTPUT.PUT_LINE ('Parse error: ' || TO_CHAR (SQLCODE) ||

' at position ' || TO_CHAR (DBMS_SQL.LAST_ERROR_POSITION));

DBMS_OUTPUT.PUT_LINE ('SQL string: ' || sqlstr);

END;

END;

/

This should put me in good stead, except for one problem: what if my SQL string is more than 243 bytes in length? The PUT_LINE procedure will raise a VALUE_ERROR if the string passed to it exceeds 255 bytes in length What an annoyance! But since I have had the foresight to hide all my calls to PARSE away in this single program, I can even address this difficulty PL/Vision Lite[1] offers a display_wrap procedure in the PLVprs package So I can avoid any VALUE_ERROR exceptions as follows:

[1] This software comes with my book Advanced Oracle PL/SQL Programming with

Packages (O'Reilly & Associates, 1996) You can also download it from

http://www.revealnet.com

CREATE OR REPLACE PACKAGE BODY dynsql

IS

PROCEDURE parse

Trang 4

(cur IN INTEGER, sqlstr IN VARCHAR2, dbmsmode IN INTEGER := NULL)

IS

BEGIN

DBMS_SQL.PARSE (cur, sqlstr, NVL (dbmsmode, DBMS_SQL.NATIVE));

EXCEPTION

WHEN OTHERS

THEN

DBMS_OUTPUT.PUT_LINE ('Parse error: ' || TO_CHAR (SQLCODE) ||

' at position ' || TO_CHAR (DBMS_SQL.LAST_ERROR_POSITION));

PLVprs.display_wrap ('SQL string: ' || sqlstr);

END;

END;

/

See how easy it is to upgrade your programs and fix shortcomings once you have encapsulated your repetitive actions behind a programmatic interface?

2.4.7 Executing DDL in PL/SQL

DBMS_SQL allows you to execute almost any DDL statements from within PL/SQL Here are some

considerations to keep in mind:

You should explicitly execute and then close your DDL cursors Currently, Oracle will automatically execute DDL statements when they are parsed with a call to PARSE Oracle Corporation warns users

of DBMS_SQL that this behavior might not be supported in the future

You cannot establish a new connection to Oracle through PL/SQL You cannot, in other words, issue

a CONNECT command from within PL/SQL; you will get an "ORA−00900: invalid SQL statement" error From this, one can deduce that CONNECT is not a SQL statement It is, rather, a SQL*Plus command

You must have the necessary privileges to execute that DDL statement granted explicitly to the account owning the program in which the DDL is being run Remember that roles are disabled during PL/SQL compilation and execution If you want to create a table using dynamic SQL, you must have CREATE TABLE or CREATE ANY TABLE privileges granted directly to your schema

Your dynamic DDL execution can result in your program hanging When I call a procedure in a package, that package is locked until execution of that program ends If another program attempts to obtain a conflicting lock (this might occur if you try to drop that package using dynamic DDL), that program will lock waiting for the other program to complete execution

2.4.8 Executing Dynamic PL/SQL

Dynamic PL/SQL is an awful lot of fun Just think: you can construct your PL/SQL block "on the fly" and then execute it from within another PL/SQL program Here are some factors to keep in mind as you delve into this relatively esoteric aspect of PL/SQL development:

The string you execute dynamically must start with a DECLARE or BEGIN statement and terminate with "END." It must, in other words, be a valid anonymous block

Trang 5

The string must end with a semicolon, unlike DDL and DML statements, which cannot end with a semicolon

The dynamic PL/SQL block executes outside the scope of the block in which the EXECUTE function

is called, but that calling block's exception section will trap exceptions raised by the dynamic PL/SQL execution

As a direct consequence of the previous rule, you can only reference globally available data structures and program elements from within the dynamic PL/SQL block

Let's explore those last two restrictions so as to avoid any confusion First of all, I will build a little utility to execute dynamic PL/SQL

/* Filename on companion disk: dynplsql.sp */*

CREATE OR REPLACE PROCEDURE dyn_plsql (blk IN VARCHAR2)

IS

cur PLS_INTEGER := DBMS_SQL.OPEN_CURSOR;

fdbk PLS_INTEGER;

BEGIN

DBMS_SQL.PARSE (cur,

'BEGIN ' || RTRIM (blk, ';') || '; END;',

DBMS_SQL.NATIVE);

fdbk := DBMS_SQL.EXECUTE (cur);

DBMS_SQL.CLOSE_CURSOR (cur);

END;

/

This one program encapsulates many of the rules mentioned previously for PL/SQL execution It guarantees that whatever I pass in is executed as a valid PL/SQL block by enclosing the string within a BEGIN−END pairing For instance, I can execute the calc_totals procedure dynamically as simply as this:

SQL> exec dyn_plsql ('calc_totals');

Now let's use this program to examine what kind of data structures you can reference within a dynamic PL/SQL block In the following anonymous block, I want to use DBMS_SQL to assign a value of 5 to the local variable num:

<<dynamic>>

DECLARE

num NUMBER;

BEGIN

dyn_plsql ('num := 5');

END;

/

This string is executed within its own BEGIN−END block, which would appear to be a nested block within

the anonymous block named "dynamic" with the label Yet when I execute this script I receive the following error:

PLS−00302: component 'NUM' must be declared

ORA−06512: at "SYS.DBMS_SYS_SQL", line 239

The PL/SQL engine is unable to resolve the reference to the variable named num I get the same error even if I qualify the variable name with its block name

<<dynamic>>

DECLARE

Ngày đăng: 07/07/2014, 00:20

TỪ KHÓA LIÊN QUAN