@@ -2729,6 +2729,93 @@ TEST(RNTupleMerger, MergeDeferredAdvanced)
27292729 }
27302730}
27312731
2732+ TEST (RNTupleMerger, MergeDeferredAdvanced2)
2733+ {
2734+ // Like MergeDeferredAdvanced, but make sure the fields have different column types so that the merging produces
2735+ // a late-extended column.
2736+ FileRaii fileGuard1 (" test_ntuple_merge_deferred_adv2_in_1.root" );
2737+ FileRaii fileGuard2 (" test_ntuple_merge_deferred_adv2_in_2.root" );
2738+
2739+ // First RNTuple with late model extended field "flt" (column is not deferred because it's still entry 0)
2740+ {
2741+ auto model1 = RNTupleModel::Create ();
2742+ auto wopts = RNTupleWriteOptions ();
2743+ wopts.SetCompression (0 );
2744+ auto tfile = TFile::Open (fileGuard1.GetPath ().c_str (), " RECREATE" );
2745+ auto writer1 = RNTupleWriter::Append (std::move (model1), " ntuple" , *tfile, wopts);
2746+ auto updater = writer1->CreateModelUpdater ();
2747+ auto field = std::make_unique<RField<float >>(" flt" );
2748+ field->SetHalfPrecision ();
2749+ updater->BeginUpdate ();
2750+ updater->AddField (std::move (field));
2751+ updater->CommitUpdate ();
2752+ auto pFlt1 = writer1->GetModel ().GetDefaultEntry ().GetPtr <float >(" flt" );
2753+ for (int i = 0 ; i < 10 ; ++i) {
2754+ *pFlt1 = i;
2755+ writer1->Fill ();
2756+ }
2757+ }
2758+
2759+ // Second RNTuple with late model extended field "flt"
2760+ {
2761+ auto model2 = RNTupleModel::Create ();
2762+ // Add a non-late model extended field so we can write some entries before we extend the model and obtain
2763+ // actual deferred columns in the extension header.
2764+ auto pInt = model2->MakeField <int >(" int" );
2765+ auto wopts = RNTupleWriteOptions ();
2766+ wopts.SetCompression (0 );
2767+ auto writer2 = RNTupleWriter::Recreate (std::move (model2), " ntuple" , fileGuard2.GetPath (), wopts);
2768+ for (int i = 0 ; i < 5 ; ++i) {
2769+ *pInt = 10 + i;
2770+ writer2->Fill ();
2771+ }
2772+ auto updater = writer2->CreateModelUpdater ();
2773+ auto field = std::make_unique<RField<float >>(" flt" );
2774+ field->SetColumnRepresentatives ({{ROOT ::ENTupleColumnType::kReal32 }});
2775+ updater->BeginUpdate ();
2776+ updater->AddField (std::move (field));
2777+ updater->CommitUpdate ();
2778+ auto pFlt2 = writer2->GetModel ().GetDefaultEntry ().GetPtr <float >(" flt" );
2779+ for (int i = 5 ; i < 10 ; ++i) {
2780+ *pInt = 10 + i;
2781+ *pFlt2 = 10 + i;
2782+ writer2->Fill ();
2783+ }
2784+ }
2785+
2786+ // Now merge them
2787+ std::vector<std::unique_ptr<RPageSource>> sources;
2788+ sources.push_back (RPageSource::Create (" ntuple" , fileGuard1.GetPath (), RNTupleReadOptions ()));
2789+ sources.push_back (RPageSource::Create (" ntuple" , fileGuard2.GetPath (), RNTupleReadOptions ()));
2790+ std::vector<RPageSource *> sourcePtrs;
2791+ for (const auto &s : sources) {
2792+ sourcePtrs.push_back (s.get ());
2793+ }
2794+
2795+ FileRaii fileGuardOut (" test_ntuple_merge_deferred_adv2_out.root" );
2796+ auto wopts = RNTupleWriteOptions ();
2797+ wopts.SetCompression (0 );
2798+ auto destination = std::make_unique<RPageSinkFile>(" ntuple" , fileGuardOut.GetPath (), wopts);
2799+ RNTupleMerger merger{std::move (destination)};
2800+ auto opts = RNTupleMergeOptions ();
2801+ opts.fMergingMode = ENTupleMergingMode::kUnion ;
2802+ auto res = merger.Merge (sourcePtrs, opts);
2803+ ASSERT_TRUE (bool (res));
2804+
2805+ auto reader = RNTupleReader::Open (" ntuple" , fileGuardOut.GetPath ());
2806+ EXPECT_EQ (reader->GetNEntries (), 20 );
2807+
2808+ auto pInt = reader->GetModel ().GetDefaultEntry ().GetPtr <int >(" int" );
2809+ auto pFlt = reader->GetModel ().GetDefaultEntry ().GetPtr <float >(" flt" );
2810+ for (auto i : reader->GetEntryRange ()) {
2811+ reader->LoadEntry (i);
2812+ float expectedFlt = (i >= 10 && i < 15 ) ? 0 : i;
2813+ EXPECT_FLOAT_EQ (*pFlt, expectedFlt);
2814+ int expectedInt = (i >= 10 && i < 20 ) * i;
2815+ EXPECT_EQ (*pInt, expectedInt);
2816+ }
2817+ }
2818+
27322819TEST (RNTupleMerger, MergeIncrementalLMExt)
27332820{
27342821 // Create the input files:
0 commit comments