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

terrymanu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git


The following commit(s) were added to refs/heads/master by this push:
     new 1f1aa6d766d Fix SQL Server row number column binding (#39229)
1f1aa6d766d is described below

commit 1f1aa6d766d031da6fd911c04654fdaf31b8ee90
Author: Liang Zhang <[email protected]>
AuthorDate: Fri Jul 24 12:57:15 2026 +0800

    Fix SQL Server row number column binding (#39229)
    
    * Fix SQL Server row number column binding
    
    Remove row-number identifiers from unparenthesized function metadata and 
preserve normalized TOP projection aliases for binding and pagination.
    
    * Fix SQL Server row number column binding
    
    Remove row-number identifiers from unparenthesized function metadata and 
preserve normalized TOP projection aliases for binding and pagination.
---
 .../database/option/SQLServerFunctionOption.java   |  3 +-
 .../option/SQLServerFunctionOptionTest.java        | 21 ++++++----
 .../statement/SQLServerStatementVisitor.java       |  5 ++-
 .../dml/pagination/top/TopProjectionSegment.java   |  3 +-
 .../pagination/top/TopProjectionSegmentTest.java}  | 36 ++++++++--------
 .../binder/src/test/resources/cases/dml/select.xml | 48 ++++++++++++++++++++++
 .../binder/src/test/resources/sqls/dml/select.xml  |  1 +
 7 files changed, 83 insertions(+), 34 deletions(-)

diff --git 
a/database/connector/dialect/sqlserver/src/main/java/org/apache/shardingsphere/database/connector/sql92/sqlserver/metadata/database/option/SQLServerFunctionOption.java
 
b/database/connector/dialect/sqlserver/src/main/java/org/apache/shardingsphere/database/connector/sql92/sqlserver/metadata/database/option/SQLServerFunctionOption.java
index 6338d9a6952..17db2ea54bb 100644
--- 
a/database/connector/dialect/sqlserver/src/main/java/org/apache/shardingsphere/database/connector/sql92/sqlserver/metadata/database/option/SQLServerFunctionOption.java
+++ 
b/database/connector/dialect/sqlserver/src/main/java/org/apache/shardingsphere/database/connector/sql92/sqlserver/metadata/database/option/SQLServerFunctionOption.java
@@ -28,9 +28,8 @@ import java.util.Collection;
  */
 public final class SQLServerFunctionOption implements DialectFunctionOption {
     
-    // TODO remove ROWNUM, ROWNUM_ and ROW_NUMBER @duanzhengqiang
     private static final Collection<String> UNPARENTHESIZED_FUNCTION_NAMES = 
new CaseInsensitiveSet<>(Arrays.asList(
-            "CURRENT_TIMESTAMP", "CURRENT_USER", "ROWNUM", "ROWNUM_", 
"ROW_NUMBER", "SESSION_USER", "SYSTEM_USER", "USER"));
+            "CURRENT_TIMESTAMP", "CURRENT_USER", "SESSION_USER", 
"SYSTEM_USER", "USER"));
     
     @Override
     public Collection<String> getUnparenthesizedFunctionNames() {
diff --git 
a/database/connector/dialect/sqlserver/src/test/java/org/apache/shardingsphere/database/connector/sql92/sqlserver/metadata/database/option/SQLServerFunctionOptionTest.java
 
b/database/connector/dialect/sqlserver/src/test/java/org/apache/shardingsphere/database/connector/sql92/sqlserver/metadata/database/option/SQLServerFunctionOptionTest.java
index a36e374ea5f..3e713a1a6ee 100644
--- 
a/database/connector/dialect/sqlserver/src/test/java/org/apache/shardingsphere/database/connector/sql92/sqlserver/metadata/database/option/SQLServerFunctionOptionTest.java
+++ 
b/database/connector/dialect/sqlserver/src/test/java/org/apache/shardingsphere/database/connector/sql92/sqlserver/metadata/database/option/SQLServerFunctionOptionTest.java
@@ -19,6 +19,12 @@ package 
org.apache.shardingsphere.database.connector.sql92.sqlserver.metadata.da
 
 import org.junit.jupiter.api.Test;
 
+import java.util.Arrays;
+import java.util.Collection;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 class SQLServerFunctionOptionTest {
@@ -27,13 +33,12 @@ class SQLServerFunctionOptionTest {
     
     @Test
     void assertGetUnparenthesizedFunctionNames() {
-        
assertTrue(functionOption.getUnparenthesizedFunctionNames().contains("CURRENT_TIMESTAMP"));
-        
assertTrue(functionOption.getUnparenthesizedFunctionNames().contains("CURRENT_USER"));
-        
assertTrue(functionOption.getUnparenthesizedFunctionNames().contains("ROWNUM"));
-        
assertTrue(functionOption.getUnparenthesizedFunctionNames().contains("ROWNUM_"));
-        
assertTrue(functionOption.getUnparenthesizedFunctionNames().contains("ROW_NUMBER"));
-        
assertTrue(functionOption.getUnparenthesizedFunctionNames().contains("SESSION_USER"));
-        
assertTrue(functionOption.getUnparenthesizedFunctionNames().contains("SYSTEM_USER"));
-        
assertTrue(functionOption.getUnparenthesizedFunctionNames().contains("USER"));
+        Collection<String> actual = 
functionOption.getUnparenthesizedFunctionNames();
+        assertThat(actual.size(), is(5));
+        assertTrue(actual.containsAll(Arrays.asList("CURRENT_TIMESTAMP", 
"CURRENT_USER", "SESSION_USER", "SYSTEM_USER", "USER")));
+        assertTrue(actual.contains("current_timestamp"));
+        assertFalse(actual.contains("ROWNUM"));
+        assertFalse(actual.contains("ROWNUM_"));
+        assertFalse(actual.contains("ROW_NUMBER"));
     }
 }
diff --git 
a/parser/sql/engine/dialect/sqlserver/src/main/java/org/apache/shardingsphere/sql/parser/engine/sqlserver/visitor/statement/SQLServerStatementVisitor.java
 
b/parser/sql/engine/dialect/sqlserver/src/main/java/org/apache/shardingsphere/sql/parser/engine/sqlserver/visitor/statement/SQLServerStatementVisitor.java
index a8a8bd532f9..cfefb57ecc8 100644
--- 
a/parser/sql/engine/dialect/sqlserver/src/main/java/org/apache/shardingsphere/sql/parser/engine/sqlserver/visitor/statement/SQLServerStatementVisitor.java
+++ 
b/parser/sql/engine/dialect/sqlserver/src/main/java/org/apache/shardingsphere/sql/parser/engine/sqlserver/visitor/statement/SQLServerStatementVisitor.java
@@ -1921,13 +1921,14 @@ public abstract class SQLServerStatementVisitor extends 
SQLServerStatementBaseVi
         int startIndex = ctx.topNum().getStart().getStartIndex();
         int stopIndex = ctx.topNum().getStop().getStopIndex();
         ASTNode topNum = visit(ctx.topNum());
+        String alias = null == ctx.alias() ? null : ((AliasSegment) 
visit(ctx.alias())).getIdentifier().getValue();
         if (topNum instanceof NumberLiteralValue) {
             NumberLiteralRowNumberValueSegment rowNumberSegment = new 
NumberLiteralRowNumberValueSegment(startIndex, stopIndex, ((NumberLiteralValue) 
topNum).getValue().longValue(), false);
-            return new TopProjectionSegment(ctx.getStart().getStartIndex(), 
ctx.getStop().getStopIndex(), rowNumberSegment, null != ctx.alias() ? 
ctx.alias().getText() : null);
+            return new TopProjectionSegment(ctx.getStart().getStartIndex(), 
ctx.getStop().getStopIndex(), rowNumberSegment, alias);
         }
         ParameterMarkerSegment parameterSegment = new 
ParameterMarkerRowNumberValueSegment(startIndex, stopIndex, 
((ParameterMarkerValue) topNum).getValue(), false);
         parameterMarkerSegments.add(parameterSegment);
-        return new TopProjectionSegment(ctx.getStart().getStartIndex(), 
ctx.getStop().getStopIndex(), (RowNumberValueSegment) parameterSegment, null != 
ctx.alias() ? ctx.alias().getText() : null);
+        return new TopProjectionSegment(ctx.getStart().getStartIndex(), 
ctx.getStop().getStopIndex(), (RowNumberValueSegment) parameterSegment, alias);
     }
     
     @Override
diff --git 
a/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/pagination/top/TopProjectionSegment.java
 
b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/pagination/top/TopProjectionSegment.java
index 9bf43f391c0..fd2c17d5c90 100644
--- 
a/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/pagination/top/TopProjectionSegment.java
+++ 
b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/pagination/top/TopProjectionSegment.java
@@ -39,7 +39,6 @@ public final class TopProjectionSegment implements 
ProjectionSegment {
     
     @Override
     public String getColumnLabel() {
-        // TODO return column label according to database result
-        return "TOP";
+        return null == alias || alias.isEmpty() ? "TOP" : alias;
     }
 }
diff --git 
a/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/pagination/top/TopProjectionSegment.java
 
b/parser/sql/statement/core/src/test/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/pagination/top/TopProjectionSegmentTest.java
similarity index 56%
copy from 
parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/pagination/top/TopProjectionSegment.java
copy to 
parser/sql/statement/core/src/test/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/pagination/top/TopProjectionSegmentTest.java
index 9bf43f391c0..bb65ee80ba4 100644
--- 
a/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/pagination/top/TopProjectionSegment.java
+++ 
b/parser/sql/statement/core/src/test/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/pagination/top/TopProjectionSegmentTest.java
@@ -17,29 +17,25 @@
 
 package 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.pagination.top;
 
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ProjectionSegment;
-import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.pagination.rownum.RowNumberValueSegment;
+import org.junit.jupiter.api.Test;
 
-/**
- * Top projection segment.
- */
-@RequiredArgsConstructor
-@Getter
-public final class TopProjectionSegment implements ProjectionSegment {
-    
-    private final int startIndex;
-    
-    private final int stopIndex;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+class TopProjectionSegmentTest {
     
-    private final RowNumberValueSegment top;
+    @Test
+    void assertGetColumnLabelWithoutAlias() {
+        assertThat(new TopProjectionSegment(0, 0, null, 
null).getColumnLabel(), is("TOP"));
+    }
     
-    private final String alias;
+    @Test
+    void assertGetColumnLabelWithEmptyAlias() {
+        assertThat(new TopProjectionSegment(0, 0, null, "").getColumnLabel(), 
is("TOP"));
+    }
     
-    @Override
-    public String getColumnLabel() {
-        // TODO return column label according to database result
-        return "TOP";
+    @Test
+    void assertGetColumnLabelWithAlias() {
+        assertThat(new TopProjectionSegment(0, 0, null, 
"rownum_").getColumnLabel(), is("rownum_"));
     }
 }
diff --git a/test/it/binder/src/test/resources/cases/dml/select.xml 
b/test/it/binder/src/test/resources/cases/dml/select.xml
index 99d21a4d5ff..5f29a1ca41a 100644
--- a/test/it/binder/src/test/resources/cases/dml/select.xml
+++ b/test/it/binder/src/test/resources/cases/dml/select.xml
@@ -1766,4 +1766,52 @@
             <parameter-marker-expression parameter-index="0" start-index="147" 
stop-index="147" />
         </parameters>
     </select>
+    
+    <select sql-case-id="select_with_top_and_delimited_row_number_alias" 
parameters="10, 5">
+        <projections start-index="7" stop-index="20">
+            <column-projection name="rownum_" start-index="7" stop-index="20" 
start-delimiter="[" end-delimiter="]">
+                <owner name="row_" start-index="7" stop-index="10" />
+            </column-projection>
+        </projections>
+        <from>
+            <subquery-table alias="row_" start-index="27" stop-index="115">
+                <subquery>
+                    <select>
+                        <projections start-index="35" stop-index="94">
+                            <top-projection alias="rownum_" start-index="35" 
stop-index="94">
+                                <top-value value="10" parameter-index="0" 
start-index="40" stop-index="40" />
+                            </top-projection>
+                        </projections>
+                        <from>
+                            <simple-table name="t_order" alias="o" 
start-index="101" stop-index="109">
+                                <table-bound>
+                                    <original-database name="foo_db_1" />
+                                    <original-schema name="dbo" />
+                                </table-bound>
+                            </simple-table>
+                        </from>
+                    </select>
+                </subquery>
+            </subquery-table>
+        </from>
+        <where start-index="117" stop-index="140">
+            <expr>
+                <binary-operation-expression start-index="123" 
stop-index="140">
+                    <left>
+                        <column name="rownum_" start-index="123" 
stop-index="136" start-delimiter="[" end-delimiter="]">
+                            <owner name="row_" start-index="123" 
stop-index="126" />
+                        </column>
+                    </left>
+                    <operator>&gt;</operator>
+                    <right>
+                        <parameter-marker-expression parameter-index="1" 
start-index="140" stop-index="140" />
+                    </right>
+                </binary-operation-expression>
+            </expr>
+        </where>
+        <parameters>
+            <parameter-marker-expression parameter-index="0" start-index="40" 
stop-index="40" />
+            <parameter-marker-expression parameter-index="1" start-index="140" 
stop-index="140" />
+        </parameters>
+    </select>
 </sql-parser-test-cases>
diff --git a/test/it/binder/src/test/resources/sqls/dml/select.xml 
b/test/it/binder/src/test/resources/sqls/dml/select.xml
index 6f807a4fb90..ef3b0a22380 100644
--- a/test/it/binder/src/test/resources/sqls/dml/select.xml
+++ b/test/it/binder/src/test/resources/sqls/dml/select.xml
@@ -32,4 +32,5 @@
     <sql-case id="select_with_left_join_where_outer_column" value="SELECT 
t1.user_id, t1.user_name, t2.order_id, t2.creation_date FROM t_user t1 LEFT 
JOIN t_order t2 ON t1.user_id = t2.user_id WHERE t1.user_name = ?" 
db-types="MySQL" case-types="PLACEHOLDER"/>
     <sql-case id="select_with_left_join_having_outer_column" value="SELECT 
COUNT(*) AS count FROM t_order o LEFT JOIN t_user u ON o.user_id = u.user_id 
GROUP BY u.user_id HAVING u.user_id IS NOT NULL" db-types="MySQL"/>
     <sql-case id="select_with_outer_join_operator" value="SELECT o.order_id 
FROM t_order o, t_order_item i WHERE o.order_id = i.order_id(+)" 
db-types="Oracle"/>
+    <sql-case id="select_with_top_and_delimited_row_number_alias" 
value="SELECT row_.[rownum_] FROM (SELECT TOP (?) ROW_NUMBER() OVER (ORDER BY 
o.order_id) AS [rownum_] FROM t_order o) row_ WHERE row_.[rownum_] &gt; ?" 
db-types="SQLServer" case-types="PLACEHOLDER"/>
 </sql-cases>

Reply via email to