2013-03-28 1 views
3

Использование 64-разрядного драйвера Informix с unixODBC (с параметром 2.2.14, который поставляется вместе с Redhat, а также с 2.3.1) и запуском в проблему , когда столбец имеет значение NULL. unixODBC использует SQLLEN для (последнего) аргумента StrLen_or_Ind, в то время как для 64-разрядного драйвера Informix используется SQLINTEGER.Informix с 64-битным unixODBC segfault

Так при тестировании, если возвращаемое значение SQL_NULL_DATA тест не работает .. так как значение -1 (которое SQL_NULL_DATA определяется как 32), как битовое целое число является 4294967296, когда рассматривается как 64 битное целое число.

Я нашел эту проблему при использовании драйвера с PHP ODBC, поскольку он вызывал segfault. Кто-нибудь знает об обходном пути? Я думал о том, что пользовательский скомпилированный unixODBC и PHP работает с драйвером ODBC Informix, но тогда он не будет работать с другими драйверами 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; 
} 

ответ

3

Это известная проблема в CSDK 3,70 и более ранних версиях драйвера Informix ODBC.

Вчера (2013-03-26), IBM выпустила IBM Informix 12.10.xC1 и компаньона IBM Informix ClientSDK 4.10.xC1. В версии ODBC там должны быть правильные 64-битные типы для SQLLEN и SQLULEN.

Это означает, что если вы получаете обновление, вы должны быть в порядке. Это означает, что любой код необходимо перекомпилировать с новой версией ODBC. Это также означает, что некоторые (коммерческие) драйверы драйверов ODBC, которые работают вокруг странности (ошибка) в Informix 3.70 и более ранних версиях, должны быть перестроены или настроены на использование стандартного интерфейса с 64-разрядным драйвером вместо багги-интерфейса Informix при работе с новые драйверы 4.10.

+0

wow .. большое спасибо! – grom

+0

Теперь я получаю segfaults с NULL для целочисленных полей. Есть идеи? – grom

+0

Кратко, нет. Можете ли вы создать минимальное воспроизведение? –

 Смежные вопросы

  • Нет связанных вопросов^_^