test: an in-tree build must not reuse another major's objects (#536) - #551
Conversation
ChronicallyJD
left a comment
There was a problem hiding this comment.
Two working files are committed by accident, and they are the only thing I
would block on. The rest I will review properly once they are out, since the diff
is currently 200 lines of artifact around the change.
fix536.patch 177 lines — a copy of this PR's own diff
gate536c.sh 23 lines — a personal gate runner
fix536.patch is the change committed twice: once as the change, once as a patch
file describing it. Whichever is edited later, the other becomes a stale
description of the code sitting next to the code.
gate536c.sh is container-specific:
cd /root/pgcolumnar || exit 1That path exists on your box and nowhere else, so anyone running it from a
checkout gets someone else's tree or an exit 1. It also carries the
ASAN_OPTIONS/LSAN_OPTIONS exports from your pg18_san note — useful knowledge,
but as a private script rather than as repo content.
Neither is referenced by SUITES, so harness_selftest's "every registered
suite has a file" check does not look at them, and nothing else will notice they
are there.
What I did check
The .gitignore addition of .pgc_built_for_major is right and belongs with the
change — that is the marker file the guard writes, and shipping it untracked is
correct.
Not a finding, stated so you know I looked
I did not review the guard itself yet. Once the two files are gone I will run it
the way I ran #543 and #538: seed a tree with foreign objects, confirm the guard
reddens, confirm it passes on a clean tree, and confirm it is not deletable
without a red.
f6894b5 to
ae46918
Compare
|
Both working files removed at
Cause: I staged with Neither would have been caught by anything. They are not in That is the third time scratch has reached a tree I was about to publish from, and the first two only surfaced because The Guard itself is unchanged and still gated: |
|
Rebased onto main after #544 landed, and rebuilt rather than resolved. Now #544 and this PR both add to Why rebuilt and not resolvedMy union resolution produced a Worth recording, because the second error is the one that nearly mattered: my script ran And the gate caught one more, which
|
harness_selftest |
89 checks PASSED, run without PGC_SKIP_BUILD so the build path under test actually runs |
| end to end | build PG18 by hand, run a suite on PG19 in the same tree → -- the tree holds objects with no recorded major and this run wants PG19; cleaning first (#536), native_vecskip 13/13 PASSED |
replication (#544's suite, now beneath this) |
41/41 PASSED |
bash -n on both changed files |
passes, and gates the commit |
#544's ten checks and this PR's ten coexist; neither was lost in the rebase.
ae46918 to
7c9cc9e
Compare
ChronicallyJD
left a comment
There was a problem hiding this comment.
The two working files are out, so this is the review of the guard I said I would
do. The guard works, proved end to end, and I would merge it. One defect,
small and real.
Verified end to end, not from the unit checks
Built a tree for PG17, leaving 25 objects behind, then ran a suite with PG19:
-- the tree holds objects with no recorded major and this run wants PG19; cleaning first (#536)
native_format.sh: PASSED
It fires, it cleans, and the suite passes afterwards. Note which branch that
exercised: a bare make leaves objects and writes no stamp, so this took the
unknown-provenance path rather than the mismatch path — which is the right
default and the one a human is most likely to hit, since building by hand before
running a suite is the natural thing to do.
The call-site check is the part I would have missed a week ago. #538's lesson
applied to your own change:
check "the build path asks pgc_build_needs_clean rather than merely naming it"
The defect: the stamp file is malformed
lib.sh:192
printf '%s\\n' "$PGC_MAJOR" > "$_pgc_stamp"Inside single quotes that is a double backslash, so printf emits a literal
\ followed by n. The stamp on disk is four bytes:
$ od -c .pgc_built_for_major
0000000 1 9 \ n
not 19 and a newline.
It works today, and only because the reader sanitises hard:
_pgc_had="$(cat "$_pgc_stamp" 2>/dev/null | tr -dc '0-9')" # -> 19tr -dc '0-9' discards the backslash and the n. Any reader that does not
strip non-digits gets 19\n:
v="$(printf '%s\\n' 19)"; [ "$v" = "19" ] -> NOT equal: [19\n]
So the file is wrong, the guard is right, and the guard is what is hiding it. A
later reader — a debug echo, a cat in a bug report, a second consumer that
compares directly — sees a value that looks like a mistake in the major number.
printf '%s\n' is the fix.
Why I am flagging a cosmetic-looking thing
It is the same class as the C-style comment I put in portlib.sh earlier today:
an escape that parses cleanly, produces no error, and means something other than
what it reads as. bash -n accepted mine; tr -dc accepts yours. Both are found
only by looking at what actually came out.
Nothing else. The edge cases are well chosen — unparseable stamp, empty want,
objects with no stamp, and no objects at all are each pinned separately, and the
"no recorded major" message avoids printing PG?.
…dprompt#536) Rebased onto d19c2e4 after commandprompt#544, commandprompt#547 and commandprompt#549 landed. Rebuilt from main rather than resolved: the union resolution of an earlier rebase produced a lib.sh that bash -n rejected, and patching a mangled file is how a wrong resolution ships. Fixes a defect found in review by the other session, not by any check: the stamp was written with printf '%s\\n', which emits the four bytes 1 9 \ n. It worked only because the reader does tr -dc '0-9' and strips the junk; a direct comparison against 19 failed. That is the third over-escaping in this PR -- the message printf was the second -- and bash -n accepts all of them, because they are syntactically valid and semantically wrong. Two checks added for it: the stamp must read back as the bare major, and lib.sh must not contain the doubled-backslash writer.
|
Your stamp finding is fixed, and the two checks I first wrote for it were both worthless. The gate caught both. Verified your finding byte for byte before changing anything: Exactly as you described: it worked only because the reader strips non-digits. Both of my first checks were useless, in opposite directions
One too loose, one too tight, neither exercising the thing under test. Both written specifically to catch a defect you had just found in my code. The fix is to make the writer testable rather than test a proxy
Three checks: the writer exists as a function, what it writes reads back as the bare major, and it is 3 bytes rather than 4. Gate, on d19c2e4
Rebased onto d19c2e4, after #544, #547 and #549. Rebuilt from main rather than resolved in place, for the reason in the commit message: an earlier union resolution produced a That is three over-escaping defects in this one PR — the stamp, the message, and the check's own grep — every one syntactically valid and semantically wrong, and every one invisible to |
7c9cc9e to
2c39bae
Compare
…nd (#554) Three PRs on 2026-08-09 each appended a block to the end of this file and every pair conflicted, while the one that edited the middle merged clean: #544 -> #549 CONFLICT #553 -> #544 clean #544 -> #551 CONFLICT #553 -> #549 clean #549 -> #551 CONFLICT #553 -> #551 clean That is row two of this file's own measurement about SUITES, happening in the file that argues it. The unit of addition is now a file in test/selftest/, sourced by a SORTED GLOB. Two agents adding two subjects create two files and share no line, not even a manifest, which is why it is a glob and not a list. THE SPLIT WAS BROKEN IN A WAY NO STATIC CHECK COULD SEE. Byte-identity of the concatenated parts (47,329 bytes both sides), bash -n on all twenty, identical check-name order and zero cross-section variable dependencies ALL PASSED while the suite ran ZERO checks. ${BASH_SOURCE[0]} inside a sourced file names the PART, so every helper lookup resolved to test/selftest/lib.sh, check() was never defined, and nothing ran. The directory is resolved once now as PGC_TESTDIR. Byte-identity proves the text is the same. It cannot prove the text still MEANS the same thing in another file. Only running it showed that. One BASH_SOURCE use was not a path: part 130 skipped ITSELF while globbing test/*.sh so as not to match its own search pattern. Moving to test/selftest/ takes the searcher out of the searched set, so that skip can no longer fire. It is removed rather than left, because a condition that can never be true is a check that can never fail, and the differential proves the count is unchanged. Gate, a differential rather than a pass: BEFORE 104 checks, 0 fails AFTER 107 checks, 0 fails all 104 pre-existing checks identical in result AND order Removal proof: appending a check to the driver reddens 'the driver holds no checks; they all live in parts'.
Closes #536, with the issue's own scope corrected first.
lib.shbuilds in$PGC_SRCDIRwith no clean and no record of which major the objects belong to. Run one suite against pg18a and then pg19a in the same tree and the second.solinks the first run's objects, fails to load withundefined symbol: get_relation_info_hook, and every cluster start dies — eight identical retries, no cause.A stamp beside the objects records their major; the build cleans when it changes.
#536 as filed was wrong about where this lives
It claimed "nothing in
test/ever runsmake clean" and that the matrix was exposed viacp -a.run_all_versions.sh:561cleans every per-major copy immediately after that copy. My grep looked formake cleanas an adjacent pair and cannot matchmake -C "$builddir" clean.build_simple_rel_hook(PG19)get_relation_info_hook(PG18)The mechanism is real and line 561 neutralises it. The matrix was never exposed, so this guard is scoped to the single-suite path — the one with no clean, and the one that actually bit me.
The end-to-end proof caught what the unit checks could not
The first version read an absent stamp as "nothing to contaminate". That is wrong for the case that matters: a tree built by hand — plain
make PG_CONFIG=..., which is how anyone debugging builds and how every gate script in this repo builds — leaves objects and no stamp. The guard stayed silent on exactly the path it exists for, and the PG19 run still died.All nine unit checks were green at that point, and the removal proof reddened correctly. Only a test that reproduced the original failure could see it. Unstamped objects are unknown provenance, not safe; three checks now pin that.
The message says only what it established
An absent stamp is not
last built for PG?. That asserts a provenance the code never recorded — #537's defect in a new message. The two cases are distinguished, and two checks pin that the unknown path never emitsPG?.Gate
harness_selftestPGC_SKIP_BUILD, so the build path under test actually runsnative_vecskip13/13 PASSEDobjects with NO stamp are unknown provenancereddenslib.shis in every suite's pathAn earlier run of mine reported these unit checks green while filtering out a failing line that
PGC_SKIP_BUILDcauses by construction. Running it the way it is meant to run is why the number above is 68 and trustworthy.I have not merged this and will not.