Skip to content

fix(v3): inline the jsonb_entry domain CHECK; plpgsql jsonb_query validator (+19% field_eq regression)#358

Merged
coderdan merged 3 commits into
eql_v3from
dan/cip-3381-v3-jsonb_entry-domain-check-calls-a-non-inlinable-sql
Jul 4, 2026
Merged

fix(v3): inline the jsonb_entry domain CHECK; plpgsql jsonb_query validator (+19% field_eq regression)#358
coderdan merged 3 commits into
eql_v3from
dan/cip-3381-v3-jsonb_entry-domain-check-calls-a-non-inlinable-sql

Conversation

@coderdan

@coderdan coderdan commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Closes #354 (Linear: CIP-3381).

What

  • eql_v3.jsonb_entry: the domain CHECK is now an inline expression mirroring is_valid_ste_vec_entry_payload's body, instead of calling it. A leading VALUE IS NULL OR preserves the validator's STRICT NULL-passes semantics (a bare COALESCE(..., false) would reject the NULL that -> returns for a missing selector).
  • eql_v3.jsonb_query: its CHECK cannot be inlined — validating the sv elements needs a subquery, and CHECK constraints forbid subqueries (discovered the hard way; this is presumably why the validators existed in the first place). Its validator is converted to plpgsql instead — cached plan vs the per-call SQL-function executor, the same finding as v3: opclass-path helper functions are LANGUAGE sql — cannot inline, regresses ORE ordered scans 43% vs v2 (validated fix: plpgsql) #353 — since the needle cast sits on the per-query hot path of every containment scenario.
  • eql_v3.json (document) is deliberately unchanged: its CHECK-crosses-into-eql_v3_internal behaviour is part of the documented privilege contract (docs/reference/permissions.md, gated by v3_privilege_tests). The doc now notes the jsonb_entry cast no longer requires the internal grant; jsonb_query's still does.

Why

Domain constraints can't inline SQL functions, so every ::eql_v3.jsonb_entry cast paid ~18 µs of SQL-function-executor overhead — the needle cast in every field_eq query, and the entire measured +19% v2→v3 regression on JSON/json/field_eq/functional (in-DB 0.011 → 0.029 ms/query with identical eq_term costs between versions). Third confirmed instance of the LANGUAGE-sql-in-non-inlinable-context pattern; attribution data in v3-regressions-report.md on cipherstash/benches#23.

Validation

  • A→B→A on a live 100k bench database (ALTER DOMAIN swap, then restored): field_eq harness 0.0287 → 0.0137 → 0.0285 ms/query; pg_stat_user_functions shows validator calls drop to zero under the inline CHECK. v2's equivalent measures 0.0109 ms on the same harness — the residual is the CHECK expression itself, which v2 also pays.
  • Accept/reject matrix probed on a built + installed bundle for both domains: valid hm/oc entries and needles accepted (incl. empty sv and extra merged fields), missing s/c, dual terms, term-less, ciphertext-carrying needle elements, scalars, JSON null, and {} all rejected; SQL NULL casts still succeed for both.
  • New family/jsonb_check tests: entry inline-CHECK ↔ validator equivalence over the payload corpus (so an edit to one that forgets the other fails CI), a hardcoded accept/reject characterization for jsonb_query, and a language guard that fails if the query validator reverts to LANGUAGE sql.

Follow-up

The document CHECK (is_valid_ste_vec_document_payload, and its per-element calls into the entry validator) still pays function-call costs per INSERT/cast — left as-is here because changing it touches the privilege contract. If ingest-side cost warrants it, the same plpgsql conversion applies; folded into the systemic audit suggested in #354.

https://claude.ai/code/session_01StQnoycoFXMDdSpQ6zKDav

… plpgsql

Domain constraints cannot inline SQL functions, so eql_v3.jsonb_entry's
CHECK calling eql_v3_internal.is_valid_ste_vec_entry_payload paid the
per-call SQL-function executor (~18us) on every cast — the needle cast
in every field_eq query, and the ENTIRE measured +19% v2->v3 regression
on that scenario (cipherstash/benches#23). The CHECK now mirrors the
validator body inline; the leading `VALUE IS NULL OR` preserves the
validator's STRICT NULL-passes semantics (a bare COALESCE(..., false)
would reject the NULL that `->` returns for a missing selector).
Validated A->B->A on a live 100k bench database: 0.0287 -> 0.0137 ->
0.0285 ms/query in-DB, with validator calls dropping to zero.

eql_v3.jsonb_query's CHECK CANNOT be inlined — validating the sv
elements needs a subquery, which CHECK constraints forbid — so its
validator is converted to plpgsql instead (cached plan vs the per-call
SQL-function executor; the #353 finding), since the needle cast sits on
the per-query hot path of every containment scenario.

The eql_v3.json document CHECK is unchanged: it is part of the
documented privilege contract (docs/reference/permissions.md, gated by
v3_privilege_tests). permissions.md now notes the jsonb_entry cast no
longer requires the internal grant; jsonb_query's still does.

New family/jsonb_check tests: entry inline-CHECK <-> validator
equivalence over a payload-shape corpus (accept/reject + NULL), a
hardcoded accept/reject characterization for jsonb_query, and a
language guard so a revert of the query validator to LANGUAGE sql
fails CI.

Closes #354

Claude-Session: https://claude.ai/code/session_01StQnoycoFXMDdSpQ6zKDav
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

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.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4813e1bb-89f5-4bd1-88b1-f7f36162c7cf

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dan/cip-3381-v3-jsonb_entry-domain-check-calls-a-non-inlinable-sql

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.

coderdan added 2 commits July 4, 2026 13:21
The jsonb_entry / jsonb_query domain doc blocks are public API docs
(Doxygen renders src/ with INTERNAL_DOCS at its default NO), and the
issue-#354 performance rationale added there is an internal concern —
wrap it in @internal/@endinternal so the published docs keep only the
user-facing contract. The #357-style notes on the internal validator
functions were already excluded via their blocks' @internal tags.

Claude-Session: https://claude.ai/code/session_01StQnoycoFXMDdSpQ6zKDav
…nb_entry-domain-check-calls-a-non-inlinable-sql
@coderdan coderdan merged commit d1d7412 into eql_v3 Jul 4, 2026
18 checks passed
@coderdan coderdan deleted the dan/cip-3381-v3-jsonb_entry-domain-check-calls-a-non-inlinable-sql branch July 4, 2026 04:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant