Repository navigation
Description
Background
EQL (Encrypt Query Language) is the SQL library we install into a customer's Postgres database to store and query encrypted JSON documents. Each field in an encrypted document is found by a SteVec selector, a keyed hash of the field's JSON path. Because the hash is keyed by the CipherStash workspace, the same path has a different selector in every workspace. That is why the SQLx test suite in packages/eql/tests/sqlx never pins a selector. It reads selectors out of the loaded test fixture (#1200).
Problem
The v3_ste_vec fixture holds ten documents, and the tests need the selector of the $.hello field. Nothing in the fixture records which selector belongs to which path. So is_hello_op (packages/eql/tests/sqlx/src/fixtures/v3_ste_vec.rs) guesses from the shape of the encrypted data. It picks the one selector that:
- appears on every row,
- has order terms (
op) of exactly two lengths, exactly one character apart, and - has only one row at the longer length.
That only works because of three facts about the fixture:
- the CLLW-OPE order-term encoding is 16 hex characters per character plus 20;
ROW_COUNT = 10makesworld-1..world-9againstworld-10a 9-to-1 split;- no other string leaf has that same profile.
Change the order-term layout, the row count or the fixture documents, and every test that uses hello_op_selector fails. The failure is loud and lists every candidate, so nothing passes silently. But the rule has to be re-derived and patched each time. coderdan raised this in review on #1204 and called it fine as a follow-up.
Proposal
- When the fixture is generated, compute the
$.helloselector with the client that already does this:ste_vec_query_selectorinsrc/fixtures/cipherstash.rs. An e2e test already uses it for$.hello, so its output matches the stored selector. Write the result intov3_ste_vec.sqlas a small table,fixtures.v3_ste_vec_selectors (path text primary key, selector text). - Let
FixtureSpec::runpass extra SQL through to the script. The driver'swrite_scriptalready has anextra_headerslot, butrun()does not expose it yet. - Make
hello_op_selectorread the$.hellorow from that table. Keep one cheap shape check (the selector carriesopon every row) so a bad table still fails loudly. Then deleteis_hello_op,HELLO_OP_RULEand their unit tests. - Update the
AGENTS.mdandtests/sqlx/README.mdparagraphs that describe rule-based selection.
v3_doc_integer's $.field does not need this, because its rule is just "the only op-carrying selector".
Trade-off
The lookup would trust the same client that wrote the fixture, where today it is derived independently from the data. That matters little here, because the selector only finds the entry, and the SQL tests still check every result against plaintext. Regenerating the fixture needs CipherStash credentials, so CI's credentialed prep job is the first place the change can be checked.
Generated by Claude Code
Activity
coderdan commentedon Oct 25, 2026
Won't fix. This is EQL v3 behaviour, and v3 will not be changed further. The fix is in EQL v4's
json_treefamily (#1288, #1325, #1329, #1363), which does not reuse the v3 SteVec design.https://claude.ai/code/session_01N7bCqa3ZVJMcEE99RzVqEu