sqlite: add _error_rc opt-in DSN parameter (#230)
Motivation
Issue #230 (closed) reported that opening a database file at a path the process cannot create surfaces a misleading error string: `db.Exec("PRAGMA journal_mode=WAL")` against a read-only path returned `"unable to open database file: out of memory"` even though `Code()` was `SQLITE_CANTOPEN`. The "out of memory" suffix came from `sqlite3_errmsg(db)` on the temporary handle that `sqlite3_open_v2` had leaked behind on failure, where an earlier initialisation step had left an unrelated errmsg.
The 2025-09 review settled the shape: `Code()` already returns the right value, so only the human-readable message should change; the legacy `errstr: errmsg` form must be preserved by default to keep Hyrum's-law callers byte-for-byte compatible; the fix is an opt-in DSN flag, parsed before `sqlite3_open_v2` so open-time errors are covered too.
What this MR delivers
-
`driver.go` (+16 lines): `_error_rc` documentation paragraph next to `_pragma`, `_txlock`, and `_dqs`. Calls out the Code()-unchanged invariant explicitly.
-
`conn.go` (+47 / -11 lines):
- New `errorRcMode bool` field on `conn` so every call site that builds an error string can consult it.
- `newConn` parses `_error_rc` via the new `getErrorRcMode` helper before openV2, so the conn carries the flag through the `(c *conn).openV2` failure path that uses the temporary db handle.
- `errstrForDB` gains an `errorRcMode bool` parameter and the conditional logic: `sqlite3_extended_errcode(db)` is compared against `rc` full first, then against `(rc & 0xff)` as a fallback per cznic's review note. On match the legacy `errstr: errmsg` form is preserved; on mismatch the canonical `sqlite3_errstr(rc)` is used alone. `db == 0` and the `msg == str` collapse keep their existing behaviour.
- The three `errstrForDB` call sites are updated: the open-V2 failure path passes `c.errorRcMode`, `(c *conn).errstr` does the same, and the backup-init error path picks the destination connection's mode (`restore ? c.errorRcMode : remoteConn.errorRcMode`).
-
`sqlite.go` (+30 lines): `getErrorRcMode` parser placed next to `getVFSName` (the other pre-openV2 query consumer). Empty value is treated as absent and returns false. Unparseable values surface a descriptive error mentioning `_error_rc`.
-
`error_rc_test.go` (+134 lines, new):
- `TestErrorRcOpenTimeUnopenable` exercises the open-time SQLITE_CANTOPEN path in all three modes (default, `_error_rc=0`, `_error_rc=1`). Assertions are structural so they hold on every platform regardless of the host-specific failure path: `Code()` returns SQLITE_CANTOPEN unchanged between modes; the canonical errstr is present; the `_error_rc=1` mode does not surface a stale "out of memory" suffix.
- `TestErrorRcSyntaxErrorPreservesErrmsg` covers the non-regression case in cznic's review refinement #2: when `extended_errcode(db)` is consistent with rc (the common case for syntax and constraint errors), the helpful `no such table` detail must remain visible in every mode.
- `TestErrorRcInvalidValue` verifies an unparseable `_error_rc` value surfaces an error from `newConn`.
-
`CHANGELOG.md` (+2 lines): TBC vNEXT entry. MR reference will be updated once the MR number is assigned.
Verification
- `go test -short -count=1 -run "TestErrorRc" ./` ok (7/7 subtests, 0.6s on darwin/arm64).
- `go test -race -short -count=1 -run "TestErrorRc" ./` ok (1.6s).
- Cross-build clean for linux/amd64, linux/386, linux/arm64, darwin/amd64, darwin/arm64, windows/amd64, freebsd/amd64, openbsd/amd64.
- `gofmt -l` clean.
- `go vet` introduces no new warnings.
What this MR explicitly does NOT do
- `Code()` semantics are unchanged in both modes; only the message changes. Existing callers that switch on `(*Error).Code()` are unaffected.
- The default behaviour is unchanged. Existing DSNs continue to produce the legacy `errstr: errmsg` form byte-for-byte.
- No DDL/DML or per-op split is introduced; the toggle is a single per-connection mode.
Resolves #230 (closed).