Skip to content
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
results/
regression.diffs
regression.out
.pgc_built_for_major
55 changes: 55 additions & 0 deletions test/harness_selftest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -843,4 +843,59 @@ eval "$_realfree"
check "premise: the real prober was restored, or every check after this lies" \
"$(pgc_port_free 1 && echo probing || echo stubbed)" "probing"


# ---- an in-tree build must not reuse another major's objects (#536) ---------
#
# lib.sh builds in $PGC_SRCDIR with no clean and no record of which major the
# objects belong to. The MATRIX is not affected -- run_all_versions.sh cleans
# each per-major copy right after its cp -a, measured after #536 was filed
# claiming otherwise. This guard is for the single-suite path only.
check "premise: the build-stamp decision is exposed to be judged" \
"$(type -t pgc_build_needs_clean)" "function"

check "building the same major again needs no clean" \
"$(pgc_build_needs_clean 18 18 yes)" "no"
check "building a DIFFERENT major needs a clean, which is the #536 case" \
"$(pgc_build_needs_clean 18 19 yes)" "yes"
check "and in the other direction too" \
"$(pgc_build_needs_clean 19 18 yes)" "yes"
check "an unparseable stamp cleans rather than guessing" \
"$(pgc_build_needs_clean garbage 18 yes)" "yes"
check "and an empty WANT is refused rather than compared" \
"$(pgc_build_needs_clean 18 "" yes)" "yes"

# The case the end-to-end proof exposed. A tree built BY HAND leaves objects and
# NO stamp; reading that as "nothing to contaminate" let the first version stay
# silent on exactly the path it exists for.
check "objects with NO stamp are unknown provenance and must be cleaned" \
"$(pgc_build_needs_clean "" 18 yes)" "yes"
check "but a tree with no objects at all needs nothing, stamp or not" \
"$(pgc_build_needs_clean "" 18 no)" "no"

_bmsg_unknown="$(pgc_build_stale_message "" 19)"
check "an unknown provenance is not reported as a major" \
"$(grep -c 'PG?' <<<"$_bmsg_unknown")" "0"
check "and it says plainly that no major was recorded" \
"$([ "$(grep -ci 'no recorded major' <<<"$_bmsg_unknown")" -ge 1 ] && echo yes || echo no)" "yes"

# The stamp must be the bare major and nothing else, and this exercises LIB.SH'S
# WRITER rather than a copy of it. Two earlier versions of this check were
# useless: one wrote its own temp file with a correct printf and verified that,
# which cannot fail; the other grepped for the bad form with a pattern that
# matched the GOOD form, so it could never pass. Both were caught by the gate.
check "premise: the stamp writer is a function that can be exercised" \
"$(type -t pgc_write_build_stamp)" "function"

_stmp="$(mktemp)"
pgc_write_build_stamp "$_stmp" 19
check "the stamp lib.sh writes is exactly the major" \
"$(cat "$_stmp")" "19"
check "and it is 3 bytes, not an escaped literal" \
"$(wc -c < "$_stmp" | tr -d ' ')" "3"
rm -f "$_stmp"

check "the build path asks pgc_build_needs_clean rather than merely naming it" \
"$([ "$(grep -c 'pgc_build_needs_clean "' "$TESTDIR/lib.sh")" -ge 1 ] && echo yes || echo no)" "yes"


pgc_summary
57 changes: 57 additions & 0 deletions test/lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,25 @@ pgc_setup() {
# installed .so and saw the same hash either side of a source change that could
# not have produced it.
if [ -z "${PGC_SKIP_BUILD:-}" ]; then
# Objects from another major link but do not load (#536).
_pgc_stamp="$PGC_SRCDIR/.pgc_built_for_major"
_pgc_had="$(cat "$_pgc_stamp" 2>/dev/null | tr -dc '0-9')"
_pgc_objs=no
[ -n "$(find "$PGC_SRCDIR/src" -maxdepth 1 -name '*.o' -print -quit 2>/dev/null)" ] && _pgc_objs=yes
if [ "$(pgc_build_needs_clean "$_pgc_had" "$PGC_MAJOR" "$_pgc_objs")" = yes ]; then
pgc_build_stale_message "$_pgc_had" "$PGC_MAJOR"
make -C "$PGC_SRCDIR" clean PG_CONFIG="$PGC_PG_CONFIG" >/dev/null 2>&1 || true
fi
echo "-- building"
if ! make -C "$PGC_SRCDIR" PG_CONFIG="$PGC_PG_CONFIG" >/dev/null; then
echo "FATAL: the build failed, so there is nothing new to test" >&2
echo " (refusing to report checks against the previously installed .so)" >&2
exit 1
fi
# Stamped only after a build that succeeded. printf '%s\n', NOT '%s\\n':
# the doubled backslash writes the four bytes 1 9 \ n, which only worked
# because the reader strips non-digits. Caught in review, not by a test.
pgc_write_build_stamp "$_pgc_stamp" "$PGC_MAJOR"
echo "-- installing"
if ! make -C "$PGC_SRCDIR" install PG_CONFIG="$PGC_PG_CONFIG" >/dev/null; then
echo "FATAL: the install failed, so the .so under test is not the one just built" >&2
Expand Down Expand Up @@ -451,6 +464,50 @@ pgc_start_failure_message() {
fi
}


# ---- an in-tree build must not reuse another major's objects (#536) ---------
#
# lib.sh builds in $PGC_SRCDIR with no clean and no record of which major the
# objects belong to. One suite against pg18a then pg19a in the same tree links
# the first run's objects into the second .so, which fails to load with
# "undefined symbol: get_relation_info_hook": every cluster start dies and the
# suite reports eight retries with no cause.
#
# The MATRIX is not exposed -- run_all_versions.sh cleans each per-major copy
# right after its cp -a. Measured, after #536 was filed claiming otherwise.
#
# Objects present with NO stamp are unknown provenance and must be cleaned: that
# is what a hand-run `make PG_CONFIG=...` leaves, which is how anyone debugging
# builds and how every gate script here builds.
pgc_build_needs_clean() {
local have="${1:-}" want="${2:-}" objects="${3:-}"

case "$want" in '' | *[!0-9]*) echo yes; return ;; esac
[ "$objects" = yes ] || { echo no; return; }
[ -z "$have" ] && { echo yes; return; }
case "$have" in *[!0-9]*) echo yes; return ;; esac
[ "$have" = "$want" ] && echo no || echo yes
}

# The stamp writer, as a function so a check can exercise THE WRITER rather
# than a copy of it. It was written inline as printf '%s\\n' -- a doubled
# backslash inside single quotes -- which emits the four bytes `1 9 \ n`. That
# passed unnoticed because the reader does tr -dc '0-9' and strips the junk; a
# direct comparison against the major failed. Found in review, not by a check.
pgc_write_build_stamp() {
printf '%s\n' "${2:-}" > "${1:-/dev/null}" 2>/dev/null || true
}

# An absent stamp is not "built for PG?" -- that asserts a provenance the code
# never recorded, which is the defect #537 was filed about.
pgc_build_stale_message() {
if [ -z "${1:-}" ]; then
printf -- '-- the tree holds objects with no recorded major and this run wants PG%s; cleaning first (#536)\n' "${2:-?}"
else
printf -- '-- the tree was last built for PG%s and this run wants PG%s; cleaning first (#536)\n' "$1" "${2:-?}"
fi
}

# ---- SQL helpers (run as root over TCP, trust auth) ------------------------

PGC_PSQL_BASE() {
Expand Down
Loading