Repository navigation
Conversation
…ment field
`element_match` takes `{"path", "wildcard_bound"?, "predicates"}`: an
array's elements by a path with a wildcard, and predicates on fields of
an element by a member path relative to it. Its operand is
`eql_v4.query_json_elements`: per predicate, the `subtree` tokens of the
member's full path at the bound, and the leaf operands the orderings
derive for its values. `eq` is one leaf as both `gte` and `lte`, so its
equality is the ordering term's (folded text, numbers as float64), and
`between` is `between_at`'s two. The elements' read is derived once and
each member's extends its tokens by the member's steps
(`JsonSubtreeKey::below`, the member and element rules the full path's
key applies). No new derivation.
Refused before anything is derived: a path with no wildcard, a
`wildcard_bound` of 0, a position in the path or a member, a wildcard in
a member, an empty predicate list, an unknown `op`, a value no ordering
takes, and a member whose full path the field does not order. The operator is listed only on a
field that orders a path and whose type names the elements domain
(`TargetDescriptor::with_elements_query_sql_domain`), so a host that
does not name it, such as the Go guest today, sees no new operator.
The heap hygiene suite's `json_open (subtree)` watch fails on this
branch, and passes on the base, with `load_keysets`' 256-byte future box
freed holding a retrieved data key and stack addresses (macOS arm64
debug build). Boxing the keyset loads' `join_all` makes the residue go
away; which write copies it in is not identified, so this is a
workaround, recorded as an observation at the call site.
Refs #1440
…PostgreSQL Seals contact lists with the JSON index, derives `element_match` through a data plan over eql-bindings' target table, and queries the column: two contacts each holding half of the predicate are returned by the superset query (`@>` and an ordering over `json_tree_path_query`) and not by `json_tree_element_match`; one contact holding both is returned by both; a folded spelling ties under the element predicate and not under exact containment; a bound below the longest array raises; and `json_tree_subtree` equals the indexed read without `k` under every budget, the sealed-root fallback at `k = 0`. The rest of the SQL's contract is pinned over the same documents and operands: the operand domain's CHECK member by member, over a derived operand and its parts; one element must satisfy every member under the sealed root and the open one; each bound, `between` and `eq` compare as the entry operators do, and a field with no ordering term or no field satisfies none; a probe's hit raises, including a token whose node was moved to the element's selector; and `k` is the place of the token whose node a row sits in. EQL tests run over real encrypted data, and the SQLx suite has no way to produce a JSON tree document, so these live here. Refs #1440
A filter compiler that targets `jsonb_path_match(col, $1, $2)` binds one predicate and its values. On `public.eql_v4_json_search` each question is its own operator, so the same filter had to be assembled per leaf. `eql_v4.query_json_predicate` is a boolean tree, one key per node (`and`, `or`, `not`, `contains`, `exists`, `cmp`, `elements`), whose leaves are the operands the family's operators already take. Its CHECK validates each leaf with that operand's own validator: the needle's, a new `json_tree_is_valid_bounds` that the element member's CHECK now calls too, and the elements predicate's. `eql_v4.json_tree_match` evaluates the tree with SQL three-valued logic, so it answers what the hand-written WHERE of those operators answers, NULL included, and `@@` on `(eql_v4_json_search, eql_v4.query_json_predicate)` is the operator form. The jsonpath `@@` blocker stays; an untyped operand now matches both and is refused as ambiguous rather than reaching native jsonb. The document is decompressed once per row and indexed once when the tree has an element leaf: the element match body moves into `json_tree_element_match_opened`, which both functions call. A `contains` leaf applies `json_tree_contains`'s rule to the projection read once. Children are read in order and the read stops at the first that decides the answer, so an element leaf the answer does not depend on cannot raise. The domain joins the catalog as the json_tree family's PredicateQuery role, so the eql-bindings v4 inventory and the Go type `eql.JSONSearchPredicateQuery` come from the generator. Refs #1441 Claude-Session: https://claude.ai/code/session_01MrxG724qYKcAWJdcABBFNY
…ent operand
`match` takes what `jsonb_path_match` takes, `{"predicate", "vars"?,
"wildcard_bound"?}`, and derives the `eql_v4.query_json_predicate`
operand. A hand-written parser reads the subset of SQL/JSON path
predicates the terms can answer: comparisons of a path (`$.a.b`,
`$.a[*].b`, `$."a b"`) with a variable or a literal, `exists(path)`,
`exists(path ? (@.x == $v && @.y > $w))`, `&&`, `||`, `!` and
parentheses. Everything else is refused at the byte it starts, as
`Error::Predicate`, naming the construct in the grammar's words and
never quoting the text, whose keys and values can be customer data.
Each comparison is compiled onto an existing operator's input and
derived by that operator's code: `==` to `eq_at`'s needle, `!=` to a
`selector` and not the needle, an ordering to a `selector`, not the
`null` needle, and the ordering's leaf, so an absent path and a JSON
null answer false as jsonpath has them; an ordering at a wildcard and
a filter of more than one condition to `element_match`, a filter of one
equality to `eq_at`. No new derivation. `!=` at a wildcard, which asks
whether some element differs, is refused.
New reasons: `json_predicate_syntax`, `json_predicate_unsupported`,
`json_predicate_unknown_variable`, `json_predicate_value_type`,
`json_predicate_not_equal_at_wildcard` and `json_predicate_too_deep`.
The operator is listed only on a type that names the domain
(`TargetDescriptor::with_predicate_query_sql_domain`), so a host that
does not, such as the Go guest today, sees no new operator.
Refs #1441
Claude-Session: https://claude.ai/code/session_01MrxG73fnX1p7oiizh3GRG5
…stgreSQL Documents sealed by Stack Encrypt's JSON index, predicates compiled by `match` through a data plan over eql-bindings' target table, and `eql_v4.json_tree_match` and `@@` over the column, row by row against PostgreSQL's `jsonb_path_match` over the plaintexts, NULL included, and against the WHERE written from the operands each leaf's own operator derives. The rest of the SQL's contract is pinned over the same documents and derived operands: the domain's CHECK node kind by node kind, over a compiled tree and its parts; for every predicate, the WHERE written from the compiled tree's own leaves; three-valued logic under `and`, `or` and `not`, and an ordering leaf alone; a `contains` leaf as `@>`, the root scalar's `alt` included; an `elements` leaf as the element match, a leaf the answer does not depend on left unread; and `@@` with a typed predicate, a blocked jsonpath, an untyped operand and NULLs. EQL tests run over real encrypted data, and the SQLx suite has no way to produce a JSON tree document, so these live here. Refs #1441 Claude-Session: https://claude.ai/code/session_01MrxG724qYKcAWJdcABBFNY
🦋 Changeset detectedLatest commit: 8b8cfc6 The changes in this PR will be included in the next version bump. This PR includes changesets to release 12 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Contributor
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueComment |
Contributor
Mutation testing (cargo-mutants,
|
| caught | missed | unviable | timeout |
|---|---|---|---|
| 226 | 0 | 51 | 0 |
Every mutant in the changed lines was caught by a test.
coderdan
force-pushed
the
feat/eql-v4-json-tree-element-match
branch
from
October 31, 2026 03:33
58fb199 to
3e0fe7b
Compare
Contributor
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Temporary. GitHub has started no Actions runs on #1446's head (
8b8cfc652) since its last force-push. This draft PR points at the same commit, against the same base, so CI runs on it. It is closed and its branch deleted once #1446 has runs of its own.https://claude.ai/code/session_01MrxG73fnX1p7oiizh3GRG5