Category Archives: IBM i

Converting the current date to a number in SQL

This is handy if you find yourself needing to populate a numeric timestamp field (eight digit number, yyyymmdd) with the current date.

update TheFile                                                
set TheDate = Dec(Replace(Char(current date, ISO), '-', ''), 8, 0)
where TheSelection

Obviously, TheFile is the file you are populating, TheDate is the field and TheSelection is whatever selection criteria you happen to be using.

flattr this!

Converting dates to numbers in RPG

I keep encountering situations where I have to write a record to some painfully old file that stores a date as an integer. Converting a date to an integer is one of those tasks that is very simple to accomplish but which I never remember how to do.

So, for future reference, here is the command for converting the current date to a seven digit integer (cyymmdd format), wrapped in a bit of code that displays the results because we all like to know what we’re writing befor we write it.

H                                                                          
D DateNum         s              7s 0                                      
 /Free                                                                     
                                                                           
     DateNum = %dec(%char(%date():*cymd0):7:0);                            
     dsply DateNum;                                                        
                                                                           
     *INLR = *ON;                                                          
                                                                           
 /End-Free                                                                 

flattr this!

Vim syntax highlighting for the IBM i

As you may have noticed, some of the personal projects I have mentioned on here have been written for the IBM i. I generally develop these locally in Vim and then copy the source files to a server so that I can compile and test them as and when I have a chance.

I like Vim. When I want to just sit down and get something down, Vim provides a distraction-free environment with a powerful collection of features. One of these features is syntax highlighting, and Vim comes with syntax files for a host of languages as standard. Unfortunately, RPG is not one of the languages supported out of the box.

Inevitably, however, it didn’t take much time with Google to discover that someone else had the same thought as me some time ago. So I must say thank you to Martin Rowe of DBG/400 for this rather gorgeous collection of syntax files.

flattr this!

RGZLIB – Now with online help and working compile commands

A couple of weeks ago, I talked about adding help text to CL commands. At the time, I discussed the (rather trivial) process for adding help text to the DSPOSRLS command that I wrote some time ago. Now, I have mad a bit of time to do the same for the RGZLIB command.

While doing this, I discovered that the compile commands I’d included with the README had made a few too many assumptions about the state of my library list. These instructions would work fine if your development library was at the top of your library list but, if not, you would have enciountered issues with members not found.

This is now fixed and the latest version of the README, along with a help-enabled version of the command, can now be found at the usual place.

flattr this!

Adding help text to CL commands

CL commands are handy, and they become even handier when you add enough help text for other people to get the most out of your utilities. So I’m thankful to Ted Holt of the increasingly misnamed Four Hundred Guru for pointing out just how straighforward a process this is.

Because I should, I have gone back to some of my earlier commands and started adding a bit of help text. DSPOSRLS is, admittely, a pretty trivial command as far as documentation goes – there are no parameters and the command can only be used interactively – but I have the source in front of me and the command does provide a conveniently simple place to start.

First, you need a source file in which to enter your help text.
crtsrcpf Library/qpnlsrc

The command I didn’t know about was this one: Generate Command Documentation (GENCMDDOC):

The Generate Command Documentation (GENCMDDOC) command generates an output file which contains documentation for a Control Language (CL) command. The generated file will be one of the following:

  • If *HTML is specified for the Generation options (GENOPT) parameter, the file will contain HyperText Markup Language (HTML) source. The generated file can be displayed using a standard internet browser, and conforms to HTML 4.0 specifications. The information used to generate the file is retrieved from the specified command (*CMD) object and any command help panel group (*PNLGRP) objects associated with the command.
  • If *UIM is specified for the GENOPT parameter, the file will contain User Interface Manager (UIM) source. The generated source is an outline for the online command help for the command. The information used to generate the file is retrieved only from the specified command (*CMD) object. This option is intended to simplify the task of writing online help for CL commands.

The command doesn’t write your documentation for you, but it does provide all the structure you need if you want your command to look like all the other ones on your system. It also makes it quite easy to see how the panel group syntax works.

And so, to the command:
GENCMDDOC CMD(Library/DSPOSRLS) TODIR('/qsys.lib/Library.lib/qpnlsrc.file') TOSTMF(dsposrls.mbr) GENOPT(*UIM)

This will create a member (DSPOSRLS) of type UIM in file QPNLSRC. Change the type to PNLGRP and then you can start editing the the generated text until you have something meaningful. The syntax struck me as being very reminiscient of Markdown which makes it quite easy to get up to speed.

Once you are hapy with your help text, you will need to create the panel group:
CRTPNLGRP PNLGRP(Library/DSPOSRLS) SRCFILE(Library/QPNLSRC)

And now you need to rebuild the command with the help text added:
CRTCMD CMD(Library/DSPOSRLS) PGM(*LIBL/DSPOSRLS) SRCFILE(Library/QCMDSRC) SRCMBR(*CMD) HLPPNLGRP(DSPOSRLS) HLPID(*CMD)

Because the panel group and command are separate objects, you do not need to recreate the command every time you change the panel group. The panel group can be changed as and when you notice the inevitable typos and the updated version will be loaded when you next press F1.

Having the help text baked into your command is handy enough as it is but there is one more rather neat trick worth mentioning. If, once you are happy with your help text, you enter the following command
GENCMDDOC CMD(DSPOSRLS) TODIR('/home/expatpaul') TOSTMF('dsposrls.html') GENOPT(*HTML)

… GENCMDDOC will spit out an IFS file, named dsposrls.html, with all the same text, but marked up for HTML. I have sent the file to my home directory but, if you use TODIR('/www/apachedft/htdocs'), the file will be created in the default location for HTML files that are served by the Apache HTTP server for i.

flattr this!

Flexible SQL with Query Manager on the IBM i

Back in November, I mentioned that it was possible to extract the SQL source from existing queries using the RTVQMQRY command. I have since had an opportunity to play around with the IBM i Query Manager and have found it to be a surprisingly useful reporting tool.

What is it?

According to the iQuery Management Programming pdf, Query Management is the i5/OS implementation of query management Common Programming Interface (CPI). This allows you to retrieve data from a relational database and – most usefully – control the output formatting. It does this, unsurprisingly, by splitting the data extraction (the QM Query) and presentation (the QM form).

Where is it?

Broadly speaking, there are two options you need to know about in order to use Query Manager. First there is the STRQM command which will lead you through the process of creating and maintaining Query Management queries and forms. And then there are the Query Management CL Commands, all of which can be found on menu CMDQM, and which allow you to work with and execute existing QM Queries.

Inevitably, there are multiple ways of achieving the same result. What follows is what works for me but I do recommend that, if you do want to play around with this, investigate the available options to see what works best in your environment.

Before you begin

In STRQM, take the Work with Query Manager profiles option and change the following:

  • The Default library for QM objects is initially set to *CURLIB. This is less than optimal, so I changed this to my own Development/Test library.
  • Paging down, I also found the Default query creation mode. This is initially set to Prompted (which gives you a rather painful Query/400-like interface for creating queries). I changed this to SQL.

Define your Query

There are a couple of options here. The first, and simplest, approach is to STRQM, take the Work with Query Manager queries option and then the Create option. This will give you an SEU screen into which you can enter your SQL (assuming, of course, your default query creation mode is set to SQL). The advantage of taking this approach is that this screen includes both a syntax checker and an option to execute the statement (either all, or a sample). So you can enter, validate and test your SQL, all from the same screen.

If, however, you have an unshakable aversion to SEU, you can create and edit a source member using whatever tool or tools you prefer and then use the CRTQMQRY command to create your query. Once the query is created, your source member is no longer needed.

Whichever way you do it, you will eventually end up with an object of type *QMQRY. This is your QM Query, an SQL select that can be executed with the STRQMQRY command.

And some flexibility

This is where things start to become interesting. The STRQMQRY command includes a Set variables (SETVAR) parameter which allows you to specify up to 50 variables to be set by query management before the query is run. That’s parameters in non-IBM speak. This is easier to explain with an example, so here’s an example using a couple of realy simple purchase order tables. The code for creating these tables and populating them with a couple of one-line orders is below the fold.

And so, to the query. I have, a very simple QM Query (which I have imaginatively named QMTEST) that lists all orders in the simple orders database.

The query looks like this…

select header_order_number, header_order_date,    
       line_ean_number, line_quantity, line_price 
from orderhdr                                     
join orderline on header_index = line_header_index

And the interactive result looks like this…

         HEADER_ORDER_NUMBER  HEADER_ORDER_DATE  LINE_EAN_NUMBER  LINE_QUANTITY  LINE_PRICE
         -------------------  -----------------  ---------------  -------------  ----------
000001   TST0000001           01/21/2013         1234567890123               5     27.5000 
000002   TST0000002           01/22/2013         1234567890321               4     16.5000 
******  * * * * *  E N D  O F  D A T A  * * * * *                                          

This is fine, as far as it goes, but it’s unlikely that you will ever want a list of all orders ever entered. So here’s the same QM Query with a date selection added.

select header_order_number, header_order_date,       
       line_ean_number, line_quantity, line_price    
from orderhdr                                        
join orderline on header_index = line_header_index   
where header_order_date between &FROMDATE and &TODATE

Now, if I run the query interactively, it will prompt for a from and to date. More usefully, however, I can enter a from and to date when launching the query with: STRQMQRY so that:

STRQMQRY QMQRY(QMTEST) SETVAR((FROMDATE '''01/21/2013''') (TODATE '''01/21/2013'''))

Returns this:

         HEADER_ORDER_NUMBER  HEADER_ORDER_DATE  LINE_EAN_NUMBER  LINE_QUANTITY  LINE_PRICE
         -------------------  -----------------  ---------------  -------------  ----------
000001   TST0000001           01/21/2013         1234567890123               5     27.5000 
******  * * * * *  E N D  O F  D A T A  * * * * *                                          

There are a couple of gotchas to watch out for here.

  • Because lower-case characters in variable names are changed to upper-case characters when passed to the command processing program, everything needs to be upper case.
  • If you are passing a string (or date) surrounded by quotes, you need to use triple quotes. The ouer quotes are removed and the double quotes within the value are condensed to a single quote when the value is passed to the command processing program. No, it didn’t make much sense to me either.

    You can, of course, pass a variable rather than a constant value and as soon as you do this, it really does become useful.

    Design your Form

    Now the fun begins. Having a parameterised, executable SQL object has its uses, but the QM form provides a presentation layer that allows you to format the output into something that’s almost pretty enough to put in front of your end users.

    You can edit a source member and then use the CRTQMFORM command to create your QM form, but the source is very sparse indeed. As such, I found that using the Work with Query Manager report forms option in STRQM to go through the prompts was quite useful.

    Execute anywhere

    And then you’re done. QM Queries are not a universal solution, obviously, but I have seen cases where they can be useful – most notably in conjunction with a tool such as CoolSpools that will email the QM Query, as an Excel workbook, to whatever distribution list has asked for it.

    And finally

    This started out as a really short blog post in which my only intention was to make a few notes to help me keep track of what I’m doing. As can sometimes happen, however, it grew into this monster that you have just ploughed your way through.

    Continue reading

flattr this!

Use SQL To Update A Sequence Number

Oddly enough, I have encountered a couple of situations in the past where I have needed to regenerate sequence numbers in a table. The approach I took at the time involved populating the field in question with a value generated from the Relative Record Number.

This approach looks a lot neater, which is why I’m noting it here for future reference.

flattr this!

Neat trick of the day: Editing database files as if they were stream files

The background

Today I found and interface that works1 by copying a stream file into a database table and then attempting to parse the resulting mess. It’s been running for almost ten years and today it broke because some of the numbers in the stream file had decimal points in them2.

The interface in question is due to be retired shortly, so the program isn’t going to be fixed and the solution is to fix the data and then chase the folks that provided the stream file.

Have you ever tried to query or update a table that not only contains unstructured data but also has Carriage Return and Line Feed characters scattered throughout?

The solution

Work with Object Links is your friend.

This is the command you need:

WRKLNK '/QSYS.LIB/library.LIB/file.FILE/member.MBR'

The editing functionality is a bit basic, but it’s a lot easier than trying to database functionality to edit non-database data.

It’s also worth noting that the Work with Object Links actually knows what to do with Carriage Return and Line Feed characters.

Footnotes

1 In the loosest sense of the word.
2 Yes, really.

flattr this!

Killing queries with SQL

Back in the 20th Century, Query was a handy tool for… well, writing ad-hoc queries. The form-filling interface is remarkably simple – it really can be used by anyone – and rather inflexible. Now, of course, we have SQL – which is a lot more powerful – and a collection of third-party tools to help with the building your query.

But Queries exist. Some of them are on menus. They must be destroyed!

Here’s an approach:

RTVQMQRY QMQRY(querylib/queryname) SRCFILE(sqllib/QSQLSRC) ALWQRYDFN(*ONLY)

The Retrieve Query Management Query (RTVQMQRY) command allows you to retrieve Structured Query Language (SQL) source from a query management query (QMQRY) object. The source records are placed into an editable source file member.

But you don’t have to have a QMQRY object to use this command. ALWQRYDFN allows you to specify whether query information is taken from a query definition (QRYDFN) object when a query management (QMQRY) object cannot be found. There are three possibilities:

  • *NO: Never use the QRYDFN. If the QMQRY object doesn’t exist, the command will fail
  • *YES: If the QMQRY object doesn’t exist, use the QRYDFN
  • *ONLY: Ignore the QMQRY object and just use the QRYDFN

Once the command is executed, you will have a member (called queryname) in library/member sqllib/QSQLSRC, based on query querylib/queryname.

And now your options are endless.

flattr this!

Getting an ISO date from a 6 digit number – The lazy approach

Someone asked me today if I had an SQL function to convert a six digit number into an ISO date. I don’t because if you store dates in date fields then there is no need to convert the date back into a date.

But we have a legacy table. And this table stores a date as a six digit number which represents YYMMDD. I’m not going to write a function for this, but the approach is pretty simple, and shown below:

with ISODATE as
 (select STORENO as I_STORENO,
 char(20000000 + STOREOPEN) as I_STOREOPEN
 from STORES)
select STORENO, 
       STORENAME,  
       date(substr(I_STOREOPEN, 1, 4) || '-' || 
            substr(I_STOREOPEN, 5, 2) || '-' || 
            substr(I_STOREOPEN, 7, 2)) 
from STORES 
join ISODATE on STORENO = I_STORENO 

In order to improve the clarity a bit, I have broken it up a bit:

The with clause builds a temporary table of store numbers and nicely formatted opening dates. This converts the six digit date into an eight character string which is almost good enough to pass to the build in date function.

The main select then splits the date into its component parts and then concatenates these (with a ‘-’ separator) so that the date can handle it.

For example:

  • A STOREOPEN date of 1st February 2003 would be represented in the STORES table as 30201
  • The with clause builds table ISODATE in which I_STORENO is represented as 20030201
  • The substr and concatenation functions generate a character representation of 2003-02-01 and then passes this to the date function

And now you have a value on which normal date calculations can be performed.

Of course, the better approach would be to replace these legacy tables in order to make this sort of manipulation unnecessary.

flattr this!