fix(pg_introspect): read FKs from pg_constraint so read-only roles get 'references' edges (#1746)#1761
Open
rithyKabir wants to merge 1 commit into
Open
Conversation
…t 'references' edges (Graphify-Labs#1746) information_schema.referential_constraints only shows constraints where the current user has WRITE access to the referencing table (owner or a privilege other than SELECT — per the PostgreSQL docs). A read-only introspection role therefore gets zero FK rows, while tables, views and routines all still appear (SELECT is enough for those views) — so --postgres built a graph with contains/reads_from edges but not a single 'references' edge, exactly as reported. Query pg_catalog.pg_constraint (contype = 'f') instead, which is not privilege-filtered. This also fixes a latent second bug: constraint names in PostgreSQL are only unique per table, so the old name-based key_column_usage joins cross-matched same-named constraints on sibling tables (verified live: two tables sharing a 'fk_owner' constraint produced columns = {user_id,user_id,user_id,user_id}). pg_constraint keys by oid, and conkey/confkey unnest WITH ORDINALITY preserves composite-FK column order. Verified against a live PostgreSQL 16 with a SELECT-only role: referential_constraints returned 0 FK rows, the new query returned all 3 (single-column, composite, and same-named sibling constraints), and introspect_postgres emitted all 3 references edges end-to-end. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1746.
Root cause
introspect_postgresreads foreign keys frominformation_schema.referential_constraints— but per the PostgreSQL docs:A read-only introspection role — exactly what hosted providers like Supabase hand out, and what
pg_introspect's ownREAD ONLYtransaction encourages — therefore gets zero FK rows, while tables/views/routines all still appear (SELECT is enough for those views). The graph then builds "successfully" withcontains/reads_fromedges but not a singlereferencesedge, matching the reported counts (459 contains + 183 reads_from, 0 references). This is also why file-based extraction of the same schema's migrations does producereferencesedges — the gap is specific to the live-introspection path.Reproduced live on PostgreSQL 16 with a SELECT-only role:
referential_constraints→ 0 rows;pg_catalog.pg_constraint(contype='f') → all 3.Fix
Query
pg_catalog.pg_constraintinstead — system catalogs are not privilege-filtered. The query returns the same 7-column shape, so DDL generation is untouched.This also fixes a latent second bug: PostgreSQL constraint names are only unique per table, and the old name-based
key_column_usagejoins cross-matched same-named constraints on sibling tables. Verified live: two tables each with a constraint namedfk_ownerproducedcolumns = {user_id,user_id,user_id,user_id}(malformed FK DDL) even for a privileged user.pg_constraintkeys by oid, andunnest(conkey) WITH ORDINALITYpreserves composite-FK column order.Verification
fk_ownerconstraints on two sibling tables, introspected as a SELECT-only role: all 3referencesedges emitted end-to-end, composite FK produces exactly one edge, bothfk_ownerFKs attributed to the correct tables.referential_constraintsand still yields thereferencesedge; the test mock now records executed queries.tests/test_pg_introspect.py7/7, plustest_extract.py+test_multilang.py(172 total) green; ruff clean.🤖 Generated with Claude Code