Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/eql-drop-ignored-query-json-cast.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@cipherstash/eql': patch
---

Drop the `CREATE CAST (public.eql_v3_json_search AS eql_v3.query_json)` that PostgreSQL always ignored (it warned "cast will be ignored because the source data type is a domain" on every install). `doc::eql_v3.query_json` was never converting; call `eql_v3.to_ste_vec_query(col)` instead. Also corrects the `eql-bindings` wire-format docs: `op` backs `_ord` / `_ord_ope` (and `text_ord` / `text_search` carry `hm` + `op`), `ob` only `_ord_ore`.
10 changes: 5 additions & 5 deletions packages/eql/crates/eql-bindings/src/v3/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,22 @@
//! For the generated flat-scalar families, every payload has the shared envelope
//! keys (required by every scalar domain CHECK, mirroring `ENVELOPE_KEYS` in
//! `eql-domains`): `v`, `i`, `c`. Then the domain's required term keys — `hm`
//! for `_eq`, `ob` for `_ord`/`_ord_ore`, `op` for `_ord_ope`, `bf` for
//! for `_eq`, `op` for `_ord`/`_ord_ope`, `ob` for `_ord_ore`, `bf` for
//! `_match`, none for
//! storage-only. `Option` does not appear in the generated scalar structs: the
//! capability **is** the type identity. Hold a [`integer::IntegerEq`] and `hm` is
//! present, guaranteed by the Rust type and (SQL-side) by the domain CHECK. A
//! missing term key is a deserialization error — the Rust analogue of the CHECK
//! constraint.
//!
//! One exception to "`ob` for `_ord`": `text`'s ordered domains carry **both**
//! `hm` and the ordering term (`text_ord`/`text_ord_ore`/`text_search` carry
//! `hm` + `ob`; `text_ord_ope` carries `hm` + `op`), where the non-text
//! One exception to "the ordering term alone": `text`'s ordered domains carry **both**
//! `hm` and the ordering term (`text_ord`/`text_ord_ope`/`text_search` carry
//! `hm` + `op`; `text_ord_ore`/`text_search_ore` carry `hm` + `ob`), where the non-text
//! ordered domains carry the ordering term alone. Text routes `=`/`<>`
//! through `hm` rather than the ordering term because lexicographic ORE/OPE
//! over text is not equality-lossless, so equality needs the HMAC. The
//! generated struct doc surfaces this structurally — its required-keys line
//! lists `hm` `ob` (or `hm` `op`) rather than just the ordering key.
//! lists `hm` `op` (or `hm` `ob`) rather than just the ordering key.
//!
//! The generated flat-scalar types are also **strict**: every scalar struct is
//! `#[serde(deny_unknown_fields)]`, so a payload carrying keys outside the
Expand Down
2 changes: 1 addition & 1 deletion packages/eql/docs/reference/json-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ separate equality-indexed scalar column when server-side grouping is required.
- **`eql_v3.jsonb_document_contains(a public.eql_v3_json_search, b public.eql_v3_json_search) RETURNS boolean`** — true if every selector in `b` is present in `a` (selector-subset containment); backs the `@>` operator.
- **`eql_v3.jsonb_contains(a jsonb, b jsonb)` / `eql_v3.jsonb_contained_by(a jsonb, b jsonb)`** — function-form containment entrypoints for platforms that cannot type an operator call (Supabase PostgREST RPC, for example); same result as `@>` / `<@` (a parity test pins this).
- **`eql_v3.jsonb_array(val jsonb) RETURNS jsonb[]`** — function-form array accessor for the same platforms.
- **`eql_v3.to_ste_vec_query(val public.eql_v3_json_search) RETURNS eql_v3.query_json`** — the GIN-indexable query shape `@>` inlines to.
- **`eql_v3.to_ste_vec_query(val public.eql_v3_json_search) RETURNS eql_v3.query_json`** — the GIN-indexable query shape `@>` inlines to. This is the only document-to-needle conversion: `doc::eql_v3.query_json` is **not** equivalent (PostgreSQL ignores casts from a domain, so it is a plain coercion that fails the `query_json` CHECK for a stored document, whose `sv` entries carry ciphertext).
- **`eql_v3.meta_data(val jsonb)`** — envelope accessor: returns `{i, v, h}` (the key header `h` included so an extracted entry can be decrypted). **`eql_v3.ciphertext(val jsonb)`** — returns the `c` field; on the ste_vec surface `c` is raw AEAD output, decryptable only together with the entry's `s` and the document `h`. **`eql_v3.selector(val jsonb)` / `(entry public.eql_v3_json_entry)`** — selector accessor.

### Path query functions
Expand Down
9 changes: 6 additions & 3 deletions packages/eql/src/v3/json/types.sql
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,9 @@ AS $$
)::eql_v3.query_json
$$;

CREATE CAST (public.eql_v3_json_search AS eql_v3.query_json)
WITH FUNCTION eql_v3.to_ste_vec_query(public.eql_v3_json_search)
AS ASSIGNMENT;
--! @note There is deliberately no `CREATE CAST` from `public.eql_v3_json_search`
--! to `eql_v3.query_json`. PostgreSQL ignores a cast whose source type is a
--! domain (it warns "cast will be ignored because the source data type is a
--! domain"), so `doc::eql_v3.query_json` would never call
--! `to_ste_vec_query`; it is a plain binary coercion that then fails the
--! `query_json` CHECK. Call `eql_v3.to_ste_vec_query(col)` explicitly.
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ aggregate eql_v3.min(public.eql_v3_text_search_ore)
aggregate eql_v3.min(public.eql_v3_timestamp_ord)
aggregate eql_v3.min(public.eql_v3_timestamp_ord_ope)
aggregate eql_v3.min(public.eql_v3_timestamp_ord_ore)
cast public.eql_v3_json_search -> eql_v3.query_json
function eql_v3.->(e public.eql_v3_json_search, selector integer)
function eql_v3.->(e public.eql_v3_json_search, selector text)
function eql_v3.->>(e public.eql_v3_json_search, selector integer)
Expand Down
Loading