Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
159 changes: 159 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
name: CI
on:
push:
branches:
- master
pull_request:
env:
PGUSER: postgres
jobs:
# Style linter (https://github.com/Postgres-Extensions/linter, vendored at
# .vendor/linter -- lint.mk is the thin local hand-off, see its comment).
# No `needs:` on anything: this is the cheapest possible check (no
# database, no container beyond a plain checkout, seconds to run), so it
# should never be waiting in a queue behind -- or racing for a runner slot
# against -- the PG matrix below. It's gated behind nothing and everything
# else of any real weight is gated behind it (see the `test` job's needs).
# Deliberately checked out WITHOUT submodules -- `make lint` is the same
# command a developer runs locally, and lint.mk self-initializes the
# submodule on first use. Using the exact same entry point here is what
# actually proves that self-init works, rather than papering over it with
# a submodules: true checkout.
lint:
name: 🧹 SQL Lint
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v4
- name: Lint SQL
run: make lint

# Cheap gate that lets the test matrix below skip itself on commits that
# touch only docs. Runs on every push/pull_request unconditionally (no
# paths-ignore on the workflow itself) -- a workflow-level paths-ignore
# would skip this job too on a docs-only push, and the required
# all-checks-passed check would then never report and get stuck Pending in
# branch protection.
#
# Also derives the supported-PostgreSQL-major list the test job's matrix
# consumes, from a single pair of constants below, so adding or dropping a
# major is a one-line edit here instead of touching the matrix directly.
changes:
name: 🔍 Detect changes & derive PG matrix
runs-on: ubuntu-latest
outputs:
docs_only: ${{ steps.diff.outputs.docs_only }}
supported_pg: ${{ steps.pg.outputs.supported_pg }}
steps:
- name: Check out the repo
uses: actions/checkout@v4
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: |
# Fail-safe FIRST, before anything else runs: any early exit below
# (an unusable BASE/HEAD, a failed git diff) leaves this in place,
# so the test matrix only ever gets skipped after actually proving
# the push is docs-only.
echo "docs_only=false" >> "$GITHUB_OUTPUT"

if [ "${{ github.event_name }}" = "pull_request" ]; then
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"

# A missing HEAD, or an all-zeros BASE (a new branch's first push,
# where GitHub reports no prior commit), means no real diff can be
# computed -- leave the fail-safe in place.
if [ -z "$HEAD" ] || [ -z "$BASE" ] || [[ "$BASE" =~ ^0+$ ]]; then
exit 0
fi

CHANGED=$(git diff --name-only "$BASE" "$HEAD") || exit 0
[ -z "$CHANGED" ] && exit 0

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

echo "changed files:"
echo "$CHANGED"
echo "docs_only=$DOCS_ONLY" >> "$GITHUB_OUTPUT"
- name: Derive the supported-PostgreSQL-major list
id: pg
run: |
# SINGLE SOURCE OF TRUTH for the supported PostgreSQL majors. To
# add or drop a major, edit only the two constants below; the test
# job's matrix derives its version list from them. Do NOT hardcode
# a supported major directly in a job matrix.
#
# NEWEST -- highest PostgreSQL major tested.
# CURRENT_FLOOR -- oldest major supported. object_reference
# requires cat_tools at both build and runtime,
# and cat_tools's own current release declares
# PostgreSQL 12 as its build floor, so
# object_reference can't usefully claim support
# for anything older either.
NEWEST=18
CURRENT_FLOOR=12

supported=$(seq "$NEWEST" -1 "$CURRENT_FLOOR")

# Emit a JSON array for the test job's matrix to consume via
# fromJSON.
json=$(printf '%s\n' $supported | paste -sd, - | sed 's/^/[/; s/$/]/')
echo "supported_pg=$json" >> "$GITHUB_OUTPUT"

test:
# Gated behind lint too, not just changes: lint is nearly free to run,
# so a baseline that's already broken by a style violation shouldn't
# also tie up runner slots on the much heavier PG matrix below.
# success() must be written explicitly -- GitHub only assumes success()
# as a job's default when the job has no if: at all.
needs: [changes, lint]
if: success() && needs.changes.outputs.docs_only != 'true'
strategy:
matrix:
# Supported majors, from the single source in the changes job.
pg: ${{ fromJSON(needs.changes.outputs.supported_pg) }}
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
Comment thread
jnasbyupgrade marked this conversation as resolved.
- name: Test on PostgreSQL ${{ matrix.pg }}
run: make test

# A single stable check name for use as a required status check in branch
# 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. test, on a docs-only push), and fails if any failed or were
# cancelled.
all-checks-passed:
needs: [changes, lint, test]
if: always()
runs-on: ubuntu-latest
steps:
- 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ test/install/schedule
# Misc
tmp/
.DS_Store
.claude/settings.local.json

# pg_tle generated files
/pg_tle/
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule ".vendor/linter"]
path = .vendor/linter
url = https://github.com/Postgres-Extensions/linter.git
13 changes: 0 additions & 13 deletions .travis.yml

This file was deleted.

1 change: 1 addition & 0 deletions .vendor/linter
Submodule linter added at b40aaf
35 changes: 34 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,23 @@ extra_clean += $(wildcard test/dump/*.log)
dump_test: test/dump/run.sh test/helpers/object_table.sql $(wildcard test/dump/*.sql)
$< -f # Force drop of databases if they exist

CAT_TOOLS_VERSION = 0.3.0
CAT_TOOLS_BUILD_DIR = tmp/cat_tools-$(CAT_TOOLS_VERSION)
extra_clean += $(CAT_TOOLS_BUILD_DIR)

.PHONY: cat_tools
cat_tools: $(DESTDIR)$(datadir)/extension/cat_tools.control
$(DESTDIR)$(datadir)/extension/cat_tools.control:
pgxn install --unstable cat_tools
# `pgxn install --unstable cat_tools` resolves to the newest release
# published to the PGXN package index, which is still 0.2.1 -- it fails
# standalone on modern PostgreSQL with "column oid specified more than
# once" at CREATE EXTENSION. A fixed release, 0.3.0, is tagged in
# cat_tools' own git repo but hasn't been uploaded to PGXN yet, so build
# it from that tag directly until PGXN has it.
rm -rf $(CAT_TOOLS_BUILD_DIR)
git clone --branch $(CAT_TOOLS_VERSION) --depth 1 https://github.com/Postgres-Extensions/cat_tools.git $(CAT_TOOLS_BUILD_DIR)
$(MAKE) -C $(CAT_TOOLS_BUILD_DIR) install PG_CONFIG=$(PG_CONFIG) DESTDIR=$(DESTDIR)
rm -rf $(CAT_TOOLS_BUILD_DIR)

.PHONY: count_nulls
count_nulls: $(DESTDIR)$(datadir)/extension/count_nulls.control
Expand All @@ -32,3 +45,23 @@ test_factory: $(DESTDIR)$(datadir)/extension/test_factory.control
$(DESTDIR)$(datadir)/extension/test_factory.control:
pgxn install test_factory


# Style linter (see https://github.com/Postgres-Extensions/linter, vendored
# at .vendor/linter -- lint.mk is the thin local hand-off, see its comment).
# Scoped to sql/object_reference.sql rather than the default `sql/ test/`:
# the versioned install/update files under sql/ (object_reference--*.sql,
# e.g. object_reference--0.1.0.sql/--stable.sql) are frozen once released and
# never hand-edited again (see this repo's CLAUDE.md / memory), so linting
# them would produce permanent, unfixable findings and make `make lint`
# unusable as a CI gate.
#
# Guarded on .git being present: a tarball build (PGXN distribution, or any
# `git archive` checkout with no .git) has no submodule to initialize, and
# Make resolves every `include` before running any target regardless of
# which target was requested -- so an unguarded self-init rule in lint.mk
# would break `make`/`make install` entirely for a tarball build, not just
# `make lint`.
ifneq ($(wildcard .git),)
LINT_TARGETS = sql/object_reference.sql test/
include lint.mk
endif
11 changes: 11 additions & 0 deletions lint.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# lint.mk — thin wrapper; the whole local footprint for consuming
# https://github.com/Postgres-Extensions/linter. Everything else lives in
# the .vendor/linter submodule; see its README for available targets/rules.
#
# Self-initializing (via the rule below) so `make lint` works right after a
# plain `git clone`, with no --recurse-submodules needed, and so CI can rely
# on the exact same entry point a developer would use locally.
.vendor/linter/lint.mk:
git submodule update --init -- .vendor/linter

include .vendor/linter/lint.mk
36 changes: 0 additions & 36 deletions pg-travis-test.sh

This file was deleted.

Binary file removed sql/.object_reference.sql.swo
Binary file not shown.
17 changes: 11 additions & 6 deletions sql/object_reference--stable.sql
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ BEGIN
RAISE DEBUG 'search_path changed to %', current_setting('search_path');
END
$$;
/*
/* EXCLUDED CODE: schema-restriction check below not currently enforced
DO $$
DECLARE
c_schema CONSTANT name := (SELECT extnamespace::regnamespace::text FROM pg_extension WHERE extname = 'cat_tools');
Expand Down Expand Up @@ -85,7 +85,7 @@ CREATE FUNCTION __object_reference.create_function(
, grants text DEFAULT NULL
) RETURNS void LANGUAGE plpgsql AS $body$
DECLARE
c_clean_args text := cat_tools.function__arg_types_text(args);
c_clean_args text := cat_tools.routine__parse_arg_types_text(args);

create_template CONSTANT text := $template$
CREATE OR REPLACE FUNCTION %s(
Expand Down Expand Up @@ -180,7 +180,7 @@ CREATE TABLE _object_reference.object(
, object_names text[] NOT NULL
, object_args text[] NOT NULL
, CONSTRAINT object__u_object_names__object_args UNIQUE( object_type, object_names, object_args )
/* TODO: this can't be a trigger because some objects won't exist when a dump is loaded
/* EXCLUDED CODE: TODO: this can't be a trigger because some objects won't exist when a dump is loaded
, CONSTRAINT object__address_sanity
-- pg_get_object_address will throw an error if anything is wrong, so the IS NOT NULL is mostly pointless
CHECK( pg_catalog.pg_get_object_address(object_type::text, object_names, object_args) IS NOT NULL )
Expand All @@ -193,7 +193,7 @@ GRANT REFERENCES ON _object_reference.object TO object_reference__dependency;
CREATE TABLE _object_reference._object_oid(
object_id int PRIMARY KEY REFERENCES _object_reference.object ON DELETE CASCADE ON UPDATE CASCADE
, classid regclass NOT NULL
/* TODO: needs to be a trigger
/* EXCLUDED CODE: TODO: needs to be a trigger
CONSTRAINT classid_must_match__object__address_classid
CHECK( classid IS NOT DISTINCT FROM cat_tools.object__address_classid(object_type) )
*/
Expand Down Expand Up @@ -523,7 +523,12 @@ SELECT __object_reference.create_function(
, $body$
SELECT cat_tools.objects__shared()
|| cat_tools.objects__address_unsupported()
|| '{event trigger}'
/*
* pg_get_object_address() doesn't recognize "partitioned table" or
* "partitioned index" (only the base "table"/"index" types it derives
* from), so object identity tracking can't round-trip them.
*/
|| '{event trigger, partitioned table, partitioned index}'
$body$
, 'Returns array of object types that are not supported.'
, 'object_reference__usage'
Expand Down Expand Up @@ -1254,7 +1259,7 @@ BEGIN
RETURN c_next_level;

EXCEPTION WHEN undefined_table THEN
/*
/* EXCLUDED CODE
CREATE TEMP TABLE __object_reference__ddl_capture AS
SELECT c_next_level, capture__start.object_group_id
;
Expand Down
Loading
Loading