Stored procedure
Stored procedures are a way to extend database functionality. Wikipedia calls a stored procedure a subroutine, which is as good a definition as any. Stored procedures (SP from here on) can be used to:
- Encapsulate complex processing
- Update a table. Sure, UPDATE does that, but then the end user (web developer) needs to be familiar with your database.
- Return data to SQL such as a result set. Again, isolate the database from the UI. A result set can consolidate columns from several tables, in essence hiding the JOINS from the ultimate end user of the data.
- Provide a service to SQL like RTVSYSVAL.
IBM have a 2006 Redbook entitled Stored Procedures, Triggers, and User-Defined Functions on DB2 Universal Database for iSeries. It's old but it provides a foundation for understanding stored procedures.
SPs are an inherent part of DB2; they are part of the database. On other platforms, the logic section of SPs is written using SQL. That's a choice on DB2 for i as well, and this is called an SQL (or MAIN) Stored Procedure. We have another choice as well, and that is to write the logic using an HLL like RPG. This is called an External stored procedure. In either case, you register a SP with DB2 via the SQL CREATE PROCEDURE statement. To use a stored procedure in your SQL, use the CALL statement.
While all programs on the IBM i can be called as stored procedures, note that CL commands are not programs. One way around this is to use QSYS.QCMDEXEC() or QSYS.QCMDEXEC2() stored procedures. You can also use the CL: prefix to use CL commands directly in your own SQL stored procedure. For example:
CL: CHGCURLIB CURLIB(MYLIB);
An external SP interfaces with DB2 via the parameter list. There are several parameter styles, depending on whether you will need to handle NULL values or not.
TODO:
- Example tables
- Example SQL SP
- One return variable
- Result set
- Example RPGLE SP
- One return variable
- Result set
- Multiple parameter list usage
- GENERAL
- WITH NULLS
- Canonical documentation for parameter lists
- Consume a result set. Java?
Error Handling in MAIN Stored Procedures
The following is an abbreviates SQL stored procedure that demonstrates three methods of error handling.
The first method (at marker <<< 1 >>> ) is to declare a global error handler. The global handler can be for either exit processing or continuation processing. It can also handle any combination of exception, warning, and/or not found conditions.
The second method (at marker <<< 2 >>> ) is to declare a local error handler. This one must be inside of another BEGIN ... END statement block. When the statement block ends the local error handler is no longer in effect.
The third method (at marker <<< 3 >>> ) is to explicitly check the SQL state returned by the last statement executed. This involves coding a Get Current Diagnostics Condition 1 statement to retrieve the returned SQL state. The presence of this statement prevents SQL from performing default error handling for the preceding statement.
Create or Replace Procedure IM_MAINTENANCE ( In CompNumb Char(5) , In ItemNumb Char(18) ) Dynamic Result Sets 1 Language SQL Deterministic Modifies SQL Data Program Type MAIN New Savepoint Level Specific IMMAINTSP Set Option AlwBlk = *AllRead , AlwCpyDta = *Optimize , CloSqlCsr = *EndActGrp , Commit = *CHG , DatFmt = *ISO , DbgView = *Source , DecResult = (31,31,00) , DftRdbCol = *None , DynDftCol = *No , DynUsrPrf = *User , ExtInd = *YES , LangId = ENU , Output = *Print , SrtSeq = *Hex , UsrPrf = *User MyProc: Begin --------------------------------------------------------------------------- -- variable definitions --------------------------------------------------------------------------- Declare CompId BigInt; -- company master id Declare DtaLib Char(10); Declare ItmTid BigInt; -- item number id Declare SqlStmt VarChar(3000); Declare IGNORE_VALUE SmallInt Default -7; Declare CurState Char(5); Declare ErrState Char(5); Declare ErrPrfx Char(11) Default 'IMMAINTSP: '; Declare ErrText VarChar(300); Declare IMMAINTSP_CURSOR Cursor With Return For IMMAINTSP_STMT; --------------------------------------------------------------------------- -- global exit handler --------------------------------------------------------------------------- Declare Exit Handler for SQLEXCEPTION, SQLWARNING, NOT FOUND <<< 1 >>> Begin Get Current Diagnostics Condition 1 ErrState = Returned_SqlState; Set ErrText = ErrPrfx || ErrText || ' State=' || ErrState; Signal SqlState '88W00' Set Message_Text = ErrText; End; --------------------------------------------------------------------------- -- clean up alias definition if needed --------------------------------------------------------------------------- Begin Declare Continue Handler for SQLSTATE '42704' Begin End; -- notfnd <<< 2 >>> Set ErrText = 'Initial cleanup of alias failed.'; Drop Alias QTEMP/IMMAINTA; End; --------------------------------------------------------------------------- -- validate company master using static SQL --------------------------------------------------------------------------- Set ErrText = 'Company number validation failed.'; Select CiCompPTid, LibCde Into CompId, DtaLib From SHR460/CICOMPPV02 Where CIORGKY = CompNumb; IF CompId is NULL Or DtaLib is NULL Then Set ErrText = 'Company number not on file or data library missing.'; Signal SqlState '88W01' Set Message_Text = ErrText; End If; --------------------------------------------------------------------------- -- point to local company data library --------------------------------------------------------------------------- Set ErrText = 'Set path failed.'; Set Path = "QSYS","QSYS2","SYSPROC","SYSIBMADM" ,"QTEMP",DtaLib,"SHR460","PGM460","PTF460" ,"QGPL"; -- for dynamic SQL Set ErrText = 'Setting schema '||DtaLib||' failed.'; Set Schema DtaLib; Get Current Diagnostics Condition 1 CurState = Returned_SqlState; <<< 3 >>> If Substr(CurState,1,2) <> '00' Or Not Exists (Select 1 from QSYS2/SYSSCHEMAS where System_Schema_Name = BackupLibrary) Then Signal SqlState '88W02' Set Message_Text = ErrText; End If; ...snip... --------------------------------------------------------------------------- -- return to caller --------------------------------------------------------------------------- Return; End;
Note: Instead of method three (above), you can simply declare a variable called exactly "SQLSTATE" (or "SQLCODE") and SQL will automatically store the result of executing the last statement in that variable for you to test directly. For example:
Declare SqlState Char(5) Default '00000'; -- or -- Declare SqlCode SmallInt Default 0;