// @(#)root/net:$Id: TSQLStatement.cxx 23091 2008-04-09 15:04:27Z rdm $
// 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.             *
 *************************************************************************/

////////////////////////////////////////////////////////////////////////////////
//                                                                      
// TSQLStatement                                                        
//                                                                      
// Abstract base class defining SQL statements, which can be submitted
// in bulk to DB server.
//                                                                      
// This is alternative to TSQLServer::Query() method, which allows only pure
// text queries and pure text result in TSQLResult classes.
// TSQLStatement is designed to support following features:
//   - usage of basic data type (like int or double) as parameters 
//     in SQL statements
//   - bulk operation when inserting/updating/selecting data in data base
//   - uasge of basic data types when accessing result set of executed query
//
//
// 1. Creation of statement
// ======================================
// To create instance of TSQLStatement class, TSQLServer::Statement() method
// should be used. Depending of the driver, used for connection to ODBC,
// appropriate object instance will be created. For the moment there are
// three different implementation of TSQLStatement class: for MySQL, 
// Oracle and ODBC. Hopefully, support of ODBC will allows usage of
// statements for most existing RDBMS.
//
//   // first connect to data base
//   TSQLServer* serv = TSQLServer::Connect("mysql://hostname.domain:3306/test",
//                                          "user", "pass");
//   // check if connection is ok
//   if ((serv!=0) && serv->IsConnected()) {
//       // create statement instance     
//       TSQLStatement* stmt = serv->Statement("CREATE TABLE TESTTABLE (ID1 INT, ID2 INT, FFIELD VARCHAR(255), FVALUE VARCHAR(255))";
//       // process statement
//       stmt->Process();
//       // destroy object
//       delete stmt;
//   }
//   delete serv;
//
//
// 2. Insert data to data base
// ===============================================
// There is a special syntax of SQL queries, which allow to use values,
// provided as parameters. For instance, insert one row in TESTTABLE, created
// with previous example, one can simply execute query like: 
// 
//    serv->Query("INSERT INTO TESTTABLE VALUES (1, 2, \"name1\", \"value1\"");
//
// But when many (100-1000) rows should be inserted, each call of 
// TSQLServer::Query() method will cause communication loop with database
// server. As a result, insertion of data will takes too much time.
//
// TSQLStatement provides a mechanism to insert many rows at once. First of all,
// appropriate statement should be created:
//
//    TSQLStatement* stmt = serv->Statement("INSERT INTO TESTTABLE (ID1, ID2, FFIELD, FVALUE) VALUES (?, ?, ?, ?)", 100);
//
// Here question marks "?" indicates where statement parameters can be inserted.
// To specify values of parameters, SetInt(), SetDouble(), SetString() and other
// methods of TSQLStatement class should be used. Before parameters values
// can be specified, NextIteration() method of statement class should be called.
// For each new row first, NextIteration() called, that parameters values are
// specified. There is one limitation - once parameter set as integer via
// SetInt(), for all other rows should be specified as integer. At the end,
// TSQLStatement::Process() should be called. Here a small example:
//
//    // first, create statement  
//    TSQLStatement* stmt = serv->Statement("INSERT INTO TESTTABLE (ID1, ID2, FFIELD, FVALUE) VALUES (?, ?, ?, ?)", 100);
//
//    for (int n=0;n<357;n++) 
//       if (stmt->NextIteration()) {
//          stmt->SetInt(0, 123);
//          stmt->SetUInt(1, n+10);
//          stmt->SetString(2, Form("name %d",n), 200);
//          stmt->SetString(3, Form("value %d", n+10), 200);
//      }
//   
//     stmt->Process();
//     delete stmt;
//
// Second argument in TSQLServer::Statement() method specifies depth of 
// of buffers, used to keep parameter values (100 in example). It is not
// a limitation of rows number, which can be inserted with the statement.
// When buffers are filled, they will be submitted to database and can be
// reused again. This happens transparent to the user in NextIteration()
// method.
//
// Oracle and some ODBC drivers support buffering of parameter values and,
// as a result, bulk insert (update) operation. MySQL (native driver and
// MyODBC 3)  does not support such mode of operation, therefore adding
// new rows will result in communication loop to database.
//
// One should also mention difference between Oracle and ODBC SQL syntax for
// parameters. ODBC (and MySQL) uses question marks to specify position,
// where parameters should be inserted (as shown in the example). Oracle uses
// :1, :2 and so on marks for specify position of parameter 0, 1, and so on.
// Therefore, similar to example query will look like:
//
//    TSQLStatement* stmt = serv->Statement("INSERT INTO TESTTABLE (ID1, ID2, FFIELD, FVALUE) VALUES (:1, :2, :3, :4)", 100);
//  
// There is a possibility to set parameter value to NULL with SetNull() method.
// If this method called for first iteration, before one should call other Set...
// to identify actual parameter type, which will be used for parameter later.
//
//
// 3. Getting data from database
// =============================
// To request data from data base, SELECT statement should be used.
// After SELECT statement is created, it must be processed
// (with TSQLStatement::Process()) method and result of statement
// should be stored in internal buffers with TSQLStatement::StoreResult()
// method. Information about selected fields (columns)
// can be obtained with GetNumFields() and GetFieldName() methods. 
// To recieve data for next result row, NextResultRow() method should be called.
// Value from each column can be taken with the GetInt(), GetDouble(),
// GetString() and other methods. 
// 
// There are no strict limitation which method should be used
// to get column values. GetString() can be used as generic method, 
// which should always return correct result, but also convertion between most 
// basic data types are supported. For instance, if column contains integer
// values, GetInt(), GetLong64(), GetDouble() and GetString() methods can be used.
// If column has float point format, GetDouble() and GetString() methods can
// be used without loss of precision while GetInt() or GetLong64() will return
// integer part of the value. One also can test, if value is NULL with IsNull()
// method.
//
// Buffer length, specified for statement in TSQLServer::Statement() call,
// will also be used to allocate buffers for column values. Usage of these
// buffers is transparent for users and does not limit number of rows,
// which can be accessed with  one statement. Example of select query:
//
//    stmt = serv->Statement("SELECT * FROM TESTTABLE", 100);
//    // process statement
//    if (stmt->Process()) {
//       // store result of statement in buffer    
//       stmt->StoreResult();
//         
//       // display info about selected field 
//       cout << "NumFields = " << stmt->GetNumFields() << endl;
//       for (int n=0;n<stmt->GetNumFields();n++) 
//          cout << "Field " << n << "  = " << stmt->GetFieldName(n) << endl;
//
//       // extract rows one after another
//       while (stmt->NextResultRow()) {
//          Double_t id1 = stmt->GetDouble(0);
//          UInt_t id2 = stmt->GetUInt(1);
//          const char* name1 = stmt->GetString(2);
//          const char* name2 = stmt->GetString(3);
//          cout << id1 << " - " << id2 << "  " << name1 << "  " << name2 << endl;
//       }
//    }    
//
// 4. Working with date/time parameters
// ====================================
// Current implementation supports date, time, date&time and timestamp 
// data (all time intervals not supported yet). To set or get date/time values,
// following methods should be used:
//   SetTime()/GetTime() - only time (hour:min:sec), 
//   SetDate()/GetDate() - only date (year-month-day), 
//   SetDatime()/GetDatime() - date and time 
//   SetTimestamp()/GetTimestamp() - timestamp with seconds fraction
// For some of these methods TDatime type can be used as parameter / return value.
// Be aware, that TDatime supports only dates after 1995-01-01.
// There are also methods to get separately year, month, day, hour, minutes and seconds.
//
// Different SQL databases has different treatement of date/time types.
// For instance, MySQL has all correspondent types (TIME, DATE, DATETIME and TIMESTAMP),
// Oracle native driver supports only DATE (which is actually date and time) and TIMESTAMP
// ODBC interface provides access for time, date and timestamps.
// Therefore, one should use correct methods to access such data.
// For instance, in MySQL SQL type 'DATE' is only date (one should use GetDate() to 
// access such data), while in Oracle it is date and time. Therefore, 
// to get complete data from 'DATE' column in Oracle, one should use GetDatime() method.
//
// The only difference of timestamp from date/time, that it has fractional
// seconds part. Be aware, that fractional part can has different meaning 
// (actual value) in different SQL plugins.
//
// 5. Binary data
// ==============
// Most of modern data bases support just binary data, which is
// typically has SQL type name 'BLOB'. To access data in such
// columns, GetBinary()/SetBinary() methods should be used. 
// Current implementation supposed, that complete content of the 
// column must be retrieved at once. Therefore very big data of 
// gigabytes size may cause a problem.
//
////////////////////////////////////////////////////////////////////////////////

#include "TSQLStatement.h"

ClassImp(TSQLStatement)

//______________________________________________________________________________
Int_t TSQLStatement::GetErrorCode() const
{
   // returns error code of last operation
   // if res==0, no error
   // Each specific implementation of TSQLStatement provides its own error coding
   
   return fErrorCode;
}

//______________________________________________________________________________
const char* TSQLStatement::GetErrorMsg() const
{
   //  returns error message of last operation
   // if no errors, return 0
   // Each specific implementation of TSQLStatement provides its own error messages
   
   return GetErrorCode()==0 ? 0 : fErrorMsg.Data();
}

//______________________________________________________________________________
void TSQLStatement::ClearError()
{
   // reset error fields
   
   fErrorCode = 0;
   fErrorMsg = "";
}

//______________________________________________________________________________
void TSQLStatement::SetError(Int_t code, const char* msg, const char* method)
{
   // set new values for error fields
   // if method specified, displays error message
   
   fErrorCode = code;
   fErrorMsg = msg;
   if ((method!=0) && fErrorOut)
      Error(method,"Code: %d  Msg: %s", code, (msg ? msg : "No message"));
}

//______________________________________________________________________________
Bool_t TSQLStatement::SetDate(Int_t npar, const TDatime& tm)
{
   // set only date value for specified parameter from TDatime object
   
   return SetDate(npar, tm.GetYear(), tm.GetMonth(), tm.GetDay());
}

//______________________________________________________________________________
Bool_t TSQLStatement::SetTime(Int_t npar, const TDatime& tm)
{
   // set only time value for specified parameter from TDatime object
   
   return SetTime(npar, tm.GetHour(), tm.GetMinute(), tm.GetSecond());
}

//______________________________________________________________________________
Bool_t TSQLStatement::SetDatime(Int_t npar, const TDatime& tm)
{
   // set date & time value for specified parameter from TDatime object
   
   return SetDatime(npar, tm.GetYear(), tm.GetMonth(), tm.GetDay(),
                          tm.GetHour(), tm.GetMinute(), tm.GetSecond());
}

//______________________________________________________________________________
Bool_t TSQLStatement::SetTimestamp(Int_t npar, const TDatime& tm)
{
   // set timestamp value for specified parameter from TDatime object
   
   return SetTimestamp(npar, tm.GetYear(), tm.GetMonth(), tm.GetDay(),
                             tm.GetHour(), tm.GetMinute(), tm.GetSecond(), 0);
}

//______________________________________________________________________________
TDatime TSQLStatement::GetDatime(Int_t npar)
{
   // return value of parameter in form of TDatime
   // Be aware, that TDatime does not allow dates before 1995-01-01 
   
   Int_t year, month, day, hour, min, sec;
   
   if (!GetDatime(npar, year, month, day, hour, min, sec))
     return TDatime();
   
   if (year<1995) {
      SetError(-1, "Date before year 1995 does not supported by TDatime type", "GetDatime");
      return TDatime();
   }
   
   return TDatime(year, month, day, hour, min, sec);
}

//______________________________________________________________________________
Int_t TSQLStatement::GetYear(Int_t npar)
{
   // return year value for parameter (if applicable) 
   
   Int_t year, month, day, hour, min, sec, frac;
   if (GetDate(npar, year, month, day)) return year;
   if (GetTimestamp(npar, year, month, day, hour, min, sec, frac)) return year;
   return 0;
}

//______________________________________________________________________________
Int_t TSQLStatement::GetMonth(Int_t npar)
{
   // return month value for parameter (if applicable) 

   Int_t year, month, day, hour, min, sec, frac;
   if (GetDate(npar, year, month, day)) return month;
   if (GetTimestamp(npar, year, month, day, hour, min, sec, frac)) return month;
   return 0;
}

//______________________________________________________________________________
Int_t TSQLStatement::GetDay(Int_t npar)
{
   // return day value for parameter (if applicable) 

   Int_t year, month, day, hour, min, sec, frac;
   if (GetDate(npar, year, month, day)) return day;
   if (GetTimestamp(npar, year, month, day, hour, min, sec, frac)) return day;
   return 0;
}

//______________________________________________________________________________
Int_t TSQLStatement::GetHour(Int_t npar)
{
   // return hours value for parameter (if applicable) 

   Int_t year, month, day, hour, min, sec, frac;
   if (GetTime(npar, hour, min, sec)) return hour;
   if (GetTimestamp(npar, year, month, day, hour, min, sec, frac)) return hour;
   return 0;
}

//______________________________________________________________________________
Int_t TSQLStatement::GetMinute(Int_t npar)
{
   // return minutes value for parameter (if applicable) 

   Int_t year, month, day, hour, min, sec, frac;
   if (GetTime(npar, hour, min, sec)) return min;
   if (GetTimestamp(npar, year, month, day, hour, min, sec, frac)) return min;
   return 0;
}

//______________________________________________________________________________
Int_t TSQLStatement::GetSecond(Int_t npar)
{
   // return seconds value for parameter (if applicable) 

   Int_t year, month, day, hour, min, sec, frac;
   if (GetTime(npar, hour, min, sec)) return sec;
   if (GetTimestamp(npar, year, month, day, hour, min, sec, frac)) return sec;
   return 0;
}

//______________________________________________________________________________
TDatime TSQLStatement::GetTimestamp(Int_t npar)
{
   // return value of parameter in form of TDatime
   // Be aware, that TDatime does not allow dates before 1995-01-01 
   
   Int_t year, month, day, hour, min, sec, frac;
   
   if (!GetTimestamp(npar, year, month, day, hour, min, sec, frac))
     return TDatime();
   
   if (year<1995) {
      SetError(-1, "Date before year 1995 does not supported by TDatime type", "GetTimestamp");
      return TDatime();
   }
   
   return TDatime(year, month, day, hour, min, sec);
}


Last change: Wed Jun 25 08:52:40 2008
Last generated: 2008-06-25 08:52

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.