Database administrator reviewing PostgreSQL COPY FREEZE eligibility and MVCC snapshot visibility.

PostgreSQL COPY FREEZE Ran. Which Rows Can Another Session See?

Mon, Nov 9, 2026

COPY ... FREEZE can complete successfully and still leave an engineer with the wrong operational conclusion. The success signal answers whether PostgreSQL accepted that load under the transaction state it observed. It does not, by itself, prove what a second session holding an older MVCC snapshot will see. PostgreSQL 18 documents FREEZE as deliberately exceptional: eligible rows are inserted already frozen, and the documentation explicitly warns that other sessions can see those rows in a way that violates normal MVCC visibility expectations.

This playbook tests only that boundary: COPY FREEZE eligibility and MVCC snapshot visibility. It does not benchmark loading, teach general MVCC, recommend production migration topology, or claim a database execution that did not occur. PostgreSQL's current documentation identifies 18 as the current stable documentation branch, and the documentation index retrieved for this research identifies PostgreSQL 18.6. The executable fixture below therefore refuses to treat any other server version as equivalent evidence.

The operational decision is intentionally stricter than “the SQL returned success.” Acceptance requires a pinned version, independent expected-case manifest, negative controls, an old-snapshot comparison using ordinary COPY, a corresponding COPY FREEZE case, reconciled row identities and counts, and retained evidence for every case. No PostgreSQL server was executed while authoring this article, so every database result below is labeled EXPECTED / NOT EXECUTED. On that evidence state, the only justified release decision is HOLD.

Define the acceptance contract

The contract has two separate questions. First, was COPY FREEZE eligible under the exact transaction state? PostgreSQL 18 says the target must have been created or truncated in the current subtransaction, with no open cursors and no older snapshots held by the loading transaction; partitioned and foreign tables are excluded. The current implementation also raises explicit errors when those prerequisites are not met rather than making successful execution sufficient evidence of some weaker fallback.

Second, what does a different session see when it already owns a Repeatable Read snapshot from before the load? Under normal Repeatable Read semantics, successive queries use the transaction's established snapshot and do not ordinarily see changes committed by transactions that started later. COPY FREEZE is a documented exception to normal visibility.

The acceptance inputs are therefore exact PostgreSQL server version, transaction boundaries, target-table identity, load method, reader isolation level, snapshot-establishment point, source rows, and case identifier. Assertions cover command success or rejection, exact logical row IDs, row count, and reader visibility.

Required artifacts are the fixture source, SHA-256 of that source at execution, SELECT version(), SHOW server_version, SHOW server_version_num, expected-case manifest, per-session stdout and stderr, timestamps, and the final evidence ledger.

A case fails acceptance if its observed result differs from its independently authored expectation, if the server version is not the pinned version, if an output file is missing, or if the reader snapshot was not demonstrably established before the writer load.

That evidence model is deliberately different from Refonte Learning's PostgreSQL logical replication failover playbook, which evaluates replication slots, WAL availability, promotion readiness and failover ownership rather than tuple freezing or old-snapshot visibility.

Pin versions and establish a baseline

Pin the behavioral claim to PostgreSQL 18.6. The PostgreSQL documentation site currently labels 18 as the current stable documentation line, while PostgreSQL 19 was still represented as a development/release-candidate branch in the retrieved pages. The relevant 18 documentation directly states the COPY FREEZE prerequisites and its MVCC warning.

The fixture must therefore fail closed unless:

server_version     = 18.6
server_version_num = 180006

Do not replace that check with psql --version. The client executable and server can differ. Capture both if useful, but qualify behavior against the server that actually executes COPY.

The baseline must also establish ordinary snapshot behavior. PostgreSQL's MVCC introduction describes each statement as observing a database snapshot, while Repeatable Read fixes transaction visibility at the snapshot established by the transaction's first non-transaction-control statement. A normal later insert is therefore expected to remain invisible to that older snapshot.

The baseline fixture uses a permanent witness table solely to force the reader's Repeatable Read snapshot before the writer modifies the target. The reader deliberately does not touch the target before the writer's TRUNCATE, because an earlier SELECT on that table would acquire an ACCESS SHARE lock and change the concurrency experiment.

Check a negative control

Case E01 starts with a pre-existing target and attempts COPY ... FREEZE without creating or truncating the target in the current subtransaction. PostgreSQL's implementation reports:

cannot perform COPY FREEZE because the table was not created
or truncated in the current subtransaction

The source checks the relation's create/truncate subtransaction identity before enabling frozen insertion. PostgreSQL's regression suite also contains a case in which TRUNCATE occurs outside the required transaction context and COPY FREEZE is rejected.

Measurable pass condition: E01 must fail; zero test rows may remain committed; stderr must contain the expected prerequisite-state error class/message.

Status: EXPECTED / NOT EXECUTED.

If E01 succeeds on the pinned server, stop. That is not a harmless variation; it invalidates the fixture's assumptions and requires HOLD.

Author an independent expected-case manifest

Write expectations before running the fixture. The manifest is an oracle, not a transcription of whatever the run produces. Its purpose is to make post-hoc reinterpretation visible.

fixture_contract: copy-freeze-mvcc-v1
postgresql_server: "18.6"
server_version_num: 180006

cases:
  E01:
    purpose: "negative control: no create/truncate in current subtransaction"
    expected_command: ERROR
    expected_committed_ids: []
    expected_reader_ids: null
    observed: NOT_EXECUTED

  E02:
    purpose: "eligible COPY FREEZE after TRUNCATE in same subtransaction"
    expected_command: COPY_3_THEN_COMMIT
    expected_committed_ids: [101, 102, 103]
    observed: NOT_EXECUTED

  E03:
    purpose: "boundary: SAVEPOINT created after TRUNCATE"
    expected_command: ERROR_CURRENT_SUBTRANSACTION
    expected_committed_ids: []
    observed: NOT_EXECUTED

  E04:
    purpose: "boundary: open cursor before COPY FREEZE"
    expected_command: ERROR_PRIOR_TRANSACTION_ACTIVITY
    expected_committed_ids: []
    observed: NOT_EXECUTED

  V01:
    purpose: "ordinary COPY after TRUNCATE versus old Repeatable Read snapshot"
    writer_ids: [201, 202, 203]
    expected_fresh_session_ids: [201, 202, 203]
    expected_old_snapshot_ids: []
    observed: NOT_EXECUTED

  V02:
    purpose: "COPY FREEZE after TRUNCATE versus old Repeatable Read snapshot"
    writer_ids: [301, 302, 303]
    expected_fresh_session_ids: [301, 302, 303]
    expected_old_snapshot_ids: [301, 302, 303]
    observed: NOT_EXECUTED

  R01:
    purpose: "rollback safety"
    writer_ids: [401, 402, 403]
    expected_after_rollback_ids: []
    observed: NOT_EXECUTED

decision:
  expected_before_execution: HOLD

V01 is particularly important because TRUNCATE itself is not MVCC-safe. PostgreSQL documents that a transaction holding a snapshot from before a concurrent TRUNCATE can see the rewritten table as empty if it had not accessed the target before the DDL operation. That makes ordinary COPY after TRUNCATE the correct neighboring control for the frozen case.

This independent-oracle pattern should not be conflated with Refonte Learning's PostgreSQL SKIP LOCKED queue fairness article. That page tests lock-skipping, claim ordering, starvation and recovery. Here the identities [201..203] and [301..303] exist only to distinguish MVCC visibility outcomes.

Construct the controlled fixture

Use a disposable database and synthetic rows only. The following harness requires bash and psql. Provisioning PostgreSQL is intentionally outside the test: the script verifies the server rather than silently selecting whatever installation happens to be present.

#!/usr/bin/env bash
set -euo pipefail

: "${PGHOST:?set PGHOST}"
: "${PGPORT:=5432}"
: "${PGUSER:?set PGUSER}"
: "${PGDATABASE:?set PGDATABASE}"

export PGAPPNAME="copyfreeze-harness"

OUT="${OUT:-./copyfreeze-evidence}"
rm -rf "$OUT"
mkdir -p "$OUT"

PSQL=(psql -X -v ON_ERROR_STOP=1
      -h "$PGHOST" -p "$PGPORT"
      -U "$PGUSER" -d "$PGDATABASE")

"${PSQL[@]}" -Atqc \
  "select current_setting('server_version_num')" \
  >"$OUT/server_version_num.txt"

test "$(cat "$OUT/server_version_num.txt")" = "180006" || {
  echo "HOLD: expected PostgreSQL 18.6 / 180006" >&2
  exit 40
}

"${PSQL[@]}" -X -c "select version()" >"$OUT/version.txt"
"${PSQL[@]}" -X -c "show server_version" >>"$OUT/version.txt"

"${PSQL[@]}" <<'SQL' >"$OUT/setup.log" 2>&1
DROP TABLE IF EXISTS cf_target;
DROP TABLE IF EXISTS cf_witness;

CREATE TABLE cf_target (
    id      integer PRIMARY KEY,
    payload text NOT NULL
);

CREATE TABLE cf_witness (
    id integer PRIMARY KEY
);

INSERT INTO cf_witness VALUES (1);
SQL

# E01: target not created/truncated in current subtransaction.
set +e
"${PSQL[@]}" <<'SQL' >"$OUT/E01.out" 2>"$OUT/E01.err"
BEGIN;
COPY cf_target (id, payload) FROM STDIN WITH (FORMAT csv, FREEZE true);
1,negative-control
\.
COMMIT;
SQL
E01_RC=$?
set -e
printf '%s\n' "$E01_RC" >"$OUT/E01.rc"

"${PSQL[@]}" -Atqc \
  "select coalesce(string_agg(id::text, ',' order by id),'EMPTY')
   from cf_target" >"$OUT/E01.rows"

# Reset any aborted-session effects.
"${PSQL[@]}" -qc "TRUNCATE cf_target"

# E02: eligible same-subtransaction truncate + freeze.
"${PSQL[@]}" <<'SQL' >"$OUT/E02.out" 2>"$OUT/E02.err"
BEGIN;
TRUNCATE cf_target;
COPY cf_target (id, payload) FROM STDIN WITH (FORMAT csv, FREEZE true);
101,e02-a
102,e02-b
103,e02-c
\.
COMMIT;
SQL
"${PSQL[@]}" -Atqc \
  "select string_agg(id::text, ',' order by id) from cf_target" \
  >"$OUT/E02.rows"

# E03: savepoint changes current subtransaction after truncate.
"${PSQL[@]}" -qc "TRUNCATE cf_target"
set +e
"${PSQL[@]}" <<'SQL' >"$OUT/E03.out" 2>"$OUT/E03.err"
BEGIN;
TRUNCATE cf_target;
SAVEPOINT after_truncate;
COPY cf_target (id, payload) FROM STDIN WITH (FORMAT csv, FREEZE true);
111,e03
\.
COMMIT;
SQL
E03_RC=$?
set -e
printf '%s\n' "$E03_RC" >"$OUT/E03.rc"
"${PSQL[@]}" -Atqc \
  "select coalesce(string_agg(id::text, ',' order by id),'EMPTY')
   from cf_target" >"$OUT/E03.rows"

# E04: open cursor makes COPY FREEZE ineligible.
"${PSQL[@]}" -qc "TRUNCATE cf_target"
set +e
"${PSQL[@]}" <<'SQL' >"$OUT/E04.out" 2>"$OUT/E04.err"
BEGIN;
TRUNCATE cf_target;
DECLARE witness_cursor CURSOR FOR SELECT FROM cf_witness;
COPY cf_target (id, payload) FROM STDIN WITH (FORMAT csv, FREEZE true);
121,e04
\.
COMMIT;
SQL
E04_RC=$?
set -e
printf '%s\n' "$E04_RC" >"$OUT/E04.rc"

run_visibility_case() {
  local case_id="$1"
  local freeze_sql="$2"
  local ids="$3"
  local csv="$4"

  "${PSQL[@]}" -qc "TRUNCATE cf_target"

  PGAPPNAME="${case_id}_reader" \
  "${PSQL[@]}" <<SQL >"$OUT/${case_id}.reader" 2>"$OUT/${case_id}.reader.err" &
BEGIN ISOLATION LEVEL REPEATABLE READ;
SELECT 'SNAPSHOT_WITNESS', count() FROM cf_witness;
SELECT pg_sleep(8);
SELECT '${case_id}_OLD_SNAPSHOT',
       coalesce(string_agg(id::text, ',' ORDER BY id), 'EMPTY')
FROM cf_target;
COMMIT;
SQL
  reader_pid=$!

  # Allow reader to establish its Repeatable Read snapshot.
  sleep 2

  "${PSQL[@]}" <<SQL >"$OUT/${case_id}.writer" 2>"$OUT/${case_id}.writer.err"
BEGIN;
TRUNCATE cf_target;
COPY cf_target (id, payload) FROM STDIN
WITH (FORMAT csv${freeze_sql});
${csv}
\\.
COMMIT;
SQL

  wait "$reader_pid"

  "${PSQL[@]}" -Atqc \
    "select coalesce(string_agg(id::text, ',' order by id),'EMPTY')
     from cf_target" >"$OUT/${case_id}.fresh"
}

run_visibility_case \
  V01 \
  "" \
  "201,202,203" \
  $'201,v01-a\n202,v01-b\n203,v01-c'

run_visibility_case \
  V02 \
  ", FREEZE true" \
  "301,302,303" \
  $'301,v02-a\n302,v02-b\n303,v02-c'

# R01: rollback safety.
"${PSQL[@]}" -qc "TRUNCATE cf_target"
"${PSQL[@]}" <<'SQL' >"$OUT/R01.out" 2>"$OUT/R01.err"
BEGIN;
TRUNCATE cf_target;
COPY cf_target (id, payload) FROM STDIN WITH (FORMAT csv, FREEZE true);
401,r01-a
402,r01-b
403,r01-c
\.
ROLLBACK;
SQL

"${PSQL[@]}" -Atqc \
  "select coalesce(string_agg(id::text, ',' order by id),'EMPTY')
   from cf_target" >"$OUT/R01.rows"

sha256sum "$0" >"$OUT/harness.sha256"
printf 'Fixture completed; compare artifacts with the authored manifest.\n'

The script deliberately does not inspect xmin and call a value of 2 “proof of freezing.” PostgreSQL documents xmin as the inserting transaction ID, but since PostgreSQL 9.4 freezing has normally been represented by a tuple flag while retaining the original xmin. An xmin = 2 test would therefore be an invalid oracle for modern PostgreSQL.

Record the state before cleanup

Before deleting the fixture, retain version.txt, the exact harness, its hash, every .out and .err file, return codes, and fresh-session row inventories.

A concrete pass criterion is:

evidence_file_count > 0
AND server_version_num.txt == 180006
AND harness.sha256 exists
AND every required case has stdout/stderr or an explicit empty file

Do not replace failed-run artifacts with a clean rerun. Preserve the first evidence directory, repair the fixture in a new directory, then compare them.

Status: EXPECTED / NOT EXECUTED.

Reproduce the misleading success

The misleading success case is V02. The writer is expected to execute:

BEGIN;
TRUNCATE cf_target;

COPY cf_target (id, payload)
FROM STDIN
WITH (FORMAT csv, FREEZE true);
301,v02-a
302,v02-b
303,v02-c
\.

COMMIT;

Nothing about a successful COPY 3 should be interpreted as “every reader follows ordinary snapshot rules.” PostgreSQL expressly says the opposite for FREEZE: qualifying rows are frozen at load time and other sessions can see them despite normal MVCC expectations.

The reader begins first:

BEGIN ISOLATION LEVEL REPEATABLE READ;

SELECT count(*) FROM cf_witness;  -- establish old snapshot

-- writer performs TRUNCATE + COPY FREEZE + COMMIT here

SELECT string_agg(id::text, ',' ORDER BY id)
FROM cf_target;

The authored expectation is 301,302,303, even though those rows were loaded after the reader established its Repeatable Read snapshot. PostgreSQL's own wiki demonstrates this specific COPY FREEZE anomaly: a reader establishes a Repeatable Read snapshot, another transaction truncates and performs COPY ... FREEZE, commits, and the older reader then observes the new “phantom” row.

That example is corroborative documentation, not execution evidence for this article. The case here therefore remains:

Case

Writer result

Old-snapshot visibility

Status

V02

COPY 3, commit

301,302,303

EXPECTED / NOT EXECUTED

This is the false-positive-success boundary operationally: the load's success is real, but treating that success as proof of ordinary snapshot isolation would be false.

Run a correct comparison case

V01 repeats the synchronization with ordinary COPY, not COPY FREEZE:

BEGIN;
TRUNCATE cf_target;

COPY cf_target (id, payload)
FROM STDIN WITH (FORMAT csv);
201,v01-a
202,v01-b
203,v01-c
\.

COMMIT;

The reader again establishes a Repeatable Read snapshot by touching only cf_witness before the writer starts. Under PostgreSQL's transaction-isolation contract, that reader does not ordinarily gain visibility of rows inserted after its snapshot.

There is an additional boundary here: TRUNCATE itself is not MVCC-safe. PostgreSQL documents that, after a concurrent truncation/rewrite commits, a transaction using an older snapshot can see the target as empty if it had not accessed that table before the DDL began. That is exactly why V01 expects EMPTY, not the previous table contents.

The paired result is therefore authored as:

Case

Load

Old Repeatable Read snapshot

Fresh post-commit session

V01

ordinary COPY

EMPTY

201,202,203

V02

COPY FREEZE

301,302,303

301,302,303

All four cells are EXPECTED / NOT EXECUTED.

This experiment is unrelated to the ownership question in Refonte Learning's PostgreSQL advisory-lock lifetime article. That playbook proves whether a lock belongs to a session or transaction and when it is released; no advisory lock is used as correctness evidence here.

Capture exceptions and warnings

Eligibility failures are evidence, not noise. Retain stderr verbatim. Current PostgreSQL source distinguishes at least two relevant error paths: a relation not created/truncated in the current subtransaction and prior transaction activity such as retained snapshots or ready portals.

For this fixture:

E01 -> expect prerequisite-state rejection
E03 -> expect current-subtransaction rejection
E04 -> expect prior-transaction-activity rejection

Pass only if each intended negative control fails for its intended reason. An unexpected syntax error, authentication error, timeout or missing relation is not a successful negative test.

Test boundaries and failure modes

The strongest boundary is the word subtransaction. A top-level transaction is not sufficient if a savepoint changes the current subtransaction after the operation that created the new relation storage.

PostgreSQL's regression material includes the pattern:

BEGIN;
TRUNCATE vistest;
SAVEPOINT s1;
COPY vistest FROM stdin CSV FREEZE;

and expects a rejection because the relation was not created or truncated in the current subtransaction. Historical PostgreSQL discussion explains the same distinction and notes that releasing the savepoint changes the relevant state.

The second boundary is transaction activity. An open cursor is intentionally included as E04 because the documented FREEZE conditions exclude open cursors. Current source checks ready portals and previously registered snapshots and reports cannot perform COPY FREEZE because of prior transaction activity.

The third boundary is relation type. PostgreSQL 18 explicitly excludes partitioned tables and foreign tables from COPY FREEZE; current source has dedicated errors for both. Those are useful future fixture extensions, but they are not required to answer the narrow snapshot question here.

The fourth boundary is isolation level. Read Committed gets a new snapshot for each command, so it is a poor primary oracle for reproducing this anomaly deterministically. Repeatable Read is the clean comparison because PostgreSQL documents that successive queries use the transaction's existing snapshot.

Finally, do not infer freezing from transaction-ID cosmetics. In modern PostgreSQL a frozen tuple normally preserves its historical xmin; freezing semantics are represented independently of changing that system column to FrozenTransactionId.

Capture complete case-by-case evidence

The evidence ledger should remain mechanical. Do not replace an expected value with prose such as “looked correct.”

Case

Assertion

Expected

Observed

Evidence state

E01

ineligible pre-existing relation rejected

error; zero committed fixture rows

NOT EXECUTED

EXPECTED

E02

same-subtransaction truncate is eligible

commit IDs 101,102,103

NOT EXECUTED

EXPECTED

E03

savepoint boundary invalidates eligibility

error; zero committed fixture rows

NOT EXECUTED

EXPECTED

E04

open cursor blocks FREEZE eligibility

prior-activity error

NOT EXECUTED

EXPECTED

V01

ordinary COPY vs old RR snapshot

reader EMPTY

NOT EXECUTED

EXPECTED

V01

fresh reader after commit

201,202,203

NOT EXECUTED

EXPECTED

V02

COPY FREEZE vs old RR snapshot

301,302,303

NOT EXECUTED

EXPECTED

V02

fresh reader after commit

301,302,303

NOT EXECUTED

EXPECTED

R01

rollback removes frozen load

EMPTY

NOT EXECUTED

EXPECTED

A useful ledger adds started_at, ended_at, server_version_num, fixture SHA-256, reader PID or application name, writer PID or application name, return code, stdout path and stderr path. The identifiers are provenance aids; none substitutes for the logical assertions.

Documented contract and observed result must occupy separate columns. PostgreSQL documentation is evidence for what the implementation promises; a local output file is evidence for what the pinned fixture actually did. Neither can impersonate the other.

Reject incomplete evidence

A measurable completeness check can be expressed as:

required_cases = {E01,E02,E03,E04,V01,V02,R01}

PASS only if:
  every case has a recorded terminal status
  AND every visibility case has reader and writer evidence
  AND version evidence is present
  AND harness hash is present
  AND no observed field remains NOT_EXECUTED

Under that rule this article cannot pass its own release gate: every observed field remains NOT EXECUTED.

That is intentional. An expected-case manifest is valuable precisely because it cannot be promoted into fabricated observation.

Reconcile state, identity and counts

Row counts alone are too weak. count(*) = 3 does not prove that the expected three synthetic rows became visible; a stale fixture could also contain three unrelated rows.

Reconcile stable logical IDs:

SELECT
    count(*) AS row_count,
    array_agg(id ORDER BY id) AS ids
FROM cf_target;

For V02, acceptance requires both:

row_count = 3
ids       = {301,302,303}

For V01 under the old snapshot:

row_count = 0
ids       = NULL / EMPTY

The fresh-session query then proves that the writer did commit the ordinary COPY rows even though the older snapshot did not see them. Without that second check, EMPTY could be misdiagnosed as writer failure rather than snapshot behavior.

ctid should not be the identity oracle. PostgreSQL documents ctid as a physical location that can change when a row moves; a primary key is the appropriate stable identity for these synthetic logical rows.

The same separation of state, identity and acceptance policy appears in a different technical domain in Refonte Learning's SQLite foreign key enforcement article. That page validates connection-specific foreign-key state and negative inserts. This playbook does not reuse its SQLite transaction workflow; the semantic link is only the general discipline of verifying behavior rather than trusting a successful configuration command.

Separate documentation from observation

Four evidence classes should never be collapsed.

Documented contract. PostgreSQL 18 says COPY FREEZE requests rows to be loaded already frozen; eligibility depends on create/truncate state, cursors and snapshots; and other sessions can see the resulting rows in violation of normal MVCC visibility.

Documented snapshot model. PostgreSQL describes MVCC statements as operating against snapshots and Repeatable Read as preserving the transaction's established view across successive commands.

Documented tuple metadata. xmin identifies the inserting transaction for a row version, but modern freezing does not normally rewrite xmin to FrozenTransactionId. System columns and PostgreSQL's vacuum/freezing documentation establish why xmin = 2 is not a valid modern test oracle.

Observation. Observation begins only after the fixture is executed against the pinned environment and its artifacts are retained. There is no PostgreSQL observation in this article.

There is one important implementation-level corroboration: current PostgreSQL source explicitly rejects prerequisite-state and prior-transaction-activity violations. That supports the fixture design but still does not constitute a local run.

Revalidate the repaired invariant

Suppose an initial run shows E03 unexpectedly succeeding because the test author accidentally issued RELEASE SAVEPOINT after_truncate before COPY. Repair the fixture, do not edit the expectation.

The revalidation invariant is:

same manifest
+ new immutable run directory
+ corrected transaction sequence
+ same PostgreSQL 18.6 pin
= eligible rerun candidate

Pass only when E03 actually fails at the intended subtransaction boundary and E02 remains successful. If both fail, the repair damaged the positive control. If both succeed, the negative control is still invalid.

Repair the workflow without masking errors

The repair target is the loading workflow, never the evidence after the fact.

If COPY FREEZE fails because the table was not created or truncated in the current subtransaction, an eligible pattern is:

BEGIN;
TRUNCATE cf_target;
COPY cf_target FROM STDIN WITH (FORMAT csv, FREEZE true);
...
\.
COMMIT;

Do not “repair” it by removing FREEZE while retaining a case label that claims frozen behavior. Ordinary COPY is a different case with a different visibility expectation.

Likewise, if an open cursor or retained transaction activity makes COPY FREEZE ineligible, close or restructure the activity before the loading transaction. PostgreSQL's source treats prior transaction activity as a correctness condition for this optimization, not as an informational warning.

A safer operational sequence is therefore: begin a dedicated loading transaction; perform only the state required to establish the new relation storage; execute the frozen load; commit or roll back as one bounded unit; then run the independent visibility/reconciliation checks from new sessions.

Do not add VACUUM FREEZE after a failed case and call the case repaired. That changes the behavior under test. The question here is specifically whether COPY FREEZE was eligible and what snapshot visibility followed from that load.

Do not remove the old-snapshot reader because it makes a release check inconvenient. Without that reader, the fixture proves only that committed rows are readable later, a property that ordinary loading already provides.

Validate rollback and rerun safety

Frozen tuples do not make transaction rollback optional. R01 tests:

BEGIN;
TRUNCATE cf_target;

COPY cf_target FROM STDIN WITH (FORMAT csv, FREEZE true);
401,r01-a
402,r01-b
403,r01-c
\.

ROLLBACK;

The independently authored expectation is that a fresh session sees no 401..403 rows after rollback. The implementation's subtransaction checks exist in part to preserve correctness when a subtransaction aborts; current PostgreSQL source comments explicitly connect the exact subtransaction test with preventing frozen tuples from surviving abort cleanup in a visible state.

Rerun safety means more than “execute the script twice.” Every run starts from a disposable target, writes a new evidence directory, verifies the exact server version, recreates the synthetic tables, and uses case-specific IDs. A stale 301 from a previous run must not be able to satisfy the next run's V02 assertion accidentally.

A rerun after a fixture repair must preserve the failed evidence directory. Otherwise the release trail loses the distinction between the failure that motivated the repair and the successful qualification that followed it.

The neighboring Python ZIP duplicate-member integrity article also preserves an independently authored manifest and occurrence-level evidence, but its problem is archive-member identity and CRC validation. It provides no evidence for PostgreSQL MVCC, and this article deliberately does not borrow its ZIP-specific evidence workflow.

Assign operational ownership

Use explicit ownership at the gate:

Fixture owner:
  maintains executable code and version pin.

Database reviewer:
  validates PostgreSQL-version applicability and transaction semantics.

Release reviewer:
  confirms manifest-versus-observed reconciliation.

Artifact custodian:
  preserves failed and passing run directories.

A case is not transferable merely because someone says “COPY succeeded.” The owner handing off evidence must provide the exact run directory and manifest comparison.

If ownership is unclear when an unexpected visibility result appears, the decision is HOLD, not speculative repair.

Apply the ACCEPT / REPAIR / RERUN / HOLD matrix

The decision is based on evidence state rather than optimism.

Condition

Decision

Required action

PostgreSQL 18.6 pin verified; every required case executed; observed values exactly match manifest; artifacts complete

ACCEPT

Approve only the bounded COPY FREEZE workflow represented by the fixture

SQL sequence violates documented eligibility, evidence collection is defective, or synchronization is demonstrably wrong

REPAIR

Correct fixture/workflow without changing expected semantics

Repair completed, environment still matches pin, and a clean disposable fixture can be recreated

RERUN

Execute all required cases again; do not cherry-pick only the failed case

Version differs, evidence is incomplete, a required case is NOT EXECUTED, or result contradicts contract without explanation

HOLD

Do not approve release behavior

The current article state is unambiguous:

E01  NOT EXECUTED
E02  NOT EXECUTED
E03  NOT EXECUTED
E04  NOT EXECUTED
V01  NOT EXECUTED
V02  NOT EXECUTED
R01  NOT EXECUTED

Decision: HOLD

That is not a judgment that PostgreSQL's documented behavior is uncertain. PostgreSQL's documentation is clear: MVCC normally gives snapshot-based visibility; Repeatable Read preserves the older snapshot; COPY FREEZE has strict prerequisites and intentionally makes frozen rows visible outside ordinary MVCC rules.

HOLD instead means the operational acceptance package requested here has not yet produced local observations. Documentation supplies the expected contract. It does not manufacture the release evidence.

Build relevant skills with Refonte Learning

The Refonte Learning Database Administrator Essentials program is relevant at the broad database-administration skill level, not as evidence that its curriculum specifically teaches this COPY FREEZE experiment. The exact program page currently lists database design and architecture, SQL query optimization, backup and recovery, performance tuning, security, cloud database management, data migration and integration, disaster recovery, role-based access control, and monitoring and maintenance.

The page states a three-month period and 12–14 hours per week, recommends basic programming knowledge, and separately lists an admission prerequisite of working toward a bachelor's or higher-level degree. It also states that successful completion can include a Training Certificate and Certificate of Internship.

The retrieved curriculum summary does not establish that this narrow PostgreSQL behavior is taught.

For engineers building release-acceptance discipline, the transferable skill is narrower: convert documentation into an independent oracle, construct a reproducible fixture, preserve negative controls and failed evidence, and refuse to convert an unexecuted expectation into an observation. The next step is to review the Database Administrator Essentials page for current program details if that broader DBA skill path is relevant.