Использование 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;
}
wow .. большое спасибо! – grom
Теперь я получаю segfaults с NULL для целочисленных полей. Есть идеи? – grom
Кратко, нет. Можете ли вы создать минимальное воспроизведение? –