Wednesday, August 6, 2014

User Spaces introduction

user space quscrtus qusdltus qusptrus

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:

  1. Qualified user space name, 20 character, User Space name 1-10 and library 11-20.
  2. Extended attribute, 10 character.
  3. Initial size of User Space, 10,0 integer.
  4. Initial value of User Space, 1 character.
  5. Public Authority, 10 character.
  6. Text description, 50 character.
  7. Replace existing, 10 character.
  8. 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:

  1. 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.
  2. Extended attribute: It is not used in the QUSLFLD, therefore, I am going to pass blank to the API.
  3. Initial size of User Space: I am going to create it as 128K.
  4. 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.
  5. Public Authority: As I want to do anything and everything to this object I am going to use '*ALL'.
  6. 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.
  7. 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.
  8. 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:

  1. Qualified user space name, 20 character, User Space name 1-10 and library 11-20.
  2. Return pointer, pointer.
  3. 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)
  1. Offset: The number of bytes away from the start of the User Space the list starts.
  2. Count: The number of list items (sets of data) that has been returned.
  3. 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:

  1. Qualified user space name, 20 character, User Space name 1-10 and library 11-20.
  2. 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:

 

This article was written for IBM i 7.1.

15 comments:

  1. What people often miss is that a user space is basically similar to an IFS stream file and can be viewed via EDTF.

    EDTF '/qsys.lib/lnlib.lib/spoollist.usrspc'

    Try it.

    ReplyDelete
    Replies
    1. Thanks for the info, i tried and it worked.

      Delete
  2. Love the free form example...push the Future!

    ReplyDelete
  3. I looked into what you mentioned and I made another post about what I found. You can read it here.

    ReplyDelete
  4. Excellent post and very interesting even for me as a java peogrammer with only basic understanding of RPG. Please keep publishing.

    ReplyDelete
    Replies
    1. To understand RPG for a Java programmer, all it takes is a converter of fixed format like Linoma or Arcad.

      In 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 ;)

      Delete
  5. Thanks Simon Hutchinson for such an informative article.

    ReplyDelete
  6. Could you also give an example of the api procedure QUSLSPL associated with user spaces..appreciate it

    ReplyDelete
    Replies
    1. Yes, I do do have a good example of using QUSLSPL coming in a future post.

      Delete
  7. Thank you for the article, and sorry for commenting late.

    I 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.

    ReplyDelete
  8. Thank you sir. You are doing great service not only to IBM i community, but to IBM i itself. Let me explain:

    There 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.

    ReplyDelete
    Replies
    1. You 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 :)

      Delete
  9. Just 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".

    The 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."

    ReplyDelete
  10. Thank you for this article. It was very helpful.

    ReplyDelete

To prevent "comment spam" all comments are moderated.
Learn about this website's comments policy here.

Some people have reported that they cannot post a comment using certain computers and browsers. If this is you feel free to use the Contact Form to send me the comment and I will post it for you, please include the title of the post so I know which one to post the comment to.