Skip to content

fix(sql): ensure FILL(LINEAR) respects the FROM clause#6790

Merged
bluestreak01 merged 3 commits intomasterfrom
nw_sample_fill_linear
Feb 20, 2026
Merged

fix(sql): ensure FILL(LINEAR) respects the FROM clause#6790
bluestreak01 merged 3 commits intomasterfrom
nw_sample_fill_linear

Conversation

@nwoolmer
Copy link
Copy Markdown
Contributor

@nwoolmer nwoolmer commented Feb 19, 2026

FILL(LINEAR) was disregarding FROM, meaning that it would return CALENDAR aligned results instead of FROM aligned results.

Note: there is no intent really for this to support fill expansion i.e. filling before and after the dataset. Only for FROM to be anchoring the initial timestamp, and TO to apply as a bound. It's unclear at this stage how pre and post filling should operate with linear fills.

nwoolmer and others added 2 commits February 19, 2026 13:59
SampleByInterpolateRecordCursorFactory (used for FILL(LINEAR)) was
ignoring the FROM-TO clause when computing bucket boundaries. The
factory and its inner cursor never received the sampleFromFunc /
sampleToFunc parameters, so initTimestamps() fell back to calendar-
aligned buckets (epoch-0 offset) instead of anchoring to the FROM
timestamp.

Pass sampleFromFunc / sampleToFunc through SqlCodeGenerator into the
factory constructor and cursor. Update initTimestamps() to call
sampler.setStart(from) when a FROM bound is present, matching the
logic already used by AbstractNoRecordSampleByCursor.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@nwoolmer nwoolmer added Bug Incorrect or unexpected behavior SQL Issues or changes relating to SQL execution labels Feb 19, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 19, 2026

Walkthrough

This PR extends the SAMPLE BY interpolation functionality to support FROM/TO timestamp boundary functions, propagating these functions from the SQL code generator through the factory to the record cursor implementation. New fields and initialization logic are added to handle timestamp-based alignment when FROM/TO functions are provided.

Changes

Cohort / File(s) Summary
SAMPLE BY Interpolation Implementation
core/src/main/java/io/questdb/griffin/SqlCodeGenerator.java, core/src/main/java/io/questdb/griffin/engine/groupby/SampleByInterpolateRecordCursorFactory.java
Extended constructor signatures to accept and propagate four new timestamp function parameters (sampleFromFunc, sampleFromFuncPos, sampleToFunc, sampleToFuncPos). Added corresponding private fields, initialization in constructors, resource cleanup in close(), and new timestamp alignment logic in initTimestamps() method to compute starting points from FROM/TO functions.
SAMPLE BY Interpolation Tests
core/src/test/java/io/questdb/test/griffin/engine/groupby/SampleByTest.java
Added comprehensive test coverage for SAMPLE BY with various fill strategies (null, prev, linear), covering calendars, offsets, time zones, gaps, duplicate timestamps, error handling, and edge cases for FROM/TO timestamp function behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • puzpuzpuz
  • bluestreak01
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main fix: ensuring FILL(LINEAR) respects the FROM clause, which is the primary change reflected in the code modifications.
Description check ✅ Passed The description accurately explains the bug being fixed and the intended behavior, relating directly to the changeset which adds FROM/TO handling to the FILL(LINEAR) interpolation path.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch nw_sample_fill_linear

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.

@nwoolmer
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 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: 1

🧹 Nitpick comments (2)
core/src/main/java/io/questdb/griffin/engine/groupby/SampleByInterpolateRecordCursorFactory.java (1)

374-377: Consider moving sampleFromFunc.init() and sampleToFunc.init() into parseParams() for consistency.

parseParams() already initializes timezoneNameFunc and offsetFunc. Initializing the new functions outside of it breaks this pattern and could be missed if parseParams() is overridden in a subclass.

♻️ Proposed refactor — consolidate into parseParams()
         parseParams(this, executionContext);
-        sampleFromFunc.init(managedCursor, executionContext);
-        sampleToFunc.init(managedCursor, executionContext);
         areTimestampsInitialized = false;

In parseParams():

     protected void parseParams(RecordCursor base, SqlExecutionContext executionContext) throws SqlException {
         timezoneNameFunc.init(base, executionContext);
         offsetFunc.init(base, executionContext);
+        sampleFromFunc.init(base, executionContext);
+        sampleToFunc.init(base, executionContext);
         rules = null;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@core/src/main/java/io/questdb/griffin/engine/groupby/SampleByInterpolateRecordCursorFactory.java`
around lines 374 - 377, parseParams currently initializes timezoneNameFunc and
offsetFunc but sampleFromFunc.init(...) and sampleToFunc.init(...) are called
externally; move those two init calls into parseParams so all function
initializations are consistent and won't be missed by subclasses overriding
parseParams. Update parseParams to accept (or access) managedCursor and
executionContext as needed, call sampleFromFunc.init(managedCursor,
executionContext) and sampleToFunc.init(managedCursor, executionContext) there,
remove the duplicate init calls from the caller (the block that sets
areTimestampsInitialized = false), and ensure any subclasses of parseParams are
updated if they override it.
core/src/test/java/io/questdb/test/griffin/engine/groupby/SampleByTest.java (1)

6044-6044: Use underscore thousands separator in 60000T.

60000T appears on lines 6044, 6057, 6072, and 6352. Per coding guidelines, numbers with 5 or more digits must use underscores as thousands separators.

Proposed fix (apply at all four locations)
-            SAMPLE BY 60000T
+            SAMPLE BY 60_000T

As per coding guidelines: "Always use underscores as thousands separators in numbers of 5 digits or more in QuestDB SQL and implementation code."

Also applies to: 6057-6057, 6072-6072, 6352-6352

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/src/test/java/io/questdb/test/griffin/engine/groupby/SampleByTest.java`
at line 6044, Replace the numeric literal using five digits without separators
in the SAMPLE BY clauses: change every "SAMPLE BY 60000T" occurrence in
SampleByTest (the SQL/text literal "SAMPLE BY 60000T") to use an underscore
thousands separator ("SAMPLE BY 60_000T"); apply the same replacement for all
other instances of 60000T in this file so numbers with 5+ digits follow the
underscore separator rule.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@core/src/main/java/io/questdb/griffin/engine/groupby/SampleByInterpolateRecordCursorFactory.java`:
- Around line 265-269: The interpolate cursor currently ignores the query TO
boundary: ensure SampleByInterpolateRecordCursorFactory computes and uses
sampleToFunc and its type (sampleToFuncType) like other fill modes instead of
letting hiSample be only sampler.nextTimestamp(prevSample); fetch the TO
timestamp via sampleToFunc.getTimestamp(null), compute sampleToFuncType
(matching how sampleFromFuncType is set in the cursor), and cap hiSample with
that TO value before generating interpolated rows; also keep freeing
sampleToFunc in the existing cleanup path to avoid leaks.

---

Nitpick comments:
In
`@core/src/main/java/io/questdb/griffin/engine/groupby/SampleByInterpolateRecordCursorFactory.java`:
- Around line 374-377: parseParams currently initializes timezoneNameFunc and
offsetFunc but sampleFromFunc.init(...) and sampleToFunc.init(...) are called
externally; move those two init calls into parseParams so all function
initializations are consistent and won't be missed by subclasses overriding
parseParams. Update parseParams to accept (or access) managedCursor and
executionContext as needed, call sampleFromFunc.init(managedCursor,
executionContext) and sampleToFunc.init(managedCursor, executionContext) there,
remove the duplicate init calls from the caller (the block that sets
areTimestampsInitialized = false), and ensure any subclasses of parseParams are
updated if they override it.

In `@core/src/test/java/io/questdb/test/griffin/engine/groupby/SampleByTest.java`:
- Line 6044: Replace the numeric literal using five digits without separators in
the SAMPLE BY clauses: change every "SAMPLE BY 60000T" occurrence in
SampleByTest (the SQL/text literal "SAMPLE BY 60000T") to use an underscore
thousands separator ("SAMPLE BY 60_000T"); apply the same replacement for all
other instances of 60000T in this file so numbers with 5+ digits follow the
underscore separator rule.

@glasstiger
Copy link
Copy Markdown
Contributor

[PR Coverage check]

😍 pass : 15 / 15 (100.00%)

file detail

path covered line new line coverage
🔵 io/questdb/griffin/engine/groupby/SampleByInterpolateRecordCursorFactory.java 15 15 100.00%

@bluestreak01 bluestreak01 changed the title fix(sql): ensure FILL(LINEAR) respects the FROM-TO clause fix(sql): ensure FILL(LINEAR) respects the FROM clause Feb 20, 2026
@bluestreak01 bluestreak01 merged commit 364a45a into master Feb 20, 2026
47 checks passed
@bluestreak01 bluestreak01 deleted the nw_sample_fill_linear branch February 20, 2026 17:20
maciulis pushed a commit to maciulis/questdb that referenced this pull request Feb 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Incorrect or unexpected behavior SQL Issues or changes relating to SQL execution

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants