Compare commits

...

157 Commits

Author SHA1 Message Date
Fujii Masao
84914e964b pg_restore: Fix wrong descriptions of --with-{schema,data,statistics} options.
Commit bde2fb797aa added the --with-schema, --with-data, and --with-statistics
options to pg_restore. These options control whether to restore schema, data,
or statistics if present in the archive. However, the help message and
documentation incorrectly described them as affecting what gets dumped.

This commit corrects those descriptions to clarify that the options control
restoration, not dumping.

Bug: #18952
Reported-by: TAKATSUKA Haruka <harukat@sraoss.co.jp>
Author: Fujii Masao <masao.fujii@gmail.com>
Reviewed-by: TAKATSUKA Haruka <harukat@sraoss.co.jp>
Reviewed-by: Daniel Gustafsson <daniel@yesql.se>
Discussion: https://postgr.es/m/18952-be40a620f8b1e755@postgresql.org
2025-06-12 23:25:21 +09:00
Álvaro Herrera
0f65f3eec4
Fix squashing algorithm for query texts
The algorithm to squash lists of constants added by commit 62d712ecfd94
was a bit too simplistic; we wanted to avoid adding unnecessary
complexity, but cases like direct function calls of typecasting
functions (and others) were missed, and bogus SQL syntax was being shown
in pg_stat_statements normalized query text field.  To fix normalization
for those cases, we need the parser to transmit information about were
each list of constant values starts and ends, so add that to a couple of
nodes.  Also add a few more test cases to make sure we're doing the
right thing.

The patch initially submitted by Sami added a new private struct in
gram.y to carry the start/end information for A_Expr, but I (Álvaro)
decided that a better fix was to remove the parser indirection via the
in_expr production, and instead create separate components in the a_expr
rule.  I'm surprised that this works and doesn't require more changes,
but I assume (without checking) that the grammar used to be more complex
and got simplified at some point.

Bump catversion.

Author: Sami Imseih <samimseih@gmail.com>
Author: Dmitry Dolgov <9erthalion6@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/CAA5RZ0tRXoPG2y6bMgBCWNDt0Tn=unRerbzYM=oW0syi1=C1OA@mail.gmail.com
2025-06-12 14:21:21 +02:00
Fujii Masao
f7b11414e9 doc: Document that MAINTAIN privilege allows statistics manipulation functions.
Database object statistics manipulation functions were introduced
in PostgreSQL 18 and are permitted under the MAINTAIN privilege.
However, the documentation previously did not mention these functions
in the list of allowed operations.

This commit updates the MAINTAIN privilege documentation to
explicitly include statistics manipulation functions, clarifying
what the privilege covers.

Author: Fujii Masao <masao.fujii@gmail.com>
Reviewed-by: Robert Treat <rob@xzilla.net>
Discussion: https://postgr.es/m/7c7e1ad5-fdf9-486f-bc63-40ac99b0461d@oss.nttdata.com
2025-06-12 14:53:32 +09:00
Michael Paquier
f85f6ab051 Revert support for improved tracking of nested queries
This commit reverts the two following commits:
- 499edb09741b, track more precisely query locations for nested
statements.
- 06450c7b8c70, a follow-up fix of 499edb09741b with query locations.
The test introduced in this commit is not reverted.  This is proving
useful to track a problem that only pgaudit was able to detect.

These prove to have issues with the tracking of SELECT statements, when
these use multiple parenthesis which is something supported by the
grammar.  Incorrect location and lengths are causing pg_stat_statements
to become confused, failing its job in query normalization with
potential out-of-bound writes because the location and the length may
not match with what can be handled.  A lot of the query patterns
discussed when this issue was reported have no test coverage in the main
regression test suite, or the recovery test 027_stream_regress.pl would
have caught the problems as pg_stat_statements is loaded by the node
running the regression tests.  A first step would be to improve the test
coverage to stress more the query normalization logic.

A different portion of this work was done in 45e0ba30fc40, with the
addition of tests for nested queries.  These can be left in the tree.
They are useful to track the way inner queries are currently tracked by
PGSS with non-top-level entries, and will be useful when reconsidering
in the future the work reverted here.

Reported-by: Alexander Kozhemyakin <a.kozhemyakin@postgrespro.ru>
Discussion: https://postgr.es/m/18947-cdd2668beffe02bf@postgresql.org
2025-06-12 10:08:55 +09:00
Peter Geoghegan
dd2ce37927 Revert "nbtree: Remove useless row compare arg."
This reverts commit 54c6ea8c81db718508eeea50991d3c1c5dff54a5.

Further analysis has shown that the forcenonrequired row compare
behavior is in fact necessary, despite the new restrictions on
RowCompares imposed by _bt_set_startikey following commit 5f4d98d4.

Discussion: https://postgr.es/m/CAH2-Wzm3bKcz3TbHGem3_+SinEyG=VZVPbApQghp7YiZj+MM3g@mail.gmail.com
2025-06-11 18:16:15 -04:00
Jeff Davis
e1458f2f1b Revert a few small patches that were intended for version 19.
- 4c787a24e7e220a60022e47c1776f22f72902899
- 78bd364ee39ca70a8f9cb8719282389866a08e14
- 7a6880fadc177873d5663961ec3a02d67e34dcbe
- 8898082a5d3e94eef073f0e08124137e096e78ef

Suggested-by: Robert Haas <robertmhaas@gmail.com>
Discussion: https://postgr.es/m/CA+TgmoZ=J=PVNZUNKaxULu+KUVSt3Y-aJ1DZ9Y3Co6mu0z62jA@mail.gmail.com
Discussion: https://postgr.es/m/60e8c6d0a6c08e67f15dbbe9e53df0119c710065.camel@j-davis.com
2025-06-11 15:10:12 -07:00
Masahiko Sawada
b774ad4933 Add tab completion for REJECT_LIMIT option.
This addresses an oversight in commit 4ac2a9bec, which introduced the
REJECT_LIMIT option to the COPY command.

Author: Atsushi Torikoshi <torikoshia@oss.nttdata.com>
Reviewed-by: Yugo Nagata <nagata@sraoss.co.jp>
Discussion: https://postgr.es/m/ac23e824d1d602f113a89c91ee56fb23@oss.nttdata.com
2025-06-11 11:44:25 -07:00
Peter Geoghegan
7c319f5491 Make _bt_killitems drop pins it acquired itself.
Teach nbtree's _bt_killitems to leave the so->currPos page that it sets
LP_DEAD items on in whatever state it was in when _bt_killitems was
called.  In particular, make sure that so->dropPin scans don't acquire a
pin whose reference is saved in so->currPos.buf.

Allowing _bt_killitems to change so->currPos.buf like this is wrong.
The immediate consequence of allowing it is that code in _bt_steppage
(that copies so->currPos into so->markPos) will behave as if the scan is
a !so->dropPin scan.  so->markPos will therefore retain the buffer pin
indefinitely, even though _bt_killitems only needs to acquire a pin
(along with a lock) for long enough to mark known-dead items LP_DEAD.

This issue came to light following a report of a failure of an assertion
from recent commit e6eed40e.  The test case in question involves the use
of mark and restore.  An initial call to _bt_killitems takes place that
leaves so->currPos.buf in a state that is inconsistent with the scan
being so->dropPin.  A subsequent call to _bt_killitems for the same
position (following so->currPos being saved in so->markPos, and then
restored as so->currPos) resulted in the failure of an assertion that
tests that so->currPos.buf is InvalidBuffer when the scan is so->dropPin
(non-assert builds got a "resource was not closed" WARNING instead).

The same problem exists on earlier releases, though the issue is far
more subtle there.  Recent commit e6eed40e introduced the so->dropPin
field as a partial replacement for testing so->currPos.buf directly.
Earlier releases won't get an assertion failure (or buffer pin leak),
but they will allow the second _bt_killitems call from the test case to
behave as if a buffer pin was consistently held since the original call
to _bt_readpage.  This is wrong; there will have been an initial window
during which no pin was held on the so->currPos page, and yet the second
_bt_killitems call will neglect to check if so->currPos.lsn continues to
match the page's now-current LSN.

As a result of all this, it's just about possible that _bt_killitems
will set the wrong items LP_DEAD (on release branches).  This could only
happen with merge joins (the sole user of nbtree mark/restore support),
when a concurrently inserted index tuple used a recently-recycled TID
(and only when the new tuple was inserted onto the same page as a
distinct concurrently-removed tuple with the same TID).  This is exactly
the scenario that _bt_killitems' check of the page's now-current LSN
against the LSN stashed in currPos was supposed to prevent.

A follow-up commit will make nbtree completely stop conditioning whether
or not a position's pin needs to be dropped on whether the 'buf' field
is set.  All call sites that might need to drop a still-held pin will be
taught to rely on the scan-level so->dropPin field recently introduced
by commit e6eed40e.  That will make bugs of the same general nature as
this one impossible (or make them much easier to detect, at least).

Author: Peter Geoghegan <pg@bowt.ie>
Reported-By: Alexander Lakhin <exclusion@gmail.com>
Discussion: https://postgr.es/m/545be1e5-3786-439a-9257-a90d30f8b849@gmail.com
Backpatch-through: 13
2025-06-11 09:17:35 -04:00
Michael Paquier
361499538c psql: Remove PARTITION BY clause in tab completion for unlogged tables
CREATE UNLOGGED TABLE was still being recommended by psql's tab
completion as a possible pattern, but the backend is rejecting this
option since e2bab2d79204.

Reported-by: Shinya Kato <shinya11.kato@gmail.com>
Reviewed-by: Nathan Bossart <nathandbossart@gmail.com>
Reviewed-by: Shinya Kato <shinya11.kato@gmail.com>
Discussion: https://postgr.es/m/CAOzEurQZ1a+6d1K8b=+Ww1NFQVwAt9KSCQsBWXYBaPnYCenK3g@mail.gmail.com
2025-06-11 09:27:28 +09:00
Tom Lane
137935bd11 Don't reduce output request size on non-Unix-socket connections.
Traditionally, libpq's pqPutMsgEnd has rounded down the amount-to-send
to be a multiple of 8K when it is eagerly writing some data.  This
still seems like a good idea when sending through a Unix socket, as
pipes typically have a buffer size of 8K or some fraction/multiple of
that.  But there's not much argument for it on a TCP connection, since
(a) standard MTU values are not commensurate with that, and (b) the
kernel typically applies its own packet splitting/merging logic.

Worse, our SSL and GSSAPI code paths both have API stipulations that
if they fail to send all the data that was offered in the previous
write attempt, we mustn't offer less data in the next attempt; else
we may get "SSL error: bad length" or "GSSAPI caller failed to
retransmit all data needing to be retried".  The previous write
attempt might've been pqFlush attempting to send everything in the
buffer, so pqPutMsgEnd can't safely write less than the full buffer
contents.  (Well, we could add some more state to track exactly how
much the previous write attempt was, but there's little value evident
in such extra complication.)  Hence, apply the round-down only on
AF_UNIX sockets, where we never use SSL or GSSAPI.

Interestingly, we had a very closely related bug report before,
which I attempted to fix in commit d053a879b.  But the test case
we had then seemingly didn't trigger this pqFlush-then-pqPutMsgEnd
scenario, or at least we failed to recognize this variant of the bug.

Bug: #18907
Reported-by: Dorjpalam Batbaatar <htgn.dbat.95@gmail.com>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/18907-d41b9bcf6f29edda@postgresql.org
Backpatch-through: 13
2025-06-10 18:39:34 -04:00
Jeff Davis
8898082a5d inet_net_pton.c: use pg_ascii_tolower() rather than tolower().
Avoid dependence on setlocale(). No behavior change.

Discussion: https://postgr.es/m/9875f7f9-50f1-4b5d-86fc-ee8b03e8c162@eisentraut.org
Reviewed-by: Peter Eisentraut <peter@eisentraut.org>
2025-06-10 11:23:20 -07:00
Jeff Davis
7a6880fadc isn.c: use pg_ascii_toupper() instead of toupper().
Avoid dependence on setlocale(). No behavior change.

Discussion: https://postgr.es/m/9875f7f9-50f1-4b5d-86fc-ee8b03e8c162@eisentraut.org
Reviewed-by: Peter Eisentraut <peter@eisentraut.org>
2025-06-10 11:23:11 -07:00
Jeff Davis
78bd364ee3 contrib/spi/refint.c: use pg_ascii_tolower() instead.
Avoid dependence on setlocale(). No behavior change.

Discussion: https://postgr.es/m/9875f7f9-50f1-4b5d-86fc-ee8b03e8c162@eisentraut.org
Reviewed-by: Peter Eisentraut <peter@eisentraut.org>
2025-06-10 11:23:05 -07:00
Jeff Davis
4c787a24e7 copyfromparse.c: use pg_ascii_tolower() rather than tolower().
Avoid dependence on setlocale(). No behavior change.

Discussion: https://postgr.es/m/9875f7f9-50f1-4b5d-86fc-ee8b03e8c162@eisentraut.org
Reviewed-by: Peter Eisentraut <peter@eisentraut.org>
2025-06-10 11:22:57 -07:00
Peter Eisentraut
3feff3916e Use exported symbols list on macOS for loadable modules as well
On macOS, when building with the make system, the exported symbols
list $(SHLIB_EXPORTS) was ignored.  This was probably not intentional,
it was probably just forgotten, since that combination has never
actually been used until now (for libpq-oauth).

The meson build system handles this correctly.  Also, other platforms
have been doing this correctly.

This fixes it.  It also does a bit of refactoring to make the code
match the layout for other platforms.

Reviewed-by: Jacob Champion <jacob.champion@enterprisedb.com>
Discussion: https://www.postgresql.org/message-id/flat/c70ca32e-b109-460d-9810-6e23ebb4473f%40eisentraut.org
2025-06-10 07:04:43 +02:00
Tom Lane
166b4f4560 pg_restore: fix incompatibility with old directory-format dumps.
pg_restore failed to restore large objects (blobs) out of
directory-format dumps made by versions before PG v12.
That's because, due to a bug fixed in commit 548e50976, those
old versions put the wrong filename into the BLOBS TOC entry.
Said bug was harmless before v17, because we ignored the
incorrect filename field --- but commit a45c78e32 assumed it
would be correct.

Reported-by: Pavel Stehule <pavel.stehule@gmail.com>
Author: Pavel Stehule <pavel.stehule@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAFj8pRCrZ=_e1Rv1N+6vDaH+6gf=9A2mE2J4RvnvKA1bLiXvXA@mail.gmail.com
Backpatch-through: 17
2025-06-08 17:06:39 -04:00
Etsuro Fujita
7d4667c620 Revert "postgres_fdw: Inherit the local transaction's access/deferrable modes."
We concluded that commit e5a3c9d9b is a feature rather than a fix; since
it was added after feature freeze, revert it.

Reported-by: Fujii Masao <masao.fujii@oss.nttdata.com>
Reported-by: Michael Paquier <michael@paquier.xyz>
Reported-by: Robert Haas <robertmhaas@gmail.com>
Discussion: https://postgr.es/m/ed2296f1-1a6b-4932-b870-5bb18c2591ae%40oss.nttdata.com
2025-06-08 17:30:00 +09:00
Bruce Momjian
73e26cbeb5 doc PG 18 relnotes: add AFTER trigger user change item
Reported-by: Noah Misch

Discussion: https://postgr.es/m/20250603172123.5f.nmisch@google.com
2025-06-07 11:25:17 -04:00
Bruce Momjian
37e5f0b61f doc PG 18 relnotes: adjust wording of initdb item 48814415d5a
And move to the top of the incompatibility list.  This will impact users
more than any other incompatibility item because of pg_upgrade.
2025-06-07 11:06:47 -04:00
Peter Eisentraut
1a857348e4 plpython: Remove obsolete test expected file
Move plpython_error_5.out to plpython_error.out, since the pre-3.5
version is no longer needed, since we raised the Python requirement to
3.6 (commit 45363fca637).

Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Jacob Champion <jacob.champion@enterprisedb.com>
Discussion: https://www.postgresql.org/message-id/d620e7c6-becc-4a8e-9b43-eea0da55faf2@eisentraut.org
2025-06-07 09:04:29 +02:00
Jeff Davis
5b40feab59 Improve CREATE DATABASE error message for invalid libc locale.
Discussion: https://postgr.es/m/73959a14-267b-49c1-8293-291b175682cb@manitou-mail.org
Reviewed-by: Daniel Verite <daniel@manitou-mail.org>
2025-06-06 15:28:51 -07:00
Nathan Bossart
a31767fc09 Use NULL instead of 0 for pointer arguments.
Commit 5fe08c006c fixed this for calls to dshash_create().  This
commit fixes calls to dshash_attach() and dsa_create_in_place().

Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/aECi_gSD9JnVWQ8T%40nathan
2025-06-06 12:08:17 -05:00
Nathan Bossart
304862973e Fixed signed/unsigned mismatch in test_dsm_registry.
Oversight in commit 8b2bcf3f28.

Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com>
Discussion: https://postgr.es/m/aECi_gSD9JnVWQ8T%40nathan
Backpatch-through: 17
2025-06-06 11:40:52 -05:00
Peter Geoghegan
e6eed40e44 Avoid BufferGetLSNAtomic() calls during nbtree scans.
Delay calling BufferGetLSNAtomic() until we finish reading a page that
actually contains items that btgettuple will return to the executor.
This reduces the number of calls during plain index scans (we'll only
call BufferGetLSNAtomic() when _bt_readpage returns true), and totally
eliminates calls during index-only scans, bitmap index scans, and plain
index scans of an unlogged relation.

Currently, when checksums (or wal_log_hints) are enabled, acquiring a
page's LSN in BufferGetLSNAtomic() involves locking the buffer header
(which involves the use of spinlocks).  Testing has shown that enabling
page-level checksums causes large regressions with certain workloads,
especially on larger multi-socket systems.

The regression isn't tied to any Postgres 18 commit.  However, Postgres
18 commit 04bec894 made initdb use checksums by default, so it seems
prudent to address the problem now.

Author: Peter Geoghegan <pg@bowt.ie>
Reviewed-By: Tomas Vondra <tomas@vondra.me>
Discussion: https://postgr.es/m/941f0190-e3c6-4622-9ac7-c04e936e5fdb@vondra.me
Discussion: https://postgr.es/m/CAH2-Wzk-Dg5XWs_jDuiHt4_7ryrSY+n=vxmHY51EVqPDFsKXmg@mail.gmail.com
2025-06-06 10:19:44 -04:00
Robert Haas
016e407f4b pg_prewarm: Allow autoprewarm to use more than 1GB to dump blocks.
Reported-by: Daria Shanina <vilensipkdm@gmail.com>
Author: Daria Shanina <vilensipkdm@gmail.com>
Author: Robert Haas <robertmhaas@gmail.com>
Backpatch-through: 13
2025-06-06 08:18:27 -04:00
Tom Lane
c37be39a74 Doc: improve description of which role runs a trigger.
Refine wording from commit 01463e1cc.

Author: Noah Misch <noah@leadboat.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/20250605163441.2f.nmisch@google.com
2025-06-05 15:24:15 -04:00
Peter Geoghegan
54c6ea8c81 nbtree: Remove useless row compare arg.
Use of a RowCompare key makes nbtree index scans ineligible to use
pstate.forcenonrequired following recent bugfix commit 5f4d98d4.
There's no longer any need for _bt_check_rowcompare to accept a
forcenonrequired argument, so remove it.
2025-06-05 14:50:43 -04:00
Álvaro Herrera
e6f98d8848
Avoid bogus scans of partitions when marking FKs enforced
Similar to commit cc733ed164c5: when an unenforced foreign key that
references a partitioned table is altered to be enforced, we scan
the constrained table based on each partition on the referenced
partitioned table.  This is bogus and likely to cause the ALTER TABLE to
fail: we must only scan the constrained table as pointing to the
top-level partitioned table.  Oversight in commit eec0040c4bcd.  Fix by
eliding those scans.

Author: Amul Sul <sulamul@gmail.com>
Reported-by: jian he <jian.universality@gmail.com>
Discussion: https://postgr.es/m/CACJufxF1e_gPOLtsDoaE4VCgQPC8KZW_kPAjPR5Rvv4Ew=fb2A@mail.gmail.com
2025-06-05 18:39:06 +02:00
Tom Lane
04acad82b0 Doc: you must own the target object to use SECURITY LABEL.
For some reason this wasn't mentioned before.

Author: Patrick Stählin <me@packi.ch>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/931e012a-57ba-41ba-9b88-24323a46dec5@packi.ch
Backpatch-through: 13
2025-06-05 11:30:12 -04:00
Álvaro Herrera
cc733ed164
Avoid bogus scans of partitions when validating FKs to partitioned tables
Validating an unvalidated foreign key that references a partitioned
table would try to queue validations for each individual partition of
the referenced table, but this is wrong: each individual partition would
not necessarily have all the referenced rows, so errors would be raised.
Avoid doing that.  The pg_constraint rows that cause this to happen are
only there to support the action triggers that implement the DELETE/
UPDATE actions of the FK, so no validating scan is necessary.

This was an oversight in commit b663b9436e75.

An equivalent oversight exists for NOT ENFORCED constraints, which is
not fixed in this commit.

Author: Amul Sul <sulamul@gmail.com>
Reported-by: Antonin Houska <ah@cybertec.at>
Reviewed-by: jian he <jian.universality@gmail.com>
Reviewed-by: Tender Wang <tndrwang@gmail.com>
Discussion: https://postgr.es/m/26983.1748418675@localhost
2025-06-05 17:17:13 +02:00
Tom Lane
4b05ebf095 Change role names used in trigger test.
The choices made in commit 01463e1cc might pose copyright hazards,
and are more cutesy than informative anyway.

Reported-by: Noah Misch <noah@leadboat.com>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/20250415155850.9b.nmisch@google.com
2025-06-05 11:05:53 -04:00
Magnus Hagander
112e40b867 psql: fix order of join clauses when listing extensions
Commit d696406a9b2 added a new join to the query for extensions, but did
so in the wrong place, causing the AND clause to be applied to the wrong
join.

Author:	Suraj Kharage <suraj.kharage@enterprisedb.com>
Reviewed-By: Dilip Kumar <dilipbalaut@gmail.com>
Discussion: https://postgr.es/m/CAF1DzPVBrN-cmPB2zb7ZU=2J4vEF2fNdArGCG9w+9fnKq4v8tg@mail.gmail.com
2025-06-05 09:54:16 +02:00
Michael Paquier
b87163e5f3 Fix copy-pasto with process count calculation in method_io_uring.c
This commit replaces the formula used for "TotalProcs" with a call to
pgaio_uring_procs() in pgaio_uring_shmem_init() for the shared memory
initialization, which is exactly the same, removing a duplication.

pgaio_uring_procs() is used for shared memory sizing and a sanity check,
and it has some documentation explaining some reasoning behind the
formula.

Author: Japin Li <japinli@hotmail.com>
Discussion: https://postgr.es/m/ME0P300MB044521067A1EDDA9EDEC3793B66DA@ME0P300MB0445.AUSP300.PROD.OUTLOOK.COM
2025-06-05 09:39:24 +09:00
Nathan Bossart
f9b1192190 doc: Remove notes about "unencrypted" passwords.
The documentation for the pg_authid system catalog and the
pg_shadow system view indicates that passwords might be stored in
cleartext, but that hasn't been possible for some time.

Oversight in commit eb61136dc7.

Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/aD2yKkZro4nbl5ol%40nathan
Backpatch-through: 13
2025-06-04 09:47:25 -05:00
Peter Eisentraut
30c15987d9 doc: Update description of pg_constraint.convalidated
The previous description listed the constraint types that this column
was used for, but that was outdated, since not-valid not-null
constraints are now possible.  So just remove that qualification,
rather than trying to keep it updated.

Author: jian he <jian.universality@gmail.com>
Reviewed-by: Robert Treat <rob@xzilla.net>
Discussion: https://www.postgresql.org/message-id/flat/CACJufxFo4yTwzbSZrP%2BzQiR6_M00skoZMFaUnNJCdY6he%3DuQfA%40mail.gmail.com
2025-06-04 15:27:44 +02:00
Peter Eisentraut
48814415d5 doc PG 18 relnotes: Add incompatibility note about checksums now default
Reviewed-by: Tomas Vondra <tomas@vondra.me>
Discussion: https://www.postgresql.org/message-id/flat/CAKAnmmKwiMHik5AHmBEdf5vqzbOBbcwEPHo4-PioWeAbzwcTOQ%40mail.gmail.com
2025-06-04 12:06:08 +02:00
Peter Eisentraut
f777d77387 Don't strip $libdir from LOAD command
Commit 4f7f7b03758 implemented the extension_control_path GUC, and to
make it work it was decided that we should strip the $libdir/ on
module_pathname from .control files, so that extensions don't need to
worry about this change.

This strip logic was implemented on expand_dynamic_library_name()
which works fine when executing the SQL functions from extensions, but
this function is also called when the LOAD command is executed, and
since the user may explicitly pass the $libdir prefix on LOAD
parameter, we should not strip in this case.

This commit fixes this issue by moving the strip logic from
expand_dynamic_library_name() to load_external_function() that is
called when the running the SQL script from extensions.

Reported-by: Evan Si <evsi@amazon.com>
Author: Matheus Alcantara <matheusssilv97@gmail.com>
Reviewed-by: Nathan Bossart <nathandbossart@gmail.com>
Reviewed-by: Rahila Syed <rahilasyed90@gmail.com>
Bug: #18920
Discussion: https://www.postgresql.org/message-id/flat/18920-b350b1c0a30af006%40postgresql.org
2025-06-04 11:38:12 +02:00
Michael Paquier
7f3381c7ee psql: Abort connection when using \syncpipeline after COPY TO/FROM
When the backend reads COPY data, it ignores all sync messages, as per
c01641f8aed0.  With psql pipelines, it is possible to manually send sync
messages with \sendpipeline which leaves the frontend in an
unrecoverable state as the backend will not send the necessary
ReadyForQuery message that is expected to feed psql result consumption
logic.

It could be possible to artificially reduce the piped_syncs and
requested_results, however libpq's state would still have queued sync
messages in its command queue, and the only way to consume those without
directly calling pqCommandQueueAdvance() is to process ReadyForQuery
messages that won't be sent since the backend ignores these.  Perhaps
this could be improved in the future, but I am not really excited about
introducing this amount of complications in libpq to manipulate the
message queues without a better use case to support it.

Hence, this patch aborts the connection if we detect excessive sync
messages after a COPY in a pipeline to avoid staying in an inconsistent
protocol state, which is the best thing we can do with pipelines in
psql for now.  Note that this change does not prevent wrapping a set
of queries inside a block made of \startpipeline and \endpipeline, only
the use of \syncpipeline for a COPY.

Reported-by: Nikita Kalinin <n.kalinin@postgrespro.ru>
Author: Anthonin Bonnefoy <anthonin.bonnefoy@datadoghq.com>
Discussion: https://postgr.es/m/18944-8a926c30f68387dd@postgresql.org
2025-06-04 09:01:29 +09:00
Peter Eisentraut
58fbfde152 Fix incorrect format placeholders 2025-06-03 21:38:04 +02:00
Noah Misch
0e164eb9f4 Fix a pg_dump scenario for platforms where SEEK_CUR != 1.
POSIX allows such platforms.  Given the lack of complaints, we may not
currently test on such a platform.  This is new in v18 (commit
7d5c83b4e90c7156655f98b7312a30ae5eeb4d27), so no back-patch.
2025-06-03 11:18:52 -07:00
Fujii Masao
73bdcfab35 Rename log_lock_failure GUC to log_lock_failures for consistency.
This commit renames the GUC log_lock_failure to log_lock_failures
to align with the existing similar setting log_lock_waits, which uses
the plural form. This improves naming consistency across related GUCs.

Suggested-by: Peter Eisentraut <peter@eisentraut.org>
Author: Fujii Masao <masao.fujii@gmail.com
Reviewed-by: Peter Eisentraut <peter@eisentraut.org>
Discussion: https://postgr.es/m/7a8198b6-d5b8-4910-b41e-8d3efcbb015d@eisentraut.org
2025-06-03 10:02:55 +09:00
Tom Lane
aa87f69c00 Disallow "=" in names of reloptions and foreign-data options.
We store values for these options as array elements with the syntax
"name=value", hence a name containing "=" confuses matters when
it's time to read the array back in.  Since validation of the
options is often done (long) after this conversion to array format,
that leads to confusing and off-point error messages.  We can
improve matters by rejecting names containing "=" up-front.

(Probably a better design would have involved pairs of array
elements, but it's too late now --- and anyway, there's no
evident use-case for option names like this.  We already
reject such names in some other contexts such as GUCs.)

Reported-by: Chapman Flack <jcflack@acm.org>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Chapman Flack <jcflack@acm.org>
Discussion: https://postgr.es/m/6830EB30.8090904@acm.org
Backpatch-through: 13
2025-06-02 15:22:44 -04:00
Melanie Plageman
31a7e175fd Correct heap vacuum boundary state setup ordering
052026c9b9 mistakenly reordered setup steps in heap_vacuum_rel(),
incorrectly moving RelationGetNumberOfBlocks() before
vacuum_get_cutoffs().

OldestXmin must be determined before RelationGetNumberOfBlocks()
calculates the number of blocks in the relation that will be vacuumed.
Otherwise tuples older than OldestXmin may be inserted into the end of
the relation into blocks that are not vacuumed. If additional tuples
newer than those inserted into unscanned blocks but older than
OldestXmin are inserted into free space earlier in the relation, the
result could be advancing pg_class.relfrozenxid to a newer value than an
unfrozen XID in one of the unscanned heap pages.

Assigning an incorrect relfrozenxid can lead to data loss, so it is
imperative that it correctly reflect the oldest unfrozen xid.

Reported-by: Peter Geoghegan <pg@bowt.ie>
Author: Melanie Plageman <melanieplageman@gmail.com>
Reviewed-by: Peter Geoghegan <pg@bowt.ie>
Discussion: https://postgr.es/m/CAH2-WzntqvVEdbbpqG5JqSZGuLWmy4PBfUO-OswfivKchr2gvw%40mail.gmail.com
2025-06-02 10:54:07 -04:00
Peter Eisentraut
fc32be3c94 Fix incorrect format placeholders
Fixes for return type of dclist_count().
2025-06-02 10:12:58 +02:00
Peter Eisentraut
32edf732e8 Rename gist stratnum support function
Commit 7406ab623fe added a gist support function that we internally
refer to by the symbol GIST_STRATNUM_PROC.  This translated from
"well-known" strategy numbers to opfamily-specific strategy numbers.
However, we later (commit 630f9a43cec) changed this to fit into
index-AM-level compare type mapping, so this function actually now
maps from compare type to opfamily-specific strategy numbers.  So this
name is no longer fitting.

Moreover, the index AM level also supports the opposite, a function to
map from strategy number to compare type.  This is currently not
supported in gist, but one might wonder what this function is supposed
to be called when it is added.

This patch changes the naming of the gist-level functionality to be
more in line with the index-AM-level functionality.  This makes sense
because these are essentially the same thing on different levels.
This also changes the names of the externally visible functions that
are provided for use as such a support function.

Reviewed-by: Paul A Jungwirth <pj@illuminatedcomputing.com>
Discussion: https://www.postgresql.org/message-id/37ebb1d9-9036-485f-a215-e55435689917%40eisentraut.org
2025-06-02 08:41:27 +02:00
Michael Paquier
5231ed8262 Use replay LSN as target for cascading logical WAL senders
A cascading WAL sender doing logical decoding (as known as doing its
work on a standby) has been using as flush LSN the value returned by
GetStandbyFlushRecPtr() (last position safely flushed to disk).  This is
incorrect as such processes are only able to decode changes up to the
LSN that has been replayed by the startup process.

This commit changes cascading logical WAL senders to use the replay LSN,
as returned by GetXLogReplayRecPtr().  This distinction is important
particularly during shutdown, when WAL senders need to send any
remaining available data to their clients, switching WAL senders to a
caught-up state.  Using the latest flush LSN rather than the replay LSN
could cause the WAL senders to be stuck in an infinite loop preventing
them to shut down, as the startup process does not run when WAL senders
attempt to catch up, so they could keep waiting for work that would
never happen.

Backpatch down to v16, where logical decoding on standbys has been
introduced.

Author: Alexey Makhmutov <a.makhmutov@postgrespro.ru>
Reviewed-by: Ajin Cherian <itsajin@gmail.com>
Reviewed-by: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/52138028-7246-421c-9161-4fa108b88070@postgrespro.ru
Backpatch-through: 16
2025-06-02 12:03:59 +09:00
Tom Lane
c98975ba85 Add commit 4672b6223 to .git-blame-ignore-revs. 2025-06-01 14:58:42 -04:00
Tom Lane
4672b62239 Run pgindent on the previous commit.
Clean up after rearranging PG_TRY blocks.

Author: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/2954090.1748723636@sss.pgh.pa.us
Backpatch-through: 13
2025-06-01 14:55:24 -04:00
Tom Lane
c6f7f11d8f Fix edge-case resource leaks in PL/Python error reporting.
PLy_elog_impl and its subroutine PLy_traceback intended to avoid
leaking any PyObject reference counts, but their coverage of the
matter was sadly incomplete.  In particular, out-of-memory errors
in most of the string-construction subroutines could lead to
reference count leaks, because those calls were outside the
PG_TRY blocks responsible for dropping reference counts.

Fix by (a) adjusting the scopes of the PG_TRY blocks, and
(b) moving the responsibility for releasing the reference counts
of the traceback-stack objects to PLy_elog_impl.  This requires
some additional "volatile" markers, but not too many.

In passing, fix an ancient thinko: use of the "e_module_o" PyObject
was guarded by "if (e_type_s)", where surely "if (e_module_o)"
was meant.  This would only have visible consequences if the
"__name__" attribute were present but the "__module__" attribute
wasn't, which apparently never happens; but someday it might.

Rearranging the PG_TRY blocks requires indenting a fair amount
of code one more tab stop, which I'll do separately for clarity.

Author: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/2954090.1748723636@sss.pgh.pa.us
Backpatch-through: 13
2025-06-01 14:48:35 -04:00
Etsuro Fujita
e5a3c9d9b5 postgres_fdw: Inherit the local transaction's access/deferrable modes.
Previously, postgres_fdw always 1) opened a remote transaction in READ
WRITE mode even when the local transaction was READ ONLY, causing a READ
ONLY transaction using it that references a foreign table mapped to a
remote view executing a volatile function to write in the remote side,
and 2) opened the remote transaction in NOT DEFERRABLE mode even when
the local transaction was DEFERRABLE, causing a SERIALIZABLE READ ONLY
DEFERRABLE transaction using it to abort due to a serialization failure
in the remote side.

To avoid these, modify postgres_fdw to open a remote transaction in the
same access/deferrable modes as the local transaction.  This commit also
modifies it to open a remote subtransaction in the same access mode as
the local subtransaction.

Although these issues exist since the introduction of postgres_fdw,
there have been no reports from the field.  So it seems fine to just fix
them in master only.

Author: Etsuro Fujita <etsuro.fujita@gmail.com>
Reviewed-by: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAPmGK16n_hcUUWuOdmeUS%2Bw4Q6dZvTEDHb%3DOP%3D5JBzo-M3QmpQ%40mail.gmail.com
2025-06-01 17:30:00 +09:00
Dean Rasheed
b006bcd531 Fix MERGE into a plain inheritance parent table.
When a MERGE's target table is the parent of an inheritance tree, any
INSERT actions insert into the parent table using ModifyTableState's
rootResultRelInfo. However, there are two bugs in the way is
initialized:

1. ExecInitMerge() incorrectly uses a different ResultRelInfo entry
from ModifyTableState's resultRelInfo array to build the insert
projection, which may not be compatible with rootResultRelInfo.

2. ExecInitModifyTable() does not fully initialize rootResultRelInfo.
Specifically, ri_WithCheckOptions, ri_WithCheckOptionExprs,
ri_returningList, and ri_projectReturning are not initialized.

This can lead to crashes, or incorrect query results due to failing to
check WCO's or process the RETURNING list for INSERT actions.

Fix both these bugs in ExecInitMerge(), noting that it is only
necessary to fully initialize rootResultRelInfo if the MERGE has
INSERT actions and the target table is a plain inheritance parent.

Backpatch to v15, where MERGE was introduced.

Reported-by: Andres Freund <andres@anarazel.de>
Author: Dean Rasheed <dean.a.rasheed@gmail.com>
Reviewed-by: Jian He <jian.universality@gmail.com>
Reviewed-by: Tender Wang <tndrwang@gmail.com>
Discussion: https://postgr.es/m/4rlmjfniiyffp6b3kv4pfy4jw3pciy6mq72rdgnedsnbsx7qe5@j5hlpiwdguvc
Backpatch-through: 15
2025-05-31 12:12:58 +01:00
Michael Paquier
e050af2868 Change internal plan ID type from uint64 to int64
uint64 was chosen to be consistent with the type used by the query ID,
but the conclusion of a recent discussion for the query ID is that int64
is a better fit as the signed form is shown to the user, for PGSS or
EXPLAIN outputs.

This commit changes the plan ID to use int64, following c3eda50b0648
that has done the same for the query ID.

The plan ID is new to v18, introduced in 2a0cd38da5cc.

Author: Michael Paquier <michael@paquier.xyz>
Reviewed-by: Sami Imseih <samimseih@gmail.com>
Discussion: https://postgr.es/m/aCvzJNwetyEI3Sgo@paquier.xyz
2025-05-31 09:40:45 +09:00
Nathan Bossart
706054b11b Ensure we have a snapshot when updating various system catalogs.
A few places that access system catalogs don't set up an active
snapshot before potentially accessing their TOAST tables.  To fix,
push an active snapshot just before each section of code that might
require accessing one of these TOAST tables, and pop it shortly
afterwards.  While at it, this commit adds some rather strict
assertions in an attempt to prevent such issues in the future.

Commit 16bf24e0e4 recently removed pg_replication_origin's TOAST
table in order to fix the same problem for that catalog.  On the
back-branches, those bugs are left in place.  We cannot easily
remove a catalog's TOAST table on released major versions, and only
replication origins with extremely long names are affected.  Given
the low severity of the issue, fixing older versions doesn't seem
worth the trouble of significantly modifying the patch.

Also, on v13 and v14, the aforementioned strict assertions have
been omitted because commit 2776922201, which added
HaveRegisteredOrActiveSnapshot(), was not back-patched.  While we
could probably back-patch it now, I've opted against it because it
seems unlikely that new TOAST snapshot issues will be introduced in
the oldest supported versions.

Reported-by: Alexander Lakhin <exclusion@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/18127-fe54b6a667f29658%40postgresql.org
Discussion: https://postgr.es/m/18309-c0bf914950c46692%40postgresql.org
Discussion: https://postgr.es/m/ZvMSUPOqUU-VNADN%40nathan
Backpatch-through: 13
2025-05-30 15:17:28 -05:00
Tom Lane
232d8caeaa Fix memory leakage in postgres_fdw's DirectModify code path.
postgres_fdw tries to use PG_TRY blocks to ensure that it will
eventually free the PGresult created by the remote modify command.
However, it's fundamentally impossible for this scheme to work
reliably when there's RETURNING data, because the query could fail
in between invocations of postgres_fdw's DirectModify methods.
There is at least one instance of exactly this situation in the
regression tests, and the ensuing session-lifespan leak is visible
under Valgrind.

We can improve matters by using a memory context reset callback
attached to the ExecutorState context.  That ensures that the
PGresult will be freed when the ExecutorState context is torn
down, even if control never reaches postgresEndDirectModify.

I have little faith that there aren't other potential PGresult
leakages in the backend modules that use libpq.  So I think it'd
be a good idea to apply this concept universally by creating
infrastructure that attaches a reset callback to every PGresult
generated in the backend.  However, that seems too invasive for
v18 at this point, let alone the back branches.  So for the
moment, apply this narrow fix that just makes DirectModify safe.
I have a patch in the queue for the more general idea, but it
will have to wait for v19.

Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Matheus Alcantara <matheusssilv97@gmail.com>
Discussion: https://postgr.es/m/2976982.1748049023@sss.pgh.pa.us
Backpatch-through: 13
2025-05-30 13:45:41 -04:00
Tom Lane
d98cefe114 Allow larger packets during GSSAPI authentication exchange.
Our GSSAPI code only allows packet sizes up to 16kB.  However it
emerges that during authentication, larger packets might be needed;
various authorities suggest 48kB or 64kB as the maximum packet size.
This limitation caused login failure for AD users who belong to many
AD groups.  To add insult to injury, we gave an unintelligible error
message, typically "GSSAPI context establishment error: The routine
must be called again to complete its function: Unknown error".

As noted in code comments, the 16kB packet limit is effectively a
protocol constant once we are doing normal data transmission: the
GSSAPI code splits the data stream at those points, and if we change
the limit then we will have cross-version compatibility problems
due to the receiver's buffer being too small in some combinations.
However, during the authentication exchange the packet sizes are
not determined by us, but by the underlying GSSAPI library.  So we
might as well just try to send what the library tells us to.
An unpatched recipient will fail on a packet larger than 16kB,
but that's not worse than the sender failing without even trying.
So this doesn't introduce any meaningful compatibility problem.

We still need a buffer size limit, but we can easily make it be
64kB rather than 16kB until transport negotiation is complete.
(Larger values were discussed, but don't seem likely to add
anything.)

Reported-by: Chris Gooch <cgooch@bamfunds.com>
Fix-suggested-by: Jacob Champion <jacob.champion@enterprisedb.com>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Jacob Champion <jacob.champion@enterprisedb.com>
Discussion: https://postgr.es/m/DS0PR22MB5971A9C8A3F44BCC6293C4DABE99A@DS0PR22MB5971.namprd22.prod.outlook.com
Backpatch-through: 13
2025-05-30 12:55:15 -04:00
Fujii Masao
961553daf5 Make XactLockTableWait() and ConditionalXactLockTableWait() interruptable more.
Previously, XactLockTableWait() and ConditionalXactLockTableWait() could enter
a non-interruptible loop when they successfully acquired a lock on a transaction
but the transaction still appeared to be running. Since this loop continued
until the transaction completed, it could result in long, uninterruptible waits.

Although this scenario is generally unlikely since XactLockTableWait() and
ConditionalXactLockTableWait() can basically acquire a transaction lock
only when the transaction is not running, it can occur in a hot standby.
In such cases, the transaction may still appear active due to
the KnownAssignedXids list, even while no lock on the transaction exists.
For example, this situation can happen when creating a logical replication
slot on a standby.

The cause of the non-interruptible loop was the absence of CHECK_FOR_INTERRUPTS()
within it. This commit adds CHECK_FOR_INTERRUPTS() to the loop in both functions,
ensuring they can be interrupted safely.

Back-patch to all supported branches.

Author: Kevin K Biju <kevinkbiju@gmail.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/CAM45KeELdjhS-rGuvN=ZLJ_asvZACucZ9LZWVzH7bGcD12DDwg@mail.gmail.com
Backpatch-through: 13
2025-05-31 00:08:40 +09:00
David Rowley
c3eda50b06 Change internal queryid type from uint64 to int64
uint64 was perhaps chosen in cff440d36 as the type was uint32 prior to
that widening work.

Having this as uint64 doesn't make much sense and just adds the overhead of
having to remember that we always output this in its signed form.  Let's
remove that overhead.

The signed form output is seemingly required since we have no way to
represent the full range of uint64 in an SQL type.  We use BIGINT in places
like pg_stat_statements, which maps directly to int64.

The release notes "Source Code" section may want to mention this
adjustment as some extensions may wish to adjust their code.

Author: David Rowley <dgrowleyml@gmail.com>
Suggested-by: Peter Eisentraut <peter@eisentraut.org>
Reviewed-by: Sami Imseih <samimseih@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/50cb0c8b-994b-48f9-a1c4-13039eb3536b@eisentraut.org
2025-05-30 22:59:39 +12:00
Bruce Momjian
03c53a7314 doc PG 18 relnotes: modify async I/O item for other improvements
Add "etc." to indicate other actions will also be improved by
asynchronous I/O.

Reported-by: Melanie Plageman

Discussion: https://postgr.es/m/CAAKRu_bqjgSYA+OdemL-X91Yv53OwsVARZy+-tRyj8YQ=kcj0A@mail.gmail.com
2025-05-29 12:37:05 -04:00
Tom Lane
470273da0f Avoid resource leaks when a dblink connection fails.
If we hit out-of-memory between creating the PGconn and inserting
it into dblink's hashtable, we'd lose track of the PGconn, which
is quite bad since it represents a live connection to a remote DB.
Fix by rearranging things so that we create the hashtable entry
first.

Also reduce the number of states we have to deal with by getting rid
of the separately-allocated remoteConn object, instead allocating it
in-line in the hashtable entries.  (That incidentally removes a
session-lifespan memory leak observed in the regression tests.)

There is an apparently-irreducible remaining OOM hazard, which
is that if the connection fails at the libpq level (ie it's
CONNECTION_BAD) then we have to pstrdup the PGconn's error message
before we can release it, and theoretically that could fail.  However,
in such cases we're only leaking memory not a live remote connection,
so I'm not convinced that it's worth sweating over.

This is a pretty low-probability failure mode of course, but losing
a live connection seems bad enough to justify back-patching.

Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Matheus Alcantara <matheusssilv97@gmail.com>
Discussion: https://postgr.es/m/1346940.1748381911@sss.pgh.pa.us
Backpatch-through: 13
2025-05-29 10:39:55 -04:00
Fujii Masao
3c4d7557e0 Fix assertion failure in pg_prewarm() on objects without storage.
An assertion test added in commit 049ef33 could fail when pg_prewarm()
was called on objects without storage, such as partitioned tables.
This resulted in the following failure in assert-enabled builds:

    Failed Assert("RelFileNumberIsValid(rlocator.relNumber)")

Note that, in non-assert builds, pg_prewarm() just failed with an error
in that case, so there was no ill effect in practice.

This commit fixes the issue by having pg_prewarm() raise an error early
if the specified object has no storage. This approach is similar to
the fix in commit 4623d7144 for pg_freespacemap.

Back-patched to v17, where the issue was introduced.

Author: Masahiro Ikeda <ikedamsh@oss.nttdata.com>
Reviewed-by: Dilip Kumar <dilipbalaut@gmail.com>
Reviewed-by: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/e082e6027610fd0a4091ae6d033aa117@oss.nttdata.com
Backpatch-through: 17
2025-05-29 17:50:32 +09:00
Michael Paquier
c3623703f3 Add AioUringCompletion in wait_event_names.txt
Oversight in c325a7633fcb, where the LWLock tranche AioUringCompletion
has been added.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Discussion: https://postgr.es/m/aDT5sBOxJTdulXnE@paquier.xyz
2025-05-29 13:25:05 +09:00
Bruce Momjian
a1de1b0833 doc PG 18 relnotes: split apart log_connections item
Also add details to asynchronous I/O item.

Reported-by: Melanie Plageman

Discussion: https://postgr.es/m/CAAKRu_YsVvyantS0X0Y_-vp_97=yGaoYJMXXyCEkR7pumAH3Jg@mail.gmail.com
2025-05-28 22:43:36 -04:00
Michael Paquier
35a428f30b pg_stat_statements: Fix parameter number gaps in normalized queries
pg_stat_statements anticipates that certain constant locations may be
recorded multiple times and attempts to avoid calculating a length for
these locations in fill_in_constant_lengths().

However, during generate_normalized_query() where normalized query
strings are generated, these locations are not excluded from
consideration.  This could increment the parameter number counter for
every recorded occurrence at such a location, leading to an incorrect
normalization in certain cases with gaps in the numbers reported.

For example, take this query:
SELECT WHERE '1' IN ('2'::int, '3'::int::text)
Before this commit, it would be normalized like that, with gaps in the
parameter numbers:
SELECT WHERE $1 IN ($3::int, $4::int::text)
However the correct, less confusing one should be like that:
SELECT WHERE $1 IN ($2::int, $3::int::text)

This commit fixes the computation of the parameter numbers to track the
number of constants replaced with an $n by a separate counter instead of
the iterator used to loop through the list of locations.

The underlying query IDs are not changed, neither are the normalized
strings for existing PGSS hash entries.  New entries with fresh
normalized queries would automatically get reshaped based on the new
parameter numbering.

Issue discovered while discussing a separate problem for HEAD, but this
affects all the stable branches.

Author: Sami Imseih <samimseih@gmail.com>
Discussion: https://postgr.es/m/CAA5RZ0tzxvWXsacGyxrixdhy3tTTDfJQqxyFBRFh31nNHBQ5qA@mail.gmail.com
Backpatch-through: 13
2025-05-29 11:26:03 +09:00
Bruce Momjian
089f27cf8a doc: clarify log_connections new "setup_durations" output 2025-05-28 21:42:34 -04:00
Bruce Momjian
bf6034d00d doc PG 18 relnotes: move ANALYZE item,split ANALYZE/EXPLAIN item
Reported-by: Yugo Nagata

Author: Yugo Nagata

Discussion: https://postgr.es/m/20250528232503.7db770f651c2c821c0e3c1df@sraoss.co.jp
2025-05-28 18:43:31 -04:00
Tom Lane
e5d64fd654 Tighten parsing of datetime input.
ParseFraction only expects to deal with fields that contain a decimal
point and digit(s).  However it's possible in some edge cases for it
to be passed input that doesn't look like that.  In particular the
input could look like a valid floating-point number, such as ".123e6".
strtod() will happily eat that, possibly producing a result that is
not within the expected range 0..1, which can result in integer
overflow in the callers.  That doesn't have any security consequences,
but it's still not very desirable.  Fix by checking that the input
has the expected form.

Similarly, DecodeNumberField only expects to deal with fields that
contain a decimal point and digit(s), but it's sometimes abused to
parse strings that might not look like that.  This could result in
failure to reject bogus input, yielding silly results.  Again, fix
by rejecting input that doesn't look as-expected.  That decision
also means that we can affirmatively answer the very old comment
questioning whether we couldn't save some duplicative code by
using ParseFractionalSecond here.

While these changes should only reject input that nobody would
consider valid, it still doesn't seem like a change to make in
stable branches.  Apply to HEAD only.

Reported-by: Evgeniy Gorbanev <gorbanev.es@gmail.com>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/1328335.1748371099@sss.pgh.pa.us
2025-05-28 15:10:48 -04:00
Tom Lane
be86ca103a Fix memory leakage when function compilation fails.
In pl_comp.c, initially create the plpgsql function's cache context
under the assumed-short-lived caller's context, and reparent it under
CacheMemoryContext only upon success.  This avoids a process-lifespan
leak of 8kB or more if the function contains syntax errors.  (This
leakage has existed for a long time without many complaints, but as
we move towards a possibly multi-threaded future, getting rid of
process-lifespan leaks grows more important.)

In funccache.c, arrange to reclaim the CachedFunction struct in case
the language-specific compile callback function throws an error;
previously, that resulted in an independent process-lifespan leak.
This is arguably a new bug in v18, since the leakage now occurred
for SQL-language functions as well as plpgsql.

Also, don't fill fn_xmin/fn_tid/dcallback until after successful
completion of the compile callback.  This avoids a scenario where a
partially-built function cache might appear already valid upon later
inspection, and another scenario where dcallback might fail upon being
presented with an incomplete cache entry.  We would have to reach such
a faulty cache entry via a pre-existing fn_extra pointer, so I'm not
sure these scenarios correspond to any live bug.  (The predecessor
code in pl_comp.c never took any care about this, and we've heard no
complaints about that.)  Still, it's better to be careful.

Given the lack of field complaints, I'm not very excited about
back-patching any of this; but it seems still in-scope for v18.

Discussion: https://postgr.es/m/999171.1748300004@sss.pgh.pa.us
2025-05-28 13:29:45 -04:00
Bruce Momjian
c861092b0e doc PG 18 relnotes: clarify multiplication item
Reported-by: Dean Rasheed

Author: Dean Rasheed

Discussion: https://postgr.es/m/CAEZATCXZGU3LLMZHobYys1MLpyNMAus7+UUpWeeFYwSaPNC2CA@mail.gmail.com
2025-05-28 12:34:11 -04:00
Michael Paquier
4fbb46f612 Adjust regex for test with opening parenthesis in character classes
As written, the test was throwing an error because of an unbalanced
parenthesis.  The regex used in the test is adjusted to not fail and to
test the case of an opening parenthesis in a character class after some
nested square brackets.

Oversight in d46911e584d4.

Discussion: https://postgr.es/m/16ab039d1af455652bdf4173402ddda145f2c73b.camel@cybertec.at
2025-05-28 09:43:31 +09:00
Michael Paquier
d46911e584 Fix conversion of SIMILAR TO regexes for character classes
The code that translates SIMILAR TO pattern matching expressions to
POSIX-style regular expressions did not consider that square brackets
can be nested.  For example, in an expression like [[:alpha:]%_], the
logic replaced the placeholders '_' and '%' but it should not.

This commit fixes the conversion logic by tracking the nesting level of
square brackets marking character class areas, while considering that
in expressions like []] or [^]] the first closing square bracket is a
regular character.  Multiple tests are added to show how the conversions
should or should not apply applied while in a character class area, with
specific cases added for all the characters converted outside character
classes like an opening parenthesis '(', dollar sign '$', etc.

Author: Laurenz Albe <laurenz.albe@cybertec.at>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/16ab039d1af455652bdf4173402ddda145f2c73b.camel@cybertec.at
Backpatch-through: 13
2025-05-28 08:58:40 +09:00
Bruce Momjian
3e782ca322 doc PG 18 relnotes: add removal details to MD5 item
Reported-by: Nathan Bossart

Author: Nathan Bossart

Discussion: https://postgr.es/m/aDXLoTcBYjfyqeTA@nathan
2025-05-27 17:50:52 -04:00
Bruce Momjian
08b8aa1748 doc PG 18 relnotes: fix markup
Reported-by: Peter Smith

Discussion: https://postgr.es/m/CAHut+PswZ7wFtpNgv3bdtYK5D0eGMpvz4CcnAxvj7gR_acazGQ@mail.gmail.com
2025-05-27 17:34:45 -04:00
Jeff Davis
34eb2a80d5 Change pg_dump default for statistics export.
Set the default behavior of pg_dump and pg_dumpall to be
--no-statistics.

Leave the default for pg_restore and pg_upgrade to be
--with-statistics.

Discussion: https://postgr.es/m/CA+TgmoZ9=RnWcCOZiKYYjZs_AW1P4QXCw--h4dOLLHuf1Omung@mail.gmail.com
Reviewed-by: Greg Sabino Mullane <htamfids@gmail.com>
Reviewed-by: Nathan Bossart <nathandbossart@gmail.com>
Reviewed-by: Robert Haas <robertmhaas@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
2025-05-27 13:54:38 -07:00
Masahiko Sawada
4c08ecd161 Fix assertion when decrementing eager scanning success and failure counters.
Previously, we asserted that the eager scan's success and failure
counters were positive before decrementing them. However, this
assumption was incorrect, as it's possible that some blocks have
already been eagerly scanned by the time eager scanning is disabled.

This commit replaces the assertions with guards to handle this
scenario gracefully.

With this change, we continue to allow read-ahead operations by the
read stream that exceed the success and failure caps. While there is a
possibility that overruns will trigger eager scans of additional
pages, this does not pose a practical concern as the overruns will not
be substantial and remain within an acceptable range.

Reviewed-by: Melanie Plageman <melanieplageman@gmail.com>
Discussion: https://postgr.es/m/CAD21AoConf6tkVCv-=JhQJj56kYsDwo4jG5+WqgT+ukSkYomSQ@mail.gmail.com
2025-05-27 11:42:36 -07:00
Peter Eisentraut
c53f3b9cc8 Improve file_copy_method entry in postgresql.conf.sample
Improve the wording of the comment a bit, fix whitespace.  Also move
the entry so that the section order is consistent with config.sgml.
2025-05-26 14:52:00 +02:00
Daniel Gustafsson
1f62dbf5f0 doc: Fix wording in JIT README
Remove superfluous 'is' from sentence.

Author: Yugo Nagata <nagata@sraoss.co.jp>
Reviewed-by: Daniel Gustafsson <daniel@yesql.se>
Discussion: https://postgr.es/m/20250526154412.5f77dfead87af9afc089cc48@sraoss.co.jp
2025-05-26 13:30:01 +02:00
Michael Paquier
52a1df85f2 Fix race condition in subscription TAP test 021_twophase
The test did not wait for all the subscriptions to have caught up when
dropping the subscription "tab_copy".  In a slow environment, it could
be possible for the replay of the COMMIT PREPARED transaction "mygid"
to not be confirmed yet, causing one prepared transaction to be left
around before moving to the next steps of the test.

One failure noticed is a transaction found in pg_prepared_xacts for the
cases where copy_data = false and two_phase = true, but there should be
none after dropping the subscription.

As an extra safety measure, a check is added before dropping the
subscription, scanning pg_prepared_xacts to make sure that no prepared
transactions are left once both subscriptions have caught up.

Issue introduced by a8fd13cab0ba, fixing a problem similar to
eaf5321c3524.

Per buildfarm member kestrel.

Author: Vignesh C <vignesh21@gmail.com>
Reviewed-by: Amit Kapila <amit.kapila16@gmail.com>
Discussion: https://postgr.es/m/CALDaNm329QaZ+bwU--bW6GjbNSZ8-38cDE8QWofafub7NV67oA@mail.gmail.com
Backpatch-through: 15
2025-05-26 17:28:37 +09:00
Amit Kapila
3bcb554fd2 Doc: Make logical replication examples executable in bulk.
To improve the usability of logical replication examples, we need to
enable bulk copy-pasting of DML/DDL series.

Currently, output command tags and prompts disrupt this workflow. While
prompts are typically removed, converting them to comments is acceptable
here, given the multi-server context.

Additionally, ensure all examples containing operators like < and > are
wrapped in CDATA blocks to guarantee correct rendering and consistency
with other places.

Author: David G. Johnston <david.g.johnston@gmail.com>
Reviewed-by: Peter Smith <smithpb2250@gmail.com>
Discussion: https://postgr.es/m/CAKFQuwbhbL1uaDTuo9shmo1rA-fX6XGotR7qZQ7rd-ia5ZDoQA@mail.gmail.com
2025-05-26 11:05:05 +05:30
Fujii Masao
47d90b741d doc: Fix documenation for snapshot export in logical decoding.
The documentation for exported snapshots in logical decoding previously
stated that snapshot creation may fail on a hot standby. This is no longer
accurate, as snapshot exporting on standbys has been supported since
PostgreSQL 10. This commit removes the outdated description.

Additionally, the docs referred to the NOEXPORT_SNAPSHOT option to
suppress snapshot exporting in CREATE_REPLICATION_SLOT. However,
since PostgreSQL 15, NOEXPORT_SNAPSHOT is considered legacy syntax
and retained only for backward compatibility. This commit updates
the documentation for v15 and later to use the modern equivalent:
SNAPSHOT 'nothing'. The older syntax is preserved in documentation for
v14 and earlier.

Back-patched to all supported branches.

Reported-by: Kevin K Biju <kevinkbiju@gmail.com>
Author: Fujii Masao <masao.fujii@gmail.com>
Reviewed-by: Kevin K Biju <kevinkbiju@gmail.com>
Discussion: https://postgr.es/m/174791480466.798.17122832105389395178@wrigleys.postgresql.org
Backpatch-through: 13
2025-05-26 12:47:33 +09:00
Bruce Momjian
44ce4e1593 doc PG 18 relnotes: clarify btree skip-scan item
Reported-by: Peter Geoghegan

Discussion: https://postgr.es/m/CAH2-Wzko57+sT=FcxHHo7jnPLhh35up_5aAvogLtj_D9bATsgQ@mail.gmail.com
2025-05-23 17:02:33 -04:00
Jacob Champion
a8f093234d oauth: Correct missing comma in Requires.private
I added libcurl to the Requires.private section of libpq.pc in commit
b0635bfda, but I missed that the Autoconf side needs commas added
explicitly. Configurations which used both --with-libcurl and
--with-openssl ended up with the following entry:

    Requires.private: libssl, libcrypto libcurl

The pkg-config parser appears to be fairly lenient in this case, and
accepts the whitespace as an equivalent separator, but let's not rely on
that. Add an add_to_list macro (inspired by Makefile.global's
add_to_path) to build up the PKG_CONFIG_REQUIRES_PRIVATE list correctly.

Reported-by: Wolfgang Walther <walther@technowledgy.de>
Reviewed-by: Fabrízio de Royes Mello <fabriziomello@gmail.com>
Discussion: https://postgr.es/m/CAOYmi+k2z7Rqj5xiWLUT0+bSXLvdE7TYgS5gCOSqSyXyTSSXiQ@mail.gmail.com
2025-05-23 13:05:38 -07:00
Jacob Champion
cbc8fd0c9a oauth: Limit JSON parsing depth in the client
Check the ctx->nested level as we go, to prevent a server from running
the client out of stack space.

The limit we choose when communicating with authorization servers can't
be overly strict, since those servers will continue to add extensions in
their JSON documents which we need to correctly ignore. For the SASL
communication, we can be more conservative, since there are no defined
extensions (and the peer is probably more Postgres code).

Reviewed-by: Aleksander Alekseev <aleksander@timescale.com>
Discussion: https://postgr.es/m/CAOYmi%2Bm71aRUEi0oQE9ciBnBS8xVtMn3CifaPu2kmJzUfhOZgA%40mail.gmail.com
2025-05-23 13:05:33 -07:00
Bruce Momjian
1ca583f6c0 doc PG 18 relnotes: update to current
Includes runtime injection point item by Michael Paquier.

Reported-by: Michael Paquier

Author: Michael Paquier

Discussion: https://postgr.es/m/aDAS0_eWzeGl4sok@paquier.xyz
2025-05-23 16:01:07 -04:00
Tom Lane
02502c1bca Fix per-relation memory leakage in autovacuum.
PgStat_StatTabEntry and AutoVacOpts structs were leaked until
the end of the autovacuum worker's run, which is bad news if
there are a lot of relations in the database.

Note: pfree'ing the PgStat_StatTabEntry structs here seems a bit
risky, because pgstat_fetch_stat_tabentry_ext does not guarantee
anything about whether its result is long-lived.  It appears okay
so long as autovacuum forces PGSTAT_FETCH_CONSISTENCY_NONE, but
I think that API could use a re-think.

Also ensure that the VacuumRelation structure passed to
vacuum() is in recoverable storage.

Back-patch to v15 where we started to manage table statistics
this way.  (The AutoVacOpts leakage is probably older, but
I'm not excited enough to worry about just that part.)

Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Andres Freund <andres@anarazel.de>
Discussion: https://postgr.es/m/285483.1746756246@sss.pgh.pa.us
Backpatch-through: 15
2025-05-23 14:43:43 -04:00
Tom Lane
6aa33afe6d Fix AlignedAllocRealloc to cope sanely with OOM.
If the inner allocation call returns NULL, we should restore the
previous state and return NULL.  Previously this code pfree'd
the old chunk anyway, which is surely wrong.

Also, make it call MemoryContextAllocationFailure rather than
summarily returning NULL.  The fact that we got control back from the
inner call proves that MCXT_ALLOC_NO_OOM was passed, so this change
is just cosmetic, but someday it might be less so.

This is just a latent bug at present: AFAICT no in-core callers use
this function at all, let alone call it with MCXT_ALLOC_NO_OOM.
Still, it's the kind of bug that might bite back-patched code pretty
hard someday, so let's back-patch to v17 where the bug was introduced
(by commit 743112a2e).

Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Andres Freund <andres@anarazel.de>
Discussion: https://postgr.es/m/285483.1746756246@sss.pgh.pa.us
Backpatch-through: 17
2025-05-23 11:47:33 -04:00
Daniel Gustafsson
fb844b9f06 Revert function to get memory context stats for processes
Due to concerns raised about the approach, and memory leaks found
in sensitive contexts the functionality is reverted. This reverts
commits 45e7e8ca9, f8c115a6c, d2a1ed172, 55ef7abf8 and 042a66291
for v18 with an intent to revisit this patch for v19.

Discussion: https://postgr.es/m/594293.1747708165@sss.pgh.pa.us
2025-05-23 15:44:54 +02:00
Peter Eisentraut
70a13c528b Move oauth_validator_libraries in postgresql.conf.sample
Move oauth_validator_libraries in postgresql.conf.sample to be grouped
with the other CONN_AUTH_AUTH settings, rather than making up a new
ad-hoc category.  This matches the internal categorization and also
how it is listed in the documentation.
2025-05-23 09:03:09 +02:00
Bruce Momjian
883339c170 doc PG 18 relnotes: adjust CREATE SUBSCRIPTION attribution
Reported-by: vignesh C

Discussion: https://postgr.es/m/CALDaNm0Wy-vJ6dE+e=y=yuq31i2KvGf-Rs-u6QOG4K7TpU_6Tw@mail.gmail.com
2025-05-22 23:02:11 -04:00
Bruce Momjian
7ddfac79f2 doc PG 18 relnotes: clarify btree skip scan item
Reported-by: Peter Geoghegan

Discussion: https://postgr.es/m/CAH2-Wz=2CWXgO1+uyR-VfN3ALMtFnfTtXK-VtkoQQ89ogm=4sg@mail.gmail.com
2025-05-22 22:24:18 -04:00
Bruce Momjian
3b7140d27e doc PG 18 relnotes: remove duplicate commit entry
Item related to btree skip scans.
2025-05-22 21:41:38 -04:00
Tom Lane
b7ab88ddb1 Fix assorted new memory leaks in libpq.
Valgrind'ing the postgres_fdw tests showed me that libpq was leaking
PGconn.be_cancel_key.  It looks like freePGconn is expecting
pqDropServerData to release it ... but in a cancel connection
object, that doesn't happen.

Looking a little closer, I was dismayed to find that freePGconn
also missed freeing the pgservice, min_protocol_version,
max_protocol_version, sslkeylogfile, scram_client_key_binary,
and scram_server_key_binary strings.  There's much less excuse
for those oversights.  Worse, that's from five different commits
(a460251f0, 4b99fed75, 285613c60, 2da74d8d6, 761c79508),
some of them by extremely senior hackers.

Fortunately, all of these are new in v18, so we haven't
shipped any leaky versions of libpq.

While at it, reorder the operations in freePGconn to match the
order of the fields in struct PGconn.  Some of those free's seem
to have been inserted with the aid of a dartboard.
2025-05-22 20:35:32 -04:00
Melanie Plageman
cb1456423d Replace deprecated log_connections values in docs and tests
9219093cab2607f modularized log_connections output to allow more
granular control over which aspects of connection establishment are
logged. It converted the boolean log_connections GUC into a list of strings
and deprecated previously supported boolean-like values on, off, true,
false, 1, 0, yes, and no. Those values still work, but they are
supported mainly for backwards compatability. As such, documented
examples of log_connections should not use these deprecated values.

Update references in the docs to deprecated log_connections values. Many
of the tests use log_connections. This commit also updates the tests to
use the new values of log_connections. In some of the tests, the updated
log_connections value covers a narrower set of aspects (e.g. the
'authentication' aspect in the tests in src/test/authentication and the
'receipt' aspect in src/test/postmaster). In other cases, the new value
for log_connections is a superset of the previous included aspects (e.g.
'all' in src/test/kerberos/t/001_auth.pl).

Reported-by: Peter Eisentraut <peter@eisentraut.org>
Author: Melanie Plageman <melanieplageman@gmail.com>
Reviewed-by: Peter Eisentraut <peter@eisentraut.org>
Reviewed-by: Jacob Champion <jacob.champion@enterprisedb.com>
Discussion: https://postgr.es/m/e1586594-3b69-4aea-87ce-73a7488cdc97%40eisentraut.org
2025-05-22 17:14:54 -04:00
Tom Lane
d376ab570e In ExecInitModifyTable, don't scribble on the source plan.
The code carelessly modified mtstate->ps.plan->targetlist,
which it's not supposed to do.  Fortunately, there's not
really any need to do that because the planner already
set up a perfectly acceptable targetlist for the plan node.
We just need to remove the erroneous assignments and update some
relevant comments.

As it happens, the erroneous assignments caused the targetlist to
point to a different part of the source plan tree, so that there
isn't really a risk of the pointer becoming dangling after executor
termination.  The only visible effect of this change we can find is
that EXPLAIN will show upper references to the ModifyTable's output
expressions using different variables.  Formerly it showed Vars from
the first target relation that survived executor-startup pruning.
Now it always shows such references using the first relation appearing
in the planner output, independently of what happens during executor
pruning.  On the whole that seems like a good thing.

Also make a small tweak in ExplainPreScanNode to ensure that the first
relation will receive a refname assignment in set_rtable_names, even
if it got pruned at startup.  Previously the Vars might be shown
without any table qualification, which is confusing in a multi-table
query.

I considered back-patching this, but since the bug doesn't seem to
have any really terrible consequences in existing branches, it
seems better to not change their EXPLAIN output.  It's not too late
for v18 though, especially since v18 already made other changes in
the EXPLAIN output for these cases.

Reported-by: Tom Lane <tgl@sss.pgh.pa.us>
Author: Andres Freund <andres@anarazel.de>
Co-authored-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/213261.1747611093@sss.pgh.pa.us
2025-05-22 14:28:51 -04:00
Tom Lane
f24605e2dc Fix memory leak in XMLSERIALIZE(... INDENT).
xmltotext_with_options sometimes tries to replace the existing
root node of a libxml2 document.  In that case xmlDocSetRootElement
will unlink and return the old root node; if we fail to free it,
it's leaked for the remainder of the session.  The amount of memory
at stake is not large, a couple hundred bytes per occurrence, but
that could still become annoying in heavy usage.

Our only other xmlDocSetRootElement call is not at risk because
it's working on a just-created document, but let's modify that
code too to make it clear that it's dependent on that.

Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Jim Jones <jim.jones@uni-muenster.de>
Discussion: https://postgr.es/m/1358967.1747858817@sss.pgh.pa.us
Backpatch-through: 16
2025-05-22 13:52:46 -04:00
Nathan Bossart
5d6eac80cd pg_dump: Adjust reltuples from 0 to -1 for dumps of older versions.
Before v14, a reltuples value of 0 was ambiguous: it could either
mean the relation is empty, or it could mean that it hadn't yet
been vacuumed or analyzed.  (Commit 3d351d916b taught v14 and newer
to use -1 for the latter case.)  This ambiguity allegedly can cause
the planner to choose inefficient plans after restoring to v18 or
newer.  To fix, let's just dump reltuples as -1 in that case.  This
will cause some truly empty tables to be seen as not-yet-processed,
but that seems unlikely to cause too much trouble in practice.

Note that we could alternatively teach pg_restore_relation_stats()
to translate reltuples based on the version argument, but since
that function doesn't exist until v18, there's no particular
advantage to that approach.  That is, there's no chance of
restoring stats dumped from a pre-v14 server to another pre-v14
server.  Per discussion, the current policy is to fix pre-v18
behavior differences during export and everything else during
import.

Commit 9879105024 fixed a similar problem for vacuumdb by removing
the check for reltuples != 0.  Presumably we could reinstate that
check now, but I've chosen to leave it in place in case reltuples
isn't accurate.  As before, processing some empty tables seems
relatively harmless.

Author: Hari Krishna Sunder <hari.db.pg@gmail.com>
Reviewed-by: Jeff Davis <pgsql@j-davis.com>
Reviewed-by: Corey Huinker <corey.huinker@gmail.com>
Discussion: https://postgr.es/m/CAAeiqZ0o2p4SX5_xPcuAbbsmXjg6MJLNuPYSLUjC%3DWh-VeW64A%40mail.gmail.com
2025-05-22 10:23:26 -05:00
Amit Langote
1722d5eb05 Revert "Don't lock partitions pruned by initial pruning"
As pointed out by Tom Lane, the patch introduced fragile and invasive
design around plan invalidation handling when locking of prunable
partitions was deferred from plancache.c to the executor. In
particular, it violated assumptions about CachedPlan immutability and
altered executor APIs in ways that are difficult to justify given the
added complexity and overhead.

This also removes the firstResultRels field added to PlannedStmt in
commit 28317de72, which was intended to support deferred locking of
certain ModifyTable result relations.

Reported-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/605328.1747710381@sss.pgh.pa.us
2025-05-22 17:02:35 +09:00
Peter Eisentraut
f3622b6476 doc: Move documentation of md5_password_warnings to a better place
Commit db6a4a985bc categorized md5_password_warnings as an
authentication setting, and the placement in postgresql.conf.sample
matches that, but in the documentation it ended up under logging
settings, which isn't unreasonable but inconsistent.  This moves the
documentation chunk to authentication settings as well.
2025-05-21 16:29:05 +02:00
Michael Paquier
3d0c3a418f Adjust operation names of pg_aios to match the documentation
pg_aios used the terms "read" and "write" for vectored I/O read and
write operations, respectively.  The documentation refers to them as
"readv" and "writev", and the code uses internally the terms
PGAIO_OP_READV and PGAIO_OP_WRITEV for them, as of "vectored".

This commit adjusts these operation names to match with the code and the
documentation.

Oversight in 8e293e689bab.

Author: Atsushi Torikoshi <torikoshia@oss.nttdata.com>
Discussion: https://postgr.es/m/6df1e949d1d759ad2767c18e5845963e@oss.nttdata.com
2025-05-21 15:58:03 +09:00
Fujii Masao
0bd762e81f Fix incorrect WAL description for PREPARE TRANSACTION record.
Since commit 8b1dccd37c7, the PREPARE TRANSACTION WAL record includes
information about dropped statistics entries. However, the WAL resource
manager description function for PREPARE TRANSACTION record failed to
parse this information correctly and always assumed there were
no such entries.

As a result, for example, pg_waldump could not display the dropped
statistics entries stored in PREPARE TRANSACTION records.

The root cause was that ParsePrepareRecord() did not set the number of
statistics entries to drop on commit or abort. These values remained
zero-initialized and were never updated from the parsed record.

This commit fixes the issue by properly setting those values during parsing.
With this fix, pg_waldump can now correctly report dropped statistics
entries in PREPARE TRANSACTION records.

Back-patch to v15, where commit 8b1dccd37c7 was introduced.

Author: Daniil Davydov <3danissimo@gmail.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/CAJDiXgh-6Epb2XiJe4uL0zF-cf0_s_7Lw1TfEHDMLzYjEmfGOw@mail.gmail.com
Backpatch-through: 15
2025-05-21 11:55:14 +09:00
Michael Paquier
06450c7b8c Fix regression with location calculation of nested statements
The statement location calculated for some nested query cases was wrong
when multiple queries are sent as a single string, these being separated
by semicolons.  As pointed by Sami Imseih, the location calculation was
incorrect when the last query of nested statement with multiple queries
does **NOT** finish with a semicolon for the last statement.  In this
case, the statement length tracked by RawStmt is 0, which is equivalent
to say that the string should be used until its end.  The code
previously discarded this case entirely, causing the location to remain
at 0, the same as pointing at the beginning of the string.  This caused
pg_stat_statements to store incorrect query strings.

This issue has been introduced in 499edb09741b.  I have looked at the
diffs generated by pgaudit back then, and noticed the difference
generated for this nested query case, but I have missed the point that
it was an actual regression with an existing case.  A test case is added
in pg_stat_statements to provide some coverage, restoring the pre-17
behavior for the calculation of the query locations.  Special thanks to
David Steele, who, through an analysis of the test diffs generated by
pgaudit with the new v18 logic, has poked me about the fact that my
original analysis of the matter was wrong.

The test output of pg_overexplain is updated to reflect the new logic,
as the new locations refer to the beginning of the argument passed to
the function explain_filter().  When the module was introduced in
8d5ceb113e3f, which was after 499edb09741b (for the new calculation
method), the locations of the test were not actually right: the plan
generated for the query string given in input of the function pointed to
the top-level query, not the nested one.

Reported-by: David Steele <david@pgbackrest.org>
Author: Michael Paquier <michael@paquier.xyz>
Reviewed-by: Anthonin Bonnefoy <anthonin.bonnefoy@datadoghq.com>
Reviewed-by: Jian He <jian.universality@gmail.com>
Reviewed-by: Sami Imseih <samimseih@gmail.com>
Reviewed-by: David Steele <david@pgbackrest.org>
Discussion: https://postgr.es/m/844a3b38-bbf1-4fb2-9fd6-f58c35c09917@pgbackrest.org
2025-05-21 10:22:12 +09:00
Nathan Bossart
a6060f1cbe pg_dump: Fix array literals in fetchAttributeStats().
Presently, fetchAttributeStats() builds array literals by treating
the elements as SQL identifiers.  This is incorrect for a couple of
reasons:

* Array literal content must match the external text representation
  of the array, i.e., what array_out() would return.  One notable
  problem is that double quotes are escaped with "" in identifiers
  but with \" in array literals.  To fix, build the array content
  using the pre-existing appendPGArray() function.

* Array literals must be written as string constants.  A notable
  problem here is that single quotes are escaped via '' in strings
  but are not escaped in the text representation of an array.  To
  fix, append the aforementioned array literal content to the query
  with appendStringLiteralAH().

While at it, modify a test case to use an identifier that would
cause the test to fail without this change.

Oversight in commit 9c02e3a986.

Reported-by: Philippe Beaudoin <pbh.emaj@free.fr>
Author: Jian He <jian.universality@gmail.com>
Co-authored-by: Nathan Bossart <nathandbossart@gmail.com>
Co-authored-by: Stepan Neretin <slpmcf@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Bug: #18923
Discussion: https://postgr.es/m/18923-e79273f87c6bed69%40postgresql.org
2025-05-20 16:31:00 -05:00
Heikki Linnakangas
cbf53e2b8a Fix cross-version upgrade test failure
Commit 29f7ce6fe7 added another view that needs adjustment in the
cross-version upgrade test. This should fix the XversionUpgrade
failures in the buildfarm.

Backpatch-through: 16
Discussion: https://www.postgresql.org/message-id/18929-077d6b7093b176e2@postgresql.org
2025-05-20 10:39:14 +03:00
Michael Paquier
54675d8986 doc: Clarify use of _ccnew and _ccold in REINDEX CONCURRENTLY
Invalid indexes are suffixed with "_ccnew" or "_ccold".  The
documentation missed to mention the initial underscore.
ChooseRelationName() may also append an extra number if indexes with a
similar name already exist; let's add a note about that too.

Author: Alec Cozens <acozens@pixelpower.com>
Discussion: https://postgr.es/m/174733277404.1455388.11471370288789479593@wrigleys.postgresql.org
Backpatch-through: 13
2025-05-20 14:39:06 +09:00
Andres Freund
acad909321 aio: Fix possible state confusions due to interrupt processing
elog()/ereport() process interrupts, iff the log message is < ERROR and the
log message will be emitted. aio's debug messages are emitted via ereport(),
but in some places the code is not ready for interrupts to be processed.

Fix the issue using a few different methods:

1) handle interrupts arriving concurrently - in some places it's easy to
   detect that by fetching the handle's generation a bit earlier
2) Check if interrupts made the work needing to be done obsolete
3) Disallow interrupts, as there's no sane way to make interrupt processing
   safe

To prevent some similar issues from being re-introduced, assert that
interrupts are held in pgaio_io_update_state().

This commit also fixes the contents of a debug message I added in 039bfc457e4.

Reported-by: Alexander Lakhin <exclusion@gmail.com>
Reviewed-by: Noah Misch <noah@leadboat.com>
Discussion: https://postgr.es/m/mvpm7ga3dfgz7bvum22hmuz26cariylmcppb3irayftc7bwk3r@l7gb6gr7azhc
2025-05-19 21:07:06 -04:00
Heikki Linnakangas
29f7ce6fe7 Fix deparsing FETCH FIRST <expr> ROWS WITH TIES
In the grammar, <expr> is a c_expr, which accepts only a limited set
of integer literals and simple expressions without parens. The
deparsing logic didn't quite match the grammar rule, and failed to use
parens e.g. for "5::bigint".

To fix, always surround the expression with parens. Would be nice to
omit the parens in simple cases, but unfortunately it's non-trivial to
detect such simple cases. Even if the expression is a simple literal
123 in the original query, after parse analysis it becomes a FuncExpr
with COERCE_IMPLICIT_CAST rather than a simple Const.

Reported-by: yonghao lee
Backpatch-through: 13
Discussion: https://www.postgresql.org/message-id/18929-077d6b7093b176e2@postgresql.org
2025-05-19 18:50:26 +03:00
Amit Kapila
ad5eaf390c Don't retreat slot's confirmed_flush LSN.
Prevent moving the confirmed_flush backwards, as this could lead to data
duplication issues caused by replicating already replicated changes.

This can happen when a client acknowledges an LSN it doesn't have to do
anything for, and thus didn't store persistently. After a restart, the
client can send the prior LSN that it stored persistently as an
acknowledgement, but we need to ignore such an LSN to avoid retreating
confirm_flush LSN.

Diagnosed-by: Zhijie Hou <houzj.fnst@fujitsu.com>
Author: shveta malik <shveta.malik@gmail.com>
Reviewed-by: Amit Kapila <amit.kapila16@gmail.com>
Reviewed-by: Dilip Kumar <dilipbalaut@gmail.com>
Tested-by: Nisha Moond <nisha.moond412@gmail.com>
Backpatch-through: 13
Discussion: https://postgr.es/m/CAJpy0uDZ29P=BYB1JDWMCh-6wXaNqMwG1u1mB4=10Ly0x7HhwQ@mail.gmail.com
Discussion: https://postgr.es/m/OS0PR01MB57164AB5716AF2E477D53F6F9489A@OS0PR01MB5716.jpnprd01.prod.outlook.com
2025-05-19 12:13:06 +05:30
Tom Lane
f8db5c7a3f Doc: add pre-branch task to run src/tools/copyright.pl.
It's common for some files with last year's copyright date
to sneak into the tree between early January (when we normally run
copyright.pl) and feature freeze.  Immediately before branching
the new release is an ideal time to fix the stragglers, so add a
note about it to the RELEASE_CHANGES checklist.

Discussion: https://postgr.es/m/CALa6HA4_Wu7-2PV0xv-Q84cT8eG7rTx6bdjUV0Pc=McAwkNMfQ@mail.gmail.com
2025-05-18 23:31:44 -04:00
Michael Paquier
2c6469d4cd Fix incorrect year in some copyright notices
A couple of new files have been added in the tree with a copyright year
of 2024 while we were already in 2025.  These should be marked with
2025, so let's fix them.

Reported-by: Shaik Mohammad Mujeeb <mujeeb.sk.dev@gmail.com>
Discussion: https://postgr.es/m/CALa6HA4_Wu7-2PV0xv-Q84cT8eG7rTx6bdjUV0Pc=McAwkNMfQ@mail.gmail.com
2025-05-19 09:46:52 +09:00
Michael Paquier
11b2dc3709 ecpg: Add missing newline in meson.build
Noticed while performing a routine sanity check of the files in the
tree.  Issue introduced by 28f04984f0c2.

Discussion: https://postgr.es/m/CALa6HA4_Wu7-2PV0xv-Q84cT8eG7rTx6bdjUV0Pc=McAwkNMfQ@mail.gmail.com
2025-05-19 09:44:17 +09:00
Alexander Korotkov
3d3a81fc24 Fix tuple_fraction calculation in generate_orderedappend_paths()
6b94e7a6da adjusted generate_orderedappend_paths() to consider fractional
paths.  However, it didn't manage to interpret the tuple_fraction value
correctly.  According to the header comment of grouping_planner(), the
tuple_fraction >= 1 specifies the absolute number of expected tuples.  That
number must be divided by the expected total number of tuples to get the
actual fraction.

Even though this is a bug fix, we don't backpatch it.  The risks of the side
effects of plan changes on stable branches are too high.

Reported-by: Andrei Lepikhov <lepihov@gmail.com>
Discussion: https://postgr.es/m/3ca271fa-ca5c-458c-8934-eb148622b270%40gmail.com
Author: Andrei Lepikhov <lepihov@gmail.com>
Reviewed-by: Junwang Zhao <zhjwpku@gmail.com>
Reviewed-by: Alvaro Herrera <alvherre@alvh.no-ip.org>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
2025-05-18 23:49:50 +03:00
Tom Lane
12eee85e51 Make our usage of memset_s() conform strictly to the C11 standard.
Per the letter of the C11 standard, one must #define
__STDC_WANT_LIB_EXT1__ as 1 before including <string.h> in order to
have access to memset_s().  It appears that many platforms are lenient
about this, because we weren't doing it and yet the code appeared to
work anyway.  But we now find that with -std=c11, macOS is strict and
doesn't declare memset_s, leading to compile failures since we try to
use it anyway.  (Given the lack of prior reports, perhaps this is new
behavior in the latest SDK?  No matter, we're clearly in the wrong.)

In addition to the immediate problem, which could be fixed merely by
adding the needed #define to explicit_bzero.c, it seems possible that
our configure-time probe for memset_s() could fail in case a platform
implements the function in some odd way due to this spec requirement.
This concern can be fixed in largely the same way that we dealt with
strchrnul() in 6da2ba1d8: switch to using a declaration-based
configure probe instead of a does-it-link probe.

Back-patch to v13 where we started using memset_s().

Reported-by: Lakshmi Narayana Velayudam <dev.narayana.v@gmail.com>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAA4pTnLcKGG78xeOjiBr5yS7ZeE-Rh=FaFQQGOO=nPzA1L8yEA@mail.gmail.com
Backpatch-through: 13
2025-05-18 12:45:55 -04:00
Daniel Gustafsson
0d4dad200d Fix function name reference in comment
Ensure that we refer to the function being used, rather than the
name of the resulting function in question.

Author: Paul A Jungwirth <pj@illuminatedcomputing.com>
Reviewed-by: Daniel Gustafsson <daniel@yesql.se>
Discussion: https://postgr.es/m/CA+renyVZNiHEv5ceKDjA4j5xC6NT6mRuW33BDERBQMi_90_t6A@mail.gmail.com
2025-05-18 10:05:38 +02:00
Daniel Gustafsson
5987553fde Align organization wording in copyright statement
This aligns the copyright and legal notice wordig with commit
a233a603bab8 and pgweb commit 2d764dbc083ab8.  Backpatch down
to all supported versions.

Author: Daniel Gustafsson <daniel@yesql.se>
Reviewed-by: Dave Page <dpage@pgadmin.org>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/744E414E-3F52-404C-97FB-ED9B3AA37DC8@yesql.se
Backpatch-through: 13
2025-05-16 11:20:07 -04:00
Richard Guo
fe29b2a1da Fix Assert failure in XMLTABLE parser
In an XMLTABLE expression, columns can be marked NOT NULL, and the
parser internally fabricates an option named "is_not_null" to
represent this.  However, the parser also allows users to specify
arbitrary option names.  This creates a conflict: a user can
explicitly use "is_not_null" as an option name and assign it a
non-Boolean value, which violates internal assumptions and triggers an
assertion failure.

To fix, this patch checks whether a user-supplied name collides with
the internally reserved option name and raises an error if so.
Additionally, the internal name is renamed to "__pg__is_not_null" to
further reduce the risk of collision with user-defined names.

Reported-by: Евгений Горбанев <gorbanyoves@basealt.ru>
Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Alvaro Herrera <alvherre@kurilemu.de>
Discussion: https://postgr.es/m/6bac9886-65bf-4cec-96bd-e304159f28db@basealt.ru
Backpatch-through: 15
2025-05-15 17:09:04 +09:00
Richard Guo
2c0ed86d39 Add explicit initialization for all PlannerGlobal fields
When creating a new PlannerGlobal node in standard_planner(), most
fields are explicitly initialized, but a few are not.  This doesn't
cause any functional issues, as makeNode() zeroes all fields by
default.  However, the inconsistency is undesirable from a clarity and
maintenance perspective.

This patch explicitly initializes the remaining fields to improve
consistency and readability.

Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: David Rowley <dgrowleyml@gmail.com>
Discussion: https://postgr.es/m/CAMbWs4-TgQHNOiouqGcuHoBqbJjWyx4UxGKxUY3FrF4trGbcPA@mail.gmail.com
2025-05-14 09:59:31 +09:00
Daniel Gustafsson
6e289f2d5d Fix order of parameters in POD documentation
The documentation for log_check() had the parameters in the wrong
order.  Also while there, rename %parameters to %params to better
documentation for similar functions which use %params.  Backpatch
down to v14 where this was introduced.

Author: Daniel Gustafsson <daniel@yesql.se>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/9F503B5-32F2-45D7-A0AE-952879AD65F1@yesql.se
Backpatch-through: 14
2025-05-13 07:29:14 -04:00
Amit Kapila
8ede692de5 Fix the race condition in the test added by 7c99dc587.
After executing ALTER SUBSCRIPTION tap_sub SET PUBLICATION, we did not
wait for the new walsender process to restart. As a result, an INSERT
executed immediately after the ALTER could be decoded and skipped,
considering it is not part of any subscribed publication. And, the old
apply worker could also confirm the LSN of such an INSERT. This could
cause the replication to resume from a point after the INSERT. In such
cases, we miss the expected warning about the missing publication.

To fix this, ensure the walsender has restarted before continuing after
ALTER SUBSCRIPTION.

Reported-by: Tom Lane as per CI
Author: vignesh C <vignesh21@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Amit Kapila <amit.kapila16@gmail.com>
Discussion: https://postgr.es/m/1230066.1745992333@sss.pgh.pa.us
2025-05-13 09:54:29 +05:30
Álvaro Herrera
dbf42b84ac
Add tab-complete for ALTER DOMAIN ADD [CONSTRAINT]
We can add tab-completion with "CHECK (" and "NOT NULL" after ALTER
DOMAIN ADD [CONSTRAINT].

ALTER DOMAIN dom ADD -> CHECK (
ALTER DOMAIN dom ADD -> NOT NULL
ALTER DOMAIN dom ADD -> CONSTRAINT
ALTER DOMAIN dom ADD CONSTRAINT nm -> CHECK (
ALTER DOMAIN dom ADD CONSTRAINT nm -> NOT NULL

Author: jian he <jian.universality@gmail.com>
Author: Dagfinn Ilmari Mannsåker <ilmari@ilmari.org>
Discussion: https://postgr.es/m/CACJufxG_f6LzAT_McC-kKmQWpuWnOYKyNBw8Kv3xzTjPqmeHcA@mail.gmail.com
2025-05-11 10:16:45 -04:00
Álvaro Herrera
0588656366
Fix comment of tsquerysend()
The comment describes the order in which fields are sent, and it had one
of the fields in the wrong place.

This has been wrong since e6dbcb72fafa (2008), so backpatch all the way
back.

Author: Emre Hasegeli <emre@hasegeli.com>
Discussion: https://postgr.es/m/CAE2gYzzf38bR_R=izhpMxAmqHXKeM5ajkmukh4mNs_oXfxcMCA@mail.gmail.com
2025-05-11 09:47:10 -04:00
Álvaro Herrera
dc9a2d54fd
relcache: Avoid memory leak on tables with no CHECK constraints
As complained about by Valgrind, in commit a379061a22a8 I failed to
realize that I was causing rd_att->constr->check to become allocated
when no CHECK constraints exist; previously it'd remain NULL.  (This was
my bug, not the mentioned commit author's).  Fix by making the
allocation conditional, and set ->check to NULL if unallocated.

Reported-by: Yasir <yasir.hussain.shah@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/202505082025.57ijx3qrbx7u@alvherre.pgsql
2025-05-11 09:22:12 -04:00
Álvaro Herrera
7b2ad43426
Sort includes in alphabetical order
Added by commit 042a66291b04, no backpatch needed.
2025-05-11 09:15:05 -04:00
Tom Lane
d4a7e4e179 Fix incorrect "return NULL" in BumpAllocLarge().
This must be "return MemoryContextAllocationFailure(context, size, flags)"
instead.  The effect of this oversight is that if we got a malloc
failure right here, the code would act as though MCXT_ALLOC_NO_OOM
had been specified, whether it was or not.  That would likely lead
to a null-pointer-dereference crash at the unsuspecting call site.

Noted while messing with a patch to improve our Valgrind leak
detection support.  Back-patch to v17 where this code came in.
2025-05-10 20:22:39 -04:00
Noah Misch
4a4ee0c2c1 Remove GLOBALTABLESPACE_OID assert for locked buffers.
Commit f4ece891fc2f3f96f0571720a1ae30db8030681b added the assertion in
an attempt to catch some defects even after VACUUM FULL or REINDEX.
However, IsCatalogTextUniqueIndexOid(tag.relNumber) always returns false
after a relfilenode change, provoking unintended assertion failures.

Reported-by: Adam Guo <adamguo@amazon.com>
Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com>
Bug: #18912
Discussion: https://postgr.es/m/18912-a41c9bd0e0ad19b1@postgresql.org
2025-05-10 07:36:27 -07:00
Bruce Momjian
99ddf8615c doc PG 18 relnotes: mv. hash joins and GROUP BY item to General
Reported-by: David Rowley

Discussion: https://postgr.es/m/CAApHDvqJz+Zf7a6abisqoTGottDSRD+YPx=aQSgCsCKD476vGA@mail.gmail.com
2025-05-09 23:40:02 -04:00
Michael Paquier
c259ba881c aio: Use runtime arguments with injections points in tests
This cleans up the code related to the testing infrastructure of AIO
that used injection points, switching the test code to use the new
facility for injection points added by 371f2db8b05e rather than tweaks
to pass and reset arguments to the callbacks run.

This removes all the dependencies to USE_INJECTION_POINTS in the AIO
code.  pgaio_io_call_inj(), pgaio_inj_io_get() and pgaio_inj_cur_handle
are now gone.

Reviewed-by: Greg Burd <greg@burd.me>
Discussion: https://postgr.es/m/Z_y9TtnXubvYAApS@paquier.xyz
2025-05-10 12:36:57 +09:00
Michael Paquier
36e5fda632 injection_points: Add support and tests for runtime arguments
This commit provides some test coverage for the runtime arguments of
injection points, for both INJECTION_POINT_CACHED() and
INJECTION_POINT(), as extended in 371f2db8b05e.

The SQL functions injection_points_cached() and injection_points_run()
are extended so as it is possible to pass an optional string value to
them.

Reviewed-by: Greg Burd <greg@burd.me>
Discussion: https://postgr.es/m/Z_y9TtnXubvYAApS@paquier.xyz
2025-05-10 07:40:25 +09:00
Michael Paquier
371f2db8b0 Add support for runtime arguments in injection points
The macros INJECTION_POINT() and INJECTION_POINT_CACHED() are extended
with an optional argument that can be passed down to the callback
attached when an injection point is run, giving to callbacks the
possibility to manipulate a stack state given by the caller.  The
existing callbacks in modules injection_points and test_aio have their
declarations adjusted based on that.

da7226993fd4 (core AIO infrastructure) and 93bc3d75d8e1 (test_aio) and
been relying on a set of workarounds where a static variable called
pgaio_inj_cur_handle is used as runtime argument in the injection point
callbacks used by the AIO tests, in combination with a TRY/CATCH block
to reset the argument value.  The infrastructure introduced in this
commit will be reused for the AIO tests, simplifying them.

Reviewed-by: Greg Burd <greg@burd.me>
Discussion: https://postgr.es/m/Z_y9TtnXubvYAApS@paquier.xyz
2025-05-10 06:56:26 +09:00
Bruce Momjian
89372d0aaa doc PG 18 relnotes: fix missing parens for crc32c()
Reported-by: Steven Niu

Discussion: https://postgr.es/m/CABBtG=ejqK58cFWpw3etVZfQfhjC-qOqV+9GQWRnLO+p9wYMbw@mail.gmail.com
2025-05-09 14:16:17 -04:00
Tom Lane
95129709fd Skip RSA-PSS ssl test when using LibreSSL.
Presently, LibreSSL does not have working support for RSA-PSS,
so disable that test.  Per discussion at
https://marc.info/?l=libressl&m=174664225002441&w=2
they do intend to fix this, but it's a ways off yet.

Reported-by: Thomas Munro <thomas.munro@gmail.com>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Daniel Gustafsson <daniel@yesql.se>
Discussion: https://postgr.es/m/CA+hUKG+fLqyweHqFSBcErueUVT0vDuSNWui-ySz3+d_APmq7dw@mail.gmail.com
Backpatch-through: 15
2025-05-09 12:29:01 -04:00
Tom Lane
75d73331d0 Hack one ssl test case to pass with current LibreSSL.
With LibreSSL, our test of error logging for cert chain depths > 0
reports the wrong certificate.  This is almost certainly their bug
not ours, so just tweak the test to accept their answer.

No back-patch needed, since this test case wasn't enabled before
e0f373ee4.

Reported-by: Thomas Munro <thomas.munro@gmail.com>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Daniel Gustafsson <daniel@yesql.se>
Discussion: https://postgr.es/m/CA+hUKG+fLqyweHqFSBcErueUVT0vDuSNWui-ySz3+d_APmq7dw@mail.gmail.com
2025-05-09 11:53:51 -04:00
Tom Lane
0aaf69965d Centralize ssl tests' check for whether we're using LibreSSL.
Right now there's only one caller, so that this is merely
an exercise in shoving code from one module to another,
but there will shortly be another one.  It seems better to
avoid having two copies of this highly-subject-to-change test.

Back-patch to v15, where we first introduced some tests that
don't work with LibreSSL.

Reported-by: Thomas Munro <thomas.munro@gmail.com>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Daniel Gustafsson <daniel@yesql.se>
Discussion: https://postgr.es/m/CA+hUKG+fLqyweHqFSBcErueUVT0vDuSNWui-ySz3+d_APmq7dw@mail.gmail.com
Backpatch-through: 15
2025-05-09 11:50:33 -04:00
Peter Eisentraut
bc35adee8d doc: Put new options in consistent order on man pages 2025-05-09 09:03:41 +02:00
Heikki Linnakangas
b28c59a6cd Use 'void *' for arbitrary buffers, 'uint8 *' for byte arrays
A 'void *' argument suggests that the caller might pass an arbitrary
struct, which is appropriate for functions like libc's read/write, or
pq_sendbytes(). 'uint8 *' is more appropriate for byte arrays that
have no structure, like the cancellation keys or SCRAM tokens. Some
places used 'char *', but 'uint8 *' is better because 'char *' is
commonly used for null-terminated strings. Change code around SCRAM,
MD5 authentication, and cancellation key handling to follow these
conventions.

Discussion: https://www.postgresql.org/message-id/61be9e31-7b7d-49d5-bc11-721800d89d64@eisentraut.org
2025-05-08 22:01:25 +03:00
Heikki Linnakangas
965213d9c5 Use more mundane 'int' type for cancel key lengths in libpq
The documented max length of a cancel key is 256 bytes, so it fits in
uint8. It nevertheless seems weird to not just use 'int', like in
commit 0f1433f053 for the backend.

Discussion: https://www.postgresql.org/message-id/61be9e31-7b7d-49d5-bc11-721800d89d64%40eisentraut.org
2025-05-08 22:01:20 +03:00
Bruce Momjian
9d710a1ac0 PG 18 relnotes: adjust RETURNING new/old item
Reported-by: jian he

Discussion: https://postgr.es/m/CACJufxFM1avdwu=OrTx_uMAjTDbFOj1Gp7mnNHOofTVj9QtmRw@mail.gmail.com
2025-05-08 11:11:08 -04:00
Daniel Gustafsson
8fcc648780 doc: Fix title markup for AT TIME ZONE and AT LOCAL
The title for AT TIME ZONE and AT LOCAL was accidentally wrapping the
"and" in the <literal> tag.  Backpatch to v17 where it was introduced
in 97957fdbaa42.

Author: Noboru Saito <noborusai@gmail.com>
Reviewed-by: Daniel Gustafsson <daniel@yesql.se>
Reviewed-by: Tatsuo Ishii <ishii@postgresql.org>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/CAAM3qn+7QUWW9R6_YwPKXmky0xGE4n63U3EsxZeWE_QtogeU8g@mail.gmail.com
Backpatch-through: 17
2025-05-08 13:53:16 +02:00
Richard Guo
c06e909c26 Track the number of presorted outer pathkeys in MergePath
When creating an explicit Sort node for the outer path of a mergejoin,
we need to determine the number of presorted keys of the outer path to
decide whether explicit incremental sort can be applied.  Currently,
this is done by repeatedly calling pathkeys_count_contained_in.

This patch caches the number of presorted outer pathkeys in MergePath,
allowing us to save several calls to pathkeys_count_contained_in.  It
can be considered a complement to the changes in commit 828e94c9d.

Reported-by: David Rowley <dgrowleyml@gmail.com>
Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Tender Wang <tndrwang@gmail.com>
Discussion: https://postgr.es/m/CAApHDvqvBireB_w6x8BN5txdvBEHxVgZBt=rUnpf5ww5P_E_ww@mail.gmail.com
2025-05-08 18:21:32 +09:00
Richard Guo
773db22269 Suppress unnecessary explicit sorting for EPQ mergejoin path
When building a ForeignPath for a joinrel, if there's a possibility
that EvalPlanQual will be executed, we must identify a suitable path
for EPQ checks.  If the outer or inner path of the chosen path is a
ForeignPath representing a pushed-down join, we replace it with its
fdw_outerpath to ensure that the EPQ check path consists entirely of
local joins.

If the chosen path is a MergePath, and its outer or inner path is a
ForeignPath that is not already well enough ordered, the MergePath
will have non-NIL outersortkeys or innersortkeys indicating the
desired ordering to be created by an explicit Sort node.  If we then
replace the outer or inner path with its corresponding fdw_outerpath,
and that path is already sufficiently ordered, we end up in an
inconsistent state: the MergePath has non-NIL outersortkeys or
innersortkeys, and its input path is already properly ordered.  This
inconsistency can result in an Assert failure or the addition of a
redundant Sort node.

To fix, check if the new outer or inner path of a MergePath is already
properly sorted, and set its outersortkeys or innersortkeys to NIL if
so.

Bug: #18902
Reported-by: Nikita Kalinin <n.kalinin@postgrespro.ru>
Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Tender Wang <tndrwang@gmail.com>
Discussion: https://postgr.es/m/18902-71c1bed2b9f7c46f@postgresql.org
2025-05-08 18:20:18 +09:00
Bruce Momjian
9fef27a83b doc PG 18 relnotes: adjust pg_log_backend_memory_contexts()
Reported-by: David Rowley

Discussion: https://postgr.es/m/CAApHDvrGLBqs_Vm9COMY7uBDvUDMKds7RwC20YjEPf+XRTY9XQ@mail.gmail.com
2025-05-07 21:11:16 -04:00
Bruce Momjian
f8d49aa130 doc PG 18 relnotes: add pg_log_backend_memory_contexts() mention
Now zero-based.

Reported-by: David Rowley

Discussion: https://postgr.es/m/CAApHDvqMfTBdfwc0Z-tHXLnBMKJLYEZDApgUzA7x_PUDZsY3GA@mail.gmail.com
2025-05-07 20:36:21 -04:00
Bruce Momjian
69aca072eb doc PG 18 relnotes: adjust pgbench per-script reporting item
Also run src/tools/add_commit_links.pl for a previous commit.

Reported-by: Yugo Nagata

Discussion: https://postgr.es/m/20250507195941.c6e1b48c73f062b727f686a8@sraoss.co.jp
2025-05-07 16:56:26 -04:00
Bruce Momjian
3bd5271729 doc PG 18 relnotes: mention GROUP SET fixes
Reported-by: Richard Guo

Discussion: https://postgr.es/m/CAMbWs4_asKPqTCt0h9pp=zHc9vmPcnczbHeF6Xkxn1LhLapcTQ@mail.gmail.com
2025-05-07 16:39:49 -04:00
Nathan Bossart
16bf24e0e4 Remove pg_replication_origin's TOAST table.
A few places that access this catalog don't set up an active
snapshot before potentially accessing its TOAST table.  However,
roname (the replication origin name) is the only varlena column, so
this is only a problem if the name requires out-of-line storage.
This commit removes its TOAST table to avoid needing to set up a
snapshot.  It also places a limit on replication origin names so
that attempts to set long names will fail with a more user-friendly
error.  Those chosen limit of 512 bytes should be sufficient to
avoid "row is too big" errors independent of BLCKSZ, but it should
also be lenient enough for all reasonable use-cases.

Bumps catversion.

Reviewed-by: Michael Paquier <michael@paquier.xyz>
Reviewed-by: Amit Kapila <amit.kapila16@gmail.com>
Reviewed-by: Euler Taveira <euler@eulerto.com>
Reviewed-by: Nisha Moond <nisha.moond412@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/ZvMSUPOqUU-VNADN%40nathan
2025-05-07 14:47:36 -05:00
Peter Geoghegan
5f4d98d4f3 Prevent premature nbtree array advancement.
nbtree array index scans could fail to return matching tuples in rare
cases where the missed tuples cover key space that the scan's arrays
incorrectly indicate has already been read.  These cases involved nearby
tuples with NULL values that were evaluated using a skip array key while
in pstate.forcenonrequired mode.

To fix, prevent forcenonrequired mode from prematurely advancing the
scan's array keys beyond key space that the scan has yet to read tuples
from: reset the scan's array keys (to the first elements in the current
scan direction) before the _bt_checkkeys call for pstate.finaltup.  That
way _bt_checkkeys starts from a clean slate, which ensures that it will
call _bt_advance_array_keys (while passing it sktrig_required=true).
This reliably restores the invariant that the scan's arrays always
accurately track its progress through the index's key space (at least
when the scan is "between pages").

Oversight in commit 8a510275, which optimized nbtree search scan key
comparisons.

Author: Peter Geoghegan <pg@bowt.ie>
Reviewed-By: Mark Dilger <mark.dilger@enterprisedb.com>
Discussion: https://postgr.es/m/CAH2-WzmodSE+gpTd1CRGU9ez8ytyyDS+Kns2r9NzgUp1s56kpw@mail.gmail.com
2025-05-07 15:20:42 -04:00
Peter Geoghegan
7e25c9363a nbtree: tighten up array recheck rules.
Be more conservative when performing a scheduled recheck of an nbtree
scan's array keys once on the next page, having set so->scanBehind: back
out of reading the page (perform another primitive scan instead) when
the next page's high key/finaltup has an untruncated prefix of matching
values and truncated suffix attributes associated with lower-order keys.
In other words, stop assuming that the lower-order keys have been
satisfied by the truncated suffix attributes in this context (only do so
when considering scheduling a recheck within _bt_advance_array_keys).

The new behavior is more logical: if the next page read after setting
so->scanBehind can only contain tuples that are themselves "behind the
scan", that's reason enough to cut our losses.  In general, when we set
so->scanBehind, we only expect to perform one recheck on the next page
to make a final decision about whether or not to continue the current
primitive index scan.  It seems unprincipled for the recheck to allow a
_bt_readpage to continue unless the scan's arrays will advance/unless
the page might actually contain relevant tuples.

In practice it is highly unlikely that things will line up like this
(the untruncated prefix of attribute values from the next page's high
key is seldom an exact match for their corresponding array's current
element following array advancement on the original/previous page).
That gives us all the more reason to keep things simple and consistent.

This was arguably an oversight in commit 9a2e2a285a, which improved
nbtree array primitive scan scheduling.

Author: Peter Geoghegan <pg@bowt.ie>
Discussion: https://postgr.es/m/CAH2-WzkXzJajgyW-pCQ7vaDPhaT3huU+Zw_j448rpCBEsu2YOQ@mail.gmail.com
2025-05-07 15:17:40 -04:00
Nathan Bossart
acea3fc49f pg_dumpall: Add --sequence-data.
I recently added this option to pg_dump, but I forgot to add it to
pg_dumpall, too.  There's probably little use for it at the moment,
but we will need it if/when we teach pg_upgrade to use pg_dumpall
to dump the database schemas.

Oversight in commit 9c49f0e8cd.

Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/aBE8rHFo922xQUwh%40nathan
2025-05-07 13:36:51 -05:00
Alexander Korotkov
ab42d643c1 Refactor ChangeVarNodesExtended() using the custom callback
fc069a3a6319 implemented Self-Join Elimination (SJE) and put related logic
to ChangeVarNodes_walker().  This commit provides refactoring to remove the
SJE-related logic from ChangeVarNodes_walker() but adds a custom callback to
ChangeVarNodesExtended(), which has a chance to process a node before
ChangeVarNodes_walker().  Passing this callback to ChangeVarNodesExtended()
allows SJE-related node handling to be kept within the analyzejoins.c.

Reported-by: Richard Guo <guofenglinux@gmail.com>
Discussion: https://postgr.es/m/CAMbWs49PE3CvnV8vrQ0Dr%3DHqgZZmX0tdNbzVNJxqc8yg-8kDQQ%40mail.gmail.com
Author: Andrei Lepikhov <lepihov@gmail.com>
Author: Alexander Korotkov <aekorotkov@gmail.com>
2025-05-07 11:10:16 +03:00
Peter Eisentraut
2448c7a9e0 doc: Put some psql documentation pieces back into alphabetical order 2025-05-07 08:23:44 +02:00
Peter Eisentraut
c0cf282551 Remove some tabs in C string literals 2025-05-07 08:23:44 +02:00
Peter Eisentraut
c11bd5f500 doc: Add link to table
Formal tables should generally have an xref in the text that points to
them.  Add them here.
2025-05-07 08:23:44 +02:00
Peter Eisentraut
a2c6d84acd doc: Fix up spacing around verbatim DocBook elements 2025-05-07 08:23:44 +02:00
Michael Paquier
c4c236ab5c Fix some comments related to IO workers
IO workers are treated as auxiliary processes.  The comments fixed in
this commit stated that there could be only one auxiliary process of
each BackendType at the same time.  This is not true for IO workers, as
up to MAX_IO_WORKERS of them can co-exist at the same time.

Author: Cédric Villemain <Cedric.Villemain@data-bene.io>
Co-authored-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/e4a3ac45-abce-4b58-a043-b4a31cd11113@Data-Bene.io
2025-05-07 14:55:57 +09:00
Peter Eisentraut
09a47c68e2 Fix whitespace 2025-05-07 07:01:03 +02:00
Bruce Momjian
b560ce7884 doc PG 18 relnotes: adjust partition planning item
Reported-by: David Rowley

Discussion: https://postgr.es/m/CAApHDvqgK7uqPZAwxsfBiFhvBHHB0txaUxhUrdwG4d5Mik_RnA@mail.gmail.com
2025-05-06 21:15:44 -04:00
Bruce Momjian
ada78f9bef doc PG 18 relnotes: small adjustments regarding options
Reported-by: jian he

Discussion: https://postgr.es/m/CACJufxH1jo=hv77AK0HUJYBBMuPmr6+JT+8g-yovuJmHUPGOZQ@mail.gmail.com
2025-05-06 17:17:46 -04:00
Bruce Momjian
575f6003ed doc PG 18 relnotes: move partition locking item to General Perf
Reported-by: Amit Langote

Discussion: https://postgr.es/m/CA+HiwqE+8Pui_NCCC7zgacnet0Cf3tc_vU+P=nhLDES-8xuCUw@mail.gmail.com
2025-05-06 16:03:56 -04:00
Bruce Momjian
45750c6cfe doc PG 18 relnotes: adjust partition items
Reported-by: David Rowley

Discussion: https://postgr.es/m/CAApHDvo+BrVTXMBPjNXBTnAovJWN9+-dYc0kN7rSDqdNvpggZQ@mail.gmail.com
2025-05-06 15:45:03 -04:00
317 changed files with 5084 additions and 5170 deletions

View File

@ -14,6 +14,9 @@
#
# $ git log --pretty=format:"%H # %cd%n# %s" $PGINDENTGITHASH -1 --date=iso
4672b6223910687b2aab075bcd2dd54ce90d5171 # 2025-06-01 14:55:24 -0400
# Run pgindent on the previous commit.
918e7287ed20eb1fe280ab6c4056ccf94dcd53a8 # 2025-04-30 19:18:30 +1200
# Fix broken indentation

View File

@ -1,5 +1,5 @@
PostgreSQL Database Management System
(formerly known as Postgres, then as Postgres95)
(also known as Postgres, formerly known as Postgres95)
Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group

15
configure vendored
View File

@ -15616,7 +15616,7 @@ fi
LIBS_including_readline="$LIBS"
LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
for ac_func in backtrace_symbols copyfile copy_file_range elf_aux_info getauxval getifaddrs getpeerucred inet_pton kqueue localeconv_l mbstowcs_l memset_s posix_fallocate ppoll pthread_is_threaded_np setproctitle setproctitle_fast strsignal syncfs sync_file_range uselocale wcstombs_l
for ac_func in backtrace_symbols copyfile copy_file_range elf_aux_info getauxval getifaddrs getpeerucred inet_pton kqueue localeconv_l mbstowcs_l posix_fallocate ppoll pthread_is_threaded_np setproctitle setproctitle_fast strsignal syncfs sync_file_range uselocale wcstombs_l
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
@ -16192,6 +16192,19 @@ cat >>confdefs.h <<_ACEOF
#define HAVE_DECL_STRCHRNUL $ac_have_decl
_ACEOF
ac_fn_c_check_decl "$LINENO" "memset_s" "ac_cv_have_decl_memset_s" "#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h>
"
if test "x$ac_cv_have_decl_memset_s" = xyes; then :
ac_have_decl=1
else
ac_have_decl=0
fi
cat >>confdefs.h <<_ACEOF
#define HAVE_DECL_MEMSET_S $ac_have_decl
_ACEOF
# This is probably only present on macOS, but may as well check always
ac_fn_c_check_decl "$LINENO" "F_FULLFSYNC" "ac_cv_have_decl_F_FULLFSYNC" "#include <fcntl.h>

View File

@ -1792,7 +1792,6 @@ AC_CHECK_FUNCS(m4_normalize([
kqueue
localeconv_l
mbstowcs_l
memset_s
posix_fallocate
ppoll
pthread_is_threaded_np
@ -1838,6 +1837,8 @@ AC_CHECK_DECLS([strlcat, strlcpy, strnlen, strsep, timingsafe_bcmp])
AC_CHECK_DECLS([preadv], [], [], [#include <sys/uio.h>])
AC_CHECK_DECLS([pwritev], [], [], [#include <sys/uio.h>])
AC_CHECK_DECLS([strchrnul], [], [], [#include <string.h>])
AC_CHECK_DECLS([memset_s], [], [], [#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h>])
# This is probably only present on macOS, but may as well check always
AC_CHECK_DECLS(F_FULLFSYNC, [], [], [#include <fcntl.h>])

View File

@ -81,7 +81,7 @@ static ExecutorRun_hook_type prev_ExecutorRun = NULL;
static ExecutorFinish_hook_type prev_ExecutorFinish = NULL;
static ExecutorEnd_hook_type prev_ExecutorEnd = NULL;
static bool explain_ExecutorStart(QueryDesc *queryDesc, int eflags);
static void explain_ExecutorStart(QueryDesc *queryDesc, int eflags);
static void explain_ExecutorRun(QueryDesc *queryDesc,
ScanDirection direction,
uint64 count);
@ -261,11 +261,9 @@ _PG_init(void)
/*
* ExecutorStart hook: start up logging if needed
*/
static bool
static void
explain_ExecutorStart(QueryDesc *queryDesc, int eflags)
{
bool plan_valid;
/*
* At the beginning of each top-level statement, decide whether we'll
* sample this statement. If nested-statement explaining is enabled,
@ -301,13 +299,9 @@ explain_ExecutorStart(QueryDesc *queryDesc, int eflags)
}
if (prev_ExecutorStart)
plan_valid = prev_ExecutorStart(queryDesc, eflags);
prev_ExecutorStart(queryDesc, eflags);
else
plan_valid = standard_ExecutorStart(queryDesc, eflags);
/* The plan may have become invalid during standard_ExecutorStart() */
if (!plan_valid)
return false;
standard_ExecutorStart(queryDesc, eflags);
if (auto_explain_enabled())
{
@ -325,8 +319,6 @@ explain_ExecutorStart(QueryDesc *queryDesc, int eflags)
MemoryContextSwitchTo(oldcxt);
}
}
return true;
}
/*

View File

@ -3,85 +3,85 @@
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION btree_gist UPDATE TO '1.8'" to load this file. \quit
CREATE FUNCTION gist_stratnum_btree(int)
CREATE FUNCTION gist_translate_cmptype_btree(int)
RETURNS smallint
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE PARALLEL SAFE STRICT;
ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_time_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_date_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_text_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_bpchar_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_bit_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_vbit_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_cidr_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_timetz_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_uuid_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_macaddr8_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_enum_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;
ALTER OPERATOR FAMILY gist_bool_ops USING gist ADD
FUNCTION 12 ("any", "any") gist_stratnum_btree (int) ;
FUNCTION 12 ("any", "any") gist_translate_cmptype_btree (int) ;

View File

@ -15,7 +15,7 @@ PG_MODULE_MAGIC_EXT(
PG_FUNCTION_INFO_V1(gbt_decompress);
PG_FUNCTION_INFO_V1(gbtreekey_in);
PG_FUNCTION_INFO_V1(gbtreekey_out);
PG_FUNCTION_INFO_V1(gist_stratnum_btree);
PG_FUNCTION_INFO_V1(gist_translate_cmptype_btree);
/**************************************************
* In/Out for keys
@ -62,7 +62,7 @@ gbt_decompress(PG_FUNCTION_ARGS)
* Returns the btree number for supported operators, otherwise invalid.
*/
Datum
gist_stratnum_btree(PG_FUNCTION_ARGS)
gist_translate_cmptype_btree(PG_FUNCTION_ARGS)
{
CompareType cmptype = PG_GETARG_INT32(0);

View File

@ -1,13 +1,13 @@
-- test stratnum support func
SELECT gist_stratnum_btree(7);
gist_stratnum_btree
---------------------
0
-- test stratnum translation support func
SELECT gist_translate_cmptype_btree(7);
gist_translate_cmptype_btree
------------------------------
0
(1 row)
SELECT gist_stratnum_btree(3);
gist_stratnum_btree
---------------------
3
SELECT gist_translate_cmptype_btree(3);
gist_translate_cmptype_btree
------------------------------
3
(1 row)

View File

@ -1,3 +1,3 @@
-- test stratnum support func
SELECT gist_stratnum_btree(7);
SELECT gist_stratnum_btree(3);
-- test stratnum translation support func
SELECT gist_translate_cmptype_btree(7);
SELECT gist_translate_cmptype_btree(3);

View File

@ -105,7 +105,7 @@ static PGresult *storeQueryResult(volatile storeInfo *sinfo, PGconn *conn, const
static void storeRow(volatile storeInfo *sinfo, PGresult *res, bool first);
static remoteConn *getConnectionByName(const char *name);
static HTAB *createConnHash(void);
static void createNewConnection(const char *name, remoteConn *rconn);
static remoteConn *createNewConnection(const char *name);
static void deleteConnection(const char *name);
static char **get_pkey_attnames(Relation rel, int16 *indnkeyatts);
static char **get_text_array_contents(ArrayType *array, int *numitems);
@ -119,7 +119,8 @@ static Relation get_rel_from_relname(text *relname_text, LOCKMODE lockmode, AclM
static char *generate_relation_name(Relation rel);
static void dblink_connstr_check(const char *connstr);
static bool dblink_connstr_has_pw(const char *connstr);
static void dblink_security_check(PGconn *conn, remoteConn *rconn, const char *connstr);
static void dblink_security_check(PGconn *conn, const char *connname,
const char *connstr);
static void dblink_res_error(PGconn *conn, const char *conname, PGresult *res,
bool fail, const char *fmt,...) pg_attribute_printf(5, 6);
static char *get_connect_string(const char *servername);
@ -147,16 +148,22 @@ static uint32 dblink_we_get_conn = 0;
static uint32 dblink_we_get_result = 0;
/*
* Following is list that holds multiple remote connections.
* Following is hash that holds multiple remote connections.
* Calling convention of each dblink function changes to accept
* connection name as the first parameter. The connection list is
* connection name as the first parameter. The connection hash is
* much like ecpg e.g. a mapping between a name and a PGconn object.
*
* To avoid potentially leaking a PGconn object in case of out-of-memory
* errors, we first create the hash entry, then open the PGconn.
* Hence, a hash entry whose rconn.conn pointer is NULL must be
* understood as a leftover from a failed create; it should be ignored
* by lookup operations, and silently replaced by create operations.
*/
typedef struct remoteConnHashEnt
{
char name[NAMEDATALEN];
remoteConn *rconn;
remoteConn rconn;
} remoteConnHashEnt;
/* initial number of connection hashes */
@ -233,7 +240,7 @@ dblink_get_conn(char *conname_or_str,
errmsg("could not establish connection"),
errdetail_internal("%s", msg)));
}
dblink_security_check(conn, rconn, connstr);
dblink_security_check(conn, NULL, connstr);
if (PQclientEncoding(conn) != GetDatabaseEncoding())
PQsetClientEncoding(conn, GetDatabaseEncodingName());
freeconn = true;
@ -296,15 +303,6 @@ dblink_connect(PG_FUNCTION_ARGS)
else if (PG_NARGS() == 1)
conname_or_str = text_to_cstring(PG_GETARG_TEXT_PP(0));
if (connname)
{
rconn = (remoteConn *) MemoryContextAlloc(TopMemoryContext,
sizeof(remoteConn));
rconn->conn = NULL;
rconn->openCursorCount = 0;
rconn->newXactForCursor = false;
}
/* first check for valid foreign data server */
connstr = get_connect_string(conname_or_str);
if (connstr == NULL)
@ -317,6 +315,13 @@ dblink_connect(PG_FUNCTION_ARGS)
if (dblink_we_connect == 0)
dblink_we_connect = WaitEventExtensionNew("DblinkConnect");
/* if we need a hashtable entry, make that first, since it might fail */
if (connname)
{
rconn = createNewConnection(connname);
Assert(rconn->conn == NULL);
}
/* OK to make connection */
conn = libpqsrv_connect(connstr, dblink_we_connect);
@ -324,8 +329,8 @@ dblink_connect(PG_FUNCTION_ARGS)
{
msg = pchomp(PQerrorMessage(conn));
libpqsrv_disconnect(conn);
if (rconn)
pfree(rconn);
if (connname)
deleteConnection(connname);
ereport(ERROR,
(errcode(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION),
@ -334,16 +339,16 @@ dblink_connect(PG_FUNCTION_ARGS)
}
/* check password actually used if not superuser */
dblink_security_check(conn, rconn, connstr);
dblink_security_check(conn, connname, connstr);
/* attempt to set client encoding to match server encoding, if needed */
if (PQclientEncoding(conn) != GetDatabaseEncoding())
PQsetClientEncoding(conn, GetDatabaseEncodingName());
/* all OK, save away the conn */
if (connname)
{
rconn->conn = conn;
createNewConnection(connname, rconn);
}
else
{
@ -383,10 +388,7 @@ dblink_disconnect(PG_FUNCTION_ARGS)
libpqsrv_disconnect(conn);
if (rconn)
{
deleteConnection(conname);
pfree(rconn);
}
else
pconn->conn = NULL;
@ -1304,6 +1306,9 @@ dblink_get_connections(PG_FUNCTION_ARGS)
hash_seq_init(&status, remoteConnHash);
while ((hentry = (remoteConnHashEnt *) hash_seq_search(&status)) != NULL)
{
/* ignore it if it's not an open connection */
if (hentry->rconn.conn == NULL)
continue;
/* stash away current value */
astate = accumArrayResult(astate,
CStringGetTextDatum(hentry->name),
@ -2539,8 +2544,8 @@ getConnectionByName(const char *name)
hentry = (remoteConnHashEnt *) hash_search(remoteConnHash,
key, HASH_FIND, NULL);
if (hentry)
return hentry->rconn;
if (hentry && hentry->rconn.conn != NULL)
return &hentry->rconn;
return NULL;
}
@ -2557,8 +2562,8 @@ createConnHash(void)
HASH_ELEM | HASH_STRINGS);
}
static void
createNewConnection(const char *name, remoteConn *rconn)
static remoteConn *
createNewConnection(const char *name)
{
remoteConnHashEnt *hentry;
bool found;
@ -2572,17 +2577,15 @@ createNewConnection(const char *name, remoteConn *rconn)
hentry = (remoteConnHashEnt *) hash_search(remoteConnHash, key,
HASH_ENTER, &found);
if (found)
{
libpqsrv_disconnect(rconn->conn);
pfree(rconn);
if (found && hentry->rconn.conn != NULL)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("duplicate connection name")));
}
hentry->rconn = rconn;
/* New, or reusable, so initialize the rconn struct to zeroes */
memset(&hentry->rconn, 0, sizeof(remoteConn));
return &hentry->rconn;
}
static void
@ -2671,9 +2674,12 @@ dblink_connstr_has_required_scram_options(const char *connstr)
* We need to make sure that the connection made used credentials
* which were provided by the user, so check what credentials were
* used to connect and then make sure that they came from the user.
*
* On failure, we close "conn" and also delete the hashtable entry
* identified by "connname" (if that's not NULL).
*/
static void
dblink_security_check(PGconn *conn, remoteConn *rconn, const char *connstr)
dblink_security_check(PGconn *conn, const char *connname, const char *connstr)
{
/* Superuser bypasses security check */
if (superuser())
@ -2703,8 +2709,8 @@ dblink_security_check(PGconn *conn, remoteConn *rconn, const char *connstr)
/* Otherwise, fail out */
libpqsrv_disconnect(conn);
if (rconn)
pfree(rconn);
if (connname)
deleteConnection(connname);
ereport(ERROR,
(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
@ -3218,7 +3224,7 @@ appendSCRAMKeysInfo(StringInfo buf)
len = pg_b64_enc_len(sizeof(MyProcPort->scram_ClientKey));
/* don't forget the zero-terminator */
client_key = palloc0(len + 1);
encoded_len = pg_b64_encode((const char *) MyProcPort->scram_ClientKey,
encoded_len = pg_b64_encode(MyProcPort->scram_ClientKey,
sizeof(MyProcPort->scram_ClientKey),
client_key, len);
if (encoded_len < 0)
@ -3227,7 +3233,7 @@ appendSCRAMKeysInfo(StringInfo buf)
len = pg_b64_enc_len(sizeof(MyProcPort->scram_ServerKey));
/* don't forget the zero-terminator */
server_key = palloc0(len + 1);
encoded_len = pg_b64_encode((const char *) MyProcPort->scram_ServerKey,
encoded_len = pg_b64_encode(MyProcPort->scram_ServerKey,
sizeof(MyProcPort->scram_ServerKey),
server_key, len);
if (encoded_len < 0)

View File

@ -251,4 +251,3 @@ sub setup_table
}
done_testing();

View File

@ -48,6 +48,10 @@ SET ROLE regress_file_fdw_superuser;
CREATE USER MAPPING FOR regress_file_fdw_superuser SERVER file_server;
CREATE USER MAPPING FOR regress_no_priv_user SERVER file_server;
-- validator tests
CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (foo 'bar'); -- ERROR
ERROR: invalid option "foo"
CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS ("a=b" 'true'); -- ERROR
ERROR: invalid option name "a=b": must not contain "="
CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'xml'); -- ERROR
ERROR: COPY format "xml" not recognized
CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'text', quote ':'); -- ERROR

View File

@ -55,6 +55,8 @@ CREATE USER MAPPING FOR regress_file_fdw_superuser SERVER file_server;
CREATE USER MAPPING FOR regress_no_priv_user SERVER file_server;
-- validator tests
CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (foo 'bar'); -- ERROR
CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS ("a=b" 'true'); -- ERROR
CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'xml'); -- ERROR
CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'text', quote ':'); -- ERROR
CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'text', escape ':'); -- ERROR

View File

@ -37,7 +37,7 @@ EXPLAIN (DEBUG) SELECT 1;
Subplans Needing Rewind: none
Relation OIDs: none
Executor Parameter Types: none
Parse Location: 16 for 8 bytes
Parse Location: 0 to end
(11 rows)
EXPLAIN (RANGE_TABLE) SELECT 1;
@ -436,7 +436,7 @@ $$);
Subplans Needing Rewind: none
Relation OIDs: NNN...
Executor Parameter Types: 23
Parse Location: 75 for 62 bytes
Parse Location: 0 to end
(47 rows)
RESET enable_hashjoin;

View File

@ -10,6 +10,8 @@ EXTENSION = pg_prewarm
DATA = pg_prewarm--1.1--1.2.sql pg_prewarm--1.1.sql pg_prewarm--1.0--1.1.sql
PGFILEDESC = "pg_prewarm - preload relation data into system buffer cache"
REGRESS = pg_prewarm
TAP_TESTS = 1
ifdef USE_PGXS

View File

@ -693,8 +693,15 @@ apw_dump_now(bool is_bgworker, bool dump_unlogged)
return 0;
}
block_info_array =
(BlockInfoRecord *) palloc(sizeof(BlockInfoRecord) * NBuffers);
/*
* With sufficiently large shared_buffers, allocation will exceed 1GB, so
* allow for a huge allocation to prevent outright failure.
*
* (In the future, it might be a good idea to redesign this to use a more
* memory-efficient data structure.)
*/
block_info_array = (BlockInfoRecord *)
palloc_extended((sizeof(BlockInfoRecord) * NBuffers), MCXT_ALLOC_HUGE);
for (num_blocks = 0, i = 0; i < NBuffers; i++)
{

View File

@ -0,0 +1,10 @@
-- Test pg_prewarm extension
CREATE EXTENSION pg_prewarm;
-- pg_prewarm() should fail if the target relation has no storage.
CREATE TABLE test (c1 int) PARTITION BY RANGE (c1);
SELECT pg_prewarm('test', 'buffer');
ERROR: relation "test" does not have storage
DETAIL: This operation is not supported for partitioned tables.
-- Cleanup
DROP TABLE test;
DROP EXTENSION pg_prewarm;

View File

@ -29,6 +29,11 @@ tests += {
'name': 'pg_prewarm',
'sd': meson.current_source_dir(),
'bd': meson.current_build_dir(),
'regress': {
'sql': [
'pg_prewarm',
],
},
'tap': {
'tests': [
't/001_basic.pl',

View File

@ -112,6 +112,14 @@ pg_prewarm(PG_FUNCTION_ARGS)
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind), get_rel_name(relOid));
/* Check that the relation has storage. */
if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("relation \"%s\" does not have storage",
RelationGetRelationName(rel)),
errdetail_relkind_not_supported(rel->rd_rel->relkind)));
/* Check that the fork exists. */
if (!smgrexists(RelationGetSmgr(rel), forkNumber))
ereport(ERROR,

View File

@ -0,0 +1,10 @@
-- Test pg_prewarm extension
CREATE EXTENSION pg_prewarm;
-- pg_prewarm() should fail if the target relation has no storage.
CREATE TABLE test (c1 int) PARTITION BY RANGE (c1);
SELECT pg_prewarm('test', 'buffer');
-- Cleanup
DROP TABLE test;
DROP EXTENSION pg_prewarm;

View File

@ -68,3 +68,61 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(4 rows)
-- Various parameter numbering patterns
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)
-- Unique query IDs with parameter numbers switched.
SELECT WHERE ($1::int, 7) IN ((8, $2::int), ($3::int, 9)) \bind '1' '2' '3' \g
--
(0 rows)
SELECT WHERE ($2::int, 10) IN ((11, $3::int), ($1::int, 12)) \bind '1' '2' '3' \g
--
(0 rows)
SELECT WHERE $1::int IN ($2::int, $3::int) \bind '1' '2' '3' \g
--
(0 rows)
SELECT WHERE $2::int IN ($3::int, $1::int) \bind '1' '2' '3' \g
--
(0 rows)
SELECT WHERE $3::int IN ($1::int, $2::int) \bind '1' '2' '3' \g
--
(0 rows)
-- Two groups of two queries with the same query ID.
SELECT WHERE '1'::int IN ($1::int, '2'::int) \bind '1' \g
--
(1 row)
SELECT WHERE '4'::int IN ($1::int, '5'::int) \bind '2' \g
--
(0 rows)
SELECT WHERE $2::int IN ($1::int, '1'::int) \bind '1' '2' \g
--
(0 rows)
SELECT WHERE $2::int IN ($1::int, '2'::int) \bind '3' '4' \g
--
(0 rows)
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
--------------------------------------------------------------+-------
SELECT WHERE $1::int IN ($2::int, $3::int) | 1
SELECT WHERE $2::int IN ($1::int, $3::int) | 2
SELECT WHERE $2::int IN ($1::int, $3::int) | 2
SELECT WHERE $2::int IN ($3::int, $1::int) | 1
SELECT WHERE $3::int IN ($1::int, $2::int) | 1
SELECT WHERE ($1::int, $4) IN (($5, $2::int), ($3::int, $6)) | 1
SELECT WHERE ($2::int, $4) IN (($5, $3::int), ($1::int, $6)) | 1
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(8 rows)

View File

@ -206,37 +206,37 @@ EXPLAIN (COSTS OFF) SELECT 1 UNION SELECT 2;
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C";
toplevel | calls | query
----------+-------+--------------------------------------------------------------------
f | 1 | DELETE FROM stats_track_tab
toplevel | calls | query
----------+-------+---------------------------------------------------------------------
t | 1 | EXPLAIN (COSTS OFF) (SELECT $1, $2)
f | 1 | EXPLAIN (COSTS OFF) (SELECT $1, $2);
t | 1 | EXPLAIN (COSTS OFF) (TABLE test_table)
f | 1 | EXPLAIN (COSTS OFF) (TABLE test_table);
t | 1 | EXPLAIN (COSTS OFF) (VALUES ($1, $2))
f | 1 | EXPLAIN (COSTS OFF) (VALUES ($1, $2));
t | 1 | EXPLAIN (COSTS OFF) DELETE FROM stats_track_tab
f | 1 | EXPLAIN (COSTS OFF) DELETE FROM stats_track_tab;
t | 1 | EXPLAIN (COSTS OFF) INSERT INTO stats_track_tab VALUES (($1))
t | 1 | EXPLAIN (COSTS OFF) MERGE INTO stats_track_tab +
| | USING (SELECT id FROM generate_series($1, $2) id) ON x = id +
| | WHEN MATCHED THEN UPDATE SET x = id +
f | 1 | EXPLAIN (COSTS OFF) INSERT INTO stats_track_tab VALUES (($1));
t | 1 | EXPLAIN (COSTS OFF) MERGE INTO stats_track_tab +
| | USING (SELECT id FROM generate_series($1, $2) id) ON x = id +
| | WHEN MATCHED THEN UPDATE SET x = id +
| | WHEN NOT MATCHED THEN INSERT (x) VALUES (id)
f | 1 | EXPLAIN (COSTS OFF) MERGE INTO stats_track_tab +
| | USING (SELECT id FROM generate_series($1, $2) id) ON x = id +
| | WHEN MATCHED THEN UPDATE SET x = id +
| | WHEN NOT MATCHED THEN INSERT (x) VALUES (id);
t | 1 | EXPLAIN (COSTS OFF) SELECT $1
t | 1 | EXPLAIN (COSTS OFF) SELECT $1 UNION SELECT $2
f | 1 | EXPLAIN (COSTS OFF) SELECT $1 UNION SELECT $2;
f | 1 | EXPLAIN (COSTS OFF) SELECT $1;
t | 1 | EXPLAIN (COSTS OFF) TABLE stats_track_tab
f | 1 | EXPLAIN (COSTS OFF) TABLE stats_track_tab;
t | 1 | EXPLAIN (COSTS OFF) UPDATE stats_track_tab SET x = $1 WHERE x = $2
f | 1 | EXPLAIN (COSTS OFF) UPDATE stats_track_tab SET x = $1 WHERE x = $2;
t | 1 | EXPLAIN (COSTS OFF) VALUES ($1)
f | 1 | INSERT INTO stats_track_tab VALUES (($1))
f | 1 | MERGE INTO stats_track_tab +
| | USING (SELECT id FROM generate_series($1, $2) id) ON x = id +
| | WHEN MATCHED THEN UPDATE SET x = id +
| | WHEN NOT MATCHED THEN INSERT (x) VALUES (id)
f | 1 | SELECT $1
f | 1 | SELECT $1 UNION SELECT $2
f | 1 | SELECT $1, $2
f | 1 | EXPLAIN (COSTS OFF) VALUES ($1);
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
f | 1 | TABLE stats_track_tab
f | 1 | TABLE test_table
f | 1 | UPDATE stats_track_tab SET x = $1 WHERE x = $2
f | 1 | VALUES ($1)
f | 1 | VALUES ($1, $2)
(23 rows)
-- EXPLAIN - top-level tracking.
@ -405,20 +405,20 @@ EXPLAIN (COSTS OFF) SELECT 1, 2 UNION SELECT 3, 4\; EXPLAIN (COSTS OFF) (SELECT
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C";
toplevel | calls | query
----------+-------+-----------------------------------------------------------------
f | 1 | (SELECT $1, $2, $3) UNION SELECT $4, $5, $6
toplevel | calls | query
----------+-------+---------------------------------------------------------------------------------------------------------------------
t | 1 | EXPLAIN (COSTS OFF) (SELECT $1, $2, $3)
t | 1 | EXPLAIN (COSTS OFF) (SELECT $1, $2, $3) UNION SELECT $4, $5, $6
f | 1 | EXPLAIN (COSTS OFF) (SELECT $1, $2, $3); EXPLAIN (COSTS OFF) (SELECT 1, 2, 3, 4);
t | 1 | EXPLAIN (COSTS OFF) (SELECT $1, $2, $3, $4)
f | 1 | EXPLAIN (COSTS OFF) (SELECT 1, 2, 3); EXPLAIN (COSTS OFF) (SELECT $1, $2, $3, $4);
t | 1 | EXPLAIN (COSTS OFF) SELECT $1
t | 1 | EXPLAIN (COSTS OFF) SELECT $1, $2
t | 1 | EXPLAIN (COSTS OFF) SELECT $1, $2 UNION SELECT $3, $4
f | 1 | SELECT $1
f | 1 | SELECT $1, $2
f | 1 | SELECT $1, $2 UNION SELECT $3, $4
f | 1 | SELECT $1, $2, $3
f | 1 | SELECT $1, $2, $3, $4
f | 1 | EXPLAIN (COSTS OFF) SELECT $1, $2 UNION SELECT $3, $4; EXPLAIN (COSTS OFF) (SELECT 1, 2, 3) UNION SELECT 3, 4, 5;
f | 1 | EXPLAIN (COSTS OFF) SELECT $1; EXPLAIN (COSTS OFF) SELECT 1, 2;
f | 1 | EXPLAIN (COSTS OFF) SELECT 1, 2 UNION SELECT 3, 4; EXPLAIN (COSTS OFF) (SELECT $1, $2, $3) UNION SELECT $4, $5, $6;
f | 1 | EXPLAIN (COSTS OFF) SELECT 1; EXPLAIN (COSTS OFF) SELECT $1, $2;
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(13 rows)
@ -494,29 +494,29 @@ EXPLAIN (COSTS OFF) INSERT INTO stats_track_tab VALUES ((1))\; EXPLAIN (COSTS OF
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C";
toplevel | calls | query
----------+-------+--------------------------------------------------------------------
f | 1 | DELETE FROM stats_track_tab
f | 1 | DELETE FROM stats_track_tab WHERE x = $1
toplevel | calls | query
----------+-------+----------------------------------------------------------------------------------------------------------------------------------
t | 1 | EXPLAIN (COSTS OFF) (TABLE test_table)
t | 1 | EXPLAIN (COSTS OFF) (VALUES ($1, $2))
t | 1 | EXPLAIN (COSTS OFF) DELETE FROM stats_track_tab
t | 1 | EXPLAIN (COSTS OFF) DELETE FROM stats_track_tab WHERE x = $1
f | 1 | EXPLAIN (COSTS OFF) DELETE FROM stats_track_tab; EXPLAIN (COSTS OFF) DELETE FROM stats_track_tab WHERE x = $1;
f | 1 | EXPLAIN (COSTS OFF) DELETE FROM stats_track_tab; EXPLAIN (COSTS OFF) DELETE FROM stats_track_tab WHERE x = 1;
t | 1 | EXPLAIN (COSTS OFF) INSERT INTO stats_track_tab VALUES ($1), ($2)
t | 1 | EXPLAIN (COSTS OFF) INSERT INTO stats_track_tab VALUES (($1))
f | 1 | EXPLAIN (COSTS OFF) INSERT INTO stats_track_tab VALUES (($1)); EXPLAIN (COSTS OFF) INSERT INTO stats_track_tab VALUES (1), (2);
f | 1 | EXPLAIN (COSTS OFF) INSERT INTO stats_track_tab VALUES ((1)); EXPLAIN (COSTS OFF) INSERT INTO stats_track_tab VALUES ($1), ($2);
t | 1 | EXPLAIN (COSTS OFF) TABLE stats_track_tab
f | 1 | EXPLAIN (COSTS OFF) TABLE stats_track_tab; EXPLAIN (COSTS OFF) (TABLE test_table);
f | 1 | EXPLAIN (COSTS OFF) TABLE stats_track_tab; EXPLAIN (COSTS OFF) (TABLE test_table);
t | 1 | EXPLAIN (COSTS OFF) UPDATE stats_track_tab SET x = $1
t | 1 | EXPLAIN (COSTS OFF) UPDATE stats_track_tab SET x = $1 WHERE x = $2
f | 1 | EXPLAIN (COSTS OFF) UPDATE stats_track_tab SET x = $1 WHERE x = $2; EXPLAIN (COSTS OFF) UPDATE stats_track_tab SET x = 1;
f | 1 | EXPLAIN (COSTS OFF) UPDATE stats_track_tab SET x = 1 WHERE x = 1; EXPLAIN (COSTS OFF) UPDATE stats_track_tab SET x = $1;
t | 1 | EXPLAIN (COSTS OFF) VALUES ($1)
f | 1 | INSERT INTO stats_track_tab VALUES ($1), ($2)
f | 1 | INSERT INTO stats_track_tab VALUES (($1))
f | 1 | EXPLAIN (COSTS OFF) VALUES ($1); EXPLAIN (COSTS OFF) (VALUES (1, 2));
f | 1 | EXPLAIN (COSTS OFF) VALUES (1); EXPLAIN (COSTS OFF) (VALUES ($1, $2));
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
f | 1 | TABLE stats_track_tab
f | 1 | TABLE test_table
f | 1 | UPDATE stats_track_tab SET x = $1
f | 1 | UPDATE stats_track_tab SET x = $1 WHERE x = $2
f | 1 | VALUES ($1)
f | 1 | VALUES ($1, $2)
(21 rows)
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
@ -547,18 +547,21 @@ EXPLAIN (COSTS OFF) MERGE INTO stats_track_tab
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C";
toplevel | calls | query
----------+-------+---------------------------------------------------------------
t | 1 | EXPLAIN (COSTS OFF) MERGE INTO stats_track_tab +
| | USING (SELECT id FROM generate_series($1, $2) id) ON x = id+
| | WHEN MATCHED THEN UPDATE SET x = id +
toplevel | calls | query
----------+-------+------------------------------------------------------------------------------------------------
t | 1 | EXPLAIN (COSTS OFF) MERGE INTO stats_track_tab +
| | USING (SELECT id FROM generate_series($1, $2) id) ON x = id +
| | WHEN MATCHED THEN UPDATE SET x = id +
| | WHEN NOT MATCHED THEN INSERT (x) VALUES (id)
f | 1 | EXPLAIN (COSTS OFF) MERGE INTO stats_track_tab +
| | USING (SELECT id FROM generate_series($1, $2) id) ON x = id +
| | WHEN MATCHED THEN UPDATE SET x = id +
| | WHEN NOT MATCHED THEN INSERT (x) VALUES (id); EXPLAIN (COSTS OFF) SELECT 1, 2, 3, 4, 5;
f | 1 | EXPLAIN (COSTS OFF) MERGE INTO stats_track_tab +
| | USING (SELECT id FROM generate_series(1, 10) id) ON x = id +
| | WHEN MATCHED THEN UPDATE SET x = id +
| | WHEN NOT MATCHED THEN INSERT (x) VALUES (id); EXPLAIN (COSTS OFF) SELECT $1, $2, $3, $4, $5;
t | 1 | EXPLAIN (COSTS OFF) SELECT $1, $2, $3, $4, $5
f | 1 | MERGE INTO stats_track_tab +
| | USING (SELECT id FROM generate_series($1, $2) id) ON x = id+
| | WHEN MATCHED THEN UPDATE SET x = id +
| | WHEN NOT MATCHED THEN INSERT (x) VALUES (id)
f | 1 | SELECT $1, $2, $3, $4, $5
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
@ -786,29 +789,29 @@ EXPLAIN (COSTS OFF) WITH a AS (select 4) SELECT 1 UNION SELECT 2;
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C";
toplevel | calls | query
----------+-------+------------------------------------------------------------------------------------------
toplevel | calls | query
----------+-------+-------------------------------------------------------------------------------------------
t | 1 | EXPLAIN (COSTS OFF) (WITH a AS (SELECT $1) (SELECT $2, $3))
f | 1 | EXPLAIN (COSTS OFF) (WITH a AS (SELECT $1) (SELECT $2, $3));
t | 1 | EXPLAIN (COSTS OFF) WITH a AS (SELECT $1) DELETE FROM stats_track_tab
f | 1 | EXPLAIN (COSTS OFF) WITH a AS (SELECT $1) DELETE FROM stats_track_tab;
t | 1 | EXPLAIN (COSTS OFF) WITH a AS (SELECT $1) INSERT INTO stats_track_tab VALUES (($2))
t | 1 | EXPLAIN (COSTS OFF) WITH a AS (SELECT $1) MERGE INTO stats_track_tab +
| | USING (SELECT id FROM generate_series($2, $3) id) ON x = id +
| | WHEN MATCHED THEN UPDATE SET x = id +
f | 1 | EXPLAIN (COSTS OFF) WITH a AS (SELECT $1) INSERT INTO stats_track_tab VALUES (($2));
t | 1 | EXPLAIN (COSTS OFF) WITH a AS (SELECT $1) MERGE INTO stats_track_tab +
| | USING (SELECT id FROM generate_series($2, $3) id) ON x = id +
| | WHEN MATCHED THEN UPDATE SET x = id +
| | WHEN NOT MATCHED THEN INSERT (x) VALUES (id)
f | 1 | EXPLAIN (COSTS OFF) WITH a AS (SELECT $1) MERGE INTO stats_track_tab +
| | USING (SELECT id FROM generate_series($2, $3) id) ON x = id +
| | WHEN MATCHED THEN UPDATE SET x = id +
| | WHEN NOT MATCHED THEN INSERT (x) VALUES (id);
t | 1 | EXPLAIN (COSTS OFF) WITH a AS (SELECT $1) SELECT $2
f | 1 | EXPLAIN (COSTS OFF) WITH a AS (SELECT $1) SELECT $2;
t | 1 | EXPLAIN (COSTS OFF) WITH a AS (SELECT $1) UPDATE stats_track_tab SET x = $2 WHERE x = $3
f | 1 | EXPLAIN (COSTS OFF) WITH a AS (SELECT $1) UPDATE stats_track_tab SET x = $2 WHERE x = $3;
t | 1 | EXPLAIN (COSTS OFF) WITH a AS (select $1) SELECT $2 UNION SELECT $3
f | 1 | EXPLAIN (COSTS OFF) WITH a AS (select $1) SELECT $2 UNION SELECT $3;
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
f | 1 | WITH a AS (SELECT $1) (SELECT $2, $3)
f | 1 | WITH a AS (SELECT $1) DELETE FROM stats_track_tab
f | 1 | WITH a AS (SELECT $1) INSERT INTO stats_track_tab VALUES (($2))
f | 1 | WITH a AS (SELECT $1) MERGE INTO stats_track_tab +
| | USING (SELECT id FROM generate_series($2, $3) id) ON x = id +
| | WHEN MATCHED THEN UPDATE SET x = id +
| | WHEN NOT MATCHED THEN INSERT (x) VALUES (id)
f | 1 | WITH a AS (SELECT $1) SELECT $2
f | 1 | WITH a AS (SELECT $1) UPDATE stats_track_tab SET x = $2 WHERE x = $3
f | 1 | WITH a AS (select $1) SELECT $2 UNION SELECT $3
(15 rows)
-- EXPLAIN with CTEs - top-level tracking
@ -918,13 +921,14 @@ EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF)
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C";
toplevel | calls | query
----------+-------+------------------------------------------------------------------------------
t | 1 | EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF) +
toplevel | calls | query
----------+-------+-------------------------------------------------------------------------------
t | 1 | EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF) +
| | DECLARE foocur CURSOR FOR SELECT * FROM stats_track_tab
f | 1 | EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF) +
| | DECLARE foocur CURSOR FOR SELECT * FROM stats_track_tab;
t | 1 | EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF) SELECT $1
f | 1 | SELECT $1
f | 1 | SELECT * FROM stats_track_tab
f | 1 | EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF) SELECT $1;
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
@ -1047,10 +1051,10 @@ SELECT toplevel, calls, query FROM pg_stat_statements
toplevel | calls | query
----------+-------+-----------------------------------------------------------------
t | 1 | CREATE TEMPORARY TABLE pgss_ctas_1 AS SELECT $1
f | 1 | CREATE TEMPORARY TABLE pgss_ctas_1 AS SELECT $1;
t | 1 | CREATE TEMPORARY TABLE pgss_ctas_2 AS EXECUTE test_prepare_pgss
f | 1 | SELECT $1
f | 1 | PREPARE test_prepare_pgss AS select generate_series($1, $2)
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
f | 1 | select generate_series($1, $2)
(5 rows)
-- CREATE TABLE AS, top-level tracking.
@ -1088,10 +1092,10 @@ EXPLAIN (COSTS OFF) CREATE TEMPORARY TABLE pgss_explain_ctas AS SELECT 1;
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C";
toplevel | calls | query
----------+-------+---------------------------------------------------------------------------
toplevel | calls | query
----------+-------+----------------------------------------------------------------------------
t | 1 | EXPLAIN (COSTS OFF) CREATE TEMPORARY TABLE pgss_explain_ctas AS SELECT $1
f | 1 | SELECT $1
f | 1 | EXPLAIN (COSTS OFF) CREATE TEMPORARY TABLE pgss_explain_ctas AS SELECT $1;
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
@ -1136,14 +1140,14 @@ CLOSE foocur;
COMMIT;
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C";
toplevel | calls | query
----------+-------+---------------------------------------------------------
toplevel | calls | query
----------+-------+----------------------------------------------------------
t | 1 | BEGIN
t | 1 | CLOSE foocur
t | 1 | COMMIT
t | 1 | DECLARE FOOCUR CURSOR FOR SELECT * from stats_track_tab
f | 1 | DECLARE FOOCUR CURSOR FOR SELECT * from stats_track_tab;
t | 1 | FETCH FORWARD 1 FROM foocur
f | 1 | SELECT * from stats_track_tab
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(7 rows)
@ -1203,25 +1207,25 @@ COPY (DELETE FROM stats_track_tab WHERE x = 2 RETURNING x) TO stdout;
2
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C";
toplevel | calls | query
----------+-------+---------------------------------------------------------------------------
toplevel | calls | query
----------+-------+-----------------------------------------------------------------------------
f | 1 | COPY (DELETE FROM stats_track_tab WHERE x = $1 RETURNING x) TO stdout
t | 1 | COPY (DELETE FROM stats_track_tab WHERE x = 2 RETURNING x) TO stdout
f | 1 | COPY (INSERT INTO stats_track_tab (x) VALUES ($1) RETURNING x) TO stdout
t | 1 | COPY (INSERT INTO stats_track_tab (x) VALUES (1) RETURNING x) TO stdout
t | 1 | COPY (MERGE INTO stats_track_tab USING (SELECT 1 id) ON x = id +
| | WHEN MATCHED THEN UPDATE SET x = id +
f | 1 | COPY (MERGE INTO stats_track_tab USING (SELECT $1 id) ON x = id +
| | WHEN MATCHED THEN UPDATE SET x = id +
| | WHEN NOT MATCHED THEN INSERT (x) VALUES (id) RETURNING x) TO stdout
t | 1 | COPY (MERGE INTO stats_track_tab USING (SELECT 1 id) ON x = id +
| | WHEN MATCHED THEN UPDATE SET x = id +
| | WHEN NOT MATCHED THEN INSERT (x) VALUES (id) RETURNING x) TO stdout
f | 1 | COPY (SELECT $1 UNION SELECT $2) TO stdout
f | 1 | COPY (SELECT $1) TO stdout
t | 1 | COPY (SELECT 1 UNION SELECT 2) TO stdout
t | 1 | COPY (SELECT 1) TO stdout
f | 1 | COPY (UPDATE stats_track_tab SET x = $1 WHERE x = $2 RETURNING x) TO stdout
t | 1 | COPY (UPDATE stats_track_tab SET x = 2 WHERE x = 1 RETURNING x) TO stdout
f | 1 | DELETE FROM stats_track_tab WHERE x = $1 RETURNING x
f | 1 | INSERT INTO stats_track_tab (x) VALUES ($1) RETURNING x
f | 1 | MERGE INTO stats_track_tab USING (SELECT $1 id) ON x = id +
| | WHEN MATCHED THEN UPDATE SET x = id +
| | WHEN NOT MATCHED THEN INSERT (x) VALUES (id) RETURNING x
f | 1 | SELECT $1
f | 1 | SELECT $1 UNION SELECT $2
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
f | 1 | UPDATE stats_track_tab SET x = $1 WHERE x = $2 RETURNING x
(13 rows)
-- COPY - top-level tracking.
@ -1319,6 +1323,57 @@ SELECT toplevel, calls, query FROM pg_stat_statements
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(4 rows)
-- DO block --- multiple inner queries with separators
SET pg_stat_statements.track = 'all';
SET pg_stat_statements.track_utility = TRUE;
CREATE TABLE pgss_do_util_tab_1 (a int);
CREATE TABLE pgss_do_util_tab_2 (a int);
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)
DO $$
DECLARE BEGIN
EXECUTE 'CREATE TABLE pgss_do_table (id INT); DROP TABLE pgss_do_table';
EXECUTE 'SELECT a FROM pgss_do_util_tab_1; SELECT a FROM pgss_do_util_tab_2';
END $$;
SELECT toplevel, calls, rows, query FROM pg_stat_statements
WHERE toplevel IS FALSE
ORDER BY query COLLATE "C";
toplevel | calls | rows | query
----------+-------+------+-------------------------------------
f | 1 | 0 | CREATE TABLE pgss_do_table (id INT)
f | 1 | 0 | DROP TABLE pgss_do_table
f | 1 | 0 | SELECT a FROM pgss_do_util_tab_1
f | 1 | 0 | SELECT a FROM pgss_do_util_tab_2
(4 rows)
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)
-- Note the extra semicolon at the end of the query.
DO $$
DECLARE BEGIN
EXECUTE 'CREATE TABLE pgss_do_table (id INT); DROP TABLE pgss_do_table;';
EXECUTE 'SELECT a FROM pgss_do_util_tab_1; SELECT a FROM pgss_do_util_tab_2;';
END $$;
SELECT toplevel, calls, rows, query FROM pg_stat_statements
WHERE toplevel IS FALSE
ORDER BY query COLLATE "C";
toplevel | calls | rows | query
----------+-------+------+-------------------------------------
f | 1 | 0 | CREATE TABLE pgss_do_table (id INT)
f | 1 | 0 | DROP TABLE pgss_do_table
f | 1 | 0 | SELECT a FROM pgss_do_util_tab_1
f | 1 | 0 | SELECT a FROM pgss_do_util_tab_2
(4 rows)
DROP TABLE pgss_do_util_tab_1, pgss_do_util_tab_2;
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;

View File

@ -58,7 +58,7 @@ SELECT 42;
(1 row)
SELECT plans, calls, rows, query FROM pg_stat_statements
WHERE query NOT LIKE 'SELECT COUNT%' ORDER BY query COLLATE "C";
WHERE query NOT LIKE 'PREPARE%' ORDER BY query COLLATE "C";
plans | calls | rows | query
-------+-------+------+----------------------------------------------------------
0 | 1 | 0 | ALTER TABLE stats_plan_test ADD COLUMN x int
@ -72,10 +72,10 @@ SELECT plans, calls, rows, query FROM pg_stat_statements
-- for the prepared statement we expect at least one replan, but cache
-- invalidations could force more
SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_stat_statements
WHERE query LIKE 'SELECT COUNT%' ORDER BY query COLLATE "C";
plans_ok | calls | rows | query
----------+-------+------+--------------------------------------
t | 4 | 4 | SELECT COUNT(*) FROM stats_plan_test
WHERE query LIKE 'PREPARE%' ORDER BY query COLLATE "C";
plans_ok | calls | rows | query
----------+-------+------+-------------------------------------------------------
t | 4 | 4 | PREPARE prep1 AS SELECT COUNT(*) FROM stats_plan_test
(1 row)
-- Cleanup

View File

@ -208,6 +208,7 @@ DEALLOCATE pgss_test;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
calls | rows | query
-------+------+------------------------------------------------------------------------------
1 | 1 | PREPARE pgss_test (int) AS SELECT $1, $2 LIMIT $3
4 | 4 | SELECT $1 +
| | -- but this one will appear +
| | AS "text"
@ -221,7 +222,6 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
2 | 2 | SELECT $1 AS "int" ORDER BY 1
1 | 2 | SELECT $1 AS i UNION SELECT $2 ORDER BY i
1 | 1 | SELECT $1 || $2
1 | 1 | SELECT $1, $2 LIMIT $3
2 | 2 | SELECT DISTINCT $1 AS "int"
0 | 0 | SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"
1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
@ -238,6 +238,65 @@ SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
(1 row)
-- normalization of constants and parameters, with constant locations
-- recorded one or more times.
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)
SELECT WHERE '1' IN ('1'::int, '3'::int::text);
--
(1 row)
SELECT WHERE (1, 2) IN ((1, 2), (2, 3));
--
(1 row)
SELECT WHERE (3, 4) IN ((5, 6), (8, 7));
--
(0 rows)
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
------------------------------------------------------------------------+-------
SELECT WHERE $1 IN ($2::int, $3::int::text) | 1
SELECT WHERE ($1, $2) IN (($3, $4), ($5, $6)) | 2
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C" | 0
(4 rows)
-- with the last element being an explicit function call with an argument, ensure
-- the normalization of the squashing interval is correct.
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)
SELECT WHERE 1 IN (1, int4(1), int4(2));
--
(1 row)
SELECT WHERE 1 = ANY (ARRAY[1, int4(1), int4(2)]);
--
(1 row)
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
------------------------------------------------------------------------+-------
SELECT WHERE $1 IN ($2 /*, ... */) | 2
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C" | 0
(3 rows)
--
-- queries with locking clauses
--

View File

@ -2,9 +2,11 @@
-- Const squashing functionality
--
CREATE EXTENSION pg_stat_statements;
--
-- Simple Lists
--
CREATE TABLE test_squash (id int, data int);
-- IN queries
-- Normal scenario, too many simple constants for an IN query
-- single element will not be squashed
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
@ -16,42 +18,150 @@ SELECT * FROM test_squash WHERE id IN (1);
----+------
(0 rows)
SELECT ARRAY[1];
array
-------
{1}
(1 row)
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
----------------------------------------------------+-------
SELECT * FROM test_squash WHERE id IN ($1) | 1
SELECT ARRAY[$1] | 1
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(3 rows)
-- more than 1 element in a list will be squashed
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)
SELECT * FROM test_squash WHERE id IN (1, 2, 3);
id | data
----+------
(0 rows)
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4);
id | data
----+------
(0 rows)
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5);
id | data
----+------
(0 rows)
SELECT ARRAY[1, 2, 3];
array
---------
{1,2,3}
(1 row)
SELECT ARRAY[1, 2, 3, 4];
array
-----------
{1,2,3,4}
(1 row)
SELECT ARRAY[1, 2, 3, 4, 5];
array
-------------
{1,2,3,4,5}
(1 row)
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
-------------------------------------------------------+-------
SELECT * FROM test_squash WHERE id IN ($1 /*, ... */) | 1
SELECT * FROM test_squash WHERE id IN ($1) | 1
SELECT * FROM test_squash WHERE id IN ($1 /*, ... */) | 3
SELECT ARRAY[$1 /*, ... */] | 3
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(3 rows)
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9);
-- built-in functions will be squashed
-- the IN and ARRAY forms of this statement will have the same queryId
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)
SELECT WHERE 1 IN (1, int4(1), int4(2), 2);
--
(1 row)
SELECT WHERE 1 = ANY (ARRAY[1, int4(1), int4(2), 2]);
--
(1 row)
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
----------------------------------------------------+-------
SELECT WHERE $1 IN ($2 /*, ... */) | 2
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(2 rows)
-- external parameters will not be squashed
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)
SELECT * FROM test_squash WHERE id IN ($1, $2, $3, $4, $5) \bind 1 2 3 4 5
;
id | data
----+------
(0 rows)
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
id | data
----+------
(0 rows)
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
SELECT * FROM test_squash WHERE id::text = ANY(ARRAY[$1, $2, $3, $4, $5]) \bind 1 2 3 4 5
;
id | data
----+------
(0 rows)
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
------------------------------------------------------------------------+-------
SELECT * FROM test_squash WHERE id IN ($1 /*, ... */) | 4
SELECT * FROM test_squash WHERE id IN ($1) | 1
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C" | 1
(4 rows)
query | calls
---------------------------------------------------------------------------+-------
SELECT * FROM test_squash WHERE id IN ($1, $2, $3, $4, $5) | 1
SELECT * FROM test_squash WHERE id::text = ANY(ARRAY[$1, $2, $3, $4, $5]) | 1
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(3 rows)
-- neither are prepared statements
-- the IN and ARRAY forms of this statement will have the same queryId
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)
PREPARE p1(int, int, int, int, int) AS
SELECT * FROM test_squash WHERE id IN ($1, $2, $3, $4, $5);
EXECUTE p1(1, 2, 3, 4, 5);
id | data
----+------
(0 rows)
DEALLOCATE p1;
PREPARE p1(int, int, int, int, int) AS
SELECT * FROM test_squash WHERE id = ANY(ARRAY[$1, $2, $3, $4, $5]);
EXECUTE p1(1, 2, 3, 4, 5);
id | data
----+------
(0 rows)
DEALLOCATE p1;
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
------------------------------------------------------------+-------
DEALLOCATE $1 | 2
PREPARE p1(int, int, int, int, int) AS +| 2
SELECT * FROM test_squash WHERE id IN ($1, $2, $3, $4, $5) |
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(3 rows)
-- More conditions in the query
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
@ -75,10 +185,25 @@ SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) AND da
----+------
(0 rows)
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9]) AND data = 2;
id | data
----+------
(0 rows)
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) AND data = 2;
id | data
----+------
(0 rows)
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) AND data = 2;
id | data
----+------
(0 rows)
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
---------------------------------------------------------------------+-------
SELECT * FROM test_squash WHERE id IN ($1 /*, ... */) AND data = $2 | 3
SELECT * FROM test_squash WHERE id IN ($1 /*, ... */) AND data = $2 | 6
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(2 rows)
@ -107,24 +232,46 @@ SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
----+------
(0 rows)
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9])
AND data = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9]);
id | data
----+------
(0 rows)
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
AND data = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
id | data
----+------
(0 rows)
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
AND data = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
id | data
----+------
(0 rows)
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
-------------------------------------------------------+-------
SELECT * FROM test_squash WHERE id IN ($1 /*, ... */)+| 3
SELECT * FROM test_squash WHERE id IN ($1 /*, ... */)+| 6
AND data IN ($2 /*, ... */) |
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(2 rows)
-- No constants simplification for OpExpr
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)
-- In the following two queries the operator expressions (+) and (@) have
-- different oppno, and will be given different query_id if squashed, even though
-- the normalized query will be the same
-- No constants squashing for OpExpr
-- The IN and ARRAY forms of this statement will have the same queryId
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)
SELECT * FROM test_squash WHERE id IN
(1 + 1, 2 + 2, 3 + 3, 4 + 4, 5 + 5, 6 + 6, 7 + 7, 8 + 8, 9 + 9);
id | data
@ -137,19 +284,35 @@ SELECT * FROM test_squash WHERE id IN
----+------
(0 rows)
SELECT * FROM test_squash WHERE id = ANY(ARRAY
[1 + 1, 2 + 2, 3 + 3, 4 + 4, 5 + 5, 6 + 6, 7 + 7, 8 + 8, 9 + 9]);
id | data
----+------
(0 rows)
SELECT * FROM test_squash WHERE id = ANY(ARRAY
[@ '-1', @ '-2', @ '-3', @ '-4', @ '-5', @ '-6', @ '-7', @ '-8', @ '-9']);
id | data
----+------
(0 rows)
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
----------------------------------------------------------------------------------------------------+-------
SELECT * FROM test_squash WHERE id IN +| 1
SELECT * FROM test_squash WHERE id IN +| 2
($1 + $2, $3 + $4, $5 + $6, $7 + $8, $9 + $10, $11 + $12, $13 + $14, $15 + $16, $17 + $18) |
SELECT * FROM test_squash WHERE id IN +| 1
SELECT * FROM test_squash WHERE id IN +| 2
(@ $1, @ $2, @ $3, @ $4, @ $5, @ $6, @ $7, @ $8, @ $9) |
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(3 rows)
--
-- FuncExpr
--
-- Verify multiple type representation end up with the same query_id
CREATE TABLE test_float (data float);
-- The casted ARRAY expressions will have the same queryId as the IN clause
-- form of the query
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
@ -181,12 +344,38 @@ SELECT data FROM test_float WHERE data IN (1.0, 1.0);
------
(0 rows)
SELECT data FROM test_float WHERE data = ANY(ARRAY['1'::double precision, '2'::double precision]);
data
------
(0 rows)
SELECT data FROM test_float WHERE data = ANY(ARRAY[1.0::double precision, 1.0::double precision]);
data
------
(0 rows)
SELECT data FROM test_float WHERE data = ANY(ARRAY[1, 2]);
data
------
(0 rows)
SELECT data FROM test_float WHERE data = ANY(ARRAY[1, '2']);
data
------
(0 rows)
SELECT data FROM test_float WHERE data = ANY(ARRAY['1', 2]);
data
------
(0 rows)
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
-----------------------------------------------------------+-------
SELECT data FROM test_float WHERE data IN ($1 /*, ... */) | 5
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(2 rows)
query | calls
--------------------------------------------------------------------+-------
SELECT data FROM test_float WHERE data = ANY(ARRAY[$1 /*, ... */]) | 3
SELECT data FROM test_float WHERE data IN ($1 /*, ... */) | 7
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(3 rows)
-- Numeric type, implicit cast is squashed
CREATE TABLE test_squash_numeric (id int, data numeric(5, 2));
@ -201,12 +390,18 @@ SELECT * FROM test_squash_numeric WHERE data IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
----+------
(0 rows)
SELECT * FROM test_squash_numeric WHERE data = ANY(ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
id | data
----+------
(0 rows)
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
-----------------------------------------------------------------+-------
SELECT * FROM test_squash_numeric WHERE data IN ($1 /*, ... */) | 1
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(2 rows)
query | calls
--------------------------------------------------------------------------+-------
SELECT * FROM test_squash_numeric WHERE data = ANY(ARRAY[$1 /*, ... */]) | 1
SELECT * FROM test_squash_numeric WHERE data IN ($1 /*, ... */) | 1
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(3 rows)
-- Bigint, implicit cast is squashed
CREATE TABLE test_squash_bigint (id int, data bigint);
@ -221,14 +416,20 @@ SELECT * FROM test_squash_bigint WHERE data IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1
----+------
(0 rows)
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
----------------------------------------------------------------+-------
SELECT * FROM test_squash_bigint WHERE data IN ($1 /*, ... */) | 1
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(2 rows)
SELECT * FROM test_squash_bigint WHERE data = ANY(ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
id | data
----+------
(0 rows)
-- Bigint, explicit cast is not squashed
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
-------------------------------------------------------------------------+-------
SELECT * FROM test_squash_bigint WHERE data = ANY(ARRAY[$1 /*, ... */]) | 1
SELECT * FROM test_squash_bigint WHERE data IN ($1 /*, ... */) | 1
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(3 rows)
-- Bigint, explicit cast is squashed
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
@ -242,15 +443,22 @@ SELECT * FROM test_squash_bigint WHERE data IN
----+------
(0 rows)
SELECT * FROM test_squash_bigint WHERE data = ANY(ARRAY[
1::bigint, 2::bigint, 3::bigint, 4::bigint, 5::bigint, 6::bigint,
7::bigint, 8::bigint, 9::bigint, 10::bigint, 11::bigint]);
id | data
----+------
(0 rows)
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
----------------------------------------------------+-------
SELECT * FROM test_squash_bigint WHERE data IN +| 1
($1 /*, ... */::bigint) |
SELECT * FROM test_squash_bigint WHERE data IN +| 2
($1 /*, ... */) |
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(2 rows)
-- Bigint, long tokens with parenthesis
-- Bigint, long tokens with parenthesis, will not squash
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
@ -264,44 +472,47 @@ SELECT * FROM test_squash_bigint WHERE id IN
----+------
(0 rows)
SELECT * FROM test_squash_bigint WHERE id = ANY(ARRAY[
abs(100), abs(200), abs(300), abs(400), abs(500), abs(600), abs(700),
abs(800), abs(900), abs(1000), ((abs(1100)))]);
id | data
----+------
(0 rows)
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
-------------------------------------------------------------------------+-------
SELECT * FROM test_squash_bigint WHERE id IN +| 1
SELECT * FROM test_squash_bigint WHERE id IN +| 2
(abs($1), abs($2), abs($3), abs($4), abs($5), abs($6), abs($7),+|
abs($8), abs($9), abs($10), ((abs($11)))) |
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(2 rows)
-- CoerceViaIO, SubLink instead of a Const
CREATE TABLE test_squash_jsonb (id int, data jsonb);
-- Multiple FuncExpr's. Will not squash
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)
SELECT * FROM test_squash_jsonb WHERE data IN
((SELECT '"1"')::jsonb, (SELECT '"2"')::jsonb, (SELECT '"3"')::jsonb,
(SELECT '"4"')::jsonb, (SELECT '"5"')::jsonb, (SELECT '"6"')::jsonb,
(SELECT '"7"')::jsonb, (SELECT '"8"')::jsonb, (SELECT '"9"')::jsonb,
(SELECT '"10"')::jsonb);
id | data
----+------
(0 rows)
SELECT WHERE 1 IN (1::int::bigint::int, 2::int::bigint::int);
--
(1 row)
SELECT WHERE 1 = ANY(ARRAY[1::int::bigint::int, 2::int::bigint::int]);
--
(1 row)
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
----------------------------------------------------------------------+-------
SELECT * FROM test_squash_jsonb WHERE data IN +| 1
((SELECT $1)::jsonb, (SELECT $2)::jsonb, (SELECT $3)::jsonb,+|
(SELECT $4)::jsonb, (SELECT $5)::jsonb, (SELECT $6)::jsonb,+|
(SELECT $7)::jsonb, (SELECT $8)::jsonb, (SELECT $9)::jsonb,+|
(SELECT $10)::jsonb) |
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
query | calls
----------------------------------------------------+-------
SELECT WHERE $1 IN ($2 /*, ... */) | 2
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(2 rows)
--
-- CoerceViaIO
--
-- Create some dummy type to force CoerceViaIO
CREATE TYPE casttesttype;
CREATE FUNCTION casttesttype_in(cstring)
@ -349,15 +560,25 @@ SELECT * FROM test_squash_cast WHERE data IN
----+------
(0 rows)
SELECT * FROM test_squash_cast WHERE data = ANY (ARRAY
[1::int4::casttesttype, 2::int4::casttesttype, 3::int4::casttesttype,
4::int4::casttesttype, 5::int4::casttesttype, 6::int4::casttesttype,
7::int4::casttesttype, 8::int4::casttesttype, 9::int4::casttesttype,
10::int4::casttesttype, 11::int4::casttesttype]);
id | data
----+------
(0 rows)
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
----------------------------------------------------+-------
SELECT * FROM test_squash_cast WHERE data IN +| 1
($1 /*, ... */::int4::casttesttype) |
SELECT * FROM test_squash_cast WHERE data IN +| 2
($1 /*, ... */) |
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(2 rows)
-- Some casting expression are simplified to Const
CREATE TABLE test_squash_jsonb (id int, data jsonb);
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
@ -366,8 +587,16 @@ SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT * FROM test_squash_jsonb WHERE data IN
(('"1"')::jsonb, ('"2"')::jsonb, ('"3"')::jsonb, ('"4"')::jsonb,
( '"5"')::jsonb, ( '"6"')::jsonb, ( '"7"')::jsonb, ( '"8"')::jsonb,
( '"9"')::jsonb, ( '"10"')::jsonb);
('"5"')::jsonb, ('"6"')::jsonb, ('"7"')::jsonb, ('"8"')::jsonb,
('"9"')::jsonb, ('"10"')::jsonb);
id | data
----+------
(0 rows)
SELECT * FROM test_squash_jsonb WHERE data = ANY (ARRAY
[('"1"')::jsonb, ('"2"')::jsonb, ('"3"')::jsonb, ('"4"')::jsonb,
('"5"')::jsonb, ('"6"')::jsonb, ('"7"')::jsonb, ('"8"')::jsonb,
('"9"')::jsonb, ('"10"')::jsonb]);
id | data
----+------
(0 rows)
@ -375,28 +604,144 @@ SELECT * FROM test_squash_jsonb WHERE data IN
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
----------------------------------------------------+-------
SELECT * FROM test_squash_jsonb WHERE data IN +| 1
(($1 /*, ... */)::jsonb) |
SELECT * FROM test_squash_jsonb WHERE data IN +| 2
($1 /*, ... */) |
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(2 rows)
-- RelabelType
-- CoerceViaIO, SubLink instead of a Const. Will not squash
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)
SELECT * FROM test_squash WHERE id IN (1::oid, 2::oid, 3::oid, 4::oid, 5::oid, 6::oid, 7::oid, 8::oid, 9::oid);
SELECT * FROM test_squash_jsonb WHERE data IN
((SELECT '"1"')::jsonb, (SELECT '"2"')::jsonb, (SELECT '"3"')::jsonb,
(SELECT '"4"')::jsonb, (SELECT '"5"')::jsonb, (SELECT '"6"')::jsonb,
(SELECT '"7"')::jsonb, (SELECT '"8"')::jsonb, (SELECT '"9"')::jsonb,
(SELECT '"10"')::jsonb);
id | data
----+------
(0 rows)
SELECT * FROM test_squash_jsonb WHERE data = ANY(ARRAY
[(SELECT '"1"')::jsonb, (SELECT '"2"')::jsonb, (SELECT '"3"')::jsonb,
(SELECT '"4"')::jsonb, (SELECT '"5"')::jsonb, (SELECT '"6"')::jsonb,
(SELECT '"7"')::jsonb, (SELECT '"8"')::jsonb, (SELECT '"9"')::jsonb,
(SELECT '"10"')::jsonb]);
id | data
----+------
(0 rows)
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
------------------------------------------------------------+-------
SELECT * FROM test_squash WHERE id IN ($1 /*, ... */::oid) | 1
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
query | calls
----------------------------------------------------------------------+-------
SELECT * FROM test_squash_jsonb WHERE data IN +| 2
((SELECT $1)::jsonb, (SELECT $2)::jsonb, (SELECT $3)::jsonb,+|
(SELECT $4)::jsonb, (SELECT $5)::jsonb, (SELECT $6)::jsonb,+|
(SELECT $7)::jsonb, (SELECT $8)::jsonb, (SELECT $9)::jsonb,+|
(SELECT $10)::jsonb) |
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(2 rows)
-- Multiple CoerceViaIO wrapping a constant. Will not squash
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)
SELECT WHERE 1 IN (1::text::int::text::int, 1::text::int::text::int);
--
(1 row)
SELECT WHERE 1 = ANY(ARRAY[1::text::int::text::int, 1::text::int::text::int]);
--
(1 row)
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
-------------------------------------------------------------------------+-------
SELECT WHERE $1 IN ($2::text::int::text::int, $3::text::int::text::int) | 2
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(2 rows)
--
-- RelabelType
--
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)
-- if there is only one level of RelabelType, the list will be squashable
SELECT * FROM test_squash WHERE id IN
(1::oid, 2::oid, 3::oid, 4::oid, 5::oid, 6::oid, 7::oid, 8::oid, 9::oid);
id | data
----+------
(0 rows)
SELECT ARRAY[1::oid, 2::oid, 3::oid, 4::oid, 5::oid, 6::oid, 7::oid, 8::oid, 9::oid];
array
---------------------
{1,2,3,4,5,6,7,8,9}
(1 row)
-- if there is at least one element with multiple levels of RelabelType,
-- the list will not be squashable
SELECT * FROM test_squash WHERE id IN (1::oid, 2::oid::int::oid);
id | data
----+------
(0 rows)
SELECT * FROM test_squash WHERE id = ANY(ARRAY[1::oid, 2::oid::int::oid]);
id | data
----+------
(0 rows)
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
--------------------------------------------------------------------+-------
SELECT * FROM test_squash WHERE id IN +| 1
($1 /*, ... */) |
SELECT * FROM test_squash WHERE id IN ($1::oid, $2::oid::int::oid) | 2
SELECT ARRAY[$1 /*, ... */] | 1
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(4 rows)
--
-- edge cases
--
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)
-- for nested arrays, only constants are squashed
SELECT ARRAY[
ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
];
array
-----------------------------------------------------------------------------------------------
{{1,2,3,4,5,6,7,8,9,10},{1,2,3,4,5,6,7,8,9,10},{1,2,3,4,5,6,7,8,9,10},{1,2,3,4,5,6,7,8,9,10}}
(1 row)
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
----------------------------------------------------+-------
SELECT ARRAY[ +| 1
ARRAY[$1 /*, ... */], +|
ARRAY[$2 /*, ... */], +|
ARRAY[$3 /*, ... */], +|
ARRAY[$4 /*, ... */] +|
] |
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
(2 rows)
-- Test constants evaluation in a CTE, which was causing issues in the past
@ -409,23 +754,59 @@ FROM cte;
--------
(0 rows)
-- Simple array would be squashed as well
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)
SELECT ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
array
------------------------
{1,2,3,4,5,6,7,8,9,10}
-- Rewritten as an OpExpr, so it will not be squashed
select where '1' IN ('1'::int, '2'::int::text);
--
(1 row)
-- Rewritten as an ArrayExpr, so it will be squashed
select where '1' IN ('1'::int, '2'::int);
--
(1 row)
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
----------------------------------------------------+-------
SELECT ARRAY[$1 /*, ... */] | 1
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
select where $1 IN ($2 /*, ... */) | 1
select where $1 IN ($2::int, $3::int::text) | 1
(3 rows)
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)
-- Both of these queries will be rewritten as an ArrayExpr, so they
-- will be squashed, and have a similar queryId
select where '1' IN ('1'::int::text, '2'::int::text);
--
(1 row)
select where '1' = ANY (array['1'::int::text, '2'::int::text]);
--
(1 row)
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
----------------------------------------------------+-------
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
select where $1 IN ($2 /*, ... */) | 2
(2 rows)
--
-- cleanup
--
DROP TABLE test_squash;
DROP TABLE test_float;
DROP TABLE test_squash_numeric;
DROP TABLE test_squash_bigint;
DROP TABLE test_squash_cast CASCADE;
DROP TABLE test_squash_jsonb;

View File

@ -540,7 +540,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+----------------------------------------------------
2 | 0 | DEALLOCATE $1
2 | 0 | DEALLOCATE ALL
2 | 2 | SELECT $1 AS a
2 | 2 | PREPARE stat_select AS SELECT $1 AS a
1 | 1 | SELECT $1 as a
1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)

View File

@ -144,7 +144,7 @@ typedef struct pgssHashKey
{
Oid userid; /* user OID */
Oid dbid; /* database OID */
uint64 queryid; /* query identifier */
int64 queryid; /* query identifier */
bool toplevel; /* query executed at top level */
} pgssHashKey;
@ -335,7 +335,7 @@ static PlannedStmt *pgss_planner(Query *parse,
const char *query_string,
int cursorOptions,
ParamListInfo boundParams);
static bool pgss_ExecutorStart(QueryDesc *queryDesc, int eflags);
static void pgss_ExecutorStart(QueryDesc *queryDesc, int eflags);
static void pgss_ExecutorRun(QueryDesc *queryDesc,
ScanDirection direction,
uint64 count);
@ -346,7 +346,7 @@ static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest, QueryCompletion *qc);
static void pgss_store(const char *query, uint64 queryId,
static void pgss_store(const char *query, int64 queryId,
int query_location, int query_len,
pgssStoreKind kind,
double total_time, uint64 rows,
@ -370,7 +370,7 @@ static char *qtext_fetch(Size query_offset, int query_len,
char *buffer, Size buffer_size);
static bool need_gc_qtexts(void);
static void gc_qtexts(void);
static TimestampTz entry_reset(Oid userid, Oid dbid, uint64 queryid, bool minmax_only);
static TimestampTz entry_reset(Oid userid, Oid dbid, int64 queryid, bool minmax_only);
static char *generate_normalized_query(JumbleState *jstate, const char *query,
int query_loc, int *query_len_p);
static void fill_in_constant_lengths(JumbleState *jstate, const char *query,
@ -852,7 +852,7 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
{
if (pgss_track_utility && IsA(query->utilityStmt, ExecuteStmt))
{
query->queryId = UINT64CONST(0);
query->queryId = INT64CONST(0);
return;
}
}
@ -899,7 +899,7 @@ pgss_planner(Query *parse,
*/
if (pgss_enabled(nesting_level)
&& pgss_track_planning && query_string
&& parse->queryId != UINT64CONST(0))
&& parse->queryId != INT64CONST(0))
{
instr_time start;
instr_time duration;
@ -989,26 +989,20 @@ pgss_planner(Query *parse,
/*
* ExecutorStart hook: start up tracking if needed
*/
static bool
static void
pgss_ExecutorStart(QueryDesc *queryDesc, int eflags)
{
bool plan_valid;
if (prev_ExecutorStart)
plan_valid = prev_ExecutorStart(queryDesc, eflags);
prev_ExecutorStart(queryDesc, eflags);
else
plan_valid = standard_ExecutorStart(queryDesc, eflags);
/* The plan may have become invalid during standard_ExecutorStart() */
if (!plan_valid)
return false;
standard_ExecutorStart(queryDesc, eflags);
/*
* If query has queryId zero, don't track it. This prevents double
* counting of optimizable statements that are directly contained in
* utility statements.
*/
if (pgss_enabled(nesting_level) && queryDesc->plannedstmt->queryId != UINT64CONST(0))
if (pgss_enabled(nesting_level) && queryDesc->plannedstmt->queryId != INT64CONST(0))
{
/*
* Set up to track total elapsed time in ExecutorRun. Make sure the
@ -1024,8 +1018,6 @@ pgss_ExecutorStart(QueryDesc *queryDesc, int eflags)
MemoryContextSwitchTo(oldcxt);
}
}
return true;
}
/*
@ -1076,9 +1068,9 @@ pgss_ExecutorFinish(QueryDesc *queryDesc)
static void
pgss_ExecutorEnd(QueryDesc *queryDesc)
{
uint64 queryId = queryDesc->plannedstmt->queryId;
int64 queryId = queryDesc->plannedstmt->queryId;
if (queryId != UINT64CONST(0) && queryDesc->totaltime &&
if (queryId != INT64CONST(0) && queryDesc->totaltime &&
pgss_enabled(nesting_level))
{
/*
@ -1119,7 +1111,7 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
DestReceiver *dest, QueryCompletion *qc)
{
Node *parsetree = pstmt->utilityStmt;
uint64 saved_queryId = pstmt->queryId;
int64 saved_queryId = pstmt->queryId;
int saved_stmt_location = pstmt->stmt_location;
int saved_stmt_len = pstmt->stmt_len;
bool enabled = pgss_track_utility && pgss_enabled(nesting_level);
@ -1139,7 +1131,7 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
* only.
*/
if (enabled)
pstmt->queryId = UINT64CONST(0);
pstmt->queryId = INT64CONST(0);
/*
* If it's an EXECUTE statement, we don't track it and don't increment the
@ -1286,7 +1278,7 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
* for the arrays in the Counters field.
*/
static void
pgss_store(const char *query, uint64 queryId,
pgss_store(const char *query, int64 queryId,
int query_location, int query_len,
pgssStoreKind kind,
double total_time, uint64 rows,
@ -1312,7 +1304,7 @@ pgss_store(const char *query, uint64 queryId,
* Nothing to do if compute_query_id isn't enabled and no other module
* computed a query identifier.
*/
if (queryId == UINT64CONST(0))
if (queryId == INT64CONST(0))
return;
/*
@ -1522,11 +1514,11 @@ pg_stat_statements_reset_1_7(PG_FUNCTION_ARGS)
{
Oid userid;
Oid dbid;
uint64 queryid;
int64 queryid;
userid = PG_GETARG_OID(0);
dbid = PG_GETARG_OID(1);
queryid = (uint64) PG_GETARG_INT64(2);
queryid = PG_GETARG_INT64(2);
entry_reset(userid, dbid, queryid, false);
@ -1538,12 +1530,12 @@ pg_stat_statements_reset_1_11(PG_FUNCTION_ARGS)
{
Oid userid;
Oid dbid;
uint64 queryid;
int64 queryid;
bool minmax_only;
userid = PG_GETARG_OID(0);
dbid = PG_GETARG_OID(1);
queryid = (uint64) PG_GETARG_INT64(2);
queryid = PG_GETARG_INT64(2);
minmax_only = PG_GETARG_BOOL(3);
PG_RETURN_TIMESTAMPTZ(entry_reset(userid, dbid, queryid, minmax_only));
@ -2679,7 +2671,7 @@ if (e) { \
* Reset entries corresponding to parameters passed.
*/
static TimestampTz
entry_reset(Oid userid, Oid dbid, uint64 queryid, bool minmax_only)
entry_reset(Oid userid, Oid dbid, int64 queryid, bool minmax_only)
{
HASH_SEQ_STATUS hash_seq;
pgssEntry *entry;
@ -2699,7 +2691,7 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid, bool minmax_only)
stats_reset = GetCurrentTimestamp();
if (userid != 0 && dbid != 0 && queryid != UINT64CONST(0))
if (userid != 0 && dbid != 0 && queryid != INT64CONST(0))
{
/* If all the parameters are available, use the fast path. */
memset(&key, 0, sizeof(pgssHashKey));
@ -2722,7 +2714,7 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid, bool minmax_only)
SINGLE_ENTRY_RESET(entry);
}
else if (userid != 0 || dbid != 0 || queryid != UINT64CONST(0))
else if (userid != 0 || dbid != 0 || queryid != INT64CONST(0))
{
/* Reset entries corresponding to valid parameters. */
hash_seq_init(&hash_seq, pgss_hash);
@ -2818,17 +2810,13 @@ generate_normalized_query(JumbleState *jstate, const char *query,
{
char *norm_query;
int query_len = *query_len_p;
int i,
norm_query_buflen, /* Space allowed for norm_query */
int norm_query_buflen, /* Space allowed for norm_query */
len_to_wrt, /* Length (in bytes) to write */
quer_loc = 0, /* Source query byte location */
n_quer_loc = 0, /* Normalized query byte location */
last_off = 0, /* Offset from start for previous tok */
last_tok_len = 0; /* Length (in bytes) of that tok */
bool in_squashed = false; /* in a run of squashed consts? */
int skipped_constants = 0; /* Position adjustment of later
* constants after squashed ones */
int num_constants_replaced = 0;
/*
* Get constants' lengths (core system only gives us locations). Note
@ -2842,16 +2830,13 @@ generate_normalized_query(JumbleState *jstate, const char *query,
* certainly isn't more than 11 bytes, even if n reaches INT_MAX. We
* could refine that limit based on the max value of n for the current
* query, but it hardly seems worth any extra effort to do so.
*
* Note this also gives enough room for the commented-out ", ..." list
* syntax used by constant squashing.
*/
norm_query_buflen = query_len + jstate->clocations_count * 10;
/* Allocate result buffer */
norm_query = palloc(norm_query_buflen + 1);
for (i = 0; i < jstate->clocations_count; i++)
for (int i = 0; i < jstate->clocations_count; i++)
{
int off, /* Offset from start for cur tok */
tok_len; /* Length (in bytes) of that tok */
@ -2866,67 +2851,24 @@ generate_normalized_query(JumbleState *jstate, const char *query,
if (tok_len < 0)
continue; /* ignore any duplicates */
/* Copy next chunk (what precedes the next constant) */
len_to_wrt = off - last_off;
len_to_wrt -= last_tok_len;
Assert(len_to_wrt >= 0);
memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
n_quer_loc += len_to_wrt;
/*
* What to do next depends on whether we're squashing constant lists,
* and whether we're already in a run of such constants.
* And insert a param symbol in place of the constant token; and, if
* we have a squashable list, insert a placeholder comment starting
* from the list's second value.
*/
if (!jstate->clocations[i].squashed)
{
/*
* This location corresponds to a constant not to be squashed.
* Print what comes before the constant ...
*/
len_to_wrt = off - last_off;
len_to_wrt -= last_tok_len;
n_quer_loc += sprintf(norm_query + n_quer_loc, "$%d%s",
num_constants_replaced + 1 + jstate->highest_extern_param_id,
jstate->clocations[i].squashed ? " /*, ... */" : "");
num_constants_replaced++;
Assert(len_to_wrt >= 0);
memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
n_quer_loc += len_to_wrt;
/* ... and then a param symbol replacing the constant itself */
n_quer_loc += sprintf(norm_query + n_quer_loc, "$%d",
i + 1 + jstate->highest_extern_param_id - skipped_constants);
/* In case previous constants were merged away, stop doing that */
in_squashed = false;
}
else if (!in_squashed)
{
/*
* This location is the start position of a run of constants to be
* squashed, so we need to print the representation of starting a
* group of stashed constants.
*
* Print what comes before the constant ...
*/
len_to_wrt = off - last_off;
len_to_wrt -= last_tok_len;
Assert(len_to_wrt >= 0);
Assert(i + 1 < jstate->clocations_count);
Assert(jstate->clocations[i + 1].squashed);
memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
n_quer_loc += len_to_wrt;
/* ... and then start a run of squashed constants */
n_quer_loc += sprintf(norm_query + n_quer_loc, "$%d /*, ... */",
i + 1 + jstate->highest_extern_param_id - skipped_constants);
/* The next location will match the block below, to end the run */
in_squashed = true;
skipped_constants++;
}
else
{
/*
* The second location of a run of squashable elements; this
* indicates its end.
*/
in_squashed = false;
}
/* Otherwise the constant is squashed away -- move forward */
/* move forward */
quer_loc = off + tok_len;
last_off = off;
last_tok_len = tok_len;
@ -3017,6 +2959,9 @@ fill_in_constant_lengths(JumbleState *jstate, const char *query,
Assert(loc >= 0);
if (locs[i].squashed)
continue; /* squashable list, ignore */
if (loc <= last_loc)
continue; /* Duplicate constant, ignore */

View File

@ -19,3 +19,19 @@ SELECT $1 \bind 'unnamed_val1' \g
\bind_named stmt1 'stmt1_val1' \g
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- Various parameter numbering patterns
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Unique query IDs with parameter numbers switched.
SELECT WHERE ($1::int, 7) IN ((8, $2::int), ($3::int, 9)) \bind '1' '2' '3' \g
SELECT WHERE ($2::int, 10) IN ((11, $3::int), ($1::int, 12)) \bind '1' '2' '3' \g
SELECT WHERE $1::int IN ($2::int, $3::int) \bind '1' '2' '3' \g
SELECT WHERE $2::int IN ($3::int, $1::int) \bind '1' '2' '3' \g
SELECT WHERE $3::int IN ($1::int, $2::int) \bind '1' '2' '3' \g
-- Two groups of two queries with the same query ID.
SELECT WHERE '1'::int IN ($1::int, '2'::int) \bind '1' \g
SELECT WHERE '4'::int IN ($1::int, '5'::int) \bind '2' \g
SELECT WHERE $2::int IN ($1::int, '1'::int) \bind '1' '2' \g
SELECT WHERE $2::int IN ($1::int, '2'::int) \bind '3' '4' \g
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";

View File

@ -334,6 +334,32 @@ END; $$;
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C", toplevel;
-- DO block --- multiple inner queries with separators
SET pg_stat_statements.track = 'all';
SET pg_stat_statements.track_utility = TRUE;
CREATE TABLE pgss_do_util_tab_1 (a int);
CREATE TABLE pgss_do_util_tab_2 (a int);
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
DO $$
DECLARE BEGIN
EXECUTE 'CREATE TABLE pgss_do_table (id INT); DROP TABLE pgss_do_table';
EXECUTE 'SELECT a FROM pgss_do_util_tab_1; SELECT a FROM pgss_do_util_tab_2';
END $$;
SELECT toplevel, calls, rows, query FROM pg_stat_statements
WHERE toplevel IS FALSE
ORDER BY query COLLATE "C";
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Note the extra semicolon at the end of the query.
DO $$
DECLARE BEGIN
EXECUTE 'CREATE TABLE pgss_do_table (id INT); DROP TABLE pgss_do_table;';
EXECUTE 'SELECT a FROM pgss_do_util_tab_1; SELECT a FROM pgss_do_util_tab_2;';
END $$;
SELECT toplevel, calls, rows, query FROM pg_stat_statements
WHERE toplevel IS FALSE
ORDER BY query COLLATE "C";
DROP TABLE pgss_do_util_tab_1, pgss_do_util_tab_2;
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;

View File

@ -20,11 +20,11 @@ SELECT 42;
SELECT 42;
SELECT 42;
SELECT plans, calls, rows, query FROM pg_stat_statements
WHERE query NOT LIKE 'SELECT COUNT%' ORDER BY query COLLATE "C";
WHERE query NOT LIKE 'PREPARE%' ORDER BY query COLLATE "C";
-- for the prepared statement we expect at least one replan, but cache
-- invalidations could force more
SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_stat_statements
WHERE query LIKE 'SELECT COUNT%' ORDER BY query COLLATE "C";
WHERE query LIKE 'PREPARE%' ORDER BY query COLLATE "C";
-- Cleanup
DROP TABLE stats_plan_test;

View File

@ -79,6 +79,22 @@ DEALLOCATE pgss_test;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- normalization of constants and parameters, with constant locations
-- recorded one or more times.
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT WHERE '1' IN ('1'::int, '3'::int::text);
SELECT WHERE (1, 2) IN ((1, 2), (2, 3));
SELECT WHERE (3, 4) IN ((5, 6), (8, 7));
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
-- with the last element being an explicit function call with an argument, ensure
-- the normalization of the squashing interval is correct.
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT WHERE 1 IN (1, int4(1), int4(2));
SELECT WHERE 1 = ANY (ARRAY[1, int4(1), int4(2)]);
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- queries with locking clauses
--

View File

@ -3,101 +3,160 @@
--
CREATE EXTENSION pg_stat_statements;
--
-- Simple Lists
--
CREATE TABLE test_squash (id int, data int);
-- IN queries
-- Normal scenario, too many simple constants for an IN query
-- single element will not be squashed
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT * FROM test_squash WHERE id IN (1);
SELECT * FROM test_squash WHERE id IN (1, 2, 3);
SELECT ARRAY[1];
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9);
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
-- more than 1 element in a list will be squashed
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT * FROM test_squash WHERE id IN (1, 2, 3);
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4);
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5);
SELECT ARRAY[1, 2, 3];
SELECT ARRAY[1, 2, 3, 4];
SELECT ARRAY[1, 2, 3, 4, 5];
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
-- built-in functions will be squashed
-- the IN and ARRAY forms of this statement will have the same queryId
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT WHERE 1 IN (1, int4(1), int4(2), 2);
SELECT WHERE 1 = ANY (ARRAY[1, int4(1), int4(2), 2]);
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
-- external parameters will not be squashed
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT * FROM test_squash WHERE id IN ($1, $2, $3, $4, $5) \bind 1 2 3 4 5
;
SELECT * FROM test_squash WHERE id::text = ANY(ARRAY[$1, $2, $3, $4, $5]) \bind 1 2 3 4 5
;
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
-- neither are prepared statements
-- the IN and ARRAY forms of this statement will have the same queryId
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
PREPARE p1(int, int, int, int, int) AS
SELECT * FROM test_squash WHERE id IN ($1, $2, $3, $4, $5);
EXECUTE p1(1, 2, 3, 4, 5);
DEALLOCATE p1;
PREPARE p1(int, int, int, int, int) AS
SELECT * FROM test_squash WHERE id = ANY(ARRAY[$1, $2, $3, $4, $5]);
EXECUTE p1(1, 2, 3, 4, 5);
DEALLOCATE p1;
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
-- More conditions in the query
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9) AND data = 2;
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) AND data = 2;
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) AND data = 2;
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9]) AND data = 2;
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) AND data = 2;
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) AND data = 2;
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
-- Multiple squashed intervals
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9)
AND data IN (1, 2, 3, 4, 5, 6, 7, 8, 9);
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
AND data IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
AND data IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9])
AND data = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9]);
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
AND data = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
AND data = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
-- No constants simplification for OpExpr
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- In the following two queries the operator expressions (+) and (@) have
-- different oppno, and will be given different query_id if squashed, even though
-- the normalized query will be the same
-- No constants squashing for OpExpr
-- The IN and ARRAY forms of this statement will have the same queryId
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT * FROM test_squash WHERE id IN
(1 + 1, 2 + 2, 3 + 3, 4 + 4, 5 + 5, 6 + 6, 7 + 7, 8 + 8, 9 + 9);
SELECT * FROM test_squash WHERE id IN
(@ '-1', @ '-2', @ '-3', @ '-4', @ '-5', @ '-6', @ '-7', @ '-8', @ '-9');
SELECT * FROM test_squash WHERE id = ANY(ARRAY
[1 + 1, 2 + 2, 3 + 3, 4 + 4, 5 + 5, 6 + 6, 7 + 7, 8 + 8, 9 + 9]);
SELECT * FROM test_squash WHERE id = ANY(ARRAY
[@ '-1', @ '-2', @ '-3', @ '-4', @ '-5', @ '-6', @ '-7', @ '-8', @ '-9']);
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- FuncExpr
--
-- Verify multiple type representation end up with the same query_id
CREATE TABLE test_float (data float);
-- The casted ARRAY expressions will have the same queryId as the IN clause
-- form of the query
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT data FROM test_float WHERE data IN (1, 2);
SELECT data FROM test_float WHERE data IN (1, '2');
SELECT data FROM test_float WHERE data IN ('1', 2);
SELECT data FROM test_float WHERE data IN ('1', '2');
SELECT data FROM test_float WHERE data IN (1.0, 1.0);
SELECT data FROM test_float WHERE data = ANY(ARRAY['1'::double precision, '2'::double precision]);
SELECT data FROM test_float WHERE data = ANY(ARRAY[1.0::double precision, 1.0::double precision]);
SELECT data FROM test_float WHERE data = ANY(ARRAY[1, 2]);
SELECT data FROM test_float WHERE data = ANY(ARRAY[1, '2']);
SELECT data FROM test_float WHERE data = ANY(ARRAY['1', 2]);
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
-- Numeric type, implicit cast is squashed
CREATE TABLE test_squash_numeric (id int, data numeric(5, 2));
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT * FROM test_squash_numeric WHERE data IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
SELECT * FROM test_squash_numeric WHERE data = ANY(ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
-- Bigint, implicit cast is squashed
CREATE TABLE test_squash_bigint (id int, data bigint);
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT * FROM test_squash_bigint WHERE data IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
SELECT * FROM test_squash_bigint WHERE data = ANY(ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
-- Bigint, explicit cast is not squashed
-- Bigint, explicit cast is squashed
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT * FROM test_squash_bigint WHERE data IN
(1::bigint, 2::bigint, 3::bigint, 4::bigint, 5::bigint, 6::bigint,
7::bigint, 8::bigint, 9::bigint, 10::bigint, 11::bigint);
SELECT * FROM test_squash_bigint WHERE data = ANY(ARRAY[
1::bigint, 2::bigint, 3::bigint, 4::bigint, 5::bigint, 6::bigint,
7::bigint, 8::bigint, 9::bigint, 10::bigint, 11::bigint]);
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
-- Bigint, long tokens with parenthesis
-- Bigint, long tokens with parenthesis, will not squash
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT * FROM test_squash_bigint WHERE id IN
(abs(100), abs(200), abs(300), abs(400), abs(500), abs(600), abs(700),
abs(800), abs(900), abs(1000), ((abs(1100))));
SELECT * FROM test_squash_bigint WHERE id = ANY(ARRAY[
abs(100), abs(200), abs(300), abs(400), abs(500), abs(600), abs(700),
abs(800), abs(900), abs(1000), ((abs(1100)))]);
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
-- CoerceViaIO, SubLink instead of a Const
CREATE TABLE test_squash_jsonb (id int, data jsonb);
-- Multiple FuncExpr's. Will not squash
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT * FROM test_squash_jsonb WHERE data IN
((SELECT '"1"')::jsonb, (SELECT '"2"')::jsonb, (SELECT '"3"')::jsonb,
(SELECT '"4"')::jsonb, (SELECT '"5"')::jsonb, (SELECT '"6"')::jsonb,
(SELECT '"7"')::jsonb, (SELECT '"8"')::jsonb, (SELECT '"9"')::jsonb,
(SELECT '"10"')::jsonb);
SELECT WHERE 1 IN (1::int::bigint::int, 2::int::bigint::int);
SELECT WHERE 1 = ANY(ARRAY[1::int::bigint::int, 2::int::bigint::int]);
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- CoerceViaIO
--
-- Create some dummy type to force CoerceViaIO
CREATE TYPE casttesttype;
@ -141,19 +200,73 @@ SELECT * FROM test_squash_cast WHERE data IN
4::int4::casttesttype, 5::int4::casttesttype, 6::int4::casttesttype,
7::int4::casttesttype, 8::int4::casttesttype, 9::int4::casttesttype,
10::int4::casttesttype, 11::int4::casttesttype);
SELECT * FROM test_squash_cast WHERE data = ANY (ARRAY
[1::int4::casttesttype, 2::int4::casttesttype, 3::int4::casttesttype,
4::int4::casttesttype, 5::int4::casttesttype, 6::int4::casttesttype,
7::int4::casttesttype, 8::int4::casttesttype, 9::int4::casttesttype,
10::int4::casttesttype, 11::int4::casttesttype]);
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
-- Some casting expression are simplified to Const
CREATE TABLE test_squash_jsonb (id int, data jsonb);
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT * FROM test_squash_jsonb WHERE data IN
(('"1"')::jsonb, ('"2"')::jsonb, ('"3"')::jsonb, ('"4"')::jsonb,
( '"5"')::jsonb, ( '"6"')::jsonb, ( '"7"')::jsonb, ( '"8"')::jsonb,
( '"9"')::jsonb, ( '"10"')::jsonb);
('"5"')::jsonb, ('"6"')::jsonb, ('"7"')::jsonb, ('"8"')::jsonb,
('"9"')::jsonb, ('"10"')::jsonb);
SELECT * FROM test_squash_jsonb WHERE data = ANY (ARRAY
[('"1"')::jsonb, ('"2"')::jsonb, ('"3"')::jsonb, ('"4"')::jsonb,
('"5"')::jsonb, ('"6"')::jsonb, ('"7"')::jsonb, ('"8"')::jsonb,
('"9"')::jsonb, ('"10"')::jsonb]);
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
-- RelabelType
-- CoerceViaIO, SubLink instead of a Const. Will not squash
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT * FROM test_squash WHERE id IN (1::oid, 2::oid, 3::oid, 4::oid, 5::oid, 6::oid, 7::oid, 8::oid, 9::oid);
SELECT * FROM test_squash_jsonb WHERE data IN
((SELECT '"1"')::jsonb, (SELECT '"2"')::jsonb, (SELECT '"3"')::jsonb,
(SELECT '"4"')::jsonb, (SELECT '"5"')::jsonb, (SELECT '"6"')::jsonb,
(SELECT '"7"')::jsonb, (SELECT '"8"')::jsonb, (SELECT '"9"')::jsonb,
(SELECT '"10"')::jsonb);
SELECT * FROM test_squash_jsonb WHERE data = ANY(ARRAY
[(SELECT '"1"')::jsonb, (SELECT '"2"')::jsonb, (SELECT '"3"')::jsonb,
(SELECT '"4"')::jsonb, (SELECT '"5"')::jsonb, (SELECT '"6"')::jsonb,
(SELECT '"7"')::jsonb, (SELECT '"8"')::jsonb, (SELECT '"9"')::jsonb,
(SELECT '"10"')::jsonb]);
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
-- Multiple CoerceViaIO wrapping a constant. Will not squash
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT WHERE 1 IN (1::text::int::text::int, 1::text::int::text::int);
SELECT WHERE 1 = ANY(ARRAY[1::text::int::text::int, 1::text::int::text::int]);
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- RelabelType
--
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- if there is only one level of RelabelType, the list will be squashable
SELECT * FROM test_squash WHERE id IN
(1::oid, 2::oid, 3::oid, 4::oid, 5::oid, 6::oid, 7::oid, 8::oid, 9::oid);
SELECT ARRAY[1::oid, 2::oid, 3::oid, 4::oid, 5::oid, 6::oid, 7::oid, 8::oid, 9::oid];
-- if there is at least one element with multiple levels of RelabelType,
-- the list will not be squashable
SELECT * FROM test_squash WHERE id IN (1::oid, 2::oid::int::oid);
SELECT * FROM test_squash WHERE id = ANY(ARRAY[1::oid, 2::oid::int::oid]);
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- edge cases
--
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- for nested arrays, only constants are squashed
SELECT ARRAY[
ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
];
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
-- Test constants evaluation in a CTE, which was causing issues in the past
@ -163,8 +276,26 @@ WITH cte AS (
SELECT ARRAY['a', 'b', 'c', const::varchar] AS result
FROM cte;
-- Simple array would be squashed as well
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
-- Rewritten as an OpExpr, so it will not be squashed
select where '1' IN ('1'::int, '2'::int::text);
-- Rewritten as an ArrayExpr, so it will be squashed
select where '1' IN ('1'::int, '2'::int);
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Both of these queries will be rewritten as an ArrayExpr, so they
-- will be squashed, and have a similar queryId
select where '1' IN ('1'::int::text, '2'::int::text);
select where '1' = ANY (array['1'::int::text, '2'::int::text]);
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- cleanup
--
DROP TABLE test_squash;
DROP TABLE test_float;
DROP TABLE test_squash_numeric;
DROP TABLE test_squash_bigint;
DROP TABLE test_squash_cast CASCADE;
DROP TABLE test_squash_jsonb;

View File

@ -577,7 +577,7 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
len = pg_b64_enc_len(sizeof(MyProcPort->scram_ClientKey));
/* don't forget the zero-terminator */
values[n] = palloc0(len + 1);
encoded_len = pg_b64_encode((const char *) MyProcPort->scram_ClientKey,
encoded_len = pg_b64_encode(MyProcPort->scram_ClientKey,
sizeof(MyProcPort->scram_ClientKey),
(char *) values[n], len);
if (encoded_len < 0)
@ -588,7 +588,7 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
len = pg_b64_enc_len(sizeof(MyProcPort->scram_ServerKey));
/* don't forget the zero-terminator */
values[n] = palloc0(len + 1);
encoded_len = pg_b64_encode((const char *) MyProcPort->scram_ServerKey,
encoded_len = pg_b64_encode(MyProcPort->scram_ServerKey,
sizeof(MyProcPort->scram_ServerKey),
(char *) values[n], len);
if (encoded_len < 0)

View File

@ -2565,6 +2565,70 @@ SELECT * FROM ft1, ft2, ft4, ft5, local_tbl WHERE ft1.c1 = ft2.c1 AND ft1.c2 = f
96 | 6 | 00096 | Tue Apr 07 00:00:00 1970 PST | Tue Apr 07 00:00:00 1970 | 6 | 6 | foo | 96 | 6 | 00096 | Tue Apr 07 00:00:00 1970 PST | Tue Apr 07 00:00:00 1970 | 6 | 6 | foo | 6 | 7 | AAA006 | 6 | 7 | AAA006 | 6 | 6 | 0006
(10 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM ft1, ft4, ft5, local_tbl WHERE ft1.c1 = ft4.c1 AND ft1.c1 = ft5.c1
AND ft1.c2 = local_tbl.c1 AND ft1.c1 < 100 AND ft5.c1 < 100 FOR UPDATE;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
LockRows
Output: ft1.c1, ft1.c2, ft1.c3, ft1.c4, ft1.c5, ft1.c6, ft1.c7, ft1.c8, ft4.c1, ft4.c2, ft4.c3, ft5.c1, ft5.c2, ft5.c3, local_tbl.c1, local_tbl.c2, local_tbl.c3, ft1.*, ft4.*, ft5.*, local_tbl.ctid
-> Merge Join
Output: ft1.c1, ft1.c2, ft1.c3, ft1.c4, ft1.c5, ft1.c6, ft1.c7, ft1.c8, ft4.c1, ft4.c2, ft4.c3, ft5.c1, ft5.c2, ft5.c3, local_tbl.c1, local_tbl.c2, local_tbl.c3, ft1.*, ft4.*, ft5.*, local_tbl.ctid
Merge Cond: (local_tbl.c1 = ft1.c2)
-> Index Scan using local_tbl_pkey on public.local_tbl
Output: local_tbl.c1, local_tbl.c2, local_tbl.c3, local_tbl.ctid
-> Sort
Output: ft1.c1, ft1.c2, ft1.c3, ft1.c4, ft1.c5, ft1.c6, ft1.c7, ft1.c8, ft1.*, ft4.c1, ft4.c2, ft4.c3, ft4.*, ft5.c1, ft5.c2, ft5.c3, ft5.*
Sort Key: ft1.c2
-> Foreign Scan
Output: ft1.c1, ft1.c2, ft1.c3, ft1.c4, ft1.c5, ft1.c6, ft1.c7, ft1.c8, ft1.*, ft4.c1, ft4.c2, ft4.c3, ft4.*, ft5.c1, ft5.c2, ft5.c3, ft5.*
Relations: ((public.ft1) INNER JOIN (public.ft4)) INNER JOIN (public.ft5)
Remote SQL: SELECT r1."C 1", r1.c2, r1.c3, r1.c4, r1.c5, r1.c6, r1.c7, r1.c8, CASE WHEN (r1.*)::text IS NOT NULL THEN ROW(r1."C 1", r1.c2, r1.c3, r1.c4, r1.c5, r1.c6, r1.c7, r1.c8) END, r2.c1, r2.c2, r2.c3, CASE WHEN (r2.*)::text IS NOT NULL THEN ROW(r2.c1, r2.c2, r2.c3) END, r3.c1, r3.c2, r3.c3, CASE WHEN (r3.*)::text IS NOT NULL THEN ROW(r3.c1, r3.c2, r3.c3) END FROM (("S 1"."T 1" r1 INNER JOIN "S 1"."T 3" r2 ON (((r1."C 1" = r2.c1)) AND ((r1."C 1" < 100)))) INNER JOIN "S 1"."T 4" r3 ON (((r1."C 1" = r3.c1)) AND ((r3.c1 < 100)))) FOR UPDATE OF r1 FOR UPDATE OF r2 FOR UPDATE OF r3
-> Merge Join
Output: ft1.c1, ft1.c2, ft1.c3, ft1.c4, ft1.c5, ft1.c6, ft1.c7, ft1.c8, ft1.*, ft4.c1, ft4.c2, ft4.c3, ft4.*, ft5.c1, ft5.c2, ft5.c3, ft5.*
Merge Cond: (ft1.c1 = ft5.c1)
-> Merge Join
Output: ft1.c1, ft1.c2, ft1.c3, ft1.c4, ft1.c5, ft1.c6, ft1.c7, ft1.c8, ft1.*, ft4.c1, ft4.c2, ft4.c3, ft4.*
Merge Cond: (ft1.c1 = ft4.c1)
-> Sort
Output: ft1.c1, ft1.c2, ft1.c3, ft1.c4, ft1.c5, ft1.c6, ft1.c7, ft1.c8, ft1.*
Sort Key: ft1.c1
-> Foreign Scan on public.ft1
Output: ft1.c1, ft1.c2, ft1.c3, ft1.c4, ft1.c5, ft1.c6, ft1.c7, ft1.c8, ft1.*
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" < 100)) FOR UPDATE
-> Sort
Output: ft4.c1, ft4.c2, ft4.c3, ft4.*
Sort Key: ft4.c1
-> Foreign Scan on public.ft4
Output: ft4.c1, ft4.c2, ft4.c3, ft4.*
Remote SQL: SELECT c1, c2, c3 FROM "S 1"."T 3" FOR UPDATE
-> Sort
Output: ft5.c1, ft5.c2, ft5.c3, ft5.*
Sort Key: ft5.c1
-> Foreign Scan on public.ft5
Output: ft5.c1, ft5.c2, ft5.c3, ft5.*
Remote SQL: SELECT c1, c2, c3 FROM "S 1"."T 4" WHERE ((c1 < 100)) FOR UPDATE
(38 rows)
SELECT * FROM ft1, ft4, ft5, local_tbl WHERE ft1.c1 = ft4.c1 AND ft1.c1 = ft5.c1
AND ft1.c2 = local_tbl.c1 AND ft1.c1 < 100 AND ft5.c1 < 100 FOR UPDATE;
c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8 | c1 | c2 | c3 | c1 | c2 | c3 | c1 | c2 | c3
----+----+-------+------------------------------+--------------------------+----+------------+-----+----+----+--------+----+----+--------+----+----+------
12 | 2 | 00012 | Tue Jan 13 00:00:00 1970 PST | Tue Jan 13 00:00:00 1970 | 2 | 2 | foo | 12 | 13 | AAA012 | 12 | 13 | AAA012 | 2 | 2 | 0002
42 | 2 | 00042 | Thu Feb 12 00:00:00 1970 PST | Thu Feb 12 00:00:00 1970 | 2 | 2 | foo | 42 | 43 | AAA042 | 42 | 43 | AAA042 | 2 | 2 | 0002
72 | 2 | 00072 | Sat Mar 14 00:00:00 1970 PST | Sat Mar 14 00:00:00 1970 | 2 | 2 | foo | 72 | 73 | AAA072 | 72 | 73 | | 2 | 2 | 0002
24 | 4 | 00024 | Sun Jan 25 00:00:00 1970 PST | Sun Jan 25 00:00:00 1970 | 4 | 4 | foo | 24 | 25 | AAA024 | 24 | 25 | AAA024 | 4 | 4 | 0004
54 | 4 | 00054 | Tue Feb 24 00:00:00 1970 PST | Tue Feb 24 00:00:00 1970 | 4 | 4 | foo | 54 | 55 | AAA054 | 54 | 55 | | 4 | 4 | 0004
84 | 4 | 00084 | Thu Mar 26 00:00:00 1970 PST | Thu Mar 26 00:00:00 1970 | 4 | 4 | foo | 84 | 85 | AAA084 | 84 | 85 | AAA084 | 4 | 4 | 0004
96 | 6 | 00096 | Tue Apr 07 00:00:00 1970 PST | Tue Apr 07 00:00:00 1970 | 6 | 6 | foo | 96 | 97 | AAA096 | 96 | 97 | AAA096 | 6 | 6 | 0006
36 | 6 | 00036 | Fri Feb 06 00:00:00 1970 PST | Fri Feb 06 00:00:00 1970 | 6 | 6 | foo | 36 | 37 | AAA036 | 36 | 37 | | 6 | 6 | 0006
66 | 6 | 00066 | Sun Mar 08 00:00:00 1970 PST | Sun Mar 08 00:00:00 1970 | 6 | 6 | foo | 66 | 67 | AAA066 | 66 | 67 | AAA066 | 6 | 6 | 0006
6 | 6 | 00006 | Wed Jan 07 00:00:00 1970 PST | Wed Jan 07 00:00:00 1970 | 6 | 6 | foo | 6 | 7 | AAA006 | 6 | 7 | AAA006 | 6 | 6 | 0006
48 | 8 | 00048 | Wed Feb 18 00:00:00 1970 PST | Wed Feb 18 00:00:00 1970 | 8 | 8 | foo | 48 | 49 | AAA048 | 48 | 49 | AAA048 | 8 | 8 | 0008
18 | 8 | 00018 | Mon Jan 19 00:00:00 1970 PST | Mon Jan 19 00:00:00 1970 | 8 | 8 | foo | 18 | 19 | AAA018 | 18 | 19 | | 8 | 8 | 0008
78 | 8 | 00078 | Fri Mar 20 00:00:00 1970 PST | Fri Mar 20 00:00:00 1970 | 8 | 8 | foo | 78 | 79 | AAA078 | 78 | 79 | AAA078 | 8 | 8 | 0008
(13 rows)
RESET enable_nestloop;
RESET enable_hashjoin;
-- test that add_paths_with_pathkeys_for_rel() arranges for the epq_path to

View File

@ -240,6 +240,7 @@ typedef struct PgFdwDirectModifyState
PGresult *result; /* result for query */
int num_tuples; /* # of result tuples */
int next_tuple; /* index of next one to return */
MemoryContextCallback result_cb; /* ensures result will get freed */
Relation resultRel; /* relcache entry for the target relation */
AttrNumber *attnoMap; /* array of attnums of input user columns */
AttrNumber ctidAttno; /* attnum of input ctid column */
@ -2670,6 +2671,17 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags)
dmstate = (PgFdwDirectModifyState *) palloc0(sizeof(PgFdwDirectModifyState));
node->fdw_state = dmstate;
/*
* We use a memory context callback to ensure that the dmstate's PGresult
* (if any) will be released, even if the query fails somewhere that's
* outside our control. The callback is always armed for the duration of
* the query; this relies on PQclear(NULL) being a no-op.
*/
dmstate->result_cb.func = (MemoryContextCallbackFunction) PQclear;
dmstate->result_cb.arg = NULL;
MemoryContextRegisterResetCallback(CurrentMemoryContext,
&dmstate->result_cb);
/*
* Identify which user to do the remote access as. This should match what
* ExecCheckPermissions() does.
@ -2817,7 +2829,13 @@ postgresEndDirectModify(ForeignScanState *node)
return;
/* Release PGresult */
PQclear(dmstate->result);
if (dmstate->result)
{
PQclear(dmstate->result);
dmstate->result = NULL;
/* ... and don't forget to disable the callback */
dmstate->result_cb.arg = NULL;
}
/* Release remote connection */
ReleaseConnection(dmstate->conn);
@ -4591,13 +4609,17 @@ execute_dml_stmt(ForeignScanState *node)
/*
* Get the result, and check for success.
*
* We don't use a PG_TRY block here, so be careful not to throw error
* without releasing the PGresult.
* We use a memory context callback to ensure that the PGresult will be
* released, even if the query fails somewhere that's outside our control.
* The callback is already registered, just need to fill in its arg.
*/
Assert(dmstate->result == NULL);
dmstate->result = pgfdw_get_result(dmstate->conn);
dmstate->result_cb.arg = dmstate->result;
if (PQresultStatus(dmstate->result) !=
(dmstate->has_returning ? PGRES_TUPLES_OK : PGRES_COMMAND_OK))
pgfdw_report_error(ERROR, dmstate->result, dmstate->conn, true,
pgfdw_report_error(ERROR, dmstate->result, dmstate->conn, false,
dmstate->query);
/* Get the number of rows affected. */
@ -4641,30 +4663,16 @@ get_returning_data(ForeignScanState *node)
}
else
{
/*
* On error, be sure to release the PGresult on the way out. Callers
* do not have PG_TRY blocks to ensure this happens.
*/
PG_TRY();
{
HeapTuple newtup;
newtup = make_tuple_from_result_row(dmstate->result,
dmstate->next_tuple,
dmstate->rel,
dmstate->attinmeta,
dmstate->retrieved_attrs,
node,
dmstate->temp_cxt);
ExecStoreHeapTuple(newtup, slot, false);
}
PG_CATCH();
{
PQclear(dmstate->result);
PG_RE_THROW();
}
PG_END_TRY();
HeapTuple newtup;
newtup = make_tuple_from_result_row(dmstate->result,
dmstate->next_tuple,
dmstate->rel,
dmstate->attinmeta,
dmstate->retrieved_attrs,
node,
dmstate->temp_cxt);
ExecStoreHeapTuple(newtup, slot, false);
/* Get the updated/deleted tuple. */
if (dmstate->rel)
resultSlot = slot;

View File

@ -715,6 +715,11 @@ SELECT * FROM ft1, ft2, ft4, ft5, local_tbl WHERE ft1.c1 = ft2.c1 AND ft1.c2 = f
AND ft1.c2 = ft5.c1 AND ft1.c2 = local_tbl.c1 AND ft1.c1 < 100 AND ft2.c1 < 100 FOR UPDATE;
SELECT * FROM ft1, ft2, ft4, ft5, local_tbl WHERE ft1.c1 = ft2.c1 AND ft1.c2 = ft4.c1
AND ft1.c2 = ft5.c1 AND ft1.c2 = local_tbl.c1 AND ft1.c1 < 100 AND ft2.c1 < 100 FOR UPDATE;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM ft1, ft4, ft5, local_tbl WHERE ft1.c1 = ft4.c1 AND ft1.c1 = ft5.c1
AND ft1.c2 = local_tbl.c1 AND ft1.c1 < 100 AND ft5.c1 < 100 FOR UPDATE;
SELECT * FROM ft1, ft4, ft5, local_tbl WHERE ft1.c1 = ft4.c1 AND ft1.c1 = ft5.c1
AND ft1.c2 = local_tbl.c1 AND ft1.c1 < 100 AND ft5.c1 < 100 FOR UPDATE;
RESET enable_nestloop;
RESET enable_hashjoin;

View File

@ -1,5 +1,5 @@
# Copyright (c) 2024, PostgreSQL Global Development Group
# Copyright (c) 2024-2025, PostgreSQL Global Development Group
use strict;
use warnings FATAL => 'all';

View File

@ -1582,7 +1582,7 @@
<structfield>rolpassword</structfield> <type>text</type>
</para>
<para>
Password (possibly encrypted); null if none. The format depends
Encrypted password; null if none. The format depends
on the form of encryption used.
</para></entry>
</row>
@ -1627,11 +1627,6 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
<replaceable>ServerKey</replaceable> are in Base64 encoded format. This format is
the same as that specified by <ulink url="https://datatracker.ietf.org/doc/html/rfc5803">RFC 5803</ulink>.
</para>
<para>
A password that does not follow either of those formats is assumed to be
unencrypted.
</para>
</sect1>
@ -2629,7 +2624,6 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
</para>
<para>
Has the constraint been validated?
Currently, can be false only for foreign keys and CHECK constraints
</para></entry>
</row>

View File

@ -140,7 +140,7 @@
An example of what this file might look like is:
<programlisting>
# This is a comment
log_connections = yes
log_connections = all
log_destination = 'syslog'
search_path = '"$user", public'
shared_buffers = 128MB
@ -337,7 +337,7 @@ UPDATE pg_settings SET setting = reset_val WHERE name = 'configuration_parameter
<option>-c name=value</option> command-line parameter, or its equivalent
<option>--name=value</option> variation. For example,
<programlisting>
postgres -c log_connections=yes --log-destination='syslog'
postgres -c log_connections=all --log-destination='syslog'
</programlisting>
Settings provided in this way override those set via
<filename>postgresql.conf</filename> or <command>ALTER SYSTEM</command>,
@ -1155,6 +1155,22 @@ include_dir 'conf.d'
</listitem>
</varlistentry>
<varlistentry id="guc-md5-password-warnings" xreflabel="md5_password_warnings">
<term><varname>md5_password_warnings</varname> (<type>boolean</type>)
<indexterm>
<primary><varname>md5_password_warnings</varname> configuration parameter</primary>
</indexterm>
</term>
<listitem>
<para>
Controls whether a <literal>WARNING</literal> about MD5 password
deprecation is produced when a <command>CREATE ROLE</command> or
<command>ALTER ROLE</command> statement sets an MD5-encrypted password.
The default value is <literal>on</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry id="guc-krb-server-keyfile" xreflabel="krb_server_keyfile">
<term><varname>krb_server_keyfile</varname> (<type>string</type>)
<indexterm>
@ -7511,12 +7527,12 @@ local0.* /var/log/postgresql
<entry><literal>setup_durations</literal></entry>
<entry>
Logs the time spent establishing the connection and setting up the
backend at the time the connection is ready to execute its first
query. The log message includes the total setup duration, starting
from the postmaster accepting the incoming connection and ending
when the connection is ready for query. It also includes the time
it took to fork the new backend and the time it took to
authenticate the user.
backend until the connection is ready to execute its first
query. The log message includes three durations: the total
setup duration (starting from the postmaster accepting the
incoming connection and ending when the connection is ready
for query), the time it took to fork the new backend, and
the time it took to authenticate the user.
</entry>
</row>
@ -7907,10 +7923,10 @@ log_line_prefix = '%m [%p] %q%u@%d/%a '
</listitem>
</varlistentry>
<varlistentry id="guc-log-lock-failure" xreflabel="log_lock_failure">
<term><varname>log_lock_failure</varname> (<type>boolean</type>)
<varlistentry id="guc-log-lock-failures" xreflabel="log_lock_failures">
<term><varname>log_lock_failures</varname> (<type>boolean</type>)
<indexterm>
<primary><varname>log_lock_failure</varname> configuration parameter</primary>
<primary><varname>log_lock_failures</varname> configuration parameter</primary>
</indexterm>
</term>
<listitem>
@ -8118,22 +8134,6 @@ log_line_prefix = '%m [%p] %q%u@%d/%a '
</listitem>
</varlistentry>
<varlistentry id="guc-md5-password-warnings" xreflabel="md5_password_warnings">
<term><varname>md5_password_warnings</varname> (<type>boolean</type>)
<indexterm>
<primary><varname>md5_password_warnings</varname> configuration parameter</primary>
</indexterm>
</term>
<listitem>
<para>
Controls whether a <literal>WARNING</literal> about MD5 password
deprecation is produced when a <command>CREATE ROLE</command> or
<command>ALTER ROLE</command> statement sets an MD5-encrypted password.
The default value is <literal>on</literal>.
</para>
</listitem>
</varlistentry>
</variablelist>
</sect2>
<sect2 id="runtime-config-logging-csvlog">

View File

@ -2223,8 +2223,9 @@ REVOKE ALL ON accounts FROM PUBLIC;
<para>
Allows <command>VACUUM</command>, <command>ANALYZE</command>,
<command>CLUSTER</command>, <command>REFRESH MATERIALIZED VIEW</command>,
<command>REINDEX</command>, and <command>LOCK TABLE</command> on a
relation.
<command>REINDEX</command>, <command>LOCK TABLE</command>,
and database object statistics manipulation functions
(see <xref linkend="functions-admin-statsmod"/>) on a relation.
</para>
</listitem>
</varlistentry>

View File

@ -10905,7 +10905,7 @@ SELECT date_bin('15 minutes', TIMESTAMP '2020-02-11 15:44:17', TIMESTAMP '2001-0
</sect2>
<sect2 id="functions-datetime-zoneconvert">
<title><literal>AT TIME ZONE and AT LOCAL</literal></title>
<title><literal>AT TIME ZONE</literal> and <literal>AT LOCAL</literal></title>
<indexterm>
<primary>time zone</primary>
@ -28663,143 +28663,6 @@ acl | {postgres=arwdDxtm/postgres,foo=r/postgres}
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>
<primary>pg_get_process_memory_contexts</primary>
</indexterm>
<function>pg_get_process_memory_contexts</function> ( <parameter>pid</parameter> <type>integer</type>, <parameter>summary</parameter> <type>boolean</type>, <parameter>timeout</parameter> <type>float</type> )
<returnvalue>setof record</returnvalue>
( <parameter>name</parameter> <type>text</type>,
<parameter>ident</parameter> <type>text</type>,
<parameter>type</parameter> <type>text</type>,
<parameter>path</parameter> <type>integer[]</type>,
<parameter>level</parameter> <type>integer</type>,
<parameter>total_bytes</parameter> <type>bigint</type>,
<parameter>total_nblocks</parameter> <type>bigint</type>,
<parameter>free_bytes</parameter> <type>bigint</type>,
<parameter>free_chunks</parameter> <type>bigint</type>,
<parameter>used_bytes</parameter> <type>bigint</type>,
<parameter>num_agg_contexts</parameter> <type>integer</type>,
<parameter>stats_timestamp</parameter> <type>timestamptz</type> )
</para>
<para>
This function handles requests to display the memory contexts of a
<productname>PostgreSQL</productname> process with the specified
process ID. The function can be used to send requests to backends as
well as <glossterm linkend="glossary-auxiliary-proc">auxiliary processes</glossterm>.
</para>
<para>
The returned record contains extended statistics per each memory
context:
<itemizedlist spacing="compact">
<listitem>
<para>
<parameter>name</parameter> - The name of the memory context.
</para>
</listitem>
<listitem>
<para>
<parameter>ident</parameter> - Memory context ID (if any).
</para>
</listitem>
<listitem>
<para>
<parameter>type</parameter> - The type of memory context, possible
values are: AllocSet, Generation, Slab and Bump.
</para>
</listitem>
<listitem>
<para>
<parameter>path</parameter> - Memory contexts are organized in a
tree model with TopMemoryContext as the root, and all other memory
contexts as nodes in the tree. The <parameter>path</parameter>
displays the path from the root to the current memory context. The
path is limited to 100 children per node, which each node limited
to a max depth of 100, to preserve memory during reporting. The
printed path will also be limited to 100 nodes counting from the
TopMemoryContext.
</para>
</listitem>
<listitem>
<para>
<parameter>level</parameter> - The level in the tree of the current
memory context.
</para>
</listitem>
<listitem>
<para>
<parameter>total_bytes</parameter> - The total number of bytes
allocated to this memory context.
</para>
</listitem>
<listitem>
<para>
<parameter>total_nblocks</parameter> - The total number of blocks
used for the allocated memory.
</para>
</listitem>
<listitem>
<para>
<parameter>free_bytes</parameter> - The amount of free memory in
this memory context.
</para>
</listitem>
<listitem>
<para>
<parameter>free_chunks</parameter> - The number of chunks that
<parameter>free_bytes</parameter> corresponds to.
</para>
</listitem>
<listitem>
<para>
<parameter>used_bytes</parameter> - The total number of bytes
currently occupied.
</para>
</listitem>
<listitem>
<para>
<parameter>num_agg_contexts</parameter> - The number of memory
contexts aggregated in the displayed statistics.
</para>
</listitem>
<listitem>
<para>
<parameter>stats_timestamp</parameter> - When the statistics were
extracted from the process.
</para>
</listitem>
</itemizedlist>
</para>
<para>
When <parameter>summary</parameter> is <literal>true</literal>, statistics
for memory contexts at levels 1 and 2 are displayed, with level 1
representing the root node (i.e., <literal>TopMemoryContext</literal>).
Statistics for contexts on level 2 and below are aggregates of all
child contexts' statistics, where <literal>num_agg_contexts</literal>
indicate the number aggregated child contexts. When
<parameter>summary</parameter> is <literal>false</literal>,
<literal>the num_agg_contexts</literal> value is <literal>1</literal>,
indicating that individual statistics are being displayed.
</para>
<para>
Busy processes can delay reporting memory context statistics,
<parameter>timeout</parameter> specifies the number of seconds
to wait for updated statistics. <parameter>timeout</parameter> can be
specified in fractions of a second.
</para>
<para>
After receiving memory context statistics from the target process, it
returns the results as one row per context. If all the contexts don't
fit within the pre-determined size limit, the remaining context
statistics are aggregated and a cumulative total is displayed. The
<literal>num_agg_contexts</literal> column indicates the number of
contexts aggregated in the displayed statistics. When
<literal>num_agg_contexts</literal> is <literal>1</literal> it means
that the context statistics are displayed separately.
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>
@ -28939,40 +28802,6 @@ LOG: Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
because it may generate a large number of log messages.
</para>
<para>
<function>pg_get_process_memory_contexts</function> can be used to request
memory contexts statistics of any <productname>PostgreSQL</productname>
process. For example:
<programlisting>
postgres=# SELECT * FROM pg_get_process_memory_contexts(
(SELECT pid FROM pg_stat_activity
WHERE backend_type = 'checkpointer'),
false, 0.5) LIMIT 1;
-[ RECORD 1 ]----+------------------------------
name | TopMemoryContext
ident |
type | AllocSet
path | {1}
level | 1
total_bytes | 90304
total_nblocks | 3
free_bytes | 2880
free_chunks | 1
used_bytes | 87424
num_agg_contexts | 1
stats_timestamp | 2025-03-24 13:55:47.796698+01
</programlisting>
<note>
<para>
While <function>pg_get_process_memory_contexts</function> can be used to
query memory contexts of the local backend,
<structname>pg_backend_memory_contexts</structname>
(see <xref linkend="view-pg-backend-memory-contexts"/> for more details)
will be less resource intensive when only the local backend is of interest.
</para>
</note>
</para>
</sect2>
<sect2 id="functions-admin-backup">
@ -29940,6 +29769,7 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
<para>
Creates a replication origin with the given external
name, and returns the internal ID assigned to it.
The name must be no longer than 512 bytes.
</para></entry>
</row>
@ -30639,7 +30469,7 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
arguments are passed as pairs of <replaceable>argname</replaceable>
and <replaceable>argvalue</replaceable> in the form:
<programlisting>
SELECT pg_restore_relation_stats(
SELECT pg_restore_relation_stats(
'<replaceable>arg1name</replaceable>', '<replaceable>arg1value</replaceable>'::<replaceable>arg1type</replaceable>,
'<replaceable>arg2name</replaceable>', '<replaceable>arg2value</replaceable>'::<replaceable>arg2type</replaceable>,
'<replaceable>arg3name</replaceable>', '<replaceable>arg3value</replaceable>'::<replaceable>arg3type</replaceable>);
@ -30650,7 +30480,7 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
<structfield>reltuples</structfield> values for the table
<structname>mytable</structname>:
<programlisting>
SELECT pg_restore_relation_stats(
SELECT pg_restore_relation_stats(
'schemaname', 'myschema',
'relname', 'mytable',
'relpages', 173::integer,
@ -30732,7 +30562,7 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
arguments are passed as pairs of <replaceable>argname</replaceable>
and <replaceable>argvalue</replaceable> in the form:
<programlisting>
SELECT pg_restore_attribute_stats(
SELECT pg_restore_attribute_stats(
'<replaceable>arg1name</replaceable>', '<replaceable>arg1value</replaceable>'::<replaceable>arg1type</replaceable>,
'<replaceable>arg2name</replaceable>', '<replaceable>arg2value</replaceable>'::<replaceable>arg2type</replaceable>,
'<replaceable>arg3name</replaceable>', '<replaceable>arg3value</replaceable>'::<replaceable>arg3type</replaceable>);
@ -30744,7 +30574,7 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
<structfield>col1</structfield> of the table
<structname>mytable</structname>:
<programlisting>
SELECT pg_restore_attribute_stats(
SELECT pg_restore_attribute_stats(
'schemaname', 'myschema',
'relname', 'mytable',
'attname', 'col1',

View File

@ -1170,7 +1170,7 @@ my_sortsupport(PG_FUNCTION_ARGS)
</varlistentry>
<varlistentry>
<term><function>stratnum</function></term>
<term><function>translate_cmptype</function></term>
<listitem>
<para>
Given a <literal>CompareType</literal> value from
@ -1188,12 +1188,23 @@ my_sortsupport(PG_FUNCTION_ARGS)
non-<literal>WITHOUT OVERLAPS</literal> part(s) of an index constraint.
</para>
<para>
This support function corresponds to the index access method callback
function <structfield>amtranslatecmptype</structfield> (see <xref
linkend="index-functions"/>). The
<structfield>amtranslatecmptype</structfield> callback function for
GiST indexes merely calls down to the
<function>translate_cmptype</function> support function of the
respective operator family, since the GiST index access method has no
fixed strategy numbers itself.
</para>
<para>
The <acronym>SQL</acronym> declaration of the function must look like
this:
<programlisting>
CREATE OR REPLACE FUNCTION my_stratnum(integer)
CREATE OR REPLACE FUNCTION my_translate_cmptype(integer)
RETURNS smallint
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
@ -1202,7 +1213,7 @@ LANGUAGE C STRICT;
And the operator family registration must look like this:
<programlisting>
ALTER OPERATOR FAMILY my_opfamily USING gist ADD
FUNCTION 12 ("any", "any") my_stratnum(int);
FUNCTION 12 ("any", "any") my_translate_cmptype(int);
</programlisting>
</para>
@ -1210,10 +1221,10 @@ ALTER OPERATOR FAMILY my_opfamily USING gist ADD
The matching code in the C module could then follow this skeleton:
<programlisting>
PG_FUNCTION_INFO_V1(my_stratnum);
PG_FUNCTION_INFO_V1(my_translate_cmptype);
Datum
my_stratnum(PG_FUNCTION_ARGS)
my_translate_cmptype(PG_FUNCTION_ARGS)
{
CompareType cmptype = PG_GETARG_INT32(0);
StrategyNumber ret = InvalidStrategy;
@ -1232,11 +1243,11 @@ my_stratnum(PG_FUNCTION_ARGS)
<para>
One translation function is provided by
<productname>PostgreSQL</productname>:
<literal>gist_stratnum_common</literal> is for operator classes that
<literal>gist_translate_cmptype_common</literal> is for operator classes that
use the <literal>RT*StrategyNumber</literal> constants.
The <literal>btree_gist</literal>
extension defines a second translation function,
<literal>gist_stratnum_btree</literal>, for operator classes that use
<literal>gist_translate_cmptype_btree</literal>, for operator classes that use
the <literal>BT*StrategyNumber</literal> constants.
</para>
</listitem>

View File

@ -11,13 +11,15 @@
<title>Legal Notice</title>
<para>
<productname>PostgreSQL</productname> is Copyright &copy; 1996&ndash;2025
by the PostgreSQL Global Development Group.
<productname>PostgreSQL</productname> Database Management System
(also known as Postgres, formerly known as Postgres95)
</para>
<para>
<productname>Postgres95</productname> is Copyright &copy; 1994&ndash;5
by the Regents of the University of California.
Portions Copyright &copy; 1996-2025, PostgreSQL Global Development Group
</para>
<para>
Portions Copyright &copy; 1994, The Regents of the University of California
</para>
<para>

View File

@ -363,34 +363,25 @@
<para>
Create some test tables on the publisher.
<programlisting>
test_pub=# CREATE TABLE t1(a int, b text, PRIMARY KEY(a));
CREATE TABLE
test_pub=# CREATE TABLE t2(c int, d text, PRIMARY KEY(c));
CREATE TABLE
test_pub=# CREATE TABLE t3(e int, f text, PRIMARY KEY(e));
CREATE TABLE
/* pub # */ CREATE TABLE t1(a int, b text, PRIMARY KEY(a));
/* pub # */ CREATE TABLE t2(c int, d text, PRIMARY KEY(c));
/* pub # */ CREATE TABLE t3(e int, f text, PRIMARY KEY(e));
</programlisting></para>
<para>
Create the same tables on the subscriber.
<programlisting>
test_sub=# CREATE TABLE t1(a int, b text, PRIMARY KEY(a));
CREATE TABLE
test_sub=# CREATE TABLE t2(c int, d text, PRIMARY KEY(c));
CREATE TABLE
test_sub=# CREATE TABLE t3(e int, f text, PRIMARY KEY(e));
CREATE TABLE
/* sub # */ CREATE TABLE t1(a int, b text, PRIMARY KEY(a));
/* sub # */ CREATE TABLE t2(c int, d text, PRIMARY KEY(c));
/* sub # */ CREATE TABLE t3(e int, f text, PRIMARY KEY(e));
</programlisting></para>
<para>
Insert data to the tables at the publisher side.
<programlisting>
test_pub=# INSERT INTO t1 VALUES (1, 'one'), (2, 'two'), (3, 'three');
INSERT 0 3
test_pub=# INSERT INTO t2 VALUES (1, 'A'), (2, 'B'), (3, 'C');
INSERT 0 3
test_pub=# INSERT INTO t3 VALUES (1, 'i'), (2, 'ii'), (3, 'iii');
INSERT 0 3
/* pub # */ INSERT INTO t1 VALUES (1, 'one'), (2, 'two'), (3, 'three');
/* pub # */ INSERT INTO t2 VALUES (1, 'A'), (2, 'B'), (3, 'C');
/* pub # */ INSERT INTO t3 VALUES (1, 'i'), (2, 'ii'), (3, 'iii');
</programlisting></para>
<para>
@ -399,41 +390,34 @@ INSERT 0 3
<link linkend="sql-createpublication-params-with-publish"><literal>publish</literal></link>
operations. The publication <literal>pub3b</literal> has a row filter (see
<xref linkend="logical-replication-row-filter"/>).
<programlisting>
test_pub=# CREATE PUBLICATION pub1 FOR TABLE t1;
CREATE PUBLICATION
test_pub=# CREATE PUBLICATION pub2 FOR TABLE t2 WITH (publish = 'truncate');
CREATE PUBLICATION
test_pub=# CREATE PUBLICATION pub3a FOR TABLE t3 WITH (publish = 'truncate');
CREATE PUBLICATION
test_pub=# CREATE PUBLICATION pub3b FOR TABLE t3 WHERE (e > 5);
CREATE PUBLICATION
</programlisting></para>
<programlisting><![CDATA[
/* pub # */ CREATE PUBLICATION pub1 FOR TABLE t1;
/* pub # */ CREATE PUBLICATION pub2 FOR TABLE t2 WITH (publish = 'truncate');
/* pub # */ CREATE PUBLICATION pub3a FOR TABLE t3 WITH (publish = 'truncate');
/* pub # */ CREATE PUBLICATION pub3b FOR TABLE t3 WHERE (e > 5);
]]></programlisting></para>
<para>
Create subscriptions for the publications. The subscription
<literal>sub3</literal> subscribes to both <literal>pub3a</literal> and
<literal>pub3b</literal>. All subscriptions will copy initial data by default.
<programlisting>
test_sub=# CREATE SUBSCRIPTION sub1
test_sub-# CONNECTION 'host=localhost dbname=test_pub application_name=sub1'
test_sub-# PUBLICATION pub1;
CREATE SUBSCRIPTION
test_sub=# CREATE SUBSCRIPTION sub2
test_sub-# CONNECTION 'host=localhost dbname=test_pub application_name=sub2'
test_sub-# PUBLICATION pub2;
CREATE SUBSCRIPTION
test_sub=# CREATE SUBSCRIPTION sub3
test_sub-# CONNECTION 'host=localhost dbname=test_pub application_name=sub3'
test_sub-# PUBLICATION pub3a, pub3b;
CREATE SUBSCRIPTION
/* sub # */ CREATE SUBSCRIPTION sub1
/* sub - */ CONNECTION 'host=localhost dbname=test_pub application_name=sub1'
/* sub - */ PUBLICATION pub1;
/* sub # */ CREATE SUBSCRIPTION sub2
/* sub - */ CONNECTION 'host=localhost dbname=test_pub application_name=sub2'
/* sub - */ PUBLICATION pub2;
/* sub # */ CREATE SUBSCRIPTION sub3
/* sub - */ CONNECTION 'host=localhost dbname=test_pub application_name=sub3'
/* sub - */ PUBLICATION pub3a, pub3b;
</programlisting></para>
<para>
Observe that initial table data is copied, regardless of the
<literal>publish</literal> operation of the publication.
<programlisting>
test_sub=# SELECT * FROM t1;
/* sub # */ SELECT * FROM t1;
a | b
---+-------
1 | one
@ -441,7 +425,7 @@ test_sub=# SELECT * FROM t1;
3 | three
(3 rows)
test_sub=# SELECT * FROM t2;
/* sub # */ SELECT * FROM t2;
c | d
---+---
1 | A
@ -456,7 +440,7 @@ test_sub=# SELECT * FROM t2;
it means the copied table <literal>t3</literal> contains all rows even when
they do not match the row filter of publication <literal>pub3b</literal>.
<programlisting>
test_sub=# SELECT * FROM t3;
/* sub # */ SELECT * FROM t3;
e | f
---+-----
1 | i
@ -468,18 +452,15 @@ test_sub=# SELECT * FROM t3;
<para>
Insert more data to the tables at the publisher side.
<programlisting>
test_pub=# INSERT INTO t1 VALUES (4, 'four'), (5, 'five'), (6, 'six');
INSERT 0 3
test_pub=# INSERT INTO t2 VALUES (4, 'D'), (5, 'E'), (6, 'F');
INSERT 0 3
test_pub=# INSERT INTO t3 VALUES (4, 'iv'), (5, 'v'), (6, 'vi');
INSERT 0 3
/* pub # */ INSERT INTO t1 VALUES (4, 'four'), (5, 'five'), (6, 'six');
/* pub # */ INSERT INTO t2 VALUES (4, 'D'), (5, 'E'), (6, 'F');
/* pub # */ INSERT INTO t3 VALUES (4, 'iv'), (5, 'v'), (6, 'vi');
</programlisting></para>
<para>
Now the publisher side data looks like:
<programlisting>
test_pub=# SELECT * FROM t1;
/* pub # */ SELECT * FROM t1;
a | b
---+-------
1 | one
@ -490,7 +471,7 @@ test_pub=# SELECT * FROM t1;
6 | six
(6 rows)
test_pub=# SELECT * FROM t2;
/* pub # */ SELECT * FROM t2;
c | d
---+---
1 | A
@ -501,7 +482,7 @@ test_pub=# SELECT * FROM t2;
6 | F
(6 rows)
test_pub=# SELECT * FROM t3;
/* pub # */ SELECT * FROM t3;
e | f
---+-----
1 | i
@ -521,7 +502,7 @@ test_pub=# SELECT * FROM t3;
only replicate data that matches the row filter of <literal>pub3b</literal>.
Now the subscriber side data looks like:
<programlisting>
test_sub=# SELECT * FROM t1;
/* sub # */ SELECT * FROM t1;
a | b
---+-------
1 | one
@ -532,7 +513,7 @@ test_sub=# SELECT * FROM t1;
6 | six
(6 rows)
test_sub=# SELECT * FROM t2;
/* sub # */ SELECT * FROM t2;
c | d
---+---
1 | A
@ -540,7 +521,7 @@ test_sub=# SELECT * FROM t2;
3 | C
(3 rows)
test_sub=# SELECT * FROM t3;
/* sub # */ SELECT * FROM t3;
e | f
---+-----
1 | i
@ -567,8 +548,7 @@ test_sub=# SELECT * FROM t3;
<para>
First, create a publication for the examples to use.
<programlisting>
test_pub=# CREATE PUBLICATION pub1 FOR ALL TABLES;
CREATE PUBLICATION
/* pub # */ CREATE PUBLICATION pub1 FOR ALL TABLES;
</programlisting></para>
<para>
Example 1: Where the subscription says <literal>connect = false</literal>
@ -579,13 +559,12 @@ CREATE PUBLICATION
<para>
Create the subscription.
<programlisting>
test_sub=# CREATE SUBSCRIPTION sub1
test_sub-# CONNECTION 'host=localhost dbname=test_pub'
test_sub-# PUBLICATION pub1
test_sub-# WITH (connect=false);
/* sub # */ CREATE SUBSCRIPTION sub1
/* sub - */ CONNECTION 'host=localhost dbname=test_pub'
/* sub - */ PUBLICATION pub1
/* sub - */ WITH (connect=false);
WARNING: subscription was created, but is not connected
HINT: To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription.
CREATE SUBSCRIPTION
</programlisting></para>
</listitem>
<listitem>
@ -594,7 +573,7 @@ CREATE SUBSCRIPTION
specified during <literal>CREATE SUBSCRIPTION</literal>, the name of the
slot to create is same as the subscription name, e.g. "sub1".
<programlisting>
test_pub=# SELECT * FROM pg_create_logical_replication_slot('sub1', 'pgoutput');
/* pub # */ SELECT * FROM pg_create_logical_replication_slot('sub1', 'pgoutput');
slot_name | lsn
-----------+-----------
sub1 | 0/19404D0
@ -606,10 +585,8 @@ test_pub=# SELECT * FROM pg_create_logical_replication_slot('sub1', 'pgoutput');
On the subscriber, complete the activation of the subscription. After
this the tables of <literal>pub1</literal> will start replicating.
<programlisting>
test_sub=# ALTER SUBSCRIPTION sub1 ENABLE;
ALTER SUBSCRIPTION
test_sub=# ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION;
ALTER SUBSCRIPTION
/* sub # */ ALTER SUBSCRIPTION sub1 ENABLE;
/* sub # */ ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION;
</programlisting></para>
</listitem>
</itemizedlist>
@ -625,13 +602,12 @@ ALTER SUBSCRIPTION
<para>
Create the subscription.
<programlisting>
test_sub=# CREATE SUBSCRIPTION sub1
test_sub-# CONNECTION 'host=localhost dbname=test_pub'
test_sub-# PUBLICATION pub1
test_sub-# WITH (connect=false, slot_name='myslot');
/* sub # */ CREATE SUBSCRIPTION sub1
/* sub - */ CONNECTION 'host=localhost dbname=test_pub'
/* sub - */ PUBLICATION pub1
/* sub - */ WITH (connect=false, slot_name='myslot');
WARNING: subscription was created, but is not connected
HINT: To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription.
CREATE SUBSCRIPTION
</programlisting></para>
</listitem>
<listitem>
@ -639,7 +615,7 @@ CREATE SUBSCRIPTION
On the publisher, manually create a slot using the same name that was
specified during <literal>CREATE SUBSCRIPTION</literal>, e.g. "myslot".
<programlisting>
test_pub=# SELECT * FROM pg_create_logical_replication_slot('myslot', 'pgoutput');
/* pub # */ SELECT * FROM pg_create_logical_replication_slot('myslot', 'pgoutput');
slot_name | lsn
-----------+-----------
myslot | 0/19059A0
@ -651,10 +627,8 @@ test_pub=# SELECT * FROM pg_create_logical_replication_slot('myslot', 'pgoutput'
On the subscriber, the remaining subscription activation steps are the
same as before.
<programlisting>
test_sub=# ALTER SUBSCRIPTION sub1 ENABLE;
ALTER SUBSCRIPTION
test_sub=# ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION;
ALTER SUBSCRIPTION
/* sub # */ ALTER SUBSCRIPTION sub1 ENABLE;
/* sub # */ ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION;
</programlisting></para>
</listitem>
</itemizedlist>
@ -669,18 +643,17 @@ ALTER SUBSCRIPTION
<literal>enabled = false</literal>, and
<literal>create_slot = false</literal> are also needed.
<programlisting>
test_sub=# CREATE SUBSCRIPTION sub1
test_sub-# CONNECTION 'host=localhost dbname=test_pub'
test_sub-# PUBLICATION pub1
test_sub-# WITH (slot_name=NONE, enabled=false, create_slot=false);
CREATE SUBSCRIPTION
/* sub # */ CREATE SUBSCRIPTION sub1
/* sub - */ CONNECTION 'host=localhost dbname=test_pub'
/* sub - */ PUBLICATION pub1
/* sub - */ WITH (slot_name=NONE, enabled=false, create_slot=false);
</programlisting></para>
</listitem>
<listitem>
<para>
On the publisher, manually create a slot using any name, e.g. "myslot".
<programlisting>
test_pub=# SELECT * FROM pg_create_logical_replication_slot('myslot', 'pgoutput');
/* pub # */ SELECT * FROM pg_create_logical_replication_slot('myslot', 'pgoutput');
slot_name | lsn
-----------+-----------
myslot | 0/1905930
@ -692,18 +665,15 @@ test_pub=# SELECT * FROM pg_create_logical_replication_slot('myslot', 'pgoutput'
On the subscriber, associate the subscription with the slot name just
created.
<programlisting>
test_sub=# ALTER SUBSCRIPTION sub1 SET (slot_name='myslot');
ALTER SUBSCRIPTION
/* sub # */ ALTER SUBSCRIPTION sub1 SET (slot_name='myslot');
</programlisting></para>
</listitem>
<listitem>
<para>
The remaining subscription activation steps are same as before.
<programlisting>
test_sub=# ALTER SUBSCRIPTION sub1 ENABLE;
ALTER SUBSCRIPTION
test_sub=# ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION;
ALTER SUBSCRIPTION
/* sub # */ ALTER SUBSCRIPTION sub1 ENABLE;
/* sub # */ ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION;
</programlisting></para>
</listitem>
</itemizedlist>
@ -752,7 +722,7 @@ ALTER SUBSCRIPTION
will return the relevant replication slots associated with the
failover-enabled subscriptions.
<programlisting>
test_sub=# SELECT
/* sub # */ SELECT
array_agg(quote_literal(s.subslotname)) AS slots
FROM pg_subscription s
WHERE s.subfailover AND
@ -775,7 +745,7 @@ test_sub=# SELECT
as they will either be dropped or re-created on the new primary server in those
cases.
<programlisting>
test_sub=# SELECT
/* sub # */ SELECT
array_agg(quote_literal(slot_name)) AS slots
FROM
(
@ -794,7 +764,7 @@ test_sub=# SELECT
Check that the logical replication slots identified above exist on
the standby server and are ready for failover.
<programlisting>
test_standby=# SELECT slot_name, (synced AND NOT temporary AND NOT conflicting) AS failover_ready
/* standby # */ SELECT slot_name, (synced AND NOT temporary AND NOT conflicting) AS failover_ready
FROM pg_replication_slots
WHERE slot_name IN
('sub1','sub2','sub3', 'pg_16394_sync_16385_7394666715149055164');
@ -1024,12 +994,9 @@ test_standby=# SELECT slot_name, (synced AND NOT temporary AND NOT conflicting)
<para>
Create some tables to be used in the following examples.
<programlisting>
test_pub=# CREATE TABLE t1(a int, b int, c text, PRIMARY KEY(a,c));
CREATE TABLE
test_pub=# CREATE TABLE t2(d int, e int, f int, PRIMARY KEY(d));
CREATE TABLE
test_pub=# CREATE TABLE t3(g int, h int, i int, PRIMARY KEY(g));
CREATE TABLE
/* pub # */ CREATE TABLE t1(a int, b int, c text, PRIMARY KEY(a,c));
/* pub # */ CREATE TABLE t2(d int, e int, f int, PRIMARY KEY(d));
/* pub # */ CREATE TABLE t3(g int, h int, i int, PRIMARY KEY(g));
</programlisting></para>
<para>
@ -1038,43 +1005,40 @@ CREATE TABLE
<literal>p2</literal> has two tables. Table <literal>t1</literal> has no row
filter, and table <literal>t2</literal> has a row filter. Publication
<literal>p3</literal> has two tables, and both of them have a row filter.
<programlisting>
test_pub=# CREATE PUBLICATION p1 FOR TABLE t1 WHERE (a > 5 AND c = 'NSW');
CREATE PUBLICATION
test_pub=# CREATE PUBLICATION p2 FOR TABLE t1, t2 WHERE (e = 99);
CREATE PUBLICATION
test_pub=# CREATE PUBLICATION p3 FOR TABLE t2 WHERE (d = 10), t3 WHERE (g = 10);
CREATE PUBLICATION
</programlisting></para>
<programlisting><![CDATA[
/* pub # */ CREATE PUBLICATION p1 FOR TABLE t1 WHERE (a > 5 AND c = 'NSW');
/* pub # */ CREATE PUBLICATION p2 FOR TABLE t1, t2 WHERE (e = 99);
/* pub # */ CREATE PUBLICATION p3 FOR TABLE t2 WHERE (d = 10), t3 WHERE (g = 10);
]]></programlisting></para>
<para>
<command>psql</command> can be used to show the row filter expressions (if
defined) for each publication.
<programlisting>
test_pub=# \dRp+
Publication p1
<programlisting><![CDATA[
/* pub # */ \dRp+
Publication p1
Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
----------+------------+---------+---------+---------+-----------+----------
postgres | f | t | t | t | t | f
Tables:
"public.t1" WHERE ((a > 5) AND (c = 'NSW'::text))
"public.t1" WHERE ((a > 5) AND (c = 'NSW'::text))
Publication p2
Publication p2
Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
----------+------------+---------+---------+---------+-----------+----------
postgres | f | t | t | t | t | f
Tables:
"public.t1"
"public.t2" WHERE (e = 99)
"public.t1"
"public.t2" WHERE (e = 99)
Publication p3
Publication p3
Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
----------+------------+---------+---------+---------+-----------+----------
postgres | f | t | t | t | t | f
Tables:
"public.t2" WHERE (d = 10)
"public.t3" WHERE (g = 10)
</programlisting></para>
"public.t2" WHERE (d = 10)
"public.t3" WHERE (g = 10)
]]></programlisting></para>
<para>
<command>psql</command> can be used to show the row filter expressions (if
@ -1082,8 +1046,8 @@ Tables:
of two publications, but has a row filter only in <literal>p1</literal>.
See that table <literal>t2</literal> is a member of two publications, and
has a different row filter in each of them.
<programlisting>
test_pub=# \d t1
<programlisting><![CDATA[
/* pub # */ \d t1
Table "public.t1"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
@ -1096,7 +1060,7 @@ Publications:
"p1" WHERE ((a > 5) AND (c = 'NSW'::text))
"p2"
test_pub=# \d t2
/* pub # */ \d t2
Table "public.t2"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
@ -1109,7 +1073,7 @@ Publications:
"p2" WHERE (e = 99)
"p3" WHERE (d = 10)
test_pub=# \d t3
/* pub # */ \d t3
Table "public.t3"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
@ -1120,43 +1084,33 @@ Indexes:
"t3_pkey" PRIMARY KEY, btree (g)
Publications:
"p3" WHERE (g = 10)
</programlisting></para>
]]></programlisting></para>
<para>
On the subscriber node, create a table <literal>t1</literal> with the same
definition as the one on the publisher, and also create the subscription
<literal>s1</literal> that subscribes to the publication <literal>p1</literal>.
<programlisting>
test_sub=# CREATE TABLE t1(a int, b int, c text, PRIMARY KEY(a,c));
CREATE TABLE
test_sub=# CREATE SUBSCRIPTION s1
test_sub-# CONNECTION 'host=localhost dbname=test_pub application_name=s1'
test_sub-# PUBLICATION p1;
CREATE SUBSCRIPTION
/* sub # */ CREATE TABLE t1(a int, b int, c text, PRIMARY KEY(a,c));
/* sub # */ CREATE SUBSCRIPTION s1
/* sub - */ CONNECTION 'host=localhost dbname=test_pub application_name=s1'
/* sub - */ PUBLICATION p1;
</programlisting></para>
<para>
Insert some rows. Only the rows satisfying the <literal>t1 WHERE</literal>
clause of publication <literal>p1</literal> are replicated.
<programlisting>
test_pub=# INSERT INTO t1 VALUES (2, 102, 'NSW');
INSERT 0 1
test_pub=# INSERT INTO t1 VALUES (3, 103, 'QLD');
INSERT 0 1
test_pub=# INSERT INTO t1 VALUES (4, 104, 'VIC');
INSERT 0 1
test_pub=# INSERT INTO t1 VALUES (5, 105, 'ACT');
INSERT 0 1
test_pub=# INSERT INTO t1 VALUES (6, 106, 'NSW');
INSERT 0 1
test_pub=# INSERT INTO t1 VALUES (7, 107, 'NT');
INSERT 0 1
test_pub=# INSERT INTO t1 VALUES (8, 108, 'QLD');
INSERT 0 1
test_pub=# INSERT INTO t1 VALUES (9, 109, 'NSW');
INSERT 0 1
/* pub # */ INSERT INTO t1 VALUES (2, 102, 'NSW');
/* pub # */ INSERT INTO t1 VALUES (3, 103, 'QLD');
/* pub # */ INSERT INTO t1 VALUES (4, 104, 'VIC');
/* pub # */ INSERT INTO t1 VALUES (5, 105, 'ACT');
/* pub # */ INSERT INTO t1 VALUES (6, 106, 'NSW');
/* pub # */ INSERT INTO t1 VALUES (7, 107, 'NT');
/* pub # */ INSERT INTO t1 VALUES (8, 108, 'QLD');
/* pub # */ INSERT INTO t1 VALUES (9, 109, 'NSW');
test_pub=# SELECT * FROM t1;
/* pub # */ SELECT * FROM t1;
a | b | c
---+-----+-----
2 | 102 | NSW
@ -1170,7 +1124,7 @@ test_pub=# SELECT * FROM t1;
(8 rows)
</programlisting>
<programlisting>
test_sub=# SELECT * FROM t1;
/* sub # */ SELECT * FROM t1;
a | b | c
---+-----+-----
6 | 106 | NSW
@ -1184,10 +1138,9 @@ test_sub=# SELECT * FROM t1;
<literal>p1</literal>. The <command>UPDATE</command> replicates
the change as normal.
<programlisting>
test_pub=# UPDATE t1 SET b = 999 WHERE a = 6;
UPDATE 1
/* pub # */ UPDATE t1 SET b = 999 WHERE a = 6;
test_pub=# SELECT * FROM t1;
/* pub # */ SELECT * FROM t1;
a | b | c
---+-----+-----
2 | 102 | NSW
@ -1201,7 +1154,7 @@ test_pub=# SELECT * FROM t1;
(8 rows)
</programlisting>
<programlisting>
test_sub=# SELECT * FROM t1;
/* sub # */ SELECT * FROM t1;
a | b | c
---+-----+-----
9 | 109 | NSW
@ -1216,10 +1169,9 @@ test_sub=# SELECT * FROM t1;
transformed into an <command>INSERT</command> and the change is replicated.
See the new row on the subscriber.
<programlisting>
test_pub=# UPDATE t1 SET a = 555 WHERE a = 2;
UPDATE 1
/* pub # */ UPDATE t1 SET a = 555 WHERE a = 2;
test_pub=# SELECT * FROM t1;
/* pub # */ SELECT * FROM t1;
a | b | c
-----+-----+-----
3 | 103 | QLD
@ -1233,7 +1185,7 @@ test_pub=# SELECT * FROM t1;
(8 rows)
</programlisting>
<programlisting>
test_sub=# SELECT * FROM t1;
/* sub # */ SELECT * FROM t1;
a | b | c
-----+-----+-----
9 | 109 | NSW
@ -1249,10 +1201,9 @@ test_sub=# SELECT * FROM t1;
transformed into a <command>DELETE</command> and the change is replicated.
See that the row is removed from the subscriber.
<programlisting>
test_pub=# UPDATE t1 SET c = 'VIC' WHERE a = 9;
UPDATE 1
/* pub # */ UPDATE t1 SET c = 'VIC' WHERE a = 9;
test_pub=# SELECT * FROM t1;
/* pub # */ SELECT * FROM t1;
a | b | c
-----+-----+-----
3 | 103 | QLD
@ -1266,7 +1217,7 @@ test_pub=# SELECT * FROM t1;
(8 rows)
</programlisting>
<programlisting>
test_sub=# SELECT * FROM t1;
/* sub # */ SELECT * FROM t1;
a | b | c
-----+-----+-----
6 | 999 | NSW
@ -1284,17 +1235,13 @@ test_sub=# SELECT * FROM t1;
<para>
Create a partitioned table on the publisher.
<programlisting>
test_pub=# CREATE TABLE parent(a int PRIMARY KEY) PARTITION BY RANGE(a);
CREATE TABLE
test_pub=# CREATE TABLE child PARTITION OF parent DEFAULT;
CREATE TABLE
/* pub # */ CREATE TABLE parent(a int PRIMARY KEY) PARTITION BY RANGE(a);
/* pub # */ CREATE TABLE child PARTITION OF parent DEFAULT;
</programlisting>
Create the same tables on the subscriber.
<programlisting>
test_sub=# CREATE TABLE parent(a int PRIMARY KEY) PARTITION BY RANGE(a);
CREATE TABLE
test_sub=# CREATE TABLE child PARTITION OF parent DEFAULT;
CREATE TABLE
/* sub # */ CREATE TABLE parent(a int PRIMARY KEY) PARTITION BY RANGE(a);
/* sub # */ CREATE TABLE child PARTITION OF parent DEFAULT;
</programlisting></para>
<para>
@ -1302,16 +1249,14 @@ CREATE TABLE
publication parameter <literal>publish_via_partition_root</literal> is set
as true. There are row filters defined on both the partitioned table
(<literal>parent</literal>), and on the partition (<literal>child</literal>).
<programlisting><![CDATA[
/* pub # */ CREATE PUBLICATION p4 FOR TABLE parent WHERE (a < 5), child WHERE (a >= 5)
/* pub - */ WITH (publish_via_partition_root=true);
]]></programlisting>
<programlisting>
test_pub=# CREATE PUBLICATION p4 FOR TABLE parent WHERE (a &lt; 5), child WHERE (a >= 5)
test_pub-# WITH (publish_via_partition_root=true);
CREATE PUBLICATION
</programlisting>
<programlisting>
test_sub=# CREATE SUBSCRIPTION s4
test_sub-# CONNECTION 'host=localhost dbname=test_pub application_name=s4'
test_sub-# PUBLICATION p4;
CREATE SUBSCRIPTION
/* sub # */ CREATE SUBSCRIPTION s4
/* sub - */ CONNECTION 'host=localhost dbname=test_pub application_name=s4'
/* sub - */ PUBLICATION p4;
</programlisting></para>
<para>
@ -1320,12 +1265,10 @@ CREATE SUBSCRIPTION
<literal>parent</literal> (because <literal>publish_via_partition_root</literal>
is true).
<programlisting>
test_pub=# INSERT INTO parent VALUES (2), (4), (6);
INSERT 0 3
test_pub=# INSERT INTO child VALUES (3), (5), (7);
INSERT 0 3
/* pub # */ INSERT INTO parent VALUES (2), (4), (6);
/* pub # */ INSERT INTO child VALUES (3), (5), (7);
test_pub=# SELECT * FROM parent ORDER BY a;
/* pub # */ SELECT * FROM parent ORDER BY a;
a
---
2
@ -1337,7 +1280,7 @@ test_pub=# SELECT * FROM parent ORDER BY a;
(6 rows)
</programlisting>
<programlisting>
test_sub=# SELECT * FROM parent ORDER BY a;
/* sub # */ SELECT * FROM parent ORDER BY a;
a
---
2
@ -1350,16 +1293,13 @@ test_sub=# SELECT * FROM parent ORDER BY a;
Repeat the same test, but with a different value for <literal>publish_via_partition_root</literal>.
The publication parameter <literal>publish_via_partition_root</literal> is
set as false. A row filter is defined on the partition (<literal>child</literal>).
<programlisting><![CDATA[
/* pub # */ DROP PUBLICATION p4;
/* pub # */ CREATE PUBLICATION p4 FOR TABLE parent, child WHERE (a >= 5)
/* pub - */ WITH (publish_via_partition_root=false);
]]></programlisting>
<programlisting>
test_pub=# DROP PUBLICATION p4;
DROP PUBLICATION
test_pub=# CREATE PUBLICATION p4 FOR TABLE parent, child WHERE (a >= 5)
test_pub-# WITH (publish_via_partition_root=false);
CREATE PUBLICATION
</programlisting>
<programlisting>
test_sub=# ALTER SUBSCRIPTION s4 REFRESH PUBLICATION;
ALTER SUBSCRIPTION
/* sub # */ ALTER SUBSCRIPTION s4 REFRESH PUBLICATION;
</programlisting></para>
<para>
@ -1367,14 +1307,11 @@ ALTER SUBSCRIPTION
row filter of <literal>child</literal> (because
<literal>publish_via_partition_root</literal> is false).
<programlisting>
test_pub=# TRUNCATE parent;
TRUNCATE TABLE
test_pub=# INSERT INTO parent VALUES (2), (4), (6);
INSERT 0 3
test_pub=# INSERT INTO child VALUES (3), (5), (7);
INSERT 0 3
/* pub # */ TRUNCATE parent;
/* pub # */ INSERT INTO parent VALUES (2), (4), (6);
/* pub # */ INSERT INTO child VALUES (3), (5), (7);
test_pub=# SELECT * FROM parent ORDER BY a;
/* pub # */ SELECT * FROM parent ORDER BY a;
a
---
2
@ -1386,7 +1323,7 @@ test_pub=# SELECT * FROM parent ORDER BY a;
(6 rows)
</programlisting>
<programlisting>
test_sub=# SELECT * FROM child ORDER BY a;
/* sub # */ SELECT * FROM child ORDER BY a;
a
---
5
@ -1505,8 +1442,7 @@ test_sub=# SELECT * FROM child ORDER BY a;
<para>
Create a table <literal>t1</literal> to be used in the following example.
<programlisting>
test_pub=# CREATE TABLE t1(id int, a text, b text, c text, d text, e text, PRIMARY KEY(id));
CREATE TABLE
/* pub # */ CREATE TABLE t1(id int, a text, b text, c text, d text, e text, PRIMARY KEY(id));
</programlisting></para>
<para>
@ -1515,15 +1451,14 @@ CREATE TABLE
replicated. Notice that the order of column names in the column list does
not matter.
<programlisting>
test_pub=# CREATE PUBLICATION p1 FOR TABLE t1 (id, b, a, d);
CREATE PUBLICATION
/* pub # */ CREATE PUBLICATION p1 FOR TABLE t1 (id, b, a, d);
</programlisting></para>
<para>
<literal>psql</literal> can be used to show the column lists (if defined)
for each publication.
<programlisting>
test_pub=# \dRp+
/* pub # */ \dRp+
Publication p1
Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
----------+------------+---------+---------+---------+-----------+----------
@ -1536,7 +1471,7 @@ Tables:
<literal>psql</literal> can be used to show the column lists (if defined)
for each table.
<programlisting>
test_pub=# \d t1
/* pub # */ \d t1
Table "public.t1"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
@ -1559,24 +1494,19 @@ Publications:
<literal>s1</literal> that subscribes to the publication
<literal>p1</literal>.
<programlisting>
test_sub=# CREATE TABLE t1(id int, b text, a text, d text, PRIMARY KEY(id));
CREATE TABLE
test_sub=# CREATE SUBSCRIPTION s1
test_sub-# CONNECTION 'host=localhost dbname=test_pub application_name=s1'
test_sub-# PUBLICATION p1;
CREATE SUBSCRIPTION
/* sub # */ CREATE TABLE t1(id int, b text, a text, d text, PRIMARY KEY(id));
/* sub # */ CREATE SUBSCRIPTION s1
/* sub - */ CONNECTION 'host=localhost dbname=test_pub application_name=s1'
/* sub - */ PUBLICATION p1;
</programlisting></para>
<para>
On the publisher node, insert some rows to table <literal>t1</literal>.
<programlisting>
test_pub=# INSERT INTO t1 VALUES(1, 'a-1', 'b-1', 'c-1', 'd-1', 'e-1');
INSERT 0 1
test_pub=# INSERT INTO t1 VALUES(2, 'a-2', 'b-2', 'c-2', 'd-2', 'e-2');
INSERT 0 1
test_pub=# INSERT INTO t1 VALUES(3, 'a-3', 'b-3', 'c-3', 'd-3', 'e-3');
INSERT 0 1
test_pub=# SELECT * FROM t1 ORDER BY id;
/* pub # */ INSERT INTO t1 VALUES(1, 'a-1', 'b-1', 'c-1', 'd-1', 'e-1');
/* pub # */ INSERT INTO t1 VALUES(2, 'a-2', 'b-2', 'c-2', 'd-2', 'e-2');
/* pub # */ INSERT INTO t1 VALUES(3, 'a-3', 'b-3', 'c-3', 'd-3', 'e-3');
/* pub # */ SELECT * FROM t1 ORDER BY id;
id | a | b | c | d | e
----+-----+-----+-----+-----+-----
1 | a-1 | b-1 | c-1 | d-1 | e-1
@ -1589,7 +1519,7 @@ test_pub=# SELECT * FROM t1 ORDER BY id;
Only data from the column list of publication <literal>p1</literal> is
replicated.
<programlisting>
test_sub=# SELECT * FROM t1 ORDER BY id;
/* sub # */ SELECT * FROM t1 ORDER BY id;
id | b | a | d
----+-----+-----+-----
1 | b-1 | a-1 | d-1
@ -1617,13 +1547,10 @@ test_sub=# SELECT * FROM t1 ORDER BY id;
For example, note below that subscriber table generated column value comes from the
subscriber column's calculation.
<programlisting>
test_pub=# CREATE TABLE tab_gen_to_gen (a int, b int GENERATED ALWAYS AS (a + 1) STORED);
CREATE TABLE
test_pub=# INSERT INTO tab_gen_to_gen VALUES (1),(2),(3);
INSERT 0 3
test_pub=# CREATE PUBLICATION pub1 FOR TABLE tab_gen_to_gen;
CREATE PUBLICATION
test_pub=# SELECT * FROM tab_gen_to_gen;
/* pub # */ CREATE TABLE tab_gen_to_gen (a int, b int GENERATED ALWAYS AS (a + 1) STORED);
/* pub # */ INSERT INTO tab_gen_to_gen VALUES (1),(2),(3);
/* pub # */ CREATE PUBLICATION pub1 FOR TABLE tab_gen_to_gen;
/* pub # */ SELECT * FROM tab_gen_to_gen;
a | b
---+---
1 | 2
@ -1631,11 +1558,9 @@ test_pub=# SELECT * FROM tab_gen_to_gen;
3 | 4
(3 rows)
test_sub=# CREATE TABLE tab_gen_to_gen (a int, b int GENERATED ALWAYS AS (a * 100) STORED);
CREATE TABLE
test_sub=# CREATE SUBSCRIPTION sub1 CONNECTION 'dbname=test_pub' PUBLICATION pub1;
CREATE SUBSCRIPTION
test_sub=# SELECT * from tab_gen_to_gen;
/* sub # */ CREATE TABLE tab_gen_to_gen (a int, b int GENERATED ALWAYS AS (a * 100) STORED);
/* sub # */ CREATE SUBSCRIPTION sub1 CONNECTION 'dbname=test_pub' PUBLICATION pub1;
/* sub # */ SELECT * from tab_gen_to_gen;
a | b
---+----
1 | 100
@ -2690,8 +2615,7 @@ CONTEXT: processing remote data for replication origin "pg_16395" during "INSER
<link linkend="sql-altersubscription-params-disable"><command>ALTER SUBSCRIPTION ... DISABLE</command></link>,
e.g.:
<programlisting>
node2=# ALTER SUBSCRIPTION sub1_node1_node2 DISABLE;
ALTER SUBSCRIPTION
/* node2 # */ ALTER SUBSCRIPTION sub1_node1_node2 DISABLE;
</programlisting>
</para>
</step>
@ -2780,8 +2704,7 @@ pg_ctl -D /opt/PostgreSQL/data2_upgraded start -l logfile
<xref linkend="two-node-cluster-disable-subscriptions-node2"/>
and now, e.g.:
<programlisting>
node2=# CREATE TABLE distributors (did integer PRIMARY KEY, name varchar(40));
CREATE TABLE
/* node2 # */ CREATE TABLE distributors (did integer PRIMARY KEY, name varchar(40));
</programlisting>
</para>
</step>
@ -2793,8 +2716,7 @@ CREATE TABLE
<link linkend="sql-altersubscription-params-enable"><command>ALTER SUBSCRIPTION ... ENABLE</command></link>,
e.g.:
<programlisting>
node2=# ALTER SUBSCRIPTION sub1_node1_node2 ENABLE;
ALTER SUBSCRIPTION
/* node2 # */ ALTER SUBSCRIPTION sub1_node1_node2 ENABLE;
</programlisting>
</para>
</step>
@ -2805,8 +2727,7 @@ ALTER SUBSCRIPTION
<link linkend="sql-altersubscription-params-refresh-publication"><command>ALTER SUBSCRIPTION ... REFRESH PUBLICATION</command></link>,
e.g.:
<programlisting>
node2=# ALTER SUBSCRIPTION sub1_node1_node2 REFRESH PUBLICATION;
ALTER SUBSCRIPTION
/* node2 # */ ALTER SUBSCRIPTION sub1_node1_node2 REFRESH PUBLICATION;
</programlisting>
</para>
</step>
@ -2844,8 +2765,7 @@ ALTER SUBSCRIPTION
<link linkend="sql-altersubscription-params-disable"><command>ALTER SUBSCRIPTION ... DISABLE</command></link>,
e.g.:
<programlisting>
node2=# ALTER SUBSCRIPTION sub1_node1_node2 DISABLE;
ALTER SUBSCRIPTION
/* node2 # */ ALTER SUBSCRIPTION sub1_node1_node2 DISABLE;
</programlisting>
</para>
</step>
@ -2896,8 +2816,7 @@ pg_ctl -D /opt/PostgreSQL/data1_upgraded start -l logfile
<link linkend="sql-altersubscription-params-disable"><command>ALTER SUBSCRIPTION ... DISABLE</command></link>,
e.g.:
<programlisting>
node3=# ALTER SUBSCRIPTION sub1_node2_node3 DISABLE;
ALTER SUBSCRIPTION
/* node3 # */ ALTER SUBSCRIPTION sub1_node2_node3 DISABLE;
</programlisting>
</para>
</step>
@ -2948,8 +2867,7 @@ pg_ctl -D /opt/PostgreSQL/data2_upgraded start -l logfile
<xref linkend="cascaded-cluster-disable-sub-node1-node2"/>
and now, e.g.:
<programlisting>
node2=# CREATE TABLE distributors (did integer PRIMARY KEY, name varchar(40));
CREATE TABLE
/* node2 # */ CREATE TABLE distributors (did integer PRIMARY KEY, name varchar(40));
</programlisting>
</para>
</step>
@ -2961,8 +2879,7 @@ CREATE TABLE
<link linkend="sql-altersubscription-params-enable"><command>ALTER SUBSCRIPTION ... ENABLE</command></link>,
e.g.:
<programlisting>
node2=# ALTER SUBSCRIPTION sub1_node1_node2 ENABLE;
ALTER SUBSCRIPTION
/* node2 # */ ALTER SUBSCRIPTION sub1_node1_node2 ENABLE;
</programlisting>
</para>
</step>
@ -2973,8 +2890,7 @@ ALTER SUBSCRIPTION
<link linkend="sql-altersubscription-params-refresh-publication"><command>ALTER SUBSCRIPTION ... REFRESH PUBLICATION</command></link>,
e.g.:
<programlisting>
node2=# ALTER SUBSCRIPTION sub1_node1_node2 REFRESH PUBLICATION;
ALTER SUBSCRIPTION
/* node2 # */ ALTER SUBSCRIPTION sub1_node1_node2 REFRESH PUBLICATION;
</programlisting>
</para>
</step>
@ -3025,8 +2941,7 @@ pg_ctl -D /opt/PostgreSQL/data3_upgraded start -l logfile
<xref linkend="cascaded-cluster-disable-sub-node2-node3"/> and now,
e.g.:
<programlisting>
node3=# CREATE TABLE distributors (did integer PRIMARY KEY, name varchar(40));
CREATE TABLE
/* node3 # */ CREATE TABLE distributors (did integer PRIMARY KEY, name varchar(40));
</programlisting>
</para>
</step>
@ -3038,8 +2953,7 @@ CREATE TABLE
<link linkend="sql-altersubscription-params-enable"><command>ALTER SUBSCRIPTION ... ENABLE</command></link>,
e.g.:
<programlisting>
node3=# ALTER SUBSCRIPTION sub1_node2_node3 ENABLE;
ALTER SUBSCRIPTION
/* node3 # */ ALTER SUBSCRIPTION sub1_node2_node3 ENABLE;
</programlisting>
</para>
</step>
@ -3050,8 +2964,7 @@ ALTER SUBSCRIPTION
<link linkend="sql-altersubscription-params-refresh-publication"><command>ALTER SUBSCRIPTION ... REFRESH PUBLICATION</command></link>,
e.g.:
<programlisting>
node3=# ALTER SUBSCRIPTION sub1_node2_node3 REFRESH PUBLICATION;
ALTER SUBSCRIPTION
/* node3 # */ ALTER SUBSCRIPTION sub1_node2_node3 REFRESH PUBLICATION;
</programlisting>
</para>
</step>
@ -3082,8 +2995,7 @@ ALTER SUBSCRIPTION
<link linkend="sql-altersubscription-params-disable"><command>ALTER SUBSCRIPTION ... DISABLE</command></link>,
e.g.:
<programlisting>
node2=# ALTER SUBSCRIPTION sub1_node1_node2 DISABLE;
ALTER SUBSCRIPTION
/* node2 # */ ALTER SUBSCRIPTION sub1_node1_node2 DISABLE;
</programlisting>
</para>
</step>
@ -3134,8 +3046,7 @@ pg_ctl -D /opt/PostgreSQL/data1_upgraded start -l logfile
<link linkend="sql-altersubscription-params-enable"><command>ALTER SUBSCRIPTION ... ENABLE</command></link>,
e.g.:
<programlisting>
node2=# ALTER SUBSCRIPTION sub1_node1_node2 ENABLE;
ALTER SUBSCRIPTION
/* node2 # */ ALTER SUBSCRIPTION sub1_node1_node2 ENABLE;
</programlisting>
</para>
</step>
@ -3146,8 +3057,7 @@ ALTER SUBSCRIPTION
<literal>node2</literal> between <xref linkend="circular-cluster-disable-sub-node2"/>
and now, e.g.:
<programlisting>
node1=# CREATE TABLE distributors (did integer PRIMARY KEY, name varchar(40));
CREATE TABLE
/* node1 # */ CREATE TABLE distributors (did integer PRIMARY KEY, name varchar(40));
</programlisting>
</para>
</step>
@ -3160,8 +3070,7 @@ CREATE TABLE
<link linkend="sql-altersubscription-params-refresh-publication"><command>ALTER SUBSCRIPTION ... REFRESH PUBLICATION</command></link>,
e.g.:
<programlisting>
node1=# ALTER SUBSCRIPTION sub1_node2_node1 REFRESH PUBLICATION;
ALTER SUBSCRIPTION
/* node1 # */ ALTER SUBSCRIPTION sub1_node2_node1 REFRESH PUBLICATION;
</programlisting>
</para>
</step>
@ -3173,8 +3082,7 @@ ALTER SUBSCRIPTION
<link linkend="sql-altersubscription-params-disable"><command>ALTER SUBSCRIPTION ... DISABLE</command></link>,
e.g.:
<programlisting>
node1=# ALTER SUBSCRIPTION sub1_node2_node1 DISABLE;
ALTER SUBSCRIPTION
/* node1 # */ ALTER SUBSCRIPTION sub1_node2_node1 DISABLE;
</programlisting>
</para>
</step>
@ -3225,8 +3133,7 @@ pg_ctl -D /opt/PostgreSQL/data2_upgraded start -l logfile
<link linkend="sql-altersubscription-params-enable"><command>ALTER SUBSCRIPTION ... ENABLE</command></link>,
e.g.:
<programlisting>
node1=# ALTER SUBSCRIPTION sub1_node2_node1 ENABLE;
ALTER SUBSCRIPTION
/* node1 # */ ALTER SUBSCRIPTION sub1_node2_node1 ENABLE;
</programlisting>
</para>
</step>
@ -3237,8 +3144,7 @@ ALTER SUBSCRIPTION
the upgraded <literal>node1</literal> between <xref linkend="circular-cluster-disable-sub-node1"/>
and now, e.g.:
<programlisting>
node2=# CREATE TABLE distributors (did integer PRIMARY KEY, name varchar(40));
CREATE TABLE
/* node2 # */ CREATE TABLE distributors (did integer PRIMARY KEY, name varchar(40));
</programlisting>
</para>
</step>
@ -3250,8 +3156,7 @@ CREATE TABLE
<link linkend="sql-altersubscription-params-refresh-publication"><command>ALTER SUBSCRIPTION ... REFRESH PUBLICATION</command></link>,
e.g.:
<programlisting>
node2=# ALTER SUBSCRIPTION sub1_node1_node2 REFRESH PUBLICATION;
ALTER SUBSCRIPTION
/* node2 # */ ALTER SUBSCRIPTION sub1_node1_node2 REFRESH PUBLICATION;
</programlisting>
</para>
</step>

View File

@ -455,9 +455,8 @@ postgres=# select * from pg_logical_slot_get_changes('regression_slot', NULL, NU
using the slot's contents without losing any changes.
</para>
<para>
Creation of a snapshot is not always possible. In particular, it will
fail when connected to a hot standby. Applications that do not require
snapshot export may suppress it with the <literal>NOEXPORT_SNAPSHOT</literal>
Applications that do not require
snapshot export may suppress it with the <literal>SNAPSHOT 'nothing'</literal>
option.
</para>
</sect2>

View File

@ -220,7 +220,12 @@
was not supported by the server.
</para>
<table>
<para>
<xref linkend="protocol-versions-table"/> shows the currently supported
protocol versions.
</para>
<table id="protocol-versions-table">
<title>Protocol versions</title>
<tgroup cols="3">

View File

@ -81,6 +81,35 @@ PostgreSQL documentation
</listitem>
</varlistentry>
<varlistentry>
<term><option>-k</option></term>
<term><option>--link</option></term>
<listitem>
<para>
Use hard links instead of copying files to the synthetic backup.
Reconstruction of the synthetic backup might be faster (no file copying)
and use less disk space, but care must be taken when using the output
directory, because any modifications to that directory (for example,
starting the server) can also affect the input directories. Likewise,
changes to the input directories (for example, starting the server on
the full backup) could affect the output directory. Thus, this option
is best used when the input directories are only copies that will be
removed after <application>pg_combinebackup</application> has completed.
</para>
<para>
Requires that the input backups and the output directory are in the
same file system.
</para>
<para>
If a backup manifest is not available or does not contain checksum of
the right type, hard links will still be created, but the file will be
also read block-by-block for the checksum calculation.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-n</option></term>
<term><option>--dry-run</option></term>
@ -137,35 +166,6 @@ PostgreSQL documentation
</listitem>
</varlistentry>
<varlistentry>
<term><option>-k</option></term>
<term><option>--link</option></term>
<listitem>
<para>
Use hard links instead of copying files to the synthetic backup.
Reconstruction of the synthetic backup might be faster (no file copying)
and use less disk space, but care must be taken when using the output
directory, because any modifications to that directory (for example,
starting the server) can also affect the input directories. Likewise,
changes to the input directories (for example, starting the server on
the full backup) could affect the output directory. Thus, this option
is best used when the input directories are only copies that will be
removed after <application>pg_combinebackup</application> has completed.
</para>
<para>
Requires that the input backups and the output directory are in the
same file system.
</para>
<para>
If a backup manifest is not available or does not contain checksum of
the right type, hard links will still be created, but the file will be
also read block-by-block for the checksum calculation.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--clone</option></term>
<listitem>

View File

@ -1134,7 +1134,7 @@ PostgreSQL documentation
<term><option>--no-statistics</option></term>
<listitem>
<para>
Do not dump statistics.
Do not dump statistics. This is the default.
</para>
</listitem>
</varlistentry>
@ -1461,7 +1461,7 @@ PostgreSQL documentation
<term><option>--with-statistics</option></term>
<listitem>
<para>
Dump statistics. This is the default.
Dump statistics.
</para>
</listitem>
</varlistentry>
@ -1681,14 +1681,14 @@ CREATE DATABASE foo WITH TEMPLATE template0;
</para>
<para>
By default, <command>pg_dump</command> will include most optimizer
statistics in the resulting dump file. However, some statistics may not be
included, such as those created explicitly with <xref
linkend="sql-createstatistics"/> or custom statistics added by an
extension. Therefore, it may be useful to run <command>ANALYZE</command>
after restoring from a dump file to ensure optimal performance; see <xref
linkend="vacuum-for-statistics"/> and <xref linkend="autovacuum"/> for more
information.
If <option>--with-statistics</option> is specified,
<command>pg_dump</command> will include most optimizer statistics in the
resulting dump file. However, some statistics may not be included, such as
those created explicitly with <xref linkend="sql-createstatistics"/> or
custom statistics added by an extension. Therefore, it may be useful to
run <command>ANALYZE</command> after restoring from a dump file to ensure
optimal performance; see <xref linkend="vacuum-for-statistics"/> and <xref
linkend="autovacuum"/> for more information.
</para>
<para>

View File

@ -567,7 +567,7 @@ exclude database <replaceable class="parameter">PATTERN</replaceable>
<term><option>--no-statistics</option></term>
<listitem>
<para>
Do not dump statistics.
Do not dump statistics. This is the default.
</para>
</listitem>
</varlistentry>
@ -695,6 +695,17 @@ exclude database <replaceable class="parameter">PATTERN</replaceable>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--sequence-data</option></term>
<listitem>
<para>
Include sequence data in the dump. This is the default behavior except
when <option>--no-data</option>, <option>--schema-only</option>, or
<option>--statistics-only</option> is specified.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--use-set-session-authorization</option></term>
<listitem>
@ -730,7 +741,7 @@ exclude database <replaceable class="parameter">PATTERN</replaceable>
<term><option>--with-statistics</option></term>
<listitem>
<para>
Dump statistics. This is the default.
Dump statistics.
</para>
</listitem>
</varlistentry>
@ -946,14 +957,14 @@ exclude database <replaceable class="parameter">PATTERN</replaceable>
</para>
<para>
By default, <command>pg_dumpall</command> will include most optimizer
statistics in the resulting dump file. However, some statistics may not be
included, such as those created explicitly with <xref
linkend="sql-createstatistics"/> or custom statistics added by an
extension. Therefore, it may be useful to run <command>ANALYZE</command>
on each database after restoring from a dump file to ensure optimal
performance. You can also run <command>vacuumdb -a -z</command> to analyze
all databases.
If <option>--with-statistics</option> is specified,
<command>pg_dumpall</command> will include most optimizer statistics in the
resulting dump file. However, some statistics may not be included, such as
those created explicitly with <xref linkend="sql-createstatistics"/> or
custom statistics added by an extension. Therefore, it may be useful to
run <command>ANALYZE</command> on each database after restoring from a dump
file to ensure optimal performance. You can also run <command>vacuumdb -a
-z</command> to analyze all databases.
</para>
<para>

View File

@ -171,25 +171,6 @@ PostgreSQL documentation
</para>
<variablelist>
<varlistentry>
<term><option>--char-signedness=<replaceable class="parameter">option</replaceable></option></term>
<listitem>
<para>
Manually set the default char signedness. Possible values are
<literal>signed</literal> and <literal>unsigned</literal>.
</para>
<para>
For a database cluster that <command>pg_upgrade</command> upgraded from
a <productname>PostgreSQL</productname> version before 18, the safe
value would be the default <type>char</type> signedness of the platform
that ran the cluster before that upgrade. For all other
clusters, <literal>signed</literal> would be the safe value. However,
this option is exclusively for use with <command>pg_upgrade</command>
and should not normally be used manually.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-c <replaceable class="parameter">xid</replaceable>,<replaceable class="parameter">xid</replaceable></option></term>
<term><option>--commit-timestamp-ids=<replaceable class="parameter">xid</replaceable>,<replaceable class="parameter">xid</replaceable></option></term>
@ -332,34 +313,6 @@ PostgreSQL documentation
</listitem>
</varlistentry>
<varlistentry>
<term><option>--wal-segsize=<replaceable class="parameter">wal_segment_size</replaceable></option></term>
<listitem>
<para>
Set the new WAL segment size, in megabytes. The value must be set to a
power of 2 between 1 and 1024 (megabytes). See the same option of <xref
linkend="app-initdb"/> for more information.
</para>
<para>
This option can also be used to change the WAL segment size of an
existing database cluster, avoiding the need to
re-<command>initdb</command>.
</para>
<note>
<para>
While <command>pg_resetwal</command> will set the WAL starting address
beyond the latest existing WAL segment file, some segment size changes
can cause previous WAL file names to be reused. It is recommended to
use <option>-l</option> together with this option to manually set the
WAL starting address if WAL file name overlap will cause problems with
your archiving strategy.
</para>
</note>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-u <replaceable class="parameter">xid</replaceable></option></term>
<term><option>--oldest-transaction-id=<replaceable class="parameter">xid</replaceable></option></term>
@ -402,6 +355,53 @@ PostgreSQL documentation
<!-- 1048576 = SLRU_PAGES_PER_SEGMENT * BLCKSZ * CLOG_XACTS_PER_BYTE -->
</listitem>
</varlistentry>
<varlistentry>
<term><option>--char-signedness=<replaceable class="parameter">option</replaceable></option></term>
<listitem>
<para>
Manually set the default char signedness. Possible values are
<literal>signed</literal> and <literal>unsigned</literal>.
</para>
<para>
For a database cluster that <command>pg_upgrade</command> upgraded from
a <productname>PostgreSQL</productname> version before 18, the safe
value would be the default <type>char</type> signedness of the platform
that ran the cluster before that upgrade. For all other
clusters, <literal>signed</literal> would be the safe value. However,
this option is exclusively for use with <command>pg_upgrade</command>
and should not normally be used manually.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--wal-segsize=<replaceable class="parameter">wal_segment_size</replaceable></option></term>
<listitem>
<para>
Set the new WAL segment size, in megabytes. The value must be set to a
power of 2 between 1 and 1024 (megabytes). See the same option of <xref
linkend="app-initdb"/> for more information.
</para>
<para>
This option can also be used to change the WAL segment size of an
existing database cluster, avoiding the need to
re-<command>initdb</command>.
</para>
<note>
<para>
While <command>pg_resetwal</command> will set the WAL starting address
beyond the latest existing WAL segment file, some segment size changes
can cause previous WAL file names to be reused. It is recommended to
use <option>-l</option> together with this option to manually set the
WAL starting address if WAL file name overlap will cause problems with
your archiving strategy.
</para>
</note>
</listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@ -177,28 +177,6 @@ PostgreSQL documentation
</listitem>
</varlistentry>
<varlistentry>
<term><option>--exclude-database=<replaceable class="parameter">pattern</replaceable></option></term>
<listitem>
<para>
Do not restore databases whose name matches
<replaceable class="parameter">pattern</replaceable>.
Multiple patterns can be excluded by writing multiple
<option>--exclude-database</option> switches. The
<replaceable class="parameter">pattern</replaceable> parameter is
interpreted as a pattern according to the same rules used by
<application>psql</application>'s <literal>\d</literal>
commands (see <xref linkend="app-psql-patterns"/>),
so multiple databases can also be excluded by writing wildcard
characters in the pattern. When using wildcards, be careful to
quote the pattern if needed to prevent shell wildcard expansion.
</para>
<para>
This option is only relevant when restoring from an archive made using <application>pg_dumpall</application>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-e</option></term>
<term><option>--exit-on-error</option></term>
@ -223,86 +201,6 @@ PostgreSQL documentation
</listitem>
</varlistentry>
<varlistentry>
<term><option>--filter=<replaceable class="parameter">filename</replaceable></option></term>
<listitem>
<para>
Specify a filename from which to read patterns for objects excluded
or included from restore. The patterns are interpreted according to the
same rules as
<option>-n</option>/<option>--schema</option> for including objects in schemas,
<option>-N</option>/<option>--exclude-schema</option> for excluding objects in schemas,
<option>-P</option>/<option>--function</option> for restoring named functions,
<option>-I</option>/<option>--index</option> for restoring named indexes,
<option>-t</option>/<option>--table</option> for restoring named tables
or <option>-T</option>/<option>--trigger</option> for restoring triggers.
To read from <literal>STDIN</literal>, use <filename>-</filename> as the
filename. The <option>--filter</option> option can be specified in
conjunction with the above listed options for including or excluding
objects, and can also be specified more than once for multiple filter
files.
</para>
<para>
The file lists one database pattern per row, with the following format:
<synopsis>
{ include | exclude } { function | index | schema | table | trigger } <replaceable class="parameter">PATTERN</replaceable>
</synopsis>
</para>
<para>
The first keyword specifies whether the objects matched by the pattern
are to be included or excluded. The second keyword specifies the type
of object to be filtered using the pattern:
<itemizedlist>
<listitem>
<para>
<literal>function</literal>: functions, works like the
<option>-P</option>/<option>--function</option> option. This keyword
can only be used with the <literal>include</literal> keyword.
</para>
</listitem>
<listitem>
<para>
<literal>index</literal>: indexes, works like the
<option>-I</option>/<option>--indexes</option> option. This keyword
can only be used with the <literal>include</literal> keyword.
</para>
</listitem>
<listitem>
<para>
<literal>schema</literal>: schemas, works like the
<option>-n</option>/<option>--schema</option> and
<option>-N</option>/<option>--exclude-schema</option> options.
</para>
</listitem>
<listitem>
<para>
<literal>table</literal>: tables, works like the
<option>-t</option>/<option>--table</option> option. This keyword
can only be used with the <literal>include</literal> keyword.
</para>
</listitem>
<listitem>
<para>
<literal>trigger</literal>: triggers, works like the
<option>-T</option>/<option>--trigger</option> option. This keyword
can only be used with the <literal>include</literal> keyword.
</para>
</listitem>
</itemizedlist>
</para>
<para>
Lines starting with <literal>#</literal> are considered comments and
ignored. Comments can be placed after an object pattern row as well.
Blank lines are also ignored. See <xref linkend="app-psql-patterns"/>
for how to perform quoting in patterns.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-F <replaceable class="parameter">format</replaceable></option></term>
<term><option>--format=<replaceable class="parameter">format</replaceable></option></term>
@ -646,15 +544,6 @@ PostgreSQL documentation
</listitem>
</varlistentry>
<varlistentry>
<term><option>--statistics-only</option></term>
<listitem>
<para>
Restore only the statistics, not schema (data definitions) or data.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-1</option></term>
<term><option>--single-transaction</option></term>
@ -714,6 +603,108 @@ PostgreSQL documentation
</listitem>
</varlistentry>
<varlistentry>
<term><option>--exclude-database=<replaceable class="parameter">pattern</replaceable></option></term>
<listitem>
<para>
Do not restore databases whose name matches
<replaceable class="parameter">pattern</replaceable>.
Multiple patterns can be excluded by writing multiple
<option>--exclude-database</option> switches. The
<replaceable class="parameter">pattern</replaceable> parameter is
interpreted as a pattern according to the same rules used by
<application>psql</application>'s <literal>\d</literal>
commands (see <xref linkend="app-psql-patterns"/>),
so multiple databases can also be excluded by writing wildcard
characters in the pattern. When using wildcards, be careful to
quote the pattern if needed to prevent shell wildcard expansion.
</para>
<para>
This option is only relevant when restoring from an archive made using <application>pg_dumpall</application>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--filter=<replaceable class="parameter">filename</replaceable></option></term>
<listitem>
<para>
Specify a filename from which to read patterns for objects excluded
or included from restore. The patterns are interpreted according to the
same rules as
<option>-n</option>/<option>--schema</option> for including objects in schemas,
<option>-N</option>/<option>--exclude-schema</option> for excluding objects in schemas,
<option>-P</option>/<option>--function</option> for restoring named functions,
<option>-I</option>/<option>--index</option> for restoring named indexes,
<option>-t</option>/<option>--table</option> for restoring named tables
or <option>-T</option>/<option>--trigger</option> for restoring triggers.
To read from <literal>STDIN</literal>, use <filename>-</filename> as the
filename. The <option>--filter</option> option can be specified in
conjunction with the above listed options for including or excluding
objects, and can also be specified more than once for multiple filter
files.
</para>
<para>
The file lists one database pattern per row, with the following format:
<synopsis>
{ include | exclude } { function | index | schema | table | trigger } <replaceable class="parameter">PATTERN</replaceable>
</synopsis>
</para>
<para>
The first keyword specifies whether the objects matched by the pattern
are to be included or excluded. The second keyword specifies the type
of object to be filtered using the pattern:
<itemizedlist>
<listitem>
<para>
<literal>function</literal>: functions, works like the
<option>-P</option>/<option>--function</option> option. This keyword
can only be used with the <literal>include</literal> keyword.
</para>
</listitem>
<listitem>
<para>
<literal>index</literal>: indexes, works like the
<option>-I</option>/<option>--indexes</option> option. This keyword
can only be used with the <literal>include</literal> keyword.
</para>
</listitem>
<listitem>
<para>
<literal>schema</literal>: schemas, works like the
<option>-n</option>/<option>--schema</option> and
<option>-N</option>/<option>--exclude-schema</option> options.
</para>
</listitem>
<listitem>
<para>
<literal>table</literal>: tables, works like the
<option>-t</option>/<option>--table</option> option. This keyword
can only be used with the <literal>include</literal> keyword.
</para>
</listitem>
<listitem>
<para>
<literal>trigger</literal>: triggers, works like the
<option>-T</option>/<option>--trigger</option> option. This keyword
can only be used with the <literal>include</literal> keyword.
</para>
</listitem>
</itemizedlist>
</para>
<para>
Lines starting with <literal>#</literal> are considered comments and
ignored. Comments can be placed after an object pattern row as well.
Blank lines are also ignored. See <xref linkend="app-psql-patterns"/>
for how to perform quoting in patterns.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--if-exists</option></term>
<listitem>
@ -851,33 +842,6 @@ PostgreSQL documentation
</listitem>
</varlistentry>
<varlistentry>
<term><option>--with-data</option></term>
<listitem>
<para>
Dump data. This is the default.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--with-schema</option></term>
<listitem>
<para>
Dump schema (data definitions). This is the default.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--with-statistics</option></term>
<listitem>
<para>
Dump statistics. This is the default.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--section=<replaceable class="parameter">sectionname</replaceable></option></term>
<listitem>
@ -897,6 +861,15 @@ PostgreSQL documentation
</listitem>
</varlistentry>
<varlistentry>
<term><option>--statistics-only</option></term>
<listitem>
<para>
Restore only the statistics, not schema (data definitions) or data.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--strict-names</option></term>
<listitem>
@ -946,6 +919,36 @@ PostgreSQL documentation
</listitem>
</varlistentry>
<varlistentry>
<term><option>--with-data</option></term>
<listitem>
<para>
Output commands to restore data, if the archive contains them.
This is the default.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--with-schema</option></term>
<listitem>
<para>
Output commands to restore schema (data definitions), if the archive
contains them. This is the default.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--with-statistics</option></term>
<listitem>
<para>
Output commands to restore statistics, if the archive contains them.
This is the default.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-?</option></term>
<term><option>--help</option></term>

View File

@ -143,35 +143,6 @@ PostgreSQL documentation
</listitem>
</varlistentry>
<varlistentry>
<term><option>-i <replaceable class="parameter">path</replaceable></option></term>
<term><option>--ignore=<replaceable class="parameter">path</replaceable></option></term>
<listitem>
<para>
Ignore the specified file or directory, which should be expressed
as a relative path name, when comparing the list of data files
actually present in the backup to those listed in the
<literal>backup_manifest</literal> file. If a directory is
specified, this option affects the entire subtree rooted at that
location. Complaints about extra files, missing files, file size
differences, or checksum mismatches will be suppressed if the
relative path name matches the specified path name. This option
can be specified multiple times.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-m <replaceable class="parameter">path</replaceable></option></term>
<term><option>--manifest-path=<replaceable class="parameter">path</replaceable></option></term>
<listitem>
<para>
Use the manifest file at the specified path, rather than one located
in the root of the backup directory.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-F <replaceable class="parameter">format</replaceable></option></term>
<term><option>--format=<replaceable class="parameter">format</replaceable></option></term>
@ -211,6 +182,35 @@ PostgreSQL documentation
</listitem>
</varlistentry>
<varlistentry>
<term><option>-i <replaceable class="parameter">path</replaceable></option></term>
<term><option>--ignore=<replaceable class="parameter">path</replaceable></option></term>
<listitem>
<para>
Ignore the specified file or directory, which should be expressed
as a relative path name, when comparing the list of data files
actually present in the backup to those listed in the
<literal>backup_manifest</literal> file. If a directory is
specified, this option affects the entire subtree rooted at that
location. Complaints about extra files, missing files, file size
differences, or checksum mismatches will be suppressed if the
relative path name matches the specified path name. This option
can be specified multiple times.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-m <replaceable class="parameter">path</replaceable></option></term>
<term><option>--manifest-path=<replaceable class="parameter">path</replaceable></option></term>
<listitem>
<para>
Use the manifest file at the specified path, rather than one located
in the root of the backup directory.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-n</option></term>
<term><option>--no-parse-wal</option></term>

View File

@ -145,15 +145,6 @@ PostgreSQL documentation
</listitem>
</varlistentry>
<varlistentry>
<term><option>--no-statistics</option></term>
<listitem>
<para>
Do not restore statistics from the old cluster into the new cluster.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-o</option> <replaceable class="parameter">options</replaceable></term>
<term><option>--old-options</option> <replaceable class="parameter">options</replaceable></term>
@ -264,50 +255,10 @@ PostgreSQL documentation
</varlistentry>
<varlistentry>
<term><option>--swap</option></term>
<term><option>--no-statistics</option></term>
<listitem>
<para>
Move the data directories from the old cluster to the new cluster.
Then, replace the catalog files with those generated for the new
cluster. This mode can outperform <option>--link</option>,
<option>--clone</option>, <option>--copy</option>, and
<option>--copy-file-range</option>, especially on clusters with many
relations.
</para>
<para>
However, this mode creates many garbage files in the old cluster, which
can prolong the file synchronization step if
<option>--sync-method=syncfs</option> is used. Therefore, it is
recommended to use <option>--sync-method=fsync</option> with
<option>--swap</option>.
</para>
<para>
Additionally, once the file transfer step begins, the old cluster will
be destructively modified and therefore will no longer be safe to
start. See <xref linkend="pgupgrade-step-revert"/> for details.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--sync-method=</option><replaceable>method</replaceable></term>
<listitem>
<para>
When set to <literal>fsync</literal>, which is the default,
<command>pg_upgrade</command> will recursively open and synchronize all
files in the upgraded cluster's data directory. The search for files
will follow symbolic links for the WAL directory and each configured
tablespace.
</para>
<para>
On Linux, <literal>syncfs</literal> may be used instead to ask the
operating system to synchronize the whole file systems that contain the
upgraded cluster's data directory, its WAL files, and each tablespace.
See <xref linkend="guc-recovery-init-sync-method"/> for information
about the caveats to be aware of when using <literal>syncfs</literal>.
</para>
<para>
This option has no effect when <option>--no-sync</option> is used.
Do not restore statistics from the old cluster into the new cluster.
</para>
</listitem>
</varlistentry>
@ -365,6 +316,55 @@ PostgreSQL documentation
</listitem>
</varlistentry>
<varlistentry>
<term><option>--swap</option></term>
<listitem>
<para>
Move the data directories from the old cluster to the new cluster.
Then, replace the catalog files with those generated for the new
cluster. This mode can outperform <option>--link</option>,
<option>--clone</option>, <option>--copy</option>, and
<option>--copy-file-range</option>, especially on clusters with many
relations.
</para>
<para>
However, this mode creates many garbage files in the old cluster, which
can prolong the file synchronization step if
<option>--sync-method=syncfs</option> is used. Therefore, it is
recommended to use <option>--sync-method=fsync</option> with
<option>--swap</option>.
</para>
<para>
Additionally, once the file transfer step begins, the old cluster will
be destructively modified and therefore will no longer be safe to
start. See <xref linkend="pgupgrade-step-revert"/> for details.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--sync-method=</option><replaceable>method</replaceable></term>
<listitem>
<para>
When set to <literal>fsync</literal>, which is the default,
<command>pg_upgrade</command> will recursively open and synchronize all
files in the upgraded cluster's data directory. The search for files
will follow symbolic links for the WAL directory and each configured
tablespace.
</para>
<para>
On Linux, <literal>syncfs</literal> may be used instead to ask the
operating system to synchronize the whole file systems that contain the
upgraded cluster's data directory, its WAL files, and each tablespace.
See <xref linkend="guc-recovery-init-sync-method"/> for information
about the caveats to be aware of when using <literal>syncfs</literal>.
</para>
<para>
This option has no effect when <option>--no-sync</option> is used.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-?</option></term>
<term><option>--help</option></term>

View File

@ -1067,16 +1067,6 @@ INSERT INTO tbls1 VALUES ($1, $2) \parse stmt1
</listitem>
</varlistentry>
<varlistentry id="app-psql-meta-command-conninfo">
<term><literal>\conninfo</literal></term>
<listitem>
<para>
Outputs information about the current database connection,
including TLS-related information if TLS is in use.
</para>
</listitem>
</varlistentry>
<varlistentry id="app-psql-meta-command-close">
<term><literal>\close</literal> <replaceable class="parameter">prepared_statement_name</replaceable></term>
@ -1106,6 +1096,16 @@ SELECT $1 \parse stmt1
</listitem>
</varlistentry>
<varlistentry id="app-psql-meta-command-conninfo">
<term><literal>\conninfo</literal></term>
<listitem>
<para>
Outputs information about the current database connection,
including TLS-related information if TLS is in use.
</para>
</listitem>
</varlistentry>
<varlistentry id="app-psql-meta-commands-copy">
<term><literal>\copy { <replaceable class="parameter">table</replaceable> [ ( <replaceable class="parameter">column_list</replaceable> ) ] }
<literal>from</literal>

View File

@ -465,14 +465,17 @@ Indexes:
</programlisting>
If the index marked <literal>INVALID</literal> is suffixed
<literal>ccnew</literal>, then it corresponds to the transient
<literal>_ccnew</literal>, then it corresponds to the transient
index created during the concurrent operation, and the recommended
recovery method is to drop it using <literal>DROP INDEX</literal>,
then attempt <command>REINDEX CONCURRENTLY</command> again.
If the invalid index is instead suffixed <literal>ccold</literal>,
If the invalid index is instead suffixed <literal>_ccold</literal>,
it corresponds to the original index which could not be dropped;
the recommended recovery method is to just drop said index, since the
rebuild proper has been successful.
A nonzero number may be appended to the suffix of the invalid index
names to keep them unique, like <literal>_ccnew1</literal>,
<literal>_ccold2</literal>, etc.
</para>
<para>

View File

@ -84,6 +84,10 @@ SECURITY LABEL [ FOR <replaceable class="parameter">provider</replaceable> ] ON
based on object labels, rather than traditional discretionary access control
(DAC) concepts such as users and groups.
</para>
<para>
You must own the database object to use <command>SECURITY LABEL</command>.
</para>
</refsect1>
<refsect1>

View File

@ -6,7 +6,7 @@
<formalpara>
<title>Release date:</title>
<para>2025-??-??, CURRENT AS OF 2025-05-01</para>
<para>2025-??-??, CURRENT AS OF 2025-05-23</para>
</formalpara>
<sect2 id="release-18-highlights">
@ -53,6 +53,24 @@
<itemizedlist>
<!--
Author: Peter Eisentraut <peter@eisentraut.org>
2024-10-16 [04bec894a04] initdb: Change default to using data checksums.
-->
<listitem>
<para>
Change initdb default to enable data checksums
<ulink url="&commit_baseurl;04bec894a04">&sect;</ulink>
</para>
<para>
Checksums can be disabled with the new initdb option --no-data-checksums.
pg_upgrade requires matching cluster checksum settings, so this new
option can be useful to upgrade non-checksum old clusters.
</para>
</listitem>
<!--
Author: Tom Lane <tgl@sss.pgh.pa.us>
2025-01-16 [d7674c9fa] Seek zone abbreviations in the IANA data before timezone
@ -82,7 +100,8 @@ Deprecate MD5 password authentication (Nathan Bossart)
</para>
<para>
Warnings generated by their use can be disabled by the server variable md5_password_warnings.
Support for MD5 passwords will be removed in a future major version release. CREATE ROLE and ALTER ROLE now emit deprecation warnings when setting MD5 passwords.
These warnings can be disabled by setting the md5_password_warnings parameter to "off".
</para>
</listitem>
@ -138,6 +157,22 @@ Previously ALTER TABLE SET [UN]LOGGED did nothing, and the creation of an unlogg
</para>
</listitem>
<!--
Author: Tom Lane <tgl@sss.pgh.pa.us>
2025-01-23 [01463e1cc] Ensure that AFTER triggers run as the instigating user.
-->
<listitem>
<para>
Execute AFTER triggers as the role that was active when trigger events were queued (Laurenz Albe)
</para>
<para>
Previously such triggers were run as the role that was active at trigger execution time (e.g., at COMMIT). This is significant for cases where the role is changed between queue time and
transaction commit.
</para>
</listitem>
<!--
Author: Fujii Masao <fujii@postgresql.org>
2024-09-12 [fefa76f70] Remove old RULE privilege completely.
@ -173,16 +208,23 @@ This is now longer needed since pg_backend_memory_contexts.path was added.
<!--
Author: David Rowley <drowley@postgresql.org>
2024-07-25 [32d3ed816] Add path column to pg_backend_memory_contexts view
Author: David Rowley <drowley@postgresql.org>
2025-04-18 [d9e03864b] Make levels 1-based in
pg_log_backend_memory_contexts()
Author: Fujii Masao <fujii@postgresql.org>
2025-04-21 [706cbed35] doc: Fix memory context level in pg_log_backend_memory_c
-->
<listitem>
<para>
Change pg_backend_memory_contexts.level to be one-based (Melih Mutlu)
Change pg_backend_memory_contexts.level and pg_log_backend_memory_contexts() to be one-based (Melih Mutlu, Atsushi Torikoshi, David Rowley, Fujii Masao)
<ulink url="&commit_baseurl;32d3ed816">&sect;</ulink>
<ulink url="&commit_baseurl;d9e03864b">&sect;</ulink>
<ulink url="&commit_baseurl;706cbed35">&sect;</ulink>
</para>
<para>
It was previously zero-based.
These were previously zero-based.
</para>
</listitem>
@ -304,16 +346,25 @@ from the grouping. This was already true for non-deferred primary keys.
<!--
Author: Richard Guo <rguo@postgresql.org>
2024-10-09 [67a54b9e8] Allow pushdown of HAVING clauses with grouping sets
Author: Richard Guo <rguo@postgresql.org>
2024-09-10 [247dea89f] Introduce an RTE for the grouping step
Author: Richard Guo <rguo@postgresql.org>
2024-09-10 [f5050f795] Mark expressions nullable by grouping sets
Author: Richard Guo <rguo@postgresql.org>
2025-03-13 [cc5d98525] Fix incorrect handling of subquery pullup
-->
<listitem>
<para>
Allow some HAVING clauses on GROUPING SETS to be pushed to WHERE clauses (Richard Guo)
<ulink url="&commit_baseurl;67a54b9e8">&sect;</ulink>
<ulink url="&commit_baseurl;247dea89f">&sect;</ulink>
<ulink url="&commit_baseurl;f5050f795">&sect;</ulink>
<ulink url="&commit_baseurl;cc5d98525">&sect;</ulink>
</para>
<para>
This allows earlier row filtering.
This allows earlier row filtering. This release also fixes some GROUPING SETS queries that used to return incorrect results.
</para>
</listitem>
@ -360,34 +411,6 @@ Allow merge joins to use incremental sorts (Richard Guo)
</para>
</listitem>
<!--
Author: David Rowley <drowley@postgresql.org>
2024-08-20 [adf97c156] Speed up Hash Join by making ExprStates support hashing
Author: David Rowley <drowley@postgresql.org>
2024-12-11 [0f5738202] Use ExprStates for hashing in GROUP BY and SubPlans
Author: Jeff Davis <jdavis@postgresql.org>
2025-03-24 [4d143509c] Create accessor functions for TupleHashEntry.
Author: Jeff Davis <jdavis@postgresql.org>
2025-03-24 [a0942f441] Add ExecCopySlotMinimalTupleExtra().
Author: Jeff Davis <jdavis@postgresql.org>
2025-03-24 [626df47ad] Remove 'additional' pointer from TupleHashEntryData.
-->
<listitem>
<para>
Improve the performance and reduce memory usage of hash joins and GROUP BY (David Rowley, Jeff Davis)
<ulink url="&commit_baseurl;adf97c156">&sect;</ulink>
<ulink url="&commit_baseurl;0f5738202">&sect;</ulink>
<ulink url="&commit_baseurl;4d143509c">&sect;</ulink>
<ulink url="&commit_baseurl;a0942f441">&sect;</ulink>
<ulink url="&commit_baseurl;626df47ad">&sect;</ulink>
</para>
<para>
This also improves hash set operations used by EXCEPT, and hash lookups of subplan values.
</para>
</listitem>
<!--
Author: Amit Langote <amitlan@postgresql.org>
2025-04-04 [88f55bc97] Make derived clause lookup in EquivalenceClass more effi
@ -397,33 +420,12 @@ Author: David Rowley <drowley@postgresql.org>
<listitem>
<para>
Allow partitions to be pruned more efficienty (Ashutosh Bapat, Yuya Watari, David Rowley)
Improve the efficiency of planning queries accessing many partitions (Ashutosh Bapat, Yuya Watari, David Rowley)
<ulink url="&commit_baseurl;88f55bc97">&sect;</ulink>
<ulink url="&commit_baseurl;d69d45a5a">&sect;</ulink>
</para>
</listitem>
<!--
Author: Amit Langote <amitlan@postgresql.org>
2025-01-30 [bb3ec16e1] Move PartitionPruneInfo out of plan nodes into PlannedSt
Author: Amit Langote <amitlan@postgresql.org>
2025-01-31 [d47cbf474] Perform runtime initial pruning outside ExecInitNode()
Author: Amit Langote <amitlan@postgresql.org>
2025-02-07 [cbc127917] Track unpruned relids to avoid processing pruned relatio
Author: Amit Langote <amitlan@postgresql.org>
2025-02-20 [525392d57] Don't lock partitions pruned by initial pruning
-->
<listitem>
<para>
Avoid the locking of pruned partitions during planning (Amit Langote)
<ulink url="&commit_baseurl;bb3ec16e1">&sect;</ulink>
<ulink url="&commit_baseurl;d47cbf474">&sect;</ulink>
<ulink url="&commit_baseurl;cbc127917">&sect;</ulink>
<ulink url="&commit_baseurl;525392d57">&sect;</ulink>
</para>
</listitem>
<!--
Author: Richard Guo <rguo@postgresql.org>
2024-07-30 [9b282a935] Fix partitionwise join with partially-redundant join cla
@ -492,8 +494,6 @@ Author: Peter Geoghegan <pg@bowt.ie>
2025-04-04 [92fe23d93] Add nbtree skip scan optimization.
Author: Peter Geoghegan <pg@bowt.ie>
2025-04-04 [8a510275d] Further optimize nbtree search scan key comparisons.
Author: Peter Geoghegan <pg@bowt.ie>
2025-04-04 [8a510275d] Further optimize nbtree search scan key comparisons.
-->
<listitem>
@ -501,11 +501,11 @@ Author: Peter Geoghegan <pg@bowt.ie>
Allow skip scans of btree indexes (Peter Geoghegan)
<ulink url="&commit_baseurl;92fe23d93">&sect;</ulink>
<ulink url="&commit_baseurl;8a510275d">&sect;</ulink>
<ulink url="&commit_baseurl;8a510275d">&sect;</ulink>
</para>
<para>
This is effective if the earlier non-referenced columns contain few unique values.
This allows multi-column btree indexes to be used by queries that only
equality-reference the second or later indexed columns.
</para>
</listitem>
@ -603,6 +603,7 @@ Add an asynchronous I/O subsystem (Andres Freund, Thomas Munro, Nazir Bilal Yavu
</para>
<para>
This feature allows backends to queue multiple read requests, which allows for more efficient sequential scans, bitmap heap scans, vacuums, etc.
This is enabled by server variable io_method, with server variables io_combine_limit and io_max_combine_limit added to control it. This also enables
effective_io_concurrency and maintenance_io_concurrency values greater than zero for systems without fadvise() support. The new system view pg_aios shows the file handles being used
for asynchronous I/O.
@ -621,6 +622,34 @@ Improve the locking performance of queries that access many relations (Tomas Von
</para>
</listitem>
<!--
Author: David Rowley <drowley@postgresql.org>
2024-08-20 [adf97c156] Speed up Hash Join by making ExprStates support hashing
Author: David Rowley <drowley@postgresql.org>
2024-12-11 [0f5738202] Use ExprStates for hashing in GROUP BY and SubPlans
Author: Jeff Davis <jdavis@postgresql.org>
2025-03-24 [4d143509c] Create accessor functions for TupleHashEntry.
Author: Jeff Davis <jdavis@postgresql.org>
2025-03-24 [a0942f441] Add ExecCopySlotMinimalTupleExtra().
Author: Jeff Davis <jdavis@postgresql.org>
2025-03-24 [626df47ad] Remove 'additional' pointer from TupleHashEntryData.
-->
<listitem>
<para>
Improve the performance and reduce memory usage of hash joins and GROUP BY (David Rowley, Jeff Davis)
<ulink url="&commit_baseurl;adf97c156">&sect;</ulink>
<ulink url="&commit_baseurl;0f5738202">&sect;</ulink>
<ulink url="&commit_baseurl;4d143509c">&sect;</ulink>
<ulink url="&commit_baseurl;a0942f441">&sect;</ulink>
<ulink url="&commit_baseurl;626df47ad">&sect;</ulink>
</para>
<para>
This also improves hash set operations used by EXCEPT, and hash lookups of subplan values.
</para>
</listitem>
<!--
Author: Melanie Plageman <melanieplageman@gmail.com>
2025-02-11 [052026c9b] Eagerly scan all-visible pages to amortize aggressive va
@ -688,15 +717,12 @@ This more accurately reflects modern hardware.
<!--
Author: Melanie Plageman <melanieplageman@gmail.com>
2025-03-12 [9219093ca] Modularize log_connections output
Author: Melanie Plageman <melanieplageman@gmail.com>
2025-03-12 [18cd15e70] Add connection establishment duration logging
-->
<listitem>
<para>
Increase the logging granularity of server variable log_connections (Melanie Plageman)
<ulink url="&commit_baseurl;9219093ca">&sect;</ulink>
<ulink url="&commit_baseurl;18cd15e70">&sect;</ulink>
</para>
<para>
@ -704,6 +730,18 @@ This server variable was previously only boolean; these options are still suppo
</para>
</listitem>
<!--
Author: Melanie Plageman <melanieplageman@gmail.com>
2025-03-12 [18cd15e70] Add connection establishment duration logging
-->
<listitem>
<para>
Add log_connections option to report the duration of connection stages (Melanie Plageman)
<ulink url="&commit_baseurl;18cd15e70">&sect;</ulink>
</para>
</listitem>
<!--
Author: Tom Lane <tgl@sss.pgh.pa.us>
2025-04-07 [3516ea768] Add local-address escape "%L" to log_line_prefix.
@ -723,7 +761,7 @@ Author: Fujii Masao <fujii@postgresql.org>
<listitem>
<para>
Add server variable log_lock_failure to log lock acquisition failures (Yuki Seino)
Add server variable log_lock_failures to log lock acquisition failures (Yuki Seino)
<ulink url="&commit_baseurl;6d376c3b0">&sect;</ulink>
</para>
@ -768,6 +806,33 @@ mode; tracking must be enabled with the server variable track_cost_delay_timing.
</para>
</listitem>
<!--
Author: Masahiko Sawada <msawada@postgresql.org>
2024-08-13 [4c1b4cdb8] Add resource statistics reporting to ANALYZE VERBOSE.
Author: Masahiko Sawada <msawada@postgresql.org>
2024-09-09 [bb7775234] Add WAL usage reporting to ANALYZE VERBOSE output.
-->
<listitem>
<para>
Add WAL, CPU, and average read statistics output to ANALYZE VERBOSE (Anthonin Bonnefoy)
<ulink url="&commit_baseurl;4c1b4cdb8">&sect;</ulink>
<ulink url="&commit_baseurl;bb7775234">&sect;</ulink>
</para>
</listitem>
<!--
Author: Michael Paquier <michael@paquier.xyz>
2025-02-17 [6a8a7ce47] Add information about WAL buffers full to VACUUM/ANALYZE
-->
<listitem>
<para>
Add full WAL buffer count to VACUUM/ANALYZE (VERBOSE) and autovacuum log output (Bertrand Drouvot)
<ulink url="&commit_baseurl;6a8a7ce47">&sect;</ulink>
</para>
</listitem>
<!--
Author: Michael Paquier <michael@paquier.xyz>
2024-12-19 [9aea73fc6] Add backend-level statistics to pgstats
@ -968,21 +1033,6 @@ This is true even if the tables in different schemas have different column names
</para>
</listitem>
<!--
Author: Daniel Gustafsson <dgustafsson@postgresql.org>
2025-04-08 [042a66291] Add function to get memory context stats for processes
Author: Daniel Gustafsson <dgustafsson@postgresql.org>
2025-04-08 [c57971034] Rename argument in pg_get_process_memory_contexts().
-->
<listitem>
<para>
Add function pg_get_process_memory_contexts() to report process memory context statistics (Rahila Syed)
<ulink url="&commit_baseurl;042a66291">&sect;</ulink>
<ulink url="&commit_baseurl;c57971034">&sect;</ulink>
</para>
</listitem>
<!--
Author: David Rowley <drowley@postgresql.org>
2024-07-01 [12227a1d5] Add context type field to pg_backend_memory_contexts
@ -1234,12 +1284,15 @@ This is useful for operating system configuration.
<!--
Author: Peter Eisentraut <peter@eisentraut.org>
2025-03-19 [4f7f7b037] extension_control_path
Author: Peter Eisentraut <peter@eisentraut.org>
2025-05-02 [81eaaa2c4] Make "directory" setting work with extension_control_pat
-->
<listitem>
<para>
Add server variable extension_control_path to specify the location of extension control files (Peter Eisentraut, Matheus Alcantara)
<ulink url="&commit_baseurl;4f7f7b037">&sect;</ulink>
<ulink url="&commit_baseurl;81eaaa2c4">&sect;</ulink>
</para>
</listitem>
@ -1323,7 +1376,7 @@ Author: Amit Kapila <akapila@postgresql.org>
<listitem>
<para>
Change the default CREATE SUBSCRIPTION streaming option from "off" to "parallel" (Hayato Kuroda, Masahiko Sawada, Peter Smith, Amit Kapila)
Change the default CREATE SUBSCRIPTION streaming option from "off" to "parallel" (Vignesh C)
<ulink url="&commit_baseurl;1bf1140be">&sect;</ulink>
</para>
</listitem>
@ -1416,9 +1469,9 @@ Add OLD/NEW support to RETURNING in DML queries (Dean Rasheed)
</para>
<para>
Previously RETURNING only returned new values for INSERT and UPDATE, old values for DELETE; MERGE would return the appropriate value for the internal query executed. This new syntax
allows INSERT with an ON CONFLICT action to return old values, UPDATE to return old values, and DELETE to return new values if the query assigned to an ON DELETE row would return new
values. New syntax allows changeable relation aliases "old" and "new" to specify which values should be returned.
Previously RETURNING only returned new values for INSERT and UPDATE, and old values for DELETE; MERGE would return the appropriate value for the internal query executed. This new syntax
allows the RETURNING list of INSERT/UPDATE/DELETE/MERGE to explicitly return old and new values by using the special aliases "old" and "new". These aliases can be renamed to
avoid identifier conflicts.
</para>
</listitem>
@ -1758,33 +1811,15 @@ Automatically include BUFFERS output in EXPLAIN ANALYZE (Guillaume Lelarge, Davi
</para>
</listitem>
<!--
Author: Masahiko Sawada <msawada@postgresql.org>
2024-08-13 [4c1b4cdb8] Add resource statistics reporting to ANALYZE VERBOSE.
Author: Masahiko Sawada <msawada@postgresql.org>
2024-09-09 [bb7775234] Add WAL usage reporting to ANALYZE VERBOSE output.
-->
<listitem>
<para>
Add WAL, CPU, and average read statistics output to EXPLAIN ANALYZE VERBOSE (Anthonin Bonnefoy)
<ulink url="&commit_baseurl;4c1b4cdb8">&sect;</ulink>
<ulink url="&commit_baseurl;bb7775234">&sect;</ulink>
</para>
</listitem>
<!--
Author: Michael Paquier <michael@paquier.xyz>
2025-02-17 [320545bfc] Add information about WAL buffers being full to EXPLAIN
Author: Michael Paquier <michael@paquier.xyz>
2025-02-17 [6a8a7ce47] Add information about WAL buffers full to VACUUM/ANALYZE
-->
<listitem>
<para>
Add full WAL buffer count to EXPLAIN (WAL), VACUUM/ANALYZE (VERBOSE), and autovacuum log output (Bertrand Drouvot)
Add full WAL buffer count to EXPLAIN (WAL) output (Bertrand Drouvot)
<ulink url="&commit_baseurl;320545bfc">&sect;</ulink>
<ulink url="&commit_baseurl;6a8a7ce47">&sect;</ulink>
</para>
</listitem>
@ -1916,7 +1951,7 @@ Author: Tom Lane <tgl@sss.pgh.pa.us>
<listitem>
<para>
Allow jsonb NULL values to be cast to scalar types as NULL (Tom Lane)
Allow jsonb "null" values to be cast to scalar types as NULL (Tom Lane)
<ulink url="&commit_baseurl;a5579a90a">&sect;</ulink>
</para>
@ -2132,7 +2167,7 @@ Author: Nathan Bossart <nathan@postgresql.org>
<listitem>
<para>
Add functions crc32() and crc32c to compute CRC values (Aleksander Alekseev)
Add functions crc32() and crc32c() to compute CRC values (Aleksander Alekseev)
<ulink url="&commit_baseurl;760162fed">&sect;</ulink>
</para>
</listitem>
@ -2596,24 +2631,23 @@ Author: Jeff Davis <jdavis@postgresql.org>
<listitem>
<para>
Add pg_dump options --with-schema, --with-data, and --with_statistics (Jeff Davis)
Add pg_dump options --with-schema, --with-data, and --with-statistics (Jeff Davis)
<ulink url="&commit_baseurl;bde2fb797">&sect;</ulink>
</para>
<para>
The negative versions of these options already existed.
</para>
</listitem>
<!--
Author: Nathan Bossart <nathan@postgresql.org>
2025-03-25 [9c49f0e8c] pg_dump: Add - -sequence-data.
Author: Nathan Bossart <nathan@postgresql.org>
2025-05-07 [acea3fc49] pg_dumpall: Add - -sequence-data.
-->
<listitem>
<para>
Add pg_dump option --sequence-data to dump sequence data that would normally be excluded (Nathan Bossart)
Add pg_dump and pg_dumpall option --sequence-data to dump sequence data that would normally be excluded (Nathan Bossart)
<ulink url="&commit_baseurl;9c49f0e8c">&sect;</ulink>
<ulink url="&commit_baseurl;acea3fc49">&sect;</ulink>
</para>
</listitem>
@ -2636,7 +2670,7 @@ Author: Tom Lane <tgl@sss.pgh.pa.us>
<listitem>
<para>
Add option --no-policies to pg_dump, pg_dumpall, pg_restore to avoid policy specification (Nikolay Samokhvalov)
Add option --no-policies to disable row level security policy processing in pg_dump, pg_dumpall, pg_restore (Nikolay Samokhvalov)
<ulink url="&commit_baseurl;cd3c45125">&sect;</ulink>
</para>
@ -2765,7 +2799,7 @@ This is to handle cases where a pre-Postgres 18 cluster's default CPU signedness
</sect4>
<sect4 id="release-18-logicalrep-app">
<title>Logical Replication Applications></title>
<title>Logical Replication Applications</title>
<itemizedlist>
@ -2859,6 +2893,18 @@ Injection points can now be created, but not run, via INJECTION_POINT_LOAD(), an
</para>
</listitem>
<!--
Author: Michael Paquier <michael@paquier.xyz>
2025-05-10 [371f2db8b] Add support for runtime arguments in injection points
-->
<listitem>
<para>
Support runtime arguments in injection points (Michael Paquier)
<ulink url="&commit_baseurl;371f2db8b">&sect;</ulink>
</para>
</listitem>
<!--
Author: Heikki Linnakangas <heikki.linnakangas@iki.fi>
2024-07-26 [20e0e7da9] Add test for early backend startup errors
@ -2912,13 +2958,22 @@ Add ARM Neon and SVE CPU intrinsics for popcount (integer bit counting) (Chiranm
<!--
Author: Dean Rasheed <dean.a.rasheed@gmail.com>
2024-07-09 [ca481d3c9] Optimise numeric multiplication for short inputs.
Author: Dean Rasheed <dean.a.rasheed@gmail.com>
2024-08-15 [c4e44224c] Extend mul_var_short() to 5 and 6-digit inputs.
Author: Dean Rasheed <dean.a.rasheed@gmail.com>
2024-08-15 [8dc28d7eb] Optimise numeric multiplication using base-NBASE^2 arith
Author: Dean Rasheed <dean.a.rasheed@gmail.com>
2024-10-04 [9428c001f] Speed up numeric division by always using the "fast" alg
-->
<listitem>
<para>
Improve the speed of multiplication (Joel Jacobson, Dean Rasheed)
Improve the speed of numeric multiplication and division (Joel Jacobson, Dean Rasheed)
<ulink url="&commit_baseurl;ca481d3c9">&sect;</ulink>
<ulink url="&commit_baseurl;c4e44224c">&sect;</ulink>
<ulink url="&commit_baseurl;8dc28d7eb">&sect;</ulink>
<ulink url="&commit_baseurl;9428c001f">&sect;</ulink>
</para>
</listitem>
@ -3308,7 +3363,7 @@ Author: Tatsuo Ishii <ishii@postgresql.org>
<listitem>
<para>
Have pgbench report the number of failed transactions (Yugo Nagata)
Have pgbench report the number of failed, retried, or skipped transactions in per-script reports (Yugo Nagata)
<ulink url="&commit_baseurl;cae0f3c40">&sect;</ulink>
</para>
</listitem>

View File

@ -3932,7 +3932,7 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
<structfield>passwd</structfield> <type>text</type>
</para>
<para>
Password (possibly encrypted); null if none. See
Encrypted password; null if none. See
<link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>
for details of how encrypted passwords are stored.
</para></entry>

View File

@ -129,10 +129,9 @@
In all cases, a trigger is executed as part of the same transaction as
the statement that triggered it, so if either the statement or the
trigger causes an error, the effects of both will be rolled back.
Also, the trigger will always run in the security context of the role
that executed the statement that caused the trigger to fire, unless
the trigger function is defined as <literal>SECURITY DEFINER</literal>,
in which case it will run as the function owner.
Also, the trigger will always run as the role that queued the trigger
event, unless the trigger function is marked as <literal>SECURITY
DEFINER</literal>, in which case it will run as the function owner.
</para>
<para>

View File

@ -3829,15 +3829,17 @@ uint32 WaitEventExtensionNew(const char *wait_event_name)
An injection point with a given <literal>name</literal> is declared using
macro:
<programlisting>
INJECTION_POINT(name);
INJECTION_POINT(name, arg);
</programlisting>
There are a few injection points already declared at strategic points
within the server code. After adding a new injection point the code needs
to be compiled in order for that injection point to be available in the
binary. Add-ins written in C-language can declare injection points in
their own code using the same macro. The injection point names should
use lower-case characters, with terms separated by dashes.
their own code using the same macro. The injection point names should use
lower-case characters, with terms separated by
dashes. <literal>arg</literal> is an optional argument value given to the
callback at run-time.
</para>
<para>
@ -3847,7 +3849,7 @@ INJECTION_POINT(name);
a two-step approach with the following macros:
<programlisting>
INJECTION_POINT_LOAD(name);
INJECTION_POINT_CACHED(name);
INJECTION_POINT_CACHED(name, arg);
</programlisting>
Before entering the critical section,
@ -3880,7 +3882,9 @@ extern void InjectionPointAttach(const char *name,
<literal>InjectionPointCallback</literal>:
<programlisting>
static void
custom_injection_callback(const char *name, const void *private_data)
custom_injection_callback(const char *name,
const void *private_data,
void *arg)
{
uint32 wait_event_info = WaitEventInjectionPointNew(name);
@ -3909,7 +3913,7 @@ if (IS_INJECTION_POINT_ATTACHED("before-foobar"))
local_var = 123;
/* also execute the callback */
INJECTION_POINT_CACHED("before-foobar");
INJECTION_POINT_CACHED("before-foobar", NULL);
}
#endif
</programlisting>

View File

@ -598,7 +598,7 @@
<entry>11</entry>
</row>
<row>
<entry><function>stratnum</function></entry>
<entry><function>translate_cmptype</function></entry>
<entry>translate compare types to strategy numbers
used by the operator class (optional)</entry>
<entry>12</entry>

View File

@ -2654,6 +2654,7 @@ decl_checks += [
['preadv', 'sys/uio.h'],
['pwritev', 'sys/uio.h'],
['strchrnul', 'string.h'],
['memset_s', 'string.h', '#define __STDC_WANT_LIB_EXT1__ 1'],
]
# Check presence of some optional LLVM functions.
@ -2667,21 +2668,23 @@ endif
foreach c : decl_checks
func = c.get(0)
header = c.get(1)
args = c.get(2, {})
prologue = c.get(2, '')
args = c.get(3, {})
varname = 'HAVE_DECL_' + func.underscorify().to_upper()
found = cc.compiles('''
#include <@0@>
@0@
#include <@1@>
int main()
{
#ifndef @1@
(void) @1@;
#ifndef @2@
(void) @2@;
#endif
return 0;
}
'''.format(header, func),
'''.format(prologue, header, func),
name: 'test whether @0@ is declared'.format(func),
# need to add cflags_warn to get at least
# -Werror=unguarded-availability-new if applicable
@ -2880,7 +2883,6 @@ func_checks = [
['kqueue'],
['localeconv_l'],
['mbstowcs_l'],
['memset_s'],
['mkdtemp'],
['posix_fadvise'],
['posix_fallocate'],

View File

@ -112,7 +112,7 @@ ifeq ($(PORTNAME), darwin)
ifneq ($(SO_MAJOR_VERSION), 0)
version_link = -compatibility_version $(SO_MAJOR_VERSION) -current_version $(SO_MAJOR_VERSION).$(SO_MINOR_VERSION)
endif
LINK.shared = $(COMPILER) -dynamiclib -install_name '$(libdir)/lib$(NAME).$(SO_MAJOR_VERSION)$(DLSUFFIX)' $(version_link) $(exported_symbols_list)
LINK.shared = $(COMPILER) -dynamiclib -install_name '$(libdir)/lib$(NAME).$(SO_MAJOR_VERSION)$(DLSUFFIX)' $(version_link)
shlib = lib$(NAME).$(SO_MAJOR_VERSION)$(DLSUFFIX)
shlib_major = lib$(NAME).$(SO_MAJOR_VERSION)$(DLSUFFIX)
else
@ -122,7 +122,7 @@ ifeq ($(PORTNAME), darwin)
BUILD.exports = $(AWK) '/^[^\#]/ {printf "_%s\n",$$1}' $< >$@
exports_file = $(SHLIB_EXPORTS:%.txt=%.list)
ifneq (,$(exports_file))
exported_symbols_list = -exported_symbols_list $(exports_file)
LINK.shared += -exported_symbols_list $(exports_file)
endif
endif

View File

@ -68,7 +68,7 @@ typedef struct BrinShared
int scantuplesortstates;
/* Query ID, for report in worker processes */
uint64 queryid;
int64 queryid;
/*
* workersdonecv is used to monitor the progress of workers. All parallel

View File

@ -1243,8 +1243,9 @@ transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
}
else
{
text *t;
const char *name;
const char *value;
text *t;
Size len;
/*
@ -1291,11 +1292,19 @@ transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
* have just "name", assume "name=true" is meant. Note: the
* namespace is not output.
*/
name = def->defname;
if (def->arg != NULL)
value = defGetString(def);
else
value = "true";
/* Insist that name not contain "=", else "a=b=c" is ambiguous */
if (strchr(name, '=') != NULL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid option name \"%s\": must not contain \"=\"",
name)));
/*
* This is not a great place for this test, but there's no other
* convenient place to filter the option out. As WITH (oids =
@ -1303,7 +1312,7 @@ transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
* amount of ugly.
*/
if (acceptOidsOff && def->defnamespace == NULL &&
strcmp(def->defname, "oids") == 0)
strcmp(name, "oids") == 0)
{
if (defGetBoolean(def))
ereport(ERROR,
@ -1313,11 +1322,11 @@ transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
continue;
}
len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
len = VARHDRSZ + strlen(name) + 1 + strlen(value);
/* +1 leaves room for sprintf's trailing null */
t = (text *) palloc(len + 1);
SET_VARSIZE(t, len);
sprintf(VARDATA(t), "%s=%s", def->defname, value);
sprintf(VARDATA(t), "%s=%s", name, value);
astate = accumArrayResult(astate, PointerGetDatum(t),
false, TEXTOID,

View File

@ -685,9 +685,9 @@ ginFinishSplit(GinBtree btree, GinBtreeStack *stack, bool freestack,
#ifdef USE_INJECTION_POINTS
if (GinPageIsLeaf(BufferGetPage(stack->buffer)))
INJECTION_POINT("gin-leave-leaf-split-incomplete");
INJECTION_POINT("gin-leave-leaf-split-incomplete", NULL);
else
INJECTION_POINT("gin-leave-internal-split-incomplete");
INJECTION_POINT("gin-leave-internal-split-incomplete", NULL);
#endif
/* search parent to lock */
@ -778,7 +778,7 @@ ginFinishSplit(GinBtree btree, GinBtreeStack *stack, bool freestack,
static void
ginFinishOldSplit(GinBtree btree, GinBtreeStack *stack, GinStatsData *buildStats, int access)
{
INJECTION_POINT("gin-finish-incomplete-split");
INJECTION_POINT("gin-finish-incomplete-split", NULL);
elog(DEBUG1, "finishing incomplete split of block %u in gin index \"%s\"",
stack->blkno, RelationGetRelationName(btree->index));

View File

@ -1058,11 +1058,11 @@ gistGetFakeLSN(Relation rel)
}
/*
* This is a stratnum support function for GiST opclasses that use the
* RT*StrategyNumber constants.
* This is a stratnum translation support function for GiST opclasses that use
* the RT*StrategyNumber constants.
*/
Datum
gist_stratnum_common(PG_FUNCTION_ARGS)
gist_translate_cmptype_common(PG_FUNCTION_ARGS)
{
CompareType cmptype = PG_GETARG_INT32(0);
@ -1090,9 +1090,9 @@ gist_stratnum_common(PG_FUNCTION_ARGS)
/*
* Returns the opclass's private stratnum used for the given compare type.
*
* Calls the opclass's GIST_STRATNUM_PROC support function, if any,
* and returns the result.
* Returns InvalidStrategy if the function is not defined.
* Calls the opclass's GIST_TRANSLATE_CMPTYPE_PROC support function, if any,
* and returns the result. Returns InvalidStrategy if the function is not
* defined.
*/
StrategyNumber
gisttranslatecmptype(CompareType cmptype, Oid opfamily)
@ -1101,7 +1101,7 @@ gisttranslatecmptype(CompareType cmptype, Oid opfamily)
Datum result;
/* Check whether the function is provided. */
funcid = get_opfamily_proc(opfamily, ANYOID, ANYOID, GIST_STRATNUM_PROC);
funcid = get_opfamily_proc(opfamily, ANYOID, ANYOID, GIST_TRANSLATE_CMPTYPE_PROC);
if (!OidIsValid(funcid))
return InvalidStrategy;

View File

@ -138,7 +138,7 @@ gistvalidate(Oid opclassoid)
ok = check_amproc_signature(procform->amproc, VOIDOID, true,
1, 1, INTERNALOID);
break;
case GIST_STRATNUM_PROC:
case GIST_TRANSLATE_CMPTYPE_PROC:
ok = check_amproc_signature(procform->amproc, INT2OID, true,
1, 1, INT4OID) &&
procform->amproclefttype == ANYOID &&
@ -265,7 +265,7 @@ gistvalidate(Oid opclassoid)
if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC ||
i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC ||
i == GIST_OPTIONS_PROC || i == GIST_SORTSUPPORT_PROC ||
i == GIST_STRATNUM_PROC)
i == GIST_TRANSLATE_CMPTYPE_PROC)
continue; /* optional methods */
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@ -336,7 +336,7 @@ gistadjustmembers(Oid opfamilyoid,
case GIST_FETCH_PROC:
case GIST_OPTIONS_PROC:
case GIST_SORTSUPPORT_PROC:
case GIST_STRATNUM_PROC:
case GIST_TRANSLATE_CMPTYPE_PROC:
/* Optional, so force it to be a soft family dependency */
op->ref_is_hard = false;
op->ref_is_family = true;

View File

@ -213,6 +213,27 @@ static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
#define TUPLOCK_from_mxstatus(status) \
(MultiXactStatusLock[(status)])
/*
* Check that we have a valid snapshot if we might need TOAST access.
*/
static inline void
AssertHasSnapshotForToast(Relation rel)
{
#ifdef USE_ASSERT_CHECKING
/* bootstrap mode in particular breaks this rule */
if (!IsNormalProcessingMode())
return;
/* if the relation doesn't have a TOAST table, we are good */
if (!OidIsValid(rel->rd_rel->reltoastrelid))
return;
Assert(HaveRegisteredOrActiveSnapshot());
#endif /* USE_ASSERT_CHECKING */
}
/* ----------------------------------------------------------------
* heap support routines
* ----------------------------------------------------------------
@ -2066,6 +2087,8 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
Assert(HeapTupleHeaderGetNatts(tup->t_data) <=
RelationGetNumberOfAttributes(relation));
AssertHasSnapshotForToast(relation);
/*
* Fill in tuple header fields and toast the tuple if necessary.
*
@ -2343,6 +2366,8 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
/* currently not needed (thus unsupported) for heap_multi_insert() */
Assert(!(options & HEAP_INSERT_NO_LOGICAL));
AssertHasSnapshotForToast(relation);
needwal = RelationNeedsWAL(relation);
saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
HEAP_DEFAULT_FILLFACTOR);
@ -2765,6 +2790,8 @@ heap_delete(Relation relation, ItemPointer tid,
Assert(ItemPointerIsValid(tid));
AssertHasSnapshotForToast(relation);
/*
* Forbid this during a parallel operation, lest it allocate a combo CID.
* Other workers might need that combo CID for visibility checks, and we
@ -3260,6 +3287,8 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
Assert(HeapTupleHeaderGetNatts(newtup->t_data) <=
RelationGetNumberOfAttributes(relation));
AssertHasSnapshotForToast(relation);
/*
* Forbid this during a parallel operation, lest it allocate a combo CID.
* Other workers might need that combo CID for visibility checks, and we
@ -3304,7 +3333,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
interesting_attrs = bms_add_members(interesting_attrs, id_attrs);
block = ItemPointerGetBlockNumber(otid);
INJECTION_POINT("heap_update-before-pin");
INJECTION_POINT("heap_update-before-pin", NULL);
buffer = ReadBuffer(relation, block);
page = BufferGetPage(buffer);
@ -4953,7 +4982,7 @@ l3:
case LockWaitError:
if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
status, infomask, relation,
NULL, log_lock_failure))
NULL, log_lock_failures))
ereport(ERROR,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("could not obtain lock on row in relation \"%s\"",
@ -4991,7 +5020,7 @@ l3:
}
break;
case LockWaitError:
if (!ConditionalXactLockTableWait(xwait, log_lock_failure))
if (!ConditionalXactLockTableWait(xwait, log_lock_failures))
ereport(ERROR,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("could not obtain lock on row in relation \"%s\"",
@ -5256,7 +5285,7 @@ heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode,
break;
case LockWaitError:
if (!ConditionalLockTupleTuplock(relation, tid, mode, log_lock_failure))
if (!ConditionalLockTupleTuplock(relation, tid, mode, log_lock_failures))
ereport(ERROR,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("could not obtain lock on row in relation \"%s\"",

View File

@ -464,7 +464,7 @@ tuple_lock_retry:
return TM_WouldBlock;
break;
case LockWaitError:
if (!ConditionalXactLockTableWait(SnapshotDirty.xmax, log_lock_failure))
if (!ConditionalXactLockTableWait(SnapshotDirty.xmax, log_lock_failures))
ereport(ERROR,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("could not obtain lock on row in relation \"%s\"",

View File

@ -757,7 +757,6 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
vacrel->vm_new_visible_pages = 0;
vacrel->vm_new_visible_frozen_pages = 0;
vacrel->vm_new_frozen_pages = 0;
vacrel->rel_pages = orig_rel_pages = RelationGetNumberOfBlocks(rel);
/*
* Get cutoffs that determine which deleted tuples are considered DEAD,
@ -776,7 +775,9 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
* to increase the number of dead tuples it can prune away.)
*/
vacrel->aggressive = vacuum_get_cutoffs(rel, params, &vacrel->cutoffs);
vacrel->rel_pages = orig_rel_pages = RelationGetNumberOfBlocks(rel);
vacrel->vistest = GlobalVisTestFor(rel);
/* Initialize state used to track oldest extant XID/MXID */
vacrel->NewRelfrozenXid = vacrel->cutoffs.OldestXmin;
vacrel->NewRelminMxid = vacrel->cutoffs.OldestMxact;
@ -1413,11 +1414,25 @@ lazy_scan_heap(LVRelState *vacrel)
if (vm_page_frozen)
{
Assert(vacrel->eager_scan_remaining_successes > 0);
vacrel->eager_scan_remaining_successes--;
if (vacrel->eager_scan_remaining_successes > 0)
vacrel->eager_scan_remaining_successes--;
if (vacrel->eager_scan_remaining_successes == 0)
{
/*
* Report only once that we disabled eager scanning. We
* may eagerly read ahead blocks in excess of the success
* or failure caps before attempting to freeze them, so we
* could reach here even after disabling additional eager
* scanning.
*/
if (vacrel->eager_scan_max_fails_per_region > 0)
ereport(vacrel->verbose ? INFO : DEBUG2,
(errmsg("disabling eager scanning after freezing %u eagerly scanned blocks of \"%s.%s.%s\"",
orig_eager_scan_success_limit,
vacrel->dbname, vacrel->relnamespace,
vacrel->relname)));
/*
* If we hit our success cap, permanently disable eager
* scanning by setting the other eager scan management
@ -1426,19 +1441,10 @@ lazy_scan_heap(LVRelState *vacrel)
vacrel->eager_scan_remaining_fails = 0;
vacrel->next_eager_scan_region_start = InvalidBlockNumber;
vacrel->eager_scan_max_fails_per_region = 0;
ereport(vacrel->verbose ? INFO : DEBUG2,
(errmsg("disabling eager scanning after freezing %u eagerly scanned blocks of \"%s.%s.%s\"",
orig_eager_scan_success_limit,
vacrel->dbname, vacrel->relnamespace,
vacrel->relname)));
}
}
else
{
Assert(vacrel->eager_scan_remaining_fails > 0);
else if (vacrel->eager_scan_remaining_fails > 0)
vacrel->eager_scan_remaining_fails--;
}
}
/*

View File

@ -851,7 +851,7 @@ systable_inplace_update_begin(Relation relation,
if (retries++ > 10000)
elog(ERROR, "giving up after too many tries to overwrite row");
INJECTION_POINT("inplace-before-pin");
INJECTION_POINT("inplace-before-pin", NULL);
scan = systable_beginscan(relation, indexId, indexOK, snapshot,
nkeys, unconstify(ScanKeyData *, key));
oldtup = systable_getnext(scan);

View File

@ -228,6 +228,8 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
BTScanOpaque so = (BTScanOpaque) scan->opaque;
bool res;
Assert(scan->heapRelation != NULL);
/* btree indexes are never lossy */
scan->xs_recheck = false;
@ -289,6 +291,8 @@ btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
int64 ntids = 0;
ItemPointer heapTid;
Assert(scan->heapRelation == NULL);
/* Each loop iteration performs another primitive index scan */
do
{
@ -393,6 +397,34 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
BTScanPosInvalidate(so->currPos);
}
/*
* We prefer to eagerly drop leaf page pins before btgettuple returns.
* This avoids making VACUUM wait to acquire a cleanup lock on the page.
*
* We cannot safely drop leaf page pins during index-only scans due to a
* race condition involving VACUUM setting pages all-visible in the VM.
* It's also unsafe for plain index scans that use a non-MVCC snapshot.
*
* When we drop pins eagerly, the mechanism that marks so->killedItems[]
* index tuples LP_DEAD has to deal with concurrent TID recycling races.
* The scheme used to detect unsafe TID recycling won't work when scanning
* unlogged relations (since it involves saving an affected page's LSN).
* Opt out of eager pin dropping during unlogged relation scans for now
* (this is preferable to opting out of kill_prior_tuple LP_DEAD setting).
*
* Also opt out of dropping leaf page pins eagerly during bitmap scans.
* Pins cannot be held for more than an instant during bitmap scans either
* way, so we might as well avoid wasting cycles on acquiring page LSNs.
*
* See nbtree/README section on making concurrent TID recycling safe.
*
* Note: so->dropPin should never change across rescans.
*/
so->dropPin = (!scan->xs_want_itup &&
IsMVCCSnapshot(scan->xs_snapshot) &&
RelationNeedsWAL(scan->indexRelation) &&
scan->heapRelation != NULL);
so->markItemIndex = -1;
so->needPrimScan = false;
so->scanBehind = false;

View File

@ -25,7 +25,7 @@
#include "utils/rel.h"
static void _bt_drop_lock_and_maybe_pin(IndexScanDesc scan, BTScanPos sp);
static inline void _bt_drop_lock_and_maybe_pin(Relation rel, BTScanOpaque so);
static Buffer _bt_moveright(Relation rel, Relation heaprel, BTScanInsert key,
Buffer buf, bool forupdate, BTStack stack,
int access);
@ -57,24 +57,29 @@ static bool _bt_endpoint(IndexScanDesc scan, ScanDirection dir);
/*
* _bt_drop_lock_and_maybe_pin()
*
* Unlock the buffer; and if it is safe to release the pin, do that, too.
* This will prevent vacuum from stalling in a blocked state trying to read a
* page when a cursor is sitting on it.
*
* See nbtree/README section on making concurrent TID recycling safe.
* Unlock so->currPos.buf. If scan is so->dropPin, drop the pin, too.
* Dropping the pin prevents VACUUM from blocking on acquiring a cleanup lock.
*/
static void
_bt_drop_lock_and_maybe_pin(IndexScanDesc scan, BTScanPos sp)
static inline void
_bt_drop_lock_and_maybe_pin(Relation rel, BTScanOpaque so)
{
_bt_unlockbuf(scan->indexRelation, sp->buf);
if (IsMVCCSnapshot(scan->xs_snapshot) &&
RelationNeedsWAL(scan->indexRelation) &&
!scan->xs_want_itup)
if (!so->dropPin)
{
ReleaseBuffer(sp->buf);
sp->buf = InvalidBuffer;
/* Just drop the lock (not the pin) */
_bt_unlockbuf(rel, so->currPos.buf);
return;
}
/*
* Drop both the lock and the pin.
*
* Have to set so->currPos.lsn so that _bt_killitems has a way to detect
* when concurrent heap TID recycling by VACUUM might have taken place.
*/
Assert(RelationNeedsWAL(rel));
so->currPos.lsn = BufferGetLSNAtomic(so->currPos.buf);
_bt_relbuf(rel, so->currPos.buf);
so->currPos.buf = InvalidBuffer;
}
/*
@ -866,8 +871,8 @@ _bt_compare(Relation rel,
* if backwards scan, the last item) in the tree that satisfies the
* qualifications in the scan key. On success exit, data about the
* matching tuple(s) on the page has been loaded into so->currPos. We'll
* drop all locks and hold onto a pin on page's buffer, except when
* _bt_drop_lock_and_maybe_pin dropped the pin to avoid blocking VACUUM.
* drop all locks and hold onto a pin on page's buffer, except during
* so->dropPin scans, when we drop both the lock and the pin.
* _bt_returnitem sets the next item to return to scan on success exit.
*
* If there are no matching items in the index, we return false, with no
@ -1610,7 +1615,13 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum,
so->currPos.currPage = BufferGetBlockNumber(so->currPos.buf);
so->currPos.prevPage = opaque->btpo_prev;
so->currPos.nextPage = opaque->btpo_next;
/* delay setting so->currPos.lsn until _bt_drop_lock_and_maybe_pin */
so->currPos.dir = dir;
so->currPos.nextTupleOffset = 0;
/* either moreRight or moreLeft should be set now (may be unset later) */
Assert(ScanDirectionIsForward(dir) ? so->currPos.moreRight :
so->currPos.moreLeft);
Assert(!P_IGNORE(opaque));
Assert(BTScanPosIsPinned(so->currPos));
Assert(!so->needPrimScan);
@ -1626,14 +1637,6 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum,
so->currPos.currPage);
}
/* initialize remaining currPos fields related to current page */
so->currPos.lsn = BufferGetLSNAtomic(so->currPos.buf);
so->currPos.dir = dir;
so->currPos.nextTupleOffset = 0;
/* either moreLeft or moreRight should be set now (may be unset later) */
Assert(ScanDirectionIsForward(dir) ? so->currPos.moreRight :
so->currPos.moreLeft);
PredicateLockPage(rel, so->currPos.currPage, scan->xs_snapshot);
/* initialize local variables */
@ -1790,9 +1793,13 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum,
IndexTuple itup = (IndexTuple) PageGetItem(page, iid);
int truncatt;
truncatt = BTreeTupleGetNAtts(itup, rel);
/* Reset arrays, per _bt_set_startikey contract */
if (pstate.forcenonrequired)
_bt_start_array_keys(scan, dir);
pstate.forcenonrequired = false;
pstate.startikey = 0; /* _bt_set_startikey ignores P_HIKEY */
truncatt = BTreeTupleGetNAtts(itup, rel);
_bt_checkkeys(scan, &pstate, arrayKeys, itup, truncatt);
}
@ -1879,8 +1886,10 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum,
pstate.offnum = offnum;
if (arrayKeys && offnum == minoff && pstate.forcenonrequired)
{
/* Reset arrays, per _bt_set_startikey contract */
pstate.forcenonrequired = false;
pstate.startikey = 0;
_bt_start_array_keys(scan, dir);
}
passes_quals = _bt_checkkeys(scan, &pstate, arrayKeys,
itup, indnatts);
@ -2101,10 +2110,9 @@ _bt_returnitem(IndexScanDesc scan, BTScanOpaque so)
*
* Wrapper on _bt_readnextpage that performs final steps for the current page.
*
* On entry, if so->currPos.buf is valid the buffer is pinned but not locked.
* If there's no pin held, it's because _bt_drop_lock_and_maybe_pin dropped
* the pin eagerly earlier on. The scan must have so->currPos.currPage set to
* a valid block, in any case.
* On entry, so->currPos must be valid. Its buffer will be pinned, though
* never locked. (Actually, when so->dropPin there won't even be a pin held,
* though so->currPos.currPage must still be set to a valid block number.)
*/
static bool
_bt_steppage(IndexScanDesc scan, ScanDirection dir)
@ -2245,12 +2253,14 @@ _bt_readfirstpage(IndexScanDesc scan, OffsetNumber offnum, ScanDirection dir)
*/
if (_bt_readpage(scan, dir, offnum, true))
{
Relation rel = scan->indexRelation;
/*
* _bt_readpage succeeded. Drop the lock (and maybe the pin) on
* so->currPos.buf in preparation for btgettuple returning tuples.
*/
Assert(BTScanPosIsPinned(so->currPos));
_bt_drop_lock_and_maybe_pin(scan, &so->currPos);
_bt_drop_lock_and_maybe_pin(rel, so);
return true;
}
@ -2288,8 +2298,8 @@ _bt_readfirstpage(IndexScanDesc scan, OffsetNumber offnum, ScanDirection dir)
*
* On success exit, so->currPos is updated to contain data from the next
* interesting page, and we return true. We hold a pin on the buffer on
* success exit, except when _bt_drop_lock_and_maybe_pin decided it was safe
* to eagerly drop the pin (to avoid blocking VACUUM).
* success exit (except during so->dropPin index scans, when we drop the pin
* eagerly to avoid blocking VACUUM).
*
* If there are no more matching records in the given direction, we drop all
* locks and pins, invalidate so->currPos, and return false.
@ -2407,7 +2417,7 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno,
*/
Assert(so->currPos.currPage == blkno);
Assert(BTScanPosIsPinned(so->currPos));
_bt_drop_lock_and_maybe_pin(scan, &so->currPos);
_bt_drop_lock_and_maybe_pin(rel, so);
return true;
}

View File

@ -105,7 +105,7 @@ typedef struct BTShared
int scantuplesortstates;
/* Query ID, for report in worker processes */
uint64 queryid;
int64 queryid;
/*
* workersdonecv is used to monitor the progress of workers. All parallel

View File

@ -2393,11 +2393,27 @@ _bt_scanbehind_checkkeys(IndexScanDesc scan, ScanDirection dir,
TupleDesc tupdesc = RelationGetDescr(rel);
BTScanOpaque so = (BTScanOpaque) scan->opaque;
int nfinaltupatts = BTreeTupleGetNAtts(finaltup, rel);
bool scanBehind;
Assert(so->numArrayKeys);
if (_bt_tuple_before_array_skeys(scan, dir, finaltup, tupdesc,
nfinaltupatts, false, 0, NULL))
nfinaltupatts, false, 0, &scanBehind))
return false;
/*
* If scanBehind was set, all of the untruncated attribute values from
* finaltup that correspond to an array match the array's current element,
* but there are other keys associated with truncated suffix attributes.
* Array advancement must have incremented the scan's arrays on the
* previous page, resulting in a set of array keys that happen to be an
* exact match for the current page high key's untruncated prefix values.
*
* This page definitely doesn't contain tuples that the scan will need to
* return. The next page may or may not contain relevant tuples. Handle
* this by cutting our losses and starting a new primscan.
*/
if (scanBehind)
return false;
if (!so->oppositeDirCheck)
@ -2473,13 +2489,14 @@ _bt_oppodir_checkkeys(IndexScanDesc scan, ScanDirection dir,
* primscan's first page would mislead _bt_advance_array_keys, which expects
* pstate.nskipadvances to be representative of every first page's key space.)
*
* Caller must reset startikey and forcenonrequired ahead of the _bt_checkkeys
* call for pstate.finaltup iff we set forcenonrequired=true. This will give
* _bt_checkkeys the opportunity to call _bt_advance_array_keys once more,
* with sktrig_required=true, to advance the arrays that were ignored during
* checks of all of the page's prior tuples. Caller doesn't need to do this
* on the rightmost/leftmost page in the index (where pstate.finaltup isn't
* set), since forcenonrequired won't be set here by us in the first place.
* Caller must call _bt_start_array_keys and reset startikey/forcenonrequired
* ahead of the finaltup _bt_checkkeys call when we set forcenonrequired=true.
* This will give _bt_checkkeys the opportunity to call _bt_advance_array_keys
* with sktrig_required=true, restoring the invariant that the scan's required
* arrays always track the scan's progress through the index's key space.
* Caller won't need to do this on the rightmost/leftmost page in the index
* (where pstate.finaltup isn't ever set), since forcenonrequired will never
* be set here in the first place.
*/
void
_bt_set_startikey(IndexScanDesc scan, BTReadPageState *pstate)
@ -2540,10 +2557,31 @@ _bt_set_startikey(IndexScanDesc scan, BTReadPageState *pstate)
if (key->sk_flags & SK_ROW_HEADER)
{
/*
* Can't let pstate.startikey get set to an ikey beyond a
* RowCompare inequality
* RowCompare inequality.
*
* Only the first subkey from a RowCompare can ever be marked
* required (that happens when the row header is marked required).
* There is no simple, general way for us to transitively deduce
* whether or not every tuple on the page satisfies a RowCompare
* key based only on firsttup and lasttup -- so we just give up.
*/
break; /* unsafe */
if (!start_past_saop_eq && !so->skipScan)
break; /* unsafe to go further */
/*
* We have to be even more careful with RowCompares that come
* after an array: we assume it's unsafe to even bypass the array.
* Calling _bt_start_array_keys to recover the scan's arrays
* following use of forcenonrequired mode isn't compatible with
* _bt_check_rowcompare's continuescan=false behavior with NULL
* row compare members. _bt_advance_array_keys must not make a
* decision on the basis of a key not being satisfied in the
* opposite-to-scan direction until the scan reaches a leaf page
* where the same key begins to be satisfied in scan direction.
* The _bt_first !used_all_subkeys behavior makes this limitation
* hard to work around some other way.
*/
return; /* completely unsafe to set pstate.startikey */
}
if (key->sk_strategy != BTEqualStrategyNumber)
{
@ -3292,87 +3330,85 @@ _bt_checkkeys_look_ahead(IndexScanDesc scan, BTReadPageState *pstate,
* current page and killed tuples thereon (generally, this should only be
* called if so->numKilled > 0).
*
* The caller does not have a lock on the page and may or may not have the
* page pinned in a buffer. Note that read-lock is sufficient for setting
* LP_DEAD status (which is only a hint).
* Caller should not have a lock on the so->currPos page, but must hold a
* buffer pin when !so->dropPin. When we return, it still won't be locked.
* It'll continue to hold whatever pins were held before calling here.
*
* We match items by heap TID before assuming they are the right ones to
* delete. We cope with cases where items have moved right due to insertions.
* If an item has moved off the current page due to a split, we'll fail to
* find it and do nothing (this is not an error case --- we assume the item
* will eventually get marked in a future indexscan).
* We match items by heap TID before assuming they are the right ones to set
* LP_DEAD. If the scan is one that holds a buffer pin on the target page
* continuously from initially reading the items until applying this function
* (if it is a !so->dropPin scan), VACUUM cannot have deleted any items on the
* page, so the page's TIDs can't have been recycled by now. There's no risk
* that we'll confuse a new index tuple that happens to use a recycled TID
* with a now-removed tuple with the same TID (that used to be on this same
* page). We can't rely on that during scans that drop buffer pins eagerly
* (so->dropPin scans), though, so we must condition setting LP_DEAD bits on
* the page LSN having not changed since back when _bt_readpage saw the page.
* We totally give up on setting LP_DEAD bits when the page LSN changed.
*
* Note that if we hold a pin on the target page continuously from initially
* reading the items until applying this function, VACUUM cannot have deleted
* any items from the page, and so there is no need to search left from the
* recorded offset. (This observation also guarantees that the item is still
* the right one to delete, which might otherwise be questionable since heap
* TIDs can get recycled.) This holds true even if the page has been modified
* by inserts and page splits, so there is no need to consult the LSN.
*
* If the pin was released after reading the page, then we re-read it. If it
* has been modified since we read it (as determined by the LSN), we dare not
* flag any entries because it is possible that the old entry was vacuumed
* away and the TID was re-used by a completely different heap tuple.
* We give up much less often during !so->dropPin scans, but it still happens.
* We cope with cases where items have moved right due to insertions. If an
* item has moved off the current page due to a split, we'll fail to find it
* and just give up on it.
*/
void
_bt_killitems(IndexScanDesc scan)
{
Relation rel = scan->indexRelation;
BTScanOpaque so = (BTScanOpaque) scan->opaque;
Page page;
BTPageOpaque opaque;
OffsetNumber minoff;
OffsetNumber maxoff;
int i;
int numKilled = so->numKilled;
bool killedsomething = false;
bool droppedpin PG_USED_FOR_ASSERTS_ONLY;
Buffer buf;
Assert(numKilled > 0);
Assert(BTScanPosIsValid(so->currPos));
Assert(scan->heapRelation != NULL); /* can't be a bitmap index scan */
/*
* Always reset the scan state, so we don't look for same items on other
* pages.
*/
/* Always invalidate so->killedItems[] before leaving so->currPos */
so->numKilled = 0;
if (BTScanPosIsPinned(so->currPos))
if (!so->dropPin)
{
/*
* We have held the pin on this page since we read the index tuples,
* so all we need to do is lock it. The pin will have prevented
* re-use of any TID on the page, so there is no need to check the
* LSN.
* concurrent VACUUMs from recycling any of the TIDs on the page.
*/
droppedpin = false;
_bt_lockbuf(scan->indexRelation, so->currPos.buf, BT_READ);
page = BufferGetPage(so->currPos.buf);
Assert(BTScanPosIsPinned(so->currPos));
buf = so->currPos.buf;
_bt_lockbuf(rel, buf, BT_READ);
}
else
{
Buffer buf;
XLogRecPtr latestlsn;
droppedpin = true;
/* Attempt to re-read the buffer, getting pin and lock. */
buf = _bt_getbuf(scan->indexRelation, so->currPos.currPage, BT_READ);
Assert(!BTScanPosIsPinned(so->currPos));
Assert(RelationNeedsWAL(rel));
buf = _bt_getbuf(rel, so->currPos.currPage, BT_READ);
page = BufferGetPage(buf);
if (BufferGetLSNAtomic(buf) == so->currPos.lsn)
so->currPos.buf = buf;
else
latestlsn = BufferGetLSNAtomic(buf);
Assert(!XLogRecPtrIsInvalid(so->currPos.lsn));
Assert(so->currPos.lsn <= latestlsn);
if (so->currPos.lsn != latestlsn)
{
/* Modified while not pinned means hinting is not safe. */
_bt_relbuf(scan->indexRelation, buf);
/* Modified, give up on hinting */
_bt_relbuf(rel, buf);
return;
}
/* Unmodified, hinting is safe */
}
page = BufferGetPage(buf);
opaque = BTPageGetOpaque(page);
minoff = P_FIRSTDATAKEY(opaque);
maxoff = PageGetMaxOffsetNumber(page);
for (i = 0; i < numKilled; i++)
for (int i = 0; i < numKilled; i++)
{
int itemIndex = so->killedItems[i];
BTScanPosItem *kitem = &so->currPos.items[itemIndex];
@ -3404,7 +3440,7 @@ _bt_killitems(IndexScanDesc scan)
* correctness.
*
* Note that the page may have been modified in almost any way
* since we first read it (in the !droppedpin case), so it's
* since we first read it (in the !so->dropPin case), so it's
* possible that this posting list tuple wasn't a posting list
* tuple when we first encountered its heap TIDs.
*/
@ -3420,7 +3456,7 @@ _bt_killitems(IndexScanDesc scan)
* though only in the common case where the page can't
* have been concurrently modified
*/
Assert(kitem->indexOffset == offnum || !droppedpin);
Assert(kitem->indexOffset == offnum || !so->dropPin);
/*
* Read-ahead to later kitems here.
@ -3484,10 +3520,13 @@ _bt_killitems(IndexScanDesc scan)
if (killedsomething)
{
opaque->btpo_flags |= BTP_HAS_GARBAGE;
MarkBufferDirtyHint(so->currPos.buf, true);
MarkBufferDirtyHint(buf, true);
}
_bt_unlockbuf(scan->indexRelation, so->currPos.buf);
if (!so->dropPin)
_bt_unlockbuf(rel, buf);
else
_bt_relbuf(rel, buf);
}

View File

@ -252,6 +252,8 @@ ParsePrepareRecord(uint8 info, xl_xact_prepare *xlrec, xl_xact_parsed_prepare *p
parsed->nsubxacts = xlrec->nsubxacts;
parsed->nrels = xlrec->ncommitrels;
parsed->nabortrels = xlrec->nabortrels;
parsed->nstats = xlrec->ncommitstats;
parsed->nabortstats = xlrec->nabortstats;
parsed->nmsgs = xlrec->ninvalmsgs;
strncpy(parsed->twophase_gid, bufptr, xlrec->gidlen);

View File

@ -872,7 +872,7 @@ MultiXactIdCreateFromMembers(int nmembers, MultiXactMember *members)
*/
multi = GetNewMultiXactId(nmembers, &offset);
INJECTION_POINT_CACHED("multixact-create-from-members");
INJECTION_POINT_CACHED("multixact-create-from-members", NULL);
/* Make an XLOG entry describing the new MXID. */
xlrec.mid = multi;
@ -1486,7 +1486,7 @@ retry:
LWLockRelease(lock);
CHECK_FOR_INTERRUPTS();
INJECTION_POINT("multixact-get-members-cv-sleep");
INJECTION_POINT("multixact-get-members-cv-sleep", NULL);
ConditionVariableSleep(&MultiXactState->nextoff_cv,
WAIT_EVENT_MULTIXACT_CREATION);

View File

@ -7882,7 +7882,7 @@ CreateRestartPoint(int flags)
* This location needs to be after CheckPointGuts() to ensure that some
* work has already happened during this checkpoint.
*/
INJECTION_POINT("create-restart-point");
INJECTION_POINT("create-restart-point", NULL);
/*
* Remember the prior checkpoint's redo ptr for

View File

@ -346,8 +346,6 @@ IsSharedRelation(Oid relationId)
relationId == PgDbRoleSettingToastIndex ||
relationId == PgParameterAclToastTable ||
relationId == PgParameterAclToastIndex ||
relationId == PgReplicationOriginToastTable ||
relationId == PgReplicationOriginToastIndex ||
relationId == PgShdescriptionToastTable ||
relationId == PgShdescriptionToastIndex ||
relationId == PgShseclabelToastTable ||

View File

@ -674,11 +674,6 @@ GRANT SELECT ON pg_backend_memory_contexts TO pg_read_all_stats;
REVOKE EXECUTE ON FUNCTION pg_get_backend_memory_contexts() FROM PUBLIC;
GRANT EXECUTE ON FUNCTION pg_get_backend_memory_contexts() TO pg_read_all_stats;
REVOKE EXECUTE ON FUNCTION
pg_get_process_memory_contexts(integer, boolean, float) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION
pg_get_process_memory_contexts(integer, boolean, float) TO pg_read_all_stats;
-- Statistics views
CREATE VIEW pg_stat_all_tables AS

View File

@ -835,7 +835,7 @@ BeginCopyTo(ParseState *pstate,
((DR_copy *) dest)->cstate = cstate;
/* Create a QueryDesc requesting no output */
cstate->queryDesc = CreateQueryDesc(plan, NULL, pstate->p_sourcetext,
cstate->queryDesc = CreateQueryDesc(plan, pstate->p_sourcetext,
GetActiveSnapshot(),
InvalidSnapshot,
dest, NULL, NULL, 0);
@ -845,8 +845,7 @@ BeginCopyTo(ParseState *pstate,
*
* ExecutorStart computes a result tupdesc for us
*/
if (!ExecutorStart(cstate->queryDesc, 0))
elog(ERROR, "ExecutorStart() failed unexpectedly");
ExecutorStart(cstate->queryDesc, 0);
tupDesc = cstate->queryDesc->tupDesc;
}

View File

@ -334,13 +334,12 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
UpdateActiveSnapshotCommandId();
/* Create a QueryDesc, redirecting output to our tuple receiver */
queryDesc = CreateQueryDesc(plan, NULL, pstate->p_sourcetext,
queryDesc = CreateQueryDesc(plan, pstate->p_sourcetext,
GetActiveSnapshot(), InvalidSnapshot,
dest, params, queryEnv, 0);
/* call ExecutorStart to prepare the plan for execution */
if (!ExecutorStart(queryDesc, GetIntoRelEFlags(into)))
elog(ERROR, "ExecutorStart() failed unexpectedly");
ExecutorStart(queryDesc, GetIntoRelEFlags(into));
/* run the plan to completion */
ExecutorRun(queryDesc, ForwardScanDirection, 0);

View File

@ -1065,16 +1065,41 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
/* Check that the chosen locales are valid, and get canonical spellings */
if (!check_locale(LC_COLLATE, dbcollate, &canonname))
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("invalid LC_COLLATE locale name: \"%s\"", dbcollate),
errhint("If the locale name is specific to ICU, use ICU_LOCALE.")));
{
if (dblocprovider == COLLPROVIDER_BUILTIN)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("invalid LC_COLLATE locale name: \"%s\"", dbcollate),
errhint("If the locale name is specific to the builtin provider, use BUILTIN_LOCALE.")));
else if (dblocprovider == COLLPROVIDER_ICU)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("invalid LC_COLLATE locale name: \"%s\"", dbcollate),
errhint("If the locale name is specific to the ICU provider, use ICU_LOCALE.")));
else
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("invalid LC_COLLATE locale name: \"%s\"", dbcollate)));
}
dbcollate = canonname;
if (!check_locale(LC_CTYPE, dbctype, &canonname))
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("invalid LC_CTYPE locale name: \"%s\"", dbctype),
errhint("If the locale name is specific to ICU, use ICU_LOCALE.")));
{
if (dblocprovider == COLLPROVIDER_BUILTIN)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("invalid LC_CTYPE locale name: \"%s\"", dbctype),
errhint("If the locale name is specific to the builtin provider, use BUILTIN_LOCALE.")));
else if (dblocprovider == COLLPROVIDER_ICU)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("invalid LC_CTYPE locale name: \"%s\"", dbctype),
errhint("If the locale name is specific to the ICU provider, use ICU_LOCALE.")));
else
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("invalid LC_CTYPE locale name: \"%s\"", dbctype)));
}
dbctype = canonname;
check_encoding_locale_matches(encoding, dbcollate, dbctype);

View File

@ -369,8 +369,7 @@ standard_ExplainOneQuery(Query *query, int cursorOptions,
}
/* run it (if needed) and produce output */
ExplainOnePlan(plan, NULL, NULL, -1, into, es, queryString, params,
queryEnv,
ExplainOnePlan(plan, into, es, queryString, params, queryEnv,
&planduration, (es->buffers ? &bufusage : NULL),
es->memory ? &mem_counters : NULL);
}
@ -492,9 +491,7 @@ ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
* to call it.
*/
void
ExplainOnePlan(PlannedStmt *plannedstmt, CachedPlan *cplan,
CachedPlanSource *plansource, int query_index,
IntoClause *into, ExplainState *es,
ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
const char *queryString, ParamListInfo params,
QueryEnvironment *queryEnv, const instr_time *planduration,
const BufferUsage *bufusage,
@ -550,7 +547,7 @@ ExplainOnePlan(PlannedStmt *plannedstmt, CachedPlan *cplan,
dest = None_Receiver;
/* Create a QueryDesc for the query */
queryDesc = CreateQueryDesc(plannedstmt, cplan, queryString,
queryDesc = CreateQueryDesc(plannedstmt, queryString,
GetActiveSnapshot(), InvalidSnapshot,
dest, params, queryEnv, instrument_option);
@ -564,17 +561,8 @@ ExplainOnePlan(PlannedStmt *plannedstmt, CachedPlan *cplan,
if (into)
eflags |= GetIntoRelEFlags(into);
/* Prepare the plan for execution. */
if (queryDesc->cplan)
{
ExecutorStartCachedPlan(queryDesc, eflags, plansource, query_index);
Assert(queryDesc->planstate);
}
else
{
if (!ExecutorStart(queryDesc, eflags))
elog(ERROR, "ExecutorStart() failed unexpectedly");
}
/* call ExecutorStart to prepare the plan for execution */
ExecutorStart(queryDesc, eflags);
/* Execute the plan for statistics if asked for */
if (es->analyze)
@ -823,14 +811,10 @@ ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
* the queryid in any of the EXPLAIN plans to keep stable the results
* generated by regression test suites.
*/
if (es->verbose && queryDesc->plannedstmt->queryId != UINT64CONST(0) &&
if (es->verbose && queryDesc->plannedstmt->queryId != INT64CONST(0) &&
compute_query_id != COMPUTE_QUERY_ID_REGRESS)
{
/*
* Output the queryid as an int64 rather than a uint64 so we match
* what would be seen in the BIGINT pg_stat_statements.queryid column.
*/
ExplainPropertyInteger("Query Identifier", NULL, (int64)
ExplainPropertyInteger("Query Identifier", NULL,
queryDesc->plannedstmt->queryId, es);
}
}
@ -1232,6 +1216,10 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
if (((ModifyTable *) plan)->exclRelRTI)
*rels_used = bms_add_member(*rels_used,
((ModifyTable *) plan)->exclRelRTI);
/* Ensure Vars used in RETURNING will have refnames */
if (plan->targetlist)
*rels_used = bms_add_member(*rels_used,
linitial_int(((ModifyTable *) plan)->resultRelations));
break;
case T_Append:
*rels_used = bms_add_members(*rels_used,

View File

@ -993,13 +993,11 @@ execute_sql_string(const char *sql, const char *filename)
QueryDesc *qdesc;
qdesc = CreateQueryDesc(stmt,
NULL,
sql,
GetActiveSnapshot(), NULL,
dest, NULL, NULL, 0);
if (!ExecutorStart(qdesc, 0))
elog(ERROR, "ExecutorStart() failed unexpectedly");
ExecutorStart(qdesc, 0);
ExecutorRun(qdesc, ForwardScanDirection, 0);
ExecutorFinish(qdesc);
ExecutorEnd(qdesc);

View File

@ -71,15 +71,26 @@ optionListToArray(List *options)
foreach(cell, options)
{
DefElem *def = lfirst(cell);
const char *name;
const char *value;
Size len;
text *t;
name = def->defname;
value = defGetString(def);
len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
/* Insist that name not contain "=", else "a=b=c" is ambiguous */
if (strchr(name, '=') != NULL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid option name \"%s\": must not contain \"=\"",
name)));
len = VARHDRSZ + strlen(name) + 1 + strlen(value);
/* +1 leaves room for sprintf's trailing null */
t = palloc(len + 1);
SET_VARSIZE(t, len);
sprintf(VARDATA(t), "%s=%s", def->defname, value);
sprintf(VARDATA(t), "%s=%s", name, value);
astate = accumArrayResult(astate, PointerGetDatum(t),
false, TEXTOID,

View File

@ -3892,9 +3892,9 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
#ifdef USE_INJECTION_POINTS
if (idx->safe)
INJECTION_POINT("reindex-conc-index-safe");
INJECTION_POINT("reindex-conc-index-safe", NULL);
else
INJECTION_POINT("reindex-conc-index-not-safe");
INJECTION_POINT("reindex-conc-index-not-safe", NULL);
#endif
idx->tableId = RelationGetRelid(heapRel);
@ -4226,7 +4226,7 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
false);
/*
* Updating pg_index might involve TOAST table access, so ensure we
* Swapping the indexes might involve TOAST table access, so ensure we
* have a valid snapshot.
*/
PushActiveSnapshot(GetTransactionSnapshot());

View File

@ -438,13 +438,12 @@ refresh_matview_datafill(DestReceiver *dest, Query *query,
UpdateActiveSnapshotCommandId();
/* Create a QueryDesc, redirecting output to our tuple receiver */
queryDesc = CreateQueryDesc(plan, NULL, queryString,
queryDesc = CreateQueryDesc(plan, queryString,
GetActiveSnapshot(), InvalidSnapshot,
dest, NULL, NULL, 0);
/* call ExecutorStart to prepare the plan for execution */
if (!ExecutorStart(queryDesc, 0))
elog(ERROR, "ExecutorStart() failed unexpectedly");
ExecutorStart(queryDesc, 0);
/* run the plan */
ExecutorRun(queryDesc, ForwardScanDirection, 0);

View File

@ -117,7 +117,6 @@ PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo pa
queryString,
CMDTAG_SELECT, /* cursor's query is always a SELECT */
list_make1(plan),
NULL,
NULL);
/*----------

View File

@ -205,8 +205,7 @@ ExecuteQuery(ParseState *pstate,
query_string,
entry->plansource->commandTag,
plan_list,
cplan,
entry->plansource);
cplan);
/*
* For CREATE TABLE ... AS EXECUTE, we must verify that the prepared
@ -586,7 +585,6 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
MemoryContextCounters mem_counters;
MemoryContext planner_ctx = NULL;
MemoryContext saved_ctx = NULL;
int query_index = 0;
if (es->memory)
{
@ -659,8 +657,7 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
PlannedStmt *pstmt = lfirst_node(PlannedStmt, p);
if (pstmt->commandType != CMD_UTILITY)
ExplainOnePlan(pstmt, cplan, entry->plansource, query_index,
into, es, query_string, paramLI, pstate->p_queryEnv,
ExplainOnePlan(pstmt, into, es, query_string, paramLI, pstate->p_queryEnv,
&planduration, (es->buffers ? &bufusage : NULL),
es->memory ? &mem_counters : NULL);
else
@ -671,8 +668,6 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
/* Separate plans with an appropriate separator */
if (lnext(plan_list, p) != NULL)
ExplainSeparatePlans(es);
query_index++;
}
if (estate)

View File

@ -430,8 +430,8 @@ static void AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation
static ObjectAddress ATExecValidateConstraint(List **wqueue,
Relation rel, char *constrName,
bool recurse, bool recursing, LOCKMODE lockmode);
static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel,
HeapTuple contuple, LOCKMODE lockmode);
static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel,
Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode);
static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
char *constrName, HeapTuple contuple,
bool recurse, bool recursing, LOCKMODE lockmode);
@ -11858,6 +11858,7 @@ AttachPartitionForeignKey(List **wqueue,
if (queueValidation)
{
Relation conrel;
Oid confrelid;
conrel = table_open(ConstraintRelationId, RowExclusiveLock);
@ -11865,9 +11866,11 @@ AttachPartitionForeignKey(List **wqueue,
if (!HeapTupleIsValid(partcontup))
elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
confrelid = ((Form_pg_constraint) GETSTRUCT(partcontup))->confrelid;
/* Use the same lock as for AT_ValidateConstraint */
QueueFKConstraintValidation(wqueue, conrel, partition, partcontup,
ShareUpdateExclusiveLock);
QueueFKConstraintValidation(wqueue, conrel, partition, confrelid,
partcontup, ShareUpdateExclusiveLock);
ReleaseSysCache(partcontup);
table_close(conrel, RowExclusiveLock);
}
@ -12463,9 +12466,12 @@ ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
/*
* Tell Phase 3 to check that the constraint is satisfied by existing
* rows.
* rows. Only applies to leaf partitions, and (for constraints that
* reference a partitioned table) only if this is not one of the
* pg_constraint rows that exist solely to support action triggers.
*/
if (rel->rd_rel->relkind == RELKIND_RELATION)
if (rel->rd_rel->relkind == RELKIND_RELATION &&
currcon->confrelid == pkrelid)
{
AlteredTableInfo *tab;
NewConstraint *newcon;
@ -12919,7 +12925,8 @@ ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
{
if (con->contype == CONSTRAINT_FOREIGN)
{
QueueFKConstraintValidation(wqueue, conrel, rel, tuple, lockmode);
QueueFKConstraintValidation(wqueue, conrel, rel, con->confrelid,
tuple, lockmode);
}
else if (con->contype == CONSTRAINT_CHECK)
{
@ -12952,8 +12959,8 @@ ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
* for the specified relation and all its children.
*/
static void
QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel,
HeapTuple contuple, LOCKMODE lockmode)
QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel,
Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode)
{
Form_pg_constraint con;
AlteredTableInfo *tab;
@ -12964,7 +12971,17 @@ QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel,
Assert(con->contype == CONSTRAINT_FOREIGN);
Assert(!con->convalidated);
if (rel->rd_rel->relkind == RELKIND_RELATION)
/*
* Add the validation to phase 3's queue; not needed for partitioned
* tables themselves, only for their partitions.
*
* When the referenced table (pkrelid) is partitioned, the referencing
* table (fkrel) has one pg_constraint row pointing to each partition
* thereof. These rows are there only to support action triggers and no
* table scan is needed, therefore skip this for them as well.
*/
if (fkrel->rd_rel->relkind == RELKIND_RELATION &&
con->confrelid == pkrelid)
{
NewConstraint *newcon;
Constraint *fkconstraint;
@ -12983,15 +13000,16 @@ QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel,
newcon->qual = (Node *) fkconstraint;
/* Find or create work queue entry for this table */
tab = ATGetQueueEntry(wqueue, rel);
tab = ATGetQueueEntry(wqueue, fkrel);
tab->constraints = lappend(tab->constraints, newcon);
}
/*
* If the table at either end of the constraint is partitioned, we need to
* recurse and handle every constraint that is a child of this constraint.
* recurse and handle every unvalidate constraint that is a child of this
* constraint.
*/
if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
if (fkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
{
ScanKeyData pkey;
@ -13023,8 +13041,12 @@ QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel,
childrel = table_open(childcon->conrelid, lockmode);
QueueFKConstraintValidation(wqueue, conrel, childrel, childtup,
lockmode);
/*
* NB: Note that pkrelid should be passed as-is during recursion,
* as it is required to identify the root referenced table.
*/
QueueFKConstraintValidation(wqueue, conrel, childrel, pkrelid,
childtup, lockmode);
table_close(childrel, NoLock);
}
@ -13032,7 +13054,11 @@ QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel,
}
/*
* Now update the catalog, while we have the door open.
* Now mark the pg_constraint row as validated (even if we didn't check,
* notably the ones for partitions on the referenced side).
*
* We rely on transaction abort to roll back this change if phase 3
* ultimately finds violating rows. This is a bit ugly.
*/
copyTuple = heap_copytuple(contuple);
copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
@ -20964,9 +20990,17 @@ ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
tab->rel = rel;
}
/*
* Detaching the partition might involve TOAST table access, so ensure we
* have a valid snapshot.
*/
PushActiveSnapshot(GetTransactionSnapshot());
/* Do the final part of detaching */
DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
PopActiveSnapshot();
ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
/* keep our lock until commit */

View File

@ -5057,21 +5057,6 @@ AfterTriggerBeginQuery(void)
}
/* ----------
* AfterTriggerAbortQuery()
*
* Called by standard_ExecutorEnd() if the query execution was aborted due to
* the plan becoming invalid during initialization.
* ----------
*/
void
AfterTriggerAbortQuery(void)
{
/* Revert the actions of AfterTriggerBeginQuery(). */
afterTriggers.query_depth--;
}
/* ----------
* AfterTriggerEndQuery()
*

View File

@ -63,7 +63,7 @@ typedef struct PVShared
*/
Oid relid;
int elevel;
uint64 queryid;
int64 queryid;
/*
* Fields for both index vacuum and cleanup.

View File

@ -285,28 +285,6 @@ are typically reset to empty once per tuple. Per-tuple contexts are usually
associated with ExprContexts, and commonly each PlanState node has its own
ExprContext to evaluate its qual and targetlist expressions in.
Relation Locking
----------------
When the executor initializes a plan tree for execution, it doesn't lock
non-index relations if the plan tree is freshly generated and not derived
from a CachedPlan. This is because such locks have already been established
during the query's parsing, rewriting, and planning phases. However, with a
cached plan tree, some relations may remain unlocked. The function
AcquireExecutorLocks() only locks unprunable relations in the plan, deferring
the locking of prunable ones to executor initialization. This avoids
unnecessary locking of relations that will be pruned during "initial" runtime
pruning in ExecDoInitialPruning().
This approach creates a window where a cached plan tree with child tables
could become outdated if another backend modifies these tables before
ExecDoInitialPruning() locks them. As a result, the executor has the added duty
to verify the plan tree's validity whenever it locks a child table after
doing initial pruning. This validation is done by checking the CachedPlan.is_valid
flag. If the plan tree is outdated (is_valid = false), the executor stops
further initialization, cleans up anything in EState that would have been
allocated up to that point, and retries execution after recreating the
invalid plan in the CachedPlan. See ExecutorStartCachedPlan().
Query Processing Control Flow
-----------------------------
@ -315,13 +293,11 @@ This is a sketch of control flow for full query processing:
CreateQueryDesc
ExecutorStart or ExecutorStartCachedPlan
ExecutorStart
CreateExecutorState
creates per-query context
switch to per-query context to run ExecDoInitialPruning and ExecInitNode
switch to per-query context to run ExecInitNode
AfterTriggerBeginQuery
ExecDoInitialPruning
does initial pruning and locks surviving partitions if needed
ExecInitNode --- recursively scans plan tree
ExecInitNode
recurse into subsidiary nodes
@ -345,12 +321,7 @@ This is a sketch of control flow for full query processing:
FreeQueryDesc
As mentioned in the "Relation Locking" section, if the plan tree is found to
be stale after locking partitions in ExecDoInitialPruning(), the control is
immediately returned to ExecutorStartCachedPlan(), which will create a new plan
tree and perform the steps starting from CreateExecutorState() again.
Per above comments, it's not really critical for ExecEndPlan to free any
Per above comments, it's not really critical for ExecEndNode to free any
memory; it'll all go away in FreeExecutorState anyway. However, we do need to
be careful to close relations, drop buffer pins, etc, so we do need to scan
the plan state tree to find these sorts of resources.

View File

@ -55,13 +55,11 @@
#include "parser/parse_relation.h"
#include "pgstat.h"
#include "rewrite/rewriteHandler.h"
#include "storage/lmgr.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/backend_status.h"
#include "utils/lsyscache.h"
#include "utils/partcache.h"
#include "utils/plancache.h"
#include "utils/rls.h"
#include "utils/snapmgr.h"
@ -119,16 +117,11 @@ static void ReportNotNullViolationError(ResultRelInfo *resultRelInfo,
* get control when ExecutorStart is called. Such a plugin would
* normally call standard_ExecutorStart().
*
* Return value indicates if the plan has been initialized successfully so
* that queryDesc->planstate contains a valid PlanState tree. It may not
* if the plan got invalidated during InitPlan().
* ----------------------------------------------------------------
*/
bool
void
ExecutorStart(QueryDesc *queryDesc, int eflags)
{
bool plan_valid;
/*
* In some cases (e.g. an EXECUTE statement or an execute message with the
* extended query protocol) the query_id won't be reported, so do it now.
@ -140,14 +133,12 @@ ExecutorStart(QueryDesc *queryDesc, int eflags)
pgstat_report_query_id(queryDesc->plannedstmt->queryId, false);
if (ExecutorStart_hook)
plan_valid = (*ExecutorStart_hook) (queryDesc, eflags);
(*ExecutorStart_hook) (queryDesc, eflags);
else
plan_valid = standard_ExecutorStart(queryDesc, eflags);
return plan_valid;
standard_ExecutorStart(queryDesc, eflags);
}
bool
void
standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
{
EState *estate;
@ -271,64 +262,6 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
InitPlan(queryDesc, eflags);
MemoryContextSwitchTo(oldcontext);
return ExecPlanStillValid(queryDesc->estate);
}
/*
* ExecutorStartCachedPlan
* Start execution for a given query in the CachedPlanSource, replanning
* if the plan is invalidated due to deferred locks taken during the
* plan's initialization
*
* This function handles cases where the CachedPlan given in queryDesc->cplan
* might become invalid during the initialization of the plan given in
* queryDesc->plannedstmt, particularly when prunable relations in it are
* locked after performing initial pruning. If the locks invalidate the plan,
* the function calls UpdateCachedPlan() to replan all queries in the
* CachedPlan, and then retries initialization.
*
* The function repeats the process until ExecutorStart() successfully
* initializes the plan, that is without the CachedPlan becoming invalid.
*/
void
ExecutorStartCachedPlan(QueryDesc *queryDesc, int eflags,
CachedPlanSource *plansource,
int query_index)
{
if (unlikely(queryDesc->cplan == NULL))
elog(ERROR, "ExecutorStartCachedPlan(): missing CachedPlan");
if (unlikely(plansource == NULL))
elog(ERROR, "ExecutorStartCachedPlan(): missing CachedPlanSource");
/*
* Loop and retry with an updated plan until no further invalidation
* occurs.
*/
while (1)
{
if (!ExecutorStart(queryDesc, eflags))
{
/*
* Clean up the current execution state before creating the new
* plan to retry ExecutorStart(). Mark execution as aborted to
* ensure that AFTER trigger state is properly reset.
*/
queryDesc->estate->es_aborted = true;
ExecutorEnd(queryDesc);
/* Retry ExecutorStart() with an updated plan tree. */
queryDesc->plannedstmt = UpdateCachedPlan(plansource, query_index,
queryDesc->queryEnv);
}
else
/*
* Exit the loop if the plan is initialized successfully and no
* sinval messages were received that invalidated the CachedPlan.
*/
break;
}
}
/* ----------------------------------------------------------------
@ -387,7 +320,6 @@ standard_ExecutorRun(QueryDesc *queryDesc,
estate = queryDesc->estate;
Assert(estate != NULL);
Assert(!estate->es_aborted);
Assert(!(estate->es_top_eflags & EXEC_FLAG_EXPLAIN_ONLY));
/* caller must ensure the query's snapshot is active */
@ -494,11 +426,8 @@ standard_ExecutorFinish(QueryDesc *queryDesc)
Assert(estate != NULL);
Assert(!(estate->es_top_eflags & EXEC_FLAG_EXPLAIN_ONLY));
/*
* This should be run once and only once per Executor instance and never
* if the execution was aborted.
*/
Assert(!estate->es_finished && !estate->es_aborted);
/* This should be run once and only once per Executor instance */
Assert(!estate->es_finished);
/* Switch into per-query memory context */
oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
@ -561,10 +490,11 @@ standard_ExecutorEnd(QueryDesc *queryDesc)
(PgStat_Counter) estate->es_parallel_workers_launched);
/*
* Check that ExecutorFinish was called, unless in EXPLAIN-only mode or if
* execution was aborted.
* Check that ExecutorFinish was called, unless in EXPLAIN-only mode. This
* Assert is needed because ExecutorFinish is new as of 9.1, and callers
* might forget to call it.
*/
Assert(estate->es_finished || estate->es_aborted ||
Assert(estate->es_finished ||
(estate->es_top_eflags & EXEC_FLAG_EXPLAIN_ONLY));
/*
@ -578,14 +508,6 @@ standard_ExecutorEnd(QueryDesc *queryDesc)
UnregisterSnapshot(estate->es_snapshot);
UnregisterSnapshot(estate->es_crosscheck_snapshot);
/*
* Reset AFTER trigger module if the query execution was aborted.
*/
if (estate->es_aborted &&
!(estate->es_top_eflags &
(EXEC_FLAG_SKIP_TRIGGERS | EXEC_FLAG_EXPLAIN_ONLY)))
AfterTriggerAbortQuery();
/*
* Must switch out of context before destroying it
*/
@ -684,21 +606,6 @@ ExecCheckPermissions(List *rangeTable, List *rteperminfos,
(rte->rtekind == RTE_SUBQUERY &&
rte->relkind == RELKIND_VIEW));
/*
* Ensure that we have at least an AccessShareLock on relations
* whose permissions need to be checked.
*
* Skip this check in a parallel worker because locks won't be
* taken until ExecInitNode() performs plan initialization.
*
* XXX: ExecCheckPermissions() in a parallel worker may be
* redundant with the checks done in the leader process, so this
* should be reviewed to ensure its necessary.
*/
Assert(IsParallelWorker() ||
CheckRelationOidLockedByMe(rte->relid, AccessShareLock,
true));
(void) getRTEPermissionInfo(rteperminfos, rte);
/* Many-to-one mapping not allowed */
Assert(!bms_is_member(rte->perminfoindex, indexset));
@ -924,12 +831,6 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
*
* Initializes the query plan: open files, allocate storage
* and start up the rule manager
*
* If the plan originates from a CachedPlan (given in queryDesc->cplan),
* it can become invalid during runtime "initial" pruning when the
* remaining set of locks is taken. The function returns early in that
* case without initializing the plan, and the caller is expected to
* retry with a new valid plan.
* ----------------------------------------------------------------
*/
static void
@ -937,7 +838,6 @@ InitPlan(QueryDesc *queryDesc, int eflags)
{
CmdType operation = queryDesc->operation;
PlannedStmt *plannedstmt = queryDesc->plannedstmt;
CachedPlan *cachedplan = queryDesc->cplan;
Plan *plan = plannedstmt->planTree;
List *rangeTable = plannedstmt->rtable;
EState *estate = queryDesc->estate;
@ -958,7 +858,6 @@ InitPlan(QueryDesc *queryDesc, int eflags)
bms_copy(plannedstmt->unprunableRelids));
estate->es_plannedstmt = plannedstmt;
estate->es_cachedplan = cachedplan;
estate->es_part_prune_infos = plannedstmt->partPruneInfos;
/*
@ -972,9 +871,6 @@ InitPlan(QueryDesc *queryDesc, int eflags)
*/
ExecDoInitialPruning(estate);
if (!ExecPlanStillValid(estate))
return;
/*
* Next, build the ExecRowMark array from the PlanRowMark(s), if any.
*/
@ -3092,9 +2988,6 @@ EvalPlanQualStart(EPQState *epqstate, Plan *planTree)
* the snapshot, rangetable, and external Param info. They need their own
* copies of local state, including a tuple table, es_param_exec_vals,
* result-rel info, etc.
*
* es_cachedplan is not copied because EPQ plan execution does not acquire
* any new locks that could invalidate the CachedPlan.
*/
rcestate->es_direction = ForwardScanDirection;
rcestate->es_snapshot = parentestate->es_snapshot;

View File

@ -1278,15 +1278,8 @@ ExecParallelGetQueryDesc(shm_toc *toc, DestReceiver *receiver,
paramspace = shm_toc_lookup(toc, PARALLEL_KEY_PARAMLISTINFO, false);
paramLI = RestoreParamList(&paramspace);
/*
* Create a QueryDesc for the query. We pass NULL for cachedplan, because
* we don't have a pointer to the CachedPlan in the leader's process. It's
* fine because the only reason the executor needs to see it is to decide
* if it should take locks on certain relations, but parallel workers
* always take locks anyway.
*/
/* Create a QueryDesc for the query. */
return CreateQueryDesc(pstmt,
NULL,
queryString,
GetActiveSnapshot(), InvalidSnapshot,
receiver, paramLI, NULL, instrument_options);
@ -1471,8 +1464,7 @@ ParallelQueryMain(dsm_segment *seg, shm_toc *toc)
/* Start up the executor */
queryDesc->plannedstmt->jitFlags = fpes->jit_flags;
if (!ExecutorStart(queryDesc, fpes->eflags))
elog(ERROR, "ExecutorStart() failed unexpectedly");
ExecutorStart(queryDesc, fpes->eflags);
/* Special executor initialization steps for parallel workers */
queryDesc->planstate->state->es_query_dsa = area;

View File

@ -26,7 +26,6 @@
#include "partitioning/partdesc.h"
#include "partitioning/partprune.h"
#include "rewrite/rewriteManip.h"
#include "storage/lmgr.h"
#include "utils/acl.h"
#include "utils/lsyscache.h"
#include "utils/partcache.h"
@ -1771,8 +1770,7 @@ adjust_partition_colnos_using_map(List *colnos, AttrMap *attrMap)
* ExecDoInitialPruning:
* Perform runtime "initial" pruning, if necessary, to determine the set
* of child subnodes that need to be initialized during ExecInitNode() for
* all plan nodes that contain a PartitionPruneInfo. This also locks the
* leaf partitions whose subnodes will be initialized if needed.
* all plan nodes that contain a PartitionPruneInfo.
*
* ExecInitPartitionExecPruning:
* Updates the PartitionPruneState found at given part_prune_index in
@ -1798,8 +1796,7 @@ adjust_partition_colnos_using_map(List *colnos, AttrMap *attrMap)
* ExecDoInitialPruning
* Perform runtime "initial" pruning, if necessary, to determine the set
* of child subnodes that need to be initialized during ExecInitNode() for
* plan nodes that support partition pruning. This also locks the leaf
* partitions whose subnodes will be initialized if needed.
* plan nodes that support partition pruning.
*
* This function iterates over each PartitionPruneInfo entry in
* estate->es_part_prune_infos. For each entry, it creates a PartitionPruneState
@ -1821,9 +1818,7 @@ adjust_partition_colnos_using_map(List *colnos, AttrMap *attrMap)
void
ExecDoInitialPruning(EState *estate)
{
PlannedStmt *stmt = estate->es_plannedstmt;
ListCell *lc;
List *locked_relids = NIL;
foreach(lc, estate->es_part_prune_infos)
{
@ -1849,68 +1844,11 @@ ExecDoInitialPruning(EState *estate)
else
validsubplan_rtis = all_leafpart_rtis;
if (ExecShouldLockRelations(estate))
{
int rtindex = -1;
while ((rtindex = bms_next_member(validsubplan_rtis,
rtindex)) >= 0)
{
RangeTblEntry *rte = exec_rt_fetch(rtindex, estate);
Assert(rte->rtekind == RTE_RELATION &&
rte->rellockmode != NoLock);
LockRelationOid(rte->relid, rte->rellockmode);
locked_relids = lappend_int(locked_relids, rtindex);
}
}
estate->es_unpruned_relids = bms_add_members(estate->es_unpruned_relids,
validsubplan_rtis);
estate->es_part_prune_results = lappend(estate->es_part_prune_results,
validsubplans);
}
/*
* Lock the first result relation of each ModifyTable node, even if it was
* pruned. This is required for ExecInitModifyTable(), which keeps its
* first result relation if all other result relations have been pruned,
* because some executor paths (e.g., in nodeModifyTable.c and
* execPartition.c) rely on there being at least one result relation.
*
* There's room for improvement here --- we actually only need to do this
* if all other result relations of the ModifyTable node were pruned, but
* we don't have an easy way to tell that here.
*/
if (stmt->resultRelations && ExecShouldLockRelations(estate))
{
foreach(lc, stmt->firstResultRels)
{
Index firstResultRel = lfirst_int(lc);
if (!bms_is_member(firstResultRel, estate->es_unpruned_relids))
{
RangeTblEntry *rte = exec_rt_fetch(firstResultRel, estate);
Assert(rte->rtekind == RTE_RELATION && rte->rellockmode != NoLock);
LockRelationOid(rte->relid, rte->rellockmode);
locked_relids = lappend_int(locked_relids, firstResultRel);
}
}
}
/*
* Release the useless locks if the plan won't be executed. This is the
* same as what CheckCachedPlan() in plancache.c does.
*/
if (!ExecPlanStillValid(estate))
{
foreach(lc, locked_relids)
{
RangeTblEntry *rte = exec_rt_fetch(lfirst_int(lc), estate);
UnlockRelationOid(rte->relid, rte->rellockmode);
}
}
}
/*

View File

@ -147,7 +147,6 @@ CreateExecutorState(void)
estate->es_top_eflags = 0;
estate->es_instrument = 0;
estate->es_finished = false;
estate->es_aborted = false;
estate->es_exprcontexts = NIL;

View File

@ -34,6 +34,7 @@
#include "utils/funccache.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/plancache.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
@ -1338,7 +1339,6 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
dest = None_Receiver;
es->qd = CreateQueryDesc(es->stmt,
NULL,
fcache->func->src,
GetActiveSnapshot(),
InvalidSnapshot,
@ -1363,8 +1363,7 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
eflags = EXEC_FLAG_SKIP_TRIGGERS;
else
eflags = 0; /* default run-to-completion flags */
if (!ExecutorStart(es->qd, eflags))
elog(ERROR, "ExecutorStart() failed unexpectedly");
ExecutorStart(es->qd, eflags);
}
es->status = F_EXEC_RUN;

Some files were not shown because too many files have changed in this diff Show More