3434import org .eclipse .rdf4j .model .Statement ;
3535import org .eclipse .rdf4j .model .Value ;
3636import org .eclipse .rdf4j .model .ValueFactory ;
37+ import org .eclipse .rdf4j .model .base .CoreDatatype ;
3738import org .eclipse .rdf4j .model .datatypes .XMLDatatypeUtil ;
3839import org .eclipse .rdf4j .model .vocabulary .FN ;
3940import org .eclipse .rdf4j .model .vocabulary .XMLSchema ;
@@ -193,6 +194,7 @@ protected class JoinVisitor extends AbstractSimpleQueryModelVisitor<RuntimeExcep
193194 private Set <String > boundVars = new HashSet <>();
194195 private double currentHighestCost = 1 ;
195196 private final Map <TupleExpr , Double > plannedPrefixRowsByRightArg = new IdentityHashMap <>();
197+ private final Set <TupleExpr > plannerOrderedArgs = Collections .newSetFromMap (new IdentityHashMap <>());
196198 private int suppressedSmallLiteralFilterAnchors ;
197199
198200 protected JoinVisitor () {
@@ -723,10 +725,12 @@ private boolean isUnsafeCoreInEqualityValue(Value value) {
723725 if (!(value instanceof Literal )) {
724726 return false ;
725727 }
726- IRI datatype = ((Literal ) value ).getDatatype ();
727- return XMLDatatypeUtil .isNumericDatatype (datatype )
728- || XMLSchema .BOOLEAN .equals (datatype )
729- || XMLSchema .DATETIME .equals (datatype );
728+ CoreDatatype coreDatatype = ((Literal ) value ).getCoreDatatype ();
729+ if (coreDatatype .isXSDDatatype ()){
730+ CoreDatatype .XSD xsdDatatype = coreDatatype .asXSDDatatypeOrNull ();
731+ return xsdDatatype .isNumericDatatype () || xsdDatatype .isCalendarDatatype () || xsdDatatype == CoreDatatype .XSD .BOOLEAN ;
732+ }
733+ return false ;
730734 }
731735
732736 private RawJoinSegment findAnchorSegment (List <Object > rawPlanItems , DeferredFilter deferredFilter ,
@@ -1142,6 +1146,7 @@ private TupleExpr buildJoinRoot(Deque<TupleExpr> orderedJoinArgs) {
11421146
11431147 Deque <TupleExpr > remaining = new ArrayDeque <>(orderedJoinArgs );
11441148 Map <TupleExpr , Double > plannedPrefixRowsForArgs = plannedPrefixRowsForArgs (orderedJoinArgs );
1149+ boolean plannerProvidedSegment = plannerProvidedSegment (orderedJoinArgs );
11451150
11461151 if (remaining .size () > 1 ) {
11471152 double cardinality = 0 ;
@@ -1167,7 +1172,7 @@ private TupleExpr buildJoinRoot(Deque<TupleExpr> orderedJoinArgs) {
11671172 cardinality = Math .max (cardinality , left .getResultSizeEstimate ());
11681173 cardinality = Math .max (cardinality , right .getResultSizeEstimate ());
11691174 Join join = createJoinWithEstimatedResultSize (left , right ,
1170- plannedPrefixRowsForArgs .remove (right ));
1175+ plannedPrefixRowsForArgs .remove (right ), plannerProvidedSegment );
11711176 join .setOrder ((Var ) supportedOrders .toArray ()[0 ]);
11721177 join .setMergeJoin (true );
11731178 remaining .addFirst (join );
@@ -1181,7 +1186,7 @@ private TupleExpr buildJoinRoot(Deque<TupleExpr> orderedJoinArgs) {
11811186 supportedOrders .retainAll (next .getSupportedOrders (tripleSource ));
11821187
11831188 Join join = createJoinWithEstimatedResultSize (root , next ,
1184- plannedPrefixRowsForArgs .remove (next ));
1189+ plannedPrefixRowsForArgs .remove (next ), plannerProvidedSegment );
11851190 if (USE_MERGE_JOIN_FOR_LAST_STATEMENT_PATTERNS_WHEN_CROSS_JOIN ) {
11861191 mergeJoinForCrossJoin (remaining , supportedOrders , root , next , join );
11871192 }
@@ -1190,12 +1195,25 @@ private TupleExpr buildJoinRoot(Deque<TupleExpr> orderedJoinArgs) {
11901195
11911196 while (!remaining .isEmpty ()) {
11921197 TupleExpr next = remaining .removeFirst ();
1193- root = createJoinWithEstimatedResultSize (root , next , plannedPrefixRowsForArgs .remove (next ));
1198+ root = createJoinWithEstimatedResultSize (root , next , plannedPrefixRowsForArgs .remove (next ),
1199+ plannerProvidedSegment );
11941200 }
11951201
11961202 return root ;
11971203 }
11981204
1205+ private boolean plannerProvidedSegment (Deque <TupleExpr > orderedJoinArgs ) {
1206+ if (plannerOrderedArgs .isEmpty ()) {
1207+ return false ;
1208+ }
1209+ for (TupleExpr orderedJoinArg : orderedJoinArgs ) {
1210+ if (!plannerOrderedArgs .contains (orderedJoinArg )) {
1211+ return false ;
1212+ }
1213+ }
1214+ return !orderedJoinArgs .isEmpty ();
1215+ }
1216+
11991217 private Map <TupleExpr , Double > plannedPrefixRowsForArgs (Deque <TupleExpr > orderedJoinArgs ) {
12001218 if (plannedPrefixRowsByRightArg .isEmpty () || orderedJoinArgs .size () < 2 ) {
12011219 return new IdentityHashMap <>();
@@ -1278,6 +1296,7 @@ private Deque<TupleExpr> optimizeJoinGroup(List<TupleExpr> joinGroup, List<Defer
12781296 JoinOrderPlanner .JoinOrderPlan plan = planningAttempt .getPlan ().get ();
12791297 Deque <TupleExpr > plannedJoinArgs = new ArrayDeque <>(plan .getOrderedArgs ());
12801298 Deque <TupleExpr > normalizedJoinArgs = positionBindingSetAssignments (plannedJoinArgs );
1299+ markPlannerOrderedArgs (normalizedJoinArgs );
12811300 if (sameIdentityOrder (plan .getOrderedArgs (), new ArrayList <>(normalizedJoinArgs ))) {
12821301 applyPlannerStepEstimates (plan );
12831302 } else {
@@ -1333,6 +1352,12 @@ private JoinOrderPlanner.PlanningAttempt planSegmentWithJoinOrderPlanner(List<Tu
13331352 return attempt ;
13341353 }
13351354
1355+ private void markPlannerOrderedArgs (Deque <TupleExpr > orderedArgs ) {
1356+ for (TupleExpr orderedArg : orderedArgs ) {
1357+ plannerOrderedArgs .add (orderedArg );
1358+ }
1359+ }
1360+
13361361 private JoinOrderPlanner .Algorithm plannerAlgorithm (int segmentSize ) {
13371362 if (JOIN_ORDER_STRATEGY == JoinOrderStrategy .DYNAMIC_PROGRAMMING ) {
13381363 return JoinOrderPlanner .Algorithm .DYNAMIC_PROGRAMMING ;
@@ -1547,9 +1572,19 @@ private Join createJoinWithEstimatedResultSize(TupleExpr left, TupleExpr right)
15471572 }
15481573
15491574 private Join createJoinWithEstimatedResultSize (TupleExpr left , TupleExpr right , Double plannedResultSize ) {
1575+ return createJoinWithEstimatedResultSize (left , right , plannedResultSize , false );
1576+ }
1577+
1578+ private Join createJoinWithEstimatedResultSize (TupleExpr left , TupleExpr right , Double plannedResultSize ,
1579+ boolean plannerProvidedSegment ) {
15501580 Join join = new Join (left , right );
15511581 if (plannedResultSize != null && isFiniteNonNegative (plannedResultSize )) {
15521582 join .setResultSizeEstimate (Math .max (join .getResultSizeEstimate (), plannedResultSize ));
1583+ } else if (plannerProvidedSegment ) {
1584+ double existingEstimate = maxFiniteResultSizeEstimate (left , right );
1585+ if (isFiniteNonNegative (existingEstimate )) {
1586+ join .setResultSizeEstimate (Math .max (join .getResultSizeEstimate (), existingEstimate ));
1587+ }
15531588 } else if (statistics .supportsJoinEstimation ()) {
15541589 double estimatedResultSize = statistics .getCardinality (join );
15551590 if (!Double .isNaN (estimatedResultSize ) && estimatedResultSize >= 0 ) {
@@ -1563,6 +1598,21 @@ private Join createJoinWithEstimatedResultSize(TupleExpr left, TupleExpr right,
15631598 return join ;
15641599 }
15651600
1601+ private double maxFiniteResultSizeEstimate (TupleExpr left , TupleExpr right ) {
1602+ double leftEstimate = left .getResultSizeEstimate ();
1603+ double rightEstimate = right .getResultSizeEstimate ();
1604+ if (isFiniteNonNegative (leftEstimate ) && isFiniteNonNegative (rightEstimate )) {
1605+ return Math .max (leftEstimate , rightEstimate );
1606+ }
1607+ if (isFiniteNonNegative (leftEstimate )) {
1608+ return leftEstimate ;
1609+ }
1610+ if (isFiniteNonNegative (rightEstimate )) {
1611+ return rightEstimate ;
1612+ }
1613+ return Double .NaN ;
1614+ }
1615+
15661616 private void copyOptimizerAnnotations (TupleExpr source , TupleExpr target ) {
15671617 for (Map .Entry <String , Long > entry : source .getLongMetricsActual ().entrySet ()) {
15681618 if (TelemetryMetricNames .isOptimizerMetric (entry .getKey ())) {
0 commit comments