Skip to content

pg_class assigns namespaces by name prefix: hides user tables named pg_*, leaks internal idx_* indexes into \di #102

Description

@erans

Split out of #88, where it was scoped out to keep that change off the pg_class path one week after #87 landed there.

Problem

Migration v28 assigns namespaces in the pg_class view with a prefix test (src/migration/registry.rs):

CASE
    WHEN name LIKE 'pg\_%' ESCAPE '\' THEN 11
    WHEN name LIKE 'information\_schema\_%' ESCAPE '\' THEN 13000
    ELSE 2200
END as relnamespace

This is wrong in both directions.

Over-matches: user tables named pg_* are hidden

A user who creates pg_myreport gets it filed under pg_catalog. \dt and every ORM that filters on nspname skip it. The table is invisible with no error.

This is the failure mode #88 called out when rejecting a blanket NOT LIKE 'pg_%' filter — the same objection applies to using the prefix for namespace assignment.

Under-matches: internal indexes leak into \di

Migrations create eight indexes named idx_*, which neither the __pgsqlite_% nor the pg_% test catches:

$ psql -c "\di"
                          List of indexes
 Schema |             Name              | Type  |  Owner   | Table
--------+-------------------------------+-------+----------+-------
 public | idx_array_types_table         | index | postgres |
 public | idx_comments_lookup           | index | postgres |
 public | idx_datetime_cache_table      | index | postgres |
 public | idx_enum_values_label         | index | postgres |
 public | idx_enum_values_type          | index | postgres |
 public | idx_fts_metadata_table        | index | postgres |
 public | idx_numeric_constraints_table | index | postgres |
 public | idx_string_constraints_table  | index | postgres |
(8 rows)

All eight are CREATE INDEX IF NOT EXISTS statements in src/migration/registry.rs. They are indexes on __pgsqlite_* tables but are not themselves named __pgsqlite_*. They pollute \di and ORM index reflection.

Fix

#88 introduces the machinery this needs: an exact-name internal-relation registry (src/catalog/internal_relations.rs) covering all 4 internal tables, 24 views, and 8 indexes, exposed to SQL as __pgsqlite_relnamespace(name) -> 11 | 13000 | 2200, plus a drift test asserting the list matches a migrated-to-head database.

So this reduces to one expression swap in a new migration:

__pgsqlite_relnamespace(name) as relnamespace

By the time this lands, the UDF will already be serving information_schema.tables and .columns in production.

Verification

Context

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions