25
Wed, Dec
0 New Articles

Expand Your Database with UDFs

RPG
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

User-defined functions are flexible and easy to create. Add them to your development toolkit to create powerful new solutions for your company.

 

Many of you are quite familiar with DDS and the ability to define physical and logical database files. For instance, if we have a physical file MYFILE defined as shown below, we can create logical files that provide a subset of the physical file fields, map physical fields to logical fields through operators such as CONCAT and SST, map physical data types to other types, etc.:

 

                R MYRCD                                              

                  NAME          30          TEXT('Company Name')     

                  XCNTRY         1          TEXT('Xfer Century')     

                  XMONTH         2          TEXT('Xfer Month')       

                  XYEAR          2          TEXT('Xfer Year')        

                  XDAY           2          TEXT('Xfer Day')         

 

For example, the logical file MYFILELF shown below maps the alphanumeric field Name to a Unicode data type and concatenates the fields XCntry, XYear, XMonth, and XDay to a XDate logical field in CYYMMDD format.

 

 

                R MYRCD                     PFILE(MYFILE)            

                  NAME            G         CCSID(1200)             

                  XDATE                     CONCAT(XCNTRY XYEAR -   

                                                   XMONTH XDAY)        

We can, however, do much, much more by combining the power of database with a high-level language such as RPG.

 

Let's say we have a user who needs to query MYFILE to determine the number of days that have passed since the Xfer (Transfer) date for each company. For demonstration purposes, we'll load four records into MYFILE using a tool such as the Data File Utility (DFU). These are the record contents:

 

NAME                            XCNTRY  XMONTH  XYEAR  XDAY

Company 1                         1       06     08     01

Company 2                         1       05     08     15

Company 3                         1       05     08     20

Company 4                         0       11     07     02      

 

Using the IBM Query product 5722-QU1, we can calculate the number of days since the transfer date using the following

result fields and field selection criteria:

 

Selected files                                                      

   ID     File           Library       Member        Record Format   

   T01    MYFILE         VINING        *FIRST        MYRCD           

Result fields                                   

  Name        Expression                        

  TIMESTAMP   '0001-01-01-00.00.00.00000' ||    

              XCntry                            

  CENTURY     DIGITS(MICROSECOND(Timestamp)     

              + 19)                             

  DATE        CHAR(DATE(SUBSTR(Century,11,2) || 

              XYear || '-' || XMonth || '-' ||  

              XDay), ISO)                       

  DAYS        DAYS(CURRENT(DATE)) - DAYS(Date)

  

Ordering of selected fields

                     

Field           Sort      Ascending/  Break  Field       

Name            Priority  Descending  Level  Text        

NAME                                         Company Name

DAYS                                                         

 

The result fields TimeStamp and Century are used to convert the alphanumeric XCntry field to a numeric field and to then add the value 19 so that we can determine the century (19xx, 20xx, etc). The result field Date is used to create a date data type field so that we can use the DAYS function to determine the number of days between the current date and the transfer date. The value of the result field Days is that number of days. For reporting purposes, we are displaying/printing the company name and the number of days between the current date and the transfer date.

 

 NAME                                   DAYS

Company 1                                15

Company 2                                32

Company 3                                27

Company 4                            36,752

 

There are other ways to calculate the number of days, but the approach used is fairly representative of what is needed on a V5R4 system. And while it's somewhat straightforward, I would certainly not want to explain this process to a typical end user. Most of the effort involved is in converting the four alphanumeric fields XCntry, XYear, XMonth, and XDay into the date field Date. It would simplify life greatly if we could provide the user with a view of MYFILE that simply provided a Date field already mapped to a date data type, and that's what we're going to do.

 

Within the database is the capability to create user-defined functions (UDFs). For the purpose of this article, UDFs are essentially the ability to have a user exit program called by the system when a particular field is used within a view. UDFs are considered part of the Structured Query Language (SQL), but it is not necessary to have the DB2 Query Manager and SQL Development Toolkit product 5722-ST1 installed on your system. The only products used in this article to create UDFs are the IBM i operating system and the ILE RPG compiler. For those of you who are SQL knowledgeable, even the RPG compiler is not needed. This article, however, is being published in RPG Developer and will demonstrate how to use UDFs and RPG without having to learn SQL (or at least not much SQL).

 

We are going to create a UDF (or program) that accepts four parameters--Xcntry, XMonth, XDay, and XYear--and returns a date data type field. To create the UDF, we will use the following CREATE FUNCTION statement:

 

create function vining/Get_Date                                 

  (char(1), char(2), char(2), char(2)) 

  returns date                                                   

  language rpgle                                                

  deterministic                                                 

  no sql                                                        

  not fenced;                                                    

 

For now, don't worry about how we'll run this statement. Let's just look at what the statement contains.

 

We are creating a function named Get_Date, which will be in the library VINING. The Get_Date function, an ILE RPG program we will create shortly, will be passed four parameters--a 1-byte character field followed by three 2-byte character fields. These parameters correspond to XCntry, XMonth, XDay, and XYear, respectively. I'll point out that we could have included the field names in this parameter list (XCntry char(1), XMonth char(2), etc.), but I'm trying to hold the information in the statement to a minimum. (For full documentation on the CREATE FUNCTION statement, see the SQL Reference manual.) Rather than a program, Get_Date could also be implemented as an entry point within a service program. We're using a program approach in order to minimize the lines of code and the number of compilation steps.

 

The Get_Date function will return one parameter, which is a date data type. The function is written in ILE RPG, is deterministic (meaning that it will always return the same result if called with the same input parameters), contains no imbedded SQL statements, and is not fenced (meaning that it can run in the same thread as the database caller of the function, which may not be the initial thread where the Query is running). The Not Fenced clause is not strictly necessary for this article, but, as it provides better performance than the alternative, Fenced, I chose to include it. Several other parameters for the CREATE FUNCTION statement are being defaulted, and you may want to look at the SQL Reference manual for details on these other options.

 

The system will call our Get_Date program with more than just the four parameters discussed above. The minimum parameters that will be passed are described below, where N is the number of input parameters defined (four in our example) on the CREATE FUNCTION statement.

 

Required Parameters:

1 to N

N input parameters

Input

Varies based on UDF definition

N + 1

1 result parameter

Output

Varies based on UDF definition

(N+2) to (2N)+1)

Indicator parameters for input parameters

Input

2-byte integers (5i 0)

2(N+1)

Indicator parameter for result parameter

Output

2-byte integer (5i 0)

+ 1

Status

Output

Char(5)

+ 1

Qualified function name

Input

Varying length Char(139)

+ 1

Specific function name

Input

Varying length Char(128)

+ 1

Message text

Output

Varying length Char(70)

 

Our Get_Date program will be called with 14 parameters. The first four will be the values for XCntry, XMonth, XDay, and XYear as each record is processed. The fifth parameter will be the output parameter where Get_Date will return the date value calculated from the input parameters.

 

The next four parameters (six through nine) will each be 2-byte signed integers representing null indicators for the four respective input parameters (XCntry, XMonth, XDay, and XYear). If the indicator value is -1, then the corresponding input parameter value is NULL. If the indicator value is 0, then the corresponding input parameter is not NULL. The next, tenth, parameter will be a 2-byte signed integer output allowing Get_Date to indicate whether or not the returned date field is NULL. As with the input parameter indicators, -1 indicates that the result is NULL.

 

The next parameter, Status, can be used by the Get_Date function to pass error-related information back to the i5/OS database (and from there to the Query). A value of all zeroes, which is the default value, indicates that no error was encountered by Get_Date. Several possible error values can be returned and can be found here. The valid error values are those starting with either '01' for warnings or '38' for exceptions. Qualified function name, the 12th parameter, will be the name of the function (VINING.GET_DATE for our example), Specific function name, the next parameter, will be GET_DATE, and the last parameter, Message text, is an output parameter where Get_Date can provide textual information related to any error the program reported in the Status parameter.

 

This is the ILE RPG source for Get_Date:

 

dGet_Date         pr                  extpgm('GET_DATE')     

d Century                        1                           

d Month                          2                           

d Day                            2                           

d Year                           2                           

d Date                            d                          

d CenturyInd                     5i 0                        

d MonthInd                       5i 0                        

d DayInd                         5i 0                        

d YearInd                        5i 0                        

d DateInd                        5i 0                        

d SQLState                       5                            

d FuncName                     139    varying                

d SpecificName                 128    varying                

d MsgText                       70    varying                

                                                              

dGet_Date         pi                                      

d Century                        1                        

d Month                          2                        

d Day                            2                         

d Year                           2                        

d Date                            d                       

d CenturyInd                     5i 0                     

d MonthInd                       5i 0                     

d DayInd                         5i 0                     

d YearInd                        5i 0                     

d DateInd                        5i 0                     

d SQLState                       5                        

d FuncName                     139    varying             

d SpecificName                 128    varying             

d MsgText                       70    varying             

                                                          

 /free                                                     

                                                       

  Date = %date((Century + Month + Day + Year) :*CMDY0);

                                                       

  *inlr = *on;                                         

  return;                                               

                                                       

 /end-free                                             

 

As you can see, there isn't much to the program! Other than the parameter definitions, we have essentially only the one calculation using the %date built-in function of ILE RPG. The program can be created with CRTBNDRPG GET_DATE.

 

Now let's see how we can use Get_Date to simplify the Query we looked at earlier. First, we need to create a view (similar conceptually to a DDS defined logical file) that maps the physical file MYFILE to a view that includes our "virtual" date field. We do this with the SQL CREATE VIEW statement.

 

create view vining/myfiledate as select               

  Name, Get_Date(XCntry, XMonth, XDay, XYear) as Date 

  from myfile;                           

 

This statement creates the view MYFILEDATE in library VINING. The view includes two fields: Name and Date. Name is simply the Name field from the physical file MYFILE. Date, on the other hand, is a field returned by the Get_Date UDF with Get_Date being passed the parameters XCntry, XMonth, XDay, and XYear from MYFILE.

 

To use this view, from Query or even another RPG program, we use MYFILEDATE as the name of the file. To now calculate the number of days since the transfer date to the current date, we can modify our earlier Query to simply say this:

 

Selected files                                                     

  ID     File           Library       Member        Record Format  

  T01    MYFILEDATE     VINING        *FIRST        MYFILEDATE     

Result fields                                                      

  Name        Expression                         Column Heading    

  DAYS        days(current(date)) - days(date)                     

Ordering of selected fields                                        

  Field           Sort      Ascending/  Break  Field               

  Name            Priority  Descending  Level  Text                

  NAME                                         Company Name        

  DAYS                                                                 

And running the Query, we get this:

 

NAME                                   DAYS  

Company 1                                15  

Company 2                                32  

Company 3                                27  

Company 4                            36,752      

 

Quite a bit easier! We have basically expanded our database to now include a date data type field that is based on the physical fields found in the existing MYFILE database.

 

Now we get to the question "Just what is required to run the SQL statements CREATE FUNCTION and CREATE VIEW?" Fortunately, it's very simple. Create a source physical file, enter the two CREATE statements into a source member, and then run the RUNSQLSTM CL command, which will process the SQL statements. For demonstration purposes, we can use the following commands:

 

CRTSRCPF QSQLSRC

STRSEU QSQLSRC MYFIRSTUDF TXT

 

While in SEU (or a similar editor), enter the following two statements. Be careful to not miss the closing semicolon (;) for each statement:

 

            create function vining/Get_Date                        

              (char(1), char(2), char(2), char(2))                 

              returns date                                          

              language rpgle                                       

  deterministic                                        

  no sql                                               

  not fenced;                                           

create view vining/myfiledate as select                

  name, get_date(xcntry, xmonth, xday, xyear) as date  

  from myfile;

                  

Exit from your editor and then run this command:

 

RUNSQLSTM SRCFILE(QSQLSRC) SRCMBR(MYFIRSTUDF) COMMIT(*NONE)  

 

If you previously entered test data into MYFILE, you are now ready to use Query and easily determine the number of days that have passed since the transfer date.

 

If you want to play with other versions of the Get_Date function or MYFILEDATA view, you can use the SQL DROP statement to delete the UDF and/or view. The statements would be either or both of the two below.

 

DROP FUNCTION GET_DATE;

DROP VIEW MYFILEDATA;

 

These statements can be run by again placing the statements into a source member of QSQLSRC and running the RUNSQLSTM command against that member.

 

The Get_Date UDF provides a very productive extension to our database. You might notice that nowhere in the CREATE FUNCTION statement or the Get_Date RPG program do we actually reference MYFILE. The Get_Date UDF can be used anytime we want to create a virtual date data type field from a physical file and the date is stored as separate fields representing the century, year, month, and day. If some of our physical files store date-related information in formats such as numeric, combined YYMMDD, etc., we can also create UDFs to cater to these other file formats. We can build these UDFs once and then reuse them many times.

 

Essentially, if we can calculate a value within an RPG program, then with UDFs and views we can have that value appear as if it actually exists within the database. What would be required, for instance, to eliminate the one remaining result field Days in our Query? The answer is a new UDF Get_Diff and a new view MYFILEDIFF.

 

This is the CREATE FUNCTION statement to create Get_Diff:

 

create function vining/Get_Diff        

  (char(1), char(2), char(2), char(2)) 

  returns integer                      

  language rpgle                       

  deterministic                         

  no sql                               

  not fenced;      

 

Here's the RPG program Get_Diff:

 

dGet_Diff         pr                  extpgm('GET_DIFF')        

d Century                        1                              

d Month                          2                              

d Day                            2                              

d Year                           2                              

d Days                          10i 0                           

d CenturyInd                     5i 0                           

d MonthInd                       5i 0                           

d DayInd                         5i 0                           

d YearInd                        5i 0                           

d DaysInd                        5i 0                           

d SQLState                       5                              

d FuncName                     139    varying                   

d SpecificName                 128    varying                    

d MsgText                       70    varying                   

                                                                

dGet_Diff         pi                                       

d Century                        1                          

d Month                          2                         

d Day                            2                         

d Year                           2                         

d Days                          10i 0                      

d CenturyInd                     5i 0                      

d MonthInd                       5i 0                      

d DayInd                         5i 0                      

d YearInd                        5i 0                      

d DaysInd                        5i 0                      

d SQLState                       5                         

d FuncName                     139    varying              

d SpecificName                 128    varying              

d MsgText                       70    varying              

                                                           

dToday            s               d   inz(*JOB)            

                                                                 

 /free                                                            

                                                                 

  Days = %diff( Today                                            

               :(%date((Century + Month + Day + Year) :*CMDY0))  

               :*DAYS);                                           

                                                                 

  *inlr = *on;                                                   

  return;                                                        

                                                                  

 /end-free                     

 

Create the view MYFILEDIFF:

 

create view vining/myfilediff as select                     

  name, get_diff(xcntry, xmonth, xday, xyear) as days       

  from myfile;                                               

 

And remove the calculation from our Query:

 

Selected files                                                          

  ID     File           Library       Member        Record Format       

  T01    MYFILEDIFF     VINING        *FIRST        MYFILEDIFF        

 

Ordering of selected fields                                             

  Field           Sort      Ascending/  Break  Field                    

  Name            Priority  Descending  Level  Text                     

  NAME                                         Company Name             

  DAYS                                              

 

This leaves us with the same output as before, a lot less work for the end user, and elimination of the exposure that some end user might miscalculate the number of days between the current date and the transfer date.                       

 

NAME                                     DAYS  

Company 1                                  15  

Company 2                                  32  

Company 3                                  27  

Company 4                              36,752                       

 

Before we leave, there are two items I would like to point out about Get_Diff. First, the Get_Diff function is defined as returning an integer data type rather than a date data type. As we're returning the number of days, this makes sense but is a change that might be easily overlooked. Second, the Get_Diff RPG program initializes the field Today to *JOB. This is to avoid the problem of the system date rolling over to the next day if the Query happens to be running at midnight. Using *JOB, rather than *SYS, prevents the Query output from changing mid-report and leaves the function as being deterministic.

 

And one item about views in general: A view can be used much like a logical file, but views do have limitations. One limitation has to do with keyed access. It is impossible to associate a key (or index) with a view in the same manner as you can with a logical file. In the case of Query, this is not a major concern as Query tends to read an entire file and allows you to specify sort criteria for the output. In the case of an RPG application using standard RPG file management and reading a view, this means you can only read the view sequentially (which may or may not be arrival sequence). This may be suitable for some batch jobs but generally precludes the use of views for most interactive applications (no CHAIN by key, for instance). Using SQL, you can have an index associated with the use of a view. This consideration is not so much a limitation of UDFs as it is a consequence of how UDFs are accessed using RPG standard data management.

 

The IBM i has long been recognized as having a strong and productive database capability. While UDFs are not new to the i database, it's been my experience that quite a few RPG developers are unaware of the flexibility available with UDFs. Or if they are aware of UDFs, many are unaware that UDFs are available within the operating system and do not require that the SQL Toolkit be installed. As you have hopefully seen, UDFs are highly flexible, aren't difficult to create, and can be a great addition to your development toolkit when providing new solutions to your company. I hope you keep this capability in mind when working on future projects.

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$

Book Reviews

Resource Center

  • SB Profound WC 5536 Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application. You can find Part 1 here. In Part 2 of our free Node.js Webinar Series, Brian May teaches you the different tooling options available for writing code, debugging, and using Git for version control. Brian will briefly discuss the different tools available, and demonstrate his preferred setup for Node development on IBM i or any platform. Attend this webinar to learn:

  • SB Profound WP 5539More than ever, there is a demand for IT to deliver innovation. Your IBM i has been an essential part of your business operations for years. However, your organization may struggle to maintain the current system and implement new projects. The thousands of customers we've worked with and surveyed state that expectations regarding the digital footprint and vision of the company are not aligned with the current IT environment.

  • SB HelpSystems ROBOT Generic IBM announced the E1080 servers using the latest Power10 processor in September 2021. The most powerful processor from IBM to date, Power10 is designed to handle the demands of doing business in today’s high-tech atmosphere, including running cloud applications, supporting big data, and managing AI workloads. But what does Power10 mean for your data center? In this recorded webinar, IBMers Dan Sundt and Dylan Boday join IBM Power Champion Tom Huntington for a discussion on why Power10 technology is the right strategic investment if you run IBM i, AIX, or Linux. In this action-packed hour, Tom will share trends from the IBM i and AIX user communities while Dan and Dylan dive into the tech specs for key hardware, including:

  • Magic MarkTRY the one package that solves all your document design and printing challenges on all your platforms. Produce bar code labels, electronic forms, ad hoc reports, and RFID tags – without programming! MarkMagic is the only document design and print solution that combines report writing, WYSIWYG label and forms design, and conditional printing in one integrated product. Make sure your data survives when catastrophe hits. Request your trial now!  Request Now.

  • SB HelpSystems ROBOT GenericForms of ransomware has been around for over 30 years, and with more and more organizations suffering attacks each year, it continues to endure. What has made ransomware such a durable threat and what is the best way to combat it? In order to prevent ransomware, organizations must first understand how it works.

  • SB HelpSystems ROBOT GenericIT security is a top priority for businesses around the world, but most IBM i pros don’t know where to begin—and most cybersecurity experts don’t know IBM i. In this session, Robin Tatam explores the business impact of lax IBM i security, the top vulnerabilities putting IBM i at risk, and the steps you can take to protect your organization. If you’re looking to avoid unexpected downtime or corrupted data, you don’t want to miss this session.

  • SB HelpSystems ROBOT GenericCan you trust all of your users all of the time? A typical end user receives 16 malicious emails each month, but only 17 percent of these phishing campaigns are reported to IT. Once an attack is underway, most organizations won’t discover the breach until six months later. A staggering amount of damage can occur in that time. Despite these risks, 93 percent of organizations are leaving their IBM i systems vulnerable to cybercrime. In this on-demand webinar, IBM i security experts Robin Tatam and Sandi Moore will reveal:

  • FORTRA Disaster protection is vital to every business. Yet, it often consists of patched together procedures that are prone to error. From automatic backups to data encryption to media management, Robot automates the routine (yet often complex) tasks of iSeries backup and recovery, saving you time and money and making the process safer and more reliable. Automate your backups with the Robot Backup and Recovery Solution. Key features include:

  • FORTRAManaging messages on your IBM i can be more than a full-time job if you have to do it manually. Messages need a response and resources must be monitored—often over multiple systems and across platforms. How can you be sure you won’t miss important system events? Automate your message center with the Robot Message Management Solution. Key features include:

  • FORTRAThe thought of printing, distributing, and storing iSeries reports manually may reduce you to tears. Paper and labor costs associated with report generation can spiral out of control. Mountains of paper threaten to swamp your files. Robot automates report bursting, distribution, bundling, and archiving, and offers secure, selective online report viewing. Manage your reports with the Robot Report Management Solution. Key features include:

  • FORTRAFor over 30 years, Robot has been a leader in systems management for IBM i. With batch job creation and scheduling at its core, the Robot Job Scheduling Solution reduces the opportunity for human error and helps you maintain service levels, automating even the biggest, most complex runbooks. Manage your job schedule with the Robot Job Scheduling Solution. Key features include:

  • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

  • LANSAWhen it comes to creating your business applications, there are hundreds of coding platforms and programming languages to choose from. These options range from very complex traditional programming languages to Low-Code platforms where sometimes no traditional coding experience is needed. Download our whitepaper, The Power of Writing Code in a Low-Code Solution, and:

  • LANSASupply Chain is becoming increasingly complex and unpredictable. From raw materials for manufacturing to food supply chains, the journey from source to production to delivery to consumers is marred with inefficiencies, manual processes, shortages, recalls, counterfeits, and scandals. In this webinar, we discuss how:

  • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

  • Profound Logic Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application.

  • SB Profound WC 5536Join us for this hour-long webcast that will explore:

  • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: