Skip to content

Commit ea26f9d

Browse files
committed
feat(stack-encrypt)!: order floats through cllw-ore's F64 target
An `f32` or `f64` order term is cllw-ore's vitaminc encryption of the value at the `F64` target, whose vitaminc-ore encoding folds `-0.0` into `0.0` and every NaN into one value above `+Infinity`: Postgres's `float8` order. So the ordering operators are now answered over floats, on the typed path (`op::Ordered` for `f32` and `f64`) and over a data plan's `float32` and `float64` fields, and stashgen writes `Lt`, `Lte`, `Gt`, `Gte` and `Between` on a float field with `ore` or `ope`. An `f32` is ordered as the `f64` it widens to: its `Orderable` target is `F64` and vitaminc widens it exactly, so an `f32` and its `f64` are one term and `f32` can join `op::Ordered`. A float `Between` is not refused when inverted (a range from NaN, which sorts last, matches nothing), as SQL's `BETWEEN` is not. The Go query profile gains a sealed `Score float64` with ORE, matching the Rust query fixture, and the live PostgreSQL test stores the score and its ORE term so the rows it reads back open. Float pins, the float ordering baseline, the integer-width lengths and the `score` field in the cross-path fixture are added; the fixtures and UI snapshots are re-recorded. Closes #1236.
1 parent ba29e03 commit ea26f9d

28 files changed

Lines changed: 695 additions & 91 deletions

‎languages/golang/CHANGELOG.md‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,16 @@ listed under **Breaking**. Releases are tagged `languages/golang/vX.Y.Z`.
7575
folded form (ADR-0002: case and accents folded, under Unicode 16), so
7676
`Café` and `cafe` tie and `alice` orders before `Zoe`. Text order terms
7777
change bytes from the alpha's; none was deployed.
78+
- **Ordering methods on floats.** A `float32` or `float64` field with
79+
`index=ore` or `index=ope` gets `Lt`, `Lte`, `Gt`, `Gte` and `Between`.
80+
Floats order as PostgreSQL's `float8` does (ADR-0002): `-0.0` ties `0.0`,
81+
and every NaN is one value, above `+Inf`. A `float32` orders as the
82+
`float64` it widens to. Every order term changes bytes from the alpha's,
83+
now that the engine encrypts them through cllw-ore's vitaminc schemes;
84+
none was deployed.
7885
- **stashgen notices an index no generated method queries**: an `ore` or
79-
`ope` index on bytes or a float (the engine answers the orderings over
80-
integers and text only), or the second of two order indexes on a field.
86+
`ope` index on bytes (the engine answers the orderings over integers,
87+
floats and text only), or the second of two order indexes on a field.
8188
The index is still stored; the notice is on stderr and in the file.
8289

8390
### Changed

‎languages/golang/cmd/stashgen/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ For `-type User`, the file `user_stash.go` holds:
124124
- `Fields`, with one entry for each sealed field.
125125
An opaque struct, or a struct with no sealed field outside an opaque one, gets no `Fields`: nothing in it is sealed on its own, so nothing can be queried.
126126
An entry encrypts one value, and has one query method for each operator the engine answers on the field: `Eq`, `Lt`, `Lte`, `Gt`, `Gte`, `Between` or `Matches`.
127-
stashgen asks the embedded engine which operators each field answers, and which index answers each one, so it holds no copy of those rules: an equality index answers `Eq`, a match index on a string `Matches`, and an ORE or OPE index on an integer or a string the four orderings and `Between`, through ORE when the field declares both. A string orders by its folded form: case and accents folded, under Unicode 16.
127+
stashgen asks the embedded engine which operators each field answers, and which index answers each one, so it holds no copy of those rules: an equality index answers `Eq`, a match index on a string `Matches`, and an ORE or OPE index on an integer, a float or a string the four orderings and `Between`, through ORE when the field declares both. A float orders as PostgreSQL's `float8` does (`-0.0` ties `0.0`, NaN sorts last), and a string by its folded form: case and accents folded, under Unicode 16.
128128
`encrypt_into=TextEq` answers `Eq`, which returns the type's query value, `eql.TextEqQuery`.
129129
`encrypt_into=TextMatch` answers `Matches`, which returns an `encrypt.EQLMatchQuery`: the `eql.TextMatchQuery` operand, with `Verify` and `TargetLimit`.
130130
The query of `Matches` carries `Verify` and `TargetLimit`, because its answer can hold rows that do not match; an exact operator's query carries neither.

‎languages/golang/encrypt/README.md‎

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -216,14 +216,14 @@ or a type defined over one (`type Email string`):
216216
| `int8`, `int16`, `int32`, `int64`, `int` | an integer of the field's own width (`int` is 64-bit) | `equality`, `ore`, `ope` | `Eq`, `Lt`, `Lte`, `Gt`, `Gte`, `Between` |
217217
| `uint8` (`byte`), `uint16`, `uint32`, `uint64`, `uint` | the same, unsigned | `equality`, `ore`, `ope` | the same |
218218
| `encrypt.Int128`, `encrypt.Uint128` | a 128-bit integer | `equality`, `ore`, `ope` | the same |
219-
| `float32`, `float64` | a float of its width | `equality`, `ore`, `ope` | `Eq` |
219+
| `float32`, `float64` | a float of its width | `equality`, `ore`, `ope` | `Eq`, `Lt`, `Lte`, `Gt`, `Gte`, `Between` |
220220
| `encrypt.Decimal` | a decimal: a 96-bit coefficient and a scale up to 28 | `equality` | `Eq` |
221221
| `encrypt.Date` | a calendar date | `equality` | `Eq` |
222222
| `time.Time` | a timestamp, to the nanosecond, in UTC | `equality` | `Eq` |
223223
224224
Each query method needs its index declared. An `ore` or `ope` index on bytes
225-
or a float stores its term, but the engine answers no ordering through it
226-
yet, so such a field has no `Lt` (see [Queries](#queries)).
225+
stores its term, but the engine answers no ordering through it, so such a
226+
field has no `Lt` (see [Queries](#queries)).
227227
228228
`encrypt.Date`, `Int128`, `Uint128` and `Decimal` are vitaminc's `vcvalue`
229229
types; build them with `encrypt.DateOf`, `ParseInt128`, `ParseUint128` and
@@ -238,8 +238,8 @@ width, and each width is passed to the order scheme as itself, so an `int8`
238238
field's terms are an `int8`'s. Equality follows vitaminc's normalisation: an
239239
integer equals itself at every width, decimals compare at any scale
240240
(`1.50 = 1.5`), and a timestamp compares to the microsecond. An order index on
241-
a decimal, date or timestamp is refused for now: those order terms wait for
242-
new cllw-ore and ore-rs releases (#1236).
241+
a decimal, date or timestamp is refused for now: the engine derives no
242+
order term for those kinds yet.
243243
244244
A `match` index stores text with no token (an empty string, separator-only
245245
text, or text shorter than the n-gram length of 3 characters) as the empty
@@ -297,7 +297,12 @@ range matches nothing. So `Between("b", "A")` matches nothing, since `A`
297297
folds to `a`, below `b`. Text holding a code point Unicode 16 does not assign has no
298298
fold, and its query or write is refused with `encrypt.ErrTerm`.
299299
300-
An order index on bytes or a float stores its term but answers no ordering
300+
Floats order as PostgreSQL's `float8` does (ADR-0002): `-0.0` and `0.0` are
301+
one value, and every NaN is one value, above `+Inf`, so `Between(math.NaN(),
302+
math.Inf(1))` matches nothing. A `float32` orders as the `float64` it
303+
widens to.
304+
305+
An order index on bytes stores its term but answers no ordering
301306
operator, so such a field has no `Lt`; nor does the `ope` index of a field
302307
that declares `ore` too, since `ore` answers the orderings. stashgen prints a notice for each such index, on stderr and in
303308
the generated file: the terms are stored, and only your own code can compare

‎languages/golang/encrypt/doc.go‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,10 @@
153153
// Eq returns an [EqualityTerm] (or, for a field with encrypt_into, the EQL
154154
// type's query value), an ordering an [OreTerm] or [OpeTerm], and Between a
155155
// [Range] of two. Their answers are exact. The orderings are answered on
156-
// integers and on text, which orders by its folded form (case and accents
157-
// folded, under Unicode 16), so Café and cafe tie and alice orders before
158-
// Zoe. Matches returns a [MatchQuery],
156+
// integers, on floats, which order as PostgreSQL's float8 does (-0.0 ties
157+
// 0.0, NaN sorts last), and on text, which orders by its folded form (case
158+
// and accents folded, under Unicode 16), so Café and cafe tie and alice
159+
// orders before Zoe. Matches returns a [MatchQuery],
159160
// whose answer can hold rows that do not match: size the fetch with
160161
// [MatchQuery.TargetLimit], then decrypt the rows and keep those
161162
// [MatchQuery.Verify] reports true, one call into the engine for the page.

‎languages/golang/encrypt/internal/testusers/kinds_stash.go‎

Lines changed: 25 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎languages/golang/encrypt/internal/testusers/probe_stash.go‎

Lines changed: 132 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)