PostgreSQL 18.6 commit log

Stamp 18.7.

commit   : f5c41f6681c910b3f69951bf6fb97c6d74605d25    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Mon, 9 Nov 2026 15:09:29 -0500    
  
committer: Tom Lane <tgl@sss.pgh.pa.us>    
date     : Mon, 9 Nov 2026 15:09:29 -0500    

Click here for diff

M configure
M configure.ac
M meson.build

Last-minute updates for release notes.

commit   : aed4c42bce80dda767277cf983baec4678c55daf    
  
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    

Click here for diff

Security: CVE-2026-6460 and many more.  

M doc/src/sgml/release-18.sgml

Update .abi-compliance-history for attribute versioning changes.

commit   : 412297476510be7a769e27e38f619110e4011904    
  
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    

Click here for diff

Security: CVE-2026-6460  
Backpatch-through: 14-18  

M .abi-compliance-history

Work around broken TimescaleDB code

commit   : edcc027ee19c57ebd7ec6d153c9c28344628e6fe    
  
author   : Andres Freund <andres@anarazel.de>    
date     : Mon, 9 Nov 2026 06:20:44 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:44 -0800    

Click here for diff

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   : 30d81a0bae6ea5945aedbf4a17e822eb3cc9b3db    
  
author   : Andres Freund <andres@anarazel.de>    
date     : Mon, 9 Nov 2026 06:20:44 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:44 -0800    

Click here for diff

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   : 5f0add5cf1c6e1c8eda2120598878ec3796cc34a    
  
author   : Andres Freund <andres@anarazel.de>    
date     : Mon, 9 Nov 2026 06:20:44 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:44 -0800    

Click here for diff

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   : cfdd09589b9f19fbc78c244e7868f1af46c460b8    
  
author   : Andres Freund <andres@anarazel.de>    
date     : Mon, 9 Nov 2026 06:20:44 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:44 -0800    

Click here for diff

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   : d835dc88d8d60816f2261128326673b11d52f0c1    
  
author   : Andres Freund <andres@anarazel.de>    
date     : Mon, 9 Nov 2026 06:20:44 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:44 -0800    

Click here for diff

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   : edcd260d9e31de73d0f3db572a9e3ce66d9fcf9e    
  
author   : Andres Freund <andres@anarazel.de>    
date     : Mon, 9 Nov 2026 06:20:44 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:44 -0800    

Click here for diff

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   : 391884d5c9117528b7466f5b4dd3a702b3034c60    
  
author   : Andres Freund <andres@anarazel.de>    
date     : Mon, 9 Nov 2026 06:20:44 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:44 -0800    

Click here for diff

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

commit   : 8b1a2a8ac21ac7ac735baa1361e86fd9ad24a2d0    
  
author   : Andres Freund <andres@anarazel.de>    
date     : Mon, 9 Nov 2026 06:20:44 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:44 -0800    

Click here for diff

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   : da2cb60900a5a6777aac7979769e917174f350c0    
  
author   : Andres Freund <andres@anarazel.de>    
date     : Mon, 9 Nov 2026 06:20:44 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:44 -0800    

Click here for diff

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   : 7c32010cacb1e4e5ec95a6982da9a0ff48329ebe    
  
author   : Andres Freund <andres@anarazel.de>    
date     : Mon, 9 Nov 2026 06:20:44 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:44 -0800    

Click here for diff

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   : dcb2ff56dafe67b6b79007641c3b65e41dd92a6c    
  
author   : Andres Freund <andres@anarazel.de>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    

Click here for diff

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   : 7bc2d46d11fd9697519db93884546777c4e7e6ba    
  
author   : Andres Freund <andres@anarazel.de>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    

Click here for diff

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   : f1b226dee1cbbc74b5df45185a5e94a547a38e4a    
  
author   : Álvaro Herrera <alvherre@kurilemu.de>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    

Click here for diff

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   : 4fb349500a20105ef476c524efea95e7b1c03a66    
  
author   : Robert Haas <rhaas@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    

Click here for diff

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   : ea228688e252b5d8767cc1bded8e327abb0f3abf    
  
author   : Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    

Click here for diff

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   : 983bcb768a9ae0fe16ef491b53176bf3e2d06866    
  
author   : Jeff Davis <jdavis@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    

Click here for diff

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   : a244ef271563a5fd751e94e1654958b803883637    
  
author   : Peter Geoghegan <pg@bowt.ie>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    

Click here for diff

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   : 317b7bcab012778c9fdab1d87b5315aba5e37364    
  
author   : Álvaro Herrera <alvherre@kurilemu.de>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    

Click here for diff

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   : ff961cbbf3af286665209b8039df3565ade198ae    
  
author   : Peter Geoghegan <pg@bowt.ie>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    

Click here for diff

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   : 6b1ca1333b346926515e62ae361781028b222f7d    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    

Click here for diff

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   : 2d91c1c78aea39285c85fae29c5737a4106eeaf0    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    

Click here for diff

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   : 35d851811f9f2ebb8d88baa27964bce8fb98ef40    
  
author   : Peter Geoghegan <pg@bowt.ie>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    

Click here for diff

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   : 780809ba636cb1b83c39c9d6265ffec72c5653b3    
  
author   : Jacob Champion <jchampion@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    

Click here for diff

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/dblink/expected/dblink.out
M contrib/dblink/sql/dblink.sql
M contrib/postgres_fdw/connection.c
M contrib/postgres_fdw/expected/postgres_fdw.out
M contrib/postgres_fdw/sql/postgres_fdw.sql
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/bin/pg_upgrade/Makefile
M src/bin/pg_upgrade/t/002_pg_upgrade.pl
M src/include/libpq/libpq-be-fe-helpers.h
M src/test/recovery/t/040_standby_failover_slots_sync.pl
M src/test/regress/expected/subscription.out
M src/test/regress/sql/subscription.sql
M src/test/subscription/t/027_nosuperuser.pl

Check ON CONFLICT target row against RLS policies before user quals.

commit   : a748ed909271ac58f09e3df0915adf487170a6ae    
  
author   : Peter Geoghegan <pg@bowt.ie>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    

Click here for diff

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   : ff6d9deca78688b211b88329d9ac484df726b441    
  
author   : Heikki Linnakangas <heikki.linnakangas@iki.fi>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    

Click here for diff

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   : fdbb7ffbb555b714d299e0d88c22b360e9034321    
  
author   : Heikki Linnakangas <heikki.linnakangas@iki.fi>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    

Click here for diff

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   : 26ff7cbd370e568734abe54b65a253058f979c55    
  
author   : Heikki Linnakangas <heikki.linnakangas@iki.fi>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    

Click here for diff

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   : 5db4823d00709ad99d8fc18bc1055ad2305a6b08    
  
author   : Heikki Linnakangas <heikki.linnakangas@iki.fi>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    

Click here for diff

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   : e72f4e91cefd09ccdc4d9f3d26b40049dd293c6d    
  
author   : Heikki Linnakangas <heikki.linnakangas@iki.fi>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    

Click here for diff

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   : 67d89bb2202df376e680e6c43d15b82dbaf92044    
  
author   : Heikki Linnakangas <heikki.linnakangas@iki.fi>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    

Click here for diff

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   : d48d28c4b68717cf2c968345444a004af7d7bee5    
  
author   : Heikki Linnakangas <heikki.linnakangas@iki.fi>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    

Click here for diff

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_stored.out
M src/test/regress/expected/generated_virtual.out
M src/test/regress/sql/alter_column_type.sql
M src/test/regress/sql/generated_stored.sql
M src/test/regress/sql/generated_virtual.sql

Check for Consts in domain DEFAULTs

commit   : 05fc87f95346caedca5a9e7ee59293a77f0e60c0    
  
author   : Heikki Linnakangas <heikki.linnakangas@iki.fi>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    

Click here for diff

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   : bb66a4e8264a56f58977b6cc1f57d34aac1cb3c0    
  
author   : Heikki Linnakangas <heikki.linnakangas@iki.fi>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:43 -0800    

Click here for diff

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   : bcc6924935f8bd228e1f9b8714c1a050e3b50dc8    
  
author   : Heikki Linnakangas <heikki.linnakangas@iki.fi>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    

Click here for diff

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   : 1bb9942e205c9021b714f07de30b9662666d72de    
  
author   : Heikki Linnakangas <heikki.linnakangas@iki.fi>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    

Click here for diff

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   : 9ddd897b30d32ba4c328cbdccd3486296df4f0b1    
  
author   : Heikki Linnakangas <heikki.linnakangas@iki.fi>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    

Click here for diff

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   : 9ef1cd650a6bcfa2ea5f64aa23b3a079a6d6cca3    
  
author   : Heikki Linnakangas <heikki.linnakangas@iki.fi>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    

Click here for diff

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   : 692f4b3c49562435b058ebfe12e08ebee5617cd3    
  
author   : Heikki Linnakangas <heikki.linnakangas@iki.fi>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    

Click here for diff

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   : afaff70f0fbe5025169c213a696dbe5468747fa1    
  
author   : Heikki Linnakangas <heikki.linnakangas@iki.fi>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    

Click here for diff

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

commit   : b8fbeeb7600778d05bbbe1e7ee81e4491b2116e6    
  
author   : Masahiko Sawada <msawada@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    

Click here for diff

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   : 921b93f250f65c730c2d21b71d7535074f79663e    
  
author   : Masahiko Sawada <msawada@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    

Click here for diff

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   : cc2d1a4c981bd83f2ba54c540c9e2bb9832dbb18    
  
author   : Masahiko Sawada <msawada@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    

Click here for diff

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   : 74a999de18d442a3fecf752dceff2144110e75c4    
  
author   : Masahiko Sawada <msawada@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    

Click here for diff

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

Replace typcache.c's buggy in_progress_list.

commit   : 2950cc7897a2c10f080f308dbef3b80665c6fd71    
  
author   : Robert Haas <rhaas@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    

Click here for diff

Commit b85a9d046efdd27775cbe7db9e92aad96aab4ada added this mechanism  
as a response to bugs reported when the same feature was previously  
committed as c14d4acb81348a34497b53a19dce2924cc4f6551. Unfortunately,  
it turns out to have multiple bugs. AtEOSubXact_TypeCache() cleared  
the entire list, instead of only the entries added at or below  
the current subtransaction level. Both AtEOXact_TypeCache() and  
AtEOSubXact_TypeCache() perform allocations in the abort path,  
which risks a second error before we've finished recovering from  
the first. Also, because in_progress_list was used to defer adding  
entries to RelIdToTypeIdCacheHash, it introduced a risk of missing  
invalidations that occurred after lookup_type_cache() performed  
the pg_type lookup and before the RelIdToTypeIdCacheHash update.  
Some of these problems are security vulnerabilities.  
  
Removing in_progress_list fixes all of these problems, but risks  
reintroducing the problems that in_progress_list was meant to fix.  
Specifically, we have to make sure that an invalidation processed  
while lookup_type_cache() is executing can't remove the associated  
RelIdToTypeIdCacheHash entry before the TypeCacheEntry is even  
returned to the caller. This commit solves that problem by never  
removing entries from that hash table under any circumstances.  
Instead, we now tolerate stale hash table entries, and check  
whether any given entry is still valid before relying on it. See  
the comments for further details.  
  
Reported-by: Peter Geoghegan <pg@bowt.ie>  
Reviewed-by: Michael Paquier <michael@paquier.xyz>  
Reviewed-by: Nathan Bossart <nathandbossart@gmail.com>  
Security: CVE-2026-120144  
Backpatch-through: 18  

M src/backend/utils/cache/typcache.c

Make \restrict keys protective in pg_dump --format=tar restore.sql.

commit   : 17e57d06cd3a3966143502626a2f5c7534f8309c    
  
author   : Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    

Click here for diff

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   : d3c9f8c61cce1827579fda3324aecdcc3b1e5ba2    
  
author   : Nathan Bossart <nathan@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    

Click here for diff

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   : 1dfde5b374b0b3554f3370913391b8d22ff81d29    
  
author   : Nathan Bossart <nathan@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    

Click here for diff

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   : 1e0a55a0fb26e13a6f0b1da87dea06957780b9e4    
  
author   : Nathan Bossart <nathan@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    

Click here for diff

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

pg_dump: Quote not-null constraint names.

commit   : 9e4cde7927da3603a2b6ca0d4e75d50b28888252    
  
author   : Nathan Bossart <nathan@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    

Click here for diff

When a child table's column is inherited from its parent but the  
child has its own not-null constraint on it, pg_dump emits that  
constraint as a standalone table constraint in the child's CREATE  
TABLE, and it prints the constraint name unquoted.  A name needing  
quotes therefore produces broken SQL (or, for a mixed-case name, a  
constraint restored under the wrong name), and a name chosen by  
the table's owner can inject arbitrary commands into the dump,  
which are then run with the privileges of whoever restores it.  
  
To fix, pass the name through fmtId().  Since fmtId() hands back  
a pointer into a single shared buffer, the constraint name and  
the column name can't be formatted in one appendPQExpBuffer()  
call, so the CONSTRAINT clause is printed separately.  
  
Reported-by: Anthropic OSS program  
Reported-by: Sarath Kumar <sarath@iitmpravartak.net>  
Reported-by: Nikolay Samokhvalov <nik@postgres.ai>  
Author: Anthropic OSS program  
Reviewed-by: Nathan Bossart <nathandbossart@gmail.com>  
Reviewed-by: Sami Imseih <samimseih.pg@gmail.com>  
Security: CVE-2026-116318  
Backpatch-through: 18  

M src/bin/pg_dump/pg_dump.c
M src/bin/pg_dump/t/002_pg_dump.pl

Prevent creating types in pg_temp from restricted code.

commit   : 448db14518504c789554f2e1be5a1efa6a9b155c    
  
author   : Nathan Bossart <nathan@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    

Click here for diff

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   : 673ef2341e3f6263f301c8f0b0f5c935bfea773a    
  
author   : Nathan Bossart <nathan@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    

Click here for diff

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

vacuumdb: Fully qualify the catalog query's operators.

commit   : e10d38d4715ecb75b274546f0c5917de2fe14829    
  
author   : Nathan Bossart <nathan@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    

Click here for diff

vacuumdb runs its catalog query with the session's default  
search_path so that --table arguments resolve as they would in  
psql, but commit edba754f05 added two constructs whose = is not  
schema-qualified: the relkind IN list in the LATERAL and the IS  
DISTINCT FROM tests on attstattarget and stxstattarget.  This  
allows a database owner to shadow the operator with their own and  
execute arbitrary code as the user running vacuumdb.  To fix,  
rewrite the former as OPERATOR(pg_catalog.=) ANY (array[...]) like  
the relkind filter in the WHERE clause, and the latter as an IS  
NULL test with OPERATOR(pg_catalog.<>).  
  
Reported-by: Noah Misch <noah@leadboat.com>  
Reported-by: Anthropic OSS program  
Author: Nathan Bossart <nathandbossart@gmail.com>  
Co-authored-by: Anthropic OSS program  
Reviewed-by: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>  
Security: CVE-2026-116313  
Backpatch-through: 18  

M src/bin/scripts/vacuumdb.c

Check table ownership when DROP OWNED BY changes policies.

commit   : 7f30f607a2579ea90c0d3ca2739a4a83eb7316ed    
  
author   : Nathan Bossart <nathan@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    

Click here for diff

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   : 8373a421558c05813ae6d428102892de38a934ea    
  
author   : Nathan Bossart <nathan@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:42 -0800    

Click here for diff

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   : d9b15fda5684b4a6feadf23b28fc4aa8812eb65a    
  
author   : Nathan Bossart <nathan@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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   : 3531f4b343d7cad7c24b17d5e4ed74f1721d2a86    
  
author   : Nathan Bossart <nathan@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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   : 8a2982de9f1a6f859d740b52cb97b457a29d009e    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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   : b552d6efb23e13a65906202bafb0e101a2355521    
  
author   : Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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   : 68ee5a81610e02be3a5c46063b089b5f5e20f2ec    
  
author   : Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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   : 061cc0ee6f017ba6b937c61a50c91a758b9a95a8    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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   : 165bbc265631f937b6396df3bfcac7e6520618e3    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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

Keep the relation open while importing its statistics.

commit   : 780900dcfab7652f510ec60059cf8dbb51bfd7f5    
  
author   : Masahiko Sawada <msawada@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

The attribute statistics import function locked the target relation  
but did not keep it open while converting its input values.  
Converting a value can run user-defined code, such as a domain's CHECK  
constraint, which can alter the relation in the same transaction.  The  
lock alone does not prevent that.  The import could then store  
statistics that no longer match the column, which can crash the  
backend when the planner later reads them.  
  
Keep the relation open across the conversion, so that such a reentrant  
ALTER TABLE is rejected by CheckTableNotInUse() as it is elsewhere.  
  
Also, the same code can drop the target column itself, which doesn't  
go through that check.  The import would then store a pg_statistic  
row for a column that no longer exists.  That only leaves a stale  
catalog row behind, but it's wrong all the same, so re-check that the  
column still exists before writing, and error out if it doesn't.  The  
column must not go away while we restore its statistics, and raising  
an error also rolls back the drop.  
  
The extended statistics import function, added in v19, has the same  
hazard and is fixed in the same way.  There the same code can also  
drop the statistics object being imported, which would leave a  
pg_statistic_ext_data row with no owner.  A statistics object created  
later with the same OID would pick that row up as its own, so we  
re-check that as well.  Branches before v19 have no such function.  
  
Reported-by: Mehmet D. INCE <mehmet@mehmetince.net>  
Author: Masahiko Sawada <sawada.mshk@gmail.com>  
Reviewed-by: Michael Paquier <michael@paquier.xyz>  
Reviewed-by: Andres Freund <andres@anarazel.de>  
Reviewed-by: Heikki Linnakangas <hlinnaka@iki.fi>  
Backpatch-through: 18  
Security: CVE-2026-116329  

M src/backend/statistics/attribute_stats.c
M src/test/regress/expected/stats_import.out
M src/test/regress/sql/stats_import.sql

plperl: Refuse to free a prepared query that is being executed.

commit   : 250405afb76579e0691267471c196bb9ea3b3e0d    
  
author   : Masahiko Sawada <msawada@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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   : f64f711882f8042a6fe6512b2cc0c949db771242    
  
author   : Heikki Linnakangas <heikki.linnakangas@iki.fi>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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   : 861b5a86952a29607c478118c9c65d95ae9ae4ad    
  
author   : Robert Haas <rhaas@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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   : 2c03c7c48fb6060155fd9d35fdcef40ddf0a28f6    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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   : 4ccea70a1817fe740bf15e9411947b88d737c61d    
  
author   : Tomas Vondra <tomas.vondra@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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   : 53a34a4defaa42c9d7013fbff491b78f41b5f18b    
  
author   : Heikki Linnakangas <heikki.linnakangas@iki.fi>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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   : aac5e6f5038a4d3b9a6d71b30e57fd9c9436033c    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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

Dispatch pre-auth cancel requests until _exit(1) handlers are disarmed

commit   : e1cc4a89318d919d3875443fd63704634aad21d4    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

BackendInitialize() collects the startup packet with two facilities able  
to exit() early:  
- StartupPacketTimeoutHandler(), timeout enabled for the startup  
process, via StartupPacketTimeoutHandler()  
- process_startup_packet_die(), for SIGTERM  
  
Both assume that shared memory is never touched, meaning that a hard  
exit() is fine, as no cleanup action need to be taken.  
  
9d9b9d46f3c5 broke this assumption by making the cancel request handling  
happen earlier in the pre-authentication process: SendCancelRequest()  
can acquire the spinlock "pss_mutex" to compare cancel keys.  If SIGALRM  
(authentication_timeout) or SIGTERM is received while "pss_mutex" is  
acquired, the process would exit with a spinlock still held.  The next  
process touching the same proc slot as the one that has exited earlier  
would wait indefinitely for the spin lock, crashing the server on PANIC  
after one minute of wait.  
  
This commit changes the cancel request handling so as a request received  
is stashed instead of immediately processed.  Its processing is delayed  
once it is safe to do so, where the startup timeout and SIGTERM handler  
are cleared.  
  
Reported-by: Anthropic OSS program  
Author: Michael Paquier <michael@paquier.xyz>  
Reviewed-by: Heikki Linnakangas <hlinnaka@iki.fi>  
Backpatch-through: 18  
Security: CVE-2026-116322  

M src/backend/tcop/backend_startup.c
M src/tools/pgindent/typedefs.list

Treat application of a leaky function to CaseTestExpr/Param as leaky.

commit   : 9a96032e100a9833534dae226d5aad0f693006c3    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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   : 3ce3d94c0da1a9ad512b743f85228e69883940ec    
  
author   : Andrew Dunstan <andrew@dunslane.net>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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   : 343663966f830427033b51c8703e64e3a2446c4e    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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   : 5215e9d6a13955c949bfe07b643927b733bd0033    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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   : 8d308caee3a53aae1ebcb2cb64f72cce108239c1    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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   : bbe476c747f31e521dbdc395fc6f16db96fc680d    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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   : f01727316219146fd54cc6448c540a5a50550c29    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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   : b3ef92518c57d8db63ac18b4adcf585da90e0673    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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   : 3e97c9a1fa19fb995e57a1a093cb7cee3f6da605    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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   : 9c2cadacd61562ba7835179209c2aa9482f61d48    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:41 -0800    

Click here for diff

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   : 8d9f203b81483e30ff71daaa57ba0038e0568b77    
  
author   : Masahiko Sawada <msawada@postgresql.org>    
date     : Mon, 9 Nov 2026 06:20:40 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:40 -0800    

Click here for diff

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 contrib/file_fdw/expected/file_fdw.out
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   : 030b28a8d1de21501fdad1e1ff2337a7ddc28a54    
  
author   : Andrew Dunstan <andrew@dunslane.net>    
date     : Mon, 9 Nov 2026 06:20:40 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:40 -0800    

Click here for diff

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   : af250ef26280d49ef301c9c64b0d4740180c8821    
  
author   : Peter Eisentraut <peter@eisentraut.org>    
date     : Mon, 9 Nov 2026 06:20:40 -0800    
  
committer: Noah Misch <noah@leadboat.com>    
date     : Mon, 9 Nov 2026 06:20:40 -0800    

Click here for diff

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   : 2134cfde5cf674a5e77debefdf60a05a07d70c5e    
  
author   : Peter Eisentraut <peter@eisentraut.org>    
date     : Mon, 9 Nov 2026 12:17:00 +0100    
  
committer: Peter Eisentraut <peter@eisentraut.org>    
date     : Mon, 9 Nov 2026 12:17:00 +0100    

Click here for diff

Source-Git-URL: https://git.postgresql.org/git/pgtranslation/messages.git  
Source-Git-Hash: 6cb5a00fa45d31470d756277abff083a5437b486  

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/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/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   : a1635642e81125a5e7b8a2e717b9c4dd2f66896b    
  
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    

Click here for diff

M doc/src/sgml/release-18.sgml

Fix more issues with extended statistics on expressions

commit   : 4bcd0d31c6878b370cac8c738a35bb7bc5db22c5    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Fri, 30 Oct 2026 17:29:35 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Fri, 30 Oct 2026 17:29:35 +0900    

Click here for diff

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

Exclude the variables in llvmjit_types.c from AddressSanitizer

commit   : 1da6ddbdc32c5bf640429828bc0353ffcb40628e    
  
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    

Click here for diff

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

Doc: tweak v18 to avoid "exceed the available area" PDF problems.

commit   : de2793682d98e8fe458f192b1a0e48e9fd623b40    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Fri, 6 Nov 2026 16:32:03 -0500    
  
committer: Tom Lane <tgl@sss.pgh.pa.us>    
date     : Fri, 6 Nov 2026 16:32:03 -0500    

Click here for diff

It's quite unclear to me why these two examples are overrunning the  
right margin in the v18 docs, when the identical examples are fine in  
other branches.  Nonetheless, the buildfarm (member alabio) is showing  
the same results I see locally, so it's real, and I lack the time  
right now to dig any deeper.  Throw a &zwsp into one example to allow  
a line break, and adjust the sample UUID in the other one so that FOP  
thinks it's a breakable word.  

M doc/src/sgml/datatype.sgml
M doc/src/sgml/func.sgml

Minor updates for back-branch release notes.

commit   : c58a86e1d312cc75d6ff6c0f045463139d6db6f5    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Fri, 6 Nov 2026 16:17:01 -0500    
  
committer: Tom Lane <tgl@sss.pgh.pa.us>    
date     : Fri, 6 Nov 2026 16:17:01 -0500    

Click here for diff

Improve entry for commits 79b101486 et al. based on comments  
from Peter Geoghegan.  
  
ASCII-ify Ante Krešić's name, because our PDF docs toolchain  
can't cope with "ć".  

M doc/src/sgml/release-18.sgml

doc: Clarify that OAuth bearer tokens are sent in clear text

commit   : 5e41e193f3f401109522eda1dd90d6e2c873bbd7    
  
author   : Daniel Gustafsson <dgustafsson@postgresql.org>    
date     : Fri, 6 Nov 2026 21:56:10 +0100    
  
committer: Daniel Gustafsson <dgustafsson@postgresql.org>    
date     : Fri, 6 Nov 2026 21:56:10 +0100    

Click here for diff

The bearer token used by OAUTHBEARER authentication is transmitted in  
clear text over the network, which makes it vulnerable to sniffing and  
similar attacks.  This adds a note that SSL connections are recommended  
for OAuth authentication.  
  
Backpatch down to v18 where OAuth was introduced.  
  
Author: Zsolt Parragi <zsolt.parragi@percona.com>  
Co-authored-by: Daniel Gustafsson <daniel@yesql.se>  
Discussion: https://postgr.es/m/CAN4CZFNy23wWKF+78QG3cfunjJpQWFc7eNP7qcYPB6Sg-NhTQQ@mail.gmail.com  
Backpatch-through: 18  

M doc/src/sgml/client-auth.sgml
M doc/src/sgml/protocol.sgml

First-draft release notes for 18.7.

commit   : 32973315d26b40225f592645c7526da7ab228531    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Fri, 6 Nov 2026 14:42:51 -0500    
  
committer: Tom Lane <tgl@sss.pgh.pa.us>    
date     : Fri, 6 Nov 2026 14:42:51 -0500    

Click here for diff

As usual, the release notes for other branches will be made by cutting  
these down, but put them up for community review first.  

M doc/src/sgml/release-18.sgml

file_fdw: Fix ANALYZE crashes with DEFAULT markers

commit   : 813a74bc5a76f13c15da902f5523d0fa755e2c5f    
  
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    

Click here for diff

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   : c7656ac9e652534d2ec7603bfe0947eec6c7fc22    
  
author   : Amit Langote <amitlan@postgresql.org>    
date     : Fri, 6 Nov 2026 16:18:36 +0900    
  
committer: Amit Langote <amitlan@postgresql.org>    
date     : Fri, 6 Nov 2026 16:18:36 +0900    

Click here for diff

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   : 744ca40cbe2cac09124cc8b560d01e71e8c1f8b8    
  
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    

Click here for diff

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   : 9b71ffa061d77af7302062df22099566fc6e16ae    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Fri, 6 Nov 2026 10:45:09 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Fri, 6 Nov 2026 10:45:09 +0900    

Click here for diff

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   : 2a5e2d2867aeb1b4989678481c5de523a22fc1cb    
  
author   : Jacob Champion <jchampion@postgresql.org>    
date     : Thu, 5 Nov 2026 16:53:48 -0800    
  
committer: Jacob Champion <jchampion@postgresql.org>    
date     : Thu, 5 Nov 2026 16:53:48 -0800    

Click here for diff

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   : e2b3549a4103efeb2fb35daef770427fa8264a17    
  
author   : Jacob Champion <jchampion@postgresql.org>    
date     : Thu, 5 Nov 2026 16:53:48 -0800    
  
committer: Jacob Champion <jchampion@postgresql.org>    
date     : Thu, 5 Nov 2026 16:53:48 -0800    

Click here for diff

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   : c88c71a4b52a514cf9e9582c2d657e0fc6d4ce97    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Fri, 6 Nov 2026 09:18:40 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Fri, 6 Nov 2026 09:18:40 +0900    

Click here for diff

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

Fix oauth validator test on EXEC_BACKEND

commit   : dcf112bd4e4cfe1651fafe169dffdaf076bd0b43    
  
author   : Daniel Gustafsson <dgustafsson@postgresql.org>    
date     : Fri, 6 Nov 2026 00:49:07 +0100    
  
committer: Daniel Gustafsson <dgustafsson@postgresql.org>    
date     : Fri, 6 Nov 2026 00:49:07 +0100    

Click here for diff

Commit 8e54a414570010af included a test for invalidated oauth validator  
with a SKIP stanza for EXEC_BACKEND Windows builds, but failed to handle  
non-Windows EXEC_BACKEND builds.  Since the debug GUC for retrieveing the  
EXEC_BACKEND state is only available since, we add an alternative output  
to capture the failure on EXEC_BACKEND.  Per buildfarm member culicidae.  
  
Author: Daniel Gustafsson <daniel@yesql.se>  
Reported-by: Jacob Champion <jacob.champion@enterprisedb.com>  
Discussion: https://postgr.es/m/C6EE5C79-AC29-4F3D-B0BD-B07E9298051A@yesql.se  

M src/test/modules/oauth_validator/t/001_server.pl

oauth: Reject quoted empty string in validator config

commit   : e83298599633528e749887b17f74fe933c552154    
  
author   : Daniel Gustafsson <dgustafsson@postgresql.org>    
date     : Thu, 5 Nov 2026 21:43:55 +0100    
  
committer: Daniel Gustafsson <dgustafsson@postgresql.org>    
date     : Thu, 5 Nov 2026 21:43:55 +0100    

Click here for diff

Commit bca67e5a3 added checking for blank validator configuration, this  
completes that work by checking for a quoted empty string as well.  
  
Backpatch down to v18 like bca67e5a3.  
  
Author: Zsolt Parragi <zsolt.parragi@percona.com>  
Reviewed-by: Daniel Gustafsson <daniel@yesql.se>  
Discussion: https://postgr.es/m/CAN4CZFP7ZMLB5Q1P4H__2d54om6fO+OEBZpWLpMwhFipobrEEw@mail.gmail.com  
Backpatch-through: 18  

M src/backend/libpq/auth-oauth.c
M src/test/modules/oauth_validator/t/001_server.pl

Handle numeric overflow when handling range values

commit   : 8a043da94728821e1dfc6758cd883bafb57bfac7    
  
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    

Click here for diff

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

oauth: Recheck oauth_validator_libraries during authentication

commit   : 8e54a414570010afed0b8b1ed10882afba380166    
  
author   : Daniel Gustafsson <dgustafsson@postgresql.org>    
date     : Thu, 5 Nov 2026 14:14:01 +0100    
  
committer: Daniel Gustafsson <dgustafsson@postgresql.org>    
date     : Thu, 5 Nov 2026 14:14:01 +0100    

Click here for diff

The validator allowlist is only enforced while parsing pg_hba.conf.  
If changing oauth_validator_libraries makes an existing oauth line  
invalid, the HBA reload fails and the old lines stay in use, so a  
removed validator keeps authenticating users.  In particular, when  
setting the list to the empty string, the server doesn't refuse an  
OAuth connection, as documented.  
  
Backpatch down to v18 where OAuth was introduced.  
  
Author: Zsolt Parragi <zsolt.parragi@percona.com>  
Reviewed-by: Daniel Gustafsson <daniel@yesql.se>  
Reviewed-by: shihao zhong <zhong950419@gmail.com>  
Discussion: https://postgr.es/m/CAN4CZFNbWXwC=s4t9Xu1kitLsQvTe-EwpVtFCjixWCHBxRTraQ@mail.gmail.com  
Backpatch-through: 18  

M src/backend/libpq/auth-oauth.c
M src/test/modules/oauth_validator/t/001_server.pl

Fix stale visibility maps in incremental backups after heap truncation

commit   : 41b4b8e8bf1a07ce4fbfa86b8e544299e1a706cf    
  
author   : Fujii Masao <fujii@postgresql.org>    
date     : Thu, 5 Nov 2026 18:06:25 +0900    
  
committer: Fujii Masao <fujii@postgresql.org>    
date     : Thu, 5 Nov 2026 18:06:25 +0900    

Click here for diff

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

Fix io_uring resource leaks across crash restarts

commit   : 5bfe88cf6a814e24ac9c7332142a75d11a2e5461    
  
author   : Fujii Masao <fujii@postgresql.org>    
date     : Thu, 5 Nov 2026 18:03:44 +0900    
  
committer: Fujii Masao <fujii@postgresql.org>    
date     : Thu, 5 Nov 2026 18:03:44 +0900    

Click here for diff

With io_method=io_uring and restart_after_crash enabled, the postmaster  
reinitializes shared memory after a backend crash. It created new  
io_uring instances without releasing its references to the old ones,  
causing resources to accumulate on each crash restart. Eventually,  
creating new instances could fail with "Too many open files" or  
"Cannot allocate memory", preventing the server from restarting.  
  
Fix this by registering an on_shmem_exit() callback to release the  
postmaster's references to the io_uring instances during shared  
memory cleanup, before creating new ones. This prevents resources  
from accumulating across crash restarts.  
  
Backpatch to v18, where io_method=io_uring was introduced.  
  
Reported-by: Lucas DRAESCHER <git@draescher.fr>  
Author: Lucas DRAESCHER <git@draescher.fr>  
Reviewed-by: ChangAo Chen <cca5507@qq.com>  
Reviewed-by: Ilmar Yunusov <tanswis42@gmail.com>  
Reviewed-by: Laetitia Avrot <laetitia.avrot@gmail.com>  
Reviewed-by: Jakub Wartak <jakub.wartak@enterprisedb.com>  
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>  
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>  
Discussion: https://postgr.es/m/zHN8Rb7syWYDBPUhCICxKLr_tQmFLg0T024irxVVb6RSSicZcqcA0eT73z2PQy7JmzzqnUzX0Hi3P2dPKRvmNx_jbAqSCLyqJvWjXEypyAI=@draescher.fr  
Backpatch-through: 18  

M src/backend/storage/aio/method_io_uring.c

SQL/JSON: Fix NULL result leakage in JSON_VALUE() RETURNING json/jsonb

commit   : 7150d0fd494166107e6efcc7e786690fd76eff08    
  
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    

Click here for diff

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

Avoid resetting subscription two_phase state to pending when already enabled.

commit   : fbaebf450d444782d25fa5a25b7bf0fb41403abc    
  
author   : Amit Kapila <akapila@postgresql.org>    
date     : Thu, 5 Nov 2026 09:38:18 +0530    
  
committer: Amit Kapila <akapila@postgresql.org>    
date     : Thu, 5 Nov 2026 09:38:18 +0530    

Click here for diff

ALTER SUBSCRIPTION ... SET (two_phase = true) always moved the  
subscription's two_phase state to pending, even if two_phase was already  
enabled. The publisher's slot still has two_phase enabled in that case,  
and prepared transactions may already exist on the subscriber, so the  
reset broke checks that rely on the enabled state:  
  
1. A later SET (two_phase = false) no longer checked for existing prepared  
transactions and disabled two_phase anyway. When those transactions were  
committed on the publisher, their changes were re-sent as regular  
transactions instead of COMMIT PREPARED, leading to self-deadlocks,  
duplicate data, or orphaned prepared transactions.  
  
2. REFRESH PUBLICATION (and SET/ADD PUBLICATION) with copy_data = true was  
allowed even though the publisher's slot still had two_phase enabled. This  
could stream prepared transactions during the initial sync of newly added  
tables and apply them as empty prepares.  
  
Fix this by leaving the state unchanged when two_phase is already enabled.  
  
This problem was introduced by commit 1462aad2e44, which allowed altering  
the two_phase option of a subscription.  
  
This changes behavior when slot_name is altered together with SET  
(two_phase = true) on an ENABLED subscription. Previously the state was  
reset to PENDING and the apply worker enabled two_phase on the replacement  
slot. Now the state stays ENABLED and the apply worker does not touch the  
replacement slot's two_phase property. This is intentional: resetting the  
state would reintroduce the problems described above, since prepared  
transactions from the old slot may still exist on the subscriber. The  
documentation already requires the replacement slot's two_phase property  
to match the subscription option.  
  
Author: Zhijie Hou <houzhijie22@gmail.com>  
Reviewed-by: shveta malik <shveta.malik@gmail.com>  
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>  
Reviewed-by: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>  
Reviewed-by: vignesh C <vignesh21@gmail.com>  
Reviewed-by: Peter Smith <smithpb2250@gmail.com>  
Reviewed-by: Amit Kapila <amit.kapila16@gmail.com>  
Discussion: https://postgr.es/m/8e85abdf-738b-4578-ac42-4dc8eaa061dd@gmail.com  
Backpatch-through: 18  

M src/backend/commands/subscriptioncmds.c
M src/test/subscription/t/021_twophase.pl

SQL/JSON: Fix ON EMPTY/ERROR state leakage on NULL input

commit   : 52033b8871b8ff3c5fe34ffbdef1db38f46586b8    
  
author   : Amit Langote <amitlan@postgresql.org>    
date     : Thu, 5 Nov 2026 08:37:55 +0900    
  
committer: Amit Langote <amitlan@postgresql.org>    
date     : Thu, 5 Nov 2026 08:37:55 +0900    

Click here for diff

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

commit   : fa0806647f5ffeb3c4536e218b8f2f4161ce3477    
  
author   : Amit Langote <amitlan@postgresql.org>    
date     : Thu, 5 Nov 2026 08:16:26 +0900    
  
committer: Amit Langote <amitlan@postgresql.org>    
date     : Thu, 5 Nov 2026 08:16:26 +0900    

Click here for diff

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   : bfbb3d84209f60698f327ad54176240d27e5ee2a    
  
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    

Click here for diff

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   : 558c00f9676e14ad8bad3d3db9c41cf443ba2854    
  
author   : Dean Rasheed <dean.a.rasheed@gmail.com>    
date     : Wed, 4 Nov 2026 19:45:41 +0000    
  
committer: Dean Rasheed <dean.a.rasheed@gmail.com>    
date     : Wed, 4 Nov 2026 19:45:41 +0000    

Click here for diff

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   : 9f09d70439311d8d05b3383c4f45ef391cd43634    
  
author   : Etsuro Fujita <efujita@postgresql.org>    
date     : Wed, 4 Nov 2026 18:30:02 +0900    
  
committer: Etsuro Fujita <efujita@postgresql.org>    
date     : Wed, 4 Nov 2026 18:30:02 +0900    

Click here for diff

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   : 9e9d24c33ada6fb2fbbc364969ff7aa57506ef32    
  
author   : Amit Langote <amitlan@postgresql.org>    
date     : Wed, 4 Nov 2026 14:50:13 +0900    
  
committer: Amit Langote <amitlan@postgresql.org>    
date     : Wed, 4 Nov 2026 14:50:13 +0900    

Click here for diff

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   : d6694976c88c038039c2b74d0aa4eaa4e083519c    
  
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    

Click here for diff

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   : 60ccd730da518037484ce96cf270b811a463b3b7    
  
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    

Click here for diff

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   : 7418d9e9605fe208e8f642a1c4789415f244dcc2    
  
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    

Click here for diff

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   : 9dd02753525ac83b5902bf4456b3bdb276f0271b    
  
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    

Click here for diff

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

Improve stability of authentication test 007_pre_auth

commit   : a02de147ab92dc3c7a4abc3648fcbfd10be94490    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Wed, 4 Nov 2026 08:38:45 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Wed, 4 Nov 2026 08:38:45 +0900    

Click here for diff

The injection point wait used by the test could be reached by autovacuum  
workers or the logical replication launcher, leading to sporadic  
failures.  
  
The configuration of the test is tweaked so as these kinds of processes  
are not spawned anymore, which should be enough to silence the problems.  
  
Reported-by: Alexander Lakhin <exclusion@gmail.com>  
Discussion: https://postgr.es/m/c984ed89-cac1-47a9-91fa-4d3287722203@gmail.com  
Backpatch-through: 18  

M src/test/authentication/t/007_pre_auth.pl

doc: Match built-in function parameter names to their SQL names

commit   : 862a20118cfaf85ccb159559f0cf4934b1a93fa2    
  
author   : Fujii Masao <fujii@postgresql.org>    
date     : Tue, 3 Nov 2026 15:03:06 +0900    
  
committer: Fujii Masao <fujii@postgresql.org>    
date     : Tue, 3 Nov 2026 15:03:06 +0900    

Click here for diff

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   : 3b859f1715143581c522baf30c83c82dc582d51b    
  
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    

Click here for diff

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

doc: Correct setting restrictions for vacuum_max_eager_freeze_failure_rate

commit   : aae8f955723ae83c989cb558c7a439ab68dded8d    
  
author   : Fujii Masao <fujii@postgresql.org>    
date     : Tue, 3 Nov 2026 14:54:02 +0900    
  
committer: Fujii Masao <fujii@postgresql.org>    
date     : Tue, 3 Nov 2026 14:54:02 +0900    

Click here for diff

The documentation incorrectly stated that this parameter could only  
be set in postgresql.conf or on the server command line. However, it  
is a PGC_USERSET parameter and can therefore be changed by any user  
within a session.  
  
Remove the incorrect restriction while retaining the note about the  
per-table storage parameter override.  
  
Backpatch to v18, where this parameter was introduced.  
  
Author: Afrah Razzak <mypg.afrah@gmail.com>  
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>  
Discussion: https://postgr.es/m/CAAJ6gzGeup6WPpraF1g6iWYOQsvLmhKk94acJ=-iq+Gg_x9W0w@mail.gmail.com  
Backpatch-through: 18  

M doc/src/sgml/config.sgml

Reject incremental backups based on a backup from a divergent history

commit   : 2afbad0cedfd4184bd13d470e7fdc13c94d1ab77    
  
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    

Click here for diff

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   : 092da6511b8ae74eb6895fad99399e33ea959d1c    
  
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    

Click here for diff

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   : 1ff9f9380d32b04de71569ccebf475324dd39aaf    
  
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    

Click here for diff

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   : 2bcf2fa49b27b19177cc082e0a952ad4816bb2ff    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Tue, 3 Nov 2026 09:48:38 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Tue, 3 Nov 2026 09:48:38 +0900    

Click here for diff

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   : 31538e165a91ac1f031d818dac2bd8ea83e3479c    
  
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    

Click here for diff

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   : 1be7010d1bae749ddeea46eb8398f14689b9a2c8    
  
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    

Click here for diff

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   : 5c8629bb58957ef552e22680cf20b5214f40328e    
  
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    

Click here for diff

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   : badcb62d62a676a9202b9a693c87166f55f743dc    
  
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    

Click here for diff

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   : 9d0081c01f9bc343df01d086690b3515ebd8b4ba    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Mon, 2 Nov 2026 15:40:50 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Mon, 2 Nov 2026 15:40:50 +0900    

Click here for diff

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   : 3a2fc766272da0621079a66747cd4503ef3ffe1a    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Mon, 2 Nov 2026 15:18:04 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Mon, 2 Nov 2026 15:18:04 +0900    

Click here for diff

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   : 5b730e81fbdfd36ed45473770676ab3f88c776db    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Mon, 2 Nov 2026 14:38:32 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Mon, 2 Nov 2026 14:38:32 +0900    

Click here for diff

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   : f10500777b2a4d3da6d8d1dd0602e542a4300410    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Sun, 1 Nov 2026 20:45:35 -0500    
  
committer: Tom Lane <tgl@sss.pgh.pa.us>    
date     : Sun, 1 Nov 2026 20:45:35 -0500    

Click here for diff

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   : dbe6cf5588da84ac7dd7268b3e95261475291f27    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Mon, 2 Nov 2026 10:30:52 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Mon, 2 Nov 2026 10:30:52 +0900    

Click here for diff

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   : 6437d97742a4bc533c280c6854f55619663c69d6    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Mon, 2 Nov 2026 08:12:38 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Mon, 2 Nov 2026 08:12:38 +0900    

Click here for diff

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

Fix failure in function cache entry hashing during validation mode.

commit   : a4bc92a82c4e362e5b1dad83f86e5e1d93a970c5    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Sun, 1 Nov 2026 17:26:05 -0500    
  
committer: Tom Lane <tgl@sss.pgh.pa.us>    
date     : Sun, 1 Nov 2026 17:26:05 -0500    

Click here for diff

When cached_function_compile() is called with forValidator = true,  
the passed FunctionCallInfo is one that the caller set up specially,  
and it's typically pretty incomplete.  compute_function_hashkey()  
was careless about this and would call get_call_result_type()  
if asked to, which would fail in any but the simplest cases.  
We'd managed not to notice in the pre-existing use-cases because  
plpgsql and functions.c don't pass includeResultType and forValidator  
both true at the same time; but plpython does, so it would reach this  
failure in cases such as polymorphic argument types.  plpython does  
not yet support polymorphic arguments, and has no test cases about  
them, so we did not see that the intended error message had degraded  
to an internal failure in get_call_result_type().  
  
To fix, just skip trying to fill the result type tupdesc when in  
validation mode.  This leads to building a hashtable entry that will  
never match any real calls, but that's true in a lot of other  
validation cases as well.  
  
The root cause here is lack of clarity about what we expect the  
passed FunctionCallInfo to contain in validation mode, so add  
comments specifying that.  Also add some plpython test cases showing  
the expected error reports.  
  
Backpatch to v18 where funccache.c was introduced.  This problem's  
not reachable in the core code in v18, since plpython didn't use  
funccache.c then.  But extensions could perhaps be using funccache.c  
as of v18, and they might hit it.  
  
Author: Tom Lane <tgl@sss.pgh.pa.us>  
Reviewed-by: shihao zhong <zhong950419@gmail.com>  
Discussion: https://postgr.es/m/2865288.1793403201@sss.pgh.pa.us  
Backpatch-through: 18  

M src/backend/utils/cache/funccache.c
M src/pl/plpython/expected/plpython_error.out
M src/pl/plpython/sql/plpython_error.sql

meson: Avoid PATH bloat from NLS .mo targets in tmp_install test

commit   : 82062cbb60f5b519a652a1b82958eccc4cc27d16    
  
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    

Click here for diff

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   : 9e20db542b0ff5fdcb839e9b440db337c7f439a5    
  
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    

Click here for diff

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   : 5787f8785cb70f3077faba9e7f8421b849c6c19c    
  
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    

Click here for diff

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   : d3a03158c9cadd4a431eafc3d5bd438aa6d63e89    
  
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    

Click here for diff

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   : 1cdfea37c268d41b47e1cf968103cb9866feec4d    
  
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    

Click here for diff

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   : 19a2002b5db959e095926d74d46be4b8aa7f20a2    
  
author   : Andres Freund <andres@anarazel.de>    
date     : Fri, 30 Oct 2026 16:20:52 -0400    
  
committer: Andres Freund <andres@anarazel.de>    
date     : Fri, 30 Oct 2026 16:20:52 -0400    

Click here for diff

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   : f9d7dee5f73c6a9a0a3d7e0ae6b7de10ebb0e9a2    
  
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    

Click here for diff

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   : 94d625f5d938e0a01cb80d85cc242ff8a64b3f54    
  
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    

Click here for diff

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   : ec65cbc00b2ccc82996e1c94b534e1fbf61fbcd1    
  
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    

Click here for diff

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   : 7ba657cba1303d2d6b89b13a5bdade500c09c88e    
  
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    

Click here for diff

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

Check all four box corners in SP-GiST quad tree <@ search

commit   : 03ba8e591543ab63d5eb230f03e024df54553b5d    
  
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    

Click here for diff

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   : e527e4a7665abc2f6aec4b19b5b0a677ec891dbd    
  
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    

Click here for diff

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

Fix nestloop parameter assignment for PHVs evaluated above outer joins

commit   : e84fb3f1c9c25867739580470bec48b19f18081d    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Fri, 30 Oct 2026 10:49:26 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Fri, 30 Oct 2026 10:49:26 +0900    

Click here for diff

A NestLoop can supply a PlaceHolderVar as a parameter if the PHV can  
be evaluated from the lefthand rels plus the required-outer rels of  
the join.  A PHV's ph_eval_at can include outer-join relids, but  
required-outer sets need not: create_lateral_join_info() keeps  
outer-join relids out of the lateral dependencies it derives from  
ph_lateral.  So when an outer join within ph_eval_at had been computed  
by an enclosing nestloop's outer side, the test in  
identify_current_nestloop_params() failed and no plan node claimed the  
parameter.  This resulted in "failed to assign all NestLoopParams to  
plan nodes", or, if an unrelated upper NestLoop happened to cover  
ph_eval_at and claimed the parameter, "plan should not reference  
subplan's variable".  
  
To fix, have create_nestloop_plan() also count the outer joins that  
have already been computed by the outer rels in scope.  
  
Back-patch to v18, where commit a16ef313f made it possible to compute  
a PHV that is used as a nestloop parameter in the outer side of the  
NestLoop.  v16 and v17 can fail the same way, since ph_eval_at has  
included outer-join relids since v16, but they lack that machinery.  
Fixing them would mean adding join-order restrictions to  
have_dangerous_phv() that could change plans that work today, which  
does not seem worth the risk for a failure this unlikely.  
  
Author: Richard Guo <guofenglinux@gmail.com>  
Discussion: https://postgr.es/m/CAMbWs4-fcAULTpWthZMHGOFK1J+Hz=Dvr1TO-M20cC317rOGOA@mail.gmail.com  
Backpatch-through: 18  

M src/backend/optimizer/plan/createplan.c
M src/test/regress/expected/join.out
M src/test/regress/sql/join.sql

Empty partial_pathlist when unsetting consider_parallel flag

commit   : cc4bf6e9acc9b9a385e37bd13bd55b5c941529cc    
  
author   : David Rowley <drowley@postgresql.org>    
date     : Thu, 29 Oct 2026 16:20:43 +1300    
  
committer: David Rowley <drowley@postgresql.org>    
date     : Thu, 29 Oct 2026 16:20:43 +1300    

Click here for diff

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   : 27a53c857394c43acc24b52018d3905189db78c6    
  
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    

Click here for diff

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   : 7ac2f74d1b3199bf575de9a97f3cf7dae8def745    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Thu, 29 Oct 2026 11:14:10 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Thu, 29 Oct 2026 11:14:10 +0900    

Click here for diff

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

commit   : 6cb715e2d6a1108977e4a12ae5e9831217793e55    
  
author   : Amit Langote <amitlan@postgresql.org>    
date     : Thu, 29 Oct 2026 10:38:34 +0900    
  
committer: Amit Langote <amitlan@postgresql.org>    
date     : Thu, 29 Oct 2026 10:38:34 +0900    

Click here for diff

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   : b9a4153603760a0685ca101ba6b32d83a61490e0    
  
author   : Amit Langote <amitlan@postgresql.org>    
date     : Thu, 29 Oct 2026 09:36:23 +0900    
  
committer: Amit Langote <amitlan@postgresql.org>    
date     : Thu, 29 Oct 2026 09:36:23 +0900    

Click here for diff

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   : 4f857f399efa9cd794bb02234691f1c10ea56712    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Thu, 29 Oct 2026 09:43:44 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Thu, 29 Oct 2026 09:43:44 +0900    

Click here for diff

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   : 5209a736a385513fff4be0d79eaaa7a3309458c9    
  
author   : Amit Langote <amitlan@postgresql.org>    
date     : Thu, 29 Oct 2026 09:14:13 +0900    
  
committer: Amit Langote <amitlan@postgresql.org>    
date     : Thu, 29 Oct 2026 09:14:13 +0900    

Click here for diff

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   : e4807a0f3f0373227de516edbdc6d8d350920c4c    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Wed, 28 Oct 2026 14:22:17 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Wed, 28 Oct 2026 14:22:17 +0900    

Click here for diff

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   : 8cddd5fc8fe80efccdae2fd4aa9b99d94d740f95    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Wed, 28 Oct 2026 08:42:52 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Wed, 28 Oct 2026 08:42:52 +0900    

Click here for diff

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   : 17a0604c2d626fa4adb2a98f584fc6b7ee72e663    
  
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    

Click here for diff

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

Disallow NOT NULL NO INHERIT constraints on partitioned tables

commit   : 35f60e5ceab72b96ef3eb8cf68d45934f2d6479e    
  
author   : David Rowley <drowley@postgresql.org>    
date     : Tue, 27 Oct 2026 19:47:09 +1300    
  
committer: David Rowley <drowley@postgresql.org>    
date     : Tue, 27 Oct 2026 19:47:09 +1300    

Click here for diff

This was blocked during normal CREATE TABLE, but cases such as CREATE  
TABLE (LIKE) would copy the source table's NOT NULL NO INHERIT constraints  
and apply them to the partitioned table being created.  This could mean  
that partitions didn't correctly receive the NOT NULL constraint when they  
were created, thus could contain NULL values.  
  
cacef1722 added a test to ensure CREATE TABLE (LIKE) PARTITION BY fails  
when the source table has any CHECK NO INHERIT constraints.  Here we do  
the same for NOT NULL constraints.  
  
It's probably unlikely that people have commonly done this, but the  
release notes may wish to note that people should check if this has  
happened to them.  The following query can used each database to identify  
problematic constraints:  
  
SELECT conrelid::regclass, conname, connoinherit  
FROM pg_constraint  
WHERE contype = 'n'  
  AND connoinherit = true  
  AND conrelid IN (SELECT oid FROM pg_class WHERE relkind = 'p');  
  
If no results are shown, the queried database isn't affected.  
  
To resolve these issues, unwanted constraints can simply be dropped from  
the partitioned table with:  
  
ALTER TABLE ... DROP CONSTRAINT ...;  
  
To remove the NO INHERIT flag, use:  
  
ALTER TABLE ... ALTER CONSTRAINT ... INHERIT;  
  
This will fail if any partition contains a NULL value in the given  
column.  Finding the cause of failures is made more difficult by the  
planner trusting that "WHERE col IS NULL" cannot be true because of the  
presence of a NOT NULL constraint.  Affected users may need to resort to  
tricks like WHERE col+0 IS NULL to find these.  If it was thought to be a  
common problem, then we could adjust get_relation_notnullatts(), and the  
equivalent functions on older branches to ignore constraints marked as NO  
INHERIT on partitioned tables, but currently that would require an  
expensive lookup to find the pg_constraint row.  
  
Backpatch to v18 where NOT NULL NO INHERIT constraints were added by  
14e87ffa5.  
  
Bug: #19770  
Reported-by: Ann C <anncalla@163.com>  
Author: David Rowley <dgrowleyml@gmail.com>  
Author: Nitin Motiani <nitinmotiani@google.com>  
Reviewed-by: Chao Li <li.evan.chao@gmail.com>  
Reviewed-by: Nitin Motiani <nitinmotiani@google.com>  
Discussion: https://postgr.es/m/19770-7c6e75fc30df7e06@postgresql.org  
Discussion: https://postgr.es/m/CAH5HC96YCzOy+9JZLQ4n0sMchVnharTOXrr4sbYuh=_d+ffjAA@mail.gmail.com  
Backpatch-through: 18  

M src/backend/catalog/heap.c
M src/test/regress/expected/create_table_like.out
M src/test/regress/sql/create_table_like.sql

Avoid truncated or zero-padded copies of replaced files in backups

commit   : b09b26ac8e2efa2837c0a4b7780d7df0ce15d0c2    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Tue, 27 Oct 2026 13:53:46 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Tue, 27 Oct 2026 13:53:46 +0900    

Click here for diff

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   : dc7efdf14f67283359923348e843a8c571aff3e0    
  
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    

Click here for diff

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   : a34d61616134c75694e99abf8beb58b7b82b3455    
  
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    

Click here for diff

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/backend/utils/cache/funccache.c
M src/pl/plpgsql/src/pl_comp.c

Fix nbtree multi-level page deletion WAL record bug.

commit   : e88259b672e0a81eecd55b08f304c10ae38f6104    
  
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    

Click here for diff

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   : 15e940a10cb0ab143b450c6961a9a331c87b554e    
  
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    

Click here for diff

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   : 606f3db5595dc8459b3d41a081b3ae8ffaae05b9    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Mon, 26 Oct 2026 17:14:26 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Mon, 26 Oct 2026 17:14:26 +0900    

Click here for diff

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   : 77c4a9037b8ab79b0c3cb85365d12935ba6dfdb8    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Mon, 26 Oct 2026 10:04:22 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Mon, 26 Oct 2026 10:04:22 +0900    

Click here for diff

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   : 5df84b355ab78cfc3e8c10d37a80d627a53ed365    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Mon, 26 Oct 2026 09:54:30 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Mon, 26 Oct 2026 09:54:30 +0900    

Click here for diff

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   : e0a537f1908f8c38c99558b1e997530874be086f    
  
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    

Click here for diff

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.in.c

Fix "variable set but not used" warning with unnamed POSIX semaphores.

commit   : b9662d4be6c7e2a762877c13caa46fd83107004d    
  
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    

Click here for diff

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   : 1735ad557d3701a43989921be727a833646dcf89    
  
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    

Click here for diff

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   : 65f3b7096b23ca905e2be4359097a36e5b180580    
  
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    

Click here for diff

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

doc PG 18 relnotes: add incompatibility item about SQLSTATE

commit   : 9412c35035a74b9e5623b4d7e67c5f77967f0d1a    
  
author   : Bruce Momjian <bruce@momjian.us>    
date     : Fri, 23 Oct 2026 15:04:50 -0400    
  
committer: Bruce Momjian <bruce@momjian.us>    
date     : Fri, 23 Oct 2026 15:04:50 -0400    

Click here for diff

In PG 18 RESTRICT returns a different SQLSTATE than previous releases.  
  
Reported-by: Kacper Kuras  
  
Author: Kacper Kuras  
  
Discussion: https://postgr.es/m/VI0P193MB3111C432A6EEE8B74AD80A30BF912@VI0P193MB3111.EURP193.PROD.OUTLOOK.COM  
  
Backpatch-through: 18 only  

M doc/src/sgml/release-18.sgml

Remove faulty Asserts added by 1999fe227.

commit   : b049b4e7b4a159d373ef394bda67f7cf25758998    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Fri, 23 Oct 2026 10:29:45 -0400    
  
committer: Tom Lane <tgl@sss.pgh.pa.us>    
date     : Fri, 23 Oct 2026 10:29:45 -0400    

Click here for diff

I added these assertions in an attempt to show that setting  
ret_old_alias/ret_new_alias in set_deparse_plan() fully covered  
all the parts of the plan where RETURNING Vars could be found.  
But it emerges that the returningOldAlias/returningNewAlias  
fields they are filled from can sometimes be NULL themselves,  
allowing the asserts to fail.  We discussed various ways that  
we might adjust the rewriter to prevent that, but the upshot  
of the discussion was just to remove the asserts again.  
  
Reported-by: Ayush Tiwari <ayushtiwari.slg01@gmail.com>  
Author: Tom Lane <tgl@sss.pgh.pa.us>  
Reviewed-by: Dean Rasheed <dean.a.rasheed@gmail.com>  
Discussion: https://postgr.es/m/CAJTYsWWsORQbyViXmsvT4-NWkjuykkjjk2c+gQtq+vw2PJON-A@mail.gmail.com  
Backpatch-through: 18  

M src/backend/utils/adt/ruleutils.c

Accept empty tsquery in binary input.

commit   : 6c4172b34dfbdaceb89305e241d9eb60926890fc    
  
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    

Click here for diff

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   : 58998c33d19932670a2191a569fe9fa2e16db1d6    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Thu, 22 Oct 2026 14:39:09 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Thu, 22 Oct 2026 14:39:09 +0900    

Click here for diff

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   : 5b5c68194c545217b10582b92a6f89c70185c1db    
  
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    

Click here for diff

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.in.c

Fix slot names in the standby slot removal check

commit   : 1b0888f69c679dbb06a0521082c06aa4b6e3bdb2    
  
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    

Click here for diff

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   : d7d45526e137d8cde1b09dce3658eeab9f79a81e    
  
author   : Fujii Masao <fujii@postgresql.org>    
date     : Thu, 22 Oct 2026 09:21:36 +0900    
  
committer: Fujii Masao <fujii@postgresql.org>    
date     : Thu, 22 Oct 2026 09:21:36 +0900    

Click here for diff

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   : a5b925cfcc9bd2a5c7caf02887794b497ff2ab26    
  
author   : Masahiko Sawada <msawada@postgresql.org>    
date     : Wed, 21 Oct 2026 11:10:41 -0700    
  
committer: Masahiko Sawada <msawada@postgresql.org>    
date     : Wed, 21 Oct 2026 11:10:41 -0700    

Click here for diff

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   : 794c29dc41a6a930136cd086b22b72b8da927053    
  
author   : Masahiko Sawada <msawada@postgresql.org>    
date     : Wed, 21 Oct 2026 10:44:09 -0700    
  
committer: Masahiko Sawada <msawada@postgresql.org>    
date     : Wed, 21 Oct 2026 10:44:09 -0700    

Click here for diff

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   : fd97624fb510905aad4cbf0ac951b0a92703bc65    
  
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    

Click here for diff

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

doc: Correct psql's \d+x documentation for sequences

commit   : 4272618fbb83f1522e68626f70f0dee4628b73f2    
  
author   : Fujii Masao <fujii@postgresql.org>    
date     : Wed, 21 Oct 2026 23:37:38 +0900    
  
committer: Fujii Masao <fujii@postgresql.org>    
date     : Wed, 21 Oct 2026 23:37:38 +0900    

Click here for diff

Commit 00f4c2959d6 added the x modifier to psql's list commands, making  
\d+x display sequence descriptions in expanded mode. However, the  
documentation and \? help indicate that x applies only without a pattern.  
  
Clarify that \d+x also uses expanded mode when describing sequences  
matched by a name or pattern, and update the command help accordingly.  
Also clarify that the same restriction applies to \x on and \x auto.  
  
Backpatch to v18, where the x modifier was introduced.  
  
Reported-by: George MacKerron <george.mackerron@databricks.com>  
Author: Fujii Masao <masao.fujii@gmail.com>  
Reviewed-by: Shihao Zhong <zhong950419@gmail.com>  
Discussion: https://postgr.es/m/FEA91EA9-CF32-4920-9674-F2C3E01C3807@databricks.com  
Backpatch-through: 18  

M doc/src/sgml/ref/psql-ref.sgml
M src/bin/psql/help.c

Fix lateral references in sort keys added by createplan.c

commit   : f6642a97fe9ca1471555d20a40f2184d6f9dbc25    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Wed, 21 Oct 2026 18:00:20 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Wed, 21 Oct 2026 18:00:20 +0900    

Click here for diff

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   : 5fb5bef3e454900c2653b830b042705ee1f9deae    
  
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    

Click here for diff

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 logical replication with lossy identity indexes.

commit   : 23fffd382ca06690fdce91bc4819a57017cb1dda    
  
author   : Amit Kapila <akapila@postgresql.org>    
date     : Wed, 21 Oct 2026 08:34:27 +0530    
  
committer: Amit Kapila <akapila@postgresql.org>    
date     : Wed, 21 Oct 2026 08:34:27 +0530    

Click here for diff

A GiST index on a multirange stores only its bounding range, so different  
multiranges can produce the same index key. However,  
RelationFindReplTupleByIndex() and RelationFindDeletedTupleInfoByIndex()  
assume that matches through a primary key or replica identity index are  
exact and skip the equality check. This can cause updates or deletes to  
target the wrong row, and conflict detection to report update_deleted  
instead of update_missing.  
  
Fix this by rechecking candidates when the index scan sets xs_recheck.  
Compare only the index key columns, using the scan keys' equality  
operators and collations to match the index's notion of key equality.  
  
Backpatch to 18, where WITHOUT OVERLAPS was introduced. The change to  
RelationFindDeletedTupleInfoByIndex() applies only to 19 and later, where  
update_deleted conflict detection was added.  
  
Reported-by: Andres Freund <andres@anarazel.de>  
Author: Paul A. Jungwirth <pj@illuminatedcomputing.com>  
Author: Zhijie Hou <houzhijie22@gmail.com>  
Reviewed-by: Nisha Moond <nisha.moond412@gmail.com>  
Reviewed-by: kedar anavardekar <kedar.anavardekar@gmail.com>  
Reviewed-by: Amit Kapila <amit.kapila16@gmail.com>  
Discussion: https://postgr.es/m/kcyaok346iwocfdourf2oojgtz7ggxmh2ugis7qqhggs4qfzc2@cu6pkox4rfwp  
Discussion: https://postgr.es/m/e5nb5jbus2oa3pffmlo7pdvdckmchd54tqld4k3n6huyg5xxqn@7rounyvufjx6  
Discussion: https://postgr.es/m/CA+renyWzTBC4GetPzJM1KqrejrmOWcGnn2NaA7tpee17HFxRPw@mail.gmail.com  
Backpatch-through: 18  

M src/backend/executor/execReplication.c
M src/test/subscription/t/034_temporal.pl

Fix cleanup of reused pgstats entries in pgstat_release_entry_ref()

commit   : 2c056b37f993a5fb12df37cfc512a25524fb2762    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Wed, 21 Oct 2026 10:55:31 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Wed, 21 Oct 2026 10:55:31 +0900    

Click here for diff

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   : eb1454ecc656d8079a2ac540b809b3b65e2debcc    
  
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    

Click here for diff

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

Fix OLD/NEW aliases that EXPLAIN prints in RETURNING SubPlans.

commit   : 4ee8af2e86388dedb02f27a4384c9f46e9c7050a    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Tue, 20 Oct 2026 17:32:31 -0400    
  
committer: Tom Lane <tgl@sss.pgh.pa.us>    
date     : Tue, 20 Oct 2026 17:32:31 -0400    

Click here for diff

set_deparse_context_plan() set the aliases for OLD and NEW in  
RETURNING when EXPLAIN printed a ModifyTable, but that's the  
wrong place: they should be set in set_deparse_plan() like other  
plan-node-specific fields.  So they were not changed when  
push_ancestor_plan() moved to the ancestor ModifyTable to print a  
Param of a SubPlan in its RETURNING list, and the aliases of some  
previously-printed ModifyTable could be used instead.  It seems  
possible that we'd also fail to print an alias at all, since  
get_variable() is unfazed by obtaining a NULL refname.  That  
shouldn't happen for RETURNING variables, so complain.  
  
Author: Henson Choi <assam258@gmail.com>  
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>  
Discussion: https://postgr.es/m/CAAAe_zAUb_XydapJqUk7O8o2crJa7xbxX=KvSi3nY9h98OcN7g@mail.gmail.com  
Backpatch-through: 18  

M src/backend/utils/adt/ruleutils.c
M src/test/regress/expected/returning.out
M src/test/regress/sql/returning.sql

In deparsing, parenthesize IS DOCUMENT where it is an operand.

commit   : b08c4df3b733a77ae6404bee51ce0daa9d8a59cc    
  
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    

Click here for diff

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   : e79df9dd9f34115c13b038c7b22a8c3903e76e9b    
  
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    

Click here for diff

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/returning.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   : e9fa8382df2d64ea3f8292cf3a39a9f7a308572d    
  
author   : David Rowley <drowley@postgresql.org>    
date     : Tue, 20 Oct 2026 10:34:01 +1300    
  
committer: David Rowley <drowley@postgresql.org>    
date     : Tue, 20 Oct 2026 10:34:01 +1300    

Click here for diff

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   : 9ba69ffcc4bc3e4cd7dd215f42e3a887c77d0b27    
  
author   : David Rowley <drowley@postgresql.org>    
date     : Mon, 19 Oct 2026 19:43:34 +1300    
  
committer: David Rowley <drowley@postgresql.org>    
date     : Mon, 19 Oct 2026 19:43:34 +1300    

Click here for diff

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   : 52af12e9d908fd528bc347a0c01bb931d3c89010    
  
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    

Click here for diff

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   : 17b45abefb59800c4e4f94bd17b5a3de91e4a633    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Mon, 19 Oct 2026 11:02:14 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Mon, 19 Oct 2026 11:02:14 +0900    

Click here for diff

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 nbtree unsatisfiable row compare assertion failure.

commit   : 4732a5c53bd858ed057d52286fb6105385baa2a6    
  
author   : Peter Geoghegan <pg@bowt.ie>    
date     : Sat, 17 Oct 2026 11:02:49 -0400    
  
committer: Peter Geoghegan <pg@bowt.ie>    
date     : Sat, 17 Oct 2026 11:02:49 -0400    

Click here for diff

An assertion within nbtree preprocessing's _bt_mark_scankey_required  
function expects the first member key of a row compare to have the same  
strategy as its header key, which its caller passes.  But the first row  
member key won't necessarily have been commuted in cases where the row  
compare is unsatisfiable due to its first member key having a NULL  
argument.  This led to spurious assertion failures in the single key  
fast path call to _bt_mark_scankey_required.  
  
To fix, always commute the row members, even when the row compare is  
unsatisfiable.  That way the header and its members will always agree on  
strategy.  
  
Oversight in commit ec986020, which improved nbtree's unsatisfiable  
RowCompare detection.  
  
Author: Peter Geoghegan <pg@bowt.ie>  
Backpatch-through: 18  

M src/backend/access/nbtree/nbtpreprocesskeys.c

Fix bogus outer-join commutation with lateral refs in PlaceHolderVars

commit   : d7938ff308547c609693b3603c9be2d684975c55    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Sat, 17 Oct 2026 17:19:23 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Sat, 17 Oct 2026 17:19:23 +0900    

Click here for diff

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   : 01a9e573fa908ba7a91e6e82656ab5a867563e13    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Sat, 17 Oct 2026 10:28:53 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Sat, 17 Oct 2026 10:28:53 +0900    

Click here for diff

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   : 92d47d544bf64274b6a7aa8775f7b35bcf71f339    
  
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    

Click here for diff

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   : dfbc1507cdb5aed65d420b620960abaf1cdd9fd2    
  
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    

Click here for diff

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   : 707b3738df95541a9f1d93f2c9a3b96e3f8416d5    
  
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    

Click here for diff

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

Fix argument given to buffer_readv_encode_error()

commit   : 368e12de252707ffa50f9a471bd9c36709c4b2f0    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Fri, 16 Oct 2026 14:30:48 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Fri, 16 Oct 2026 14:30:48 +0900    

Click here for diff

buffer_readv_encode_error() expects a uint8 for its "error_count"  
argument, but the value given was a boolean.  That's harmless in  
practice, but let's be clean.  
  
Thinko in 047cba7fa0f8.  
  
Author: Antonin Houska <ah@cybertec.at>  
Reviewed-by: Steven Niu <niushiji@gmail.com>  
Discussion: https://postgr.es/m/8073.1791959482@localhost  
Backpatch-through: 18  

M src/backend/storage/buffer/bufmgr.c

Add sentinel_ok() check to BumpCheck().

commit   : 773164824353fbb023cec7f3deb11d6b5c6c06b8    
  
author   : David Rowley <drowley@postgresql.org>    
date     : Fri, 16 Oct 2026 18:00:06 +1300    
  
committer: David Rowley <drowley@postgresql.org>    
date     : Fri, 16 Oct 2026 18:00:06 +1300    

Click here for diff

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

Simplify restriction matching in self-join elimination.

commit   : 10b2d9986b958b413d169b30ea43ae32a405bc72    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Thu, 15 Oct 2026 12:42:34 -0400    
  
committer: Tom Lane <tgl@sss.pgh.pa.us>    
date     : Thu, 15 Oct 2026 12:42:34 -0400    

Click here for diff

In match_unique_clauses(), use equal() to compare complete restriction  
clauses after substituting the relation ID, instead of comparing their  
operands separately.  This ensures that the operator and input  
collation also match, preventing incorrect self-join elimination when  
restrictions have identical operands but different semantics.  
  
The former code apparently intended to support matching clauses to  
commuted versions of those clauses, but the amount of complication  
that added seems far out of proportion to the value, so just drop  
that goal.  (If we wanted to reinstate it, something based on  
applying CommuteOpExpr or equivalent logic would be better.)  
  
Author: Tender Wang <tndrwang@gmail.com>  
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>  
Discussion: https://postgr.es/m/CAHewXNmwYrp0DTuisCyfXXjVa_irUdDFApUzLVDg3J4MAR6gEg@mail.gmail.com  
Backpatch-through: 18  

M src/backend/optimizer/plan/analyzejoins.c

Fix _bt_readnextpage half-dead leftmost page assert.

commit   : 7a28f1d0280ac11c12aa27e36d330e751f4285fe    
  
author   : Peter Geoghegan <pg@bowt.ie>    
date     : Thu, 15 Oct 2026 12:17:18 -0400    
  
committer: Peter Geoghegan <pg@bowt.ie>    
date     : Thu, 15 Oct 2026 12:17:18 -0400    

Click here for diff

Backwards scans that reach a half-dead leftmost leaf page step over the  
page without calling _bt_readpage.  This happens whenever the most  
recent _bt_lock_and_validate_left call returned a half-dead leftmost  
page.  An assertion within _bt_readnextpage failed to consider this,  
causing it to fail spuriously.  
  
To fix, make the assertion accept the case where currPage doesn't match  
lastcurrblkno as long as blkno is P_NONE (it's always P_NONE when the  
last leaf page examined by _bt_readnextpage is either the leftmost or  
rightmost page).  
  
Oversight in commit caca6d8d27.  
  
Author: Peter Geoghegan <pg@bowt.ie>  
Backpatch-through: 18  

M src/backend/access/nbtree/nbtsearch.c

Update v18 .abi-compliance-history for changes in reorderbuffer.h.

commit   : 8a59a526de838dabcba16bfc5d994242048a1696    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Thu, 15 Oct 2026 11:39:19 -0400    
  
committer: Tom Lane <tgl@sss.pgh.pa.us>    
date     : Thu, 15 Oct 2026 11:39:19 -0400    

Click here for diff

This also documents c7328770b and 57193b3a0, which cancelled each  
other out but still need to be reflected in the ABI history.  

M .abi-compliance-history

Fix CreateThread() failure checks

commit   : 4d62ef92fda14315f465a686c5ef272cdb2dc8e4    
  
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    

Click here for diff

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   : 94d53e9ec69fe30140cc0cf62b1f08f69b683347    
  
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    

Click here for diff

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

Fix EPQ run-time pruning using the parent's PARAM_EXEC values

commit   : 57193b3a09ed3555e9c6e3822173427a3249b335    
  
author   : David Rowley <drowley@postgresql.org>    
date     : Thu, 15 Oct 2026 15:50:11 +1300    
  
committer: David Rowley <drowley@postgresql.org>    
date     : Thu, 15 Oct 2026 15:50:11 +1300    

Click here for diff

8741e48e5 made EvalPlanQualStart() borrow the parent EState's  
es_part_prune_states.  However, the ExprContext in each  
PartitionPruneState belongs to the parent EState, so exec pruning during  
an EPQ recheck used the parent's PARAM_EXEC values, i.e. those from the  
old row version.  This could prune away the partition the recheck  
needed, causing the row to be silently skipped, e.g. with a correlated  
subquery on a partitioned table in the WHERE clause of an UPDATE.  
  
Here we fix this by giving the EPQ EState its own PartitionPruneStates,  
while still borrowing the parent's es_part_prune_results so that the  
same subplans are initialized.  As the states are no longer shared, the  
"initialized" field added by 1b5dd3a24 is no longer needed, so remove  
it.  This also undoes the ABI break that field caused in 18.  
  
Backpatch to 18, where 8741e48e5 and 1b5dd3a24 were backpatched.  
  
Author: Zsolt Parragi <zsolt.parragi@percona.com>  
Reviewed-by: David Rowley <dgrowleyml@gmail.com>  
Discussion: https://postgr.es/m/CAN4CZFPZ6qKnv6FPmq1RQZ49cHALCwLBu_NJdqUrkZNg0z81JQ%40mail.gmail.com  
Backpatch-through: 18  

M src/backend/executor/execMain.c
M src/backend/executor/execPartition.c
M src/include/executor/execPartition.h
M src/test/isolation/expected/eval-plan-qual-partition-prune.out
M src/test/isolation/specs/eval-plan-qual-partition-prune.spec

Fix self-join elimination to avoid inheritance scans.

commit   : ebf82fae68b7e8e9773a84561f87f45368953eae    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Wed, 14 Oct 2026 16:38:15 -0400    
  
committer: Tom Lane <tgl@sss.pgh.pa.us>    
date     : Wed, 14 Oct 2026 16:38:15 -0400    

Click here for diff

Self-join elimination failed to check for inheritance cases,  
which has two big problems.  First, a unique index on the parent  
table isn't sufficient to prove uniqueness across its children too.  
Second, since we matched RTEs by OID without considering rte->inh,  
we could try to merge "mytable" with "mytable ONLY", which doesn't  
even represent the same set of rows.  
  
We can fix both issues by just skipping RTEs that have inh set.  
preprocess_relation_rtes already cleared that flag for tables known  
not to have inheritance children, so this doesn't break SJE for the  
cases where it's valid.  
  
Author: Tender Wang <tndrwang@gmail.com>  
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>  
Discussion: https://postgr.es/m/CAHewXNn4EjCQEvanN_FPKQ3OJWaBXq8Pxcj-DZYbeQ6_4xSFgw@mail.gmail.com  
Backpatch-through: 18  

M src/backend/optimizer/plan/analyzejoins.c

Clear more substructure of a dead relation during join removal.

commit   : 18aca7257eb0330d7ea6391a085ff45d57496ce2    
  
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    

Click here for diff

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   : f4284d9aa621253d0e4ec43e1f5bb30307c152c0    
  
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    

Click here for diff

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   : 529e08c32987890ba9eae74d8ed680cef646d197    
  
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    

Click here for diff

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   : 5eae2800a854cd60e973b576fd9dac9db299dfd1    
  
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    

Click here for diff

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   : fc834ccab5b48538ee1ffecc19b180a0f6a573a6    
  
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    

Click here for diff

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   : f47c63d7a453d7607ceabe74856c2d60669bfe7a    
  
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    

Click here for diff

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   : 04ddc52427f9fb4e8141f4204f34ae380af20f92    
  
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    

Click here for diff

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   : 0aba37abfeaa8c44b6a59f672611be9650e0ccc7    
  
author   : David Rowley <drowley@postgresql.org>    
date     : Wed, 14 Oct 2026 22:07:53 +1300    
  
committer: David Rowley <drowley@postgresql.org>    
date     : Wed, 14 Oct 2026 22:07:53 +1300    

Click here for diff

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

Don't use SubPlan-containing expressions for startup-time pruning

commit   : 70ffc30e12b1de43d6160682fc44c78b8de00f24    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Wed, 14 Oct 2026 14:56:08 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Wed, 14 Oct 2026 14:56:08 +0900    

Click here for diff

match_clause_to_partition_key() tells whether a comparison value can  
be evaluated during executor-startup pruning by checking that it has  
no Vars, no volatile functions and no PARAM_EXEC Params.  A SubPlan  
normally fails the last test, since the testexpr of an ANY/ALL SubPlan  
references the subplan's output Params.  But if the left-hand side is  
a NULL constant, the strict comparison operator in the testexpr is  
const-folded to a NULL Const, and an uncorrelated SubPlan has no  
parParam or args either, so nothing is left for pull_exec_paramids()  
to find.  The expression then reaches initial_pruning_steps, where  
ExecDoInitialPruning() must evaluate it before any PlanState exists,  
and we fail with "SubPlan found with no parent plan".  
  
To fix, don't allow such an expression to be used for startup-time  
pruning.  It's still fine for per-scan pruning, which has a PlanState  
to evaluate it with.  Expressions containing exec Params are  
unaffected.  
  
This has been broken since d47cbf474, which moved initial pruning out  
of ExecInitNode(); before that, pruning always had the parent node's  
PlanState available.  Back-patch to v18.  
  
Author: Richard Guo <guofenglinux@gmail.com>  
Reviewed-by: Amit Langote <amitlangote09@gmail.com>  
Discussion: https://postgr.es/m/CAMbWs48nYDu7joYeyMKYdX8kHoL_ASEax2fqZH6L+HHqO2nkXA@mail.gmail.com  
Backpatch-through: 18  

M src/backend/partitioning/partprune.c
M src/test/regress/expected/partition_prune.out
M src/test/regress/sql/partition_prune.sql

Disallow joins whose lateral references can never be satisfied

commit   : 7432ecba189895c37716ec333a087cf35d9ec424    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Wed, 14 Oct 2026 14:31:54 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Wed, 14 Oct 2026 14:31:54 +0900    

Click here for diff

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

commit   : 64fb20a52394a8f5a81bcff7bb39e0a1cde7d0b8    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Wed, 14 Oct 2026 12:05:51 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Wed, 14 Oct 2026 12:05:51 +0900    

Click here for diff

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   : d21a3e1fe5f454355efbc3bccbd2b4d81e0f7c27    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Wed, 14 Oct 2026 12:05:12 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Wed, 14 Oct 2026 12:05:12 +0900    

Click here for diff

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   : c6a51ad94a23fe18d7b6e82c6c83db9196864b78    
  
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    

Click here for diff

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   : 942266c7f41a897a93b9c1019dc258cf0fd642b3    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Wed, 14 Oct 2026 10:16:19 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Wed, 14 Oct 2026 10:16:19 +0900    

Click here for diff

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   : 476b9eb3c4e136b62f0965408105d7c0e5b7dd4f    
  
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    

Click here for diff

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
M src/test/regress/expected/join.out
M src/test/regress/sql/join.sql

Fix two errors in extraction of GIN indexquals from jsonpath queries.

commit   : 154e2f10157c61ebd101048b415ca7372ee32ac7    
  
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    

Click here for diff

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   : a85b5523e679870414bf94e44dee424d73753abb    
  
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    

Click here for diff

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

Doc: remove some annoying zero-width spaces.

commit   : 3c6135f839d03ef96a7313bca09ba4c1ec8dd161    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Fri, 9 Oct 2026 21:14:58 -0400    
  
committer: Tom Lane <tgl@sss.pgh.pa.us>    
date     : Fri, 9 Oct 2026 21:14:58 -0400    

Click here for diff

To avoid warnings and lines that extend beyond the right page  
margin in the PDF documentation, we have zero-width spaces in  
the source at strategic places.  They are not visible to the  
naked eye, but allow FOP to break the lines in sane places.  
  
However, some of these zero-width spaces were in the middle of  
text that people might copy and paste from the HTML documentation,  
which leads to mysterious syntax errors.  
  
Scanning the documentation for &zwsp; identified four places  
that this patch improves:  
  
- an ISO 8601 interval literal didn't actually need a line break  
  in either the US letter or A4 format, so simply remove that  
  zero-width space.  
  
- three v18-era examples for uuid_extract_timestamp() and  
  uuid_extract_version() had zero-width spaces in the middle of UUID  
  literals.  Instead, add regular spaces around the "::" cast symbols,  
  allowing FOP to break the lines there.  This solution isn't  
  invisible in the HTML output, but it doesn't look bad.  
  
Reported-by: Y. Saburov <y.saburov@gmail.com>  
Author: Laurenz Albe <laurenz.albe@cybertec.at>  
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>  
Discussion: https://postgr.es/m/179143326813.1061054.14714458907175925072@wrigleys.postgresql.org  
Backpatch-through: 18  

M doc/src/sgml/datatype.sgml
M doc/src/sgml/func.sgml

Fix incorrect reinit of run-time pruning for EPQ rechecks

commit   : c7328770ba50f6250930cb023effe3354c49de32    
  
author   : David Rowley <drowley@postgresql.org>    
date     : Fri, 9 Oct 2026 11:04:22 +1300    
  
committer: David Rowley <drowley@postgresql.org>    
date     : Fri, 9 Oct 2026 11:04:22 +1300    

Click here for diff

For run-time partition pruning running during execution (rather than at  
executor startup), EvalPlanQualStart() reuses the es_part_prune_states  
from the parent EState.  This could cause an issue when the EPQ recheck is  
finished, its EState is cleaned up and the es_query_cxt is deleted by  
FreeExecutorState().  That cleanup could free memory that was allocated  
again in InitExecPartitionPruneContexts() when it was called for the EPQ's  
EState, and naturally, those fields got allocated in the EPQ's  
es_query_cxt MemoryContext, which overwrote the ones for the parent  
EState.  If pruning occurs again after the EPQ's EState was cleaned up,  
then we could have accessed free'd memory.  This certainly caused issues in  
debug builds because we clobber freed memory, but may not have in  
production, depending on if the MemoryContext being deleted results in a  
free().  It may not if the allocation was on the context's init block and  
the context was pushed onto context_freelists[] for reuse.  
  
Here we fix this by adding an "initialized" field to PartitionPruneState  
and exit early in InitExecPartitionPruneContexts() when the state is  
already initialized.  
  
This was broken by bb3ec16e1, but the breakage there is slightly  
different as that commit neglected to do what was fixed in 8741e48e5  
(which did get backpatched to v18 in 9a82a64ed).  
  
Reported-by: Vladimir Savin <vladimir@encord.com>  
Author: David Rowley <dgrowleyml@gmail.com>  
Reviewed-by: Andrey Rachitskiy <pl0h0yp1@gmail.com>  
Discussion: https://postgr.es/m/CAP2G_gGpV=rKMLuET4RW-qO41GvBvi6Czsjsj16eT9hAwHxG6w@mail.gmail.com  
Backpatch-through: 18  

M src/backend/executor/execPartition.c
M src/include/executor/execPartition.h
A src/test/isolation/expected/eval-plan-qual-partition-prune.out
M src/test/isolation/isolation_schedule
A src/test/isolation/specs/eval-plan-qual-partition-prune.spec

Fix parallel vacuum I/O timing reporting.

commit   : 6bfb33088cea797020886f73c1cfdb5a3290e0eb    
  
author   : Masahiko Sawada <msawada@postgresql.org>    
date     : Thu, 8 Oct 2026 11:26:15 -0700    
  
committer: Masahiko Sawada <msawada@postgresql.org>    
date     : Thu, 8 Oct 2026 11:26:15 -0700    

Click here for diff

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   : 3b7c13ec54b157dcda292d82c320510904e99531    
  
author   : Peter Geoghegan <pg@bowt.ie>    
date     : Thu, 8 Oct 2026 14:21:17 -0400    
  
committer: Peter Geoghegan <pg@bowt.ie>    
date     : Thu, 8 Oct 2026 14:21:17 -0400    

Click here for diff

_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

oauth_validator: Remove racing GUC check

commit   : af28fc13e20db49d559c00a8156753090c5516fd    
  
author   : Jacob Champion <jchampion@postgresql.org>    
date     : Thu, 8 Oct 2026 10:58:18 -0700    
  
committer: Jacob Champion <jchampion@postgresql.org>    
date     : Thu, 8 Oct 2026 10:58:18 -0700    

Click here for diff

Follow-up to bca67e5a3. The test added there changed the value of  
oauth_validator_libraries, waited for the postmaster to apply it, and  
verified the change on an existing background connection. But there's no  
guarantee that the SIGHUP will have arrived and completed its work on  
existing backends by that time, so the final test flaked.  
  
We only care about the postmaster's state here, since we're testing the  
GUC's effects on newly forked backends, so remove the background check  
entirely.  
  
Per buildfarm member serinus.  
  
Reported-by: Alexander Lakhin <exclusion@gmail.com>  
Discussion: https://postgr.es/m/a37a3cea-5bd8-4385-a1d4-bb89394c079c%40gmail.com  
Backpatch-through: 18  

M src/test/modules/oauth_validator/t/001_server.pl

pg_walsummary: Fix --quiet to suppress limit block output

commit   : 1370a7832a2ab7bda5625fd1f0448808b534cd50    
  
author   : Fujii Masao <fujii@postgresql.org>    
date     : Wed, 7 Oct 2026 14:57:41 +0900    
  
committer: Fujii Masao <fujii@postgresql.org>    
date     : Wed, 7 Oct 2026 14:57:41 +0900    

Click here for diff

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   : 96c869df749259cfb989e3e8857b3974a574492c    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Wed, 7 Oct 2026 14:37:53 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Wed, 7 Oct 2026 14:37:53 +0900    

Click here for diff

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   : 53b058d3b7f41a258035d9f3f590deef300a0df1    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Wed, 7 Oct 2026 14:37:42 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Wed, 7 Oct 2026 14:37:42 +0900    

Click here for diff

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   : acccaf0d8ed8e4f7523e2afa24e011be4256493a    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Wed, 7 Oct 2026 10:39:21 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Wed, 7 Oct 2026 10:39:21 +0900    

Click here for diff

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   : 9012f6f267070f97435921b3ef7c2e6afb668ee0    
  
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    

Click here for diff

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   : 882fd416d6ed605de09c5ae636bf753bcd5f47d0    
  
author   : Peter Geoghegan <pg@bowt.ie>    
date     : Tue, 6 Oct 2026 18:21:35 -0400    
  
committer: Peter Geoghegan <pg@bowt.ie>    
date     : Tue, 6 Oct 2026 18:21:35 -0400    

Click here for diff

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

Doc: correct overambitious precision claim for contrib/seg.

commit   : cc8e921045d7f6e7c52faa899282a715347ac312    
  
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    

Click here for diff

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   : 95d9604471a319b6ccfd0a7d884fd604d3da5b20    
  
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    

Click here for diff

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   : 3a09e91a49a27cad78de6d13fad6f7df13c37d65    
  
author   : Etsuro Fujita <efujita@postgresql.org>    
date     : Tue, 6 Oct 2026 18:30:02 +0900    
  
committer: Etsuro Fujita <efujita@postgresql.org>    
date     : Tue, 6 Oct 2026 18:30:02 +0900    

Click here for diff

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   : d8a311e8de0df48565b47d7f2f5439fb9000e1fa    
  
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    

Click here for diff

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

Fix ADD PRIMARY KEY after dropping and re-adding a column

commit   : 5659e549adeb9de1a89efa181b032044ada64b1f    
  
author   : Fujii Masao <fujii@postgresql.org>    
date     : Tue, 6 Oct 2026 13:13:47 +0900    
  
committer: Fujii Masao <fujii@postgresql.org>    
date     : Tue, 6 Oct 2026 13:13:47 +0900    

Click here for diff

Previously, dropping a NOT NULL column, adding a new column with the same  
name, and making it a primary key in a single ALTER TABLE command could  
fail with "primary key column ... is not marked NOT NULL". This happened  
because ALTER TABLE checked for an existing NOT NULL constraint before  
dropping the old column. Finding the constraint caused it to skip adding  
one for the new column, even though the old constraint would be removed  
with the old column.  
  
Defer this check until after column drops and additions have been  
executed, so that it examines the columns that will actually form the  
primary key. Add any required NOT NULL constraints before creating the  
primary-key index.  
  
Backpatch to v18, where this issue was introduced by commit a379061a22a.  
  
Reported-by: Ludvig Janiuk <ludvig.janiuk@proton.me>  
Author: Fujii Masao <masao.fujii@gmail.com>  
Co-authored-by: Manuel Reyes Bravo <manuelreyesbravo@gmail.com>  
Discussion: https://postgr.es/m/0a9b4c44-cdff-4ec8-a8fb-b3a8b59e9ba9@proton.me  
Backpatch-through: 18  

M src/backend/commands/tablecmds.c
M src/test/regress/expected/alter_table.out
M src/test/regress/sql/alter_table.sql

Don't reprocess PHV copies within outer aggregates' arguments

commit   : 3bfc7c8491e6744fd0a1d98f7a98b01528eeae41    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Tue, 6 Oct 2026 10:58:21 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Tue, 6 Oct 2026 10:58:21 +0900    

Click here for diff

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   : ae152929717136efb6eb0fb6dda2d89aaaa260ae    
  
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    

Click here for diff

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   : dc60d6aa1eba67361ee0f91fec2fa6af7f9db3b9    
  
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    

Click here for diff

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   : 90cc09f57f9f5bdee3a2d44ed21760d90b2616ea    
  
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    

Click here for diff

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   : 8d5b228ae985e0c30df1cac95ec33e14599ef010    
  
author   : David Rowley <drowley@postgresql.org>    
date     : Tue, 6 Oct 2026 01:21:53 +1300    
  
committer: David Rowley <drowley@postgresql.org>    
date     : Tue, 6 Oct 2026 01:21:53 +1300    

Click here for diff

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   : 5f671c4312bb5addac4de4f329907a4681e81276    
  
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    

Click here for diff

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   : b7866c934d6242ea07edfc8f137618dde721465f    
  
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    

Click here for diff

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   : 628b70d3a3a936afa84dbdc344c02ae990797573    
  
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    

Click here for diff

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   : 8ea5048263c6ef9bd91de8f0ba0857976f94008f    
  
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    

Click here for diff

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   : 710364f7a817e4768f6cb898df37be4ada50f292    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Mon, 5 Oct 2026 11:11:14 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Mon, 5 Oct 2026 11:11:14 +0900    

Click here for diff

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   : 30baaacdd1c039e4aced3f4970f24c425a5e0145    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Sun, 4 Oct 2026 16:04:16 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Sun, 4 Oct 2026 16:04:16 +0900    

Click here for diff

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

commit   : 7a776fc9d2874a396512a92c45311f53ef4c0235    
  
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    

Click here for diff

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   : 2a8e94f90c4e387486b02fdca47552b550d09766    
  
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    

Click here for diff

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   : 37bbf5bba09c4244d74f152557e69f73f2b6690e    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Fri, 2 Oct 2026 17:02:41 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Fri, 2 Oct 2026 17:02:41 +0900    

Click here for diff

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   : 95f6bfb518bf04956d36be43b1b819816f847040    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Fri, 2 Oct 2026 10:43:28 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Fri, 2 Oct 2026 10:43:28 +0900    

Click here for diff

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   : 630e607397424196a0a3ebb14a5658c2473ddadf    
  
author   : David Rowley <drowley@postgresql.org>    
date     : Thu, 1 Oct 2026 15:17:08 +1300    
  
committer: David Rowley <drowley@postgresql.org>    
date     : Thu, 1 Oct 2026 15:17:08 +1300    

Click here for diff

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   : c45ba888d7af7b38e33832a0e240e34ca98ef866    
  
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    

Click here for diff

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 incorrect parallel build of jsonapi.o with Makefiles.

commit   : a70876d7bf44feb41b431daf7baca07d7413d2ef    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Wed, 30 Sep 2026 15:58:33 -0400    
  
committer: Tom Lane <tgl@sss.pgh.pa.us>    
date     : Wed, 30 Sep 2026 15:58:33 -0400    

Click here for diff

Commit 0785d1b8b wrote  
  
jsonapi_shlib.o: override CPPFLAGS += -DJSONAPI_USE_PQEXPBUFFER  
  
forgetting that GNU make treats this change to CPPFLAGS as affecting  
not only jsonapi_shlib.o, but anything that gets built in pursuit of  
building jsonapi_shlib.o.  Because of an even more ancient hack  
(cf 7143b3e82), that will include jsonapi.o if it's not been built  
already.  If we do apply this -D switch to jsonapi.o, it gets built  
with the wrong expansible-string infrastructure, which manifests  
immediately as a link failure in pg_combinebackup and would misbehave  
at runtime even if the build somehow completed.  
  
Perhaps remarkably, we'd not seen field reports of build failures  
till now, but it's pretty easy to reproduce a mis-build by trying  
to build src/common/ with --shuffle and a high -j factor.  
  
To fix, don't modify CPPFLAGS this way; instead make a dedicated  
build rule for jsonapi_shlib.o that injects the additional switches.  
  
There might be other places with similar problems, but some desultory  
attempts with -j32 --shuffle on a 32-thread CPU didn't find any more  
build failures.  Also, it looks like the meson build infrastructure  
lacks this problem (but I'm no meson expert).  
  
Bug: #19727  
Reported-by: Ross Burton <ross@burtonini.com>  
Author: Tom Lane <tgl@sss.pgh.pa.us>  
Reviewed-by: Ross Burton <ross@burtonini.com>  
Discussion: https://postgr.es/m/19727-32d67d9dea410f38@postgresql.org  
Backpatch-through: 18  

M src/common/Makefile

Fix clobbering of proc entry's statusFlags during slot invalidation

commit   : c5666f7e16ca6845dac21b46bb2704038b35dad7    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Wed, 30 Sep 2026 14:37:17 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Wed, 30 Sep 2026 14:37:17 +0900    

Click here for diff

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

Prevent self-join elimination when RTEs' checkAsUser fields differ.

commit   : 37c2bc950c3452e54cd8e6af243bdd760f59dd0d    
  
author   : Tom Lane <tgl@sss.pgh.pa.us>    
date     : Tue, 29 Sep 2026 11:11:15 -0400    
  
committer: Tom Lane <tgl@sss.pgh.pa.us>    
date     : Tue, 29 Sep 2026 11:11:15 -0400    

Click here for diff

SJE didn't consider the possibility that two RTEs referencing the  
same table have different securityQuals, and might choose to merge  
them anyway, thereby possibly losing quals that need to be enforced.  
This is a regression introduced by 2ebf25e7d, since before that we  
didn't re-generate baserestrictinfo lists from the RTEs' securityQuals  
after performing SJE.  
  
To defend against this, only consider SJE between RTEs with the same  
checkAsUser values.  That solves the problem because the set of  
applicable RLS policies depends only on the role that is considered  
to be accessing the table, so that the securityQuals must be equal  
if the checkAsUser values are.  It might also keep us from creating  
similar bugs if we ever invent other features that depend on the  
accessing role.  And it's a lot cheaper than comparing securityQuals  
trees themselves would be, not least because we can't sort them so  
the preliminary sort step wouldn't help.  
  
(The first proposed solution was to not perform SJE at all on RTEs  
with nonempty securityQuals, but that seems rather sad, especially  
since such cases worked before 2ebf25e7d.  Making the restriction  
depend on checkAsUser seems much less likely to interfere with SJE  
unnecessarily, since in most cases that'll be the same for all  
potentially-mergeable RTEs.)  
  
The added test cases show that SJE is rejected when necessary, and  
also demonstrate two quirks of this implementation.  One is that if  
a removed security qual includes an InitPlan, we'll still attach the  
now-unused InitPlan to the plan.  That's because SS_process_sublinks  
runs before self-join elimination, so the extra InitPlan has already  
been made.  The other quirk is that we won't merge RTEs having zero  
and nonzero checkAsUser fields, even if the calling user matches the  
nonzero checkAsUser value so that there is no difference for this  
user.  That's intentional so that SJE doesn't require having to mark  
the plan as caller-dependent.  
  
Reported-by: Yonghwa Lee <underdog@theori.io>  
Author: Tom Lane <tgl@sss.pgh.pa.us>  
Reviewed-by: Ayush Tiwari <ayushtiwari.slg01@gmail.com>  
Discussion: https://postgr.es/m/20260926174457.13.noahmisch@microsoft.com  
Backpatch-through: 18  

M src/backend/optimizer/plan/analyzejoins.c
M src/test/regress/expected/rowsecurity.out
M src/test/regress/sql/rowsecurity.sql

Doc: add missing new line in DO example block

commit   : 2c6d4ebae991c62c63d27694b27a149790216b82    
  
author   : David Rowley <drowley@postgresql.org>    
date     : Wed, 30 Sep 2026 02:17:05 +1300    
  
committer: David Rowley <drowley@postgresql.org>    
date     : Wed, 30 Sep 2026 02:17:05 +1300    

Click here for diff

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   : 10f285252ae5845fa55865438d1f4c0ac81c5aae    
  
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    

Click here for diff

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   : ea24eadda01088e0fa43d7961244db4ffd9961e9    
  
author   : David Rowley <drowley@postgresql.org>    
date     : Tue, 29 Sep 2026 15:49:08 +1300    
  
committer: David Rowley <drowley@postgresql.org>    
date     : Tue, 29 Sep 2026 15:49:08 +1300    

Click here for diff

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   : b3cbb4a4b3e5ea01dccfac675dd017a187bb8b01    
  
author   : Amit Kapila <akapila@postgresql.org>    
date     : Mon, 28 Sep 2026 08:47:22 -0400    
  
committer: Amit Kapila <akapila@postgresql.org>    
date     : Mon, 28 Sep 2026 08:47:22 -0400    

Click here for diff

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   : af48a67026145bd02fa953524fcdb0d84c1d9de6    
  
author   : Amit Kapila <akapila@postgresql.org>    
date     : Mon, 28 Sep 2026 06:57:50 -0400    
  
committer: Amit Kapila <akapila@postgresql.org>    
date     : Mon, 28 Sep 2026 06:57:50 -0400    

Click here for diff

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/conflict.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   : ccffd34399f5cd8266c98f0365b648a80e67727b    
  
author   : Fujii Masao <fujii@postgresql.org>    
date     : Mon, 28 Sep 2026 14:20:27 +0900    
  
committer: Fujii Masao <fujii@postgresql.org>    
date     : Mon, 28 Sep 2026 14:20:27 +0900    

Click here for diff

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   : 652a2f6fb9415e30e1115a34da9fc7c8f5eb4773    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Mon, 28 Sep 2026 06:55:07 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Mon, 28 Sep 2026 06:55:07 +0900    

Click here for diff

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   : 2d748cfe337ec5afab3863f733caca43333c89cf    
  
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    

Click here for diff

_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

Fix import of statistics for domains over [multi]range types and tsvector

commit   : 753057e340da8f22e57c9a409ea7a235fd6a46bd    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Sat, 26 Sep 2026 09:28:18 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Sat, 26 Sep 2026 09:28:18 +0900    

Click here for diff

pg_restore_attribute_stats() rejected range_length_histogram,  
range_empty_frac and range_bounds_histogram for a column whose type is a  
domain over a range or a multirange type.  The same issue existed for  
most_common_elems and elem_count_histogram for a domain over tsvector.  
pg_restore_extended_stats() had the same set of problems for expressions  
whose type is a domain over a range, a multirange type, or tsvector.  
  
ANALYZE is able to generate such stats, incorporating the knowledge to  
handle domains in the in-core typanalyze callbacks.  
  
This problem is resolved by being more aggressive with the fetch of the  
base type of a domain in the early phases of restore for attribute and  
extended stats (special tip to Jian He for pointing out the unnecessary  
typcache lookups), reflecting on the surrounding helper routines shared  
by both code paths.  The fix for v18 is local to attribute_stats.c, as  
only attribute stats need to be touched.  
  
Tests are included in a fashion consistent with the surroundings.  The  
consequence of this issue was the rejection of stats that ANALYZE was  
able to build, which was not critical but annoying as it would lead to a  
gap in the stats restored.  
  
Reported-by: Qifan Liu <imchifan@163.com>  
Author: Michael Paquier <michael@paquier.xyz>  
Reviewed-by: Jian He <jian.universality@gmail.com>  
Reviewed-by: Corey Huinker <corey.huinker@gmail.com>  
Discussion: https://postgr.es/m/19715-b8be35083016f289@postgresql.org  
Backpatch-through: 18  

M src/backend/statistics/attribute_stats.c
M src/test/regress/expected/stats_import.out
M src/test/regress/sql/stats_import.sql

Doc: clarify there's no log_line_prefix in csvlog or jsonlog output.

commit   : b331a2c84cac0b19f66df498b45552b5da183ba1    
  
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    

Click here for diff

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   : e1b2a86d6e7e9a93039d3893b62c913d8b4164af    
  
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    

Click here for diff

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   : 66d1de70c848961c8536656c360fb8e0cbbdf1a7    
  
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    

Click here for diff

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

Fix assertion in pg_get_shmem_pagesize() in single-user mode

commit   : 297eb6a04f870f6b1f7c15cd710dc2f30a8776e3    
  
author   : Heikki Linnakangas <heikki.linnakangas@iki.fi>    
date     : Thu, 24 Sep 2026 14:38:17 +0300    
  
committer: Heikki Linnakangas <heikki.linnakangas@iki.fi>    
date     : Thu, 24 Sep 2026 14:38:17 +0300    

Click here for diff

Remove assertion that was too strict, and tripped if you queried the  
pg_shmem_allocations_numa or the pg_buffercache_os_pages view in  
single-user mode.  
  
The point of the assertion was that the function should only be called  
after "the server has started", but it would be OK to call it from the  
postmaster, too, as long as it's after startup, specifically after  
huge_pages_status has been set.  We already have a direct assertion  
for that.  
  
Author: Ayush Tiwari <ayushtiwari.slg01@gmail.com>  
Discussion: https://www.postgresql.org/message-id/CAJTYsWV-=w5_oFgCnfAixWGc=gMa2pkDinLyGLsb4sKcDaf2FA@mail.gmail.com  
Backpatch-through: 18  

M src/backend/storage/ipc/shmem.c

Wait for transactions of an initial decoding snapshot to commit

commit   : 6210f00cac2fa62b24881b31130bdb2224f7b950    
  
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    

Click here for diff

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/access/transam/xact.c
M src/backend/replication/logical/snapbuild.c
M src/test/recovery/meson.build
A src/test/recovery/t/057_snapshot_commit_race.pl

Fix incorrect block accounting in TID Range Scans

commit   : 39a0db101105eab3f4044d11c609c58b9459ea16    
  
author   : David Rowley <drowley@postgresql.org>    
date     : Thu, 24 Sep 2026 01:20:02 +1200    
  
committer: David Rowley <drowley@postgresql.org>    
date     : Thu, 24 Sep 2026 01:20:02 +1200    

Click here for diff

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   : 71672056784f7b29e7592f9a9a413f9cd8fde5cf    
  
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    

Click here for diff

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   : ce9bd103e0e34b37187edae5e3b0bd90fedf1373    
  
author   : Dean Rasheed <dean.a.rasheed@gmail.com>    
date     : Wed, 23 Sep 2026 09:39:06 +0100    
  
committer: Dean Rasheed <dean.a.rasheed@gmail.com>    
date     : Wed, 23 Sep 2026 09:39:06 +0100    

Click here for diff

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   : 9733eb9e8df78f97c2f90abb7373d7bed4c5a4b3    
  
author   : Melanie Plageman <melanieplageman@gmail.com>    
date     : Tue, 22 Sep 2026 18:27:25 -0400    
  
committer: Melanie Plageman <melanieplageman@gmail.com>    
date     : Tue, 22 Sep 2026 18:27:25 -0400    

Click here for diff

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   : b28c6c443e4001708e8f6d38f0868ac239452caa    
  
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    

Click here for diff

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

Update .abi-compliance-history for parallel GIN index build fix

commit   : ce2648b16ab8d18bd4fb4ac6fed5e260ac309f6d    
  
author   : Peter Eisentraut <peter@eisentraut.org>    
date     : Tue, 22 Sep 2026 11:47:40 +0200    
  
committer: Peter Eisentraut <peter@eisentraut.org>    
date     : Tue, 22 Sep 2026 11:47:40 +0200    

Click here for diff

Discussion: https://www.postgresql.org/message-id/flat/19545-0f25b7e47351e8fc%40postgresql.org  

M .abi-compliance-history

Fix join removal when Vars reference the removed join via its alias

commit   : 051db7737c18b1c5d25cdc4ad508608c4b53fafc    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Sat, 19 Sep 2026 22:48:09 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Sat, 19 Sep 2026 22:48:09 +0900    

Click here for diff

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   : 83bd1c1baddc37ed1eb5b26d6dea3ee275c1bdd2    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Sat, 19 Sep 2026 21:55:30 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Sat, 19 Sep 2026 21:55:30 +0900    

Click here for diff

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   : b8736db6e09137c36b19222ee73e81a28dc1690c    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Sat, 19 Sep 2026 20:14:00 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Sat, 19 Sep 2026 20:14:00 +0900    

Click here for diff

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   : a477847ebe2a1f700ce1844a8b718fdd66e9b7d0    
  
author   : Masahiko Sawada <msawada@postgresql.org>    
date     : Fri, 18 Sep 2026 10:18:58 -0700    
  
committer: Masahiko Sawada <msawada@postgresql.org>    
date     : Fri, 18 Sep 2026 10:18:58 -0700    

Click here for diff

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   : 720d3df25f4926849dadee19269d7c8efd6f1cf8    
  
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    

Click here for diff

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   : 63d9e26c27b9965a78f0d59512a80d84935a7c4b    
  
author   : David Rowley <drowley@postgresql.org>    
date     : Fri, 18 Sep 2026 17:15:30 +1200    
  
committer: David Rowley <drowley@postgresql.org>    
date     : Fri, 18 Sep 2026 17:15:30 +1200    

Click here for diff

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   : 1480ad3d3473771d401d92b05e78fb02f4191f92    
  
author   : David Rowley <drowley@postgresql.org>    
date     : Fri, 18 Sep 2026 15:50:46 +1200    
  
committer: David Rowley <drowley@postgresql.org>    
date     : Fri, 18 Sep 2026 15:50:46 +1200    

Click here for diff

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

Test output fixes for oauth validator test suite

commit   : 459ef520d8fd94aebaad573d9abbd42d7bb8d502    
  
author   : Daniel Gustafsson <dgustafsson@postgresql.org>    
date     : Thu, 17 Sep 2026 14:56:28 +0200    
  
committer: Daniel Gustafsson <dgustafsson@postgresql.org>    
date     : Thu, 17 Sep 2026 14:56:28 +0200    

Click here for diff

Commit 99c3f5e2037bc1192004f accidentally missed including the test  
fixes.  

M src/test/modules/oauth_validator/t/001_server.pl

Fix postmaster crash on whitespace-only oauth_validator_libraries

commit   : 99c3f5e2037bc1192004f3893fd0decd97a7f165    
  
author   : Daniel Gustafsson <dgustafsson@postgresql.org>    
date     : Thu, 17 Sep 2026 14:52:30 +0200    
  
committer: Daniel Gustafsson <dgustafsson@postgresql.org>    
date     : Thu, 17 Sep 2026 14:52:30 +0200    

Click here for diff

The check_oauth_validator check for an empty validator list didn't test  
for a string with only whitespace, which would cause the postmaster to  
crash.  Instead of testing for the empty string cases, pass the config  
value to SplitDirectoriesString unconditionally.  If an empty list is  
returned then the input string was empty.  Since pstrdup cannot handle  
NULL, assert that the string ie set.  While users cannot set the string  
to NULL, it is initialized to NULL so guard against programmer error.  
  
Check the parsed list instead.  Assert that the GUC string is non-NULL  
before pstrdup(); users cannot set it to NULL, but the C variable is  
initialized that way.  
  
This also adds a TAP test that reloads a whitespace-only setting after  
pg_hba_file_rules and waits until the existing backend sees the restored  
GUC.  
  
Author: Grigorev Jurij <ju.grigorev@ftdata.ru>  
Reviewed-by: Jacob Champion <jacob.champion@enterprisedb.com>  
Reviewed-by: Daniel Gustafsson <daniel@yesql.se>  
Discussion: https://postgr.es/m/04fa84f6ebbe400f940e179ebe1070e9@localhost.localdomain  
Backpatch-through: 18  

M src/backend/libpq/auth-oauth.c
M src/test/modules/oauth_validator/t/001_server.pl

Tolerate partial pgstats entries in pgstat_gc_entry_refs()

commit   : e99e00b2751d3d4b5516d3c6243070da53ecad12    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Thu, 17 Sep 2026 19:25:25 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Thu, 17 Sep 2026 19:25:25 +0900    

Click here for diff

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   : fcf81a52261e1dab59ba1251687708d035ca0bb0    
  
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    

Click here for diff

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   : 1ac292cb1436c788fb6ea29551b0fe459e2cb340    
  
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    

Click here for diff

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

doc: Database and tablespace size functions throws error

commit   : 3d2e8573e9cb91bd2b545184f4f9b326d237bcd1    
  
author   : Daniel Gustafsson <dgustafsson@postgresql.org>    
date     : Tue, 15 Sep 2026 13:46:52 +0200    
  
committer: Daniel Gustafsson <dgustafsson@postgresql.org>    
date     : Tue, 15 Sep 2026 13:46:52 +0200    

Click here for diff

Commit b6463ea6ef3e46 added an explicit ereport on missing object  
to pg_database_size and pg_tablespace_size, but the documentation  
wasn't updated to match.  
  
Author: Zhong ShiHao <zhong950419@gmail.com>  
Reviewed-by: Chao Li <li.evan.chao@gmail.com>  
Reviewed-by: solai v <solai.cdac@gmail.com>  
Discussion: https://postgr.es/m/CAGRkXqQSegqDnkRf344G=80mJMn78o9u=auHnx=u1jf=XpuE0A@mail.gmail.com  
Backpatch-through: 18  

M doc/src/sgml/func.sgml

Fix missing SIREAD lock on the row found by ON CONFLICT.

commit   : 6567f3f1f21353b5b0e9538d058998ab76c5a37c    
  
author   : Dean Rasheed <dean.a.rasheed@gmail.com>    
date     : Tue, 15 Sep 2026 11:47:07 +0100    
  
committer: Dean Rasheed <dean.a.rasheed@gmail.com>    
date     : Tue, 15 Sep 2026 11:47:07 +0100    

Click here for diff

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 parallel GIN index build with keys larger than 65535 bytes

commit   : 937a0e68cb55ac38341f242cf3302906e6e2ec42    
  
author   : Peter Eisentraut <peter@eisentraut.org>    
date     : Mon, 14 Sep 2026 16:04:59 +0200    
  
committer: Peter Eisentraut <peter@eisentraut.org>    
date     : Mon, 14 Sep 2026 16:04:59 +0200    

Click here for diff

During a parallel GIN build, each key is serialized into a GinTuple, a  
transient representation used only while sorting.  _gin_build_tuple()  
lays out the whole tuple (the palloc size, the key memcpy, and the  
offset of the posting list) from a local variable holding the real key  
length, but then stored that length in GinTuple.keylen, which was  
uint16.  For a key longer than 65535 bytes, the stored length was thus  
silently truncated.  
  
On read-back, GinTupleGetFirst() and _gin_parse_tuple_items()  
recompute the posting-list offset from the truncated keylen and land  
inside the key data, so ginPostingListDecodeAllSegments() decodes  
garbage: an assertion failure with assertions enabled, and a read past  
the end of the allocation without, which could in turn lead to a crash  
or garbage being written into the index.  Only parallel builds are  
affected, because only they materialize a GinTuple; a serial build of  
the same data succeeds, as index_form_tuple() compresses large keys  
before the GinMaxItemSize check.  
  
To fix, widen GinTuple.keylen to Size, which is what VARSIZE_ANY()  
returns and what GinBuffer.keylen already uses, and use Size for the  
local in _gin_build_tuple() too, which was an int that truncated  
VARSIZE_ANY() the same way.  
  
Widening keylen moves GinTuple.data.  The key value is accessed in  
place in data, so data must be MAXALIGN'ed; before, that was only true  
by accident of the field layout.  This branch predates the use of C11  
alignas, so force the alignment the way PGAlignedBlock did: put keylen  
in a union with double and int64 members, which puts data at a  
MAXALIGN'ed offset on all platforms, including 32-bit ones where Size  
is 4 bytes and MAXIMUM_ALIGNOF is 8.  A static assertion verifies the  
resulting offset.  
  
Bug: #19545  
Author: Ewan Young <kdbase.hack@gmail.com>  
Reported-by: Yuelin Wang <1217816127@qq.com>  
Reviewed-by: Peter Eisentraut <peter@eisentraut.org>  
Discussion: https://www.postgresql.org/message-id/flat/19545-0f25b7e47351e8fc%40postgresql.org  

M src/backend/access/gin/gininsert.c
M src/include/access/gin_tuple.h

Fix const-folding of JSON constructors inside a simple CASE

commit   : 29aef0f68ac89770d8623a48d33af96530deb011    
  
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    

Click here for diff

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   : 37b6e2657c09666c3b804bbc43d0e6a0dfc321dd    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Mon, 14 Sep 2026 12:28:22 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Mon, 14 Sep 2026 12:28:22 +0900    

Click here for diff

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/paramassign.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   : c66a31c21595a88ec5f38e4c92c51dbd8d42846e    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Sat, 12 Sep 2026 09:04:08 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Sat, 12 Sep 2026 09:04:08 +0900    

Click here for diff

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   : d33449024c8cb3420a664a598430125dca59cfa5    
  
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    

Click here for diff

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   : 810e63b1443c928fa7889054c46d0f3df10b32ef    
  
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    

Click here for diff

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   : 5e6c1448deff8ac198d7b4803cc537985872410c    
  
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    

Click here for diff

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.  
  
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/bin/pg_dump/pg_dump_sort.c
M src/test/modules/test_resowner/test_resowner_many.c

psql: Schema-qualify catalogs in ALTER DATABASE RESET completion

commit   : db5d375d9ab2f83d29789eabef6720e1d4446667    
  
author   : Fujii Masao <fujii@postgresql.org>    
date     : Fri, 11 Sep 2026 18:14:14 +0900    
  
committer: Fujii Masao <fujii@postgresql.org>    
date     : Fri, 11 Sep 2026 18:14:14 +0900    

Click here for diff

The tab-completion query used for ALTER DATABASE ... RESET referenced  
pg_db_role_setting and pg_database without schema qualification, so a  
same-named relation earlier in search_path could be used instead of the  
system catalogs.  This could make psql offer misleading RESET candidates.  
  
Commit dbf5a83d465 schema-qualified the unnest() call in this same query,  
but the catalog references were still left unqualified.  Fix that by  
referring to pg_catalog.pg_db_role_setting and pg_catalog.pg_database  
explicitly.  
  
Backpatch to 18, where the tab-completion for ALTER DATABASE ... RESET  
was introduced.  
  
Bug: #19523  
Reported-by: Zhou Digoal <digoal@126.com>  
Author: Vismay Tiwari <vismay.t@gmail.com>  
Reviewed-by: Paul Kim <mok03127@gmail.com>  
Reviewed-by: Kirill Reshke <reshkekirill@gmail.com>  
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>  
Discussion: https://postgr.es/m/19523-424457118202f570@postgresql.org  
Discussion: https://postgr.es/m/CALHMmB84qkCgv3QAR78YawgfqQZCxSPkRhppxzU=e6fg8RA+AA@mail.gmail.com  
Backpatch-through: 18  

M src/bin/psql/tab-complete.in.c

Fix concurrency issues with DROP TABLESPACE

commit   : d77627daac5baa895a1d37d2d12666bbf55585a5    
  
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    

Click here for diff

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   : 5d63dbd9774dcb28d7c88bfce42505376b201861    
  
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    

Click here for diff

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

Stabilize 026_overwrite_contrecord test

commit   : 8f4dfcd8e6a1ba70b3d87f8fa30b716a75cb085d    
  
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    

Click here for diff

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   : e0f5f79d2f69be061646c685e3a1a6851e742743    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Tue, 8 Sep 2026 10:06:06 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Tue, 8 Sep 2026 10:06:06 +0900    

Click here for diff

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   : c0b3980507f459c09b5324b6536c9e1c31fad378    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Sat, 5 Sep 2026 09:12:48 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Sat, 5 Sep 2026 09:12:48 +0900    

Click here for diff

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

Disallow joins whose lateral references need an unformable outer join

commit   : cbec572918fd1dd197e13e21952d2b5f0be2e942    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Fri, 4 Sep 2026 10:33:39 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Fri, 4 Sep 2026 10:33:39 +0900    

Click here for diff

If a LATERAL subquery references a PlaceHolderVar that must be  
evaluated at an outer join, the subquery's lateral_relids include that  
outer join's relid, since the lateral parameter is the outer join's  
output.  join_is_legal() verified that the rels named by a proposed  
join's minimum parameterization could still be joined to from outside,  
but it did not consider such outer-join relids.  As a result, when  
identity 3 permitted a commuted join order, we could approve a join  
that includes part of that outer join's required input, even though  
the outer join could then be completed only above the proposed join,  
leaving the lateral parameter forever unsatisfiable.  
  
In assert-enabled builds this tripped the backstop Assert in  
try_nestloop_path().  In production builds the bogus join generated  
only paths whose parameterization can never be satisfied, so they  
could never appear in a complete plan.  This not only wasted planning  
effort, but also could mislead the clauseless-join heuristics into  
thinking that legality of this join means that some other join rel  
need not be formed, and that could lead to failure to find any plan at  
all.  
  
Such joins used to be rejected by the have_dangerous_phv() check in  
join_is_legal(), which refused any join where a PlaceHolderVar's eval  
set overlapped the other side of the join without being contained in  
it.  Commit a16ef313f removed that restriction, exposing this  
oversight.  
  
To fix, teach join_is_legal() to reject a proposed join if its minimum  
parameterization includes an outer-join relid whose outer join cannot  
be formed strictly outside the join, that is, when any rel needed to  
form that outer join must become part of the proposed join's own join  
tree.  
  
Back-patch to v18, where commit a16ef313f removed the  
have_dangerous_phv() restriction.  
  
Author: Richard Guo <guofenglinux@gmail.com>  
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>  
Reviewed-by: Tender Wang <tndrwang@gmail.com>  
Discussion: https://postgr.es/m/CAMbWs49ZveAYx9T5b0=gxn0pnvqq=MbN-Z4KLbLAE+kH06mWVA@mail.gmail.com  
Backpatch-through: 18  

M src/backend/optimizer/path/joinrels.c
M src/test/regress/expected/join.out
M src/test/regress/sql/join.sql

Fix lack of message pluralization

commit   : 672f4ac4a3fcc791cfd3eb3e5dc2bb1c962c8a8c    
  
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    

Click here for diff

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   : d2943b94bcff19de5be3e84e2803a22010693f70    
  
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    

Click here for diff

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   : 8254de34bc4226756755ee585c0e24dd995e12a2    
  
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    

Click here for diff

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   : b51dd2b2c5a390dca8868675d7f01d8d3874cd7b    
  
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    

Click here for diff

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 qual pushdown past grouping through simple CASE

commit   : dd7c135bb97ec7a5dad09fa9e1e2dd9c044ceee8    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Wed, 2 Sep 2026 15:32:54 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Wed, 2 Sep 2026 15:32:54 +0900    

Click here for diff

Commit 44fb59fc6 taught the grouping-conflict walker to treat the arg  
of a simple CASE as a direct operand of each WHEN comparison, but it  
only checked the collation, on the assumption that the WHEN operator  
is always the type-default "=" and thus matches the grouping eqop.  
That assumption fails once the arg is relabeled to another type: the  
WHEN then compares under that type's "=", which need not agree with  
the grouping equality.  For instance, with a DISTINCT over a citext  
column, a qual such as "CASE t::text WHEN 'A' THEN ..." was pushed  
below the Unique, although the equivalent "t::text = 'A'" is correctly  
kept above it.  
  
Instead of special-casing the arg, have the walker bind a Var arg  
while walking the WHEN conditions and resolve each CaseTestExpr to it,  
so that the arg is checked exactly as each WHEN uses it: with the  
opfamily and collation checks of a direct operand when the WHEN is a  
comparison, and as a non-operand reference otherwise.  A non-Var arg  
is walked once as a non-operand, as before.  The CaseTestExpr in an  
ArrayCoerceExpr's elemexpr and a JsonConstructorExpr's coercion stand  
for something else and are left alone.  
  
Reported-by: Tender Wang <tndrwang@gmail.com>  
Author: Richard Guo <guofenglinux@gmail.com>  
Reviewed-by: Tender Wang <tndrwang@gmail.com>  
Reviewed-by: Ewan Young <kdbase.hack@gmail.com>  
Discussion: https://postgr.es/m/CAHewXNkvGTOgijRLjmudpg=wz0d-J8ChY2o7ksh3Be+Q_Bxwog@mail.gmail.com  
Backpatch-through: 18  

M src/backend/optimizer/util/clauses.c
M src/test/regress/expected/collate.icu.utf8.out
M src/test/regress/expected/subselect.out
M src/test/regress/sql/collate.icu.utf8.sql
M src/test/regress/sql/subselect.sql

Fix checkpointer restartpoint assertion failure

commit   : da7e446abbd5607e8a1cb6fa82f7b04097e1d12b    
  
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    

Click here for diff

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

Fix OID-counter-sensitive row ordering in foreign_key test

commit   : 6544d0a39d9c0c0e89dbcf38eb4bfb9201923816    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Wed, 2 Sep 2026 14:03:50 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Wed, 2 Sep 2026 14:03:50 +0900    

Click here for diff

Several queries in the foreign_key test sort pg_constraint rows with  
"ORDER BY oid::regclass::text".  These OIDs are constraint OIDs, not  
relation OIDs, so the regclass cast renders them as numeric strings,  
and text comparison of numeric strings depends on digit count.  If the  
OID counter crosses a power of ten while the test runs, the output  
order flips, causing spurious test failures.  This cannot happen in a  
fresh cluster, but installcheck against a long-lived cluster can hit  
it.  
  
To fix, sort by constraint name, which is unique within each of these  
queries, and use COLLATE "C" so that the order does not depend on the  
database's default collation either.  
  
Author: Richard Guo <guofenglinux@gmail.com>  
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>  
Reviewed-by: jian he <jian.universality@gmail.com>  
Discussion: https://postgr.es/m/CAMbWs49KaJFGs4rfBWk+YmHPTXZ+pJTMJ12iaz7ruZdSK+sC5w@mail.gmail.com  
Backpatch-through: 18  

M src/test/regress/expected/foreign_key.out
M src/test/regress/sql/foreign_key.sql

Remove quals made redundant by reducing outer joins to antijoins

commit   : e509be9ceea96c579c2f67329069b13515bcc556    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Wed, 2 Sep 2026 13:38:41 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Wed, 2 Sep 2026 13:38:41 +0900    

Click here for diff

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   : e075a10719c1a426d4602ffd4c52d5ee79b084b1    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Wed, 2 Sep 2026 09:19:35 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Wed, 2 Sep 2026 09:19:35 +0900    

Click here for diff

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   : 4aa640a395cab42012fd26570dafca1001a6debd    
  
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    

Click here for diff

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   : bfb08d0946e91cf73d1a3bca8c908d6dccb80735    
  
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    

Click here for diff

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   : ec434db1f49d015150cbedfe16fdf7c6456fa8d5    
  
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    

Click here for diff

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

Fix comment in attribute_stats.c

commit   : dcf14ef31cd2d867004ede87d8060716ea62619d    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Tue, 1 Sep 2026 08:12:40 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Tue, 1 Sep 2026 08:12:40 +0900    

Click here for diff

Oversight in ce207d2a7901.  
  
Author: Corey Huinker <corey.huinker@gmail.com>  
Discussion: https://postgr.es/m/CADkLM=eo7MtuCE=YjovW+=ASw1=q39qQ3qarrsw+EKfU901ztA@mail.gmail.com  
Backpatch-through: 18  

M src/backend/statistics/attribute_stats.c

Harden spell.c against out-of-order FLAG lines in Hunspell files.

commit   : 1f02a1e488f7b0f4c962a19d6b7b8e172f97d9c2    
  
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    

Click here for diff

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   : b4e76a5f6336dbb688262a67a084747a0112db43    
  
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    

Click here for diff

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   : 9f25197bf27c4c4a02d754842bc4055d83be735b    
  
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    

Click here for diff

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   : b3fa61ab7986ba980f0c03112caa3c01c4f91f0b    
  
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    

Click here for diff

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

Propagate disabled_nodes to single-child Append paths

commit   : 718f281672a91856f0d241aa5602d4d14f055a93    
  
author   : Richard Guo <rguo@postgresql.org>    
date     : Fri, 28 Aug 2026 14:51:36 +0900    
  
committer: Richard Guo <rguo@postgresql.org>    
date     : Fri, 28 Aug 2026 14:51:36 +0900    

Click here for diff

create_append_path() skips cost_append() when an Append has exactly  
one child whose parallel awareness matches its own, since setrefs.c  
strips such an Append out entirely.  In that case it copies the  
child's rowcount and costs directly, but it failed to copy  
disabled_nodes.  An Append over a disabled child therefore claimed to  
contain no disabled nodes, letting a disabled path win over one that  
is not disabled.  
  
This is a regression in v18; before e22253467, disable_cost was folded  
into a path's startup and total costs, so it rode along in the fields  
this shortcut already copies.  
  
Back-patch to v18.  This can change plans in stable branches, but only  
for installations that have explicitly disabled a node type, and only  
to stop using the node they asked us to avoid.  
  
Reported-by: Man Zeng <zengman@halodbtech.com>  
Author: Tender Wang <tndrwang@gmail.com>  
Reviewed-by: Richard Guo <guofenglinux@gmail.com>  
Reviewed-by: David Rowley <dgrowleyml@gmail.com>  
Discussion: https://postgr.es/m/CAHewXNm_Zx5EDoaD7wo7bq6cfRroNznS+RBCzT_p2-CWQXpgSw@mail.gmail.com  
Backpatch-through: 18  

M src/backend/optimizer/util/pathnode.c

Fix incorrect multi-column RANGE partition pruning

commit   : 7c503d683ca06d14052614ff39ec8648d1d722d0    
  
author   : David Rowley <drowley@postgresql.org>    
date     : Fri, 28 Aug 2026 13:11:23 +1200    
  
committer: David Rowley <drowley@postgresql.org>    
date     : Fri, 28 Aug 2026 13:11:23 +1200    

Click here for diff

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   : 2bf6144b4e8f806f9a6934a5af3999c65b3504ff    
  
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    

Click here for diff

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   : 5bf208645b1f989f2b643155d5313f9366b479d1    
  
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    

Click here for diff

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   : b90eeb29eea0cdbec236abc9fbdf8f448c26f929    
  
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    

Click here for diff

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   : 38eb0cfa3c988bcb8b388fde118c35cf37a3a9d9    
  
author   : Amit Kapila <akapila@postgresql.org>    
date     : Thu, 27 Aug 2026 09:54:16 +0530    
  
committer: Amit Kapila <akapila@postgresql.org>    
date     : Thu, 27 Aug 2026 09:54:16 +0530    

Click here for diff

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   : 8554d746964dd6ceaea827e7edbc45a1bc5948c9    
  
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    

Click here for diff

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   : 74c9832ca61037eec25b8f214fc71883494900d8    
  
author   : Peter Geoghegan <pg@bowt.ie>    
date     : Wed, 26 Aug 2026 15:38:13 -0400    
  
committer: Peter Geoghegan <pg@bowt.ie>    
date     : Wed, 26 Aug 2026 15:38:13 -0400    

Click here for diff

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   : ec074c41efbd93ddc4b882c142c15c8d5b2ae6f8    
  
author   : Masahiko Sawada <msawada@postgresql.org>    
date     : Wed, 26 Aug 2026 12:03:58 -0700    
  
committer: Masahiko Sawada <msawada@postgresql.org>    
date     : Wed, 26 Aug 2026 12:03:58 -0700    

Click here for diff

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   : 715d8397195c6435bae837446c3ae9b1dcd4221a    
  
author   : David Rowley <drowley@postgresql.org>    
date     : Thu, 27 Aug 2026 02:42:01 +1200    
  
committer: David Rowley <drowley@postgresql.org>    
date     : Thu, 27 Aug 2026 02:42:01 +1200    

Click here for diff

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   : 4c66f172a09296b08d53526f802ddd2b461bd7e8    
  
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    

Click here for diff

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

ltree/crc32.c: fix fragile code.

commit   : 8fa803dd695073a6fad49c49878b46783ac2f207    
  
author   : Jeff Davis <jdavis@postgresql.org>    
date     : Mon, 24 Aug 2026 13:33:31 -0700    
  
committer: Jeff Davis <jdavis@postgresql.org>    
date     : Mon, 24 Aug 2026 13:33:31 -0700    

Click here for diff

Explicitly make space for the NUL when casefolding.  
  
No known bug in the previous code, because the previous buffer (size  
12) was more than large enough for folding any codepoint with enough  
room left for a NUL. The builtin provider's limit is 7; ICU's limit  
seems to be 7 also; and libc always does 1:1 mappings so the real  
limit is MAX_MULTIBYTE_CHAR_LEN + 1 (size 5).  
  
Reviewed-by: Heikki Linnakangas <hlinnaka@iki.fi>  
Reviewed-by: Rithvika Devisetti <devisettirithvika@gmail.com>  
Discussion: https://postgr.es/m/2cfbc37ae8deccd3825e36b7f31c391cbf8ff9b8.camel@j-davis.com  
Backpatch-through: 18  

M contrib/ltree/crc32.c

pg_locale.c: add explanatory comments.

commit   : 364fc8e72e97fcd24a1f272879af5a64ec851277    
  
author   : Jeff Davis <jdavis@postgresql.org>    
date     : Mon, 24 Aug 2026 11:51:59 -0700    
  
committer: Jeff Davis <jdavis@postgresql.org>    
date     : Mon, 24 Aug 2026 11:51:59 -0700    

Click here for diff

Explain the purpose of case mapping functions, rather than just the  
API.  
  
Suggested-by: Andres Freund <andres@anarazel.de>  
Discussion: https://postgr.es/m/v36ssaygf7grb3qzfsjhtdzi7kqd45ds56nyuf7gi5qjml4qbb@ezmfqzmhlrs2  
Backpatch-through: 18  

M src/backend/utils/adt/pg_locale.c

Add C test function for pg_locale.h APIs.

commit   : ed37fefedb3f81cddca576bb256f162d5c036ab1    
  
author   : Jeff Davis <jdavis@postgresql.org>    
date     : Mon, 24 Aug 2026 11:51:52 -0700    
  
committer: Jeff Davis <jdavis@postgresql.org>    
date     : Mon, 24 Aug 2026 11:51:52 -0700    

Click here for diff

Test the API independently to account for fallback paths that aren't  
adequately tested from SQL.  
  
The backport to 18 also tests the previously-supported behavior where  
a size of -1 meant that the string was NUL-terminated. That behavior  
was later removed in 19.  
  
Reviewed-by: Andres Freund <andres@anarazel.de>  
Discussion: https://postgr.es/m/v36ssaygf7grb3qzfsjhtdzi7kqd45ds56nyuf7gi5qjml4qbb@ezmfqzmhlrs2  
Backpatch-through: 18  

M src/test/regress/expected/misc_functions.out
M src/test/regress/regress.c
M src/test/regress/sql/misc_functions.sql

Fix GIN posting tree page deletion with incomplete splits.

commit   : 9448c3e7ba5c40aadf07e1109685a3691d60880b    
  
author   : Peter Geoghegan <pg@bowt.ie>    
date     : Sat, 22 Aug 2026 14:50:37 -0400    
  
committer: Peter Geoghegan <pg@bowt.ie>    
date     : Sat, 22 Aug 2026 14:50:37 -0400    

Click here for diff

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   : 739f5a947d57845ec60d998e8b0e357e8b906049    
  
author   : David Rowley <drowley@postgresql.org>    
date     : Sat, 22 Aug 2026 12:49:42 +1200    
  
committer: David Rowley <drowley@postgresql.org>    
date     : Sat, 22 Aug 2026 12:49:42 +1200    

Click here for diff

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

psql: Do not let invalid \getresults affect the next query

commit   : fd9bb365db5b83249b97b788df4af4e7877fc1e0    
  
author   : Fujii Masao <fujii@postgresql.org>    
date     : Fri, 21 Aug 2026 12:36:01 +0900    
  
committer: Fujii Masao <fujii@postgresql.org>    
date     : Fri, 21 Aug 2026 12:36:01 +0900    

Click here for diff

In pipeline mode, an invalid \getresults argument could previously  
affect the next SQL command in the same pipeline. For example, after  
reporting an error for \getresults -1, psql could treat the following  
SQL command as a request to read pending pipeline results instead of  
sending it to the server, making the command appear to be skipped or  
causing missing results.  
  
This happened because psql marked \getresults as a request to read  
pipeline results before validating its optional argument. When  
validation failed, psql reported the error without running the normal  
cleanup path that clears the request.  
  
Fix this by validating the \getresults argument before marking the  
command as a request to read pipeline results. After an invalid  
argument, psql now reports the error and sends the following SQL command  
normally.  
  
Backpatch to v18, where psql pipeline meta-commands were introduced.  
  
Author: Fujii Masao <masao.fujii@gmail.com>  
Reviewed-by: Anthonin Bonnefoy <anthonin.bonnefoy@datadoghq.com>  
Discussion: https://postgr.es/m/CAHGQGwGkgM2HKZeig5hobEgUCjn7MMJux4UCcN=OfOzOEvRT0A@mail.gmail.com  
Backpatch-through: 18  

M src/bin/psql/command.c
M src/test/regress/expected/psql_pipeline.out
M src/test/regress/sql/psql_pipeline.sql

Attempt to stabilize plan of self-join test in tidscan.sql

commit   : 88d2c0e3a64ceffccd6d1347caaabb4e8a72938c    
  
author   : David Rowley <drowley@postgresql.org>    
date     : Fri, 21 Aug 2026 12:27:01 +1200    
  
committer: David Rowley <drowley@postgresql.org>    
date     : Fri, 21 Aug 2026 12:27:01 +1200    

Click here for diff

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   : 3977cde272bc38d33d73bc8c9ff5c1156fb95342    
  
author   : Peter Geoghegan <pg@bowt.ie>    
date     : Thu, 20 Aug 2026 19:47:29 -0400    
  
committer: Peter Geoghegan <pg@bowt.ie>    
date     : Thu, 20 Aug 2026 19:47:29 -0400    

Click here for diff

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

test_aio: Fix broken error recovery assertions in 001_aio

commit   : 497de6394e687e18b007f571ede7505465c6ee21    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Fri, 21 Aug 2026 07:05:13 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Fri, 21 Aug 2026 07:05:13 +0900    

Click here for diff

The three error recovery checks in `test_handle()` used "qr/^|ok$/" to  
look for the marker "ok" in psql's output.  '^' matches every string, so  
the assertions passed no matter what psql printed.  
  
Spelling the regex correctly as "qr/^ok\|$/" exposed that the explicit  
xact case was actually failing, reporting an incorrect "current  
transaction is aborted" instead of showing that an AIO handle can be  
acquired again after an error.  This is rewritten with a ROLLBACK,  
similarly to the subxact counterpart.  
  
While on it, the subxact case had no marker column in its query, so add  
one there for consistency, and reformat to use same pattern.  
  
Author: Jelte Fennema-Nio <me@jeltef.nl>  
Discussion: https://postgr.es/m/DKSU6GI1YLG5.3VF6M4IRKQ7XE@jeltef.nl  
Backpatch-through: 18  

M src/test/modules/test_aio/t/001_aio.pl

hashtext: fix fragile code.

commit   : b3510700e994a525958dc98a33d320786312d19f    
  
author   : Jeff Davis <jdavis@postgresql.org>    
date     : Thu, 20 Aug 2026 13:54:36 -0700    
  
committer: Jeff Davis <jdavis@postgresql.org>    
date     : Thu, 20 Aug 2026 13:54:36 -0700    

Click here for diff

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/pg_locale_libc.c
M src/backend/utils/adt/varchar.c

Ensure all pg_locale.h APIs work with collate_is_c.

commit   : 47c350c522e3c6624d03cbe7630c167302926881    
  
author   : Jeff Davis <jdavis@postgresql.org>    
date     : Thu, 20 Aug 2026 09:26:22 -0700    
  
committer: Jeff Davis <jdavis@postgresql.org>    
date     : Thu, 20 Aug 2026 09:26:22 -0700    

Click here for diff

Safer for callers, so that checking collate_is_c is only needed if the  
caller wants to optimize for that case.  
  
Backpatch to avoid creating a hazard for other backpatches in this  
area.  
  
Suggested-by: Andres Freund <andres@anarazel.de>  
Suggested-by: Heikki Linnakangas <hlinnaka@iki.fi>  
Discussion: https://postgr.es/m/v36ssaygf7grb3qzfsjhtdzi7kqd45ds56nyuf7gi5qjml4qbb@ezmfqzmhlrs2  
Backpatch-through: 18  

M src/backend/utils/adt/pg_locale.c

Skip bogus find_composite_type_dependencies() call on sequences

commit   : ad0256238f920dd1e8f180a97c44370dca19771a    
  
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    

Click here for diff

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

Fix postmaster failing to exit when startup crashes during crash restart

commit   : 6a1759a02aed282ab1af51207301974fa51bc33f    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Thu, 20 Aug 2026 15:17:32 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Thu, 20 Aug 2026 15:17:32 +0900    

Click here for diff

Commit 9b43e6793b0f removed the PM_STARTUP shortcut that exited the  
postmaster directly when the startup process died, routing the case  
through HandleChildCrash() so that children processes running during  
PM_STARTUP are cleaned up rather than orphaned.  
  
However, HandleChildCrash() does nothing if FatalError is already set,  
and that is exactly the case while reinitializing after a crash: a  
relaunched startup process that dies before WAL redo starts would leave  
the state machine stuck at PM_STARTUP, preventing the postmaster to shut  
down.  
  
This issue is fixed by restoring the pre-9b43e6793b0f shortcut behavior:  
if FatalError is set when the startup process crashes, signal the  
remaining children and move to PM_NO_CHILDREN, so as the postmaster can  
properly exit with nothing orphaned.  This is only reachable in v18 and  
newer versions, the early return of HandleChildCrash() on FatalError  
being introduced in f0b7ab725139.  
  
This issue has been reported on Windows, for a postmaster with its  
console gone.  A trick to make InitPostmasterChild() fail aggressively  
was equally able to stuck a postmaster.  
  
Reported-by: Kuan-Ting Kuo <m0935388420@gmail.com>  
Author: Zexin Li <lizi.openmind@gmail.com>  
Discussion: https://postgr.es/m/19623-f9bd331940be1273@postgresql.org  
Backpatch-through: 18  

M src/backend/postmaster/postmaster.c

Reject too many arguments in CREATE TRIGGER

commit   : d6cde5b6b1bbcc13dbbcdbf85e81682f186a4c07    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Thu, 20 Aug 2026 09:38:29 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Thu, 20 Aug 2026 09:38:29 +0900    

Click here for diff

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   : d7ce15e4de6fdfcab6fb813e74daba5511e64a4b    
  
author   : Peter Geoghegan <pg@bowt.ie>    
date     : Wed, 19 Aug 2026 15:45:52 -0400    
  
committer: Peter Geoghegan <pg@bowt.ie>    
date     : Wed, 19 Aug 2026 15:45:52 -0400    

Click here for diff

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

pg_locale.c, unicode_case.c: use size_t for iteration.

commit   : 3ca22c7b7ccb1f8897ca6d05f4e6c4934c537eeb    
  
author   : Jeff Davis <jdavis@postgresql.org>    
date     : Wed, 19 Aug 2026 10:36:44 -0700    
  
committer: Jeff Davis <jdavis@postgresql.org>    
date     : Wed, 19 Aug 2026 10:36:44 -0700    

Click here for diff

Already done by e615da8cb21b in master. Backpatch these particular  
cases to make other backpatches in this area safer, and to be safe for  
callers in extensions that we don't know about.  
  
This applies to REL_19_STABLE and REL_18_STABLE only.  
  
Suggested-by: Andres Freund <andres@anarazel.de>  
Reviewed-by: Andres Freund <andres@anarazel.de>  
Discussion: https://postgr.es/m/v36ssaygf7grb3qzfsjhtdzi7kqd45ds56nyuf7gi5qjml4qbb@ezmfqzmhlrs2  

M src/backend/utils/adt/pg_locale.c
M src/common/unicode_case.c

Fix GIN multiple-VACUUM-scans pending list bug.

commit   : b0a36462bf28ad233f016f4b8ad3ce021ff9b76b    
  
author   : Peter Geoghegan <pg@bowt.ie>    
date     : Wed, 19 Aug 2026 13:46:42 -0400    
  
committer: Peter Geoghegan <pg@bowt.ie>    
date     : Wed, 19 Aug 2026 13:46:42 -0400    

Click here for diff

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   : 9900966c7d598fedb72850c4d0db2d5ac6986d37    
  
author   : Peter Geoghegan <pg@bowt.ie>    
date     : Wed, 19 Aug 2026 12:31:05 -0400    
  
committer: Peter Geoghegan <pg@bowt.ie>    
date     : Wed, 19 Aug 2026 12:31:05 -0400    

Click here for diff

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   : 8753b6904e861f15c4889c7e0623580e357860b9    
  
author   : Fujii Masao <fujii@postgresql.org>    
date     : Wed, 19 Aug 2026 12:37:09 +0900    
  
committer: Fujii Masao <fujii@postgresql.org>    
date     : Wed, 19 Aug 2026 12:37:09 +0900    

Click here for diff

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   : 789ae09a5f21487083c379727e434b7ba88fab6b    
  
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    

Click here for diff

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   : 3f01b0c4a826382837abc741020586e12777ad97    
  
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    

Click here for diff

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   : ecf2492b44545dd2cf0bf6411bf3bdbb6a5b5f22    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Wed, 19 Aug 2026 11:32:51 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Wed, 19 Aug 2026 11:32:51 +0900    

Click here for diff

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   : 39faf4de206ec05764c4e151507fdcefc6612929    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Wed, 19 Aug 2026 09:51:54 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Wed, 19 Aug 2026 09:51:54 +0900    

Click here for diff

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   : b6193ff038552762dca38ff6c3b046f873ccd96f    
  
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    

Click here for diff

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

pg_locale.c: comment improvements.

commit   : 02a2137c1ae521c09050e3d1614dd5d79c4107db    
  
author   : Jeff Davis <jdavis@postgresql.org>    
date     : Tue, 18 Aug 2026 13:19:59 -0700    
  
committer: Jeff Davis <jdavis@postgresql.org>    
date     : Tue, 18 Aug 2026 13:19:59 -0700    

Click here for diff

Add missing comments and fix outdated/incorrect comments in  
pg_locale.c and related files.  
  
Suggested-by: Andres Freund <andres@anarazel.de>  
Discussion: https://postgr.es/m/v3nniwcrxejmcfvz56xbd22hphprqleuornd6hqkmw2bl7kgmz@cnytz2ee5ltk  
Backpatch-through: 18  

M src/backend/utils/adt/pg_locale.c
M src/backend/utils/adt/pg_locale_builtin.c
M src/backend/utils/adt/pg_locale_icu.c
M src/include/utils/pg_locale.h

Fix stream abort for a transaction that was never streamed.

commit   : caa463e3f8d2b9c1441e6a59c50f90b8598439f3    
  
author   : Masahiko Sawada <msawada@postgresql.org>    
date     : Tue, 18 Aug 2026 11:56:55 -0700    
  
committer: Masahiko Sawada <msawada@postgresql.org>    
date     : Tue, 18 Aug 2026 11:56:55 -0700    

Click here for diff

Commit 072ee847ad4 taught logical decoding to discard the changes of a  
transaction that is already known to be aborted when it is picked for  
eviction. That path reuses ReorderBufferTruncateTXN(), which marks  
every subtransaction that still has in-memory changes as  
streamed. Since nothing is streamed in that path, and the top-level  
transaction is never marked, a subtransaction ends up flagged as  
streamed even though the output plugin has never seen it. Decoding the  
subsequent abort record then makes ReorderBufferAbort() invoke the  
stream_abort callback for that subtransaction.  
  
For pgoutput this sends a Stream Abort ('A') message to a subscriber  
that requested streaming = off, and it does so regardless of the  
negotiated protocol version, so even a client speaking a version that  
predates transaction streaming receives a message it cannot  
parse. test_decoding dereferences a NULL pointer and crashes, since it  
allocates its per-transaction state in the begin or stream start  
callback, neither of which runs for a transaction discarded as  
aborted.  
  
This commit fixes this by marking a subtransaction as streamed only  
when it has changes and its top-level transaction is already marked as  
streamed. All streaming call sites mark the top-level transaction  
before truncating it, so their behavior is unchanged, while the  
abort-discard path never marks the top-level transaction and therefore  
now leaves its subtransactions unmarked.  
  
Backpatch to v18, where commit 072ee847ad4 was introduced.  
  
Bug: #19616  
Reported-by: Tyler Smart <tyler@smarts.io>  
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>  
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>  
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>  
Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com>  
Discussion: https://postgr.es/m/19616-f6153af509910853@postgresql.org  
Backpatch-through: 18  

M contrib/test_decoding/expected/stream.out
M contrib/test_decoding/sql/stream.sql
M src/backend/replication/logical/reorderbuffer.c

test_json_parser: Fix broken file ref and inverted result check

commit   : 607c9033a39306fe10221d2211aa7f99125c4997    
  
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    

Click here for diff

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: Fix description of log_line_prefix %L in release notes

commit   : db06f97899cc286cc37421d41b97f16a02fa8411    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Tue, 18 Aug 2026 15:33:02 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Tue, 18 Aug 2026 15:33:02 +0900    

Click here for diff

%L was described as showing in output the client IP, but it shows the  
local IP of the server connected to.  
  
Author: Keisuke Kuroda <keisuke.kuroda.3862@gmail.com>  
Reviewed-by: Greg Sabino Mullane <htamfids@gmail.com>  
Discussion: https://postgr.es/m/CANDwggK8AZJx+tGKY+h_8a90rqZ_+4dvGOFvDuRVVSjQvAafbg@mail.gmail.com  
Backpatch-through: 18, only  

M doc/src/sgml/release-18.sgml

pg_locale.c: Fix REL_18_STABLE for srclen < 0.

commit   : 27e2afb492cd553093c92ea917d24651b5a9ae16    
  
author   : Jeff Davis <jdavis@postgresql.org>    
date     : Mon, 17 Aug 2026 17:55:42 -0700    
  
committer: Jeff Davis <jdavis@postgresql.org>    
date     : Mon, 17 Aug 2026 17:55:42 -0700    

Click here for diff

Ensure that srclen >= 0 before calling strlower_c(), etc.  
  
Commit 5f003855e7 backported strlower_c(), etc., to 18, but did not  
account for the srclen < 0 behavior that existed in 18 (and was  
removed later in version 19 with commit 6d22c67c3b).  
  
No callers in core use the srclen < 0 behavior, but it's possible  
(though unlikely) that extensions do.  
  
This applies to REL_18_STABLE only.  
  
Discussion: https://postgr.es/m/v36ssaygf7grb3qzfsjhtdzi7kqd45ds56nyuf7gi5qjml4qbb@ezmfqzmhlrs2  

M src/backend/utils/adt/pg_locale.c

Doc: clean up documentation about text search datatype limits.

commit   : 171f1249474c8b69f3fb11e6655ed9cee8e491f9    
  
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    

Click here for diff

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   : 69915dac4b7dcbd9880f5f1370cec5303b07043f    
  
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    

Click here for diff

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   : 184292588d685766c1299f02296cb0ecdb1065fe    
  
author   : Peter Geoghegan <pg@bowt.ie>    
date     : Mon, 17 Aug 2026 17:31:09 -0400    
  
committer: Peter Geoghegan <pg@bowt.ie>    
date     : Mon, 17 Aug 2026 17:31:09 -0400    

Click here for diff

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   : af659efcb3a15dad690e9c9666ed82ed350e5522    
  
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    

Click here for diff

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

psql: Fix \d+ display of REPLICA IDENTITY NOTHING

commit   : baa7b142aace6821ce085906f314a75bcc4d95c8    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Mon, 17 Aug 2026 09:21:08 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Mon, 17 Aug 2026 09:21:08 +0900    

Click here for diff

Commit 18954ce7f69 has replaced hardcoded relreplident values in  
describe.c with CppAsString2 macros, but used a DEFAULT instead of a  
NOTHING at one location.  This caused \d+ on a table with REPLICA  
IDENTITY NOTHING to show incorrect data.  
  
Author: Shinya Kato <shinya11.kato@gmail.com>  
Discussion: https://postgr.es/m/CAOzEurRHWt+9XLBrbXQOSVkE45NksJ4F28twuqgZ7veW0RzpUQ@mail.gmail.com  
Backpatch-through: 18  

M src/bin/psql/describe.c
M src/test/regress/expected/replica_identity.out
M src/test/regress/sql/replica_identity.sql

Fix ASAN failure after flex errors in GUC file parsing

commit   : 0391fee2dc30c801fc17a1eeb6a82448d359e052    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Mon, 17 Aug 2026 08:53:29 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Mon, 17 Aug 2026 08:53:29 +0900    

Click here for diff

As detected by ASAN, the scanner value used when parsing GUC files can  
be indeterminate when the flex error handler sigjumps to the old cleanup  
path, before yylex_init() is called.  
  
The flex scanner state is now made volatile in ParseConfigFp(), since  
its value is assigned after sigsetjmp() and cna be accessed after  
siglongjmp().  yylex_init() cannot use a volatile pointer; a temporary  
variable is used before assigning the result of yylex_init() to it.  
  
While on it, yy_create_buffer() is changed to detect the case where it  
returns a NULL value.  Based on my read of the flex code, this cannot be  
reached currently.  Future upstream changes or changes in the error  
logic of the GUC file parsing could make that reachable, and it is four  
extra lines of code.  
  
Oversight in d663f150b5ed.  
  
Reported-by: Ilia Kashintsev <ilia.kashintsev@gmail.com>  
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>  
Reviewed-by: Andrey Rachitskiy <pl0h0yp1@gmail.com>  
Discussion: https://postgr.es/m/19612-24ccb4fc6da7786f@postgresql.org  
Backpatch-through: 18  

M src/backend/utils/misc/guc-file.l

doc: Clarify the logging collector's guarantees.

commit   : 247ef8481d89c04554702a0d18ec5455fb19f321    
  
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    

Click here for diff

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   : d41b3e5865d4953430a41ec0dd32e9afb14eaea8    
  
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    

Click here for diff

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   : bcb776671d9b654ca753e4500faee8e95b61d6b5    
  
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    

Click here for diff

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   : 0468246f5dce721af1f0dd0c465f81c028128a98    
  
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    

Click here for diff

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   : a68beda425a0471230f39ef9852d5dffc38ee7c1    
  
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    

Click here for diff

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

Reject REPLICA IDENTITY USING INDEX on column with invalid NOT NULL

commit   : 864ef0fb1c91e83dff143d6de3f6f85390b6415b    
  
author   : Álvaro Herrera <alvherre@kurilemu.de>    
date     : Wed, 12 Aug 2026 19:15:48 +0200    
  
committer: Álvaro Herrera <alvherre@kurilemu.de>    
date     : Wed, 12 Aug 2026 19:15:48 +0200    

Click here for diff

ALTER TABLE ... REPLICA IDENTITY USING INDEX verified key columns by  
reading pg_attribute.attnotnull, but commit a379061a22a8 made  
attnotnull true also for unvalidated (NOT VALID) not-null constraints,  
which do not prove the column null-free.  An index over such a column  
could thus be marked as replica identity even though the column might  
contain NULLs, causing apply-side divergence for UPDATE/DELETE on the  
nullable rows.  
  
Fix by additionally requiring convalidated for the underlying  
constraint, mirroring the fix d9ffc27291f applied to ATExecAddIdentity  
for the analogous identity-column case.  
  
Author: Ante Krešić <ante@tigerdata.com>  
Reviewed-by: Aleksander Alekseev <aleksander@tigerdata.com>  
Reviewed-by: solai v <solai.cdac@gmail.com>  
Backpatch-through: 18  
Discussion: https://postgr.es/m/CABXQ4dJUibZzN91qvWmsfA7MUDn9YRCNyu3CcukdyokbH1=41Q@mail.gmail.com  

M src/backend/commands/tablecmds.c
M src/test/regress/expected/replica_identity.out
M src/test/regress/sql/replica_identity.sql

Fix poll_query_until() timeout handling with an undefined query in Cluster.pm

commit   : cfa6884677ce05dce7fd1103c2fa1f71ce02d4b1    
  
author   : Michael Paquier <michael@paquier.xyz>    
date     : Wed, 12 Aug 2026 16:23:12 +0900    
  
committer: Michael Paquier <michael@paquier.xyz>    
date     : Wed, 12 Aug 2026 16:23:12 +0900    

Click here for diff

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   : bdd37402b6c881e790398537e847267097bc6b15    
  
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    

Click here for diff

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