commands: the aggregation expression evaluator
The whole corpus is green: 46 pass, 0 fail, byte-identical to mongod 8.3.7 on
every case including all 27 expressions and the compound `_id` that was the
last accumulator failure.
Expressions are *compiled once per pipeline and evaluated per document*, and
that split is the point rather than an optimisation: it keeps the property M2's
refusals bought, which is that a pipeline that cannot be answered is refused
before a single document is read instead of half way through with part of the
work already reported. `Expr` is the compiled tree, `compile_expr` reports,
`eval_expr` cannot.
Nineteen operators: `$literal`, the five arithmetic ones, seven comparisons,
`$and`/`$or`/`$not`, `$cond` in both its forms, `$ifNull` and `$switch`. Plus
the two shapes that are not operators at all -- a compound document, which is
what a `$group` `_id` usually is, and an array.
Everything the corpus recorded, and none of it guessable:
- absent and a present null are *different* internally, because `$ifNull`
treats them alike and `$push` does not. Hence `?bson.Value` throughout,
where the obvious shortcut is to fold absent into `.null` at the boundary
and lose the distinction for good.
- arithmetic over absent or null is `null` -- not an error, not zero -- and
over a string is an error, 7157723.
- `$divide` by zero is 4848401, `$switch` with no branch and no default is
40069, and both are failures only a document can produce, so `EvalError`
exists and `report_eval_error` maps it.
- two operators in one expression document is 15983 and *not* `$group`'s
40238: mongod distinguishes an expression from an accumulator there.
- truthiness is MongoDB's, so `-5` is true and `0.0` is false.
- `$mod` follows the dividend's sign, so -5 mod 4 is -1.
- `$not` takes a bare argument as readily as a one-element array.
`compile_expr` and `compile_operator` call each other, so their error set is
written out rather than inferred -- Zig cannot infer a cycle, and the failure
mode is a "dependency loop" message that says nothing about expressions.
Three cases left the Tier 0 refusal test, because a compound `_id`, `$literal`
and a `$multiply` argument all work now. What is refused should be what is
missing, so an unknown operator and a wrong operand count took their place.
191/191 unit tests in ReleaseFast and ReleaseSafe, 83/83 fuzz, e2e 49, e2e2
concurrent, e2e3 16, e2e4 17, e2e6 72, e2e7 86, crud corpus unchanged at
201/90/196.
This commit is contained in:
@@ -62,8 +62,8 @@ Recorded against mongod 8.3.7. At the M2 tip it read 9 pass / 10 fail; with the
|
||||
accumulators in:
|
||||
|
||||
```
|
||||
group-accumulators.json 18 pass 1 fail 0 skip
|
||||
expressions.json 1 pass 26 fail 0 skip
|
||||
group-accumulators.json 19 pass 0 fail 0 skip
|
||||
expressions.json 27 pass 0 fail 0 skip
|
||||
```
|
||||
|
||||
`group-accumulators` found its first real disagreement on the way to 18:
|
||||
@@ -71,11 +71,10 @@ expressions.json 1 pass 26 fail 0 skip
|
||||
that counted documents rather than numbers would have passed every test
|
||||
anybody would think to write by hand.
|
||||
|
||||
`expressions.json` is the next tier's spec, recorded and not yet implemented --
|
||||
its one pass is the unknown-operator refusal M2 already answers correctly.
|
||||
Expressions are exercised through `$group`, because `_id` and the accumulator
|
||||
arguments are the only expression positions that exist until `$addFields` and
|
||||
`$project`'s computed fields land.
|
||||
`expressions.json` was recorded before the evaluator was written and read
|
||||
1 pass / 26 fail against it; it is green now. Expressions are exercised through
|
||||
`$group`, because `_id` and the accumulator arguments are the only expression
|
||||
positions that exist until `$addFields` and `$project`'s computed fields land.
|
||||
|
||||
What recording it settled, none of which is guessable:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user