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
10 changes: 4 additions & 6 deletions Dockerfile-multigres
Original file line number Diff line number Diff line change
Expand Up @@ -263,12 +263,10 @@ RUN chown -R postgres:postgres /usr/lib/postgresql && \
RUN sed -i 's/ timescaledb,//g; s/ plv8,//g; s/ postgis,//g; s/ pgrouting,//g' \
/etc/postgresql-custom/supautils.conf

# Add orioledb to shared_preload_libraries and configure as default table access method
# orioledb-17 is pinned to 17.11+, which added output_plugin_libraries
# as an allow-list for logical decoding output plugins.
RUN sed -i "s/\(shared_preload_libraries.*\)'/\1, orioledb'/" /etc/postgresql/postgresql.conf && \
echo "default_table_access_method = 'orioledb'" >> /etc/postgresql/postgresql.conf && \
echo "output_plugin_libraries = 'pgoutput, test_decoding, wal2json'" >> /etc/postgresql/postgresql.conf
# OrioleDB configuration
RUN sed -i /etc/postgresql/postgresql.conf \
-e "/shared_preload_libraries/ s|'|'orioledb,|" \
-e "$ a default_table_access_method = 'orioledb'" \

# Register orioledb before initdb migrations run
RUN echo "CREATE EXTENSION orioledb;" > /docker-entrypoint-initdb.d/init-scripts/00-pre-init.sql && \
Expand Down
10 changes: 4 additions & 6 deletions Dockerfile-orioledb-17
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,10 @@ RUN sed -i 's/ timescaledb,//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
RUN sed -i 's/\(shared_preload_libraries.*\)'\''\(.*\)$/\1, orioledb'\''\2/' "/etc/postgresql/postgresql.conf" && \
echo "default_table_access_method = 'orioledb'" >> "/etc/postgresql/postgresql.conf"

# orioledb-17 is pinned to 17.11+, which added output_plugin_libraries as an
# allow-list for logical decoding output plugins
RUN echo "output_plugin_libraries = 'pgoutput, test_decoding, wal2json'" >> "/etc/postgresql/postgresql.conf"
RUN sed -i /etc/postgresql/postgresql.conf \
-e "/shared_preload_libraries/ s|'|'orioledb,|" \
-e "$ a default_table_access_method = 'orioledb'" \
-e "$ a output_plugin_libraries = 'pgoutput, test_decoding, wal2json'"

# Include schema migrations
COPY migrations/db /docker-entrypoint-initdb.d/
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,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
8 changes: 0 additions & 8 deletions ansible/tasks/stage2-setup-postgres.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,6 @@
path: '/etc/postgresql/postgresql.conf'
state: 'present'

# orioledb-17 is pinned to 17.11+, which added output_plugin_libraries
# as an allow-list for logical decoding output plugins.
- name: Allow wal2json as a logical decoding output plugin
ansible.builtin.lineinfile:
line: "output_plugin_libraries = 'pgoutput, test_decoding, wal2json'"
path: '/etc/postgresql/postgresql.conf'
state: 'present'

- name: Add ORIOLEDB_ENABLED environment variable
ansible.builtin.lineinfile:
line: 'ORIOLEDB_ENABLED=true'
Expand Down
6 changes: 3 additions & 3 deletions ansible/vars.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ postgres_major:
- "17"
- orioledb-17
postgres_release:
postgresorioledb-17: "17.11.0.001-orioledb"
postgres17: "17.6.1.177"
postgres15: "15.14.1.177"
postgresorioledb-17: "17.11.0.001-orioledb-rc6"
postgres17: "17.11.0.001-rc6"
postgres15: "15.19.0.001-rc6"
supabase_admin_agent_splay: 30s
###############################################################################################################
# The following block of yaml is for get_url and co throughout the playbook #
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
64 changes: 36 additions & 28 deletions nix/checks.nix
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,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 All @@ -245,7 +247,10 @@
# Concurrency/isolation specs run via pg_isolation_regress (the stock
# PostgreSQL isolation tester). Specs live in tests/isolation/specs/,
# expected output in tests/isolation/expected/. Add new spec names here.
isolationSpecList = [ "sample_isolation" ];
isolationSpecList = [
"merge_serialization"
"sample_isolation"
];
in
pkgs.writeShellApplication rec {
name = "postgres-${pgpkg.version}-check-harness";
Expand All @@ -254,24 +259,23 @@
"pipefail"
];
runtimeInputs = with pkgs; [
coreutils
bash
coreutils
getkey-script
netcat
perl
pgpkg
pg_isolation_regress
pg_prove
pg_regress
pg_isolation_regress
pgpkg
procps
python3
start-postgres-server-bin
which
getkey-script
supabase-groonga
python3
netcat
which
];

text = ''

#shellcheck disable=SC1091
source ${bashlog}
#shellcheck disable=SC1091
Expand Down Expand Up @@ -363,44 +367,48 @@
substitute ${./tests/postgresql.conf.in} "$PGTAP_CLUSTER"/postgresql.conf \
--subst-var-by PGSODIUM_GETKEY_SCRIPT "${getkey-script}/bin/pgsodium-getkey" \
--subst-var-by PRELOAD_LIBRARIES "$PRELOAD_LIBRARIES"

# Check if postgresql.conf exists
if [ ! -f "$PGTAP_CLUSTER/postgresql.conf" ]; then
log error "postgresql.conf is missing!"
exit 1
fi

{
echo "listen_addresses = '127.0.0.1'"
echo "port = ${pgPort}"
echo "session_preload_libraries = 'supautils'"

echo "dynamic_library_path = '${supautils}/lib:\$libdir'"
echo "output_plugin_libraries = 'pgoutput, test_decoding, wal2json'"
echo "session_preload_libraries = 'supautils'"
} >> "$PGTAP_CLUSTER"/postgresql.conf
echo "host all all 127.0.0.1/32 trust" >> "$PGTAP_CLUSTER/pg_hba.conf"
log info "Checking shared_preload_libraries setting:"
log info "$(grep -rn "shared_preload_libraries" "$PGTAP_CLUSTER"/postgresql.conf)"

# Configure OrioleDB if running orioledb-17 check
if ${lib.boolToString isOrioleDB}; then
log info "Configuring OrioleDB..."

# 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"
# orioledb-17 is pinned to 17.11+, which added output_plugin_libraries
# as an allow-list for logical decoding output plugins.
echo "output_plugin_libraries = 'pgoutput, test_decoding, wal2json'" >> "$PGTAP_CLUSTER/postgresql.conf"
fi

# Check if postgresql.conf exists
if [ ! -f "$PGTAP_CLUSTER/postgresql.conf" ]; then
log error "postgresql.conf is missing!"
exit 1
fi
log info "Checking shared_preload_libraries setting:"
log info "$(grep -rn "shared_preload_libraries" "$PGTAP_CLUSTER"/postgresql.conf)"

log info "Configuring local auth"
echo "host all all 127.0.0.1/32 trust" >> "$PGTAP_CLUSTER/pg_hba.conf"

# PostgreSQL startup
if [[ "$(uname)" == "Darwin" ]]; then
log_cmd pg_ctl -D "$PGTAP_CLUSTER" -l "$PGTAP_CLUSTER/postgresql.log" -o "-k $PGTAP_CLUSTER -p ${pgPort} -d 5" start
log_cmd pg_ctl -D "$PGTAP_CLUSTER" -l "$PGTAP_CLUSTER/postgresql.log" -o "-k $PGTAP_CLUSTER -p ${pgPort} -d 5" start
else
mkdir -p "$PGTAP_CLUSTER/sockets"
log_cmd pg_ctl -D "$PGTAP_CLUSTER" -l "$PGTAP_CLUSTER/postgresql.log" -o "-k $PGTAP_CLUSTER/sockets -p ${pgPort} -d 5" start
mkdir -p "$PGTAP_CLUSTER/sockets"
log_cmd pg_ctl -D "$PGTAP_CLUSTER" -l "$PGTAP_CLUSTER/postgresql.log" -o "-k $PGTAP_CLUSTER/sockets -p ${pgPort} -d 5" start
fi || {
log error "pg_ctl failed to start PostgreSQL"
log error "Contents of postgresql.log:"
cat "$PGTAP_CLUSTER"/postgresql.log
exit 1
log error "pg_ctl failed to start PostgreSQL"
log error "Contents of postgresql.log:"
cat "$PGTAP_CLUSTER"/postgresql.log
exit 1
}

log info "Waiting for PostgreSQL to be ready..."
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
4 changes: 2 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
2 changes: 2 additions & 0 deletions nix/ext/wal2json.nix
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ 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);
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;
Loading
Loading