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

chengzhang 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 c4bcc5da2c0 fix:Support for the LAG and LEAD analytical functions 
(#35928)
c4bcc5da2c0 is described below

commit c4bcc5da2c0a6e3cde5c7c7f06b785fd15f06f3e
Author: cxy <[email protected]>
AuthorDate: Wed Jul 9 16:18:03 2025 +0800

    fix:Support for the LAG and LEAD analytical functions (#35928)
    
    * Fix:Full support for xml methods
    
    * Fix:Full support for xml methods
    
    * Fix:Full support for xml methods
    
    * fix:Support for the CHANGETABLE function
    
    * fix:Support for the CHANGETABLE function
    
    * fix:Support for the CHANGETABLE function
    
    * fix:Support for the AI_GENERATE_EMBEDDINGS function
    
    * fix:Support for the LAG and LEAD analytical functions
---
 .../src/main/antlr4/imports/sqlserver/BaseRule.g4  |   5 +
 .../antlr4/imports/sqlserver/SQLServerKeyword.g4   |   8 ++
 .../statement/SQLServerStatementVisitor.java       |  33 ++++-
 .../parser/src/main/resources/case/dml/select.xml  | 152 +++++++++++++++++++++
 .../main/resources/sql/supported/dml/select.xml    |   2 +
 5 files changed, 199 insertions(+), 1 deletion(-)

diff --git 
a/parser/sql/dialect/sqlserver/src/main/antlr4/imports/sqlserver/BaseRule.g4 
b/parser/sql/dialect/sqlserver/src/main/antlr4/imports/sqlserver/BaseRule.g4
index be5f06d959d..de09739a190 100644
--- a/parser/sql/dialect/sqlserver/src/main/antlr4/imports/sqlserver/BaseRule.g4
+++ b/parser/sql/dialect/sqlserver/src/main/antlr4/imports/sqlserver/BaseRule.g4
@@ -501,6 +501,11 @@ convertExpr
 
 windowFunction
     : funcName = (FIRST_VALUE | LAST_VALUE) LP_ expr RP_ nullTreatment? 
overClause
+    | lagLeadFunction
+    ;
+
+lagLeadFunction
+    : funcName = (LAG | LEAD) LP_ expr (COMMA_ expr)? (COMMA_ expr)? RP_ 
nullTreatment? overClause
     ;
 
 nullTreatment
diff --git 
a/parser/sql/dialect/sqlserver/src/main/antlr4/imports/sqlserver/SQLServerKeyword.g4
 
b/parser/sql/dialect/sqlserver/src/main/antlr4/imports/sqlserver/SQLServerKeyword.g4
index 1fbd8317bf7..a648231cce6 100644
--- 
a/parser/sql/dialect/sqlserver/src/main/antlr4/imports/sqlserver/SQLServerKeyword.g4
+++ 
b/parser/sql/dialect/sqlserver/src/main/antlr4/imports/sqlserver/SQLServerKeyword.g4
@@ -2022,3 +2022,11 @@ AI_GENERATE_EMBEDDINGS
 PARAMETERS
     : P A R A M E T E R S
     ;
+
+LAG
+    : L A G
+    ;
+
+LEAD
+    : L E A D
+    ;
diff --git 
a/parser/sql/dialect/sqlserver/src/main/java/org/apache/shardingsphere/sql/parser/sqlserver/visitor/statement/SQLServerStatementVisitor.java
 
b/parser/sql/dialect/sqlserver/src/main/java/org/apache/shardingsphere/sql/parser/sqlserver/visitor/statement/SQLServerStatementVisitor.java
index a46ff082bf4..55c36235d4f 100644
--- 
a/parser/sql/dialect/sqlserver/src/main/java/org/apache/shardingsphere/sql/parser/sqlserver/visitor/statement/SQLServerStatementVisitor.java
+++ 
b/parser/sql/dialect/sqlserver/src/main/java/org/apache/shardingsphere/sql/parser/sqlserver/visitor/statement/SQLServerStatementVisitor.java
@@ -239,6 +239,7 @@ import 
org.apache.shardingsphere.sql.parser.autogen.SQLServerStatementParser.Cha
 import 
org.apache.shardingsphere.sql.parser.autogen.SQLServerStatementParser.XmlMethodCallContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.SQLServerStatementParser.AiFunctionContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.SQLServerStatementParser.AiGenerateEmbeddingsFunctionContext;
+import 
org.apache.shardingsphere.sql.parser.autogen.SQLServerStatementParser.LagLeadFunctionContext;
 
 import java.util.Collection;
 import java.util.Collections;
@@ -835,8 +836,38 @@ public abstract class SQLServerStatementVisitor extends 
SQLServerStatementBaseVi
     
     @Override
     public final ASTNode visitWindowFunction(final WindowFunctionContext ctx) {
+        if (null != ctx.lagLeadFunction()) {
+            return visit(ctx.lagLeadFunction());
+        }
         FunctionSegment result = new 
FunctionSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex(), 
ctx.funcName.getText(), getOriginalText(ctx));
-        result.getParameters().add((ExpressionSegment) visit(ctx.expr()));
+        result.getParameters().add((ExpressionSegment) visit(ctx.getChild(2)));
+        return result;
+    }
+    
+    @Override
+    public final ASTNode visitLagLeadFunction(final LagLeadFunctionContext 
ctx) {
+        FunctionSegment result = new 
FunctionSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex(), 
ctx.funcName.getText(), getOriginalText(ctx));
+        Collection<ExpressionSegment> parameters = 
getLagLeadFunctionParameters(ctx);
+        result.getParameters().addAll(parameters);
+        return result;
+    }
+    
+    private Collection<ExpressionSegment> getLagLeadFunctionParameters(final 
LagLeadFunctionContext ctx) {
+        Collection<ExpressionSegment> result = new LinkedList<>();
+        boolean foundLP = false;
+        for (int i = 0; i < ctx.getChildCount(); i++) {
+            String childText = ctx.getChild(i).getText();
+            if ("(".equals(childText)) {
+                foundLP = true;
+                continue;
+            }
+            if (")".equals(childText) && foundLP) {
+                break;
+            }
+            if (foundLP && ctx.getChild(i) instanceof ExprContext) {
+                result.add((ExpressionSegment) visit(ctx.getChild(i)));
+            }
+        }
         return result;
     }
     
diff --git a/test/it/parser/src/main/resources/case/dml/select.xml 
b/test/it/parser/src/main/resources/case/dml/select.xml
index bec6138a59e..873a6c6531c 100644
--- a/test/it/parser/src/main/resources/case/dml/select.xml
+++ b/test/it/parser/src/main/resources/case/dml/select.xml
@@ -10396,4 +10396,156 @@
             <simple-table name="myTable" start-index="80" stop-index="86" />
         </from>
     </select>
+
+    <select sql-case-id="select_lag_over">
+        <projections start-index="7" stop-index="151">
+            <column-projection name="BusinessEntityID" start-index="7" 
stop-index="22" />
+            <expression-projection text="YEAR(QuotaDate)" alias="SalesYear" 
start-index="25" stop-index="52">
+                <expr>
+                    <function function-name="YEAR" text="YEAR(QuotaDate)" 
start-index="25" stop-index="39">
+                        <parameter>
+                            <column name="QuotaDate" start-index="30" 
stop-index="38" />
+                        </parameter>
+                    </function>
+                </expr>
+            </expression-projection>
+            <column-projection name="SalesQuota" alias="CurrentQuota" 
start-index="55" stop-index="80" />
+            <expression-projection text="LAG(SalesQuota, 1,0) OVER (ORDER BY 
YEAR(QuotaDate))" alias="PreviousQuota" start-index="83" stop-index="151">
+                <expr>
+                    <function function-name="LAG" text="LAG(SalesQuota, 1,0) 
OVER (ORDER BY YEAR(QuotaDate))" start-index="83" stop-index="134">
+                        <parameter>
+                            <column name="SalesQuota" start-index="87" 
stop-index="96" />
+                        </parameter>
+                        <parameter>
+                            <literal-expression value="1" start-index="99" 
stop-index="99" />
+                        </parameter>
+                        <parameter>
+                            <literal-expression value="0" start-index="101" 
stop-index="101" />
+                        </parameter>
+                    </function>
+                </expr>
+            </expression-projection>
+        </projections>
+        <from>
+            <simple-table name="SalesPersonQuotaHistory" start-index="158" 
stop-index="186">
+                <owner name="Sales" start-index="158" stop-index="162" />
+            </simple-table>
+        </from>
+        <where start-index="188" stop-index="254">
+            <expr start-index="194" stop-index="254">
+                <binary-operation-expression start-index="194" 
stop-index="254">
+                    <left>
+                        <binary-operation-expression start-index="194" 
stop-index="215">
+                            <left>
+                                <column name="BusinessEntityID" 
start-index="194" stop-index="209" />
+                            </left>
+                            <right>
+                                <literal-expression value="275" 
start-index="213" stop-index="215" />
+                            </right>
+                            <operator>=</operator>
+                        </binary-operation-expression>
+                    </left>
+                    <right>
+                        <in-expression start-index="221" stop-index="254">
+                            <not>false</not>
+                            <left>
+                                <function function-name="YEAR" 
text="YEAR(QuotaDate)" start-index="221" stop-index="235">
+                                    <parameter>
+                                        <column name="QuotaDate" 
start-index="226" stop-index="234" />
+                                    </parameter>
+                                </function>
+                            </left>
+                            <right>
+                                <list-expression start-index="240" 
stop-index="254">
+                                    <items>
+                                        <literal-expression value="2005" 
start-index="241" stop-index="246" />
+                                    </items>
+                                    <items>
+                                        <literal-expression value="2006" 
start-index="248" stop-index="253" />
+                                    </items>
+                                </list-expression>
+                            </right>
+                        </in-expression>
+                    </right>
+                    <operator>AND</operator>
+                </binary-operation-expression>
+            </expr>
+        </where>
+    </select>
+
+    <select sql-case-id="select_lead_over">
+        <projections start-index="7" stop-index="148">
+            <column-projection name="BusinessEntityID" start-index="7" 
stop-index="22" />
+            <expression-projection text="YEAR(QuotaDate)" alias="SalesYear" 
start-index="25" stop-index="52">
+                <expr>
+                    <function function-name="YEAR" text="YEAR(QuotaDate)" 
start-index="25" stop-index="39">
+                        <parameter>
+                            <column name="QuotaDate" start-index="30" 
stop-index="38" />
+                        </parameter>
+                    </function>
+                </expr>
+            </expression-projection>
+            <column-projection name="SalesQuota" alias="CurrentQuota" 
start-index="55" stop-index="80" />
+            <expression-projection text="LEAD(SalesQuota, 1,0) OVER (ORDER BY 
YEAR(QuotaDate))" alias="NextQuota" start-index="83" stop-index="148">
+                <expr>
+                    <function function-name="LEAD" text="LEAD(SalesQuota, 1,0) 
OVER (ORDER BY YEAR(QuotaDate))" start-index="83" stop-index="135">
+                        <parameter>
+                            <column name="SalesQuota" start-index="88" 
stop-index="97" />
+                        </parameter>
+                        <parameter>
+                            <literal-expression value="1" start-index="100" 
stop-index="100" />
+                        </parameter>
+                        <parameter>
+                            <literal-expression value="0" start-index="102" 
stop-index="102" />
+                        </parameter>
+                    </function>
+                </expr>
+            </expression-projection>
+        </projections>
+        <from>
+            <simple-table name="SalesPersonQuotaHistory" start-index="155" 
stop-index="183">
+                <owner name="Sales" start-index="155" stop-index="159" />
+            </simple-table>
+        </from>
+        <where start-index="185" stop-index="251">
+            <expr start-index="191" stop-index="251">
+                <binary-operation-expression start-index="191" 
stop-index="251">
+                    <left>
+                        <binary-operation-expression start-index="191" 
stop-index="212">
+                            <left>
+                                <column name="BusinessEntityID" 
start-index="191" stop-index="206" />
+                            </left>
+                            <right>
+                                <literal-expression value="275" 
start-index="210" stop-index="212" />
+                            </right>
+                            <operator>=</operator>
+                        </binary-operation-expression>
+                    </left>
+                    <right>
+                        <in-expression start-index="218" stop-index="251">
+                            <not>false</not>
+                            <left>
+                                <function function-name="YEAR" 
text="YEAR(QuotaDate)" start-index="218" stop-index="232">
+                                    <parameter>
+                                        <column name="QuotaDate" 
start-index="223" stop-index="231" />
+                                    </parameter>
+                                </function>
+                            </left>
+                            <right>
+                                <list-expression start-index="237" 
stop-index="251">
+                                    <items>
+                                        <literal-expression value="2005" 
start-index="238" stop-index="243" />
+                                    </items>
+                                    <items>
+                                        <literal-expression value="2006" 
start-index="245" stop-index="250" />
+                                    </items>
+                                </list-expression>
+                            </right>
+                        </in-expression>
+                    </right>
+                    <operator>AND</operator>
+                </binary-operation-expression>
+            </expr>
+        </where>
+    </select>
 </sql-parser-test-cases>
diff --git a/test/it/parser/src/main/resources/sql/supported/dml/select.xml 
b/test/it/parser/src/main/resources/sql/supported/dml/select.xml
index 1d4ba111223..b2c00f4267a 100644
--- a/test/it/parser/src/main/resources/sql/supported/dml/select.xml
+++ b/test/it/parser/src/main/resources/sql/supported/dml/select.xml
@@ -325,4 +325,6 @@
     <sql-case id="select_instruction_nodes" value="SELECT T2.Loc.query('.') 
FROM T CROSS APPLY Instructions.nodes('/root/Location') AS T2(Loc) " 
db-types="SQLServer"/>
     <sql-case id="select_cross_apply_changetable_version" value="SELECT e.[Emp 
ID], e.SSN, e.FirstName, e.LastName, c.SYS_CHANGE_VERSION, c.SYS_CHANGE_CONTEXT 
FROM Employees AS e CROSS APPLY CHANGETABLE (VERSION Employees, ([Emp ID], 
SSN), (e.[Emp ID], e.SSN)) AS c;" db-types="SQLServer"/>
     <sql-case id="select_ai_generate_embeddings" value="SELECT id, 
AI_GENERATE_EMBEDDINGS(large_text USE MODEL MyAzureOpenAIModel) FROM myTable;" 
db-types="SQLServer"/>
+    <sql-case id="select_lag_over" value="SELECT BusinessEntityID, 
YEAR(QuotaDate) AS SalesYear, SalesQuota AS CurrentQuota, LAG(SalesQuota, 1,0) 
OVER (ORDER BY YEAR(QuotaDate)) AS PreviousQuota FROM 
Sales.SalesPersonQuotaHistory WHERE BusinessEntityID = 275 AND YEAR(QuotaDate) 
IN ('2005','2006'); " db-types="SQLServer"/>
+    <sql-case id="select_lead_over" value="SELECT BusinessEntityID, 
YEAR(QuotaDate) AS SalesYear, SalesQuota AS CurrentQuota, LEAD(SalesQuota, 1,0) 
OVER (ORDER BY YEAR(QuotaDate)) AS NextQuota FROM Sales.SalesPersonQuotaHistory 
WHERE BusinessEntityID = 275 AND YEAR(QuotaDate) IN ('2005','2006'); " 
db-types="SQLServer"/>
 </sql-cases>

Reply via email to