Skip to content

object_reference cannot survive binary pg_upgrade: _sentry_mv crashes it, and repair logic can't fix stale OIDs anyway #24

Description

@jnasbyupgrade

Summary

object_reference cannot currently survive a binary pg_upgrade, and the repair logic that's supposed to fix stale OID references after one has its own bugs, independent of that.

Root cause 1: _sentry_mv crashes the upgrade outright

_sentry_mv (CREATE MATERIALIZED VIEW _object_reference._sentry_mv AS SELECT _object_reference._repair();, marked via pg_extension_config_dump()) exists to auto-run _repair() whenever a logical pg_dump/pg_restore happens. Under binary pg_upgrade it fails with:

pg_class heap OID value not set when in binary upgrade mode

Confirmed as a general PostgreSQL limitation, not specific to this extension's code (reproduced with a bare, non-extension materialized view under the same pg_extension_config_dump marking): restoring a config-dumped materialized view triggers a REFRESH, which builds a brand-new heap — and binary-upgrade mode only pre-assigns OIDs for the objects its own schema-restore script explicitly creates, not for a heap created via a later REFRESH.

Root cause 2: real repair work is needed after every binary pg_upgrade — not an edge case

Verified empirically across two independent upgrade pairs (PG 12→13, PG 16→17), with _sentry_mv temporarily worked around purely to let the upgrade complete: of 14 tracked object types spanning nearly every supported cat_tools.object_type, table/index/sequence/view/materialized-view/column/type stay valid (pg_upgrade's OID-preservation covers pg_class/pg_type/pg_enum), but constraint, function, cast, default-value, and trigger — anything backed by a different catalog — get a brand-new OID every single upgrade, unconditionally. This isn't a rare corner case; it's certain to happen on any tracked function/trigger/constraint/cast/default across any binary upgrade.

Root cause 3: the repair functions themselves can't fix that case today

  • _repair() (what _sentry_mv invokes) calls _object_oid__add(), which does a plain INSERT INTO _object_oid(...) with no ON CONFLICT. The moment a row already exists for that object_id — exactly what pg_upgrade leaves behind, since it preserves table data physically rather than wiping it the way a logical restore does — it crashes: duplicate key value violates unique constraint "_object_oid_pkey".
  • _object_reference.fix_refs()'s "extraneous ID information" branch (the one that should handle "row exists but is stale") references r_object.object_id instead of r_object_v.object_id — a typo that makes it crash (missing FROM-clause entry for table "r_object") instead of emitting its intended warning, for both fix_refs(true) and fix_refs(false).

Net effect

Today: a binary pg_upgrade of a database with object_reference installed fails outright (root cause 1). Even after that's fixed, the extension has no working way to repair the OID staleness that pg_upgrade genuinely introduces for several object types (root causes 2+3) — _repair()/fix_refs() need real fixes to handle "OID row present but stale," not just "OID row missing," regardless of how/when they get invoked.

Scope note

This issue is about the underlying bugs (the crash, the missing-repair-path bugs) — not about when/how repair gets automatically triggered after an upgrade. That design question is tracked separately in a follow-up issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions