Q: | |
23-20. | The most elegant (not necessarily the most efficient) solution involves recursion:
/* Filename on web page: println.sp */
CREATE OR REPLACE PROCEDURE println (val IN VARCHAR2)
IS
BEGIN
/* Don't display lines longer than 80 characters;
they are hard to read. */
IF LENGTH (val) > 80
THEN
DBMS_OUTPUT.PUT_LINE (SUBSTR (val, 1, 80));
println (SUBSTR (val, 81));
ELSE
DBMS_OUTPUT.PUT_LINE (val);
END IF;
END;
/ |
Q: | |
23-21. | To obtain this behavior, add the following exception section to the implementation of println: EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.ENABLE (1000000);
println (val);
END;
/ In other words, if any problem occurs, expand the buffer to its maximum size and then try, try again. |
Q: | |
23-22. | The most interesting aspect of the solution is the implementation of the Boolean overloading. Here is one possible approach:
/* Filename on web page: print.pkg */
CREATE OR REPLACE PACKAGE BODY print
IS
PROCEDURE ln (val IN VARCHAR2)
IS
BEGIN
IF LENGTH (val) > 80
THEN
DBMS_OUTPUT.PUT_LINE (SUBSTR (val, 1, 80));
ln (SUBSTR (val, 81));
ELSE
DBMS_OUTPUT.PUT_LINE (val);
END IF;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.ENABLE (1000000);
ln (val);
END;
PROCEDURE ln (val IN DATE) IS
BEGIN
ln (TO_CHAR (val, 'MM/DD/YYYY HH24:MI:SS'));
END;
PROCEDURE ln (val IN NUMBER) IS
BEGIN
ln (TO_CHAR (val));
END;
PROCEDURE ln (val IN BOOLEAN) IS
BEGIN
IF val
THEN
ln ('TRUE'),
ELSIF NOT val
THEN
ln ('FALSE'),
ELSE
ln (''),
END IF;
END;
END print;
/ |
Q: | |
23-23. | The various PUT procedures all modify the contents of the DBMS_OUTPUT buffer, which is a package variable data structure. Therefore, Oracle could not assert WNPS or RNPS on these (and any other) DBMS_OUTPUT procedures. |
Q: | |
23-24. | Remember that the DBMS_OUTPUT buffer is only flushed to the screen by SQL*Plus when the PL/SQL block terminates. The “SELECT … FROM emp” query executed is not a PL/SQL block, so when it finishes running, the DBMS_OUTPUT buffer is not flushed. To see the trace information, it is necessary to run some PL/SQL block after the SQL statement. This block does not need to call DBMS_OUTPUT.PUT_LINE; it only needs to complete successfully. Even if you execute a do-nothing block, as shown next, you will then see the trace information (and the new Oracle8 extended ROWID format): SQL> BEGIN NULL; END;
2 /
emp-AAAAfBAACAAAAEqAAA
...
emp-AAAAfBAACAAAAEqAAN Of course, if you run this query from within a PL/SQL program, its output is displayed when the program finishes. |
Q: | |
23-25. | Row 1 is the starting point for GET_LINES. |
Q: | |
23-26. | A tricky one! Take it one step at a time: Create your own buffer (an index-by table of a type defined in DBMS_OUTPUT itself, in fact) in a PL/SQL package, let’s call it xbuffer for “Expand Buffer.” Build a procedure that calls DBMS_OUTPUT.GET_LINES to copy the DBMS_OUTPUT buffer contents into the xbuffer.contents. Then disable DBMS_OUTPUT to erase its buffer contents.
Here is one possible implementation:
/* Filename on web page: xbuff.pkg */
CREATE OR REPLACE PACKAGE xbuff
IS
contents DBMS_OUTPUT.CHARARR;
PROCEDURE dumpit;
END;
/
CREATE OR REPLACE PACKAGE BODY xbuff
IS
PROCEDURE dumpit
IS
tempbuff DBMS_OUTPUT.CHARARR;
linenum PLS_INTEGER := 1000;
BEGIN
DBMS_OUTPUT.GET_LINES (tempbuff, linenum);
IF linenum > 0
THEN
linenum := tempbuff.FIRST;
LOOP
EXIT WHEN linenum IS NULL;
contents (NVL (contents.LAST, 0) + 1) := tempbuff(linenum);
linenum := tempbuff.NEXT (linenum);
END LOOP;
END IF;
END;
END;
/ The following test script shows the storage of approximately 5 MB of data:
/* Filename on web page: xbuff.tst */
CONNECT scott/tiger
SET SERVEROUTPUT ON
DECLARE
PROCEDURE fill_and_dump
IS
BEGIN
/* 1000 bytes per round. */
FOR indx IN 1 .. 50
LOOP
DBMS_OUTPUT.PUT_LINE ('PL/SQL and Java both'),
END LOOP;
xbuff.dumpit;
DBMS_OUTPUT.DISABLE;
DBMS_OUTPUT.ENABLE (2000);
END;
BEGIN
/* Buffer is set to 2000. Let's fill it up,
then dump it to my buffer, disable, re-enable,
fill it again, dump it and see what the total
amount is in xbuff.contents. */
FOR oneK IN 1 .. 100
LOOP
fill_and_dump;
END LOOP;
DBMS_OUTPUT.PUT_LINE (
xbuff.contents.COUNT * 1000 ||
' bytes through DBMS_OUTPUT.PUT_LINE in one block.'),
END;
/ You’ll see the following results: SQL> @xbuff.tst
Connected.
5100000 bytes through DBMS_OUTPUT.PUT_LINE in one block. |
Q: | |
23-27. | Curious, isn’t it? The error occurs because another session (A) has changed the definition of (recompiled) code that was already referenced in “your” session (B). Information about any code is cached in the session’s private memory area; it is now, however, out of date, and that causes the error. DBMS_OUTPUT.PUT_LINE displays nothing because the package has been set to “disabled.” That may seem ridiculous on the face of it, since you issued a call to SET SERVEROUTPUT ON to enable the package. That was true, but take a look at the error stack information: ORA-04068: existing state of packages has been discarded What this is saying is that the “states” (values of data structures) of all the packages used by the session have been set back to their defaults. This includes DBMS_OUTPUT, and the default state is to be disabled. So whenever you get this error in SQL*Plus, you should either reconnect, or run your login.sql script to reinitialize your settings. |