Skip to content
Merged
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
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ This is the **Encrypt Query Language (EQL)** - a PostgreSQL extension for search

Adding a scalar encrypted-domain type is one row in the Rust catalog `eql-domains::CATALOG` (`crates/eql-domains/src/lib.rs`): a `DomainFamily` giving the type `name` (e.g. `int8`), its `ScalarKind` (the `kind` field), the `Domain`s mapping each generated (bare) domain name to its fixed index `Term`s (`eq => [Hm]`, `ord`/`ord_ore => [Ore]`), and the `Fixture` value list. Term capabilities are fixed in the `Term` enum's `impl` methods (with unit tests): `Hm` provides equality, and `Ore` provides equality plus ordering. There is no TOML manifest and no Python — the catalog is the source of truth, validated by the compiler (an undefined term or unknown scalar is a compile error) plus catalog `#[test]`s. `mise run build` runs `cargo run -p eql-codegen`, which regenerates the scalar SQL surface into `src/v3/scalars/<T>/` from `CATALOG` at the start of every build; that surface includes supported comparison wrappers plus blockers for native `jsonb` operators that would otherwise be reachable through domain fallback. `cargo run -p eql-codegen` regenerates every type at once (the same call `mise run build` uses; there is no per-type codegen task). The generated `*_types.sql` / `*_functions.sql` / `*_operators.sql` / `*_aggregates.sql` files are gitignored and never committed. The per-type plaintext fixture lists the SQLx matrix consumes are **not** a generated file — they are materialised from each `CATALOG` row at compile time as `eql_domains::INT4_VALUES` / `INT2_VALUES` (the `int_values!` macro) and read directly by `ScalarType::FIXTURE_VALUES`; a Rust source of truth no longer round-trips through a committed generated `.rs`. Generated SQL carries a `-- AUTOMATICALLY GENERATED FILE` header (the project-wide marker `docs:validate` greps on); change the catalog and rebuild, never hand-edit. Hand-written SQL beyond the fixed surface goes in `src/v3/scalars/<T>/<T>_extensions.sql` with no auto-generated header and explicit `-- REQUIRE:` edges — that file IS committed. `jsonb` is out of scope for this scalar materializer.

The same generator also emits the **Rust payload bindings** under `crates/eql-bindings/src/v3/<family>.rs` (structs + `DomainType` impls) and the `inventory.rs` `all()` list, from the same `CATALOG` — committed with a `// @generated` header (unlike the gitignored SQL, because `ts-rs`/`schemars` derive the committed TypeScript/JSON Schema off them and they must exist on a clean clone). The hand-written `DomainType` trait, the shared newtypes (`SchemaVersion`/`Identifier`/`Ciphertext`/`Hmac256`/`OreBlock256`/`BloomFilter`), the `PhantomData` plumbing, and the architectural module doc (including the non-derivable float-NaN and bool storage-only caveats) stay hand-written in `crates/eql-bindings/src/v3/{mod,domain_type,terms}.rs`. Generated structs carry a catalog-derived struct doc — a summary line (`` `eql_v3.<name>` — <capability>. ``) plus a detail line listing the supported operators and required payload keys, all derived from data the catalog already holds (the capability label, `Term::operators_for_terms`, and `ENVELOPE_KEYS` ++ `Term::term_json_keys` — see `struct_doc_lines` in `crates/eql-codegen/src/bindings.rs`). The required-key list makes structural distinctions visible — e.g. `text_ord` lists `` `v` `i` `c` `hm` `ob` `` (dual-term) versus an integer `int4_ord`'s `` `v` `i` `c` `ob` ``. There are **no per-field docs**: per-field/term semantics live on the shared term newtypes (`terms.rs`, flowing into the TS term files and JSON Schema `$defs`), and non-derivable per-family caveats (float-NaN, bool storage-only) in `mod.rs`. Free-form prose belongs at the **struct level** (a future optional catalog `doc` field emitted as extra `#[doc]` lines), never as per-field docs. JSON Schemas are emitted by **schemars 1.x** as JSON Schema 2020-12. `mise run types:generate` regenerates the Rust bindings (via `eql-codegen bindings`) then the TS/JSON; `mise run types:check` is the committed-reference drift gate — it regenerates and `git diff`s all three (`crates/eql-bindings/src/v3` + `bindings/` + `schema/`), the same regenerate-and-diff pattern `codegen:parity` uses for the (gitignored) SQL surface, adapted because the bindings output is committed.

**Adding a new encrypted-domain type: follow `docs/reference/adding-a-scalar-encrypted-domain-type.md`.** The mechanics are fixed for ordered scalar domains; the catalog row only declares the name, kind, bare domain names, and terms. New term behavior belongs in the `Term` enum's `impl` methods in `crates/eql-domains/src` with tests, not in free-form catalog data.

Regeneration is deterministic: an identical `CATALOG` produces byte-identical SQL. If `mise run build` produces unexpected output, the change is in `crates/eql-domains/src` (the catalog/terms) or `crates/eql-codegen/src` (the renderers) — not in random run-to-run variation.
Expand Down
13 changes: 9 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/eql-bindings/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ serde = { version = "1", features = ["derive"] }
# impl pins `const: 2` via serde_json::json!.
serde_json = "1"
ts-rs = "10"
schemars = "0.8"
schemars = "1"

[dev-dependencies]
# Parity oracle: tests/catalog_parity.rs asserts the v3 domain inventory
Expand Down
19 changes: 4 additions & 15 deletions crates/eql-bindings/bindings/v3/Bool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,8 @@ import type { Identifier } from "./Identifier";
import type { SchemaVersion } from "./SchemaVersion";

/**
* `eql_v3.bool` — storage only / encryption-only; every operator is blocked.
* `eql_v3.bool` — storage-only domain.
*
* Operators: none. Required keys: `v` `i` `c`.
*/
export type Bool = {
/**
* Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other
* value fails deserialization.
*/
v: SchemaVersion,
/**
* Table/column identifier. Required by the domain CHECK.
*/
i: Identifier,
/**
* mp_base85 source ciphertext. Required by the domain CHECK.
*/
c: Ciphertext, };
export type Bool = { v: SchemaVersion, i: Identifier, c: Ciphertext, };
19 changes: 4 additions & 15 deletions crates/eql-bindings/bindings/v3/Date.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,8 @@ import type { Identifier } from "./Identifier";
import type { SchemaVersion } from "./SchemaVersion";

/**
* `eql_v3.date` — storage only; every operator is blocked.
* `eql_v3.date` — storage-only domain.
*
* Operators: none. Required keys: `v` `i` `c`.
*/
export type Date = {
/**
* Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other
* value fails deserialization.
*/
v: SchemaVersion,
/**
* Table/column identifier. Required by the domain CHECK.
*/
i: Identifier,
/**
* mp_base85 source ciphertext. Required by the domain CHECK.
*/
c: Ciphertext, };
export type Date = { v: SchemaVersion, i: Identifier, c: Ciphertext, };
23 changes: 4 additions & 19 deletions crates/eql-bindings/bindings/v3/DateEq.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,8 @@ import type { Identifier } from "./Identifier";
import type { SchemaVersion } from "./SchemaVersion";

/**
* `eql_v3.date_eq` — HMAC equality (`=`, `<>`).
* `eql_v3.date_eq` — equality domain.
*
* Operators: `=` `<>`. Required keys: `v` `i` `c` `hm`.
*/
export type DateEq = {
/**
* Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other
* value fails deserialization.
*/
v: SchemaVersion,
/**
* Table/column identifier. Required by the domain CHECK.
*/
i: Identifier,
/**
* mp_base85 source ciphertext. Required by the domain CHECK.
*/
c: Ciphertext,
/**
* HMAC-SHA-256 equality term.
*/
hm: Hmac256, };
export type DateEq = { v: SchemaVersion, i: Identifier, c: Ciphertext, hm: Hmac256, };
23 changes: 4 additions & 19 deletions crates/eql-bindings/bindings/v3/DateOrd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,8 @@ import type { OreBlock256 } from "./OreBlock256";
import type { SchemaVersion } from "./SchemaVersion";

/**
* `eql_v3.date_ord` — full comparison (`=` `<>` `<` `<=` `>` `>=`).
* `eql_v3.date_ord` — ordering domain.
*
* Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `ob`.
*/
export type DateOrd = {
/**
* Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other
* value fails deserialization.
*/
v: SchemaVersion,
/**
* Table/column identifier. Required by the domain CHECK.
*/
i: Identifier,
/**
* mp_base85 source ciphertext. Required by the domain CHECK.
*/
c: Ciphertext,
/**
* Block-ORE order term. Serves equality too.
*/
ob: OreBlock256, };
export type DateOrd = { v: SchemaVersion, i: Identifier, c: Ciphertext, ob: OreBlock256, };
23 changes: 4 additions & 19 deletions crates/eql-bindings/bindings/v3/DateOrdOre.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,8 @@ import type { OreBlock256 } from "./OreBlock256";
import type { SchemaVersion } from "./SchemaVersion";

/**
* `eql_v3.date_ord_ore` — full comparison, scheme-explicit name.
* `eql_v3.date_ord_ore` — ordering domain.
*
* Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `ob`.
*/
export type DateOrdOre = {
/**
* Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other
* value fails deserialization.
*/
v: SchemaVersion,
/**
* Table/column identifier. Required by the domain CHECK.
*/
i: Identifier,
/**
* mp_base85 source ciphertext. Required by the domain CHECK.
*/
c: Ciphertext,
/**
* Block-ORE order term. Serves equality too.
*/
ob: OreBlock256, };
export type DateOrdOre = { v: SchemaVersion, i: Identifier, c: Ciphertext, ob: OreBlock256, };
19 changes: 4 additions & 15 deletions crates/eql-bindings/bindings/v3/Float4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,8 @@ import type { Identifier } from "./Identifier";
import type { SchemaVersion } from "./SchemaVersion";

/**
* `eql_v3.float4` — storage only; every operator is blocked.
* `eql_v3.float4` — storage-only domain.
*
* Operators: none. Required keys: `v` `i` `c`.
*/
export type Float4 = {
/**
* Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other
* value fails deserialization.
*/
v: SchemaVersion,
/**
* Table/column identifier. Required by the domain CHECK.
*/
i: Identifier,
/**
* mp_base85 source ciphertext. Required by the domain CHECK.
*/
c: Ciphertext, };
export type Float4 = { v: SchemaVersion, i: Identifier, c: Ciphertext, };
23 changes: 4 additions & 19 deletions crates/eql-bindings/bindings/v3/Float4Eq.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,8 @@ import type { Identifier } from "./Identifier";
import type { SchemaVersion } from "./SchemaVersion";

/**
* `eql_v3.float4_eq` — HMAC equality (`=`, `<>`).
* `eql_v3.float4_eq` — equality domain.
*
* Operators: `=` `<>`. Required keys: `v` `i` `c` `hm`.
*/
export type Float4Eq = {
/**
* Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other
* value fails deserialization.
*/
v: SchemaVersion,
/**
* Table/column identifier. Required by the domain CHECK.
*/
i: Identifier,
/**
* mp_base85 source ciphertext. Required by the domain CHECK.
*/
c: Ciphertext,
/**
* HMAC-SHA-256 equality term.
*/
hm: Hmac256, };
export type Float4Eq = { v: SchemaVersion, i: Identifier, c: Ciphertext, hm: Hmac256, };
23 changes: 4 additions & 19 deletions crates/eql-bindings/bindings/v3/Float4Ord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,8 @@ import type { OreBlock256 } from "./OreBlock256";
import type { SchemaVersion } from "./SchemaVersion";

/**
* `eql_v3.float4_ord` — full comparison (`=` `<>` `<` `<=` `>` `>=`).
* `eql_v3.float4_ord` — ordering domain.
*
* Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `ob`.
*/
export type Float4Ord = {
/**
* Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other
* value fails deserialization.
*/
v: SchemaVersion,
/**
* Table/column identifier. Required by the domain CHECK.
*/
i: Identifier,
/**
* mp_base85 source ciphertext. Required by the domain CHECK.
*/
c: Ciphertext,
/**
* Block-ORE order term (8 blocks for float). Serves equality too.
*/
ob: OreBlock256, };
export type Float4Ord = { v: SchemaVersion, i: Identifier, c: Ciphertext, ob: OreBlock256, };
23 changes: 4 additions & 19 deletions crates/eql-bindings/bindings/v3/Float4OrdOre.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,8 @@ import type { OreBlock256 } from "./OreBlock256";
import type { SchemaVersion } from "./SchemaVersion";

/**
* `eql_v3.float4_ord_ore` — full comparison, scheme-explicit name.
* `eql_v3.float4_ord_ore` — ordering domain.
*
* Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `ob`.
*/
export type Float4OrdOre = {
/**
* Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other
* value fails deserialization.
*/
v: SchemaVersion,
/**
* Table/column identifier. Required by the domain CHECK.
*/
i: Identifier,
/**
* mp_base85 source ciphertext. Required by the domain CHECK.
*/
c: Ciphertext,
/**
* Block-ORE order term (8 blocks for float). Serves equality too.
*/
ob: OreBlock256, };
export type Float4OrdOre = { v: SchemaVersion, i: Identifier, c: Ciphertext, ob: OreBlock256, };
19 changes: 4 additions & 15 deletions crates/eql-bindings/bindings/v3/Float8.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,8 @@ import type { Identifier } from "./Identifier";
import type { SchemaVersion } from "./SchemaVersion";

/**
* `eql_v3.float8` — storage only; every operator is blocked.
* `eql_v3.float8` — storage-only domain.
*
* Operators: none. Required keys: `v` `i` `c`.
*/
export type Float8 = {
/**
* Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other
* value fails deserialization.
*/
v: SchemaVersion,
/**
* Table/column identifier. Required by the domain CHECK.
*/
i: Identifier,
/**
* mp_base85 source ciphertext. Required by the domain CHECK.
*/
c: Ciphertext, };
export type Float8 = { v: SchemaVersion, i: Identifier, c: Ciphertext, };
23 changes: 4 additions & 19 deletions crates/eql-bindings/bindings/v3/Float8Eq.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,8 @@ import type { Identifier } from "./Identifier";
import type { SchemaVersion } from "./SchemaVersion";

/**
* `eql_v3.float8_eq` — HMAC equality (`=`, `<>`).
* `eql_v3.float8_eq` — equality domain.
*
* Operators: `=` `<>`. Required keys: `v` `i` `c` `hm`.
*/
export type Float8Eq = {
/**
* Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other
* value fails deserialization.
*/
v: SchemaVersion,
/**
* Table/column identifier. Required by the domain CHECK.
*/
i: Identifier,
/**
* mp_base85 source ciphertext. Required by the domain CHECK.
*/
c: Ciphertext,
/**
* HMAC-SHA-256 equality term.
*/
hm: Hmac256, };
export type Float8Eq = { v: SchemaVersion, i: Identifier, c: Ciphertext, hm: Hmac256, };
23 changes: 4 additions & 19 deletions crates/eql-bindings/bindings/v3/Float8Ord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,8 @@ import type { OreBlock256 } from "./OreBlock256";
import type { SchemaVersion } from "./SchemaVersion";

/**
* `eql_v3.float8_ord` — full comparison (`=` `<>` `<` `<=` `>` `>=`).
* `eql_v3.float8_ord` — ordering domain.
*
* Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `ob`.
*/
export type Float8Ord = {
/**
* Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other
* value fails deserialization.
*/
v: SchemaVersion,
/**
* Table/column identifier. Required by the domain CHECK.
*/
i: Identifier,
/**
* mp_base85 source ciphertext. Required by the domain CHECK.
*/
c: Ciphertext,
/**
* Block-ORE order term (8 blocks for float). Serves equality too.
*/
ob: OreBlock256, };
export type Float8Ord = { v: SchemaVersion, i: Identifier, c: Ciphertext, ob: OreBlock256, };
Loading
Loading