@@ -72,6 +72,9 @@ final class LmdbSketchJoinOptimizer implements QueryOptimizer {
7272
7373 private static final double FINITE_BINDING_GUARD_PRODUCT_LIMIT = 5000.0d ;
7474 private static final int CORRELATED_ANTI_JOIN_COMPLEX_SUFFIX_PATTERN_LIMIT = 3 ;
75+ private static final String FINITE_ANCHOR_PLANNER_ID = "lmdb-finite-anchor" ;
76+ private static final String FINITE_ANCHOR_PLANNER_PATH = "CANONICAL_FINITE_ANCHOR" ;
77+ private static final String LMDB_PHYSICAL_REFINEMENT = "costModel=lmdb, accessPathSelection=per-step" ;
7578 private final EvaluationStatistics statistics ;
7679 private final boolean trackResultSize ;
7780
@@ -1330,6 +1333,7 @@ private boolean relocateLeftJoinLocalFilters(Filter filter, LeftJoin leftJoin) {
13301333 private TupleExpr buildOrderedRoot (CollectedJoinArgs collected , Set <String > outerBoundVars ) {
13311334 rewriteSmallLiteralDeferredFilterAnchors (collected );
13321335 deduplicateEquivalentBindingSetAssignments (collected .joinArgs );
1336+ moveUnionIndependentFiniteSuffixAfterScopedUnion (collected .joinArgs , outerBoundVars );
13331337 Set <String > scopeBindingNames = new HashSet <>(outerBoundVars );
13341338 for (TupleExpr joinArg : collected .joinArgs ) {
13351339 scopeBindingNames .addAll (joinArg .getBindingNames ());
@@ -1347,6 +1351,7 @@ private TupleExpr buildOrderedRoot(CollectedJoinArgs collected, Set<String> oute
13471351 LmdbSmallLiteralFilterAnchors .add (collected .joinArgs , filters , outerBoundVars );
13481352 deduplicateEquivalentBindingSetAssignments (collected .joinArgs );
13491353 splitCartesianBindingSetAssignments (collected .joinArgs );
1354+ moveUnionIndependentFiniteSuffixAfterScopedUnion (collected .joinArgs , outerBoundVars );
13501355 List <TupleExpr > roots = new ArrayList <>();
13511356 List <TupleExpr > currentSegment = new ArrayList <>();
13521357 List <DelayedExtension > delayedExtensions = new ArrayList <>();
@@ -1391,6 +1396,84 @@ && shouldPlaceBindingOnlySegmentAfterSeparator(currentSegment, optimizedSeparato
13911396 return root ;
13921397 }
13931398
1399+ private void moveUnionIndependentFiniteSuffixAfterScopedUnion (List <TupleExpr > joinArgs ,
1400+ Set <String > outerBoundVars ) {
1401+ int unionIndex = singleScopedUnionIndex (joinArgs );
1402+ if (unionIndex <= 1 ) {
1403+ return ;
1404+ }
1405+ TupleExpr union = joinArgs .get (unionIndex );
1406+ Set <String > unionBindings = plannerBindingNames (union .getBindingNames ());
1407+ if (unionBindings .isEmpty ()) {
1408+ return ;
1409+ }
1410+ int suffixStart = unionIndependentFiniteSuffixStart (joinArgs , unionIndex , unionBindings , outerBoundVars );
1411+ if (suffixStart < 0 ) {
1412+ return ;
1413+ }
1414+ List <TupleExpr > suffix = new ArrayList <>(joinArgs .subList (suffixStart , unionIndex ));
1415+ joinArgs .subList (suffixStart , unionIndex ).clear ();
1416+ joinArgs .addAll (suffixStart + 1 , suffix );
1417+ }
1418+
1419+ private int singleScopedUnionIndex (List <TupleExpr > joinArgs ) {
1420+ int unionIndex = -1 ;
1421+ for (int i = 0 ; i < joinArgs .size (); i ++) {
1422+ TupleExpr joinArg = joinArgs .get (i );
1423+ if (!(joinArg instanceof Union ) || !TupleExprs .isVariableScopeChange (joinArg )) {
1424+ continue ;
1425+ }
1426+ if (unionIndex >= 0 ) {
1427+ return -1 ;
1428+ }
1429+ unionIndex = i ;
1430+ }
1431+ return unionIndex ;
1432+ }
1433+
1434+ private int unionIndependentFiniteSuffixStart (List <TupleExpr > joinArgs , int unionIndex ,
1435+ Set <String > unionBindings , Set <String > outerBoundVars ) {
1436+ Set <String > prefixBindings = new HashSet <>(outerBoundVars );
1437+ boolean hasFiniteUnionAnchor = false ;
1438+ for (int i = 0 ; i < unionIndex - 1 ; i ++) {
1439+ TupleExpr prefixFactor = joinArgs .get (i );
1440+ Optional <Set <String >> assignmentNames = LmdbJoinPlanSupport
1441+ .positionableBindingSetAssignmentNames (prefixFactor );
1442+ if (assignmentNames .isPresent () && !Collections .disjoint (assignmentNames .get (), unionBindings )) {
1443+ hasFiniteUnionAnchor = true ;
1444+ }
1445+ prefixBindings .addAll (plannerBindingNames (prefixFactor .getBindingNames ()));
1446+ if (hasFiniteUnionAnchor
1447+ && canMoveFiniteSuffixAfterUnion (joinArgs .subList (i + 1 , unionIndex ), prefixBindings ,
1448+ unionBindings )) {
1449+ return i + 1 ;
1450+ }
1451+ }
1452+ return -1 ;
1453+ }
1454+
1455+ private boolean canMoveFiniteSuffixAfterUnion (List <TupleExpr > suffix , Set <String > prefixBindings ,
1456+ Set <String > unionBindings ) {
1457+ if (suffix .isEmpty ()) {
1458+ return false ;
1459+ }
1460+ Set <String > availableBindings = new HashSet <>(prefixBindings );
1461+ for (TupleExpr factor : suffix ) {
1462+ if (TupleExprs .isVariableScopeChange (factor )
1463+ || !(factor instanceof BindingSetAssignment || factor instanceof StatementPattern )) {
1464+ return false ;
1465+ }
1466+ Set <String > factorBindings = plannerBindingNames (factor .getBindingNames ());
1467+ Set <String > introducedBindings = new HashSet <>(factorBindings );
1468+ introducedBindings .removeAll (availableBindings );
1469+ if (!Collections .disjoint (introducedBindings , unionBindings )) {
1470+ return false ;
1471+ }
1472+ availableBindings .addAll (factorBindings );
1473+ }
1474+ return true ;
1475+ }
1476+
13941477 private DelayedExtension delayableIndependentExtension (TupleExpr joinArg , List <TupleExpr > joinArgs , int index ,
13951478 List <DeferredFilter > filters ) {
13961479 if (!(joinArg instanceof Extension extension ) || TupleExprs .isVariableScopeChange (extension )) {
@@ -1729,8 +1812,7 @@ private void appendSegmentRoot(List<TupleExpr> roots, List<TupleExpr> segment, L
17291812 LmdbJoinPlanSupport .markRedundantRequiredExistsFilters (segment , filters );
17301813 List <DeferredFilter > segmentFilters = new ArrayList <>();
17311814 for (DeferredFilter filter : filters ) {
1732- if (!filter .applied && segmentBindings .containsAll (filter .requiredVars )
1733- && !Collections .disjoint (segmentLocalBindings , filter .requiredVars )) {
1815+ if (!filter .applied && segmentLocalBindings .containsAll (filter .requiredVars )) {
17341816 segmentFilters .add (filter );
17351817 }
17361818 }
@@ -1782,7 +1864,9 @@ private OrderedSegment orderSegment(List<TupleExpr> segment, Set<String> boundBe
17821864 Optional <List <TupleExpr >> canonicalFiniteAnchorOrder = canonicalFiniteAnchorOrder (segment ,
17831865 boundBeforeSegment , plannerFilters );
17841866 if (canonicalFiniteAnchorOrder .isPresent ()) {
1785- return new OrderedSegment (new ArrayDeque <>(canonicalFiniteAnchorOrder .get ()), Map .of (), true );
1867+ List <TupleExpr > orderedArgs = canonicalFiniteAnchorOrder .get ();
1868+ applyFiniteAnchorPlannerMetrics (orderedArgs );
1869+ return new OrderedSegment (new ArrayDeque <>(orderedArgs ), Map .of (), true );
17861870 }
17871871 }
17881872 JoinOrderPlanner planner = (JoinOrderPlanner ) statistics ;
@@ -1795,7 +1879,9 @@ private OrderedSegment orderSegment(List<TupleExpr> segment, Set<String> boundBe
17951879 Optional <List <TupleExpr >> canonicalFiniteAnchorOrder = canonicalFiniteAnchorOrder (segment ,
17961880 boundBeforeSegment , plannerFilters );
17971881 if (canonicalFiniteAnchorOrder .isPresent ()) {
1798- return new OrderedSegment (new ArrayDeque <>(canonicalFiniteAnchorOrder .get ()), Map .of (), true );
1882+ List <TupleExpr > orderedArgs = canonicalFiniteAnchorOrder .get ();
1883+ applyFiniteAnchorPlannerMetrics (orderedArgs );
1884+ return new OrderedSegment (new ArrayDeque <>(orderedArgs ), Map .of (), true );
17991885 }
18001886 }
18011887 return locallySelectiveFallbackOrder (segment );
@@ -2293,6 +2379,17 @@ private void applyPlannerStepEstimates(JoinOrderPlanner.JoinOrderPlan plan) {
22932379 }
22942380 }
22952381
2382+ private void applyFiniteAnchorPlannerMetrics (List <TupleExpr > orderedArgs ) {
2383+ if (orderedArgs .isEmpty ()) {
2384+ return ;
2385+ }
2386+ TupleExpr root = orderedArgs .get (0 );
2387+ root .setStringMetricPlanned (TelemetryMetricNames .PLANNER_ID , FINITE_ANCHOR_PLANNER_ID );
2388+ root .setStringMetricPlanned (TelemetryMetricNames .PLANNER_PATH , FINITE_ANCHOR_PLANNER_PATH );
2389+ root .setStringMetricPlanned (TelemetryMetricNames .OPTIMIZER_PHYSICAL_REFINEMENT ,
2390+ LMDB_PHYSICAL_REFINEMENT );
2391+ }
2392+
22962393 private final class AntiJoinCost {
22972394 private final double workRows ;
22982395 private final double outputRows ;
0 commit comments