Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write SQLite callback in COBOL

I'm a COBOL programmer , and my latest project is to connect a COBOL application to a SQLite3 database.

I have been following this guide, and their solution is exactly what I would need in my COBOL application. I have successfully managed to create, connect, insert data and close the database, but the problem arises when I try to select data from the database.

In the tutorial, they use a callback with double pointers.

static int callback(void *NotUsed, int argc, char **argv, char **azColName){
   int i;
   for(i=0; i<argc; i++){
      printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
   }
   printf("\n");
   return 0;
}

My solution in COBOL is the following

   WORKING-STORAGE SECTION.
  *----------------------------------------------------------------*

   01 sqlite3-db         pointer.
   01 err_msg            pointer.
   01 sqlite             pointer.
   01 res                pointer.

   01 notused            pointer.
   01 argc               pic 99 comp-5.
   01 argv               pointer.
   01 azColName          pointer.
   01 Writefunction-Ptr  procedure-pointer.

   procedure division.
          set Writefunction-Ptr to entry "sqlite-callback".

          *>Random code.

          call "sqlite3_exec" using
             by value sqlite3-db
             by reference sqlQuery
             by value Writefunction-Ptr
             by value 0
             by reference err_msg
           returning rc
          end-call

          *>Random code.

   stop run. 

   entry "sqlite-callback" using
           by value notused
           by value argc
           by reference argv
           by reference azColName.

       display argc

       goback.
   Entry-Termination.

The callback works because it is called the number of rows that is returned from the database, and the integer argc contains the number of columns that the table contains.

The questions is:

Double pointers in COBOL, how are they represented? In my solution I declare a pointer and call the callback with "by reference" on the pointer. Don't know if this is the correct way to represent double pointers in COBOL?

How do I display the content of the azColName and argv, and not just the memory address which the pointer points to?

I hove now tried to use SET ADDRESS OF, but I still don´t get it to work. Must be something i´ve missed. My solution at the moment looks like:

WORKING-STORAGE SECTION.
01 argv pointer.

Linkage Section.
01 link-area   pic x.

procedure division using link-area.

 *> RANDOM CODE

     set address of link-area to argv
     call "sqlite3_exec" using
        by value sqlite3-db
        by reference z"SELECT * FROM Cars"
        by value Writefunction-Ptr
        by value 0
        by reference err_msg
        returning rc
      end-call

 *> RANDOM CODE


entry "sqlite-callback" using
  by value notused
  by value argc
  by reference argv
  by reference azColName.

  display argc.

  if address of link-area not = null
     display "Contents of new argv: " link-area
  else
     display "empty"
  end-if

  goback.

Entry-Termination.

The result I get is that the if statement is always false, so the string "empty" is displayed. But still argc is set to the number of colums in the table.

The working solution:

   WORKING-STORAGE SECTION.

   01 argv.
       03  firstColumn   pointer.
       03  secondColumn  pointer.
       03  thirdColumn   pointer.

   01 azColName          pointer.
   01 argc               pic 99 comp-5.
   01 notused            pointer.

   01 Writefunction-Ptr  procedure-pointer.

  *-----------------------------------------------------------------
   Linkage Section.

   01 Cars_Id       pic 9(2).
   01 Cars_Name     pic X(20).
   01 Cars_Price    pic 9(10).
  /-----------------------------------------------------------------
   procedure division.

    //code      

    set Writefunction-Ptr to entry "sqlite-callback".

          initialize sqlQuery
          move z"SELECT * FROM Cars;" to sqlQuery


          call "sqlite3_exec" using
             by value sqlite3-db
             by reference sqlQuery
             by value Writefunction-Ptr
             by value 0
             by reference err_msg
           returning rc
          end-call

    //code

    stop run.



   entry "sqlite-callback" using
           by value notused
           by value argc
           by reference argv
           by reference azColName.

       set address of Cars_Id to firstColumn
       set address of Cars_Name to secondColumn
       set address of Cars_Price to thirdColumn


       display Cars_Id "|" Cars_Name "|" Cars_Price

       goback.
   Entry-Termination.
like image 863
Marcus Sundberg Avatar asked Dec 18 '15 13:12

Marcus Sundberg


1 Answers

We really need to know the compiler you are using.

Your SET statement is in the wrong place. argv only has an address when the ENTRY is called. Before the entry is called, it will be binary zero, or some unpredictable rubbish.

If you move the SET to after the ENTRY, you should then be able to use the value of LINK-AREA. argv will still only be an address, but LINK-AREA (give it a better name, please) will be pointing to that address, so define it as argv should be defined, and then LINK-AREA can be used to get the actual content of argv.


When using BY REFERENCE the compiler generates code to pass a pointer with the address of the data-item.

On the PROCEDURE DIVISION USING or ENTRY USING the item should also be BY REFERENCE and the compiler generates code to map your LINKAGE SECTION definition to the address passed.

This is similar if you use BY CONTENT, but the compiler takes a copy of the data within your program and passes a pointer which references that instead. On the USING of the PROCEDURE DIVISION or ENTRY that is still defined as BY REFERENCE.

With BY VALUE the compiler "passes" the actual value, although there are limits, either those specified in the Standard or extensions to the Standard with your actual compiler. The USING of the PROCEDURE DIVISION or ENTRY should specify BY VALUE as well.

On all the USINGs, BY REFERENCE/CONTENT/VALUE are propagated, you don't need to specify BY at all if all are the same and you want BY REFERENCE, it is the default.

If you "pass" a POINTER (USAGE POINTER) then you can access the data pointed to by using SET ADDRESS OF. The SET ADDRESS OF item must be in the LINKAGE SECTION.

You can also use an implicit pointer with SET ADDRESS OF. SET ADDRESS OF a TO ADDRESS OF b will change the address mapped in the LINKAGE SECTION to the address of b, an item that is defined somewhere in your program's DATA DIVISION (any SECTION other than the FILE SECTION).

If you want to see the data pointed-to by a POINTER, define an item which has the correct size and type and use SET ADDRESS OF item TO pointer-name.

The POINTER can of course be pointing to a simple data-item or a group data-item (a structure).

All items in the LINKAGE SECTION must have an address before they are referenced. Those on a USING are given addressability by the compiler (and the compiler assumes that the correct number of addresses/items are passed). All other LINKAGE SECTION items must have addressability established by SET ADDRESS OF or by passing ADDRESS OF in the USING of a CALL and having the CALLed program set the address.

like image 148
Bill Woodger Avatar answered Oct 03 '22 05:10

Bill Woodger