diff --git a/tree/dataframe/inc/ROOT/RDF/RLoopManager.hxx b/tree/dataframe/inc/ROOT/RDF/RLoopManager.hxx index a01c63a8e3bfde7ab23db77d1e356d08941a47b8..86bf32c3e06bd7043d97a19f59bb623bef0c0982 100644 --- a/tree/dataframe/inc/ROOT/RDF/RLoopManager.hxx +++ b/tree/dataframe/inc/ROOT/RDF/RLoopManager.hxx @@ -123,6 +123,7 @@ class RLoopManager : public RNodeBase { /// Cache of the tree/chain branch names. Never access directy, always use GetBranchNames(). ColumnNames_t fValidBranchNames; + void CheckIndexedFriends(); void RunEmptySourceMT(); void RunEmptySource(); void RunTreeProcessorMT(); diff --git a/tree/dataframe/src/RLoopManager.cxx b/tree/dataframe/src/RLoopManager.cxx index cc875a8a08af2e01b570d12c6d3657cb229a61b1..22f02aad3cc8a988f834bda5fc53ed9ca2ddce9f 100644 --- a/tree/dataframe/src/RLoopManager.cxx +++ b/tree/dataframe/src/RLoopManager.cxx @@ -207,6 +207,25 @@ RLoopManager::RLoopManager(std::unique_ptr<RDataSource> ds, const ColumnNames_t fDataSource->SetNSlots(fNSlots); } +// ROOT-9559: we cannot handle indexed friends +void RLoopManager::CheckIndexedFriends() +{ + auto friends = fTree->GetListOfFriends(); + if (!friends) + return; + for (auto friendElObj : *friends) { + auto friendEl = static_cast<TFriendElement *>(friendElObj); + auto friendTree = friendEl->GetTree(); + if (friendTree && friendTree->GetTreeIndex()) { + std::string err = fTree->GetName(); + err += " has a friend, \""; + err += friendTree->GetName(); + err += "\", which has an index. This is not supported."; + throw std::runtime_error(err); + } + } +} + /// Run event loop with no source files, in parallel. void RLoopManager::RunEmptySourceMT() { @@ -259,6 +278,7 @@ void RLoopManager::RunEmptySource() void RLoopManager::RunTreeProcessorMT() { #ifdef R__USE_IMT + CheckIndexedFriends(); RSlotStack slotStack(fNSlots); const auto &entryList = fTree->GetEntryList() ? *fTree->GetEntryList() : TEntryList(); auto tp = std::make_unique<ROOT::TTreeProcessorMT>(*fTree, entryList); @@ -284,6 +304,7 @@ void RLoopManager::RunTreeProcessorMT() /// Run event loop over one or multiple ROOT files, in sequence. void RLoopManager::RunTreeReader() { + CheckIndexedFriends(); TTreeReader r(fTree.get(), fTree->GetEntryList()); if (0 == fTree->GetEntriesFast()) return; diff --git a/tree/dataframe/test/dataframe_friends.cxx b/tree/dataframe/test/dataframe_friends.cxx index 685d86f33cd458fd4940d620aee640257938b9c4..ab7639c71a874ac3c4fc2913fab9e128b70d5d39 100644 --- a/tree/dataframe/test/dataframe_friends.cxx +++ b/tree/dataframe/test/dataframe_friends.cxx @@ -211,4 +211,76 @@ TEST_F(RDFAndFriends, FriendChainMT) ROOT::DisableImplicitMT(); } +// ROOT-9559 +void FillIndexedFriend(const char *mainfile, const char *auxfile) +{ + // Start by creating main Tree + TFile f(mainfile, "RECREATE"); + TTree mainTree("mainTree", "mainTree"); + int idx; + mainTree.Branch("idx", &idx); + float x; + mainTree.Branch("x", &x); + + idx = 1; + x = 0.5; + mainTree.Fill(); + idx = 1; + x = 2; + mainTree.Fill(); + idx = 1; + x = 5; + mainTree.Fill(); + idx = 2; + x = 1; + mainTree.Fill(); + idx = 2; + x = 8; + mainTree.Fill(); + mainTree.Write(); + f.Close(); + + // And aux tree + TFile f2(auxfile, "RECREATE"); + TTree auxTree("auxTree", "auxTree"); + auxTree.Branch("idx", &idx); + float y; + auxTree.Branch("y", &y); + idx = 1; + y = 5; + auxTree.Fill(); + idx = 2; + y = 7; + auxTree.Fill(); + auxTree.Write(); + f2.Close(); +} + +TEST(RDFAndFriendsNoFixture, IndexedFriend) +{ + auto mainFile = "IndexedFriend_main.root"; + auto auxFile = "IndexedFriend_aux.root"; + FillIndexedFriend(mainFile, auxFile); + + TChain mainChain("mainTree", "mainTree"); + mainChain.Add(mainFile); + TChain auxChain("auxTree", "auxTree"); + auxChain.Add(auxFile); + + auxChain.BuildIndex("idx"); + mainChain.AddFriend(&auxChain); + + int ret = 1; + try { + auto df = ROOT::RDataFrame(mainChain); + *df.Min<int>("x"); + } catch (const std::runtime_error &) { + ret = 0; + } + EXPECT_EQ(0, ret); + + gSystem->Unlink(mainFile); + gSystem->Unlink(auxFile); +} + #endif // R__USE_IMT