coracle-database64-bitlong-integerunixodbc

SQLBindCol for long integer value on 64Bit platform?


I want to query data from an oracle db with unixodbc which has an NUMBER(19) column. So this must fit in an long under 64 bit. But i dont know the right type for the TargetType parameter. With type SQL_C_LONG im getting only positive values right, that are smaller then INT_MAX (2147483647). Negative values causing an overflow.

With type SQL_C_SBIGINT i'm getting error

HY004:1:0:[Oracle][ODBC]Invalid SQL data type <-25>

So how can i query values into an long the right way?

Operating system: debian Debian 5.10 64bit

unixODBC: 2.3.6

oracle odbc driver version: 19.1

example code:

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

void extract_error( SQLHANDLE handle, SQLSMALLINT type )
{

    SQLCHAR  odbc_error_text[SQL_MAX_MESSAGE_LENGTH  + 1];

    SQLINTEGER      i = 0;
    SQLINTEGER      native = 0;
    SQLCHAR         state[ SQL_SQLSTATE_SIZE + 1 ];
    SQLSMALLINT     len = 0;
    SQLRETURN       ret;


    memset( &odbc_error_text, 0, sizeof(odbc_error_text) );

    while( (ret = SQLGetDiagRec( type, handle, ++i, state,
                    &native, odbc_error_text,
                    sizeof(odbc_error_text), &len )) == SQL_SUCCESS  )
    {
        fprintf( stderr, "%s:%d:%d:%s\n", state, i, native, odbc_error_text );
        
    }

    if( ret == SQL_INVALID_HANDLE )
    {
        fprintf( stderr, "Invalid Handle!\n" );
    }

    return;

}

int main(int argc, char **argv) 
{
    SQLHENV env;
    SQLHDBC dbc;
    SQLHSTMT stmt;
    
    SQLSMALLINT columns;

    SQLLEN indicator;
    
    SQLCHAR        ColumnName[512];
    SQLSMALLINT    ColumnNameLen;
    SQLSMALLINT    ColumnDataType;
    SQLULEN        ColumnDataSize;
    SQLSMALLINT    ColumnDataDigits;
    SQLSMALLINT    ColumnDataNullable;
    SQLCHAR *      ColumnData;
    SQLLEN         ColumnDataLen;
    
    int result_int;
    long result_long;
    

    SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
    SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
    
    SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);

    if( !SQL_SUCCEEDED(SQLDriverConnect(dbc,
                    NULL,
                    "DSN=oracle;uid=TEST;pwd=TEST;", SQL_NTS,
                    NULL, 0, 
                    NULL, SQL_DRIVER_COMPLETE)) )
    {
        fprintf(stderr, "Connection error!\n");
        extract_error( dbc, SQL_HANDLE_DBC );
        goto exit;
    }

    SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
    SQLPrepare( stmt, "SELECT mylong FROM testtab WHERE id = 3", SQL_NTS);
    
    SQLDescribeCol( stmt,                    // Select Statement (Prepared)
                    1,                     // Columnn Number
                    ColumnName,            // Column Name (returned)
                    512,                // size of Column Name buffer
                    &ColumnNameLen,        // Actual size of column name
                    &ColumnDataType,       // SQL Data type of column
                    &ColumnDataSize,       // Data size of column in table
                    &ColumnDataDigits,     // Number of decimal digits
                    &ColumnDataNullable);  // Whether column nullable

    printf("Column Name : %s\n  Column Name Len : %i\n  SQL Data Type : %i\n  Data Size : %i\n  DecimalDigits : %i\n  Nullable %i\n",
                 ColumnName, 
                 (int)ColumnNameLen, 
                 (int)ColumnDataType,
                 (int)ColumnDataSize,
                 (int)ColumnDataDigits,
                 (int)ColumnDataNullable);


    
    if(! SQL_SUCCEEDED(SQLBindCol(stmt, 1,
            SQL_C_LONG,
            &result_long,
            (SQLLEN)NULL, 
            &indicator)) )
    {
        extract_error( stmt, SQL_HANDLE_STMT );
        goto exit;
    }
            
    if(! SQL_SUCCEEDED(SQLExecute(stmt)))
    {
        extract_error( stmt, SQL_HANDLE_STMT );
        goto exit;
    }


    while(SQL_SUCCEEDED(SQLFetch(stmt)))
    {
        printf("SizeIndicator: %d, Result: %ld\n", (int)indicator, result_long);
    }
    else
    {
        fprintf(stderr, "Error fetching data\n" );
        extract_error( stmt, SQL_HANDLE_STMT );
    }
    
exit:
    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
    
    SQLDisconnect(dbc);
    SQLFreeHandle(SQL_HANDLE_DBC, dbc);
    
    SQLFreeHandle(SQL_HANDLE_ENV, env);
    
    return 0;
}

Content of testtab:

0   -58
1   -9.223.372.036.854.775.807
2   42
3   9.223.372.036.854.775.807

Output:


SizeIndicator: 4, Result: 4294967238
SizeIndicator: 0, Result: 2147483648
SizeIndicator: 4, Result: 42
SizeIndicator: 0, Result: 2147483647

Solution

  • For UnixODBC and Oracle ODBC, to get long 64 bit value the proper approch will be to fetch the value as a string.

    Use SQL_C_CHAR Then you can convert the value in long long (64 bit) using strtoll.

    This will also work for negative numbers.