Skip to content

fix(sql): crash when using parallel ORDER BY with LIMIT and JIT filter#6482

Merged
bluestreak01 merged 3 commits intomasterfrom
puzpuzpuz_fix_bind_vars_in_parallel_topk
Dec 2, 2025
Merged

fix(sql): crash when using parallel ORDER BY with LIMIT and JIT filter#6482
bluestreak01 merged 3 commits intomasterfrom
puzpuzpuz_fix_bind_vars_in_parallel_topk

Conversation

@puzpuzpuz
Copy link
Copy Markdown
Contributor

@puzpuzpuz puzpuzpuz commented Dec 1, 2025

Parallel ORDER BY + LIMIT queries that involve a JIT-compiled filter like the following one

SELECT * FROM tab WHERE key = ? ORDER BY price DESC LIMIT 1;

were producing segfaults due to missing bind variable memory initialization.

The crash occurs both when bind variables are used explicitly, but also when the filter involves not-yet-existing symbols, e.g.

SELECT * FROM tab WHERE key = 'non_existing_key' ORDER BY price DESC LIMIT 42;

As a mitigation of this bug, cairo.sql.parallel.topk.enabled=false can be set. This will disable the problematic execution plan.

@puzpuzpuz puzpuzpuz self-assigned this Dec 1, 2025
@puzpuzpuz puzpuzpuz added Bug Incorrect or unexpected behavior SQL Issues or changes relating to SQL execution labels Dec 1, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Dec 1, 2025

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

This PR refactors bind variable memory preparation by extracting prepareBindVarMemory and writeBindVarFunction from AsyncJitFilteredRecordCursorFactory to the centralized AsyncFilterUtils class, enabling code reuse across async filtering components. Updates all call sites accordingly and adds a test for parallel TopK with bind variables.

Changes

Cohort / File(s) Change Summary
Utility Consolidation
core/src/main/java/io/questdb/griffin/engine/table/AsyncFilterUtils.java
Added public prepareBindVarMemory method to initialize bind variable memory, and private writeBindVarFunction helper to encode bind variables by ColumnTypeTag. Supports BOOLEAN, BYTE, GEOBYTE, SHORT, GEOSHORT, CHAR, INT, IPv4, GEOINT, SYMBOL, FLOAT, LONG, GEOLONG, DATE, TIMESTAMP, DOUBLE, UUID types with special handling for SYMBOL initialization and FLOAT (two-word NaN).
Import Updates
core/src/main/java/io/questdb/griffin/engine/table/AsyncGroupByAtom.java, core/src/main/java/io/questdb/griffin/engine/table/AsyncGroupByNotKeyedAtom.java
Updated static imports to use AsyncFilterUtils.prepareBindVarMemory instead of AsyncJitFilteredRecordCursorFactory.prepareBindVarMemory.
Conditional Bind Variable Logic
core/src/main/java/io/questdb/griffin/engine/table/AsyncTopKAtom.java
Added static import of prepareBindVarMemory. Introduced conditional initialization in init() to invoke Function.init and prepareBindVarMemory when bindVarFunctions is non-null.
Method Extraction Source
core/src/main/java/io/questdb/griffin/engine/table/AsyncJitFilteredRecordCursorFactory.java
Removed public prepareBindVarMemory and private writeBindVarFunction methods; added static import from AsyncFilterUtils to use extracted utility.
Test Enhancement
core/src/test/java/io/questdb/test/cairo/fuzz/ParallelTopKFuzzTest.java
Added testParallelTopKWithBindVariablesInFilter test case using BindVariableService. Introduced BindVariablesInitializer functional interface for pre-query context setup and extended testParallelTopK overloads to support optional bind variable initialization.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Type encoding logic: Verify that writeBindVarFunction correctly handles all ColumnTypeTag cases (especially SYMBOL initialization and FLOAT two-word NaN representation).
  • Method signature consistency: Ensure the extracted prepareBindVarMemory method in AsyncFilterUtils has identical behavior to the original in AsyncJitFilteredRecordCursorFactory.
  • Bind variable lifecycle in AsyncTopKAtom: Confirm that the conditional initialization of bindVarFunctions and memory preparation in init() preserves correct resource lifecycle and initialization order.
  • Test coverage: Review that the new testParallelTopKWithBindVariablesInFilter test adequately validates the refactored code path.

Suggested reviewers

  • bluestreak01
  • RaphDal

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main bug fix: addressing a crash in parallel ORDER BY with LIMIT queries using JIT filters. The title accurately reflects the primary change across all modified files.
Description check ✅ Passed The description is directly related to the changeset, explaining the bug (segfaults with parallel ORDER BY + LIMIT + JIT filter) and the root cause (missing bind variable memory initialization). It matches the file changes that add bind variable memory initialization.

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.

@puzpuzpuz
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Dec 1, 2025

✅ 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: 0

🧹 Nitpick comments (1)
core/src/main/java/io/questdb/griffin/engine/table/AsyncFilterUtils.java (1)

120-189: Per‑type bind‑var serialization matches expected layout; consider tiny error‑message tweak

The switch over ColumnType.tagOf(function.getType()) covers the expected scalar and geo types, writes fixed‑size representations into MemoryCARW (with the special FLOAT handling and 128‑bit UUID), and fails fast for unsupported types. The SYMBOL branch correctly ensures initialization with symbolTableSource/executionContext before reading the int ID.

As a minor polish, you might consider using ColumnType.nameOf(columnType) instead of columnTypeTag in the default case to preserve any additional type information in the error message, but this is non‑blocking.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between be9aa81 and 1129eb2.

📒 Files selected for processing (6)
  • core/src/main/java/io/questdb/griffin/engine/table/AsyncFilterUtils.java (2 hunks)
  • core/src/main/java/io/questdb/griffin/engine/table/AsyncGroupByAtom.java (1 hunks)
  • core/src/main/java/io/questdb/griffin/engine/table/AsyncGroupByNotKeyedAtom.java (1 hunks)
  • core/src/main/java/io/questdb/griffin/engine/table/AsyncJitFilteredRecordCursorFactory.java (1 hunks)
  • core/src/main/java/io/questdb/griffin/engine/table/AsyncTopKAtom.java (2 hunks)
  • core/src/test/java/io/questdb/test/cairo/fuzz/ParallelTopKFuzzTest.java (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-10T14:28:48.329Z
Learnt from: mtopolnik
Repo: questdb/questdb PR: 0
File: :0-0
Timestamp: 2025-11-10T14:28:48.329Z
Learning: In AsOfJoinDenseRecordCursorFactoryBase.java, the `backwardScanExhausted` flag is intentionally NOT reset in `toTop()` because backward scan results are reusable across cursor rewinds. The backward scan caches historical matches that remain valid when the cursor is rewound.

Applied to files:

  • core/src/main/java/io/questdb/griffin/engine/table/AsyncJitFilteredRecordCursorFactory.java
🧬 Code graph analysis (4)
core/src/main/java/io/questdb/griffin/engine/table/AsyncGroupByNotKeyedAtom.java (1)
core/src/main/java/io/questdb/griffin/engine/table/AsyncFilterUtils.java (1)
  • AsyncFilterUtils (48-190)
core/src/main/java/io/questdb/griffin/engine/table/AsyncTopKAtom.java (1)
core/src/main/java/io/questdb/griffin/engine/table/AsyncFilterUtils.java (1)
  • AsyncFilterUtils (48-190)
core/src/main/java/io/questdb/griffin/engine/table/AsyncJitFilteredRecordCursorFactory.java (1)
core/src/main/java/io/questdb/griffin/engine/table/AsyncFilterUtils.java (1)
  • AsyncFilterUtils (48-190)
core/src/main/java/io/questdb/griffin/engine/table/AsyncGroupByAtom.java (1)
core/src/main/java/io/questdb/griffin/engine/table/AsyncFilterUtils.java (1)
  • AsyncFilterUtils (48-190)
⏰ 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 (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 (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 Griffin tests on linux-x64-zfs)
  • GitHub Check: New pull request (Rust Test and Lint on linux-jdk17)
  • 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 Other tests on linux-arm64)
  • 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 (SelfHosted Griffin tests on linux-arm64)
  • GitHub Check: New pull request (Hosted Running tests on mac-griffin)
  • GitHub Check: New pull request (SelfHosted Griffin tests on linux-x86-graal)
  • GitHub Check: New pull request (SelfHosted Cairo tests on linux-arm64)
  • GitHub Check: New pull request (SelfHosted Cairo tests on linux-x86-graal)
  • GitHub Check: New pull request (SelfHosted Cairo tests on linux-x64-zfs)
  • GitHub Check: New pull request (Check Changes Check changes)
🔇 Additional comments (12)
core/src/main/java/io/questdb/griffin/engine/table/AsyncGroupByNotKeyedAtom.java (1)

55-55: LGTM!

Import correctly updated to use the centralized AsyncFilterUtils.prepareBindVarMemory. The call site at line 248 remains functionally equivalent.

core/src/main/java/io/questdb/griffin/engine/table/AsyncGroupByAtom.java (1)

69-69: LGTM!

Import correctly updated to use the centralized AsyncFilterUtils.prepareBindVarMemory, consistent with the refactor pattern across other atoms.

core/src/main/java/io/questdb/griffin/engine/table/AsyncTopKAtom.java (2)

54-55: LGTM!

Static import correctly added for the centralized bind variable memory preparation utility.


257-261: Key fix for the crash - properly initializes bind variable memory for parallel TopK queries.

This addition correctly addresses the segmentation fault by ensuring bind variable memory is initialized before JIT-compiled filters access it. The pattern matches the initialization in AsyncGroupByAtom and AsyncGroupByNotKeyedAtom.

core/src/test/java/io/questdb/test/cairo/fuzz/ParallelTopKFuzzTest.java (4)

29-31: LGTM!

Required imports added for bind variable test functionality.


151-163: Good test coverage for the fix.

This test directly exercises the crash scenario from the PR description: parallel ORDER BY + LIMIT with a bind variable in the filter. The expected result correctly matches testParallelTopKFilter which uses the literal 'k0'.


165-167: Clean refactoring of the test helper.

The delegation pattern keeps existing tests unchanged while adding bind variable support. The initializer is called early in the test setup (before table creation), which ensures bind variables are available throughout the test execution.

Also applies to: 169-178


211-213: LGTM!

Simple and appropriate functional interface for test initialization.

core/src/main/java/io/questdb/griffin/engine/table/AsyncJitFilteredRecordCursorFactory.java (2)

60-60: LGTM!

Import correctly updated to use the centralized AsyncFilterUtils.prepareBindVarMemory, completing the refactor from this class.


372-377: Good refactor - AsyncJitFilterAtom.init() now uses the centralized utility.

The initialization pattern remains consistent with other atoms while eliminating code duplication. The bind variable memory preparation is correctly called after Function.init().

core/src/main/java/io/questdb/griffin/engine/table/AsyncFilterUtils.java (2)

27-37: New imports align with added bind‑var handling helpers

The additional imports (ColumnType, SymbolTableSource, SqlException, SqlExecutionContext, CompiledFilterSymbolBindVariable) are all used by the new helper methods and look consistent with their responsibilities.


104-118: Bind‑var memory preparation is straightforward and side‑effect safe

The logic to truncate bindVarMemory only when bindVarFunctions.size() > 0 and then serialize each function via writeBindVarFunction is clean and avoids unnecessary work when there are no bind variables. This should correctly initialize the bind‑var buffer before compiled filter execution.

@glasstiger
Copy link
Copy Markdown
Contributor

[PR Coverage check]

😍 pass : 47 / 52 (90.38%)

file detail

path covered line new line coverage
🔵 io/questdb/griffin/engine/table/AsyncFilterUtils.java 44 49 89.80%
🔵 io/questdb/griffin/engine/table/AsyncTopKAtom.java 3 3 100.00%

@bluestreak01 bluestreak01 merged commit 6ebd268 into master Dec 2, 2025
43 checks passed
@bluestreak01 bluestreak01 deleted the puzpuzpuz_fix_bind_vars_in_parallel_topk branch December 2, 2025 07:43
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