diff --git a/src/engine/QueryPlanner.cpp b/src/engine/QueryPlanner.cpp index bb412a6cde..6048fe5bec 100644 --- a/src/engine/QueryPlanner.cpp +++ b/src/engine/QueryPlanner.cpp @@ -1827,6 +1827,13 @@ std::vector QueryPlanner::createJoinCandidates( return candidates; } + // If both sides are spatial joins, return immediately to prevent a regular + // join on the variables, which would lead to the spatial join never having + // children. + if (checkSpatialJoin(a, b) == std::pair{true, true}) { + return candidates; + } + if (a.type == SubtreePlan::MINUS) { AD_THROW( "MINUS can only appear after" @@ -1897,25 +1904,31 @@ std::vector QueryPlanner::createJoinCandidates( } // _____________________________________________________________________________ -auto QueryPlanner::createSpatialJoin( - const SubtreePlan& a, const SubtreePlan& b, - const std::vector>& jcs) - -> std::optional { +std::pair QueryPlanner::checkSpatialJoin(const SubtreePlan& a, + const SubtreePlan& b) { auto aIsSpatialJoin = std::dynamic_pointer_cast(a._qet->getRootOperation()); auto bIsSpatialJoin = std::dynamic_pointer_cast(b._qet->getRootOperation()); - auto aIs = static_cast(aIsSpatialJoin); - auto bIs = static_cast(bIsSpatialJoin); + return std::pair{static_cast(aIsSpatialJoin), + static_cast(bIsSpatialJoin)}; +} + +// _____________________________________________________________________________ +auto QueryPlanner::createSpatialJoin( + const SubtreePlan& a, const SubtreePlan& b, + const std::vector>& jcs) + -> std::optional { + auto [aIs, bIs] = checkSpatialJoin(a, b); // Exactly one of the inputs must be a SpatialJoin. if (aIs == bIs) { return std::nullopt; } - const SubtreePlan& spatialSubtreePlan = aIsSpatialJoin ? a : b; - const SubtreePlan& otherSubtreePlan = aIsSpatialJoin ? b : a; + const SubtreePlan& spatialSubtreePlan = aIs ? a : b; + const SubtreePlan& otherSubtreePlan = aIs ? b : a; std::shared_ptr op = spatialSubtreePlan._qet->getRootOperation(); auto spatialJoin = static_cast(op.get()); @@ -1929,7 +1942,7 @@ auto QueryPlanner::createSpatialJoin( "Currently, if both sides of a SpatialJoin are variables, then the" "SpatialJoin must be the only connection between these variables"); } - ColumnIndex ind = aIsSpatialJoin ? jcs[0][1] : jcs[0][0]; + ColumnIndex ind = aIs ? jcs[0][1] : jcs[0][0]; const Variable& var = otherSubtreePlan._qet->getVariableAndInfoByColumnIndex(ind).first; std::cout << spatialJoin->onlyForTestingGetTask().first << " " diff --git a/src/engine/QueryPlanner.h b/src/engine/QueryPlanner.h index b029b7d5a3..af5dff1aac 100644 --- a/src/engine/QueryPlanner.h +++ b/src/engine/QueryPlanner.h @@ -348,6 +348,10 @@ class QueryPlanner { const SubtreePlan& a, const SubtreePlan& b, const std::vector>& jcs); + // Helper to check if subtree plans are spatial join + [[nodiscard]] static std::pair checkSpatialJoin( + const SubtreePlan& a, const SubtreePlan& b); + // if one of the inputs is a spatial join which is compatible with the other // input, then add that other input to the spatial join as a child instead of // creating a normal join. diff --git a/src/engine/SpatialJoin.cpp b/src/engine/SpatialJoin.cpp index ca5b1a2820..0ae1b0d33c 100644 --- a/src/engine/SpatialJoin.cpp +++ b/src/engine/SpatialJoin.cpp @@ -87,11 +87,14 @@ std::optional SpatialJoin::getMaxResults() const { // ____________________________________________________________________________ std::vector SpatialJoin::getChildren() { - if (!(childLeft_ && childRight_)) { - AD_THROW("SpatialJoin needs two children, but at least one is missing"); + std::vector result; + if (childLeft_) { + result.push_back(childLeft_.get()); } - - return {childLeft_.get(), childRight_.get()}; + if (childRight_) { + result.push_back(childRight_.get()); + } + return result; } // ____________________________________________________________________________