Fix missing f-string prefix in two DataError messages
Two error messages were built from plain string literals containing
{...} placeholders but missing the f prefix, so the literal placeholder
text was shown to the user instead of the offending value:
- types/datetime.py: "timestamp too small (before year 1): {s!r}"
- types/json.py: "unknown jsonb binary format: {data[0]}"
The sibling branches next to the datetime message are already f-strings,
which is what makes the omission stand out. Add the missing prefix to
both so the value is interpolated.
fix: check that we receive exactly one result after executing a command
This should never happen with a working FE-BE communication, but it is
not impossible it seems: see #1337, where I think it is happening for a
lock not behaving correctly in a broken Python runtime. It is also not
impossible that some broken Postgres implementation would send us this
curveball, so let's identify it as an unexpected condition.
Replace #1340 where it was proposed to accept the condition as good, but
this is not a condition to ignore IMO as it underlines something
serious.
Daniele Varrazzo [Mon, 22 Jun 2026 21:51:29 +0000 (23:51 +0200)]
fix(c): fix variable used in fetch_many to check for returned errors
We were looping on the results previously reurnied, but only checking
the latest one. Apparently this has been enough so far, probably because the
only way it would have filed would have been in a multiple query, one of
which failing with a disconnection, and it doesn't seem it ever happened
in practice.
Change var name in the Python implementation because `res` in the loop
seems to clobber the external one.
Daniele Varrazzo [Thu, 21 May 2026 16:27:34 +0000 (09:27 -0700)]
fix: discard prepared statement upon ALTER or DISCARD statements
There is still the case that people may issue a DROP or ALTER into a
prepared statement or something else obfuscating the status message, but
at least now an explicit DISCARD can be used as escape hatch for
whatever client/server state inconsistency.
Oliver Haas [Thu, 30 Apr 2026 07:52:16 +0000 (09:52 +0200)]
fix(pool): fix race in the construction of the sync ConnectionPool lock
The lock used to make `ConnectionPool.open()` thread-safe was itself
constructed lazily in `_ensure_lock()` without any guard. Two threads
racing the first `open()` could each create a different `Lock`, defeat
the mutual exclusion in `_open()`, both pass the `_closed` guard, and
both call `_start_workers()`. The second one tripped
`assert not self._workers`.
Construct the sync lock eagerly in `__init__`. The async lock must stay
lazy because `asyncio.Lock` binds to the running event loop.
chore(deps): bump the actions group across 1 directory with 4 updates
Bumps the actions group with 4 updates in the / directory: [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action), [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel), [actions/upload-artifact](https://github.com/actions/upload-artifact) and [actions/github-script](https://github.com/actions/github-script).
Updates `docker/setup-qemu-action` from 3 to 4
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v3...v4)
Updates `pypa/cibuildwheel` from 3.3.1 to 3.4.1
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v3.3.1...v3.4.1)
Updates `actions/upload-artifact` from 6 to 7
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)
Updates `actions/github-script` from 8 to 9
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v8...v9)
Since Mypy 1.20 the SQLite cache is used by default. This highlights
Mypy concurrency issues running pre-commit in the CI, which are known to
the project devs but which we practically never encountered using the
file-based cache. See https://github.com/python/mypy/issues/21136.
The alternative solution of avoiding parallel Mypy runs in pre-commit
using `require_serial: false`, reveals other issues such as
https://github.com/python/mypy/issues/17362.
An alternative which would probably work could be to pass no file names
to mypy and re-mypy the entire codebase at every commit using
`pass_filenames: false`. See https://github.com/python/mypy/issues/13916.
This for me seems about 25% slower when changing a single file.
This commit restores the 1.19 behaviour of using the file-based cache,
which was working adequately for us. In the future we may decide to move
to `pass_filenames: false`.
Eric Buehl [Wed, 18 Mar 2026 17:30:54 +0000 (10:30 -0700)]
fix: premature ConnectionTimeout
When time.monotonic() returns a large value (e.g. on a long-running system, or when patched by a time-mocking library) causes ConnectionTimeout to fire immediately.
Gabor Gevay [Wed, 18 Feb 2026 10:06:25 +0000 (11:06 +0100)]
Fix ValueError when server sends ErrorResponse during Sync after Parse
When using prepared statements, psycopg sends Parse+Sync via
PQsendPrepare and assumes libpq returns exactly one PGresult.
However, the PostgreSQL wire protocol allows ErrorResponse during
Sync processing (e.g., when an implicit transaction commit fails).
In this case, libpq returns two PGresult objects — one for the
successful ParseComplete and one for the ErrorResponse — causing
a ValueError: "too many values to unpack (expected 1)".
This commit handles multiple results from PQsendPrepare by
iterating over all returned results and checking each for errors,
consistent with how the non-prepare execute path already works.
Varun Chawla [Tue, 10 Feb 2026 08:11:28 +0000 (00:11 -0800)]
fix: correct typo in tstrings error message and fix sql.rst docs
- Fix error message in _tstrings.py: reference nonexistent "sql.Composite"
corrected to "sql.Composable" (the actual base class)
- Fix docs/api/sql.rst: remove incorrect escape_identifier() example that
passed a str where bytes is required (closes #421)
- Fix typo "re-exoprts" -> "re-exports" in conninfo.py
Co-Authored-By: Claude Opus 4.6
Note: this MR is AI slop which has been pushed by the author without
even considering if it was correct. Corrected by a human being in the
next commit on this branch.
This PR adds critical sections to the PGconn class so that
race condition between calling into the libpq bindings and
calling `close` which sets the `_pgconn_ptr` to NULL are
eliminated.
Also add one high-level and one low-level tests that trigger TSAN
warnings without this PR.
Erik Wienhold [Wed, 14 Jan 2026 13:42:18 +0000 (14:42 +0100)]
fix: retain pgconn on OperationalError
When multiple connection attempts fail, a new OperationalError with the
combined attempt errors is raised without retaining the pgconn attribute
of the original attempt errors. Fix this by assigning the pgconn from
the last attempt error.
dependabot[bot] [Thu, 1 Jan 2026 07:10:50 +0000 (07:10 +0000)]
chore(deps): bump the actions group with 2 updates
Bumps the actions group with 2 updates: [actions/cache](https://github.com/actions/cache) and [actions/upload-artifact](https://github.com/actions/upload-artifact).
Updates `actions/cache` from 4 to 5
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)
Updates `actions/upload-artifact` from 5 to 6
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)
dependabot[bot] [Mon, 1 Dec 2025 07:39:07 +0000 (07:39 +0000)]
chore(deps): bump the actions group with 2 updates
Bumps the actions group with 2 updates: [actions/checkout](https://github.com/actions/checkout) and [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel).
Updates `actions/checkout` from 5 to 6
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)
Updates `pypa/cibuildwheel` from 3.2.1 to 3.3.0
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v3.2.1...v3.3.0)
fix(adapters): avoid race condition when replacing class name with itself
Another thread may be switched to between the dmap.pop(fqn) instruction
and the dmap[scls] one, typically at program startup when multiple
threads are making their "first" queries.
fix: mark external libpq C functions as noexcept nogil
Add noexcept nogil declarations to libpq bindings and endian conversion
functions. This improves things for when we start using critical
sections inside Cython-defines types. It's also more accurate this way,
instead of only marking the APIs that are explicitly used in a nogil
block.