Previously dates were an int64 number of days from 1970-01-01. Functions
and operators needing to act on a date could manipulate the value of
the date without any overflow checks, causing multiple bugs:
- The postgres binary wire protocol uses an int32 number of days since
2000-01-01, thus it was possible to have dates in cockroach outside
of the expressible range over the wire. We were blindly converting the
int64 to an int32, possibly discarding data.
- Adding dates to ints was done by normal addition. However this could
overflow. Our normal int + int path uses overflow-checked addition,
but dates were not subject to such checks.
- Converting dates to a text representation (for sending over pgwire or
our distsql serialization) converted the number of days to a time.Time
using the standard library time.Date method, which (more-or-less) stores
time as an int64 number of nanos. A very high year could overflow the
nanosecond part causing it to silently by incorrect.
- The infinity dates were not treated as infinity, but converted to an
actual date.
This change adds a new pgdate.Date type. It internally represented as
an int32 number of days since 2000-01-01, because that's exactly what
is needed by the postgres wire protocol. It doesn't export any internal
properties, and instead forces users of it to use its API, which is
guaranteed to be safe. The API does all overflow and bounds checking,
and correctly handles the infinity dates. This fixes the above bugs
since it's now not possible to mistakenly misuse Dates.
When reading from any existing on-disk data, dates outside of the postgres
bounds are converted to +/- infinity. This presents a problem since
datums need to roundtrip to the same on-disk encoding so that old index
entries can be deleted. In order to round trip correctly, the original
on-disk integer is saved and used during serialization if it exists.
Fixes #36557
Release note (sql change): Dates are now fully Postgres-compatible,
including support for sentinel values (+/- infinity) and the Postgres
date range (4714-11-24 BC to 5874897-12-31). Existing dates outside of
this range will be converted to the +/- infinity dates.