Commit 892d8477 authored by Ian Chechin's avatar Ian Chechin
Browse files

sqlite: document _texttotime empty-decltype upgrade, widen #248 comment, add CHANGELOG

Addresses !133 review: reword the rows.go comment to cover every empty-decltype
TEXT case (aggregates/expressions, subqueries, typeless real columns) rather
than only MAX/MIN/COALESCE, document the *string RFC3339Nano reformat
consequence on the _texttotime DSN doc, and add the v1.54.0 CHANGELOG entry.
parent f2c87584
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
# Changelog

 - 2026-06-25 v1.54.0:
     - Under the opt-in `_texttotime` DSN parameter, best-effort parse date-shaped TEXT values from columns SQLite reports with an empty declared type — aggregates and expressions over a date column (`MAX(d)`, `COALESCE(d, ...)`, `upper(d)`, `d || ''`), subqueries, and typeless real columns (`CREATE TABLE t(x)`) — into `time.Time`, instead of delivering them as a raw string that `Scan` cannot store into a `*time.Time`. The existing declared `DATE`/`DATETIME`/`TIME`/`TIMESTAMP` path is unchanged; this only adds the empty-decltype case. The conversion is strictly best-effort: a value that does not parse as a time falls through to the original string, so no `Scan` that worked before can newly fail. `ColumnTypeScanType` continues to report `string` for empty-decltype columns, since the declared type cannot prove the column is temporal. Without `_texttotime` the behavior is byte-for-byte unchanged. Resolves [GitLab issue #248](https://gitlab.com/cznic/sqlite/-/issues/248).
     - See [GitLab merge request #133](https://gitlab.com/cznic/sqlite/-/merge_requests/133), thanks Ian Chechin!

 - 2026-06-21 v1.53.0:
     - Add **experimental** `netbsd/amd64` support, resolving the long-standing build break in [GitLab issue #246](https://gitlab.com/cznic/sqlite/-/issues/246). This target is intentionally **not yet listed among the supported platforms** in the package documentation: the port had been broken for years and is only now revived, and there is as yet no real-world experience running it under production workloads. Green CI is not the same as battle-tested — so while the full test suite (including the `pcache` and `vec` packages and the `-race` concurrency test) passes on NetBSD 10.1 / Go 1.26.3, and the entire upstream toolchain (`libc`, `cc`, `ccgo`, `libz`, `libtcl8.6`, `libsqlite3`, `libsqlite_vec`) is green on the NetBSD CI builder, the target is offered for evaluation only. If you run NetBSD, please exercise it with your own workloads and report back via #246; the intent is to promote it to a fully supported platform after a period of broader real-world testing (on the order of a month) elapses without surprises.
     - Implementation notes: the previously shipped `lib/sqlite_netbsd_amd64.go` was a stale old-generator transpile that no longer compiled (the `mu.enter`/`mu.leave` break in #246); it is replaced by a fresh new-generator transpile consistent with every other platform, and `modernc.org/sqlite/vec` (sqlite-vec) is vendored and auto-registers on netbsd. Correct operation requires the matching pinned `modernc.org/libc`, which carries two NetBSD-specific fixes found during this work: the `mmap(2)` `PAD`-argument ABI (without it, concurrent WAL access faults with SIGBUS in the WAL-index shared memory) and a working `abort(3)` (the prior stub left SQLite's crash-recovery `writecrash` test unable to terminate by signal). As usual, downstream modules must pin the exact `modernc.org/libc` version this module's `go.mod` pins.
+9 −1
Original line number Diff line number Diff line
@@ -74,7 +74,15 @@ func newDriver() *Driver { return d }
// to time if the field contain integer (int64).
//
// _texttotime: Enable ColumnTypeScanType to report time.Time instead of string
// for TEXT columns declared as DATE, DATETIME, TIME, or TIMESTAMP.
// for TEXT columns declared as DATE, DATETIME, TIME, or TIMESTAMP. It also
// best-effort upgrades date-shaped TEXT values from columns SQLite reports with
// an empty declared type (aggregates and expressions such as MAX(d) or
// upper(d), subqueries, and typeless real columns) to time.Time, since the
// declared-type test cannot catch those (#248). When that upgrade fires, a Scan
// into interface{} yields a time.Time where it previously yielded a string, and
// a Scan into *string receives the value reformatted to RFC3339Nano rather than
// the raw stored text. A value that does not parse as a time is delivered
// unchanged as the original string.
//
// _timezone: A timezone to use for all time reads and writes, such as "UTC".
// The value is parsed by time.LoadLocation.
+10 −8
Original line number Diff line number Diff line
@@ -200,14 +200,16 @@ func (r *rows) Next(dest []driver.Value) (err error) {
					}
					dest[i] = val
				default:
					// An aggregate or expression over a date column
					// (MAX/MIN/COALESCE) drops the declared type, so SQLite
					// reports an empty decltype here and the column would be
					// delivered as a raw string that Scan cannot store into
					// *time.Time (#248). Under _texttotime, best-effort parse
					// the value: on success deliver time.Time, otherwise fall
					// back to the original string, so no Scan that worked
					// before can newly fail.
					// A TEXT column with no declared type. SQLite reports an
					// empty decltype not only for aggregates and expressions
					// over a date column (MAX/MIN/COALESCE, upper(x), x||''),
					// but also for subqueries and for typeless real columns
					// (CREATE TABLE t(x)). Any of these would be delivered as
					// a raw string that Scan cannot store into *time.Time
					// (#248). Under _texttotime, best-effort parse the value:
					// on success deliver time.Time, otherwise fall back to the
					// original string, so no Scan that worked before can newly
					// fail.
					if r.c.textToTime && r.decltypes[i] == "" {
						if val, ok, idx := r.c.parseTime(v, int(r.parseFmtIdx[i])); ok {
							if r.parseFmtIdx[i] < 0 && idx >= 0 {