Skip to content
Open
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
86 changes: 77 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,81 @@ on:
push:
branches:
- master
paths-ignore:
- '**.md'
- '**.asc'
pull_request:
paths-ignore:
- '**.md'
- '**.asc'
env:
PGUSER: postgres
jobs:
# Cheap gate that lets the heavy jobs below skip themselves on commits
# that touch only docs. This must run on every push/pull_request (no
# paths-ignore on the workflow itself), otherwise the required
# all-checks-passed check would never report on doc-only pushes and get
# stuck Pending in branch protection.
changes:
name: 🔍 Detect docs-only changes
runs-on: ubuntu-latest
outputs:
docs_only: ${{ steps.diff.outputs.docs_only }}
steps:
- name: Check out the repo
uses: actions/checkout@v6
with:
# Full history needed so BASE and HEAD below are both reachable
# for `git diff`.
fetch-depth: 0
- name: Compute per-push changed files
id: diff
run: |
if [ "${{ github.event_name }}" = "pull_request" ] && \
[ "${{ github.event.action }}" = "synchronize" ] && \
[ -n "${{ github.event.before }}" ]; then
# A push to an already-open PR: before/after give the true
# per-push diff, same as for a branch push.
BASE="${{ github.event.before }}"
HEAD="${{ github.event.after }}"
elif [ "${{ github.event_name }}" = "pull_request" ]; then
# First run for this PR (opened/reopened/etc, or synchronize
# without a usable before): fall back to the whole base...head
# diff.
BASE="${{ github.event.pull_request.base.sha }}"
HEAD="${{ github.event.pull_request.head.sha }}"
else
BASE="${{ github.event.before }}"
HEAD="${{ github.event.after }}"
fi

echo "base=$BASE"
echo "head=$HEAD"

# Fail-safe: a missing HEAD, or an all-zeros BASE (e.g. a new
# branch's first push, where GitHub reports no prior commit),
# means we can't compute a real diff. Run the full matrix rather
# than risk skipping tests.
if [ -z "$HEAD" ] || [ -z "$BASE" ] || [[ "$BASE" =~ ^0+$ ]]; then
echo "docs_only=false" >> "$GITHUB_OUTPUT"
exit 0
fi

CHANGED=$(git diff --name-only "$BASE" "$HEAD" || echo __DIFF_FAILED__)

DOCS_ONLY=true
if [ "$CHANGED" = "__DIFF_FAILED__" ] || [ -z "$CHANGED" ]; then
DOCS_ONLY=false
else
while IFS= read -r f; do
if ! [[ "$f" =~ \.(md|asc)$ ]]; then
DOCS_ONLY=false
break
fi
done <<< "$CHANGED"
fi

echo "changed files:"
echo "$CHANGED"
echo "docs_only=$DOCS_ONLY" >> "$GITHUB_OUTPUT"

test:
needs: [changes]
if: needs.changes.outputs.docs_only != 'true'
strategy:
matrix:
pg: [18, 17, 16, 15, 14, 13, 12, 11, 10]
Expand All @@ -31,6 +95,8 @@ jobs:
run: make test

pg-upgrade-test:
needs: [changes]
if: needs.changes.outputs.docs_only != 'true'
strategy:
matrix:
include:
Expand Down Expand Up @@ -117,6 +183,8 @@ jobs:
# we cannot pg_upgrade from a cluster that has them installed.

extension-update-test:
needs: [changes]
if: needs.changes.outputs.docs_only != 'true'
strategy:
matrix:
# Restricted to PG10 only because the pre-0.2.2 install scripts use
Expand Down Expand Up @@ -173,10 +241,10 @@ jobs:
# protection rules. Matrix jobs produce check names like "🐘 PostgreSQL 14"
# which would all need to be listed individually and updated whenever the
# matrix changes. This job passes if all others passed or were skipped (e.g.
# on a docs-only push with paths-ignore), and fails if any failed or were
# cancelled.
# the heavy jobs gated off by the `changes` job on a docs-only push), and
# fails if any failed or were cancelled.
all-checks-passed:
needs: [test, pg-upgrade-test, extension-update-test]
needs: [changes, test, pg-upgrade-test, extension-update-test]
if: always()
runs-on: ubuntu-latest
steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/claude-code-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ jobs:
if: steps.gate.outputs.decision == 'run'
# Intentionally tracks the major-version tag (not a pinned SHA) so
# upstream fixes are picked up automatically.
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.sha }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/claude.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
- name: Checkout repository
# Intentionally tracks the major-version tag (not a pinned SHA) so
# upstream fixes are picked up automatically.
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 1
persist-credentials: false
Expand Down
17 changes: 13 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,19 @@ Rules for what to track in git:

0. If a `.sql.in` file exists, track the `.sql.in` and **not** the corresponding `.sql`.
1. If no `.sql.in` exists, track the `.sql` directly (e.g. historical pre-0.2.0 files).
2. Version-specific install scripts (e.g. `sql/cat_tools--0.2.2.sql.in`) MUST be tracked.
3. Update scripts (e.g. `sql/cat_tools--0.2.1--0.2.2.sql.in`) MUST be tracked.
4. The current version'''s install script (e.g. `sql/cat_tools--0.2.2.sql.in`) is generated
by `make` from `sql/cat_tools.sql.in`, but MUST still be tracked (rule 2 applies).
2. Version-specific install scripts (e.g. `sql/cat_tools--0.2.2.sql.in`) are tracked BY
DEFAULT. They enable update testing (install an old version, `ALTER EXTENSION UPDATE`,
verify) and, because a new MAJOR PostgreSQL version can unpredictably break installing
an OLDER extension version, keeping old versions committed lets CI catch when a version
stops installing on a newer PG.
See https://github.com/Postgres-Extensions/pgxntool/issues/51.
3. Update scripts (e.g. `sql/cat_tools--0.2.1--0.2.2.sql.in`) MUST be tracked — they are
essential to the update path and have no substitute.
4. EXCEPTION to rule 2: a minor version that doesn't significantly change the extension
(e.g. a small bug fix) is unlikely to cross a PG supported-version boundary, so its
generated install script adds little test-coverage value and MAY be omitted (regenerated
from `sql/cat_tools.sql.in` at build time). 0.2.3 is such a case — which is why
`sql/cat_tools--0.2.3.sql.in` is intentionally NOT tracked.
5. Version-specific files MUST NEVER be edited manually — always edit `sql/cat_tools.sql.in`
and regenerate.

Expand Down
8 changes: 8 additions & 0 deletions HISTORY.asc
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
0.2.3
-----
Fix backwards pg_class.relkind mapping (c/f/m) in relation__kind() and
relation__relkind(). Per pg_class.h the correct mapping is 'c' = composite
type, 'f' = foreign table, 'm' = materialized view; releases through 0.2.2 had
these three arms reversed.


0.2.2
-----
Compatibility release: fixes broken installs on PostgreSQL 11 and 12+, and
Expand Down
4 changes: 2 additions & 2 deletions META.in.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"name": "cat_tools",

"X_comment": "REQUIRED. Version of the distribution. http://pgxn.org/spec/#version",
"version": "0.2.2",
"version": "0.2.3",

"X_comment": "REQUIRED. Short description of distribution.",
"abstract": "Tools for interfacing with the Postgres catalog",
Expand All @@ -37,7 +37,7 @@
"file": "sql/cat_tools.sql",

"X_comment": "REQUIRED. Version the extension is at.",
"version": "0.2.2",
"version": "0.2.3",

"X_comment": "Optional: \"abstract\": Description of the extension.",
"abstract": "Tools for interfacing with the catalog",
Expand Down
4 changes: 2 additions & 2 deletions META.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"name": "cat_tools",

"X_comment": "REQUIRED. Version of the distribution. http://pgxn.org/spec/#version",
"version": "0.2.2",
"version": "0.2.3",

"X_comment": "REQUIRED. Short description of distribution.",
"abstract": "Tools for interfacing with the Postgres catalog",
Expand All @@ -37,7 +37,7 @@
"file": "sql/cat_tools.sql",

"X_comment": "REQUIRED. Version the extension is at.",
"version": "0.2.2",
"version": "0.2.3",

"X_comment": "Optional: \"abstract\": Description of the extension.",
"abstract": "Tools for interfacing with the catalog",
Expand Down
87 changes: 87 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Releasing cat_tools

cat_tools builds on pgxntool (https://github.com/Postgres-Extensions/pgxntool);
the release machinery (`make tag`, `make dist`) lives in `pgxntool/base.mk`. These
steps cut a new release.

## 1. Pre-release checks
- [ ] Open issues/PRs for this release reviewed, merged or deferred.
- [ ] CI green on all supported PostgreSQL versions (the `all-checks-passed` job on master).
- [ ] Locally: `make verify-results` passes. It depends on `test` (so it runs the suite
first, then gates on the results). `make test` alone is non-gating — pgxntool marks
`installcheck` `.IGNORE`, so it never returns non-zero on a regression; only
`verify-results` (which inspects `test/regression.diffs`) is a real gate.

## 2. Decide the version and what to track
- [ ] Pick the new version (semantic versioning).
- [ ] **Minor change? Consider NOT committing the generated versioned install script.**
If this release makes only fairly minor changes (unlikely to cross a PostgreSQL
supported-version boundary), decide whether to omit the generated
`sql/cat_tools--<version>.sql.in` — it has little update-test-coverage value and
omitting it keeps the repo smaller (it is regenerated from `sql/cat_tools.sql.in`
at build time). The update script (`sql/cat_tools--<prev>--<version>.sql.in`) is
ALWAYS committed. See CLAUDE.md "SQL file conventions".

## 3. Update version + changelog

> **⚠️ CRITICAL — you are temporarily leaving the `stable` pseudo-version.** Master's
> `default_version` normally sits at the `stable` pseudo-version so that source edits
> regenerate `cat_tools--stable.sql` and never a frozen released file. Stamping a real
> version here points the generated current-version file at `cat_tools--<version>`. The
> moment this release is merged you **MUST** flip `default_version` back to `stable` on
> master (step 6). If you forget, the next source edit on master will regenerate — and
> corrupt — the just-released version's install file.

- [ ] Bump `default_version` in `cat_tools.control` (bumped by hand).
- [ ] Bump the version in `META.in.json` — the source of truth is
`provides.cat_tools.version` (also update the top-level `version`); `META.json`,
`control.mk`, and `meta.mk` (which feeds `PGXNVERSION`) regenerate via `make`.
- [ ] Advance `release_status` in `META.in.json` as appropriate (unstable → testing →
stable).
- [ ] Add/finish the update script `sql/cat_tools--<prev>--<version>.sql.in`; confirm
`ALTER EXTENSION cat_tools UPDATE` from the previous version reaches the new one,
on multiple PG majors.
- [ ] Stamp `HISTORY.asc`: the top `STABLE` section accumulates user-facing changes as
PRs land; at release, rename that header to the new version number.

## 4. Verify
- [ ] `make verify-results` green (it runs `test` first, then gates on the results).
- [ ] From a clean checkout (or `git archive` of the tag): `make && make install`
regenerates and installs cleanly and `CREATE EXTENSION cat_tools;` reports the new
version — confirms a PGXN consumer can build from the tracked sources alone. (This
mirrors what `make dist` ships, since it archives the tag: committed files only, so
any omitted generated install script is regenerated on the consumer's side.)

## 5. Tag and distribute
- [ ] Commit the release changes; working tree must be clean — `make tag` aborts with
"Untracked changes!" on a dirty tree.
- [ ] `make tag` — creates a git tag named exactly the version, UNPREFIXED (e.g. `0.2.3`,
matching the existing `0.2.2` tag; no `v` prefix), taken from `PGXNVERSION`, and
pushes it to `origin`. It is idempotent when the tag already points at HEAD, and
errors if the tag exists on a different commit. To move an existing tag use
`make forcetag` (= `make rmtag` then `make tag`); `make rmtag` deletes the tag
locally and on `origin`.
- [ ] `make dist` — depends on `tag` (and builds the HTML docs), then
`git archive`s the tag into `../cat_tools-<version>.zip` (parent directory).
Because it archives the tag, only committed files are included. If a `.gitattributes`
exists it must be committed, or `dist` aborts (git archive only honors
`export-ignore` for committed files). `make forcedist` = `forcetag` + `dist`.
- [ ] Upload the `../cat_tools-<version>.zip` to PGXN (manual).

## 6. Return master to `stable` (CRITICAL — do not skip)
- [ ] As soon as the release is merged, flip `default_version` back to the `stable`
pseudo-version on master (`cat_tools.control` + `META.in.json`), open a new top
`STABLE` section in `HISTORY.asc`, and re-seed a fresh
`sql/cat_tools--<this-release>--stable.sql.in` update script for the next cycle.
Leaving master stamped at the real version means the next source edit regenerates
and corrupts the released version's install file.
- [ ] `rm` any stale generated `sql/cat_tools--<released-version>.sql.in` /
`cat_tools--<released-version>.sql` left in the tree — once `default_version` is
`stable`, `make` no longer regenerates them, so they are one-time cleanup.

> The persistent `stable` pseudo-version (a permanent version literally named `stable`,
> with a live `sql/cat_tools--<last-release>--stable.sql.in` update script that every
> source fix targets) decouples fixes from version bumps. The machinery is built into
> `sql.mk`; it lands immediately after the 0.2.3 release, so 0.2.3 itself is the last
> release cut before the scheme exists — steps 3/6 above describe the flow from the
> next release onward.
2 changes: 1 addition & 1 deletion cat_tools.control
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
comment = 'Tools for intorfacing with the catalog'
default_version = '0.2.2'
default_version = '0.2.3'
relocatable = false
schema = 'cat_tools'
2 changes: 1 addition & 1 deletion control.mk
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
EXTENSIONS += cat_tools
EXTENSION_SQL_FILES += sql/cat_tools.sql
EXTENSION_cat_tools_VERSION := 0.2.2
EXTENSION_cat_tools_VERSION := 0.2.3
EXTENSION_cat_tools_VERSION_FILE = sql/cat_tools--$(EXTENSION_cat_tools_VERSION).sql
EXTENSION_VERSION_FILES += $(EXTENSION_cat_tools_VERSION_FILE)
$(EXTENSION_cat_tools_VERSION_FILE): sql/cat_tools.sql cat_tools.control
Expand Down
34 changes: 34 additions & 0 deletions sql/cat_tools--0.2.2--0.2.3.sql.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Correct the c/f/m relkind mapping per pg_class.h in both functions.
* CREATE OR REPLACE preserves existing grants.
*/

CREATE OR REPLACE FUNCTION cat_tools.relation__kind(
relkind cat_tools.relation_relkind
) RETURNS cat_tools.relation_type LANGUAGE sql STRICT IMMUTABLE AS $body$
SELECT CASE relkind
WHEN 'r' THEN 'table'
WHEN 'i' THEN 'index'
WHEN 'S' THEN 'sequence'
WHEN 't' THEN 'toast table'
WHEN 'v' THEN 'view'
WHEN 'c' THEN 'composite type'
WHEN 'f' THEN 'foreign table'
WHEN 'm' THEN 'materialized view'
END::cat_tools.relation_type
$body$;

CREATE OR REPLACE FUNCTION cat_tools.relation__relkind(
kind cat_tools.relation_type
) RETURNS cat_tools.relation_relkind LANGUAGE sql STRICT IMMUTABLE AS $body$
SELECT CASE kind
WHEN 'table' THEN 'r'
WHEN 'index' THEN 'i'
WHEN 'sequence' THEN 'S'
WHEN 'toast table' THEN 't'
WHEN 'view' THEN 'v'
WHEN 'composite type' THEN 'c'
WHEN 'foreign table' THEN 'f'
WHEN 'materialized view' THEN 'm'
END::cat_tools.relation_relkind
$body$;
14 changes: 8 additions & 6 deletions sql/cat_tools.sql.in
Original file line number Diff line number Diff line change
Expand Up @@ -646,15 +646,16 @@ SELECT __cat_tools.create_function(
, 'relkind cat_tools.relation_relkind'
, 'cat_tools.relation_type LANGUAGE sql STRICT IMMUTABLE'
, $body$
/* Per pg_class.h: relkind 'c' = composite type, 'f' = foreign table, 'm' = materialized view. */
SELECT CASE relkind
WHEN 'r' THEN 'table'
WHEN 'i' THEN 'index'
WHEN 'S' THEN 'sequence'
WHEN 't' THEN 'toast table'
WHEN 'v' THEN 'view'
WHEN 'c' THEN 'materialized view'
WHEN 'f' THEN 'composite type'
WHEN 'm' THEN 'foreign table'
WHEN 'c' THEN 'composite type'
WHEN 'f' THEN 'foreign table'
WHEN 'm' THEN 'materialized view'
END::cat_tools.relation_type
$body$
, 'cat_tools__usage'
Expand All @@ -666,15 +667,16 @@ SELECT __cat_tools.create_function(
, 'kind cat_tools.relation_type'
, 'cat_tools.relation_relkind LANGUAGE sql STRICT IMMUTABLE'
, $body$
/* Mapping per pg_class.h, same as relation__kind() above. */
SELECT CASE kind
WHEN 'table' THEN 'r'
WHEN 'index' THEN 'i'
WHEN 'sequence' THEN 'S'
WHEN 'toast table' THEN 't'
WHEN 'view' THEN 'v'
WHEN 'materialized view' THEN 'c'
WHEN 'composite type' THEN 'f'
WHEN 'foreign table' THEN 'm'
WHEN 'composite type' THEN 'c'
WHEN 'foreign table' THEN 'f'
WHEN 'materialized view' THEN 'm'
END::cat_tools.relation_relkind
$body$
, 'cat_tools__usage'
Expand Down
6 changes: 3 additions & 3 deletions test/expected/relation_type.out
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ ok 9 - SELECT cat_tools.relation_relkind('index')
ok 10 - SELECT cat_tools.relation_relkind('sequence')
ok 11 - SELECT cat_tools.relation_relkind('toast table')
ok 12 - SELECT cat_tools.relation_relkind('view')
ok 13 - SELECT cat_tools.relation_relkind('materialized view')
ok 14 - SELECT cat_tools.relation_relkind('composite type')
ok 15 - SELECT cat_tools.relation_relkind('foreign table')
ok 13 - SELECT cat_tools.relation_relkind('composite type')
ok 14 - SELECT cat_tools.relation_relkind('foreign table')
ok 15 - SELECT cat_tools.relation_relkind('materialized view')
ok 16 - SELECT cat_tools.relation_type('r')
ok 17 - SELECT cat_tools.relation_type('i')
ok 18 - SELECT cat_tools.relation_type('S')
Expand Down
Loading
Loading