Skip to content
Closed
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
2 changes: 2 additions & 0 deletions Dockerfile-orioledb-17
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,10 @@ RUN sed -i \
chown -R postgres:postgres /etc/postgresql-custom

# Remove timescaledb, plv8, postgis, pgrouting references (not available in orioledb build)
# and output_plugin_libraries (GUC does not exist before PG 17.11; orioledb is on an older base)
RUN sed -i 's/ timescaledb,//g;' "/etc/postgresql/postgresql.conf" && \
sed -i 's/db_user_namespace = off/#db_user_namespace = off/g;' "/etc/postgresql/postgresql.conf" && \
sed -i 's/^output_plugin_libraries/#output_plugin_libraries/g;' "/etc/postgresql/postgresql.conf" && \
sed -i 's/ timescaledb,//g; s/ plv8,//g; s/ postgis,//g; s/ pgrouting,//g' "/etc/postgresql-custom/supautils.conf"

# OrioleDB configuration
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,8 @@ This is the same PostgreSQL build that powers [Supabase](https://supabase.io), b


## Primary Features
- ✅ Postgres [postgresql-15.14](https://www.postgresql.org/docs/15/index.html)
- ✅ Postgres [postgresql-17.6](https://www.postgresql.org/docs/17/index.html)
- ✅ Postgres [postgresql-15.19](https://www.postgresql.org/docs/15/index.html)
- ✅ Postgres [postgresql-17.11](https://www.postgresql.org/docs/17/index.html)
- ✅ Postgres [orioledb-postgresql-17_11](https://github.com/orioledb/orioledb)
- ✅ Ubuntu 24.04 (Noble Numbat).
- ✅ [wal_level](https://www.postgresql.org/docs/current/runtime-config-wal.html) = logical and [max_replication_slots](https://www.postgresql.org/docs/current/runtime-config-replication.html) = 5. Ready for replication.
Expand Down
3 changes: 3 additions & 0 deletions ansible/files/postgresql_config/postgresql.conf.j2
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@ shared_buffers = 128MB # min 128kB

wal_level = logical # minimal, replica, or logical
# (change requires restart)
output_plugin_libraries = 'pgoutput, test_decoding, wal2json' # allowlist of logical decoding
# output plugins (PG 15.19 / 17.11+); wal2json is
# required by Realtime and shipped in the image
#fsync = on # flush data to disk for crash safety
# (turning this off can cause
# unrecoverable data corruption)
Expand Down
6 changes: 6 additions & 0 deletions ansible/tasks/stage2-setup-postgres.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@
when: stage2 and is_psql_oriole
become: true
block:
- name: Comment out output_plugin_libraries if orioledb build (GUC does not exist before PG 17.11)
ansible.builtin.replace:
path: '/etc/postgresql/postgresql.conf'
regexp: '^output_plugin_libraries'
replace: '#output_plugin_libraries'

- name: Append orioledb to shared_preload_libraries append within closing quote
ansible.builtin.replace:
path: '/etc/postgresql/postgresql.conf'
Expand Down
2 changes: 2 additions & 0 deletions docker/pgctld/postgresql.conf.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ max_parallel_maintenance_workers = {{.MaxParallelMaintenanceWorkers}} # taken fr

wal_level = logical # minimal, replica, or logical
# (change requires restart)
output_plugin_libraries = 'pgoutput, test_decoding, wal2json' # allowlist of logical decoding
# output plugins (PG 15.19 / 17.11+)
wal_buffers = {{.WalBuffers}} # min 32kB, -1 sets based on shared_buffers
# (change requires restart)
min_wal_size = {{.MinWalSize}}
Expand Down
4 changes: 2 additions & 2 deletions migrations/schema-15.sql
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

\restrict SupabaseTestDumpKey123

-- Dumped from database version 15.14
-- Dumped by pg_dump version 15.14
-- Dumped from database version 15.19
-- Dumped by pg_dump version 15.19

SET statement_timeout = 0;
SET lock_timeout = 0;
Expand Down
4 changes: 2 additions & 2 deletions migrations/schema-17.sql
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

\restrict SupabaseTestDumpKey123

-- Dumped from database version 17.6
-- Dumped by pg_dump version 17.6
-- Dumped from database version 17.11
-- Dumped by pg_dump version 17.11

SET statement_timeout = 0;
SET lock_timeout = 0;
Expand Down
22 changes: 22 additions & 0 deletions nix/checks.nix
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,22 @@
# Tests to skip for OrioleDB (not compatible with OrioleDB storage)
orioledbSkipTests = [
"index_advisor" # index_advisor doesn't support OrioleDB tables

# Checks CVE/behavior changes from 17.7 -> 17.11, drop when orioledb base is updated to/past these.
"operator_breaking_change" # CVE-2026-2004 (17.8)
"pgcrypto" # CVE-2026-2005 (17.8)
"pg_trgm" # CVE-2026-2006 multibyte (17.8)
"intarray_ltree_query" # CVE-2026-6473 (17.10)
"ltree_reindex" # ltree multibyte fix (17.8/17.10)
"hstore_copy_binary" # hstore recv crash fix (17.x > 17.6)
"merge_repeatable_read" # MERGE 40001 serialization fix
"multirange_create_priv" # CVE-2026-6472 (17.10)
"create_statistics_priv" # CVE-2025-12817 (17.7)
"pgcrypto_cipher_matrix" # CVE-2026-14663 (17.11)
"output_plugin_libraries" # CVE-2026-6471 (17.11)
"btree_gist_nan" # btree_gist NaN fix (17.11)
"ltree_label_overflow" # ltree comparison overflow fix (17.11)
"replica_identity_upsert" # MERGE/ON CONFLICT replica-identity check (17.7/15.15)
];

# Helper function to filter SQL files based on version
Expand Down Expand Up @@ -256,6 +272,8 @@
"pg_cron_trigger_privileges" # needs pg_cron + the postgres role and cron-schema grants from the full migrations, not in the CLI prime file
"supautils_restrict_versions" # needs the postgres role + primed hstore from the full migrations/prime, not present in the CLI variant
"amcheck" # needs the postgres/anon/authenticated/service_role roles and the default privileges from the full migrations, plus amcheck primed by prime.sql
"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
# Version-specific extension tests
"z_17_ext_interface"
"z_17_pg_stat_monitor"
Expand Down Expand Up @@ -411,6 +429,10 @@
# Add orioledb to shared_preload_libraries
perl -pi -e "s/(shared_preload_libraries = ')/\$1orioledb, /" "$PGTAP_CLUSTER/postgresql.conf"
log info "OrioleDB added to shared_preload_libraries"
else
# PG 15.19 / 17.11+: allowlist wal2json for logical decoding (GUC does not
# exist on the orioledb 17.9 base, hence the else branch)
echo "output_plugin_libraries = 'pgoutput, test_decoding, wal2json'" >> "$PGTAP_CLUSTER"/postgresql.conf
fi

# Check if postgresql.conf exists
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.14";
hash = "sha256-Bt110wXNOHDuYrOTLmYcYkVD6vmuK6N83sCk+O3QUdI=";
version = "15.19";
hash = "sha256-4aZKh6RrgluIwILkUYFhpHqrU8RWlJZPi6HfKPeFn4k=";
};
"17" = {
version = "17.6";
hash = "sha256-4GMKNgCuonURcVVjJZ7CERzV9DU6SwQOC+gn+UzXqLA=";
version = "17.11";
hash = "sha256-3Sfys8Wec+0UqjMkkBJCv2mgMqY0eAXydOYmAyLUKXk=";
};
};
orioledb = {
Expand Down
6 changes: 5 additions & 1 deletion nix/ext/tests/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,11 @@ let
self.packages.${pkgs.pkgsLinux.stdenv.hostPlatform.system}.postgresql_orioledb-17
);
settings = lib.mkForce (
((installedExtension "17").defaultSettings or { })
# output_plugin_libraries does not exist before PG 17.11; orioledb is on an
# older base, so strip it from any extension's defaultSettings (e.g. wal2json)
(removeAttrs ((installedExtension "17").defaultSettings or { }) [
"output_plugin_libraries"
])
// {
jit = "off";
shared_preload_libraries = [
Expand Down
7 changes: 5 additions & 2 deletions nix/ext/tests/lib.nix
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ let
system = pkgs.pkgsLinux.stdenv.hostPlatform.system;

expectedVersions = {
"15" = "15.14";
"17" = "17.6";
"15" = "15.19";
"17" = "17.11";
};

defaultPort = 5432;
Expand Down Expand Up @@ -115,6 +115,9 @@ let
${
if majorVersion == "orioledb-17" then
''
# OrioleDB: comment out output_plugin_libraries (GUC does not exist before PG 17.11;
# orioledb line is on a 17.9 base and would fail to start on an unknown parameter)
sed -i 's/^output_plugin_libraries/#output_plugin_libraries/' $out/postgresql.conf
# OrioleDB: also remove pgjwt from supautils privileged_extensions
sed -i 's/ pgjwt,//g;' $out/supautils.conf
# OrioleDB: append orioledb to shared_preload_libraries
Expand Down
4 changes: 4 additions & 0 deletions nix/ext/wal2json.nix
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ pkgs.buildEnv {
"multi-" + lib.concatStringsSep "-" (map (v: lib.replaceStrings [ "." ] [ "-" ] v) versions);
defaultSettings = {
wal_level = "logical";
# PG 15.19 / 17.11+ only load output plugins named here (CVE-2026-6471);
# stripped for the orioledb specialisation in nix/ext/tests/default.nix
# (GUC does not exist on orioledb's older base)
output_plugin_libraries = "pgoutput, test_decoding, wal2json";
};
};
}
47 changes: 47 additions & 0 deletions nix/tests/expected/btree_gist_nan.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
-- btree_gist NaN handling in the float4/float8 opclasses (comparisons and the
-- GiST penalty/distance functions) previously gave wrong answers when a NaN was
-- present; upstream recommends reindexing btree_gist float indexes that may hold
-- NaN after the update. This pins the post-fix within-version correctness of
-- index scans over a float8 column containing NaN.
--
-- Upstream commit: (PG 15.19) / (PG 17.11), fixed 2026-08-13.
-- (The cross-version pre-upgrade-build / post-upgrade-REINDEX leg is A3,
-- PSQL-1235.)
--
-- Refs: PSQL-1110, PSQL-1234.
BEGIN;
CREATE EXTENSION IF NOT EXISTS btree_gist;
NOTICE: extension "btree_gist" already exists, skipping
CREATE TABLE bg_nan (v float8);
INSERT INTO bg_nan VALUES (1), (2), (3), ('NaN');
CREATE INDEX bg_nan_gist ON bg_nan USING gist (v);
-- Force index scans so we exercise the opclass, not a seqscan recheck.
SET enable_seqscan = off;
-- NaN sorts as greater than every non-NaN value and equals only itself.
SELECT count(*) AS eq_nan FROM bg_nan WHERE v = 'NaN'::float8;
eq_nan
--------
1
(1 row)

SELECT count(*) AS gt_one FROM bg_nan WHERE v > 1; -- 2, 3, NaN
gt_one
--------
3
(1 row)

SELECT count(*) AS ne_two FROM bg_nan WHERE v <> 2; -- 1, 3, NaN
ne_two
--------
3
(1 row)

SELECT v FROM bg_nan WHERE v >= 3 ORDER BY v; -- 3, NaN
v
-----
3
NaN
(2 rows)

RESET enable_seqscan;
ROLLBACK;
26 changes: 26 additions & 0 deletions nix/tests/expected/create_statistics_priv.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
-- CVE-2025-12817: CREATE STATISTICS did not check CREATE privilege on the
-- schema where the statistics object is created, letting a table owner create
-- statistics objects in any schema (naming-conflict / privilege concern).
--
-- Upstream commits: 2393d374 + d202ec1f (PG 15.15), e2fb3dfa (PG 17.7). The fix
-- adds a pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE).
--
-- Verified as a non-superuser table owner. Refs: PSQL-1110, PSQL-1234.
BEGIN;
CREATE SCHEMA owned_ns;
CREATE SCHEMA forbidden_ns;
-- postgres can create in owned_ns only; it has no rights on forbidden_ns.
GRANT CREATE, USAGE ON SCHEMA owned_ns TO postgres;
SET ROLE postgres;
-- A table postgres owns, in a schema postgres controls.
CREATE TABLE owned_ns.stat_tbl (a int, b int);
INSERT INTO owned_ns.stat_tbl SELECT g % 10, g % 5 FROM generate_series(1, 100) g;
-- Positive control: stats object in owned_ns (postgres has CREATE) is allowed.
CREATE STATISTICS owned_ns.okstat (dependencies) ON a, b FROM owned_ns.stat_tbl;
-- The fix: a stats object targeting a schema where postgres lacks CREATE is denied.
SAVEPOINT no_priv;
CREATE STATISTICS forbidden_ns.badstat (dependencies) ON a, b FROM owned_ns.stat_tbl;
ERROR: permission denied for schema forbidden_ns
ROLLBACK TO SAVEPOINT no_priv;
RESET ROLE;
ROLLBACK;
47 changes: 47 additions & 0 deletions nix/tests/expected/hstore_copy_binary.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
-- Non-CVE behavior change: the hstore receive function had a NULL-pointer
-- dereference (backend crash) on COPY BINARY of an hstore whose binary form
-- contains a DUPLICATE key where the second occurrence's value is NULL.
--
-- Upstream commits: 63c05e03 (PG 15.x), 0dfbe42d (PG 17.x).
--
-- A normal INSERT cannot reproduce this: hstore de-duplicates on text input, so
-- a stored value never carries a duplicate key into the binary path. We instead
-- hand-craft a COPY-BINARY stream whose single hstore field contains the pair
-- sequence [ 'a' => '1', 'a' => NULL ] and feed it through hstore_recv via
-- COPY ... FROM. Pre-fix this crashed the backend; on the fixed builds the
-- duplicate is de-duplicated and the row loads cleanly.
--
-- pg_regress runs as the superuser supabase_admin, so lo_export / server-side
-- COPY FROM a file are permitted. Refs: PSQL-1110, PSQL-1234.
BEGIN;
CREATE TABLE hstore_dst (h hstore);
-- Materialise the crafted COPY-BINARY stream to a file (created and exported in
-- separate statements so the large object is visible to lo_export).
SELECT lo_from_bytea(81000,
'\x5047434f50590aff0d0a00'::bytea || -- COPY binary signature
'\x00000000'::bytea || '\x00000000'::bytea || -- flags + header-extension length
'\x0001'::bytea || '\x00000017'::bytea || -- one row, one field of length 23
'\x00000002'::bytea || -- hstore: 2 pairs
'\x00000001'::bytea||'\x61'::bytea||'\x00000001'::bytea||'\x31'::bytea || -- 'a' => '1'
'\x00000001'::bytea||'\x61'::bytea||'\xffffffff'::bytea || -- 'a' => NULL
'\xffff'::bytea) AS loid; -- COPY trailer
loid
-------
81000
(1 row)

SELECT lo_export(81000, '/tmp/pg_regress_hstore_dup.bin') AS exported;
exported
----------
1
(1 row)

-- Must not crash the backend; the duplicate key is de-duplicated on receive.
COPY hstore_dst FROM '/tmp/pg_regress_hstore_dup.bin' WITH (FORMAT binary);
SELECT h AS received, akeys(h) AS keys FROM hstore_dst;
received | keys
----------+------
"a"=>"1" | {a}
(1 row)

ROLLBACK;
46 changes: 46 additions & 0 deletions nix/tests/expected/intarray_ltree_query.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
-- CVE-2026-6473: memory-allocation overflow umbrella covering, among others,
-- contrib intarray query_int and contrib ltree ltxtquery / lquery parsing.
--
-- Upstream key commits: 84a9f264 (intarray/ltree), 9c2fa5b6 (ltree lquery) on
-- PG 15.18; c4d04cc4 and siblings on PG 17.10. Full list:
-- git log REL_17_6..REL_17_10 --grep='CVE-2026-6473'
--
-- Functional regression: well-formed queries parse and match correctly; a
-- malformed query raises a clean parse error instead of crashing.
--
-- Refs: PSQL-1110, PSQL-1234.
BEGIN;
-- 1) intarray query_int matching.
SELECT '{1,2,3}'::int[] @@ '2&4'::query_int AS q_and; -- expect false
q_and
-------
f
(1 row)

SELECT '{1,2,3}'::int[] @@ '2|4'::query_int AS q_or; -- expect true
q_or
------
t
(1 row)

-- 2) ltree lquery and ltxtquery matching.
SELECT 'Top.Science.Astronomy'::ltree ~ 'Top.*.Astronomy'::lquery AS lquery_match; -- true
lquery_match
--------------
t
(1 row)

SELECT 'Top.Science.Astronomy'::ltree @ 'Astronomy & Top'::ltxtquery AS ltxtquery_match; -- true
ltxtquery_match
-----------------
t
(1 row)

-- 3) A malformed query_int must raise a clean parse error, not crash.
SAVEPOINT bad_query;
SELECT '{1}'::int[] @@ '2&&'::query_int;
ERROR: syntax error
LINE 1: SELECT '{1}'::int[] @@ '2&&'::query_int;
^
ROLLBACK TO SAVEPOINT bad_query;
ROLLBACK;
28 changes: 28 additions & 0 deletions nix/tests/expected/ltree_label_overflow.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-- contrib/ltree: an integer overflow in ltree comparisons made values with more
-- than about 14,653 labels compare incorrectly; a btree index over such values
-- could become corrupt and need reindexing. This pins the post-fix comparison
-- correctness for very deep ltree values (no index needed: a btree entry that
-- size cannot exist, so this exercises the comparator directly).
--
-- Upstream commit: (PG 15.19) / (PG 17.11), fixed 2026-08-13.
--
-- Refs: PSQL-1110, PSQL-1234.
BEGIN;
CREATE EXTENSION IF NOT EXISTS ltree;
NOTICE: extension "ltree" already exists, skipping
-- Build two deep ltree values (~15,000 labels) differing only in the last label.
WITH v AS (
SELECT (repeat('a.', 15000) || 'x')::ltree AS a,
(repeat('a.', 15000) || 'y')::ltree AS b
)
SELECT nlevel(a) > 14653 AS deep_enough,
a < b AS a_lt_b,
NOT (b < a) AS b_not_lt_a,
a = a AS a_eq_a
FROM v;
deep_enough | a_lt_b | b_not_lt_a | a_eq_a
-------------+--------+------------+--------
t | t | t | t
(1 row)

ROLLBACK;
Loading
Loading