The interval CARRIER, and interval_out.
A deftype, deliberately, and not a defrecord. The consolidation
plan warns that "a record's structural =/hash would split GROUP BY
and joins", and that is true -- but it is a reason to choose the
carrier's equality, not a reason to avoid a carrier. PostgreSQL
compares '1 mon' and '30 days' as EQUAL (interval_cmp_value:
a month is 30 days, a day is 24 hours) while PRINTING them
differently, so:
structural equality splits two values PostgreSQL groups; canonical TEXT cannot be the key either, for the same reason.
clojure.core/= routes a non-collection, non-Number object to
.equals, and clojure.core/hash routes a non-IHashEq,
non-Number, non-String object to .hashCode. So implementing those
two over interval_cmp_value makes the EXISTING machinery correct,
including the parts no comparator can reach: GROUP BY is
datahike's own group-by over the non-aggregate :find elements,
and DISTINCT, clojure.set/intersection, contains? on a set and
datahike's hash join are all clojure.core calls. None of them
takes a comparator; all of them are served by .hashCode.
Comparable covers ORDER BY, the comparison operators, BETWEEN and
min/max, through fns/order-cmp's existing :else (compare a b) --
so that function needs no interval branch at all. The same route
bits.clj's PgBit takes.
THE SPAN NEEDS MORE THAN 64 BITS. month * 30 + day can reach
6.4e10 days, and multiplying that by 86,400,000,000 µs overflows
int64 -- which is why the C uses INT128. compareTo therefore works
in BigInteger. hashCode does NOT: interval_hash deliberately
narrows the INT128 to its low 64 bits (int128_to_int64) before
hashing, so two spans differing by exactly 2^64 µs hash together in
PostgreSQL, and matching that is what keeps our hashing consistent
with a real server's.
toString is NOT the SQL rendering. Output goes through
types/->pg-text, which is the single funnel for ::text, array
elements, record fields, COPY, pg_dump and to_jsonb.
The interval CARRIER, and `interval_out`. A `deftype`, deliberately, and not a `defrecord`. The consolidation plan warns that "a record's structural `=`/hash would split GROUP BY and joins", and that is true -- but it is a reason to choose the carrier's equality, not a reason to avoid a carrier. PostgreSQL compares `'1 mon'` and `'30 days'` as EQUAL (`interval_cmp_value`: a month is 30 days, a day is 24 hours) while PRINTING them differently, so: structural equality splits two values PostgreSQL groups; canonical TEXT cannot be the key either, for the same reason. `clojure.core/=` routes a non-collection, non-Number object to `.equals`, and `clojure.core/hash` routes a non-`IHashEq`, non-Number, non-String object to `.hashCode`. So implementing those two over `interval_cmp_value` makes the EXISTING machinery correct, including the parts no comparator can reach: `GROUP BY` is datahike's own `group-by` over the non-aggregate `:find` elements, and `DISTINCT`, `clojure.set/intersection`, `contains?` on a set and datahike's hash join are all `clojure.core` calls. None of them takes a comparator; all of them are served by `.hashCode`. `Comparable` covers ORDER BY, the comparison operators, BETWEEN and min/max, through `fns/order-cmp`'s existing `:else (compare a b)` -- so that function needs no interval branch at all. The same route `bits.clj`'s `PgBit` takes. THE SPAN NEEDS MORE THAN 64 BITS. `month * 30 + day` can reach 6.4e10 days, and multiplying that by 86,400,000,000 µs overflows int64 -- which is why the C uses INT128. `compareTo` therefore works in `BigInteger`. `hashCode` does NOT: `interval_hash` deliberately narrows the INT128 to its low 64 bits (`int128_to_int64`) before hashing, so two spans differing by exactly 2^64 µs hash together in PostgreSQL, and matching that is what keeps our hashing consistent with a real server's. `toString` is NOT the SQL rendering. Output goes through `types/->pg-text`, which is the single funnel for `::text`, array elements, record fields, COPY, pg_dump and `to_jsonb`.
(->pg-text-postgres iv)interval_out under IntervalStyle = postgres, which is the only
style the server reports (show IntervalStyle answers postgres
unconditionally today). The other three are in the backlog with the
GUC.
`interval_out` under IntervalStyle = postgres, which is the only style the server reports (`show IntervalStyle` answers `postgres` unconditionally today). The other three are in the backlog with the GUC.
(cmp-span months days micros)interval_cmp_value (timestamp.c): the INT128 span, as a
BigInteger. A month is 30 days and a day is 24 hours -- the
equivalence that makes '1 mon' = '30 days'.
`interval_cmp_value` (timestamp.c): the INT128 span, as a `BigInteger`. A month is 30 days and a day is 24 hours -- the equivalence that makes `'1 mon' = '30 days'`.
(fields iv)interval2itm (timestamp.c): the carrier as the seven output
fields. The divisions are TRUNCATING, so a negative interval keeps
each field's own sign -- which is what makes -1 days +02:03:04
printable at all.
`interval2itm` (timestamp.c): the carrier as the seven output fields. The divisions are TRUNCATING, so a negative interval keeps each field's own sign -- which is what makes `-1 days +02:03:04` printable at all.
(interval months days micros)A carrier from the decoder's three fields.
A carrier from the decoder's three fields.
(interval-in s)(interval-in s range)interval_in. Text to a PgInterval, or the infinity sentinels.
THE FALLBACK ORDER IS EXACT and is the part worth getting right:
ParseDateTime then DecodeInterval, and DecodeISO8601Interval
is tried ONLY when that returned DTERR_BAD_FORMAT specifically --
not on an overflow. So an input the ordinary decoder rejects as out
of range must NOT be retried as ISO, or '2147483648 months' would
fall through and be refused with the wrong error.
And FIELD_OVERFLOW is REMAPPED to INTERVAL_OVERFLOW, SQLSTATE
22015 -- interval field value out of range -- not the 22008 the
datetime types raise (timestamp.c:941-943).
`interval_in`. Text to a `PgInterval`, or the infinity sentinels. THE FALLBACK ORDER IS EXACT and is the part worth getting right: `ParseDateTime` then `DecodeInterval`, and `DecodeISO8601Interval` is tried ONLY when that returned DTERR_BAD_FORMAT specifically -- not on an overflow. So an input the ordinary decoder rejects as out of range must NOT be retried as ISO, or `'2147483648 months'` would fall through and be refused with the wrong error. And FIELD_OVERFLOW is REMAPPED to INTERVAL_OVERFLOW, SQLSTATE 22015 -- `interval field value out of range` -- not the 22008 the datetime types raise (timestamp.c:941-943).
(justify-days iv)interval_justify_days: whole months out of the day field, with the
same sign correction.
`interval_justify_days`: whole months out of the day field, with the same sign correction.
(justify-hours iv)interval_justify_hours: whole days out of the time field, then a
sign correction so day and time agree in sign.
`interval_justify_hours`: whole days out of the time field, then a sign correction so day and time agree in sign.
(justify-interval iv)interval_justify_interval. Not simply justify-days after
justify-hours: the FIRST month extraction runs only when day and
time already agree in sign, and the final sign correction looks at
the time field too -- month > 0 && (day < 0 || (day == 0 && time < 0)). Composing the other two gives a different answer for a mixed
interval.
`interval_justify_interval`. Not simply justify-days after justify-hours: the FIRST month extraction runs only when day and time already agree in sign, and the final sign correction looks at the time field too -- `month > 0 && (day < 0 || (day == 0 && time < 0))`. Composing the other two gives a different answer for a mixed interval.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |