Skip to content

Commit 70e67f4

Browse files
committed
Keep window specifications parseable inside lookahead
- The base window name is a choice rather than an optional, so that lookahead scans through a window specification keep its check: `JSON_VALUE(... DEFAULT sum(a) OVER (PARTITION BY b) ON EMPTY)` parses again. - Only an OVER window may extend a named window, not WITHIN GROUP. - QUALIFY after WINDOW requires a WINDOW clause.
1 parent ed3fb3a commit 70e67f4

2 files changed

Lines changed: 26 additions & 8 deletions

File tree

‎src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt‎

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8419,10 +8419,10 @@ PlainSelect PlainSelect() #PlainSelect:
84198419
windowName = RelObjectName() <K_AS> winDef = windowDefinition() { List<WindowDefinition> winDefs = new ArrayList<WindowDefinition>(); winDefs.add(winDef.withWindowName(windowName)); }
84208420
( LOOKAHEAD(2) "," windowName = RelObjectName() <K_AS> winDef = windowDefinition() { winDefs.add(winDef.withWindowName(windowName)); } )*
84218421
{ plainSelect.setWindowDefinitions(winDefs); }
8422+
// DuckDB: QUALIFY after WINDOW
8423+
[ LOOKAHEAD(2, { plainSelect.getQualify() == null }) qualify=Qualify()
8424+
{ plainSelect.setQualify(qualify); plainSelect.setQualifyAfterWindow(true); } ]
84228425
]
8423-
// DuckDB: QUALIFY after WINDOW
8424-
[ LOOKAHEAD(2, { plainSelect.getQualify() == null }) qualify=Qualify()
8425-
{ plainSelect.setQualify(qualify); plainSelect.setQualifyAfterWindow(true); } ]
84268426
[ LOOKAHEAD(<K_ORDER> <K_BY>) orderByElements = OrderByElements() { plainSelect.setOrderByElements(orderByElements); } ]
84278427
[ LOOKAHEAD(2) interpolateElements = InterpolateClause() { plainSelect.setInterpolate(interpolateElements); } ]
84288428
[ LOOKAHEAD(2) forClause = ForClause() {plainSelect.setForClause(forClause);} ]
@@ -13493,7 +13493,13 @@ void windowFun(AnalyticExpression retval):{
1349313493
(
1349413494
windowName = RelObjectName() { retval.setWindowName(windowName); }
1349513495
|
13496-
winDef = windowDefinition() { retval.setWindowDefinition(winDef); }
13496+
winDef = windowDefinition()
13497+
{
13498+
if (winDef.getBaseWindowName() != null && retval.getType() != AnalyticType.OVER) {
13499+
throw new ParseException("Only an OVER window may extend a named window");
13500+
}
13501+
retval.setWindowDefinition(winDef);
13502+
}
1349713503

1349813504
[
1349913505
LOOKAHEAD(2) <K_OVER> "("
@@ -13515,9 +13521,14 @@ WindowDefinition windowDefinition() : {
1351513521
String baseWindowName;
1351613522
} {
1351713523
"("
13518-
// DuckDB: (w ORDER BY b) extends the named window w
13519-
[ LOOKAHEAD({ isBaseWindowNameAhead() }) baseWindowName=RelObjectName()
13520-
{ winDef.setBaseWindowName(baseWindowName); } ]
13524+
// DuckDB: (w ORDER BY b) extends the named window w. A choice, not an optional, so that
13525+
// lookahead scans through a window specification keep the check
13526+
(
13527+
LOOKAHEAD({ isBaseWindowNameAhead() }) baseWindowName=RelObjectName()
13528+
{ winDef.setBaseWindowName(baseWindowName); }
13529+
|
13530+
{}
13531+
)
1352113532
[<K_PARTITION> <K_BY>
1352213533
(LOOKAHEAD(3) expressionList=ComplexExpressionList()
1352313534
| "(" {partitionByBrackets = true;} expressionList=ComplexExpressionList() ")" )

‎src/test/java/net/sf/jsqlparser/statement/select/DuckDBTest.java‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1083,7 +1083,10 @@ void testWindowAndFilterForms() throws JSQLParserException {
10831083
"SELECT sum(a) OVER w2 FROM t WINDOW w AS (PARTITION BY c), w2 AS (w ORDER BY b)",
10841084
"SELECT sum(a) OVER (ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) FROM t",
10851085
"SELECT sum(a) FILTER (b > 1) FROM t",
1086-
"SELECT count(*) FILTER (b > 1) OVER () FROM t"}) {
1086+
"SELECT count(*) FILTER (b > 1) OVER () FROM t",
1087+
// window specifications inside syntactic lookahead
1088+
"SELECT JSON_VALUE(j, '$.a' DEFAULT sum(a) OVER (PARTITION BY b) ON EMPTY) FROM t",
1089+
"SELECT JSON_VALUE(j, '$.a' DEFAULT sum(a) OVER (w ORDER BY b) ON EMPTY) FROM t"}) {
10871090
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
10881091
}
10891092
PlainSelect select = (PlainSelect) CCJSqlParserUtil.parse(
@@ -1092,6 +1095,10 @@ void testWindowAndFilterForms() throws JSQLParserException {
10921095
(net.sf.jsqlparser.expression.AnalyticExpression) select.getSelectItem(0)
10931096
.getExpression();
10941097
Assertions.assertFalse(sum.isFilterWhereKeyword());
1098+
Assertions.assertThrows(JSQLParserException.class, () -> CCJSqlParserUtil.parse(
1099+
"SELECT percentile_cont(0.5) WITHIN GROUP (w ORDER BY a) FROM t"));
1100+
Assertions.assertThrows(JSQLParserException.class,
1101+
() -> CCJSqlParserUtil.parse("SELECT a FROM t ORDER SIBLINGS BY a QUALIFY x = 1"));
10951102
// PostgreSQL requires WHERE
10961103
Assertions.assertThrows(JSQLParserException.class,
10971104
() -> CCJSqlParserUtil.parse("SELECT sum(a) FILTER (b > 1) FROM t",

0 commit comments

Comments
 (0)