Mars DB exporter can silently lose schema sections under a lower-privilege DSN (PLUTO-772 class)
PLUTO-772-derived audit of mars/db/export-db.py. The exporter uses information_schema.columns, table_constraints/key_column_usage/constraint_column_usage, triggers, and routines; these readers are privilege-relative, while pg_catalog is not equivalently filtered. Current actual exporter DSN is mars/.env.local DATABASE_URL, parsed by get_database_url(); read-only connection current_user=postgres/current_database=postgres on 2026-10-06. Same-session counts info_schema/catalog match: routines 74/74, PK 47/47, FK 109/109, UNIQUE 3/3, distinct triggers 46/46, columns 1828/1828. Checked-in schema.md still has PK/trigger/index sections (45 tables, 90 views, 69 functions); 180 schema.md commits scanned from first 14 functions/36 PK/35 trigger annotations to last 69/46/71, no abrupt >15 function/>10 PK/>10 trigger collapse. So NOT presently degraded under the actual privileged DSN, but the exporter will silently emit less if future DSN/fallback uses an unprivileged role. Fix readers to pg_catalog, print connected role, and fail before writing schema.md/reference TSV if section counts collapse against committed snapshot; tests must force underprivileged mismatch red and prove no artifact write. Preserve scoped reference-data mirror. This is a preventive fix-forward row, not a claim of current missing schema objects. Cite PLUTO-772.
Activity
-
Scope clarification from pmmaster-evolutiva-cc (PLUTO-772/VENUS-453 parity): columns are NOT exempt. Current 1828/1828 info_schema/catalog equality is due to postgres privilege, not a theorem. MARS-800 implementation must move columns to pg_catalog AND include column counts in the fail-before-write completeness guard. Forced-red lower-privilege fixture must remove SELECT from one table while other sections remain visible, proving columns alone trigger refusal. No exporter edit yet.
-
Pushed 44317ef3c67d (tooling-only, app version remains 2.85.18). Mars exporter now reads columns/constraints/triggers/routines via pg_catalog; TSV header comes from the actual SELECT projection; schema.md records connected role and section counts and refuses any structural count decrease without --allow-shrink. Regenerated schema.md from production read-only postgres role: 48 tables, 91 views, 73 functions, 1840 columns, 48 PK columns, 109 FKs, 76 trigger events. Five focused Python tests including forced column-only decrease and schema-file unchanged on refusal; push gate 1014/1014. Independent lower-privilege live DB export not exercised; test simulates a one-column visibility loss.
-
Read-only live role contrast after 44317ef: SET LOCAL ROLE anon inside READ ONLY transaction returned information_schema public column count 0 while pg_catalog returned 1840 on the same connection; ROLLBACK. Privileged exporter run generated schema.md with all 1840 columns and TSV 194 active lookup rows. Focused Python 5/5, including simulated one-column visibility loss refusing before schema write; full push gate 1014/1014. Exact-commit Vercel status success; production health deploymentId dpl_ABUBStqHjp881fcSgt9x8wQmiLGQ matches commit target, version unchanged 2.85.18. No production DDL/data write and no full exporter invocation under anon (lookup SELECT would fail); reference-data TSV timestamp-only diff excluded.
-
Done at 44317ef3c67d; exporter/catalog readers, reference TSV projection, role/count header, fail-before-write guard and tests shipped. Production read-only export and same-session anon visibility contrast verified; exact Vercel deployment matched. Tooling-only commit, app remains v2.85.18. Full exporter under anon unexercised because active lookup read needs privileges; no schema/data write.