Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion tree/dataframe/src/RNTupleDS.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -197,15 +197,20 @@ class RNTupleColumnReader : public ROOT::Detail::RDF::RColumnReaderBase {
}
}

RFieldZero fieldZero;
ROOT::Internal::SetAllowFieldSubstitutions(fieldZero, true);
fieldZero.Attach(std::move(fField));
try {
ROOT::Internal::CallConnectPageSourceOnField(*fField, source);
ROOT::Internal::CallConnectPageSourceOnField(fieldZero, source);
} catch (const ROOT::RException &err) {
fField = std::move(fieldZero.ReleaseSubfields()[0]);
auto onDiskType = source.GetSharedDescriptorGuard()->GetFieldDescriptor(fField->GetOnDiskId()).GetTypeName();
std::string msg = "RNTupleDS: invalid type \"" + fField->GetTypeName() + "\" for column \"" +
fDataSource->fFieldId2QualifiedName[fField->GetOnDiskId()] + "\" with on-disk type \"" +
onDiskType + "\"";
throw std::runtime_error(msg);
}
fField = std::move(fieldZero.ReleaseSubfields()[0]);

if (fValuePtr) {
// When the reader reconnects to a new file, the fValuePtr is already set
Expand Down
25 changes: 23 additions & 2 deletions tree/ntuple/inc/ROOT/RField.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,26 @@ namespace Detail {
class RFieldVisitor;
} // namespace Detail

class RFieldZero;
namespace Internal {
void SetAllowFieldSubstitutions(RFieldZero &fieldZero, bool val);
}

/// The container field for an ntuple model, which itself has no physical representation.
/// Therefore, the zero field must not be connected to a page source or sink.
class RFieldZero final : public RFieldBase {
friend void ROOT::Internal::SetAllowFieldSubstitutions(RFieldZero &, bool);

/// If field substitutions are allowed, upon connecting to a page source the field hierarchy will replace created
/// fields by fields that match the on-disk schema. This happens for
/// - Vector fields (RVectorField, RRVecField) that connect to an on-disk fixed-size array
/// - Streamer fields that connect to an on-disk class field
/// Field substitutions must not be enabled when the field hierarchy already handed out RValue objects because
/// they would leave dangling field pointers to the replaced fields. This is used in cases when the field/model
/// is created by RNTuple (not imposed), before it is made available to the user.
/// This flag is reset on Clone().
bool fAllowFieldSubstitutions = false;

protected:
std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
void ConstructValue(void *) const final {}
Expand All @@ -64,6 +81,10 @@ public:
size_t GetAlignment() const final { return 0; }

void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;

bool GetAllowFieldSubstitutions() const { return fAllowFieldSubstitutions; }
/// Moves all subfields into the returned vector.
std::vector<std::unique_ptr<RFieldBase>> ReleaseSubfields();
};

/// Used in RFieldBase::Check() to record field creation failures.
Expand Down Expand Up @@ -187,7 +208,7 @@ protected:
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final;
void ReadInClusterImpl(RNTupleLocalIndex localIndex, void *to) final;

void BeforeConnectPageSource(ROOT::Internal::RPageSource &pageSource) final;
std::unique_ptr<RFieldBase> BeforeConnectPageSource(ROOT::Internal::RPageSource &pageSource) final;
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final;

public:
Expand Down Expand Up @@ -242,7 +263,7 @@ protected:
// Returns the list of seen streamer infos
ROOT::RExtraTypeInfoDescriptor GetExtraTypeInfo() const final;

void BeforeConnectPageSource(ROOT::Internal::RPageSource &source) final;
std::unique_ptr<RFieldBase> BeforeConnectPageSource(ROOT::Internal::RPageSource &source) final;
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final;

public:
Expand Down
8 changes: 8 additions & 0 deletions tree/ntuple/inc/ROOT/RField/RFieldSequenceContainer.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ public:

/// The type-erased field for a RVec<Type>
class RRVecField : public RFieldBase {
friend class RArrayAsRVecField; // to call ResizeRVec()

// Ensures that the RVec pointed to by rvec has at least nItems valid elements
// Returns the possibly new "begin pointer" of the RVec, i.e. the pointer to the data area.
static unsigned char *
ResizeRVec(void *rvec, std::size_t nItems, std::size_t itemSize, const RFieldBase *itemField, RDeleter *itemDeleter);

public:
/// the RRVecDeleter is also used by RArrayAsRVecField and therefore declared public
class RRVecDeleter : public RDeleter {
Expand Down Expand Up @@ -147,6 +154,7 @@ protected:
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final;
std::size_t ReadBulkImpl(const RBulkSpec &bulkSpec) final;

std::unique_ptr<RFieldBase> BeforeConnectPageSource(ROOT::Internal::RPageSource &pageSource) final;
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final;

void CommitClusterImpl() final { fNWritten = 0; }
Expand Down
9 changes: 8 additions & 1 deletion tree/ntuple/inc/ROOT/RFieldBase.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ This is and can only be partially enforced through C++.
*/
// clang-format on
class RFieldBase {
friend class RFieldZero; // to reset fParent pointer in ReleaseSubfields()
friend class ROOT::Experimental::Detail::RRawPtrWriteEntry; // to call Append()
friend struct ROOT::Internal::RFieldCallbackInjector; // used for unit tests
friend struct ROOT::Internal::RFieldRepresentationModifier; // used for unit tests
Expand Down Expand Up @@ -513,7 +514,13 @@ protected:
/// Called by ConnectPageSource() before connecting; derived classes may override this as appropriate, e.g.
/// for the application of I/O rules. In the process, the field at hand or its subfields may be marked as
/// "artifical", i.e. introduced by schema evolution and not backed by on-disk information.
virtual void BeforeConnectPageSource(ROOT::Internal::RPageSource & /* source */) {}
/// May return a field substitute that fits the on-disk schema as a replacement for the field at hand.
/// A field substitute must read into the same in-memory layout than the original field and field substitutions
/// must not be cyclic.
virtual std::unique_ptr<RFieldBase> BeforeConnectPageSource(ROOT::Internal::RPageSource & /* source */)
{
return nullptr;
}

/// For non-artificial fields, check compatibility of the in-memory field and the on-disk field. In the process,
/// the field at hand may change its on-disk ID or perform other tasks related to automatic schema evolution.
Expand Down
2 changes: 1 addition & 1 deletion tree/ntuple/inc/ROOT/RNTupleReader.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ private:
/// The model is generated from the RNTuple metadata on storage.
explicit RNTupleReader(std::unique_ptr<Internal::RPageSource> source, const ROOT::RNTupleReadOptions &options);

void ConnectModel(ROOT::RNTupleModel &model);
void ConnectModel(ROOT::RNTupleModel &model, bool allowFieldSubstitutions);
RNTupleReader *GetDisplayReader();
void InitPageSource(bool enableMetrics);

Expand Down
7 changes: 5 additions & 2 deletions tree/ntuple/inc/ROOT/RNTupleView.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ protected:
static std::unique_ptr<ROOT::RFieldBase>
CreateField(ROOT::DescriptorId_t fieldId, Internal::RPageSource &pageSource, std::string_view typeName = "")
{
RFieldZero fieldZero;
Internal::SetAllowFieldSubstitutions(fieldZero, true);
std::unique_ptr<ROOT::RFieldBase> field;
{
const auto &desc = pageSource.GetSharedDescriptorGuard().GetRef();
Expand All @@ -103,8 +105,9 @@ protected:
}
}
field->SetOnDiskId(fieldId);
ROOT::Internal::CallConnectPageSourceOnField(*field, pageSource);
return field;
fieldZero.Attach(std::move(field));
ROOT::Internal::CallConnectPageSourceOnField(fieldZero, pageSource);
return std::move(fieldZero.ReleaseSubfields()[0]);
}

RNTupleViewBase(std::unique_ptr<ROOT::RFieldBase> field, ROOT::RNTupleGlobalRange range)
Expand Down
14 changes: 14 additions & 0 deletions tree/ntuple/src/RField.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@
#include <type_traits>
#include <unordered_set>

void ROOT::Internal::SetAllowFieldSubstitutions(RFieldZero &fieldZero, bool val)
{
fieldZero.fAllowFieldSubstitutions = val;
}

std::unique_ptr<ROOT::RFieldBase> ROOT::RFieldZero::CloneImpl(std::string_view /*newName*/) const
{
auto result = std::make_unique<RFieldZero>();
Expand All @@ -39,6 +44,15 @@ std::unique_ptr<ROOT::RFieldBase> ROOT::RFieldZero::CloneImpl(std::string_view /
return result;
}

std::vector<std::unique_ptr<ROOT::RFieldBase>> ROOT::RFieldZero::ReleaseSubfields()
{
std::vector<std::unique_ptr<ROOT::RFieldBase>> result;
std::swap(fSubfields, result);
for (auto &f : result)
f->fParent = nullptr;
return result;
}

void ROOT::RFieldZero::AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const
{
visitor.VisitFieldZero(*this);
Expand Down
29 changes: 26 additions & 3 deletions tree/ntuple/src/RFieldBase.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -945,8 +945,12 @@ void ROOT::RFieldBase::ConnectPageSink(ROOT::Internal::RPageSink &pageSink, ROOT

void ROOT::RFieldBase::ConnectPageSource(ROOT::Internal::RPageSource &pageSource)
{
if (dynamic_cast<ROOT::RFieldZero *>(this))
throw RException(R__FAIL("invalid attempt to connect zero field to page source"));
if (dynamic_cast<ROOT::RFieldZero *>(this)) {
for (auto &f : fSubfields)
f->ConnectPageSource(pageSource);
return;
}

if (fState != EState::kUnconnected)
throw RException(R__FAIL("invalid attempt to connect an already connected field to a page source"));

Expand All @@ -955,7 +959,26 @@ void ROOT::RFieldBase::ConnectPageSource(ROOT::Internal::RPageSource &pageSource
if (!fDescription.empty())
throw RException(R__FAIL("setting description only valid when connecting to a page sink"));

BeforeConnectPageSource(pageSource);
auto substitute = BeforeConnectPageSource(pageSource);
if (substitute) {
const RFieldBase *itr = this;
while (itr->GetParent()) {
itr = itr->GetParent();
}
if (typeid(*itr) == typeid(RFieldZero) && static_cast<const RFieldZero *>(itr)->GetAllowFieldSubstitutions()) {
for (auto &f : fParent->fSubfields) {
if (f.get() != this)
continue;

f = std::move(substitute);
f->ConnectPageSource(pageSource);
return;
}
R__ASSERT(false); // never here
} else {
throw RException(R__FAIL("invalid attempt to substitute field " + GetQualifiedFieldName()));
}
}

if (!fIsArtificial) {
R__ASSERT(fOnDiskId != kInvalidDescriptorId);
Expand Down
27 changes: 18 additions & 9 deletions tree/ntuple/src/RFieldMeta.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -393,17 +393,20 @@ void ROOT::RClassField::PrepareStagingArea(const std::vector<const TSchemaRule *
throw RException(R__FAIL(std::string("cannot find on disk rule source member ") + GetTypeName() + "." +
source->GetName()));
}
const auto &memberFieldDesc = desc.GetFieldDescriptor(memberFieldId);

auto memberType = source->GetTypeForDeclaration() + source->GetDimensions();
stagingItem.fField = Create("" /* we don't need a field name */, std::string(memberType)).Unwrap();
stagingItem.fField->SetOnDiskId(memberFieldDesc.GetId());
auto memberField = Create("" /* we don't need a field name */, std::string(memberType)).Unwrap();
memberField->SetOnDiskId(memberFieldId);
auto fieldZero = std::make_unique<RFieldZero>();
Internal::SetAllowFieldSubstitutions(*fieldZero, true);
fieldZero->Attach(std::move(memberField));
stagingItem.fField = std::move(fieldZero);

stagingItem.fOffset = fStagingClass->GetDataMemberOffset(source->GetName());
// Since we successfully looked up the source member in the RNTuple on-disk metadata, we expect it
// to be present in the TClass instance, too.
R__ASSERT(stagingItem.fOffset != TVirtualStreamerInfo::kMissing);
stagingAreaSize = std::max(stagingAreaSize, stagingItem.fOffset + stagingItem.fField->GetValueSize());
stagingAreaSize = std::max(stagingAreaSize, stagingItem.fOffset + stagingItem.fField->begin()->GetValueSize());
}
}

Expand All @@ -413,8 +416,9 @@ void ROOT::RClassField::PrepareStagingArea(const std::vector<const TSchemaRule *
fStagingArea = std::make_unique<unsigned char[]>(stagingAreaSize);

for (const auto &[_, si] : fStagingItems) {
if (!(si.fField->GetTraits() & kTraitTriviallyConstructible)) {
CallConstructValueOn(*si.fField, fStagingArea.get() + si.fOffset);
const auto &memberField = *si.fField->cbegin();
if (!(memberField.GetTraits() & kTraitTriviallyConstructible)) {
CallConstructValueOn(memberField, fStagingArea.get() + si.fOffset);
}
}
}
Expand All @@ -436,7 +440,7 @@ void ROOT::RClassField::AddReadCallbacksFromIORule(const TSchemaRule *rule)
});
}

void ROOT::RClassField::BeforeConnectPageSource(ROOT::Internal::RPageSource &pageSource)
std::unique_ptr<ROOT::RFieldBase> ROOT::RClassField::BeforeConnectPageSource(ROOT::Internal::RPageSource &pageSource)
{
std::vector<const TSchemaRule *> rules;
// On-disk members that are not targeted by an I/O rule; all other sub fields of the in-memory class
Expand Down Expand Up @@ -476,8 +480,10 @@ void ROOT::RClassField::BeforeConnectPageSource(ROOT::Internal::RPageSource &pag
if (!rules.empty()) {
SetStagingClass(fieldDesc.GetTypeName(), fieldDesc.GetTypeVersion());
PrepareStagingArea(rules, desc, fieldDesc);
for (auto &[_, si] : fStagingItems)
for (auto &[_, si] : fStagingItems) {
Internal::CallConnectPageSourceOnField(*si.fField, pageSource);
si.fField = std::move(static_cast<RFieldZero *>(si.fField.get())->ReleaseSubfields()[0]);
}

// Remove target member of read rules from the list of regular members of the underlying on-disk field
for (const auto rule : rules) {
Expand All @@ -501,6 +507,8 @@ void ROOT::RClassField::BeforeConnectPageSource(ROOT::Internal::RPageSource &pag
CallSetArtificialOn(*field);
}
}

return nullptr;
}

void ROOT::RClassField::ReconcileOnDiskField(const RNTupleDescriptor &desc)
Expand Down Expand Up @@ -973,9 +981,10 @@ void ROOT::RStreamerField::GenerateColumns(const ROOT::RNTupleDescriptor &desc)
GenerateColumnsImpl<ROOT::Internal::RColumnIndex, std::byte>(desc);
}

void ROOT::RStreamerField::BeforeConnectPageSource(ROOT::Internal::RPageSource &source)
std::unique_ptr<ROOT::RFieldBase> ROOT::RStreamerField::BeforeConnectPageSource(ROOT::Internal::RPageSource &source)
{
source.RegisterStreamerInfos();
return nullptr;
}

void ROOT::RStreamerField::ReconcileOnDiskField(const RNTupleDescriptor &desc)
Expand Down
Loading
Loading