38#include <initializer_list>
39#include <unordered_map>
65 const Ssiz_t wordLen = strlen(word);
66 if (str.Length() < wordLen)
70 return str.Length() == wordLen || str(wordLen) ==
' ';
75 std::initializer_list<std::pair<const char *, T>> validValues)
77 const Ssiz_t patternLen = strlen(pattern);
78 assert(pattern[patternLen - 1] ==
'=');
80 idx >= 0 && opts.
Length() > idx + patternLen) {
81 auto sub =
TString(opts(idx + patternLen, opts.
Length() - idx - patternLen));
82 for (
const auto &[
name,
value] : validValues) {
93 return ParseStringOption<ENTupleMergingMode>(opts,
"rntuple.MergingMode=",
95 {
"Filter", ENTupleMergingMode::kFilter},
96 {
"Union", ENTupleMergingMode::kUnion},
97 {
"Strict", ENTupleMergingMode::kStrict},
103 return ParseStringOption<ENTupleMergeErrBehavior>(opts,
"rntuple.ErrBehavior=",
105 {
"Abort", ENTupleMergeErrBehavior::kAbort},
106 {
"Skip", ENTupleMergeErrBehavior::kSkip},
112 return ParseStringOption<ENTupleMergeVersionBehavior>(
113 opts,
"rntuple.VersionBehavior=",
115 {
"WarnOnHigherVersion", ENTupleMergeVersionBehavior::kWarnOnHigherVersion},
116 {
"AbortOnHigherVersion", ENTupleMergeVersionBehavior::kAbortOnHigherVersion},
126 if (!inputs || inputs->
GetEntries() < 3 || !mergeInfo) {
135 std::string ntupleName = std::string(itr()->GetName());
139 TFile *outFile =
dynamic_cast<TFile *
>(secondArg);
147 TKey *outKey = outFile->
FindKey(ntupleName.c_str());
159 const bool defaultComp = mergeInfo->
fOptions.
Contains(
"DefaultCompression");
160 const bool firstSrcComp = mergeInfo->
fOptions.
Contains(
"FirstSrcCompression");
161 const bool extraVerbose = mergeInfo->
fOptions.
Contains(
"rntuple.ExtraVerbose");
162 if (defaultComp && firstSrcComp) {
165 "only the latter will apply.";
167 std::optional<std::uint32_t> compression;
171 }
else if (!defaultComp) {
181 std::vector<std::unique_ptr<RPageSourceFile>> sources;
182 std::vector<RPageSource *> sourcePtrs;
184 while (
const auto &pitr = itr()) {
197 source->Attach(RNTupleSerializer::EDescriptorDeserializeMode::kRaw);
199 auto descGuard = source->FindClusterId(0, firstClusterId);
202 <<
"Asked to use the first source's compression as the output compression, but the "
203 "first source (file '"
205 <<
"') has an empty RNTuple, therefore the output compression could not be "
209 const auto &firstCluster = descGuard->GetClusterDescriptor(firstClusterId);
210 auto colRangeIter = firstCluster.GetColumnRangeIterable();
211 auto firstColRange = colRangeIter.begin();
212 if (firstColRange == colRangeIter.end()) {
214 <<
"Asked to use the first source's compression as the output compression, but the "
215 "first source (file '"
217 <<
"') has an empty RNTuple, therefore the output compression could not be "
221 compression = (*firstColRange).GetCompressionSettings();
224 sources.push_back(std::move(source));
230 auto destination = std::make_unique<ROOT::Internal::RPageSinkFile>(ntupleName, *outFile, writeOpts);
231 std::unique_ptr<ROOT::RNTupleModel> model;
235 outSource->Attach(RNTupleSerializer::EDescriptorDeserializeMode::kForWriting);
236 outSource->LoadAllPageLists();
237 auto desc = outSource->GetSharedDescriptorGuard();
238 model = destination->InitFromDescriptor(desc.GetConstRef(),
true );
242 sourcePtrs.reserve(sources.size());
243 for (
const auto &s : sources) {
244 sourcePtrs.push_back(s.get());
248 RNTupleMerger merger{std::move(destination), std::move(model)};
261 merger.Merge(sourcePtrs, mergerOpts).ThrowOnError();
268}
catch (
const std::exception &
ex) {
280struct RChangeCompressionFunc {
282 std::uint32_t fCompressionSettings;
286 std::size_t fBufSize;
289 void operator()()
const
295 std::unique_ptr<std::byte[]> unzipBufOwned;
297 if (fCompressionSettings != 0) {
298 unzipBufOwned = MakeUninitArray<std::byte>(bytesPacked);
299 unzipBuf = unzipBufOwned.get();
307 std::size_t nBytesZipped;
308 if (fCompressionSettings != 0) {
310 assert(fBufSize >= bytesPacked + checksumSize);
313 nBytesZipped = bytesPacked;
321 std::optional<ROOT::Experimental::TTaskGroup> &fGroup;
323 template <
typename T>
324 void operator()(T &&
f)
346struct RColReprMapping {
347 std::uint32_t fSource;
353struct RColReprExtension : RColReprMapping {
355 std::vector<ROOT::Internal::RColumnFormat> fSourceRepr;
359 std::uint32_t fOrigFirstElementIndex = 0;
362static std::optional<std::uint32_t>
363FindColumnReprMapping(
const std::vector<RColReprMapping> &mappings, std::uint32_t sourceReprIndex)
365 for (
const auto [
src,
dst] : mappings)
366 if (
src == sourceReprIndex)
371struct RColumnOutInfo {
376using ColumnIdMap_t = std::unordered_map<std::string, RColumnOutInfo>;
378struct RColumnMergeInfo {
382 std::string fColumnName;
387 std::uint16_t fOutputReprIndex = 0;
389 std::optional<std::type_index> fInMemoryType;
394struct RSealedPageMergeData {
397 std::deque<RPageStorage::SealedPageSequence_t> fPagesV;
398 std::vector<RPageStorage::RSealedPageGroup> fGroups;
399 std::vector<std::unique_ptr<std::byte[]>> fBuffers;
402struct RColumnInfoGroup {
403 std::vector<RColumnMergeInfo> fExtraDstColumns;
404 std::vector<RColumnMergeInfo> fCommonColumns;
408using FieldCollectionMap_t = std::unordered_map<const ROOT::RFieldDescriptor *, std::vector<T>>;
410struct RDescriptorsComparison {
411 std::vector<const ROOT::RFieldDescriptor *> fExtraDstFields;
412 std::vector<const ROOT::RFieldDescriptor *> fExtraSrcFields;
413 std::vector<RCommonField> fCommonFields;
416 std::unordered_map<ROOT::DescriptorId_t, std::vector<const ROOT::RFieldDescriptor *>> fExtraSrcSubfields;
419 std::string fFastMergeErrors;
424 FieldCollectionMap_t<RColReprMapping> fColReprMappings;
425 FieldCollectionMap_t<RColReprExtension> fColReprExtensions;
434 RDescriptorsComparison &
result, std::vector<std::string> &errors)
442 if (srcColCardinality != dstColCardinality) {
443 std::stringstream ss;
445 <<
"` has a different column cardinality than previously-seen field with the same name (old: "
446 << dstColCardinality <<
", new: " << srcColCardinality <<
")";
447 errors.push_back(ss.str());
451 if (srcColCardinality == 0)
454 const auto srcNColReprs = srcColumns.size() / srcColCardinality;
455 const auto dstNColReprs = dstColumns.size() / dstColCardinality;
456 std::uint32_t nextDstReprIndex = dstNColReprs;
462 for (
auto srcReprIdx = 0u; srcReprIdx < srcNColReprs; ++srcReprIdx) {
463 std::int64_t matchingRepr = -1;
464 for (
auto dstReprIdx = 0u; dstReprIdx < dstNColReprs; ++dstReprIdx) {
466 for (
auto reprColIdx = 0u; reprColIdx < srcColCardinality; ++reprColIdx) {
467 const auto srcColId = srcColumns[srcReprIdx * srcColCardinality + reprColIdx];
469 const auto dstColId = dstColumns[dstReprIdx * dstColCardinality + reprColIdx];
471 if (srcCol.GetType() != dstCol.GetType()) {
483 for (
auto reprColIdx = 0u; reprColIdx < srcColCardinality; ++reprColIdx) {
484 const auto srcColId = srcColumns[srcReprIdx * srcColCardinality + reprColIdx];
486 const auto dstColId = dstColumns[dstReprIdx * dstColCardinality + reprColIdx];
488 if (srcCol.GetType() != dstCol.GetType() || srcCol.GetBitsOnStorage() != dstCol.GetBitsOnStorage() ||
489 srcCol.GetValueRange() != dstCol.GetValueRange()) {
497 matchingRepr = dstReprIdx;
503 if (errors.empty()) {
504 if (matchingRepr >= 0 && matchingRepr != srcReprIdx) {
506 assert(matchingRepr < std::numeric_limits<std::uint32_t>::max());
507 result.fColReprMappings[&dstField].push_back(
508 RColReprMapping{srcReprIdx,
static_cast<std::uint32_t
>(matchingRepr)});
509 }
else if (matchingRepr < 0) {
511 std::vector<ROOT::Internal::RColumnFormat> newRepr;
512 newRepr.reserve(srcColCardinality);
513 std::uint32_t firstElemIdx = 0;
514 for (
auto reprColIdx = 0u; reprColIdx < srcColCardinality; ++reprColIdx) {
515 const auto srcColId = srcColumns[srcReprIdx * srcColCardinality + reprColIdx];
518 assert(firstElemIdx == 0 || firstElemIdx == srcCol.GetFirstElementIndex());
519 firstElemIdx = srcCol.GetFirstElementIndex();
520 auto &reprElement = newRepr.emplace_back();
521 reprElement.fType = srcCol.GetType();
522 reprElement.fBitWidth = srcCol.GetBitsOnStorage();
523 reprElement.fValueRange = srcCol.GetValueRange();
525 RColReprExtension extension{{srcReprIdx, nextDstReprIndex}, newRepr, firstElemIdx};
526 nextDstReprIndex += newRepr.size();
527 result.fColReprExtensions[&dstField].push_back(extension);
528 result.fColReprMappings[&dstField].push_back(extension);
536 bool forAttributeMerging)
538 RDescriptorsComparison res;
541 for (
const auto &dstField :
dst.GetTopLevelFields()) {
542 const auto srcFieldId =
src.FindFieldId(dstField.GetFieldName());
544 const auto &srcField =
src.GetFieldDescriptor(srcFieldId);
545 res.fCommonFields.push_back({srcField, dstField});
547 res.fExtraDstFields.emplace_back(&dstField);
552 for (
const auto &srcField :
src.GetTopLevelFields()) {
553 const auto dstFieldId =
dst.FindFieldId(srcField.GetFieldName());
555 res.fExtraSrcFields.push_back(&srcField);
559 if (forAttributeMerging) {
561 const auto userDataFieldId =
src.FindFieldId(kMetaFieldNames[kMetaFieldIndex_UserData]);
563 return R__FAIL(
"attribute user data field not found. The RNTuple is corrupted and cannot be merged.");
565 const auto &userDataField =
src.GetFieldDescriptor(userDataFieldId);
566 for (
const auto subfieldId : userDataField.GetLinkIds()) {
567 const auto subfieldName =
src.GetQualifiedFieldName(subfieldId);
568 const auto dstFieldId =
dst.FindFieldId(subfieldName);
570 const auto &subfield =
src.GetFieldDescriptor(subfieldId);
571 res.fExtraSrcSubfields[userDataFieldId].push_back(&subfield);
584 if (!projCompatible) {
585 std::stringstream ss;
587 <<
"` is incompatible with previously-seen field with that name because the "
588 << (srcField.
IsProjectedField() ?
"new" :
"old") <<
" one is projected and the other isn't";
589 errors.push_back(ss.str());
594 if (srcName != dstName) {
595 std::stringstream ss;
597 <<
"` is projected to a different field than a previously-seen field with the same name (old: " << dstName
598 <<
", new: " << srcName <<
")";
599 errors.push_back(ss.str());
608 bool forAttributeMerging =
false)
620 auto res = descCmpRes.Unwrap();
621 std::vector<std::string> slowMergeErrors;
622 std::vector<std::string> errors;
625 auto fieldsToCheck = res.fCommonFields;
626 const auto nTopLevelFields = fieldsToCheck.size();
629 for (std::size_t fieldIdx = 0; fieldIdx < fieldsToCheck.size(); ++fieldIdx) {
630 const auto &field = fieldsToCheck[fieldIdx];
633 const auto &fieldName = field.fSrc->GetFieldName();
636 auto &errs = (fieldIdx < nTopLevelFields) ? slowMergeErrors : errors;
643 const auto &dstTyName = field.fDst->GetTypeName();
644 if (srcTyName != dstTyName) {
645 std::stringstream ss;
646 ss <<
"Field `" << fieldName
647 <<
"` has a type incompatible with a previously-seen field with the same name: (old: " << dstTyName
648 <<
", new: " << srcTyName <<
")";
649 errors.push_back(ss.str());
653 const auto srcTyChk = field.fSrc->GetTypeChecksum();
654 const auto dstTyChk = field.fDst->GetTypeChecksum();
655 if (srcTyChk && dstTyChk && *srcTyChk != *dstTyChk) {
656 std::stringstream ss;
657 ss <<
"Field `" << field.fSrc->GetFieldName()
658 <<
"` has a different type checksum than previously-seen field with the same name";
659 errors.push_back(ss.str());
663 const auto srcTyVer = field.fSrc->GetTypeVersion();
664 const auto dstTyVer = field.fDst->GetTypeVersion();
665 if (srcTyVer != dstTyVer) {
666 std::stringstream ss;
667 ss <<
"Field `" << field.fSrc->GetFieldName()
668 <<
"` has a different type version than previously-seen field with the same name (old: " << dstTyVer
669 <<
", new: " << srcTyVer <<
")";
670 errors.push_back(ss.str());
674 const auto srcFldVer = field.fSrc->GetFieldVersion();
675 const auto dstFldVer = field.fDst->GetFieldVersion();
676 if (srcFldVer != dstFldVer) {
677 std::stringstream ss;
678 ss <<
"Field `" << field.fSrc->GetFieldName()
679 <<
"` has a different field version than previously-seen field with the same name (old: " << dstFldVer
680 <<
", new: " << srcFldVer <<
")";
681 errors.push_back(ss.str());
684 const auto srcRole = field.fSrc->GetStructure();
685 const auto dstRole = field.fDst->GetStructure();
686 if (srcRole != dstRole) {
687 std::stringstream ss;
688 ss <<
"Field `" << field.fSrc->GetFieldName()
689 <<
"` has a different structural role than previously-seen field with the same name (old: " << dstRole
690 <<
", new: " << srcRole <<
")";
691 errors.push_back(ss.str());
695 if (!field.fSrc->IsProjectedField()) {
700 const auto &srcLinks = field.fSrc->GetLinkIds();
701 const auto &dstLinks = field.fDst->GetLinkIds();
702 if (srcLinks.size() != dstLinks.size()) {
703 std::stringstream ss;
704 ss <<
"Field `" << field.fSrc->GetFieldName()
705 <<
"` has a different number of children than previously-seen field with the same name (old: "
706 << dstLinks.size() <<
", new: " << srcLinks.size() <<
")";
707 errors.push_back(ss.str());
709 for (std::size_t linkIdx = 0, linkNum = srcLinks.size(); linkIdx < linkNum; ++linkIdx) {
710 const auto &srcSubfield =
src.GetFieldDescriptor(srcLinks[linkIdx]);
711 const auto &dstSubfield =
dst.GetFieldDescriptor(dstLinks[linkIdx]);
712 fieldsToCheck.push_back(RCommonField{srcSubfield, dstSubfield});
718 if (!slowMergeErrors.empty()) {
720 for (
const auto &err : slowMergeErrors)
721 errMsg += std::string(
"\n * ") + err;
723 errMsg = errMsg.substr(1);
728 if (!errors.empty()) {
729 for (
const auto &err : errors)
730 errMsg += std::string(
"\n * ") + err;
731 errMsg = errMsg.substr(1);
732 res.fFastMergeErrors = errMsg;
749 if (logicalIds.size() == 1) {
751 const auto type = column.GetType();
755 if (!realFloat && !realDouble) {
757 "` has a Real32Trunc/Real32Quant column but is not an RRealField");
759 const auto bits = column.GetBitsOnStorage();
763 realFloat->SetTruncated(bits);
767 const auto valueRange = column.GetValueRange();
770 "` has a Real32Quant column with no value range");
773 realFloat->SetQuantized(bits,
774 {
static_cast<float>(valueRange->fMin),
static_cast<float>(valueRange->fMax)});
776 realDouble->
SetQuantized(bits, {valueRange->fMin, valueRange->fMax});
804 const auto &newFields = descCmp.fExtraSrcFields;
810 std::string msg =
"destination doesn't contain field";
811 if (newFields.size() > 1)
814 msg += std::accumulate(newFields.begin(), newFields.end(), std::string{}, [](
const auto &acc,
const auto *field) {
815 return acc + (acc.length() ?
", " :
"") +
'`' + field->GetFieldName() +
'`';
818 <<
" to the destination model (entry #" << nEntries <<
").";
824 changeset.fAddedFields.reserve(newFields.size());
826 for (
const auto *fieldDesc : newFields) {
827 if (fieldDesc->IsProjectedField())
830 auto field = fieldDesc->CreateField(srcDesc, cfOpts);
835 for (
const auto &colId : fieldDesc->GetLogicalColumnIds()) {
837 representatives.push_back(column.GetType());
839 field->SetColumnRepresentatives({representatives});
846 changeset.AddField(std::move(field));
849 for (
const auto *fieldDesc : newFields) {
850 if (!fieldDesc->IsProjectedField())
854 auto field = fieldDesc->CreateField(srcDesc, cfOpts);
855 const auto sourceId = fieldDesc->GetProjectionSourceId();
857 fieldMap[field.get()] = &sourceField;
859 for (
const auto &subfield : *field) {
863 fieldMap[&subfield] = &subSourceField;
865 changeset.fAddedProjectedFields.emplace_back(field.get());
870 for (
const auto &[parentId, subfields] : descCmp.fExtraSrcSubfields) {
873 for (
const auto *subfield : subfields) {
874 auto field = subfield->CreateField(srcDesc, cfOpts);
875 changeset.AddField(std::move(field), parentFQName);
891 auto &commonFields = descCmp.fCommonFields;
892 commonFields.reserve(commonFields.size() + newFields.size());
921 for (
auto it = newFields.rbegin(); it != newFields.rend(); ++it) {
922 const auto *field = *it;
923 const auto newFieldInDstId = dstDesc.
FindFieldId(field->GetFieldName());
924 const auto &newFieldInDst = dstDesc.GetFieldDescriptor(newFieldInDstId);
925 commonFields.insert(commonFields.begin(), RCommonField{*field, newFieldInDst});
941 if (fieldType ==
"bool")
return typeid(
bool);
942 if (fieldType ==
"std::byte")
return typeid(std::byte);
943 if (fieldType ==
"char")
return typeid(char);
944 if (fieldType ==
"std::int8_t")
return typeid(std::int8_t);
945 if (fieldType ==
"std::uint8_t")
return typeid(std::uint8_t);
946 if (fieldType ==
"std::int16_t")
return typeid(std::int16_t);
947 if (fieldType ==
"std::uint16_t")
return typeid(std::uint16_t);
948 if (fieldType ==
"std::int32_t")
return typeid(std::int32_t);
949 if (fieldType ==
"std::uint32_t")
return typeid(std::uint32_t);
950 if (fieldType ==
"std::int64_t")
return typeid(std::int64_t);
951 if (fieldType ==
"std::uint64_t")
return typeid(std::uint64_t);
952 if (fieldType ==
"float")
return typeid(float);
953 if (fieldType ==
"double")
return typeid(
double);
961 ColumnIdMap_t &colIdMap,
const std::string &prefix =
"")
966 RColumnOutInfo info{};
969 name +
'.' + std::to_string(colDesc.GetIndex()) +
'.' + std::to_string(colDesc.GetRepresentationIndex());
970 colIdMap[colName] = info;
973 for (
const auto &subId : fieldDesc.
GetLinkIds()) {
981 const FieldCollectionMap_t<RColReprExtension> &extensions,
983 std::unordered_map<
ROOT::DescriptorId_t, std::vector<const ROOT::RFieldDescriptor *>> &outProjectionPointees)
985 const auto it = extensions.find(&field);
986 if (it != extensions.end())
987 outExtensions.emplace_back(it->first, it->second);
1000class RNTupleMergeStrategy {
1003 std::unique_ptr<ROOT::RNTupleModel> fDstModel;
1014 : fDstModel(std::move(dstModel)), fDestination(
dst), fDstDescriptor(
dst.GetDescriptor()), fMergeOpts(mergeOpts)
1018 virtual ~RNTupleMergeStrategy() =
default;
1034 virtual void BeforeCommit() = 0;
1042 const RNTupleMergeStrategy &fParentStrategy;
1043 std::shared_ptr<ROOT::NTupleSize_t> fRangeStartPtr;
1046 RNTupleAttrMergeActions(
const RNTupleMergeStrategy &parentStrategy,
ROOT::NTupleSize_t initialStartOffset)
1047 : fAttrEntryStartOffset(initialStartOffset), fParentStrategy(parentStrategy)
1056 if (!fRangeStartPtr)
1060 *fRangeStartPtr += fAttrEntryStartOffset;
1061 return ENTupleMergeActionResult::kEmit;
1066 fAttrEntryStartOffset = fParentStrategy.GetNDstEntries();
1067 fRangeStartPtr =
nullptr;
1068 return ENTupleMergeActionResult::kDrop;
1079class RNTupleMergeStrategyDefault final :
public RNTupleMergeStrategy {
1080 std::unique_ptr<ROOT::Internal::RPageAllocator> fPageAlloc = std::make_unique<ROOT::Internal::RPageAllocatorHeap>();
1081 std::optional<ROOT::Experimental::TTaskGroup> fTaskGroup;
1082 std::unordered_map<ROOT::DescriptorId_t, std::vector<const ROOT::RFieldDescriptor *>> fProjectionPointees;
1084 std::vector<RColumnMergeInfo> fColumns;
1086 ColumnIdMap_t fColumnIdMap;
1091 RSealedPageMergeData &sealedPageData);
1093 ROOT::RResult<void> GenerateZeroPagesForColumns(
size_t nEntriesToGenerate, std::span<const RColumnMergeInfo> columns,
1094 RSealedPageMergeData &sealedPageData);
1097 const FieldCollectionMap_t<RColReprMapping> &colReprMappings,
1099 const std::string &prefix =
"");
1101 RColumnInfoGroup GatherColumnInfos(
const RDescriptorsComparison &descCmp,
const ROOT::RNTupleDescriptor &srcDesc);
1104 std::span<const RColumnMergeInfo> extraDstColumns);
1109 : RNTupleMergeStrategy(std::move(dstModel),
dst, mergeOpts)
1119 for (
const auto &field :
dst.GetDescriptor().GetTopLevelFields()) {
1129 return fDestination.GetNEntries();
1134 source.Attach(RNTupleSerializer::EDescriptorDeserializeMode::kForWriting);
1135 source.LoadAllPageLists();
1136 fSrcDescriptor = &source.GetSharedDescriptorGuard().GetConstRef();
1139 constexpr bool copyClusters =
false;
1140 fDstModel = fDestination.InitFromDescriptor(*fSrcDescriptor, copyClusters);
1148 fMergeOpts.fExtraVerbose,
1153 void BeforeCommit() final {}
1166void RNTupleMergeStrategyDefault::AddColumnsFromField(std::vector<RColumnMergeInfo> &columns,
1168 const FieldCollectionMap_t<RColReprMapping> &colReprMappings,
1171 const std::string &prefix)
1181 columns.reserve(columns.size() + columnIds.size());
1187 RColumnMergeInfo info{};
1196 info.fParentFieldDescriptor = &srcFieldDesc;
1200 info.fParentNTupleDescriptor = &srcDesc;
1202 const auto mappingsIt = colReprMappings.find(&dstFieldDesc);
1203 std::uint16_t reprIndex = srcColumn.GetRepresentationIndex();
1204 if (mappingsIt != colReprMappings.end()) {
1205 if (
auto outReprIdx = FindColumnReprMapping(mappingsIt->second, reprIndex); outReprIdx)
1206 reprIndex = *outReprIdx;
1209 info.fColumnName =
name +
'.' + std::to_string(srcColumn.GetIndex()) +
'.' + std::to_string(reprIndex);
1213 if (
auto it = fColumnIdMap.find(info.fColumnName); it != fColumnIdMap.end()) {
1215 info.fOutputId = it->second.fColumnId;
1216 info.fOutputReprIndex = reprIndex;
1219 info.fOutputId = fColumnIdMap.size();
1224 const auto dstColumnIndex = reprIndex * dstFieldDesc.
GetColumnCardinality() + srcColumn.GetIndex();
1226 const auto &dstColumn = fDstDescriptor.GetColumnDescriptor(dstColumnId);
1227 columnType = dstColumn.GetType();
1228 info.fOutputReprIndex = reprIndex;
1229 fColumnIdMap[info.fColumnName] = RColumnOutInfo{info.fOutputId};
1232 if (fMergeOpts.fExtraVerbose) {
1234 <<
", phys.id " << srcColumn.GetPhysicalId() <<
", type "
1236 << info.fOutputId <<
", type "
1243 columns.emplace_back(info);
1246 const auto &srcChildrenIds = srcFieldDesc.
GetLinkIds();
1247 const auto &dstChildrenIds = dstFieldDesc.
GetLinkIds();
1248 assert(srcChildrenIds.size() == dstChildrenIds.size());
1249 for (
auto i = 0u; i < srcChildrenIds.size(); ++i) {
1251 const auto &dstChild = fDstDescriptor.GetFieldDescriptor(dstChildrenIds[i]);
1252 AddColumnsFromField(columns, srcDesc, colReprMappings, srcChild, dstChild,
name);
1259RColumnInfoGroup RNTupleMergeStrategyDefault::GatherColumnInfos(
const RDescriptorsComparison &descCmp,
1262 RColumnInfoGroup res;
1264 AddColumnsFromField(res.fExtraDstColumns, fDstDescriptor, descCmp.fColReprMappings, *field, *field);
1266 for (
const auto &[srcField, dstField] : descCmp.fCommonFields) {
1267 AddColumnsFromField(res.fCommonColumns, srcDesc, descCmp.fColReprMappings, *srcField, *dstField);
1273ROOT::RResult<void> RNTupleMergeStrategyDefault::GenerateZeroPagesForColumns(
size_t nEntriesToGenerate,
1274 std::span<const RColumnMergeInfo> columns,
1275 RSealedPageMergeData &sealedPageData)
1277 if (!nEntriesToGenerate)
1280 for (
const auto &column : columns) {
1290 bool skipColumn =
false;
1291 auto nRepetitions = std::max<std::uint64_t>(field->
GetNRepetitions(), 1);
1308 return R__FAIL(
"Destination RNTuple contains a streamer field (" + field->
GetFieldName() +
1309 ") that is not present in one of the sources. "
1310 "Creating a default value for a streamer field is ill-defined, therefore the merging "
1311 "process will abort.");
1318 const auto &columnDesc = fDstDescriptor.GetColumnDescriptor(column.fOutputId);
1320 const auto nElements = nEntriesToGenerate * nRepetitions;
1321 const auto nBytesOnStorage = colElement->GetPackedSize(nElements);
1323 constexpr auto kPageSizeLimit = 256 * 1024;
1325 const size_t nPages = nBytesOnStorage / kPageSizeLimit + !!(nBytesOnStorage % kPageSizeLimit);
1326 for (
size_t i = 0; i < nPages; ++i) {
1327 const auto pageSize = (i < nPages - 1) ? kPageSizeLimit : nBytesOnStorage - kPageSizeLimit * (nPages - 1);
1329 const auto bufSize = pageSize + checksumSize;
1330 assert(pageSize % colElement->GetSize() == 0);
1331 const auto nElementsPerPage = pageSize / colElement->GetSize();
1332 auto page = fPageAlloc->NewPage(colElement->GetSize(), nElementsPerPage);
1333 page.GrowUnchecked(nElementsPerPage);
1334 memset(page.GetBuffer(), 0, page.GetNBytes());
1336 auto &buffer = sealedPageData.fBuffers.emplace_back(
new std::byte[bufSize]);
1338 sealConf.
fElement = colElement.get();
1339 sealConf.
fPage = &page;
1340 sealConf.
fBuffer = buffer.get();
1342 sealConf.
fWriteChecksum = fDestination.GetWriteOptions().GetEnablePageChecksums();
1345 sealedPageData.fPagesV.push_back({sealedPage});
1346 sealedPageData.fGroups.emplace_back(column.fOutputId, sealedPageData.fPagesV.back().cbegin(),
1347 sealedPageData.fPagesV.back().cend());
1357 std::span<RColumnMergeInfo> commonColumns,
1359 RSealedPageMergeData &sealedPageData)
1361 const auto nCommonColumnsInCluster = commonColumnSet.size();
1362 assert(nCommonColumnsInCluster <= commonColumns.size());
1364 if (nCommonColumnsInCluster == 0)
1372 const std::uint32_t outCompression = fMergeOpts.fCompressionSettings.value();
1374 for (
size_t colIdx = 0; colIdx < nCommonColumnsInCluster; ++colIdx) {
1375 const auto &column = commonColumns[colIdx];
1376 const auto &columnId = column.fInputId;
1379 const auto &columnDesc = fSrcDescriptor->GetColumnDescriptor(columnId);
1380 const auto srcColElement = column.fInMemoryType
1385 const auto &pages = clusterDesc.
GetPageRange(columnId);
1388 sealedPages.resize(pages.GetPageInfos().size());
1392 assert(!columnRange.IsSuppressed());
1393 const auto colRangeCompressionSettings = columnRange.GetCompressionSettings().value();
1400 const bool needsRecompressing = colRangeCompressionSettings != outCompression;
1402 if (needsRecompressing && fMergeOpts.fExtraVerbose) {
1404 <<
": { compression: " << colRangeCompressionSettings <<
" => "
1405 << fMergeOpts.fCompressionSettings.value() <<
", onDiskType: "
1407 srcColElement->GetIdentifier().fOnDiskType)
1411 const size_t pageBufferBaseIdx = sealedPageData.fBuffers.size();
1414 if (needsRecompressing)
1415 sealedPageData.fBuffers.resize(sealedPageData.fBuffers.size() + pages.GetPageInfos().size());
1422 if (columnDesc.GetFirstElementIndex() > clusterDesc.
GetFirstEntryIndex() && fDestination.GetNEntries() > 0) {
1423 const auto nMissingEntries = columnDesc.GetFirstElementIndex() - clusterDesc.
GetFirstEntryIndex();
1424 auto res = GenerateZeroPagesForColumns(nMissingEntries, {&column, 1}, sealedPageData);
1430 std::uint64_t pageIdx = 0;
1431 for (
const auto &pageInfo : pages.GetPageInfos()) {
1432 assert(pageIdx < sealedPages.size());
1433 assert(sealedPageData.fBuffers.size() == 0 || pageIdx < sealedPageData.fBuffers.size());
1443 sealedPage.
SetBufferSize(pageInfo.GetLocator().GetNBytesOnStorage() + checksumSize);
1444 sealedPage.
SetBuffer(onDiskPage->GetAddress());
1449 if (needsRecompressing) {
1450 const auto uncompressedSize = srcColElement->GetSize() * sealedPage.
GetNElements();
1451 auto &buffer = sealedPageData.fBuffers[pageBufferBaseIdx + pageIdx];
1452 const auto bufSize = uncompressedSize + checksumSize;
1456 buffer = MakeUninitArray<std::byte>(bufSize);
1459 RTaskVisitor{fTaskGroup}(RChangeCompressionFunc{
1466 fDestination.GetWriteOptions()
1478 sealedPageData.fPagesV.push_back(std::move(sealedPages));
1479 sealedPageData.fGroups.emplace_back(column.fOutputId, sealedPageData.fPagesV.back().cbegin(),
1480 sealedPageData.fPagesV.back().cend());
1492 std::span<RColumnMergeInfo> commonColumns,
1493 std::span<const RColumnMergeInfo> extraDstColumns)
1497 std::vector<RColumnMergeInfo> missingColumns{extraDstColumns.begin(), extraDstColumns.end()};
1499 R__ASSERT(fSrcDescriptor->GetNClusters() == fSrcDescriptor->GetNActiveClusters());
1500 for (
const auto &clusterDesc : fSrcDescriptor->GetActiveClusterIterable()) {
1501 const auto nClusterEntries = clusterDesc.
GetNEntries();
1508 FieldCollectionMap_t<ROOT::DescriptorId_t> columnsInCluster;
1522 int nCommonColumnsInCluster = 0;
1523 std::partition(commonColumns.begin(), commonColumns.end(), [&](
const auto &column) {
1524 if (clusterDesc.ContainsColumn(column.fInputId)) {
1525 const auto &colRange = clusterDesc.GetColumnRange(column.fInputId);
1526 ++nCommonColumnsInCluster;
1527 columnsInCluster[column.fParentFieldDescriptor].push_back(column.fOutputId);
1528 if (!colRange.IsSuppressed()) {
1529 commonColumnSet.emplace(column.fInputId);
1532 fDestination.CommitSuppressedColumn(ColumnHandle_t{column.fOutputId});
1547 for (
const auto &[fieldDesc, columnIds] : columnsInCluster) {
1548 const auto &fieldFQName = fSrcDescriptor->GetQualifiedFieldName(fieldDesc->GetId());
1549 const auto cardinality = fieldDesc->GetColumnCardinality();
1550 for (
auto i = 0u; i < fieldDesc->GetLogicalColumnIds().
size(); ++i) {
1551 const auto colIndex = i % cardinality;
1552 const auto reprIndex = i / cardinality;
1553 const auto colName =
"." + fieldFQName +
'.' + std::to_string(colIndex) +
'.' + std::to_string(reprIndex);
1554 const auto colIt = fColumnIdMap.find(colName);
1555 assert(colIt != fColumnIdMap.end());
1556 const auto colOutId = colIt->second.fColumnId;
1557 if (std::find(columnIds.begin(), columnIds.end(), colOutId) == columnIds.end()) {
1558 fDestination.CommitSuppressedColumn(ColumnHandle_t{colOutId});
1563 RSealedPageMergeData sealedPageData;
1564 auto res = MergeCommonColumns(clusterPool, clusterDesc, commonColumns, commonColumnSet, sealedPageData);
1572 missingColumns.resize(extraDstColumns.size());
1573 for (
size_t i = nCommonColumnsInCluster; i < commonColumns.size(); ++i)
1574 missingColumns.push_back(commonColumns[i]);
1576 res = GenerateZeroPagesForColumns(nClusterEntries, missingColumns, sealedPageData);
1581 fDestination.CommitSealedPageV(sealedPageData.fGroups);
1582 fDestination.CommitCluster(nClusterEntries);
1597 if (!descCmp.fColReprExtensions.empty()) {
1598 for (
const auto &field : descCmp.fExtraDstFields) {
1609 std::vector<std::pair<const ROOT::RFieldDescriptor *, std::vector<RColReprExtension>>> colExtensions;
1610 colExtensions.reserve(descCmp.fColReprExtensions.size());
1611 for (
const auto &commonField : descCmp.fCommonFields) {
1612 const auto *field = commonField.fDst;
1614 fProjectionPointees);
1616 for (
const auto &field : descCmp.fExtraSrcFields) {
1621 for (
const auto &[fieldDesc, extensions] : colExtensions) {
1622 auto &mappings = descCmp.fColReprMappings[fieldDesc];
1623 for (
const auto &extension : extensions) {
1624 const auto firstColumnId = fDestination.AddColumnRepresentation(*fieldDesc, extension.fSourceRepr,
1625 extension.fOrigFirstElementIndex);
1630 if (
auto it = fProjectionPointees.find(fieldDesc->GetId()); it != fProjectionPointees.end()) {
1631 for (
const auto &projection : it->second) {
1632 for (
auto colIdx = 0u; colIdx < extension.fSourceRepr.size(); ++colIdx)
1633 fDestination.AddAliasColumn(fDstDescriptor, *projection, firstColumnId + colIdx);
1636 mappings.push_back(extension);
1642 auto columnInfos = GatherColumnInfos(descCmp, *fSrcDescriptor);
1643 auto res = MergeSourceClusters(source, columnInfos.fCommonColumns, columnInfos.fExtraDstColumns);
1657 std::unique_ptr<RNTupleMergeActions>
fActions = std::make_unique<RNTupleMergeActions>();
1663 std::unique_ptr<RNTupleMergeActions> actions = std::make_unique<RNTupleMergeActions>())
1664 : RNTupleMergeStrategy(std::move(dstModel),
dst, mergeOpts),
fActions(std::move(actions))
1687 fDestination.
Init(*fDstModel);
1730 for (
const auto &[srcField, dstField] : descCmp.fCommonFields) {
1732 const auto dstType = dstDesc.GetTypeNameForComparison(*dstField);
1733 if (srcType != dstType) {
1735 "` has a type incompatible with a previously-seen field with the same name: (old: " + srcType +
1736 ", new: " + dstType +
")");
1742 auto srcModel = fDstModel->Clone();
1751 fieldZero.SetOnDiskId(fieldZeroId);
1752 const auto resetFieldIdAndColumnRepresentatives = [](
ROOT::RFieldBase &field,
const auto &recursiveCall) ->
void {
1756 recursiveCall(*
f, recursiveCall);
1761 for (
auto *field : fieldZero.GetMutableSubfields()) {
1766 resetFieldIdAndColumnRepresentatives(*field, resetFieldIdAndColumnRepresentatives);
1781 auto srcEntry = srcModel->CreateBareEntry();
1785 for (
const auto &[srcFieldDesc, _dstFieldDesc] : descCmp.fCommonFields) {
1795 for (
const auto *field : descCmp.fExtraDstFields) {
1802 for (
auto idx = 0u; idx < fSrcDescriptor->
GetNEntries(); ++idx) {
1803 srcEntry->Read(idx);
1841 std::unique_ptr<ROOT::RNTupleModel> model)
1842 : fDestination(std::move(destination)), fModel(std::move(model))
1859 constexpr bool doSlowMerge =
false;
1863 const auto dstCompSettings =
fDestination->GetWriteOptions().GetCompression();
1867 return R__FAIL(std::string(
"The compression given to RNTupleMergeOptions is different from that of the "
1869 std::to_string(*mergeOpts.
fCompressionSettings) +
", sink: " + std::to_string(dstCompSettings) +
1870 ") This is currently unsupported.");
1877 "passing an already-initialized destination to RNTupleMerger::Merge (i.e. trying to do incremental "
1878 "merging) can only be done by providing a valid ROOT::RNTupleModel when constructing the RNTupleMerger.");
1881 if (doSlowMerge &&
fModel) {
1882 return R__FAIL(
"incremental slow merging is currently unsupported.");
1886#define SKIP_OR_ABORT(errMsg) \
1887 if (mergeOpts.fErrBehavior == ENTupleMergeErrBehavior::kSkip) { \
1888 R__LOG_WARNING(NTupleMergeLog()) << "Skipping RNTuple due to: " << (errMsg); \
1891 return R__FAIL(errMsg); \
1894 auto strat = doSlowMerge ? std::unique_ptr<RNTupleMergeStrategy>(
1896 : std::unique_ptr<RNTupleMergeStrategy>(
1898 struct RAttrSetMergeData {
1899 std::unique_ptr<ROOT::Internal::RPagePersistentSink> fSink;
1902 RAttrSetMergeData(std::unique_ptr<ROOT::Internal::RPagePersistentSink> attrSink,
1905 : fSink(std::move(attrSink)),
1906 fStrategy(
nullptr, *fSink, mergeOpts,
1907 std::make_unique<RNTupleAttrMergeActions>(parentStrategy, initialStartOffset))
1912 std::unordered_map<std::string, RAttrSetMergeData> attrSetsData;
1922 <<
"RNTuple '" << srcDescriptor->
GetName()
1923 <<
"' has a higher format version than the latest supported by this version "
1924 "of ROOT. Merging will work but some features may be dropped.";
1926 return R__FAIL(
"RNTuple '" + srcDescriptor->GetName() +
1927 "' has a higher format version than the latest supported by this version. Refusing to "
1928 "merge, since RNTupleMergeOptions::fVersionBehavior is set to AbortOnHigherVersion.");
1932 for (
const auto &extraTypeInfoDesc : srcDescriptor->GetExtraTypeInfoIterable())
1939 SKIP_OR_ABORT(std::string(
"Source RNTuple has an incompatible schema with the destination:\n") +
1940 descCmpRes.GetError()->GetReport())
1942 auto descCmp = descCmpRes.Unwrap();
1944 if (!doSlowMerge && !descCmp.fFastMergeErrors.empty()) {
1945 SKIP_OR_ABORT(std::string(
"Source RNTuple has an incompatible schema with the destination:\n") +
1946 descCmp.fFastMergeErrors)
1952 std::string msg =
"Source RNTuple is missing the following fields:";
1953 for (
const auto *field : descCmp.fExtraDstFields) {
1960 if (!descCmp.fExtraSrcFields.empty()) {
1963 auto res = strat->ExtendDestinationModel(descCmp);
1968 std::string msg =
"Source RNTuple has extra fields that the destination RNTuple doesn't have:";
1969 for (
const auto *field : descCmp.fExtraSrcFields) {
1976 const auto prevNEntries = strat->GetNDstEntries();
1978 auto res = strat->MergeSource(*source, descCmp);
1985 auto attrSetDataIt = attrSetsData.find(attrSetDesc.GetName());
1986 if (attrSetDataIt == attrSetsData.end()) {
1991 fDestination->CloneAsHidden(attrSetDesc.GetName(), sinkOpts).release()));
1992 auto [insertedIt, inserted] =
1993 attrSetsData.try_emplace(attrSetDesc.GetName(), std::move(sink), *strat, mergeOpts, prevNEntries);
1995 attrSetDataIt = insertedIt;
1997 auto &attrSetData = attrSetDataIt->second;
1999 const auto attrLink =
2002 auto &attrStrat = attrSetData.fStrategy;
2003 attrStrat.InitSource(*attrSource);
2006 attrSource->GetSharedDescriptorGuard().GetConstRef(),
2009 SKIP_OR_ABORT(std::string(
"Source RNTuple has an incompatible schema with the destination:\n") +
2010 descCmpRes.GetError()->GetReport())
2012 descCmp = descCmpRes.Unwrap();
2014 if (!descCmp.fExtraSrcFields.empty() || !descCmp.fExtraSrcSubfields.empty()) {
2015 auto edmRes = attrStrat.ExtendDestinationModel(descCmp);
2020 auto mergeSourceRes = attrStrat.MergeSource(*attrSource, descCmp);
2021 if (!mergeSourceRes)
2027 for (
auto &[attrSetName, attrSetData] : attrSetsData) {
2028 attrSetData.fStrategy.BeforeCommit();
2029 if (attrSetData.fSink->GetNEntries() > 0) {
2030 attrSetData.fSink->CommitClusterGroup();
2032 auto anchorLink = attrSetData.fSink->CommitDataset();
2033 fDestination->CommitAttributeSet(attrSetName, anchorLink);
2036 strat->BeforeCommit();
#define R__FORWARD_ERROR(res)
Short-hand to return an RResult<T> in an error state (i.e. after checking)
#define R__FORWARD_RESULT(res)
Short-hand to return an RResult<T> value from a subroutine to the calling stack frame.
#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 void VerifyProjectedFieldsCompatibility(const ROOT::RNTupleDescriptor &dst, const ROOT::RNTupleDescriptor &src, const ROOT::RFieldDescriptor &dstField, const ROOT::RFieldDescriptor &srcField, std::vector< std::string > &errors)
static std::optional< ENTupleMergeErrBehavior > ParseOptionErrBehavior(const TString &opts)
static ROOT::RResult< void > ExtendDestinationModel(RDescriptorsComparison &descCmp, ROOT::RNTupleModel &dstModel, ROOT::Internal::RPageSink &destination, const ROOT::RNTupleDescriptor &srcDesc, ROOT::NTupleSize_t nEntries, bool verbose, bool forSlowMerge)
static ROOT::RResult< RDescriptorsComparison > CompareDescriptorStructure(const ROOT::RNTupleDescriptor &dst, const ROOT::RNTupleDescriptor &src, bool forAttributeMerging=false)
Compares the top level fields of dst and src and determines whether they can be merged or not.
static ROOT::RLogChannel & NTupleMergeLog()
#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 ROOT::RResult< RDescriptorsComparison > FillCommonAndExtraFields(const ROOT::RNTupleDescriptor &dst, const ROOT::RNTupleDescriptor &src, bool forAttributeMerging)
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.
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(ENTupleColumnType onDiskType)
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.
ROOT::RResult< void > ExtendDestinationModel(RDescriptorsComparison &descCmp) final
ROOT::NTupleSize_t GetNDstEntries() const final
RNTupleMergeStrategySlow(std::unique_ptr< RNTupleModel > dstModel, ROOT::Internal::RPagePersistentSink &dst, const RNTupleMergeOptions &mergeOpts, std::unique_ptr< RNTupleMergeActions > actions=std::make_unique< RNTupleMergeActions >())
void InitSource(RPageSource &source) final
void BeforeCommit() final
RNTupleMergeActionContext fMergeContext
std::unique_ptr< RNTupleMergeActions > fActions
std::unique_ptr< ROOT::REntry > fDstEntry
ROOT::RResult< void > MergeSource(RPageSource &source, RDescriptorsComparison &descCmp) final
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 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.
const ROOT::RNTupleDescriptor & GetDescriptor() const final
Return the RNTupleDescriptor being constructed.
Abstract interface to write data into an ntuple.
void Init(RNTupleModel &model)
Physically creates the storage container to hold the ntuple (e.g., a keys a TFile or an S3 bucket) In...
virtual std::uint64_t CommitCluster(ROOT::NTupleSize_t nNewEntries)
Finalize the current cluster and create a new one for the following data.
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.
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.
const ROOT::RNTupleDescriptor & GetConstRef() const
Abstract interface to read data from an ntuple.
void Attach(ROOT::Internal::RNTupleSerializer::EDescriptorDeserializeMode mode=ROOT::Internal::RNTupleSerializer::EDescriptorDeserializeMode::kForReading)
Open the physical storage container and deserialize header and footer.
RSharedDescriptorGuard GetSharedDescriptorGuard() const
Takes the read lock for the descriptor.
virtual std::unique_ptr< RPageSource > OpenWithDifferentAnchor(const ROOT::Internal::RNTupleLink &anchorLink, const ROOT::RNTupleReadOptions &options={})=0
Creates a new PageSource using the same underlying file as this but referring to a different RNTuple,...
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
The REntry is a collection of values in an RNTuple corresponding to a complete row in the data set.
Base class for all ROOT issued exceptions.
A field translates read and write calls from/to underlying columns to/from tree values.
void SetColumnRepresentatives(const RColumnRepresentations::Selection_t &representatives)
Fixes a column representative.
void SetOnDiskId(ROOT::DescriptorId_t id)
std::vector< RFieldBase * > GetMutableSubfields()
std::string GetQualifiedFieldName() const
Returns the field name and parent field names separated by dots (grandparent.parent....
const std::string & GetFieldName() const
std::vector< ROOT::ENTupleColumnType > ColumnRepresentation_t
ROOT::DescriptorId_t GetOnDiskId() const
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
ROOT::NTupleSize_t R__DEPRECATED(6, 46, "This function is ill-conceived in the descriptor " "as not all cluster descriptors may be present. This interface is no longer publicly exposed.") GetNElements(ROOT ROOT::DescriptorId_t GetFieldZeroId() const
Returns the logical parent of all top-level RNTuple data fields.
ROOT::NTupleSize_t GetNEntries() const
We know the number of entries from adding the cluster summaries.
std::unique_ptr< ROOT::RNTupleModel > CreateModel(const RCreateModelOptions &options=RCreateModelOptions()) const
Re-create the C++ model from the stored metadata.
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::Experimental::RNTupleAttrSetDescriptorIterable GetAttrSetIterable() 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
constexpr const char * kMetaFieldNames[]
@ kMetaFieldIndex_RangeStart
void SetAllowFieldSubstitutions(RFieldZero &fieldZero, bool val)
ROOT::RFieldZero & GetFieldZeroOfModel(RNTupleModel &model)
void CallConnectPageSourceOnField(RFieldBase &, ROOT::Internal::RPageSource &)
std::unique_ptr< T[]> MakeUninitArray(std::size_t size)
Make an array of default-initialized elements.
@ kWarnOnHigherVersion
The merger will emit a warning when merging RNTuples with higher version than the latest supported by...
void CallFlushColumnsOnField(RFieldBase &)
@ 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.
void CallSetArtificialOnField(RFieldBase &)
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.
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
constexpr DescriptorId_t kInvalidDescriptorId
if(fPos !=fText.size()) Fail("unexpected trailing content")
Information regarding the current slow merge process that gets passed to the RNTupleMergeActions.
ROOT::NTupleSize_t fSourceEntryIndex
ROOT::NTupleSize_t fDestinationEntryIndex
Set of merging options to pass to RNTupleMerger.
ENTupleMergeVersionBehavior fVersionBehavior
Determines how the Merge function behaves depending on the RNTuple sources' version.
bool fExtraVerbose
If true, the merger will emit further diagnostics and information.
std::optional< std::uint32_t > fCompressionSettings
If fCompressionSettings is empty (the default), the merger will not change the compression of any of ...
ENTupleMergingMode fMergingMode
Determines how the merging treats sources with different models (.
ENTupleMergeErrBehavior fErrBehavior
Determines how the Merge function behaves upon merging errors.
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...
void SetEmulateUnknownTypes(bool v)
Modifiers passed to CreateModel()
void SetCreateBare(bool v)
void SetReconstructProjections(bool v)