Context
#1092 stops the persisted cost estimate from leaking row-security bound values by dropping every plan node's detail (and raw_plan) whenever a row-security directive was applied. That is engine-agnostic and fail-closed, but reviewers lose all predicate / index-condition text on row-secured queries.
Proposal
Instead of dropping the text, scrub the bound values out of it before persistence in DefaultQueryCostEstimateService: replace every value from the resolved RowSecurityDirective.values() / rewrite binds with a marker such as <row-security> in each node's detail and in raw_plan.
Must handle engine-specific renderings: PostgreSQL quoting and casts ('x'::text, doubled ''), MySQL attached_condition, MongoDB extended JSON ({"$eq": "x"}, escaped quotes, numbers/dates), SQL++ / Cypher / Elasticsearch plan output.
Fail closed: after substitution, if any raw bound value (or its escaped form) is still found in a node's detail, drop that detail; if found in raw_plan, null it — i.e. degrade to the #1092 behaviour rather than risk a miss.
Acceptance
- Per-engine unit tests covering quoting/escaping/casts, plus a PostgreSQL integration test asserting the bound value appears nowhere in the persisted or returned estimate while the redacted predicate shape is kept.
- Non-RLS queries unchanged.
- docs/05-backend.md → "Automatic pre-flight cost estimate" and docs/07-security.md updated.
Could be combined with the generic-plan approach (PostgreSQL) — redaction then covers the engines that have no generic-plan EXPLAIN.
Context
#1092 stops the persisted cost estimate from leaking row-security bound values by dropping every plan node's
detail(andraw_plan) whenever a row-security directive was applied. That is engine-agnostic and fail-closed, but reviewers lose all predicate / index-condition text on row-secured queries.Proposal
Instead of dropping the text, scrub the bound values out of it before persistence in
DefaultQueryCostEstimateService: replace every value from the resolvedRowSecurityDirective.values()/ rewrite binds with a marker such as<row-security>in each node'sdetailand inraw_plan.Must handle engine-specific renderings: PostgreSQL quoting and casts (
'x'::text, doubled''), MySQLattached_condition, MongoDB extended JSON ({"$eq": "x"}, escaped quotes, numbers/dates), SQL++ / Cypher / Elasticsearch plan output.Fail closed: after substitution, if any raw bound value (or its escaped form) is still found in a node's
detail, drop thatdetail; if found inraw_plan, null it — i.e. degrade to the #1092 behaviour rather than risk a miss.Acceptance
Could be combined with the generic-plan approach (PostgreSQL) — redaction then covers the engines that have no generic-plan EXPLAIN.