You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
refactor(spec,plugin-security)!: QueryAST stops declaring what no executor runs (#4286) (#4294)
Tombstone `query.joins` and `query.windowFunctions` (retiredKey — authoring
either is a tsc error and the parse carries the prescription), and delete the
orphaned Join and WindowFunction schema clusters with their keys: `expand` is
the live spelling for related records, and the one live window-function door
(`SqlDriver.findWithWindowFunctions()`) consumes its own flat driver shape
that the spec vocabulary never matched. Both removals register as protocol-18
semantic migrations (`query-joins-retired`, `query-window-functions-retired`)
— QueryAST is a request shape with no stored source to rewrite (#4196
precedent). Both names stay reserved at the REST boundary while the
tombstones live (retiredKey keeps them in `keyof QueryAST`).
Mark the six search sub-flags and `AggregationNode.filter`
`[EXPERIMENTAL — not enforced]` — declared engine affordances the ADR-0061
expansion and the aggregation paths never receive.
Close the gate that let the class stay invisible: `QuerySchema` joins the
liveness ledger through SPEC_ONLY_SCHEMAS as governed type `query`
(liveness/query.json — 15 live with evidence, 7 experimental via describe
markers, 5 dead). `cursor` / `distinct` / `having` are recorded dead with
evidence pending their #4286 step-3/4 dispositions, not unilaterally removed
— `distinct` is mis-wired (its only effect is REST count-suppression) and
`having` is the strongest enforce candidate (engine.aggregate() currently
rebuilds the driver AST without it).
The FLS predicate guard drops its windowFunctions walk (the clause no longer
exists to leak through); the `having` and `aggregations[].filter` walks stay
while those members stay declared.
Claude-Session: https://claude.ai/code/session_012K2BX3WQwPBA2ZBehqJUBX
Co-authored-by: Claude <noreply@anthropic.com>
refactor(data)!: the QueryAST request surface stops declaring what no executor runs — `joins` and `windowFunctions` removed, six search flags and `aggregations[].filter` marked experimental, and the liveness ledger now governs the query surface (#4286)
7
+
8
+
#4196 removed one declared-but-inert member from `FieldNode`. Applying the same
9
+
method to the rest of the request surface (#4286) found 12 more members of
10
+
`QueryAST` that no executor runs — `packages/objectql`'s `engine.ts` contains
11
+
zero reads of any of them on the query path. This change dispositions the
12
+
mechanical tiers and closes the gate that let the class stay invisible.
13
+
14
+
**Removed (tombstoned): `query.joins` and `query.windowFunctions`.**
15
+
16
+
-`joins` — no engine or driver ever read it; a query carrying it silently ran
17
+
as a single-table query. Related-record retrieval already has a live
18
+
spelling: `expand`. The orphaned `JoinNode` / `JoinNodeInput` /
19
+
`JoinNodeSchema` / `JoinType` / `JoinStrategy` exports are deleted with the
20
+
key (`data/JoinNode`, `data/JoinType`, `data/JoinStrategy` leave the
21
+
published JSON schemas).
22
+
-`windowFunctions` — `find()` never applied it, so every OVER clause it
23
+
declared was silently dropped. The one live door is the SQL driver's own
24
+
`findWithWindowFunctions(object, query)` (driver-level, not on the
25
+
`IDataDriver` contract), and its input is a flat driver shape the spec
26
+
vocabulary never matched — `WindowFunctionNodeSchema` declared `field` /
27
+
`over` / `frame` members that door never read. The `WindowFunction` /
28
+
`WindowSpec` / `WindowFunctionNode` exports are deleted with the key.
|`joins`| No driver reads it — there is no `query.joins` / `ast.joins` consumer anywhere in `packages/`|
96
93
|`having`| Never read: `engine.aggregate()` forwards only `object`/`where`/`groupBy`/`aggregations`, and the in-memory fallback has no HAVING stage |
97
94
|`cursor`| Accepted by `EngineQueryOptions`, but no driver implements keyset pagination |
98
95
|`distinct`| Not applied by `find()`; the SQL and in-memory drivers expose a separate `distinct(object, field, filters?)` method instead |
99
-
|`windowFunctions`|Not applied by `find()`; `SqlDriver.findWithWindowFunctions()`is a separate method that is not on the `IDataDriver` contract|
100
-
|`search.fuzzy` / `boost` / `operator` / `minScore` / `language` / `highlight`|Parsed by the schema but ignored: only `query` and `fields` drive the expansion |
96
+
|`aggregations[].filter`|`[EXPERIMENTAL — not enforced]` — a SQL `FILTER (WHERE …)`affordance neither the SQL builders nor the in-memory fallback applies|
97
+
|`search.fuzzy` / `boost` / `operator` / `minScore` / `language` / `highlight`|`[EXPERIMENTAL — not enforced]` — only `query` and `fields` drive the expansion |
101
98
102
99
`top` is the exception that *is* honored — the engine normalises it to `limit`.
100
+
101
+
Two members left this table by being **removed** (#4286, ADR-0049 enforce-or-remove):
102
+
`joins` and `windowFunctions` are tombstoned in `@objectstack/spec` 18 — a query
103
+
carrying either fails to parse with the upgrade prescription, and authoring one is a
104
+
`tsc` error. Related records are read through `expand`; window functions remain a
105
+
SQL-driver door (`SqlDriver.findWithWindowFunctions()`). The inert members that
106
+
*remain* declared (`having`, `cursor`, `distinct`, and the experimental flags above)
107
+
are tracked in the liveness ledger (`packages/spec/liveness/query.json`) pending
108
+
their #4286 dispositions.
103
109
</Callout>
104
110
105
111
### Key Types
@@ -118,7 +124,7 @@ interface AggregationNode {
118
124
field?:string; // optional for COUNT(*)
119
125
alias:string; // result column alias
120
126
distinct?:boolean; // DISTINCT before aggregation — in-memory path only
121
-
filter?:FilterCondition; // FILTER WHERE clause — protocol only, never applied
127
+
filter?:FilterCondition; //[EXPERIMENTAL — not enforced] FILTER WHERE clause — never applied
122
128
}
123
129
124
130
// FieldNode — one entry of the select list. A field name, optionally dotted to
@@ -742,51 +748,43 @@ expansion emits a plain `$contains`, which `SqlDriver` compiles to a parameteris
742
748
`LIKE '%…%'` with no case folding — so the dialect's own `LIKE`/collation rules decide —
743
749
while the in-memory driver matches with a case-insensitive regex. Only `select` /
744
750
`status` option *labels* are matched case-insensitively by the expansion itself.
745
-
`fuzzy`, `boost`, `operator`, `minScore`, `language`, and `highlight` are accepted by
746
-
the schema but ignored by the expansion.
747
-
748
-
### Joins (protocol only)
751
+
`fuzzy`, `boost`, `operator`, `minScore`, `language`, and `highlight` carry
752
+
`[EXPERIMENTAL — not enforced]` markers (#4286): the schema accepts them, the
753
+
expansion ignores them.
749
754
750
-
`QuerySchema` defines a `joins` array, but **no driver reads it** — there is no
751
-
`query.joins` consumer anywhere in `packages/`, so a query carrying `joins` silently
752
-
runs as a single-table query. Use `expand` (§4) for relationship loading, or two
753
-
queries joined in application code.
755
+
### Joins — removed (#4286)
754
756
755
-
For reference, the protocol shape is:
757
+
`query.joins` was **removed in `@objectstack/spec` 18** (#4286, ADR-0049
758
+
enforce-or-remove): no driver ever read it, so a query carrying `joins` silently ran
759
+
as a single-table query. The key is tombstoned — authoring it is a `tsc` error, and a
760
+
query that still carries it (even as an empty array) fails to parse with the upgrade
761
+
prescription. The `JoinNode` / `JoinType` / `JoinStrategy` exports left with it.
0 commit comments