RPGFree reading and updating a file using SQL embedded syntax

Reading and Updating a File Using SQL Embedded in RPGFree




In this tutorial, I’ll show you how to read a file from the beginning and update a field in the same file using embedded SQL in free-format RPG IV (RPGFree).



Step 1: Create the Physical File

First, we need to create a file and populate it with some test data. Create the file using the following SQL source:

Source member: IPFLSQL
Source file: myLib/MySourceFile

-- IPFLSQL.SQL
-- Create the physical table IPFLI00F

CREATE OR REPLACE TABLE myLib/IPFLI00F (
  IIFANN CHARACTER(1) NOT NULL WITH DEFAULT,
  IITEXT CHARACTER(25) NOT NULL WITH DEFAULT
)
RCDFMT IPFLI;

Run this member with the following command:

RUNSQLSTM SRCFILE(myLib/MySourceFile) SRCMBR(IPFLSQL)

Now, manually insert a few records into the file IPFLI00F using DFU, ACS, or any method of your choice. The values are not important — just a few test records.


Step 2: Compile and Run the RPG Program

Below is the RPGFree program that reads all records from the file and updates the field IIFANN to '*' for each record:

Source member: IPFL01
Type: SQLRPGLE

**free
// ********************************************************
// *  How to read an entire file from the beginning       *
// *  and update a field in the same file                 *
// *  using SQL in RPG IV                                 *
// ********************************************************
// *  This program reads the file IPFLI00F and updates    *
// *  the field IIFANN to '*' for all records             *
// *                                                      *
// *  The program uses a cursor to fetch records and      *
// *  updates them in a loop.                             *
// *  The program is written in RPGfree with SQL.         *
// *  The SQL cursor is declared to read the file.        *
// *  The program uses a data structure to hold the       *
// *  fetched record data.                                *
// *  The program ends by closing the cursor and setting  *
// *  the last record indicator to on.                    *
// *  The program is expected to be run in an environment *
// *  where the file IPFLI00F exists and is accessible.   *
// ********************************************************
ctl-opt option(*nodebugio:*srcstmt:*nounref);
 
// IPFLI00F This file is readed from the beginning
 
dcl-ds RecordDsC1;
  rDsIIFANN char( 1);
  rDsIITEXT char(25);
end-ds;
 
exec sql set option Commit = *None;
exec sql set option SRTSEQ = *LANGIDSHR;
exec sql set option CLOSQLCSR = *ENDMOD;
// preapre the data recordset
exec sql declare C1 cursor for
  select
         IIFANN,
         IITEXT
  from IPFLI00F
  where
      IIFANN <> 'A'
  order by IITEXT
  for read only;
 
// ********************************************************
// * PGM start                                            *
// ********************************************************
 
exec sql close C1;
exec sql open C1;
 
dow sqlCode = 0;
  exec sql fetch C1 into :RecordDsC1;
  if sqlcode = 0;
    // update field IIFANN table IPFLI00F
    exec sql update IPFLI00F
      set IIFANN = '*';
  endif;
enddo;
exec sql close C1;
*inlr = *on;

🔎 Note: The UPDATE statement should include a WHERE clause to avoid updating all records unconditionally.


Step 3: Run the Program

Use the CALL command to run the program:

CALL IPFL01

If you want to debug it, use:

STRDBG PGM(IPFL01) UPDPROD(*YES) OPMSRC(*YES)

After the program runs, all records will have IIFANN set to '*'.


Conclusion

This is a simple example of how to use embedded SQL in RPGFree to read and update data. By using cursors and data structures, you can manipulate records in a clean and modern way on IBM i.

Feel free to share your thoughts or ask questions in the comments — I read and appreciate every single one!








Update: Thanks to anonymous – A better way to update rows using cursors

A big thank you to the anonymous reader who left a great comment below this post.

They pointed out a better way to perform updates in an SQLRPGLE program using cursors, by simply including FOR UPDATE OF in the DECLARE CURSOR statement and using WHERE CURRENT OF in the UPDATE.

This avoids the need to perform a second search with a WHERE clause (based on keys), since the row can be updated directly using the cursor's current position. This is more efficient, cleaner, and less error-prone.

Here is the updated version of the source code using this approach:

**free
// ********************************************************
// *  How to read an entire file from the beginning       *
// *  and update a field in the same file                 *
// *  using SQL in RPG IV                                 *
// ********************************************************
// *  This program reads the file IPFLI00F and updates    *
// *  the field IIFANN to '*' for all records             *
// *                                                      *
// *  The program uses a cursor to fetch records and      *
// *  updates them in a loop.                             *
// *  The program is written in RPGfree with SQL.         *
// *  The SQL cursor is declared to read the file.        *
// *  The program uses a data structure to hold the       *
// *  fetched record data.                                *
// *  The program ends by closing the cursor and setting  *
// *  the last record indicator to on.                    *
// *  The program is expected to be run in an environment *
// *  where the file IPFLI00F exists and is accessible.   *
// ********************************************************

ctl-opt option(*nodebugio:*srcstmt:*nounref);

// IPFLI00F This file is read from the beginning

dcl-ds RecordDsC1;
  rDsIIFANN char( 1);
  rDsIITEXT char(25);
end-ds;

exec sql set option Commit = *None;
exec sql set option SRTSEQ = *LANGIDSHR;
exec sql set option CLOSQLCSR = *ENDMOD;

// Prepare the data recordset and allow update on IIFANN
exec sql declare C1 cursor for
  select
         IIFANN,
         IITEXT
  from IPFLI00F
  where
      IIFANN <> 'A'
  order by IITEXT
  for update of IIFANN;

// ********************************************************
// * PGM start                                            *
// ********************************************************

exec sql close C1;
exec sql open C1;

dow sqlCode = 0;
  exec sql fetch C1 into :RecordDsC1;
  if sqlcode = 0;
    // Update field IIFANN in table IPFLI00F using cursor
    exec sql update IPFLI00F
      set IIFANN = '*'
      where current of C1;
  endif;
enddo;

exec sql close C1;
*inlr = *on;

I always appreciate constructive feedback like this. It helps improve both the code and the learning experience for the whole IBM i community.

Thank you again, anonymous! 😊
Aldo





Update2: Thanks to Christian Larsen – A better way to update rows using cursors

A special thanks to Christian Larsen.

I really appreciate Christian’s contribution. His attention and input help make these examples cleaner, more modern, and more professional.

Here is the updated version of the source code using Christian suggestion:


**free
// ********************************************************
// *  How to read an entire file from the beginning       *
// *  and update a field in the same file                 *
// *  using SQL in RPG IV                                 *
// ********************************************************
// *  This program reads the file IPFLI00F and updates    *
// *  the field IIFANN to '*' for all records             *
// *                                                      *
// *  The program uses a cursor to fetch records and      *
// *  updates them in a loop.                             *
// *  The program is written in RPGfree with SQL.         *
// *  The SQL cursor is declared to read the file.        *
// *  The program uses a data structure to hold the       *
// *  fetched record data.                                *
// ********************************************************
 
ctl-opt main(main)
        option(*nodebugio:*srcstmt:*nounref)
        dftactgrp(*no)
        actgrp(*new);
 
// IPFLI00F - This file is read from the beginning
 
dcl-ds RecordDsC1;
  rDsIIFANN char(1);
  rDsIITEXT char(25);
end-ds;
 
dcl-proc main;
 
  exec sql
    set option Commit = *None,
               SRTSEQ = *LANGIDSHR,
               CLOSQLCSR = *ENDMOD;
 
  // Prepare the data recordset and allow update on IIFANN
  exec sql declare C1 cursor for
    select IIFANN,
           IITEXT
      from IPFLI00F
     where IIFANN <> 'A'
     order by IITEXT
     for update of IIFANN;
 
  exec sql open C1;
 
  dow sqlCode = 0;
    exec sql fetch C1 into :RecordDsC1;
    if sqlcode = 0;
      // Update field IIFANN in table IPFLI00F using cursor
      exec sql update IPFLI00F
        set IIFANN = '*'
        where current of C1;
    endif;
  enddo;
 
end-proc;


Thanks, Christian, for taking the time to share your insights!

Comments

  1. Hi Aldo. Note that your comment @ Note: can be avoided entirely by including in your declare of cursor the phrase "FOR UPDATE OF" followed by list of column name(s) you'll be updating, then your UPDATE SQL can be changed to UPDATE table SET column name(s) = values WHERE CURRENT OF the cursor name. That will update the row the cursor is currently pointing to, without having to perform another fetch behind the scenes (which is what happens when you include a where clause in an UPDATE statement.)

    Cheers :)

    ReplyDelete
    Replies
    1. Hi anonymous,
      thank you for your valuable suggestion!
      I have updated the post with a new version of the source code that uses FOR UPDATE OF IIFANN in the cursor declaration and WHERE CURRENT OF C1 in the update statement, as you recommended.

      I would really appreciate it if you could take a quick look and let me know if this implementation fully aligns with what you had in mind.

      Thanks for helping improve my blog!

      Delete
  2. Hey Aldo, great post.

    I was just going to mention "WHERE CURRENT OF." I already talked about that in one of my videos.

    Also, I'd like to say that the program could be improved a little more...
    Why not using a non-cycled structure?
    Also, I see that you use the option of automatically closing cursors at the end of the module execution... that way you don't have to close it manually.

    For example, we can make some changes...

    ctl-opt main(main) option(*nodebugio:*srcstmt:*nounref) dftactgrp(*no) actgrp(*new);

    // Here I'm indicating that my main procedure is going to be "main" ... and telling the compiler that I'm not going to use the default activation group (in fact, I want to use the *NEW group, in this case)...

    dcl-proc main;
    // Starting the main procedure ... everything is inside it, in this program...

    dcl-ds RecordDsC1;
    rDsIIFANN char( 1);
    rDsIITEXT char(25);
    end-ds;

    exec sql
    set option commit = *none,
    srtseq = *langidshr,
    closqlcsr = *endmod;

    // This can be written like this, and there's no need to repeat the "exec sql" statement.

    // exec sql close C1;
    // This isn't necessary, since we specified "closqlcsr=*endmod", and we are starting the program in a *NEW activation group, so everything is closed when it starts...
    exec sql open C1;

    // ...

    // exec sql close C1;
    // *inlr = *on;
    // We no longer need these two statements.

    end-proc;
    // This way we are ending the program.

    In fact, since we specified the *new activation group, it's not even necessary to specify "closqlcsr=*endmod," since with new, all open cursors would be closed upon program termination.
    Instead of *endmod, we can use *endpgm, or *endactgrp....

    Thanks for all ;-)

    ReplyDelete
    Replies
    1. Hi Christian,
      I’ve just updated the post with your suggestions; the code is now cleaner and more modern thanks to your input.
      I really appreciate the time you took to read my article and share your insights. It’s great to see you interested in my posts, and your contribution has definitely improved this example.

      I invite you to check the updated source to make sure it matches exactly what you had in mind.

      Thanks again!

      Delete

Post a Comment

Popular posts from this blog

options(*CONVERT) in RPGfree '

(IBM i fans only) Efficient WRKSPLF with WSF - How to Search string into spooled files, Sort, and Generate PDFs on IBMi