This release adds direct PostgreSQL type scanning through database/sql on Go 1.27, improves compatibility with
libpq connection strings and PostgreSQL date/time values, and includes further decoder hardening. See Changes for
connection-string and date/time behavior changes that may affect existing applications.
Features
stdlib: support Go 1.27's driver.RowsColumnScanner, allowing PostgreSQL types such as arrays and ranges to be
scanned directly into Go values without pgtype.Map.SQLScanner. Existing database/sql scalar conversions and sql.Scanner behavior are preserved. The minimum supported Go version remains 1.25.
Add Rows.TypeMap to expose the type map used to decode rows, including rows created by RowsFromResultReader
that have no underlying Conn. Custom implementations of Rows, including mocks, must add this method.
pgconn: add Config.MaxProtocolMessageBodyLen to configure the maximum incoming protocol message body size
(carter-ya)
pgconn: add ErrReadOnlyConnection, ErrReadWriteConnection, ErrPrimaryConnection, and ErrStandbyConnection
sentinel errors for target_session_attrs validation, allowing callers to use errors.Is (Adrian-Stefan Mares)
pgxpool: accept pool_ping_timeout in connection strings to configure Config.PingTimeout. The default is zero;
zero and negative durations mean no timeout (1991santhu)
Changes
Name-based row-to-struct mapping now matches explicit db tags case-insensitively, with exact matches taking
precedence so tags can still distinguish quoted column names that differ only by case (AlisinaDevelo)
pgconn: resolve the OS user account only when no user is supplied by the connection string, environment, or service
file, avoiding unnecessary account lookups and crashes in some restricted container environments. Home-directory
defaults for password, service, and TLS files remain available independently of the account lookup. On Unix these
now use $HOME rather than the OS account's home directory (Mohamed MAACHE)
pgtype: date, timestamp and timestamptz text values are now parsed and written by a hand-written parser and
encoder for PostgreSQL's ISO date/time format instead of time.Parse and time.Format. Go's layout language cannot
express a variable-width year or the BC era, which is the root of the bugs below. The text scan path is roughly 2.5x
faster for timestamp and timestamptz. Bug fixes:
timestamp and timestamptz no longer silently move February 29 of a BC leap year to March 1 when encoding. time.Date(-4712, 2, 29, ...) was written as 4713-03-01 BC and is now written as 4713-02-29 BC. This
affected ordinary four-digit BC years, not only extended-range ones. date was never affected.
timestamp and timestamptz can now scan BC leap days. 4713-02-29 BC previously failed with day out of range. date could already scan them.
Years past 9999 can now be scanned. 10000-01-02 03:04:05 previously failed to parse, so timestamp and timestamptz values at the high end of PostgreSQL's range were unreadable over the simple protocol and in any
other text-format result.
time.Time arguments in the simple protocol now encode BC dates correctly, using the same timestamp encoder.
Fractional seconds beyond microsecond precision are rounded the way the server rounds them (round half to even,
carrying into the rest of the value) instead of being kept at full precision. PostgreSQL never sends more than six
fractional digits, so this only affects values from other sources.
Behavior changes:
date now rejects impossible dates instead of normalizing them. 2024-02-30 returned 2024-03-01 and 2024-13-01 returned 2025-01-01; both are now errors. timestamp and timestamptz already rejected them.
All three types now reject values outside PostgreSQL's range for that type, in the binary format as well as the
text format. PostgreSQL never sends out-of-range dates, so this only affects corrupt or hand-built input; the range
is checked in both formats so that whether a value is accepted does not depend on QueryExecMode. timestamptz also rejects time zone displacements outside PostgreSQL's signed 32-bit seconds range, while accepting
the wider offsets emitted for POSIX time zones, such as +16.
timestamptz values scanned from the text format are now returned in time.Local, or in ScanLocation when it is
set, matching what the binary format has always returned. Previously the text path kept whatever location time.Parse derived from the offset the server sent, so the same value scanned in the two formats could report a
different Location() and Zone(). The instant is unchanged, but everything that renders the location changes
with it: Timestamptz.MarshalJSON now writes the client's offset rather than the server's, so a value the server
sent as +05:30 marshals as 2024-01-01T13:34:05-08:00 on a UTC-8 client instead of 2024-01-02T03:04:05+05:30,
and DecodeDatabaseSQLValue hands database/sql a time.Time in that same location. Set the codec's ScanLocation to time.UTC to pin the location regardless of the client's zone.
Error messages from these paths have changed.
pgconn: connection URIs (postgres://...) are now parsed by a new parser designed to exactly match libpq's URI
parser behavior instead of net/url,
making pgx accept and reject exactly the same URIs as libpq (verified by differential fuzzing against libpq itself).
Most connection strings are unaffected. Edge-case behavior changes, all matching libpq:
+ in query values is literal, no longer decoded as a space.
Malformed percent-encoding is a parse error instead of the parameter being silently dropped. %00 is rejected.
Leading/trailing spaces in URI components are trimmed; interior spaces are a parse error (encode them as %20).
# is ordinary data, not a fragment delimiter.
The userinfo terminator is the first @ before any / (previously the last @).
When a query parameter is repeated, the last occurrence wins (previously the first).
ssl=true is accepted as an alias for sslmode=require in URIs (JDBC compatibility). A repeated ssl key
follows the same last-occurrence-wins rule as other repeated parameters, even across the rewrite to sslmode. If
the final ssl value is not true, an independent explicit sslmode remains in effect.
Multiple hosts with mixed port specs are positionally aligned: postgres://h1,h2:5433/db now means h1:5432 and
h2:5433 (previously both hosts got port 5433). A port list that is neither a single port nor exactly one port per
host is an error (could not match N port numbers to M hosts), also for keyword/value connection strings.
An IPv6 address in a URI must be enclosed in brackets. A bare postgres://::1/db was previously accepted as host ::1; it is now read as an empty host followed by port :1 and fails with an invalid port error. Write it as postgres://[::1]/db.
Empty host list elements (e.g. h1,,h2) get the default host instead of being dropped. Likewise, an empty host in
a keyword/value string (host=) now means the default host -- typically the Unix socket directory -- where it
previously meant a TCP connection to an empty hostname.
An empty port (?port= in a URI or port= in a keyword/value string) now means the default port 5432 for the
affected hosts; previously it was an invalid port error. Like any connection-string port, a present-but-empty port
takes precedence over PGPORT.
ASCII control characters (tab, newline, ...) in a URI are ordinary data bytes, as they are to libpq; net/url
rejected any URI containing one. The exception is a literal NUL byte, which is still rejected, as net/url did.
(libpq never sees one -- C strings end at the first NUL -- but in Go a raw NUL could otherwise pass through into
the NUL-delimited startup message and inject extra parameters.)
Unlike libpq, unrecognized URI query parameters are still accepted (they become runtime parameters or pgx-specific
options). Parse error messages avoid quoting the unredacted connection string and redact recognizable password
fields on a best-effort basis. Invalid connection strings can be structurally ambiguous, so password redaction
cannot be guaranteed for every malformed input.
pgconn: keyword/value connection strings (host=... user=...) now match libpq's parser exactly, the same treatment
the URI parser received above and verified the same way, by differential fuzzing against libpq itself. Most
connection strings are unaffected. Behavior changes, all matching libpq:
A backslash escapes whatever character follows it and is dropped, where previously only \\ and \' were
unescaped and every other backslash was kept. A value containing a backslash must now escape it, as libpq
requires: sslcert=C:\path\to\cert reads as C:pathtocert and has to be written sslcert=C:\\path\\to\\cert.
This mainly affects Windows certificate and key paths, which previously came through intact without doubling.
A trailing backslash in an unquoted value escapes the end of the string, so it is dropped and the value ends
there; it was previously rejected with invalid backslash. Inside a quoted value the escaped terminator leaves
the string unterminated, which is still an error.
Whitespace inside a keyword is an error (missing "=" after "us" in connection info string) instead of becoming
part of the key. Whitespace around the = is unaffected. This most often shows up with an unquoted value
containing a space: application_name=my app host=x previously set neither parameter and sent app host to the
server as a runtime parameter, and now fails to parse.
As with URIs, unrecognized keywords are still accepted where libpq rejects them, and an empty user= is still
dropped so that PGUSER and the OS user still apply.
Fixes
Keep the connection open after a recoverable PostgreSQL error from Begin or BeginTx
(Victor Alejandro Sanz Ararat)
Call TraceQueryEnd when Exec fails while deallocating invalidated cached statements (Chris Bandy)
Deallocate a failed prepare using the statement name actually sent to the server, and skip cleanup if Parse never
completed, avoiding leaked prepared statements and unnecessary cleanup errors (Eliran Ben-Zikri)
Fix LoadTypes overwriting scalar codecs such as box and point with an incorrect ArrayCodec (Arsen Ozhetov)
pgconn: retrieve field descriptions when cached descriptions are empty, such as for cursor FETCH statements,
including batch and pipeline execution (water)
pgconn: keep batch statement descriptions and result formats aligned when commands return no rows or when Batch.ExecStatement is mixed with other batch commands; preserve field descriptions for empty results
pgconn: handle empty and comment-only queries in pipeline mode, discard stale statement data after bind errors,
and return a nil result from Pipeline.GetResults on error
pgconn: skip reading the password file when a password is already set (Jared Fowkes)
pgxpool: treat non-positive MaxConnLifetime values as unlimited instead of immediately expiring connections
(Aurelien Pillevesse)
pgtype: support non-comma text array delimiters through ArrayCodec.Delimiter, including the semicolon delimiter
used by box[]. LoadType and LoadTypes now load the delimiter from PostgreSQL (Sueun Cho)
pgtype: quote text array elements containing internal whitespace (Louisa Huang)
pgtype: quote and escape text range bounds containing delimiters, quotes, or backslashes, and distinguish empty
string bounds from unbounded ranges (Sueun Cho)
pgtype: preserve decimal precision in Numeric.ScanScientific and accept scientific notation in Numeric.UnmarshalJSON; reject out-of-range scientific exponents and preserve the original input in parse errors
(Sueun Cho)
pgtype: encode and decode numeric infinity in JSON as "Infinity" and "-Infinity" instead of encoding it as zero
(Vladimir Saraikin)
pgtype: treat a valid Numeric with a nil Int as zero in Int64Value, and return errors when converting NaN or
infinity to an integer instead of panicking (Vladimir Saraikin)
pgtype: fix an infinite loop when decoding binary numeric zero with a nonzero digit count (Vladimir Saraikin)
pgtype: fix binary numeric digit-count overflow and trailing-byte handling. Binary encoding now rejects values
whose digit count, weight, or scale cannot fit the wire format, while accepting the full unsigned digit-count range.
pgtype: fix scanning through multiple pointer levels, including SQL NULL and XML values, and return an error
instead of panicking when a pointer-to-pointer scan destination is nil (Rangel Reale)
pgtype: use bounds-checked binary reads throughout the codecs and reject malformed lengths, counts, and trailing
data. This includes fixes for panics on malformed records and truncated multiranges (Vladimir Saraikin), and
validation of bit / varbit bit lengths against the actual data (g3m0sis).
pgtype: return errors instead of panicking on malformed interval text (greymoth-jp), unterminated composite text
fields, and text arrays whose dimensions and element counts disagree
pgtype: cap the initial allocation estimate when parsing hstore text to avoid excessive allocation from unvalidated
separator counts; valid hstores may still contain any number of pairs (AshSgDe29071999)
pgtype: correct reversed bounds in integer scan error messages
pgconn: a backslash as the last byte of a quoted value in a keyword/value connection string no longer panics with slice bounds out of range. host='a\ -- and the shorter ='\, reachable through pgx.ParseConfig and pgxpool.ParseConfig -- now return unterminated quoted string in connection info string, libpq's own message for
the same input. The unquoted branch has been guarded since be69c1c; the quoted branch carried the same unguarded
increment since the parser was ported from pgx v3. Found by fuzzing (Maxim Korotkov)
pgconn: error messages that embed the connection string now also redact password and sslpassword values supplied
as URI query parameters; previously only the userinfo password was redacted. Redaction matches keys the way the
parser does -- percent-encoded spellings such as pass%77ord= are recognized -- and masks the entire raw value, so
a password containing a space cannot leak its tail into the error message. Credentials stranded outside the
userinfo by a malformed URI are masked whole, and invalid-port errors no longer embed the offending text (which in
a malformed URI can be a mislaid password). Redaction of invalid connection strings is necessarily best effort:
their structure may be ambiguous, so some malformed inputs can still expose password text in an error.
pgconn: ParseConfigOptions.ConnStringAllowedKeys no longer exempts an explicitly supplied empty port (?port= in
a URI or port= in a keyword/value string) from the allow-list. Only the implied all-empty port list of a
multi-host URI without ports (postgres://h1,h2/db) is exempt. An explicit empty port shadows PGPORT even though
it is empty, so it must be allowed like any other user-supplied key. The URI-only ssl=true alias is accepted when
either ssl or sslmode is allowed, and every ssl/sslmode spelling written in the URI is validated --
including occurrences superseded by later repeated parameters.
pgconn: drain socket before close in asyncClose so context cancellation produces a TCP FIN instead of RST, avoiding "connection reset by peer" on the server / proxy (Sean Chittenden at CrowdStrike, Inc.)
pgproto3: StartupMessage.Encode rejects a NUL byte in any parameter name or value instead of writing it. The
startup message body is a run of NUL-delimited strings whose length is data-driven, so a NUL in a value ends that
parameter and everything after it is read by the server as further parameters -- an application_name of x\x00user\x00admin changed the role the connection logged in as. libpq cannot reach this state because its
parameters are NUL-terminated C strings. Connect now fails with nothing written to the wire, which covers
settings that bypass connection string parsing: service files and direct assignment to Config.RuntimeParams, Config.User, or Config.Database.
pgconn: keyword/value connection strings containing a NUL byte are rejected by ParseConfig, as URIs already were.
Configuration
📅Schedule: (UTC)
Branch creation
At any time (no schedule defined)
Automerge
At any time (no schedule defined)
🚦Automerge: Enabled.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕Ignore: Close this PR and you won't be reminded about this update again.
If you want to rebase/retry this PR, check this box
This PR contains the following updates:
| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) | `v5.10.0` → `v5.11.0` |  |  |
---
### Release Notes
<details>
<summary>jackc/pgx (github.com/jackc/pgx/v5)</summary>
### [`v5.11.0`](https://github.com/jackc/pgx/releases/tag/v5.11.0)
[Compare Source](https://github.com/jackc/pgx/compare/v5.10.0...v5.11.0)
This release adds direct PostgreSQL type scanning through `database/sql` on Go 1.27, improves compatibility with
libpq connection strings and PostgreSQL date/time values, and includes further decoder hardening. See Changes for
connection-string and date/time behavior changes that may affect existing applications.
#### Features
- stdlib: support Go 1.27's `driver.RowsColumnScanner`, allowing PostgreSQL types such as arrays and ranges to be
scanned directly into Go values without `pgtype.Map.SQLScanner`. Existing `database/sql` scalar conversions and
`sql.Scanner` behavior are preserved. The minimum supported Go version remains 1.25.
- Add `Rows.TypeMap` to expose the type map used to decode rows, including rows created by `RowsFromResultReader`
that have no underlying `Conn`. Custom implementations of `Rows`, including mocks, must add this method.
- pgconn: add `Config.MaxProtocolMessageBodyLen` to configure the maximum incoming protocol message body size
(carter-ya)
- pgconn: add `ErrReadOnlyConnection`, `ErrReadWriteConnection`, `ErrPrimaryConnection`, and `ErrStandbyConnection`
sentinel errors for `target_session_attrs` validation, allowing callers to use `errors.Is` (Adrian-Stefan Mares)
- pgxpool: accept `pool_ping_timeout` in connection strings to configure `Config.PingTimeout`. The default is zero;
zero and negative durations mean no timeout (1991santhu)
#### Changes
- Name-based row-to-struct mapping now matches explicit `db` tags case-insensitively, with exact matches taking
precedence so tags can still distinguish quoted column names that differ only by case (AlisinaDevelo)
- pgconn: resolve the OS user account only when no user is supplied by the connection string, environment, or service
file, avoiding unnecessary account lookups and crashes in some restricted container environments. Home-directory
defaults for password, service, and TLS files remain available independently of the account lookup. On Unix these
now use `$HOME` rather than the OS account's home directory (Mohamed MAACHE)
- pgtype: `date`, `timestamp` and `timestamptz` text values are now parsed and written by a hand-written parser and
encoder for PostgreSQL's ISO date/time format instead of `time.Parse` and `time.Format`. Go's layout language cannot
express a variable-width year or the BC era, which is the root of the bugs below. The text scan path is roughly 2.5x
faster for `timestamp` and `timestamptz`. Bug fixes:
- `timestamp` and `timestamptz` no longer silently move February 29 of a BC leap year to March 1 when encoding.
`time.Date(-4712, 2, 29, ...)` was written as `4713-03-01 BC` and is now written as `4713-02-29 BC`. This
affected ordinary four-digit BC years, not only extended-range ones. `date` was never affected.
- `timestamp` and `timestamptz` can now scan BC leap days. `4713-02-29 BC` previously failed with
`day out of range`. `date` could already scan them.
- Years past 9999 can now be scanned. `10000-01-02 03:04:05` previously failed to parse, so `timestamp` and
`timestamptz` values at the high end of PostgreSQL's range were unreadable over the simple protocol and in any
other text-format result.
- `time.Time` arguments in the simple protocol now encode BC dates correctly, using the same timestamp encoder.
- Fractional seconds beyond microsecond precision are rounded the way the server rounds them (round half to even,
carrying into the rest of the value) instead of being kept at full precision. PostgreSQL never sends more than six
fractional digits, so this only affects values from other sources.
Behavior changes:
- `date` now rejects impossible dates instead of normalizing them. `2024-02-30` returned `2024-03-01` and
`2024-13-01` returned `2025-01-01`; both are now errors. `timestamp` and `timestamptz` already rejected them.
- All three types now reject values outside PostgreSQL's range for that type, in the binary format as well as the
text format. PostgreSQL never sends out-of-range dates, so this only affects corrupt or hand-built input; the range
is checked in both formats so that whether a value is accepted does not depend on `QueryExecMode`.
`timestamptz` also rejects time zone displacements outside PostgreSQL's signed 32-bit seconds range, while accepting
the wider offsets emitted for POSIX time zones, such as `+16`.
- `timestamptz` values scanned from the text format are now returned in `time.Local`, or in `ScanLocation` when it is
set, matching what the binary format has always returned. Previously the text path kept whatever location
`time.Parse` derived from the offset the server sent, so the same value scanned in the two formats could report a
different `Location()` and `Zone()`. The instant is unchanged, but everything that renders the location changes
with it: `Timestamptz.MarshalJSON` now writes the client's offset rather than the server's, so a value the server
sent as `+05:30` marshals as `2024-01-01T13:34:05-08:00` on a UTC-8 client instead of `2024-01-02T03:04:05+05:30`,
and `DecodeDatabaseSQLValue` hands `database/sql` a `time.Time` in that same location. Set the codec's
`ScanLocation` to `time.UTC` to pin the location regardless of the client's zone.
- Error messages from these paths have changed.
- pgconn: connection URIs (`postgres://...`) are now parsed by a new parser designed to exactly match libpq's URI
parser behavior instead of `net/url`,
making pgx accept and reject exactly the same URIs as libpq (verified by differential fuzzing against libpq itself).
Most connection strings are unaffected. Edge-case behavior changes, all matching libpq:
- `+` in query values is literal, no longer decoded as a space.
- Malformed percent-encoding is a parse error instead of the parameter being silently dropped. `%00` is rejected.
- Leading/trailing spaces in URI components are trimmed; interior spaces are a parse error (encode them as `%20`).
- `#` is ordinary data, not a fragment delimiter.
- The userinfo terminator is the first `@` before any `/` (previously the last `@`).
- When a query parameter is repeated, the last occurrence wins (previously the first).
- `ssl=true` is accepted as an alias for `sslmode=require` in URIs (JDBC compatibility). A repeated `ssl` key
follows the same last-occurrence-wins rule as other repeated parameters, even across the rewrite to `sslmode`. If
the final `ssl` value is not `true`, an independent explicit `sslmode` remains in effect.
- Multiple hosts with mixed port specs are positionally aligned: `postgres://h1,h2:5433/db` now means h1:5432 and
h2:5433 (previously both hosts got port 5433). A port list that is neither a single port nor exactly one port per
host is an error (`could not match N port numbers to M hosts`), also for keyword/value connection strings.
- An IPv6 address in a URI must be enclosed in brackets. A bare `postgres://::1/db` was previously accepted as host
`::1`; it is now read as an empty host followed by port `:1` and fails with an invalid port error. Write it as
`postgres://[::1]/db`.
- Empty host list elements (e.g. `h1,,h2`) get the default host instead of being dropped. Likewise, an empty host in
a keyword/value string (`host=`) now means the default host -- typically the Unix socket directory -- where it
previously meant a TCP connection to an empty hostname.
- An empty port (`?port=` in a URI or `port=` in a keyword/value string) now means the default port 5432 for the
affected hosts; previously it was an invalid port error. Like any connection-string port, a present-but-empty port
takes precedence over `PGPORT`.
- ASCII control characters (tab, newline, ...) in a URI are ordinary data bytes, as they are to libpq; `net/url`
rejected any URI containing one. The exception is a literal NUL byte, which is still rejected, as `net/url` did.
(libpq never sees one -- C strings end at the first NUL -- but in Go a raw NUL could otherwise pass through into
the NUL-delimited startup message and inject extra parameters.)
Unlike libpq, unrecognized URI query parameters are still accepted (they become runtime parameters or pgx-specific
options). Parse error messages avoid quoting the unredacted connection string and redact recognizable password
fields on a best-effort basis. Invalid connection strings can be structurally ambiguous, so password redaction
cannot be guaranteed for every malformed input.
- pgconn: keyword/value connection strings (`host=... user=...`) now match libpq's parser exactly, the same treatment
the URI parser received above and verified the same way, by differential fuzzing against libpq itself. Most
connection strings are unaffected. Behavior changes, all matching libpq:
- A backslash escapes whatever character follows it and is dropped, where previously only `\\` and `\'` were
unescaped and every other backslash was kept. A value containing a backslash must now escape it, as libpq
requires: `sslcert=C:\path\to\cert` reads as `C:pathtocert` and has to be written `sslcert=C:\\path\\to\\cert`.
This mainly affects Windows certificate and key paths, which previously came through intact without doubling.
- A trailing backslash in an unquoted value escapes the end of the string, so it is dropped and the value ends
there; it was previously rejected with `invalid backslash`. Inside a quoted value the escaped terminator leaves
the string unterminated, which is still an error.
- Whitespace inside a keyword is an error (`missing "=" after "us" in connection info string`) instead of becoming
part of the key. Whitespace around the `=` is unaffected. This most often shows up with an unquoted value
containing a space: `application_name=my app host=x` previously set neither parameter and sent `app host` to the
server as a runtime parameter, and now fails to parse.
As with URIs, unrecognized keywords are still accepted where libpq rejects them, and an empty `user=` is still
dropped so that `PGUSER` and the OS user still apply.
#### Fixes
- Keep the connection open after a recoverable PostgreSQL error from `Begin` or `BeginTx`
(Victor Alejandro Sanz Ararat)
- Call `TraceQueryEnd` when `Exec` fails while deallocating invalidated cached statements (Chris Bandy)
- Deallocate a failed prepare using the statement name actually sent to the server, and skip cleanup if Parse never
completed, avoiding leaked prepared statements and unnecessary cleanup errors (Eliran Ben-Zikri)
- Fix `LoadTypes` overwriting scalar codecs such as `box` and `point` with an incorrect `ArrayCodec` (Arsen Ozhetov)
- pgconn: retrieve field descriptions when cached descriptions are empty, such as for cursor `FETCH` statements,
including batch and pipeline execution (water)
- pgconn: keep batch statement descriptions and result formats aligned when commands return no rows or when
`Batch.ExecStatement` is mixed with other batch commands; preserve field descriptions for empty results
- pgconn: handle empty and comment-only queries in pipeline mode, discard stale statement data after bind errors,
and return a nil result from `Pipeline.GetResults` on error
- pgconn: skip reading the password file when a password is already set (Jared Fowkes)
- pgxpool: treat non-positive `MaxConnLifetime` values as unlimited instead of immediately expiring connections
(Aurelien Pillevesse)
- pgtype: support non-comma text array delimiters through `ArrayCodec.Delimiter`, including the semicolon delimiter
used by `box[]`. `LoadType` and `LoadTypes` now load the delimiter from PostgreSQL (Sueun Cho)
- pgtype: quote text array elements containing internal whitespace (Louisa Huang)
- pgtype: quote and escape text range bounds containing delimiters, quotes, or backslashes, and distinguish empty
string bounds from unbounded ranges (Sueun Cho)
- pgtype: preserve decimal precision in `Numeric.ScanScientific` and accept scientific notation in
`Numeric.UnmarshalJSON`; reject out-of-range scientific exponents and preserve the original input in parse errors
(Sueun Cho)
- pgtype: encode and decode numeric infinity in JSON as `"Infinity"` and `"-Infinity"` instead of encoding it as zero
(Vladimir Saraikin)
- pgtype: treat a valid `Numeric` with a nil `Int` as zero in `Int64Value`, and return errors when converting NaN or
infinity to an integer instead of panicking (Vladimir Saraikin)
- pgtype: fix an infinite loop when decoding binary numeric zero with a nonzero digit count (Vladimir Saraikin)
- pgtype: fix binary numeric digit-count overflow and trailing-byte handling. Binary encoding now rejects values
whose digit count, weight, or scale cannot fit the wire format, while accepting the full unsigned digit-count range.
- pgtype: fix scanning through multiple pointer levels, including SQL NULL and XML values, and return an error
instead of panicking when a pointer-to-pointer scan destination is nil (Rangel Reale)
- pgtype: use bounds-checked binary reads throughout the codecs and reject malformed lengths, counts, and trailing
data. This includes fixes for panics on malformed records and truncated multiranges (Vladimir Saraikin), and
validation of `bit` / `varbit` bit lengths against the actual data (g3m0sis).
- pgtype: return errors instead of panicking on malformed interval text (greymoth-jp), unterminated composite text
fields, and text arrays whose dimensions and element counts disagree
- pgtype: cap the initial allocation estimate when parsing hstore text to avoid excessive allocation from unvalidated
separator counts; valid hstores may still contain any number of pairs (AshSgDe29071999)
- pgtype: correct reversed bounds in integer scan error messages
- pgconn: a backslash as the last byte of a quoted value in a keyword/value connection string no longer panics with
`slice bounds out of range`. `host='a\` -- and the shorter `='\`, reachable through `pgx.ParseConfig` and
`pgxpool.ParseConfig` -- now return `unterminated quoted string in connection info string`, libpq's own message for
the same input. The unquoted branch has been guarded since [`be69c1c`](https://github.com/jackc/pgx/commit/be69c1c1); the quoted branch carried the same unguarded
increment since the parser was ported from pgx v3. Found by fuzzing (Maxim Korotkov)
- pgconn: error messages that embed the connection string now also redact `password` and `sslpassword` values supplied
as URI query parameters; previously only the userinfo password was redacted. Redaction matches keys the way the
parser does -- percent-encoded spellings such as `pass%77ord=` are recognized -- and masks the entire raw value, so
a password containing a space cannot leak its tail into the error message. Credentials stranded outside the
userinfo by a malformed URI are masked whole, and invalid-port errors no longer embed the offending text (which in
a malformed URI can be a mislaid password). Redaction of invalid connection strings is necessarily best effort:
their structure may be ambiguous, so some malformed inputs can still expose password text in an error.
- pgconn: `ParseConfigOptions.ConnStringAllowedKeys` no longer exempts an explicitly supplied empty port (`?port=` in
a URI or `port=` in a keyword/value string) from the allow-list. Only the implied all-empty port list of a
multi-host URI without ports (`postgres://h1,h2/db`) is exempt. An explicit empty port shadows `PGPORT` even though
it is empty, so it must be allowed like any other user-supplied key. The URI-only `ssl=true` alias is accepted when
either `ssl` or `sslmode` is allowed, and every `ssl`/`sslmode` spelling written in the URI is validated --
including occurrences superseded by later repeated parameters.
- pgconn: drain socket before close in `asyncClose` so context cancellation produces a TCP FIN instead of RST, avoiding "connection reset by peer" on the server / proxy (Sean Chittenden at CrowdStrike, Inc.)
- pgproto3: `StartupMessage.Encode` rejects a NUL byte in any parameter name or value instead of writing it. The
startup message body is a run of NUL-delimited strings whose length is data-driven, so a NUL in a value ends that
parameter and everything after it is read by the server as further parameters -- an `application_name` of
`x\x00user\x00admin` changed the role the connection logged in as. libpq cannot reach this state because its
parameters are NUL-terminated C strings. `Connect` now fails with nothing written to the wire, which covers
settings that bypass connection string parsing: service files and direct assignment to `Config.RuntimeParams`,
`Config.User`, or `Config.Database`.
- pgconn: keyword/value connection strings containing a NUL byte are rejected by `ParseConfig`, as URIs already were.
</details>
---
### Configuration
📅 **Schedule**: (UTC)
- Branch creation
- At any time (no schedule defined)
- Automerge
- At any time (no schedule defined)
🚦 **Automerge**: Enabled.
♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about this update again.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box
---
This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4zOS4yIiwidXBkYXRlZEluVmVyIjoiNDQuMzkuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
This PR contains the following updates:
v5.10.0→v5.11.0Release Notes
jackc/pgx (github.com/jackc/pgx/v5)
v5.11.0Compare Source
This release adds direct PostgreSQL type scanning through
database/sqlon Go 1.27, improves compatibility withlibpq connection strings and PostgreSQL date/time values, and includes further decoder hardening. See Changes for
connection-string and date/time behavior changes that may affect existing applications.
Features
driver.RowsColumnScanner, allowing PostgreSQL types such as arrays and ranges to bescanned directly into Go values without
pgtype.Map.SQLScanner. Existingdatabase/sqlscalar conversions andsql.Scannerbehavior are preserved. The minimum supported Go version remains 1.25.Rows.TypeMapto expose the type map used to decode rows, including rows created byRowsFromResultReaderthat have no underlying
Conn. Custom implementations ofRows, including mocks, must add this method.Config.MaxProtocolMessageBodyLento configure the maximum incoming protocol message body size(carter-ya)
ErrReadOnlyConnection,ErrReadWriteConnection,ErrPrimaryConnection, andErrStandbyConnectionsentinel errors for
target_session_attrsvalidation, allowing callers to useerrors.Is(Adrian-Stefan Mares)pool_ping_timeoutin connection strings to configureConfig.PingTimeout. The default is zero;zero and negative durations mean no timeout (1991santhu)
Changes
Name-based row-to-struct mapping now matches explicit
dbtags case-insensitively, with exact matches takingprecedence so tags can still distinguish quoted column names that differ only by case (AlisinaDevelo)
pgconn: resolve the OS user account only when no user is supplied by the connection string, environment, or service
file, avoiding unnecessary account lookups and crashes in some restricted container environments. Home-directory
defaults for password, service, and TLS files remain available independently of the account lookup. On Unix these
now use
$HOMErather than the OS account's home directory (Mohamed MAACHE)pgtype:
date,timestampandtimestamptztext values are now parsed and written by a hand-written parser andencoder for PostgreSQL's ISO date/time format instead of
time.Parseandtime.Format. Go's layout language cannotexpress a variable-width year or the BC era, which is the root of the bugs below. The text scan path is roughly 2.5x
faster for
timestampandtimestamptz. Bug fixes:timestampandtimestamptzno longer silently move February 29 of a BC leap year to March 1 when encoding.time.Date(-4712, 2, 29, ...)was written as4713-03-01 BCand is now written as4713-02-29 BC. Thisaffected ordinary four-digit BC years, not only extended-range ones.
datewas never affected.timestampandtimestamptzcan now scan BC leap days.4713-02-29 BCpreviously failed withday out of range.datecould already scan them.10000-01-02 03:04:05previously failed to parse, sotimestampandtimestamptzvalues at the high end of PostgreSQL's range were unreadable over the simple protocol and in anyother text-format result.
time.Timearguments in the simple protocol now encode BC dates correctly, using the same timestamp encoder.carrying into the rest of the value) instead of being kept at full precision. PostgreSQL never sends more than six
fractional digits, so this only affects values from other sources.
Behavior changes:
datenow rejects impossible dates instead of normalizing them.2024-02-30returned2024-03-01and2024-13-01returned2025-01-01; both are now errors.timestampandtimestamptzalready rejected them.text format. PostgreSQL never sends out-of-range dates, so this only affects corrupt or hand-built input; the range
is checked in both formats so that whether a value is accepted does not depend on
QueryExecMode.timestamptzalso rejects time zone displacements outside PostgreSQL's signed 32-bit seconds range, while acceptingthe wider offsets emitted for POSIX time zones, such as
+16.timestamptzvalues scanned from the text format are now returned intime.Local, or inScanLocationwhen it isset, matching what the binary format has always returned. Previously the text path kept whatever location
time.Parsederived from the offset the server sent, so the same value scanned in the two formats could report adifferent
Location()andZone(). The instant is unchanged, but everything that renders the location changeswith it:
Timestamptz.MarshalJSONnow writes the client's offset rather than the server's, so a value the serversent as
+05:30marshals as2024-01-01T13:34:05-08:00on a UTC-8 client instead of2024-01-02T03:04:05+05:30,and
DecodeDatabaseSQLValuehandsdatabase/sqlatime.Timein that same location. Set the codec'sScanLocationtotime.UTCto pin the location regardless of the client's zone.pgconn: connection URIs (
postgres://...) are now parsed by a new parser designed to exactly match libpq's URIparser behavior instead of
net/url,making pgx accept and reject exactly the same URIs as libpq (verified by differential fuzzing against libpq itself).
Most connection strings are unaffected. Edge-case behavior changes, all matching libpq:
+in query values is literal, no longer decoded as a space.%00is rejected.%20).#is ordinary data, not a fragment delimiter.@before any/(previously the last@).ssl=trueis accepted as an alias forsslmode=requirein URIs (JDBC compatibility). A repeatedsslkeyfollows the same last-occurrence-wins rule as other repeated parameters, even across the rewrite to
sslmode. Ifthe final
sslvalue is nottrue, an independent explicitsslmoderemains in effect.postgres://h1,h2:5433/dbnow means h1:5432 andh2:5433 (previously both hosts got port 5433). A port list that is neither a single port nor exactly one port per
host is an error (
could not match N port numbers to M hosts), also for keyword/value connection strings.postgres://::1/dbwas previously accepted as host::1; it is now read as an empty host followed by port:1and fails with an invalid port error. Write it aspostgres://[::1]/db.h1,,h2) get the default host instead of being dropped. Likewise, an empty host ina keyword/value string (
host=) now means the default host -- typically the Unix socket directory -- where itpreviously meant a TCP connection to an empty hostname.
?port=in a URI orport=in a keyword/value string) now means the default port 5432 for theaffected hosts; previously it was an invalid port error. Like any connection-string port, a present-but-empty port
takes precedence over
PGPORT.net/urlrejected any URI containing one. The exception is a literal NUL byte, which is still rejected, as
net/urldid.(libpq never sees one -- C strings end at the first NUL -- but in Go a raw NUL could otherwise pass through into
the NUL-delimited startup message and inject extra parameters.)
Unlike libpq, unrecognized URI query parameters are still accepted (they become runtime parameters or pgx-specific
options). Parse error messages avoid quoting the unredacted connection string and redact recognizable password
fields on a best-effort basis. Invalid connection strings can be structurally ambiguous, so password redaction
cannot be guaranteed for every malformed input.
pgconn: keyword/value connection strings (
host=... user=...) now match libpq's parser exactly, the same treatmentthe URI parser received above and verified the same way, by differential fuzzing against libpq itself. Most
connection strings are unaffected. Behavior changes, all matching libpq:
\\and\'wereunescaped and every other backslash was kept. A value containing a backslash must now escape it, as libpq
requires:
sslcert=C:\path\to\certreads asC:pathtocertand has to be writtensslcert=C:\\path\\to\\cert.This mainly affects Windows certificate and key paths, which previously came through intact without doubling.
there; it was previously rejected with
invalid backslash. Inside a quoted value the escaped terminator leavesthe string unterminated, which is still an error.
missing "=" after "us" in connection info string) instead of becomingpart of the key. Whitespace around the
=is unaffected. This most often shows up with an unquoted valuecontaining a space:
application_name=my app host=xpreviously set neither parameter and sentapp hostto theserver as a runtime parameter, and now fails to parse.
As with URIs, unrecognized keywords are still accepted where libpq rejects them, and an empty
user=is stilldropped so that
PGUSERand the OS user still apply.Fixes
BeginorBeginTx(Victor Alejandro Sanz Ararat)
TraceQueryEndwhenExecfails while deallocating invalidated cached statements (Chris Bandy)completed, avoiding leaked prepared statements and unnecessary cleanup errors (Eliran Ben-Zikri)
LoadTypesoverwriting scalar codecs such asboxandpointwith an incorrectArrayCodec(Arsen Ozhetov)FETCHstatements,including batch and pipeline execution (water)
Batch.ExecStatementis mixed with other batch commands; preserve field descriptions for empty resultsand return a nil result from
Pipeline.GetResultson errorMaxConnLifetimevalues as unlimited instead of immediately expiring connections(Aurelien Pillevesse)
ArrayCodec.Delimiter, including the semicolon delimiterused by
box[].LoadTypeandLoadTypesnow load the delimiter from PostgreSQL (Sueun Cho)string bounds from unbounded ranges (Sueun Cho)
Numeric.ScanScientificand accept scientific notation inNumeric.UnmarshalJSON; reject out-of-range scientific exponents and preserve the original input in parse errors(Sueun Cho)
"Infinity"and"-Infinity"instead of encoding it as zero(Vladimir Saraikin)
Numericwith a nilIntas zero inInt64Value, and return errors when converting NaN orinfinity to an integer instead of panicking (Vladimir Saraikin)
whose digit count, weight, or scale cannot fit the wire format, while accepting the full unsigned digit-count range.
instead of panicking when a pointer-to-pointer scan destination is nil (Rangel Reale)
data. This includes fixes for panics on malformed records and truncated multiranges (Vladimir Saraikin), and
validation of
bit/varbitbit lengths against the actual data (g3m0sis).fields, and text arrays whose dimensions and element counts disagree
separator counts; valid hstores may still contain any number of pairs (AshSgDe29071999)
slice bounds out of range.host='a\-- and the shorter='\, reachable throughpgx.ParseConfigandpgxpool.ParseConfig-- now returnunterminated quoted string in connection info string, libpq's own message forthe same input. The unquoted branch has been guarded since
be69c1c; the quoted branch carried the same unguardedincrement since the parser was ported from pgx v3. Found by fuzzing (Maxim Korotkov)
passwordandsslpasswordvalues suppliedas URI query parameters; previously only the userinfo password was redacted. Redaction matches keys the way the
parser does -- percent-encoded spellings such as
pass%77ord=are recognized -- and masks the entire raw value, soa password containing a space cannot leak its tail into the error message. Credentials stranded outside the
userinfo by a malformed URI are masked whole, and invalid-port errors no longer embed the offending text (which in
a malformed URI can be a mislaid password). Redaction of invalid connection strings is necessarily best effort:
their structure may be ambiguous, so some malformed inputs can still expose password text in an error.
ParseConfigOptions.ConnStringAllowedKeysno longer exempts an explicitly supplied empty port (?port=ina URI or
port=in a keyword/value string) from the allow-list. Only the implied all-empty port list of amulti-host URI without ports (
postgres://h1,h2/db) is exempt. An explicit empty port shadowsPGPORTeven thoughit is empty, so it must be allowed like any other user-supplied key. The URI-only
ssl=truealias is accepted wheneither
sslorsslmodeis allowed, and everyssl/sslmodespelling written in the URI is validated --including occurrences superseded by later repeated parameters.
asyncCloseso context cancellation produces a TCP FIN instead of RST, avoiding "connection reset by peer" on the server / proxy (Sean Chittenden at CrowdStrike, Inc.)StartupMessage.Encoderejects a NUL byte in any parameter name or value instead of writing it. Thestartup message body is a run of NUL-delimited strings whose length is data-driven, so a NUL in a value ends that
parameter and everything after it is read by the server as further parameters -- an
application_nameofx\x00user\x00adminchanged the role the connection logged in as. libpq cannot reach this state because itsparameters are NUL-terminated C strings.
Connectnow fails with nothing written to the wire, which coverssettings that bypass connection string parsing: service files and direct assignment to
Config.RuntimeParams,Config.User, orConfig.Database.ParseConfig, as URIs already were.Configuration
📅 Schedule: (UTC)
🚦 Automerge: Enabled.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR has been generated by Mend Renovate CLI.