// -*- mode:C++; tab-width:2; c-basic-offset:2; indent-tabs-mode:nil -*-
//
// Copyright (C) 2000-2005 by Roger Rene Kommer / artefaktur, Kassel, Germany.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public License (LGPL).
//
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// License ACDK-FreeLicense document enclosed in the distribution
// for more for more details.
// This file is part of the Artefaktur Component Development Kit:
// ACDK
//
// Please refer to
// - http://www.acdk.de
// - http://www.artefaktur.com
// - http://acdk.sourceforge.net
// for more information.
//
// $Header: /cvsroot/acdk/acdk/acdk_sql_odbc/src/acdk/sql/odbc/ODBCColumn.cpp,v 1.17 2005/04/06 19:03:44 kommer Exp $
#include "ODBCColumn.h"
#include "ODBCResultSet.h"
#include "ODBCResultSetMetaData.h"
#include "ODBCStatement.h"
#include "ODBCHandle.h"
#include "ODBCConnection.h"
#include "ODBCDriver.h"
#include <acdk/sql/SQLException.h>
#include <acdk/sql/SQLWarning.h>
#include <acdk/lang/Number.h>
#include <acdk/lang/Integer.h>
#include <acdk/lang/Byte.h>
#include <acdk/lang/Short.h>
#include <acdk/lang/Float.h>
#include <acdk/lang/Double.h>
#include <acdk/lang/Long.h>
#include <acdk/lang/Boolean.h>
#if !defined(ACDK_MINI)
#include <acdk/util/Date.h>
#include <acdk/sql/Time.h>
#include <acdk/sql/Timestamp.h>
#endif
/FONT>
#include <acdk/util/Properties.h>
#if !defined(ACDK_MINI)
#include <acdk/lang/NoSuchMethodException.h>
#include <acdk/lang/Thread.h>
#endif //!defined(ACDK_MINI)
#include <acdk/sql/Types.h>
#include <acdk/util/logging/Log.h>
#include <acdk/lang/System.h>
namespace acdk {
namespace sql {
namespace odbc {
using namespace acdk::lang;
ODBCColumn::ODBCColumn(INP(RODBCHandle) handle, int col)
: __hndl(handle)
, _col(col)
, _obj(Nil),
_autoIncrementFlag(AF_invalid), _caseSensitiveFlag(AF_invalid), _catalogNameFlag(AF_invalid),
_displaySizeFlag(AF_invalid), _fixedPrecScaleFlag(AF_invalid), _columnLabelFlag(AF_invalid),
_columnNameFlag(AF_invalid), _nullableFlag(AF_invalid), _octetLengthFlag(AF_invalid),
_precisionFlag(AF_invalid), _scaleFlag(AF_invalid), _schemaNameFlag(AF_invalid),
_searchableFlag(AF_invalid), _tableNameFlag(AF_invalid), _columnTypeFlag(AF_invalid),
_columnTypeNameFlag(AF_invalid), _unsignedFlag(AF_invalid), _writableFlag(AF_invalid)
{
}
ODBCColumn::~ODBCColumn()
{
ACDK_NLOG("acdk.sql.odbc", Debug, "ODBCColumn::~ODBCColumn");
}
RString
ODBCColumn::getColumnName() THROWS1(::acdk::sql::RSQLException)
{
_chkAttr(AI_columnName);
return _columnName;
}
RODBCHandle
ODBCColumn::_getODBCHandle() THROWS1(RSQLException)
{
return __hndl;
/*
if (__hndl == Nil)
{
__hndl = (_rset != Nil) ? _rset->_getODBCHandle() : RODBCHandle(Nil);
if (__hndl == Nil) {
THROW1(SQLException, "no handle");
}
}
return __hndl;
*/
}
void
ODBCColumn::_chkNull()
{
RODBCHandle hndl = _getODBCHandle();
if (_wasNull == true) {
// no possibility to return a base value which represents SQLNULL
RODBCHandle hndl = _getODBCHandle();
::acdk::sql::RSQLException ex = new SQLException("object was null.");
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
}
}
// less information is available via:
//
// SQLRETURN SQLDescribeCol(SQLHSTMT StatementHandle, SQLSMALLINT ColumnNumber, SQLCHAR//ColumnName, SQLSMALLINT BufferLength,
// SQLSMALLINT//NameLengthPtr, SQLSMALLINT//DataTypePtr, SQLUINTEGER//ColumnSizePtr, SQLSMALLINT//DecimalDigitsPtr,
// SQLSMALLINT//NullablePtr);
// but we use this one:
//
// SQLRETURN SQLColAttribute(SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, SQLPOINTER CharacterAttributePtr,
// SQLSMALLINT BufferLength, SQLSMALLINT//StringLengthPtr, SQLPOINTER NumericAttributePtr);
// FieldIdentifier can be one of:
// SQL_DESC_AUTO_UNIQUE_VALUE SQL_TRUE, SQL_FALSE
// SQL_DESC_BASE_COLUMN_NAME The base column name for the result set column.
// SQL_DESC_BASE_TABLE_NAME The name of the base table that contains the column.
// SQL_DESC_CASE_SENSITIVE SQL_TRUE, SQL_FALSE
// SQL_DESC_CATALOG_NAME The catalog of the table that contains the column.
// SQL_DESC_CONCISE_TYPE The concise data type.
// SQL_DESC_COUNT The number of columns available in the result set.
// SQL_DESC_DISPLAY_SIZE Maximum number of characters required to display data from the column.
// SQL_DESC_FIXED_PREC_SCALE SQL_TRUE, SQL_FALSE
// SQL_DESC_LABEL The column label or title.
// SQL_DESC_LENGTH A numeric value that is either the maximum or actual character length of a character string or binary data type.
// SQL_DESC_LITERAL_PREFIX This VARCHAR(128) record field contains the character or characters that the driver recognizes as a prefix for a literal of this data type.
// SQL_DESC_LITERAL_SUFFIX This VARCHAR(128) record field contains the character or characters that the driver recognizes as a suffix for a literal of this data type.
// SQL_DESC_LOCAL_TYPE_NAME This VARCHAR(128) record field contains any localized (native language) name for the data type that may be different from the regular name of the data type.
// SQL_DESC_NAME The column alias, if it applies. If the column alias does not apply, the column name is returned.
// SQL_DESC_NULLABLE SQL_NULLABLE, SQL_NO_NULLS, SQL_NULLABLE_UNKNOWN
// SQL_DESC_NUM_PREC_RADIX 2 for SQL_DESC_PRECISION in bits, 10 for SQL_DESC_PRECISION in digits
// SQL_DESC_OCTET_LENGTH The length, in bytes, of a character string or binary data type.
// SQL_DESC_PRECISION A numeric value that for a numeric data type denotes the applicable precision.
// SQL_DESC_SCALE For DECIMAL and NUMERIC data types, this is the defined scale. It is undefined for all other data types.
// SQL_DESC_SCHEMA_NAME The schema of the table that contains the column
// SQL_DESC_SEARCHABLE SQL_PRED_NONE, SQL_PRED_CHAR, SQL_PRED_BASIC, SQL_PRED_SEARCHABLE
// SQL_DESC_TABLE_NAME The name of the table that contains the column
// SQL_DESC_TYPE A numeric value that specifies the SQL data type
// SQL_DESC_TYPE_NAME Data source dependent data type name
// SQL_DESC_UNNAMED SQL_NAMED, SQL_UNNAMED
// SQL_DESC_UNSIGNED SQL_TRUE, SQL_FALSE
// SQL_DESC_UPDATABLE SQL_ATTR_READONLY, SQL_ATTR_WRITE, SQL_ATTR_READWRITE_UNKNOWN
// before ODBCVER 0x300 SQLColAttribute didn't exist, instead SQLColAttributes did.
// the argument-types are similar (transparently mappable), but the attribute-IDs
// changed completely...
// unfortunately not every flag has a similar name and some mappings are not clear.
// Note: all defines below are only for internal use and should never be moved into a header.
#if ODBCVER < 0x300
# define SQLColAttribute SQLColAttributes
# define SQL_PRED_NONE SQL_UNSEARCHABLE
# define SQL_PRED_CHAR SQL_LIKE_ONLY
# define SQL_PRED_BASIC SQL_ALL_EXCEPT_LIKE
# define SQL_PRED_SEARCHABLE SQL_SEARCHABLE
// following types have similar SQL_COLUMN_* defines, but may return other data:
// SQL_DESC_COUNT 1001
// SQL_DESC_TYPE 1002
// SQL_DESC_LENGTH 1003
// SQL_DESC_PRECISION 1005
// SQL_DESC_SCALE 1006
// SQL_DESC_NULLABLE 1008
// SQL_DESC_NAME 1011
// SQL_COLUMN_COUNT 0
// SQL_COLUMN_NAME 1
// SQL_COLUMN_TYPE 2
// SQL_COLUMN_LENGTH 3
// SQL_COLUMN_PRECISION 4
// SQL_COLUMN_SCALE 5
// SQL_COLUMN_NULLABLE 7
// win32-includes of VC has following direct-mappings:
// SQL_DESC_AUTO_UNIQUE_VALUE -> SQL_COLUMN_AUTO_INCREMENT
// SQL_DESC_CATALOG_NAME -> SQL_COLUMN_QUALIFIER_NAME
// SQL_DESC_CONCISE_TYPE -> SQL_COLUMN_TYPE
// SQL_DESC_FIXED_PREC_SCALE -> SQL_COLUMN_MONEY
// SQL_DESC_SCHEMA_NAME -> SQL_COLUMN_OWNER_NAME
#endif // ODBCVER
// known JDBC types:
// BigInt, Binary, Bit, Char, Date, Time, TimeStamp, Decimal, Double, Float, Integer,
// LongVarBinary, LongVarChar, Numeric, Real, SmallInt, TinyInt, VarBinary, VarChar
#define MAP_SQL(s,m) \
case s: \
_columnType = ::acdk::sql:: m; \
return true;
#define NOMAP_SQL(s) \
case s: // fall through
#define MDBCTYPENAME(t) \
case ::acdk::sql:: t: \
_columnTypeName = SCS( #t ); \
return true;
#define MDBC_ATTR(attr, type) \
case AI ## attr: \
if (attr ## Flag == AF_valid) \
return true; \
if (attr ## Flag == AF_failed) \
return false; \
if (_get ## type ## Attribute(AI ## attr, attr) == true) { \
attr ## Flag = AF_valid; \
return true; \
} \
attr ## Flag = AF_failed; \
return false; \
break;
#define MDBC_BOOLATTR(attr) \
MDBC_ATTR(attr, Boolean)
#define MDBC_INTATTR(attr) \
MDBC_ATTR(attr, Integer)
#define MDBC_STRINGATTR(attr) \
MDBC_ATTR(attr, String)
bool
ODBCColumn::_getAttribute(AttributeID attrID)
{
RODBCHandle hndl = _getODBCHandle();
int tmp = 0;
bool valid;
#if defined(ACDK_ODBC_DEBUG)
System::err->println(RString("SQLODBC [") + Integer::toString(Thread::currentThreadId().threadID()) + "]: getAttr(col=" + Integer::toString(_col) + ", id=" + Integer::toString(attrID) + ")");
#endif
/FONT>
switch (attrID) {
MDBC_BOOLATTR(_autoIncrement);
//_getStringAttribute(SQL_DESC_BASE_COLUMN_NAME);
//_getStringAttribute(SQL_DESC_BASE_TABLE_NAME);
MDBC_BOOLATTR(_caseSensitive);
MDBC_STRINGATTR(_catalogName);
//_getIntegerAttribute(SQL_DESC_CONCISE_TYPE);
//_columnCount = _getIntegerAttribute(SQL_COLUMN_COUNT);
MDBC_INTATTR(_displaySize);
//_getBooleanAttribute(SQL_COLUMN_MONEY); // SQL_DESC_FIXED_PREC_SCALE
MDBC_STRINGATTR(_columnLabel);
MDBC_BOOLATTR(_fixedPrecScale);
//_getStringAttribute(SQL_DESC_LITERAL_PREFIX);
//_getStringAttribute(SQL_DESC_LITERAL_SUFFIX);
//_getStringAttribute(SQL_DESC_LOCAL_TYPE_NAME);
MDBC_STRINGATTR(_columnName);
case (AI_nullable):
if (_nullableFlag == AF_failed)
return false;
if (_nullableFlag == AF_valid)
return true;
valid = _getIntegerAttribute(attrID, tmp);
if (valid == 1) {
_nullableFlag = AF_valid;
switch (tmp) {
case SQL_NULLABLE:
_nullable = ::acdk::sql::ColumnNullable;
return true;
case SQL_NO_NULLS:
_nullable = ::acdk::sql::ColumnNoNulls;
return true;
case SQL_NULLABLE_UNKNOWN:
_nullable = ::acdk::sql::ColumnNullableUnknown;
return true;
default:
RString msg = RString("value ") + ::acdk::lang::Integer::toString(tmp) + " unknown for attribute(nullable)" + " in column " + Integer::toString(_col) + ".";
hndl->_addWarning(new SQLWarning(msg));
_nullable = ::acdk::sql::ColumnNullableUnknown;
_nullableFlag = AF_failed;
return false;
}
}
_nullableFlag = AF_failed;
_nullable = ::acdk::sql::ColumnNullableUnknown;
return false;
//_getIntegerAttribute(SQL_DESC_NUM_PREC_RADIX);
MDBC_INTATTR(_octetLength);
MDBC_INTATTR(_precision);
MDBC_INTATTR(_scale);
MDBC_STRINGATTR(_schemaName);
case AI_searchable:
if (_searchableFlag == AF_failed)
return false;
if (_searchableFlag == AF_valid)
return true;
valid = _getIntegerAttribute(attrID, tmp);
if (valid != 0)
{
_searchableFlag = AF_valid;
switch (tmp) {
case SQL_PRED_NONE: // SQL_UNSEARCHABLE
_searchable = false;
return true;
case SQL_PRED_CHAR: // SQL_LIKE_ONLY
_searchable = false; // should this be true, too?
return true;
case SQL_PRED_BASIC: // SQL_EXCEPT_LIKE
_searchable = false; // should this be true, too?
return true;
case SQL_PRED_SEARCHABLE:
_searchable = true;
return true;
default:
RString msg = RString("value ") + ::acdk::lang::Integer::toString(tmp) + " unknown for attribute(searchable)" + " in column " + Integer::toString(_col) + ".";
hndl->_addWarning(new SQLWarning(msg));
_searchable = false;
_searchableFlag = AF_failed;
return true;
break;
}
}
_searchable = false;
_searchableFlag = AF_failed;
return false;
MDBC_STRINGATTR(_tableName);
case AI_columnType:
#if defined(ACDK_ODBC_DEBUG)
System::err->println(RString("SQLODBC [") + Integer::toString(Thread::currentThreadId().threadID()) + "]: getAttr() flag for type is " + Integer::toString(_columnTypeFlag) + ")");
#endif
/FONT>
if (_columnTypeFlag == AF_failed)
return false;
if (_columnTypeFlag == AF_valid)
return true;
valid = _getIntegerAttribute(attrID, tmp);
#if defined(ACDK_ODBC_DEBUG)
System::err->println(RString("SQLODBC [") + Integer::toString(Thread::currentThreadId().threadID()) + "]: getAttr() sql returns " + (valid != 0)? "true" : "false" + Integer::toString(tmp) + ")");
#endif
/FONT>
if (valid == true) {
_columnTypeFlag = AF_valid;
switch (tmp) {
// basic SQL types (direct mapped)
// SQL_UNKNOWN_TYPE moved to end.
MAP_SQL(SQL_CHAR, CharSqlType)
MAP_SQL(SQL_NUMERIC, NumericSqlType)
MAP_SQL(SQL_DECIMAL, DecimalSqlType)
MAP_SQL(SQL_INTEGER, IntegerSqlType)
MAP_SQL(SQL_SMALLINT, SmallIntSqlType)
MAP_SQL(SQL_FLOAT, FloatSqlType)
MAP_SQL(SQL_REAL, RealSqlType)
MAP_SQL(SQL_DOUBLE, DoubleSqlType)
MAP_SQL(SQL_VARCHAR, VarCharSqlType)
// three new types since ODBC 3.x
MAP_SQL(SQL_TYPE_DATE, DateSqlType)
MAP_SQL(SQL_TYPE_TIME, TimeSqlType)
MAP_SQL(SQL_TYPE_TIMESTAMP, TimeStampSqlType)
// extended SQL types
// following three types should be mapped internally by 3.x-drivers to basic types SQL_TYPE_*, but sometimes they aren't
MAP_SQL(SQL_DATE, OldDateSqlType)
MAP_SQL(SQL_TIME, OldTimeSqlType)
MAP_SQL(SQL_TIMESTAMP, OldTimeStampSqlType)
MAP_SQL(SQL_LONGVARCHAR, LongVarCharSqlType)
MAP_SQL(SQL_BINARY, BinarySqlType)
MAP_SQL(SQL_VARBINARY, VarBinarySqlType)
MAP_SQL(SQL_LONGVARBINARY, LongVarBinarySqlType)
MAP_SQL(SQL_BIGINT, BigIntSqlType)
MAP_SQL(SQL_TINYINT, TinyIntSqlType)
MAP_SQL(SQL_BIT, BitSqlType)
#if (ODBCVER >= 0x0300)
// following types for unicode or similar wide-chars and are equal to those without a `W since ODBC 3.x
#if defined(ACDK_HAS_SQL_WCHAR)
MAP_SQL(SQL_WCHAR, WCharSqlType)
MAP_SQL(SQL_WLONGVARCHAR, WLongVarCharSqlType)
MAP_SQL(SQL_WVARCHAR, WVarCharSqlType)
#endif
/FONT>
#endif
/FONT>
#if (ODBCVER >= 0x0300) && defined(ACDK_OS_WIN32)
NOMAP_SQL(SQL_GUID)
#endif // ODBCVER
NOMAP_SQL(SQL_INTERVAL_YEAR)
NOMAP_SQL(SQL_INTERVAL_MONTH)
NOMAP_SQL(SQL_INTERVAL_DAY)
NOMAP_SQL(SQL_INTERVAL_HOUR)
NOMAP_SQL(SQL_INTERVAL_MINUTE)
NOMAP_SQL(SQL_INTERVAL_SECOND)
NOMAP_SQL(SQL_INTERVAL_YEAR_TO_MONTH)
NOMAP_SQL(SQL_INTERVAL_DAY_TO_HOUR)
NOMAP_SQL(SQL_INTERVAL_DAY_TO_MINUTE)
NOMAP_SQL(SQL_INTERVAL_DAY_TO_SECOND)
NOMAP_SQL(SQL_INTERVAL_HOUR_TO_MINUTE)
NOMAP_SQL(SQL_INTERVAL_HOUR_TO_SECOND)
NOMAP_SQL(SQL_INTERVAL_MINUTE_TO_SECOND)
case SQL_UNKNOWN_TYPE:
default:
RString msg = RString("value ") + ::acdk::lang::Integer::toString(tmp) + " unknown or unhandled for attribute(type)" + " in column " + Integer::toString(_col) + ".";
hndl->_addWarning(new SQLWarning(msg));
_columnTypeFlag = AF_failed;
return false;
}
}
_columnType = ::acdk::sql::NullSqlType; // as defined there, but should be of type unknown!
_columnTypeFlag = AF_failed;
return false;
case AI_columnTypeName:
if (_columnTypeNameFlag == AF_failed)
return false;
if (_columnTypeNameFlag == AF_valid)
return true;
// we don't use _getStringAttribute() to get _columnTypeName, because we're not interested
// in the name used by the driver, but the type's MDBC-Name.
// _columnTypeName = _getStringAttribute(SQL_DESC_TYPE_NAME);
if (_getAttribute(AI_columnType) == true) {
_columnTypeNameFlag = AF_valid;
switch (_columnType)
{
MDBCTYPENAME(CharSqlType)
MDBCTYPENAME(NumericSqlType)
MDBCTYPENAME(DecimalSqlType)
MDBCTYPENAME(IntegerSqlType)
MDBCTYPENAME(SmallIntSqlType)
MDBCTYPENAME(FloatSqlType)
MDBCTYPENAME(RealSqlType)
MDBCTYPENAME(DoubleSqlType)
MDBCTYPENAME(VarCharSqlType)
MDBCTYPENAME(WVarCharSqlType)
MDBCTYPENAME(DateSqlType)
MDBCTYPENAME(TimeSqlType)
MDBCTYPENAME(TimeStampSqlType)
MDBCTYPENAME(OldDateSqlType)
MDBCTYPENAME(OldTimeSqlType)
MDBCTYPENAME(OldTimeStampSqlType)
MDBCTYPENAME(LongVarCharSqlType)
MDBCTYPENAME(BinarySqlType)
MDBCTYPENAME(VarBinarySqlType)
MDBCTYPENAME(LongVarBinarySqlType)
MDBCTYPENAME(BigIntSqlType)
MDBCTYPENAME(TinyIntSqlType)
MDBCTYPENAME(BitSqlType)
default:
_columnTypeNameFlag = AF_failed;
_columnTypeName = Nil;
RString msg = RString("invalid value for columnType (") + ::acdk::lang::Integer::toString(tmp) + ") in column " + Integer::toString(_col) + ".";
::acdk::sql::RSQLException ex = new SQLException(msg);
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
return false;
}
}
_columnTypeNameFlag = AF_failed;
_columnTypeName = Nil;
return false;
//_getIntegerAttribute(SQL_DESC_UNNAMED);
MDBC_BOOLATTR(_unsigned);
case AI_writable:
if (_writableFlag == AF_failed)
return false;
if (_writableFlag == AF_valid)
return true;
valid = _getIntegerAttribute(attrID, tmp);
if (valid != 0) {
_writableFlag = AF_valid;
switch (tmp) {
case SQL_ATTR_READONLY:
_writable = false;
break;
case SQL_ATTR_WRITE:
_writable = true;
break;
case SQL_ATTR_READWRITE_UNKNOWN:
_writable = false;
//_writableFlag = AF_failed;
break;
default:
RString msg = RString("value ") + ::acdk::lang::Integer::toString(tmp) + " unknown for attribute(updatable)" + " in column " + Integer::toString(_col) + ".";
hndl->_addWarning(new SQLWarning(msg));
_writable = false;
_writableFlag = AF_failed;
return false;
}
}
_writableFlag = AF_failed;
_writable = false;
return false;
default:
RString msg = RString("invalid AttributeID (") + ::acdk::lang::Integer::toString(attrID) + ") in column " + Integer::toString(_col) + ".";
::acdk::sql::RSQLException ex = new SQLException(msg);
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
}
return false;
}
#undef MDBC_ATTR
#undef MDBC_INTATTR
#undef MDBC_BOOLATTR
#undef MDBC_STRINGATTR
#undef MAP_SQL
#undef NOMAP_SQL
#undef MDBCTYPENAME
bool
ODBCColumn::_getStringAttribute(int attrID, RString& value)
{
SQLRETURN ret;
SQLCHAR res[4097] = { 0 };
SQLSMALLINT len = 0; // do we need this?
RODBCHandle hndl = _getODBCHandle();
// special handling for MS SQL
if (attrID == AI_columnName)
{
SQLRETURN ret = SQLColAttribute(hndl->_getSQLHandle(), _col, attrID, res, sizeof(res), &len, 0);
if (ret == SQL_SUCCESS)
{
value = ODBCSTR2STR(res, len);
return true;
}
ret = SQLColAttribute(hndl->_getSQLHandle(), _col, AI_columnLabel, res, sizeof(res), &len, 0);
if (ret == SQL_SUCCESS)
{
value = ODBCSTR2STR(res, len);
return true;
}
// give up and let default call throw the exception
}
ret = callSQL6(hndl, SQLColAttribute, _col, attrID, res, sizeof(res), &len, 0);
if ((ret != SQL_SUCCESS) && (ret != SQL_SUCCESS_WITH_INFO))
return false;
value = ODBCSTR2STR(res, len);
return true;
}
bool
ODBCColumn::_getBooleanAttribute(int attrID, bool& value)
{
SQLRETURN ret;
SQLINTEGER res;
RODBCHandle hndl = _getODBCHandle();
ret = callSQL6(hndl, SQLColAttribute, _col, attrID, 0, 0, 0, &res);
if ((ret != SQL_SUCCESS) && (ret != SQL_SUCCESS_WITH_INFO))
return false;
value = (res != 0);
return true;
}
bool
ODBCColumn::_getIntegerAttribute(int attrID, int& value)
{
SQLRETURN ret;
SQLINTEGER res = 0;
RODBCHandle hndl = _getODBCHandle();
ret = callSQL6(hndl, SQLColAttribute, _col, attrID, 0, 0, 0, &res);
if ((ret != SQL_SUCCESS) && (ret != SQL_SUCCESS_WITH_INFO))
return false;
value = res;
return true;
}
void
ODBCColumn::_chkAttr(AttributeID attrID)
{
RODBCHandle hndl = _getODBCHandle();
if (_getAttribute(attrID) == false) {
::acdk::sql::RSQLException ex = new SQLException(RString("no value for requested item (") + Integer::toString(attrID) + ") in Column " + Integer::toString(_col) + ".");
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
}
}
//virtual
int
ODBCColumn::getColumnCount()
{
RODBCHandle hndl = _getODBCHandle();
int res;
#if defined(ACDK_OS_WIN32)
if (_getIntegerAttribute(SQL_DESC_COUNT, res) == false) {
#else
/FONT>
if (_getIntegerAttribute(SQL_COLUMN_COUNT, res) == false) {
#endif
/FONT>
::acdk::sql::RSQLException ex = new SQLException("no value for column-count.");
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
}
return res;
}
#define MKDATA(__dbt, __tt, __sz, __res) \
case ::acdk::sql:: __dbt: \
type = __tt; \
len = __sz; \
res = __res; \
break;
#define VALDATA(__dbt, __tt) \
MKDATA(__dbt, __tt, 0, &vRes)
#define BUFDATA(__dbt, __var) \
MKDATA(__dbt, SQL_C_CHAR, sizeof(bRes. __var), &bRes)
#define XVARDATA(__dbt) \
case ::acdk::sql:: __dbt: \
_chkAttr(AI_octetLength); \
varbuf = new byteArray(_octetLength + 1); \
type = SQL_C_CHAR; \
len = _octetLength + 1; \
res = varbuf->data(); \
break;
#define MK_NRESULT(__dbt, __ot, __dat) \
case ::acdk::sql:: __dbt: \
if (_wasNull == true) { \
vRes.__dat = 0; \
} \
_obj = new __ot(vRes.__dat); \
break;
#define MK_SRESULT(__dbt) \
case ::acdk::sql:: __dbt: \
if (_wasNull == true) { \
_obj = new String(""); \
} else { \
_obj = (acdk::lang::Object)SCS(reinterpret_cast<char *>(varbuf->data())); \
} \
break;
#if defined(_UNICODE)
#define MK_WSRESULT(__dbt) \
case ::acdk::sql:: __dbt: \
if (_wasNull == true) { \
_obj = new String(""); \
} else { \
_obj = (acdk::lang::Object)ODBCSTR2STR(varbuf->data(), varbuf->length()); \
} \
break;
#else
/FONT>
#define MK_WSRESULT(__dbt) \
case ::acdk::sql:: __dbt: \
if (_wasNull == true) { \
_obj = new String(""); \
} else { \
_obj = new String(reinterpret_cast<char*>(varbuf->data()), NormalSST | CCAscii); \
} \
break;
#endif
/FONT>
#define MK_BRESULT(__dbt) \
case ::acdk::sql:: __dbt: \
if (_wasNull == true) { \
_obj = new byteArray((const unsigned char *)(""), 0); \
} else { \
_obj = new byteArray(varbuf->data(), len_ind); \
} \
break;
// SQLRETURN SQLGetData(SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLPOINTER TargetValuePtr,
// SQLINTEGER BufferLength, SQLINTEGER *StrLen_or_IndPtr);
acdk::lang::Object
ODBCColumn::_getData()
{
union {
char byteval;
short shortval;
long longval;
float floatval;
double doubleval;
} vRes;
union {
char jlongbuf[39]; // 20 digits (or 19 + sign) + perhaps terminating 0, but oracle uses decimal with a size of 38
DATE_STRUCT datebuf;
TIME_STRUCT timebuf;
TIMESTAMP_STRUCT timestampbuf;
#if ODBCVER >= 0x0300
SQL_NUMERIC_STRUCT numericbuf;
#if defined(ACDK_OS_WIN32)
SQLGUID guidbuf;
#endif // ACDK_OS_WIN32
SQL_INTERVAL_STRUCT intervalbuf;
#endif // ODBCVER
} bRes;
RbyteArray varbuf;
SQLPOINTER res;
int type, len = 0;
SQLINTEGER len_ind;
RODBCHandle hndl = _getODBCHandle();
#if defined(ACDK_ODBC_DEBUG)
System::err->println(RString("SQLODBC [") + Integer::toString(Thread::currentThreadId().threadID()) + "]: getData(" + Integer::toString(_col) + ")");
#endif
/FONT>
_chkAttr(AI_columnType);
#if defined(ACDK_ODBC_DEBUG)
System::err->println(RString("SQLODBC [") + Integer::toString(Thread::currentThreadId().threadID()) + "]: getData() type is " + Integer::toString(_columnType));
_chkAttr(AI_columnName);
System::err->println(RString("SQLODBC [") + Integer::toString(Thread::currentThreadId().threadID()) + "]: getData() type is " + _columnName);
#endif
/FONT>
switch (_columnType) {
VALDATA(TinyIntSqlType, SQL_C_UTINYINT);
VALDATA(BitSqlType, SQL_C_BIT);
VALDATA(SmallIntSqlType, SQL_C_USHORT);
VALDATA(IntegerSqlType, SQL_C_ULONG);
VALDATA(RealSqlType, SQL_C_FLOAT);
VALDATA(FloatSqlType, SQL_C_DOUBLE);
VALDATA(DoubleSqlType, SQL_C_DOUBLE);
BUFDATA(BigIntSqlType, jlongbuf);
BUFDATA(DateSqlType, datebuf);
BUFDATA(TimeSqlType, timebuf);
BUFDATA(TimeStampSqlType, timestampbuf);
BUFDATA(OldDateSqlType, datebuf);
BUFDATA(OldTimeSqlType, timebuf);
BUFDATA(OldTimeStampSqlType, timestampbuf);
// BUFDATA(Numeric, numericbuf);
BUFDATA(NumericSqlType, jlongbuf);
// BUFDATA(Decimal, decimalbuf);
BUFDATA(DecimalSqlType, jlongbuf);
XVARDATA(CharSqlType);
XVARDATA(VarCharSqlType);
XVARDATA(LongVarCharSqlType);
#if !defined(ACDK_MINI)
XVARDATA(WLongVarCharSqlType);
XVARDATA(WVarCharSqlType);
XVARDATA(WCharSqlType);
#endif //!defined(ACDK_MINI)
XVARDATA(BinarySqlType);
XVARDATA(VarBinarySqlType);
XVARDATA(LongVarBinarySqlType);
default:
::acdk::sql::RSQLException ex = new SQLException(RString("unknown or unhandled column-type (") + Integer::toString(_columnType) + ") in column " + Integer::toString(_col) + ".");
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
break;
}
callSQL5(hndl, SQLGetData, _col, type, res, len, &len_ind);
_wasNull = false;
#if defined(ACDK_ODBC_DEBUG)
System::err->println(RString("SQLODBC [") + Integer::toString(Thread::currentThreadId().threadID()) + "]: getData() lenInd is " + Integer::toString(len_ind));
#endif
/FONT>
switch (len_ind) {
case SQL_NTS: // 0-terminated string, just do nothing
break;
case SQL_NULL_DATA:
_wasNull = true;
break;
case SQL_NO_TOTAL: // should this be an exception?
{
::acdk::sql::RSQLWarning wg = new SQLWarning(RString("driver can't determine number of available bytes for long data in column ") + Integer::toString(_col) + ".");
hndl->_addWarning(wg);
}
return Nil;
case SQL_DATA_AT_EXEC: // invalid for receiving results
case SQL_DEFAULT_PARAM: // invalid for receiving results
case SQL_COLUMN_IGNORE: // invalid for receiving results
default:
if (len_ind < 0) {
RString msg = RString("unknown or invalid length indicator for getting data (") + ::acdk::lang::Integer::toString(len_ind) + ") in column " + Integer::toString(_col) + ".";
::acdk::sql::RSQLException ex = new SQLException(msg);
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
return Nil;
}
// maybe we should check here, if len_ind equals len for all char[]
break;
}
switch (_columnType) {
MK_NRESULT(TinyIntSqlType, ::acdk::lang::Byte, byteval)
MK_NRESULT(BitSqlType, ::acdk::lang::Byte, byteval)
MK_NRESULT(SmallIntSqlType, ::acdk::lang::Short, shortval)
MK_NRESULT(IntegerSqlType, ::acdk::lang::Integer, longval)
MK_NRESULT(RealSqlType, ::acdk::lang::Float, floatval)
MK_NRESULT(FloatSqlType, ::acdk::lang::Double, doubleval)
MK_NRESULT(DoubleSqlType, ::acdk::lang::Double, doubleval)
// (Numeric, ::acdk::math::BigDecimal)
// (Decimal, ::acdk::math::BigDecimal)
case ::acdk::sql::NumericSqlType:
case ::acdk::sql::DecimalSqlType:
case ::acdk::sql::BigIntSqlType:
if (_wasNull == true) {
_obj = new Long(jlong(0));
} else {
try {
::acdk::lang::RString tmp = SCS(bRes.jlongbuf);
tmp = tmp->replace(',', '.');
if (tmp->indexOf('.') >= 0)
_obj = new ::acdk::lang::Double(::acdk::lang::Double::parseDouble(tmp));
else
_obj = new ::acdk::lang::Long(::acdk::lang::Long::parseLong(tmp));
} catch(RNumberFormatException) {
RString msg = RString("value \"") + bRes.jlongbuf + "\" in column " + Integer::toString(_col) + " can't be converted into a numeric value.";
::acdk::sql::RSQLException ex = new SQLException(msg);
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
}
}
break;
#if !defined(ACDK_MINI)
case ::acdk::sql::DateSqlType:
case ::acdk::sql::OldDateSqlType:
if (_wasNull == true) {
_obj = new ::acdk::util::Date(0, 0, 0);
} else {
_obj = new ::acdk::util::Date(bRes.datebuf.year, bRes.datebuf.month, bRes.datebuf.day); // deprecated constructor of Date!!!
}
break;
case ::acdk::sql::TimeSqlType:
case ::acdk::sql::OldTimeSqlType:
if (_wasNull == true)
{
_obj = new class acdk::sql::Time(0, 0, 0);
} else {
_obj = new class acdk::sql::Time(bRes.timebuf.hour, bRes.timebuf.minute, bRes.timebuf.second); // uses deprecated constructor of Date!!!
}
break;
case ::acdk::sql::TimeStampSqlType:
case ::acdk::sql::OldTimeStampSqlType:
if (_wasNull == true) {
_obj = new ::acdk::sql::Timestamp(0, 0, 0, 0, 0, 0, 0);
} else {
_obj = new ::acdk::sql::Timestamp(bRes.timestampbuf.year, bRes.timestampbuf.month, bRes.timestampbuf.day,
bRes.timestampbuf.hour, bRes.timestampbuf.minute, bRes.timestampbuf.second,
bRes.timestampbuf.fraction); // uses deprecated constructor of Date!!!
}
break;
#endif
/FONT>
case ::acdk::sql::CharSqlType:
case ::acdk::sql::VarCharSqlType:
case ::acdk::sql::LongVarCharSqlType:
if (_wasNull == true)
{
_obj = &String::emptyString();
}
else
{
_obj = (acdk::lang::Object)SCS(reinterpret_cast<char *>(varbuf->data()));
}
break;
#if !defined(ACDK_MINI)
case ::acdk::sql::WCharSqlType:
case ::acdk::sql::WVarCharSqlType:
case ::acdk::sql::WLongVarCharSqlType:
if (_wasNull == true) {
_obj = &String::emptyString();
} else {
byte* d = varbuf->data();
_obj = ODBCSTR2STR(varbuf->data(), -1);
}
break;
//MK_WSRESULT(WVarChar)
#endif //!defined(ACDK_MINI)
MK_BRESULT(BinarySqlType)
MK_BRESULT(VarBinarySqlType)
MK_BRESULT(LongVarBinarySqlType)
default:
::acdk::sql::RSQLException ex = new SQLException(RString("unknown or unhandled column-type (") + Integer::toString(_columnType) + ") in column " + Integer::toString(_col) + ".");
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
break;
}
#if defined(ACDK_ODBC_DEBUG)
if (_obj != Nil)
System::err->println(RString("SQLODBC [") + Integer::toString(Thread::currentThreadId().threadID()) + "]: getData() object is of type " + _obj->getName());
else
System::err->println(RString("SQLODBC [") + Integer::toString(Thread::currentThreadId().threadID()) + "]: getData() object is of type NIL");
#endif
/FONT>
if (_obj == Nil) {
::acdk::sql::RSQLException ex = new SQLException(RString("can't get data for column ") + Integer::toString(_col) + ".");
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
}
return _obj;
}
#undef XVARDATA
#undef BUFDATA
#undef VALDATA
#undef MKDATA
#undef MK_NRESULT
#undef MK_SRESULT
#undef MK_BRESULT
//virtual
RNumber
ODBCColumn::_getNumber()
{
RNumber num;
if (_obj == Nil)
_getData();
if (instanceof(_obj, Number) == false) {
RODBCHandle hndl = _getODBCHandle();
::acdk::sql::RSQLException ex = new SQLException(RString("object in column ") + Integer::toString(_col) + "can't be converted into an number.");
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
}
num = RNumber(_obj); // should work without an exception and should return value != Nil
if (num == Nil) {
RODBCHandle hndl = _getODBCHandle();
::acdk::sql::RSQLException ex = new SQLException(RString("can't make a number out of object in column ") + Integer::toString(_col) + ".");
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
}
return num;
}
//virtual
RNumber
ODBCColumn::getNumber()
{
RNumber num;
num = _getNumber();
if (_wasNull == true)
return Nil;
return num;
}
//virtual
int
ODBCColumn::getInt()
{
int res;
try {
res = _getNumber()->intValue();
} catch (Exception nsme) {
RODBCHandle hndl = _getODBCHandle();
::acdk::sql::RSQLException ex = new SQLException(RString("object in column ") + Integer::toString(_col) + "can't be converted into an integer.");
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
}
//_chkNull();
if (_wasNull == true)
return 0;
return res;
}
//virtual
byte
ODBCColumn::getByte()
{
byte res;
try {
res = _getNumber()->byteValue();
} catch (Exception nsme) {
RODBCHandle hndl = _getODBCHandle();
::acdk::sql::RSQLException ex = new SQLException(RString("object in column ") + Integer::toString(_col) + "can't be converted into an boolean.");
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
}
//_chkNull();
if (_wasNull == true)
return 0;
return res;
}
//virtual
double
ODBCColumn::getDouble()
{
double res;
try {
res = _getNumber()->doubleValue();
} catch (Exception nsme) {
RODBCHandle hndl = _getODBCHandle();
::acdk::sql::RSQLException ex = new SQLException(RString("object in column ") + Integer::toString(_col) + "can't be converted into an double.");
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
}
//_chkNull();
if (_wasNull == true)
return 0;
return res;
}
//virtual
float
ODBCColumn::getFloat()
{
float res;
try {
res = _getNumber()->floatValue();
} catch (Exception nsme) {
RODBCHandle hndl = _getODBCHandle();
::acdk::sql::RSQLException ex = new SQLException(RString("object in column ") + Integer::toString(_col) + "can't be converted into an float.");
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
}
//_chkNull();
if (_wasNull == true)
return 0;
return res;
}
//virtual
jlong
ODBCColumn::getLong()
{
jlong res;
try {
res = _getNumber()->intValue();
} catch (Exception nsme) {
RODBCHandle hndl = _getODBCHandle();
::acdk::sql::RSQLException ex = new SQLException(RString("object in column ") + Integer::toString(_col) + "can't be converted into an long.");
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
}
//_chkNull();
if (_wasNull == true)
return 0;
return res;
}
//virtual
short
ODBCColumn::getShort()
{
short res;
try {
res = _getNumber()->intValue();
} catch (Exception nsme) {
RODBCHandle hndl = _getODBCHandle();
::acdk::sql::RSQLException ex = new SQLException(RString("object in column ") + Integer::toString(_col) + "can't be converted into an short.");
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
}
//_chkNull();
if (_wasNull == true)
return 0;
return res;
}
//virtual
RbyteArray
ODBCColumn::getBytes()
{
if (_obj == Nil)
_getData();
if ((instanceof(_obj, charArray) == false) &&
(instanceof(_obj, byteArray) == false) &&
(instanceof(_obj, shortArray) == false) &&
(instanceof(_obj, intArray) == false) &&
(instanceof(_obj, longArray) == false) &&
(instanceof(_obj, floatArray) == false) &&
(instanceof(_obj, doubleArray) == false)) {
RODBCHandle hndl = _getODBCHandle();
::acdk::sql::RSQLException ex = new SQLException(RString("object in column ") + Integer::toString(_col) + "can't be converted into a byteArray.");
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
}
if (_wasNull == true)
return Nil;
return RbyteArray(_obj);
}
//virtual
bool
ODBCColumn::getBoolean()
{
RBoolean val;
bool res;
if (_obj == Nil)
_getData();
if (instanceof(_obj, acdk::lang::Boolean) == false) {
RODBCHandle hndl = _getODBCHandle();
::acdk::sql::RSQLException ex = new SQLException(RString("object in column ") + Integer::toString(_col) + "can't be converted into a Boolean.");
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
}
val = acdk::lang::RBoolean(_obj); // should work without an exception and should return value != Nil
if (val == Nil) {
RODBCHandle hndl = _getODBCHandle();
::acdk::sql::RSQLException ex = new SQLException(RString("can't make a boolean out of object in column ") + Integer::toString(_col) + ".");
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
}
try {
res = val->booleanValue(); // only defined for RBoolean ...
} catch (Exception nsme) {
RODBCHandle hndl = _getODBCHandle();
::acdk::sql::RSQLException ex = new SQLException(RString("no method to get a boolean out of object in column ") + Integer::toString(_col) + ".");
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
}
//_chkNull();
if (_wasNull == true)
return false;
return res;
}
#if !defined(ACDK_MINI)
//virtual
::acdk::util::RDate
ODBCColumn::getDate()
{
if (_obj == Nil)
_getData();
if (_wasNull == true)
return Nil;
if (instanceof(_obj, acdk::util::Date) == false) {
RODBCHandle hndl = _getODBCHandle();
::acdk::sql::RSQLException ex = new SQLException(RString("object in column ") + Integer::toString(_col) + "can't be converted into a date.");
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
}
return ::acdk::util::RDate(_obj);
}
#endif //!defined(ACDK_MINI)
//virtual
RString
ODBCColumn::getString()
{
RString res;
if (_obj == Nil)
_getData();
if (_wasNull)
return Nil;
if (instanceof(_obj, acdk::util::String) == false) {
try {
res = _obj->toString();
} catch (Exception nsme) {
RODBCHandle hndl = _getODBCHandle();
::acdk::sql::RSQLException ex = new SQLException(RString("object in column ") + Integer::toString(_col) + "can't be converted into an string.");
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
}
} else {
res = ::acdk::util::RString(_obj);
}
return res;
}
#if !defined(ACDK_MINI)
RBlob
ODBCColumn::getBlob()
{
THROW0(UnsupportedOperationException);
return Nil;
}
//virtual
RTime
ODBCColumn::getTime()
{
if (_obj == Nil)
_getData();
if (_wasNull)
return Nil;
if (instanceof(_obj, ::acdk::sql::Time) == false)
{
RODBCHandle hndl = _getODBCHandle();
::acdk::sql::RSQLException ex = new SQLException(RString("object in column ") + Integer::toString(_col) + "can't be converted into a time.");
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
}
return ::acdk::sql::RTime(_obj);
}
//virtual
RTimestamp
ODBCColumn::getTimestamp()
{
if (_obj == Nil)
_getData();
if (_wasNull)
return Nil;
if (instanceof(_obj, acdk::sql::Timestamp) == false)
{
RODBCHandle hndl = _getODBCHandle();
::acdk::sql::RSQLException ex = new SQLException(RString("object in column ") + Integer::toString(_col) + "can't be converted into a timestamp.");
hndl->_addException(ex);
SQLTHROW(hndl->_getExceptions());
}
return ::acdk::sql::RTimestamp(_obj);
}
#endif
/FONT>
} // odbc
} // sql
} // acdk
|