Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RNTupleProcessor.cxx
Go to the documentation of this file.
1/// \file RNTupleProcessor.cxx
2/// \author Florine de Geus <florine.de.geus@cern.ch>
3/// \date 2024-03-26
4/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
5/// is welcome!
6
7/*************************************************************************
8 * Copyright (C) 1995-2024, Rene Brun and Fons Rademakers. *
9 * All rights reserved. *
10 * *
11 * For the licensing terms see $ROOTSYS/LICENSE. *
12 * For the list of contributors see $ROOTSYS/README/CREDITS. *
13 *************************************************************************/
14
16
17#include <ROOT/RFieldBase.hxx>
18#include <ROOT/RNTuple.hxx>
20#include <ROOT/StringUtils.hxx>
21
22#include <TDirectory.h>
23
24#include <iomanip>
25
28{
29 processor.AddAllFieldsToEntry(RNTupleProcessorProvenance(), includeSubfields);
30 return processor.fEntry.get();
31}
32
33//------------------------------------------------------------------------------
34
35std::unique_ptr<ROOT::Internal::RPageSource> ROOT::Experimental::RNTupleOpenSpec::CreatePageSource() const
36{
37 if (const std::string *storagePath = std::get_if<std::string>(&fStorage))
39
40 auto dir = std::get<TDirectory *>(fStorage);
41 auto ntuple = std::unique_ptr<ROOT::RNTuple>(dir->Get<ROOT::RNTuple>(fNTupleName.c_str()));
43}
44
45std::unique_ptr<ROOT::Experimental::RNTupleProcessor>
47{
48 return std::unique_ptr<RNTupleSingleProcessor>(new RNTupleSingleProcessor(std::move(ntuple), options));
49}
50
51std::unique_ptr<ROOT::Experimental::RNTupleProcessor>
52ROOT::Experimental::RNTupleProcessor::CreateChain(std::vector<RNTupleOpenSpec> ntuples,
53 const RNTupleProcessorOptions &options)
54{
55 if (ntuples.empty())
56 throw RException(R__FAIL("at least one RNTuple must be provided"));
57
58 std::vector<std::unique_ptr<RNTupleProcessor>> innerProcessors;
59 innerProcessors.reserve(ntuples.size());
60
61 for (auto &ntuple : ntuples) {
62 innerProcessors.emplace_back(Create(std::move(ntuple)));
63 }
64
65 return CreateChain(std::move(innerProcessors), options);
66}
67
68std::unique_ptr<ROOT::Experimental::RNTupleProcessor>
69ROOT::Experimental::RNTupleProcessor::CreateChain(std::vector<std::unique_ptr<RNTupleProcessor>> innerProcessors,
70 const RNTupleProcessorOptions &options)
71{
72 if (innerProcessors.empty())
73 throw RException(R__FAIL("at least one inner processor must be provided"));
74
75 return std::unique_ptr<RNTupleChainProcessor>(new RNTupleChainProcessor(std::move(innerProcessors), options));
76}
77
78std::unique_ptr<ROOT::Experimental::RNTupleProcessor>
80 const std::vector<std::string> &joinFields,
81 const RNTupleProcessorOptions &options)
82{
83 if (joinFields.size() > 4) {
84 throw RException(R__FAIL("a maximum of four join fields is allowed"));
85 }
86
87 if (std::unordered_set(joinFields.begin(), joinFields.end()).size() < joinFields.size()) {
88 throw RException(R__FAIL("join fields must be unique"));
89 }
90
91 std::unique_ptr<RNTupleProcessor> primaryProcessor = RNTupleProcessor::Create(std::move(primaryNTuple), options);
92
93 std::unique_ptr<RNTupleProcessor> auxProcessor = RNTupleProcessor::Create(std::move(auxNTuple));
94
95 return CreateRNTupleJoinProcessor(std::move(primaryProcessor), std::move(auxProcessor), joinFields, options);
96}
97
98std::unique_ptr<ROOT::Experimental::RNTupleProcessor>
99ROOT::Experimental::CreateRNTupleJoinProcessor(std::unique_ptr<RNTupleProcessor> primaryProcessor,
100 std::unique_ptr<RNTupleProcessor> auxProcessor,
101 const std::vector<std::string> &joinFields,
102 const RNTupleProcessorOptions &options)
103{
104 if (joinFields.size() > 4) {
105 throw RException(R__FAIL("a maximum of four join fields is allowed"));
106 }
107
108 if (std::unordered_set(joinFields.begin(), joinFields.end()).size() < joinFields.size()) {
109 throw RException(R__FAIL("join fields must be unique"));
110 }
111
112 return std::unique_ptr<RNTupleJoinProcessor>(
113 new RNTupleJoinProcessor(std::move(primaryProcessor), std::move(auxProcessor), joinFields, options));
114}
115
116//------------------------------------------------------------------------------
117
126
128 std::shared_ptr<ROOT::Experimental::Internal::RNTupleProcessorEntry> entry)
129{
130 // The processor has already been initialized.
131 if (IsInitialized() && fPageSource)
132 return;
133
134 if (!entry)
135 fEntry = std::make_shared<Internal::RNTupleProcessorEntry>();
136 else
137 fEntry = std::move(entry);
138
139 fPageSource = fNTupleSpec.CreatePageSource();
140 fPageSource->Attach();
141
142 fNEntries = fPageSource->GetNEntries();
143}
144
146{
147 Initialize();
148 auto desc = fPageSource->GetSharedDescriptorGuard();
149 auto fieldZeroId = desc->GetFieldZeroId();
150
151 // TODO handle subfields
152 return desc->FindFieldId(fieldName, fieldZeroId) != ROOT::kInvalidDescriptorId;
153}
154
155std::unique_ptr<ROOT::RFieldBase>
157 const std::string &typeName)
158{
159 assert(fPageSource);
160
161 // Strip the "R_rntproc_join_" prefix (for join fields) from the field name, if present.
162 const std::string onDiskFieldName =
163 qualifiedFieldName.find("R_rntproc_join_") == 0 ? qualifiedFieldName.substr(15) : qualifiedFieldName;
164
165 auto descGuard = fPageSource->GetSharedDescriptorGuard();
166 const auto &desc = descGuard.GetConstRef();
167
168 const auto onDiskFieldId = desc.FindFieldId(onDiskFieldName);
169
170 if (onDiskFieldId == kInvalidDescriptorId) {
171 return nullptr;
172 }
173
174 std::unique_ptr<ROOT::RFieldBase> field;
175 if (typeName.empty()) {
176 const auto &fieldDesc = desc.GetFieldDescriptor(onDiskFieldId);
177 field = fieldDesc.CreateField(desc);
178 } else {
179 // Strip the parent field name prefix(es), if present.
180 std::string subfieldName = onDiskFieldName;
181 auto posDot = onDiskFieldName.find_last_of('.');
182 if (posDot != std::string::npos)
183 subfieldName = onDiskFieldName.substr(posDot + 1);
184
185 field = ROOT::RFieldBase::Create(subfieldName, typeName).Unwrap();
186 }
187
188 field->SetOnDiskId(onDiskFieldId);
189 return ConnectField(std::move(field));
190}
191
192std::unique_ptr<ROOT::RFieldBase>
193ROOT::Experimental::RNTupleSingleProcessor::ConnectField(std::unique_ptr<ROOT::RFieldBase> field)
194{
195 ROOT::RFieldZero fieldZero;
197 fieldZero.Attach(std::move(field));
198 ROOT::Internal::CallConnectPageSourceOnField(fieldZero, *fPageSource);
199 return std::move(fieldZero.ReleaseSubfields()[0]);
200}
201
204 const std::string &typeName, void *valuePtr,
205 const Internal::RNTupleProcessorProvenance &provenance)
206{
207 auto fieldIdx = fEntry->FindFieldIndex(canonicalFieldName, typeName);
208 if (!fieldIdx) {
209 // Strip the processor name prefix(es), if present.
210 std::string qualifiedFieldName = canonicalFieldName;
211 if (provenance.IsPresentInFieldName(qualifiedFieldName)) {
212 qualifiedFieldName = qualifiedFieldName.substr(provenance.Get().size() + 1);
213 }
214
215 auto field = CreateAndConnectField(qualifiedFieldName, typeName);
216
217 if (!field) {
218 throw RException(R__FAIL("cannot register field with name \"" + qualifiedFieldName +
219 "\" because it is not present in the on-disk information of the RNTuple(s) this "
220 "processor is created from"));
221 }
222
223 fieldIdx = fEntry->AddField(qualifiedFieldName, std::move(field), valuePtr, provenance);
224 }
225
226 return *fieldIdx;
227}
228
231 const std::string &canonicalFieldName, void *valuePtr,
232 const Internal::RNTupleProcessorProvenance &provenance)
233{
234 auto fieldIdx = fEntry->FindFieldIndex(canonicalFieldName, field->GetTypeName());
235 if (!fieldIdx) {
236 // Strip the processor name prefix(es), if present.
237 std::string qualifiedFieldName = canonicalFieldName;
238 if (provenance.IsPresentInFieldName(qualifiedFieldName)) {
239 qualifiedFieldName = qualifiedFieldName.substr(provenance.Get().size() + 1);
240 }
241
242 field = ConnectField(std::move(field));
243
244 if (!field) {
245 throw RException(R__FAIL("cannot register field with name \"" + qualifiedFieldName +
246 "\" because it is not present in the on-disk information of the RNTuple(s) this "
247 "processor is created from"));
248 }
249
250 fieldIdx = fEntry->AddField(qualifiedFieldName, std::move(field), valuePtr, provenance);
251 }
252
253 return *fieldIdx;
254}
255
257 const Internal::RNTupleProcessorProvenance &provenance, bool includeSubfields)
258{
259 Initialize();
260 auto descGuard = fPageSource->GetSharedDescriptorGuard();
261 const auto &desc = descGuard.GetConstRef();
262 auto fnAddSubfields = [this, &desc, &provenance](const ROOT::RFieldDescriptor &field, auto &fn) -> void {
263 std::string fieldName = desc.GetQualifiedFieldName(field.GetId());
264 if (auto prefix = provenance.Get(); !prefix.empty())
265 fieldName = prefix + "." + fieldName;
266
267 AddFieldToEntry(fieldName, field.GetTypeName(), nullptr, provenance);
268 for (const auto &subfield : desc.GetFieldIterable(field.GetId())) {
269 fn(subfield, fn);
270 }
271 };
272
273 for (const auto &field : desc.GetTopLevelFields()) {
274 if (includeSubfields) {
275 fnAddSubfields(field, fnAddSubfields);
276 } else {
277 std::string fieldName = desc.GetQualifiedFieldName(field.GetId());
278 if (auto prefix = provenance.Get(); !prefix.empty())
279 fieldName = prefix + "." + fieldName;
280
281 AddFieldToEntry(fieldName, field.GetTypeName(), nullptr, provenance);
282 }
283 }
284}
285
287 REntryMapping &mapping)
288{
289 if (globalEntryNumber >= fNEntries || !fEntry)
291 else
292 mapping.SetEntryNumber(globalEntryNumber);
293}
294
296{
297 assert(mapping.GetEntryNumber() != kInvalidNTupleIndex);
298
299 for (auto fieldIdx : fFieldIdxs) {
300 fEntry->ReadValue(fieldIdx, mapping.GetEntryNumber());
301 }
302
303 fNEntriesProcessed++;
304}
305
307 const std::unordered_set<ROOT::Experimental::Internal::RNTupleProcessorEntry::FieldIndex_t> &fieldIdxs,
308 const Internal::RNTupleProcessorProvenance & /* provenance */, bool updateFields)
309{
310 Initialize(fEntry);
311
312 fFieldIdxs = fieldIdxs;
313
314 if (updateFields) {
315 for (const auto &fieldIdx : fFieldIdxs) {
316 const auto &currField = fEntry->GetValue(fieldIdx).GetField();
317 auto newField = CreateAndConnectField(fEntry->GetQualifiedFieldName(fieldIdx), currField.GetTypeName());
318
319 fEntry->UpdateField(fieldIdx, std::move(newField));
320 }
321 }
322}
323
325{
326 if (fPageSource) {
327 fEntry->ResetFields(fFieldIdxs);
328 fPageSource.reset();
329 }
330}
331
333 ROOT::NTupleSize_t entryOffset)
334{
335 Connect(fFieldIdxs);
336 joinTable.Add(*fPageSource, Internal::RNTupleJoinTable::kDefaultPartitionKey, entryOffset);
337}
338
340{
341 static constexpr int width = 32;
342
343 std::string ntupleNameTrunc = fNTupleSpec.fNTupleName.substr(0, width - 4);
344 if (ntupleNameTrunc.size() < fNTupleSpec.fNTupleName.size())
345 ntupleNameTrunc = fNTupleSpec.fNTupleName.substr(0, width - 6) + "..";
346
347 output << "+" << std::setfill('-') << std::setw(width - 1) << "+\n";
348 output << std::setfill(' ') << "| " << ntupleNameTrunc << std::setw(width - 2 - ntupleNameTrunc.size()) << " |\n";
349
350 if (const std::string *storage = std::get_if<std::string>(&fNTupleSpec.fStorage)) {
351 std::string storageTrunc = storage->substr(0, width - 5);
352 if (storageTrunc.size() < storage->size())
353 storageTrunc = storage->substr(0, width - 8) + "...";
354
355 output << std::setfill(' ') << "| " << storageTrunc << std::setw(width - 2 - storageTrunc.size()) << " |\n";
356 } else {
357 output << "| " << std::setw(width - 2) << " |\n";
358 }
359
360 output << "+" << std::setfill('-') << std::setw(width - 1) << "+\n";
361}
362
363//------------------------------------------------------------------------------
364
366 std::vector<std::unique_ptr<RNTupleProcessor>> processors, const RNTupleProcessorOptions &options)
367 : RNTupleProcessor(options), fInnerProcessors(std::move(processors))
368{
369 if (fOptions.GetProcessorName().empty()) {
370 // `CreateChain` ensures there is at least one inner processor.
372 }
373
375}
376
378 std::shared_ptr<ROOT::Experimental::Internal::RNTupleProcessorEntry> entry)
379{
380 if (IsInitialized())
381 return;
382
383 if (!entry)
384 fEntry = std::make_shared<Internal::RNTupleProcessorEntry>();
385 else
386 fEntry = std::move(entry);
387
388 fInnerProcessors[0]->Initialize(fEntry);
389}
390
392{
393 if (fNEntries == kInvalidNTupleIndex) {
394 fNEntries = 0;
395
396 for (unsigned i = 0; i < fInnerProcessors.size(); ++i) {
397 if (fInnerNEntries[i] == kInvalidNTupleIndex) {
398 fInnerProcessors[i]->Initialize(fEntry);
399 fInnerNEntries[i] = fInnerProcessors[i]->GetNEntries();
400 }
401
402 fNEntries += fInnerNEntries[i];
403 }
404 }
405
406 return fNEntries;
407}
408
410 const std::unordered_set<ROOT::Experimental::Internal::RNTupleProcessorEntry::FieldIndex_t> &fieldIdxs,
411 const Internal::RNTupleProcessorProvenance &provenance, bool /* updateFields */)
412{
413 Initialize();
414 fFieldIdxs = fieldIdxs;
415 fProvenance = provenance;
416 ConnectInnerProcessor(fCurrentProcessorNumber);
417}
418
420{
421 for (const auto &innerProc : fInnerProcessors) {
422 innerProc->Disconnect();
423 }
424}
425
427{
428 if (fCurrentProcessorNumber != processorNumber) {
429 fInnerProcessors[fCurrentProcessorNumber]->Disconnect();
430 fCurrentProcessorNumber = processorNumber;
431 }
432
433 auto &innerProc = fInnerProcessors[processorNumber];
434 innerProc->Initialize(fEntry);
435 innerProc->Connect(fFieldIdxs, fProvenance, /*updateFields=*/true);
436}
437
439ROOT::Experimental::RNTupleChainProcessor::AddFieldToEntry(const std::string &fieldName, const std::string &typeName,
440 void *valuePtr,
441 const Internal::RNTupleProcessorProvenance &provenance)
442{
443 return fInnerProcessors[fCurrentProcessorNumber]->AddFieldToEntry(fieldName, typeName, valuePtr, provenance);
444}
445
448 const std::string &fieldName, void *valuePtr,
449 const Internal::RNTupleProcessorProvenance &provenance)
450{
451 return fInnerProcessors[fCurrentProcessorNumber]->AddFieldToEntry(std::move(field), fieldName, valuePtr, provenance);
452}
453
455 const Internal::RNTupleProcessorProvenance &provenance, bool includeSubfields)
456{
457 Initialize();
458 fInnerProcessors[0]->AddAllFieldsToEntry(provenance, includeSubfields);
459}
460
462 REntryMapping &mapping)
463{
464 std::size_t currProcessorIdx = mapping.GetInnerProcessorIdx();
465
466 // If the requested entry number is lower than the current entry number, we have to again localise the correct local
467 // entry number starting from the first processor in the chain. Otherwise, we can continue looking from the inner
468 // processor that is currently connected, which is much faster when the chain consists of many inner processors.
469 if (fLastLoadedEntry != ROOT::kInvalidNTupleIndex && globalEntryNumber < fLastLoadedEntry) {
470 currProcessorIdx = 0;
471 ConnectInnerProcessor(currProcessorIdx);
472 }
473
474 ROOT::NTupleSize_t entriesSeen = 0;
475 for (unsigned i = 0; i < currProcessorIdx; ++i) {
476 if (fInnerNEntries[i] == kInvalidNTupleIndex) {
477 fInnerNEntries[i] = fInnerProcessors[i]->GetNEntries();
478 }
479
480 if (fInnerNEntries[i] >= globalEntryNumber) {
481 currProcessorIdx = 0;
482 ConnectInnerProcessor(currProcessorIdx);
483 break;
484 }
485 entriesSeen += fInnerNEntries[i];
486 }
487
488 ROOT::NTupleSize_t localEntryNumber = globalEntryNumber - entriesSeen;
489 fInnerProcessors[currProcessorIdx]->SetEntryMapping(localEntryNumber, mapping.GetInnerProcessorMapping());
490
491 // As long as the entry fails to load from the current processor, we decrement the local entry number with the number
492 // of entries in this processor and try with the next processor until we find the correct local entry number.
494 if (fInnerNEntries[currProcessorIdx] == kInvalidNTupleIndex) {
495 fInnerNEntries[currProcessorIdx] = fInnerProcessors[currProcessorIdx]->GetNEntries();
496 }
497
498 localEntryNumber -= fInnerNEntries[currProcessorIdx];
499
500 // The provided global entry number is larger than the number of available entries.
501 if (++currProcessorIdx >= fInnerProcessors.size()) {
503 return;
504 }
505
506 ConnectInnerProcessor(currProcessorIdx);
507 fInnerProcessors[currProcessorIdx]->SetEntryMapping(localEntryNumber, mapping.GetInnerProcessorMapping());
508 }
509
510 mapping.SetEntryNumber(globalEntryNumber);
511 mapping.SetInnerProcessorIdx(currProcessorIdx);
512}
513
515{
516 assert(mapping.GetEntryNumber() != kInvalidNTupleIndex);
517
518 if (fCurrentProcessorNumber != mapping.GetInnerProcessorIdx())
519 ConnectInnerProcessor(mapping.GetInnerProcessorIdx());
520 fInnerProcessors[mapping.GetInnerProcessorIdx()]->LoadEntry(mapping.GetInnerProcessorMapping());
521 fLastLoadedEntry = mapping.GetEntryNumber();
522 fNEntriesProcessed++;
523}
524
526 ROOT::NTupleSize_t entryOffset)
527{
528 for (unsigned i = 0; i < fInnerProcessors.size(); ++i) {
529 const auto &innerProc = fInnerProcessors[i];
530 // TODO can this be done (more) lazily? I.e. only when a match cannot be found in the current inner proc?
531 innerProc->Initialize(fEntry);
532 innerProc->AddEntriesToJoinTable(joinTable, entryOffset);
533 entryOffset += innerProc->GetNEntries();
534 }
535}
536
538{
539 for (const auto &innerProc : fInnerProcessors) {
540 innerProc->PrintStructure(output);
541 }
542}
543
544//------------------------------------------------------------------------------
545
546ROOT::Experimental::RNTupleJoinProcessor::RNTupleJoinProcessor(std::unique_ptr<RNTupleProcessor> primaryProcessor,
547 std::unique_ptr<RNTupleProcessor> auxProcessor,
548 const std::vector<std::string> &joinFields,
549 const RNTupleProcessorOptions &options)
550 : RNTupleProcessor(options),
551 fPrimaryProcessor(std::move(primaryProcessor)),
552 fAuxiliaryProcessor(std::move(auxProcessor)),
553 fJoinFieldNames(joinFields)
554{
555 if (fOptions.GetProcessorName().empty()) {
556 fOptions.SetProcessorName(fPrimaryProcessor->fOptions.GetProcessorName());
557 }
558}
559
561 std::shared_ptr<ROOT::Experimental::Internal::RNTupleProcessorEntry> entry)
562{
563 if (IsInitialized())
564 return;
565
566 if (!entry)
567 fEntry = std::make_shared<Internal::RNTupleProcessorEntry>();
568 else
569 fEntry = std::move(entry);
570
571 fPrimaryProcessor->Initialize(fEntry);
572 fAuxiliaryProcessor->Initialize(fEntry);
573
574 if (!fJoinFieldNames.empty()) {
575 for (const auto &joinField : fJoinFieldNames) {
576 if (!fPrimaryProcessor->CanReadFieldFromDisk(joinField)) {
577 throw RException(R__FAIL("could not find join field \"" + joinField + "\" in primary processor \"" +
578 fPrimaryProcessor->fOptions.GetProcessorName() + "\""));
579 }
580 if (!fAuxiliaryProcessor->CanReadFieldFromDisk(joinField)) {
581 throw RException(R__FAIL("could not find join field \"" + joinField + "\" in auxiliary processor \"" +
582 fAuxiliaryProcessor->fOptions.GetProcessorName() + "\""));
583 }
584
585 // We prepend the name of the primary processor in this case to prevent reading from the wrong join field in
586 // composed join operations.
587 auto fieldIdx = AddFieldToEntry(fOptions.GetProcessorName() + ".R_rntproc_join_" + joinField, "std::uint64_t",
588 nullptr, Internal::RNTupleProcessorProvenance(fOptions.GetProcessorName()));
589 fJoinFieldIdxs.insert(fieldIdx);
590 }
591
592 fJoinTable = Internal::RNTupleJoinTable::Create(fJoinFieldNames);
593 }
594}
595
597 const std::unordered_set<ROOT::Experimental::Internal::RNTupleProcessorEntry::FieldIndex_t> &fieldIdxs,
598 const Internal::RNTupleProcessorProvenance &provenance, bool updateFields)
599{
600 Initialize();
601
602 auto auxProvenance = provenance.Evolve(fAuxiliaryProcessor->fOptions.GetProcessorName());
603 for (const auto &fieldIdx : fieldIdxs) {
604 const auto &fieldProvenance = fEntry->GetFieldProvenance(fieldIdx);
605 if (fieldProvenance.Contains(auxProvenance))
606 fAuxiliaryFieldIdxs.insert(fieldIdx);
607 else
608 fFieldIdxs.insert(fieldIdx);
609 }
610
611 fPrimaryProcessor->Connect(fFieldIdxs, provenance, updateFields);
612 fAuxiliaryProcessor->Connect(fAuxiliaryFieldIdxs, auxProvenance, updateFields);
613}
614
616{
617 fPrimaryProcessor->Disconnect();
618 fAuxiliaryProcessor->Disconnect();
619}
620
622ROOT::Experimental::RNTupleJoinProcessor::AddFieldToEntry(const std::string &fieldName, const std::string &typeName,
623 void *valuePtr,
624 const Internal::RNTupleProcessorProvenance &provenance)
625{
626 auto auxProvenance = provenance.Evolve(fAuxiliaryProcessor->fOptions.GetProcessorName());
627 if (auxProvenance.IsPresentInFieldName(fieldName)) {
628 // If the primaryProcessor has a field with the name of the auxProcessor (either as a "proper" field or because
629 // the primary processor itself is a join where its auxProcessor bears the same name as the current auxProcessor),
630 // there will be name conflicts, so error out.
631 if (fPrimaryProcessor->CanReadFieldFromDisk(fieldName)) {
632 throw RException(R__FAIL("ambiguous field name: \"" + fieldName +
633 "\" is present in the primary RNTupleProcessor \"" +
634 fPrimaryProcessor->fOptions.GetProcessorName() +
635 "\", but may also refer to a field in the auxiliary RNTupleProcessor named \"" +
636 fAuxiliaryProcessor->fOptions.GetProcessorName() +
637 "\". To avoid this ambiguity, rename the auxiliary RNTupleProcessor."));
638 }
639
640 auto fieldIdx = fAuxiliaryProcessor->AddFieldToEntry(fieldName, typeName, valuePtr, auxProvenance);
641 if (fieldIdx)
642 fAuxiliaryFieldIdxs.insert(fieldIdx);
643 return fieldIdx;
644 } else {
645 auto fieldIdx = fPrimaryProcessor->AddFieldToEntry(fieldName, typeName, valuePtr, provenance);
646 if (fieldIdx)
647 fFieldIdxs.insert(fieldIdx);
648 return fieldIdx;
649 }
650}
651
653ROOT::Experimental::RNTupleJoinProcessor::AddFieldToEntry(std::unique_ptr<ROOT::RFieldBase> field,
654 const std::string &fieldName, void *valuePtr,
655 const Internal::RNTupleProcessorProvenance &provenance)
656{
657 auto auxProvenance = provenance.Evolve(fAuxiliaryProcessor->GetOptions().GetProcessorName());
658 if (auxProvenance.IsPresentInFieldName(fieldName)) {
659 // If the primaryProcessor has a field with the name of the auxProcessor (either as a "proper" field or because
660 // the primary processor itself is a join where its auxProcessor bears the same name as the current auxProcessor),
661 // there will be name conflicts, so error out.
662 if (fPrimaryProcessor->CanReadFieldFromDisk(fieldName)) {
663 throw RException(R__FAIL("ambiguous field name: \"" + fieldName +
664 "\" is present in the primary RNTupleProcessor \"" +
665 fPrimaryProcessor->GetOptions().GetProcessorName() +
666 "\", but may also refer to a field in the auxiliary RNTupleProcessor named \"" +
667 fAuxiliaryProcessor->GetOptions().GetProcessorName() +
668 "\". To avoid this ambiguity, rename the auxiliary RNTupleProcessor."));
669 }
670
671 auto fieldIdx = fAuxiliaryProcessor->AddFieldToEntry(std::move(field), fieldName, valuePtr, auxProvenance);
672 fAuxiliaryFieldIdxs.insert(fieldIdx);
673 return fieldIdx;
674 } else {
675 auto fieldIdx = fPrimaryProcessor->AddFieldToEntry(std::move(field), fieldName, valuePtr, provenance);
676 if (fieldIdx)
677 fFieldIdxs.insert(fieldIdx);
678 return fieldIdx;
679 }
680}
681
683 const Internal::RNTupleProcessorProvenance &provenance, bool includeSubfields)
684{
685 Initialize();
686 fPrimaryProcessor->AddAllFieldsToEntry(provenance, includeSubfields);
687 auto auxProvenance = provenance.Evolve(fAuxiliaryProcessor->GetOptions().GetProcessorName());
688 fAuxiliaryProcessor->AddAllFieldsToEntry(auxProvenance, includeSubfields);
689}
690
692{
693 for (const auto &fieldIdx : fAuxiliaryFieldIdxs) {
694 fEntry->SetFieldValidity(fieldIdx, isValid);
695 }
696}
697
699 REntryMapping &mapping)
700{
701 fPrimaryProcessor->SetEntryMapping(globalEntryNumber, mapping.GetPrimaryProcessorMapping());
704 for (auto fieldIdx : fFieldIdxs) {
705 fEntry->SetFieldValidity(fieldIdx, false);
706 }
707 SetAuxiliaryFieldValidity(false);
708 return;
709 }
710 // We only set the entry mapping of the auxiliary ntuple here if the join is aligned. For unaligne joins, we need to
711 // first get the correct entry number from the join table, which requires reading join field data from disk. Because
712 // we want to do this only when the entry is actually loaded to avoid unnecessary reads, we postpone this to the
713 // actual `LoadEntry` call.
714 if (!fJoinTable) {
715 fAuxiliaryProcessor->SetEntryMapping(globalEntryNumber, mapping.GetAuxiliaryProcessorMapping());
716 }
717 mapping.SetEntryNumber(globalEntryNumber);
718}
719
721{
722 assert(mapping.GetEntryNumber() != kInvalidNTupleIndex);
723
724 fPrimaryProcessor->LoadEntry(mapping.GetPrimaryProcessorMapping());
725 fNEntriesProcessed++;
726
727 if (!fJoinTable) {
728 fAuxiliaryProcessor->LoadEntry(mapping.GetAuxiliaryProcessorMapping());
729 return;
730 }
731
732 if (!fJoinTableIsBuilt) {
733 fAuxiliaryProcessor->AddEntriesToJoinTable(*fJoinTable);
734 fJoinTableIsBuilt = true;
735 }
736
737 // Collect the values of the join fields for this entry.
738 std::vector<ROOT::Experimental::Internal::RNTupleJoinTable::JoinValue_t> values;
739 values.reserve(fJoinFieldIdxs.size());
740 for (const auto &fieldIdx : fJoinFieldIdxs) {
741 auto val = fEntry->GetValue(fieldIdx).GetRef<ROOT::Experimental::Internal::RNTupleJoinTable::JoinValue_t>();
742 values.push_back(val);
743 }
744
745 // Find the entry index corresponding to the join field values for each auxiliary processor and load the
746 // corresponding entry.
747 const auto entryIdx = fJoinTable->GetEntryIndex(values);
748 REntryMapping auxMapping;
749 fAuxiliaryProcessor->SetEntryMapping(entryIdx, auxMapping);
750
751 if (entryIdx == kInvalidNTupleIndex) {
752 SetAuxiliaryFieldValidity(false);
753 } else {
754 SetAuxiliaryFieldValidity(true);
755 fAuxiliaryProcessor->LoadEntry(auxMapping);
756 }
757}
758
760{
761 if (fNEntries == kInvalidNTupleIndex)
762 fNEntries = fPrimaryProcessor->GetNEntries();
763 return fNEntries;
764}
765
767 ROOT::NTupleSize_t entryOffset)
768{
769 fPrimaryProcessor->AddEntriesToJoinTable(joinTable, entryOffset);
770}
771
773{
774 std::ostringstream primaryStructureStr;
775 fPrimaryProcessor->PrintStructure(primaryStructureStr);
776 const auto primaryStructure = ROOT::Split(primaryStructureStr.str(), "\n", /*skipEmpty=*/true);
777 const auto primaryStructureWidth = primaryStructure.front().size();
778
779 std::ostringstream auxStructureStr;
780 fAuxiliaryProcessor->PrintStructure(auxStructureStr);
781 const auto auxStructure = ROOT::Split(auxStructureStr.str(), "\n", /*skipEmpty=*/true);
782
783 const auto maxLength = std::max(primaryStructure.size(), auxStructure.size());
784 for (unsigned i = 0; i < maxLength; i++) {
785 if (i < primaryStructure.size())
786 output << primaryStructure[i];
787 else
788 output << std::setw(primaryStructureWidth) << "";
789
790 if (i < auxStructure.size())
791 output << " " << auxStructure[i];
792
793 output << "\n";
794 }
795}
#define R__FAIL(msg)
Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult...
Definition RError.hxx:322
Option_t Option_t width
Builds a join table on one or several fields of an RNTuple so it can be joined onto other RNTuples.
static std::unique_ptr< RNTupleJoinTable > Create(const std::vector< std::string > &joinFieldNames)
Create an RNTupleJoinTable from an existing RNTuple.
RNTupleJoinTable & Add(ROOT::Internal::RPageSource &pageSource, PartitionKey_t partitionKey=kDefaultPartitionKey, ROOT::NTupleSize_t entryOffset=0)
Add an entry mapping to the join table.
static constexpr PartitionKey_t kDefaultPartitionKey
Collection of values in an RNTupleProcessor, analogous to REntry, with checks and support for missing...
std::string Get() const
Get the full processor provenance, in the form of "x.y.z".
bool IsPresentInFieldName(std::string_view fieldName) const
Check whether the provided field name contains this provenance.
RNTupleProcessorProvenance Evolve(const std::string &processorName) const
Add a new processor to the provenance.
Processor specialization for vertically combined (chained) RNTupleProcessors.
void PrintStructureImpl(std::ostream &output) const final
Processor-specific implementation for printing its structure, called by PrintStructure().
void AddEntriesToJoinTable(Internal::RNTupleJoinTable &joinTable, ROOT::NTupleSize_t entryOffset=0) final
Add the entry mappings for this processor to the provided join table.
void ConnectInnerProcessor(std::size_t processorNumber)
Update the entry to reflect any missing fields in the current inner processor.
Internal::RNTupleProcessorEntry::FieldIndex_t AddFieldToEntry(const std::string &fieldName, const std::string &typeName, void *valuePtr=nullptr, const Internal::RNTupleProcessorProvenance &provenance=Internal::RNTupleProcessorProvenance()) final
Add a field to the entry.
void AddAllFieldsToEntry(const Internal::RNTupleProcessorProvenance &provenance, bool includeSubfields) final
Add all known fields (from on-disk information) to the entry.
ROOT::NTupleSize_t GetNEntries() final
Get the total number of entries in this processor.
void Initialize(std::shared_ptr< Internal::RNTupleProcessorEntry > entry=nullptr) final
Initialize the processor by creating an (initially empty) fEntry, or setting an existing one.
std::vector< ROOT::NTupleSize_t > fInnerNEntries
void LoadEntryImpl(const REntryMapping &mapping) final
Load the entry identified by the entry mapping.
void SetEntryMapping(ROOT::NTupleSize_t globalEntryNumber, REntryMapping &mapping) final
Set the local entry mapping (for nested processors), according to a global entry number.
void Connect(const std::unordered_set< Internal::RNTupleProcessorEntry::FieldIndex_t > &fieldIdxs, const Internal::RNTupleProcessorProvenance &provenance=Internal::RNTupleProcessorProvenance(), bool updateFields=false) final
Connect the provided fields indices in the entry to their on-disk fields.
void Disconnect() final
Disconnect the processor from associated physical storage.
std::vector< std::unique_ptr< RNTupleProcessor > > fInnerProcessors
Processor specialization for horizontally combined (joined) RNTupleProcessors.
void PrintStructureImpl(std::ostream &output) const final
Processor-specific implementation for printing its structure, called by PrintStructure().
void SetEntryMapping(ROOT::NTupleSize_t globalEntryNumber, REntryMapping &mapping) final
Set the local entry mapping (for nested processors), according to a global entry number.
void AddEntriesToJoinTable(Internal::RNTupleJoinTable &joinTable, ROOT::NTupleSize_t entryOffset=0) final
Add the entry mappings for this processor to the provided join table.
void Disconnect() final
Disconnect the processor from associated physical storage.
ROOT::NTupleSize_t GetNEntries() final
Get the total number of entries in this processor.
void SetAuxiliaryFieldValidity(bool validity)
Set the validity for all fields in the auxiliary processor at once.
void Connect(const std::unordered_set< Internal::RNTupleProcessorEntry::FieldIndex_t > &fieldIdxs, const Internal::RNTupleProcessorProvenance &provenance=Internal::RNTupleProcessorProvenance(), bool updateFields=false) final
Connect the provided fields indices in the entry to their on-disk fields.
void AddAllFieldsToEntry(const Internal::RNTupleProcessorProvenance &provenance, bool includeSubfields) final
Add all known fields (from on-disk information) to the entry.
std::unique_ptr< RNTupleProcessor > fPrimaryProcessor
void LoadEntryImpl(const REntryMapping &mapping) final
Load the entry identified by the entry mapping.
void Initialize(std::shared_ptr< Internal::RNTupleProcessorEntry > entry=nullptr) final
Initialize the processor by creating an (initially empty) fEntry, or setting an existing one.
Internal::RNTupleProcessorEntry::FieldIndex_t AddFieldToEntry(const std::string &fieldName, const std::string &typeName, void *valuePtr=nullptr, const Internal::RNTupleProcessorProvenance &provenance=Internal::RNTupleProcessorProvenance()) final
Add a field to the entry.
Specification of the name and location of an RNTuple, used for creating a new RNTupleProcessor.
std::variant< std::string, TDirectory * > fStorage
std::unique_ptr< ROOT::Internal::RPageSource > CreatePageSource() const
Maps a processor's global (outer) entry index to its inner ones.
void SetEntryNumber(ROOT::NTupleSize_t entryNumber)
Interface for iterating over entries of vertically ("chained") and/or horizontally ("joined") combine...
static std::unique_ptr< RNTupleProcessor > CreateChain(std::vector< RNTupleOpenSpec > ntuples, const RNTupleProcessorOptions &opts=RNTupleProcessorOptions())
Create an RNTupleProcessor for a chain (i.e., a vertical combination) of RNTuples.
virtual void AddAllFieldsToEntry(const Internal::RNTupleProcessorProvenance &provenance, bool includeSubfields)=0
Add all known fields (from on-disk information) to the entry.
std::shared_ptr< Internal::RNTupleProcessorEntry > fEntry
static std::unique_ptr< RNTupleProcessor > Create(RNTupleOpenSpec ntuple, const RNTupleProcessorOptions &opts=RNTupleProcessorOptions())
Create an RNTupleProcessor for a single RNTuple.
Processor specialization for processing a single RNTuple.
void AddEntriesToJoinTable(Internal::RNTupleJoinTable &joinTable, ROOT::NTupleSize_t entryOffset=0) final
Add the entry mappings for this processor to the provided join table.
std::unique_ptr< ROOT::RFieldBase > ConnectField(std::unique_ptr< ROOT::RFieldBase > field)
Connect an existing field to the processor's page source.
void Connect(const std::unordered_set< Internal::RNTupleProcessorEntry::FieldIndex_t > &fieldIdxs, const Internal::RNTupleProcessorProvenance &provenance=Internal::RNTupleProcessorProvenance(), bool updateFields=false) final
Connect the provided fields indices in the entry to their on-disk fields.
void Initialize(std::shared_ptr< Internal::RNTupleProcessorEntry > entry=nullptr) final
Initialize the processor by creating an (initially empty) fEntry, or setting an existing one.
void PrintStructureImpl(std::ostream &output) const final
Processor-specific implementation for printing its structure, called by PrintStructure().
void Disconnect() final
Disconnect the processor from associated physical storage.
void LoadEntryImpl(const REntryMapping &mapping) final
Load the entry identified by the entry mapping.
bool CanReadFieldFromDisk(std::string_view fieldName) final
Check if a field exists on-disk and can be read by the processor.
void SetEntryMapping(ROOT::NTupleSize_t globalEntryNumber, REntryMapping &mapping) final
Set the local entry mapping (for nested processors), according to a global entry number.
Internal::RNTupleProcessorEntry::FieldIndex_t AddFieldToEntry(const std::string &fieldName, const std::string &typeName, void *valuePtr=nullptr, const Internal::RNTupleProcessorProvenance &provenance=Internal::RNTupleProcessorProvenance()) final
Add a field to the entry.
void AddAllFieldsToEntry(const Internal::RNTupleProcessorProvenance &provenance, bool includeSubfields) final
Add all known fields (from on-disk information) to the entry.
std::unique_ptr< ROOT::RFieldBase > CreateAndConnectField(const std::string &qualifiedFieldName, const std::string &typeName)
Create a new field and connect it to the processor's page source.
static std::unique_ptr< RPageSourceFile > CreateFromAnchor(const RNTuple &anchor, const ROOT::RNTupleReadOptions &options=ROOT::RNTupleReadOptions())
Used from the RNTuple class to build a datasource if the anchor is already available.
static std::unique_ptr< RPageSource > Create(std::string_view ntupleName, std::string_view location, const ROOT::RNTupleReadOptions &options=ROOT::RNTupleReadOptions())
Guess the concrete derived page source from the file name (location)
Base class for all ROOT issued exceptions.
Definition RError.hxx:78
static RResult< std::unique_ptr< RFieldBase > > Create(const std::string &fieldName, const std::string &typeName, const ROOT::RCreateFieldOptions &options, const ROOT::RNTupleDescriptor *desc, ROOT::DescriptorId_t fieldId)
Factory method to resurrect a field from the stored on-disk type information.
Metadata stored for every field of an RNTuple.
The container field for an ntuple model, which itself has no physical representation.
Definition RField.hxx:58
std::vector< std::unique_ptr< RFieldBase > > ReleaseSubfields()
Moves all subfields into the returned vector.
Definition RField.cxx:64
void Attach(std::unique_ptr< RFieldBase > child)
A public version of the Attach method that allows piece-wise construction of the zero field.
Definition RField.cxx:40
Representation of an RNTuple data set in a ROOT file.
Definition RNTuple.hxx:67
const RNTupleProcessorEntry * LoadFullRNTupleProcessorEntry(ROOT::Experimental::RNTupleProcessor &processor, bool includeSubfields)
std::unique_ptr< RNTupleProcessor > CreateRNTupleJoinProcessor(RNTupleOpenSpec primaryNTuple, RNTupleOpenSpec auxNTuple, const std::vector< std::string > &joinFields, const RNTupleProcessorOptions &opts=RNTupleProcessorOptions())
Create an RNTupleProcessor for a join (i.e., a horizontal combination) of RNTuples.
void SetAllowFieldSubstitutions(RFieldZero &fieldZero, bool val)
Definition RField.cxx:35
void CallConnectPageSourceOnField(RFieldBase &, ROOT::Internal::RPageSource &)
constexpr NTupleSize_t kInvalidNTupleIndex
std::vector< std::string > Split(std::string_view str, std::string_view delims, bool skipEmpty=false)
Splits a string at each character in delims.
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
constexpr DescriptorId_t kInvalidDescriptorId