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