Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RSqliteDS.cxx
Go to the documentation of this file.
1// Author: Jakob Blomer CERN 07/2018
2
3/*************************************************************************
4 * Copyright (C) 1995-2017, Rene Brun and Fons Rademakers. *
5 * All rights reserved. *
6 * *
7 * For the licensing terms see $ROOTSYS/LICENSE. *
8 * For the list of contributors see $ROOTSYS/README/CREDITS. *
9 *************************************************************************/
10
11#include <ROOT/RSqliteDS.hxx>
12#include <ROOT/RRawFile.hxx>
13
14#include "TError.h"
15#include "TRandom.h"
16#include "TSystem.h"
17
18#include <algorithm>
19#include <cctype>
20#include <cerrno>
21#include <cstring> // for memcpy
22#include <ctime>
23#include <memory> // for placement new
24#include <stdexcept>
25#include <utility>
26
27#include <sqlite3.h>
28
29namespace {
30
31// In order to provide direct access to remote sqlite files through HTTP and HTTPS, this datasource provides a custom
32// "SQlite VFS module" that uses Davix for data access. The SQlite VFS modules are roughly what TSystem is
33// for ROOT -- an abstraction of the operating system interface.
34//
35// SQlite allows for registering custom VFS modules, which are a set of C callback functions that SQlite invokes when
36// it needs to read from a file, write to a file, etc. More information is available under https://sqlite.org/vfs.html
37//
38// In the context of a data source, SQlite will only ever call reading functions from the VFS module, the sqlite
39// files are not modified. Therefore, only a subset of the callback functions provide a non-trivial implementation.
40// The custom VFS module uses a RRawFile for the byte access, thereby it can access local and remote files.
41
42////////////////////////////////////////////////////////////////////////////
43/// SQlite VFS modules are identified by string names. The name has to be unique for the entire application.
44constexpr char const *gSQliteVfsName = "ROOT-Davix-readonly";
45
46////////////////////////////////////////////////////////////////////////////
47/// Holds the state of an open sqlite database. Objects of this struct are created in VfsRdOnlyOpen()
48/// and then passed by sqlite to the file I/O callbacks (read, close, etc.). This uses C style inheritance
49/// where the struct starts with a sqlite3_file member (base class) which is extended by members related to
50/// this particular VFS module. Every callback here thus casts the sqlite3_file input parameter to its "derived"
51/// type VfsRootFile.
52struct VfsRootFile {
53 VfsRootFile() = default;
54
55 sqlite3_file pFile;
56 std::unique_ptr<ROOT::Internal::RRawFile> fRawFile;
57};
58
59// The following callbacks implement the I/O operations of an open database
60
61////////////////////////////////////////////////////////////////////////////
62/// Releases the resources associated to a file opened with davix
63int VfsRdOnlyClose(sqlite3_file *pFile)
64{
65 VfsRootFile *p = reinterpret_cast<VfsRootFile *>(pFile);
66 // We can't use delete because the storage for p is managed by sqlite
67 p->~VfsRootFile();
68 return SQLITE_OK;
69}
70
71////////////////////////////////////////////////////////////////////////////
72/// Issues a byte range request for a chunk to the raw file
73int VfsRdOnlyRead(sqlite3_file *pFile, void *zBuf, int count, sqlite_int64 offset)
74{
75 VfsRootFile *p = reinterpret_cast<VfsRootFile *>(pFile);
76 auto nbytes = p->fRawFile->ReadAt(zBuf, count, offset);
77 return (nbytes != static_cast<unsigned int>(count)) ? SQLITE_IOERR : SQLITE_OK;
78}
79
80////////////////////////////////////////////////////////////////////////////
81/// We do not write to a database in the RDataSource and therefore can simply return an error for this callback
82int VfsRdOnlyWrite(sqlite3_file * /*pFile*/, const void * /*zBuf*/, int /*iAmt*/, sqlite_int64 /*iOfst*/)
83{
84 return SQLITE_OPEN_READONLY;
85}
86
87////////////////////////////////////////////////////////////////////////////
88/// We do not write to a database in the RDataSource and therefore can simply return an error for this callback
89int VfsRdOnlyTruncate(sqlite3_file * /*pFile*/, sqlite_int64 /*size*/)
90{
91 return SQLITE_OPEN_READONLY;
92}
93
94////////////////////////////////////////////////////////////////////////////
95/// As the database is read-only, syncing data to disc is a no-op and always succeeds
96int VfsRdOnlySync(sqlite3_file * /*pFile*/, int /*flags*/)
97{
98 return SQLITE_OK;
99}
100
101////////////////////////////////////////////////////////////////////////////
102/// Returns the cached file size
103int VfsRdOnlyFileSize(sqlite3_file *pFile, sqlite_int64 *pSize)
104{
105 VfsRootFile *p = reinterpret_cast<VfsRootFile *>(pFile);
106 *pSize = p->fRawFile->GetSize();
107 return SQLITE_OK;
108}
109
110////////////////////////////////////////////////////////////////////////////
111/// As the database is read-only, locks for concurrent access are no-ops and always succeeds
112int VfsRdOnlyLock(sqlite3_file * /*pFile*/, int /*level*/)
113{
114 return SQLITE_OK;
115}
116
117////////////////////////////////////////////////////////////////////////////
118/// As the database is read-only, locks for concurrent access are no-ops and always succeeds
119int VfsRdOnlyUnlock(sqlite3_file * /*pFile*/, int /*level*/)
120{
121 return SQLITE_OK;
122}
123
124////////////////////////////////////////////////////////////////////////////
125/// As the database is read-only, locks for concurrent access are no-ops and always succeeds
126int VfsRdOnlyCheckReservedLock(sqlite3_file * /*pFile*/, int *pResOut)
127{
128 *pResOut = 0;
129 return SQLITE_OK;
130}
131
132////////////////////////////////////////////////////////////////////////////
133/// As the database is read-only, we know there are no additional control files such as a database journal
134int VfsRdOnlyFileControl(sqlite3_file * /*p*/, int /*op*/, void * /*pArg*/)
135{
136 return SQLITE_NOTFOUND;
137}
138
139////////////////////////////////////////////////////////////////////////////
140/// The database device's sector size is only needed for writing
141int VfsRdOnlySectorSize(sqlite3_file * /*pFile*/)
142{
143 return SQLITE_OPEN_READONLY;
144}
145
146////////////////////////////////////////////////////////////////////////////
147/// The database device's properties are only needed for writing
148int VfsRdOnlyDeviceCharacteristics(sqlite3_file * /*pFile*/)
149{
150 return SQLITE_OPEN_READONLY;
151}
152
153////////////////////////////////////////////////////////////////////////////
154/// Set the function pointers of the custom VFS I/O operations in a
155/// forward-compatible way
156sqlite3_io_methods GetSqlite3IoMethods()
157{
158 // The C style initialization is compatible with version 1 and later versions of the struct.
159 // Version 1 was introduced with sqlite 3.6, version 2 with sqlite 3.7.8, version 3 with sqlite 3.7.17
160 sqlite3_io_methods io_methods;
161 memset(&io_methods, 0, sizeof(io_methods));
162 io_methods.iVersion = 1;
163 io_methods.xClose = VfsRdOnlyClose;
164 io_methods.xRead = VfsRdOnlyRead;
165 io_methods.xWrite = VfsRdOnlyWrite;
166 io_methods.xTruncate = VfsRdOnlyTruncate;
167 io_methods.xSync = VfsRdOnlySync;
168 io_methods.xFileSize = VfsRdOnlyFileSize;
169 io_methods.xLock = VfsRdOnlyLock;
170 io_methods.xUnlock = VfsRdOnlyUnlock;
171 io_methods.xCheckReservedLock = VfsRdOnlyCheckReservedLock;
172 io_methods.xFileControl = VfsRdOnlyFileControl;
173 io_methods.xSectorSize = VfsRdOnlySectorSize;
174 io_methods.xDeviceCharacteristics = VfsRdOnlyDeviceCharacteristics;
175 return io_methods;
176}
177
178////////////////////////////////////////////////////////////////////////////
179/// Fills a new VfsRootFile struct enclosing a Davix file
180int VfsRdOnlyOpen(sqlite3_vfs * /*vfs*/, const char *zName, sqlite3_file *pFile, int flags, int * /*pOutFlags*/)
181{
182 // Storage for the VfsRootFile structure has been already allocated by sqlite, so we use placement new
183 VfsRootFile *p = new (pFile) VfsRootFile();
184 p->pFile.pMethods = nullptr;
185
186 // This global struct contains the function pointers to all the callback operations that act on an open database.
187 // It is passed via the pFile struct back to sqlite so that it can call back to the functions provided above.
188 static const sqlite3_io_methods io_methods = GetSqlite3IoMethods();
189
190 if (flags & (SQLITE_OPEN_READWRITE | SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_EXCLUSIVE))
191 return SQLITE_IOERR;
192
193 p->fRawFile = ROOT::Internal::RRawFile::Create(zName);
194 if (!p->fRawFile) {
195 ::Error("VfsRdOnlyOpen", "Cannot open %s\n", zName);
196 return SQLITE_IOERR;
197 }
198
199 p->pFile.pMethods = &io_methods;
200 return SQLITE_OK;
201}
202
203// The following callbacks implement operating system specific functionality. In contrast to the previous callbacks,
204// there is no need to implement any customized logic for the following ones. An implementation has to be
205// provided nevertheless to have a fully functional VFS module.
206
207////////////////////////////////////////////////////////////////////////////
208/// This VFS module cannot remove files
209int VfsRdOnlyDelete(sqlite3_vfs * /*vfs*/, const char * /*zName*/, int /*syncDir*/)
210{
211 return SQLITE_IOERR_DELETE;
212}
213
214////////////////////////////////////////////////////////////////////////////
215/// Access control always allows read-only access to databases
216int VfsRdOnlyAccess(sqlite3_vfs * /*vfs*/, const char * /*zPath*/, int flags, int *pResOut)
217{
218 *pResOut = 0;
219 if (flags == SQLITE_ACCESS_READWRITE) {
220 return SQLITE_OPEN_READONLY;
221 }
222 return SQLITE_OK;
223}
224
225////////////////////////////////////////////////////////////////////////////
226/// No distinction between relative and full paths for URLs, returns the input path name
227int VfsRdOnlyFullPathname(sqlite3_vfs * /*vfs*/, const char *zPath, int nOut, char *zOut)
228{
229 zOut[nOut - 1] = '\0';
230 sqlite3_snprintf(nOut, zOut, "%s", zPath);
231 return SQLITE_OK;
232}
233
234////////////////////////////////////////////////////////////////////////////
235/// Let TRandom fill the buffer with random bytes
236int VfsRdOnlyRandomness(sqlite3_vfs * /*vfs*/, int nBuf, char *zBuf)
237{
238 for (int i = 0; i < nBuf; ++i) {
239 zBuf[i] = (char)gRandom->Integer(256);
240 }
241 return nBuf;
242}
243
244////////////////////////////////////////////////////////////////////////////
245/// Use ROOT's platform independent sleep wrapper
246int VfsRdOnlySleep(sqlite3_vfs * /*vfs*/, int microseconds)
247{
248 // Millisecond precision but sleep at least number of given microseconds as requested
249 gSystem->Sleep((microseconds + 1000 - 1) / 1000);
250 return microseconds;
251}
252
253////////////////////////////////////////////////////////////////////////////
254/// Use sqlite default implementation
255int VfsRdOnlyGetLastError(sqlite3_vfs * /*vfs*/, int /*not_used1*/, char * /*not_used2*/)
256{
257 return errno;
258}
259
260////////////////////////////////////////////////////////////////////////////
261/// Return UTC as being done in the sqlite unix VFS without gettimeofday()
262int VfsRdOnlyCurrentTimeInt64(sqlite3_vfs * /*vfs*/, sqlite3_int64 *piNow)
263{
264 static constexpr sqlite3_int64 unixEpoch = 24405875 * (sqlite3_int64)8640000;
265 time_t t;
266 time(&t);
267 *piNow = ((sqlite3_int64)t) * 1000 + unixEpoch;
268 return SQLITE_OK;
269}
270
271////////////////////////////////////////////////////////////////////////////
272/// Wrapper around VfsRdOnlyCurrentTimeInt64
273int VfsRdOnlyCurrentTime(sqlite3_vfs *vfs, double *prNow)
274{
275 sqlite3_int64 i = 0;
276 int rc = VfsRdOnlyCurrentTimeInt64(vfs, &i);
277 *prNow = i / 86400000.0;
278 return rc;
279}
280
281////////////////////////////////////////////////////////////////////////////
282/// Set the function pointers of the VFS implementation in a
283/// forward-compatible way
284sqlite3_vfs GetSqlite3Vfs()
285{
286 // The C style initialization is compatible with version 1 and later versions of the struct.
287 // Version 1 was introduced with sqlite 3.5, version 2 with sqlite 3.7, version 3 with sqlite 3.7.6
288 sqlite3_vfs vfs;
289 memset(&vfs, 0, sizeof(vfs));
290 vfs.iVersion = 1;
291 vfs.szOsFile = sizeof(VfsRootFile);
292 vfs.mxPathname = 2000;
293 vfs.zName = gSQliteVfsName;
294 vfs.xOpen = VfsRdOnlyOpen;
295 vfs.xDelete = VfsRdOnlyDelete;
296 vfs.xAccess = VfsRdOnlyAccess;
297 vfs.xFullPathname = VfsRdOnlyFullPathname;
298 vfs.xRandomness = VfsRdOnlyRandomness;
299 vfs.xSleep = VfsRdOnlySleep;
300 vfs.xCurrentTime = VfsRdOnlyCurrentTime;
301 vfs.xGetLastError = VfsRdOnlyGetLastError;
302 return vfs;
303}
304
305////////////////////////////////////////////////////////////////////////////
306/// A global struct of function pointers and details on the VfsRootFile class that together constitue a VFS module
307struct sqlite3_vfs kSqlite3Vfs = GetSqlite3Vfs();
308
309bool RegisterSqliteVfs()
310{
311 int retval;
312 retval = sqlite3_vfs_register(&kSqlite3Vfs, false);
313 return (retval == SQLITE_OK);
314}
315
316} // anonymous namespace
317
318namespace ROOT {
319
320namespace RDF {
321
322namespace Internal {
323////////////////////////////////////////////////////////////////////////////
324/// The state of an open dataset in terms of the sqlite3 C library.
326 sqlite3 *fDb = nullptr;
327 sqlite3_stmt *fQuery = nullptr;
328};
329}
330
331RSqliteDS::Value_t::Value_t(RSqliteDS::ETypes type)
332 : fType(type), fIsActive(false), fInteger(0), fReal(0.0), fText(), fBlob(), fNull(nullptr)
333{
334 switch (type) {
335 case ETypes::kInteger: fPtr = &fInteger; break;
336 case ETypes::kReal: fPtr = &fReal; break;
337 case ETypes::kText: fPtr = &fText; break;
338 case ETypes::kBlob: fPtr = &fBlob; break;
339 case ETypes::kNull: fPtr = &fNull; break;
340 default: throw std::runtime_error("Internal error");
341 }
342}
343
344constexpr char const *RSqliteDS::fgTypeNames[];
345
346////////////////////////////////////////////////////////////////////////////
347/// \brief Build the dataframe
348/// \param[in] fileName The path to an sqlite3 file, will be opened read-only
349/// \param[in] query A valid sqlite3 SELECT query
350///
351/// The constructor opens the sqlite file, prepares the query engine and determines the column names and types.
352RSqliteDS::RSqliteDS(const std::string &fileName, const std::string &query)
353 : fDataSet(std::make_unique<Internal::RSqliteDSDataSet>()), fNSlots(0), fNRow(0)
354{
355 static bool hasSqliteVfs = RegisterSqliteVfs();
356 if (!hasSqliteVfs)
357 throw std::runtime_error("Cannot register SQlite VFS in RSqliteDS");
358
359 int retval;
360
361 retval = sqlite3_open_v2(fileName.c_str(), &fDataSet->fDb, SQLITE_OPEN_READONLY | SQLITE_OPEN_NOMUTEX,
362 gSQliteVfsName);
363 if (retval != SQLITE_OK)
364 SqliteError(retval);
365
366 // Certain complex queries trigger creation of temporary tables. Depending on the build options of sqlite,
367 // sqlite may try to store such temporary tables on disk, using our custom VFS module to do so.
368 // Creation of new database files, however, is not supported by the custom VFS module. Thus we set the behavior
369 // of the database connection to "temp_store=2", meaning that temporary tables should always be maintained
370 // in memory.
371 retval = sqlite3_exec(fDataSet->fDb, "PRAGMA temp_store=2;", nullptr, nullptr, nullptr);
372 if (retval != SQLITE_OK)
373 SqliteError(retval);
374
375 retval = sqlite3_prepare_v2(fDataSet->fDb, query.c_str(), -1, &fDataSet->fQuery, nullptr);
376 if (retval != SQLITE_OK)
377 SqliteError(retval);
378
379 int colCount = sqlite3_column_count(fDataSet->fQuery);
380 retval = sqlite3_step(fDataSet->fQuery);
381 if ((retval != SQLITE_ROW) && (retval != SQLITE_DONE))
382 SqliteError(retval);
383
384 fValues.reserve(colCount);
385 for (int i = 0; i < colCount; ++i) {
386 fColumnNames.emplace_back(sqlite3_column_name(fDataSet->fQuery, i));
387 int type = SQLITE_NULL;
388 // Try first with the declared column type and then with the dynamic type
389 // for expressions
390 const char *declTypeCstr = sqlite3_column_decltype(fDataSet->fQuery, i);
391 if (declTypeCstr == nullptr) {
392 if (retval == SQLITE_ROW)
393 type = sqlite3_column_type(fDataSet->fQuery, i);
394 } else {
395 std::string declType(declTypeCstr);
396 std::transform(declType.begin(), declType.end(), declType.begin(), ::toupper);
397 if (declType == "INTEGER")
398 type = SQLITE_INTEGER;
399 else if (declType == "FLOAT")
400 type = SQLITE_FLOAT;
401 else if (declType == "TEXT")
402 type = SQLITE_TEXT;
403 else if (declType == "BLOB")
404 type = SQLITE_BLOB;
405 else
406 throw std::runtime_error("Unexpected column decl type");
407 }
408
409 switch (type) {
410 case SQLITE_INTEGER:
412 fValues.emplace_back(ETypes::kInteger);
413 break;
414 case SQLITE_FLOAT:
415 fColumnTypes.push_back(ETypes::kReal);
416 fValues.emplace_back(ETypes::kReal);
417 break;
418 case SQLITE_TEXT:
419 fColumnTypes.push_back(ETypes::kText);
420 fValues.emplace_back(ETypes::kText);
421 break;
422 case SQLITE_BLOB:
423 fColumnTypes.push_back(ETypes::kBlob);
424 fValues.emplace_back(ETypes::kBlob);
425 break;
426 case SQLITE_NULL:
427 // TODO: Null values in first rows are not well handled
428 fColumnTypes.push_back(ETypes::kNull);
429 fValues.emplace_back(ETypes::kNull);
430 break;
431 default: throw std::runtime_error("Unhandled data type");
432 }
433 }
434}
435
436////////////////////////////////////////////////////////////////////////////
437/// Frees the sqlite resources and closes the file.
438RSqliteDS::~RSqliteDS()
439{
440 // sqlite3_finalize returns the error code of the most recent operation on fQuery.
441 sqlite3_finalize(fDataSet->fQuery);
442 // Closing can possibly fail with SQLITE_BUSY, in which case resources are leaked. This should not happen
443 // the way it is used in this class because we cleanup the prepared statement before.
444 sqlite3_close(fDataSet->fDb);
445}
446
447////////////////////////////////////////////////////////////////////////////
448/// Returns the SELECT queries names. The column names have been cached in the constructor.
449/// For expressions, the column name is the string of the expression unless the query defines a column name with as
450/// like in "SELECT 1 + 1 as mycolumn FROM table"
451const std::vector<std::string> &RSqliteDS::GetColumnNames() const
452{
453 return fColumnNames;
454}
455
456////////////////////////////////////////////////////////////////////////////
457/// Activates the given column's result value.
458RDataSource::Record_t RSqliteDS::GetColumnReadersImpl(std::string_view name, const std::type_info &ti)
459{
460 const auto index = std::distance(fColumnNames.begin(), std::find(fColumnNames.begin(), fColumnNames.end(), name));
461 const auto type = fColumnTypes[index];
462
463 if ((type == ETypes::kInteger && typeid(Long64_t) != ti) || (type == ETypes::kReal && typeid(double) != ti) ||
464 (type == ETypes::kText && typeid(std::string) != ti) ||
465 (type == ETypes::kBlob && typeid(std::vector<unsigned char>) != ti) ||
466 (type == ETypes::kNull && typeid(void *) != ti)) {
467 std::string errmsg = "The type selected for column \"";
468 errmsg += name;
469 errmsg += "\" does not correspond to column type, which is ";
470 errmsg += GetTypeName(name);
471 throw std::runtime_error(errmsg);
472 }
473
474 fValues[index].fIsActive = true;
475 return std::vector<void *>{fNSlots, &fValues[index].fPtr};
476}
477
478////////////////////////////////////////////////////////////////////////////
479/// Returns a range of size 1 as long as more rows are available in the SQL result set.
480/// This inherently serialized the RDF independent of the number of slots.
481std::vector<std::pair<ULong64_t, ULong64_t>> RSqliteDS::GetEntryRanges()
482{
483 std::vector<std::pair<ULong64_t, ULong64_t>> entryRanges;
484 int retval = sqlite3_step(fDataSet->fQuery);
485 switch (retval) {
486 case SQLITE_DONE: return entryRanges;
487 case SQLITE_ROW:
488 entryRanges.emplace_back(fNRow, fNRow + 1);
489 fNRow++;
490 return entryRanges;
491 default:
492 SqliteError(retval);
493 // Never here
494 abort();
495 }
496}
497
498////////////////////////////////////////////////////////////////////////////
499/// Returns the C++ type for a given column name, implemented as a linear search through all the columns.
500std::string RSqliteDS::GetTypeName(std::string_view colName) const
501{
502 unsigned N = fColumnNames.size();
503
504 for (unsigned i = 0; i < N; ++i) {
505 if (colName == fColumnNames[i]) {
506 return fgTypeNames[static_cast<int>(fColumnTypes[i])];
507 }
508 }
509 throw std::runtime_error("Unknown column: " + std::string(colName));
510}
511
512////////////////////////////////////////////////////////////////////////////
513/// A linear search through the columns for the given name
514bool RSqliteDS::HasColumn(std::string_view colName) const
515{
516 return std::find(fColumnNames.begin(), fColumnNames.end(), colName) != fColumnNames.end();
517}
518
519////////////////////////////////////////////////////////////////////////////
520/// Resets the SQlite query engine at the beginning of the event loop.
521void RSqliteDS::Initialize()
522{
523 fNRow = 0;
524 int retval = sqlite3_reset(fDataSet->fQuery);
525 if (retval != SQLITE_OK)
526 throw std::runtime_error("SQlite error, reset");
527}
528
529std::string RSqliteDS::GetLabel()
530{
531 return "RSqliteDS";
532}
533
534////////////////////////////////////////////////////////////////////////////////////////////////
535/// \brief Factory method to create a SQlite RDataFrame.
536/// \param[in] fileName Path of the sqlite file.
537/// \param[in] query SQL query that defines the data set.
538RDataFrame FromSqlite(std::string_view fileName, std::string_view query)
539{
540 ROOT::RDataFrame rdf(std::make_unique<RSqliteDS>(std::string(fileName), std::string(query)));
541 return rdf;
542}
543
544////////////////////////////////////////////////////////////////////////////
545/// Stores the result of the current active sqlite query row as a C++ value.
546bool RSqliteDS::SetEntry(unsigned int /* slot */, ULong64_t entry)
547{
548 assert(entry + 1 == fNRow);
549 (void)entry;
550 unsigned N = fValues.size();
551 for (unsigned i = 0; i < N; ++i) {
552 if (!fValues[i].fIsActive)
553 continue;
554
555 int nbytes;
556 switch (fValues[i].fType) {
557 case ETypes::kInteger: fValues[i].fInteger = sqlite3_column_int64(fDataSet->fQuery, i); break;
558 case ETypes::kReal: fValues[i].fReal = sqlite3_column_double(fDataSet->fQuery, i); break;
559 case ETypes::kText:
560 nbytes = sqlite3_column_bytes(fDataSet->fQuery, i);
561 if (nbytes == 0) {
562 fValues[i].fText = "";
563 } else {
564 fValues[i].fText = reinterpret_cast<const char *>(sqlite3_column_text(fDataSet->fQuery, i));
565 }
566 break;
567 case ETypes::kBlob:
568 nbytes = sqlite3_column_bytes(fDataSet->fQuery, i);
569 fValues[i].fBlob.resize(nbytes);
570 if (nbytes > 0) {
571 std::memcpy(fValues[i].fBlob.data(), sqlite3_column_blob(fDataSet->fQuery, i), nbytes);
572 }
573 break;
574 case ETypes::kNull: break;
575 default: throw std::runtime_error("Unhandled column type");
576 }
577 }
578 return true;
579}
580
581////////////////////////////////////////////////////////////////////////////////////////////////
582/// Almost a no-op, many slots can in fact reduce the performance due to thread synchronization.
583void RSqliteDS::SetNSlots(unsigned int nSlots)
584{
585 if (nSlots > 1) {
586 ::Warning("SetNSlots", "Currently the SQlite data source faces performance degradation in multi-threaded mode. "
587 "Consider turning off IMT.");
588 }
589 fNSlots = nSlots;
590}
591
592////////////////////////////////////////////////////////////////////////////////////////////////
593/// Helper function to throw an exception if there is a fatal sqlite error, e.g. an I/O error.
594void RSqliteDS::SqliteError(int errcode)
595{
596 std::string errmsg = "SQlite error: ";
597#if SQLITE_VERSION_NUMBER < 3007015
598 errmsg += std::to_string(errcode);
599#else
600 errmsg += sqlite3_errstr(errcode);
601#endif
602 throw std::runtime_error(errmsg);
603}
604
605} // namespace RDF
606
607} // namespace ROOT
long long Long64_t
Definition RtypesCore.h:69
unsigned long long ULong64_t
Definition RtypesCore.h:70
void Error(const char *location, const char *msgfmt,...)
Use this function in case an error occurred.
Definition TError.cxx:185
void Warning(const char *location, const char *msgfmt,...)
Use this function in warning situations.
Definition TError.cxx:229
#define N
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
char name[80]
Definition TGX11.cxx:110
R__EXTERN TRandom * gRandom
Definition TRandom.h:62
R__EXTERN TSystem * gSystem
Definition TSystem.h:561
static std::unique_ptr< RRawFile > Create(std::string_view url, ROptions options=ROptions())
Factory method that returns a suitable concrete implementation according to the transport in the url.
Definition RRawFile.cxx:64
std::vector< void * > Record_t
static constexpr char const * fgTypeNames[]
Corresponds to the types defined in ETypes.
Definition RSqliteDS.hxx:90
unsigned int fNSlots
Definition RSqliteDS.hxx:81
std::vector< std::string > fColumnNames
Definition RSqliteDS.hxx:83
std::vector< ETypes > fColumnTypes
Definition RSqliteDS.hxx:84
std::string GetTypeName(std::string_view colName) const final
Returns the C++ type for a given column name, implemented as a linear search through all the columns.
ETypes
All the types known to SQlite. Changes require changing fgTypeNames, too.
Definition RSqliteDS.hxx:55
RSqliteDS(const std::string &fileName, const std::string &query)
Build the dataframe.
std::unique_ptr< Internal::RSqliteDSDataSet > fDataSet
Definition RSqliteDS.hxx:80
void SqliteError(int errcode)
Helper function to throw an exception if there is a fatal sqlite error, e.g. an I/O error.
std::vector< Value_t > fValues
The data source is inherently single-threaded and returns only one row at a time. This vector holds t...
Definition RSqliteDS.hxx:86
ROOT's RDataFrame offers a modern, high-level interface for analysis of data stored in TTree ,...
virtual UInt_t Integer(UInt_t imax)
Returns a random integer uniformly distributed on the interval [ 0, imax-1 ].
Definition TRandom.cxx:361
virtual void Sleep(UInt_t milliSec)
Sleep milliSec milli seconds.
Definition TSystem.cxx:437
RDataFrame FromSqlite(std::string_view fileName, std::string_view query)
Factory method to create a SQlite RDataFrame.
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
The state of an open dataset in terms of the sqlite3 C library.
void * fPtr
Points to one of the values; an address to this pointer is returned by GetColumnReadersImpl.
Definition RSqliteDS.hxx:75
std::vector< unsigned char > fBlob
Definition RSqliteDS.hxx:73