Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RNTupleModel.cxx
Go to the documentation of this file.
1/// \file RNTupleModel.cxx
2/// \ingroup NTuple ROOT7
3/// \author Jakob Blomer <jblomer@cern.ch>
4/// \date 2018-10-15
5/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
6/// is welcome!
7
8/*************************************************************************
9 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
10 * All rights reserved. *
11 * *
12 * For the licensing terms see $ROOTSYS/LICENSE. *
13 * For the list of contributors see $ROOTSYS/README/CREDITS. *
14 *************************************************************************/
15
16#include <ROOT/RError.hxx>
17#include <ROOT/RField.hxx>
19#include <ROOT/RNTupleModel.hxx>
21#include <ROOT/StringUtils.hxx>
22
23#include <atomic>
24#include <cstdlib>
25#include <memory>
26#include <utility>
27
28namespace {
29std::uint64_t GetNewModelId()
30{
31 static std::atomic<std::uint64_t> gLastModelId = 0;
32 return ++gLastModelId;
33}
34} // anonymous namespace
35
38 const FieldMap_t &fieldMap)
39{
40 auto source = fieldMap.at(target);
41 const bool hasCompatibleStructure =
42 (source->GetStructure() == target->GetStructure()) ||
43 ((source->GetStructure() == ENTupleStructure::kCollection) && dynamic_cast<const RCardinalityField *>(target));
44 if (!hasCompatibleStructure)
45 return R__FAIL("field mapping structural mismatch: " + source->GetFieldName() + " --> " + target->GetFieldName());
46 if (source->GetStructure() == ENTupleStructure::kLeaf) {
47 if (target->GetTypeName() != source->GetTypeName())
48 return R__FAIL("field mapping type mismatch: " + source->GetFieldName() + " --> " + target->GetFieldName());
49 }
50
51 // We support projections only across records and collections. In the following, we check that the projected
52 // field is on the same path of collection fields in the field tree than the source field.
53
54 // Finds the first non-record parent field of the input field
55 auto fnBreakPoint = [](const RFieldBase *f) -> const RFieldBase * {
56 auto parent = f->GetParent();
57 while (parent) {
58 if (parent->GetStructure() != ENTupleStructure::kRecord)
59 return parent;
60 parent = parent->GetParent();
61 }
62 // We reached the zero field
63 return nullptr;
64 };
65
66 // If source or target has a variant or reference as a parent, error out
67 auto *sourceBreakPoint = fnBreakPoint(source);
68 if (sourceBreakPoint && sourceBreakPoint->GetStructure() != ENTupleStructure::kCollection)
69 return R__FAIL("unsupported field mapping (source structure)");
70 auto *targetBreakPoint = fnBreakPoint(target);
71 if (targetBreakPoint && sourceBreakPoint->GetStructure() != ENTupleStructure::kCollection)
72 return R__FAIL("unsupported field mapping (target structure)");
73
74 if (!sourceBreakPoint && !targetBreakPoint) {
75 // Source and target have no collections as parent
77 }
78 if (sourceBreakPoint && targetBreakPoint) {
79 if (sourceBreakPoint == targetBreakPoint) {
80 // Source and target are children of the same collection
82 }
83 if (auto it = fieldMap.find(targetBreakPoint); it != fieldMap.end() && it->second == sourceBreakPoint) {
84 // The parent collection of parent is mapped to the parent collection of the source
86 }
87 // Source and target are children of different collections
88 return R__FAIL("field mapping structure mismatch: " + source->GetFieldName() + " --> " + target->GetFieldName());
89 }
90
91 // Either source or target have no collection as a parent, but the other one has; that doesn't fit
92 return R__FAIL("field mapping structure mismatch: " + source->GetFieldName() + " --> " + target->GetFieldName());
93}
94
96ROOT::Experimental::RNTupleModel::RProjectedFields::Add(std::unique_ptr<RFieldBase> field, const FieldMap_t &fieldMap)
97{
98 auto result = EnsureValidMapping(field.get(), fieldMap);
99 if (!result)
100 return R__FORWARD_ERROR(result);
101 for (const auto &f : *field) {
102 result = EnsureValidMapping(&f, fieldMap);
103 if (!result)
104 return R__FORWARD_ERROR(result);
105 }
106
107 fFieldMap.insert(fieldMap.begin(), fieldMap.end());
108 fFieldZero->Attach(std::move(field));
109 return RResult<void>::Success();
110}
111
114{
115 if (auto it = fFieldMap.find(target); it != fFieldMap.end())
116 return it->second;
117 return nullptr;
118}
119
120std::unique_ptr<ROOT::Experimental::RNTupleModel::RProjectedFields>
122{
123 auto cloneFieldZero = std::unique_ptr<RFieldZero>(static_cast<RFieldZero *>(fFieldZero->Clone("").release()));
124 auto clone = std::unique_ptr<RProjectedFields>(new RProjectedFields(std::move(cloneFieldZero)));
125 clone->fModel = newModel;
126 // TODO(jblomer): improve quadratic search to re-wire the field mappings given the new model and the cloned
127 // projected fields. Not too critical as we generally expect a limited number of projected fields
128 for (const auto &[k, v] : fFieldMap) {
129 for (const auto &f : *clone->GetFieldZero()) {
130 if (f.GetQualifiedFieldName() == k->GetQualifiedFieldName()) {
131 clone->fFieldMap[&f] = clone->fModel->FindField(v->GetQualifiedFieldName());
132 break;
133 }
134 }
135 }
136 return clone;
137}
138
140 : fWriter(writer), fOpenChangeset(fWriter.GetUpdatableModel())
141{
142}
143
145{
146 fOpenChangeset.fModel.Unfreeze();
147 // We set the model ID to zero until CommitUpdate(). That prevents calls to RNTupleWriter::Fill() in the middle
148 // of updates
149 std::swap(fOpenChangeset.fModel.fModelId, fNewModelId);
150}
151
153{
154 fOpenChangeset.fModel.Freeze();
155 std::swap(fOpenChangeset.fModel.fModelId, fNewModelId);
156 if (fOpenChangeset.IsEmpty())
157 return;
158 Internal::RNTupleModelChangeset toCommit{fOpenChangeset.fModel};
159 std::swap(fOpenChangeset.fAddedFields, toCommit.fAddedFields);
160 std::swap(fOpenChangeset.fAddedProjectedFields, toCommit.fAddedProjectedFields);
161 fWriter.GetSink().UpdateSchema(toCommit, fWriter.GetNEntries());
162}
163
164void ROOT::Experimental::RNTupleModel::RUpdater::AddField(std::unique_ptr<RFieldBase> field)
165{
166 auto fieldp = field.get();
167 fOpenChangeset.fModel.AddField(std::move(field));
168 fOpenChangeset.fAddedFields.emplace_back(fieldp);
169}
170
173 std::function<std::string(const std::string &)> mapping)
174{
175 auto fieldp = field.get();
176 auto result = fOpenChangeset.fModel.AddProjectedField(std::move(field), mapping);
177 if (result)
178 fOpenChangeset.fAddedProjectedFields.emplace_back(fieldp);
180}
181
183{
184 RResult<void> nameValid = RFieldBase::EnsureValidFieldName(fieldName);
185 if (!nameValid) {
186 nameValid.Throw();
187 }
188 auto fieldNameStr = std::string(fieldName);
189 if (fFieldNames.count(fieldNameStr) > 0)
190 throw RException(R__FAIL("field name '" + fieldNameStr + "' already exists in NTuple model"));
191}
192
194{
195 if (IsFrozen())
196 throw RException(R__FAIL("invalid attempt to modify frozen model"));
197}
198
200{
201 if (!fDefaultEntry)
202 throw RException(R__FAIL("invalid attempt to use default entry of bare model"));
203}
204
205ROOT::Experimental::RNTupleModel::RNTupleModel(std::unique_ptr<RFieldZero> fieldZero)
206 : fFieldZero(std::move(fieldZero)), fModelId(GetNewModelId())
207{}
208
209std::unique_ptr<ROOT::Experimental::RNTupleModel> ROOT::Experimental::RNTupleModel::CreateBare()
210{
211 return CreateBare(std::make_unique<RFieldZero>());
212}
213
214std::unique_ptr<ROOT::Experimental::RNTupleModel>
215ROOT::Experimental::RNTupleModel::CreateBare(std::unique_ptr<RFieldZero> fieldZero)
216{
217 auto model = std::unique_ptr<RNTupleModel>(new RNTupleModel(std::move(fieldZero)));
218 model->fProjectedFields = std::make_unique<RProjectedFields>(model.get());
219 return model;
220}
221
222std::unique_ptr<ROOT::Experimental::RNTupleModel> ROOT::Experimental::RNTupleModel::Create()
223{
224 return Create(std::make_unique<RFieldZero>());
225}
226
227std::unique_ptr<ROOT::Experimental::RNTupleModel>
228ROOT::Experimental::RNTupleModel::Create(std::unique_ptr<RFieldZero> fieldZero)
229{
230 auto model = CreateBare(std::move(fieldZero));
231 model->fDefaultEntry = std::unique_ptr<REntry>(new REntry(model->fModelId));
232 return model;
233}
234
235std::unique_ptr<ROOT::Experimental::RNTupleModel> ROOT::Experimental::RNTupleModel::Clone() const
236{
237 auto cloneModel = std::unique_ptr<RNTupleModel>(
238 new RNTupleModel(std::unique_ptr<RFieldZero>(static_cast<RFieldZero *>(fFieldZero->Clone("").release()))));
239 cloneModel->fModelId = GetNewModelId();
240 cloneModel->fIsFrozen = fIsFrozen;
241 cloneModel->fFieldNames = fFieldNames;
242 cloneModel->fDescription = fDescription;
243 cloneModel->fProjectedFields = fProjectedFields->Clone(cloneModel.get());
244 if (fDefaultEntry) {
245 cloneModel->fDefaultEntry = std::unique_ptr<REntry>(new REntry(cloneModel->fModelId));
246 for (const auto &f : cloneModel->fFieldZero->GetSubFields()) {
247 cloneModel->fDefaultEntry->AddValue(f->CreateValue());
248 }
249 }
250 return cloneModel;
251}
252
254{
255 if (fieldName.empty())
256 return nullptr;
257
258 auto *field = static_cast<ROOT::Experimental::RFieldBase *>(fFieldZero.get());
259 for (auto subfieldName : ROOT::Split(fieldName, ".")) {
260 const auto subfields = field->GetSubFields();
261 auto it = std::find_if(subfields.begin(), subfields.end(),
262 [&](const auto *f) { return f->GetFieldName() == subfieldName; });
263 if (it != subfields.end()) {
264 field = *it;
265 } else {
266 field = nullptr;
267 break;
268 }
269 }
270
271 return field;
272}
273
274void ROOT::Experimental::RNTupleModel::AddField(std::unique_ptr<RFieldBase> field)
275{
276 EnsureNotFrozen();
277 if (!field)
278 throw RException(R__FAIL("null field"));
279 EnsureValidFieldName(field->GetFieldName());
280
281 if (fDefaultEntry)
282 fDefaultEntry->AddValue(field->CreateValue());
283 fFieldNames.insert(field->GetFieldName());
284 fFieldZero->Attach(std::move(field));
285}
286
289 std::function<std::string(const std::string &)> mapping)
290{
291 EnsureNotFrozen();
292 if (!field)
293 return R__FAIL("null field");
294 auto fieldName = field->GetFieldName();
295
297 auto sourceField = FindField(mapping(fieldName));
298 if (!sourceField)
299 return R__FAIL("no such field: " + mapping(fieldName));
300 fieldMap[field.get()] = sourceField;
301 for (const auto &subField : *field) {
302 sourceField = FindField(mapping(subField.GetQualifiedFieldName()));
303 if (!sourceField)
304 return R__FAIL("no such field: " + mapping(fieldName));
305 fieldMap[&subField] = sourceField;
306 }
307
308 EnsureValidFieldName(fieldName);
309 auto result = fProjectedFields->Add(std::move(field), fieldMap);
310 if (!result) {
311 return R__FORWARD_ERROR(result);
312 }
313 fFieldNames.insert(fieldName);
314 return RResult<void>::Success();
315}
316
317std::shared_ptr<ROOT::Experimental::RNTupleCollectionWriter>
319 std::unique_ptr<RNTupleModel> collectionModel)
320{
321 EnsureNotFrozen();
322 EnsureValidFieldName(fieldName);
323 if (!collectionModel) {
324 throw RException(R__FAIL("null collectionModel"));
325 }
326
327 auto collectionWriter = std::make_shared<RNTupleCollectionWriter>(std::move(collectionModel->fDefaultEntry));
328
329 auto field = std::make_unique<RCollectionField>(fieldName, collectionWriter, std::move(collectionModel->fFieldZero));
330 field->SetDescription(collectionModel->GetDescription());
331
332 if (fDefaultEntry)
333 fDefaultEntry->AddValue(field->BindValue(std::shared_ptr<void>(collectionWriter->GetOffsetPtr(), [](void *) {})));
334
335 fFieldNames.insert(field->GetFieldName());
336 fFieldZero->Attach(std::move(field));
337 return collectionWriter;
338}
339
341{
342 if (!IsFrozen())
343 throw RException(R__FAIL("invalid attempt to get mutable zero field of unfrozen model"));
344 return *fFieldZero;
345}
346
348{
349 auto f = FindField(fieldName);
350 if (!f)
351 throw RException(R__FAIL("invalid field: " + std::string(fieldName)));
352
353 return *f;
354}
355
357{
358 EnsureNotBare();
359 return *fDefaultEntry;
360}
361
363{
364 if (!IsFrozen())
365 throw RException(R__FAIL("invalid attempt to get default entry of unfrozen model"));
366 EnsureNotBare();
367 return *fDefaultEntry;
368}
369
370std::unique_ptr<ROOT::Experimental::REntry> ROOT::Experimental::RNTupleModel::CreateEntry() const
371{
372 if (!IsFrozen())
373 throw RException(R__FAIL("invalid attempt to create entry of unfrozen model"));
374
375 auto entry = std::unique_ptr<REntry>(new REntry(fModelId));
376 for (const auto &f : fFieldZero->GetSubFields()) {
377 entry->AddValue(f->CreateValue());
378 }
379 return entry;
380}
381
382std::unique_ptr<ROOT::Experimental::REntry> ROOT::Experimental::RNTupleModel::CreateBareEntry() const
383{
384 if (!IsFrozen())
385 throw RException(R__FAIL("invalid attempt to create entry of unfrozen model"));
386
387 auto entry = std::unique_ptr<REntry>(new REntry(fModelId));
388 for (const auto &f : fFieldZero->GetSubFields()) {
389 entry->AddValue(f->BindValue(nullptr));
390 }
391 return entry;
392}
393
395{
396 if (!IsFrozen())
397 throw RException(R__FAIL("invalid attempt to get field token of unfrozen model"));
398
399 const auto &topLevelFields = fFieldZero->GetSubFields();
400 auto it = std::find_if(topLevelFields.begin(), topLevelFields.end(),
401 [&fieldName](const RFieldBase *f) { return f->GetFieldName() == fieldName; });
402
403 if (it == topLevelFields.end()) {
404 throw RException(R__FAIL("invalid field name: " + std::string(fieldName)));
405 }
406 return REntry::RFieldToken(std::distance(topLevelFields.begin(), it), fModelId);
407}
408
410{
411 if (!IsFrozen())
412 throw RException(R__FAIL("invalid attempt to create bulk of unfrozen model"));
413
414 auto f = FindField(fieldName);
415 if (!f)
416 throw RException(R__FAIL("no such field: " + std::string(fieldName)));
417 return f->CreateBulk();
418}
419
421{
422 if (!IsFrozen())
423 return;
424
425 fModelId = GetNewModelId();
426 if (fDefaultEntry)
427 fDefaultEntry->fModelId = fModelId;
428 fIsFrozen = false;
429}
430
432{
433 fIsFrozen = true;
434}
435
436void ROOT::Experimental::RNTupleModel::SetDescription(std::string_view description)
437{
438 EnsureNotFrozen();
439 fDescription = std::string(description);
440}
#define R__FORWARD_ERROR(res)
Short-hand to return an RResult<T> in an error state (i.e. after checking)
Definition RError.hxx:294
#define R__FORWARD_RESULT(res)
Short-hand to return an RResult<T> value from a subroutine to the calling stack frame.
Definition RError.hxx:292
#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:290
#define f(i)
Definition RSha256.hxx:104
TObject * clone(const char *newname) const override
Definition RooChi2Var.h:9
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 target
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 result
An artificial field that transforms an RNTuple column that contains the offset of collections into co...
Definition RField.hxx:1715
The field token identifies a top-level field in this entry.
Definition REntry.hxx:54
The REntry is a collection of values in an ntuple corresponding to a complete row in the data set.
Definition REntry.hxx:45
Base class for all ROOT issued exceptions.
Definition RError.hxx:78
Similar to RValue but manages an array of consecutive values.
Definition RField.hxx:231
A field translates read and write calls from/to underlying columns to/from tree values.
Definition RField.hxx:95
const RFieldBase * GetParent() const
Definition RField.hxx:673
std::vector< RFieldBase * > GetSubFields()
Definition RField.cxx:876
static RResult< void > EnsureValidFieldName(std::string_view fieldName)
Check whether a given string is a valid field name.
Definition RField.cxx:774
The container field for an ntuple model, which itself has no physical representation.
Definition RField.hxx:717
Projected fields are fields whose columns are reused from existing fields.
RResult< void > EnsureValidMapping(const RFieldBase *target, const FieldMap_t &fieldMap)
Asserts that the passed field is a valid target of the source field provided in the field map.
std::unordered_map< const RFieldBase *, const RFieldBase * > FieldMap_t
The map keys are the projected target fields, the map values are the backing source fields Note that ...
const RFieldBase * GetSourceField(const RFieldBase *target) const
RResult< void > Add(std::unique_ptr< RFieldBase > field, const FieldMap_t &fieldMap)
Adds a new projected field.
std::unique_ptr< RProjectedFields > Clone(const RNTupleModel *newModel) const
The new model needs to be a clone of fModel.
void CommitUpdate()
Commit changes since the last call to BeginUpdate().
void BeginUpdate()
Begin a new set of alterations to the underlying model.
RResult< void > AddProjectedField(std::unique_ptr< RFieldBase > field, std::function< std::string(const std::string &)> mapping)
void AddField(std::unique_ptr< RFieldBase > field)
The RNTupleModel encapulates the schema of an ntuple.
std::unordered_set< std::string > fFieldNames
Keeps track of which field names are taken, including projected field names.
void EnsureValidFieldName(std::string_view fieldName)
Checks that user-provided field names are valid in the context of this NTuple model.
std::uint64_t fModelId
Every model has a unique ID to distinguish it from other models.
REntry::RFieldToken GetToken(std::string_view fieldName) const
Creates a token to be used in REntry methods to address a top-level field.
RResult< void > AddProjectedField(std::unique_ptr< RFieldBase > field, std::function< std::string(const std::string &)> mapping)
Adds a top-level field based on existing fields.
void EnsureNotBare() const
Throws an RException if fDefaultEntry is nullptr.
std::unique_ptr< RNTupleModel > Clone() const
void EnsureNotFrozen() const
Throws an RException if fFrozen is true.
std::shared_ptr< RNTupleCollectionWriter > MakeCollection(std::string_view fieldName, std::unique_ptr< RNTupleModel > collectionModel)
Ingests a model for a sub collection and attaches it to the current model.
const RFieldBase & GetField(std::string_view fieldName) const
std::unique_ptr< REntry > CreateBareEntry() const
In a bare entry, all values point to nullptr.
std::unique_ptr< REntry > CreateEntry() const
RFieldBase::RBulk CreateBulk(std::string_view fieldName) const
Calls the given field's CreateBulk() method. Throws an exception if no field with the given name exis...
static std::unique_ptr< RNTupleModel > Create()
void SetDescription(std::string_view description)
std::unique_ptr< REntry > fDefaultEntry
Contains field values corresponding to the created top-level fields.
RFieldBase * FindField(std::string_view fieldName) const
The field name can be a top-level field or a nested field. Returns nullptr if the field is not in the...
RNTupleModel(std::unique_ptr< RFieldZero > fieldZero)
static std::unique_ptr< RNTupleModel > CreateBare()
A bare model has no default entry.
void AddField(std::unique_ptr< RFieldBase > field)
Adds a field whose type is not known at compile time.
RFieldZero & GetFieldZero()
Non-const access to the root field is used to commit clusters during writing and to set the on-disk f...
std::unique_ptr< RFieldZero > fFieldZero
Hierarchy of fields consisting of simple types and collections (sub trees)
An RNTuple that gets filled with entries (data) and writes them to storage.
void Throw()
Throws an RException with fError.
Definition RError.cxx:67
The class is used as a return type for operations that can fail; wraps a value of type T or an RError...
Definition RError.hxx:194
std::vector< std::string > Split(std::string_view str, std::string_view delims, bool skipEmpty=false)
Splits a string at each character in delims.
The incremental changes to a RNTupleModel