24
Tue, Dec
1 New Articles

RTVSQLSRC

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

Several issues ago, I suggested that you consider moving to SQL to define your database files. In this issue, I want to continue to support that notion by providing RTVSQLSRC, a CL command that will allow you to re-create the SQL statements you used to create your files.

Typically, when you create a file using SQL, the SQL statements used to create the file are lost to cyberspace because you don't save them in a source member as you do with DDS. SQL statements are usually entered through a PC-based tool, the OS/400 STRSQL command, or a third-party SQL product, such as ASC's SEQUEL.

What if you have created a file using SQL and later need to change the file or create a similar file on another system? With SQL, you would use the ALTER TABLE statement and change the field attributes or insert a new field. You rarely need to re-create the file when SQL is available.

For example, suppose you need to create a simple file named MYFILE and include two fields, CUSTNO and CUSTNAME. You could run the following SQL statement to create the file:

Create Table MyFile  (CustNo Dec(7,0) Not Null, 
            CustName Char(30) Not Null)

Later, if you need to change the length of one of the fields, you use the ALTER TABLE statement. In this example, if you need to change the length of the CUSTNO field to nine digits, you'd use this statement:

Alter Table MyFile 
   Alter Column CustNo Set Data Type Dec(9,0) Not Null 

The CUSTNO field is now a nine-digit decimal field. Pretty simple, isn't it?

Now, suppose you want to add a field to the file. That's nearly as easy. Use the ALTER TABLE statement again, but this time, indicate that you're adding a field, as follows:

Alter Table MyFile 
   Add Column ShipTo Char(25) Not Null 

You now have a third field in your file: SHIPTO, which is a 25-position character field.

To accomplish a similar function with DDS, you would go into the DDS source, change the field attributes or add the new field, and then run the CHGPF command to "re-create" the file and preserve the existing data.

The cool thing about DDS is that (a) it is very easy to comprehend and (b) you preserve the original source code used to create the file. This allows easy changes or, if necessary, re-creation of the file, a relatively easy task.

But what happens to the SQL statements once they've evaporated from your job?

The good news is that IBM has provided the QSQGNDDL API to retrieve SQL source needed to create/re-create a file. The API, however, is not limited to just files created with SQL; it can also generate SQL source needed to create a file that was originally created with DDS.

If you use the QSQGNDDL API to retrieve the SQL source needed to create the above file, you end up with the following SQL source:

Create Table Myfile (
CustNo Decimal(9,0) Not Null,
CompName Char(30) Not Null,
Address Char(25) not Null);

The API generates the above SQL CREATE statement and places it into a source member of your choosing.

QSQGNDDL: Generate Data Definition Language

DDS is a data definition language (DDL). SQL is also a DDL. Although SQL can be a DDL, it can also be a data manipulation language.

The QSQGNDDL API extracts the necessary SQL DDL statements to create the file. It does not extract the data manipulation language statements, such as INSERT and UPDATE, which may or may not have been used to insert data into the file. When a file is re-created, it is empty, so be aware of that. If you need to preserve your data in an existing file, you need to use the ALTER TABLE statement.

The purpose of this article is simply to illustrate that you can retrieve the SQL used to create an existing file. That file need not necessarily be created with SQL, as the API will also produce SQL CREATE statements for non-SQL physical and logical files.

To use QSQGNDDL, you have to prepare a data structure, identify the file and the source file where the SQL statements are coming from and being stored, and specify any other special flags.

One of the flags that may be specified is to generate a DROP statement. The DROP statement in SQL is the same as a DLTF command. The DROP statement deletes the object, just like the DLTF CL command does. So use caution when the DROP statement is generated.

As mentioned, the API uses a data structure to identify the various flags and attributes you've specified to control the API. The data structure name is SQSR0100, and it's located in the QSQGNDDL source member in QRPGLESRC in the QSYSINC library. You can include it in your RPG IV source code by including the following statement:

   /COPY QSYSINC/QRPGLESRC,QSQGNDDL

This data structure is illustrated in the table below.

Subfield
Attributes
Description
Object Name
Char(258)
The name of the database file whose SQL is to be retrieved
Object Lib
Char(258)
The library containing the file
Object Type
Char(10)
The SQL object type (such as VIEW or TABLE)
Source File
Char(10)
The source file that will receive the generated SQL statements
Source Lib
Char(10)
The source file's library
Source Member
Char(10)
The source member that will receive the generated SQL statements
Severity Level
Int4 (10i0)
The error severity at which to cause the retrieve SQL command to fail
Replace Member
Char(1)
'1' = Replace source member
'0' = Do not replace source member
Stmt Formatting
Char(1)
'1' = Insert hex X'00' at the end of each SQL statement
'0' = Generate usable SQL statements
Date Format
Char(3)
Format for date fields
Date Separator
Char(1)
Date separator symbol
Time Format
Char(3)
Format for time fields
Time Separator
Char(1)
Time separator symbol
Naming Syntax
Char(3)
'SYS' = use OS/400 library/file naming
'SQL' = use SQL library.file naming
Decimal
Char(1)
Symbol used for decimal notation
Standards
Char(1)
'0' = Generate SQL statements with OS/400 extensions
'1' = Generate SQL statements to DB2 standards
'2' = Generate SQL statements to ISO/ANSI standards
Drop
Char(1)
'0' = Do not generate a DROP instruction
'1' = Generate a DROP instruction
Msg Level
Int4 (10i0)
A number indicating the level at which error messages are generated. For example, if 20 is specified, messages for errors of severity 20 or higher are generated, but 19 and lower are not.
COMMENT
Char(1)
'0' = Do not generate COMMENT ON statements
'1' = Generate COMMENT ON statements
LABEL
Char(1)
'0' = Do not generate LABEL ON statements
'1' = Generate LABEL ON statements
Comment Header
Char(1)
'0' = Do not generate a comment block in the header area (top) of the source member
'1' = Generate date/time stamp, version, and other information as a comment at the top of the source member
Reserved
Char(*)
Reserved for future use

Of course, populating this data structure isn't too difficult, but wouldn't it be easier if you had a RTVSQLSRC CL command? I agree. So I wrote one.

Listed in Figure 1 is the command definition source code for the RTVSQLSRC command. It is a typical "user-defined" OS/400 CL command.

RTVSQLSRC:  CMD        PROMPT('Retrieve SQL Source')
             /* Command processing program is: RTVSQLSRC  */
             PARM       KWD(FILE) TYPE(QUAL) MIN(1) PROMPT('File name')
 QUAL:       QUAL       TYPE(*NAME) MIN(1) EXPR(*YES)
             QUAL       TYPE(*NAME) DFT(*LIBL) SPCVAL((*CURLIB) +
                          (*LIBL)) MIN(0) EXPR(*YES) PROMPT('Library')
             PARM       KWD(SRCFILE) TYPE(SRCFILE) PROMPT('Source +
                          file name')
 SRCFILE:    QUAL       TYPE(*NAME) DFT(QSQLSRC) EXPR(*YES)
             QUAL       TYPE(*NAME) DFT(*LIBL) SPCVAL((*CURLIB) +
                          (*LIBL)) MIN(0) EXPR(*YES) PROMPT('Library')
             PARM       KWD(SRCMBR) TYPE(*NAME) DFT(*FILE) +
                          SPCVAL((*FILE)) EXPR(*YES) PROMPT('Source +
                          member')
             PARM       KWD(REPLACE) TYPE(*LGL) DFT(*YES) +
                          SPCVAL((*YES '1') (*NO '0') (*ON '1') +
                          (*OFF '0') (*REPLACE '1') (*NOREPLACE +
                          '0')) EXPR(*YES) PROMPT('Replace source +
                          member')
             PARM       KWD(NAMING) TYPE(*CHAR) LEN(3) RSTD(*YES) +
                          DFT(*SYS) SPCVAL((*SYS 'SYS') (*SQL +
                          'SQL')) EXPR(*YES) PROMPT('Qualified name +
                          syntax')
             PARM       KWD(SQLLVL) TYPE(*INT2) RSTD(*YES) DFT(*SYS) +
                          SPCVAL((*SYS 0) (*DB2SQL 1) (*ANSISQL 2)) +
                          PROMPT('SQL Standards syntax')
             PARM       KWD(GENDROP) TYPE(*LGL) RSTD(*YES) DFT(*NO) +
                          SPCVAL((*NO '0') (*YES '1')) EXPR(*YES) +
                          PROMPT('Generate DROP file statement')
             PARM       KWD(COMMENT) TYPE(*LGL) RSTD(*YES) DFT(*YES) +
                          SPCVAL((*YES '1') (*NO '0')) EXPR(*YES) +
                          PROMPT('Generate header comments')
             PARM       KWD(MSGLVL) TYPE(*INT2) DFT(30) RANGE(0 39) +
                          PROMPT('Message generation level')
             PARM       KWD(SEVLVL) TYPE(*INT2) RSTD(*YES) DFT(30) +
                          VALUES(0 10 20 30 40) PROMPT('Error +
                          severity level to fail')

Figure 1: Here's the command definition source for RTVSQLSRC.

The RTVSQLSRC command allows you to specify a subset of the settings of the data structure used by the QSQGNDDL API. If I did not expose a setting that you want to be able to control, you can simply add it to the command, provided you know something about user-written CL commands.

Re-creating Your File with SQL

The RTVSQLSRC command can retrieve the SQL statements needed to create any file on the system, regardless of whether or not the file was originally created with SQL or DDS. Once the SQL source is retrieved into the source member, you can use the OS/400 RUNSQLSTM command.

The SQL source generated by RTVSQLSRC is formatted to be compatible with the OS/400 RUNSQLSTM CL command. Rebuilding the file using the generated SQL statements is simple. For example:

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

This RUNSQLSTM command will process the SQL statements stored in the CUSTMAST source member of the QSQLSRC source file. RUNSQLSTM is provided on all AS/400 and iSeries systems to run SQL statements stored in a source file member.

The Command Processing Program

To make this command easy to use required wrapping the QSQGNDDL API in an RPG IV program. All the parameters from the command are passed to the program using a procedure interface instead of the conventional *ENTRY/PLIST. Unfortunately, RPG IV requires a prototype whenever a procedure interface is used, so you have duplicate code in the beginning of the source member.

To compile the command's RPG IV command processing program (CPP) RTVSQLSRC, use option 14 in PDM (the CRTBNDRPG command). Optionally, the "Compile with no prompting" from within CODE/400 can be used as well.

The only requirement for the program is that it must run in a "regular" activation group, so I've included the DFTACTGRP(*NO) keyword in the Header specification on line 3 of the source member. See Figure 2.

 ** Program name: RTVSQLSRC
 ** Download the latest version at: 
 **     http://www.rpgiv.com/newsletter
BNDDIR('QC2LE') OPTION(*SRCSTMT:*NODEBUGIO)
DFTACTGRP(*NO)

D RtvSQLSrc       PR
D  FileLib                      20A
D  SrcFileLib                   20A
D  Srcmbr                       10A
D  bReplace                      1N   OPTIONS(*NOPASS)
D  szNaming                      3A   OPTIONS(*NOPASS)
D  nStandard                     5I 0 OPTIONS(*NOPASS)
D  bDrop                         1N   OPTIONS(*NOPASS)
D  bHeader                       1N   OPTIONS(*NOPASS)
D  nMsgLvl                       5I 0 OPTIONS(*NOPASS)
D  nSevLvl                       5I 0 OPTIONS(*NOPASS)

D RtvSQLSrc       PI
D  FileLib                      20A
D  SrcFileLib                   20A
D  Srcmbr                       10A
D  bReplace                      1N   OPTIONS(*NOPASS)
D  szNaming                      3A   OPTIONS(*NOPASS)
D  nStandard                     5I 0 OPTIONS(*NOPASS)
D  bDrop                         1N   OPTIONS(*NOPASS)
D  bHeader                       1N   OPTIONS(*NOPASS)
D  nMsgLvl                       5I 0 OPTIONS(*NOPASS)
D  nSevLvl                       5I 0 OPTIONS(*NOPASS)

D SafeAddMbr      PR
D  SrcFileLib                   20A   Value
D  SrcMbr                       10A   Value

D system          PR                  extproc('system')
D  szCMDString                    *   OPTIONS(*STRING) VALUE

D  apiError       DS                  Inz
D   errDSLen                    10I 0 Inz(%size(apiError))
D   errRtnLen                   10I 0 Inz
D   errMsgID                     7A   Inz(*ALLX'00')
D   errReserved                  1A   Inz(X'00')
D   errMsgData                  64A   Inz(*ALLX'00')

 /COPY QSYSINC/QRPGLESRC,QSQGNDDL
 /COPY QSYSINC/QRPGLESRC,QUSROBJD

D pFileLib        S               *
D InFileLib       DS                  BASED(pFileLib)
D  InFile                       10A
D  InLib                        10A
D pSrcFileLib     S               *
D InSrcFileLib    DS                  BASED(pSrcFileLib)
D  SrcFile                      10A
D  SrcLib                       10A

D QRtvOBJD        PR                  Extpgm('QUSROBJD')
D  rtnData                            LIKE(QUSD0200)
D  rtnLen                       10I 0 Const
D  odFormat                      8A   Const
D  ObjLib                       20A   Const
D  ObjType                      10A   Const
D  apiErrorDS                         Like(ApiError)

D  InReplace      S              1N   Inz('1')
D  InNaming       S              3A   Inz('SYS')
D  inMsgLvl       S              5I 0 Inz(30)
D  inSevLvl       S              5I 0 Inz(30)
D  inStandard     S              5I 0 Inz(0)
D  inDrop         S              1N   Inz('0')
D  inHeader       S              1N   Inz('1')

D FileType        S                   Like(QUSEoA05)
D SQLTempl        S                   Like(QSQR0100)
D QRtvSQLSrc      PR                  Extpgm('QSQGNDDL')
D  sqTempl                            Like(QSQR0100)
D  sqtLen                       10I 0 Const
D  sqFormat                      9A   Const
D  apiError                     16A

C                   eval      *INLR = *ON

C                   eval      pFileLib = %addr(FileLib)
C                   eval      pSrcFileLIb = %addr(SrcFileLib)

C                   if        srcmbr = '*FILE'
C                   eval      srcmbr = InFile
C                   endif
 ** Copy the input parms to their variables, if specified.
C                   if        %parms >= 4
C                   eval      InReplace = bReplace
C                   if        %parms >= 5
C                   eval      InNaming = szNaming
C                   if        %parms >= 6
C                   eval      InStandard= nStandard
C                   if        %parms >= 7
C                   eval      InDrop = bDrop
C                   if        %parms >= 8
C                   eval      InHeader = bHeader
C                   if        %parms >= 9
C                   eval      inMsgLvl = nMsgLvl
C                   if        %parms >= 10
C                   eval      InSevLvl = nSevLvl
C                   endif
C                   endif
C                   endif
C                   endif
C                   endif
C                   endif
C                   endif

 ** Retrieve the file attribute (LF or PF)
C                   callp     qrtvobjd(QUSD0200 : %Len(QUSD0200) :
C                                 'OBJD0200' : FileLIb :'*FILE':apiError)

C                   if        %subst(errMSGID:1:5) = 'CPF98'
C** Failed - Source file not found. :(
C                   return
C                   endif

 ** Does the member exist? It has to or this stupid API won't work!
C                   callp     SafeAddMbr(InSrcFileLib : SrcMbr)
C

C                   eval      FileType = QUSEoA05
C                   CLEAR                   QSQR0100
C                   eval      qsqOBJN = InFile
C                   eval      qsqOBJL = inLib
C                   select
C                   when      FileType = 'PF'
C                   eval      qsqObjT = 'TABLE'
C                   when      FileType = 'LF'
C                   eval      qsqObjT = 'VIEW'
C                   endsl

C                   eval      qsqSFilN = SrcFile
C                   eval      qsqSFilL = SrcLib
C                   eval      qsqSFilM = SrcMbr
C
C                   eval      qsqSL02 = InSevLvl
C                   eval      qsqRo   = InReplace
C                   eval      qsqNo02 = InNaming
C                   eval      qsqML02 = InMsgLvl
C                   eval      qsqSFo  = '0'
C                   eval      qsqDF02 = 'ISO'
C                   eval      qsqTF02 = 'ISO'
C                   eval      qsqDS02 = ' '
C                   eval      qsqTS02 = ' '
C                   eval      qsqDP02 = '.'
C                   eval      qsqSo01 = %char(InStandard)
C                   eval      qsqDo   = InDrop
C                   eval      qsqCo   = '1'
C                   eval      qsqLo   = '1'
C                   eval      qsqHo   = InHeader

 **  Copy the data structure to our local data structure
 **  Note: When everyone is on V5R1 and later, we can use
 **        a qualified data structure instead.
C                   eval      sqltempl = Qsqr0100
C                   CallP     qrtvsqlsrc(sqltempl : %Len(QSQR0100) :
C                                 'SQLR0100' : apiError)
C
C                   return

P SafeAddMbr      B                   Export
D SafeAddMbr      PI
D  SrcFileLib                   20A   Value
D  SrcMbr                       10A   Value

D InSrcFileLib    DS
D  SrcFile                      10A
D  SrcLib                       10A

** The structure returned by the QusRMBRD API.
D szMbrd0100      DS                  INZ
D  nBytesRtn                    10I 0
D  nBytesAval                   10I 0
D  szFileName                   10A
D  szLibName                    10A
D  szMbrName                    10A
D  szFileAttr                   10A
D  szSrcType                    10A
D  dtCrtDate                    13A
D  dtLstChg                     13A
D  szMbrText                    50A
D  bIsSource                     1A
D CPF_MbrNotFound...
D                 C                   CONST('CPF9815')
**----------------------------------------------------------------
** Input Parameters to the QUSRMBRD API
**----------------------------------------------------------------
**  Tells the APIs how long the buffers are that are being used.
D nBufLen         S             10I 0
** Format to be returned
D Format          S              8A   Inz('MBRD0100')
** Whether or not to ignore overrides (0=Ignore, 1 = Apply)
D bOvr            S              1A   Inz('0')

C                   eval      InSrcFileLib = SrcFileLib

**----------------------------------------------------------------
C                   Reset                   apiError
C                   Eval      nBufLen = %size(szMbrD0100)
**----------------------------------------------------------------
C                   Call      'QUSRMBRD'
C                   Parm                    szMbrD0100
C                   Parm                    nBufLen
C                   Parm                    Format
C                   Parm                    SrcFileLIb
C                   Parm                    SrcMbr
C                   Parm                    bOvr
C                   Parm                    apiError

 ** If the member doesn't exist, add it; otherwise, just return.
C                   if        errRtnLen > 0
C                   if        errMsgID = CPF_MbrNotFound or
C                             errMsgID = 'CPF3019' or
C                             errMsgID = 'CPF32DE' or
C                             errMsgID = 'CPF3C27' or
C                             errMsgID = 'CPF3C26'
C                   callp     system('ADDPFM FILE(' + %TrimR(srcLib) + '/'
C                               + srcfile + ') MBR(' + srcmbr + ')')
C                   endif
C                   endif
C                   return
P SafeAddMbr      E  

Figure 2: This is the RTVSQLSRC program source.

Bob Cozzi has been programming in RPG since 1978. Since then, he has written many articles and several books, including The Modern RPG Language --the most widely used RPG reference manual in the world. Bob is also a very popular speaker at industry events such as COMMON and RPG World and is the author of his own Web site, www.rpgiv.com, and of the RPG ToolKit, an add-on library for RPG IV programmers.

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: