Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TSQLStatement.cxx
Go to the documentation of this file.
1// @(#)root/net:$Id$
2// Author: Sergey Linev 6/02/2006
3
4/*************************************************************************
5 * Copyright (C) 1995-2006, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12////////////////////////////////////////////////////////////////////////////////
13///
14/// Abstract base class defining SQL statements, which can be submitted
15/// in bulk to DB server.
16///
17/// This is alternative to TSQLServer::Query() method, which allows only pure
18/// text queries and pure text result in TSQLResult classes.
19/// TSQLStatement is designed to support following features:
20/// - usage of basic data types (like int or double) as parameters
21/// in SQL statements
22/// - bulk operation when inserting/updating/selecting data in database
23/// - uasge of basic data types when accessing result set of executed query
24///
25///
26/// 1. Creation of statement
27/// ======================================
28/// To create an instance of the TSQLStatement class, the TSQLServer::Statement() method
29/// should be used. Depending on the driver used for an ODBC connection,
30/// the appropriate object instance will be created. For the moment there are
31/// six different implementations of the TSQLStatement class: for MySQL,
32/// Oracle, PostgreSQL, SQLite3 and ODBC. Hopefully, support of ODBC will allow usage of
33/// statements for most existing RDBMS.
34///
35/// // first, connect to the database
36/// TSQLServer* serv = TSQLServer::Connect("mysql://hostname.domain:3306/test",
37/// "user", "pass");
38/// // check if connection is ok
39/// if ((serv!=0) && serv->IsConnected()) {
40/// // create instance of sql-statement
41/// TSQLStatement* stmt = serv->Statement("CREATE TABLE TESTTABLE (ID1 INT, ID2 INT, FFIELD VARCHAR(255), FVALUE VARCHAR(255))";
42/// // process statement
43/// stmt->Process();
44/// // destroy object
45/// delete stmt;
46/// }
47/// delete serv;
48///
49///
50/// 2. Insert data to data base
51/// ===============================================
52/// There is a special syntax of SQL queries which allows to use values
53/// provided as parameters. For instance, to insert one row into the TESTTABLE created
54/// with the previous example, one can simply execute a query like:
55///
56/// serv->Query("INSERT INTO TESTTABLE VALUES (1, 2, \"name1\", \"value1\"");
57///
58/// However, when many (100-1000) rows should be inserted, each call of
59/// TSQLServer::Query() method will cause communication loop with database
60/// server, and the statement has to be evaluated each time instead of using a prepared statement.
61/// As a result, insertion of data takes too much time.
62///
63/// TSQLStatement provides a mechanism to insert many rows at once.
64/// First of all, an appropriate statement should be created:
65///
66/// TSQLStatement* stmt = serv->Statement("INSERT INTO TESTTABLE (ID1, ID2, FFIELD, FVALUE) VALUES (?, ?, ?, ?)", 100);
67///
68/// Here question marks "?" indicate where statement parameters can be inserted.
69/// To specify values of parameters, SetInt(), SetDouble(), SetString() and other
70/// methods of the TSQLStatement class should be used. Before parameters values
71/// can be specified, the NextIteration() method of statement class should be called.
72/// For each new row, NextIteration() has to be called first, then parameters values are
73/// specified. There is one limitation for most type-aware DBMS - once a parameter is set as integer via
74/// SetInt(), all other rows should be specified as integer. At the end,
75/// TSQLStatement::Process() should be called. Here a small example:
76///
77/// // first, create statement
78/// TSQLStatement* stmt = serv->Statement("INSERT INTO TESTTABLE (ID1, ID2, FFIELD, FVALUE) VALUES (?, ?, ?, ?)", 100);
79///
80/// for (int n=0;n<357;n++)
81/// if (stmt->NextIteration()) {
82/// stmt->SetInt(0, 123);
83/// stmt->SetUInt(1, n+10);
84/// stmt->SetString(2, Form("name %d",n), 200);
85/// stmt->SetString(3, Form("value %d", n+10), 200);
86/// }
87///
88/// stmt->Process();
89/// delete stmt;
90///
91/// The second argument in the TSQLServer::Statement() method specifies the depth of
92/// of buffers which are used to keep parameter values (100 in the example). It is not
93/// a limitation of the number of rows which can be inserted with the statement.
94/// When buffers are filled, they will be submitted to database and can be
95/// reused again. This happens transparent to the user in the NextIteration()
96/// method.
97///
98/// Oracle and some ODBC drivers support buffering of parameter values and,
99/// as a result, bulk insert (update) operation. MySQL (native driver and
100/// MyODBC 3) does not support such a mode of operation, therefore adding
101/// new rows will result in communication loop to database.
102///
103/// Local databases (SQLite3) do not use any buffering at all in the TSQLStatement
104/// implementation (but inside the library). They still profit from the
105/// usage of prepared statements. When inserting many rows into a SQLite3 database,
106/// consider using a transaction via the methods StartTransaction() and Commit()
107/// of the TSQLServer, as autocommit is active by default and causes a sync to disk
108/// after each single insert.
109///
110/// One should also mention differences between Oracle and ODBC SQL syntax for
111/// parameters. ODBC (and MySQL) use question marks to specify the position
112/// where parameters should be inserted (as shown in the example). Oracle uses
113/// :1, :2 and so on as marks to specify the position of parameter 0, 1, and so on.
114/// Therefore, similar to the example, a query will look like:
115///
116/// TSQLStatement* stmt = serv->Statement("INSERT INTO TESTTABLE (ID1, ID2, FFIELD, FVALUE) VALUES (:1, :2, :3, :4)", 100);
117///
118/// SQLite3 supports both these syntaxes and some more.
119///
120/// There is a possibility to set a parameter value to NULL with the SetNull() method.
121/// If this method is to be called for the first iteration, one should first call another Set...
122/// method to identify the actual type which will be used for the parameter later.
123///
124///
125/// 3. Getting data from database
126/// =============================
127/// To request data from a database, the SELECT statement should be used.
128/// After a SELECT statement is created, it must be processed
129/// with the TSQLStatement::Process() method and the result of statement
130/// should be stored in internal buffers with the method TSQLStatement::StoreResult().
131/// Information about selected fields (columns)
132/// can be obtained with GetNumFields() and GetFieldName() methods.
133/// To receive data for the next result row, NextResultRow() method should be called.
134/// Value from each column can be retrieved with the GetInt(), GetDouble(),
135/// GetString() and other methods.
136///
137/// There are no strict limitations on which method should be used
138/// to get column values. GetString() can be used as a generic method,
139/// which should always return correct result, but also conversions between most
140/// basic data types are supported. For instance, if a column contains integer
141/// values, GetInt(), GetLong64(), GetDouble() and GetString() methods can be used.
142/// If column has floating point format, GetDouble() and GetString() methods can
143/// be used without loss of precision while GetInt() or GetLong64() will return
144/// only the integer part of the value. One also can test whether
145/// a value is NULL with the IsNull() method.
146///
147/// The buffer length specified for a statement in the TSQLServer::Statement() call
148/// will also be used to allocate buffers for column values. Usage of these
149/// buffers is transparent for users and does not limit the number of rows
150/// which can be accessed with one statement. Again, local databases do not work
151/// with buffers inside TSQLStatement at all and ignore this value.
152/// Example of select query:
153///
154/// stmt = serv->Statement("SELECT * FROM TESTTABLE", 100);
155/// // process statement
156/// if (stmt->Process()) {
157/// // store result of statement in buffer
158/// stmt->StoreResult();
159///
160/// // display info about selected field
161/// std::cout << "NumFields = " << stmt->GetNumFields() << std::endl;
162/// for (int n=0;n<stmt->GetNumFields();n++)
163/// std::cout << "Field " << n << " = " << stmt->GetFieldName(n) << std::endl;
164///
165/// // extract rows one after another
166/// while (stmt->NextResultRow()) {
167/// Double_t id1 = stmt->GetDouble(0);
168/// UInt_t id2 = stmt->GetUInt(1);
169/// const char* name1 = stmt->GetString(2);
170/// const char* name2 = stmt->GetString(3);
171/// std::cout << id1 << " - " << id2 << " " << name1 << " " << name2 << std::endl;
172/// }
173/// }
174///
175/// 4. Working with date/time parameters
176/// ====================================
177/// The current implementation supports date, time, date&time and timestamp
178/// data (all time intervals are not supported yet). To set or get date/time values,
179/// the following methods should be used:
180/// SetTime()/GetTime() - only time (hour:min:sec),
181/// SetDate()/GetDate() - only date (year-month-day),
182/// SetDatime()/GetDatime() - date and time
183/// SetTimestamp()/GetTimestamp() - timestamp with seconds fraction
184/// For some of these methods TDatime type can be used as parameter / return value.
185/// Be aware that TDatime supports only dates after 1995-01-01.
186/// There are also methods to get year, month, day, hour, minutes and seconds separately.
187///
188/// Note that different SQL databases treat date/time types differently.
189/// For instance, MySQL has all correspondent types (TIME, DATE, DATETIME and TIMESTAMP),
190/// Oracle native driver supports only DATE (which is actually date and time) and TIMESTAMP
191/// ODBC interface provides access for time, date and timestamps,
192/// for PostgreSQL, TIMESTAMP is available and can be retrieved via all methods,
193/// the implementation for SQLite interprets the column content as
194/// a timestamp with second fraction.
195/// Due to these differences, one should use correct methods to access such data.
196/// For instance, in MySQL SQL type 'DATE' is only date (one should use GetDate() to
197/// access such data), while in Oracle it is date and time. Therefore,
198/// to get complete data from a 'DATE' column in Oracle, one should use the GetDatime() method.
199///
200/// The only difference between timestamp and date/time is that timestamp has a fractional
201/// seconds part. Be aware that the fractional part has different meanings
202/// (actual value) in different SQL plugins.
203/// For PostgreSQL, it is given back as microseconds, while for SQLite3,
204/// milliseconds correspond to the fraction (similar to the DATETIME-functions
205/// implemented in the SQLite3 language).
206///
207/// 5. Binary data
208/// ==============
209/// Most modern data bases support just binary data, which is
210/// typically has SQL type name 'BLOB'. To access data in such
211/// columns, GetBinary()/SetBinary() methods should be used.
212/// The current implementation implies that the complete content of the
213/// column must be retrieved at once. Therefore, very big data of
214/// gigabytes size may cause a problem.
215///
216/// In addition, for PostgresSQL, the methods GetLargeObject()/SetLargeObject()
217/// are implemented with similar syntax. They retrieve a large object for the OID
218/// given in the column of the statement. For non-PostgreSQL databases,
219/// calling GetLargeObject()/SetLargeObject() is redirected to GetBinary()/SetBinary().
220///
221/// Since ROOT 6.36, the GetLargeObject/GetBinary(Int_t col, void* &mem, Long_t& size) API
222/// is defined strictly to return new memory `mem` that must be released with
223/// `delete [] (unsigned char *) mem` by the caller.
224/// Older uses of this API (such as those of oracle, mysql, odbc) that relied on
225/// internal buffer management (owning pointers) are deprecated/no longer supported.
226///
227////////////////////////////////////////////////////////////////////////////////
228
229#include "TSQLStatement.h"
230
232
233////////////////////////////////////////////////////////////////////////////////
234/// returns error code of last operation
235/// if res==0, no error
236/// Each specific implementation of TSQLStatement provides its own error coding
237
239{
240 return fErrorCode;
241}
242
243////////////////////////////////////////////////////////////////////////////////
244/// returns error message of last operation
245/// if no errors, return 0
246/// Each specific implementation of TSQLStatement provides its own error messages
247
248const char* TSQLStatement::GetErrorMsg() const
249{
250 return GetErrorCode()==0 ? nullptr : fErrorMsg.Data();
251}
252
253////////////////////////////////////////////////////////////////////////////////
254/// reset error fields
255
257{
258 fErrorCode = 0;
259 fErrorMsg = "";
260}
261
262////////////////////////////////////////////////////////////////////////////////
263/// set new values for error fields
264/// if method specified, displays error message
265
266void TSQLStatement::SetError(Int_t code, const char* msg, const char* method)
267{
268 fErrorCode = code;
269 fErrorMsg = msg;
270 if (method && fErrorOut)
271 Error(method,"Code: %d Msg: %s", code, (msg ? msg : "No message"));
272}
273
274////////////////////////////////////////////////////////////////////////////////
275/// set only date value for specified parameter from TDatime object
276
278{
279 return SetDate(npar, tm.GetYear(), tm.GetMonth(), tm.GetDay());
280}
281
282////////////////////////////////////////////////////////////////////////////////
283/// set only time value for specified parameter from TDatime object
284
286{
287 return SetTime(npar, tm.GetHour(), tm.GetMinute(), tm.GetSecond());
288}
289
290////////////////////////////////////////////////////////////////////////////////
291/// set date & time value for specified parameter from TDatime object
292
294{
295 return SetDatime(npar, tm.GetYear(), tm.GetMonth(), tm.GetDay(),
296 tm.GetHour(), tm.GetMinute(), tm.GetSecond());
297}
298
299////////////////////////////////////////////////////////////////////////////////
300/// set timestamp value for specified parameter from TDatime object
301
303{
304 return SetTimestamp(npar, tm.GetYear(), tm.GetMonth(), tm.GetDay(),
305 tm.GetHour(), tm.GetMinute(), tm.GetSecond(), 0);
306}
307
308////////////////////////////////////////////////////////////////////////////////
309/// return value of parameter in form of TDatime
310/// Be aware, that TDatime does not allow dates before 1995-01-01
311
313{
314 Int_t year, month, day, hour, min, sec;
315
316 if (!GetDatime(npar, year, month, day, hour, min, sec))
317 return TDatime();
318
319 if (year<1995) {
320 SetError(-1, "Date before year 1995 does not supported by TDatime type", "GetDatime");
321 return TDatime();
322 }
323
324 return TDatime(year, month, day, hour, min, sec);
325}
326
327////////////////////////////////////////////////////////////////////////////////
328/// return year value for parameter (if applicable)
329
331{
332 Int_t year, month, day, hour, min, sec, frac;
333 if (GetDate(npar, year, month, day)) return year;
334 if (GetTimestamp(npar, year, month, day, hour, min, sec, frac)) return year;
335 return 0;
336}
337
338////////////////////////////////////////////////////////////////////////////////
339/// return month value for parameter (if applicable)
340
342{
343 Int_t year, month, day, hour, min, sec, frac;
344 if (GetDate(npar, year, month, day)) return month;
345 if (GetTimestamp(npar, year, month, day, hour, min, sec, frac)) return month;
346 return 0;
347}
348
349////////////////////////////////////////////////////////////////////////////////
350/// return day value for parameter (if applicable)
351
353{
354 Int_t year, month, day, hour, min, sec, frac;
355 if (GetDate(npar, year, month, day)) return day;
356 if (GetTimestamp(npar, year, month, day, hour, min, sec, frac)) return day;
357 return 0;
358}
359
360////////////////////////////////////////////////////////////////////////////////
361/// return hours value for parameter (if applicable)
362
364{
365 Int_t year, month, day, hour, min, sec, frac;
366 if (GetTime(npar, hour, min, sec)) return hour;
367 if (GetTimestamp(npar, year, month, day, hour, min, sec, frac)) return hour;
368 return 0;
369}
370
371////////////////////////////////////////////////////////////////////////////////
372/// return minutes value for parameter (if applicable)
373
375{
376 Int_t year, month, day, hour, min, sec, frac;
377 if (GetTime(npar, hour, min, sec)) return min;
378 if (GetTimestamp(npar, year, month, day, hour, min, sec, frac)) return min;
379 return 0;
380}
381
382////////////////////////////////////////////////////////////////////////////////
383/// return seconds value for parameter (if applicable)
384
386{
387 Int_t year, month, day, hour, min, sec, frac;
388 if (GetTime(npar, hour, min, sec)) return sec;
389 if (GetTimestamp(npar, year, month, day, hour, min, sec, frac)) return sec;
390 return 0;
391}
392
393////////////////////////////////////////////////////////////////////////////////
394/// return seconds fraction value for parameter (if applicable)
395
397{
398 Int_t year, month, day, hour, min, sec, frac;
399 if (GetTimestamp(npar, year, month, day, hour, min, sec, frac)) return frac;
400 return 0;
401}
402
403////////////////////////////////////////////////////////////////////////////////
404/// return value of parameter in form of TDatime
405/// Be aware, that TDatime does not allow dates before 1995-01-01
406
408{
409 Int_t year, month, day, hour, min, sec, frac;
410
411 if (!GetTimestamp(npar, year, month, day, hour, min, sec, frac))
412 return TDatime();
413
414 if (year<1995) {
415 SetError(-1, "Date before year 1995 does not supported by TDatime type", "GetTimestamp");
416 return TDatime();
417 }
418
419 return TDatime(year, month, day, hour, min, sec);
420}
421
422////////////////////////////////////////////////////////////////////////////////
423/// Set parameter as timestamp
424
429
430////////////////////////////////////////////////////////////////////////////////
431/// Set parameter as TTimeStamp
432
434{
435 Warning("SetTimestamp", "Not implemented for TTimeStamp");
436 return kFALSE;
437}
438
439////////////////////////////////////////////////////////////////////////////////
440/// Get parameter as timestamp
441
446
447////////////////////////////////////////////////////////////////////////////////
448/// Get parameter as TTimeStamp
449
451{
452 Warning("GetTimestamp", "Not implemented for TTimeStamp");
453 return kFALSE;
454}
455
constexpr Bool_t kFALSE
Definition RtypesCore.h:94
#define ClassImp(name)
Definition Rtypes.h:374
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
This class stores the date and time with a precision of one second in an unsigned 32 bit word (950130...
Definition TDatime.h:37
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition TObject.cxx:1057
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:1071
virtual Int_t GetErrorCode() const
returns error code of last operation if res==0, no error Each specific implementation of TSQLStatemen...
TString fErrorMsg
virtual Bool_t GetDatime(Int_t, Int_t &, Int_t &, Int_t &, Int_t &, Int_t &, Int_t &)
void SetError(Int_t code, const char *msg, const char *method=nullptr)
set new values for error fields if method specified, displays error message
Int_t GetDay(Int_t)
return day value for parameter (if applicable)
virtual Bool_t SetTimestamp(Int_t, Int_t, Int_t, Int_t, Int_t, Int_t, Int_t, Int_t=0)
Set parameter as timestamp.
virtual Bool_t SetDatime(Int_t, Int_t, Int_t, Int_t, Int_t, Int_t, Int_t)
virtual Bool_t GetDate(Int_t, Int_t &, Int_t &, Int_t &)
void ClearError()
reset error fields
Int_t GetMonth(Int_t)
return month value for parameter (if applicable)
Int_t GetSecondsFraction(Int_t)
return seconds fraction value for parameter (if applicable)
virtual Bool_t SetTime(Int_t, Int_t, Int_t, Int_t)
virtual Bool_t GetTime(Int_t, Int_t &, Int_t &, Int_t &)
Int_t GetSecond(Int_t)
return seconds value for parameter (if applicable)
Int_t GetMinute(Int_t)
return minutes value for parameter (if applicable)
virtual const char * GetErrorMsg() const
returns error message of last operation if no errors, return 0 Each specific implementation of TSQLSt...
Int_t GetYear(Int_t)
return year value for parameter (if applicable)
virtual Bool_t SetDate(Int_t, Int_t, Int_t, Int_t)
virtual Bool_t GetTimestamp(Int_t, Int_t &, Int_t &, Int_t &, Int_t &, Int_t &, Int_t &, Int_t &)
Get parameter as timestamp.
Int_t GetHour(Int_t)
return hours value for parameter (if applicable)
const char * Data() const
Definition TString.h:376
The TTimeStamp encapsulates seconds and ns since EPOCH.
Definition TTimeStamp.h:45