summaryrefslogtreecommitdiff
AgeCommit message (Collapse)Author
15 hoursTolerate partial pgstats entries in pgstat_gc_entry_refs()REL_17_STABLEMichael Paquier
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
20 hoursFix assertion after aborting internal subtransaction at transaction endFujii Masao
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
40 hoursUse the join collation when unique-ifying a semijoin's RHSAlexander Korotkov
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
3 daysFix missing SIREAD lock on the row found by ON CONFLICT.Dean Rasheed
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
4 daysFix const-folding of JSON constructors inside a simple CASERichard Guo
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
4 daysFix stale copies of PHVs in subqueriesRichard Guo
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
6 daysdoc: Fix output column name for pg_stat_get_backend_subxact()Michael Paquier
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
6 daysFix option argument lookup in in-tree getopt_long().Nathan Bossart
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
6 dayspg_ctl: Silence warnings about unused global variablesPeter Eisentraut
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
6 daysRemove unused global variablesPeter Eisentraut
The new clang 23 has a new warning about set-but-unused static (internal-linkage) global variables: -Wunused-but-set-global, which is activated in PostgreSQL builds via -Wall. This triggers a few warnings in PostgreSQL code. This commit removes several such variables that were either never used or whose last use was removed some time ago. For pq_init_crypto_lib, we apply the same #ifdef HAVE_CRYPTO_LOCK that the other uses of the variable already have, so that the variable is either fully used or fully nonexistent, depending on the configuration, not half-way. Reviewed-by: Andreas Karlsson <andreas@proxel.se> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Discussion: https://www.postgresql.org/message-id/flat/eb013f9d-2247-444e-8815-9d17b4ce78e7%40eisentraut.org
8 daysFix concurrency issues with DROP TABLESPACEAndrew Dunstan
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
10 daysRevert "Mark modified the FSM buffer as dirty during recovery"Alexander Korotkov
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>
10 daysdoc: Remove unnecessary CVE items from release notesMichael Paquier
The following CVEs were listed in the release notes of some stable branches, but they should not: - CVE-2026-16238, not on v17 and older - CVE-2026-14676, not on v17 and older - CVE-2026-14681, not on v16 and older - CVE-2026-14672, not on v15 and older Author: Yogesh Sharma <yogesh.sharma@catprosystems.com> Discussion: https://postgr.es/m/340842e1-31ce-4551-9d79-71de490f67e9@CatProSystems.com Backpatch-through: 14-17
10 daysStabilize 026_overwrite_contrecord testFujii Masao
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
10 daysFix nestloop parameter handling for PlaceHolderVars in child joinsRichard Guo
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
13 daysFix incorrect error message in xlogreader.cMichael Paquier
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
14 daysssl: Fix expected output for OpenSSL 4Daniel Gustafsson
When adding support for OpenSSL 4, one occurrence of OpenSSL error message matching was missed in the backpatches to PostgreSQL 17 and 16. Reported-by: Christoph Berg <cb@df7cb.de> Discussion: https://postgr.es/m/apky-gqiLVtFkR-w@msg.df7cb.de Backpatch-through: 16,17
2026-09-03Fix lack of message pluralizationPeter Eisentraut
Fixups for commits b99b74144f9, 79b101486c1, bf7d19be9b1.
2026-09-03Avoid backend hang during temp table cleanup in deferrable transactionsFujii Masao
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
2026-09-03Stabilize recovery conflict stats checks in 031_recovery_conflict.plFujii Masao
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
2026-09-02Stabilize xid_wraparound/t/002_limits.pl test.Tom Lane
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
2026-09-02Fix checkpointer restartpoint assertion failureFujii Masao
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
2026-09-02Remove quals made redundant by reducing outer joins to antijoinsRichard Guo
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
2026-09-02Speed up pg_waldump TAP testMichael Paquier
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
2026-09-01doc: clarify aliasing of VALUES in FROM clausesFujii Masao
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
2026-09-01Handle PG_INT32_MIN negation overflow in right()Daniel Gustafsson
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
2026-09-01Fix integer to_char() overflow with V formatFujii Masao
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
2026-08-29Harden spell.c against out-of-order FLAG lines in Hunspell files.Tom Lane
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
2026-08-28Update .abi-compliance-history for join removal fix.Tom Lane
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
2026-08-28Perform join removal by editing the query's jointree.Tom Lane
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
2026-08-28Make platform guards in two regression tests match meson buildsAndrew Dunstan
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>
2026-08-28Fix incorrect multi-column RANGE partition pruningDavid Rowley
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
2026-08-27Fix temporary WAL receiver slot handling on timeline switchesFujii Masao
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
2026-08-27Stabilize 019_replslot_limitFujii Masao
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
2026-08-27Don't create a shell type for function returning an arrayHeikki Linnakangas
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
2026-08-27Don't choose an invalid index for REPLICA IDENTITY FULL lookups.Amit Kapila
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
2026-08-26Pin two ctype-dependent test_regex_utf8 cases to pg_c_utf8Andrew Dunstan
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>
2026-08-26Export subxip[] for snapshots taken during recovery.Peter Geoghegan
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
2026-08-26Fix crash in subscription refresh on concurrent relation drop.Masahiko Sawada
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
2026-08-26Close relations opened specifically for AFTER triggersDavid Rowley
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
2026-08-25Don't assume DISTINCT ON implies uniqueness when the tlist has SRFsRichard Guo
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
2026-08-22Fix GIN posting tree page deletion with incomplete splits.Peter Geoghegan
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
2026-08-22Doc: fix typoDavid Rowley
Author: Jochen Bandhauer <jb@jbitc.de> Discussion: https://postgr.es/m/69ecfa04-9177-42fd-8d5d-9f375669fc5b@jbitc.de Backpatch-through: 14
2026-08-21Attempt to stabilize plan of self-join test in tidscan.sqlDavid Rowley
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
2026-08-20Fix snapshot import xmin ProcArrayLock bug.Peter Geoghegan
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
2026-08-20hashtext: fix fragile code.Jeff Davis
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
2026-08-20Skip bogus find_composite_type_dependencies() call on sequencesHeikki Linnakangas
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
2026-08-20Reject too many arguments in CREATE TRIGGERMichael Paquier
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
2026-08-19GiST: Invalidate killed items consistently.Peter Geoghegan
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
2026-08-19Fix GIN multiple-VACUUM-scans pending list bug.Peter Geoghegan
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