Mutation benchmark of progress reporting: protocol, scripts, results ===================================================================== Not a patch (no .patch/.diff, so that cfbot leaves it alone). Each section below is one file; to rerun, split them back into mutacion/ (and huecos/equivalentes.md next to it), put the patches judged in mutacion/ronda1/ (round 1: the test of 2026-11-02, available on request) and mutacion/ronda2/ (round 2: v6-0001 and v6-0002 as posted), then: mutacion/correr.sh setup round 1, both judges mutacion/correr.sh noche (about 4 hours on 8+ cores) mutacion/correr.sh reserva the reserve, on round 1's tree mutacion/correr.sh ronda2 round 2: the 572 and the reserve mutants.jsonl and reserva.jsonl are not included (224 kB and 130 kB): generar.py and generar-reserva.py rebuild them from the source at the base, and correr.sh checks they are the sealed ones. The seals, each taken before the run it covers: PROTOCOL.sha256 (sealed 2026-11-03T00:08:56-03:00): ca59654dc65434caf3e12bd11e7718a41c8fcfe06a6fdc22f35d982ed095b0bc PROTOCOL.md 470f79b37e74b135a57fc8b4a66e19ed9a79a3542a9502b8f835684e3fc5b27b generar.py a6ac328798ac914b42c3e2742404ebd0a4d3fdf93513184a7f1bafbd1c7fcb0c mutants.jsonl f958c9a82d21457052dcb2a1addff20e438871c6d4e5295f8c5009556e0f0418 SAMPLE.txt 687feccc9080702d44e619fc2223e99f8b0fe9b6294be13c9b7231d326b96b06 informe.py 51743ab2885d72dfe07129f7ef1d1dcc9dd31e1285a039b105f4d367682d24d0 alcance.py 669d574dca551c2e34bed1eac49e33722f880d991cb346d1adb73ec01bcbd984 ../v6-0001-Add-PROGRESS_DEBUG-to-test-the-whole-sequence-of-.patch 03f944640dfd244bdaa6b7b70d3b76f57b4cdc97eecb29e472b4956e1b037949 ../v6-0002-Check-the-documented-progress-phases-against-the-.patch RESERVA.sha256 (sealed 2026-11-03T13:37:59Z): 2d1c0506c1ee981f8c4a4786764b3d84a317f47f7e171136354ad7dcf9775346 RESERVA.md 59748834d4b47327c78f9c61574371c75813e9741c82cf8d4dd34010c034d055 generar-reserva.py 3c9a7cc2050c72fda3f081465b3767ed592a5ca1118807eba8d587ff639723e6 reserva.jsonl 669d574dca551c2e34bed1eac49e33722f880d991cb346d1adb73ec01bcbd984 ../v6-0001-Add-PROGRESS_DEBUG-to-test-the-whole-sequence-of-.patch 03f944640dfd244bdaa6b7b70d3b76f57b4cdc97eecb29e472b4956e1b037949 ../v6-0002-Check-the-documented-progress-phases-against-the-.patch RONDA2.sha256 (sealed 2026-11-04T00:47:58Z): e90c96f1a35a95ef452f6aa9a28d7a750ff269c19f96d5bbfb33a3d09620573f RONDA2.md 83508fbf4ccd62b1dd1cc02017014bd349b143262824c4814058378d1ff44733 ronda2/v6-0001-Add-PROGRESS_DEBUG-to-test-the-whole-sequence-of-.patch 73691375e1483f631ca2254e42a5c7a728b7961d5d3071be603ae10469aa1087 ronda2/v6-0002-Check-the-documented-progress-phases-against-the-.patch 4dc42841c473a5d209cb077443ce06ed64a85b2eaf85ea3f9b1e2a29aa1ffcdd ../huecos/equivalentes.md SAMPLE.txt (the 150 ids judge A judged): m0001 m0018 m0027 m0030 m0031 m0036 m0041 m0042 m0048 m0049 m0056 m0061 m0063 m0065 m0069 m0076 m0088 m0089 m0091 m0092 m0107 m0111 m0112 m0115 m0117 m0118 m0121 m0123 m0126 m0129 m0133 m0137 m0138 m0141 m0145 m0148 m0154 m0165 m0166 m0167 m0169 m0170 m0171 m0174 m0178 m0179 m0180 m0181 m0182 m0183 m0184 m0186 m0189 m0194 m0199 m0201 m0202 m0210 m0213 m0216 m0219 m0224 m0229 m0233 m0237 m0246 m0249 m0253 m0254 m0261 m0263 m0267 m0269 m0273 m0275 m0276 m0285 m0291 m0292 m0297 m0303 m0304 m0319 m0321 m0324 m0325 m0329 m0332 m0333 m0335 m0345 m0348 m0351 m0354 m0359 m0363 m0366 m0371 m0374 m0377 m0382 m0385 m0386 m0401 m0408 m0415 m0420 m0421 m0422 m0423 m0428 m0438 m0439 m0441 m0446 m0450 m0454 m0458 m0464 m0465 m0466 m0468 m0469 m0470 m0478 m0479 m0483 m0491 m0492 m0495 m0497 m0502 m0504 m0507 m0508 m0514 m0516 m0521 m0527 m0530 m0534 m0536 m0537 m0542 m0543 m0551 m0557 m0563 m0565 m0570 ===== mutacion/PROTOCOL.md ===== # Mutation benchmark of progress reporting — protocol Declared and sealed (PROTOCOL.sha256) on 2026-11-02, before any mutant was judged. ## 1. Question If a bug is introduced in the progress reporting of a command, is it caught (a) by PostgreSQL's test suite as it is today, and (b) by test_progress, the framework of the v6 patches? ## 2. The bugs: mutants `generar.py` takes every call that reports progress in `src/backend` and `contrib` of master a054b77e40d + v6-0001 + v6-0002 (calls of `pgstat_progress_{start,end}_command`, `update_param`, `incr_param`, `parallel_incr_param`, `update_multi_param`; not their definitions in backend_progress.c, not comments). There are 187 such calls. Each gets every mutant its kind admits; one mutant changes one call: | op | change | applies to | |---|---|---| | DEL | the call becomes `((void) 0)` | every call | | PLUS1 | the value becomes `((value) + 1)` | update, incr | | ZERO | the value becomes `0` | update | | SWAP | the parameter becomes `((param) ^ 1)`, its neighbour | update, incr | | SHORT | the count becomes `((count) - 1)`: last parameter dropped | multi | That gives 572 mutants (`mutants.jsonl`, ids m0001-m0572). A ZERO of a value that is already a literal 0 would be equivalent by construction and is not generated; there is none in this tree. The operators model the bugs fixed in 2026 (a value off by one, a counter not reset, a report in the wrong parameter, a command that never ends) without being chosen per bug: they apply blindly to every call. ## 3. The judges Both run on one build: the v6 patches, `-DPROGRESS_DEBUG`, assertions on (`-Dcassert=true`, as developers and cfbot run), debugoptimized. The trace is off unless a test turns it on, so judge A's tests run as on any build. - **Judge A, PostgreSQL's tests today**: the whole `meson test` suite except test_progress (and the `setup` suite that installs it). Only 3 test files of the tree look at a pg_stat_progress_* view (001_stream_rep.pl, 048_vacuum_horizon_floor.pl, copy.sql); every other test counts too: a mutant that breaks anything is caught. - **Judge B, test_progress**: `meson test --suite test_progress` (001 and 002). Per mutant: apply it, rebuild (`ninja`), reinstall (`--suite setup`), run the judge, restore the file. Judge A takes about 3 minutes per mutant, so it judges a **random sample of 150** drawn before the run with `random.Random(20261103).sample(sorted(ids), 150)` (`SAMPLE.txt`). Judge B judges all 572. The headline compares both judges on the same 150. ## 4. Counting 1. **Caught**: the judge's run fails (rc != 0): a test fails, crashes or times out (A 1800 s, B 600 s per mutant). Crashes and assertion failures are counted as caught and reported apart. 2. **Stillborn**: the mutant does not compile; left out of every rate. 3. **Controls**: both judges run without a mutant twice before and once after. A test that fails in a control does not count as catching: a mutant caught only through such tests is counted as survived. If a control of judge B fails, the run is void. 4. **Survivors** are listed one by one in results.md, and each one of judge B's is classified by hand afterwards: not reached by its scenarios, not observable (equivalent in effect), or a real gap. The classification is added to results.md; it does not change the counts. 5. Rates are given with a 95% Wilson interval; results are also broken down by command, by op and by kind of call (`informe.py`). ## 5. What is not claimed - The mutants are plausible bugs, not a census of real ones; the retro of the ten fixes of 2026 (RETRO.md) is the evidence about real bugs. - test_progress was written knowing the code it checks. Mutation does not remove that; it removes the choice of which bugs to count. - One machine, Linux x86-64, gcc. ## 6. Before sealing The mechanics (correr.sh, aplicar.py) were rehearsed on 10 mutants in the sandbox after sealing; those runs are not counted: the run that counts regenerates and judges every mutant from scratch outside. Any change to the sealed files after this point is listed below with its reason. ## Changes after sealing All before the run that counts; none changes what is counted. - 2026-11-03, rehearsal in the sandbox (10 mutants for judge B, 1 for A): `correr.sh` took the judge and the workdir in the wrong order, read the wrong column for the failing tests, and flagged as crashes the backends that tests kill on purpose (signals 3 and 9); fixed (`correr.sh` is not sealed). - 2026-11-03, `informe.py` (sealed) also reads `results/reach.json`, written by the new `correr.sh cobertura` step (`alcance.py`): a gcov run of each judge's tests that tells which of the 187 calls each one executes. It is used for section 4.4 only, to split the survivors into "not executed" and "executed, change not caught", and to report how many calls each judge executes. The rehearsal's gcov run gave A 178 of 187, B 163 of 187 (in the sandbox, where the two tar-based suites fail). No scenario was added to test_progress: the framework is judged as sealed. ===== mutacion/generar.py ===== #!/usr/bin/env python3 """Generate the mutants of the progress-reporting call sites of a source tree. Every call of pgstat_progress_{start,end}_command, update_param, incr_param, parallel_incr_param and update_multi_param in src/backend and contrib (except backend_progress.c, which defines them) is a site. Each site gets the mutants its kind admits (see PROTOCOL.md): DEL the call becomes ((void) 0) every kind PLUS1 the value argument becomes ((value) + 1) update, incr ZERO the value argument becomes 0 update SWAP the parameter becomes ((param) ^ 1), its neighbour update, incr SHORT the count becomes ((count) - 1): last one dropped multi Output: one JSON object per line: id, file, line, func, op, offset, length, old (the exact call text), new. The ids are stable for a given tree. generar.py > mutants.jsonl """ import json import os import re import sys FUNCS = ("pgstat_progress_start_command", "pgstat_progress_end_command", "pgstat_progress_update_param", "pgstat_progress_incr_param", "pgstat_progress_parallel_incr_param", "pgstat_progress_update_multi_param") CALL = re.compile(r"\b(" + "|".join(FUNCS) + r")\s*\(") def blank_comments_and_strings(src): """Same length as src, with comments and literals blanked, so that the scanner neither matches a call inside a comment nor miscounts parens.""" out = list(src) i, n = 0, len(src) while i < n: c = src[i] if src.startswith("/*", i): j = src.find("*/", i + 2) j = n if j < 0 else j + 2 for k in range(i, j): if out[k] != "\n": out[k] = " " i = j elif src.startswith("//", i): j = src.find("\n", i) j = n if j < 0 else j for k in range(i, j): out[k] = " " i = j elif c in "\"'": j = i + 1 while j < n and src[j] != c: j += 2 if src[j] == "\\" else 1 for k in range(i + 1, min(j, n)): out[k] = " " i = j + 1 else: i += 1 return "".join(out) def split_args(text, start): """text[start] is just after '('; return (end, [(a, b) spans of args]).""" depth, i, args, a = 0, start, [], start while i < len(text): c = text[i] if c in "([{": depth += 1 elif c in ")]}": if depth == 0: args.append((a, i)) return i + 1, args depth -= 1 elif c == "," and depth == 0: args.append((a, i)) a = i + 1 i += 1 raise ValueError("unbalanced call") def sites(root): for top in ("src/backend", "contrib"): for d, _, files in os.walk(os.path.join(root, top)): for f in sorted(files): if not f.endswith(".c") or f == "backend_progress.c": continue p = os.path.join(d, f) src = open(p, encoding="utf-8", errors="surrogateescape").read() clean = blank_comments_and_strings(src) for m in CALL.finditer(clean): # a definition or prototype is not a call before = clean[:m.start()].rstrip() if before.endswith(("void", "extern")): continue end, args = split_args(clean, m.end()) yield os.path.relpath(p, root), src, m.group(1), m.start(), end, args def mutants(root): n = 0 for rel, src, func, s, e, args in sorted(sites(root), key=lambda x: (x[0], x[3])): old = src[s:e] line = src.count("\n", 0, s) + 1 a = [src[x:y].strip() for x, y in args] head = src[s:args[0][0]] # "func(" kind = func.replace("pgstat_progress_", "") ops = [("DEL", "((void) 0)")] if kind == "update_param": ops += [("PLUS1", f"{head}{a[0]}, (({a[1]}) + 1))"), ("ZERO", f"{head}{a[0]}, 0)"), ("SWAP", f"{head}(({a[0]}) ^ 1), {a[1]})")] elif kind in ("incr_param", "parallel_incr_param"): ops += [("PLUS1", f"{head}{a[0]}, (({a[1]}) + 1))"), ("SWAP", f"{head}(({a[0]}) ^ 1), {a[1]})")] elif kind == "update_multi_param": ops += [("SHORT", f"{head}(({a[0]}) - 1), {a[1]}, {a[2]})")] # an equivalent mutant by construction is not generated: it changes # nothing, so no test could catch it if kind == "update_param" and re.fullmatch(r"\(?\s*(\(\s*int64\s*\)\s*)?0[lL]?\s*\)?", a[1]): ops = [o for o in ops if o[0] != "ZERO"] for op, new in ops: n += 1 yield {"id": f"m{n:04d}", "file": rel, "line": line, "func": kind, "op": op, "offset": s, "length": e - s, "old": old, "new": new, "args": a} if __name__ == "__main__": for m in mutants(sys.argv[1]): print(json.dumps(m, ensure_ascii=False)) ===== mutacion/aplicar.py ===== #!/usr/bin/env python3 """Apply one mutant to a source tree, checking that the site is unchanged. aplicar.py """ import json import os import sys tree, path, mid = sys.argv[1:4] for line in open(path, encoding="utf-8"): m = json.loads(line) if m["id"] == mid: break else: sys.exit(f"no mutant {mid}") p = os.path.join(tree, m["file"]) src = open(p, encoding="utf-8", errors="surrogateescape").read() s, e = m["offset"], m["offset"] + m["length"] if src[s:e] != m["old"]: sys.exit(f"{mid}: {m['file']}:{m['line']} is not the text the mutant was made for") open(p, "w", encoding="utf-8", errors="surrogateescape").write(src[:s] + m["new"] + src[e:]) print(m["file"]) ===== mutacion/correr.sh ===== #!/usr/bin/env bash # Mutation benchmark of progress reporting: which of the mutants of the 187 # call sites does PostgreSQL's own test suite catch (judge A) and which does # test_progress catch (judge B)? The protocol is PROTOCOL.md, sealed in # PROTOCOL.sha256 before any mutant ran; this script refuses to run if the # protocol, the generator or the patches changed since. # # correr.sh setup two trees (A and B), built once # correr.sh control [n] n unmutated runs of both judges # correr.sh juez A|B [jobs] every mutant of the judge, resumable # correr.sh cobertura gcov: which sites each judge runs # correr.sh noche control 2, both judges in parallel, # control 1 again, coverage, the report # correr.sh informe results/*.jsonl -> RESULTADOS.md # correr.sh reserva [jobs] judge B on the held-out mutants # (RESERVA.md), sealed in RESERVA.sha256 # correr.sh ronda2 the second round (RONDA2.md): the # patches of ronda2/, sealed in # RONDA2.sha256, judged by test_progress # on the 572 and on the reserve # # Judge A runs every mutant of the declared sample (SAMPLE.txt); judge B # runs all of them. Both trees are the v6 patches with PROGRESS_DEBUG and # assertions; trace_progress stays off for judge A's tests, as on any build. set -uo pipefail H=$(cd "$(dirname "$0")" && pwd) # Round 1 judged the v6 of 2026-11-02 (never posted); its patches are kept in ronda1/. # PROTOCOL.sha256 and RESERVA.sha256 name them ../, where they were when sealed. V=$H/ronda1 seal_ok() { (cd "$H" && sed 's# \.\./v6-# ronda1/v6-#' "$1" | sha256sum -c --quiet -); } CMD=${1:?setup, control, juez, noche or informe}; W=${2:?workdir} BASE=a054b77e40d0579a9ee1218e2447d5af045d1083 REPO=${PG_REPO:-$HOME/Proyectos/postgresql} TA=${TIMEOUT_A:-1800}; TB=${TIMEOUT_B:-600} mkdir -p "$W/results" sealed() { seal_ok PROTOCOL.sha256 || { echo "PROTOCOL.md, the generator, the sample or the patches changed since sealed"; exit 2; } } ident() { export GIT_COMMITTER_NAME=x GIT_COMMITTER_EMAIL=x@x; } if [ "$CMD" = setup ]; then sealed; ident for j in A B; do S=$W/src-$j B=$W/build-$j rm -rf "$S" "$B" git clone -q --shared --no-checkout "$REPO" "$S" && git -C "$S" checkout -q --detach $BASE && git -C "$S" am -q "$V"/v6-0001-*.patch "$V"/v6-0002-*.patch || exit 2 meson setup "$B" "$S" --buildtype=debugoptimized -Dcassert=true -Dtap_tests=enabled \ -Dc_args=-DPROGRESS_DEBUG >"$W/setup-$j.log" 2>&1 || { echo "$j: setup failed"; exit 2; } ninja -C "$B" >"$W/build-$j.log" 2>&1 || { echo "$j: build failed"; exit 2; } meson test -C "$B" -q --suite setup --suite ecpg >"$W/prep-$j.log" 2>&1 || { echo "$j: tmp_install or ecpg failed"; exit 2; } echo "$j: built" done python3 "$H/generar.py" "$W/src-B" >"$W/mutants.jsonl" cmp -s "$W/mutants.jsonl" "$H/mutants.jsonl" || { echo "the tree gives other mutants than the sealed mutants.jsonl"; exit 2; } echo "mutants: $(wc -l <"$W/mutants.jsonl"), sample for A: $(wc -l <"$H/SAMPLE.txt")" exit 0 fi # one judge on the tree as it is; prints "rc failing-tests trap" judge() { local j=$1 B=$W/build-$1 jobs=${2:-8} t log=$W/last-$1.log rc rm -rf "$B/testrun" if [ "$j" = A ]; then timeout "$TA" meson test -C "$B" -q --no-rebuild --num-processes "$jobs" \ --no-suite setup --no-suite test_progress >"$log" 2>&1 else timeout "$TB" meson test -C "$B" -q --no-rebuild --num-processes 2 \ --suite test_progress >"$log" 2>&1 fi rc=$? t=$(grep -E '^ *[0-9]+/[0-9]+ .* (FAIL|ERROR|TIMEOUT) ' "$log" | grep -oE 'postgresql:[^ ]+' | sed 's/^postgresql://' | sort -u | paste -sd, -) local trap=0 # an assertion failure, an abort or a segfault; tests kill backends on # purpose with other signals grep -rqsE 'TRAP: |terminated by signal (6|11)[^0-9]' "$B/testrun" --include=*.log && trap=1 echo "$rc ${t:--} $trap" } if [ "$CMD" = control ]; then sealed n=${3:-1} for i in $(seq 1 "$n"); do for j in A B; do for f in $(git -C "$W/src-$j" diff --name-only); do git -C "$W/src-$j" checkout -q -- "$f"; done ninja -C "$W/build-$j" >/dev/null 2>&1 && meson test -C "$W/build-$j" -q --suite setup >/dev/null 2>&1 s=$(date +%s); read -r rc t trap < <(judge $j 12) printf '{"control": "%s", "at": "%s", "rc": %s, "failed": "%s", "trap": %s, "secs": %s}\n' \ "$j" "$(date -Is)" "$rc" "$t" "$trap" "$(( $(date +%s) - s ))" | tee -a "$W/results/control.jsonl" done done exit 0 fi if [ "$CMD" = reserva ]; then seal_ok RESERVA.sha256 || { echo "RESERVA.md, the reserve or the patches changed since sealed"; exit 2; } MUTANTS=$H/reserva.jsonl TAG=-reserva exec "$0" juez "$W" B "${3:-12}" fi if [ "$CMD" = juez ]; then sealed M=${MUTANTS:-$W/mutants.jsonl} j=${3:?A or B}; jobs=${4:-12}; S=$W/src-$j B=$W/build-$j R=$W/results/judge-$j${TAG:-}.jsonl touch "$R" if [ "$j" = A ]; then ids=$(cat "$H/SAMPLE.txt"); else ids=$(python3 -c " import json,sys; print('\n'.join(json.loads(l)['id'] for l in open(sys.argv[1])))" "$M"); fi # only to rehearse the mechanics on a few mutants [ -n "${ONLY_IDS:-}" ] && ids=$(echo "$ONLY_IDS" | tr ' ' '\n') total=$(echo "$ids" | wc -l); k=0 for id in $ids; do k=$((k+1)) grep -q "\"id\": \"$id\"" "$R" && continue # the tree back to the patches, then the mutant for f in $(git -C "$S" diff --name-only); do git -C "$S" checkout -q -- "$f"; done f=$(python3 "$H/aplicar.py" "$S" "$M" "$id") || { echo "$id: apply failed"; exit 2; } s=$(date +%s) if ! ninja -C "$B" >"$W/last-build-$j.log" 2>&1; then res='"stillborn"'; rc=-1; t=-; trap=0 else meson test -C "$B" -q --suite setup >/dev/null 2>&1 read -r rc t trap < <(judge "$j" "$jobs") [ "$rc" = 0 ] && res='"survived"' || res='"killed"' fi git -C "$S" checkout -q -- "$f" printf '{"id": "%s", "judge": "%s", "result": %s, "rc": %s, "failed": "%s", "trap": %s, "secs": %s}\n' \ "$id" "$j" "$res" "$rc" "$t" "$trap" "$(( $(date +%s) - s ))" >>"$R" echo "[$j $k/$total] $id $res $t" done # leave the tree and the build as the patches ninja -C "$B" >/dev/null 2>&1 echo "judge $j: done" exit 0 fi if [ "$CMD" = cobertura ]; then # which of the 187 sites each judge's tests execute (gcov), to classify the # survivors (PROTOCOL.md, changes after sealing) sealed; ident S=$W/src-C B=$W/build-C if [ ! -f "$B/build.ninja" ]; then rm -rf "$S" "$B" git clone -q --shared --no-checkout "$REPO" "$S" && git -C "$S" checkout -q --detach $BASE && git -C "$S" am -q "$V"/v6-0001-*.patch "$V"/v6-0002-*.patch || exit 2 meson setup "$B" "$S" --buildtype=debugoptimized -Dcassert=true -Dtap_tests=enabled \ -Db_coverage=true -Dc_args=-DPROGRESS_DEBUG >"$W/setup-C.log" 2>&1 || { echo "C: setup failed"; exit 2; } ninja -C "$B" >"$W/build-C.log" 2>&1 || { echo "C: build failed"; exit 2; } meson test -C "$B" -q --suite setup --suite ecpg >"$W/prep-C.log" 2>&1 || { echo "C: prep failed"; exit 2; } fi rm -f "$W/results/reach.json" for j in B A; do find "$B" -name '*.gcda' -delete rm -rf "$B/testrun" if [ $j = A ]; then timeout 3600 meson test -C "$B" -q --no-rebuild --num-processes 12 \ --no-suite setup --no-suite test_progress >"$W/cov-$j.log" 2>&1 else timeout 1200 meson test -C "$B" -q --no-rebuild --num-processes 2 \ --suite test_progress >"$W/cov-$j.log" 2>&1 fi echo "coverage run $j: rc=$?" python3 "$H/alcance.py" "$B" "$W/mutants.jsonl" "$j" "$W/results/reach.json" done exit 0 fi if [ "$CMD" = ronda2 ]; then sealed (cd "$H" && sha256sum -c --quiet RONDA2.sha256) || { echo "RONDA2.md or the patches of ronda2/ changed since sealed"; exit 2; } ident S=$W/src-R2 B=$W/build-R2 if [ ! -f "$B/build.ninja" ]; then rm -rf "$S" "$B" git clone -q --shared --no-checkout "$REPO" "$S" && git -C "$S" checkout -q --detach $BASE && git -C "$S" am -q "$H"/ronda2/v6-0001-*.patch "$H"/ronda2/v6-0002-*.patch || exit 2 meson setup "$B" "$S" --buildtype=debugoptimized -Dcassert=true -Dtap_tests=enabled \ -Dc_args=-DPROGRESS_DEBUG >"$W/setup-R2.log" 2>&1 || { echo "R2: setup failed"; exit 2; } ninja -C "$B" >"$W/build-R2.log" 2>&1 || { echo "R2: build failed"; exit 2; } meson test -C "$B" -q --suite setup >"$W/prep-R2.log" 2>&1 || { echo "R2: tmp_install failed"; exit 2; } fi # the new patches change no call site: the same mutants, at the same offsets python3 "$H/generar.py" "$S" | cmp -s - "$H/mutants.jsonl" || { echo "R2 gives other mutants than the sealed mutants.jsonl"; exit 2; } python3 "$H/generar-reserva.py" "$S" | cmp -s - "$H/reserva.jsonl" || { echo "R2 gives another reserve than the sealed reserva.jsonl"; exit 2; } # unmutated, the new test must pass read -r rc t trap < <(judge R2 2) printf '{"judge": "R2", "at": "%s", "rc": %s, "failed": "%s", "trap": %s}\n' \ "$(date -Is)" "$rc" "$t" "$trap" | tee -a "$W/results/control.jsonl" [ "$rc" = 0 ] || { echo "R2 fails without a mutant"; exit 2; } "$0" juez "$W" R2 2 || exit 2 MUTANTS=$H/reserva.jsonl TAG=-reserva "$0" juez "$W" R2 2 || exit 2 echo "round 2: done" exit 0 fi if [ "$CMD" = noche ]; then sealed "$0" control "$W" 2 ( "$0" juez "$W" B 2 >"$W/judge-B.out" 2>&1 ) & pb=$! ( "$0" juez "$W" A 12 >"$W/judge-A.out" 2>&1 ) & pa=$! wait $pb; echo "judge B finished: $(tail -1 "$W/judge-B.out")" wait $pa; echo "judge A finished: $(tail -1 "$W/judge-A.out")" "$0" control "$W" 1 "$0" cobertura "$W" "$0" informe "$W" exit 0 fi if [ "$CMD" = informe ]; then python3 "$H/informe.py" "$W" "$H" exit $? fi echo "unknown command $CMD"; exit 2 ===== mutacion/alcance.py ===== #!/usr/bin/env python3 """Which progress call sites a test run executed, from gcov data. alcance.py Reads every .gcda under the build dir (gcov --json-format --stdout) and marks a site reached when any line of its call ran. Appends {judge: {site: bool}} to out.json (a site is "file:line" of mutants.jsonl).""" import glob import json import os import subprocess import sys B, muts, judge, out = sys.argv[1:5] sites = {} for l in open(muts): m = json.loads(l) k = f"{m['file']}:{m['line']}" sites[k] = (m["file"], m["line"], m["line"] + m["old"].count("\n")) files = {f for f, _, _ in sites.values()} hits = {} # (relative source, line) -> count for gcda in glob.glob(os.path.join(B, "**", "*.gcda"), recursive=True): base = os.path.basename(gcda) if not any(os.path.basename(f) in base for f in files): continue r = subprocess.run(["gcov", "--json-format", "--stdout", gcda], cwd=os.path.dirname(gcda), capture_output=True, text=True) for doc in r.stdout.splitlines(): if not doc.strip().startswith("{"): continue for f in json.loads(doc).get("files", []): src = f["file"] rel = next((x for x in files if src.endswith(x)), None) if rel is None: continue for ln in f["lines"]: key = (rel, ln["line_number"]) hits[key] = max(hits.get(key, 0), ln["count"]) res = {} for k, (f, a, b) in sites.items(): known = [hits[(f, i)] for i in range(a, b + 1) if (f, i) in hits] res[k] = None if not known else any(c > 0 for c in known) data = json.load(open(out)) if os.path.exists(out) else {} data[judge] = res json.dump(data, open(out, "w"), indent=1, sort_keys=True) v = list(res.values()) print(f"{judge}: {sum(1 for x in v if x)} of {len(v)} sites reached, " f"{sum(1 for x in v if x is None)} without line data") ===== mutacion/informe.py ===== #!/usr/bin/env python3 """Report of the mutation benchmark (PROTOCOL.md section 5). informe.py Writes /results.md and /measurements-mutation.txt.""" import collections import json import math import os import sys W, H = sys.argv[1], sys.argv[2] def jl(p): return [json.loads(l) for l in open(p)] if os.path.exists(p) else [] muts = {m["id"]: m for m in jl(os.path.join(W, "mutants.jsonl"))} sample = [l.strip() for l in open(os.path.join(H, "SAMPLE.txt")) if l.strip()] A = {r["id"]: r for r in jl(os.path.join(W, "results", "judge-A.jsonl"))} B = {r["id"]: r for r in jl(os.path.join(W, "results", "judge-B.jsonl"))} ctl = jl(os.path.join(W, "results", "control.jsonl")) rp = os.path.join(W, "results", "reach.json") reach = json.load(open(rp)) if os.path.exists(rp) else {} def reached(j, m): """Did judge j's tests execute the call of mutant m (gcov)? None: unknown.""" return reach.get(j, {}).get(f"{m['file']}:{m['line']}") COMMAND = { # file -> command, for the breakdown "vacuumlazy.c": "VACUUM", "vacuumparallel.c": "VACUUM", "vacuum.c": "VACUUM", "analyze.c": "ANALYZE", "extended_stats.c": "ANALYZE", "indexcmds.c": "CREATE INDEX", "index.c": "CREATE INDEX", "nbtsort.c": "CREATE INDEX", "nbtree.c": "CREATE INDEX", "gininsert.c": "CREATE INDEX", "hash.c": "CREATE INDEX", "hashsort.c": "CREATE INDEX", "lmgr.c": "CREATE INDEX", "heapam_handler.c": "heap AM", "repack.c": "REPACK", "copyfrom.c": "COPY", "copyto.c": "COPY", "copyfromparse.c": "COPY", "basebackup_progress.c": "BASE_BACKUP", "datachecksum_state.c": "DATACHECKSUMS", "xact.c": "(end of xact)", "parallel.c": "(parallel)", } def cmd(m): return COMMAND.get(os.path.basename(m["file"]), os.path.basename(m["file"])) def wilson(k, n, z=1.96): if n == 0: return (0.0, 0.0) p = k / n d = 1 + z * z / n c = p + z * z / (2 * n) r = z * math.sqrt(p * (1 - p) / n + z * z / (4 * n * n)) return ((c - r) / d, (c + r) / d) def pct(k, n): lo, hi = wilson(k, n) return f"{k}/{n} = {100 * k / n:.1f}% [95% CI {100 * lo:.1f}-{100 * hi:.1f}%]" if n else "0/0" # tests that failed with no mutant: a kill by A that only fails those is not # counted (PROTOCOL.md 4.3) flaky = set() for c in ctl: if c["failed"] != "-": flaky |= set(c["failed"].split(",")) def killed(r): if r is None or r["result"] == "stillborn": return None if r["result"] != "killed": return False if r["failed"] == "-": return True # rc != 0 without a failing test: timeout or crash of meson return bool(set(r["failed"].split(",")) - flaky) still = sorted(i for i in muts if (B.get(i) or A.get(i) or {}).get("result") == "stillborn") live_b = [i for i in muts if i in B and killed(B[i]) is not None] paired = [i for i in sample if i in A and i in B and killed(A[i]) is not None and killed(B[i]) is not None] out = [] w = out.append w("# Mutation benchmark of progress reporting: results\n") w(f"Protocol: PROTOCOL.md, sealed before any mutant ran (PROTOCOL.sha256). Mutants: " f"{len(muts)} over {len({(m['file'], m['offset']) for m in muts.values()})} call sites; " f"stillborn (do not compile): {len(still)}.") w(f"Controls (no mutant): {len(ctl)} runs, " f"{sum(1 for c in ctl if c['rc'] == 0)} passed; tests failing without a mutant: " f"{', '.join(sorted(flaky)) or 'none'}.\n") if len(paired) < len(sample) or len(live_b) < len(muts) - len(still): w(f"**INCOMPLETE**: judge A {len(paired)}/{len(sample)} of the sample, " f"judge B {len(live_b)}/{len(muts) - len(still)}.\n") kA = [i for i in paired if killed(A[i])] kB = [i for i in paired if killed(B[i])] w("## The headline: the same mutants, both judges\n") w(f"Sample of {len(sample)} mutants drawn before the run (seed in PROTOCOL.md); {len(paired)} judged by both.\n") w(f"- PostgreSQL's test suite today (judge A): **{pct(len(kA), len(paired))}** caught") w(f"- test_progress (judge B): **{pct(len(kB), len(paired))}** caught") both = set(kA) & set(kB) w(f"- caught by both {len(both)}, only by A {len(set(kA) - both)}, only by B " f"{len(set(kB) - both)}, by neither {len(paired) - len(set(kA) | set(kB))}\n") nsites = len({(m['file'], m['offset']) for m in muts.values()}) if reach: rA = sum(1 for v in reach.get("A", {}).values() if v) rB = sum(1 for v in reach.get("B", {}).values() if v) w("## Executed versus checked\n") w(f"Of the {nsites} call sites, PostgreSQL's tests execute **{rA}** and test_progress " f"**{rB}** (gcov, one run of each). Restricted to the paired mutants whose call " "today's tests execute:") ex = [i for i in paired if reached("A", muts[i])] w(f"- judge A catches {pct(sum(1 for i in ex if killed(A[i])), len(ex))}") w(f"- judge B catches {pct(sum(1 for i in ex if killed(B[i])), len(ex))}\n") w("## test_progress on every mutant\n") kb_all = [i for i in live_b if killed(B[i])] w(f"- all mutants: {pct(len(kb_all), len(live_b))}") crash = [i for i in kb_all if B[i]["trap"]] w(f"- of those caught, by a crash or assertion failure rather than a check: {len(crash)}\n") def table(key, title): w(f"### By {title}\n") w("| | mutants | caught by B | B % | sample | caught by A | caught by B |") w("|---|---|---|---|---|---|---|") groups = collections.defaultdict(list) for i in live_b: groups[key(muts[i])].append(i) for g in sorted(groups, key=lambda g: -len(groups[g])): ids = groups[g] kb = sum(1 for i in ids if killed(B[i])) ps = [i for i in paired if key(muts[i]) == g] w(f"| {g} | {len(ids)} | {kb} | {100 * kb / len(ids):.0f}% | {len(ps)} | " f"{sum(1 for i in ps if killed(A[i]))} | {sum(1 for i in ps if killed(B[i]))} |") w("") table(cmd, "command") table(lambda m: m["op"], "kind of mutant") table(lambda m: m["func"], "reporting call") w("## What test_progress does not catch\n") surv = [i for i in sorted(live_b) if not killed(B[i])] nr = [i for i in surv if reached("B", muts[i]) is False] w(f"{len(surv)} survivors: {len(nr)} at a call that test_progress never executes " f"(gcov), {len(surv) - len(nr)} at a call it executes (PROTOCOL.md 4.4).\n") for title, ids in (("Not executed by test_progress", nr), ("Executed, but the change was not caught", [i for i in surv if i not in nr])): w(f"### {title} ({len(ids)})\n") for i in ids: m = muts[i] a = "" if i not in A else (" — A: caught" if killed(A[i]) else " — A: survived") w(f"- {i} {m['file']}:{m['line']} {m['op']} `{' '.join(m['old'].split())[:70]}`{a}") w("") w("") w("## Caught by A but not by B\n") for i in sorted(set(kA) - set(kB)): m = muts[i] w(f"- {i} {m['file']}:{m['line']} {m['op']}: A failed {A[i]['failed']}") w("") secsA = [A[i]["secs"] for i in A] secsB = [B[i]["secs"] for i in B] if secsA and secsB: w(f"Cost per mutant: judge A median {sorted(secsA)[len(secsA) // 2]} s, judge B " f"{sorted(secsB)[len(secsB) // 2]} s (rebuild + install + tests).") open(os.path.join(H, "results.md"), "w").write("\n".join(out) + "\n") # the attachment: ASCII, <= 80 columns t = [] t.append("Mutation benchmark of progress reporting") t.append("========================================") t.append("") t.append(f"Mutants: {len(muts)}, generated from the {len({(m['file'], m['offset']) for m in muts.values()})}") t.append("calls that report progress in src/backend and contrib (generar.py):") t.append("each call deleted; each value +1 or 0; each parameter swapped with its") t.append("neighbour; each multi-parameter update one parameter short.") t.append(f"Stillborn (do not compile): {len(still)}.") t.append("") t.append(f"Sample judged by both, drawn before the run: {len(paired)}") t.append(f" PostgreSQL's tests today: {pct(len(kA), len(paired))}") t.append(f" test_progress: {pct(len(kB), len(paired))}") t.append(f" only test_progress {len(set(kB) - both)}, only today's tests {len(set(kA) - both)}, " f"neither {len(paired) - len(set(kA) | set(kB))}") t.append("") t.append(f"test_progress on all {len(live_b)}: {pct(len(kb_all), len(live_b))}") if reach: t.append("") t.append(f"Call sites executed (gcov): today's tests {rA} of {nsites},") t.append(f"test_progress {rB} of {nsites}.") open(os.path.join(H, "measurements-mutation.txt"), "w").write("\n".join(t) + "\n") print("\n".join(t)) ===== mutacion/review.md ===== # Mutation benchmark: review of the results Raw results: `corrida/` (control.jsonl, judge-A.jsonl, judge-B.jsonl, reach.json), from the run outside the sandbox on 2026-11-03 02:15-06:05, after the protocol was sealed (PROTOCOL.sealed-at 00:08). Counts by `informe.py`: `results.md`. ## 1. The run is clean - 572 mutants, 0 stillborn; judge A judged its 150, judge B all 572. - 10 control runs without a mutant (2 before, 1 after, plus 2 of an earlier start): all passed, no test failed. - No mutant was caught through a crash or an assertion failure: every catch by test_progress is a check that names what is wrong. ## 2. One catch of judge A is an infrastructure failure m0183 (nbtsort.c:391, ZERO: PROGRESS_CREATEIDX_SUBPHASE reported as 0 instead of INDEXBUILD_TABLESCAN) made 216 TAP suites fail in 73 s (rc 2), from amcheck to ssl and subscription. That value is read by nothing but the view. Judged again alone (`corrida/rejuicio-m0183.log`), today's tests pass with it. Per the protocol (4.1) it stays counted as caught; without it judge A catches **9/150 = 6.0% [95% CI 3.2-11.0%]**. The headline below gives the protocol's number. ## 3. What each judge catches, on the same 150 mutants - PostgreSQL's tests today: 10/150 = 6.7% [3.7-11.8%] (9/150 without m0183). - test_progress: 84/150 = 56.0% [48.0-63.7%]. - Only test_progress: 75. Only today's tests: 1 (m0183 above, so none in fact). Neither: 65. - test_progress on all 572: 342 = 59.8% [55.7-63.7%]. What today's tests do catch: the base backup command never started, or a counter of it never set (m0224, m0233: recovery/001_stream_rep.pl:665 reads pg_stat_progress_basebackup), and COPY's tuple and byte counters (7 mutants: 6 through regress's copy.sql, which reads pg_stat_progress_copy, 1 through test_copy_callbacks). Outside those two views, nothing of what the suite executes is looked at. ## 4. Executed is not checked gcov over one run of each judge: today's tests execute 179 of the 187 calls, test_progress 163. On the 139 sampled mutants whose call today's tests execute, they catch 10 (7.2%); test_progress catches 83 (59.7%). The progress code is exercised by the suite; its numbers are not looked at. ## 5. What test_progress misses (230 of 572) - 79 at a call test_progress never executes: hash index builds, lock waits of concurrent index builds (lmgr.c), the paths of REPACK (CONCURRENTLY) that apply concurrent changes, COPY paths, the end of a command at subtransaction abort (xact.c:5328) and a parallel worker's report to its leader (parallel.c:1244). Scenarios missing from 001. - 151 at a call it executes, by parameter: - CREATE INDEX subphase (34): its values are defined by each index AM, so 001 lists it in %UNCHECKED. - The phase of CREATE INDEX (19), REPACK (10), data checksums (6), VACUUM (3): a mutant that reports another defined phase, or skips one, passes the `phase` rule (which only checks that the value is defined); it is caught only where 001 checks the succession of phases of that variant. - Lock-wait counters (8, %UNCHECKED: no concurrency in 001) and OIDs reported once (index, access method, current index of VACUUM; 10). - The rest (about 60) spread over counters whose last value is right even when one intermediate report is lost or off by one, and multi-parameter updates whose dropped parameter is written again later. So the misses are concentrated in three places that are known and listed in the test (%UNCHECKED, the per-variant phase successions, the missing scenarios). They are the next work on the framework. None of it was tuned before the run: the framework was judged as sealed, and closing these gaps should be judged again on all 572. ## 6. What this does not claim The mutants are plausible bugs, not real ones (RETRO.md has the real ones: 9 of 10). One machine, Linux x86-64, gcc 16.2.1. ===== mutacion/results.md ===== # Mutation benchmark of progress reporting: results Protocol: PROTOCOL.md, sealed before any mutant ran (PROTOCOL.sha256). Mutants: 572 over 187 call sites; stillborn (do not compile): 0. Controls (no mutant): 10 runs, 10 passed; tests failing without a mutant: none. ## The headline: the same mutants, both judges Sample of 150 mutants drawn before the run (seed in PROTOCOL.md); 150 judged by both. - PostgreSQL's test suite today (judge A): **10/150 = 6.7% [95% CI 3.7-11.8%]** caught - test_progress (judge B): **84/150 = 56.0% [95% CI 48.0-63.7%]** caught - caught by both 9, only by A 1, only by B 75, by neither 65 ## Executed versus checked Of the 187 call sites, PostgreSQL's tests execute **179** and test_progress **163** (gcov, one run of each). Restricted to the paired mutants whose call today's tests execute: - judge A catches 10/139 = 7.2% [95% CI 4.0-12.7%] - judge B catches 83/139 = 59.7% [95% CI 51.4-67.5%] ## test_progress on every mutant - all mutants: 342/572 = 59.8% [95% CI 55.7-63.7%] - of those caught, by a crash or assertion failure rather than a check: 0 ### By command | | mutants | caught by B | B % | sample | caught by A | caught by B | |---|---|---|---|---|---|---| | CREATE INDEX | 194 | 69 | 36% | 50 | 1 | 17 | | VACUUM | 101 | 58 | 57% | 31 | 0 | 15 | | heap AM | 62 | 52 | 84% | 16 | 0 | 15 | | ANALYZE | 46 | 41 | 89% | 7 | 0 | 7 | | COPY | 44 | 32 | 73% | 13 | 7 | 9 | | REPACK | 44 | 25 | 57% | 13 | 0 | 4 | | DATACHECKSUMS | 44 | 36 | 82% | 11 | 0 | 9 | | BASE_BACKUP | 32 | 27 | 84% | 8 | 2 | 8 | | (parallel) | 3 | 1 | 33% | 1 | 0 | 0 | | (end of xact) | 2 | 1 | 50% | 0 | 0 | 0 | ### By kind of mutant | | mutants | caught by B | B % | sample | caught by A | caught by B | |---|---|---|---|---|---|---| | DEL | 187 | 114 | 61% | 48 | 3 | 26 | | PLUS1 | 121 | 72 | 60% | 28 | 0 | 15 | | SWAP | 121 | 79 | 65% | 33 | 4 | 19 | | ZERO | 108 | 61 | 56% | 30 | 3 | 19 | | SHORT | 35 | 16 | 46% | 11 | 0 | 5 | ### By reporting call | | mutants | caught by B | B % | sample | caught by A | caught by B | |---|---|---|---|---|---|---| | update_param | 432 | 261 | 60% | 106 | 8 | 65 | | update_multi_param | 70 | 43 | 61% | 22 | 1 | 11 | | incr_param | 30 | 11 | 37% | 11 | 0 | 2 | | end_command | 17 | 14 | 82% | 3 | 0 | 2 | | start_command | 14 | 10 | 71% | 6 | 1 | 3 | | parallel_incr_param | 9 | 3 | 33% | 2 | 0 | 1 | ## What test_progress does not catch 230 survivors: 79 at a call that test_progress never executes (gcov), 151 at a call it executes (PROTOCOL.md 4.4). ### Not executed by test_progress (79) - m0023 src/backend/access/hash/hash.c:196 DEL `pgstat_progress_update_param(PROGRESS_CREATEIDX_TUPLES_TOTAL, buildsta` - m0024 src/backend/access/hash/hash.c:196 PLUS1 `pgstat_progress_update_param(PROGRESS_CREATEIDX_TUPLES_TOTAL, buildsta` - m0025 src/backend/access/hash/hash.c:196 ZERO `pgstat_progress_update_param(PROGRESS_CREATEIDX_TUPLES_TOTAL, buildsta` - m0026 src/backend/access/hash/hash.c:196 SWAP `pgstat_progress_update_param(PROGRESS_CREATEIDX_TUPLES_TOTAL, buildsta` - m0027 src/backend/access/hash/hashsort.c:154 DEL `pgstat_progress_update_param(PROGRESS_CREATEIDX_TUPLES_DONE, ++tups_do` — A: survived - m0028 src/backend/access/hash/hashsort.c:154 PLUS1 `pgstat_progress_update_param(PROGRESS_CREATEIDX_TUPLES_DONE, ++tups_do` - m0029 src/backend/access/hash/hashsort.c:154 ZERO `pgstat_progress_update_param(PROGRESS_CREATEIDX_TUPLES_DONE, ++tups_do` - m0030 src/backend/access/hash/hashsort.c:154 SWAP `pgstat_progress_update_param(PROGRESS_CREATEIDX_TUPLES_DONE, ++tups_do` — A: survived - m0094 src/backend/access/heap/vacuumlazy.c:657 DEL `pgstat_progress_update_param(PROGRESS_VACUUM_STARTED_BY, params->is_wr` - m0095 src/backend/access/heap/vacuumlazy.c:657 PLUS1 `pgstat_progress_update_param(PROGRESS_VACUUM_STARTED_BY, params->is_wr` - m0096 src/backend/access/heap/vacuumlazy.c:657 ZERO `pgstat_progress_update_param(PROGRESS_VACUUM_STARTED_BY, params->is_wr` - m0097 src/backend/access/heap/vacuumlazy.c:657 SWAP `pgstat_progress_update_param(PROGRESS_VACUUM_STARTED_BY, params->is_wr` - m0145 src/backend/access/heap/vacuumlazy.c:2943 DEL `pgstat_progress_update_multi_param(3, progress_index, progress_val)` — A: survived - m0146 src/backend/access/heap/vacuumlazy.c:2943 SHORT `pgstat_progress_update_multi_param(3, progress_index, progress_val)` - m0191 src/backend/access/nbtree/nbtsort.c:560 DEL `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_BTR` - m0192 src/backend/access/nbtree/nbtsort.c:560 PLUS1 `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_BTR` - m0193 src/backend/access/nbtree/nbtsort.c:560 ZERO `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_BTR` - m0194 src/backend/access/nbtree/nbtsort.c:560 SWAP `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_BTR` — A: survived - m0199 src/backend/access/nbtree/nbtsort.c:1262 DEL `pgstat_progress_update_param(PROGRESS_CREATEIDX_TUPLES_DONE, ++tuples_` — A: survived - m0200 src/backend/access/nbtree/nbtsort.c:1262 PLUS1 `pgstat_progress_update_param(PROGRESS_CREATEIDX_TUPLES_DONE, ++tuples_` - m0201 src/backend/access/nbtree/nbtsort.c:1262 ZERO `pgstat_progress_update_param(PROGRESS_CREATEIDX_TUPLES_DONE, ++tuples_` — A: survived - m0202 src/backend/access/nbtree/nbtsort.c:1262 SWAP `pgstat_progress_update_param(PROGRESS_CREATEIDX_TUPLES_DONE, ++tuples_` — A: survived - m0219 src/backend/access/transam/parallel.c:1244 DEL `pgstat_progress_incr_param(index, incr)` — A: survived - m0221 src/backend/access/transam/parallel.c:1244 SWAP `pgstat_progress_incr_param(index, incr)` - m0223 src/backend/access/transam/xact.c:5328 DEL `pgstat_progress_end_command()` - m0279 src/backend/commands/analyze.c:204 DEL `pgstat_progress_update_param(PROGRESS_ANALYZE_STARTED_BY, PROGRESS_ANA` - m0280 src/backend/commands/analyze.c:204 PLUS1 `pgstat_progress_update_param(PROGRESS_ANALYZE_STARTED_BY, PROGRESS_ANA` - m0281 src/backend/commands/analyze.c:204 ZERO `pgstat_progress_update_param(PROGRESS_ANALYZE_STARTED_BY, PROGRESS_ANA` - m0282 src/backend/commands/analyze.c:204 SWAP `pgstat_progress_update_param(PROGRESS_ANALYZE_STARTED_BY, PROGRESS_ANA` - m0318 src/backend/commands/copyfrom.c:525 DEL `pgstat_progress_update_param(PROGRESS_COPY_TUPLES_PROCESSED, *processe` - m0319 src/backend/commands/copyfrom.c:525 PLUS1 `pgstat_progress_update_param(PROGRESS_COPY_TUPLES_PROCESSED, *processe` — A: survived - m0320 src/backend/commands/copyfrom.c:525 ZERO `pgstat_progress_update_param(PROGRESS_COPY_TUPLES_PROCESSED, *processe` - m0321 src/backend/commands/copyfrom.c:525 SWAP `pgstat_progress_update_param(PROGRESS_COPY_TUPLES_PROCESSED, *processe` — A: survived - m0334 src/backend/commands/copyfrom.c:1453 DEL `pgstat_progress_update_param(PROGRESS_COPY_TUPLES_PROCESSED, ++process` - m0335 src/backend/commands/copyfrom.c:1453 PLUS1 `pgstat_progress_update_param(PROGRESS_COPY_TUPLES_PROCESSED, ++process` — A: survived - m0336 src/backend/commands/copyfrom.c:1453 ZERO `pgstat_progress_update_param(PROGRESS_COPY_TUPLES_PROCESSED, ++process` - m0337 src/backend/commands/copyfrom.c:1453 SWAP `pgstat_progress_update_param(PROGRESS_COPY_TUPLES_PROCESSED, ++process` - m0358 src/backend/commands/copyto.c:1738 DEL `pgstat_progress_update_param(PROGRESS_COPY_TUPLES_PROCESSED, ++myState` - m0359 src/backend/commands/copyto.c:1738 PLUS1 `pgstat_progress_update_param(PROGRESS_COPY_TUPLES_PROCESSED, ++myState` — A: survived - m0360 src/backend/commands/copyto.c:1738 ZERO `pgstat_progress_update_param(PROGRESS_COPY_TUPLES_PROCESSED, ++myState` - m0361 src/backend/commands/copyto.c:1738 SWAP `pgstat_progress_update_param(PROGRESS_COPY_TUPLES_PROCESSED, ++myState` - m0366 src/backend/commands/indexcmds.c:501 DEL `pgstat_progress_update_param(PROGRESS_WAITFOR_CURRENT_PID, holder->pid` — A: survived - m0367 src/backend/commands/indexcmds.c:501 PLUS1 `pgstat_progress_update_param(PROGRESS_WAITFOR_CURRENT_PID, holder->pid` - m0368 src/backend/commands/indexcmds.c:501 ZERO `pgstat_progress_update_param(PROGRESS_WAITFOR_CURRENT_PID, holder->pid` - m0369 src/backend/commands/indexcmds.c:501 SWAP `pgstat_progress_update_param(PROGRESS_WAITFOR_CURRENT_PID, holder->pid` - m0370 src/backend/commands/indexcmds.c:508 DEL `pgstat_progress_update_param(PROGRESS_WAITFOR_DONE, i + 1)` - m0371 src/backend/commands/indexcmds.c:508 PLUS1 `pgstat_progress_update_param(PROGRESS_WAITFOR_DONE, i + 1)` — A: survived - m0372 src/backend/commands/indexcmds.c:508 ZERO `pgstat_progress_update_param(PROGRESS_WAITFOR_DONE, i + 1)` - m0373 src/backend/commands/indexcmds.c:508 SWAP `pgstat_progress_update_param(PROGRESS_WAITFOR_DONE, i + 1)` - m0387 src/backend/commands/indexcmds.c:1308 DEL `pgstat_progress_end_command()` - m0392 src/backend/commands/indexcmds.c:1513 DEL `pgstat_progress_incr_param(PROGRESS_CREATEIDX_PARTITIONS_DONE, 1)` - m0393 src/backend/commands/indexcmds.c:1513 PLUS1 `pgstat_progress_incr_param(PROGRESS_CREATEIDX_PARTITIONS_DONE, 1)` - m0394 src/backend/commands/indexcmds.c:1513 SWAP `pgstat_progress_incr_param(PROGRESS_CREATEIDX_PARTITIONS_DONE, 1)` - m0396 src/backend/commands/indexcmds.c:1626 DEL `pgstat_progress_incr_param(PROGRESS_CREATEIDX_PARTITIONS_DONE, 1)` - m0397 src/backend/commands/indexcmds.c:1626 PLUS1 `pgstat_progress_incr_param(PROGRESS_CREATEIDX_PARTITIONS_DONE, 1)` - m0398 src/backend/commands/indexcmds.c:1626 SWAP `pgstat_progress_incr_param(PROGRESS_CREATEIDX_PARTITIONS_DONE, 1)` - m0464 src/backend/commands/repack.c:2902 DEL `pgstat_progress_incr_param(PROGRESS_REPACK_HEAP_TUPLES_INSERTED, 1)` — A: survived - m0465 src/backend/commands/repack.c:2902 PLUS1 `pgstat_progress_incr_param(PROGRESS_REPACK_HEAP_TUPLES_INSERTED, 1)` — A: survived - m0466 src/backend/commands/repack.c:2902 SWAP `pgstat_progress_incr_param(PROGRESS_REPACK_HEAP_TUPLES_INSERTED, 1)` — A: survived - m0467 src/backend/commands/repack.c:2948 DEL `pgstat_progress_incr_param(PROGRESS_REPACK_HEAP_TUPLES_UPDATED, 1)` - m0468 src/backend/commands/repack.c:2948 PLUS1 `pgstat_progress_incr_param(PROGRESS_REPACK_HEAP_TUPLES_UPDATED, 1)` — A: survived - m0469 src/backend/commands/repack.c:2948 SWAP `pgstat_progress_incr_param(PROGRESS_REPACK_HEAP_TUPLES_UPDATED, 1)` — A: survived - m0470 src/backend/commands/repack.c:2973 DEL `pgstat_progress_incr_param(PROGRESS_REPACK_HEAP_TUPLES_DELETED, 1)` — A: survived - m0471 src/backend/commands/repack.c:2973 PLUS1 `pgstat_progress_incr_param(PROGRESS_REPACK_HEAP_TUPLES_DELETED, 1)` - m0472 src/backend/commands/repack.c:2973 SWAP `pgstat_progress_incr_param(PROGRESS_REPACK_HEAP_TUPLES_DELETED, 1)` - m0488 src/backend/commands/vacuum.c:2597 DEL `pgstat_progress_parallel_incr_param(PROGRESS_VACUUM_DELAY_TIME, parall` - m0489 src/backend/commands/vacuum.c:2597 PLUS1 `pgstat_progress_parallel_incr_param(PROGRESS_VACUUM_DELAY_TIME, parall` - m0490 src/backend/commands/vacuum.c:2597 SWAP `pgstat_progress_parallel_incr_param(PROGRESS_VACUUM_DELAY_TIME, parall` - m0505 src/backend/commands/vacuumparallel.c:1401 DEL `pgstat_progress_parallel_incr_param(PROGRESS_VACUUM_DELAY_TIME, parall` - m0506 src/backend/commands/vacuumparallel.c:1401 PLUS1 `pgstat_progress_parallel_incr_param(PROGRESS_VACUUM_DELAY_TIME, parall` - m0507 src/backend/commands/vacuumparallel.c:1401 SWAP `pgstat_progress_parallel_incr_param(PROGRESS_VACUUM_DELAY_TIME, parall` — A: survived - m0563 src/backend/storage/lmgr/lmgr.c:956 DEL `pgstat_progress_update_param(PROGRESS_WAITFOR_CURRENT_PID, holder->pid` — A: survived - m0564 src/backend/storage/lmgr/lmgr.c:956 PLUS1 `pgstat_progress_update_param(PROGRESS_WAITFOR_CURRENT_PID, holder->pid` - m0565 src/backend/storage/lmgr/lmgr.c:956 ZERO `pgstat_progress_update_param(PROGRESS_WAITFOR_CURRENT_PID, holder->pid` — A: survived - m0566 src/backend/storage/lmgr/lmgr.c:956 SWAP `pgstat_progress_update_param(PROGRESS_WAITFOR_CURRENT_PID, holder->pid` - m0567 src/backend/storage/lmgr/lmgr.c:963 DEL `pgstat_progress_update_param(PROGRESS_WAITFOR_DONE, ++done)` - m0568 src/backend/storage/lmgr/lmgr.c:963 PLUS1 `pgstat_progress_update_param(PROGRESS_WAITFOR_DONE, ++done)` - m0569 src/backend/storage/lmgr/lmgr.c:963 ZERO `pgstat_progress_update_param(PROGRESS_WAITFOR_DONE, ++done)` - m0570 src/backend/storage/lmgr/lmgr.c:963 SWAP `pgstat_progress_update_param(PROGRESS_WAITFOR_DONE, ++done)` — A: survived ### Executed, but the change was not caught (151) - m0001 src/backend/access/gin/gininsert.c:687 DEL `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_GIN` — A: survived - m0002 src/backend/access/gin/gininsert.c:687 PLUS1 `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_GIN` - m0003 src/backend/access/gin/gininsert.c:687 ZERO `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_GIN` - m0004 src/backend/access/gin/gininsert.c:687 SWAP `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_GIN` - m0005 src/backend/access/gin/gininsert.c:1662 DEL `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_GIN` - m0006 src/backend/access/gin/gininsert.c:1662 PLUS1 `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_GIN` - m0007 src/backend/access/gin/gininsert.c:1662 ZERO `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_GIN` - m0008 src/backend/access/gin/gininsert.c:1662 SWAP `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_GIN` - m0010 src/backend/access/gin/gininsert.c:1693 SHORT `pgstat_progress_update_multi_param(4, progress_index, progress_vals)` - m0015 src/backend/access/gin/gininsert.c:1869 DEL `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_GIN` - m0016 src/backend/access/gin/gininsert.c:1869 PLUS1 `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_GIN` - m0017 src/backend/access/gin/gininsert.c:1869 ZERO `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_GIN` - m0018 src/backend/access/gin/gininsert.c:1869 SWAP `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_GIN` — A: survived - m0019 src/backend/access/gin/gininsert.c:1875 DEL `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_GIN` - m0020 src/backend/access/gin/gininsert.c:1875 PLUS1 `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_GIN` - m0021 src/backend/access/gin/gininsert.c:1875 ZERO `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_GIN` - m0022 src/backend/access/gin/gininsert.c:1875 SWAP `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_GIN` - m0043 src/backend/access/heap/heapam_handler.c:731 DEL `pgstat_progress_update_param(PROGRESS_REPACK_HEAP_BLKS_SCANNED, heapSc` - m0047 src/backend/access/heap/heapam_handler.c:747 DEL `pgstat_progress_update_param(PROGRESS_REPACK_HEAP_BLKS_SCANNED, (heapS` - m0049 src/backend/access/heap/heapam_handler.c:747 ZERO `pgstat_progress_update_param(PROGRESS_REPACK_HEAP_BLKS_SCANNED, (heapS` — A: survived - m0052 src/backend/access/heap/heapam_handler.c:859 PLUS1 `pgstat_progress_update_param(PROGRESS_REPACK_HEAP_TUPLES_SCANNED, *num` - m0073 src/backend/access/heap/heapam_handler.c:1302 DEL `pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_DONE, blocks_done)` - m0074 src/backend/access/heap/heapam_handler.c:1302 PLUS1 `pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_DONE, blocks_done)` - m0075 src/backend/access/heap/heapam_handler.c:1302 ZERO `pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_DONE, blocks_done)` - m0085 src/backend/access/heap/heapam_handler.c:1783 DEL `pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_DONE, hscan->rs_cblo` - m0086 src/backend/access/heap/heapam_handler.c:1783 PLUS1 `pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_DONE, hscan->rs_cblo` - m0087 src/backend/access/heap/heapam_handler.c:1783 ZERO `pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_DONE, hscan->rs_cblo` - m0113 src/backend/access/heap/vacuumlazy.c:1384 DEL `pgstat_progress_update_param(PROGRESS_VACUUM_PHASE, PROGRESS_VACUUM_PH` - m0114 src/backend/access/heap/vacuumlazy.c:1384 PLUS1 `pgstat_progress_update_param(PROGRESS_VACUUM_PHASE, PROGRESS_VACUUM_PH` - m0115 src/backend/access/heap/vacuumlazy.c:1384 ZERO `pgstat_progress_update_param(PROGRESS_VACUUM_PHASE, PROGRESS_VACUUM_PH` — A: survived - m0117 src/backend/access/heap/vacuumlazy.c:1417 DEL `pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_SCANNED, blkno)` — A: survived - m0118 src/backend/access/heap/vacuumlazy.c:1417 PLUS1 `pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_SCANNED, blkno)` — A: survived - m0119 src/backend/access/heap/vacuumlazy.c:1417 ZERO `pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_SCANNED, blkno)` - m0130 src/backend/access/heap/vacuumlazy.c:2540 SHORT `pgstat_progress_update_multi_param(2, progress_start_index, progress_s` - m0131 src/backend/access/heap/vacuumlazy.c:2554 DEL `pgstat_progress_update_param(PROGRESS_VACUUM_INDEXES_PROCESSED, idx + ` - m0133 src/backend/access/heap/vacuumlazy.c:2554 ZERO `pgstat_progress_update_param(PROGRESS_VACUUM_INDEXES_PROCESSED, idx + ` — A: survived - m0134 src/backend/access/heap/vacuumlazy.c:2554 SWAP `pgstat_progress_update_param(PROGRESS_VACUUM_INDEXES_PROCESSED, idx + ` - m0141 src/backend/access/heap/vacuumlazy.c:2786 DEL `pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_VACUUMED, blkno` — A: survived - m0142 src/backend/access/heap/vacuumlazy.c:2786 PLUS1 `pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_VACUUMED, blkno` - m0143 src/backend/access/heap/vacuumlazy.c:2786 ZERO `pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_VACUUMED, blkno` - m0148 src/backend/access/heap/vacuumlazy.c:2991 SHORT `pgstat_progress_update_multi_param(2, progress_start_index, progress_s` — A: survived - m0149 src/backend/access/heap/vacuumlazy.c:3005 DEL `pgstat_progress_update_param(PROGRESS_VACUUM_INDEXES_PROCESSED, idx + ` - m0151 src/backend/access/heap/vacuumlazy.c:3005 ZERO `pgstat_progress_update_param(PROGRESS_VACUUM_INDEXES_PROCESSED, idx + ` - m0152 src/backend/access/heap/vacuumlazy.c:3005 SWAP `pgstat_progress_update_param(PROGRESS_VACUUM_INDEXES_PROCESSED, idx + ` - m0153 src/backend/access/heap/vacuumlazy.c:3019 DEL `pgstat_progress_update_multi_param(2, progress_end_index, progress_end` - m0154 src/backend/access/heap/vacuumlazy.c:3019 SHORT `pgstat_progress_update_multi_param(2, progress_end_index, progress_end` — A: survived - m0155 src/backend/access/heap/vacuumlazy.c:3059 DEL `pgstat_progress_update_param(PROGRESS_VACUUM_CURRENT_INDEX_RELID, (int` - m0157 src/backend/access/heap/vacuumlazy.c:3059 ZERO `pgstat_progress_update_param(PROGRESS_VACUUM_CURRENT_INDEX_RELID, (int` - m0161 src/backend/access/heap/vacuumlazy.c:3123 DEL `pgstat_progress_update_param(PROGRESS_VACUUM_CURRENT_INDEX_RELID, (int` - m0163 src/backend/access/heap/vacuumlazy.c:3123 ZERO `pgstat_progress_update_param(PROGRESS_VACUUM_CURRENT_INDEX_RELID, (int` - m0165 src/backend/access/heap/vacuumlazy.c:3145 DEL `pgstat_progress_update_multi_param(3, reset_index, reset_val)` — A: survived - m0166 src/backend/access/heap/vacuumlazy.c:3145 SHORT `pgstat_progress_update_multi_param(3, reset_index, reset_val)` — A: survived - m0181 src/backend/access/nbtree/nbtsort.c:391 DEL `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_BTR` — A: survived - m0182 src/backend/access/nbtree/nbtsort.c:391 PLUS1 `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_BTR` — A: survived - m0183 src/backend/access/nbtree/nbtsort.c:391 ZERO `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_BTR` — A: caught - m0184 src/backend/access/nbtree/nbtsort.c:391 SWAP `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_BTR` — A: survived - m0185 src/backend/access/nbtree/nbtsort.c:503 DEL `pgstat_progress_update_multi_param(3, progress_index, progress_vals)` - m0186 src/backend/access/nbtree/nbtsort.c:503 SHORT `pgstat_progress_update_multi_param(3, progress_index, progress_vals)` — A: survived - m0187 src/backend/access/nbtree/nbtsort.c:555 DEL `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_BTR` - m0188 src/backend/access/nbtree/nbtsort.c:555 PLUS1 `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_BTR` - m0189 src/backend/access/nbtree/nbtsort.c:555 ZERO `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_BTR` — A: survived - m0195 src/backend/access/nbtree/nbtsort.c:574 DEL `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_BTR` - m0196 src/backend/access/nbtree/nbtsort.c:574 PLUS1 `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_BTR` - m0197 src/backend/access/nbtree/nbtsort.c:574 ZERO `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_BTR` - m0207 src/backend/access/nbtree/nbtsort.c:1372 DEL `pgstat_progress_update_param(PROGRESS_CREATEIDX_TUPLES_DONE, ++tuples_` - m0208 src/backend/access/nbtree/nbtsort.c:1372 PLUS1 `pgstat_progress_update_param(PROGRESS_CREATEIDX_TUPLES_DONE, ++tuples_` - m0209 src/backend/access/nbtree/nbtsort.c:1372 ZERO `pgstat_progress_update_param(PROGRESS_CREATEIDX_TUPLES_DONE, ++tuples_` - m0211 src/backend/access/nbtree/nbtsort.c:1939 DEL `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_BTR` - m0212 src/backend/access/nbtree/nbtsort.c:1939 PLUS1 `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_BTR` - m0213 src/backend/access/nbtree/nbtsort.c:1939 ZERO `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_BTR` — A: survived - m0214 src/backend/access/nbtree/nbtsort.c:1939 SWAP `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_BTR` - m0215 src/backend/access/nbtree/nbtsort.c:1945 DEL `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_BTR` - m0216 src/backend/access/nbtree/nbtsort.c:1945 PLUS1 `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_BTR` — A: survived - m0217 src/backend/access/nbtree/nbtsort.c:1945 ZERO `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_BTR` - m0218 src/backend/access/nbtree/nbtsort.c:1945 SWAP `pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_BTR` - m0225 src/backend/backup/basebackup_progress.c:77 DEL `pgstat_progress_update_param(PROGRESS_BASEBACKUP_BACKUP_TOTAL, -1)` - m0226 src/backend/backup/basebackup_progress.c:77 PLUS1 `pgstat_progress_update_param(PROGRESS_BASEBACKUP_BACKUP_TOTAL, -1)` - m0227 src/backend/backup/basebackup_progress.c:77 ZERO `pgstat_progress_update_param(PROGRESS_BASEBACKUP_BACKUP_TOTAL, -1)` - m0235 src/backend/backup/basebackup_progress.c:133 DEL `pgstat_progress_update_param(PROGRESS_BASEBACKUP_TBLSPC_STREAMED, sink` - m0251 src/backend/backup/basebackup_progress.c:238 SHORT `pgstat_progress_update_multi_param(2, index, val)` - m0257 src/backend/catalog/index.c:3195 SHORT `pgstat_progress_update_multi_param(6, progress_index, progress_vals)` - m0259 src/backend/catalog/index.c:3497 SHORT `pgstat_progress_update_multi_param(5, progress_index, progress_vals)` - m0268 src/backend/catalog/index.c:3793 SHORT `pgstat_progress_update_multi_param(2, progress_cols, progress_vals)` - m0269 src/backend/catalog/index.c:3820 DEL `pgstat_progress_update_param(PROGRESS_CREATEIDX_ACCESS_METHOD_OID, iRe` — A: survived - m0270 src/backend/catalog/index.c:3820 PLUS1 `pgstat_progress_update_param(PROGRESS_CREATEIDX_ACCESS_METHOD_OID, iRe` - m0271 src/backend/catalog/index.c:3820 ZERO `pgstat_progress_update_param(PROGRESS_CREATEIDX_ACCESS_METHOD_OID, iRe` - m0313 src/backend/commands/analyze.c:1650 SHORT `pgstat_progress_update_multi_param(3, progress_index, progress_vals)` - m0362 src/backend/commands/indexcmds.c:457 DEL `pgstat_progress_update_param(PROGRESS_WAITFOR_TOTAL, n_old_snapshots)` - m0363 src/backend/commands/indexcmds.c:457 PLUS1 `pgstat_progress_update_param(PROGRESS_WAITFOR_TOTAL, n_old_snapshots)` — A: survived - m0364 src/backend/commands/indexcmds.c:457 ZERO `pgstat_progress_update_param(PROGRESS_WAITFOR_TOTAL, n_old_snapshots)` - m0365 src/backend/commands/indexcmds.c:457 SWAP `pgstat_progress_update_param(PROGRESS_WAITFOR_TOTAL, n_old_snapshots)` - m0379 src/backend/commands/indexcmds.c:650 DEL `pgstat_progress_update_param(PROGRESS_CREATEIDX_INDEX_OID, InvalidOid)` - m0381 src/backend/commands/indexcmds.c:650 ZERO `pgstat_progress_update_param(PROGRESS_CREATEIDX_INDEX_OID, InvalidOid)` - m0382 src/backend/commands/indexcmds.c:650 SWAP `pgstat_progress_update_param(PROGRESS_CREATEIDX_INDEX_OID, InvalidOid)` — A: survived - m0414 src/backend/commands/indexcmds.c:4123 DEL `pgstat_progress_start_command(PROGRESS_COMMAND_CREATE_INDEX, idx->tabl` - m0415 src/backend/commands/indexcmds.c:4129 DEL `pgstat_progress_update_multi_param(4, progress_index, progress_vals)` — A: survived - m0416 src/backend/commands/indexcmds.c:4129 SHORT `pgstat_progress_update_multi_param(4, progress_index, progress_vals)` - m0417 src/backend/commands/indexcmds.c:4271 DEL `pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATE` - m0418 src/backend/commands/indexcmds.c:4271 PLUS1 `pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATE` - m0419 src/backend/commands/indexcmds.c:4271 ZERO `pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATE` - m0420 src/backend/commands/indexcmds.c:4271 SWAP `pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATE` — A: survived - m0421 src/backend/commands/indexcmds.c:4301 DEL `pgstat_progress_start_command(PROGRESS_COMMAND_CREATE_INDEX, newidx->t` — A: survived - m0422 src/backend/commands/indexcmds.c:4306 DEL `pgstat_progress_update_multi_param(4, progress_index, progress_vals)` — A: survived - m0423 src/backend/commands/indexcmds.c:4306 SHORT `pgstat_progress_update_multi_param(4, progress_index, progress_vals)` — A: survived - m0424 src/backend/commands/indexcmds.c:4332 DEL `pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATE` - m0425 src/backend/commands/indexcmds.c:4332 PLUS1 `pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATE` - m0426 src/backend/commands/indexcmds.c:4332 ZERO `pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATE` - m0427 src/backend/commands/indexcmds.c:4332 SWAP `pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATE` - m0428 src/backend/commands/indexcmds.c:4367 DEL `pgstat_progress_start_command(PROGRESS_COMMAND_CREATE_INDEX, newidx->t` — A: survived - m0430 src/backend/commands/indexcmds.c:4372 SHORT `pgstat_progress_update_multi_param(4, progress_index, progress_vals)` - m0431 src/backend/commands/indexcmds.c:4402 DEL `pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATE` - m0432 src/backend/commands/indexcmds.c:4402 PLUS1 `pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATE` - m0433 src/backend/commands/indexcmds.c:4402 ZERO `pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATE` - m0434 src/backend/commands/indexcmds.c:4402 SWAP `pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATE` - m0435 src/backend/commands/indexcmds.c:4500 DEL `pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATE` - m0436 src/backend/commands/indexcmds.c:4500 PLUS1 `pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATE` - m0437 src/backend/commands/indexcmds.c:4500 ZERO `pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATE` - m0438 src/backend/commands/indexcmds.c:4500 SWAP `pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATE` — A: survived - m0439 src/backend/commands/indexcmds.c:4543 DEL `pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATE` — A: survived - m0441 src/backend/commands/indexcmds.c:4543 ZERO `pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATE` — A: survived - m0442 src/backend/commands/indexcmds.c:4543 SWAP `pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATE` - m0448 src/backend/commands/repack.c:1229 DEL `pgstat_progress_update_param(PROGRESS_REPACK_PHASE, PROGRESS_REPACK_PH` - m0450 src/backend/commands/repack.c:1229 ZERO `pgstat_progress_update_param(PROGRESS_REPACK_PHASE, PROGRESS_REPACK_PH` — A: survived - m0451 src/backend/commands/repack.c:1229 SWAP `pgstat_progress_update_param(PROGRESS_REPACK_PHASE, PROGRESS_REPACK_PH` - m0477 src/backend/commands/repack.c:3571 DEL `pgstat_progress_update_param(PROGRESS_REPACK_PHASE, PROGRESS_REPACK_PH` - m0478 src/backend/commands/repack.c:3571 PLUS1 `pgstat_progress_update_param(PROGRESS_REPACK_PHASE, PROGRESS_REPACK_PH` — A: survived - m0479 src/backend/commands/repack.c:3571 ZERO `pgstat_progress_update_param(PROGRESS_REPACK_PHASE, PROGRESS_REPACK_PH` — A: survived - m0480 src/backend/commands/repack.c:3571 SWAP `pgstat_progress_update_param(PROGRESS_REPACK_PHASE, PROGRESS_REPACK_PH` - m0481 src/backend/commands/repack.c:3647 DEL `pgstat_progress_update_param(PROGRESS_REPACK_PHASE, PROGRESS_REPACK_PH` - m0482 src/backend/commands/repack.c:3647 PLUS1 `pgstat_progress_update_param(PROGRESS_REPACK_PHASE, PROGRESS_REPACK_PH` - m0484 src/backend/commands/repack.c:3647 SWAP `pgstat_progress_update_param(PROGRESS_REPACK_PHASE, PROGRESS_REPACK_PH` - m0492 src/backend/commands/vacuum.c:2606 PLUS1 `pgstat_progress_incr_param(PROGRESS_ANALYZE_DELAY_TIME, INSTR_TIME_GET` — A: survived - m0495 src/backend/commands/vacuum.c:2609 PLUS1 `pgstat_progress_incr_param(PROGRESS_VACUUM_DELAY_TIME, INSTR_TIME_GET_` — A: survived - m0497 src/backend/commands/vacuumparallel.c:1162 DEL `pgstat_progress_update_multi_param(2, progress_index, progress_val)` — A: survived - m0498 src/backend/commands/vacuumparallel.c:1162 SHORT `pgstat_progress_update_multi_param(2, progress_index, progress_val)` - m0504 src/backend/commands/vacuumparallel.c:1388 DEL `pgstat_progress_start_command(PROGRESS_COMMAND_VACUUM, shared->relid)` — A: survived - m0508 src/backend/commands/vacuumparallel.c:1409 DEL `pgstat_progress_end_command()` — A: survived - m0518 src/backend/postmaster/datachecksum_state.c:1325 DEL `pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_PHASE, PROGRESS_DA` - m0520 src/backend/postmaster/datachecksum_state.c:1325 ZERO `pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_PHASE, PROGRESS_DA` - m0521 src/backend/postmaster/datachecksum_state.c:1325 SWAP `pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_PHASE, PROGRESS_DA` — A: survived - m0532 src/backend/postmaster/datachecksum_state.c:1453 SHORT `pgstat_progress_update_multi_param(2, index, vals)` - m0543 src/backend/postmaster/datachecksum_state.c:1851 SHORT `pgstat_progress_update_multi_param(2, index, vals)` — A: survived - m0548 src/backend/postmaster/datachecksum_state.c:1923 DEL `pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_PHASE, PROGRESS_DA` - m0549 src/backend/postmaster/datachecksum_state.c:1923 PLUS1 `pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_PHASE, PROGRESS_DA` - m0550 src/backend/postmaster/datachecksum_state.c:1923 ZERO `pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_PHASE, PROGRESS_DA` - m0559 src/backend/storage/lmgr/lmgr.c:936 DEL `pgstat_progress_update_param(PROGRESS_WAITFOR_TOTAL, total)` - m0560 src/backend/storage/lmgr/lmgr.c:936 PLUS1 `pgstat_progress_update_param(PROGRESS_WAITFOR_TOTAL, total)` - m0561 src/backend/storage/lmgr/lmgr.c:936 ZERO `pgstat_progress_update_param(PROGRESS_WAITFOR_TOTAL, total)` - m0562 src/backend/storage/lmgr/lmgr.c:936 SWAP `pgstat_progress_update_param(PROGRESS_WAITFOR_TOTAL, total)` - m0571 src/backend/storage/lmgr/lmgr.c:977 DEL `pgstat_progress_update_multi_param(3, index, values)` - m0572 src/backend/storage/lmgr/lmgr.c:977 SHORT `pgstat_progress_update_multi_param(3, index, values)` ## Caught by A but not by B - m0183 src/backend/access/nbtree/nbtsort.c:391 ZERO: A failed amcheck/001_verify_heapam,amcheck/002_cic,amcheck/003_cic_2pc,amcheck/004_verify_nbtree_unique,amcheck/005_pitr,amcheck/006_verify_gin,authentication/001_password,authentication/002_saslprep,authentication/003_peer,authentication/004_file_inclusion,authentication/005_sspi,authentication/006_login_trigger,authentication/007_pre_auth,auto_explain/001_auto_explain,basebackup_to_shell/001_basic,bloom/001_wal,brin/01_workitems,brin/02_wal_consistency,commit_ts/001_base,commit_ts/002_standby,commit_ts/003_standby_2,commit_ts/004_restart,dblink/001_auth_scram,ecpg/001_ecpg_err_warn_msg,ecpg/002_ecpg_err_warn_msg_informix,icu/010_database,libpq_pipeline/001_libpq_pipeline,oauth_validator/001_server,oauth_validator/002_client,oid2name/001_basic,pg_basebackup/040_pg_createsubscriber,pgbench/001_pgbench_with_server,pgbench/002_pgbench_no_server,pg_bsd_indent/001_pg_bsd_indent,pg_combinebackup/005_integrity,pg_ctl/003_promote,pg_ctl/004_logrotate,pg_dump/001_basic,pg_dump/002_pg_dump,pg_dump/003_pg_dump_with_server,pg_dump/004_pg_dump_parallel,pg_dump/005_pg_dump_filterfile,pg_dump/006_pg_dump_compress,pg_dump/010_dump_connstr,pg_plan_advice/001_foreign_scan,pg_prewarm/001_basic,pg_resetwal/001_basic,pg_resetwal/002_corrupted,pg_rewind/001_basic,pg_rewind/002_databases,pg_rewind/003_extrafiles,pg_rewind/004_pg_xlog_symlink,pg_rewind/005_same_timeline,pg_rewind/006_options,pg_rewind/007_standby_source,pg_rewind/008_min_recovery_point,pg_rewind/009_growing_files,pg_rewind/010_keep_recycled_wals,pg_rewind/011_wal_copy,pg_stash_advice/001_persist,pg_stat_statements/010_restart,pg_test_fsync/001_basic,pg_test_timing/001_basic,pg_verifybackup/001_basic,pg_verifybackup/002_algorithm,pg_verifybackup/003_corruption,pg_verifybackup/004_options,pg_verifybackup/005_bad_manifest,pg_verifybackup/006_encoding,pg_verifybackup/007_wal,pg_verifybackup/008_untar,pg_verifybackup/009_extract,pg_verifybackup/010_client_untar,pg_visibility/001_concurrent_transaction,pg_visibility/002_corrupt_vm,pg_waldump/001_basic,pg_waldump/002_save_fullpage,pg_walsummary/001_basic,pg_walsummary/002_blocks,pg_walsummary/003_tli_switch,postgres_fdw/001_auth_scram,postgres_fdw/010_subscription,postmaster/001_basic,postmaster/002_connection_limits,postmaster/003_start_stop,postmaster/004_negotiate,psql/001_basic,psql/010_tab_completion,psql/020_cancel,psql/030_pager,recovery/027_stream_regress,scripts/010_clusterdb,scripts/011_clusterdb_all,scripts/020_createdb,scripts/040_createuser,scripts/050_dropdb,scripts/070_dropuser,scripts/080_pg_isready,scripts/090_reindexdb,scripts/091_reindexdb_all,scripts/100_vacuumdb,scripts/101_vacuumdb_all,scripts/102_vacuumdb_stages,scripts/200_connstr,sepgsql/001_sepgsql,ssl/001_ssltests,ssl/002_scram,ssl/003_sslinfo,ssl/004_sni,ssl_passphrase_callback/001_testfunc,subscription/001_rep_changes,subscription/002_types,subscription/003_constraints,subscription/004_sync,subscription/005_encoding,subscription/006_rewrite,subscription/007_ddl,subscription/008_diff_schema,subscription/009_matviews,subscription/010_truncate,subscription/011_generated,subscription/012_collation,subscription/013_partition,subscription/014_binary,subscription/015_stream,subscription/016_stream_subxact,subscription/017_stream_ddl,subscription/018_stream_subxact_abort,subscription/019_stream_subxact_ddl_abort,subscription/020_messages,subscription/021_twophase,subscription/022_twophase_cascade,subscription/023_twophase_stream,subscription/024_add_drop_pub,subscription/025_rep_changes_for_schema,subscription/026_stats,subscription/027_nosuperuser,subscription/028_row_filter,subscription/029_on_error,subscription/030_origin,subscription/031_column_list,subscription/032_subscribe_use_index,subscription/033_run_as_table_owner,subscription/034_temporal,subscription/035_conflicts,subscription/036_sequences,subscription/037_except,subscription/038_walsnd_shutdown_timeout,subscription/100_bugs,test_aio/001_aio,test_aio/002_io_workers,test_aio/003_initdb,test_aio/004_read_stream,test_autovacuum/001_parallel_autovacuum,test_autovacuum/002_toast_relopts,test_checksums/001_basic,test_checksums/002_restarts,test_checksums/003_standby_restarts,test_checksums/004_offline,test_checksums/005_injection,test_checksums/006_pgbench_single,test_checksums/007_pgbench_standby,test_checksums/008_pitr,test_checksums/009_fpi,test_checksums/010_backup_straddle,test_checksums/011_standby_straddle,test_checksums/012_offline_standby,test_checksums/013_rewind,test_checksums/014_lockstep,test_checksums/015_standby_crash_after_disable,test_checksums/016_promote_enable_crash,test_checksums/017_restartpoint_race,test_checksums/018_enable_crash_windows,test_checksums/019_standby_shutdown_catchup,test_checksums/020_cascade_divergence,test_checksums/021_rewind_divergent_transitions,test_checksums/022_rewind_state,test_checksums/023_rewind_standby_target,test_checksums/024_combinebackup_mixed,test_custom_rmgrs/001_basic,test_custom_stats/001_custom_stats,test_decoding/001_repl_stats,test_escape/001_test_escape,test_extensions/001_extension_control_path,test_int128/001_test_int128,test_json_parser/001_test_json_parser_incremental,test_json_parser/002_inline,test_json_parser/003_test_semantic,test_json_parser/004_test_parser_perf,test_misc/001_constraint_validation,test_misc/002_tablespace,test_misc/003_check_guc,test_misc/004_io_direct,test_misc/005_timeouts,test_misc/006_signal_autovacuum,test_misc/007_catcache_inval,test_misc/008_replslot_single_user,test_misc/009_log_temp_files,test_misc/010_index_concurrently_upsert,test_misc/011_lock_stats,test_misc/012_ddlutils,test_misc/013_temp_obj_multisession,test_misc/014_log_statement_max_length,test_misc/015_temp_schema_exit_deferrable,test_pg_dump/001_base,test_saslprep/001_saslprep_ranges,test_shmem/001_late_shmem_alloc,test_slru/001_multixact,test_slru/002_multixact_wraparound,vacuumlo/001_basic,worker_spi/001_worker_spi,worker_spi/002_worker_terminate,xid_wraparound/001_emergency_vacuum,xid_wraparound/002_limits,xid_wraparound/003_wraparounds,xid_wraparound/004_notify_freeze Cost per mutant: judge A median 107 s, judge B 11 s (rebuild + install + tests). ===== mutacion/RESERVA.md ===== # Held-out mutants: protocol of the second round Written and sealed on 2026-11-03, after the first run (PROTOCOL.md, results.md) and before any of the gaps it found (review.md section 5) is closed. ## Why Closing the gaps against the same 572 mutants that showed them would tune the test to the benchmark: the 572 become the training set. So a second set is fixed now, with operators the first run did not use, on the same call sites of the same tree (src-A, the tree whose generar.py output is the sealed mutants.jsonl, sha256 a6ac3287...). ## The set `generar-reserva.py src-A > reserva.jsonl`: 301 mutants, ids r0001-r0301. - MINUS1 (121): the value of an update or increment, minus one. - XOR2 (121): the parameter of an update or increment, xor 2 (a parameter two away). - FIRST (35): a multi-parameter update without its first parameter. - DUP (13): an increment made twice. - RELID0 (11): a command started with InvalidOid as its relation. While the framework is changed, the contents of reserva.jsonl are not read: only its sha256 is used. The changes are made against review.md and the 572 alone. ## How it is judged Judge B (test_progress), with the mechanics of PROTOCOL.md (correr.sh, the same definition of caught, stillborn mutants reported and excluded), on all 301: 1. Before: with the v6 patches sealed below. 2. After: with the next version, once the gaps are closed. Both numbers are reported, and the 572 are judged again too. The 572's gain shows what was closed; the reserve's gain shows whether the closing holds on bugs it was not made against. If the reserve gains much less than the 572, that is said. ===== mutacion/generar-reserva.py ===== #!/usr/bin/env python3 """Generate the held-out mutants: other operators on the same call sites. Sealed before any gap of the first run is closed, and not looked at while the framework is changed, so that a second round is judged on bugs the changes were not made against. Same sites and scanner as generar.py; operators: MINUS1 the value argument becomes ((value) - 1) update, incr XOR2 the parameter becomes ((param) ^ 2) update, incr DUP the call is made twice incr RELID0 the relation becomes InvalidOid start FIRST the first parameter of the update is dropped multi generar-reserva.py > reserva.jsonl """ import json import re import sys from generar import sites def mutants(root): n = 0 for rel, src, func, s, e, args in sorted(sites(root), key=lambda x: (x[0], x[3])): old = src[s:e] line = src.count("\n", 0, s) + 1 a = [src[x:y].strip() for x, y in args] head = src[s:args[0][0]] kind = func.replace("pgstat_progress_", "") ops = [] if kind in ("update_param", "incr_param", "parallel_incr_param"): ops += [("MINUS1", f"{head}{a[0]}, (({a[1]}) - 1))"), ("XOR2", f"{head}(({a[0]}) ^ 2), {a[1]})")] if kind in ("incr_param", "parallel_incr_param"): ops += [("DUP", f"({old}, {old})")] if kind == "start_command": if not re.fullmatch(r"InvalidOid", a[1]): ops += [("RELID0", f"{head}{a[0]}, InvalidOid)")] if kind == "update_multi_param": ops += [("FIRST", f"{head}(({a[0]}) - 1), ({a[1]}) + 1, ({a[2]}) + 1)")] for op, new in ops: n += 1 yield {"id": f"r{n:04d}", "file": rel, "line": line, "func": kind, "op": op, "offset": s, "length": e - s, "old": old, "new": new, "args": a} if __name__ == "__main__": for m in mutants(sys.argv[1]): print(json.dumps(m, ensure_ascii=False)) ===== mutacion/reserva-antes.md ===== # Held-out mutants, before closing the gaps (v6 as sealed) Run on 2026-11-03 by `correr.sh reserva`, after RESERVA.sha256 was sealed (13:37 UTC). Raw: `corrida/judge-B-reserva.jsonl`. Only totals by operator were looked at, not which sites survived (RESERVA.md). - 301 mutants, 0 stillborn; test_progress catches 194 = 64.5% [95% CI 58.9-69.6%]. - Every catch is a check of 001_progress (two also of 002_doc_phases); no crash. - By operator: XOR2 87/121, MINUS1 71/121, FIRST 25/35, RELID0 8/11, DUP 3/13. ===== mutacion/RONDA2.md ===== # Second round: the gaps closed, judged again Written before the second round runs; sealed in RONDA2.sha256 with the patches it judges (`ronda2/`). ## What changed Only test_progress (t/001_progress.pl, ProgressCheck.pm and the Makefile, in patch 0001): no line of the backend, of progress.h or of the documentation. The call sites are the same 187, so generar.py gives the same 572 mutants and generar-reserva.py the same 301 (correr.sh checks both before judging). The checks were written against review.md section 5 and the 572 alone. The reserve (RESERVA.md, sealed 2026-11-03T13:37:59Z, before the first change to the test) was judged once before the changes and its contents were not read: only totals by operator. A development loop (`../huecos/probar.sh`) applied survivors of the 572 to a second tree to see which checks they fail; it refuses the reserve's ids. What the new checks look at, by principle: - The whole succession of each counter, not its final value: VACUUM's heap and index counters, CREATE INDEX's blocks and tuples, REPACK's blocks and tuples, the counters of the data checksums launcher and workers, ANALYZE of a partition tree. - The exact succession of phases and subphases of each variant: REINDEX CONCURRENTLY per transaction, VACUUM in several cycles, REPACK (CONCURRENTLY), the btree and GIN subphases read from their own headers. - Values against the catalog: the index and access method of each run, the partitions in order. - Scenarios that were missing: a hash build that sorts, a parallel btree build, a unique build with recently dead tuples, CREATE INDEX CONCURRENTLY that waits for a writer and for an old snapshot, CREATE INDEX IF NOT EXISTS, a partition tree with an index already attached and a partitioned partition, COPY of a query, COPY FROM through a BEFORE ROW trigger, a failure inside a subtransaction, a base backup of two tablespaces without an estimate, REPACK (CONCURRENTLY) with changes applied during catch-up (held at a known point by an advisory lock, no injection point), the parallel workers of VACUUM, and VACUUM and ANALYZE started by autovacuum. - Added after the first draft of this file, before sealing: a parallel unique build with recently dead tuples (both spools sorted), a build without deduplication, the blocks of a partitioned CREATE INDEX per partition, REPACK (CONCURRENTLY) blocks and command, a base backup without an estimate never reporting a total of 0, and VACUUM in failsafe mode (xid_wraparound consumes the xids; it is in EXTRA_INSTALL). The serial CREATE INDEX checks set max_parallel_maintenance_workers = 0: a parallel scan reports the blocks handed out, not the blocks done, and exact successions on it were flaky under load. ## How it is judged `correr.sh ronda2`: one tree with the patches of `ronda2/`, PROGRESS_DEBUG and assertions, as in the first round. One unmutated run must pass. Then judge B, with the same mechanics and the same definition of caught (PROTOCOL.md), on all 572 and on all 301. Reported side by side, and never one without the other: - the 572: 342 before (round 1), and after; - the reserve: 194 before (reserva-antes.md), and after. The 572 were the training set of this round, so their gain is expected. The reserve's gain is the one that says whether the checks hold on bugs they were not made against. Mutants that no check can catch from the trace (a value written twice, an off by one at a moment the trace cannot tell apart) are listed by id with the reason, not left out of the counts. Before sealing, a development pass of all 572 against this test (`../huecos/probar.sh`, not the judge) left 24 alive, the ones `../huecos/equivalentes.md` lists by id with the reason: 15 the trace cannot tell apart by construction, 9 not deterministic. That pass is not the count; the count is this round's judge. ===== mutacion/../huecos/equivalentes.md ===== # Round-1 mutants the trace cannot tell apart The trace logs a parameter only when its value changes (backend_progress.c, progress_debug_log()). A mutant that leaves every change identical is out of reach of any check on the trace. SWAP in this benchmark is `index ^ 1`: it writes the value to the neighbouring parameter. Dev harness only (probar.sh); the formal count is round 2. ## Same changes, by construction | mutant | site | why | |---|---|---| | m0043 | heapam_handler.c:731 DEL | REPACK heap_blks_scanned = rs_nblocks is set again, to the same value, at the end of the scan | | m0074 | heapam_handler.c:1302 PLUS1 | blocks_done+1 of a serial build scan: the logged succession is the same once deduplicated | | m0086 | heapam_handler.c:1783 PLUS1 | same, validation scan | | m0142 | vacuumlazy.c:2786 PLUS1 | heap_blks_vacuumed: the next write sets the value the +1 already gave | | m0166 | vacuumlazy.c:3145 SHORT | the dropped parameter of the reset is already 0 there | | m0251 | basebackup_progress.c:238 SHORT | tablespaces_streamed is already the number of tablespaces | | m0257 | index.c:3195 SHORT | blocks_total is already 0 when a build starts: the previous scan, if any, ended by setting it back to 0 (checked on a partitioned CREATE INDEX: n,0 per partition) | | m0259 | index.c:3497 SHORT | same, when the validation starts | | m0379, m0381, m0382 | indexcmds.c:650 DEL/ZERO/SWAP | index_relid is set to InvalidOid when it is already 0: it is set to an OID only by CIC and REINDEX, after this point. SWAP (index ^ 1) writes 0 to a parameter that is 0 too | | m0448, m0477 | repack.c:1229, 3571 DEL | FINAL_CLEANUP and SWAP_REL_FILES are also set by finish_heap_swap(): the phase does not change | | m0518, m0520 | datachecksum_state.c:1325 DEL/ZERO | PROGRESS_DATACHECKSUMS_PHASE_ENABLING is 0, the value every command starts with | Note on m0518/m0520: because ENABLING is 0, a reader cannot tell "enabling" from "not set yet". That is a property of the view, not of the test; worth a line on the list. ## Not deterministic | mutant | site | why | |---|---|---| | m0363 | indexcmds.c:457 PLUS1 | WAIT_3 waits for every old snapshot, and the test's own polling sessions can be among them: the check is total >= 1, done 0..total, not an exact total (an exact one was flaky) | | m0492, m0495 | vacuum.c:2606, 2609 PLUS1 | delay_time in nanoseconds: one more nanosecond of a measured sleep | | m0488, m0489, m0490 | vacuum.c:2597 DEL/PLUS1/SWAP | a parallel worker reports its delay only after sleeping for 1 s in total (PARALLEL_VACUUM_DELAY_REPORT_INTERVAL_NS) | | m0505, m0506, m0507 | vacuumparallel.c:1401 DEL/PLUS1/SWAP | a worker reports its remaining delay only if it slept, which depends on whether it got an index to process | ## Count (dev harness, round-1 mutants) 572 mutants: 342 killed by v6; 206 more by the checks added since (lotes 1-6); 24 left, all listed above: 15 trace-equivalent, 9 not deterministic. Expected, not yet measured: the formal count is round 2 (correr.sh ronda2), which also runs the held-out reserve. ===== mutacion/ronda2-results.md ===== # Round 2: results Run on 2026-11-03/04 by `correr.sh ronda2`, after RONDA2.sha256 was sealed (00:47:58 UTC), on the patches of `ronda2/` (v6, base a054b77e40d). Raw: `corrida/judge-R2.jsonl`, `corrida/judge-R2-reserva.jsonl`, `corrida/control.jsonl`. The unmutated run passed (rc 0); 0 stillborn, 0 traps. Judge time: 166 min for the 572, 90 for the 301. Round 1 judged the test of 2026-11-02 (`ronda1/`, never posted; v5 as posted was not judged). Round 2 judges the same mutants, by the same judge, on the v6 that is posted. | set | round 1 (test of 2026-11-02) | round 2 (v6 as posted) | |-----------------------------|------------------------------|------------------------------| | the 572 (training set) | 342 = 59.8% [CI 55.7-63.7%] | 548 = 95.8% [CI 93.8-97.2%] | | the reserve, 301 (held out) | 194 = 64.5% [CI 58.9-69.6%] | 285 = 94.7% [CI 91.5-96.7%] | | sample of 150 (PROTOCOL.md) | 84 = 56.0% [CI 48.0-63.7%] | 144 = 96.0% [CI 91.5-98.2%] | On the sample, PostgreSQL's tests today catch 10 (round 1, judge A); test_progress now catches all 10 of those and 134 more; neither catches 6. By operator, the 572: DEL 180/187, PLUS1 113/121, SHORT 31/35, SWAP 118/121, ZERO 106/108. The reserve: XOR2 118/121, MINUS1 114/121, FIRST 33/35, RELID0 11/11, DUP 9/13. ## What survived The 572: 24, exactly the ids `../huecos/equivalentes.md` lists, sealed before this run: 15 the trace cannot tell apart by construction, 9 not deterministic. The reserve: 16. Its contents were read only after this round was judged, to classify them; no check was changed after. Not deterministic (9): - r0254, r0256, r0257, r0259, r0260, r0262, r0269, r0271: the delay time of VACUUM and ANALYZE (vacuum.c, vacuumparallel.c), minus 1 ns or added twice. The time slept is not known in advance, so no exact value can be expected. - r0264 (vacuumparallel.c:1179, FIRST): current_index_relid is not reset after an index; which indexes the leader takes, rather than its workers, depends on the run. Equivalent in the trace (3): - r0024 (heapam_handler.c:747, MINUS1): REPACK's blocks scanned go 0..n-1; the first 0 is no change, and the end of the scan writes n, so the trace is the same. - r0247 (repack.c:3571, MINUS1): REPACK (CONCURRENTLY) writes CATCH_UP again instead of SWAP_REL_FILES, which the common swap code (repack.c:2055) then writes anyway. - r0277 (datachecksum_state.c:1325, XOR2): writes ENABLING, which is 0, into dbs_done, still 0 (as m0518 and m0520). Catchable, not checked (4): - r0023 (heapam_handler.c:731, XOR2): REPACK writes heap_blks_total's value into parameter 10, which REPACK does not use. A check that a command never writes a parameter its view does not show would catch it, and every XOR2 of that kind. - r0113 (basebackup_progress.c:77, MINUS1): backup_total starts at -2, not -1, so the view shows -2 where it should show NULL; the test checks only that it is never 0. - r0191 (indexcmds.c:650, XOR2): index_relid is not reset to 0 between the partitions of a partitioned CREATE INDEX; the test does not check its succession there. - r0073 (vacuumlazy.c:2943, FIRST): failsafe leaves indexes_total as it was. The test triggers failsafe before any index is vacuumed, when it is 0 anyway. These four are left as they are in v6: checks written now would be written knowing the reserve, and the reserve would no longer measure anything.