Skip to content

Commit 25640d3

Browse files
authored
fix explain cross join (#5725)
1 parent 865a1e1 commit 25640d3

64 files changed

Lines changed: 1412 additions & 84 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

core/pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,19 @@
4040
<artifactId>maven-surefire-plugin</artifactId>
4141
<configuration>
4242
<groups>slow</groups>
43+
<properties>
44+
<includeTags>slow</includeTags>
45+
</properties>
4346
</configuration>
4447
</plugin>
4548
<plugin>
4649
<groupId>org.apache.maven.plugins</groupId>
4750
<artifactId>maven-failsafe-plugin</artifactId>
4851
<configuration>
4952
<groups>slow</groups>
53+
<properties>
54+
<includeTags>slow</includeTags>
55+
</properties>
5056
</configuration>
5157
</plugin>
5258
</plugins>
@@ -65,13 +71,19 @@
6571
<artifactId>maven-surefire-plugin</artifactId>
6672
<configuration>
6773
<excludedGroups>slow</excludedGroups>
74+
<properties>
75+
<excludeTags>slow</excludeTags>
76+
</properties>
6877
</configuration>
6978
</plugin>
7079
<plugin>
7180
<groupId>org.apache.maven.plugins</groupId>
7281
<artifactId>maven-failsafe-plugin</artifactId>
7382
<configuration>
7483
<excludedGroups>slow</excludedGroups>
84+
<properties>
85+
<excludeTags>slow</excludeTags>
86+
</properties>
7587
</configuration>
7688
</plugin>
7789
</plugins>

core/query/src/main/java/org/eclipse/rdf4j/query/explanation/GenericPlanNode.java

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import java.util.ArrayList;
1515
import java.util.Arrays;
1616
import java.util.Collections;
17+
import java.util.Comparator;
1718
import java.util.LinkedHashMap;
1819
import java.util.List;
1920
import java.util.Map;
@@ -169,7 +170,8 @@ public void setType(String type) {
169170
}
170171

171172
public List<GenericPlanNode> getPlans() {
172-
return plans.isEmpty() ? null : plans; // for simplified json
173+
List<GenericPlanNode> orderedPlans = orderedPlansForDisplay();
174+
return orderedPlans.isEmpty() ? null : orderedPlans; // for simplified json
173175
}
174176

175177
public void setPlans(List<GenericPlanNode> plans) {
@@ -575,16 +577,18 @@ public void setAlgorithm(String algorithm) {
575577
*/
576578
@Override
577579
public String toString() {
578-
return getHumanReadable(0);
580+
return getHumanReadable(0, isProjectionElemListNode());
579581
}
580582

581583
/**
582584
* @param prettyBoxDrawingType for deciding if we should use single or double walled character for drawing the
583585
* connectors between nodes in the query plan. Eg. ├ or ╠ and ─ o
586+
* @param ordered
584587
* @return
585588
*/
586-
private String getHumanReadable(int prettyBoxDrawingType) {
589+
private String getHumanReadable(int prettyBoxDrawingType, boolean ordered) {
587590
StringBuilder sb = new StringBuilder();
591+
List<GenericPlanNode> displayPlans = ordered ? orderedPlansForDisplay() : plans;
588592

589593
if (timedOut != null && timedOut) {
590594
sb.append("Timed out while retrieving explanation! Explanation may be incomplete!").append(newLine);
@@ -607,7 +611,7 @@ private String getHumanReadable(int prettyBoxDrawingType) {
607611
// we use box-drawing characters to "group" nodes in the plan visually when there are exactly two child plans
608612
// and
609613
// the child plans contain child plans
610-
if (plans.size() == 2 && plans.stream().anyMatch(p -> !p.plans.isEmpty())) {
614+
if (displayPlans.size() == 2 && displayPlans.stream().anyMatch(p -> !p.plans.isEmpty())) {
611615

612616
String start;
613617
String horizontal;
@@ -626,8 +630,8 @@ private String getHumanReadable(int prettyBoxDrawingType) {
626630
end = "└";
627631
}
628632

629-
String left = plans.get(0).getHumanReadable(prettyBoxDrawingType + 1);
630-
String right = plans.get(1).getHumanReadable(prettyBoxDrawingType + 1);
633+
String left = displayPlans.get(0).getHumanReadable(prettyBoxDrawingType + 1, false);
634+
String right = displayPlans.get(1).getHumanReadable(prettyBoxDrawingType + 1, false);
631635
boolean join = type.contains("Join");
632636

633637
{
@@ -655,10 +659,14 @@ private String getHumanReadable(int prettyBoxDrawingType) {
655659

656660
} else {
657661

658-
for (int i = 0; i < plans.size(); i++) {
659-
GenericPlanNode child = plans.get(i);
662+
for (int i = 0; i < displayPlans.size(); i++) {
663+
GenericPlanNode child = displayPlans.get(i);
660664
int j = i;
661-
sb.append(Arrays.stream(child.getHumanReadable(prettyBoxDrawingType + 1).split(newLine))
665+
sb.append(Arrays
666+
.stream(child
667+
.getHumanReadable(prettyBoxDrawingType + 1,
668+
child.isProjectionElemListNode() && !isMultiProjectionNode())
669+
.split(newLine))
662670
.map(c -> {
663671
if (type.startsWith("StatementPattern") && child.type.startsWith("Var")) {
664672
return spoc[j] + ": " + c;
@@ -674,6 +682,23 @@ private String getHumanReadable(int prettyBoxDrawingType) {
674682
return sb.toString();
675683
}
676684

685+
private List<GenericPlanNode> orderedPlansForDisplay() {
686+
if (!isProjectionElemListNode() || plans.size() < 2) {
687+
return plans;
688+
}
689+
List<GenericPlanNode> orderedPlans = new ArrayList<>(plans);
690+
orderedPlans.sort(Comparator.comparing(plan -> plan.getType() == null ? "" : plan.getType()));
691+
return orderedPlans;
692+
}
693+
694+
private boolean isProjectionElemListNode() {
695+
return type != null && type.startsWith("ProjectionElemList");
696+
}
697+
698+
private boolean isMultiProjectionNode() {
699+
return type != null && type.startsWith("MultiProjection");
700+
}
701+
677702
/**
678703
* @return Human readable number. Eg. 12.1M for 1212213.4 and UNKNOWN for -1.
679704
*/
@@ -1336,6 +1361,7 @@ private static double getMaxResultSizeActual(GenericPlanNode genericPlanNode) {
13361361
private String toDotInternal(double maxResultSizeActual, double maxTotalTime, double maxSelfTime) {
13371362
StringBuilder sb = new StringBuilder();
13381363
sb.append(" ");
1364+
List<GenericPlanNode> displayPlans = orderedPlansForDisplay();
13391365

13401366
if (newScope != null && newScope) {
13411367
sb.append("subgraph cluster_")
@@ -1377,13 +1403,13 @@ private String toDotInternal(double maxResultSizeActual, double maxTotalTime, do
13771403
.orElse(""));
13781404

13791405
sb.append("</table>>").append(" shape=plaintext];").append(newLine);
1380-
for (int i = 0; i < plans.size(); i++) {
1381-
GenericPlanNode p = plans.get(i);
1406+
for (int i = 0; i < displayPlans.size(); i++) {
1407+
GenericPlanNode p = displayPlans.get(i);
13821408
String linkLabel = "index " + i;
13831409

1384-
if (plans.size() == 2) {
1410+
if (displayPlans.size() == 2) {
13851411
linkLabel = i == 0 ? "left" : "right";
1386-
} else if (plans.size() == 1) {
1412+
} else if (displayPlans.size() == 1) {
13871413
linkLabel = "";
13881414
}
13891415

@@ -1398,7 +1424,7 @@ private String toDotInternal(double maxResultSizeActual, double maxTotalTime, do
13981424
.append(newLine);
13991425
}
14001426

1401-
plans.forEach(p -> sb.append(p.toDotInternal(maxResultSizeActual, maxTotalTime, maxSelfTime)));
1427+
displayPlans.forEach(p -> sb.append(p.toDotInternal(maxResultSizeActual, maxTotalTime, maxSelfTime)));
14021428

14031429
if (newScope != null && newScope) {
14041430
sb.append(newLine).append("}").append(newLine);

core/query/src/test/java/org/eclipse/rdf4j/query/explanation/GenericPlanNodeTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,20 @@ void toStringOrdersExplainAnnotationsIndependentlyOfInsertionOrder() {
199199
assertTrue(actual.indexOf("bindingState=bound") < actual.indexOf("indexNames=spoc, posc"), actual);
200200
}
201201

202+
@Test
203+
void toStringSortsProjectionElemListChildrenAlphabetically() {
204+
GenericPlanNode projectionElemList = new GenericPlanNode("ProjectionElemList");
205+
projectionElemList.addPlans(
206+
new GenericPlanNode("ProjectionElem \"d\""),
207+
new GenericPlanNode("ProjectionElem \"a\""),
208+
new GenericPlanNode("ProjectionElem \"code\""));
209+
210+
String actual = projectionElemList.toString();
211+
212+
assertTrue(actual.indexOf("ProjectionElem \"a\"") < actual.indexOf("ProjectionElem \"code\""), actual);
213+
assertTrue(actual.indexOf("ProjectionElem \"code\"") < actual.indexOf("ProjectionElem \"d\""), actual);
214+
}
215+
202216
@Test
203217
void toDotIncludesExplainAnnotationRows() {
204218
GenericPlanNode join = new GenericPlanNode("Join");

0 commit comments

Comments
 (0)