From c2f0962d1bbf8c0d7e4221fb8560fc9d9bbe16fe Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Thu, 30 Apr 2026 17:53:16 -0500 Subject: [PATCH 01/29] ci: use binary pg_upgrade instead of pg_upgradecluster in pg-upgrade-test Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aef0efe..c50d75c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ jobs: new_pg: "13" - old_pg: "12" new_pg: "18" - name: 🔄 pg_upgrade ${{ matrix.old_pg }} → ${{ matrix.new_pg }} + name: 🔄 Binary pg_upgrade ${{ matrix.old_pg }} → ${{ matrix.new_pg }} runs-on: ubuntu-latest container: pgxn/pgxn-tools steps: @@ -56,8 +56,19 @@ jobs: run: apt-get install -y postgresql-${{ matrix.new_pg }} postgresql-server-dev-${{ matrix.new_pg }} - name: Install cat_tools into new cluster run: make install PGUSER=postgres PG_CONFIG=/usr/lib/postgresql/${{ matrix.new_pg }}/bin/pg_config - - name: Upgrade cluster to PostgreSQL ${{ matrix.new_pg }} - run: pg_upgradecluster -v ${{ matrix.new_pg }} ${{ matrix.old_pg }} test + - name: Stop old cluster, binary pg_upgrade to PostgreSQL ${{ matrix.new_pg }}, start new cluster + run: | + pg_ctlcluster ${{ matrix.old_pg }} test stop + pg_createcluster ${{ matrix.new_pg }} test + sed -i "s/^port = .*/port = 5432/" /etc/postgresql/${{ matrix.new_pg }}/test/postgresql.conf + su -c "cd /tmp && /usr/lib/postgresql/${{ matrix.new_pg }}/bin/pg_upgrade \ + -b /usr/lib/postgresql/${{ matrix.old_pg }}/bin \ + -B /usr/lib/postgresql/${{ matrix.new_pg }}/bin \ + -d /var/lib/postgresql/${{ matrix.old_pg }}/test \ + -D /var/lib/postgresql/${{ matrix.new_pg }}/test \ + -o '-c config_file=/etc/postgresql/${{ matrix.old_pg }}/test/postgresql.conf' \ + -O '-c config_file=/etc/postgresql/${{ matrix.new_pg }}/test/postgresql.conf'" postgres + pg_ctlcluster ${{ matrix.new_pg }} test start - name: Verify extension version after upgrade run: | VERSION=$(psql -U postgres -d cat_tools_upgrade_test -tAc "SELECT extversion FROM pg_extension WHERE extname = 'cat_tools'") From 972e550518e163c2d23de2e78899c625f368962a Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Thu, 30 Apr 2026 17:57:42 -0500 Subject: [PATCH 02/29] =?UTF-8?q?Fix=20binary=20pg=5Fupgrade=20PG10/11?= =?UTF-8?q?=E2=86=92PG12:=20exclude=20relhasoids=20from=20pg=5Fclass=5Fv?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `relhasoids` was removed in PG12. When binary pg_upgrade restores view definitions on PG12, `_cat_tools.pg_class_v` fails because it references `c.relhasoids` (included by the dynamic column-list builder on PG10/11 where the column exists). Explicitly add `relhasoids` to the omit list in `omit_column()` so the view is defined without it on all PostgreSQL versions, making binary pg_upgrade from PG10/PG11 to PG12+ work correctly. Co-Authored-By: Claude Sonnet 4.6 --- sql/cat_tools--0.2.0--0.2.2.sql.in | 2 +- sql/cat_tools--0.2.1--0.2.2.sql.in | 2 +- sql/cat_tools--0.2.2.sql.in | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sql/cat_tools--0.2.0--0.2.2.sql.in b/sql/cat_tools--0.2.0--0.2.2.sql.in index ecc068d..4b81e8d 100644 --- a/sql/cat_tools--0.2.0--0.2.2.sql.in +++ b/sql/cat_tools--0.2.0--0.2.2.sql.in @@ -498,7 +498,7 @@ CREATE OR REPLACE VIEW _cat_tools.pg_class_v AS LEFT JOIN pg_namespace n ON( n.oid = c.relnamespace ) ; $fmt$ - , __cat_tools.omit_column('pg_catalog.pg_class') + , __cat_tools.omit_column('pg_catalog.pg_class', array['oid', 'relhasoids']) )); REVOKE ALL ON _cat_tools.pg_class_v FROM public; diff --git a/sql/cat_tools--0.2.1--0.2.2.sql.in b/sql/cat_tools--0.2.1--0.2.2.sql.in index fef880c..7a29130 100644 --- a/sql/cat_tools--0.2.1--0.2.2.sql.in +++ b/sql/cat_tools--0.2.1--0.2.2.sql.in @@ -42,7 +42,7 @@ CREATE OR REPLACE VIEW _cat_tools.pg_class_v AS LEFT JOIN pg_namespace n ON( n.oid = c.relnamespace ) ; $fmt$ - , __cat_tools.omit_column('pg_catalog.pg_class') + , __cat_tools.omit_column('pg_catalog.pg_class', array['oid', 'relhasoids']) )); REVOKE ALL ON _cat_tools.pg_class_v FROM public; diff --git a/sql/cat_tools--0.2.2.sql.in b/sql/cat_tools--0.2.2.sql.in index bf119eb..05fa2e2 100644 --- a/sql/cat_tools--0.2.2.sql.in +++ b/sql/cat_tools--0.2.2.sql.in @@ -67,7 +67,7 @@ CREATE OR REPLACE VIEW _cat_tools.pg_class_v AS LEFT JOIN pg_namespace n ON( n.oid = c.relnamespace ) ; $fmt$ - , __cat_tools.omit_column('pg_catalog.pg_class') + , __cat_tools.omit_column('pg_catalog.pg_class', array['oid', 'relhasoids']) )); REVOKE ALL ON _cat_tools.pg_class_v FROM public; From 5e107dc7689f4ddf54b03e81772dd95b50eeff29 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Thu, 30 Apr 2026 18:04:10 -0500 Subject: [PATCH 03/29] ci: cat pg_upgrade log on failure for easier diagnosis Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c50d75c..ca1fb7b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,7 +67,8 @@ jobs: -d /var/lib/postgresql/${{ matrix.old_pg }}/test \ -D /var/lib/postgresql/${{ matrix.new_pg }}/test \ -o '-c config_file=/etc/postgresql/${{ matrix.old_pg }}/test/postgresql.conf' \ - -O '-c config_file=/etc/postgresql/${{ matrix.new_pg }}/test/postgresql.conf'" postgres + -O '-c config_file=/etc/postgresql/${{ matrix.new_pg }}/test/postgresql.conf'" postgres \ + || { echo "--- pg_upgrade logs ---"; cat /tmp/pg_upgrade_dump_*.log 2>/dev/null || true; exit 1; } pg_ctlcluster ${{ matrix.new_pg }} test start - name: Verify extension version after upgrade run: | From 484d690f34ea59268b48656c3165f1fe5fac58e3 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Thu, 30 Apr 2026 18:06:45 -0500 Subject: [PATCH 04/29] ci: run pg_upgrade in dedicated dir, dump all logs on failure pg_upgrade writes multiple log files (pg_upgrade_internal.log, pg_upgrade_server.log, pg_upgrade_dump_*.log, etc.) to its working directory. Run it from a dedicated /tmp/pg_upgrade_logs dir and use `tail -n +1` on failure to print all logs with their filenames. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ca1fb7b..413095e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,14 +61,16 @@ jobs: pg_ctlcluster ${{ matrix.old_pg }} test stop pg_createcluster ${{ matrix.new_pg }} test sed -i "s/^port = .*/port = 5432/" /etc/postgresql/${{ matrix.new_pg }}/test/postgresql.conf - su -c "cd /tmp && /usr/lib/postgresql/${{ matrix.new_pg }}/bin/pg_upgrade \ + mkdir -p /tmp/pg_upgrade_logs + chown postgres:postgres /tmp/pg_upgrade_logs + su -c "cd /tmp/pg_upgrade_logs && /usr/lib/postgresql/${{ matrix.new_pg }}/bin/pg_upgrade \ -b /usr/lib/postgresql/${{ matrix.old_pg }}/bin \ -B /usr/lib/postgresql/${{ matrix.new_pg }}/bin \ -d /var/lib/postgresql/${{ matrix.old_pg }}/test \ -D /var/lib/postgresql/${{ matrix.new_pg }}/test \ -o '-c config_file=/etc/postgresql/${{ matrix.old_pg }}/test/postgresql.conf' \ -O '-c config_file=/etc/postgresql/${{ matrix.new_pg }}/test/postgresql.conf'" postgres \ - || { echo "--- pg_upgrade logs ---"; cat /tmp/pg_upgrade_dump_*.log 2>/dev/null || true; exit 1; } + || { tail -n +1 /tmp/pg_upgrade_logs/*.log; exit 1; } pg_ctlcluster ${{ matrix.new_pg }} test start - name: Verify extension version after upgrade run: | From 14bd772fd98c77ed4c6459c446257a796faa912c Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Thu, 30 Apr 2026 18:10:17 -0500 Subject: [PATCH 05/29] ci: fix pg_upgrade checksum mismatch and empty log glob - Pass --no-data-checksums to pg_createcluster so the new cluster matches the old cluster (which pg-start creates without checksums) - Use find+xargs instead of bare glob for log output so it doesn't fail when pg_upgrade exits before writing any log files Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 413095e..6779338 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,7 +59,7 @@ jobs: - name: Stop old cluster, binary pg_upgrade to PostgreSQL ${{ matrix.new_pg }}, start new cluster run: | pg_ctlcluster ${{ matrix.old_pg }} test stop - pg_createcluster ${{ matrix.new_pg }} test + pg_createcluster ${{ matrix.new_pg }} test -- --no-data-checksums sed -i "s/^port = .*/port = 5432/" /etc/postgresql/${{ matrix.new_pg }}/test/postgresql.conf mkdir -p /tmp/pg_upgrade_logs chown postgres:postgres /tmp/pg_upgrade_logs @@ -70,7 +70,7 @@ jobs: -D /var/lib/postgresql/${{ matrix.new_pg }}/test \ -o '-c config_file=/etc/postgresql/${{ matrix.old_pg }}/test/postgresql.conf' \ -O '-c config_file=/etc/postgresql/${{ matrix.new_pg }}/test/postgresql.conf'" postgres \ - || { tail -n +1 /tmp/pg_upgrade_logs/*.log; exit 1; } + || { find /tmp/pg_upgrade_logs -name '*.log' | sort | xargs -r tail -n +1; exit 1; } pg_ctlcluster ${{ matrix.new_pg }} test start - name: Verify extension version after upgrade run: | From 59842f39841c5835c560b4c278a8a4304b4b9399 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Thu, 30 Apr 2026 18:14:29 -0500 Subject: [PATCH 06/29] ci: fix duplicate runs and checksum mismatch - Only run on push to master (not PR branches); pull_request handles those, preventing each PR push from triggering two CI runs - Detect old cluster's data_checksums setting and pass --data-checksums to pg_createcluster only if needed, instead of --no-data-checksums which doesn't exist pre-PG18 Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6779338..69f9ad9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,9 @@ name: CI -on: [push, pull_request] +on: + push: + branches: + - master + pull_request: jobs: test: strategy: @@ -59,7 +63,11 @@ jobs: - name: Stop old cluster, binary pg_upgrade to PostgreSQL ${{ matrix.new_pg }}, start new cluster run: | pg_ctlcluster ${{ matrix.old_pg }} test stop - pg_createcluster ${{ matrix.new_pg }} test -- --no-data-checksums + checksum_flag="" + if psql -U postgres -tAc "SELECT setting FROM pg_settings WHERE name='data_checksums'" | grep -q on; then + checksum_flag="-- --data-checksums" + fi + pg_createcluster ${{ matrix.new_pg }} test $checksum_flag sed -i "s/^port = .*/port = 5432/" /etc/postgresql/${{ matrix.new_pg }}/test/postgresql.conf mkdir -p /tmp/pg_upgrade_logs chown postgres:postgres /tmp/pg_upgrade_logs From 9aee08f8c92a404c01a9c03ec35723cfca3e798e Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Thu, 30 Apr 2026 18:21:33 -0500 Subject: [PATCH 07/29] ci: check data_checksums before stopping old cluster The psql query to detect checksums was running after pg_ctlcluster stop, so it always failed silently and checksum_flag was never set. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 69f9ad9..bd47c5f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,11 +62,11 @@ jobs: run: make install PGUSER=postgres PG_CONFIG=/usr/lib/postgresql/${{ matrix.new_pg }}/bin/pg_config - name: Stop old cluster, binary pg_upgrade to PostgreSQL ${{ matrix.new_pg }}, start new cluster run: | - pg_ctlcluster ${{ matrix.old_pg }} test stop checksum_flag="" if psql -U postgres -tAc "SELECT setting FROM pg_settings WHERE name='data_checksums'" | grep -q on; then checksum_flag="-- --data-checksums" fi + pg_ctlcluster ${{ matrix.old_pg }} test stop pg_createcluster ${{ matrix.new_pg }} test $checksum_flag sed -i "s/^port = .*/port = 5432/" /etc/postgresql/${{ matrix.new_pg }}/test/postgresql.conf mkdir -p /tmp/pg_upgrade_logs From b21f9e5e25efe33d0ceaefdfa44d0640065ce0d9 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 1 May 2026 13:48:43 -0500 Subject: [PATCH 08/29] ci: properly handle checksum mismatch across all PG versions PG18 changed initdb's default to enable data checksums. Use pg_controldata (works on stopped clusters) to detect the old cluster's checksum setting, then: - If old cluster has checksums: pass --data-checksums to new initdb - If not, and new initdb supports --no-data-checksums (PG18+): explicitly disable to override the new default - Otherwise: no flag needed (pre-PG18 default is no checksums) Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bd47c5f..da3e7a0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,12 +62,21 @@ jobs: run: make install PGUSER=postgres PG_CONFIG=/usr/lib/postgresql/${{ matrix.new_pg }}/bin/pg_config - name: Stop old cluster, binary pg_upgrade to PostgreSQL ${{ matrix.new_pg }}, start new cluster run: | - checksum_flag="" - if psql -U postgres -tAc "SELECT setting FROM pg_settings WHERE name='data_checksums'" | grep -q on; then - checksum_flag="-- --data-checksums" - fi pg_ctlcluster ${{ matrix.old_pg }} test stop - pg_createcluster ${{ matrix.new_pg }} test $checksum_flag + checksum_initdb_flag="" + if /usr/lib/postgresql/${{ matrix.old_pg }}/bin/pg_controldata \ + /var/lib/postgresql/${{ matrix.old_pg }}/test \ + | grep -q "Data page checksum version: [^0]"; then + checksum_initdb_flag="--data-checksums" + elif /usr/lib/postgresql/${{ matrix.new_pg }}/bin/initdb --help 2>&1 \ + | grep -q -- "--no-data-checksums"; then + checksum_initdb_flag="--no-data-checksums" + fi + if [ -n "$checksum_initdb_flag" ]; then + pg_createcluster ${{ matrix.new_pg }} test -- $checksum_initdb_flag + else + pg_createcluster ${{ matrix.new_pg }} test + fi sed -i "s/^port = .*/port = 5432/" /etc/postgresql/${{ matrix.new_pg }}/test/postgresql.conf mkdir -p /tmp/pg_upgrade_logs chown postgres:postgres /tmp/pg_upgrade_logs From 19eeed52ea389974f642df171d22ada9536b9001 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 1 May 2026 13:52:06 -0500 Subject: [PATCH 09/29] ci: always use data checksums on both clusters Instead of detecting and matching checksums, recreate the old cluster with --data-checksums immediately after pg-start, and always pass --data-checksums when creating the new cluster. Both clusters consistently have checksums; no detection logic needed. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index da3e7a0..7d7e0dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,6 +44,12 @@ jobs: steps: - name: Start PostgreSQL ${{ matrix.old_pg }} run: pg-start ${{ matrix.old_pg }} + - name: Recreate old cluster with data checksums enabled + run: | + pg_ctlcluster ${{ matrix.old_pg }} test stop + pg_dropcluster ${{ matrix.old_pg }} test + pg_createcluster ${{ matrix.old_pg }} test -- --data-checksums + pg_ctlcluster ${{ matrix.old_pg }} test start - name: Check out the repo uses: actions/checkout@v4 - name: Install rsync @@ -63,20 +69,7 @@ jobs: - name: Stop old cluster, binary pg_upgrade to PostgreSQL ${{ matrix.new_pg }}, start new cluster run: | pg_ctlcluster ${{ matrix.old_pg }} test stop - checksum_initdb_flag="" - if /usr/lib/postgresql/${{ matrix.old_pg }}/bin/pg_controldata \ - /var/lib/postgresql/${{ matrix.old_pg }}/test \ - | grep -q "Data page checksum version: [^0]"; then - checksum_initdb_flag="--data-checksums" - elif /usr/lib/postgresql/${{ matrix.new_pg }}/bin/initdb --help 2>&1 \ - | grep -q -- "--no-data-checksums"; then - checksum_initdb_flag="--no-data-checksums" - fi - if [ -n "$checksum_initdb_flag" ]; then - pg_createcluster ${{ matrix.new_pg }} test -- $checksum_initdb_flag - else - pg_createcluster ${{ matrix.new_pg }} test - fi + pg_createcluster ${{ matrix.new_pg }} test -- --data-checksums sed -i "s/^port = .*/port = 5432/" /etc/postgresql/${{ matrix.new_pg }}/test/postgresql.conf mkdir -p /tmp/pg_upgrade_logs chown postgres:postgres /tmp/pg_upgrade_logs From 1940fc7a72f9edae892d2a93767e6dd2c48f06b2 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 1 May 2026 13:55:21 -0500 Subject: [PATCH 10/29] ci: wait for cluster ready after recreate; drop standard test job - Add pg_isready -t 30 after pg_ctlcluster start to wait for the socket to be available before proceeding - Remove the standard 'test' job (not needed on this branch) Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7d7e0dc..2b0ac07 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,23 +5,6 @@ on: - master pull_request: jobs: - test: - strategy: - matrix: - pg: [18, 17, 16, 15, 14, 13, 12, 11, 10] - name: 🐘 PostgreSQL ${{ matrix.pg }} - runs-on: ubuntu-latest - container: pgxn/pgxn-tools - steps: - - name: Start PostgreSQL ${{ matrix.pg }} - run: pg-start ${{ matrix.pg }} - - name: Check out the repo - uses: actions/checkout@v4 - - name: Install rsync - run: apt-get install -y rsync - - name: Test on PostgreSQL ${{ matrix.pg }} - run: make test PGUSER=postgres - pg-upgrade-test: strategy: matrix: @@ -48,8 +31,9 @@ jobs: run: | pg_ctlcluster ${{ matrix.old_pg }} test stop pg_dropcluster ${{ matrix.old_pg }} test - pg_createcluster ${{ matrix.old_pg }} test -- --data-checksums + pg_createcluster -p 5432 ${{ matrix.old_pg }} test -- --data-checksums pg_ctlcluster ${{ matrix.old_pg }} test start + pg_isready -U postgres -t 30 - name: Check out the repo uses: actions/checkout@v4 - name: Install rsync From 07b47693eaa02ba78e2f285c43dc73300c4d83ef Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 1 May 2026 13:57:52 -0500 Subject: [PATCH 11/29] ci: use trust auth on recreated old cluster pg_createcluster defaults to peer/md5 auth; the container runs as root so peer auth fails for user postgres. Pass --auth trust to match what pg-start configures. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b0ac07..273c134 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: run: | pg_ctlcluster ${{ matrix.old_pg }} test stop pg_dropcluster ${{ matrix.old_pg }} test - pg_createcluster -p 5432 ${{ matrix.old_pg }} test -- --data-checksums + pg_createcluster -p 5432 ${{ matrix.old_pg }} test -- --data-checksums --auth trust pg_ctlcluster ${{ matrix.old_pg }} test start pg_isready -U postgres -t 30 - name: Check out the repo From 1bcc208f4afcb917e67f7e475d1b481685642e28 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 1 May 2026 14:01:02 -0500 Subject: [PATCH 12/29] Also omit relhaspkey from pg_class_v (removed in PG11) Like relhasoids (removed in PG12), relhaspkey was removed in PG11. Without excluding it, binary pg_upgrade from PG10 to PG11+ fails with "column c.relhaspkey does not exist". Co-Authored-By: Claude Sonnet 4.6 --- sql/cat_tools--0.2.0--0.2.2.sql.in | 2 +- sql/cat_tools--0.2.1--0.2.2.sql.in | 2 +- sql/cat_tools--0.2.2.sql.in | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sql/cat_tools--0.2.0--0.2.2.sql.in b/sql/cat_tools--0.2.0--0.2.2.sql.in index 4b81e8d..592ecdb 100644 --- a/sql/cat_tools--0.2.0--0.2.2.sql.in +++ b/sql/cat_tools--0.2.0--0.2.2.sql.in @@ -498,7 +498,7 @@ CREATE OR REPLACE VIEW _cat_tools.pg_class_v AS LEFT JOIN pg_namespace n ON( n.oid = c.relnamespace ) ; $fmt$ - , __cat_tools.omit_column('pg_catalog.pg_class', array['oid', 'relhasoids']) + , __cat_tools.omit_column('pg_catalog.pg_class', array['oid', 'relhasoids', 'relhaspkey']) )); REVOKE ALL ON _cat_tools.pg_class_v FROM public; diff --git a/sql/cat_tools--0.2.1--0.2.2.sql.in b/sql/cat_tools--0.2.1--0.2.2.sql.in index 7a29130..5a44cc2 100644 --- a/sql/cat_tools--0.2.1--0.2.2.sql.in +++ b/sql/cat_tools--0.2.1--0.2.2.sql.in @@ -42,7 +42,7 @@ CREATE OR REPLACE VIEW _cat_tools.pg_class_v AS LEFT JOIN pg_namespace n ON( n.oid = c.relnamespace ) ; $fmt$ - , __cat_tools.omit_column('pg_catalog.pg_class', array['oid', 'relhasoids']) + , __cat_tools.omit_column('pg_catalog.pg_class', array['oid', 'relhasoids', 'relhaspkey']) )); REVOKE ALL ON _cat_tools.pg_class_v FROM public; diff --git a/sql/cat_tools--0.2.2.sql.in b/sql/cat_tools--0.2.2.sql.in index 05fa2e2..31cb823 100644 --- a/sql/cat_tools--0.2.2.sql.in +++ b/sql/cat_tools--0.2.2.sql.in @@ -67,7 +67,7 @@ CREATE OR REPLACE VIEW _cat_tools.pg_class_v AS LEFT JOIN pg_namespace n ON( n.oid = c.relnamespace ) ; $fmt$ - , __cat_tools.omit_column('pg_catalog.pg_class', array['oid', 'relhasoids']) + , __cat_tools.omit_column('pg_catalog.pg_class', array['oid', 'relhasoids', 'relhaspkey']) )); REVOKE ALL ON _cat_tools.pg_class_v FROM public; From dacea531d0562731ef204e8e2155da11493a8434 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 1 May 2026 14:01:10 -0500 Subject: [PATCH 13/29] ci: try pg_createcluster without explicit port Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 273c134..157392f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: run: | pg_ctlcluster ${{ matrix.old_pg }} test stop pg_dropcluster ${{ matrix.old_pg }} test - pg_createcluster -p 5432 ${{ matrix.old_pg }} test -- --data-checksums --auth trust + pg_createcluster ${{ matrix.old_pg }} test -- --data-checksums --auth trust pg_ctlcluster ${{ matrix.old_pg }} test start pg_isready -U postgres -t 30 - name: Check out the repo From 3908501037a450cc6ab1b0a7eb929993ebde9189 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 1 May 2026 14:02:49 -0500 Subject: [PATCH 14/29] ci: restore -p 5432 on pg_createcluster with explanation Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 157392f..e1af86a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,10 @@ jobs: run: | pg_ctlcluster ${{ matrix.old_pg }} test stop pg_dropcluster ${{ matrix.old_pg }} test - pg_createcluster ${{ matrix.old_pg }} test -- --data-checksums --auth trust + # -p 5432: pg_createcluster assigns the next available port, which + # may not be 5432 after pg-start has claimed and released it. Force + # 5432 so subsequent psql/createdb calls connect without -p. + pg_createcluster -p 5432 ${{ matrix.old_pg }} test -- --data-checksums --auth trust pg_ctlcluster ${{ matrix.old_pg }} test start pg_isready -U postgres -t 30 - name: Check out the repo From 53826b763cdac1a75ceb4c930ccca618f72925d7 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 1 May 2026 14:04:30 -0500 Subject: [PATCH 15/29] ci: add --auth trust to new cluster creation Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e1af86a..6bbb19b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,7 +56,7 @@ jobs: - name: Stop old cluster, binary pg_upgrade to PostgreSQL ${{ matrix.new_pg }}, start new cluster run: | pg_ctlcluster ${{ matrix.old_pg }} test stop - pg_createcluster ${{ matrix.new_pg }} test -- --data-checksums + pg_createcluster ${{ matrix.new_pg }} test -- --data-checksums --auth trust sed -i "s/^port = .*/port = 5432/" /etc/postgresql/${{ matrix.new_pg }}/test/postgresql.conf mkdir -p /tmp/pg_upgrade_logs chown postgres:postgres /tmp/pg_upgrade_logs From d3b2e6e6ea108ad403e5540548353404f338d21f Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 1 May 2026 14:09:50 -0500 Subject: [PATCH 16/29] ci: DRY up repeated options via job-level env vars; fix PG17+ log path - Set PGUSER=postgres at job level, eliminating -U postgres / PGUSER=postgres throughout all steps - Set INITDB_OPTS=--data-checksums --auth trust at job level so both pg_createcluster calls stay in sync - Fix log capture on failure: PG17+ writes pg_upgrade logs to $new_datadir/pg_upgrade_output.d/ instead of CWD; search both paths Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 51 ++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6bbb19b..80acdad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,11 @@ jobs: name: 🔄 Binary pg_upgrade ${{ matrix.old_pg }} → ${{ matrix.new_pg }} runs-on: ubuntu-latest container: pgxn/pgxn-tools + env: + PGUSER: postgres + # Both clusters must use the same initdb options so pg_upgrade sees + # consistent settings (checksums, auth) on old and new clusters. + INITDB_OPTS: --data-checksums --auth trust steps: - name: Start PostgreSQL ${{ matrix.old_pg }} run: pg-start ${{ matrix.old_pg }} @@ -34,30 +39,32 @@ jobs: # -p 5432: pg_createcluster assigns the next available port, which # may not be 5432 after pg-start has claimed and released it. Force # 5432 so subsequent psql/createdb calls connect without -p. - pg_createcluster -p 5432 ${{ matrix.old_pg }} test -- --data-checksums --auth trust + pg_createcluster -p 5432 ${{ matrix.old_pg }} test -- $INITDB_OPTS pg_ctlcluster ${{ matrix.old_pg }} test start - pg_isready -U postgres -t 30 + pg_isready -t 30 - name: Check out the repo uses: actions/checkout@v4 - name: Install rsync run: apt-get install -y rsync - name: Install cat_tools into old cluster - run: make install PGUSER=postgres + run: make install - name: Test cat_tools on old cluster - run: make test PGUSER=postgres + run: make test - name: Prepare upgrade test database run: | - createdb -U postgres cat_tools_upgrade_test - psql -U postgres -d cat_tools_upgrade_test -c "CREATE EXTENSION cat_tools" + createdb cat_tools_upgrade_test + psql -d cat_tools_upgrade_test -c "CREATE EXTENSION cat_tools" - name: Install PostgreSQL ${{ matrix.new_pg }} run: apt-get install -y postgresql-${{ matrix.new_pg }} postgresql-server-dev-${{ matrix.new_pg }} - name: Install cat_tools into new cluster - run: make install PGUSER=postgres PG_CONFIG=/usr/lib/postgresql/${{ matrix.new_pg }}/bin/pg_config + run: make install PG_CONFIG=/usr/lib/postgresql/${{ matrix.new_pg }}/bin/pg_config - name: Stop old cluster, binary pg_upgrade to PostgreSQL ${{ matrix.new_pg }}, start new cluster run: | pg_ctlcluster ${{ matrix.old_pg }} test stop - pg_createcluster ${{ matrix.new_pg }} test -- --data-checksums --auth trust + pg_createcluster ${{ matrix.new_pg }} test -- $INITDB_OPTS sed -i "s/^port = .*/port = 5432/" /etc/postgresql/${{ matrix.new_pg }}/test/postgresql.conf + # PG17+ writes logs to $new_datadir/pg_upgrade_output.d/; older + # versions write to CWD. Search both on failure. mkdir -p /tmp/pg_upgrade_logs chown postgres:postgres /tmp/pg_upgrade_logs su -c "cd /tmp/pg_upgrade_logs && /usr/lib/postgresql/${{ matrix.new_pg }}/bin/pg_upgrade \ @@ -67,15 +74,17 @@ jobs: -D /var/lib/postgresql/${{ matrix.new_pg }}/test \ -o '-c config_file=/etc/postgresql/${{ matrix.old_pg }}/test/postgresql.conf' \ -O '-c config_file=/etc/postgresql/${{ matrix.new_pg }}/test/postgresql.conf'" postgres \ - || { find /tmp/pg_upgrade_logs -name '*.log' | sort | xargs -r tail -n +1; exit 1; } + || { find /tmp/pg_upgrade_logs \ + /var/lib/postgresql/${{ matrix.new_pg }}/test/pg_upgrade_output.d \ + -name '*.log' 2>/dev/null | sort | xargs -r tail -n +1; exit 1; } pg_ctlcluster ${{ matrix.new_pg }} test start - name: Verify extension version after upgrade run: | - VERSION=$(psql -U postgres -d cat_tools_upgrade_test -tAc "SELECT extversion FROM pg_extension WHERE extname = 'cat_tools'") + VERSION=$(psql -d cat_tools_upgrade_test -tAc "SELECT extversion FROM pg_extension WHERE extname = 'cat_tools'") echo "Extension version: ${VERSION:-}" echo "$VERSION" | grep -q "0.2.2" - name: Run test suite on new cluster - run: make test PGUSER=postgres + run: make test extension-update-test: strategy: @@ -94,6 +103,8 @@ jobs: name: ⬆️ Extension update test on PostgreSQL ${{ matrix.pg }} runs-on: ubuntu-latest container: pgxn/pgxn-tools + env: + PGUSER: postgres steps: - name: Start PostgreSQL ${{ matrix.pg }} run: pg-start ${{ matrix.pg }} @@ -102,21 +113,21 @@ jobs: - name: Install rsync run: apt-get install -y rsync - name: Install cat_tools (all versions) - run: make install PGUSER=postgres + run: make install - name: Test upgrade from 0.2.0 (direct 0.2.0→0.2.2 path) run: | - psql -U postgres -c "CREATE EXTENSION cat_tools VERSION '0.2.0'" - psql -U postgres -c "ALTER EXTENSION cat_tools UPDATE" - VERSION=$(psql -U postgres -tAc "SELECT extversion FROM pg_extension WHERE extname = 'cat_tools'") + psql -c "CREATE EXTENSION cat_tools VERSION '0.2.0'" + psql -c "ALTER EXTENSION cat_tools UPDATE" + VERSION=$(psql -tAc "SELECT extversion FROM pg_extension WHERE extname = 'cat_tools'") echo "Version after 0.2.0 upgrade: ${VERSION:-}" echo "$VERSION" | grep -q "0.2.2" - name: Test upgrade from 0.2.1 (0.2.1→0.2.2 path) run: | - createdb -U postgres cat_tools_from_021 - psql -U postgres -d cat_tools_from_021 -c "CREATE EXTENSION cat_tools VERSION '0.2.1'" - psql -U postgres -d cat_tools_from_021 -c "ALTER EXTENSION cat_tools UPDATE" - VERSION=$(psql -U postgres -d cat_tools_from_021 -tAc "SELECT extversion FROM pg_extension WHERE extname = 'cat_tools'") + createdb cat_tools_from_021 + psql -d cat_tools_from_021 -c "CREATE EXTENSION cat_tools VERSION '0.2.1'" + psql -d cat_tools_from_021 -c "ALTER EXTENSION cat_tools UPDATE" + VERSION=$(psql -d cat_tools_from_021 -tAc "SELECT extversion FROM pg_extension WHERE extname = 'cat_tools'") echo "Version after 0.2.1 upgrade: ${VERSION:-}" echo "$VERSION" | grep -q "0.2.2" - name: Run test suite on updated extension - run: make test PGUSER=postgres + run: make test From 677f825c01f9b458fc1c2c28ad0e39714d67c62a Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 1 May 2026 14:12:30 -0500 Subject: [PATCH 17/29] Also omit attcacheoff from pg_attribute_v (removed in PG18) attcacheoff was an internal column in pg_attribute removed in PG18. Without excluding it, binary pg_upgrade to PG18 fails with "column a.attcacheoff does not exist". Co-Authored-By: Claude Sonnet 4.6 --- sql/cat_tools--0.2.0--0.2.2.sql.in | 2 +- sql/cat_tools--0.2.1--0.2.2.sql.in | 2 +- sql/cat_tools--0.2.2.sql.in | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sql/cat_tools--0.2.0--0.2.2.sql.in b/sql/cat_tools--0.2.0--0.2.2.sql.in index 592ecdb..c8b632c 100644 --- a/sql/cat_tools--0.2.0--0.2.2.sql.in +++ b/sql/cat_tools--0.2.0--0.2.2.sql.in @@ -531,7 +531,7 @@ $fmt$ * attmissingval is explicitly included above (cast to text[] via SED markers). * Omit it here so it doesn't appear twice, and omit oid to avoid conflicts on PG12+. */ - , __cat_tools.omit_column('pg_catalog.pg_attribute', array['oid', 'attmissingval']) + , __cat_tools.omit_column('pg_catalog.pg_attribute', array['oid', 'attmissingval', 'attcacheoff']) , __cat_tools.omit_column('pg_catalog.pg_type') )); REVOKE ALL ON _cat_tools.pg_attribute_v FROM public; diff --git a/sql/cat_tools--0.2.1--0.2.2.sql.in b/sql/cat_tools--0.2.1--0.2.2.sql.in index 5a44cc2..a1cdbe9 100644 --- a/sql/cat_tools--0.2.1--0.2.2.sql.in +++ b/sql/cat_tools--0.2.1--0.2.2.sql.in @@ -75,7 +75,7 @@ $fmt$ * attmissingval is explicitly included above (cast to text[] via SED markers). * Omit it here so it doesn't appear twice, and omit oid to avoid conflicts on PG12+. */ - , __cat_tools.omit_column('pg_catalog.pg_attribute', array['oid', 'attmissingval']) + , __cat_tools.omit_column('pg_catalog.pg_attribute', array['oid', 'attmissingval', 'attcacheoff']) , __cat_tools.omit_column('pg_catalog.pg_type') )); REVOKE ALL ON _cat_tools.pg_attribute_v FROM public; diff --git a/sql/cat_tools--0.2.2.sql.in b/sql/cat_tools--0.2.2.sql.in index 31cb823..6cdbdca 100644 --- a/sql/cat_tools--0.2.2.sql.in +++ b/sql/cat_tools--0.2.2.sql.in @@ -766,7 +766,7 @@ $fmt$ * attmissingval is explicitly included above (cast to text[] via SED markers). * Omit it here so it doesn't appear twice, and omit oid to avoid conflicts on PG12+. */ - , __cat_tools.omit_column('pg_catalog.pg_attribute', array['oid', 'attmissingval']) + , __cat_tools.omit_column('pg_catalog.pg_attribute', array['oid', 'attmissingval', 'attcacheoff']) , __cat_tools.omit_column('pg_catalog.pg_type') )); REVOKE ALL ON _cat_tools.pg_attribute_v FROM public; From 57256aaf7057c600c2221109546c731f27c1512d Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 1 May 2026 14:15:45 -0500 Subject: [PATCH 18/29] ci: restore standard per-version test job Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 80acdad..73c6558 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,25 @@ on: - master pull_request: jobs: + test: + strategy: + matrix: + pg: [18, 17, 16, 15, 14, 13, 12, 11, 10] + name: 🐘 PostgreSQL ${{ matrix.pg }} + runs-on: ubuntu-latest + container: pgxn/pgxn-tools + env: + PGUSER: postgres + steps: + - name: Start PostgreSQL ${{ matrix.pg }} + run: pg-start ${{ matrix.pg }} + - name: Check out the repo + uses: actions/checkout@v4 + - name: Install rsync + run: apt-get install -y rsync + - name: Test on PostgreSQL ${{ matrix.pg }} + run: make test + pg-upgrade-test: strategy: matrix: From 2f69473fd1b65b68c44e0768ca999ae591356dc5 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 1 May 2026 14:23:07 -0500 Subject: [PATCH 19/29] ci: move PGUSER to workflow level; use env vars for PGDATABASE and PG_CONFIG - PGUSER: postgres moved to top-level env (applies to all jobs) - PGDATABASE set at step level where a specific db is needed - PG_CONFIG set at step level for new cluster install (make inherits env) Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 73c6558..63eef4b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,8 @@ on: branches: - master pull_request: +env: + PGUSER: postgres jobs: test: strategy: @@ -12,8 +14,6 @@ jobs: name: 🐘 PostgreSQL ${{ matrix.pg }} runs-on: ubuntu-latest container: pgxn/pgxn-tools - env: - PGUSER: postgres steps: - name: Start PostgreSQL ${{ matrix.pg }} run: pg-start ${{ matrix.pg }} @@ -44,7 +44,6 @@ jobs: runs-on: ubuntu-latest container: pgxn/pgxn-tools env: - PGUSER: postgres # Both clusters must use the same initdb options so pg_upgrade sees # consistent settings (checksums, auth) on old and new clusters. INITDB_OPTS: --data-checksums --auth trust @@ -70,13 +69,17 @@ jobs: - name: Test cat_tools on old cluster run: make test - name: Prepare upgrade test database + env: + PGDATABASE: cat_tools_upgrade_test run: | - createdb cat_tools_upgrade_test - psql -d cat_tools_upgrade_test -c "CREATE EXTENSION cat_tools" + createdb "$PGDATABASE" + psql -c "CREATE EXTENSION cat_tools" - name: Install PostgreSQL ${{ matrix.new_pg }} run: apt-get install -y postgresql-${{ matrix.new_pg }} postgresql-server-dev-${{ matrix.new_pg }} - name: Install cat_tools into new cluster - run: make install PG_CONFIG=/usr/lib/postgresql/${{ matrix.new_pg }}/bin/pg_config + env: + PG_CONFIG: /usr/lib/postgresql/${{ matrix.new_pg }}/bin/pg_config + run: make install - name: Stop old cluster, binary pg_upgrade to PostgreSQL ${{ matrix.new_pg }}, start new cluster run: | pg_ctlcluster ${{ matrix.old_pg }} test stop @@ -98,8 +101,10 @@ jobs: -name '*.log' 2>/dev/null | sort | xargs -r tail -n +1; exit 1; } pg_ctlcluster ${{ matrix.new_pg }} test start - name: Verify extension version after upgrade + env: + PGDATABASE: cat_tools_upgrade_test run: | - VERSION=$(psql -d cat_tools_upgrade_test -tAc "SELECT extversion FROM pg_extension WHERE extname = 'cat_tools'") + VERSION=$(psql -tAc "SELECT extversion FROM pg_extension WHERE extname = 'cat_tools'") echo "Extension version: ${VERSION:-}" echo "$VERSION" | grep -q "0.2.2" - name: Run test suite on new cluster @@ -122,8 +127,6 @@ jobs: name: ⬆️ Extension update test on PostgreSQL ${{ matrix.pg }} runs-on: ubuntu-latest container: pgxn/pgxn-tools - env: - PGUSER: postgres steps: - name: Start PostgreSQL ${{ matrix.pg }} run: pg-start ${{ matrix.pg }} @@ -141,11 +144,13 @@ jobs: echo "Version after 0.2.0 upgrade: ${VERSION:-}" echo "$VERSION" | grep -q "0.2.2" - name: Test upgrade from 0.2.1 (0.2.1→0.2.2 path) + env: + PGDATABASE: cat_tools_from_021 run: | - createdb cat_tools_from_021 - psql -d cat_tools_from_021 -c "CREATE EXTENSION cat_tools VERSION '0.2.1'" - psql -d cat_tools_from_021 -c "ALTER EXTENSION cat_tools UPDATE" - VERSION=$(psql -d cat_tools_from_021 -tAc "SELECT extversion FROM pg_extension WHERE extname = 'cat_tools'") + createdb "$PGDATABASE" + psql -c "CREATE EXTENSION cat_tools VERSION '0.2.1'" + psql -c "ALTER EXTENSION cat_tools UPDATE" + VERSION=$(psql -tAc "SELECT extversion FROM pg_extension WHERE extname = 'cat_tools'") echo "Version after 0.2.1 upgrade: ${VERSION:-}" echo "$VERSION" | grep -q "0.2.2" - name: Run test suite on updated extension From d9cd41aef11e399edd5ac45eb9550be05e656724 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 1 May 2026 14:35:59 -0500 Subject: [PATCH 20/29] HISTORY: document pg_upgrade compatibility fixes as re-release of 0.2.2 README: warn that catalog-exposing objects are not a stable API Co-Authored-By: Claude Sonnet 4.6 --- HISTORY.asc | 26 ++++++++++++++++++++++++++ README.asc | 10 ++++++++++ 2 files changed, 36 insertions(+) diff --git a/HISTORY.asc b/HISTORY.asc index 8740ed8..485c0e9 100644 --- a/HISTORY.asc +++ b/HISTORY.asc @@ -3,6 +3,10 @@ Compatibility release: fixes broken installs on PostgreSQL 11 and 12+, and provides an upgrade path from 0.2.0 and 0.2.1. +NOTE: This entry covers two releases distributed under the same version number. +The re-release adds `pg_upgrade` compatibility fixes omitted from the original +release (see "`pg_upgrade` Compatibility" below). + ### PostgreSQL Version Support cat_tools 0.2.1 (and earlier) only installs correctly on **PostgreSQL 9.2 – 10**. @@ -35,6 +39,24 @@ afterward.** The upgrade will fail with an error if any such dependent objects exist — this is intentional, to avoid silently breaking user-defined objects. After dropping your dependent objects, run `ALTER EXTENSION cat_tools UPDATE` again. +### `pg_upgrade` Compatibility (Re-release) + +`pg_upgrade` physically copies data files and re-applies schema definitions on +the new cluster. Any view that references a catalog column removed in the new +PostgreSQL version will cause the upgrade to fail. The initial 0.2.2 release +omitted `oid` and `attmissingval` from the catalog views, but missed several +columns that were later removed from PostgreSQL: + +* `relhasoids` was removed from `pg_catalog.pg_class` in PG12. +* `relhaspkey` was removed from `pg_catalog.pg_class` in PG17. +* `attcacheoff` was removed from `pg_catalog.pg_attribute` in PG17. + +Without this fix, running `pg_upgrade` across any of these version boundaries +with cat_tools installed would fail. + +**You must have the re-release of 0.2.2 installed before running `pg_upgrade` +to PostgreSQL 12 or later.** + ### Changes * `sql/cat_tools--0.1.4--0.1.5.sql` was empty; added `-- empty upgrade` placeholder so @@ -48,6 +70,10 @@ After dropping your dependent objects, run `ALTER EXTENSION cat_tools UPDATE` ag which also applies all 0.2.1 function additions in a single step. * `GRANT SELECT ON cat_tools.pg_extension_v TO cat_tools__usage` is now applied on the upgrade path from 0.2.0 (it was absent in 0.2.0 and only added via the 0.2.1 upgrade). +* (Re-release) `_cat_tools.pg_class_v` now explicitly omits `relhasoids` (removed in PG12) + and `relhaspkey` (removed in PG17) to prevent `pg_upgrade` failures. +* (Re-release) `_cat_tools.pg_attribute_v` now explicitly omits `attcacheoff` (removed in + PG17) to prevent `pg_upgrade` failures. 0.2.1 ----- diff --git a/README.asc b/README.asc index 9fa8761..cbd36d8 100644 --- a/README.asc +++ b/README.asc @@ -4,6 +4,16 @@ tables/views/functions. They are meant for use by code, not by people. To make use of them, you need to grant `cat_tools__usage` to any roles that need access. +[WARNING] +==== +Any function or view in this extension that exposes raw PostgreSQL catalog +information does *not* provide a stable API. The PostgreSQL system catalogs +change between major versions — columns are added, removed, and change type. +If your code depends on the specific columns returned by objects such as +`cat_tools.pg_class_v`, `cat_tools.column`, or `cat_tools.pg_attribute_v`, +it may break when you upgrade PostgreSQL. +==== + == Current Status image:https://badge.fury.io/pg/cat_tools.svg["PGXN version",link="https://badge.fury.io/pg/cat_tools"] From 65e6e00e64099757284b79247854df6c79c869c8 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 1 May 2026 14:36:19 -0500 Subject: [PATCH 21/29] HISTORY: clarify 0.2.2 re-release wording Co-Authored-By: Claude Sonnet 4.6 --- HISTORY.asc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/HISTORY.asc b/HISTORY.asc index 485c0e9..0d08644 100644 --- a/HISTORY.asc +++ b/HISTORY.asc @@ -3,9 +3,9 @@ Compatibility release: fixes broken installs on PostgreSQL 11 and 12+, and provides an upgrade path from 0.2.0 and 0.2.1. -NOTE: This entry covers two releases distributed under the same version number. -The re-release adds `pg_upgrade` compatibility fixes omitted from the original -release (see "`pg_upgrade` Compatibility" below). +NOTE: This is a re-release of 0.2.2 (originally released 2026-04-27), adding +`pg_upgrade` compatibility fixes that were omitted from the original release +(see "`pg_upgrade` Compatibility" below). ### PostgreSQL Version Support From 43f354468eba13fbba68e79e4418d2f14ff58242 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 1 May 2026 14:39:53 -0500 Subject: [PATCH 22/29] ci: move PG_CONFIG to job level; inline PGDATABASE in run scripts Remove step-level env: blocks to avoid future duplication. PG_CONFIG is now at pg-upgrade-test job level alongside INITDB_OPTS. PGDATABASE values are inlined with -d flags since they differ between steps and a job-level value would affect make test calls. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 63eef4b..f12af6e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,6 +47,7 @@ jobs: # Both clusters must use the same initdb options so pg_upgrade sees # consistent settings (checksums, auth) on old and new clusters. INITDB_OPTS: --data-checksums --auth trust + PG_CONFIG: /usr/lib/postgresql/${{ matrix.new_pg }}/bin/pg_config steps: - name: Start PostgreSQL ${{ matrix.old_pg }} run: pg-start ${{ matrix.old_pg }} @@ -69,16 +70,12 @@ jobs: - name: Test cat_tools on old cluster run: make test - name: Prepare upgrade test database - env: - PGDATABASE: cat_tools_upgrade_test run: | - createdb "$PGDATABASE" - psql -c "CREATE EXTENSION cat_tools" + createdb cat_tools_upgrade_test + psql -d cat_tools_upgrade_test -c "CREATE EXTENSION cat_tools" - name: Install PostgreSQL ${{ matrix.new_pg }} run: apt-get install -y postgresql-${{ matrix.new_pg }} postgresql-server-dev-${{ matrix.new_pg }} - name: Install cat_tools into new cluster - env: - PG_CONFIG: /usr/lib/postgresql/${{ matrix.new_pg }}/bin/pg_config run: make install - name: Stop old cluster, binary pg_upgrade to PostgreSQL ${{ matrix.new_pg }}, start new cluster run: | @@ -101,10 +98,8 @@ jobs: -name '*.log' 2>/dev/null | sort | xargs -r tail -n +1; exit 1; } pg_ctlcluster ${{ matrix.new_pg }} test start - name: Verify extension version after upgrade - env: - PGDATABASE: cat_tools_upgrade_test run: | - VERSION=$(psql -tAc "SELECT extversion FROM pg_extension WHERE extname = 'cat_tools'") + VERSION=$(psql -d cat_tools_upgrade_test -tAc "SELECT extversion FROM pg_extension WHERE extname = 'cat_tools'") echo "Extension version: ${VERSION:-}" echo "$VERSION" | grep -q "0.2.2" - name: Run test suite on new cluster @@ -144,13 +139,11 @@ jobs: echo "Version after 0.2.0 upgrade: ${VERSION:-}" echo "$VERSION" | grep -q "0.2.2" - name: Test upgrade from 0.2.1 (0.2.1→0.2.2 path) - env: - PGDATABASE: cat_tools_from_021 run: | - createdb "$PGDATABASE" - psql -c "CREATE EXTENSION cat_tools VERSION '0.2.1'" - psql -c "ALTER EXTENSION cat_tools UPDATE" - VERSION=$(psql -tAc "SELECT extversion FROM pg_extension WHERE extname = 'cat_tools'") + createdb cat_tools_from_021 + psql -d cat_tools_from_021 -c "CREATE EXTENSION cat_tools VERSION '0.2.1'" + psql -d cat_tools_from_021 -c "ALTER EXTENSION cat_tools UPDATE" + VERSION=$(psql -d cat_tools_from_021 -tAc "SELECT extversion FROM pg_extension WHERE extname = 'cat_tools'") echo "Version after 0.2.1 upgrade: ${VERSION:-}" echo "$VERSION" | grep -q "0.2.2" - name: Run test suite on updated extension From de17aac48db27d253c2182fbfd8246d8eec47bb4 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 1 May 2026 14:43:33 -0500 Subject: [PATCH 23/29] ci: use -p 5432 on new cluster creation; drop make test from upgrade job HISTORY: remove re-release note (now in PR description only) Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 7 +------ HISTORY.asc | 3 --- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f12af6e..a05e79c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,8 +67,6 @@ jobs: run: apt-get install -y rsync - name: Install cat_tools into old cluster run: make install - - name: Test cat_tools on old cluster - run: make test - name: Prepare upgrade test database run: | createdb cat_tools_upgrade_test @@ -80,8 +78,7 @@ jobs: - name: Stop old cluster, binary pg_upgrade to PostgreSQL ${{ matrix.new_pg }}, start new cluster run: | pg_ctlcluster ${{ matrix.old_pg }} test stop - pg_createcluster ${{ matrix.new_pg }} test -- $INITDB_OPTS - sed -i "s/^port = .*/port = 5432/" /etc/postgresql/${{ matrix.new_pg }}/test/postgresql.conf + pg_createcluster -p 5432 ${{ matrix.new_pg }} test -- $INITDB_OPTS # PG17+ writes logs to $new_datadir/pg_upgrade_output.d/; older # versions write to CWD. Search both on failure. mkdir -p /tmp/pg_upgrade_logs @@ -102,8 +99,6 @@ jobs: VERSION=$(psql -d cat_tools_upgrade_test -tAc "SELECT extversion FROM pg_extension WHERE extname = 'cat_tools'") echo "Extension version: ${VERSION:-}" echo "$VERSION" | grep -q "0.2.2" - - name: Run test suite on new cluster - run: make test extension-update-test: strategy: diff --git a/HISTORY.asc b/HISTORY.asc index 0d08644..b25e350 100644 --- a/HISTORY.asc +++ b/HISTORY.asc @@ -3,9 +3,6 @@ Compatibility release: fixes broken installs on PostgreSQL 11 and 12+, and provides an upgrade path from 0.2.0 and 0.2.1. -NOTE: This is a re-release of 0.2.2 (originally released 2026-04-27), adding -`pg_upgrade` compatibility fixes that were omitted from the original release -(see "`pg_upgrade` Compatibility" below). ### PostgreSQL Version Support From b69fb4dfe25f8774dc6b788871a85b28757b0310 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 1 May 2026 14:50:52 -0500 Subject: [PATCH 24/29] ci: restore make test on upgraded cluster; add TODO for extension update test Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a05e79c..d28cbef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -99,6 +99,12 @@ jobs: VERSION=$(psql -d cat_tools_upgrade_test -tAc "SELECT extversion FROM pg_extension WHERE extname = 'cat_tools'") echo "Extension version: ${VERSION:-}" echo "$VERSION" | grep -q "0.2.2" + - name: Run test suite on upgraded cluster + run: make test + # TODO: also test ALTER EXTENSION cat_tools UPDATE here, once the + # pg_upgrade source versions (e.g. 0.2.0) can install on the new_pg + # version. Currently the pre-0.2.2 install scripts fail on PG11+ so + # we cannot pg_upgrade from a cluster that has them installed. extension-update-test: strategy: From fa6d92ba4ccef97074b8a5722eed652c6658d706 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 1 May 2026 14:52:16 -0500 Subject: [PATCH 25/29] ci: use default database in pg-upgrade-test instead of a separate db Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d28cbef..638acde 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,10 +67,8 @@ jobs: run: apt-get install -y rsync - name: Install cat_tools into old cluster run: make install - - name: Prepare upgrade test database - run: | - createdb cat_tools_upgrade_test - psql -d cat_tools_upgrade_test -c "CREATE EXTENSION cat_tools" + - name: Install cat_tools extension into upgrade test database + run: psql -c "CREATE EXTENSION cat_tools" - name: Install PostgreSQL ${{ matrix.new_pg }} run: apt-get install -y postgresql-${{ matrix.new_pg }} postgresql-server-dev-${{ matrix.new_pg }} - name: Install cat_tools into new cluster @@ -96,7 +94,7 @@ jobs: pg_ctlcluster ${{ matrix.new_pg }} test start - name: Verify extension version after upgrade run: | - VERSION=$(psql -d cat_tools_upgrade_test -tAc "SELECT extversion FROM pg_extension WHERE extname = 'cat_tools'") + VERSION=$(psql -tAc "SELECT extversion FROM pg_extension WHERE extname = 'cat_tools'") echo "Extension version: ${VERSION:-}" echo "$VERSION" | grep -q "0.2.2" - name: Run test suite on upgraded cluster From 443e42c19913eb222bc5e6edc0b062b2e1487964 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 1 May 2026 14:59:44 -0500 Subject: [PATCH 26/29] ci: fix PG_CONFIG - pass as make arg for new cluster only, not job-level env Job-level PG_CONFIG broke the old cluster install since the new PG isn't installed yet at that point. Pass it inline as a make argument for the new cluster step instead. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 638acde..959f78f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,7 +47,6 @@ jobs: # Both clusters must use the same initdb options so pg_upgrade sees # consistent settings (checksums, auth) on old and new clusters. INITDB_OPTS: --data-checksums --auth trust - PG_CONFIG: /usr/lib/postgresql/${{ matrix.new_pg }}/bin/pg_config steps: - name: Start PostgreSQL ${{ matrix.old_pg }} run: pg-start ${{ matrix.old_pg }} @@ -72,7 +71,7 @@ jobs: - name: Install PostgreSQL ${{ matrix.new_pg }} run: apt-get install -y postgresql-${{ matrix.new_pg }} postgresql-server-dev-${{ matrix.new_pg }} - name: Install cat_tools into new cluster - run: make install + run: make install PG_CONFIG=/usr/lib/postgresql/${{ matrix.new_pg }}/bin/pg_config - name: Stop old cluster, binary pg_upgrade to PostgreSQL ${{ matrix.new_pg }}, start new cluster run: | pg_ctlcluster ${{ matrix.old_pg }} test stop From f91848096739e59c3ab28053850664a15c3a7f61 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 1 May 2026 15:02:40 -0500 Subject: [PATCH 27/29] ci: add comment explaining why PG_CONFIG must be explicit for new cluster Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 959f78f..cfc103e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,6 +71,9 @@ jobs: - name: Install PostgreSQL ${{ matrix.new_pg }} run: apt-get install -y postgresql-${{ matrix.new_pg }} postgresql-server-dev-${{ matrix.new_pg }} - name: Install cat_tools into new cluster + # PG_CONFIG must be specified explicitly: at this point both old and new + # PostgreSQL are installed, and the default pg_config on PATH may not be + # the new version's. run: make install PG_CONFIG=/usr/lib/postgresql/${{ matrix.new_pg }}/bin/pg_config - name: Stop old cluster, binary pg_upgrade to PostgreSQL ${{ matrix.new_pg }}, start new cluster run: | From db14cafbc5dcb634a47542187bb65b4bd86de376 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 1 May 2026 15:04:55 -0500 Subject: [PATCH 28/29] CLAUDE.md: add CI monitoring and bug-fix comment guidelines Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index f8f07bd..44f3caa 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,5 +1,13 @@ # Claude Code Instructions for cat_tools +## GitHub CI + +After pushing to a branch with an open PR, monitor CI using `gh pr checks --watch` in a background subagent until all jobs pass or a failure is confirmed. Investigate and fix failures immediately rather than leaving them for the user to notice. + +## Bug Fixes + +When fixing a bug, add a comment at the fix site explaining what the bug was and why the fix works. The goal is to prevent re-introducing the bug later. + ## Git **Never delete a branch without explicit user approval.** This includes `git push origin --delete`, `git branch -d`, and `git branch -D`. Always ask first. From 4af408e540212f9f2554e77bc970a2e8f2f9b1b5 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 1 May 2026 15:12:03 -0500 Subject: [PATCH 29/29] ci: skip on docs-only changes; add all-checks-passed summary job paths-ignore skips CI entirely when only *.md or *.asc files change. all-checks-passed is a single stable check name suitable for use as a required status check in branch protection rules. It fails if any job failed or was cancelled, passes if all passed or were skipped (e.g. on a docs-only push). It also self-inspects ci.yml to catch any jobs accidentally omitted from its needs list. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cfc103e..ba85fd6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,13 @@ on: push: branches: - master + paths-ignore: + - '**.md' + - '**.asc' pull_request: + paths-ignore: + - '**.md' + - '**.asc' env: PGUSER: postgres jobs: @@ -149,3 +155,35 @@ jobs: echo "$VERSION" | grep -q "0.2.2" - name: Run test suite on updated extension run: make test + + all-checks-passed: + needs: [test, pg-upgrade-test, extension-update-test] + if: always() + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Verify all jobs are listed in needs + # Ensures this job won't silently ignore a newly-added job that was + # omitted from the needs list above. + run: | + DEFINED=$(python3 -c " + import yaml + with open('.github/workflows/ci.yml') as f: + w = yaml.safe_load(f) + print('\n'.join(sorted(j for j in w['jobs'] if j != 'all-checks-passed'))) + ") + NEEDED=$(echo '${{ toJson(needs) }}' | python3 -c " + import json, sys + print('\n'.join(sorted(json.load(sys.stdin)))) + ") + if [ "$DEFINED" != "$NEEDED" ]; then + echo "Some jobs are missing from all-checks-passed needs:" + diff <(echo "$DEFINED") <(echo "$NEEDED") + exit 1 + fi + - name: Check all jobs passed or were skipped + run: | + if [[ "${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}" == "true" ]]; then + echo "One or more jobs failed or were cancelled" + exit 1 + fi