0

I am trying to connect from Progress OpenEdge to MySQL using libmysql.dll and mysql-functions.i which I found online.

It works connecting from Progress OpenEdge 101b to MySql, but when I try to connect from Progress OpenEdge 1102 to MySql it crashes Progress with the following Problem signature of APPCRASH and Exception code: c0000374.

I got the libmysql.dll from here: https://code.google.com/p/pvpgn-magic-builder/source/browse/trunk/module/include/mysql/4.1.21/libmySQL.dll?r=125

Here is the mysql-functions.i code:

define variable mysql-row    as character extent 1000.
define variable db_log_query as logical  no-undo init no.

function db_close returns integer (input mysql as memptr):

    run mysql_close(mysql).
    set-size(mysql) = 0.
    return 1.
end.

function db_connect returns integer (input-output mysql as memptr):

    define variable mysql_conn as memptr. 
    define variable mysql-host as character no-undo format "x(12)".
    define variable mysql-db   as character no-undo.
    define variable mysql-port as integer   no-undo.
    define variable mysql-user as character no-undo.
    define variable mysql-pass as character no-undo.

    set-size(mysql) = 952.
    set-size(mysql_conn) = 952. 

     assign mysql-host = "localhost"
            mysql-db   = ""
            mysql-port = 5002
            mysql-user = "root"
            mysql-pass = "".


    run mysql_init(0,output mysql).

    run mysql_real_connect(input-output mysql,mysql-host,mysql-user,
                      mysql-pass,mysql-db,mysql-port,"",0,output mysql_conn).
    set-size(mysql_conn) = 0.
    return 1.
end.

function db_last_error returns character (input mysql as memptr).
    define variable err-msg as character no-undo.
    define variable mysql_err as memptr no-undo.

    set-size(mysql_err) = 1000.
    run mysql_error(mysql,output mysql_err).
    err-msg = replace(get-string(mysql_err,1),"'","\\'").
    set-size(mysql_err) = 0.
    return err-msg.
end.                        


function db_fetch_into_array returns integer (input mysql_res as memptr):

    define variable num-fields as integer.
    define variable i          as integer.
    define variable mysql_row  as memptr.
    define variable mysql_element as memptr.
    define variable row_lengths   as memptr.
    define variable stat as integer.
    define variable field-lengths as integer no-undo extent 200.

    if get-size(mysql_res) = 0 then
        return -1.

    set-size(mysql_row) = 800.

    run mysql_num_fields(mysql_res,output num-fields).

    run mysql_fetch_row(mysql_res,output mysql_row).
    if get-pointer-value(mysql_row) = 0 then do:
        stat = -1.
        return stat.
    end.

    run mysql_fetch_lengths(mysql_res,output row_lengths).

    do i = 1 to num-fields:
        field-lengths[i] = get-long(row_lengths,i * 4 - 3).
    end.

    set-size(mysql_element) = field-lengths[i].

    do i = 1 to num-fields:
        set-pointer-value(mysql_element) = get-long(mysql_row, i * 4 - 3).
        if field-lengths[i] > 0 then
            mysql-row[i] = get-string(mysql_element,1,field-lengths[i]).
        else
            mysql-row[i] = "".
    end.
    set-size(mysql_element) = 0.
    set-size(mysql_row) = 0.
    set-size(row_lengths) = 0.

    return 1.
end.

function db_free_result returns integer (mysql_res as memptr):

    run mysql_free_result(mysql_res).
    set-size(mysql_res) = 0.
    return 1.
end.

function db_query returns integer(mysql as memptr,sql-query as character):

    define variable stat as integer.

    run mysql_query(mysql,sql-query,output stat).
    if sql-query begins "select" or db_log_query = no then
        return stat.

    define variable sql        as character no-undo.
    define variable log-sql    as character no-undo.
    define variable query-stat as integer   no-undo.
    define variable cur-table  as character no-undo.
    define variable err-msg    as character no-undo.
    define variable mysql_err  as memptr    no-undo.
    define variable mysql2     as memptr    no-undo.

    if sql-query begins "insert" or sql-query begins "delete" or
       sql-query begins "replace" then
        cur-table = entry(3,sql-query," ").
    else 
        cur-table = entry(2,sql-query," ").

    assign cur-table = entry(1,cur-table,"(")
           /* Now handled by mysql_real_escape_string 
           log-sql = replace(sql-query,"\\","") 
           log-sql   = replace(replace(log-sql,"'","\\'"),'"','\\"').
           */
           log-sql = sql-query.

    if stat <> 0 then do:
        set-size(mysql_err) = 1000.
        run mysql_error(mysql,output mysql_err).
        err-msg = replace(get-string(mysql_err,1),"'","\\'").
        set-size(mysql_err) = 0.
    end.

    sql = "SET SQL_LOG_BIN=0".
    db_connect(mysql2).
    run mysql_query(mysql2,sql,output query-stat).

    sql = "insert into utility.update_log" +
                "(user_id,sql,error,program_name,table_name) values('" +
                userid(ldbname(1)) + "','" +  /* will have to fix this */
                log-sql    + "','" +
                err-msg       + "','" +
                program-name(1)    + "','" +
                cur-table          + "')".

    run mysql_query(mysql2,sql,output query-stat).
    db_close(mysql2).
    return stat.
end.

function db_use_result returns memptr (input mysql as memptr):

    define variable mysql_res as memptr.
    set-size(mysql_res) = 84.

    run mysql_use_result(mysql,output mysql_res).

    return mysql_res.

end.

function db_escape_string return character (input mysql as memptr,
                                        input cFrom as character):

    define variable lcFrom as longchar  no-undo.
    define variable mFrom  as memptr    no-undo.
    define variable mTo    as memptr    no-undo.
    define variable iTo    as integer   no-undo.
    define variable cOut   as character no-undo.

    if get-size(mysql) = 0 then
        leave.

    assign lcFrom = cFrom.

    copy-lob from object lcFrom to mFrom no-error.

    set-size(mTo) = (get-size(mFrom) * 2) + 1 no-error.

    run mysql_real_escape_string(input  mysql,
                                 output mTo,
                                 input  mFrom,
                                 input  get-size(mFrom),
                                 output iTo) no-error.

    set-size(mTo)   = iTo no-error.
    assign cOut     = string(mTo).
    set-size(mFrom) = 0 no-error.
    set-size(mTo)   = 0 no-error.
    assign lcFrom   = "".

    return string(cOut).
end.

/********************** end public functions *******************/

procedure mysql-fetch-into-array:

    define variable num-fields as integer.
    define variable i          as integer.
    define variable mysql_row  as memptr.
    define variable mysql_element as memptr.
    define variable row_lengths as memptr.
    define variable field-lengths as integer no-undo extent 200.

    define input    parameter mysql_res as memptr.
    define output   parameter stat as integer.

    IF get-size(mysql_res) = 0 then do:
        assign stat = -1.
        return.
    end.

    set-size(mysql_row) = 800.
    set-size(row_lengths) = 1000.

    run mysql_num_fields(mysql_res,output num-fields).

    run mysql_fetch_row(mysql_res,output mysql_row).
    if get-pointer-value(mysql_row) = 0 then do:
        stat = -1.
        leave.
    end.

    run mysql_fetch_lengths(mysql_res,output row_lengths).

    do i = 1 to num-fields:
        field-lengths[i] = get-long(row_lengths,i * 4 - 3).
    end.

    do i = 1 to num-fields:
        /* Updates pointer size to include all the field */
        set-size(mysql_element) = field-lengths[i].
        set-pointer-value(mysql_element) = get-long(mysql_row, i * 4 - 3).
        mysql-row[i] = get-string(mysql_element,1,field-lengths[i]).
    end.
    set-size(mysql_element) = 0.
    set-size(mysql_row) = 0.
    set-size(row_lengths) = 0.
end.

procedure mysql-fetch-fields-into-array:

    /* note: this function needs to be fixed, see 
            mysql-fetch-into-array to fix */

    define variable num-fields as integer.
    define variable i          as integer.
    define variable mysql_field  as memptr.
    define variable mysql_element as memptr.
    define variable used-length as integer.
    define variable tmp-long as integer.

    define input    parameter mysql_res as memptr.
    define output   parameter stat as integer.

    if get-size(mysql_res) = 0 then do:
        assign stat = -1.
        return.
    end.

    set-size(mysql_field) = 100.

    run mysql_num_fields(mysql_res,output num-fields).

    set-size(mysql_element) = 2000.

    used-length = 0.
    do i = 1 to num-fields:
        run mysql_fetch_field(mysql_res,output mysql_field).
        tmp-long = get-long(mysql_field,1).
        set-pointer-value(mysql_element) = tmp-long.
        mysql-row[i] = get-string(mysql_element,1).
    end.
    set-size(mysql_element) = 0.
    set-size(mysql_field) = 0.
end.


procedure mysql_fetch_field external "libmysql.dll" persistent:
    define input  parameter mysql_res as memptr.
    define return parameter mysql_field as memptr.
end procedure.

procedure mysql_init external "libmysql.dll" persistent:

    define input parameter nothing as long.
    define return parameter mysql as memptr.

end.

procedure mysql_real_connect external "libmysql.dll" persistent: 

    define input-output parameter mysql       as memptr.
    define input parameter host        as character.
    define input parameter mysql-user  as character.
    define input parameter passwd      as character.
    define input parameter db          as character. 
    define input parameter port        as unsigned-short.
    define input parameter socket      as character.
    define input parameter client_flag as short.
    define return parameter stuff      as memptr.
end.

procedure mysql_error external "libmysql.dll" persistent:

    define input parameter mysql       as memptr.
    define return parameter my-err     as memptr.
end.

procedure mysql_close external "libmysql.dll" persistent:

    define input parameter mysql as memptr.

end procedure.

procedure mysql_query external "libmysql.dll" persistent:

    define input parameter mysql as memptr.
    define input parameter sql-query as character.
    define return parameter stat as short.

end procedure.

procedure mysql_use_result external "libmysql.dll" persistent:

    define input parameter mysql as memptr.
    define return parameter mysql_res as memptr.

end.

procedure mysql_fetch_row external "libmysql.dll" persistent:

    define input parameter mysql_res as memptr.
    define return parameter mysql_row as memptr.

end .

procedure mysql_num_fields external "libmysql.dll" persistent:

    define input parameter mysql_res as memptr.
    define return parameter num-rows as short.

end.

procedure mysql_fetch_lengths external "libmysql.dll" persistent:

    define input parameter mysql_res as memptr.
    define return parameter row_lengths as memptr.

end.

procedure mysql_free_result external "libmysql.dll" persistent:

    define input parameter mysql_res as memptr.

end.

procedure mysql_real_escape_string external "libmysql.dll" persistent:
    define input parameter mysql as memptr.
    define output parameter cTo as memptr.
    define input parameter cFrom as memptr.
    define input parameter iLength as long.
    define return parameter ierror as long.
end.
iceiceady
  • 21
  • 2

2 Answers2

0

That APPCRASH error seems related to improperly freeing a memptr:

http://knowledgebase.progress.com/articles/Article/000036217

http://knowledgebase.progress.com/articles/Article/000047105

There are also a lot of fixed size memptrs and other hard coded sizings in the code that you have shown. I.e.:

set-size(mysql_row) = 800.

...  extent 200.

Is it possible that some of those are too small for the returned data?

Tom Bascom
  • 13,405
  • 2
  • 27
  • 33
  • Is memory used differently in later versions of OpenEdge? I have found both these links before in the Knowledgebase, but wasn't sure it was the same as it mentions storing things in the clipboard, – iceiceady Apr 25 '15 at 14:21
  • 1
    There are sure to be differences in where it is allocated and things like that. The nature of pointer based bugs is very sensitive to everything else going on in memory so a big change like skipping ahead 5+ years (oe10.1 to oe11.2) is going to have an impact. Yes, one of these links is clipboard related but the core problem is freeing the memptr, not that it is using the clipboard. – Tom Bascom Apr 25 '15 at 16:43
  • To be honest I had suspected that it might be the memptr's that were causing the issue and ran the clipboard problem past more senior developers, but faced a wall of "It can't be Progress, it must be the dll". I might try get the db_connect function working and build up from that. Do you think I should use the suggested procedures in clipboard resolution on the Knowledgebase? – iceiceady Apr 25 '15 at 17:35
  • I would start by clearly identifying where the problem occurs. Especially if it is repeatable. You can turn on various client-log levels to trace MEMPTRs and follow the program flow to where it fails. That will probably tell you a lot about what is really happening. – Tom Bascom Apr 25 '15 at 18:23
  • I'm a bit further on. It would seem like the set-size function is not updating the memptr size as it goes through. If I remove the set-size functions it runs in 11.2, but obviously then I'm leaving the memory pointers in place and I can't make a second call to the same variable. This would make it look similar to the clipboard issue, but I'm not sure how to do this with my code. – iceiceady Apr 30 '15 at 10:45
0

As a lot of values are hard coded, as said Tom, it would be interesting to verify if the size of the objects returned by the dll is correct.

For example, in db_connect, "912" is or has been the size of the MYSQL structure (see mysql/mysql.h), returned into the mysql MEMPTR. (search for "typedef struct st_mysql" into mysql/mysql.h)

It is possible e.g. that the size of the structure is not exactly 912 bytes anymore, which did not cause 10.2 to crash but is not tolerated by 11.2 any more.