User Space is used by many IBM APIs to hold the generated results. It is similar to a data area, but can be a lot larger and will expand in size as more data is added to it. What I consider to be a drawback for using them is that they can only be accessed by API. As I want to talk about some APIs in future posts I thought it would be a good idea to give this introduction to using User Spaces first.
In this example I will be using the following User Space APIs to:
- QUSCRTUS – Create user space
- QUSDLTUS – Delete user space
- QUSPTRUS – Get pointer to user space
In this example I am going to use the List Fields API, QUSLFLD, to generate a list of the fields in a file. I just chose this API at almost random to show how to use the APIs in a very simple manner to demonstrate how they work. You will probably want to add more complicated code to your use of these APIs to cope with errors, etc.
First we need to create the User Space. This we do with the QUSCRTUS API, which requires the following parameters passed to it:
- Qualified user space name, 20 character, User Space name 1-10 and library 11-20.
- Extended attribute, 10 character.
- Initial size of User Space, 10,0 integer.
- Initial value of User Space, 1 character.
- Public Authority, 10 character.
- Text description, 50 character.
- Replace existing, 10 character.
- Error code, 32767 character.
Below is the Procedure prototype, first in free format RPG and then in fixed column. I have decided to give the prototype a meaningful name, CrtUserSpace, rather than use the API's name, QUSRCRTUS. The API's name has to be given in the extpgm:
01 dcl-pr CrtUserSpace extpgm('QUSCRTUS') ; 02 *n char(20) const ; // Name 03 *n char(10) const ; // Attribute 04 *n int(10) const ; // Initial size 05 *n char(1) const ; // Initial value 06 *n char(10) const ; // Authority 07 *n char(50) const ; // Text 08 *n char(10) const options(*nopass) ; // Replace existing 09 *n char(32767) options(*varsize:*nopass) ; // Error feedback 10 end-pr ; 01 D CrtUserSpace PR extpgm('QUSCRTUS') * Name 02 D 20A const * Attribute 03 D 10A const * Intial size 04 D 10I 0 const * Initial value 05 D 1A const * Authority 06 D 10A const * Text 07 D 50A const * Replace existing 08 D 10A const options(*nopass) * Error feedback 09 D 32767A options(*varsize:*nopass) |
I could have given each of the variables in the Procedure prototype a name, but I prefer not to. Therefore, I have had to code *n in the free format to indicate that there is no name. I do not have to do that in the fixed column code.
When I call the API I would do it like this:
38 CrtUserSpace('@USRSPACE QTEMP':'':131072:x'00': '*ALL':'List of fields in TESTFILE':'*YES':QUSEC) ; |
The values passed are as follows:
- Qualified user space name: I am going to call my user space @USRSPACE and have it in library QTEMP. I always build my User Spaces in QTEMP as I use them for just a short period of time.
- Extended attribute: It is not used in the QUSLFLD, therefore, I am going to pass blank to the API.
- Initial size of User Space: I am going to create it as 128K.
- Initial value of User Space: IBM says I will "achieve the best performance" if I initialize it with x'00', so I will follow their advice.
- Public Authority: As I want to do anything and everything to this object I am going to use '*ALL'.
- Text description: This is an optional field, and I am going to use it so that the User Space's description will contain the name of the file.
- Replace existing: If there is already a User Space of the name @USRSPACE in QTEMP then by passing '*YES' I will replace it with this User Space.
- Error code: I am going to use standard error code data structure, QUSEC, that I have described in the post QCAPCMD another alternative to QCMDEXC, to break this field into its parts.
Having created the User Space the next step is to fill it with data from the API we are using. As this post is about how to use User Spaces I am going to skip this part, and go straight to how we retrieve the data from the User Space.
You use pointers to retrieve the data from a User Space, therefore, the next API we need is QUSPTRUS, Retrieve Pointer to User Space. This API has three parameters:
- Qualified user space name, 20 character, User Space name 1-10 and library 11-20.
- Return pointer, pointer.
- Error code, 32767 character.
Below is the Procedure prototype, first in free format RPG and then in fixed column. As I mentioned above, I like to give the prototypes more meaningful names. I have used the name GetPointer instead of QUSPTRUS.
11 dcl-pr GetPointer extpgm('QUSPTRUS') ; 12 *n char(20) const ; // Name 13 *n pointer ; // Pointer to user space 14 *n char(32767) options(*varsize:*nopass) ; // Error feedback 15 end-pr ; 11 D GetPointer PR extpgm('QUSPTRUS') * User space name 12 D 20A const * Pointer 13 D * * Error feedback 14 D 32767A options(*varsize:*nopass) |
40 GetPointer('@USRSPACE QTEMP':UserSpacePointer) ; |
When I retrieve the pointer I get the "header" information from the User Space. I define the header information in a data structure like this, free format followed by fixed, notice how the pointer, UserSpacePointer, is defined in the BASED keyword. I have only defined the fields that are of interest to me:
29 dcl-ds ListHeader based(UserSpacePointer) qualified ; 30 Offset int(10) pos(125) ; 31 Count int(10) pos(133) ; 32 Size int(10) pos(137) ; 33 end-ds ; 29 D ListHeader DS based(UserSpacePointer) 30 D qualified 31 D Offset 10I 0 overlay(ListHeader:125) 32 D Count 10I 0 overlay(ListHeader:133) 33 D Size 10I 0 overlay(ListHeader:137) |
- Offset: The number of bytes away from the start of the User Space the list starts.
- Count: The number of list items (sets of data) that has been returned.
- Size: How many bytes each list item is.
With the information from the header I can then retrieve the data from the User Space to get the specific information I wanted. In this example it is the names of the fields in the file TESTFILE. This information is used using another pointer, that I have called FieldPointer. This is the part where the output is specific to the API that was called to output to the User Space, QUSLFLD. As I am only interested in the field names I have only defined them in the data structure FieldInfo, notice that the pointer FieldPointer is defined in the BASED keyword. Free format version is followed by fixed format.
34 dcl-ds FieldInfo based(FieldPointer) qualified ; 35 Name char(10) pos(1) ; 36 end-ds ; 34 D FieldInfo DS based(FieldPointer) 35 D qualified 36 D Name 10A overlay(FieldInfo:1) |
I am using a FOR to perform the loop in the program. I perform the loop the number of times held in the Count retrieved from the User Space's header, line 41:
41 for i = 1 to ListHeader.Count ; 42 FieldPointer = UserSpacePointer + ListHeader.Offset + (ListHeader.Size * (i - 1)) ; 43 dsply FieldInfo.Name ; 44 endfor ; |
Every time the loop is performed I retrieve the next list item from the User Space into the FieldPointer, line 42. Then I display the name of the field, FieldInfo.Name, using the DSPLY operation code, line 43.
Once I have performed the loop the allotted number of times the program then end. What about the User Space? If I do not delete it then it is left on disk using up valuable disk space. This is a moot point in my example as I built the User Space in QTEMP. But I want to show how you can delete if you want, using the QUSDLTUS.
The Delete User Space just has two parameters:
- Qualified user space name, 20 character, User Space name 1-10 and library 11-20.
- Error code, 32767 character.
I have defined the Procedure prototype with the name DltUsrSpace:
16 dcl-pr DltUserSpace extpgm('QUSDLTUS') ; 17 *n char(20) const ; // Name 18 *n char(32767) options(*varsize:*nopass) ; // Error feedback 19 end-pr ; 16 D DltUserSpace PR extpgm('QUSDLTUS') 17 D 20A const 18 D 32767A options(*varsize:*nopass) |
At the end of my program I call it:
46 DltUserSpace('@USRSPACE QTEMP':QUSEC) ; |
As I have shown how all the parts work I need to put it all together.
01 dcl-pr CrtUserSpace extpgm('QUSCRTUS') ; 02 *n char(20) const ; // Name 03 *n char(10) const ; // Attribute 04 *n int(10) const ; // Initial size 05 *n char(1) const ; // Initial value 06 *n char(10) const ; // Authority 07 *n char(50) const ; // Text 08 *n char(10) const options(*nopass) ; // Replace existing 09 *n char(32767) options(*varsize:*nopass) ; // Error feedback 10 end-pr ; 11 dcl-pr GetPointer extpgm('QUSPTRUS') ; 12 *n char(20) const ; // Name 13 *n pointer ; // Pointer to user space 14 *n char(32767) options(*varsize:*nopass) ; // Error feedback 15 end-pr ; 16 dcl-pr DltUserSpace extpgm('QUSDLTUS') ; 17 *n char(20) const ; // Name 18 *n char(32767) options(*varsize:*nopass) ; // Error feedback 19 end-pr ; 20 /copy qsysinc/qrpglesrc,qusec 21 dcl-pr ListFields extpgm('QUSLFLD') ; 22 *n char(20) const ; // User space name 23 *n char(8) const ; // Format 24 *n char(20) const ; // File name 25 *n char(10) const ; // Record format 26 *n char(1) const ; // Use override 27 *n char(32767) options(*varsize:*nopass) ; // Error feedback 28 end-pr ; 29 dcl-ds ListHeader based(UserSpacePointer) qualified ; 30 Offset int(10) pos(125) ; 31 Count int(10) pos(133) ; 32 Size int(10) pos(137) ; 33 end-ds ; 34 dcl-ds FieldInfo based(FieldPointer) qualified ; 35 Name char(10) pos(1) ; 36 end-ds ; 37 dcl-s i int(3) ; 38 CrtUserSpace('@USRSPACE QTEMP':'':131072:x'00': '*ALL':'List of fields in TESTFILE':'*YES':QUSEC) ; 39 ListFields('@USRSPACE QTEMP':'FLDL0100':'TESTFILE *LIBL': 'TESTFILER':'0':QUSEC) ; 40 GetPointer('@USRSPACE QTEMP':UserSpacePointer) ; 41 for i = 1 to ListHeader.Count ; 42 FieldPointer = UserSpacePointer + ListHeader.Offset + (ListHeader.Size * (i - 1)) ; 43 dsply FieldInfo.Name ; 44 endfor ; 45 *inlr = *on ; 46 DltUserSpace('@USRSPACE QTEMP':QUSEC) ; |
For those of you who cannot use the free format for definition specifications the definition section would look like:
01 D CrtUserSpace PR extpgm('QUSCRTUS') * Name 02 D 20A const * Attribute 03 D 10A const * Intial size 04 D 10I 0 const * Initial value 05 D 1A const * Authority 06 D 10A const * Text 07 D 50A const * Replace existing 08 D 10A const options(*nopass) * Error feedback 09 D 32767A options(*varsize:*nopass) 11 D GetPointer PR extpgm('QUSPTRUS') * User space name 12 D 20A const * Pointer 13 D * * Error feedback 14 D 32767A options(*varsize:*nopass) 16 D DltUserSpace PR extpgm('QUSDLTUS') * User space name 17 D 20A const * Error feedback 18 D 32767A options(*varsize:*nopass) 20 /copy qsysinc/qrpglesrc,qusec 21 D ListFields PR extpgm('QUSLFLD') 22 D 20A const 23 D 8A const 24 D 20A const 25 D 10A const 26 D 1A const 27 D 32767A options(*varsize:*nopass) 29 D ListHeader DS based(UserSpacePointer) 30 D qualified 31 D Offset 10I 0 overlay(ListHeader:125) 32 D Count 10I 0 overlay(ListHeader:133) 33 D Size 10I 0 overlay(ListHeader:137) 34 D FieldInfo DS based(FieldPointer) 35 D qualified 36 D Name 10A overlay(FieldInfo:1) 37 D i S 3I 0 |
You can learn more about these built in functions from the IBM web site:
- Create User Space API, QUSRCRTUS
- Retrieve Pointer to User Space API, QUSPTRUS
- Delete User space API, QUSDLTUS
This article was written for IBM i 7.1.
What people often miss is that a user space is basically similar to an IFS stream file and can be viewed via EDTF.
ReplyDeleteEDTF '/qsys.lib/lnlib.lib/spoollist.usrspc'
Try it.
Thanks for the info, i tried and it worked.
DeleteLove the free form example...push the Future!
ReplyDeleteI looked into what you mentioned and I made another post about what I found. You can read it here.
ReplyDeleteExcellent post and very interesting even for me as a java peogrammer with only basic understanding of RPG. Please keep publishing.
ReplyDeleteTo understand RPG for a Java programmer, all it takes is a converter of fixed format like Linoma or Arcad.
DeleteIn this case, Simon has given both the FREE code and the fixed format code. Consider just the first part that is in FREE, and ignore the second part ;)
Thanks Simon Hutchinson for such an informative article.
ReplyDeleteCould you also give an example of the api procedure QUSLSPL associated with user spaces..appreciate it
ReplyDeleteYes, I do do have a good example of using QUSLSPL coming in a future post.
DeleteThank you for the article, and sorry for commenting late.
ReplyDeleteI have a general question about prototyping. You wrote: "I could have given each of the variables in the Procedure prototype a name, but I prefer not to."
Why do you prefer not to give the variables names? Do you do so only in API prototypes or also in your own sub procedures?
Until now, I prefer having meaningful names in a prototype in regards of documentation. For example, LPEX’s autocompletion (keyboard shortcut: ctrl + space) shows the variable names used in the prototype in a tooltip.
Thank you sir. You are doing great service not only to IBM i community, but to IBM i itself. Let me explain:
ReplyDeleteThere are powerful tools that scare people, and therefore not used. This scare needs to defeated. The fear areas are:
1. ILE
2. Pointers
3. User Spaces
4. Data Queues
5. IFS Files
6. SQL (believe it or not)
7. SQL PL
8. DDL
9. Triggers
10 APIs
11. **FREE
(Kindly add to this long list)
Only when these myths are broken, the true power of Power Systems will be known to the world.
OK, you got me on number 4.
DeleteYou think people are not afraid of data queues? You gotta see the shops where they would rather write on tables directly and wait if there is a record lock, rather than write on a data queue :)
DeleteJust before someone would ask me, "What is the fear of SQL?", let me tell what they say. "Well, one single mistake and you can corrupt the entire file".
ReplyDeleteThe answer I give, "If you use commitment control, you can recover. However if your use DLTF, and had not taken a backup, you are screwed."
Thank you for this article. It was very helpful.
ReplyDelete