diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 609953660..0512c9031 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -168,6 +168,14 @@ jobs: steps: - uses: actions/checkout@v4 + # THE RELEASE TAGS, FOR native_upgrade_converge'S FIXTURE ARMS (#1341). Each + # released fixture must be byte-identical to what its tag shipped, compared by + # blob id against `v:pgcolumnar--.sql`. actions/checkout + # fetches no tags, so these arms used to skip on every CI run. Depth 1 is + # enough: only the file at each tagged commit is read. + - name: fetch the release tags, for the fixture-blob comparisons + run: git fetch --depth=1 origin '+refs/tags/v1.0-alpha*:refs/tags/v1.0-alpha*' + - name: Add the PGDG repository and install PostgreSQL ${{ matrix.pg }} with its headers run: | set -euo pipefail @@ -557,6 +565,14 @@ jobs: git fetch --depth=1 origin \ "+refs/heads/${{ github.base_ref }}:refs/remotes/origin/${{ github.base_ref }}" + # THE RELEASE TAGS, FOR native_upgrade_converge'S FIXTURE ARMS (#1341). Each + # released fixture must be byte-identical to what its tag shipped, compared by + # blob id against `v:pgcolumnar--.sql`. actions/checkout + # fetches no tags, so these arms used to skip on every CI run. Depth 1 is + # enough: only the file at each tagged commit is read. + - name: fetch the release tags, for the fixture-blob comparisons + run: git fetch --depth=1 origin '+refs/tags/v1.0-alpha*:refs/tags/v1.0-alpha*' + # The repository and the package lists first, on their own, because the # cache key below is derived from the versions apt resolves and cannot be # computed before the lists exist. diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 02c8b54b4..3d48bdca2 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -90,6 +90,15 @@ jobs: with: fetch-depth: 2 + # THE RELEASE TAGS, FOR native_upgrade_converge'S FIXTURE ARMS (#1341). Each + # released fixture must be byte-identical to what its tag shipped, compared by + # blob id against `v:pgcolumnar--.sql`. actions/checkout + # fetches no tags, so these arms used to skip on every CI run. Depth 1 is + # enough: only the file at each tagged commit is read, and it leaves the + # checkout's own history as it was (HEAD~1 still resolves at fetch-depth 2). + - name: fetch the release tags, for the fixture-blob comparisons + run: git fetch --depth=1 origin '+refs/tags/v1.0-alpha*:refs/tags/v1.0-alpha*' + # The packages are ~102 MB, of which ~91 MB is the LLVM toolchain that # postgresql-server-dev-N depends on. That download is what stalls: every # failure of this step sat at the 600s bound while every success finished @@ -727,6 +736,15 @@ jobs: steps: - uses: actions/checkout@v4 + # THE RELEASE TAGS, FOR native_upgrade_converge'S FIXTURE ARMS (#1341). Each + # released fixture must be byte-identical to what its tag shipped, compared by + # blob id against `v:pgcolumnar--.sql`. actions/checkout + # fetches no tags, so these arms used to skip on every CI run. Depth 1 is + # enough: only the file at each tagged commit is read, and it leaves the + # checkout's own history as it was (HEAD~1 still resolves at fetch-depth 2). + - name: fetch the release tags, for the fixture-blob comparisons + run: git fetch --depth=1 origin '+refs/tags/v1.0-alpha*:refs/tags/v1.0-alpha*' + - name: Add the PGDG repository and install PostgreSQL ${{ matrix.pg }} with its headers run: | set -euo pipefail diff --git a/test/native_upgrade_converge.sh b/test/native_upgrade_converge.sh index 454900137..78b4b1443 100755 --- a/test/native_upgrade_converge.sh +++ b/test/native_upgrade_converge.sh @@ -219,23 +219,26 @@ _fx_git() { git -C "$HERE/.." "$@" 2>/dev/null; } if ! _fx_git rev-parse --git-dir >/dev/null; then _fx_why="no git repository in the tree under test" elif [ -z "$(_fx_git tag -l 'v1.0-alpha*')" ]; then - # CI checks out at depth 1 with no tags, so this cannot run there. It runs - # locally and in the five-major release gate, which is where a fixture is - # captured and therefore where it can be captured wrongly. + # actions/checkout fetches no tags, so CI fetches the release tags in a step + # of its own for these arms (#1341). A local checkout without them still + # lands here, and the arms skip rather than compare against nothing. _fx_why="no release tags in this checkout" else _fx_why="" fi +# The name is written out in each call rather than held in a variable: the twin +# grader reads a check's name from the call, and `check "$_fx_name"` was invisible +# to it, so a port could drop this arm and still grade complete. for v in $_FX_TAGGED; do - _fx_name="the $v fixture is byte-identical to what v$v shipped" if [ -n "$_fx_why" ]; then - check_skip "$_fx_name" "SKIP $_fx_name ($_fx_why)" "$_fx_why" + check_skip "the $v fixture is byte-identical to what v$v shipped" \ + "SKIP the $v fixture is byte-identical to what v$v shipped ($_fx_why)" "$_fx_why" continue fi _fx_tag="$(_fx_git rev-parse "v$v:pgcolumnar--$v.sql" || echo "no such path at v$v")" _fx_fix="$(_fx_git hash-object "$HERE/fixtures/pgcolumnar--$v.sql" || echo "fixture missing")" - check "$_fx_name" "$_fx_fix" "$_fx_tag" + check "the $v fixture is byte-identical to what v$v shipped" "$_fx_fix" "$_fx_tag" done # THE ARM THAT CATCHES THE QUIET HALF. Staging unconditionally fixes it; this diff --git a/test/pytest/TESTS.md b/test/pytest/TESTS.md index ab5ab84d8..ae1bcf83f 100644 --- a/test/pytest/TESTS.md +++ b/test/pytest/TESTS.md @@ -7531,7 +7531,7 @@ Part of the pytest twin of `test/drop_cleanup.sh` (#1265, ported for #1341): its ## 143. test_native_upgrade_converge.py: every release upgrades to a fresh catalog, and the window before it -The pytest twin of `test/native_upgrade_converge.sh` (#1341), by the shell's names, except its release-tag checks, so the pair is declared INCOMPLETE. `ALTER EXTENSION pgcolumnar UPDATE` must bring a database on any released version to a catalog identical to a fresh install of the current `default_version`: every function's definition, ACL and comment, and every relation, column, type and foreign-data wrapper. The released starting points are the install scripts in `test/fixtures`. They are staged into the extension directory once for the module, under a lock that makes a second stager wait, and whatever was there is put back after. A backup already there means an earlier run died, so it holds the original and is kept. +The pytest twin of `test/native_upgrade_converge.sh` (#1341), every check by the shell's name, so the pair is declared COMPLETE. `ALTER EXTENSION pgcolumnar UPDATE` must bring a database on any released version to a catalog identical to a fresh install of the current `default_version`: every function's definition, ACL and comment, and every relation, column, type and foreign-data wrapper. The released starting points are the install scripts in `test/fixtures`. They are staged into the extension directory once for the module, under a lock that makes a second stager wait, and whatever was there is put back after. A backup already there means an earlier run died, so it holds the original and is kept. Between installing a new library and running the update, a database runs the new library over its previous release's catalog (#1341 item 66). Reads and writes must work there. `DROP TABLE`, `TRUNCATE` and `pgcolumnar.vacuum()` need a catalog table the old one lacks, so they must be refused with 42P01, change nothing, and carry a hint naming the update. After the update all three succeed on the same rows. @@ -7544,6 +7544,8 @@ Between installing a new library and running the update, a database runs the new | `test_the_staged_fixtures_are_the_committed_ones` | the control's `default_version`; no backup was already staged; every fixture was staged, and each staged script is the committed fixture | | `test_each_release_upgrades_to_a_fresh_catalog` | parametrized by the four fixtures: a fresh install of the target has more than 100 objects to compare, the upgrade applies, the extension is at the target, and its catalog equals the fresh install's | | `test_the_upgrade_window` | at the previous catalog: rows written, deleted and read; the three operations refused with the hint and nothing changed; after the update the rows are intact and all three succeed | +| `test_the_fixture_sweep_found_fixtures` | the fixtures on disk, less the synthetic `1.0-alpha`, are not an empty list: an empty sweep would compare nothing and report clean | +| `test_each_fixture_is_what_its_tag_shipped` | parametrized by those fixtures: each one's blob id equals `v:pgcolumnar--.sql`'s; with no git repository or no release tags it cannot run (UNMET_PRECONDITION) rather than pass. CI fetches the tags for it | ## 144. test_fk_referencing.py: a foreign key referencing a columnar table is refused where it is chosen diff --git a/test/pytest/expected_tests.txt b/test/pytest/expected_tests.txt index 6b01c6e7c..71063c813 100644 --- a/test/pytest/expected_tests.txt +++ b/test/pytest/expected_tests.txt @@ -858,4 +858,6 @@ guard_tests 442 # tests, one per check, by collection over the cluster files. # cluster_tests 1018 -> 1025 for #1341, test_native_upgrade_converge.py ported by name except its # release-tag arms (1 -> 8 tests), by collection over the cluster files. -cluster_tests 1025 +# cluster_tests 1025 -> 1029 for #1341, test_native_upgrade_converge.py's release-tag arms (the +# fixture sweep premise and one test per tagged fixture: 1 + 3), by collection over the cluster files. +cluster_tests 1029 diff --git a/test/pytest/test_compare_to_bash.py b/test/pytest/test_compare_to_bash.py index 88f4f7038..22634568d 100644 --- a/test/pytest/test_compare_to_bash.py +++ b/test/pytest/test_compare_to_bash.py @@ -168,6 +168,7 @@ "native_skip", "native_toasted_write", "native_truncate", + "native_upgrade_converge", "native_vacuum_race", "native_varlena_bound", "native_vecskip", @@ -226,12 +227,6 @@ # about the PORT rather than a licence: the standing arm does not grade it, so the # reason is the only thing standing between a declared gap and a forgotten one. INCOMPLETE = { - # #1341 ported every arm but the release-tag ones: each tagged fixture is - # byte-identical to what its tag shipped. CI checks out without tags, so the shell - # skips them there with check_skip, and this layer has no verdict for an arm that - # runs on one checkout and not another. - "native_upgrade_converge": "every arm is ported except the release-tag sweep and " - "the per-tag fixture-blob comparisons", # #1341 item 29 ported the arms four fixes added with shell arms only. Each # pair below holds that fix's arms and nothing else of its shell suite. "inheritance": "the #1259 arms (a partitioned columnar parent is refused with " diff --git a/test/pytest/test_native_upgrade_converge.py b/test/pytest/test_native_upgrade_converge.py index d1f220b17..4c2276325 100644 --- a/test/pytest/test_native_upgrade_converge.py +++ b/test/pytest/test_native_upgrade_converge.py @@ -306,6 +306,46 @@ def test_the_staged_fixtures_are_the_committed_ones(staging, expect): "every staged install script is the committed fixture, not a leftover") +# The fixtures to compare against their tags, derived from the fixtures on disk as +# the shell derives them, so a fixture added at the next cycle-open is checked +# without anyone listing it. 1.0-alpha is synthetic: v1.0-alpha shipped +# pgcolumnar--1.0-dev.sql, so no tag holds a pgcolumnar--1.0-alpha.sql. +TAGGED = sorted(p.name[len("pgcolumnar--"):-len(".sql")] for p in FIXTURES.glob("pgcolumnar--*.sql") + if p.name != "pgcolumnar--1.0-alpha.sql") + + +def _git(*args): + """-> git's stripped stdout in the repository under test, or None when it fails.""" + r = subprocess.run(["git", "-C", str(REPO), *args], capture_output=True, text=True) + return r.stdout.strip() if r.returncode == 0 else None + + +def test_the_fixture_sweep_found_fixtures(expect): + # An empty sweep would leave nothing below to compare and report clean. + expect.text("yes" if TAGGED else "no", "yes", + "premise: the fixture sweep found fixtures to compare against their tags") + + +@pytest.mark.parametrize("v", TAGGED) +def test_each_fixture_is_what_its_tag_shipped(v, expect): + """Blob ids, not a diff: a blob id is a lookup that no similarity heuristic can + distort. The local tag is trusted, as in the shell; if this fails and the fixture + looks right, check the tag with `git ls-remote origin refs/tags/v`.""" + if _git("rev-parse", "--git-dir") is None: + why = "no git repository in the tree under test" + elif not _git("tag", "-l", "v1.0-alpha*"): + why = "no release tags in this checkout" + else: + why = "" + if why: + expect.cannot_run("UNMET_PRECONDITION", why, + name=f"the {v} fixture is byte-identical to what v{v} shipped") + return + tag = _git("rev-parse", f"v{v}:pgcolumnar--{v}.sql") or query_error(f"no such path at v{v}") + fixture = _git("hash-object", str(FIXTURES / f"pgcolumnar--{v}.sql")) or query_error("fixture missing") + expect.text(fixture, tag, f"the {v} fixture is byte-identical to what v{v} shipped") + + @pytest.mark.parametrize("from_", VERSIONS) def test_each_release_upgrades_to_a_fresh_catalog(pgc_cluster, staging, reference, from_, expect): extdir, _, _ = staging