Raw File
TODBCStatement.cxx
// @(#)root/odbc:$Id$
// Author: Sergey Linev   6/02/2006

/*************************************************************************
 * Copyright (C) 1995-2006, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/


//________________________________________________________________________
//
//  SQL statement class for ODBC
//
//  See TSQLStatement class documentation for more details
//
//________________________________________________________________________


#include "TODBCStatement.h"
#include "TODBCServer.h"
#include "TDataType.h"
#include "strlcpy.h"
#include "snprintf.h"

#include <sqlext.h>
#include <cstdlib>
#include <iostream>

#define kSqlTime      123781
#define kSqlDate      123782
#define kSqlTimestamp 123783
#define kSqlBinary    123784


ClassImp(TODBCStatement);

////////////////////////////////////////////////////////////////////////////////
///constructor

TODBCStatement::TODBCStatement(SQLHSTMT stmt, Int_t rowarrsize, Bool_t errout) :
   TSQLStatement(errout)
{
   fHstmt = stmt;
   fBufferPreferredSize = rowarrsize;

   fBuffer = nullptr;
   fStatusBuffer = nullptr;
   fNumBuffers = 0;
   fBufferLength = 0;
   fBufferCounter = 0;

   fWorkingMode = 0;

   fNumParsProcessed = 0;
   fNumRowsFetched = 0;

   SQLSMALLINT   paramsCount = 0;
   SQLRETURN retcode = SQLNumParams(fHstmt, &paramsCount);
   if (ExtractErrors(retcode,"Constructor"))
      paramsCount = 0;

   if (paramsCount>0) {

      fWorkingMode = 1; // we are now using buffers for parameters
      fNumParsProcessed = 0;

      SQLSetStmtAttr(fHstmt, SQL_ATTR_PARAM_BIND_TYPE, SQL_PARAM_BIND_BY_COLUMN, 0);

      SQLUINTEGER setsize = fBufferPreferredSize;
      retcode = SQLSetStmtAttr(fHstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER) (long) setsize, 0);
      ExtractErrors(retcode,"Constructor");

      SQLUINTEGER getsize = 0;

      retcode = SQLGetStmtAttr(fHstmt, SQL_ATTR_PARAMSET_SIZE, &getsize, 0, nullptr);
      ExtractErrors(retcode,"Constructor");

      Int_t bufferlen = fBufferPreferredSize;

      // MySQL is not yet support array of parameters
      if (getsize<=1) bufferlen=1; else
      if (getsize!=setsize) {
         SQLSetStmtAttr(fHstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER) 1, 0);
         bufferlen = 1;
      }

      SetNumBuffers(paramsCount, bufferlen);

      SQLSetStmtAttr(fHstmt, SQL_ATTR_PARAM_STATUS_PTR, fStatusBuffer, 0);
      SQLSetStmtAttr(fHstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, &fNumParsProcessed, 0);

      // indicates that we are starting
      fBufferCounter = -1;
   }

   fNumRowsFetched = 0;
   fLastResultRow = 0;
}

////////////////////////////////////////////////////////////////////////////////
///destructor

TODBCStatement::~TODBCStatement()
{
   Close();
}

////////////////////////////////////////////////////////////////////////////////
/// Close statement

void TODBCStatement::Close(Option_t *)
{
   FreeBuffers();

   SQLFreeHandle(SQL_HANDLE_STMT, fHstmt);

   fHstmt = nullptr;
}

////////////////////////////////////////////////////////////////////////////////
/// process statement

Bool_t TODBCStatement::Process()
{
   ClearError();

   SQLRETURN retcode = SQL_SUCCESS;

   if (IsParSettMode()) {

      // check if we start filling buffers, but not complete it
      if (fBufferCounter>=0) {
         // if buffer used not fully, set smaller size of buffer arrays
         if ((fBufferCounter>0) && (fBufferCounter<fBufferLength-1)) {
            SQLUINTEGER setsize = fBufferCounter+1;
            SQLSetStmtAttr(fHstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER) (long) setsize, 0);
         }
         retcode = SQLExecute(fHstmt);
      }

      // after Process we finish working with parameters data,
      // if necessary, user can try to access resultset of statement
      fWorkingMode = 0;
      FreeBuffers();
      fBufferCounter = -1;
   } else {

      // just execute statement,
      // later one can try to access results of statement
      retcode = SQLExecute(fHstmt);
   }

   return !ExtractErrors(retcode, "Process");
}

////////////////////////////////////////////////////////////////////////////////
///get number of affected rows

Int_t TODBCStatement::GetNumAffectedRows()
{
   ClearError();

   SQLLEN    rowCount;
   SQLRETURN retcode = SQL_SUCCESS;

   retcode = SQLRowCount(fHstmt, &rowCount);

   if (ExtractErrors(retcode, "GetNumAffectedRows")) return -1;

   return rowCount;
}

////////////////////////////////////////////////////////////////////////////////
/// Store result of statement processing.
/// Results set, produced by processing of statement, can be stored, and accessed by
/// TODBCStamenet methoods like NextResultRow(), GetInt(), GetLong() and so on.

Bool_t TODBCStatement::StoreResult()
{
   ClearError();

   if (IsParSettMode()) {
      SetError(-1,"Call Process() method before","StoreResult");
      return kFALSE;
   }

   FreeBuffers();

   SQLSMALLINT columnCount = 0;

   SQLRETURN retcode = SQLNumResultCols(fHstmt, &columnCount);
   if (ExtractErrors(retcode, "StoreResult")) return kFALSE;

   if (columnCount==0) return kFALSE;

   SetNumBuffers(columnCount, fBufferPreferredSize);

   SQLULEN arrsize = fBufferLength;

   SQLSetStmtAttr(fHstmt, SQL_ATTR_ROW_BIND_TYPE, SQL_BIND_BY_COLUMN, 0);
   SQLSetStmtAttr(fHstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER) arrsize, 0);
   SQLSetStmtAttr(fHstmt, SQL_ATTR_ROW_STATUS_PTR, fStatusBuffer, 0);
   SQLSetStmtAttr(fHstmt, SQL_ATTR_ROWS_FETCHED_PTR, &fNumRowsFetched, 0);

   for (int n=0;n<fNumBuffers;n++) {
      SQLCHAR     columnName[1024];
      SQLSMALLINT nameLength;
      SQLSMALLINT dataType;
      SQLULEN     columnSize;
      SQLSMALLINT decimalDigits;
      SQLSMALLINT nullable;

      retcode = SQLDescribeCol(fHstmt, n+1, columnName, 1024,
                               &nameLength, &dataType,
                               &columnSize, &decimalDigits, &nullable);

      BindColumn(n, dataType, columnSize);

      if (nameLength>0) {
         fBuffer[n].fBnamebuffer = new char[nameLength+1];
         strlcpy(fBuffer[n].fBnamebuffer, (const char*) columnName, nameLength+1);
      }
   }

   fNumRowsFetched = 0;
   fLastResultRow = 0;

   fWorkingMode = 2;

   return kTRUE;
}

////////////////////////////////////////////////////////////////////////////////
///return number of fields

Int_t TODBCStatement::GetNumFields()
{
   return IsResultSet() ? fNumBuffers : -1;
}

////////////////////////////////////////////////////////////////////////////////
///return field name

const char* TODBCStatement::GetFieldName(Int_t nfield)
{
   ClearError();

   if (!IsResultSet() || (nfield<0) || (nfield>=fNumBuffers)) return nullptr;

   return fBuffer[nfield].fBnamebuffer;
}


////////////////////////////////////////////////////////////////////////////////
///next result row

Bool_t TODBCStatement::NextResultRow()
{
   ClearError();

   if (!IsResultSet()) return kFALSE;

   if ((fNumRowsFetched==0) ||
       (1.*fBufferCounter >= 1.*(fNumRowsFetched-1))) {

      fBufferCounter = 0;
      fNumRowsFetched = 0;

      SQLRETURN retcode = SQLFetchScroll(fHstmt, SQL_FETCH_NEXT, 0);
      if (retcode==SQL_NO_DATA) fNumRowsFetched=0; else
         ExtractErrors(retcode,"NextResultRow");

      // this is workaround of Oracle Linux ODBC driver
      // it does not returns number of fetched lines, therefore one should
      // calculate it from current row number
      if (!IsError() && (retcode!=SQL_NO_DATA) && (fNumRowsFetched==0)) {
         SQLULEN rownumber = 0;
         SQLRETURN retcode2 = SQLGetStmtAttr(fHstmt, SQL_ATTR_ROW_NUMBER, &rownumber, 0, nullptr);
         ExtractErrors(retcode2, "NextResultRow");

         if (!IsError()) {
            fNumRowsFetched = rownumber - fLastResultRow;
            fLastResultRow = rownumber;
         }
      }

      if (1.*fNumRowsFetched>fBufferLength)
         SetError(-1, "Missmatch between buffer length and fetched rows number", "NextResultRow");

      if (IsError() || (fNumRowsFetched==0)) {
         fWorkingMode = 0;
         FreeBuffers();
      }

   } else
      fBufferCounter++;

   return IsResultSet();
}

////////////////////////////////////////////////////////////////////////////////
/// Extract errors, produced by last ODBC function call

Bool_t TODBCStatement::ExtractErrors(SQLRETURN retcode, const char* method)
{
   if ((retcode== SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO)) return kFALSE;

   SQLINTEGER i = 0;
   SQLINTEGER native;
   SQLCHAR state[ 7 ];
   SQLCHAR text[256];
   SQLSMALLINT len;
   SQLRETURN ret;
   do {
      ret = SQLGetDiagRec(SQL_HANDLE_STMT, fHstmt, ++i, state, &native, text,
                          sizeof(text), &len );
      if (ret == SQL_SUCCESS) SetError(native, (const char*) text, method);
//         Error(method, "%s:%ld:%ld:%s\n", state, i, native, text);
   }
   while( ret == SQL_SUCCESS );
   return kTRUE;
}

////////////////////////////////////////////////////////////////////////////////
///run next iteration

Bool_t TODBCStatement::NextIteration()
{
   ClearError();

   if (!IsParSettMode() || !fBuffer || (fBufferLength <= 0)) return kFALSE;

   if (fBufferCounter >= fBufferLength-1) {
      SQLRETURN retcode = SQLExecute(fHstmt);
      if (ExtractErrors(retcode,"NextIteration")) return kFALSE;
      fBufferCounter = 0;
   } else
      fBufferCounter++;

   // probably, we do not need it, but anyway
   fStatusBuffer[fBufferCounter] = SQL_ROW_SUCCESS;

   return kTRUE;
}

////////////////////////////////////////////////////////////////////////////////
///return number of parameters

Int_t TODBCStatement::GetNumParameters()
{
   return IsParSettMode() ? fNumBuffers : 0;
}

////////////////////////////////////////////////////////////////////////////////
///set number of buffers

void TODBCStatement::SetNumBuffers(Int_t isize, Int_t ilen)
{
   FreeBuffers();

   fNumBuffers = isize;
   fBufferLength = ilen;
   fBufferCounter = 0;

   fBuffer = new ODBCBufferRec_t[fNumBuffers];
   for (Int_t n=0;n<fNumBuffers;n++) {
      fBuffer[n].fBroottype = 0;
      fBuffer[n].fBsqltype = 0;
      fBuffer[n].fBsqlctype = 0;
      fBuffer[n].fBbuffer = nullptr;
      fBuffer[n].fBelementsize = 0;
      fBuffer[n].fBlenarray = nullptr;
      fBuffer[n].fBstrbuffer = nullptr;
      fBuffer[n].fBnamebuffer = nullptr;
   }

   fStatusBuffer = new SQLUSMALLINT[fBufferLength];
}

////////////////////////////////////////////////////////////////////////////////
/// Free allocated buffers

void TODBCStatement::FreeBuffers()
{
   if (!fBuffer) return;
   for (Int_t n=0;n<fNumBuffers;n++) {
      if (fBuffer[n].fBbuffer)
        free(fBuffer[n].fBbuffer);
      delete[] fBuffer[n].fBlenarray;
      delete[] fBuffer[n].fBstrbuffer;
      delete[] fBuffer[n].fBnamebuffer;
   }

   delete[] fStatusBuffer;
   delete[] fBuffer;
   fBuffer = nullptr;
   fNumBuffers = 0;
   fBufferLength = 0;
   fStatusBuffer = nullptr;
}

////////////////////////////////////////////////////////////////////////////////
/// Bind result column to buffer. Allocate buffer of appropriate type

Bool_t TODBCStatement::BindColumn(Int_t ncol, SQLSMALLINT sqltype, SQLUINTEGER size)
{
   ClearError();

   if ((ncol<0) || (ncol>=fNumBuffers)) {
      SetError(-1,"Internal error. Column number invalid","BindColumn");
      return kFALSE;
   }

   if (fBuffer[ncol].fBsqltype!=0) {
      SetError(-1,"Internal error. Bind for column already done","BindColumn");
      return kFALSE;
   }

   SQLSMALLINT sqlctype = 0;
   switch (sqltype) {
      case SQL_CHAR:
      case SQL_VARCHAR:   sqlctype = SQL_C_CHAR; break;
      case SQL_BINARY:
      case SQL_LONGVARBINARY:
      case SQL_VARBINARY: sqlctype = SQL_C_BINARY; break;
      case SQL_LONGVARCHAR: Info("BindColumn","BIG VARCHAR not supported yet"); return kFALSE; break;

      case SQL_DECIMAL:   sqlctype = SQL_C_DOUBLE; break;
      case SQL_NUMERIC:   sqlctype = SQL_C_DOUBLE; break;
      case SQL_SMALLINT:  sqlctype = SQL_C_SLONG; break;
      case SQL_INTEGER:   sqlctype = SQL_C_SLONG; break;
      case SQL_FLOAT:     sqlctype = SQL_C_FLOAT; break;
      case SQL_REAL:
      case SQL_DOUBLE:    sqlctype = SQL_C_DOUBLE; break;
      case SQL_TINYINT:   sqlctype = SQL_C_STINYINT; break;
      case SQL_BIGINT:    sqlctype = SQL_C_SBIGINT; break;
      case SQL_TYPE_DATE: sqlctype = SQL_C_TYPE_DATE; break;
      case SQL_TYPE_TIME: sqlctype = SQL_C_TYPE_TIME; break;
      case SQL_TYPE_TIMESTAMP: sqlctype = SQL_C_TYPE_TIMESTAMP; break;
      default: {
         SetError(-1, Form("SQL type %d not supported",sqltype), "BindColumn");
         return kFALSE;
      }
   }

   int elemsize = 0;

   switch (sqlctype) {
      case SQL_C_ULONG:    elemsize = sizeof(SQLUINTEGER); break;
      case SQL_C_SLONG:    elemsize = sizeof(SQLINTEGER); break;
      case SQL_C_UBIGINT:  elemsize = sizeof(ULong64_t); break; // should be SQLUBIGINT, but it is 64-bit structure on some platforms
      case SQL_C_SBIGINT:  elemsize = sizeof(Long64_t); break; // should be SQLBIGINT, but it is 64-bit structure on some platforms
      case SQL_C_USHORT:   elemsize = sizeof(SQLUSMALLINT); break;
      case SQL_C_SSHORT:   elemsize = sizeof(SQLSMALLINT); break;
      case SQL_C_UTINYINT: elemsize = sizeof(SQLCHAR); break;
      case SQL_C_STINYINT: elemsize = sizeof(SQLSCHAR); break;
      case SQL_C_FLOAT:    elemsize = sizeof(SQLREAL); break;
      case SQL_C_DOUBLE:   elemsize = sizeof(SQLDOUBLE); break;
      case SQL_C_CHAR:     elemsize = size; break;
      case SQL_C_BINARY:   elemsize = size; break;
      case SQL_C_TYPE_DATE: elemsize = sizeof(DATE_STRUCT); break;
      case SQL_C_TYPE_TIME: elemsize = sizeof(TIME_STRUCT); break;
      case SQL_C_TYPE_TIMESTAMP: elemsize = sizeof(TIMESTAMP_STRUCT); break;

      default: {
         SetError(-1, Form("SQL C Type %d is not supported",sqlctype), "BindColumn");
         return kFALSE;
      }
   }

   fBuffer[ncol].fBroottype    = 0;
   fBuffer[ncol].fBsqltype     = sqltype;
   fBuffer[ncol].fBsqlctype    = sqlctype;
   fBuffer[ncol].fBbuffer      = malloc(elemsize * fBufferLength);
   fBuffer[ncol].fBelementsize = elemsize;
   fBuffer[ncol].fBlenarray    = new SQLLEN[fBufferLength];

   SQLRETURN retcode =
      SQLBindCol(fHstmt, ncol+1, sqlctype, fBuffer[ncol].fBbuffer,
                 elemsize,
                 fBuffer[ncol].fBlenarray);

   return !ExtractErrors(retcode, "BindColumn");
}

////////////////////////////////////////////////////////////////////////////////
/// Bind query parameter with buffer. Creates buffer of appropriate type

Bool_t TODBCStatement::BindParam(Int_t npar, Int_t roottype, Int_t size)
{
   ClearError();

   if ((npar<0) || (npar>=fNumBuffers)) return kFALSE;

   if (fBuffer[npar].fBroottype!=0) {
      SetError(-1,Form("ParameterType for par %d already specified", npar),"BindParam");
      return kFALSE;
   }

   SQLSMALLINT sqltype = 0, sqlctype = 0;
   int elemsize = 0;

   switch (roottype) {
      case kUInt_t:     sqltype = SQL_INTEGER; sqlctype = SQL_C_ULONG;    elemsize = sizeof(SQLUINTEGER); break;
      case kInt_t:      sqltype = SQL_INTEGER; sqlctype = SQL_C_SLONG;    elemsize = sizeof(SQLINTEGER); break;
      case kULong_t:    sqltype = SQL_INTEGER; sqlctype = SQL_C_ULONG;    elemsize = sizeof(SQLUINTEGER); break;
      case kLong_t:     sqltype = SQL_INTEGER; sqlctype = SQL_C_SLONG;    elemsize = sizeof(SQLINTEGER); break;

      // here SQLUBIGINT/SQLBIGINT types should be used,
       // but on 32-bit platforms it is structures, which makes its usage inconvinient
      case kULong64_t:  sqltype = SQL_BIGINT;  sqlctype = SQL_C_UBIGINT;  elemsize = sizeof(ULong64_t); break;
      case kLong64_t:   sqltype = SQL_BIGINT;  sqlctype = SQL_C_SBIGINT;  elemsize = sizeof(Long64_t); break;

      case kUShort_t:   sqltype = SQL_SMALLINT;sqlctype = SQL_C_USHORT;   elemsize = sizeof(SQLUSMALLINT); break;
      case kShort_t:    sqltype = SQL_SMALLINT;sqlctype = SQL_C_SSHORT;   elemsize = sizeof(SQLSMALLINT); break;
      case kUChar_t:    sqltype = SQL_TINYINT; sqlctype = SQL_C_UTINYINT; elemsize = sizeof(SQLCHAR); break;
      case kChar_t:     sqltype = SQL_TINYINT; sqlctype = SQL_C_STINYINT; elemsize = sizeof(SQLSCHAR); break;
      case kBool_t:     sqltype = SQL_TINYINT; sqlctype = SQL_C_UTINYINT; elemsize = sizeof(SQLCHAR); break;
      case kFloat_t:    sqltype = SQL_FLOAT;   sqlctype = SQL_C_FLOAT;    elemsize = sizeof(SQLREAL); break;
      case kFloat16_t:  sqltype = SQL_FLOAT;   sqlctype = SQL_C_FLOAT;    elemsize = sizeof(SQLREAL); break;
      case kDouble_t:   sqltype = SQL_DOUBLE;  sqlctype = SQL_C_DOUBLE;   elemsize = sizeof(SQLDOUBLE); break;
      case kDouble32_t: sqltype = SQL_DOUBLE;  sqlctype = SQL_C_DOUBLE;   elemsize = sizeof(SQLDOUBLE); break;
      case kCharStar:   sqltype = SQL_CHAR;    sqlctype = SQL_C_CHAR;     elemsize = size; break;
      case kSqlBinary:  sqltype = SQL_BINARY;  sqlctype = SQL_C_BINARY;   elemsize = size; break;
      case kSqlDate:    sqltype = SQL_TYPE_DATE; sqlctype = SQL_C_TYPE_DATE; elemsize = sizeof(DATE_STRUCT); break;
      case kSqlTime:    sqltype = SQL_TYPE_TIME; sqlctype = SQL_C_TYPE_TIME; elemsize = sizeof(TIME_STRUCT); break;
      case kSqlTimestamp: sqltype = SQL_TYPE_TIMESTAMP; sqlctype = SQL_C_TYPE_TIMESTAMP; elemsize = sizeof(TIMESTAMP_STRUCT); break;
      default: {
         SetError(-1, Form("Root type %d is not supported", roottype), "BindParam");
         return kFALSE;
      }
   }

   void* buffer = malloc(elemsize * fBufferLength);
   SQLLEN* lenarray = new SQLLEN[fBufferLength];
   SQLRETURN retcode =
      SQLBindParameter(fHstmt, npar+1, SQL_PARAM_INPUT,
                       sqlctype, sqltype, 0, 0,
                       buffer, elemsize, lenarray);

   if (ExtractErrors(retcode, "BindParam")) {
      free(buffer);
      delete[] lenarray;
      return kFALSE;
   }

   fBuffer[npar].fBroottype = roottype;
   fBuffer[npar].fBsqlctype = sqlctype;
   fBuffer[npar].fBsqltype = sqltype;
   fBuffer[npar].fBbuffer = buffer;
   fBuffer[npar].fBelementsize = elemsize;
   fBuffer[npar].fBlenarray = lenarray;

   return kTRUE;
}

////////////////////////////////////////////////////////////////////////////////
/// Get parameter address

void* TODBCStatement::GetParAddr(Int_t npar, Int_t roottype, Int_t length)
{
   ClearError();

   if (!fBuffer || (npar < 0) || (npar >= fNumBuffers) || (fBufferCounter < 0)) {
      SetError(-1, "Invalid parameter number","GetParAddr");
      return nullptr;
   }

   if (!fBuffer[npar].fBbuffer) {
      if (IsParSettMode() && (roottype != 0) && (fBufferCounter == 0))
         if (!BindParam(npar, roottype, length)) return nullptr;

      if (!fBuffer[npar].fBbuffer) return nullptr;
   }

   if (roottype!=0)
      if (fBuffer[npar].fBroottype!=roottype) return nullptr;

   return (char*)fBuffer[npar].fBbuffer + fBufferCounter*fBuffer[npar].fBelementsize;
}

////////////////////////////////////////////////////////////////////////////////
///convert to numeric type

long double TODBCStatement::ConvertToNumeric(Int_t npar)
{
   void* addr = GetParAddr(npar);
   if (!addr) return 0;

   switch (fBuffer[npar].fBsqlctype) {
      case SQL_C_ULONG:    return *((SQLUINTEGER*) addr); break;
      case SQL_C_SLONG:    return *((SQLINTEGER*) addr); break;
      case SQL_C_UBIGINT:  return *((ULong64_t*) addr); break;
      case SQL_C_SBIGINT:  return *((Long64_t*) addr); break;
      case SQL_C_USHORT:   return *((SQLUSMALLINT*) addr); break;
      case SQL_C_SSHORT:   return *((SQLSMALLINT*) addr); break;
      case SQL_C_UTINYINT: return *((SQLCHAR*) addr); break;
      case SQL_C_STINYINT: return *((SQLSCHAR*) addr); break;
      case SQL_C_FLOAT:    return *((SQLREAL*) addr); break;
      case SQL_C_DOUBLE:   return *((SQLDOUBLE*) addr); break;
      case SQL_C_TYPE_DATE: {
         DATE_STRUCT* dt = (DATE_STRUCT*) addr;
         TDatime rtm(dt->year, dt->month,  dt->day, 0, 0, 0);
         return rtm.GetDate();
         break;
      }
      case SQL_C_TYPE_TIME: {
         TIME_STRUCT* tm = (TIME_STRUCT*) addr;
         TDatime rtm(2000, 1, 1, tm->hour, tm->minute, tm->second);
         return rtm.GetTime();
         break;
      }
      case SQL_C_TYPE_TIMESTAMP: {
         TIMESTAMP_STRUCT* tm = (TIMESTAMP_STRUCT*) addr;
         TDatime rtm(tm->year, tm->month,  tm->day,
                     tm->hour, tm->minute, tm->second);
         return rtm.Get();
         break;
      }
   }
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
///convert to string

const char* TODBCStatement::ConvertToString(Int_t npar)
{
   void* addr = GetParAddr(npar);
   if (!addr) return nullptr;
   if (!fBuffer[npar].fBstrbuffer)
      fBuffer[npar].fBstrbuffer = new char[100];

   char* buf = fBuffer[npar].fBstrbuffer;

   switch(fBuffer[npar].fBsqlctype) {
#if (SIZEOF_LONG == 8)
      case SQL_C_SLONG:   snprintf(buf, 100, "%d", *((SQLINTEGER*) addr)); break;
      case SQL_C_ULONG:   snprintf(buf, 100, "%u", *((SQLUINTEGER*) addr)); break;
#else
      case SQL_C_SLONG:   snprintf(buf, 100, "%ld", (long)*((SQLINTEGER*) addr)); break;
      case SQL_C_ULONG:   snprintf(buf, 100, "%lu", (unsigned long)*((SQLUINTEGER*) addr)); break;
#endif
      case SQL_C_SBIGINT: snprintf(buf, 100, "%lld", *((Long64_t*) addr)); break;
      case SQL_C_UBIGINT: snprintf(buf, 100, "%llu", *((ULong64_t*) addr)); break;
      case SQL_C_SSHORT:  snprintf(buf, 100, "%hd", *((SQLSMALLINT*) addr)); break;
      case SQL_C_USHORT:  snprintf(buf, 100, "%hu", *((SQLUSMALLINT*) addr)); break;
      case SQL_C_STINYINT:snprintf(buf, 100, "%d", *((SQLSCHAR*) addr)); break;
      case SQL_C_UTINYINT:snprintf(buf, 100, "%u", *((SQLCHAR*) addr)); break;
      case SQL_C_FLOAT:   snprintf(buf, 100, TSQLServer::GetFloatFormat(), *((SQLREAL*) addr)); break;
      case SQL_C_DOUBLE:  snprintf(buf, 100, TSQLServer::GetFloatFormat(), *((SQLDOUBLE*) addr)); break;
      case SQL_C_TYPE_DATE: {
         DATE_STRUCT* dt = (DATE_STRUCT*) addr;
         snprintf(buf,100,"%4.4d-%2.2d-%2.2d",
                  dt->year, dt->month,  dt->day);
         break;
      }
      case SQL_C_TYPE_TIME: {
         TIME_STRUCT* tm = (TIME_STRUCT*) addr;
         snprintf(buf,100,"%2.2d:%2.2d:%2.2d",
                  tm->hour, tm->minute, tm->second);
         break;
      }
      case SQL_C_TYPE_TIMESTAMP: {
         TIMESTAMP_STRUCT* tm = (TIMESTAMP_STRUCT*) addr;
         snprintf(buf,100,"%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d",
                  tm->year, tm->month,  tm->day,
                  tm->hour, tm->minute, tm->second);
         break;
      }
      default: return nullptr;
   }

   return buf;
}

////////////////////////////////////////////////////////////////////////////////
/// Verifies if field value is NULL

Bool_t TODBCStatement::IsNull(Int_t npar)
{
   void* addr = GetParAddr(npar);
   if (!addr) return kTRUE;

   return fBuffer[npar].fBlenarray[fBufferCounter] == SQL_NULL_DATA;
}

////////////////////////////////////////////////////////////////////////////////
///get parameter as integer

Int_t TODBCStatement::GetInt(Int_t npar)
{
   void* addr = GetParAddr(npar);
   if (!addr) return 0;

   if (fBuffer[npar].fBsqlctype==SQL_C_SLONG)
      return (Int_t) *((SQLINTEGER*) addr);

   return (Int_t) ConvertToNumeric(npar);
}

////////////////////////////////////////////////////////////////////////////////
///get parameter as unsigned integer

UInt_t TODBCStatement::GetUInt(Int_t npar)
{
   void* addr = GetParAddr(npar);
   if (!addr) return 0;

   if (fBuffer[npar].fBsqlctype==SQL_C_ULONG)
      return (UInt_t) *((SQLUINTEGER*) addr);

   return (UInt_t) ConvertToNumeric(npar);
}

////////////////////////////////////////////////////////////////////////////////
///get parameter as Long_t

Long_t TODBCStatement::GetLong(Int_t npar)
{
   void* addr = GetParAddr(npar);
   if (!addr) return 0;

   if (fBuffer[npar].fBsqlctype==SQL_C_SLONG)
     return (Long_t) *((SQLINTEGER*) addr);

   return (Long_t) ConvertToNumeric(npar);
}

////////////////////////////////////////////////////////////////////////////////
///get parameter as Long64_t

Long64_t TODBCStatement::GetLong64(Int_t npar)
{
   void* addr = GetParAddr(npar);
   if (!addr) return 0;

   if (fBuffer[npar].fBsqlctype==SQL_C_SBIGINT)
     return *((Long64_t*) addr);

   return (Long64_t) ConvertToNumeric(npar);
}

////////////////////////////////////////////////////////////////////////////////
///get parameter as ULong64_t

ULong64_t TODBCStatement::GetULong64(Int_t npar)
{
   void* addr = GetParAddr(npar);
   if (!addr) return 0;

   if (fBuffer[npar].fBsqlctype==SQL_C_UBIGINT)
     return *((ULong64_t*) addr);

   return (ULong64_t) ConvertToNumeric(npar);
}

////////////////////////////////////////////////////////////////////////////////
///get parameter as Double_t

Double_t TODBCStatement::GetDouble(Int_t npar)
{
   void* addr = GetParAddr(npar);
   if (!addr) return 0;

   if (fBuffer[npar].fBsqlctype==SQL_C_DOUBLE)
     return *((SQLDOUBLE*) addr);

   return (Double_t) ConvertToNumeric(npar);
}

////////////////////////////////////////////////////////////////////////////////
///get parameter as string

const char* TODBCStatement::GetString(Int_t npar)
{
   void* addr = GetParAddr(npar);
   if (!addr) return nullptr;

   if (fBuffer[npar].fBsqlctype==SQL_C_CHAR) {
      // first check if string is null

      int len = fBuffer[npar].fBlenarray[fBufferCounter];

      if ((len == SQL_NULL_DATA) || (len==0)) return nullptr;

      char* res = (char*) addr;
      if (len < fBuffer[npar].fBelementsize) {
         *(res + len) = 0;
         return res;
      }

      if (len > fBuffer[npar].fBelementsize) {
         SetError(-1, Form("Problems with string size %d", len), "GetString");
         return nullptr;
      }

      if (!fBuffer[npar].fBstrbuffer)
         fBuffer[npar].fBstrbuffer = new char[len+1];

      strlcpy(fBuffer[npar].fBstrbuffer, res, len+1);

      res = fBuffer[npar].fBstrbuffer;
      *(res + len) = 0;
      return res;
   }

   return ConvertToString(npar);
}

////////////////////////////////////////////////////////////////////////////////
/// return parameter as binary data

Bool_t TODBCStatement::GetBinary(Int_t npar, void* &mem, Long_t& size)
{
   mem = nullptr;
   size = 0;

   void* addr = GetParAddr(npar);
   if (!addr) return kFALSE;

   if ((fBuffer[npar].fBsqlctype==SQL_C_BINARY) ||
       (fBuffer[npar].fBsqlctype==SQL_C_CHAR)) {

      // first check if data length is null
      int len = fBuffer[npar].fBlenarray[fBufferCounter];

      if ((len == SQL_NULL_DATA) || (len==0)) return kTRUE;

      size = len;

      if (!fBuffer[npar].fBstrbuffer)
         fBuffer[npar].fBstrbuffer = new char[size];

      memcpy(fBuffer[npar].fBstrbuffer, addr, size);

      mem = fBuffer[npar].fBstrbuffer;

      return kTRUE;
   }

   return kFALSE;
}


////////////////////////////////////////////////////////////////////////////////
/// return field value as date

Bool_t TODBCStatement::GetDate(Int_t npar, Int_t& year, Int_t& month, Int_t& day)
{
   void* addr = GetParAddr(npar);
   if (!addr) return kFALSE;

   if (fBuffer[npar].fBsqlctype!=SQL_C_TYPE_DATE) return kFALSE;

   DATE_STRUCT* dt = (DATE_STRUCT*) addr;
   year = dt->year;
   month = dt->month;
   day = dt->day;

   return kTRUE;
}

////////////////////////////////////////////////////////////////////////////////
/// return field value as time

Bool_t TODBCStatement::GetTime(Int_t npar, Int_t& hour, Int_t& min, Int_t& sec)
{
   void* addr = GetParAddr(npar);
   if (!addr) return kFALSE;

   if (fBuffer[npar].fBsqlctype!=SQL_C_TYPE_TIME) return kFALSE;

   TIME_STRUCT* tm = (TIME_STRUCT*) addr;
   hour = tm->hour;
   min = tm->minute;
   sec = tm->second;

   return kTRUE;
}

////////////////////////////////////////////////////////////////////////////////
/// return field value as date & time

Bool_t TODBCStatement::GetDatime(Int_t npar, Int_t& year, Int_t& month, Int_t& day, Int_t& hour, Int_t& min, Int_t& sec)
{
   void* addr = GetParAddr(npar);
   if (!addr) return kFALSE;

   if (fBuffer[npar].fBsqlctype!=SQL_C_TYPE_TIMESTAMP) return kFALSE;

   TIMESTAMP_STRUCT* tm = (TIMESTAMP_STRUCT*) addr;

   year = tm->year;
   month = tm->month;
   day = tm->day;
   hour = tm->hour;
   min = tm->minute;
   sec = tm->second;
   return kTRUE;
}

////////////////////////////////////////////////////////////////////////////////
/// return field value as time stamp

Bool_t TODBCStatement::GetTimestamp(Int_t npar, Int_t& year, Int_t& month, Int_t& day, Int_t& hour, Int_t& min, Int_t& sec, Int_t& frac)
{
   void* addr = GetParAddr(npar);
   if (!addr) return kFALSE;

   if (fBuffer[npar].fBsqlctype!=SQL_C_TYPE_TIMESTAMP) return kFALSE;

   TIMESTAMP_STRUCT* tm = (TIMESTAMP_STRUCT*) addr;

   year = tm->year;
   month = tm->month;
   day = tm->day;
   hour = tm->hour;
   min = tm->minute;
   sec = tm->second;
   frac = tm->fraction;
   return kTRUE;
}


////////////////////////////////////////////////////////////////////////////////
/// Set NULL as parameter value
/// If NULL should be set for statement parameter during first iteration,
/// one should call before proper Set... method to identify type of argument for
/// the future. For instance, if one suppose to have double as type of parameter,
/// code should look like:
///    stmt->SetDouble(2, 0.);
///    stmt->SetNull(2);

Bool_t TODBCStatement::SetNull(Int_t npar)
{
   void* addr = GetParAddr(npar, kInt_t);
   if (addr)
      *((SQLINTEGER*) addr) = 0;

   if ((npar >= 0) && (npar < fNumBuffers))
      fBuffer[npar].fBlenarray[fBufferCounter] = SQL_NULL_DATA;

   return kTRUE;
}

////////////////////////////////////////////////////////////////////////////////
///set parameter as Int_t

Bool_t TODBCStatement::SetInt(Int_t npar, Int_t value)
{
   void* addr = GetParAddr(npar, kInt_t);
   if (!addr) return kFALSE;

   *((SQLINTEGER*) addr) = value;

   fBuffer[npar].fBlenarray[fBufferCounter] = 0;

   return kTRUE;
}

////////////////////////////////////////////////////////////////////////////////
///set parameter as UInt_t

Bool_t TODBCStatement::SetUInt(Int_t npar, UInt_t value)
{
   void* addr = GetParAddr(npar, kUInt_t);
   if (!addr) return kFALSE;

   *((SQLUINTEGER*) addr) = value;

   fBuffer[npar].fBlenarray[fBufferCounter] = 0;

   return kTRUE;
}

////////////////////////////////////////////////////////////////////////////////
///set parameter as Long_t

Bool_t TODBCStatement::SetLong(Int_t npar, Long_t value)
{
   void* addr = GetParAddr(npar, kLong_t);
   if (!addr) return kFALSE;

   *((SQLINTEGER*) addr) = value;

   fBuffer[npar].fBlenarray[fBufferCounter] = 0;

   return kTRUE;
}

////////////////////////////////////////////////////////////////////////////////
///set parameter as Long64_t

Bool_t TODBCStatement::SetLong64(Int_t npar, Long64_t value)
{
   void* addr = GetParAddr(npar, kLong64_t);
   if (!addr) return kFALSE;

   *((Long64_t*) addr) = value;

   fBuffer[npar].fBlenarray[fBufferCounter] = 0;

   return kTRUE;
}

////////////////////////////////////////////////////////////////////////////////
///set parameter as ULong64_t

Bool_t TODBCStatement::SetULong64(Int_t npar, ULong64_t value)
{
   void* addr = GetParAddr(npar, kULong64_t);
   if (!addr) return kFALSE;

   *((ULong64_t*) addr) = value;

   fBuffer[npar].fBlenarray[fBufferCounter] = 0;

   return kTRUE;
}

////////////////////////////////////////////////////////////////////////////////
///set parameter as Double_t

Bool_t TODBCStatement::SetDouble(Int_t npar, Double_t value)
{
   void* addr = GetParAddr(npar, kDouble_t);
   if (!addr) return kFALSE;

   *((SQLDOUBLE*) addr) = value;

   fBuffer[npar].fBlenarray[fBufferCounter] = 0;

   return kTRUE;
}

////////////////////////////////////////////////////////////////////////////////
///set parameter as string

Bool_t TODBCStatement::SetString(Int_t npar, const char* value, Int_t maxsize)
{
   void* addr = GetParAddr(npar, kCharStar, maxsize);
   if (!addr) return kFALSE;

   if (value) {
      int len = strlen(value);

      if (len>=fBuffer[npar].fBelementsize) {
         len = fBuffer[npar].fBelementsize;
         strlcpy((char*) addr, value, len+1);
         fBuffer[npar].fBlenarray[fBufferCounter] = len;

      } else if (len>0) {
         strlcpy((char*) addr, value, maxsize);
         fBuffer[npar].fBlenarray[fBufferCounter] = SQL_NTS;
      } else {
         *((char*) addr) = 0;
         fBuffer[npar].fBlenarray[fBufferCounter] = SQL_NTS;
      }
   } else {
      *((char*) addr) = 0;
      fBuffer[npar].fBlenarray[fBufferCounter] = SQL_NTS;
   }

   return kTRUE;
}

////////////////////////////////////////////////////////////////////////////////
///set parameter value as binary data

Bool_t TODBCStatement::SetBinary(Int_t npar, void* mem, Long_t size, Long_t maxsize)
{
   void* addr = GetParAddr(npar, kSqlBinary, maxsize);
   if (!addr) return kFALSE;

   if (size>fBuffer[npar].fBelementsize)
      size = fBuffer[npar].fBelementsize;

   memcpy(addr, mem, size);
   fBuffer[npar].fBlenarray[fBufferCounter] = size;

   return kTRUE;
}

////////////////////////////////////////////////////////////////////////////////
/// set parameter value as date

Bool_t TODBCStatement::SetDate(Int_t npar, Int_t year, Int_t month, Int_t day)
{
   void* addr = GetParAddr(npar, kSqlDate);
   if (!addr) return kFALSE;

   DATE_STRUCT* dt = (DATE_STRUCT*) addr;
   dt->year = year;
   dt->month = month;
   dt->day = day;

   fBuffer[npar].fBlenarray[fBufferCounter] = 0;

   return kTRUE;
}

////////////////////////////////////////////////////////////////////////////////
/// set parameter value as time

Bool_t TODBCStatement::SetTime(Int_t npar, Int_t hour, Int_t min, Int_t sec)
{
   void* addr = GetParAddr(npar, kSqlTime);
   if (!addr) return kFALSE;

   TIME_STRUCT* tm = (TIME_STRUCT*) addr;
   tm->hour = hour;
   tm->minute = min;
   tm->second = sec;

   fBuffer[npar].fBlenarray[fBufferCounter] = 0;

   return kTRUE;
}

////////////////////////////////////////////////////////////////////////////////
/// set parameter value as date & time

Bool_t TODBCStatement::SetDatime(Int_t npar, Int_t year, Int_t month, Int_t day, Int_t hour, Int_t min, Int_t sec)
{
   void* addr = GetParAddr(npar, kSqlTimestamp);
   if (!addr) return kFALSE;

   TIMESTAMP_STRUCT* tm = (TIMESTAMP_STRUCT*) addr;
   tm->year = year;
   tm->month = month;
   tm->day = day;
   tm->hour = hour;
   tm->minute = min;
   tm->second = sec;
   tm->fraction = 0;

   fBuffer[npar].fBlenarray[fBufferCounter] = 0;

   return kTRUE;
}

////////////////////////////////////////////////////////////////////////////////
/// set parameter value as timestamp

Bool_t TODBCStatement::SetTimestamp(Int_t npar, Int_t year, Int_t month, Int_t day, Int_t hour, Int_t min, Int_t sec, Int_t frac)
{
   void* addr = GetParAddr(npar, kSqlTimestamp);
   if (!addr) return kFALSE;

   TIMESTAMP_STRUCT* tm = (TIMESTAMP_STRUCT*) addr;
   tm->year = year;
   tm->month = month;
   tm->day = day;
   tm->hour = hour;
   tm->minute = min;
   tm->second = sec;
   tm->fraction = frac;

   fBuffer[npar].fBlenarray[fBufferCounter] = 0;

   return kTRUE;
}
back to top