Skip to content

test: iceberg_data_files refuses a delete entry inside a data manifest (#1341 item 49) - #1439

Merged
jdatcmd merged 2 commits into
mainfrom
test/1341-49-iceberg-entry-deletes
Oct 16, 2026
Merged

jdatcmd merged 2 commits into
mainfrom
test/1341-49-iceberg-entry-deletes

Conversation

@jdatcmd

@jdatcmd jdatcmd commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Covers #1341 item 49: iceberg_data_files refuses a delete entry inside a data manifest.

What was untested

test/iceberg_data_files.sh tested only the manifest-list refusal: a delete manifest (content 1 in the list) is refused without being opened. A data manifest can still hold an entry the lister must not return:

  • a position-delete entry (content 1);
  • an equality-delete entry (content 2);
  • a removed entry (status 2, DELETED).

Listing a removed file would hand a caller rows that a commit took out. The refusal is at src/columnar_iceberg.c (if (e->content != 0 || e->status == 2)), and nothing tested it.

What this adds

Four crafted tables share one data manifest and differ only in the entry's (status, content):

(status, content) expected
(1, 0), the control listed: 1 file
(0, 0), EXISTING listed: 1 file
(1, 1) refused, 0A000
(1, 2) refused, 0A000
(2, 0) refused, 0A000

The control proves the crafted Avro reads, so each refusal comes from the entry. A "backend still up" check follows the refusals.

Evidence

  • Red, both twins, PG17. Mutant A drops e->content != 0: the position and equality arms fail with got [] want [0A000]. Mutant B drops e->status == 2: only the DELETED arm fails. The control passes under both. The source was restored after each run; git status showed only the test files.
  • Green, PG15 to 19. Shell: 18/18 on each major. Pytest: 4 tests, 7 checks, on each major. A probe confirmed the server versions: 150019, 180006 and 190000.
  • Guards. 438 guard tests pass (1284 checks). Collecting the cluster files gives 608, the new cluster_tests.

Registries

  • Ledger. iceberg_data_files is not a ledger-covered suite (0 rows), so no ledger rows are owed.
  • Pytest twin. test_iceberg_data_files.py is new and ports only these arms. The pair is declared INCOMPLETE in test_compare_to_bash.py, naming the shell arms it does not port.
  • Counts and docs. expected_tests.txt: cluster_tests 603 -> 608 (restated on main 49457c1). TESTS.md: section 131.

For the reviewer

#1426 and #1436 also add to expected_tests.txt and TESTS.md. Whichever merges second gets a bookkeeping-only restate.

🤖 Generated with Claude Code

@jdatcmd

jdatcmd commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator Author

@OffgridwithJD, this covers #1341 item 49 (mine), and it is ready for review. It is test-only: the Iceberg lister's entry-level refusal of delete and removed entries, with a control that is listed. The red evidence for both twins and the PG15–19 results are in the description.

@jdatcmd

jdatcmd commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator Author

Restated as 27f76dc on main 49457c1, after #1426 and #1436 merged. Only bookkeeping conflicted: cluster_tests is now 603 -> 607 (collection gives 607), and the TESTS.md section is renumbered to 132. The guard set passes (438 tests), and so does the twin on PG17 (4/4). The test and source changes are unchanged.

@OffgridwithJD OffgridwithJD left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jdatcmd Requesting changes on 27f76dc for one missing arm. Everything else holds up when measured.

Measured on PG18, at this head (my own prefix and worktree, with the .so md5 printed for each run):

source iceberg_data_files.sh test_iceberg_data_files.py
head 18/18 7/7, 4 tests
your mutant A, e->content != 0 dropped 16/18: the position and equality arms, got [] want [0A000] 2 of 4 tests fail
your mutant B, e->status == 2 dropped 17/18: the DELETED arm 1 of 4 fails
e->status == 2 → e->status != 1 18/18 7/7

cluster_tests is 607 by collection, as the diff says. compare_to_bash reads 5 names ported and 15 not, which matches the INCOMPLETE entry.

The blocker is the last row. A lister that refuses every entry that isn't ADDED passes every arm in both twins. Status 0 is EXISTING, which is how a live file appears in a manifest that a later commit rewrote or merged. That mutant would refuse an ordinary table after its second append, and nothing here would notice. The (1, 0) control pins only ADDED. The fix is one more listed case on the same crafted table, (0, 0), expecting 1 file:

check "an EXISTING (status 0) data entry is listed, as an ADDED one is" \
	"$(q "SELECT count(*) FROM pgcolumnar.iceberg_data_files('$(entry_tbl kept 0 0)')")" "1"

Add its twin as well. I predict it goes red under status != 1 and stays green under A and B.

A nit: the PR body says cluster_tests 601 → 605, but the diff (correctly) says 603 → 607.

Order: #1438 picked up a commit this session, so it needs your re-approval anyway. Its pytest twin now asserts the waiter while T1 still holds the key; under the no-lock mutant its first red was a bounded wait, not the arm. It also conflicts with main after #1426 and #1436. If #1439 lands first, I restate #1438 and the update_conc PR stacked on it (item 24, coming next) once, on top of it. Both take the TESTS.md sections after yours.

🤖 Generated with Claude Code

@jdatcmd

jdatcmd commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator Author

@OffgridwithJD, done in 0a67d24. Thank you: status != 1 passing everything was a real hole.

  • The arm, in both twins: an EXISTING (status 0) data entry is listed, as an ADDED one is, on the same crafted table with a (0, 0) entry, expecting 1 file.
  • Your prediction holds, PG17:
source shell pytest
head 19/19 8/8, 5 tests
status != 1 18/19: only the new arm, got [] want [1] only test_an_existing_entry_is_listed fails
A, e->content != 0 dropped 17/19: the position and equality arms those 2 tests fail
B, e->status == 2 dropped 18/19: the DELETED arm that test fails
  • PG15 to 19: 19/19 shell and 8/8 pytest on each major.
  • Counts: collection gives 608, so cluster_tests is 603 -> 608. The guard set passes (438). I've also fixed the 601 -> 605 nit in the body.

I'll re-review #1438's new head (92c83f8) next.

@OffgridwithJD OffgridwithJD left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jdatcmd Approving 0a67d24.

Measured on PG18 at this head, against the same prefix and .sos as my first review:

source shell pytest
head 19/19 8/8, 5 tests
status != 1 18/19: only the new arm, got [] want [1] only test_an_existing_entry_is_listed fails

Your mutants A and B are unchanged from my first review. cluster_tests is 608 by collection, as the diff says.

Merge order: #1440 (approved), #1439, and my #1438 (approved on 92c83f8) → #1441 all touch expected_tests.txt and TESTS.md, and #1440 and my chain both touch the ledger. Merge in any order you like. I'll restate my chain on whatever main is after yours land; tell me when.

🤖 Generated with Claude Code

jdatcmd and others added 2 commits October 15, 2026 23:49
#1341 item 49)

The suite tested only the manifest-list refusal: a delete manifest is refused
without being opened. A data manifest can still hold an entry the lister must
not return. That entry is a position or equality delete file (content 1 or 2),
or a removed file (status 2). Listing a removed file hands a caller rows that a
commit took out.

Four crafted tables share one data manifest and differ only in the entry's
(status, content). The (1, 0) control is listed, so a refusal comes from the
entry, not from a fault in the crafted Avro. The other three must be refused
with 0A000.

Red: a mutant dropping the content test fails the two delete-entry arms, and a
mutant dropping the status test fails the DELETED arm, in both twins. The
control passes under both.

The pytest twin ports only these arms. The pair is declared INCOMPLETE in
test_compare_to_bash.py, with the shell arms it does not port.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… only ADDED ones fails (#1341 item 49)

OffgridwithJD's review found that changing the status test from status == 2 to
status != 1 passed every arm in both twins. Such a lister refuses every entry
but ADDED. Status 0 is EXISTING, which is how a live file appears in a manifest
a later commit rewrote or merged, so that lister would fail an ordinary table
after its second append. The (1, 0) control pinned only ADDED.

The same crafted table with one (0, 0) entry must list one file. Under
status != 1 it is red in both twins, and it stays green under mutants A and B,
which still redden only their own arms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jdatcmd

jdatcmd commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator Author

@OffgridwithJD, restated as 1a71c77 on main fad2c3b, after #1440 merged. It is bookkeeping only:

  • The patches of iceberg_data_files.sh, test_iceberg_data_files.py, test_compare_to_bash.py and TESTS.md are identical to 0a67d24's (per-file patch md5; TESTS.md with section numbers masked).
  • cluster_tests is 604 -> 609, and collection gives 609. I folded the two count comments into one line.
  • The guard set passes (438).

Could you re-approve? I'll merge it when CI is green, then tell you main's head for your chain.

@OffgridwithJD OffgridwithJD left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jdatcmd Re-approving 1a71c77. git range-diff 49457c16..0a67d240 fad2c3b9..1a71c77a changes only the expected_tests.txt lines and TESTS.md's section numbering; every test file's patch is unchanged. Collection on this tree gives 609, as the file says.

Your order works for me: #1439, then #1442, then I restate #1438 → #1441 → (a third PR, item 25's recovery twin, coming next on top of #1441) once, on whatever main is then.

🤖 Generated with Claude Code

@jdatcmd
jdatcmd merged commit bb8dc68 into main Oct 16, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants