diff --git a/.gitignore b/.gitignore index 37af0cf2..98b64012 100644 --- a/.gitignore +++ b/.gitignore @@ -227,6 +227,7 @@ tests/sqlx/migrations/001_install_eql.sql tests/sqlx/fixtures/eql_v2* tests/sqlx/fixtures/v3_ste_vec.sql tests/sqlx/fixtures/v3_doc_int4.sql +tests/sqlx/fixtures/v3_numeric_collision.sql # Generated encrypted-domain SQL — regenerated by `tasks/build.sh` from the # eql-scalars::CATALOG via `cargo run -p eql-codegen` on every build. The diff --git a/CHANGELOG.md b/CHANGELOG.md index 88814a49..017b05d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,20 +23,25 @@ Each entry that ships in a published release links to the PR that introduced it. ### Added - **`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)) -- **`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)) +- **`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)) - **`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)) - **`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)) - **`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)) -- **`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)) +- **`eql_v3.timestamptz` encrypted-domain type family (ordered).** Four jsonb-backed domains for encrypted `timestamptz` columns — `eql_v3.timestamptz` (storage-only), `eql_v3.timestamptz_eq` (`=` / `<>` via HMAC), and `eql_v3.timestamptz_ord` / `eql_v3.timestamptz_ord_ore` (also `<` `<=` `>` `>=`, `MIN` / `MAX` via 12-block ORE) — 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. Ordering works because the `eql_v3` ORE block comparator now derives its block count from the ciphertext width (see the comparator entry below) instead of assuming 8. Index via a functional index on the `eql_v3.eq_term` / `eql_v3.ord_term` extractors, not an operator class on the domain. ([#257](https://github.com/cipherstash/encrypt-query-language/pull/257)) +- **`eql_v3.numeric` encrypted-domain type family (ordered).** Four jsonb-backed domains for encrypted `numeric` / `decimal` columns — `eql_v3.numeric` (storage-only), `eql_v3.numeric_eq` (`=` / `<>` via HMAC), and `eql_v3.numeric_ord` / `eql_v3.numeric_ord_ore` (also `<` `<=` `>` `>=`, `MIN` / `MAX` via 14-block ORE) — generated from the `numeric` row in `eql-scalars::CATALOG`. cipherstash encrypts `Plaintext::Decimal` at native 14-block ORE width; ordering matches `rust_decimal::Decimal` ordering exactly (equivalent scales such as `1` and `1.0` collide, like Postgres `numeric`). Index via a functional index on the `eql_v3.eq_term` / `eql_v3.ord_term` extractors. Why: a type-safe, ordered encrypted decimal column, the first scalar to exercise an ORE term wider than 8 blocks. ([#241](https://github.com/cipherstash/encrypt-query-language/issues/241), [#276](https://github.com/cipherstash/encrypt-query-language/pull/276)) - **Per-domain `MIN` / `MAX` aggregates for the encrypted-domain family.** `eql_v3.min(eql_v3._ord)` / `eql_v3.max(eql_v3._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)) - **`eql_v3.text` encrypted-domain family (`text`, `text_eq`, `text_match`, `text_ord`, `text_ord_ore`, `text_search`).** 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`). The combined **`text_search`** domain carries all three capabilities in one type — `=` / `<>` via HMAC, `<` `<=` `>` `>=` / `min` / `max` via ORE, and `@>` / `<@` via bloom filter. 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` / `text_search` domains — deliberately *not* SQL `LIKE` (no wildcard/anchoring; probabilistic ngram containment) — and never backs equality. **Equality on the ordered text domains (`text_ord`, `text_ord_ore`) and on `text_search` always routes `=` / `<>` through `hm` (exact HMAC), never the ORE term — ORE is not exact-equality for text** (integer ordered domains keep exact ORE equality, which is lossless for them). ([#260](https://github.com/cipherstash/encrypt-query-language/pull/260)) -- **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.` 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)) +- **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.` 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)) - **`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)) ### Changed - **Scalar encrypted-domain types are now defined in a Rust catalog, not TOML manifests; the Python codegen toolchain is removed.** Adding a scalar encrypted-domain type (`int4`, `int8`, …) is now one row in `eql-scalars::CATALOG` (`crates/eql-scalars/src/lib.rs`) instead of authoring `tasks/codegen/types/.toml`. `mise run build` regenerates the gitignored SQL surface via `cargo run -p eql-codegen` (Rust, std-only) rather than the Python generator. The catalog row's `Fixture` list is the single source of truth for that type's plaintext fixtures: the SQLx test matrix reads it directly as a compile-time-materialised const (`eql_scalars::INT4_VALUES` / `INT2_VALUES`, `ScalarType::FIXTURE_VALUES`), so there is no longer a generated, committed `tests/sqlx/src/fixtures/_values.rs` — a Rust source of truth no longer round-trips through generated Rust. The shipped SQL is unchanged — `release/*.sql` is byte-identical across the cutover — so there is no change for callers installing EQL; this only affects contributors who extend the scalar domain families. The `python` mise tool, the `pytest`-based `test:codegen` (now `cargo test -p eql-scalars -p eql-codegen`), the per-type `mise run codegen:domain` tasks, and the per-type `tests/sqlx/snapshots/_matrix_tests.txt` baselines (collapsed into one catalog-reconciled `tests/sqlx/snapshots/matrix_tests.txt`) are gone. Why: a single compiler-validated source of truth shared by the generator and the SQLx test harness, and one fewer toolchain in the build/test path — building and testing EQL no longer needs Python (Python remains only for the separate docs-markdown tooling). ([#252](https://github.com/cipherstash/encrypt-query-language/pull/252)) +### Fixed + +- **The `eql_v3` ORE block comparator now orders ciphertexts of any block count, not just 8.** `eql_v3.compare_ore_block_256_term` derives the block count `N` from the term length (`octet_length = 49·N + 16`) instead of hardcoding 8, so encrypted types whose native ORE width exceeds 8 blocks — `numeric` (14) and `timestamptz` (12) — order, range-query, `ORDER BY`, and `MIN`/`MAX` correctly instead of silently mis-ordering. Malformed terms (length not `49·N + 16` for `N ≥ 1`) now raise instead of returning a bogus comparison. The self-contained `eql_v3` SEM type was renamed `eql_v3.ore_block_u64_8_256 → eql_v3.ore_block_256` to reflect that it is width-agnostic (the `eql_v2` type is unchanged). No effect on existing 8-block types (a no-op for `N = 8`). ([#241](https://github.com/cipherstash/encrypt-query-language/issues/241), [#276](https://github.com/cipherstash/encrypt-query-language/pull/276)) + ## [2.3.1] — 2026-05-21 ### Fixed diff --git a/CLAUDE.md b/CLAUDE.md index 6005f72d..34149104 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -53,7 +53,7 @@ This project uses `mise` for task management. Common commands: This is the **Encrypt Query Language (EQL)** - a PostgreSQL extension for searchable encryption. Key architectural components: ### Core Structure -- **Schema**: Core EQL functions/types are in the `eql_v2` PostgreSQL schema. The encrypted-domain type families (`int4` and future scalar domains) live in a separate `eql_v3` schema (see below). The `eql_v3` surface is **self-contained**: it owns its own copies of the searchable-encrypted-metadata (SEM) index-term types (`eql_v3.hmac_256`, `eql_v3.ore_block_u64_8_256`, hand-written under `src/v3/sem/`) and has no runtime dependency on `eql_v2`. `eql_v2` is unchanged and remains the documented public API. +- **Schema**: Core EQL functions/types are in the `eql_v2` PostgreSQL schema. The encrypted-domain type families (`int4` and future scalar domains) live in a separate `eql_v3` schema (see below). The `eql_v3` surface is **self-contained**: it owns its own copies of the searchable-encrypted-metadata (SEM) index-term types (`eql_v3.hmac_256`, `eql_v3.ore_block_256`, hand-written under `src/v3/sem/`) and has no runtime dependency on `eql_v2`. `eql_v2` is unchanged and remains the documented public API. - **Main Type**: `eql_v2_encrypted` - composite type for encrypted columns (stored as JSONB) - **Configuration**: `eql_v2_configuration` table tracks encryption configs - **Index Types**: Various encrypted index types (blake3, hmac_256, bloom_filter, ore variants) @@ -64,7 +64,7 @@ This is the **Encrypt Query Language (EQL)** - a PostgreSQL extension for search - `src/operators/` - SQL operators for encrypted data comparisons - `src/config/` - Configuration management functions - `src/blake3/`, `src/hmac_256/`, `src/bloom_filter/`, `src/ore_*` - Index implementations -- `src/v3/` - Self-contained `eql_v3` surface: `src/v3/schema.sql`, forked `src/v3/crypto.sql` / `src/v3/common.sql`, hand-written SEM index-term types under `src/v3/sem/` (`hmac_256`, `ore_block_u64_8_256`), and the generated scalar encrypted-domain families under `src/v3/scalars//` (plus the shared blocker `src/v3/scalars/functions.sql`) +- `src/v3/` - Self-contained `eql_v3` surface: `src/v3/schema.sql`, forked `src/v3/crypto.sql` / `src/v3/common.sql`, hand-written SEM index-term types under `src/v3/sem/` (`hmac_256`, `ore_block_256`), and the generated scalar encrypted-domain families under `src/v3/scalars//` (plus the shared blocker `src/v3/scalars/functions.sql`) - `tasks/` - mise task scripts - `tests/sqlx/` - Rust/SQLx test framework (PostgreSQL 14-17 support) - `release/` - Generated SQL installation files @@ -78,7 +78,7 @@ This is the **Encrypt Query Language (EQL)** - a PostgreSQL extension for search ### Encrypted-Domain Types -`src/v3/scalars/` holds the generated **encrypted-domain type families** — jsonb-backed PostgreSQL domains in the **`eql_v3` schema**, one domain per operator/index capability (`eql_v3.` storage-only, `eql_v3._eq`, `eql_v3._ord`). The schema qualifier replaces the old version-prefixed name, so the domains are `eql_v3.int4`, `eql_v3.int4_eq`, `eql_v3.int4_ord`, `eql_v3.int4_ord_ore` — created in `eql_v3`, not `public`. Their extractors/wrappers/aggregates (`eql_v3.eq_term`, `eql_v3.ord_term`, `eql_v3.eq`/`lt`/…, `eql_v3.min`/`max`) also live in `eql_v3`, and the SEM index-term types they return and construct (`eql_v3.hmac_256`, `eql_v3.ore_block_u64_8_256`) are **also `eql_v3`** — hand-written under `src/v3/sem/` so the whole v3 surface is self-contained (no `eql_v2.` appears anywhere in v3 SQL; CI gates this via `mise run test:self_contained_v3` and the standalone `release/cipherstash-encrypt-v3.sql` installer). `eql_v3.int4` (PR #239, supersedes #225) is the reference scalar implementation; future scalar types such as `int8`, `bool`, `date`, `float`, `numeric`, `timestamp`, `text`, and `jsonb` follow this materializer pattern. `text`, `numeric`, and `jsonb` are planned but have no generated SQL surface yet — `jsonb` in particular needs a separate SQL design beyond the ordered-scalar materializer. The `eql-scalars` fixture catalog (`crates/eql-scalars`) already models their fixture values ahead of the SQL surface. +`src/v3/scalars/` holds the generated **encrypted-domain type families** — jsonb-backed PostgreSQL domains in the **`eql_v3` schema**, one domain per operator/index capability (`eql_v3.` storage-only, `eql_v3._eq`, `eql_v3._ord`). The schema qualifier replaces the old version-prefixed name, so the domains are `eql_v3.int4`, `eql_v3.int4_eq`, `eql_v3.int4_ord`, `eql_v3.int4_ord_ore` — created in `eql_v3`, not `public`. Their extractors/wrappers/aggregates (`eql_v3.eq_term`, `eql_v3.ord_term`, `eql_v3.eq`/`lt`/…, `eql_v3.min`/`max`) also live in `eql_v3`, and the SEM index-term types they return and construct (`eql_v3.hmac_256`, `eql_v3.ore_block_256`) are **also `eql_v3`** — hand-written under `src/v3/sem/` so the whole v3 surface is self-contained (no `eql_v2.` appears anywhere in v3 SQL; CI gates this via `mise run test:self_contained_v3` and the standalone `release/cipherstash-encrypt-v3.sql` installer). `eql_v3.int4` (PR #239, supersedes #225) is the reference scalar implementation; future scalar types such as `int8`, `bool`, `date`, `float`, `numeric`, `timestamp`, `text`, and `jsonb` follow this materializer pattern. `text`, `numeric`, and `jsonb` are planned but have no generated SQL surface yet — `jsonb` in particular needs a separate SQL design beyond the ordered-scalar materializer. The `eql-scalars` fixture catalog (`crates/eql-scalars`) already models their fixture values ahead of the SQL surface. Adding a scalar encrypted-domain type is one row in the Rust catalog `eql-scalars::CATALOG` (`crates/eql-scalars/src/lib.rs`): a `ScalarSpec` giving the type `token` (e.g. `int8`), its `ScalarKind` (the `kind` field), the `DomainSpec`s mapping each generated domain suffix 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//` 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_scalars::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//_extensions.sql` with no auto-generated header and explicit `-- REQUIRE:` edges — that file IS committed. `text` and `jsonb` are out of scope for this scalar materializer. diff --git a/Cargo.lock b/Cargo.lock index 92723c93..367a00ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1208,6 +1208,7 @@ dependencies = [ "hex", "jsonschema", "paste", + "rust_decimal", "serde", "serde_json", "sqlx", @@ -3722,6 +3723,7 @@ dependencies = [ "memchr", "once_cell", "percent-encoding", + "rust_decimal", "serde", "serde_json", "sha2", @@ -3803,6 +3805,7 @@ dependencies = [ "percent-encoding", "rand 0.8.6", "rsa", + "rust_decimal", "serde", "sha1", "sha2", @@ -3841,6 +3844,7 @@ dependencies = [ "memchr", "once_cell", "rand 0.8.6", + "rust_decimal", "serde", "serde_json", "sha2", diff --git a/crates/eql-codegen/src/context.rs b/crates/eql-codegen/src/context.rs index 15aeacc3..d5b928f3 100644 --- a/crates/eql-codegen/src/context.rs +++ b/crates/eql-codegen/src/context.rs @@ -133,7 +133,7 @@ pub struct FunctionsContext { /// Build the inlinable index-extractor entry for a domain term. /// /// The `RETURNS` type name equals the constructor name (`hmac_256`, -/// `ore_block_u64_8_256`); qualify it with `SCHEMA` — the same schema as the +/// `ore_block_256`); qualify it with `SCHEMA` — the same schema as the /// body's constructor call — so the declared return type and the call stay in /// lockstep. `Term::returns()` is intentionally not used. pub fn extractor_entry(term: Term) -> FnEntry { diff --git a/crates/eql-codegen/src/generate.rs b/crates/eql-codegen/src/generate.rs index af6c9cbc..49af8967 100644 --- a/crates/eql-codegen/src/generate.rs +++ b/crates/eql-codegen/src/generate.rs @@ -468,7 +468,7 @@ mod tests { let sql = render_functions_file(s.token, domain(s, "_ord")); assert_eq!(sql.matches("CREATE FUNCTION").count(), 45); assert!(sql.contains("CREATE FUNCTION eql_v3.ord_term(a eql_v3.int4_ord)")); - assert!(sql.contains("RETURNS eql_v3.ore_block_u64_8_256")); + assert!(sql.contains("RETURNS eql_v3.ore_block_256")); assert_eq!( sql.matches("LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE") .count(), diff --git a/crates/eql-scalars/src/kind.rs b/crates/eql-scalars/src/kind.rs index 69ab70f5..e91813da 100644 --- a/crates/eql-scalars/src/kind.rs +++ b/crates/eql-scalars/src/kind.rs @@ -97,11 +97,11 @@ impl ScalarKind { } /// A debug/identifier string for the kind: the canonical Rust plaintext type - /// name (`"i32"`, `"chrono::NaiveDate"`). `Numeric`/`Jsonb` have **no - /// generated SQL surface** and no catalog row, so calling this on them is a - /// programming error and panics loudly rather than returning a plausible SQL - /// token a premature caller might feed into codegen. Only call site today is - /// `crates/eql-scalars/src/tests.rs`. + /// name (`"i32"`, `"chrono::NaiveDate"`, `"rust_decimal::Decimal"`). `Jsonb` + /// has **no generated SQL surface** and no catalog row, so calling this on it + /// is a programming error and panics loudly rather than returning a plausible + /// SQL token a premature caller might feed into codegen. Only call site today + /// is `crates/eql-scalars/src/tests.rs`. pub const fn rust_type(self) -> &'static str { match self { ScalarKind::I16 => "i16", @@ -110,8 +110,9 @@ impl ScalarKind { ScalarKind::Text => "text", ScalarKind::Date => "chrono::NaiveDate", ScalarKind::Timestamptz => "chrono::DateTime", - ScalarKind::Numeric | ScalarKind::Jsonb => { - panic!("ScalarKind::rust_type: numeric/jsonb have no generated surface yet") + ScalarKind::Numeric => "rust_decimal::Decimal", + ScalarKind::Jsonb => { + panic!("ScalarKind::rust_type: jsonb has no generated surface yet") } } } diff --git a/crates/eql-scalars/src/lib.rs b/crates/eql-scalars/src/lib.rs index b9f87c70..7607609c 100644 --- a/crates/eql-scalars/src/lib.rs +++ b/crates/eql-scalars/src/lib.rs @@ -230,13 +230,15 @@ const ORDERED_INT_DOMAINS: &[DomainSpec] = &[ }, ]; -/// Equality-only domains: storage (no terms) + `_eq` (hm). Used by scalar types -/// that can hash for equality but cannot (yet) be ordered. `timestamptz` is the -/// first such type: 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 an -/// ordered domain would silently mis-order. Ordering is deferred until a -/// wide-ORE (12-block) term exists. +/// Equality-only domains: storage (no terms) + `_eq` (hm). The canonical shape +/// for a scalar type that can hash for equality but is not ORE-orderable. +/// **Currently unused:** `timestamptz` (the previous sole user) was promoted to +/// the ordered shape once `eql_v3.compare_ore_block_256_term` generalized to N +/// blocks and could order its native 12-block ORE width. Retained — and still +/// validated as a known-valid shape by `every_type_uses_a_known_domain_shape` — +/// so a future non-orderable scalar (e.g. a hash-only type) can reuse it without +/// reconstructing the shape. +#[allow(dead_code)] const EQ_ONLY_DOMAINS: &[DomainSpec] = &[ DomainSpec { suffix: "", @@ -296,6 +298,17 @@ const TIMESTAMPTZ_FIXTURES: &[Fixture] = fixtures!(timestamptz; "2012-06-30T11:59:59Z", "2016-03-15T08:15:30Z", "2020-10-21T14:45:00Z", "2024-02-29T17:30:45Z", "2038-01-19T03:14:07Z", "2099-12-31T23:59:59Z"); +/// `numeric` fixture plaintexts — distinct by `Decimal` value, spanning sign, +/// magnitude, and scale, and including `0` plus the min/max pivots +/// (`-1000000000000` / `1000000000000`). They mirror `ore-rs`'s own +/// order-pinning vectors so the 14-block ORE edges (sign + high/low blocks) are +/// exercised. Each literal is distinct by parsed value (no `"1"`/`"1.0"` +/// aliasing) — the harness `numeric_fixtures_distinct_by_value` guard enforces +/// this, since the zero-dep catalog only dedupes by literal string. +const NUMERIC_FIXTURES: &[Fixture] = fixtures!(numeric; + "-1000000000000", "-1000000", "-1.001", "-1", "-0.5", "-0.001", + "0", "0.001", "0.5", "0.999999999", "1", "1.001", "1000000", "1000000000000"); + const INT4: ScalarSpec = ScalarSpec { token: "int4", kind: ScalarKind::I32, @@ -332,27 +345,42 @@ pub const DATE: ScalarSpec = ScalarSpec { fixtures: DATE_FIXTURES, }; -/// `timestamptz` — an **equality-only** (UTC-normalized) non-integer scalar. -/// Uses `EQ_ONLY_DOMAINS` (storage + `_eq`) rather than the four-domain ordered -/// shape: 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 an -/// ordered timestamptz domain would silently mis-order. Ordering is deferred to -/// a future PR that adds a wide-ORE (12-block) term. The three "pivot" fixture -/// values are retained as equality pivots; the kind stays ordered-shaped -/// (carries a rust type, no i128 range) so the harness can parse them. +/// `timestamptz` — an **ordered**, UTC-normalized non-integer scalar. Uses the +/// four-domain ordered shape (storage, `_eq`, `_ord`, `_ord_ore`): cipherstash +/// encrypts `Plaintext::Timestamp` at native 12-block ORE width, which the +/// generalized `eql_v3.compare_ore_block_256_term` comparator orders correctly. +/// Values are UTC-normalized (cipherstash has no tz-preserving type) and encrypt +/// under the `timestamp` cast. /// /// Public (like `DATE`) because the SQLx harness reads `TIMESTAMPTZ.fixtures` -/// directly to parse the RFC3339 strings into `chrono::DateTime` at -/// runtime — there is no `TIMESTAMPTZ_VALUES` const (chrono is not -/// `const`-friendly and `eql-scalars` stays zero-dep). +/// directly to parse the RFC3339 strings into `chrono::DateTime` at runtime +/// (no `TIMESTAMPTZ_VALUES` const; `eql-scalars` stays zero-dep). pub const TIMESTAMPTZ: ScalarSpec = ScalarSpec { token: "timestamptz", kind: ScalarKind::Timestamptz, - domains: EQ_ONLY_DOMAINS, + domains: ORDERED_INT_DOMAINS, fixtures: TIMESTAMPTZ_FIXTURES, }; +/// `numeric` — an **ordered** non-integer scalar backed by +/// `rust_decimal::Decimal`. Uses the four-domain ordered shape: cipherstash +/// encrypts `Plaintext::Decimal` at native 14-block ORE width, which the +/// generalized `eql_v3.compare_ore_block_256_term` comparator orders correctly. +/// `numeric_value` returns `None` (no i128 range); ordering is supplied by the +/// harness `Decimal: Ord`, which `ore-rs` guarantees agrees with the ciphertext +/// order (equivalent scales collide, like `Decimal`'s own `Ord`). +/// +/// Public (like `DATE` / `TIMESTAMPTZ`) so the SQLx harness reads +/// `NUMERIC.fixtures` directly to parse the decimal strings into +/// `rust_decimal::Decimal` at runtime (the catalog stays zero-dep: no +/// `rust_decimal`). +pub const NUMERIC: ScalarSpec = ScalarSpec { + token: "numeric", + kind: ScalarKind::Numeric, + domains: ORDERED_INT_DOMAINS, + fixtures: NUMERIC_FIXTURES, +}; + /// Domains for `text`: the ordered shape (with exact `hm` equality on the /// ordered domains), a `_match` domain (`Bloom` containment), and a combined /// `_search` domain carrying equality + ordering + match in one type. @@ -417,7 +445,7 @@ pub const TEXT: ScalarSpec = ScalarSpec { /// The scalar catalog — the single source of truth. Order is significant (it /// drives generation order). New types are appended as their SQL surface lands. -pub const CATALOG: &[ScalarSpec] = &[INT4, INT2, INT8, DATE, TIMESTAMPTZ, TEXT]; +pub const CATALOG: &[ScalarSpec] = &[INT4, INT2, INT8, DATE, TIMESTAMPTZ, NUMERIC, TEXT]; /// Materialise an integer scalar's fixtures into a typed `&'static` slice at /// compile time. This is the **single-sourced** plaintext list the SQLx test diff --git a/crates/eql-scalars/src/term.rs b/crates/eql-scalars/src/term.rs index 461a1714..5db834b8 100644 --- a/crates/eql-scalars/src/term.rs +++ b/crates/eql-scalars/src/term.rs @@ -28,7 +28,7 @@ impl Term { pub const fn ctor(self) -> &'static str { match self { Term::Hm => "hmac_256", - Term::Ore => "ore_block_u64_8_256", + Term::Ore => "ore_block_256", Term::Bloom => "bloom_filter", } } @@ -57,8 +57,8 @@ impl Term { match self { Term::Hm => &["src/v3/sem/hmac_256/functions.sql"], Term::Ore => &[ - "src/v3/sem/ore_block_u64_8_256/functions.sql", - "src/v3/sem/ore_block_u64_8_256/operators.sql", + "src/v3/sem/ore_block_256/functions.sql", + "src/v3/sem/ore_block_256/operators.sql", ], Term::Bloom => &["src/v3/sem/bloom_filter/functions.sql"], } diff --git a/crates/eql-scalars/src/tests.rs b/crates/eql-scalars/src/tests.rs index 017de217..be5e9880 100644 --- a/crates/eql-scalars/src/tests.rs +++ b/crates/eql-scalars/src/tests.rs @@ -125,14 +125,24 @@ mod rust_tests { #[test] fn timestamptz_maps_to_datetime() { - // Temporal, non-integer, equality-only kind: it carries a rust type but - // no i128 range, so it is not `is_int()` and `as_bounded_int()` returns + // Temporal, non-integer, ordered kind: it carries a rust type but no + // i128 range, so it is not `is_int()` and `as_bounded_int()` returns // `None` — the bounded accessors are not reachable for it. assert_eq!(ScalarKind::Timestamptz.rust_type(), "chrono::DateTime"); assert!(!ScalarKind::Timestamptz.is_int()); assert_eq!(ScalarKind::Timestamptz.as_bounded_int(), None); } + #[test] + fn numeric_maps_to_decimal() { + // Ordered, non-integer, non-chrono kind (14-block ORE): carries a rust + // type but no i128 range, so it is not `is_int()` and `as_bounded_int()` + // returns `None`. Pins the now-real `rust_type` arm (it no longer panics). + assert_eq!(ScalarKind::Numeric.rust_type(), "rust_decimal::Decimal"); + assert!(!ScalarKind::Numeric.is_int()); + assert_eq!(ScalarKind::Numeric.as_bounded_int(), None); + } + /// The structural guarantee that replaces the old runtime panics: a /// `Min`/`Max`/`Zero` pivot sentinel may only appear in a `CATALOG` row whose /// kind is an integer kind. `numeric_value` would resolve to `None` for a @@ -170,7 +180,24 @@ mod rust_tests { let date = CATALOG.iter().find(|s| s.token == "date").unwrap(); assert!(!date.is_eq_only(), "date is ordered"); let ts = CATALOG.iter().find(|s| s.token == "timestamptz").unwrap(); - assert!(ts.is_eq_only(), "timestamptz is equality-only"); + assert!( + !ts.is_eq_only(), + "timestamptz is now ordered (native 12-block ORE, comparator generalized to N blocks)" + ); + + // No catalog type is currently eq-only, so exercise `is_eq_only()`'s + // positive path with a synthetic spec built on the retained + // `EQ_ONLY_DOMAINS` shape (storage + `_eq`, no `_ord`). + let eq_only = ScalarSpec { + token: "synthetic_eq_only", + kind: ScalarKind::Timestamptz, + domains: EQ_ONLY_DOMAINS, + fixtures: &[], + }; + assert!( + eq_only.is_eq_only(), + "a storage+_eq spec (no _ord) must be detected as eq-only" + ); } } @@ -193,14 +220,14 @@ mod term_tests { let ore = Term::Ore; assert_eq!(ore.json_key(), "ob"); assert_eq!(ore.extractor(), "ord_term"); - assert_eq!(ore.ctor(), "ore_block_u64_8_256"); + assert_eq!(ore.ctor(), "ore_block_256"); assert_eq!(ore.role(), Role::Ord); assert_eq!(ore.operators(), &["=", "<>", "<", "<=", ">", ">="]); assert_eq!( ore.requires(), &[ - "src/v3/sem/ore_block_u64_8_256/functions.sql", - "src/v3/sem/ore_block_u64_8_256/operators.sql", + "src/v3/sem/ore_block_256/functions.sql", + "src/v3/sem/ore_block_256/operators.sql", ] ); } @@ -277,8 +304,8 @@ mod term_helper_tests { assert_eq!( Term::term_requires(&[Term::Ore, Term::Ore, Term::Hm]), vec![ - "src/v3/sem/ore_block_u64_8_256/functions.sql", - "src/v3/sem/ore_block_u64_8_256/operators.sql", + "src/v3/sem/ore_block_256/functions.sql", + "src/v3/sem/ore_block_256/operators.sql", "src/v3/sem/hmac_256/functions.sql", ] ); @@ -496,11 +523,19 @@ mod catalog_tests { } #[test] - fn catalog_has_int4_int2_int8_date_timestamptz_text_in_order() { + fn catalog_has_int4_int2_int8_date_timestamptz_numeric_text_in_order() { let tokens: Vec<&str> = CATALOG.iter().map(|s| s.token).collect(); assert_eq!( tokens, - vec!["int4", "int2", "int8", "date", "timestamptz", "text"] + vec![ + "int4", + "int2", + "int8", + "date", + "timestamptz", + "numeric", + "text" + ] ); } @@ -710,17 +745,14 @@ mod catalog_tests { #[test] fn ordered_and_eq_only_shapes_are_used_as_declared() { - // Pin which catalog tokens carry which shape, so a row silently flipping - // ORDERED_INT_DOMAINS <-> EQ_ONLY_DOMAINS is caught. timestamptz is - // equality-only (12-block ORE vs 8-block comparator); the rest ordered - // (text adds `_match` and `_search` domains on top, so it is not - // eq_only either). + // All current catalog types use the four-domain ordered shape; none is + // equality-only. (timestamptz was promoted to ordered once the ORE + // comparator generalized to N blocks — see the numeric/ORE work.) for s in CATALOG { let is_eq_only = s.domains.len() == 2; - let expect_eq_only = s.token == "timestamptz"; - assert_eq!( - is_eq_only, expect_eq_only, - "{} domain shape (eq_only={is_eq_only}) does not match expectation", + assert!( + !is_eq_only, + "{} is unexpectedly eq-only; no catalog type is eq-only currently", s.token ); } diff --git a/crates/eql-tests-macros/src/lib.rs b/crates/eql-tests-macros/src/lib.rs index aba3ebaa..8ad49520 100644 --- a/crates/eql-tests-macros/src/lib.rs +++ b/crates/eql-tests-macros/src/lib.rs @@ -93,6 +93,14 @@ fn is_text_token(token: &str) -> bool { spec_for_token(token).kind.is_text() } +/// True when `token`'s catalog row is the `numeric` kind (owned +/// `rust_decimal::Decimal`). Like `text` it is ordered but non-integer and +/// non-chrono, so it stamps the `numeric` fixture discriminator and draws its +/// values from the harness accessor (`numeric_values()`). +fn is_numeric_token(token: &str) -> bool { + matches!(spec_for_token(token).kind, eql_scalars::ScalarKind::Numeric) +} + /// True when `token`'s catalog row declares no ordered domain — equality-only. /// Replaces the `[eq_only]` marker. Consumed by [`matrix_suite_for_entry`] to /// keep an eq-only type out of the ordered matrix (which exercises ordering @@ -224,10 +232,12 @@ fn scalar_fixture_modules_tokens(list: &ScalarList) -> TokenStream2 { format_ident!("temporal") } else if is_text_token(&token_str) { format_ident!("text") + } else if is_numeric_token(&token_str) { + format_ident!("numeric") } else { panic!( - "scalar token `{token_str}` is neither integer, temporal, nor \ - text — no fixture discriminator is wired for its kind" + "scalar token `{token_str}` is neither integer, temporal, text, \ + nor numeric — no fixture discriminator is wired for its kind" ) }; quote! { diff --git a/crates/eql-types/README.md b/crates/eql-types/README.md index aff5f5f9..04281901 100644 --- a/crates/eql-types/README.md +++ b/crates/eql-types/README.md @@ -21,7 +21,7 @@ hand-copying. The [`src/v3/`](src/v3/) module has one type per **SQL domain** in the `eql_v3` schema — `Int4` / `Int4Eq` / `Int4Ord` / `Int4OrdOre`, and likewise -for `int2`, `int8`, `date`, `timestamptz` (eq-only), and `text` (which adds +for `int2`, `int8`, `date`, `timestamptz`, `numeric`, and `text` (which adds `TextMatch`) — each carrying its index terms as **required** fields. The capability is the type identity; `Option` never appears. A payload missing its term key fails to deserialize: the Rust analogue of the SQL domain's @@ -34,7 +34,7 @@ Shared wire fields are reusable newtypes in |---------|----------|-------|-------| | `Ciphertext` | `c` | `String` | every domain (envelope) | | `Hmac256` | `hm` | `String` | `_eq` domains | -| `OreBlockU64_8_256` | `ob` | `Vec` | `_ord` / `_ord_ore` domains | +| `OreBlock256` | `ob` | `Vec` | `_ord` / `_ord_ore` domains | | `BloomFilter` | `bf` | `Vec` (signed!) | `_match` domains | Note "v3" names the SQL schema generation (`eql_v3.*`); the JSON envelope diff --git a/crates/eql-types/bindings/v3/DateOrd.ts b/crates/eql-types/bindings/v3/DateOrd.ts index c9ff2efd..c81eb642 100644 --- a/crates/eql-types/bindings/v3/DateOrd.ts +++ b/crates/eql-types/bindings/v3/DateOrd.ts @@ -1,7 +1,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { Ciphertext } from "./Ciphertext"; import type { Identifier } from "./Identifier"; -import type { OreBlockU64_8_256 } from "./OreBlockU64_8_256"; +import type { OreBlock256 } from "./OreBlock256"; import type { SchemaVersion } from "./SchemaVersion"; /** @@ -24,4 +24,4 @@ c: Ciphertext, /** * Block-ORE order term. Serves equality too. */ -ob: OreBlockU64_8_256, }; +ob: OreBlock256, }; diff --git a/crates/eql-types/bindings/v3/DateOrdOre.ts b/crates/eql-types/bindings/v3/DateOrdOre.ts index ccf48568..4baf81f6 100644 --- a/crates/eql-types/bindings/v3/DateOrdOre.ts +++ b/crates/eql-types/bindings/v3/DateOrdOre.ts @@ -1,7 +1,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { Ciphertext } from "./Ciphertext"; import type { Identifier } from "./Identifier"; -import type { OreBlockU64_8_256 } from "./OreBlockU64_8_256"; +import type { OreBlock256 } from "./OreBlock256"; import type { SchemaVersion } from "./SchemaVersion"; /** @@ -24,4 +24,4 @@ c: Ciphertext, /** * Block-ORE order term. Serves equality too. */ -ob: OreBlockU64_8_256, }; +ob: OreBlock256, }; diff --git a/crates/eql-types/bindings/v3/Int2Ord.ts b/crates/eql-types/bindings/v3/Int2Ord.ts index 1cf345dd..38e23008 100644 --- a/crates/eql-types/bindings/v3/Int2Ord.ts +++ b/crates/eql-types/bindings/v3/Int2Ord.ts @@ -1,7 +1,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { Ciphertext } from "./Ciphertext"; import type { Identifier } from "./Identifier"; -import type { OreBlockU64_8_256 } from "./OreBlockU64_8_256"; +import type { OreBlock256 } from "./OreBlock256"; import type { SchemaVersion } from "./SchemaVersion"; /** @@ -24,4 +24,4 @@ c: Ciphertext, /** * Block-ORE order term. Serves equality too. */ -ob: OreBlockU64_8_256, }; +ob: OreBlock256, }; diff --git a/crates/eql-types/bindings/v3/Int2OrdOre.ts b/crates/eql-types/bindings/v3/Int2OrdOre.ts index 3e3f5464..1193826a 100644 --- a/crates/eql-types/bindings/v3/Int2OrdOre.ts +++ b/crates/eql-types/bindings/v3/Int2OrdOre.ts @@ -1,7 +1,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { Ciphertext } from "./Ciphertext"; import type { Identifier } from "./Identifier"; -import type { OreBlockU64_8_256 } from "./OreBlockU64_8_256"; +import type { OreBlock256 } from "./OreBlock256"; import type { SchemaVersion } from "./SchemaVersion"; /** @@ -24,4 +24,4 @@ c: Ciphertext, /** * Block-ORE order term. Serves equality too. */ -ob: OreBlockU64_8_256, }; +ob: OreBlock256, }; diff --git a/crates/eql-types/bindings/v3/Int4Ord.ts b/crates/eql-types/bindings/v3/Int4Ord.ts index 6463698f..ee25c670 100644 --- a/crates/eql-types/bindings/v3/Int4Ord.ts +++ b/crates/eql-types/bindings/v3/Int4Ord.ts @@ -1,7 +1,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { Ciphertext } from "./Ciphertext"; import type { Identifier } from "./Identifier"; -import type { OreBlockU64_8_256 } from "./OreBlockU64_8_256"; +import type { OreBlock256 } from "./OreBlock256"; import type { SchemaVersion } from "./SchemaVersion"; /** @@ -24,4 +24,4 @@ c: Ciphertext, /** * Block-ORE order term. Serves equality too. */ -ob: OreBlockU64_8_256, }; +ob: OreBlock256, }; diff --git a/crates/eql-types/bindings/v3/Int4OrdOre.ts b/crates/eql-types/bindings/v3/Int4OrdOre.ts index 54e05a76..17be0f8e 100644 --- a/crates/eql-types/bindings/v3/Int4OrdOre.ts +++ b/crates/eql-types/bindings/v3/Int4OrdOre.ts @@ -1,7 +1,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { Ciphertext } from "./Ciphertext"; import type { Identifier } from "./Identifier"; -import type { OreBlockU64_8_256 } from "./OreBlockU64_8_256"; +import type { OreBlock256 } from "./OreBlock256"; import type { SchemaVersion } from "./SchemaVersion"; /** @@ -26,4 +26,4 @@ c: Ciphertext, * Block-ORE order term. Serves equality too — ORE over a * full-domain `int4` is lossless, so no separate `hm` is carried. */ -ob: OreBlockU64_8_256, }; +ob: OreBlock256, }; diff --git a/crates/eql-types/bindings/v3/Int8Ord.ts b/crates/eql-types/bindings/v3/Int8Ord.ts index 97abe2e0..7199defd 100644 --- a/crates/eql-types/bindings/v3/Int8Ord.ts +++ b/crates/eql-types/bindings/v3/Int8Ord.ts @@ -1,7 +1,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { Ciphertext } from "./Ciphertext"; import type { Identifier } from "./Identifier"; -import type { OreBlockU64_8_256 } from "./OreBlockU64_8_256"; +import type { OreBlock256 } from "./OreBlock256"; import type { SchemaVersion } from "./SchemaVersion"; /** @@ -24,4 +24,4 @@ c: Ciphertext, /** * Block-ORE order term. Serves equality too. */ -ob: OreBlockU64_8_256, }; +ob: OreBlock256, }; diff --git a/crates/eql-types/bindings/v3/Int8OrdOre.ts b/crates/eql-types/bindings/v3/Int8OrdOre.ts index 38f5c435..6dd492db 100644 --- a/crates/eql-types/bindings/v3/Int8OrdOre.ts +++ b/crates/eql-types/bindings/v3/Int8OrdOre.ts @@ -1,7 +1,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { Ciphertext } from "./Ciphertext"; import type { Identifier } from "./Identifier"; -import type { OreBlockU64_8_256 } from "./OreBlockU64_8_256"; +import type { OreBlock256 } from "./OreBlock256"; import type { SchemaVersion } from "./SchemaVersion"; /** @@ -24,4 +24,4 @@ c: Ciphertext, /** * Block-ORE order term. Serves equality too. */ -ob: OreBlockU64_8_256, }; +ob: OreBlock256, }; diff --git a/crates/eql-types/bindings/v3/Numeric.ts b/crates/eql-types/bindings/v3/Numeric.ts new file mode 100644 index 00000000..dfcda818 --- /dev/null +++ b/crates/eql-types/bindings/v3/Numeric.ts @@ -0,0 +1,22 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Ciphertext } from "./Ciphertext"; +import type { Identifier } from "./Identifier"; +import type { SchemaVersion } from "./SchemaVersion"; + +/** + * `eql_v3.numeric` — storage only; every operator is blocked. + */ +export type Numeric = { +/** + * 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, }; diff --git a/crates/eql-types/bindings/v3/NumericEq.ts b/crates/eql-types/bindings/v3/NumericEq.ts new file mode 100644 index 00000000..e3b3ff46 --- /dev/null +++ b/crates/eql-types/bindings/v3/NumericEq.ts @@ -0,0 +1,27 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Ciphertext } from "./Ciphertext"; +import type { Hmac256 } from "./Hmac256"; +import type { Identifier } from "./Identifier"; +import type { SchemaVersion } from "./SchemaVersion"; + +/** + * `eql_v3.numeric_eq` — HMAC equality (`=`, `<>`). + */ +export type NumericEq = { +/** + * 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, }; diff --git a/crates/eql-types/bindings/v3/NumericOrd.ts b/crates/eql-types/bindings/v3/NumericOrd.ts new file mode 100644 index 00000000..491295dc --- /dev/null +++ b/crates/eql-types/bindings/v3/NumericOrd.ts @@ -0,0 +1,27 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Ciphertext } from "./Ciphertext"; +import type { Identifier } from "./Identifier"; +import type { OreBlock256 } from "./OreBlock256"; +import type { SchemaVersion } from "./SchemaVersion"; + +/** + * `eql_v3.numeric_ord` — full comparison (`=` `<>` `<` `<=` `>` `>=`). + */ +export type NumericOrd = { +/** + * 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 (14 blocks for numeric). Serves equality too. + */ +ob: OreBlock256, }; diff --git a/crates/eql-types/bindings/v3/NumericOrdOre.ts b/crates/eql-types/bindings/v3/NumericOrdOre.ts new file mode 100644 index 00000000..84643745 --- /dev/null +++ b/crates/eql-types/bindings/v3/NumericOrdOre.ts @@ -0,0 +1,27 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Ciphertext } from "./Ciphertext"; +import type { Identifier } from "./Identifier"; +import type { OreBlock256 } from "./OreBlock256"; +import type { SchemaVersion } from "./SchemaVersion"; + +/** + * `eql_v3.numeric_ord_ore` — full comparison, scheme-explicit name. + */ +export type NumericOrdOre = { +/** + * 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 (14 blocks for numeric). Serves equality too. + */ +ob: OreBlock256, }; diff --git a/crates/eql-types/bindings/v3/OreBlock256.ts b/crates/eql-types/bindings/v3/OreBlock256.ts new file mode 100644 index 00000000..0e200b48 --- /dev/null +++ b/crates/eql-types/bindings/v3/OreBlock256.ts @@ -0,0 +1,11 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Block-ORE order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` + * domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's + * domain, so it serves equality too. The block count is width-agnostic on the + * wire (8 for the int scalars, 12 for timestamptz, 14 for numeric) — the + * array just carries more block strings. SQL-side constructor: + * `eql_v3.ore_block_256`. + */ +export type OreBlock256 = Array; diff --git a/crates/eql-types/bindings/v3/OreBlockU64_8_256.ts b/crates/eql-types/bindings/v3/OreBlockU64_8_256.ts deleted file mode 100644 index 5701b17f..00000000 --- a/crates/eql-types/bindings/v3/OreBlockU64_8_256.ts +++ /dev/null @@ -1,9 +0,0 @@ -// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. - -/** - * Block-ORE (u64, 8 blocks, 256) order term — the `ob` wire key. Backs the - * `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless - * over the scalar's domain, so it serves equality too. SQL-side constructor: - * `eql_v3.ore_block_u64_8_256`. - */ -export type OreBlockU64_8_256 = Array; diff --git a/crates/eql-types/bindings/v3/TextOrd.ts b/crates/eql-types/bindings/v3/TextOrd.ts index a5fefcd0..e3e1de7d 100644 --- a/crates/eql-types/bindings/v3/TextOrd.ts +++ b/crates/eql-types/bindings/v3/TextOrd.ts @@ -2,7 +2,7 @@ import type { Ciphertext } from "./Ciphertext"; import type { Hmac256 } from "./Hmac256"; import type { Identifier } from "./Identifier"; -import type { OreBlockU64_8_256 } from "./OreBlockU64_8_256"; +import type { OreBlock256 } from "./OreBlock256"; import type { SchemaVersion } from "./SchemaVersion"; /** @@ -31,4 +31,4 @@ hm: Hmac256, /** * Block-ORE order term. */ -ob: OreBlockU64_8_256, }; +ob: OreBlock256, }; diff --git a/crates/eql-types/bindings/v3/TextOrdOre.ts b/crates/eql-types/bindings/v3/TextOrdOre.ts index 1f427cde..7aed3dd5 100644 --- a/crates/eql-types/bindings/v3/TextOrdOre.ts +++ b/crates/eql-types/bindings/v3/TextOrdOre.ts @@ -2,7 +2,7 @@ import type { Ciphertext } from "./Ciphertext"; import type { Hmac256 } from "./Hmac256"; import type { Identifier } from "./Identifier"; -import type { OreBlockU64_8_256 } from "./OreBlockU64_8_256"; +import type { OreBlock256 } from "./OreBlock256"; import type { SchemaVersion } from "./SchemaVersion"; /** @@ -32,4 +32,4 @@ hm: Hmac256, /** * Block-ORE order term. */ -ob: OreBlockU64_8_256, }; +ob: OreBlock256, }; diff --git a/crates/eql-types/bindings/v3/TextSearch.ts b/crates/eql-types/bindings/v3/TextSearch.ts index 51b296ea..e9570937 100644 --- a/crates/eql-types/bindings/v3/TextSearch.ts +++ b/crates/eql-types/bindings/v3/TextSearch.ts @@ -3,7 +3,7 @@ import type { BloomFilter } from "./BloomFilter"; import type { Ciphertext } from "./Ciphertext"; import type { Hmac256 } from "./Hmac256"; import type { Identifier } from "./Identifier"; -import type { OreBlockU64_8_256 } from "./OreBlockU64_8_256"; +import type { OreBlock256 } from "./OreBlock256"; import type { SchemaVersion } from "./SchemaVersion"; /** @@ -32,7 +32,7 @@ hm: Hmac256, /** * Block-ORE order term. */ -ob: OreBlockU64_8_256, +ob: OreBlock256, /** * Bloom-filter match term (signed smallint bit positions). */ diff --git a/crates/eql-types/bindings/v3/TimestamptzOrd.ts b/crates/eql-types/bindings/v3/TimestamptzOrd.ts new file mode 100644 index 00000000..19b97573 --- /dev/null +++ b/crates/eql-types/bindings/v3/TimestamptzOrd.ts @@ -0,0 +1,27 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Ciphertext } from "./Ciphertext"; +import type { Identifier } from "./Identifier"; +import type { OreBlock256 } from "./OreBlock256"; +import type { SchemaVersion } from "./SchemaVersion"; + +/** + * `eql_v3.timestamptz_ord` — full comparison (`=` `<>` `<` `<=` `>` `>=`). + */ +export type TimestamptzOrd = { +/** + * 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 (12 blocks for timestamptz). Serves equality too. + */ +ob: OreBlock256, }; diff --git a/crates/eql-types/bindings/v3/TimestamptzOrdOre.ts b/crates/eql-types/bindings/v3/TimestamptzOrdOre.ts new file mode 100644 index 00000000..a84d6808 --- /dev/null +++ b/crates/eql-types/bindings/v3/TimestamptzOrdOre.ts @@ -0,0 +1,27 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Ciphertext } from "./Ciphertext"; +import type { Identifier } from "./Identifier"; +import type { OreBlock256 } from "./OreBlock256"; +import type { SchemaVersion } from "./SchemaVersion"; + +/** + * `eql_v3.timestamptz_ord_ore` — full comparison, scheme-explicit name. + */ +export type TimestamptzOrdOre = { +/** + * 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 (12 blocks for timestamptz). Serves equality too. + */ +ob: OreBlock256, }; diff --git a/crates/eql-types/schema/v3/date_ord.json b/crates/eql-types/schema/v3/date_ord.json index 03315ea1..90bbfbfc 100644 --- a/crates/eql-types/schema/v3/date_ord.json +++ b/crates/eql-types/schema/v3/date_ord.json @@ -26,8 +26,8 @@ ], "type": "object" }, - "OreBlockU64_8_256": { - "description": "Block-ORE (u64, 8 blocks, 256) order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. SQL-side constructor: `eql_v3.ore_block_u64_8_256`.", + "OreBlock256": { + "description": "Block-ORE order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. The block count is width-agnostic on the wire (8 for the int scalars, 12 for timestamptz, 14 for numeric) — the array just carries more block strings. SQL-side constructor: `eql_v3.ore_block_256`.", "items": { "type": "string" }, @@ -60,7 +60,7 @@ "ob": { "allOf": [ { - "$ref": "#/definitions/OreBlockU64_8_256" + "$ref": "#/definitions/OreBlock256" } ], "description": "Block-ORE order term. Serves equality too." diff --git a/crates/eql-types/schema/v3/date_ord_ore.json b/crates/eql-types/schema/v3/date_ord_ore.json index dfff7403..9c4da4bd 100644 --- a/crates/eql-types/schema/v3/date_ord_ore.json +++ b/crates/eql-types/schema/v3/date_ord_ore.json @@ -26,8 +26,8 @@ ], "type": "object" }, - "OreBlockU64_8_256": { - "description": "Block-ORE (u64, 8 blocks, 256) order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. SQL-side constructor: `eql_v3.ore_block_u64_8_256`.", + "OreBlock256": { + "description": "Block-ORE order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. The block count is width-agnostic on the wire (8 for the int scalars, 12 for timestamptz, 14 for numeric) — the array just carries more block strings. SQL-side constructor: `eql_v3.ore_block_256`.", "items": { "type": "string" }, @@ -60,7 +60,7 @@ "ob": { "allOf": [ { - "$ref": "#/definitions/OreBlockU64_8_256" + "$ref": "#/definitions/OreBlock256" } ], "description": "Block-ORE order term. Serves equality too." diff --git a/crates/eql-types/schema/v3/int2_ord.json b/crates/eql-types/schema/v3/int2_ord.json index 5073b3a4..bb851fc0 100644 --- a/crates/eql-types/schema/v3/int2_ord.json +++ b/crates/eql-types/schema/v3/int2_ord.json @@ -26,8 +26,8 @@ ], "type": "object" }, - "OreBlockU64_8_256": { - "description": "Block-ORE (u64, 8 blocks, 256) order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. SQL-side constructor: `eql_v3.ore_block_u64_8_256`.", + "OreBlock256": { + "description": "Block-ORE order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. The block count is width-agnostic on the wire (8 for the int scalars, 12 for timestamptz, 14 for numeric) — the array just carries more block strings. SQL-side constructor: `eql_v3.ore_block_256`.", "items": { "type": "string" }, @@ -60,7 +60,7 @@ "ob": { "allOf": [ { - "$ref": "#/definitions/OreBlockU64_8_256" + "$ref": "#/definitions/OreBlock256" } ], "description": "Block-ORE order term. Serves equality too." diff --git a/crates/eql-types/schema/v3/int2_ord_ore.json b/crates/eql-types/schema/v3/int2_ord_ore.json index 83b37587..14782a10 100644 --- a/crates/eql-types/schema/v3/int2_ord_ore.json +++ b/crates/eql-types/schema/v3/int2_ord_ore.json @@ -26,8 +26,8 @@ ], "type": "object" }, - "OreBlockU64_8_256": { - "description": "Block-ORE (u64, 8 blocks, 256) order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. SQL-side constructor: `eql_v3.ore_block_u64_8_256`.", + "OreBlock256": { + "description": "Block-ORE order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. The block count is width-agnostic on the wire (8 for the int scalars, 12 for timestamptz, 14 for numeric) — the array just carries more block strings. SQL-side constructor: `eql_v3.ore_block_256`.", "items": { "type": "string" }, @@ -60,7 +60,7 @@ "ob": { "allOf": [ { - "$ref": "#/definitions/OreBlockU64_8_256" + "$ref": "#/definitions/OreBlock256" } ], "description": "Block-ORE order term. Serves equality too." diff --git a/crates/eql-types/schema/v3/int4_ord.json b/crates/eql-types/schema/v3/int4_ord.json index cbacaa32..5eb0b7ec 100644 --- a/crates/eql-types/schema/v3/int4_ord.json +++ b/crates/eql-types/schema/v3/int4_ord.json @@ -26,8 +26,8 @@ ], "type": "object" }, - "OreBlockU64_8_256": { - "description": "Block-ORE (u64, 8 blocks, 256) order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. SQL-side constructor: `eql_v3.ore_block_u64_8_256`.", + "OreBlock256": { + "description": "Block-ORE order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. The block count is width-agnostic on the wire (8 for the int scalars, 12 for timestamptz, 14 for numeric) — the array just carries more block strings. SQL-side constructor: `eql_v3.ore_block_256`.", "items": { "type": "string" }, @@ -60,7 +60,7 @@ "ob": { "allOf": [ { - "$ref": "#/definitions/OreBlockU64_8_256" + "$ref": "#/definitions/OreBlock256" } ], "description": "Block-ORE order term. Serves equality too." diff --git a/crates/eql-types/schema/v3/int4_ord_ore.json b/crates/eql-types/schema/v3/int4_ord_ore.json index b8cdb95f..326d2688 100644 --- a/crates/eql-types/schema/v3/int4_ord_ore.json +++ b/crates/eql-types/schema/v3/int4_ord_ore.json @@ -26,8 +26,8 @@ ], "type": "object" }, - "OreBlockU64_8_256": { - "description": "Block-ORE (u64, 8 blocks, 256) order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. SQL-side constructor: `eql_v3.ore_block_u64_8_256`.", + "OreBlock256": { + "description": "Block-ORE order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. The block count is width-agnostic on the wire (8 for the int scalars, 12 for timestamptz, 14 for numeric) — the array just carries more block strings. SQL-side constructor: `eql_v3.ore_block_256`.", "items": { "type": "string" }, @@ -60,7 +60,7 @@ "ob": { "allOf": [ { - "$ref": "#/definitions/OreBlockU64_8_256" + "$ref": "#/definitions/OreBlock256" } ], "description": "Block-ORE order term. Serves equality too — ORE over a full-domain `int4` is lossless, so no separate `hm` is carried." diff --git a/crates/eql-types/schema/v3/int8_ord.json b/crates/eql-types/schema/v3/int8_ord.json index 9adc8520..b3146c34 100644 --- a/crates/eql-types/schema/v3/int8_ord.json +++ b/crates/eql-types/schema/v3/int8_ord.json @@ -26,8 +26,8 @@ ], "type": "object" }, - "OreBlockU64_8_256": { - "description": "Block-ORE (u64, 8 blocks, 256) order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. SQL-side constructor: `eql_v3.ore_block_u64_8_256`.", + "OreBlock256": { + "description": "Block-ORE order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. The block count is width-agnostic on the wire (8 for the int scalars, 12 for timestamptz, 14 for numeric) — the array just carries more block strings. SQL-side constructor: `eql_v3.ore_block_256`.", "items": { "type": "string" }, @@ -60,7 +60,7 @@ "ob": { "allOf": [ { - "$ref": "#/definitions/OreBlockU64_8_256" + "$ref": "#/definitions/OreBlock256" } ], "description": "Block-ORE order term. Serves equality too." diff --git a/crates/eql-types/schema/v3/int8_ord_ore.json b/crates/eql-types/schema/v3/int8_ord_ore.json index 174e68af..4c14eb98 100644 --- a/crates/eql-types/schema/v3/int8_ord_ore.json +++ b/crates/eql-types/schema/v3/int8_ord_ore.json @@ -26,8 +26,8 @@ ], "type": "object" }, - "OreBlockU64_8_256": { - "description": "Block-ORE (u64, 8 blocks, 256) order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. SQL-side constructor: `eql_v3.ore_block_u64_8_256`.", + "OreBlock256": { + "description": "Block-ORE order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. The block count is width-agnostic on the wire (8 for the int scalars, 12 for timestamptz, 14 for numeric) — the array just carries more block strings. SQL-side constructor: `eql_v3.ore_block_256`.", "items": { "type": "string" }, @@ -60,7 +60,7 @@ "ob": { "allOf": [ { - "$ref": "#/definitions/OreBlockU64_8_256" + "$ref": "#/definitions/OreBlock256" } ], "description": "Block-ORE order term. Serves equality too." diff --git a/crates/eql-types/schema/v3/numeric.json b/crates/eql-types/schema/v3/numeric.json new file mode 100644 index 00000000..c89d356f --- /dev/null +++ b/crates/eql-types/schema/v3/numeric.json @@ -0,0 +1,69 @@ +{ + "$id": "https://schemas.cipherstash.com/eql/v3/numeric.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "definitions": { + "Ciphertext": { + "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "type": "string" + }, + "Identifier": { + "additionalProperties": false, + "description": "Table + column identifier — wire shape `{\"t\": \"...\", \"c\": \"...\"}`.\n\nShared by every payload.", + "properties": { + "c": { + "description": "Column name.", + "type": "string" + }, + "t": { + "description": "Table name.", + "type": "string" + } + }, + "required": [ + "c", + "t" + ], + "type": "object" + }, + "SchemaVersion": { + "const": 2, + "description": "The envelope version field (`v`) — always exactly `2` on the wire.", + "type": "integer" + } + }, + "description": "`eql_v3.numeric` — storage only; every operator is blocked.", + "properties": { + "c": { + "allOf": [ + { + "$ref": "#/definitions/Ciphertext" + } + ], + "description": "mp_base85 source ciphertext. Required by the domain CHECK." + }, + "i": { + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ], + "description": "Table/column identifier. Required by the domain CHECK." + }, + "v": { + "allOf": [ + { + "$ref": "#/definitions/SchemaVersion" + } + ], + "description": "Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other value fails deserialization." + } + }, + "required": [ + "c", + "i", + "v" + ], + "title": "Numeric", + "type": "object" +} \ No newline at end of file diff --git a/crates/eql-types/schema/v3/numeric_eq.json b/crates/eql-types/schema/v3/numeric_eq.json new file mode 100644 index 00000000..8cfc98c8 --- /dev/null +++ b/crates/eql-types/schema/v3/numeric_eq.json @@ -0,0 +1,82 @@ +{ + "$id": "https://schemas.cipherstash.com/eql/v3/numeric_eq.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "definitions": { + "Ciphertext": { + "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "type": "string" + }, + "Hmac256": { + "description": "HMAC-SHA-256 equality term — the `hm` wire key. Backs the `_eq` domains (`=`, `<>`). SQL-side constructor: `eql_v3.hmac_256`.", + "type": "string" + }, + "Identifier": { + "additionalProperties": false, + "description": "Table + column identifier — wire shape `{\"t\": \"...\", \"c\": \"...\"}`.\n\nShared by every payload.", + "properties": { + "c": { + "description": "Column name.", + "type": "string" + }, + "t": { + "description": "Table name.", + "type": "string" + } + }, + "required": [ + "c", + "t" + ], + "type": "object" + }, + "SchemaVersion": { + "const": 2, + "description": "The envelope version field (`v`) — always exactly `2` on the wire.", + "type": "integer" + } + }, + "description": "`eql_v3.numeric_eq` — HMAC equality (`=`, `<>`).", + "properties": { + "c": { + "allOf": [ + { + "$ref": "#/definitions/Ciphertext" + } + ], + "description": "mp_base85 source ciphertext. Required by the domain CHECK." + }, + "hm": { + "allOf": [ + { + "$ref": "#/definitions/Hmac256" + } + ], + "description": "HMAC-SHA-256 equality term." + }, + "i": { + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ], + "description": "Table/column identifier. Required by the domain CHECK." + }, + "v": { + "allOf": [ + { + "$ref": "#/definitions/SchemaVersion" + } + ], + "description": "Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other value fails deserialization." + } + }, + "required": [ + "c", + "hm", + "i", + "v" + ], + "title": "NumericEq", + "type": "object" +} \ No newline at end of file diff --git a/crates/eql-types/schema/v3/numeric_ord.json b/crates/eql-types/schema/v3/numeric_ord.json new file mode 100644 index 00000000..f4d6571b --- /dev/null +++ b/crates/eql-types/schema/v3/numeric_ord.json @@ -0,0 +1,85 @@ +{ + "$id": "https://schemas.cipherstash.com/eql/v3/numeric_ord.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "definitions": { + "Ciphertext": { + "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "type": "string" + }, + "Identifier": { + "additionalProperties": false, + "description": "Table + column identifier — wire shape `{\"t\": \"...\", \"c\": \"...\"}`.\n\nShared by every payload.", + "properties": { + "c": { + "description": "Column name.", + "type": "string" + }, + "t": { + "description": "Table name.", + "type": "string" + } + }, + "required": [ + "c", + "t" + ], + "type": "object" + }, + "OreBlock256": { + "description": "Block-ORE order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. The block count is width-agnostic on the wire (8 for the int scalars, 12 for timestamptz, 14 for numeric) — the array just carries more block strings. SQL-side constructor: `eql_v3.ore_block_256`.", + "items": { + "type": "string" + }, + "type": "array" + }, + "SchemaVersion": { + "const": 2, + "description": "The envelope version field (`v`) — always exactly `2` on the wire.", + "type": "integer" + } + }, + "description": "`eql_v3.numeric_ord` — full comparison (`=` `<>` `<` `<=` `>` `>=`).", + "properties": { + "c": { + "allOf": [ + { + "$ref": "#/definitions/Ciphertext" + } + ], + "description": "mp_base85 source ciphertext. Required by the domain CHECK." + }, + "i": { + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ], + "description": "Table/column identifier. Required by the domain CHECK." + }, + "ob": { + "allOf": [ + { + "$ref": "#/definitions/OreBlock256" + } + ], + "description": "Block-ORE order term (14 blocks for numeric). Serves equality too." + }, + "v": { + "allOf": [ + { + "$ref": "#/definitions/SchemaVersion" + } + ], + "description": "Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other value fails deserialization." + } + }, + "required": [ + "c", + "i", + "ob", + "v" + ], + "title": "NumericOrd", + "type": "object" +} \ No newline at end of file diff --git a/crates/eql-types/schema/v3/numeric_ord_ore.json b/crates/eql-types/schema/v3/numeric_ord_ore.json new file mode 100644 index 00000000..748b2ba6 --- /dev/null +++ b/crates/eql-types/schema/v3/numeric_ord_ore.json @@ -0,0 +1,85 @@ +{ + "$id": "https://schemas.cipherstash.com/eql/v3/numeric_ord_ore.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "definitions": { + "Ciphertext": { + "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "type": "string" + }, + "Identifier": { + "additionalProperties": false, + "description": "Table + column identifier — wire shape `{\"t\": \"...\", \"c\": \"...\"}`.\n\nShared by every payload.", + "properties": { + "c": { + "description": "Column name.", + "type": "string" + }, + "t": { + "description": "Table name.", + "type": "string" + } + }, + "required": [ + "c", + "t" + ], + "type": "object" + }, + "OreBlock256": { + "description": "Block-ORE order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. The block count is width-agnostic on the wire (8 for the int scalars, 12 for timestamptz, 14 for numeric) — the array just carries more block strings. SQL-side constructor: `eql_v3.ore_block_256`.", + "items": { + "type": "string" + }, + "type": "array" + }, + "SchemaVersion": { + "const": 2, + "description": "The envelope version field (`v`) — always exactly `2` on the wire.", + "type": "integer" + } + }, + "description": "`eql_v3.numeric_ord_ore` — full comparison, scheme-explicit name.", + "properties": { + "c": { + "allOf": [ + { + "$ref": "#/definitions/Ciphertext" + } + ], + "description": "mp_base85 source ciphertext. Required by the domain CHECK." + }, + "i": { + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ], + "description": "Table/column identifier. Required by the domain CHECK." + }, + "ob": { + "allOf": [ + { + "$ref": "#/definitions/OreBlock256" + } + ], + "description": "Block-ORE order term (14 blocks for numeric). Serves equality too." + }, + "v": { + "allOf": [ + { + "$ref": "#/definitions/SchemaVersion" + } + ], + "description": "Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other value fails deserialization." + } + }, + "required": [ + "c", + "i", + "ob", + "v" + ], + "title": "NumericOrdOre", + "type": "object" +} \ No newline at end of file diff --git a/crates/eql-types/schema/v3/text_ord.json b/crates/eql-types/schema/v3/text_ord.json index 6b059e2a..0b69333d 100644 --- a/crates/eql-types/schema/v3/text_ord.json +++ b/crates/eql-types/schema/v3/text_ord.json @@ -30,8 +30,8 @@ ], "type": "object" }, - "OreBlockU64_8_256": { - "description": "Block-ORE (u64, 8 blocks, 256) order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. SQL-side constructor: `eql_v3.ore_block_u64_8_256`.", + "OreBlock256": { + "description": "Block-ORE order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. The block count is width-agnostic on the wire (8 for the int scalars, 12 for timestamptz, 14 for numeric) — the array just carries more block strings. SQL-side constructor: `eql_v3.ore_block_256`.", "items": { "type": "string" }, @@ -72,7 +72,7 @@ "ob": { "allOf": [ { - "$ref": "#/definitions/OreBlockU64_8_256" + "$ref": "#/definitions/OreBlock256" } ], "description": "Block-ORE order term." diff --git a/crates/eql-types/schema/v3/text_ord_ore.json b/crates/eql-types/schema/v3/text_ord_ore.json index d4899142..094f9a49 100644 --- a/crates/eql-types/schema/v3/text_ord_ore.json +++ b/crates/eql-types/schema/v3/text_ord_ore.json @@ -30,8 +30,8 @@ ], "type": "object" }, - "OreBlockU64_8_256": { - "description": "Block-ORE (u64, 8 blocks, 256) order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. SQL-side constructor: `eql_v3.ore_block_u64_8_256`.", + "OreBlock256": { + "description": "Block-ORE order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. The block count is width-agnostic on the wire (8 for the int scalars, 12 for timestamptz, 14 for numeric) — the array just carries more block strings. SQL-side constructor: `eql_v3.ore_block_256`.", "items": { "type": "string" }, @@ -72,7 +72,7 @@ "ob": { "allOf": [ { - "$ref": "#/definitions/OreBlockU64_8_256" + "$ref": "#/definitions/OreBlock256" } ], "description": "Block-ORE order term." diff --git a/crates/eql-types/schema/v3/text_search.json b/crates/eql-types/schema/v3/text_search.json index 87188f4b..2beaeffe 100644 --- a/crates/eql-types/schema/v3/text_search.json +++ b/crates/eql-types/schema/v3/text_search.json @@ -40,8 +40,8 @@ ], "type": "object" }, - "OreBlockU64_8_256": { - "description": "Block-ORE (u64, 8 blocks, 256) order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. SQL-side constructor: `eql_v3.ore_block_u64_8_256`.", + "OreBlock256": { + "description": "Block-ORE order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. The block count is width-agnostic on the wire (8 for the int scalars, 12 for timestamptz, 14 for numeric) — the array just carries more block strings. SQL-side constructor: `eql_v3.ore_block_256`.", "items": { "type": "string" }, @@ -90,7 +90,7 @@ "ob": { "allOf": [ { - "$ref": "#/definitions/OreBlockU64_8_256" + "$ref": "#/definitions/OreBlock256" } ], "description": "Block-ORE order term." diff --git a/crates/eql-types/schema/v3/timestamptz_ord.json b/crates/eql-types/schema/v3/timestamptz_ord.json new file mode 100644 index 00000000..993bc94c --- /dev/null +++ b/crates/eql-types/schema/v3/timestamptz_ord.json @@ -0,0 +1,85 @@ +{ + "$id": "https://schemas.cipherstash.com/eql/v3/timestamptz_ord.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "definitions": { + "Ciphertext": { + "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "type": "string" + }, + "Identifier": { + "additionalProperties": false, + "description": "Table + column identifier — wire shape `{\"t\": \"...\", \"c\": \"...\"}`.\n\nShared by every payload.", + "properties": { + "c": { + "description": "Column name.", + "type": "string" + }, + "t": { + "description": "Table name.", + "type": "string" + } + }, + "required": [ + "c", + "t" + ], + "type": "object" + }, + "OreBlock256": { + "description": "Block-ORE order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. The block count is width-agnostic on the wire (8 for the int scalars, 12 for timestamptz, 14 for numeric) — the array just carries more block strings. SQL-side constructor: `eql_v3.ore_block_256`.", + "items": { + "type": "string" + }, + "type": "array" + }, + "SchemaVersion": { + "const": 2, + "description": "The envelope version field (`v`) — always exactly `2` on the wire.", + "type": "integer" + } + }, + "description": "`eql_v3.timestamptz_ord` — full comparison (`=` `<>` `<` `<=` `>` `>=`).", + "properties": { + "c": { + "allOf": [ + { + "$ref": "#/definitions/Ciphertext" + } + ], + "description": "mp_base85 source ciphertext. Required by the domain CHECK." + }, + "i": { + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ], + "description": "Table/column identifier. Required by the domain CHECK." + }, + "ob": { + "allOf": [ + { + "$ref": "#/definitions/OreBlock256" + } + ], + "description": "Block-ORE order term (12 blocks for timestamptz). Serves equality too." + }, + "v": { + "allOf": [ + { + "$ref": "#/definitions/SchemaVersion" + } + ], + "description": "Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other value fails deserialization." + } + }, + "required": [ + "c", + "i", + "ob", + "v" + ], + "title": "TimestamptzOrd", + "type": "object" +} \ No newline at end of file diff --git a/crates/eql-types/schema/v3/timestamptz_ord_ore.json b/crates/eql-types/schema/v3/timestamptz_ord_ore.json new file mode 100644 index 00000000..9d202d6e --- /dev/null +++ b/crates/eql-types/schema/v3/timestamptz_ord_ore.json @@ -0,0 +1,85 @@ +{ + "$id": "https://schemas.cipherstash.com/eql/v3/timestamptz_ord_ore.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "definitions": { + "Ciphertext": { + "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "type": "string" + }, + "Identifier": { + "additionalProperties": false, + "description": "Table + column identifier — wire shape `{\"t\": \"...\", \"c\": \"...\"}`.\n\nShared by every payload.", + "properties": { + "c": { + "description": "Column name.", + "type": "string" + }, + "t": { + "description": "Table name.", + "type": "string" + } + }, + "required": [ + "c", + "t" + ], + "type": "object" + }, + "OreBlock256": { + "description": "Block-ORE order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's domain, so it serves equality too. The block count is width-agnostic on the wire (8 for the int scalars, 12 for timestamptz, 14 for numeric) — the array just carries more block strings. SQL-side constructor: `eql_v3.ore_block_256`.", + "items": { + "type": "string" + }, + "type": "array" + }, + "SchemaVersion": { + "const": 2, + "description": "The envelope version field (`v`) — always exactly `2` on the wire.", + "type": "integer" + } + }, + "description": "`eql_v3.timestamptz_ord_ore` — full comparison, scheme-explicit name.", + "properties": { + "c": { + "allOf": [ + { + "$ref": "#/definitions/Ciphertext" + } + ], + "description": "mp_base85 source ciphertext. Required by the domain CHECK." + }, + "i": { + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ], + "description": "Table/column identifier. Required by the domain CHECK." + }, + "ob": { + "allOf": [ + { + "$ref": "#/definitions/OreBlock256" + } + ], + "description": "Block-ORE order term (12 blocks for timestamptz). Serves equality too." + }, + "v": { + "allOf": [ + { + "$ref": "#/definitions/SchemaVersion" + } + ], + "description": "Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other value fails deserialization." + } + }, + "required": [ + "c", + "i", + "ob", + "v" + ], + "title": "TimestamptzOrdOre", + "type": "object" +} \ No newline at end of file diff --git a/crates/eql-types/src/v3/date.rs b/crates/eql-types/src/v3/date.rs index ffd0bbbb..1da37028 100644 --- a/crates/eql-types/src/v3/date.rs +++ b/crates/eql-types/src/v3/date.rs @@ -5,7 +5,7 @@ use schemars::{schema::RootSchema, schema_for}; -use crate::v3::terms::{Ciphertext, Hmac256, OreBlockU64_8_256}; +use crate::v3::terms::{Ciphertext, Hmac256, OreBlock256}; use crate::v3::DomainType; use crate::{Identifier, SchemaVersion}; use schemars::JsonSchema; @@ -83,7 +83,7 @@ pub struct DateOrdOre { /// mp_base85 source ciphertext. Required by the domain CHECK. pub c: Ciphertext, /// Block-ORE order term. Serves equality too. - pub ob: OreBlockU64_8_256, + pub ob: OreBlock256, } impl DomainType for DateOrdOre { @@ -113,7 +113,7 @@ pub struct DateOrd { /// mp_base85 source ciphertext. Required by the domain CHECK. pub c: Ciphertext, /// Block-ORE order term. Serves equality too. - pub ob: OreBlockU64_8_256, + pub ob: OreBlock256, } impl DomainType for DateOrd { diff --git a/crates/eql-types/src/v3/int2.rs b/crates/eql-types/src/v3/int2.rs index 8e2fc939..6ee64e5a 100644 --- a/crates/eql-types/src/v3/int2.rs +++ b/crates/eql-types/src/v3/int2.rs @@ -3,7 +3,7 @@ use schemars::{schema::RootSchema, schema_for}; -use crate::v3::terms::{Ciphertext, Hmac256, OreBlockU64_8_256}; +use crate::v3::terms::{Ciphertext, Hmac256, OreBlock256}; use crate::v3::DomainType; use crate::{Identifier, SchemaVersion}; use schemars::JsonSchema; @@ -81,7 +81,7 @@ pub struct Int2OrdOre { /// mp_base85 source ciphertext. Required by the domain CHECK. pub c: Ciphertext, /// Block-ORE order term. Serves equality too. - pub ob: OreBlockU64_8_256, + pub ob: OreBlock256, } impl DomainType for Int2OrdOre { @@ -111,7 +111,7 @@ pub struct Int2Ord { /// mp_base85 source ciphertext. Required by the domain CHECK. pub c: Ciphertext, /// Block-ORE order term. Serves equality too. - pub ob: OreBlockU64_8_256, + pub ob: OreBlock256, } impl DomainType for Int2Ord { diff --git a/crates/eql-types/src/v3/int4.rs b/crates/eql-types/src/v3/int4.rs index 752ddbf1..74296f49 100644 --- a/crates/eql-types/src/v3/int4.rs +++ b/crates/eql-types/src/v3/int4.rs @@ -9,7 +9,7 @@ use schemars::{schema::RootSchema, schema_for}; -use crate::v3::terms::{Ciphertext, Hmac256, OreBlockU64_8_256}; +use crate::v3::terms::{Ciphertext, Hmac256, OreBlock256}; use crate::v3::DomainType; use crate::{Identifier, SchemaVersion}; use schemars::JsonSchema; @@ -89,7 +89,7 @@ pub struct Int4OrdOre { pub c: Ciphertext, /// Block-ORE order term. Serves equality too — ORE over a /// full-domain `int4` is lossless, so no separate `hm` is carried. - pub ob: OreBlockU64_8_256, + pub ob: OreBlock256, } impl DomainType for Int4OrdOre { @@ -119,7 +119,7 @@ pub struct Int4Ord { /// mp_base85 source ciphertext. Required by the domain CHECK. pub c: Ciphertext, /// Block-ORE order term. Serves equality too. - pub ob: OreBlockU64_8_256, + pub ob: OreBlock256, } impl DomainType for Int4Ord { diff --git a/crates/eql-types/src/v3/int8.rs b/crates/eql-types/src/v3/int8.rs index ea0f5714..0bd46fd2 100644 --- a/crates/eql-types/src/v3/int8.rs +++ b/crates/eql-types/src/v3/int8.rs @@ -3,7 +3,7 @@ use schemars::{schema::RootSchema, schema_for}; -use crate::v3::terms::{Ciphertext, Hmac256, OreBlockU64_8_256}; +use crate::v3::terms::{Ciphertext, Hmac256, OreBlock256}; use crate::v3::DomainType; use crate::{Identifier, SchemaVersion}; use schemars::JsonSchema; @@ -81,7 +81,7 @@ pub struct Int8OrdOre { /// mp_base85 source ciphertext. Required by the domain CHECK. pub c: Ciphertext, /// Block-ORE order term. Serves equality too. - pub ob: OreBlockU64_8_256, + pub ob: OreBlock256, } impl DomainType for Int8OrdOre { @@ -111,7 +111,7 @@ pub struct Int8Ord { /// mp_base85 source ciphertext. Required by the domain CHECK. pub c: Ciphertext, /// Block-ORE order term. Serves equality too. - pub ob: OreBlockU64_8_256, + pub ob: OreBlock256, } impl DomainType for Int8Ord { diff --git a/crates/eql-types/src/v3/mod.rs b/crates/eql-types/src/v3/mod.rs index deb3b297..954a4471 100644 --- a/crates/eql-types/src/v3/mod.rs +++ b/crates/eql-types/src/v3/mod.rs @@ -52,6 +52,7 @@ pub mod date; pub mod int2; pub mod int4; pub mod int8; +pub mod numeric; pub mod terms; pub mod text; pub mod timestamptz; @@ -151,6 +152,12 @@ pub fn all() -> Vec> { Box::new(PhantomData::), Box::new(PhantomData::), Box::new(PhantomData::), + Box::new(PhantomData::), + Box::new(PhantomData::), + Box::new(PhantomData::), + Box::new(PhantomData::), + Box::new(PhantomData::), + Box::new(PhantomData::), Box::new(PhantomData::), Box::new(PhantomData::), Box::new(PhantomData::), diff --git a/crates/eql-types/src/v3/numeric.rs b/crates/eql-types/src/v3/numeric.rs new file mode 100644 index 00000000..d7d5b08a --- /dev/null +++ b/crates/eql-types/src/v3/numeric.rs @@ -0,0 +1,136 @@ +//! The `numeric` encrypted-domain family — an ordered, non-integer scalar +//! backed by `rust_decimal::Decimal`. Same four-domain ordered shape as +//! [`crate::v3::int4`] (ORE compares ciphertext, so decimals order like +//! integers); see that module for the capability table. +//! +//! `numeric` is the first scalar whose native ORE term is wider than 8 blocks +//! (14 blocks): the wire shape is unchanged — the `ob` array simply carries +//! more block strings — and the generalized `eql_v3.ore_block_256` comparator +//! orders any block count, so no new type is needed here. + +use schemars::{schema::RootSchema, schema_for}; + +use crate::v3::terms::{Ciphertext, Hmac256, OreBlock256}; +use crate::v3::DomainType; +use crate::{Identifier, SchemaVersion}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use ts_rs::TS; + +/// `eql_v3.numeric` — storage only; every operator is blocked. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] +#[ts(export, export_to = "v3/")] +#[serde(deny_unknown_fields)] +pub struct Numeric { + /// Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other + /// value fails deserialization. + pub v: SchemaVersion, + /// Table/column identifier. Required by the domain CHECK. + pub i: Identifier, + /// mp_base85 source ciphertext. Required by the domain CHECK. + pub c: Ciphertext, +} + +impl DomainType for Numeric { + fn sql_domain_static() -> &'static str { + "eql_v3.numeric" + } + + fn sql_domain(&self) -> &'static str { + Self::sql_domain_static() + } + + fn schema(&self) -> RootSchema { + schema_for!(Numeric) + } +} + +/// `eql_v3.numeric_eq` — HMAC equality (`=`, `<>`). +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] +#[ts(export, export_to = "v3/")] +#[serde(deny_unknown_fields)] +pub struct NumericEq { + /// Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other + /// value fails deserialization. + pub v: SchemaVersion, + /// Table/column identifier. Required by the domain CHECK. + pub i: Identifier, + /// mp_base85 source ciphertext. Required by the domain CHECK. + pub c: Ciphertext, + /// HMAC-SHA-256 equality term. + pub hm: Hmac256, +} + +impl DomainType for NumericEq { + fn sql_domain_static() -> &'static str { + "eql_v3.numeric_eq" + } + + fn sql_domain(&self) -> &'static str { + Self::sql_domain_static() + } + + fn schema(&self) -> RootSchema { + schema_for!(NumericEq) + } +} + +/// `eql_v3.numeric_ord_ore` — full comparison, scheme-explicit name. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] +#[ts(export, export_to = "v3/")] +#[serde(deny_unknown_fields)] +pub struct NumericOrdOre { + /// Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other + /// value fails deserialization. + pub v: SchemaVersion, + /// Table/column identifier. Required by the domain CHECK. + pub i: Identifier, + /// mp_base85 source ciphertext. Required by the domain CHECK. + pub c: Ciphertext, + /// Block-ORE order term (14 blocks for numeric). Serves equality too. + pub ob: OreBlock256, +} + +impl DomainType for NumericOrdOre { + fn sql_domain_static() -> &'static str { + "eql_v3.numeric_ord_ore" + } + + fn sql_domain(&self) -> &'static str { + Self::sql_domain_static() + } + + fn schema(&self) -> RootSchema { + schema_for!(NumericOrdOre) + } +} + +/// `eql_v3.numeric_ord` — full comparison (`=` `<>` `<` `<=` `>` `>=`). +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] +#[ts(export, export_to = "v3/")] +#[serde(deny_unknown_fields)] +pub struct NumericOrd { + /// Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other + /// value fails deserialization. + pub v: SchemaVersion, + /// Table/column identifier. Required by the domain CHECK. + pub i: Identifier, + /// mp_base85 source ciphertext. Required by the domain CHECK. + pub c: Ciphertext, + /// Block-ORE order term (14 blocks for numeric). Serves equality too. + pub ob: OreBlock256, +} + +impl DomainType for NumericOrd { + fn sql_domain_static() -> &'static str { + "eql_v3.numeric_ord" + } + + fn sql_domain(&self) -> &'static str { + Self::sql_domain_static() + } + + fn schema(&self) -> RootSchema { + schema_for!(NumericOrd) + } +} diff --git a/crates/eql-types/src/v3/terms.rs b/crates/eql-types/src/v3/terms.rs index d12cfad4..9a2bfc66 100644 --- a/crates/eql-types/src/v3/terms.rs +++ b/crates/eql-types/src/v3/terms.rs @@ -28,13 +28,15 @@ pub struct Ciphertext(pub String); #[ts(export, export_to = "v3/")] pub struct Hmac256(pub String); -/// Block-ORE (u64, 8 blocks, 256) order term — the `ob` wire key. Backs the -/// `_ord` / `_ord_ore` domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless -/// over the scalar's domain, so it serves equality too. SQL-side constructor: -/// `eql_v3.ore_block_u64_8_256`. +/// Block-ORE order term — the `ob` wire key. Backs the `_ord` / `_ord_ore` +/// domains (`=` `<>` `<` `<=` `>` `>=`); ORE is lossless over the scalar's +/// domain, so it serves equality too. The block count is width-agnostic on the +/// wire (8 for the int scalars, 12 for timestamptz, 14 for numeric) — the +/// array just carries more block strings. SQL-side constructor: +/// `eql_v3.ore_block_256`. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] -pub struct OreBlockU64_8_256(pub Vec); +pub struct OreBlock256(pub Vec); /// Bloom-filter match term — the `bf` wire key. Backs the `_match` domains /// (`@>`/`<@` containment). @@ -107,7 +109,7 @@ impl From for Hmac256 { } } -impl From> for OreBlockU64_8_256 { +impl From> for OreBlock256 { fn from(value: Vec) -> Self { Self(value) } diff --git a/crates/eql-types/src/v3/text.rs b/crates/eql-types/src/v3/text.rs index a0008947..2d6b0598 100644 --- a/crates/eql-types/src/v3/text.rs +++ b/crates/eql-types/src/v3/text.rs @@ -4,7 +4,7 @@ use schemars::{schema::RootSchema, schema_for}; -use crate::v3::terms::{BloomFilter, Ciphertext, Hmac256, OreBlockU64_8_256}; +use crate::v3::terms::{BloomFilter, Ciphertext, Hmac256, OreBlock256}; use crate::v3::DomainType; use crate::{Identifier, SchemaVersion}; use schemars::JsonSchema; @@ -117,7 +117,7 @@ pub struct TextOrdOre { /// HMAC-SHA-256 equality term. Text routes `=`/`<>` through `hm`. pub hm: Hmac256, /// Block-ORE order term. - pub ob: OreBlockU64_8_256, + pub ob: OreBlock256, } impl DomainType for TextOrdOre { @@ -151,7 +151,7 @@ pub struct TextOrd { /// HMAC-SHA-256 equality term. Text routes `=`/`<>` through `hm`. pub hm: Hmac256, /// Block-ORE order term. - pub ob: OreBlockU64_8_256, + pub ob: OreBlock256, } impl DomainType for TextOrd { @@ -185,7 +185,7 @@ pub struct TextSearch { /// HMAC-SHA-256 equality term. pub hm: Hmac256, /// Block-ORE order term. - pub ob: OreBlockU64_8_256, + pub ob: OreBlock256, /// Bloom-filter match term (signed smallint bit positions). pub bf: BloomFilter, } diff --git a/crates/eql-types/src/v3/timestamptz.rs b/crates/eql-types/src/v3/timestamptz.rs index 3deb51a2..a86ad5d5 100644 --- a/crates/eql-types/src/v3/timestamptz.rs +++ b/crates/eql-types/src/v3/timestamptz.rs @@ -1,12 +1,17 @@ -//! The `timestamptz` encrypted-domain family — **equality-only** (storage + -//! `_eq`). There is no ordered domain: cipherstash encrypts timestamps at -//! native 12-block ORE width, but EQL's only ORE comparator is hardcoded to -//! 8 blocks, so an ordered timestamptz domain would silently mis-order. -//! Ordering arrives with a future wide-ORE term (see `eql-scalars`). +//! The `timestamptz` encrypted-domain family — an ordered, non-integer scalar. +//! Same four-domain ordered shape as [`crate::v3::int4`] (ORE compares +//! ciphertext, so timestamps order like integers); see that module for the +//! capability table. +//! +//! cipherstash encrypts timestamps at native 12-block ORE width. The family +//! was equality-only while EQL's ORE comparator was hardcoded to 8 blocks; +//! now that `eql_v3.ore_block_256` derives the block count from the term +//! length, the 12-block `ob` term orders correctly and the ordered domains +//! ship. The wire shape is unchanged — the `ob` array just carries 12 blocks. use schemars::{schema::RootSchema, schema_for}; -use crate::v3::terms::{Ciphertext, Hmac256}; +use crate::v3::terms::{Ciphertext, Hmac256, OreBlock256}; use crate::v3::DomainType; use crate::{Identifier, SchemaVersion}; use schemars::JsonSchema; @@ -70,3 +75,63 @@ impl DomainType for TimestamptzEq { schema_for!(TimestamptzEq) } } + +/// `eql_v3.timestamptz_ord_ore` — full comparison, scheme-explicit name. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] +#[ts(export, export_to = "v3/")] +#[serde(deny_unknown_fields)] +pub struct TimestamptzOrdOre { + /// Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other + /// value fails deserialization. + pub v: SchemaVersion, + /// Table/column identifier. Required by the domain CHECK. + pub i: Identifier, + /// mp_base85 source ciphertext. Required by the domain CHECK. + pub c: Ciphertext, + /// Block-ORE order term (12 blocks for timestamptz). Serves equality too. + pub ob: OreBlock256, +} + +impl DomainType for TimestamptzOrdOre { + fn sql_domain_static() -> &'static str { + "eql_v3.timestamptz_ord_ore" + } + + fn sql_domain(&self) -> &'static str { + Self::sql_domain_static() + } + + fn schema(&self) -> RootSchema { + schema_for!(TimestamptzOrdOre) + } +} + +/// `eql_v3.timestamptz_ord` — full comparison (`=` `<>` `<` `<=` `>` `>=`). +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] +#[ts(export, export_to = "v3/")] +#[serde(deny_unknown_fields)] +pub struct TimestamptzOrd { + /// Envelope version — always `2` (`EQL_SCHEMA_VERSION`); any other + /// value fails deserialization. + pub v: SchemaVersion, + /// Table/column identifier. Required by the domain CHECK. + pub i: Identifier, + /// mp_base85 source ciphertext. Required by the domain CHECK. + pub c: Ciphertext, + /// Block-ORE order term (12 blocks for timestamptz). Serves equality too. + pub ob: OreBlock256, +} + +impl DomainType for TimestamptzOrd { + fn sql_domain_static() -> &'static str { + "eql_v3.timestamptz_ord" + } + + fn sql_domain(&self) -> &'static str { + Self::sql_domain_static() + } + + fn schema(&self) -> RootSchema { + schema_for!(TimestamptzOrd) + } +} diff --git a/crates/eql-types/tests/v3_conformance.rs b/crates/eql-types/tests/v3_conformance.rs index a20e6e99..df345674 100644 --- a/crates/eql-types/tests/v3_conformance.rs +++ b/crates/eql-types/tests/v3_conformance.rs @@ -168,7 +168,7 @@ fn non_int4_tokens_round_trip_every_domain() { // `catalog_parity.rs` checks domain *names* only, never the wire shape. // This sweep roundtrips every non-int4 domain and pins its catalog name, // failing the instant a token drifts from the shared envelope/term contract. - use eql_types::v3::{date::*, int2::*, int8::*, text::*}; + use eql_types::v3::{date::*, int2::*, int8::*, numeric::*, text::*}; // Wire builders for the three shapes the ordered tokens share. let storage = |t: &str| json!({ "v": 2, "i": { "t": t, "c": "x" }, "c": "ct" }); @@ -204,6 +204,13 @@ fn non_int4_tokens_round_trip_every_domain() { round_trip!(DateOrd, ord("a"), "eql_v3.date_ord"); round_trip!(DateOrdOre, ord("a"), "eql_v3.date_ord_ore"); + // numeric is the first scalar whose native ORE term exceeds 8 blocks (14); + // the wire shape is identical, so the same `ord` builder applies. + round_trip!(Numeric, storage("a"), "eql_v3.numeric"); + round_trip!(NumericEq, eq("a"), "eql_v3.numeric_eq"); + round_trip!(NumericOrd, ord("a"), "eql_v3.numeric_ord"); + round_trip!(NumericOrdOre, ord("a"), "eql_v3.numeric_ord_ore"); + // text_match is covered by `text_match_round_trips_signed_bloom_filter`. round_trip!(Text, storage("a"), "eql_v3.text"); round_trip!(TextEq, eq("a"), "eql_v3.text_eq"); @@ -213,12 +220,16 @@ fn non_int4_tokens_round_trip_every_domain() { } #[test] -fn timestamptz_round_trips_and_enforces_equality_term() { - // The one structurally-distinct token: equality-only, no `_ord`/`_ord_ore` - // (the 8-block-ORE limitation). The int4 template was copy-pasted to - // produce it, so an accidental extra `ob` field or a dropped `hm` would - // pass `catalog_parity` (domain names only) but is caught here. - use eql_types::v3::timestamptz::{Timestamptz, TimestamptzEq}; +fn timestamptz_round_trips_and_enforces_term_capabilities() { + // timestamptz is an ordered token (12-block ORE) — it carries the full + // storage/`_eq`/`_ord`/`_ord_ore` shape, the same as the int scalars. The + // int4 template was copy-pasted to produce it, so a dropped `hm`/`ob` or a + // field typo would pass `catalog_parity` (domain names only) but is caught + // here. (Was equality-only while the ORE comparator was hardcoded to 8 + // blocks; promoted once `eql_v3.ore_block_256` generalized to any width.) + use eql_types::v3::timestamptz::{ + Timestamptz, TimestamptzEq, TimestamptzOrd, TimestamptzOrdOre, + }; // Storage-only: envelope, no term. let storage = json!({ @@ -241,16 +252,40 @@ fn timestamptz_round_trips_and_enforces_equality_term() { assert_eq!(serde_json::to_value(&parsed).unwrap(), with_hm); assert_eq!(TimestamptzEq::sql_domain_static(), "eql_v3.timestamptz_eq"); - // `_eq` is the only searchable shape this token has, so its equality term - // cannot silently become optional. + // Ordered: envelope + ob (a 12-block array on the wire; shape is the same). + let with_ob = json!({ + "v": 2, + "i": { "t": "events", "c": "occurred_at" }, + "c": "mp_base85_ciphertext", + "ob": ["b0", "b1"] + }); + let parsed: TimestamptzOrd = serde_json::from_value(with_ob.clone()).unwrap(); + assert_eq!(serde_json::to_value(&parsed).unwrap(), with_ob); + assert_eq!( + TimestamptzOrd::sql_domain_static(), + "eql_v3.timestamptz_ord" + ); + let parsed: TimestamptzOrdOre = serde_json::from_value(with_ob.clone()).unwrap(); + assert_eq!(serde_json::to_value(&parsed).unwrap(), with_ob); + assert_eq!( + TimestamptzOrdOre::sql_domain_static(), + "eql_v3.timestamptz_ord_ore" + ); + + // The searchable domains cannot let their term silently become optional. let no_hm = json!({ "v": 2, "i": { "t": "events", "c": "occurred_at" }, "c": "mp_base85_ciphertext" }); - let result: Result = serde_json::from_value(no_hm); + let result: Result = serde_json::from_value(no_hm.clone()); assert!( result.is_err(), "TimestamptzEq must reject a payload with no hm" ); + let result: Result = serde_json::from_value(no_hm); + assert!( + result.is_err(), + "TimestamptzOrd must reject a payload with no ob" + ); } diff --git a/docs/analysis/2026-06-11-v3-scalar-vs-jsonb-test-coverage.md b/docs/analysis/2026-06-11-v3-scalar-vs-jsonb-test-coverage.md index 474fb78e..d4cac1ee 100644 --- a/docs/analysis/2026-06-11-v3-scalar-vs-jsonb-test-coverage.md +++ b/docs/analysis/2026-06-11-v3-scalar-vs-jsonb-test-coverage.md @@ -125,7 +125,7 @@ Source of truth: `crates/eql-scalars/src` (`CATALOG`). Adding a type is one `Sca | Term | Key | Extractor → type | Provides | Operators | |---|---|---|---|---| | `Hm` | `hm` | `eq_term` → `eql_v3.hmac_256` | equality | `=` `<>` | -| `Ore` | `ob` | `ord_term` → `eql_v3.ore_block_u64_8_256` | equality + ordering | `=` `<>` `<` `<=` `>` `>=` | +| `Ore` | `ob` | `ord_term` → `eql_v3.ore_block_256` | equality + ordering | `=` `<>` `<` `<=` `>` `>=` | | `Bloom` | `bf` | `match_term` → `eql_v3.bloom_filter` | containment | `@>` `<@` | Domain → role: empty ⇒ Storage, first term `Hm` ⇒ Eq, `Ore` ⇒ Ord, `Bloom` ⇒ Match. diff --git a/docs/analysis/2026-06-17-ore-block-256-wire-format-validation.md b/docs/analysis/2026-06-17-ore-block-256-wire-format-validation.md new file mode 100644 index 00000000..bcb90c34 --- /dev/null +++ b/docs/analysis/2026-06-17-ore-block-256-wire-format-validation.md @@ -0,0 +1,71 @@ +# ORE block-256 wire format — N-from-length validation + +- **Date:** 2026-06-17 +- **Context:** Validates the `eql_v3` N-block comparator (`eql_v3.compare_ore_block_256_term`) against a PR review claim that ore-rs always emits 8 blocks and chunks wide values into an array. +- **Verdict:** Claim does **not** hold for the pinned deps (`ore-rs 0.8.3`, `cipherstash-client 0.35.0`). Deriving block count `N` from term length is correct for what the crypto emits today. + +## Claim under review + +> "The current version of ore.rs is always 8-blocks output. To handle longer values, 8-block chunks are created in an array. ... the upcoming changes to ore.rs will support a variable length encoding." + +## Wire format (per term) + +``` +[ N PRP bytes ][ N×16B left blocks ][ 16B hash key ][ N×32B right blocks ] +octet_length = 17·N + 16 + 32·N = 49·N + 16 => N = (octet_length − 16) / 49 +``` + +Linear in `N` and invertible for `N ≥ 1`, so length recovers the exact `N` the encryptor used. + +## Evidence + +**1. ore-rs 0.8.3 — native variable width, not 8-block** ([decimal.rs](https://docs.rs/crate/ore-rs/0.8.3/source/src/decimal.rs), [chrono.rs](https://docs.rs/crate/ore-rs/0.8.3/source/src/chrono.rs)) + +```rust +//! ... feeding the 14-byte plaintext through the existing fixed-N ORE machinery (`N = 14`). // L7 +const ENCODED_LEN: usize = ::ENCODED_LEN; // L16 +type FullOutput = CipherText; // L20 +``` + +**2. cipherstash-client 0.35.0 — single full-width term, only `text` uses an array** ([ore_indexer/mod.rs](https://docs.rs/crate/cipherstash-client/0.35.0/source/src/encryption/ore_indexer/mod.rs)) + +```rust +// Decimal (14 bytes) and Timestamp (12 bytes) exceed the 8-byte block; +// ... encrypted at their native ore-rs widths. // L136–139 +Plaintext::Decimal(Some(x)) => Ok(IndexTerm::OreFull(x.encrypt(&cipher)?.to_bytes())), // L140 +Plaintext::Timestamp(Some(x)) => Ok(IndexTerm::OreFull(x.encrypt(&cipher)?.to_bytes())), // L141 +``` + +> doc comment: *"Strings will return an `IndexTerm::OreArray`. All other types will return a `IndexTerm::OreFull`."* + +Integers go through `pad_orderable_to_8` (→ N=8); Decimal/Timestamp do **not**. `OreFull` = one term, not chunked. + +**3. Real ZeroKMS fixtures — measured, end-to-end** (`tests/sqlx/fixtures/eql_v2_*.sql`) + +| type | `ob` array | term bytes | derived N | +|------|-----------:|-----------:|----------:| +| int4 | 1 term | 408 | 8 (`49·8+16`) | +| timestamptz | 1 term | 604 | 12 (`49·12+16`) | +| numeric | 1 term | 702 | 14 (`49·14+16`) | + +A numeric value is **one 702-byte term**, not an array of 408-byte chunks. Pinned by `numeric_term_is_14_blocks` / timestamptz-width tests in `tests/sqlx/tests/ore_block_comparator_tests.rs`. + +## Correctness of the derivation + +- Equal-length guard (`bit_length(a) != bit_length(b)` → raise) ⇒ `a`/`b` share `N`; deriving from `a` alone is sound and blocks cross-type comparison. +- Well-formedness guard is **both** clauses — `octet_length <= 16 OR (octet_length − 16) % 49 != 0`. Modulo alone admits a 16-byte term (`N=0`) that would silently return `0` (equal); `<= 16` is load-bearing. +- N=8 reduces to the old constants (`right_offset = 17·8 = 136`, left base `1+8 = 9`) ⇒ no-op for existing int types. +- Ordering verified against the plaintext oracle (`Decimal`/`DateTime` `Ord`) over **all pairs** + antisymmetry — `assert_orders_like_oracle`, `ore_block_comparator_tests.rs`. + +## Forward-looking caveat + +- The reviewer's "upcoming variable-length ore-rs" appears to already be present in 0.8.3 (native N=14/12). If a *further* format change is planned, it is a fixture-regeneration / breaking change that equally affects the existing `eql_v2` 8-block comparator — it does not make the current derivation incorrect. +- Action: confirm any tracked ore-rs format change before treating this as blocking; scope against the specific change, not an indefinite hold. + +## References + +- Design: `docs/plans/2026-06-11-ore-block-comparator-n-blocks-design.md` +- Comparator: `src/v3/sem/ore_block_256/functions.sql` +- Tests: `tests/sqlx/tests/ore_block_comparator_tests.rs` +- ore-rs 0.8.3: https://crates.io/crates/ore-rs/0.8.3 (no `repository` field; docs.rs source is authoritative) +- cipherstash-client 0.35.0: https://crates.io/crates/cipherstash-client/0.35.0 · repo declared: https://github.com/cipherstash/cipherstash-suite diff --git a/docs/reference/adding-a-scalar-encrypted-domain-type.md b/docs/reference/adding-a-scalar-encrypted-domain-type.md index 6c760f4f..8c0bdf6e 100644 --- a/docs/reference/adding-a-scalar-encrypted-domain-type.md +++ b/docs/reference/adding-a-scalar-encrypted-domain-type.md @@ -13,7 +13,7 @@ A scalar encrypted-domain type is a family of concrete `jsonb` domains in the an `eql_v2` uninstall. Their extractors, comparison wrappers, and MIN/MAX aggregates also live in `eql_v3`; the searchable-encrypted-metadata (SEM) index-term types they return (`eql_v3.hmac_256`, -`eql_v3.ore_block_u64_8_256`) are **also `eql_v3`** — hand-written under +`eql_v3.ore_block_256`) are **also `eql_v3`** — hand-written under `src/v3/sem/`. The whole v3 surface is self-contained: it owns every type it needs and has no runtime dependency on `eql_v2` (CI gates this — see §6). @@ -124,7 +124,7 @@ behaviour change, not a refactor: | Term | JSON key | Extractor | Returns | Operators | | ------- | -------- | ------------ | -------------------------------- | -------------------------- | | `Hm` | `hm` | `eq_term` | `eql_v3.hmac_256` | `=` `<>` | -| `Ore` | `ob` | `ord_term` | `eql_v3.ore_block_u64_8_256` | `=` `<>` `<` `<=` `>` `>=` | +| `Ore` | `ob` | `ord_term` | `eql_v3.ore_block_256` | `=` `<>` `<` `<=` `>` `>=` | | `Bloom` | `bf` | `match_term` | `eql_v3.bloom_filter` | `@>` `<@` | A type that needs a non-ORE equality term on an ordered domain needs a **new @@ -204,14 +204,16 @@ there is no `_VALUES` const; the SQLx harness parses the catalog strings into A **temporal** scalar (`date` is the *ordered* temporal reference) is *ordered but non-integer*, so it diverges from the integer path in three places — all in the catalog/harness, never the SQL codegen (domains stay jsonb-backed and -token-driven). **`timestamptz` is the exception: it is equality-only, not -ordered** — its catalog row uses `EQ_ONLY_DOMAINS` (storage + `_eq`, no -`_ord`/`_ord_ore`), the eq-only shape of §3, because cipherstash encrypts -`Plaintext::Timestamp` at native 12-block ORE width while EQL's only comparator -(`eql_v2.compare_ore_block_u64_8_256_term`) is hardcoded to 8 blocks, so an -ordered `timestamptz` domain would silently mis-order (see the catalog comment on -the `TIMESTAMPTZ` spec). Its value-wiring is still the temporal path below; only -its domain set differs. The three divergences (for the ordered `date`): +token-driven). **`timestamptz` follows the same *ordered* temporal path as +`date`** — its catalog row carries the full ordered domain set (storage + `_eq` + +`_ord`/`_ord_ore`). cipherstash encrypts `Plaintext::Timestamp` at native +12-block ORE width, and the `eql_v3` comparator +(`eql_v3.compare_ore_block_256_term`) now derives its block count `N` from the +term length instead of assuming 8, so the 12-block ciphertexts order correctly +(see the N-block ORE comparator entry in the `CHANGELOG.md` and the catalog +comment on the `TIMESTAMPTZ` spec). Its value-wiring is the temporal path below; +the only practical difference from `date` is that values are UTC-normalized. The +three divergences (for the ordered `date`): - **String-backed fixtures.** `eql-scalars` stays zero-dependency, so the catalog stores ISO strings (`Fixture::Date("1970-01-01")`), not `chrono` @@ -326,6 +328,42 @@ first added) also needs, in `tests/sqlx/src/fixtures/eql_plaintext.rs`: `Plaintext::*` variant (`Plaintext::NaiveDate` / `Plaintext::Timestamp` / `Plaintext::Text`), plus the three mirrored `#[test]`s. +#### A fourth fixture shape: non-integer, non-chrono, non-text (`numeric` / `Decimal`) + +`numeric` (backed by `rust_decimal::Decimal`, 14-block ORE — the first scalar +whose ORE term is wider than 8 blocks) is ordered but is **neither** the integer +materialiser, **nor** chrono (`temporal`), **nor** `text` (it owns a `Decimal`, +not a `String`, and has no `Match` index). It therefore introduces a **fourth +fixture discriminator**, which means touching the proc-macro routing, not just +the type list. Beyond the §3.1 `eql_plaintext.rs` wiring above (`Cast::DECIMAL`, +`PlaintextSqlType::NUMERIC`, the `cast_for_kind` / `plaintext_sql_type_for_kind` +arms, `Sealed for Decimal`, `EqlPlaintext for Decimal` → `Plaintext::Decimal`), +it also needs: + +- an **`is_numeric_token`** arm in `crates/eql-tests-macros/src/lib.rs`'s + fixture-module router — without it `scalar_types!(fixture_modules)` panics at + compile time on the unrecognised kind (the router handled only `temporal` / + `text` before); +- a **`numeric` arm** in the `scalar_fixture!` macro + (`tests/sqlx/src/fixtures/scalar_fixture.rs`) — the temporal arm's twin + (`[Unique, Ore]`, pivot-presence asserts via `OrderedScalar`), but no `Match` + and no chrono; +- a hand-written **`numeric_values()`** accessor plus `impl ScalarType` / + `OrderedScalar for Decimal` in `tests/sqlx/src/scalar_domains.rs` — parsing the + catalog's `Fixture::Numeric` strings into a `LazyLock>` (the + catalog stays zero-dep; the parse lives in the harness). `Decimal: Ord` supplies + the expected sort order — `ore-rs` guarantees the ciphertext order agrees, and + equivalent scales (`1` ≡ `1.0`) collide like `Decimal`'s own `Ord`. Add a + **`fixtures_are_distinct_by_value`** guard (parse → `HashSet`): the zero-dep + catalog only dedupes by literal string, so `"1"` / `"1.0"` would slip past it + but collide in both the ORE ciphertext and the fixture table; +- the **`rust_decimal` dependency** + the sqlx **`rust_decimal` feature** in + `tests/sqlx/Cargo.toml` (in `[dependencies]`, not `[dev-dependencies]` — the + `Decimal` impls live in the crate's library code). + +See the N-block ORE comparator entry in the `CHANGELOG.md` for the comparator +change the wide `numeric` / `timestamptz` terms rely on. + ### New-capability domains (e.g. `_match` / `Bloom`) A domain carrying a capability the matrix does not model — `text`'s `_match` @@ -558,8 +596,8 @@ CREATE INDEX ... ON table_name USING btree (eql_v3.ord_term(col)); CREATE INDEX ... ON table_name USING hash (eql_v3.eq_term(col)); ``` -`ore` depends on `src/v3/sem/ore_block_u64_8_256/functions.sql` and -`src/v3/sem/ore_block_u64_8_256/operators.sql`; `hm` depends on +`ore` depends on `src/v3/sem/ore_block_256/functions.sql` and +`src/v3/sem/ore_block_256/operators.sql`; `hm` depends on `src/v3/sem/hmac_256/functions.sql`. ### Extension files @@ -623,7 +661,7 @@ edits: extractor names (`eq`, `neq`, `lt`, `lte`, `gt`, `gte`, `eq_term`, `ord_term`, the `Bloom` term's `match_term` extractor and its `contains` / `contained_by` containment wrappers) plus the generated `min` / `max` aggregates and the SEM - `hmac_256` / `ore_block_u64_8_256` / `bloom_filter` constructors are already + `hmac_256` / `ore_block_256` / `bloom_filter` constructors are already covered by `eql_v3`-schema entries. A new scalar type inherits coverage; **a new term needs splinter entries for each new name it introduces — both its extractor and its comparison wrappers** (adding `Bloom` required `match_term`, @@ -665,7 +703,7 @@ recognises exactly these two forms; any other argument is a usage error. The generator targets the `eql_v3` schema throughout: `SCHEMA = "eql_v3"` (`crates/eql-codegen/src/consts.rs`) qualifies both the domain families and the SEM index-term types the extractors return (`eql_v3.hmac_256`, -`eql_v3.ore_block_u64_8_256`), so no generated SQL references `eql_v2`. +`eql_v3.ore_block_256`), so no generated SQL references `eql_v2`. `tasks/build.sh` runs `cargo run -p eql-codegen` at the start of every `mise run build`, so the generated SQL is never checked in. (The build first sweeps every diff --git a/docs/reference/eql-functions.md b/docs/reference/eql-functions.md index b610349c..91c4013f 100644 --- a/docs/reference/eql-functions.md +++ b/docs/reference/eql-functions.md @@ -435,8 +435,8 @@ SEM index-term types. ```sql -- int4 — generated for every scalar type's eq / ord variants. eql_v3.eq_term(a eql_v3.int4_eq) RETURNS eql_v3.hmac_256 -eql_v3.ord_term(a eql_v3.int4_ord) RETURNS eql_v3.ore_block_u64_8_256 -eql_v3.ord_term(a eql_v3.int4_ord_ore) RETURNS eql_v3.ore_block_u64_8_256 +eql_v3.ord_term(a eql_v3.int4_ord) RETURNS eql_v3.ore_block_256 +eql_v3.ord_term(a eql_v3.int4_ord_ore) RETURNS eql_v3.ore_block_256 ``` **Example:** diff --git a/docs/reference/sql-support.md b/docs/reference/sql-support.md index 82770ad9..9cd7e544 100644 --- a/docs/reference/sql-support.md +++ b/docs/reference/sql-support.md @@ -61,7 +61,7 @@ Use the equivalent [`jsonb_path_query`](#jsonb-functions-and-selectors-enabled-b ## Encrypted-domain scalar types (`eql_v3.`) -Scalar encrypted-domain types (e.g. `eql_v3.int4`; see [Adding a Scalar Encrypted-Domain Type](./adding-a-scalar-encrypted-domain-type.md)) are a different access model from the matrix above. Instead of configuring a search index on an `eql_v2_encrypted` column, you type the column as a specific domain *variant* whose operator surface is fixed at generation time. The index terms travel in the payload; there is no `add_search_config` step. The domains and their operator surface live in the `eql_v3` schema (dropped by `DROP SCHEMA eql_v3 CASCADE`, and they survive an `eql_v2` uninstall); their extracted index-term types are the self-contained `eql_v3` SEM types (`eql_v3.hmac_256`, `eql_v3.ore_block_u64_8_256`). +Scalar encrypted-domain types (e.g. `eql_v3.int4`; see [Adding a Scalar Encrypted-Domain Type](./adding-a-scalar-encrypted-domain-type.md)) are a different access model from the matrix above. Instead of configuring a search index on an `eql_v2_encrypted` column, you type the column as a specific domain *variant* whose operator surface is fixed at generation time. The index terms travel in the payload; there is no `add_search_config` step. The domains and their operator surface live in the `eql_v3` schema (dropped by `DROP SCHEMA eql_v3 CASCADE`, and they survive an `eql_v2` uninstall); their extracted index-term types are the self-contained `eql_v3` SEM types (`eql_v3.hmac_256`, `eql_v3.ore_block_256`). Each scalar type `` generates one storage-only variant plus eq/ord query variants: diff --git a/src/v3/schema.sql b/src/v3/schema.sql index 41be4d40..7ec2f51f 100644 --- a/src/v3/schema.sql +++ b/src/v3/schema.sql @@ -4,7 +4,7 @@ --! Creates the eql_v3 schema, which houses the self-contained encrypted-domain --! type families (eql_v3.int4, eql_v3.int8, and future scalar domains): their --! jsonb-backed domains, the searchable-encrypted-metadata (SEM) index-term ---! types they use (eql_v3.hmac_256, eql_v3.ore_block_u64_8_256), the index-term +--! types they use (eql_v3.hmac_256, eql_v3.ore_block_256), the index-term --! extractors, comparison wrappers, blockers, and aggregates. The v3 surface is --! self-contained — it owns every type it needs and has no runtime dependency --! on another EQL schema. diff --git a/src/v3/sem/bloom_filter/functions.sql b/src/v3/sem/bloom_filter/functions.sql index 291ba736..d4c782c5 100644 --- a/src/v3/sem/bloom_filter/functions.sql +++ b/src/v3/sem/bloom_filter/functions.sql @@ -16,7 +16,7 @@ --! @return boolean True when the `bf` key is present and non-null. --! --! @internal Defined for parity with the eql_v3 SEM index-term predicates ---! (`has_hmac_256` / `has_ore_block_u64_8_256`); it is not currently called by +--! (`has_hmac_256` / `has_ore_block_256`); it is not currently called by --! the extractor below, which gates on value-shape inline, nor by the generated --! domain CHECK, which tests `bf` presence via the envelope-key skeleton. Kept --! as the canonical presence test for callers that need one. diff --git a/src/v3/sem/ore_block_u64_8_256/functions.sql b/src/v3/sem/ore_block_256/functions.sql similarity index 54% rename from src/v3/sem/ore_block_u64_8_256/functions.sql rename to src/v3/sem/ore_block_256/functions.sql index 2a13ef31..0cd323d8 100644 --- a/src/v3/sem/ore_block_u64_8_256/functions.sql +++ b/src/v3/sem/ore_block_256/functions.sql @@ -1,9 +1,9 @@ -- REQUIRE: src/v3/schema.sql -- REQUIRE: src/v3/crypto.sql -- REQUIRE: src/v3/common.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/types.sql +-- REQUIRE: src/v3/sem/ore_block_256/types.sql ---! @file v3/sem/ore_block_u64_8_256/functions.sql +--! @file v3/sem/ore_block_256/functions.sql --! @brief ORE block construction, extraction, and comparison (eql_v3 SEM). --! --! jsonb-only subset of src/ore_block_u64_8_256/functions.sql. The @@ -16,24 +16,25 @@ --! @brief Convert JSONB array to ORE block composite type --! @internal --! @param val jsonb Array of hex-encoded ORE block terms ---! @return eql_v3.ore_block_u64_8_256 ORE block composite, or NULL if input is null +--! @return eql_v3.ore_block_256 ORE block composite, or NULL if input is null --! @note Inlinable `LANGUAGE sql` IMMUTABLE form (no `SET search_path`) so the --! planner can fold this per-encrypted-value helper into the calling query. --! This deliberately diverges from the v2 plpgsql equivalent (intentionally --! left unchanged): the `CASE WHEN jsonb_typeof(val) = 'array'` guard only --! evaluates the array path for an array, so a non-array JSON scalar returns ---! NULL here instead of raising. The sole caller passes `val->'ob'`, always an ---! array or JSON null, so the divergence is unreachable in practice; JSON null ---! and empty array still return NULL exactly as before. -CREATE FUNCTION eql_v3.jsonb_array_to_ore_block_u64_8_256(val jsonb) -RETURNS eql_v3.ore_block_u64_8_256 +--! NULL here instead of raising. The sole caller (`ore_block_256`) only reaches +--! this when `has_ore_block_256(val)` is true, which now requires `val->'ob'` +--! to be a JSON array, so the non-array branch is unreachable in practice; +--! empty array still returns NULL exactly as before (pinned by T7). +CREATE FUNCTION eql_v3.jsonb_array_to_ore_block_256(val jsonb) +RETURNS eql_v3.ore_block_256 IMMUTABLE AS $$ SELECT CASE WHEN jsonb_typeof(val) = 'array' THEN ROW(( - SELECT array_agg(ROW(b)::eql_v3.ore_block_u64_8_256_term) + SELECT array_agg(ROW(b)::eql_v3.ore_block_256_term) FROM unnest(eql_v3.jsonb_array_to_bytea_array(val)) AS b - ))::eql_v3.ore_block_u64_8_256 + ))::eql_v3.ore_block_256 ELSE NULL END; $$ LANGUAGE sql; @@ -43,58 +44,66 @@ $$ LANGUAGE sql; --! SQL-function inlining. It takes a bare `jsonb` arg (not a jsonb-backed --! encrypted DOMAIN), so the structural skip in tasks/pin_search_path.sql does --! not recognise it; this marker is the documented manual opt-in. -COMMENT ON FUNCTION eql_v3.jsonb_array_to_ore_block_u64_8_256(jsonb) IS +COMMENT ON FUNCTION eql_v3.jsonb_array_to_ore_block_256(jsonb) IS 'eql-inline-critical: per-encrypted-value ORE helper; must stay inlinable (unpinned search_path)'; --! @brief Extract ORE block index term from JSONB payload --! @param val jsonb containing encrypted EQL payload ---! @return eql_v3.ore_block_u64_8_256 ORE block index term +--! @return eql_v3.ore_block_256 ORE block index term --! @throws Exception if 'ob' field is missing -CREATE FUNCTION eql_v3.ore_block_u64_8_256(val jsonb) - RETURNS eql_v3.ore_block_u64_8_256 +CREATE FUNCTION eql_v3.ore_block_256(val jsonb) + RETURNS eql_v3.ore_block_256 IMMUTABLE STRICT PARALLEL SAFE SET search_path = pg_catalog, extensions, public AS $$ BEGIN -- Declared STRICT: PostgreSQL returns NULL for a NULL argument without -- entering the body, so no explicit `val IS NULL` guard is needed. - IF eql_v3.has_ore_block_u64_8_256(val) THEN - RETURN eql_v3.jsonb_array_to_ore_block_u64_8_256(val->'ob'); + IF eql_v3.has_ore_block_256(val) THEN + RETURN eql_v3.jsonb_array_to_ore_block_256(val->'ob'); END IF; RAISE 'Expected an ore index (ob) value in json: %', val; END; $$ LANGUAGE plpgsql; ---! @brief Check if JSONB payload contains ORE block index term +--! @brief Check if JSONB payload contains an ORE block index term --! @param val jsonb containing encrypted EQL payload ---! @return boolean True if 'ob' field is present and non-null -CREATE FUNCTION eql_v3.has_ore_block_u64_8_256(val jsonb) +--! @return boolean True only if the 'ob' field is present and is a JSON array +--! @note A well-formed ORE index term is always a JSON array of block terms, so +--! this guard treats a present-but-non-array `ob` (a scalar or object) as +--! absent. That makes the extractor `ore_block_256(val)` RAISE on a +--! structurally invalid `ob` payload at the boundary instead of silently +--! degrading it to a NULL index term in `jsonb_array_to_ore_block_256`. The +--! previous `val ->> 'ob' IS NOT NULL` form stringified scalars/objects and so +--! reported them as present. `{}` (absent `ob`) and `{"ob": null}` (JSON null) +--! both remain `false`. +CREATE FUNCTION eql_v3.has_ore_block_256(val jsonb) RETURNS boolean IMMUTABLE STRICT PARALLEL SAFE SET search_path = pg_catalog, extensions, public AS $$ BEGIN - RETURN val ->> 'ob' IS NOT NULL; + RETURN COALESCE(jsonb_typeof(val -> 'ob') = 'array', false); END; $$ LANGUAGE plpgsql; --! @brief Compare two ORE block terms using cryptographic comparison --! @internal ---! @param a eql_v3.ore_block_u64_8_256_term First ORE term ---! @param b eql_v3.ore_block_u64_8_256_term Second ORE term +--! @param a eql_v3.ore_block_256_term First ORE term +--! @param b eql_v3.ore_block_256_term Second ORE term --! @return integer -1 if a < b, 0 if a = b, 1 if a > b --! @throws Exception if ciphertexts are different lengths ---! @note Marked `IMMUTABLE` (the three `compare_ore_block_u64_8_256_term(s)` +--! @note Marked `IMMUTABLE` (the three `compare_ore_block_256_term(s)` --! overloads all are). This deliberately diverges from the v2 originals, --! which carry no volatility marker and so default to `VOLATILE`. The --! comparison is deterministic — its only crypto call, pgcrypto `encrypt()`, --! is itself `IMMUTABLE STRICT PARALLEL SAFE` — so `IMMUTABLE` lets the --! planner fold/cache these in ordering and index contexts. NOT `STRICT`: --! the NULL-handling branches below are load-bearing for the array overload. -CREATE FUNCTION eql_v3.compare_ore_block_u64_8_256_term(a eql_v3.ore_block_u64_8_256_term, b eql_v3.ore_block_u64_8_256_term) +CREATE FUNCTION eql_v3.compare_ore_block_256_term(a eql_v3.ore_block_256_term, b eql_v3.ore_block_256_term) RETURNS integer IMMUTABLE SET search_path = pg_catalog, extensions, public @@ -109,7 +118,16 @@ AS $$ left_block_size CONSTANT smallint := 16; right_block_size CONSTANT smallint := 32; - right_offset CONSTANT smallint := 136; -- 8 * 17 + + -- Block count N is DERIVED from the ciphertext length, not hardcoded to 8. + -- Wire format per term: + -- [ N PRP bytes ][ N*16B left blocks ][ 16B hash key ][ N*32B right blocks ] + -- octet_length = 17*N + 16 + 32*N = 49*N + 16 => N = (octet_length - 16) / 49 + -- This serves int4 (N=8, 408B), timestamp (N=12, 604B), and numeric + -- (N=14, 702B) with one comparator. + n integer; + left_offset integer; -- ordinal offset of the first left block (1 + N PRP bytes) + right_offset integer; -- ordinal start of the right CT (= total left CT length = 17*N) indicator smallint := 0; BEGIN @@ -129,10 +147,23 @@ AS $$ RAISE EXCEPTION 'Ciphertexts are different lengths'; END IF; - FOR block IN 0..7 LOOP + -- Well-formedness: length must be exactly 49*N + 16 for some N >= 1. The + -- modulo alone is insufficient -- a 16-byte term passes (16 - 16) % 49 = 0 + -- and derives N = 0, which would fall through to the all-blocks-equal path + -- and return 0 instead of raising. The `<= 16` clause is load-bearing. + IF octet_length(a.bytes) <= 16 OR (octet_length(a.bytes) - 16) % 49 != 0 THEN + RAISE EXCEPTION 'Malformed ORE term: % bytes', octet_length(a.bytes); + END IF; + + n := (octet_length(a.bytes) - 16) / 49; + left_offset := 1 + n; -- left blocks begin right after the N PRP bytes + right_offset := 17 * n; -- right CT begins right after the 17*N-byte left CT + + FOR block IN 0..n-1 LOOP + -- Compare each PRP byte (the first N bytes) and its 16-byte left block. IF substr(a.bytes, 1 + block, 1) != substr(b.bytes, 1 + block, 1) - OR substr(a.bytes, 9 + left_block_size * block, left_block_size) != substr(b.bytes, 9 + left_block_size * block, left_block_size) + OR substr(a.bytes, left_offset + left_block_size * block, left_block_size) != substr(b.bytes, left_offset + left_block_size * block, left_block_size) THEN IF eq THEN unequal_block := block; @@ -145,11 +176,13 @@ AS $$ RETURN 0::integer; END IF; + -- Hash key is the IV from the right CT of b. hash_key := substr(b.bytes, right_offset + 1, 16); + -- First right block is at right_offset + nonce_size (ordinally indexed). target_block := substr(b.bytes, right_offset + 17 + (unequal_block * right_block_size), right_block_size); - data_block := substr(a.bytes, 9 + (left_block_size * unequal_block), left_block_size); + data_block := substr(a.bytes, left_offset + (left_block_size * unequal_block), left_block_size); encrypt_block := encrypt(data_block::bytea, hash_key::bytea, 'aes-ecb'); @@ -170,10 +203,10 @@ $$ LANGUAGE plpgsql; --! @brief Compare arrays of ORE block terms recursively --! @internal ---! @param a eql_v3.ore_block_u64_8_256_term[] First array ---! @param b eql_v3.ore_block_u64_8_256_term[] Second array +--! @param a eql_v3.ore_block_256_term[] First array +--! @param b eql_v3.ore_block_256_term[] Second array --! @return integer -1/0/1, or NULL if either array is NULL -CREATE FUNCTION eql_v3.compare_ore_block_u64_8_256_terms(a eql_v3.ore_block_u64_8_256_term[], b eql_v3.ore_block_u64_8_256_term[]) +CREATE FUNCTION eql_v3.compare_ore_block_256_terms(a eql_v3.ore_block_256_term[], b eql_v3.ore_block_256_term[]) RETURNS integer IMMUTABLE SET search_path = pg_catalog, extensions, public @@ -197,10 +230,10 @@ AS $$ RETURN 1; END IF; - cmp_result := eql_v3.compare_ore_block_u64_8_256_term(a[1], b[1]); + cmp_result := eql_v3.compare_ore_block_256_term(a[1], b[1]); IF cmp_result = 0 THEN - RETURN eql_v3.compare_ore_block_u64_8_256_terms(a[2:array_length(a,1)], b[2:array_length(b,1)]); + RETURN eql_v3.compare_ore_block_256_terms(a[2:array_length(a,1)], b[2:array_length(b,1)]); END IF; RETURN cmp_result; @@ -210,15 +243,15 @@ $$ LANGUAGE plpgsql; --! @brief Compare ORE block composite types --! @internal ---! @param a eql_v3.ore_block_u64_8_256 First ORE block ---! @param b eql_v3.ore_block_u64_8_256 Second ORE block +--! @param a eql_v3.ore_block_256 First ORE block +--! @param b eql_v3.ore_block_256 Second ORE block --! @return integer -1/0/1 -CREATE FUNCTION eql_v3.compare_ore_block_u64_8_256_terms(a eql_v3.ore_block_u64_8_256, b eql_v3.ore_block_u64_8_256) +CREATE FUNCTION eql_v3.compare_ore_block_256_terms(a eql_v3.ore_block_256, b eql_v3.ore_block_256) RETURNS integer IMMUTABLE SET search_path = pg_catalog, extensions, public AS $$ BEGIN - RETURN eql_v3.compare_ore_block_u64_8_256_terms(a.terms, b.terms); + RETURN eql_v3.compare_ore_block_256_terms(a.terms, b.terms); END $$ LANGUAGE plpgsql; diff --git a/src/v3/sem/ore_block_u64_8_256/operator_class.sql b/src/v3/sem/ore_block_256/operator_class.sql similarity index 53% rename from src/v3/sem/ore_block_u64_8_256/operator_class.sql rename to src/v3/sem/ore_block_256/operator_class.sql index b367c8f6..04018d31 100644 --- a/src/v3/sem/ore_block_u64_8_256/operator_class.sql +++ b/src/v3/sem/ore_block_256/operator_class.sql @@ -1,9 +1,9 @@ -- REQUIRE: src/v3/schema.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/types.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/functions.sql +-- REQUIRE: src/v3/sem/ore_block_256/types.sql +-- REQUIRE: src/v3/sem/ore_block_256/functions.sql ---! @file v3/sem/ore_block_u64_8_256/operator_class.sql ---! @brief B-tree operator family + default class on eql_v3.ore_block_u64_8_256. +--! @file v3/sem/ore_block_256/operator_class.sql +--! @brief B-tree operator family + default class on eql_v3.ore_block_256. --! --! Gives the composite type its DEFAULT btree opclass so the recommended --! functional index `CREATE INDEX ON t (eql_v3.ord_term(col))` engages without @@ -11,16 +11,16 @@ --! variant by the `**/*operator_class.sql` glob. --! @brief B-tree operator family for ORE block types -CREATE OPERATOR FAMILY eql_v3.ore_block_u64_8_256_operator_family USING btree; +CREATE OPERATOR FAMILY eql_v3.ore_block_256_operator_family USING btree; --! @brief B-tree operator class for ORE block encrypted values --! --! Supports operators: <, <=, =, >=, >. Uses comparison function ---! compare_ore_block_u64_8_256_terms. -CREATE OPERATOR CLASS eql_v3.ore_block_u64_8_256_operator_class DEFAULT FOR TYPE eql_v3.ore_block_u64_8_256 USING btree FAMILY eql_v3.ore_block_u64_8_256_operator_family AS +--! compare_ore_block_256_terms. +CREATE OPERATOR CLASS eql_v3.ore_block_256_operator_class DEFAULT FOR TYPE eql_v3.ore_block_256 USING btree FAMILY eql_v3.ore_block_256_operator_family AS OPERATOR 1 <, OPERATOR 2 <=, OPERATOR 3 =, OPERATOR 4 >=, OPERATOR 5 >, - FUNCTION 1 eql_v3.compare_ore_block_u64_8_256_terms(a eql_v3.ore_block_u64_8_256, b eql_v3.ore_block_u64_8_256); + FUNCTION 1 eql_v3.compare_ore_block_256_terms(a eql_v3.ore_block_256, b eql_v3.ore_block_256); diff --git a/src/v3/sem/ore_block_256/operators.sql b/src/v3/sem/ore_block_256/operators.sql new file mode 100644 index 00000000..525ecc94 --- /dev/null +++ b/src/v3/sem/ore_block_256/operators.sql @@ -0,0 +1,180 @@ +-- REQUIRE: src/v3/schema.sql +-- REQUIRE: src/v3/sem/ore_block_256/types.sql +-- REQUIRE: src/v3/sem/ore_block_256/functions.sql + +--! @file v3/sem/ore_block_256/operators.sql +--! @brief Comparison operators on eql_v3.ore_block_256. +--! +--! The six backing functions are inlinable single-statement SQL so the planner +--! can fold the eql_v3 comparison wrappers through to functional-index matching. + +--! @brief Equality backing function for ORE block types +--! @internal +--! +--! @param a eql_v3.ore_block_256 Left operand +--! @param b eql_v3.ore_block_256 Right operand +--! @return boolean True if the ORE blocks are equal +--! +--! @see eql_v3.compare_ore_block_256_terms +CREATE FUNCTION eql_v3.ore_block_256_eq(a eql_v3.ore_block_256, b eql_v3.ore_block_256) +RETURNS boolean + LANGUAGE sql + IMMUTABLE STRICT PARALLEL SAFE +AS $$ + SELECT eql_v3.compare_ore_block_256_terms(a, b) = 0 +$$; + +--! @brief Not-equal backing function for ORE block types +--! @internal +--! +--! @param a eql_v3.ore_block_256 Left operand +--! @param b eql_v3.ore_block_256 Right operand +--! @return boolean True if the ORE blocks are not equal +--! +--! @see eql_v3.compare_ore_block_256_terms +CREATE FUNCTION eql_v3.ore_block_256_neq(a eql_v3.ore_block_256, b eql_v3.ore_block_256) +RETURNS boolean + LANGUAGE sql + IMMUTABLE STRICT PARALLEL SAFE +AS $$ + SELECT eql_v3.compare_ore_block_256_terms(a, b) <> 0 +$$; + +--! @brief Less-than backing function for ORE block types +--! @internal +--! +--! @param a eql_v3.ore_block_256 Left operand +--! @param b eql_v3.ore_block_256 Right operand +--! @return boolean True if the left operand is less than the right operand +--! +--! @see eql_v3.compare_ore_block_256_terms +CREATE FUNCTION eql_v3.ore_block_256_lt(a eql_v3.ore_block_256, b eql_v3.ore_block_256) +RETURNS boolean + LANGUAGE sql + IMMUTABLE STRICT PARALLEL SAFE +AS $$ + SELECT eql_v3.compare_ore_block_256_terms(a, b) = -1 +$$; + +--! @brief Less-than-or-equal backing function for ORE block types +--! @internal +--! +--! @param a eql_v3.ore_block_256 Left operand +--! @param b eql_v3.ore_block_256 Right operand +--! @return boolean True if the left operand is less than or equal to the right operand +--! +--! @see eql_v3.compare_ore_block_256_terms +CREATE FUNCTION eql_v3.ore_block_256_lte(a eql_v3.ore_block_256, b eql_v3.ore_block_256) +RETURNS boolean + LANGUAGE sql + IMMUTABLE STRICT PARALLEL SAFE +AS $$ + SELECT eql_v3.compare_ore_block_256_terms(a, b) != 1 +$$; + +--! @brief Greater-than backing function for ORE block types +--! @internal +--! +--! @param a eql_v3.ore_block_256 Left operand +--! @param b eql_v3.ore_block_256 Right operand +--! @return boolean True if the left operand is greater than the right operand +--! +--! @see eql_v3.compare_ore_block_256_terms +CREATE FUNCTION eql_v3.ore_block_256_gt(a eql_v3.ore_block_256, b eql_v3.ore_block_256) +RETURNS boolean + LANGUAGE sql + IMMUTABLE STRICT PARALLEL SAFE +AS $$ + SELECT eql_v3.compare_ore_block_256_terms(a, b) = 1 +$$; + +--! @brief Greater-than-or-equal backing function for ORE block types +--! @internal +--! +--! @param a eql_v3.ore_block_256 Left operand +--! @param b eql_v3.ore_block_256 Right operand +--! @return boolean True if the left operand is greater than or equal to the right operand +--! +--! @see eql_v3.compare_ore_block_256_terms +CREATE FUNCTION eql_v3.ore_block_256_gte(a eql_v3.ore_block_256, b eql_v3.ore_block_256) +RETURNS boolean + LANGUAGE sql + IMMUTABLE STRICT PARALLEL SAFE +AS $$ + SELECT eql_v3.compare_ore_block_256_terms(a, b) != -1 +$$; + + +--! @brief = operator for ORE block types +--! +--! COMMUTATOR is the operator itself: equality is symmetric. Required for the +--! MERGES flag — without it the planner raises "could not find commutator" the +--! first time an ore_block equality is used as a join qual (e.g. via the inlined +--! eql_v3._ord_ore equality wrappers). +CREATE OPERATOR = ( + FUNCTION=eql_v3.ore_block_256_eq, + LEFTARG=eql_v3.ore_block_256, + RIGHTARG=eql_v3.ore_block_256, + COMMUTATOR = =, + NEGATOR = <>, + RESTRICT = eqsel, + JOIN = eqjoinsel, + HASHES, + MERGES +); + +--! @brief <> operator for ORE block types +CREATE OPERATOR <> ( + FUNCTION=eql_v3.ore_block_256_neq, + LEFTARG=eql_v3.ore_block_256, + RIGHTARG=eql_v3.ore_block_256, + COMMUTATOR = <>, + NEGATOR = =, + RESTRICT = neqsel, + JOIN = neqjoinsel, + MERGES +); + +--! @brief > operator for ORE block types +CREATE OPERATOR > ( + FUNCTION=eql_v3.ore_block_256_gt, + LEFTARG=eql_v3.ore_block_256, + RIGHTARG=eql_v3.ore_block_256, + COMMUTATOR = <, + NEGATOR = <=, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +--! @brief < operator for ORE block types +CREATE OPERATOR < ( + FUNCTION=eql_v3.ore_block_256_lt, + LEFTARG=eql_v3.ore_block_256, + RIGHTARG=eql_v3.ore_block_256, + COMMUTATOR = >, + NEGATOR = >=, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +--! @brief <= operator for ORE block types +CREATE OPERATOR <= ( + FUNCTION=eql_v3.ore_block_256_lte, + LEFTARG=eql_v3.ore_block_256, + RIGHTARG=eql_v3.ore_block_256, + COMMUTATOR = >=, + NEGATOR = >, + RESTRICT = scalarlesel, + JOIN = scalarlejoinsel +); + +--! @brief >= operator for ORE block types +CREATE OPERATOR >= ( + FUNCTION=eql_v3.ore_block_256_gte, + LEFTARG=eql_v3.ore_block_256, + RIGHTARG=eql_v3.ore_block_256, + COMMUTATOR = <=, + NEGATOR = <, + RESTRICT = scalargesel, + JOIN = scalargejoinsel +); diff --git a/src/v3/sem/ore_block_u64_8_256/types.sql b/src/v3/sem/ore_block_256/types.sql similarity index 79% rename from src/v3/sem/ore_block_u64_8_256/types.sql rename to src/v3/sem/ore_block_256/types.sql index f7e44dd0..b77c5733 100644 --- a/src/v3/sem/ore_block_u64_8_256/types.sql +++ b/src/v3/sem/ore_block_256/types.sql @@ -1,6 +1,6 @@ -- REQUIRE: src/v3/schema.sql ---! @file v3/sem/ore_block_u64_8_256/types.sql +--! @file v3/sem/ore_block_256/types.sql --! @brief ORE block index-term types (eql_v3 SEM). --! --! Self-contained eql_v3 copies of the Order-Revealing Encryption block types @@ -10,7 +10,7 @@ --! --! Composite type representing a single ORE block term. Stores encrypted data --! as bytea that enables range comparisons without decryption. -CREATE TYPE eql_v3.ore_block_u64_8_256_term AS ( +CREATE TYPE eql_v3.ore_block_256_term AS ( bytes bytea ); @@ -21,6 +21,6 @@ CREATE TYPE eql_v3.ore_block_u64_8_256_term AS ( --! in the 'ob' field of encrypted data payloads. --! --! @note Transient type used only during query execution. -CREATE TYPE eql_v3.ore_block_u64_8_256 AS ( - terms eql_v3.ore_block_u64_8_256_term[] +CREATE TYPE eql_v3.ore_block_256 AS ( + terms eql_v3.ore_block_256_term[] ); diff --git a/src/v3/sem/ore_block_u64_8_256/operators.sql b/src/v3/sem/ore_block_u64_8_256/operators.sql deleted file mode 100644 index d2e670b7..00000000 --- a/src/v3/sem/ore_block_u64_8_256/operators.sql +++ /dev/null @@ -1,180 +0,0 @@ --- REQUIRE: src/v3/schema.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/types.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/functions.sql - ---! @file v3/sem/ore_block_u64_8_256/operators.sql ---! @brief Comparison operators on eql_v3.ore_block_u64_8_256. ---! ---! The six backing functions are inlinable single-statement SQL so the planner ---! can fold the eql_v3 comparison wrappers through to functional-index matching. - ---! @brief Equality backing function for ORE block types ---! @internal ---! ---! @param a eql_v3.ore_block_u64_8_256 Left operand ---! @param b eql_v3.ore_block_u64_8_256 Right operand ---! @return boolean True if the ORE blocks are equal ---! ---! @see eql_v3.compare_ore_block_u64_8_256_terms -CREATE FUNCTION eql_v3.ore_block_u64_8_256_eq(a eql_v3.ore_block_u64_8_256, b eql_v3.ore_block_u64_8_256) -RETURNS boolean - LANGUAGE sql - IMMUTABLE STRICT PARALLEL SAFE -AS $$ - SELECT eql_v3.compare_ore_block_u64_8_256_terms(a, b) = 0 -$$; - ---! @brief Not-equal backing function for ORE block types ---! @internal ---! ---! @param a eql_v3.ore_block_u64_8_256 Left operand ---! @param b eql_v3.ore_block_u64_8_256 Right operand ---! @return boolean True if the ORE blocks are not equal ---! ---! @see eql_v3.compare_ore_block_u64_8_256_terms -CREATE FUNCTION eql_v3.ore_block_u64_8_256_neq(a eql_v3.ore_block_u64_8_256, b eql_v3.ore_block_u64_8_256) -RETURNS boolean - LANGUAGE sql - IMMUTABLE STRICT PARALLEL SAFE -AS $$ - SELECT eql_v3.compare_ore_block_u64_8_256_terms(a, b) <> 0 -$$; - ---! @brief Less-than backing function for ORE block types ---! @internal ---! ---! @param a eql_v3.ore_block_u64_8_256 Left operand ---! @param b eql_v3.ore_block_u64_8_256 Right operand ---! @return boolean True if the left operand is less than the right operand ---! ---! @see eql_v3.compare_ore_block_u64_8_256_terms -CREATE FUNCTION eql_v3.ore_block_u64_8_256_lt(a eql_v3.ore_block_u64_8_256, b eql_v3.ore_block_u64_8_256) -RETURNS boolean - LANGUAGE sql - IMMUTABLE STRICT PARALLEL SAFE -AS $$ - SELECT eql_v3.compare_ore_block_u64_8_256_terms(a, b) = -1 -$$; - ---! @brief Less-than-or-equal backing function for ORE block types ---! @internal ---! ---! @param a eql_v3.ore_block_u64_8_256 Left operand ---! @param b eql_v3.ore_block_u64_8_256 Right operand ---! @return boolean True if the left operand is less than or equal to the right operand ---! ---! @see eql_v3.compare_ore_block_u64_8_256_terms -CREATE FUNCTION eql_v3.ore_block_u64_8_256_lte(a eql_v3.ore_block_u64_8_256, b eql_v3.ore_block_u64_8_256) -RETURNS boolean - LANGUAGE sql - IMMUTABLE STRICT PARALLEL SAFE -AS $$ - SELECT eql_v3.compare_ore_block_u64_8_256_terms(a, b) != 1 -$$; - ---! @brief Greater-than backing function for ORE block types ---! @internal ---! ---! @param a eql_v3.ore_block_u64_8_256 Left operand ---! @param b eql_v3.ore_block_u64_8_256 Right operand ---! @return boolean True if the left operand is greater than the right operand ---! ---! @see eql_v3.compare_ore_block_u64_8_256_terms -CREATE FUNCTION eql_v3.ore_block_u64_8_256_gt(a eql_v3.ore_block_u64_8_256, b eql_v3.ore_block_u64_8_256) -RETURNS boolean - LANGUAGE sql - IMMUTABLE STRICT PARALLEL SAFE -AS $$ - SELECT eql_v3.compare_ore_block_u64_8_256_terms(a, b) = 1 -$$; - ---! @brief Greater-than-or-equal backing function for ORE block types ---! @internal ---! ---! @param a eql_v3.ore_block_u64_8_256 Left operand ---! @param b eql_v3.ore_block_u64_8_256 Right operand ---! @return boolean True if the left operand is greater than or equal to the right operand ---! ---! @see eql_v3.compare_ore_block_u64_8_256_terms -CREATE FUNCTION eql_v3.ore_block_u64_8_256_gte(a eql_v3.ore_block_u64_8_256, b eql_v3.ore_block_u64_8_256) -RETURNS boolean - LANGUAGE sql - IMMUTABLE STRICT PARALLEL SAFE -AS $$ - SELECT eql_v3.compare_ore_block_u64_8_256_terms(a, b) != -1 -$$; - - ---! @brief = operator for ORE block types ---! ---! COMMUTATOR is the operator itself: equality is symmetric. Required for the ---! MERGES flag — without it the planner raises "could not find commutator" the ---! first time an ore_block equality is used as a join qual (e.g. via the inlined ---! eql_v3._ord_ore equality wrappers). -CREATE OPERATOR = ( - FUNCTION=eql_v3.ore_block_u64_8_256_eq, - LEFTARG=eql_v3.ore_block_u64_8_256, - RIGHTARG=eql_v3.ore_block_u64_8_256, - COMMUTATOR = =, - NEGATOR = <>, - RESTRICT = eqsel, - JOIN = eqjoinsel, - HASHES, - MERGES -); - ---! @brief <> operator for ORE block types -CREATE OPERATOR <> ( - FUNCTION=eql_v3.ore_block_u64_8_256_neq, - LEFTARG=eql_v3.ore_block_u64_8_256, - RIGHTARG=eql_v3.ore_block_u64_8_256, - COMMUTATOR = <>, - NEGATOR = =, - RESTRICT = neqsel, - JOIN = neqjoinsel, - MERGES -); - ---! @brief > operator for ORE block types -CREATE OPERATOR > ( - FUNCTION=eql_v3.ore_block_u64_8_256_gt, - LEFTARG=eql_v3.ore_block_u64_8_256, - RIGHTARG=eql_v3.ore_block_u64_8_256, - COMMUTATOR = <, - NEGATOR = <=, - RESTRICT = scalargtsel, - JOIN = scalargtjoinsel -); - ---! @brief < operator for ORE block types -CREATE OPERATOR < ( - FUNCTION=eql_v3.ore_block_u64_8_256_lt, - LEFTARG=eql_v3.ore_block_u64_8_256, - RIGHTARG=eql_v3.ore_block_u64_8_256, - COMMUTATOR = >, - NEGATOR = >=, - RESTRICT = scalarltsel, - JOIN = scalarltjoinsel -); - ---! @brief <= operator for ORE block types -CREATE OPERATOR <= ( - FUNCTION=eql_v3.ore_block_u64_8_256_lte, - LEFTARG=eql_v3.ore_block_u64_8_256, - RIGHTARG=eql_v3.ore_block_u64_8_256, - COMMUTATOR = >=, - NEGATOR = >, - RESTRICT = scalarlesel, - JOIN = scalarlejoinsel -); - ---! @brief >= operator for ORE block types -CREATE OPERATOR >= ( - FUNCTION=eql_v3.ore_block_u64_8_256_gte, - LEFTARG=eql_v3.ore_block_u64_8_256, - RIGHTARG=eql_v3.ore_block_u64_8_256, - COMMUTATOR = <=, - NEGATOR = <, - RESTRICT = scalargesel, - JOIN = scalargejoinsel -); diff --git a/tasks/pin_search_path.sql b/tasks/pin_search_path.sql index d00e3426..bfbd702c 100644 --- a/tasks/pin_search_path.sql +++ b/tasks/pin_search_path.sql @@ -259,13 +259,13 @@ BEGIN n.nspname = 'eql_v3' AND ( (p.pronargs = 2 - AND p.proname IN ('ore_block_u64_8_256_eq', 'ore_block_u64_8_256_neq', - 'ore_block_u64_8_256_lt', 'ore_block_u64_8_256_lte', - 'ore_block_u64_8_256_gt', 'ore_block_u64_8_256_gte')) + AND p.proname IN ('ore_block_256_eq', 'ore_block_256_neq', + 'ore_block_256_lt', 'ore_block_256_lte', + 'ore_block_256_gt', 'ore_block_256_gte')) -- Inner ORE-CLLW comparison helpers backing the `<`, `<=`, `=`, `>=`, -- `>`, `<>` operators on the eql_v3.ore_cllw composite type (registered -- via the DEFAULT eql_v3.ore_cllw_ops btree opclass). Same precedent as - -- the ore_block_u64_8_256_* helpers above and the eql_v2.ore_cllw_* + -- the ore_block_256_* helpers above and the eql_v2.ore_cllw_* -- helpers: PG only carries the inlined operator wrapper through to -- functional-index match if the inner backing function is also -- inlinable. They take the composite arg (not a jsonb-backed domain), diff --git a/tasks/test/clean_install_v3.sh b/tasks/test/clean_install_v3.sh index cb9e9d02..07553f67 100755 --- a/tasks/test/clean_install_v3.sh +++ b/tasks/test/clean_install_v3.sh @@ -36,7 +36,7 @@ echo "==> smoke: domains, SEM types, extractors, opclass functional index (D4)" -- Domains and SEM types exist in eql_v3. SELECT 'eql_v3.int4_ord'::regtype; SELECT 'eql_v3.hmac_256'::regtype; -SELECT 'eql_v3.ore_block_u64_8_256'::regtype; +SELECT 'eql_v3.ore_block_256'::regtype; -- A real ordered-domain column + the documented functional index. This is the -- D4 proof: it fails outright if the ported operator_class is absent. diff --git a/tasks/test/splinter.sh b/tasks/test/splinter.sh index fcf53e8c..97383be0 100755 --- a/tasks/test/splinter.sh +++ b/tasks/test/splinter.sh @@ -108,7 +108,7 @@ function_search_path_mutable eql_v2 grouped_value function Aggregate: same as mi # they need their own rows. The plpgsql blockers are pinned by # tasks/pin_search_path.sql and do not surface here. function_search_path_mutable eql_v3 eq_term function HMAC equality term extractor for the eql_v3 *_eq domains: returns eql_v3.hmac_256. Must inline so `eql_v3.eq_term(col)` folds into the calling query and matches the functional hash/btree index built on the same expression. SET search_path would disable SQL function inlining (see PostgreSQL inline_function). -function_search_path_mutable eql_v3 ord_term function ORE-block order term extractor for the eql_v3 ordered domains: returns eql_v3.ore_block_u64_8_256 (carrying the main DEFAULT btree opclass). Used inside the inlinable comparison wrappers and as the functional-index expression USING btree (eql_v3.ord_term(col)); must inline. Covers both ord_term overloads (eql_v3.int4_ord, eql_v3.int4_ord_ore). +function_search_path_mutable eql_v3 ord_term function ORE-block order term extractor for the eql_v3 ordered domains: returns eql_v3.ore_block_256 (carrying the main DEFAULT btree opclass). Used inside the inlinable comparison wrappers and as the functional-index expression USING btree (eql_v3.ord_term(col)); must inline. Covers both ord_term overloads (eql_v3.int4_ord, eql_v3.int4_ord_ore). function_search_path_mutable eql_v3 match_term function Bloom-filter match term extractor for the eql_v3 *_match domains: returns eql_v3.bloom_filter. Used inside the inlinable @>/<@ containment wrappers and as the functional-index expression USING gin (eql_v3.match_term(col)); must inline so the GIN index engages. SET search_path would disable SQL function inlining. function_search_path_mutable eql_v3 contains function Containment (@>) comparison wrapper on the eql_v3 *_match domains. Inlines to `match_term(a) @> match_term(b)`; must reach the functional GIN index on eql_v3.match_term(col) for bloom-filter match to engage Bitmap Index Scan. function_search_path_mutable eql_v3 contained_by function Contained-by (<@) comparison wrapper on the eql_v3 *_match domains. Same rationale as eql_v3.contains. @@ -120,16 +120,16 @@ function_search_path_mutable eql_v3 gt function Greater-than comparison wrapper function_search_path_mutable eql_v3 gte function Greater-than-or-equal comparison wrapper on the eql_v3 ordered domains. Same rationale as eql_v3.lt. function_search_path_mutable eql_v3 min function Per-domain MIN aggregate on the eql_v3 ordered domains (splinter labels aggregates type=function): ALTER AGGREGATE has no SET configuration_parameter syntax, and ALTER ROUTINE/FUNCTION reject aggregates. The aggregate's SFUNC carries a pinned search_path. function_search_path_mutable eql_v3 max function Per-domain MAX aggregate on the eql_v3 ordered domains. Same as eql_v3.min. -function_search_path_mutable eql_v3 ore_block_u64_8_256_eq function Inner comparator for the eql_v3 ore_block_u64_8_256 type's `=` operator (self-contained SEM fork). The eql_v3 *_ord comparison wrappers inline to `ord_term(a) op ord_term(b)`; the planner only carries that through to the functional ORE index if this inner function is also inlinable (no SET, IMMUTABLE). Mirrors eql_v2.ore_block_u64_8_256_eq. -function_search_path_mutable eql_v3 ore_block_u64_8_256_neq function Inner comparator for the eql_v3 ore_block_u64_8_256 `<>` operator. Same rationale as eql_v3.ore_block_u64_8_256_eq. -function_search_path_mutable eql_v3 ore_block_u64_8_256_lt function Inner comparator for the eql_v3 ore_block_u64_8_256 `<` operator. Same rationale as eql_v3.ore_block_u64_8_256_eq. -function_search_path_mutable eql_v3 ore_block_u64_8_256_lte function Inner comparator for the eql_v3 ore_block_u64_8_256 `<=` operator. Same rationale as eql_v3.ore_block_u64_8_256_eq. -function_search_path_mutable eql_v3 ore_block_u64_8_256_gt function Inner comparator for the eql_v3 ore_block_u64_8_256 `>` operator. Same rationale as eql_v3.ore_block_u64_8_256_eq. -function_search_path_mutable eql_v3 ore_block_u64_8_256_gte function Inner comparator for the eql_v3 ore_block_u64_8_256 `>=` operator. Same rationale as eql_v3.ore_block_u64_8_256_eq. +function_search_path_mutable eql_v3 ore_block_256_eq function Inner comparator for the eql_v3 ore_block_256 type's `=` operator (self-contained SEM fork). The eql_v3 *_ord comparison wrappers inline to `ord_term(a) op ord_term(b)`; the planner only carries that through to the functional ORE index if this inner function is also inlinable (no SET, IMMUTABLE). Mirrors eql_v2.ore_block_u64_8_256_eq. +function_search_path_mutable eql_v3 ore_block_256_neq function Inner comparator for the eql_v3 ore_block_256 `<>` operator. Same rationale as eql_v3.ore_block_256_eq. +function_search_path_mutable eql_v3 ore_block_256_lt function Inner comparator for the eql_v3 ore_block_256 `<` operator. Same rationale as eql_v3.ore_block_256_eq. +function_search_path_mutable eql_v3 ore_block_256_lte function Inner comparator for the eql_v3 ore_block_256 `<=` operator. Same rationale as eql_v3.ore_block_256_eq. +function_search_path_mutable eql_v3 ore_block_256_gt function Inner comparator for the eql_v3 ore_block_256 `>` operator. Same rationale as eql_v3.ore_block_256_eq. +function_search_path_mutable eql_v3 ore_block_256_gte function Inner comparator for the eql_v3 ore_block_256 `>=` operator. Same rationale as eql_v3.ore_block_256_eq. function_search_path_mutable eql_v3 hmac_256 function HMAC equality extractor for the eql_v3 SEM fork: inlinable SQL (jsonb) constructor used inside eql_v3.eq_term. Must inline so the functional hash/btree index on eql_v3.eq_term(col) engages. Mirrors eql_v2.hmac_256. function_search_path_mutable eql_v3 bloom_filter function Bloom-filter match extractor for the eql_v3 SEM fork: inlinable SQL (jsonb) constructor used inside eql_v3.match_term. Must inline so the functional GIN index on eql_v3.match_term(col) engages. Mirrors eql_v3.hmac_256. -function_search_path_mutable eql_v3 jsonb_array_to_bytea_array function Hand-written jsonb→bytea[] helper for the eql_v3 SEM fork: inlinable SQL (no SET, IMMUTABLE). Reached per-encrypted-value through eql_v3.ore_block_u64_8_256; must inline so the planner can fold it into the calling query. Pinned by neither the structural skip (it takes bare jsonb, not a jsonb-backed domain) nor an inline-critical OID clause — it carries the documented `eql-inline-critical` COMMENT marker that tasks/pin_search_path.sql honours. The eql_v2 copy stays plpgsql (pinned) by design. -function_search_path_mutable eql_v3 jsonb_array_to_ore_block_u64_8_256 function Hand-written jsonb→ore_block composite helper for the eql_v3 SEM fork: inlinable SQL (no SET, IMMUTABLE). Same rationale as eql_v3.jsonb_array_to_bytea_array — reached per-encrypted-value through eql_v3.ore_block_u64_8_256, carries the `eql-inline-critical` COMMENT marker. The eql_v2 copy stays plpgsql (pinned) by design. +function_search_path_mutable eql_v3 jsonb_array_to_bytea_array function Hand-written jsonb→bytea[] helper for the eql_v3 SEM fork: inlinable SQL (no SET, IMMUTABLE). Reached per-encrypted-value through eql_v3.ore_block_256; must inline so the planner can fold it into the calling query. Pinned by neither the structural skip (it takes bare jsonb, not a jsonb-backed domain) nor an inline-critical OID clause — it carries the documented `eql-inline-critical` COMMENT marker that tasks/pin_search_path.sql honours. The eql_v2 copy stays plpgsql (pinned) by design. +function_search_path_mutable eql_v3 jsonb_array_to_ore_block_256 function Hand-written jsonb→ore_block composite helper for the eql_v3 SEM fork: inlinable SQL (no SET, IMMUTABLE). Same rationale as eql_v3.jsonb_array_to_bytea_array — reached per-encrypted-value through eql_v3.ore_block_256, carries the `eql-inline-critical` COMMENT marker. The eql_v2 copy stays plpgsql (pinned) by design. function_search_path_mutable eql_v3 ore_cllw_eq function Inner comparator for the eql_v3.ore_cllw composite type's `=` operator (self-contained SEM fork, DEFAULT FOR TYPE btree opclass eql_v3.ore_cllw_ops). The outer same-type operators back the opclass; the planner only carries the inlined form through to functional-index match if this inner function is also inlinable (no SET, IMMUTABLE). The plpgsql FUNCTION 1 comparator (compare_ore_cllw_term) stays pinned by design. Mirrors eql_v2.ore_cllw_eq. function_search_path_mutable eql_v3 ore_cllw_neq function Inner comparator for the eql_v3.ore_cllw `<>` operator. Same rationale as eql_v3.ore_cllw_eq. function_search_path_mutable eql_v3 ore_cllw_lt function Inner comparator for the eql_v3.ore_cllw `<` operator. Same rationale as eql_v3.ore_cllw_eq. diff --git a/tests/codegen/reference/date/date_ord_functions.sql b/tests/codegen/reference/date/date_ord_functions.sql index 8c2adf48..99fa57eb 100644 --- a/tests/codegen/reference/date/date_ord_functions.sql +++ b/tests/codegen/reference/date/date_ord_functions.sql @@ -3,19 +3,19 @@ -- REQUIRE: src/v3/schema.sql -- REQUIRE: src/v3/scalars/date/date_types.sql -- REQUIRE: src/v3/scalars/functions.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/functions.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/operators.sql +-- REQUIRE: src/v3/sem/ore_block_256/functions.sql +-- REQUIRE: src/v3/sem/ore_block_256/operators.sql --! @file encrypted_domain/date/date_ord_functions.sql --! @brief Functions for eql_v3.date_ord. --! @brief Index extractor for eql_v3.date_ord. --! @param a eql_v3.date_ord ---! @return eql_v3.ore_block_u64_8_256 +--! @return eql_v3.ore_block_256 CREATE FUNCTION eql_v3.ord_term(a eql_v3.date_ord) -RETURNS eql_v3.ore_block_u64_8_256 +RETURNS eql_v3.ore_block_256 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE -AS $$ SELECT eql_v3.ore_block_u64_8_256(a::jsonb) $$; +AS $$ SELECT eql_v3.ore_block_256(a::jsonb) $$; --! @brief Operator wrapper for eql_v3.date_ord. --! @param a eql_v3.date_ord diff --git a/tests/codegen/reference/date/date_ord_ore_functions.sql b/tests/codegen/reference/date/date_ord_ore_functions.sql index 1fe59073..d65e3d9d 100644 --- a/tests/codegen/reference/date/date_ord_ore_functions.sql +++ b/tests/codegen/reference/date/date_ord_ore_functions.sql @@ -3,19 +3,19 @@ -- REQUIRE: src/v3/schema.sql -- REQUIRE: src/v3/scalars/date/date_types.sql -- REQUIRE: src/v3/scalars/functions.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/functions.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/operators.sql +-- REQUIRE: src/v3/sem/ore_block_256/functions.sql +-- REQUIRE: src/v3/sem/ore_block_256/operators.sql --! @file encrypted_domain/date/date_ord_ore_functions.sql --! @brief Functions for eql_v3.date_ord_ore. --! @brief Index extractor for eql_v3.date_ord_ore. --! @param a eql_v3.date_ord_ore ---! @return eql_v3.ore_block_u64_8_256 +--! @return eql_v3.ore_block_256 CREATE FUNCTION eql_v3.ord_term(a eql_v3.date_ord_ore) -RETURNS eql_v3.ore_block_u64_8_256 +RETURNS eql_v3.ore_block_256 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE -AS $$ SELECT eql_v3.ore_block_u64_8_256(a::jsonb) $$; +AS $$ SELECT eql_v3.ore_block_256(a::jsonb) $$; --! @brief Operator wrapper for eql_v3.date_ord_ore. --! @param a eql_v3.date_ord_ore diff --git a/tests/codegen/reference/int2/int2_ord_functions.sql b/tests/codegen/reference/int2/int2_ord_functions.sql index 58c977d2..a9a375a8 100644 --- a/tests/codegen/reference/int2/int2_ord_functions.sql +++ b/tests/codegen/reference/int2/int2_ord_functions.sql @@ -3,19 +3,19 @@ -- REQUIRE: src/v3/schema.sql -- REQUIRE: src/v3/scalars/int2/int2_types.sql -- REQUIRE: src/v3/scalars/functions.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/functions.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/operators.sql +-- REQUIRE: src/v3/sem/ore_block_256/functions.sql +-- REQUIRE: src/v3/sem/ore_block_256/operators.sql --! @file encrypted_domain/int2/int2_ord_functions.sql --! @brief Functions for eql_v3.int2_ord. --! @brief Index extractor for eql_v3.int2_ord. --! @param a eql_v3.int2_ord ---! @return eql_v3.ore_block_u64_8_256 +--! @return eql_v3.ore_block_256 CREATE FUNCTION eql_v3.ord_term(a eql_v3.int2_ord) -RETURNS eql_v3.ore_block_u64_8_256 +RETURNS eql_v3.ore_block_256 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE -AS $$ SELECT eql_v3.ore_block_u64_8_256(a::jsonb) $$; +AS $$ SELECT eql_v3.ore_block_256(a::jsonb) $$; --! @brief Operator wrapper for eql_v3.int2_ord. --! @param a eql_v3.int2_ord diff --git a/tests/codegen/reference/int2/int2_ord_ore_functions.sql b/tests/codegen/reference/int2/int2_ord_ore_functions.sql index ab200402..f28400bd 100644 --- a/tests/codegen/reference/int2/int2_ord_ore_functions.sql +++ b/tests/codegen/reference/int2/int2_ord_ore_functions.sql @@ -3,19 +3,19 @@ -- REQUIRE: src/v3/schema.sql -- REQUIRE: src/v3/scalars/int2/int2_types.sql -- REQUIRE: src/v3/scalars/functions.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/functions.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/operators.sql +-- REQUIRE: src/v3/sem/ore_block_256/functions.sql +-- REQUIRE: src/v3/sem/ore_block_256/operators.sql --! @file encrypted_domain/int2/int2_ord_ore_functions.sql --! @brief Functions for eql_v3.int2_ord_ore. --! @brief Index extractor for eql_v3.int2_ord_ore. --! @param a eql_v3.int2_ord_ore ---! @return eql_v3.ore_block_u64_8_256 +--! @return eql_v3.ore_block_256 CREATE FUNCTION eql_v3.ord_term(a eql_v3.int2_ord_ore) -RETURNS eql_v3.ore_block_u64_8_256 +RETURNS eql_v3.ore_block_256 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE -AS $$ SELECT eql_v3.ore_block_u64_8_256(a::jsonb) $$; +AS $$ SELECT eql_v3.ore_block_256(a::jsonb) $$; --! @brief Operator wrapper for eql_v3.int2_ord_ore. --! @param a eql_v3.int2_ord_ore diff --git a/tests/codegen/reference/int4/int4_ord_functions.sql b/tests/codegen/reference/int4/int4_ord_functions.sql index 4b170fcb..b4c67732 100644 --- a/tests/codegen/reference/int4/int4_ord_functions.sql +++ b/tests/codegen/reference/int4/int4_ord_functions.sql @@ -3,19 +3,19 @@ -- REQUIRE: src/v3/schema.sql -- REQUIRE: src/v3/scalars/int4/int4_types.sql -- REQUIRE: src/v3/scalars/functions.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/functions.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/operators.sql +-- REQUIRE: src/v3/sem/ore_block_256/functions.sql +-- REQUIRE: src/v3/sem/ore_block_256/operators.sql --! @file encrypted_domain/int4/int4_ord_functions.sql --! @brief Functions for eql_v3.int4_ord. --! @brief Index extractor for eql_v3.int4_ord. --! @param a eql_v3.int4_ord ---! @return eql_v3.ore_block_u64_8_256 +--! @return eql_v3.ore_block_256 CREATE FUNCTION eql_v3.ord_term(a eql_v3.int4_ord) -RETURNS eql_v3.ore_block_u64_8_256 +RETURNS eql_v3.ore_block_256 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE -AS $$ SELECT eql_v3.ore_block_u64_8_256(a::jsonb) $$; +AS $$ SELECT eql_v3.ore_block_256(a::jsonb) $$; --! @brief Operator wrapper for eql_v3.int4_ord. --! @param a eql_v3.int4_ord diff --git a/tests/codegen/reference/int4/int4_ord_ore_functions.sql b/tests/codegen/reference/int4/int4_ord_ore_functions.sql index e93c8491..964fc480 100644 --- a/tests/codegen/reference/int4/int4_ord_ore_functions.sql +++ b/tests/codegen/reference/int4/int4_ord_ore_functions.sql @@ -3,19 +3,19 @@ -- REQUIRE: src/v3/schema.sql -- REQUIRE: src/v3/scalars/int4/int4_types.sql -- REQUIRE: src/v3/scalars/functions.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/functions.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/operators.sql +-- REQUIRE: src/v3/sem/ore_block_256/functions.sql +-- REQUIRE: src/v3/sem/ore_block_256/operators.sql --! @file encrypted_domain/int4/int4_ord_ore_functions.sql --! @brief Functions for eql_v3.int4_ord_ore. --! @brief Index extractor for eql_v3.int4_ord_ore. --! @param a eql_v3.int4_ord_ore ---! @return eql_v3.ore_block_u64_8_256 +--! @return eql_v3.ore_block_256 CREATE FUNCTION eql_v3.ord_term(a eql_v3.int4_ord_ore) -RETURNS eql_v3.ore_block_u64_8_256 +RETURNS eql_v3.ore_block_256 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE -AS $$ SELECT eql_v3.ore_block_u64_8_256(a::jsonb) $$; +AS $$ SELECT eql_v3.ore_block_256(a::jsonb) $$; --! @brief Operator wrapper for eql_v3.int4_ord_ore. --! @param a eql_v3.int4_ord_ore diff --git a/tests/codegen/reference/int8/int8_ord_functions.sql b/tests/codegen/reference/int8/int8_ord_functions.sql index 109dcd2b..c86fa6a0 100644 --- a/tests/codegen/reference/int8/int8_ord_functions.sql +++ b/tests/codegen/reference/int8/int8_ord_functions.sql @@ -3,19 +3,19 @@ -- REQUIRE: src/v3/schema.sql -- REQUIRE: src/v3/scalars/int8/int8_types.sql -- REQUIRE: src/v3/scalars/functions.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/functions.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/operators.sql +-- REQUIRE: src/v3/sem/ore_block_256/functions.sql +-- REQUIRE: src/v3/sem/ore_block_256/operators.sql --! @file encrypted_domain/int8/int8_ord_functions.sql --! @brief Functions for eql_v3.int8_ord. --! @brief Index extractor for eql_v3.int8_ord. --! @param a eql_v3.int8_ord ---! @return eql_v3.ore_block_u64_8_256 +--! @return eql_v3.ore_block_256 CREATE FUNCTION eql_v3.ord_term(a eql_v3.int8_ord) -RETURNS eql_v3.ore_block_u64_8_256 +RETURNS eql_v3.ore_block_256 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE -AS $$ SELECT eql_v3.ore_block_u64_8_256(a::jsonb) $$; +AS $$ SELECT eql_v3.ore_block_256(a::jsonb) $$; --! @brief Operator wrapper for eql_v3.int8_ord. --! @param a eql_v3.int8_ord diff --git a/tests/codegen/reference/int8/int8_ord_ore_functions.sql b/tests/codegen/reference/int8/int8_ord_ore_functions.sql index dd413fce..6d9ac335 100644 --- a/tests/codegen/reference/int8/int8_ord_ore_functions.sql +++ b/tests/codegen/reference/int8/int8_ord_ore_functions.sql @@ -3,19 +3,19 @@ -- REQUIRE: src/v3/schema.sql -- REQUIRE: src/v3/scalars/int8/int8_types.sql -- REQUIRE: src/v3/scalars/functions.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/functions.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/operators.sql +-- REQUIRE: src/v3/sem/ore_block_256/functions.sql +-- REQUIRE: src/v3/sem/ore_block_256/operators.sql --! @file encrypted_domain/int8/int8_ord_ore_functions.sql --! @brief Functions for eql_v3.int8_ord_ore. --! @brief Index extractor for eql_v3.int8_ord_ore. --! @param a eql_v3.int8_ord_ore ---! @return eql_v3.ore_block_u64_8_256 +--! @return eql_v3.ore_block_256 CREATE FUNCTION eql_v3.ord_term(a eql_v3.int8_ord_ore) -RETURNS eql_v3.ore_block_u64_8_256 +RETURNS eql_v3.ore_block_256 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE -AS $$ SELECT eql_v3.ore_block_u64_8_256(a::jsonb) $$; +AS $$ SELECT eql_v3.ore_block_256(a::jsonb) $$; --! @brief Operator wrapper for eql_v3.int8_ord_ore. --! @param a eql_v3.int8_ord_ore diff --git a/tests/codegen/reference/numeric/numeric_eq_functions.sql b/tests/codegen/reference/numeric/numeric_eq_functions.sql new file mode 100644 index 00000000..2b20d70b --- /dev/null +++ b/tests/codegen/reference/numeric/numeric_eq_functions.sql @@ -0,0 +1,407 @@ +-- REFERENCE: hand-maintained parity baseline for crates/eql-codegen - see ../README.md +-- AUTOMATICALLY GENERATED FILE. +-- REQUIRE: src/v3/schema.sql +-- REQUIRE: src/v3/scalars/numeric/numeric_types.sql +-- REQUIRE: src/v3/scalars/functions.sql +-- REQUIRE: src/v3/sem/hmac_256/functions.sql + +--! @file encrypted_domain/numeric/numeric_eq_functions.sql +--! @brief Functions for eql_v3.numeric_eq. + +--! @brief Index extractor for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @return eql_v3.hmac_256 +CREATE FUNCTION eql_v3.eq_term(a eql_v3.numeric_eq) +RETURNS eql_v3.hmac_256 +LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.hmac_256(a::jsonb) $$; + +--! @brief Operator wrapper for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b eql_v3.numeric_eq +--! @return boolean +CREATE FUNCTION eql_v3.eq(a eql_v3.numeric_eq, b eql_v3.numeric_eq) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.eq_term(a) = eql_v3.eq_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.eq(a eql_v3.numeric_eq, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.eq_term(a) = eql_v3.eq_term(b::eql_v3.numeric_eq) $$; + +--! @brief Operator wrapper for eql_v3.numeric_eq. +--! @param a jsonb +--! @param b eql_v3.numeric_eq +--! @return boolean +CREATE FUNCTION eql_v3.eq(a jsonb, b eql_v3.numeric_eq) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.eq_term(a::eql_v3.numeric_eq) = eql_v3.eq_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b eql_v3.numeric_eq +--! @return boolean +CREATE FUNCTION eql_v3.neq(a eql_v3.numeric_eq, b eql_v3.numeric_eq) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.eq_term(a) <> eql_v3.eq_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.neq(a eql_v3.numeric_eq, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.eq_term(a) <> eql_v3.eq_term(b::eql_v3.numeric_eq) $$; + +--! @brief Operator wrapper for eql_v3.numeric_eq. +--! @param a jsonb +--! @param b eql_v3.numeric_eq +--! @return boolean +CREATE FUNCTION eql_v3.neq(a jsonb, b eql_v3.numeric_eq) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.eq_term(a::eql_v3.numeric_eq) <> eql_v3.eq_term(b) $$; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b eql_v3.numeric_eq +--! @return boolean +CREATE FUNCTION eql_v3.lt(a eql_v3.numeric_eq, b eql_v3.numeric_eq) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.lt(a eql_v3.numeric_eq, b jsonb) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a jsonb +--! @param b eql_v3.numeric_eq +--! @return boolean +CREATE FUNCTION eql_v3.lt(a jsonb, b eql_v3.numeric_eq) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b eql_v3.numeric_eq +--! @return boolean +CREATE FUNCTION eql_v3.lte(a eql_v3.numeric_eq, b eql_v3.numeric_eq) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<=', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.lte(a eql_v3.numeric_eq, b jsonb) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<=', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a jsonb +--! @param b eql_v3.numeric_eq +--! @return boolean +CREATE FUNCTION eql_v3.lte(a jsonb, b eql_v3.numeric_eq) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<=', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b eql_v3.numeric_eq +--! @return boolean +CREATE FUNCTION eql_v3.gt(a eql_v3.numeric_eq, b eql_v3.numeric_eq) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '>', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.gt(a eql_v3.numeric_eq, b jsonb) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '>', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a jsonb +--! @param b eql_v3.numeric_eq +--! @return boolean +CREATE FUNCTION eql_v3.gt(a jsonb, b eql_v3.numeric_eq) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '>', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b eql_v3.numeric_eq +--! @return boolean +CREATE FUNCTION eql_v3.gte(a eql_v3.numeric_eq, b eql_v3.numeric_eq) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '>=', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.gte(a eql_v3.numeric_eq, b jsonb) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '>=', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a jsonb +--! @param b eql_v3.numeric_eq +--! @return boolean +CREATE FUNCTION eql_v3.gte(a jsonb, b eql_v3.numeric_eq) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '>=', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b eql_v3.numeric_eq +--! @return boolean +CREATE FUNCTION eql_v3.contains(a eql_v3.numeric_eq, b eql_v3.numeric_eq) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@>', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.contains(a eql_v3.numeric_eq, b jsonb) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@>', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a jsonb +--! @param b eql_v3.numeric_eq +--! @return boolean +CREATE FUNCTION eql_v3.contains(a jsonb, b eql_v3.numeric_eq) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@>', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b eql_v3.numeric_eq +--! @return boolean +CREATE FUNCTION eql_v3.contained_by(a eql_v3.numeric_eq, b eql_v3.numeric_eq) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<@', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.contained_by(a eql_v3.numeric_eq, b jsonb) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<@', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a jsonb +--! @param b eql_v3.numeric_eq +--! @return boolean +CREATE FUNCTION eql_v3.contained_by(a jsonb, b eql_v3.numeric_eq) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<@', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param selector text +--! @return eql_v3.numeric_eq +CREATE FUNCTION eql_v3."->"(a eql_v3.numeric_eq, selector text) +RETURNS eql_v3.numeric_eq IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param selector integer +--! @return eql_v3.numeric_eq +CREATE FUNCTION eql_v3."->"(a eql_v3.numeric_eq, selector integer) +RETURNS eql_v3.numeric_eq IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a jsonb +--! @param selector eql_v3.numeric_eq +--! @return eql_v3.numeric_eq +CREATE FUNCTION eql_v3."->"(a jsonb, selector eql_v3.numeric_eq) +RETURNS eql_v3.numeric_eq IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param selector text +--! @return text +CREATE FUNCTION eql_v3."->>"(a eql_v3.numeric_eq, selector text) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->>', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param selector integer +--! @return text +CREATE FUNCTION eql_v3."->>"(a eql_v3.numeric_eq, selector integer) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->>', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a jsonb +--! @param selector eql_v3.numeric_eq +--! @return text +CREATE FUNCTION eql_v3."->>"(a jsonb, selector eql_v3.numeric_eq) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->>', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b text +--! @return boolean +CREATE FUNCTION eql_v3."?"(a eql_v3.numeric_eq, b text) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '?', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b text[] +--! @return boolean +CREATE FUNCTION eql_v3."?|"(a eql_v3.numeric_eq, b text[]) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '?|', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b text[] +--! @return boolean +CREATE FUNCTION eql_v3."?&"(a eql_v3.numeric_eq, b text[]) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '?&', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b jsonpath +--! @return boolean +CREATE FUNCTION eql_v3."@?"(a eql_v3.numeric_eq, b jsonpath) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@?', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b jsonpath +--! @return boolean +CREATE FUNCTION eql_v3."@@"(a eql_v3.numeric_eq, b jsonpath) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@@', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b text[] +--! @return jsonb +CREATE FUNCTION eql_v3."#>"(a eql_v3.numeric_eq, b text[]) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '#>', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b text[] +--! @return text +CREATE FUNCTION eql_v3."#>>"(a eql_v3.numeric_eq, b text[]) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '#>>', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b text +--! @return jsonb +CREATE FUNCTION eql_v3."-"(a eql_v3.numeric_eq, b text) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '-', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b integer +--! @return jsonb +CREATE FUNCTION eql_v3."-"(a eql_v3.numeric_eq, b integer) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '-', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b text[] +--! @return jsonb +CREATE FUNCTION eql_v3."-"(a eql_v3.numeric_eq, b text[]) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '-', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b text[] +--! @return jsonb +CREATE FUNCTION eql_v3."#-"(a eql_v3.numeric_eq, b text[]) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '#-', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b eql_v3.numeric_eq +--! @return jsonb +CREATE FUNCTION eql_v3."||"(a eql_v3.numeric_eq, b eql_v3.numeric_eq) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '||', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a eql_v3.numeric_eq +--! @param b jsonb +--! @return jsonb +CREATE FUNCTION eql_v3."||"(a eql_v3.numeric_eq, b jsonb) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '||', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_eq. +--! @param a jsonb +--! @param b eql_v3.numeric_eq +--! @return jsonb +CREATE FUNCTION eql_v3."||"(a jsonb, b eql_v3.numeric_eq) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '||', 'eql_v3.numeric_eq'; END; $$ +LANGUAGE plpgsql; diff --git a/tests/codegen/reference/numeric/numeric_eq_operators.sql b/tests/codegen/reference/numeric/numeric_eq_operators.sql new file mode 100644 index 00000000..6ec23570 --- /dev/null +++ b/tests/codegen/reference/numeric/numeric_eq_operators.sql @@ -0,0 +1,234 @@ +-- REFERENCE: hand-maintained parity baseline for crates/eql-codegen - see ../README.md +-- AUTOMATICALLY GENERATED FILE. +-- REQUIRE: src/v3/schema.sql +-- REQUIRE: src/v3/scalars/numeric/numeric_types.sql +-- REQUIRE: src/v3/scalars/numeric/numeric_eq_functions.sql + +--! @file encrypted_domain/numeric/numeric_eq_operators.sql +--! @brief Operators for eql_v3.numeric_eq. + +CREATE OPERATOR = ( + FUNCTION = eql_v3.eq, + LEFTARG = eql_v3.numeric_eq, RIGHTARG = eql_v3.numeric_eq, + COMMUTATOR = =, NEGATOR = <>, RESTRICT = eqsel, JOIN = eqjoinsel +); + +CREATE OPERATOR = ( + FUNCTION = eql_v3.eq, + LEFTARG = eql_v3.numeric_eq, RIGHTARG = jsonb, + COMMUTATOR = =, NEGATOR = <>, RESTRICT = eqsel, JOIN = eqjoinsel +); + +CREATE OPERATOR = ( + FUNCTION = eql_v3.eq, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_eq, + COMMUTATOR = =, NEGATOR = <>, RESTRICT = eqsel, JOIN = eqjoinsel +); + +CREATE OPERATOR <> ( + FUNCTION = eql_v3.neq, + LEFTARG = eql_v3.numeric_eq, RIGHTARG = eql_v3.numeric_eq, + COMMUTATOR = <>, NEGATOR = =, RESTRICT = neqsel, JOIN = neqjoinsel +); + +CREATE OPERATOR <> ( + FUNCTION = eql_v3.neq, + LEFTARG = eql_v3.numeric_eq, RIGHTARG = jsonb, + COMMUTATOR = <>, NEGATOR = =, RESTRICT = neqsel, JOIN = neqjoinsel +); + +CREATE OPERATOR <> ( + FUNCTION = eql_v3.neq, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_eq, + COMMUTATOR = <>, NEGATOR = =, RESTRICT = neqsel, JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + FUNCTION = eql_v3.lt, + LEFTARG = eql_v3.numeric_eq, RIGHTARG = eql_v3.numeric_eq +); + +CREATE OPERATOR < ( + FUNCTION = eql_v3.lt, + LEFTARG = eql_v3.numeric_eq, RIGHTARG = jsonb +); + +CREATE OPERATOR < ( + FUNCTION = eql_v3.lt, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_eq +); + +CREATE OPERATOR <= ( + FUNCTION = eql_v3.lte, + LEFTARG = eql_v3.numeric_eq, RIGHTARG = eql_v3.numeric_eq +); + +CREATE OPERATOR <= ( + FUNCTION = eql_v3.lte, + LEFTARG = eql_v3.numeric_eq, RIGHTARG = jsonb +); + +CREATE OPERATOR <= ( + FUNCTION = eql_v3.lte, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_eq +); + +CREATE OPERATOR > ( + FUNCTION = eql_v3.gt, + LEFTARG = eql_v3.numeric_eq, RIGHTARG = eql_v3.numeric_eq +); + +CREATE OPERATOR > ( + FUNCTION = eql_v3.gt, + LEFTARG = eql_v3.numeric_eq, RIGHTARG = jsonb +); + +CREATE OPERATOR > ( + FUNCTION = eql_v3.gt, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_eq +); + +CREATE OPERATOR >= ( + FUNCTION = eql_v3.gte, + LEFTARG = eql_v3.numeric_eq, RIGHTARG = eql_v3.numeric_eq +); + +CREATE OPERATOR >= ( + FUNCTION = eql_v3.gte, + LEFTARG = eql_v3.numeric_eq, RIGHTARG = jsonb +); + +CREATE OPERATOR >= ( + FUNCTION = eql_v3.gte, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_eq +); + +CREATE OPERATOR @> ( + FUNCTION = eql_v3.contains, + LEFTARG = eql_v3.numeric_eq, RIGHTARG = eql_v3.numeric_eq +); + +CREATE OPERATOR @> ( + FUNCTION = eql_v3.contains, + LEFTARG = eql_v3.numeric_eq, RIGHTARG = jsonb +); + +CREATE OPERATOR @> ( + FUNCTION = eql_v3.contains, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_eq +); + +CREATE OPERATOR <@ ( + FUNCTION = eql_v3.contained_by, + LEFTARG = eql_v3.numeric_eq, RIGHTARG = eql_v3.numeric_eq +); + +CREATE OPERATOR <@ ( + FUNCTION = eql_v3.contained_by, + LEFTARG = eql_v3.numeric_eq, RIGHTARG = jsonb +); + +CREATE OPERATOR <@ ( + FUNCTION = eql_v3.contained_by, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_eq +); + +CREATE OPERATOR -> ( + FUNCTION = eql_v3."->", + LEFTARG = eql_v3.numeric_eq, RIGHTARG = text +); + +CREATE OPERATOR -> ( + FUNCTION = eql_v3."->", + LEFTARG = eql_v3.numeric_eq, RIGHTARG = integer +); + +CREATE OPERATOR -> ( + FUNCTION = eql_v3."->", + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_eq +); + +CREATE OPERATOR ->> ( + FUNCTION = eql_v3."->>", + LEFTARG = eql_v3.numeric_eq, RIGHTARG = text +); + +CREATE OPERATOR ->> ( + FUNCTION = eql_v3."->>", + LEFTARG = eql_v3.numeric_eq, RIGHTARG = integer +); + +CREATE OPERATOR ->> ( + FUNCTION = eql_v3."->>", + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_eq +); + +CREATE OPERATOR ? ( + FUNCTION = eql_v3."?", + LEFTARG = eql_v3.numeric_eq, RIGHTARG = text +); + +CREATE OPERATOR ?| ( + FUNCTION = eql_v3."?|", + LEFTARG = eql_v3.numeric_eq, RIGHTARG = text[] +); + +CREATE OPERATOR ?& ( + FUNCTION = eql_v3."?&", + LEFTARG = eql_v3.numeric_eq, RIGHTARG = text[] +); + +CREATE OPERATOR @? ( + FUNCTION = eql_v3."@?", + LEFTARG = eql_v3.numeric_eq, RIGHTARG = jsonpath +); + +CREATE OPERATOR @@ ( + FUNCTION = eql_v3."@@", + LEFTARG = eql_v3.numeric_eq, RIGHTARG = jsonpath +); + +CREATE OPERATOR #> ( + FUNCTION = eql_v3."#>", + LEFTARG = eql_v3.numeric_eq, RIGHTARG = text[] +); + +CREATE OPERATOR #>> ( + FUNCTION = eql_v3."#>>", + LEFTARG = eql_v3.numeric_eq, RIGHTARG = text[] +); + +CREATE OPERATOR - ( + FUNCTION = eql_v3."-", + LEFTARG = eql_v3.numeric_eq, RIGHTARG = text +); + +CREATE OPERATOR - ( + FUNCTION = eql_v3."-", + LEFTARG = eql_v3.numeric_eq, RIGHTARG = integer +); + +CREATE OPERATOR - ( + FUNCTION = eql_v3."-", + LEFTARG = eql_v3.numeric_eq, RIGHTARG = text[] +); + +CREATE OPERATOR #- ( + FUNCTION = eql_v3."#-", + LEFTARG = eql_v3.numeric_eq, RIGHTARG = text[] +); + +CREATE OPERATOR || ( + FUNCTION = eql_v3."||", + LEFTARG = eql_v3.numeric_eq, RIGHTARG = eql_v3.numeric_eq +); + +CREATE OPERATOR || ( + FUNCTION = eql_v3."||", + LEFTARG = eql_v3.numeric_eq, RIGHTARG = jsonb +); + +CREATE OPERATOR || ( + FUNCTION = eql_v3."||", + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_eq +); diff --git a/tests/codegen/reference/numeric/numeric_functions.sql b/tests/codegen/reference/numeric/numeric_functions.sql new file mode 100644 index 00000000..9c6146ab --- /dev/null +++ b/tests/codegen/reference/numeric/numeric_functions.sql @@ -0,0 +1,404 @@ +-- REFERENCE: hand-maintained parity baseline for crates/eql-codegen - see ../README.md +-- AUTOMATICALLY GENERATED FILE. +-- REQUIRE: src/v3/schema.sql +-- REQUIRE: src/v3/scalars/numeric/numeric_types.sql +-- REQUIRE: src/v3/scalars/functions.sql + +--! @file encrypted_domain/numeric/numeric_functions.sql +--! @brief Functions for eql_v3.numeric. + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b eql_v3.numeric +--! @return boolean +CREATE FUNCTION eql_v3.eq(a eql_v3.numeric, b eql_v3.numeric) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '=', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.eq(a eql_v3.numeric, b jsonb) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '=', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a jsonb +--! @param b eql_v3.numeric +--! @return boolean +CREATE FUNCTION eql_v3.eq(a jsonb, b eql_v3.numeric) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '=', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b eql_v3.numeric +--! @return boolean +CREATE FUNCTION eql_v3.neq(a eql_v3.numeric, b eql_v3.numeric) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<>', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.neq(a eql_v3.numeric, b jsonb) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<>', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a jsonb +--! @param b eql_v3.numeric +--! @return boolean +CREATE FUNCTION eql_v3.neq(a jsonb, b eql_v3.numeric) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<>', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b eql_v3.numeric +--! @return boolean +CREATE FUNCTION eql_v3.lt(a eql_v3.numeric, b eql_v3.numeric) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.lt(a eql_v3.numeric, b jsonb) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a jsonb +--! @param b eql_v3.numeric +--! @return boolean +CREATE FUNCTION eql_v3.lt(a jsonb, b eql_v3.numeric) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b eql_v3.numeric +--! @return boolean +CREATE FUNCTION eql_v3.lte(a eql_v3.numeric, b eql_v3.numeric) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<=', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.lte(a eql_v3.numeric, b jsonb) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<=', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a jsonb +--! @param b eql_v3.numeric +--! @return boolean +CREATE FUNCTION eql_v3.lte(a jsonb, b eql_v3.numeric) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<=', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b eql_v3.numeric +--! @return boolean +CREATE FUNCTION eql_v3.gt(a eql_v3.numeric, b eql_v3.numeric) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '>', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.gt(a eql_v3.numeric, b jsonb) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '>', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a jsonb +--! @param b eql_v3.numeric +--! @return boolean +CREATE FUNCTION eql_v3.gt(a jsonb, b eql_v3.numeric) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '>', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b eql_v3.numeric +--! @return boolean +CREATE FUNCTION eql_v3.gte(a eql_v3.numeric, b eql_v3.numeric) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '>=', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.gte(a eql_v3.numeric, b jsonb) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '>=', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a jsonb +--! @param b eql_v3.numeric +--! @return boolean +CREATE FUNCTION eql_v3.gte(a jsonb, b eql_v3.numeric) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '>=', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b eql_v3.numeric +--! @return boolean +CREATE FUNCTION eql_v3.contains(a eql_v3.numeric, b eql_v3.numeric) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@>', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.contains(a eql_v3.numeric, b jsonb) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@>', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a jsonb +--! @param b eql_v3.numeric +--! @return boolean +CREATE FUNCTION eql_v3.contains(a jsonb, b eql_v3.numeric) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@>', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b eql_v3.numeric +--! @return boolean +CREATE FUNCTION eql_v3.contained_by(a eql_v3.numeric, b eql_v3.numeric) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<@', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.contained_by(a eql_v3.numeric, b jsonb) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<@', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a jsonb +--! @param b eql_v3.numeric +--! @return boolean +CREATE FUNCTION eql_v3.contained_by(a jsonb, b eql_v3.numeric) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<@', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param selector text +--! @return eql_v3.numeric +CREATE FUNCTION eql_v3."->"(a eql_v3.numeric, selector text) +RETURNS eql_v3.numeric IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param selector integer +--! @return eql_v3.numeric +CREATE FUNCTION eql_v3."->"(a eql_v3.numeric, selector integer) +RETURNS eql_v3.numeric IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a jsonb +--! @param selector eql_v3.numeric +--! @return eql_v3.numeric +CREATE FUNCTION eql_v3."->"(a jsonb, selector eql_v3.numeric) +RETURNS eql_v3.numeric IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param selector text +--! @return text +CREATE FUNCTION eql_v3."->>"(a eql_v3.numeric, selector text) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->>', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param selector integer +--! @return text +CREATE FUNCTION eql_v3."->>"(a eql_v3.numeric, selector integer) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->>', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a jsonb +--! @param selector eql_v3.numeric +--! @return text +CREATE FUNCTION eql_v3."->>"(a jsonb, selector eql_v3.numeric) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->>', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b text +--! @return boolean +CREATE FUNCTION eql_v3."?"(a eql_v3.numeric, b text) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '?', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b text[] +--! @return boolean +CREATE FUNCTION eql_v3."?|"(a eql_v3.numeric, b text[]) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '?|', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b text[] +--! @return boolean +CREATE FUNCTION eql_v3."?&"(a eql_v3.numeric, b text[]) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '?&', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b jsonpath +--! @return boolean +CREATE FUNCTION eql_v3."@?"(a eql_v3.numeric, b jsonpath) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@?', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b jsonpath +--! @return boolean +CREATE FUNCTION eql_v3."@@"(a eql_v3.numeric, b jsonpath) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@@', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b text[] +--! @return jsonb +CREATE FUNCTION eql_v3."#>"(a eql_v3.numeric, b text[]) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '#>', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b text[] +--! @return text +CREATE FUNCTION eql_v3."#>>"(a eql_v3.numeric, b text[]) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '#>>', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b text +--! @return jsonb +CREATE FUNCTION eql_v3."-"(a eql_v3.numeric, b text) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '-', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b integer +--! @return jsonb +CREATE FUNCTION eql_v3."-"(a eql_v3.numeric, b integer) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '-', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b text[] +--! @return jsonb +CREATE FUNCTION eql_v3."-"(a eql_v3.numeric, b text[]) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '-', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b text[] +--! @return jsonb +CREATE FUNCTION eql_v3."#-"(a eql_v3.numeric, b text[]) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '#-', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b eql_v3.numeric +--! @return jsonb +CREATE FUNCTION eql_v3."||"(a eql_v3.numeric, b eql_v3.numeric) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '||', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a eql_v3.numeric +--! @param b jsonb +--! @return jsonb +CREATE FUNCTION eql_v3."||"(a eql_v3.numeric, b jsonb) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '||', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric. +--! @param a jsonb +--! @param b eql_v3.numeric +--! @return jsonb +CREATE FUNCTION eql_v3."||"(a jsonb, b eql_v3.numeric) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '||', 'eql_v3.numeric'; END; $$ +LANGUAGE plpgsql; diff --git a/tests/codegen/reference/numeric/numeric_operators.sql b/tests/codegen/reference/numeric/numeric_operators.sql new file mode 100644 index 00000000..b62f419e --- /dev/null +++ b/tests/codegen/reference/numeric/numeric_operators.sql @@ -0,0 +1,228 @@ +-- REFERENCE: hand-maintained parity baseline for crates/eql-codegen - see ../README.md +-- AUTOMATICALLY GENERATED FILE. +-- REQUIRE: src/v3/schema.sql +-- REQUIRE: src/v3/scalars/numeric/numeric_types.sql +-- REQUIRE: src/v3/scalars/numeric/numeric_functions.sql + +--! @file encrypted_domain/numeric/numeric_operators.sql +--! @brief Operators for eql_v3.numeric. + +CREATE OPERATOR = ( + FUNCTION = eql_v3.eq, + LEFTARG = eql_v3.numeric, RIGHTARG = eql_v3.numeric +); + +CREATE OPERATOR = ( + FUNCTION = eql_v3.eq, + LEFTARG = eql_v3.numeric, RIGHTARG = jsonb +); + +CREATE OPERATOR = ( + FUNCTION = eql_v3.eq, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric +); + +CREATE OPERATOR <> ( + FUNCTION = eql_v3.neq, + LEFTARG = eql_v3.numeric, RIGHTARG = eql_v3.numeric +); + +CREATE OPERATOR <> ( + FUNCTION = eql_v3.neq, + LEFTARG = eql_v3.numeric, RIGHTARG = jsonb +); + +CREATE OPERATOR <> ( + FUNCTION = eql_v3.neq, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric +); + +CREATE OPERATOR < ( + FUNCTION = eql_v3.lt, + LEFTARG = eql_v3.numeric, RIGHTARG = eql_v3.numeric +); + +CREATE OPERATOR < ( + FUNCTION = eql_v3.lt, + LEFTARG = eql_v3.numeric, RIGHTARG = jsonb +); + +CREATE OPERATOR < ( + FUNCTION = eql_v3.lt, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric +); + +CREATE OPERATOR <= ( + FUNCTION = eql_v3.lte, + LEFTARG = eql_v3.numeric, RIGHTARG = eql_v3.numeric +); + +CREATE OPERATOR <= ( + FUNCTION = eql_v3.lte, + LEFTARG = eql_v3.numeric, RIGHTARG = jsonb +); + +CREATE OPERATOR <= ( + FUNCTION = eql_v3.lte, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric +); + +CREATE OPERATOR > ( + FUNCTION = eql_v3.gt, + LEFTARG = eql_v3.numeric, RIGHTARG = eql_v3.numeric +); + +CREATE OPERATOR > ( + FUNCTION = eql_v3.gt, + LEFTARG = eql_v3.numeric, RIGHTARG = jsonb +); + +CREATE OPERATOR > ( + FUNCTION = eql_v3.gt, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric +); + +CREATE OPERATOR >= ( + FUNCTION = eql_v3.gte, + LEFTARG = eql_v3.numeric, RIGHTARG = eql_v3.numeric +); + +CREATE OPERATOR >= ( + FUNCTION = eql_v3.gte, + LEFTARG = eql_v3.numeric, RIGHTARG = jsonb +); + +CREATE OPERATOR >= ( + FUNCTION = eql_v3.gte, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric +); + +CREATE OPERATOR @> ( + FUNCTION = eql_v3.contains, + LEFTARG = eql_v3.numeric, RIGHTARG = eql_v3.numeric +); + +CREATE OPERATOR @> ( + FUNCTION = eql_v3.contains, + LEFTARG = eql_v3.numeric, RIGHTARG = jsonb +); + +CREATE OPERATOR @> ( + FUNCTION = eql_v3.contains, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric +); + +CREATE OPERATOR <@ ( + FUNCTION = eql_v3.contained_by, + LEFTARG = eql_v3.numeric, RIGHTARG = eql_v3.numeric +); + +CREATE OPERATOR <@ ( + FUNCTION = eql_v3.contained_by, + LEFTARG = eql_v3.numeric, RIGHTARG = jsonb +); + +CREATE OPERATOR <@ ( + FUNCTION = eql_v3.contained_by, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric +); + +CREATE OPERATOR -> ( + FUNCTION = eql_v3."->", + LEFTARG = eql_v3.numeric, RIGHTARG = text +); + +CREATE OPERATOR -> ( + FUNCTION = eql_v3."->", + LEFTARG = eql_v3.numeric, RIGHTARG = integer +); + +CREATE OPERATOR -> ( + FUNCTION = eql_v3."->", + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric +); + +CREATE OPERATOR ->> ( + FUNCTION = eql_v3."->>", + LEFTARG = eql_v3.numeric, RIGHTARG = text +); + +CREATE OPERATOR ->> ( + FUNCTION = eql_v3."->>", + LEFTARG = eql_v3.numeric, RIGHTARG = integer +); + +CREATE OPERATOR ->> ( + FUNCTION = eql_v3."->>", + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric +); + +CREATE OPERATOR ? ( + FUNCTION = eql_v3."?", + LEFTARG = eql_v3.numeric, RIGHTARG = text +); + +CREATE OPERATOR ?| ( + FUNCTION = eql_v3."?|", + LEFTARG = eql_v3.numeric, RIGHTARG = text[] +); + +CREATE OPERATOR ?& ( + FUNCTION = eql_v3."?&", + LEFTARG = eql_v3.numeric, RIGHTARG = text[] +); + +CREATE OPERATOR @? ( + FUNCTION = eql_v3."@?", + LEFTARG = eql_v3.numeric, RIGHTARG = jsonpath +); + +CREATE OPERATOR @@ ( + FUNCTION = eql_v3."@@", + LEFTARG = eql_v3.numeric, RIGHTARG = jsonpath +); + +CREATE OPERATOR #> ( + FUNCTION = eql_v3."#>", + LEFTARG = eql_v3.numeric, RIGHTARG = text[] +); + +CREATE OPERATOR #>> ( + FUNCTION = eql_v3."#>>", + LEFTARG = eql_v3.numeric, RIGHTARG = text[] +); + +CREATE OPERATOR - ( + FUNCTION = eql_v3."-", + LEFTARG = eql_v3.numeric, RIGHTARG = text +); + +CREATE OPERATOR - ( + FUNCTION = eql_v3."-", + LEFTARG = eql_v3.numeric, RIGHTARG = integer +); + +CREATE OPERATOR - ( + FUNCTION = eql_v3."-", + LEFTARG = eql_v3.numeric, RIGHTARG = text[] +); + +CREATE OPERATOR #- ( + FUNCTION = eql_v3."#-", + LEFTARG = eql_v3.numeric, RIGHTARG = text[] +); + +CREATE OPERATOR || ( + FUNCTION = eql_v3."||", + LEFTARG = eql_v3.numeric, RIGHTARG = eql_v3.numeric +); + +CREATE OPERATOR || ( + FUNCTION = eql_v3."||", + LEFTARG = eql_v3.numeric, RIGHTARG = jsonb +); + +CREATE OPERATOR || ( + FUNCTION = eql_v3."||", + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric +); diff --git a/tests/codegen/reference/numeric/numeric_ord_aggregates.sql b/tests/codegen/reference/numeric/numeric_ord_aggregates.sql new file mode 100644 index 00000000..03bf02c1 --- /dev/null +++ b/tests/codegen/reference/numeric/numeric_ord_aggregates.sql @@ -0,0 +1,63 @@ +-- REFERENCE: hand-maintained parity baseline for crates/eql-codegen - see ../README.md +-- AUTOMATICALLY GENERATED FILE. +-- REQUIRE: src/v3/schema.sql +-- REQUIRE: src/v3/scalars/numeric/numeric_types.sql +-- REQUIRE: src/v3/scalars/numeric/numeric_ord_functions.sql +-- REQUIRE: src/v3/scalars/numeric/numeric_ord_operators.sql + +--! @file encrypted_domain/numeric/numeric_ord_aggregates.sql +--! @brief Aggregates for eql_v3.numeric_ord. + +--! @brief State function for min on eql_v3.numeric_ord. +--! @param state eql_v3.numeric_ord +--! @param value eql_v3.numeric_ord +--! @return eql_v3.numeric_ord +CREATE FUNCTION eql_v3.min_sfunc(state eql_v3.numeric_ord, value eql_v3.numeric_ord) +RETURNS eql_v3.numeric_ord +LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE +SET search_path = pg_catalog, extensions, public +AS $$ +BEGIN + IF value < state THEN + RETURN value; + END IF; + RETURN state; +END; +$$; + +--! @brief min aggregate for eql_v3.numeric_ord. +--! @param input eql_v3.numeric_ord +--! @return eql_v3.numeric_ord +CREATE AGGREGATE eql_v3.min(eql_v3.numeric_ord) ( + sfunc = eql_v3.min_sfunc, + stype = eql_v3.numeric_ord, + combinefunc = eql_v3.min_sfunc, + parallel = safe +); + +--! @brief State function for max on eql_v3.numeric_ord. +--! @param state eql_v3.numeric_ord +--! @param value eql_v3.numeric_ord +--! @return eql_v3.numeric_ord +CREATE FUNCTION eql_v3.max_sfunc(state eql_v3.numeric_ord, value eql_v3.numeric_ord) +RETURNS eql_v3.numeric_ord +LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE +SET search_path = pg_catalog, extensions, public +AS $$ +BEGIN + IF value > state THEN + RETURN value; + END IF; + RETURN state; +END; +$$; + +--! @brief max aggregate for eql_v3.numeric_ord. +--! @param input eql_v3.numeric_ord +--! @return eql_v3.numeric_ord +CREATE AGGREGATE eql_v3.max(eql_v3.numeric_ord) ( + sfunc = eql_v3.max_sfunc, + stype = eql_v3.numeric_ord, + combinefunc = eql_v3.max_sfunc, + parallel = safe +); diff --git a/tests/codegen/reference/numeric/numeric_ord_functions.sql b/tests/codegen/reference/numeric/numeric_ord_functions.sql new file mode 100644 index 00000000..5467f834 --- /dev/null +++ b/tests/codegen/reference/numeric/numeric_ord_functions.sql @@ -0,0 +1,396 @@ +-- REFERENCE: hand-maintained parity baseline for crates/eql-codegen - see ../README.md +-- AUTOMATICALLY GENERATED FILE. +-- REQUIRE: src/v3/schema.sql +-- REQUIRE: src/v3/scalars/numeric/numeric_types.sql +-- REQUIRE: src/v3/scalars/functions.sql +-- REQUIRE: src/v3/sem/ore_block_256/functions.sql +-- REQUIRE: src/v3/sem/ore_block_256/operators.sql + +--! @file encrypted_domain/numeric/numeric_ord_functions.sql +--! @brief Functions for eql_v3.numeric_ord. + +--! @brief Index extractor for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @return eql_v3.ore_block_256 +CREATE FUNCTION eql_v3.ord_term(a eql_v3.numeric_ord) +RETURNS eql_v3.ore_block_256 +LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ore_block_256(a::jsonb) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b eql_v3.numeric_ord +--! @return boolean +CREATE FUNCTION eql_v3.eq(a eql_v3.numeric_ord, b eql_v3.numeric_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) = eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.eq(a eql_v3.numeric_ord, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) = eql_v3.ord_term(b::eql_v3.numeric_ord) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord. +--! @param a jsonb +--! @param b eql_v3.numeric_ord +--! @return boolean +CREATE FUNCTION eql_v3.eq(a jsonb, b eql_v3.numeric_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.numeric_ord) = eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b eql_v3.numeric_ord +--! @return boolean +CREATE FUNCTION eql_v3.neq(a eql_v3.numeric_ord, b eql_v3.numeric_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) <> eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.neq(a eql_v3.numeric_ord, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) <> eql_v3.ord_term(b::eql_v3.numeric_ord) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord. +--! @param a jsonb +--! @param b eql_v3.numeric_ord +--! @return boolean +CREATE FUNCTION eql_v3.neq(a jsonb, b eql_v3.numeric_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.numeric_ord) <> eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b eql_v3.numeric_ord +--! @return boolean +CREATE FUNCTION eql_v3.lt(a eql_v3.numeric_ord, b eql_v3.numeric_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) < eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.lt(a eql_v3.numeric_ord, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) < eql_v3.ord_term(b::eql_v3.numeric_ord) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord. +--! @param a jsonb +--! @param b eql_v3.numeric_ord +--! @return boolean +CREATE FUNCTION eql_v3.lt(a jsonb, b eql_v3.numeric_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.numeric_ord) < eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b eql_v3.numeric_ord +--! @return boolean +CREATE FUNCTION eql_v3.lte(a eql_v3.numeric_ord, b eql_v3.numeric_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) <= eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.lte(a eql_v3.numeric_ord, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) <= eql_v3.ord_term(b::eql_v3.numeric_ord) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord. +--! @param a jsonb +--! @param b eql_v3.numeric_ord +--! @return boolean +CREATE FUNCTION eql_v3.lte(a jsonb, b eql_v3.numeric_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.numeric_ord) <= eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b eql_v3.numeric_ord +--! @return boolean +CREATE FUNCTION eql_v3.gt(a eql_v3.numeric_ord, b eql_v3.numeric_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) > eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.gt(a eql_v3.numeric_ord, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) > eql_v3.ord_term(b::eql_v3.numeric_ord) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord. +--! @param a jsonb +--! @param b eql_v3.numeric_ord +--! @return boolean +CREATE FUNCTION eql_v3.gt(a jsonb, b eql_v3.numeric_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.numeric_ord) > eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b eql_v3.numeric_ord +--! @return boolean +CREATE FUNCTION eql_v3.gte(a eql_v3.numeric_ord, b eql_v3.numeric_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) >= eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.gte(a eql_v3.numeric_ord, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) >= eql_v3.ord_term(b::eql_v3.numeric_ord) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord. +--! @param a jsonb +--! @param b eql_v3.numeric_ord +--! @return boolean +CREATE FUNCTION eql_v3.gte(a jsonb, b eql_v3.numeric_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.numeric_ord) >= eql_v3.ord_term(b) $$; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b eql_v3.numeric_ord +--! @return boolean +CREATE FUNCTION eql_v3.contains(a eql_v3.numeric_ord, b eql_v3.numeric_ord) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@>', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.contains(a eql_v3.numeric_ord, b jsonb) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@>', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a jsonb +--! @param b eql_v3.numeric_ord +--! @return boolean +CREATE FUNCTION eql_v3.contains(a jsonb, b eql_v3.numeric_ord) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@>', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b eql_v3.numeric_ord +--! @return boolean +CREATE FUNCTION eql_v3.contained_by(a eql_v3.numeric_ord, b eql_v3.numeric_ord) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<@', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.contained_by(a eql_v3.numeric_ord, b jsonb) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<@', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a jsonb +--! @param b eql_v3.numeric_ord +--! @return boolean +CREATE FUNCTION eql_v3.contained_by(a jsonb, b eql_v3.numeric_ord) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<@', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param selector text +--! @return eql_v3.numeric_ord +CREATE FUNCTION eql_v3."->"(a eql_v3.numeric_ord, selector text) +RETURNS eql_v3.numeric_ord IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param selector integer +--! @return eql_v3.numeric_ord +CREATE FUNCTION eql_v3."->"(a eql_v3.numeric_ord, selector integer) +RETURNS eql_v3.numeric_ord IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a jsonb +--! @param selector eql_v3.numeric_ord +--! @return eql_v3.numeric_ord +CREATE FUNCTION eql_v3."->"(a jsonb, selector eql_v3.numeric_ord) +RETURNS eql_v3.numeric_ord IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param selector text +--! @return text +CREATE FUNCTION eql_v3."->>"(a eql_v3.numeric_ord, selector text) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->>', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param selector integer +--! @return text +CREATE FUNCTION eql_v3."->>"(a eql_v3.numeric_ord, selector integer) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->>', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a jsonb +--! @param selector eql_v3.numeric_ord +--! @return text +CREATE FUNCTION eql_v3."->>"(a jsonb, selector eql_v3.numeric_ord) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->>', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b text +--! @return boolean +CREATE FUNCTION eql_v3."?"(a eql_v3.numeric_ord, b text) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '?', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b text[] +--! @return boolean +CREATE FUNCTION eql_v3."?|"(a eql_v3.numeric_ord, b text[]) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '?|', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b text[] +--! @return boolean +CREATE FUNCTION eql_v3."?&"(a eql_v3.numeric_ord, b text[]) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '?&', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b jsonpath +--! @return boolean +CREATE FUNCTION eql_v3."@?"(a eql_v3.numeric_ord, b jsonpath) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@?', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b jsonpath +--! @return boolean +CREATE FUNCTION eql_v3."@@"(a eql_v3.numeric_ord, b jsonpath) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@@', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b text[] +--! @return jsonb +CREATE FUNCTION eql_v3."#>"(a eql_v3.numeric_ord, b text[]) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '#>', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b text[] +--! @return text +CREATE FUNCTION eql_v3."#>>"(a eql_v3.numeric_ord, b text[]) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '#>>', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b text +--! @return jsonb +CREATE FUNCTION eql_v3."-"(a eql_v3.numeric_ord, b text) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '-', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b integer +--! @return jsonb +CREATE FUNCTION eql_v3."-"(a eql_v3.numeric_ord, b integer) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '-', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b text[] +--! @return jsonb +CREATE FUNCTION eql_v3."-"(a eql_v3.numeric_ord, b text[]) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '-', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b text[] +--! @return jsonb +CREATE FUNCTION eql_v3."#-"(a eql_v3.numeric_ord, b text[]) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '#-', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b eql_v3.numeric_ord +--! @return jsonb +CREATE FUNCTION eql_v3."||"(a eql_v3.numeric_ord, b eql_v3.numeric_ord) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '||', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a eql_v3.numeric_ord +--! @param b jsonb +--! @return jsonb +CREATE FUNCTION eql_v3."||"(a eql_v3.numeric_ord, b jsonb) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '||', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord. +--! @param a jsonb +--! @param b eql_v3.numeric_ord +--! @return jsonb +CREATE FUNCTION eql_v3."||"(a jsonb, b eql_v3.numeric_ord) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '||', 'eql_v3.numeric_ord'; END; $$ +LANGUAGE plpgsql; diff --git a/tests/codegen/reference/numeric/numeric_ord_operators.sql b/tests/codegen/reference/numeric/numeric_ord_operators.sql new file mode 100644 index 00000000..847bf6a1 --- /dev/null +++ b/tests/codegen/reference/numeric/numeric_ord_operators.sql @@ -0,0 +1,246 @@ +-- REFERENCE: hand-maintained parity baseline for crates/eql-codegen - see ../README.md +-- AUTOMATICALLY GENERATED FILE. +-- REQUIRE: src/v3/schema.sql +-- REQUIRE: src/v3/scalars/numeric/numeric_types.sql +-- REQUIRE: src/v3/scalars/numeric/numeric_ord_functions.sql + +--! @file encrypted_domain/numeric/numeric_ord_operators.sql +--! @brief Operators for eql_v3.numeric_ord. + +CREATE OPERATOR = ( + FUNCTION = eql_v3.eq, + LEFTARG = eql_v3.numeric_ord, RIGHTARG = eql_v3.numeric_ord, + COMMUTATOR = =, NEGATOR = <>, RESTRICT = eqsel, JOIN = eqjoinsel +); + +CREATE OPERATOR = ( + FUNCTION = eql_v3.eq, + LEFTARG = eql_v3.numeric_ord, RIGHTARG = jsonb, + COMMUTATOR = =, NEGATOR = <>, RESTRICT = eqsel, JOIN = eqjoinsel +); + +CREATE OPERATOR = ( + FUNCTION = eql_v3.eq, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_ord, + COMMUTATOR = =, NEGATOR = <>, RESTRICT = eqsel, JOIN = eqjoinsel +); + +CREATE OPERATOR <> ( + FUNCTION = eql_v3.neq, + LEFTARG = eql_v3.numeric_ord, RIGHTARG = eql_v3.numeric_ord, + COMMUTATOR = <>, NEGATOR = =, RESTRICT = neqsel, JOIN = neqjoinsel +); + +CREATE OPERATOR <> ( + FUNCTION = eql_v3.neq, + LEFTARG = eql_v3.numeric_ord, RIGHTARG = jsonb, + COMMUTATOR = <>, NEGATOR = =, RESTRICT = neqsel, JOIN = neqjoinsel +); + +CREATE OPERATOR <> ( + FUNCTION = eql_v3.neq, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_ord, + COMMUTATOR = <>, NEGATOR = =, RESTRICT = neqsel, JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + FUNCTION = eql_v3.lt, + LEFTARG = eql_v3.numeric_ord, RIGHTARG = eql_v3.numeric_ord, + COMMUTATOR = >, NEGATOR = >=, RESTRICT = scalarltsel, JOIN = scalarltjoinsel +); + +CREATE OPERATOR < ( + FUNCTION = eql_v3.lt, + LEFTARG = eql_v3.numeric_ord, RIGHTARG = jsonb, + COMMUTATOR = >, NEGATOR = >=, RESTRICT = scalarltsel, JOIN = scalarltjoinsel +); + +CREATE OPERATOR < ( + FUNCTION = eql_v3.lt, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_ord, + COMMUTATOR = >, NEGATOR = >=, RESTRICT = scalarltsel, JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + FUNCTION = eql_v3.lte, + LEFTARG = eql_v3.numeric_ord, RIGHTARG = eql_v3.numeric_ord, + COMMUTATOR = >=, NEGATOR = >, RESTRICT = scalarlesel, JOIN = scalarlejoinsel +); + +CREATE OPERATOR <= ( + FUNCTION = eql_v3.lte, + LEFTARG = eql_v3.numeric_ord, RIGHTARG = jsonb, + COMMUTATOR = >=, NEGATOR = >, RESTRICT = scalarlesel, JOIN = scalarlejoinsel +); + +CREATE OPERATOR <= ( + FUNCTION = eql_v3.lte, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_ord, + COMMUTATOR = >=, NEGATOR = >, RESTRICT = scalarlesel, JOIN = scalarlejoinsel +); + +CREATE OPERATOR > ( + FUNCTION = eql_v3.gt, + LEFTARG = eql_v3.numeric_ord, RIGHTARG = eql_v3.numeric_ord, + COMMUTATOR = <, NEGATOR = <=, RESTRICT = scalargtsel, JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + FUNCTION = eql_v3.gt, + LEFTARG = eql_v3.numeric_ord, RIGHTARG = jsonb, + COMMUTATOR = <, NEGATOR = <=, RESTRICT = scalargtsel, JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + FUNCTION = eql_v3.gt, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_ord, + COMMUTATOR = <, NEGATOR = <=, RESTRICT = scalargtsel, JOIN = scalargtjoinsel +); + +CREATE OPERATOR >= ( + FUNCTION = eql_v3.gte, + LEFTARG = eql_v3.numeric_ord, RIGHTARG = eql_v3.numeric_ord, + COMMUTATOR = <=, NEGATOR = <, RESTRICT = scalargesel, JOIN = scalargejoinsel +); + +CREATE OPERATOR >= ( + FUNCTION = eql_v3.gte, + LEFTARG = eql_v3.numeric_ord, RIGHTARG = jsonb, + COMMUTATOR = <=, NEGATOR = <, RESTRICT = scalargesel, JOIN = scalargejoinsel +); + +CREATE OPERATOR >= ( + FUNCTION = eql_v3.gte, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_ord, + COMMUTATOR = <=, NEGATOR = <, RESTRICT = scalargesel, JOIN = scalargejoinsel +); + +CREATE OPERATOR @> ( + FUNCTION = eql_v3.contains, + LEFTARG = eql_v3.numeric_ord, RIGHTARG = eql_v3.numeric_ord +); + +CREATE OPERATOR @> ( + FUNCTION = eql_v3.contains, + LEFTARG = eql_v3.numeric_ord, RIGHTARG = jsonb +); + +CREATE OPERATOR @> ( + FUNCTION = eql_v3.contains, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_ord +); + +CREATE OPERATOR <@ ( + FUNCTION = eql_v3.contained_by, + LEFTARG = eql_v3.numeric_ord, RIGHTARG = eql_v3.numeric_ord +); + +CREATE OPERATOR <@ ( + FUNCTION = eql_v3.contained_by, + LEFTARG = eql_v3.numeric_ord, RIGHTARG = jsonb +); + +CREATE OPERATOR <@ ( + FUNCTION = eql_v3.contained_by, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_ord +); + +CREATE OPERATOR -> ( + FUNCTION = eql_v3."->", + LEFTARG = eql_v3.numeric_ord, RIGHTARG = text +); + +CREATE OPERATOR -> ( + FUNCTION = eql_v3."->", + LEFTARG = eql_v3.numeric_ord, RIGHTARG = integer +); + +CREATE OPERATOR -> ( + FUNCTION = eql_v3."->", + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_ord +); + +CREATE OPERATOR ->> ( + FUNCTION = eql_v3."->>", + LEFTARG = eql_v3.numeric_ord, RIGHTARG = text +); + +CREATE OPERATOR ->> ( + FUNCTION = eql_v3."->>", + LEFTARG = eql_v3.numeric_ord, RIGHTARG = integer +); + +CREATE OPERATOR ->> ( + FUNCTION = eql_v3."->>", + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_ord +); + +CREATE OPERATOR ? ( + FUNCTION = eql_v3."?", + LEFTARG = eql_v3.numeric_ord, RIGHTARG = text +); + +CREATE OPERATOR ?| ( + FUNCTION = eql_v3."?|", + LEFTARG = eql_v3.numeric_ord, RIGHTARG = text[] +); + +CREATE OPERATOR ?& ( + FUNCTION = eql_v3."?&", + LEFTARG = eql_v3.numeric_ord, RIGHTARG = text[] +); + +CREATE OPERATOR @? ( + FUNCTION = eql_v3."@?", + LEFTARG = eql_v3.numeric_ord, RIGHTARG = jsonpath +); + +CREATE OPERATOR @@ ( + FUNCTION = eql_v3."@@", + LEFTARG = eql_v3.numeric_ord, RIGHTARG = jsonpath +); + +CREATE OPERATOR #> ( + FUNCTION = eql_v3."#>", + LEFTARG = eql_v3.numeric_ord, RIGHTARG = text[] +); + +CREATE OPERATOR #>> ( + FUNCTION = eql_v3."#>>", + LEFTARG = eql_v3.numeric_ord, RIGHTARG = text[] +); + +CREATE OPERATOR - ( + FUNCTION = eql_v3."-", + LEFTARG = eql_v3.numeric_ord, RIGHTARG = text +); + +CREATE OPERATOR - ( + FUNCTION = eql_v3."-", + LEFTARG = eql_v3.numeric_ord, RIGHTARG = integer +); + +CREATE OPERATOR - ( + FUNCTION = eql_v3."-", + LEFTARG = eql_v3.numeric_ord, RIGHTARG = text[] +); + +CREATE OPERATOR #- ( + FUNCTION = eql_v3."#-", + LEFTARG = eql_v3.numeric_ord, RIGHTARG = text[] +); + +CREATE OPERATOR || ( + FUNCTION = eql_v3."||", + LEFTARG = eql_v3.numeric_ord, RIGHTARG = eql_v3.numeric_ord +); + +CREATE OPERATOR || ( + FUNCTION = eql_v3."||", + LEFTARG = eql_v3.numeric_ord, RIGHTARG = jsonb +); + +CREATE OPERATOR || ( + FUNCTION = eql_v3."||", + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_ord +); diff --git a/tests/codegen/reference/numeric/numeric_ord_ore_aggregates.sql b/tests/codegen/reference/numeric/numeric_ord_ore_aggregates.sql new file mode 100644 index 00000000..e78a6ffb --- /dev/null +++ b/tests/codegen/reference/numeric/numeric_ord_ore_aggregates.sql @@ -0,0 +1,63 @@ +-- REFERENCE: hand-maintained parity baseline for crates/eql-codegen - see ../README.md +-- AUTOMATICALLY GENERATED FILE. +-- REQUIRE: src/v3/schema.sql +-- REQUIRE: src/v3/scalars/numeric/numeric_types.sql +-- REQUIRE: src/v3/scalars/numeric/numeric_ord_ore_functions.sql +-- REQUIRE: src/v3/scalars/numeric/numeric_ord_ore_operators.sql + +--! @file encrypted_domain/numeric/numeric_ord_ore_aggregates.sql +--! @brief Aggregates for eql_v3.numeric_ord_ore. + +--! @brief State function for min on eql_v3.numeric_ord_ore. +--! @param state eql_v3.numeric_ord_ore +--! @param value eql_v3.numeric_ord_ore +--! @return eql_v3.numeric_ord_ore +CREATE FUNCTION eql_v3.min_sfunc(state eql_v3.numeric_ord_ore, value eql_v3.numeric_ord_ore) +RETURNS eql_v3.numeric_ord_ore +LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE +SET search_path = pg_catalog, extensions, public +AS $$ +BEGIN + IF value < state THEN + RETURN value; + END IF; + RETURN state; +END; +$$; + +--! @brief min aggregate for eql_v3.numeric_ord_ore. +--! @param input eql_v3.numeric_ord_ore +--! @return eql_v3.numeric_ord_ore +CREATE AGGREGATE eql_v3.min(eql_v3.numeric_ord_ore) ( + sfunc = eql_v3.min_sfunc, + stype = eql_v3.numeric_ord_ore, + combinefunc = eql_v3.min_sfunc, + parallel = safe +); + +--! @brief State function for max on eql_v3.numeric_ord_ore. +--! @param state eql_v3.numeric_ord_ore +--! @param value eql_v3.numeric_ord_ore +--! @return eql_v3.numeric_ord_ore +CREATE FUNCTION eql_v3.max_sfunc(state eql_v3.numeric_ord_ore, value eql_v3.numeric_ord_ore) +RETURNS eql_v3.numeric_ord_ore +LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE +SET search_path = pg_catalog, extensions, public +AS $$ +BEGIN + IF value > state THEN + RETURN value; + END IF; + RETURN state; +END; +$$; + +--! @brief max aggregate for eql_v3.numeric_ord_ore. +--! @param input eql_v3.numeric_ord_ore +--! @return eql_v3.numeric_ord_ore +CREATE AGGREGATE eql_v3.max(eql_v3.numeric_ord_ore) ( + sfunc = eql_v3.max_sfunc, + stype = eql_v3.numeric_ord_ore, + combinefunc = eql_v3.max_sfunc, + parallel = safe +); diff --git a/tests/codegen/reference/numeric/numeric_ord_ore_functions.sql b/tests/codegen/reference/numeric/numeric_ord_ore_functions.sql new file mode 100644 index 00000000..a62ab25a --- /dev/null +++ b/tests/codegen/reference/numeric/numeric_ord_ore_functions.sql @@ -0,0 +1,396 @@ +-- REFERENCE: hand-maintained parity baseline for crates/eql-codegen - see ../README.md +-- AUTOMATICALLY GENERATED FILE. +-- REQUIRE: src/v3/schema.sql +-- REQUIRE: src/v3/scalars/numeric/numeric_types.sql +-- REQUIRE: src/v3/scalars/functions.sql +-- REQUIRE: src/v3/sem/ore_block_256/functions.sql +-- REQUIRE: src/v3/sem/ore_block_256/operators.sql + +--! @file encrypted_domain/numeric/numeric_ord_ore_functions.sql +--! @brief Functions for eql_v3.numeric_ord_ore. + +--! @brief Index extractor for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @return eql_v3.ore_block_256 +CREATE FUNCTION eql_v3.ord_term(a eql_v3.numeric_ord_ore) +RETURNS eql_v3.ore_block_256 +LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ore_block_256(a::jsonb) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b eql_v3.numeric_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.eq(a eql_v3.numeric_ord_ore, b eql_v3.numeric_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) = eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.eq(a eql_v3.numeric_ord_ore, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) = eql_v3.ord_term(b::eql_v3.numeric_ord_ore) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord_ore. +--! @param a jsonb +--! @param b eql_v3.numeric_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.eq(a jsonb, b eql_v3.numeric_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.numeric_ord_ore) = eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b eql_v3.numeric_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.neq(a eql_v3.numeric_ord_ore, b eql_v3.numeric_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) <> eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.neq(a eql_v3.numeric_ord_ore, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) <> eql_v3.ord_term(b::eql_v3.numeric_ord_ore) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord_ore. +--! @param a jsonb +--! @param b eql_v3.numeric_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.neq(a jsonb, b eql_v3.numeric_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.numeric_ord_ore) <> eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b eql_v3.numeric_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.lt(a eql_v3.numeric_ord_ore, b eql_v3.numeric_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) < eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.lt(a eql_v3.numeric_ord_ore, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) < eql_v3.ord_term(b::eql_v3.numeric_ord_ore) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord_ore. +--! @param a jsonb +--! @param b eql_v3.numeric_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.lt(a jsonb, b eql_v3.numeric_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.numeric_ord_ore) < eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b eql_v3.numeric_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.lte(a eql_v3.numeric_ord_ore, b eql_v3.numeric_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) <= eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.lte(a eql_v3.numeric_ord_ore, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) <= eql_v3.ord_term(b::eql_v3.numeric_ord_ore) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord_ore. +--! @param a jsonb +--! @param b eql_v3.numeric_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.lte(a jsonb, b eql_v3.numeric_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.numeric_ord_ore) <= eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b eql_v3.numeric_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.gt(a eql_v3.numeric_ord_ore, b eql_v3.numeric_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) > eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.gt(a eql_v3.numeric_ord_ore, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) > eql_v3.ord_term(b::eql_v3.numeric_ord_ore) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord_ore. +--! @param a jsonb +--! @param b eql_v3.numeric_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.gt(a jsonb, b eql_v3.numeric_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.numeric_ord_ore) > eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b eql_v3.numeric_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.gte(a eql_v3.numeric_ord_ore, b eql_v3.numeric_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) >= eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.gte(a eql_v3.numeric_ord_ore, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) >= eql_v3.ord_term(b::eql_v3.numeric_ord_ore) $$; + +--! @brief Operator wrapper for eql_v3.numeric_ord_ore. +--! @param a jsonb +--! @param b eql_v3.numeric_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.gte(a jsonb, b eql_v3.numeric_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.numeric_ord_ore) >= eql_v3.ord_term(b) $$; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b eql_v3.numeric_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.contains(a eql_v3.numeric_ord_ore, b eql_v3.numeric_ord_ore) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@>', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.contains(a eql_v3.numeric_ord_ore, b jsonb) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@>', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a jsonb +--! @param b eql_v3.numeric_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.contains(a jsonb, b eql_v3.numeric_ord_ore) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@>', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b eql_v3.numeric_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.contained_by(a eql_v3.numeric_ord_ore, b eql_v3.numeric_ord_ore) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<@', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.contained_by(a eql_v3.numeric_ord_ore, b jsonb) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<@', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a jsonb +--! @param b eql_v3.numeric_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.contained_by(a jsonb, b eql_v3.numeric_ord_ore) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<@', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param selector text +--! @return eql_v3.numeric_ord_ore +CREATE FUNCTION eql_v3."->"(a eql_v3.numeric_ord_ore, selector text) +RETURNS eql_v3.numeric_ord_ore IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param selector integer +--! @return eql_v3.numeric_ord_ore +CREATE FUNCTION eql_v3."->"(a eql_v3.numeric_ord_ore, selector integer) +RETURNS eql_v3.numeric_ord_ore IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a jsonb +--! @param selector eql_v3.numeric_ord_ore +--! @return eql_v3.numeric_ord_ore +CREATE FUNCTION eql_v3."->"(a jsonb, selector eql_v3.numeric_ord_ore) +RETURNS eql_v3.numeric_ord_ore IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param selector text +--! @return text +CREATE FUNCTION eql_v3."->>"(a eql_v3.numeric_ord_ore, selector text) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->>', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param selector integer +--! @return text +CREATE FUNCTION eql_v3."->>"(a eql_v3.numeric_ord_ore, selector integer) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->>', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a jsonb +--! @param selector eql_v3.numeric_ord_ore +--! @return text +CREATE FUNCTION eql_v3."->>"(a jsonb, selector eql_v3.numeric_ord_ore) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->>', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b text +--! @return boolean +CREATE FUNCTION eql_v3."?"(a eql_v3.numeric_ord_ore, b text) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '?', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b text[] +--! @return boolean +CREATE FUNCTION eql_v3."?|"(a eql_v3.numeric_ord_ore, b text[]) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '?|', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b text[] +--! @return boolean +CREATE FUNCTION eql_v3."?&"(a eql_v3.numeric_ord_ore, b text[]) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '?&', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b jsonpath +--! @return boolean +CREATE FUNCTION eql_v3."@?"(a eql_v3.numeric_ord_ore, b jsonpath) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@?', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b jsonpath +--! @return boolean +CREATE FUNCTION eql_v3."@@"(a eql_v3.numeric_ord_ore, b jsonpath) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@@', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b text[] +--! @return jsonb +CREATE FUNCTION eql_v3."#>"(a eql_v3.numeric_ord_ore, b text[]) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '#>', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b text[] +--! @return text +CREATE FUNCTION eql_v3."#>>"(a eql_v3.numeric_ord_ore, b text[]) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '#>>', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b text +--! @return jsonb +CREATE FUNCTION eql_v3."-"(a eql_v3.numeric_ord_ore, b text) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '-', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b integer +--! @return jsonb +CREATE FUNCTION eql_v3."-"(a eql_v3.numeric_ord_ore, b integer) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '-', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b text[] +--! @return jsonb +CREATE FUNCTION eql_v3."-"(a eql_v3.numeric_ord_ore, b text[]) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '-', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b text[] +--! @return jsonb +CREATE FUNCTION eql_v3."#-"(a eql_v3.numeric_ord_ore, b text[]) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '#-', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b eql_v3.numeric_ord_ore +--! @return jsonb +CREATE FUNCTION eql_v3."||"(a eql_v3.numeric_ord_ore, b eql_v3.numeric_ord_ore) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '||', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a eql_v3.numeric_ord_ore +--! @param b jsonb +--! @return jsonb +CREATE FUNCTION eql_v3."||"(a eql_v3.numeric_ord_ore, b jsonb) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '||', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.numeric_ord_ore. +--! @param a jsonb +--! @param b eql_v3.numeric_ord_ore +--! @return jsonb +CREATE FUNCTION eql_v3."||"(a jsonb, b eql_v3.numeric_ord_ore) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '||', 'eql_v3.numeric_ord_ore'; END; $$ +LANGUAGE plpgsql; diff --git a/tests/codegen/reference/numeric/numeric_ord_ore_operators.sql b/tests/codegen/reference/numeric/numeric_ord_ore_operators.sql new file mode 100644 index 00000000..e449a61a --- /dev/null +++ b/tests/codegen/reference/numeric/numeric_ord_ore_operators.sql @@ -0,0 +1,246 @@ +-- REFERENCE: hand-maintained parity baseline for crates/eql-codegen - see ../README.md +-- AUTOMATICALLY GENERATED FILE. +-- REQUIRE: src/v3/schema.sql +-- REQUIRE: src/v3/scalars/numeric/numeric_types.sql +-- REQUIRE: src/v3/scalars/numeric/numeric_ord_ore_functions.sql + +--! @file encrypted_domain/numeric/numeric_ord_ore_operators.sql +--! @brief Operators for eql_v3.numeric_ord_ore. + +CREATE OPERATOR = ( + FUNCTION = eql_v3.eq, + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = eql_v3.numeric_ord_ore, + COMMUTATOR = =, NEGATOR = <>, RESTRICT = eqsel, JOIN = eqjoinsel +); + +CREATE OPERATOR = ( + FUNCTION = eql_v3.eq, + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = jsonb, + COMMUTATOR = =, NEGATOR = <>, RESTRICT = eqsel, JOIN = eqjoinsel +); + +CREATE OPERATOR = ( + FUNCTION = eql_v3.eq, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_ord_ore, + COMMUTATOR = =, NEGATOR = <>, RESTRICT = eqsel, JOIN = eqjoinsel +); + +CREATE OPERATOR <> ( + FUNCTION = eql_v3.neq, + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = eql_v3.numeric_ord_ore, + COMMUTATOR = <>, NEGATOR = =, RESTRICT = neqsel, JOIN = neqjoinsel +); + +CREATE OPERATOR <> ( + FUNCTION = eql_v3.neq, + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = jsonb, + COMMUTATOR = <>, NEGATOR = =, RESTRICT = neqsel, JOIN = neqjoinsel +); + +CREATE OPERATOR <> ( + FUNCTION = eql_v3.neq, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_ord_ore, + COMMUTATOR = <>, NEGATOR = =, RESTRICT = neqsel, JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + FUNCTION = eql_v3.lt, + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = eql_v3.numeric_ord_ore, + COMMUTATOR = >, NEGATOR = >=, RESTRICT = scalarltsel, JOIN = scalarltjoinsel +); + +CREATE OPERATOR < ( + FUNCTION = eql_v3.lt, + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = jsonb, + COMMUTATOR = >, NEGATOR = >=, RESTRICT = scalarltsel, JOIN = scalarltjoinsel +); + +CREATE OPERATOR < ( + FUNCTION = eql_v3.lt, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_ord_ore, + COMMUTATOR = >, NEGATOR = >=, RESTRICT = scalarltsel, JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + FUNCTION = eql_v3.lte, + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = eql_v3.numeric_ord_ore, + COMMUTATOR = >=, NEGATOR = >, RESTRICT = scalarlesel, JOIN = scalarlejoinsel +); + +CREATE OPERATOR <= ( + FUNCTION = eql_v3.lte, + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = jsonb, + COMMUTATOR = >=, NEGATOR = >, RESTRICT = scalarlesel, JOIN = scalarlejoinsel +); + +CREATE OPERATOR <= ( + FUNCTION = eql_v3.lte, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_ord_ore, + COMMUTATOR = >=, NEGATOR = >, RESTRICT = scalarlesel, JOIN = scalarlejoinsel +); + +CREATE OPERATOR > ( + FUNCTION = eql_v3.gt, + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = eql_v3.numeric_ord_ore, + COMMUTATOR = <, NEGATOR = <=, RESTRICT = scalargtsel, JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + FUNCTION = eql_v3.gt, + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = jsonb, + COMMUTATOR = <, NEGATOR = <=, RESTRICT = scalargtsel, JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + FUNCTION = eql_v3.gt, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_ord_ore, + COMMUTATOR = <, NEGATOR = <=, RESTRICT = scalargtsel, JOIN = scalargtjoinsel +); + +CREATE OPERATOR >= ( + FUNCTION = eql_v3.gte, + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = eql_v3.numeric_ord_ore, + COMMUTATOR = <=, NEGATOR = <, RESTRICT = scalargesel, JOIN = scalargejoinsel +); + +CREATE OPERATOR >= ( + FUNCTION = eql_v3.gte, + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = jsonb, + COMMUTATOR = <=, NEGATOR = <, RESTRICT = scalargesel, JOIN = scalargejoinsel +); + +CREATE OPERATOR >= ( + FUNCTION = eql_v3.gte, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_ord_ore, + COMMUTATOR = <=, NEGATOR = <, RESTRICT = scalargesel, JOIN = scalargejoinsel +); + +CREATE OPERATOR @> ( + FUNCTION = eql_v3.contains, + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = eql_v3.numeric_ord_ore +); + +CREATE OPERATOR @> ( + FUNCTION = eql_v3.contains, + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = jsonb +); + +CREATE OPERATOR @> ( + FUNCTION = eql_v3.contains, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_ord_ore +); + +CREATE OPERATOR <@ ( + FUNCTION = eql_v3.contained_by, + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = eql_v3.numeric_ord_ore +); + +CREATE OPERATOR <@ ( + FUNCTION = eql_v3.contained_by, + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = jsonb +); + +CREATE OPERATOR <@ ( + FUNCTION = eql_v3.contained_by, + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_ord_ore +); + +CREATE OPERATOR -> ( + FUNCTION = eql_v3."->", + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = text +); + +CREATE OPERATOR -> ( + FUNCTION = eql_v3."->", + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = integer +); + +CREATE OPERATOR -> ( + FUNCTION = eql_v3."->", + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_ord_ore +); + +CREATE OPERATOR ->> ( + FUNCTION = eql_v3."->>", + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = text +); + +CREATE OPERATOR ->> ( + FUNCTION = eql_v3."->>", + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = integer +); + +CREATE OPERATOR ->> ( + FUNCTION = eql_v3."->>", + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_ord_ore +); + +CREATE OPERATOR ? ( + FUNCTION = eql_v3."?", + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = text +); + +CREATE OPERATOR ?| ( + FUNCTION = eql_v3."?|", + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = text[] +); + +CREATE OPERATOR ?& ( + FUNCTION = eql_v3."?&", + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = text[] +); + +CREATE OPERATOR @? ( + FUNCTION = eql_v3."@?", + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = jsonpath +); + +CREATE OPERATOR @@ ( + FUNCTION = eql_v3."@@", + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = jsonpath +); + +CREATE OPERATOR #> ( + FUNCTION = eql_v3."#>", + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = text[] +); + +CREATE OPERATOR #>> ( + FUNCTION = eql_v3."#>>", + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = text[] +); + +CREATE OPERATOR - ( + FUNCTION = eql_v3."-", + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = text +); + +CREATE OPERATOR - ( + FUNCTION = eql_v3."-", + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = integer +); + +CREATE OPERATOR - ( + FUNCTION = eql_v3."-", + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = text[] +); + +CREATE OPERATOR #- ( + FUNCTION = eql_v3."#-", + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = text[] +); + +CREATE OPERATOR || ( + FUNCTION = eql_v3."||", + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = eql_v3.numeric_ord_ore +); + +CREATE OPERATOR || ( + FUNCTION = eql_v3."||", + LEFTARG = eql_v3.numeric_ord_ore, RIGHTARG = jsonb +); + +CREATE OPERATOR || ( + FUNCTION = eql_v3."||", + LEFTARG = jsonb, RIGHTARG = eql_v3.numeric_ord_ore +); diff --git a/tests/codegen/reference/numeric/numeric_types.sql b/tests/codegen/reference/numeric/numeric_types.sql new file mode 100644 index 00000000..6c9b85d3 --- /dev/null +++ b/tests/codegen/reference/numeric/numeric_types.sql @@ -0,0 +1,73 @@ +-- REFERENCE: hand-maintained parity baseline for crates/eql-codegen - see ../README.md +-- AUTOMATICALLY GENERATED FILE. +-- REQUIRE: src/v3/schema.sql + +--! @file v3/scalars/numeric/numeric_types.sql +--! @brief Encrypted-domain types for numeric. + +DO $$ +BEGIN + --! @brief Encrypted domain eql_v3.numeric. + IF NOT EXISTS ( + SELECT 1 FROM pg_type + WHERE typname = 'numeric' AND typnamespace = 'eql_v3'::regnamespace + ) THEN + CREATE DOMAIN eql_v3.numeric AS jsonb + CHECK ( + jsonb_typeof(VALUE) = 'object' + AND VALUE ? 'v' + AND VALUE ? 'i' + AND VALUE ? 'c' + AND VALUE->>'v' = '2' + ); + END IF; + + --! @brief Encrypted domain eql_v3.numeric_eq. + IF NOT EXISTS ( + SELECT 1 FROM pg_type + WHERE typname = 'numeric_eq' AND typnamespace = 'eql_v3'::regnamespace + ) THEN + CREATE DOMAIN eql_v3.numeric_eq AS jsonb + CHECK ( + jsonb_typeof(VALUE) = 'object' + AND VALUE ? 'v' + AND VALUE ? 'i' + AND VALUE ? 'c' + AND VALUE ? 'hm' + AND VALUE->>'v' = '2' + ); + END IF; + + --! @brief Encrypted domain eql_v3.numeric_ord_ore. + IF NOT EXISTS ( + SELECT 1 FROM pg_type + WHERE typname = 'numeric_ord_ore' AND typnamespace = 'eql_v3'::regnamespace + ) THEN + CREATE DOMAIN eql_v3.numeric_ord_ore AS jsonb + CHECK ( + jsonb_typeof(VALUE) = 'object' + AND VALUE ? 'v' + AND VALUE ? 'i' + AND VALUE ? 'c' + AND VALUE ? 'ob' + AND VALUE->>'v' = '2' + ); + END IF; + + --! @brief Encrypted domain eql_v3.numeric_ord. + IF NOT EXISTS ( + SELECT 1 FROM pg_type + WHERE typname = 'numeric_ord' AND typnamespace = 'eql_v3'::regnamespace + ) THEN + CREATE DOMAIN eql_v3.numeric_ord AS jsonb + CHECK ( + jsonb_typeof(VALUE) = 'object' + AND VALUE ? 'v' + AND VALUE ? 'i' + AND VALUE ? 'c' + AND VALUE ? 'ob' + AND VALUE->>'v' = '2' + ); + END IF; +END +$$; diff --git a/tests/codegen/reference/text/text_ord_functions.sql b/tests/codegen/reference/text/text_ord_functions.sql index f07e8b64..5b67ab5e 100644 --- a/tests/codegen/reference/text/text_ord_functions.sql +++ b/tests/codegen/reference/text/text_ord_functions.sql @@ -4,8 +4,8 @@ -- REQUIRE: src/v3/scalars/text/text_types.sql -- REQUIRE: src/v3/scalars/functions.sql -- REQUIRE: src/v3/sem/hmac_256/functions.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/functions.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/operators.sql +-- REQUIRE: src/v3/sem/ore_block_256/functions.sql +-- REQUIRE: src/v3/sem/ore_block_256/operators.sql --! @file encrypted_domain/text/text_ord_functions.sql --! @brief Functions for eql_v3.text_ord. @@ -20,11 +20,11 @@ AS $$ SELECT eql_v3.hmac_256(a::jsonb) $$; --! @brief Index extractor for eql_v3.text_ord. --! @param a eql_v3.text_ord ---! @return eql_v3.ore_block_u64_8_256 +--! @return eql_v3.ore_block_256 CREATE FUNCTION eql_v3.ord_term(a eql_v3.text_ord) -RETURNS eql_v3.ore_block_u64_8_256 +RETURNS eql_v3.ore_block_256 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE -AS $$ SELECT eql_v3.ore_block_u64_8_256(a::jsonb) $$; +AS $$ SELECT eql_v3.ore_block_256(a::jsonb) $$; --! @brief Operator wrapper for eql_v3.text_ord. --! @param a eql_v3.text_ord diff --git a/tests/codegen/reference/text/text_ord_ore_functions.sql b/tests/codegen/reference/text/text_ord_ore_functions.sql index 58e1abac..e541eee9 100644 --- a/tests/codegen/reference/text/text_ord_ore_functions.sql +++ b/tests/codegen/reference/text/text_ord_ore_functions.sql @@ -4,8 +4,8 @@ -- REQUIRE: src/v3/scalars/text/text_types.sql -- REQUIRE: src/v3/scalars/functions.sql -- REQUIRE: src/v3/sem/hmac_256/functions.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/functions.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/operators.sql +-- REQUIRE: src/v3/sem/ore_block_256/functions.sql +-- REQUIRE: src/v3/sem/ore_block_256/operators.sql --! @file encrypted_domain/text/text_ord_ore_functions.sql --! @brief Functions for eql_v3.text_ord_ore. @@ -20,11 +20,11 @@ AS $$ SELECT eql_v3.hmac_256(a::jsonb) $$; --! @brief Index extractor for eql_v3.text_ord_ore. --! @param a eql_v3.text_ord_ore ---! @return eql_v3.ore_block_u64_8_256 +--! @return eql_v3.ore_block_256 CREATE FUNCTION eql_v3.ord_term(a eql_v3.text_ord_ore) -RETURNS eql_v3.ore_block_u64_8_256 +RETURNS eql_v3.ore_block_256 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE -AS $$ SELECT eql_v3.ore_block_u64_8_256(a::jsonb) $$; +AS $$ SELECT eql_v3.ore_block_256(a::jsonb) $$; --! @brief Operator wrapper for eql_v3.text_ord_ore. --! @param a eql_v3.text_ord_ore diff --git a/tests/codegen/reference/text/text_search_functions.sql b/tests/codegen/reference/text/text_search_functions.sql index 21dd5793..3146f152 100644 --- a/tests/codegen/reference/text/text_search_functions.sql +++ b/tests/codegen/reference/text/text_search_functions.sql @@ -4,8 +4,8 @@ -- REQUIRE: src/v3/scalars/text/text_types.sql -- REQUIRE: src/v3/scalars/functions.sql -- REQUIRE: src/v3/sem/hmac_256/functions.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/functions.sql --- REQUIRE: src/v3/sem/ore_block_u64_8_256/operators.sql +-- REQUIRE: src/v3/sem/ore_block_256/functions.sql +-- REQUIRE: src/v3/sem/ore_block_256/operators.sql -- REQUIRE: src/v3/sem/bloom_filter/functions.sql --! @file encrypted_domain/text/text_search_functions.sql @@ -21,11 +21,11 @@ AS $$ SELECT eql_v3.hmac_256(a::jsonb) $$; --! @brief Index extractor for eql_v3.text_search. --! @param a eql_v3.text_search ---! @return eql_v3.ore_block_u64_8_256 +--! @return eql_v3.ore_block_256 CREATE FUNCTION eql_v3.ord_term(a eql_v3.text_search) -RETURNS eql_v3.ore_block_u64_8_256 +RETURNS eql_v3.ore_block_256 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE -AS $$ SELECT eql_v3.ore_block_u64_8_256(a::jsonb) $$; +AS $$ SELECT eql_v3.ore_block_256(a::jsonb) $$; --! @brief Index extractor for eql_v3.text_search. --! @param a eql_v3.text_search diff --git a/tests/codegen/reference/timestamptz/timestamptz_ord_aggregates.sql b/tests/codegen/reference/timestamptz/timestamptz_ord_aggregates.sql new file mode 100644 index 00000000..637fb338 --- /dev/null +++ b/tests/codegen/reference/timestamptz/timestamptz_ord_aggregates.sql @@ -0,0 +1,63 @@ +-- REFERENCE: hand-maintained parity baseline for crates/eql-codegen - see ../README.md +-- AUTOMATICALLY GENERATED FILE. +-- REQUIRE: src/v3/schema.sql +-- REQUIRE: src/v3/scalars/timestamptz/timestamptz_types.sql +-- REQUIRE: src/v3/scalars/timestamptz/timestamptz_ord_functions.sql +-- REQUIRE: src/v3/scalars/timestamptz/timestamptz_ord_operators.sql + +--! @file encrypted_domain/timestamptz/timestamptz_ord_aggregates.sql +--! @brief Aggregates for eql_v3.timestamptz_ord. + +--! @brief State function for min on eql_v3.timestamptz_ord. +--! @param state eql_v3.timestamptz_ord +--! @param value eql_v3.timestamptz_ord +--! @return eql_v3.timestamptz_ord +CREATE FUNCTION eql_v3.min_sfunc(state eql_v3.timestamptz_ord, value eql_v3.timestamptz_ord) +RETURNS eql_v3.timestamptz_ord +LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE +SET search_path = pg_catalog, extensions, public +AS $$ +BEGIN + IF value < state THEN + RETURN value; + END IF; + RETURN state; +END; +$$; + +--! @brief min aggregate for eql_v3.timestamptz_ord. +--! @param input eql_v3.timestamptz_ord +--! @return eql_v3.timestamptz_ord +CREATE AGGREGATE eql_v3.min(eql_v3.timestamptz_ord) ( + sfunc = eql_v3.min_sfunc, + stype = eql_v3.timestamptz_ord, + combinefunc = eql_v3.min_sfunc, + parallel = safe +); + +--! @brief State function for max on eql_v3.timestamptz_ord. +--! @param state eql_v3.timestamptz_ord +--! @param value eql_v3.timestamptz_ord +--! @return eql_v3.timestamptz_ord +CREATE FUNCTION eql_v3.max_sfunc(state eql_v3.timestamptz_ord, value eql_v3.timestamptz_ord) +RETURNS eql_v3.timestamptz_ord +LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE +SET search_path = pg_catalog, extensions, public +AS $$ +BEGIN + IF value > state THEN + RETURN value; + END IF; + RETURN state; +END; +$$; + +--! @brief max aggregate for eql_v3.timestamptz_ord. +--! @param input eql_v3.timestamptz_ord +--! @return eql_v3.timestamptz_ord +CREATE AGGREGATE eql_v3.max(eql_v3.timestamptz_ord) ( + sfunc = eql_v3.max_sfunc, + stype = eql_v3.timestamptz_ord, + combinefunc = eql_v3.max_sfunc, + parallel = safe +); diff --git a/tests/codegen/reference/timestamptz/timestamptz_ord_functions.sql b/tests/codegen/reference/timestamptz/timestamptz_ord_functions.sql new file mode 100644 index 00000000..518e8e8d --- /dev/null +++ b/tests/codegen/reference/timestamptz/timestamptz_ord_functions.sql @@ -0,0 +1,396 @@ +-- REFERENCE: hand-maintained parity baseline for crates/eql-codegen - see ../README.md +-- AUTOMATICALLY GENERATED FILE. +-- REQUIRE: src/v3/schema.sql +-- REQUIRE: src/v3/scalars/timestamptz/timestamptz_types.sql +-- REQUIRE: src/v3/scalars/functions.sql +-- REQUIRE: src/v3/sem/ore_block_256/functions.sql +-- REQUIRE: src/v3/sem/ore_block_256/operators.sql + +--! @file encrypted_domain/timestamptz/timestamptz_ord_functions.sql +--! @brief Functions for eql_v3.timestamptz_ord. + +--! @brief Index extractor for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @return eql_v3.ore_block_256 +CREATE FUNCTION eql_v3.ord_term(a eql_v3.timestamptz_ord) +RETURNS eql_v3.ore_block_256 +LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ore_block_256(a::jsonb) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b eql_v3.timestamptz_ord +--! @return boolean +CREATE FUNCTION eql_v3.eq(a eql_v3.timestamptz_ord, b eql_v3.timestamptz_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) = eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.eq(a eql_v3.timestamptz_ord, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) = eql_v3.ord_term(b::eql_v3.timestamptz_ord) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord. +--! @param a jsonb +--! @param b eql_v3.timestamptz_ord +--! @return boolean +CREATE FUNCTION eql_v3.eq(a jsonb, b eql_v3.timestamptz_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.timestamptz_ord) = eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b eql_v3.timestamptz_ord +--! @return boolean +CREATE FUNCTION eql_v3.neq(a eql_v3.timestamptz_ord, b eql_v3.timestamptz_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) <> eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.neq(a eql_v3.timestamptz_ord, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) <> eql_v3.ord_term(b::eql_v3.timestamptz_ord) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord. +--! @param a jsonb +--! @param b eql_v3.timestamptz_ord +--! @return boolean +CREATE FUNCTION eql_v3.neq(a jsonb, b eql_v3.timestamptz_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.timestamptz_ord) <> eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b eql_v3.timestamptz_ord +--! @return boolean +CREATE FUNCTION eql_v3.lt(a eql_v3.timestamptz_ord, b eql_v3.timestamptz_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) < eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.lt(a eql_v3.timestamptz_ord, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) < eql_v3.ord_term(b::eql_v3.timestamptz_ord) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord. +--! @param a jsonb +--! @param b eql_v3.timestamptz_ord +--! @return boolean +CREATE FUNCTION eql_v3.lt(a jsonb, b eql_v3.timestamptz_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.timestamptz_ord) < eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b eql_v3.timestamptz_ord +--! @return boolean +CREATE FUNCTION eql_v3.lte(a eql_v3.timestamptz_ord, b eql_v3.timestamptz_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) <= eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.lte(a eql_v3.timestamptz_ord, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) <= eql_v3.ord_term(b::eql_v3.timestamptz_ord) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord. +--! @param a jsonb +--! @param b eql_v3.timestamptz_ord +--! @return boolean +CREATE FUNCTION eql_v3.lte(a jsonb, b eql_v3.timestamptz_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.timestamptz_ord) <= eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b eql_v3.timestamptz_ord +--! @return boolean +CREATE FUNCTION eql_v3.gt(a eql_v3.timestamptz_ord, b eql_v3.timestamptz_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) > eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.gt(a eql_v3.timestamptz_ord, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) > eql_v3.ord_term(b::eql_v3.timestamptz_ord) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord. +--! @param a jsonb +--! @param b eql_v3.timestamptz_ord +--! @return boolean +CREATE FUNCTION eql_v3.gt(a jsonb, b eql_v3.timestamptz_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.timestamptz_ord) > eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b eql_v3.timestamptz_ord +--! @return boolean +CREATE FUNCTION eql_v3.gte(a eql_v3.timestamptz_ord, b eql_v3.timestamptz_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) >= eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.gte(a eql_v3.timestamptz_ord, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) >= eql_v3.ord_term(b::eql_v3.timestamptz_ord) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord. +--! @param a jsonb +--! @param b eql_v3.timestamptz_ord +--! @return boolean +CREATE FUNCTION eql_v3.gte(a jsonb, b eql_v3.timestamptz_ord) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.timestamptz_ord) >= eql_v3.ord_term(b) $$; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b eql_v3.timestamptz_ord +--! @return boolean +CREATE FUNCTION eql_v3.contains(a eql_v3.timestamptz_ord, b eql_v3.timestamptz_ord) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@>', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.contains(a eql_v3.timestamptz_ord, b jsonb) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@>', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a jsonb +--! @param b eql_v3.timestamptz_ord +--! @return boolean +CREATE FUNCTION eql_v3.contains(a jsonb, b eql_v3.timestamptz_ord) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@>', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b eql_v3.timestamptz_ord +--! @return boolean +CREATE FUNCTION eql_v3.contained_by(a eql_v3.timestamptz_ord, b eql_v3.timestamptz_ord) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<@', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.contained_by(a eql_v3.timestamptz_ord, b jsonb) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<@', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a jsonb +--! @param b eql_v3.timestamptz_ord +--! @return boolean +CREATE FUNCTION eql_v3.contained_by(a jsonb, b eql_v3.timestamptz_ord) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<@', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param selector text +--! @return eql_v3.timestamptz_ord +CREATE FUNCTION eql_v3."->"(a eql_v3.timestamptz_ord, selector text) +RETURNS eql_v3.timestamptz_ord IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param selector integer +--! @return eql_v3.timestamptz_ord +CREATE FUNCTION eql_v3."->"(a eql_v3.timestamptz_ord, selector integer) +RETURNS eql_v3.timestamptz_ord IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a jsonb +--! @param selector eql_v3.timestamptz_ord +--! @return eql_v3.timestamptz_ord +CREATE FUNCTION eql_v3."->"(a jsonb, selector eql_v3.timestamptz_ord) +RETURNS eql_v3.timestamptz_ord IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param selector text +--! @return text +CREATE FUNCTION eql_v3."->>"(a eql_v3.timestamptz_ord, selector text) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->>', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param selector integer +--! @return text +CREATE FUNCTION eql_v3."->>"(a eql_v3.timestamptz_ord, selector integer) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->>', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a jsonb +--! @param selector eql_v3.timestamptz_ord +--! @return text +CREATE FUNCTION eql_v3."->>"(a jsonb, selector eql_v3.timestamptz_ord) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->>', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b text +--! @return boolean +CREATE FUNCTION eql_v3."?"(a eql_v3.timestamptz_ord, b text) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '?', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b text[] +--! @return boolean +CREATE FUNCTION eql_v3."?|"(a eql_v3.timestamptz_ord, b text[]) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '?|', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b text[] +--! @return boolean +CREATE FUNCTION eql_v3."?&"(a eql_v3.timestamptz_ord, b text[]) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '?&', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b jsonpath +--! @return boolean +CREATE FUNCTION eql_v3."@?"(a eql_v3.timestamptz_ord, b jsonpath) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@?', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b jsonpath +--! @return boolean +CREATE FUNCTION eql_v3."@@"(a eql_v3.timestamptz_ord, b jsonpath) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@@', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b text[] +--! @return jsonb +CREATE FUNCTION eql_v3."#>"(a eql_v3.timestamptz_ord, b text[]) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '#>', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b text[] +--! @return text +CREATE FUNCTION eql_v3."#>>"(a eql_v3.timestamptz_ord, b text[]) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '#>>', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b text +--! @return jsonb +CREATE FUNCTION eql_v3."-"(a eql_v3.timestamptz_ord, b text) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '-', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b integer +--! @return jsonb +CREATE FUNCTION eql_v3."-"(a eql_v3.timestamptz_ord, b integer) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '-', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b text[] +--! @return jsonb +CREATE FUNCTION eql_v3."-"(a eql_v3.timestamptz_ord, b text[]) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '-', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b text[] +--! @return jsonb +CREATE FUNCTION eql_v3."#-"(a eql_v3.timestamptz_ord, b text[]) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '#-', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b eql_v3.timestamptz_ord +--! @return jsonb +CREATE FUNCTION eql_v3."||"(a eql_v3.timestamptz_ord, b eql_v3.timestamptz_ord) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '||', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a eql_v3.timestamptz_ord +--! @param b jsonb +--! @return jsonb +CREATE FUNCTION eql_v3."||"(a eql_v3.timestamptz_ord, b jsonb) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '||', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord. +--! @param a jsonb +--! @param b eql_v3.timestamptz_ord +--! @return jsonb +CREATE FUNCTION eql_v3."||"(a jsonb, b eql_v3.timestamptz_ord) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '||', 'eql_v3.timestamptz_ord'; END; $$ +LANGUAGE plpgsql; diff --git a/tests/codegen/reference/timestamptz/timestamptz_ord_operators.sql b/tests/codegen/reference/timestamptz/timestamptz_ord_operators.sql new file mode 100644 index 00000000..3a05729b --- /dev/null +++ b/tests/codegen/reference/timestamptz/timestamptz_ord_operators.sql @@ -0,0 +1,246 @@ +-- REFERENCE: hand-maintained parity baseline for crates/eql-codegen - see ../README.md +-- AUTOMATICALLY GENERATED FILE. +-- REQUIRE: src/v3/schema.sql +-- REQUIRE: src/v3/scalars/timestamptz/timestamptz_types.sql +-- REQUIRE: src/v3/scalars/timestamptz/timestamptz_ord_functions.sql + +--! @file encrypted_domain/timestamptz/timestamptz_ord_operators.sql +--! @brief Operators for eql_v3.timestamptz_ord. + +CREATE OPERATOR = ( + FUNCTION = eql_v3.eq, + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = eql_v3.timestamptz_ord, + COMMUTATOR = =, NEGATOR = <>, RESTRICT = eqsel, JOIN = eqjoinsel +); + +CREATE OPERATOR = ( + FUNCTION = eql_v3.eq, + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = jsonb, + COMMUTATOR = =, NEGATOR = <>, RESTRICT = eqsel, JOIN = eqjoinsel +); + +CREATE OPERATOR = ( + FUNCTION = eql_v3.eq, + LEFTARG = jsonb, RIGHTARG = eql_v3.timestamptz_ord, + COMMUTATOR = =, NEGATOR = <>, RESTRICT = eqsel, JOIN = eqjoinsel +); + +CREATE OPERATOR <> ( + FUNCTION = eql_v3.neq, + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = eql_v3.timestamptz_ord, + COMMUTATOR = <>, NEGATOR = =, RESTRICT = neqsel, JOIN = neqjoinsel +); + +CREATE OPERATOR <> ( + FUNCTION = eql_v3.neq, + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = jsonb, + COMMUTATOR = <>, NEGATOR = =, RESTRICT = neqsel, JOIN = neqjoinsel +); + +CREATE OPERATOR <> ( + FUNCTION = eql_v3.neq, + LEFTARG = jsonb, RIGHTARG = eql_v3.timestamptz_ord, + COMMUTATOR = <>, NEGATOR = =, RESTRICT = neqsel, JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + FUNCTION = eql_v3.lt, + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = eql_v3.timestamptz_ord, + COMMUTATOR = >, NEGATOR = >=, RESTRICT = scalarltsel, JOIN = scalarltjoinsel +); + +CREATE OPERATOR < ( + FUNCTION = eql_v3.lt, + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = jsonb, + COMMUTATOR = >, NEGATOR = >=, RESTRICT = scalarltsel, JOIN = scalarltjoinsel +); + +CREATE OPERATOR < ( + FUNCTION = eql_v3.lt, + LEFTARG = jsonb, RIGHTARG = eql_v3.timestamptz_ord, + COMMUTATOR = >, NEGATOR = >=, RESTRICT = scalarltsel, JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + FUNCTION = eql_v3.lte, + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = eql_v3.timestamptz_ord, + COMMUTATOR = >=, NEGATOR = >, RESTRICT = scalarlesel, JOIN = scalarlejoinsel +); + +CREATE OPERATOR <= ( + FUNCTION = eql_v3.lte, + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = jsonb, + COMMUTATOR = >=, NEGATOR = >, RESTRICT = scalarlesel, JOIN = scalarlejoinsel +); + +CREATE OPERATOR <= ( + FUNCTION = eql_v3.lte, + LEFTARG = jsonb, RIGHTARG = eql_v3.timestamptz_ord, + COMMUTATOR = >=, NEGATOR = >, RESTRICT = scalarlesel, JOIN = scalarlejoinsel +); + +CREATE OPERATOR > ( + FUNCTION = eql_v3.gt, + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = eql_v3.timestamptz_ord, + COMMUTATOR = <, NEGATOR = <=, RESTRICT = scalargtsel, JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + FUNCTION = eql_v3.gt, + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = jsonb, + COMMUTATOR = <, NEGATOR = <=, RESTRICT = scalargtsel, JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + FUNCTION = eql_v3.gt, + LEFTARG = jsonb, RIGHTARG = eql_v3.timestamptz_ord, + COMMUTATOR = <, NEGATOR = <=, RESTRICT = scalargtsel, JOIN = scalargtjoinsel +); + +CREATE OPERATOR >= ( + FUNCTION = eql_v3.gte, + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = eql_v3.timestamptz_ord, + COMMUTATOR = <=, NEGATOR = <, RESTRICT = scalargesel, JOIN = scalargejoinsel +); + +CREATE OPERATOR >= ( + FUNCTION = eql_v3.gte, + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = jsonb, + COMMUTATOR = <=, NEGATOR = <, RESTRICT = scalargesel, JOIN = scalargejoinsel +); + +CREATE OPERATOR >= ( + FUNCTION = eql_v3.gte, + LEFTARG = jsonb, RIGHTARG = eql_v3.timestamptz_ord, + COMMUTATOR = <=, NEGATOR = <, RESTRICT = scalargesel, JOIN = scalargejoinsel +); + +CREATE OPERATOR @> ( + FUNCTION = eql_v3.contains, + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = eql_v3.timestamptz_ord +); + +CREATE OPERATOR @> ( + FUNCTION = eql_v3.contains, + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = jsonb +); + +CREATE OPERATOR @> ( + FUNCTION = eql_v3.contains, + LEFTARG = jsonb, RIGHTARG = eql_v3.timestamptz_ord +); + +CREATE OPERATOR <@ ( + FUNCTION = eql_v3.contained_by, + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = eql_v3.timestamptz_ord +); + +CREATE OPERATOR <@ ( + FUNCTION = eql_v3.contained_by, + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = jsonb +); + +CREATE OPERATOR <@ ( + FUNCTION = eql_v3.contained_by, + LEFTARG = jsonb, RIGHTARG = eql_v3.timestamptz_ord +); + +CREATE OPERATOR -> ( + FUNCTION = eql_v3."->", + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = text +); + +CREATE OPERATOR -> ( + FUNCTION = eql_v3."->", + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = integer +); + +CREATE OPERATOR -> ( + FUNCTION = eql_v3."->", + LEFTARG = jsonb, RIGHTARG = eql_v3.timestamptz_ord +); + +CREATE OPERATOR ->> ( + FUNCTION = eql_v3."->>", + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = text +); + +CREATE OPERATOR ->> ( + FUNCTION = eql_v3."->>", + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = integer +); + +CREATE OPERATOR ->> ( + FUNCTION = eql_v3."->>", + LEFTARG = jsonb, RIGHTARG = eql_v3.timestamptz_ord +); + +CREATE OPERATOR ? ( + FUNCTION = eql_v3."?", + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = text +); + +CREATE OPERATOR ?| ( + FUNCTION = eql_v3."?|", + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = text[] +); + +CREATE OPERATOR ?& ( + FUNCTION = eql_v3."?&", + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = text[] +); + +CREATE OPERATOR @? ( + FUNCTION = eql_v3."@?", + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = jsonpath +); + +CREATE OPERATOR @@ ( + FUNCTION = eql_v3."@@", + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = jsonpath +); + +CREATE OPERATOR #> ( + FUNCTION = eql_v3."#>", + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = text[] +); + +CREATE OPERATOR #>> ( + FUNCTION = eql_v3."#>>", + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = text[] +); + +CREATE OPERATOR - ( + FUNCTION = eql_v3."-", + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = text +); + +CREATE OPERATOR - ( + FUNCTION = eql_v3."-", + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = integer +); + +CREATE OPERATOR - ( + FUNCTION = eql_v3."-", + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = text[] +); + +CREATE OPERATOR #- ( + FUNCTION = eql_v3."#-", + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = text[] +); + +CREATE OPERATOR || ( + FUNCTION = eql_v3."||", + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = eql_v3.timestamptz_ord +); + +CREATE OPERATOR || ( + FUNCTION = eql_v3."||", + LEFTARG = eql_v3.timestamptz_ord, RIGHTARG = jsonb +); + +CREATE OPERATOR || ( + FUNCTION = eql_v3."||", + LEFTARG = jsonb, RIGHTARG = eql_v3.timestamptz_ord +); diff --git a/tests/codegen/reference/timestamptz/timestamptz_ord_ore_aggregates.sql b/tests/codegen/reference/timestamptz/timestamptz_ord_ore_aggregates.sql new file mode 100644 index 00000000..73bae985 --- /dev/null +++ b/tests/codegen/reference/timestamptz/timestamptz_ord_ore_aggregates.sql @@ -0,0 +1,63 @@ +-- REFERENCE: hand-maintained parity baseline for crates/eql-codegen - see ../README.md +-- AUTOMATICALLY GENERATED FILE. +-- REQUIRE: src/v3/schema.sql +-- REQUIRE: src/v3/scalars/timestamptz/timestamptz_types.sql +-- REQUIRE: src/v3/scalars/timestamptz/timestamptz_ord_ore_functions.sql +-- REQUIRE: src/v3/scalars/timestamptz/timestamptz_ord_ore_operators.sql + +--! @file encrypted_domain/timestamptz/timestamptz_ord_ore_aggregates.sql +--! @brief Aggregates for eql_v3.timestamptz_ord_ore. + +--! @brief State function for min on eql_v3.timestamptz_ord_ore. +--! @param state eql_v3.timestamptz_ord_ore +--! @param value eql_v3.timestamptz_ord_ore +--! @return eql_v3.timestamptz_ord_ore +CREATE FUNCTION eql_v3.min_sfunc(state eql_v3.timestamptz_ord_ore, value eql_v3.timestamptz_ord_ore) +RETURNS eql_v3.timestamptz_ord_ore +LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE +SET search_path = pg_catalog, extensions, public +AS $$ +BEGIN + IF value < state THEN + RETURN value; + END IF; + RETURN state; +END; +$$; + +--! @brief min aggregate for eql_v3.timestamptz_ord_ore. +--! @param input eql_v3.timestamptz_ord_ore +--! @return eql_v3.timestamptz_ord_ore +CREATE AGGREGATE eql_v3.min(eql_v3.timestamptz_ord_ore) ( + sfunc = eql_v3.min_sfunc, + stype = eql_v3.timestamptz_ord_ore, + combinefunc = eql_v3.min_sfunc, + parallel = safe +); + +--! @brief State function for max on eql_v3.timestamptz_ord_ore. +--! @param state eql_v3.timestamptz_ord_ore +--! @param value eql_v3.timestamptz_ord_ore +--! @return eql_v3.timestamptz_ord_ore +CREATE FUNCTION eql_v3.max_sfunc(state eql_v3.timestamptz_ord_ore, value eql_v3.timestamptz_ord_ore) +RETURNS eql_v3.timestamptz_ord_ore +LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE +SET search_path = pg_catalog, extensions, public +AS $$ +BEGIN + IF value > state THEN + RETURN value; + END IF; + RETURN state; +END; +$$; + +--! @brief max aggregate for eql_v3.timestamptz_ord_ore. +--! @param input eql_v3.timestamptz_ord_ore +--! @return eql_v3.timestamptz_ord_ore +CREATE AGGREGATE eql_v3.max(eql_v3.timestamptz_ord_ore) ( + sfunc = eql_v3.max_sfunc, + stype = eql_v3.timestamptz_ord_ore, + combinefunc = eql_v3.max_sfunc, + parallel = safe +); diff --git a/tests/codegen/reference/timestamptz/timestamptz_ord_ore_functions.sql b/tests/codegen/reference/timestamptz/timestamptz_ord_ore_functions.sql new file mode 100644 index 00000000..c93ddef7 --- /dev/null +++ b/tests/codegen/reference/timestamptz/timestamptz_ord_ore_functions.sql @@ -0,0 +1,396 @@ +-- REFERENCE: hand-maintained parity baseline for crates/eql-codegen - see ../README.md +-- AUTOMATICALLY GENERATED FILE. +-- REQUIRE: src/v3/schema.sql +-- REQUIRE: src/v3/scalars/timestamptz/timestamptz_types.sql +-- REQUIRE: src/v3/scalars/functions.sql +-- REQUIRE: src/v3/sem/ore_block_256/functions.sql +-- REQUIRE: src/v3/sem/ore_block_256/operators.sql + +--! @file encrypted_domain/timestamptz/timestamptz_ord_ore_functions.sql +--! @brief Functions for eql_v3.timestamptz_ord_ore. + +--! @brief Index extractor for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @return eql_v3.ore_block_256 +CREATE FUNCTION eql_v3.ord_term(a eql_v3.timestamptz_ord_ore) +RETURNS eql_v3.ore_block_256 +LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ore_block_256(a::jsonb) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b eql_v3.timestamptz_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.eq(a eql_v3.timestamptz_ord_ore, b eql_v3.timestamptz_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) = eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.eq(a eql_v3.timestamptz_ord_ore, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) = eql_v3.ord_term(b::eql_v3.timestamptz_ord_ore) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord_ore. +--! @param a jsonb +--! @param b eql_v3.timestamptz_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.eq(a jsonb, b eql_v3.timestamptz_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.timestamptz_ord_ore) = eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b eql_v3.timestamptz_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.neq(a eql_v3.timestamptz_ord_ore, b eql_v3.timestamptz_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) <> eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.neq(a eql_v3.timestamptz_ord_ore, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) <> eql_v3.ord_term(b::eql_v3.timestamptz_ord_ore) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord_ore. +--! @param a jsonb +--! @param b eql_v3.timestamptz_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.neq(a jsonb, b eql_v3.timestamptz_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.timestamptz_ord_ore) <> eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b eql_v3.timestamptz_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.lt(a eql_v3.timestamptz_ord_ore, b eql_v3.timestamptz_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) < eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.lt(a eql_v3.timestamptz_ord_ore, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) < eql_v3.ord_term(b::eql_v3.timestamptz_ord_ore) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord_ore. +--! @param a jsonb +--! @param b eql_v3.timestamptz_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.lt(a jsonb, b eql_v3.timestamptz_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.timestamptz_ord_ore) < eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b eql_v3.timestamptz_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.lte(a eql_v3.timestamptz_ord_ore, b eql_v3.timestamptz_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) <= eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.lte(a eql_v3.timestamptz_ord_ore, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) <= eql_v3.ord_term(b::eql_v3.timestamptz_ord_ore) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord_ore. +--! @param a jsonb +--! @param b eql_v3.timestamptz_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.lte(a jsonb, b eql_v3.timestamptz_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.timestamptz_ord_ore) <= eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b eql_v3.timestamptz_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.gt(a eql_v3.timestamptz_ord_ore, b eql_v3.timestamptz_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) > eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.gt(a eql_v3.timestamptz_ord_ore, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) > eql_v3.ord_term(b::eql_v3.timestamptz_ord_ore) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord_ore. +--! @param a jsonb +--! @param b eql_v3.timestamptz_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.gt(a jsonb, b eql_v3.timestamptz_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.timestamptz_ord_ore) > eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b eql_v3.timestamptz_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.gte(a eql_v3.timestamptz_ord_ore, b eql_v3.timestamptz_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) >= eql_v3.ord_term(b) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.gte(a eql_v3.timestamptz_ord_ore, b jsonb) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a) >= eql_v3.ord_term(b::eql_v3.timestamptz_ord_ore) $$; + +--! @brief Operator wrapper for eql_v3.timestamptz_ord_ore. +--! @param a jsonb +--! @param b eql_v3.timestamptz_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.gte(a jsonb, b eql_v3.timestamptz_ord_ore) +RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE +AS $$ SELECT eql_v3.ord_term(a::eql_v3.timestamptz_ord_ore) >= eql_v3.ord_term(b) $$; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b eql_v3.timestamptz_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.contains(a eql_v3.timestamptz_ord_ore, b eql_v3.timestamptz_ord_ore) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@>', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.contains(a eql_v3.timestamptz_ord_ore, b jsonb) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@>', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a jsonb +--! @param b eql_v3.timestamptz_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.contains(a jsonb, b eql_v3.timestamptz_ord_ore) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@>', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b eql_v3.timestamptz_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.contained_by(a eql_v3.timestamptz_ord_ore, b eql_v3.timestamptz_ord_ore) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<@', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b jsonb +--! @return boolean +CREATE FUNCTION eql_v3.contained_by(a eql_v3.timestamptz_ord_ore, b jsonb) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<@', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a jsonb +--! @param b eql_v3.timestamptz_ord_ore +--! @return boolean +CREATE FUNCTION eql_v3.contained_by(a jsonb, b eql_v3.timestamptz_ord_ore) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '<@', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param selector text +--! @return eql_v3.timestamptz_ord_ore +CREATE FUNCTION eql_v3."->"(a eql_v3.timestamptz_ord_ore, selector text) +RETURNS eql_v3.timestamptz_ord_ore IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param selector integer +--! @return eql_v3.timestamptz_ord_ore +CREATE FUNCTION eql_v3."->"(a eql_v3.timestamptz_ord_ore, selector integer) +RETURNS eql_v3.timestamptz_ord_ore IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a jsonb +--! @param selector eql_v3.timestamptz_ord_ore +--! @return eql_v3.timestamptz_ord_ore +CREATE FUNCTION eql_v3."->"(a jsonb, selector eql_v3.timestamptz_ord_ore) +RETURNS eql_v3.timestamptz_ord_ore IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param selector text +--! @return text +CREATE FUNCTION eql_v3."->>"(a eql_v3.timestamptz_ord_ore, selector text) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->>', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param selector integer +--! @return text +CREATE FUNCTION eql_v3."->>"(a eql_v3.timestamptz_ord_ore, selector integer) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->>', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a jsonb +--! @param selector eql_v3.timestamptz_ord_ore +--! @return text +CREATE FUNCTION eql_v3."->>"(a jsonb, selector eql_v3.timestamptz_ord_ore) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '->>', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b text +--! @return boolean +CREATE FUNCTION eql_v3."?"(a eql_v3.timestamptz_ord_ore, b text) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '?', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b text[] +--! @return boolean +CREATE FUNCTION eql_v3."?|"(a eql_v3.timestamptz_ord_ore, b text[]) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '?|', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b text[] +--! @return boolean +CREATE FUNCTION eql_v3."?&"(a eql_v3.timestamptz_ord_ore, b text[]) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '?&', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b jsonpath +--! @return boolean +CREATE FUNCTION eql_v3."@?"(a eql_v3.timestamptz_ord_ore, b jsonpath) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@?', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b jsonpath +--! @return boolean +CREATE FUNCTION eql_v3."@@"(a eql_v3.timestamptz_ord_ore, b jsonpath) +RETURNS boolean IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '@@', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b text[] +--! @return jsonb +CREATE FUNCTION eql_v3."#>"(a eql_v3.timestamptz_ord_ore, b text[]) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '#>', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b text[] +--! @return text +CREATE FUNCTION eql_v3."#>>"(a eql_v3.timestamptz_ord_ore, b text[]) +RETURNS text IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '#>>', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b text +--! @return jsonb +CREATE FUNCTION eql_v3."-"(a eql_v3.timestamptz_ord_ore, b text) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '-', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b integer +--! @return jsonb +CREATE FUNCTION eql_v3."-"(a eql_v3.timestamptz_ord_ore, b integer) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '-', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b text[] +--! @return jsonb +CREATE FUNCTION eql_v3."-"(a eql_v3.timestamptz_ord_ore, b text[]) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '-', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b text[] +--! @return jsonb +CREATE FUNCTION eql_v3."#-"(a eql_v3.timestamptz_ord_ore, b text[]) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '#-', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b eql_v3.timestamptz_ord_ore +--! @return jsonb +CREATE FUNCTION eql_v3."||"(a eql_v3.timestamptz_ord_ore, b eql_v3.timestamptz_ord_ore) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '||', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a eql_v3.timestamptz_ord_ore +--! @param b jsonb +--! @return jsonb +CREATE FUNCTION eql_v3."||"(a eql_v3.timestamptz_ord_ore, b jsonb) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '||', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; + +--! @brief Unsupported operator blocker for eql_v3.timestamptz_ord_ore. +--! @param a jsonb +--! @param b eql_v3.timestamptz_ord_ore +--! @return jsonb +CREATE FUNCTION eql_v3."||"(a jsonb, b eql_v3.timestamptz_ord_ore) +RETURNS jsonb IMMUTABLE PARALLEL SAFE +AS $$ BEGIN RAISE EXCEPTION 'operator % is not supported for %', '||', 'eql_v3.timestamptz_ord_ore'; END; $$ +LANGUAGE plpgsql; diff --git a/tests/codegen/reference/timestamptz/timestamptz_ord_ore_operators.sql b/tests/codegen/reference/timestamptz/timestamptz_ord_ore_operators.sql new file mode 100644 index 00000000..d5b7980c --- /dev/null +++ b/tests/codegen/reference/timestamptz/timestamptz_ord_ore_operators.sql @@ -0,0 +1,246 @@ +-- REFERENCE: hand-maintained parity baseline for crates/eql-codegen - see ../README.md +-- AUTOMATICALLY GENERATED FILE. +-- REQUIRE: src/v3/schema.sql +-- REQUIRE: src/v3/scalars/timestamptz/timestamptz_types.sql +-- REQUIRE: src/v3/scalars/timestamptz/timestamptz_ord_ore_functions.sql + +--! @file encrypted_domain/timestamptz/timestamptz_ord_ore_operators.sql +--! @brief Operators for eql_v3.timestamptz_ord_ore. + +CREATE OPERATOR = ( + FUNCTION = eql_v3.eq, + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = eql_v3.timestamptz_ord_ore, + COMMUTATOR = =, NEGATOR = <>, RESTRICT = eqsel, JOIN = eqjoinsel +); + +CREATE OPERATOR = ( + FUNCTION = eql_v3.eq, + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = jsonb, + COMMUTATOR = =, NEGATOR = <>, RESTRICT = eqsel, JOIN = eqjoinsel +); + +CREATE OPERATOR = ( + FUNCTION = eql_v3.eq, + LEFTARG = jsonb, RIGHTARG = eql_v3.timestamptz_ord_ore, + COMMUTATOR = =, NEGATOR = <>, RESTRICT = eqsel, JOIN = eqjoinsel +); + +CREATE OPERATOR <> ( + FUNCTION = eql_v3.neq, + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = eql_v3.timestamptz_ord_ore, + COMMUTATOR = <>, NEGATOR = =, RESTRICT = neqsel, JOIN = neqjoinsel +); + +CREATE OPERATOR <> ( + FUNCTION = eql_v3.neq, + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = jsonb, + COMMUTATOR = <>, NEGATOR = =, RESTRICT = neqsel, JOIN = neqjoinsel +); + +CREATE OPERATOR <> ( + FUNCTION = eql_v3.neq, + LEFTARG = jsonb, RIGHTARG = eql_v3.timestamptz_ord_ore, + COMMUTATOR = <>, NEGATOR = =, RESTRICT = neqsel, JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + FUNCTION = eql_v3.lt, + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = eql_v3.timestamptz_ord_ore, + COMMUTATOR = >, NEGATOR = >=, RESTRICT = scalarltsel, JOIN = scalarltjoinsel +); + +CREATE OPERATOR < ( + FUNCTION = eql_v3.lt, + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = jsonb, + COMMUTATOR = >, NEGATOR = >=, RESTRICT = scalarltsel, JOIN = scalarltjoinsel +); + +CREATE OPERATOR < ( + FUNCTION = eql_v3.lt, + LEFTARG = jsonb, RIGHTARG = eql_v3.timestamptz_ord_ore, + COMMUTATOR = >, NEGATOR = >=, RESTRICT = scalarltsel, JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + FUNCTION = eql_v3.lte, + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = eql_v3.timestamptz_ord_ore, + COMMUTATOR = >=, NEGATOR = >, RESTRICT = scalarlesel, JOIN = scalarlejoinsel +); + +CREATE OPERATOR <= ( + FUNCTION = eql_v3.lte, + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = jsonb, + COMMUTATOR = >=, NEGATOR = >, RESTRICT = scalarlesel, JOIN = scalarlejoinsel +); + +CREATE OPERATOR <= ( + FUNCTION = eql_v3.lte, + LEFTARG = jsonb, RIGHTARG = eql_v3.timestamptz_ord_ore, + COMMUTATOR = >=, NEGATOR = >, RESTRICT = scalarlesel, JOIN = scalarlejoinsel +); + +CREATE OPERATOR > ( + FUNCTION = eql_v3.gt, + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = eql_v3.timestamptz_ord_ore, + COMMUTATOR = <, NEGATOR = <=, RESTRICT = scalargtsel, JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + FUNCTION = eql_v3.gt, + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = jsonb, + COMMUTATOR = <, NEGATOR = <=, RESTRICT = scalargtsel, JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + FUNCTION = eql_v3.gt, + LEFTARG = jsonb, RIGHTARG = eql_v3.timestamptz_ord_ore, + COMMUTATOR = <, NEGATOR = <=, RESTRICT = scalargtsel, JOIN = scalargtjoinsel +); + +CREATE OPERATOR >= ( + FUNCTION = eql_v3.gte, + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = eql_v3.timestamptz_ord_ore, + COMMUTATOR = <=, NEGATOR = <, RESTRICT = scalargesel, JOIN = scalargejoinsel +); + +CREATE OPERATOR >= ( + FUNCTION = eql_v3.gte, + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = jsonb, + COMMUTATOR = <=, NEGATOR = <, RESTRICT = scalargesel, JOIN = scalargejoinsel +); + +CREATE OPERATOR >= ( + FUNCTION = eql_v3.gte, + LEFTARG = jsonb, RIGHTARG = eql_v3.timestamptz_ord_ore, + COMMUTATOR = <=, NEGATOR = <, RESTRICT = scalargesel, JOIN = scalargejoinsel +); + +CREATE OPERATOR @> ( + FUNCTION = eql_v3.contains, + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = eql_v3.timestamptz_ord_ore +); + +CREATE OPERATOR @> ( + FUNCTION = eql_v3.contains, + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = jsonb +); + +CREATE OPERATOR @> ( + FUNCTION = eql_v3.contains, + LEFTARG = jsonb, RIGHTARG = eql_v3.timestamptz_ord_ore +); + +CREATE OPERATOR <@ ( + FUNCTION = eql_v3.contained_by, + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = eql_v3.timestamptz_ord_ore +); + +CREATE OPERATOR <@ ( + FUNCTION = eql_v3.contained_by, + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = jsonb +); + +CREATE OPERATOR <@ ( + FUNCTION = eql_v3.contained_by, + LEFTARG = jsonb, RIGHTARG = eql_v3.timestamptz_ord_ore +); + +CREATE OPERATOR -> ( + FUNCTION = eql_v3."->", + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = text +); + +CREATE OPERATOR -> ( + FUNCTION = eql_v3."->", + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = integer +); + +CREATE OPERATOR -> ( + FUNCTION = eql_v3."->", + LEFTARG = jsonb, RIGHTARG = eql_v3.timestamptz_ord_ore +); + +CREATE OPERATOR ->> ( + FUNCTION = eql_v3."->>", + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = text +); + +CREATE OPERATOR ->> ( + FUNCTION = eql_v3."->>", + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = integer +); + +CREATE OPERATOR ->> ( + FUNCTION = eql_v3."->>", + LEFTARG = jsonb, RIGHTARG = eql_v3.timestamptz_ord_ore +); + +CREATE OPERATOR ? ( + FUNCTION = eql_v3."?", + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = text +); + +CREATE OPERATOR ?| ( + FUNCTION = eql_v3."?|", + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = text[] +); + +CREATE OPERATOR ?& ( + FUNCTION = eql_v3."?&", + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = text[] +); + +CREATE OPERATOR @? ( + FUNCTION = eql_v3."@?", + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = jsonpath +); + +CREATE OPERATOR @@ ( + FUNCTION = eql_v3."@@", + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = jsonpath +); + +CREATE OPERATOR #> ( + FUNCTION = eql_v3."#>", + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = text[] +); + +CREATE OPERATOR #>> ( + FUNCTION = eql_v3."#>>", + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = text[] +); + +CREATE OPERATOR - ( + FUNCTION = eql_v3."-", + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = text +); + +CREATE OPERATOR - ( + FUNCTION = eql_v3."-", + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = integer +); + +CREATE OPERATOR - ( + FUNCTION = eql_v3."-", + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = text[] +); + +CREATE OPERATOR #- ( + FUNCTION = eql_v3."#-", + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = text[] +); + +CREATE OPERATOR || ( + FUNCTION = eql_v3."||", + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = eql_v3.timestamptz_ord_ore +); + +CREATE OPERATOR || ( + FUNCTION = eql_v3."||", + LEFTARG = eql_v3.timestamptz_ord_ore, RIGHTARG = jsonb +); + +CREATE OPERATOR || ( + FUNCTION = eql_v3."||", + LEFTARG = jsonb, RIGHTARG = eql_v3.timestamptz_ord_ore +); diff --git a/tests/codegen/reference/timestamptz/timestamptz_types.sql b/tests/codegen/reference/timestamptz/timestamptz_types.sql index 61445e87..38930d16 100644 --- a/tests/codegen/reference/timestamptz/timestamptz_types.sql +++ b/tests/codegen/reference/timestamptz/timestamptz_types.sql @@ -37,5 +37,37 @@ BEGIN AND VALUE->>'v' = '2' ); END IF; + + --! @brief Encrypted domain eql_v3.timestamptz_ord_ore. + IF NOT EXISTS ( + SELECT 1 FROM pg_type + WHERE typname = 'timestamptz_ord_ore' AND typnamespace = 'eql_v3'::regnamespace + ) THEN + CREATE DOMAIN eql_v3.timestamptz_ord_ore AS jsonb + CHECK ( + jsonb_typeof(VALUE) = 'object' + AND VALUE ? 'v' + AND VALUE ? 'i' + AND VALUE ? 'c' + AND VALUE ? 'ob' + AND VALUE->>'v' = '2' + ); + END IF; + + --! @brief Encrypted domain eql_v3.timestamptz_ord. + IF NOT EXISTS ( + SELECT 1 FROM pg_type + WHERE typname = 'timestamptz_ord' AND typnamespace = 'eql_v3'::regnamespace + ) THEN + CREATE DOMAIN eql_v3.timestamptz_ord AS jsonb + CHECK ( + jsonb_typeof(VALUE) = 'object' + AND VALUE ? 'v' + AND VALUE ? 'i' + AND VALUE ? 'c' + AND VALUE ? 'ob' + AND VALUE->>'v' = '2' + ); + END IF; END $$; diff --git a/tests/sqlx/Cargo.toml b/tests/sqlx/Cargo.toml index fbdd9506..a142233c 100644 --- a/tests/sqlx/Cargo.toml +++ b/tests/sqlx/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -sqlx = { version = "0.8", features = ["runtime-tokio", "postgres", "macros", "chrono"] } +sqlx = { version = "0.8", features = ["runtime-tokio", "postgres", "macros", "chrono", "rust_decimal"] } tokio = { version = "1", features = ["full"] } serde = { version = "1", features = ["derive"] } serde_json = "1" @@ -16,6 +16,11 @@ cipherstash-client = { version = "0.35", features = ["tokio"] } # it as a direct dependency so the harness can name `chrono::NaiveDate` for the # `date` scalar (Encode/Decode/Type come from the sqlx `chrono` feature above). chrono = { version = "0.4", default-features = false } +# rust_decimal backs the `numeric` scalar. The sqlx `rust_decimal` feature above +# provides Encode/Decode/Type; this names the type directly for the +# harness impls (scalar_domains.rs, eql_plaintext.rs). Already in the tree +# transitively (cipherstash-client / ore-rs). +rust_decimal = "1" paste = "1" eql-scalars = { path = "../../crates/eql-scalars" } eql-tests-macros = { path = "../../crates/eql-tests-macros" } diff --git a/tests/sqlx/fixtures/drop_operator_classes.sql b/tests/sqlx/fixtures/drop_operator_classes.sql index 094073aa..13d1d350 100644 --- a/tests/sqlx/fixtures/drop_operator_classes.sql +++ b/tests/sqlx/fixtures/drop_operator_classes.sql @@ -20,8 +20,8 @@ DROP OPERATOR FAMILY IF EXISTS eql_v2.ore_block_u64_8_256_operator_family USING -- the `*operator_class.sql` suffix, so the Supabase build's `**/*operator_class.sql` -- glob excludes it as well. Without this the unqualified-name opclass check below -- still finds the eql_v3 copy. -DROP OPERATOR CLASS IF EXISTS eql_v3.ore_block_u64_8_256_operator_class USING btree CASCADE; -DROP OPERATOR FAMILY IF EXISTS eql_v3.ore_block_u64_8_256_operator_family USING btree CASCADE; +DROP OPERATOR CLASS IF EXISTS eql_v3.ore_block_256_operator_class USING btree CASCADE; +DROP OPERATOR FAMILY IF EXISTS eql_v3.ore_block_256_operator_family USING btree CASCADE; -- Drop ore_block_u64_8_256 operators (also excluded from Supabase build) DROP OPERATOR IF EXISTS = (eql_v2.ore_block_u64_8_256, eql_v2.ore_block_u64_8_256) CASCADE; diff --git a/tests/sqlx/src/fixtures/eql_plaintext.rs b/tests/sqlx/src/fixtures/eql_plaintext.rs index 9e85aac5..f447429b 100644 --- a/tests/sqlx/src/fixtures/eql_plaintext.rs +++ b/tests/sqlx/src/fixtures/eql_plaintext.rs @@ -60,6 +60,7 @@ impl PlaintextSqlType { pub const TIMESTAMPTZ: PlaintextSqlType = PlaintextSqlType("timestamp with time zone"); pub const TEXT: PlaintextSqlType = PlaintextSqlType("text"); pub const JSONB: PlaintextSqlType = PlaintextSqlType("jsonb"); + pub const NUMERIC: PlaintextSqlType = PlaintextSqlType("numeric"); pub fn as_str(&self) -> &'static str { self.0 @@ -86,7 +87,8 @@ const fn cast_for_kind(kind: ScalarKind) -> Cast { ScalarKind::Date => Cast::DATE, ScalarKind::Timestamptz => Cast::TIMESTAMP, ScalarKind::Text => Cast::TEXT, - ScalarKind::Numeric | ScalarKind::Jsonb => { + ScalarKind::Numeric => Cast::DECIMAL, + ScalarKind::Jsonb => { panic!("EqlPlaintext is only implemented for the wired scalar kinds") } } @@ -103,7 +105,8 @@ const fn plaintext_sql_type_for_kind(kind: ScalarKind) -> PlaintextSqlType { ScalarKind::Date => PlaintextSqlType::DATE, ScalarKind::Timestamptz => PlaintextSqlType::TIMESTAMPTZ, ScalarKind::Text => PlaintextSqlType::TEXT, - ScalarKind::Numeric | ScalarKind::Jsonb => { + ScalarKind::Numeric => PlaintextSqlType::NUMERIC, + ScalarKind::Jsonb => { panic!("EqlPlaintext is only implemented for the wired scalar kinds") } } @@ -118,6 +121,7 @@ mod sealed { impl Sealed for chrono::DateTime {} impl Sealed for String {} impl Sealed for serde_json::Value {} + impl Sealed for rust_decimal::Decimal {} } /// A Rust type usable as a fixture `plaintext` value, carrying its EQL cast @@ -213,6 +217,14 @@ impl EqlPlaintext for serde_json::Value { } } +impl EqlPlaintext for rust_decimal::Decimal { + const KIND: ScalarKind = ScalarKind::Numeric; + + fn to_plaintext(&self) -> Plaintext { + Plaintext::Decimal(Some(*self)) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/tests/sqlx/src/fixtures/mod.rs b/tests/sqlx/src/fixtures/mod.rs index 098db4f6..62e86713 100644 --- a/tests/sqlx/src/fixtures/mod.rs +++ b/tests/sqlx/src/fixtures/mod.rs @@ -39,6 +39,13 @@ pub mod v3_ste_vec; // jsonb-entry behaviour matrix (`JsonbEntryInt4`). pub mod v3_doc_int4; +// The numeric scale-equivalence collision fixture (`1`, `1.0`, `2`). Not a +// CATALOG scalar — the catalog distinctness guard forbids the value-equal pair +// `1`/`1.0` — so it is hand-written and registered here directly (like the +// other `v3_` fixtures). Gives the `1 == 1.0` ORE collision an always-on +// (committed-fixture) home instead of a creds-gated runtime encryption. +pub mod v3_numeric_collision; + // The per-type scalar fixture modules (`eql_v2_int4`, `eql_v2_int2`, …) are // generated from the harness list in `scalar_types.rs`. Each expands to // `pub mod eql_v2_ { … scalar_fixture! … }`, reading its plaintext values diff --git a/tests/sqlx/src/fixtures/scalar_fixture.rs b/tests/sqlx/src/fixtures/scalar_fixture.rs index 6efcfa3d..b84f0a0d 100644 --- a/tests/sqlx/src/fixtures/scalar_fixture.rs +++ b/tests/sqlx/src/fixtures/scalar_fixture.rs @@ -138,6 +138,37 @@ macro_rules! scalar_fixture { } }; + // Numeric scalars (`rust_decimal::Decimal`): ordered, non-chrono. Same + // shape as `temporal` — `[Unique, Ore]` indexes, pivot-presence asserts via + // `OrderedScalar` — but materialised from owned `Decimal` values (no `Match` + // index, no chrono). + (numeric, $name:literal, $ty:ty, $values:expr $(,)?) => { + $crate::scalar_fixture!(@common $name, $ty, $values, [Unique, Ore]); + + #[cfg(test)] + mod tests { + use super::*; + use $crate::scalar_domains::OrderedScalar; + + #[test] + fn spec_is_complete() { + assert!(spec().check_complete().is_ok()); + } + + #[test] + fn spec_includes_pivots() { + let spec = spec(); + let values = spec.values(); + let min = <$ty as OrderedScalar>::min_pivot(); + let mid = <$ty as OrderedScalar>::mid_pivot(); + let max = <$ty as OrderedScalar>::max_pivot(); + assert!(values.contains(&min), "spec must include min_pivot {min:?}"); + assert!(values.contains(&mid), "spec must include mid_pivot {mid:?}"); + assert!(values.contains(&max), "spec must include max_pivot {max:?}"); + } + } + }; + // Shared expansion: the `spec()` builder + the gated generator test. The // trailing `[Unique, Ore, ...]` token list parametrizes the index set. (@common $name:literal, $ty:ty, $values:expr, [$($ix:ident),+ $(,)?]) => { diff --git a/tests/sqlx/src/fixtures/v3_numeric_collision.rs b/tests/sqlx/src/fixtures/v3_numeric_collision.rs new file mode 100644 index 00000000..fe58c63d --- /dev/null +++ b/tests/sqlx/src/fixtures/v3_numeric_collision.rs @@ -0,0 +1,77 @@ +//! The `v3_numeric_collision` fixture — the scale-equivalence collision pair +//! (`1`, `1.0`) plus a `2` discriminator, encrypted at numeric ORE width. +//! +//! Hand-written, non-catalog (like `v3_ste_vec` / `v3_doc_int4`, hence the +//! `v3_` prefix), because the catalog-driven `eql_v2_numeric` fixture CANNOT +//! carry it: `numeric_value_guards::fixtures_are_distinct_by_value` forbids two +//! fixtures that alias to the same `Decimal`, and `1` / `1.0` are value-equal. +//! So the `1 == 1.0` ORE collision — that scale-equivalent decimals encrypt to +//! comparison-equal ORE terms — has no catalog home. This tiny bespoke fixture +//! is the only place those two representations can coexist. +//! +//! Rows are addressed by `id` (NOT `plaintext`): `WHERE plaintext = 1` matches +//! both `1` and `1.0` (numeric equality ignores scale), so the collision test +//! fetches `id = 1` (`1`), `id = 2` (`1.0`), `id = 3` (`2`). The `id` is the +//! 1-based insertion ordinal the driver assigns over `VALUES`. +//! +//! Gitignored output: tests/sqlx/fixtures/v3_numeric_collision.sql +//! (regenerated by `mise run fixture:generate:all`). + +use anyhow::Result; +use rust_decimal::Decimal; +use std::str::FromStr; + +use super::index_kind::IndexKind; +use super::spec::FixtureSpec; + +/// The committed fixture name → table `fixtures.v3_numeric_collision`, script +/// `v3_numeric_collision.sql`, SQLx ref `scripts("v3_numeric_collision")`. +const NAME: &str = "v3_numeric_collision"; + +/// The fixture plaintexts, in insertion order. `id` is the 1-based ordinal, so +/// `1` → id 1, `1.0` → id 2, `2` → id 3. `1` and `1.0` are deliberately +/// value-equal (the collision pair); `2` is a distinct discriminator so the +/// test can prove the comparator is not a degenerate everything-collides. +fn values() -> Vec { + ["1", "1.0", "2"] + .iter() + .map(|s| Decimal::from_str(s).expect("valid decimal literal")) + .collect() +} + +/// Generate `tests/sqlx/fixtures/v3_numeric_collision.sql`. Encrypts the +/// three decimals at numeric ORE width via the standard `.run()` driver — the +/// encryption input and the `plaintext` oracle column are the same value stream, +/// so no split (`run_with_payloads`) is needed. +pub async fn generate() -> Result<()> { + let values = values(); + FixtureSpec::new(NAME) + .with_index(IndexKind::Unique) + .with_index(IndexKind::Ore) + .with_column_type("jsonb") + .with_values(&values) + .run() + .await +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn the_collision_pair_is_value_equal_but_distinct_in_scale() { + let v = values(); + assert_eq!(v.len(), 3, "fixture is [1, 1.0, 2]"); + // The collision pair compares equal by value... + assert_eq!(v[0], v[1], "1 and 1.0 must be value-equal (the collision)"); + // ...yet is two distinct textual representations (different scale), so + // the catalog distinctness guard would reject them together. + assert_ne!( + v[0].scale(), + v[1].scale(), + "1 (scale 0) and 1.0 (scale 1) must differ in scale" + ); + // The discriminator is genuinely larger. + assert!(v[2] > v[0], "2 must order after 1"); + } +} diff --git a/tests/sqlx/src/scalar_domains.rs b/tests/sqlx/src/scalar_domains.rs index 16e5a088..11d0ff15 100644 --- a/tests/sqlx/src/scalar_domains.rs +++ b/tests/sqlx/src/scalar_domains.rs @@ -319,11 +319,11 @@ temporal_values! { } // `timestamptz`'s `ScalarType` wiring, generated from its catalog row by the -// same `temporal_values!` path as `date`. timestamptz is equality-only (its -// catalog row uses the eq-only domain shape), but the *value* wiring is +// same `temporal_values!` path as `date`. timestamptz is ordered (its catalog +// row uses the ordered domain shape, 12-block ORE), and the *value* wiring is // identical to any temporal scalar: RFC3339 strings parsed once into // `DateTime` behind `timestamptz_values()`. The pivots are retained as the -// three equality anchors the matrix sweeps. +// three min/mid/max anchors the matrix sweeps. temporal_values! { cell = TIMESTAMPTZ_VALUES_CELL, accessor = timestamptz_values, @@ -485,6 +485,88 @@ impl MatchScalar for String { // numeric origin / sign boundary. The signed-only sign-boundary test bounds on // `SignedScalar`, so a `String` instantiation of it would not compile. +// `numeric` is hand-written (like `text`): an owned `rust_decimal::Decimal`, +// not chrono-backed, so it parses the catalog's `Fixture::Numeric` strings into +// a `LazyLock>` rather than going through `temporal_values!`. The +// catalog stays zero-dep, so the parse happens here, not in `eql-scalars`. + +/// Typed `Decimal` fixture values, parsed once from `numeric`'s catalog row. +static NUMERIC_VALUES_CELL: std::sync::LazyLock> = + std::sync::LazyLock::new(|| { + use std::str::FromStr; + eql_scalars::NUMERIC + .fixtures + .iter() + .map(|f| match f { + eql_scalars::Fixture::Numeric(s) => rust_decimal::Decimal::from_str(s) + .unwrap_or_else(|e| panic!("invalid numeric catalog fixture {s:?}: {e}")), + other => panic!("non-numeric fixture in numeric catalog row: {other:?}"), + }) + .collect() + }); + +/// The `Decimal` fixture values, in catalog order. Public so the `eql_v2_numeric` +/// fixture module (emitted by `scalar_types!(fixture_modules)`) can hand the +/// slice to `scalar_fixture!`. +pub fn numeric_values() -> &'static [rust_decimal::Decimal] { + &NUMERIC_VALUES_CELL +} + +impl ScalarType for rust_decimal::Decimal { + const PG_TYPE: &'static str = "numeric"; + + fn fixture_values() -> &'static [Self] { + numeric_values() + } + // `to_sql_literal` inherits the default (`value.to_string()`): a `Decimal`'s + // `Display` form (e.g. `-1000000000000`, `0.001`) is a valid SQL numeric + // literal, so no quoting/override is needed (unlike `text` / `date`). +} + +impl OrderedScalar for rust_decimal::Decimal { + /// The smallest fixture decimal. Present verbatim in `fixture_values()`. + fn min_pivot() -> Self { + use std::str::FromStr; + rust_decimal::Decimal::from_str("-1000000000000").unwrap() + } + + /// The largest fixture decimal. Present verbatim in `fixture_values()`. + fn max_pivot() -> Self { + use std::str::FromStr; + rust_decimal::Decimal::from_str("1000000000000").unwrap() + } + // `mid_pivot` inherits the default `Self::default()` = `Decimal::ZERO` = 0, + // which is a real fixture and the numeric origin. +} + +// `Decimal` is deliberately NOT `SignedScalar`: like `text`, it is an +// ordered non-integer kind. The signed-only sign-boundary test bounds on +// `SignedScalar`, so it is not instantiated for numeric. + +/// `eql-scalars`' distinctness invariant keys `Fixture::Numeric` by its literal +/// string, so `"1"` and `"1.0"` would pass there as "distinct". But they denote +/// the same `Decimal` value (and collide in the ORE ciphertext, per ore-rs's +/// `equivalent_forms_collide_in_ciphertext`), so an aliasing pair would insert +/// duplicate `plaintext` rows and break `fetch_fixture_payload`'s `fetch_one`. +/// This guards distinctness by parsed value, which is the property the fixture +/// table relies on. +#[cfg(test)] +mod numeric_value_guards { + use super::*; + + #[test] + fn fixtures_are_distinct_by_value() { + use std::collections::HashSet; + let vals = numeric_values(); // &[Decimal], parsed from the catalog + let unique: HashSet<_> = vals.iter().collect(); + assert_eq!( + unique.len(), + vals.len(), + "two numeric fixtures alias to the same Decimal value", + ); + } +} + #[cfg(test)] mod text_value_tests { use super::*; diff --git a/tests/sqlx/src/scalar_types.rs b/tests/sqlx/src/scalar_types.rs index a51b288d..926d363d 100644 --- a/tests/sqlx/src/scalar_types.rs +++ b/tests/sqlx/src/scalar_types.rs @@ -56,6 +56,7 @@ macro_rules! scalar_types { int8 => i64, date => chrono::NaiveDate, timestamptz => chrono::DateTime, + numeric => rust_decimal::Decimal, text => String, } }; diff --git a/tests/sqlx/tests/encrypted_domain/family/inlinability.rs b/tests/sqlx/tests/encrypted_domain/family/inlinability.rs index 922d9ee4..9c62404d 100644 --- a/tests/sqlx/tests/encrypted_domain/family/inlinability.rs +++ b/tests/sqlx/tests/encrypted_domain/family/inlinability.rs @@ -89,7 +89,7 @@ async fn no_encrypted_domain_inline_critical_function_is_pinned(pool: PgPool) -> /// Direct guard for the self-contained eql_v3 SEM index-term functions. Unlike /// the structural guard above (which covers jsonb-domain-arg functions), these -/// take a composite (the ore_block_u64_8_256 and ore_cllw comparators) or raw +/// take a composite (the ore_block_256 and ore_cllw comparators) or raw /// jsonb (hmac_256, bloom_filter, the ore_cllw/has_ore_cllw extractors, the two /// per-encrypted-value `jsonb_array_to_*` helpers) arg, so they are NOT caught /// by the structural pin-skip and need explicit inline_critical allowlisting. If @@ -97,7 +97,7 @@ async fn no_encrypted_domain_inline_critical_function_is_pinned(pool: PgPool) -> /// regresses to Seq Scan — this test fails instead. /// /// `jsonb_array_to_bytea_array(jsonb)` and -/// `jsonb_array_to_ore_block_u64_8_256(jsonb)` are included here: both take a +/// `jsonb_array_to_ore_block_256(jsonb)` are included here: both take a /// bare `jsonb` arg (not a jsonb-backed encrypted DOMAIN), so the structural /// skip in tasks/pin_search_path.sql does not recognise them — they are kept /// unpinned by the `eql-inline-critical` COMMENT marker instead. This test @@ -113,7 +113,7 @@ async fn eql_v3_sem_inline_critical_functions_are_unpinned(pool: PgPool) -> Resu WITH expected(proname, pronargs, arg0, arg1) AS ( VALUES ('jsonb_array_to_bytea_array', 1, 'jsonb'::regtype, 0::oid), - ('jsonb_array_to_ore_block_u64_8_256', 1, 'jsonb'::regtype, 0::oid), + ('jsonb_array_to_ore_block_256', 1, 'jsonb'::regtype, 0::oid), ('ore_cllw', 1, 'jsonb'::regtype, 0::oid), ('has_ore_cllw', 1, 'jsonb'::regtype, 0::oid), ('meta_data', 1, 'jsonb'::regtype, 0::oid), @@ -135,9 +135,9 @@ async fn eql_v3_sem_inline_critical_functions_are_unpinned(pool: PgPool) -> Resu WHERE n.nspname = 'eql_v3' AND ( (p.pronargs = 2 AND p.proname IN ( - 'ore_block_u64_8_256_eq','ore_block_u64_8_256_neq', - 'ore_block_u64_8_256_lt','ore_block_u64_8_256_lte', - 'ore_block_u64_8_256_gt','ore_block_u64_8_256_gte')) + 'ore_block_256_eq','ore_block_256_neq', + 'ore_block_256_lt','ore_block_256_lte', + 'ore_block_256_gt','ore_block_256_gte')) OR (p.pronargs = 2 AND p.proname IN ( 'ore_cllw_eq','ore_cllw_neq', 'ore_cllw_lt','ore_cllw_lte', @@ -148,7 +148,7 @@ async fn eql_v3_sem_inline_critical_functions_are_unpinned(pool: PgPool) -> Resu 'ore_cllw', 'has_ore_cllw', 'jsonb_array_to_bytea_array', - 'jsonb_array_to_ore_block_u64_8_256') + 'jsonb_array_to_ore_block_256') AND p.proargtypes[0] = 'jsonb'::regtype) OR e.proname IS NOT NULL ) @@ -173,7 +173,7 @@ async fn eql_v3_sem_inline_critical_functions_are_unpinned(pool: PgPool) -> Resu } /// Companion guard for the two bare-`jsonb` per-encrypted-value helpers -/// (`jsonb_array_to_bytea_array`, `jsonb_array_to_ore_block_u64_8_256`). The +/// (`jsonb_array_to_bytea_array`, `jsonb_array_to_ore_block_256`). The /// unpinned state asserted above is only DURABLE because each helper carries an /// `eql-inline-critical` COMMENT marker that `tasks/pin_search_path.sql` honours /// (it skips pinning functions whose `pg_description` matches @@ -193,7 +193,7 @@ async fn eql_v3_sem_inline_critical_helpers_carry_marker(pool: PgPool) -> Result WITH expected(proname, pronargs, arg0, arg1) AS ( VALUES ('jsonb_array_to_bytea_array', 1, 'jsonb'::regtype, 0::oid), - ('jsonb_array_to_ore_block_u64_8_256', 1, 'jsonb'::regtype, 0::oid), + ('jsonb_array_to_ore_block_256', 1, 'jsonb'::regtype, 0::oid), ('ore_cllw', 1, 'jsonb'::regtype, 0::oid), ('has_ore_cllw', 1, 'jsonb'::regtype, 0::oid), ('meta_data', 1, 'jsonb'::regtype, 0::oid), diff --git a/tests/sqlx/tests/encrypted_domain/family/mutations.rs b/tests/sqlx/tests/encrypted_domain/family/mutations.rs index d78d5520..889293a5 100644 --- a/tests/sqlx/tests/encrypted_domain/family/mutations.rs +++ b/tests/sqlx/tests/encrypted_domain/family/mutations.rs @@ -212,15 +212,23 @@ async fn blocking_lt_flips_lt_arm_but_not_order_by(pool: PgPool) -> Result<()> { let mut ascending: Vec = ::fixture_values().to_vec(); ascending.sort(); - // Baseline: `<` works (no raise) and ORDER BY is plaintext-sorted. - let lt_baseline: Option = sqlx::query_scalar(lt_sql) - .bind(PLACEHOLDER_PAYLOAD) - .bind(PLACEHOLDER_PAYLOAD) - .fetch_one(&pool) - .await?; + // Baseline: `<` works (no raise) and ORDER BY is plaintext-sorted. Uses two + // REAL fixture ORE payloads (smallest two plaintexts), not PLACEHOLDER_PAYLOAD + // — the latter carries a 1-byte `ob` stub that the N-block comparator's + // well-formedness guard now (correctly) rejects. PLACEHOLDER stays in the + // post-mutation `assert_raises` below, where the `lt` blocker raises before + // the comparator ever inspects the term. + let lt_baseline: Option = sqlx::query_scalar( + "SELECT (SELECT payload FROM fixtures.eql_v2_int4 WHERE plaintext = $1)::eql_v3.int4_ord \ + < (SELECT payload FROM fixtures.eql_v2_int4 WHERE plaintext = $2)::eql_v3.int4_ord", + ) + .bind(ascending[0]) + .bind(ascending[1]) + .fetch_one(&pool) + .await?; ensure!( - lt_baseline.is_some(), - "baseline: `_ord` `<` must return a boolean (got {lt_baseline:?})" + lt_baseline == Some(true), + "baseline: smaller `_ord` `<` larger must be true (got {lt_baseline:?})" ); let order_baseline: Vec = sqlx::query_scalar(order_by_sql).fetch_all(&pool).await?; ensure!( @@ -259,7 +267,7 @@ async fn blocking_lt_flips_lt_arm_but_not_order_by(pool: PgPool) -> Result<()> { // 6. `_eq` equality must route through `eq_term` (`hm`), never ORE — the // mirror of #3 for the eq path. Rerouting it through -// `ore_block_u64_8_256` (`ob`) over ob-stripped rows breaks equality. +// `ore_block_256` (`ob`) over ob-stripped rows breaks equality. // // Two notes on why this is shaped differently from the plan's literal // "returns 0 where forward expects 1": @@ -267,7 +275,7 @@ async fn blocking_lt_flips_lt_arm_but_not_order_by(pool: PgPool) -> Result<()> { // `=` through ORE on the RAW fixture would still match (both terms are // injective per plaintext) — vacuous. Stripping `ob` forces the // rerouted operator onto an absent term, exactly as #3 strips `hm`. -// - `ore_block_u64_8_256(jsonb)` RAISES on an absent `ob` ("Expected an +// - `ore_block_256(jsonb)` RAISES on an absent `ob` ("Expected an // ore index (ob)"), whereas `hmac_256(jsonb)` returns NULL on an absent // `hm`. So the eq path breaks via a raise, not a 0-count. Either way the // correct hm-routed equality matches and the rerouted one does not. @@ -297,12 +305,12 @@ async fn rerouting_eq_eq_through_ob_flips_eq_arm(pool: PgPool) -> Result<()> { ); // Mutation: reroute `_eq` `=` through ORE. The `ob` key is absent, so - // `eql_v3.ore_block_u64_8_256(jsonb)` raises rather than matching. + // `eql_v3.ore_block_256(jsonb)` raises rather than matching. mutate( &pool, "CREATE OR REPLACE FUNCTION eql_v3.eq(a eql_v3.int4_eq, b eql_v3.int4_eq) \ RETURNS boolean LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE \ - AS $$ SELECT eql_v3.ore_block_u64_8_256(a::jsonb) = eql_v3.ore_block_u64_8_256(b::jsonb) $$", + AS $$ SELECT eql_v3.ore_block_256(a::jsonb) = eql_v3.ore_block_256(b::jsonb) $$", ) .await?; @@ -354,8 +362,8 @@ async fn collapsing_ord_term_flips_order_by_arm(pool: PgPool) -> Result<()> { let const_payload = fetch_fixture_payload::(&pool, 0).await?; let ddl = format!( "CREATE OR REPLACE FUNCTION eql_v3.ord_term(a eql_v3.int4_ord) \ - RETURNS eql_v3.ore_block_u64_8_256 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE \ - AS $mutbody$ SELECT eql_v3.ore_block_u64_8_256('{esc}'::jsonb) $mutbody$", + RETURNS eql_v3.ore_block_256 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE \ + AS $mutbody$ SELECT eql_v3.ore_block_256('{esc}'::jsonb) $mutbody$", esc = const_payload.replace('\'', "''"), ); mutate(&pool, &ddl).await?; @@ -409,8 +417,8 @@ async fn making_ord_term_non_strict_flips_order_by_nulls_arm(pool: PgPool) -> Re let const_payload = fetch_fixture_payload::(&pool, 0).await?; let ddl = format!( "CREATE OR REPLACE FUNCTION eql_v3.ord_term(a eql_v3.int4_ord) \ - RETURNS eql_v3.ore_block_u64_8_256 LANGUAGE sql IMMUTABLE PARALLEL SAFE \ - AS $mutbody$ SELECT eql_v3.ore_block_u64_8_256(\ + RETURNS eql_v3.ore_block_256 LANGUAGE sql IMMUTABLE PARALLEL SAFE \ + AS $mutbody$ SELECT eql_v3.ore_block_256(\ coalesce(a, '{esc}'::jsonb::eql_v3.int4_ord)::jsonb) $mutbody$", esc = const_payload.replace('\'', "''"), ); diff --git a/tests/sqlx/tests/encrypted_domain/family/sem.rs b/tests/sqlx/tests/encrypted_domain/family/sem.rs index 7518f6d2..d13847e5 100644 --- a/tests/sqlx/tests/encrypted_domain/family/sem.rs +++ b/tests/sqlx/tests/encrypted_domain/family/sem.rs @@ -1,6 +1,6 @@ //! Direct behavioural tests for the self-contained `eql_v3` searchable- //! encrypted-metadata (SEM) index-term functions (`eql_v3.hmac_256`, -//! `eql_v3.ore_block_u64_8_256` and their comparators). +//! `eql_v3.ore_block_256` and their comparators). //! //! These functions are a HAND-PORT of the `eql_v2` originals (`src/v3/sem/`). //! The scalar matrix already exercises the happy path of the *array* comparator @@ -13,7 +13,7 @@ //! against a faithful-port slip — see below). //! - T2: the `'Ciphertexts are different lengths'` RAISE (all real fixtures are //! equal length, so the matrix never hits it). -//! - T3: NULL-term ordering inside `compare_ore_block_u64_8_256_term` — the +//! - T3: NULL-term ordering inside `compare_ore_block_256_term` — the //! `STRICT` comparison wrappers short-circuit before these branches run. //! - T4: array-level NULL + empty/cardinality base cases of the recursion. //! - T5: presence checks (`has_*`) and the missing-`ob` RAISE. @@ -30,13 +30,13 @@ use sqlx::PgPool; /// A single term built directly from hex — no encryption needed for the /// structural/edge-case tests. fn term(hex: &str) -> String { - format!("ROW(decode('{hex}', 'hex'))::eql_v3.ore_block_u64_8_256_term") + format!("ROW(decode('{hex}', 'hex'))::eql_v3.ore_block_256_term") } /// T1 — Differential parity: the same real `ob` payload must compare identically /// through the `eql_v2` and `eql_v3` array comparators. `eql_v2` is the trusted /// oracle; `eql_v3` is the byte-port. Both sides route through the SAME path -/// (jsonb extractor → composite → `compare_ore_block_u64_8_256_terms`) so the +/// (jsonb extractor → composite → `compare_ore_block_256_terms`) so the /// schema prefix is the only variable — any divergence is a genuine port bug. /// v3 has no encrypted-arg `compare` overload, hence the extractor routing. #[sqlx::test] @@ -60,8 +60,8 @@ async fn ore_v2_v3_comparator_parity_on_real_fixtures(pool: PgPool) -> Result<() SELECT eql_v2.compare_ore_block_u64_8_256_terms( eql_v2.ore_block_u64_8_256(a.j), eql_v2.ore_block_u64_8_256(b.j)) AS v2, - eql_v3.compare_ore_block_u64_8_256_terms( - eql_v3.ore_block_u64_8_256(a.j), eql_v3.ore_block_u64_8_256(b.j)) AS v3 + eql_v3.compare_ore_block_256_terms( + eql_v3.ore_block_256(a.j), eql_v3.ore_block_256(b.j)) AS v3 FROM a, b "#; @@ -97,7 +97,7 @@ async fn ore_v2_v3_comparator_parity_on_real_fixtures(pool: PgPool) -> Result<() #[sqlx::test] async fn ore_term_comparator_rejects_different_length_ciphertexts(pool: PgPool) -> Result<()> { let sql = format!( - "SELECT eql_v3.compare_ore_block_u64_8_256_term({}, {})", + "SELECT eql_v3.compare_ore_block_256_term({}, {})", term("aabbccdd"), // 4 bytes term("aabbccddee"), // 5 bytes ); @@ -105,26 +105,26 @@ async fn ore_term_comparator_rejects_different_length_ciphertexts(pool: PgPool) Ok(()) } -/// T3 — NULL-term ordering inside `compare_ore_block_u64_8_256_term`. The +/// T3 — NULL-term ordering inside `compare_ore_block_256_term`. The /// function is intentionally NOT `STRICT`, so these defensive branches are /// reachable by a direct call (the `STRICT` comparison wrappers never reach /// them). Pins: `(NULL, t) = -1`, `(t, NULL) = 1`, `(NULL, NULL) = 0`. #[sqlx::test] async fn ore_term_comparator_null_ordering(pool: PgPool) -> Result<()> { let t = term("aabb"); - let n = "NULL::eql_v3.ore_block_u64_8_256_term"; + let n = "NULL::eql_v3.ore_block_256_term"; let cases = [ ( - format!("SELECT eql_v3.compare_ore_block_u64_8_256_term({n}, {t})"), + format!("SELECT eql_v3.compare_ore_block_256_term({n}, {t})"), -1, ), ( - format!("SELECT eql_v3.compare_ore_block_u64_8_256_term({t}, {n})"), + format!("SELECT eql_v3.compare_ore_block_256_term({t}, {n})"), 1, ), ( - format!("SELECT eql_v3.compare_ore_block_u64_8_256_term({n}, {n})"), + format!("SELECT eql_v3.compare_ore_block_256_term({n}, {n})"), 0, ), ]; @@ -137,20 +137,20 @@ async fn ore_term_comparator_null_ordering(pool: PgPool) -> Result<()> { } /// T4 — Array-level NULL and empty/cardinality base cases of the recursive -/// `compare_ore_block_u64_8_256_terms(term[], term[])`. NULL array → NULL; +/// `compare_ore_block_256_terms(term[], term[])`. NULL array → NULL; /// both empty → 0; empty vs non-empty → -1; non-empty vs empty → 1. #[sqlx::test] async fn ore_terms_array_null_and_empty_base_cases(pool: PgPool) -> Result<()> { let t = format!("ARRAY[{}]", term("aabb")); - let empty = "ARRAY[]::eql_v3.ore_block_u64_8_256_term[]"; - let null_arr = "NULL::eql_v3.ore_block_u64_8_256_term[]"; + let empty = "ARRAY[]::eql_v3.ore_block_256_term[]"; + let null_arr = "NULL::eql_v3.ore_block_256_term[]"; // NULL array operand → NULL result (the array overload returns NULL; it is // not STRICT). Typed as Option; the shared `assert_null` helper only // types Option, so query directly here. for sql in [ - format!("SELECT eql_v3.compare_ore_block_u64_8_256_terms({null_arr}, {t})"), - format!("SELECT eql_v3.compare_ore_block_u64_8_256_terms({t}, {null_arr})"), + format!("SELECT eql_v3.compare_ore_block_256_terms({null_arr}, {t})"), + format!("SELECT eql_v3.compare_ore_block_256_terms({t}, {null_arr})"), ] { let got: Option = sqlx::query_scalar(&sql).fetch_one(&pool).await?; assert!(got.is_none(), "NULL array operand must yield NULL: {sql}"); @@ -158,15 +158,15 @@ async fn ore_terms_array_null_and_empty_base_cases(pool: PgPool) -> Result<()> { let cases = [ ( - format!("SELECT eql_v3.compare_ore_block_u64_8_256_terms({empty}, {empty})"), + format!("SELECT eql_v3.compare_ore_block_256_terms({empty}, {empty})"), 0, ), ( - format!("SELECT eql_v3.compare_ore_block_u64_8_256_terms({empty}, {t})"), + format!("SELECT eql_v3.compare_ore_block_256_terms({empty}, {t})"), -1, ), ( - format!("SELECT eql_v3.compare_ore_block_u64_8_256_terms({t}, {empty})"), + format!("SELECT eql_v3.compare_ore_block_256_terms({t}, {empty})"), 1, ), ]; @@ -177,22 +177,32 @@ async fn ore_terms_array_null_and_empty_base_cases(pool: PgPool) -> Result<()> { Ok(()) } -/// T5 — SEM presence checks (`has_ore_block_u64_8_256`, `has_hmac_256`), the -/// extractor's missing-`ob` RAISE, and its NULL-jsonb short-circuit. +/// T5 — SEM presence checks (`has_ore_block_256`, `has_hmac_256`), the +/// extractor's missing-`ob` and non-array-`ob` RAISEs, and its NULL-jsonb +/// short-circuit. #[sqlx::test] async fn sem_presence_checks_and_missing_ob_behaviour(pool: PgPool) -> Result<()> { let bool_cases = [ ( - r#"SELECT eql_v3.has_ore_block_u64_8_256('{"ob":["aa"]}'::jsonb)"#, + r#"SELECT eql_v3.has_ore_block_256('{"ob":["aa"]}'::jsonb)"#, true, ), + (r#"SELECT eql_v3.has_ore_block_256('{}'::jsonb)"#, false), + // json-null `ob` is typed `'null'`, not `'array'` → absent. ( - r#"SELECT eql_v3.has_ore_block_u64_8_256('{}'::jsonb)"#, + r#"SELECT eql_v3.has_ore_block_256('{"ob":null}'::jsonb)"#, false, ), - // json-null `ob` → `->>` yields NULL → absent. + // Present-but-non-array `ob` is rejected as absent: a well-formed ORE + // term is always a JSON array of block terms, so a scalar and an object + // both → false. This is the boundary that makes `ore_block_256` RAISE on + // a malformed `ob` instead of degrading it to a NULL index term. ( - r#"SELECT eql_v3.has_ore_block_u64_8_256('{"ob":null}'::jsonb)"#, + r#"SELECT eql_v3.has_ore_block_256('{"ob":5}'::jsonb)"#, + false, + ), + ( + r#"SELECT eql_v3.has_ore_block_256('{"ob":{}}'::jsonb)"#, false, ), (r#"SELECT eql_v3.has_hmac_256('{"hm":"abc"}'::jsonb)"#, true), @@ -206,17 +216,26 @@ async fn sem_presence_checks_and_missing_ob_behaviour(pool: PgPool) -> Result<() // Missing `ob` → RAISE. assert_raises( &pool, - r#"SELECT eql_v3.ore_block_u64_8_256('{"foo":1}'::jsonb)"#, + r#"SELECT eql_v3.ore_block_256('{"foo":1}'::jsonb)"#, + &[], + "Expected an ore index (ob) value", + ) + .await?; + + // Present-but-non-array `ob` → RAISE at the extractor boundary, NOT a silent + // NULL index term (`has_ore_block_256` reports it absent). + assert_raises( + &pool, + r#"SELECT eql_v3.ore_block_256('{"ob":5}'::jsonb)"#, &[], "Expected an ore index (ob) value", ) .await?; // NULL jsonb → NULL composite (STRICT short-circuit), NOT a raise. - let is_null: bool = - sqlx::query_scalar("SELECT eql_v3.ore_block_u64_8_256(NULL::jsonb) IS NULL") - .fetch_one(&pool) - .await?; + let is_null: bool = sqlx::query_scalar("SELECT eql_v3.ore_block_256(NULL::jsonb) IS NULL") + .fetch_one(&pool) + .await?; assert!( is_null, "NULL jsonb must extract to a NULL composite, not raise" @@ -312,7 +331,7 @@ async fn jsonb_array_to_bytea_array_input_shapes(pool: PgPool) -> Result<()> { Ok(()) } -/// T7 — Characterization of `eql_v3.jsonb_array_to_ore_block_u64_8_256(jsonb)` +/// T7 — Characterization of `eql_v3.jsonb_array_to_ore_block_256(jsonb)` /// across the same three input shapes. Safety net for the same plpgsql→sql /// inlining refactor. Behaviour pinned: /// - JSON null (`'null'`) → NULL composite @@ -325,7 +344,7 @@ async fn jsonb_array_to_ore_block_input_shapes(pool: PgPool) -> Result<()> { // SQL NULL (distinct from JSON null `'null'`). Not STRICT, so the body // runs: `jsonb_typeof(NULL)` is NULL → CASE guard not-true → ELSE NULL. let is_null: bool = - sqlx::query_scalar("SELECT eql_v3.jsonb_array_to_ore_block_u64_8_256(NULL::jsonb) IS NULL") + sqlx::query_scalar("SELECT eql_v3.jsonb_array_to_ore_block_256(NULL::jsonb) IS NULL") .fetch_one(&pool) .await?; assert!( @@ -334,23 +353,22 @@ async fn jsonb_array_to_ore_block_input_shapes(pool: PgPool) -> Result<()> { ); // JSON null → NULL composite. - let is_null: bool = sqlx::query_scalar( - "SELECT eql_v3.jsonb_array_to_ore_block_u64_8_256('null'::jsonb) IS NULL", - ) - .fetch_one(&pool) - .await?; + let is_null: bool = + sqlx::query_scalar("SELECT eql_v3.jsonb_array_to_ore_block_256('null'::jsonb) IS NULL") + .fetch_one(&pool) + .await?; assert!(is_null, "JSON null must yield NULL composite"); // Empty array → NULL composite. let is_null: bool = - sqlx::query_scalar("SELECT eql_v3.jsonb_array_to_ore_block_u64_8_256('[]'::jsonb) IS NULL") + sqlx::query_scalar("SELECT eql_v3.jsonb_array_to_ore_block_256('[]'::jsonb) IS NULL") .fetch_one(&pool) .await?; assert!(is_null, "empty JSON array must yield NULL composite"); // Single-element array → non-NULL composite with exactly 1 term. let term_count: i32 = sqlx::query_scalar( - "SELECT cardinality((eql_v3.jsonb_array_to_ore_block_u64_8_256('[\"aabb\"]'::jsonb)).terms)", + "SELECT cardinality((eql_v3.jsonb_array_to_ore_block_256('[\"aabb\"]'::jsonb)).terms)", ) .fetch_one(&pool) .await?; @@ -361,7 +379,7 @@ async fn jsonb_array_to_ore_block_input_shapes(pool: PgPool) -> Result<()> { // Populated array → non-NULL composite with one term per element. let term_count: i32 = sqlx::query_scalar( - "SELECT cardinality((eql_v3.jsonb_array_to_ore_block_u64_8_256('[\"aabb\",\"ccdd\",\"eeff\"]'::jsonb)).terms)", + "SELECT cardinality((eql_v3.jsonb_array_to_ore_block_256('[\"aabb\",\"ccdd\",\"eeff\"]'::jsonb)).terms)", ) .fetch_one(&pool) .await?; @@ -372,7 +390,7 @@ async fn jsonb_array_to_ore_block_input_shapes(pool: PgPool) -> Result<()> { // Deliberate delta: a non-array JSON scalar returns NULL (not a raise). let is_null: bool = - sqlx::query_scalar("SELECT eql_v3.jsonb_array_to_ore_block_u64_8_256('5'::jsonb) IS NULL") + sqlx::query_scalar("SELECT eql_v3.jsonb_array_to_ore_block_256('5'::jsonb) IS NULL") .fetch_one(&pool) .await?; assert!( @@ -383,7 +401,7 @@ async fn jsonb_array_to_ore_block_input_shapes(pool: PgPool) -> Result<()> { // Same delta for a non-array JSON object — `jsonb_typeof` is 'object', so // the CASE guard is not-true → ELSE NULL (not a raise). let is_null: bool = - sqlx::query_scalar("SELECT eql_v3.jsonb_array_to_ore_block_u64_8_256('{}'::jsonb) IS NULL") + sqlx::query_scalar("SELECT eql_v3.jsonb_array_to_ore_block_256('{}'::jsonb) IS NULL") .fetch_one(&pool) .await?; assert!( @@ -394,7 +412,7 @@ async fn jsonb_array_to_ore_block_input_shapes(pool: PgPool) -> Result<()> { Ok(()) } -/// T8 — Catalog pin for all three `compare_ore_block_u64_8_256_term(s)` overloads +/// T8 — Catalog pin for all three `compare_ore_block_256_term(s)` overloads /// (term×term, term[]×term[], composite×composite). Two load-bearing catalog /// properties are pinned at the same layer: /// @@ -420,8 +438,8 @@ async fn ore_comparators_are_immutable(pool: PgPool) -> Result<()> { FROM pg_catalog.pg_proc p WHERE p.pronamespace = 'eql_v3'::regnamespace AND p.proname IN ( - 'compare_ore_block_u64_8_256_term', - 'compare_ore_block_u64_8_256_terms' + 'compare_ore_block_256_term', + 'compare_ore_block_256_terms' ) ORDER BY args "#, @@ -440,11 +458,11 @@ async fn ore_comparators_are_immutable(pool: PgPool) -> Result<()> { for (args, provolatile, isstrict) in &rows { assert_eq!( provolatile, "i", - "compare_ore_block_u64_8_256_term(s)({args}) must be IMMUTABLE, got provolatile={provolatile}" + "compare_ore_block_256_term(s)({args}) must be IMMUTABLE, got provolatile={provolatile}" ); assert!( !isstrict, - "compare_ore_block_u64_8_256_term(s)({args}) must NOT be STRICT (NULL branches are load-bearing)" + "compare_ore_block_256_term(s)({args}) must NOT be STRICT (NULL branches are load-bearing)" ); } Ok(()) @@ -516,7 +534,7 @@ async fn bloom_filter_extractor_empty_array_is_empty_not_null(pool: PgPool) -> R } /// T10 — `eql_v3.has_bloom_filter(jsonb)` presence predicate. Mirrors the -/// `has_hmac_256` / `has_ore_block_u64_8_256` coverage in T5: its two-part guard +/// `has_hmac_256` / `has_ore_block_256` coverage in T5: its two-part guard /// (`val ? 'bf'` AND `val ->> 'bf' IS NOT NULL`) is exercised across present, /// absent, and json-null cases. The `{"bf":null}` → false case pins the /// `IS NOT NULL` half — the predicate is not reached transitively by the @@ -547,7 +565,7 @@ async fn has_bloom_filter_detects_bf_presence(pool: PgPool) -> Result<()> { Ok(()) } -/// T11 — Planner-selectivity metadata for the `eql_v3.ore_block_u64_8_256` +/// T11 — Planner-selectivity metadata for the `eql_v3.ore_block_256` /// `=` / `<>` operators. `<>` must use the inequality estimators /// (`neqsel` / `neqjoinsel`) and must NOT declare `HASHES` — an earlier revision /// copied `=`'s `eqsel` / `eqjoinsel` + `HASHES` onto `<>`, which is meaningless @@ -561,8 +579,8 @@ async fn ore_block_comparison_operators_declare_correct_selectivity(pool: PgPool SELECT o.oprrest::text, o.oprjoin::text, o.oprcanhash, o.oprcanmerge FROM pg_operator o WHERE o.oprname = '=' - AND o.oprleft = 'eql_v3.ore_block_u64_8_256'::regtype - AND o.oprright = 'eql_v3.ore_block_u64_8_256'::regtype + AND o.oprleft = 'eql_v3.ore_block_256'::regtype + AND o.oprright = 'eql_v3.ore_block_256'::regtype "#, ) .fetch_one(&pool) @@ -577,8 +595,8 @@ async fn ore_block_comparison_operators_declare_correct_selectivity(pool: PgPool SELECT o.oprrest::text, o.oprjoin::text, o.oprcanhash FROM pg_operator o WHERE o.oprname = '<>' - AND o.oprleft = 'eql_v3.ore_block_u64_8_256'::regtype - AND o.oprright = 'eql_v3.ore_block_u64_8_256'::regtype + AND o.oprleft = 'eql_v3.ore_block_256'::regtype + AND o.oprright = 'eql_v3.ore_block_256'::regtype "#, ) .fetch_one(&pool) diff --git a/tests/sqlx/tests/generate_all_fixtures.rs b/tests/sqlx/tests/generate_all_fixtures.rs index 40db2216..dcb50c27 100644 --- a/tests/sqlx/tests/generate_all_fixtures.rs +++ b/tests/sqlx/tests/generate_all_fixtures.rs @@ -49,5 +49,14 @@ async fn generate_all() -> anyhow::Result<()> { eprintln!("Generating fixture v3_doc_int4 (scalar-shaped SteVec document)..."); eql_tests::fixtures::v3_doc_int4::generate().await?; eprintln!("Regenerated v3_doc_int4."); + + // The numeric scale-equivalence collision fixture (`1`, `1.0`, `2`). Not a + // CATALOG scalar — the distinctness guard forbids `1`/`1.0` coexisting in + // `eql_v2_numeric` — so it rides the same pipeline as a hand-written + // `FixtureSpec`. Gives the always-on `1 == 1.0` ORE collision test + // its committed fixture. + eprintln!("Generating fixture v3_numeric_collision (1 == 1.0 ORE collision)..."); + eql_tests::fixtures::v3_numeric_collision::generate().await?; + eprintln!("Regenerated v3_numeric_collision."); Ok(()) } diff --git a/tests/sqlx/tests/ore_block_comparator_tests.rs b/tests/sqlx/tests/ore_block_comparator_tests.rs new file mode 100644 index 00000000..5a65cd85 --- /dev/null +++ b/tests/sqlx/tests/ore_block_comparator_tests.rs @@ -0,0 +1,424 @@ +//! Direct tests for the generalized N-block ORE comparator +//! `eql_v3.compare_ore_block_256_term(s)`. +//! +//! Every test here is **always-on** — it runs in normal (no-creds) CI, because +//! it sources real ORE terms from committed fixtures rather than encrypting at +//! runtime: +//! * The malformed-length guards build ORE terms by hand from short byte +//! strings, exercising length validation without real ciphertexts. +//! * The ordering properties (all-pairs oracle agreement + antisymmetry) read +//! the committed `eql_v2_numeric` / `eql_v2_timestamptz` fixtures, whose +//! catalog order is the strict ascending oracle. +//! * The `1 == 1.0` ORE collision reads the committed `v3_numeric_collision` +//! fixture — the one place the value-equal pair can live, since the catalog +//! distinctness guard forbids it in `eql_v2_numeric`. +//! +//! Fixtures are generated once (with creds) in the `build-archive` CI job and +//! baked into the test binaries via `include_str!`, so the no-creds shards +//! consume them directly. See `tasks/test/sqlx-archive.sh`. + +use anyhow::Result; +use sqlx::PgPool; + +/// Fetch two fixture payloads by plaintext literal, wrap each in the ordered +/// extractor, and return the ORE comparison. The single fetch + `ord_term` + +/// `compare_ore_block_256_terms` shape every chain/pair test shares; `lo`/`hi` +/// are the plaintext SQL literals (with cast), e.g. `"(-1)::numeric"`. +async fn compare_fixture_pair( + pool: &PgPool, + table: &str, + ord_domain: &str, + lo: &str, + hi: &str, +) -> Result { + let sql = format!( + "SELECT eql_v3.compare_ore_block_256_terms( \ + eql_v3.ord_term((SELECT payload FROM fixtures.{table} WHERE plaintext = {lo})::eql_v3.{ord_domain}), \ + eql_v3.ord_term((SELECT payload FROM fixtures.{table} WHERE plaintext = {hi})::eql_v3.{ord_domain}))" + ); + Ok(sqlx::query_scalar::<_, i32>(&sql).fetch_one(pool).await?) +} + +/// A hand-built ORE term of `len` bytes filled with `fill`. Creds-free — the +/// bytes are cryptographically meaningless, so this only drives length/structure +/// validation, never ordering semantics. +fn term_sql(fill: char, len: usize) -> String { + format!("ROW(repeat('{fill}', {len})::bytea)::eql_v3.ore_block_256_term") +} + +/// Assert the ORE comparator agrees with an explicit oracle order over a +/// committed fixture. `ascending[i]` is the SQL literal (with cast) for the +/// value whose oracle rank is `i`; its real ciphertext is fetched from +/// `fixtures.{table}` by `plaintext` and loaded into a connection-local +/// `ore_sample(rank, payload)`. +/// +/// Two properties, both over EVERY pair (not just adjacent): +/// * **Oracle agreement** — `rank a < rank b` ⇒ `compare(a, b) = -1`. +/// All-pairs subsumes totality and transitivity; combined with antisymmetry +/// it also pins the `>` direction, so a one-sided bug cannot hide. +/// * **Antisymmetry** — `compare(a, b) = -compare(b, a)` for all distinct +/// pairs. +/// +/// Creds-free: every term is a real committed ciphertext. The per-row +/// `rows_affected == 1` check fails loudly if the `ascending` list drifts from +/// the fixture (a removed/renamed value resolves to zero rows). +async fn assert_orders_like_oracle( + pool: &PgPool, + table: &str, + ord_domain: &str, + ascending: &[String], +) -> Result<()> { + // TEMP is connection-scoped and a pool may hand out different connections + // per query — pin everything to one acquired connection. + let mut conn = pool.acquire().await?; + sqlx::query("CREATE TEMP TABLE ore_sample (rank int, payload jsonb)") + .execute(&mut *conn) + .await?; + for (rank, literal) in ascending.iter().enumerate() { + let inserted = sqlx::query(&format!( + "INSERT INTO ore_sample (rank, payload) \ + SELECT {rank}, payload FROM fixtures.{table} WHERE plaintext = {literal}" + )) + .execute(&mut *conn) + .await? + .rows_affected(); + anyhow::ensure!( + inserted == 1, + "expected exactly 1 fixture row for {literal} (rank {rank}), got {inserted} \ + — the ascending list has drifted from fixtures.{table}" + ); + } + + // Oracle agreement: lower rank (smaller value) MUST compare -1. + let order_violations: i64 = sqlx::query_scalar(&format!( + "SELECT count(*) FROM ore_sample a JOIN ore_sample b ON a.rank < b.rank \ + WHERE eql_v3.compare_ore_block_256_terms( \ + eql_v3.ord_term(a.payload::eql_v3.{ord_domain}), \ + eql_v3.ord_term(b.payload::eql_v3.{ord_domain})) <> -1" + )) + .fetch_one(&mut *conn) + .await?; + assert_eq!( + order_violations, 0, + "ORE comparator disagreed with the oracle order on some pair" + ); + + // Antisymmetry: compare(a, b) = -compare(b, a) for every distinct pair. + let antisymmetry_violations: i64 = sqlx::query_scalar(&format!( + "SELECT count(*) FROM ore_sample a JOIN ore_sample b ON a.rank <> b.rank \ + WHERE eql_v3.compare_ore_block_256_terms( \ + eql_v3.ord_term(a.payload::eql_v3.{ord_domain}), \ + eql_v3.ord_term(b.payload::eql_v3.{ord_domain})) \ + <> - eql_v3.compare_ore_block_256_terms( \ + eql_v3.ord_term(b.payload::eql_v3.{ord_domain}), \ + eql_v3.ord_term(a.payload::eql_v3.{ord_domain}))" + )) + .fetch_one(&mut *conn) + .await?; + assert_eq!( + antisymmetry_violations, 0, + "ORE comparator violated antisymmetry on some pair" + ); + + Ok(()) +} + +/// A `bytea` whose length is NOT a valid `49*N + 16` must raise, not silently +/// return 0. Uses a 4-byte term (equal lengths so the equal-length guard does +/// not fire first). +#[sqlx::test] +async fn comparator_rejects_non_conforming_length(pool: PgPool) -> Result<()> { + let sql = "SELECT eql_v3.compare_ore_block_256_term( \ + ROW('\\x00010203'::bytea)::eql_v3.ore_block_256_term, \ + ROW('\\x04050607'::bytea)::eql_v3.ore_block_256_term)"; + let err = sqlx::query_scalar::<_, i32>(sql) + .fetch_one(&pool) + .await + .expect_err("a 4-byte ORE term must raise, not return a comparison"); + assert!( + err.to_string() + .to_lowercase() + .contains("malformed ore term"), + "expected malformed-term error, got: {err}" + ); + Ok(()) +} + +/// A 16-byte term satisfies `(16 - 16) % 49 == 0` and derives N = 0; the +/// `<= 16` clause must still reject it (otherwise it falls through to the +/// all-blocks-equal path and wrongly returns 0). +#[sqlx::test] +async fn comparator_rejects_sixteen_byte_term(pool: PgPool) -> Result<()> { + let sql = "SELECT eql_v3.compare_ore_block_256_term( \ + ROW(repeat('a', 16)::bytea)::eql_v3.ore_block_256_term, \ + ROW(repeat('b', 16)::bytea)::eql_v3.ore_block_256_term)"; + let err = sqlx::query_scalar::<_, i32>(sql) + .fetch_one(&pool) + .await + .expect_err("a 16-byte ORE term (N=0) must raise"); + assert!( + err.to_string() + .to_lowercase() + .contains("malformed ore term"), + "expected malformed-term error, got: {err}" + ); + Ok(()) +} + +/// Cross-width footgun: now that N is derived per-term, comparing terms of two +/// different (individually valid) widths must raise via the equal-length guard, +/// not silently compare the shared prefix. Both lengths here are well-formed — +/// 408 = 49*8 + 16 (the int4 width, N=8) and 702 = 49*14 + 16 (the numeric +/// width, N=14) — so the only thing that fires is the different-lengths check, +/// ahead of the malformed-length guard. Creds-free (hand-built bytea). +#[sqlx::test] +async fn comparator_rejects_mismatched_block_widths(pool: PgPool) -> Result<()> { + let sql = "SELECT eql_v3.compare_ore_block_256_term( \ + ROW(repeat('a', 408)::bytea)::eql_v3.ore_block_256_term, \ + ROW(repeat('b', 702)::bytea)::eql_v3.ore_block_256_term)"; + let err = sqlx::query_scalar::<_, i32>(sql) + .fetch_one(&pool) + .await + .expect_err("an 8-block vs 14-block ORE term comparison must raise"); + assert!( + err.to_string().to_lowercase().contains("different lengths"), + "expected different-lengths error, got: {err}" + ); + Ok(()) +} + +/// Sweep the `49*N + 16` length guard across boundary/off-by lengths the +/// point-example tests above don't reach. Both operands are kept the SAME length +/// so only the malformed-length guard can fire (the different-lengths guard at +/// `bit_length` precedes it). Creds-free. +/// +/// Valid lengths are pinned to exact return values (verified against +/// `src/v3/sem/ore_block_256/functions.sql`): +/// * equal operands take the all-blocks-equal path → return **0** +/// (`functions.sql:166-168`; the `encrypt()` branch is unreachable); +/// * differing operands fall through to the `encrypt()` path → return **±1** +/// (`functions.sql:170-190`), which is the branch the length guard protects. +#[sqlx::test] +async fn comparator_length_guard_sweep(pool: PgPool) -> Result<()> { + // Invalid: not 49*N + 16 (16 and 4 are covered by the dedicated tests above). + for len in [15usize, 17, 50, 64, 66, 407, 409, 701, 703] { + let sql = format!( + "SELECT eql_v3.compare_ore_block_256_term({}, {})", + term_sql('a', len), + term_sql('b', len) + ); + let err = sqlx::query_scalar::<_, i32>(&sql) + .fetch_one(&pool) + .await + .expect_err(&format!("length {len} (not 49*N+16) must raise")); + assert!( + err.to_string() + .to_lowercase() + .contains("malformed ore term"), + "len {len}: expected malformed-term error, got: {err}" + ); + } + + // Valid: 49*N + 16 for N = 1..=14 (spans the int4/timestamp/numeric widths). + for n in 1..=14usize { + let len = 49 * n + 16; + + let eq: i32 = sqlx::query_scalar(&format!( + "SELECT eql_v3.compare_ore_block_256_term({}, {})", + term_sql('a', len), + term_sql('a', len) + )) + .fetch_one(&pool) + .await?; + assert_eq!(eq, 0, "len {len} (N={n}): identical terms must compare 0"); + + let ne: i32 = sqlx::query_scalar(&format!( + "SELECT eql_v3.compare_ore_block_256_term({}, {})", + term_sql('a', len), + term_sql('b', len) + )) + .fetch_one(&pool) + .await?; + assert!( + ne == -1 || ne == 1, + "len {len} (N={n}): differing terms must compare ±1 (encrypt path), got {ne}" + ); + } + Ok(()) +} + +/// Width: a numeric ORE term must be 14 blocks => 49*14 + 16 = 702 bytes. +#[sqlx::test(fixtures(path = "../fixtures", scripts("eql_v2_numeric")))] +async fn numeric_term_is_14_blocks(pool: PgPool) -> Result<()> { + let width: i32 = sqlx::query_scalar( + "SELECT octet_length((((eql_v3.ord_term( \ + (SELECT payload FROM fixtures.eql_v2_numeric WHERE plaintext = (-1000000)::numeric) \ + ::eql_v3.numeric_ord)).terms)[1]).bytes)", + ) + .fetch_one(&pool) + .await?; + assert_eq!(width, 702, "numeric ORE term must be 14 blocks (702 bytes)"); + Ok(()) +} + +/// 14-block numeric terms must order like `Decimal`'s `Ord` over ALL pairs (not +/// just adjacent), plus antisymmetry. Spans sign, magnitude, and fractional +/// (low-block) scale, so the left blocks — not just the right blocks — decide +/// ordering. This is the regression the missed `9 -> 1+n` left-offset would +/// fail; the all-pairs sweep makes a single lucky pair unable to mask it. The +/// list is the strict ascending oracle (matching `NUMERIC_FIXTURES`' catalog +/// order); `assert_orders_like_oracle` fails loudly if it drifts from the +/// committed fixture. +#[sqlx::test(fixtures(path = "../fixtures", scripts("eql_v2_numeric")))] +async fn numeric_terms_order_like_decimal_ord(pool: PgPool) -> Result<()> { + let ascending: Vec = [ + "-1000000000000", + "-1000000", + "-1.001", + "-1", + "-0.5", + "-0.001", + "0", + "0.001", + "0.5", + "0.999999999", + "1", + "1.001", + "1000000", + "1000000000000", + ] + .iter() + .map(|v| format!("({v})::numeric")) + .collect(); + assert_orders_like_oracle(&pool, "eql_v2_numeric", "numeric_ord", &ascending).await +} + +/// Width + single-pair sanity for the 12-block (timestamptz, N=12 => 604 bytes) +/// term. The full ordering property is `timestamptz_terms_order_like_datetime_ord`. +#[sqlx::test(fixtures(path = "../fixtures", scripts("eql_v2_timestamptz")))] +async fn timestamptz_term_is_12_blocks(pool: PgPool) -> Result<()> { + let width: i32 = sqlx::query_scalar( + "SELECT octet_length((((eql_v3.ord_term( \ + (SELECT payload FROM fixtures.eql_v2_timestamptz WHERE plaintext = '1970-01-01T00:00:00Z'::timestamptz) \ + ::eql_v3.timestamptz_ord)).terms)[1]).bytes)", + ) + .fetch_one(&pool) + .await?; + assert_eq!( + width, 604, + "timestamptz ORE term must be 12 blocks (604 bytes)" + ); + Ok(()) +} + +/// 12-block (timestamptz) terms must order like `DateTime`'s `Ord` over +/// ALL pairs, plus antisymmetry. N=12 is the only width strictly between the +/// working 8 and the headline 14, so it needs the same left-block-deciding +/// coverage as numeric. The 15 values are the strict ascending oracle (matching +/// `TIMESTAMPTZ_FIXTURES`' catalog order); `assert_orders_like_oracle` fails +/// loudly if the list drifts from the committed fixture. +#[sqlx::test(fixtures(path = "../fixtures", scripts("eql_v2_timestamptz")))] +async fn timestamptz_terms_order_like_datetime_ord(pool: PgPool) -> Result<()> { + let ascending: Vec = [ + "1900-01-01T00:00:00Z", + "1950-07-15T06:30:00Z", + "1969-12-31T23:59:59Z", + "1970-01-01T00:00:00Z", + "1970-01-01T00:00:01Z", + "1985-04-12T23:20:50Z", + "1999-12-31T23:59:59Z", + "2000-01-01T00:00:00Z", + "2004-02-29T12:00:00Z", + "2012-06-30T11:59:59Z", + "2016-03-15T08:15:30Z", + "2020-10-21T14:45:00Z", + "2024-02-29T17:30:45Z", + "2038-01-19T03:14:07Z", + "2099-12-31T23:59:59Z", + ] + .iter() + .map(|v| format!("'{v}'::timestamptz")) + .collect(); + assert_orders_like_oracle(&pool, "eql_v2_timestamptz", "timestamptz_ord", &ascending).await +} + +/// A real wide-block term must compare equal to itself — the reflexive +/// `eq`-true path (`functions.sql:166`) at N=14 and N=12, creds-free (reuses the +/// generated fixtures). Distinct from the `1 == 1.0` collision (Gap 1), which is +/// equality across *different* ciphertexts. +#[sqlx::test(fixtures(path = "../fixtures", scripts("eql_v2_numeric", "eql_v2_timestamptz")))] +async fn wide_block_term_compares_equal_to_itself(pool: PgPool) -> Result<()> { + let numeric = compare_fixture_pair( + &pool, + "eql_v2_numeric", + "numeric_ord", + "(1)::numeric", + "(1)::numeric", + ) + .await?; + assert_eq!(numeric, 0, "a 14-block numeric term must equal itself"); + + let timestamptz = compare_fixture_pair( + &pool, + "eql_v2_timestamptz", + "timestamptz_ord", + "'2000-01-01T00:00:00Z'::timestamptz", + "'2000-01-01T00:00:00Z'::timestamptz", + ) + .await?; + assert_eq!( + timestamptz, 0, + "a 12-block timestamptz term must equal itself" + ); + Ok(()) +} + +/// Compare the collision-fixture rows addressed by `id`. The +/// `v3_numeric_collision` fixture stores `1` (id 1), `1.0` (id 2), `2` (id 3); +/// rows are fetched by `id` because `WHERE plaintext = 1` is ambiguous (numeric +/// equality matches both `1` and `1.0`). +async fn compare_collision_ids(pool: &PgPool, a: i64, b: i64) -> Result { + let sql = format!( + "SELECT eql_v3.compare_ore_block_256_terms( \ + eql_v3.ord_term((SELECT payload FROM fixtures.v3_numeric_collision WHERE id = {a})::eql_v3.numeric_ord), \ + eql_v3.ord_term((SELECT payload FROM fixtures.v3_numeric_collision WHERE id = {b})::eql_v3.numeric_ord))" + ); + Ok(sqlx::query_scalar::<_, i32>(&sql).fetch_one(pool).await?) +} + +/// Scale-equivalent decimals (`1` and `1.0`) must collide in the ORE +/// ciphertext: they are value-equal numerics, so their ORE terms must compare +/// `0`. Always-on via the committed `v3_numeric_collision` fixture — the only +/// place the value-equal pair can live, since the catalog distinctness guard +/// (`scalar_domains.rs` `numeric_value_guards`) forbids it in `eql_v2_numeric`. +/// This is the positive counterpart to that negative guard. +/// +/// Asserted in BOTH directions (a scale-biased comparator could pass a +/// one-directional check); the `1`-vs-`2` guards are load-bearing — they defeat +/// a degenerate everything-returns-0 comparator that would otherwise pass the +/// collision assertions. +#[sqlx::test(fixtures(path = "../fixtures", scripts("v3_numeric_collision")))] +async fn numeric_scale_equivalents_collide(pool: PgPool) -> Result<()> { + // ids: 1 => `1`, 2 => `1.0`, 3 => `2`. + assert_eq!( + compare_collision_ids(&pool, 1, 2).await?, + 0, + "1 and 1.0 must collide" + ); + assert_eq!( + compare_collision_ids(&pool, 2, 1).await?, + 0, + "the collision must be order-independent" + ); + assert_eq!( + compare_collision_ids(&pool, 1, 3).await?, + -1, + "1 must order before 2" + ); + assert_eq!( + compare_collision_ids(&pool, 3, 1).await?, + 1, + "2 must order after 1" + ); + Ok(()) +}