fix(query): block runtime filters across row-sensitive probe paths by dantengsky · Pull Request #20805 · databendlabs/databend · GitHub
Skip to content

fix(query): block runtime filters across row-sensitive probe paths - #20805

Open
dantengsky wants to merge 4 commits into
databendlabs:mainfrom
dantengsky:fix/m0-rf-row-sensitive
Open

dantengsky wants to merge 4 commits into
databendlabs:mainfrom
dantengsky:fix/m0-rf-row-sensitive

Conversation

@dantengsky

@dantengsky dantengsky commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

I hereby agree to the terms of the CLA available at: https://docs.databend.com/dev/policies/cla/

Motivation

Do not apply a join runtime filter below LIMIT, a window, or a scalar-subquery check. Removing rows there can change the answer or hide a required error.

Two small reproductions

Measured on a one-node OSS server: old base 5c09ab04fdc5, fixed code 5cee5dafcbb5. Each result below was observed three times. Run each example in a fresh session/schema; runtime filters are enabled by default.

1. LIMIT: expected no rows, old version returns 1

CREATE TABLE t(k INT);
CREATE TABLE keep_t(k INT);
INSERT INTO t VALUES (0);
INSERT INTO t VALUES (1);
INSERT INTO keep_t VALUES (1);

SELECT q.k
FROM (SELECT k FROM t ORDER BY k LIMIT 1) q
JOIN keep_t j ON q.k = j.k;

The inner query must select 0 first. It cannot match keep_t, so the result must be empty.

Actual SELECT output Old version Fixed version
RF on (default) 1 no rows
RF off (control) no rows no rows

The old RF removes the block containing 0 before LIMIT, allowing 1 to become the first row. The fix prevents that pushdown.

No SET is required here. Both join-reorder settings were tested. Keep the two separate INSERTs: they create separate blocks, allowing the old RF to prune one. With both values in one block, this tiny case did not show the wrong result.

2. Scalar subquery: expected an error, old version returns 2 30

SET disable_join_reorder = 1;
CREATE TABLE a(k INT);
CREATE TABLE b(k INT, v INT);
CREATE TABLE keep_a(k INT);
INSERT INTO a VALUES (1);
INSERT INTO a VALUES (2);
INSERT INTO b VALUES (1,10), (1,20), (2,30);
INSERT INTO keep_a VALUES (0), (2);

SELECT q.k, q.v
FROM (
  SELECT a.k, (SELECT b.v FROM b WHERE b.k = a.k) AS v
  FROM a
) q
JOIN keep_a c ON q.k = c.k
WHERE q.v IS NOT NULL;

For this plan, key 1 has two scalar values. RF off reports:

1001: Scalar subquery can't return more than one row, but got 2
Actual SELECT output Old version Fixed version
RF on (default) 2 30 — no error error 1001 above
RF off (control) error 1001 error 1001

The old parent RF removes key 1 before the scalar check, hiding the error. Although that intermediate join is labelled Inner, it still enforces scalar cardinality. The fix treats its single_to_inner marker as a barrier.

Why the SET: this small example needs disable_join_reorder = 1 to keep q on the parent's probe side. With the default reordering, the measured plan swaps the sides; the RF targets keep_a instead, and even the old version reports the error. This SET exposes the bad path; it is not a requirement for the defect in general. The separate INSERTs into a again allow block pruning.

enable_join_runtime_filter = 1 is already the default, so neither example needs that SET. Setting it to 0 is only the control shown in the tables, not a reproduction requirement.

Test coverage

  • Row-sensitive SQL: a two-row fixture checks LIMIT, window count and an expression join key with RF off/on. Separate INSERTs allow the old RF to prune a block and expose the incorrect result.
  • Scalar SQL: two input keys and three scalar rows check that RF preserves the duplicate-row error. Comments explain the block layout, keep-side key 0 and required join-reorder setting.
  • Rust: three deterministic tests check operator barriers, safe versus blocked equality endpoints, and converted-Single markers. They assert target selection directly, including that safe targets remain enabled—properties not established by correct SQL results alone.
  • Expected plans: CSE/CTE/self-join plans exclude unsupported materialized-CTE targets; virtual-column/TPC-DS plans exclude unsafe extra endpoints. The five golden files change only RF annotations, not SQL, estimates or timing placeholders.

Tests

  • Unit Test

Both SQL files fail at the intended assertions on the old version and pass over HTTP/MySQL on the fix. The complete RF module (7 tests), query all-targets clippy and formatting checks pass.

Type of change

  • Bug Fix (non-breaking change which fixes an issue)

AI assistance

  • AI usage: AI coding agents investigated the physical RF paths, drafted the scoped checks and synthetic regressions, and assisted validation and privacy review.
  • Responsible human: @dantengsky
  • The responsible human has read every line of this diff and can explain each change

This change is Reviewable

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-21T06:21:49.715898Z 658da97 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions github-actions Bot added the pr-bugfix this PR patches a bug in codebase label Oct 20, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 154ba57d61

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/query/service/src/physical_plans/runtime_filter/builder.rs
@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

⏱ CI timing · Dev

run 39551875187 · 25.9 min (main p50 30.4 min, −4.5 min)

stage this run main p50 Δ
total 25.9 min 30.4 min −4.5 min
build (aarch64, ci profile) 13.7 min 17.6 min −3.9 min
├ deps + crates (parallel) 0.7 min — —
├ databend-query codegen 3.5 min — —
├ databend-binaries link 7.7 min — —
└ sccache hit rate 99.7 % — —
test_unit 15.2 min 15.3 min −0.1 min
mac_check — 13.6 min —
tests after build (49 jobs) 10.3 min 14.2 min −3.9 min
├ queue p50 / max 1.4 min / 3.3 min 1.8 min / 9.4 min −0.3 min
├ sqllogic standalone (8 jobs, max) 4.3 min 4.1 min +0.2 min
├ sqllogic cluster (8 jobs, max) 7.0 min 7.2 min −0.2 min
├ sqllogic stage (2 jobs, max) 5.1 min 4.8 min +0.3 min
├ sqllogic other (10 jobs, max) 5.5 min 5.9 min −0.4 min
├ stateless (2 jobs, max) 6.3 min 6.2 min +0.1 min
├ stateful (4 jobs, max) 4.5 min 4.7 min −0.2 min
├ compat (6 jobs, max) 4.8 min 4.8 min +0.0 min
├ meta / logs / lineage / udf (5 jobs, max) 4.4 min 4.4 min −0.0 min
├ ee (2 jobs, max) 1.1 min 1.1 min −0.0 min
├ private_tasks (1 jobs, max) 8.8 min 8.8 min −0.0 min
└ fuzz (1 jobs, max) 1.1 min 1.1 min −0.1 min

Minutes unless stated; durations exclude queue time unless stated; "max" = longest job in the group. Baseline = median of the last 10 successful runs (Dev: Main on main; Cloud: recent Cloud runs). Build breakdown has no baseline. Updated on each push.

@dantengsky
dantengsky marked this pull request as draft October 20, 2026 13:02
@dantengsky dantengsky added the ci-benchmark-cloud Benchmark: run only cloud tests for tpch/hits label Oct 21, 2026
@dantengsky

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

Reviewed commit: 658da972c4

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@dantengsky
dantengsky marked this pull request as ready for review October 21, 2026 06:14
@github-actions

Copy link
Copy Markdown
Contributor

Docker Image for PR

  • tag: pr-20805-88feda2-1792563419

note: this image tag is only available for internal use.

@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

⏱ CI timing · Cloud (PR image)

run 39552344373 · to image 23.7 min (recent p50 32.4 min, −8.6 min)

stage this run recent p50 Δ
build aarch64 (release) 18.5 min 28.1 min −9.6 min
├ deps + crates (parallel) 3.1 min — —
├ databend-query codegen 4.2 min — —
├ databend-binaries link 7.8 min — —
└ sccache hit rate 99.7 % — —
build x86_64 (release) 17.9 min 25.4 min −7.4 min
├ deps + crates (parallel) 3.3 min — —
├ databend-query codegen 3.7 min — —
├ databend-binaries link 8.4 min — —
└ sccache hit rate 99.7 % — —
docker (multi-arch) 2.9 min 2.9 min +0.0 min

Minutes unless stated; durations exclude queue time unless stated; "max" = longest job in the group. Baseline = median of the last 10 successful runs (Dev: Main on main; Cloud: recent Cloud runs). Build breakdown has no baseline. Updated on each push.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-benchmark-cloud Benchmark: run only cloud tests for tpch/hits pr-bugfix this PR patches a bug in codebase

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant