Pages

Wednesday, March 18, 2020

Check if program is running interactive or batch

interactive or batch using sql rpg

In the past I wrote about a CL program that submit itself to batch, to do that the CL program must be able to determine if it running interactive or batch. The post describes how you can determine that in a CL program. But what about a RPG program or in SQL?

In this post I will give examples in both RPG and SQL how I can determine if the program is interactive or batch. This is not to replace my tried and trusted CL method. It is just an alternative that might prove useful in certain scenarios.

 

Determine if interactive or batch in RPG

Why would a RPG program need to know if it was running in batch? The program might want to show data on a display file if it is interactive and generate an output file if it is in batch. I can call the Retrieve job information API, QUSRJOBI, and have returned to me a single character variable that will tell me if I am interactive or batch. This is so simple my example program is only 14 lines.

01  **free

02  dcl-pr QUSRJOBI extpgm ;
03    *n char(32766) options(*varsize) ;
04    *n int(10:0) const ;
05    *n char(8) const ;
06    *n char(26) const ;
07    *n char(16) const ;
08  end-pr ;

09  dcl-ds Returned len(86) ;
10    JobType char(1) pos(61) ;
11  end-ds ;

12  QUSRJOBI(Returned:%size(Returned):'JOBI0100':'*':'') ;
13  dump(a) ;

14  *inlr = *on ;

Line 1: I always use totally free RPG.

Lines 2 – 8: Prototype definition for the API program QUSRJOBI. Notice that I don't bother to give any of the parameters names.

Lines 9 – 11: The API returns a data structure of information, depending on which format I pass to it. The data structure needs to be 86 characters long. To determine whether the job is interactive or batch I just need one character in the data structure, in position 61. What the rest of the data structure contains does not concern me for this purpose.

Line 12: Call the API program. The parameters used are:

  1. Data structure for the returned results
  2. Length of the data structure. I calculate that use the %SIZE built in fuction
  3. Results format
  4. Job name. As I am only interested in this job's information I pass *
  5. Internal job identifier. Not needed. I have two apostrophes ( ' ) next to each other, which passes null to the API

Line 13: I have use the DUMP operation code rather than the display operation code as this program needs to run both interactive and batch.

I think this is one of the simplest RPG programs I have shown on this website.

When I run this program interactive and look in the generated spool file, QPPGMDMP, I see the following:

NAME          ATTRIBUTES     VALUE
RETURNED      DS
  JOBTYPE     CHAR(1)        'I'

Then I submit the program to batch. The value of JobType is now:

NAME          ATTRIBUTES     VALUE
RETURNED      DS
  JOBTYPE     CHAR(1)        'B'

Obviously: I = Interactive and B = Batch.

I would not insert the above logic into every program I needed to know if it running interactive or batch. I would create a procedure in an external service program and call that to determine what the job type is. The procedure would look like this:

01  **free
02  ctl-opt nomain option(*srcstmt) ;

03  /define WhatIsJobType
04  /include mylib/mysrc,prototypes
05  /undefine WhatIsJobType

06  dcl-proc WhatIsJobType export ;
07    dcl-pi *n char(1) ;
08    end-pi ;

09    dcl-pr QUSRJOBI extpgm ;
10      *n char(32766) options(*varsize) ;
11      *n int(10:0) const ;
12      *n char(8) const ;
13      *n char(26) const ;
14      *n char(16) const ;
15    end-pr ;

16    dcl-ds Returned len(86) ;
17      JobType char(1) pos(61) ;
18    end-ds ;

19    QUSRJOBI(Returned:%size(Returned):'JOBI0100':'*':'') ;
20    return JobType ;
21  end-proc ;

How does the source for this procedure differ from the program I gave previously?

Line 2: I need the NOMAIN control option so that the compiler knows that none of the procedures in this module can be directly called using the CALL command.

Lines 3 – 5: I have placed the procedure prototype in another member that will be included (copied) into this source member when it is compiled. The advantage in having the procedure prototype in a place where multiple members can use it is that I am guaranteed that the prototype is always the same and correct. The /DEFINE just tells the compiler which part of the external source member I want to include in this source member.

Line 6: Start of the procedure. As I will be calling this from programs and procedures not included in this module I need to give the EXPORT keyword.

Line 7 and 8: The procedure interface is needed as despite there being no parameters passed to this procedure it does return a single character variable to whatever called it.

Line 20: I need to return the value of JobType to the calling program.

Line 21: End of the procedure.

The procedure prototype is found in the member PROTOTYPES:

01  **free

02  /if defined(WhatIsJobType)
03    dcl-pr WhatIsJobType char(1) ;
04    end-pr ;
05  /endif

The prototype definition matches the procedure interface with no parameters passed and one returned.

I compile the source member to create a module, which I will call MOD001. I add this module to my binding directory, TEST, using the following command:

ADDBNDDIRE BNDDIR(TEST) OBJ((MOD001 *MODULE))

The program that calls this procedure is very simple:

01  **free
02  ctl-opt option(*srcstmt) bnddir('TEST') ;

03  /define WhatIsJobType
04  /include mylib/mysrc,prototypes
05  /undefine WhatIsJobType

06  dcl-s Flag char(1) ;

07  Flag = WhatIsJobType() ;
08  dump(a) ;

09  *inlr = *on ;

Line 2: I have put the BNDDIR keyword into the compile option so that neither myself or another can forget give the binding directory name when creating this program.

lines 3 – 5: Procedure prototype definition will be copied in from the member PROTOTYPES.

Line 6: I need to define a variable that will contain the value returned from the procedure.

Line 7: The procedure is called and the value it returns is placed in the variable Flag.

I am using a dump so that I could see the values returned.

Instead of using a procedure calling QUSRJOBI I could have created a CL procedure that contained the RTVJOBA command.

 

Determine if interactive or batch in SQL

To be able to use the SQL statement I am going to show your user profile must have the *JOBCTL special authority, or the user profile has been given function usage authority to either QIBM_DB_SQLADM or QIBM_DB_SYSMON. The need for these authority settings will probably rule this method out for use in a regular production environment as I would not want to give all the users those authorities.

This method is dependent upon a column in the GET_JOB_INFO table function that was added in IBM i 7.4 TR1 and 7.3 TR7. If you are using an earlier release or TR of IBM i then this is not for you.

Those Technology Refreshes added a new column to this table function: V_ACTIVE_JOB_TYPE

This columns can contain 14 possible values, the only ones I am concerned with are:

  • BCH Batch
  • INT Interactive

I can retrieve this value using this SQL statement:

SELECT V_ACTIVE_JOB_TYPE
  FROM TABLE(QSYS2.GET_JOB_INFO('*'))

The parameter of asterisk ( * ) passed to the table function means I only want the information for the current job.

But I want to use this in a program to determine whether the program is running interactive or batch.

01  **free
02  ctl-opt option(*srcstmt) ;
                          
03  dcl-s JobType char(3) ;
                       
04  exec sql SELECT V_ACTIVE_JOB_TYPE
                      INTO :JobType
               FROM TABLE(QSYS2.GET_JOB_INFO('*')) ;
                                                
05  if (JobType = 'INT') ;
06    dsply ('Job type = ' + JobType) ;
07  else ;
08    dump(a) ;
09  endif ;

10  *inlr = *on ;

Line 3: I need a variable to contain the value returned from the table function.

Line 4: I am retrieving the value of V_ACTIVE_JOB_TYPE into the program variable JobType for the current job.

Lines 5 – 9: Depending upon what value was retrieved I am either displaying the value of JobType or performing a program dump.

When I ran the program interactive the following is displayed:

DSPLY  Job type = INT

I submitted the program to batch, and when I looked in the program dump's spool file:

NAME       ATTRIBUTES    VALUE 
JOBTYPE    CHAR(3)       'BCH'

 

You can learn more about this from the IBM website:

 

This article was written for IBM i 7.4, and should work for some earlier releases too.

4 comments:

  1. For some reason the GET_JOB_INFO table function started running incredibly slow last week at our company. As in, up to 15 seconds to return a result - causing our interactive screens to slow way down for our users. It would only run this slow in an interactive job type, batch was still running sub millisecond. We have opened a case with IBM and are changing our programs to not use that table function. We will likely use QUSRJOBI or RTVJOBA via CL program call.

    ReplyDelete
    Replies
    1. If something changes "suddenly" then it likely to be a change someone has made to the partition.

      Delete
    2. We sent job watcher data to IBM and they let us know it is a problem with DNS resolution. We are looking into that and will hopefully discover what was changed or misconfgured.

      Delete
    3. It was indeed a dns issue. After switching our machine to a different dns controller, the problem resolved.

      Delete

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.