skip to Main Content

I have a project for sending data from DB2 on an IBM system over to a PostgreSQL server on a RHEL system. I am using a trigger that sends information to a data queue, which then gets read and sent over to the PostgreSQL server using a SQL statement through a JDBC connection on RPGLE.

The code is (more or less) as follows (I had to remove actual column and table names for security reasons):


dcl-proc doPostgreConn export;
  dcl-pi doPostgreConn like(connection) end-pi;
  //Code to change and set CLASSPATH variable, if necessary is here
  //...

  prop = JDBC_Properties();
  JDBC_setProp(prop: 'user'         : 'user');
  JDBC_setProp(prop: 'password'     : 'password');
  JDBC_setProp(prop: 'databaseName' : 'database');
  JDBC_setProp(prop: 'loggerLevel'  : 'TRACE'     );
  JDBC_setProp(prop: 'loggerFile'   : '/home/PostgreSQL/log');

  pgconn = JDBC_ConnProp('org.postgresql.Driver'
                     :'jdbc:postgresql://[some_IP]:5432/database'
                     : prop );
  JDBC_freeProp(prop);
  return pgconn;
end-proc;


dcl-proc doPGWriteMyTable export;
  dcl-pi doPGWriteMyTable like(success);
    i#schm char(10);
    i#rec char(334);
  end-pi;

  dcl-ds record extname('MYTABLE') end-ds;
  dcl-s prepStmtTxt varchar(10000);

  record = i#rec;

  pgconn = doPostgreConn;
  if pgconn = *NULL;
    //Custom Error Handling
  endif;

  prepStmtTxt = 'INSERT INTO ' + %trim(i#schm) + '.MYTABLE ' +
    '  VALUES (?, ?, ?) ';

  if PGWriteMYTABLEPrep = *NULL;

    PGWriteMYTABLEPrep = JDBC_PrepStmt(pgconn:prepStmtTxt);

    if PGWriteMYTABLEPrep = *NULL;
        
    endif;
  endif;

  JDBC_setString (PGWriteMYTABLEPrep: 1: StrCol);
  JDBC_setDecimal (PGWriteMYTABLEPrep: 2: DecCol);
  JDBC_setDate (PGWriteMYTABLEPrep: 75: DateCol);

  if JDBC_execPrepUpd( PGWriteMYTABLEPrep ) < 0;
    //Custom Error Handling
  endif;

  JDBC_Close(pgconn);

  return *on;
end-proc;


dcl-proc doPGDeleteMYTABLE export;
  dcl-pi doPGDeleteMYTABLE like(success);
    i#schm char(10);
    i#rec char(334);
  end-pi;

  dcl-ds record extname('MYTABLE') end-ds;
  dcl-s sqlstmt varchar(32000);
  dcl-s deleteSuccess ind;

  record = i#rec;

  sqlstmt = 'DELETE FROM ' + %trim(i#schm) + '.MYTABLE WHERE '; //Basically the key

  pgconn = doPostgreConn;
  if JDBC_ExecUpd(pgconn:sqlstmt) < 0;
    //Custom error handling
  endif;

  DoPostgreClose(pgconn);

  return *on;
end-proc;

The data queue read program essentially calls DoPGDeleteMYTABLE and then DoPGWriteMYTABLE, in that order (There is no unique key, so we simply delete all of the matching records on the PostgreSQL server and then re-add them).

The problem is, while the data queue read program is running, the first loop works perfectly fine, and then fails. The order goes like this:

  1. Record updated
  2. Delete any existing PG records: successful
  3. Prepare the write statement: successful
  4. Write any existing DB2 records to PG: successful
  5. Record updated
  6. Delete any existing PG records: successful
  7. Prepare the statement: successful
  8. Write any existing DB2 records to PG: unsuccessful
  9. repeat 5 through 8 until data queue job is restarted

The errors I receive are not very helpful. The job log on the AS400 simply tells me

org.postgresql.util.PSQLException: This connection has been closed.

even though I can see the open connection on the PostgreSQL server, and closing it from RPGLE does still work.

The JDBC job log does not tell me any information around the time the write happens. It just says that the prepare was successful, and then nothing.

Version information:

IBM OS 7.4

PostgreSQL 13.7 on x86_64-redhat-linux-gnu, compiled by gcc (GCC) 11.2.1 20220127 (Red Hat 11.2.1-9), 64-bit

PostgreSQL JDBC Driver postgresql-42.2.19

RPGLE is utilizing Scott Klement’s JDBCR4

Nothing I have found online has yet to help with the issue. If there is anything else I can provide or try in order to get more information, please let me know.

2

Answers


  1. Chosen as BEST ANSWER

    I finally got it figured out. It was a dumb thing that I didn't realize I needed to do - turns out you have to free the prepared statement after using it the first time.

    Using JDBCR4, you just call (using my example)

    JDBC_FreePrepStmt(PGWriteMYTABLEPrep);
    

    Which looks like this, if anybody needs info that doesn't use JDBCR4:

          *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
          * JDBC_FreePrepStmt(): Free prepared statement
          *
          *    prep = (input) Prepared Statement to Free
          *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
         P JDBC_FreePrepStmt...
         P                 B                   export
         D JDBC_FreePrepStmt...
         D                 PI
         D   prep                              like(PreparedStatement)
          /free
             stmt_close(prep);
             DeleteLocalRef(JNIENV_P: prep);
             prep = *NULL;
          /end-free
         P                 E
    

    In the end, a very poorly worded error, with a very simple solution.


  2. I don’t see anything that jumps out in the code you’ve posted, but given that it works the first time an fails the second, I’d guess something is reset (or not reset) between loops.

    Personally, I’d recommend opening the connection once outside the DELETE/WRITE procs; but I don’t think it’s a fix.

    The "connection closed" is interesting…might be worthwhile to run a comm trace to see if in fact the connection is being closed and if so from what side.

    Note, while I love RPG, I’m not a fan of calling Java from RPG. I did some benchmarking long, long ago and it was much faster to have a small java app handle JDBC rather than using it from RPG.

    You might also consider an Open Source alternative to calling Java directly from RPG.

    AppServer4RPG
    Application Server to make Java Components available for IBM i RPG programs, runs on IBM i or any other Java platform. Packaged with ArdGate to access any JDBC database using all native SQL interfaces from IBM i.

    ArdGate basically registers itself as a DRDA Application Requester Driver (ARD) and allow you to talk to any JDBC database like you would any other remote DRDA (aka Db2) database.

    Which means, you could read/write to PostgreSQL from the green screen STRSQL.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search