Skip to content

Commit

Permalink
[CALCITE-6817] Add string representation of default nulls direction f…
Browse files Browse the repository at this point in the history
…or RelNode
  • Loading branch information
ILuffZhe committed Feb 10, 2025
1 parent ed66fcd commit f1c370a
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 5 deletions.
18 changes: 15 additions & 3 deletions core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -2442,20 +2442,32 @@ public static String toString(final RelNode rel) {
return toString(rel, SqlExplainLevel.EXPPLAN_ATTRIBUTES);
}

/**
* Converts a relational expression to a string, showing just basic
* attributes, and doesn't expand detail info for {@code rel}.
*/
public static String toString(
final RelNode rel,
SqlExplainLevel detailLevel) {
return toString(rel, detailLevel, false);
}

/**
* Converts a relational expression to a string;
* returns null if and only if {@code rel} is null.
* returns null if and only if {@code rel} is null,
* returns expanded detail info for {@code rel} if {@code expand} is true.
*/
public static @PolyNull String toString(
final @PolyNull RelNode rel,
SqlExplainLevel detailLevel) {
SqlExplainLevel detailLevel,
boolean expand) {
if (rel == null) {
return null;
}
final StringWriter sw = new StringWriter();
final RelWriter planWriter =
new RelWriterImpl(
new PrintWriter(sw), detailLevel, false);
new PrintWriter(sw), detailLevel, false, expand);
rel.explain(planWriter);
return sw.toString();
}
Expand Down
11 changes: 11 additions & 0 deletions core/src/main/java/org/apache/calcite/rel/RelFieldCollation.java
Original file line number Diff line number Diff line change
Expand Up @@ -329,4 +329,15 @@ public String shortString() {
return direction.shortString;
}
}

public String fullString() {
switch (nullDirection) {
case FIRST:
return direction.shortString + "-nulls-first";
case LAST:
return direction.shortString + "-nulls-last";
default:
return direction.shortString;
}
}
}
9 changes: 9 additions & 0 deletions core/src/main/java/org/apache/calcite/rel/RelWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,13 @@ default RelWriter itemIf(String term, @Nullable Object value, boolean condition)
default boolean nest() {
return false;
}

/**
* Returns whether the writer needs to expand node's detail information when printing plan.
* For example, LogicalSort(sort0=[$0], dir0=[ASC]) will be expanded to
* LogicalSort(sort0=[$0], dir0=[ASC-nulls-last]).
*/
default boolean expand() {
return false;
}
}
6 changes: 5 additions & 1 deletion core/src/main/java/org/apache/calcite/rel/core/Sort.java
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,11 @@ public List<RexNode> getSortExps() {
}
for (Ord<RelFieldCollation> ord
: Ord.zip(collation.getFieldCollations())) {
pw.item("dir" + ord.i, ord.e.shortString());
if (!pw.expand()) {
pw.item("dir" + ord.i, ord.e.shortString());
} else {
pw.item("dir" + ord.i, ord.e.fullString());
}
}
}
pw.itemIf("offset", offset, offset != null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public class RelWriterImpl implements RelWriter {
protected final PrintWriter pw;
protected final SqlExplainLevel detailLevel;
protected final boolean withIdPrefix;
protected final boolean expand;
protected final Spacer spacer = new Spacer();
private final List<Pair<String, @Nullable Object>> values = new ArrayList<>();

Expand All @@ -53,9 +54,15 @@ public RelWriterImpl(PrintWriter pw) {
public RelWriterImpl(
PrintWriter pw, SqlExplainLevel detailLevel,
boolean withIdPrefix) {
this(pw, detailLevel, withIdPrefix, false);
}
public RelWriterImpl(
PrintWriter pw, SqlExplainLevel detailLevel,
boolean withIdPrefix, boolean expand) {
this.pw = pw;
this.detailLevel = detailLevel;
this.withIdPrefix = withIdPrefix;
this.expand = expand;
}

//~ Methods ----------------------------------------------------------------
Expand Down Expand Up @@ -180,4 +187,8 @@ public String simple() {
buf.append(")");
return buf.toString();
}

@Override public boolean expand() {
return this.expand;
}
}
67 changes: 67 additions & 0 deletions core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;

import static org.apache.calcite.test.Matchers.hasExpandedTree;
import static org.apache.calcite.test.Matchers.hasFieldNames;
import static org.apache.calcite.test.Matchers.hasHints;
import static org.apache.calcite.test.Matchers.hasTree;
Expand Down Expand Up @@ -4064,6 +4065,72 @@ private static RelBuilder assertSize(RelBuilder b,
assertThat(root, hasTree(expected));
}

/** Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-6817">[CALCITE-6817]
* Add string representation of default nulls direction for RelNode</a>. */
@Test void testDescWithDefaultNullDirection() {
// Equivalent SQL:
// SELECT *
// FROM emp
// ORDER BY empno DESC
final RelBuilder builder = RelBuilder.create(config().build());
final RelNode root =
builder.scan("EMP")
.sort(builder.desc(builder.field(0)))
.build();
final String expected =
"LogicalSort(sort0=[$0], dir0=[DESC])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n";
final String expectedExpanded =
"LogicalSort(sort0=[$0], dir0=[DESC-nulls-first])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n";
assertThat(root, hasTree(expected));
assertThat(root, hasExpandedTree(expectedExpanded));

// Equivalent SQL:
// SELECT *
// FROM emp
// ORDER BY empno DESC NULLS FIRST
final RelNode root2 =
builder.scan("EMP")
.sort(builder.nullsFirst(builder.desc(builder.field(0))))
.build();
assertThat(root2, hasTree(expected));
assertThat(root2, hasExpandedTree(expectedExpanded));
}

/** Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-6817">[CALCITE-6817]
* Add string representation of default nulls direction for RelNode</a>. */
@Test void testAscWithDefaultNullDirection() {
// Equivalent SQL:
// SELECT *
// FROM emp
// ORDER BY empno ASC
final RelBuilder builder = RelBuilder.create(config().build());
final RelNode root =
builder.scan("EMP")
.sort(builder.field(0))
.build();
final String expected =
"LogicalSort(sort0=[$0], dir0=[ASC])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n";
final String expectedExpanded =
"LogicalSort(sort0=[$0], dir0=[ASC-nulls-last])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n";
assertThat(root, hasTree(expected));
assertThat(root, hasExpandedTree(expectedExpanded));

// Equivalent SQL:
// SELECT *
// FROM emp
// ORDER BY empno ASC NULLS LAST
final RelNode root2 =
builder.scan("EMP")
.sort(builder.nullsLast(builder.field(0)))
.build();
assertThat(root2, hasTree(expected));
assertThat(root2, hasExpandedTree(expectedExpanded));
}

@Test void testLimit() {
// Equivalent SQL:
// SELECT *
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,11 @@ public DruidTable getDruidTable() {
}
for (Ord<RelFieldCollation> ord
: Ord.zip(sort.collation.getFieldCollations())) {
pw.item("dir" + ord.i, ord.e.shortString());
if (!pw.expand()) {
pw.item("dir" + ord.i, ord.e.shortString());
} else {
pw.item("dir" + ord.i, ord.e.fullString());
}
}
pw.itemIf("fetch", sort.fetch, sort.fetch != null);
} else {
Expand Down
12 changes: 12 additions & 0 deletions testkit/src/main/java/org/apache/calcite/test/Matchers.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.apache.calcite.rel.RelValidityChecker;
import org.apache.calcite.rel.hint.Hintable;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.sql.SqlExplainLevel;
import org.apache.calcite.util.TestUtil;
import org.apache.calcite.util.Util;

Expand Down Expand Up @@ -236,6 +237,17 @@ public static Matcher<RelNode> hasTree(final String value) {
});
}

/**
* Basically similar to {@link #hasTree(String)}, except for expanding RelNode's detail info.
* For example, default nulls direction will be compared through this.
*/
public static Matcher<RelNode> hasExpandedTree(final String value) {
return compose(Is.is(value), input -> {
// Convert RelNode to a string with Linux line-endings
return Util.toLinux(RelOptUtil.toString(input, SqlExplainLevel.EXPPLAN_ATTRIBUTES, true));
});
}

/**
* Creates a Matcher that matches a {@link RelNode} if its field
* names, converting to a list, are equal to the given {@code value}.
Expand Down

0 comments on commit f1c370a

Please sign in to comment.