This is an automated email from the ASF dual-hosted git repository.

zhehu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/main by this push:
     new f1c370a0cb [CALCITE-6817] Add string representation of default nulls 
direction for RelNode
f1c370a0cb is described below

commit f1c370a0cb57675b6e5a442b3d98e29d75a64043
Author: Zhe Hu <[email protected]>
AuthorDate: Fri Feb 7 19:37:22 2025 +0800

    [CALCITE-6817] Add string representation of default nulls direction for 
RelNode
---
 .../java/org/apache/calcite/plan/RelOptUtil.java   | 18 +++++-
 .../org/apache/calcite/rel/RelFieldCollation.java  | 11 ++++
 .../java/org/apache/calcite/rel/RelWriter.java     |  9 +++
 .../java/org/apache/calcite/rel/core/Sort.java     |  6 +-
 .../calcite/rel/externalize/RelWriterImpl.java     | 11 ++++
 .../org/apache/calcite/test/RelBuilderTest.java    | 67 ++++++++++++++++++++++
 .../apache/calcite/adapter/druid/DruidQuery.java   |  6 +-
 .../java/org/apache/calcite/test/Matchers.java     | 12 ++++
 8 files changed, 135 insertions(+), 5 deletions(-)

diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java 
b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
index 0c73d0c00e..01e712c900 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
@@ -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();
   }
diff --git a/core/src/main/java/org/apache/calcite/rel/RelFieldCollation.java 
b/core/src/main/java/org/apache/calcite/rel/RelFieldCollation.java
index 9359f92230..985ae902ca 100644
--- a/core/src/main/java/org/apache/calcite/rel/RelFieldCollation.java
+++ b/core/src/main/java/org/apache/calcite/rel/RelFieldCollation.java
@@ -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;
+    }
+  }
 }
diff --git a/core/src/main/java/org/apache/calcite/rel/RelWriter.java 
b/core/src/main/java/org/apache/calcite/rel/RelWriter.java
index 17b44227f0..6af6e11178 100644
--- a/core/src/main/java/org/apache/calcite/rel/RelWriter.java
+++ b/core/src/main/java/org/apache/calcite/rel/RelWriter.java
@@ -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;
+  }
 }
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Sort.java 
b/core/src/main/java/org/apache/calcite/rel/core/Sort.java
index 5f83f32c2e..9271a62107 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Sort.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Sort.java
@@ -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);
diff --git 
a/core/src/main/java/org/apache/calcite/rel/externalize/RelWriterImpl.java 
b/core/src/main/java/org/apache/calcite/rel/externalize/RelWriterImpl.java
index 9df7a3c27e..8d52ffbe38 100644
--- a/core/src/main/java/org/apache/calcite/rel/externalize/RelWriterImpl.java
+++ b/core/src/main/java/org/apache/calcite/rel/externalize/RelWriterImpl.java
@@ -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<>();
 
@@ -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 ----------------------------------------------------------------
@@ -180,4 +187,8 @@ public String simple() {
     buf.append(")");
     return buf.toString();
   }
+
+  @Override public boolean expand() {
+    return this.expand;
+  }
 }
diff --git a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java 
b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
index 3b0cc5f986..ba2c10c701 100644
--- a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
@@ -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;
@@ -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 *
diff --git 
a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java 
b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java
index 1012c717ce..a393d0e0a6 100644
--- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java
+++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java
@@ -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 {
diff --git a/testkit/src/main/java/org/apache/calcite/test/Matchers.java 
b/testkit/src/main/java/org/apache/calcite/test/Matchers.java
index 635750c46a..d410affe73 100644
--- a/testkit/src/main/java/org/apache/calcite/test/Matchers.java
+++ b/testkit/src/main/java/org/apache/calcite/test/Matchers.java
@@ -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;
 
@@ -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}.

Reply via email to