Skip to content

Commit 008d69c

Browse files
committed
refactor(v3): rename ore_block_u64_8_256 -> ore_block_256 (SEM type + tests)
Renames the self-contained eql_v3 SEM type and its symbols to the width-agnostic name ahead of the N-block comparator generalization. eql_v2.ore_block_u64_8_256 (the public API) is unchanged. Deviations from the plan, both verified against schema context: - Also renamed the v3 operator class/family drops in tests/sqlx/fixtures/drop_operator_classes.sql (lines 23-24), which the plan's Task 1.7 file list omitted; without it the Supabase-simulation fixture would silently no-op (DROP ... IF EXISTS) on the renamed v3 opclass and leave it installed. - Did NOT rename tests/sqlx/src/index_types.rs ORE_BLOCK_U64_8_256: its value is the cipherstash crypto index-term identifier (sibling to hm/ore64/ore_cllw_u64_8), not the v3 SQL type name, and it is unused. The v3 SQL-type rename does not change the wire index identifier.
1 parent ea976a5 commit 008d69c

21 files changed

Lines changed: 324 additions & 322 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ Each entry that ships in a published release links to the PR that introduced it.
2323
### Added
2424

2525
- **`eql_v3` encrypted-JSONB (SteVec) document type.** A self-contained encrypted-JSONB surface in the `eql_v3` schema: the storage domain `eql_v3.json` plus `eql_v3.ste_vec_entry` (a single sv element) and `eql_v3.ste_vec_query` (a containment needle). Encrypted JSON documents are searchable without decryption via document containment (`@>`, `<@`), field/array access (`->`, `->>`, `jsonb_path_query` / `_exists` / `_query_first`, `jsonb_array_length` / `_elements` / `_elements_text`), entry equality (`=`, `<>`) on extracted leaves, and entry-level ordered range (`<`, `<=`, `>`, `>=`) on ordered leaves via CLLW ORE (`eql_v3.ore_cllw`). Comparisons are leaf-level only: root-document `=`, `<>`, `<`, `<=`, `>`, `>=` are blocked (they raise rather than falling through to native whole-`jsonb` comparison). Index via a functional index on the `eql_v3.eq_term` / `eql_v3.ore_cllw` extractors (entry equality / ordering) and `eql_v3.to_ste_vec_query(col)::jsonb jsonb_path_ops` GIN (containment); ordered leaves get a default btree opclass on `eql_v3.ore_cllw`. Every other native `jsonb` operator reachable through domain fallback (`?`, `?|`, `?&`, `@?`, `@@`, `#>`, `#>>`, `-`, `#-`, `||`, and the root comparisons above) is blocked — it raises rather than silently routing to plaintext-jsonb semantics. Note: because `eql_v3.json` is a `jsonb` domain, selector/operand literals must be typed (`col -> 'sel'::text` — the CipherStash Proxy interface passes typed parameters); a *bare untyped* literal (`col -> 'sel'`) resolves to the native `jsonb` operator instead of the encrypted one (PostgreSQL reduces the domain to its base type for unknown-typed literals), and the same caveat applies to the native-jsonb blockers — see `docs/decisions/2026-06-10-eql-v3-json-type-kind.md`. The whole surface owns its SEM index-term types (`eql_v3.hmac_256`, `eql_v3.ore_cllw`) and has no `eql_v2` dependency (CI-gated by `test:self_contained_v3` and the standalone `release/cipherstash-encrypt-v3.sql` installer). Why: a type-safe, searchable encrypted JSONB document column namespaced under `eql_v3`, complementing the scalar encrypted-domain families. The existing `eql_v2` SteVec surface on `eql_v2_encrypted` is unchanged. ([#267](https://github.com/cipherstash/encrypt-query-language/pull/267))
26-
- **`eql_v3` encrypted-domain schema, with the `int4` family as its first member.** Encrypted-domain type families now live in a new, additional `eql_v3` schema (the existing `eql_v2` schema is unchanged — it keeps the core types/operators and stays the documented public API). Four jsonb-backed domains for encrypted `int4` columns: `eql_v3.int4` (storage-only), `eql_v3.int4_eq` (`=` / `<>` via HMAC), and `eql_v3.int4_ord` / `eql_v3.int4_ord_ore` (also `<` `<=` `>` `>=` via ORE block terms). Supported comparisons resolve to inlinable wrappers; the native `jsonb` operator surface reachable through domain fallback is blocked (raises rather than silently mis-resolving). Each domain's `CHECK` requires the EQL envelope (`v`, `i`), the ciphertext (`c`), and the variant's index term(s), and pins the payload version (`VALUE->>'v' = '2'`, matching `eql_v2._encrypted_check_v`) — so a missing key or wrong-version payload is rejected on insert or cast rather than surfacing later at query time. Index via a functional index on the `eql_v3.eq_term` / `eql_v3.ord_term` extractors, not an operator class on the domain. The extractors return the searchable-encrypted-metadata index-term types `eql_v3.hmac_256` / `eql_v3.ore_block_u64_8_256`, which `eql_v3` owns directly (see the self-contained `eql_v3` schema entry below). Why: a type-safe, per-capability encrypted integer column instead of the untyped `eql_v2_encrypted`, namespaced under its own schema. This is the reference scalar implementation for the generated domain family. ([#239](https://github.com/cipherstash/encrypt-query-language/pull/239), supersedes [#225](https://github.com/cipherstash/encrypt-query-language/pull/225))
26+
- **`eql_v3` encrypted-domain schema, with the `int4` family as its first member.** Encrypted-domain type families now live in a new, additional `eql_v3` schema (the existing `eql_v2` schema is unchanged — it keeps the core types/operators and stays the documented public API). Four jsonb-backed domains for encrypted `int4` columns: `eql_v3.int4` (storage-only), `eql_v3.int4_eq` (`=` / `<>` via HMAC), and `eql_v3.int4_ord` / `eql_v3.int4_ord_ore` (also `<` `<=` `>` `>=` via ORE block terms). Supported comparisons resolve to inlinable wrappers; the native `jsonb` operator surface reachable through domain fallback is blocked (raises rather than silently mis-resolving). Each domain's `CHECK` requires the EQL envelope (`v`, `i`), the ciphertext (`c`), and the variant's index term(s), and pins the payload version (`VALUE->>'v' = '2'`, matching `eql_v2._encrypted_check_v`) — so a missing key or wrong-version payload is rejected on insert or cast rather than surfacing later at query time. Index via a functional index on the `eql_v3.eq_term` / `eql_v3.ord_term` extractors, not an operator class on the domain. The extractors return the searchable-encrypted-metadata index-term types `eql_v3.hmac_256` / `eql_v3.ore_block_256`, which `eql_v3` owns directly (see the self-contained `eql_v3` schema entry below). Why: a type-safe, per-capability encrypted integer column instead of the untyped `eql_v2_encrypted`, namespaced under its own schema. This is the reference scalar implementation for the generated domain family. ([#239](https://github.com/cipherstash/encrypt-query-language/pull/239), supersedes [#225](https://github.com/cipherstash/encrypt-query-language/pull/225))
2727
- **`eql_v3.int2` encrypted-domain type family.** Four jsonb-backed domains for encrypted `int2` columns — `eql_v3.int2` (storage-only), `eql_v3.int2_eq` (`=` / `<>` via HMAC), and `eql_v3.int2_ord` / `eql_v3.int2_ord_ore` (also `<` `<=` `>` `>=` via ORE block terms, with `MIN` / `MAX` aggregates) — generated from the `int2` row in `eql-scalars::CATALOG` by the same materializer as the `eql_v3.int4` reference. Index via a functional index on the `eql_v3.eq_term` / `eql_v3.ord_term` extractors, not an operator class on the domain. Why: a type-safe, per-capability encrypted `smallint` column, proving the scalar generator generalizes beyond the `int4` reference. ([#243](https://github.com/cipherstash/encrypt-query-language/pull/243))
2828
- **`eql_v3.int8` encrypted-domain type family.** Four jsonb-backed domains for encrypted `int8` columns — `eql_v3.int8` (storage-only), `eql_v3.int8_eq` (`=` / `<>` via HMAC), and `eql_v3.int8_ord` / `eql_v3.int8_ord_ore` (also `<` `<=` `>` `>=` via ORE block terms, with `MIN` / `MAX` aggregates) — generated from the `int8` row in `eql-scalars::CATALOG` by the same materializer as the `eql_v3.int4` reference. Index via a functional index on the `eql_v3.eq_term` / `eql_v3.ord_term` extractors, not an operator class on the domain. Why: a type-safe, per-capability encrypted `bigint` column, extending the scalar generator across the full 64-bit integer width. ([#253](https://github.com/cipherstash/encrypt-query-language/pull/253))
2929
- **`eql_v3.date` encrypted-domain type family.** Four jsonb-backed domains for encrypted `date` columns — `eql_v3.date` (storage-only), `eql_v3.date_eq` (`=` / `<>` via HMAC), and `eql_v3.date_ord` / `eql_v3.date_ord_ore` (also `<` `<=` `>` `>=` via ORE block terms, with `MIN` / `MAX` aggregates) — generated from the `date` row in `eql-scalars::CATALOG` by the same materializer as the `eql_v3.int4` reference. Plaintexts encrypt under the `date` cast and compare via the same ORE block terms as the integer scalars (ORE is plaintext-agnostic — dates order like integers). Index via a functional index on the `eql_v3.eq_term` / `eql_v3.ord_term` extractors, not an operator class on the domain. Why: the first **non-integer ordered** scalar encrypted-domain type — a type-safe, per-capability encrypted `date` column — proving the generator and SQLx test matrix generalize beyond fixed-width integers. ([#256](https://github.com/cipherstash/encrypt-query-language/pull/256))
3030
- **`eql_v3.timestamptz` encrypted-domain type family (equality-only).** Two jsonb-backed domains for encrypted `timestamptz` columns — `eql_v3.timestamptz` (storage-only) and `eql_v3.timestamptz_eq` (`=` / `<>` via HMAC) — generated from the `timestamptz` row in `eql-scalars::CATALOG` by the same materializer as the `eql_v3.date` family. Values are **UTC-normalized** (cipherstash has no timezone-preserving type): plaintexts encrypt under the `timestamp` cast. Index via a functional index on the `eql_v3.eq_term` extractor, not an operator class on the domain. **Ordering (`<` `<=` `>` `>=`, `MIN` / `MAX`) is deferred:** cipherstash encrypts `Plaintext::Timestamp` at native 12-block ORE width, but EQL's only ORE comparator (`eql_v2.compare_ore_block_u64_8_256_term`) is hardcoded to 8 blocks, so ordered timestamptz domains would silently mis-order. There are no `eql_v3.timestamptz_ord` / `_ord_ore` domains and no timestamptz `MIN` / `MAX` aggregates until a wide-ORE (12-block) term lands — tracked in [#241](https://github.com/cipherstash/encrypt-query-language/issues/241). Why: a type-safe, equality-searchable encrypted UTC-timestamp column, stacking on the `date` temporal-scalar foundation; ordering follows once the comparator supports the native ciphertext width. ([#257](https://github.com/cipherstash/encrypt-query-language/pull/257))
3131
- **Per-domain `MIN` / `MAX` aggregates for the encrypted-domain family.** `eql_v3.min(eql_v3.<T>_ord)` / `eql_v3.max(eql_v3.<T>_ord)` (and the `_ord_ore` twin) are generated for every ord-capable scalar variant, giving type-safe extrema on domain-typed columns — comparison routes through the variant's `<` / `>` operator (ORE block term, no decryption). The aggregates are declared `PARALLEL = SAFE` with a combine function (the state function itself — min/max are associative), so PostgreSQL can use partial/parallel aggregation on large `GROUP BY` workloads. Why: the new domain types previously had no equivalent of the composite-type aggregates. The existing `eql_v2.min(eql_v2_encrypted)` / `eql_v2.max(eql_v2_encrypted)` aggregates are **retained** and continue to work on `eql_v2_encrypted` columns; the per-domain aggregates are additive and coexist with them. ([#239](https://github.com/cipherstash/encrypt-query-language/pull/239))
3232
- **`eql_v3.text` encrypted-domain family (`text`, `text_eq`, `text_match`, `text_ord`, `text_ord_ore`).** Adds equality (`=` / `<>` via HMAC), match (`@>` / `<@` via a new self-contained `eql_v3.bloom_filter` SEM index term), and ORE ordering (`<` `<=` `>` `>=`, `min` / `max`) for encrypted text, at parity with EQL v2 text — generated from the `text` row in `eql-scalars::CATALOG` by the same materializer as the `eql_v3.int4` reference. `text` is the first scalar to add a new index `Term` (`Bloom`) and the first non-integer, unbounded ordered kind (lexicographic pivots, hand-written `impl ScalarType`). Index via a functional index on the `eql_v3.eq_term` / `eql_v3.ord_term` / `eql_v3.match_term` extractors, not an operator class on the domain. Why: brings searchable encrypted text to the namespaced, `eql_v2`-free `eql_v3` surface. Match is exposed as bloom-filter containment on the `text_match` domain — deliberately *not* SQL `LIKE` (no wildcard/anchoring; probabilistic ngram containment) — and never backs equality (which always routes through `Hm`). ([#260](https://github.com/cipherstash/encrypt-query-language/pull/260))
33-
- **Self-contained `eql_v3` schema + standalone `release/cipherstash-encrypt-v3.sql` installer.** The `eql_v3` encrypted-domain surface no longer depends on `eql_v2` at runtime: it now owns its own copies of the searchable-encrypted-metadata (SEM) index-term types — `eql_v3.hmac_256` and `eql_v3.ore_block_u64_8_256` (with its btree operator class) — so the `eql_v3.eq_term` / `eql_v3.ord_term` extractors return `eql_v3` types and no `eql_v2.<symbol>` appears anywhere in the v3 SQL. The whole v3 surface relocated under a single `src/v3/` tree (`src/v3/sem/` for the hand-written SEM types, `src/v3/scalars/` for the generated domain families). A new build variant ships the `eql_v3` schema on its own as `release/cipherstash-encrypt-v3.sql`, installable into a database with no `eql_v2` present; a CI gate greps that artifact and its dependency closure to keep it `eql_v2`-free. Why: a clean foundation for the per-scalar encrypted-domain model to stand alone, ahead of it replacing the `eql_v2_encrypted` composite column type. This is additive — a new schema and a new artifact — and leaves `eql_v2` byte-for-byte unchanged. ([#255](https://github.com/cipherstash/encrypt-query-language/pull/255))
33+
- **Self-contained `eql_v3` schema + standalone `release/cipherstash-encrypt-v3.sql` installer.** The `eql_v3` encrypted-domain surface no longer depends on `eql_v2` at runtime: it now owns its own copies of the searchable-encrypted-metadata (SEM) index-term types — `eql_v3.hmac_256` and `eql_v3.ore_block_256` (with its btree operator class) — so the `eql_v3.eq_term` / `eql_v3.ord_term` extractors return `eql_v3` types and no `eql_v2.<symbol>` appears anywhere in the v3 SQL. The whole v3 surface relocated under a single `src/v3/` tree (`src/v3/sem/` for the hand-written SEM types, `src/v3/scalars/` for the generated domain families). A new build variant ships the `eql_v3` schema on its own as `release/cipherstash-encrypt-v3.sql`, installable into a database with no `eql_v2` present; a CI gate greps that artifact and its dependency closure to keep it `eql_v2`-free. Why: a clean foundation for the per-scalar encrypted-domain model to stand alone, ahead of it replacing the `eql_v2_encrypted` composite column type. This is additive — a new schema and a new artifact — and leaves `eql_v2` byte-for-byte unchanged. ([#255](https://github.com/cipherstash/encrypt-query-language/pull/255))
3434
- **`eql_v3.min` / `eql_v3.max` aggregates over `eql_v3.ste_vec_entry`.** SteVec document entries extracted at a selector (`doc -> 'sel'`) can now be aggregated like ordered scalars: `eql_v3.min(doc -> 'sel')` / `eql_v3.max(...)` return the entry with the smallest / largest ordered leaf. Ordering routes through the entry's `oc` (CLLW ORE) term via `eql_v3.ore_cllw` — the same comparator the entry `<` / `<=` / `>` / `>=` operators use, not the scalar Block-ORE `ord_term`. Only `oc`-carrying entries are orderable: an entry without an `oc` term (`eql_v3.ore_cllw` returns NULL) is non-orderable and is ignored by the aggregate — the same way the `eql_v3.ore_cllw` btree NULL-filters such rows — so a mix of `oc`-carrying and `oc`-less entries yields the extremum of the orderable subset rather than a corrupted result. Declared `PARALLEL = SAFE` with a combine function (the state function itself), so partial / parallel aggregation is available on large `GROUP BY` workloads. Why: brings encrypted-JSONB entry ordering to parity with the scalar encrypted-domain families' `MIN` / `MAX`, and lets the shared scalar behaviour matrix cover entry aggregation. Additive — the document and entry comparison surface is otherwise unchanged. ([#267](https://github.com/cipherstash/encrypt-query-language/pull/267))
3535

3636
### Changed

crates/eql-codegen/src/context.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ pub struct FunctionsContext {
133133
/// Build the inlinable index-extractor entry for a domain term.
134134
///
135135
/// The `RETURNS` type name equals the constructor name (`hmac_256`,
136-
/// `ore_block_u64_8_256`); qualify it with `SCHEMA` — the same schema as the
136+
/// `ore_block_256`); qualify it with `SCHEMA` — the same schema as the
137137
/// body's constructor call — so the declared return type and the call stay in
138138
/// lockstep. `Term::returns()` is intentionally not used.
139139
pub fn extractor_entry(term: Term) -> FnEntry {

crates/eql-codegen/src/generate.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ mod tests {
468468
let sql = render_functions_file(s.token, domain(s, "_ord"));
469469
assert_eq!(sql.matches("CREATE FUNCTION").count(), 45);
470470
assert!(sql.contains("CREATE FUNCTION eql_v3.ord_term(a eql_v3.int4_ord)"));
471-
assert!(sql.contains("RETURNS eql_v3.ore_block_u64_8_256"));
471+
assert!(sql.contains("RETURNS eql_v3.ore_block_256"));
472472
assert_eq!(
473473
sql.matches("LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE")
474474
.count(),

crates/eql-scalars/src/term.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ impl Term {
2828
pub const fn ctor(self) -> &'static str {
2929
match self {
3030
Term::Hm => "hmac_256",
31-
Term::Ore => "ore_block_u64_8_256",
31+
Term::Ore => "ore_block_256",
3232
Term::Bloom => "bloom_filter",
3333
}
3434
}
@@ -57,8 +57,8 @@ impl Term {
5757
match self {
5858
Term::Hm => &["src/v3/sem/hmac_256/functions.sql"],
5959
Term::Ore => &[
60-
"src/v3/sem/ore_block_u64_8_256/functions.sql",
61-
"src/v3/sem/ore_block_u64_8_256/operators.sql",
60+
"src/v3/sem/ore_block_256/functions.sql",
61+
"src/v3/sem/ore_block_256/operators.sql",
6262
],
6363
Term::Bloom => &["src/v3/sem/bloom_filter/functions.sql"],
6464
}

crates/eql-scalars/src/tests.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -193,14 +193,14 @@ mod term_tests {
193193
let ore = Term::Ore;
194194
assert_eq!(ore.json_key(), "ob");
195195
assert_eq!(ore.extractor(), "ord_term");
196-
assert_eq!(ore.ctor(), "ore_block_u64_8_256");
196+
assert_eq!(ore.ctor(), "ore_block_256");
197197
assert_eq!(ore.role(), Role::Ord);
198198
assert_eq!(ore.operators(), &["=", "<>", "<", "<=", ">", ">="]);
199199
assert_eq!(
200200
ore.requires(),
201201
&[
202-
"src/v3/sem/ore_block_u64_8_256/functions.sql",
203-
"src/v3/sem/ore_block_u64_8_256/operators.sql",
202+
"src/v3/sem/ore_block_256/functions.sql",
203+
"src/v3/sem/ore_block_256/operators.sql",
204204
]
205205
);
206206
}
@@ -277,8 +277,8 @@ mod term_helper_tests {
277277
assert_eq!(
278278
Term::term_requires(&[Term::Ore, Term::Ore, Term::Hm]),
279279
vec![
280-
"src/v3/sem/ore_block_u64_8_256/functions.sql",
281-
"src/v3/sem/ore_block_u64_8_256/operators.sql",
280+
"src/v3/sem/ore_block_256/functions.sql",
281+
"src/v3/sem/ore_block_256/operators.sql",
282282
"src/v3/sem/hmac_256/functions.sql",
283283
]
284284
);

src/v3/schema.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
--! Creates the eql_v3 schema, which houses the self-contained encrypted-domain
55
--! type families (eql_v3.int4, eql_v3.int8, and future scalar domains): their
66
--! jsonb-backed domains, the searchable-encrypted-metadata (SEM) index-term
7-
--! types they use (eql_v3.hmac_256, eql_v3.ore_block_u64_8_256), the index-term
7+
--! types they use (eql_v3.hmac_256, eql_v3.ore_block_256), the index-term
88
--! extractors, comparison wrappers, blockers, and aggregates. The v3 surface is
99
--! self-contained — it owns every type it needs and has no runtime dependency
1010
--! on another EQL schema.

src/v3/sem/bloom_filter/functions.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
--! @return boolean True when the `bf` key is present and non-null.
1717
--!
1818
--! @internal Defined for parity with the eql_v3 SEM index-term predicates
19-
--! (`has_hmac_256` / `has_ore_block_u64_8_256`); it is not currently called by
19+
--! (`has_hmac_256` / `has_ore_block_256`); it is not currently called by
2020
--! the extractor below, which gates on value-shape inline, nor by the generated
2121
--! domain CHECK, which tests `bf` presence via the envelope-key skeleton. Kept
2222
--! as the canonical presence test for callers that need one.

0 commit comments

Comments
 (0)