3

Informix 64 ビット ドライバを unixODBC (Redhat に付属するデフォルトの 2.2.14 と 2.3.1 の両方) で使用すると、列に NULL 値がある場合に問題が発生します。unixODBC は (最後の) StrLen_or_Ind 引数に SQLLEN を使用していますが、Informix 64 ビット ドライバは SQLINTEGER を使用しているようです。

したがって、戻り値が SQL_NULL_DATA であるかどうかをテストする場合、テストは機能しません.32 ビット整数としての値 -1 (SQL_NULL_DATA が定義されている) は、64 ビット整数として扱われると 4294967296 になるためです。

PHP ODBC でドライバーを使用すると、segfault が発生するため、この問題が見つかりました。誰かが回避策を知っていますか? Informix ODBC ドライバーで動作するカスタム コンパイルされた unixODBC と PHP を使用することを考えましたが、他の ODBC ドライバーでは正しく動作しません :(

以下は、これをテストするために私が書いた簡単な C プログラムです。

#include <stdio.h>
#include <stdlib.h>
#include <sql.h>
#include <sqlext.h>

typedef struct odbc_connection {
    SQLHENV henv;
    SQLHDBC hdbc;
} odbc_connection;

typedef struct odbc_result_value {
    char name[32];
    char *value;
    SQLLEN vallen;
    SQLLEN coltype;
} odbc_result_value;

typedef struct odbc_result {
    SQLHSTMT stmt;
    odbc_result_value *values;
    SQLSMALLINT numcols;
    odbc_connection *conn;
} odbc_result;

int print_error (SQLHENV    henv,
                 SQLHDBC    hdbc,
                 SQLHSTMT   hstmt)
{
    SQLCHAR     buffer[SQL_MAX_MESSAGE_LENGTH + 1];
    SQLCHAR     sqlstate[SQL_SQLSTATE_SIZE + 1];
    SQLINTEGER  sqlcode;
    SQLSMALLINT length;


    while ( SQLError(henv, hdbc, hstmt, sqlstate, &sqlcode, buffer,
                     SQL_MAX_MESSAGE_LENGTH + 1, &length) == SQL_SUCCESS )
    {
        printf("\n **** ERROR *****\n");
        printf("         SQLSTATE: %s\n", sqlstate);
        printf("Native Error Code: %ld\n", sqlcode);
        printf("%s \n", buffer);
    };

    return ( SQL_ERROR);
}

int terminate(SQLHENV henv,
              SQLHDBC hdbc)
{
    SQLRETURN   rc;

    rc = SQLDisconnect (hdbc);               /* disconnect from database  */
    if (rc != SQL_SUCCESS )
        print_error (henv, hdbc, SQL_NULL_HSTMT);
    rc = SQLFreeConnect (hdbc);              /* free connection handle    */
    if (rc != SQL_SUCCESS )
        print_error (henv, hdbc, SQL_NULL_HSTMT);
    rc = SQLFreeEnv (henv);                  /* free environment handle   */
    if (rc != SQL_SUCCESS )
        print_error (henv, hdbc, SQL_NULL_HSTMT);

    return(rc);
}

int check_error (SQLHENV    henv,
                 SQLHDBC    hdbc,
                 SQLHSTMT   hstmt,
                 SQLRETURN  frc)
{
    SQLRETURN   rc;

    print_error(henv, hdbc, hstmt);

    switch (frc){
    case SQL_SUCCESS : break;
    case SQL_ERROR :
    case SQL_INVALID_HANDLE:
        printf("\n ** FATAL ERROR, Attempting to rollback transaction**\n");
        rc = SQLTransact(henv, hdbc, SQL_ROLLBACK);
        if (rc != SQL_SUCCESS)
            printf("Rollback Failed, Exiting application\n");
        else
            printf("Rollback Successful, Exiting application\n");
        terminate(henv, hdbc);
        exit(frc);
        break;
    case SQL_SUCCESS_WITH_INFO :
        printf("\n ** Warning Message, application continuing\n");
        break;
    case SQL_NO_DATA_FOUND :
        printf("\n ** No Data Found ** \n");
        break;
    default :
        printf("\n ** Invalid Return Code ** \n");
        printf(" ** Attempting to rollback transaction **\n");
        SQLTransact(henv, hdbc, SQL_ROLLBACK);
        terminate(henv, hdbc);
        exit(frc);
        break;
    }
    return(SQL_SUCCESS);

}

odbc_connection* odbc_connect(char *dsn, char *user, char* password)
{
    SQLRETURN rc;
    odbc_connection *conn;

    conn = (odbc_connection *) malloc(sizeof(odbc_connection));

    // Allocate environment handle
    rc = SQLAllocEnv(&conn->henv);
    if (rc != SQL_SUCCESS) {
        printf("Unable to allocate environment\n");
        check_error(conn->henv, conn->hdbc, SQL_NULL_HSTMT, rc);
    }

    // Allocate connection handle
    rc = SQLAllocConnect(conn->henv, &conn->hdbc);
    if (rc != SQL_SUCCESS) {
        printf("Unable to allocate connection handle\n");
        check_error(conn->henv, conn->hdbc, SQL_NULL_HSTMT, rc);
    }

    // Connect to database
rc = SQLConnect(conn->hdbc, dsn, SQL_NTS, user, SQL_NTS, password,
SQL_NTS);
    if (rc != SQL_SUCCESS) {
        printf("Unable to connect\n");
        check_error(conn->henv, conn->hdbc, SQL_NULL_HSTMT, rc);
    }

    return conn;
}

odbc_result* odbc_query(odbc_connection *conn, char *sql)
{
    SQLRETURN rc;
    odbc_result *result;
    int i;
    SQLSMALLINT colnamelen; /* Not used */
    SQLLEN displaysize;

    result = (odbc_result *) malloc(sizeof(odbc_result));
    result->conn = conn;

    rc = SQLAllocStmt(conn->hdbc, &(result->stmt));
    if (rc != SQL_SUCCESS) {
        printf("Unable to allocate statement\n");
        check_error(conn->henv, conn->hdbc, SQL_NULL_HSTMT, rc);
    }

    rc = SQLExecDirect(result->stmt, sql, SQL_NTS);
    if (rc != SQL_SUCCESS) {
        printf("Unable to execute statement\n");
        check_error(conn->henv, conn->hdbc, SQL_NULL_HSTMT, rc);
    }

    SQLNumResultCols(result->stmt, &(result->numcols));

    if (result->numcols > 0) {
        // Bind columns
        result->values = (odbc_result_value *) malloc(sizeof(odbc_result_value) * result->numcols);

        for (i = 0; i < result->numcols; i++) {
            rc = SQLColAttributes(result->stmt, (SQLUSMALLINT)(i+1),
                SQL_COLUMN_NAME, result->values[i].name,
                sizeof(result->values[i].name), &colnamelen, 0);
            rc = SQLColAttributes(result->stmt, (SQLUSMALLINT)(i+1), 
                SQL_COLUMN_TYPE, NULL, 0, NULL, &result->values[i].coltype);
            rc = SQLColAttributes(result->stmt, (SQLUSMALLINT)(i+1),
                SQL_COLUMN_DISPLAY_SIZE, NULL, 0, NULL, &displaysize);
            result->values[i].value = (char *) malloc(sizeof(char) * (displaysize + 1));
            rc = SQLBindCol(result->stmt, (SQLUSMALLINT)(i+1), SQL_C_CHAR,
                result->values[i].value, displaysize + 1, &result->values[i].vallen);
        }
    }

    return result;
}

int odbc_print_row(odbc_result *result) {
    SQLRETURN rc;
    int i;

    rc = SQLFetch(result->stmt);
    if (rc != SQL_SUCCESS) {
        return 0;
    }

    for (i = 0; i < result->numcols; i++) {
        /* BUG: the 64 bit informix driver here is has returned a 32 bit -1
        integer but is stored in a 64 integer field */
        if (result->values[i].vallen == SQL_NULL_DATA) {
            printf("NULL;");
        } else {
            printf("\"");
            printf("%s", result->values[i].value);
            printf("\";");
        }
    }
    printf("\n");
    return 1;
}

int main(int argc, char *argv[])
{
    odbc_connection* conn;
    odbc_result *result;

    conn = odbc_connect("authlive", "auth", "xxx");
    result = odbc_query(conn, argv[1]);

    while (odbc_print_row(result));

    SQLFreeStmt(result->stmt, SQL_CLOSE);
    free(result);
    terminate(conn->henv, conn->hdbc);
    free(conn);
    return 0;
}
4

1 に答える 1

3

これは、CSDK 3.70 以前のバージョンの Informix ODBC ドライバの既知の問題です。

昨日 (2013 年 3 月 26 日)、IBM は IBM Informix 12.10.xC1 とそれに対応する IBM Informix ClientSDK 4.10.xC1 をリリースしました。そこにある ODBC のバージョンには、SQLLEN と SQLULEN の正しい 64 ビット型が含まれている必要があります。

これは、アップグレードされた場合は問題ないことを意味します。これは、すべてのコードを新しいバージョンの ODBC で再コンパイルする必要があることを意味します。これはまた、Informix 3.70 以前の奇妙な (バグ) を回避する (商用の) ODBC ドライバ マネージャを再構築するか、バグのある Informix インターフェイスの代わりに標準の 64 ビット ドライバ インターフェイスを使用するように構成する必要があることを意味します。新しい 4.10 ドライバー。

于 2013-03-28T01:49:15.987 に答える