00001 // @(#)root/net:$Id: TSQLStatement.cxx 23091 2008-04-09 15:04:27Z rdm $ 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 //////////////////////////////////////////////////////////////////////////////// 00013 // 00014 // TSQLStatement 00015 // 00016 // Abstract base class defining SQL statements, which can be submitted 00017 // in bulk to DB server. 00018 // 00019 // This is alternative to TSQLServer::Query() method, which allows only pure 00020 // text queries and pure text result in TSQLResult classes. 00021 // TSQLStatement is designed to support following features: 00022 // - usage of basic data type (like int or double) as parameters 00023 // in SQL statements 00024 // - bulk operation when inserting/updating/selecting data in data base 00025 // - uasge of basic data types when accessing result set of executed query 00026 // 00027 // 00028 // 1. Creation of statement 00029 // ====================================== 00030 // To create instance of TSQLStatement class, TSQLServer::Statement() method 00031 // should be used. Depending of the driver, used for connection to ODBC, 00032 // appropriate object instance will be created. For the moment there are 00033 // three different implementation of TSQLStatement class: for MySQL, 00034 // Oracle and ODBC. Hopefully, support of ODBC will allows usage of 00035 // statements for most existing RDBMS. 00036 // 00037 // // first connect to data base 00038 // TSQLServer* serv = TSQLServer::Connect("mysql://hostname.domain:3306/test", 00039 // "user", "pass"); 00040 // // check if connection is ok 00041 // if ((serv!=0) && serv->IsConnected()) { 00042 // // create statement instance 00043 // TSQLStatement* stmt = serv->Statement("CREATE TABLE TESTTABLE (ID1 INT, ID2 INT, FFIELD VARCHAR(255), FVALUE VARCHAR(255))"; 00044 // // process statement 00045 // stmt->Process(); 00046 // // destroy object 00047 // delete stmt; 00048 // } 00049 // delete serv; 00050 // 00051 // 00052 // 2. Insert data to data base 00053 // =============================================== 00054 // There is a special syntax of SQL queries, which allow to use values, 00055 // provided as parameters. For instance, insert one row in TESTTABLE, created 00056 // with previous example, one can simply execute query like: 00057 // 00058 // serv->Query("INSERT INTO TESTTABLE VALUES (1, 2, \"name1\", \"value1\""); 00059 // 00060 // But when many (100-1000) rows should be inserted, each call of 00061 // TSQLServer::Query() method will cause communication loop with database 00062 // server. As a result, insertion of data will takes too much time. 00063 // 00064 // TSQLStatement provides a mechanism to insert many rows at once. First of all, 00065 // appropriate statement should be created: 00066 // 00067 // TSQLStatement* stmt = serv->Statement("INSERT INTO TESTTABLE (ID1, ID2, FFIELD, FVALUE) VALUES (?, ?, ?, ?)", 100); 00068 // 00069 // Here question marks "?" indicates where statement parameters can be inserted. 00070 // To specify values of parameters, SetInt(), SetDouble(), SetString() and other 00071 // methods of TSQLStatement class should be used. Before parameters values 00072 // can be specified, NextIteration() method of statement class should be called. 00073 // For each new row first, NextIteration() called, that parameters values are 00074 // specified. There is one limitation - once parameter set as integer via 00075 // SetInt(), for all other rows should be specified as integer. At the end, 00076 // TSQLStatement::Process() should be called. Here a small example: 00077 // 00078 // // first, create statement 00079 // TSQLStatement* stmt = serv->Statement("INSERT INTO TESTTABLE (ID1, ID2, FFIELD, FVALUE) VALUES (?, ?, ?, ?)", 100); 00080 // 00081 // for (int n=0;n<357;n++) 00082 // if (stmt->NextIteration()) { 00083 // stmt->SetInt(0, 123); 00084 // stmt->SetUInt(1, n+10); 00085 // stmt->SetString(2, Form("name %d",n), 200); 00086 // stmt->SetString(3, Form("value %d", n+10), 200); 00087 // } 00088 // 00089 // stmt->Process(); 00090 // delete stmt; 00091 // 00092 // Second argument in TSQLServer::Statement() method specifies depth of 00093 // of buffers, used to keep parameter values (100 in example). It is not 00094 // a limitation of rows number, which can be inserted with the statement. 00095 // When buffers are filled, they will be submitted to database and can be 00096 // reused again. This happens transparent to the user in NextIteration() 00097 // method. 00098 // 00099 // Oracle and some ODBC drivers support buffering of parameter values and, 00100 // as a result, bulk insert (update) operation. MySQL (native driver and 00101 // MyODBC 3) does not support such mode of operation, therefore adding 00102 // new rows will result in communication loop to database. 00103 // 00104 // One should also mention difference between Oracle and ODBC SQL syntax for 00105 // parameters. ODBC (and MySQL) uses question marks to specify position, 00106 // where parameters should be inserted (as shown in the example). Oracle uses 00107 // :1, :2 and so on marks for specify position of parameter 0, 1, and so on. 00108 // Therefore, similar to example query will look like: 00109 // 00110 // TSQLStatement* stmt = serv->Statement("INSERT INTO TESTTABLE (ID1, ID2, FFIELD, FVALUE) VALUES (:1, :2, :3, :4)", 100); 00111 // 00112 // There is a possibility to set parameter value to NULL with SetNull() method. 00113 // If this method called for first iteration, before one should call other Set... 00114 // to identify actual parameter type, which will be used for parameter later. 00115 // 00116 // 00117 // 3. Getting data from database 00118 // ============================= 00119 // To request data from data base, SELECT statement should be used. 00120 // After SELECT statement is created, it must be processed 00121 // (with TSQLStatement::Process()) method and result of statement 00122 // should be stored in internal buffers with TSQLStatement::StoreResult() 00123 // method. Information about selected fields (columns) 00124 // can be obtained with GetNumFields() and GetFieldName() methods. 00125 // To recieve data for next result row, NextResultRow() method should be called. 00126 // Value from each column can be taken with the GetInt(), GetDouble(), 00127 // GetString() and other methods. 00128 // 00129 // There are no strict limitation which method should be used 00130 // to get column values. GetString() can be used as generic method, 00131 // which should always return correct result, but also convertion between most 00132 // basic data types are supported. For instance, if column contains integer 00133 // values, GetInt(), GetLong64(), GetDouble() and GetString() methods can be used. 00134 // If column has float point format, GetDouble() and GetString() methods can 00135 // be used without loss of precision while GetInt() or GetLong64() will return 00136 // integer part of the value. One also can test, if value is NULL with IsNull() 00137 // method. 00138 // 00139 // Buffer length, specified for statement in TSQLServer::Statement() call, 00140 // will also be used to allocate buffers for column values. Usage of these 00141 // buffers is transparent for users and does not limit number of rows, 00142 // which can be accessed with one statement. Example of select query: 00143 // 00144 // stmt = serv->Statement("SELECT * FROM TESTTABLE", 100); 00145 // // process statement 00146 // if (stmt->Process()) { 00147 // // store result of statement in buffer 00148 // stmt->StoreResult(); 00149 // 00150 // // display info about selected field 00151 // cout << "NumFields = " << stmt->GetNumFields() << endl; 00152 // for (int n=0;n<stmt->GetNumFields();n++) 00153 // cout << "Field " << n << " = " << stmt->GetFieldName(n) << endl; 00154 // 00155 // // extract rows one after another 00156 // while (stmt->NextResultRow()) { 00157 // Double_t id1 = stmt->GetDouble(0); 00158 // UInt_t id2 = stmt->GetUInt(1); 00159 // const char* name1 = stmt->GetString(2); 00160 // const char* name2 = stmt->GetString(3); 00161 // cout << id1 << " - " << id2 << " " << name1 << " " << name2 << endl; 00162 // } 00163 // } 00164 // 00165 // 4. Working with date/time parameters 00166 // ==================================== 00167 // Current implementation supports date, time, date&time and timestamp 00168 // data (all time intervals not supported yet). To set or get date/time values, 00169 // following methods should be used: 00170 // SetTime()/GetTime() - only time (hour:min:sec), 00171 // SetDate()/GetDate() - only date (year-month-day), 00172 // SetDatime()/GetDatime() - date and time 00173 // SetTimestamp()/GetTimestamp() - timestamp with seconds fraction 00174 // For some of these methods TDatime type can be used as parameter / return value. 00175 // Be aware, that TDatime supports only dates after 1995-01-01. 00176 // There are also methods to get separately year, month, day, hour, minutes and seconds. 00177 // 00178 // Different SQL databases has different treatement of date/time types. 00179 // For instance, MySQL has all correspondent types (TIME, DATE, DATETIME and TIMESTAMP), 00180 // Oracle native driver supports only DATE (which is actually date and time) and TIMESTAMP 00181 // ODBC interface provides access for time, date and timestamps. 00182 // Therefore, one should use correct methods to access such data. 00183 // For instance, in MySQL SQL type 'DATE' is only date (one should use GetDate() to 00184 // access such data), while in Oracle it is date and time. Therefore, 00185 // to get complete data from 'DATE' column in Oracle, one should use GetDatime() method. 00186 // 00187 // The only difference of timestamp from date/time, that it has fractional 00188 // seconds part. Be aware, that fractional part can has different meaning 00189 // (actual value) in different SQL plugins. 00190 // 00191 // 5. Binary data 00192 // ============== 00193 // Most of modern data bases support just binary data, which is 00194 // typically has SQL type name 'BLOB'. To access data in such 00195 // columns, GetBinary()/SetBinary() methods should be used. 00196 // Current implementation supposed, that complete content of the 00197 // column must be retrieved at once. Therefore very big data of 00198 // gigabytes size may cause a problem. 00199 // 00200 //////////////////////////////////////////////////////////////////////////////// 00201 00202 #include "TSQLStatement.h" 00203 00204 ClassImp(TSQLStatement) 00205 00206 //______________________________________________________________________________ 00207 Int_t TSQLStatement::GetErrorCode() const 00208 { 00209 // returns error code of last operation 00210 // if res==0, no error 00211 // Each specific implementation of TSQLStatement provides its own error coding 00212 00213 return fErrorCode; 00214 } 00215 00216 //______________________________________________________________________________ 00217 const char* TSQLStatement::GetErrorMsg() const 00218 { 00219 // returns error message of last operation 00220 // if no errors, return 0 00221 // Each specific implementation of TSQLStatement provides its own error messages 00222 00223 return GetErrorCode()==0 ? 0 : fErrorMsg.Data(); 00224 } 00225 00226 //______________________________________________________________________________ 00227 void TSQLStatement::ClearError() 00228 { 00229 // reset error fields 00230 00231 fErrorCode = 0; 00232 fErrorMsg = ""; 00233 } 00234 00235 //______________________________________________________________________________ 00236 void TSQLStatement::SetError(Int_t code, const char* msg, const char* method) 00237 { 00238 // set new values for error fields 00239 // if method specified, displays error message 00240 00241 fErrorCode = code; 00242 fErrorMsg = msg; 00243 if ((method!=0) && fErrorOut) 00244 Error(method,"Code: %d Msg: %s", code, (msg ? msg : "No message")); 00245 } 00246 00247 //______________________________________________________________________________ 00248 Bool_t TSQLStatement::SetDate(Int_t npar, const TDatime& tm) 00249 { 00250 // set only date value for specified parameter from TDatime object 00251 00252 return SetDate(npar, tm.GetYear(), tm.GetMonth(), tm.GetDay()); 00253 } 00254 00255 //______________________________________________________________________________ 00256 Bool_t TSQLStatement::SetTime(Int_t npar, const TDatime& tm) 00257 { 00258 // set only time value for specified parameter from TDatime object 00259 00260 return SetTime(npar, tm.GetHour(), tm.GetMinute(), tm.GetSecond()); 00261 } 00262 00263 //______________________________________________________________________________ 00264 Bool_t TSQLStatement::SetDatime(Int_t npar, const TDatime& tm) 00265 { 00266 // set date & time value for specified parameter from TDatime object 00267 00268 return SetDatime(npar, tm.GetYear(), tm.GetMonth(), tm.GetDay(), 00269 tm.GetHour(), tm.GetMinute(), tm.GetSecond()); 00270 } 00271 00272 //______________________________________________________________________________ 00273 Bool_t TSQLStatement::SetTimestamp(Int_t npar, const TDatime& tm) 00274 { 00275 // set timestamp value for specified parameter from TDatime object 00276 00277 return SetTimestamp(npar, tm.GetYear(), tm.GetMonth(), tm.GetDay(), 00278 tm.GetHour(), tm.GetMinute(), tm.GetSecond(), 0); 00279 } 00280 00281 //______________________________________________________________________________ 00282 TDatime TSQLStatement::GetDatime(Int_t npar) 00283 { 00284 // return value of parameter in form of TDatime 00285 // Be aware, that TDatime does not allow dates before 1995-01-01 00286 00287 Int_t year, month, day, hour, min, sec; 00288 00289 if (!GetDatime(npar, year, month, day, hour, min, sec)) 00290 return TDatime(); 00291 00292 if (year<1995) { 00293 SetError(-1, "Date before year 1995 does not supported by TDatime type", "GetDatime"); 00294 return TDatime(); 00295 } 00296 00297 return TDatime(year, month, day, hour, min, sec); 00298 } 00299 00300 //______________________________________________________________________________ 00301 Int_t TSQLStatement::GetYear(Int_t npar) 00302 { 00303 // return year value for parameter (if applicable) 00304 00305 Int_t year, month, day, hour, min, sec, frac; 00306 if (GetDate(npar, year, month, day)) return year; 00307 if (GetTimestamp(npar, year, month, day, hour, min, sec, frac)) return year; 00308 return 0; 00309 } 00310 00311 //______________________________________________________________________________ 00312 Int_t TSQLStatement::GetMonth(Int_t npar) 00313 { 00314 // return month value for parameter (if applicable) 00315 00316 Int_t year, month, day, hour, min, sec, frac; 00317 if (GetDate(npar, year, month, day)) return month; 00318 if (GetTimestamp(npar, year, month, day, hour, min, sec, frac)) return month; 00319 return 0; 00320 } 00321 00322 //______________________________________________________________________________ 00323 Int_t TSQLStatement::GetDay(Int_t npar) 00324 { 00325 // return day value for parameter (if applicable) 00326 00327 Int_t year, month, day, hour, min, sec, frac; 00328 if (GetDate(npar, year, month, day)) return day; 00329 if (GetTimestamp(npar, year, month, day, hour, min, sec, frac)) return day; 00330 return 0; 00331 } 00332 00333 //______________________________________________________________________________ 00334 Int_t TSQLStatement::GetHour(Int_t npar) 00335 { 00336 // return hours value for parameter (if applicable) 00337 00338 Int_t year, month, day, hour, min, sec, frac; 00339 if (GetTime(npar, hour, min, sec)) return hour; 00340 if (GetTimestamp(npar, year, month, day, hour, min, sec, frac)) return hour; 00341 return 0; 00342 } 00343 00344 //______________________________________________________________________________ 00345 Int_t TSQLStatement::GetMinute(Int_t npar) 00346 { 00347 // return minutes value for parameter (if applicable) 00348 00349 Int_t year, month, day, hour, min, sec, frac; 00350 if (GetTime(npar, hour, min, sec)) return min; 00351 if (GetTimestamp(npar, year, month, day, hour, min, sec, frac)) return min; 00352 return 0; 00353 } 00354 00355 //______________________________________________________________________________ 00356 Int_t TSQLStatement::GetSecond(Int_t npar) 00357 { 00358 // return seconds value for parameter (if applicable) 00359 00360 Int_t year, month, day, hour, min, sec, frac; 00361 if (GetTime(npar, hour, min, sec)) return sec; 00362 if (GetTimestamp(npar, year, month, day, hour, min, sec, frac)) return sec; 00363 return 0; 00364 } 00365 00366 //______________________________________________________________________________ 00367 TDatime TSQLStatement::GetTimestamp(Int_t npar) 00368 { 00369 // return value of parameter in form of TDatime 00370 // Be aware, that TDatime does not allow dates before 1995-01-01 00371 00372 Int_t year, month, day, hour, min, sec, frac; 00373 00374 if (!GetTimestamp(npar, year, month, day, hour, min, sec, frac)) 00375 return TDatime(); 00376 00377 if (year<1995) { 00378 SetError(-1, "Date before year 1995 does not supported by TDatime type", "GetTimestamp"); 00379 return TDatime(); 00380 } 00381 00382 return TDatime(year, month, day, hour, min, sec); 00383 } 00384