#include "TODBCServer.h"
#include "TODBCRow.h"
#include "TODBCResult.h"
#include "TODBCStatement.h"
#include "TSQLColumnInfo.h"
#include "TSQLTableInfo.h"
#include "TUrl.h"
#include "TString.h"
#include "TObjString.h"
#include "TList.h"
#include "Riostream.h"
#include <sqlext.h>
ClassImp(TODBCServer)
TODBCServer::TODBCServer(const char *db, const char *uid, const char *pw) :
   TSQLServer()
{
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   TString connstr;
   Bool_t simpleconnect = kTRUE;
   SQLRETURN retcode;
   SQLHWND  hwnd;
   fPort = 1; 
   if ((strncmp(db, "odbc", 4)!=0) || (strlen(db)<8)) {
      SetError(-1, "db argument should be started from odbc...","TODBCServer");
      goto zombie;
   }
   if (strncmp(db, "odbc://", 7)==0) {
      TUrl url(db);
      if (!url.IsValid()) {
         SetError(-1, Form("not valid URL: %s", db), "TODBCServer");
         goto zombie;
      }
      const char* driver = "MyODBC";
      const char* dbase = url.GetFile();
      if (dbase!=0)
         if (*dbase=='/') dbase++; 
      if ((uid==0) || (*uid==0) && (strlen(url.GetUser())>0)) {
         uid = url.GetUser();
         pw = url.GetPasswd();
      }
      if (strlen(url.GetOptions())!=0) driver = url.GetOptions();
      connstr.Form("DRIVER={%s};"
                   "SERVER=%s;"
                   "DATABASE=%s;"
                   "USER=%s;"
                   "PASSWORD=%s;"
                   "OPTION=3;",
                    driver, url.GetHost(), dbase, uid, pw);
      if (url.GetPort()>0)
          connstr += Form("PORT=%d;", url.GetPort());
      fHost = url.GetHost();
      fPort = url.GetPort()>0 ? url.GetPort() : 1;
      fDB = dbase;
      simpleconnect = kFALSE;
   } else
   if (strncmp(db, "odbcd://", 8)==0) {
      connstr = db+8;
      simpleconnect = kFALSE;
   } else
   if (strncmp(db, "odbcn://", 8)==0) {
      connstr = db+8;
      simpleconnect = kTRUE;
   } else {
      SetError(-1, "db argument is invalid", "TODBCServer");
      goto zombie;
   }
   retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &fHenv);
   if (ExtractErrors(retcode, "TODBCServer")) goto zombie;
   
   retcode = SQLSetEnvAttr(fHenv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
   if (ExtractErrors(retcode, "TODBCServer")) goto zombie;
   
   retcode = SQLAllocHandle(SQL_HANDLE_DBC, fHenv, &fHdbc);
   if (ExtractErrors(retcode, "TODBCServer")) goto zombie;
   
   retcode = SQLSetConnectAttr(fHdbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER) 5, 0);
   if (ExtractErrors(retcode, "TODBCServer")) goto zombie;
   char sbuf[2048];
   SQLSMALLINT reslen;
   SQLINTEGER reslen1;
   hwnd = 0;
   if (simpleconnect)
      retcode = SQLConnect(fHdbc, (SQLCHAR*) connstr.Data(), SQL_NTS,
                                  (SQLCHAR*) uid, SQL_NTS,
                                  (SQLCHAR*) pw, SQL_NTS);
   else
      retcode = SQLDriverConnect(fHdbc, hwnd,
                                 (SQLCHAR*) connstr.Data(), SQL_NTS,
                                 (SQLCHAR*) sbuf, sizeof(sbuf), &reslen, SQL_DRIVER_NOPROMPT);
   if (ExtractErrors(retcode, "TODBCServer")) goto zombie;
   fType = "ODBC";
                  
   retcode = SQLGetInfo(fHdbc, SQL_USER_NAME, sbuf, sizeof(sbuf), &reslen);
   if (ExtractErrors(retcode, "TODBCServer")) goto zombie;
   fUserId = sbuf;
   retcode = SQLGetInfo(fHdbc, SQL_DBMS_NAME, sbuf, sizeof(sbuf), &reslen);
   if (ExtractErrors(retcode, "TODBCServer")) goto zombie;
   fServerInfo = sbuf;
   fType = sbuf;
   
   retcode = SQLGetInfo(fHdbc, SQL_DBMS_VER, sbuf, sizeof(sbuf), &reslen);
   if (ExtractErrors(retcode, "TODBCServer")) goto zombie;
   fServerInfo += " ";
   fServerInfo += sbuf;
   
   retcode = SQLGetConnectAttr(fHdbc, SQL_ATTR_CURRENT_CATALOG, sbuf, sizeof(sbuf), &reslen1);
   if (ExtractErrors(retcode, "TODBCServer")) goto zombie;
   if (fDB.Length()==0) fDB = sbuf;
   retcode = SQLGetInfo(fHdbc, SQL_SERVER_NAME, sbuf, sizeof(sbuf), &reslen);
   if (ExtractErrors(retcode, "TODBCServer")) goto zombie;
   if (fHost.Length()==0) fHost = sbuf;
   return;
zombie:
   fPort = -1;
   fHost = "";
   MakeZombie();
}
TODBCServer::~TODBCServer()
{
   
   if (IsConnected())
      Close();
}
TList* TODBCServer::ListData(Bool_t isdrivers)
{
   
   
   SQLHENV   henv;
   SQLRETURN retcode;
   retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
   if ((retcode!=SQL_SUCCESS) && (retcode!=SQL_SUCCESS_WITH_INFO)) return 0;
   retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
   if ((retcode!=SQL_SUCCESS) && (retcode!=SQL_SUCCESS_WITH_INFO)) return 0;
   
   TList* lst = 0;
   
   char namebuf[2048], optbuf[2048];
   SQLSMALLINT reslen1, reslen2;
   
   do {
      strcpy(namebuf, ""); 
      strcpy(optbuf, "");
      if (isdrivers)
         retcode = SQLDrivers(henv, (lst==0 ? SQL_FETCH_FIRST : SQL_FETCH_NEXT), 
                     (SQLCHAR*) namebuf, sizeof(namebuf), &reslen1,
                     (SQLCHAR*) optbuf, sizeof(optbuf), &reslen2);
      else
         retcode = SQLDataSources(henv, (lst==0 ? SQL_FETCH_FIRST : SQL_FETCH_NEXT), 
                     (SQLCHAR*) namebuf, sizeof(namebuf), &reslen1,
                     (SQLCHAR*) optbuf, sizeof(optbuf), &reslen2);
                     
      if (retcode==SQL_NO_DATA) break;
      if ((retcode==SQL_SUCCESS) || (retcode==SQL_SUCCESS_WITH_INFO)) {
         if (lst==0) { 
            lst = new TList; 
            lst->SetOwner(kTRUE);
         } 
         for (int n=0;n<reslen2-1;n++)
            if (optbuf[n]=='\0') optbuf[n] = ';';
         
         lst->Add(new TNamed(namebuf, optbuf));
      } 
   } while ((retcode==SQL_SUCCESS) || (retcode==SQL_SUCCESS_WITH_INFO));
   SQLFreeHandle(SQL_HANDLE_ENV, henv);
   
   return lst;
   
}
TList* TODBCServer::GetDrivers()
{
   
   
   
   
   return ListData(kTRUE);
}
void TODBCServer::PrintDrivers()
{
   
   
    
   TList* lst = GetDrivers();
   cout << "List of ODBC drivers:" << endl;
   TIter iter(lst);
   TNamed* n = 0;
   while ((n = (TNamed*) iter()) != 0) 
      cout << "  " << n->GetName() << " : " << n->GetTitle() << endl; 
   delete lst;
}
TList* TODBCServer::GetDataSources()
{
   
   
   
   
   return ListData(kFALSE);
}
void TODBCServer::PrintDataSources()
{
   
   
    
   TList* lst = GetDataSources();
   cout << "List of ODBC data sources:" << endl;
   TIter iter(lst);
   TNamed* n = 0;
   while ((n = (TNamed*) iter()) != 0) 
      cout << "  " << n->GetName() << " : " << n->GetTitle() << endl; 
   delete lst;
}
Bool_t TODBCServer::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;
   while (SQLGetDiagRec(SQL_HANDLE_ENV, fHenv, ++i, state, &native, text,
                          sizeof(text), &len ) == SQL_SUCCESS)
      
      SetError(native, (const char*) text, method);
   i = 0;
   while (SQLGetDiagRec(SQL_HANDLE_DBC, fHdbc, ++i, state, &native, text,
                          sizeof(text), &len ) == SQL_SUCCESS)
      SetError(native, (const char*) text, method);
   return kTRUE;
}
#define CheckConnect(method, res)                       \
   {                                                    \
      ClearError();                                     \
      if (!IsConnected()) {                             \
         SetError(-1,"ODBC driver is not connected",method); \
         return res;                                    \
      }                                                 \
   }
void TODBCServer::Close(Option_t *)
{
   
   SQLDisconnect(fHdbc);
   SQLFreeHandle(SQL_HANDLE_DBC, fHdbc);
   SQLFreeHandle(SQL_HANDLE_ENV, fHenv);
   fPort = -1;
}
TSQLResult *TODBCServer::Query(const char *sql)
{
   
   
   
   CheckConnect("Query", 0);
   SQLRETURN    retcode;
   SQLHSTMT     hstmt;
   SQLAllocHandle(SQL_HANDLE_STMT, fHdbc, &hstmt);
   retcode = SQLExecDirect(hstmt, (SQLCHAR*) sql, SQL_NTS);
   if (ExtractErrors(retcode, "Query")) {
      SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
      return 0;
   }
   return new TODBCResult(hstmt);
}
Bool_t TODBCServer::Exec(const char* sql)
{
   
   
   CheckConnect("Exec", 0);
   SQLRETURN    retcode;
   SQLHSTMT     hstmt;
   SQLAllocHandle(SQL_HANDLE_STMT, fHdbc, &hstmt);
   retcode = SQLExecDirect(hstmt, (SQLCHAR*) sql, SQL_NTS);
   Bool_t res = !ExtractErrors(retcode, "Exec");
   SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
   return res;
}
Int_t TODBCServer::SelectDataBase(const char *db)
{
   
   
   
   CheckConnect("SelectDataBase", -1);
   
   SQLRETURN retcode = SQLSetConnectAttr(fHdbc, SQL_ATTR_CURRENT_CATALOG, (SQLCHAR*) db, SQL_NTS);
   if (ExtractErrors(retcode, "SelectDataBase")) return -1;
   
   fDB = db;
   return 0;
}
TSQLResult *TODBCServer::GetDataBases(const char *)
{
   
   
   
   
   CheckConnect("GetDataBases", 0);
   return 0;
}
TSQLResult *TODBCServer::GetTables(const char*, const char* wild)
{
   
   
   
   
   CheckConnect("GetTables", 0);
   SQLRETURN    retcode;
   SQLHSTMT     hstmt;
   SQLAllocHandle(SQL_HANDLE_STMT, fHdbc, &hstmt);
   SQLCHAR* schemaName = 0;
   SQLSMALLINT schemaNameLength = 0;
   
   SQLCHAR* tableName = 0;
   SQLSMALLINT tableNameLength = 0;
   if ((wild!=0) && (strlen(wild)!=0)) {
      tableName = (SQLCHAR*) wild;
      tableNameLength = strlen(wild);
      SQLSetStmtAttr(hstmt, SQL_ATTR_METADATA_ID, (SQLPOINTER) SQL_FALSE, 0);
   }
   retcode = SQLTables(hstmt, NULL, 0, schemaName, schemaNameLength, tableName, tableNameLength, (SQLCHAR*) "TABLE", 5);
   if (ExtractErrors(retcode, "GetTables")) {
      SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
      return 0;
   }
   return new TODBCResult(hstmt);
}
TList* TODBCServer::GetTablesList(const char* wild)
{
   
   
   CheckConnect("GetTablesList", 0);
   TSQLResult* res = GetTables(0, wild);
   if (res==0) return 0;
   TList* lst = 0;
   TSQLRow* row = 0;
   while ((row = res->Next())!=0) {
      const char* tablename = row->GetField(2);
      if (tablename!=0) {
         if (lst==0) {
            lst = new TList;
            lst->SetOwner(kTRUE);
         }
         lst->Add(new TObjString(tablename));
      }
      delete row;
   }
   delete res;
   return lst;
}
TSQLTableInfo* TODBCServer::GetTableInfo(const char* tablename)
{
   
   
   CheckConnect("GetTableInfo", 0);
   #define STR_LEN 128+1
   #define REM_LEN 254+1
   
   SQLCHAR       szCatalog[STR_LEN], szSchema[STR_LEN];
   SQLCHAR       szTableName[STR_LEN], szColumnName[STR_LEN];
   SQLCHAR       szTypeName[STR_LEN], szRemarks[REM_LEN];
   SQLCHAR       szColumnDefault[STR_LEN], szIsNullable[STR_LEN];
   SQLLEN        columnSize, bufferLength, charOctetLength, ordinalPosition;
   SQLSMALLINT   dataType, decimalDigits, numPrecRadix, nullable;
   SQLSMALLINT   sqlDataType, datetimeSubtypeCode;
   SQLRETURN     retcode;
   SQLHSTMT      hstmt;
   
   SQLLEN cbCatalog, cbSchema, cbTableName, cbColumnName;
   SQLLEN cbDataType, cbTypeName, cbColumnSize, cbBufferLength;
   SQLLEN cbDecimalDigits, cbNumPrecRadix, cbNullable, cbRemarks;
   SQLLEN cbColumnDefault, cbSQLDataType, cbDatetimeSubtypeCode, cbCharOctetLength;
   SQLLEN cbOrdinalPosition, cbIsNullable;
   SQLAllocHandle(SQL_HANDLE_STMT, fHdbc, &hstmt);
   retcode = SQLColumns(hstmt, NULL, 0, NULL, 0, (SQLCHAR*) tablename, SQL_NTS, NULL, 0);
   if (ExtractErrors(retcode, "GetTableInfo")) {
      SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
      return 0;
   }
   TList* lst = 0;
   
   SQLBindCol(hstmt, 1, SQL_C_CHAR, szCatalog, STR_LEN,&cbCatalog);
   SQLBindCol(hstmt, 2, SQL_C_CHAR, szSchema, STR_LEN, &cbSchema);
   SQLBindCol(hstmt, 3, SQL_C_CHAR, szTableName, STR_LEN,&cbTableName);
   SQLBindCol(hstmt, 4, SQL_C_CHAR, szColumnName, STR_LEN, &cbColumnName);
   SQLBindCol(hstmt, 5, SQL_C_SSHORT, &dataType, 0, &cbDataType);
   SQLBindCol(hstmt, 6, SQL_C_CHAR, szTypeName, STR_LEN, &cbTypeName);
   SQLBindCol(hstmt, 7, SQL_C_SLONG, &columnSize, 0, &cbColumnSize);
   SQLBindCol(hstmt, 8, SQL_C_SLONG, &bufferLength, 0, &cbBufferLength);
   SQLBindCol(hstmt, 9, SQL_C_SSHORT, &decimalDigits, 0, &cbDecimalDigits);
   SQLBindCol(hstmt, 10, SQL_C_SSHORT, &numPrecRadix, 0, &cbNumPrecRadix);
   SQLBindCol(hstmt, 11, SQL_C_SSHORT, &nullable, 0, &cbNullable);
   SQLBindCol(hstmt, 12, SQL_C_CHAR, szRemarks, REM_LEN, &cbRemarks);
   SQLBindCol(hstmt, 13, SQL_C_CHAR, szColumnDefault, STR_LEN, &cbColumnDefault);
   SQLBindCol(hstmt, 14, SQL_C_SSHORT, &sqlDataType, 0, &cbSQLDataType);
   SQLBindCol(hstmt, 15, SQL_C_SSHORT, &datetimeSubtypeCode, 0, &cbDatetimeSubtypeCode);
   SQLBindCol(hstmt, 16, SQL_C_SLONG, &charOctetLength, 0, &cbCharOctetLength);
   SQLBindCol(hstmt, 17, SQL_C_SLONG, &ordinalPosition, 0, &cbOrdinalPosition);
   SQLBindCol(hstmt, 18, SQL_C_CHAR, szIsNullable, STR_LEN, &cbIsNullable);
   retcode = SQLFetch(hstmt);
   while ((retcode==SQL_SUCCESS) || (retcode==SQL_SUCCESS_WITH_INFO)) {
      Int_t sqltype = kSQL_NONE;
      Int_t data_size = -1;    
      Int_t data_length = -1;  
      Int_t data_scale = -1;   
      Int_t data_sign = -1; 
      switch (dataType) {
         case SQL_CHAR:
            sqltype = kSQL_CHAR;
            data_size = columnSize;
            data_length = charOctetLength;
            break;
         case SQL_VARCHAR:
         case SQL_LONGVARCHAR:
            sqltype = kSQL_VARCHAR;
            data_size = columnSize;
            data_length = charOctetLength;
            break;
         case SQL_DECIMAL:
         case SQL_NUMERIC:
            sqltype = kSQL_NUMERIC;
            data_size = columnSize; 
            data_length = columnSize;
            data_scale = decimalDigits;
            break;
         case SQL_INTEGER:
         case SQL_TINYINT:
         case SQL_BIGINT:
            sqltype = kSQL_INTEGER;
            data_size = columnSize;
            break;
         case SQL_REAL:
         case SQL_FLOAT:
            sqltype = kSQL_FLOAT;
            data_size = columnSize;
            data_sign = 1;
            break;
         case SQL_DOUBLE:
            sqltype = kSQL_DOUBLE;
            data_size = columnSize;
            data_sign = 1;
            break;
         case SQL_BINARY:
         case SQL_VARBINARY:
         case SQL_LONGVARBINARY:
            sqltype = kSQL_BINARY;
            data_size = columnSize;
            break;
         case SQL_TYPE_TIMESTAMP:
            sqltype = kSQL_TIMESTAMP;
            data_size = columnSize;
            break;
      }
      if (lst==0) lst = new TList;
      lst->Add(new TSQLColumnInfo((const char*) szColumnName,
                                  (const char*) szTypeName,
                                  nullable!=0,
                                  sqltype,
                                  data_size,
                                  data_length,
                                  data_scale,
                                  data_sign));
      retcode = SQLFetch(hstmt);
   }
   SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
   return new TSQLTableInfo(tablename, lst);
}
TSQLResult *TODBCServer::GetColumns(const char*, const char *table, const char*)
{
   
   
   
   
   CheckConnect("GetColumns", 0);
   SQLRETURN    retcode;
   SQLHSTMT     hstmt;
   SQLAllocHandle(SQL_HANDLE_STMT, fHdbc, &hstmt);
   retcode = SQLColumns(hstmt, NULL, 0, NULL, 0, (SQLCHAR*) table, SQL_NTS, NULL, 0);
   if (ExtractErrors(retcode, "GetColumns")) {
      SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
      return 0;
   }
   return new TODBCResult(hstmt);
}
Int_t TODBCServer::GetMaxIdentifierLength()
{
   
   CheckConnect("GetMaxIdentifierLength", 20);
   SQLUINTEGER info = 0;
   SQLRETURN retcode;
   retcode = SQLGetInfo(fHdbc, SQL_MAX_IDENTIFIER_LEN, (SQLPOINTER)&info, sizeof(info), NULL);
   if (ExtractErrors(retcode, "GetMaxIdentifierLength")) return 20;
   return info;
}
Int_t TODBCServer::CreateDataBase(const char*)
{
   
   CheckConnect("CreateDataBase", -1);
   return -1;
}
Int_t TODBCServer::DropDataBase(const char*)
{
   
   
   CheckConnect("DropDataBase", -1);
   return -1;
}
Int_t TODBCServer::Reload()
{
   
   
   CheckConnect("Reload", -1);
   return -1;
}
Int_t TODBCServer::Shutdown()
{
   
   
   CheckConnect("Shutdown", -1);
   return -1;
}
const char *TODBCServer::ServerInfo()
{
   
   CheckConnect("ServerInfo", 0);
   return fServerInfo;
}
TSQLStatement *TODBCServer::Statement(const char *sql, Int_t bufsize)
{
   
   
   CheckConnect("Statement", 0);
   if (!sql || !*sql) {
      SetError(-1, "no query string specified", "Statement");
      return 0;
   }
   SQLRETURN    retcode;
   SQLHSTMT     hstmt;
   retcode = SQLAllocHandle(SQL_HANDLE_STMT, fHdbc, &hstmt);
   if (ExtractErrors(retcode, "Statement")) return 0;
   retcode = SQLPrepare(hstmt, (SQLCHAR*) sql, SQL_NTS);
   if (ExtractErrors(retcode, "Statement")) {
      SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
      return 0;
   }
   return new TODBCStatement(hstmt, bufsize, fErrorOut);
}
Bool_t TODBCServer::StartTransaction()
{
   
   
   
   CheckConnect("StartTransaction", kFALSE);
   SQLUINTEGER info = 0;
   SQLRETURN retcode;
   retcode = SQLGetInfo(fHdbc, SQL_TXN_CAPABLE, (SQLPOINTER)&info, sizeof(info), NULL);
   if (ExtractErrors(retcode, "StartTransaction")) return kFALSE;
   if (info==0) {
      SetError(-1,"Transactions not supported","StartTransaction");
      return kFALSE;
   }
   if (!Commit()) return kFALSE;
   retcode = SQLSetConnectAttr(fHdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_OFF, 0);
   if (ExtractErrors(retcode, "StartTransaction")) return kFALSE;
   return kTRUE;
}
Bool_t TODBCServer::EndTransaction(Bool_t commit)
{
   
   
   const char* method = commit ? "Commit" : "Rollback";
   CheckConnect(method, kFALSE);
   SQLRETURN retcode = SQLEndTran(SQL_HANDLE_DBC, fHdbc, commit ? SQL_COMMIT : SQL_ROLLBACK);
   if (ExtractErrors(retcode, method)) return kFALSE;
   retcode = SQLSetConnectAttr(fHdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_ON, 0);
   return kTRUE;
}
Bool_t TODBCServer::Commit()
{
   
   return EndTransaction(kTRUE);
}
Bool_t TODBCServer::Rollback()
{
   
   return EndTransaction(kFALSE);
}
This page has been automatically generated. If you have any comments or suggestions about the page layout send a mail to ROOT support, or contact the developers with any questions or problems regarding ROOT.