Skip to content

Commit b317381

Browse files
authored
Sync repos: Release 180.18.1 (#204)
* Merged PR 2053371: [SECURITY] Bump dotnet-sdk from 8.0.419 to 8.0.420 * Merged PR 2040726: Fix invalid OVER clause parsing - reject OVER([col]) syntax <!-- COPILOT_AI_GENERATED_START --> # Fix invalid OVER clause parsing - reject OVER([col]) syntax ## Summary Fixed a parser bug where invalid OVER clause syntax like `COUNT(*) OVER([col])` was incorrectly accepted. The parser now properly rejects OVER clauses that contain only an identifier in parentheses without PARTITION BY or ORDER BY clauses. ## Problem The parser was accepting invalid syntax such as: ```sql SELECT COUNT(*) OVER([col]) FROM t1 SELECT SUM(Amount) OVER(MyColumn) FROM t1 ``` This is invalid because an OVER clause with parentheses must contain one of: - `PARTITION BY` clause - `ORDER BY` clause - Window frame clause - Or be empty: `OVER()` Valid window name references (introduced in SQL Server 2022) must be written WITHOUT parentheses: ```sql SELECT SUM(c1) OVER Win1 FROM t1 WINDOW Win1 AS (PARTITION BY c1) ``` Additionally, partial window specifications CAN include a window name inside parentheses, but ONLY when combined with other clauses: ```sql SELECT SUM(c1) OVER (Win1 ORDER BY c2) FROM t1 WINDOW Win1 AS (PARTITION BY c1) ``` ## Root Cause In SQL Server 2022 (TSql160), support was added for the WINDOW clause feature, which allows referencing named windows. The grammar rule `overClauseBeginning` was modified to accept an optional identifier inside parentheses to support partial window specifications like `OVER (Win1 PARTITION BY ...)`. However, the rule `overClauseNoOrderBy` (used by aggregate functions like COUNT, SUM, AVG) was still using `overClauseBeginning`, which meant it would accept `OVER(identifier)` and interpret the identifier as a window name, even though no other clauses were present. This resulted in invalid syntax being accepted. ## Solution Created a new grammar rule `overClauseBeginningNoWindowName` that does not allow window name identifiers inside parentheses. Modified `overClauseNoOrderBy` to use this new rule instead of `overClauseBeginning`. The new rule uses explicit alternatives instead of an optional clause to ensure ANTLR properly rejects identifiers that aren&#39;t followed by the `BY` keyword: ```antlr overClauseBeginningNoWindowName: OVER LeftParenthesis ( Identifier { Match(&quot;PARTITION&quot;) } BY expressionList | /* empty - allow OVER() with no PARTITION BY */ ) ``` This ensures that when parsing `OVER([col])`, the parser will: 1. Try to match the PARTITION BY alternative 2. See the identifier but realize it&#39;s not followed by BY 3. Try the empty alternative 4. Fail because `[col]` is present when nothing was expected 5. Generate an error ## Files Modified - `SqlScriptDom/Parser/TSql/TSql160.g` - Added `overClauseBeginningNoWindowName` rule and updated `overClauseNoOrderBy` - `SqlScriptDom/Parser/TSql/TSql170.g` - Same changes as TSql160.g - `SqlScriptDom/Parser/TSql/TSql180.g` - Same changes as TSql180.g - `Test/SqlDom/ParserErrorsTests.cs` - Added `InvalidOverClauseNegative... * Merged PR 2072841: Adding release notes for 180.18.1 Adding release notes for 180.18.1 ---- #### AI description (iteration 1) #### PR Classification Documentation update adding release notes for version 180.18.1 of Microsoft.SqlServer.TransactSql.ScriptDom. #### PR Summary This pull request adds comprehensive release notes for version 180.18.1, documenting bug fixes, dependency updates, and changes to the SQL script generator. - `/release-notes/180/180.18.1.md`: Documents platform support for .NET Framework 4.7.2, .NET 8, and .NET Standard 2.0+ - `/release-notes/180/180.18.1.md`: Lists five parser bug fixes including TRIM clause parsing, DATEADD/DATEDIFF/DATEPART function argument handling, and OVER clause validation - `/release-notes/180/180.18.1.md`: Notes .NET SDK update to version 8.0.420 and script generator improvement for semicolon placement before trailing comments <!-- GitOpsUserAgent=GitOps.Apps.Server.pullrequestcopilot --> --------- Co-authored-by: Dependabot <Dependabot> Co-authored-by: GitHub Copilot <GitHub Copilot>
1 parent 6e95673 commit b317381

9 files changed

Lines changed: 166 additions & 6 deletions

File tree

SqlScriptDom/Parser/TSql/TSql160.g

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31967,6 +31967,11 @@ overClause returns [OverClause vResult]
3196731967
)?
3196831968
tRParen:RightParenthesis
3196931969
{
31970+
// Window name inside parentheses requires at least one clause (PARTITION BY, ORDER BY, or window frame)
31971+
if (vResult.WindowName != null && vResult.Partitions.Count == 0 && vResult.OrderByClause == null && vResult.WindowFrameClause == null)
31972+
{
31973+
ThrowParseErrorException("SQL46010", tRParen, TSqlParserResource.SQL46010Message, vResult.WindowName.Value);
31974+
}
3197031975
UpdateTokenInfo(vResult,tRParen);
3197131976
}
3197231977
| vResult = overClauseWithWindow
@@ -31989,7 +31994,7 @@ overClauseWithWindow returns [OverClause vResult = FragmentFactory.CreateFragmen
3198931994

3199031995
overClauseNoOrderBy returns [OverClause vResult]
3199131996
:
31992-
vResult = overClauseBeginning
31997+
vResult = overClauseBeginningNoWindowName
3199331998
tRParen:RightParenthesis
3199431999
{
3199532000
UpdateTokenInfo(vResult,tRParen);
@@ -32021,6 +32026,25 @@ overClauseBeginning returns [OverClause vResult = FragmentFactory.CreateFragment
3202132026
)?
3202232027
;
3202332028

32029+
overClauseBeginningNoWindowName returns [OverClause vResult = FragmentFactory.CreateFragment<OverClause>()]
32030+
:
32031+
tOver:Over
32032+
{
32033+
UpdateTokenInfo(vResult,tOver);
32034+
}
32035+
LeftParenthesis
32036+
(
32037+
(Identifier By) =>
32038+
tPartition:Identifier
32039+
{
32040+
Match(tPartition, CodeGenerationSupporter.Partition);
32041+
}
32042+
By expressionList[vResult, vResult.Partitions]
32043+
|
32044+
/* empty - allow OVER() with no PARTITION BY */
32045+
)
32046+
;
32047+
3202432048
windowFrameClause returns [WindowFrameClause vResult = FragmentFactory.CreateFragment<WindowFrameClause>()]
3202532049
:
3202632050
tRowsRange:Identifier

SqlScriptDom/Parser/TSql/TSql170.g

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32956,6 +32956,11 @@ overClause returns [OverClause vResult]
3295632956
)?
3295732957
tRParen:RightParenthesis
3295832958
{
32959+
// Window name inside parentheses requires at least one clause (PARTITION BY, ORDER BY, or window frame)
32960+
if (vResult.WindowName != null && vResult.Partitions.Count == 0 && vResult.OrderByClause == null && vResult.WindowFrameClause == null)
32961+
{
32962+
ThrowParseErrorException("SQL46010", tRParen, TSqlParserResource.SQL46010Message, vResult.WindowName.Value);
32963+
}
3295932964
UpdateTokenInfo(vResult,tRParen);
3296032965
}
3296132966
| vResult = overClauseWithWindow
@@ -32978,7 +32983,7 @@ overClauseWithWindow returns [OverClause vResult = FragmentFactory.CreateFragmen
3297832983

3297932984
overClauseNoOrderBy returns [OverClause vResult]
3298032985
:
32981-
vResult = overClauseBeginning
32986+
vResult = overClauseBeginningNoWindowName
3298232987
tRParen:RightParenthesis
3298332988
{
3298432989
UpdateTokenInfo(vResult,tRParen);
@@ -33010,6 +33015,25 @@ overClauseBeginning returns [OverClause vResult = FragmentFactory.CreateFragment
3301033015
)?
3301133016
;
3301233017

33018+
overClauseBeginningNoWindowName returns [OverClause vResult = FragmentFactory.CreateFragment<OverClause>()]
33019+
:
33020+
tOver:Over
33021+
{
33022+
UpdateTokenInfo(vResult,tOver);
33023+
}
33024+
LeftParenthesis
33025+
(
33026+
(Identifier By) =>
33027+
tPartition:Identifier
33028+
{
33029+
Match(tPartition, CodeGenerationSupporter.Partition);
33030+
}
33031+
By expressionList[vResult, vResult.Partitions]
33032+
|
33033+
/* empty - allow OVER() with no PARTITION BY */
33034+
)
33035+
;
33036+
3301333037
windowFrameClause returns [WindowFrameClause vResult = FragmentFactory.CreateFragment<WindowFrameClause>()]
3301433038
:
3301533039
tRowsRange:Identifier

SqlScriptDom/Parser/TSql/TSql180.g

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32956,6 +32956,11 @@ overClause returns [OverClause vResult]
3295632956
)?
3295732957
tRParen:RightParenthesis
3295832958
{
32959+
// Window name inside parentheses requires at least one clause (PARTITION BY, ORDER BY, or window frame)
32960+
if (vResult.WindowName != null && vResult.Partitions.Count == 0 && vResult.OrderByClause == null && vResult.WindowFrameClause == null)
32961+
{
32962+
ThrowParseErrorException("SQL46010", tRParen, TSqlParserResource.SQL46010Message, vResult.WindowName.Value);
32963+
}
3295932964
UpdateTokenInfo(vResult,tRParen);
3296032965
}
3296132966
| vResult = overClauseWithWindow
@@ -32978,7 +32983,7 @@ overClauseWithWindow returns [OverClause vResult = FragmentFactory.CreateFragmen
3297832983

3297932984
overClauseNoOrderBy returns [OverClause vResult]
3298032985
:
32981-
vResult = overClauseBeginning
32986+
vResult = overClauseBeginningNoWindowName
3298232987
tRParen:RightParenthesis
3298332988
{
3298432989
UpdateTokenInfo(vResult,tRParen);
@@ -33010,6 +33015,25 @@ overClauseBeginning returns [OverClause vResult = FragmentFactory.CreateFragment
3301033015
)?
3301133016
;
3301233017

33018+
overClauseBeginningNoWindowName returns [OverClause vResult = FragmentFactory.CreateFragment<OverClause>()]
33019+
:
33020+
tOver:Over
33021+
{
33022+
UpdateTokenInfo(vResult,tOver);
33023+
}
33024+
LeftParenthesis
33025+
(
33026+
(Identifier By) =>
33027+
tPartition:Identifier
33028+
{
33029+
Match(tPartition, CodeGenerationSupporter.Partition);
33030+
}
33031+
By expressionList[vResult, vResult.Partitions]
33032+
|
33033+
/* empty - allow OVER() with no PARTITION BY */
33034+
)
33035+
;
33036+
3301333037
windowFrameClause returns [WindowFrameClause vResult = FragmentFactory.CreateFragment<WindowFrameClause>()]
3301433038
:
3301533039
tRowsRange:Identifier

Test/SqlDom/Baselines160/WindowClauseTests160.sql

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,16 @@ SELECT Sum(t.c1) OVER Win1
103103
FROM t1 AS t
104104
GROUP BY t.c1
105105
WINDOW Win1 AS (PARTITION BY t.c1)
106-
ORDER BY t.c1;
106+
ORDER BY t.c1;
107+
108+
109+
GO
110+
SELECT COUNT(*) OVER Win1
111+
FROM tb1
112+
WINDOW Win1 AS (PARTITION BY c1);
113+
114+
115+
GO
116+
SELECT COUNT(*) OVER (Win1 ORDER BY c1)
117+
FROM tb1
118+
WINDOW Win1 AS (PARTITION BY c1);

Test/SqlDom/Only160SyntaxTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public partial class SqlDomTests
2020
new ParserTest160("ExpressionTests160.sql", nErrors80: 1, nErrors90: 0, nErrors100: 0, nErrors110: 0, nErrors120: 0, nErrors130: 0, nErrors140: 0, nErrors150: 0),
2121
new ParserTest160("CreateUserFromExternalProvider160.sql", nErrors80: 2, nErrors90: 1, nErrors100: 1, nErrors110: 1, nErrors120: 1, nErrors130: 1, nErrors140: 1, nErrors150: 1),
2222
new ParserTest160("CreateExternalTableStatementTests160.sql", nErrors80: 2, nErrors90: 2, nErrors100: 2, nErrors110: 2, nErrors120: 2, nErrors130: 2, nErrors140: 2, nErrors150: 2),
23-
new ParserTest160("WindowClauseTests160.sql", nErrors80: 14, nErrors90: 13, nErrors100: 13, nErrors110: 13, nErrors120: 13, nErrors130: 13, nErrors140: 13, nErrors150: 13),
23+
new ParserTest160("WindowClauseTests160.sql", nErrors80: 16, nErrors90: 15, nErrors100: 15, nErrors110: 15, nErrors120: 15, nErrors130: 15, nErrors140: 15, nErrors150: 15),
2424
new ParserTest160("CreateExternalDataSourceStatementTests160.sql", nErrors80: 2, nErrors90: 2, nErrors100: 2, nErrors110: 2, nErrors120: 2, nErrors130: 0, nErrors140: 0, nErrors150: 0),
2525
new ParserTest160("CreateDatabaseTests160.sql", nErrors80: 4, nErrors90: 4, nErrors100: 4, nErrors110: 4, nErrors120: 4, nErrors130: 4, nErrors140: 4, nErrors150: 4),
2626
new ParserTest160("CreateLedgerTableTests160.sql", nErrors80: 14, nErrors90: 14, nErrors100: 14, nErrors110: 14, nErrors120: 14, nErrors130: 14, nErrors140: 14, nErrors150: 14),

Test/SqlDom/ParserErrorsTests.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2156,6 +2156,41 @@ public void IgnoreOrRecpectNullsSyntaxNegativeTest()
21562156
new ParserErrorInfo(41, "SQL46010", "NULLS"));
21572157
}
21582158

2159+
/// <summary>
2160+
/// Negative tests for invalid OVER clause syntax.
2161+
/// Tests for issue: Invalid OVER clause parses successfully
2162+
/// OVER clause with just a column reference in parentheses should fail.
2163+
/// </summary>
2164+
[TestMethod]
2165+
[Priority(0)]
2166+
[SqlStudioTestCategory(Category.UnitTest)]
2167+
public void InvalidOverClauseNegativeTest()
2168+
{
2169+
// OVER clause with just an identifier in parentheses is invalid
2170+
// Valid syntax is either OVER identifier (window name reference without parens)
2171+
// or OVER (PARTITION BY ...) or OVER (ORDER BY ...) or OVER ()
2172+
// The error occurs at the closing paren when no clauses follow the window name
2173+
// Note: Error message shows the identifier value without quotes
2174+
//
2175+
ParserTestUtils.ErrorTest160("SELECT COUNT(*) OVER([col]) FROM t1",
2176+
new ParserErrorInfo(26, "SQL46010", "col"));
2177+
ParserTestUtils.ErrorTest170("SELECT COUNT(*) OVER([col]) FROM t1",
2178+
new ParserErrorInfo(26, "SQL46010", "col"));
2179+
ParserTestUtils.ErrorTest180("SELECT COUNT(*) OVER([col]) FROM t1",
2180+
new ParserErrorInfo(26, "SQL46010", "col"));
2181+
2182+
// Another variant with a regular identifier
2183+
ParserTestUtils.ErrorTest160("SELECT SUM(Amount) OVER(MyColumn) FROM t1",
2184+
new ParserErrorInfo(32, "SQL46010", "MyColumn"));
2185+
ParserTestUtils.ErrorTest170("SELECT SUM(Amount) OVER(MyColumn) FROM t1",
2186+
new ParserErrorInfo(32, "SQL46010", "MyColumn"));
2187+
2188+
// This should also fail for aggregate functions
2189+
ParserTestUtils.ErrorTest160("SELECT AVG(Value) OVER(col1) FROM t1",
2190+
new ParserErrorInfo(27, "SQL46010", "col1"));
2191+
}
2192+
2193+
21592194
/// <summary>
21602195
/// Negative tests for IS [NOT] DISTINCT FROM syntax.
21612196
/// </summary>

Test/SqlDom/TestScripts/WindowClauseTests160.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,14 @@ SELECT Sum(t.c1) OVER Win1 FROM t1 t
7777
GROUP BY t.c1
7878
WINDOW Win1 AS (PARTITION BY t.c1)
7979
ORDER BY t.c1
80+
GO
81+
82+
-- checking COUNT(*) with WINDOW clause and window name reference (without parentheses)
83+
SELECT COUNT(*) OVER Win1 FROM tb1
84+
WINDOW Win1 AS (PARTITION BY c1)
85+
GO
86+
87+
-- checking COUNT(*) with partial window specification (window name inside parentheses)
88+
SELECT COUNT(*) OVER (Win1 ORDER BY c1) FROM tb1
89+
WINDOW Win1 AS (PARTITION BY c1)
8090
GO

global.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"sdk": {
3-
"version": "8.0.419",
3+
"version": "8.0.420",
44
"rollForward": "latestMajor"
55
},
66
"msbuild-sdks": {

release-notes/180/180.18.1.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Release Notes
2+
3+
## Microsoft.SqlServer.TransactSql.ScriptDom 180.18.1
4+
This update brings the following changes over the previous release:
5+
6+
### Target Platform Support
7+
8+
* .NET Framework 4.7.2 (Windows x86, Windows x64)
9+
* .NET 8 (Windows x86, Windows x64, Linux, macOS)
10+
* .NET Standard 2.0+ (Windows x86, Windows x64, Linux, macOS)
11+
12+
### Dependencies
13+
* Updates .NET SDK to latest patch version 8.0.420
14+
15+
#### .NET Framework
16+
#### .NET Core
17+
18+
### New Features
19+
20+
### Fixed
21+
* TRIM with FROM clause in RETURN statement fails to parse [#188](https://github.com/microsoft/SqlScriptDOM/issues/188)
22+
* DATEADD/DATEDIFF/DATEPART/DATENAME first argument parsed as ColumnReferenceExpression instead of datepart keyword [#196](https://github.com/microsoft/SqlScriptDOM/issues/196)
23+
* DAY used as argument for DATEADD() is interpreted as ColumnReferenceExpression [#124](https://github.com/microsoft/SqlScriptDOM/issues/124)
24+
* TSql160Parser misidentifies interval parameter in DATEDIFF function as ColumnReferenceExpression [#98](https://github.com/microsoft/SqlScriptDOM/issues/98)
25+
* Invalid OVER clause parses successfully [#195](https://github.com/microsoft/SqlScriptDOM/issues/195)
26+
27+
### Changes
28+
* Updates the Transact-SQL script generator to ensure that semicolons are correctly placed before any trailing comments
29+
30+
### Known Issues
31+
* None

0 commit comments

Comments
 (0)