Skip to content

Eql v3#246

Draft
tobyhede wants to merge 106 commits into
mainfrom
eql_v3
Draft

Eql v3#246
tobyhede wants to merge 106 commits into
mainfrom
eql_v3

Conversation

@tobyhede
Copy link
Copy Markdown
Contributor

THIS IS THE EQL_V3 STACK.
VERY WIP.

tobyhede added 9 commits May 28, 2026 13:01
Adds the encrypted-int4 fixture generator and benchmark dataset
generation tooling: tasks/fixtures/ and tasks/bench.toml, the
tests/benchmarks/ harness, the CipherStash Proxy docker-compose used to
encrypt fixture data, the int4 fixture install migration, and bench-data
test coverage. Registers the new task files in mise.toml and ignores
local mise credential overrides.
Generator no longer shells out to a CipherStash Proxy container to encrypt
fixture values. `cipherstash-client` 0.35 is now a direct dependency of the
SQLx test crate and the new `fixtures::cipherstash` module owns ZeroKMS
bootstrap (cached in a OnceCell), the index-name -> ColumnConfig mapping,
and the per-value encrypt helper. The Rust fixture owns configuration and
encryption; the DB only owns table structure.

Removes: `tests/docker-compose.proxy.yml`, the `proxy:up`/`proxy:logs`/
`proxy:down` mise tasks, the `mise run proxy:up` step from `test:sqlx`,
and the `restart_proxy_and_wait` / `insert_through_proxy` / `PROXY_PORT`
plumbing in `driver.rs`. The working-table `payload` column is now plain
`jsonb` (no `eql_v2_encrypted` composite) and no `add_search_config` rows
are written to `eql_v2_configuration` during generation.

`mise run test:sqlx` now needs `CS_CLIENT_ACCESS_KEY` (or `CS_CLIENT_ID`
+ `CS_CLIENT_KEY`) and `CS_WORKSPACE_CRN` in the process env so
`AutoStrategy::detect()` / `EnvKeyProvider` can pick them up.
`cipherstash::encrypt_store` now takes a slice and returns a `Vec`, so a
fixture run issues one `encrypt_eql` call regardless of value count
instead of one per value. The 14-row `eql_v2_int4` fixture drops from 14
ZeroKMS round trips to one; the planned bench fixture (~10k rows) gets
the same treatment. Empty input short-circuits before `cipher()` so a
caller with nothing to encrypt does not pay the bootstrap cost.

`driver::insert_direct` now does one batched encrypt then a per-row
INSERT loop — the INSERT is invariant across rows so the SQL string is
lifted out of the loop. The working table is local Postgres and the
per-row execute cost is in microseconds, so batching the INSERTs is not
worth the dynamic-SQL complexity.

Test coverage closes the gap flagged in the PR review:

- Cheap unit tests (no live ZeroKMS): `index_type_for` mapping for
  unique/ore/match plus the unknown-name error path; an empty-batch
  short-circuit test that proves `cipher()` is not reached when there
  is nothing to encrypt (visible because the test runs without CS_*
  env vars).
- Live tests gated by `fixture-gen` + `#[ignore]`: single-value
  round trip, batch length + per-payload identifier check, and a
  distinct-plaintexts → distinct `hm` assertion that mirrors the
  fixture-tests' equality term check at the unit-test layer.
`test:sqlx` copies `release/cipherstash-encrypt.sql` into
`tests/sqlx/migrations/001_install_eql.sql` so the EQL extension is
applied to each per-test database. Without an explicit build dep a
stale release artifact silently ships an old EQL extension — visible
when regression-guard migrations (e.g. 003_install_ste_vec_data.sql)
fail on a `_encrypted_check_c` shape the current source has already
fixed. Declaring `depends = ["build"]` makes the release artifact
fresh on every direct invocation of `test:sqlx`; the top-level
`test.sh` already builds, so CI behaviour is unchanged.
The eql_v2_int4 fixture generator encrypts via cipherstash-client, whose
EnvKeyProvider requires CS_CLIENT_ID + CS_CLIENT_KEY to load the client key.
test:sqlx regenerates fixtures every run, so CI needs that pair — but
f8dfd92 only wired CS_CLIENT_ACCESS_KEY + CS_WORKSPACE_CRN (ZeroKMS auth),
leaving the generator failing with "CS_CLIENT_ID environment variable not
set". Add the client-key pair to the test job env.

Also correct comments in mise.toml, tasks/fixtures.toml, Cargo.toml, and
FIXTURE_SCHEMA.md that framed the two credential pairs as alternatives: auth
(access key + workspace CRN) and key material (client id + key) are distinct
roles and both are required.
feat(fixtures): plug-in framework + eql_v2_int4 reference fixture
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 29, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cf5a347a-b09c-45f0-9a9d-7d46a0ea4e5d

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

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch eql_v3

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

tobyhede added 20 commits June 1, 2026 12:32
Render eql_v2_<T> jsonb-backed domain families (types, functions,
operators, aggregates) from a minimal TOML manifest. Term capabilities
are fixed in terms.py (hm -> equality, ore -> equality+ordering);
output is byte-identical and headed AUTO-GENERATED — DO NOT EDIT.

Hardening: _sql_str() quote-doubler at every SQL interpolation
boundary, distinct-fixture-value validation in spec.py, SQL-identifier
validation of manifest domain names, and codegen:domain / codegen:domain:all
mise tasks. mise run build regenerates all domains every build.

Part of PR #239 (eql_v2_int4 variant family).
Four jsonb-backed domains for encrypted int4: storage-only eql_v2_int4
(every operator blocked), eql_v2_int4_eq (hm; =, <>), and the ordered
pair eql_v2_int4_ord / eql_v2_int4_ord_ore (ore; = <> < <= > >=).

Domain CHECK pins the envelope version (VALUE->>'v' = '2'). Unsupported
operators are error-throwing placeholders carrying an explanatory comment.
Uniform extractors eql_v2.eq_term/ord_term keep functional indexes engaging
without ::jsonb casts. Committed reference baselines guard byte parity.

Part of PR #239.
MIN/MAX aggregates on ord-capable int4 domains route comparison through
the domain's ORE operators — no decryption. min/max are associative, so
the state function doubles as combinefunc; with PARALLEL SAFE sfuncs and
parallel = safe, PostgreSQL can use partial/parallel aggregation on the
large GROUP BY workloads these aggregates exist to serve.

Part of PR #239.
Add blocker_language, blocker_strict, domain_over_domain, and domain_opclass
structural lints enforcing the encrypted-domain footguns (blockers must be
plpgsql and non-STRICT; no domain-over-domain; no opclass on a domain).
pin_search_path recognises the converged extractor/wrapper names intrinsically.

Part of PR #239.
…guard

One ScalarType impl plus an ordered_numeric_matrix! invocation generates
the full SQLx suite for a scalar. Fixture values are single-sourced from
the manifest. Coverage includes:

- always-on cost-preference proof (~5000 rows, enable_seqscan ON) that
  asserts on the EXPLAIN node type (Index/Index Only/Bitmap Index Scan),
  not an index-name substring; eq_count pinned to == 1 so the derived
  <> count is load-bearing
- a live-DB structural guard querying pg_operator that fails if any native
  jsonb operator is absent from the generator's blocked surface
- ORDER BY NULLS FIRST/LAST coverage for ordered domains

Part of PR #239.
…angelog

Add encrypted-domain implementation spec and generator reference; document
both aggregate paths in eql-functions / sql-support; record the int4 family
under CHANGELOG Added. CLAUDE.md gains the encrypted-domain materializer
guidance and footgun list.

Part of PR #239.
Pin third-party actions to commit SHAs, set permissions: contents: read
and persist-credentials: false on checkouts, and add a codegen job gating
the PG matrix.

Part of PR #239.
The jsonb_operator_surface guard queried pg_operator for every operator with
a jsonb operand and asserted the set is a subset of the 20 known native jsonb
operators. It swept in EQL's own cross-type operators on the legacy
eql_v2_encrypted composite (`eql_v2_encrypted ~~ jsonb`, `jsonb ~~
eql_v2_encrypted`) — they take a jsonb operand but are not native and are
unreachable from a storage scalar domain — failing CI with ["~~", "~~*"].

Exclude operands typed eql_v2_encrypted so the guard tests only the native
jsonb surface a domain can fall through to. The deliberate design is unchanged:
int4 has no LIKE, operator_surface.py pins exactly 20 operators and excludes
~~/~~*, and the matrix native_absent_ops arm asserts ~~/~~* parse-error on
storage domains.

Verified: full encrypted_domain suite 239 passed / 0 failed (was 238/1);
operator_surface Python tests 11 passed; no codegen change.
Move the int4 matrix `cargo expand` snapshot and its regeneration tooling
into this stacked PR so the main int4 PR (#239) no longer carries the
~37k-line generated snapshot.

The snapshot is a body-level fidelity backstop for the
`ordered_numeric_matrix!` / `scalar_domain_matrix!` macros — it catches
changes *inside* generated test bodies, complementing the name-inventory
snapshot (`int4_matrix_tests.txt`, checked in test-eql.yml) that catches
add/remove of whole arms.

- tests/sqlx/snapshots/int4_expanded.rs — the expanded matrix snapshot
- .github/workflows/macro-expand-eql.yml — nightly, non-blocking drift check
- mise.toml — the `test:matrix:expand` regeneration task (pinned nightly)
Hardens the non-blocking macro-expand snapshot lane:

1. Pin cargo-expand to 1.0.122 (was unpinned). cargo-expand drives the
   rustfmt pass, so an unpinned version can drift the snapshot even with a
   frozen macro + nightly.
2. Single-source the pinned nightly date in mise.toml. The workflow now
   greps `nightly-YYYY-MM-DD` from mise.toml instead of hardcoding it, so
   there is nothing to bump in lockstep — the date lives in one place.
3. Don't truncate the snapshot before a possibly-failing expand: the mise
   task now expands into a mktemp file and `mv`s on success, so a transient
   expand failure no longer leaves a 0-byte snapshot locally.
4. SHA-pin the third-party actions (checkout, mise-action, rust-cache),
   reusing the exact SHAs already pinned in test-eql.yml (commit 41c7496).
5. Document the intended body-only-change nightly gap (no pull_request
   trigger) in the workflow header comment.
…xture!

Recovered from orphaned commit 0a60f71 (dropped by a reset during the
stacked-PR shuffle). The eql_v2_int4 fixture file was ~95% boilerplate
shared with future scalar types: the spec() builder, the fixture-gen
generator test, and the property-test module differed only in name, the
Rust plaintext type, and the value const.

Add a scalar_fixture!(name, ty, values) macro that stamps out all three.
MIN/MAX in the signed-extremes test derive from <$ty>. The int2 hunk
from the original commit is dropped — int2 is not on this branch.

Test-infra only; no caller-observable change. Fixture property tests
pass (3/3); generate() still compiles under --features fixture-gen.
cargo fmt --check (tasks/test/lint.sh) flagged scalar_fixture.rs: two
assert! lines in the generated property-test arm exceed the line width, so
rustfmt wraps them. The file was committed unformatted in 4098c69 and CI
lint catches it. Formatting only — the macro expands identically, no
behaviour change.
feat: eql_v2_int4 variant family (v3)
Introduce a separate eql_v3 schema for the encrypted-domain type families
and move the int4 family into it, dropping the redundant version prefix:
eql_v3.int4{,_eq,_ord,_ord_ore}, with extractors/wrappers/aggregates
(eql_v3.eq_term, ord_term, eq/neq/lt/lte/gt/gte, min/max) also in eql_v3.
The core index-term types (eql_v2.hmac_256, eql_v2.ore_block_u64_8_256)
stay in eql_v2 and are referenced cross-schema. eql_v2 is unchanged.

- codegen: DOMAIN_SCHEMA/CORE_SCHEMA in templates.py; schema-qualified
  domain_name; schema-v3 REQUIRE edges in generate.py
- new src/schema-v3.sql; blocker helper moved to eql_v3
- pin_search_path: pin loop + structural skip broadened to eql_v3
- lints: operator/blocker/domain_over_domain/domain_opclass recognisers
  extended to eql_v3
- splinter: scope + eql_v3 allowlist rows
- SQLx harness + family/lint tests + codegen tests + reference baseline
- uninstallers drop eql_v3; docs, CHANGELOG, CLAUDE.md updated

Resolves the open PR #239 review thread asking to move the family to eql_v3.

The generator/test-quality hardening that was previously bundled here
(_sql_str, brief disambiguation, placeholder/aggregate rationale comments,
assert_index_scan_uses) now lands separately on v3-domain-type-int4, since
it is schema-independent and useful to every scalar type.
The int4 domain family moved from eql_v2 to eql_v3, but three generated
matrix tests still hardcoded eql_v2. Fixes the CI failures on PG 15/16/17:

- scale_default_combos used eql_v2.ord_term as the index extractor, but
  the ord column is eql_v3.int4_ord (extractor is eql_v3.ord_term).
- the aggregate parallel-safety introspection query filtered pg_proc on
  'eql_v2'::regnamespace, finding no min/max aggregate (now in eql_v3).

Test-only; no production SQL change.
The domain_over_domain and domain_opclass lint messages hard-coded
'eql_v2_*' and recommended eql_v2.eq_term/ord_term. Now that these
lints also target eql_v3 domains, a message firing on an eql_v3 domain
pointed at the wrong surface. Drop the version-specific label
('another encrypted-domain', 'an encrypted-domain type') and build the
extractor recommendation from the offending domain's own schema
(%s.eq_term / %s.ord_term via tn.nspname) so remediation is correct in
either namespace.

Addresses CodeRabbit review feedback on #247.
The feature-gated scale-preference sweep arm asserted
plan_text.contains(index) on raw EXPLAIN text, which can false-pass on
an incidental mention of the index name. Switch it to the JSON-plan
helper assert_index_scan_uses, matching the always-on default-category
arm and every other EXPLAIN assertion in the file, so the sweep proves
a genuine Index/Index-Only/Bitmap-Index scan node rather than a
substring.

Addresses CodeRabbit review feedback on #247.
Add the int2 ordered numeric scalar to the generated encrypted-domain
family, stacked on the int4 reference. Four jsonb-backed domains
(eql_v2_int2{,_eq,_ord,_ord_ore}) are generated from the new
tasks/codegen/types/int2.toml manifest by the existing type-generic
materializer — no generator behaviour changes.

- Register the int2 ScalarKind (i16, MIN -32768 / MAX 32767 / zero) in
  tasks/codegen/scalars.py, with test_scalars.py coverage.
- Commit the generated fixture-value const
  tests/sqlx/src/fixtures/int2_values.rs (single source of truth shared by
  the fixture generator and the matrix oracle).
- Wire the SQLx matrix oracle: impl ScalarType for i16, the eql_v2_int2
  fixture via the scalar_fixture! macro (mirroring eql_v2_int4), and the
  sealed EqlPlaintext impl for i16 (small_int cast, Plaintext::SmallInt,
  smallint oracle column).
- Add the ordered_numeric_matrix! invocation and the int2 matrix test-name
  inventory snapshot.
- Record the new family in CHANGELOG.md.

Keep the codegen reference int4-only: it is a golden master for the
type-generic generator, so one anchor detects all template/term drift. New
scalar types add no per-type baseline; documented in the spec and
tests/codegen/reference/README.md. int2 is guaranteed by the int4 reference,
the int2_values.rs staleness guard + test_scalars.py, and the SQLx matrix.
The test task hand-listed only eql_v2_int4 for fixture regeneration, so the
int2 matrix test's compile-time include_str! of eql_v2_int2.sql failed in CI
(fixtures are gitignored and regenerated each run). Add a fixture:generate:all
task that enumerates tasks/codegen/types/*.toml — the same manifests
codegen:domain:all drives — and regenerates the fixture for every type
declaring a [fixture] table, then call it from the test task. New scalar types
are now picked up automatically.
feat: move int4 encrypted-domain family into eql_v3 schema
tobyhede and others added 30 commits June 3, 2026 18:58
…_domain

Also repoint src/lint/lints.sql's REQUIRE from the moved src/schema-v3.sql to
src/v3/schema.sql so the combined build's dependency graph still resolves.
…iant (D9, D13)

Also add the v3 installer/uninstaller to the build task's MISE outputs list (and
tasks/uninstall-v3.sql to sources) so the incremental cache tracks them.
… build

The combined-build pin_search_path allowlist is eql_v2-scoped; the self-contained
eql_v3 SEM functions (composite/jsonb args) would be pinned, silently breaking v3
functional-index inlining. Mirror the eql_v2 treatment, allowlist them in splinter,
and add a direct regression guard.
… eql_v3 SEM fork

The ord_term extractor now returns eql_v3.ore_block_u64_8_256 (D12), so the
CREATE-OR-REPLACE mutation redefinitions must declare the eql_v3 return type and
construct eql_v3 SEM terms; the eq-reroute mutations move to eql_v3 SEM too. The
drop_operator_classes fixture now also drops the new eql_v3 ORE btree opclass,
which the Supabase build's **/*operator_class.sql glob likewise excludes.
…, fix blocker doc path

Hoist the src/v3 path strings into named constants in generate.rs (byte-identical
output — parity holds). Remove the now-dead Term::returns() (codegen builds the
extractor return type from CORE_SCHEMA + ctor) and its legacy eql_v2 test
assertions. Repoint the shared blocker's doc comment to src/v3/scalars/<T>/.
The eql_v3 SEM index-term functions are a hand-port of the eql_v2 originals.
The scalar matrix already exercises the array comparator's happy path end-to-end
against real ciphertext fixtures, but several branches are structurally
unreachable there and were tested only on the eql_v2 copies. Add a sibling
family test module covering them:

- differential v2<->v3 parity on real `ob` fixtures (both sides routed through
  extractor -> composite -> compare_..._terms, so the schema prefix is the only
  variable) — the strongest guard against a faithful-port slip;
- the 'Ciphertexts are different lengths' RAISE;
- NULL-term ordering branches the STRICT wrappers bypass;
- array NULL + empty/cardinality recursion base cases;
- has_* presence checks, the missing-`ob` RAISE, and the NULL-jsonb short-circuit.

Verified non-vacuous: a deliberately broken comparator fails T1/T2/T3 while the
independent T4/T5 stay green.
…entity names

Addresses CodeRabbit review: the artifact/test self-containment assertions only
matched schema-qualified eql_v2. references; extend them to also reject
eql_v2_<entity> names (eql_v2_encrypted, eql_v2_configuration) while still
allowing prose mentions of eql_v2 in doc comments. Verified the v3 artifact and
src/v3 contain zero eql_v2_ occurrences.
…e SCHEMA

With eql_v3 fully self-contained, the encrypted-domain families and the SEM
index-term types they call live in one schema, so there is no second schema to
point the core types at. Replace the CORE_SCHEMA/DOMAIN_SCHEMA pair with one
SCHEMA = "eql_v3" constant; the templates read it via the {{ schema }} global.
Output is byte-identical (golden parity holds).
…ardening

- Cargo.toml: update stale fixture command to fixture:generate:all
- eql-functions.md: index examples use distinct _eq/_ord columns (a column
  carries a single domain, so eq_term/ord_term apply to different columns)
- scalars/mod.rs: header describes the scalar_types!(matrix_suites) layout
- codegen-parity.sh: ls *.sql -> portable find (no set -e abort on empty dir)
- inlinability.rs: narrow arity-1 hmac_256 match to the jsonb overload
…ators

The six comparison operator backing functions (_eq, _neq, _lt, _lte,
_gt, _gte) were missing required @param and @return Doxygen tags,
failing docs:validate:required-tags with 6 errors and 6 warnings.
Tags follow the eql_v2 sibling src/ore_block_u64_8_256/operators.sql
convention.
cargo fmt --check was failing CI (test:lint and test:crates jobs) on
this file; apply rustfmt with no logic changes.
…t consts

Make the SQLx scalar-matrix harness type-agnostic ahead of the first
non-integer scalar, without adding one. Three integer assumptions are lifted:

- `ScalarType::FIXTURE_VALUES` (a `const`) becomes `fn fixture_values()`, so a
  scalar whose values can't be const-constructed can return a borrow of a
  lazily-built `Vec` instead. Integer impls still hand back their
  `eql_scalars::<T>_VALUES` const.
- New `min_pivot()` / `max_pivot()` trait methods replace the matrix's direct
  `<T>::MIN` / `<T>::MAX` pivot references, so a scalar without an inherent
  `::MIN`/`::MAX` const can supply an explicit sentinel.
- The ORDER BY arms build their `WHERE` clause from `to_sql_literal(zero)`
  instead of a hardcoded `> 0`, so a non-integer plaintext column typechecks.

Behaviour-preserving for the existing `int4` / `int2` types: the integer
`min_pivot`/`max_pivot` resolve to `Self::MIN`/`Self::MAX`, `to_sql_literal(0)`
renders `0`, and the generated test names are unchanged. The int4 cargo-expand
snapshot is regenerated to track the method-based bodies.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
- scalar_domains.rs: document fixture_values() stable-order contract
  (callers compare element-wise and index positionally without sorting)
- eql-tests-macros: assert emitted min_pivot/max_pivot bodies instead of
  loose MIN/MAX substrings that also match the doc comment

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
feat(int8): add eql_v3.int8 encrypted-domain type family
…test hardening

Collates and resolves code-review feedback on the self-contained eql_v3 PR.

Inlinable SEM helpers (coderdan threads):
- Convert eql_v3.jsonb_array_to_bytea_array and
  eql_v3.jsonb_array_to_ore_block_u64_8_256 from LANGUAGE plpgsql to
  inlinable LANGUAGE sql IMMUTABLE (no SET), using a CASE-scalar-subquery
  form so JSON-null/empty-array inputs still return NULL rather than
  raising (a naive FROM-SRF + WHERE rewrite would regress null to error).
- These take a bare jsonb (not a domain), so pin_search_path.sql's
  structural skip does not cover them; opt them in via its documented
  'eql-inline-critical' COMMENT marker so they install unpinned and the
  planner can inline them. Add matching splinter allowlist rows. The
  eql_v2 copies stay plpgsql by design. Direct SEM tests added (sem.rs).

Stale references:
- eql-functions.md / sql-support.md: v3 extractors now document eql_v3
  SEM return types, not eql_v2.
- pin_search_path.sql header, scalar_types.rs and mutations.rs comments.

Test/build hardening:
- writer.rs validates the AUTO-GENERATED ownership header before writing.
- eql-scalars int_values! fails at compile time on narrowed-fixture overflow.
- parity.rs asserts the generated file set, not just per-file contents.
- mise.toml: shared test:sqlx:prep so test:sqlx:watch gets the same prep.
- build.sh v3-only build rejects REQUIRE edges outside src/v3.
- fixtures: single-source PAYLOAD_COLUMN const; enforce 63-byte identifier limit.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Self-contained eql_v3 schema + standalone v3 installer
refactor(tests): generalise scalar matrix harness off integer-inherent consts
Add the `eql_v3.date` encrypted-domain scalar — the first non-integer ordered
type — on top of the integer-agnostic harness refactor (parent PR). The SQL
codegen needs no change (domains are jsonb-backed and token-driven); the work is
one catalog row plus the temporal wiring the refactor enabled.

Catalog (`eql-scalars`): `ScalarKind::Date` (`chrono::NaiveDate`),
`Fixture::Date(&str)` (zero-dep ISO strings), `DATE_FIXTURES` (16 dates incl.
the three matrix pivots), and `pub const DATE` appended to `CATALOG`, with
mirrored panic / pivot-presence / token-order tests.

Harness: an explicit `[temporal]` marker in the `scalar_types!` dispatch list
drives the divergences from the integer path — the `impl ScalarType` for a
temporal scalar is hand-written (chrono values can't be a `const` slice; pivots
are explicit sentinels), and `scalar_fixture!` stamps a pivot-presence assert
instead of the integer signed-extreme asserts. Adds `impl ScalarType for
NaiveDate` (LazyLock-parsed values, `min_pivot`/`max_pivot` sentinels,
quoted `to_sql_literal`), `EqlPlaintext for NaiveDate` (Cast::DATE), the sqlx
`chrono` feature + direct `chrono` dep, the CHANGELOG entry, and a temporal-kinds
note in the adding-a-scalar reference.
feat(scalars): add eql_v3.date encrypted-domain type + temporal wiring
… reference

CATALOG now includes the non-integer ordered scalar (date) alongside the
integers, so the 'only the integer scalars today' wording was stale.
Addresses CodeRabbit feedback on PR #256.
ScalarKind loses the five partial accessors that panicked on non-integer
kinds; bounds now live on the total BoundedIntKind, reached via
ScalarKind::as_bounded_int(). Date::min_symbol() is now a compile error.
A CATALOG invariant test replaces the deleted #[should_panic] tests.
Update the adding-a-scalar reference's `kind` bullet to reflect that the
bounded-numeric accessors moved to the total BoundedIntKind sub-enum, and
add the implementation plan under docs/superpowers/plans/.
refactor(eql-scalars): total BoundedIntKind sub-enum (replaces panicking ScalarKind accessors)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant