diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index f9a955af12da1..4d8f98d953b15 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -14,6 +14,12 @@ # # $ git log --pretty=format:"%H # %cd%n# %s" $PGINDENTGITHASH -1 --date=iso +99e44c3181c779ae0f3539ba7f408661983fbf8e # 2026-06-29 15:27:44 -0400 +# Run pgperltidy + +3f815dd11374b54deb29228dc0040179864af828 # 2026-06-29 15:16:25 -0400 +# Sync typedefs.list with the buildfarm. + bd57abbb1910e51e45761b59985745d094ae9e03 # 2026-06-04 10:15:37 -0500 # Re-pgindent nodeModifyTable.c after commit 993a7aa0e4. diff --git a/configure b/configure index 5f77f3cac29f3..35b0b72f0a70f 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for PostgreSQL 19beta1. +# Generated by GNU Autoconf 2.69 for PostgreSQL 20devel. # # Report bugs to . # @@ -582,8 +582,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='PostgreSQL' PACKAGE_TARNAME='postgresql' -PACKAGE_VERSION='19beta1' -PACKAGE_STRING='PostgreSQL 19beta1' +PACKAGE_VERSION='20devel' +PACKAGE_STRING='PostgreSQL 20devel' PACKAGE_BUGREPORT='pgsql-bugs@lists.postgresql.org' PACKAGE_URL='https://www.postgresql.org/' @@ -1468,7 +1468,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures PostgreSQL 19beta1 to adapt to many kinds of systems. +\`configure' configures PostgreSQL 20devel to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1533,7 +1533,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of PostgreSQL 19beta1:";; + short | recursive ) echo "Configuration of PostgreSQL 20devel:";; esac cat <<\_ACEOF @@ -1724,7 +1724,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -PostgreSQL configure 19beta1 +PostgreSQL configure 20devel generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2477,7 +2477,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by PostgreSQL $as_me 19beta1, which was +It was created by PostgreSQL $as_me 20devel, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -20348,7 +20348,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by PostgreSQL $as_me 19beta1, which was +This file was extended by PostgreSQL $as_me 20devel, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -20419,7 +20419,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -PostgreSQL config.status 19beta1 +PostgreSQL config.status 20devel configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 61cee42daa721..0e624fe36b96d 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Read the Autoconf manual for details. dnl m4_pattern_forbid(^PGAC_)dnl to catch undefined macros -AC_INIT([PostgreSQL], [19beta1], [pgsql-bugs@lists.postgresql.org], [], [https://www.postgresql.org/]) +AC_INIT([PostgreSQL], [20devel], [pgsql-bugs@lists.postgresql.org], [], [https://www.postgresql.org/]) m4_if(m4_defn([m4_PACKAGE_VERSION]), [2.69], [], [m4_fatal([Autoconf version 2.69 is required. Untested combinations of 'autoconf' and PostgreSQL versions are not diff --git a/contrib/dblink/t/001_auth_scram.pl b/contrib/dblink/t/001_auth_scram.pl index b087b38e5a58a..8d6280656d51e 100644 --- a/contrib/dblink/t/001_auth_scram.pl +++ b/contrib/dblink/t/001_auth_scram.pl @@ -103,10 +103,10 @@ { my $connstr = $node1->connstr($db0) . qq' user=$user'; - $node1->safe_psql($db0, + $node1->safe_psql( + $db0, qq'ALTER USER MAPPING FOR $user SERVER $fdw_server3 OPTIONS(add use_scram_passthrough \'false\')', - connstr => $connstr - ); + connstr => $connstr); my ($ret, $stdout, $stderr) = $node1->psql( $db0, @@ -114,10 +114,9 @@ connstr => $connstr); is($ret, 3, 'SCRAM passthrough disabled on user mapping should fail'); - like( - $stderr, - qr/password/i, - 'expected password-related error when scram passthrough disabled on user mapping'); + like($stderr, qr/password/i, + 'expected password-related error when scram passthrough disabled on user mapping' + ); } # Ensure that trust connections fail without superuser opt-in. diff --git a/contrib/hstore_plpython/expected/hstore_plpython.out b/contrib/hstore_plpython/expected/hstore_plpython.out index 5fb56a2f65d10..5f8315e84dd40 100644 --- a/contrib/hstore_plpython/expected/hstore_plpython.out +++ b/contrib/hstore_plpython/expected/hstore_plpython.out @@ -43,6 +43,71 @@ SELECT test1bad(); ERROR: not a Python mapping CONTEXT: while creating return value PL/Python function "test1bad" +-- A mapping whose items() raises should be reported as an error, not crash +-- the backend +CREATE FUNCTION test1broken() RETURNS hstore +LANGUAGE plpython3u +TRANSFORM FOR TYPE hstore +AS $$ +class C(dict): + def items(self): + raise ValueError('items failed') +d = C() +d['x'] = 1 +return d +$$; +SELECT test1broken(); +ERROR: could not get items from Python mapping +CONTEXT: while creating return value +PL/Python function "test1broken" +-- Likewise for a mapping whose items() does not return key/value pairs +CREATE FUNCTION test1malformed() RETURNS hstore +LANGUAGE plpython3u +TRANSFORM FOR TYPE hstore +AS $$ +class C(dict): + def items(self): + return [42] +d = C() +d['x'] = 1 +return d +$$; +SELECT test1malformed(); +ERROR: items() of a Python mapping must return key/value pairs +CONTEXT: while creating return value +PL/Python function "test1malformed" +-- Likewise for a mapping whose items() yields fewer pairs than its length +CREATE FUNCTION test1short() RETURNS hstore +LANGUAGE plpython3u +TRANSFORM FOR TYPE hstore +AS $$ +class C(dict): + def items(self): + return [] +d = C() +d['x'] = 1 +return d +$$; +SELECT test1short(); +ERROR: items() of a Python mapping must return key/value pairs +CONTEXT: while creating return value +PL/Python function "test1short" +-- Likewise for a mapping whose __len__() raises +CREATE FUNCTION test1brokenlen() RETURNS hstore +LANGUAGE plpython3u +TRANSFORM FOR TYPE hstore +AS $$ +class C(dict): + def __len__(self): + raise ValueError('len failed') +d = C() +d['x'] = 1 +return d +$$; +SELECT test1brokenlen(); +ERROR: could not get size of Python mapping +CONTEXT: while creating return value +PL/Python function "test1brokenlen" -- test hstore[] -> python CREATE FUNCTION test1arr(val hstore[]) RETURNS int LANGUAGE plpython3u diff --git a/contrib/hstore_plpython/hstore_plpython.c b/contrib/hstore_plpython/hstore_plpython.c index f1e483980f4c7..b9d8b4537f781 100644 --- a/contrib/hstore_plpython/hstore_plpython.c +++ b/contrib/hstore_plpython/hstore_plpython.c @@ -143,7 +143,16 @@ plpython_to_hstore(PG_FUNCTION_ARGS) errmsg("not a Python mapping"))); pcount = PyMapping_Size(dict); + if (pcount < 0) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("could not get size of Python mapping"))); + items = PyMapping_Items(dict); + if (items == NULL) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("could not get items from Python mapping"))); PG_TRY(); { @@ -160,6 +169,13 @@ plpython_to_hstore(PG_FUNCTION_ARGS) PyObject *value; tuple = PyList_GetItem(items, i); + + /* The mapping's items() must yield key/value pairs */ + if (tuple == NULL || !PyTuple_Check(tuple) || PyTuple_Size(tuple) < 2) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("items() of a Python mapping must return key/value pairs"))); + key = PyTuple_GetItem(tuple, 0); value = PyTuple_GetItem(tuple, 1); diff --git a/contrib/hstore_plpython/sql/hstore_plpython.sql b/contrib/hstore_plpython/sql/hstore_plpython.sql index ebd61e6c467ac..a2b2046380f1a 100644 --- a/contrib/hstore_plpython/sql/hstore_plpython.sql +++ b/contrib/hstore_plpython/sql/hstore_plpython.sql @@ -38,6 +38,71 @@ $$; SELECT test1bad(); +-- A mapping whose items() raises should be reported as an error, not crash +-- the backend +CREATE FUNCTION test1broken() RETURNS hstore +LANGUAGE plpython3u +TRANSFORM FOR TYPE hstore +AS $$ +class C(dict): + def items(self): + raise ValueError('items failed') +d = C() +d['x'] = 1 +return d +$$; + +SELECT test1broken(); + + +-- Likewise for a mapping whose items() does not return key/value pairs +CREATE FUNCTION test1malformed() RETURNS hstore +LANGUAGE plpython3u +TRANSFORM FOR TYPE hstore +AS $$ +class C(dict): + def items(self): + return [42] +d = C() +d['x'] = 1 +return d +$$; + +SELECT test1malformed(); + + +-- Likewise for a mapping whose items() yields fewer pairs than its length +CREATE FUNCTION test1short() RETURNS hstore +LANGUAGE plpython3u +TRANSFORM FOR TYPE hstore +AS $$ +class C(dict): + def items(self): + return [] +d = C() +d['x'] = 1 +return d +$$; + +SELECT test1short(); + + +-- Likewise for a mapping whose __len__() raises +CREATE FUNCTION test1brokenlen() RETURNS hstore +LANGUAGE plpython3u +TRANSFORM FOR TYPE hstore +AS $$ +class C(dict): + def __len__(self): + raise ValueError('len failed') +d = C() +d['x'] = 1 +return d +$$; + +SELECT test1brokenlen(); + + -- test hstore[] -> python CREATE FUNCTION test1arr(val hstore[]) RETURNS int LANGUAGE plpython3u diff --git a/contrib/jsonb_plpython/expected/jsonb_plpython.out b/contrib/jsonb_plpython/expected/jsonb_plpython.out index cac963de69c9f..8d3f532880913 100644 --- a/contrib/jsonb_plpython/expected/jsonb_plpython.out +++ b/contrib/jsonb_plpython/expected/jsonb_plpython.out @@ -304,3 +304,92 @@ SELECT test_dict1(); {"": 2, "a": 1, "33": 3} (1 row) +-- A custom sequence whose __getitem__ raises should be reported as an error, +-- not crash the backend +CREATE FUNCTION test_broken_sequence() RETURNS jsonb +LANGUAGE plpython3u +TRANSFORM FOR TYPE jsonb +AS $$ +class C: + def __len__(self): + return 2 + def __getitem__(self, i): + raise ValueError('getitem failed') +return C() +$$; +SELECT test_broken_sequence(); +ERROR: could not get element 0 from sequence +DETAIL: ValueError: getitem failed +CONTEXT: Traceback (most recent call last): +while creating return value +PL/Python function "test_broken_sequence" +-- A mapping whose items() raises should be reported as an error, not crash +-- the backend +CREATE FUNCTION test_broken_mapping() RETURNS jsonb +LANGUAGE plpython3u +TRANSFORM FOR TYPE jsonb +AS $$ +class C(dict): + def items(self): + raise ValueError('items failed') +d = C() +d['x'] = 1 +return d +$$; +SELECT test_broken_mapping(); +ERROR: could not get items from Python mapping +DETAIL: ValueError: items failed +CONTEXT: Traceback (most recent call last): +while creating return value +PL/Python function "test_broken_mapping" +-- Likewise for a mapping whose items() does not return key/value pairs +CREATE FUNCTION test_malformed_mapping() RETURNS jsonb +LANGUAGE plpython3u +TRANSFORM FOR TYPE jsonb +AS $$ +class C(dict): + def items(self): + return [42] +d = C() +d['x'] = 1 +return d +$$; +SELECT test_malformed_mapping(); +ERROR: items() of a Python mapping must return key/value pairs +CONTEXT: while creating return value +PL/Python function "test_malformed_mapping" +-- Likewise for a mapping whose items() yields fewer pairs than its length +CREATE FUNCTION test_short_mapping() RETURNS jsonb +LANGUAGE plpython3u +TRANSFORM FOR TYPE jsonb +AS $$ +class C(dict): + def items(self): + return [] +d = C() +d['x'] = 1 +return d +$$; +SELECT test_short_mapping(); +ERROR: items() of a Python mapping must return key/value pairs +DETAIL: IndexError: list index out of range +CONTEXT: while creating return value +PL/Python function "test_short_mapping" +-- Likewise for a mapping whose __len__() raises +CREATE FUNCTION test_broken_len_mapping() RETURNS jsonb +LANGUAGE plpython3u +TRANSFORM FOR TYPE jsonb +AS $$ +class C(dict): + def __len__(self): + raise ValueError('len failed') +d = C() +d['x'] = 1 +return d +$$; +SELECT test_broken_len_mapping(); +ERROR: could not get size of Python mapping +DETAIL: ValueError: len failed +CONTEXT: Traceback (most recent call last): +while creating return value +PL/Python function "test_broken_len_mapping" diff --git a/contrib/jsonb_plpython/jsonb_plpython.c b/contrib/jsonb_plpython/jsonb_plpython.c index 909612a6039b1..dc17c9ee570da 100644 --- a/contrib/jsonb_plpython/jsonb_plpython.c +++ b/contrib/jsonb_plpython/jsonb_plpython.c @@ -274,7 +274,12 @@ PLyMapping_ToJsonbValue(PyObject *obj, JsonbInState *jsonb_state) PyObject *volatile items; pcount = PyMapping_Size(obj); + if (pcount < 0) + PLy_elog(ERROR, "could not get size of Python mapping"); + items = PyMapping_Items(obj); + if (items == NULL) + PLy_elog(ERROR, "could not get items from Python mapping"); PG_TRY(); { @@ -286,8 +291,15 @@ PLyMapping_ToJsonbValue(PyObject *obj, JsonbInState *jsonb_state) { JsonbValue jbvKey; PyObject *item = PyList_GetItem(items, i); - PyObject *key = PyTuple_GetItem(item, 0); - PyObject *value = PyTuple_GetItem(item, 1); + PyObject *key; + PyObject *value; + + /* The mapping's items() must yield key/value pairs */ + if (item == NULL || !PyTuple_Check(item) || PyTuple_Size(item) < 2) + PLy_elog(ERROR, "items() of a Python mapping must return key/value pairs"); + + key = PyTuple_GetItem(item, 0); + value = PyTuple_GetItem(item, 1); /* Python dictionary can have None as key */ if (key == Py_None) @@ -337,7 +349,10 @@ PLySequence_ToJsonbValue(PyObject *obj, JsonbInState *jsonb_state) for (i = 0; i < pcount; i++) { value = PySequence_GetItem(obj, i); - Assert(value); + + /* PySequence_GetItem() can return NULL, with an exception set */ + if (value == NULL) + PLy_elog(ERROR, "could not get element %d from sequence", (int) i); PLyObject_ToJsonbValue(value, jsonb_state, true); Py_XDECREF(value); diff --git a/contrib/jsonb_plpython/sql/jsonb_plpython.sql b/contrib/jsonb_plpython/sql/jsonb_plpython.sql index 29dc33279a092..fd8485c89c124 100644 --- a/contrib/jsonb_plpython/sql/jsonb_plpython.sql +++ b/contrib/jsonb_plpython/sql/jsonb_plpython.sql @@ -181,3 +181,80 @@ return x $$; SELECT test_dict1(); + +-- A custom sequence whose __getitem__ raises should be reported as an error, +-- not crash the backend +CREATE FUNCTION test_broken_sequence() RETURNS jsonb +LANGUAGE plpython3u +TRANSFORM FOR TYPE jsonb +AS $$ +class C: + def __len__(self): + return 2 + def __getitem__(self, i): + raise ValueError('getitem failed') +return C() +$$; + +SELECT test_broken_sequence(); + +-- A mapping whose items() raises should be reported as an error, not crash +-- the backend +CREATE FUNCTION test_broken_mapping() RETURNS jsonb +LANGUAGE plpython3u +TRANSFORM FOR TYPE jsonb +AS $$ +class C(dict): + def items(self): + raise ValueError('items failed') +d = C() +d['x'] = 1 +return d +$$; + +SELECT test_broken_mapping(); + +-- Likewise for a mapping whose items() does not return key/value pairs +CREATE FUNCTION test_malformed_mapping() RETURNS jsonb +LANGUAGE plpython3u +TRANSFORM FOR TYPE jsonb +AS $$ +class C(dict): + def items(self): + return [42] +d = C() +d['x'] = 1 +return d +$$; + +SELECT test_malformed_mapping(); + +-- Likewise for a mapping whose items() yields fewer pairs than its length +CREATE FUNCTION test_short_mapping() RETURNS jsonb +LANGUAGE plpython3u +TRANSFORM FOR TYPE jsonb +AS $$ +class C(dict): + def items(self): + return [] +d = C() +d['x'] = 1 +return d +$$; + +SELECT test_short_mapping(); + +-- Likewise for a mapping whose __len__() raises +CREATE FUNCTION test_broken_len_mapping() RETURNS jsonb +LANGUAGE plpython3u +TRANSFORM FOR TYPE jsonb +AS $$ +class C(dict): + def __len__(self): + raise ValueError('len failed') +d = C() +d['x'] = 1 +return d +$$; + +SELECT test_broken_len_mapping(); diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 13853b8b72079..0805c56cb1ba5 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -6335,9 +6335,10 @@ DELETE FROM ft2 WHERE c1 = 1200 RETURNING tableoid::regclass; -- Test UPDATE FOR PORTION OF UPDATE ft8 FOR PORTION OF c4 FROM '2005-01-01' TO '2006-01-01' -SET c2 = c2 + 1 -WHERE c1 = '[1,2)'; + SET c2 = c2 + 1 + WHERE c1 = '[1,2)'; -- error ERROR: foreign tables don't support FOR PORTION OF +DETAIL: "ft8" is a foreign table. SELECT * FROM ft8 WHERE c1 = '[1,2)' ORDER BY c1, c4; c1 | c2 | c3 | c4 -------+----+--------+------------------------- @@ -6346,14 +6347,49 @@ SELECT * FROM ft8 WHERE c1 = '[1,2)' ORDER BY c1, c4; -- Test DELETE FOR PORTION OF DELETE FROM ft8 FOR PORTION OF c4 FROM '2005-01-01' TO '2006-01-01' -WHERE c1 = '[2,3)'; + WHERE c1 = '[2,3)'; -- error ERROR: foreign tables don't support FOR PORTION OF +DETAIL: "ft8" is a foreign table. SELECT * FROM ft8 WHERE c1 = '[2,3)' ORDER BY c1, c4; c1 | c2 | c3 | c4 -------+----+--------+------------------------- [2,3) | 3 | AAA002 | [01-01-2000,01-01-2020) (1 row) +-- FOR PORTION OF fails if a child partition is a foreign table, even if the +-- root is not. But a child partition that is pruned doesn't cause an error. +CREATE TABLE fpo_part_parent ( + c1 int4range NOT NULL, + c2 int NOT NULL, + c3 text, + c4 daterange NOT NULL +) PARTITION BY LIST (c2); +CREATE TABLE fpo_part_local PARTITION OF fpo_part_parent FOR VALUES IN (1); +INSERT INTO fpo_part_local VALUES ('[1,2)', 1, 'one', '[2024-01-01,2024-12-31)'); +CREATE FOREIGN TABLE fpo_part_foreign + PARTITION OF fpo_part_parent FOR VALUES IN (6) + SERVER loopback OPTIONS (schema_name 'S 1', table_name 'T 5'); +DELETE FROM fpo_part_parent + FOR PORTION OF c4 FROM '2001-01-01' TO '2001-02-01' WHERE c2 = 6; -- error +ERROR: foreign tables don't support FOR PORTION OF +DETAIL: "fpo_part_foreign" is a foreign table. +UPDATE fpo_part_parent + FOR PORTION OF c4 FROM '2001-01-01' TO '2001-02-01' SET c3 = 'x' WHERE c2 = 6; -- error +ERROR: foreign tables don't support FOR PORTION OF +DETAIL: "fpo_part_foreign" is a foreign table. +UPDATE fpo_part_parent + FOR PORTION OF c4 FROM '2024-06-01' TO '2024-07-01' SET c3 = 'edited' WHERE c2 = 1; -- okay +DELETE FROM fpo_part_parent + FOR PORTION OF c4 FROM '2024-06-01' TO '2024-06-15' WHERE c2 = 1; -- okay +SELECT c1, c2, c3, c4 FROM fpo_part_local ORDER BY c4; + c1 | c2 | c3 | c4 +-------+----+--------+------------------------- + [1,2) | 1 | one | [01-01-2024,06-01-2024) + [1,2) | 1 | edited | [06-15-2024,07-01-2024) + [1,2) | 1 | one | [07-01-2024,12-31-2024) +(3 rows) + +DROP TABLE fpo_part_parent; -- Test UPDATE/DELETE with RETURNING on a three-table join INSERT INTO ft2 (c1,c2,c3) SELECT id, id - 1200, to_char(id, 'FM00000') FROM generate_series(1201, 1300) id; diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 6dbae583ecc4f..6fa45773c30d6 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -6164,12 +6164,12 @@ import_fetched_statistics(const char *schemaname, Assert(PQntuples(remstats->att) >= 1); attimport_plan = SPI_prepare(attimport_sql, ATTIMPORT_SQL_NUM_FIELDS, - (Oid *) attimport_argtypes); + attimport_argtypes); if (attimport_plan == NULL) elog(ERROR, "failed to prepare attimport_sql query"); attclear_plan = SPI_prepare(attclear_sql, ATTCLEAR_SQL_NUM_FIELDS, - (Oid *) attclear_argtypes); + attclear_argtypes); if (attclear_plan == NULL) elog(ERROR, "failed to prepare attclear_sql query"); @@ -6247,7 +6247,7 @@ import_fetched_statistics(const char *schemaname, spirc = SPI_execute_with_args(relimport_sql, RELIMPORT_SQL_NUM_FIELDS, - (Oid *) relimport_argtypes, + relimport_argtypes, values, nulls, false, 1); if (spirc != SPI_OK_SELECT) elog(ERROR, "failed to execute relimport_sql query for foreign table \"%s.%s\"", diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql index 697c4a92e2d03..8162c5496bfd2 100644 --- a/contrib/postgres_fdw/sql/postgres_fdw.sql +++ b/contrib/postgres_fdw/sql/postgres_fdw.sql @@ -1578,15 +1578,39 @@ DELETE FROM ft2 WHERE c1 = 1200 RETURNING tableoid::regclass; -- Test UPDATE FOR PORTION OF UPDATE ft8 FOR PORTION OF c4 FROM '2005-01-01' TO '2006-01-01' -SET c2 = c2 + 1 -WHERE c1 = '[1,2)'; + SET c2 = c2 + 1 + WHERE c1 = '[1,2)'; -- error SELECT * FROM ft8 WHERE c1 = '[1,2)' ORDER BY c1, c4; -- Test DELETE FOR PORTION OF DELETE FROM ft8 FOR PORTION OF c4 FROM '2005-01-01' TO '2006-01-01' -WHERE c1 = '[2,3)'; + WHERE c1 = '[2,3)'; -- error SELECT * FROM ft8 WHERE c1 = '[2,3)' ORDER BY c1, c4; +-- FOR PORTION OF fails if a child partition is a foreign table, even if the +-- root is not. But a child partition that is pruned doesn't cause an error. +CREATE TABLE fpo_part_parent ( + c1 int4range NOT NULL, + c2 int NOT NULL, + c3 text, + c4 daterange NOT NULL +) PARTITION BY LIST (c2); +CREATE TABLE fpo_part_local PARTITION OF fpo_part_parent FOR VALUES IN (1); +INSERT INTO fpo_part_local VALUES ('[1,2)', 1, 'one', '[2024-01-01,2024-12-31)'); +CREATE FOREIGN TABLE fpo_part_foreign + PARTITION OF fpo_part_parent FOR VALUES IN (6) + SERVER loopback OPTIONS (schema_name 'S 1', table_name 'T 5'); +DELETE FROM fpo_part_parent + FOR PORTION OF c4 FROM '2001-01-01' TO '2001-02-01' WHERE c2 = 6; -- error +UPDATE fpo_part_parent + FOR PORTION OF c4 FROM '2001-01-01' TO '2001-02-01' SET c3 = 'x' WHERE c2 = 6; -- error +UPDATE fpo_part_parent + FOR PORTION OF c4 FROM '2024-06-01' TO '2024-07-01' SET c3 = 'edited' WHERE c2 = 1; -- okay +DELETE FROM fpo_part_parent + FOR PORTION OF c4 FROM '2024-06-01' TO '2024-06-15' WHERE c2 = 1; -- okay +SELECT c1, c2, c3, c4 FROM fpo_part_local ORDER BY c4; +DROP TABLE fpo_part_parent; + -- Test UPDATE/DELETE with RETURNING on a three-table join INSERT INTO ft2 (c1,c2,c3) SELECT id, id - 1200, to_char(id, 'FM00000') FROM generate_series(1201, 1300) id; diff --git a/contrib/postgres_fdw/t/001_auth_scram.pl b/contrib/postgres_fdw/t/001_auth_scram.pl index c4b57cd81b38d..972faf1f5529a 100644 --- a/contrib/postgres_fdw/t/001_auth_scram.pl +++ b/contrib/postgres_fdw/t/001_auth_scram.pl @@ -75,16 +75,19 @@ { my $connstr = $node1->connstr($db0) . qq' user=$user'; - $node1->safe_psql($db0, + $node1->safe_psql( + $db0, qq'ALTER USER MAPPING FOR $user SERVER $fdw_server3 OPTIONS(add use_scram_passthrough \'false\')', - connstr => $connstr - ); + connstr => $connstr); $node1->safe_psql( $db0, qq'CREATE FOREIGN TABLE override_t (g int, col2 int) SERVER $fdw_server3 OPTIONS (table_name \'t\');', - connstr => $connstr ); - $node1->safe_psql($db0, qq'GRANT SELECT ON override_t TO $user;', connstr => $connstr); + connstr => $connstr); + $node1->safe_psql( + $db0, + qq'GRANT SELECT ON override_t TO $user;', + connstr => $connstr); my ($ret, $stdout, $stderr) = $node1->psql( $db0, @@ -92,10 +95,9 @@ connstr => $connstr); is($ret, 3, 'SCRAM passthrough disabled on user mapping should fail'); - like( - $stderr, - qr/password/i, - 'expected password-related error when scram passthrough disabled on user mapping'); + like($stderr, qr/password/i, + 'expected password-related error when scram passthrough disabled on user mapping' + ); } SKIP: diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index fa566c9e5535a..569fc0e7dbaa6 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -5842,7 +5842,7 @@ ANY num_sync ( + enable_group_by_reordering (boolean) enable_group_by_reordering configuration parameter @@ -11894,7 +11894,7 @@ dynamic_library_path = '/usr/local/lib/postgresql:$libdir' - + quote_all_identifiers (boolean) quote_all_identifiers configuration parameter diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml index 45db20d47d88d..fbd0ebbf10f40 100644 --- a/doc/src/sgml/ddl.sgml +++ b/doc/src/sgml/ddl.sgml @@ -4090,7 +4090,9 @@ VALUES ('Albany', NULL, NULL, 'NY'); similar fashion. Thus, for example, a merged column will be marked not-null if any one of the column definitions it came from is marked not-null. Check constraints are merged if they have the same name, - and the merge will fail if their conditions are different. + and the merge will fail if their conditions are different. For merged + check constraints, stricter enforceability is preserved: if any inherited + copy is enforced, the merged constraint is enforced. @@ -4104,8 +4106,9 @@ VALUES ('Albany', NULL, NULL, 'NY'); To do this the new child table must already include columns with the same names and types as the columns of the parent. It must also include check constraints with the same names and check expressions as those of the - parent. Similarly an inheritance link can be removed from a child using the - NO INHERIT variant of ALTER TABLE. + parent, as well as matching not-null constraints. Similarly an inheritance + link can be removed from a child using the NO INHERIT + variant of ALTER TABLE. Dynamically adding and removing inheritance links like this can be useful when the inheritance relationship is being used for table partitioning (see ). @@ -4124,21 +4127,23 @@ VALUES ('Albany', NULL, NULL, 'NY'); A parent table cannot be dropped while any of its children remain. Neither - can columns or check constraints of child tables be dropped or altered - if they are inherited - from any parent tables. If you wish to remove a table and all of its - descendants, one easy way is to drop the parent table with the - CASCADE option (see ). + can inherited columns or inherited check and not-null constraints of child + tables be dropped directly. Some properties of inherited constraints can + be altered, but each resulting constraint must remain compatible with all + parent constraints from which it is inherited. If you wish to remove a + table and all of its descendants, one easy way is to drop the parent table + with the CASCADE option (see ). ALTER TABLE will - propagate any changes in column data definitions and check - constraints down the inheritance hierarchy. Again, dropping - columns that are depended on by other tables is only possible when using - the CASCADE option. ALTER - TABLE follows the same rules for duplicate column merging - and rejection that apply during CREATE TABLE. + propagate changes in column definitions and in inheritable constraints + (check and not-null constraints) down the inheritance hierarchy. Again, + dropping columns that are depended on by other tables is only possible + when using the CASCADE option. ALTER + TABLE follows the same rules for merging or rejecting duplicate + inherited column and constraint definitions that apply during + CREATE TABLE. diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml index 25a85082759b4..0e7e7c8ca5377 100644 --- a/doc/src/sgml/filelist.sgml +++ b/doc/src/sgml/filelist.sgml @@ -185,7 +185,7 @@ - + diff --git a/doc/src/sgml/func/func-info.sgml b/doc/src/sgml/func/func-info.sgml index 211bc8b238b92..69ef3857cfa20 100644 --- a/doc/src/sgml/func/func-info.sgml +++ b/doc/src/sgml/func/func-info.sgml @@ -3856,9 +3856,7 @@ acl | {postgres=arwdDxtm/postgres,foo=r/postgres} reconstruct DDL statements for various global database objects. Each function returns a set of text rows, one SQL statement per row. (This is a decompiled reconstruction, not the original text of the - command.) Functions that accept VARIADIC options - take alternating name/value text pairs; values are parsed as boolean, - integer or text. + command.) @@ -3876,6 +3874,34 @@ acl | {postgres=arwdDxtm/postgres,foo=r/postgres} + + + + pg_get_database_ddl + + pg_get_database_ddl + ( database regdatabase + , pretty boolean + DEFAULT false + , owner boolean + DEFAULT true + , tablespace boolean + DEFAULT true ) + setof text + + + Reconstructs the CREATE + DATABASE statement for the specified database, + followed by ALTER DATABASE + statements for connection limit, template status, + and configuration settings. Each statement is returned as a separate + row. When pretty is true, the output is + pretty-printed. When owner is false, the + OWNER clause is omitted. When + tablespace is false, the + TABLESPACE clause is omitted. + + @@ -3883,19 +3909,22 @@ acl | {postgres=arwdDxtm/postgres,foo=r/postgres} pg_get_role_ddl ( role regrole - , VARIADIC options - text[] ) + , pretty boolean + DEFAULT false + , memberships boolean + DEFAULT true ) setof text - Reconstructs the CREATE ROLE statement and any - ALTER ROLE ... SET statements for the given role. - Each statement is returned as a separate row. + Reconstructs the CREATE ROLE + statement and any + ALTER ROLE ... SET statements for the given + role. Each statement is returned as a separate row. Password information is never included in the output. - The following options are supported: pretty (boolean) - for pretty-printed output and memberships (boolean, - default true) to include GRANT statements for - role memberships and their options. + When pretty is true, the output is + pretty-printed. When memberships is false, + GRANT statements + for role memberships are omitted. @@ -3905,48 +3934,30 @@ acl | {postgres=arwdDxtm/postgres,foo=r/postgres} pg_get_tablespace_ddl ( tablespace oid - , VARIADIC options - text[] ) + , pretty boolean + DEFAULT false + , owner boolean + DEFAULT true ) setof text pg_get_tablespace_ddl ( tablespace name - , VARIADIC options - text[] ) - setof text - - - Reconstructs the CREATE TABLESPACE statement for - the specified tablespace (by OID or name). If the tablespace has - options set, an ALTER TABLESPACE ... SET statement - is also returned. Each statement is returned as a separate row. - The following options are supported: pretty (boolean) - for formatted output and owner (boolean) to include - OWNER. - - - - - - pg_get_database_ddl - - pg_get_database_ddl - ( database regdatabase - , VARIADIC options - text[] ) + , pretty boolean + DEFAULT false + , owner boolean + DEFAULT true ) setof text - Reconstructs the CREATE DATABASE statement for the - specified database, followed by ALTER DATABASE - statements for connection limit, template status, and configuration - settings. Each statement is returned as a separate row. - The following options are supported: - pretty (boolean) for formatted output, - owner (boolean) to include OWNER, - and tablespace (boolean) to include - TABLESPACE. + Reconstructs the CREATE + TABLESPACE statement for the specified tablespace (by + OID or name). If the tablespace has options set, an + ALTER TABLESPACE ... SET + statement is also returned. Each statement is + returned as a separate row. When pretty is + true, the output is pretty-printed. When owner + is false, the OWNER clause is omitted. diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index 08d5b8245529f..05bd501c6822a 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -3359,7 +3359,7 @@ description | Waiting for a newly initialized WAL file to reach durable storage - wait_time bigint + wait_time double precision Total time spent waiting for locks of this type, in milliseconds. @@ -5526,6 +5526,25 @@ description | Waiting for a newly initialized WAL file to reach durable storage + + + + pg_stat_get_backend_lock + + pg_stat_get_backend_lock ( integer ) + setof record + + + Returns lock statistics about the backend with the specified + process ID. The output fields are exactly the same as the ones in the + pg_stat_lock view. + + + The function does not return lock statistics for the checkpointer, + the background writer, the startup process and the autovacuum launcher. + + + diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml index 67a05593140c5..ff7071bef5b4d 100644 --- a/doc/src/sgml/ref/alter_table.sgml +++ b/doc/src/sgml/ref/alter_table.sgml @@ -586,8 +586,18 @@ WITH ( MODULUS numeric_literal, REM This form alters the attributes of a constraint that was previously - created. Currently FOREIGN KEY and CHECK - constraints may be altered in this fashion, but see below. + created. Currently, the deferrability attributes can be altered only + for FOREIGN KEY constraints. The enforceability + attribute can be altered for FOREIGN KEY and + CHECK constraints. + + + + Setting a constraint to NOT ENFORCED causes the + database system to stop checking it for new or updated rows. Setting + a constraint to ENFORCED causes the database system + to verify that existing rows satisfy the constraint and to check it + for new or updated rows. @@ -1196,7 +1206,8 @@ WITH ( MODULUS numeric_literal, REM For range-partitioned tables, the ranges of merged partitions - must be adjacent in order to be merged. + must be adjacent; this applies even if the partitioned table + has no default partition. The partition bounds of merged partitions are combined to form the new partition bound for partition_name. diff --git a/doc/src/sgml/ref/delete.sgml b/doc/src/sgml/ref/delete.sgml index 9066d7ea83df8..ffdcd7fc4fafa 100644 --- a/doc/src/sgml/ref/delete.sgml +++ b/doc/src/sgml/ref/delete.sgml @@ -231,10 +231,9 @@ DELETE FROM [ ONLY ] table_name [ * from this cursor. The cursor must be a non-grouping query on the DELETE's target table. Note that WHERE CURRENT OF cannot be - specified together with a Boolean condition. See - - for more information about using cursors with - WHERE CURRENT OF. + specified together with a Boolean condition or FOR PORTION + OF. See for more information + about using cursors with WHERE CURRENT OF. diff --git a/doc/src/sgml/ref/update.sgml b/doc/src/sgml/ref/update.sgml index dd57bead90cdb..21a8fd8b0374a 100644 --- a/doc/src/sgml/ref/update.sgml +++ b/doc/src/sgml/ref/update.sgml @@ -287,10 +287,9 @@ UPDATE [ ONLY ] table_name [ * ] from this cursor. The cursor must be a non-grouping query on the UPDATE's target table. Note that WHERE CURRENT OF cannot be - specified together with a Boolean condition. See - - for more information about using cursors with - WHERE CURRENT OF. + specified together with a Boolean condition or FOR PORTION + OF. See for more information + about using cursors with WHERE CURRENT OF. diff --git a/doc/src/sgml/release-19.sgml b/doc/src/sgml/release-19.sgml deleted file mode 100644 index b0925d5f24bbd..0000000000000 --- a/doc/src/sgml/release-19.sgml +++ /dev/null @@ -1,3330 +0,0 @@ - - - - - Release 19 - - - Release date: - 2026-??-??, AS OF 2026-06-17 - - - - Overview - - - PostgreSQL 19 contains many new features - and enhancements, including: - - - - - fill in later - - - - - The above items and other new features of - PostgreSQL 19 are explained in more detail - in the sections below. - - - - - - - Migration to Version 19 - - - A dump/restore using or use of - or logical replication is required for - those wishing to migrate data from any previous release. See for general information on migrating to new - major releases. - - - - Version 19 contains a number of changes that may affect compatibility - with previous releases. Observe the following incompatibilities: - - - - - - - -Add server variable password_expiration_warning_threshold to warn about password expiration (Gilles Darold, Nathan Bossart) -§ - - - -The default warning period is seven days. - - - - - - - -Issue a warning after successful MD5 password authentication (Nathan Bossart) -§ - - - -The warning can be disabled via server variable md5_password_warnings. MD5 passwords were marked as deprecated in PostgreSQL 18. - - - - - - - -Remove RADIUS support (Thomas Munro) -§ - - - -PostgreSQL only supported RADIUS over UDP, which is unfixably insecure. - - - - - - - -Force standard_conforming_strings to always be on in the database server (Tom Lane) -§ - - - -Dumps created using pre-PostgreSQL 19 versions of pg_dump -or pg_dumpall, and using standard_conforming_strings = off, -will not properly load into PostgreSQL 19 and later servers. Users should create dumps using PostgreSQL 19 -or later versions of these applications, or use standard_conforming_strings = on. - - - -Client applications still support operations with servers having standard_conforming_strings = off, for compatibility with old servers. The server variable escape_string_warning has been removed as unnecessary. - - - - - - - -Disallow carriage returns and line feeds in database, role, and tablespace names (Mahendra Singh Thalor) -§ - - - -pg_upgrade will also disallow upgrading of clusters that use such names. This was changed to avoid security problems. - - - - - - - -Change the default index opclasses for inet and cidr data types from to GiST (Tom Lane) -§ -§ - - - -The inet/cidr opclasses are broken because they can exclude rows that should be returned. pg_upgrade will disallow upgrading of clusters with inet/cidr indexes. - - - - - - - -Stop reordering non-schema objects created by CREATE SCHEMA (Tom Lane, Jian He) -§ -§ - - - -The goal of the reordering was to avoid dependencies, but it was imperfect. PostgreSQL now uses the specified object ordering, except for foreign keys which are created last. - - - - - - - -Disallow system columns from being used in COPY FROM ... WHERE (Tom Lane) -§ - - - -The values of such columns were not well-defined. - - - - - - - -Change a json_array() call which returns no rows to return an empty JSON array (Richard Guo) -§ - - - -This previously returned NULL. - - - - - - - -Cause transactions to pass their READ ONLY and DEFERRABLE status to sessions (Etsuro Fujita) -§ - - - -This means READ ONLY transactions can no longer modify rows processed by sessions. - - - - - - - -Change default of max_locks_per_transaction from 64 to 128 (Heikki Linnakangas) -§ - - - -Lock size allocation has changed, so effectively settings must now be doubled to match their capacity in previous releases. - - - - - - - -Change JIT to be disabled by default (Jelte Fennema-Nio) -§ - - - -Previously JIT was enabled by default, and activated based on optimizer costs, but this costing has been determined to be unreliable. This change requires sites that are doing many -large analytical queries to manually enable JIT. - - - - - - - -Rename column sync_error_count to sync_table_error_count in system view pg_stat_subscription_stats (Vignesh C) -§ - - - -This is necessary since sequence errors are now tracked separately. - - - - - - - -Rename wait event type BUFFERPIN to BUFFER (Andres Freund) -§ - - - - - - - -Change index access method handlers to use a static IndexAmRoutines structure, rather than dynamically allocated ones (Matthias van de Meent) -§ - - - - - - - -Remove optimizer hook get_relation_info_hook and add better-placed hook build_simple_rel_hook (Robert Haas) -§ - - - - - - - -Remove MULE_INTERNAL encoding (Thomas Munro) -§ - - - -This encoding was complex and rarely used. Databases using it will need to be dumped and restored with a different encoding. - - - - - - - - - Changes - - - Below you will find a detailed account of the changes between - PostgreSQL 19 and the previous major - release. - - - - Server - - - Optimizer - - - - - - -Allow NOT IN clauses to be converted to more efficient ANTI JOINs when NULLs are not present (Richard Guo) -§ - - - - - - - -Allow more LEFT JOINs to be converted to ANTI JOINs (Tender Wang, Richard Guo) -§ - - - - - - - -Allow use of Memoize for ANTI JOINs with unique inner sides (Richard Guo) -§ - - - - - - - -Allow some aggregate processing to be performed before joins (Richard Guo, Antonin Houska) -§ -§ -§ - - - -This can reduce the number of rows needed to be processed. - - - - - - - -Improve hash join's handling of tuples with NULL join keys (Tom Lane) -§ - - - - - - - -Improve the planning of semijoins (Richard Guo) -§ - - - - - - - -Allow Append and MergeAppend to consider explicit incremental sorts (Richard Guo) -§ - - - - - - - -Convert IS [NOT] DISTINCT FROM NULL to IS [NOT] NULL during constant folding (Richard Guo) -§ - - - -The latter form is more easily optimized. - - - - - - - -Simplify IS [NOT] DISTINCT FROM to equality/inequality operators when inputs are proven non-nullable (Richard Guo) -§ - - - - - - - -Perform earlier constant folding of var IS [NOT] NULL in the optimizer (Richard Guo) -§ - - - -This allows for later optimizations. - - - - - - - -Simplify COALESCE() and ROW(...) IS [NOT] NULL to avoid evaluating unnecessary arguments (Richard Guo) -§ -§ - - - - - - - -Simplify IS [NOT] TRUE/FALSE/UNKNOWN to plain boolean expressions when the input is proven non-nullable (Richard Guo) -§ - - - - - - - -Speed up join selectivity computations for large optimizer statistics targets (Ilia Evdokimov, David Geier) -§ - - - - - - - -Enable proper optimizer statistics for functions returning boolean values (Tom Lane) -§ - - - - - - - -Allow extended statistics on virtual generated columns (Yugo Nagata) -§ - - - - - - - -Allow function pg_restore_extended_stats() to restore optimizer extended statistics (Corey Huinker, Michael Paquier, Chao Li) -§ -§ -§ -§ - - - - - - - -Add function pg_clear_extended_stats() to remove extended statistics (Corey Huinker, Michael Paquier) -§ - - - - - - - -Adjust the optimizer to consider startup costs of partial paths (Robert Haas, Tomas Vondra) -§ - - - - - - - -Allow negative values of pg_aggregate.aggtransspace to indicate unbounded memory usage (Richard Guo) -§ - - - -This information is used by the optimizer in planning memory usage. - - - - - - - - - General Performance - - - - - - -Improve performance of foreign key constraint checks (Junwang Zhao, Amit Langote, Chao Li) -§ -§ -§ -§ - - - - - - - -Improve asynchronous I/O read-ahead scheduling for large requests (Andres Freund) -§ -§ -§ - - - - - - - -Allow io_method method worker to automatically control needed background workers (Thomas Munro) -§ - - - -The new server variables are io_min_workers, io_max_workers, io_worker_idle_timeout, and io_worker_launch_interval. - - - - - - - -Allow query table scans to mark pages as all-visible in the visibility map (Melanie Plageman) -§ - - - -Previously only VACUUM and COPY ... FREEZE could do this. - - - - - - - -Allow autovacuum to use parallel autovacuum workers (Daniil Davydov) -§ -§ - - - -The maximum number of workers is controlled by server variable autovacuum_max_parallel_workers and per-table storage parameter autovacuum_parallel_workers. - - - - - - - -Allow TID Range Scans to be parallelized (Cary Huang, David Rowley) -§ - - - - - - - -Improve COPY FROM performance for text and CSV input using SIMD CPU instructions (Nazir Bilal Yavuz, Shinya Kato) -§ - - - - - - - -Improve NOTIFY to only wake up backends that are listening to specified notifications (Joel Jacobson) -§ - - - -Previously most backends were woken by NOTIFY. - - - - - - - -Change the default TOAST compression method from pglz to the more efficient lz4 (Euler Taveira) -§ - - - -This is done by changing the default for server variable default_toast_compression. - - - - - - - -Improve performance of internal row deformation (David Rowley) -§ - - - - - - - -Improve performance of repeated UTF-8 case-folding operations (Andreas Karlsson) -§ - - - - - - - -Improve performance of hash index bulk-deletion and GIN index vacuuming using streaming reads (Xuneng Zhou) -§ -§ - - - - - - - -Improve sort performance using radix sort (John Naylor) -§ - - - - - - - -Improve timing performance measurements (Lukas Fittl, Andres Freund, David Geier) -§ -§ - - - -This benefits EXPLAIN (ANALYZE, TIMING) and pg_test_timing, and is controlled via server variable timing_clock_source. - - - - - - - -Optimize plpgsql syntax SELECT simple-expression INTO var (Tom Lane) -§ - - - - - - - - - System Views - - - - - - -Add system view pg_stat_lock and function pg_stat_get_lock() to report per-lock-type statistics (Bertrand Drouvot) -§ - - - - - - - -Add system view pg_stat_recovery to report recovery status (Xuneng Zhou, Shinya Kato) -§ -§ - - - - - - - -Add system view pg_stat_autovacuum_scores to report per-table autovacuum details (Sami Imseih) -§ - - - - - - - -Add system view pg_dsm_registry_allocations to report dynamic shared memory details (Florents Tselai, Nathan Bossart) -§ -§ - - - - - - - -Add vacuum initiation details to system view pg_stat_progress_vacuum (Shinya Kato) -§ - - - -The new started_by column reports the initiator of the vacuum, and mode indicates its aggressiveness. - - - - - - - -Add analyze initiation details to system view pg_stat_progress_analyze (Shinya Kato) -§ - - - -The new started_by column reports the initiator of the analyze. - - - - - - - -Add mem_exceeded_count column to system view pg_stat_replication_slots (Bertrand Drouvot) -§ - - - -This reports the number of times that logical_decoding_work_mem was exceeded. - - - - - - - -Add slot synchronization skip information to pg_stat_replication_slots and pg_replication_slots (Shlok Kyal) -§ -§ -§ - - - -The new columns are slotsync_skip_count, slotsync_last_skip, and slotsync_skip_reason. - - - - - - - -Add update_deleted column to system view pg_stat_subscription_stats (Zhijie Hou) -§ - - - -This reports the number of rows where updates were ignored due to concurrent deletes. This requires the subscriber have retain_dead_tuples enabled. - - - - - - - -Add sync_seq_error_count column to system view pg_stat_subscription_stats to report sequence synchronization errors (Vignesh C) -§ -§ - - - - - - - -Add stats_reset column to system views pg_stat_all_tables, pg_stat_all_indexes, and pg_statio_all_sequences (Bertrand Drouvot, Sami Imseih, Shihao Zhong) -§ - - - -It also appears in the sys and user view variants. - - - - - - - -Add stats_reset column to system views pg_stat_user_functions and pg_stat_database_conflicts (Bertrand Drouvot, Shihao Zhong) -§ -§ - - - - - - - -Add location column to system views pg_available_extensions and pg_available_extension_versions to report the file system directory of extensions (Matheus Alcantara) -§ - - - - - - - -Add backup_type column to system view pg_stat_progress_basebackup to report the type of backup (Shinya Kato) -§ - - - -Possible values are full or incremental. - - - - - - - -Add connecting value to system view column pg_stat_wal_receiver.status (Xuneng Zhou) -§ - - - - - - - -Add reporting of the bytes written to WAL for full page images (Shinya Kato) -§ - - - -This is accessible via system view pg_stat_wal and function pg_stat_get_backend_wal(). - - - - - - - -Add columns to system views pg_stats, pg_stats_ext, and pg_stats_ext_exprs (Corey Huinker) -§ - - - -Adds table OID and attribute number columns to pg_stats, and table OID and statistics object OID columns to the other two. - - - - - - - -Add information about range type extended statistics to system view pg_stats_ext_exprs (Corey Huinker, Michael Paquier) -§ - - - - - - - - Monitoring - - - - - - -Allow log_min_messages log levels to be specified by process type (Euler Taveira) -§ - - - -The new format is type:level. A value without a colon controls all process types, allowing backward compatibility. - - - - - - - -Add server variable log_autoanalyze_min_duration to log long-running analyze operations by autovacuum operations (Shinya Kato) -§ - - - -Server variable log_autovacuum_min_duration now only controls logging of vacuum operations by autovacuum. - - - - - - - -Enable server variable log_lock_waits by default (Laurenz Albe) -§ - - - - - - - -Add server variable debug_print_raw_parse to log raw parse trees (Chao Li) -§ - - - -This is also enabled when the server is started with debug level three and higher. - - - - - - - -Make messages coming from remote servers appear in the server logs in the same format as local server messages (Vignesh C) -§ - - - -These include replication, , and servers. - - - - - - - -Add reporting of WAL full-page write bytes to VACUUM and ANALYZE logging (Shinya Kato) -§ - - - - - - - -Add IO wait events for COPY FROM/TO on a pipe, file, or program (Nikolay Samokhvalov) -§ - - - - - - - -Add wait events for WAL write and flush LSNs (Xuneng Zhou) -§ - - - - - - - -Have pg_get_sequence_data() return the sequence page LSN (Vignesh C) -§ - - - - - - - -Add function pg_get_multixact_stats() to report multixact activity (Naga Appani) -§ - - - - - - - -Issue warnings when the wraparound of xid and multi-xids is less than 100 million (Nathan Bossart) -§ - - - -The previous warning was 40 million. Warnings are issued to clients and in the server log. - - - - - - - - - Server Configuration - - - - - - -Allow online enabling and disabling of data checksums (Daniel Gustafsson, Magnus Hagander, Tomas Vondra) -§ -§ - - - -Previously the checksum status could only be changed while the cluster was offline using pg_checksums. - - - - - - - -Add scoring system to control the order that tables are processed by autovacuum (Nathan Bossart) -§ - - - -The new server variables are autovacuum_freeze_score_weight, autovacuum_multixact_freeze_score_weight, autovacuum_vacuum_score_weight, vacuum_insert_score_weight, and -autovacuum_analyze_score_weight. - - - - - - - -Add server-side support for SNI (Server Name Indication) (Daniel Gustafsson, Jacob Champion) -§ - - - -New configuration file PGDATA/pg_hosts.conf specifies hostname/key pairs. - - - - - - - -Add a new OAUTH flow hook PQAUTHDATA_OAUTH_BEARER_TOKEN_V2 (Jacob Champion) -§ -§ - - - -This is an improved version of PQAUTHDATA_OAUTH_BEARER_TOKEN by adding the issuer identifier and error message specification. - - - - - - - -Allow roles pg_read_all_data and pg_write_all_data to read/write large objects (Nitin Motiani, Nathan Bossart) -§ - - - -These roles are designed to allow non-super users to run pg_dump. - - - - - - - -Allow background workers to be configured to terminate before database-level operations (Aya Iwata) -§ - - - -This allows database-level operations to complete more quickly since blocking background workers can now be terminated. - - - - - - - -Allow server variables that represent lists to be emptied by setting the value to NULL (Tom Lane) -§ - - - - - - - -Update GB18030 encoding from version 2000 to 2022 (Chao Li, Zheng Tao) -§ - - - -See the commit message for compatibility details. - - - - - - - - - <link linkend="streaming-replication">Streaming Replication and Recovery</link> - - - - - - -Add WAIT FOR command to allow standbys to wait for LSN values to be written, flushed, or replayed (Kartyshov Ivan, Alexander Korotkov, Xuneng Zhou) -§ -§ - - - - - - - -Improve function pg_sync_replication_slots() to wait for replication synchronization completion (Ajin Cherian, Zhijie Hou) -§ - - - -Previously, certain synchronization failures would not be reported. - - - - - - - -Add server variable wal_sender_shutdown_timeout to limit replica synchronization waits during shutdown (Andrey Silitskiy, Hayato Kuroda) -§ - - - -By default, senders still wait forever for synchronization. - - - - - - - -Allow wal_receiver_timeout to be set per-subscription and user (Fujii Masao) -§ -§ - - - -This allows subscribers to use different wal_receiver_timeout values. - - - - - - - -Add optional pid parameter to pg_replication_origin_session_setup() to allow parallelization of SQL-level replication solutions (Doruk Yilmaz, Hayato Kuroda) -§ - - - - - - - - - <link linkend="logical-replication">Logical Replication</link> - - - - - - -Allow sequence values stored in subscribers to match the publisher (Vignesh C) -§ -§ -§ - - - -This is enabled during CREATE SUBSCRIPTION, ALTER SUBSCRIPTION ... REFRESH PUBLICATION, and ALTER SUBSCRIPTION ... REFRESH SEQUENCES. The latter only updates values, not sequence -existence. Function pg_get_sequence_data() allows inspection of sequence synchronization. - - - - - - - -Allow CREATE/ALTER PUBLICATION to publish all sequences (Vignesh C, Tomas Vondra) -§ - - - -This is enabled with the ALL SEQUENCES clause. - - - - - - - -Allow ALTER SUBSCRIPTION on publications to synchronize the existence of sequences on subscribers to match the publisher (Vignesh C) -§ - - - -This is enabled with the REFRESH SEQUENCES clause. - - - - - - - -Allow CREATE/ALTER PUBLICATION to exclude some tables (Vignesh C, Shlok Kyal) -§ -§ -§ -§ - - - -This is controlled with the EXCEPT clause, and is useful when specifying ALL TABLES. - - - - - - - -Add CREATE/ALTER PUBLICATION setting retain_dead_tuples to retain information needed for conflict resolution (Zhijie Hou) -§ -§ -§ - - - -Also add setting max_retention_duration to limit retain_dead_tuples retention. - - - - - - - -Allow CREATE SUBSCRIPTION to use foreign data wrapper connection parameters (Jeff Davis) -§ - - - -The connection parameters are referenced via CREATE SUBSCRIPTION ... SERVER. - - - - - - - -When server variable wal_level is replica, allow automatic enablement of logical replication when needed (Masahiko Sawada) -§ - - - -New server variable effective_wal_level reports the effective WAL level. - - - - - - - - - - - Query Commands - - - - - - -Add support for SQL Property Graph Queries (SQL/PGQ) (Peter Eisentraut, Ashutosh Bapat) -§ -§ -§ - - - -Internally these are processed like views so are written as standard relational queries. - - - - - - - -Add FOR PORTION OF clause to UPDATE and DELETE (Paul A. Jungwirth) -§ -§ - - - -This allows operations on temporal ranges. - - - - - - - -Add GROUP BY ALL syntax to SELECT to automatically group all non-aggregate and non-window-function target list parameters (David Christensen) -§ - - - - - - - -Allow GROUP BY to process target list subqueries that have expressions referencing non-subquery columns (Tom Lane) -§ - - - -Also fix a bug in how GROUPING() handles target list subquery aliases. - - - - - - - -Allow window functions to ignore NULLs with the IGNORE NULLS/RESPECT NULLS clause (Oliver Ford, Tatsuo Ishii) -§ - - - -Supported window functions are lead(), lag(), first_value(), last_value(), and nth_value(). - - - - - - - -Add support for INSERT ... ON CONFLICT DO SELECT ... RETURNING (Andreas Karlsson, Marko Tiikkaja, Viktor Holmberg) -§ - - - -This allows conflicting rows to be returned, and optionally locked with FOR UPDATE/SHARE. - - - - - - - - - Utility Commands - - - - - - -Add REPACK command which replaces VACUUM FULL and CLUSTER (Antonin Houska) -§ - - - -The two former commands did similar things, but with confusing names, so unify them as REPACK. The old commands have been retained for compatibility. - - - - - - - -Allow REPACK to rebuild tables without access-exclusive locking (Antonin Houska, Mihail Nikalayeu, Álvaro Herrera) -§ -§ -§ - - - -This is enabled via the CONCURRENTLY option. Server variable max_repack_replication_slots was also added. - - - - - - - -Allow partitions to be merged and split using ALTER TABLE ... MERGE/SPLIT PARTITIONS (Dmitry Koval, Alexander Korotkov, Tender Wang, Richard Guo, Dagfinn Ilmari Mannsåker, Fujii Masao, Jian He) -§ -§ - - - - - - - -Allow GRANT/REVOKE to specify the effective role performing the privileges adjustment (Nathan Bossart, Tom Lane) -§ - - - -The GRANTED BY clause controls this. - - - - - - - -Allow CREATE SCHEMA to create more types of objects in newly-created schemas (Kirill Reshke, Jian He, Tom Lane) -§ - - - - - - - -Allow CHECKPOINT to accept a list of options (Christoph Berg) -§ -§ -§ - - - -Supported options are MODE and FLUSH_UNLOGGED. - - - - - - - -Add CONNECTION clause to CREATE FOREIGN DATA WRAPPER to specify a function to be called for subscription connection parameters (Jeff Davis, Noriyoshi Shinoda) -§ -§ - - - - - - - -Add memory usage and parallelism reporting to VACUUM (VERBOSE) and autovacuum logs (Tatsuya Kawata, Daniil Davydov) -§ -§ - - - - - - - <link linkend="ddl-constraints">Constraints</link> - - - - - - -Allow ALTER TABLE ALTER CONSTRAINT ... [NOT] ENFORCED for CHECK constraints (Jian He) -§ - - - -Previously enforcement changes were only supported for foreign key constraints. - - - - - - - -Allow ALTER TABLE ... COLUMN SET EXPRESSION to succeed on virtual columns with CHECK constraints (Jian He) -§ - - - -This was previously prohibited. - - - - - - - - <xref linkend="sql-copy"/> - - - - - - -Allow multiple headers lines to be skipped by COPY FROM (Shinya Kato, Fujii Masao) -§ - - - -Previously only a single header line could be skipped. - - - - - - - -Allow COPY FROM to set invalid input values to NULL (Jian He, Kirill Reshke) -§ - - - -This is done using the COPY option ON_ERROR SET_NULL. - - - - - - - -Allow COPY TO to output JSON format (Joe Conway, Jian He, Andrew Dunstan) -§ -§ - - - -JSON output can also be a single JSON array using the COPY option FORCE_ARRAY. - - - - - - - -Allow COPY TO to process partitioned tables (Jian He, Ajin Cherian) -§ -§ - - - -Previously COPY (SELECT ...) had to be used to output partitioned tables. This also improves logical replication table synchronization. - - - - - - - - <xref linkend="sql-explain"/> - - - - - - -Add EXPLAIN ANALYZE option IO to report asynchronous IO activity (Tomas Vondra) -§ -§ -§ - - - - - - - -Add reporting of WAL full-page write bytes to EXPLAIN (ANALYZE, WAL) output (Shinya Kato) -§ - - - - - - - -Add Memoize cache and lookup estimates to EXPLAIN output (Ilia Evdokimov, Lukas Fittl) -§ - - - -This can show why Memoize was chosen. - - - - - - - - - - - Data Types - - - - - - -Add the 64-bit unsigned data type oid8 (Michael Paquier) -§ - - - - - - - -Add more jsonpath string methods (Florents Tselai, David E. Wheeler) -§ - - - -They are ltrim(), rtrim(), btrim(), lower(), upper(), initcap(), replace(), and split_part(). These are immutable like their non-JSON string variants. - - - - - - - -Allow casts between bytea and uuid data types (Dagfinn Ilmari Mannsåker, Aleksander Alekseev) -§ - - - - - - - -Add ability to cast between database names and oid8s using regdatabase (Ian Lawrence Barwick) -§ - - - - - - - -Add functions tid_block() and tid_offset() to extract block numbers and offsets from tid values (Ayush Tiwari) -§ - - - - - - - - - Functions - - - - - - -Add date, timestamp, and timestamptz versions of random(min, max) (Damien Clochard, Dean Rasheed) -§ -§ - - - - - - - -Allow encode() and decode() to process data in base64url and base32hex formats (Andrey Borodin, Aleksander Alekseev, Florents Tselai) -§ -§ -§ - - - -This format retains ordering, unlike base32. - - - - - - - -Add functions to return a set of ranges resulting from range subtraction (Paul A. Jungwirth) -§ - - - -The functions are range_minus_multi() and multirange_minus_multi(). This is useful to represent range subtraction results with gaps. - - - - - - - -Add function error_on_null() to return the supplied parameter, or error on NULL input (Joel Jacobson) -§ - - - - - - - -Allow IS JSON to work on domains defined over supported base types (Jian He) -§ - - - -The supported base types are TEXT, JSON, JSONB, and BYTEA. - - - - - - - -Add full text stemmers for Polish and Esperanto (Tom Lane) -§ - - - -The Dutch stemmer has also been updated. The old Dutch stemmer is available via dutch_porter. - - - - - - - -Add function pg_get_role_ddl() to output role creation commands (Mario Gonzalez, Bryan Green, Andrew Dunstan, Euler Taveira) -§ - - - - - - - -Add function pg_get_tablespace_ddl() to output tablespace creation commands (Nishant Sharma, Manni Wood, Andrew Dunstan, Euler Taveira) -§ - - - - - - - -Add function pg_get_database_ddl() to output database creation commands (Akshay Joshi, Andrew Dunstan, Euler Taveira) -§ - - - - - - - -Allow event triggers to be written using PL/Python (Euler Taveira, Dimitri Fontaine) -§ - - - - - - - - - <link linkend="libpq">Libpq</link> - - - - - - -Allow libpq connections to specify a service file via servicefile (Torsten Förtsch, Ryo Kanbayashi) -§ - - - - - - - -Add special libpq protocol version 3.9999 for version testing (Jelte Fennema-Nio) -§ - - - - - - - -Add libpq function PQgetThreadLock() to retrieve the current locking callback (Jacob Champion) -§ - - - - - - - -Add libpq connection parameter oauth_ca_file to specify the OAUTH certificate authority file (Jonathan Gonzalez V., Jacob Champion) -§ - - - -This can also be set via the PGOAUTHCAFILE environment variable. The default is to use curl's built-in certificates. - - - - - - - -Allow custom OAUTH validators to register custom pg_hba.conf authentication options (Jacob Champion) -§ - - - - - - - -Allow OAUTH validators to supply failure details (Jacob Champion) -§ - - - -This is done by setting the ValidatorModuleResult structure member error_detail. - - - - - - - -Allow libpq environment variable PGOAUTHDEBUG to specify particular debug options (Zsolt Parragi, Jacob Champion) -§ - - - -The UNSAFE option still generates all debugging output. - - - - - - - - - <xref linkend="app-psql"/> - - - - - - -Allow the search path to appear in the psql prompt via %S (Florents Tselai) -§ - - - -This works when psql is connected to PostgreSQL 18 or later. - - - - - - - -Allow the hot standby status to appear in the psql prompt via %i (Jim Jones) -§ - - - - - - - -Modify psql backslash commands to show comments for publications, subscriptions, and extended statistics (Fujii Masao, Jim Jones) -§ - - - -The modified commands are \dRp+, \dRs+, and \dX+. - - - - - - - -Allow control over how booleans are displayed in psql (David G. Johnston) -§ - - - -The \pset variables are display_true and display_false. - - - - - - - -Add psql variable SERVICEFILE to reference the service file location (Ryo Kanbayashi) -§ - - - - - - - -Allow psql to more accurately determine if the pager is needed (Erik Wienhold) -§ - - - - - - - -Add or improve psql tab completion (Yamaguchi Atsuo, Yugo Nagata, Haruna Miwa, Xuneng Zhou, Dagfinn Ilmari Mannsåker, Fujii Masao, Álvaro Herrera, Jian He, Tatsuya Kawata, Ian Lawrence Barwick, Vasuki M) -§ -§ -§ -§ -§ -§ -§ -§ -§ -§ -§ -§ -§ -§ - - - - - - - - - Server Applications - - - - - - -Change vacuumdb's and options to analyze partitioned tables when no targets are specified (Laurenz Albe, Mircea Cadariu, Chao Li) -§ -§ - - - -Previously it skipped partitioned tables. This now matches the behavior of ANALYZE. - - - - - - - -Allow vacuumdb to report its commands without running them using option (Corey Huinker) -§ - - - - - - - -Allow pg_verifybackup to read WAL files stored in tar archives (Amul Sul) -§ - - - -Add option as an alias for the existing and deprecated option. - - - - - - - -Allow pg_waldump to read WAL files stored in tar archives (Amul Sul) -§ - - - - - - - -Improve performance of pg_upgrade copying large object metadata (Nathan Bossart) -§ -§ -§ -§ - - - -Various methods are used, depending on the PostgreSQL version of the old cluster. - - - - - - - -Allow pg_upgrade to process non-default tablespaces stored in the PGDATA directory (Nathan Bossart) -§ - - - -Previously such tablespaces generated an error. - - - - - - - -Add pgbench option to continue after SQL errors (Rintaro Ikeda, Yugo Nagata, Fujii Masao) -§ - - - - - - - -Improve the usability of pg_test_timing (Hannu Krosing, Tom Lane) -§ -§ - - - -Report nanoseconds instead of microseconds. In addition to histogram output, output a second table that reports exact timings, with an optional cutoff set by . - - - - - - - <link - linkend="app-pgdump"><application>pg_dump</application></link>/<link - linkend="app-pg-dumpall"><application>pg_dumpall</application></link>/<link - linkend="app-pgrestore"><application>pg_restore</application></link> - - - - - - -Allow pg_dump to include restorable extended statistics (Corey Huinker) -§ - - - - - - - - - <link linkend="app-pgcreatesubscriber"><application>pg_createsubscriber</application></link> - - - - - - -Allow pg_createsubscriber to ignore specified publications that already exist (Shubham Khanna) -§ - - - -Previously this generated an error. - - - - - - - -Change the way pg_createsubscriber stores recovery parameters (Alyona Vinter) -§ - - - -Changes are stored in optionally-included pg_createsubscriber.conf rather than directly in postgresql.auto.conf. - - - - - - - -Add pg_createsubscriber option / to redirect output to files (Gyan Sreejith, Hayato Kuroda) -§ - - - - - - - - - - - Source Code - - - - - - -Restore support for AIX (Aditya Kamath, Srirama Kucherlapati, Peter Eisentraut) -§ -§ - - - -This uses gcc and only supports 64-bit builds. - - - - - - - -Change Solaris to use unnamed POSIX semaphores (Tom Lane) -§ - - - -Previously it used System V semaphores. - - - - - - - -Require Visual Studio 2019 or later (Peter Eisentraut) -§ - - - - - - - -Allow MSVC to create PL/Python using the Python Limited API (Bryan Green) -§ - - - - - - - -Allow building on AArch64 using MSVC (Niyas Sait, Greg Burd, Dave Cramer) -§ - - - - - - - -Allow execution stack backtraces on Windows using DbgHelp (Bryan Green) -§ - - - - - - - -Change the supported C language version to C11 (Peter Eisentraut) -§ -§ - - - -Previously C99 was used. - - - - - - - -Use standard C23 and C++ attributes if available (Peter Eisentraut) -§ - - - - - - - -Use AVX2 CPU instructions for calculating page checksums (Matthew Sterrett, Andrew Kim) -§ - - - - - - - -Use ARM Crypto Extension to Compute CRC32C (John Naylor) -§ - - - - - - - -Change hex_encode() and hex_decode() to use SIMD CPU instructions (Nathan Bossart, Chiranmoy Bhattacharya) -§ - - - - - - - -Require Meson version 0.57.2 or later (Peter Eisentraut) -§ - - - - - - - -Add Meson option to build both shared and static libraries, or only shared (Peter Eisentraut) -§ - - - - - - - -Update Unicode data to version 17.0.0 (Peter Eisentraut) -§ - - - - - - - -Add hooks planner_setup_hook, planner_shutdown_hook, joinrel_setup_hook, and join_path_setup_hook (Robert Haas) -§ -§ - - - - - - - -Allow extensions to replace set-returning functions in the FROM clause with SQL queries (Paul A. Jungwirth) -§ - - - - - - - -Make multixid members 64-bit (Maxim Orlov) -§ - - - - - - - -Change function prototypes to use uint* instead of bit* typedefs (Nathan Bossart) -§ - - - - - - - -Allow logical decoding plugins to specify if they do not access shared catalogs (Antonin Houska) -§ - - - - - - - -Add simplified and improved shared memory registration function ShmemRequestStruct() (Heikki Linnakangas, Ashutosh Bapat) -§ - - - -Functions ShmemInitStruct() and ShmemInitHash() remain for backward compatibility. - - - - - - - -Add server variable debug_exec_backend to report how parameters are passed to new backends (Daniel Gustafsson) -§ - - - - - - - -Add documentation section about temporal tables (Paul A. Jungwirth) -§ - - - - - - - -Document the environment variables that control the regression tests (Michael Paquier) -§ - - - - - - - -Update documented systemd example to include a restart setting (Andrew Jackson) -§ - - - - - - - - - Additional Modules - - - - - - -Add module to stabilize and control planner decisions (Robert Haas) -§ -§ - - - - - - - -Add extension to allow per-query-id advice to be specified (Robert Haas, Lukas Fittl) -§ -§ - - - - - - - -Show sizes of FETCH queries as constants in (Sami Imseih) -§ - - - -Fetches of different sizes will now be grouped together in output. - - - - - - - -Add generic and custom plan counts to (Sami Imseih) -§ - - - - - - - -Refactor reporting of shared memory mapping (Bertrand Drouvot) -§ - - - -New function pg_buffercache_os_pages() and system view pg_buffercache_os_pages allow reporting of shared memory mapping; the function optionally includes NUMA details. Function -pg_buffercache_numa_pages() remains for backward compatibility. - - - - - - - -Add functions to to mark buffers as dirty (Nazir Bilal Yavuz) -§ - - - -The functions are pg_buffercache_mark_dirty(), pg_buffercache_mark_dirty_relation(), and pg_buffercache_mark_dirty_all(). - - - - - - - -Allow pushdown of array comparisons in prepared statements to foreign servers (Alexander Pyhalov) -§ - - - - - - - -Allow the retrieval of statistics from foreign data wrapper servers (Corey Huinker, Etsuro Fujita) -§ - - - -This is enabled for by using the option restore_stats. The default is for ANALYZE to retrieve rows from the remote server to locally generate statistics. - - - - - - - -Allow to read files or program output that uses multi-line headers (Shinya Kato) -§ - - - - - - - -Add server variable auto_explain.log_io to add IO reporting to auto_explain (Tomas Vondra) -§ - - - - - - - -Allow auto_explain to add extension-specific EXPLAIN options via server variable auto_explain.log_extension_options (Robert Haas) -§ - - - - - - - -Change to support all btree-supported cross-type comparisons (Tom Lane) -§ -§ - - - - - - - -Improve performance of indexes by using streaming reads (Xuneng Zhou) -§ -§ - - - - - - - -Improve performance of by using streaming reads (Xuneng Zhou) -§ -§ - - - - - - - -Allow 's dmetaphone() to use single-byte encodings beyond ASCII (Peter Eisentraut) -§ - - - - - - - -Modify oid2name to report the relation file path (David Bidoc) -§ - - - - - - - - - - - Acknowledgments - - - The following individuals (in alphabetical order) have contributed - to this release as patch authors, committers, reviewers, testers, - or reporters of issues. - - - - fill in later - - - - diff --git a/doc/src/sgml/release-20.sgml b/doc/src/sgml/release-20.sgml new file mode 100644 index 0000000000000..5fe36c1d7d263 --- /dev/null +++ b/doc/src/sgml/release-20.sgml @@ -0,0 +1,16 @@ + + + + + Release 20 + + + Release date: + 2026-??-?? + + + + This is just a placeholder for now. + + + diff --git a/doc/src/sgml/release.sgml b/doc/src/sgml/release.sgml index ae6cc3cc2cb57..9d861a0f8c0e9 100644 --- a/doc/src/sgml/release.sgml +++ b/doc/src/sgml/release.sgml @@ -70,7 +70,7 @@ For new features, add links to the documentation sections. All the active branches have to be edited concurrently when doing that. --> -&release-19; +&release-20; Prior Releases diff --git a/doc/src/sgml/spi.sgml b/doc/src/sgml/spi.sgml index e30d0962ae761..5846fcceb8976 100644 --- a/doc/src/sgml/spi.sgml +++ b/doc/src/sgml/spi.sgml @@ -845,7 +845,7 @@ int SPI_execute_extended(const char *command, int SPI_execute_with_args(const char *command, - int nargs, Oid *argtypes, + int nargs, const Oid *argtypes, const Datum *values, const char *nulls, bool read_only, long count) @@ -997,7 +997,7 @@ int SPI_execute_with_args(const char *command, -SPIPlanPtr SPI_prepare(const char * command, int nargs, Oid * argtypes) +SPIPlanPtr SPI_prepare(const char * command, int nargs, const Oid * argtypes) @@ -1160,7 +1160,7 @@ SPIPlanPtr SPI_prepare(const char * command, int SPIPlanPtr SPI_prepare_cursor(const char * command, int nargs, - Oid * argtypes, int cursorOptions) + const Oid * argtypes, int cursorOptions) @@ -2086,7 +2086,7 @@ int SPI_execute_plan_with_paramlist(SPIPlanPtr plan, -int SPI_execp(SPIPlanPtr plan, Datum * values, const char * nulls, long count) +int SPI_execp(SPIPlanPtr plan, const Datum * values, const char * nulls, long count) @@ -2191,7 +2191,7 @@ int SPI_execp(SPIPlanPtr plan, Datum * values< Portal SPI_cursor_open(const char * name, SPIPlanPtr plan, - Datum * values, const char * nulls, + const Datum * values, const char * nulls, bool read_only) @@ -2316,7 +2316,7 @@ Portal SPI_cursor_open(const char * name, SPIPlanPtr Portal SPI_cursor_open_with_args(const char *name, const char *command, - int nargs, Oid *argtypes, + int nargs, const Oid *argtypes, const Datum *values, const char *nulls, bool read_only, int cursorOptions) @@ -4694,7 +4694,7 @@ HeapTupleHeader SPI_returntuple(HeapTuple row, TupleDesc HeapTuple SPI_modifytuple(Relation rel, HeapTuple row, int ncols, - int * colnum, Datum * values, const char * nulls) + const int * colnum, const Datum * values, const char * nulls) diff --git a/meson.build b/meson.build index 568e0e150bfa8..d88a7a7030878 100644 --- a/meson.build +++ b/meson.build @@ -8,7 +8,7 @@ project('postgresql', ['c'], - version: '19beta1', + version: '20devel', license: 'PostgreSQL', # We want < 0.62 for python 3.6 compatibility on old platforms. diff --git a/src/backend/access/hash/hash_xlog.c b/src/backend/access/hash/hash_xlog.c index 2060620c7dec9..e9a2b9aa9a723 100644 --- a/src/backend/access/hash/hash_xlog.c +++ b/src/backend/access/hash/hash_xlog.c @@ -29,7 +29,6 @@ hash_xlog_init_meta_page(XLogReaderState *record) XLogRecPtr lsn = record->EndRecPtr; Page page; Buffer metabuf; - ForkNumber forknum; xl_hash_init_meta_page *xlrec = (xl_hash_init_meta_page *) XLogRecGetData(record); @@ -41,16 +40,7 @@ hash_xlog_init_meta_page(XLogReaderState *record) page = BufferGetPage(metabuf); PageSetLSN(page, lsn); MarkBufferDirty(metabuf); - - /* - * Force the on-disk state of init forks to always be in sync with the - * state in shared buffers. See XLogReadBufferForRedoExtended. We need - * special handling for init forks as create index operations don't log a - * full page image of the metapage. - */ - XLogRecGetBlockTag(record, 0, NULL, &forknum, NULL); - if (forknum == INIT_FORKNUM) - FlushOneBuffer(metabuf); + XLogFlushBufferForRedoIfInit(record, 0, metabuf); /* all done */ UnlockReleaseBuffer(metabuf); @@ -68,7 +58,6 @@ hash_xlog_init_bitmap_page(XLogReaderState *record) Page page; HashMetaPage metap; uint32 num_buckets; - ForkNumber forknum; xl_hash_init_bitmap_page *xlrec = (xl_hash_init_bitmap_page *) XLogRecGetData(record); @@ -79,16 +68,7 @@ hash_xlog_init_bitmap_page(XLogReaderState *record) _hash_initbitmapbuffer(bitmapbuf, xlrec->bmsize, true); PageSetLSN(BufferGetPage(bitmapbuf), lsn); MarkBufferDirty(bitmapbuf); - - /* - * Force the on-disk state of init forks to always be in sync with the - * state in shared buffers. See XLogReadBufferForRedoExtended. We need - * special handling for init forks as create index operations don't log a - * full page image of the metapage. - */ - XLogRecGetBlockTag(record, 0, NULL, &forknum, NULL); - if (forknum == INIT_FORKNUM) - FlushOneBuffer(bitmapbuf); + XLogFlushBufferForRedoIfInit(record, 0, bitmapbuf); UnlockReleaseBuffer(bitmapbuf); /* add the new bitmap page to the metapage's list of bitmaps */ @@ -109,10 +89,7 @@ hash_xlog_init_bitmap_page(XLogReaderState *record) PageSetLSN(page, lsn); MarkBufferDirty(metabuf); - - XLogRecGetBlockTag(record, 1, NULL, &forknum, NULL); - if (forknum == INIT_FORKNUM) - FlushOneBuffer(metabuf); + XLogFlushBufferForRedoIfInit(record, 1, metabuf); } if (BufferIsValid(metabuf)) UnlockReleaseBuffer(metabuf); diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index de4cf96eaa2ca..3a89149016fe6 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -2514,6 +2514,7 @@ CommitTransaction(void) AtEOXact_Files(true); AtEOXact_ComboCid(); AtEOXact_HashTables(true); + AtEOXact_RI(true); AtEOXact_PgStat(true, is_parallel_worker); AtEOXact_Snapshot(true, false); AtEOXact_ApplyLauncher(true); @@ -2809,6 +2810,7 @@ PrepareTransaction(void) AtEOXact_Files(true); AtEOXact_ComboCid(); AtEOXact_HashTables(true); + AtEOXact_RI(true); /* don't call AtEOXact_PgStat here; we fixed pgstat state above */ AtEOXact_Snapshot(true, true); /* we treat PREPARE as ROLLBACK so far as waking workers goes */ @@ -3039,6 +3041,7 @@ AbortTransaction(void) AtEOXact_Files(false); AtEOXact_ComboCid(); AtEOXact_HashTables(false); + AtEOXact_RI(false); AtEOXact_PgStat(false, is_parallel_worker); AtEOXact_ApplyLauncher(false); AtEOXact_LogicalRepWorkers(false); diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c index fdc341d8fa48b..d8c179c5dccb2 100644 --- a/src/backend/access/transam/xlogutils.c +++ b/src/backend/access/transam/xlogutils.c @@ -321,6 +321,28 @@ XLogInitBufferForRedo(XLogReaderState *record, uint8 block_id) return buf; } +/* + * If a redo routine modified an init fork, flush the buffer immediately. + * + * At the end of crash recovery the init forks of unlogged relations are + * copied to the main fork directly from disk, without going through shared + * buffers. Therefore, redo routines that update init forks without + * restoring a full-page image must call this after setting the page LSN and + * marking the buffer dirty. + */ +void +XLogFlushBufferForRedoIfInit(XLogReaderState *record, uint8 block_id, + Buffer buffer) +{ + ForkNumber forknum; + + Assert(BufferIsValid(buffer)); + + XLogRecGetBlockTag(record, block_id, NULL, &forknum, NULL); + if (forknum == INIT_FORKNUM) + FlushOneBuffer(buffer); +} + /* * XLogReadBufferForRedoExtended * Like XLogReadBufferForRedo, but with extra options. @@ -398,7 +420,9 @@ XLogReadBufferForRedoExtended(XLogReaderState *record, * At the end of crash recovery the init forks of unlogged relations * are copied, without going through shared buffers. So we need to * force the on-disk state of init forks to always be in sync with the - * state in shared buffers. + * state in shared buffers. Use XLogFlushBufferForRedoIfInit() for + * redo routines that dirty init-fork buffers without restoring a + * full-page image. */ if (forknum == INIT_FORKNUM) FlushOneBuffer(*buf); diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 0087585b2c4e1..80a527ed4c642 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -921,7 +921,7 @@ CopyFrom(CopyFromState cstate) ExecInitResultRelation(estate, resultRelInfo, 1); /* Verify the named relation is a valid target for INSERT */ - CheckValidResultRel(resultRelInfo, CMD_INSERT, ONCONFLICT_NONE, NIL); + CheckValidResultRel(resultRelInfo, CMD_INSERT, ONCONFLICT_NONE, NIL, NULL); ExecOpenIndices(resultRelInfo, false); diff --git a/src/backend/commands/copyto.c b/src/backend/commands/copyto.c index 6755bb698de73..d3adc752ae30c 100644 --- a/src/backend/commands/copyto.c +++ b/src/backend/commands/copyto.c @@ -1051,15 +1051,19 @@ BeginCopyTo(ParseState *pstate, { cstate->json_buf = makeStringInfo(); - if (rel && list_length(cstate->attnumlist) < tupDesc->natts) + /* + * Build a projected TupleDesc describing only the selected columns so + * that composite_to_json() emits the right column names and types; + * needed when an explicit column list was given (possibly with a + * different column order) or when generated columns are excluded from + * the output. + */ + if (rel && (attnamelist != NIL || + list_length(cstate->attnumlist) < tupDesc->natts)) { int natts = list_length(cstate->attnumlist); TupleDesc resultDesc; - /* - * Build a TupleDesc describing only the selected columns so that - * composite_to_json() emits the right column names and types. - */ resultDesc = CreateTemplateTupleDesc(natts); foreach_int(attnum, cstate->attnumlist) diff --git a/src/backend/commands/sequence_xlog.c b/src/backend/commands/sequence_xlog.c index d0aed48e26801..fcb3230cf3be0 100644 --- a/src/backend/commands/sequence_xlog.c +++ b/src/backend/commands/sequence_xlog.c @@ -63,6 +63,7 @@ seq_redo(XLogReaderState *record) memcpy(page, localpage, BufferGetPageSize(buffer)); MarkBufferDirty(buffer); + XLogFlushBufferForRedoIfInit(record, 0, buffer); UnlockReleaseBuffer(buffer); pfree(localpage); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 33e065d61ce00..472db112fa7a0 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -437,6 +437,7 @@ static bool ATExecAlterFKConstrEnforceability(List **wqueue, ATAlterConstraint * static bool ATExecAlterCheckConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, HeapTuple contuple, bool recurse, bool recursing, + List *changing_conids, LOCKMODE lockmode); static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, @@ -459,6 +460,7 @@ static void AlterFKConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint static void AlterCheckConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Oid conrelid, bool recurse, bool recursing, + List *changing_conids, LOCKMODE lockmode); static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, @@ -466,6 +468,10 @@ static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cm List **otherrelids, LOCKMODE lockmode); static void AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel, HeapTuple contuple); +static bool ATCheckCheckConstrHasEnforcedParent(Relation conrel, Relation rel, + HeapTuple contuple, + List *changing_conids, + Oid *enforced_parentoid); static ObjectAddress ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName, bool recurse, bool recursing, LOCKMODE lockmode); @@ -477,6 +483,7 @@ static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relat static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode); +static bool constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc); static int transformColumnNameList(Oid relId, List *colList, int16 *attnums, Oid *atttypids, Oid *attcollids); static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid, @@ -12459,7 +12466,7 @@ ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, else if (currcon->contype == CONSTRAINT_CHECK) changed = ATExecAlterCheckConstrEnforceability(wqueue, cmdcon, conrel, contuple, recurse, false, - lockmode); + NIL, lockmode); } else if (cmdcon->alterDeferrability && ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, rel, @@ -12646,12 +12653,16 @@ ATExecAlterFKConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon, static bool ATExecAlterCheckConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, HeapTuple contuple, - bool recurse, bool recursing, LOCKMODE lockmode) + bool recurse, bool recursing, + List *changing_conids, + LOCKMODE lockmode) { Form_pg_constraint currcon; Relation rel; bool changed = false; List *children = NIL; + bool target_enforced = cmdcon->is_enforced; + Oid enforced_parentoid = InvalidOid; /* Since this function recurses, it could be driven to stack overflow */ check_stack_depth(); @@ -12668,16 +12679,57 @@ ATExecAlterCheckConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon, */ rel = table_open(currcon->conrelid, NoLock); - if (currcon->conenforced != cmdcon->is_enforced) + /* + * When setting a constraint to NOT ENFORCED, check whether any matching + * parent constraint remains ENFORCED and is not part of this ALTER. + * + * For a direct ALTER of an inherited constraint, reject the command, + * because the child cannot be weakened while its parent remains enforced. + * + * During recursion, another parent outside this ALTER may still enforce + * the same constraint in a regular inheritance hierarchy. In that case, + * keep the child constraint ENFORCED so that its merged enforceability + * still reflects the remaining enforced parent. Partitions do not need + * this recursive parent check because a partition can have only one + * direct parent. + */ + if (!cmdcon->is_enforced && + (!recursing || !rel->rd_rel->relispartition) && + ATCheckCheckConstrHasEnforcedParent(conrel, rel, contuple, + changing_conids, + &enforced_parentoid)) { - AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple); + if (!recursing) + ereport(ERROR, + errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("cannot mark inherited constraint \"%s\" as %s", + NameStr(currcon->conname), + "NOT ENFORCED"), + errdetail("The matching constraint on parent table \"%s\" is %s.", + get_rel_name(enforced_parentoid), "ENFORCED")); + + target_enforced = true; + } + + /* + * Update to the merged enforceability if needed. This may differ from the + * requested enforceability when another matching parent constraint + * remains enforced. + */ + if (currcon->conenforced != target_enforced) + { + ATAlterConstraint updatecon = *cmdcon; + + updatecon.is_enforced = target_enforced; + AlterConstrUpdateConstraintEntry(&updatecon, conrel, contuple); changed = true; } /* * Note that we must recurse even when trying to change a check constraint * to not enforced if it is already not enforced, in case descendant - * constraints might be enforced and need to be changed to not enforced. + * constraints might be enforced and need to be changed to not enforced, + * unless they still inherit an enforced constraint from another parent. * Conversely, we should do nothing if a constraint is being set to * enforced and is already enforced, as descendant constraints cannot be * different in that case. @@ -12690,28 +12742,66 @@ ATExecAlterCheckConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon, * try to look for it in the children. */ if (!recursing && !currcon->connoinherit) + { + Assert(changing_conids == NIL); + children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL); + /* + * When setting NOT ENFORCED, build the set of equivalent CHECK + * constraints that this command will attempt to change before + * visiting descendants. The root itself has already been checked + * above. + */ + if (!cmdcon->is_enforced) + changing_conids = list_make1_oid(currcon->oid); + + foreach_oid(childoid, children) + { + if (childoid == RelationGetRelid(rel)) + continue; + + /* + * If we are told not to recurse, there had better not be any + * child tables, because we can't change constraint + * enforceability on the parent unless we have changed + * enforceability for all child. + */ + if (!recurse) + ereport(ERROR, + errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("constraint must be altered on child tables too"), + errhint("Do not specify the ONLY keyword.")); + + /* + * It is sufficient to look up the constraint by name here. + * Supported DDL ensures that inheritable CHECK constraints + * with the same name have equivalent definitions when they + * are propagated to children or when inheritance is + * established. All descendants returned by + * find_all_inheritors must have this constraint: inherited + * CHECK constraints propagate to all children at + * inheritance-link creation time and cannot be dropped + * independently on child tables. + */ + if (!cmdcon->is_enforced) + changing_conids = + list_append_unique_oid(changing_conids, + get_relation_constraint_oid(childoid, + cmdcon->conname, + false)); + } + } + foreach_oid(childoid, children) { if (childoid == RelationGetRelid(rel)) continue; - /* - * If we are told not to recurse, there had better not be any - * child tables, because we can't change constraint enforceability - * on the parent unless we have changed enforceability for all - * child. - */ - if (!recurse) - ereport(ERROR, - errcode(ERRCODE_INVALID_TABLE_DEFINITION), - errmsg("constraint must be altered on child tables too"), - errhint("Do not specify the ONLY keyword.")); - AlterCheckConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, childoid, false, true, + changing_conids, lockmode); } } @@ -12723,7 +12813,7 @@ ATExecAlterCheckConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon, */ if (rel->rd_rel->relkind == RELKIND_RELATION && !currcon->conenforced && - cmdcon->is_enforced) + target_enforced) { AlteredTableInfo *tab; NewConstraint *newcon; @@ -12763,6 +12853,7 @@ static void AlterCheckConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Oid conrelid, bool recurse, bool recursing, + List *changing_conids, LOCKMODE lockmode) { SysScanDesc pscan; @@ -12792,11 +12883,138 @@ AlterCheckConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon, cmdcon->conname, get_rel_name(conrelid))); ATExecAlterCheckConstrEnforceability(wqueue, cmdcon, conrel, childtup, - recurse, recursing, lockmode); + recurse, recursing, changing_conids, + lockmode); systable_endscan(pscan); } +/* + * When setting an inherited CHECK constraint to NOT ENFORCED, look for a + * matching parent constraint that remains ENFORCED and is not part of the same + * ALTER. + */ +static bool +ATCheckCheckConstrHasEnforcedParent(Relation conrel, Relation rel, + HeapTuple contuple, + List *changing_conids, + Oid *enforced_parentoid) +{ + Form_pg_constraint currcon; + Relation inhrel; + SysScanDesc scan; + ScanKeyData skey; + HeapTuple inheritsTuple; + + /* Since this function recurses, it could be driven to stack overflow */ + check_stack_depth(); + + currcon = (Form_pg_constraint) GETSTRUCT(contuple); + Assert(currcon->contype == CONSTRAINT_CHECK); + + if (currcon->coninhcount <= 0) + return false; + + inhrel = table_open(InheritsRelationId, AccessShareLock); + + ScanKeyInit(&skey, + Anum_pg_inherits_inhrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(RelationGetRelid(rel))); + scan = systable_beginscan(inhrel, InheritsRelidSeqnoIndexId, + true, NULL, 1, &skey); + + while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan))) + { + Oid parentoid; + Relation parentrel = NULL; + SysScanDesc pscan; + ScanKeyData pkey[3]; + HeapTuple parenttup; + + parentoid = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhparent; + + ScanKeyInit(&pkey[0], + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(parentoid)); + ScanKeyInit(&pkey[1], + Anum_pg_constraint_contypid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(InvalidOid)); + ScanKeyInit(&pkey[2], + Anum_pg_constraint_conname, + BTEqualStrategyNumber, F_NAMEEQ, + NameGetDatum(&currcon->conname)); + + pscan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, + true, NULL, 3, pkey); + + /* + * ConstraintRelidTypidNameIndexId is unique on (conrelid, contypid, + * conname), so this loop body executes at most once per parent. + */ + while (HeapTupleIsValid(parenttup = systable_getnext(pscan))) + { + Form_pg_constraint parentcon; + + parentcon = (Form_pg_constraint) GETSTRUCT(parenttup); + + if (parentcon->contype != CONSTRAINT_CHECK || + parentcon->connoinherit || + !parentcon->conenforced) + continue; + + if (!constraints_equivalent(parenttup, contuple, + RelationGetDescr(conrel))) + elog(ERROR, "child table \"%s\" has different definition for check constraint \"%s\"", + RelationGetRelationName(rel), + NameStr(parentcon->conname)); + + /* + * A parent listed in changing_conids is being changed by the same + * ALTER, but it may not have been updated yet. For regular + * inheritance, recurse upward to check whether an equivalent + * enforced parent outside the ALTER will make it remain enforced. + * Partitions cannot have multiple parents, so they do not need + * this check. + */ + if (!rel->rd_rel->relispartition && + list_member_oid(changing_conids, parentcon->oid)) + { + Oid parent_enforced_parentoid = InvalidOid; + + if (parentrel == NULL) + parentrel = table_open(parentoid, NoLock); + + if (!ATCheckCheckConstrHasEnforcedParent(conrel, + parentrel, + parenttup, + changing_conids, + &parent_enforced_parentoid)) + continue; + } + + *enforced_parentoid = parentoid; + if (parentrel != NULL) + table_close(parentrel, NoLock); + systable_endscan(pscan); + systable_endscan(scan); + table_close(inhrel, AccessShareLock); + return true; + } + + if (parentrel != NULL) + table_close(parentrel, NoLock); + systable_endscan(pscan); + } + + systable_endscan(scan); + table_close(inhrel, AccessShareLock); + + return false; +} + /* * Returns true if the constraint's deferrability is altered. * diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 4b30f7686801a..c3af96989ba3e 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1063,7 +1063,8 @@ InitPlan(QueryDesc *queryDesc, int eflags) */ void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation, - OnConflictAction onConflictAction, List *mergeActions) + OnConflictAction onConflictAction, List *mergeActions, + ModifyTable *mtnode) { Relation resultRel = resultRelInfo->ri_RelationDesc; FdwRoutine *fdwroutine; @@ -1126,6 +1127,14 @@ CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation, RelationGetRelationName(resultRel)))); break; case RELKIND_FOREIGN_TABLE: + /* We don't support FOR PORTION OF FDW queries. */ + if (mtnode && mtnode->forPortionOf) + ereport(ERROR, + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("foreign tables don't support FOR PORTION OF"), + errdetail("\"%s\" is a foreign table.", + RelationGetRelationName(resultRel))); + /* Okay only if the FDW supports it */ fdwroutine = resultRelInfo->ri_FdwRoutine; switch (operation) diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index d96d4f9947b79..33ec5bfde4c4d 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -368,7 +368,7 @@ ExecFindPartition(ModifyTableState *mtstate, /* Verify this ResultRelInfo allows INSERTs */ CheckValidResultRel(rri, CMD_INSERT, node ? node->onConflictAction : ONCONFLICT_NONE, - NIL); + NIL, node); /* * Initialize information needed to insert this and @@ -594,7 +594,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, * required when the operation is CMD_UPDATE. */ CheckValidResultRel(leaf_part_rri, CMD_INSERT, - node ? node->onConflictAction : ONCONFLICT_NONE, NIL); + node ? node->onConflictAction : ONCONFLICT_NONE, NIL, node); /* * Open partition indices. The user may have asked to check for conflicts diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 846dc516b4366..c333d7139fae0 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -5312,7 +5312,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) * Verify result relation is a valid target for the current operation */ CheckValidResultRel(resultRelInfo, operation, node->onConflictAction, - mergeActions); + mergeActions, node); resultRelInfo++; i++; diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 52f3b11301c55..23a8395766c30 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -68,7 +68,7 @@ static int _SPI_execute_plan(SPIPlanPtr plan, const SPIExecuteOptions *options, Snapshot snapshot, Snapshot crosscheck_snapshot, bool fire_triggers); -static ParamListInfo _SPI_convert_params(int nargs, Oid *argtypes, +static ParamListInfo _SPI_convert_params(int nargs, const Oid *argtypes, const Datum *Values, const char *Nulls); static int _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount); @@ -702,7 +702,7 @@ SPI_execute_plan(SPIPlanPtr plan, const Datum *Values, const char *Nulls, /* Obsolete version of SPI_execute_plan */ int -SPI_execp(SPIPlanPtr plan, Datum *Values, const char *Nulls, long tcount) +SPI_execp(SPIPlanPtr plan, const Datum *Values, const char *Nulls, long tcount) { return SPI_execute_plan(plan, Values, Nulls, false, tcount); } @@ -811,7 +811,7 @@ SPI_execute_snapshot(SPIPlanPtr plan, */ int SPI_execute_with_args(const char *src, - int nargs, Oid *argtypes, + int nargs, const Oid *argtypes, const Datum *Values, const char *Nulls, bool read_only, long tcount) { @@ -858,13 +858,13 @@ SPI_execute_with_args(const char *src, } SPIPlanPtr -SPI_prepare(const char *src, int nargs, Oid *argtypes) +SPI_prepare(const char *src, int nargs, const Oid *argtypes) { return SPI_prepare_cursor(src, nargs, argtypes, 0); } SPIPlanPtr -SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes, +SPI_prepare_cursor(const char *src, int nargs, const Oid *argtypes, int cursorOptions) { _SPI_plan plan; @@ -1104,8 +1104,8 @@ SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc) } HeapTuple -SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum, - Datum *Values, const char *Nulls) +SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, const int *attnum, + const Datum *Values, const char *Nulls) { MemoryContext oldcxt; HeapTuple mtuple; @@ -1472,8 +1472,8 @@ SPI_cursor_open(const char *name, SPIPlanPtr plan, Portal SPI_cursor_open_with_args(const char *name, const char *src, - int nargs, Oid *argtypes, - Datum *Values, const char *Nulls, + int nargs, const Oid *argtypes, + const Datum *Values, const char *Nulls, bool read_only, int cursorOptions) { Portal result; @@ -2846,7 +2846,7 @@ _SPI_execute_plan(SPIPlanPtr plan, const SPIExecuteOptions *options, * Convert arrays of query parameters to form wanted by planner and executor */ static ParamListInfo -_SPI_convert_params(int nargs, Oid *argtypes, +_SPI_convert_params(int nargs, const Oid *argtypes, const Datum *Values, const char *Nulls) { ParamListInfo paramLI; @@ -3170,8 +3170,11 @@ _SPI_make_plan_non_temp(SPIPlanPtr plan) newplan->nargs = plan->nargs; if (plan->nargs > 0) { - newplan->argtypes = palloc_array(Oid, plan->nargs); - memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid)); + Oid *newplan_argtypes; + + newplan_argtypes = palloc_array(Oid, plan->nargs); + memcpy(newplan_argtypes, plan->argtypes, plan->nargs * sizeof(Oid)); + newplan->argtypes = newplan_argtypes; } else newplan->argtypes = NULL; @@ -3235,8 +3238,11 @@ _SPI_save_plan(SPIPlanPtr plan) newplan->nargs = plan->nargs; if (plan->nargs > 0) { - newplan->argtypes = palloc_array(Oid, plan->nargs); - memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid)); + Oid *newplan_argtypes; + + newplan_argtypes = palloc_array(Oid, plan->nargs); + memcpy(newplan_argtypes, plan->argtypes, plan->nargs * sizeof(Oid)); + newplan->argtypes = newplan_argtypes; } else newplan->argtypes = NULL; diff --git a/src/backend/nodes/queryjumblefuncs.c b/src/backend/nodes/queryjumblefuncs.c index 7c63766a51c5d..9e5b241e6ff4e 100644 --- a/src/backend/nodes/queryjumblefuncs.c +++ b/src/backend/nodes/queryjumblefuncs.c @@ -39,7 +39,7 @@ #include "access/transam.h" #include "catalog/pg_proc.h" -#include "common/hashfn.h" +#include "common/hashfn_unstable.h" #include "common/int.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" @@ -48,8 +48,6 @@ #include "parser/scanner.h" #include "parser/scansup.h" -#define JUMBLE_SIZE 1024 /* query serialization buffer size */ - /* GUC parameters */ int compute_query_id = COMPUTE_QUERY_ID_AUTO; @@ -186,8 +184,7 @@ InitJumble(void) jstate = palloc_object(JumbleState); /* Set up workspace for query jumbling */ - jstate->jumble = (unsigned char *) palloc(JUMBLE_SIZE); - jstate->jumble_len = 0; + fasthash_init(&jstate->jumble, 0); jstate->clocations_buf_size = 32; jstate->clocations = (LocationLen *) palloc(jstate->clocations_buf_size * sizeof(LocationLen)); @@ -221,14 +218,18 @@ DoJumble(JumbleState *jstate, Node *node) if (jstate->has_squashed_lists) jstate->highest_extern_param_id = 0; - /* Process the jumble buffer and produce the hash value */ - return DatumGetInt64(hash_any_extended(jstate->jumble, - jstate->jumble_len, - 0)); + /* + * Produce the final hash; we pass zero as the tweak since we already + * emitted the length following each variable-length value + */ + return (int64) fasthash_final64(&jstate->jumble, 0); } /* - * AppendJumbleInternal: Internal function for appending to the jumble buffer + * AppendJumbleInternal: Internal function for appending to the jumble state + * + * Accumulates the given value into the streaming fasthash state, processing + * it in 8-byte chunks. * * Note: Callers must ensure that size > 0. */ @@ -236,59 +237,24 @@ static pg_attribute_always_inline void AppendJumbleInternal(JumbleState *jstate, const unsigned char *item, Size size) { - unsigned char *jumble = jstate->jumble; - Size jumble_len = jstate->jumble_len; + Size orig_size PG_USED_FOR_ASSERTS_ONLY = size; /* Ensure the caller didn't mess up */ Assert(size > 0); - /* - * Fast path for when there's enough space left in the buffer. This is - * worthwhile as means the memcpy can be inlined into very efficient code - * when 'size' is a compile-time constant. - */ - if (likely(size <= JUMBLE_SIZE - jumble_len)) + while (size >= FH_SIZEOF_ACCUM) { - memcpy(jumble + jumble_len, item, size); - jstate->jumble_len += size; - -#ifdef USE_ASSERT_CHECKING - jstate->total_jumble_len += size; -#endif - - return; + fasthash_accum(&jstate->jumble, (const char *) item, FH_SIZEOF_ACCUM); + item += FH_SIZEOF_ACCUM; + size -= FH_SIZEOF_ACCUM; } - /* - * Whenever the jumble buffer is full, we hash the current contents and - * reset the buffer to contain just that hash value, thus relying on the - * hash to summarize everything so far. - */ - do - { - Size part_size; - - if (unlikely(jumble_len >= JUMBLE_SIZE)) - { - int64 start_hash; - - start_hash = DatumGetInt64(hash_any_extended(jumble, - JUMBLE_SIZE, 0)); - memcpy(jumble, &start_hash, sizeof(start_hash)); - jumble_len = sizeof(start_hash); - } - part_size = Min(size, JUMBLE_SIZE - jumble_len); - memcpy(jumble + jumble_len, item, part_size); - jumble_len += part_size; - item += part_size; - size -= part_size; + if (size > 0) + fasthash_accum(&jstate->jumble, (const char *) item, size); #ifdef USE_ASSERT_CHECKING - jstate->total_jumble_len += part_size; + jstate->total_jumble_len += orig_size; #endif - } while (size > 0); - - jstate->jumble_len = jumble_len; } /* @@ -302,6 +268,12 @@ AppendJumble(JumbleState *jstate, const unsigned char *value, Size size) FlushPendingNulls(jstate); AppendJumbleInternal(jstate, value, size); + + /* + * Fold in the length so variable-length values are self-delimiting, which + * lets DoJumble finalize with a zero tweak (see hashfn_unstable.h). + */ + fasthash_accum(&jstate->jumble, (const char *) &size, sizeof(size)); } /* @@ -369,9 +341,31 @@ AppendJumble64(JumbleState *jstate, const unsigned char *value) AppendJumbleInternal(jstate, value, 8); } +/* + * AppendJumbleString + * Add the given NUL-terminated string to the jumble state. + * + * Length is computed on the fly while hashing, avoiding a separate strlen. + */ +static pg_noinline void +AppendJumbleString(JumbleState *jstate, const char *str) +{ + Size size; + + if (jstate->pending_nulls > 0) + FlushPendingNulls(jstate); + + size = fasthash_accum_cstring(&jstate->jumble, str); + fasthash_accum(&jstate->jumble, (const char *) &size, sizeof(size)); + +#ifdef USE_ASSERT_CHECKING + jstate->total_jumble_len += size; +#endif +} + /* * FlushPendingNulls - * Incorporate the pending_nulls value into the jumble buffer. + * Incorporate the pending_nulls value into the jumble hash. * * Note: Callers must ensure that there's at least 1 pending NULL. */ @@ -549,7 +543,7 @@ do { \ #define JUMBLE_STRING(str) \ do { \ if (expr->str) \ - AppendJumble(jstate, (const unsigned char *) (expr->str), strlen(expr->str) + 1); \ + AppendJumbleString(jstate, expr->str); \ else \ AppendJumbleNull(jstate); \ } while(0) @@ -600,7 +594,7 @@ _jumbleNode(JumbleState *jstate, Node *node) break; } - /* Ensure we added something to the jumble buffer */ + /* Ensure we added something to the jumble */ Assert(jstate->total_jumble_len > prev_jumble_len); } diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index f4689e7c9f8bf..846bd7c1fbe6a 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -1081,6 +1081,18 @@ subquery_planner(PlannerGlobal *glob, Query *parse, char *plan_name, /* exclRelTlist contains only Vars, so no preprocessing needed */ } + if (parse->forPortionOf) + { + parse->forPortionOf->targetRange = + preprocess_expression(root, + parse->forPortionOf->targetRange, + EXPRKIND_TARGET); + if (contain_volatile_functions(parse->forPortionOf->targetRange)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("FOR PORTION OF bounds cannot contain volatile functions"))); + } + foreach(l, parse->mergeActionList) { MergeAction *action = (MergeAction *) lfirst(l); diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 93fa66ae57ce7..2932d17a107b8 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -81,6 +81,7 @@ static OnConflictExpr *transformOnConflictClause(ParseState *pstate, static ForPortionOfExpr *transformForPortionOfClause(ParseState *pstate, int rtindex, const ForPortionOfClause *forPortionOf, + const Node *whereClause, bool isUpdate); static int count_rowexpr_columns(ParseState *pstate, Node *expr); static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt, @@ -626,6 +627,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) qry->forPortionOf = transformForPortionOfClause(pstate, qry->resultRelation, stmt->forPortionOf, + stmt->whereClause, false); qual = transformWhereClause(pstate, stmt->whereClause, @@ -1319,6 +1321,7 @@ static ForPortionOfExpr * transformForPortionOfClause(ParseState *pstate, int rtindex, const ForPortionOfClause *forPortionOf, + const Node *whereClause, bool isUpdate) { Relation targetrel = pstate->p_target_relation; @@ -1335,11 +1338,11 @@ transformForPortionOfClause(ParseState *pstate, ForPortionOfExpr *result; Var *rangeVar; - /* We don't support FOR PORTION OF FDW queries. */ - if (targetrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) + /* disallow FOR PORTION OF ... WHERE CURRENT OF */ + if (whereClause && IsA(whereClause, CurrentOfExpr)) ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("foreign tables don't support FOR PORTION OF"))); + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("WHERE CURRENT OF with FOR PORTION OF is not implemented")); result = makeNode(ForPortionOfExpr); @@ -1489,9 +1492,6 @@ transformForPortionOfClause(ParseState *pstate, args, InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL); } - if (contain_volatile_functions_after_planning((Expr *) result->targetRange)) - ereport(ERROR, - (errmsg("FOR PORTION OF bounds cannot contain volatile functions"))); /* * Build overlapsExpr to use as an extra qual. This means we only hit rows @@ -2884,6 +2884,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->forPortionOf = transformForPortionOfClause(pstate, qry->resultRelation, stmt->forPortionOf, + stmt->whereClause, true); nsitem = pstate->p_target_nsitem; diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index fb306c05112e7..a9b6be7203b3d 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -526,7 +526,6 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, errmsg("%s is not an ordered-set aggregate, so it cannot have WITHIN GROUP", NameListToString(funcname)), parser_errposition(pstate, location))); - } if (ignore_nulls != NO_NULLTREATMENT) @@ -2701,9 +2700,6 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location) break; case EXPR_KIND_WINDOW_PARTITION: case EXPR_KIND_WINDOW_ORDER: - /* okay, these are effectively GROUP BY/ORDER BY */ - pstate->p_hasTargetSRFs = true; - break; case EXPR_KIND_WINDOW_FRAME_RANGE: case EXPR_KIND_WINDOW_FRAME_ROWS: case EXPR_KIND_WINDOW_FRAME_GROUPS: diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index e9aaf24c1bec8..127bd9e7da3cc 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -3632,10 +3632,11 @@ check_av_worker_gucs(void) if (autovacuum_worker_slots < autovacuum_max_workers) ereport(WARNING, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("\"autovacuum_max_workers\" (%d) should be less than or equal to \"autovacuum_worker_slots\" (%d)", - autovacuum_max_workers, autovacuum_worker_slots), - errdetail("The server will only start up to \"autovacuum_worker_slots\" (%d) autovacuum workers at a given time.", - autovacuum_worker_slots))); + errmsg("\"%s\" (%d) should be less than or equal to \"%s\" (%d)", + "autovacuum_max_workers", autovacuum_max_workers, + "autovacuum_worker_slots", autovacuum_worker_slots), + errdetail("The server will only start up to \"%s\" (%d) autovacuum workers at a given time.", + "autovacuum_worker_slots", autovacuum_worker_slots))); } /* diff --git a/src/backend/statistics/extended_stats_funcs.c b/src/backend/statistics/extended_stats_funcs.c index 2cb3942056f9a..a5dce8a220641 100644 --- a/src/backend/statistics/extended_stats_funcs.c +++ b/src/backend/statistics/extended_stats_funcs.c @@ -1033,7 +1033,7 @@ jbv_to_infunc_datum(JsonbValue *jval, PGFunction func, AttrNumber exprnum, } /* - * Build an array datum with element type elemtypid from a text datum, used as + * Build an array datum with element type typid from a text datum, used as * value of an attribute in a pg_statistic tuple. * * If an error is encountered, capture it, and reduce the elevel to WARNING. @@ -1044,7 +1044,6 @@ static Datum array_in_safe(FmgrInfo *array_in, const char *s, Oid typid, int32 typmod, AttrNumber exprnum, const char *element_name, bool *ok) { - LOCAL_FCINFO(fcinfo, 3); Datum result; ErrorSaveContext escontext = { @@ -1053,17 +1052,6 @@ array_in_safe(FmgrInfo *array_in, const char *s, Oid typid, int32 typmod, }; *ok = false; - InitFunctionCallInfoData(*fcinfo, array_in, 3, InvalidOid, - (Node *) &escontext, NULL); - - fcinfo->args[0].value = CStringGetDatum(s); - fcinfo->args[0].isnull = false; - fcinfo->args[1].value = ObjectIdGetDatum(typid); - fcinfo->args[1].isnull = false; - fcinfo->args[2].value = Int32GetDatum(typmod); - fcinfo->args[2].isnull = false; - - result = FunctionCallInvoke(fcinfo); /* * If the array_in function returned an error, we will want to report that @@ -1071,7 +1059,8 @@ array_in_safe(FmgrInfo *array_in, const char *s, Oid typid, int32 typmod, * Overwriting the existing hint (if any) is not ideal, and an error * context would only work for level >= ERROR. */ - if (escontext.error_occurred) + if (!InputFunctionCallSafe(array_in, (char *) s, typid, typmod, + (Node *) &escontext, &result)) { StringInfoData hint_str; diff --git a/src/backend/statistics/stat_utils.c b/src/backend/statistics/stat_utils.c index a673e3c704b25..0b190e8823703 100644 --- a/src/backend/statistics/stat_utils.c +++ b/src/backend/statistics/stat_utils.c @@ -555,7 +555,7 @@ statatt_get_elem_type(Oid atttypid, char atttyptype, } /* - * Build an array with element type elemtypid from a text datum, used as + * Build an array with element type typid from a text datum, used as * value of an attribute in a tuple to-be-inserted into pg_statistic. * * The typid and typmod should be derived from a previous call to @@ -569,7 +569,6 @@ Datum statatt_build_stavalues(const char *staname, FmgrInfo *array_in, Datum d, Oid typid, int32 typmod, bool *ok) { - LOCAL_FCINFO(fcinfo, 8); char *s; Datum result; ErrorSaveContext escontext = {T_ErrorSaveContext}; @@ -578,28 +577,18 @@ statatt_build_stavalues(const char *staname, FmgrInfo *array_in, Datum d, Oid ty s = TextDatumGetCString(d); - InitFunctionCallInfoData(*fcinfo, array_in, 3, InvalidOid, - (Node *) &escontext, NULL); - - fcinfo->args[0].value = CStringGetDatum(s); - fcinfo->args[0].isnull = false; - fcinfo->args[1].value = ObjectIdGetDatum(typid); - fcinfo->args[1].isnull = false; - fcinfo->args[2].value = Int32GetDatum(typmod); - fcinfo->args[2].isnull = false; - - result = FunctionCallInvoke(fcinfo); - - pfree(s); - - if (escontext.error_occurred) + if (!InputFunctionCallSafe(array_in, s, typid, typmod, + (Node *) &escontext, &result)) { + pfree(s); escontext.error_data->elevel = WARNING; ThrowErrorData(escontext.error_data); *ok = false; return (Datum) 0; } + pfree(s); + if (ARR_NDIM(DatumGetArrayTypeP(result)) != 1) { ereport(WARNING, diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index d6c0cc1f6d489..f79a8fa5da21b 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -6715,24 +6715,7 @@ LockBufferForCleanup(Buffer buffer) { /* Successfully acquired exclusive lock with pincount 1 */ UnlockBufHdr(bufHdr); - - /* - * Emit the log message if recovery conflict on buffer pin was - * resolved but the startup process waited longer than - * deadlock_timeout for it. - */ - if (logged_recovery_conflict) - LogRecoveryConflict(RECOVERY_CONFLICT_BUFFERPIN, - waitStart, GetCurrentTimestamp(), - NULL, false); - - if (waiting) - { - /* reset ps display to remove the suffix if we added one */ - set_ps_display_remove_suffix(); - waiting = false; - } - return; + goto cleanup_lock_acquired; } /* Failed, so mark myself as waiting for pincount 1 */ if (buf_state & BM_PIN_COUNT_WAITER) @@ -6743,9 +6726,32 @@ LockBufferForCleanup(Buffer buffer) } bufHdr->wait_backend_pgprocno = MyProcNumber; PinCountWaitBuf = bufHdr; - UnlockBufHdrExt(bufHdr, buf_state, - BM_PIN_COUNT_WAITER, 0, - 0); + + /* + * Publish BM_PIN_COUNT_WAITER while retaining the buffer header lock. + * The shared refcount can be decremented while BM_LOCKED is set, so + * use an atomic operation that preserves concurrent refcount changes. + */ + pg_atomic_fetch_or_u64(&bufHdr->state, BM_PIN_COUNT_WAITER); + + /* + * Recheck the refcount after publishing the waiter flag, while shared + * refcount increments are still prevented by BM_LOCKED. If only our + * pin remains, the cleanup-lock condition has already been satisfied, + * so remove the waiter state and return without sleeping. + */ + buf_state = pg_atomic_read_u64(&bufHdr->state); + + if (BUF_STATE_GET_REFCOUNT(buf_state) == 1) + { + UnlockBufHdrExt(bufHdr, buf_state, + 0, BM_PIN_COUNT_WAITER, + 0); + PinCountWaitBuf = NULL; + goto cleanup_lock_acquired; + } + + UnlockBufHdr(bufHdr); LockBuffer(buffer, BUFFER_LOCK_UNLOCK); /* Wait to be signaled by UnpinBuffer() */ @@ -6816,6 +6822,26 @@ LockBufferForCleanup(Buffer buffer) PinCountWaitBuf = NULL; /* Loop back and try again */ } + +cleanup_lock_acquired: + + /* + * Emit the log message if recovery conflict on buffer pin was resolved + * but the startup process waited longer than deadlock_timeout for it. + */ + if (logged_recovery_conflict) + LogRecoveryConflict(RECOVERY_CONFLICT_BUFFERPIN, + waitStart, GetCurrentTimestamp(), + NULL, false); + + if (waiting) + { + /* reset ps display to remove the suffix if we added one */ + set_ps_display_remove_suffix(); + waiting = false; + } + + return; } /* diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index 6fa9de33e1ca2..7d01c981a1f68 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -1608,12 +1608,13 @@ ProcSleep(LOCALLOCK *locallock) TimestampDifference(get_timeout_start_time(DEADLOCK_TIMEOUT), GetCurrentTimestamp(), &secs, &usecs); - msecs = secs * 1000 + usecs / 1000; - usecs = usecs % 1000; - /* Increment the lock statistics counters if done waiting. */ if (myWaitStatus == PROC_WAIT_STATUS_OK) - pgstat_count_lock_waits(locallock->tag.lock.locktag_type, msecs); + pgstat_count_lock_waits(locallock->tag.lock.locktag_type, + (PgStat_Counter) secs * 1000000 + usecs); + + msecs = secs * 1000 + usecs / 1000; + usecs = usecs % 1000; if (log_lock_waits) { diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c index 73461c9bca590..b736b2ccc6ff3 100644 --- a/src/backend/utils/activity/pgstat_backend.c +++ b/src/backend/utils/activity/pgstat_backend.c @@ -39,6 +39,7 @@ */ static PgStat_BackendPending PendingBackendStats; static bool backend_has_iostats = false; +static bool backend_has_lockstats = false; /* * WAL usage counters saved from pgWalUsage at the previous call to @@ -86,6 +87,39 @@ pgstat_count_backend_io_op(IOObject io_object, IOContext io_context, pgstat_report_fixed = true; } +/* + * Utility routines to report lock stats for backends, kept here to avoid + * exposing PendingBackendStats to the outside world. + */ +void +pgstat_count_backend_lock_waits(uint8 locktag_type, PgStat_Counter usecs) +{ + if (!pgstat_tracks_backend_bktype(MyBackendType)) + return; + + Assert(locktag_type <= LOCKTAG_LAST_TYPE); + + PendingBackendStats.pending_lock.stats[locktag_type].waits++; + PendingBackendStats.pending_lock.stats[locktag_type].wait_time += usecs; + + backend_has_lockstats = true; + pgstat_report_fixed = true; +} + +void +pgstat_count_backend_lock_fastpath_exceeded(uint8 locktag_type) +{ + if (!pgstat_tracks_backend_bktype(MyBackendType)) + return; + + Assert(locktag_type <= LOCKTAG_LAST_TYPE); + + PendingBackendStats.pending_lock.stats[locktag_type].fastpath_exceeded++; + + backend_has_lockstats = true; + pgstat_report_fixed = true; +} + /* * Returns statistics of a backend by proc number. */ @@ -262,6 +296,36 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref) prevBackendWalUsage = pgWalUsage; } +/* + * Flush out locally pending backend lock statistics. Locking is managed + * by the caller. + */ +static void +pgstat_flush_backend_entry_lock(PgStat_EntryRef *entry_ref) +{ + PgStatShared_Backend *shbackendent; + PgStat_PendingLock *bktype_shstats; + + if (!backend_has_lockstats) + return; + + shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats; + bktype_shstats = &shbackendent->stats.lock_stats; + + for (int i = 0; i <= LOCKTAG_LAST_TYPE; i++) + { +#define LOCKSTAT_ACC(fld) \ + (bktype_shstats->stats[i].fld += PendingBackendStats.pending_lock.stats[i].fld) + LOCKSTAT_ACC(waits); + LOCKSTAT_ACC(wait_time); + LOCKSTAT_ACC(fastpath_exceeded); +#undef LOCKSTAT_ACC + } + + MemSet(&PendingBackendStats.pending_lock, 0, sizeof(PgStat_PendingLock)); + backend_has_lockstats = false; +} + /* * Flush out locally pending backend statistics * @@ -286,6 +350,10 @@ pgstat_flush_backend(bool nowait, uint32 flags) pgstat_backend_wal_have_pending()) has_pending_data = true; + /* Some lock data pending? */ + if ((flags & PGSTAT_BACKEND_FLUSH_LOCK) && backend_has_lockstats) + has_pending_data = true; + if (!has_pending_data) return false; @@ -301,6 +369,9 @@ pgstat_flush_backend(bool nowait, uint32 flags) if (flags & PGSTAT_BACKEND_FLUSH_WAL) pgstat_flush_backend_entry_wal(entry_ref); + if (flags & PGSTAT_BACKEND_FLUSH_LOCK) + pgstat_flush_backend_entry_lock(entry_ref); + pgstat_unlock_entry(entry_ref); return false; @@ -339,6 +410,7 @@ pgstat_create_backend(ProcNumber procnum) MemSet(&PendingBackendStats, 0, sizeof(PgStat_BackendPending)); backend_has_iostats = false; + backend_has_lockstats = false; /* * Initialize prevBackendWalUsage with pgWalUsage so that diff --git a/src/backend/utils/activity/pgstat_lock.c b/src/backend/utils/activity/pgstat_lock.c index aec64f8fb4b63..c20c7599683cc 100644 --- a/src/backend/utils/activity/pgstat_lock.c +++ b/src/backend/utils/activity/pgstat_lock.c @@ -131,6 +131,8 @@ pgstat_count_lock_fastpath_exceeded(uint8 locktag_type) PendingLockStats.stats[locktag_type].fastpath_exceeded++; have_lockstats = true; pgstat_report_fixed = true; + + pgstat_count_backend_lock_fastpath_exceeded(locktag_type); } /* @@ -140,11 +142,13 @@ pgstat_count_lock_fastpath_exceeded(uint8 locktag_type) * like lock acquisitions. */ void -pgstat_count_lock_waits(uint8 locktag_type, long msecs) +pgstat_count_lock_waits(uint8 locktag_type, PgStat_Counter usecs) { Assert(locktag_type <= LOCKTAG_LAST_TYPE); PendingLockStats.stats[locktag_type].waits++; - PendingLockStats.stats[locktag_type].wait_time += (PgStat_Counter) msecs; + PendingLockStats.stats[locktag_type].wait_time += usecs; have_lockstats = true; pgstat_report_fixed = true; + + pgstat_count_backend_lock_waits(locktag_type, usecs); } diff --git a/src/backend/utils/adt/ddlutils.c b/src/backend/utils/adt/ddlutils.c index f32fcd453efa3..a70f1c2865501 100644 --- a/src/backend/utils/adt/ddlutils.c +++ b/src/backend/utils/adt/ddlutils.c @@ -33,7 +33,6 @@ #include "mb/pg_wchar.h" #include "miscadmin.h" #include "utils/acl.h" -#include "utils/array.h" #include "utils/builtins.h" #include "utils/datetime.h" #include "utils/fmgroids.h" @@ -46,35 +45,6 @@ #include "utils/timestamp.h" #include "utils/varlena.h" -/* Option value types for DDL option parsing */ -typedef enum -{ - DDL_OPT_BOOL, - DDL_OPT_TEXT, - DDL_OPT_INT, -} DdlOptType; - -/* - * A single DDL option descriptor: caller fills in name and type, - * parse_ddl_options fills in isset + the appropriate value field. - */ -typedef struct DdlOption -{ - const char *name; /* option name (case-insensitive match) */ - DdlOptType type; /* expected value type */ - bool isset; /* true if caller supplied this option */ - /* fields for specific option types */ - union - { - bool boolval; /* filled in for DDL_OPT_BOOL */ - char *textval; /* filled in for DDL_OPT_TEXT (palloc'd) */ - int intval; /* filled in for DDL_OPT_INT */ - }; -} DdlOption; - - -static void parse_ddl_options(FunctionCallInfo fcinfo, int variadic_start, - DdlOption *opts, int nopts); static void append_ddl_option(StringInfo buf, bool pretty, int indent, const char *fmt, ...) pg_attribute_printf(4, 5); @@ -83,150 +53,11 @@ static void append_guc_value(StringInfo buf, const char *name, static List *pg_get_role_ddl_internal(Oid roleid, bool pretty, bool memberships); static List *pg_get_tablespace_ddl_internal(Oid tsid, bool pretty, bool no_owner); -static Datum pg_get_tablespace_ddl_srf(FunctionCallInfo fcinfo, Oid tsid, bool isnull); +static Datum pg_get_tablespace_ddl_srf(FunctionCallInfo fcinfo, Oid tsid); static List *pg_get_database_ddl_internal(Oid dbid, bool pretty, bool no_owner, bool no_tablespace); -/* - * parse_ddl_options - * Parse variadic name/value option pairs - * - * Options are passed as alternating key/value text pairs. The caller - * provides an array of DdlOption descriptors specifying the accepted - * option names and their types; this function matches each supplied - * pair against the array, validates the value, and fills in the - * result fields. - */ -static void -parse_ddl_options(FunctionCallInfo fcinfo, int variadic_start, - DdlOption *opts, int nopts) -{ - Datum *args; - bool *nulls; - Oid *types; - int nargs; - - /* Clear all output fields */ - for (int i = 0; i < nopts; i++) - { - opts[i].isset = false; - switch (opts[i].type) - { - case DDL_OPT_BOOL: - opts[i].boolval = false; - break; - case DDL_OPT_TEXT: - opts[i].textval = NULL; - break; - case DDL_OPT_INT: - opts[i].intval = 0; - break; - } - } - - nargs = extract_variadic_args(fcinfo, variadic_start, true, - &args, &types, &nulls); - - if (nargs <= 0) - return; - - /* Handle DEFAULT NULL case */ - if (nargs == 1 && nulls[0]) - return; - - if (nargs % 2 != 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("variadic arguments must be name/value pairs"), - errhint("Provide an even number of variadic arguments that can be divided into pairs."))); - - /* - * For each option name/value pair, find corresponding positional option - * for the option name, and assign the option value. - */ - for (int i = 0; i < nargs; i += 2) - { - char *name; - char *valstr; - DdlOption *opt = NULL; - - if (nulls[i]) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("option name at variadic position %d is null", i + 1))); - - name = TextDatumGetCString(args[i]); - - if (nulls[i + 1]) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("value for option \"%s\" must not be null", name))); - - /* Find matching option descriptor */ - for (int j = 0; j < nopts; j++) - { - if (pg_strcasecmp(name, opts[j].name) == 0) - { - opt = &opts[j]; - break; - } - } - - if (opt == NULL) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized option: \"%s\"", name))); - - if (opt->isset) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("option \"%s\" is specified more than once", - name))); - - valstr = TextDatumGetCString(args[i + 1]); - - switch (opt->type) - { - case DDL_OPT_BOOL: - if (!parse_bool(valstr, &opt->boolval)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid value for boolean option \"%s\": %s", - name, valstr))); - break; - - case DDL_OPT_TEXT: - opt->textval = valstr; - valstr = NULL; /* don't pfree below */ - break; - - case DDL_OPT_INT: - { - char *endp; - long val; - - errno = 0; - val = strtol(valstr, &endp, 10); - if (*endp != '\0' || errno == ERANGE || - val < PG_INT32_MIN || val > PG_INT32_MAX) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid value for integer option \"%s\": %s", - name, valstr))); - opt->intval = (int) val; - } - break; - } - - opt->isset = true; - - if (valstr) - pfree(valstr); - pfree(name); - } -} - /* * Helper to append a formatted string with optional pretty-printing. */ @@ -590,7 +421,6 @@ pg_get_role_ddl_internal(Oid roleid, bool pretty, bool memberships) * Each row is a complete SQL statement. The first row is always the * CREATE ROLE statement; subsequent rows are ALTER ROLE SET statements * and optionally GRANT statements for role memberships. - * Returns no rows if the role argument is NULL. */ Datum pg_get_role_ddl(PG_FUNCTION_ARGS) @@ -602,26 +432,17 @@ pg_get_role_ddl(PG_FUNCTION_ARGS) { MemoryContext oldcontext; Oid roleid; - DdlOption opts[] = { - {"pretty", DDL_OPT_BOOL}, - {"memberships", DDL_OPT_BOOL}, - }; + bool pretty; + bool memberships; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - if (PG_ARGISNULL(0)) - { - MemoryContextSwitchTo(oldcontext); - SRF_RETURN_DONE(funcctx); - } - roleid = PG_GETARG_OID(0); - parse_ddl_options(fcinfo, 1, opts, lengthof(opts)); + pretty = PG_GETARG_BOOL(1); + memberships = PG_GETARG_BOOL(2); - statements = pg_get_role_ddl_internal(roleid, - opts[0].isset && opts[0].boolval, - !opts[1].isset || opts[1].boolval); + statements = pg_get_role_ddl_internal(roleid, pretty, memberships); funcctx->user_fctx = statements; funcctx->max_calls = list_length(statements); @@ -755,7 +576,7 @@ pg_get_tablespace_ddl_internal(Oid tsid, bool pretty, bool no_owner) * pg_get_tablespace_ddl_srf - common SRF logic for tablespace DDL */ static Datum -pg_get_tablespace_ddl_srf(FunctionCallInfo fcinfo, Oid tsid, bool isnull) +pg_get_tablespace_ddl_srf(FunctionCallInfo fcinfo, Oid tsid) { FuncCallContext *funcctx; List *statements; @@ -763,25 +584,16 @@ pg_get_tablespace_ddl_srf(FunctionCallInfo fcinfo, Oid tsid, bool isnull) if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; - DdlOption opts[] = { - {"pretty", DDL_OPT_BOOL}, - {"owner", DDL_OPT_BOOL}, - }; + bool pretty; + bool no_owner; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - if (isnull) - { - MemoryContextSwitchTo(oldcontext); - SRF_RETURN_DONE(funcctx); - } - - parse_ddl_options(fcinfo, 1, opts, lengthof(opts)); + pretty = PG_GETARG_BOOL(1); + no_owner = !PG_GETARG_BOOL(2); - statements = pg_get_tablespace_ddl_internal(tsid, - opts[0].isset && opts[0].boolval, - opts[1].isset && !opts[1].boolval); + statements = pg_get_tablespace_ddl_internal(tsid, pretty, no_owner); funcctx->user_fctx = statements; funcctx->max_calls = list_length(statements); @@ -813,14 +625,9 @@ pg_get_tablespace_ddl_srf(FunctionCallInfo fcinfo, Oid tsid, bool isnull) Datum pg_get_tablespace_ddl_oid(PG_FUNCTION_ARGS) { - Oid tsid = InvalidOid; - bool isnull; - - isnull = PG_ARGISNULL(0); - if (!isnull) - tsid = PG_GETARG_OID(0); + Oid tsid = PG_GETARG_OID(0); - return pg_get_tablespace_ddl_srf(fcinfo, tsid, isnull); + return pg_get_tablespace_ddl_srf(fcinfo, tsid); } /* @@ -830,19 +637,10 @@ pg_get_tablespace_ddl_oid(PG_FUNCTION_ARGS) Datum pg_get_tablespace_ddl_name(PG_FUNCTION_ARGS) { - Oid tsid = InvalidOid; - Name tspname; - bool isnull; + Name tspname = PG_GETARG_NAME(0); + Oid tsid = get_tablespace_oid(NameStr(*tspname), false); - isnull = PG_ARGISNULL(0); - - if (!isnull) - { - tspname = PG_GETARG_NAME(0); - tsid = get_tablespace_oid(NameStr(*tspname), false); - } - - return pg_get_tablespace_ddl_srf(fcinfo, tsid, isnull); + return pg_get_tablespace_ddl_srf(fcinfo, tsid); } /* @@ -1140,28 +938,20 @@ pg_get_database_ddl(PG_FUNCTION_ARGS) { MemoryContext oldcontext; Oid dbid; - DdlOption opts[] = { - {"pretty", DDL_OPT_BOOL}, - {"owner", DDL_OPT_BOOL}, - {"tablespace", DDL_OPT_BOOL}, - }; + bool pretty; + bool no_owner; + bool no_tablespace; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - if (PG_ARGISNULL(0)) - { - MemoryContextSwitchTo(oldcontext); - SRF_RETURN_DONE(funcctx); - } - dbid = PG_GETARG_OID(0); - parse_ddl_options(fcinfo, 1, opts, lengthof(opts)); + pretty = PG_GETARG_BOOL(1); + no_owner = !PG_GETARG_BOOL(2); + no_tablespace = !PG_GETARG_BOOL(3); - statements = pg_get_database_ddl_internal(dbid, - opts[0].isset && opts[0].boolval, - opts[1].isset && !opts[1].boolval, - opts[2].isset && !opts[2].boolval); + statements = pg_get_database_ddl_internal(dbid, pretty, no_owner, + no_tablespace); funcctx->user_fctx = statements; funcctx->max_calls = list_length(statements); diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index 6f9c9c72de561..0c6a20843a501 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -1737,38 +1737,78 @@ pg_stat_get_wal(PG_FUNCTION_ARGS) wal_stats->stat_reset_timestamp)); } -Datum -pg_stat_get_lock(PG_FUNCTION_ARGS) +/* + * pg_stat_lock_build_tuples + * + * Helper routine for pg_stat_get_lock() and pg_stat_get_backend_lock(), + * filling a result tuplestore with one tuple for each lock type. + */ +static void +pg_stat_lock_build_tuples(ReturnSetInfo *rsinfo, + PgStat_LockEntry *lock_stats, + TimestampTz stat_reset_timestamp) { #define PG_STAT_LOCK_COLS 5 - ReturnSetInfo *rsinfo; - PgStat_Lock *lock_stats; - - InitMaterializedSRF(fcinfo, 0); - rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; - - lock_stats = pgstat_fetch_stat_lock(); - for (int lcktype = 0; lcktype <= LOCKTAG_LAST_TYPE; lcktype++) { - const char *locktypename; Datum values[PG_STAT_LOCK_COLS] = {0}; bool nulls[PG_STAT_LOCK_COLS] = {0}; - PgStat_LockEntry *lck_stats = &lock_stats->stats[lcktype]; + PgStat_LockEntry *lck_stats = &lock_stats[lcktype]; int i = 0; - locktypename = LockTagTypeNames[lcktype]; - - values[i++] = CStringGetTextDatum(locktypename); + values[i++] = CStringGetTextDatum(LockTagTypeNames[lcktype]); values[i++] = Int64GetDatum(lck_stats->waits); - values[i++] = Int64GetDatum(lck_stats->wait_time); + values[i++] = Float8GetDatum(pg_stat_us_to_ms(lck_stats->wait_time)); values[i++] = Int64GetDatum(lck_stats->fastpath_exceeded); - values[i] = TimestampTzGetDatum(lock_stats->stat_reset_timestamp); + if (stat_reset_timestamp != 0) + values[i] = TimestampTzGetDatum(stat_reset_timestamp); + else + nulls[i] = true; Assert(i + 1 == PG_STAT_LOCK_COLS); tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls); } +} + +Datum +pg_stat_get_lock(PG_FUNCTION_ARGS) +{ + ReturnSetInfo *rsinfo; + PgStat_Lock *lock_stats; + + InitMaterializedSRF(fcinfo, 0); + rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + + lock_stats = pgstat_fetch_stat_lock(); + + pg_stat_lock_build_tuples(rsinfo, lock_stats->stats, + lock_stats->stat_reset_timestamp); + + return (Datum) 0; +} + +/* + * Returns lock statistics for a backend with given PID. + */ +Datum +pg_stat_get_backend_lock(PG_FUNCTION_ARGS) +{ + int pid; + ReturnSetInfo *rsinfo; + PgStat_Backend *backend_stats; + + InitMaterializedSRF(fcinfo, 0); + rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + + pid = PG_GETARG_INT32(0); + backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL); + + if (!backend_stats) + return (Datum) 0; + + pg_stat_lock_build_tuples(rsinfo, backend_stats->lock_stats.stats, + backend_stats->stat_reset_timestamp); return (Datum) 0; } diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 44129a35c0895..627a9fb38ea37 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -228,7 +228,7 @@ typedef struct RI_CompareHashEntry * relations are held open with locks for the transaction duration, preventing * relcache invalidation. The entry itself is torn down at batch end by * ri_FastPathEndBatch(); on abort, ResourceOwner releases the cached - * relations and the XactCallback NULLs the static cache pointer to prevent + * relations and AtEOXact_RI() NULLs the static cache pointer to prevent * any subsequent access. */ typedef struct RI_FastPathEntry @@ -308,7 +308,7 @@ static RI_ConstraintInfo *ri_FetchConstraintInfo(Trigger *trigger, Relation trig_rel, bool rel_is_pk); static RI_ConstraintInfo *ri_LoadConstraintInfo(Oid constraintOid); static Oid get_ri_constraint_root(Oid constrOid); -static SPIPlanPtr ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes, +static SPIPlanPtr ri_PlanCheck(const char *querystr, int nargs, const Oid *argtypes, RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel); static bool ri_PerformCheck(const RI_ConstraintInfo *riinfo, RI_QueryKey *qkey, SPIPlanPtr qplan, @@ -2605,7 +2605,7 @@ InvalidateConstraintCacheCallBack(Datum arg, SysCacheIdentifier cacheid, * Prepare execution plan for a query to enforce an RI restriction */ static SPIPlanPtr -ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes, +ri_PlanCheck(const char *querystr, int nargs, const Oid *argtypes, RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel) { SPIPlanPtr qplan; @@ -4187,8 +4187,8 @@ RI_FKey_trigger_type(Oid tgfoid) * Registered as an AfterTriggerBatchCallback. Note: the flush can * do real work (CCI, security context switch, index probes) and can * throw ERROR on a constraint violation. If that happens, - * ri_FastPathTeardown never runs; ResourceOwner + XactCallback - * handle resource cleanup on the abort path. + * ri_FastPathTeardown never runs; ResourceOwner releases the cached + * relations and AtEOXact_RI() resets the static state on the abort path. */ static void ri_FastPathEndBatch(void *arg) @@ -4273,15 +4273,47 @@ ri_FastPathTeardown(void) ri_fastpath_callback_registered = false; } -static bool ri_fastpath_xact_callback_registered = false; - -static void -ri_FastPathXactCallback(XactEvent event, void *arg) +/* + * AtEOXact_RI + * Reset fast-path batching state at end of transaction. + * + * Called from CommitTransaction() and PrepareTransaction() with isCommit + * true, and from AbortTransaction() with isCommit false. + * + * By the time we get here on a clean commit or prepare, the fast-path cache + * has already been flushed and torn down by ri_FastPathEndBatch() (an + * AfterTriggerBatchCallback fired from AfterTriggerFireDeferred(), well before + * this point), so the static pointers are already clear and the reset below is + * a no-op. A surviving cache at commit means a trigger batch was never + * flushed, which would have silently skipped FK checks, so we complain. + * + * On abort, ri_FastPathEndBatch()/ri_FastPathTeardown() may not have run (a + * flush can error out partway): the ResourceOwner releases the cached + * relations and the TopTransactionContext reset frees the cache memory, but + * the process-local static pointers below would dangle into the next + * transaction. This resets them so they don't. + * + * The reset touches only backend-local static state (no relations, locks, + * buffers or catalog access), so it has no ordering dependency on the + * surrounding ResourceOwnerRelease() / AtEOXact_* steps. + */ +void +AtEOXact_RI(bool isCommit) { /* - * On abort, ResourceOwner already released relations; on commit, - * ri_FastPathTeardown already ran. Either way, just NULL the static - * pointers so they don't dangle into the next transaction. + * The cache must be empty on a clean commit or prepare; a survivor means + * a trigger batch went unflushed. Assert for assert-enabled builds and, + * since the transaction is already committed by now and FK checks may + * have been skipped, also warn in production builds. + */ + Assert(ri_fastpath_cache == NULL || !isCommit); + if (isCommit && ri_fastpath_cache != NULL) + elog(WARNING, "RI fast-path cache not flushed at end of transaction"); + + /* + * Clear the static pointers/flags. The cache memory lives in + * TopTransactionContext and is freed by the end-of-transaction + * memory-context reset; here we only drop the references to it. */ ri_fastpath_cache = NULL; ri_fastpath_callback_registered = false; @@ -4316,12 +4348,6 @@ ri_FastPathGetEntry(const RI_ConstraintInfo *riinfo, Relation fk_rel) { HASHCTL ctl; - if (!ri_fastpath_xact_callback_registered) - { - RegisterXactCallback(ri_FastPathXactCallback, NULL); - ri_fastpath_xact_callback_registered = true; - } - ctl.keysize = sizeof(Oid); ctl.entrysize = sizeof(RI_FastPathEntry); ctl.hcxt = TopTransactionContext; diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index d6efd07073a1e..cbc70fde7168e 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5295,6 +5295,14 @@ convert_string_datum(Datum value, Oid typid, Oid collid, bool *failure) return NULL; } + /* + * If we don't have a collation, act as though it's "C". This would + * normally happen only for the "char" type, but perhaps there are other + * cases. + */ + if (!OidIsValid(collid)) + return val; + mylocale = pg_newlocale_from_collation(collid); if (!mylocale->collate_is_c) diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c index 698e7c1aa220f..26f1bd6451534 100644 --- a/src/backend/utils/cache/plancache.c +++ b/src/backend/utils/cache/plancache.c @@ -393,7 +393,7 @@ void CompleteCachedPlan(CachedPlanSource *plansource, List *querytree_list, MemoryContext querytree_context, - Oid *param_types, + const Oid *param_types, int num_params, ParserSetupHook parserSetup, void *parserSetupArg, diff --git a/src/bin/pg_combinebackup/reconstruct.c b/src/bin/pg_combinebackup/reconstruct.c index 3349aa2441d29..38e756be7ef82 100644 --- a/src/bin/pg_combinebackup/reconstruct.c +++ b/src/bin/pg_combinebackup/reconstruct.c @@ -705,6 +705,9 @@ write_reconstructed_file(char *input_filename, if (wb < 0) pg_fatal("error while copying file range from \"%s\" to \"%s\": %m", input_filename, output_filename); + else if (wb == 0) + pg_fatal("unexpected end of file while copying file range from \"%s\" to \"%s\"", + input_filename, output_filename); nwritten += wb; diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c index 7556fb3f22a35..f8f31382835c8 100644 --- a/src/bin/pg_upgrade/check.c +++ b/src/bin/pg_upgrade/check.c @@ -2635,7 +2635,7 @@ check_old_cluster_global_names(ClusterInfo *cluster) { fclose(script); pg_log(PG_REPORT, "fatal"); - pg_fatal("Your installation contains databases, roles, or tablespace with names\n" + pg_fatal("Your installation contains databases, roles, or tablespaces with names\n" "with invalid characters (newline or carriage return). To fix this,\n" "rename these objects.\n" "A list of all objects with invalid names is in the file:\n" diff --git a/src/common/d2s.c b/src/common/d2s.c index 34e7cd33bf9f5..0a9cb83fc4929 100644 --- a/src/common/d2s.c +++ b/src/common/d2s.c @@ -53,7 +53,7 @@ * alignment concerns that apply elsewhere. */ #if !defined(HAVE_INT128) && defined(_MSC_VER) \ - && !defined(RYU_ONLY_64_BIT_OPS) && defined(_M_X64) + && !defined(RYU_ONLY_64_BIT_OPS) && defined(__x86_64__) #define HAS_64_BIT_INTRINSICS #endif diff --git a/src/fe_utils/string_utils.c b/src/fe_utils/string_utils.c index 38fffbd036bf7..7a762251f32bd 100644 --- a/src/fe_utils/string_utils.c +++ b/src/fe_utils/string_utils.c @@ -568,6 +568,10 @@ appendByteaLiteral(PQExpBuffer buf, const unsigned char *str, size_t length, * Append the given string to the shell command being built in the buffer, * with shell-style quoting as needed to create exactly one argument. * + * Forbid LF or CR characters, which have scant practical use beyond designing + * security breaches. The Windows command shell is unusable as a conduit for + * arguments containing LF or CR characters. + * * appendShellString() simply prints an error and dies if LF or CR appears. * appendShellStringNoError() omits those characters from the result, and * returns false if there were any. diff --git a/src/include/access/xlogutils.h b/src/include/access/xlogutils.h index b97387c6d4c43..0c6c74100694f 100644 --- a/src/include/access/xlogutils.h +++ b/src/include/access/xlogutils.h @@ -87,6 +87,8 @@ typedef struct ReadLocalXLogPageNoWaitPrivate extern XLogRedoAction XLogReadBufferForRedo(XLogReaderState *record, uint8 block_id, Buffer *buf); extern Buffer XLogInitBufferForRedo(XLogReaderState *record, uint8 block_id); +extern void XLogFlushBufferForRedoIfInit(XLogReaderState *record, + uint8 block_id, Buffer buffer); extern XLogRedoAction XLogReadBufferForRedoExtended(XLogReaderState *record, uint8 block_id, ReadBufferMode mode, bool get_cleanup_lock, diff --git a/src/include/c.h b/src/include/c.h index f32989a633105..ef4dc5d1e634a 100644 --- a/src/include/c.h +++ b/src/include/c.h @@ -106,6 +106,62 @@ extern "C++" * ---------------------------------------------------------------- */ +/* + * Not all compilers follow gcc's names of macros for particular target + * architectures. Let's standardize on gcc's names (with trailing __), + * and cause those to become defined here if they are not already. + * + * Note: while this list is alphabetical, it's necessary to check _M_ARM64 + * before _M_AMD64, because Microsoft's ARM64EC environment defines both. + */ +#if defined(__arm__) || defined(__arm) +#ifndef __arm__ +#define __arm__ 1 +#endif +#elif defined(__aarch64__) || defined(_M_ARM64) +#ifndef __aarch64__ +#define __aarch64__ 1 +#endif +#elif defined(__loongarch64__) || defined(__loongarch64) +#ifndef __loongarch64__ +#define __loongarch64__ 1 +#endif +#elif defined(__mips__) +/* no work */ +#elif defined(__mips64__) +/* no work */ +#elif defined(__powerpc__) || defined(__ppc__) +#ifndef __powerpc__ +#define __powerpc__ 1 +#endif +#elif defined(__powerpc64__) || defined(__ppc64__) +#ifndef __powerpc64__ +#define __powerpc64__ 1 +#endif +#elif defined(__riscv__) +/* no work */ +#elif defined(__riscv64__) +/* no work */ +#elif defined(__s390__) +/* no work */ +#elif defined(__s390x__) +/* no work */ +#elif defined(__sparc__) || defined(__sparc) +#ifndef __sparc__ +#define __sparc__ 1 +#endif +#elif defined(__i386__) || defined (__i386) || defined(_M_IX86) +#ifndef __i386__ +#define __i386__ 1 +#endif +#elif defined(__x86_64__) || defined(__x86_64) || defined (__amd64) || defined(_M_AMD64) +#ifndef __x86_64__ +#define __x86_64__ 1 +#endif +#else +#error "cannot identify target architecture" +#endif + /* * Disable "inline" if PG_FORCE_DISABLE_INLINE is defined. * This is used to work around compiler bugs and might also be useful for @@ -1337,7 +1393,7 @@ typedef struct PGAlignedXLogBlock PGAlignedXLogBlock; * SSE2 instructions are part of the spec for the 64-bit x86 ISA. We assume * that compilers targeting this architecture understand SSE2 intrinsics. */ -#if (defined(__x86_64__) || defined(_M_AMD64)) +#if defined(__x86_64__) #define USE_SSE2 #else /* ! x86_64 */ diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 2fe3be9ada58c..e06eb2abbcbbc 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -57,6 +57,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 202606251 +#define CATALOG_VERSION_NO 202606302 #endif diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 384ba908d35bc..73bb7fbb4304f 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -6064,7 +6064,7 @@ { oid => '6509', descr => 'statistics: per lock type statistics', proname => 'pg_stat_get_lock', prorows => '10', proretset => 't', provolatile => 'v', proparallel => 'r', prorettype => 'record', - proargtypes => '', proallargtypes => '{text,int8,int8,int8,timestamptz}', + proargtypes => '', proallargtypes => '{text,int8,float8,int8,timestamptz}', proargmodes => '{o,o,o,o,o}', proargnames => '{locktype,waits,wait_time,fastpath_exceeded,stats_reset}', prosrc => 'pg_stat_get_lock' }, @@ -6092,6 +6092,14 @@ proargmodes => '{i,o,o,o,o,o,o}', proargnames => '{backend_pid,wal_records,wal_fpi,wal_bytes,wal_fpi_bytes,wal_buffers_full,stats_reset}', prosrc => 'pg_stat_get_backend_wal' }, +{ oid => '9682', descr => 'statistics: backend lock statistics', + proname => 'pg_stat_get_backend_lock', prorows => '10', proretset => 't', + provolatile => 'v', proparallel => 'r', prorettype => 'record', + proargtypes => 'int4', + proallargtypes => '{int4,text,int8,float8,int8,timestamptz}', + proargmodes => '{i,o,o,o,o,o}', + proargnames => '{backend_pid,locktype,waits,wait_time,fastpath_exceeded,stats_reset}', + prosrc => 'pg_stat_get_backend_lock' }, { oid => '6248', descr => 'statistics: information about WAL prefetching', proname => 'pg_stat_get_recovery_prefetch', prorows => '1', proretset => 't', provolatile => 'v', prorettype => 'record', proargtypes => '', @@ -8591,30 +8599,27 @@ proname => 'pg_get_constraintdef', provolatile => 's', prorettype => 'text', proargtypes => 'oid bool', prosrc => 'pg_get_constraintdef_ext' }, { oid => '6501', descr => 'get DDL to recreate a role', - proname => 'pg_get_role_ddl', prorows => '10', provariadic => 'text', - proisstrict => 'f', proretset => 't', provolatile => 's', - pronargdefaults => '1', prorettype => 'text', proargtypes => 'regrole _text', - proallargtypes => '{regrole,_text}', proargmodes => '{i,v}', - proargdefaults => '{NULL}', prosrc => 'pg_get_role_ddl' }, + proname => 'pg_get_role_ddl', prorows => '10', proretset => 't', + provolatile => 's', pronargdefaults => '2', prorettype => 'text', + proargtypes => 'regrole bool bool', + proargnames => '{role,pretty,memberships}', proargdefaults => '{false,true}', + prosrc => 'pg_get_role_ddl' }, { oid => '6499', descr => 'get DDL to recreate a tablespace', - proname => 'pg_get_tablespace_ddl', prorows => '10', provariadic => 'text', - proisstrict => 'f', proretset => 't', provolatile => 's', - pronargdefaults => '1', prorettype => 'text', proargtypes => 'oid _text', - proallargtypes => '{oid,_text}', proargmodes => '{i,v}', - proargdefaults => '{NULL}', prosrc => 'pg_get_tablespace_ddl_oid' }, + proname => 'pg_get_tablespace_ddl', prorows => '10', proretset => 't', + provolatile => 's', pronargdefaults => '2', prorettype => 'text', + proargtypes => 'oid bool bool', proargnames => '{tablespace,pretty,owner}', + proargdefaults => '{false,true}', prosrc => 'pg_get_tablespace_ddl_oid' }, { oid => '6500', descr => 'get DDL to recreate a tablespace', - proname => 'pg_get_tablespace_ddl', prorows => '10', provariadic => 'text', - proisstrict => 'f', proretset => 't', provolatile => 's', - pronargdefaults => '1', prorettype => 'text', proargtypes => 'name _text', - proallargtypes => '{name,_text}', proargmodes => '{i,v}', - proargdefaults => '{NULL}', prosrc => 'pg_get_tablespace_ddl_name' }, + proname => 'pg_get_tablespace_ddl', prorows => '10', proretset => 't', + provolatile => 's', pronargdefaults => '2', prorettype => 'text', + proargtypes => 'name bool bool', proargnames => '{tablespace,pretty,owner}', + proargdefaults => '{false,true}', prosrc => 'pg_get_tablespace_ddl_name' }, { oid => '6502', descr => 'get DDL to recreate a database', - proname => 'pg_get_database_ddl', prorows => '10', provariadic => 'text', - proisstrict => 'f', proretset => 't', provolatile => 's', - pronargdefaults => '1', prorettype => 'text', - proargtypes => 'regdatabase _text', proallargtypes => '{regdatabase,_text}', - proargmodes => '{i,v}', proargdefaults => '{NULL}', - prosrc => 'pg_get_database_ddl' }, + proname => 'pg_get_database_ddl', prorows => '10', proretset => 't', + provolatile => 's', pronargdefaults => '3', prorettype => 'text', + proargtypes => 'regdatabase bool bool bool', + proargnames => '{database,pretty,owner,tablespace}', + proargdefaults => '{false,true,true}', prosrc => 'pg_get_database_ddl' }, { oid => '2509', descr => 'deparse an encoded expression with pretty-print option', proname => 'pg_get_expr', provolatile => 's', prorettype => 'text', diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h index 1d9869973c094..0c3d485abf47f 100644 --- a/src/include/commands/trigger.h +++ b/src/include/commands/trigger.h @@ -310,4 +310,6 @@ extern void RegisterAfterTriggerBatchCallback(AfterTriggerBatchCallback callback void *arg); extern bool AfterTriggerIsActive(void); +extern void AtEOXact_RI(bool isCommit); + #endif /* TRIGGER_H */ diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 650baab3efcaa..1798e6027d4c2 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -249,7 +249,7 @@ extern bool ExecCheckPermissions(List *rangeTable, extern bool ExecCheckOneRelPerms(RTEPermissionInfo *perminfo); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation, OnConflictAction onConflictAction, - List *mergeActions); + List *mergeActions, ModifyTable *mtnode); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, Index resultRelationIndex, diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h index f4985cb715d9f..c7a3b7abb2609 100644 --- a/src/include/executor/spi.h +++ b/src/include/executor/spi.h @@ -119,7 +119,7 @@ extern int SPI_execute_plan_with_paramlist(SPIPlanPtr plan, ParamListInfo params, bool read_only, long tcount); extern int SPI_exec(const char *src, long tcount); -extern int SPI_execp(SPIPlanPtr plan, Datum *Values, const char *Nulls, +extern int SPI_execp(SPIPlanPtr plan, const Datum *Values, const char *Nulls, long tcount); extern int SPI_execute_snapshot(SPIPlanPtr plan, const Datum *Values, const char *Nulls, @@ -127,11 +127,11 @@ extern int SPI_execute_snapshot(SPIPlanPtr plan, Snapshot crosscheck_snapshot, bool read_only, bool fire_triggers, long tcount); extern int SPI_execute_with_args(const char *src, - int nargs, Oid *argtypes, + int nargs, const Oid *argtypes, const Datum *Values, const char *Nulls, bool read_only, long tcount); -extern SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes); -extern SPIPlanPtr SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes, +extern SPIPlanPtr SPI_prepare(const char *src, int nargs, const Oid *argtypes); +extern SPIPlanPtr SPI_prepare_cursor(const char *src, int nargs, const Oid *argtypes, int cursorOptions); extern SPIPlanPtr SPI_prepare_extended(const char *src, const SPIPrepareOptions *options); @@ -155,7 +155,7 @@ extern CachedPlan *SPI_plan_get_cached_plan(SPIPlanPtr plan); extern HeapTuple SPI_copytuple(HeapTuple tuple); extern HeapTupleHeader SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc); extern HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, - int *attnum, Datum *Values, const char *Nulls); + const int *attnum, const Datum *Values, const char *Nulls); extern int SPI_fnumber(TupleDesc tupdesc, const char *fname); extern char *SPI_fname(TupleDesc tupdesc, int fnumber); extern char *SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber); @@ -175,8 +175,8 @@ extern Portal SPI_cursor_open(const char *name, SPIPlanPtr plan, const Datum *Values, const char *Nulls, bool read_only); extern Portal SPI_cursor_open_with_args(const char *name, const char *src, - int nargs, Oid *argtypes, - Datum *Values, const char *Nulls, + int nargs, const Oid *argtypes, + const Datum *Values, const char *Nulls, bool read_only, int cursorOptions); extern Portal SPI_cursor_open_with_paramlist(const char *name, SPIPlanPtr plan, ParamListInfo params, bool read_only); diff --git a/src/include/executor/spi_priv.h b/src/include/executor/spi_priv.h index 240383b97b9db..9fb82ad098422 100644 --- a/src/include/executor/spi_priv.h +++ b/src/include/executor/spi_priv.h @@ -97,7 +97,7 @@ typedef struct _SPI_plan RawParseMode parse_mode; /* raw_parser() mode */ int cursor_options; /* Cursor options used for planning */ int nargs; /* number of plan arguments */ - Oid *argtypes; /* Argument types (NULL if nargs is 0) */ + const Oid *argtypes; /* Argument types (NULL if nargs is 0) */ ParserSetupHook parserSetup; /* alternative parameter spec method */ void *parserSetupArg; } _SPI_plan; diff --git a/src/include/nodes/queryjumble.h b/src/include/nodes/queryjumble.h index f331449ba78f6..253911c0165d5 100644 --- a/src/include/nodes/queryjumble.h +++ b/src/include/nodes/queryjumble.h @@ -14,6 +14,7 @@ #ifndef QUERYJUMBLE_H #define QUERYJUMBLE_H +#include "common/hashfn_unstable.h" #include "nodes/parsenodes.h" /* @@ -37,11 +38,8 @@ typedef struct LocationLen */ typedef struct JumbleState { - /* Jumble of current query tree */ - unsigned char *jumble; - - /* Number of bytes used in jumble[] */ - Size jumble_len; + /* Streaming hash state for the current query jumble */ + fasthash_state jumble; /* Array of locations of constants that should be removed */ LocationLen *clocations; @@ -66,13 +64,13 @@ typedef struct JumbleState /* * Count of the number of NULL nodes seen since last appending a value. - * These are flushed out to the jumble buffer before subsequent appends - * and before performing the final jumble hash. + * These are flushed out to the jumble hash before subsequent appends and + * before performing the final jumble hash. */ unsigned int pending_nulls; #ifdef USE_ASSERT_CHECKING - /* The total number of bytes added to the jumble buffer */ + /* The total number of bytes added to the jumble */ Size total_jumble_len; #endif } JumbleState; diff --git a/src/include/pgstat.h b/src/include/pgstat.h index dfa2e8376382a..58a44857f1311 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -349,7 +349,7 @@ typedef struct PgStat_IO typedef struct PgStat_LockEntry { PgStat_Counter waits; - PgStat_Counter wait_time; /* time in milliseconds */ + PgStat_Counter wait_time; /* time in microseconds */ PgStat_Counter fastpath_exceeded; } PgStat_LockEntry; @@ -523,6 +523,7 @@ typedef struct PgStat_Backend TimestampTz stat_reset_timestamp; PgStat_BktypeIO io_stats; PgStat_WalCounters wal_counters; + PgStat_PendingLock lock_stats; } PgStat_Backend; /* --------- @@ -535,6 +536,12 @@ typedef struct PgStat_BackendPending * Backend statistics store the same amount of IO data as PGSTAT_KIND_IO. */ PgStat_PendingIO pending_io; + + /* + * Backend statistics store the same amount of lock data as + * PGSTAT_KIND_LOCK. + */ + PgStat_PendingLock pending_lock; } PgStat_BackendPending; /* @@ -586,6 +593,11 @@ extern void pgstat_count_backend_io_op(IOObject io_object, IOContext io_context, IOOp io_op, uint32 cnt, uint64 bytes); + +/* used by pgstat_lock.c for lock stats tracked in backends */ +extern void pgstat_count_backend_lock_waits(uint8 locktag_type, PgStat_Counter usecs); +extern void pgstat_count_backend_lock_fastpath_exceeded(uint8 locktag_type); + extern PgStat_Backend *pgstat_fetch_stat_backend(ProcNumber procNumber); extern PgStat_Backend *pgstat_fetch_stat_backend_by_pid(int pid, BackendType *bktype); @@ -638,7 +650,8 @@ extern bool pgstat_tracks_io_op(BackendType bktype, IOObject io_object, extern void pgstat_lock_flush(bool nowait); extern void pgstat_count_lock_fastpath_exceeded(uint8 locktag_type); -extern void pgstat_count_lock_waits(uint8 locktag_type, long msecs); +extern void pgstat_count_lock_waits(uint8 locktag_type, + PgStat_Counter usecs); extern PgStat_Lock *pgstat_fetch_stat_lock(void); /* diff --git a/src/include/port/atomics.h b/src/include/port/atomics.h index d8b1d20fe60fa..a605ea81d0760 100644 --- a/src/include/port/atomics.h +++ b/src/include/port/atomics.h @@ -63,11 +63,11 @@ * compiler barrier. * */ -#if defined(__arm__) || defined(__arm) || defined(__aarch64__) +#if defined(__arm__) || defined(__aarch64__) #include "port/atomics/arch-arm.h" -#elif defined(__i386__) || defined(__i386) || defined(__x86_64__) +#elif defined(__i386__) || defined(__x86_64__) #include "port/atomics/arch-x86.h" -#elif defined(__ppc__) || defined(__powerpc__) || defined(__ppc64__) || defined(__powerpc64__) +#elif defined(__powerpc__) || defined(__powerpc64__) #include "port/atomics/arch-ppc.h" #endif @@ -154,11 +154,6 @@ #define pg_read_barrier() pg_read_barrier_impl() #define pg_write_barrier() pg_write_barrier_impl() -/* - * Spinloop delay - Allow CPU to relax in busy loops - */ -#define pg_spin_delay() pg_spin_delay_impl() - /* * pg_atomic_init_flag - initialize atomic flag. * diff --git a/src/include/port/atomics/arch-x86.h b/src/include/port/atomics/arch-x86.h index 8cfe402c33935..231831dcf5979 100644 --- a/src/include/port/atomics/arch-x86.h +++ b/src/include/port/atomics/arch-x86.h @@ -32,7 +32,7 @@ */ #if defined(__GNUC__) || defined(__INTEL_COMPILER) -#if defined(__i386__) || defined(__i386) +#if defined(__i386__) #define pg_memory_barrier_impl() \ __asm__ __volatile__ ("lock; addl $0,0(%%esp)" : : : "memory", "cc") #elif defined(__x86_64__) @@ -76,60 +76,6 @@ typedef struct pg_atomic_uint64 } pg_atomic_uint64; #endif /* __x86_64__ */ -#endif /* defined(__GNUC__) || defined(__INTEL_COMPILER) */ - -#if !defined(PG_HAVE_SPIN_DELAY) -/* - * This sequence is equivalent to the PAUSE instruction ("rep" is - * ignored by old IA32 processors if the following instruction is - * not a string operation); the IA-32 Architecture Software - * Developer's Manual, Vol. 3, Section 7.7.2 describes why using - * PAUSE in the inner loop of a spin lock is necessary for good - * performance: - * - * The PAUSE instruction improves the performance of IA-32 - * processors supporting Hyper-Threading Technology when - * executing spin-wait loops and other routines where one - * thread is accessing a shared lock or semaphore in a tight - * polling loop. When executing a spin-wait loop, the - * processor can suffer a severe performance penalty when - * exiting the loop because it detects a possible memory order - * violation and flushes the core processor's pipeline. The - * PAUSE instruction provides a hint to the processor that the - * code sequence is a spin-wait loop. The processor uses this - * hint to avoid the memory order violation and prevent the - * pipeline flush. In addition, the PAUSE instruction - * de-pipelines the spin-wait loop to prevent it from - * consuming execution resources excessively. - */ -#if defined(__GNUC__) || defined(__INTEL_COMPILER) -#define PG_HAVE_SPIN_DELAY -static inline void -pg_spin_delay_impl(void) -{ - __asm__ __volatile__(" rep; nop \n"); -} -#elif defined(_MSC_VER) && defined(__x86_64__) -#define PG_HAVE_SPIN_DELAY -static __forceinline void -pg_spin_delay_impl(void) -{ - _mm_pause(); -} -#elif defined(_MSC_VER) -#define PG_HAVE_SPIN_DELAY -static __forceinline void -pg_spin_delay_impl(void) -{ - /* See comment for gcc code. Same code, MASM syntax */ - __asm rep nop; -} -#endif -#endif /* !defined(PG_HAVE_SPIN_DELAY) */ - - -#if defined(__GNUC__) || defined(__INTEL_COMPILER) - #define PG_HAVE_ATOMIC_TEST_SET_FLAG static inline bool pg_atomic_test_set_flag_impl(volatile pg_atomic_flag *ptr) @@ -238,6 +184,6 @@ pg_atomic_fetch_add_u64_impl(volatile pg_atomic_uint64 *ptr, int64 add_) /* * 8 byte reads / writes have single-copy atomicity on all x86-64 cpus. */ -#if defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) /* gcc, msvc */ +#if defined(__x86_64__) #define PG_HAVE_8BYTE_SINGLE_COPY_ATOMICITY #endif /* 8 byte single-copy atomicity */ diff --git a/src/include/port/atomics/generic.h b/src/include/port/atomics/generic.h index fd64b6cbd8681..daa772e9a6d00 100644 --- a/src/include/port/atomics/generic.h +++ b/src/include/port/atomics/generic.h @@ -28,12 +28,6 @@ # define pg_write_barrier_impl pg_memory_barrier_impl #endif -#ifndef PG_HAVE_SPIN_DELAY -#define PG_HAVE_SPIN_DELAY -#define pg_spin_delay_impl() ((void)0) -#endif - - /* provide fallback */ #if !defined(PG_HAVE_ATOMIC_FLAG_SUPPORT) && defined(PG_HAVE_ATOMIC_U32_SUPPORT) #define PG_HAVE_ATOMIC_FLAG_SUPPORT diff --git a/src/include/port/pg_bitutils.h b/src/include/port/pg_bitutils.h index 7a00d197013ba..2864ba431db1d 100644 --- a/src/include/port/pg_bitutils.h +++ b/src/include/port/pg_bitutils.h @@ -82,7 +82,7 @@ pg_leftmost_one_pos64(uint64 word) #error "cannot find integer type of the same size as uint64_t" #endif -#elif defined(_MSC_VER) && (defined(_M_AMD64) || defined(_M_ARM64)) +#elif defined(_MSC_VER) && (defined(__x86_64__) || defined(__aarch64__)) unsigned long result; bool non_zero; @@ -155,7 +155,7 @@ pg_rightmost_one_pos64(uint64 word) #error "cannot find integer type of the same size as uint64_t" #endif -#elif defined(_MSC_VER) && (defined(_M_AMD64) || defined(_M_ARM64)) +#elif defined(_MSC_VER) && (defined(__x86_64__) || defined(__aarch64__)) unsigned long result; bool non_zero; diff --git a/src/include/port/pg_cpu.h b/src/include/port/pg_cpu.h index 566ed7a16e393..22aaa1aa5bcc5 100644 --- a/src/include/port/pg_cpu.h +++ b/src/include/port/pg_cpu.h @@ -13,7 +13,7 @@ #ifndef PG_CPU_H #define PG_CPU_H -#if defined(USE_SSE2) || defined(__i386__) +#if defined(__x86_64__) || defined(__i386__) typedef enum X86FeatureId { @@ -58,6 +58,6 @@ x86_feature_available(X86FeatureId feature) extern uint32 x86_tsc_frequency_khz(char *source, size_t source_size); -#endif /* defined(USE_SSE2) || defined(__i386__) */ +#endif /* defined(__x86_64__) || defined(__i386__) */ #endif /* PG_CPU_H */ diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 655f8737b6ff1..826cc202847fc 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -95,7 +95,7 @@ typedef struct instr_time * PG_INSTR_TSC_CLOCK controls whether the TSC clock source is compiled in, and * potentially used based on timing_tsc_enabled. */ -#if defined(__x86_64__) || defined(_M_X64) +#if defined(__x86_64__) #define PG_INSTR_TICKS_TO_NS 1 #define PG_INSTR_TSC_CLOCK 1 #elif defined(WIN32) diff --git a/src/include/storage/s_lock.h b/src/include/storage/s_lock.h index c9e5251199005..96fcaaac01e76 100644 --- a/src/include/storage/s_lock.h +++ b/src/include/storage/s_lock.h @@ -247,7 +247,7 @@ spin_delay(void) * We use the int-width variant of the builtin because it works on more chips * than other widths. */ -#if defined(__arm__) || defined(__arm) || defined(__aarch64__) +#if defined(__arm__) || defined(__aarch64__) #ifdef HAVE_GCC__SYNC_INT32_TAS #define HAS_TEST_AND_SET @@ -287,7 +287,7 @@ spin_delay(void) #endif /* __aarch64__ */ #endif /* HAVE_GCC__SYNC_INT32_TAS */ -#endif /* __arm__ || __arm || __aarch64__ */ +#endif /* __arm__ || __aarch64__ */ /* S/390 and S/390x Linux (32- and 64-bit zSeries) */ @@ -391,7 +391,7 @@ do \ /* PowerPC */ -#if defined(__ppc__) || defined(__powerpc__) || defined(__ppc64__) || defined(__powerpc64__) +#if defined(__powerpc__) || defined(__powerpc64__) #define HAS_TEST_AND_SET typedef unsigned int slock_t; @@ -602,7 +602,7 @@ typedef LONG slock_t; #define SPIN_DELAY() spin_delay() -#ifdef _M_ARM64 +#ifdef __aarch64__ static __forceinline void spin_delay(void) { @@ -633,7 +633,7 @@ spin_delay(void) #include -#ifdef _M_ARM64 +#ifdef __aarch64__ /* _ReadWriteBarrier() is insufficient on non-TSO architectures. */ #pragma intrinsic(_InterlockedExchange) diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h index 3ca4f45489568..b3dc3ff7d8bb7 100644 --- a/src/include/utils/pgstat_internal.h +++ b/src/include/utils/pgstat_internal.h @@ -705,7 +705,8 @@ extern void pgstat_archiver_snapshot_cb(void); /* flags for pgstat_flush_backend() */ #define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */ #define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */ -#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL) +#define PGSTAT_BACKEND_FLUSH_LOCK (1 << 2) /* Flush lock statistics */ +#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_LOCK) extern bool pgstat_flush_backend(bool nowait, uint32 flags); extern bool pgstat_backend_flush_cb(bool nowait); diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h index 7a4a85c803835..a0355e79c2835 100644 --- a/src/include/utils/plancache.h +++ b/src/include/utils/plancache.h @@ -214,7 +214,7 @@ extern CachedPlanSource *CreateOneShotCachedPlan(RawStmt *raw_parse_tree, extern void CompleteCachedPlan(CachedPlanSource *plansource, List *querytree_list, MemoryContext querytree_context, - Oid *param_types, + const Oid *param_types, int num_params, ParserSetupHook parserSetup, void *parserSetupArg, diff --git a/src/pl/plpython/expected/plpython_composite.out b/src/pl/plpython/expected/plpython_composite.out index 674af93ddcfe0..ffce7fc1be75b 100644 --- a/src/pl/plpython/expected/plpython_composite.out +++ b/src/pl/plpython/expected/plpython_composite.out @@ -606,3 +606,19 @@ DETAIL: Missing left parenthesis. HINT: To return a composite type in an array, return the composite type as a Python tuple, e.g., "[('foo',)]". CONTEXT: while creating return value PL/Python function "composite_type_as_list_broken" +-- A custom sequence whose length matches the tuple but whose __getitem__ +-- raises should be reported as an error, not crash the backend. +CREATE FUNCTION composite_type_as_broken_sequence() RETURNS type_record AS $$ +class C: + def __len__(self): + return 2 + def __getitem__(self, i): + raise ValueError('getitem failed') +return C() +$$ LANGUAGE plpython3u; +SELECT * FROM composite_type_as_broken_sequence(); +ERROR: could not get element 0 from sequence +DETAIL: ValueError: getitem failed +CONTEXT: Traceback (most recent call last): +while creating return value +PL/Python function "composite_type_as_broken_sequence" diff --git a/src/pl/plpython/expected/plpython_spi.out b/src/pl/plpython/expected/plpython_spi.out index b572f9bf73bcb..0320ff01f6b40 100644 --- a/src/pl/plpython/expected/plpython_spi.out +++ b/src/pl/plpython/expected/plpython_spi.out @@ -451,3 +451,54 @@ SELECT plan_composite_args(); (3,label) (1 row) +-- A custom argument sequence whose length matches the plan but whose +-- __getitem__ raises should be reported as an error, not crash the backend. +CREATE FUNCTION plan_broken_arg_sequence() RETURNS void AS $$ +plan = plpy.prepare("select $1", ["int4"]) +class C: + def __len__(self): + return 1 + def __getitem__(self, i): + raise ValueError('getitem failed') +plpy.execute(plan, C()) +$$ LANGUAGE plpython3u; +SELECT plan_broken_arg_sequence(); +ERROR: spiexceptions.ExternalRoutineException: could not get element 0 from sequence +DETAIL: ValueError: getitem failed +CONTEXT: Traceback (most recent call last): + PL/Python function "plan_broken_arg_sequence", line 8, in + plpy.execute(plan, C()) +PL/Python function "plan_broken_arg_sequence" +-- Likewise for the type-name list passed to plpy.prepare(). +CREATE FUNCTION prepare_broken_type_sequence() RETURNS void AS $$ +class C: + def __len__(self): + return 1 + def __getitem__(self, i): + raise ValueError('getitem failed') +plpy.prepare("select $1", C()) +$$ LANGUAGE plpython3u; +SELECT prepare_broken_type_sequence(); +ERROR: spiexceptions.ExternalRoutineException: could not get element 0 from sequence +DETAIL: ValueError: getitem failed +CONTEXT: Traceback (most recent call last): + PL/Python function "prepare_broken_type_sequence", line 7, in + plpy.prepare("select $1", C()) +PL/Python function "prepare_broken_type_sequence" +-- Likewise for the argument sequence passed to plpy.cursor(). +CREATE FUNCTION cursor_broken_arg_sequence() RETURNS void AS $$ +plan = plpy.prepare("select $1", ["int4"]) +class C: + def __len__(self): + return 1 + def __getitem__(self, i): + raise ValueError('getitem failed') +plpy.cursor(plan, C()) +$$ LANGUAGE plpython3u; +SELECT cursor_broken_arg_sequence(); +ERROR: spiexceptions.ExternalRoutineException: could not get element 0 from sequence +DETAIL: ValueError: getitem failed +CONTEXT: Traceback (most recent call last): + PL/Python function "cursor_broken_arg_sequence", line 8, in + plpy.cursor(plan, C()) +PL/Python function "cursor_broken_arg_sequence" diff --git a/src/pl/plpython/expected/plpython_types.out b/src/pl/plpython/expected/plpython_types.out index 8a680e15c14d0..0cb3d6ea8c688 100644 --- a/src/pl/plpython/expected/plpython_types.out +++ b/src/pl/plpython/expected/plpython_types.out @@ -796,6 +796,22 @@ SELECT * FROM test_type_conversion_array_error(); ERROR: return value of function with array return type is not a Python sequence CONTEXT: while creating return value PL/Python function "test_type_conversion_array_error" +-- A custom sequence whose __getitem__ raises should be reported as an error, +-- not crash the backend. +CREATE FUNCTION test_type_conversion_array_getitem_fail() RETURNS int[] AS $$ +class C: + def __len__(self): + return 2 + def __getitem__(self, i): + raise ValueError('getitem failed') +return C() +$$ LANGUAGE plpython3u; +SELECT * FROM test_type_conversion_array_getitem_fail(); +ERROR: could not get element 0 from sequence +DETAIL: ValueError: getitem failed +CONTEXT: Traceback (most recent call last): +while creating return value +PL/Python function "test_type_conversion_array_getitem_fail" -- -- Domains over arrays -- diff --git a/src/pl/plpython/plpy_cursorobject.c b/src/pl/plpython/plpy_cursorobject.c index cc74c4df6ba67..0725fbc19f264 100644 --- a/src/pl/plpython/plpy_cursorobject.c +++ b/src/pl/plpython/plpy_cursorobject.c @@ -258,6 +258,11 @@ PLy_cursor_plan(PyObject *ob, PyObject *args) PyObject *elem; elem = PySequence_GetItem(args, j); + + /* PySequence_GetItem() can return NULL, with an exception set */ + if (elem == NULL) + PLy_elog(ERROR, "could not get element %d from sequence", j); + PG_TRY(2); { bool isnull; diff --git a/src/pl/plpython/plpy_spi.c b/src/pl/plpython/plpy_spi.c index 4ad40bf78f3da..4980873efcf22 100644 --- a/src/pl/plpython/plpy_spi.c +++ b/src/pl/plpython/plpy_spi.c @@ -87,6 +87,11 @@ PLy_spi_prepare(PyObject *self, PyObject *args) int32 typmod; optr = PySequence_GetItem(list, i); + + /* PySequence_GetItem() can return NULL, with an exception set */ + if (optr == NULL) + PLy_elog(ERROR, "could not get element %d from sequence", i); + if (PyUnicode_Check(optr)) sptr = PLyUnicode_AsString(optr); else @@ -250,6 +255,11 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit) PyObject *elem; elem = PySequence_GetItem(list, j); + + /* PySequence_GetItem() can return NULL, with an exception set */ + if (elem == NULL) + PLy_elog(ERROR, "could not get element %d from sequence", j); + PG_TRY(2); { bool isnull; diff --git a/src/pl/plpython/plpy_typeio.c b/src/pl/plpython/plpy_typeio.c index 92d55bf9f423e..e499ed8cbfda2 100644 --- a/src/pl/plpython/plpy_typeio.c +++ b/src/pl/plpython/plpy_typeio.c @@ -1210,6 +1210,10 @@ PLySequence_ToArray_recurse(PyObject *obj, ArrayBuildState **astatep, /* fetch the array element */ PyObject *subobj = PySequence_GetItem(obj, i); + /* PySequence_GetItem() can return NULL, with an exception set */ + if (subobj == NULL) + PLy_elog(ERROR, "could not get element %d from sequence", i); + /* need PG_TRY to ensure we release the subobj's refcount */ PG_TRY(); { @@ -1456,7 +1460,10 @@ PLySequence_ToComposite(PLyObToDatum *arg, TupleDesc desc, PyObject *sequence) PG_TRY(); { value = PySequence_GetItem(sequence, idx); - Assert(value); + + /* PySequence_GetItem() can return NULL, with an exception set */ + if (value == NULL) + PLy_elog(ERROR, "could not get element %d from sequence", idx); values[i] = att->func(att, value, &nulls[i], false); diff --git a/src/pl/plpython/sql/plpython_composite.sql b/src/pl/plpython/sql/plpython_composite.sql index 1bb9b83b7191e..b401b3f2f6b93 100644 --- a/src/pl/plpython/sql/plpython_composite.sql +++ b/src/pl/plpython/sql/plpython_composite.sql @@ -233,3 +233,15 @@ CREATE FUNCTION composite_type_as_list_broken() RETURNS type_record[] AS $$ return [['first', 1]]; $$ LANGUAGE plpython3u; SELECT * FROM composite_type_as_list_broken(); + +-- A custom sequence whose length matches the tuple but whose __getitem__ +-- raises should be reported as an error, not crash the backend. +CREATE FUNCTION composite_type_as_broken_sequence() RETURNS type_record AS $$ +class C: + def __len__(self): + return 2 + def __getitem__(self, i): + raise ValueError('getitem failed') +return C() +$$ LANGUAGE plpython3u; +SELECT * FROM composite_type_as_broken_sequence(); diff --git a/src/pl/plpython/sql/plpython_spi.sql b/src/pl/plpython/sql/plpython_spi.sql index 00dcc8bb66937..276d130431e56 100644 --- a/src/pl/plpython/sql/plpython_spi.sql +++ b/src/pl/plpython/sql/plpython_spi.sql @@ -307,3 +307,42 @@ SELECT cursor_fetch_next_empty(); SELECT cursor_plan(); SELECT cursor_plan_wrong_args(); SELECT plan_composite_args(); + +-- A custom argument sequence whose length matches the plan but whose +-- __getitem__ raises should be reported as an error, not crash the backend. +CREATE FUNCTION plan_broken_arg_sequence() RETURNS void AS $$ +plan = plpy.prepare("select $1", ["int4"]) +class C: + def __len__(self): + return 1 + def __getitem__(self, i): + raise ValueError('getitem failed') +plpy.execute(plan, C()) +$$ LANGUAGE plpython3u; + +SELECT plan_broken_arg_sequence(); + +-- Likewise for the type-name list passed to plpy.prepare(). +CREATE FUNCTION prepare_broken_type_sequence() RETURNS void AS $$ +class C: + def __len__(self): + return 1 + def __getitem__(self, i): + raise ValueError('getitem failed') +plpy.prepare("select $1", C()) +$$ LANGUAGE plpython3u; + +SELECT prepare_broken_type_sequence(); + +-- Likewise for the argument sequence passed to plpy.cursor(). +CREATE FUNCTION cursor_broken_arg_sequence() RETURNS void AS $$ +plan = plpy.prepare("select $1", ["int4"]) +class C: + def __len__(self): + return 1 + def __getitem__(self, i): + raise ValueError('getitem failed') +plpy.cursor(plan, C()) +$$ LANGUAGE plpython3u; + +SELECT cursor_broken_arg_sequence(); diff --git a/src/pl/plpython/sql/plpython_types.sql b/src/pl/plpython/sql/plpython_types.sql index 0985a9cca2f44..31549c7f4f131 100644 --- a/src/pl/plpython/sql/plpython_types.sql +++ b/src/pl/plpython/sql/plpython_types.sql @@ -417,6 +417,19 @@ $$ LANGUAGE plpython3u; SELECT * FROM test_type_conversion_array_error(); +-- A custom sequence whose __getitem__ raises should be reported as an error, +-- not crash the backend. +CREATE FUNCTION test_type_conversion_array_getitem_fail() RETURNS int[] AS $$ +class C: + def __len__(self): + return 2 + def __getitem__(self, i): + raise ValueError('getitem failed') +return C() +$$ LANGUAGE plpython3u; + +SELECT * FROM test_type_conversion_array_getitem_fail(); + -- -- Domains over arrays diff --git a/src/port/pg_cpu_x86.c b/src/port/pg_cpu_x86.c index 0405ba19f6f53..b050677f717d3 100644 --- a/src/port/pg_cpu_x86.c +++ b/src/port/pg_cpu_x86.c @@ -19,7 +19,7 @@ #include "postgres_fe.h" #endif -#if defined(USE_SSE2) || defined(__i386__) +#if defined(__x86_64__) || defined(__i386__) #ifdef _MSC_VER #include @@ -287,10 +287,10 @@ x86_hypervisor_tsc_frequency_khz(void) return 0; } -#else /* defined(USE_SSE2) || defined(__i386__) */ +#else /* defined(__x86_64__) || defined(__i386__) */ /* prevent linker complaints about empty module */ extern int pg_cpu_x86_dummy_variable; int pg_cpu_x86_dummy_variable = 0; -#endif /* ! (USE_SSE2 || __i386__) */ +#endif /* ! (__x86_64__ || __i386__) */ diff --git a/src/test/modules/test_misc/t/012_ddlutils.pl b/src/test/modules/test_misc/t/012_ddlutils.pl index bcdb831a6761a..c4096879922e1 100644 --- a/src/test/modules/test_misc/t/012_ddlutils.pl +++ b/src/test/modules/test_misc/t/012_ddlutils.pl @@ -93,7 +93,7 @@ sub ddl_filter # Pretty-printed output $result = $node->safe_psql('postgres', - q{SELECT * FROM pg_get_role_ddl('regress_role_ddl_test2', 'pretty', 'true')} + q{SELECT * FROM pg_get_role_ddl('regress_role_ddl_test2', pretty => true)} ); like($result, qr/\n\s+SUPERUSER/, 'role pretty-print indents attributes'); @@ -123,7 +123,7 @@ sub ddl_filter # Memberships suppressed $result = $node->safe_psql('postgres', - q{SELECT * FROM pg_get_role_ddl('regress_role_ddl_member', 'memberships', 'false')} + q{SELECT * FROM pg_get_role_ddl('regress_role_ddl_member', memberships => false)} ); unlike($result, qr/GRANT/, 'memberships suppressed'); @@ -176,18 +176,21 @@ sub ddl_filter q{SELECT count(*) FROM pg_get_database_ddl(NULL)}); is($result, '0', 'NULL database returns no rows'); -# Invalid option +# Invalid option (bad boolean cast) ($ret, $stdout, $stderr) = $node->psql('postgres', - q{SELECT * FROM pg_get_database_ddl('regression_ddlutils_test', 'owner', 'invalid')} + q{SELECT * FROM pg_get_database_ddl('regression_ddlutils_test', owner => 'invalid')} ); isnt($ret, 0, 'invalid boolean option errors'); -like($stderr, qr/invalid value/, 'invalid option error message'); +like( + $stderr, + qr/invalid input syntax for type boolean/, + 'invalid option error message'); -# Duplicate option +# Duplicate named argument ($ret, $stdout, $stderr) = $node->psql( 'postgres', q{SELECT * FROM pg_get_database_ddl('regression_ddlutils_test', - 'owner', 'false', 'owner', 'true')}); + owner => false, owner => true)}); isnt($ret, 0, 'duplicate option errors'); # Basic output (without locale details) @@ -218,9 +221,17 @@ sub ddl_filter 'postgres', q{SELECT pg_get_database_ddl FROM pg_get_database_ddl('regression_ddlutils_test', - 'pretty', 'true', 'tablespace', 'false')})); + pretty => true, tablespace => false)})); like($result, qr/\n\s+WITH TEMPLATE/, 'database DDL pretty-prints WITH'); +# Owner suppressed +$result = ddl_filter( + $node->safe_psql( + 'postgres', + q{SELECT pg_get_database_ddl + FROM pg_get_database_ddl('regression_ddlutils_test', owner => false)})); +unlike($result, qr/OWNER/, 'database DDL owner suppressed'); + # Permission check $node->safe_psql( 'postgres', q{ @@ -292,14 +303,14 @@ sub ddl_filter $result = $node->safe_psql( 'postgres', q{SELECT * FROM pg_get_tablespace_ddl('regress_allopt_tblsp', - 'pretty', 'true')}); + pretty => true)}); like($result, qr/\n\s+OWNER/, 'tablespace DDL pretty-prints OWNER'); # Owner suppressed $result = $node->safe_psql( 'postgres', q{SELECT * FROM pg_get_tablespace_ddl('regress_allopt_tblsp', - 'owner', 'false')}); + owner => false)}); unlike($result, qr/OWNER/, 'tablespace DDL owner suppressed'); # Lookup by OID diff --git a/src/test/recovery/meson.build b/src/test/recovery/meson.build index 9eb8ed114254a..ad0d85f41897e 100644 --- a/src/test/recovery/meson.build +++ b/src/test/recovery/meson.build @@ -62,6 +62,7 @@ tests += { 't/051_effective_wal_level.pl', 't/052_checkpoint_segment_missing.pl', 't/053_standby_login_event_trigger.pl', + 't/054_unlogged_sequence_promotion.pl', ], }, } diff --git a/src/test/recovery/t/051_effective_wal_level.pl b/src/test/recovery/t/051_effective_wal_level.pl index c45eddc7383a7..d4bc7f0aa407c 100644 --- a/src/test/recovery/t/051_effective_wal_level.pl +++ b/src/test/recovery/t/051_effective_wal_level.pl @@ -76,8 +76,7 @@ sub wait_for_logical_decoding_disabled # Wait for the checkpointer to disable logical decoding. wait_for_logical_decoding_disabled($primary); test_wal_level($primary, "replica|replica", - "logical decoding disabled after repack" -); + "logical decoding disabled after repack"); # Create a new logical slot and check that effective_wal_level must be increased # to 'logical'. diff --git a/src/test/recovery/t/054_unlogged_sequence_promotion.pl b/src/test/recovery/t/054_unlogged_sequence_promotion.pl new file mode 100644 index 0000000000000..96d1e4bf18be4 --- /dev/null +++ b/src/test/recovery/t/054_unlogged_sequence_promotion.pl @@ -0,0 +1,34 @@ +# Copyright (c) 2026, PostgreSQL Global Development Group + +# Test that unlogged sequences created on a primary can be read after +# promotion of a standby that replayed their init fork. + +use strict; +use warnings FATAL => 'all'; +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +my $node_primary = PostgreSQL::Test::Cluster->new('primary'); +$node_primary->init(allows_streaming => 1); +$node_primary->start; + +my $backup_name = 'my_backup'; +$node_primary->backup($backup_name); + +my $node_standby = PostgreSQL::Test::Cluster->new('standby'); +$node_standby->init_from_backup($node_primary, $backup_name, + has_streaming => 1); +$node_standby->start; + +# Create the unlogged sequence after the standby has started, so its init fork +# is generated by WAL replay on the standby. +$node_primary->safe_psql('postgres', "CREATE UNLOGGED SEQUENCE ulseq"); +$node_primary->wait_for_replay_catchup($node_standby); + +$node_standby->promote; + +is($node_standby->safe_psql('postgres', "SELECT nextval('ulseq')"), + 1, 'unlogged sequence can be read after standby promotion'); + +done_testing(); diff --git a/src/test/regress/expected/constraints.out b/src/test/regress/expected/constraints.out index e54fec7fb5777..83f97f684d542 100644 --- a/src/test/regress/expected/constraints.out +++ b/src/test/regress/expected/constraints.out @@ -446,8 +446,15 @@ alter table parted_ch_2 alter constraint cc_2 enforced; --error ERROR: check constraint "cc_2" of relation "parted_ch_2" is violated by some row delete from parted_ch where a = 16; alter table parted_ch_2 alter constraint cc_2 enforced; -alter table parted_ch_2 alter constraint cc not enforced; -alter table parted_ch_2 alter constraint cc_1 not enforced; +alter table parted_ch_2 alter constraint cc not enforced; --error +ERROR: cannot mark inherited constraint "cc" as NOT ENFORCED +DETAIL: The matching constraint on parent table "parted_ch" is ENFORCED. +alter table only parted_ch_2 alter constraint cc not enforced; --error +ERROR: cannot mark inherited constraint "cc" as NOT ENFORCED +DETAIL: The matching constraint on parent table "parted_ch" is ENFORCED. +alter table parted_ch_2 alter constraint cc_1 not enforced; --error +ERROR: cannot mark inherited constraint "cc_1" as NOT ENFORCED +DETAIL: The matching constraint on parent table "parted_ch" is ENFORCED. alter table parted_ch_2 alter constraint cc_2 not enforced; --check these CHECK constraint status again select * from check_constraint_status; @@ -457,12 +464,12 @@ select * from check_constraint_status; cc | parted_ch_1 | t | t cc | parted_ch_11 | t | t cc | parted_ch_12 | t | t - cc | parted_ch_2 | f | f + cc | parted_ch_2 | t | t cc_1 | parted_ch | t | t cc_1 | parted_ch_1 | t | t cc_1 | parted_ch_11 | t | t cc_1 | parted_ch_12 | t | t - cc_1 | parted_ch_2 | f | f + cc_1 | parted_ch_2 | t | t cc_2 | parted_ch_2 | f | f (11 rows) diff --git a/src/test/regress/expected/copy.out b/src/test/regress/expected/copy.out index 37498cdd6e7aa..ace38225623af 100644 --- a/src/test/regress/expected/copy.out +++ b/src/test/regress/expected/copy.out @@ -73,6 +73,15 @@ copy copytest3 to stdout csv header; c1,"col with , comma","col with "" quote" 1,a,1 2,b,2 +-- testing explicit column order +create temp table copytest_order (a int, b int, c int); +copy copytest_order from stdin; +copy copytest_order (c, b, a) to stdout; +3 2 1 +copy copytest_order (c, b, a) to stdout (format csv); +3,2,1 +copy copytest_order (c, b, a) to stdout (format json); +{"c":3,"b":2,"a":1} --- test copying in JSON mode with various styles copy (select 1 union all select 2) to stdout with (format json); {"?column?":1} diff --git a/src/test/regress/expected/for_portion_of.out b/src/test/regress/expected/for_portion_of.out index 4340897211771..207e370627e2d 100644 --- a/src/test/regress/expected/for_portion_of.out +++ b/src/test/regress/expected/for_portion_of.out @@ -2446,4 +2446,46 @@ NOTICE: fpo_before_row1: BEFORE UPDATE ROW: NOTICE: old: [10,100) NOTICE: new: [30,70) DROP TABLE fpo_update_of_trigger; +-- CURSORs +CREATE TABLE fpo_cursed ( + id int, + valid_at int4range +); +INSERT INTO fpo_cursed (id, valid_at) VALUES (1, '[10,100)'); +-- UPDATE FOR PORTION OF is not permitted with a CURSOR: +BEGIN; +DECLARE fpo_cur CURSOR FOR SELECT * FROM fpo_cursed; +FETCH NEXT FROM fpo_cur; + id | valid_at +----+---------- + 1 | [10,100) +(1 row) + +UPDATE fpo_cursed + FOR PORTION OF valid_at FROM 5 TO 6 + SET id = 2 + WHERE CURRENT OF fpo_cur; +ERROR: WHERE CURRENT OF with FOR PORTION OF is not implemented +ROLLBACK; +-- DELETE FOR PORTION OF is not permitted with a CURSOR: +BEGIN; +DECLARE fpo_cur CURSOR FOR SELECT * FROM fpo_cursed; +FETCH NEXT FROM fpo_cur; + id | valid_at +----+---------- + 1 | [10,100) +(1 row) + +DELETE FROM fpo_cursed + FOR PORTION OF valid_at FROM 8 TO 9 + WHERE CURRENT OF fpo_cur; +ERROR: WHERE CURRENT OF with FOR PORTION OF is not implemented +ROLLBACK; +SELECT * FROM fpo_cursed; + id | valid_at +----+---------- + 1 | [10,100) +(1 row) + +DROP TABLE fpo_cursed; RESET datestyle; diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out index 3d8e8d8afd241..3c2ff55d3baa9 100644 --- a/src/test/regress/expected/inherit.out +++ b/src/test/regress/expected/inherit.out @@ -1479,6 +1479,98 @@ NOTICE: drop cascades to 3 other objects DETAIL: drop cascades to table p1_c1 drop cascades to table p1_c2 drop cascades to table p1_c3 +-- an inherited CHECK constraint cannot be NOT ENFORCED under an ENFORCED parent +create table p1(f1 int constraint p1_a_check check (f1 > 0) enforced); +create table p1_c1() inherits(p1); +alter table p1_c1 alter constraint p1_a_check not enforced; --error +ERROR: cannot mark inherited constraint "p1_a_check" as NOT ENFORCED +DETAIL: The matching constraint on parent table "p1" is ENFORCED. +alter table p1 alter constraint p1_a_check not enforced; --ok +alter table p1_c1 alter constraint p1_a_check not enforced; --ok +drop table p1 cascade; +NOTICE: drop cascades to table p1_c1 +-- recursive NOT ENFORCED merges with ENFORCED constraints from other parents +create table p1(a int constraint p1_a_check check (a > 0) enforced); +create table p2(a int constraint p1_a_check check (a > 0) enforced); +create table p1_c1() inherits (p1, p2); +NOTICE: merging multiple inherited definitions of column "a" +-- make p1_c1_g2's oid smaller than p1_c1_g1's, to ensure ordering of +-- pg_constraint rows to not impact the test results. +create table p1_c1_g2() inherits (p1_c1); +create table p1_c1_g1() inherits (p1_c1); +alter table p1_c1_g2 inherit p1_c1_g1; +create table p1_c2() inherits (p1); +alter table p1 alter constraint p1_a_check not enforced; --ok +select conname, conenforced, convalidated, conrelid::regclass +from pg_constraint +where conname = 'p1_a_check' and contype = 'c' +order by conrelid::regclass::text collate "C"; + conname | conenforced | convalidated | conrelid +------------+-------------+--------------+---------- + p1_a_check | f | f | p1 + p1_a_check | t | t | p1_c1 + p1_a_check | t | t | p1_c1_g1 + p1_a_check | t | t | p1_c1_g2 + p1_a_check | f | f | p1_c2 + p1_a_check | t | t | p2 +(6 rows) + +alter table p1_c1 alter constraint p1_a_check not enforced; --error +ERROR: cannot mark inherited constraint "p1_a_check" as NOT ENFORCED +DETAIL: The matching constraint on parent table "p2" is ENFORCED. +alter table p2 alter constraint p1_a_check not enforced; --ok +alter table p1_c1 alter constraint p1_a_check not enforced; --ok +drop table p1, p2 cascade; +NOTICE: drop cascades to 4 other objects +DETAIL: drop cascades to table p1_c1 +drop cascades to table p1_c1_g1 +drop cascades to table p1_c1_g2 +drop cascades to table p1_c2 +-- recursive NOT ENFORCED can change all matching enforced parents together +create table gp(a int constraint gp_a_check check (a > 0) enforced); +create table p1() inherits (gp); +create table p2() inherits (gp); +create table p1_c1() inherits (p1, p2); +NOTICE: merging multiple inherited definitions of column "a" +alter table gp alter constraint gp_a_check not enforced; --ok +select conname, conenforced, convalidated, conrelid::regclass +from pg_constraint +where conname = 'gp_a_check' and contype = 'c' +order by conrelid::regclass::text collate "C"; + conname | conenforced | convalidated | conrelid +------------+-------------+--------------+---------- + gp_a_check | f | f | gp + gp_a_check | f | f | p1 + gp_a_check | f | f | p1_c1 + gp_a_check | f | f | p2 +(4 rows) + +drop table gp cascade; +NOTICE: drop cascades to 3 other objects +DETAIL: drop cascades to table p1 +drop cascades to table p2 +drop cascades to table p1_c1 +-- recursive NOT ENFORCED can change a direct-plus-indirect diamond together +create table gp(a int constraint gp_a_check check (a > 0) enforced); +create table p1_c1() inherits (gp); +create table p1() inherits (gp); +alter table p1_c1 inherit p1; +alter table gp alter constraint gp_a_check not enforced; --ok +select conname, conenforced, convalidated, conrelid::regclass +from pg_constraint +where conname = 'gp_a_check' and contype = 'c' +order by conrelid::regclass::text collate "C"; + conname | conenforced | convalidated | conrelid +------------+-------------+--------------+---------- + gp_a_check | f | f | gp + gp_a_check | f | f | p1 + gp_a_check | f | f | p1_c1 +(3 rows) + +drop table gp cascade; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to table p1 +drop cascades to table p1_c1 --for "no inherit" check constraint, it will not recurse to child table create table p1(f1 int constraint p1_a_check check (f1 > 0) no inherit not enforced); create table p1_c1(f1 int constraint p1_a_check check (f1 > 0) not enforced); diff --git a/src/test/regress/expected/planner_est.out b/src/test/regress/expected/planner_est.out index b62a47552fad4..236cb274a78d9 100644 --- a/src/test/regress/expected/planner_est.out +++ b/src/test/regress/expected/planner_est.out @@ -210,4 +210,15 @@ false, true, false, true); -> Result (cost=N..N rows=1 width=N) (4 rows) +-- Verify that scalarineqsel() works on "char" columns +CREATE TEMP TABLE char_table_1 AS + SELECT i::"char" AS c FROM generate_series(64,96) i; +ANALYZE char_table_1; +EXPLAIN (COSTS OFF) SELECT * FROM char_table_1 WHERE c < 'Q'; + QUERY PLAN +----------------------------- + Seq Scan on char_table_1 + Filter: (c < 'Q'::"char") +(2 rows) + DROP FUNCTION explain_mask_costs(text, bool, bool, bool, bool); diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out index bbb1db3c43347..fa550676f8355 100644 --- a/src/test/regress/expected/stats.out +++ b/src/test/regress/expected/stats.out @@ -2019,6 +2019,10 @@ BEGIN END; $$; SELECT fastpath_exceeded AS fastpath_exceeded_before FROM pg_stat_lock WHERE locktype = 'relation' \gset +-- Test pg_stat_get_backend_lock() +SELECT fastpath_exceeded AS backend_fastpath_exceeded_before + FROM pg_stat_get_backend_lock(pg_backend_pid()) + WHERE locktype = 'relation' \gset -- Needs a lock on each partition SELECT count(*) FROM part_test; count @@ -2039,5 +2043,13 @@ SELECT fastpath_exceeded > :fastpath_exceeded_before FROM pg_stat_lock WHERE loc t (1 row) +SELECT fastpath_exceeded > :backend_fastpath_exceeded_before + FROM pg_stat_get_backend_lock(pg_backend_pid()) + WHERE locktype = 'relation'; + ?column? +---------- + t +(1 row) + DROP TABLE part_test; -- End of Stats Test diff --git a/src/test/regress/expected/tsrf.out b/src/test/regress/expected/tsrf.out index c4f7b187f5baf..005f1268f4b2c 100644 --- a/src/test/regress/expected/tsrf.out +++ b/src/test/regress/expected/tsrf.out @@ -267,7 +267,21 @@ ERROR: window function calls cannot contain set-returning function calls LINE 1: SELECT min(generate_series(1, 3)) OVER() FROM few; ^ HINT: You might be able to move the set-returning function into a LATERAL FROM item. --- SRFs are normally computed after window functions +--- ... nor in window definitions +SELECT sum(id) OVER (PARTITION BY generate_series(1, 3)) FROM few; +ERROR: set-returning functions are not allowed in window definitions +LINE 1: SELECT sum(id) OVER (PARTITION BY generate_series(1, 3)) FRO... + ^ +SELECT sum(id) OVER (ORDER BY generate_series(1, 3)) FROM few; +ERROR: set-returning functions are not allowed in window definitions +LINE 1: SELECT sum(id) OVER (ORDER BY generate_series(1, 3)) FROM fe... + ^ +SELECT sum(id) OVER (ROWS BETWEEN UNBOUNDED PRECEDING + AND generate_series(1, 3) FOLLOWING) FROM few; +ERROR: set-returning functions are not allowed in window definitions +LINE 2: AND generate_series(1, 3) FOLLOWING) FR... + ^ +-- SRFs are computed after window functions SELECT id,lag(id) OVER(), count(*) OVER(), generate_series(1,3) FROM few; id | lag | count | generate_series ----+-----+-------+----------------- @@ -282,15 +296,6 @@ SELECT id,lag(id) OVER(), count(*) OVER(), generate_series(1,3) FROM few; 3 | 2 | 3 | 3 (9 rows) --- unless referencing SRFs -SELECT SUM(count(*)) OVER(PARTITION BY generate_series(1,3) ORDER BY generate_series(1,3)), generate_series(1,3) g FROM few GROUP BY g; - sum | g ------+--- - 3 | 1 - 3 | 2 - 3 | 3 -(3 rows) - -- sorting + grouping SELECT few.dataa, count(*), min(id), max(id), generate_series(1,3) FROM few GROUP BY few.dataa ORDER BY 5, 1; dataa | count | min | max | generate_series diff --git a/src/test/regress/sql/constraints.sql b/src/test/regress/sql/constraints.sql index dc133b124bbfd..9705962eb9f0e 100644 --- a/src/test/regress/sql/constraints.sql +++ b/src/test/regress/sql/constraints.sql @@ -309,8 +309,9 @@ select * from check_constraint_status; alter table parted_ch_2 alter constraint cc_2 enforced; --error delete from parted_ch where a = 16; alter table parted_ch_2 alter constraint cc_2 enforced; -alter table parted_ch_2 alter constraint cc not enforced; -alter table parted_ch_2 alter constraint cc_1 not enforced; +alter table parted_ch_2 alter constraint cc not enforced; --error +alter table only parted_ch_2 alter constraint cc not enforced; --error +alter table parted_ch_2 alter constraint cc_1 not enforced; --error alter table parted_ch_2 alter constraint cc_2 not enforced; --check these CHECK constraint status again diff --git a/src/test/regress/sql/copy.sql b/src/test/regress/sql/copy.sql index 094fd76c12bfb..507b822946fb2 100644 --- a/src/test/regress/sql/copy.sql +++ b/src/test/regress/sql/copy.sql @@ -82,6 +82,15 @@ this is just a line full of junk that would error out if parsed copy copytest3 to stdout csv header; +-- testing explicit column order +create temp table copytest_order (a int, b int, c int); +copy copytest_order from stdin; +1 2 3 +\. +copy copytest_order (c, b, a) to stdout; +copy copytest_order (c, b, a) to stdout (format csv); +copy copytest_order (c, b, a) to stdout (format json); + --- test copying in JSON mode with various styles copy (select 1 union all select 2) to stdout with (format json); copy (select 1 as foo union all select 2) to stdout with (format json); diff --git a/src/test/regress/sql/for_portion_of.sql b/src/test/regress/sql/for_portion_of.sql index 7b08f8cf45e14..a3c41abf7b7dd 100644 --- a/src/test/regress/sql/for_portion_of.sql +++ b/src/test/regress/sql/for_portion_of.sql @@ -1591,4 +1591,32 @@ UPDATE fpo_update_of_trigger SET id = 2; DROP TABLE fpo_update_of_trigger; +-- CURSORs +CREATE TABLE fpo_cursed ( + id int, + valid_at int4range +); +INSERT INTO fpo_cursed (id, valid_at) VALUES (1, '[10,100)'); + +-- UPDATE FOR PORTION OF is not permitted with a CURSOR: +BEGIN; +DECLARE fpo_cur CURSOR FOR SELECT * FROM fpo_cursed; +FETCH NEXT FROM fpo_cur; +UPDATE fpo_cursed + FOR PORTION OF valid_at FROM 5 TO 6 + SET id = 2 + WHERE CURRENT OF fpo_cur; +ROLLBACK; + +-- DELETE FOR PORTION OF is not permitted with a CURSOR: +BEGIN; +DECLARE fpo_cur CURSOR FOR SELECT * FROM fpo_cursed; +FETCH NEXT FROM fpo_cur; +DELETE FROM fpo_cursed + FOR PORTION OF valid_at FROM 8 TO 9 + WHERE CURRENT OF fpo_cur; +ROLLBACK; +SELECT * FROM fpo_cursed; +DROP TABLE fpo_cursed; + RESET datestyle; diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql index 8f986904389c5..072fca13c1351 100644 --- a/src/test/regress/sql/inherit.sql +++ b/src/test/regress/sql/inherit.sql @@ -535,6 +535,58 @@ where conname = 'inh_check_constraint3' and contype = 'c' order by conrelid::regclass::text collate "C"; drop table p1 cascade; +-- an inherited CHECK constraint cannot be NOT ENFORCED under an ENFORCED parent +create table p1(f1 int constraint p1_a_check check (f1 > 0) enforced); +create table p1_c1() inherits(p1); +alter table p1_c1 alter constraint p1_a_check not enforced; --error +alter table p1 alter constraint p1_a_check not enforced; --ok +alter table p1_c1 alter constraint p1_a_check not enforced; --ok +drop table p1 cascade; + +-- recursive NOT ENFORCED merges with ENFORCED constraints from other parents +create table p1(a int constraint p1_a_check check (a > 0) enforced); +create table p2(a int constraint p1_a_check check (a > 0) enforced); +create table p1_c1() inherits (p1, p2); +-- make p1_c1_g2's oid smaller than p1_c1_g1's, to ensure ordering of +-- pg_constraint rows to not impact the test results. +create table p1_c1_g2() inherits (p1_c1); +create table p1_c1_g1() inherits (p1_c1); +alter table p1_c1_g2 inherit p1_c1_g1; +create table p1_c2() inherits (p1); +alter table p1 alter constraint p1_a_check not enforced; --ok +select conname, conenforced, convalidated, conrelid::regclass +from pg_constraint +where conname = 'p1_a_check' and contype = 'c' +order by conrelid::regclass::text collate "C"; +alter table p1_c1 alter constraint p1_a_check not enforced; --error +alter table p2 alter constraint p1_a_check not enforced; --ok +alter table p1_c1 alter constraint p1_a_check not enforced; --ok +drop table p1, p2 cascade; + +-- recursive NOT ENFORCED can change all matching enforced parents together +create table gp(a int constraint gp_a_check check (a > 0) enforced); +create table p1() inherits (gp); +create table p2() inherits (gp); +create table p1_c1() inherits (p1, p2); +alter table gp alter constraint gp_a_check not enforced; --ok +select conname, conenforced, convalidated, conrelid::regclass +from pg_constraint +where conname = 'gp_a_check' and contype = 'c' +order by conrelid::regclass::text collate "C"; +drop table gp cascade; + +-- recursive NOT ENFORCED can change a direct-plus-indirect diamond together +create table gp(a int constraint gp_a_check check (a > 0) enforced); +create table p1_c1() inherits (gp); +create table p1() inherits (gp); +alter table p1_c1 inherit p1; +alter table gp alter constraint gp_a_check not enforced; --ok +select conname, conenforced, convalidated, conrelid::regclass +from pg_constraint +where conname = 'gp_a_check' and contype = 'c' +order by conrelid::regclass::text collate "C"; +drop table gp cascade; + --for "no inherit" check constraint, it will not recurse to child table create table p1(f1 int constraint p1_a_check check (f1 > 0) no inherit not enforced); create table p1_c1(f1 int constraint p1_a_check check (f1 > 0) not enforced); diff --git a/src/test/regress/sql/planner_est.sql b/src/test/regress/sql/planner_est.sql index 53210d5baad8e..2b696a4e4e55d 100644 --- a/src/test/regress/sql/planner_est.sql +++ b/src/test/regress/sql/planner_est.sql @@ -147,4 +147,10 @@ SELECT explain_mask_costs($$ SELECT * FROM tenk1 WHERE unique1 <> ALL (ARRAY[1, 2, 98, (SELECT 99), NULL]);$$, false, true, false, true); +-- Verify that scalarineqsel() works on "char" columns +CREATE TEMP TABLE char_table_1 AS + SELECT i::"char" AS c FROM generate_series(64,96) i; +ANALYZE char_table_1; +EXPLAIN (COSTS OFF) SELECT * FROM char_table_1 WHERE c < 'Q'; + DROP FUNCTION explain_mask_costs(text, bool, bool, bool, bool); diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql index 610fd21fae447..f5683302a75c0 100644 --- a/src/test/regress/sql/stats.sql +++ b/src/test/regress/sql/stats.sql @@ -998,6 +998,11 @@ $$; SELECT fastpath_exceeded AS fastpath_exceeded_before FROM pg_stat_lock WHERE locktype = 'relation' \gset +-- Test pg_stat_get_backend_lock() +SELECT fastpath_exceeded AS backend_fastpath_exceeded_before + FROM pg_stat_get_backend_lock(pg_backend_pid()) + WHERE locktype = 'relation' \gset + -- Needs a lock on each partition SELECT count(*) FROM part_test; @@ -1006,6 +1011,10 @@ SELECT pg_stat_force_next_flush(); SELECT fastpath_exceeded > :fastpath_exceeded_before FROM pg_stat_lock WHERE locktype = 'relation'; +SELECT fastpath_exceeded > :backend_fastpath_exceeded_before + FROM pg_stat_get_backend_lock(pg_backend_pid()) + WHERE locktype = 'relation'; + DROP TABLE part_test; -- End of Stats Test diff --git a/src/test/regress/sql/tsrf.sql b/src/test/regress/sql/tsrf.sql index 7c22529a0db0a..8442ba9e743b3 100644 --- a/src/test/regress/sql/tsrf.sql +++ b/src/test/regress/sql/tsrf.sql @@ -82,10 +82,14 @@ SELECT sum((3 = ANY(SELECT lag(x) over(order by x) -- SRFs are not allowed in window function arguments, either SELECT min(generate_series(1, 3)) OVER() FROM few; --- SRFs are normally computed after window functions +--- ... nor in window definitions +SELECT sum(id) OVER (PARTITION BY generate_series(1, 3)) FROM few; +SELECT sum(id) OVER (ORDER BY generate_series(1, 3)) FROM few; +SELECT sum(id) OVER (ROWS BETWEEN UNBOUNDED PRECEDING + AND generate_series(1, 3) FOLLOWING) FROM few; + +-- SRFs are computed after window functions SELECT id,lag(id) OVER(), count(*) OVER(), generate_series(1,3) FROM few; --- unless referencing SRFs -SELECT SUM(count(*)) OVER(PARTITION BY generate_series(1,3) ORDER BY generate_series(1,3)), generate_series(1,3) g FROM few GROUP BY g; -- sorting + grouping SELECT few.dataa, count(*), min(id), max(id), generate_series(1,3) FROM few GROUP BY few.dataa ORDER BY 5, 1; diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl index 01f3573e1fd0c..cb7c2a061934f 100644 --- a/src/test/ssl/t/001_ssltests.pl +++ b/src/test/ssl/t/001_ssltests.pl @@ -885,7 +885,8 @@ sub switch_server_cert "$common_connstr user=ssltestuser sslcert=ssl/client-revoked.crt " . sslkey('client-revoked.key'), "certificate authorization fails with revoked client cert", - expected_stderr => qr!SSL error: (ssl[a-z0-9/]*|tls) alert certificate revoked!, + expected_stderr => + qr!SSL error: (ssl[a-z0-9/]*|tls) alert certificate revoked!, log_like => [ qr{Client certificate verification failed at depth 0: certificate revoked}, qr{Failed certificate data \(unverified\): subject "/CN=ssltestuser", serial number \d+, issuer "/CN=Test CA for PostgreSQL SSL regression test client certs"}, @@ -987,7 +988,8 @@ sub switch_server_cert "$common_connstr user=ssltestuser sslcert=ssl/client-revoked.crt " . sslkey('client-revoked.key'), "certificate authorization fails with revoked client cert with server-side CRL directory", - expected_stderr => qr!SSL error: (ssl[a-z0-9/]*|tls) alert certificate revoked!, + expected_stderr => + qr!SSL error: (ssl[a-z0-9/]*|tls) alert certificate revoked!, log_like => [ qr{Client certificate verification failed at depth 0: certificate revoked}, qr{Failed certificate data \(unverified\): subject "/CN=ssltestuser", serial number \d+, issuer "/CN=Test CA for PostgreSQL SSL regression test client certs"}, @@ -998,7 +1000,8 @@ sub switch_server_cert "$common_connstr user=ssltestuser sslcert=ssl/client-revoked-utf8.crt " . sslkey('client-revoked-utf8.key'), "certificate authorization fails with revoked UTF-8 client cert with server-side CRL directory", - expected_stderr => qr!SSL error: (ssl[a-z0-9/]*|tls) alert certificate revoked!, + expected_stderr => + qr!SSL error: (ssl[a-z0-9/]*|tls) alert certificate revoked!, log_like => [ qr{Client certificate verification failed at depth 0: certificate revoked}, qr{Failed certificate data \(unverified\): subject "/CN=\\xce\\x9f\\xce\\xb4\\xcf\\x85\\xcf\\x83\\xcf\\x83\\xce\\xad\\xce\\xb1\\xcf\\x82", serial number \d+, issuer "/CN=Test CA for PostgreSQL SSL regression test client certs"}, diff --git a/src/test/subscription/t/100_bugs.pl b/src/test/subscription/t/100_bugs.pl index 31dc63ae8c413..075c52f98fd2e 100644 --- a/src/test/subscription/t/100_bugs.pl +++ b/src/test/subscription/t/100_bugs.pl @@ -644,8 +644,10 @@ BEGIN ); # Clean up -$node_publisher->safe_psql('postgres', "SELECT pg_drop_replication_slot('upsert_slot')"); -$node_publisher->safe_psql('postgres', "DROP PUBLICATION pub_rowfilter_error"); +$node_publisher->safe_psql('postgres', + "SELECT pg_drop_replication_slot('upsert_slot')"); +$node_publisher->safe_psql('postgres', + "DROP PUBLICATION pub_rowfilter_error"); $node_publisher->safe_psql('postgres', "DROP TABLE tab_upsert"); $node_publisher->stop('fast'); diff --git a/src/tools/git_changelog b/src/tools/git_changelog index d5f651ecf5b0e..c92c6cfe3e32d 100755 --- a/src/tools/git_changelog +++ b/src/tools/git_changelog @@ -59,7 +59,7 @@ require IPC::Open2; # (We could get this from "git branches", but not worth the trouble.) # NB: master must be first! my @BRANCHES = qw(master - REL_18_STABLE + REL_19_STABLE REL_18_STABLE REL_17_STABLE REL_16_STABLE REL_15_STABLE REL_14_STABLE REL_13_STABLE REL_12_STABLE REL_11_STABLE REL_10_STABLE REL9_6_STABLE REL9_5_STABLE REL9_4_STABLE REL9_3_STABLE REL9_2_STABLE REL9_1_STABLE REL9_0_STABLE diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index c5db6ca67051c..3a2720fb5f988 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -525,6 +525,7 @@ ConnType ConnectionStateEnum ConnectionTiming ConnectionWarning +ConnectionWarningFilter ConsiderSplitContext Const ConstrCheck @@ -723,7 +724,6 @@ ENGINE EOM_flatten_into_method EOM_get_flat_size_method EPQState -EPlan EState EStatus EVP_CIPHER @@ -3110,7 +3110,6 @@ TParserStateActionItem TQueueDestReceiver TRGM TSAnyCacheEntry -TscClockSourceInfo TSConfigCacheEntry TSConfigInfo TSDictInfo @@ -3261,6 +3260,7 @@ TriggerInfo TriggerInstrumentation TriggerTransition TruncateStmt +TscClockSourceInfo TsmRoutine TupOutputState TupSortStatus diff --git a/src/tools/version_stamp.pl b/src/tools/version_stamp.pl index ea50ee41c691e..8f94e9e288697 100755 --- a/src/tools/version_stamp.pl +++ b/src/tools/version_stamp.pl @@ -25,7 +25,7 @@ # Major version is hard-wired into the script. We update it when we branch # a new development version. -my $majorversion = 19; +my $majorversion = 20; # Validate argument and compute derived variables my $minor = shift;