ClickHouse / ClickHouse/ClickHouse

Row policy on the shard-local table is bypassed with `serialize_query_plan = 1` on a non-local replica

Open
#112,891 6 comments 0 reactions 1 assignee Claimed by @yariks5s View on GitHub
clickgap-analyzed comp-query-optimizer culprit-pr-not-found potential bug
Dominant language
C++
Stars
49.9k
Forks
9k
Avg merge
21h 32m
Merged PRs (30d)
515

Description

🕵 **Describe the bug**

With `serialize_query_plan = 1`, a read shipped to a non-local replica of a `Distributed` table bypasses the row policy defined on the shard-local table: the receiving server deserializes an already-built read plan, so it never applies its own row policies. With `serialize_query_plan = 0` (text shipping) the same query is filtered correctly.

This matters because defining the policy on the underlying local table is the documented way to filter reads that go through a `Distributed` table (`docs/en/sql-reference/statements/create/row-policy.md`), so the workaround silently returns unfiltered rows in this mode.

**How to reproduce**

Version: 26.8.1.1 (reproduced on `master`, commit `bde560d7b00`).

Server config: a cluster `self` with one replica pointing back at the server itself, and `1`.

```sql
CREATE TABLE rp_leaf (x UInt32, y UInt32) ENGINE = MergeTree ORDER BY x;
INSERT INTO rp_leaf SELECT number, number FROM numbers(10);
CREATE TABLE rp_dist AS rp_leaf ENGINE = Distributed('self', currentDatabase(), rp_leaf);

CREATE ROW POLICY rp_l ON rp_leaf FOR SELECT USING y < 5 TO CURRENT_USER;

SELECT count() FROM rp_dist SETTINGS prefer_localhost_replica = 0, serialize_query_plan = 0; -- 5, correct
SELECT count() FROM rp_dist SETTINGS prefer_localhost_replica = 0, serialize_query_plan = 1; -- 10, policy skipped
SELECT count() FROM rp_leaf; -- 5, correct
```

**Expected behavior**

Both remote reads return `5`. Either the shipped plan carries the filter, or the receiving server re-applies its local row policies to the deserialized plan, or plan shipping is refused when the shard-local table has a row policy that applies (fail close).

**Additional context**

Two stateless tests already pin `serialize_query_plan = 0` for exactly this reason:

- `tests/queries/0_stateless/04498_row_policy_role_scope_interserver.sh`
- `tests/queries/0_stateless/04652_nested_merge_prewhere_type_mismatch.sql` (added by https://github.com/ClickHouse/ClickHouse/pull/112326)

Related: https://github.com/ClickHouse/ClickHouse/pull/112326

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.