1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00
Commit Graph

62805 Commits

Author SHA1 Message Date
Fujii Masao
d7d45526e1 Fix a race in 035_standby_logical_decoding.pl
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
2026-10-22 09:22:53 +09:00
Masahiko Sawada
a5b925cfcc Reject currtid2() on unlogged tables during recovery.
Commit 1e707d3256 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 1e707d3256.

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
2026-10-21 11:10:41 -07:00
Masahiko Sawada
794c29dc41 Reject COPY TO of unlogged tables during recovery.
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
2026-10-21 10:44:09 -07:00
Tom Lane
fd97624fb5 findtimezone: update Windows timezone name list for recent events.
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
2026-10-21 11:52:51 -04:00
Fujii Masao
4272618fbb doc: Correct psql's \d+x documentation for sequences
Commit 00f4c2959d 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
2026-10-21 23:39:01 +09:00
Richard Guo
f6642a97fe Fix lateral references in sort keys added by createplan.c
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
2026-10-21 18:02:01 +09:00
Richard Guo
5fb5bef3e4 Fix sorting a child rel by an expression containing a PlaceHolderVar
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
2026-10-21 15:39:07 +09:00
Amit Kapila
23fffd382c Fix logical replication with lossy identity indexes.
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
2026-10-21 08:34:27 +05:30
Michael Paquier
2c056b37f9 Fix cleanup of reused pgstats entries in pgstat_release_entry_ref()
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
2026-10-21 10:55:31 +09:00
Tom Lane
eb1454ecc6 Fix cross-version upgrade tests for commits 0497318e4 et al.
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.
2026-10-20 18:49:30 -04:00
Tom Lane
4ee8af2e86 Fix OLD/NEW aliases that EXPLAIN prints in RETURNING SubPlans.
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
2026-10-20 17:32:31 -04:00
Tom Lane
b08c4df3b7 In deparsing, parenthesize IS DOCUMENT where it is an operand.
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
2026-10-20 14:29:02 -04:00
Tom Lane
e79df9dd9f Fix two issues in deparsing.
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
2026-10-19 17:51:31 -04:00
David Rowley
e9fa8382df Fix a few mistakes in partition aggregate tests
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
2026-10-20 10:34:01 +13:00
David Rowley
9ba69ffcc4 Recompute allocChunkLimit when reusing an AllocSet context
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
2026-10-19 19:43:34 +13:00
John Naylor
52af12e9d9 Fix use-after-free in jsonb_set_lax for Const NULL new_value
Commit a83586b554 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
2026-10-19 11:34:06 +07:00
Richard Guo
17b45abefb Don't remove an RTE_RESULT needed by a nested PlaceHolderVar
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
2026-10-19 11:03:27 +09:00
Peter Geoghegan
4732a5c53b Fix nbtree unsatisfiable row compare assertion failure.
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
2026-10-17 11:02:49 -04:00
Richard Guo
d7938ff308 Fix bogus outer-join commutation with lateral refs in PlaceHolderVars
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
2026-10-17 17:19:23 +09:00
Richard Guo
01a9e573fa Fix skipping of join quals for appendrel children
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
2026-10-17 10:30:04 +09:00
Fujii Masao
92d47d544b Avoid spurious failures in the login event trigger recovery test
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 bec61f5935, 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
2026-10-17 00:19:40 +09:00
Fujii Masao
dfbc1507cd doc: Correct the description of server behavior after an OOM kill
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
2026-10-16 22:43:29 +09:00
Richard Guo
707b3738df Fix bogus Assert in find_var_for_subquery_tle
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
2026-10-16 16:46:35 +09:00
Michael Paquier
368e12de25 Fix argument given to buffer_readv_encode_error()
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 047cba7fa0.

Author: Antonin Houska <ah@cybertec.at>
Reviewed-by: Steven Niu <niushiji@gmail.com>
Discussion: https://postgr.es/m/8073.1791959482@localhost
Backpatch-through: 18
2026-10-16 14:30:48 +09:00
David Rowley
7731648243 Add sentinel_ok() check to BumpCheck().
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
2026-10-16 18:00:06 +13:00
Tom Lane
10b2d9986b Simplify restriction matching in self-join elimination.
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
2026-10-15 12:42:46 -04:00
Peter Geoghegan
7a28f1d028 Fix _bt_readnextpage half-dead leftmost page assert.
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
2026-10-15 12:17:18 -04:00
Tom Lane
8a59a526de Update v18 .abi-compliance-history for changes in reorderbuffer.h.
This also documents c7328770b and 57193b3a0, which cancelled each
other out but still need to be reflected in the ABI history.
2026-10-15 11:39:19 -04:00
Heikki Linnakangas
4d62ef92fd Fix CreateThread() failure checks
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
2026-10-15 12:04:44 +03:00
Heikki Linnakangas
94d53e9ec6 Fix checks for aborted updating transaction in multixids
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
2026-10-15 11:23:12 +03:00
David Rowley
57193b3a09 Fix EPQ run-time pruning using the parent's PARAM_EXEC values
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
2026-10-15 15:50:11 +13:00
Tom Lane
ebf82fae68 Fix self-join elimination to avoid inheritance scans.
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
2026-10-14 16:38:15 -04:00
Tom Lane
18aca7257e Clear more substructure of a dead relation during join removal.
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
2026-10-14 15:37:28 -04:00
Peter Geoghegan
f4284d9aa6 Fix hash bucket split buffer pin leak.
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
2026-10-14 14:26:04 -04:00
Tom Lane
529e08c329 Doc: improve documentation of the JSON object aggregates.
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
2026-10-14 14:00:48 -04:00
Álvaro Herrera
5eae2800a8 Reorderbuffer: handle catalog-change-bearing aborted subxacts better
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 8c67d29fd5 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
2026-10-14 19:42:04 +02:00
John Naylor
fc834ccab5 Plug holes in SP-GiST quad tree getQuadrant()
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
2026-10-14 18:19:39 +07:00
Fujii Masao
f47c63d7a4 pg_basebackup: Report correct size estimate for incremental backups
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
2026-10-14 19:56:40 +09:00
Fujii Masao
04ddc52427 doc: Clarify that base backup progress counts uncompressed archive data
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
2026-10-14 19:53:46 +09:00
David Rowley
0aba37abfe Accumulate Memoize parallel worker stats across rescans
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
2026-10-14 22:07:53 +13:00
Richard Guo
70ffc30e12 Don't use SubPlan-containing expressions for startup-time pruning
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
2026-10-14 14:56:08 +09:00
Richard Guo
7432ecba18 Disallow joins whose lateral references can never be satisfied
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
2026-10-14 14:31:54 +09:00
Richard Guo
64fb20a523 Give antijoins made from sublinks a relid
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
2026-10-14 12:09:56 +09:00
Michael Paquier
d21a3e1fe5 Fix potential use-after-frees in WAL sender
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 879ad9f90e 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
2026-10-14 12:05:12 +09:00
Fujii Masao
c6a51ad94a Add missing FreeDir() in CheckTablespaceDirectory()
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
2026-10-14 11:11:51 +09:00
Michael Paquier
942266c7f4 Release memory allocated by ndistinct_for_combination()
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 83772cc78e and ad8a166ca8 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
2026-10-14 10:16:19 +09:00
Tom Lane
476b9eb3c4 Delay qp_callback processing till after join removal.
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
2026-10-13 17:53:20 -04:00
Tom Lane
154e2f1015 Fix two errors in extraction of GIN indexquals from jsonpath queries.
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
2026-10-13 16:33:35 -04:00
Peter Geoghegan
a85b5523e6 Fix hash index scans that start during a bucket split.
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
2026-10-10 10:26:18 -04:00
Tom Lane
3c6135f839 Doc: remove some annoying zero-width spaces.
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
2026-10-09 21:14:58 -04:00