Skip to content

feat(sql): recognize OR of timestamp IN predicates as interval intrinsics#6673

Merged
bluestreak01 merged 7 commits intomasterfrom
vi_fix_interval
Jan 20, 2026
Merged

feat(sql): recognize OR of timestamp IN predicates as interval intrinsics#6673
bluestreak01 merged 7 commits intomasterfrom
vi_fix_interval

Conversation

@bluestreak01
Copy link
Copy Markdown
Member

Summary

Fixes #6668

This PR enables QuestDB to recognize OR combinations of timestamp IN predicates as intrinsic interval filters, allowing the query optimizer to use efficient interval forward scans instead of falling back to row-by-row JIT filtering.

Before

-- Uses interval scan (good)
SELECT * FROM trades WHERE timestamp IN '2018-01-01';

-- Falls back to JIT filter (slow)
SELECT * FROM trades 
WHERE timestamp IN '2018-01-01' OR timestamp IN '2018-01-02' OR timestamp IN '2018-01-03';

After

Both queries now use efficient interval forward scans.

Changes

  • WhereClauseParser.java: Added support for extracting timestamp intrinsics from OR trees

    • isOrOfTimestampIn() - checks if an OR tree consists entirely of timestamp IN/= predicates
    • extractOrTimestampIntervals() - extracts intervals using union semantics
    • tryExtractOrTimestampIntrinsics() - wrapper with precondition checks
    • Modified extract() to handle OR nodes during tree traversal
  • RuntimeIntervalModelBuilder.java: Added unionIntervals(CharSequence seq, ...) method to parse and union string intervals (e.g., '2018-01-01' spanning a full day)

  • IntrinsicModel.java: Added corresponding wrapper method

Behavior

Query Before After
timestamp IN '2018-01-01' Interval (full day) Interval (full day)
timestamp IN ('2018-01-01', '2018-01-02') Points Points (unchanged)
timestamp IN '2018-01-01' OR timestamp IN '2018-01-02' No intrinsic (JIT filter) Intervals (full days)
timestamp = 'x' OR timestamp = 'y' No intrinsic (JIT filter) Intervals

Test plan

  • Updated WhereClauseParserTest with new test cases
  • All existing tests pass
  • New tests verify OR intrinsic extraction works correctly

🤖 Generated with Claude Code

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 19, 2026

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Adds OR-based extraction of timestamp IN/=/interval predicates into the WHERE parser, new string-based interval union APIs, modernizes several switch statements to switch expressions, and updates/extends tests to validate OR-to-interval extraction and related planner output.

Changes

Cohort / File(s) Summary
OR-Timestamp Intrinsic Extraction
core/src/main/java/io/questdb/griffin/WhereClauseParser.java
Adds OR-aware extraction and integration into the IntrinsicModel: isOrOfTimestampIn, extractOrTimestampIntervals, tryExtractOrTimestampIntrinsics, and traversal adjustments to consume OR trees as timestamp intrinsics.
Interval Union API Expansion
core/src/main/java/io/questdb/griffin/model/IntrinsicModel.java, core/src/main/java/io/questdb/griffin/model/RuntimeIntervalModelBuilder.java
Adds unionIntervals(CharSequence seq, int lo, int lim, int position) to both classes to parse and union string-encoded intervals into the runtime interval model (with guards against dynamic ranges).
Switch Expression Modernization
core/src/main/java/io/questdb/griffin/engine/functions/bool/InTimestampTimestampFunctionFactory.java, core/src/main/java/io/questdb/griffin/model/IntervalUtils.java
Replaces traditional switch statements with switch expressions in multiple methods while preserving behavior.
Planner / Explain Test Update
core/src/test/java/io/questdb/test/griffin/ExplainPlanTest.java
Updates testSelectStaticTsInterval3 expected plan to reflect interval-forward-scan planning for OR-derived intervals.
WhereClauseParser Tests Added/Updated
core/src/test/java/io/questdb/test/griffin/WhereClauseParserTest.java
Adds six tests for OR+timestamp predicate interactions and updates existing expectations (e.g., testTwoIntervalsWithOr) to assert interval extraction semantics.
Test Formatting Changes
core/src/test/java/io/questdb/test/griffin/TimestampQueryTest.java, core/src/test/java/io/questdb/test/griffin/engine/functions/InTimestampTimestampTest.java
Replace multi-line string concatenations with Java text blocks; no behavioral changes.
Test Query Adjustments
core/src/test/java/io/questdb/test/griffin/TimeFrameCursorTest.java, core/src/test/java/io/questdb/test/griffin/WindowJoinTest.java
Removes one TimeFrameCursor test case and tightens a WHERE clause in WindowJoinTest (modifies OR/AND combination in query).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

SQL, Enhancement, Performance

Suggested reviewers

  • puzpuzpuz
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.16% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: enabling recognition of OR combinations of timestamp IN predicates as interval intrinsics for query optimization.
Description check ✅ Passed The description provides comprehensive context about the changes, including before/after examples, specific file modifications, behavioral differences, and test coverage.
Linked Issues check ✅ Passed The PR successfully addresses issue #6668 by implementing OR recognition for timestamp IN predicates as interval intrinsics, transforming JIT filter fallbacks into efficient interval forward scans.
Out of Scope Changes check ✅ Passed All changes are scoped to the interval intrinsic optimization objective. Refactoring of switch statements to switch expressions and test formatting updates serve the core functionality.

✏️ 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@bluestreak01
Copy link
Copy Markdown
Member Author

@CodeRabbit review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 19, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
`@core/src/main/java/io/questdb/griffin/model/RuntimeIntervalModelBuilder.java`:
- Around line 382-410: unionIntervals currently parses an interval string into
staticIntervals then only extracts and re-adds the last [lo,hi] pair, dropping
any earlier pairs produced by IntervalUtils.parseInterval; change the flow to
keep the full parsed range appended by IntervalUtils.parseInterval (remove the
staticIntervals.setPos(size) and the manual add of only the last pair) and then
merge those new pairs into the existing set by calling
IntervalUtils.unionInPlace(staticIntervals, size) when intervalApplied is true
(or just set intervalApplied = true if this is the first parsed batch). Locate
this logic in unionIntervals and adjust handling of size, the parsed range
slice, and the calls to IntervalUtils.applyLastEncodedInterval /
IntervalUtils.unionInPlace accordingly.

In `@core/src/test/java/io/questdb/test/griffin/WindowJoinTest.java`:
- Around line 2616-2618: In WindowJoinTest.java inside the SQL string used for
the window join (the snippet building "from (trades limit 5) t window join
(select * from prices where ts in '2023-01-01T09:03:00.000000Z' or ts =
'2023-01-01T09:07:00.000000Z' and ts = '2023-01-01T09:08:00.000000Z') p on
(t.sym = p.sym)"), replace the mistaken AND between the two ts equality clauses
with OR (or adjust parentheses to express the intended logic) so the timestamp
conditions are not mutually exclusive; update the SQL string in the test method
in WindowJoinTest to use OR for the third timestamp check.

- Add testTimestampMixedInAndEqualsOr: mixed IN and = operators in OR
- Add testTimestampOrWithRangeFilter: OR combined with > filter
- Add testTimestampOrNotExtractedWhenIntervalFilterFirst: documents current limitation
- Add testTimestampOrWithBindVariables: bind variables fall back to filter
- Add testTimestampOrWithPeriodicIntervals: periodic intervals in OR
- Fix unionIntervals to properly handle periodic intervals that expand to multiple pairs
- Update testTimestampEqualsOrSingleValues to use date-only format (24-hour span)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@bluestreak01
Copy link
Copy Markdown
Member Author

@CodeRabbit review again

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 19, 2026

@bluestreak01: I'll review the changes again.


🧠 Learnings used
Learnt from: bluestreak01
Repo: questdb/questdb PR: 6673
File: core/src/test/java/io/questdb/test/griffin/WindowJoinTest.java:2616-2618
Timestamp: 2026-01-19T21:43:12.938Z
Learning: In test `testWindowJoinFailsWhenSlaveDoesNotSupportTimeFrames` in `core/src/test/java/io/questdb/test/griffin/WindowJoinTest.java`, the use of `AND` between timestamp equality conditions (e.g., `ts = 'X' AND ts = 'Y'`) is intentional to prevent intrinsic interval creation. This ensures the window join fails as expected when the slave doesn't support time frames, which is necessary after PR `#6673` added OR timestamp intrinsics recognition.
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@core/src/main/java/io/questdb/griffin/WhereClauseParser.java`:
- Around line 2170-2190: The '=' branch in extractOrTimestampIntervals currently
treats the RHS as a quoted interval string (using valueNode.token with offsets
1..len-1), which expands date-only equality into a full-day interval and
mishandles numeric literals; change this to parse the timestamp into a point and
call model.intersectIntervals(...) or model.unionIntervals(...) with identical
start and end values (i.e., a [ts,ts] interval) instead of slicing the token,
using the same leftFirst conditional, and mirror the behavior used by
analyzeEquals; use ExpressionNode, isTimestamp(node.lhs), valueNode, and
model.intersectIntervals/model.unionIntervals to locate and update the logic.

In `@core/src/test/java/io/questdb/test/griffin/WhereClauseParserTest.java`:
- Around line 3303-3314: The test name and comments are inconsistent with the
assertion: update the test method name
testTimestampOrNotExtractedWhenIntervalFilterFirst to reflect that OR-derived
intervals are extracted when an interval filter appears first (e.g.,
testTimestampOrExtractedWhenIntervalFilterFirst) and change the comment above
the IntrinsicModel m = modelOf(...) to state that OR clauses are extracted into
interval filters (and thus intervalToString should contain the two intervals);
ensure any mention of "OR extraction is skipped" is removed or inverted, keeping
references to IntrinsicModel, modelOf, hasIntervalFilters, and intervalToString
so reviewers can locate the change.

@bluestreak01
Copy link
Copy Markdown
Member Author

@CodeRabbit review again, check if your concerns have been addressed

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 19, 2026

@bluestreak01: I'll review the changes again and check if the previous concerns have been addressed.


🧠 Learnings used
Learnt from: bluestreak01
Repo: questdb/questdb PR: 6673
File: core/src/test/java/io/questdb/test/griffin/WindowJoinTest.java:2616-2618
Timestamp: 2026-01-19T21:43:12.938Z
Learning: In test `testWindowJoinFailsWhenSlaveDoesNotSupportTimeFrames` in `core/src/test/java/io/questdb/test/griffin/WindowJoinTest.java`, the use of `AND` between timestamp equality conditions (e.g., `ts = 'X' AND ts = 'Y'`) is intentional to prevent intrinsic interval creation. This ensures the window join fails as expected when the slave doesn't support time frames, which is necessary after PR `#6673` added OR timestamp intrinsics recognition.
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@bluestreak01
Copy link
Copy Markdown
Member Author

@CodeRabbit did you find any bugs?

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 20, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Fetch all review comments made by coderabbitai on PR `#6673`
gh pr view 6673 --repo questdb/questdb --json reviews,comments --jq '.reviews[] | select(.author.login == "coderabbitai" or .author.login == "coderabbitai[bot]") | {state: .state, body: .body, createdAt: .createdAt}'

Length of output: 3779


🏁 Script executed:

#!/bin/bash
# Fetch review thread comments (line-level comments) from the PR
gh api repos/questdb/questdb/pulls/6673/comments --jq '.[] | select(.user.login == "coderabbitai" or .user.login == "coderabbitai[bot]") | {path: .path, line: .line, body: .body, created_at: .created_at, in_reply_to_id: .in_reply_to_id}'

Length of output: 13959


@bluestreak01: Yes, I found 2 potential bugs and 2 maintenance issues in my previous reviews, and I'm happy to report that all have been addressed:

Bugs Found (Major Issues)

  1. RuntimeIntervalModelBuilder.java (line 401) - Union dropping additional intervals

    • The unionIntervals method was only keeping the last [lo, hi] pair from parsed intervals, discarding any earlier ones from complex interval strings
    • Fixed in commit f2088ac
  2. WhereClauseParser.java (lines 2170-2190) - Timestamp equality mishandled

    • The = branch in extractOrTimestampIntervals was treating timestamp equality as an interval string, which could widen date-only equality to a full-day interval and mishandle numeric literals
    • Fixed in commits e0b6a70 to d024b7e

Maintenance Issues (Minor)

  1. WindowJoinTest.java (line 2618) - Confusing AND instead of OR

    • Initially flagged as a typo, but you clarified this was intentional to prevent intrinsic interval creation for testing window join failure
    • Confirmed as correct design (learning added to my knowledge base)
  2. WhereClauseParserTest.java (line 3315) - Test name/comments misaligned

    • Test name said "NotExtracted" but assertion expected extraction
    • Fixed in commits e0b6a70 to d024b7e

All concerns have been resolved! The code looks good now. 🎉


🧠 Learnings used
Learnt from: bluestreak01
Repo: questdb/questdb PR: 6673
File: core/src/test/java/io/questdb/test/griffin/WindowJoinTest.java:2616-2618
Timestamp: 2026-01-19T21:43:12.938Z
Learning: In test `testWindowJoinFailsWhenSlaveDoesNotSupportTimeFrames` in `core/src/test/java/io/questdb/test/griffin/WindowJoinTest.java`, the use of `AND` between timestamp equality conditions (e.g., `ts = 'X' AND ts = 'Y'`) is intentional to prevent intrinsic interval creation. This ensures the window join fails as expected when the slave doesn't support time frames, which is necessary after PR `#6673` added OR timestamp intrinsics recognition.

If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

@glasstiger
Copy link
Copy Markdown
Contributor

[PR Coverage check]

😍 pass : 100 / 120 (83.33%)

file detail

path covered line new line coverage
🔵 io/questdb/griffin/engine/functions/bool/InTimestampTimestampFunctionFactory.java 17 23 73.91%
🔵 io/questdb/griffin/model/IntervalUtils.java 6 8 75.00%
🔵 io/questdb/griffin/model/RuntimeIntervalModelBuilder.java 9 11 81.82%
🔵 io/questdb/griffin/WhereClauseParser.java 66 76 86.84%
🔵 io/questdb/griffin/model/IntrinsicModel.java 2 2 100.00%

@bluestreak01 bluestreak01 merged commit e6b4f09 into master Jan 20, 2026
43 checks passed
@bluestreak01 bluestreak01 deleted the vi_fix_interval branch January 20, 2026 19:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bugs/friction when handling IN lists with multiple intervals

2 participants