Informix

Informix

Connect with Db2, Informix, Netezza, open source, and other data experts to gain value from your data, share insights, and solve problems.

 View Only
Expand all | Collapse all

ancient Informix product - Perform

  • 1.  ancient Informix product - Perform

    Posted Thu March 12, 2026 06:29 PM
    This one is for the Informix dinosaurs who remember Ace and Perform.  
    The manual documents the pf_getval() as:
    pf_getval(tagname, retvalue, valtype, vallen)
            char* tagname, *retvalue;
            short valtype, vallen;
     
    and it lists the possible return values as:   
     
            0  The operation was successful; display field was found.
    3700  The user is not permitted to read the field.       
    3759  There is no such field tag in the form.            
        
    First question - is the return value an int?  Or does pf_getval() update sqlca.sqlcode (which is an int)?  Or does it do something else?  I have run finderr for the three possible return values, and they come back with messages similar to, but not exactly matching, those listed above.  I can't find the function prototype documented anywhere, and I can't find the header file for it, so I'm not sure whether it uses an int for the return.  I think I remember that K&R C defaulted all functions to an int return, but I've only actually written ANSI-style C, so I could have that wrong.  But if it updates sqlca.sqlcode, then I need to know so the developers can check the correct thing.
     
    Second question - does pf_getval() support the use of indicator variables like ESQL/C does for normal host variables?  If 'retvalue' in the function definition about is a host variable, can we associate an indicator variable with it when calling pf_getval()?  I strongly doubt it, given that 'retvalue' above actually expects a pointer, but I want confirmation so I can pass that info to the rest of our developers.
     
    Thanks.


    ------------------------------
    mark collins
    ------------------------------


  • 2.  RE: ancient Informix product - Perform

    Posted Thu March 12, 2026 10:23 PM

    Hi,

    Use nm to search library for the symbol then use readelf -wi like this:

    https://unix.stackexchange.com/questions/104468/finding-function-parameters-for-functions-in-shared-object-libraries

    Regards,
    David.



    ------------------------------
    David Williams
    Senior Database Platform Engineer
    Flutter
    London
    ------------------------------



  • 3.  RE: ancient Informix product - Perform

    Posted Thu March 12, 2026 10:46 PM
    Download aubit4gl most of the higher level functions map across, and will source code

    Cheers
    Paul

    Paul Watson
    Oninit LLC
    +1-913-387-7529
    www.oninit.com
    Oninit®️ is a registered trademark of Oninit LLC





  • 4.  RE: ancient Informix product - Perform

    Posted Fri March 13, 2026 10:28 AM

    Thanks Paul.  I'll add that to the list of products to evaluate when we start evaluating how to update this application for the future.  Right now, we're just trying to migrate it from HP to Red Hat.



    ------------------------------
    mark collins
    ------------------------------



  • 5.  RE: ancient Informix product - Perform

    Posted Fri March 13, 2026 10:29 AM

    Hi David,

    Thanks for that link.  I'll keep that in mind for future similar issues.



    ------------------------------
    mark collins
    ------------------------------



  • 6.  RE: ancient Informix product - Perform

    Posted Fri March 13, 2026 06:20 AM

    Hello Mark,

    If you download/try Aubit4gl, you also want to try FOURJ's Genero BDL. 

    Quick search to help you (needs more investigation):

    Seems pf_getval() is implemented in e33lfunc.o provided in lib/tools/libsperf.a

    After ar -x, doing some disassembling with:

    objdump -d -M intel e33lfunc.o

    Seems that the function return type is 32-bit integer (EAX op), typically used as status for C functions.

    Maybe we could help more if you explain in which context this function is called and in which conditions you get an error.

    Since "perform" is an old Informix product, you should also check 64-bit vs 32-bit integer concerns.

    Seb



    ------------------------------
    Sebastien FLAESCH
    ------------------------------



  • 7.  RE: ancient Informix product - Perform

    Posted Fri March 13, 2026 10:24 AM
    Edited by mark collins Fri March 13, 2026 10:26 AM

    Hi Sebastien,

    Right now, we're focused on shifting this legacy application from HP to Red Hat, so migrating to Aubit4gl or Genero are way out of scope.  

    Thanks for confirming that pf_getval() simply returns an integer.  I suspected as much, but could not prove it based on the documentation.  I was about to try constructing a test application to prove it, but I like your method of disassembling.

    The context is that as part of migrating to RH, we're looking at the existing code and seeing some truly atrocious coding.  One example is:

    $dec_t   acct;
    $char    prog[4], line[8];
    $short   progind, lineind, acctind;
    
    sqlca.sqlcode = 0;
    pf_getval("a0",prog,CCHARTYPE,4);
    pf_getval("f301",line,CCHARTYPE,8);
    
    if (sqlca.sqlcode != 0) {
        flag=1;
        pf_msg("problem in part 1 %d",sqlca.sqlcode,0,0);
    }
    
    if (progind < 0) {
        pf_msg("problem in prog  %d",sqlca.sqlcode,0,0);
    }
    
    if (lineind < 0) {
        pf_msg("problem in line %d",sqlca.sqlcode,0,0);
    }
    
    $select acctnum
     into $acct:acctind
     from  accounts
     where   accounts.program = $prog
       and accounts.line_item = $line;
    

    There are so many flaws in this code, such as not checking the status of the first call to pf_getval() and improper number of arguments sent to pf_msg().  But since the original code was checking sqlca.sqlcode after the second call to pf_getval(), the question was raised as to whether the function used sqlca.sqlcode as its method of reporting errors.  Based on your findings, the code should do 'rc = pf_getval(...)' and check the value of rc (assuming that rc is defined as an int).

    The question about indicator variables came because the code defines indicators and appears to be testing them in the second and third 'if' statements, but there was never anything to initialize these variables, and they were not referenced with the host variables.  As you can see in the SELECT statement, the original programmer understood how to use indicators in that context, but why they thought the other two indicators had been set is a bit of a mystery.  I was just trying to confirm whether pf_getval() supported that host_var:indicator_var syntax.  I strongly doubt it, since it expects a pointer rather than an actual host variable as its argument.



    ------------------------------
    mark collins
    ------------------------------



  • 8.  RE: ancient Informix product - Perform

    Posted Fri March 13, 2026 11:16 AM
    Edited by Sebastien FLAESCH Fri March 13, 2026 12:46 PM

    Mark,

    This piece of code looks to me like ESQL/C code...
    Are you allowed to give more context?
    What is the file extension of the source file?
    How is this code compiled?
    Are you sure this is used by the Informix perform tool?

    I bet that the pf_getval() function sets the global sqlca variable.
    So the return value for such functions if probably irrelevant, expecting the caller to check sqlca.sqlcode. 
    Then, if this pf_getval() function ONLY sets sqlca.sqlcode to <0 in case of error, and leaves it unchanged (0) if no error occurs, it can work. 

    About indicator variables (acctind), from what I see here it would be set by the row fetching of the SELECT statement.
    Just expecting some if( sqlca.sqlcode == 100 ) checking after the SELECT... but you probably did not give all the source code.

    For pf_msg(), I would bet that the remaining params (0,0) are normally used for additional debug information, for error messages with more that just a single %d placeholder.
    If it's using some ?printf() under the cover I see no issue here.

    So all in all, that code does not look so bad to me ...

    Seb



    ------------------------------
    Sebastien FLAESCH
    ------------------------------



  • 9.  RE: ancient Informix product - Perform

    Posted Fri March 13, 2026 11:28 AM
    Pf_getval is probably the same as mi_get_value and is the generic 'return value' function to get a column 

    Cheers
    Paul

    Paul Watson
    Oninit LLC
    +1-913-387-7529
    www.oninit.com
    Oninit®️ is a registered trademark of Oninit LLC





  • 10.  RE: ancient Informix product - Perform

    Posted Fri March 13, 2026 12:19 PM
    Edited by mark collins Fri March 13, 2026 12:23 PM

    Sebastien (and everyone else),

    Sorry, I thought I had put more context in the original message.  What happened was I had started typing out the original post with lots of detail, and then something happened that reset my connection and I lost that post.  When I created it again, I inadvertently left out some critical details as I was rushing to complete the post at the end of the day.  In my mind, I had typed these details, but as I said, they got lost when the connection reset.

    The actual Informix product is called Informix SQL.  It contains the Ace report definition tool, Perform screen generator, a menu creation tool, and an interactive query tool similar to dbaccess.  Ace and Perform have basic functionality for creating reports and screens, respectively.  But Informix provided the ability to extend the functionality by creating a C module, compiling that program, and linking it with the Informix-provided runner programs sacego (for Ace) and sperform (Perform) to create a customized version of the runner, with its own name.  The C module would contain your functions, and could be accessed via CALL statements in the 'instructions' section of the Perform source, such as:

    before editadd editupdate of salary
            begin
            { update }
            let f301 = dsal
            let f203 = f003
            let f000 = 0.0
            call get_acct()
            { end of update }
            end

    The get_acct() function is the module from which my previous C code excerpt was taken.  The declaration of that function is 'valueptr get_acct()'.  And yes, there is additional error checking after the SELECT:

    if (sqlca.sqlcode != 0) {
        pf_msg("invalid account, please add to cacct if you want posted %d"
                    ,sqlca.sqlcode,0,0);
    else
        if (acctind >= 0)
            pf_putval(&acct,CDECIMALTYPE,"f000");
    }
    

    The Perform screen opens up with a blank form (just the field labels).  Users can choose Q to query, which allows them to tab through these empty fields and enter whatever information they have and hit Enter.  Perform will then query the database and populate the screen with the first row that matches the information provided by the user.  The user can choose N or P to move to the next or previous matching result, or U to update the data on the screen, E to exit, and so on.

    In this case, the data comes from multiple tables which are specified in the 'tables' section of the Perform screen source.  Normally, the database is queried at the beginning and the returned data is displayed on the screen, the user updates it or exits.  But in this case, as they update information in certain screen fields (salary in this case), the CALL get_acct() will cause the function in the custom version of sperform to be executed.  The get_acct() function pulls data from two of the fields on the screen form (prog and line) which may have been updated from their original values and does a SELECT to get acct_num.  Assuming that SELECT was successful and the returned value is NOT NULL, it updates the field identified by tag "f000" with the acct_num that was just retrieved.  This is b

    In the Informix SQL Reference manual (hard to find online, I've saved a copy on my PC), there are five functions provided for interfacing with Perform forms:

    • pf_gettype() - returns the type and length of the display field for a given field tag;
    • pf_getval() - returns the value and length of the display field IF it is a character field;
    • pf_putval() - puts a value in the field identified by the field tag, requires that the user have correct permission;
    • pf_nxfield() - controls cursor placement on the Perform screen;
    • pf_msg() - display message at bottom of the Perform screen.

    The manual does not answer the questions that I posed in the original post.  And the functions are documented in K&R style rather than ANSI C style, so I was guessing that if there was a return, it would be an integer in the way that functions normally return values in C.  

    The manual does indicate "return values" for each of these functions.  It does not explicitly state whether those return values are done in the normal C manner or via sqlcode.  The values that are listed for pf_getval() return codes are:

          0 The operation was successful; display field was found.
    3700 The user is not permitted to read the field.
    3759 There is no such field tag in the form.

    which are close to what finderr returns for those numbers as sqlcode values:

    -3700   Permission not granted to allow reading of table-name.
    -3759   The display field field-name does not exist in the form.

    The problem with pf_msg() is that it takes three arguments:

    pf_msg(msgstr, reverseflag, bellflag)
        char* msgstr;
        short reverseflag, bellflag;

    The reverse flag causes the message to appear in inverse text (e.g., black text on white background), and the bell flag causes the terminal bell to ring. 

    In this case, the programmer attempted to combine the functionality of sprintf() and pf_msg(), passing a message format string with '%d' to display a numeric value, a value (sqlcode), the inverse flag, and the bell flag. 

    pf_msg("problem in prog  %d",sqlca.sqlcode,0,0);

    It runs as-is on both HP and RH, but it is not correct.

    To compile the C program and create a customized version of sacego or sperform, you use the cace or cperf scripts that come with Informix SQL.

    From his comments, I know Sebastien is familiar with Perform, but I included the detailed info above for those who have never heard of this tool.  It dates back to at least Informix 5.x (back in the 1990s), and probably was around even earlier than that.



    ------------------------------
    mark collins
    ------------------------------



  • 11.  RE: ancient Informix product - Perform

    Posted Tue March 17, 2026 09:06 AM
    if you are basically reports then Aubit should be ideal, it just converts your ace reports to 4gl - never had any issues at all 

    On 3/13/2026 11:19 AM, mark collins via IBM Community wrote:
    0100019ce7fe91b8-8f30d398-729c-4b81-a8d6-b58e4768dae5-000000@email.amazonses.com">
    Sebastien (and everyone else), Sorry, I thought I had put more context in the original message. What happened was I had started typing out... -posted to the "Informix" group





  • 12.  RE: ancient Informix product - Perform

    Posted Fri March 13, 2026 11:34 AM
    A couple of years ago I migrated a HP app layer in 4gl to Linux aubit4gl.  Probably the easier app layer migration I have ever done.

    Move the code to Linux and run a4gl instead of c4gl.  Had to duplicate a couple of the pop/push functions to the ifx_pop/ifx_push but that was it

    Disgustingly painless, done in a few hours. 

    Cheers
    Paul

    Paul Watson
    Oninit LLC
    +1-913-387-7529
    www.oninit.com
    Oninit®️ is a registered trademark of Oninit LLC





  • 13.  RE: ancient Informix product - Perform

    Posted 26 days ago
    Edited by Jean-Guy Charron 26 days ago

    Hi Mark,

    I'm one of that dinosaurs ;) 

    Sorry for the delay, I was on vacations.

    I made a lot of work to add functionnality to Perfom and Ace through the years.

    I share some code to the group related to pf_getval and pf_putval.

    #define TO_C            1
    #define TO_SQL          2
    /*****************/
    conv_type ( t, to )
    /*****************/
    /*****************************************************************/
    /* Conversion entre les types SQL et C.                          */
    /*****************************************************************/
    int  t, to;
    {
       short type;
     
       if ( to == TO_C )
          switch ( t )
          {  case SQLCHAR:   type = CCHARTYPE;
                             break;
             case SQLSMINT:  type = CSHORTTYPE;
                             break;
             case SQLINT:
             case SQLSERIAL:
                             type = CLONGTYPE;
                             break;
             case SQLDATE:
                             type = CDATETYPE;
                             break;
             case SQLFLOAT:  type = CDOUBLETYPE;

                             break;
             case SQLSMFLOAT:type = CFLOATTYPE;
                             break;
             case SQLMONEY:  type = CMONEYTYPE;
                             break;
             case SQLDECIMAL:type = CDECIMALTYPE;
                             break;
             default:        type = CCHARTYPE;
                             break;
          }
       else
          switch ( t )
          {  case CCHARTYPE:
             case CFIXCHARTYPE:
             case CSTRINGTYPE:
                             type = SQLCHAR;
                             break;
             case CINTTYPE:
             case CLONGTYPE: type = SQLINT;
                             break;
             case CDATETYPE: type = SQLDATE;
                             break;
             case CSHORTTYPE:type = SQLSMINT;
                             break;

             case CFLOATTYPE:type = SQLSMFLOAT;
                             break;
             case CDOUBLETYPE:
                             type = SQLFLOAT;
                             break;
             case CDECIMALTYPE:
                             type = SQLDECIMAL;
                             break;
             default:        type = SQLCHAR;
                             break;
          }
       return ( type );
    }/*conv_type*/

    /***************************/
    valueptr sys_pf_getval( tag )
    /***************************/
    /*******************************************************************/
    /* Retourne la valeur d'un tag.                                    */
    /*******************************************************************/
    valueptr tag;
    {
       short typ, len, cc;
       char  ctag[50], cval[81], buf[80];
     
       bycopy(tag->v_charp, ctag, tag->v_len);
       ctag[tag->v_len] = EOS;
     
       if ( (cc = pf_gettype ( ctag, &typ, &len )) != 0 )
       {  sprintf ( buf, "SYS_PF_GETVAL: pf_gettype tag %s inexistant cc = %d.",
                    ctag, cc );
          pf_msg ( buf, 1, 1 );
          intreturn ( ERREUR );
       }
     
       typ = conv_type ( typ, TO_C );
       if ( typ == CCHARTYPE )
          len++;
       if ( (cc = pf_getval ( ctag, cval, typ, len )) != 0 )
       {  sprintf ( buf, "SYS_PF_GETVAL: tag %s inexistant cc = %d.", ctag, cc );
          pf_msg ( buf, 1, 1 );
          intreturn ( ERREUR );
       }
     
       switch ( typ )
       {
          case CFIXCHARTYPE:
          case CSTRINGTYPE:
          case CCHARTYPE:   strreturn ( cval, len );
     
          case CSHORTTYPE:  intreturn ( *((short *) cval) );
     
          case CINTTYPE:
          case CLONGTYPE:   lngreturn ( *((long *) cval) );
     
          case CFLOATTYPE:  floreturn ( *((float *) cval) );
     
          case CDOUBLETYPE: dubreturn ( *((double *) cval) );
     
          case CDECIMALTYPE:decreturn ( *((dec_t *) cval) );
     
          default:          intreturn ( -1l );

       }
    }

    /********************************/
    valueptr sys_pf_putval( tag, val )
    /********************************/
    /*******************************************************************/
    /* Met une valeur dans un tag.                                     */
    /*******************************************************************/
    valueptr tag, val;
    {
       short typ, len, cr;
       char  ctag[50], buf[81];
     
       bycopy(tag->v_charp, ctag, tag->v_len);
       ctag[tag->v_len] = EOS;
     
       switch ( val->v_type )
       {
          case SQLCHAR:      cr = pf_putval ( val->v_charp, CCHARTYPE, ctag );
                             break;
     
          case SQLSMINT:     cr = pf_putval ( &val->v_int, CSHORTTYPE, ctag );
                             break;
     
          case SQLSERIAL:
          case SQLDATE:
          case SQLINT:       cr = pf_putval ( &val->v_long, CLONGTYPE, ctag );
                             break;
     
          case SQLSMFLOAT:   cr = pf_putval ( &val->v_float, CFLOATTYPE, ctag );
                             break;
     
          case SQLFLOAT:     cr = pf_putval ( &val->v_double, CDOUBLETYPE, ctag );
                             break;
     
          case SQLMONEY:
          case SQLDECIMAL:   cr = pf_putval ( &val->v_decimal, CDECIMALTYPE, ctag);
                             break;
     
          default:           sprintf ( buf,
                                       "SYS_PF_PUTVAL: type tag %s inexistant.",
                                       ctag );
                             pf_msg ( buf, 1, 1 );
                             intreturn ( ERREUR );
                             break;
       }
     
       if ( cr != 0 )
       {  sprintf ( buf, "SYS_PF_PUTVAL: tag %s inexistant cc = %d.", ctag, cr );

          pf_msg ( buf, 1, 1 );
          intreturn ( ERREUR );
       }
       intreturn ( SUCCES );
    }

    For NULL you can use the v_ind field of the value structure.


    I hope that this will help you !

    Best Regards,



    ------------------------------
    Jean-Guy Charron
    Logiciels Sys-Thèmes Inc
    Terrebonne, Québec, Canada

    ------------------------------



  • 14.  RE: ancient Informix product - Perform

    Posted 26 days ago
    Edited by mark collins 26 days ago

    Hello Jean-Guy,

    That does help.  From your example, it seems that pf_getval(), pf_putval(), and pf_gettype() return short rather than int.  

    And I'm one of those dinosuars as well, having first used Perform and Ace around 1996.



    ------------------------------
    mark collins
    ------------------------------



  • 15.  RE: ancient Informix product - Perform

    Posted 25 days ago

    Hi Mark,

    And for me I started using it in Informix 3.3 (pre-SQL) in 1988 and Informix 2.10 (SQL) in 1990.

    A long time ago...

    I  developped "standard" function to execute sql statement from inside a form or an ace.
    Selecting from a list of record (ex: customer code, product code,...)

    So if you have question on perform or ace just ask.

    Best Regards



    ------------------------------
    Jean-Guy Charron
    ------------------------------