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
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.