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

Oracle Built−in Packages- P26 pot

5 105 0

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

THÔNG TIN TÀI LIỆU

2.5.4.7 Build those utilities! I suppose you could read this section and shrug, thinking: "Well, I don't feel like I have the time to write these kinds of generic utilities." I urge you to reject this line of reasoning. You always have time to create a more generic, generally useful implementation. Any time you spend up−front to craft high−quality, well−designed modules will pay off −− for you personally, and for all others who benefit from your labors. PL/SQL offers endless possibilities for building reusable code, which will save you many hours of development, debugging, and maintenance. The DBMS_SQL package in particular is a veritable gold mine. Dive in and try your hand at dynamic SQL! 2.5.4.8 Full text of intab procedure Because the full intab procedure is so long, I haven't included it here. For the full text of this procedure, see the intab.sp file on the companion disk. 2.5.5 Indirect Referencing in PL/SQL I cut my teeth, so to speak, with Oracle software on a product called SQL*Forms. Back around 1987−1989, it was a really hot product for application development, and I was a guru. I wrote recursive triggers. I wrote a debugger for this product so thoroughly lacking in a debugger. I could produce robust forms in little or no time. It was very sad, then, for many of us, when Windows and other GUIs came along, and Oracle Corporation did not keep up. Of course, Oracle Forms has come a long way and is competing well with PowerBuilder and SQL*Windows and Visual Basic. That makes me happy because it has some really neat features, one of which is indirect referencing. Oracle Forms offers two built−ins, the NAME_IN function and the COPY procedure, which allow you to retrieve and set by name the values of variables inside your form. For example, I can set the value of the global variable GLOBAL.right_now to SYSDATE with the following call, COPY (TO_CHAR (SYSDATE), 'global.right_now'); and I can retrieve the value of that same global with a call to NAME_IN: v_date := TO_DATE (NAME_IN ('global.right_now')); Some of you might, and should, be saying to yourself, "Well, heck, why not just use the assignment operator," as in, v_date := TO_DATE (:GLOBAL.right_now); and you would of course be right −− in this situation. The power of NAME_IN and COPY, however, become clear when instead of using them with "hard−coded" variable names, you construct the name of the variable at runtime. Here is an example: COPY (TO_CHAR (SYSDATE), 'global.date_in_year' || TO_CHAR (v_yearnum)); In this case, the name of the variable to which SYSDATE is assigned is not determined until this line of code is executed and the v_yearnum variable is evaluated. This "indirect reference" to the GLOBAL variable in question offers developers a tremendous amount of flexibility. Indirect referencing will come in most handy when you are building generic utilities in which the very structure of your PL/SQL in−memory data is not defined until runtime. I encountered the need for indirect referencing when building a PL/SQL code generator (now a product called PL/Generator from RevealNet). I wanted to provide a utility that would read a template file and then generate [Appendix A] What's on the Companion Disk? 2.5.4 Displaying Table Contents with Method 4 Dynamic SQL 116 PL/SQL packages from that file. I found that by using indirect referencing, I could make these templates user−extensible. While this is neither the time nor place to really explore the generator technology, the following text shows a fragment of the template file. The square brackets ([ and ]) are used to "tag" template commands. The [IF] command implements "meta−conditional" logic, and that is where my indirect referencing comes into play: [IF]myvars.checkstatus IF SQL%ROWCOUNT = 0 THEN DBMS_OUTPUT.PUT_LINE ('No records updated!'); END IF; [ENDIF] That is the code to be processed by PL/Generator. So before I do the generation, I will set the myvars.checkstatus packaged global variable to "T" with a statement like this: SQL> exec myvars.checkstatus := 'T'; When I run my utility, I employ indirect referencing to "look up" the value of the variable with the name "myvars.checkstatus." That lookup will return TRUE and the check against ROWCOUNT thus will be included in the package that is generated. To accomplish this feat, follow these steps: 1. Grab the variable name from within the template file. 2. Use dynamic PL/SQL to evaluate the packaged variable. (Remember: with dynamic SQL, you can only read/modify "global" or package−based data.) In this section, I will show you how to build a package called dynvar to perform this kind of dynamic or indirect referencing for string values. You can easily overload the programs in dynvar to support other datatypes. Here is the specification of the package: /* Filename on companion disk: dynvar.spp */* CREATE OR REPLACE PACKAGE dynvar IS PROCEDURE assign (expr_in IN VARCHAR2, var_inout IN OUT VARCHAR2); FUNCTION val (var_in IN VARCHAR2) RETURN VARCHAR2; PROCEDURE copyto (val_in IN VARCHAR2, nm_in IN VARCHAR2) END dynvar; / These three programs function as follows: dynvar.assign Assigns a value returned by the expression to the specified variable. This procedure is most helpful when you want to use dynamic PL/SQL to assign a value to a locally declared variable. dynvar.val This is similar to the Oracle Forms NAME_IN function. It retrieves the value currently held by the specified variable. In this case, var_in is a string containing the name of the variable you want to evaluate, not the variable itself. dynvar.copyto This is similar to the Oracle Forms COPY procedure. It "copies" the specified value, which can be an expression, to the variable. In this case, you provide the name of the variable, not a direct reference to the variable. [Appendix A] What's on the Companion Disk? 2.5.4 Displaying Table Contents with Method 4 Dynamic SQL 117 Here is a demonstration script using all three of these programs: /* Filename on companion disk: dynvar.tst */* CREATE OR REPLACE PACKAGE TSTVAR IS str1 varchar2(2000); str2 varchar2(2000); END; / DECLARE v_pkg CHAR(7) := 'tstvar.'; localstr VARCHAR2(100); BEGIN dynvar.assign ('abc' || 'def' , localstr); /* Note 1 below */ DBMS_OUTPUT.PUT_LINE ('local string set to ' || localstr); dynvar.copyto ('abcdefghi', v_pkg || 'str1'); /* Note 2 below */ DBMS_OUTPUT.PUT_LINE ('global string set to ' || tstvar.str1); DBMS_OUTPUT.PUT_LINE ('value retrieved dynamically ' || dynvar.val (v_pkg || 'str1')); /* Note 3 below */ tstvar.str2 := 'tstvar.str1'; DBMS_OUTPUT.PUT_LINE ('double indirection gets us ' || dynvar.val (dynvar.val (v_pkg || 'str2'))); /* Note 4 below */ DBMS_OUTPUT.PUT_LINE ('expression retrieved dynamically ' || dynvar.val (v_pkg || 'str1' || '|| '' wow!''')); /* Note 5 below */ END; / This is the output from the program: SQL> @dynvar.tst Package created. local string set to abcdef global string set to abcdefghi value retrieved dynamically abcdefghi double indirection gets us abcdefghi expression retrieved dynamically abcdefghi wow! Here are some notes on program behavior: 1. The call to dynvar.assign assigned the concatenated string to the local variable, which is not possible with "normal" dynamic PL/SQL, since the data structures referenced in your dynamic PL/SQL block must be global. 2. The call to dynvar.copyto copied the string "abcdefghi" to the PL/SQL variable tstvar.str1 indirectly, since the name of the variable was constructed at runtime. 3. In the first call to dynvar.val, I construct the name of the variable and pass that to dynvar for processing. It returns "abcdefghi." 4. I then use two levels of indirection to get the value of the tstvar.str1 variable. In this case, I assign to tstvar.str2 the name of its sister variable in the package, tstvar.str1. I then call dynvar.val twice, first [Appendix A] What's on the Companion Disk? 2.5.4 Displaying Table Contents with Method 4 Dynamic SQL 118 to evaluate tstvar.str2 and retrieve the value "tstvar.str1." Then I call dynvar.val to evaluate tstvar.str1 and return the first nine letters of the alphabet. 5. In this last call to dynvar.val, I do not pass a variable name. Instead, I pass an expression, a concatenation of the variable with the string "wow." This function doesn't mind in the least. As long as the string you pass is a valid string expression, it will be evaluated and returned. The following sections show the implementations of the dynvar programs (all found in the dynvar.spp file), along with an explanation. A few comments for clarification: in each of these I make use of two variables, cur and fdbk. These are defined at the package level. In addition, to improve performance, I open the cursor when the package is initialized and therefore do not need to open and close it in each program. 2.5.5.1 Assigning a value The dynvar.assign procedure performs a dynamic assignment of a string expression to a string variable. That variable can be either locally or globally (in a package) defined. PROCEDURE assign (expr_in IN VARCHAR2, var_inout IN OUT VARCHAR2) IS BEGIN DBMS_SQL.PARSE (cur, 'BEGIN :var := ''' || expr_in || '''; END;', DBMS_SQL.NATIVE); DBMS_SQL.BIND_VARIABLE (cur, 'var', 'a', 2000); fdbk := DBMS_SQL.EXECUTE (cur); DBMS_SQL.VARIABLE_VALUE (cur, 'var', var_inout); END; Here are the basic steps performed in this procedure: • Parse the assignment statement constructed with a bind variable and the expression. (Remember that the cursor was previously opened and remains open for the duration of your session.) • Bind the variable (even though it is an OUT value) that will receive the value of the expression. The value I bind into it is irrelevant, but dynamic PL/SQL requires that you call the BIND_VARIABLE procedure for each placeholder in your string. • Execute the PL/SQL block. • Extract the value assigned to the placeholder variable and pass it to the variable you provided in the call to the procedure. There are several interesting aspects to dynvar.assign: • In the parse, I construct my assignment string, making sure to enclose the expression within single quotes. I do this because expr_in will be evaluated to a string when it is passed into the procedure. If I do not enclose it in quotes, the PL/SQL compiler will try to interpret the string as code rather than as a literal. • The second argument to dynvar.assign must be declared as IN OUT because, when I perform the call to the VARIABLE_VALUE procedure, I deposit a new value directly into the variable provided in [Appendix A] What's on the Companion Disk? 2.5.5 Indirect Referencing in PL/SQL 119 the call to the procedure. • When you call dynvar.assign, you pass the actual variable in the second argument, not a string (whether literal or a variable) containing the name of the variable. If you want to assign a value to a variable, but reference the variable by its name, use the dynvar.copyto procedure. 2.5.5.2 Retrieving a value The dynvar.val function extracts the value contained in the variable you have named in the argument to this program. FUNCTION val (var_in IN VARCHAR2) RETURN VARCHAR2 IS retval VARCHAR2(2000); BEGIN DBMS_SQL.PARSE (cur, 'BEGIN :val := ' || var_in || '; END;', DBMS_SQL.NATIVE); DBMS_SQL.BIND_VARIABLE (cur, 'val', 'a', 2000); fdbk := DBMS_SQL.EXECUTE (cur); DBMS_SQL.VARIABLE_VALUE (cur, 'val', retval); RETURN retval; END; Here are the steps performed by dynvar.val: • Parse the assignment string. Notice that in this case I do not surround the var_in value with single quotes. It's not a string expression in this case, but is instead the name of the variable. If you try to use dynvar.val to extract the value of a local, nonpackaged variable, you will receive a compile error. • Bind the placeholder variable. As with assign, the value I bind is irrelevant, since it actually is an OUT placeholder, about to receive the value from the variable name that is passed in. • Execute the dynamic PL/SQL block, extract the value into a local variable (whereas in dynvar.assign, it was extracted directly into the variable you provide), and then return the value. 2.5.5.3 Copying a value to a packaged variable The dynvar.copyto procedure copies the value to the variable named in the second argument. This corresponds to Oracle Forms' COPY and allows you to indirectly reference the variable you want to modify. PROCEDURE copyto (val_in IN VARCHAR2, nm_in IN VARCHAR2) IS BEGIN DBMS_SQL.PARSE (cur, 'BEGIN ' || nm_in || ' := ''' || val_in || '''; END;', DBMS_SQL.NATIVE); fdbk := DBMS_SQL.EXECUTE (cur); END; This procedure constructs and parses the string and then executes; there's not much to it. There is no need to call VARIABLE_VALUE because I do not need to extract any values. I am modifying the named variable right in the execution of the PL/SQL block. I also do not call BIND_VARIABLE, because I have not used any placeholders. Instead, I concatentate directly (into the PL/SQL block) the value I want to assign to the named variable. Why do I do this? Let's [Appendix A] What's on the Companion Disk? 2.5.5 Indirect Referencing in PL/SQL 120 . very sad, then, for many of us, when Windows and other GUIs came along, and Oracle Corporation did not keep up. Of course, Oracle Forms has come a long way and is competing well with PowerBuilder. happy because it has some really neat features, one of which is indirect referencing. Oracle Forms offers two built−ins, the NAME_IN function and the COPY procedure, which allow you to retrieve. file on the companion disk. 2.5.5 Indirect Referencing in PL/SQL I cut my teeth, so to speak, with Oracle software on a product called SQL*Forms. Back around 1987−1989, it was a really hot product

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

Xem thêm: Oracle Built−in Packages- P26 pot

Mục lục

    A. What's on the Companion Disk?

    1.1 The Power of Built-in Packages

    1.1.1 A Kinder , More Sharing Oracle

    1.2 Built-in Packages Covered in This Book

    1.3.1 What Is a Package?

    1.3.2 Controlling Access with Packages

    1.3.3 Referencing Built-in Package Elements

    1.3.4 Exception Handling and Built-in Packages

    1.3.5 Encapsulating Access to the Built-in Packages

    1.3.6 Calling Built-in Packaged Code from Oracle Developer/2000 Release 1

TÀI LIỆU CÙNG NGƯỜI DÙNG

  • Đang cập nhật ...

TÀI LIỆU LIÊN QUAN