@@ -19,13 +19,7 @@ public void testReverseParserSuccess() {
1919 String ppl = "source=EMP | reverse" ;
2020 RelNode root = getRelNode (ppl );
2121 String expectedLogical =
22- ""
23- + "LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5],"
24- + " COMM=[$6], DEPTNO=[$7])\n "
25- + " LogicalSort(sort0=[$8], dir0=[DESC])\n "
26- + " LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4],"
27- + " SAL=[$5], COMM=[$6], DEPTNO=[$7], __reverse_row_num__=[ROW_NUMBER() OVER ()])\n "
28- + " LogicalTableScan(table=[[scott, EMP]])\n " ;
22+ "LogicalSort(sort0=[$0], dir0=[DESC])\n " + " LogicalTableScan(table=[[scott, EMP]])\n " ;
2923 verifyLogical (root , expectedLogical );
3024
3125 String expectedResult =
@@ -60,67 +54,47 @@ public void testReverseParserSuccess() {
6054 verifyResult (root , expectedResult );
6155
6256 String expectedSparkSql =
63- ""
64- + "SELECT `EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`\n "
65- + "FROM (SELECT `EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`,"
66- + " ROW_NUMBER() OVER () `__reverse_row_num__`\n "
67- + "FROM `scott`.`EMP`\n "
68- + "ORDER BY 9 DESC NULLS FIRST) `t0`" ;
57+ "SELECT *\n " + "FROM `scott`.`EMP`\n " + "ORDER BY `EMPNO` DESC NULLS FIRST" ;
6958 verifyPPLToSparkSQL (root , expectedSparkSql );
7059 }
7160
7261 @ Test
7362 public void testReverseWithSortParserSuccess () {
7463 String ppl = "source=EMP | sort ENAME | reverse" ;
7564 RelNode root = getRelNode (ppl );
65+ // Optimization rule may show double sorts in logical plan but physical execution is optimized
7666 String expectedLogical =
77- ""
78- + "LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5],"
79- + " COMM=[$6], DEPTNO=[$7])\n "
80- + " LogicalSort(sort0=[$8], dir0=[DESC])\n "
81- + " LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4],"
82- + " SAL=[$5], COMM=[$6], DEPTNO=[$7], __reverse_row_num__=[ROW_NUMBER() OVER ()])\n "
83- + " LogicalSort(sort0=[$1], dir0=[ASC-nulls-first])\n "
84- + " LogicalTableScan(table=[[scott, EMP]])\n " ;
67+ "LogicalSort(sort0=[$1], dir0=[DESC-nulls-last])\n "
68+ + " LogicalSort(sort0=[$1], dir0=[ASC-nulls-first])\n "
69+ + " LogicalTableScan(table=[[scott, EMP]])\n " ;
8570 verifyLogical (root , expectedLogical );
8671
8772 String expectedSparkSql =
88- ""
89- + "SELECT `EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`\n "
90- + "FROM (SELECT `EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`,"
91- + " ROW_NUMBER() OVER () `__reverse_row_num__`\n "
73+ "SELECT *\n "
74+ + "FROM (SELECT `EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`\n "
9275 + "FROM `scott`.`EMP`\n "
93- + "ORDER BY `ENAME`) `t0 `\n "
94- + "ORDER BY `__reverse_row_num__ ` DESC NULLS FIRST " ;
76+ + "ORDER BY `ENAME`) `t `\n "
77+ + "ORDER BY `ENAME ` DESC" ;
9578 verifyPPLToSparkSQL (root , expectedSparkSql );
9679 }
9780
9881 @ Test
9982 public void testDoubleReverseParserSuccess () {
10083 String ppl = "source=EMP | reverse | reverse" ;
10184 RelNode root = getRelNode (ppl );
85+ // Without optimization rule, shows consecutive sorts
10286 String expectedLogical =
103- ""
104- + "LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5],"
105- + " COMM=[$6], DEPTNO=[$7])\n "
106- + " LogicalSort(sort0=[$8], dir0=[DESC])\n "
107- + " LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4],"
108- + " SAL=[$5], COMM=[$6], DEPTNO=[$7], __reverse_row_num__=[ROW_NUMBER() OVER ()])\n "
109- + " LogicalSort(sort0=[$8], dir0=[DESC])\n "
110- + " LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4],"
111- + " SAL=[$5], COMM=[$6], DEPTNO=[$7], __reverse_row_num__=[ROW_NUMBER() OVER ()])\n "
112- + " LogicalTableScan(table=[[scott, EMP]])\n " ;
87+ "LogicalSort(sort0=[$0], dir0=[ASC])\n "
88+ + " LogicalSort(sort0=[$0], dir0=[DESC])\n "
89+ + " LogicalTableScan(table=[[scott, EMP]])\n " ;
11390 verifyLogical (root , expectedLogical );
11491
11592 String expectedSparkSql =
116- "SELECT `EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`\n "
117- + "FROM (SELECT `EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`,"
118- + " ROW_NUMBER() OVER () `__reverse_row_num__`\n "
119- + "FROM (SELECT `EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`,"
120- + " ROW_NUMBER() OVER () `__reverse_row_num__`\n "
93+ "SELECT *\n "
94+ + "FROM (SELECT `EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`\n "
12195 + "FROM `scott`.`EMP`\n "
122- + "ORDER BY 9 DESC NULLS FIRST) `t0 `\n "
123- + "ORDER BY 9 DESC NULLS FIRST) `t2` " ;
96+ + "ORDER BY `EMPNO` DESC NULLS FIRST) `t `\n "
97+ + "ORDER BY `EMPNO` NULLS LAST " ;
12498 verifyPPLToSparkSQL (root , expectedSparkSql );
12599 }
126100
@@ -129,13 +103,8 @@ public void testReverseWithHeadParserSuccess() {
129103 String ppl = "source=EMP | reverse | head 2" ;
130104 RelNode root = getRelNode (ppl );
131105 String expectedLogical =
132- ""
133- + "LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5],"
134- + " COMM=[$6], DEPTNO=[$7])\n "
135- + " LogicalSort(sort0=[$8], dir0=[DESC], fetch=[2])\n "
136- + " LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4],"
137- + " SAL=[$5], COMM=[$6], DEPTNO=[$7], __reverse_row_num__=[ROW_NUMBER() OVER ()])\n "
138- + " LogicalTableScan(table=[[scott, EMP]])\n " ;
106+ "LogicalSort(sort0=[$0], dir0=[DESC], fetch=[2])\n "
107+ + " LogicalTableScan(table=[[scott, EMP]])\n " ;
139108 verifyLogical (root , expectedLogical );
140109
141110 String expectedResult =
@@ -146,12 +115,7 @@ public void testReverseWithHeadParserSuccess() {
146115 verifyResult (root , expectedResult );
147116
148117 String expectedSparkSql =
149- "SELECT `EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`\n "
150- + "FROM (SELECT `EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`,"
151- + " ROW_NUMBER() OVER () `__reverse_row_num__`\n "
152- + "FROM `scott`.`EMP`\n "
153- + "ORDER BY 9 DESC NULLS FIRST\n "
154- + "LIMIT 2) `t0`" ;
118+ "SELECT *\n " + "FROM `scott`.`EMP`\n " + "ORDER BY `EMPNO` DESC NULLS FIRST\n " + "LIMIT 2" ;
155119 verifyPPLToSparkSQL (root , expectedSparkSql );
156120 }
157121
@@ -178,4 +142,114 @@ public void testReverseWithExpressionShouldFail() {
178142 String ppl = "source=EMP | reverse EMPNO + 1" ;
179143 getRelNode (ppl );
180144 }
145+
146+ @ Test
147+ public void testMultipleSortsWithReverseParserSuccess () {
148+ String ppl = "source=EMP | sort + SAL | sort - ENAME | reverse" ;
149+ RelNode root = getRelNode (ppl );
150+ String expectedLogical =
151+ "LogicalSort(sort0=[$1], dir0=[ASC-nulls-first])\n "
152+ + " LogicalSort(sort0=[$1], dir0=[DESC-nulls-last])\n "
153+ + " LogicalSort(sort0=[$5], dir0=[ASC-nulls-first])\n "
154+ + " LogicalTableScan(table=[[scott, EMP]])\n " ;
155+ verifyLogical (root , expectedLogical );
156+
157+ String expectedSparkSql =
158+ "SELECT *\n "
159+ + "FROM (SELECT `EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`\n "
160+ + "FROM (SELECT `EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`\n "
161+ + "FROM `scott`.`EMP`\n "
162+ + "ORDER BY `SAL`) `t`\n "
163+ + "ORDER BY `ENAME` DESC) `t0`\n "
164+ + "ORDER BY `ENAME`" ;
165+ verifyPPLToSparkSQL (root , expectedSparkSql );
166+ }
167+
168+ @ Test
169+ public void testMultiFieldSortWithReverseParserSuccess () {
170+ String ppl = "source=EMP | sort + SAL, - ENAME | reverse" ;
171+ RelNode root = getRelNode (ppl );
172+ String expectedLogical =
173+ "LogicalSort(sort0=[$5], sort1=[$1], dir0=[DESC-nulls-last], dir1=[ASC-nulls-first])\n "
174+ + " LogicalSort(sort0=[$5], sort1=[$1], dir0=[ASC-nulls-first],"
175+ + " dir1=[DESC-nulls-last])\n "
176+ + " LogicalTableScan(table=[[scott, EMP]])\n " ;
177+ verifyLogical (root , expectedLogical );
178+
179+ String expectedSparkSql =
180+ "SELECT *\n "
181+ + "FROM (SELECT `EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`\n "
182+ + "FROM `scott`.`EMP`\n "
183+ + "ORDER BY `SAL`, `ENAME` DESC) `t`\n "
184+ + "ORDER BY `SAL` DESC, `ENAME`" ;
185+ verifyPPLToSparkSQL (root , expectedSparkSql );
186+ }
187+
188+ @ Test
189+ public void testComplexMultiFieldSortWithReverseParserSuccess () {
190+ String ppl = "source=EMP | sort DEPTNO, + SAL, - ENAME | reverse" ;
191+ RelNode root = getRelNode (ppl );
192+ String expectedLogical =
193+ "LogicalSort(sort0=[$7], sort1=[$5], sort2=[$1], dir0=[DESC-nulls-last],"
194+ + " dir1=[DESC-nulls-last], dir2=[ASC-nulls-first])\n "
195+ + " LogicalSort(sort0=[$7], sort1=[$5], sort2=[$1], dir0=[ASC-nulls-first],"
196+ + " dir1=[ASC-nulls-first], dir2=[DESC-nulls-last])\n "
197+ + " LogicalTableScan(table=[[scott, EMP]])\n " ;
198+ verifyLogical (root , expectedLogical );
199+
200+ String expectedSparkSql =
201+ "SELECT *\n "
202+ + "FROM (SELECT `EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`\n "
203+ + "FROM `scott`.`EMP`\n "
204+ + "ORDER BY `DEPTNO`, `SAL`, `ENAME` DESC) `t`\n "
205+ + "ORDER BY `DEPTNO` DESC, `SAL` DESC, `ENAME`" ;
206+ verifyPPLToSparkSQL (root , expectedSparkSql );
207+ }
208+
209+ @ Test
210+ public void testReverseWithFieldsAndSortParserSuccess () {
211+ String ppl = "source=EMP | fields ENAME, SAL, DEPTNO | sort + SAL | reverse" ;
212+ RelNode root = getRelNode (ppl );
213+ String expectedLogical =
214+ "LogicalSort(sort0=[$1], dir0=[DESC-nulls-last])\n "
215+ + " LogicalSort(sort0=[$1], dir0=[ASC-nulls-first])\n "
216+ + " LogicalProject(ENAME=[$1], SAL=[$5], DEPTNO=[$7])\n "
217+ + " LogicalTableScan(table=[[scott, EMP]])\n " ;
218+ verifyLogical (root , expectedLogical );
219+
220+ String expectedSparkSql =
221+ "SELECT *\n "
222+ + "FROM (SELECT `ENAME`, `SAL`, `DEPTNO`\n "
223+ + "FROM `scott`.`EMP`\n "
224+ + "ORDER BY `SAL`) `t0`\n "
225+ + "ORDER BY `SAL` DESC" ;
226+ verifyPPLToSparkSQL (root , expectedSparkSql );
227+ }
228+
229+ @ Test
230+ public void testHeadThenSortReverseNoOpt () {
231+ // Tests fetch limit behavior: head 5 | sort field | reverse
232+ // Should NOT be optimized to preserve "take first 5, then sort" semantics
233+ String ppl = "source=EMP | head 5 | sort + SAL | reverse" ;
234+ RelNode root = getRelNode (ppl );
235+
236+ // Should have three LogicalSort nodes: fetch=5, sort SAL, reverse
237+ // Calcite's built-in optimization will handle the physical plan optimization
238+ String expectedLogical =
239+ "LogicalSort(sort0=[$5], dir0=[DESC-nulls-last])\n "
240+ + " LogicalSort(sort0=[$5], dir0=[ASC-nulls-first])\n "
241+ + " LogicalSort(fetch=[5])\n "
242+ + " LogicalTableScan(table=[[scott, EMP]])\n " ;
243+ verifyLogical (root , expectedLogical );
244+
245+ String expectedSparkSql =
246+ "SELECT *\n "
247+ + "FROM (SELECT `EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`\n "
248+ + "FROM (SELECT `EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`\n "
249+ + "FROM `scott`.`EMP`\n "
250+ + "LIMIT 5) `t`\n "
251+ + "ORDER BY `SAL`) `t0`\n "
252+ + "ORDER BY `SAL` DESC" ;
253+ verifyPPLToSparkSQL (root , expectedSparkSql );
254+ }
181255}
0 commit comments