Stamp 17.12.
commit : 702b3dc86d35bcc08c8e23cffa3c4d9fe9d32c85
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 9 Nov 2026 15:11:08 -0500
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 9 Nov 2026 15:11:08 -0500 M configure
M configure.ac
M meson.build
Last-minute updates for release notes.
commit : 18c4a9c5f66a1fdf02be21751b931338a04d5790
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 9 Nov 2026 12:43:05 -0500
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 9 Nov 2026 12:43:05 -0500 Security: CVE-2026-6460 and many more. M doc/src/sgml/release-17.sgml
Update .abi-compliance-history for attribute versioning changes.
commit : 3643ce4253f32925cae26651eeeb0cb972af81b8
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 9 Nov 2026 11:45:01 -0500
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 9 Nov 2026 11:45:01 -0500 Security: CVE-2026-6460
Backpatch-through: 14-18 M .abi-compliance-history
Work around broken TimescaleDB code
commit : 5f8d39154b401aaade47971f32c8ef9a4881228c
author : Andres Freund <andres@anarazel.de>
date : Mon, 9 Nov 2026 06:20:49 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:49 -0800 TimescaleDB crashes if an attribute option is present during chunk
creation as part of a DDL command, due to calling AlterTableInternal()
without having set up event trigger state. With the introduction of
attribute versioning, which stores the version in an internal attribute
option in the back-branches, attribute options are much more common.
The breakage is easy enough to work around, so do that.
This will only be applied to release branches, as this is clearly a bug
in TimescaleDB.
Security: CVE-2026-6460
Backpatch-through: 14-19 M src/backend/commands/event_trigger.c
Add checks for cross-rowtype deforming and conversions
commit : d0b52be9b1191423d8b601fb1c7c1dee96c9c038
author : Andres Freund <andres@anarazel.de>
date : Mon, 9 Nov 2026 06:20:49 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:49 -0800 The previously added attribute versioning checks prevent cases of
deforming a datum tuple with an incompatible tuple descriptor for the
same type. However, we have a number of cases where we deform a datum
tuple with a descriptor for a different relation. The additional checks
introduces here add verification for such cases, by checking that the
tuple descriptors are compatible.
The following cases are now checked during deforming:
- Deforming a versioned tuple with a RECORDOID tupledesc
This can happen with triggers and EvalPlanQual rechecks of function
scans.
- Deforming a versioned tuple with a tupledesc for another relation
This happens a bunch for partitioned tables.
Additionally, when changing the type of a tuple, the old and new types
are compared for compatibility. This currently happens in:
- heap_copy_tuple_as_datum() and via that in places like
ExecEvalConvertRowtype(), ExecFetchSlotHeapTupleDatum(), ...
- plpgsql's coerce_function_result_tuple()
In a perfect world these extra checks would not be needed. But...
It is perhaps worth noting that this knowingly emits error messages in
an untranslatable way. That was considered acceptable for now, these
error checks should not normally be hit.
Note that these checks do add nontrivial overhead. This is ameliorated
by the fact that the common ways to reach the checks are part of already
expensive operations (like trigger invocations during DML into a
partitioned table), which are expensive enough to make the added
overhead comparatively small.
Reviewed-by: Melanie Plageman <melanieplageman@gmail.com>
Reviewed-by: Tomas Vondra <tomas@vondra.me>
Reviewed-by: Noah Misch <noah@leadboat.com>
Security: CVE-2026-6460
Backpatch-through: 14 M src/backend/access/common/heaptuple.c
M src/test/isolation/expected/eval-plan-qual-rowtype-alter.out
M src/test/regress/expected/rowtypes_alter.out
Add attribute versioning specific tests
commit : 9f6302533e7fcfc3dd5444620dffbdf2102948ec
author : Andres Freund <andres@anarazel.de>
date : Mon, 9 Nov 2026 06:20:49 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:49 -0800 Some of these were originally written with AI contributions, but they
have been heavily rewritten since.
The most important thing this tests is pg_upgrade's handling of
attribute versions, including verification of composite columns staying
properly readable across upgrades.
Security: CVE-2026-6460
Backpatch-through: 14 M contrib/test_decoding/Makefile
A contrib/test_decoding/expected/rowtypes_alter.out
M contrib/test_decoding/meson.build
A contrib/test_decoding/sql/rowtypes_alter.sql
M src/bin/pg_dump/t/002_pg_dump.pl
M src/bin/pg_upgrade/Makefile
M src/bin/pg_upgrade/meson.build
A src/bin/pg_upgrade/t/009_composite_types.pl
M src/test/regress/expected/alter_table.out
A src/test/regress/expected/attribute_version.out
M src/test/regress/parallel_schedule
M src/test/regress/regress.c
M src/test/regress/sql/alter_table.sql
A src/test/regress/sql/attribute_version.sql
Introduce attribute versioning to prevent unsafe deforming
commit : bcbcbd5a23c6740a55d024a5e24c003fc1f2891a
author : Andres Freund <andres@anarazel.de>
date : Mon, 9 Nov 2026 06:20:49 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:49 -0800 Using a tuple descriptor that does not match the "shape" of a composite
datum can lead to crashes, spurious errors and can be used for exploits.
While postgres has code that prevents such mismatches, there are a
number of weaknesses in those protections that are nontrivial to fix,
particularly in the back-branches.
This commit introduces a new safety layer to prevent the worst of the
negative consequences. From now on, composite datums are marked as such
and named composite datums are versioned, allowing detection of
mismatches between a tuple descriptor and the composite during
deforming. In case of mismatches, errors are raised.
For details about how this works, see
src/backend/catalog/README.attver.md.
Note that, over time, we should prevent most of the things that the
safety layer detects at a higher level.
This fixes a number of security relevant composite type confusions.
This is a significant piece of infrastructure grafted onto the existing
code, in a back-patchable way. There unfortunately still are some
compatibility issues due to this commit:
- To get the full protection, extensions need to be rebuilt, due to
DatumGetHeapTupleHeader() not being a macro anymore (but an
out-of-line function). That also means that some extensions built
against a minor version with this change will fail with a linker
failure when loaded in an earlier minor version.
Extensions that have not been recompiled can observe
HeapTupleHeaderGetTypMod() returning parts of the version instead of
-1.
- Upgrading to a minor version with this fix, performing DDL,
downgrading and then upgrading again can cause errors for composite
datums formed while downgraded; DDL while downgraded can additionally
break the version invariants.
- Code looking at pg_attribute.attoptions will see the new pg_attver
key, which could lead to external applications setting this new key
via DDL.
To reduce the impact of that, ALTER COLUMN ... SET (pg_attver = ...)
is ignored.
- Dumps of a server with this change, made by a pg_dump without it,
contain ALTER COLUMN ... SET (pg_attver = ...), which servers without
this change reject. As pg_upgrade uses the target's pg_dump,
upgrading from a minor version with this change to a minor version of
a later major version without it fails.
- Use of TupleDescCopy() is forbidden with tupledescs for named
composites. The new TupleDescCopySize()/TupleDescCopySized() can copy
any descriptor, and verify that the destination is large enough.
- Code that forms datums and then modifies fields in t_choice.t_datum
without removing HEAP_IS_DATUM can trigger spurious errors. To reduce
the impact of that, the version check skips some known cases of such
modifications (see list below for the known effects on extensions).
- DatumGetHeapTupleHeader() will be slower when pre-upgrade tuples are
passed, due to copying those datums before modifying them.
- Tuples returned by heap_form_tuple() are now marked as datum tuples
and, for named composites, version-checked when deformed. Code that
writes such tuples to its own storage needs to clear the marking (via
HeapTupleHeaderClearDatum()).
- heap_modify_tuple() used to copy t_ctid, which is not possible
anymore, due to that containing the datum version. No callers could be
found that need the copy.
At least some of the above should be mentioned in the release notes.
Investigation via github codesearch, a dump of all the extensions on
pgxn, extensions from various distributions, etc, suggests the following
effects on extensions:
- PostGIS does some quite broken things on the upgrade from < 3.6 to
3.6, including corrupting the catalog. This is more noticeable with
these changes. In particular WARNINGs will be emitted and pg_upgrade
will fail, until PostGIS is fixed.
See https://trac.osgeo.org/postgis/ticket/6142
- PL/R (trigger arguments), pg_variables (stored records) and pljs copy
tuples and set the composite datum header fields by hand, which leads
to spurious errors when the result is read. They should use
heap_copy_tuple_as_datum() instead.
- ddlx fails for columns with an explicitly set attribute option, as it
expects at most one option per column. pgcontext refuses to convert
versioned columns, as it rejects columns with any attribute options.
- firebird_fdw spuriously errors out in some cases, due to overwriting
the xmax (but not xmin). Other FDWs clear out xmin, which this commit
uses as a signal that the version information is not usable, but
there's no easy way to detect this in the firebird_fdw case. The fix
would be a one-liner.
- storage_engine's rowcompress table AM stores composite datum-style
tuples, which will raise errors on read if the table schema is
subsequently changed. The fix is trivial.
- pgrx-based extensions will not be protected, even after recompiling,
due to pgrx copying postgres code like heap_getattr(). pgrx will need
to update that copied code.
- pg_strom's arrow FDW bypasses core functionality for more than 100
columns and inserts attributes itself. That does not set the attribute
versions correctly and thus will trigger spurious errors if composites
of that FDW are used.
- timescaledb has a bug causing crashes during chunk creation as part of
DDL commands if attribute options are present. With this change every
new / altered attribute has an attribute option.
A later commit will work around these crashes.
- Citus' distributed aggregation path theoretically can hit the new
error in TupleDescCopy(). However, it looks like Citus' planner does
not ever generate such plans - but the ERROR can be reached by a user
issuing a query with calls to the relevant internal Citus functions.
The fix again is trivial, should it be needed (use
CreateTupleDescCopy() instead of open-coding it).
The new composite_type_mismatch developer option allows superusers to
turn the version mismatch error into a warning, or to disable the check.
Both settings are unsafe.
With this infrastructure in place, additional checks can be
performed (e.g. in heap_datum_check(), heap_datum_update_type()), at the
cost of some complexity and runtime overhead. A subsequent commit adds
those.
Reported-by: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Reported-by: Andres Freund <andres@anarazel.de>
Reported-by: Andrew Gierth <andrew@tao11.riddles.org.uk>
Reported-by: Alexander Lakhin <exclusion@gmail.com>
Reported-by: Francesco Verardi <frevadiscor89@gmail.com>
Reported-by: Hcamael <baiyjrh@gmail.com>
Reported-by: Tomer Fichman <tomer@irregular.com>
Reported-by: OpenAI Security Research Team
Reported-by: Peter Geoghegan <pg@bowt.ie>
Reported-by: Mehmet Ince @mdisec <mehmet@mehmetince.net>
Reported-by: Bryan Green <dbryan.green@gmail.com>
Reported-by: Sumedh Pathak <sumedhpathak@microsoft.com>
Reported-by: Nicholas Carlini <npc@anthropic.com>
Reported-by: Sergey Shinderuk <s.shinderuk@postgrespro.ru>
Reported-by: Yuxiao Guo <dllggyx@outlook.com>
Reported-by: Nikhil Sontakke <nikhil@planetscale.com>
Reported-by: Stephan Lipp <Stephan@Adalogics.com>
Reported-by: Daniel Bakker <daniel@jackds.nl>
Reviewed-by: Melanie Plageman <melanieplageman@gmail.com>
Reviewed-by: Tomas Vondra <tomas@vondra.me>
Reviewed-by: Noah Misch <noah@leadboat.com>
Reviewed-by: Robert Haas <robertmhaas@gmail.com> (parts)
Security: CVE-2026-6460
Backpatch-through: 14 M contrib/amcheck/verify_heapam.c
M doc/src/sgml/catalogs.sgml
M doc/src/sgml/config.sgml
M src/backend/access/common/heaptuple.c
M src/backend/access/common/indextuple.c
M src/backend/access/common/reloptions.c
M src/backend/access/common/tupdesc.c
M src/backend/access/heap/heapam.c
M src/backend/access/heap/heaptoast.c
M src/backend/access/heap/hio.c
M src/backend/access/heap/rewriteheap.c
A src/backend/catalog/README.attver.md
M src/backend/catalog/catalog.c
M src/backend/catalog/heap.c
M src/backend/commands/sequence.c
M src/backend/commands/tablecmds.c
M src/backend/executor/execExprInterp.c
M src/backend/executor/execSRF.c
M src/backend/executor/execTuples.c
M src/backend/executor/spi.c
M src/backend/utils/adt/expandedrecord.c
M src/backend/utils/adt/jsonfuncs.c
M src/backend/utils/adt/pg_upgrade_support.c
M src/backend/utils/cache/relcache.c
M src/backend/utils/cache/typcache.c
M src/backend/utils/errcodes.txt
M src/backend/utils/misc/guc_tables.c
M src/backend/utils/sort/tuplesortvariants.c
M src/bin/pg_amcheck/t/004_verify_heapam.pl
M src/bin/pg_dump/pg_dump.c
M src/bin/pg_dump/pg_dump.h
M src/include/access/htup_details.h
M src/include/access/reloptions.h
M src/include/access/tupdesc.h
M src/include/catalog/binary_upgrade.h
M src/include/catalog/catalog.h
M src/include/catalog/heap.h
M src/include/catalog/pg_attribute.h
M src/include/catalog/pg_class.h
M src/include/catalog/pg_proc.dat
M src/include/fmgr.h
M src/include/storage/itemptr.h
M src/include/utils/expandedrecord.h
M src/pl/plpgsql/src/expected/plpgsql_record.out
M src/pl/plpgsql/src/pl_exec.c
M src/test/isolation/expected/rowtypes-alter.out
M src/test/modules/spgist_name_ops/expected/spgist_name_ops.out
M src/test/regress/expected/rowtypes_alter.out
M src/tools/pgindent/typedefs.list
Always acquire expanded record descriptors early
commit : deebcc772b5b27bfaa2d20608ca77a1439fc252c
author : Andres Freund <andres@anarazel.de>
date : Mon, 9 Nov 2026 06:20:49 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:49 -0800 Only make_expanded_record_from_datum() deferred tuple descriptor lookup,
and it has no direct/indirect users in core nor in known open source
extensions (checked via pgxn and github). The other ERH constructors
already acquire a descriptor eagerly.
The main reason for this change is that subsequent commits are
simplified by it.
This also allows implementing the datum constructor using
make_expanded_record_from_typeid(), removing a good bit of duplicated
code.
This preserves ABI compatibility, to allow for backpatching.
Reviewed-by: Tomas Vondra <tomas@vondra.me>
Reviewed-by: Melanie Plageman <melanieplageman@gmail.com>
Security: CVE-2026-6460
Backpatch-through: 14 M src/backend/utils/adt/expandedrecord.c
M src/include/utils/expandedrecord.h
M src/test/regress/expected/rowtypes_alter.out
plpgsql: Check compatibility of source/target ERHs
commit : db59ee006aa461afcc007eb4a70aa5bb5c8b73dd
author : Andres Freund <andres@anarazel.de>
date : Mon, 9 Nov 2026 06:20:49 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:49 -0800 Otherwise we can assign tuples not matching the target tupledescriptor,
e.g. because one of the tuple descriptors has been acquired before/after
some DDL.
It may seem tempting to just not enter the surrounding "if" if the
er_tupdesc_id doesn't match (in fact both Heikki and I (Andres) were
previously tempted), but the fieldwise-assignment path differs
behaviorally, due to not copying the ctid along. As the tupdesc_id can
increment due to invalidation processing, that would lead to
unpredictable behavior. Tests were added in previous commits that would
show that behavior.
Note that there are some cases where this will cause failures that are
not required for correctness, when assigning a variable of a changed
type to a previously used variable. It'd add more complexity to make
that work; for a bugfix this seems good enough.
As also noted in a comment, this perhaps should be done in the expanded
record layer. But that'd require a few more changes and would make it
harder to emit a somewhat understandable error message.
Reviewed-by: Tomas Vondra <tomas@vondra.me>
Reviewed-by: Melanie Plageman <melanieplageman@gmail.com>
Security: CVE-2026-6460
Backpatch-through: 14 M src/pl/plpgsql/src/expected/plpgsql_record.out
M src/pl/plpgsql/src/pl_exec.c
M src/test/regress/expected/rowtypes_alter.out
Add tests for row type changes causing issues
commit : 3b2c73d0ec03996cdf6047dc407468d976221978
author : Andres Freund <andres@anarazel.de>
date : Mon, 9 Nov 2026 06:20:49 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:49 -0800 Most of these show the problem by displaying 'FIXME' in test output in
case of problems, for more details about that aspect see
rowtypes_alter.sql.
A large percentage of these tests were converted from a larger set of
tests that were not mergeable in their former shape.
Author: Andres Freund <andres@anarazel.de>
Author: Heikki Linnakangas <heikki.linnakangas@iki.fi> (in different form)
Security: CVE-2026-6460
Backpatch-through: 14 A src/test/isolation/expected/eval-plan-qual-rowtype-alter.out
A src/test/isolation/expected/rowtypes-alter.out
M src/test/isolation/isolation_schedule
A src/test/isolation/specs/eval-plan-qual-rowtype-alter.spec
A src/test/isolation/specs/rowtypes-alter.spec
A src/test/regress/expected/rowtypes_alter.out
M src/test/regress/parallel_schedule
A src/test/regress/sql/rowtypes_alter.sql
Expand ALTER TABLE related tests
commit : f397e0ae3428fc4d5c6dcbf243130e8ce0435c7a
author : Andres Freund <andres@anarazel.de>
date : Mon, 9 Nov 2026 06:20:49 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:49 -0800 An upcoming fix touches related code. It turns out there were a number
of cases there were not or only incidentally tested. In particular, we
didn't leave tables with combinations of dropped columns to be tested by
002_pg_upgrade.pl
Security: CVE-2026-6460
Backpatch-through: 14 M src/test/regress/expected/alter_table.out
M src/test/regress/sql/alter_table.sql
Add some very basic tests for make_expanded_record_from_datum()
commit : d3d599625d0312a834d403e209f0500d7475c40d
author : Andres Freund <andres@anarazel.de>
date : Mon, 9 Nov 2026 06:20:49 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:49 -0800 make_expanded_record_from_datum() / DatumGetExpandedRecord() /
PG_GETARG_EXPANDED_RECORD() were unused. I could not find any extensions
using it either.
It seemed too likely that one of the subsequent fixes in this series
would break them, therefore this adds some tests (written with AI help).
Security: CVE-2026-6460
Backpatch-through: 14 M src/test/regress/expected/rowtypes.out
M src/test/regress/expected/test_setup.out
M src/test/regress/regress.c
M src/test/regress/sql/rowtypes.sql
M src/test/regress/sql/test_setup.sql
Add test for NOT NULL differing across partition hierarchy
commit : 05283881a75e2a19919e1fb0fa54748585de0d4d
author : Andres Freund <andres@anarazel.de>
date : Mon, 9 Nov 2026 06:20:48 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:48 -0800 Test a trigger on a partition table where a columns NOT NULLness differs
between parent and child.
Development versions of an upcoming fix broke those cases without
breaking any extant tests.
Security: CVE-2026-6460
Backpatch-through: 14 M src/test/regress/expected/triggers.out
M src/test/regress/sql/triggers.sql
Expand EPQ test coverage
commit : 98e9001cf417e8970122c4581c2e8b462285e02b
author : Andres Freund <andres@anarazel.de>
date : Mon, 9 Nov 2026 06:20:48 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:48 -0800 Test EPQ handling in presence of dropped columns and resjunk elements in
the targetlist. Development versions of an upcoming fix broke those
cases without breaking any extant tests.
Security: CVE-2026-6460
Backpatch-through: 14 A src/test/isolation/expected/eval-plan-qual-dropped-column.out
A src/test/isolation/expected/eval-plan-qual-resjunk.out
M src/test/isolation/isolation_schedule
A src/test/isolation/specs/eval-plan-qual-dropped-column.spec
A src/test/isolation/specs/eval-plan-qual-resjunk.spec
plpgsql: Expand test coverage of assignments
commit : d5cf40e02cef7a4c195b0814cfd9481404446700
author : Andres Freund <andres@anarazel.de>
date : Mon, 9 Nov 2026 06:20:48 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:48 -0800 We previously were lacking coverage for important assignment related
codepaths that will be touched in an upcoming fix:
- Assignment between record variables preserve ctid/tableoid only when
the source tuple can be copied verbatim; where a conversion is needed
they are silently dropped.
- A record variable's declared type is re-resolved by name
Security: CVE-2026-6460
Backpatch-through: 14 M src/pl/plpgsql/src/expected/plpgsql_record.out
M src/pl/plpgsql/src/sql/plpgsql_record.sql
Harden some allocations against integer wraparound
commit : a17bc8b3cac07360fd2331fa3a907394617caa68
author : Álvaro Herrera <alvherre@kurilemu.de>
date : Mon, 9 Nov 2026 06:20:48 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:48 -0800 This isn't closing any specific security bug, but attacks are perhaps
conceivable due to various forms of type confusion or data corruption.
Tighten that up a bit. More is undoubtely possible.
Author: Álvaro Herrera <alvherre@kurilemu.de>
Security: CVE-2026-120139
Backpatch-through: 14 M src/backend/access/common/detoast.c
M src/backend/access/common/toast_compression.c
M src/backend/utils/adt/array_userfuncs.c
M src/include/access/tableam.h
M src/include/varatt.h
Fix type confusion with materialized set-returning functions
commit : 55c00bffe79616f66153e3bc9c04f6297a8eecd9
author : Robert Haas <rhaas@postgresql.org>
date : Mon, 9 Nov 2026 06:20:48 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:48 -0800 Set-returning functions in the target list with indeterminate RECORD
return types incorrectly used the same tuple descriptor to read and
return tuples from the tuplestore across invocations.
These types of SRFs resolve their rowtype from the passed in record
argument and it can differ from invocation to invocation. Reading tuples
with the wrong shape of tuple descriptor can lead to incorrect results,
crashes, and security issues. It was reported as a way to read arbitrary
out-of-line values via json_populate_recordset().
To fix this, drop and recreate the slot and its tuple descriptor per SRF
invocation for materialized SRFs in the target list with dynamic RECORD
return types.
Only the target-list path is affected: table functions in FROM validate
every result against the query-supplied descriptor, and value-per-call
SRFs never retain a slot.
Co-authored-by: Melanie Plageman <melanieplageman@gmail.com>
Co-authored-by: Robert Haas <rhaas@postgresql.org>
Security: CVE-2026-120141
Backpatch-through: 14 M src/backend/executor/execSRF.c
M src/test/regress/expected/json.out
M src/test/regress/sql/json.sql
Align REASSIGN OWNED caller-privilege checks with ALTER ... OWNER TO.
commit : 0b8d1768f2def0800b651aee63447aea79b1493d
author : Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:48 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:48 -0800 Commit 3d14e171e9e2236139e8976f3309a588bcc8683b (v16) made giving an
object to another role depend on the ability to SET ROLE to that role.
However, a role granted "INHERIT TRUE, SET FALSE" could bypass that, via
REASSIGN OWNED. A role granted "INHERIT FALSE, SET TRUE" saw the
inverse problem: REASSIGN OWNED failed spuriously, while ALTER ... OWNER
succeeded.
For avoidance of doubt, fixing this doesn't change the limitations of
SET FALSE. If a role owns nothing, SET FALSE grantees can't make that
role own its first object. For many kinds of objects, once a role owns
one, "INHERIT TRUE, SET FALSE" prevents nothing compared to "INHERIT
TRUE, SET TRUE".
REASSIGN OWNED also bypassed the CREATEDB privilege check in ALTER
DATABASE ... OWNER TO. Fix by duplicating the check. This affects all
supported versions. This sort of omission remains easy to miss under
the present design, so refactoring would be prudent.
Document the way REASSIGN OWNED still differs from ALTER ... OWNER TO:
the new owner's CREATE privilege on the schema of a relation or type is
not checked.
Back-patch to v14 (all supported versions). In v15 and v14, REASSIGN
OWNED's privs checks is stricter than the membership checks in ALTER
... OWNER TO. Leave that alone, rather than aligning the commands. Do
fix the CREATEDB omission.
Reported-by: Zsolt Parragi <zsolt.parragi@percona.com>
Author: Zsolt Parragi <zsolt.parragi@percona.com>
Backpatch-through: 14
Security: CVE-2026-120146 M doc/src/sgml/ref/reassign_owned.sgml
M src/backend/catalog/pg_shdepend.c
M src/backend/commands/user.c
M src/test/regress/expected/create_role.out
M src/test/regress/expected/database.out
M src/test/regress/expected/dependency.out
M src/test/regress/sql/create_role.sql
M src/test/regress/sql/database.sql
Close portals at end of trigger functions with transition tables.
commit : 695d72c57eb1074e324463308ea8e4bd0dee7fec
author : Jeff Davis <jdavis@postgresql.org>
date : Mon, 9 Nov 2026 06:20:48 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:48 -0800 The query environment needed for portals over transition tables for
triggers lasts only as long as the SPI connection, so the portals must
be closed. If not closed within the function, SPI_finish()
force-closes them.
Reported-by: Peter Geoghegan <pg@bowt.ie>
Reviewed-by: Amit Langote <amitlangote09@gmail.com>
Backpatch-through: 14
Security: CVE-2026-120143 M doc/src/sgml/spi.sgml
M src/backend/executor/spi.c
M src/backend/utils/mmgr/portalmem.c
M src/include/utils/portal.h
Enforce view row visibility for INSERT ... ON CONFLICT target rows.
commit : a14aacebeb426e1d91b5813cb1a502305ee0a9e0
author : Peter Geoghegan <pg@bowt.ie>
date : Mon, 9 Nov 2026 06:20:48 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:48 -0800 The WHERE condition of an automatically updatable view restricts the
rows that an UPDATE, DELETE, or MERGE through the view can affect to
those that are visible through it. INSERT ... ON CONFLICT DO UPDATE
through such a view did not follow this rule. The conflicting row is
found through the arbiter index rather than by scanning the view, and
the view's WHERE condition was never applied to it. DO UPDATE therefore
updated the row whether or not the view showed it, and evaluated its SET
expressions, its WHERE clause, and RETURNING against it; ON CONFLICT DO
SELECT read it the same way. This odd set of behaviors was documented,
but it created a security issue, described further down.
Fix by having rewriteTargetView() emit the view's WHERE quals as a
WithCheckOption of new kind WCO_VIEW_CONFLICT_CHECK, which
ExecOnConflictUpdate() and ExecOnConflictSelect() check against the
conflicting row, innermost view first and before the ON CONFLICT WHERE
clause is evaluated. Using a new WCO kind for this is perhaps a little
unorthodox, but doing so provides exactly what is needed here: it raises
an error when a conflicting row doesn't satisfy the view's quals. This
makes sense because ON CONFLICT DO UPDATE guarantees that each row
proposed for insertion is either inserted or updated, neither of which
is feasible here. The arbiter index belongs to the base relation, so
the conflicting row makes insertion the wrong thing to do. The view's
WHERE condition puts the conflicting row beyond the reach of an update
through the view, but it doesn't make sense to act as if there's no row
to update, because when that happens we're supposed to take the
insertion path instead (that's what upsert promises). This situation is
fundamentally irreconcilable, so throwing an error is all we can do.
This fixes a security bug. A user whose privileges on a view allow an
upsert through it, but who holds none on its base relation, can modify
every row that the view hides, one at a time, by guessing their keys, as
if the view had no WHERE condition. The same user can read those rows,
through the conflict action's WHERE clause, its SET expressions, and
RETURNING. This historical behavior is hard to defend in general, and
impossible to defend in cases where the target view happens to be a
security barrier view.
The problem dates to commit 168d5805e, which added ON CONFLICT and
documented its original behavior with updatable views. ON CONFLICT DO
SELECT was added to Postgres 19 by commit 88327092f, and it inherited
that behavior.
An upsert through a view that relied on reaching rows the view hides now
fails. This should be called out as an incompatibility in the release
notes for the next set of minor releases.
Reported-by: Qing Xu <emyiqing@gmail.com>
Reported-by: Carl Andersen <carl@spearbit.com>
Reported-by: Zsolt Parragi <zsolt.parragi@percona.com>
Reported-by: shihao zhong <zhong950419@gmail.com>
Author: Peter Geoghegan <pg@bowt.ie>
Author: Zsolt Parragi <zsolt.parragi@percona.com>
Reviewed-by: Robert Haas <robertmhaas@gmail.com>
Security: CVE-2026-16237
Backpatch-through: 14 M doc/src/sgml/ref/create_view.sgml
M src/backend/executor/execMain.c
M src/backend/executor/nodeModifyTable.c
M src/backend/rewrite/rewriteHandler.c
M src/include/nodes/parsenodes.h
M src/test/regress/expected/rowsecurity.out
M src/test/regress/expected/updatable_views.out
M src/test/regress/sql/rowsecurity.sql
M src/test/regress/sql/updatable_views.sql
Protect against miscalculation of tuple sizes via int32 overflow
commit : bef80ad61bfd90710b261c58f9c48203d0ce7961
author : Álvaro Herrera <alvherre@kurilemu.de>
date : Mon, 9 Nov 2026 06:20:48 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:48 -0800 In-memory tuple sizes were being calculated in 64-bit-sized variables
and later assigned to 32-bit variables, potentially leading to integer
wraparound, which could then be used to overwrite server memory with
attacker-controlled contents. Repair by using overflow-aware addition
and, for additions done in loops, checking that the result complies with
the 1GB palloc limit at each step.
Reported-by: Hillai Ben Sasson <hillai.bensasson@wiz.io>
Reported-by: Jayden Sarveshkumar <jayden@verialabs.com>
Reported-by: Mehmet Ince <mehmet@mehmetince.net>
Author: Álvaro Herrera <alvherre@kurilemu.de>
Reviewed-by: Tatsuo Ishii <ishii@postgresql.org>
Security: CVE-2026-120139
Backpatch-through: 14 M src/backend/access/common/heaptuple.c
M src/backend/access/heap/heaptoast.c
M src/backend/executor/execTuples.c
M src/backend/utils/adt/array_userfuncs.c
M src/backend/utils/adt/multirangetypes.c
M src/include/access/htup_details.h
M src/include/access/tupmacs.h
Reject pseudo-type output parameters in SQL functions.
commit : 5e99fab61471a6967730f067852cce4a178c18bc
author : Peter Geoghegan <pg@bowt.ie>
date : Mon, 9 Nov 2026 06:20:48 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:48 -0800 fmgr_sql_validator() rejected unsafe pseudo-type input parameters, but
neglected to apply the same rule to output parameters. An SQL function
could therefore declare an output column of type "any". This created
problems for tupledesc_match(), which checks an SQL function's result
rows against its declared output columns using IsBinaryCoercible().
IsBinaryCoercible accepts every type for "any", so rows of an arbitrary
row type could be read using a tuple descriptor that does not match
their physical layout. This could be abused to disclose server memory
contents, and to perform arbitrary code execution.
To fix, teach fmgr_sql_validator() to apply its pseudo-type check to
parameters of every mode (not just for input parameters).
Note that the treatment of record is somewhat arbitrary: record and
record[] remain allowed as output column types, while record remains
disallowed as an input parameter type. Allowing record in either
position is likely safe, but a back-patched security fix is not the
place to change existing behavior either way.
Reported-by: Peter Geoghegan <pg@bowt.ie>
Reported-by: Stephan Lipp <Stephan@Adalogics.com>
Author: Peter Geoghegan <pg@bowt.ie>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Security: CVE-2026-18447
Backpatch-through: 14 M src/backend/catalog/pg_proc.c
M src/test/regress/expected/create_function_sql.out
M src/test/regress/sql/create_function_sql.sql
plpgsql: recheck record field's type after casting value.
commit : 3a3eb761619cfd24fb3817628ccec699f2cc9187
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 9 Nov 2026 06:20:48 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:48 -0800 exec_cast_value() can reach code that will change the rowtype of a
RECORD variable, but the RECFIELD case in exec_assign_value() doesn't
account for that and assumes the record field type and column number
that it previously looked up are still good. Recheck that data
if the tuple type ID changed; since that condition never holds in
normal usage, this is a cheap enough safety check.
Reported-by: Claude and Ada Logics
Author: Tom Lane <tgl@sss.pgh.pa.us>
Backpatch-through: 14
Security: CVE-2026-120151 M src/pl/plpgsql/src/pl_exec.c
Avoid using a dangling pointer in expanded-record domain checks.
commit : f4d200615f24a20f1507adf127aad4d944e24f9c
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 9 Nov 2026 06:20:48 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:48 -0800 build_dummy_expanded_header() sets up the expanded record's dummy
child to have eoh_context equal to the parent's er_short_term_cxt.
If some operation on the dummy record causes creation of a short-term
context belonging to the dummy record itself, that will be a child
of the parent's er_short_term_cxt, which means it will get deleted
when we reset the parent's er_short_term_cxt at the end of the callers
of build_dummy_expanded_header(). So the next time through, we'll try
to use a dangling context pointer, which often will now be pointing
at some other unrelated context. That at least risks a crash, and
it's reported to be exploitable for arbitrary code execution.
To fix, just clear the potentially-dangling pointer in the next
usage cycle.
Reported-by: Claude and Ada Logics
Author: Tom Lane <tgl@sss.pgh.pa.us>
Backpatch-through: 14
Security: CVE-2026-120150 M src/backend/utils/adt/expandedrecord.c
Teach equalRowTypes() to safely handle dropped columns.
commit : 354d5c71b1257da60f3186367c0b4c6f1d9cb692
author : Peter Geoghegan <pg@bowt.ie>
date : Mon, 9 Nov 2026 06:20:48 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:48 -0800 equalRowTypes compared the name, type, typmod, and collation of
corresponding attributes. That's sufficient when neither attribute has
been dropped, since their physical storage attributes (attlen and
attalign) are derived from their type. But a dropped attribute has its
atttypid set to InvalidOid while keeping the attlen and attalign of the
type that the column used to have. Two row types that merely had
dropped attributes in the same positions could therefore be considered
equal, even when the dropped attributes' widths or alignments differed.
That was a problem for typcache.c's record type cache, which relies on
equalRowTypes to decide whether a tuple descriptor matches one that was
already assigned a record typmod. A tuple built using one tuple
descriptor could be assigned the typmod of a physically incompatible
descriptor, allowing it to be deformed using that descriptor. That
could be abused to make the server disclose memory contents, and to
achieve arbitrary code execution.
To fix, make equalRowTypes also compare the attlen and attalign of
corresponding dropped attributes.
Oversight in commit 20e58105, which introduced equalRowTypes.
equalTupleDescs already compared attlen and attalign, but equalRowTypes
missed that precedent.
Reported-by: Peter Geoghegan <pg@bowt.ie>
Author: Peter Geoghegan <pg@bowt.ie>
Reviewed-by: Robert Haas <robertmhaas@gmail.com>
Reviewed-by: Andrew Dunstan <andrew@dunslane.net>
Security: CVE-2026-120142
Backpatch-through: 17 M src/backend/access/common/tupdesc.c
Validate all connection options before using libpqsrv_connect*
commit : ea9b427faa3aedff241548b9164ccd496857021a
author : Jacob Champion <jchampion@postgresql.org>
date : Mon, 9 Nov 2026 06:20:48 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:48 -0800 Connection options passed to dblink(), dblink_connect[_u](), and
dblink_exec() didn't flow through the same validation path as options
which were set via foreign server definitions. Additionally, connection
options added to subscriptions didn't go through any validation except
for the password check. This allowed the sslkeylogfile parameter and
all oauth_* parameters, both introduced in PG18, to be set by relatively
unprivileged users.
Add libpqsrv sanitization functions which block unsafe and unknown
connection options. These are called before libpqsrv_connect() and its
variants. (Extensions which use those functions are encouraged to do the
same.) walrcv_connect() makes an exception to this rule for
primary_conninfo, which is still allowed to contain any parameters.
To reduce the size of the patchset, the implementation has the following
drawbacks, which we hope to improve after release:
- The superuser is also restricted.
- The sanitization functions are not invoked when administering foreign
server options, or when modifying subscriptions without connecting, to
avoid interfering with pg_upgrade and dump/restore. The restrictions
will be applied later, at connection time, which may be a surprise.
Backpatch all the way down. Though the new options aren't present in
older libpqs, running an older server with a newer libpq is common. The
tests for the newer options unfortunately cannot be easily backpatched
past PG18.
Reported-by: Andrey Krasichkov <buglloc@yandex-team.ru>
Reported-by: b.altawil <b.altawil@proton.me>
Reported-by: Zsolt Parragi <zsolt.parragi@percona.com>
Suggested-by: Noah Misch <noah@leadboat.com> (sslkeylogfile part)
Reviewed-by: Noah Misch <noah@leadboat.com> (sslkeylogfile part)
Reviewed-by: Andres Freund <andres@anarazel.de> (PG14/15 backpatch)
Backpatch-through: 14
Security: CVE-2026-14661 M contrib/dblink/dblink.c
M contrib/postgres_fdw/connection.c
M src/backend/libpq/Makefile
A src/backend/libpq/be-fe-helpers.c
M src/backend/libpq/meson.build
M src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
M src/include/libpq/libpq-be-fe-helpers.h
Check ON CONFLICT target row against RLS policies before user quals.
commit : 9c677791d3f7b60e78d5fc3d45e34ce1367eee86
author : Peter Geoghegan <pg@bowt.ie>
date : Mon, 9 Nov 2026 06:20:48 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:48 -0800 ExecOnConflictUpdate() and ExecOnConflictSelect() evaluated the
user-supplied ON CONFLICT WHERE clause before checking the pre-existing
conflicting row against the RLS policy USING quals collected as
WCO_RLS_CONFLICT_CHECK. A user could therefore have that clause read a
row that no policy allowed them to see.
Fix by performing the check immediately after the conflicting row is
locked and found visible, before the WHERE clause is evaluated. A row
that fails the check now raises an error whatever the WHERE clause would
have returned, so the two outcomes are no longer distinguishable.
This moves the USING quals of the UPDATE policies ahead of the WHERE
clause as well, not just those of the SELECT policies. Only the latter
is a security issue, since a row that nothing but an UPDATE policy hides
can be read with a plain SELECT anyway. No order is consistent with
every other command: a plain UPDATE evaluates both kinds of USING qual
ahead of the user's quals, whereas MERGE evaluates a WHEN MATCHED ... AND
condition ahead of the UPDATE policies' USING quals, since it cannot
know whether it is going to update until then. ON CONFLICT DO UPDATE
now behaves like UPDATE.
This dates to 168d5805e, which added WCO_RLS_CONFLICT_CHECK along with
ON CONFLICT itself. ON CONFLICT DO SELECT was added in 19 by 88327092f,
so the back branches only need the ExecOnConflictUpdate() part of the
fix.
Reported-by: Peter Geoghegan <pg@bowt.ie>
Reported-by: Zsolt Parragi <zsolt.parragi@percona.com>
Reported-by: Only see myself <1950233439@qq.com>
Reported-by: Nicholas Carlini <npc@anthropic.com>
Reported-by: Joao Detomini <joao.detomini@enterprisedb.com>
Reported-by: Josef Šimánek <josef.simanek@gmail.com>
Reported-by: Qing Xu <emyiqing@gmail.com>
Author: Peter Geoghegan <pg@bowt.ie>
Author: Zsolt Parragi <zsolt.parragi@percona.com>
Reviewed-by: Robert Haas <robertmhaas@gmail.com>
Security: CVE-2026-120154
Backpatch-through: 14 M src/backend/executor/nodeModifyTable.c
M src/test/regress/expected/rowsecurity.out
M src/test/regress/sql/rowsecurity.sql
Fix race condition adding a DEFAULT expression to a function
commit : 5da4d4b1b4c613f080a4ade4bdaeef2eb7859578
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Mon, 9 Nov 2026 06:20:48 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:48 -0800 Because check_composite_type_dependency() didn't hold a lock while
inspecting the pg_proc entry, it was possible that the dependency was
deemed harmless, but concurrently, another backend added a DEFAULT
expression to the function that should've been rejected. To fix, hold
an AccessShareLock on the pg_proc object while we inspect it, and on
the other side of the story, hold an AccessExclusiveLock on the
pg_proc object when modifying it.
Note that we don't lock functions or other objects except relations
when using them in queries, so the AccessExclusiveLock doesn't prevent
concurrent queries using the function.
This also adds locking to other objects (constraints, types) inspected
in check_composite_type_dependency(), although that is just pro forma,
because there is no DDL to replace the expression of an existing CHECK
constraint, and altering a rowtype already holds a lock that conflicts
with an ALTER DOMAIN SET DEFAULT expression on a domain over the same
rowtype.
Relations don't have this problem, because there is no DDL command to
alter an existing table's PARTITION BY expression, nor an existing
index's expressions or quals. The OF type can be altered, but AFAICS
we have sufficient locking in place for that.
Reviewed-by: Andres Freund <andres@anarazel.de>
Security: CVE-2026-6460
Backpatch-through: 14 M src/backend/catalog/pg_proc.c
M src/backend/commands/tablecmds.c
M src/test/isolation/expected/type-ddl-locking.out
M src/test/isolation/specs/type-ddl-locking.spec
Lock affected rowtype and dependent types on ALTER ATTRIBUTE
commit : 83ec4accdcec1dccb3b7ecab7567af8938834145
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Mon, 9 Nov 2026 06:20:48 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:48 -0800 On ALTER TYPE ... ALTER ATTRIBUTE, acquire AccessExclusiveLock on the
rowtype, and all dependent types (arrays, domains, etc. over the
target type). This ensures that a concurrent backend doesn't add a
dependency to the type that we would miss in our dependency checks.
We were already holding the lock on the corresponding relation, and
that conflicted with many operations, but not all operations that use
a type also lock its relation.
Note that we don't lock types (or other objects except relations) when
using them in queries, so the AccessExclusiveLock doesn't prevent
concurrent queries from using the type.
Reviewed-by: Andres Freund <andres@anarazel.de>
Security: CVE-2026-6460
Backpatch-through: 14 M src/backend/commands/tablecmds.c
M src/test/isolation/expected/type-ddl-locking.out
M src/test/isolation/specs/type-ddl-locking.spec
Prevent ALTERing a row type that's concurrently used in a column
commit : 800e673510f666e5b04ccab96c233cc8b438b46b
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Mon, 9 Nov 2026 06:20:48 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:48 -0800 ALTER TABLE/TYPE checks if the type is being used in a column and
forbids it, because that would require rewriting the data in the
affected table. But the check missed cases where the type was
concurrently used in a CREATE TABLE or ALTER TABLE ADD COLUMN that
hasn't committed yet.
Commit 2fbb21170e fixed a similar race condition for dropping the
column type altogether, but it wasn't sufficient to fix the problem
with altering the type, because altering a composite type doesn't
currently lock the type. It does lock the *relation* corresponding to
the type (i.e. pg_type.typrelid), but CREATE TABLE did not hold onto
that lock, even though it acquires it momentarily. To fix, don't
release the lock.
Reviewed-by: Andres Freund <andres@anarazel.de>
Security: CVE-2026-6460
Backpatch-through: 14 M src/backend/catalog/heap.c
A src/test/isolation/expected/type-ddl-locking.out
M src/test/isolation/isolation_schedule
A src/test/isolation/specs/type-ddl-locking.spec
Start to record type dependencies on index expressions et al
commit : d4f8c3e15eff25dde290cfd828dec943e6e9bb6e
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Mon, 9 Nov 2026 06:20:48 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:48 -0800 With this, we can remove find_missing_composite_type_dependencies() in
'master', but I'll do that in a separate commit. This doesn't help
with existing missing dependencies, so we need to keep
find_missing_composite_type_dependencies() in backbranches. It's
still better to record them going forward, though, even in
backbranches.
For all reporters and public discussions, see previous commits.
Security: CVE-2026-6460
Backpatch-through: 14 M src/backend/catalog/dependency.c
M src/backend/commands/tablecmds.c
M src/test/regress/expected/alter_column_type.out
M src/test/regress/expected/privileges.out
Check for missing dependencies on types
commit : b019147c91546d732df09d1f8fbe852871a0c2ea
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Mon, 9 Nov 2026 06:20:48 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:48 -0800 If an expression that's stored in a catalog references a rowtype in a
way that would break ALTERing its attribute, we record a dependency on
the type, and find_composite_type_dependencies() prevents ALTERing it.
However, we have failed to record the dependency in some cases, namely
if the expression itself doesn't depend on the type in an incompatible
way, but produces a *result* of the given datatype which is stored.
That problem arises from index expressions, PARTITION BY expressions,
and from expression statistics.
To fix, scan the pg_index, pg_partitioned_table and pg_statistic_ext
catalogs for those cases as a separate sub-step of
find_composite_type_dependencies(). In the future, after we have
added the missing dependency creation and backfilled any existing
missing dependencies, we can remove this code. That means, in a new
major version.
For all reporters and public discussions, see previous commits.
Co-authored-by: Nathan Bossart <nathan@postgresql.org>
Reviewed-by: Amit Langote <amitlangote09@gmail.com>
Reviewed-by: Andres Freund <andres@anarazel.de>
Security: CVE-2026-6460
Backpatch-through: 14 M src/backend/commands/tablecmds.c
M src/test/regress/expected/alter_column_type.out
M src/test/regress/sql/alter_column_type.sql
Forbid changing type if the rowtype is used in a foreign table
commit : f234ae72be7f115fc825985805e88951d2086d3b
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Mon, 9 Nov 2026 06:20:48 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:48 -0800 Because the foreign table's statistics could have datums in the old
format.
For all reporters and public discussions, see previous commits.
Co-authored-by: Nikhil Sontakke <nikhil@planetscale.com>
Reviewed-by: Amit Langote <amitlangote09@gmail.com>
Reviewed-by: Andres Freund <andres@anarazel.de>
Security: CVE-2026-6460
Backpatch-through: 14 M src/backend/commands/tablecmds.c
Check relation dependencies more carefully
commit : 23a1d88253c136a985e8e969037b28ced41a6b16
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Mon, 9 Nov 2026 06:20:47 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:47 -0800 Similar to previous commits, if a relation depends on a composite
type, we need to inspect it more carefully. We already checked tables
using the composite type in columns, but the dependency might also
exist because a partitioned table's PARTITION BY expression uses the
type, or an expression in an expression or partial index.
For all reporters and public discussions, see previous commits.
Co-authored-by: Nathan Bossart <nathandbossart@gmail.com>
Reviewed-by: Amit Langote <amitlangote09@gmail.com>
Reviewed-by: Andres Freund <andres@anarazel.de>
Security: CVE-2026-6460
Backpatch-through: 14 M src/backend/commands/tablecmds.c
M src/test/regress/expected/alter_column_type.out
M src/test/regress/expected/generated.out
M src/test/regress/sql/alter_column_type.sql
M src/test/regress/sql/generated.sql
Check for Consts in domain DEFAULTs
commit : b65b69f2c16e2be76278184e7615b3273cfbbeaa
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Mon, 9 Nov 2026 06:20:47 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:47 -0800 Similar to previous commits, also look more closely at a type
depending on a composite type. If it's a domain, its DEFAULT
expression might contain a Const that would need to be rewritten if
the composite type was ALTERed.
For all reporters and public discussions, see previous commits.
Co-authored-by: Nathan Bossart <nathandbossart@gmail.com>
Reviewed-by: Amit Langote <amitlangote09@gmail.com>
Reviewed-by: Andres Freund <andres@anarazel.de>
Security: CVE-2026-6460
Backpatch-through: 14 M src/backend/commands/tablecmds.c
M src/test/regress/expected/alter_column_type.out
M src/test/regress/sql/alter_column_type.sql
Check for Consts in constraint definitions
commit : 7e1aeaad34fbede9c23cc9c6dd2f92d4864af514
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Mon, 9 Nov 2026 06:20:47 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:47 -0800 Similar to previous commit, we also need to look more closely at a
dependency from a constraint to a composite type. The constraint's
DEFAULT might contain a Const or other expression that would need to
be rewritten, if the composite type was ALTERed.
For all reporters and public discussions, see previous commits.
Co-authored-by: Nathan Bossart <nathandbossart@gmail.com>
Reviewed-by: Amit Langote <amitlangote09@gmail.com>
Reviewed-by: Andres Freund <andres@anarazel.de>
Security: CVE-2026-6460
Backpatch-through: 14 M src/backend/commands/tablecmds.c
M src/test/regress/expected/alter_column_type.out
M src/test/regress/expected/alter_table.out
M src/test/regress/sql/alter_column_type.sql
M src/test/regress/sql/alter_table.sql
Check for Consts in function DEFAULTs and body
commit : 1e3b37da9f225d911227e55f7553c682298083b8
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Mon, 9 Nov 2026 06:20:47 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:47 -0800 A function that uses a composite type as its return or argument type
doesn't prevent altering the composite type. However, if it's used in
an argument DEFAULT expression, or in the new-style SQL function body,
we must not allow it, because e.g. any Consts in those expressions
would become invalid and we don't have the machinery to rewrite them.
If there's a pg_depend entry on a function, look more closely where
the dependency comes from, and throw an error if it's from those
expressions.
For all reporters and public discussion, see previous commit.
Co-authored-by: Nathan Bossart <nathandbossart@gmail.com>
Reviewed-by: Amit Langote <amitlangote09@gmail.com>
Reviewed-by: Andres Freund <andres@anarazel.de>
Security: CVE-2026-6460
Backpatch-through: 14 M src/backend/catalog/dependency.c
M src/backend/commands/tablecmds.c
M src/include/catalog/dependency.h
M src/test/regress/expected/alter_column_type.out
M src/test/regress/sql/alter_column_type.sql
Forbid ALTERing column type by default when other objects depend on it
commit : 7d089b28c5e0744e14f5cd5f9c5a0f2f00a994a4
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Mon, 9 Nov 2026 06:20:47 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:47 -0800 Previously, we'd allow changing the column type of a table or
composite type (ALTER COLUMN / ATTRIBUTE ... TYPE ...) even if there
are objects depending on the row type. We only made some checks on
dependencies from relation objects, and assumed that dependencies from
other kinds of object were OK. This commit flips the logic around to
forbid if there are any dependencies, except for relations and a few
other cases that are known to be OK.
This commit is mostly refactoring, but some cases that were previously
broken, like expressions in views, are now forbidden. Some of the
dependencies that we continue to allow are known to be broken too --
subsequent commits will tighten them up.
The attributions and discussion links are for the whole wider issue
covered by all the related commits, not just the variants fixed by
this one commit.
Reported-by: Andrew Gierth <andrew@tao11.riddles.org.uk>
Reported-by: Alexander Lakhin <exclusion@gmail.com>
Reported-by: Francesco Verardi <frevadiscor89@gmail.com>
Reported-by: Hcamael <baiyjrh@gmail.com>
Reported-by: Tomer Fichman <tomer@irregular.com>
Reported-by: OpenAI Security Research Team
Reported-by: Peter Geoghegan <pg@bowt.ie>
Reported-by: Mehmet Ince @mdisec <mehmet@mehmetince.net>
Reported-by: Bryan Green <dbryan.green@gmail.com>
Reported-by: Sumedh Pathak <sumedhpathak@microsoft.com>
Reported-by: Nicholas Carlini <npc@anthropic.com>
Reported-by: Sergey Shinderuk <s.shinderuk@postgrespro.ru>
Reported-by: Yuxiao Guo <dllggyx@outlook.com>
Reported-by: Nikhil Sontakke <nikhil@planetscale.com>
Reviewed-by: Andres Freund <andres@anarazel.de>
Reviewed-by: Amit Langote <amitlangote09@gmail.com>
Reviewed-by: Álvaro Herrera <alvherre@kurilemu.de>
Reviewed-by: Robert Haas <robertmhaas@gmail.com>
Reviewed-by: Peter Geoghegan <pg@bowt.ie>
Discussion: https://www.postgresql.org/message-id/CA%2BUBoq0F2ua2fQEAU3-6w0oxOgFc9BgEN4gRcchiUUZ%2BWouVrQ%40mail.gmail.com
Discussion: https://www.postgresql.org/message-id/CA%2BbCEdDvsVApd%2BAADX%3D5uyj6Q5C3r5aruWNzOFaYqaFqLwE_5g%40mail.gmail.com
Security: CVE-2026-6460
Backpatch-through: 14 M src/backend/commands/tablecmds.c
M src/test/regress/expected/alter_column_type.out
M src/test/regress/expected/alter_table.out
M src/test/regress/sql/alter_column_type.sql
M src/test/regress/sql/alter_table.sql
Avoid visiting a type twice in find_composite_type_dependencies()
commit : fbee8e90f7d45f83fb639139e49774b42fc0ec59
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Mon, 9 Nov 2026 06:20:47 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:47 -0800 When recursing, keep track of visited types in a hash table. This is
currently just an insignificant performance optimization, but it will
come handy in the upcoming patches where we can use the hash table to
do expensive catalog scans only once. The upcoming patches will be
backpatched which is why this is backpatched too.
Add a test for the case that a type ultimately depends on the same
composite type via two different paths. That's the case where the
hash table lets us avoid some work. It isn't very likely to be
broken, but let's have coverage for it for the sake of completeness.
Security: CVE-2026-6460
Backpatch-through: 14 M src/backend/commands/tablecmds.c
M src/test/regress/expected/alter_column_type.out
M src/test/regress/sql/alter_column_type.sql
Split find_composite_type_dependencies() into smaller functions
commit : 11658b4df7f1cf47a580fc683256b974dcc66f1e
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Mon, 9 Nov 2026 06:20:47 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:47 -0800 To make it a little easier to see what the upcoming commits change,
move some of the logic from find_composite_type_dependencies() into
subroutines. This is pure refactoring, there is no change in
user-visible behavior.
Security: CVE-2026-6460
Backpatch-through: 14 M src/backend/commands/tablecmds.c
Add tests for find_composite_type_dependencies() bugs
commit : 3e8bb75c864196e4dcb65d494884e1d4be1f197d
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Mon, 9 Nov 2026 06:20:47 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:47 -0800 There are a bunch of bugs in find_composite_type_dependencies(), which
will be fixed by subsequent commits. Usually we include tests in each
commit that fixes a bug, but there are so many variants of these that
it feels easier to add all the tests in one commit first, to show the
big picture. This commit memorizes the incorrect results in the
expected output, marked with FIXME comments to point out that they are
broken. The next commits will fix the issues, adjust the expected
output for the correct behavior, and remove the FIXMEs.
Backpatch-through: 14
Security: CVE-2026-6460 A src/test/regress/expected/alter_column_type.out
M src/test/regress/expected/alter_table.out
M src/test/regress/expected/privileges.out
M src/test/regress/parallel_schedule
A src/test/regress/sql/alter_column_type.sql
M src/test/regress/sql/alter_table.sql
M src/test/regress/sql/privileges.sql
Allow SET LOGGED/UNLOGGED and changing AM when the rowtype is in use
commit : bfcb6625eaae6956cd8cb9c06745ea342c253ac5
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Mon, 9 Nov 2026 06:20:47 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:47 -0800 Those commands rewrite the table, but they don't affect other tables
or Consts using the rowtype.
The reason to do this now is that the next commits will tighten up the
dependency checking to reject more cases. After those commits, we
would also unnecessarily reject SET LOGGED/UNLOGGED in more cases, so
it seems nice to eliminate the unnecessary checks first. That's also
why this is backpatched.
Co-authored-by: Nathan Bossart <nathandbossart@gmail.com>
Security: CVE-2026-6460
Backpatch-through: 14 M src/backend/commands/tablecmds.c
M src/test/regress/expected/alter_table.out
M src/test/regress/sql/alter_table.sql
dblink: Refuse to use a connection that a dblink call is already using.
commit : 33f4232cbe0a20381ed55c1ce850cbdfb7fa1d89
author : Masahiko Sawada <msawada@postgresql.org>
date : Mon, 9 Nov 2026 06:20:47 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:47 -0800 Previously, a dblink query that returned rows kept using its
connection after handing each row to the declared column type's input
function. That input function can run user-defined code (e.g., a
domain type with a CHECK constraint), so SQL can re-enter dblink while
an outer call is still reading results, and close the connection that
call is reading from. The close freed the connection object while the
outer call held a pointer to it, so the next libpq call read and wrote
freed memory.
dblink never had a rule about reentrancy. Calls that issued a new
query failed on their own, but by accident rather than by design:
libpq rejects a new query while one is in progress. It doesn't reject
reading a result, so a reentrant dblink_get_result() consumed a row
out of the outer query's single-row stream.
Fix this by marking a connection while a dblink call is materializing
a result from it, and refusing every other dblink call on that
connection until it's done.
Backpatch to all supported branches.
Reported-by: 안지용 <jfdg1y76@gmail.com>
Reviewed-by: Jacob Champion <champion.p@gmail.com>
Reviewed-by: Greg Burd <greg@burd.me>
Backpatch-through: 14
Security: CVE-2026-120147 M contrib/dblink/dblink.c
M contrib/dblink/expected/dblink.out
M contrib/dblink/sql/dblink.sql
Fix use-after-free in pg_show_all_settings().
commit : 46c7529fa636d40c66cf6e99ffeea243181e6cdc
author : Masahiko Sawada <msawada@postgresql.org>
date : Mon, 9 Nov 2026 06:20:47 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:47 -0800 Commit 3057465acfbe changed show_all_settings(), used by the
pg_show_all_settings() SQL function, to collect pointers to all GUC
entries on its first call and return one row per call from that saved
array. When a module is loaded while the scan is still in progress,
the placeholder entries for that module's prefix are replaced and
freed, while the saved array keeps pointing at them. That caused a
use-after-free. Depending on what reused the freed chunk, the scan
returns stale rows or the backend crashes.
Fix this by using the materialize mode for show_all_settings() so that
the GUC array is consumed before any other expression in the query can
run and free an entry. This is also how the sibling function
show_all_file_settings() already works. We only have a few hundred
GUCs in practice, so materializing them all is acceptable.
Backpatch to v16, where that commit first appeared. On v15 and
earlier show_all_settings() reads each GUC entry by number on every
call and never keeps a pointer across calls, so it is not affected.
Reported-by: Stephan Lipp <Stephan@Adalogics.com>
Reviewed-by: Greg Burd <greg@burd.me>
Backpatch-through: 16
Security: CVE-2026-120152 M src/backend/utils/misc/guc_funcs.c
Reject system catalogs as logical replication targets.
commit : 9532d4c94e90e69f2b9b69d5aa5440509d60cd75
author : Masahiko Sawada <msawada@postgresql.org>
date : Mon, 9 Nov 2026 06:20:47 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:47 -0800 The subscriber relied on the publisher refusing to add a system
catalog to a publication, and checked only the relation kind it was
about to apply into. However, the relation is named by the publisher,
and a publisher that speaks the logical replication protocol doesn't
have to be a PostgreSQL server. A malicious one can name a system
catalog, and the apply worker will write into it.
The danger is limited to subscriptions owned by a superuser. Changes
are applied as the table owner, or as the subscription owner when
run_as_owner is set, and a non-superuser can never hold write
privileges on a system catalog, so a subscription owned by a
non-superuser stops at the privilege check.
We can't depend on the publisher for this, so check the target
relation on the subscriber too. A relation that the server maintains
for its own use, such as a system catalog, must never be written by an
apply worker, and neither must a temporary relation. We don't reject
unlogged relations the way the publisher does; replicating into one is
fine. The check runs when a worker opens the target relation to apply
changes, and also when getting the relation list, so that such a
relation is never recorded in pg_subscription_rel in the first place.
The test attaches a callback of its own to the injection point, which
needs a form of injection_points_attach() that v18 and older don't
have, so it is applied to v19 and later.
Reported-by: qian zhang <ttwzlt@gmail.com>
Author: Masahiko Sawada <sawada.mshk@gmail.com>
Reviewed-by: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Backpatch-through: 14
Security: CVE-2026-120145 M src/backend/commands/subscriptioncmds.c
M src/backend/executor/execReplication.c
M src/backend/replication/logical/relation.c
M src/backend/replication/logical/worker.c
M src/include/executor/executor.h
Reject pseudo-types in extended statistics expressions.
commit : 7f51d62e115503bfc1a9997089aece2935d99185
author : Masahiko Sawada <msawada@postgresql.org>
date : Mon, 9 Nov 2026 06:20:47 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:47 -0800 Previously, CREATE STATISTICS accepted an expression of a pseudo-type.
So the statistics were built on a value whose row type is identified
only by a typmod assigned locally in the backend that ran ANALYZE.
Therefore, other sessions could read the statistics as the wrong data
type, which can crash the server or return wrong results.
Reject such expressions at CREATE STATISTICS time, including a type
that contains a pseudo-type, such as a composite type with an anyarray
column. An expression whose collation can't be determined is now
rejected too, but ANALYZE fails on such statistics anyway.
Since a statistics expression has no column name, teach the column type
check to report an error without one.
A statistics object built this way on an older minor release can still
exist on v18 and older, so in those branches also error out when such
statistics are used by the planner, their contents are read, or ANALYZE
builds them. Those objects should be dropped.
Backpatch to 14, where statistics on expressions were introduced.
Reported-by: Mehmet D. INCE <mehmet@mehmetince.net>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Reviewed-by: Andres Freund <andres@anarazel.de>
Backpatch-through: 14
Security: CVE-2026-120148 M src/backend/catalog/heap.c
M src/backend/commands/statscmds.c
M src/backend/statistics/extended_stats.c
M src/backend/statistics/mcv.c
M src/backend/utils/adt/ruleutils.c
M src/backend/utils/adt/selfuncs.c
M src/test/regress/expected/stats_ext.out
M src/test/regress/sql/stats_ext.sql
Make \restrict keys protective in pg_dump --format=tar restore.sql.
commit : 4d3c1372455c7c493ccbee98dbb07eb334d21c46
author : Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:47 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:47 -0800 pg_dump tar format contains an undocumented restore.sql archive member
that the user can edit according to its embedded comments and then run
with psql. Commit 71ea0d6795438f95f4ee6e35867058c44b270d51 added
\restrict and \unrestrict to fix CVE-2025-8714. That restore.sql always
used "(null)" as the restriction key, so the key did not provide its
intended protection. Also, there was an initial \unrestrict before any
restrict, yielding a spurious error. Given the lack of reports about
that spurious error in over a year, restore.sql may see negligible use.
This defers fixing restore.sql from "pg_dump --format=tar --inserts",
because that relies on "\i" commands. That case is not subject to
attack, but it fails to restore table data. We should eventually fix
"pg_dump --format=tar --inserts" restore.sql or remove restore.sql.
Given this combination with --inserts has been broken to that degree,
again for over a year, the combination must see little use indeed.
Back-patch to v14 (all supported versions).
Reported-by: Pavel Kopylov <pkopylov@cloudlinux.com>
Reviewed-by: Nathan Bossart <nathandbossart@gmail.com> (non-test)
Reviewed-by: Jacob Champion <champion.p@gmail.com> (test)
Reviewed-by: Andres Freund <andres@anarazel.de> (test)
Bug: #19718
Discussion: https://postgr.es/m/19718-7e945d0ff9b5a589@postgresql.org
Backpatch-through: 14
Security: CVE-2026-116330 M doc/src/sgml/ref/pg_dump.sgml
M src/bin/pg_dump/Makefile
M src/bin/pg_dump/meson.build
M src/bin/pg_dump/pg_dump.c
A src/bin/pg_dump/t/011_restore.pl
Reject more unsafe names in extension script substitutions.
commit : 438b435f0810ea550f1650cc7b26177c87b543b7
author : Nathan Bossart <nathan@postgresql.org>
date : Mon, 9 Nov 2026 06:20:47 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:47 -0800 Commit cd5f2a3570 rejected schema and owner names containing
quotes, backslashes, and dollar signs, but names containing
newlines, carriage returns, comment delimiters, or the substitution
tokens themselves can still achieve SQL injection. To fix, reject
those names, too.
Reported-by: Anthropic OSS program
Reported-by: Sven Klemm <sven@tigerdata.com>
Reported-by: Aaron Chen <chenyoulong20g@ict.ac.cn>
Author: Nathan Bossart <nathandbossart@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Greg Burd <greg@burd.me>
Security: CVE-2026-120153
Backpatch-through: 14 M src/backend/commands/extension.c
M src/test/modules/test_extensions/expected/test_extensions.out
M src/test/modules/test_extensions/sql/test_extensions.sql
Avoid out-of-bounds reads in parallel btree scans with array keys.
commit : 4cd18df09917c856b79615dfa23aa4cd5ee7c23d
author : Nathan Bossart <nathan@postgresql.org>
date : Mon, 9 Nov 2026 06:20:47 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:47 -0800 The backends participating in a parallel btree scan with array
keys (e.g., "= ANY (...)" quals) hand their positions in the arrays
back and forth through shared memory, on the assumption that they
all built the same arrays. Nothing verifies that, so a function
mislabeled as PARALLEL SAFE or STABLE can leave the backends with
different arrays, after which a backend might read past the end of
its own array or, if the backends don't even agree on how many
arrays there are, read the saved positions from the wrong place.
To fix, save the number of arrays along with the positions, refuse
to restore anything when it doesn't match, and reject a saved
element number that isn't in range for our own array. v17 saves
only element numbers, so it just needs the range check.
Reported-by: Claude and Ada Logics
Author: Nathan Bossart <nathandbossart@gmail.com>
Reviewed-by: Peter Geoghegan <pg@bowt.ie>
Reviewed-by: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Security: CVE-2026-116333
Backpatch-through: 17 M src/backend/access/nbtree/nbtree.c
Avoid out-of-bounds accesses in FDW batch inserts.
commit : e0bd77667310243f2c6eb60deb3a170fb30d766e
author : Nathan Bossart <nathan@postgresql.org>
date : Mon, 9 Nov 2026 06:20:47 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:47 -0800 Foreign data wrappers are expected to return the number of
successfully inserted tuples via ExecForeignBatchInsert's numSlots
parameter. Both the executor and COPY FROM use that number as the
length of the slot array. If the FDW reports more rows than we
sent, the backend proceeds to read past the end of the array.
(postgres_fdw is acutely affected: if the remote table has an
INSERT rule, the reported count may be higher.) To fix, clamp the
reported count to the number of rows that were sent.
This is a stopgap. Clamping keeps statements that happen to
succeed today succeeding, at the cost of misreporting the number
of rows. A better solution will likely involve reworking the API,
but that is left as a future exercise.
Reported-by: Sumedh Pathak <sumedhpathak@microsoft.com>
Reported-by: Sven Klemm <sven@tigerdata.com>
Author: Nathan Bossart <nathandbossart@gmail.com>
Reviewed-by: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Jacob Champion <champion.p@gmail.com>
Reviewed-by: Tomas Vondra <tomas@vondra.me>
Reviewed-by: Etsuro Fujita <etsuro.fujita@gmail.com>
Security: CVE-2026-120149
Backpatch-through: 14 M contrib/postgres_fdw/expected/postgres_fdw.out
M contrib/postgres_fdw/sql/postgres_fdw.sql
M src/backend/commands/copyfrom.c
M src/backend/executor/nodeModifyTable.c
Prevent creating types in pg_temp from restricted code.
commit : 3f082dcc7b53d40a44752b72268ab1f2413bf011
author : Nathan Bossart <nathan@postgresql.org>
date : Mon, 9 Nov 2026 06:20:47 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:47 -0800 Commit 62aba76568 taught security-restricted operations to refuse
temporary table creation, since the calling session might not
expect untrusted objects to appear at the front of its search
path, but the same hazard exists for types. This oversight
allowed code running within such an operation to create domains,
enum types, and range types in the session's temporary schema,
potentially leading to privilege escalations. To fix, refuse to
create these types in a temporary schema within a
security-restricted operation, as we already do for tables.
The checks live in the command paths rather than in TypeCreate(),
which also creates the rowtypes of the transient relations that
REFRESH MATERIALIZED VIEW CONCURRENTLY and rewrites of temporary
tables legitimately place in pg_temp within a security-restricted
operation. Composite types are already covered by the existing
check in DefineRelation(), and creating base or shell types
requires superuser. Other object kinds are left alone, since
unqualified names never resolve to them in pg_temp.
Reported-by: Anthropic OSS program
Author: Anthropic OSS program
Co-authored-by: Nathan Bossart <nathandbossart@gmail.com>
Reviewed-by: Greg Burd <greg@burd.me>
Security: CVE-2026-116316
Backpatch-through: 14 M src/backend/commands/typecmds.c
M src/test/regress/expected/privileges.out
M src/test/regress/sql/privileges.sql
pg_restore: Fix out-of-bounds accesses and path traversal.
commit : a6dc99b284296ec48190883e737e1a1ebaefddbe
author : Nathan Bossart <nathan@postgresql.org>
date : Mon, 9 Nov 2026 06:20:47 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:47 -0800 pg_restore trusts the contents of the archives it reads, so a
maliciously crafted archive can cause out-of-bounds reads and
writes, or make pg_restore read files outside the archive
directory. This commit fixes the following issues:
* Dependency IDs read from the table of contents are used as array
subscripts without validation. To fix, reject any that is not a
positive integer.
* _getObjectDescription() and IssueACLPerBlob() can read past the
end of a string when an entry's DROP statement or large object
ACL has an unexpected form. To fix, check for the expected
prefix before moving past it.
* A crafted archive can cause the dump ID index to be built before
the table of contents has been read, in which case it is too
small for every later lookup. To fix, have the tar format reject
archives whose table of contents is not the first member.
* The directory format opens the files named in the archive
without inspecting them. To fix, reject names that contain a
directory separator and anything that isn't a regular file.
* A tar member length that does not fit in a pgoff_t can cause a
buffer overrun on 32-bit systems. To fix, reject such lengths.
* In the back-branches, buildTocEntryArrays() sizes the dump ID
index arrays with unchecked multiplication, which could overflow
size_t on 32-bit systems. To fix, use pg_malloc0_array().
A corrupt archive can still crash pg_restore in other ways, but
hardening against that didn't seem appropriate for a security fix,
and therefore it is left as a future exercise.
Reported-by: Dilip Kumar <dilipbalaut@gmail.com>
Reported-by: Yonghwa Lee <underdog@theori.io>
Reported-by: OpenSec <security@opensec.in>
Reported-by: Ben Dixon <uk.bendixon@gmail.com>
Author: Dilip Kumar <dilipbalaut@gmail.com>
Author: Yonghwa Lee <underdog@theori.io>
Co-authored-by: Nathan Bossart <nathandbossart@gmail.com>
Reviewed-by: Noah Misch <noah@leadboat.com>
Reviewed-by: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Security: CVE-2026-116314
Backpatch-through: 14 M src/bin/pg_dump/compress_io.c
M src/bin/pg_dump/pg_backup_archiver.c
M src/bin/pg_dump/pg_backup_db.c
M src/bin/pg_dump/pg_backup_directory.c
M src/bin/pg_dump/pg_backup_tar.c
Check table ownership when DROP OWNED BY changes policies.
commit : 21c732aadf80b20721314cb59a838f9634ab2195
author : Nathan Bossart <nathan@postgresql.org>
date : Mon, 9 Nov 2026 06:20:47 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:47 -0800 DROP OWNED BY removes the given roles from any row-level security
policies that apply to them, but it does not check that the current
user owns the table. This allows a role to remove itself from
restrictive policies on tables it does not own. To fix, leave such
policies unchanged and issue a warning, as is already done for
privileges that the current user cannot revoke.
Reported-by: Zsolt Parragi <zsolt.parragi@percona.com>
Reported-by: Anthropic OSS program
Author: Zsolt Parragi <zsolt.parragi@percona.com>
Co-authored-by: Anthropic OSS program
Reviewed-by: Nathan Bossart <nathandbossart@gmail.com>
Reviewed-by: Sami Imseih <samimseih.pg@gmail.com>
Security: CVE-2026-116311
Backpatch-through: 14 M src/backend/catalog/pg_shdepend.c
M src/backend/commands/policy.c
M src/test/regress/expected/rowsecurity.out
M src/test/regress/sql/rowsecurity.sql
dict_int: Return NULL for zero-length tokens.
commit : b24a45486cdd1458c0d948c71bf0f2432bded9b6
author : Nathan Bossart <nathan@postgresql.org>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 dintdict_lexize() does not check for zero-length tokens before
inspecting its input, so it can read past the end of the token. In
some cases, the returned lexeme can even contain data from well
beyond it. To fix, return NULL for a zero-length token, as
dict_ispell, dict_synonym, and dict_xsyn already do.
Reported-by: Nathan Bossart <nathandbossart@gmail.com>
Reported-by: Anthropic OSS program
Author: Nathan Bossart <nathandbossart@gmail.com>
Co-authored-by: Anthropic OSS program
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Tatsuo Ishii <ishii@postgresql.org>
Security: CVE-2026-116308
Backpatch-through: 14 M contrib/dict_int/dict_int.c
M contrib/dict_int/expected/dict_int.out
M contrib/dict_int/sql/dict_int.sql
Check EXECUTE privilege on foreign key cast functions.
commit : 445e10c5041aa098bed835a3f0812763a4ad50e1
author : Nathan Bossart <nathan@postgresql.org>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 When a foreign key's referencing column must be cast to the type of
the referenced column, the comparison that decides whether an
UPDATE of the referencing table changed the key executes the cast
function without first checking for EXECUTE privileges. To fix,
check privileges and invoke the function execution hook before
calling the cast function. Note that this check is made as the
user issuing the UPDATE, since the comparison happens before any
switch to the referenced table's owner.
Reported-by: Noah Misch <noah@leadboat.com>
Author: Nathan Bossart <nathandbossart@gmail.com>
Security: CVE-2026-116303
Backpatch-through: 14 M src/backend/utils/adt/ri_triggers.c
M src/test/regress/expected/foreign_key.out
M src/test/regress/sql/foreign_key.sql
Check EXECUTE privilege on type-to-json cast functions.
commit : 9520dbea70842cbf357e611c110c38c3427d8692
author : Nathan Bossart <nathan@postgresql.org>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 When converting a value to json or jsonb, to_json() and its many
friends execute the cast function without first checking for
EXECUTE privileges. To fix, check privileges and invoke the
object access hook before calling the cast function. This
covers to_json(), to_jsonb(), row_to_json(), array_to_json(), the
json[b]_build_*() and *_agg() functions, the SQL/JSON constructors
(v16 and later), and COPY TO with FORMAT json (v19 and later).
Reported-by: Filip Janus <fjanus@redhat.com>
Reported-by: Anthropic OSS program
Author: Nathan Bossart <nathandbossart@gmail.com>
Co-authored-by: Anthropic OSS program
Reviewed-by: Andrew Dunstan <andrew@dunslane.net>
Security: CVE-2026-116303
Backpatch-through: 14 M src/backend/utils/adt/json.c
M src/backend/utils/adt/jsonb.c
M src/test/regress/expected/privileges.out
M src/test/regress/sql/privileges.sql
Fix use-after-free issues due to drop of portals being set up
commit : 36f42216eb6135c3de6a12607d49f516c5d0dfad
author : Michael Paquier <michael@paquier.xyz>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 Between CreatePortal() and PortalStart(), a portal can run user-defined
code due to planning and executor startup, like SQL functions. A
function attempting to close a portal would free it, without its creator
knowing about it. This could lead to use-after-free issues when trying
to use portals that have already been closed.
With this change, PortalDrop() now refuses to act on portals marked as
PORTAL_NEW or PORTAL_DEFINED. CLOSE ALL skips them, as it does for
active portals. Note that AtAbort_Portals() (for transaction aborts)
and AtSubAbort_Portals() (for subtransaction aborts) now puts such
portals in a failed state so as they can be cleaned up during error
cleanup.
Two sets of tests are added:
- Portal manipulations during planning and executor startup.
- Portals and Bind messages, relying on injection points to check
various transaction cases with portals marked as either PORTAL_NEW or
PORTAL_DEFINED, where the on-error cleanup of portals marked as failed
matters.
Reported-by: Tomas Vondra <tomas@vondra.me>
Author: Michael Paquier <michael@paquier.xyz>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Backpatch-through: 14
Security: CVE-2026-116332 M src/backend/tcop/postgres.c
M src/backend/utils/mmgr/portalmem.c
M src/test/modules/injection_points/Makefile
A src/test/modules/injection_points/expected/portal_bind.out
M src/test/modules/injection_points/meson.build
A src/test/modules/injection_points/sql/portal_bind.sql
A src/test/regress/expected/portals_inflight.out
M src/test/regress/parallel_schedule
A src/test/regress/sql/portals_inflight.sql
Reject extnamespace if it's temporary or it's '$user'::regnamespace.
commit : 5c751533737eb08c97d6816e43039324e34bad42
author : Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 Historical behavior was bizarre. 'CREATE EXTENSION tcn SCHEMA "$user"'
set extnamespace='$user'::regnamespace, but the extension's object
landed in the schema named after the bootstrap superuser if such a
schema existed, else in pg_temp. Many extension scripts failed, since
pg_temp is searched for only a subset of object types. In other
scenarios, a trusted-extension script located the wrong object and
executed arbitrary SQL of a non-superuser's choice. The prior commit's
locking additions could not cover this. Back-patch to v14 (all
supported versions).
Reported-by: Noah Misch <noah@leadboat.com>
Reviewed-by: Greg Burd <greg@burd.me>
Backpatch-through: 14
Security: CVE-2026-6467 M src/backend/catalog/namespace.c
M src/backend/commands/extension.c
M src/test/modules/test_extensions/expected/test_extensions.out
M src/test/modules/test_extensions/sql/test_extensions.sql
Block ALTER SCHEMA RENAME attack on CREATE EXTENSION etc., via locking.
commit : 79f9793fc44c75d3096ef67677a729ad21a89c2c
author : Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 Commands that run extension SQL scripts intentionally depart from a
standard, "Remove from search_path any schemas that permit untrusted
users to create objects." While CVE-2020-14350 blocked most attacks,
one remained via ALTER SCHEMA extschema RENAME. A user with
database-level CREATE privilege could execute arbitrary code as the
bootstrap superuser. Block that attack by having ALTER SCHEMA RENAME,
CREATE EXTENSION, and ALTER EXTENSION UPDATE lock the schema. This
makes schema rename locking more like that for object types
AlterObjectRename_internal() covers.
Also, make extension-mutating commands take self-exclusive locks. Make
relevant commands take the same lock on each dependency extension. This
doesn't stop a known attack against a specific extension, but
overlapping extension mutations are fragile and not useful.
Back-patch to v14 (all supported versions).
Reported-by: Noah Misch <noah@leadboat.com>
Reviewed-by: Greg Burd <greg@burd.me>
Backpatch-through: 14
Security: CVE-2026-6467 M contrib/ltree/.gitignore
M contrib/ltree/Makefile
A contrib/ltree/expected/schema-rename-attack.out
M contrib/ltree/meson.build
A contrib/ltree/specs/schema-rename-attack.spec
M src/backend/commands/extension.c
M src/backend/commands/schemacmds.c
Fix crash in REINDEX CONCURRENTLY due to DDLs run in index functions
commit : 23e7e2bde0c80df73fc5604a4f9e0f9f2a34ac65
author : Michael Paquier <michael@paquier.xyz>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 ReindexRelationConcurrently() keeps track of the OIDs of the relations
it processes across all its phases, relying on session locks to protect
them. A function called from an index predicate or expression could
drop one of the indexes being processed by a REINDEX CONCURRENTLY,
causing a crash during the swap phase due to a relation gone missing.
The relations being reindexed, on which are taken the session locks, are
now pinned during the build and validation phases so as no manipulation
of their definition can happen in the session running a REINDEX
CONCURRENTLY.
Reported-by: Florent Saudel <fsaudel@protonmail.com>
Author: Michael Paquier <michael@paquier.xyz>
Backpatch-through: 14
Security: CVE-2026-116336 M src/backend/commands/indexcmds.c
M src/test/regress/expected/create_index.out
M src/test/regress/sql/create_index.sql
Check that commutator/negator operators return the same data type.
commit : 22f838a6d5eabb8ca50e0afbb3a09543298733c1
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 The commutator or negator of an operator must surely return the same
data type as the operator itself (always boolean for a negator, but
for a commutator it could be any type). CREATE/ALTER OPERATOR
failed to enforce this, providing a route for evildoers to reach
type-confusion problems.
Another ancient oversight in this feature is that if the first CREATE
OPERATOR names a commutator or negator, but no corresponding clause
appears in the CREATE OPERATOR that fills in the shell operator, we
failed to preserve the shell operator's back-link, resulting in
inconsistent catalog state. That has to be considered part of the
security problem, since it prevents checking whether the two operators
have matching result types. However, fixing it means that an operator
created without any COMMUTATOR or NEGATOR clause at all can acquire a
commutator/negator, if it overwrites a shell operator that links to
such a sibling object. That creates a new security problem: an
attacker could set up a trojan-horse operator that names a
commutator/negator operator that doesn't exist yet, but that the
attacker expects to be created later. Then the attacker has the
potential to inject his own code into uses of that victim operator.
The object_ownercheck() test in OperatorCreate() doesn't prevent
that if the second operator is created by a superuser. To fix this,
we change that test to insist on exact equality of creating user.
(This tightening could potentially break innocent use-cases, except
that it's hard to foresee innocent cases where the two operators
wouldn't be created by the same role. The logic does not prevent
either operator from being reassigned to a new owner later.)
In v16 and earlier, this fix incorporates portions of commit
2b5154bea. Before that, there were yet other scenarios for creating
inconsistent commutator/negator links, potentially allowing type
confusion. I also adopted some of the cosmetic refactoring done in
that commit, just to make pg_operator.c look less different across
branches.
In master, this deletes OperatorUpd() in favor of an expanded function
UpdateOperatorLinks(). In the back branches, OperatorUpd() is kept to
preserve ABI compatibility. I doubt there are any external callers,
but as a one-line wrapper it's cheap enough to not worry about.
Reported-by: Stephan Lipp <Stephan@Adalogics.com>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Backpatch-through: 14
Security: CVE-2026-116335 M src/backend/catalog/pg_operator.c
M src/backend/commands/operatorcmds.c
M src/include/catalog/pg_operator.h
M src/test/regress/expected/alter_operator.out
M src/test/regress/expected/create_operator.out
M src/test/regress/sql/alter_operator.sql
M src/test/regress/sql/create_operator.sql
plperl: Refuse to free a prepared query that is being executed.
commit : 2009539be115a1be8f8c57fac69cba9d95fac7d6
author : Masahiko Sawada <msawada@postgresql.org>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 Previously, spi_freeplan() released a prepared query's plan, and the
memory context holding it, without checking whether the query was
still running. Executing a prepared query can re-enter plperl, and the
re-entered code could free the query being run. That memory context
also holds state the running execution still needs, so freeing it
midway left that execution using freed memory and crashed the backend.
plperl is trusted, so any role able to create a function can reach
this.
Fix this by counting the executions in progress for each prepared
query and refusing spi_freeplan() while that count is above zero. A
count rather than a flag is needed because the same query can be
running more than once at a time.
Backpatch to all supported branches, where spi_freeplan() has always
freed the plan unconditionally.
Reported-by: Zheng Yu <zheng@depthfirst.com>
Reviewed-by: Andrew Dunstan <andrew@dunslane.net>
Backpatch-through: 14
Security: CVE-2026-116307 M src/pl/plperl/expected/plperl.out
M src/pl/plperl/plperl.c
M src/pl/plperl/sql/plperl.sql
Don't leave a stale slot in 'tcs_original_insert_tuple'
commit : a5a82b4d27b0294eb0033f09959d8587120a4ea3
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 When ExecInsert() returns, after a partition-routed insertion, it can
leave the 'mtstate->mt_transition_capture->tcs_original_insert_tuple'
field pointing to the original slot. If ExecARInsertTriggers() or
ExecARUpdateTriggers() is called later for a different tuple, it will
add the stale 'tcs_original_insert_tuple' to the trigger transition
table, instead of the new tuple.
ExecCrossPartitionUpdate() recognized this problem and reset the field
after calling ExecInsert(), but ExecMergeNotMatched() didn't get the
memo, so a MERGE that did an INSERT followed by an UPDATE action would
use the wrong tuple for the trigger transition table on the UPDATE.
There is another near miss in ExecBatchInsert(): it also calls
ExecARInsertTriggers() with the incorrect tuple already set in
'tcs_original_insert_tuple', but because we don't currently support
transition tables with foreign tables, you get an error before that
causes any harm. Similarly, batching in CopyFrom() is not supported
with statement triggers.
The way the TransitionCaptureState->tcs_original_insert_tuple field is
set and reset is error prone, as this bug shows. Its purpose is to
smuggle the original tuple to ExecARInsertTriggers() and
ExecARUpdateTriggers() functions -- it always corresponds to new tuple
that's passed as the 'slot' or 'newslot' argument. That's a pretty
cavalier way of passing an argument, and prone to mistakes. In
'master', make it a normal argument to those functions instead and
remove 'tcs_original_insert_tuple' field altogether. In hindsight, we
should've done that in commit 6973533650c0 already, which removed the
only other field in the struct that had to be set correctly for the
new tuple on each call. In stable branches, to avoid breaking ABI
compatibility, just fix the immediate issue by setting the
'tcs_original_insert_tuple' field closer to the ExecAR*Triggers()
calls that need it, and resetting it immediately afterwards.
In the passing, remove a stale comment in ExecInsert() that talked
about the "active tuple conversion map". That was removed in commit
6973533650c0.
Add a regression test; the existing MERGE transition-table test uses a
non-partitioned target and does not reach this path.
Reported-by: Jacek Bylina <jacek@enveritas.org>
Reported-by: Timothy Ball <timball@enveritas.org>
Reported-by: Claude and Ada Logics
Co-authored-by: Jacek Bylina <jacek@enveritas.org>
Co-authored-by: Jacob Robertson <jacob@enveritas.org>
Co-authored-by: Nigel Gott <nigel@enveritas.org>
Reviewed-by: Amit Langote <amitlangote09@gmail.com>
Backpatch-through: 15
Security: CVE-2026-116312 M src/backend/executor/nodeModifyTable.c
M src/test/regress/expected/triggers.out
M src/test/regress/sql/triggers.sql
find_or_make_matching_shared_tupledesc: Fix possible use-after-free.
commit : a4d40a6ae9f0c0d68a26ea625d50f9a22e0cfc41
author : Robert Haas <rhaas@postgresql.org>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 Previously, the following sequence of events was possible: (1) new
anonymous-record typmod gets entered into shared_typmod_table;
(2) another process resolves the typmod and stores a pointer to the
associated TupleDesc; (3) the first process realizes that the
TupleDesc duplicates one that already has some other typmod assigned
to it and frees the TupleDesc; (4) later, the other process
accesses the now-freed storage, interprets it as a TupleDesc, and
uses it to deform a tuple.
It is likely that an attacker can control the new contents of the
freed memory to a sufficient degree to achieve arbitrary code
execution; hence, this is a security vulnerability.
Reported-by: Peter Geoghegan <pg@bowt.ie>
Reviewed-by: Andrew Dunstan <andrew@dunslane.net>
Security: CVE-2026-116304
Backpatch-through: 14 M src/backend/utils/cache/typcache.c
Don't use MinMaxAggPath if aggregate's input and output types don't match.
commit : dd2cd3803920a923d63380e91f8e0c17725d190c
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 A sort-operator option only makes sense for an aggregate that behaves
like MIN or MAX. We can't really verify whether an aggregate does,
but for our purposes the most important expectation is that the
aggregate returns the same type as its input. Without this, the
planner's conversion to a MinMaxAggPath plan risks type violations.
Since such aggregate definitions might already exist in the wild,
check this during planning and don't generate MinMaxAggPath if the
types don't match.
Reported-by: Zheng Yu <zheng@depthfirst.com>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Backpatch-through: 14-19
Security: CVE-2026-116331 M src/backend/optimizer/plan/planagg.c
Handle composite type changes in ExecEvalFieldStore[De]Form
commit : 02ca67429a264b3b16bf28532404eec4853a579f
author : Tomas Vondra <tomas.vondra@postgresql.org>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 The composite type may be modified during execution, e.g. between
deforming and forming a value, but the executor code ignored that and
could have deformed/formed the tuple with a mismatching descriptor. The
consequences could be severe, including reading/writing memory, etc.
Fixed by remembering the descriptor in ExecInitExprRec(), and using it
when deforming and forming tuples later, instead of looking up a new
descriptor. This is the same approach already used for RowExpr.
This means attributes added after initializing the descriptor will be
treated as NULL, and dropped attributes will be removed from the tuple.
Technically, this is an ABI break. It modifies the ExprEvalStep, which
is part of the ABI. The meaning of one pointer (replacing rowcache with
tupdesc) changes, and ncolumns gets removed. Neither changes the layout
or size of the struct, and so should be backwards compatible. It could
have a bigger impact on custom JIT providers, but we've not found any.
Reported-by: OpenAI Codex Security
Reported-by: Bronson Yen <bronson@calif.io>
Reported-by: Mehmet D. INCE <mehmet@mehmetince.net>
Author: Tomas Vondra <tomas@vondra.me>
Co-Authored-by: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Reviewed-by: Andres Freund <andres@anarazel.de>
Security: CVE-2026-116306
Backpatch-through: 14 M src/backend/executor/execExpr.c
M src/backend/executor/execExprInterp.c
M src/include/executor/execExpr.h
M src/test/regress/expected/rowtypes.out
M src/test/regress/sql/rowtypes.sql
Fix reusing large allocations in generation memory contexts
commit : f34ccffddbe59d313d214d2a584147e735e00219
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 If an allocation larger than 1 GB is made from a generation memory
context, and the allocation is later freed, don't try to reuse the
block for smaller allocations later. The problem is that the standard
layout for a MemoryChunk cannot represent cannot represent an offset
larger than 1 GB (MEMORYCHUNK_MAX_BLOCKOFFSET) from the beginning of
the block, so if MemoryChunk is carved from the very end of the larger
block, the offset stored in the memory chunk overflows. That leads to
memory corruption.
Backpatch to version 15, where the reuse of blocks was introduced,
although the segfault is only reproducable on version 16 onwards.
Version 15 used a custom GenerationChunk struct which didn't have the
overflow risk that the MemoryChunk struct used in later versions has.
Reported-by: Jayden Sarveshkumar <jayden@verialabs.com>
Reviewed-by: Noah Misch <noah@leadboat.com>
Backpatch-through: v15
Security: CVE-2026-116328 M src/backend/utils/mmgr/generation.c
reindexdb: Escape index names in parallel catalog query
commit : 2680b0781ac853736be8c93f85c6f581ef5d805f
author : Michael Paquier <michael@paquier.xyz>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 get_parallel_tabidx_list() builds an array of regclass values, and
incorrectly assigned quotes to the query built, leading to incorrect
queries once the names of the objects included single quotes.
This issue is by embedding each object name inside their own
appendQualifiedRelation() before being attached to the final catalog
query. Some tests are added.
Issue introduced by 47f99a407de2.
Reported-by: Anthropic OSS program
Author: Anthropic OSS program
Reviewed-by: Álvaro Herrera <alvherre@kurilemu.de>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Security: CVE-2026-116325
Backpatch-through: 17 M src/bin/scripts/common.c
M src/bin/scripts/reindexdb.c
M src/bin/scripts/t/090_reindexdb.pl
Treat application of a leaky function to CaseTestExpr/Param as leaky.
commit : 606c23540b9039380d8ef940657bd44bc54eff1c
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 A CaseTestExpr or internal Param represents the value of an expression
somewhere else in the query. If that expression contains a Var then
there is potential for information leakage. Therefore, teach
contain_leaked_vars() to reject such cases.
In principle we could try to find the referenced expression and
perform a more accurate analysis, but that seems not worth the
substantial work required, because in most real-world cases
there'd be a Var there anyway.
Reported-by: Only see myself <1950233439@qq.com>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Backpatch-through: 14
Security: CVE-2026-116327 M src/backend/optimizer/util/clauses.c
M src/backend/optimizer/util/var.c
M src/include/optimizer/optimizer.h
Further harden PL/Perl against tied-scalar mischief.
commit : 6e5524cdad1064d660a9d349649dcb4f6c9d3cc4
author : Andrew Dunstan <andrew@dunslane.net>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 PL/Perl decides whether Perl threw an error by testing SvTRUE(ERRSV).
If the thrown value is an object whose class overloads "bool", that
runs user-supplied Perl code. There is no eval{} in scope at some of
these tests, so if that code throws in turn, Perl writes the message
to stderr and exits the backend, not the behavior we want.
To fix, don't ask Perl at all in the case that matters. Any reference
is true regardless of what its class claims, so a thrown object is an
error by definition, while overloading cannot apply to a non-reference.
Add errsv_is_true() to encapsulate that and replace tests of ERRSV
with that.
Also, consider the possibility that trying to stringify ERRSV throws
an error, which can also be arranged with a hostile object definition.
That risks an infinite loop in PG_PERL_TRY, so install code to prevent
repeating such a loop.
Lastly, stringify ERRSV outside our ereport() calls not inside. If we
do it inside then a thrown Perl error temporarily leaks an errordata[]
slot for the unfinished ereport. While it's not clear that this makes
it possible to overrun ERRORDATA_STACK_SIZE, it seems best to avoid
the leak.
Reported-by: Dwij Mehta <dwijimehta@gmail.com>
Author: Andrew Dunstan <andrew@dunslane.net>
Co-authored-by: Tom Lane <tgl@sss.pgh.pa.us>
Backpatch-through: 14
Security: CVE-2026-116326 M src/pl/plperl/plperl.c
M src/pl/plperl/plperl.h
plperl: don't save and re-use temp context in return_next().
commit : eab8001348eed01401e06b4aa4876817dff005ba
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 Commit ebdefb93b arranged to run plperl_return_next_internal()
in a temporary memory context that got reset at the end of each
call, so as not to accumulate lots of leaked memory in a PL/Perl
function that returns many rows. But this arrangement is unsafe
because re-entrant calls of return_next are possible, as shown
by the new regression test. In that case the inner call's reset
destroys data the outer call still needs, leading to crashes.
Instead, just create and delete a temp context for each call.
That works correctly in the nested case, and in the normal case
it's not really much slower than the old way thanks to the context
recycling logic in aset.c (which long postdates ebdefb93b, but is
in every still-supported branch).
Reported-by: Anthropic OSS program
Author: Anthropic OSS program
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Backpatch-through: 14
Security: CVE-2026-116319 M src/pl/plperl/expected/plperl_tied.out
M src/pl/plperl/plperl.c
M src/pl/plperl/sql/plperl_tied.sql
Hold a reference on a Perl array or hash while scanning it.
commit : d1883981a146996edbf26cb8a5f49a70c1e92e69
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 Previously we assumed that the object would stick around while we
examined it, but there are a few ways that the last refcount could get
dropped meanwhile, allowing the object to be recycled and creating a
use-after-free hazard. To prevent that, we must acquire our own
refcount and release it when we're done. The tricky part is to ensure
that the refcount gets released if an error is thrown partway through.
A TRY/FINALLY block would work, but that answer would be expensive
and notationally invasive. The method used here is to push a
refcount-carrying RV onto Perl's temporaries stack so that temporaries
cleanup will release the reference. A simple benchmark suggests that
that adds just a couple percent of overhead, which seems acceptable.
The first draft of this patch was created by Anthropic's OSS program.
Reported-by: Anthropic OSS program
Author: Tom Lane <tgl@sss.pgh.pa.us>
Co-authored-by: Anthropic OSS program
Backpatch-through: 14
Security: CVE-2026-116320 M contrib/hstore_plperl/hstore_plperl.c
M contrib/jsonb_plperl/jsonb_plperl.c
M src/pl/plperl/plperl.c
Fix PL/Perl to cope with errors thrown from tied structures.
commit : 62a19e1588d617dd03137525e94b63b1edd9c64f
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 An error thrown from a tied array, hash, or scalar may occur while
PL/Perl is trying to return a function value, in which case we are
outside the call_sv() execution loop that would normally handle
trapping of Perl errors. That is problematic: if Perl doesn't think
it is inside an eval-like context, it will bleat on stderr and then
exit(); while if it does think that, it will longjmp out to some outer
level of Perl execution, which wreaks havoc. We need to keep Perl's
jmp_buf stack in sync with Postgres', so that we never have a situation
where one of those stack pointers points above the function level the
other stack was just used to jump out to.
The core idea of this fix is to replace PG_TRY blocks within PL/Perl
with "PG_PERL_TRY" blocks, which have fundamentally the same semantics
as PG_TRY, but establish new jmp_buf levels within both stacks and pop
both correctly at exit. If a Perl error is caught using Perl's stack,
we'll convert it to a Postgres error using logic similar to what
PL/Perl uses for the purpose elsewhere.
This simple core idea has some complications though. One big one
is that since PG_PERL_TRY adjusts the state of the current Perl
interpreter, we must make sure that we don't switch interpreters
partway through the block. Which the old code did freely.
To fix that without adding more TRY blocks than necessary, refactor
the contract between plperl_call_handler and its sub-handlers
(plperl_func_handler et al) so that we look up the target function and
switch to the associated interpreter before we enter the sub-handler.
I also spent a deal of effort on tightening the error-condition
guarantees for select_perl_context and plperl_init_interp, as per the
changes to their header comments. As they stood, it was really hard
to say anything about what state would be left behind after a failure
during Perl interpreter creation, which cascaded into not being able
to promise anything about what would happen in their callers.
Within PG_PERL_TRY, there are more problems. Aside from just pushing
a new jmp_buf stack level (handled by JMPENV_PUSH/JMPENV_POP), we must
see to it that if control does reach Perl_die then it will choose to
longjmp() rather than summarily exit(). At least in Perl 5.40, there
are two gating tests: PL_in_eval must be nonzero (preferably with the
EVAL_INEVAL bit set), and S_dopoptoeval must find a CXt_EVAL context
on the PERL_CONTEXT stack. We could hack PL_in_eval directly, but
there's no direct way to manipulate the context stack. The solution
used here is to execute OP_ENTERTRY and then (if a longjmp didn't
happen) OP_LEAVETRY. That requires manually saving/restoring PL_op,
as well as ERRSV, but otherwise seems to work fairly well.
Another interesting bit is that if possible, we create only one jmp_buf
struct and link it into both error stacks. It'd be cleaner to keep
the two mechanisms entirely separate, but that would require two
sigsetjmp calls in close proximity, which turns out to break clang's
optimizer quite thoroughly. We do get the small benefit of saving the
cycles that would go into a second sigsetjmp call. A complication is
that we need to be able to figure out whether a caught longjmp was
triggered by Perl or Postgres. We do that by adding some state in
elog.c so that we can tell whether the topmost error data is complete
or not. If it isn't, then the error was thrown by Perl. (An example
where this distinction matters is that the sv2cstr calls used in
plperl.c's own ereports could throw Perl errors, at a point where we
are still filling in a Postgres error.) Ideally this state would be
carried by a new flag in ErrorData, but to avoid an ABI break in
stable branches, hide it inside elog.c for now.
However, we can't use that trick on MinGW-64, because there we use
gcc's __builtin_setjmp() while Perl uses MinGW's plain setjmp().
So in that case, resort to managing the jmp_buf stacks separately.
Reported-by: Ngoc Trần <anbas1342@gmail.com>
Reported-by: Anthropic OSS program
Author: Tom Lane <tgl@sss.pgh.pa.us>
Backpatch-through: 14
Security: CVE-2026-116326 M src/backend/utils/error/elog.c
M src/include/c.h
M src/include/utils/elog.h
M src/pl/plperl/expected/plperl_tied.out
M src/pl/plperl/plperl.c
M src/pl/plperl/plperl.h
M src/pl/plperl/sql/plperl_tied.sql
Support tied Perl objects in plperl and associated contrib modules.
commit : 217eb08b45c5e90d3f084a4de42590da229541a3
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 Up to now, if you passed a "tied" object to a PL/Perl operation,
it would most likely get treated as "undef", since we failed to
perform get magic on values we're passed. Tied hashes worked
partially, in that you could read out their keys, but the values
always read as undef.
To fix, use hv_iterval() not HeVAL() to fetch values from perl
hashes, and be careful to apply plperl_materialize_sv() before
testing SvOK() or SvROK() on any perl SV.
Add regression test cases exercising some typical usage scenarios
for tied objects.
This is a back-patch of commit ccab6d36d. The original intent was
for that to be master-only on the grounds that it wasn't worth
the risk to back-patch. However, we now realize that there are
several security issues around tied Perl objects, some of which
are reachable even without this fix. Having to test different
behaviors in master and back branches doesn't seem like a good
plan, so as a first step do this to make the branches more alike.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/569769.1786901901@sss.pgh.pa.us
Backpatch-through: 14-19
Security: CVE-2026-116326 M contrib/hstore_plperl/expected/hstore_plperl.out
M contrib/hstore_plperl/hstore_plperl.c
M contrib/hstore_plperl/sql/hstore_plperl.sql
M contrib/jsonb_plperl/expected/jsonb_plperl.out
M contrib/jsonb_plperl/jsonb_plperl.c
M contrib/jsonb_plperl/sql/jsonb_plperl.sql
M src/pl/plperl/GNUmakefile
A src/pl/plperl/expected/plperl_tied.out
M src/pl/plperl/meson.build
M src/pl/plperl/plperl.c
M src/pl/plperl/plperl.h
A src/pl/plperl/sql/plperl_tied.sql
Avoid use-after-free hazards in re-entrant use of pltcl.
commit : 9d15ca06332471d25e863e6a844879da9742faa9
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 When pltcl invokes a SQL datatype input function, it was passing a
string that is owned by the Tcl interpreter. This is hazardous
since the input function could re-entrantly call the same Tcl
interpreter (for example, via a pltcl function in a domain check
constraint), and that call could modify or free the string we're
depending on. Avoid the hazard by ensuring that we've pstrdup'd
whatever Tcl returned.
In all of these call sites, there's no great need to worry about
pfree'ing the extra string immediately, since we're either inside a
short-lived subtransaction or about to return from the pltcl function.
So I didn't complicate the code by doing that.
Reported-by: Anthropic OSS program
Author: Tom Lane <tgl@sss.pgh.pa.us>
Backpatch-through: 14
Security: CVE-2026-116323 M src/pl/tcl/pltcl.c
Prevent construction of invalid "name" values via element assignment.
commit : 5788eaee3f2efd9cd6f01b27dfb49d96d9f2dd0c
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 Although type "name" is of length NAMEDATALEN, we must not allow the
last byte to become nonzero. Otherwise, operations that treat it as
a C string will read off the end, resulting in crashes or disclosure
of server memory. array_set_element() failed to enforce this.
Another bad outcome of element assignment could be to construct an
invalidly-encoded multibyte character. Check that too while we're
at it.
Ideally we'd extend array_set_element()'s API to pass the array type
OID, but that's infeasible in released branches, and it seems not
worth the trouble even in master given the tiny usage of fixed-length
array types. Instead just test for elmlen == 1 && arraytyplen ==
NAMEDATALEN to decide whether it's a name.
Reported-by: Anthropic OSS program
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Noah Misch <noah@leadboat.com>
Backpatch-through: 14
Security: CVE-2026-116321 M src/backend/utils/adt/arrayfuncs.c
Fix potential buffer overrun in ts_rank_cd().
commit : 7f8424ece5d078becf0c018ef33e3866218de023
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 get_docrep() assumed there could be no more QueryItems per group
than there are lexemes in the input tsvector. That's true with valid
tsvector input, which will never show duplicate positions for any
lexeme. But with duplicate positions it's possible to overrun the
allocated space. Guard against that by dropping items if there are
more than we expect (it doesn't seem worth working harder than that).
The submitted test case that reached this constructed an invalid
tsvector by dint of starting with one, namely a tsvector containing
a zero position, which tsvectorrecv() failed to prevent before
10cc5aa96. It's not clear whether the problem is reachable without
that.
Reported-by: Anthropic OSS program
Author: Anthropic OSS program
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Backpatch-through: 14
Security: CVE-2026-116317 M src/backend/utils/adt/tsrank.c
M src/include/tsearch/ts_type.h
Fix potential buffer overrun in tsvector_delete().
commit : cacc71935952b1e47d852a9e9be3a548fc5ac40e
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 tsvector_delete_by_indices() sized its output as VARSIZE(input) on the
assumption that the result can never be larger, but that only holds
when the input's lexeme data area is laid out in WordEntry order, like
the output's will be. With a different ordering, additional pad bytes
could be needed for alignment of position data. ts_type.h explicitly
says that a different lexeme data ordering is legal, and
tsvectorrecv() for one can produce other orderings. To fix, use the
same solution as tsvector_concat(): allocate one extra byte per
output lexeme. That's probably considerably more than enough, but
it's not worth working harder.
Reported-by: Anthropic OSS program
Author: Anthropic OSS program
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Backpatch-through: 14
Security: CVE-2026-116315 M src/backend/utils/adt/tsvector_op.c
Fix stale attribute pointer in COPY FROM error context.
commit : 3094cdb7d97384eb18587908f10583462a997a44
author : Masahiko Sawada <msawada@postgresql.org>
date : Mon, 9 Nov 2026 06:20:46 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:46 -0800 Previously, cur_attname and cur_attval were left set when ON_ERROR was
'ignore' or 'set_null'. As a result, if an error was raised while
processing the next row, the COPY error context callback could
dereference a pointer that no longer belonged to it. cur_attval isn't
a copy of the field value; it points into attribute_buf, which the
next row resets and, for a longer line, repallocs. The callback
measures the value with strlen() before applying the display limit, so
the limit bounds what we print but not what we read. So depending on
what has reused the allocation, the CONTEXT line reports a value from
the previous row or bytes belonging to something else entirely.
file_fdw reaches the same code through its soft-error retry loop, so
on v18 and later, a SELECT or ANALYZE on such a foreign table hits
this too.
Fix this by making sure to clear both fields before processing the
next column data.
This also corrects the context reported for a REJECT_LIMIT overrun,
which is a statement-level error but was picking up the column and
value left behind by the last skipped row.
Backpatch to v17, where ON_ERROR was introduced.
Reported-by: 안지용 <jfdg1y76@gmail.com>
Backpatch-through: 17
Security: CVE-2026-116310 M src/backend/commands/copyfromparse.c
M src/test/regress/expected/copy2.out
M src/test/regress/sql/copy2.sql
Fix type confusion in PL/Perl's get_perl_array_ref().
commit : dcc32e7d92b7558f2c4e2dfaffbed5ec8ceb51c4
author : Andrew Dunstan <andrew@dunslane.net>
date : Mon, 9 Nov 2026 06:20:45 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:45 -0800 get_perl_array_ref() used sv_isa(sv, "PostgreSQL::InServer::ARRAY") to
recognize PL/Perl's own array wrapper objects, then cast SvRV(sv) to
HV * and looked up the "array" key. But sv_isa() only reports which
package a reference was blessed into, and blessing is an unprivileged
Perl operation that constrains nothing about what is blessed. A
function in trusted plperl could therefore return
my $x = "hi"; return bless \$x, 'PostgreSQL::InServer::ARRAY';
whereupon hv_fetch_string() would read a hash bucket array out of the
body of a plain scalar and dereference it. Since plperl is trusted,
any role with CREATE on a database can reach this.
Fix, per a suggestion from Tom Lane, by requiring SvTYPE(SvRV(sv)) to
be SVt_PVHV before consulting sv_isa(). A reference blessed into that
package but pointing at anything else now falls through to the caller's
existing handling for other references, which looks through the
reference; the example above fails with "malformed array literal". Add
a regression test next to the existing forged-object case.
It's not clear if this is exploitable, but out of an abundance of
caution we treat it as if it is.
Reported-by: Zheng Yu <zheng@depthfirst.com>
Backpatch-through: 14
Security: CVE-2026-116309 M src/pl/plperl/expected/plperl_array.out
M src/pl/plperl/plperl.c
M src/pl/plperl/sql/plperl_array.sql
pg_basebackup: Fix use of externally-controlled format string
commit : 04986ad59d6167d3ed884a2045b99fca8a9733a8
author : Peter Eisentraut <peter@eisentraut.org>
date : Mon, 9 Nov 2026 06:20:45 -0800
committer: Noah Misch <noah@leadboat.com>
date : Mon, 9 Nov 2026 06:20:45 -0800 appendPQExpBuffer() was accidentally used instead of
appendBinaryPQExpBuffer() in pg_basebackup. The former would
interpret the string to be appended as a format string, which could
lead to misbehavior. (The string contents are related to backup
manifest contents. Those do not normally contain any data that could
look like a format placeholders. In order for this to misbehave,
files with unusual names would have to exist in the data directory.)
- The hunk in ReceiveBackupManifestInMemoryChunk() is from commit
0d8c9c1210c, 2020, PG13. This is only reachable against a pre-PG15
server, because of the next item.
- The hunk in ReceiveArchiveStreamChunk() is from commit cc333f32336,
2022, PG15.
Security: CVE-2026-16240
Backpatch-through: 14 M src/bin/pg_basebackup/pg_basebackup.c
Translation updates
commit : f3f9bf05b30e221aa6d8cb741adf670a1962d5f5
author : Peter Eisentraut <peter@eisentraut.org>
date : Mon, 9 Nov 2026 12:20:29 +0100
committer: Peter Eisentraut <peter@eisentraut.org>
date : Mon, 9 Nov 2026 12:20:29 +0100 Source-Git-URL: https://git.postgresql.org/git/pgtranslation/messages.git
Source-Git-Hash: 5ad37e2a22a02d3174730415001146bd511a842a M src/backend/po/LINGUAS
M src/backend/po/de.po
M src/backend/po/ja.po
M src/backend/po/ru.po
M src/backend/po/sv.po
M src/backend/po/zh_CN.po
A src/backend/po/zh_TW.po
M src/bin/initdb/po/de.po
M src/bin/initdb/po/ja.po
M src/bin/initdb/po/sv.po
M src/bin/initdb/po/zh_CN.po
M src/bin/initdb/po/zh_TW.po
M src/bin/pg_amcheck/po/ru.po
M src/bin/pg_amcheck/po/sv.po
M src/bin/pg_amcheck/po/zh_CN.po
M src/bin/pg_amcheck/po/zh_TW.po
M src/bin/pg_archivecleanup/po/LINGUAS
M src/bin/pg_archivecleanup/po/sv.po
M src/bin/pg_archivecleanup/po/zh_CN.po
A src/bin/pg_archivecleanup/po/zh_TW.po
M src/bin/pg_basebackup/po/LINGUAS
M src/bin/pg_basebackup/po/sv.po
M src/bin/pg_basebackup/po/zh_CN.po
A src/bin/pg_basebackup/po/zh_TW.po
M src/bin/pg_checksums/po/sv.po
M src/bin/pg_checksums/po/zh_CN.po
M src/bin/pg_checksums/po/zh_TW.po
M src/bin/pg_combinebackup/po/LINGUAS
M src/bin/pg_combinebackup/po/de.po
M src/bin/pg_combinebackup/po/ru.po
M src/bin/pg_combinebackup/po/sv.po
A src/bin/pg_combinebackup/po/zh_CN.po
A src/bin/pg_combinebackup/po/zh_TW.po
M src/bin/pg_config/po/sv.po
M src/bin/pg_config/po/zh_CN.po
M src/bin/pg_config/po/zh_TW.po
M src/bin/pg_controldata/po/sv.po
M src/bin/pg_controldata/po/zh_CN.po
M src/bin/pg_controldata/po/zh_TW.po
M src/bin/pg_ctl/po/sv.po
M src/bin/pg_ctl/po/zh_CN.po
M src/bin/pg_ctl/po/zh_TW.po
M src/bin/pg_dump/po/de.po
M src/bin/pg_dump/po/ja.po
M src/bin/pg_dump/po/ru.po
M src/bin/pg_dump/po/sv.po
M src/bin/pg_dump/po/zh_CN.po
M src/bin/pg_dump/po/zh_TW.po
M src/bin/pg_resetwal/po/LINGUAS
M src/bin/pg_resetwal/po/ru.po
M src/bin/pg_resetwal/po/sv.po
M src/bin/pg_resetwal/po/zh_CN.po
A src/bin/pg_resetwal/po/zh_TW.po
M src/bin/pg_rewind/po/ru.po
M src/bin/pg_rewind/po/sv.po
M src/bin/pg_rewind/po/zh_CN.po
M src/bin/pg_rewind/po/zh_TW.po
M src/bin/pg_test_fsync/po/ru.po
M src/bin/pg_test_fsync/po/sv.po
M src/bin/pg_test_fsync/po/zh_CN.po
M src/bin/pg_test_fsync/po/zh_TW.po
M src/bin/pg_test_timing/po/sv.po
M src/bin/pg_test_timing/po/zh_CN.po
M src/bin/pg_test_timing/po/zh_TW.po
M src/bin/pg_upgrade/po/LINGUAS
M src/bin/pg_upgrade/po/de.po
M src/bin/pg_upgrade/po/ja.po
M src/bin/pg_upgrade/po/ru.po
M src/bin/pg_upgrade/po/sv.po
M src/bin/pg_upgrade/po/zh_CN.po
A src/bin/pg_upgrade/po/zh_TW.po
M src/bin/pg_verifybackup/po/sv.po
M src/bin/pg_verifybackup/po/zh_CN.po
M src/bin/pg_verifybackup/po/zh_TW.po
M src/bin/pg_waldump/po/ru.po
M src/bin/pg_waldump/po/sv.po
M src/bin/pg_waldump/po/zh_CN.po
M src/bin/pg_waldump/po/zh_TW.po
M src/bin/pg_walsummary/po/LINGUAS
M src/bin/pg_walsummary/po/sv.po
A src/bin/pg_walsummary/po/zh_CN.po
A src/bin/pg_walsummary/po/zh_TW.po
M src/bin/psql/po/de.po
M src/bin/psql/po/ja.po
M src/bin/psql/po/ru.po
M src/bin/psql/po/sv.po
M src/bin/psql/po/zh_CN.po
M src/bin/psql/po/zh_TW.po
M src/bin/scripts/po/LINGUAS
M src/bin/scripts/po/de.po
M src/bin/scripts/po/ja.po
M src/bin/scripts/po/ru.po
M src/bin/scripts/po/sv.po
M src/bin/scripts/po/zh_CN.po
A src/bin/scripts/po/zh_TW.po
M src/interfaces/ecpg/ecpglib/po/de.po
M src/interfaces/ecpg/ecpglib/po/ja.po
M src/interfaces/ecpg/ecpglib/po/ru.po
M src/interfaces/ecpg/ecpglib/po/sv.po
M src/interfaces/ecpg/ecpglib/po/zh_CN.po
M src/interfaces/ecpg/ecpglib/po/zh_TW.po
M src/interfaces/ecpg/preproc/po/zh_CN.po
M src/interfaces/ecpg/preproc/po/zh_TW.po
M src/interfaces/libpq/po/ja.po
M src/interfaces/libpq/po/ru.po
M src/interfaces/libpq/po/sv.po
M src/interfaces/libpq/po/zh_CN.po
M src/interfaces/libpq/po/zh_TW.po
M src/pl/plperl/po/de.po
M src/pl/plperl/po/ru.po
M src/pl/plperl/po/sv.po
M src/pl/plperl/po/zh_CN.po
M src/pl/plperl/po/zh_TW.po
M src/pl/plpgsql/src/po/de.po
M src/pl/plpgsql/src/po/ru.po
M src/pl/plpgsql/src/po/sv.po
M src/pl/plpgsql/src/po/zh_CN.po
M src/pl/plpgsql/src/po/zh_TW.po
M src/pl/plpython/po/sv.po
M src/pl/plpython/po/zh_CN.po
M src/pl/plpython/po/zh_TW.po
M src/pl/tcl/po/ru.po
M src/pl/tcl/po/zh_CN.po
M src/pl/tcl/po/zh_TW.po
Release notes for 18.7, 17.12, 16.16, 15.20, 14.25.
commit : 74194d895e22da2a292598ea321c83b17505da95
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Sun, 8 Nov 2026 16:41:09 -0500
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Sun, 8 Nov 2026 16:41:09 -0500 M doc/src/sgml/release-17.sgml
Exclude the variables in llvmjit_types.c from AddressSanitizer
commit : e6b2f7c2b41d6261dac15825dede55e94ca35614
author : Andres Freund <andres@anarazel.de>
date : Fri, 6 Nov 2026 19:56:22 -0500
committer: Andres Freund <andres@anarazel.de>
date : Fri, 6 Nov 2026 19:56:22 -0500 If address sanitizer is enabled, types of global variables are changed
to a container type, to include space for a red zone after each global
variable. Unfortunately, that causes mismatches with the generated code,
which in turn causes crashes when JIT is combined with ASAN.
This does not normally present a problem with autoconf builds, as we do
not pass CFLAGS on to bitcode generation, so in autoconf builds the
bitcode generation never sees sanitizer flags. Somewhat similarly, in
meson builds, c_args were not passed on to bitcode generation, but that
was changed with 1039352e1 in 17.
For now, just use #pragma to exclude global variables in llvmjit_types.c
from address sanitizer.
That might not be the best fix medium term, but we need a short-term
fix, due to an upcoming fix's tests hitting this issue.
We did not notice this historically as with cirrus-ci address sanitizer
was tested with autoconf, which does not have this specific issue. The
GHA based CI was only added after JIT was disabled by default.
For now this is just backpatched to 17, as earlier branches
a) don't have pg_attribute_no_sanitize_address()
b) don't currently need it, due to 1039352e1 only being in >= 17
Author: Henson Choi <assam258@gmail.com>
Discussion: https://postgr.es/m/CAAAe_zA49cHXJ7J4hmeuSa75s+eSgCOnK_9Uvq6wUEJ2Bgvdfg@mail.gmail.com
Backpatch-through: 17 M src/backend/jit/llvm/llvmjit_types.c
file_fdw: Fix ANALYZE crashes with DEFAULT markers
commit : b2b1910d9ab30bdb30b10c09e8c7813c74216b88
author : Fujii Masao <fujii@postgresql.org>
date : Fri, 6 Nov 2026 23:31:41 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Fri, 6 Nov 2026 23:31:41 +0900 ANALYZE on a file_fdw foreign table failed an assertion when the input
contained a DEFAULT marker, and could crash in builds without assertions.
This happened because the sampling code passed a NULL ExprContext to
NextCopyFrom(), which requires one to evaluate column defaults. Fix
this by providing an expression context for sampling, allowing defaults
to be evaluated without assertion failures or crashes.
Backpatch to v16, which introduced DEFAULT marker support.
Author: Fujii Masao <masao.fujii@gmail.com>
Reviewed-by: Jim Jones <jim.jones@uni-muenster.de>
Reviewed-by: Ayoub Kazar <ayoub.kazar@data-bene.io>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/CAHGQGwGwihFV9FmfSbbrMsEr4fti7aHZFkpyMv5-byjfc2EmOw@mail.gmail.com
Backpatch-through: 16 M contrib/file_fdw/expected/file_fdw.out
M contrib/file_fdw/file_fdw.c
M contrib/file_fdw/sql/file_fdw.sql
JSON_TABLE: fix missing collation assignments
commit : 03550d3cfb941af2cc5312d8b8efcf90b8d8dbec
author : Amit Langote <amitlan@postgresql.org>
date : Fri, 6 Nov 2026 16:18:29 +0900
committer: Amit Langote <amitlan@postgresql.org>
date : Fri, 6 Nov 2026 16:18:29 +0900 transformJsonTable() transforms the context item and the PASSING
arguments into the JsonExpr stored in TableFunc.docexpr, and copies the
transformed PASSING arguments into TableFunc.passingvalexprs, but never
ran assign_expr_collations() over them. That is needed here because
assign_query_collations() doesn't look into the range table, which is
why transformRangeTableFunc() does it for XMLTABLE, as does
transformJsonTableColumn() for JSON_TABLE's own column expressions.
As a result, expressions in there were left without collations. A text
comparison failed with "could not determine which collation to use for
string comparison", even with an explicit COLLATE clause, and a row
comparison was left with an empty inputcollids list, which made the
executor compare zero columns and return the result for equal rows (so
ROW(1, 2) < ROW(3, 4) yielded false), or trip an assertion in
ExecInitExprRec() in assert-enabled builds.
Fix by calling assign_expr_collations() on the transformed JsonExpr,
before the PASSING arguments are copied out of it.
Oversight in commit de3600452b6, so back-patch to 17.
Bug: #19768
Reported-by: Venu Kummari <venu2805k@gmail.com>
Author: Venu Kummari <venu2805k@gmail.com>
Discussion: https://postgr.es/m/19768-3633daa830be95db@postgresql.org
Backpatch-through: 17 M src/backend/parser/parse_jsontable.c
M src/test/regress/expected/sqljson_jsontable.out
M src/test/regress/sql/sqljson_jsontable.sql
Fix crash with single-input aggregates with multiple sort keys
commit : 2a3ac15548c6318a33e954b9858284bc05e62bc8
author : David Rowley <drowley@postgresql.org>
date : Fri, 6 Nov 2026 20:18:18 +1300
committer: David Rowley <drowley@postgresql.org>
date : Fri, 6 Nov 2026 20:18:18 +1300 An aggregate's ORDER BY can list the same sort key more than once with
different sort operators, and transformDistinctClause() copies such
items into the DISTINCT list. So an aggregate with a single input can
have several sort keys and several DISTINCT columns.
nodeAgg.c and execExpr.c mistakenly only checked the number of inputs
when deciding if a Datum sort was possible. That check must also
consider the number of sort keys.
Fix by using the Datum sort only for a single input with a single sort
key.
Also for v16+, fix crash in pre-sorted aggregate code where the uniqslot
was only created when the number of inputs for a DISTINCT aggregate was
above 1. The uniqslot is required when there are multiple DISTINCT
columns, and an ORDER BY clause may cause more than one of these to exist
when there's only a single input column.
Author: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Reviewed-by: David Rowley <dgrowleyml@gmail.com>
Discussion: https://postgr.es/m/CAJTYsWXMuo5QZSy_az+86mty2XdbDw_7uB3C4FjWpFACvD_rzg@mail.gmail.com
Backpatch-through: 14 M src/backend/executor/execExpr.c
M src/backend/executor/nodeAgg.c
M src/test/regress/expected/aggregates.out
M src/test/regress/sql/aggregates.sql
Revert "Fix use-after-free in COPY (ON_ERROR)"
commit : 134dc506ee9bb4698f203236b0e35cb371380eda
author : Michael Paquier <michael@paquier.xyz>
date : Fri, 6 Nov 2026 10:45:11 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Fri, 6 Nov 2026 10:45:11 +0900 This reverts commit 23e2a332dd24. It looks like it is going to need a
little bit more work, and v19 RC2 is close by.
Backpatch-through: 17 M src/backend/commands/copyfromparse.c
M src/test/regress/expected/copy2.out
M src/test/regress/sql/copy2.sql
doc: Improve output_plugin_libraries documentation
commit : 5b80ddaf69027052e19f8cd73e499fb6f8a0cb03
author : Jacob Champion <jchampion@postgresql.org>
date : Thu, 5 Nov 2026 16:54:22 -0800
committer: Jacob Champion <jchampion@postgresql.org>
date : Thu, 5 Nov 2026 16:54:22 -0800 Describe the effects of removing the builtin plugins from
output_plugin_libraries. It may not be obvious to DBAs why they might
want to keep those in the allowlist, since it wasn't previously possible
to pick and choose plugins without modifying the Postgres installation
itself.
Reported-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Reviewed-by: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/CAOYmi%2Bmt5bcV6B0DZR-t1tfpX8wjk8Aj%2BftEd%3DxXuH5qege2%2BQ%40mail.gmail.com
Backpatch-through: 14 M doc/src/sgml/config.sgml
M doc/src/sgml/ref/pg_recvlogical.sgml
Add a check_hook for output_plugin_libraries
commit : 7c78c8cff4df94df1bc1a053ccc105d2868b6a92
author : Jacob Champion <jchampion@postgresql.org>
date : Thu, 5 Nov 2026 16:54:21 -0800
committer: Jacob Champion <jchampion@postgresql.org>
date : Thu, 5 Nov 2026 16:54:21 -0800 I omitted explicit syntax validation of output_plugin_libraries in
226e49cbed. [ALTER SYSTEM] SET does this implicitly due to the parser
grammar, but a DBA could still accidentally put a bad value into
postgresql.conf, and a superuser could do the same with connection
options or set_config(). Logical replication would then fail later on.
This is an annoying papercut for a security option to have, so reject
these cases immediately in a check_hook.
Tests by Hayato Kuroda.
Co-authored-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Reported-by: Fujii Masao <masao.fujii@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/CAOYmi%2BkBsG5rNRb1j2VjgTKg8HY90e7vSvZPLeouiAc6nXP9XA%40mail.gmail.com
Backpatch-through: 14 M src/backend/replication/logical/logical.c
M src/backend/utils/misc/guc_tables.c
M src/include/utils/guc_hooks.h
M src/test/regress/expected/guc.out
M src/test/regress/sql/guc.sql
Fix use-after-free in COPY (ON_ERROR)
commit : 61b06d2ba61d04398585d6d06b1ee5f4cd53c3b5
author : Michael Paquier <michael@paquier.xyz>
date : Fri, 6 Nov 2026 09:18:42 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Fri, 6 Nov 2026 09:18:42 +0900 When ON_ERROR handled a soft error, some of the contents set for the
error context callback could remain set and pointing at areas that could
be reset, leading to some use-after-frees depending on the input data
provided through COPY.
These contents are now reset at the beginning of NextCopyFrom(), before
processing the next tuple.
SET_NULL is affected down to v19, IGNORE down to v17.
Author: Gowdham M <gowdham.m@zohocorp.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Reviewed-by: Shihao Zhong <zhong950419@gmail.com>
Discussion: https://postgr.es/m/1a181ff6be8.6d14feb261368.4036709598649280477@zohocorp.com
Backpatch-through: 17 M src/backend/commands/copyfromparse.c
M src/test/regress/expected/copy2.out
M src/test/regress/sql/copy2.sql
Handle numeric overflow when handling range values
commit : dfd98ce58eae35ae264ccd3ca210a3d574ac5927
author : Tomas Vondra <tomas.vondra@postgresql.org>
date : Thu, 5 Nov 2026 13:00:43 +0100
committer: Tomas Vondra <tomas.vondra@postgresql.org>
date : Thu, 5 Nov 2026 13:00:43 +0100 The numrange_subdiff() function was calculating the difference simply by
calling numeric_sub and numeric_float8, but those functions are not
overflow-safe. It's possible to insert valid numeric ranges for which
the difference overflows either numeric or float8.
The overflow may affect various operations on numrange columns. It may
cause failures during ANALYZE on the table, inserts into GiST indexes,
selectivity estimation, and possibly other operations.
Fixed by handling overflows in numrange_subdiff() - extreme differences
are treated as infinity.
Author: Kirill Reshke <reshkekirill@gmail.com>
Reviewed-by: shihao zhong <zhong950419@gmail.com>
Backpatch-through: 14
Discussion: https://postgr.es/m/CALdSSPgBvXz-VSF0p-5k%3D_uFfN38cWrERmm_AC4K67N6MWQhmg%40mail.gmail.com M src/backend/utils/adt/rangetypes.c
M src/test/regress/expected/multirangetypes.out
M src/test/regress/sql/multirangetypes.sql
Fix stale visibility maps in incremental backups after heap truncation
commit : 0733c72198c9d49b239703a01d9fb5ecba7d04fb
author : Fujii Masao <fujii@postgresql.org>
date : Thu, 5 Nov 2026 18:07:48 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Thu, 5 Nov 2026 18:07:48 +0900 An incremental backup taken after VACUUM truncates a table can restore
stale visibility map bits when both data checksums and wal_log_hints are
disabled. If the restored table grows again, the stale bits can cause
VACUUM to skip required freezing and advance relfrozenxid past unfrozen
tuples, or cause index-only scans to return deleted rows.
Heap truncation clears bits for removed heap pages in the last remaining
VM page. With both settings disabled, that page is not separately
WAL-logged. The WAL summarizer records the VM truncation limit but does
not mark the remaining page as modified, allowing incremental backups
to reuse its old contents. The truncation record precedes the incremental
backup's start LSN, so recovery does not replay it to clear those bits.
Fix this by marking the last remaining VM page as modified when
summarizing a truncation record, so incremental backups include its
current contents. Do this conservatively even when truncation falls on
a VM page boundary and the remaining page does not need to change.
Note that existing WAL summaries are not regenerated by this fix.
Incremental backups taken after updating can therefore still be affected
if they use summaries generated before the fix. Take a new full backup
from an uncorrupted server after updating and use it as the basis for
subsequent incremental backups. This does not repair existing backups
or corruption already present in a restored database.
Backpatch to PostgreSQL 17, where incremental backup was introduced.
Bug: #19775
Author: Ke <kehan5800@gmail.com>
Co-authored-by: Fujii Masao <masao.fujii@gmail.com>
Co-authored-by: Shihao Zhong <zhong950419@gmail.com>
Discussion: https://postgr.es/m/19775-087aebfa1a709b09@postgresql.org
Discussion: https://postgr.es/m/CAGRkXqSnghF+wbyP-PdfjVLfbejJG3cvZd9hN-gxsvCWj2F64Q@mail.gmail.com
Backpatch-through: 17 M src/backend/postmaster/walsummarizer.c
M src/bin/pg_combinebackup/meson.build
A src/bin/pg_combinebackup/t/014_vm_truncation.pl
SQL/JSON: Fix NULL result leakage in JSON_VALUE() RETURNING json/jsonb
commit : 31af62f380c658301061f484e94151f1ef71b013
author : Amit Langote <amitlan@postgresql.org>
date : Thu, 5 Nov 2026 12:24:43 +0900
committer: Amit Langote <amitlan@postgresql.org>
date : Thu, 5 Nov 2026 12:24:43 +0900 Once JSON_VALUE(... RETURNING json/jsonb) returned NULL, every later
evaluation of the same expression in the same statement also returned
NULL, even for inputs that had a valid value.
In ExecEvalJsonExprPath(), the JSON_VALUE_OP branch for RETURNING json
or jsonb converts the matched item to its string form but, unlike the
sibling branches, never sets *op->resnull to false. So a NULL result
from a previous evaluation, whether due to a JSON null item, an empty
match, or an error handled by NULL ON ERROR, remained in effect. That
caused the subsequent coercion via the type's input function, which is
guarded by that flag, to be skipped, and the valid item to be returned
as SQL NULL. The result thus depended on what earlier rows, or earlier
expressions sharing the same ExprState, had returned.
Fix by setting *op->resnull to false in that branch. Also add a
comment noting that ExecGetJsonValueItemString() takes care of that
in the final branch.
Oversight in commit 6185c9737cf, so back-patch to 17.
Bug: #19695
Reported-by: Chaitanya Choudhary <chaitanyyachoudhary@gmail.com>
Author: Srinath Reddy Sadipiralla <srinath2133@gmail.com>
Reviewed-by: Manu <manuelreyesbravo@gmail.com>
Discussion: https://postgr.es/m/19695-2f2c573ef1660737@postgresql.org
Backpatch-through: 17 M src/backend/executor/execExprInterp.c
M src/test/regress/expected/sqljson_queryfuncs.out
M src/test/regress/sql/sqljson_queryfuncs.sql
SQL/JSON: Fix ON EMPTY/ERROR state leakage on NULL input
commit : 775e03191fa663b523363e6fa1eb7c3f1d565350
author : Amit Langote <amitlan@postgresql.org>
date : Thu, 5 Nov 2026 08:38:02 +0900
committer: Amit Langote <amitlan@postgresql.org>
date : Thu, 5 Nov 2026 08:38:02 +0900 ExecInitJsonExpr() emitted JUMP_IF_NULL steps to skip the
EEOP_JSONEXPR_PATH step when either the context item or the path
expression was NULL, landing on a CONST step that returned NULL.
However, ExecEvalJsonExprPath(), which implements EEOP_JSONEXPR_PATH,
was also the only place where JsonExprState's empty and error flags
got reset for a new row. Skipping it left the flags as the previous
row had set them, and since execution then fell through the
ON EMPTY / ON ERROR steps that check those flags, a NULL-input row
returned the previous row's ON EMPTY / ON ERROR expression instead of
NULL, making the result depend on the order of the input rows.
This could only happen when ExecInitJsonExpr() had emitted the
ON EMPTY / ON ERROR steps for the NULL-input row to fall through,
which it does unless the corresponding expression is a NULL constant
and the RETURNING type is not a domain. So JSON_VALUE and JSON_QUERY
with their default NULL ON EMPTY / NULL ON ERROR behaviors were not
affected, but JSON_EXISTS, whose default is FALSE ON ERROR, was.
Fix by pointing those JUMP_IF_NULL steps at EEOP_JSONEXPR_PATH itself
and dropping the CONST NULL step. ExecEvalJsonExprPath() now returns
NULL for NULL input after resetting the flags, skipping jsonpath
evaluation and the ON EMPTY / ON ERROR steps as before, but still
jumping to the coercion steps when RETURNING a domain type so that its
constraints are checked.
Oversight in commit 6185c9737cf, so back-patch to 17.
Bug: #19621
Reported-by: Suyang Zhong <syzhong16@gmail.com>
Author: jian he <jian.universality@gmail.com>
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Reviewed-by: Srinath Reddy Sadipiralla <srinath2133@gmail.com>
Tested-by: Manu <manuelreyesbravo@gmail.com>
Discussion: https://postgr.es/m/19621-0a480d2dc74e6bd5@postgresql.org
Backpatch-through: 17 M src/backend/executor/execExpr.c
M src/backend/executor/execExprInterp.c
M src/test/regress/expected/sqljson_queryfuncs.out
M src/test/regress/sql/sqljson_queryfuncs.sql
Fix multi-row INSERT with a sublink in a target column subscript
commit : f116737684c28526e90f262098027fb7ddb518cd
author : Amit Langote <amitlan@postgresql.org>
date : Thu, 5 Nov 2026 08:16:20 +0900
committer: Amit Langote <amitlan@postgresql.org>
date : Thu, 5 Nov 2026 08:16:20 +0900 For INSERT ... VALUES with more than one row, transformInsertStmt()
calls transformInsertRow() once for each VALUES row and once more for
the final targetlist, and each call transformed the same raw
indirection from the target column list. That doesn't work for
sublinks, because transformSubLink() replaces the raw subselect with
its analyzed Query, so the second pass failed with "unexpected
non-SELECT command in SubLink". Other nodes that are transformed in
place, such as NullTest, failed with "unrecognized node type".
Fix by giving each transformation its own copy of the indirection.
Claude found this issue when reviewing another bug.
Discussion: https://postgr.es/m/CA+HiwqHCZLZV_20nz3fQX+1=vyFLDMbG0_3wY=QujNKZWts0nw@mail.gmail.com
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Shihao Zhong <zhong950419@gmail.com>
Backpatch-through: 14 M src/backend/parser/analyze.c
M src/test/regress/expected/insert.out
M src/test/regress/sql/insert.sql
Fix accidental ignoring of subsequent keys in Sort nodes
commit : 9171fca990e668a0fb50008232722e835878cdd1
author : David Rowley <drowley@postgresql.org>
date : Thu, 5 Nov 2026 10:21:17 +1300
committer: David Rowley <drowley@postgresql.org>
date : Thu, 5 Nov 2026 10:21:17 +1300 91e9e89dc (v15) changed nodeSort.c to allow the slightly more efficient
Datum sort method to be used when the Sort node was only outputting a
single column. That commit mistakenly failed to consider additional
sort keys.
This wasn't trivial to hit as adding more sort keys on another column
would add a resjunk target entry, so Datum sort wouldn't be used. Hitting
this required all sort keys to be on the only output column, and then you
might think that subsequent sort keys *could* be ignored in that case, but
the collation for subsequent keys might differ from the first, and if the
first sort key had a nondeterministic collation, ignoring subsequent keys
isn't correct.
Ordinary ORDER BY queries were not affected by this as repeating the same
column in the ORDER BY clause with different collations would lead to
additional redundant target entries and would have failed the one-column
requirement to enable the Datum sort optimization. The only known case
where problems could occur was sorting for the presorted aggregate
optimization added in 1349d2790 (v16).
A query such as the following would trigger this issue if the "x" column
was defined with a nondeterministic collation as the presorted input
would arrive at the aggregate only presorted by 'x' (using its
nondeterministic collation), rather than 'x, x COLLATE "C"':
SELECT count(DISTINCT x COLLATE "C") FROM t GROUP BY x;
This could lead to COUNT(DISTINCT) returning a count higher than the actual
number of distinct values according to the "C" collation.
Fix this by only using the Datum sort optimization when there's 1 output
column and 1 sort key, rather than just checking for 1 output column.
Reported-by: Nidal FAKIH <nidal.fakih@soptimal.com>
Author: David Rowley <dgrowleyml@gmail.com>
Discussion: https://postgr.es/m/c6790a7b-7c10-4baf-9c8f-b404420c63f9@soptimal.com
Backpatch-through: 15 M src/backend/executor/nodeSort.c
Fix ExecCheckIndexConstraints() crash when es_snapshot is invalid.
commit : 0d83f9e0ee04aeb60b846e3c65437ace4ffd1d5c
author : Dean Rasheed <dean.a.rasheed@gmail.com>
date : Wed, 4 Nov 2026 19:45:42 +0000
committer: Dean Rasheed <dean.a.rasheed@gmail.com>
date : Wed, 4 Nov 2026 19:45:42 +0000 At transaction level SERIALIZABLE, commit fb60892f403 made
check_exclusion_or_unique_constraint() read the conflicting tuple
again using estate->es_snapshot when violationOK is true and
conflictTid is non-NULL, to ensure that INSERT ... ON CONFLICT takes
an SIREAD lock. This can only happen from ExecCheckIndexConstraints(),
but not all callers of ExecCheckIndexConstraints() initialize
estate->es_snapshot, and the re-fetch crashes if it's invalid.
In core Postgres, this only affects the logical replication apply
worker which, starting from v18, uses ExecCheckIndexConstraints() to
log conflicts while applying changes, without initializing es_snapshot
in the EState that it builds. However, there is also extension code
that does the same, so the problem affects all versions that
fb60892f403 was applied to.
Fix by skipping the re-fetch in check_exclusion_or_unique_constraint()
if the snapshot is invalid.
Reported-by: Zsolt Parragi <zsolt.parragi@percona.com>
Author: Zsolt Parragi <zsolt.parragi@percona.com>
Reviewed-by: Andrey Borodin <x4mmm@yandex-team.ru>
Discussion: https://postgr.es/m/CAN4CZFPz4ysPyo1ahD-5n0iyKxmuo2Wvn%3DPgEZTyW63_Tu7Eaw%40mail.gmail.com
Backpatch-through: 14 M src/backend/executor/execIndexing.c
Fix comment in ExecAppendAsyncReset.
commit : 3676c8e0de125a963ee1e82bf01e0da56ba17556
author : Etsuro Fujita <efujita@postgresql.org>
date : Wed, 4 Nov 2026 18:30:04 +0900
committer: Etsuro Fujita <efujita@postgresql.org>
date : Wed, 4 Nov 2026 18:30:04 +0900 We call ExecAppendAsyncEventWait with as_syncdone=true, which forces it
to wait until at least one event occurs, ensuring it never polls.
Oversight in commit f57df2068.
Author: Etsuro Fujita <etsuro.fujita@gmail.com>
Reviewed-by: Chao Li <lic@highgo.com>
Discussion: https://postgr.es/m/CAPmGK1648ovA1i_4X%2BvO%3DCi%2BkSpvvG0H9jcb2m4inybXtSyw6A%40mail.gmail.com
Backpatch-through: 14 M src/backend/executor/nodeAppend.c
Don't free a cached RI plan that may be in use
commit : f3ea7cb465a765146133046ef02f5cea3341c6b4
author : Amit Langote <amitlan@postgresql.org>
date : Wed, 4 Nov 2026 14:50:52 +0900
committer: Amit Langote <amitlan@postgresql.org>
date : Wed, 4 Nov 2026 14:50:52 +0900 Currently, ri_FetchPreparedPlan() immediately frees a cached SPI plan
if SPI_plan_is_valid() returns false, without tracking whether the
plan might be in use. That can cause _SPI_execute_plan() to read
freed memory. This happens, for example, when an RI check's or
action's query runs some user-defined code such as a cast function or
BEFORE ROW trigger function which may perform its own DML using its
own query that is not restricted from firing its own triggers, some
of which may be RI triggers for the same constraint as the outer RI
check and hence use the same cached plan. If an invalidation arrives
that marks the cached plans referenced by the outer check's SPI plan
invalid between the outer check having called _SPI_execute_plan() and
the inner check's ri_FetchPreparedPlan(), the latter will free the
SPI plan as mentioned above.
Lucas Jeffrey reported an instance of this bug involving a
self-referencing ON DELETE CASCADE foreign key. The RI action's
DELETE fires a BEFORE DELETE trigger on the rows it deletes, which
invalidates the action's plan with GRANT and REVOKE on the table and
then runs a DELETE of its own, and the RI action fired by that DELETE
is the inner check described above. Separately, Tom Lane reported
that buildfarm member trilobite, which runs with CLOBBER_CACHE_ALWAYS,
hit this in the regression tests on master after a change there made
more FK checks go through SPI.
Fix by keeping a stack of the plans that every ri_PerformCheck() call
in progress is executing, pushed before SPI_execute_snapshot() and
popped whether it returns or throws an error, the latter using
PG_FINALLY(). ri_FetchPreparedPlan() now frees an invalid plan on the
spot when it is not on that stack, as before, since nothing can be
executing it. Otherwise it only detaches the plan from its hash entry
and queues it on a list. Whenever a check finishes, the queued plans
that are no longer on the stack are freed. So a plan is held only
while a check is actually executing it, however many invalidations
happen meanwhile. A new AtEOXact_RI() frees whatever is left.
Add the test from Lucas's original patch, which reproduces the crash
on an ordinary cassert build, and a test that raises errors from
inside a check's query, some of them inside subtransactions that are
then rolled back, to check that the stack stays exact.
Author: Lucas Jeffrey <lucas.jeffrey@anachronics.com>
Co-authored-by: Trakshan Mishra <trakshanmishra477@gmail.com>
Co-authored-by: Amit Langote <amitlangote09@gmail.com>
Reported-by: Lucas Jeffrey <lucas.jeffrey@anachronics.com>
Reported-by: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Tomas Vondra <tomas@vondra.me>
Discussion: https://postgr.es/m/3043944.1792163844@sss.pgh.pa.us
Discussion: https://postgr.es/m/CAGHzy7S0XfXdK3amd5c4QhpS1o9dySVzPr+-d9ohaE4iV_vNog@mail.gmail.com
Backpatch-through: 14 M src/backend/access/transam/xact.c
M src/backend/utils/adt/ri_triggers.c
M src/include/commands/trigger.h
M src/test/regress/expected/foreign_key.out
M src/test/regress/sql/foreign_key.sql
Fix incorrect Assert in add_foreign_ordered_paths
commit : edbb2813746de72e0a7ae20bd7ebe5b37fb78b23
author : David Rowley <drowley@postgresql.org>
date : Wed, 4 Nov 2026 15:56:46 +1300
committer: David Rowley <drowley@postgresql.org>
date : Wed, 4 Nov 2026 15:56:46 +1300 12933dc60 (originally 66c0185a3) added setop_pathkeys for providing
presorted inputs for set operation from child queries when that query
doesn't have any other pathkey requirements.
add_foreign_ordered_paths() assumed that if the query had an ORDER BY
that the query_pathkeys would be equal to sort_pathkeys. That's not
true when query_pathkeys is set to setop_pathkeys, which can happen
for set-op child queries with an ORDER BY when all the sort_pathkeys are
redundant pathkeys. (See standard_qp_callback()).
Here we relax the Assert to allow for sort_pathkeys to be empty. In
this case it is technically ok to mark pushdown_safe as true so that
postgresGetForeignUpperPaths() allows add_foreign_final_paths() to
generate paths with the LIMIT clause pushed down. We don't adjust that
here as that could cause plan changes in the backbranches. Let's just
fix things enough so the Assert doesn't fail and do a proper fix in
master.
Backpatch to 17 where the setop_pathkeys field was added.
Reported-by: Robins Tharakan <tharakan@gmail.com>
Author: David Rowley <dgrowleyml@gmail.com>
Reviewed-by: Robins Tharakan <tharakan@gmail.com>
Discussion: https://postgr.es/m/CAEP4nAwnSefmi4w+89XbHQ0f-0OixCYUJo9kVcbXSYL8L4rEPg@mail.gmail.com
Backpatch-through: 17 M contrib/postgres_fdw/expected/postgres_fdw.out
M contrib/postgres_fdw/postgres_fdw.c
M contrib/postgres_fdw/sql/postgres_fdw.sql
Fix possible index corruption in pg_trgm GiST indexes
commit : 145a7175ee0abf9c0894662f34a42988e5ea6276
author : Fujii Masao <fujii@postgresql.org>
date : Wed, 4 Nov 2026 11:18:49 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Wed, 4 Nov 2026 11:18:49 +0900 Commit 911e7020770, which added configurable signature lengths,
introduced an error in how gist_trgm_ops index entries are combined.
Some entries could lose the flag identifying their format, causing
later inserts to treat them as empty and lose information about
existing entries. As a result, queries using these indexes could
miss matching rows.
Fix this by preserving the format flag when combining index entries,
so that subsequent inserts correctly retain the information they
represent.
This fix does not repair existing indexes. Users should REINDEX all
existing gist_trgm_ops indexes after upgrading to a release containing
this fix.
Backpatch to all supported versions.
Author: Nate Clark <natec425@gmail.com>
Reviewed-by: Kirill Reshke <reshkekirill@gmail.com>
Reviewed-by: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/19720-b3e83f5e99c485c5@postgresql.org
Backpatch-through: 14 M contrib/pg_trgm/trgm_gist.c
Fix memory leaks when freeing WAL readers
commit : aa6f14da4b6f796d94f78959be2eb8deb05152c6
author : Fujii Masao <fujii@postgresql.org>
date : Wed, 4 Nov 2026 11:16:44 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Wed, 4 Nov 2026 11:16:44 +0900 Commit 3f1ce97346 introduced a circular buffer for decoded WAL records,
with separate allocations for records that do not fit in the buffer.
However, XLogReaderFree() did not release separately allocated
records remaining in the decode queue. In particular, the last record
returned by XLogReadRecord() remains queued until released, so freeing
the reader could leak that record.
For many callers, the leaked memory is reclaimed when the containing
memory context is reset or deleted. However, it can accumulate in
long-lived contexts. For example, when checkpointing prepared
transactions, XlogReadTwoPhaseData() creates a WAL reader, reads the
PREPARE record, and frees the reader. Large PREPARE records can
therefore leave allocations in checkpointer_context, which is not
reset during normal operation. These allocations accumulate until
an error resets the context or the checkpointer exits.
Fix this by calling ResetDecoder() in XLogReaderFree() to release all
separately allocated records in the queue. Do this before freeing the
decode buffer, since the queue can also contain records stored in that
buffer.
Backpatch to v15, where the circular WAL decoding buffer was
introduced.
Author: Tomás Senart <ts@perfloop.ai>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/CAEEBa3Uai8TGPd6P5SX_fzbu3KHMwSvu9WuqA8JPSPQ1RJR8fQ@mail.gmail.com
Backpatch-through: 15 M src/backend/access/transam/xlogreader.c
Fix Memoize to correctly use clause's input collation
commit : f79bf8d8f3272e6d4d3dd14f5ffad7c31f017e49
author : David Rowley <drowley@postgresql.org>
date : Wed, 4 Nov 2026 13:43:17 +1300
committer: David Rowley <drowley@postgresql.org>
date : Wed, 4 Nov 2026 13:43:17 +1300 When building Memoize cache keys from a parameterized path's join clauses,
paraminfo_get_equal_hashops() used the outer side expression of each
clause verbatim, which meant the cache was hashed and probed using that
expression's collation rather than the input collation of the join
condition.
If the expression's collation is nondeterministic and the clause compares
using a different collation, values that the clause can distinguish, such
as 'abc' and 'ABC' under a case-insensitive collation, could be treated as
equal by the cache. This could cause a lookup to return the cached
results for another value, giving wrong query results.
Here we fix this by relabeling the cache key expression with the join
clause's input collation, so that cache lookups match values the same way
as the join clause does.
Author: shihao zhong <zhong950419@gmail.com>
Reviewed-by: Manu <manuelreyesbravo@gmail.com>
Reviewed-by: David Rowley <dgrowleyml@gmail.com>
Discussion: https://postgr.es/m/CAGRkXqTBsBegZ8rNMYjwkeu1V5wRQ06zR6jTVickqP_3E7mb8g@mail.gmail.com
Backpatch-through: 14 M src/backend/optimizer/path/joinpath.c
M src/test/regress/expected/collate.icu.utf8.out
M src/test/regress/sql/collate.icu.utf8.sql
doc: Match built-in function parameter names to their SQL names
commit : b6090b039be788f4258662f3a427a58238277f56
author : Fujii Masao <fujii@postgresql.org>
date : Tue, 3 Nov 2026 15:03:52 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Tue, 3 Nov 2026 15:03:52 +0900 Several documented function signatures used parameter names that
differed from the registered SQL names, causing named-argument calls
using the documented names to fail. Update the signatures and
corresponding descriptions to use the SQL-visible names, without
affecting existing calls.
Backpatch to all supported versions, except for the changes to
pg_logical_emit_message and pg_input_error_info, which are
backpatched through v17 and v16, respectively.
Reported-by: Mi cro <micro6947@gmail.com>
Author: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/19D9D4CB-8C31-493E-982A-1E17042D68C5%40gmail.com
Backpatch-through: 14 M doc/src/sgml/func.sgml
M doc/src/sgml/textsearch.sgml
doc: Fix column name in routine_sequence_usage documentation
commit : 1522acabb7d342a6629f6c734a5f03cbeb263382
author : Fujii Masao <fujii@postgresql.org>
date : Tue, 3 Nov 2026 14:58:56 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Tue, 3 Nov 2026 14:58:56 +0900 The sequence catalog column of information_schema.routine_sequence_usage
is named sequence_catalog in the view definition, but the documentation
previously called it schema_catalog. Correct the documented column name
to match the view.
Backpatch to all supported versions.
Reported-by: Mi cro <micro6947@gmail.com>
Author: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/179342977864.1246.4971529844925475150@wrigleys.postgresql.org
Backpatch-through: 14 M doc/src/sgml/information_schema.sgml
Reject incremental backups based on a backup from a divergent history
commit : ecb43083fe34ebd2b00263c90dcf1ffc63b4c2a4
author : Fujii Masao <fujii@postgresql.org>
date : Tue, 3 Nov 2026 13:03:38 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Tue, 3 Nov 2026 13:03:38 +0900 After a failover, an incremental backup could accept a prior backup
whose WAL range extends beyond the point where the new primary left the
old timeline. Combining these backups could retain pages from the
abandoned history, producing a corrupt backup that might still recover
and start successfully.
The validation checked the end of the prior backup's final WAL range
against the new backup's start LSN, but not against the timeline's
switch point. This allowed an incompatible prior backup to pass
validation once the new primary had advanced far enough.
Reject incremental backups if the prior backup's final WAL range
extends beyond the timeline's switch point.
Backpatch to v17, where incremental backup was introduced.
Author: Sai Pranav <rajasaipranav0@gmail.com>
Co-authored-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/CAPHduZC9pWm_vjznmhZNFsDZKLimqc6dKXOozLUKZVPs19zbZg@mail.gmail.com
Backpatch-through: 17 M doc/src/sgml/backup.sgml
M src/backend/backup/basebackup_incremental.c
M src/bin/pg_combinebackup/t/008_promote.pl
Fix incorrect LSNs in WAL decoding error messages
commit : 5f472ab564d153a047eb3f7d3edf2c8388899ee9
author : Fujii Masao <fujii@postgresql.org>
date : Tue, 3 Nov 2026 10:51:05 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Tue, 3 Nov 2026 10:51:05 +0900 Commit 3f1ce97346 introduced a circular WAL decoding buffer,
allowing records to be decoded before they are consumed. However,
decoding error messages continued to report the location of the last
record consumed instead of the record that failed decoding. With
recovery prefetching, the reported location could be many records
behind the failing record, making WAL problems harder to diagnose.
Fix this by reporting the location of the record being decoded.
Errors now identify the failing record even with recovery prefetching,
making it easier to investigate malformed WAL during recovery or with
tools such as pg_waldump and pg_walinspect.
Backpatch to v15, where the WAL decoding buffer was introduced.
Author: Rahul Yadav <rahul@rhyadav.dev>
Reviewed-by: Surya Poondla <suryapoondla4@gmail.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/P2qIkq4--F-9@rhyadav.dev
Backpatch-through: 15 M src/backend/access/transam/xlogreader.c
Fix lost wakeups in physical WAL senders
commit : 6d34d5500e9dad93f2514295cc350fced1ec843e
author : Fujii Masao <fujii@postgresql.org>
date : Tue, 3 Nov 2026 10:46:41 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Tue, 3 Nov 2026 10:46:41 +0900 Physical WAL senders could sleep even when flushed WAL was waiting to
be sent, delaying replication and synchronous commits until another
event woke them. In the reported case, commits stalled for about ten
seconds, until the standby sent its next periodic status report.
Commit bc971f4025c changed WAL sender wakeups to use condition
variables, which notify only senders already on the waitlist.
However, XLogSendPhysical() checks the flush position before
WalSndWait() joins the waitlist. If WAL is flushed between these two
steps, the wakeup can be missed, leaving the sender asleep despite
having more WAL to send.
Fix this by rechecking the flush position after joining the waitlist
and returning to the send loop if more WAL is available. For cascading
senders, also check for promotion or a timeline change, which require
the sender to revisit the current timeline even if the flush position
has not advanced.
Backpatch to v16, where commit bc971f4025c introduced the
condition-variable wakeup mechanism.
Author: Alex Speller <alex@alexspeller.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/mvx5bs7q.33b99c32-67d8-4373-8611-7a8250fb2fd3@we.are.superhuman.com
Backpatch-through: 16 M src/backend/replication/walsender.c
Make Memoize cache keys only of Params from outer query levels
commit : db7e885d093f96ffdf70bbb5dae92fa2fa13a031
author : Richard Guo <rguo@postgresql.org>
date : Tue, 3 Nov 2026 09:49:37 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Tue, 3 Nov 2026 09:49:37 +0900 Commit 708eb5158 made each PARAM_EXEC Param buried inside a larger
Memoize cache key expression a cache key by itself, and, in master
only, commit f8da64e6d extended that to the Params in the memoized
relation's base quals, targetlist and join clauses. Both meant to
catch Params supplied by an outer query level, but they took every
PARAM_EXEC Param they found, including the output Params of initPlans
and SubPlans.
When an initPlan's output Param is made a cache key, it is evaluated
on every cache probe, which runs the initPlan even where the query
doesn't need it, such as in a CASE arm that is never taken, and can
raise an error the query shouldn't raise. A SubPlan's output Params
have a value only while the SubPlan evaluates its testexpr, so making
a cache key of one fails with "plan should not reference subplan's
variable".
To fix, make cache keys only of the Params listed in some outer query
level's plan_params, as those hold values supplied by outer levels and
are always safe to evaluate. Any other Param is left to purge the
cache when it changes.
Back-patch to all supported branches.
Reported-by: Fujii Masao <masao.fujii@gmail.com>
Author: Richard Guo <guofenglinux@gmail.com>
Discussion: https://postgr.es/m/CAHGQGwHdaau1KooFbY10=MDhKtFVgpmPrDiwNpEe6bWu7wpQYA@mail.gmail.com
Backpatch-through: 14 M src/backend/optimizer/path/joinpath.c
M src/test/regress/expected/memoize.out
M src/test/regress/sql/memoize.sql
Delay checkpoint start while creating a multixact
commit : 4022edb93330b95c7d910ca5951a6f8f7f9015c8
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Tue, 3 Nov 2026 01:39:20 +0200
committer: Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Tue, 3 Nov 2026 01:39:20 +0200 A checkpoint starting between the CREATE_ID record and the SLRU update
could lose the multixact after a crash.
Author: shihao zhong <zhong950419@gmail.com>
Discussion: https://www.postgresql.org/message-id/CAGRkXqSuiJ_X4+Z6rsD42pcuACnD9PhoQ-Os+mUJNmJarBWkNg@mail.gmail.com
Backpatch-through: 14 M src/backend/access/transam/multixact.c
Fix BRIN minmax-multi distance for tid values
commit : c83ddfcfe9817684f5e2347137b303c6c3763c57
author : Tomas Vondra <tomas.vondra@postgresql.org>
date : Mon, 2 Nov 2026 21:49:28 +0100
committer: Tomas Vondra <tomas.vondra@postgresql.org>
date : Mon, 2 Nov 2026 21:49:28 +0100 The distance function for tid values maps item pointers to float8 as
(block * MaxHeapTuplesPerPage + offset)
but the multiplication was done in uint32 arithmetic, so for high block
values (above 14759338, ~112GB) the result wrapped around. The distance
would then be very inaccurate (possibly negative), resulting in a less
efficient index.
Fix by doing the arithmetics in double.
It's also possible the (user-supplied) item pointers have offsets
greater than MaxHeapTuplesPerPage, confusing the mapping to double
value. This commit does not try to address that, it's not clear what
would be a better mapping.
Author: Tomas Vondra <tomas@vondra.me>
Backpatch-through: 14
Discussion: https://postgr.es/m/CALdSSPgBvXz-VSF0p-5k%3D_uFfN38cWrERmm_AC4K67N6MWQhmg%40mail.gmail.com M src/backend/access/brin/brin_minmax_multi.c
Handle numeric overflow in numeric_minmax_multi_ops
commit : 9ab2a624ebe92431d19a92367e8a99dccfc5e10a
author : Tomas Vondra <tomas.vondra@postgresql.org>
date : Mon, 2 Nov 2026 20:09:59 +0100
committer: Tomas Vondra <tomas.vondra@postgresql.org>
date : Mon, 2 Nov 2026 20:09:59 +0100 When calculating the distance between numeric values, the code could
overflow in two ways. A subtraction of two finite values may still
overflow, or the result may not fit into float8.
The distances don't need to be perfectly accurate, although it may make
the index less efficient. And for most minmax-multi opclasses it works
like that, but for numeric the functions may error-out. That prevents
summarization, either when creating an index or later during vacuum.
Fix by using numeric_sub_safe() and numeric_float8_no_overflow(), and
returning infinity when either of those fails. The older releases need
to use numeric_sub_opt_error(), but the principle is the same.
Also, handle NaN values the same as for float4/float8. When both values
are NaN, return distance 0. When one value is NaN, return infinity.
While at it, free the detoasted copies of arguments, and the calculated
numeric distance. For small values it does not matter, but for extreme
numeric values it may make a difference.
Reported by Kirill Reshke, with a patch fixing the issue. Various
improvements and backpatching by me.
Backpatch to 14, where minmax-multi indexes were introduced.
Author: Kirill Reshke <reshkekirill@gmail.com>
Author: Tomas Vondra <tomas@vondra.me>
Reviewed-by: shihao zhong <zhong950419@gmail.com>
Backpatch-through: 14
Discussion: https://postgr.es/m/CALdSSPgBvXz-VSF0p-5k%3D_uFfN38cWrERmm_AC4K67N6MWQhmg%40mail.gmail.com M src/backend/access/brin/brin_minmax_multi.c
M src/test/regress/expected/brin_multi.out
M src/test/regress/sql/brin_multi.sql
Fix missing tts_tid assignments in ExecForceStoreHeapTuple().
commit : 7e594c30f49c9b5b014bc6dd9b22c5380d071d01
author : Nathan Bossart <nathan@postgresql.org>
date : Mon, 2 Nov 2026 10:13:52 -0600
committer: Nathan Bossart <nathan@postgresql.org>
date : Mon, 2 Nov 2026 10:13:52 -0600 ExecForceStoreHeapTuple() clears the slot before storing the tuple,
which invalidates the slot's TID, but it only sets the TID again
for slots of type TTSOpsHeapTuple. Besides producing wrong query
results, this can lead to data corruption if an affected tuple is
locked (e.g., via SELECT ... FOR UPDATE). To fix, set the slot's
TID for the other kinds of slots, too.
Oversight in commit b8d71745ea.
Author: Virender Singla <virender.cse@gmail.com>
Co-authored-by: Greg Burd <greg@burd.me>
Reviewed-by: Dilip Kumar <dilipbalaut@gmail.com>
Reviewed-by: Andres Freund <andres@anarazel.de>
Reviewed-by: Nikolay Samokhvalov <nik@postgres.ai>
Reviewed-by: Heikki Linnakangas <hlinnaka@iki.fi>
Tested-by: Shihao Zhong <zhong950419@gmail.com>
Discussion: https://postgr.es/m/CAM6Zo8wZOLnCWRO_tuuXVX9J4N4JN6GsEnk8WJtT0%3D_0zy-1dw%40mail.gmail.com
Discussion: https://postgr.es/m/0498c10f-839b-4f68-9994-c29b454e55a4%40app.fastmail.com
Backpatch-through: 14 M src/backend/executor/execTuples.c
Update .abi-compliance-history for hashed SubPlan fix
commit : 25b55788d89d47d558c282a8e62d474d0c804ee9
author : Richard Guo <rguo@postgresql.org>
date : Mon, 2 Nov 2026 15:41:23 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Mon, 2 Nov 2026 15:41:23 +0900 Author: Richard Guo <guofenglinux@gmail.com>
Discussion: https://postgr.es/m/179087936257.78721.1170374394656748858@mail.gmail.com
Backpatch-through: 14-18 M .abi-compliance-history
Delay checkpoint start in RecordTransactionAbortPrepared()
commit : 56e831ea2227b701548899db40a49af17a7ec79c
author : Michael Paquier <michael@paquier.xyz>
date : Mon, 2 Nov 2026 15:18:09 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Mon, 2 Nov 2026 15:18:09 +0900 ROLLBACK PREPARED flushes its abort record and then updates pg_xact.
Nothing prevented a checkpoint from setting its redo pointer between
these two steps. A crash before the removal of the 2PC state file would
then cause recovery to begin after the abort record and would restore
the transaction as prepared, even though its rollback has been
aknowledged. Standbys could for example replay the abort record, and a
follow-up COMMIT PREPARED on a primary could make them flip a
transaction from aborted to committed.
Losing an abort is harmless for a regular transaction, but not for a
prepared transaction whose 2PC state file is still on disk. This issue
is fixed by setting DELAY_CHKPT_START (delayChkpt in v14) around the
abort record and the pg_xact update, as done for COMMIT PREPARED.
This issue exists since the introduction of 2PC, since d0a89683a3a4.
Author: Shihao Zhong <zhong950419@gmail.com>
Discussion: https://postgr.es/m/CAGRkXqRhpq=fd8Qf_-Ki1OW7b1iL+o3YuHiHQhLw5RUJy2JGXQ@mail.gmail.com
Backpatch-through: 14 M src/backend/access/transam/twophase.c
Fix reuse of stale hash tables by duplicated hashed SubPlans
commit : fcb44c5e579fd32dbe96adf08a9f82ddaa518176
author : Richard Guo <rguo@postgresql.org>
date : Mon, 2 Nov 2026 14:39:11 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Mon, 2 Nov 2026 14:39:11 +0900 The same SubPlan can appear more than once in a plan tree. For
example, when an equivalence class has a constant member containing a
SubPlan, each derived "member = constant" clause references that
SubPlan; a hash join's hash keys and its hash clauses can both contain
it; and extract_restriction_or_clauses() can copy it into a new
restriction clause while leaving it in the original join clause. Each
reference gets its own SubPlanState, with its own hash tables, but
they all share the subplan's PlanState.
A hashed SubPlan whose subquery references outer query levels must
rebuild its hash tables whenever those parameters change.
ExecHashSubPlan() detected that by checking chgParam of the subplan's
PlanState. But buildSubPlanHash() rescans that PlanState, which
clears its chgParam, so after one SubPlanState had rebuilt its hash
tables, the others no longer saw the change and kept probing hash
tables built for previous parameter values, producing wrong query
results.
To fix, give each SubPlanState its own flag to mark its hash tables as
stale. ExecReScan() sets it, alongside propagating chgParam to the
node's subplans, ExecHashSubPlan() checks it instead of the subplan's
chgParam, and buildSubPlanHash() clears it. The new field fits into
existing padding in SubPlanState, so this does not change the struct's
size or the offsets of its other fields.
Reported-by: Samuel Olaoye <dapsalmy@gmail.com>
Author: Rahul Yadav <rahul@rhyadav.dev>
Reviewed-by: Alexandra Wang <alexandra.wang.oss@gmail.com>
Reviewed-by: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: shihao zhong <zhong950419@gmail.com>
Discussion: https://postgr.es/m/179087936257.78721.1170374394656748858@mail.gmail.com
Backpatch-through: 14 M src/backend/executor/execAmi.c
M src/backend/executor/nodeSubplan.c
M src/include/nodes/execnodes.h
M src/test/regress/expected/subselect.out
M src/test/regress/sql/subselect.sql
Fix double-free when XMLSERIALIZE(... INDENT) output exceeds 1GB.
commit : 7fa0a5f21cdf241a3f19641a6846eabbd2fbcce5
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Sun, 1 Nov 2026 20:45:36 -0500
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Sun, 1 Nov 2026 20:45:36 -0500 In xmltotext_with_options(), xmlSaveClose() frees the save context
unconditionally, but ctxt was only reset to NULL on the error branch.
When the close succeeded and a later step threw an error -- typically,
cstring_to_text_with_len() rejecting a result larger than MaxAllocSize
-- control reached the PG_CATCH block with ctxt still pointing at the
freed context, so xmlSaveClose() was called a second time on freed
memory and crashed the backend.
Author: Jim Jones <jim.jones@uni-muenster.de>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/bbadb6b2-d226-41a1-ace3-e0eb20648f58@uni-muenster.de
Backpatch-through: 16 M src/backend/utils/adt/xml.c
Fix wrong results from Memoize with a Param inside a cache key
commit : d6f60a031f21d57c6e574d5166d114b7d510f5e6
author : Richard Guo <rguo@postgresql.org>
date : Mon, 2 Nov 2026 10:31:29 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Mon, 2 Nov 2026 10:31:29 +0900 ExecReScanMemoize() purges the whole cache only when a Param changes
which is not part of the cache key; a change to a cache-key Param is
assumed to be handled by looking up a different cache entry. To that
end, create_memoize_plan() put every Param appearing in the cache key
expressions into keyparamids. That assumption holds only when the
cache key values determine the Param's value.
It fails for an upper-level Param which is buried inside a larger
cache-key expression and also appears elsewhere in the subplan. With
a cache key of "t2.hundred + t0.ten" and a filter "t1.twenty =
t0.ten", different (t2.hundred, t0.ten) pairs produce the same
cache-key value while the filter selects different rows, so one cache
entry wrongly serves both. Since t0.ten was included in keyparamids,
the cache was never purged when it changed.
Also, pull_paramids() collected PARAM_EXTERN Params too, whose numbers
are unrelated to those of PARAM_EXEC Params. An external Param in a
cache key could therefore keep a change to an unrelated executor Param
from purging the cache.
To fix, put into keyparamids only the Params whose values the cache
keys determine. These are the nestloop Params which replace the outer
rel's Vars in the cache keys, as the inner scan can only reference the
outer rel through the cache keys, and, in binary mode only, a Param
which is a cache key by itself. In logical mode the hash equality
operator may not distinguish values that affect the results
differently. Any other Param now purges the cache when it changes.
Also make pull_paramids() collect PARAM_EXEC Params only.
Purging alone would empty the cache on every rescan of the enclosing
plan whenever a buried Param changes, even when the Param is used
nowhere else and the old code was correct and kept its cache entries
across rescans. So also make each buried Param a cache key by itself,
which puts Memoize into binary mode, so that cache entries survive
across rescans with the same Param value. A buried Param whose type
can't be hashed is left to the purging, rather than giving up on
Memoize.
Back-patch to all supported branches.
Reported-by: Jacob Brazeal <jacob.brazeal@gmail.com>
Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: David Rowley <dgrowleyml@gmail.com>
Reviewed-by: Yilin Zhang <jiezhilove@126.com>
Discussion: https://postgr.es/m/CA+COZaAHZT7T2G2F0-drdQB+StWJxOzqeDxp=ESp+YUiy-kOaQ@mail.gmail.com
Backpatch-through: 14 M src/backend/optimizer/path/joinpath.c
M src/backend/optimizer/plan/createplan.c
M src/backend/optimizer/util/clauses.c
M src/include/nodes/execnodes.h
M src/include/nodes/plannodes.h
M src/test/regress/expected/memoize.out
M src/test/regress/sql/memoize.sql
Fix pgoutput crash on missing option values
commit : c3ef3f2283d6a9b8d63ba35c5bc5ad4f1864682d
author : Michael Paquier <michael@paquier.xyz>
date : Mon, 2 Nov 2026 08:12:43 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Mon, 2 Nov 2026 08:12:43 +0900 The replication protocol allows options without values, but pgoutput
dereferenced a NULL DefElem argument for parameters proto_version and
publication_names. A replication client was able to crash a WAL sender.
This issue is fixed by reading both options with defGetString(), which
is able to report a missing parameter cleanly.
Reported-by: Brar Piening <brar@gmx.de>
Author: Zhijie Hou <houzhijie22@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Reviewed-by: Kirill Reshke <reshkekirill@gmail.com>
Discussion: https://postgr.es/m/19795-6d7d506133b7f062@postgresql.org
Backpatch-through: 14 M src/backend/replication/pgoutput/pgoutput.c
M src/bin/pg_basebackup/t/030_pg_recvlogical.pl
meson: Avoid PATH bloat from NLS .mo targets in tmp_install test
commit : 2709ad493ee8c414457e2caf7ad9f8c9f1c97c99
author : Andrew Dunstan <andrew@dunslane.net>
date : Mon, 13 Jul 2026 10:50:54 -0400
committer: Andrew Dunstan <andrew@dunslane.net>
date : Mon, 13 Jul 2026 10:50:54 -0400 On Windows, meson adds the build directory of every target in a test's
'depends:' to that test's PATH. tmp_install depended on
installed_targets, which with NLS enabled includes several hundred .mo
targets, pushing PATH past the Windows length limit. tmp_install and
initdb_cache then failed with no output.
Fix by depending on a stamp custom_target that itself depends on
installed_targets. Meson doesn't walk a custom_target's dependencies
when building the test PATH, so this adds at most one entry. This can
be removed once we mandate a meson version containing the upstream fix
(expected in 1.13).
Reviewed-by: Tristan Partin <tristan@partin.io>
Backpatch-through: 16
Discussion: https://postgr.es/m/CAD5tBcJXAX8bz5q9ngE4znvVLiXaPO89D0yTFhJgEPrPyT-Erw@mail.gmail.com M meson.build
Fix incremental backup data loss due to WAL summary merge order
commit : 9580e29da90c5f49cf34613d772ebbd5614882b1
author : Fujii Masao <fujii@postgresql.org>
date : Mon, 2 Nov 2026 00:22:09 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Mon, 2 Nov 2026 00:22:09 +0900 An incremental backup taken after a relation is truncated and extended
again can omit modified blocks. Combining it with an earlier backup can
then restore stale data, causing live rows to disappear and deleted rows
to reappear.
WAL summaries were merged without ensuring LSN order. Applying a limit
block from an older summary after processing a newer summary could
discard references to blocks modified later, causing those blocks to
be omitted from the backup.
Sort the required summaries by ascending start LSN before merging them,
preserving references to later block modifications so that the required
blocks are included in the backup.
Backpatch to 17, where incremental backup was introduced.
Bug: #19774
Reported-by: Ke <kehan5800@gmail.com>
Author: Ke <kehan5800@gmail.com>
Co-authored-by: Fujii Masao <masao.fujii@gmail.com>
Reviewed-by: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Discussion: https://www.postgresql.org/message-id/19774-8a1cbfd1774fcd0c%40postgresql.org
Backpatch-through: 17 M src/backend/backup/basebackup_incremental.c
M src/bin/pg_combinebackup/meson.build
A src/bin/pg_combinebackup/t/013_summary_order.pl
Fix WAL summary boundary checks for incremental backups.
commit : ec756e5913b8d890fab435a0ca5b7d24a60306b8
author : Fujii Masao <fujii@postgresql.org>
date : Mon, 2 Nov 2026 00:18:40 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Mon, 2 Nov 2026 00:18:40 +0900 A standby can summarize received WAL ahead of replay. If recovery ends
exactly where an old-timeline summary begins, FilterWalSummaries()
incorrectly includes that summary even though it covers only WAL outside
the promoted server's history. Truncation information in the summary
can cause an incremental backup to omit modified blocks, leading
pg_combinebackup to reconstruct files with stale data from an earlier
backup. For example, updated rows can revert to their previous values
after restoring the backup.
Fix this by excluding summaries that only touch the boundaries of the
requested LSN range without overlapping it. This makes
FilterWalSummaries() consistent with GetWalSummaries() and its existing
function comments. The old-timeline summary starting at the switch point
is thus excluded, preventing its truncation information from causing
the backup to omit required modified blocks.
Back-patch to PostgreSQL 17, where incremental backup was introduced.
Author: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Manu <manuelreyesbravo@gmail.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Reviewed-by: shihao zhong <zhong950419@gmail.com>
Discussion: https://postgr.es/m/CAJTYsWV7RThdo4K62G8hC=Anpkbr54QWbOvyVvw5uvx4+-x_DQ@mail.gmail.com
Backpatch-through: 17 M src/backend/backup/walsummary.c
Fix two crashes on a concurrently dropped schema
commit : f1ba2b826c52ab6e593d5d25c15dbde9d417f386
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Sat, 31 Oct 2026 13:24:50 +0200
committer: Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Sat, 31 Oct 2026 13:24:50 +0200 pg_identify_object() looks up the object's schema name without a lock
on the schema, so a concurrent DROP SCHEMA can make get_namespace_name()
return NULL, which was then passed to quote_identifier(). Report the
schema as NULL instead, as is already done for objects that have gone
away.
Similarly, schema_to_xml() and the related SQL/XML functions look up
the schema name without a lock on the schema, and a concurrent DROP
SCHEMA can make get_namespace_name() return NULL, which was then
dereferenced. Report that the schema does not exist, or skip it when
mapping all schemas of the database.
Author: Baji Shaik <baji.pgdev@gmail.com>
Discussion: https://www.postgresql.org/message-id/CA+fm-RPD0m9QL8W+804SGBBxn5iFz5wn=kncaP9SkM-s4PUANw@mail.gmail.com
Backpatch-through: 14 M src/backend/catalog/objectaddress.c
M src/backend/utils/adt/xml.c
Fix error handling in pg_flush_data() and FilePrefetch()
commit : ce2797dca4f81bad7e4b838761bf717ceaaa584c
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Sat, 31 Oct 2026 11:24:22 +0200
committer: Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Sat, 31 Oct 2026 11:24:22 +0200 posix_fadvise() returns an error code directly instead of setting
errno. However, pg_flush_data() uses %m and errcode_for_file_access()
to report errors without setting errno, potentially reporting an
unrelated error. Fix this by assigning the returned error code to
errno before reporting the error.
FilePrefetch() is documented to return an errno error code on failure,
but it returns -1 when FileAccess() fails. Fix this by returning errno
instead.
The first bug one only affects a warning, and the second doesn't have
any effect because the only existing caller ignores the return value.
So these are pretty harmless bugs, but let's be tidy.
Author: Chao Li <lic@highgo.com>
Discussion: https://www.postgresql.org/message-id/F1916C7A-F152-4AF3-87A9-40492D8B3670@gmail.com
Backpatch-through: 14 M src/backend/storage/file/fd.c
tests: Don't use the initdb template for older installations
commit : 9c44922d125cc369c0356187244f6b80a801ea8e
author : Andres Freund <andres@anarazel.de>
date : Fri, 30 Oct 2026 16:20:54 -0400
committer: Andres Freund <andres@anarazel.de>
date : Fri, 30 Oct 2026 16:20:54 -0400 Previously the initdb template was used even when creating a Cluster.pm
instance for an old install. Obviously the current template is not right for
that case. The only reason we didn't notice before is because the existing
cross-version tests, e.g. 002_pg_upgrade.pl, always pass initdb options for
the old node.
Oversight in 252dcb32397.
Discussion: https://postgr.es/m/xhzif5hemvslxymzhk3ykdypwqvjm7pz5dm7taizgxgfjw6fwa@6gygmz5bdnc3
Backpatch-through: 17 M src/test/perl/PostgreSQL/Test/Cluster.pm
Update time zone data files to tzdata release 2026f.
commit : 8e1414de3af7ca12ad2196f0ad4be81e33fb5af9
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Fri, 30 Oct 2026 14:35:24 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Fri, 30 Oct 2026 14:35:24 -0400 Cuba will not fall back on 2026-11-01; they will stay on DST this
winter (but apparently plan to revert to normal next year).
Historical corrections for Brazil. M src/timezone/data/tzdata.zi
libpq: handle GSS credentials more sanely.
commit : df9e6d7c7be5b178bf2c04f74d2f5dd1f7ec77d0
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Fri, 30 Oct 2026 13:56:10 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Fri, 30 Oct 2026 13:56:10 -0400 pg_GSS_have_cred_cache() always acquires new GSS credentials.
If we already had some, they were just leaked due to overwriting
conn->gcred, and that's a nontrivial amount of memory (around 16K
according to reporter). Unfortunately, pg_GSS_continue() did this
each time through, and it is typically called more than once in a
GSSAPI connection cycle. So repeated connections using GSS
authentication could leak substantial amounts of memory.
IMO the source of this problem is really the poorly-designed API
of pg_GSS_have_cred_cache(): it's unclear that it always gets new
credentials. Let's rename it and remove the unnecessary pointer
parameter to make its behavior clearer.
To add insult to injury, pg_GSS_continue() shouldn't even be
trying to acquire credentials unless GSS delegation is enabled,
which it is not by default; so the whole effort is often wasted.
Problems evidently introduced by 6633cfb21, so back-patch to v16.
Bug: #19411
Reported-by: Matt Carter <Matt.Carter@twosigma.com>
Diagnosed-by: Matt Carter <Matt.Carter@twosigma.com>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/19411-0440c8897a04b638@postgresql.org
Backpatch-through: 16 M src/interfaces/libpq/fe-auth.c
M src/interfaces/libpq/fe-connect.c
M src/interfaces/libpq/fe-gssapi-common.c
M src/interfaces/libpq/fe-gssapi-common.h
M src/interfaces/libpq/fe-secure-gssapi.c
Return INT_MAX or INT_MIN from strtoint() on overflow.
commit : 2acade963cb1c6c81c6f09a018ceca43958e1be9
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Fri, 30 Oct 2026 11:03:13 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Fri, 30 Oct 2026 11:03:13 -0400 Previously, strtoint() set errno to ERANGE on overflow but returned
the result of casting the long value to int. When the value is outside
the range of int, this produces an implementation-defined result that
cannot reliably indicate the direction of overflow. POSIX requires
strtol() to return exactly LONG_MAX or LONG_MIN on overflow, which
permits safe sign-testing or range-testing. By analogy, make
strtoint() return INT_MAX or INT_MIN on overflow.
Back-patch, since future bug fixes may expect this behavior.
Author: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/A33EE17C-D014-4980-B3DD-AC51588A06EC@gmail.com
Backpatch-through: 14 M src/common/string.c
Fix crash-restart race in 041_checkpoint_at_promote.pl
commit : abab508718463e5d3a43ba2f95874127fb8739f3
author : Fujii Masao <fujii@postgresql.org>
date : Fri, 30 Oct 2026 20:50:54 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Fri, 30 Oct 2026 20:50:54 +0900 The buildfarm member bushmaster reported failures in the final test query
in 041_checkpoint_at_promote.pl with "the database system is in recovery
mode".
After killing a backend to trigger crash recovery, the test should wait
for the postmaster to terminate the remaining server processes and restart
the server, then wait for recovery to finish and connections to be accepted
before running the final query.
Previously, the test assumed that the killed backend's connection closing
meant the postmaster had noticed the crash and begun restarting. However,
the connection can close before the postmaster notices the crash. The
subsequent connection check can then succeed before recovery starts,
causing the test to mistake this for recovery having completed. The final
query could then run during recovery and fail.
Fix this by waiting for the postmaster to log that all server processes
have terminated and it is reinitializing before checking that connections
are accepted again. This ensures that the postmaster has noticed the crash
and begun restarting before the test waits for recovery to finish.
Backpatch to v17, where this test was introduced.
Reported-by: Alexander Lakhin <exclusion@gmail.com>
Author: Fujii Masao <masao.fujii@gmail.com>
Reviewed-by: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Discussion: https://postgr.es/m/e7abd282-655d-495e-94c9-e38a58917ebf@gmail.com
Backpatch-through: 17 M src/test/recovery/t/041_checkpoint_at_promote.pl
Fix more issues with extended statistics on expressions
commit : 8b517a30c832d2614d00e1dbe53fe06216adf548
author : Michael Paquier <michael@paquier.xyz>
date : Fri, 30 Oct 2026 17:29:43 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Fri, 30 Oct 2026 17:29:43 +0900 When evaluating expressions (through make_build_data()) and building
stats data for expressions (through build_expr_data()), both routines
forgot that examine_expression() could return a NULL result, depending
on the data generated by the typanalyze function assigned to a type.
This would crash the server.
This commit includes some tests added to test_custom_types that are able
to reproduce all these issues with two fancy typanalyze callbacks:
- False result with a valid stats target, for the expression evaluation.
- False result with a valid stats target on second call, for the stats
data build.
No typanalyze callback in core have the idea to do that, and I doubt
that anything out there has the idea to do that. If they do, this
switches the expression stats to fail gracefully instead of crashing,
based on the combinations that are assumed as supported by the core
code.
This is a continuation of 3b7a6fa15720.
Reported-by: Yuelin Wang <1217816127@qq.com>
Author: Shihao Zhong <zhong950419@gmail.com>
Author: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/19763-14da3f96c02f07e8@postgresql.org
Backpatch-through: 14 M src/backend/statistics/extended_stats.c
M src/test/modules/test_custom_types/README
M src/test/modules/test_custom_types/expected/test_custom_types.out
M src/test/modules/test_custom_types/sql/test_custom_types.sql
M src/test/modules/test_custom_types/test_custom_types–1.0.sql
M src/test/modules/test_custom_types/test_custom_types.c
Check all four box corners in SP-GiST quad tree <@ search
commit : 9ad421a9b970b672136fc681f2001876dec01072
author : John Naylor <john.naylor@postgresql.org>
date : Fri, 30 Oct 2026 12:31:39 +0700
committer: John Naylor <john.naylor@postgresql.org>
date : Fri, 30 Oct 2026 12:31:39 +0700 When the centroid lies outside the query box, spg_quad_inner_consistent()
keeps only the quadrants that contain a corner of the box. It meant to
test all four corners, but the last step set x back to low.x instead of
y to low.y, so it tested (low.x, high.y) twice and never (high.x, low.y).
With exact comparisons the missing corner would not matter: a box that
does not contain the centroid lies on one side of it on at least one
axis, so (high.x, low.y) shares a quadrant with another corner, but
getQuadrant() compares with FPeq(), which tiebreaks to the lowest
numbered quadrant.
When a box edge lies within EPSILON of the centroid, the corner on that
edge counts as being on the centroid's line, and (high.x, low.y) may be
the only corner in its quadrant. That quadrant was then skipped. Fix
the missing corner test.
Discussion: https://postgr.es/m/CANWCAZZZpkwrX0kC4Jo6KM27J6dyBdCDir-PbjEYAS06r%3D4-ag%40mail.gmail.com
Backpatch-through: 14 M src/backend/access/spgist/spgquadtreeproc.c
M src/test/regress/expected/spgist.out
M src/test/regress/sql/spgist.sql
Remove unwarranted Assert in dependencies_clauselist_selectivity()
commit : 1fd6ed1f8cf5e0e40d2c66ea0ac4788af757b937
author : David Rowley <drowley@postgresql.org>
date : Fri, 30 Oct 2026 15:27:09 +1300
committer: David Rowley <drowley@postgresql.org>
date : Fri, 30 Oct 2026 15:27:09 +1300 The loop above from where the Assert is removed handles assigning the same
attnum to any duplicate expressions, so asserting there are no duplicate
attnums just after purposefully making duplicate ones isn't the correct
thing to do.
The Assert was added by a4d75c86bf, so backpatch to v14
Author: BHUVAN CHILUKUTI <c.bhuvan@iitg.ac.in>
Reviewed-by: David Rowley <dgrowleyml@gmail.com>
Discussion: https://postgr.es/m/PN0P287MB0903563C70D8E5B81912E855A7602@PN0P287MB0903.INDP287.PROD.OUTLOOK.COM
Backpatch-through: 14 M src/backend/statistics/dependencies.c
Empty partial_pathlist when unsetting consider_parallel flag
commit : 85a38715894b4fb19c5b544713dc4865e71e9622
author : David Rowley <drowley@postgresql.org>
date : Thu, 29 Oct 2026 16:21:08 +1300
committer: David Rowley <drowley@postgresql.org>
date : Thu, 29 Oct 2026 16:21:08 +1300 It's not valid for a RelOptInfo to have a non-empty partial_pathlist
when its consider_parallel is true. In set_append_rel_pathlist() when
we see the parent rel doesn't have consider_parallel set, we set that
property to false for the child. It is possible that the child already
has some partial paths at that point, so we need to nullify that list as
well as set the consider_parallel flag to false.
For the reported problem case, the partial paths had been created during
set_subquery_pathlist(). These not being empty was causing an Assert
failure on cassert builds. I think this could only have gone on to
result in some wasted planner effort in non-cassert builds, but cannot
confirm if anything worse could have happened.
Bug: #19803
Reported-by: Gowtham Reddy <gowthamdoma18@gmail.com>
Author: David Rowley <dgrowleyml@gmail.com>
Tested-by: Gowtham Reddy <gowthamdoma18@gmail.com>
Discussion: https://postgr.es/m/19803-1fcc1df108c54ab6@postgresql.org
Backpatch-through: 14 M src/backend/optimizer/path/allpaths.c
Don't replace clone clauses with constant TRUE
commit : ba7127f240c03f08e888b1b9a228774210fbcf4f
author : Richard Guo <rguo@postgresql.org>
date : Thu, 29 Oct 2026 11:27:12 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Thu, 29 Oct 2026 11:27:12 +0900 When reconsider_outer_join_clauses() finds an outer-join clause
OUTERVAR = INNERVAR whose OUTERVAR is known to equal a constant, it
pushes INNERVAR = CONSTANT into the inner side and replaces the clause
with constant TRUE. The replacement assumes that OUTERVAR cannot have
gone to NULL where the clause is evaluated.
That assumption fails for a clone made for outer join identity 3.
When an outer join is pushed into the RHS of a lower outer join, its
relid is not added to the joinrel until the commuted pair is complete,
so a version of a clause that lacks that join's nulling bit can be
applied above it, after it has null-extended OUTERVAR. This is fine
for the original clause, because it is strict, which is what allowed
the commutation in the first place; but it is not fine for constant
TRUE. As a result, inner rows could be joined to null-extended outer
rows, producing wrong query results.
To fix, throw back a clone as-is rather than the TRUE replacement, as
v15 and earlier did. The derived INNERVAR = CONSTANT clause remains
valid and is still pushed down. This can leave a redundant join
filter in some plans, as seen in an existing regression test.
To keep the thrown-back clause from affecting selectivity estimates,
including for parameterized scans, where the cached selectivity is not
used, restore the redundancy marker removed by b448f1c8d: a norm_selec
greater than 1 marks a redundant clause, for which
clause_selectivity_ext() always returns 1.0.
Back-patch to v16, where clone clauses were introduced.
Bug: #19765
Reported-by: Viktor Leis <leis@in.tum.de>
Diagnosed-by: Tender Wang <tndrwang@gmail.com>
Author: Richard Guo <guofenglinux@gmail.com>
Co-authored-by: Nitin Motiani <nitinmotiani@google.com>
Reviewed-by: Tender Wang <tndrwang@gmail.com>
Discussion: https://postgr.es/m/19765-5778b18918040583@postgresql.org
Backpatch-through: 16 M src/backend/optimizer/path/clausesel.c
M src/backend/optimizer/path/equivclass.c
M src/include/nodes/pathnodes.h
M src/test/regress/expected/join.out
M src/test/regress/sql/join.sql
Do not take a full-page image for REGBUF_NO_CHANGE buffers
commit : fd1f61e435114f90cef15a7573f37b6b9e6256a9
author : Michael Paquier <michael@paquier.xyz>
date : Thu, 29 Oct 2026 11:14:34 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Thu, 29 Oct 2026 11:14:34 +0900 A buffer registered with REGBUF_NO_CHANGE is not modified by the record
and, in the case of the hash index AM, may be held with only a pin (no
content lock). The hash index code registers the primary bucket page
this way in hashbucketcleanup(), _hash_freeovflpage() and
_hash_squeezebucket(), solely to force a cleanup lock on that block
during replay.
When wal_consistency_checking includes "hash", XLogRecordAssemble()
included a full-page image for block only pinned, because
XLR_CHECK_CONSISTENCY forces an image regardless of REGBUF_NO_IMAGE.
The record CRC is then computed over the image while a concurrent
backend, which legitimately holds nothing stronger than a pin
on that page, could modify it before the record is copied into the WAL
buffers. This would result is an intermittent "incorrect resource
manager data checksum in record" failure on a standby.
As of this commit, never take an image for a REGBUF_NO_CHANGE buffer,
even under XLR_CHECK_CONSISTENCY. There is no need to do a consistency
check on a block not changed by a record. verifyBackupPageConsistency()
is taught to skip a registered block that carries no image rather than
asserting one should be present.
REGBUF_NO_CHANGE exists since 00d7fb5e2e39, so backpatch down to v17.
On older branches, the CRC problem can be reproduced as well, but we
lack the mechanism to check how a buffer is handled by the code
assembling a record. Per discussion, the long term picture should be to
fix the hash index AM so as REGBUF_NO_CHANGE is removed. This would be
a HEAD-only change.
Reported-by: Alexander Lakhin <exclusion@gmail.com>
Diagnosed-by: Thomas Munro <thomas.munro@gmail.com>
Author: Greg Burd <greg@burd.me>
Discussion: https://postgr.es/m/ats4pi-RM0Uom0T9@floki
Backpatch-through: 17 M src/backend/access/transam/README
M src/backend/access/transam/xloginsert.c
M src/backend/access/transam/xlogrecovery.c
M src/include/access/xloginsert.h
Fix JSON_TABLE with sublinks in PASSING arguments
commit : 8793978e30accc730b31f6557afb2c8469e48250
author : Amit Langote <amitlan@postgresql.org>
date : Thu, 29 Oct 2026 10:38:24 +0900
committer: Amit Langote <amitlan@postgresql.org>
date : Thu, 29 Oct 2026 10:38:24 +0900 transformJsonTable() handed the raw PASSING argument list both to the
JsonFuncExpr built for the row pattern and, via
transformJsonTableColumn(), to the JsonFuncExpr built for each column
other than FOR ORDINALITY, so the same raw expressions were run through
transformExpr() once for the row pattern and again for every such
column. That doesn't work for sublinks: transformSubLink() replaces
the SubLink's raw subselect with the analyzed Query, so the second
pass failed with "unexpected non-SELECT command in SubLink". Other
nodes that are transformed in place, such as NullTest, failed with
"unrecognized node type". Any such PASSING argument therefore made
JSON_TABLE fail unless all of its columns were FOR ORDINALITY.
Fix by giving each transformation its own copy of the raw PASSING
arguments.
Bug: #19786
Reported-by: Alperen Keles <alpkeles99@gmail.com>
Author: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Co-authored-by: Amit Langote <amitlangote09@gmail.com>
Discussion: https://postgr.es/m/19786-70758103a4b2bff2@postgresql.org
Backpatch-through: 17 M src/backend/parser/parse_jsontable.c
M src/test/regress/expected/sqljson_jsontable.out
M src/test/regress/sql/sqljson_jsontable.sql
SQL/JSON: fix error handling when coercing to a domain fails softly
commit : 46798ab1adf34bf57b7585019033db89fb0641e2
author : Amit Langote <amitlan@postgresql.org>
date : Thu, 29 Oct 2026 09:36:07 +0900
committer: Amit Langote <amitlan@postgresql.org>
date : Thu, 29 Oct 2026 09:36:07 +0900 SQL/JSON query functions could throw an error, or report the wrong one,
in cases where the ON ERROR or ON EMPTY clause said what to do instead.
This happened when the RETURNING type, or a DEFAULT expression,
involved a domain: if the value failed to convert to the domain's base
type, that failure was saved to be handled by the ON ERROR behavior,
but the domain's constraints were then checked anyway, against the
dummy NULL left behind by the failed conversion. A CHECK expression
can throw a hard error on that NULL, which ON ERROR cannot catch, and
a NOT NULL constraint reports a violation that overwrites the saved
error, so the error message ends up describing the wrong problem.
For example, this failed with "division by zero" instead of returning
11:
CREATE DOMAIN d AS int CHECK (1 / coalesce(VALUE, 0) = 0);
SELECT JSON_VALUE(jsonb '"x"', '$' RETURNING d DEFAULT 11 ON ERROR);
Fix by stopping as soon as a soft error has been saved, in each of the
places that kept going:
* The domain constraint checks of a CoerceToDomain in an ON ERROR or ON
EMPTY DEFAULT expression, which is the only case where they are
evaluated softly. ExecInitCoerceToDomain now emits a jump before each
constraint check that skips the remaining checks if an error has
already been saved.
* populate_domain, called from json_populate_type when coercing to a
domain. Return as soon as the conversion to the base type fails,
instead of going on to check the domain's constraints.
* populate_record, likewise. Stop populating the remaining columns as
soon as one of them fails.
* ExecEvalJsonCoercion. Return at once if the DEFAULT expression whose
result it would coerce has already failed, and leave throwing the
error to the COERCION_FINISH step that follows.
Reported-by: jian he <jian.universality@gmail.com>
Author: jian he <jian.universality@gmail.com>
Discussion: https://postgr.es/m/CACJufxHDK39zF+1zZF6prH_wtn1SDt74_mrqYk1Oo82cCu44ng@mail.gmail.com
Backpatch-through: 17 M src/backend/executor/execExpr.c
M src/backend/executor/execExprInterp.c
M src/backend/utils/adt/jsonfuncs.c
M src/test/regress/expected/sqljson_queryfuncs.out
M src/test/regress/sql/sqljson_queryfuncs.sql
Work around failure with SPARC in base backup code
commit : 59bc83d415ac9d1fe57dfb659d8b1e47debf2894
author : Michael Paquier <michael@paquier.xyz>
date : Thu, 29 Oct 2026 09:44:07 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Thu, 29 Oct 2026 09:44:07 +0900 As reported by buildfarm members thorntail and billbug, both running
SPARC, 4a92c8c29bd0 is making the base backup code unstable on such
platforms.
The only suspicion I have at this stage is a stack frame issue, pointing
to something unrelated to 4a92c8c29bd0, that the addition of an array
worth a size of PG_CONTROL_FILE_SIZE within perform_base_backup() is
able to trigger.
thorntail complained with a SIGBUS, pointing at misaligned access, with
a sporadic failure. billbug complained with a SIGSEGV (siglongjmp issue
with PG_ENSURE_ERROR_CLEANUP perhaps?) in a more stable fashion.
The code is changed to use a palloc0() instead, which should cool down
the problem with the stack, hopefully. Unfortunately, I could not test
this hypothesis directly, lacking an environment able to do so.
Discussion: https://postgr.es/m/auGYf3jXtzLrr4gA@paquier.xyz
Backpatch-through: 15 M src/backend/backup/basebackup.c
SQL/JSON: always rethrow soft errors from DEFAULT expressions
commit : 6dfe31d48a9b401f4b05fef63f6eb5b990823368
author : Amit Langote <amitlan@postgresql.org>
date : Thu, 29 Oct 2026 09:13:52 +0900
committer: Amit Langote <amitlan@postgresql.org>
date : Thu, 29 Oct 2026 09:13:52 +0900 The DEFAULT expression of an ON ERROR or ON EMPTY clause is compiled
with the JsonExpr's ErrorSaveContext in effect, so that any error
raised while evaluating it is saved rather than thrown and can be
reported later as "could not coerce ON ERROR expression (DEFAULT) to
the RETURNING type". That reporting is done by an
EEOP_JSONEXPR_COERCION_FINISH step, but ExecInitJsonExpr only added
that step when the DEFAULT expression needed a runtime coercion, or
when its top-level node was a CoerceViaIO or a CoerceToDomain. Those
are not the only nodes that can save an error: the ErrorSaveContext
applies to the whole expression tree, so a cast nested inside, say,
an OpExpr saves its error just the same. In that case there was
nothing to rethrow it and the query silently returned NULL instead
of failing, for example:
SELECT JSON_VALUE('"a"', '$' RETURNING int
DEFAULT (('a' || (random() * 0)::int)::int + 0)
ON ERROR);
Fix by adding the COERCION_FINISH step after every DEFAULT expression
that is evaluated softly, that is, whenever the ON ERROR behavior is
not ERROR.
Reported-by: jian he <jian.universality@gmail.com>
Author: jian he <jian.universality@gmail.com>
Discussion: https://postgr.es/m/CACJufxHDK39zF+1zZF6prH_wtn1SDt74_mrqYk1Oo82cCu44ng@mail.gmail.com
Backpatch-through: 17 M src/backend/executor/execExpr.c
M src/test/regress/expected/sqljson_jsontable.out
M src/test/regress/expected/sqljson_queryfuncs.out
M src/test/regress/sql/sqljson_jsontable.sql
M src/test/regress/sql/sqljson_queryfuncs.sql
Fix stale pg_index flags in the relcache for TOAST indexes
commit : b3852eec68aec0078460e5409db3f78f2f200ebf
author : Michael Paquier <michael@paquier.xyz>
date : Wed, 28 Oct 2026 14:22:27 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Wed, 28 Oct 2026 14:22:27 +0900 RelationReloadIndexInfo() re-reads the mutable pg_index fields
(indisready, indisvalid, indislive, etc.) only for indexes that are
not system relations. The TOAST index of a user table lives in
pg_toast and counts as one, but REINDEX CONCURRENTLY changes these
fields on it like on any other index.
A backend that had the TOAST indexes open when the invalidation
arrived kept indisready = false for the new index, and did not insert
its chunks there. Once the old index is dropped, reading such a value
fails with "missing chunk number 0 for toast value N in pg_toast_X".
The chunks remain in the TOAST table, only the index references were
gone, so a rebuild of the TOAST index would fix this issue.
This issue is fixed by re-reading the pg_index row for TOAST indexes,
filling in their relcache fields. This is the same as for an index on a
user table. Previouly, IsSystemRelation() was used, meaning the
pg_index data was not re-read for a TOAST relation or a relation with a
pinned OID (created by initdb). This check is meant for indexes whose
values never change, so IsCatalogRelation() is fine, implying that only
indexes created by initdb should be skipped (REINDEX CONCURRENTLY does
not work on catalogs).
This code is as old as 8ec943856a4, and was only used for CREATE INDEX
CONCURRENTLY. 5dc92b844e68, that has introduced REINDEX CONCURRENTLY,
has made possible to reach the problem with TOAST indexes due to these
getting concurrently built, validated and then swapped.
A test is added, able to reproduce the issue. It forces an insert
before a swap, with a second session that maintains the TOAST indexes
open while the new index becomes ready. This has been slightly
simplified by me compared to what the author has sent.
Oversight in 5dc92b844e68.
Reported-by: Saicharanreddy Chinthareddy <saicharh@amazon.com>
Author: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/CALj2ACUAv=7wjOV3pRFhQP4WHME_JazuhoCr01i4_F2XA=JKog@mail.gmail.com
Backpatch-through: 14 M src/backend/access/common/toast_internals.c
M src/backend/utils/cache/relcache.c
M src/test/modules/injection_points/Makefile
A src/test/modules/injection_points/expected/reindex_concurrently_toast.out
M src/test/modules/injection_points/meson.build
A src/test/modules/injection_points/specs/reindex_concurrently_toast.spec
Avoid torn copy of the control file in base backups
commit : 4aeefb26d62ba3408aa71bef65beba68a4af7025
author : Michael Paquier <michael@paquier.xyz>
date : Wed, 28 Oct 2026 08:42:57 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Wed, 28 Oct 2026 08:42:57 +0900 Base backups read the control file (pg_control) from disk without any
protection against concurrent writes by the server, making it exposed to
torn copies. Attempting to use such a control file at startup would
cause a failure with a CRC not matching.
Base backups now read the control file while holding ControlFileLock.
c558e6fd92f has done the same for some SQL functions. The contents of
the file are saved in memory and sent over the wire as-is.
Backpatch down to v15. This issue is older than v15 but v14 is left out
of the picture as it is going to be EOL soon, so it does not seem worth
taking a risk for.
Author: David Steele <david@pgbackrest.org>
Reviewed-by: Heikki Linnakangas <hlinnaka@iki.fi>
Discussion: https://postgr.es/m/d1697de1-eb7f-41bf-a4b7-bba7203df11f@pgbackrest.org
Backpatch-through: 15 M src/backend/backup/basebackup.c
Fix deparsing of AT TIME ZONE and similar as FROM-clause functions.
commit : 7933cf45a001322d87e679993c4586ef09bf7e08
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Tue, 27 Oct 2026 17:24:02 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Tue, 27 Oct 2026 17:24:02 -0400 ruleutils.c's looks_like_function() assumed that all COERCE_SQL_SYNTAX
cases produce output that looks like a function call (or at least
like one of the syntaxes the func_expr_windowless production accepts).
But AT TIME ZONE, AT LOCAL, OVERLAPS and IS xxx NORMALIZED print as
other sorts of a_expr, so those cases would produce invalid SQL syntax
when used in a FROM clause. Extend the logic to detect which specific
functions are safe or unsafe.
Author: Henson Choi <assam258@gmail.com>
Reviewed-by: Prashant Chinnam <crprashant@hotmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAAAe_zC68s-DwVX1o7C1HVcRk+Z+RTS6Vw19auZFfz=KaX8tZA@mail.gmail.com
Backpatch-through: 14 M src/backend/utils/adt/ruleutils.c
M src/test/regress/expected/create_view.out
M src/test/regress/sql/create_view.sql
Avoid truncated or zero-padded copies of replaced files in backups
commit : 1fbe31b9d25a6a31977a8a94be469fffe4597f77
author : Michael Paquier <michael@paquier.xyz>
date : Tue, 27 Oct 2026 13:53:52 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Tue, 27 Oct 2026 13:53:52 +0900 Base backups call stat() on each file while scanning the data directory,
and later send exactly that many bytes. If the file is replaced by for
example a concurrent rename(), the backup contains the new file
truncated or zero-padded to the size of the old one. This is harmless
for relation files as WAL replay repairs them. However, it causes harm
for non-relation files manipulated as temporary file renamed in place.
Here is a list of non-relation files that use a temporary file followed
by a final rename() (I may be missing some):
* postgresql.auto.conf
* pg_logical/replorigin_checkpoint (a damaged copy fails its CRC check
and prevents startup)
* pg_prewarm's autoprewarm.blocks
* pg_stash_advice.tsv.
When sending files that are not relation files, fstat() is called again
on the opened file and the refreshed stats are used for the tar header
and the backup manifest. Since the rename
is atomic, the opened file is always complete. Relation files keep using
the earlier stat, to avoid copying data that WAL replay would restore
anyway (because that's cheaper).
Back-patch down to v15. v14 is going to be EOL soon, so I have decided
to leave it out of the picture (risk/reward thing).
Author: David Steele <david@pgbackrest.org>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Reviewed-by: Shihao Zhong <zhong950419@gmail.com>
Discussion: https://postgr.es/m/CAGRkXqS_On-pLgcv6xjH8mvGhdcPmfWtythW3r8pYKUG+hsiuQ@mail.gmail.com
Backpatch-through: 15 M src/backend/backup/basebackup.c
doc: Fix option name in --filter description for pg_restore
commit : 5df90cccea76f7ba92bc01313cf75b11685f84e7
author : Daniel Gustafsson <dgustafsson@postgresql.org>
date : Mon, 26 Oct 2026 19:49:38 +0100
committer: Daniel Gustafsson <dgustafsson@postgresql.org>
date : Mon, 26 Oct 2026 19:49:38 +0100 The description of the "index" filter keyword referred to the --index
option as --indexes, which is a typo. Oversight in commit a5cf808be5.
Author: Taras Kloba <taras.kloba@gmail.com>
Reviewed-by: Daniel Gustafsson <daniel@yesql.se>
Discussion: https://postgr.es/m/CAODqpgofaVQBZ73rmPoj83Y1-6gbGhLiBKHD=82b1wiNkoF-AQ@mail.gmail.com
Backpatch-through: 17 M doc/src/sgml/ref/pg_restore.sgml
Fix PL/pgSQL's handling of anycompatiblemultirange.
commit : fa10d961d3748c4d995b892875b31003ae726817
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 26 Oct 2026 14:03:24 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 26 Oct 2026 14:03:24 -0400 When validating a PL/pgSQL function with polymorphic arguments or
result type, we substitute a plausible concrete type for each
polymorphic type. anycompatiblemultirange was missed in both places
that do this, when multiranges were added. As a result, CREATE
FUNCTION with an anycompatiblemultirange argument failed with
"PL/pgSQL functions cannot accept type anycompatiblemultirange" when
check_function_bodies is on.
Also, a function returning anycompatiblemultirange was compiled by
the validator with an int4 result type, and since the function cache
key doesn't include the result type, a later call in the same session
with int4range arguments reused that entry and failed trying to cast
the multirange result to integer.
Map anycompatiblemultirange to int4multirange, as is done for
anymultirange, to fix both issues.
Author: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAM2+6=VEXCo7tPaxX+9_zN8SJ9DiPp2NTj_sPGN+Tb0wWTBp=w@mail.gmail.com
Backpatch-through: 14 M src/pl/plpgsql/src/pl_comp.c
Fix nbtree multi-level page deletion WAL record bug.
commit : eea2ff2939177e0e3eee7fded2b2fcd612dc8122
author : Peter Geoghegan <pg@bowt.ie>
date : Mon, 26 Oct 2026 09:50:58 -0400
committer: Peter Geoghegan <pg@bowt.ie>
date : Mon, 26 Oct 2026 09:50:58 -0400 The second phase of a multi-level nbtree page deletion operation marks a
"top parent" internal page fully deleted, and updates the to-be-deleted
subtree's half-dead page. The leaf page needs to have its top parent
link updated in preparation for marking the next top parent page (the
subtree page one level down) fully deleted. The second phase reads the
leaf page's sibling links and releases its lock on the leaf, relocking
it only after it has examined the target page. This created a small
window where stale information could be WAL logged.
It was just about possible for a concurrent split of the half-dead
page's left sibling page to update the half-dead page's left sibling
link. _bt_unlink_halfdead_page neglected to re-copy the leaf page's
sibling links when it reacquired a lock on that page. The atomic action
that the WAL record represents doesn't really need to change any of its
sibling links, so this shouldn't have been a problem. However, since we
use REGBUF_WILL_INIT for the half-dead leaf page's buffer, using a stale
value led to corrupted half-dead leaf pages on standbys. This bug has
been shown to cause wal_consistency_checking failures.
To fix, re-read btpo_prev (and btpo_next) from the leaf once it has been
relocked, before the links are logged. That way we'll reliably WAL log
the current leafleftsib.
In practice this bug was very hard to hit. It required a concurrent
split at just the right/wrong time, with a multi-level page deletion.
Even then, the resulting corruption on standbys only persisted until the
half-dead page was marked fully deleted -- which typically happened
immediately after the corruption was introduced.
Oversight in commit efada2b8e9, which fixed a race condition in nbtree
page deletion.
Author: Peter Geoghegan <pg@bowt.ie>
Discussion: https://postgr.es/m/CAH2-Wznr2Gw5OQE9KMP_Xd92F9mV=gv7qE9Q6taAvfrKqsE5Qg@mail.gmail.com
Backpatch-through: 14 M src/backend/access/nbtree/nbtpage.c
pg_combinebackup: Reject out-of-range block numbers in incremental files
commit : 0bffea8f6030c5a5d11e5fda42f2bf9264a54ccf
author : Peter Eisentraut <peter@eisentraut.org>
date : Mon, 26 Oct 2026 10:22:02 +0100
committer: Peter Eisentraut <peter@eisentraut.org>
date : Mon, 26 Oct 2026 10:22:02 +0100 The header of an incremental file was only partially validated:
num_blocks and truncation_block_length were checked against
RELSEG_SIZE, but the block numbers stored after them were not checked
at all. A block number of 0xFFFFFFFF made
find_reconstructed_block_length() compute a reconstructed length of
zero, since it adds 1 to the block number in 32-bit arithmetic, and
reconstruct_from_incremental_file() then allocated zero-length
sourcemap and offsetmap arrays but still indexed them with the block
numbers taken from the file. In an assertions-enabled build, this
would be caught by an Assert, but otherwise it would index the arrays
out of bounds, typically crashing the process.
To fix, check the block numbers when the header is read, like the
other header fields.
Reported-by: Sajeeb Lohani working with TrendAI Zero Day Initiative M src/bin/pg_combinebackup/reconstruct.c
Fix partial two-phase files left by crash during checkpoint
commit : 15f57b683d236b7dad4d57d03d8b7bac0e63a204
author : Michael Paquier <michael@paquier.xyz>
date : Mon, 26 Oct 2026 17:14:35 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Mon, 26 Oct 2026 17:14:35 +0900 CheckPointTwoPhase() wrote each 2PC file in place, with O_TRUNC. A
crash mid-write, or a base backup copying the file at that point left an
empty or partial file that would make startup fail, most likely on an
"incorrect size of file" ERROR.
The 2PC files are now written through a temporary file that gets renamed
at the end, relying on the atomicity of the rename to make startup safe,
be it after a crash or a base backup.
Author: Shihao Zhong <zhong950419@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Reviewed-by: David Steele <david@pgbackrest.org>
Discussion: https://postgr.es/m/CAGRkXqS_On-pLgcv6xjH8mvGhdcPmfWtythW3r8pYKUG+hsiuQ@mail.gmail.com
Backpatch-through: 14 M src/backend/access/transam/twophase.c
Look through wrapper paths when collecting enforced clause serials.
commit : 3e914438a3ac4abfde004b8a850cb198f64613f3
author : Richard Guo <rguo@postgresql.org>
date : Mon, 26 Oct 2026 10:05:32 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Mon, 26 Oct 2026 10:05:32 +0900 get_param_path_clause_serials() handles join paths and Append paths
explicitly, and for anything else returns the ppi_serials of the
path's ParamPathInfo on the assumption that it is a baserel scan. But
Material, Memoize, Projection, Sort, IncrementalSort, Unique, Agg and
GroupingSets paths all inherit their subpath's ParamPathInfo, and that
assumption fails when the subpath is a join or Append.
This could drop a join clause for good. If a LATERAL UNION ALL
subquery references an outer relation in an expression that becomes an
EquivalenceClass member, the members get no child version of it, so
their parameterized scans cannot enforce the derived clause. The
appendrel's ParamPathInfo claims it nonetheless, and once a
Materialize sat on the Append, create_nestloop_path() dropped the
clause from the join above as already enforced, returning rows that
fail it.
To fix, make get_param_path_clause_serials() recurse through such
wrapper paths, since they enforce no clauses of their own. Also use
it in get_memoize_path(), which unsafely read ppi_serials directly for
its inner-unique check.
To try to clarify what's going on here, introduce a new function
get_wrapper_parampathinfo() that pathnode.c should use instead of
directly assuming that copying a wrapper path's subpath's param_info
is what to do. This is just cosmetic at present but might become
less so in future.
Back-patch to v16, where the serial-based detection of enforced
clauses was introduced.
Author: Richard Guo <guofenglinux@gmail.com>
Co-authored-by: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: shihao zhong <zhong950419@gmail.com>
Reviewed-by: Matheus Alcantara <matheusssilv97@gmail.com>
Discussion: https://postgr.es/m/CAMbWs49jVFRZ7oOgMK9zYt7d=SLxpiz=dN=QMu5iV8TS+9T0AA@mail.gmail.com
Backpatch-through: 16 M src/backend/optimizer/path/joinpath.c
M src/backend/optimizer/util/pathnode.c
M src/backend/optimizer/util/relnode.c
M src/include/nodes/pathnodes.h
M src/include/optimizer/pathnode.h
M src/test/regress/expected/join.out
M src/test/regress/sql/join.sql
Set HEAP_XMAX_INVALID for tuples updated by aborted subtransaction
commit : 08f0cec8fb55093bd225b77bb45983c6001ad8de
author : Michael Paquier <michael@paquier.xyz>
date : Mon, 26 Oct 2026 09:54:44 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Mon, 26 Oct 2026 09:54:44 +0900 HeapTupleSatisfiesVacuumHorizon() did not set HEAP_XMAX_INVALID on a
tuple inserted, then updated by a subtransaction that rolled back.
rewriteheap.c relies on this hint to tell if such tuples should be
treated as updated and would drop them, causing rewrites to lose some
tuples.
HeapTupleSatisfiesVacuumHorizon() is updated to set the hint
HEAP_XMAX_INVALID, so as heap AM is able to redirect such a tuple to its
correct route, and does not lose it.
This issue impacted both CLUSTER (down to v14) and REPACK [USING] (down
to v19).
Reported-by: Alperen Keles <alpkeles99@gmail.com>
Author: Shihao Zhong <zhong950419@gmail.com>
Author: Michael Paquier <michael@paquier.xyz>
Reported-by: Zhijie Hou <houzhijie22@gmail.com>
Reported-by: Takeshi Ideriha <iderihatakeshi@gmail.com>
Reported-by: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Discussion: https://postgr.es/m/19760-8f9a0eba17bc8c92@postgresql.org
Backpatch-through: 14 M src/backend/access/heap/heapam_visibility.c
M src/test/regress/expected/cluster.out
M src/test/regress/sql/cluster.sql
Fix tab completion for concurrent matview refresh
commit : 127c686b1b543951d0bfb3bedad4855ac059ea16
author : Daniel Gustafsson <dgustafsson@postgresql.org>
date : Mon, 26 Oct 2026 00:18:48 +0100
committer: Daniel Gustafsson <dgustafsson@postgresql.org>
date : Mon, 26 Oct 2026 00:18:48 +0100 Refreshing materialized views with CONCURRENT cannot use WITH NO DATA,
but the tab completion supported this combination which is rejected by
the backend.
Author: Sho Ito <i.sho.0628@gmail.com>
Reviewed-by: Daniel Gustafsson <daniel@yesql.se>
Discussion: https://postgr.es/m/CAG7L5+7JOn_tjLjS1PJFTARtEybeJ1-aAAftZZ5L25_QZrjP4w@mail.gmail.com
Backpatch-through: 14 M src/bin/psql/tab-complete.c
Fix "variable set but not used" warning with unnamed POSIX semaphores.
commit : 313288df8f9f34026cd33980e838d22b8b0001d9
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Sun, 25 Oct 2026 18:18:30 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Sun, 25 Oct 2026 18:18:30 -0400 Late-model clang is able to notice, when building posix_sema.c with
USE_NAMED_POSIX_SEMAPHORES not defined, that the static variable
nextSemKey is set once and read nowhere. Rearrange the #ifdef's
to not declare or set it unless USE_NAMED_POSIX_SEMAPHORES. As a
small side benefit, we can skip a stat() call altogether if we're
not going to need it to initialize this variable, which I think
clarifies the code a fraction.
Per buildfarm member fritillary. M src/backend/port/posix_sema.c
Retain locks after marking the partitioned index valid
commit : bd807012669ed8573317562b78c1221e4fabe373
author : Álvaro Herrera <alvherre@kurilemu.de>
date : Sun, 25 Oct 2026 10:23:46 +0100
committer: Álvaro Herrera <alvherre@kurilemu.de>
date : Sun, 25 Oct 2026 10:23:46 +0100 Commit 8b08f7d4820f (pg11-era), for no apparent reason, made
validatePartitionedIndex() release the locks on the parent partitioned
index and table when marking the parent index valid after marking the
index partition as valid. This leads to race condition, which
particularly manifest when restoring from pg_dump in parallel mode, for
instance causing restore of FKs to fail if the PK that they depend on is
being concurrently validated. Fix by retaining those locks until
transaction commit, as we normally do everywhere in DDL code.
Per buildfarm failures.
Author: Andrey Borodin <x4mmm@yandex-team.ru> (in absentia)
Diagnosed-by: Kirill Reshke <reshkekirill@gmail.com>
Backpatch-through: 14
Discussion: https://postgr.es/m/l4joqljvbou26unhplkdj7m5olxn7rsy2loqlcta44nie5k3bs@zudmva7han7g M src/backend/commands/tablecmds.c
Fix GIN index search for root strings in strict jsonpath queries.
commit : 19656b77d16d996e94d01ade59a6132826b8c347
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Sat, 24 Oct 2026 14:27:12 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Sat, 24 Oct 2026 14:27:12 -0400 A jsonb_ops index scan for 'strict $ == "a scalar"' missed a matching
root string. Root scalars are stored in single-element arrays, so
gin_extract_jsonb() indexes strings with JGINFLAG_KEY. In strict mode,
jsonb_ops__extract_nodes() instead searches for JGINFLAG_STR at the
root. Leave gin_extract_jsonb() alone (else we'd force people to
reindex) and make jsonb_ops__extract_nodes() search for JGINFLAG_KEY
instead.
Author: Rui Zhao <zhaorui126@gmail.com>
Reviewed-by: shihao zhong <zhong950419@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAHWVJhHH0k3PxWmS==V1p_PONOMUaSm6LjUV487fwnOqd0jTQw@mail.gmail.com
Backpatch-through: 14 M src/backend/utils/adt/jsonb_gin.c
M src/test/regress/expected/jsonb.out
M src/test/regress/sql/jsonb.sql
Accept empty tsquery in binary input.
commit : e05b7e2a45c7a151ad3f8771ba7f5d8b4ddd8d0c
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Thu, 22 Oct 2026 13:51:27 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Thu, 22 Oct 2026 13:51:27 -0400 tsqueryrecv() calls findoprnd() even when the input has no query
items, so it fails on an empty tsquery, which is legal data.
This breaks, for example, binary COPY of a table containing an
empty tsquery.
Skip the operand walk for an empty query, as parse_tsquery() does.
Bug: #19778
Reported-by: Shay Rojansky <roji@roji.org>
Author: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/19778-1befc1fba593ab1b@postgresql.org
Backpatch-through: 14 M src/backend/utils/adt/tsquery.c
Fix argument handling in pg_indexam_progress_phasename()
commit : 8af1a12c9ea0e19d11c285fc80a9e434069a1a91
author : Michael Paquier <michael@paquier.xyz>
date : Thu, 22 Oct 2026 14:39:11 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Thu, 22 Oct 2026 14:39:11 +0900 The function is declared in pg_proc.dat a bigint as its second input
argument, but the function's code used PG_GETARG_INT32().
This is an issue in 32-bit architectures in stable branches (up to v18)
where bigint is still passed by reference, reporting bogus numbers.
Since 2a600a93c7b (v19 and HEAD), this is less of a problem as Datums
became 8-byte wide everywhere.
Oversight in ab0dfc961b6.
Author: Markos Fountoulakis <markos@planetscale.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/e791bbb7-af3a-4a91-a5bb-c744e5c3ef54@planetscale.com
Backpatch-through: 14 M src/backend/utils/adt/amutils.c
psql: Fix tab completion for DROP INDEX CONCURRENTLY
commit : 4cd931b4314368618dc747c177af2c54352a0203
author : Fujii Masao <fujii@postgresql.org>
date : Thu, 22 Oct 2026 10:49:40 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Thu, 22 Oct 2026 10:49:40 +0900 psql offered CASCADE after DROP INDEX CONCURRENTLY and an index name,
even though CASCADE is not supported with CONCURRENTLY. Offer only
RESTRICT in this case, while retaining both CASCADE and RESTRICT
for ordinary DROP INDEX.
Backpatch to all supported versions.
Author: Sho Ito <i.sho.0628@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Shihao Zhong <zhong950419@gmail.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/CAG7L5+7KXgvJUGCGXzyQrt7UysC9iytaYzx-7YyQTFYXjHVTWQ@mail.gmail.com
Backpatch-through: 14 M src/bin/psql/tab-complete.c
Fix slot names in the standby slot removal check
commit : 4ec789e1aa5006ac7c66f9e06fb3495f32459301
author : Fujii Masao <fujii@postgresql.org>
date : Thu, 22 Oct 2026 09:24:28 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Thu, 22 Oct 2026 09:24:28 +0900 The DROP DATABASE test in 035_standby_logical_decoding.pl created
slots with the prefix "drop_db_" but checked for their removal using
"drop_db". Sined names never existed, the checks passed
even if the actual slots remained.
Fix this by using the correct prefix when checking for slot removal.
Backpatch to v16, where this testce the check was introduced.
Author: Fujii Masao <masao.fujii@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/CAHGQGwEnuLZnFRF5CzK47n_hXT-iCXnzirxG2v_V-=QNMLkpEA@mail.gmail.com
Backpatch-through: 16 M src/test/recovery/t/035_standby_logical_decoding.pl
Fix a race in 035_standby_logical_decoding.pl
commit : b8ab5237eea9f44632e387880c9e5babef5fa0eb
author : Fujii Masao <fujii@postgresql.org>
date : Thu, 22 Oct 2026 09:23:18 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Thu, 22 Oct 2026 09:23:18 +0900 Buildfarm animals francolin and kestrel reported intermittent failures
in 035_standby_logical_decoding.pl.
The test checks that replaying DROP DATABASE on a standby terminates
pg_recvlogical with the expected recovery-conflict message and removes
the database's replication slots. To exercise this while pg_recvlogical
is streaming, the test must wait for streaming to start before dropping
the database on the primary.
Previously, the test treated an active slot in pg_replication_slots as
confirmation that pg_recvlogical was streaming. However, the slot can
become active before pg_recvlogical has entered streaming mode. If
DROP DATABASE is replayed during this window, pg_recvlogical can report
a connection-closed error instead of the recovery-conflict message,
causing the test to fail.
Run pg_recvlogical with --verbose and wait for "streaming initiated"
instead of checking whether the slot is active. This ensures that
pg_recvlogical has entered streaming mode before the database is
dropped on the primary.
Back-patch to v16, where this test was introduced.
Reported-by: Alexander Lakhin <exclusion@gmail.com>
Author: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Reviewed-by: Alexander Lakhin <exclusion@gmail.com>
Reviewed-by: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/a50ff89b-fb2c-4912-8f89-50dd0bd0916b@gmail.com
Backpatch-through: 16 M src/test/recovery/t/035_standby_logical_decoding.pl
Reject currtid2() on unlogged tables during recovery.
commit : 3e53104dc32f78b4957beb582bed6e1bd19c685c
author : Masahiko Sawada <msawada@postgresql.org>
date : Wed, 21 Oct 2026 11:10:44 -0700
committer: Masahiko Sawada <msawada@postgresql.org>
date : Wed, 21 Oct 2026 11:10:44 -0700 Commit 1e707d32564 made COPY TO report a proper error for unlogged
tables during recovery. currtid2() has the same problem: it reads the
table directly without going through the planner, so on a standby it
failed with a low-level error for an unlogged table:
ERROR: could not open file "base/5/16384": No such file or directory
Check for this case and report the same error as SELECT. No test is
added, as currtid2() is an undocumented internal function.
Backpatch to all supported versions, like commit 1e707d32564.
Reported-by: Masahiko Sawada <sawada.mshk@gmail.com>
Author: Jim Jones <jim.jones@uni-muenster.de>
Discussion: https://postgr.es/m/CAD21AoDNa7pL760_23AN_wYPMkWXCt+Kw0_vJAcci4R5DoCQGg@mail.gmail.com
Backpatch-through: 14 M src/backend/utils/adt/tid.c
Reject COPY TO of unlogged tables during recovery.
commit : 17f66a68179bfe91a57987ffafafa9063c3907c7
author : Masahiko Sawada <msawada@postgresql.org>
date : Wed, 21 Oct 2026 10:44:12 -0700
committer: Masahiko Sawada <msawada@postgresql.org>
date : Wed, 21 Oct 2026 10:44:12 -0700 Unlogged tables have no data on a standby, so reading one there must
fail. SELECT and COPY (SELECT ...) TO already report a clear error,
but COPY with a table name read the table directly and failed with a
low-level error instead:
ERROR: could not open file "base/5/16384": No such file or directory
Check for this case in COPY TO and report the same error as SELECT. On
v19 and later, also check each partition when copying from a
partitioned table, so that an unlogged partition is reported too.
Backpatch to all supported versions, since the low-level error looks
like data corruption.
Author: Jim Jones <jim.jones@uni-muenster.de>
Reviewed-by: Zsolt Parragi <zsolt.parragi@percona.com>
Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com>
Discussion: https://postgr.es/m/b6d71815-d362-4579-bf07-73eca01aee6d@uni-muenster.de
Backpatch-through: 14 M src/backend/commands/copyto.c
M src/test/recovery/t/001_stream_rep.pl
findtimezone: update Windows timezone name list for recent events.
commit : 5f0f69019e3375c44e516f2d152f451cd9df0197
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Wed, 21 Oct 2026 11:52:51 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Wed, 21 Oct 2026 11:52:51 -0400 The main portion of findtimezone.c's win32_tzmap[] is now directly
generated from the CLDR project's current windowsZones.xml file (using
a script available in the discussion thread, but not yet merged into
our tree). Windows zone names that are no longer present in CLDR are
kept, but are moved to a separate list at the end of the array to
simplify future management.
The important changes that this brings are inclusion of new zone names
for Canada's upcoming timekeeping changes. In addition, this tracks a
few minor changes that CLDR has made in other entries:
"Central Asia Standard Time" now maps to Asia/Bishkek, and
"Mountain Standard Time (Mexico)" now maps to America/Mazatlan.
Reported-by: CyberNative AI <ceo@cybernative.ai>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/179219370621.3670768.16110484606338109870@cybernative.ai
Backpatch-through: 14 M src/bin/initdb/findtimezone.c
Fix lateral references in sort keys added by createplan.c
commit : 2b66c9c8aed91c494a417101ba67f2fb61e86493
author : Richard Guo <rguo@postgresql.org>
date : Wed, 21 Oct 2026 18:03:02 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Wed, 21 Oct 2026 18:03:02 +0900 When prepare_sort_from_pathkeys() cannot find a pathkey's expression
in the input plan's targetlist, it adds a resjunk entry computing an
EquivalenceClass member's expression. It copied that expression
as-is, without replacing lateral references with nestloop Params the
way build_path_tlist() does for the rest of the targetlist.
This matters when the sort key contains a PlaceHolderVar whose
expression has a lateral reference, and the sort is placed directly
atop the plan node that evaluates the PlaceHolderVar. The targetlist
already contains the PlaceHolderVar, so the sort key is considered
computable, but the entry added for it still has the outer relation's
Var inside the PlaceHolderVar. If the node is a join, setrefs.c fails
with "variable not found in subplan target lists". If it is a scan,
setrefs.c leaves the Var in place, and the executor fetches it from
the scan tuple, leading to wrong results.
To fix, pass root to prepare_sort_from_pathkeys() and apply
replace_nestloop_params() to the expression before adding it to the
targetlist.
Back-patch to all supported branches.
Author: Richard Guo <guofenglinux@gmail.com>
Discussion: https://postgr.es/m/CAMbWs49LWNm7_OxuW1V71wLi+Ev3KbfY1bMY36uydNKwSPnbtQ@mail.gmail.com
Backpatch-through: 14 M src/backend/optimizer/plan/createplan.c
M src/test/regress/expected/join.out
M src/test/regress/sql/join.sql
Fix sorting a child rel by an expression containing a PlaceHolderVar
commit : 05d073e2cd1ea956ad723b4bb4f571f91cd66873
author : Richard Guo <rguo@postgresql.org>
date : Wed, 21 Oct 2026 15:37:31 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Wed, 21 Oct 2026 15:37:31 +0900 When building sort keys for a child relation, such as the per-child
sort columns of a MergeAppend over partitionwise child joins,
prepare_sort_from_pathkeys() may need to compute an EquivalenceClass
member from the child's targetlist. find_computable_ec_member()
accepts a member if all the Vars and quasi-Vars it contains appear in
that targetlist. It ignores child members that don't belong to the
given relids, but it can still choose a parent member. Usually that
is harmless, since a parent member's Vars never appear in a child's
targetlist. PlaceHolderVars are different: equal() ignores their
phexpr and phrels, so a parent PlaceHolderVar matches its translated
copy in the child's targetlist. As a result, an untranslated parent
expression could be added to the child plan's targetlist, leading to
"variable not found in subplan target lists" errors from setrefs.c.
To fix, when relids is given, also ignore parent members that don't
belong to it. This changes nothing for a non-child relation, because
any member computable from its targetlist belongs to it anyway. When
relids is NULL, only child members are filtered, as before.
Also update the comments for prepare_sort_from_pathkeys() to reflect
that 'relids' can identify a non-child relation being sorted, which
has long been the case.
Back-patch to all supported branches.
Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: wenhui qiu <qiuwenhuifx@gmail.com>
Discussion: https://postgr.es/m/CAMbWs48bLLEGy6YC+n01w6=YR=28cj_kpoKcAgOHQR7jbtLb6w@mail.gmail.com
Backpatch-through: 14 M src/backend/optimizer/path/equivclass.c
M src/backend/optimizer/plan/createplan.c
M src/test/regress/expected/partition_join.out
M src/test/regress/sql/partition_join.sql
Fix cleanup of reused pgstats entries in pgstat_release_entry_ref()
commit : 1613efa1a5eb2ef68104c64b9aff615887e8e015
author : Michael Paquier <michael@paquier.xyz>
date : Wed, 21 Oct 2026 10:55:33 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Wed, 21 Oct 2026 10:55:33 +0900 When a stats entry is reinitialized and dropped again while a backend
still holds a reference from an older generation, the backend releasing
the last reference found a generation mismatch and left behind an entry
marked as dropped with a refcount of 0. Nothing would free it, and the
checkpointer would trigger an assertion at shutdown when flushing the
stats.
Looking more at the issue, using some hardcoded sleeps before the
dshash_find() that takes an exclusive lock when releasing an entry with
some concurrent activity, there was more going on:
- A missing entry could be reported as an error at backend exit, error
that should not be hit even if the entry was reinitialized and dropped
concurrently.
- The generation check was not correct for the case of 0 due to the
first issue. A generation of 0 refers to an entry that has been freshly
created.
This commit reworks the entry release to be safer concurrent-wise when
an entry targetted for release is found as already existing:
- If marked as dropped without refount at 0, free it.
- In other cases, ignore it. The entry has been reinitialized or
created again with the same key, and is still in use.
Sami has posted a basic test case (equivalent added in this commit) that
gives a way to reproduce the assertion failure on backend exit, which is
a consequence of the incorrect handling of entry releases.
Reported-by: Sami Imseih <samimseih.pg@gmail.com>
Reviewed-by: Sami Imseih <samimseih.pg@gmail.com>
Discussion: https://postgr.es/m/CAN12+YJXE+rEOVo74uM5Ofbj_t_m0DHvd_Mo4ppeHV=aDvvXoQ@mail.gmail.com
Backpatch-through: 15 M contrib/test_decoding/t/001_repl_stats.pl
M src/backend/utils/activity/pgstat_shmem.c
Fix cross-version upgrade tests for commits 0497318e4 et al.
commit : 80da124a4f338a612550180dac86eb33850e2254
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Tue, 20 Oct 2026 18:49:30 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Tue, 20 Oct 2026 18:49:30 -0400 Dumps taken from unpatched versions (pre-v14) will not have the AS
keyword that ruleutils.c now prints, causing a harmless diff in dump
output. Adjust the cross-version upgrade's old dump to make it match.
While I'm here, move the stanza that I hastily put in a non-optimal
place in commit f8ab6c622. It's worth fixing because it messed up
the logical structure of adjust_database_contents, making it
un-obvious where to put future additions.
Per buildfarm. M src/test/perl/PostgreSQL/Test/AdjustUpgrade.pm
In deparsing, parenthesize IS DOCUMENT where it is an operand.
commit : 467bb7ea7247d2657470bb8fba1d306d8cf7a57d
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Tue, 20 Oct 2026 14:29:02 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Tue, 20 Oct 2026 14:29:02 -0400 isSimpleNode() and related code treated every XmlExpr as
self-delimiting. Most of them are, because they look like function
calls with some extra decoration. But "x IS DOCUMENT" is not: it is
a postfix test, like IS NULL. Printed as the operand of another
operator, or as the operand of a postfix test, it was therefore not
parenthesized and could not be parsed back, or was parsed as something
unintended. Under-parenthesization occurred even in non-pretty-print
cases, so that this could break dump/restore workflows.
To fix, treat IS DOCUMENT as neither simple nor function-like, and
give it the same treatment as other postfix tests.
Also pass the single argument of IS DOCUMENT, not the argument list,
to get_rule_expr_paren(). This avoids unnecessary parenthesization
of the argument.
Author: Henson Choi <assam258@gmail.com>
Reviewed-by: Prashant Chinnam <crprashant@hotmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAAAe_zDuPSkc8ihCctV67Lxbqock+S4wqCHHo8xxZ1t4teXo0g@mail.gmail.com
Backpatch-through: 14 M src/backend/utils/adt/ruleutils.c
M src/test/regress/expected/create_view.out
M src/test/regress/sql/create_view.sql
Fix two issues in deparsing.
commit : 91246c72695cdddf62003f6bf4c4ade511f134f4
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 19 Oct 2026 17:51:09 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 19 Oct 2026 17:51:09 -0400 If the target relation of UPDATE, DELETE, or MERGE has an alias,
we printed that "bare" (without AS or forced quoting), the same
as we do in FROM. However, if that alias is "set", there's an
ambiguity with the SET keyword that must follow the relation
specification. The parser would choose the wrong interpretation,
so that the dumped query would fail to reload. The least surprising
and most future-proof fix seems to be to always print AS in these
contexts, as we already did in INSERT.
Also, we were insufficiently careful about parenthesizing the
left-hand argument of a SubLink (IN, op ANY/ALL, or row comparison),
so that the printed expression might not parse back with the intended
meaning depending on operator precedence. This is only a problem in
"pretty-print" mode, since non-pretty mode fully parenthesizes every
operator. So it isn't a hazard for pg_dump, but it still seems like
something to fix. To do so, switch to using get_rule_expr_paren.
Presently that will add parens in some cases where it wouldn't really
be necessary, but that's better than the reverse, and it arguably
is helpful for readability anyway.
Author: Henson Choi <assam258@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAAAe_zDqXin5NeB5atRCbZqUdcMOeyXRqhan+tVp0F30OCURnw@mail.gmail.com
Discussion: https://postgr.es/m/CAAAe_zCy+D45Qd_br484F+V34y0BVA=hp_7cb=u6HysyMkE3UQ@mail.gmail.com
Backpatch-through: 14 M src/backend/utils/adt/ruleutils.c
M src/test/regress/expected/create_view.out
M src/test/regress/expected/rules.out
M src/test/regress/sql/create_view.sql
Fix a few mistakes in partition aggregate tests
commit : 503573b853962373dff7f2eb85f9007cba8cf312
author : David Rowley <drowley@postgresql.org>
date : Tue, 20 Oct 2026 10:34:26 +1300
committer: David Rowley <drowley@postgresql.org>
date : Tue, 20 Oct 2026 10:34:26 +1300 The pattern seems to be to EXPLAIN the query to ensure we get the
expected partition-wise aggregate plan, then run the same query to check
the results are what we expect. A couple of places didn't use the same
query between the EXPLAIN and the checking of the results, which
effectively voided the plan check.
Additionally, a comment claimed array_agg() didn't support partial mode,
but that was made not true in 16fd03e95.
Author: Matthias van de Meent <boekewurm@gmail.com>
Reviewed-by: Pierre Forstmann <pierre.forstmann@gmail.com>
Reviewed-by: David Rowley <dgrowleyml@gmail.com>
Discussion: https://postgr.es/m/CAEze2WgjJUiqV-VZVrWqYz+ETLtd4oKk2gbvBWxf1Fo0is5XVQ@mail.gmail.com
Backpatch-through: 14 M src/test/regress/expected/partition_aggregate.out
M src/test/regress/sql/partition_aggregate.sql
Recompute allocChunkLimit when reusing an AllocSet context
commit : 63de1f8fa42060e10326d2d9a0d48e559594ab86
author : David Rowley <drowley@postgresql.org>
date : Mon, 19 Oct 2026 19:43:55 +1300
committer: David Rowley <drowley@postgresql.org>
date : Mon, 19 Oct 2026 19:43:55 +1300 AllocSet contexts with different maxBlockSize values can have the same
freeListIndex. When reusing a context from one of the context_freelists,
we updated maxBlockSize, but we failed to adjust allocChunkLimit, even
though that ordinarily depends on maxBlockSize. This could result in
some allocations being put on an external block when they shouldn't have
been, resulting in excessive malloc() calls, or if the allocChunkLimit was
left set higher than it should be, allocations could have resulted in
memory not being free()'d after pfree() due to the chunk having been put
on a multi-chunk block (these would still later be cleaned up when the
context is reset, so no memory leaks).
The repercussions of this are only inefficient use of memory and perhaps
some performance instability depending on which context happened to be
top of the freelist when a new AllocSet context was created.
To fix, we move the allocChunkLimit calculation code into a helper
function and call that to reset the allocChunkLimit when reusing a
context.
Author: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Donghang Lin <donghanglin@gmail.com>
Reviewed-by: David Rowley <dgrowleyml@gmail.com>
Discussion: https://postgr.es/m/1D54961E-6BEF-411A-BFAD-610C3B18D7E7@gmail.com
Backpatch-through: 14 M src/backend/utils/mmgr/aset.c
Fix use-after-free in jsonb_set_lax for Const NULL new_value
commit : 723075b0315d7a71c8d99252938f3582c6c1cee9
author : John Naylor <john.naylor@postgresql.org>
date : Mon, 19 Oct 2026 11:29:41 +0700
committer: John Naylor <john.naylor@postgresql.org>
date : Mon, 19 Oct 2026 11:29:41 +0700 Commit a83586b5543 decided to scribble on the caller's fcinfo but failed
to take into account that const arguments are written once at expression
init and assumed never to change.
The use_json_null branch overwrote fcinfo->args[2] with a jsonb null
built in the per-tuple expression context. In later rows,
args[2].isnull is still false and args[2].value still holds that
pointer, but the per-tuple context has been reset. Later rows find
args[2].isnull = false, short-circuit past the NULL handling at the top
of the function, and jump straight to jsonb_set with the stale pointer
regardless of their own null_value_treatment argument, which is never
read.
To fix, call jsonb_set through its own call frame instead. Arguments 0,
1 and 3 were already checked non-NULL above, so we can use
DirectFunctionCall4.
Add regression tests covering use_json_null followed by delete_key and
return_target, and a multi-row default-treatment query.
Reported-by: Evan Si <evsi@amazon.com>
Discussion: https://postgr.es/m/CANWCAZY-PS=ev8TiiV1j=NoR2xLopEL8fDq0fyQZahHiP5b+Pw@mail.gmail.com
Backpatch-through: 14 M src/backend/utils/adt/jsonfuncs.c
M src/test/regress/expected/jsonb.out
M src/test/regress/sql/jsonb.sql
Don't remove an RTE_RESULT needed by a nested PlaceHolderVar
commit : 94035972af3f10cdaf9ff2b5d232ad5f45463469
author : Richard Guo <rguo@postgresql.org>
date : Mon, 19 Oct 2026 11:04:17 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Mon, 19 Oct 2026 11:04:17 +0900 When remove_useless_result_rtes() removes an RTE_RESULT, any
PlaceHolderVars that were to be evaluated there must be evaluated
somewhere else. If the RTE_RESULT is the nullable side of an outer
join, there is no other place that would preserve the PHVs' nulling
behavior, so find_dependent_phvs() refuses the removal if any such PHV
exists anywhere in the query. If the RTE_RESULT is a child of an
inner join, its PHVs can instead be evaluated at the join of all its
sibling rels, unless a sibling needs them before that join is formed.
find_dependent_phvs_in_jointree() checks for that by looking for such
PHVs in the join quals and LATERAL RTEs of the siblings.
That misses a PHV that is evaluated at some but not all of the sibling
rels and contains one of the RTE_RESULT's PHVs. Such a PHV can appear
anywhere in the query, such as in the targetlist. This can happen
when pulled-up expressions are wrapped in PHVs because of outer joins
or grouping sets. Once the RTE_RESULT is removed, the outer PHV needs
a value that is only available at a join above its own evaluation
level. The outer PHV's rel thus becomes laterally dependent on the
other sibling rels. If one of those rels laterally references it in
turn, we end up with a rel that is laterally dependent on itself,
causing an assertion failure in create_lateral_join_info().
To fix, also search the whole query for PHVs that are evaluated at
some but not all of the sibling rels, and refuse to remove the
RTE_RESULT if any of them contains one of its PHVs. An outer PHV that
is evaluated at all of the sibling rels is not a problem, since the
moved PHV is available there, so we don't block removal in that common
case.
Back-patch to all supported branches.
Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Discussion: https://postgr.es/m/CAMbWs4_hLOsW8_SePp_3EmYTLX8Oc4zEZEvypyPRszpoMhDdGQ@mail.gmail.com
Backpatch-through: 14 M src/backend/optimizer/prep/prepjointree.c
M src/test/regress/expected/join.out
M src/test/regress/sql/join.sql
Fix bogus outer-join commutation with lateral refs in PlaceHolderVars
commit : 65e944d92b082cc16e2265a91bab8b73934a0e74
author : Richard Guo <rguo@postgresql.org>
date : Sat, 17 Oct 2026 17:20:01 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Sat, 17 Oct 2026 17:20:01 +0900 make_outerjoininfo() decides whether outer join identity 3 applies
between an outer join and a lower one in its LHS by checking that the
join clause is strict for the lower join's RHS. It relied on the
clause then necessarily referencing that RHS. That is not so when the
strictness comes through a PlaceHolderVar: find_nonnullable_rels()
looks inside the PHV's expression and reports rels it references
laterally, while pull_varnos() reports only where the PHV is
evaluated.
In that case we marked the two joins as commutable although the clause
does not use the lower join's RHS at all. Joinrels for the same set
of base rels could then be built both with and without the upper
join's relid, leading to an assertion failure in
standard_join_search(). The bogus commutation also caused an extra
clone of the upper join's clause to be generated, which could be
applied at a higher join in addition to the clone already applied
below it. In non-assert builds, this could lead to planner errors
such as "variable not found in subplan target lists".
To fix, ignore rels that the clause is strict for but does not
reference, consistent with clause_relids treating a PHV as computed at
its ph_eval_at.
Back-patch to v16, where this commutation logic was introduced.
Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Tender Wang <tndrwang@gmail.com>
Discussion: https://postgr.es/m/CAMbWs49xvh4-uPv3rxWkk_rtMT1ic+psHvEAxaFEXu2Rf-32Tw@mail.gmail.com
Backpatch-through: 16 M src/backend/optimizer/plan/initsplan.c
M src/test/regress/expected/join.out
M src/test/regress/sql/join.sql
Fix skipping of join quals for appendrel children
commit : 55b9f54dcb467cf6b466dbd96bff4b66fd60065b
author : Richard Guo <rguo@postgresql.org>
date : Sat, 17 Oct 2026 10:30:55 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Sat, 17 Oct 2026 10:30:55 +0900 Commit 767c59895 made set_append_rel_size() skip join quals that came
from above an outer join that can null the parent rel. Such quals are
of no use while generating paths for the child, and skipping them also
keeps them away from adjust_appendrel_attrs(), which cannot apply
nullingrels to a non-Var translation.
However, it identified such quals by looking at their clause_relids.
That misses a reference to the parent rel that is nulled by such an
outer join but appears only within a PlaceHolderVar, because
pull_varnos() reports only where the PHV is evaluated. Such a qual
could then reach adjust_appendrel_attrs(), which failed with "failed
to apply nullingrels to a non-Var" if the referenced output of a UNION
ALL member was not a plain Var.
To fix, also skip quals whose clause_relids overlap the parent rel's
lateral_referencers. A reference to the parent rel within a PHV that
is evaluated elsewhere is necessarily a lateral reference, so this
catches all such quals. These quals are no use for the child either,
since they can never be enforced by a scan of the parent rel; this is
the same reasoning join_clause_is_movable_to() uses.
Back-patch to v16, where commit 767c59895 was introduced.
Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: wenhui qiu <qiuwenhuifx@gmail.com>
Discussion: https://postgr.es/m/CAMbWs4_fzrrN9G6-wLwwrdM-n=fwj3Uhr1zkgj3o91EEC-CuEA@mail.gmail.com
Backpatch-through: 16 M src/backend/optimizer/path/allpaths.c
M src/test/regress/expected/join.out
M src/test/regress/sql/join.sql
Avoid spurious failures in the login event trigger recovery test
commit : e4cc408acd2d5ef56d717930b0873eedb4c9ba2e
author : Fujii Masao <fujii@postgresql.org>
date : Sat, 17 Oct 2026 00:18:39 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Sat, 17 Oct 2026 00:18:39 +0900 Buildfarm member skink reported failures in
053_standby_login_event_trigger.pl. The checks that dathasloginevt was
cleared on the primary and that the cleared flag reached the standby
both found the flag still set.
The test assumed that the first login after dropping the trigger would
clear the flag. However, EventTriggerOnLogin() acquires the database
lock conditionally to avoid delaying connections for this cleanup.
Concurrent connection startup, including that of an autovacuum worker,
can hold a conflicting lock and cause the cleanup to be skipped. The
standby correctly retained the flag because it had not been cleared
on the primary.
Fix this by using poll_query_until() to retry with fresh connections
to the test database until the flag is cleared, giving the login cleanup
repeated opportunities to acquire the lock.
Backpatch to v17, like commit bec61f59354, which added this test.
Reported-by: Alexander Lakhin <exclusion@gmail.com>
Author: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/09561429-50c1-4d06-a262-300c7a845175@gmail.com
Backpatch-through: 17 M src/test/recovery/t/053_standby_login_event_trigger.pl
doc: Correct the description of server behavior after an OOM kill
commit : daad43f7ac5d12c4c57a348703b1f2e0db612763
author : Fujii Masao <fujii@postgresql.org>
date : Fri, 16 Oct 2026 22:42:40 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Fri, 16 Oct 2026 22:42:40 +0900 The documentation stated that existing connections would continue to
function normally after the OOM killer terminated the postmaster.
In fact, those connections eventually terminate when server processes
detect the postmaster's death.
Clarify that the OOM killer can terminate either the postmaster or a
child process. If the postmaster is killed, the server must be restarted.
If a child process is killed, PostgreSQL typically terminates the
remaining connections and either recovers automatically or shuts down,
depending on restart_after_crash.
Backpatch to all supported versions.
Author: Takeshi Ideriha <iderihatakeshi@gmail.com>
Reviewed-by: Manuel Reyes Bravo <manuelreyesbravo@gmail.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/ce269321-a7f4-48f4-a9ca-a4a4983b9fd6@gmail.com
Backpatch-through: 14 M doc/src/sgml/runtime.sgml
Fix bogus Assert in find_var_for_subquery_tle
commit : cf24f8e616ba21c84dd684ea9029be0654312dc6
author : Richard Guo <rguo@postgresql.org>
date : Fri, 16 Oct 2026 16:45:03 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Fri, 16 Oct 2026 16:45:03 +0900 find_var_for_subquery_tle() searches a subquery rel's targetlist for a
Var referencing a given subquery output column, and asserted that any
Var it finds there belongs to that rel. That does not hold for an
appendrel child. set_append_rel_size() builds the child's targetlist
by translating the parent's, and as noted there the result can contain
arbitrary expressions. In particular, when a member of a LATERAL UNION
ALL subquery is pulled up and its output is a lateral reference, the
translated expression is a Var of the laterally referenced rel. If the
child is itself a subquery with sorted paths, convert_subquery_pathkeys()
would trip the Assert.
In non-assert builds, the function went on to compare such a Var's
varattno with the subquery column's resno, and so could match a Var of
an unrelated rel.
To fix, ignore Vars that do not belong to the subquery rel, the same
way set_rel_width() already does. Also correct the adjacent comment,
which claimed that the only non-Var entries are placeholders.
Back-patch to all supported branches.
Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: shihao zhong <zhong950419@gmail.com>
Discussion: https://postgr.es/m/CAMbWs48fFiPi7Q5yJCoM_nAs47zd5XgvPHd9nZyM_ewq+k_jpQ@mail.gmail.com
Backpatch-through: 14 M src/backend/optimizer/path/pathkeys.c
M src/test/regress/expected/join.out
M src/test/regress/sql/join.sql
Add sentinel_ok() check to BumpCheck().
commit : 86848d19bdbbf0daac6ca1b92a8885179cae8db5
author : David Rowley <drowley@postgresql.org>
date : Fri, 16 Oct 2026 18:00:44 +1300
committer: David Rowley <drowley@postgresql.org>
date : Fri, 16 Oct 2026 18:00:44 +1300 Effectively an oversight in 29f6a959cf. The code went to the trouble of
doing set_sentinel() but no code ever checked it was still set during
BumpCheck(). Since bump context types are used in a handful of places,
it very much makes sense to backpatch this to where bump.c was added so we
can more easily discover any memory stomping bugs.
Reported-by: shihao zhong <zhong950419@gmail.com>
Author: David Rowley <dgrowleyml@gmail.com>
Discussion: https://postgr.es/m/CAGRkXqQxTCthDEnw_10OEGLVNMUeyE4So-K29jmn9nAkxtr1ow@mail.gmail.com
Backpatch-through: 17 M src/backend/utils/mmgr/bump.c
Update .abi-compliance-history for changes in reorderbuffer.h
commit : 96823bfd7c9dd1bb070d3d084da61dc2af8dc710
author : Álvaro Herrera <alvherre@kurilemu.de>
date : Thu, 15 Oct 2026 16:38:13 +0200
committer: Álvaro Herrera <alvherre@kurilemu.de>
date : Thu, 15 Oct 2026 16:38:13 +0200 The ABI changes happened in this commit:
Author: Álvaro Herrera <alvherre@kurilemu.de>
Branch: master [beff3cd00] 2026-10-14 19:42:04 +0200
Branch: REL_19_STABLE [963ff022f] 2026-10-14 19:42:04 +0200
Branch: REL_18_STABLE [5eae2800a] 2026-10-14 19:42:04 +0200
Branch: REL_17_STABLE [624bd2b1f] 2026-10-14 19:42:04 +0200
Branch: REL_16_STABLE [ff5ba5f19] 2026-10-14 19:42:04 +0200
Branch: REL_15_STABLE [0a6dc4fe1] 2026-10-14 19:42:04 +0200
Branch: REL_14_STABLE [83be64488] 2026-10-14 19:42:04 +0200
Reorderbuffer: handle catalog-change-bearing aborted subxacts better
However, I'm now backpatching only between 14 and 17. Later branches
have further ABI changes and their compliance files will be updated
separately.
Author: Álvaro Herrera <alvherre@kurilemu.de>
Discussion: https://postgr.es/m/as_Etcja0IsaMxlt@alvherre.pgsql M .abi-compliance-history
Fix CreateThread() failure checks
commit : 2bc73b3e80ab6916098ecc50dd964e668b237124
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Thu, 15 Oct 2026 12:04:27 +0300
committer: Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Thu, 15 Oct 2026 12:04:27 +0300 CreateThread returns NULL on failure, not INVALID_HANDLE_VALUE. In
setitimer(), a failed thread creation would silently disable all
timer-based timeouts.
Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://www.postgresql.org/message-id/CAH7T-apWWfEt+V8uLU7KXv7CMM9LoqJ1Zo2LdWmXMz=u-Ek9=w@mail.gmail.com
Backpatch-through: 14 M src/backend/port/win32/timer.c
M src/bin/pg_test_fsync/pg_test_fsync.c
Fix checks for aborted updating transaction in multixids
commit : ff9eacc568d56f83611b80e00961e9adde2bfdc7
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Thu, 15 Oct 2026 11:23:06 +0300
committer: Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Thu, 15 Oct 2026 11:23:06 +0300 As mentioned in the comments in TransactionIdDidAbort() and in
heapam_visibility.c, the usual procedure for checking whether a
transaction aborted is to first call TransactionIdIsInProgress(), and
then !TransactionIdDidCommit(). A couple places didn't get the memo,
and used TransactionIdDidAbort() incorrectly.
In DoesMultiXactIdConflict(), the consequence was that if a
conflicting transaction was just aborting, and had already marked the
transaction as aborted in pg_xact, but had not yet removed itself from
ProcArray, the function would return false meaning that it does not
conflict, even though it was still considered as running by
TransactionIdIsInProgress(). That could cause trouble later. One
known consequence is that if you then went ahead with updating the row
again and called MultiXactIdExpand(), MultiXactIdExpand() would see
both the old aborting transaction and the new updater as still
running, and throw an ERROR: new multixact has more than one updating
member. The multixact-aborted-updater.spec included in this commit
exercises that scenario.
Another place that got this wrong was in heap_update(), where we check
if the updating transaction that was part of a multixid committed or
aborted. Because it used TransactionIdDidAbort(), if the updating
transaction crashed, it was considered as committed and heap_update()
incorrectly returned TM_Updated indicating a conflict, even though the
row could be updated. This would only happen if the multixid as whole
was still considered running, i.e. if the tuple was still locked by
other transactions while the updating transaction crashed. That can
happen if the locker is a prepared transaction. The included
060_multixact_crashed_updater.pl test tests that scenario.
Author: Chee Wooson <wuqi@vastdata.com.cn>
Tested-by: Lionel Hamayon <lionel.hamayon@evolution-digitale.fr>
Discussion: https://www.postgresql.org/message-id/5778D6297D3CCF21+202609151836512678486@vastdata.com.cn
Backpatch-through: 14 M src/backend/access/heap/heapam.c
M src/backend/access/transam/xact.c
M src/test/modules/injection_points/Makefile
A src/test/modules/injection_points/expected/multixact-aborted-updater.out
M src/test/modules/injection_points/meson.build
A src/test/modules/injection_points/specs/multixact-aborted-updater.spec
M src/test/recovery/meson.build
A src/test/recovery/t/060_multixact_crashed_updater.pl
Clear more substructure of a dead relation during join removal.
commit : 12f10c25ce3febf9b94748d28c8332f827779980
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Wed, 14 Oct 2026 15:37:28 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Wed, 14 Oct 2026 15:37:28 -0400 The tablesample and securityQuals subtrees of a RangeTblEntry can
include Vars, which will cause trouble if they point at a removed
rel and are visited during later processing. Hence, clear those
fields during join removal, as we already do for the subquery field.
remove_useless_outer_joins got this right for securityQuals but
not tablesample, while remove_self_join_rel missed doing it for
securityQuals. (It escapes trouble for tablesample only because we
made a policy decision to never touch sampled relations for SJE.)
Oversight in 2ebf25e7d. Back-patch to v16, like that commit
(but the SJE issue only goes as far back as v18).
Author: shihao zhong <zhong950419@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAGRkXqSOAT1yop44B8jwacL_91rxK=QuoU8Of3NLMb9NgAO2NA@mail.gmail.com
Backpatch-through: 16 M src/backend/optimizer/plan/analyzejoins.c
Fix hash bucket split buffer pin leak.
commit : b57161883677baf71c12662b5347085a5ef14adc
author : Peter Geoghegan <pg@bowt.ie>
date : Wed, 14 Oct 2026 14:26:04 -0400
committer: Peter Geoghegan <pg@bowt.ie>
date : Wed, 14 Oct 2026 14:26:04 -0400 The process by which a hash index inserter finishes an incomplete bucket
split involves a walk of the new bucket (the bucket "being populated")
to collect the TIDs that the interrupted split already moved. This
process keeps a pin on the new bucket's primary page, and then takes
conditional cleanup locks on both buckets. When either lock could not
be acquired, we gave up, but neglected to release the new bucket's pin.
To fix, release the buffer pin on failure.
Oversight in commit 6d46f4783, which improved hash index bucket split
behavior by switching it from using a heavyweight lock to using these
cleanup locks.
Author: Peter Geoghegan <pg@bowt.ie>
Reviewed-by: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Discussion: https://postgr.es/m/CAH2-Wz=b9sSBv15HsgZAuazjJu8-YRGHOpLjvp+dJgEDaBx0Ng@mail.gmail.com
Backpatch-through: 14 M src/backend/access/hash/hashpage.c
Doc: improve documentation of the JSON object aggregates.
commit : 298191036cce3b7634fd4d4b71eeb29def103f0b
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Wed, 14 Oct 2026 14:00:30 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Wed, 14 Oct 2026 14:00:30 -0400 Say which value wins for a repeated key, and how skipped null values
interact with that and with the unique check. Also point out that
duplicate keys behave differently for json and jsonb cases. (I still
think that's a bug, but at least now it's documented.)
Author: shihao zhong <zhong950419@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAGRkXqTCfSu=aEUCvKDTGroEWOcyz3MCv4Xi8x1PZ1T1rEmG=Q@mail.gmail.com
Backpatch-through: 14 M doc/src/sgml/func.sgml
Reorderbuffer: handle catalog-change-bearing aborted subxacts better
commit : 624bd2b1fc0c9750c28a55b436e17b109b72d701
author : Álvaro Herrera <alvherre@kurilemu.de>
date : Wed, 14 Oct 2026 19:42:04 +0200
committer: Álvaro Herrera <alvherre@kurilemu.de>
date : Wed, 14 Oct 2026 19:42:04 +0200 When constructing a historic snapshot for logical decoding,
reorderbuffer needs to keep track of movement of tuples in catalog
tables. For the most part this works correctly, except that when a
subtransaction that wrote a catalog tuple aborts, we failed to "forget"
about that change. There's no known reports of anything actually
breaking except assertion failures, but it is likely possible to
construct cases where historical snapshots produce bogus visibility
answers, potentially causing incorrect decoding output.
To fix, have the decode of a subtransaction abort go over the TUPLECID
list that has been accumulated on the ReorderBufferTXN for its topmost
parent transaction, and remove all the entries that were marked as
originating from the aborted subtransaction or its children. There are
some little details in which various devils lie, which the added code
comments explain (we hope).
Add a couple of isolation test cases that trip the assertions with the
original coding. They're not ideal test design, because they pass in
non-assert builds without the fix. But we'd rather have the fix be
verified in that limited scenario than nowhere at all, and they are
cheap enough.
This bug is sibling to the one fixed in commit 8c67d29fd51c and, like
that one, it goes all the way back to the introduction of
reorderbuffers, so backpatch to all live branches.
While reviewing this, I (Álvaro) was bothered by the snapmgr.c
tuplecid_data file-scoped variable, as it's easily confused with a
function parameter used widely in reorderbuffer.c. In branches 19 and
master, rename that to avoid confusion.
Reported-by: Arseny Sher <a.sher@postgrespro.ru>
Reported-by: Mark Dilger <mark.dilger@enterprisedb.com>
Reported-by: Alexander Kozhemyakin <a.kozhemyakin@postgrespro.ru>
Author: Bingshuai Li <lucian1412@outlook.com>
Author: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Author: Zhijie Hou <houzhijie22@gmail.com>
Backpatch-through: 14
Bug: #19555
Discussion: https://postgr.es/m/87r2c8xocq.fsf@ars-thinkpad
Discussion: https://postgr.es/m/CAHgHdKu5e3XY5e90Tuaxq_R4WrKxSV734Q%2BLwo5y39Omp2A-Gg@mail.gmail.com
Discussion: https://postgr.es/m/19555-3c700f40a13045cd@postgresql.org M contrib/test_decoding/Makefile
A contrib/test_decoding/expected/tuplecid_nested.out
A contrib/test_decoding/expected/tuplecid_restart.out
M contrib/test_decoding/meson.build
A contrib/test_decoding/specs/tuplecid_nested.spec
A contrib/test_decoding/specs/tuplecid_restart.spec
M src/backend/replication/logical/decode.c
M src/backend/replication/logical/reorderbuffer.c
M src/backend/replication/logical/snapbuild.c
M src/include/replication/reorderbuffer.h
Plug holes in SP-GiST quad tree getQuadrant()
commit : 3d8c50317e01a8126eea09d1faf3786076890c64
author : John Naylor <john.naylor@postgresql.org>
date : Wed, 14 Oct 2026 18:19:39 +0700
committer: John Naylor <john.naylor@postgresql.org>
date : Wed, 14 Oct 2026 18:19:39 +0700 getQuadrant() places a point on each axis with the fuzzy comparisons
FPlt(), FPgt() and FPeq(), which do not cover every case. FPeq()
compares the rounded difference against EPSILON, while FPlt() and FPgt()
round the sum of one operand and EPSILON. When the centroid plus
EPSILON rounds up, a point equal to that rounded sum is more than
EPSILON above the centroid, so FPeq() is false. FPgt() compares it with
the same rounded sum, so it is false too, and FPlt() is false because
the point is above the centroid. The same gap exists below the
centroid, when the centroid minus EPSILON rounds down. No quadrant
matches, and index builds, inserts and searches fail with
"getQuadrant: impossible case".
To fix, fall back to deciding each axis separately: Use fuzzy tests
first and then tiebreak with exact comparisons. That gives every finite
value a quadrant (NaN coordinates are still an error). Points that
matched a fuzzy test before are routed as before, so existing indexes
remain valid.
Bug: #19597
Reported-by: Michael Malis <malis@pgrust.com>
Author: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Reviewed-by: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Reviewed-by: Pierre Forstmann <pierre.forstmann@gmail.com>
Discussion: https://postgr.es/m/19597-39c532e61d78dff6@postgresql.org
Backpatch-through: 14 M src/backend/access/spgist/spgquadtreeproc.c
M src/test/regress/expected/spgist.out
M src/test/regress/sql/spgist.sql
pg_basebackup: Report correct size estimate for incremental backups
commit : 4c674b952a61c07d26c33d5f9c8aed8c1c1696fa
author : Fujii Masao <fujii@postgresql.org>
date : Wed, 14 Oct 2026 19:54:45 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Wed, 14 Oct 2026 19:54:45 +0900 Progress reporting in pg_basebackup and pg_stat_progress_basebackup used
the estimated size of a full backup even when taking an incremental backup.
The size estimation pass did not account for incremental files and counted
every file at its full size. Progress could therefore remain at only a few
percent even after the incremental backup completed successfully.
Fix this by making the size estimation pass account for incremental files,
counting them at the size they will occupy in the archive.
Backpatch to 17, where incremental backup was introduced.
Author: Daria Lepikhova <daria.n.lepikhova@gmail.com>
Reviewed-by: Manuel Reyes Bravo <manuelreyesbravo@gmail.com>
Reviewed-by: Zsolt Parragi <zsolt.parragi@percona.com>
Reviewed-by: Surya Poondla <suryapoondla4@gmail.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/CAK49S1ASwk7L-7LYUUz1MU_+uQnZ75srva2XRR4r-w0HCVZeUQ@mail.gmail.com
Backpatch-through: 17 M doc/src/sgml/protocol.sgml
M src/backend/backup/basebackup.c
M src/backend/backup/basebackup_incremental.c
M src/bin/pg_basebackup/meson.build
A src/bin/pg_basebackup/t/012_incremental_progress.pl
doc: Clarify that base backup progress counts uncompressed archive data
commit : 116de0c12a2586a8a8af544ddaeeae86fab1e728
author : Fujii Masao <fujii@postgresql.org>
date : Wed, 14 Oct 2026 19:52:39 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Wed, 14 Oct 2026 19:52:39 +0900 The documentation described base backup progress in terms of data
streamed, which could be mistaken for the amount of data sent over the
connection when server-side compression is enabled. Clarify that the
counters measure archive data before server-side compression.
The progress report message was also documented as counting bytes from
the current tablespace, although it reports a cumulative count for the
whole backup. Correct the description to say that the count covers all
tablespaces since the backup began.
Backpatch to 15, where both the progress report message and server-side
compression were introduced.
Suggested-by: Surya Poondla <suryapoondla4@gmail.com>
Author: Daria Lepikhova <daria.n.lepikhova@gmail.com>
Reviewed-by: Surya Poondla <suryapoondla4@gmail.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/CAOVWO5pNe6XprMnt5OTT+-2Sm67z5GX4DzUNArEHubF0z9-PZQ@mail.gmail.com
Backpatch-through: 15 M doc/src/sgml/monitoring.sgml
M doc/src/sgml/protocol.sgml
M doc/src/sgml/ref/pg_basebackup.sgml
M src/include/backup/basebackup_sink.h
Accumulate Memoize parallel worker stats across rescans
commit : 04609efb38411ec83c7293528e6ea251e89cb231
author : David Rowley <drowley@postgresql.org>
date : Wed, 14 Oct 2026 22:08:21 +1300
committer: David Rowley <drowley@postgresql.org>
date : Wed, 14 Oct 2026 22:08:21 +1300 When a Gather or Gather Merge node is rescanned, it starts a new set of
workers, which write their Memoize stats into the same shared memory
slots as the previous worker. ExecEndMemoize() used memcpy() for this,
so EXPLAIN ANALYZE showed only the stats for the final scan.
Here we adjust ExecEndMemoize() to accumulate the counters so that we
gather up the totals over all rescans.
Author: Tatsuya Kawata <kawatatatsuya0913@gmail.com>
Reviewed-by: David Rowley <dgrowleyml@gmail.com>
Discussion: https://postgr.es/m/CAHza6qeA-4vB355co-2yAxDMRTpEneY0rg9CD2zMLYscYvv2UA@mail.gmail.com
Backpatch-through: 14 M src/backend/executor/nodeMemoize.c
Disallow joins whose lateral references can never be satisfied
commit : 9e8f4ce286bf03306fc0ad29374095782b2a8136
author : Richard Guo <rguo@postgresql.org>
date : Wed, 14 Oct 2026 14:32:32 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Wed, 14 Oct 2026 14:32:32 +0900 If a proposed join has lateral references to rels outside it,
join_is_legal() checks that those rels can still be joined to from
outside. To do that it collects the rels that must end up on the
inner side of an outer join with the proposed join, directly or
indirectly, and rejects the join if any of its lateral references
point to one of them.
That search followed only outer joins, from min_lefthand to
min_righthand. It failed to notice that a rel that laterally
references the proposed join, or any rel found by the search, cannot
supply a parameter to the proposed join either, and that the same
holds for rels on the inner side of an outer join with such a rel. As
a result, we could approve a join whose lateral references could never
be satisfied.
Such a join can never appear in a complete plan. Worse, as the
existing comment warns, its apparent legality could mislead the
clauseless-join heuristics into thinking that some other join rel need
not be formed. In particular, have_join_order_restriction() would not
force a clauseless outer join if one of its input rels appeared to
have a legal clause join elsewhere, and if that outer join was
required, we would fail with "failed to build any N-way joins".
To fix, also include in the search any input rel of the current join
search that laterally references a rel already found. The input rels
are taken from root->initial_rels rather than from the query's base
rels, because an input can be the joinrel of a sub-joinlist, as
happens for a full join or when from_collapse_limit or
join_collapse_limit prevents flattening. Such a joinrel is joined as
a unit, so it has to be included as a whole.
Back-patch to all supported branches.
Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Tender Wang <tndrwang@gmail.com>
Reviewed-by: wenhui qiu <qiuwenhuifx@gmail.com>
Discussion: https://postgr.es/m/CAMbWs4_p6iWbsjNcYpd6-6mWFnog3UJ2fZKrxWAQTsyNoyeQTw@mail.gmail.com
Backpatch-through: 14 M src/backend/optimizer/path/allpaths.c
M src/backend/optimizer/path/joinrels.c
M src/test/regress/expected/join.out
M src/test/regress/sql/join.sql
Give antijoins made from sublinks a relid
commit : 854f81f07c7dbd16189d6cd381eb53360f0a7b03
author : Richard Guo <rguo@postgresql.org>
date : Wed, 14 Oct 2026 12:06:40 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Wed, 14 Oct 2026 12:06:40 +0900 When pull_up_sublinks() converts a sublink to an antijoin, it builds a
JoinExpr with no RTE, on the grounds that nothing above the join can
reference its RHS. That overlooks PlaceHolderVars. A PHV with no
Vars of its own is evaluated at its phrels, and for the outputs of a
pulled-up subquery phrels is all the relids in the subquery's
jointree, including the RHS of such a join.
A qual using the PHV thus appears to reference the RHS rels, and can
be pushed down into the RHS as a parameterized join clause. There it
has the opposite of the intended effect: outer rows that fail the qual
find no match and are emitted, giving wrong results. For an outer
join, including an antijoin made by reducing a left join, this cannot
happen because the join's relid is among the qual's relids. Before
v16, the qual's nullable_relids prevented it.
To fix, make a join RTE for an antijoin built from a sublink, so that
it has a relid like any other outer join. Semijoins are left as they
are: a qual evaluated within a semijoin's RHS does not change the
result.
Back-patch to v16. Before that, the qual's nullable_relids included
the antijoin's RHS, which kept the qual from being moved there; that
protection went away when nullable_relids was replaced by outer-join
relids.
Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: wenhui qiu <qiuwenhuifx@gmail.com>
Discussion: https://postgr.es/m/CAMbWs4_rEdcG89BgC-AO7b+Bf2z8w3976+4zP==Urt=CXB1hsg@mail.gmail.com
Backpatch-through: 16 M src/backend/optimizer/plan/initsplan.c
M src/backend/optimizer/plan/subselect.c
M src/include/nodes/pathnodes.h
M src/include/nodes/primnodes.h
M src/test/regress/expected/join.out
M src/test/regress/sql/join.sql
Fix potential use-after-frees in WAL sender
commit : 39173a9bd0ddcbdb14d1e05bc7a82cad6e7f7fee
author : Michael Paquier <michael@paquier.xyz>
date : Wed, 14 Oct 2026 12:05:14 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Wed, 14 Oct 2026 12:05:14 +0900 A WAL sender tracks its WAL reader and logical decoding context as two
static variables, which were left set after being freed for a
START_REPLICATION command. The WAL reader is used for in a physical
replication command, both the WAL reader and the logical decoding
context are used for a logical replication command.
Both variables are now cleared once their contents are released at the
end of each replication command, to keep the release of the resources
and the maintenance of the pointers close to each other. Some more
cleanup is done by WalSndErrorCleanup(), as well, once the WAL reader is
clear.
The assertion added by 879ad9f90e83 is moved before the execution of a
replication command, checking now that both the WAL reader and the
logical decoding context are never set before the execution of a new
command.
Author: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/CAJTYsWW+hdmmVt_A4hvGv2_5RWK8-kaOf5ncT65=5i_hyD=BHg@mail.gmail.com
Backpatch-through: 14 M src/backend/replication/walsender.c
Add missing FreeDir() in CheckTablespaceDirectory()
commit : c5e6dd73636dbb682852d95c55cb6bb0ad364ec5
author : Fujii Masao <fujii@postgresql.org>
date : Wed, 14 Oct 2026 11:11:08 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Wed, 14 Oct 2026 11:11:08 +0900 CheckTablespaceDirectory() did not close the directory opened with
AllocateDir(), leaving its resources held during subsequent recovery,
potentially indefinitely on a standby. Add the missing FreeDir() call.
Backpatch to all supported versions.
Author: Chao Li <lic@highgo.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/66A784B7-55C1-4613-8C24-B4C2A8D75414@gmail.com
Backpatch-through: 14 M src/backend/access/transam/xlogrecovery.c
Release memory allocated by ndistinct_for_combination()
commit : a8c98c8843678560364eb20fe203046b183ccf6d
author : Michael Paquier <michael@paquier.xyz>
date : Wed, 14 Oct 2026 10:16:21 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Wed, 14 Oct 2026 10:16:21 +0900 Building extended statistics of type "ndistinct" calls
ndistinct_for_combination() once for every combination of columns. Each
call allocates arrays for the whole sample and never frees them, with
memory accumulating until the whole statistics object is built. The
sample has 300 multiplied by target rows. With large statistics targets
(default_statistics_target up to 10k), this can add up to gigabytes
worth of memory, useless once a combination is computed.
ndistinct_for_combination() is now called in a dedicated memory context,
reset after each combination is computed. We only need the estimated
number of distinct values, so there is no need to copy anything out of
the memory context.
Similar fixes have been done in 83772cc78e03 and ad8a166ca868 for other
areas of the extended stats computations.
Author: Ilia Evdokimov <ilya.evdokimov@tantorlabs.com>
Discussion: https://postgr.es/m/20d6689e-f490-4ca7-a92f-e92f691ba07e@tantorlabs.com
Backpatch-through: 14 M src/backend/statistics/mvdistinct.c
Delay qp_callback processing till after join removal.
commit : 1000f7b14a1dad0acf1c82cb8fdd5a7a8ab1df0b
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Tue, 13 Oct 2026 17:53:20 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Tue, 13 Oct 2026 17:53:20 -0400 Commit 2ebf25e7d left query_planner's qp_callback call where
it had been historically, but that turns out to be a bad idea.
It means that the code reached via qp_callback is subject to the
must-be-able-to-restart rule created by looping back after join
removal. We found one place that fails that requirement:
adjust_group_pathkeys_for_groupagg sets Aggref.aggpresorted flags,
and there's nothing that will reset those in case of a query_planner
restart, yet the selected set of Aggrefs can change. There might be
other places with similar issues.
Fortunately, qp_callback's main charter is to compute/update pathkey
values, and that is not something that join removal depends on or
should depend on. (Whether join removal is valid is surely not
dependent on what order the rel might get scanned in.) Nothing else
within the restart loop depends on it either --- in other words,
the existing placement was a bit random to begin with. So we can
fix this problem just by moving that call to below the restart loop.
That costs nothing, indeed it saves some cycles by not having to do
that processing more than once. And removing an unstated requirement
from code that's not directly called by query_planner should help
forestall future bugs.
While the known bug only exists in v18 and up (older branches lack the
aggpresorted optimization), I've chosen to back-patch the code change
to v16 like 2ebf25e7d was. This is the sort of API requirement that
we don't really want to be different across branches.
Reported-by: shihao zhong <zhong950419@gmail.com>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: shihao zhong <zhong950419@gmail.com>
Discussion: https://postgr.es/m/CAGRkXqSOAT1yop44B8jwacL_91rxK=QuoU8Of3NLMb9NgAO2NA@mail.gmail.com
Backpatch-through: 16 M src/backend/optimizer/plan/planmain.c
Fix two errors in extraction of GIN indexquals from jsonpath queries.
commit : 65f1b71c35cd8f2fbb0dd9b771c76d7ec71f9b44
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Tue, 13 Oct 2026 16:33:35 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Tue, 13 Oct 2026 16:33:35 -0400 extract_jsp_bool_expr applied De Morgan's laws incorrectly when
considering an AND operator underneath a NOT. If one AND input
was implementable as a GIN indexqual while the other was not,
we derived an incomplete indexqual, leading to some rows not
being returned that should be.
extract_jsp_bool_expr also failed to check for methods being
applied to literals, so that for example '$.x == "42".double()'
was treated as '$.x == "42"' not '$.x == 42'. The indexqual
thus selected the wrong rows, all of which would fail qual
recheck, leading to no rows being returned.
While we're here, improve the comments a trifle.
Reported-by: Chinmay Kanchi <cgkanchi@gmail.com>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAJqPDh9Cm=x8eYZ1Jf=_avd2WEqc=0t_f7_SO4igo-CKPi25_Q@mail.gmail.com
Discussion: https://postgr.es/m/CAJqPDh-tu5KY66KU8cvAEg3DQrZLzDZwjkLLa42zD+_Xm1WJAg@mail.gmail.com
Backpatch-through: 14 M src/backend/utils/adt/jsonb_gin.c
M src/test/regress/expected/jsonb.out
M src/test/regress/sql/jsonb.sql
Fix hash index scans that start during a bucket split.
commit : ab22337434958cb59cdca7cddd0cbd9a27ea4765
author : Peter Geoghegan <pg@bowt.ie>
date : Sat, 10 Oct 2026 10:26:18 -0400
committer: Peter Geoghegan <pg@bowt.ie>
date : Sat, 10 Oct 2026 10:26:18 -0400 A hash index scan whose target bucket is still being populated by a
split must read both buckets of the split: forward scans read the bucket
being populated (skipping the tuples that the split copied there) and
then the bucket being split, and backward scans read them in the
opposite order. _hash_readnext and _hash_readprev make the transition
from the first bucket to the second, but _hash_next did not: on reaching
the end of the first bucket, it ended the scan, so every matching tuple
in the second bucket was overlooked.
To fix, teach _hash_next to make the same transition, using helper
functions shared with _hash_readnext and _hash_readprev.
_hash_next only missed the transition when the last page that it read in
the first bucket had a match (otherwise _hash_readpage's loop made the
required transition). Getting wrong answers also needed a matching
tuple to be inserted into the bucket being populated _after_ the split
began. Forward scans then lost the older tuples in the bucket being
split. An incomplete split would keep the buckets in this state until
an inserter finished the split at some indeterminate later time. Note
that wrong answers didn't require an incomplete split.
Although this fix treats the bug as the omission of a required step in
_hash_next, the real problem is the way that page stepping works in
general. The logic for stepping to the next page appears both in
high-level routines like _hash_next and in low-level routines like
_hash_readnext and _hash_readprev. If anything, _hash_next already knew
too much about page stepping (cf. nbtree's _bt_next function). All of
the logic around stepping really belongs in one routine, modeled on
nbtree's _bt_steppage function, but that approach is much too invasive
for a backpatched fix.
Claude Code found this problem. The committed test case is a simplified
version of the one that it wrote to demonstrate this bug.
Oversight in commit 7c75ef57, which introduced page-at-a-time processing
of hash index scans.
Author: Peter Geoghegan <pg@bowt.ie>
Author: Amit Kapila <amit.kapila16@gmail.com>
Reviewed-by: Vignesh C <vignesh21@gmail.com>
Discussion: https://postgr.es/m/CAH2-Wz=4CJK9ysZzbzxBGGTkgAg9ib8cJyCx=9q_d1r8tf0ang@mail.gmail.com
Backpatch-through: 14 M src/backend/access/hash/hashpage.c
M src/backend/access/hash/hashsearch.c
M src/test/modules/Makefile
A src/test/modules/hash/.gitignore
A src/test/modules/hash/Makefile
A src/test/modules/hash/expected/hash_incomplete_splits.out
A src/test/modules/hash/meson.build
A src/test/modules/hash/sql/hash_incomplete_splits.sql
M src/test/modules/meson.build
Fix parallel vacuum I/O timing reporting.
commit : 35c508af520963bf1245b86f437c21f834cc2be0
author : Masahiko Sawada <msawada@postgresql.org>
date : Thu, 8 Oct 2026 11:26:17 -0700
committer: Masahiko Sawada <msawada@postgresql.org>
date : Thu, 8 Oct 2026 11:26:17 -0700 Commit 5cd72cc0c5 made the VACUUM VERBOSE and autovacuum logs report
buffer usage from the pgBufferUsage delta, which includes the parallel
workers' usage, but left the I/O timings on the leader's
pgStatBlockReadTime and pgStatBlockWriteTime. So the log counted the
blocks the workers read and dirtied but not the time they spent on
them.
Fix this by taking the I/O timings from the same BufferUsage delta.
This also removes a small error in the leader's own timings, since
pgStatBlock{Read,Write}Time accumulate whole microseconds per I/O.
Although 5cd72cc0c5 was backpatched to v15, BufferUsage on v16 doesn't
track the I/O time of temporary relations, so the same fix there would
drop that time from the log. We don't think a reporting issue is worth
v16-specific code, nor worth leaving v16 unfixed between v15 and v17,
so backpatch to v17 only.
Found by Bharath using AI assisted review with Claude.
Reported-by: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Author: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Reviewed-by: Sami Imseih <samimseih.pg@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com>
Reviewed-by: Shihao Zhong <zhong950419@gmail.com>
Discussion: https://postgr.es/m/CALj2ACXrqaHrYmGdpAoGKXtiY9Xw4o1dxSC+EELxn47GDO6HAQ@mail.gmail.com
Backpatch-through: 17 M src/backend/access/heap/vacuumlazy.c
Remove faulty nbtree page deletion assertion.
commit : 87e417681653937bd8d01740e1ff3c3c224ba66e
author : Peter Geoghegan <pg@bowt.ie>
date : Thu, 8 Oct 2026 14:21:15 -0400
committer: Peter Geoghegan <pg@bowt.ie>
date : Thu, 8 Oct 2026 14:21:15 -0400 _bt_pagedel only builds a search stack when it needs one to locate the
downlink of the page that it's about to mark half-dead (and when it
doesn't already have one from an earlier sibling deletion that took
place during the same _bt_pagedel call). _bt_pagedel won't build a
search stack for the page its caller passes whenever it happens to be
one that an earlier interrupted VACUUM left half-dead (since there is no
downlink to remove). We nevertheless asserted that page deletion could
only build a search stack for that initial caller-passed page.
To fix, remove the assertion, which spuriously failed when a half-dead
leaf page was passed by the _bt_pagedel caller and its right sibling
page was empty/had to be considered for deletion during the same call
(necessitating building a search stack to locate the right sibling's
downlink).
Oversight in commit cd8c73a38, which refactored nbtree page deletion's
INCOMPLETE_SPLIT check.
Author: Peter Geoghegan <pg@bowt.ie>
Reported-by: Iliia Khaprov <i.khaprov@gmail.com>
Discussion: https://postgr.es/m/CAFO+O_p5So4MXwQ1VEcL9+9vGgODu11TTWcYq9RDbxnira8o2w@mail.gmail.com
Backpatch-through: 14 M src/backend/access/nbtree/nbtpage.c
pg_walsummary: Fix --quiet to suppress limit block output
commit : d843a801b0f375349b18cf07f1bbe0169d699be9
author : Fujii Masao <fujii@postgresql.org>
date : Wed, 7 Oct 2026 15:01:36 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Wed, 7 Oct 2026 15:01:36 +0900 Previously, pg_walsummary printed limit block lines even with --quiet
because the output was generated before checking the quiet flag.
Fix this by moving the output after the quiet check.
Backpatch to v17, where pg_walsummary was introduced.
Author: Koshi Shibagaki <shibagaki.koshi@fujitsu.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/TY5PR01MB18445D1AF87F64E9835BAD0B0FA962@TY5PR01MB18445.jpnprd01.prod.outlook.com
Backpatch-through: 17 M src/bin/pg_walsummary/pg_walsummary.c
M src/bin/pg_walsummary/t/002_blocks.pl
Fix PlaceHolderVar placement for join alias expressions
commit : d9595e7e6c8c7c63cfe1f22a37a5957226afd7f1
author : Richard Guo <rguo@postgresql.org>
date : Wed, 7 Oct 2026 14:38:54 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Wed, 7 Oct 2026 14:38:54 +0900 When a join alias Var with nonempty varnullingrels expands to an
expression that cannot carry the nullingrels itself, such as the
RowExpr of a whole-row Var, add_nullingrels_if_needed() wraps it in a
PlaceHolderVar. The choice of the PHV's phrels had several problems.
First, phrels was taken from all Vars in the expression. If a LATERAL
subquery within the join has been pulled up, the expression can
contain lateral references to rels outside the join, so the PHV could
be evaluated above the outer join that is supposed to null it. This
gave wrong results, or "wrong phnullingrels" errors.
Second, for a variable-free expression we fell back to the join's
relids but removed the join's own outer-join relid. That leaves a set
spanning both sides of the outer join without the join itself, which
can make the PHV look nullable by that join and cause "wrong
phnullingrels" errors.
Third, that fallback raised "unsupported join alias expression" when
the Var is an outer reference from a subquery. The restriction is
unnecessary: every planner caller passes root->parse as the query, so
the Var always belongs to root->parse regardless of its varlevelsup.
To fix, restrict phrels to the rels within the join, and if nothing is
left, evaluate the PHV at the join including its outer-join relid, at
any varlevelsup. Add an assertion that the query is root->parse.
Author: Richard Guo <guofenglinux@gmail.com>
Discussion: https://postgr.es/m/CAMbWs49q_gvoeOoF0+KZ-DpQaDb=nGFfSgrz2V_n1_64L4ZNVQ@mail.gmail.com
Backpatch-through: 16 M src/backend/optimizer/util/var.c
M src/test/regress/expected/join.out
M src/test/regress/sql/join.sql
Fix parser failure with whole-row join alias Vars
commit : 0c42350131bdc173964e72f94d3096cc11e9bc2e
author : Richard Guo <rguo@postgresql.org>
date : Wed, 7 Oct 2026 14:38:42 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Wed, 7 Oct 2026 14:38:42 +0900 When a query has aggregates or GROUP BY, parseCheckAggregates()
flattens join alias Vars in the grouping expressions, target list and
HAVING clause so that aliased and unaliased references compare equal.
The parser has no PlannerInfo, so this relies on every join alias
expression being able to carry varnullingrels without a PHV. That
doesn't hold for a whole-row Var of a join on the nullable side of an
outer join: it expands to a RowExpr, and pushing the nulling bits down
into its fields would turn a NULL row into ROW(NULL, ...). Such
queries therefore failed with "unsupported join alias expression".
To fix, leave a nulled whole-row join Var unexpanded when no root is
available. The planner expands it later and wraps it in a
PlaceHolderVar as before. Nothing is lost for GROUP BY matching: a
nulled whole-row Var can only be equal to the same Var, never to an
expression over the join's columns.
This has been broken since the introduction of varnullingrels in v16.
Author: Richard Guo <guofenglinux@gmail.com>
Discussion: https://postgr.es/m/CAMbWs49q_gvoeOoF0+KZ-DpQaDb=nGFfSgrz2V_n1_64L4ZNVQ@mail.gmail.com
Backpatch-through: 16 M src/backend/optimizer/util/var.c
M src/test/regress/expected/join.out
M src/test/regress/sql/join.sql
reindexdb: Forbid index-level REINDEX with CONCURRENTLY and multiple jobs
commit : 869c53ff8fe412b0da066d95419753481412a2a4
author : Michael Paquier <michael@paquier.xyz>
date : Wed, 7 Oct 2026 10:39:32 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Wed, 7 Oct 2026 10:39:32 +0900 reindexdb batches the REINDEX commands of all indexes attached to the
same table into a single multi-statement query when using multiple jobs,
as two steps with query "gen" and "run".
Such a query fails with REINDEX CONCURRENTLY, as this command cannot run
inside a transaction block. Batching cannot be avoided because the
indexes of one table have to be re-indexed one at a time to avoid
conflicts across jobs.
Let's error out cleanly on the reindexdb side if this combination of
options is used, instead of having the server complain about that. This
case has not been considered by 47f99a407d.
If there is interest in supporting this combination of options, the code
should be redesigned for this purpose with some better batching, rather
than try to use sets of "gen" and "run" routines. As currently written,
an index-level reindexdb uses a set of "gen" steps with one final "run"
step, which is nice code-wise. (Note: I doubt that that this would be
worth the effort.)
Author: Kirill Reshke <reshkekirill@gmail.com>
Discussion: https://postgr.es/m/CALdSSPiEkAmS+n7Ea9XDMrLjkbQk=CrB7zw1kcq9B47hfKUhXw@mail.gmail.com
Backpatch-through: 17 M src/bin/scripts/reindexdb.c
M src/bin/scripts/t/090_reindexdb.pl
Don't attempt to upgrade bpchar indexes from versions before 14.
commit : 0fc4077f7f51a2e38924026e89862c150b784c5f
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Tue, 6 Oct 2026 21:16:52 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Tue, 6 Oct 2026 21:16:52 -0400 As of a32b8dc98 and friends, amcheck will complain about bpchar
indexes that claim to allow de-duplication. This causes our
cross-version-upgrade tests to fail when upgrading from a version
that lacks those commits, since there are some such indexes in the
core regression tests. Drop such indexes from the upgrade source
database. (oidvector indexes would be problematic too, but
there aren't any user-defined indexes on oidvector in our tests.)
Author: Peter Geoghegan <pg@bowt.ie>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAH2-Wzk-oO3HjLaByo8Vpse6Tpha1mqAW8DFShh-kS-Wo59ugA@mail.gmail.com
Backpatch-through: 14 M src/test/perl/PostgreSQL/Test/AdjustUpgrade.pm
Rescind unsafe deduplication support.
commit : 74f26a193417093ac5caff2da6a7b8ac69b16254
author : Peter Geoghegan <pg@bowt.ie>
date : Tue, 6 Oct 2026 18:21:33 -0400
committer: Peter Geoghegan <pg@bowt.ie>
date : Tue, 6 Oct 2026 18:21:33 -0400 bpchar equality ignores trailing spaces, so equal values in a column
with no length specifier can have different images. Similarly,
oidvector equality intentionally pays no attention to the array lower
bound field. We nevertheless allowed nbtree deduplication to be used
with indexes built with bpchar_ops, bpchar_pattern_ops, or
oidvector_ops. A posting list split could therefore place a TID in an
index tuple whose key was equal to, but not bytewise identical to, the
corresponding table value. This allowed index-only scans to return
subtly wrong results.
To fix, drop support function 4 from the affected opclasses (we can't
change catalog contents on branches 14 through 18, so we just disable
deduplication by adding hard-coding to the relevant support functions).
Deliberately leave the oidvector opclass alone on branches 14 through
18, though. Disallowing oidvector deduplication on these branches would
make pg_amcheck report that pg_proc_proname_args_nsp_index has a corrupt
metapage, needlessly alarming users.
System catalog oidvector values always have a lower bound of 0, so the
issue can't cause harm there; a user can only hit it by indexing an
oidvector column that stores the output of a function like trim_array().
It seems very unlikely that any user would ever do that.
Users should REINDEX any B-Tree index on a bpchar column.
Author: Shihao Zhong <zhong950419@gmail.com>
Co-authored-by: Peter Geoghegan <pg@bowt.ie>
Reported-by: Ke <kehan5800@gmail.com>
Reported-by: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Bug: #19749
Discussion: https://postgr.es/m/19749-d46adf7c7d910b07@postgresql.org
Discussion: https://postgr.es/m/CAGRkXqQQ311Lg=62x5UdZtPuRFgpT6XpPMw4X9E3tgbTu162Mw@mail.gmail.com
Backpatch-through: 14 M contrib/amcheck/verify_nbtree.c
M doc/src/sgml/btree.sgml
M src/backend/utils/adt/datum.c
M src/backend/utils/adt/varlena.c
M src/test/regress/expected/btree_index.out
M src/test/regress/sql/btree_index.sql
Use a non-locking initial test in TAS_SPIN on AArch64.
commit : e10dc5b853aaf59f5d792068285142638f039aae
author : Nathan Bossart <nathan@postgresql.org>
date : Tue, 6 Oct 2026 14:32:42 -0500
committer: Nathan Bossart <nathan@postgresql.org>
date : Tue, 6 Oct 2026 14:32:42 -0500 Our testing showed that this is helpful at sufficiently high
contention levels and doesn't hurt performance on smaller machines.
The new TAS_SPIN macro for AArch64 is identical to the ones added
for PPC and x86_64 (see commits bc2a050d40 and b03d196be0).
Back-patch of commit 3d0b4b1068, which first shipped in v18.
Salvatore Dipietro reports that this change is beneficial on newer
Linux kernels, and the original commit has aged long enough to
provide some confidence that it won't cause problems. v14 is left
alone since it lacks commit a82a5eee31 and will soon be out of
support.
Reported-by: Salvatore Dipietro <dipiets@amazon.it>
Reviewed-by: Jingtang Zhang <mrdrivingduck@gmail.com>
Reviewed-by: Andres Freund <andres@anarazel.de>
Tested-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/ZxgDEb_VpWyNZKB_%40nathan
Discussion: https://postgr.es/m/20260915114623.431827-1-dipiets%40amazon.it
Backpatch-through: 15 M src/include/storage/s_lock.h
Doc: correct overambitious precision claim for contrib/seg.
commit : 826986a053a6df3e8153af974e3c42b7798c4360
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Tue, 6 Oct 2026 15:07:01 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Tue, 6 Oct 2026 15:07:01 -0400 seg.sgml claims that a seg can store 7 significant digits, but this
is nonsense. The underlying float4 data type is not (quite) that
precise, and seg_out() intentionally rounds off to at most FLT_DIG
decimal places. With IEEE float arithmetic, FLT_DIG will be defined
as 6, or at least that's the case on all platforms I know of.
AFAICT this thinko goes clear back to the introduction of seg
(commit a3694b420), but it wasn't questioned until now.
Bug: #19744
Reported-by: Ke <kehan5800@gmail.com>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/19744-8c700582bbb1d075@postgresql.org
Backpatch-through: 14 M doc/src/sgml/seg.sgml
Fix COPY FROM ... WHERE for negated operators.
commit : 6b428e47b050bddb9316a53402fb19ac452c05bd
author : Nathan Bossart <nathan@postgresql.org>
date : Tue, 6 Oct 2026 09:04:32 -0500
committer: Nathan Bossart <nathan@postgresql.org>
date : Tue, 6 Oct 2026 09:04:32 -0500 COPY FROM runs its WHERE clause through eval_const_expressions(),
which among other things replaces NOT above an operator with the
operator's negator, if it has one (e.g., NOT (a > 0) becomes
a <= 0). The node it builds for the negator has no opfuncid, and
since COPY FROM never calls fix_opfuncids() on the clause, nothing
fills it in before the executor needs it. So
COPY tab FROM stdin WHERE NOT (a > 0)
fails with "cache lookup failed for function 0".
To fix, call fix_opfuncids() on the clause, as the relcache does
for index predicates.
Oversight in commit 31f3817402.
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/asPZRTci4oBvxR9A%40nathan
Backpatch-through: 14 M src/backend/commands/copy.c
M src/test/regress/expected/copy2.out
M src/test/regress/sql/copy2.sql
postgres_fdw: Fix typo in server version check in AcquireSampleRowsFunc.
commit : 3efbf3e1b4275904299d9a0d16f5e9a8267b8dd0
author : Etsuro Fujita <efujita@postgresql.org>
date : Tue, 6 Oct 2026 18:30:04 +0900
committer: Etsuro Fujita <efujita@postgresql.org>
date : Tue, 6 Oct 2026 18:30:04 +0900 s/95000/90500/g
Oversight in commit 8ad51b5f4.
Author: Etsuro Fujita <etsuro.fujita@gmail.com>
Reviewed-by: Chao Li <lic@highgo.com>
Reviewed-by: Daniel Gustafsson <daniel@yesql.se>
Discussion: https://postgr.es/m/CAPmGK143-FdvSCmKc3bqfAQeGNs_aqVqF3op25TR_NTfBmL4BQ%40mail.gmail.com
Backpatch-through: 16 M contrib/postgres_fdw/postgres_fdw.c
Flatten join alias Vars before rechecking subquery pullup safety
commit : c37a2bb54ccc1e56e2c60aed161a89ce27c676f4
author : Richard Guo <rguo@postgresql.org>
date : Tue, 6 Oct 2026 14:40:17 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Tue, 6 Oct 2026 14:40:17 +0900 pull_up_simple_subquery() rechecks is_simple_subquery() after pulling
up the subquery's own subqueries, but it flattened join alias Vars in
the subquery's targetlist only after that, and never in its quals. A
join alias Var within the subquery, such as a whole-row Var of a join,
can expand to an expression containing lateral references to the outer
query once the join's inputs have been pulled up, and until it is
flattened is_simple_subquery() cannot see them. We could thus pull up
a LATERAL subquery whose targetlist or quals reference rels outside
the lowest outer join above it. This leads to assertion failures in
distribute_qual_to_rels() or "wrong phnullingrels" errors, and in some
cases to wrong query results, where rows that should have been
preserved by an outer join above the subquery are discarded.
To fix, flatten join alias Vars in the targetlist, and in the jointree
quals if the subquery is LATERAL, before the recheck. If the recheck
fails, the flattened copy is discarded along with the rest of the
modified subquery, so no harm is done.
Back-patch to all supported branches, as this can lead to wrong query
results.
Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Tatsuya Kawata <kawatatatsuya0913@gmail.com>
Discussion: https://postgr.es/m/CAMbWs48GFZ=3Bjc1ug9JRsB0Qupg+CGcb9js5vmTM6kY2OGWOg@mail.gmail.com
Backpatch-through: 14 M src/backend/optimizer/prep/prepjointree.c
M src/test/regress/expected/join.out
M src/test/regress/sql/join.sql
Don't reprocess PHV copies within outer aggregates' arguments
commit : 00d1b4598aba11a82c406120eb86d38c6f3f5c6c
author : Richard Guo <rguo@postgresql.org>
date : Tue, 6 Oct 2026 10:59:11 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Tue, 6 Oct 2026 10:59:11 +0900 Since commit e28cf96e9, the copies of a query level's PlaceHolderVars
that were pushed down into its subqueries are preprocessed by that
level before its SubLinks are turned into SubPlans, so such a copy may
already contain SubPlans by the time the subquery is planned. That
commit taught build_subplan not to process the SubLinks of a PHV that
is passed down as a SubPlan argument, but overlooked that such a copy
can also appear within the arguments of an outer-level Aggref,
GroupingFunc, or ReturningExpr. The SubLinks in those arguments are
still processed when build_subplan collects the node, and that
processing descended into the already-preprocessed PHV and tripped the
no-SubPlan assertion in process_sublinks_mutator.
To fix, don't descend into PHVs when processing the SubLinks in such a
node's arguments. Any PHV found there is a pushed-down copy that its
owning level has already preprocessed.
Back-patch to v16, as with commit e28cf96e9.
Author: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Reviewed-by: Richard Guo <guofenglinux@gmail.com>
Discussion: https://postgr.es/m/CAJTYsWW8THbrnsmYnDqnUgg0PjGrqw2vNOWb8qx7eu191KPW3g@mail.gmail.com
Backpatch-through: 16 M src/backend/optimizer/plan/subselect.c
M src/test/regress/expected/join.out
M src/test/regress/sql/join.sql
Doc: remove stray right brace in TRANSFORM option syntax summaries.
commit : 83764809acb4ecb6a199ee4e04519e610c8b16c6
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 5 Oct 2026 20:40:03 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 5 Oct 2026 20:40:03 -0400 Author: Lele Gaifax <lele@metapensiero.it>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/87bj98oaen.fsf@metapensiero.it
Backpatch-through: 14 M doc/src/sgml/ref/create_function.sgml
M doc/src/sgml/ref/create_procedure.sgml
Fix pg_blocking_pids() for relation extension locks.
commit : c0df269c5b9d33568ed35a7bfc92bcd967ed4a56
author : Nathan Bossart <nathan@postgresql.org>
date : Mon, 5 Oct 2026 14:52:18 -0500
committer: Nathan Bossart <nathan@postgresql.org>
date : Mon, 5 Oct 2026 14:52:18 -0500 pg_blocking_pids() assumes that members of the same lock group
never block each other, but that hasn't been true for relation
extension locks since v13. This commit teaches the function to
report lock group members as blockers for relation extension locks.
Since parallel workers are reported with their leader's PID, the
result may now include the blocked session's own PID.
Oversight in commit 85f6b49c2c.
Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Kirill Reshke <reshkekirill@gmail.com>
Reviewed-by: Shihao Zhong <zhong950419@gmail.com>
Discussion: https://postgr.es/m/apTpV8h%2BCYHf%2B1PJ%40bdtpg
Backpatch-through: 14 M doc/src/sgml/func.sgml
M src/backend/utils/adt/lockfuncs.c
Handle operator-stack-overflow errors softly in tsquery and ltxtquery.
commit : ffbe37115ad1016e78e59de14c7f86ed8aaefc3e
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 5 Oct 2026 15:45:40 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 5 Oct 2026 15:45:40 -0400 When we converted these types' input functions to report errors
through the soft-error mechanism, we missed that stack-overflow
errors can be triggered through bad user input. Convert those
to be reported softly, too.
Bug: #19745
Reported-by: Ke <kehan5800@gmail.com>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/19745-39cd7b93a505f9a9@postgresql.org
Backpatch-through: 16 M contrib/ltree/ltxtquery_io.c
M src/backend/utils/adt/tsquery.c
Fix tuplesort memory accounting for datum sorts on byref types
commit : 2414028ab64bcffccfee75790a15b5960de8d095
author : David Rowley <drowley@postgresql.org>
date : Tue, 6 Oct 2026 01:22:23 +1300
committer: David Rowley <drowley@postgresql.org>
date : Tue, 6 Oct 2026 01:22:23 +1300 6ed83d5fa made it so tuplesorts could use a bump.c memory allocator for
non-bounded sorts in order to save memory and increase performance.
That commit didn't get the memory accounting correct for datum sorts and
mistakenly passed 0 bytes to tuplesort_puttuple_common(). That was
correct for byval types, but failed to take into account palloc'd memory
for byref types.
Fix by using datumGetSize() when using the bump allocator and by using
GetMemoryChunkSpace() when not.
Backpatch to v17, where 6ed83d5fa was added.
Author: Mario Karuza <mkaruza.pg@icloud.com>
Reviewed-by: David Rowley <dgrowleyml@gmail.com>
Reviewed-by: Joao Detomini <joao.detomini@enterprisedb.com>
Discussion: https://postgr.es/m/e9f9f54337cd86a982837763958518cdbf08863c.camel%40icloud.com
Backpatch-through: 17 M src/backend/utils/sort/tuplesortvariants.c
Don't sleep with vacuum_delay_point() while holding locks
commit : 71d1ace33c77914bcf0ee591f8fc57fd186cb18a
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Mon, 5 Oct 2026 13:12:33 +0300
committer: Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Mon, 5 Oct 2026 13:12:33 +0300 It's a bad idea to sleep while holding locks, because you might block
another process that wants to acquire the same lock. Furthermore,
it's not safe to call ProcessConfigFile() in a critical section or
while holding locks. So avoid calling vacuum_delay_point() while
holding locks, and also make vacuum_delay_point() return quickly
without sleeping if it's called while interrupts cannot be processed.
To find the vacuum_delay_point() calls that were made while holding
locks, I added "Assert(INTERRUPTS_CAN_BE_PROCESSED())" in
vacuum_delay_point() and ran the regression tests. I didn't include
that Assert in this commit, because there might be more places that do
that that are not covered by the regression tests, including
extensions.
In ginInsertCleanup(), there was already another vacuum_delay_point()
call later in the loop, while not holding any locks, so just remove
the other call that was made while holding the lock.
In hashbucketcleanup(), move the vacuum_delay_point() call up the
stack to its caller. The call in hashbucketcleanup() was not able to
handle interrupts because it held a lock, and because there were no
vacuum_delay_point() or CHECK_FOR_INTERRUPTS() calls in the caller's
loop, the whole hash index vacuuming phase was uninterruptible by
pending shutdown or query cancel. Now it can be interrupted between
buckets. Unfortunately, hashbucketcleanup() has to process all the
bucket's pages in one go without pausing; fixing that would require
changing how hashbucketcleanup()'s lock chaining works.
In acquire_sample_rows(), move the vacuum_delay_point() call in the
loop to between pages, to a time where we're not holding the buffer
lock.
In master, also remove a misleading CHECK_FOR_INTERRUPTS() call from
pgstat_write_statsfile(). It was a no-op because we're always holding
a lock on the current dshash partition at that point. A call that
does nothing is harmless, but it makes you think that the loop is
interruptible when in reality it's not.
Author: Kevin Rocker <me@kevinrocker.com>
Author: Andrey Borodin <amborodin@acm.org>
Author: Mostafa <mostafa.nabil.nafie@gmail.com>
Reported-by: Greg Burd <greg@burd.me>
Reported-by: Sergei Kornilov <sk@zsrv.org>
Reviewed-by: Kirill Reshke <reshkekirill@gmail.com>
Reviewed-by: Neil Chen <carpenter.nail.cz@gmail.com>
Tested-by: Greg Burd <greg@burd.me>
Discussion: https://postgr.es/m/19628-c2b17d358181a1ea@postgresql.org
Discussion: https://postgr.es/m/492c6247-43d3-477b-8981-fb0c56767b38%40app.fastmail.com
Backpatch-through: 14 M src/backend/access/gin/ginfast.c
M src/backend/access/hash/hash.c
M src/backend/commands/analyze.c
M src/backend/commands/vacuum.c
Fix relids of EC-derived join clauses for lateral UNION ALL members
commit : b83b7ddf7a64dce41624b5c0dabefd74dfe10ec0
author : Richard Guo <rguo@postgresql.org>
date : Mon, 5 Oct 2026 15:01:48 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Mon, 5 Oct 2026 15:01:48 +0900 When a LATERAL UNION ALL subquery is flattened into an appendrel, a
child EquivalenceMember's expression can contain lateral references to
other relations. create_join_clause() builds a clause from such a
member with make_restrictinfo(), which computes clause_relids from the
expression, and so includes the relids of the lateral references along
with those of any outer joins that null them. Commit 03107b4ed made
sure that clause_relids include the child's relids, but it only ever
added relids.
This is a problem when a lateral reference is nullable. The child is
parameterized by its lateral_relids, which do not include the outer
joins that null the lateral references, so the clause fails the
join_clause_is_movable_into() assertion that
get_baserel_parampathinfo() applies to EC-derived clauses.
To fix, set a child clause's clause_relids from its members' em_relids
rather than from their expressions. This matches how
adjust_appendrel_attrs() computes clause_relids for a child's other
join clauses. The lateral references need not be included, since
every path for the child is parameterized by at least its
lateral_relids. Also update num_base_rels to match, which commit
03107b4ed neglected to do.
Back-patch to v16, where outer-join relids were added to
clause_relids. Before that, the relids of a lateral reference are
always part of the child's parameterization, so the problem does not
arise.
Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: wenhui qiu <qiuwenhuifx@gmail.com>
Discussion: https://postgr.es/m/CAMbWs4-cRxpQNyQJW=PkDagSDGQ=3Rp2t_fSAyROKsDStjKHUQ@mail.gmail.com
Backpatch-through: 16 M src/backend/optimizer/path/equivclass.c
M src/test/regress/expected/join.out
M src/test/regress/sql/join.sql
Disable autovacuum in 031_recovery_conflict.pl
commit : adc1dc6f5b107cc9603852907613839636aad5a4
author : Fujii Masao <fujii@postgresql.org>
date : Mon, 5 Oct 2026 13:02:44 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Mon, 5 Oct 2026 13:02:44 +0900 Buildfarm members adder and drongo failed to observe the expected relation
lock wait in the startup deadlock test. In adder's failure, autovacuum
pruned dead rows before the test's explicit VACUUM FREEZE, canceling the
standby backend too early and releasing the relation lock wait before the
test could observe it.
Fix this by disabling autovacuum for the test node, leaving the explicit
VACUUM commands in control of when pruning occurs. This also prevents
auto-analyze workers on other relations from holding back xmin and
interfering with the snapshot conflict test.
Backpatch to v17, where this test is enabled.
Reported-by: Alexander Lakhin <exclusion@gmail.com>
Author: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/421c0aee-84c8-4c07-b4b9-263095479755%40gmail.com
Discussion: https://postgr.es/m/714729ea-f536-0faf-353b-cbd5c8f241ea%40gmail.com
Backpatch-through: 17 M src/test/recovery/t/031_recovery_conflict.pl
Stabilize recovery conflict count checks in 031_recovery_conflict.pl
commit : 92ee148b64ddc1685b5d0e74d5a083c0b22a6db7
author : Fujii Masao <fujii@postgresql.org>
date : Mon, 5 Oct 2026 13:01:22 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Mon, 5 Oct 2026 13:01:22 +0900 Buildfarm members olingo and adder reported failures in
031_recovery_conflict.pl where a recovery conflict was counted twice.
olingo saw the snapshot conflict count reach 2 instead of 1, while adder
saw the same for the buffer pin conflict count.
The startup process can signal a standby backend again while it is
reporting a recovery-conflict FATAL error. Each processed conflict can
increment the corresponding statistic. Thus, even when the expected
conflict occurs, its counter can exceed 1. The existing poll for exactly 1
can then time out, and the exact check of the aggregate count can fail as
well.
Fix this by polling for a positive count for each conflict type and
accepting an aggregate count of at least the expected number.
Backpatch to v17, where this test was enabled.
Reported-by: Alexander Lakhin <exclusion@gmail.com>
Author: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Reviewed-by: Alexander Lakhin <exclusion@gmail.com>
Reviewed-by: Nazir Bilal Yavuz <byavuz81@gmail.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/421c0aee-84c8-4c07-b4b9-263095479755%40gmail.com
Backpatch-through: 17 M src/test/recovery/t/031_recovery_conflict.pl
Skip isolation tests that terminate other backends on Windows
commit : 5efbd9536ced70dc5d29abd320d3dc26774509ad
author : Michael Paquier <michael@paquier.xyz>
date : Mon, 5 Oct 2026 11:11:17 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Mon, 5 Oct 2026 11:11:17 +0900 On Windows, a backend exiting resets its connection, and the client
discards any data not yet read, including the FATAL message of a
terminated backend. isolationtester polls one connection at a time, so
when it waits on a different session the message can get lost, failing
the permutation and the tests that run after it.
wait_cleanup in injection_points (down to v17) and slot_creation_error
in test_decoding (down to v15) have this pattern, and have been failing
randomly in the buildfarm. Let's skip them on Windows.
The cause of the instabilities is proving to be tricky to deal with,
pointing for example at 29992a6a509b. This will be tackled later,
perhaps with a backpatch (or perhaps with a HEAD-only solution), but it
is difficult to be sure at this stage of the work. In theory,
a8458f508a7 should make a revert of 29992a6a509b (revert of a revert!)
safe on WIN32.
Note that 278cdea6b9d2, that has introduced the isolation test
slot_creation_error in test_decoding, has mentioned the potential output
instability.
Author: Zsolt Parragi <zsolt.parragi@percona.com>
Author: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/CAN4CZFOoiKa9E4Gnh_OzLk3S5Z6RFyg_oY1e_WddCVYhkpTVow@mail.gmail.com
Backpatch-through: 15 M contrib/test_decoding/Makefile
M contrib/test_decoding/meson.build
M src/test/modules/injection_points/Makefile
M src/test/modules/injection_points/meson.build
Fix join alias expansion in lateral TABLESAMPLE clauses
commit : 70cf3250610adea0ff5d15d89c65026ebfbdb80a
author : Richard Guo <rguo@postgresql.org>
date : Sun, 4 Oct 2026 16:07:10 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Sun, 4 Oct 2026 16:07:10 +0900 preprocess_expression skips flatten_join_alias_vars for TABLESAMPLE
clauses, on the assumption that they cannot contain Vars of the
current query level. That's not true once a LATERAL subquery
containing a sampled relation has been pulled up: the relation's
TABLESAMPLE clause then becomes a lateral reference of this level, and
if it referred to a join alias Var, that Var is left unexpanded.
extract_lateral_references then pulls the join alias Var out as a
lateral reference and fails with "no relation entry for relid N".
To fix, treat such clauses like lateral function and VALUES RTEs:
introduce EXPRKIND_TABLESAMPLE_LATERAL, used for the TABLESAMPLE
clause of a lateral relation RTE, and keep skipping the alias
expansion only for the non-lateral kind.
Back-patch to all supported branches, as this dates back to the
introduction of TABLESAMPLE.
Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Ewan Young <kdbase.hack@gmail.com>
Reviewed-by: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Discussion: https://postgr.es/m/CAMbWs4834TwOtyyycyvja3MuOBkfpXy3dFAL0zt_vcM1KtqV-Q@mail.gmail.com
Backpatch-through: 14 M src/backend/optimizer/plan/planner.c
M src/test/regress/expected/tablesample.out
M src/test/regress/sql/tablesample.sql
Set hasSubLinks when expanding a whole-row join alias reference
commit : 6c0698339e3d792f679fc39866fb7d3ebeb3fe09
author : Richard Guo <rguo@postgresql.org>
date : Sun, 4 Oct 2026 12:06:24 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Sun, 4 Oct 2026 12:06:24 +0900 If a subquery has been flattened into its parent, the joinaliasvars
entries of a join above it can be arbitrary expressions rather than
plain Vars, so expanding a reference to such a join alias may insert a
SubLink into a lower-level subquery. flatten_join_alias_vars_mutator
detects that and sets the subquery's hasSubLinks flag, but only in the
single-column code path; the whole-row path just asserted in a comment
that its recursive call would handle this, which is true only when the
alias entry is itself a Var referencing another join.
Hence a whole-row reference to such a join appearing in a sub-select
left that sub-select's hasSubLinks false, so preprocess_expression
skipped SS_process_sublinks for it, and the unprocessed SubLink
reached code that is not prepared for one: this produced "cannot
handle unplanned sub-select" from cost_qual_eval, an assertion failure
in preprocess_aggrefs, or "unrecognized node type" at execution,
depending on where in the sub-select the SubLink ended up.
To fix, make the same check in the whole-row expansion path.
Back-patch to all supported branches.
Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Discussion: https://postgr.es/m/CAMbWs49PgeNFhQTZ2gSzwatF0_LXyegMPGt++jSfxUN7NZuQ5Q@mail.gmail.com
Backpatch-through: 14 M src/backend/optimizer/util/var.c
M src/test/regress/expected/subselect.out
M src/test/regress/sql/subselect.sql
Fix lcov duplicate directory specification in non-VPATH builds.
commit : 2dc69bf953af145c6aba5dc9cebce374f6841bba
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Sat, 3 Oct 2026 14:44:39 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Sat, 3 Oct 2026 14:44:39 -0400 In an in-tree build, "make coverage-html" invoked lcov with two
"-d ." options, because $(srcdir) is just ".". With lcov 2.0
(and perhaps earlier versions?) that results in scanning every
foo.gcda file twice and producing counts twice what they should
be. With later lcov versions (at least 2.3), it results in
"duplicate file" errors.
In VPATH builds we do need two -d options to cover both regular
and generated .c files. But in in-tree builds it suffices to
specify "-d .", so suppress the second switch.
Author: Narek Galstyan <narek.galstyan@enterprisedb.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAHsn6_xCDQWe8_vVFhtFk27_xTdyVV+Dr0yWzaooBZ6+-VH-5w@mail.gmail.com
Backpatch-through: 14 M src/Makefile.global.in
pg_dump: Include column statistics for index-backed constraints
commit : 5e158a2e43c2e7fcc1e579f81c2f3645285cba40
author : Michael Paquier <michael@paquier.xyz>
date : Fri, 2 Oct 2026 17:02:44 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Fri, 2 Oct 2026 17:02:44 +0900 dumpIndex() emits ALTER INDEX .. SET STATISTICS for per-column
statistics targets, but dumpConstraint() never did. Targets set on the
expression columns of an exclusion constraint's index were silently
lost on dump/restore and pg_upgrade.
The same code can be used in the index and constraint dump code to
generate these ALTER INDEX commands. The logic has been refactored so
that both share a new subroutine in charge of generating all these
custom SET STATISTICS, if any.
Oversight in e4fca461ab6f.
Author: Zsolt Parragi <zsolt.parragi@percona.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Andrew Krylosov <krylosov.andrew@gmail.com>
Discussion: https://postgr.es/m/CAN4CZFMd9riOuV5LkM+uV=uF-HYp49C8Fh22xWyMxptGLBCZ3A@mail.gmail.com
Backpatch-through: 14 M src/bin/pg_dump/pg_dump.c
M src/bin/pg_dump/t/002_pg_dump.pl
Fix crash when describing a FETCH statement whose cursor is gone
commit : 2affc196466b46f6ad51245300f47f101c9d1922
author : Michael Paquier <michael@paquier.xyz>
date : Fri, 2 Oct 2026 10:43:30 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Fri, 2 Oct 2026 10:43:30 +0900 FetchStatementTargetList() dereferenced the portal named by a
FetchStmt without checking that it still exists. With the extended
query protocol, a client can sent a Parse "FETCH n FROM c" while cursor
"c" is open (which caches a result's TupleDesc), issue a CLOSE on cursor
"c", and then Describe the prepared statement or a portal bound to it.
Describe would re-resolve the target list through the named cursor, and
crash. In a non-assert builds, this causes a NULL pointer dereference.
An assertion was triggered in assert builds slightly before the pointer
dereference.
This commit changes the describe of a FetchStmt to match with the
ExecuteStmt case: if the named cursor's portal is invalid, fail rather
than assume that the portal should always exist.
A test case is added to libpq_pipeline, backpatchable all the way down.
Author: Dirkjan Bussink <d.bussink@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/7DE248EB-7509-4F8A-9F37-4694A40BBAB7@gmail.com
Backpatch-through: 14 M src/backend/tcop/pquery.c
M src/test/modules/libpq_pipeline/libpq_pipeline.c
Doc: mention platform-dependent behavior of bitshift operators
commit : 2250163c3964eec31eef74182f5bd772d4adb543
author : David Rowley <drowley@postgresql.org>
date : Thu, 1 Oct 2026 15:17:28 +1300
committer: David Rowley <drowley@postgresql.org>
date : Thu, 1 Oct 2026 15:17:28 +1300 There have been several recent complaints that << and >> don't behave
the same way on all supported platforms. There is a risk that <<
overflows the signed integer type, which is undefined behavior, and there
are platform-dependent behaviors from shifting by numbers of bits >= the
bitwidth of the type, and also from shifting by negative values.
Document this to warn unsuspecting users of these facts.
Author: David Rowley <dgrowleyml@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAApHDvr0O6ZF8NO=Rq9Q+Sz=FHT=NZ1kkNTXZDcNph-OWuk7TQ@mail.gmail.com
Backpatch-through: 14 M doc/src/sgml/func.sgml
Update time zone data files to tzdata release 2026e.
commit : 1ad6311420d47be5a036453b16a9450bdf2fef94
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Wed, 30 Sep 2026 17:28:14 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Wed, 30 Sep 2026 17:28:14 -0400 Canada's Northwest Territories will move to permanent UTC-06 on
2026-11-01. For now, assume their TZ abbreviation will be CST from
that time forward. This affects only America/Inuvik as the rest of
the territory is covered by America/Edmonton, for which the
equivalent change was released in 2026c.
Manitoba (America/Winnipeg) will move to permanent UTC-05 on
2026-10-31. For now, assume their TZ abbreviation will be EST
from that time forward.
(Upstream notes that more changes may be needed soon if smaller
jurisdictions choose to break away from what these have done,
but for now the plan is wait-and-see.)
Historical corrections for Colombia, Iran, and Ireland.
The backward-compatibility zone names EST5EDT, CST6CDT, MST7MDT, and
PST8PDT now conform better to POSIX. For example, EST5EDT now always
uses the abbreviation "EST" for standard time (now always 5 hours
behind UTC) and "EDT" for daylight saving time, whereas it formerly
had different UT offsets before standard time was introduced and
sometimes used abbreviations like "LMT", "EWT" and "EPT", all contrary
to POSIX. Also, though not specified by POSIX these names now use US
federal rules rather than rules of specific places like New York,
reverting to 2024a behavior. This change affects only timestamps
before 1966-10-30 at 01:00 standard time.
Backpatch-through: 14 M src/timezone/data/tzdata.zi
Fix clobbering of proc entry's statusFlags during slot invalidation
commit : 295f75b4bb60c22acb7bdcd7aebf7bb8965658aa
author : Michael Paquier <michael@paquier.xyz>
date : Wed, 30 Sep 2026 14:37:19 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Wed, 30 Sep 2026 14:37:19 +0900 ReplicationSlotRelease() copied MyProc->statusFlags unconditionally
when resetting PROC_IN_LOGICAL_DECODING, using its pgxactoff to decide
which area to update.
Slot invalidations can be done by the checkpointer or the startup
process, and ReplicationSlotRelease() ignored the fact that pgxactoff is
only meaningful for processes in the proc array, that auxiliary
processes never join. pgxactoff would remain at 0 for these, meaning
that an incorrect set of status flags would be updated.
The consequences of this issue could be various, depending on the
statsFlags lost in the proc slot incorrectly overwritten. Losing
PROC_AFFECTS_ALL_HORIZONS has been mentioned as the worse case, causing
VACUUM to remove rows a standby may needs even if standby feedback is
enabled. Assertions failures are another possibility.
Let's skip the update of statusFlags unless PROC_IN_LOGICAL_DECODING is
set. Nothing but StartupDecodingContext() sets it, and no auxiliary
processes call it.
Author: Vlad Lesin <vladlesin@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/889a06dd-45fc-423b-9dd2-87d5b5dc60c6@gmail.com
Backpatch-through: 14 M src/backend/replication/slot.c
Doc: add missing new line in DO example block
commit : 104bec3af45c007608f8e35e73a11a027f7b21c7
author : David Rowley <drowley@postgresql.org>
date : Wed, 30 Sep 2026 02:17:31 +1300
committer: David Rowley <drowley@postgresql.org>
date : Wed, 30 Sep 2026 02:17:31 +1300 Reported-by: Oli Sennhauser <oli.sennhauser@fromdual.com>
Discussion: https://postgr.es/m/777637c4-9d31-498d-a8ef-645fb510bf5a@fromdual.com
Backpatch-through: 14 M doc/src/sgml/ref/do.sgml
doc: Fix incorrect output in example
commit : fe66782df5dde55571316211a747f5a0ff7157e9
author : Daniel Gustafsson <dgustafsson@postgresql.org>
date : Tue, 29 Sep 2026 10:49:15 +0200
committer: Daniel Gustafsson <dgustafsson@postgresql.org>
date : Tue, 29 Sep 2026 10:49:15 +0200 Reported-by: y.saburov@gmail.com
Reviewed-by: David G. Johnston <david.g.johnston@gmail.com>
Discussion: https://postgr.es/m/CAKFQuwZ5zQzv1uyoH1UD5ETb7+ArqF2zNXVU4Et+b0_E8bS2sQ@mail.gmail.com
Backpatch-through: 14 M doc/src/sgml/func.sgml
Fix incorrect calcSumX2 in numeric deserialize functions
commit : 583b414e51105ba6554137e0df166a68b149b593
author : David Rowley <drowley@postgresql.org>
date : Tue, 29 Sep 2026 15:49:31 +1300
committer: David Rowley <drowley@postgresql.org>
date : Tue, 29 Sep 2026 15:49:31 +1300 calcSumX2 is used to inform the aggregate transition function whether it
should include the square of the transition value in the aggregate
state. The deserialize functions for various numeric aggregates that do
use calcSumX2 weren't properly setting the property to true. This does
not matter in core as we never perform any aggregate transitions after a
deserialize operation.
This fix is only required for external code which plumbs aggregate paths
together in such a way that could result in aggregate transitions being
performed after states have been deserialized.
Author: Maxime Schoemans <maxime.schoemans@enterprisedb.com>
Reviewed-by: Andrey Borodin <x4mmm@yandex-team.ru>
Discussion: https://postgr.es/m/FEBF5C3D-CC53-455F-AA9D-4D377A2E957C@enterprisedb.com
Backpatch-through: 14 M src/backend/utils/adt/numeric.c
Update .abi-compliance-history for change in struct LogicalRepRelMapEntry.
commit : 6dba8019b64495f29659bb5096a678c040660647
author : Amit Kapila <akapila@postgresql.org>
date : Mon, 28 Sep 2026 08:41:24 -0400
committer: Amit Kapila <akapila@postgresql.org>
date : Mon, 28 Sep 2026 08:41:24 -0400 Author: vignesh C <vignesh21@gmail.com>
Discussion: https://postgr.es/m/CADzfLwUJovFcnknCC9wjZKECX9xecgnGzC2r2TMV8h4QDD_jwQ@mail.gmail.com
Backpatch-through: 16-18 M .abi-compliance-history
Fix tuple search during apply after concurrent index DDL.
commit : 0405dcf7c6b1162dd1e4377d1862aa0a38629e6b
author : Amit Kapila <akapila@postgresql.org>
date : Mon, 28 Sep 2026 06:44:47 -0400
committer: Amit Kapila <akapila@postgresql.org>
date : Mon, 28 Sep 2026 06:44:47 -0400 When searching the local relation by index, the apply worker can use the
first match only if the index is the relation's replica identity or
primary key. Otherwise, every match must be compared with the search slot.
Only the index OID was saved in the relation map, so this was determined
again during the search. Concurrent DROP INDEX or REINDEX can change the
index status between these two checks, causing the tuple search to fail
and the change to be silently skipped as an update_missing conflict.
Fix this by recording the answer as idxisreplident in the relation map
entry and avoid getting it second time in FindReplTupleInLocalRel() and
RelationFindReplTupleByIndex().
Oversight in 89e46da5e5.
Author: Mikhail Nikalayeu <mihailnikalayeu@gmail.com>
Author: vignesh C <vignesh21@gmail.com>
Reviewed-by: Amit Kapila <amit.kapila16@gmail.com>
Reviewed-by: Zhijie Hou <houzj.fnst@fujitsu.com>
Discussion: https://postgr.es/m/CADzfLwUJovFcnknCC9wjZKECX9xecgnGzC2r2TMV8h4QDD_jwQ@mail.gmail.com
Backpatch-through: 16, where it was introduced M src/backend/executor/execReplication.c
M src/backend/replication/logical/relation.c
M src/backend/replication/logical/worker.c
M src/include/executor/executor.h
M src/include/replication/logicalrelation.h
M src/test/subscription/Makefile
M src/test/subscription/meson.build
M src/test/subscription/t/032_subscribe_use_index.pl
Prevent stale pg_locks.waitstart values
commit : 0165f38d11ec00a0efacccb746c2d1d6309b0f0b
author : Fujii Masao <fujii@postgresql.org>
date : Mon, 28 Sep 2026 14:22:04 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Mon, 28 Sep 2026 14:22:04 +0900 Previously, pg_locks.waitstart could report the start time of a previous
lock wait for a new wait. For a regular backend, this could happen briefly
before the new start time was stored. On a standby, the startup process could
show the old time throughout the next wait, making it appear to have started
earlier than it did.
This happened because PGPROC->waitStart could remain set after a previous
wait in two cases. First, RemoveFromWaitQueue() did not clear it when a wait
was canceled due to a lock timeout, cancellation, or deadlock. Second, even
after a successful lock grant, ProcWakeup() could clear it before the waiter
stored its new start time.
Fix this by clearing waitStart in RemoveFromWaitQueue() when a failed wait
is removed, at the end of ProcSleep() to handle writes made after
ProcWakeup() cleared it, and in LockErrorCleanup() when an error bypasses
the ProcSleep() reset. This prevents pg_locks.waitstart from showing the
previous wait's time for a new wait.
Backpatch to all supported versions.
Author: Shihao Zhong <zhong950419@gmail.com>
Reported-by: Alex Shapalov <shapalov@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Reviewed-by: Andrew Krylosov <krylosov.andrew@gmail.com>
Discussion: https://postgr.es/m/CAPrb+Q+XN=sNusXiUeWmMo2H7Qgq3Y4uPekSSLkHcnCyf7GhXg@mail.gmail.com
Discussion: https://postgr.es/m/CAGRkXqQLxZBr-ouVrtaX2utMggi4+TiVMbgH04b_0JLgKryxbA@mail.gmail.com
Backpatch-through: 14 M src/backend/storage/lmgr/lock.c
M src/backend/storage/lmgr/proc.c
Enforce pg_class.relchecks limit (32,767 or INT16_MAX)
commit : 9ca1d5726c21e3ae182ce461d439e360a0658ad8
author : Michael Paquier <michael@paquier.xyz>
date : Mon, 28 Sep 2026 06:55:08 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Mon, 28 Sep 2026 06:55:08 +0900 pg_class.relchecks reports the number of CHECK constraints in a table,
and is stored within a smallint. It was possible to define a table that
could overflow the counter when specifying more than 32k constraints
(new table or adding a constraint). This would cause the table to
become undroppable, as this counter should never be negative when
dropping these constraints.
Author: Matthias van de Meent <boekewurm+postgres@gmail.com>
Reviewed-by: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Jian He <jian.universality@gmail.com>
Reviewed-by: Sivaprasad <sivaprasad.postgres@gmail.com>
Discussion: https://postgr.es/m/CAEze2WjSW4qaHYnvkTtbknp85eGEirGJTLMZatotdJJKiA+_hA@mail.gmail.com
Backpatch-through: 14 M doc/src/sgml/limits.sgml
M src/backend/catalog/heap.c
Don't emit garbage "\[un]restrict (null)" commands in pg_dump.
commit : 45fc3ce22482ec50c0e1b5817782970bf03dbc0b
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Sat, 26 Sep 2026 12:55:40 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Sat, 26 Sep 2026 12:55:40 -0400 _reconnectToDB() failed to consider the possibility that it could
be called without any restrict_key set. This is reachable while
generating the "restore.sql" file in tar dump mode, which nobody
noticed because that file is mostly unused (and nothing really
bad happens anyway unless you specified --inserts mode). But
I wonder if there are other code paths that reach the issue.
Bug: #19718
Reported-by: Pavel Kopylov <pkopylov@cloudlinux.com>
Author: Pavel Kopylov <pkopylov@cloudlinux.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/19718-7e945d0ff9b5a589@postgresql.org
Backpatch-through: 14 M src/bin/pg_dump/pg_backup_archiver.c
Doc: clarify there's no log_line_prefix in csvlog or jsonlog output.
commit : 163288afd32a448244c8df75b3c33181aadd7f20
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Fri, 25 Sep 2026 18:36:17 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Fri, 25 Sep 2026 18:36:17 -0400 The documentation didn't make clear that "log_line_prefix" is
not output when "log_destination" is "csvlog" or "jsonlog".
Reported-by: Michael Schönitzer <michael@schoenitzer.de>
Author: Laurenz Albe <laurenz.albe@cybertec.at>
Co-authored-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/179032875673.1026201.6950455317699774504@wrigleys.postgresql.org
Backpatch-through: 14 M doc/src/sgml/config.sgml
Modernize crypt-des.c a bit, avoiding dubious casts.
commit : 5f664e6128235f7446ab02354ca9345a55f97255
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Fri, 25 Sep 2026 15:34:38 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Fri, 25 Sep 2026 15:34:38 -0400 The header comment for this file says it assumes that "the CPU is not
picky about alignment", which we wouldn't really accept for Postgres.
The two functions that have alignment requirements have been hacked in
ways that are inconsistent and yet both ugly. des_setkey just casts
its "const char *" argument to "const uint32 *", relying on the caller
to ensure that that pointer is actually word-aligned, which the caller
does by declaring the buffer as uint32[] and then casting to "char *".
Meanwhile des_cipher carefully memcpy's to and from an internal
uint32[] buffer, which is pretty silly given that what it's passed
must be word-aligned for the benefit of des_setkey. And on top of
that we have a bunch of ugly casting and oddly-written pointer
arithmetic in the caller px_crypt_des. The odd pointer arithmetic
causes warnings about possible buffer overrun when using recent gcc
at -O3 or higher.
Let's clean this up by converting the buffer variable into a union
of a uint8 array and a uint32 array, so that we can remove all these
casts, and also replace the strange loop logic with a simple subscript
variable to satisfy gcc.
Reported-by: Andres Freund <andres@anarazel.de>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Co-authored-by: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Discussion: https://postgr.es/m/2005885.1790359097@sss.pgh.pa.us
Discussion: https://postgr.es/m/3nuudxv365kjnmwjhnygdakhxuktpdjvf26rt26eb44esgqdrj@y2x3vomkrfoo
Backpatch-through: 14 M contrib/pgcrypto/crypt-des.c
Silence another "may be used uninitialized" compiler warning.
commit : e44d3f6c144f29a43b282dd476b3c4224a3cb887
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Thu, 24 Sep 2026 17:57:05 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Thu, 24 Sep 2026 17:57:05 -0400 gcc 12.5.0 complains about this instance in LookupFuncWithArgs().
This is the same type of problem fixed in commit f04781df5, and
it's just as obscure why this compiler version picks on this case
when others don't. Still, silence it in the same way as f04781df5.
Author: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/1812498.1790279465@sss.pgh.pa.us
Backpatch-through: 14 M src/backend/parser/parse_func.c
Wait for transactions of an initial decoding snapshot to commit
commit : b93df6eacb76fdb89a7bb4c0aac1b2e62814c242
author : Álvaro Herrera <alvherre@kurilemu.de>
date : Thu, 24 Sep 2026 11:52:47 +0200
committer: Álvaro Herrera <alvherre@kurilemu.de>
date : Thu, 24 Sep 2026 11:52:47 +0200 SnapBuildInitialSnapshot() converts the snapshot builder's list of
committed transactions into a regular MVCC snapshot, which is then used
with HeapTupleSatisfiesMVCC(). However, a snapshot produced that way
and used as an MVCC snapshot can potentially cause data corruption, if
it captures a transaction after it writes its commit WAL record but
before it has updated its CLOG entry: it will be incorrectly used to set
hint bits as if that transaction had aborted.
Fix by having SnapBuildInitialSnapshot() wait until such transactions
have removed themselves from procarray, which guarantees correct
visibility.
Other uses of SnapBuildBuildSnapshot only use the snapshot as
historical, so they don't need the waits, but add commentary in that
function to alert possible future callers.
While at this, backpatch commit 504fe10d1d26, "Fix misplaced comment in
snapbuild.c." The comment it moved was nearby the place we modify.
This bug was diagnosed by stress-testing of REPACK (CONCURRENTLY) in
branch 19, so add a test case that tickles it using that feature in that
branch and master. However, the bug exists in all branches, and in
branch 18 we can add a test with regular logical decoding using
injection points that tickles it. Unfortunately that test cannot be
backpatched to earlier branches for lack of facilities.
Reported-by: Mihail Nikalayeu <mihailnikalayeu@gmail.com>
Author: Antonin Houska <ah@cybertec.at>
Author: Rui Zhao <zhaorui126@gmail.com>
Backpatch-through: 14
Discussion: https://postgr.es/m/85833.1768840165@localhost
Discussion: https://postgr.es/m/CADzfLwU78as45To9a%3D-Qkr5jEg3tMxc5rUtdKy2MTv4r_SDGng%40mail.gmail.com M src/backend/replication/logical/snapbuild.c
Fix incorrect block accounting in TID Range Scans
commit : 25ebf0097fcc1aaf7bb56816e7508142c1613ab9
author : David Rowley <drowley@postgresql.org>
date : Thu, 24 Sep 2026 01:20:27 +1200
committer: David Rowley <drowley@postgresql.org>
date : Thu, 24 Sep 2026 01:20:27 +1200 heapgettup_advance_block() (or heapgettup() and heapgettup_pagemode() in
versions before v16) would incorrectly decrement rs_numblocks when
advancing to the next block. Ordinarily this was fine, but if the TID
Range scan plan was used by a scrollable cursor and the cursor was fetched
backwards enough to result in another block being required, then
rs_numblocks may have reached 0 prematurely resulting in the scan ending
prematurely.
Reported-by: Jeff Davis <pgsql@j-davis.com>
Author: Ewan Young <kdbase.hack@gmail.com>
Author: David Rowley <dgrowleyml@gmail.com>
Discussion: https://postgr.es/m/6b50233fcc0f7d6f4845f083777fa9702d9612e4.camel@j-davis.com
Backpatch-through: 14 M src/backend/access/heap/heapam.c
M src/test/regress/expected/tidrangescan.out
M src/test/regress/sql/tidrangescan.sql
doc: Fix example output for format()
commit : dede2443ceb7bbb3b7a856c250b13e86f5f606ef
author : Daniel Gustafsson <dgustafsson@postgresql.org>
date : Wed, 23 Sep 2026 14:07:25 +0200
committer: Daniel Gustafsson <dgustafsson@postgresql.org>
date : Wed, 23 Sep 2026 14:07:25 +0200 The example output for format() didn't show the actual output
generated for the example query. Backpatch to all supported
branches.
Author: shihao zhong <zhong950419@gmail.com>
Reported-by: y.saburov@gmail.com
Reviewed-by: David G. Johnston <david.g.johnston@gmail.com>
Discussion: https://postgr.es/m/178979714249.1241.1230833804069549466@wrigleys.postgresql.org
Backpatch-through: 14 M doc/src/sgml/func.sgml
Fix concurrent delete handling in MERGE ... WHEN NOT MATCHED BY SOURCE.
commit : 745152e451c10ff8635b0f0f186de98fe0528fec
author : Dean Rasheed <dean.a.rasheed@gmail.com>
date : Wed, 23 Sep 2026 09:39:07 +0100
committer: Dean Rasheed <dean.a.rasheed@gmail.com>
date : Wed, 23 Sep 2026 09:39:07 +0100 When executing a WHEN NOT MATCHED BY SOURCE action in MERGE, if the
tuple from the target table was concurrently deleted, the retry code
would incorrectly execute a WHEN NOT MATCHED BY TARGET action for the
row instead, giving incorrect results.
Fix in ExecMergeMatched() -- when handling a TM_Deleted failure
status, it should only set *matched to false (telling the caller to
execute any WHEN NOT MATCHED BY TARGET actions) if the failed action
was a WHEN MATCHED action. For a failed WHEN NOT MATCHED BY SOURCE
action, the source tuple did not exist, and the target tuple has just
been deleted, so it should do nothing.
Back-patch to v17, where support for WHEN NOT MATCHED BY SOURCE
actions was introduced.
Reported-by: Jeff Davis <pgsql@j-davis.com>
Author: Tender Wang <tndrwang@gmail.com>
Reviewed-by: Dean Rasheed <dean.a.rasheed@gmail.com>
Discussion: https://postgr.es/m/ccdab5ba02c65af195b5a6d2d744a01d9de47cd3.camel@j-davis.com
Backpatch-through: 17 M src/backend/executor/nodeModifyTable.c
M src/test/isolation/expected/merge-delete.out
M src/test/isolation/specs/merge-delete.spec
Avoid replay cleanup locks for freeze-only and VM-only records
commit : 5897bd1d8d36cb45523d0ac20da734398b7e4b16
author : Melanie Plageman <melanieplageman@gmail.com>
date : Tue, 22 Sep 2026 17:32:04 -0400
committer: Melanie Plageman <melanieplageman@gmail.com>
date : Tue, 22 Sep 2026 17:32:04 -0400 6dbb490261a combined pruning and freezing in a single WAL record but
unconditionally requested a cleanup lock during replay. Prior to version
17, freeze-only records were replayed under an ordinary exclusive lock.
1252a4ee286 subsequently folded visibility map updates into the same
records, extending the unnecessary cleanup-lock requirement to VM-only
updates starting in version 19.
Request a cleanup lock in redo only when the record actually prunes.
Freezing and visibility updates do not move or remove tuple storage, so
they need not wait for other buffer pins to be released. This avoids
unnecessary replay delays and cancellation of standby queries holding
buffer pins.
The fix is the same for all versions, though it addresses two oversights
in master/19 and one in 18/17.
Reported-by: Satyanarayana Narlapuram <satyanarlapuram@gmail.com>
Reported-by: Andrey Borodin <x4mmm@yandex-team.ru>
Reviewed-by: Andrey Borodin <x4mmm@yandex-team.ru>
Discussion: https://postgr.es/m/85b6968b-49f2-4243-86a6-9b4116e6439b@iki.fi
Discussion: https://postgr.es/m/CAHg+QDf3NcB3vOAqQ5EFcV5DYYUZ9snCSJAU-x1JDbtcjK3eBQ@mail.gmail.com
Discussion: https://postgr.es/m/F9BF304E-1298-481C-829A-B4E10AED025A@yandex-team.ru
Backpatch-through: 17 M src/backend/access/heap/pruneheap.c
Silence warnings about unused global variables
commit : 8e183d9bd49a78c0d0647230202ccc710ebcd06b
author : Peter Eisentraut <peter@eisentraut.org>
date : Tue, 22 Sep 2026 19:30:29 +0200
committer: Peter Eisentraut <peter@eisentraut.org>
date : Tue, 22 Sep 2026 19:30:29 +0200 Similar to commit "Remove unused global variables", silence
-Wunused-but-set-global warnings from clang 23 (included in -Wall).
These are variables that are used in assert-enabled builds only, so
annotate them with PG_USED_FOR_ASSERTS_ONLY. M src/backend/utils/cache/syscache.c
M src/test/modules/test_resowner/test_resowner_many.c
Fix join removal when Vars reference the removed join via its alias
commit : bd003dca09221998f801da15400e340800cbb0b6
author : Richard Guo <rguo@postgresql.org>
date : Sat, 19 Sep 2026 22:49:07 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Sat, 19 Sep 2026 22:49:07 +0900 When a join has an alias, the parser marks every Var referenced
through that alias with the join's RT index as its syntactic referent
(varnosyn), even though varno points at the underlying base relation.
Since commit 2ebf25e7d, join removal strips the removed relids from
the query tree using ChangeVarNodes() with INVALID_VAR, which asserts
that the relid doesn't appear in any field identifying a single
relation. A varnosyn that names the removed join violates that, so a
query like
SELECT j.x FROM (t1 s(x) LEFT JOIN t2 ON s.x = t2.a) j;
with t2.a unique fails an assertion. In non-assert builds, varnosyn
is silently set to INVALID_VAR.
Such syntactic references are still valid after the removal: the join
is only taken out of the jointree, and its RTE remains in the
rangetable through to the finished plan. So, when deleting a relid,
leave varnosyn unchanged, as join removal did before 2ebf25e7d.
Back-patch to v16, as with commit 2ebf25e7d.
Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAMbWs484SQSdzWTgZYKvwtuC6J4m7acw0LfBjp3aWPvav7gC_w@mail.gmail.com
Backpatch-through: 16 M src/backend/rewrite/rewriteManip.c
M src/test/regress/expected/join.out
M src/test/regress/sql/join.sql
Fix preprocessing of PHV copies pushed down into subqueries
commit : 51f9e30f38c5f649c63b8ae1bbad9159937744d6
author : Richard Guo <rguo@postgresql.org>
date : Sat, 19 Sep 2026 21:56:33 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Sat, 19 Sep 2026 21:56:33 +0900 Commit e28cf96e9 preprocessed the copies of a query level's
PlaceHolderVars that were pushed down into its subqueries in a single
walk at the start of subquery_planner. That was wrong in two ways.
First, such copies can also be inserted later, when a join alias Var
of the outer level is expanded within a LATERAL subquery or a
SubLink's subselect and the alias expression contains a PHV. Those
copies were never preprocessed, so a SubLink within them survived into
the subquery's lateral references or into a SubPlan's argument list,
tripping an assertion in identify_current_nestloop_params or failing
in cost_qual_eval.
Second, when a pushed-down copy directly contains another PHV of the
same level, the walk preprocessed the inner one first and then the
outer one, so the outer one's preprocessing ran into the SubPlans just
created in the inner one and tripped an assertion in
flatten_join_alias_vars.
To fix, preprocess the copies right after join alias expansion
instead: within preprocess_expression, just before SubLinks are turned
into SubPlans, and for LATERAL subquery RTEs, right after their
aliases are flattened. Preprocessing a copy handles everything within
it, including any further copies nested inside its SubLinks, so the
walker no longer looks inside a copy.
Back-patch to v16, as with commit e28cf96e9.
Reported-by: Fujii Masao <masao.fujii@gmail.com>
Author: Richard Guo <guofenglinux@gmail.com>
Discussion: https://postgr.es/m/CAHGQGwH-acRnAk1=Ki4EiAJEt67+Rz=_jyg7pbP_c6BK4oHZ4Q@mail.gmail.com
Backpatch-through: 16 M src/backend/optimizer/plan/planner.c
M src/backend/optimizer/plan/subselect.c
M src/backend/optimizer/util/var.c
M src/test/regress/expected/join.out
M src/test/regress/sql/join.sql
Fix PASSING of toasted text values in JSON query functions
commit : 8bccf0159409b9ce46171c5182772c0a27aa8bad
author : Michael Paquier <michael@paquier.xyz>
date : Sat, 19 Sep 2026 20:14:03 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Sat, 19 Sep 2026 20:14:03 +0900 The PASSING arguments of JSON_EXISTS(), JSON_QUERY(), JSON_VALUE() and
JSON_TABLE() are evaluated by plain expression steps and given to the
jsonpath executor as-is.
When dealing with a varlena, JsonItemFromDatum() was building a
jbvString directly the contents of a varlena. For a varatt_external
(varatt_external[oid/oid8] on HEAD), this ignored that the contents of
the varlena need to be detoasted, leading to incorrect contents
returned.
Let's detoast the value, like the jsonb and json cases. b0feda79fdf0
has fixed the same issue for subscript values in jsonb.
Note that the results of DatumGetTextPP() are allocated in the memory
context used for the evaluation of the expression, which is reset once
per row.
Oversight in 6185c9737cf4.
Author: Chaitanya Choudhary <chaitanyyachoudhary@gmail.com>
Author: Shihao Zhong <zhong950419@gmail.com>
Discussion: https://postgr.es/m/19693-2ecd2b52c838b3e5@postgresql.org
Backpatch-through: 17 M src/backend/utils/adt/jsonpath_exec.c
M src/test/regress/expected/sqljson_queryfuncs.out
M src/test/regress/sql/sqljson_queryfuncs.sql
Fix memory leak in pgoutput with streamed prepared transactions.
commit : 3d6a4a582efe5c64abf1a3669d707f3f248bd3ef
author : Masahiko Sawada <msawada@postgresql.org>
date : Fri, 18 Sep 2026 10:19:01 -0700
committer: Masahiko Sawada <msawada@postgresql.org>
date : Fri, 18 Sep 2026 10:19:01 -0700 pgoutput records the toplevel XID in the relation's streamed_txns
list, allocated in CacheMemoryContext, when it sends a relation's
schema during a streamed transaction. We clean up that list at stream
commit and stream abort, but missed stream prepare, so the XIDs stay
around until a relcache invalidation for the relation or until the
walsender exits.
Fix by cleaning up the cache after writing the stream prepare message,
marking the schema as sent as we do at stream commit. The subscriber
applies the spooled relation messages at PREPARE time and its relation
map isn't rolled back by ROLLBACK PREPARED, so we don't need to send
the schema again even if the prepared transaction eventually aborts.
Oversight in commit 63cf61cdeb7b.
Author: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com>
Discussion: https://postgr.es/m/CAJTYsWWt5aaKHCnV=jNOwABzspd-WbnhWLndDqtESDQRQ-BkKQ@mail.gmail.com
Backpatch-through: 15 M src/backend/replication/pgoutput/pgoutput.c
Use default limitOption in subquery generated for MIN/MAX index scan.
commit : 70d0ac3c91f16ea29722dc6e5cee1d5a9f88533a
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Fri, 18 Sep 2026 11:02:10 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Fri, 18 Sep 2026 11:02:10 -0400 We unintentionally copied the original query's limitOption field while
building the modified subquery. If it was LIMIT_OPTION_WITH_TIES,
that behavior would propagate through to execution, potentially
causing a "more than one row returned by a subquery used as an
expression" error. Fix by resetting to the default LIMIT_OPTION_COUNT.
This bug dates back to the introduction of WITH TIES.
Bug: #19694
Reported-by: Chaitanya Choudhary <chaitanyyachoudhary@gmail.com>
Author: Chaitanya Choudhary <chaitanyyachoudhary@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/19694-4e61bd0475398eef@postgresql.org
Backpatch-through: 14 M src/backend/optimizer/plan/planagg.c
Fix pruning of DEFAULT partition in RANGE partitioned tables
commit : d21d4f1ca0c44ecf9f75c0519e1c034d0fb3b630
author : David Rowley <drowley@postgresql.org>
date : Fri, 18 Sep 2026 17:15:52 +1200
committer: David Rowley <drowley@postgresql.org>
date : Fri, 18 Sep 2026 17:15:52 +1200 Some code added in 489247b0e tried to prune the DEFAULT partition when
the next partition had a MINVALUE clause and likewise when the final
partition to scan had a MAXVALUE clause for the final partition key in
the pruning step. This code was incorrect as it could prune the default
partition incorrectly in cases such as:
p: PARTITION BY RANGE (a, b)
p1: FOR VALUES FROM (13, 0) TO (19, MAXVALUE)
pd: DEFAULT
SELECT * FROM t WHERE a = 32 AND b >= -7;
Here the pruning step for a = 32 and b >= -7 would see that only the
default partition needs to be scan, but it would then see that the
partition prior to the default had a MAXVALUE bound then prune away the
default thinking that it needn't be scanned. This could result in
incorrect results.
Fix this by moving the code that looks for the MAXVALUE bound into the
code handling BTEqualStrategyNumber so that when we're pruning with a
prefix of the partition keys, we check if the bound for the offset we've
calculated lands on a partition where the next partition key is bounded
with MAXVALUE. If so we don't include the default partition.
This leaves only the case of the first partition key. We handle that by
modifying the existing code that was checking the last key covered by
the given values.
Author: Ewan Young <kdbase.hack@gmail.com>
Reviewed-by: Tender Wang <tndrwang@gmail.com>
Reviewed-by: David Rowley <dgrowleyml@gmail.com>
Discussion: https://postgr.es/m/CAON2xHO=sqdqp=z8zWkybnWp0AuvefnAi2ez2vOYWrhXB6hHWQ@mail.gmail.com
Backpatch-through: 14 M src/backend/partitioning/partprune.c
M src/test/regress/expected/partition_prune.out
M src/test/regress/sql/partition_prune.sql
Add CHECK_FOR_INTERRUPTS when building pruning steps
commit : d35a4c4de64fa3a3fb047b6565fc68c1b0582caf
author : David Rowley <drowley@postgresql.org>
date : Fri, 18 Sep 2026 15:51:17 +1200
committer: David Rowley <drowley@postgresql.org>
date : Fri, 18 Sep 2026 15:51:17 +1200 Building the partition pruning steps for excessively complex queries
can take some time. Add CHECK_FOR_INTERRUPTS() to
get_steps_using_prefix_recurse() to allow more timely processing of
interruptions.
Bug: #19692
Reported-by: Qifan Liu <imchifan@163.com>
Discussion: https://postgr.es/m/19692-dacfc57cda5035fc@postgresql.org
Backpatch-through: 14 M src/backend/partitioning/partprune.c
Tolerate partial pgstats entries in pgstat_gc_entry_refs()
commit : 6e0011596c92d290dffe566fa851235df6b18d5b
author : Michael Paquier <michael@paquier.xyz>
date : Thu, 17 Sep 2026 19:25:27 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Thu, 17 Sep 2026 19:25:27 +0900 pgstat_get_entry_ref_cached() inserts a local entry_ref with
shmem-related fields set to NULL, expecting pgstat_get_entry_ref() (its
sole caller) to fill them up before returning. If an ERROR happens
while pgstat_get_entry_ref() runs, it could be possible to finish with a
local pgstats entry partially filled.
This could lead to a crash of pgstat_gc_entry_refs(), which tolerates a
NULL shared_stats in an assertion but unconditionally dereferenced its
"dropped" and "generation" fields.
This extends 4069df21beb8, being a cheap insurance against NULL pointer
dereference, if some code paths of pgstat_get_entry_ref() are not able
to perform any cleanup actions (for example after a dsm_create()
throwing an ERROR).
Reviewed-by: Grigorev Jurij <ju.grigorev@ftdata.ru>
Discussion: https://postgr.es/m/aqtiKTvl519bu8-V@paquier.xyz
Backpatch-through: 15 M src/backend/utils/activity/pgstat_shmem.c
Fix assertion after aborting internal subtransaction at transaction end
commit : 84287ab890ffed4cfe334019c2cb6abdaaf7c97f
author : Fujii Masao <fujii@postgresql.org>
date : Thu, 17 Sep 2026 14:25:43 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Thu, 17 Sep 2026 14:25:43 +0900 Previously, aborting an internal subtransaction during COMMIT or
PREPARE TRANSACTION could cause the following assertion failure.
This could happen, for example, when a deferred constraint trigger fired
at COMMIT and its PL/pgSQL exception block caught an error raised
while executing the trigger function.
TRAP: failed Assert("s->blockState == TBLOCK_SUBINPROGRESS || s->blockState
== TBLOCK_INPROGRESS || s->blockState == TBLOCK_IMPLICIT_INPROGRESS ||
s->blockState == TBLOCK_PARALLEL_INPROGRESS || s->blockState ==
TBLOCK_STARTED"), File: "xact.c", Line: 4851, PID: 73455
An internal subtransaction should be able to be aborted while the parent
transaction is in the COMMIT or PREPARE TRANSACTION phase. However,
RollbackAndReleaseCurrentSubTransaction()'s assertion check previously
did not allow TBLOCK_END and TBLOCK_PREPARE as parent transaction
states, causing the assertion failure.
This commit fixes the assertion check by allowing those two parent
transaction states.
Backpatch to all supported versions.
Reported-by: Fabrízio de Royes Mello <fabrizio@planetscale.com>
Author: Patrick Reynolds <piki@planetscale.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Fabrízio de Royes Mello <fabrizio@planetscale.com>
Discussion: https://postgr.es/m/CABo-N97AeMbWuYTWg-3%3D2DkTR3EkvS%2BFt%3DyEaWB181STsR1mBg%40mail.gmail.com
Backpatch-through: 14 M src/backend/access/transam/xact.c
M src/test/regress/expected/triggers.out
M src/test/regress/sql/triggers.sql
Use the join collation when unique-ifying a semijoin's RHS
commit : a10d14afca3601f519e8ba219627c8c0e9bf72c7
author : Alexander Korotkov <akorotkov@postgresql.org>
date : Wed, 16 Sep 2026 12:17:53 +0300
committer: Alexander Korotkov <akorotkov@postgresql.org>
date : Wed, 16 Sep 2026 12:17:53 +0300 A semijoin whose RHS is unique-ified groups the RHS on the expressions in
SpecialJoinInfo.semi_rhs_exprs. Those were recorded with whatever collation
the RHS expression itself exposes, which need not be the collation the join
compares with. Neither SortGroupClause nor the pathkey machinery carries a
collation of its own, so both Unique-over-Sort and HashAggregate then grouped
by the wrong equality: values the join considers equal survived, and the
following inner join emitted the outer row once per survivor.
With a non-deterministic collation on one side, "SELECT count(*) FROM t WHERE
c IN (SELECT c0 FROM t2)" therefore counted more rows than the same predicate
reports for the rows of t.
Label each RHS expression with the operator's input collation, the same
treatment process_equivalence() gives to equivalence class members. Every
consumer of semi_rhs_exprs reads the collation off the expression, so this
fixes the sort-based and hash-based paths together; in the branches where
create_unique_path() also passes these expressions to
relation_has_unique_index_for(), it likewise stops a unique index built with a
different collation from being taken as proof that unique-ification can be
skipped. The same goes for a subquery's DISTINCT computed under a different
collation, since translate_sub_tlist() punts on the relabeled expressions.
Reported-by: Suyang Zhong <syzhong16@gmail.com>
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Reviewed-by: Tender Wang <tndrwang@gmail.com>
Reviewed-by: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com>
Discussion: https://postgr.es/m/19633-647cd4c73a84b085%40postgresql.org
Backpatch-through: 14 M src/backend/optimizer/plan/initsplan.c
M src/test/regress/expected/collate.icu.utf8.out
M src/test/regress/sql/collate.icu.utf8.sql
Fix missing SIREAD lock on the row found by ON CONFLICT.
commit : 5b4ebfbfaec322dd7b2d2ee25075ae9efe9bee0a
author : Dean Rasheed <dean.a.rasheed@gmail.com>
date : Tue, 15 Sep 2026 11:47:08 +0100
committer: Dean Rasheed <dean.a.rasheed@gmail.com>
date : Tue, 15 Sep 2026 11:47:08 +0100 INSERT ... ON CONFLICT decides what to do based on the conflicting row
found by the arbiter index probe, but SSI never saw that read: the
probe runs with a dirty snapshot, which predicate locking ignores, and
the later fetch of the row uses SnapshotAny. When the statement then
writes nothing, as with DO NOTHING, DO UPDATE with a WHERE clause
rejecting the row, or DO SELECT, nothing records the read at all. A
concurrent writer of that row went unnoticed and write skew could
commit at SERIALIZABLE, even though the same schedule with a plain
SELECT of the row fails with a serialization error.
To fix, read the conflicting tuple again with the query snapshot,
right where the probe finds it. The table AM takes the SIREAD lock
and checks for a concurrent writer of the tuple as part of that read,
both under the buffer lock, so a writer either sees the lock or is
seen. A predicate lock by itself acquired separately after the probe
could not offer that: a writer passing its conflict check in between
would be missed. Doing this in the probe covers every conflict
action, including rows that the WHERE clause of DO UPDATE or DO SELECT
then rejects.
The DO NOTHING and DO UPDATE cases have been broken since ON CONFLICT
was added in 9.5; DO SELECT is new in v19. Backpatch to all supported
branches.
Author: Zsolt Parragi <zsolt.parragi@percona.com>
Author: Andrey Borodin <x4mmm@yandex-team.ru>
Reported-by: Andrey Borodin <x4mmm@yandex-team.ru>
Reported-by: Zsolt Parragi <zsolt.parragi@percona.com>
Discussion: https://postgr.es/m/787936C5-4155-4CF9-939D-39DC0EC1C892@yandex-team.ru
Discussion: https://postgr.es/m/CAN4CZFM1GkHJkpMeo4G5rxtacVsfeKCJYiik9E9AKX1E9VYQ1w@mail.gmail.com
Backpatch-through: 14 M src/backend/executor/execIndexing.c
A src/test/isolation/expected/insert-conflict-serializable.out
M src/test/isolation/isolation_schedule
A src/test/isolation/specs/insert-conflict-serializable.spec
M src/test/modules/injection_points/Makefile
A src/test/modules/injection_points/expected/on_conflict_probe_window.out
M src/test/modules/injection_points/meson.build
A src/test/modules/injection_points/specs/on_conflict_probe_window.spec
Fix const-folding of JSON constructors inside a simple CASE
commit : 23088673d311ae274069f7147cf280ee2540aeea
author : Richard Guo <rguo@postgresql.org>
date : Mon, 14 Sep 2026 17:07:17 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Mon, 14 Sep 2026 17:07:17 +0900 A JSON constructor with a RETURNING clause uses a CaseTestExpr as the
placeholder for its result in the coercion expression. When such a
constructor appears in a WHEN clause of a simple CASE whose test
expression is a constant, eval_const_expressions substituted that
constant for the placeholder, so the coercion produced the CASE's test
value instead of the constructor's result. For instance,
CASE 'x' WHEN JSON_OBJECT('a': 'b' RETURNING text) THEN 1 ELSE 0 END
evaluated to 1.
To fix, keep case_val out of scope while simplifying the coercion, as
is already done for the elemexpr of an ArrayCoerceExpr.
Back-patch to v16, where the SQL/JSON constructor functions were
introduced.
Author: Richard Guo <guofenglinux@gmail.com>
Discussion: https://postgr.es/m/CAMbWs48A=VCFbteTkuCoknO1_0-Cu0aMBT0M07dm7vj1QyixDg@mail.gmail.com
Backpatch-through: 16 M src/backend/optimizer/util/clauses.c
M src/include/nodes/primnodes.h
M src/test/regress/expected/sqljson.out
M src/test/regress/sql/sqljson.sql
Fix stale copies of PHVs in subqueries
commit : ccb7d9dba1c3705c544ddee129f2dec677920a03
author : Richard Guo <rguo@postgresql.org>
date : Mon, 14 Sep 2026 12:29:44 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Mon, 14 Sep 2026 12:29:44 +0900 When a subquery references an output of another subquery that gets
pulled up, and that output must be wrapped in a PlaceHolderVar because
of an intermediate outer join, the PHV expression is pushed down into
the subquery. That copy is not preprocessed along with the outer
query's expressions, so the two copies can diverge. This used to be
harmless, but since commit 2ebf25e7d join removal edits the whole
query tree, walking into subqueries, and can trip an assert in
ChangeVarNodes if it removes a rel whose Var survives only in such a
copy.
To fix, preprocess these copies at their owning query level, early in
subquery_planner, before anything can consume them (in particular
before SubLinks are turned into SubPlans). This covers copies pushed
into both LATERAL subquery RTEs and SubLink subselects, and handles
nested copies innermost-first. extract_lateral_references no longer
preprocesses the copies it pulls out.
Correspondingly, the subquery's own processing must leave the contents
of an upper-level PHV alone, since the owning level has already
preprocessed them: eval_const_expressions returns such a PHV
unchanged, and flatten_join_alias_vars no longer recurses into it.
Back-patch to v16, as with commit 2ebf25e7d.
Reported-by: Tender Wang <tndrwang@gmail.com>
Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAHewXN=kWGAXV537mKtSyBYobGdHhYJVDJJMXXZEmmPWE_zaPw@mail.gmail.com
Backpatch-through: 16 M src/backend/optimizer/plan/initsplan.c
M src/backend/optimizer/plan/planner.c
M src/backend/optimizer/plan/subselect.c
M src/backend/optimizer/util/clauses.c
M src/backend/optimizer/util/var.c
M src/test/regress/expected/join.out
M src/test/regress/sql/join.sql
M src/tools/pgindent/typedefs.list
doc: Fix output column name for pg_stat_get_backend_subxact()
commit : 25d6716cef088b4a2330000117355fea7f4d8242
author : Michael Paquier <michael@paquier.xyz>
date : Sat, 12 Sep 2026 09:04:10 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Sat, 12 Sep 2026 09:04:10 +0900 pg_stat_get_backend_subxact()'s second attribute was written as
"subxact_overflow" in the docs, but its name is "subxact_overflowed".
Note that the attribute name is still wrong in the TupleDesc generated
in the function; pg_proc agrees with "overflowed".
Author: Shihao Zhong <zhong950419@gmail.com>
Reviewed-by: Jim Jones <jim.jones@uni-muenster.de>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/CAGRkXqTBZ+zbVuDC8xGEB6Btj61hsui5H5nGqzFBDyOXc=4bjQ@mail.gmail.com
Backpatch-through: 16 M doc/src/sgml/monitoring.sgml
Fix option argument lookup in in-tree getopt_long().
commit : a11bce64a3be38f726cdca682f1e5b723cfd34d1
author : Nathan Bossart <nathan@postgresql.org>
date : Fri, 11 Sep 2026 15:18:40 -0500
committer: Nathan Bossart <nathan@postgresql.org>
date : Fri, 11 Sep 2026 15:18:40 -0500 The in-tree getopt_long() moves each non-option to the end of argv,
which might put it right where an option's argument lookup expects
to find the argument. For example, "vacuumdb postgres --jobs"
takes "postgres" as the number of jobs instead of complaining that
--jobs is missing its argument. To fix, stop the argument lookups
at the start of the moved non-options, which we already track to
know when to stop scanning.
Oversight in commit 411b720343.
Author: Sehrope Sarkuni <sehrope@jackdb.com>
Reviewed-by: solai v <solai.cdac@gmail.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17 M src/bin/scripts/t/100_vacuumdb.pl
M src/port/getopt_long.c
pg_ctl: Silence warnings about unused global variables
commit : 3317dc47241dca90466650d36a0cad40c97bb0f7
author : Peter Eisentraut <peter@eisentraut.org>
date : Fri, 11 Sep 2026 19:12:43 +0200
committer: Peter Eisentraut <peter@eisentraut.org>
date : Fri, 11 Sep 2026 19:12:43 +0200 Several global variables are only used in Windows build. This causes
-Wunused-but-set-global warnings that the new clang 23 enables via
-Wall.
This commit marks these with an unused attribute to silence the
warnings.
(In the master branch, this is addressed differently, but for
backpatching, this just silences the warnings without any behavior
change.)
Reviewed-by: Andreas Karlsson <andreas@proxel.se>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://www.postgresql.org/message-id/flat/eb013f9d-2247-444e-8815-9d17b4ce78e7%40eisentraut.org M src/bin/pg_ctl/pg_ctl.c
Remove unused global variables
commit : be1f3b20cbce8e0f60a32054f54cd2498ee21fcf
author : Peter Eisentraut <peter@eisentraut.org>
date : Fri, 11 Sep 2026 17:43:11 +0200
committer: Peter Eisentraut <peter@eisentraut.org>
date : Fri, 11 Sep 2026 17:43:11 +0200 The new clang 23 has a new warning about set-but-unused
static (internal-linkage) global variables: -Wunused-but-set-global,
which is activated in PostgreSQL builds via -Wall. This triggers a
few warnings in PostgreSQL code. This commit removes several such
variables that were either never used or whose last use was removed
some time ago.
For pq_init_crypto_lib, we apply the same #ifdef HAVE_CRYPTO_LOCK that
the other uses of the variable already have, so that the variable is
either fully used or fully nonexistent, depending on the
configuration, not half-way.
Reviewed-by: Andreas Karlsson <andreas@proxel.se>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://www.postgresql.org/message-id/flat/eb013f9d-2247-444e-8815-9d17b4ce78e7%40eisentraut.org M src/backend/access/transam/xlog.c
M src/backend/postmaster/postmaster.c
M src/backend/utils/adt/jsonpath_scan.l
M src/bin/pg_dump/pg_dump_sort.c
M src/interfaces/libpq/fe-secure-openssl.c
M src/test/modules/test_resowner/test_resowner_many.c
Fix concurrency issues with DROP TABLESPACE
commit : 960b6b53241e41ec4c0e960b23c718c8aaba076b
author : Andrew Dunstan <andrew@dunslane.net>
date : Wed, 9 Sep 2026 10:50:09 -0400
committer: Andrew Dunstan <andrew@dunslane.net>
date : Wed, 9 Sep 2026 10:50:09 -0400 DROP TABLESPACE checked pg_shdepend for dependent objects without
first locking the tablespace. A concurrent command that recorded a
shared dependency on the tablespace right after that check could still
commit, leaving an object whose pg_shdepend entry (or, for a relation,
pg_class.reltablespace) pointed to a tablespace that no longer existed.
Close the race by having DropTableSpace() take an AccessExclusiveLock
on the tablespace before calling checkSharedDependencies(). That
conflicts with the AccessShareLock shdepLockAndCheckObject() takes when
recording a new dependency, so the loser of the race blocks and
rechecks once the winner commits.
That AccessExclusiveLock creates a new deadlock: ALTER TABLESPACE
RENAME/SET and the internal ACL/owner updates in DROP OWNED and
REASSIGN OWNED touched the catalog tuple without locking the
tablespace, risking a lock-order cycle with DROP. Fix by taking an
AccessShareLock first in all four paths, rechecking pg_shdepend after
any wait in the DROP OWNED and REASSIGN OWNED cases. GRANT, REVOKE,
and ALTER TABLESPACE ... OWNER TO already lock the object.
Add isolation tests covering both orderings of the original race
(dependency-first and drop-first) and all four previously-unlocked
update paths.
Author: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Reviewed-by: Andrew Dunstan <andrew@dunslane.net>
Discussion: https://postgr.es/m/CAJTYsWXjAQFnGKzXsht3XK8KHhyhontwFJw4JAHsUSfs_ptR4g@mail.gmail.com
Backpatch-through: 14 M src/backend/catalog/pg_shdepend.c
M src/backend/commands/tablespace.c
A src/test/isolation/expected/tablespace-dependency-locking.out
M src/test/isolation/isolation_schedule
A src/test/isolation/specs/tablespace-dependency-locking.spec
Revert "Mark modified the FSM buffer as dirty during recovery"
commit : bee33974ee726785a674fafd1c58f8d0cd9c6667
author : Alexander Korotkov <akorotkov@postgresql.org>
date : Tue, 8 Sep 2026 13:30:55 +0300
committer: Alexander Korotkov <akorotkov@postgresql.org>
date : Tue, 8 Sep 2026 13:30:55 +0300 This reverts commit c06d1a4ba6b26eef27b04074683cccade6c277ee.
The commit assumed that if the FSM code tolerates torn pages, everything else
does so. Readers that do not tolerate that include RelationCopyStorage(),
used by ALTER TABLE ... SET TABLESPACE for every fork, the read stream in
RelationCopyStorageUsingBuffer() used by CREATE DATABASE ... STRATEGY =
wal_log, and, most awkwardly, the checksum verification in base backups and
pg_checksums.
Reported-by: Noah Misch <noah@leadboat.com>
Discussion: https://postgr.es/m/20260901211837.f6.noahmisch%40microsoft.com
Backpatch-through: 14
Reviewed-by: Andrey Borodin <x4mmm@yandex-team.ru> M src/backend/storage/freespace/freespace.c
doc: Remove unnecessary CVE items from release notes
commit : 253662496c5f1c1fd0a24c104712900a9594fd2b
author : Michael Paquier <michael@paquier.xyz>
date : Tue, 8 Sep 2026 17:27:12 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Tue, 8 Sep 2026 17:27:12 +0900 The following CVEs were listed in the release notes of some stable
branches, but they should not:
- CVE-2026-16238, not on v17 and older
- CVE-2026-14676, not on v17 and older
- CVE-2026-14681, not on v16 and older
- CVE-2026-14672, not on v15 and older
Author: Yogesh Sharma <yogesh.sharma@catprosystems.com>
Discussion: https://postgr.es/m/340842e1-31ce-4551-9d79-71de490f67e9@CatProSystems.com
Backpatch-through: 14-17 M doc/src/sgml/release-17.sgml
Stabilize 026_overwrite_contrecord test
commit : 7e321bb93e2a25d09089bd293f5a45637aea5228
author : Fujii Masao <fujii@postgresql.org>
date : Tue, 8 Sep 2026 16:48:29 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Tue, 8 Sep 2026 16:48:29 +0900 On slow machines, this test can take long enough to generate WAL that a
time-based checkpoint occurs before the primary is stopped. If the
checkpoint record is written to the tail WAL segment that the test later
removes, a standby initialized from the resulting backup tries to read
the missing checkpoint record and fails with a PANIC during startup.
This caused the test to fail on buildfarm member skink.
Fix this by setting checkpoint_timeout high enough to prevent unrelated
checkpoints during the test. This follows the approach used by other
recovery tests, such as 043_no_contrecord_switch.pl, that depend on a
specific WAL layout.
Backpatch to all supported versions.
Reported-by: Alexander Lakhin <exclusion@gmail.com>
Author: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/CAHGQGwFajfjKhvNSTkTcE-wFn30p_A0_i1cvk-Q=FJhDr_5cXA@mail.gmail.com
Discussion: https://postgr.es/m/9ffdb19a-7a89-424e-925a-dd981c37f0ba@gmail.com
Backpatch-through: 14 M src/test/recovery/t/026_overwrite_contrecord.pl
Fix nestloop parameter handling for PlaceHolderVars in child joins
commit : f2e9e3aa1237611bc18c2b0742f019eea59c7364
author : Richard Guo <rguo@postgresql.org>
date : Tue, 8 Sep 2026 10:09:08 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Tue, 8 Sep 2026 10:09:08 +0900 When creating a nestloop plan for a partitionwise child join, the
outer rel's relids are child relids, but PlaceHolderInfo.ph_eval_at is
always expressed in terms of the topmost parent rels. As a result,
replace_nestloop_params() and identify_current_nestloop_params()
failed to recognize that a PlaceHolderVar evaluated at the outer child
rel can be supplied as a nestloop param. Instead, the Vars within the
PHV's expression were replaced with params, but the outer child rel
emits only the PHV, not those bare Vars, leading to "variable not
found in subplan target list" errors from setrefs.c.
To fix, also include the outer rel's top parent relids in the relid
set used for these checks, so that ph_eval_at comparisons are done in
terms of parent rels while Var checks continue to work in terms of
child rels.
On v18 and later, the required-outer set passed to
identify_current_nestloop_params() has the same problem: it is in
terms of child rels once a parameterized child join path has been
reparameterized by an upper child join. With the above fix in place,
a PlaceHolderVar that depends on both the outer rel and the parameter
source becomes a single NestLoopParam, and that param was never
claimed by any nestloop node, leading to "failed to assign all
NestLoopParams to plan nodes" errors. To fix, also include the top
parents of any child rels in that set. Older branches lack this code
path, so they receive only the first change.
Back-patch to all supported branches.
Bug: #19653
Reported-by: Annie <10215501441@stu.ecnu.edu.cn>
Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/19653-9352cc6ba17b662f@postgresql.org
Backpatch-through: 14 M src/backend/optimizer/plan/createplan.c
M src/test/regress/expected/partition_join.out
M src/test/regress/sql/partition_join.sql
Fix incorrect error message in xlogreader.c
commit : 018bfcfd9fa4e520970ba3bda370f78bb473c365
author : Michael Paquier <michael@paquier.xyz>
date : Sat, 5 Sep 2026 09:12:51 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Sat, 5 Sep 2026 09:12:51 +0900 When neither BKPIMAGE_HAS_HOLE nor BKPIMAGE_COMPRESSED is set,
DecodeXLogRecord() checks that bimg_len needs to be equal to BLCKSZ.
However, the associated error message reported the data length
associated to a block (DecodedBkpBlock.data_len), and not the length of
the block (DecodedBkpBlock.bimg_len).
Oversight in 57aa5b2bb11a, probably due to some copy-paste from the
surroundings.
Author: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Discussion: https://postgr.es/m/CAJTYsWVz9ymE7aMt2ZiQvs1bxNvhjfTf+XaJEsdFw7MT5dvtYQ@mail.gmail.com
Backpatch-through: 14 M src/backend/access/transam/xlogreader.c
ssl: Fix expected output for OpenSSL 4
commit : 612dd54254391c59d1d7db8071bf8570143d8fe4
author : Daniel Gustafsson <dgustafsson@postgresql.org>
date : Fri, 4 Sep 2026 13:42:42 +0200
committer: Daniel Gustafsson <dgustafsson@postgresql.org>
date : Fri, 4 Sep 2026 13:42:42 +0200 When adding support for OpenSSL 4, one occurrence of OpenSSL error
message matching was missed in the backpatches to PostgreSQL 17 and
16.
Reported-by: Christoph Berg <cb@df7cb.de>
Discussion: https://postgr.es/m/apky-gqiLVtFkR-w@msg.df7cb.de
Backpatch-through: 16,17 M src/test/ssl/t/001_ssltests.pl
Fix lack of message pluralization
commit : ec3f6a6a7dd82a8ce455a0710ef75172f9f318d1
author : Peter Eisentraut <peter@eisentraut.org>
date : Thu, 3 Sep 2026 11:49:09 +0200
committer: Peter Eisentraut <peter@eisentraut.org>
date : Thu, 3 Sep 2026 11:49:09 +0200 Fixups for commits b99b74144f9, 79b101486c1, bf7d19be9b1. M src/backend/commands/trigger.c
M src/backend/replication/logical/worker.c
M src/backend/utils/time/snapmgr.c
Avoid backend hang during temp table cleanup in deferrable transactions
commit : 4639b6cfe3f310b71e1e227dd2a915b053992c9b
author : Fujii Masao <fujii@postgresql.org>
date : Thu, 3 Sep 2026 12:14:44 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Thu, 3 Sep 2026 12:14:44 +0900 Previously, a session that had created temporary objects could hang during
exit if its default transaction mode was SERIALIZABLE READ ONLY DEFERRABLE
and a concurrent serializable transaction was prepared. During backend
exit, temporary-relation cleanup pushed a regular transaction snapshot,
which honored the session's default settings and could wait for a safe
serializable snapshot. So, if the conflicting transaction was prepared,
this wait could last indefinitely.
The wait occurred while the backend was already exiting and interrupts
were held off, so even pg_terminate_backend() could not cancel it. The
backend therefore remained until the prepared transaction was resolved.
Temporary-relation cleanup only needs an active MVCC snapshot for fetching
TOAST data from catalog tuples while dropping temporary objects. It does
not need a transaction snapshot affected by the user's default isolation
settings. So, use a catalog snapshot instead, which provides the needed
protection without entering the deferrable safe-snapshot wait.
Backpatch to all supported versions.
Bug: #19441
Reported-by: Alexander Lakhin <exclusion@gmail.com>
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Reviewed-by: Andrey Borodin <x4mmm@yandex-team.ru>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/19441-ec29f3b1363b4a68@postgresql.org
Backpatch-through: 14 M src/backend/catalog/namespace.c
M src/test/modules/test_misc/meson.build
A src/test/modules/test_misc/t/015_temp_schema_exit_deferrable.pl
Stabilize recovery conflict stats checks in 031_recovery_conflict.pl
commit : a830fa886cee8b5c366787c50aadb1d5f17b2348
author : Fujii Masao <fujii@postgresql.org>
date : Thu, 3 Sep 2026 12:09:49 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Thu, 3 Sep 2026 12:09:49 +0900 The buildfarm member akepa reported a failure in the
031_recovery_conflict.pl test.
The test checked pg_stat_database_conflicts immediately after detecting
a recovery conflict in the standby log. However, the conflict counter is
flushed by the canceled backend during backend exit, so WAL replay
completion and the log message did not guarantee that the updated
statistics are visible yet. So, previously, the test could see a conflict
counter of 0 even though the conflict had already occurred, triggering
the test failure.
Fix this by polling for the expected conflict counter instead of reading
it only once, handling the asynchronous pgstats update.
Per buildfarm member akepa.
Backpatch to v17, where this test is enabled and has the same race.
Author: Fujii Masao <masao.fujii@gmail.com>
Reviewed-by: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Reviewed-by: Nazir Bilal Yavuz <byavuz81@gmail.com>
Discussion: https://postgr.es/m/CAHGQGwHmiLNRfvJDAR=PmxQgf7DbzPSO1M-1RoqO8oy=t2G5KA@mail.gmail.com
Backpatch-through: 17 M src/test/recovery/t/031_recovery_conflict.pl
Stabilize xid_wraparound/t/002_limits.pl test.
commit : 61636c17b391bb5b93dbeeed24553198187752fc
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Wed, 2 Sep 2026 10:53:35 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Wed, 2 Sep 2026 10:53:35 -0400 Not many buildfarm members run this test, since it's gated behind
PG_TEST_EXTRA=xid_wraparound, but of those that do, the slower ones
not infrequently fail. The reason seems to be that the test expects
an INSERT command to either succeed or fail, but there's a window
where it can succeed while issuing a warning about impending
wraparound. poll_query_until treats nonempty stderr as a failure,
so it loops an extra time until the INSERT succeeds with no warning.
There seems no reason to treat this behavior as wrong, so adjust
the test to accept it.
Reported-by: Alexander Lakhin <exclusion@gmail.com>
Diagnosed-by: Alexander Lakhin <exclusion@gmail.com>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/7a68bee2-91bd-481d-be44-160e6beebc83@gmail.com
Backpatch-through: 17 M src/test/modules/xid_wraparound/t/002_limits.pl
Fix checkpointer restartpoint assertion failure
commit : 5d13f7530545ecd3ac011b9ed1b4aac669e37b19
author : Fujii Masao <fujii@postgresql.org>
date : Wed, 2 Sep 2026 14:33:25 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Wed, 2 Sep 2026 14:33:25 +0900 When recovery starts from a backup without a signal file, pg_subtrans
is not started at the beginning of recovery and remains unstarted
throughout recovery. However, previously, a restartpoint run by
the checkpointer during recovery nevertheless tried to truncate
pg_subtrans, triggering the assertion failure:
TRAP: failed Assert("TransactionIdIsValid(initial)")
This commit fixes this by tracking whether pg_subtrans has been
started during recovery, and have the checkpointer check this flag
before truncating pg_subtrans at restartpoints.
Backpatch to all supported versions.
Reported-by: Imran Zaheer <imran.zhir@gmail.com>
Author: Imran Zaheer <imran.zhir@gmail.com>
Author: Fujii Masao <masao.fujii@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/CA+UBfamzfEReT0VOGRUW_=_AecCYQ-CNKLR_T4Q+YEmJAH3fdg@mail.gmail.com
Backpatch-through: 14 M src/backend/access/transam/xlog.c
M src/backend/access/transam/xlogrecovery.c
M src/include/access/xlogrecovery.h
Remove quals made redundant by reducing outer joins to antijoins
commit : 197a41aa6ee9f9e1467eaee43c04cf52a8367067
author : Richard Guo <rguo@postgresql.org>
date : Wed, 2 Sep 2026 13:39:29 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Wed, 2 Sep 2026 13:39:29 +0900 When reduce_outer_joins reduces an outer join to an antijoin, any IS
NULL qual on a Var from the antijoin's nullable side is necessarily
true. Previously, such quals were discarded later in
distribute_qual_to_rels, mainly to avoid bogus selectivity estimates.
But that discard was incomplete: the qual remained in the jointree,
while its Vars were not counted in attr_needed. Since commit
2ebf25e7d, join removal edits the jointree and expects it to contain
no other references to a removed rel, so it could remove a rel that
such a discarded qual still references, and then trip an assertion on
the qual's stale Var.
To fix, move this processing to an earlier phase: such quals are now
removed from the jointree by reduce_outer_joins itself. This way
later phases see a consistent query tree, and
check_redundant_nullability_qual is no longer needed, so remove it.
Back-patch to v16, as with commit 2ebf25e7d.
Reported-by: Tender Wang <tndrwang@gmail.com>
Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAHewXNk8b0TsSy4dL=CO7FXL2W3WBm0BcdP-zwNJePa-Qj4HzA@mail.gmail.com
Backpatch-through: 16 M src/backend/optimizer/plan/initsplan.c
M src/backend/optimizer/prep/prepjointree.c
M src/backend/optimizer/util/clauses.c
M src/test/regress/expected/join.out
M src/test/regress/sql/join.sql
Speed up pg_waldump TAP test
commit : 680f707fb2e4e3a954137e027e89afbf2d6f7944
author : Michael Paquier <michael@paquier.xyz>
date : Wed, 2 Sep 2026 09:19:37 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Wed, 2 Sep 2026 09:19:37 +0900 Several invocations decoded and printed the full WAL range even though
the test only needs to confirm that a command form works or that
decoding reaches a specific error. Limit those checks to one record,
and start the fall-off-the-end checks near the end of the generated WAL.
This change reduces the IPC overhead overall, particularly on Windows,
without reducing coverage.
I am usually hesitant to backpatch such changes as this is only an
improvement, but the gains are too good in terms of IO and runtime, for
both the CI and the buildfarm. Based on the numbers provided, the CI
takes 30% less time to run the test with this change on Windows (worst
case shown on the lists). These tests have been introduced in
96063e28366b.
Author: Sehrope Sarkuni <sehrope@jackdb.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Reviewed-by: Nazir Bilal Yavuz <byavuz81@gmail.com>
Discussion: https://postgr.es/m/CAH7T-araSEdsNpxiKaMOW8kr_ZHhekCLHx5yzm2ksq4FdgULbA@mail.gmail.com
Backpatch-through: 17 M src/bin/pg_waldump/t/001_basic.pl
doc: clarify aliasing of VALUES in FROM clauses
commit : 45dc7bd2492a05a737f75dae4f5667142a5b562a
author : Fujii Masao <fujii@postgresql.org>
date : Tue, 1 Sep 2026 22:46:10 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Tue, 1 Sep 2026 22:46:10 +0900 The VALUES reference page said that an AS clause was required when
VALUES is used in a FROM clause. This was imprecise because AS is
optional when specifying an alias.
In v14 and v15, a table alias is still required in this case, so state
that explicitly instead. In v16 and later, commit bcedd8f5fce made table
aliases for subqueries in FROM clauses optional, so state that the
table alias is optional there. In all branches, continue to recommend
explicit column aliases as good practice.
Backpatch to all supported versions.
Author: Ian Barwick <barwick@gmail.com>
Reviewed-by: Laurenz Albe <laurenz.albe@cybertec.at>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/CAB8KJ=jrvZdNLLhYSiCgbjLTz2LKEjYw+-HVQkaF+kgk61KtHA@mail.gmail.com
Backpatch-through: 14 M doc/src/sgml/ref/values.sgml
Handle PG_INT32_MIN negation overflow in right()
commit : 3103bdafde6b0dfa8edbcefb48ac51097c6a9c1e
author : Daniel Gustafsson <dgustafsson@postgresql.org>
date : Tue, 1 Sep 2026 10:13:30 +0200
committer: Daniel Gustafsson <dgustafsson@postgresql.org>
date : Tue, 1 Sep 2026 10:13:30 +0200 A negative n means "return all but the first |n| characters", so
text_right() negates n before clipping. Negating PG_INT32_MIN
overflows; with -fwrapv the result is PG_INT32_MIN again, still
negative, and pg_mbcharcliplen() then returns an offset of zero,
so the whole string is returned where the correct answer is an
empty string:
SELECT right('abcdef', (-2147483648)::int4); -- 'abcdef', want ''
SELECT right('abcdef', -2147483647); -- '', correct
Clamp to PG_INT32_MAX instead. Any n whose absolute value is at
least the string's length skips all of it, and a text value cannot
be longer than PG_INT32_MAX, so this gives the same answer for every
other input. Erroring out, as text_format_string_conversion() does
for a width of INT_MIN, would not be correct here: unlike a format
width, an out-of-range skip count has a well-defined result.
Using pg_neg_s32_overflow() would be a slightly more optimal fix but
as it's only available in PostgreSQL 18 and later the decision was
taken to apply the same fix to all backbranches.
Backpatch to all supported versions.
Author: Ewan Young <kdbase.hack@gmail.com>
Reviewed-by: Daniel Gustafsson <daniel@yesql.se>
Reviewed-by: Dagfinn Ilmari Mannsåker <ilmari@ilmari.org>
Reviewed-by: David Rowley <dgrowleyml@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/CAON2xHNnBz-AcPJgDmd5_39+8qR5AUKEZk4X3ZM-0zdsATn8kQ@mail.gmail.com
Backpatch-through: 14 M src/backend/utils/adt/varlena.c
M src/test/regress/expected/text.out
M src/test/regress/sql/text.sql
Fix integer to_char() overflow with V format
commit : 04c25af21c02bf2c5152c3c8ab2ba877c231a598
author : Fujii Masao <fujii@postgresql.org>
date : Tue, 1 Sep 2026 11:42:13 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Tue, 1 Sep 2026 11:42:13 +0900 When to_char() formatted an integer value with a V pattern, it could
return an incorrect result instead of reporting an overflow. V shifts
the decimal point by multiplying the input value by a power of ten before
formatting it, so, for example,
to_char(3, '9V999999999')
requires computing 3 * 10^9. This result does not fit in int4, but
the integer variant of to_char() performed the multiplication using a
plain int32 expression. The intermediate result could therefore
overflow, causing the function to output incorrect digits instead of
raising "integer out of range".
Use dtoi4() and int4mul() for this calculation so that both an
out-of-range multiplier and an out-of-range product are detected, as
with ordinary integer arithmetic. This also matches the existing int8
implementation, which uses dtoi8() and int8mul() for the same
operation.
After this change, to_char() with V format either returns the
correctly formatted result when the scaled value fits in int4, or
raises "integer out of range" when it does not.
Backpatch to all supported versions.
Reported-by: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Reviewed-by: Miłosz Bieniek <bieniek.milosz@proton.me>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/CAB8bMivEfqZxOVdzc3kZDN++XshmkEz2t7dfGBU8+oUm864EZg@mail.gmail.com
Backpatch-through: 14 M src/backend/utils/adt/formatting.c
M src/test/regress/expected/int4.out
M src/test/regress/sql/int4.sql
Harden spell.c against out-of-order FLAG lines in Hunspell files.
commit : e96b27296bec1c6212874eceb394b4f8b7d06dcc
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Sat, 29 Aug 2026 19:56:49 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Sat, 29 Aug 2026 19:56:49 -0400 The compound flags collected from COMPOUNDFLAG and friends are stored in
either the string or the integer member of a union, according to the
flag mode that the affix file's FLAG line declares. NIImportOOAffixes()
converted each flag as soon as it read it, using the mode in effect at
that point, and recorded that mode in the entry. Since FLAG may appear
anywhere in the file, including after the compound flags, entries written
before and after it could disagree about which member of the union holds
the flag.
In assert-enabled builds, this would result in an assertion failure.
Otherwise, cmpcmdflag() takes the mode from its first argument and
applies it to both, so it can read an integer as a char pointer and pass
that to strcmp(). Depending on which way the mismatch goes, the result
is a segfault while sorting the array, a segfault in the bsearch() that
later looks flags up (the lookup key is built with the final mode, so
this happens even when the array itself is consistent), or, when both
members happen to be readable, no crash at all and a compound flag that
is never found, which silently disables compound word splitting. This
isn't a security bug because we consider dictionary files to be trusted
data, but it's still worth fixing. (In practice, dictionary files
usually put the FLAG line first, which is why this went unreported for
so long.)
Fix by keeping the flags as strings while the file is read and converting
them once it has been read in full, when the mode is final. This also
makes the position of the FLAG line irrelevant, which is how the flags on
AF, SFX and PFX lines are already treated: those are parsed in a second
pass and so always use the final mode. That precedent is reason for
behaving this way rather than throwing an error.
The old ispell file format reaches addCompoundAffixFlagValue() too,
from NIImportAffixes(), and returns without entering NIImportOOAffixes(),
so it needs the conversion step as well.
While we're here, also fix some integer width mismatches: store the
result of strtol() into a "long", and cast to int only after we've done
range checks. Typically a value too wide for int would fail the range
checks anyway, but in some cases it would be silently accepted after
truncation to int.
Author: Ewan Young <kdbase.hack@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAON2xHN3QmsaySM6DGWa1gttcbJoFh0wjAE-_ZpSPo=LKN1hYw@mail.gmail.com
Backpatch-through: 14 M src/backend/tsearch/spell.c
Update .abi-compliance-history for join removal fix.
commit : ecf45757c21ef140de613834ad6e01f0904f1fb0
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Fri, 28 Aug 2026 16:37:11 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Fri, 28 Aug 2026 16:37:11 -0400 Bug: #19560
Reported-by: Orestis Markou <orestis@orestis.gr>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Thom Brown <thom@linux.com>
Reviewed-by: Jacob Brazeal <jacob.brazeal@gmail.com>
Discussion: https://postgr.es/m/1186816.1784573544@sss.pgh.pa.us
Backpatch-through: 16-18 M .abi-compliance-history
Perform join removal by editing the query's jointree.
commit : 13466d1f78394a8ec2d6d0b1c9d8bf5ac3acb99a
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Fri, 28 Aug 2026 15:12:26 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Fri, 28 Aug 2026 15:12:26 -0400 analyzejoins.c decided which joins could be dropped by consulting the
planner's derived data structures, but then implemented the removal
by updating those structures in-place. That is a lot of fiddly work,
and nothing keeps it in step with the rest of the planner:
remove_leftjoinrel_from_query only bothered to update "parts of the
planner's data structures that will actually be consulted later", with
no good way to know what those are. Bug #19560 is one consequence.
In that report, removing a join leaves an EquivalenceClass that now
gives rise to a base restriction clause, but base restriction clauses
have already been generated and nothing reconsiders them, so the WHERE
condition disappears from the plan and we return wrong answers.
The self-join elimination code has the same design and the same type
of hazard. We have seen many related bugs over the years too, so it's
time to do something drastic.
To fix, do the removals by editing root->parse->jointree (which is a
far simpler and more stable representation than the derived data),
and then have query_planner() discard everything it computed from the
jointree and derive it over again. This requires quite a bit less
code, and doesn't require touching analyzejoins.c every time we change
the data derived by query_planner(). For typical cases it can actually
save a bit of planning time, though in cases where we have to iterate
the derivation loop many times it does add some time.
reduce_unique_semijoins() gets the same treatment: rather than deleting
the semijoin's SpecialJoinInfo and relying on the jointree not being
consulted again, it now changes the JoinExpr's jointype to JOIN_INNER
and recalculates everything.
Some plans change in the join regression test. Qual evaluation order
shifts in a few cases, because the conditions now reach later planning
in jointree order rather than in whatever order the removal code
re-distributed them. A few plans improve, since the rebuilt relation
targetlists no longer carry columns that only a removed join needed.
We also detect a constant-false filter condition whose test used to
carry a FIXME label. One plan gets marginally worse, because the old
code recomputed attr_needed from equivalence classes after a join
removal; that is more accurate than what deconstruct_jointree()
derives from the original clauses, but we no longer do that. Making
that recomputation happen anyway could be worth doing, but it should
be considered independently and perhaps implemented differently.
Back-patch to v16, on the grounds that the introduction of
varnullingrels in v16 made the old approach significantly more complex
and bug-prone; notably, bug #19560 does not manifest before v16.
In released branches, do not remove externally-visible fixup
functions such as remove_join_clause_from_rels, in case any
extensions are relying on them; but they're no longer used by core
code. But we must nonetheless break API/ABI for remove_useless_joins,
reduce_unique_semijoins, and remove_useless_self_joins, as those now
have different outputs and very different behavior than before.
It seems unlikely that any extensions are calling those; but just in
case, make the breakage more obvious by renaming remove_useless_joins
to remove_useless_outer_joins, which is a more sensible name for it
anyway since the addition of remove_useless_self_joins.
Full disclosure: initial drafts of this patch were made with
Claude Opus 4.8.
Bug: #19560
Reported-by: Orestis Markou <orestis@orestis.gr>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Thom Brown <thom@linux.com>
Reviewed-by: Jacob Brazeal <jacob.brazeal@gmail.com>
Discussion: https://postgr.es/m/1186816.1784573544@sss.pgh.pa.us
Backpatch-through: 16 M src/backend/optimizer/plan/analyzejoins.c
M src/backend/optimizer/plan/planmain.c
M src/backend/optimizer/plan/planner.c
M src/backend/rewrite/rewriteManip.c
M src/include/nodes/primnodes.h
M src/include/optimizer/planmain.h
M src/test/regress/expected/join.out
M src/test/regress/expected/rowsecurity.out
M src/test/regress/sql/join.sql
M src/test/regress/sql/rowsecurity.sql
Make platform guards in two regression tests match meson builds
commit : a26957518b31e22ea2ac9c395815eab55ca84ac3
author : Andrew Dunstan <andrew@dunslane.net>
date : Fri, 28 Aug 2026 09:36:13 -0400
committer: Andrew Dunstan <andrew@dunslane.net>
date : Fri, 28 Aug 2026 09:36:13 -0400 collate.linux.utf8 skips itself unless version() matches "linux-gnu",
and infinite_recurse skips itself when version() matches
"powerpc64[^,]*-linux-gnu". configure substitutes the GNU host triplet
into that string, but the meson build composes it from
host_machine.cpu_family() and host_system, which never carries the ABI
suffix. So ever since meson support arrived in 16, collate.linux.utf8
has not run at all on a meson build, and infinite_recurse has been
running on ppc64 Linux the very case it means to stay away from.
Meson documentation says it reports 'ppc64' instead of 'powerpc64'.
Fix by matching "-linux[-,]" and "p(ower)?pc64[^,]*-linux", which match
all spellings. Keeping the punctuation on either side confines the
match to the platform field. Neither pattern excludes musl, but
collate.linux.utf8's other conditions already require a set of glibc
locales to be present.
Backpatch to 16, where the meson build was introduced.
Discussion: https://postgr.es/m/a40b19da-9a02-47b4-8afd-2bbbde8db1e8@dunslane.net
Reviewed-By: Jonathan Gonzalez V. <jonathan@abdiel.eu>
Reviewed-By: Nazir Bilal Yavuz <byavuz81@gmail.com> M src/test/regress/expected/collate.linux.utf8.out
M src/test/regress/expected/collate.linux.utf8_1.out
M src/test/regress/expected/infinite_recurse.out
M src/test/regress/expected/infinite_recurse_1.out
M src/test/regress/sql/collate.linux.utf8.sql
M src/test/regress/sql/infinite_recurse.sql
Fix incorrect multi-column RANGE partition pruning
commit : 2d8494e24f75982a5663ea5ca55195ebf392fe76
author : David Rowley <drowley@postgresql.org>
date : Fri, 28 Aug 2026 13:11:46 +1200
committer: David Rowley <drowley@postgresql.org>
date : Fri, 28 Aug 2026 13:11:46 +1200 When performing partition pruning with a RANGE partitioned table where
the pruning quals are only present for a leading prefix of the partition
key, it was possible that partition pruning would accidentally prune away
some partitions which shouldn't be pruned and include some partitions that
were not needed.
This happened due to an incorrectly coded loop bound which was
terminating the loop when the bound reached the first or last element in
the partition bound array. This resulted in those end elements not being
checked in cases where they should be checked. It appears that it might
have been coded this way to avoid stepping off the array, but that was
done incorrectly as it failed to take into account the direction of travel
through the array (the loop can go forwards or backwards). I.e., it's
valid to loop when 'off' is the last element if we're going backwards
through the array, and valid to loop if 'off' is 0 and we're looping
forward through the array, but the code as it was didn't allow that.
Here we fix this by moving the loop condition check to after we've
calculated the array element to process, and break from the loop if that
element is beyond either end of the array.
Example of accidentally pruned partition:
p: partition by range (a, b);
p1: for values from (1, 4) to (1, 7);
p2: for values from (1, 7) to (3, 8);
p3: for values from (4, 8) to (6, 9);
def: default;
select * from p where a <= 1;
Here p2 was pruned by mistake.
Example of accidentally not pruning a partition:
p: partition by range (a, b);
p1: for values from (7, 2) to (7, 7);
def: default;
select * from p where a > 7;
No partitions would be pruned in this case, despite it being impossible
for matching rows to exist in p1.
Author: David Rowley <dgrowleyml@gmail.com>
Reviewed-by: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Reviewed-by: Tender Wang <tndrwang@gmail.com>
Discussion: https://postgr.es/m/CAApHDvp5ne9AWaH-tG1Lke-USLz3NwWLWTUdP5NT7ypKtcFqcg@mail.gmail.com
Backpatch-through: 14 M src/backend/partitioning/partprune.c
M src/test/regress/expected/partition_prune.out
M src/test/regress/sql/partition_prune.sql
Fix temporary WAL receiver slot handling on timeline switches
commit : 4c8fa02c5feaf7dc9be85da06987836777cd34fe
author : Fujii Masao <fujii@postgresql.org>
date : Fri, 28 Aug 2026 01:15:23 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Fri, 28 Aug 2026 01:15:23 +0900 Previously, when wal_receiver_create_temp_slot was enabled, a timeline
switch could cause the walreceiver to try to create the same temporary
replication slot again on the same connection. The slot had already been
created before the first streaming attempt and still existed, so the
second creation attempt failed with a FATAL error such as
"could not create replication slot ...".
The walreceiver would later be restarted and streaming replication could
continue, so this did not permanently break replication. Nevertheless,
the unexpected failure is a bug and should be fixed.
Fix this by tracking whether the temporary replication slot has already
been created for the lifetime of the walreceiver and skipping subsequent
creation attempts. Also copy the retained slot name to shared memory on
each streaming attempt, since RequestXLogStreaming() clears it when
streaming is restarted without a configured primary slot. This also
keeps pg_stat_wal_receiver.slot_name populated after timeline switches.
Backpatch to all supported versions.
Author: ChangAo Chen <cca5507@qq.com>
Reviewed-by: Quan Zongliang <quanzongliang@yeah.net>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/tencent_628FDAF814231923BC8E8357BBBC50F94207@qq.com
Backpatch-through: 14 M src/backend/replication/walreceiver.c
M src/test/recovery/t/004_timeline_switch.pl
Stabilize 019_replslot_limit
commit : 11c4bc621ae7cf27f00b95d0cf04e7a995c3dbe9
author : Fujii Masao <fujii@postgresql.org>
date : Thu, 27 Aug 2026 23:51:21 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Thu, 27 Aug 2026 23:51:21 +0900 The test assumed that advancing WAL would lead to a checkpoint that
invalidates the obsolete replication slot. If a checkpoint that started
before the WAL switch completes first, the following checkpoint can be
skipped as idle, so the expected walsender termination is not logged.
Force a CHECKPOINT in a background psql session after advancing WAL, so
the slot invalidation is exercised deterministically.
This has been observed on buildfarm members alligator and partridge:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=alligator&dt=2024-12-13%2001%3A24%3A58
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=partridge&dt=2026-08-06%2018%3A00%3A11
Backpatch to all supported versions.
Reported-by: Alexander Lakhin <exclusion@gmail.com>
Author: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Reviewed-by: Alexander Lakhin <exclusion@gmail.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/0b07ead5-a5da-445e-9698-a7d340708bdf@gmail.com
Backpatch-through: 14 M src/test/recovery/t/019_replslot_limit.pl
Don't create a shell type for function returning an array
commit : b62206043be298dfdf493d5dc4bcac9e573a99ce
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Thu, 27 Aug 2026 14:31:44 +0300
committer: Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Thu, 27 Aug 2026 14:31:44 +0300 Refactor the checks in the function to move all the conditions for
when to attempt creating a shell type into one place. Add a check for
the array syntax.
In addition to rejecting array syntax, another user-visible effect is
that the error message is now different if the type specified a
typmod. You now get "type does not exist" instead of the more
specific "type modifier cannot be specified for shell type". That
seems better; the implicit shell type creation exists only for
backwards compatibility, and it never worked with type modifiers, so
if there's a type modifier it's most likely not because the user tried
to create a shell type,
Add test for the array syntax, the type modifier, and some other cases
for which we don't create shell types.
Discussion: https://www.postgresql.org/message-id/de673feb-41b4-4685-b24b-6408b95e58ab@iki.fi
Backpatch-through: 14 M src/backend/commands/functioncmds.c
M src/test/regress/expected/create_type.out
M src/test/regress/sql/create_type.sql
Don't choose an invalid index for REPLICA IDENTITY FULL lookups.
commit : 92c0182141cf6355f5ca38b282531aca64068d4e
author : Amit Kapila <akapila@postgresql.org>
date : Thu, 27 Aug 2026 09:52:28 +0530
committer: Amit Kapila <akapila@postgresql.org>
date : Thu, 27 Aug 2026 09:52:28 +0530 For a REPLICA IDENTITY FULL remote relation whose local counterpart has
no primary key or replica identity, FindUsableIndexForReplicaIdentityFull()
chooses the first index of a suitable shape from RelationGetIndexList().
That list excludes only indexes that are not indislive, so an invalid
index left behind by a failed CREATE INDEX CONCURRENTLY can be selected.
Such an index need not contain every row. Consequently, changes for rows
that it fails to find can be silently dropped as missing-tuple conflicts.
If the index contains no rows at all, the scan can instead error out and
cause the apply worker to exit.
Skip invalid indexes, as the planner does.
Author: Mikhail Nikalayeu <mihailnikalayeu@gmail.com>
Reviewed-by: Miłosz Bieniek <bieniek.milosz@proton.me>
Reviewed-by: Amit Kapila <amit.kapila16@gmail.com>
Reviewed-by: Shlok Kyal <shlok.kyal.oss@gmail.com>
Reviewed-by: Vignesh C <vignesh21@gmail.com>
Reviewed-by: Ajin Cherian <itsajin@gmail.com>
Discussion: https://postgr.es/m/CADzfLwWuubcbJBDRZ_J1SSqHDNjNmUYSAgf5y=17LxmP401xbw@mail.gmail.com
Backpatch-through: 16, where it was introduced M src/backend/replication/logical/relation.c
M src/test/subscription/t/032_subscribe_use_index.pl
Pin two ctype-dependent test_regex_utf8 cases to pg_c_utf8
commit : 33b8a42dfba090cf140f8e5d01c4eddf2c08100c
author : Andrew Dunstan <andrew@dunslane.net>
date : Sat, 22 Aug 2026 17:47:24 -0400
committer: Andrew Dunstan <andrew@dunslane.net>
date : Sat, 22 Aug 2026 17:47:24 -0400 test_regex_utf8 decides whether to run by looking at the database
encoding alone, but two of its cases, [[:graph:]] and [[:print:]] over
E'xᔀሷ', depend on the ctype as well. In a database with encoding
UTF8 and locale C they match just the x, because isgraph() and isprint()
are false for anything outside ASCII, and the file fails.
No buildfarm animal builds such a cluster, which is why this went
unnoticed, and why the to_date() crash in 18.5 went undetected for want
of exactly this coverage. A pending buildfarm client change will let an
animal be configured that way.
Fix by giving the two cases an explicit collation, so that they exercise
a fixed Unicode ctype instead of whatever the database happened to be
initialized with. test_regex() already passes its input collation down
to the regex compiler. The expected results are unchanged; only the
echoed queries differ.
Backpatch-through: 17 (15 and 16 get a different fix)
Reviewed-by: Jonathan Gonzalez V. <jonathan@abdiel.eu> M src/test/modules/test_regex/expected/test_regex_utf8.out
M src/test/modules/test_regex/sql/test_regex_utf8.sql
Export subxip[] for snapshots taken during recovery.
commit : 2ae67ada74d9e8407e4286414dfddf5d157f4dc3
author : Peter Geoghegan <pg@bowt.ie>
date : Wed, 26 Aug 2026 15:38:11 -0400
committer: Peter Geoghegan <pg@bowt.ie>
date : Wed, 26 Aug 2026 15:38:11 -0400 A snapshot taken during recovery stores all of its in-progress XIDs in
subxip, every running top-level XID included, leaving xip empty. Unlike
with other snapshots, its suboverflowed flag does not mean that subxip
is redundant. We nevertheless treated it that way during snapshot
export, so an importing session could see in-progress transactions as
aborted. This misbehavior could also lead to hint bits being
incorrectly set on the standby; affected tuples then wrongly appeared
visible or invisible to sessions that never imported the snapshot.
To fix, teach snapshot export to include the subxip[] array regardless
of the overflow flag when the snapshot is taken during recovery. This
is in line with how CopySnapshot() and SerializeSnapshot() already
handle the same issue.
Claude Code diagnosed this problem. The committed TAP test is a
simplified version of the one that it wrote to demonstrate this bug.
Oversight in commit 6c2003f8a, which enabled snapshot export and import
during recovery.
Author: Peter Geoghegan <pg@bowt.ie>
Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Bug: #17846
Discussion: https://postgr.es/m/CAH2-WzmHVeYY%3Dpjz9x8DhhxVjXHX0pvoQ-MdiB1Tt6%3Do2GTiKg%40mail.gmail.com
Discussion: https://postgr.es/m/17846-1a0e5ce976f4c01a@postgresql.org
Backpatch-through: 14 M src/backend/utils/time/snapmgr.c
M src/test/recovery/meson.build
A src/test/recovery/t/056_standby_snapshot_export.pl
Fix crash in subscription refresh on concurrent relation drop.
commit : ebf7971dabac93558ebe0b1e47b4d665ac20c054
author : Masahiko Sawada <msawada@postgresql.org>
date : Wed, 26 Aug 2026 12:04:01 -0700
committer: Masahiko Sawada <msawada@postgresql.org>
date : Wed, 26 Aug 2026 12:04:01 -0700 Commit 46b4f5c11b0 made the logical replication origin checks quote
the schema and relation names they interpolate into the query sent to
the publisher. Those names can be NULL, which that commit overlooked.
AlterSubscription_refresh() collects the OIDs of the relations already
present in pg_subscription_rel and hands them to
check_publications_origin_tables() and
check_publications_origin_sequences(), which append the
schema-qualified name of each one to the query so that
already-subscribed relations are excluded from the check. The
relations are never locked, so one of them can be dropped concurrently
before its name is read, and get_rel_name() and get_namespace_name()
return NULL. quote_literal_cstr() dereferences it and crashes the
backend.
This commit fixes this by skipping a relation whose name is no longer
available. A dropped relation is not synchronized anyway, and the
appended clauses only exclude relations from a check whose sole effect
is a WARNING, so omitting one can at most produce a spurious WARNING.
The window is reachable from ALTER SUBSCRIPTION ... REFRESH
PUBLICATION and from SET, ADD and DROP PUBLICATION, which refresh by
default, but only when copy_data is true and origin is none. Backpatch
to v16 as commit 46b4f5c11b0 was back-patched that far. The sequence
path exists only in v19 and later.
The test is applied to v19 and later only. Adding it to v17 and v18
would require enabling injection point support in
src/test/subscription there, and v16 predates injection points
entirely. That is more test infrastructure churn on stable branches
than this fix warrants.
Reported-by: SATYANARAYANA NARLAPURAM <satyanarlapuram@gmail.com>
Author: SATYANARAYANA NARLAPURAM <satyanarlapuram@gmail.com>
Co-authored-by: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Reviewed-by: Ajin Cherian <itsajin@gmail.com>
Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com>
Discussion: https://postgr.es/m/CAHg+QDcd_o3707Ey8c8b7HkE-t14g8c0tk8ME3ctywDsh3ut8g@mail.gmail.com
Backpatch-through: 16 M src/backend/commands/subscriptioncmds.c
Close relations opened specifically for AFTER triggers
commit : 70d5b066825a8ce5f786313dc9751f3cf1d29569
author : David Rowley <drowley@postgresql.org>
date : Thu, 27 Aug 2026 02:42:32 +1200
committer: David Rowley <drowley@postgresql.org>
date : Thu, 27 Aug 2026 02:42:32 +1200 39dcfda2d fixed an incorrect reuse of ResultRelInfos for AFTER triggers
when the ResultRelInfo needed to have a different ri_RootResultRelInfo.
That caused an issue in logical replication apply workers as
finish_edata() neglects to call ExecCloseResultRelations() and instead
relies on ExecCleanupTupleRouting() to close relations opened during
partitioning's tuple routing. Since 39dcfda2d, because we may have done
some additional table_opens() calls due to having to create an additional
ResultRelInfo because of requirements to have a different
ri_RootResultRelInfo, we should now be explicitly closing any relations
opened on ResultRelInfos in EState's es_trig_target_relations.
Since finish_edate() seems to want to avoid calling
ExecCloseResultRelations(), add a new external function named
ExecCloseTrigTargetRelations().
Reported-by: Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
Author: Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com>
Author: David Rowley <dgrowleyml@gmail.com>
Reviewed-by: Zhijie Hou (Fujitsu) <houzj.fnst@fujitsu.com>
Discussion: https://postgr.es/m/OS9PR01MB121491E7E05950D108AF9A6D8F5A72@OS9PR01MB12149.jpnprd01.prod.outlook.com
Backpatch-through: 15 M src/backend/executor/execMain.c
M src/backend/replication/logical/worker.c
M src/include/executor/executor.h
M src/test/subscription/t/013_partition.pl
Don't assume DISTINCT ON implies uniqueness when the tlist has SRFs
commit : a4eb938b33557193d90c1b396afd2c274e28b07e
author : Richard Guo <rguo@postgresql.org>
date : Tue, 25 Aug 2026 10:16:58 +0900
committer: Richard Guo <rguo@postgresql.org>
date : Tue, 25 Aug 2026 10:16:58 +0900 query_is_distinct_for() treated a subquery's DISTINCT ON clause as
proof that its output is unique over the DISTINCT ON columns, even if
the targetlist contains set-returning functions. That's not true:
when the query has an ORDER BY, the planner postpones evaluation of
SRFs that are not DISTINCT ON or ORDER BY columns until after the
Unique step, so the subquery can produce duplicates of the DISTINCT ON
columns. Relying on this bogus uniqueness proof allowed join removal
and unique-inner joins to produce wrong results.
Plain DISTINCT is not affected, since all tlist columns are DISTINCT
columns there, and so any SRFs get expanded before the Unique step.
To fix, make query_supports_distinctness() and query_is_distinct_for()
refuse to prove distinctness via DISTINCT ON if the targetlist
contains any SRFs. This is more conservative than necessary, since
the SRFs are only postponed when there is an ORDER BY and none of them
appear in a sort/group column, but it doesn't seem worth the trouble
to check that precisely.
Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAMbWs4-hfd1Pyy_zBejsVUSy-3dx16rz2hgUakkKnAg3qg2q=Q@mail.gmail.com
Backpatch-through: 14 M src/backend/optimizer/plan/analyzejoins.c
M src/test/regress/expected/join.out
M src/test/regress/sql/join.sql
Fix GIN posting tree page deletion with incomplete splits.
commit : aa14281fdde6b2cc7a794dd02bc128dd0cb20b45
author : Peter Geoghegan <pg@bowt.ie>
date : Sat, 22 Aug 2026 14:50:35 -0400
committer: Peter Geoghegan <pg@bowt.ie>
date : Sat, 22 Aug 2026 14:50:35 -0400 GIN posting tree page deletion failed to consider whether the target
page's left sibling page, or the deletion target itself, was marked as
incompletely split. Page deletion finds the target page's left sibling
by walking the parent's downlinks, but an incompletely split page's new
right half is part of the sibling chain despite having no downlink.
Deletion could therefore overwrite the rightlink of the wrong page,
disconnecting the split's still-live right half from the sibling chain.
Scans would then silently miss tuples from that page.
To fix, teach the relevant page deletion path to avoid deleting a
posting tree page whose left sibling is marked incompletely split (and
to avoid doing so when the target page itself is so marked). This is
essentially the same approach used by nbtree page deletion.
Claude Code found this problem. The committed test case is a simplified
version of the one that it wrote to demonstrate this bug.
Author: Peter Geoghegan <pg@bowt.ie>
Reviewed-by: Andrey Borodin <x4mmm@yandex-team.ru>
Discussion: https://postgr.es/m/CAH2-Wz=sKJcn+OtfVN9rdg+Ps9e4cuQWNP-9t12UE2d8nEG90Q@mail.gmail.com
Backpatch-through: 14 M src/backend/access/gin/ginvacuum.c
M src/test/modules/gin/Makefile
M src/test/modules/gin/expected/gin_incomplete_splits.out
M src/test/modules/gin/sql/gin_incomplete_splits.sql
Doc: fix typo
commit : 762bc1bb8ac93232e2c1dfdf1074e0602735cb17
author : David Rowley <drowley@postgresql.org>
date : Sat, 22 Aug 2026 12:50:08 +1200
committer: David Rowley <drowley@postgresql.org>
date : Sat, 22 Aug 2026 12:50:08 +1200 Author: Jochen Bandhauer <jb@jbitc.de>
Discussion: https://postgr.es/m/69ecfa04-9177-42fd-8d5d-9f375669fc5b@jbitc.de
Backpatch-through: 14 M doc/src/sgml/maintenance.sgml
Attempt to stabilize plan of self-join test in tidscan.sql
commit : 1c5a57b87626b99aa3ca1163dd7d0418df0c197b
author : David Rowley <drowley@postgresql.org>
date : Fri, 21 Aug 2026 12:27:29 +1200
committer: David Rowley <drowley@postgresql.org>
date : Fri, 21 Aug 2026 12:27:29 +1200 The test that checks the expected plan for this self-join test has been
known to have failed in the past due to badly timed VACUUMs causing
small variations in row estimates on one of the tables, resulting in a
swapped join order. Currently, failures have only been seen in v14, and
seemingly due to 74388a1ac and 4496020e6 the failures have not been seen
in more recent versions.
Here we shrink down the number of matching rows on one side of the join
to make the alternative join order's costs more expensive relative to
the cheapest join order. Previously the alternative order had the same
cost.
We do this in all supported versions to reduce the chances of future
changes reintroducing stability issues with these queries.
Reported-by: Alexander Lakhin <exclusion@gmail.com>
Author: David Rowley <dgrowleyml@gmail.com>
Discussion: https://postgr.es/m/f5d1f4c2-6224-4797-be17-c86e77f96c9c@gmail.com
Backpatch-through: 14 M src/test/regress/expected/tidscan.out
M src/test/regress/sql/tidscan.sql
Fix snapshot import xmin ProcArrayLock bug.
commit : 24bf23fe665f265328cc6ff82a5a5d5faab2b3bd
author : Peter Geoghegan <pg@bowt.ie>
date : Thu, 20 Aug 2026 19:47:27 -0400
committer: Peter Geoghegan <pg@bowt.ie>
date : Thu, 20 Aug 2026 19:47:27 -0400 ProcArrayInstallImportedXmin verifies that the source transaction (the
transaction whose snapshot we're importing) is still running, and then
installs the caller's imported xmin. These steps have to be atomic.
But it was just about possible for VACUUM to fail to observe the
imported xmin in either the source proc or the importing one. This
could result in VACUUM pruning away deleted tuples that were still
visible to the imported snapshot.
To fix, take ProcArrayLock in exclusive mode while importing an exported
snapshot's xmin within ProcArrayInstallImportedXmin. That guarantees
that a concurrent VACUUM's OldestXmin cannot advance past the xmin (one
proc or the other always advertises an xmin that holds it back).
Author: Chee Wooson <chee.wooson@gmail.com>
Reviewed-by: Peter Geoghegan <pg@bowt.ie>
Discussion: https://postgr.es/m/20260730042128.714201-1-chee.wooson@gmail.com
Backpatch-through: 14 M src/backend/storage/ipc/procarray.c
hashtext: fix fragile code.
commit : e2a44c77112c76113dad8dd6e99bddd91250f6c4
author : Jeff Davis <jdavis@postgresql.org>
date : Thu, 20 Aug 2026 13:54:26 -0700
committer: Jeff Davis <jdavis@postgresql.org>
date : Thu, 20 Aug 2026 13:54:26 -0700 Previously, in the path for non-deterministic collations, the code
assumed that bsize==rsize. That assumption seems to be true for ICU,
and all non-deterministic collations are ICU, so it's not known to be
an actual bug.
The only known place where bsize may not equal rsize is in the libc
provider, where strxfrm() can return an upper bound of the size needed
to store the result. That means the initial call to determine the
buffer size (with dest==NULL, n==0) could return a larger number than
the actual call with an adequate dest buffer. That's OK, because libc
locales are always deterministic.
Commit 679c5084cf2 partially fixed the assumption, but missed this
part. Fix it, and add a more prominent documentation note.
Reviewed-by: Haibo Yan <tristan.yim@gmail.com>
Discussion: https://postgr.es/m/CABXr29Hb31nkj1g2Jmk+1BhAm=3ecGs_pWy4tU++j8CQBnbMxQ@mail.gmail.com
Backpatch-through: 16 M src/backend/access/hash/hashfunc.c
M src/backend/utils/adt/pg_locale.c
M src/backend/utils/adt/varchar.c
Skip bogus find_composite_type_dependencies() call on sequences
commit : 25251bb27fad53075b94556dabc486fc8de84ba4
author : Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Thu, 20 Aug 2026 11:45:51 +0300
committer: Heikki Linnakangas <heikki.linnakangas@iki.fi>
date : Thu, 20 Aug 2026 11:45:51 +0300 A sequence has no rowtype. We called
find_composite_type_dependencies() with InvalidOid, which is harmless
but pointless. Skip it.
This started to happen with commit 344d62fb9a97 in v15, which added
the ALTER SEQUENCE ... SET LOGGED/UNLOGGED subcommand. Before that,
sequences were never rewritten. While this is harmless, backpatch to
keep the code the same on all branches, to make backpatching future
patches a little easier.
Backpatch-through: 15 M src/backend/commands/tablecmds.c
Reject too many arguments in CREATE TRIGGER
commit : 259ca794c983772e5f8a321ad49c64f7d85d2c9f
author : Michael Paquier <michael@paquier.xyz>
date : Thu, 20 Aug 2026 09:38:31 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Thu, 20 Aug 2026 09:38:31 +0900 The number of trigger arguments is stored as a smallint, but there was
no check that the number of arguments fits with the catalog data type.
This could result in an invalid negative value being stored once one
defined more than INT16_MAX arguments, with an overflowed value stored
in the catalogs.
Looking at other catalogs that store a number of arguments, we have
similar protections already in place (aggregates, functions, etc.).
Reported-by: Xingwang Xiang <v3rdant.xiang@gmail.com>
Author: Kyotaro Horiguchi <horikyota.ntt@gmail.com>
Discussion: https://postgr.es/m/19627-5b72a57e332e2b3f@postgresql.org
Backpatch-through: 14 M src/backend/commands/trigger.c
GiST: Invalidate killed items consistently.
commit : d316899e1436e4d0268f596f405f9cdeb9cbc1b9
author : Peter Geoghegan <pg@bowt.ie>
date : Wed, 19 Aug 2026 15:45:50 -0400
committer: Peter Geoghegan <pg@bowt.ie>
date : Wed, 19 Aug 2026 15:45:50 -0400 GiST neglected to invalidate its killedItems[] array on a rescan. As a
result, it was just about possible for the wrong tuples from the wrong
index page to be LP_DEAD-marked on a rescan. The scan mistakenly
believed that the previous rescan's killedItems[] were for this rescan's
curBlkno, causing index corruption.
To fix, bring GiST in line with nbtree and hash: call gistkillitems from
both gistrescan and gistendscan (the existing gistgettuple caller still
handles the common case where we need to LP_DEAD-mark before moving on
to the next page). That way the scan's pending killedItems[] are passed
to gistkillitems while they still describe items from curBlkno. When
gistkillitems runs, it'll invalidate the array in passing (and won't
needlessly miss out on an opportunity to LP_DEAD-mark eligible index
tuples). Back branches just get minimal hardening: we invalidate
killedItems[] at the places where the master branch gets new calls to
gistkillitems (and we invalidate curBlkno and curPageLSN on a rescan).
The test that proved corruption on master didn't result in corruption on
any stable branch, though only because, without commit 9c9ddf109, we'd
clobber curPageLSN without also updating curBlkno -- which accidentally
prevented it. Relying on gistkillitems to not LP_DEAD-mark by passing
it a curBlkno whose curPageLSN was taken from an entirely different page
seems like a very bad idea, which is why this issue is being treated as
a bug affecting all stable branches.
Author: Peter Geoghegan <pg@bowt.ie>
Reviewed-By: Andrey Borodin <x4mmm@yandex-team.ru>
Discussion: https://postgr.es/m/CAH2-WzmwEThnQf17Ju+t0N9_KJLsEQSXzYrFnaS2=s4KnGGrqw@mail.gmail.com
Backpatch-through: 14 M src/backend/access/gist/gistget.c
M src/backend/access/gist/gistscan.c
Fix GIN multiple-VACUUM-scans pending list bug.
commit : d778f00c145d94285e77587f00a44dcf99cc0620
author : Peter Geoghegan <pg@bowt.ie>
date : Wed, 19 Aug 2026 13:46:40 -0400
committer: Peter Geoghegan <pg@bowt.ie>
date : Wed, 19 Aug 2026 13:46:40 -0400 ginbulkdelete performs pending list cleanup before it searches the entry
tree (and any posting trees) for dead TIDs. This is necessary to avoid
leaving behind dangling TID references that index vacuuming is required
to remove; nothing prevents recently inserted pending list tuples from
containing TIDs that VACUUM already considers dead.
However, ginbulkdelete neglected to perform pending list cleanup on
VACUUM's second or subsequent call. It was therefore possible for a
VACUUM that requires multiple rounds of index vacuuming to leave behind
dangling references.
To fix, teach ginbulkdelete to perform pending list cleanup during every
call. In passing, tweak some related comments in the pending list
cleanup path to make it clear why it's safe for VACUUM to not _fully_
empty an index's pending list.
This was arguably an oversight in commit e2c79e14, which fixed a similar
issue where pending list cleanup by VACUUM could end early, but missed
this closely related problem.
Author: Peter Geoghegan <pg@bowt.ie>
Reviewed-by: Andrey Borodin <x4mmm@yandex-team.ru>
Discussion: https://postgr.es/m/CAH2-Wzmsa-RPA2Ko8A5LaGOnmbpimJ--71xkiBqwgjk3Fq8YEg@mail.gmail.com
Backpatch-through: 14 M src/backend/access/gin/ginfast.c
M src/backend/access/gin/ginvacuum.c
M src/include/access/gin_private.h
Fix GIN VACUUM posting tree root split bug.
commit : af7cbf4e206824d2ade9b8ce699501262f7a5773
author : Peter Geoghegan <pg@bowt.ie>
date : Wed, 19 Aug 2026 12:31:03 -0400
committer: Peter Geoghegan <pg@bowt.ie>
date : Wed, 19 Aug 2026 12:31:03 -0400 ginVacuumPostingTreeLeaves swaps a shared buffer lock for an exclusive
one when it encounters a leaf page. It neglected to re-verify whether a
page that was initially a leaf root page became an internal page due to
a concurrent root page split (during the window when no lock was held).
It was therefore possible for GIN VACUUM to spuriously treat an internal
page as a leaf page, leading to data corruption. VACUUM could miss dead
TIDs that it was required to remove, leaving behind dangling references
in the index.
To fix, re-verify that a leaf page is still a leaf page after an
exclusive lock is acquired. If it isn't, drop our exclusive lock and
acquire a shared lock so that the non-leaf root page gets processed in
the usual way.
Oversight in commit fd83c83d, which fixed a deadlock bug in GIN posting
tree vacuuming.
Author: Peter Geoghegan <pg@bowt.ie>
Reviewed-by: Andrey Borodin <x4mmm@yandex-team.ru>
Discussion: https://postgr.es/m/CAH2-Wz=RBpJTQgvOxr6C=J04dExmFSt1E3F-r+cRTQ56hEotkg@mail.gmail.com
Backpatch-through: 14 M src/backend/access/gin/ginvacuum.c
psql: Fix psql slash option leaks
commit : 4c5485aedfa0f4c88b4d951018b85d0302bff5f0
author : Fujii Masao <fujii@postgresql.org>
date : Wed, 19 Aug 2026 12:38:16 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Wed, 19 Aug 2026 12:38:16 +0900 psql_scan_slash_option() returns a malloc'd string, but \getresults,
\gset in pipeline mode, \restrict, and \unrestrict did not free it
after consuming or copying the value.
Free these option strings after use.
Backpatch to all supported versions. In v17 and older, only \restrict
and \unrestrict are affected, so those branches need only that part of
the fix.
Author: Fujii Masao <masao.fujii@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/CAHGQGwEh3R3=1tx_a5=fTDJ+ycuwxWMEn6bG_Yt4B5P+hE7AVw@mail.gmail.com
Backpatch-through: 14 M src/bin/psql/command.c
psql: Avoid returning oom_buffer from psql slash option scanner
commit : d2e19268dc9f2fc9a28eeb8052b96d5f3f417f00
author : Fujii Masao <fujii@postgresql.org>
date : Wed, 19 Aug 2026 12:34:28 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Wed, 19 Aug 2026 12:34:28 +0900 psql_scan_slash_option() builds option text in a local PQExpBufferData
and returns the buffer's data pointer to its caller. If either the initial
allocation or a later enlargement failed, that data pointer could be
the static PQExpBuffer OOM buffer rather than malloc-owned storage.
The callers could then eventually pass it to free(), causing undefined
behavior.
Detect a broken option buffer before returning it, report OOM, and return
NULL instead. Also avoid evaluating a backtick substitution when the option
buffer is already broken, since doing so could otherwise touch the static
OOM buffer.
This keeps the existing NULL-return convention for slash options. Callers
are not generally changed to distinguish OOM from no option.
Backpatch to all supported versions.
Reported-by: Junwang Zhao <zhjwpku@gmail.com>
Author: Fujii Masao <masao.fujii@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Junwang Zhao <zhjwpku@gmail.com>
Discussion: https://postgr.es/m/CAHGQGwEh3R3=1tx_a5=fTDJ+ycuwxWMEn6bG_Yt4B5P+hE7AVw@mail.gmail.com
Backpatch-through: 14 M src/bin/psql/command.c
M src/bin/psql/psqlscanslash.l
psql: Avoid returning oom_buffer from psql slash command scanner
commit : ab001a3a6925e882d2585903a7448a250ec7b428
author : Fujii Masao <fujii@postgresql.org>
date : Wed, 19 Aug 2026 12:31:00 +0900
committer: Fujii Masao <fujii@postgresql.org>
date : Wed, 19 Aug 2026 12:31:00 +0900 psql_scan_slash_command() builds the command name in a local
PQExpBufferData and returns the buffer's data pointer to its caller. If
either the initial allocation or a later enlargement failed, that data
pointer could be the static PQExpBuffer OOM buffer rather than malloc-owned
storage. HandleSlashCmds() could then eventually pass it to free(), causing
undefined behavior.
Detect a broken command-name buffer before returning it, report OOM, and
return NULL instead. Teach HandleSlashCmds() to treat a NULL command name
as a command error before trying to compare or dispatch it.
Backpatch to all supported versions.
Reported-by: Junwang Zhao <zhjwpku@gmail.com>
Author: Fujii Masao <masao.fujii@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Junwang Zhao <zhjwpku@gmail.com>
Discussion: https://postgr.es/m/CAHGQGwEh3R3=1tx_a5=fTDJ+ycuwxWMEn6bG_Yt4B5P+hE7AVw@mail.gmail.com
Backpatch-through: 14 M src/bin/psql/command.c
M src/bin/psql/psqlscanslash.l
Fix relcache reference leak when decoding TRUNCATE
commit : 26a9d8ea643520856ca558b926970e70fcbbf6dc
author : Michael Paquier <michael@paquier.xyz>
date : Wed, 19 Aug 2026 11:32:52 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Wed, 19 Aug 2026 11:32:52 +0900 ReorderBufferProcessTXN() opens every relation referenced by a
TRUNCATE change. When RelationIsLogicallyLogged() returns false,
it skips the relation without releasing the reference acquired
by RelationIdGetRelation().
Looking at the in-core code paths building XLOG_HEAP_TRUNCATE records,
no relation OIDs would be included if they do not satisfy
RelationIsLogicallyLogged(). One pattern that could go through is if a
table is switched to SET UNLOGGED, but that would not be reachable in
practice as the decoding happens after a historical snapshot is taken,
so the relation should still be valid.
This is a defense-in-depth measure in practice, and we tend to be
careful about how Relations are handled when sending changes to output
plugins, so backpatch all the way down.
Author: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Discussion: https://postgr.es/m/7DD65D03-3B5A-43B2-99AD-8E6AF5372BAB@gmail.com
Backpatch-through: 14 M src/backend/replication/logical/reorderbuffer.c
Report single-page checksum failures in pg_stat_database
commit : 7f92983dcd76afa03117da6ae2c0c60ed7ce74e9
author : Michael Paquier <michael@paquier.xyz>
date : Wed, 19 Aug 2026 09:51:56 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Wed, 19 Aug 2026 09:51:56 +0900 Base backups reported checksum failures to pg_stat_database only for
files with more than one failing page. Commit 6b9e875f728 placed the
report inside the block emitting the per-file summary WARNING, which
was skipped for a single failure. As a result, a backup failing on
files with one corrupted page each left checksum_failures untouched.
To fix, emit the per-file summary and the pgstat report for any
non-zero failure count. The end-of-backup total WARNING had the same
off-by-one and is now also emitted for a single failure.
Author: Zsolt Parragi <zsolt.parragi@percona.com>
Reviewed-by: Nazir Bilal Yavuz <byavuz81@gmail.com>
Discussion: https://postgr.es/m/CAN4CZFN+Bi6XmaH8zOdMWjoycYFx9nKtOr+dzQf0o-UQ+Rdqmw@mail.gmail.com
Backpatch-through: 14 M src/backend/backup/basebackup.c
M src/bin/pg_basebackup/t/010_pg_basebackup.pl
Defend against null "SV *" pointers in plperl modules.
commit : 2860d322ae98dd3c1f8608fc02fb777dd51d41f2
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Tue, 18 Aug 2026 17:33:44 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Tue, 18 Aug 2026 17:33:44 -0400 Tied hashes, and probably tied arrays, are capable of returning Perl
value pointers that are actually NULL, not the usual pointer to an
undef SV. We were not defending against that everywhere, leading
to possible SIGSEGV. Fix the code to consistently treat a null
pointer returned from hv_iternext or av_fetch like a !SvOK one.
(Note that the large diff in SV_to_JsonbValue is actually quite
trivial, but it required reindenting a chunk of existing code.)
Claude Code found the instance in hstore_plperl, and I found the
others by code auditing. Perhaps the other instances aren't
actually reachable, but I see little reason to assume that.
The known test cases for these errors require perl's Tie modules,
which may not be present, so it doesn't seem worth the trouble
to create regression test cases that would cover them.
Reported-by: Claude Code (via Noah Misch)
Author: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/569769.1786901901@sss.pgh.pa.us
Backpatch-through: 14 M contrib/hstore_plperl/hstore_plperl.c
M contrib/jsonb_plperl/jsonb_plperl.c
M src/pl/plperl/plperl.c
test_json_parser: Fix broken file ref and inverted result check
commit : 10a6960632d4299324aaac69b25a9d9652b1fa0d
author : Andrew Dunstan <andrew@dunslane.net>
date : Tue, 18 Aug 2026 09:06:05 -0400
committer: Andrew Dunstan <andrew@dunslane.net>
date : Tue, 18 Aug 2026 09:06:05 -0400 A comma following a file ref in a perl print statement makes the
statement just print the file's GLOB rather than redirecting the
following arguments to the file. This meant the test was not testing any
contents and the logs were instead bulked up with what the test was
supposed to be testing.
Also, run_log() returns 1 for success, not 0, so the tests for the
return values were wrong. (We were getting 0 because of the comma error
above.)
Backpatch-thru: 17 M src/test/modules/test_json_parser/t/004_test_parser_perf.pl
Doc: clean up documentation about text search datatype limits.
commit : c5450dcb3fe70bee79fdfe8a89f84dd8fffb566e
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 17 Aug 2026 18:17:51 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 17 Aug 2026 18:17:51 -0400 textsearch.sgml neglected to mention that the MAXSTRPOS total-length
limit applies to tsquery as well as tsvector. It also claims that
there is a 32K limit on the total number of nodes in a tsquery, which
is wrong. (I suspect that QueryOperator.left may once have been
int16, which would give rise to such a limit. But it's uint32 now,
so you'd hit the 1GB varlena limit well before overflowing that.)
While at it, re-order the bullet points into an order that makes
more sense, to me anyway.
Reported-by: Claude Code (via Noah Misch)
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/455079.1786897319@sss.pgh.pa.us
Backpatch-through: 14 M doc/src/sgml/textsearch.sgml
Tighten up tsqueryrecv().
commit : 91989795d2876c5105171226eb1fd24eabe97504
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 17 Aug 2026 18:09:07 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 17 Aug 2026 18:09:07 -0400 tsqueryrecv() accepted zero-length lexemes, which tsqueryin() doesn't.
It also accepted phrase distance values larger than MAXENTRYPOS,
which tsqueryin() doesn't. While neither of these omissions are
very harmful in themselves, they do allow accepting tsquery values
that will fail in a subsequent textual dump/reload.
Commit 23d9ad771 performed similar tightening of tsvectorrecv(),
but I left off these changes at the time because they didn't seem
to have security implications.
Reported-by: Claude Code (via Noah Misch)
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/455079.1786897319@sss.pgh.pa.us
Backpatch-through: 14 M src/backend/utils/adt/tsquery.c
GiST: Deprecate F_TUPLES_DELETED opaque area flag.
commit : 727295ae6dd2a120d8cefd802a555a3e1a8d4e34
author : Peter Geoghegan <pg@bowt.ie>
date : Mon, 17 Aug 2026 17:31:07 -0400
committer: Peter Geoghegan <pg@bowt.ie>
date : Mon, 17 Aug 2026 17:31:07 -0400 This flag hasn't been useful since the removal of old-style VACUUM FULL,
so remove it now (this includes removing vestigial code that
unnecessarily set the flag).
Also add test coverage of gistprunepage(). Had that test case been
available before now, the issue fixed by this commit would have been
detected by wal_consistency_checking buildfarm animals.
Author: Peter Geoghegan <pg@bowt.ie>
Reviewed-by: Michael Paquiër <michael@paquier.xyz>
Discussion: https://postgr.es/m/CAH2-WznbTsQCrjmd=eSawfPqcxCjSFUkk6Qzd3z+gpNte5i03Q@mail.gmail.com
Backpatch-through: 14 M src/backend/access/gist/gistvacuum.c
M src/backend/access/gist/gistxlog.c
M src/include/access/gist.h
M src/test/regress/expected/gist.out
M src/test/regress/sql/gist.sql
Make plperl's handling of Perl arrays safer and more consistent.
commit : 3c4d77480cfc0235f313f7ac9085bdcf698dac6e
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 17 Aug 2026 15:06:02 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Mon, 17 Aug 2026 15:06:02 -0400 plperl_func_handler()'s stanza for handling an arrayref result in
a SETOF function could loop forever (or at least till OOM) when
given a tied array, since av_fetch won't necessarily ever return
a null pointer in that case. Be consistent with the other places
where we traverse a perl array: call av_len() once and use len+1
as the loop limit, silently ignoring any null pointers we get back
from that range of subscripts.
But actually, Perl's preferred locution for this seems to be to
use av_count() not av_len()+1. av_count() seems better since
there's less risk of forgetting to add 1. Also, both of those
functions return Size_t (or SSize_t) not int, creating at least
a theoretical overflow hazard. While we're modernizing this,
let's use the correct variable type where we can, and include an
overflow check where we can't.
Reported-by: Claude Code (via Noah Misch)
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Discussion: https://postgr.es/m/569769.1786901901@sss.pgh.pa.us
Backpatch-through: 14 M contrib/jsonb_plperl/jsonb_plperl.c
M src/pl/plperl/plperl.c
doc: Clarify the logging collector's guarantees.
commit : 786db8dcf168bd9df8f55047337525ac19118b1c
author : Nathan Bossart <nathan@postgresql.org>
date : Fri, 14 Aug 2026 15:27:16 -0500
committer: Nathan Bossart <nathan@postgresql.org>
date : Fri, 14 Aug 2026 15:27:16 -0500 Presently, the documentation for logging_collector says that the
collector "is designed to never lose messages," which reads as a
stronger promise than we actually make. The collector does not
fsync the log file or retry failed writes, so log messages can go
missing after an operating system crash, power loss, or a write
error. Reword that sentence and add a note about what is not
guaranteed.
Author: Daniel Bauman <danielbaniel@gmail.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Reviewed-by: Zhenwei Shang <a934172442@gmail.com>
Reviewed-by: Robert Treat <rob@xzilla.net>
Discussion: https://postgr.es/m/CAMtj0_a86DdDKkW-ReVpQpqjndVS6GMrwXVpQY4G3-SGY7saMQ%40mail.gmail.com
Backpatch-through: 14 M doc/src/sgml/config.sgml
Add missing PGDLLIMPORT marker.
commit : 45df9b21ae617ff124da4e8364ae76199ae5acf6
author : Nathan Bossart <nathan@postgresql.org>
date : Fri, 14 Aug 2026 13:41:32 -0500
committer: Nathan Bossart <nathan@postgresql.org>
date : Fri, 14 Aug 2026 13:41:32 -0500 Oversight in commit ffca23839c.
Reported-by: Anton Voloshin <a.voloshin@postgrespro.ru>
Author: Anton Voloshin <a.voloshin@postgrespro.ru>
Backpatch-through: 14 M src/include/utils/acl.h
psql: count every COPY FROM STDIN when scanning a query string.
commit : 3828ab7047ba8204c9d71cf606762e9b2c46c024
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Fri, 14 Aug 2026 12:14:24 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Fri, 14 Aug 2026 12:14:24 -0400 When SendQuery() is not told how many COPY FROM STDIN commands the
query string contains (as for -c, \gexec, and \watch), it scans the
string to count them itself. But it called psql_scan() only once,
which stops at the first semicolon, so any COPY FROM STDIN past the
first sub-command was not counted, causing failure of cases that used
to work. Oversight in commit 3045a25ba.
Author: Zsolt Parragi <zsolt.parragi@percona.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAN4CZFPqa6c+u4uX5jJ8LANHTQ4dxM3m4_8G9WmX_A4-2wuv2A@mail.gmail.com
Backpatch-through: 14 M src/bin/psql/common.c
M src/bin/psql/t/001_basic.pl
Fix authorization check for role membership changes.
commit : be0076b71b68f2e7cb2553af48aa8f8ecc8bda40
author : Nathan Bossart <nathan@postgresql.org>
date : Thu, 13 Aug 2026 16:35:07 -0500
committer: Nathan Bossart <nathan@postgresql.org>
date : Thu, 13 Aug 2026 16:35:07 -0500 Presently, check_role_membership_authorization() decides whether
the current user may grant or revoke membership in a role by
calling is_admin_of_role(), which recurses through all grants,
while it chooses the grantor to record for the resulting entry by
calling select_best_admin(), which recurses only through inherited
grants. When the two disagree, the permission check passes and the
grantor lookup then comes up empty, so the user sees an internal
"no possible grantors" error. ALTER GROUP ... ADD USER reaches the
same error through the separate check in AlterRole().
To fix, teach both checks to search the same way
select_best_admin() does via a new has_admin_privs_of_role(). The
new check passes exactly when the grantor lookup was going to
succeed, so nothing that works today starts failing; the internal
error simply becomes a proper permission error. Note that this
leaves the other callers of is_admin_of_role() alone, so a role
reachable only through a non-inherited grant can still be dropped,
renamed, or altered. Whether that ought to change as well is left
as a future exercise.
Oversight in commit ce6b672e44.
Reported-by: ChangAo Chen <cca5507@qq.com>
Author: ChangAo Chen <cca5507@qq.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Pretham <prezza672@gmail.com>
Reviewed-by: Robert Haas <robertmhaas@gmail.com>
Reviewed-by: Jacob Champion <jacob.champion@enterprisedb.com>
Discussion: https://postgr.es/m/tencent_ADCE2B34B230A9B631854806104FEF40C105%40qq.com
Discussion: https://postgr.es/m/CAJUn_kN%2BMhbb8fYP5xxQCq1KEziOinM6HgYx4ts_pPDnQ2y1nQ%40mail.gmail.com
Backpatch-through: 16 M src/backend/commands/user.c
M src/backend/utils/adt/acl.c
M src/include/utils/acl.h
M src/test/regress/expected/privileges.out
M src/test/regress/sql/privileges.sql
Consistently enforce tsvector/tsquery maximum lengths.
commit : b42d0c310bec31073cf523a99caf5c79abf9e204
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Thu, 13 Aug 2026 11:20:27 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Thu, 13 Aug 2026 11:20:27 -0400 Some places rejected individual tokens longer than MAXSTRLEN, while
others rejected ones longer than MAXSTRLEN-1. The data structure is
perfectly capable of handling MAXSTRLEN, so there's nothing wrong
with using the looser bound. Moreover, as things stand there is a
dump/reload hazard: some code paths permit construction of a tsvector
or tsquery that would later be rejected by tsvectorin or tsqueryin.
So standardize on using MAXSTRLEN.
Identical remarks apply to MAXSTRPOS (the total data length),
so fix that too.
Back-patch, in hopes of avoiding cases where a value acceptable to
one supported release is not acceptable to another.
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Zsolt Parragi <zsolt.parragi@percona.com>
Discussion: https://postgr.es/m/CAN4CZFNYQo4zfbRR435uD0vSfuy5y7dnFOXDfKr9zYoL1JnAxA@mail.gmail.com
Backpatch-through: 14 M src/backend/tsearch/ts_parse.c
M src/backend/utils/adt/tsquery.c
M src/backend/utils/adt/tsvector.c
M src/backend/utils/adt/tsvector_op.c
Fix poll_query_until() timeout handling with an undefined query in Cluster.pm
commit : 1cafef358c883857bb718cb83cf84436abb3e993
author : Michael Paquier <michael@paquier.xyz>
date : Wed, 12 Aug 2026 16:23:14 +0900
committer: Michael Paquier <michael@paquier.xyz>
date : Wed, 12 Aug 2026 16:23:14 +0900 On timeout, poll_query_until() would fail to print its report when
"$query" was undefined. Instead of the expected report, this prevented
the contents of stdout and stderr from being shown.
Note that some of the in-core tests use an undefined query for
connection-only polls.
Author: Bryan Green <dbryan.green@gmail.com>
Reviewed-by: Jonathan Gonzalez V. <jonathan.abdiel@gmail.com>
Discussion: https://postgr.es/m/0f515c6d-6032-4c5f-80ac-5c78faae9522@gmail.com
Backpatch-through: 14 M src/test/perl/PostgreSQL/Test/Cluster.pm
Tweak allocation rule for tzload()'s "union local_storage" variable.
commit : 5c52853cb71dab5bb6c2e482ac8becad6ab0278c
author : Tom Lane <tgl@sss.pgh.pa.us>
date : Tue, 11 Aug 2026 18:17:48 -0400
committer: Tom Lane <tgl@sss.pgh.pa.us>
date : Tue, 11 Aug 2026 18:17:48 -0400 By default, allocate this via malloc, as we've been doing since commit
62c8421e8. Commit aeb07c55f adopted upstream tzdb's default of
allocating it on the stack, but that still doesn't seem like a good
idea for the reasons given in 62c8421e8 (and now memorialized in a
comment, in hopes that we don't make the same mistake again).
However, under USE_VALGRIND, put it on the stack as upstream does.
This accidentally prevents a crash when Python 3.14 is used under
Valgrind. The reasons for that are obscure, and it's most likely
not our bug, and even if we figured it out it'd be nice to have a fix
for buildfarm member skink now rather than after persuading the guilty
party to fix it.
In the normal non-USE_VALGRIND case, this has no effect on the logic
in released branches, and it reverts master to match them.
Reported-by: Alexander Lakhin <exclusion@gmail.com>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/f071e691-5930-4738-9dbd-43ed2da367fe@gmail.com
Backpatch-through: 14 M src/timezone/localtime.c