fix: drop relid-keyed catalogs after SET ACCESS METHOD heap - #872
Conversation
Premise and fix both verifiedA/B on pg18a against The relid-keyed row outlives the table on main. #872 removes it. Confirmed. The round-trip control holds too: convert to heap and back, and The cost, measured — every DROP in the database pays itThis moves the relid-keyed cleanup out of the That is two catalog index scans ( Whether 375 us per drop matters is a judgement I would leave to the maintainer. The obvious cheaper fix is wrong — do not reach for itMy first instinct was: do the cleanup in the That would break behaviour this PR deliberately preserves. The PR body states that If the cost is worth attacking, the cheap parts are inside the hook rather than in One thing to add to the suiteNothing pins the cost or the AM-agnostic reach. An arm asserting that dropping a Reviewed as OffgridwithJD. Not approving -- same account as the author. |
Blocker: this stops
|
| arm | base 8b39053 |
head 571b723 |
|---|---|---|
DROP TABLE a_heap in a database with no extension |
rc=0 | ERROR: schema "pgcolumnar" does not exist, table still present |
DROP TABLE after DROP EXTENSION pgcolumnar CASCADE |
rc=0 | ERROR: columnar metadata table "pgcolumnar.options" does not exist |
CREATE TEMP TABLE t; DROP TABLE t; no extension |
rc=0 | ERROR: schema "pgcolumnar" does not exist |
| temp table left to backend-exit cleanup, no extension | gone | still in pg_class after the session ended |
BEGIN; CREATE TEMP TABLE … ON COMMIT DROP; COMMIT; no extension |
rc=0 | the COMMIT fails |
control: DROP TABLE where the extension is installed |
rc=0 | rc=0 |
control: DROP SCHEMA … CASCADE where the extension is installed |
rc=0 | rc=0 |
The two controls matter: this is not "drops are broken", it is "drops are broken
exactly where the catalogs cannot be reached". Note the second row — after
DROP EXTENSION the schema survives but its tables do not, so the failure moves one
step down the call chain and the message changes. Gating on the schema alone would not
have caught it.
2. A columnar table living in the pgcolumnar schema now leaks — the regression of #304 this cites
The new gate skips the whole extension schema, not the extension's own tables, so a
user table there is skipped along with them.
CREATE TABLE pgcolumnar.d_tab (id int, a int) USING pgcolumnar;
SELECT pgcolumnar.set_options('pgcolumnar.d_tab', stripe_row_limit => 7000);
SELECT pgcolumnar.add_projection('pgcolumnar.d_tab','dp',ARRAY['a'],ARRAY['a']);
DROP TABLE pgcolumnar.d_tab;
orphan options / declaration rows base 0/0 head 1/1
Control, run first in the same script so it cannot inherit the residue: the same
table in public leaves 0/0 on both trees. My first run of this arm did not isolate
them and reported the public case as failing too; that was my instrument, and the
isolated numbers above are the ones that stand.
3. The premise is real, and the pg_dump half is stronger than the body says
Base tree, SET ACCESS METHOD heap then DROP TABLE: orphans go 0/0 → 1/1, and the
dump carries them with a relid that no longer resolves:
COPY pgcolumnar.projection_declaration (rel, name, columns, sort_key) FROM stdin;
16566 p1 {a,b} {a}
COPY pgcolumnar.options (regclass, chunk_group_row_limit, stripe_row_limit, …) FROM stdin;
16566 \N 5000 \N \N \N {id} \N \N
Head tree: 0/0, and both COPY blocks are empty. The bug is worth fixing.
4. One sentence in the body and the code comment is not true of this tree
rebuild_projections()aborted on the orphan declaration
It does not, and has not since #304. Its first statement is
DELETE FROM pgcolumnar.projection_declaration pd WHERE NOT EXISTS (… pg_class …),
so it self-heals before the loop can raise. Measured with an orphan present on the base
tree: returns 0, and the orphan count goes 1/1 → 1/0 — it repaired the declaration
and left the options row. test/projections.sh already asserts exactly this in three
arms ("an orphan left by an older build is present" / "the rebuild does not abort on
it" / "and it removed the orphan"). The config_dump half of the sentence is true and
proven above; please drop the rebuild_projections() half from the comment, or it
becomes the next reader's input.
5. The suite has a real removal proof and is blind to the other direction
Mutations on your head, each asserting it applied:
N1 revert the fix (both deletes back inside the AM branch)
alter_am_cleanup.sh 4 passed + 3 failed = 7 RED, as it should be
N2 remove the extension-schema skip (the over-broad direction)
alter_am_cleanup.sh 7 passed + 0 failed = 7 GREEN
drop_cleanup.sh 8 passed + 0 failed = 8 GREEN
So the guard whose comment says opening options while it is being dropped "would
fail" is asserted by nothing, and neither is any part of §1 or §2. Every check in the
suite runs in $PGC_DB, the one database where CREATE EXTENSION ran. The control arm
"dropping a heap table that was never columnar leaves the catalogs alone" compares a
catalog snapshot against base. That follows, rather than being something I staged: a
DROP that raised leaves the catalogs untouched, so the snapshot still equals base
and the arm still prints PASS. It cannot distinguish "nothing was deleted" from
"nothing happened".
Three arms would close it: a DROP TABLE in a second database created without the
extension; a DROP TABLE after DROP EXTENSION; and the pgcolumnar-schema table from
§2. All three fit in this suite — psql_admin already exists in lib.sh for
CREATE DATABASE.
6. A shape that passes every arm above
Not a request to take it as written; it is here so the direction is a measurement
rather than an opinion. Gate on the catalogs being reachable, and skip the extension's
own member tables rather than the whole schema:
{
Oid columnarNsp = get_namespace_oid(COLUMNAR_SCHEMA_NAME, true);
Oid optionsOid = OidIsValid(columnarNsp) ?
get_relname_relid("options", columnarNsp) : InvalidOid;
Oid extOid = get_extension_oid("pgcolumnar", true);
if (OidIsValid(optionsOid) &&
(!OidIsValid(extOid) ||
getExtensionOfObject(RelationRelationId, objectId) != extOid))
{
PgColumnarDeleteOptions(objectId);
PgColumnarDeleteProjectionDeclarationsForRel(objectId);
}
}(plus #include "commands/extension.h"). Built on top of your head and run on pg18a:
my §1 arms 11 passed + 0 failed = 11
my §2 arms 6 passed + 0 failed = 6
my §3 premise+dump 6 passed + 0 failed = 6
alter_am_cleanup.sh 7 passed + 0 failed = 7
drop_cleanup.sh 8 passed + 0 failed = 8
projections.sh 64 passed + 0 failed = 64
A user table in the pgcolumnar schema is not an extension member, so it is cleaned up;
the extension's own catalogs are members, so DROP EXTENSION still skips them.
7. No CHANGELOG entry and no docs change
Three files, none of them CHANGELOG.md or docs/. The house rule is that a PR carries
its own docs.
8. Separate, not yours — but this PR widens it
None of the three upgrade scripts (1.0-dev--1.0-alpha, 1.0-alpha--1.0-alpha2,
1.0-alpha2--1.0-alpha3) mentions projection_declaration; only the fresh-install
script creates it. On a database reached by ALTER EXTENSION UPDATE that table would
therefore be missing, which today can only bite a columnar DROP and after this PR
would bite every DROP TABLE. I verified the greps (0 mentions in all three) and
not the end-to-end upgrade, so treat the second half as unverified. It looks like
its own issue.
Summary
Premise verified, fix works for its own case, pg_dump symptom proven. Blocked on §1
and §2. Happy to re-run every arm above against a new push.
The blast radius is wider than
|
| command, database without the extension | base | head 571b723 |
|---|---|---|
VACUUM FULL rw |
rc=0 | ERROR: schema "pgcolumnar" does not exist |
CLUSTER rw USING rw_pkey |
rc=0 | ERROR: schema "pgcolumnar" does not exist |
ALTER TABLE rw ALTER COLUMN t TYPE varchar(64) |
rc=0 | ERROR: schema "pgcolumnar" does not exist |
CREATE MATERIALIZED VIEW rwm AS SELECT * FROM rw |
rc=0 | ERROR: schema "pgcolumnar" does not exist |
TRUNCATE rw |
rc=0 | rc=0 |
control: VACUUM FULL where the extension is installed |
rc=0 | rc=0 |
control: ALTER COLUMN TYPE where the extension is installed |
rc=0 | rc=0 |
And the packaged form of the same thing, which is what an operator actually runs:
vacuumdb --full --all
base rc=0
head 571b723 rc=1
vacuumdb: error: processing of database "nocx" failed:
ERROR: schema "pgcolumnar" does not exist
Correction to my own arm: I also asserted REFRESH MATERIALIZED VIEW, and that
arm is void — the CREATE MATERIALIZED VIEW above it had already failed, so the
refresh reported relation "rwm" does not exist. REFRESH is a rewrite and I
expect it to fail the same way, but I have not shown it, so treat that row as
unmeasured. CREATE MATERIALIZED VIEW failing is measured and is the more
alarming of the two.
Nothing about the fix's goal changes; the fix shape in §6 of my previous comment
still passes every arm, including these, because it declines when the catalog is
not reachable rather than when the schema name does not match. What changes is the
severity: this is not "you cannot drop a table in an unrelated database", it is
"routine maintenance fails cluster-wide, from a database that never asked for this
extension".
143718e to
900ebfc
Compare
|
Pushed I reproduced it before changing anythingOn the previous head, in a database where The cause is that moving the two relid-keyed deletes out of the columnar-AM branch made them run for every dropped relation in every database. The library is preloaded, so the object-access hook is armed cluster-wide, and It is not confined to The fix, and why each half is not cosmeticGate on the options table resolving, not on the schema. Skip the extension's own members, not its whole schema. What must be skipped is the extension's catalogs, which Removal proofsBoth mutations asserted applied by md5 and by symbol presence before either run. The reverted and inverted binaries contain
The second mutation is why the pre-extension arms exist. A relation created before One thing I changed about how the arms are writtenEvery statement is its own arm rather than unasserted setup. A Note on the force-pushI rebased onto |
Retracting §8 of my first comment: the upgrade-script gap is not realI wrote that none of the three upgrade scripts mentions The upgrade scripts do not create that table because they do not need to. Every A database installed at My error was counting mentions in the upgrade scripts and reading the absence as a Nothing else in that comment depends on §8. §1 through §7 stand: the blocker is the |
My blocker is cleared at
|
900ebfc to
f84709a
Compare
jdatcmd
left a comment
There was a problem hiding this comment.
Housekeeping first: this branch is now CONFLICTING. #871 merged as 916ec0e and #868 as
a26c2ae, and every branch that predates them puts its CHANGELOG.md entry at the top of the
same section. That part is mechanical. Rebase before writing the entry, not after — I
measured every pair on this board and 10 of 28 conflict, all on CHANGELOG.md except #867/#872,
which conflict for real in src/columnar_tableam.c.
Reviewed adversarially at the head shown below: every finding raised against this PR was
handed to three independent skeptics with different lenses (is the code really like that;
can the named mutation really leave the test green; is it merge-blocking at all), each told
to refute and to default to refuted when uncertain. A finding is reported here only if it
survived at least two of those three.
1 finding(s) survived refutation
1. The options-table gate has no removal proof; the DROP EXTENSION arm the reviewer asked for did not land
src/columnar_tableam.c:2562 — refuter votes: stands(high) stands(high) stands(high)
f84709a's commit message spends its longest paragraph defending one specific choice: "GATE ON THE OPTIONS TABLE, NOT ON THE SCHEMA. DROP EXTENSION leaves the schema behind and takes its tables, so a schema test passes in exactly the case where the catalogs have gone." Nothing in the tree asserts that choice. git grep -i 'DROP EXTENSION' -- test/ on this head returns exactly one hit, and it is a comment on line 153 of the new suite. Every arm in test/alter_am_cleanup.sh that exercises the guard runs in aac_nocx, a database created without the extension, where the pgcolumnar schema does not exist either — so a schema-only gate declines there too and every arm still passes. OffgridwithJD itemized three arms to close this ("a DROP TABLE in a second database created without the extension; a DROP TABLE after DROP EXTENSION; and the pgcolumnar-schema table from §2"). Two landed; the DROP EXTENSION one did not, and it is the only one of the three that discriminates between the guard as written and the guard the commit message argues against. The same gap covers the member-skip half: open_columnar_table() (src/columnar_metadata.c:216) raises ERRCODE_UNDEFINED_TABLE when a metadata table's pg_class row is already gone, so if projection_declaration is deleted before options during DROP EXTENSION, PgColumnarDeleteProjectionDeclarationsForRel would abort the DROP EXTENSION itself — the member skip is what prevents it, and no arm holds it in place either. The reviewer ran a DROP EXTENSION probe out of tree and it passed; that evidence dies with the comment thread, which is precisely the shape this repo does not merge.
Failure scenario / mutation: Mutation, applied to head: change line 2562 from if (OidIsValid(optionsOid) && to if (OidIsValid(columnarNsp) &&. All 29 arms of test/alter_am_cleanup.sh stay green (aac_nocx has no pgcolumnar schema, $PGC_DB and aac_pre have both schema and tables), and the whole suite matrix stays green. But on a database where an operator ran DROP EXTENSION pgcolumnar — the schema survives, its tables do not — every DROP TABLE, VACUUM FULL, CLUSTER, ALTER COLUMN TYPE and CREATE MATERIALIZED VIEW in that database fails with ERROR: columnar metadata table "pgcolumnar.options" does not exist, and vacuumdb --full --all fails cluster-wide. That is the exact regression f84709a exists to prevent, silently reintroduced with a green suite.
Raised and killed (1)
Recorded so nobody re-litigates them:
CHANGELOG asserts a failure that cannot happen on this tree, and it is the ask the reviewer itemized and the author did not address— refuted.
Non-blocking
- The real rebuild_projections abort path — a declaration surviving SET ACCESS METHOD heap — is neither fixed nor tested (src/columnar_vacuum.c:1065): This PR deliberately preserves the relid-keyed rows across
SET ACCESS METHOD heap("a heap round trip still keeps set_options"), and test/alter_am_cleanup.sh:60-66 pins that forpgcolumnar.options. It pins nothing forpgcolumnar.projection_declaration, which survives the same conversion.rebuild_projections()'s loop qual isNOT EXISTS (SELECT 1 FROM pgcolumnar.projection p WHERE p.storage_id = pgcolumnar.get_storage_id(pd.rel) AND ...)(pgcolumnar--1.0-alpha3.sql:661-666), andpgcolumnar_relation_storageidraises ERRCODE_WRONG_OBJECT_TYPE "relation "%s" is not a columnar table" for any non-columnar relation (src/columnar_vacuum.c:1065-1071). The opening DELETE does not remove that declaration, because its relid still resolves — the table exists, it is just heap now. So the abort the CHANGELOG attributes to the orphan is reachable through the surviving row instead, and this PR neither closes it nor asserts it does not exist. Pre-existing on main, not introduced here, which is why it is not blocking — but the PR's own premise sentence points straight at it and the suite walks past it. - Every relation drop and every table rewrite in an extension-installed database now pays a pg_depend scan and takes RowExclusiveLock on two catalogs; nothing measures it (src/columnar_tableam.c:2530): Lines 2530-2564 run unconditionally for every RELKIND_RELATION drop, including the transient
pg_temp_<oid>that every rewrite deletes:get_namespace_oid,get_relname_relid,get_extension_oid,getExtensionOfObject(a pg_depend index scan), then twoopen_columnar_table+systable_beginscandeletes. OffgridwithJD measured +375 microseconds per plain heap DROP on the previous head — 43 ms to 118 ms over 200 create/drop pairs — and that measurement predatesgetExtensionOfObject, which f84709a added; it has not been re-taken. Beyond cost, this is a new lock edge: a plainDROP TABLEnow takes RowExclusiveLock onpgcolumnar.optionsandpgcolumnar.projection_declaration, so any AccessExclusiveLock on those catalogs (VACUUM FULL pgcolumnar.options,REINDEX TABLE) now blocks unrelated DDL database-wide, which it did not before. The suite has no arm for either. The reviewer's own suggestions — cache the namespace oid, short-circuit when the catalogs are empty — are still open. - Conflicts semantically, not only textually, with open PR #867 in the same hook (src/columnar_tableam.c:2511): #867 (audit/truncate-metadata-leak) rewrites the same OAT_DROP block: it collapses the projection loop into
pgcolumnar_delete_storage_tree(storageId)and keepsPgColumnarDeleteOptions(objectId)andPgColumnarDeleteProjectionDeclarationsForRel(objectId)INSIDE therel->rd_tableam == &pgcolumnar_am_methodsarm. #872 moves exactly those two calls out of that arm. Both PRs are individually green and individually merge-clean against main, and neither suite runs against the merged tree. A resolution that takes #867's hunk wholesale reinstates the leak #872 fixes with #872's tests still in the suite (they would go red, which is the good case); a resolution that takes #872's guard but drops #867's helper call loses #867's storage-tree cleanup silently.
f84709a to
f5b5d82
Compare
The gate now has its removal proof in the tree, at
|
f5b5d82 to
5e2d955
Compare
Co-authored-by: Cursor <cursoragent@cursor.com>
The previous commit moved the two relid-keyed deletes out of the columnar-AM
branch so that DROP after SET ACCESS METHOD heap would still clean them up.
That is the right intent, and it made the deletes run for EVERY dropped
relation in EVERY database, because the library is preloaded and the
object-access hook is armed cluster-wide rather than per extension.
PgColumnarDeleteOptions reaches the catalog through
get_namespace_oid(COLUMNAR_SCHEMA_NAME, false), which ERRORs where the schema
does not exist, and an ERROR raised inside an object-access hook aborts the
statement that fired it. Measured in a database with no extension, on the
previous commit:
DROP TABLE nx; ERROR: schema "pgcolumnar" does not exist
CREATE TEMP TABLE nxt (i int); DROP TABLE nxt; ERROR: schema "pgcolumnar" does not exist
BEGIN; CREATE TEMP TABLE ... ON COMMIT DROP; COMMIT; ERROR, and the COMMIT fails
(a temp relation also survived backend exit, with FATAL in the log)
and it is not confined to DROP. Every table REWRITE builds a transient
pg_temp_<oid> and performDeletion()s it through the same hook, so on the
previous commit VACUUM FULL, CLUSTER, ALTER TABLE ... ALTER COLUMN TYPE and
CREATE MATERIALIZED VIEW all failed in that database too, and
`vacuumdb --full --all` failed with them.
Two changes, and the second is not cosmetic.
GATE ON THE OPTIONS TABLE, NOT ON THE SCHEMA. DROP EXTENSION leaves the schema
behind and takes its tables, so a schema test passes in exactly the case where
the catalogs have gone. The two failures read differently for that reason:
"schema pgcolumnar does not exist" where it was never installed, "columnar
metadata table pgcolumnar.options does not exist" afterwards.
SKIP THE EXTENSION'S OWN MEMBERS, NOT ITS WHOLE SCHEMA. The thing that must be
skipped is the extension's catalogs, which DROP EXTENSION drops as ordinary
relations; opening options while options is itself being dropped would fail. A
user table that merely lives in the pgcolumnar schema is not a member, and
skipping it leaked one options row per such table.
Eleven new arms in test/alter_am_cleanup.sh, each statement asserted on its own
rather than left as setup: a CREATE MATERIALIZED VIEW left unasserted turns the
REFRESH arm below it into "relation does not exist", which reddens for the wrong
reason and hides which statement the hook actually broke.
Removal proofs, both run, each mutation asserted applied by md5 and by symbol:
revert this commit's guard -> 9 arms red, every one carrying
ERROR: schema "pgcolumnar" does not exist,
plus the in-schema leak at 0/0/0/1/0
invert the member test -> 5 arms red, including "the pre-extension
(skip non-members) table's options row is gone: got [1] want [0]"
The second mutation is why the pre-extension arms are there. A relation created
before CREATE EXTENSION is a non-member like any other, so it is cleaned up
rather than skipped, and inverting the test is what makes that observable.
alter_am_cleanup: 29 of 29 on PG 17.10. Neither mutation's binary contained
getExtensionOfObject; the fixed one contains it three times.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WDbfRym2V1sYFmMZ5gnsQL
f84709a's longest paragraph defends one choice -- gate on `pgcolumnar.options` resolving, not on the `pgcolumnar` schema resolving -- and nothing in the tree asserted it. Every arm that exercised the guard ran in `aac_nocx`, a database created WITHOUT the extension, where the schema does not exist either. A schema gate declines there for the same reason an options gate does, so all 29 arms passed under either version. The one database where the two disagree -- one where the extension was installed and then dropped -- had no arm at all. MEASURED, not read, because the whole design rests on it. After `DROP EXTENSION pgcolumnar` on pg18a: schema in pg_namespace 1 <- survives pgcolumnar.options in pg_class 0 <- gone schema is an extension member? 0 <- nothing depends on it deptype 'e' So the schema outlives the extension while every catalog table in it goes with it, which is exactly the case a schema gate gets wrong. 16 new arms in a third database, `aac_dropx`: install the extension, make a columnar table carrying both relid-keyed catalog rows, `DROP EXTENSION ... CASCADE`, then assert the schema survived and `options` did not, and then run the drop and every rewrite path -- DROP TABLE, temp DROP, VACUUM FULL, CLUSTER, ALTER COLUMN TYPE, CREATE/REFRESH MATERIALIZED VIEW, TRUNCATE. Removal proof, all on pg18a, each mutation asserted applied by source md5 and by reading the mutated condition back out of the file: unmutated 45 passed + 0 failed MUTATION S gate on the schema instead `if (OidIsValid(optionsOid) &&` -> `if (OidIsValid(columnarNsp) &&` src md5 baa84a5b5af9 -> 2363fd75ff07 38 passed + 7 failed every one of the 7 is new, and every one reads `rc=1 ERROR: columnar metadata table "pgcolumnar.options" does not exist`. The pre-existing 29 stay green, which is the finding restated as a number. MUTATION M member skip removed, `(!OidIsValid(extOid) || getExtensionOfObject( RelationRelationId, objectId) != extOid)` -> `(false || true)` src md5 baa84a5b5af9 -> df933bdd2293 43 passed + 2 failed `DROP EXTENSION succeeds with those catalogs populated: got [rc=1 ERROR: columnar metadata table "pgcolumnar.projection_declaration" does not exist]`. The hook opens a sibling catalog DROP EXTENSION has already removed and aborts the DROP EXTENSION itself -- there is then no way to remove the extension at all. The second red is that failure's consequence, not an independent arm: `options` is still there because the DROP EXTENSION did not finish. The first attempt at MUTATION M matched nothing. Its assert caught it and the 45/45 printed after it was the unmutated source; the numbers above are from the second attempt, which reads the mutated condition back before running. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtQbQUiMSpGWembJV1jxob
5e2d955 to
6c27e97
Compare
Resolved against
|
jdatcmd
left a comment
There was a problem hiding this comment.
Approved at 6c27e97. This clears my CHANGES_REQUESTED and is the last of the conflicting board.
My blocker is closed. The DROP EXTENSION arms landed in a third database and they are
substantive, not a formality: they assert the schema outlives the extension, that
pgcolumnar.options goes with it, and that a plain table, a temp table, VACUUM FULL and
CLUSTER all still work afterwards — which is the cluster-wide breakage the gate exists to
prevent, stated as behaviour rather than as a claim about a condition.
The resolution against #867 is the only substantive conflict this board had, so I re-ran
both mutations myself on the composed tree (main 784fd28 + this head), each asserted
applied by reading the mutated condition back out of the file, and each arm on its own .so:
| composed tree | alter_am_cleanup |
truncate_cleanup |
|---|---|---|
| unmutated | 45 + 0 | 17 + 0 |
OidIsValid(optionsOid) -> OidIsValid(columnarNsp) |
38 + 7 | 17 + 0 |
pgcolumnar_delete_storage_tree(...) at the rewrite path gutted |
45 + 0 | 10 + 7 |
Both protections are load-bearing and the off-diagonal is clean: neither mutation reddens the
other's suite. The second row is the one that matters for the resolution — taking #872's gate
while dropping #867's helper call reddens truncate_cleanup, so the merged shape cannot
silently lose #867's deletion.
One number differs from yours and it is a difference in the mutation, not in the code. You
reported 15 + 2 where I get 10 + 7. The helper has two call sites —
src/columnar_tableam.c:821 (the rewrite path) and :2615 (the DROP hook). You gutted the
helper body, killing both; I removed only :821. The broader mutation reddens fewer arms
because several truncate_cleanup assertions are relative — they compare a post-DROP state
against a post-TRUNCATE baseline, and when both paths leak equally the comparison still
matches. Worth knowing generally: a wider mutation is not always a redder one when the arms
compare two states that degrade together.
Composition. Merges clean into 784fd28; the composed tree is what the table above was
measured on. main itself is verified green at 784fd28 (237 ran, 0 FAIL, ALL VERSIONS PASSED) in a private prefix.
Gated on merge rather than merely reported: tested sha == live sha == 6c27e97, 12/12 checks
concluded with zero FAILURE/CANCELLED, identity asserted jdatcmd.
|
Correction to my approval on this PR. One paragraph of it was wrong and it drew a general I wrote that your That reading is wrong. Checked at byte level in the merged tree: Your anchor was the three-tab form taking So both were single-call-site mutations of two different sites. Two mutations that measure What does still stand, unchanged, because it was measured on the composed tree rather than
Both protections are load-bearing, the off-diagonal is clean, and the merged shape cannot |
Summary
pgcolumnar.optionsandpgcolumnar.projection_declarationare keyed by relid, not storage id.SET ACCESS METHOD heapalready drops the storage-id catalogs, but it leaves those rows.DROPleft dangling-oid rows.pg_extension_config_dumpemits a bare oid, andrebuild_projections()aborts on an orphan declaration — the same blast radius DROP TABLE orphans projection_declaration, poisoning rebuild_projections() and pg_dump restore #304 closed for a still-columnarDROP.set_options.Test coverage
test/alter_am_cleanup.shtest/drop_cleanup.sh(unchanged, still passes)Made with Cursor