Db2 for z/OS and its ecosystem

Db2 for z/OS and its ecosystem

Connect with Db2, Informix, Netezza, open source, and other data experts to gain value from your data, share insights, and solve problems.

 View Only

Debug native SQL routines with new trace procedures in Db2 for z/OS

By Paul McWilliams posted Mon December 09, 2019 03:35 PM

  

This Db2 for z/OS News from the Lab blog entry was originally published on 2018-11-02.

By Jae Lee and Paul McWilliams.

In Db2 11 and 12, APARs PI44721 and introduce tracing procedures for writing trace information from native SQL procedures. Application developers can use these procedures to debug native SQL routines, by writing SQL statements to write and retrieve the trace information. These new procedures are especially useful for debugging SQL routines running on production systems.

APAR PI44721 introduces the following sample procedures in the DSN8 schema:

  • DSN8.DISABLE
  • DSN8.ENABLE
  • DSN8.GET_LINE
  • DSN8.GET_LINES
  • DSN8.NEW_LINE
  • DSN8.PUT
  • DSN8.PUT_LINE

APAR PI93887 introduces support in Db2 for z/OS for invoking functions defined with MODIFIES SQL DATA from a subselect. This makes it possible for the trace procedures to be called from a function which is invoked from subselect. Before APAR PI93887, Db2 for z/OS issues SQLCODE -740 for this situation.

Let's look at how to invoke the new trace procedures from a compiled SQL scalar function to obtain trace data. In this example, we trace the function REVERSE which returns the text of an input string in reverse order. You can also find this example function in CREATE FUNCTION (compiled SQL scalar).

CREATE FUNCTION REVERSE(INSTR VARCHAR(4000))
 RETURNS VARCHAR(4000)
 DETERMINISTIC
 NO EXTERNAL ACTION
 CONTAINS SQL
  BEGIN
   DECLARE REVSTR, RESTSTR VARCHAR(4000) DEFAULT '';
   DECLARE LEN INT;
   
   IF INSTR IS NULL THEN
   RETURN NULL;
   END IF;


   SET (RESTSTR, LEN) = (INSTR, LENGTH(INSTR));


   WHILE LEN > 0 DO
    SET (REVSTR, RESTSTR, LEN) =
                (SUBSTR(RESTSTR, 1, 1) CONCAT REVSTR,
                 SUBSTR(RESTSTR, 2, LEN - 1),
                 LEN - 1);
  END WHILE;


  RETURN REVSTR;


 END

For native SQL routines, you can use ALTER PROCEDURE or ALTER FUNCTION statement with the ADD VERSION clause to create a new version that invokes trace procedures in the routine body. When the problem occurs in the function, you can activate a version that invokes the trace procedures and execute to obtain the trace information. The ALTER PROCEDURE or ALTER FUNCTION statement must also specify the MODIFIES SQL DATA clause because trace procedures write trace information to SQL objects, namely declared global temp tables.

ALTER FUNCTION REVERSE(INSTR VARCHAR(4000))
 ADD VERSION V2 (INSTR VARCHAR(4000))
 RETURNS VARCHAR(4000)
 DETERMINISTIC
 NO EXTERNAL ACTION
 MODIFIES SQL DATA

To initiate tracing, you must invoke the DSN8.ENABLE procedure. This procedure creates the necessary objects for writing the trace data, including global temporary tables and global variables.

   CALL DSN8.ENABLE(NULL);

You can have two options for writing the trace information. You can use the DSN8.PUT procedure to the message buffer or you can use the DSN8.PUT_LINE procedure to write a complete line, including the end of line character, to the message buffer. Both procedures take one argument which is trace data to be written in the message buffer. You can invoke the DSN8.NEW_LINE procedure to add an end of line character to the message buffer.

   CALL DSN8.PUT('START OF FUNCTION');  
   CALL DSN8.NEW_LINE();  


   CALL DSN8.PUT_LINE('BEFORE LOOP STARTS - ' ||        
                                 'CONTENT OF INPUT: ' || RESTSTR);  

After the trace is written, you have two options for retrieving the data: DSN8.GET_LINE and DSN8.GET_LINES.

The DSN8.GET_LINE procedure takes two output arguments: the first output argument is the MSGBUFFER SQL variable, a VARGRAPHIC value that trace information. The second argument is an INTEGER value that indicates whether the trace is returned or not. The second argument returns 0 if trace is returned, and it returns 1 if no trace is returned.

  WHILE STATUS = 0
   DO
    CALL DSN8.GET_LINE(MSGBUFFER, STATUS);
         IF STATUS = 0 THEN
      INSERT INTO ADMF001.TRACE_TABLE VALUES (MSGBUFFER);   
         END IF;
    END WHILE;

The DSN8.GET_LINES procedure takes two arguments. The first output argument is an ARRAY data type that is defined as VARGRAPHIC data type. It contains multiple lines from the message buffer. The second argument is and input and output argument of type INTEGER. When it is used as an input, it specifies the number of lines to be retrieved and when it is used as output, it indicates the actual number of lines that were retrieved. To retrieve the entire message, you can pass the DSN8.DBMS_ROW_COUNT_E built-in global variable as a second argument.

DECLARE LINE_ARRAY DSN8.GRPHICARR;      -- Type created by DSN8.ENABLE


      CALL DSN8.GET_LINES(LINE_ARRAY, DSN8.DBMS_ROW_COUNT_E);
   SET IX = 1;                         
     WHILE IX <= DSN8.DBMS_ROW_COUNT_E DO                  
      INSERT INTO ADMF001.TRACE_TABLE VALUES (LINE_ARRAY(IX));     
      SET IX = IX + 1;                       
   END WHILE;   

At the end, you invoke the DSN8.DISABLE procedure to indicate tracing is completed and clean up the objects that were created for tracing.

   CALL DSN8.DISABLE;   

Here is the ALTER FUNCTION statement that defines a new version of the REVERSE function. The body of this version of the function invokes the trace procedures.

ALTER FUNCTION REVERSE(INSTR VARCHAR(4000))
 ADD VERSION V2 (INSTR VARCHAR(4000))
 RETURNS VARCHAR(4000)
 DETERMINISTIC
 NO EXTERNAL ACTION
 MODIFIES SQL DATA
  BEGIN
   DECLARE REVSTR, RESTSTR VARCHAR(4000) DEFAULT '';
   DECLARE LEN INT;
   DECLARE IX     INT DEFAULT 1;            
   DECLARE NUMLINES  INT DEFAULT 0;            
   DECLARE LINE_ARRAY DSN8.GRPHICARR;  
   DECLARE MSGBUFFER VARCHAR(1000);
   DECLARE STATUS   INT DEFAULT 0;


   IF INSTR IS NULL THEN
   RETURN NULL;
   END IF; 


   CALL DSN8.ENABLE(NULL);
   CALL DSN8.PUT('START OF FUNCTION');  
   CALL DSN8.NEW_LINE();  


   SET (RESTSTR, LEN) = (INSTR, LENGTH(INSTR));  


   CALL DSN8.PUT_LINE('BEFORE LOOP STARTS - ' ||        
             'CONTENT OF INPUT: ' || RESTSTR);  


   WHILE LEN > 0 DO
    SET (REVSTR, RESTSTR, LEN) =
               (SUBSTR(RESTSTR, 1, 1) CONCAT REVSTR,
                SUBSTR(RESTSTR, 2, LEN - 1),
                LEN - 1);


    CALL DSN8.PUT('LOOP #' || IX || ' - ');


    CALL DSN8.PUT_LINE('Output: ' || REVSTR ||         
              ', Next char: ' || SUBSTR(RESTSTR,1,1) ||
              ', Input: ' || RESTSTR          
             );                    
    SET IX = IX + 1;


  END WHILE;


  CALL DSN8.PUT_LINE('END OF FUNCTION');      


  WHILE STATUS = 0
  DO
    CALL DSN8.GET_LINE(MSGBUFFER, STATUS);
     IF STATUS = 0 THEN
      INSERT INTO ADMF001.TRACE_TABLE VALUES (MSGBUFFER);   
     END IF;
  END WHILE;


  CALL DSN8.DISABLE;   


  RETURN REVSTR;
 END

Invoke a REVERSE function a query.
SELECT REVERSE(C1) FROM MYTABLE;

This example stores the trace messages into a table called TRACE_TABLE. Let's assume that 'ABCD' is the value of a VARCHAR column in a row in the MYTABLE table. When the value of the column is 'ABCD' and it is passed in as an argument for the REVERSE function, the expected result is 'DCBA'. After the execution of the REVERSE function completes with the trace procedures, TRACE_TABLE contains following rows:

  +--------------------------------------------------------
1_| START OF FUNCTION                                      
2_| BEFORE LOOP STARTS - CONTENT OF INPUT: ABCD            
3_| LOOP #1 - Output: A, Next char: B, Input: BCD          
4_| LOOP #2 - Output: BA, Next char: C, Input: CD          
5_| LOOP #3 - Output: CBA, Next char: D, Input: D          
6_| LOOP #4 - Output: DCBA, Next char:  , Input:           
7_| END OF FUNCTION                                        
  +--------------------------------------------------------

Jae Lee is a Software Developer for Db2 for z/OS and Paul McWilliams is an Information Developer for Db2 for z/OS.





#Db2forz/OS
#db2z/os
#Db2Znews
0 comments
12 views

Permalink