Archive for the 'Databases' Category

anonymous (sql) blocks in db2

SQL Server supports the execution of SQL not within the context of a stored procedure or trigger. This is quite a handy feature when debugging as it allows one to write thowaway code without the overhead of declaring a new stored procedure that later has to be dropped.

Although it is possible to do this in DB2 too, it isn’t as transparent as it is in SQL Server (because – I expect – DB2 is more ANSI SQL compliant than SQL Server). The trick is to declare the code that one wants executed in an ATOMIC block:

   begin atomic
   declare var_a char(1);
   set var_a = (select ibmreqd from sysibm.sysdummy1);
   end
   @

This may be executed from a Command Center session, or (having connected to a database, and the above dumped into a file called "script.sql") via the CLP:

   $ db2 -td@ -vf script.sql

Thanks to DHRUV for a detailed post on the subject.

db2 drop schema … please!

The best documented way to drop a schema in DB2 is the

   db2 "DROP SCHEMA <schema_name> RESTRICT"

command. This, however, requires that all objects in the schema have been dropped before DB2 will allow the schema to be dropped. This is somewhat inconvenient for ad-hoc testing and fortunately in DB2 9 one can now use the stored procedure admin_drop_schema:

   db2 "call admin_drop_schema('<schema_name>', NULL, 'ERRORSCHEMA', 'ERRORTABLE')

Simple enough, but it took me long enough to track this down that I thought it worth posting.

declaring db2 global temporary tables & cursors

Since v7.1 DB2 LUW (or UDB, if you prefer) has supported session-specific global temporary tables. Once declared, the table may be referenced until the session ends, at which point the rows are deleted and the description is dropped. (See the IBM Infocenter pages for details). The syntax for declaring these tables is, however, a little arcane, and I found using them in conjuction with cursor declarations somewhat tricky.

Specifically, if the cursor is opened over the temporary table, the cursor declaration must follow the table declaration. However, it seems that DB2’s parser expects only variable declarations to follow the table declaration. For example, the following code:

   connect to testdb@
   set current schema='testschema'@
   
   DROP PROCEDURE temp_proc@
   CREATE PROCEDURE temp_proc(
         IN input_var CHAR(1))
      LANGUAGE SQL
      DYNAMIC RESULT SETS 1
      CALLED ON NULL INPUT
   BEGIN
   
      DECLARE SQLCODE INTEGER DEFAULT 0;
      DECLARE GLOBAL TEMPORARY TABLE t_y_or_n (y_or_n char(1))
         ON COMMIT PRESERVE ROWS NOT LOGGED;
   
      DECLARE local_var CHAR(1);
      DECLARE crsr CURSOR WITH RETURN TO CLIENT FOR
         SELECT y_or_n FROM session.t_y_or_n WHERE y_or_n=local_var;
      INSERT INTO session.t_y_or_n
         SELECT ibmreqd FROM sysibm.sysdummy1;
      SET local_var = input_var;
   
      OPEN crsr;
   
   END@
   
   CALL temp_proc('Y')@
   terminate@

yields the error:

   DB21034E The command was processed as an SQL statement because it
   was not a valid Command Line Processor command. During SQL
   processing it returned:
   SQL0104N An unexpected token "<variable declaration>" was
   found following "". Expected tokens may include: "<SQL statement>".
   LINE NUMBER=12.
   SQLSTATE=42601

The solution seems to be to put the table declaration and the the cursor declaration in different scope. This is achieved using labels, i.e.:

   connect to testdb@
   set current schema='testschema'@
   
   DROP PROCEDURE temp_proc@
   CREATE PROCEDURE temp_proc(
         IN input_var CHAR(1))
      LANGUAGE SQL
      DYNAMIC RESULT SETS 1
      CALLED ON NULL INPUT
   P1 : BEGIN
      DECLARE SQLCODE INTEGER DEFAULT 0;
      DECLARE GLOBAL TEMPORARY TABLE t_y_or_n (y_or_n char(1))
         ON COMMIT PRESERVE ROWS NOT LOGGED;
      P2 : BEGIN
         DECLARE local_var CHAR(1);
         DECLARE crsr CURSOR WITH RETURN TO CLIENT FOR
            SELECT y_or_n FROM session.t_y_or_n WHERE y_or_n=local_var;
         INSERT INTO session.t_y_or_n
            SELECT ibmreqd FROM sysibm.sysdummy1;
         SET local_var = input_var;
         OPEN crsr;
      END P2;
   END P1@
   
   CALL temp_proc('Y')@
   terminate@

The above code can be run by copying it into a file and executing it with:

   db2 -td@ -v -f <script_name>

Thanks to nemali on the Dev Shed forums for his/her post on the subject.

retrieving large values without truncation in the DB2 CLP

The DB2 command-line processor (CLP; invoked from a shell with the command db2) truncates output greater than 8K. One way to retrieve the full text of a column value that exceeds that size (e.g. CLOB data) is to use the export command. For example, having connected a database and set the current schema, one could use a statement of the following form from within the db2 CLP (note that the "db2 =>" is the CLP prompt):

   db2 => export to result.del of del modified by chardel'' coldel; decpt, select <column> from <table>

The "of del" indicates that the output file should contain delimited data. The delimiters used, and how they are applied, is defined by the modified by clause and its qualifiers "chardel" and "coldel";

  • chardel: defines the character that will be used to delimit character data; in the above example a single quote (') is used.
  • coldel: defines the character that will be used to delimit columns; in the above example a semi-colon (;) is used.

The file produced by this statement would look something like:

   1;'foo';256;'KB'
   2;'bar';512;'KB'
   3;'baz';1;'MB'

Note, however, that if the column value that is being exported is longer than the default character column that can be exported, export will truncate the data and issue the warning:

SQL3132W The character data in column "DATA" will be truncated to size "32700"

To work around this, the lobfile parameter may be used to specify a base prefix for the files that will be used to store LOB data without truncation. The form of the command then becomes:

   export to result.del of del lobfile result_lob_ modified by chardel'' coldel; decpt, select data from debug_data

For more information please refer the IBM Infocenter pages that describe the EXPORT command, and its file-type modifiers.

using cursors as OUT parameters in PL/SQL stored procedures

The system that I’ve been working on uses as a standard stored procedures that declare an OUT parameter through which a result cursor is “returned” to the caller. While it is easy enough to call the procedure and process the result set in Java, I occasionally want to call such a procedure from SQL*Plus. To do this, one must bind a suitably typed variable to the procedure’s output parameter, for example:

   SQL> var proc_res refcursor;
   SQL> exec foo.get_stuff (:proc_res);
   
   PL/SQL procedure successfully completed.
   
   SQL> print proc_res;
   
   DUM
   ---
   X

In this example package foo and procedure get_stuff are declared as:

   SQL> create or replace package foo is
    2 type cursor_out is ref cursor;
    3 procedure get_stuff (results out cursor_out);
    4 end foo;
    5 /
   
   Package created.
   
   SQL>
   SQL> create or replace package body foo as
    2 procedure get_stuff (results out cursor_out) is
    3 c1 cursor_out;
    4 begin
    5 open results for select * from dual;
    6 end get_stuff;
    7
    8 end foo;
    9 /
   
   Package body created.

printing PL/SQL debug output in SQL*Plus

Oracle Database provides the put_line procedure (in the dbms_output package) to print statements. This output is not, however, displayed by default in SQL*Plus; it must be enabled using:

   SQL> set echo on
   SQL> set serveroutput on

put_line can then be used as follows:

   SQL> declare
    2 x number;
    3 begin
    4 x := 3;
    5 dbms_output.put_line('x is ' || x);
    6 end;
    7 /
   x is 3
   
   PL/SQL procedure successfully completed.
   
   SQL>

oracle database XE confined

I am currently empoyed to develop/maintain commercial software that is deployed on Oracle Database Enterprise Edition. However, to streamline my day-to-day activities, I’m using Oracle Database Express Edition (XE) in development.

Being somewhat lighter than its peer editions, XE is better suited to a desktop environment and I’ve managed to get it running in a memory constrained VMware Virtual Machine running Ubuntu GNU/Linux 8.04 (Hardy Heron) with 768mb RAM. It is not, however, what one would call a light-weight database and does require a few tweaks to get it to compensate for an environment where resources are scarce.

I’ve started a wiki page that details the various problems that I have encountered, and the solutions that I’ve found. As I encounter issues I’ll post updates in the form of comments.