RPG Defined Funtions
RPG Defined Funtions
RPG Defined Funtions
Presented by
Scott Klement
http://www.scottklement.com
2009-2012, Scott Klement
There are 10 types of people in the world. Those who understand binary, and those who dont.
What is a User defined function (UDF)? Why would I consider using one? UDF info and examples What is a User defined table function (UDTF)? UDTF info and examples Links to more information
Although UDFs can be written in many languages, including SQL itself, this talk will focus on writing them in RPG.
SQL Routines
SQL Supports the ability to write routines, callable as an SQL statement, or as part of a larger SQL statement.
Triggers
Fire automatically when an event happens.
What's a UDF (1 of 2)
A UDF is a function that takes input parameters, and returns an output (variable or table) you can write them in SQL or in "external" languages like RPG, Cobol, C, CL, Java and more Think: Calling an RPG subprocedure from SQL!
Select empno, lastname, firstname, midinit, hiredate from EMPMAST order by hiredate
This query can read a file, but what about calculated data? File has date in YYYYMMDD format, but you need it in MM/DD/YYYY format File has the date hired, but you need to know the years of service.
4
What's a UDF (2 of 2)
Select empno, lastname, firstname, midinit, toMdy(hiredate), yearsActive(hiredate,termdate) from EMPMAST order by hiredate
toMdy() calls an RPG subprocedure, passes hiredate as a parameter. RPG routine outputs the date in MM/DD/YYYY format. yearsActive() calls an RPG subprocedure, passes hiredate as a parameter RPG routine figures out how many years since that date, and returns it. Output of select will contain the data from the RPG program
5
Like W h a t?!
SQL and RPG make a great team. RPG is right for some things, and SQL is right for some things!
7
D retval s 10a /free monitor; retval = %char( %date(ymd:*iso) : *USA ); on-error; retval = 'ERROR'; endmon; return retval; /end-free P E
9
You now have routines that are callable from ILE languages but how do you call them from elsewhere?
10
Think of "Create Function" as SQL's version of a prototype. It gives SQL all of the details needed to call your subprocedure. This is an SQL statement. You can run it from any place that SQL is available STRSQL, IBM i Navigator, RUNSQLSTM, from your code, etc. It doesn't matter. Personally, I like putting them into a source member, and running with RUNSQLSTM. Then you can repeat it when you need to. 11
12
toMdy = function name (name you use in SQL statements) ymd = parameter name. Decimal(8,0) is the data type Returns char(10) describes the return value
. . . language rpgle deterministic no sql external name 'mylib/UDFDEMO1(TOMDY)' parameter style general program type sub
Programming language to call -- controls how parms are passed.
Options that control how your routine gets called. (more info coming up)
Contains SQL?
Remember: Your UDF is run from an SQL statement. If your RPG uses SQL, its a statement inside another statement! SQL can handle this, but it needs to know how you plan to use SQL NO SQL (fastest)
Doesnt use any SQL whatsoever. If it tries to use SQL, an error will occur.
CONTAINS SQL
Can use only a very restrictive number of SQL statements that neither read nor update files. (such as COMMIT/ROLLBACK or SET with various variables.)
Deterministic?
In SQL, deterministic means that if a function is called with the same parameter values, itll always return the same result. toMdy(20091231) returns 12/31/2009 Every time you call toMdy() and pass 20091231, itll always return 12/31/2009. That means SQL doesnt have to call it repeatedly if the parameter value doesnt change. It can remember the last answer and not call your function. (Improves performance.) By default, functions are NOT DETERMINISTIC so specify the DETERMINISTIC option if your routine would benefit from it
Democritus was one of the first philosophers to anticipate determinism, and many consider him the father of modern science. But has nothing to do with RPG or SQL.
17
Parameter Styles (1 of 2)
Parameter Styles (2 of 2)
GENERAL (only works with *SRVPGM calls) (SIMPLE CALL is an alias)
What you see is what you get. There are no extra parameters passed from SQL to your *srvpgm, just the ones given on the SQL statement. The return value of the subprocedure becomes the return value of the UDF. GENERAL WITH NULLS (only with *SRVPGM calls) Same as GENERAL except that extra parameters are passed from SQL to RPG containing null indicators for all parameters and return values.. SQL (or DB2SQL which is the same thing!) Subprocedure return values arent used, instead return value is passed in the parameter list. Null indicators are passed in the parameter list for all parameters and return values. Various additional parms for error handling, and other stuff. (more later!) Supports calling programs as well as subprocedures There are others, but these are the ones that are useful from an RPG program
19
s s
monitor; if (termDate <> 0); myTerm = %date(termDate:*iso); endif; retval = %diff( myTerm : %date( hireDate : *iso) : *YEARS ); on-error; retval = -1; endmon; return retval; /end-free P E
To specify no date, 0 must be passed. Thats okay in this case, but not in every UDF!
Any invalid date (in either parameter) will return an error of -1. Not the best way of handling errors!!
0 is a special value in our employee master file that means never terminated
21
If employee worked from 2004-2009, its 5 years. I can easily change the numbers to test different dates.
Tip: SYSIBM/SYSDUMMY1 is an IBM-supplied file thats intended for testing UDFs. It makes it easy to make ad-hoc calls to your UDF.
22
General cannot work with programs because programs cannot return a value. Cant report errors in a standard way. Have to roll-your-own error handling. Doesnt provide support for null indicators on the parameters so special values must be used. What if your database is using nulls?
23
SQL parameter style can call programs instead of procedures. To do that, simply leave off the parenthesis and procedure name. d
25
Input parameters
const const const const
Returned value Null indicators for each input parameter Null indicator for return value SQL State and error message let you return your own SQL errors
monitor; if (n_termDate<>PARM_NULL and termDate <> 0); myTerm = %date(termDate:*iso); endif; on-error; sqlstt = '38999'; errorMsg = 'Invalid termination date!'; return; endmon;
26
Even though parms to the RPG have changed, the SQL call is the same Select yearsActiveSql( hireDate, termDate ) from EMPMAST
27
28
30
Since they're callable from embedded SQL, they can even be used to add this sort of capability to greenscreen applications!
32
Existing Report
Customer: 4760 Date: 04/09/2009 ItemNo Description ---------- ------------------------7895 CHESY-GRIL GM SKLS 5-1 6" 1891 TURKEY BREAST ROLL 26#BOX 2050 CHICKEN BREAST 10# CW BX 1894 SMK TURKY BRST LOG 26#CW 6970 SMK 25% HAM ROUND 35#CW 3261 KLEMENT FS COTTO 25# EW 2393 GAR PEP BF C-OFF IN 13#BX 8063 CKD BR SLDR PATY 1.5OZ10# 2053 CHICKEN ROLL 5#PC-10#BOX Qty UOM --------- --11 BXS 4 BXS 12 BXS 9 BXS 4 BXS 3 BXS 4 BXS 1 BXS 2 BXS Weight ----------110.00 107.53 12.66 213.63 154.25 75.00 54.66 10.00 20.00 Cost ---------21.89 8.76 29.88 19.71 7.92 4.77 11.96 0.00 4.38
Code the business logic as a UDTF, and I can re-use this report as part of any program!
33
Yes
No End
SQL Outputs one row (record) to calling SELECT statement.
36
0 2 2 0 const 0 const 0 0 0 0 0 0 varying const varying const varying 0 const The columns of the returned file.
Call Type lets SQL notify if it's the Open, Fetch or Close event. 37
if n_Date=PARM_NULL or n_CustNo=PARM_NULL; SQLSTT = '38999'; errorMsg = 'Both CUSTNO and DATE are manditory'; return; endif; select; when CallType = CALL_OPEN; exsr doOpen; when CallType = CALL_FETCH; exsr doFetch; when CallType = CALL_CLOSE; exsr doClose; *INLR = *ON; endsl; This routine will be called with OPEN first, then with FETCH repeated for every row, and finally CLOSE.
38
Move to the start of the list of "rows" that this UDTF will return.
40
You might think of these the way you think of the RPG Cycle.
The fetch subrotuine is called once for each record Called once with CallType=OPEN. in *INZSR. You do whatever you'd traditionally do
Called many times with CallType=FETCH each time you use the parameters to return one record. Return '02000' to signal the end. Finally called with CallType=CLOSE.
42
You must use TABLE() to tell SQL that it's a table function. You must use "as" (in this example, "as t").
43
You can use functions like SUM() or AVG() on your UDTF as well. No need to write logic
44
Loading data to be displayed in a table (subfile), and want user to be able to sort by clicking the column?
Select item, desc, qty from table(CustSales( :CustNo, :Date )) as y order by case when :ColNo = '1' then item when :ColNo = 2' then desc when :ColNo = 3' then qty end
It's not different from any other SELECT statement in embedded SQL!
You can take advantage of stuff like WHERE and ORDER BY to filter the output
46
"
In System iNetwork Programming Tips newsletter (free): July 29, 2004, Scott Klement, SQL User Defined Functions in RPG"
http://www.systeminetwork.com/article/rpg-programming/sql-user-defined-functions-in-rpg-60942
48
This Presentation
You can download a PDF copy of this presentation from: http://www.scottklement.com/presentations/
Thank you!
50