Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RDFInterfaceUtils.cxx
Go to the documentation of this file.
1// Author: Enrico Guiraud, Danilo Piparo CERN 02/2018
2
3/*************************************************************************
4 * Copyright (C) 1995-2018, 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/RDataSource.hxx>
12#include <ROOT/RTTreeDS.hxx>
15#include <ROOT/RDF/RDisplay.hxx>
20#include "ROOT/RLogger.hxx"
22#include <ROOT/RDF/Utils.hxx>
23#include <string_view>
24#include <TBranch.h>
25#include <TClass.h>
26#include <TClassEdit.h>
27#include <TDataType.h>
28#include <TError.h>
29#include <TLeaf.h>
30#include <TObjArray.h>
31#include <TPRegexp.h>
32#include <TROOT.h>
33#include <TString.h>
34#include <TTree.h>
35#include <TVirtualMutex.h>
36
37// pragma to disable warnings on Rcpp which have
38// so many noise compiling
39#if defined(__GNUC__)
40#pragma GCC diagnostic push
41#pragma GCC diagnostic ignored "-Woverloaded-virtual"
42#pragma GCC diagnostic ignored "-Wshadow"
43#endif
44#include "exprtk.hpp"
45#if defined(__GNUC__)
46#pragma GCC diagnostic pop
47#endif
48
49#include <algorithm>
50#include <cassert>
51#include <cstdlib> // for size_t
52#include <iterator> // for back_insert_iterator
53#include <map>
54#include <memory>
55#include <set>
56#include <sstream>
57#include <stdexcept>
58#include <string>
59#include <typeinfo>
60#include <unordered_map>
61#include <unordered_set>
62#include <utility> // for pair
63#include <vector>
64
65namespace ROOT::Detail::RDF {
66class RDefineBase;
67}
68
69namespace {
72
73/// A string expression such as those passed to Filter and Define, digested to a standardized form
74struct ParsedExpression {
75 /// The string expression with the dummy variable names in fVarNames in place of the original column names
76 std::string fExpr;
77 /// The list of valid column names that were used in the original string expression.
78 /// Duplicates are removed and column aliases (created with Alias calls) are resolved.
79 ColumnNames_t fUsedCols;
80 /// The list of variable names used in fExpr, with same ordering and size as fUsedCols
81 ColumnNames_t fVarNames;
82};
83
84/// Look at expression `expr` and return a pair of (column names used, aliases used)
85std::pair<ColumnNames_t, ColumnNames_t> FindUsedColsAndAliases(const std::string &expr,
86 const ROOT::Internal::RDF::RColumnRegister &colRegister,
87 const ColumnNames_t &dataSourceColNames)
88{
89 exprtk::lexer::generator tokens;
90 const auto tokensOk = tokens.process(expr);
91 if (!tokensOk) {
92 const auto msg = "Failed to tokenize expression:\n" + expr + "\n\nMake sure it is valid C++.";
93 throw std::runtime_error(msg);
94 }
95
96 std::unordered_set<std::string> usedCols;
97 std::unordered_set<std::string> usedAliases;
98
99 // iterate over tokens in expression and fill usedCols and usedAliases
100 const auto nTokens = tokens.size();
101 const auto kSymbol = exprtk::lexer::token::e_symbol;
102 for (auto i = 0u; i < nTokens; ++i) {
103 const auto &tok = tokens[i];
104 // exprtk::lexer classifies '&' as e_symbol for some reason
105 if (tok.type != kSymbol || tok.value == "&" || tok.value == "|") {
106 // token is not a potential variable name, skip it
107 continue;
108 }
109 // Skip symbols that are member accesses (obj.method) — they are not column references.
110 // exprtk::lexer does not produce dot-prefixed tokens, so the token immediately before a method
111 // name is always the literal "." token when it is a member access.
112 if (i > 0 && tokens[i - 1].value == ".") {
113 continue;
114 }
115
116 ColumnNames_t potentialColNames({tok.value});
117
118 // if token is the start of a dot chain (a.b.c...), a.b, a.b.c etc. are also potential column names
119 auto dotChainKeepsGoing = [&](unsigned int _i) {
120 return _i + 2 <= nTokens && tokens[_i + 1].value == "." && tokens[_i + 2].type == kSymbol;
121 };
122 while (dotChainKeepsGoing(i)) {
123 potentialColNames.emplace_back(potentialColNames.back() + "." + tokens[i + 2].value);
124 i += 2; // consume the tokens we looked at
125 }
126
127 // in an expression such as `a.b`, if `a` is a column alias add it to `usedAliases` and
128 // replace the alias with the real column name in `potentialColNames`.
129 const auto maybeAnAlias = potentialColNames[0]; // intentionally a copy as we'll modify potentialColNames later
130 const auto &resolvedAlias = colRegister.ResolveAlias(maybeAnAlias);
131 if (resolvedAlias != maybeAnAlias) { // this is an alias
132 usedAliases.insert(maybeAnAlias);
133 for (auto &s : potentialColNames)
134 s.replace(0, maybeAnAlias.size(), resolvedAlias);
135 }
136
137 // find the longest potential column name that is an actual column name
138 // (potential columns are sorted by length, so we search from the end to find the longest)
139 auto isRDFColumn = [&](const std::string &col) {
140 if (colRegister.IsDefineOrAlias(col) || IsStrInVec(col, dataSourceColNames))
141 return true;
142 return false;
143 };
144 const auto longestRDFColMatch = std::find_if(potentialColNames.crbegin(), potentialColNames.crend(), isRDFColumn);
145 if (longestRDFColMatch != potentialColNames.crend())
146 usedCols.insert(*longestRDFColMatch);
147 }
148
149 return {{usedCols.begin(), usedCols.end()}, {usedAliases.begin(), usedAliases.end()}};
150}
151
152/// Substitute each '.' in a string with '\.'
153std::string EscapeDots(const std::string &s)
154{
155 TString out(s);
156 TPRegexp dot("\\.");
157 dot.Substitute(out, "\\.", "g");
158 return std::string(std::move(out));
159}
160
161TString ResolveAliases(const TString &expr, const ColumnNames_t &usedAliases,
162 const ROOT::Internal::RDF::RColumnRegister &colRegister)
163{
164 TString out(expr);
165
166 for (const auto &alias : usedAliases) {
167 const auto &col = colRegister.ResolveAlias(alias);
168 TPRegexp replacer("(?<!\\.)\\b" + EscapeDots(alias) + "\\b");
169 replacer.Substitute(out, col.data(), "g");
170 }
171
172 return out;
173}
174
175ParsedExpression ParseRDFExpression(std::string_view expr, const ROOT::Internal::RDF::RColumnRegister &colRegister,
176 const ColumnNames_t &dataSourceColNames)
177{
178 // transform `#var` into `R_rdf_sizeof_var`
179 TString preProcessedExpr(expr);
180 // regex explanation:
181 // #
182 // - matches the literal character #
183 // (?!(ifdef|ifndef|if|else|elif|endif|pragma|define|undef|include|line))
184 // - negative lookahead (i.e. don't match these occurrences) to exclude preprocessor directives e.g. #ifdef
185 // ([a-zA-Z_][a-zA-Z0-9_]*)
186 // - match characters that make up the variable name after the #
187 // (?=(?:[^"]*"[^"]*")*[^"]*$)
188 // - positive lookahead (i.e. match these occurrences) checking that there is always an even number of quotes
189 // *after* the matched character sequence, using the non-capturing group (?:[^"]*"[^"]*"). We leverage the
190 // fact that for any character sequence to be within quotes there must be an odd number of quotes after the
191 // sequence and before the end of the line.
192 TPRegexp colSizeReplacer(
193 R"REGEX(#(?!(ifdef|ifndef|if|else|elif|endif|pragma|define|undef|include|line))([a-zA-Z_][a-zA-Z0-9_]*)(?=(?:[^"]*"[^"]*")*[^"]*$))REGEX");
194 colSizeReplacer.Substitute(preProcessedExpr, "R_rdf_sizeof_$2", "g");
195
196 ColumnNames_t usedCols;
197 ColumnNames_t usedAliases;
198 std::tie(usedCols, usedAliases) =
199 FindUsedColsAndAliases(std::string(preProcessedExpr), colRegister, dataSourceColNames);
200
201 const auto exprNoAliases = ResolveAliases(preProcessedExpr, usedAliases, colRegister);
202
203 // when we are done, exprWithVars willl be the same as preProcessedExpr but column names will be substituted with
204 // the dummy variable names in varNames
205 TString exprWithVars(exprNoAliases);
206
207 ColumnNames_t varNames(usedCols.size());
208 for (auto i = 0u; i < varNames.size(); ++i)
209 varNames[i] = "var" + std::to_string(i);
210
211 // sort the vector usedColsAndAliases by decreasing length of its elements,
212 // so in case of friends we guarantee we never substitute a column name with another column containing it
213 // ex. without sorting when passing "x" and "fr.x", the replacer would output "var0" and "fr.var0",
214 // because it has already substituted "x", hence the "x" in "fr.x" would be recognized as "var0",
215 // whereas the desired behaviour is handling them as "var0" and "var1"
216 std::sort(usedCols.begin(), usedCols.end(),
217 [](const std::string &a, const std::string &b) { return a.size() > b.size(); });
218 for (const auto &col : usedCols) {
219 const auto varIdx = std::distance(usedCols.begin(), std::find(usedCols.begin(), usedCols.end(), col));
220 TPRegexp replacer("(?<!\\.)\\b" + EscapeDots(col) + "\\b");
221 replacer.Substitute(exprWithVars, varNames[varIdx], "g");
222 }
223
224 return ParsedExpression{std::string(std::move(exprWithVars)), std::move(usedCols), std::move(varNames)};
225}
226
227/// Return the static global map of Filter/Define functions that have been jitted.
228/// It's used to check whether a given expression has already been jitted, and
229/// to look up its associated variable name if it is.
230/// Keys in the map are the body of the expression, values are the name of the
231/// jitted variable that corresponds to that expression. For example, for:
232/// auto f1(){ return 42; }
233/// key would be "(){ return 42; }" and value would be "f1".
234std::unordered_map<std::string, std::string> &GetJittedExprs() {
235 static std::unordered_map<std::string, std::string> jittedExpressions;
236 return jittedExpressions;
237}
238
239std::string BuildFunctionString(const std::string &expr, const ColumnNames_t &vars, const ColumnNames_t &varTypes,
240 bool isSingleColumn = false, const std::string &varyColType = "")
241{
242 assert(vars.size() == varTypes.size());
243
244 TPRegexp re(R"(\breturn\b)");
245 const bool hasReturnStmt = re.MatchB(expr);
246
247 static const std::vector<std::string> fundamentalTypes = {
248 "int",
249 "signed",
250 "signed int",
251 "Int_t",
252 "unsigned",
253 "unsigned int",
254 "UInt_t",
255 "double",
256 "Double_t",
257 "float",
258 "Float_t",
259 "char",
260 "Char_t",
261 "unsigned char",
262 "UChar_t",
263 "bool",
264 "Bool_t",
265 "short",
266 "short int",
267 "Short_t",
268 "long",
269 "long int",
270 "long long int",
271 "Long64_t",
272 "unsigned long",
273 "unsigned long int",
274 "ULong64_t",
275 "std::size_t",
276 "size_t",
277 "Ssiz_t"
278 };
279
280 std::stringstream ss;
281 ss << "(";
282 for (auto i = 0u; i < vars.size(); ++i) {
283 std::string fullType;
284 const auto &type = varTypes[i];
285 if (std::find(fundamentalTypes.begin(), fundamentalTypes.end(), type) != fundamentalTypes.end()) {
286 // pass it by const value to help detect common mistakes such as if(x = 3)
287 fullType = "const " + type + " ";
288 } else {
289 // We pass by reference to avoid expensive copies
290 // It can't be const reference in general, as users might want/need to call non-const methods on the values
291 fullType = type + "& ";
292 }
293 ss << fullType << vars[i] << ", ";
294 }
295 if (!vars.empty())
296 ss.seekp(-2, ss.cur);
297
298 // When building the function expression for a Vary call, we try to help the
299 // user by removing the need to explicitly write the vector return type.
300 // For now, Vary works by returning a (nested) RVec, depending on how many
301 // variables need to vary in lockstep.
302 auto finalizeExprForVary = [&]() {
303 std::string trailRetType{};
304 // Trim formatting characters at the extremes of the user expression
305 auto first_not_space = expr.find_first_not_of(" \n\t");
306 auto last_not_space = expr.find_last_not_of(" \n\t");
307 if (first_not_space != std::string::npos && last_not_space != std::string::npos && expr[first_not_space] == '{' &&
308 expr[last_not_space] == '}') {
309 // User expression is of type '{...}', a potential constructor for an
310 // RVec. At the same time, they have not decided the RVec return type
311 // Add trailing return type for the convenience of the user
312 // The innermost value type is by default the type of the first given column
313 trailRetType = " -> ";
314 if (isSingleColumn)
315 trailRetType += "ROOT::RVec<" + varyColType + ">";
316 else
317 trailRetType += "ROOT::RVec<ROOT::RVec<" + varyColType + ">>";
318 trailRetType += ' ';
319 }
320 std::string trailRetToken{trailRetType.empty() ? ") {" : ')' + trailRetType + '{'};
321 if (!hasReturnStmt)
322 trailRetToken += " return ";
323 return trailRetToken;
324 };
325
326 if (!varyColType.empty())
327 ss << finalizeExprForVary();
328 else
329 ss << (hasReturnStmt ? ") {" : ") { return ");
330
331 // Must inject \n to avoid cases where the user puts a comment after the expression
332 ss << expr << "\n;}\n";
333
334 return ss.str();
335}
336
337/// Declare a function to the interpreter in namespace R_rdf, return the name of the jitted function.
338/// If the function is already in GetJittedExprs, return the name for the function that has already been jitted.
339std::string DeclareFunction(const std::string &expr, const ColumnNames_t &vars, const ColumnNames_t &varTypes,
340 bool isSingleColumn = false, const std::string &varyColType = "")
341{
343
344 const auto funcCode = BuildFunctionString(expr, vars, varTypes, isSingleColumn, varyColType);
345 auto &exprMap = GetJittedExprs();
346 const auto exprIt = exprMap.find(funcCode);
347 if (exprIt != exprMap.end()) {
348 // expression already there
349 const auto funcName = exprIt->second;
350 return funcName;
351 }
352
353 // new expression
354 const auto funcBaseName = "func" + std::to_string(exprMap.size());
355 const auto funcFullName = "R_rdf::" + funcBaseName;
356
357 const auto toDeclare = "namespace R_rdf {\nauto " + funcBaseName + funcCode + "\nusing " + funcBaseName +
358 "_ret_t = typename ROOT::TypeTraits::CallableTraits<decltype(" + funcBaseName +
359 ")>::ret_type;\n}";
361
362 // InterpreterDeclare could throw. If it doesn't, mark the function as already jitted
363 exprMap.insert({funcCode, funcFullName});
364
365 return funcFullName;
366}
367
368/// Each jitted function comes with a func_ret_t type alias for its return type.
369/// Resolve that alias and return the true type as string.
370std::string RetTypeOfFunc(const std::string &funcName)
371{
372 const auto dt = gROOT->GetType((funcName + "_ret_t").c_str());
373 R__ASSERT(dt != nullptr);
374 const auto type = dt->GetFullTypeName();
375 return type;
376}
377
378[[noreturn]] void
379ThrowJitBuildActionHelperTypeError(const std::string &actionTypeNameBase, const std::type_info &helperArgType)
380{
381 int err = 0;
382 const char *cname = TClassEdit::DemangleTypeIdName(helperArgType, err);
383 std::string actionHelperTypeName = cname;
384 delete[] cname;
385 if (err != 0)
386 actionHelperTypeName = helperArgType.name();
387
388 std::string exceptionText =
389 "RDataFrame::Jit: cannot just-in-time compile a \"" + actionTypeNameBase + "\" action using helper type \"" +
390 actionHelperTypeName +
391 "\". This typically happens in a custom `Fill` or `Book` invocation where the types of the input columns have "
392 "not been specified as template parameters and the ROOT interpreter has no knowledge of this type of action "
393 "helper. Please add template parameters for the types of the input columns to avoid jitting this action (i.e. "
394 "`df.Fill<float>(..., {\"x\"})`, where `float` is the type of `x`) or declare the action helper type to the "
395 "interpreter, e.g. via gInterpreter->Declare.";
396
397 throw std::runtime_error(exceptionText);
398}
399
400} // anonymous namespace
401
402namespace ROOT {
403namespace Internal {
404namespace RDF {
405
406/// Take a list of column names, return that list with entries starting by '#' filtered out.
407/// The function throws when filtering out a column this way.
408ColumnNames_t FilterArraySizeColNames(const ColumnNames_t &columnNames, const std::string &action)
409{
410 ColumnNames_t columnListWithoutSizeColumns;
411 ColumnNames_t filteredColumns;
412 std::copy_if(columnNames.begin(), columnNames.end(), std::back_inserter(columnListWithoutSizeColumns),
413 [&](const std::string &name) {
414 if (name[0] == '#') {
415 filteredColumns.emplace_back(name);
416 return false;
417 } else {
418 return true;
419 }
420 });
421
422 if (!filteredColumns.empty()) {
423 std::string msg = "Column name(s) {";
424 for (auto &c : filteredColumns)
425 msg += c + ", ";
426 msg[msg.size() - 2] = '}';
427 msg += "will be ignored. Please go through a valid Alias to " + action + " an array size column";
428 throw std::runtime_error(msg);
429 }
430
431 return columnListWithoutSizeColumns;
432}
433
434void CheckValidCppVarName(std::string_view var, const std::string &where)
435{
436 bool isValid = true;
437
438 if (var.empty())
439 isValid = false;
440 const char firstChar = var[0];
441
442 // first character must be either a letter or an underscore
443 auto isALetter = [](char c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); };
444 const bool isValidFirstChar = firstChar == '_' || isALetter(firstChar);
445 if (!isValidFirstChar)
446 isValid = false;
447
448 // all characters must be either a letter, an underscore or a number
449 auto isANumber = [](char c) { return c >= '0' && c <= '9'; };
450 auto isValidTok = [&isALetter, &isANumber](char c) { return c == '_' || isALetter(c) || isANumber(c); };
451 for (const char c : var)
452 if (!isValidTok(c))
453 isValid = false;
454
455 if (!isValid) {
456 const auto objName = where == "Define" ? "column" : (where == "Alias" ? "alias" : "variation");
457 const auto error = "RDataFrame::" + where + ": cannot define " + objName + " \"" + std::string(var) +
458 "\". Not a valid C++ variable name.";
459 throw std::runtime_error(error);
460 }
461}
462
463std::string DemangleTypeIdName(const std::type_info &typeInfo)
464{
465 int dummy(0);
466 char *tn = TClassEdit::DemangleTypeIdName(typeInfo, dummy);
467 std::string tname(tn);
468 free(tn);
469 return tname;
470}
471
472ColumnNames_t
473ConvertRegexToColumns(const ColumnNames_t &colNames, std::string_view columnNameRegexp, std::string_view callerName)
474{
475 const auto theRegexSize = columnNameRegexp.size();
476 std::string theRegex(columnNameRegexp);
477
478 const auto isEmptyRegex = 0 == theRegexSize;
479 // This is to avoid cases where branches called b1, b2, b3 are all matched by expression "b"
480 if (theRegexSize > 0 && theRegex[0] != '^')
481 theRegex = "^" + theRegex;
482 if (theRegexSize > 0 && theRegex[theRegexSize - 1] != '$')
483 theRegex = theRegex + "$";
484
485 ColumnNames_t selectedColumns;
486
487 // Since we support gcc48 and it does not provide in its stl std::regex,
488 // we need to use TPRegexp
489 TPRegexp regexp(theRegex);
490 for (auto &&colName : colNames) {
491 if ((isEmptyRegex || regexp.MatchB(colName.c_str())) && !IsInternalColumn(colName)) {
492 selectedColumns.emplace_back(colName);
493 }
494 }
495
496 if (selectedColumns.empty()) {
497 std::string text(callerName);
498 if (columnNameRegexp.empty()) {
499 text = ": there is no column available to match.";
500 } else {
501 text = ": regex \"" + std::string(columnNameRegexp) + "\" did not match any column.";
502 }
503 throw std::runtime_error(text);
504 }
505 return selectedColumns;
506}
507
508/// Throw if column `definedColView` is already there.
509void CheckForRedefinition(const std::string &where, std::string_view definedColView, const RColumnRegister &colRegister,
510 const ColumnNames_t &dataSourceColumns)
511{
512
513 std::string error{};
514 if (colRegister.IsAlias(definedColView))
515 error = "An alias with that name, pointing to column \"" + std::string(colRegister.ResolveAlias(definedColView)) +
516 "\", already exists in this branch of the computation graph.";
517 else if (colRegister.IsDefineOrAlias(definedColView))
518 error = "A column with that name has already been Define'd. Use Redefine to force redefinition.";
519 else if (std::find(dataSourceColumns.begin(), dataSourceColumns.end(), definedColView) != dataSourceColumns.end())
520 error =
521 "A column with that name is already present in the input data source. Use Redefine to force redefinition.";
522
523 if (!error.empty()) {
524 error = "RDataFrame::" + where + ": cannot define column \"" + std::string(definedColView) + "\". " + error;
525 throw std::runtime_error(error);
526 }
527}
528
529/// Throw if column `definedColView` is _not_ already there.
530void CheckForDefinition(const std::string &where, std::string_view definedColView, const RColumnRegister &colRegister,
531 const ColumnNames_t &dataSourceColumns)
532{
533 std::string error{};
534
535 if (colRegister.IsAlias(definedColView)) {
536 error = "An alias with that name, pointing to column \"" + std::string(colRegister.ResolveAlias(definedColView)) +
537 "\", already exists. Aliases cannot be Redefined or Varied.";
538 }
539
540 if (error.empty()) {
541 const bool isAlreadyDefined = colRegister.IsDefineOrAlias(definedColView);
542 const bool isADSColumn =
543 std::find(dataSourceColumns.begin(), dataSourceColumns.end(), definedColView) != dataSourceColumns.end();
544
545 if (!isAlreadyDefined && !isADSColumn)
546 error = "No column with that name was found in the dataset. Use Define to create a new column.";
547 }
548
549 if (!error.empty()) {
550 if (where == "DefaultValueFor")
551 error = "RDataFrame::" + where + ": cannot provide default values for column \"" +
552 std::string(definedColView) + "\". " + error;
553 else
554 error = "RDataFrame::" + where + ": cannot redefine or vary column \"" + std::string(definedColView) + "\". " +
555 error;
556 throw std::runtime_error(error);
557 }
558}
559
560/// Throw if the column has systematic variations attached.
561void CheckForNoVariations(const std::string &where, std::string_view definedColView, const RColumnRegister &colRegister)
562{
563 const std::string definedCol(definedColView);
564 const auto &variationDeps = colRegister.GetVariationDeps(definedCol);
565 if (!variationDeps.empty()) {
566 if (where == "Redefine") {
567 const std::string error = "RDataFrame::" + where + ": cannot redefine column \"" + definedCol +
568 "\". The column depends on one or more systematic variations and re-defining varied "
569 "columns is not supported.";
570 throw std::runtime_error(error);
571 } else if (where == "DefaultValueFor") {
572 const std::string error = "RDataFrame::" + where + ": cannot provide a default value for column \"" +
573 definedCol +
574 "\". The column depends on one or more systematic variations and it should not be "
575 "possible to have missing values in varied columns.";
576 throw std::runtime_error(error);
577 } else {
578 const std::string error =
579 "RDataFrame::" + where + ": this operation cannot work with columns that depend on systematic variations.";
580 throw std::runtime_error(error);
581 }
582 }
583}
584
585void CheckTypesAndPars(unsigned int nTemplateParams, unsigned int nColumnNames)
586{
587 if (nTemplateParams != nColumnNames) {
588 std::string err_msg = "The number of template parameters specified is ";
589 err_msg += std::to_string(nTemplateParams);
590 err_msg += " while ";
591 err_msg += std::to_string(nColumnNames);
592 err_msg += " columns have been specified.";
593 throw std::runtime_error(err_msg);
594 }
595}
596
597/// Choose between local column names or default column names, throw in case of errors.
598const ColumnNames_t
599SelectColumns(unsigned int nRequiredNames, const ColumnNames_t &names, const ColumnNames_t &defaultNames)
600{
601 if (names.empty()) {
602 // use default column names
603 if (defaultNames.size() < nRequiredNames)
604 throw std::runtime_error(
605 std::to_string(nRequiredNames) + " column name" + (nRequiredNames == 1 ? " is" : "s are") +
606 " required but none were provided and the default list has size " + std::to_string(defaultNames.size()));
607 // return first nRequiredNames default column names
608 return ColumnNames_t(defaultNames.begin(), defaultNames.begin() + nRequiredNames);
609 } else {
610 // use column names provided by the user to this particular transformation/action
611 if (names.size() != nRequiredNames) {
612 auto msg = std::to_string(nRequiredNames) + " column name" + (nRequiredNames == 1 ? " is" : "s are") +
613 " required but " + std::to_string(names.size()) + (names.size() == 1 ? " was" : " were") +
614 " provided:";
615 for (const auto &name : names)
616 msg += " \"" + name + "\",";
617 msg.back() = '.';
618 throw std::runtime_error(msg);
619 }
620 return names;
621 }
622}
623
624ColumnNames_t FindUnknownColumns(const ColumnNames_t &requiredCols, const RColumnRegister &definedCols,
625 const ColumnNames_t &dataSourceColumns)
626{
627 ColumnNames_t unknownColumns;
628 for (auto &column : requiredCols) {
629 if (definedCols.IsDefineOrAlias(column))
630 continue;
631 const auto isDataSourceColumn =
632 std::find(dataSourceColumns.begin(), dataSourceColumns.end(), column) != dataSourceColumns.end();
633 if (isDataSourceColumn)
634 continue;
635 unknownColumns.emplace_back(column);
636 }
637 return unknownColumns;
638}
639
640std::vector<std::string> GetFilterNames(const std::shared_ptr<RLoopManager> &loopManager)
641{
642 return loopManager->GetFiltersNames();
643}
644
645ParsedTreePath ParseTreePath(std::string_view fullTreeName)
646{
647 // split name into directory and treename if needed
648 std::string_view dirName = "";
649 std::string_view treeName = fullTreeName;
650 const auto lastSlash = fullTreeName.rfind('/');
651 if (std::string_view::npos != lastSlash) {
652 dirName = treeName.substr(0, lastSlash);
653 treeName = treeName.substr(lastSlash + 1, treeName.size());
654 }
655 return {std::string(treeName), std::string(dirName)};
656}
657
658std::string PrettyPrintAddr(const void *const addr)
659{
660 std::stringstream s;
661 // Windows-friendly
662 s << std::hex << std::showbase << reinterpret_cast<size_t>(addr);
663 return s.str();
664}
665
666/// Book the jitting of a Filter call
667std::shared_ptr<RDFDetail::RJittedFilter>
668BookFilterJit(std::shared_ptr<RDFDetail::RNodeBase> prevNode, std::string_view name, std::string_view expression,
669 const RColumnRegister &colRegister, TTree *tree, RDataSource *ds)
670{
671 const auto &dsColumns = ds ? ds->GetColumnNames() : ColumnNames_t{};
672
673 const auto parsedExpr = ParseRDFExpression(expression, colRegister, dsColumns);
674 const auto exprVarTypes =
675 GetValidatedArgTypes(parsedExpr.fUsedCols, colRegister, tree, ds, "Filter", /*vector2RVec=*/true);
676 const auto funcName = DeclareFunction(parsedExpr.fExpr, parsedExpr.fVarNames, exprVarTypes);
677 const auto type = RetTypeOfFunc(funcName);
678 if (type != "bool")
679 throw std::runtime_error("Filter: the following expression does not evaluate to bool:\n" +
680 std::string(expression));
681
682 auto *lm = prevNode->GetLoopManagerUnchecked();
683 const auto jittedFilter = std::make_shared<RDFDetail::RJittedFilter>(
684 lm, name, Union(colRegister.GetVariationDeps(parsedExpr.fUsedCols), prevNode->GetVariations()), prevNode);
685
686 // Produce code snippet that creates the filter and registers it with the corresponding RJittedFilter
687 std::stringstream filterInvocation;
688 filterInvocation << "(const std::vector<std::string> &colNames, "
689 << "ROOT::Internal::RDF::RColumnRegister &colRegister, "
690 << "ROOT::Detail::RDF::RLoopManager &lm, "
691 << "void *jittedFilter, "
692 << "std::shared_ptr<void> *) {\n";
693 filterInvocation << " ROOT::Internal::RDF::JitFilterHelper(" << funcName << ", "
694 << "colNames, "
695 << "colRegister, "
696 << "lm, "
697 << "reinterpret_cast<ROOT::Detail::RDF::RJittedFilter*>(jittedFilter)"
698 << ");\n}\n";
699 lm->RegisterJitHelperCall(filterInvocation.str(),
700 std::make_unique<ROOT::Internal::RDF::RColumnRegister>(colRegister), parsedExpr.fUsedCols,
701 jittedFilter);
702
703 return jittedFilter;
704}
705
706/// Book the jitting of a Define call
707std::shared_ptr<RJittedDefine> BookDefineJit(std::string_view name, std::string_view expression, RLoopManager &lm,
708 RDataSource *ds, const RColumnRegister &colRegister)
709{
710 const auto &dsColumns = ds ? ds->GetColumnNames() : ColumnNames_t{};
711
712 const auto parsedExpr = ParseRDFExpression(expression, colRegister, dsColumns);
713 const auto exprVarTypes =
714 GetValidatedArgTypes(parsedExpr.fUsedCols, colRegister, nullptr, ds, "Define", /*vector2RVec=*/true);
715 const auto funcName = DeclareFunction(parsedExpr.fExpr, parsedExpr.fVarNames, exprVarTypes);
716 const auto type = RetTypeOfFunc(funcName);
717
718 auto jittedDefine = std::make_shared<RDFDetail::RJittedDefine>(name, type, lm, colRegister, parsedExpr.fUsedCols);
719
720 // lifetime of pointees:
721 // - lm is the loop manager, and if that goes out of scope jitting does not happen at all (i.e. will always be valid)
722 // - jittedDefine: heap-allocated weak_ptr that will be deleted by JitDefineHelper after usage
723 // - definesAddr: heap-allocated, will be deleted by JitDefineHelper after usage
724 std::stringstream defineInvocation;
725 defineInvocation << "(const std::vector<std::string> &colNames, "
726 << "ROOT::Internal::RDF::RColumnRegister &colRegister, "
727 << "ROOT::Detail::RDF::RLoopManager &lm, "
728 << "void *jittedDefine, "
729 << "std::shared_ptr<void> *) {\n";
730 defineInvocation << " ROOT::Internal::RDF::JitDefineHelper<ROOT::Internal::RDF::DefineTypes::RDefineTag>("
731 << funcName << ", "
732 << "colNames, "
733 << "colRegister, "
734 << "lm, "
735 << "reinterpret_cast<ROOT::Detail::RDF::RJittedDefine *>(jittedDefine)"
736 << ");\n}\n";
737 lm.RegisterJitHelperCall(defineInvocation.str(), std::make_unique<ROOT::Internal::RDF::RColumnRegister>(colRegister),
738 parsedExpr.fUsedCols, jittedDefine);
739
740 return jittedDefine;
741}
742
743/// Book the jitting of a DefinePerSample call
744std::shared_ptr<RJittedDefine> BookDefinePerSampleJit(std::string_view name, std::string_view expression,
745 RLoopManager &lm, const RColumnRegister &colRegister)
746{
747 const auto funcName = DeclareFunction(std::string(expression), {"rdfslot_", "rdfsampleinfo_"},
748 {"unsigned int", "const ROOT::RDF::RSampleInfo"});
749 const auto retType = RetTypeOfFunc(funcName);
750
751 auto jittedDefine = std::make_shared<RDFDetail::RJittedDefine>(name, retType, lm, colRegister, ColumnNames_t{});
752
753 // lifetime of pointees:
754 // - lm is the loop manager, and if that goes out of scope jitting does not happen at all (i.e. will always be valid)
755 // - jittedDefine: heap-allocated weak_ptr that will be deleted by JitDefineHelper after usage
756 // - definesAddr: heap-allocated, will be deleted by JitDefineHelper after usage
757 std::stringstream defineInvocation;
758 defineInvocation << "(const std::vector<std::string> &colNames, "
759 << "ROOT::Internal::RDF::RColumnRegister &colRegister, "
760 << "ROOT::Detail::RDF::RLoopManager &lm, "
761 << "void *jittedDefine, "
762 << "std::shared_ptr<void> *) {\n";
763 defineInvocation << " ROOT::Internal::RDF::JitDefineHelper<ROOT::Internal::RDF::DefineTypes::RDefinePerSampleTag>("
764 << funcName << ", "
765 << "colNames, "
766 << "colRegister, "
767 << "lm, "
768 << "reinterpret_cast<ROOT::Detail::RDF::RJittedDefine *>(jittedDefine)"
769 << ");\n}\n";
770 lm.RegisterJitHelperCall(defineInvocation.str(), std::make_unique<ROOT::Internal::RDF::RColumnRegister>(colRegister),
771 {}, jittedDefine);
772 return jittedDefine;
773}
774
775/// Book the jitting of a Vary call
776std::shared_ptr<RJittedVariation>
777BookVariationJit(const std::vector<std::string> &colNames, std::string_view variationName,
778 const std::vector<std::string> &variationTags, std::string_view expression, RLoopManager &lm,
779 RDataSource *ds, const RColumnRegister &colRegister, bool isSingleColumn,
780 const std::string &varyColType)
781{
782 const auto &dsColumns = ds ? ds->GetColumnNames() : ColumnNames_t{};
783
784 const auto parsedExpr = ParseRDFExpression(expression, colRegister, dsColumns);
785 const auto exprVarTypes =
786 GetValidatedArgTypes(parsedExpr.fUsedCols, colRegister, nullptr, ds, "Vary", /*vector2RVec=*/true);
787 const auto funcName =
788 DeclareFunction(parsedExpr.fExpr, parsedExpr.fVarNames, exprVarTypes, isSingleColumn, varyColType);
789 const auto type = RetTypeOfFunc(funcName);
790
791 if (type.rfind("ROOT::VecOps::RVec", 0) != 0) {
792 throw std::runtime_error(
793 "Jitted Vary expressions must return an RVec object. The following expression return type is '" + type +
794 "' instead:\n" + parsedExpr.fExpr);
795 }
796
797 auto jittedVariation = std::make_shared<RJittedVariation>(colNames, variationName, variationTags, type, colRegister,
798 lm, parsedExpr.fUsedCols);
799
800 // build invocation to JitVariationHelper
801 // variation tag (array of strings) passed as const char** plus size.
802 // lifetime of pointees:
803 // - lm is the loop manager, and if that goes out of scope jitting does not happen at all (i.e. will always be valid)
804 // - jittedVariation: heap-allocated weak_ptr that will be deleted by JitDefineHelper after usage
805 // - definesAddr: heap-allocated, will be deleted by JitDefineHelper after usage
806 // - variedColsOnHeap: deleted by registration function
807 std::stringstream varyInvocation;
808 varyInvocation << "(const std::vector<std::string> &inputColNames, "
809 << "ROOT::Internal::RDF::RColumnRegister &colRegister, "
810 << "ROOT::Detail::RDF::RLoopManager &lm, "
811 << "void *jittedVariation, "
812 << "std::shared_ptr<void> *helperArg) {\n";
813 varyInvocation
814 << " auto *variedColNamesAndTags = reinterpret_cast<std::shared_ptr<std::pair<std::vector<std::string>, "
815 "std::vector<std::string>>> *>(helperArg);"
816 << " ROOT::Internal::RDF::JitVariationHelper<" << (isSingleColumn ? "true" : "false") << ">(" << funcName
817 << ", "
818 << "inputColNames, "
819 << "colRegister, "
820 << "lm, "
821 << "reinterpret_cast<ROOT::Internal::RDF::RJittedVariation *>(jittedVariation), "
822 << "(*variedColNamesAndTags)->first, "
823 << "(*variedColNamesAndTags)->second"
824 << ");\n}\n";
825 lm.RegisterJitHelperCall(
826 varyInvocation.str(), std::make_unique<ROOT::Internal::RDF::RColumnRegister>(colRegister), parsedExpr.fUsedCols,
827 jittedVariation,
828 std::make_shared<std::pair<std::vector<std::string>, std::vector<std::string>>>(colNames, variationTags));
829 return jittedVariation;
830}
831
832// Jit and call something equivalent to "this->BuildAndBook<ColTypes...>(params...)"
833// (see comments in the body for actual jitted code)
834std::string JitBuildAction(const ColumnNames_t &cols, const std::type_info &helperArgType, const std::type_info &at,
835 TTree *tree, const unsigned int nSlots, const RColumnRegister &colRegister, RDataSource *ds,
836 const bool vector2RVec)
837{
838 // retrieve type of action as a string
839 auto actionTypeClass = TClass::GetClass(at);
840 if (!actionTypeClass) {
841 std::string exceptionText = "An error occurred while inferring the action type of the operation.";
842 throw std::runtime_error(exceptionText);
843 }
844 const std::string actionTypeName = actionTypeClass->GetName();
845 const std::string actionTypeNameBase = actionTypeName.substr(actionTypeName.rfind(':') + 1);
846
847 // retrieve type of result of the action as a string
848 const auto helperArgTypeName = TypeID2TypeName(helperArgType);
849 if (helperArgTypeName.empty()) {
850 ThrowJitBuildActionHelperTypeError(actionTypeNameBase, helperArgType);
851 }
852
853 // Build a call to CallBuildAction with the appropriate argument. When run through the interpreter, this code will
854 // just-in-time create an RAction object and it will assign it to its corresponding RJittedAction.
855 std::stringstream createAction_str;
856 createAction_str << "(const std::vector<std::string> &colNames, "
857 << "ROOT::Internal::RDF::RColumnRegister &colRegister, "
858 << "ROOT::Detail::RDF::RLoopManager &lm, "
859 << "void *jittedAction, "
860 << "std::shared_ptr<void> *helperArg) {\n";
861 createAction_str << " ROOT::Internal::RDF::CallBuildAction<" << actionTypeName;
862 const auto columnTypeNames = GetValidatedArgTypes(cols, colRegister, tree, ds, actionTypeNameBase, vector2RVec);
863 for (auto &colType : columnTypeNames)
864 createAction_str << ", " << colType;
865 createAction_str << ">("
866 << "colNames, "
867 << "colRegister, "
868 << "lm, "
869 << "reinterpret_cast<ROOT::Internal::RDF::RJittedAction *>(jittedAction), " << nSlots << ", "
870 << "reinterpret_cast<std::shared_ptr<" << helperArgTypeName << "> *>(helperArg)"
871 << ");\n}\n";
872 return createAction_str.str();
873}
874
875bool AtLeastOneEmptyString(const std::vector<std::string_view> strings)
876{
877 for (const auto &s : strings) {
878 if (s.empty())
879 return true;
880 }
881 return false;
882}
883
884std::shared_ptr<RNodeBase> UpcastNode(std::shared_ptr<RNodeBase> ptr)
885{
886 return ptr;
887}
888
889/// Given the desired number of columns and the user-provided list of columns:
890/// * fallback to using the first nColumns default columns if needed (or throw if nColumns > nDefaultColumns)
891/// * check that selected column names refer to valid branches, custom columns or datasource columns (throw if not)
892/// * replace column names from aliases by the actual column name
893/// Return the list of selected column names.
894ColumnNames_t GetValidatedColumnNames(RLoopManager &lm, const unsigned int nColumns, const ColumnNames_t &columns,
895 const RColumnRegister &colRegister, RDataSource *ds)
896{
897 auto selectedColumns = SelectColumns(nColumns, columns, lm.GetDefaultColumnNames());
898
899 for (auto &col : selectedColumns) {
900 col = colRegister.ResolveAlias(col);
901 }
902
903 // Complain if there are still unknown columns at this point
904 auto unknownColumns = FindUnknownColumns(selectedColumns, colRegister, ds ? ds->GetColumnNames() : ColumnNames_t{});
905
906 if (!unknownColumns.empty()) {
907 // Some columns are still unknown, we need to understand if the error
908 // should be printed or if the user requested to explicitly disable it.
909 // Look for a possible overlap between the unknown columns and the
910 // columns we should ignore for the purpose of the following exception
911 std::set<std::string> intersection;
912 const auto &colsToIgnore = lm.GetSuppressErrorsForMissingBranches();
913 std::sort(unknownColumns.begin(), unknownColumns.end());
914 std::set_intersection(unknownColumns.cbegin(), unknownColumns.cend(), colsToIgnore.cbegin(), colsToIgnore.cend(),
915 std::inserter(intersection, intersection.begin()));
916 if (intersection.empty()) {
917 std::string errMsg = std::string("Unknown column") + (unknownColumns.size() > 1 ? "s: " : ": ");
918 for (auto &unknownColumn : unknownColumns)
919 errMsg += '"' + unknownColumn + "\", ";
920 errMsg.resize(errMsg.size() - 2); // remove last ", "
921 throw std::runtime_error(errMsg);
922 }
923 }
924
925 return selectedColumns;
926}
927
928std::vector<std::string> GetValidatedArgTypes(const ColumnNames_t &colNames, const RColumnRegister &colRegister,
929 TTree *tree, RDataSource *ds, const std::string &context,
930 bool vector2RVec)
931{
932 auto toCheckedArgType = [&](const std::string &c) {
933 RDFDetail::RDefineBase *define = colRegister.GetDefine(c);
934 const auto colType = ColumnName2ColumnTypeName(c, tree, ds, define, vector2RVec);
935 if (colType.rfind("CLING_UNKNOWN_TYPE", 0) == 0) { // the interpreter does not know this type
936 const auto msg =
937 "The type of custom column \"" + c + "\" (" + colType.substr(19) +
938 ") is not known to the interpreter, but a just-in-time-compiled " + context +
939 " call requires this column. Make sure to create and load ROOT dictionaries for this column's class.";
940 throw std::runtime_error(msg);
941 }
942 return colType;
943 };
944 std::vector<std::string> colTypes;
945 colTypes.reserve(colNames.size());
946 std::transform(colNames.begin(), colNames.end(), std::back_inserter(colTypes), toCheckedArgType);
947 return colTypes;
948}
949
950void CheckForDuplicateSnapshotColumns(const ColumnNames_t &cols)
951{
952 std::unordered_set<std::string> uniqueCols;
953 for (auto &col : cols) {
954 if (!uniqueCols.insert(col).second) {
955 const auto msg = "Error: column \"" + col +
956 "\" was passed to Snapshot twice. This is not supported: only one of the columns would be "
957 "readable with RDataFrame.";
958 throw std::logic_error(msg);
959 }
960 }
961}
962
963void CheckSnapshotOptionsFormatCompatibility(const ROOT::RDF::RSnapshotOptions &opts)
964{
965 const ROOT::RDF::RSnapshotOptions defaultSnapshotOpts;
966 std::string optionName;
967
970 if (opts.fApproxZippedClusterSize != defaultSnapshotOpts.fApproxZippedClusterSize) {
971 optionName = "fApproxZippedClusterSize";
972 } else if (opts.fMaxUnzippedClusterSize != defaultSnapshotOpts.fMaxUnzippedClusterSize) {
973 optionName = "fMaxUnzippedClusterSize";
974 } else if (opts.fInitialUnzippedPageSize != defaultSnapshotOpts.fInitialUnzippedPageSize) {
975 optionName = "fInitialUnzippedPageSize";
976 } else if (opts.fMaxUnzippedPageSize != defaultSnapshotOpts.fMaxUnzippedPageSize) {
977 optionName = "fMaxUnzippedPageSize";
978 } else if (opts.fEnablePageChecksums != defaultSnapshotOpts.fEnablePageChecksums) {
979 optionName = "fEnablePageChecksums";
980 } else if (opts.fEnableSamePageMerging != defaultSnapshotOpts.fEnableSamePageMerging) {
981 optionName = "fEnableSamePageMerging";
982 }
983
984 if (!optionName.empty()) {
985 Warning("Snapshot",
986 "The RNTuple-specific %s option in RSnapshotOptions has been set, but the output format is "
987 "set to TTree, so this option won't have any effect. Use the other options available in "
988 "RSnapshotOptions to "
989 "configure the output TTree. Alternatively, change fOutputFormat to snapshot to RNTuple instead.",
990 optionName.c_str());
991 }
993 if (opts.fAutoFlush != defaultSnapshotOpts.fAutoFlush) {
994 optionName = "fAutoFlush";
995 } else if (opts.fSplitLevel != defaultSnapshotOpts.fSplitLevel) {
996 optionName = "fSplitLevel";
997 } else if (opts.fBasketSize != defaultSnapshotOpts.fBasketSize) {
998 optionName = "fBasketSize";
999 }
1000
1001 if (!optionName.empty()) {
1002 Warning(
1003 "Snapshot",
1004 "The TTree-specific %s option in RSnapshotOptions has been set, but the output format is set to RNTuple, "
1005 "so this option won't have any effect. Use the fNTupleWriteOptions option available in RSnapshotOptions to "
1006 "configure the output RNTuple. Alternatively, change fOutputFormat to snapshot to TTree instead.",
1007 optionName.c_str());
1008 }
1009 }
1010}
1011
1012/// Return copies of colsWithoutAliases and colsWithAliases with size branches for variable-sized array branches added
1013/// in the right positions (i.e. before the array branches that need them).
1014std::pair<std::vector<std::string>, std::vector<std::string>>
1015AddSizeBranches(ROOT::RDF::RDataSource *ds, std::vector<std::string> &&colsWithoutAliases,
1016 std::vector<std::string> &&colsWithAliases)
1017{
1018 TTree *tree{};
1019 if (auto treeDS = dynamic_cast<ROOT::Internal::RDF::RTTreeDS *>(ds))
1020 tree = treeDS->GetTree();
1021 if (!tree) // nothing to do
1022 return {std::move(colsWithoutAliases), std::move(colsWithAliases)};
1023
1024 assert(colsWithoutAliases.size() == colsWithAliases.size());
1025
1026 auto nCols = colsWithoutAliases.size();
1027 // Use index-iteration as we modify the vector during the iteration.
1028 for (std::size_t i = 0u; i < nCols; ++i) {
1029 const auto &colName = colsWithoutAliases[i];
1030
1031 auto *b = tree->GetBranch(colName.c_str());
1032 if (!b) // try harder
1033 b = tree->FindBranch(colName.c_str());
1034
1035 if (!b)
1036 continue;
1037
1038 auto *leaves = b->GetListOfLeaves();
1039 if (b->IsA() != TBranch::Class() || leaves->GetEntries() != 1)
1040 continue; // this branch is not a variable-sized array, nothing to do
1041
1042 TLeaf *countLeaf = static_cast<TLeaf *>(leaves->At(0))->GetLeafCount();
1043 if (!countLeaf || IsStrInVec(countLeaf->GetName(), colsWithoutAliases))
1044 continue; // not a variable-sized array or the size branch is already there, nothing to do
1045
1046 // otherwise we must insert the size in colsWithoutAliases _and_ colsWithAliases
1047 colsWithoutAliases.insert(colsWithoutAliases.begin() + i, countLeaf->GetName());
1048 colsWithAliases.insert(colsWithAliases.begin() + i, countLeaf->GetName());
1049 ++nCols;
1050 ++i; // as we inserted an element in the vector we iterate over, we need to move the index forward one extra time
1051 }
1052
1053 return {std::move(colsWithoutAliases), std::move(colsWithAliases)};
1054}
1055
1056void RemoveDuplicates(ColumnNames_t &columnNames)
1057{
1058 std::set<std::string> uniqueCols;
1059 columnNames.erase(
1060 std::remove_if(columnNames.begin(), columnNames.end(),
1061 [&uniqueCols](const std::string &colName) { return !uniqueCols.insert(colName).second; }),
1062 columnNames.end());
1063}
1064
1065void RemoveRNTupleSubfields(ColumnNames_t &columnNames)
1066{
1067 ColumnNames_t parentFields;
1068
1069 std::copy_if(columnNames.cbegin(), columnNames.cend(), std::back_inserter(parentFields),
1070 [](const std::string &colName) { return colName.find('.') == std::string::npos; });
1071
1072 columnNames.erase(std::remove_if(columnNames.begin(), columnNames.end(),
1073 [&parentFields](const std::string &colName) {
1074 if (colName.find('.') == std::string::npos)
1075 return false;
1076 const auto parentFieldName = colName.substr(0, colName.find_first_of('.'));
1077 return std::find(parentFields.cbegin(), parentFields.cend(), parentFieldName) !=
1078 parentFields.end();
1079 }),
1080 columnNames.end());
1081}
1082} // namespace RDF
1083} // namespace Internal
1084} // namespace ROOT
1085
1086namespace {
1087void AddDataSourceColumn(const std::string &colName, const std::type_info &typeID, ROOT::Detail::RDF::RLoopManager &lm,
1089{
1090
1091 if (colRegister.IsDefineOrAlias(colName))
1092 return;
1093
1094 if (lm.HasDataSourceColumnReaders(colName, typeID))
1095 return;
1096
1097 if (!ds.HasColumn(colName) &&
1099 return;
1100
1101 const auto nSlots = lm.GetNSlots();
1102 std::vector<std::unique_ptr<ROOT::Detail::RDF::RColumnReaderBase>> colReaders;
1103 colReaders.reserve(nSlots);
1104 // TODO consider changing the interface so we return all of these for all slots in one go
1105 for (auto slot = 0u; slot < nSlots; ++slot)
1106 colReaders.emplace_back(
1107 ROOT::Internal::RDF::CreateColumnReader(ds, slot, colName, typeID, /*treeReader*/ nullptr));
1108
1109 lm.AddDataSourceColumnReaders(colName, std::move(colReaders), typeID);
1110}
1111} // namespace
1112
1113void ROOT::Internal::RDF::AddDSColumns(const std::vector<std::string> &colNames, ROOT::Detail::RDF::RLoopManager &lm,
1115 const std::vector<const std::type_info *> &colTypeIDs,
1117{
1118 auto nCols = colNames.size();
1119 assert(nCols == colTypeIDs.size() && "Must provide exactly one column type for each column to create");
1120 for (decltype(nCols) i{}; i < nCols; i++) {
1121 AddDataSourceColumn(colNames[i], *colTypeIDs[i], lm, ds, colRegister);
1122 }
1123}
free(fBuffer)
#define b(i)
Definition RSha256.hxx:100
#define c(i)
Definition RSha256.hxx:101
#define a(i)
Definition RSha256.hxx:99
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:139
void Warning(const char *location, const char *msgfmt,...)
Use this function in warning situations.
Definition TError.cxx:252
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 cname
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
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
Option_t Option_t TPoint TPoint const char text
char name[80]
Definition TGX11.cxx:142
R__EXTERN TVirtualMutex * gROOTMutex
Definition TROOT.h:63
#define gROOT
Definition TROOT.h:417
#define R__LOCKGUARD(mutex)
The head node of a RDF computation graph.
const std::set< std::string > & GetSuppressErrorsForMissingBranches() const
void AddDataSourceColumnReaders(std::string_view col, std::vector< std::unique_ptr< RColumnReaderBase > > &&readers, const std::type_info &ti)
bool HasDataSourceColumnReaders(std::string_view col, const std::type_info &ti) const
Return true if AddDataSourceColumnReaders was called for column name col.
A binder for user-defined columns, variations and aliases.
bool IsDefineOrAlias(std::string_view name) const
Check if the provided name is tracked in the names list.
std::string_view ResolveAlias(std::string_view alias) const
Return the actual column name that the alias resolves to.
RDataSource defines an API that RDataFrame can use to read arbitrary data formats.
virtual bool HasColumn(std::string_view colName) const =0
Checks if the dataset has a certain column.
static TClass * Class()
TObjArray * GetListOfLeaves()
Definition TBranch.h:256
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:2999
A TLeaf describes individual elements of a TBranch See TBranch structure in TTree.
Definition TLeaf.h:57
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
Basic string class.
Definition TString.h:137
A TTree represents a columnar dataset.
Definition TTree.h:95
virtual TBranch * FindBranch(const char *name)
Return the branch that correspond to the path 'branchname', which can include the name of the tree or...
Definition TTree.cxx:4971
virtual TBranch * GetBranch(const char *name)
Return pointer to the branch with the given name in this tree or its friends.
Definition TTree.cxx:5459
virtual TTree * GetTree() const
Definition TTree.h:618
std::string TypeID2TypeName(const std::type_info &id)
Returns the name of a type starting from its type_info An empty string is returned in case of failure...
Definition RDFUtils.cxx:212
bool IsStrInVec(const std::string &str, const std::vector< std::string > &vec)
Definition RDFUtils.cxx:572
std::string PrettyPrintAddr(const void *const addr)
std::unique_ptr< ROOT::Detail::RDF::RColumnReaderBase > CreateColumnReader(ROOT::RDF::RDataSource &ds, unsigned int slot, std::string_view col, const std::type_info &tid, TTreeReader *treeReader)
Definition RDFUtils.cxx:720
std::string ColumnName2ColumnTypeName(const std::string &colName, TTree *, RDataSource *, RDefineBase *, bool vector2RVec=true)
Return a string containing the type of the given branch.
Definition RDFUtils.cxx:351
std::vector< T > Union(const std::vector< T > &v1, const std::vector< T > &v2)
Return a vector with all elements of v1 and v2 and duplicates removed.
Definition Utils.hxx:259
bool IsInternalColumn(std::string_view colName)
Whether custom column with name colName is an "internal" column such as rdfentry_ or rdfslot_.
Definition RDFUtils.cxx:504
void InterpreterDeclare(const std::string &code)
Declare code in the interpreter via the TInterpreter::Declare method, throw in case of errors.
Definition RDFUtils.cxx:455
std::vector< std::string > ColumnNames_t
char * DemangleTypeIdName(const std::type_info &ti, int &errorCode)
Demangle in a portable way the type id name.
if(fPos !=fText.size()) Fail("unexpected trailing content")
A collection of options to steer the creation of the dataset on disk through Snapshot().
std::size_t fMaxUnzippedPageSize
*(RNTuple only)* Maximum allowed page size before compression
int fAutoFlush
*(TTree only)* AutoFlush value for output tree
ESnapshotOutputFormat fOutputFormat
Which data format to write to.
bool fEnableSamePageMerging
*(RNTuple only)* Enable identical-page deduplication. Requires page checksumming
std::size_t fInitialUnzippedPageSize
*(RNTuple only)* Initial page size before compression
bool fEnablePageChecksums
*(RNTuple only)* Enable checksumming for pages
std::size_t fApproxZippedClusterSize
*(RNTuple only)* Approximate target compressed cluster size
int fSplitLevel
*(TTree only)* Split level of output tree
std::size_t fMaxUnzippedClusterSize
*(RNTuple only)* Maximum uncompressed cluster size
int fBasketSize
*(TTree only)* Set a custom basket size option.