2005/5/9

     
 

ODBCHandle.cpp

artefaktur
// -*- 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/ODBCHandle.cpp,v 1.13 2005/03/08 18:55:37 kommer Exp $
#include "ODBCHandle.h"
#include <acdk/lang/Integer.h>
#include <acdk/lang/StringBuffer.h>
#include <acdk/lang/System.h>
#include <acdk/util/logging/Log.h>

int forceODBCLibToLink()
{
  return 0;
}
namespace acdk {
namespace sql {
namespace odbc {

struct TypeToTypeMap
{
  int odbcType;
  int sqlType;
};

/*
TypeToTypeMap typeMap[] =
{
  
};

int 
ODBCHandle::odbcTypeToSqlType(int typ)
{

}
int 
ODBCHandle::sqlTypeToOdbcType(int typ)
{
}
*/

// #define DOUT(msg) do { std::cout <<  msg << ". " << __FILE__ << ":" << __LINE__ << std::endl; } while(false)
#define DOUT(msg) do { } while(false)

#if 0
#define SQLCHAINP(chain, msg) \
if ((chain != Nil) /* && (msg != Nil) */) { \
  ::acdk::sql::RSQLException tSQL = new SQLException(msg); \
  tSQL->setNextException(chain); \
  chain = tSQL; \
  tSQL = Nil; \
} else { \
  chain = new SQLException(msg); \
}

#endif // 0

ODBCHandle::ODBCHandle(SQLSMALLINT htype) 
: _handle(SQL_NULL_HANDLE)
, _htype(htype)
, _initialized(false)
, _returnCodeValid(false)
, _numberRecordsValid(false)
, _cursorRowCountValid(false)
, _rowCountValid(false)
, _dynamicFunctionCodeValid(false)
, _statusRecords(Nil) 
{
  ACDK_NLOG("acdk.sql.odbc", Debug, "ODBCHandle::ODBCHandle");
}

ODBCHandle::~ODBCHandle() 
{ 
  //crashes on linux because called in __do_global_dtors ACDK_NLOG("acdk.sql.odbc", Debug, "ODBCHandle::~ODBCHandle");
  deinit();
  DOUT("ODBCHandle::~ODBCHandle() AFTER dinit()");
}

RODBCHandle
ODBCHandle::init(INP(RODBCHandle) prnt)
{
  SQLRETURN err;
  SQLHANDLE parent;
  RString hndltype;
  ::acdk::sql::RSQLException excpt = Nil;
  ::acdk::sql::RSQLWarning warng = Nil;
  
  if (prnt != Nil)
    parent = prnt->_getSQLHandle();
  else
    parent = SQL_NULL_HANDLE;
  
  if (_initialized == true)
    return this;
#if ODBCVER >= 0x300
  ACDK_NLOG("acdk.sql.odbc", Debug, "ODBCHandle:: SQLAllocHandle");
  err = ::SQLAllocHandle(_htype, parent, &_handle);
#else // ODBCVER
  switch (_htype) {
    case SQL_HANDLE_ENV:
      ACDK_NLOG("acdk.sql.odbc", Debug, "ODBCHandle:: SQLAllocEnv");
      err = ::SQLAllocEnv(&_handle);
      break;
    case SQL_HANDLE_DBC:
      ACDK_NLOG("acdk.sql.odbc", Debug, "ODBCHandle:: SQLAllocEnv");
      err = ::SQLAllocConnect(parent, &_handle);
      break;
    case SQL_HANDLE_STMT:
      ACDK_NLOG("acdk.sql.odbc", Debug, "ODBCHandle:: SQLAllocEnv");
      err = ::SQLAllocStmt(parent, &_handle);
      break;
    default:
      _addException("unknown handle-type");
      SQLTHROW(_excpt);
      break;
  }
#endif // ODBCVER
  switch (err) {
    case SQL_SUCCESS: // all went ok.
      _initialized = true;
      break;
    case SQL_SUCCESS_WITH_INFO:
      if ((prnt != Nil) && (parent != SQL_NULL_HANDLE))
        prnt->_getSQLdiag(err, __FILE__, __LINE__);
      SQLTHROW(_excpt);
      break;
    case SQL_INVALID_HANDLE:
      _addException("invalid parent handle");
      SQLTHROW(_excpt);
      break;
    case SQL_ERROR:
      if (_handle != SQL_NULL_HANDLE) {
        _getSQLdiag(err, __FILE__, __LINE__);
      } else if ((prnt != Nil) && (parent != SQL_NULL_HANDLE)) {
        prnt->_getSQLdiag(err, __FILE__, __LINE__);
        SQLTHROW(prnt->_getExceptions());
        break;
      } else {
        _addException("SQLAllocHandle returns error, and there's no possibility to get more info about the reason");
      }
      SQLTHROW(_excpt);
      break;
    default:  // ooops!
      _addException("unknown error from SQLAllocHandle");
      SQLTHROW(_excpt);
      break;
  }
#if ODBCVER >= 0x0300
  if (_htype == SQL_HANDLE_ENV) {
    /*
     * SQLRETURN SQLSetEnvAttr(SQLHENV EnvironmentHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength);
     */
    // we always have to set the version.
    callSQL3(this, SQLSetEnvAttr, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC2, SQL_IS_UINTEGER);
  }
#endif // ODBCVER
  return this;
}

void
ODBCHandle::deinit()
{
  if (_initialized == false)
    return;
  if (sys::core_system::getState() == sys::AfterMain)
    return;
  // SQLFreeHandle() is (maybe) the only SQL-call without the handle as first arg...
#if ODBCVER >= 0x300
  if (_htype == SQL_HANDLE_DBC)
    ::SQLDisconnect(_handle);
  _chkSQLrcode(SQLFreeHandle(_htype, _handle), "SQLFreeHandle", __FILE__, __LINE__);
#else // ODBCVER
  switch (_htype) {
    case SQL_HANDLE_ENV:
      ACDK_NLOG("acdk.sql.odbc", Debug, "ODBCHandle:: SQLFreeEnv");
      ::SQLFreeEnv(&_handle);
      break;
    case SQL_HANDLE_DBC:
      ACDK_NLOG("acdk.sql.odbc", Debug, "ODBCHandle:: SQLDisconnect & SQLFreeConnect");
      ::SQLDisconnect(&_handle);
      ::SQLFreeConnect(&_handle);
      break;
    case SQL_HANDLE_STMT:
      ACDK_NLOG("acdk.sql.odbc", Debug, "ODBCHandle:: SQLFreeStmt");
      ::SQLFreeStmt(&_handle, SQL_DROP);
      break;
    default:
      _addException("unknown handle-type");
      SQLTHROW(_excpt);
      break;
  }
#endif // ODBCVER
  _initialized = false;
  _handle = 0;
  _htype = -1;
}

/*
 * helper-functions to get combined warning/error-List from underlaying ODBC-Driver and
 * devide it into separate object-lists of type SQLException and SQLWarning.
 * it returns false for no error or warning, and true if there're warnings
 * the references may be already initialized or not.
 */


void
ODBCHandle::_getSQLdiag(SQLRETURN err, char *file, int line)
{
  SYNCTHIS();
  const char *errstr;
  
  switch (err) {
    case SQL_SUCCESS:
      errstr = "SQL_SUCCESS";
      break;
    case SQL_SUCCESS_WITH_INFO:
      errstr = "SQL_SUCCESS_WITH_INFO";
      break;
    case SQL_INVALID_HANDLE:
      errstr = "SQL_INVALID_HANDLE";
      break;
    case SQL_NO_DATA:
      errstr = "SQL_NO_DATA";
      break;
    case SQL_NEED_DATA:
      errstr = "SQL_NEED_DATA";
      break;
    case SQL_STILL_EXECUTING:
      errstr = "SQL_STILL_EXECUTING";
      break;
    case SQL_ERROR:
      errstr = "SQL_ERROR";
      break;
    default:
      errstr = "SQL_UNKNOWN_ERR";
      break;
  }

  // maybe there're multiple errors, which we chain to a SQLException-list
  //### on linux crash this:
  _getallSQLdiags(errstr, file, line);
  DOUT("after _getallSQLdiags(errstr, file, line);");
#if ODBCVER < 0x0300
  _returnCodeValid = true;
  _returnCode = err;
#endif // ODBCVER
}



void
ODBCHandle::_getallSQLdiags(const char* errstr, char *file, int line)
{
  SQLRETURN resRet = 0;
  char resStr[SQL_MAX_MESSAGE_LENGTH + 1];
  memset(resStr, '\0', sizeof(resStr));
  SQLINTEGER resInt = 0;
  SQLSMALLINT diagLen = 0;
  bool ret = false;
  
#if ODBCVER >= 0x0300 && !defined(ACDK_OS_LINUX)
    // first all ODBC HeaderFields
  _numberRecordsValid = _getsingleSQLdiag(0, SQL_DIAG_NUMBER, &resInt, sizeof(resInt), diagLen, errstr);
  _numberRecords = (_numberRecordsValid == true) ? resInt : 0;

  _returnCodeValid = _getsingleSQLdiag(0, SQL_DIAG_RETURNCODE, &resRet, sizeof(resRet), diagLen, errstr);
  _returnCode = (_returnCodeValid == true)? resRet : 0;

  if (_htype == SQL_HANDLE_STMT) {
    _cursorRowCountValid = _getsingleSQLdiag(0, SQL_DIAG_CURSOR_ROW_COUNT, &resInt, sizeof(resInt), diagLen, errstr);
    _cursorRowCount = (_cursorRowCountValid == true)? resInt : 0;

    ret = _getsingleSQLdiag(0, SQL_DIAG_DYNAMIC_FUNCTION, resStr, sizeof(resStr), diagLen, errstr);
    if (diagLen < 0 || diagLen > (SQLSMALLINT)sizeof(resStr))
      ret = false;
    if (ret == true) {
      _dynamicFunction = ODBCSTR2STR(resStr, diagLen);
    } else { 
      _dynamicFunction = Nil;
    }

    _dynamicFunctionCodeValid = _getsingleSQLdiag(0, SQL_DIAG_DYNAMIC_FUNCTION_CODE, &resInt, sizeof(resInt), diagLen, errstr);
    _dynamicFunctionCode = (_dynamicFunctionCodeValid == true)? resInt : 0;

    _rowCountValid = _getsingleSQLdiag(0, SQL_DIAG_ROW_COUNT, &resInt, sizeof(resInt), diagLen, errstr);
    _rowCount = (_rowCountValid == true)? resInt : 0;
  } else {
    _cursorRowCount = 0;
    _cursorRowCountValid = false;
    _dynamicFunction = Nil;
    _dynamicFunctionCode = 0;
    _dynamicFunctionCodeValid = false;
    _rowCount = 0;
    _rowCountValid = false;
  }
  _statusRecords = Nil;
  if (_numberRecords == 0) 
  {
    StringBuffer sb;
    sb << "SQL-Error: " << (char*)errstr; // << " at " << (const char*)file << ":" << line;
    _addException(SBSTR("SQL-Error: " << errstr << " at " << file << ":" << line));
  }
  else
  {
    _statusRecords = new ODBCStatusRecordArray(_numberRecords);
    for (int n = 1; n <= _numberRecords; n++) 
    {
      RODBCStatusRecord sr = new ODBCStatusRecord();

      ret = _getsingleSQLdiag(n, SQL_DIAG_CLASS_ORIGIN, resStr, sizeof(resStr), diagLen, errstr);
      if (diagLen < 0 || diagLen > (SQLSMALLINT)sizeof(resStr))
        ret = false;
      sr->_classOrigin = (ret == true)? RString(ODBCSTR2STR(resStr, diagLen)) : RString(Nil);
      ret = _getsingleSQLdiag(n, SQL_DIAG_SUBCLASS_ORIGIN, resStr, sizeof(resStr), diagLen, errstr);
      if (diagLen < 0 || diagLen > (SQLSMALLINT)sizeof(resStr))
        ret = false;
      sr->_subClassOrigin = (ret == true)? RString(ODBCSTR2STR(resStr, diagLen)) : RString(Nil);
      ret = _getsingleSQLdiag(n, SQL_DIAG_CONNECTION_NAME, resStr, sizeof(resStr), diagLen, errstr);
      if (diagLen < 0 || diagLen > (SQLSMALLINT)sizeof(resStr))
        ret = false;
      sr->_connectionName = (ret == true)? RString(ODBCSTR2STR(resStr, diagLen)) : RString(Nil);
      ret = _getsingleSQLdiag(n, SQL_DIAG_MESSAGE_TEXT, resStr, sizeof(resStr), diagLen, errstr);
      if (diagLen < 0 || diagLen > (SQLSMALLINT)sizeof(resStr))
        ret = false;
      sr->_messageText = (ret == true)? RString(ODBCSTR2STR(resStr, diagLen)) : RString(Nil);
      sr->_nativeErrValid = _getsingleSQLdiag(n, SQL_DIAG_NATIVE, &resInt, sizeof(resInt), diagLen, errstr);
      sr->_nativeErr = (sr->_nativeErrValid == true)? resInt : 0;
      ret = _getsingleSQLdiag(n, SQL_DIAG_SQLSTATE, resStr, sizeof(resStr), diagLen, errstr);
      if (diagLen < 0 || diagLen > (SQLSMALLINT)sizeof(resStr))
        ret = false;
      sr->_sqlState = (ret == true)? RString(ODBCSTR2STR(resStr, diagLen)) : RString(Nil);
      if (_htype == SQL_HANDLE_STMT) {
        sr->_columnNumberValid = _getsingleSQLdiag(n, SQL_DIAG_COLUMN_NUMBER, &resInt, sizeof(resInt), diagLen, errstr);
        sr->_columnNumber = (sr->_columnNumberValid == true)? resInt : -1;
        sr->_rowNumberValid = _getsingleSQLdiag(n, SQL_DIAG_ROW_NUMBER, &resInt, sizeof(resInt), diagLen, errstr);
        sr->_rowNumber = (sr->_rowNumberValid == true)? resInt : -1;
      }
      _statusRecords->append(sr);
      DOUT("_statusRecords->append(sr);");

      if (true) // ((sr->_sqlState != Nil) && (sr->_sqlState->startsWith("00") == false)) {
      { 
        StringBuffer sb;
        RString unset("<unset>");
        sb.append("[SQLSTATE:");
        sb.append(sr->_sqlState);
        sb.append("][CLASS_ORIGIN:");
        sb.append((sr->_classOrigin != Nil)? sr->_classOrigin : unset);
        sb.append("][SUBCLASS_ORIGIN:");
        sb.append((sr->_subClassOrigin != Nil)? sr->_subClassOrigin : unset);
        sb.append("][CONNECTION_NAME:");
        sb.append((sr->_connectionName != Nil)? sr->_connectionName : unset);
        sb.append("][MESSAGE_TEXT:");
        sb.append((sr->_messageText != Nil)? sr->_messageText : unset);
        sb.append("][NATIVE_ERR:");
        sb.append((sr->_nativeErrValid == true)? ::acdk::lang::Integer::toString(sr->_nativeErr) : unset);
        if (_htype == SQL_HANDLE_STMT) {
          sb.append("][COLUMN_NUMBER:");
          if (sr->_columnNumberValid == true) {
            switch (sr->_columnNumber) {
              case SQL_NO_COLUMN_NUMBER:
                sb.append("<no_column>");
                break;
              case SQL_COLUMN_NUMBER_UNKNOWN:
                sb.append("<unknown_column>");
                break;
              default:
                sb.append(::acdk::lang::Integer::toString(sr->_columnNumber));
                break;
            }
          } else {
            sb.append(unset);
          }
          sb.append("][ROW_NUMBER:");
          if (sr->_rowNumberValid == true) {
            switch (sr->_rowNumber) {
              case SQL_NO_ROW_NUMBER:
                sb.append("<no_row>");
                break;
              case SQL_ROW_NUMBER_UNKNOWN:
                sb.append("<row_column>");
                break;
              default:
                sb.append(::acdk::lang::Integer::toString(sr->_rowNumber));
                break;
            }
          } else {
            sb.append("<unset>");
          }
        }
        DOUT("X1");
        sb.append("]");
        if (sr->_sqlState->startsWith("00") == true) 
        {
          /* just do nothing for simple status-messages */
#if defined(ACDK_ODBC_DEBUG)
          ::acdk::lang::System::err->println(RString("[") + file + ":" + Integer::toString(line) + "] INFO: " + sb.toString());
#endif
/FONT>
        } 
        else if (sr->_sqlState->startsWith("01") == true) 
        {
          _addWarning(RString("[") + file + ":" + Integer::toString(line) + "] WARN:");
          _addWarning(sb.toString());
        } 
        else 
        {
          _addException(RString("[") + file + ":" + Integer::toString(line) + "] ERR:");
          _addException(sb.toString());
        }
      }
      DOUT("X2");
    }
  }
#else // ODBCVER >= 0x0300
  DOUT("X4");
  _numberRecordsValid = true;
  _numberRecords = 1;

  _cursorRowCount = 0;
  _cursorRowCountValid = false;
  _dynamicFunction = Nil;
  _dynamicFunctionCode = 0;
  _dynamicFunctionCodeValid = false;
  _rowCount = 0;
  _rowCountValid = false;

  StringBuffer sb;
  RODBCStatusRecord sr = new ODBCStatusRecord();
  SQLINTEGER nativeerr;
  ODBC_NATIVE_CHAR state[6];
  DOUT("X5");
  switch (_htype) {
    case SQL_HANDLE_ENV:
      resRet = SQLError(_handle, SQL_NULL_HDBC, SQL_NULL_HSTMT, state, &nativeerr, (ODBC_NATIVE_CHAR *)resStr, sizeof(resStr), &diagLen);
      break;
    case SQL_HANDLE_DBC:
      resRet = SQLError(SQL_NULL_HDBC, _handle, SQL_NULL_HSTMT, state, &nativeerr, (ODBC_NATIVE_CHAR *)resStr, sizeof(resStr), &diagLen);
      break;
    case SQL_HANDLE_STMT:
      resRet = SQLError(SQL_NULL_HDBC, SQL_NULL_HSTMT, _handle, state, &nativeerr, (ODBC_NATIVE_CHAR *)resStr, sizeof(resStr), &diagLen);
      break;
    default:
      _addException("unknown handle-type");
      SQLTHROW(_excpt);
      break;
  }

  sr->_messageText = ODBCSTR2STR(resStr, diagLen);
  sr->_nativeErrValid = (nativeerr != 0)? true : false;
  sr->_columnNumberValid = false;
  sr->_columnNumber =  -1;
  sr->_rowNumberValid = false;
  sr->_rowNumber = -1;
  if (sr->_sqlState == Nil)
    sr->_sqlState = "";

  sb.append("[SQLSTATE:");
  sb.append(sr->_sqlState);
  sb.append("][MESSAGE_TEXT:");
  sb.append((sr->_messageText != Nil)? sr->_messageText : "<unset>");
  sb.append("][NATIVE_ERR:");
  sb.append((sr->_nativeErrValid == true)? ::acdk::lang::Integer::toString(sr->_nativeErr) : RString("<unset>"));
  sb.append("]");
  
  if (sr->_sqlState->startsWith("00") == true) {
    /* just do nothing for simple status-messages */
#if defined(ACDK_ODBC_EDBUG)
    ::acdk::lang::System::err->println(RString("[") + file + ":" + Integer::toString(line) + "] INFO: ") + sb.toString());
#endif
/FONT>
  } else if (sr->_sqlState->startsWith("01") == true) {
    _addWarning(RString("[") + file + ":" + Integer::toString(line) + "] WARN:");
    _addWarning(sb.toString());
  } else {
    _addException(RString("[") + file + ":" + Integer::toString(line) + "] ERR:");
    _addException(sb.toString());
  }
  _statusRecords = new ODBCStatusRecordArray(_numberRecords);
  _statusRecords->append(sr);
#endif // ODBCVER
  DOUT("Xa");
}

bool
ODBCHandle::_getsingleSQLdiag(SQLSMALLINT diagRec, SQLSMALLINT diagID, SQLPOINTER diagPtr, SQLSMALLINT diagSize, SQLSMALLINT& diagLen, const char* errstr)
{

#if ODBCVER >= 0x0300
  SQLRETURN err;
  StringBuffer errmsg;
  RString diagName;

  /*
   * SQLRETURN SQLGetDiagRec(SQLSMALLINT HandleType,SQLHANDLE Handle,SQLSMALLINT RecNumber,SQLCHAR *Sqlstate,
   *                         SQLINTEGER *NativeErrorPtr,SQLCHAR *MessageText,SQLSMALLINT BufferLength,
   *                         SQLSMALLINT *TextLengthPtr);
   */
  /*
   * SQLRETURN SQLGetDiagField(SQLSMALLINT HandleType,SQLHANDLE Handle,SQLSMALLINT RecNumber,
   *                           SQLSMALLINT DiagIdentifier,SQLPOINTER DiagInfoPtr,SQLSMALLINT BufferLength,
   *                           SQLSMALLINT *StringLengthPtr);
   */
  err = SQLGetDiagField(_htype, _handle, diagRec, diagID, diagPtr, diagSize, &diagLen);
  DOUT("SQLGetDiagField1: rec=" << diagRec << "; diagId=" << diagID << "; err=" << err);

  if (err != SQL_SUCCESS) {

#define MKDIAGSTR(__id) \
  case SQL_DIAG_ ## __id: \
    diagName = new String(#__id); \
    break;

    switch (diagID) {
      MKDIAGSTR(NUMBER)
      MKDIAGSTR(RETURNCODE)
      MKDIAGSTR(CURSOR_ROW_COUNT)
      MKDIAGSTR(DYNAMIC_FUNCTION)
      MKDIAGSTR(DYNAMIC_FUNCTION_CODE)
      MKDIAGSTR(ROW_COUNT)
      MKDIAGSTR(CLASS_ORIGIN)
      MKDIAGSTR(SUBCLASS_ORIGIN)
      MKDIAGSTR(CONNECTION_NAME)
      MKDIAGSTR(MESSAGE_TEXT)
      MKDIAGSTR(NATIVE)
      MKDIAGSTR(SQLSTATE)
      MKDIAGSTR(COLUMN_NUMBER)
      MKDIAGSTR(ROW_NUMBER)
      default:
        diagName = Nil;
        break;
    }
#undef MKDIAGSTR
  }

  switch (err) 
  {
    case SQL_SUCCESS: // nothing went wrong this time.
      return true;
      break;
    case SQL_SUCCESS_WITH_INFO: // message-buffer is too small, need at least msglen bytes
      errmsg.append("double err: message buffer too small (");
      errmsg.append(::acdk::lang::String::valueOf(diagSize));
      errmsg.append(" instead of ");
      errmsg.append(::acdk::lang::String::valueOf(diagLen));
      errmsg.append(") after ");
      errmsg.append(errstr);
      _addWarning(errmsg.toString());
      return true;
      /* not reached */
    case SQL_INVALID_HANDLE:  // maybe the caller have forgotten to call SQLAllocHandle()
      errmsg.append("double err: invalid handle after ");
      errmsg.append(errstr);
      _addException(errmsg.toString());
      return false;
      /* not reached */
    case SQL_ERROR: // either (RecNumber <= 0) or (BufferLength < 0)
      errmsg.append("double err: invalid BufferLength (");
      errmsg.append(Integer::toString(diagSize));
      errmsg.append(") or RecNumber (");
      errmsg.append(Integer::toString(diagRec));
      errmsg.append(") for DiagIdentifier ");
      if (diagName != Nil) {
        errmsg.append("\"");
        errmsg.append(diagName);
        errmsg.append("\" ");
      }
      errmsg.append("(");
      errmsg.append(Integer::toString(diagID));
      errmsg.append(") after ");
      errmsg.append(errstr);
      _addException(errmsg.toString());
      return false;
      /* not reached */
    case SQL_NO_DATA:/*
      errmsg.append("no further information available for RecNumber (");
      errmsg.append(Integer::toString(diagRec));
      errmsg.append(") for DiagIdentifier ");
      if (diagName != Nil) {
        errmsg.append("\"");
        errmsg.append(diagName);
        errmsg.append("\" ");
      }
      errmsg.append("(");
      errmsg.append(Integer::toString(diagID));
      errmsg.append(") after ");
      errmsg.append(errstr);
      _addException(errmsg.toString());*/
      return false;
      /* not reached */
      break;
    default:
    {

      errmsg.append("double err: unhandled SQL error ");
      errmsg.append(::acdk::lang::String::valueOf(err));
      errmsg.append(" for RecNumber (");
      errmsg.append(Integer::toString(diagRec));
      errmsg.append(") for DiagIdentifier ");
      if (diagName != Nil) {
        errmsg.append("\"");
        errmsg.append(diagName);
        errmsg.append("\" ");
      }
      errmsg.append("(");
      errmsg.append(Integer::toString(diagID));
      errmsg.append(") after ");
      errmsg.append(errstr);
      _addException(errmsg.toString());
      return false;
      /* not reached */
    }
  }
#else
/FONT>
  return false;
#endif // ODBCVER
}

int
ODBCHandle::_chkSQLrcode(SQLRETURN err, char *func, char *file, int line)
{
  ACDK_NLOG("acdk.sql.odbc", Debug, SBSTR("Called " << func << "(" << (int)_handle << ") with rc=" << err << " on " << file << ":" << line));
  RString msg = Nil;
  switch (err) {
    case SQL_SUCCESS: // all went ok.
      //_getSQLdiag(err, file, line);
      break;
    case SQL_NO_DATA:
      if (RString(func)->compareTo("SQLFetch") == 0) {
        _getSQLdiag(err, file, line);
        break;
      }
      // else fall through
    case SQL_SUCCESS_WITH_INFO:
    case SQL_NEED_DATA:
    case SQL_STILL_EXECUTING:
    case SQL_ERROR:
      _getSQLdiag(err, file, line);
      SQLTHROW(_excpt); // may return, if getSQLdiag only found some warnings
      break;
    case SQL_INVALID_HANDLE:
      msg = RString("invalid handle for ") + func;
      _addException(RString("[") + file + ":" + Integer::toString(line) + "] ERR:");
      _addException(msg);
      SQLTHROW(_excpt);
      break;
    default:  // ooops!
      msg = RString("unknown error from ") + func + ": " + Integer::toString(err);
      _addException(RString("[") + file + ":" + Integer::toString(line) + "] ERR:");
      _addException(msg);
      SQLTHROW(_excpt);
      break;
  }
#if defined(ACDK_ODBC_DEBUG)
  if (_warng != Nil) {
    ::acdk::sql::RSQLWarning w = _warng;
    ::acdk::lang::System::err->println("following warnings have been collected:");
    do {
      ::acdk::lang::System::err->println(w->getMessage());
      w = w->getNextWarning();
    } while (w != Nil);
    _warng = Nil;
  }
#endif
/FONT>
  return err;
}

/* this function is a quick implementation and should be later a wrapper to _setSQLAttr(key, ptr, len) */

void
ODBCHandle::_setSQLFlag(SQLUINTEGER key, SQLUINTEGER val)
{
  SQLRETURN err;
  char *fnam;
  RString msg;
  
  /*
   * options can be set with:
   *
   * SQLRETURN SQLSetXXXXAttr(SQLHANDLE Handle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength);
   *
   * where namespace odbc {X can be one of Env, Connect, Stmt or Desc
   */

  switch (_htype) {
#if ODBCVER >= 0x0300
    case SQL_HANDLE_ENV:
      fnam = "SQLSetEnvAttr";
      err = ::SQLSetEnvAttr(_handle, key, &val, sizeof(val));
      break;
#endif // ODBCVER
    case SQL_HANDLE_DBC:
      fnam = "SQLSetConnectAttr";
#if ODBCVER >= 0x0300
      err = ::SQLSetConnectAttr(_handle, key, &val, sizeof(val));
#else // ODBCVER
      err = ::SQLSetConnectOption(_handle, key, val);
#endif // ODBCVER
      break;
    case SQL_HANDLE_STMT:
      fnam = "SQLSetStmtAttr";
#if ODBCVER >= 0x0300
      err = ::SQLSetStmtAttr(_handle, key, &val, sizeof(val));
#else // ODBCVER
      err = ::SQLSetStmtOption(_handle, key, val);
#endif // ODBCVER
      break;
#if ODBCVER >= 0x0300
    case SQL_HANDLE_DESC:
      fnam = "SQLSetDescAttr";
      //err = ::SQLSetDescAttr(_handle, key, &val, sizeof(val)); // not existant.
      _addException("no method to set Attributes for a descriptor handle");
      SQLTHROW(_excpt);
      break;
#endif // ODBCVER
    default:
      _addException("unknown handle-type");
      SQLTHROW(_excpt);
      break;
  }
  _chkSQLrcode(err, fnam, __FILE__, __LINE__);
}

} // odbc
} // sql
} // acdk