37#include <initializer_list>
38#include <unordered_map>
64 const Ssiz_t wordLen = strlen(word);
65 if (str.Length() < wordLen)
69 return str.Length() == wordLen || str(wordLen) ==
' ';
74 std::initializer_list<std::pair<const char *, T>> validValues)
76 const Ssiz_t patternLen = strlen(pattern);
77 assert(pattern[patternLen - 1] ==
'=');
79 idx >= 0 && opts.
Length() > idx + patternLen) {
80 auto sub =
TString(opts(idx + patternLen, opts.
Length() - idx - patternLen));
81 for (
const auto &[
name,
value] : validValues) {
92 return ParseStringOption<ENTupleMergingMode>(opts,
"rntuple.MergingMode=",
94 {
"Filter", ENTupleMergingMode::kFilter},
95 {
"Union", ENTupleMergingMode::kUnion},
96 {
"Strict", ENTupleMergingMode::kStrict},
102 return ParseStringOption<ENTupleMergeErrBehavior>(opts,
"rntuple.ErrBehavior=",
104 {
"Abort", ENTupleMergeErrBehavior::kAbort},
105 {
"Skip", ENTupleMergeErrBehavior::kSkip},
111 return ParseStringOption<ENTupleMergeVersionBehavior>(
112 opts,
"rntuple.VersionBehavior=",
114 {
"WarnOnHigherVersion", ENTupleMergeVersionBehavior::kWarnOnHigherVersion},
115 {
"AbortOnHigherVersion", ENTupleMergeVersionBehavior::kAbortOnHigherVersion},
125 if (!inputs || inputs->
GetEntries() < 3 || !mergeInfo) {
134 std::string ntupleName = std::string(itr()->GetName());
138 TFile *outFile =
dynamic_cast<TFile *
>(secondArg);
146 TKey *outKey = outFile->
FindKey(ntupleName.c_str());
158 const bool defaultComp = mergeInfo->
fOptions.
Contains(
"DefaultCompression");
159 const bool firstSrcComp = mergeInfo->
fOptions.
Contains(
"FirstSrcCompression");
160 const bool extraVerbose = mergeInfo->
fOptions.
Contains(
"rntuple.ExtraVerbose");
161 if (defaultComp && firstSrcComp) {
164 "only the latter will apply.";
166 std::optional<std::uint32_t> compression;
170 }
else if (!defaultComp) {
180 std::vector<std::unique_ptr<RPageSourceFile>> sources;
181 std::vector<RPageSource *> sourcePtrs;
183 while (
const auto &pitr = itr()) {
196 source->Attach(RNTupleSerializer::EDescriptorDeserializeMode::kRaw);
198 auto descGuard = source->FindClusterId(0, firstClusterId);
201 <<
"Asked to use the first source's compression as the output compression, but the "
202 "first source (file '"
204 <<
"') has an empty RNTuple, therefore the output compression could not be "
208 const auto &firstCluster = descGuard->GetClusterDescriptor(firstClusterId);
209 auto colRangeIter = firstCluster.GetColumnRangeIterable();
210 auto firstColRange = colRangeIter.begin();
211 if (firstColRange == colRangeIter.end()) {
213 <<
"Asked to use the first source's compression as the output compression, but the "
214 "first source (file '"
216 <<
"') has an empty RNTuple, therefore the output compression could not be "
220 compression = (*firstColRange).GetCompressionSettings();
223 sources.push_back(std::move(source));
229 auto destination = std::make_unique<ROOT::Internal::RPageSinkFile>(ntupleName, *outFile, writeOpts);
230 std::unique_ptr<ROOT::RNTupleModel> model;
234 outSource->Attach(RNTupleSerializer::EDescriptorDeserializeMode::kForWriting);
235 outSource->LoadAllPageLists();
236 auto desc = outSource->GetSharedDescriptorGuard();
237 model = destination->InitFromDescriptor(desc.GetConstRef(),
true );
241 sourcePtrs.reserve(sources.size());
242 for (
const auto &s : sources) {
243 sourcePtrs.push_back(s.get());
247 RNTupleMerger merger{std::move(destination), std::move(model)};
260 merger.Merge(sourcePtrs, mergerOpts).ThrowOnError();
267}
catch (
const std::exception &
ex) {
274struct RChangeCompressionFunc {
276 std::uint32_t fCompressionSettings;
280 std::size_t fBufSize;
283 void operator()()
const
289 std::unique_ptr<std::byte[]> unzipBufOwned;
291 if (fCompressionSettings != 0) {
292 unzipBufOwned = MakeUninitArray<std::byte>(bytesPacked);
293 unzipBuf = unzipBufOwned.get();
301 std::size_t nBytesZipped;
302 if (fCompressionSettings != 0) {
304 assert(fBufSize >= bytesPacked + checksumSize);
307 nBytesZipped = bytesPacked;
315 std::optional<ROOT::Experimental::TTaskGroup> &fGroup;
317 template <
typename T>
318 void operator()(T &&
f)
340struct RColReprMapping {
341 std::uint32_t fSource;
347struct RColReprExtension : RColReprMapping {
349 std::vector<ROOT::Internal::RColumnFormat> fSourceRepr;
353 std::uint32_t fOrigFirstElementIndex = 0;
356static std::optional<std::uint32_t>
357FindColumnReprMapping(
const std::vector<RColReprMapping> &mappings, std::uint32_t sourceReprIndex)
359 for (
const auto [
src,
dst] : mappings)
360 if (
src == sourceReprIndex)
365struct RColumnOutInfo {
369struct RColumnMergeInfo {
373 std::string fColumnName;
378 std::uint16_t fOutputReprIndex = 0;
380 std::optional<std::type_index> fInMemoryType;
386using ColumnIdMap_t = std::unordered_map<std::string, RColumnOutInfo>;
388struct RColumnInfoGroup {
389 std::vector<RColumnMergeInfo> fExtraDstColumns;
390 std::vector<RColumnMergeInfo> fCommonColumns;
393struct RSealedPageMergeData {
396 std::deque<RPageStorage::SealedPageSequence_t> fPagesV;
397 std::vector<RPageStorage::RSealedPageGroup> fGroups;
398 std::vector<std::unique_ptr<std::byte[]>> fBuffers;
402using FieldCollectionMap_t = std::unordered_map<const ROOT::RFieldDescriptor *, std::vector<T>>;
404struct RDescriptorsComparison {
405 std::vector<const ROOT::RFieldDescriptor *> fExtraDstFields;
406 std::vector<const ROOT::RFieldDescriptor *> fExtraSrcFields;
407 std::vector<RCommonField> fCommonFields;
411 FieldCollectionMap_t<RColReprMapping> fColReprMappings;
412 FieldCollectionMap_t<RColReprExtension> fColReprExtensions;
421 RDescriptorsComparison &
result, std::vector<std::string> &errors)
429 if (srcColCardinality != dstColCardinality) {
430 std::stringstream ss;
432 <<
"` has a different column cardinality than previously-seen field with the same name (old: "
433 << dstColCardinality <<
", new: " << srcColCardinality <<
")";
434 errors.push_back(ss.str());
438 if (srcColCardinality == 0)
441 const auto srcNColReprs = srcColumns.size() / srcColCardinality;
442 const auto dstNColReprs = dstColumns.size() / dstColCardinality;
443 std::uint32_t nextDstReprIndex = dstNColReprs;
449 for (
auto srcReprIdx = 0u; srcReprIdx < srcNColReprs; ++srcReprIdx) {
450 std::int64_t matchingRepr = -1;
451 for (
auto dstReprIdx = 0u; dstReprIdx < dstNColReprs; ++dstReprIdx) {
453 for (
auto reprColIdx = 0u; reprColIdx < srcColCardinality; ++reprColIdx) {
454 const auto srcColId = srcColumns[srcReprIdx * srcColCardinality + reprColIdx];
456 const auto dstColId = dstColumns[dstReprIdx * dstColCardinality + reprColIdx];
458 if (srcCol.GetType() != dstCol.GetType()) {
470 for (
auto reprColIdx = 0u; reprColIdx < srcColCardinality; ++reprColIdx) {
471 const auto srcColId = srcColumns[srcReprIdx * srcColCardinality + reprColIdx];
473 const auto dstColId = dstColumns[dstReprIdx * dstColCardinality + reprColIdx];
475 if (srcCol.GetType() != dstCol.GetType() || srcCol.GetBitsOnStorage() != dstCol.GetBitsOnStorage() ||
476 srcCol.GetValueRange() != dstCol.GetValueRange()) {
484 matchingRepr = dstReprIdx;
490 if (errors.empty()) {
491 if (matchingRepr >= 0 && matchingRepr != srcReprIdx) {
493 assert(matchingRepr < std::numeric_limits<std::uint32_t>::max());
494 result.fColReprMappings[&dstField].push_back(
495 RColReprMapping{srcReprIdx,
static_cast<std::uint32_t
>(matchingRepr)});
496 }
else if (matchingRepr < 0) {
498 std::vector<ROOT::Internal::RColumnFormat> newRepr;
499 newRepr.reserve(srcColCardinality);
500 std::uint32_t firstElemIdx = 0;
501 for (
auto reprColIdx = 0u; reprColIdx < srcColCardinality; ++reprColIdx) {
502 const auto srcColId = srcColumns[srcReprIdx * srcColCardinality + reprColIdx];
505 assert(firstElemIdx == 0 || firstElemIdx == srcCol.GetFirstElementIndex());
506 firstElemIdx = srcCol.GetFirstElementIndex();
507 auto &reprElement = newRepr.emplace_back();
508 reprElement.fType = srcCol.GetType();
509 reprElement.fBitWidth = srcCol.GetBitsOnStorage();
510 reprElement.fValueRange = srcCol.GetValueRange();
512 RColReprExtension extension{{srcReprIdx, nextDstReprIndex}, newRepr, firstElemIdx};
513 nextDstReprIndex += newRepr.size();
514 result.fColReprExtensions[&dstField].push_back(extension);
515 result.fColReprMappings[&dstField].push_back(extension);
521static RDescriptorsComparison
524 RDescriptorsComparison res;
526 for (
const auto &dstField :
dst.GetTopLevelFields()) {
527 const auto srcFieldId =
src.FindFieldId(dstField.GetFieldName());
529 const auto &srcField =
src.GetFieldDescriptor(srcFieldId);
530 res.fCommonFields.push_back({srcField, dstField});
532 res.fExtraDstFields.emplace_back(&dstField);
535 for (
const auto &srcField :
src.GetTopLevelFields()) {
536 const auto dstFieldId =
dst.FindFieldId(srcField.GetFieldName());
538 res.fExtraSrcFields.push_back(&srcField);
557 std::vector<std::string> errors;
560 auto fieldsToCheck = res.fCommonFields;
562 for (std::size_t fieldIdx = 0; fieldIdx < fieldsToCheck.size(); ++fieldIdx) {
563 const auto &field = fieldsToCheck[fieldIdx];
566 const auto &fieldName = field.fSrc->GetFieldName();
569 bool projCompatible = field.fSrc->IsProjectedField() == field.fDst->IsProjectedField();
570 if (!projCompatible) {
571 std::stringstream ss;
572 ss <<
"Field `" << fieldName <<
"` is incompatible with previously-seen field with that name because the "
573 << (field.fSrc->IsProjectedField() ?
"new" :
"old") <<
" one is projected and the other isn't";
574 errors.push_back(ss.str());
575 }
else if (field.fSrc->IsProjectedField()) {
577 const auto srcName =
src.GetQualifiedFieldName(field.fSrc->GetProjectionSourceId());
578 const auto dstName =
dst.GetQualifiedFieldName(field.fDst->GetProjectionSourceId());
579 if (srcName != dstName) {
580 std::stringstream ss;
581 ss <<
"Field `" << fieldName
582 <<
"` is projected to a different field than a previously-seen field with the same name (old: "
583 << dstName <<
", new: " << srcName <<
")";
584 errors.push_back(ss.str());
592 const auto &dstTyName = field.fDst->GetTypeName();
593 if (srcTyName != dstTyName) {
594 std::stringstream ss;
595 ss <<
"Field `" << fieldName
596 <<
"` has a type incompatible with a previously-seen field with the same name: (old: " << dstTyName
597 <<
", new: " << srcTyName <<
")";
598 errors.push_back(ss.str());
602 const auto srcTyChk = field.fSrc->GetTypeChecksum();
603 const auto dstTyChk = field.fDst->GetTypeChecksum();
604 if (srcTyChk && dstTyChk && *srcTyChk != *dstTyChk) {
605 std::stringstream ss;
606 ss <<
"Field `" << field.fSrc->GetFieldName()
607 <<
"` has a different type checksum than previously-seen field with the same name";
608 errors.push_back(ss.str());
612 const auto srcTyVer = field.fSrc->GetTypeVersion();
613 const auto dstTyVer = field.fDst->GetTypeVersion();
614 if (srcTyVer != dstTyVer) {
615 std::stringstream ss;
616 ss <<
"Field `" << field.fSrc->GetFieldName()
617 <<
"` has a different type version than previously-seen field with the same name (old: " << dstTyVer
618 <<
", new: " << srcTyVer <<
")";
619 errors.push_back(ss.str());
623 const auto srcFldVer = field.fSrc->GetFieldVersion();
624 const auto dstFldVer = field.fDst->GetFieldVersion();
625 if (srcFldVer != dstFldVer) {
626 std::stringstream ss;
627 ss <<
"Field `" << field.fSrc->GetFieldName()
628 <<
"` has a different field version than previously-seen field with the same name (old: " << dstFldVer
629 <<
", new: " << srcFldVer <<
")";
630 errors.push_back(ss.str());
633 const auto srcRole = field.fSrc->GetStructure();
634 const auto dstRole = field.fDst->GetStructure();
635 if (srcRole != dstRole) {
636 std::stringstream ss;
637 ss <<
"Field `" << field.fSrc->GetFieldName()
638 <<
"` has a different structural role than previously-seen field with the same name (old: " << dstRole
639 <<
", new: " << srcRole <<
")";
640 errors.push_back(ss.str());
644 if (!field.fSrc->IsProjectedField()) {
649 const auto &srcLinks = field.fSrc->GetLinkIds();
650 const auto &dstLinks = field.fDst->GetLinkIds();
651 if (srcLinks.size() != dstLinks.size()) {
652 std::stringstream ss;
653 ss <<
"Field `" << field.fSrc->GetFieldName()
654 <<
"` has a different number of children than previously-seen field with the same name (old: "
655 << dstLinks.size() <<
", new: " << srcLinks.size() <<
")";
656 errors.push_back(ss.str());
658 for (std::size_t linkIdx = 0, linkNum = srcLinks.size(); linkIdx < linkNum; ++linkIdx) {
659 const auto &srcSubfield =
src.GetFieldDescriptor(srcLinks[linkIdx]);
660 const auto &dstSubfield =
dst.GetFieldDescriptor(dstLinks[linkIdx]);
661 fieldsToCheck.push_back(RCommonField{srcSubfield, dstSubfield});
667 for (
const auto &err : errors)
668 errMsg += std::string(
"\n * ") + err;
671 errMsg = errMsg.substr(1);
690 if (logicalIds.size() == 1) {
692 const auto type = column.GetType();
696 if (!realFloat && !realDouble) {
698 "` has a Real32Trunc/Real32Quant column but is not an RRealField");
700 const auto bits = column.GetBitsOnStorage();
704 realFloat->SetTruncated(bits);
708 const auto valueRange = column.GetValueRange();
711 "` has a Real32Quant column with no value range");
714 realFloat->SetQuantized(bits,
715 {
static_cast<float>(valueRange->fMin),
static_cast<float>(valueRange->fMax)});
717 realDouble->
SetQuantized(bits, {valueRange->fMin, valueRange->fMax});
744 const auto &newFields = descCmp.fExtraSrcFields;
745 auto &commonFields = descCmp.fCommonFields;
751 std::string msg =
"destination doesn't contain field";
752 if (newFields.size() > 1)
755 msg += std::accumulate(newFields.begin(), newFields.end(), std::string{}, [](
const auto &acc,
const auto *field) {
756 return acc + (acc.length() ?
", " :
"") +
'`' + field->GetFieldName() +
'`';
759 <<
" to the destination model (entry #" << destination.
GetNEntries() <<
").";
762 changeset.fAddedFields.reserve(newFields.size());
764 for (
const auto *fieldDesc : newFields) {
765 if (fieldDesc->IsProjectedField())
768 auto field = fieldDesc->CreateField(srcDesc);
772 for (
const auto &colId : fieldDesc->GetLogicalColumnIds()) {
774 representatives.push_back(column.GetType());
776 field->SetColumnRepresentatives({representatives});
782 changeset.AddField(std::move(field));
785 for (
const auto *fieldDesc : newFields) {
786 if (!fieldDesc->IsProjectedField())
790 auto field = fieldDesc->CreateField(srcDesc);
791 const auto sourceId = fieldDesc->GetProjectionSourceId();
793 fieldMap[field.get()] = &sourceField;
795 for (
const auto &subfield : *field) {
799 fieldMap[&subfield] = &subSourceField;
801 changeset.fAddedProjectedFields.emplace_back(field.get());
816 commonFields.reserve(commonFields.size() + newFields.size());
845 for (
auto it = newFields.rbegin(); it != newFields.rend(); ++it) {
846 const auto *field = *it;
847 const auto newFieldInDstId = dstDesc.
FindFieldId(field->GetFieldName());
848 const auto &newFieldInDst = dstDesc.GetFieldDescriptor(newFieldInDstId);
849 commonFields.insert(commonFields.begin(), RCommonField{*field, newFieldInDst});
865 if (fieldType ==
"bool")
return typeid(
bool);
866 if (fieldType ==
"std::byte")
return typeid(std::byte);
867 if (fieldType ==
"char")
return typeid(char);
868 if (fieldType ==
"std::int8_t")
return typeid(std::int8_t);
869 if (fieldType ==
"std::uint8_t")
return typeid(std::uint8_t);
870 if (fieldType ==
"std::int16_t")
return typeid(std::int16_t);
871 if (fieldType ==
"std::uint16_t")
return typeid(std::uint16_t);
872 if (fieldType ==
"std::int32_t")
return typeid(std::int32_t);
873 if (fieldType ==
"std::uint32_t")
return typeid(std::uint32_t);
874 if (fieldType ==
"std::int64_t")
return typeid(std::int64_t);
875 if (fieldType ==
"std::uint64_t")
return typeid(std::uint64_t);
876 if (fieldType ==
"float")
return typeid(float);
877 if (fieldType ==
"double")
return typeid(
double);
885 ColumnIdMap_t &colIdMap,
const std::string &prefix =
"")
890 RColumnOutInfo info{};
893 name +
'.' + std::to_string(colDesc.GetIndex()) +
'.' + std::to_string(colDesc.GetRepresentationIndex());
894 colIdMap[colName] = info;
897 for (
const auto &subId : fieldDesc.
GetLinkIds()) {
905 const FieldCollectionMap_t<RColReprExtension> &extensions,
907 std::unordered_map<
ROOT::DescriptorId_t, std::vector<const ROOT::RFieldDescriptor *>> &outProjectionPointees)
909 const auto it = extensions.find(&field);
910 if (it != extensions.end())
911 outExtensions.emplace_back(it->first, it->second);
923class RNTupleMergeStrategy {
926 std::unique_ptr<ROOT::RNTupleModel> &fDstModel;
937 : fDstModel(dstModel), fDestination(
dst), fDstDescriptor(
dst.GetDescriptor()), fMergeOpts(mergeOpts)
941 virtual ~RNTupleMergeStrategy() =
default;
947 virtual void BeforeCommit() = 0;
950class RNTupleMergeStrategyDefault final :
public RNTupleMergeStrategy {
951 std::unique_ptr<ROOT::Internal::RPageAllocator> fPageAlloc = std::make_unique<ROOT::Internal::RPageAllocatorHeap>();
952 std::optional<ROOT::Experimental::TTaskGroup> fTaskGroup;
953 std::unordered_map<ROOT::DescriptorId_t, std::vector<const ROOT::RFieldDescriptor *>> fProjectionPointees;
955 std::vector<RColumnMergeInfo> fColumns;
957 ColumnIdMap_t fColumnIdMap;
962 RSealedPageMergeData &sealedPageData);
964 ROOT::RResult<void> GenerateZeroPagesForColumns(
size_t nEntriesToGenerate, std::span<const RColumnMergeInfo> columns,
965 RSealedPageMergeData &sealedPageData);
968 const FieldCollectionMap_t<RColReprMapping> &colReprMappings,
970 const std::string &prefix =
"");
972 RColumnInfoGroup GatherColumnInfos(
const RDescriptorsComparison &descCmp,
const ROOT::RNTupleDescriptor &srcDesc);
975 std::span<const RColumnMergeInfo> extraDstColumns);
980 : RNTupleMergeStrategy(dstModel,
dst, mergeOpts)
990 for (
const auto &field :
dst.GetDescriptor().GetTopLevelFields()) {
998 source.Attach(RNTupleSerializer::EDescriptorDeserializeMode::kForWriting);
999 source.LoadAllPageLists();
1000 fSrcDescriptor = &source.GetSharedDescriptorGuard().GetConstRef();
1003 constexpr bool copyClusters =
false;
1004 fDstModel = fDestination.InitFromDescriptor(*fSrcDescriptor, copyClusters);
1020 void BeforeCommit() final {}
1034void RNTupleMergeStrategyDefault::AddColumnsFromField(std::vector<RColumnMergeInfo> &columns,
1036 const FieldCollectionMap_t<RColReprMapping> &colReprMappings,
1039 const std::string &prefix)
1049 columns.reserve(columns.size() + columnIds.size());
1055 RColumnMergeInfo info{};
1064 info.fParentFieldDescriptor = &srcFieldDesc;
1068 info.fParentNTupleDescriptor = &srcDesc;
1070 const auto mappingsIt = colReprMappings.find(&dstFieldDesc);
1071 std::uint16_t reprIndex = srcColumn.GetRepresentationIndex();
1072 if (mappingsIt != colReprMappings.end()) {
1073 if (
auto outReprIdx = FindColumnReprMapping(mappingsIt->second, reprIndex); outReprIdx)
1074 reprIndex = *outReprIdx;
1077 info.fColumnName =
name +
'.' + std::to_string(srcColumn.GetIndex()) +
'.' + std::to_string(reprIndex);
1081 if (
auto it = fColumnIdMap.find(info.fColumnName); it != fColumnIdMap.end()) {
1083 info.fOutputId = it->second.fColumnId;
1084 info.fOutputReprIndex = reprIndex;
1087 info.fOutputId = fColumnIdMap.size();
1092 const auto dstColumnIndex = reprIndex * dstFieldDesc.
GetColumnCardinality() + srcColumn.GetIndex();
1094 const auto &dstColumn = fDstDescriptor.GetColumnDescriptor(dstColumnId);
1095 columnType = dstColumn.GetType();
1096 info.fOutputReprIndex = reprIndex;
1097 fColumnIdMap[info.fColumnName] = RColumnOutInfo{info.fOutputId};
1100 if (fMergeOpts.fExtraVerbose) {
1102 <<
", phys.id " << srcColumn.GetPhysicalId() <<
", type "
1104 << info.fOutputId <<
", type "
1111 columns.emplace_back(info);
1114 const auto &srcChildrenIds = srcFieldDesc.
GetLinkIds();
1115 const auto &dstChildrenIds = dstFieldDesc.
GetLinkIds();
1116 assert(srcChildrenIds.size() == dstChildrenIds.size());
1117 for (
auto i = 0u; i < srcChildrenIds.size(); ++i) {
1119 const auto &dstChild = fDstDescriptor.GetFieldDescriptor(dstChildrenIds[i]);
1120 AddColumnsFromField(columns, srcDesc, colReprMappings, srcChild, dstChild,
name);
1127RColumnInfoGroup RNTupleMergeStrategyDefault::GatherColumnInfos(
const RDescriptorsComparison &descCmp,
1130 RColumnInfoGroup res;
1132 AddColumnsFromField(res.fExtraDstColumns, fDstDescriptor, descCmp.fColReprMappings, *field, *field);
1134 for (
const auto &[srcField, dstField] : descCmp.fCommonFields) {
1135 AddColumnsFromField(res.fCommonColumns, srcDesc, descCmp.fColReprMappings, *srcField, *dstField);
1141ROOT::RResult<void> RNTupleMergeStrategyDefault::GenerateZeroPagesForColumns(
size_t nEntriesToGenerate,
1142 std::span<const RColumnMergeInfo> columns,
1143 RSealedPageMergeData &sealedPageData)
1145 if (!nEntriesToGenerate)
1148 for (
const auto &column : columns) {
1158 bool skipColumn =
false;
1159 auto nRepetitions = std::max<std::uint64_t>(field->
GetNRepetitions(), 1);
1176 return R__FAIL(
"Destination RNTuple contains a streamer field (" + field->
GetFieldName() +
1177 ") that is not present in one of the sources. "
1178 "Creating a default value for a streamer field is ill-defined, therefore the merging "
1179 "process will abort.");
1186 const auto &columnDesc = fDstDescriptor.GetColumnDescriptor(column.fOutputId);
1188 const auto nElements = nEntriesToGenerate * nRepetitions;
1189 const auto nBytesOnStorage = colElement->GetPackedSize(nElements);
1191 constexpr auto kPageSizeLimit = 256 * 1024;
1193 const size_t nPages = nBytesOnStorage / kPageSizeLimit + !!(nBytesOnStorage % kPageSizeLimit);
1194 for (
size_t i = 0; i < nPages; ++i) {
1195 const auto pageSize = (i < nPages - 1) ? kPageSizeLimit : nBytesOnStorage - kPageSizeLimit * (nPages - 1);
1197 const auto bufSize = pageSize + checksumSize;
1198 assert(pageSize % colElement->GetSize() == 0);
1199 const auto nElementsPerPage = pageSize / colElement->GetSize();
1200 auto page = fPageAlloc->NewPage(colElement->GetSize(), nElementsPerPage);
1201 page.GrowUnchecked(nElementsPerPage);
1202 memset(page.GetBuffer(), 0, page.GetNBytes());
1204 auto &buffer = sealedPageData.fBuffers.emplace_back(
new std::byte[bufSize]);
1206 sealConf.
fElement = colElement.get();
1207 sealConf.
fPage = &page;
1208 sealConf.
fBuffer = buffer.get();
1210 sealConf.
fWriteChecksum = fDestination.GetWriteOptions().GetEnablePageChecksums();
1213 sealedPageData.fPagesV.push_back({sealedPage});
1214 sealedPageData.fGroups.emplace_back(column.fOutputId, sealedPageData.fPagesV.back().cbegin(),
1215 sealedPageData.fPagesV.back().cend());
1225 std::span<RColumnMergeInfo> commonColumns,
1227 RSealedPageMergeData &sealedPageData)
1229 const auto nCommonColumnsInCluster = commonColumnSet.size();
1230 assert(nCommonColumnsInCluster <= commonColumns.size());
1232 if (nCommonColumnsInCluster == 0)
1240 const std::uint32_t outCompression = fMergeOpts.fCompressionSettings.value();
1242 for (
size_t colIdx = 0; colIdx < nCommonColumnsInCluster; ++colIdx) {
1243 const auto &column = commonColumns[colIdx];
1244 const auto &columnId = column.fInputId;
1247 const auto &columnDesc = fSrcDescriptor->GetColumnDescriptor(columnId);
1248 const auto srcColElement = column.fInMemoryType
1253 const auto &pages = clusterDesc.
GetPageRange(columnId);
1256 sealedPages.resize(pages.GetPageInfos().size());
1260 assert(!columnRange.IsSuppressed());
1261 const auto colRangeCompressionSettings = columnRange.GetCompressionSettings().value();
1268 const bool needsRecompressing = colRangeCompressionSettings != outCompression;
1270 if (needsRecompressing && fMergeOpts.fExtraVerbose) {
1272 <<
": { compression: " << colRangeCompressionSettings <<
" => "
1273 << fMergeOpts.fCompressionSettings.value() <<
", onDiskType: "
1275 srcColElement->GetIdentifier().fOnDiskType)
1279 const size_t pageBufferBaseIdx = sealedPageData.fBuffers.size();
1282 if (needsRecompressing)
1283 sealedPageData.fBuffers.resize(sealedPageData.fBuffers.size() + pages.GetPageInfos().size());
1290 if (columnDesc.GetFirstElementIndex() > clusterDesc.
GetFirstEntryIndex() && fDestination.GetNEntries() > 0) {
1291 const auto nMissingEntries = columnDesc.GetFirstElementIndex() - clusterDesc.
GetFirstEntryIndex();
1292 auto res = GenerateZeroPagesForColumns(nMissingEntries, {&column, 1}, sealedPageData);
1298 std::uint64_t pageIdx = 0;
1299 for (
const auto &pageInfo : pages.GetPageInfos()) {
1300 assert(pageIdx < sealedPages.size());
1301 assert(sealedPageData.fBuffers.size() == 0 || pageIdx < sealedPageData.fBuffers.size());
1311 sealedPage.
SetBufferSize(pageInfo.GetLocator().GetNBytesOnStorage() + checksumSize);
1312 sealedPage.
SetBuffer(onDiskPage->GetAddress());
1317 if (needsRecompressing) {
1318 const auto uncompressedSize = srcColElement->GetSize() * sealedPage.
GetNElements();
1319 auto &buffer = sealedPageData.fBuffers[pageBufferBaseIdx + pageIdx];
1320 const auto bufSize = uncompressedSize + checksumSize;
1324 buffer = MakeUninitArray<std::byte>(bufSize);
1327 RTaskVisitor{fTaskGroup}(RChangeCompressionFunc{
1334 fDestination.GetWriteOptions()
1346 sealedPageData.fPagesV.push_back(std::move(sealedPages));
1347 sealedPageData.fGroups.emplace_back(column.fOutputId, sealedPageData.fPagesV.back().cbegin(),
1348 sealedPageData.fPagesV.back().cend());
1360 std::span<RColumnMergeInfo> commonColumns,
1361 std::span<const RColumnMergeInfo> extraDstColumns)
1365 std::vector<RColumnMergeInfo> missingColumns{extraDstColumns.begin(), extraDstColumns.end()};
1367 R__ASSERT(fSrcDescriptor->GetNClusters() == fSrcDescriptor->GetNActiveClusters());
1368 for (
const auto &clusterDesc : fSrcDescriptor->GetActiveClusterIterable()) {
1369 const auto nClusterEntries = clusterDesc.
GetNEntries();
1376 FieldCollectionMap_t<ROOT::DescriptorId_t> columnsInCluster;
1390 int nCommonColumnsInCluster = 0;
1391 std::partition(commonColumns.begin(), commonColumns.end(), [&](
const auto &column) {
1392 if (clusterDesc.ContainsColumn(column.fInputId)) {
1393 const auto &colRange = clusterDesc.GetColumnRange(column.fInputId);
1394 ++nCommonColumnsInCluster;
1395 columnsInCluster[column.fParentFieldDescriptor].push_back(column.fOutputId);
1396 if (!colRange.IsSuppressed()) {
1397 commonColumnSet.emplace(column.fInputId);
1400 fDestination.CommitSuppressedColumn(ColumnHandle_t{column.fOutputId});
1415 for (
const auto &[fieldDesc, columnIds] : columnsInCluster) {
1416 const auto &fieldFQName = fSrcDescriptor->GetQualifiedFieldName(fieldDesc->GetId());
1417 const auto cardinality = fieldDesc->GetColumnCardinality();
1418 for (
auto i = 0u; i < fieldDesc->GetLogicalColumnIds().
size(); ++i) {
1419 const auto colIndex = i % cardinality;
1420 const auto reprIndex = i / cardinality;
1421 const auto colName =
"." + fieldFQName +
'.' + std::to_string(colIndex) +
'.' + std::to_string(reprIndex);
1422 const auto colIt = fColumnIdMap.find(colName);
1423 assert(colIt != fColumnIdMap.end());
1424 const auto colOutId = colIt->second.fColumnId;
1425 if (std::find(columnIds.begin(), columnIds.end(), colOutId) == columnIds.end()) {
1426 fDestination.CommitSuppressedColumn(ColumnHandle_t{colOutId});
1431 RSealedPageMergeData sealedPageData;
1432 auto res = MergeCommonColumns(clusterPool, clusterDesc, commonColumns, commonColumnSet, sealedPageData);
1440 missingColumns.resize(extraDstColumns.size());
1441 for (
size_t i = nCommonColumnsInCluster; i < commonColumns.size(); ++i)
1442 missingColumns.push_back(commonColumns[i]);
1444 res = GenerateZeroPagesForColumns(nClusterEntries, missingColumns, sealedPageData);
1449 fDestination.CommitSealedPageV(sealedPageData.fGroups);
1450 fDestination.CommitCluster(nClusterEntries);
1465 if (!descCmp.fColReprExtensions.empty()) {
1466 for (
const auto &field : descCmp.fExtraDstFields) {
1477 std::vector<std::pair<const ROOT::RFieldDescriptor *, std::vector<RColReprExtension>>> colExtensions;
1478 colExtensions.reserve(descCmp.fColReprExtensions.size());
1479 for (
const auto &commonField : descCmp.fCommonFields) {
1480 const auto *field = commonField.fDst;
1482 fProjectionPointees);
1484 for (
const auto &field : descCmp.fExtraSrcFields) {
1489 for (
const auto &[fieldDesc, extensions] : colExtensions) {
1490 auto &mappings = descCmp.fColReprMappings[fieldDesc];
1491 for (
const auto &extension : extensions) {
1492 const auto firstColumnId = fDestination.AddColumnRepresentation(*fieldDesc, extension.fSourceRepr,
1493 extension.fOrigFirstElementIndex);
1498 if (
auto it = fProjectionPointees.find(fieldDesc->GetId()); it != fProjectionPointees.end()) {
1499 for (
const auto &projection : it->second) {
1500 for (
auto colIdx = 0u; colIdx < extension.fSourceRepr.size(); ++colIdx)
1501 fDestination.AddAliasColumn(fDstDescriptor, *projection, firstColumnId + colIdx);
1504 mappings.push_back(extension);
1510 auto columnInfos = GatherColumnInfos(descCmp, *fSrcDescriptor);
1511 auto res = MergeSourceClusters(source, columnInfos.fCommonColumns, columnInfos.fExtraDstColumns);
1516 std::unique_ptr<ROOT::RNTupleModel> model)
1517 : fDestination(std::move(destination)), fModel(std::move(model))
1535 const auto dstCompSettings =
fDestination->GetWriteOptions().GetCompression();
1539 return R__FAIL(std::string(
"The compression given to RNTupleMergeOptions is different from that of the "
1541 std::to_string(*mergeOpts.
fCompressionSettings) +
", sink: " + std::to_string(dstCompSettings) +
1542 ") This is currently unsupported.");
1549 "passing an already-initialized destination to RNTupleMerger::Merge (i.e. trying to do incremental "
1550 "merging) can only be done by providing a valid ROOT::RNTupleModel when constructing the RNTupleMerger.");
1554#define SKIP_OR_ABORT(errMsg) \
1555 if (mergeOpts.fErrBehavior == ENTupleMergeErrBehavior::kSkip) { \
1556 R__LOG_WARNING(NTupleMergeLog()) << "Skipping RNTuple due to: " << (errMsg); \
1559 return R__FAIL(errMsg); \
1563 std::unique_ptr<RNTupleMergeStrategy>(
new RNTupleMergeStrategyDefault(
fModel, *
fDestination, mergeOpts));
1567 strat->InitSource(*source);
1574 <<
"RNTuple '" << srcDescriptor->
GetName()
1575 <<
"' has a higher format version than the latest supported by this version "
1576 "of ROOT. Merging will work but some features may be dropped.";
1578 return R__FAIL(
"RNTuple '" + srcDescriptor->GetName() +
1579 "' has a higher format version than the latest supported by this version. Refusing to "
1580 "merge, since RNTupleMergeOptions::fVersionBehavior is set to AbortOnHigherVersion.");
1584 for (
const auto &extraTypeInfoDesc : srcDescriptor->GetExtraTypeInfoIterable())
1588 auto descCmpRes = strat->CompareSrcAndDstDescriptors();
1590 SKIP_OR_ABORT(std::string(
"Source RNTuple has an incompatible schema with the destination:\n") +
1591 descCmpRes.GetError()->GetReport())
1593 auto descCmp = descCmpRes.Unwrap();
1598 std::string msg =
"Source RNTuple is missing the following fields:";
1599 for (
const auto *field : descCmp.fExtraDstFields) {
1606 if (!descCmp.fExtraSrcFields.empty()) {
1609 auto res = strat->ExtendDestinationModel(descCmp);
1614 std::string msg =
"Source RNTuple has extra fields that the destination RNTuple doesn't have:";
1615 for (
const auto *field : descCmp.fExtraSrcFields) {
1622 auto res = strat->MergeSource(*source, descCmp);
1628 strat->BeforeCommit();
#define R__FORWARD_ERROR(res)
Short-hand to return an RResult<T> in an error state (i.e. after checking)
#define R__FAIL(msg)
Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult...
#define R__LOG_WARNING(...)
#define R__LOG_ERROR(...)
static void MatchColumnRepresentations(const ROOT::RNTupleDescriptor &srcDesc, const ROOT::RNTupleDescriptor &dstDesc, const ROOT::RFieldDescriptor &srcField, const ROOT::RFieldDescriptor &dstField, RDescriptorsComparison &result, std::vector< std::string > &errors)
static std::optional< std::type_index > ColumnInMemoryType(std::string_view fieldType, ENTupleColumnType onDiskType)
static ROOT::RResult< RDescriptorsComparison > CompareDescriptorStructure(const ROOT::RNTupleDescriptor &dst, const ROOT::RNTupleDescriptor &src)
Compares the top level fields of dst and src and determines whether they can be merged or not.
static RDescriptorsComparison FillCommonAndExtraFields(const ROOT::RNTupleDescriptor &dst, const ROOT::RNTupleDescriptor &src)
static std::optional< ENTupleMergeErrBehavior > ParseOptionErrBehavior(const TString &opts)
static ROOT::RLogChannel & NTupleMergeLog()
static ROOT::RResult< void > ExtendDestinationModel(RDescriptorsComparison &descCmp, ROOT::RNTupleModel &dstModel, ROOT::Internal::RPageSink &destination, const ROOT::RNTupleDescriptor &srcDesc, bool verbose)
#define SKIP_OR_ABORT(errMsg)
static std::optional< T > ParseStringOption(const TString &opts, const char *pattern, std::initializer_list< std::pair< const char *, T > > validValues)
static void AddColumnExtensionsInFieldOrder(const ROOT::RFieldDescriptor &field, const ROOT::RNTupleDescriptor &desc, const FieldCollectionMap_t< RColReprExtension > &extensions, std::vector< std::pair< const ROOT::RFieldDescriptor *, std::vector< RColReprExtension > > > &outExtensions, std::unordered_map< ROOT::DescriptorId_t, std::vector< const ROOT::RFieldDescriptor * > > &outProjectionPointees)
static std::optional< ENTupleMergingMode > ParseOptionMergingMode(const TString &opts)
static void PrefillColumnMap(const ROOT::RNTupleDescriptor &desc, const ROOT::RFieldDescriptor &fieldDesc, ColumnIdMap_t &colIdMap, const std::string &prefix="")
static ROOT::RResult< void > RestoreRealFieldParams(ROOT::RFieldBase &field, const ROOT::RFieldDescriptor &fieldDesc, const ROOT::RNTupleDescriptor &srcDesc)
static std::optional< ENTupleMergeVersionBehavior > ParseOptionVersionBehavior(const TString &opts)
static bool BeginsWithDelimitedWord(const TString &str, const char *word)
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
long long Long64_t
Portable signed long integer 8 bytes.
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
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
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 child
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 src
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
The available trivial, native content types of a column.
Given a set of RPageSources merge them into an RPagePersistentSink, optionally changing their compres...
std::unique_ptr< ROOT::RNTupleModel > fModel
RNTupleMerger(std::unique_ptr< ROOT::Internal::RPagePersistentSink > destination, std::unique_ptr< ROOT::RNTupleModel > model)
Creates a RNTupleMerger with the given destination.
std::unique_ptr< ROOT::Internal::RPagePersistentSink > fDestination
RResult< void > Merge(std::span< ROOT::Internal::RPageSource * > sources, const RNTupleMergeOptions &mergeOpts=RNTupleMergeOptions())
Merge a given set of sources into the destination.
A class to manage the asynchronous execution of work items.
Managed a set of clusters containing compressed and packed pages.
RCluster * GetCluster(ROOT::DescriptorId_t clusterId, const RCluster::ColumnSet_t &physicalColumns)
Returns the requested cluster either from the pool or, in case of a cache miss, lets the I/O thread l...
An in-memory subset of the packed and compressed pages of a cluster.
std::unordered_set< ROOT::DescriptorId_t > ColumnSet_t
const ROnDiskPage * GetOnDiskPage(const ROnDiskPage::Key &key) const
A column element encapsulates the translation between basic C++ types and their column representation...
static const char * GetColumnTypeName(ROOT::ENTupleColumnType type)
static std::unique_ptr< RColumnElementBase > Generate(ROOT::ENTupleColumnType type)
If CppT == void, use the default C++ type for the given column type.
std::size_t GetPackedSize(std::size_t nElements=1U) const
The in-memory representation of a 32bit or 64bit on-disk index column.
Holds the index and the tag of a kSwitch column.
static std::size_t Zip(const void *from, std::size_t nbytes, int compression, void *to)
Returns the size of the compressed data, written into the provided output buffer.
static void Unzip(const void *from, size_t nbytes, size_t dataLen, void *to)
The nbytes parameter provides the size ls of the from buffer.
A helper class for serializing and deserialization of the RNTuple binary format.
Abstract interface to allocate and release pages.
Base class for a sink with a physical storage backend.
Abstract interface to write data into an ntuple.
virtual void UpdateSchema(const ROOT::Internal::RNTupleModelChangeset &changeset, ROOT::NTupleSize_t firstEntry)=0
Incorporate incremental changes to the model into the ntuple descriptor.
RSealedPage SealPage(const ROOT::Internal::RPage &page, const ROOT::Internal::RColumnElementBase &element)
Helper for streaming a page.
virtual const ROOT::RNTupleDescriptor & GetDescriptor() const =0
Return the RNTupleDescriptor being constructed.
virtual ROOT::NTupleSize_t GetNEntries() const =0
Storage provider that reads ntuple pages from a file.
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.
Abstract interface to read data from an ntuple.
RSharedDescriptorGuard GetSharedDescriptorGuard() const
Takes the read lock for the descriptor.
Common functionality of an ntuple storage for both reading and writing.
static constexpr std::size_t kNBytesPageChecksum
The page checksum is a 64bit xxhash3.
std::deque< RSealedPage > SealedPageSequence_t
RColumnHandle ColumnHandle_t
The column handle identifies a column with the current open page storage.
std::unordered_map< const ROOT::RFieldBase *, const ROOT::RFieldBase * > FieldMap_t
The map keys are the projected target fields, the map values are the backing source fields Note that ...
RResult< void > Add(std::unique_ptr< ROOT::RFieldBase > field, const FieldMap_t &fieldMap)
Adds a new projected field.
Metadata for RNTuple clusters.
ROOT::NTupleSize_t GetNEntries() const
ROOT::DescriptorId_t GetId() const
const RPageRange & GetPageRange(ROOT::DescriptorId_t physicalId) const
bool ContainsColumn(ROOT::DescriptorId_t physicalId) const
const RColumnRange & GetColumnRange(ROOT::DescriptorId_t physicalId) const
ROOT::NTupleSize_t GetFirstEntryIndex() const
ROOT::DescriptorId_t GetPhysicalId() const
ROOT::DescriptorId_t GetLogicalId() const
Base class for all ROOT issued exceptions.
A field translates read and write calls from/to underlying columns to/from tree values.
std::vector< RFieldBase * > GetMutableSubfields()
std::string GetQualifiedFieldName() const
Returns the field name and parent field names separated by dots (grandparent.parent....
std::vector< ROOT::ENTupleColumnType > ColumnRepresentation_t
Metadata stored for every field of an RNTuple.
const std::vector< ROOT::DescriptorId_t > & GetLogicalColumnIds() const
ROOT::ENTupleStructure GetStructure() const
std::uint32_t GetColumnCardinality() const
const std::vector< ROOT::DescriptorId_t > & GetLinkIds() const
ROOT::DescriptorId_t GetParentId() const
std::uint64_t GetNRepetitions() const
bool IsProjectedField() const
ROOT::DescriptorId_t GetProjectionSourceId() const
const std::string & GetFieldName() const
const std::string & GetTypeName() const
A log configuration for a channel, e.g.
The on-storage metadata of an RNTuple.
const RColumnDescriptor & GetColumnDescriptor(ROOT::DescriptorId_t columnId) const
const RFieldDescriptor & GetFieldDescriptor(ROOT::DescriptorId_t fieldId) const
const std::string & GetName() const
std::string GetTypeNameForComparison(const RFieldDescriptor &fieldDesc) const
Adjust the type name of the passed RFieldDescriptor for comparison with another renormalized type nam...
ROOT::DescriptorId_t FindFieldId(std::string_view fieldName, ROOT::DescriptorId_t parentId) const
ROOT::DescriptorId_t R__DEPRECATED(6, 46, "This function is ill-defined in the descriptor " "as not all cluster descriptors may be present. This interface is no longer exposed.") FindClusterId(ROOT ROOT::DescriptorId_t R__DEPRECATED(6, 46, "This function is ill-defined in the descriptor " "as not all cluster descriptors may be present. This interface is no longer exposed.") FindNextClusterId(ROOT ROOT::DescriptorId_t R__DEPRECATED(6, 46, "This function is ill-defined in the descriptor " "as not all cluster descriptors may be present. This interface is no longer exposed.") FindPrevClusterId(ROOT std::string GetQualifiedFieldName(ROOT::DescriptorId_t fieldId) const
Walks up the parents of the field ID and returns a field name of the form a.b.c.d In case of invalid ...
The RNTupleModel encapulates the schema of an RNTuple.
void Unfreeze()
Transitions an RNTupleModel from the frozen state back to the building state, invalidating all previo...
void Freeze()
Transitions an RNTupleModel from the building state to the frozen state, disabling adding additional ...
const ROOT::RFieldBase & GetConstField(std::string_view fieldName) const
Common user-tunable settings for storing RNTuples.
bool GetEnablePageChecksums() const
void SetCompression(std::uint32_t val)
Representation of an RNTuple data set in a ROOT file.
Long64_t Merge(TCollection *input, TFileMergeInfo *mergeInfo)
RNTuple implements the hadd MergeFile interface Merge this NTuple with the input list entries.
static constexpr std::uint64_t GetCurrentVersion()
Returns the RNTuple version in the following form: Epoch: 2 most significant bytes Major: next 2 byte...
void SetQuantized(std::size_t nBits, std::pair< T, T > valueRange)
Sets this field to use a quantized integer representation using nBits per value.
void SetTruncated(std::size_t nBits)
Set the on-disk representation of this field to a single-precision float truncated to nBits.
void ThrowOnError()
Short-hand method to throw an exception in the case of errors.
The class is used as a return type for operations that can fail; wraps a value of type T or an RError...
Collection abstract base class.
virtual Int_t GetEntries() const
TKey * FindKey(const char *keyname) const override
Find key with name keyname in the current directory.
TObject * Get(const char *namecycle) override
Return pointer to object identified by namecycle.
A class to pass information from the TFileMerger to the objects being merged.
TString fOptions
Additional text based option being passed down to customize the merge.
A file, usually with extension .root, that stores data and code in the form of serialized objects in ...
Int_t GetCompressionSettings() const
Book space in a file, create I/O buffers, to fill them, (un)compress them.
T * ReadObject()
To read an object (non deriving from TObject) from the file.
const char * GetName() const override
Returns name of object.
Mother of all ROOT objects.
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
@ kStrict
The merger will refuse to merge any 2 RNTuples whose schema doesn't match exactly.
@ kUnion
The merger will update the output model to include all columns from all sources.
@ kWarnOnHigherVersion
The merger will emit a warning when merging RNTuples with higher version than the latest supported by...
std::unique_ptr< T[]> MakeUninitArray(std::size_t size)
Make an array of default-initialized elements.
RProjectedFields & GetProjectedFieldsOfModel(RNTupleModel &model)
std::unique_ptr< RColumnElementBase > GenerateColumnElement(std::type_index inMemoryType, ROOT::ENTupleColumnType onDiskType)
std::string GetRenormalizedTypeName(const std::string &metaNormalizedName)
Given a type name normalized by ROOT meta, renormalize it for RNTuple. E.g., insert std::prefix.
Bool_t IsImplicitMTEnabled()
Returns true if the implicit multi-threading in ROOT is enabled.
std::uint64_t DescriptorId_t
Distriniguishes elements of the same type within a descriptor, e.g. different fields.
constexpr DescriptorId_t kInvalidDescriptorId
Type GetType(const std::string &Name)
if(fPos !=fText.size()) Fail("unexpected trailing content")
Set of merging options to pass to RNTupleMerger.
ENTupleMergingMode fMergingMode
Determines how the merging treats sources with different models (.
ENTupleMergeVersionBehavior fVersionBehavior
Determines how the Merge function behaves depending on the RNTuple sources' version.
ENTupleMergeErrBehavior fErrBehavior
Determines how the Merge function behaves upon merging errors.
std::optional< std::uint32_t > fCompressionSettings
If fCompressionSettings is empty (the default), the merger will not change the compression of any of ...
bool fExtraVerbose
If true, the merger will emit further diagnostics and information.
The incremental changes to a RNTupleModel
On-disk pages within a page source are identified by the column and page number.
Parameters for the SealPage() method.
bool fWriteChecksum
Adds a 8 byte little-endian xxhash3 checksum to the page payload.
std::uint32_t fCompressionSettings
Compression algorithm and level to apply.
void * fBuffer
Location for sealed output. The memory buffer has to be large enough.
const ROOT::Internal::RPage * fPage
Input page to be sealed.
const ROOT::Internal::RColumnElementBase * fElement
Corresponds to the page's elements, for size calculation etc.
A sealed page contains the bytes of a page as written to storage (packed & compressed).
RResult< void > VerifyChecksumIfEnabled() const
void SetBuffer(const void *buffer)
std::uint32_t GetNElements() const
void SetHasChecksum(bool hasChecksum)
const void * GetBuffer() const
void SetNElements(std::uint32_t nElements)
bool GetHasChecksum() const
std::size_t GetBufferSize() const
void SetBufferSize(std::size_t bufferSize)
std::size_t GetDataSize() const
@ kUseGeneralPurpose
Use the new recommended general-purpose setting; it is a best trade-off between compression ratio/dec...