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

mbudiu 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 4ae896d4a5 [CALCITE-7369] ProjectToWindowRule loses column alias when 
optimizing OVER window queries
4ae896d4a5 is described below

commit 4ae896d4a5992fe271fa763e3f3d2bddc8b74954
Author: lincoln.lil <[email protected]>
AuthorDate: Mon Jan 12 14:33:22 2026 +0800

    [CALCITE-7369] ProjectToWindowRule loses column alias when optimizing OVER 
window queries
---
 .../apache/calcite/rel/logical/LogicalWindow.java  |  3 +-
 .../apache/calcite/rel/rules/CalcRelSplitter.java  | 63 ++++++++++++++++----
 .../rel/logical/ToLogicalConverterTest.java        |  8 +--
 .../calcite/rel/rel2sql/RelToSqlConverterTest.java | 32 +++++------
 .../org/apache/calcite/test/RelOptRulesTest.java   | 33 +++++++++++
 .../org/apache/calcite/test/RelOptRulesTest.xml    | 67 ++++++++++++++++++----
 core/src/test/resources/sql/sub-query.iq           |  4 +-
 core/src/test/resources/sql/winagg.iq              |  2 +-
 .../java/org/apache/calcite/test/PigRelOpTest.java |  4 +-
 9 files changed, 170 insertions(+), 46 deletions(-)

diff --git 
a/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java 
b/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
index 815b8f8ca5..16b247974b 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
@@ -244,11 +244,12 @@ public static RelNode create(RelOptCluster cluster,
       }
     }
 
+    int callIndex = 0;
     for (Ord<Group> window : Ord.zip(groups)) {
       for (Ord<RexWinAggCall> over : Ord.zip(window.e.aggCalls)) {
         // Add the k-th over expression of
         // the i-th window to the output of the program.
-        String name = fieldNames.get(over.i);
+        String name = fieldNames.get(callIndex++);
         if (name == null || name.startsWith("$")) {
           name = "w" + window.i + "$o" + over.i;
         }
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/CalcRelSplitter.java 
b/core/src/main/java/org/apache/calcite/rel/rules/CalcRelSplitter.java
index 67bbbae246..17e6333dc2 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/CalcRelSplitter.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/CalcRelSplitter.java
@@ -605,7 +605,7 @@ private RexProgram createProgramForLevel(
       projectRefs.add(new RexLocalRef(index, expr.getType()));
 
       // Inherit meaningful field name if possible.
-      fieldNames.add(deriveFieldName(expr, i));
+      fieldNames.add(deriveFieldName(expr, projectExprOrdinal, i));
     }
     RexLocalRef conditionRef;
     if (conditionExprOrdinal >= 0) {
@@ -627,18 +627,61 @@ private RexProgram createProgramForLevel(
         outputRowType);
   }
 
-  private String deriveFieldName(RexNode expr, int ordinal) {
+  /**
+   * Derives a field name for a projected expression.
+   *
+   * <p>If {@code expr} is a {@link RexInputRef}, returns the corresponding
+   * input field name. Otherwise, attempts to retrieve the name from the
+   * original program's projections. If no meaningful name is found, or if
+   * the name looks like an auto-generated name such as {@code $n} (but not
+   * starting with {@code $EXPR}), returns a synthesized name {@code "$" + 
ordinal}.
+   *
+   * @param expr      Expression to derive the name for
+   * @param exprIndex Index of the expression in the program's expression list
+   * @param ordinal   Position in the projection (used to generate a fallback 
name)
+   * @return Derived or synthesized field name
+   */
+  private String deriveFieldName(RexNode expr, int exprIndex, int ordinal) {
+    String fieldName = null;
     if (expr instanceof RexInputRef) {
-      int inputIndex = ((RexInputRef) expr).getIndex();
-      String fieldName =
-          child.getRowType().getFieldList().get(inputIndex).getName();
-      // Don't inherit field names like '$3' from child: that's
-      // confusing.
-      if (!fieldName.startsWith("$") || fieldName.startsWith("$EXPR")) {
-        return fieldName;
+      fieldName = getInputRefName((RexInputRef) expr);
+    } else {
+      fieldName = findProjectedFieldName(exprIndex);
+    }
+    return normalizeFieldName(fieldName, ordinal);
+  }
+
+  private String getInputRefName(RexInputRef ref) {
+    int inputIndex = ref.getIndex();
+    return child.getRowType().getFieldList().get(inputIndex).getName();
+  }
+
+  /**
+   * Return the output field name corresponding to the given expression index 
{@code exprIndex},
+   * or {@code null} if the expression is not part of the program's projection.
+   *
+   * @param exprIndex Index of the expression in the program's expression list
+   * @return the output field name for the given expression index, or {@code 
null} if not projected
+   */
+  private @Nullable String findProjectedFieldName(int exprIndex) {
+    List<RexLocalRef> projects = program.getProjectList();
+    List<String> fieldNames = program.getOutputRowType().getFieldNames();
+    for (int i = 0; i < projects.size(); i++) {
+      // If the project entry refers to the given expression, return its name.
+      if (projects.get(i).getIndex() == exprIndex) {
+        return fieldNames.get(i);
       }
     }
-    return "$" + ordinal;
+    return null;
+  }
+
+  private String normalizeFieldName(@Nullable String fieldName, int ordinal) {
+    // Don't inherit field names like '$3' from child: that's confusing.
+    if (fieldName == null
+            || (fieldName.startsWith("$") && !fieldName.startsWith("$EXPR"))) {
+      return "$" + ordinal;
+    }
+    return fieldName;
   }
 
   /**
diff --git 
a/core/src/test/java/org/apache/calcite/rel/logical/ToLogicalConverterTest.java 
b/core/src/test/java/org/apache/calcite/rel/logical/ToLogicalConverterTest.java
index 062bea6d50..fb7deb5b8a 100644
--- 
a/core/src/test/java/org/apache/calcite/rel/logical/ToLogicalConverterTest.java
+++ 
b/core/src/test/java/org/apache/calcite/rel/logical/ToLogicalConverterTest.java
@@ -477,12 +477,12 @@ private void verify(RelNode rel, String expectedPhysical, 
String expectedLogical
   @Test void testWindow() {
     String sql = "SELECT rank() over (order by \"hire_date\") FROM 
\"employee\"";
     String expectedPhysical = ""
-        + "EnumerableProject($0=[$17])\n"
+        + "EnumerableProject(EXPR$0=[$17])\n"
         + "  EnumerableWindow(window#0=[window(order by [9] aggs [RANK()])])\n"
         + "    JdbcToEnumerableConverter\n"
         + "      JdbcTableScan(table=[[foodmart, employee]])\n";
     String expectedLogical = ""
-        + "LogicalProject($0=[$17])\n"
+        + "LogicalProject(EXPR$0=[$17])\n"
         + "  LogicalWindow(window#0=[window(order by [9] aggs [RANK()])])\n"
         + "    LogicalTableScan(table=[[foodmart, employee]])\n";
     verify(rel(sql), expectedPhysical, expectedLogical);
@@ -493,13 +493,13 @@ void testWindowExcludeImp(String excludeClause, String 
expectedExcludeString) {
     String sql = String.format(Locale.ROOT, "SELECT sum(\"salary\") over 
(order by \"hire_date\" "
         + "rows between unbounded preceding and current row %s) FROM 
\"employee\"", excludeClause);
     String expectedPhysical =
-        String.format(Locale.ROOT, "EnumerableProject($0=[$17])\n"
+        String.format(Locale.ROOT, "EnumerableProject(EXPR$0=[$17])\n"
             + "  EnumerableWindow(window#0=[window(order by [9] rows between"
             + " UNBOUNDED PRECEDING and CURRENT ROW %saggs [SUM($11)])])\n"
             + "    JdbcToEnumerableConverter\n"
             + "      JdbcTableScan(table=[[foodmart, employee]])\n", 
expectedExcludeString);
     String expectedLogical =
-        String.format(Locale.ROOT, "LogicalProject($0=[$17])\n"
+        String.format(Locale.ROOT, "LogicalProject(EXPR$0=[$17])\n"
             + "  LogicalWindow(window#0=[window(order by [9] rows between"
             + " UNBOUNDED PRECEDING and CURRENT ROW %saggs [SUM($11)])])\n"
             + "    LogicalTableScan(table=[[foodmart, employee]])\n", 
expectedExcludeString);
diff --git 
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java 
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
index 5a9b278c35..a44d05ce64 100644
--- 
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
+++ 
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
@@ -5566,11 +5566,11 @@ private void checkLiteral2(String expression, String 
expected) {
    * Support Window in RelToSqlConverter</a>. */
   @Test void testConvertWindowToSql() {
     String query0 = "SELECT row_number() over (order by \"hire_date\") FROM 
\"employee\"";
-    String expected0 = "SELECT ROW_NUMBER() OVER (ORDER BY \"hire_date\") AS 
\"$0\"\n"
+    String expected0 = "SELECT ROW_NUMBER() OVER (ORDER BY \"hire_date\")\n"
             + "FROM \"foodmart\".\"employee\"";
 
     String query1 = "SELECT rank() over (order by \"hire_date\") FROM 
\"employee\"";
-    String expected1 = "SELECT RANK() OVER (ORDER BY \"hire_date\") AS 
\"$0\"\n"
+    String expected1 = "SELECT RANK() OVER (ORDER BY \"hire_date\")\n"
             + "FROM \"foodmart\".\"employee\"";
 
     String query2 = "SELECT lead(\"employee_id\",1,'NA') over "
@@ -5578,14 +5578,14 @@ private void checkLiteral2(String expression, String 
expected) {
             + "FROM \"employee\"";
     String expected2 = "SELECT LEAD(\"employee_id\", 1, 'NA') OVER "
             + "(PARTITION BY \"hire_date\" "
-            + "ORDER BY \"employee_id\") AS \"$0\"\n"
+            + "ORDER BY \"employee_id\")\n"
             + "FROM \"foodmart\".\"employee\"";
 
     String query3 = "SELECT lag(\"employee_id\",1,'NA') over "
             + "(partition by \"hire_date\" order by \"employee_id\")\n"
             + "FROM \"employee\"";
     String expected3 = "SELECT LAG(\"employee_id\", 1, 'NA') OVER "
-            + "(PARTITION BY \"hire_date\" ORDER BY \"employee_id\") AS 
\"$0\"\n"
+            + "(PARTITION BY \"hire_date\" ORDER BY \"employee_id\")\n"
             + "FROM \"foodmart\".\"employee\"";
 
     String query4 = "SELECT lag(\"employee_id\",1,'NA') "
@@ -5596,13 +5596,13 @@ private void checkLiteral2(String expression, String 
expected) {
             + "count(*) over (partition by \"birth_date\" order by 
\"employee_id\") as count2\n"
             + "FROM \"employee\"";
     String expected4 = "SELECT LAG(\"employee_id\", 1, 'NA') OVER "
-            + "(PARTITION BY \"hire_date\" ORDER BY \"employee_id\") AS 
\"$0\", "
+            + "(PARTITION BY \"hire_date\" ORDER BY \"employee_id\") AS 
\"LAG1\", "
             + "LAG(\"employee_id\", 1, 'NA') OVER "
-            + "(PARTITION BY \"birth_date\" ORDER BY \"employee_id\") AS 
\"$1\", "
+            + "(PARTITION BY \"birth_date\" ORDER BY \"employee_id\") AS 
\"LAG2\", "
             + "COUNT(*) OVER (PARTITION BY \"hire_date\" ORDER BY 
\"employee_id\" "
-            + "RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS \"$2\", "
+            + "RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS 
\"COUNT1\", "
             + "COUNT(*) OVER (PARTITION BY \"birth_date\" ORDER BY 
\"employee_id\" "
-            + "RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS \"$3\"\n"
+            + "RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS 
\"COUNT2\"\n"
             + "FROM \"foodmart\".\"employee\"";
 
     String query5 = "SELECT lag(\"employee_id\",1,'NA') "
@@ -5613,13 +5613,13 @@ private void checkLiteral2(String expression, String 
expected) {
             + "max(sum(\"employee_id\")) over (partition by \"birth_date\" 
order by \"employee_id\") as count2\n"
             + "FROM \"employee\" group by \"employee_id\", \"hire_date\", 
\"birth_date\"";
     String expected5 = "SELECT LAG(\"employee_id\", 1, 'NA') OVER "
-            + "(PARTITION BY \"hire_date\" ORDER BY \"employee_id\") AS 
\"$0\", "
+            + "(PARTITION BY \"hire_date\" ORDER BY \"employee_id\") AS 
\"LAG1\", "
             + "LAG(\"employee_id\", 1, 'NA') OVER "
-            + "(PARTITION BY \"birth_date\" ORDER BY \"employee_id\") AS 
\"$1\", "
+            + "(PARTITION BY \"birth_date\" ORDER BY \"employee_id\") AS 
\"LAG2\", "
             + "MAX(SUM(\"employee_id\")) OVER (PARTITION BY \"hire_date\" 
ORDER BY \"employee_id\" "
-            + "RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS \"$2\", "
+            + "RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS 
\"COUNT1\", "
             + "MAX(SUM(\"employee_id\")) OVER (PARTITION BY \"birth_date\" 
ORDER BY \"employee_id\" "
-            + "RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS \"$3\"\n"
+            + "RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS 
\"COUNT2\"\n"
             + "FROM \"foodmart\".\"employee\"\n"
             + "GROUP BY \"employee_id\", \"hire_date\", \"birth_date\"";
 
@@ -5635,7 +5635,7 @@ private void checkLiteral2(String expression, String 
expected) {
         + "count(distinct \"employee_id\") over (order by \"hire_date\") FROM 
\"employee\"";
     String expected7 = "SELECT "
         + "COUNT(DISTINCT \"employee_id\") OVER (ORDER BY \"hire_date\""
-        + " RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS \"$0\"\n"
+        + " RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)\n"
         + "FROM \"foodmart\".\"employee\"";
 
     String query8 = "SELECT "
@@ -5823,9 +5823,9 @@ private void checkLiteral2(String expression, String 
expected) {
         + "FROM ( SELECT \"product_name\", "
         + "SUM(\"product_id\") OVER (PARTITION BY \"product_name\") AS 
\"daily_sales\" "
         + "FROM \"product\" ) subquery";
-    String expected00 = "SELECT RANK() OVER (ORDER BY \"$1\" DESC) AS \"$0\"\n"
+    String expected00 = "SELECT RANK() OVER (ORDER BY \"daily_sales\" DESC) AS 
\"rank1\"\n"
         + "FROM (SELECT \"product_name\", SUM(\"product_id\") OVER (PARTITION 
BY \"product_name\" "
-        + "RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS 
\"$1\"\n"
+        + "RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS 
\"daily_sales\"\n"
         + "FROM \"foodmart\".\"product\") AS \"t0\"";
     String expected01 = "SELECT RANK() OVER (ORDER BY \"daily_sales\" DESC) AS 
\"rank1\"\n"
         + "FROM (SELECT \"product_name\", SUM(\"product_id\") OVER (PARTITION 
BY \"product_name\""
@@ -5840,7 +5840,7 @@ private void checkLiteral2(String expression, String 
expected) {
         + "RANK() OVER (ORDER BY \"product_name\" DESC) AS \"rank1\" "
         + "FROM (SELECT \"product_id\", \"product_name\" FROM \"product\") a";
     String expected10 = "SELECT \"product_id\","
-        + " RANK() OVER (ORDER BY \"product_name\" DESC) AS \"$1\"\n"
+        + " RANK() OVER (ORDER BY \"product_name\" DESC) AS \"rank1\"\n"
         + "FROM \"foodmart\".\"product\"";
     String expected11 = "SELECT \"product_id\","
         + " RANK() OVER (ORDER BY \"product_name\" DESC) AS \"rank1\"\n"
diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java 
b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
index a839054349..7de683e9ac 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -12059,4 +12059,37 @@ private void 
checkLoptOptimizeJoinRule(LoptOptimizeJoinRule rule) {
         .check();
   }
 
+  /** Test case of
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7369";>[CALCITE-7369]
+   * ProjectToWindowRule loses column alias when optimizing OVER window 
queries</a>. */
+  @Test void testProjectToWindowRuleForNestedOver() {
+    HepProgramBuilder builder = new HepProgramBuilder();
+    builder.addRuleClass(ProjectToWindowRule.class);
+    HepPlanner hepPlanner = new HepPlanner(builder.build());
+    hepPlanner.addRule(CoreRules.PROJECT_TO_LOGICAL_PROJECT_AND_WINDOW);
+
+    final String sql =
+            "select deptno, f1, f2 from (select *, last_value(deptno) over 
(order by empno) f2\n"
+            + "from (select *, first_value(deptno) over (order by empno) f1 
from emp))\n";
+    sql(sql).withPlanner(hepPlanner)
+            .check();
+  }
+
+  /** Test case of
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7369";>[CALCITE-7369]
+   * ProjectToWindowRule loses column alias when optimizing OVER window 
queries</a>. */
+  @Test void testProjectToWindowRuleForMultiOver() {
+    HepProgramBuilder builder = new HepProgramBuilder();
+    builder.addRuleClass(ProjectToWindowRule.class);
+    HepPlanner hepPlanner = new HepPlanner(builder.build());
+    hepPlanner.addRule(CoreRules.PROJECT_TO_LOGICAL_PROJECT_AND_WINDOW);
+
+    final String sql = "select * from ("
+            + "select empno, deptno, last_value(deptno) over (order by empno) 
f1,\n"
+            + "first_value(deptno) over (order by empno desc) f3,\n"
+            + "count(deptno) over (order by empno) f2\n"
+            + "from emp) where f2 > 10";
+    sql(sql).withPlanner(hepPlanner)
+            .check();
+  }
 }
diff --git 
a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml 
b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
index 465ce0e3e1..7c56417de6 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -5439,7 +5439,7 @@ from emp]]>
     </Resource>
     <Resource name="planAfter">
       <![CDATA[
-LogicalProject($0=[$3], $1=[$4])
+LogicalProject(SUM1=[$3], SUM2=[$4])
   LogicalWindow(window#0=[window(partition {1} order by [0] aggs [SUM($1), 
SUM($2)])])
     LogicalProject(SAL=[$5], DEPTNO=[$7], $2=[+($7, $5)])
       LogicalTableScan(table=[[CATALOG, SALES, EMP]])
@@ -5461,7 +5461,7 @@ ROWS BETWEEN 5 + 5 PRECEDING AND 1 PRECEDING) AS w_count 
from emp
     </Resource>
     <Resource name="planBefore">
       <![CDATA[
-LogicalProject($0=[$1])
+LogicalProject(W_COUNT=[$1])
   LogicalWindow(window#0=[window(order by [0] rows between $1 PRECEDING and $2 
PRECEDING aggs [COUNT()])], constants=[[10, 1]])
     LogicalProject(EMPNO=[$0])
       LogicalTableScan(table=[[CATALOG, SALES, EMP]])
@@ -7382,7 +7382,7 @@ LogicalProject(EMPNO=[$0], DEPTNO=[$1], W_COUNT=[$2])
       <![CDATA[
 LogicalProject(EMPNO=[$0], DEPTNO=[$1], W_COUNT=[$2])
   LogicalFilter(condition=[IS NULL($2)])
-    LogicalProject(EMPNO=[$0], DEPTNO=[$7], $2=[$9])
+    LogicalProject(EMPNO=[$0], DEPTNO=[$7], W_COUNT=[$9])
       LogicalWindow(window#0=[window(partition {7} order by [0] aggs 
[COUNT($0)])])
         LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
@@ -11833,6 +11833,31 @@ LogicalProject(DNAME=[$1])
     LogicalAggregate(group=[{0}])
       LogicalProject(DEPTNO=[$7])
         LogicalTableScan(table=[[scott, EMP]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testProjectToWindowRuleForMultiOver">
+    <Resource name="sql">
+      <![CDATA[select * from (select empno, deptno, last_value(deptno) over 
(order by empno) f1,
+first_value(deptno) over (order by empno desc) f3,
+count(deptno) over (order by empno) f2
+from emp) where f2 > 10]]>
+    </Resource>
+    <Resource name="planBefore">
+      <![CDATA[
+LogicalProject(EMPNO=[$0], DEPTNO=[$1], F1=[$2], F3=[$3], F2=[$4])
+  LogicalFilter(condition=[>($4, 10)])
+    LogicalProject(EMPNO=[$0], DEPTNO=[$7], F1=[LAST_VALUE($7) OVER (ORDER BY 
$0)], F3=[FIRST_VALUE($7) OVER (ORDER BY $0 DESC)], F2=[COUNT($7) OVER (ORDER 
BY $0)])
+      LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+    </Resource>
+    <Resource name="planAfter">
+      <![CDATA[
+LogicalProject(EMPNO=[$0], DEPTNO=[$1], F1=[$2], F3=[$3], F2=[$4])
+  LogicalFilter(condition=[>($4, 10)])
+    LogicalProject(EMPNO=[$0], DEPTNO=[$7], F1=[$9], F3=[$11], F2=[$10])
+      LogicalWindow(window#0=[window(order by [0] aggs [LAST_VALUE($7), 
COUNT($7)])], window#1=[window(order by [0 DESC] aggs [FIRST_VALUE($7)])])
+        LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
     </Resource>
   </TestCase>
@@ -11847,7 +11872,7 @@ from emp]]>
     </Resource>
     <Resource name="planAfter">
       <![CDATA[
-LogicalProject($0=[$9], $1=[$11], $2=[$10], $3=[$12])
+LogicalProject(COUNT1=[$9], COUNT2=[$11], SUM1=[$10], SUM2=[$12])
   LogicalWindow(window#0=[window(partition {0} order by [5] aggs [COUNT(), 
SUM($7)])], window#1=[window(partition {7} order by [5] aggs [COUNT(), 
SUM($7)])])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
@@ -11856,6 +11881,28 @@ LogicalProject($0=[$9], $1=[$11], $2=[$10], $3=[$12])
       <![CDATA[
 LogicalProject(COUNT1=[COUNT() OVER (PARTITION BY $0 ORDER BY $5)], 
COUNT2=[COUNT() OVER (PARTITION BY $7 ORDER BY $5)], SUM1=[SUM($7) OVER 
(PARTITION BY $0 ORDER BY $5)], SUM2=[SUM($7) OVER (PARTITION BY $7 ORDER BY 
$5)])
   LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testProjectToWindowRuleForNestedOver">
+    <Resource name="sql">
+      <![CDATA[select deptno, f1, f2 from (select *, last_value(deptno) over 
(order by empno) f2
+from (select *, first_value(deptno) over (order by empno) f1 from emp))
+]]>
+    </Resource>
+    <Resource name="planBefore">
+      <![CDATA[
+LogicalProject(DEPTNO=[$7], F1=[$9], F2=[LAST_VALUE($7) OVER (ORDER BY $0)])
+  LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], F1=[FIRST_VALUE($7) OVER (ORDER 
BY $0)])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+    </Resource>
+    <Resource name="planAfter">
+      <![CDATA[
+LogicalProject(DEPTNO=[$7], F1=[$9], F2=[$10])
+  LogicalWindow(window#0=[window(order by [0] aggs [LAST_VALUE($7)])])
+    LogicalWindow(window#0=[window(order by [0] aggs [FIRST_VALUE($7)])])
+      LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
     </Resource>
   </TestCase>
@@ -11889,7 +11936,7 @@ from (
     </Resource>
     <Resource name="planAfter">
       <![CDATA[
-LogicalProject($0=[$2], $1=[$3])
+LogicalProject(COL1=[$2], COL2=[$3])
   LogicalWindow(window#0=[window(partition {1} order by [0] aggs [SUM($2), 
SUM($3)])], constants=[[100, 1000]])
     LogicalProject(SAL=[$5], DEPTNO=[$7])
       LogicalTableScan(table=[[CATALOG, SALES, EMP]])
@@ -16459,7 +16506,7 @@ from (
     </Resource>
     <Resource name="planAfter">
       <![CDATA[
-LogicalProject($0=[$2], $1=[$3], $2=[$4])
+LogicalProject(COL1=[$2], COL2=[$3], COL3=[$4])
   LogicalWindow(window#0=[window(partition {1} range between UNBOUNDED 
PRECEDING and CURRENT ROW aggs [SUM($2)])], window#1=[window(order by [1] aggs 
[SUM($2)])], window#2=[window(partition {1} range between UNBOUNDED PRECEDING 
and CURRENT ROW aggs [SUM(5000)])], constants=[[100]])
     LogicalProject(SAL=[$5], DEPTNO=[$7])
       LogicalFilter(condition=[=($5, 5000)])
@@ -16492,7 +16539,7 @@ LogicalProject(COL1=[SUM(100) OVER (ORDER BY $7, $0 
RANGE BETWEEN CURRENT ROW AN
     </Resource>
     <Resource name="planAfter">
       <![CDATA[
-LogicalProject($0=[$3], $1=[$4])
+LogicalProject(COL1=[$3], COL2=[$4])
   LogicalWindow(window#0=[window(order by [2, 0] range between CURRENT ROW and 
UNBOUNDED FOLLOWING aggs [SUM($3)])], window#1=[window(partition {2} order by 
[2, 0] range between UNBOUNDED PRECEDING and UNBOUNDED FOLLOWING aggs 
[SUM($3)])], constants=[[100]])
     LogicalProject(EMPNO=[$0], SAL=[$5], DEPTNO=[$7])
       LogicalFilter(condition=[=($5, 5000)])
@@ -22115,7 +22162,7 @@ LogicalProject(EXPR$0=[$1], EXPR$1=[$2], 
EXPR$2=[SUM($2) OVER (PARTITION BY $1 O
     </Resource>
     <Resource name="planAfter">
       <![CDATA[
-LogicalProject(EXPR$0=[$1], EXPR$1=[$2], $2=[$3])
+LogicalProject(EXPR$0=[$1], EXPR$1=[$2], EXPR$2=[$3])
   LogicalWindow(window#0=[window(partition {1} order by [2] aggs [SUM($2)])])
     LogicalAggregate(group=[{0}], EXPR$0=[MIN($1)], EXPR$1=[SUM($2)])
       LogicalProject(DEPTNO=[$7], EMPNO=[$0], SAL=[$5])
@@ -22164,11 +22211,11 @@ LogicalUnion(all=[true])
     <Resource name="planAfter">
       <![CDATA[
 LogicalUnion(all=[true])
-  LogicalProject($0=[$2])
+  LogicalProject(S=[$2])
     LogicalWindow(window#0=[window(partition {1} order by [0] aggs 
[SUM($2)])], constants=[[100]])
       LogicalProject(SAL=[$5], DEPTNO=[$7])
         LogicalTableScan(table=[[CATALOG, SALES, EMP]])
-  LogicalProject($0=[$2])
+  LogicalProject(S=[$2])
     LogicalWindow(window#0=[window(partition {1} order by [0] aggs 
[SUM($2)])], constants=[[1000]])
       LogicalProject(SAL=[$5], DEPTNO=[$7])
         LogicalTableScan(table=[[CATALOG, SALES, EMP]])
diff --git a/core/src/test/resources/sql/sub-query.iq 
b/core/src/test/resources/sql/sub-query.iq
index 971e8c0065..404520f0dc 100644
--- a/core/src/test/resources/sql/sub-query.iq
+++ b/core/src/test/resources/sql/sub-query.iq
@@ -724,7 +724,7 @@ EnumerableCalc(expr#0..4=[{inputs}], DNAME=[$t1], 
EXPR$1=[$t2])
   EnumerableHashJoin(condition=[=($0, $3)], joinType=[left])
     EnumerableCalc(expr#0..2=[{inputs}], proj#0..1=[{exprs}])
       EnumerableTableScan(table=[[scott, DEPT]])
-    EnumerableCalc(expr#0..8=[{inputs}], expr#9=[1], expr#10=[<=($t8, $t9)], 
COMM=[$t6], DEPTNO=[$t7], $2=[$t8], $condition=[$t10])
+    EnumerableCalc(expr#0..8=[{inputs}], expr#9=[1], expr#10=[<=($t8, $t9)], 
COMM=[$t6], DEPTNO=[$t7], rn=[$t8], $condition=[$t10])
       EnumerableWindow(window#0=[window(partition {7} order by [6 DESC] rows 
between UNBOUNDED PRECEDING and CURRENT ROW aggs [ROW_NUMBER()])])
         EnumerableCalc(expr#0..7=[{inputs}], expr#8=[IS NOT NULL($t7)], 
proj#0..7=[{exprs}], $condition=[$t8])
           EnumerableTableScan(table=[[scott, EMP]])
@@ -2435,7 +2435,7 @@ EnumerableCalc(expr#0..5=[{inputs}], expr#6=[NOT($t3)], 
expr#7=[IS NOT NULL($t3)
       EnumerableSort(sort0=[$1], dir0=[ASC])
         EnumerableCalc(expr#0..2=[{inputs}], cs=[$t1], DEPTNO=[$t0], rn=[$t2])
           EnumerableWindow(window#0=[window(partition {0} order by [1 DESC] 
rows between UNBOUNDED PRECEDING and CURRENT ROW aggs [ROW_NUMBER()])])
-            EnumerableCalc(expr#0..2=[{inputs}], expr#3=[CAST($t0):INTEGER NOT 
NULL], expr#4=[0], expr#5=[>($t3, $t4)], expr#6=[null:TINYINT], 
expr#7=[CASE($t5, $t0, $t6)], expr#8=[IS NOT NULL($t7)], 
expr#9=[CAST($t7):INTEGER], expr#10=[Sarg[10; NULL AS TRUE]], 
expr#11=[SEARCH($t9, $t10)], DEPTNO=[$t0], $1=[$t8], $condition=[$t11])
+            EnumerableCalc(expr#0..2=[{inputs}], expr#3=[CAST($t0):INTEGER NOT 
NULL], expr#4=[0], expr#5=[>($t3, $t4)], expr#6=[null:TINYINT], 
expr#7=[CASE($t5, $t0, $t6)], expr#8=[IS NOT NULL($t7)], 
expr#9=[CAST($t7):INTEGER], expr#10=[Sarg[10; NULL AS TRUE]], 
expr#11=[SEARCH($t9, $t10)], DEPTNO=[$t0], cs=[$t8], $condition=[$t11])
               EnumerableTableScan(table=[[scott, DEPT]])
 !plan
 
diff --git a/core/src/test/resources/sql/winagg.iq 
b/core/src/test/resources/sql/winagg.iq
index feb4dc6951..beb90d9fd4 100644
--- a/core/src/test/resources/sql/winagg.iq
+++ b/core/src/test/resources/sql/winagg.iq
@@ -1003,7 +1003,7 @@ select gender, count(*) over(partition by gender order by 
ename) as count1 from
 
 # Get the plan and result which push filter past window
 select gender, count(*) over(partition by gender order by ename) as count1 
from emp where gender = 'F';
-EnumerableCalc(expr#0..3=[{inputs}], GENDER=[$t2], $1=[$t3])
+EnumerableCalc(expr#0..3=[{inputs}], GENDER=[$t2], COUNT1=[$t3])
   EnumerableWindow(window#0=[window(partition {2} order by [0] aggs 
[COUNT()])])
     EnumerableCalc(expr#0..2=[{inputs}], expr#3=['F'], expr#4=[=($t2, $t3)], 
proj#0..2=[{exprs}], $condition=[$t4])
       EnumerableValues(tuples=[[{ 'Jane ', 10, 'F' }, { 'Bob  ', 10, 'M' }, { 
'Eric ', 20, 'M' }, { 'Susan', 30, 'F' }, { 'Alice', 30, 'F' }, { 'Adam ', 50, 
'M' }, { 'Eve  ', 50, 'F' }, { 'Grace', 60, 'F' }, { 'Wilma', null, 'F' }]])
diff --git a/piglet/src/test/java/org/apache/calcite/test/PigRelOpTest.java 
b/piglet/src/test/java/org/apache/calcite/test/PigRelOpTest.java
index 5067503b89..e1c60c10cb 100644
--- a/piglet/src/test/java/org/apache/calcite/test/PigRelOpTest.java
+++ b/piglet/src/test/java/org/apache/calcite/test/PigRelOpTest.java
@@ -1665,10 +1665,10 @@ private Fluent pig(String script) {
         + " name=[$1], age=[$2], city=[$3])\n"
         + "    LogicalTableScan(table=[[emp1]])\n";
 
-    final String sql = "SELECT w0$o0 AS rank_A, id, name, age, city\n"
+    final String sql = "SELECT rank_A, id, name, age, city\n"
         + "FROM (SELECT id, name, age, city, RANK() OVER ()\n"
         + "    FROM emp1) AS t\n"
-        + "WHERE w0$o0 > 1";
+        + "WHERE rank_A > 1";
     pig(script).assertRel(hasTree(plan))
         .assertSql(is(sql));
   }

Reply via email to