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