feat(sql): implement arithmetic with window functions#6626
feat(sql): implement arithmetic with window functions#6626bluestreak01 merged 30 commits intomasterfrom
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughExtends the expression parser to support SQL window functions with OVER clauses, including PARTITION BY, ORDER BY, and frame specifications (ROWS/RANGE/GROUPS/CUMULATIVE). Adds windowContext field to ExpressionNode, updates SqlKeywords with token detection utilities, and refactors SqlParser to delegate window parsing to ExpressionParser. Comprehensive test coverage validates frame semantics and error handling. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
The PR description mentions "Integration tests for window function arithmetic execution", but the arithmetic only seems to be covered in |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
core/src/test/java/io/questdb/test/griffin/RpnBuilder.java (1)
38-48: RPN window serialization should output explicitEXCLUDE NO OTHERSfor round-trip fidelity.The current implementation at lines 188–200 only serializes exclusion clauses when
exclusionKind != EXCLUDE_NO_OTHERS. However,WindowColumntracks whether an exclusion clause was explicitly specified via theexclusionKindPosfield: when the parser encounters explicitEXCLUDE NO OTHERS, it setsexclusionKindPosto the token position (non-zero), whereas the implicit default leaves it at 0.To preserve round-trip fidelity, check
getExclusionKindPos()and outputexclude no otherswhenexclusionKindPos != 0:if (wc.getExclusionKindPos() > 0 || wc.getExclusionKind() != WindowColumn.EXCLUDE_NO_OTHERS) { sink.put(" exclude "); switch (wc.getExclusionKind()) { case WindowColumn.EXCLUDE_NO_OTHERS: sink.put("no others"); break; // ... existing cases } }
🤖 Fix all issues with AI agents
In @core/src/main/java/io/questdb/griffin/ExpressionParser.java:
- Around line 464-488: The PARTITION BY parsing loop in ExpressionParser (tokens
via SqlUtil.fetchNext, lexer.unparseLast, parseWindowExpr, and
windowCol.getPartitionBy()) currently allows empty lists and trailing commas
because it only checks for ORDER/')' after consuming a comma; update the logic
to track whether an expression was just parsed (or equivalently require a valid
expression token before accepting a comma) so that: 1) if the first fetched
token after "PARTITION BY" is ORDER or ')' you throw "'column name expected" (no
empty list allowed), and 2) if a comma is followed by ORDER or ')' you throw a
"'column name expected" or "'order', ',' or ')' expected" error as appropriate;
ensure lexer.unparseLast()/parseWindowExpr usage remains the same but add the
upfront token validation to reject trailing commas and empty lists.
In @core/src/main/java/io/questdb/griffin/model/ExpressionNode.java:
- Around line 199-205: WindowColumn currently inherits
QueryColumn.equals()/hashCode() and therefore ignores its window-specific state;
add overrides of equals(Object) and hashCode() in the WindowColumn class that
incorporate the base QueryColumn identity (alias, ast, includeIntoWildcard) and
all window-specific fields (e.g., partitionBy, orderBy, framingMode,
exclusionKind, ignoreNulls, rowsHi/rowsLo, start/stop expressions,
peer/first/last flags, and any other window state) using Objects.equals(...) and
Objects.hash(...), ensuring the implementations are consistent with
ExpressionNode's use of windowContext so two WindowColumn instances with
different window specifications are not treated as equal.
🧹 Nitpick comments (9)
core/src/test/java/io/questdb/test/griffin/engine/SqlCompilerImplTest.java (3)
165-201: Updated frame-bound error assertions look correct; consider de-duplicating repeated message literals.The new messages (“frame bound value expected before …”) match the PR’s goal of more actionable frame errors. To keep the test easier to maintain as wording evolves, consider pulling the repeated strings into local vars (or a small helper) within this method.
7169-7203:assertWithReorder(...)refactor is fine, but ensure whitespace is intentional across set ops.The new helper-based construction reduces duplication, but it’s now easy to accidentally introduce double spaces / trailing spaces (which can make debugging failures harder when comparing query strings). I’d suggest normalizing the string building in the helper (see next comment).
7544-7547: Simplify query string construction inassertWithReorder(avoid" " + " "and trailing space).This is test-only, but the current concatenation is harder to read and includes a trailing space in the second query.
Proposed refactor
private void assertWithReorder(String expected, String setOperation) throws Exception { - assertSql(expected, "select avg(event) from ict " + " " + setOperation + " " + "select distinct avg(event) from ict"); - assertSql(expected, "select distinct avg(event) from ict" + " " + setOperation + " " + "select avg(event) from ict "); + assertSql( + expected, + "select avg(event) from ict " + setOperation + " select distinct avg(event) from ict" + ); + assertSql( + expected, + "select distinct avg(event) from ict " + setOperation + " select avg(event) from ict" + ); }core/src/test/java/io/questdb/test/griffin/engine/window/WindowFunctionTest.java (1)
2232-2263: Updated EXCLUDE error offsets look consistent, but they’re brittle.These tests hardcode character positions (101/125/133), which will keep breaking on harmless formatting/tokenization shifts. If you want to reduce churn, consider asserting only the message (or message + a small positional range around
EXCLUDE) rather than an exact offset.core/src/main/java/io/questdb/griffin/SqlKeywords.java (1)
40-42: New keyword helpers look fine; consider naming + null-contract consistency.
- If the codebase convention is
isXxxKeyword(),iRightParen()reads a bit “special”—worth aligning unless there’s a specific reason.- None of these helpers are null-safe; that’s consistent with most existing
SqlKeywordsmethods, but please ensure call sites don’t pass null.Also applies to: 264-266, 346-358, 1407-1412, 1780-1789
core/src/test/java/io/questdb/test/griffin/DeclareTest.java (1)
715-739: Good coverage for DECLARE substitution inPARTITION BY; consider asserting actual results too.This validates parsing/planning, but still doesn’t prove execution semantics (which was raised in the PR thread). If feasible, insert a tiny deterministic dataset and
assertSqltherow_numoutput for a couple partitions.core/src/test/java/io/questdb/test/griffin/SqlParserTest.java (2)
12958-12975: RemoveSystem.out.printlndebug output from test helper (noisy + can slow/flakify CI).
This will print for every window-frame permutation across many tests.Proposed diff
@@ - for (String frameType : frameTypes) { - String s = query.replace("#FRAME", frameType); - System.out.println(s); - ExecutionModel model = compiler.generateExecutionModel(s, sqlExecutionContext); + for (String frameType : frameTypes) { + final String queryWithFrame = query.replace("#FRAME", frameType); + ExecutionModel model = compiler.generateExecutionModel(queryWithFrame, sqlExecutionContext); Assert.assertEquals(ExecutionModel.QUERY, model.getModelType()); sink.clear(); ((Sinkable) model).toSink(sink); String expected = expectedTemplate.replace("#FRAME", frameType.trim()); TestUtils.assertEquals(expected, sink); if (model instanceof QueryModel && model.getModelType() == ExecutionModel.QUERY) { validateTopDownColumns((QueryModel) model); } }
12562-12592:testVWAPcovers “window arithmetic”, but the name/partitioning looks misleading (“daily” vsPARTITION BY symbol, timestamp).
Partitioning bytimestampmakes each partition effectively a single timestamp value; if the intent is truly VWAP per day, consider partitioning by a day bucket (e.g.,timestamp_floor('d', timestamp)), or rename the test to reflect what it’s actually validating.core/src/test/java/io/questdb/test/griffin/ExpressionParserTest.java (1)
1894-1898: Minor: Misplaced section comment.The comment
// Window function testson line 1894 appears to be misplaced since window function tests actually begin around line 1587 withtestWindowFrameCurrentRowWithPrecedingEnd. Consider removing this comment or moving it to the start of the window function test section.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
core/src/main/java/io/questdb/griffin/ExpressionParser.javacore/src/main/java/io/questdb/griffin/SqlKeywords.javacore/src/main/java/io/questdb/griffin/SqlParser.javacore/src/main/java/io/questdb/griffin/model/ExpressionNode.javacore/src/test/java/io/questdb/test/griffin/DeclareTest.javacore/src/test/java/io/questdb/test/griffin/ExpressionParserTest.javacore/src/test/java/io/questdb/test/griffin/FunctionParserTest.javacore/src/test/java/io/questdb/test/griffin/GroupByTest.javacore/src/test/java/io/questdb/test/griffin/RpnBuilder.javacore/src/test/java/io/questdb/test/griffin/SqlParserTest.javacore/src/test/java/io/questdb/test/griffin/engine/SqlCompilerImplTest.javacore/src/test/java/io/questdb/test/griffin/engine/functions/activity/CancelQueryFunctionFactoryTest.javacore/src/test/java/io/questdb/test/griffin/engine/window/WindowFunctionTest.javacore/src/test/resources/sqllogictest/test/sql/pivot/pivot_errors.test
🧰 Additional context used
🧬 Code graph analysis (2)
core/src/main/java/io/questdb/griffin/ExpressionParser.java (4)
core/src/main/java/io/questdb/griffin/model/WindowColumn.java (1)
WindowColumn(38-306)core/src/main/java/io/questdb/griffin/SqlKeywords.java (1)
SqlKeywords(32-2490)core/src/main/java/io/questdb/griffin/SqlUtil.java (1)
SqlUtil(76-1469)core/src/main/java/io/questdb/griffin/model/ExpressionNode.java (1)
ExpressionNode(42-435)
core/src/test/java/io/questdb/test/griffin/engine/window/WindowFunctionTest.java (1)
core/src/test/java/io/questdb/test/tools/TestUtils.java (1)
TestUtils(155-2680)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (35)
- GitHub Check: New pull request (Coverage Report Coverage Report)
- GitHub Check: New pull request (Hosted Running tests on windows-other-2)
- GitHub Check: New pull request (Hosted Running tests on windows-other-1)
- GitHub Check: New pull request (Hosted Running tests on windows-pgwire)
- GitHub Check: New pull request (Hosted Running tests on windows-cairo-2)
- GitHub Check: New pull request (Hosted Running tests on windows-cairo-1)
- GitHub Check: New pull request (Hosted Running tests on windows-fuzz2)
- GitHub Check: New pull request (Hosted Running tests on windows-fuzz1)
- GitHub Check: New pull request (Hosted Running tests on windows-griffin-sub)
- GitHub Check: New pull request (Hosted Running tests on windows-griffin-base)
- GitHub Check: New pull request (Hosted Running tests on mac-other)
- GitHub Check: New pull request (Hosted Running tests on mac-pgwire)
- GitHub Check: New pull request (Hosted Running tests on mac-cairo-fuzz)
- GitHub Check: New pull request (Hosted Running tests on mac-cairo)
- GitHub Check: New pull request (Hosted Running tests on mac-griffin)
- GitHub Check: New pull request (SelfHosted Running tests with cover on linux-other)
- GitHub Check: New pull request (SelfHosted Running tests with cover on linux-pgwire)
- GitHub Check: New pull request (SelfHosted Running tests with cover on linux-cairo-sub)
- GitHub Check: New pull request (SelfHosted Running tests with cover on linux-cairo-root)
- GitHub Check: New pull request (SelfHosted Running tests with cover on linux-fuzz2)
- GitHub Check: New pull request (SelfHosted Running tests with cover on linux-fuzz1)
- GitHub Check: New pull request (SelfHosted Running tests with cover on linux-griffin-sub)
- GitHub Check: New pull request (Rust Test and Lint on linux-jdk17)
- GitHub Check: New pull request (SelfHosted Running tests with cover on linux-griffin-root)
- GitHub Check: New pull request (Trigger Enterprise CI Trigger Enterprise Pipeline)
- GitHub Check: New pull request (SelfHosted Cairo tests on linux-x86-graal)
- GitHub Check: New pull request (SelfHosted Other tests on linux-x64-zfs)
- GitHub Check: New pull request (SelfHosted Other tests on linux-x86-graal)
- GitHub Check: New pull request (SelfHosted Cairo tests on linux-x64-zfs)
- GitHub Check: New pull request (SelfHosted Cairo tests on linux-arm64)
- GitHub Check: New pull request (SelfHosted Griffin tests on linux-x86-graal)
- GitHub Check: New pull request (SelfHosted Griffin tests on linux-x64-zfs)
- GitHub Check: New pull request (SelfHosted Other tests on linux-arm64)
- GitHub Check: New pull request (SelfHosted Griffin tests on linux-arm64)
- GitHub Check: New pull request (Check Changes Check changes)
🔇 Additional comments (27)
core/src/test/java/io/questdb/test/griffin/FunctionParserTest.java (1)
904-970: LGTM! Good use of Java text blocks for improved readability.Converting the concatenated string to a text block makes the expected binary output much easier to read and maintain. The test semantics remain unchanged.
core/src/test/java/io/questdb/test/griffin/GroupByTest.java (1)
228-233: LGTM!The updated error message correctly reflects the new window parsing behavior. The position [109] accurately points to the
zalias in the GROUP BY clause, and the message "window functions are not allowed in GROUP BY" is consistent with similar validation tests in this file.core/src/test/java/io/questdb/test/griffin/engine/functions/activity/CancelQueryFunctionFactoryTest.java (1)
134-138: LGTM!Clean conversion to text block syntax improves readability without changing the test's behavior.
core/src/main/java/io/questdb/griffin/model/ExpressionNode.java (4)
69-69: LGTM!The new
windowContextfield follows the established pattern of public fields in this model class.
144-144: Shallow copy of pooled WindowColumn is intentional but warrants attention.The comment correctly documents that WindowColumn is pooled and shared. Ensure the pool's lifecycle guarantees that the referenced WindowColumn remains valid for the duration of the cloned ExpressionNode's usage; otherwise, stale data could be accessed if the WindowColumn is recycled prematurely.
161-161: LGTM!Properly resets
windowContextto null, consistent with other field resets in this method.
179-179: LGTM!Shallow copy of
windowContextis consistent with how other reference fields (lhs,rhs,queryModel) are handled incopyFrom().core/src/test/java/io/questdb/test/griffin/engine/window/WindowFunctionTest.java (3)
3499-3515: Lag IGNORE/RESPECT NULLS parse errors now point at the correct unexpected token (over).The updated offsets (24/25) match the start of
overafterignore/respect, aligning well with the new, more actionable messages.
4272-4288: Lead IGNORE/RESPECT NULLS parse errors mirror lag (good).Same pattern as
lag(...): the updated offsets (25/26) target theovertoken and the messages are consistent.
8147-8194: Flyweight message assertion updated appropriately for “... after ‘nulls’”.This looks aligned with the new parser wording and keeps using
getFlyweightMessage()+assertContains(), which is the right level of strictness for this assertion.core/src/test/resources/sqllogictest/test/sql/pivot/pivot_errors.test (2)
4-12: LGTM! Test cases cover new window function behavior in PIVOT.The added test case validates that window functions like
sum(41) over()are now accepted within PIVOT aggregates, returning the expected NULL values. The naming convention change from Roman to Arabic numerals is consistent.
20-23: LGTM! Error handling test for nested window contexts.This test correctly validates that nested window function contexts (e.g.,
sum(sum(41) over())) are rejected with an appropriate error message.core/src/main/java/io/questdb/griffin/SqlParser.java (5)
183-199: LGTM! Constructor correctly threads windowColumnPool to ExpressionParser.Both constructor paths (legacy and non-legacy operator precedence) now pass
characterStoreandwindowColumnPooltoExpressionParser, enabling window function parsing within expressions.
358-377: LGTM! Window context traversal enables declared variables in window clauses.The recursive replacement logic correctly traverses all window context elements:
partitionByandorderBylistsrowsLoExprandrowsHiExprframe bounds with position preservationThis enables features like
PARTITION BY @varandORDER BY @colin window functions.
871-871: Minor formatting improvement.
3663-3675: LGTM! Clean integration of window function parsing.The implementation correctly uses
expr.windowContextto detect window functions already parsed byExpressionParser. The pre-parsedWindowColumnis used directly, while non-window expressions follow the existing path. The inline comments clearly explain the delegation model.
3353-3353: Helper methods are properly defined in SqlKeywords.java.All three methods are verified to exist with correct implementations:
isNotForKeyword()at SqlKeywords.java:1407iRightParen()at SqlKeywords.java:40isComma()at SqlKeywords.java:264Usage in SqlParser.java is consistent with these definitions.
core/src/main/java/io/questdb/griffin/ExpressionParser.java (2)
602-777: Window frame parsing/validation looks solid and the error messages are much more actionable.The explicit checks around time units (RANGE-only), single-bound rules, and BETWEEN bound ordering are the right guardrails.
1405-1468: PARTITION BY syntax edge cases need shape tests.Most of the suggested test scenarios already have coverage (IGNORE NULLS without OVER, IGNORE/IGNORE NULL typos, nested window rejection in CAST, and window functions in CASE branches). However, consider adding tests for PARTITION BY clause syntax errors—specifically malformed patterns like
PARTITION BY a, ORDER BY ...orPARTITION BY a, )to ensure the parser properly rejects these.core/src/test/java/io/questdb/test/griffin/RpnBuilder.java (1)
62-127:serializeExpressionToRpn()/serializeFrameBound()approach is reasonable.The lhs/rhs vs args split matches typical QuestDB expression tree conventions, and the time-unit rendering is clear.
core/src/test/java/io/questdb/test/griffin/SqlParserTest.java (2)
12652-12669: Good negative coverage: reject window functions as arguments to other functions.
This matches the stated “nested-window detection” behavior and protects against subtle parse-tree acceptance regressions.
115-117: Updated window-frame error messages: tests now assert clearer, more actionable guidance (good).
Given how many tests key off exact strings/positions, consider keeping expectations as “contains” where practical to reduce churn when wording inevitably evolves.Also applies to: 145-149, 180-190, 194-205, 208-219, 222-233, 434-440, 444-449, 490-495, 499-511, 568-587, 589-601, 639-652, 668-679, 696-702, 741-747, 814-820, 823-830, 12320-12323
core/src/test/java/io/questdb/test/griffin/ExpressionParserTest.java (5)
839-845: LGTM on text block conversions.The conversion of multi-line string literals to Java text blocks improves readability while maintaining the original test behavior. The use of escape sequences (
\r,\s) is appropriate.Also applies to: 1108-1113, 1118-1125, 1139-1146
1587-1694: Comprehensive coverage of window frame validation errors.The negative test cases cover key invalid frame specifications with clear, actionable error messages. The tests validate:
- Frame boundary ordering constraints (CURRENT ROW, PRECEDING, FOLLOWING)
- UNBOUNDED placement rules
- Time unit restrictions (RANGE only, not ROWS/GROUPS)
- Single-bound vs BETWEEN syntax requirements
1696-1766: LGTM on window function arithmetic and CUMULATIVE tests.The tests effectively validate:
- Arithmetic operations with window functions in RPN format
- CUMULATIVE shorthand expansion to explicit frame specification
- Proper error handling when CUMULATIVE is used without ORDER BY
- Real-world patterns like VWAP calculations
1768-1853: Good coverage of IGNORE/RESPECT NULLS and CASE expression integration.Notable strengths:
- User-friendly error message for the common "null" vs "nulls" typo (line 1773)
- Comprehensive CASE expression tests covering window functions in WHEN conditions, THEN clauses, ELSE clauses, and simple CASE syntax
- Proper validation that IGNORE NULLS requires an OVER clause
1855-1928: LGTM on remaining window function tests.The tests provide good coverage of:
- Complex PARTITION BY expressions (LIKE, function calls)
- RANGE frames with time units
- ROWS frames with various bounds
- Error handling for unterminated OVER clauses
- Basic window function syntax variations
core/src/main/java/io/questdb/griffin/model/ExpressionNode.java
Outdated
Show resolved
Hide resolved
|
Either I'm missing something or the changes don't work as expected: create table x (
id long,
ts timestamp
) timestamp(ts) partition by day;
insert into x values (1, now()), (2, now()), (3, now());
-- this works:
SELECT row_number() OVER (ORDER BY ts) AS adjusted_rank FROM x;
-- this gives "window function called in non-window context, make sure to add OVER clause" error
SELECT row_number() OVER (ORDER BY ts) + 1 AS adjusted_rank FROM x;
-- this works:
SELECT sum(id) OVER (ORDER BY ts), lag(id) OVER (ORDER BY ts) AS id_diff FROM x;
-- this gives "window function called in non-window context, make sure to add OVER clause" error
SELECT sum(id) OVER (ORDER BY ts) - lag(id) OVER (ORDER BY ts) AS id_diff FROM x;
-- this also gives the same error
SELECT CASE
WHEN row_number() OVER (ORDER BY ts) > 1 THEN 'subsequent'
ELSE 'first'
END FROM x; |
core/src/main/java/io/questdb/griffin/model/ExpressionNode.java
Outdated
Show resolved
Hide resolved
core/src/main/java/io/questdb/griffin/model/ExpressionNode.java
Outdated
Show resolved
Hide resolved
|
@puzpuzpuz all fixed |
|
@CodeRabbit review the changes and see if PR comments have been addressed |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain@bluestreak01, I'll review the changes to verify if the PR comments have been addressed. 🏁 Script executed: Length of output: 8008 🏁 Script executed: Length of output: 2627 🏁 Script executed: Length of output: 1023 🏁 Script executed: Length of output: 5389 🏁 Script executed: Length of output: 4053 🏁 Script executed: Length of output: 15070 🏁 Script executed: Length of output: 4291 @bluestreak01, I've reviewed the changes. Here's what I found: ✅ Integration Tests AddedGood news! A new integration test file Coverage of Specific SQL PatternsHowever, checking against the specific failing examples puzpuzpuz reported: ✅ Covered:
❌ Not covered:
RecommendationTo fully verify that all the reported issues are fixed, I suggest adding tests for:
Would you like me to help generate these additional test cases? |
|
@CodeRabbit review the fixes |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain@bluestreak01, I'll review the latest fixes to verify the missing test coverage has been addressed. 🏁 Script executed: Length of output: 1965 🏁 Script executed: Length of output: 5544 🏁 Script executed: Length of output: 524 @bluestreak01, I've reviewed the latest fixes. Here's my assessment: ✅ Fixed IssuesCASE WHEN with window function directly - Now covered in "SELECT CASE " +
" WHEN row_number() OVER (ORDER BY ts) = 1 THEN 'first' " +
" WHEN row_number() OVER (ORDER BY ts) = 3 THEN 'last' " +
" ELSE 'middle' " +
"END AS category " +
"FROM x"This matches puzpuzpuz's example perfectly! ✅ ❌ Still MissingWindow function + constant arithmetic - No tests found for this pattern: SELECT row_number() OVER (ORDER BY ts) + 1 AS adjusted_rank FROM x;puzpuzpuz reported this specific query was failing with "window function called in non-window context" error. While you have tests for window-to-window arithmetic ( RecommendationTo fully address all the reported issues, please add an integration test similar to: @Test
public void testWindowFunctionWithConstantArithmetic() throws Exception {
assertQuery(
"""
adjusted_rank
2
3
4
""",
"SELECT row_number() OVER (ORDER BY ts) + 1 AS adjusted_rank FROM x",
"CREATE TABLE x AS (" +
"SELECT timestamp_sequence('2024-01-01', 1000000) AS ts FROM long_sequence(3)" +
") TIMESTAMP(ts) PARTITION BY DAY",
null,
false,
true
);
}This would verify that the specific failing example from puzpuzpuz's comment now works correctly. |
|
@CodeRabbit i added more tests and addressed the nit |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 6768 @bluestreak01, excellent work! ✅ I can confirm you've fully addressed all the missing test coverage: ✅ All Requested Tests Added
SummaryAll reported issues now have comprehensive integration test coverage:
The PR is now complete with solid test coverage! 🚀 If you found this review helpful, would you consider giving us a shout-out on X? Thank you for using CodeRabbit! |
[PR Coverage check]😍 pass : 471 / 513 (91.81%) file detail
|
Summary
Enables window functions to participate in arithmetic expressions and other operations. Previously, window functions could only appear as standalone SELECT columns. Now they can be used in expressions like:
Documentation overhaul: questdb/documentation#319
Technical Details
Architecture Change
Window function parsing has been moved from
SqlParsertoExpressionParser. This allows window functions to be parsed as part of the expression tree rather than as special-case column handling.Key changes:
ExpressionParser enhancements (
ExpressionParser.java)parseWindowClause()to parse OVER clause after function callsparseWindowExpr()to parse expressions within window clauses (PARTITION BY, ORDER BY, frame bounds)parseWindowFrameClause()andparseWindowFrameBound()for frame specification parsingopStack.setBottom()to isolate inner expression parsing from outer contextWindowExprTreeBuilderfield to avoid per-expression allocationsExpressionNode extension (
ExpressionNode.java)windowContextfield to attach parsedWindowColumnto function nodesclear()method for object pool reuseSqlParser simplification (
SqlParser.java)expr.windowContext != nullto use ExpressionParser resultrecursiveReplace()to traverse window context for declared variable substitutionImproved error messages
Zero-GC Compliance
WindowExprTreeBuilderis a reusable field withreset()method, not allocated per-expressionWindowColumnobjects come fromObjectPoolwhich callsclear()before reuseNested Window Function Detection
Window functions are rejected in contexts where they don't make sense:
f(row_number() over ())→ errorWHERE row_number() over () > 5→ errorCASE WHEN x THEN row_number() over () END→ OKTest Plan