Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions nix/checks.nix
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@
"output_plugin_libraries" # needs wal_level=logical + logical-decoding infra, not exercised in the CLI variant
"btree_gist_nan" # needs btree_gist, not in the CLI prime file
"hstore_copy_binary" # needs hstore
"bpchar_btree_dedup" # needs pageinspect, not in the CLI prime file
"pgrst_watch_triggers" # needs dblink, not in the CLI prime file
# Version-specific extension tests
"z_17_ext_interface"
Expand Down
8 changes: 4 additions & 4 deletions nix/config.nix
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ in
supportedPostgresVersions = {
postgres = {
"15" = {
version = "15.19";
hash = "sha256-4aZKh6RrgluIwILkUYFhpHqrU8RWlJZPi6HfKPeFn4k=";
version = "15.20";
hash = "sha256-MOZcu7dxo5tVPtLffmViLmVkeu1V28Gb0P6FP/CghEI=";
};
"17" = {
version = "17.11";
hash = "sha256-3Sfys8Wec+0UqjMkkBJCv2mgMqY0eAXydOYmAyLUKXk=";
version = "17.12";
hash = "sha256-nPE2pwPY7i+DRlxDvk6ejnkwyCsL+6E75+JTe+dprFU=";
};
};
orioledb = {
Expand Down
33 changes: 33 additions & 0 deletions nix/tests/expected/alter_column_type_rowtype_deps.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
-- Fixes CVE-2026-6460.
--
-- What changed: after a column type change, stored expressions could still read the table's row type in its old layout.
-- That could crash the backend or expose memory.
-- Postgres now refuses ALTER COLUMN TYPE when certain stored code uses the row type.
-- It also adds the composite_type_mismatch setting.
--
-- Who notices: migrations such as `ALTER TABLE profiles ALTER COLUMN x TYPE ...`.
-- They now fail with "cannot alter table ... because function ... uses its row type" if the row type appears in any of these:
-- - a BEGIN ATOMIC function body
-- - a default for a function argument or a column
-- - an index expression on another table
-- To fix: drop the dependent object, run the ALTER, then recreate the object.
-- Functions that only take or return the row type are fine, for example `RETURNS SETOF profiles` for a PostgREST RPC.
--
-- Upstream commits: 22762f03a and others (PG 15.20), bfcb6625e and others (PG 17.12).
BEGIN;
-- The new GUC exists.
SELECT count(*) AS has_guc FROM pg_settings WHERE name = 'composite_type_mismatch';
has_guc
---------
1
(1 row)

CREATE TABLE act_tbl (a bytea);
CREATE FUNCTION act_func() RETURNS act_tbl
IMMUTABLE LANGUAGE sql BEGIN ATOMIC SELECT '(\\x4649584d45)'::act_tbl; END;
-- Must now be rejected.
SAVEPOINT s;
ALTER TABLE act_tbl ALTER COLUMN a TYPE text;
ERROR: cannot alter table "act_tbl" because function act_func() uses its row type
ROLLBACK TO SAVEPOINT s;
ROLLBACK;
36 changes: 36 additions & 0 deletions nix/tests/expected/bpchar_btree_dedup.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
-- What changed: btree deduplication isn't safe for bpchar, because bpchar equality ignores trailing spaces.
-- In rare cases index-only scans returned wrong results.
-- Postgres no longer deduplicates bpchar_ops indexes.
-- New indexes report allequalimage = false, which this test checks.
--
-- Who notices: anyone with a btree index on a `bpchar` or `char(n)` column, including primary keys and unique constraints.
-- Upstream recommends a REINDEX after the upgrade.
-- Until then, amcheck's bt_index_check() reports these indexes as corrupt ("metapage incorrectly indicates that deduplication is safe").
-- Expect support tickets about it.
-- This query lists the indexes to rebuild:
--
-- SELECT DISTINCT i.indexrelid::regclass FROM pg_index i
-- JOIN pg_class c ON c.oid = i.indexrelid
-- JOIN pg_am am ON am.oid = c.relam AND am.amname = 'btree'
-- JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY (i.indkey)
-- WHERE a.atttypid = 'bpchar'::regtype;
--
-- Upstream commits: de0d4f0b1 (PG 15.20), 74f26a193 (PG 17.12).
BEGIN;
CREATE TABLE bpchar_dedup (b bpchar, c char(10), t text) USING heap;
CREATE INDEX bpchar_dedup_b ON bpchar_dedup (b);
CREATE INDEX bpchar_dedup_c ON bpchar_dedup (c);
CREATE INDEX bpchar_dedup_t ON bpchar_dedup (t);
-- bpchar and char(n) report false after the fix.
-- text still reports true.
SELECT 'b' AS col, allequalimage FROM bt_metap('bpchar_dedup_b')
UNION ALL SELECT 'c', allequalimage FROM bt_metap('bpchar_dedup_c')
UNION ALL SELECT 't', allequalimage FROM bt_metap('bpchar_dedup_t');
col | allequalimage
-----+---------------
b | f
c | f
t | t
(3 rows)

ROLLBACK;
27 changes: 27 additions & 0 deletions nix/tests/expected/cast_function_execute_privilege.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-- Fixes CVE-2026-116303.
--
-- What changed: to_json() and to_jsonb() called a user-defined cast function without checking EXECUTE on it.
-- They now check, like normal cast evaluation.
--
-- Who notices: projects that meet both conditions:
-- - they defined a custom cast to json or jsonb
-- - they revoked EXECUTE on its function from PUBLIC, for example with a blanket revoke from anon and authenticated
-- Those roles now get "permission denied for function ..." when they convert the type to JSON.
-- That includes PostgREST responses.
-- To fix: grant EXECUTE on the cast function.
--
-- Upstream commits: 879c760d8 (PG 15.20), 9520dbea7 (PG 17.12).
BEGIN;
CREATE TYPE cast_priv_enum AS ENUM ('a');
CREATE FUNCTION cast_priv_enum_to_json(cast_priv_enum) RETURNS json
LANGUAGE sql IMMUTABLE AS $$ SELECT to_json($1::text) $$;
CREATE CAST (cast_priv_enum AS json) WITH FUNCTION cast_priv_enum_to_json(cast_priv_enum);
REVOKE ALL ON FUNCTION cast_priv_enum_to_json(cast_priv_enum) FROM PUBLIC;
SET ROLE postgres;
-- Must be denied.
SAVEPOINT s;
SELECT to_json('a'::cast_priv_enum);
ERROR: permission denied for function cast_priv_enum_to_json
ROLLBACK TO SAVEPOINT s;
RESET ROLE;
ROLLBACK;
28 changes: 28 additions & 0 deletions nix/tests/expected/drop_owned_rls_policy.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-- Fixes CVE-2026-116311.
--
-- What changed: `DROP OWNED BY some_role` could remove the role from RLS policies on tables the caller doesn't own.
-- A role could use this to escape a restrictive policy.
-- Postgres now leaves those policies alone and issues a WARNING.
--
-- Who notices: clean-up scripts that run `DROP OWNED BY some_role` as a role that doesn't own every affected table.
-- The policies stay, so a later `DROP ROLE some_role` can fail on the remaining dependency.
-- To fix: run the clean-up as the table owner (on Supabase, usually `postgres`), or remove the role from those policies first.
--
-- Upstream commits: 1f2dd21c1 (PG 15.20), 21c732aad (PG 17.12).
BEGIN;
CREATE ROLE regress_dob_role NOLOGIN;
CREATE TABLE dob_tbl (c int);
ALTER TABLE dob_tbl ENABLE ROW LEVEL SECURITY;
CREATE POLICY dob_restrict ON dob_tbl AS RESTRICTIVE TO regress_dob_role USING (c > 0);
SET ROLE regress_dob_role;
DROP OWNED BY regress_dob_role; -- should warn
WARNING: role "regress_dob_role" could not be removed from policy "dob_restrict" for table "dob_tbl"
RESET ROLE;
-- Policy must still apply to regress_dob_role.
SELECT polname, polroles::regrole[] FROM pg_policy WHERE polrelid = 'dob_tbl'::regclass;
polname | polroles
--------------+--------------------
dob_restrict | {regress_dob_role}
(1 row)

ROLLBACK;
32 changes: 32 additions & 0 deletions nix/tests/expected/function_def_target_alias.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
-- What changed: pg_get_functiondef(), pg_get_ruledef() and \d+ now always print AS before a target-table alias in UPDATE and DELETE.
-- So `UPDATE t x` becomes `UPDATE t AS x`.
-- Without AS, an alias named `set` produced a definition that failed to restore.
--
-- Who notices: queries run the same.
-- Schema-diff tools compare these printed definitions, though.
-- So `supabase db diff`, `supabase db pull` and migra may report changes to unchanged BEGIN ATOMIC functions and rules right after the upgrade.
--
-- Upstream commits: 697e1d3d5 (PG 15.20), 91246c726 (PG 17.12).
BEGIN;
CREATE TABLE fdta_tbl (id int, val int);
CREATE FUNCTION fdta_func() RETURNS void LANGUAGE sql
BEGIN ATOMIC
UPDATE fdta_tbl t SET val = t.val + 1 WHERE t.id = 1;
DELETE FROM fdta_tbl d WHERE d.id = 2;
END;
SELECT pg_get_functiondef('fdta_func'::regproc);
pg_get_functiondef
-----------------------------------------------
CREATE OR REPLACE FUNCTION public.fdta_func()+
RETURNS void +
LANGUAGE sql +
BEGIN ATOMIC +
UPDATE fdta_tbl AS t SET val = (t.val + 1) +
WHERE (t.id = 1); +
DELETE FROM fdta_tbl AS d +
WHERE (d.id = 2); +
END +

(1 row)

ROLLBACK;
64 changes: 64 additions & 0 deletions nix/tests/expected/jsonpath_gin_lookup.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
-- What changed: jsonpath searches with `@@` and `@?` on a jsonb_ops GIN index missed matching rows in these cases:
-- - a top-level scalar, for example `strict $ == "a scalar"`
-- - a method on a literal, for example `"25".double()`
-- - an AND inside a NOT
-- The test checks that index scans find the same rows as a sequential scan.
--
-- Who notices: these queries may return more rows after the upgrade (the correct ones).
-- Before, the result depended on whether the planner chose the index.
-- Existing indexes don't need a REINDEX.
--
-- Upstream commits: 3b2a60481 + 04a8915fa (PG 15.20), 19656b77d + 65f1b71c3 (PG 17.12).
BEGIN;
CREATE TABLE jgin (j jsonb) USING heap;
INSERT INTO jgin VALUES
('"a scalar"'),
('{"age": 25}'),
('{"age": 25.0}'),
('{"bad": false, "coauthors": 98}'),
('{"bad": true, "coauthors": 98}'),
('{"bad": false, "coauthors": 1}'),
('{}');
CREATE INDEX jgin_idx ON jgin USING gin (j jsonb_ops);
-- Ground truth (seqscan).
SET LOCAL enable_bitmapscan = off;
SELECT count(*) FROM jgin WHERE j @@ 'strict $ == "a scalar"';
count
-------
1
(1 row)

SELECT count(*) FROM jgin WHERE j @@ '$.age == "25".double()';
count
-------
2
(1 row)

SELECT count(*) FROM jgin WHERE j @@ '!(!($.bad == false) && $.coauthors == 98)';
count
-------
6
(1 row)

-- Through the GIN index: must match.
SET LOCAL enable_bitmapscan = on;
SET LOCAL enable_seqscan = off;
SELECT count(*) FROM jgin WHERE j @@ 'strict $ == "a scalar"';
count
-------
1
(1 row)

SELECT count(*) FROM jgin WHERE j @@ '$.age == "25".double()';
count
-------
2
(1 row)

SELECT count(*) FROM jgin WHERE j @@ '!(!($.bad == false) && $.coauthors == 98)';
count
-------
6
(1 row)

ROLLBACK;
81 changes: 81 additions & 0 deletions nix/tests/expected/partition_prune_range.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
-- What changed: RANGE partition pruning could skip partitions that held matching rows, so queries silently returned too few rows.
-- Two cases:
-- - a multi-column partition key, with filters on only the leading columns
-- - a DEFAULT partition next to a partition bound that uses MINVALUE or MAXVALUE
-- The test checks that counts match those with enable_partition_pruning off.
--
-- Who notices: projects with tables like these, for example partitioned by (tenant_id, created_at).
-- Affected queries may return more rows after the upgrade (the correct ones).
-- All rows sit in the right partitions, so no data needs fixing.
--
-- Upstream commits: 1a501b6e2 + 9b8c0f238 (PG 15.20), 2d8494e24 + d21d4f1ca (PG 17.12).
BEGIN;
-- Multi-column RANGE, qual on the leading column only.
CREATE TABLE ppr_mc (a int, b int) PARTITION BY RANGE (a, b);
CREATE TABLE ppr_mc1 PARTITION OF ppr_mc FOR VALUES FROM (1, 4) TO (1, 7);
CREATE TABLE ppr_mc2 PARTITION OF ppr_mc FOR VALUES FROM (1, 7) TO (3, 8);
CREATE TABLE ppr_mc3 PARTITION OF ppr_mc FOR VALUES FROM (4, 8) TO (6, 9);
CREATE TABLE ppr_mc_def PARTITION OF ppr_mc DEFAULT;
INSERT INTO ppr_mc VALUES (1, 5), (1, 8), (2, 0), (5, 8), (0, 0);
-- DEFAULT partition next to a MAXVALUE bound.
CREATE TABLE ppr_max (a int, b int) PARTITION BY RANGE (a, b);
CREATE TABLE ppr_max1 PARTITION OF ppr_max FOR VALUES FROM (13, 0) TO (19, MAXVALUE);
CREATE TABLE ppr_max_def PARTITION OF ppr_max DEFAULT;
INSERT INTO ppr_max VALUES (15, 1), (32, 0), (25, 3);
-- DEFAULT partition next to a MINVALUE bound.
CREATE TABLE ppr_min (a int, b int) PARTITION BY RANGE (a, b);
CREATE TABLE ppr_min1 PARTITION OF ppr_min FOR VALUES FROM (13, MINVALUE) TO (19, 5);
CREATE TABLE ppr_min_def PARTITION OF ppr_min DEFAULT;
INSERT INTO ppr_min VALUES (15, 1), (5, 0), (13, 3);
-- With pruning.
SELECT count(*) FROM ppr_mc WHERE a <= 1; -- (1,5), (1,8), (0,0)
count
-------
3
(1 row)

SELECT count(*) FROM ppr_max WHERE a = 32 AND b >= -7; -- (32,0)
count
-------
1
(1 row)

SELECT count(*) FROM ppr_max WHERE a >= 25 AND b >= 0; -- (32,0), (25,3)
count
-------
2
(1 row)

SELECT count(*) FROM ppr_min WHERE a = 5 AND b <= 7; -- (5,0)
count
-------
1
(1 row)

-- Without pruning: must match.
SET LOCAL enable_partition_pruning = off;
SELECT count(*) FROM ppr_mc WHERE a <= 1;
count
-------
3
(1 row)

SELECT count(*) FROM ppr_max WHERE a = 32 AND b >= -7;
count
-------
1
(1 row)

SELECT count(*) FROM ppr_max WHERE a >= 25 AND b >= 0;
count
-------
2
(1 row)

SELECT count(*) FROM ppr_min WHERE a = 5 AND b <= 7;
count
-------
1
(1 row)

ROLLBACK;
37 changes: 37 additions & 0 deletions nix/tests/expected/to_char_v_overflow.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
-- What changed: to_char() with the V format multiplies by a power of ten.
-- For example, '999V99' turns 1.23 into 123.
-- With integer input, if the result overflowed the type, to_char() printed wrong digits without an error.
-- Now it fails with "integer out of range".
-- The test cases come from upstream int4.sql.
--
-- Who notices: formatting code that uses V on large integers, for example to show amounts in cents.
-- It used to print wrong values, sometimes negative.
-- Now it fails.
-- To fix: cast the input to numeric first.
--
-- Upstream commits: 772251744 (PG 15.20), 04c25af21 (PG 17.12).
SELECT to_char(2, '9V999999999'); -- 2 * 10^9 fits
to_char
-------------
2000000000
(1 row)

SELECT to_char(214748364, '999999999V9'); -- 2147483640 fits
to_char
-------------
2147483640
(1 row)

SELECT to_char(-2, '9V999999999'); -- fits
to_char
-------------
-2000000000
(1 row)

-- These overflow int4 and must now error.
SELECT to_char(3, '9V999999999');
ERROR: integer out of range
SELECT to_char(2147483647, '9V9');
ERROR: integer out of range
SELECT to_char(1, '9V9999999999');
ERROR: integer out of range
Loading
Loading