TODBCServer.cxx

Go to the documentation of this file.
00001 // @(#)root/odbc:$Id: TODBCServer.cxx 33535 2010-05-17 16:49:24Z brun $
00002 // Author: Sergey Linev   6/02/2006
00003 
00004 /*************************************************************************
00005  * Copyright (C) 1995-2006, Rene Brun and Fons Rademakers.               *
00006  * All rights reserved.                                                  *
00007  *                                                                       *
00008  * For the licensing terms see $ROOTSYS/LICENSE.                         *
00009  * For the list of contributors see $ROOTSYS/README/CREDITS.             *
00010  *************************************************************************/
00011 
00012 #include "TODBCServer.h"
00013 
00014 #include "TODBCRow.h"
00015 #include "TODBCResult.h"
00016 #include "TODBCStatement.h"
00017 #include "TSQLColumnInfo.h"
00018 #include "TSQLTableInfo.h"
00019 #include "TUrl.h"
00020 #include "TString.h"
00021 #include "TObjString.h"
00022 #include "TList.h"
00023 #include "Riostream.h"
00024 
00025 
00026 #include <sqlext.h>
00027 
00028 
00029 ClassImp(TODBCServer)
00030 
00031 //______________________________________________________________________________
00032 TODBCServer::TODBCServer(const char *db, const char *uid, const char *pw) :
00033    TSQLServer()
00034 {
00035    // Open a connection to a ODBC server. The db arguments can be:
00036    // 1. Form "odbc://[user[:passwd]@]<host>[:<port>][/<database>][?Driver]",
00037    //    e.g.: "odbc://pcroot.cern.ch:3306/test?MySQL".
00038    //    Driver argument specifies ODBC driver, which should be used for
00039    //    connection. By default, MyODBC driver name is used.
00040    //    The uid is the username and pw the password that should be used
00041    //    for the connection.
00042    //    If uid and pw are not specified (==0), user and passwd arguments from
00043    //    URL will be used. Works only with MySQL ODBC, probably with PostrSQL
00044    //    ODBC.
00045    // 2. Form "odbcd://DRIVER={MyODBC};SERVER=pcroot.cern.ch;DATABASE=test;USER=user;PASSWORD=pass;OPTION=3;PORT=3306;"
00046    //    This is a form, which is accepted by SQLDriverConnect function of ODBC.
00047    //    Here some other arguments can be specified, which are not included
00048    //    in standard URL format.
00049    // 3. Form "odbcn://MySpecialConfig", where MySpecialConfig is entry,
00050    //    defined in user DSN (user data source). Here uid and pw should be
00051    //    always specified.
00052    //
00053    //    Configuring unixODBC under Linux: http://www.unixodbc.org/odbcinst.html
00054    //    Remarks: for variants 1 & 2 it is enough to create/configure
00055    //    odbcinst.ini file. For variant 3 file odbc.ini should be created.
00056    //    Path to this files can be specified in enviromental variables like
00057    //      export ODBCINI=/home/my/unixODBC/etc/odbc.ini
00058    //      export ODBCSYSINI=/home/my/unixODBC/etc
00059    //
00060    //    Configuring MySQL ODBC under Windows.
00061    //    Installing ODBC driver for MySQL is enough to use it under Windows.
00062    //    Afer odbcd:// variant can be used with DRIVER={MySQL ODBC 3.51 Driver};
00063    //    To configure User DSN, go into Start menu -> Settings ->
00064    //    Control panel -> Administrative tools-> Data Sources (ODBC).
00065    //
00066    //    To install Oracle ODBC driver for Windows, one should download
00067    //    and install either complete Oracle client (~500 MB), or so-called
00068    //    Instant Client Basic and Instant Client ODBC (~20 MB together).
00069    //    Some remark about Instant Client:
00070    //       1) Two additional DLLs are required: mfc71.dll & msver71.dll
00071    //          They can be found either in MS VC++ 7.1 Free Toolkit or
00072    //          downloaded from other Internet sites
00073    //       2) ORACLE_HOME enviroment variable should be specified and point to
00074    //           location, where Instant Client files are extracted
00075    //       3) Run odbc_install.exe from account with administrative rights
00076    //       3) In $ORACLE_HOME/network/admin/ directory appropriate *.ora files
00077    //          like ldap.ora, sqlnet.ora, tnsnames.ora should be installed.
00078    //          Contact your Oracle administrator to get these files.
00079    //    After Oracle ODBC driver is installed, appropriate entry in ODBC drivers
00080    //    list like "Oracle in instantclient10_2" should appiar. Connection
00081    //    string example:
00082    //     "odbcd://DRIVER={Oracle in instantclient10_2};DBQ=db-test;UID=user_name;PWD=user_pass;";
00083 
00084    TString connstr;
00085    Bool_t simpleconnect = kTRUE;
00086 
00087    SQLRETURN retcode;
00088    SQLHWND  hwnd;
00089 
00090    fPort = 1; // indicate that we are connected
00091 
00092    if ((strncmp(db, "odbc", 4)!=0) || (strlen(db)<8)) {
00093       SetError(-1, "db argument should be started from odbc...","TODBCServer");
00094       goto zombie;
00095    }
00096 
00097    if (strncmp(db, "odbc://", 7)==0) {
00098       TUrl url(db);
00099       if (!url.IsValid()) {
00100          SetError(-1, Form("not valid URL: %s", db), "TODBCServer");
00101          goto zombie;
00102       }
00103       const char* driver = "MyODBC";
00104       const char* dbase = url.GetFile();
00105       if (dbase!=0)
00106          if (*dbase=='/') dbase++; //skip leading "/" if appears
00107 
00108       if (((uid==0) || (*uid==0)) && (strlen(url.GetUser())>0)) {
00109          uid = url.GetUser();
00110          pw = url.GetPasswd();
00111       }
00112 
00113       if (strlen(url.GetOptions())!=0) driver = url.GetOptions();
00114 
00115       connstr.Form("DRIVER={%s};"
00116                    "SERVER=%s;"
00117                    "DATABASE=%s;"
00118                    "USER=%s;"
00119                    "PASSWORD=%s;"
00120                    "OPTION=3;",
00121                     driver, url.GetHost(), dbase, uid, pw);
00122       if (url.GetPort()>0)
00123           connstr += Form("PORT=%d;", url.GetPort());
00124 
00125       fHost = url.GetHost();
00126       fPort = url.GetPort()>0 ? url.GetPort() : 1;
00127       fDB = dbase;
00128       simpleconnect = kFALSE;
00129    } else
00130    if (strncmp(db, "odbcd://", 8)==0) {
00131       connstr = db+8;
00132       simpleconnect = kFALSE;
00133    } else
00134    if (strncmp(db, "odbcn://", 8)==0) {
00135       connstr = db+8;
00136       simpleconnect = kTRUE;
00137    } else {
00138       SetError(-1, "db argument is invalid", "TODBCServer");
00139       goto zombie;
00140    }
00141 
00142    retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &fHenv);
00143    if (ExtractErrors(retcode, "TODBCServer")) goto zombie;
00144 
00145    /* Set the ODBC version environment attribute */
00146    retcode = SQLSetEnvAttr(fHenv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
00147    if (ExtractErrors(retcode, "TODBCServer")) goto zombie;
00148 
00149    /* Allocate connection handle */
00150    retcode = SQLAllocHandle(SQL_HANDLE_DBC, fHenv, &fHdbc);
00151    if (ExtractErrors(retcode, "TODBCServer")) goto zombie;
00152 
00153    /* Set login timeout to 5 seconds. */
00154    retcode = SQLSetConnectAttr(fHdbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER) 5, 0);
00155    if (ExtractErrors(retcode, "TODBCServer")) goto zombie;
00156 
00157    char sbuf[2048];
00158 
00159    SQLSMALLINT reslen;
00160    SQLINTEGER reslen1;
00161 
00162    hwnd = 0;
00163 
00164    if (simpleconnect)
00165       retcode = SQLConnect(fHdbc, (SQLCHAR*) connstr.Data(), SQL_NTS,
00166                                   (SQLCHAR*) uid, SQL_NTS,
00167                                   (SQLCHAR*) pw, SQL_NTS);
00168    else
00169       retcode = SQLDriverConnect(fHdbc, hwnd,
00170                                  (SQLCHAR*) connstr.Data(), SQL_NTS,
00171                                  (SQLCHAR*) sbuf, sizeof(sbuf), &reslen, SQL_DRIVER_NOPROMPT);
00172 
00173    if (ExtractErrors(retcode, "TODBCServer")) goto zombie;
00174 
00175    fType = "ODBC";
00176                   
00177    retcode = SQLGetInfo(fHdbc, SQL_USER_NAME, sbuf, sizeof(sbuf), &reslen);
00178    if (ExtractErrors(retcode, "TODBCServer")) goto zombie;
00179    fUserId = sbuf;
00180 
00181    retcode = SQLGetInfo(fHdbc, SQL_DBMS_NAME, sbuf, sizeof(sbuf), &reslen);
00182    if (ExtractErrors(retcode, "TODBCServer")) goto zombie;
00183    fServerInfo = sbuf;
00184    fType = sbuf;
00185    
00186    retcode = SQLGetInfo(fHdbc, SQL_DBMS_VER, sbuf, sizeof(sbuf), &reslen);
00187    if (ExtractErrors(retcode, "TODBCServer")) goto zombie;
00188    fServerInfo += " ";
00189    fServerInfo += sbuf;
00190 
00191    // take current catalog - database name
00192    retcode = SQLGetConnectAttr(fHdbc, SQL_ATTR_CURRENT_CATALOG, sbuf, sizeof(sbuf), &reslen1);
00193    if (ExtractErrors(retcode, "TODBCServer")) goto zombie;
00194    if (fDB.Length()==0) fDB = sbuf;
00195 
00196    retcode = SQLGetInfo(fHdbc, SQL_SERVER_NAME, sbuf, sizeof(sbuf), &reslen);
00197    if (ExtractErrors(retcode, "TODBCServer")) goto zombie;
00198    if (fHost.Length()==0) fHost = sbuf;
00199 
00200 /*   
00201    
00202    SQLUINTEGER iinfo;
00203    retcode = SQLGetInfo(fHdbc, SQL_PARAM_ARRAY_ROW_COUNTS, &iinfo, sizeof(iinfo), 0);  
00204    if (ExtractErrors(retcode, "TODBCServer")) goto zombie;
00205    Info("Constr", "SQL_PARAM_ARRAY_ROW_COUNTS = %u", iinfo);
00206    
00207    retcode = SQLGetInfo(fHdbc, SQL_PARAM_ARRAY_SELECTS, &iinfo, sizeof(iinfo), 0);  
00208    if (ExtractErrors(retcode, "TODBCServer")) goto zombie;
00209    Info("Constr", "SQL_PARAM_ARRAY_SELECTS = %u", iinfo);
00210 
00211    retcode = SQLGetInfo(fHdbc, SQL_BATCH_ROW_COUNT, &iinfo, sizeof(iinfo), 0);  
00212    if (ExtractErrors(retcode, "TODBCServer")) goto zombie;
00213    Info("Constr", "SQL_BATCH_ROW_COUNT = %u", iinfo);
00214 */
00215 
00216    return;
00217 
00218 zombie:
00219    fPort = -1;
00220    fHost = "";
00221    MakeZombie();
00222 }
00223 
00224 //______________________________________________________________________________
00225 TODBCServer::~TODBCServer()
00226 {
00227    // Close connection to MySQL DB server.
00228 
00229    if (IsConnected())
00230       Close();
00231 }
00232 
00233 //______________________________________________________________________________
00234 TList* TODBCServer::ListData(Bool_t isdrivers)
00235 {
00236    // Produce TList object with list of available 
00237    // ODBC drivers (isdrivers = kTRUE) or data sources (isdrivers = kFALSE)
00238 
00239    SQLHENV   henv;
00240    SQLRETURN retcode;
00241 
00242    retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
00243    if ((retcode!=SQL_SUCCESS) && (retcode!=SQL_SUCCESS_WITH_INFO)) return 0;
00244 
00245    retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
00246    if ((retcode!=SQL_SUCCESS) && (retcode!=SQL_SUCCESS_WITH_INFO)) return 0;
00247    
00248    TList* lst = 0;
00249    
00250    char namebuf[2048], optbuf[2048];
00251    SQLSMALLINT reslen1, reslen2;
00252    
00253    do {
00254       strcpy(namebuf, ""); 
00255       strcpy(optbuf, "");
00256       if (isdrivers)
00257          retcode = SQLDrivers(henv, (lst==0 ? SQL_FETCH_FIRST : SQL_FETCH_NEXT), 
00258                      (SQLCHAR*) namebuf, sizeof(namebuf), &reslen1,
00259                      (SQLCHAR*) optbuf, sizeof(optbuf), &reslen2);
00260       else
00261          retcode = SQLDataSources(henv, (lst==0 ? SQL_FETCH_FIRST : SQL_FETCH_NEXT), 
00262                      (SQLCHAR*) namebuf, sizeof(namebuf), &reslen1,
00263                      (SQLCHAR*) optbuf, sizeof(optbuf), &reslen2);
00264                      
00265       if (retcode==SQL_NO_DATA) break;
00266       if ((retcode==SQL_SUCCESS) || (retcode==SQL_SUCCESS_WITH_INFO)) {
00267          if (lst==0) { 
00268             lst = new TList; 
00269             lst->SetOwner(kTRUE);
00270          } 
00271          for (int n=0;n<reslen2-1;n++)
00272             if (optbuf[n]=='\0') optbuf[n] = ';';
00273          
00274          lst->Add(new TNamed(namebuf, optbuf));
00275       } 
00276    } while ((retcode==SQL_SUCCESS) || (retcode==SQL_SUCCESS_WITH_INFO));
00277 
00278    SQLFreeHandle(SQL_HANDLE_ENV, henv);
00279    
00280    return lst;
00281    
00282 }
00283 
00284 
00285 //______________________________________________________________________________
00286 TList* TODBCServer::GetDrivers()
00287 {
00288    // Produce TList object with list of available ODBC drivers
00289    // User must delete TList object aftewards
00290    // Name of driver can be used in connecting to data base in form
00291    // TSQLServer::Connect("odbcd://DRIVER={<drivername>};DBQ=<dbname>;UID=user;PWD=pass;", 0, 0);
00292 
00293    return ListData(kTRUE);
00294 }
00295 
00296 //______________________________________________________________________________
00297 void TODBCServer::PrintDrivers()
00298 {
00299    // Print list of ODBC drivers in form:
00300    //   <name> : <options list>
00301     
00302    TList* lst = GetDrivers();
00303    cout << "List of ODBC drivers:" << endl;
00304    TIter iter(lst);
00305    TNamed* n = 0;
00306    while ((n = (TNamed*) iter()) != 0) 
00307       cout << "  " << n->GetName() << " : " << n->GetTitle() << endl; 
00308    delete lst;
00309 }
00310 
00311 //______________________________________________________________________________
00312 TList* TODBCServer::GetDataSources()
00313 {
00314    // Produce TList object with list of available ODBC data sources
00315    // User must delete TList object aftewards
00316    // Name of data source can be used later for connection:
00317    // TSQLServer::Connect("odbcn://<data_source_name>", "user", "pass");
00318 
00319    return ListData(kFALSE);
00320 }
00321 
00322 //______________________________________________________________________________
00323 void TODBCServer::PrintDataSources()
00324 {
00325    // Print list of ODBC data sources in form:
00326    //   <name> : <options list>
00327     
00328    TList* lst = GetDataSources();
00329    cout << "List of ODBC data sources:" << endl;
00330    TIter iter(lst);
00331    TNamed* n = 0;
00332    while ((n = (TNamed*) iter()) != 0) 
00333       cout << "  " << n->GetName() << " : " << n->GetTitle() << endl; 
00334    delete lst;
00335 }
00336 
00337 //______________________________________________________________________________
00338 Bool_t TODBCServer::ExtractErrors(SQLRETURN retcode, const char* method)
00339 {
00340    // Extract errors, produced by last ODBC function call
00341 
00342    if ((retcode==SQL_SUCCESS) || (retcode==SQL_SUCCESS_WITH_INFO)) return kFALSE;
00343 
00344    SQLINTEGER i = 0;
00345    SQLINTEGER native;
00346    SQLCHAR state[7];
00347    SQLCHAR text[256];
00348    SQLSMALLINT len;
00349 
00350    while (SQLGetDiagRec(SQL_HANDLE_ENV, fHenv, ++i, state, &native, text,
00351                           sizeof(text), &len ) == SQL_SUCCESS)
00352       //Error(method, "%s:%ld:%ld:%s\n", state, i, native, text);
00353       SetError(native, (const char*) text, method);
00354 
00355    i = 0;
00356 
00357    while (SQLGetDiagRec(SQL_HANDLE_DBC, fHdbc, ++i, state, &native, text,
00358                           sizeof(text), &len ) == SQL_SUCCESS)
00359 //      Error(method, "%s:%ld:%ld:%s\n", state, i, native, text);
00360       SetError(native, (const char*) text, method);
00361 
00362    return kTRUE;
00363 }
00364 
00365 // Reset error and check that server connected
00366 #define CheckConnect(method, res)                       \
00367    {                                                    \
00368       ClearError();                                     \
00369       if (!IsConnected()) {                             \
00370          SetError(-1,"ODBC driver is not connected",method); \
00371          return res;                                    \
00372       }                                                 \
00373    }
00374 
00375 //______________________________________________________________________________
00376 void TODBCServer::Close(Option_t *)
00377 {
00378    // Close connection to MySQL DB server.
00379 
00380    SQLDisconnect(fHdbc);
00381    SQLFreeHandle(SQL_HANDLE_DBC, fHdbc);
00382    SQLFreeHandle(SQL_HANDLE_ENV, fHenv);
00383    fPort = -1;
00384 }
00385 
00386 //______________________________________________________________________________
00387 TSQLResult *TODBCServer::Query(const char *sql)
00388 {
00389    // Execute SQL command. Result object must be deleted by the user.
00390    // Returns a pointer to a TSQLResult object if successful, 0 otherwise.
00391    // The result object must be deleted by the user.
00392 
00393    CheckConnect("Query", 0);
00394 
00395    SQLRETURN    retcode;
00396    SQLHSTMT     hstmt;
00397 
00398    SQLAllocHandle(SQL_HANDLE_STMT, fHdbc, &hstmt);
00399 
00400    retcode = SQLExecDirect(hstmt, (SQLCHAR*) sql, SQL_NTS);
00401    if (ExtractErrors(retcode, "Query")) {
00402       SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
00403       return 0;
00404    }
00405 
00406    return new TODBCResult(hstmt);
00407 }
00408 
00409 //______________________________________________________________________________
00410 Bool_t TODBCServer::Exec(const char* sql)
00411 {
00412    // Executes query which does not produce any results set
00413    // Return kTRUE if successfull
00414 
00415    CheckConnect("Exec", 0);
00416 
00417    SQLRETURN    retcode;
00418    SQLHSTMT     hstmt;
00419 
00420    SQLAllocHandle(SQL_HANDLE_STMT, fHdbc, &hstmt);
00421 
00422    retcode = SQLExecDirect(hstmt, (SQLCHAR*) sql, SQL_NTS);
00423 
00424    Bool_t res = !ExtractErrors(retcode, "Exec");
00425 
00426    SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
00427 
00428    return res;
00429 }
00430 
00431 //______________________________________________________________________________
00432 Int_t TODBCServer::SelectDataBase(const char *db)
00433 {
00434    // Select a database. Returns 0 if successful, non-zero otherwise.
00435    // Not all RDBMS support selecting of database (catalog) after connecting
00436    // Normally user should specify database name at time of connection
00437 
00438    CheckConnect("SelectDataBase", -1);
00439    
00440    SQLRETURN retcode = SQLSetConnectAttr(fHdbc, SQL_ATTR_CURRENT_CATALOG, (SQLCHAR*) db, SQL_NTS);
00441    if (ExtractErrors(retcode, "SelectDataBase")) return -1;
00442    
00443    fDB = db;
00444 
00445    return 0;
00446 }
00447 
00448 //______________________________________________________________________________
00449 TSQLResult *TODBCServer::GetDataBases(const char *)
00450 {
00451    // List all available databases. Wild is for wildcarding "t%" list all
00452    // databases starting with "t".
00453    // Returns a pointer to a TSQLResult object if successful, 0 otherwise.
00454    // The result object must be deleted by the user.
00455 
00456    CheckConnect("GetDataBases", 0);
00457 
00458    return 0;
00459 }
00460 
00461 //______________________________________________________________________________
00462 TSQLResult *TODBCServer::GetTables(const char*, const char* wild)
00463 {
00464    // List all tables in the specified database. Wild is for wildcarding
00465    // "t%" list all tables starting with "t".
00466    // Returns a pointer to a TSQLResult object if successful, 0 otherwise.
00467    // The result object must be deleted by the user.
00468 
00469    CheckConnect("GetTables", 0);
00470 
00471    SQLRETURN    retcode;
00472    SQLHSTMT     hstmt;
00473 
00474    SQLAllocHandle(SQL_HANDLE_STMT, fHdbc, &hstmt);
00475 
00476    SQLCHAR* schemaName = 0;
00477    SQLSMALLINT schemaNameLength = 0;
00478 
00479 /*
00480    TString schemabuf;
00481    // schema is used by Oracle to specify to which user belong table
00482    // therefore, to see correct tables, schema name is set to user name
00483    if ((fUserId.Length()>0) && (fServerInfo.Contains("Oracle"))) {
00484       schemabuf = fUserId;
00485       schemabuf.ToUpper();
00486       schemaName = (SQLCHAR*) schemabuf.Data();
00487       schemaNameLength = schemabuf.Length();
00488    }
00489 */
00490    
00491    SQLCHAR* tableName = 0;
00492    SQLSMALLINT tableNameLength = 0;
00493 
00494    if ((wild!=0) && (strlen(wild)!=0)) {
00495       tableName = (SQLCHAR*) wild;
00496       tableNameLength = strlen(wild);
00497       SQLSetStmtAttr(hstmt, SQL_ATTR_METADATA_ID, (SQLPOINTER) SQL_FALSE, 0);
00498    }
00499 
00500    retcode = SQLTables(hstmt, NULL, 0, schemaName, schemaNameLength, tableName, tableNameLength, (SQLCHAR*) "TABLE", 5);
00501    if (ExtractErrors(retcode, "GetTables")) {
00502       SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
00503       return 0;
00504    }
00505 
00506    return new TODBCResult(hstmt);
00507 }
00508 
00509 //______________________________________________________________________________
00510 TList* TODBCServer::GetTablesList(const char* wild)
00511 {
00512    // Return list of tables in database
00513    // See TSQLServer::GetTablesList() for details.
00514 
00515    CheckConnect("GetTablesList", 0);
00516 
00517    TSQLResult* res = GetTables(0, wild);
00518    if (res==0) return 0;
00519 
00520    TList* lst = 0;
00521 
00522    TSQLRow* row = 0;
00523 
00524    while ((row = res->Next())!=0) {
00525       const char* tablename = row->GetField(2);
00526       if (tablename!=0) {
00527 //         Info("List","%s %s %s %s %s", tablename, row->GetField(0), row->GetField(1), row->GetField(3), row->GetField(4));
00528          if (lst==0) {
00529             lst = new TList;
00530             lst->SetOwner(kTRUE);
00531          }
00532          lst->Add(new TObjString(tablename));
00533       }
00534       delete row;
00535    }
00536 
00537    delete res;
00538 
00539    return lst;
00540 }
00541 
00542 
00543 //______________________________________________________________________________
00544 TSQLTableInfo* TODBCServer::GetTableInfo(const char* tablename)
00545 {
00546    // Produces SQL table info
00547    // Object must be deleted by user
00548 
00549    CheckConnect("GetTableInfo", 0);
00550 
00551    #define STR_LEN 128+1
00552    #define REM_LEN 254+1
00553 
00554    /* Declare buffers for result set data */
00555 
00556    SQLCHAR       szCatalog[STR_LEN], szSchema[STR_LEN];
00557    SQLCHAR       szTableName[STR_LEN], szColumnName[STR_LEN];
00558    SQLCHAR       szTypeName[STR_LEN], szRemarks[REM_LEN];
00559    SQLCHAR       szColumnDefault[STR_LEN], szIsNullable[STR_LEN];
00560    SQLLEN        columnSize, bufferLength, charOctetLength, ordinalPosition;
00561    SQLSMALLINT   dataType, decimalDigits, numPrecRadix, nullable;
00562    SQLSMALLINT   sqlDataType, datetimeSubtypeCode;
00563    SQLRETURN     retcode;
00564    SQLHSTMT      hstmt;
00565 
00566    /* Declare buffers for bytes available to return */
00567 
00568    SQLLEN cbCatalog, cbSchema, cbTableName, cbColumnName;
00569    SQLLEN cbDataType, cbTypeName, cbColumnSize, cbBufferLength;
00570    SQLLEN cbDecimalDigits, cbNumPrecRadix, cbNullable, cbRemarks;
00571    SQLLEN cbColumnDefault, cbSQLDataType, cbDatetimeSubtypeCode, cbCharOctetLength;
00572    SQLLEN cbOrdinalPosition, cbIsNullable;
00573 
00574 
00575    SQLAllocHandle(SQL_HANDLE_STMT, fHdbc, &hstmt);
00576 
00577    retcode = SQLColumns(hstmt, NULL, 0, NULL, 0, (SQLCHAR*) tablename, SQL_NTS, NULL, 0);
00578    if (ExtractErrors(retcode, "GetTableInfo")) {
00579       SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
00580       return 0;
00581    }
00582 
00583    TList* lst = 0;
00584 
00585    /* Bind columns in result set to buffers */
00586 
00587    SQLBindCol(hstmt, 1, SQL_C_CHAR, szCatalog, STR_LEN,&cbCatalog);
00588    SQLBindCol(hstmt, 2, SQL_C_CHAR, szSchema, STR_LEN, &cbSchema);
00589    SQLBindCol(hstmt, 3, SQL_C_CHAR, szTableName, STR_LEN,&cbTableName);
00590    SQLBindCol(hstmt, 4, SQL_C_CHAR, szColumnName, STR_LEN, &cbColumnName);
00591    SQLBindCol(hstmt, 5, SQL_C_SSHORT, &dataType, 0, &cbDataType);
00592    SQLBindCol(hstmt, 6, SQL_C_CHAR, szTypeName, STR_LEN, &cbTypeName);
00593    SQLBindCol(hstmt, 7, SQL_C_SLONG, &columnSize, 0, &cbColumnSize);
00594    SQLBindCol(hstmt, 8, SQL_C_SLONG, &bufferLength, 0, &cbBufferLength);
00595    SQLBindCol(hstmt, 9, SQL_C_SSHORT, &decimalDigits, 0, &cbDecimalDigits);
00596    SQLBindCol(hstmt, 10, SQL_C_SSHORT, &numPrecRadix, 0, &cbNumPrecRadix);
00597    SQLBindCol(hstmt, 11, SQL_C_SSHORT, &nullable, 0, &cbNullable);
00598    SQLBindCol(hstmt, 12, SQL_C_CHAR, szRemarks, REM_LEN, &cbRemarks);
00599    SQLBindCol(hstmt, 13, SQL_C_CHAR, szColumnDefault, STR_LEN, &cbColumnDefault);
00600    SQLBindCol(hstmt, 14, SQL_C_SSHORT, &sqlDataType, 0, &cbSQLDataType);
00601    SQLBindCol(hstmt, 15, SQL_C_SSHORT, &datetimeSubtypeCode, 0, &cbDatetimeSubtypeCode);
00602    SQLBindCol(hstmt, 16, SQL_C_SLONG, &charOctetLength, 0, &cbCharOctetLength);
00603    SQLBindCol(hstmt, 17, SQL_C_SLONG, &ordinalPosition, 0, &cbOrdinalPosition);
00604    SQLBindCol(hstmt, 18, SQL_C_CHAR, szIsNullable, STR_LEN, &cbIsNullable);
00605 
00606    retcode = SQLFetch(hstmt);
00607 
00608    while ((retcode==SQL_SUCCESS) || (retcode==SQL_SUCCESS_WITH_INFO)) {
00609 
00610       Int_t sqltype = kSQL_NONE;
00611 
00612       Int_t data_size = -1;    // size in bytes
00613       Int_t data_length = -1;  // declaration like VARCHAR(n) or NUMERIC(n)
00614       Int_t data_scale = -1;   // second argument in declaration
00615       Int_t data_sign = -1; // no info about sign
00616 
00617       switch (dataType) {
00618          case SQL_CHAR:
00619             sqltype = kSQL_CHAR;
00620             data_size = columnSize;
00621             data_length = charOctetLength;
00622             break;
00623          case SQL_VARCHAR:
00624          case SQL_LONGVARCHAR:
00625             sqltype = kSQL_VARCHAR;
00626             data_size = columnSize;
00627             data_length = charOctetLength;
00628             break;
00629          case SQL_DECIMAL:
00630          case SQL_NUMERIC:
00631             sqltype = kSQL_NUMERIC;
00632             data_size = columnSize; // size of column in database
00633             data_length = columnSize;
00634             data_scale = decimalDigits;
00635             break;
00636          case SQL_INTEGER:
00637          case SQL_TINYINT:
00638          case SQL_BIGINT:
00639             sqltype = kSQL_INTEGER;
00640             data_size = columnSize;
00641             break;
00642          case SQL_REAL:
00643          case SQL_FLOAT:
00644             sqltype = kSQL_FLOAT;
00645             data_size = columnSize;
00646             data_sign = 1;
00647             break;
00648          case SQL_DOUBLE:
00649             sqltype = kSQL_DOUBLE;
00650             data_size = columnSize;
00651             data_sign = 1;
00652             break;
00653          case SQL_BINARY:
00654          case SQL_VARBINARY:
00655          case SQL_LONGVARBINARY:
00656             sqltype = kSQL_BINARY;
00657             data_size = columnSize;
00658             break;
00659          case SQL_TYPE_TIMESTAMP:
00660             sqltype = kSQL_TIMESTAMP;
00661             data_size = columnSize;
00662             break;
00663       }
00664 
00665       if (lst==0) lst = new TList;
00666 
00667       lst->Add(new TSQLColumnInfo((const char*) szColumnName,
00668                                   (const char*) szTypeName,
00669                                   nullable!=0,
00670                                   sqltype,
00671                                   data_size,
00672                                   data_length,
00673                                   data_scale,
00674                                   data_sign));
00675 
00676       retcode = SQLFetch(hstmt);
00677    }
00678 
00679    SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
00680 
00681    return new TSQLTableInfo(tablename, lst);
00682 }
00683 
00684 //______________________________________________________________________________
00685 TSQLResult *TODBCServer::GetColumns(const char*, const char *table, const char*)
00686 {
00687    // List all columns in specified table in the specified database.
00688    // Wild is for wildcarding "t%" list all columns starting with "t".
00689    // Returns a pointer to a TSQLResult object if successful, 0 otherwise.
00690    // The result object must be deleted by the user.
00691 
00692    CheckConnect("GetColumns", 0);
00693 
00694    SQLRETURN    retcode;
00695    SQLHSTMT     hstmt;
00696 
00697    SQLAllocHandle(SQL_HANDLE_STMT, fHdbc, &hstmt);
00698 
00699    retcode = SQLColumns(hstmt, NULL, 0, NULL, 0, (SQLCHAR*) table, SQL_NTS, NULL, 0);
00700    if (ExtractErrors(retcode, "GetColumns")) {
00701       SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
00702       return 0;
00703    }
00704 
00705    return new TODBCResult(hstmt);
00706 }
00707 
00708 //______________________________________________________________________________
00709 Int_t TODBCServer::GetMaxIdentifierLength()
00710 {
00711    // returns maximum allowed length of identifier (table name, column name, index name)
00712 
00713    CheckConnect("GetMaxIdentifierLength", 20);
00714 
00715    SQLUINTEGER info = 0;
00716    SQLRETURN retcode;
00717 
00718    retcode = SQLGetInfo(fHdbc, SQL_MAX_IDENTIFIER_LEN, (SQLPOINTER)&info, sizeof(info), NULL);
00719 
00720    if (ExtractErrors(retcode, "GetMaxIdentifierLength")) return 20;
00721 
00722    return info;
00723 }
00724 
00725 //______________________________________________________________________________
00726 Int_t TODBCServer::CreateDataBase(const char*)
00727 {
00728    // Create a database. Returns 0 if successful, non-zero otherwise.
00729 
00730    CheckConnect("CreateDataBase", -1);
00731 
00732    return -1;
00733 }
00734 
00735 //______________________________________________________________________________
00736 Int_t TODBCServer::DropDataBase(const char*)
00737 {
00738    // Drop (i.e. delete) a database. Returns 0 if successful, non-zero
00739    // otherwise.
00740 
00741    CheckConnect("DropDataBase", -1);
00742 
00743    return -1;
00744 }
00745 
00746 //______________________________________________________________________________
00747 Int_t TODBCServer::Reload()
00748 {
00749    // Reload permission tables. Returns 0 if successful, non-zero
00750    // otherwise. User must have reload permissions.
00751 
00752    CheckConnect("Reload", -1);
00753 
00754    return -1;
00755 }
00756 
00757 //______________________________________________________________________________
00758 Int_t TODBCServer::Shutdown()
00759 {
00760    // Shutdown the database server. Returns 0 if successful, non-zero
00761    // otherwise. User must have shutdown permissions.
00762 
00763    CheckConnect("Shutdown", -1);
00764 
00765    return -1;
00766 }
00767 
00768 //______________________________________________________________________________
00769 const char *TODBCServer::ServerInfo()
00770 {
00771    // Return server info.
00772 
00773    CheckConnect("ServerInfo", 0);
00774 
00775    return fServerInfo;
00776 }
00777 
00778 //______________________________________________________________________________
00779 TSQLStatement *TODBCServer::Statement(const char *sql, Int_t bufsize)
00780 {
00781    // Creates ODBC statement for provided query.
00782    // See TSQLStatement class for more details.
00783 
00784    CheckConnect("Statement", 0);
00785 
00786    if (!sql || !*sql) {
00787       SetError(-1, "no query string specified", "Statement");
00788       return 0;
00789    }
00790 
00791 //   SQLUINTEGER info = 0;
00792 //   SQLGetInfo(fHdbc, SQL_PARAM_ARRAY_ROW_COUNTS, (SQLPOINTER)&info, sizeof(info), NULL);
00793 //   if (info==SQL_PARC_BATCH) Info("Statement","info==SQL_PARC_BATCH"); else
00794 //   if (info==SQL_PARC_NO_BATCH) Info("Statement","info==SQL_PARC_NO_BATCH"); else
00795 //    Info("Statement","info==%u", info);
00796 
00797 
00798    SQLRETURN    retcode;
00799    SQLHSTMT     hstmt;
00800 
00801    retcode = SQLAllocHandle(SQL_HANDLE_STMT, fHdbc, &hstmt);
00802    if (ExtractErrors(retcode, "Statement")) return 0;
00803 
00804    retcode = SQLPrepare(hstmt, (SQLCHAR*) sql, SQL_NTS);
00805    if (ExtractErrors(retcode, "Statement")) {
00806       SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
00807       return 0;
00808    }
00809 
00810    return new TODBCStatement(hstmt, bufsize, fErrorOut);
00811 }
00812 
00813 //______________________________________________________________________________
00814 Bool_t TODBCServer::StartTransaction()
00815 {
00816    // Starts transaction.
00817    // Check for transaction support.
00818    // Switch off autocommitment mode.
00819 
00820    CheckConnect("StartTransaction", kFALSE);
00821 
00822    SQLUINTEGER info = 0;
00823    SQLRETURN retcode;
00824 
00825    retcode = SQLGetInfo(fHdbc, SQL_TXN_CAPABLE, (SQLPOINTER)&info, sizeof(info), NULL);
00826    if (ExtractErrors(retcode, "StartTransaction")) return kFALSE;
00827 
00828    if (info==0) {
00829       SetError(-1,"Transactions not supported","StartTransaction");
00830       return kFALSE;
00831    }
00832 
00833    if (!Commit()) return kFALSE;
00834 
00835    retcode = SQLSetConnectAttr(fHdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_OFF, 0);
00836    if (ExtractErrors(retcode, "StartTransaction")) return kFALSE;
00837 
00838    return kTRUE;
00839 }
00840 
00841 //______________________________________________________________________________
00842 Bool_t TODBCServer::EndTransaction(Bool_t commit)
00843 {
00844    // Complete current transaction (commit = kTRUE) or rollback
00845    // Switches on autocommit mode of ODBC driver
00846 
00847    const char* method = commit ? "Commit" : "Rollback";
00848 
00849    CheckConnect(method, kFALSE);
00850 
00851    SQLRETURN retcode = SQLEndTran(SQL_HANDLE_DBC, fHdbc, commit ? SQL_COMMIT : SQL_ROLLBACK);
00852    if (ExtractErrors(retcode, method)) return kFALSE;
00853 
00854    retcode = SQLSetConnectAttr(fHdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_ON, 0);
00855 
00856    return kTRUE;
00857 }
00858 
00859 //______________________________________________________________________________
00860 Bool_t TODBCServer::Commit()
00861 {
00862    // Commit transaction
00863 
00864    return EndTransaction(kTRUE);
00865 }
00866 
00867 //______________________________________________________________________________
00868 Bool_t TODBCServer::Rollback()
00869 {
00870    // Rollback transaction
00871 
00872    return EndTransaction(kFALSE);
00873 }

Generated on Tue Jul 5 15:14:38 2011 for ROOT_528-00b_version by  doxygen 1.5.1