Tải bản đầy đủ
PLV: Top-Level Constants and Functions

PLV: Top-Level Constants and Functions

Tải bản đầy đủ

[Appendix A] Appendix: PL/SQL Exercises
The default value for NULL substitutions is the string NULL. You can obtain the current setting of the
substitution value by calling the nullval function:
FUNCTION nullval RETURN VARCHAR2;

The p package makes use of the substitute value for NULLs. When you call p.l to display text that RTRIMs
to a NULL, it will automatically display the string that you have specified for substitution. In the following
example, I set the NULL substitution value to N/A and then demonstrate its use in a call to the p.l
procedure.
SQL> exec PLV.set_nullval ('N/A');
SQL> VARIABLE v NUMBER;
SQL> exec p.l (:v);
N/A

III. Building Block
Packages

6.2 Setting the PL/Vision
Date Mask

Copyright (c) 2000 O'Reilly Associates. All rights reserved.

6. PLV: Top−Level Constants and Functions

252

Chapter 6
PLV: Top−Level Constants
and Functions

6.2 Setting the PL/Vision Date Mask
To standardize the way that date information is displayed inside PL/Vision, the PLV package maintains a
PL/Vision date mask. This mask is used in the p, PLVtrc, PLVtab, and PLVlog packages to convert dates to
strings.
The default date mask for PL/Vision is stored in the c_datemask constant and has this value:
FMMonth DD, YYYY HH24:MI:SS

The FM prefix is a toggle that requests suppression of all padded blanks and zeroes.
You can change the date mask with a call to set_datemask, whose header is:
PROCEDURE set_datemask (datemask_in IN VARCHAR2 := c_datemask)

Since the default value for set_datemask is the default date mask for PL/Vision, you can also reset the
date mask to the default by calling set_datemask without any arguments.
You can retrieve the date mask (which is to say, you can use the date mask yourself) by calling the
datemask function:
FUNCTION datemask RETURN VARCHAR2;

The following calls to set_datemask and the datemask function illustrate the behavior of these
programs.
SQL> exec p.l(sysdate);
May 17, 1996 13:41:56

Change the date mask to show only month and year:
SQL> exec PLV.set_datemask ('Month YYYY');
SQL> exec p.l(sysdate);
May
1996

Change the date mask to suppress those extra spaces:
SQL> exec PLV.set_datemask ('fmMonth YYYY');
SQL> exec p.l(sysdate);
May 1996

Now return the date mask back to the default:
SQL> exec PLV.set_datemask
SQL> exec p.l(sysdate);
May 17, 1996 13:42:37

253

[Appendix A] Appendix: PL/SQL Exercises
The following query uses the datemask function inside SQL to view the date and time of stored information
(the PLV package makes this function accessible in SQL by including a RESTRICT_REFERENCES pragma):
SQL> SELECT TO_CHAR (hiredate, PLV.datemask)
2
FROM emp
3
WHERE deptno = 10;
TO_CHAR(HIREDATE,PLV.DATEMASK)
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
November 17, 1981 09:18:44
June 9, 1981 11:11:32
January 23, 1982 17:01:00

6.1 Null Substitution Value

6.3 Assertion Routines

Copyright (c) 2000 O'Reilly Associates. All rights reserved.

254

Chapter 6
PLV: Top−Level Constants
and Functions

6.3 Assertion Routines
PL/Vision provides a set of generic routines you can use in your own programs to assert the validity of your
program's assumptions. Just about every piece of software you write makes assumptions about the data it
manipulates. For example, parameters may have only certain values or be within a certain range; a string
value should have a certain format; an underlying data structure is assumed to have been created. It's fine to
have such rules and assumptions, but it is also very important to verify or "assert" that none of the rules is
being violated.
The cleanest way to perform this task is to call a prebuilt assertion routine (see Chapter 20 in Oracle PL/SQL
Programming). The PLV package offers a variety of procedures to allow you to validate assumptions in the
most natural possible manner. In all cases, if the assumption is violated the assertion program will take up to
two actions:
1.
Display a message if provided. This string is optional and the default for the string is NULL.
2.
Raise the assertion_failure exception. You can then handle this exception in the program that
called the assertion routine, or you can let the exception terminate that program and propagate to the
enclosing block.
The PLV assertion routines come in the following flavors:
Procedure Name

Description

assert

Generic assertion routine. You pass it a Boolean expression or value and assert tests to
see if that expression is TRUE.

assert_inrange Generic assertion routine to check date and numeric ranges. You provide the value to
be checked along with start and end values. If the value (either date or number) does
not fall within the specified range, assert_inrange
assert_notnull Generic assertion routine to check that the specified value is NOT NULL.
The assert procedure is the most generic of the assertion routines. It is called, in fact, by the other assertion
routines. The header for assert is as follows:
PROCEDURE assert
(bool_in IN BOOLEAN, stg_in IN VARCHAR2 := NULL);

6.3.1 Using the assert Procedure
Let's take a look at how to incorporate assertion routines in your code, and then examine the impact. The
following procedure translates a code into a description string. There are only three valid codes, an
assumption that is validated by the call to PLV.assert:

255

[Appendix A] Appendix: PL/SQL Exercises
CREATE OR REPLACE FUNCTION calltype (code_in IN VARCHAR2)
RETURN VARCHAR2
IS
retval VARCHAR2(100) := NULL;
BEGIN
PLV.assert
(code_in IN ('E', 'C', 'I'), 'Enter E C or I...');
IF code_in = 'E'
THEN retval := 'EMERGENCY';
ELSIF code_in = 'C'
THEN retval := 'COMPLAINT';
ELSIF code_in = 'I'
THEN retval := 'INFORMATION';
END IF;
RETURN retval;
END calltype;
/

Notice that I pass a complex Boolean expression as an argument to the assert routine. This may seem odd at
first glance, but you will get used to it quickly. A program's Boolean argument can be a literal, a variable, or
an expression.
Now we will try using the calltype function by embedding it in calls to the p.l procedure so that we can
see the results.
In the first call to calltype below, I pass a valid code and p.l displays the correct returned description. In
the second call to calltype, I pass an invalid code, J. As a result, the assertion routine displays the message
as specified in the function and then raises the exception, which goes unhandled.
SQL> exec p.l(calltype('E'));
EMERGENCY
SQL> exec p.l(calltype('J'));
Enter E C or I...
ERROR at line 1:
ORA−06510: PL/SQL: unhandled user−defined exception

The error is displayed as a "user−defined exception" because PLV.assert raised the
assertion_failure exception, which is not a system exception. You can trap for that exception as
shown below:
BEGIN
p.l (calltype ('J'));
EXCEPTION
WHEN PLV.assertion_failure
THEN
p.l ('Invalid call type code');
END;

6.3.2 Asserting NOT NULL
The other assertion routines are designed to handle specific kinds of assertions that programmers must
commonly handle. The assert_notnull routine, for example, allows you to easily make sure that an
argument to a program is NOT NULL.
Without an assertion routine, you will write variations of code like this over and over again in your programs:
IF code_in IS NULL
THEN
p.l ('The code cannot be null!');
RAISE VALUE_ERROR;
ELSE

6.3.2 Asserting NOT NULL

256

[Appendix A] Appendix: PL/SQL Exercises
act_on_code (code_in);
END IF;

With PLV.assert_notnull, you simply attempt to assert the rule. If the code "passes," you move on to
your action:
PLV.assert_notnull (code_in);
act_on_code (code_in);

You save on the typing and your indentation flattens out, thereby improving the readability of your program.
PLV offers four overloadings of assert_notnull, so you can pass it Booleans, strings, dates, and
numbers.

6.3.3 Asserting "In Range"
The range assertion routines will probably save you the most code and provide a higher level of coverage of
problem data. PLV offers two overloaded assert_inrange programs: one for dates and one for numbers.
The date range assertion routine header is:
PROCEDURE assert_inrange
(val_in IN DATE,
start_in IN DATE := SYSDATE,
end_in IN DATE := SYSDATE+1,
stg_in IN VARCHAR2 := NULL,
truncate_in IN BOOLEAN := TRUE);

The first three arguments should be clear: You provide the value you want to check, as well as the start and
end dates. Notice that the default start date is SYSDATE or "now" (at midnight) and the default end date is
SYSDATE+1 or "tomorrow" (at midnight). The fourth argument, stg_in, is the optional string for display.
The fifth parameter, truncate_in, allows you to specify whether or not you want the end−point dates to be
truncated. When a date is truncated (with the default mask, which is your only option in
assert_inrange), the time portion is removed. The default setting for this argument is to perform
truncation. I offer this option because in many cases when developers want to perform date range checks, they
do not want to have to deal with the time component. That aspect of a date variable can, in fact, cause
"obviously correct" dates to fail.
The default values of assert_inrange for dates is designed to allow you to assert with a minimum of
typing that a date falls on the current day. Consider this call to the assertion program:
IF PLV.assert_inrange (v_hiredate)
THEN
...

If no other arguments are specified, then PLV checks to see if
v_hiredate BETWEEN TRUNC (SYSDATE) AND TRUNC (SYSDATE+1)

which, given the way TRUNC works, asks: "Is hiredate between midnight of last night and midnight of
the coming night?" In other words, does v_hiredate fall anywhere during the current day?
The numeric assert_inrange is more straightforward. As you can see from the header below, there is no
truncate argument. It simply checks to see if the specified number falls within the specified range.
PROCEDURE assert_inrange
(val_in IN NUMBER,

6.3.3 Asserting "In Range"

257

[Appendix A] Appendix: PL/SQL Exercises
start_in IN NUMBER,
end_in IN NUMBER,
stg_in IN VARCHAR2 := NULL);

The following procedure updates the salary of an employee, but only if the new salary does not exceed the
maximum salary allowed in the system (returned by the personnel package max_salary function):
PROCEDURE update_salary
(emp_in IN emp.empno%TYPE, newsal_in IN NUMBER)
BEGIN
PLV.assert_inrange (newsal_in, 0, personnel.max_salary);
UPDATE emp
SET sal = newsal_in
WHERE empid = emp_in;
END;

If you are careful and consistent in your use of assertion programs like those in the PLV package, your
programs will be more robust and less likely to fail in unpredictable ways.

6.2 Setting the PL/Vision
Date Mask

6.4 PLV Utilities

Copyright (c) 2000 O'Reilly Associates. All rights reserved.

6.3.3 Asserting "In Range"

258

Chapter 6
PLV: Top−Level Constants
and Functions

6.4 PLV Utilities
PL/Vision comes with a set of utility procedures and functions. These programs offer shortcuts to executing
commonly needed operations or information in PL/SQL programs. In some cases, the utility exists simply to
make it possible to access the information from within a SQL statement. These programs are described below.

6.4.1 Converting Boolean to String
The boolstg function translates a Boolean expression into a string describing that Boolean's value. The
header for boolstg is:
FUNCTION boolstg
(bool_in IN BOOLEAN, stg_in IN VARCHAR2 := NULL)
RETURN VARCHAR2;

The second argument allows you to pass a string that is prefixed to the string describing the Boolean (TRUE,
FALSE, or NULL). The various ways to call PLV.boolstg are illustrated below:
SQL> exec p.l(PLV.boolstg (TRUE));
TRUE
SQL> exec p.l(PLV.boolstg (TRUE, 'This is'));
This is TRUE

6.4.2 Obtaining the Error Message
The errm function provides a PL/SQL function interface to the SQLERRM builtin function. You cannot call
SQLERRM in a SQL statement, which is annoying when you have error information in a SQL database table
and you want to display the corresponding error message text. You want to do something like this:
SELECT errcode, SQLERRM (errcode)
FROM error_log
WHERE create_ts < SYSDATE;

but the SQL layer returns this error message:
ORA−00904: invalid column name

The errm function allows you to use SQLERRM inside SQL by hiding that builtin behind the function
interface and by using the RESTRICT_REFERENCES pragma in the specification. With PLV, you change
that SQL statement to:
SELECT errcode, PLV.errm (errcode)
FROM error_log
WHERE create_ts < SYSDATE;

and get the information you need to analyze and fix your problems.

259

[Appendix A] Appendix: PL/SQL Exercises

6.4.3 Retrieving Date and Time
The now function is simply a quick way to display the current date and time. Its header is:
FUNCTION now RETURN VARCHAR2;

I built PLV.now because I got tired of typing:
SELECT TO_CHAR (SYSDATE, 'HH:MI:SS') FROM dual;

just to see the current time. With PLV.now, you can at any point see both the date and time from within
SQL*Plus with either of these commands:
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
SQL> SELECT PLV.now from DUAL;
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
August 3, 1996 20:19:35
SQL> exec p.l(PLV.now);
August 3, 1996 20:20:48

6.4.4 Pausing Your Program
The pause procedure of the PLV package provides a cover for the DBMS_LOCK.SLEEP procedure; its
header is:
PROCEDURE pause (seconds_in IN INTEGER);

Why bother providing this pause program, when it is nothing more than a call to the builtin SLEEP
procedure? Most PL/SQL developers will never use the DBMS_LOCK package; few of us need to create and
manipulate locks with the Oracle Lock Management services. Yet this package contains SLEEP because it is
the context in which Oracle developers realized they needed this capability.
The PLV.pause procedure offers, at least within PL/Vision, a more rational location for this technology.
The following "infinite" loop uses PLV.pause to make sure that there is an hour's delay between each
retrieval of data from a DBMS_PIPE named hourly_production.
LOOP
process_line_data ('hourly_production');
PLV.pause (60 * 60);
END LOOP;

6.3 Assertion Routines

6.5 The Predefined
Datatypes

Copyright (c) 2000 O'Reilly Associates. All rights reserved.

6.4.3 Retrieving Date and Time

260

Chapter 6
PLV: Top−Level Constants
and Functions

6.5 The Predefined Datatypes
The PLV package provides several centrally located, predefined datatypes. These elements are used
throughout PL/Vision, but you can make use of them as well.
The two variables that are to be used as predefined datatypes are the following:
plsql_identifier VARCHAR2(100) := 'IRRELEVANT';
max_varchar2 VARCHAR2(32767) := 'IRRELEVANT';

Use the plsql_identifier variable whenever you need to declare a VARCHAR2 variable or constant
that holds a PL/SQL identifier, such as a table name or column name or program name. Currently these names
are limited to 30 characters. That may, however, change in the future and you will find errors popping up in
your utilities if you declare variables like this:
v_table_name VARCHAR2(30);

Instead, use the predefined datatype as follows:
v_table_name PLV.plsql_identifier%TYPE;

Use the max_varchar2 variable whenever you need to declare a string variable to the maximum number of
characters allowable in PL/SQL. Again, today that maximum size is 32,767, but this value may increase in the
future. By relying on max_varchar2 in your declarations and parameter definitions, you (or the supplier of
PL/Vision) can change the definition in one place and, with a compile, upgrade all your code.
NOTE: Do you notice any conflict between the declarations of these predefined datatypes
and the best practices I described earlier in this book? I have declared variables in my
package specification; the best practice recommends strongly that you always hide your data
structures in the package body. At the very least, you might be thinking, I should make the
plsql_identifier and max_varchar2 data structures constants, so their values
cannot be changed.
Well, take a look at the default value for these variables. It doesn't matter what value is
assigned to these variables. They are only to be used in %TYPE anchored declarations to pass
on the datatype and constraint. And I couldn't make them CONSTANTs even if I wanted to (I
tried that on the first pass). It turns out that you cannot anchor variables to constants; if you
want to use %TYPE, you must remove the CONSTANT keyword.

6.4 PLV Utilities

6.6 The Predefined
Constants

261

[Appendix A] Appendix: PL/SQL Exercises
Copyright (c) 2000 O'Reilly Associates. All rights reserved.

262