Skip to content

Standard internal update-time disable mechanism, and refuse to self-track extension-owned objects #40

Description

@jnasbyupgrade

Summary

Two related, foundational needs surfaced while working out #38 (automated OID-repair), but independent of that design and needed regardless of how it's finalized:

  1. A standard, internal mechanism for safely disabling whatever needs disabling during the extension's own update scripts — not a one-off, inline solution per update script.
  2. object_reference must refuse to track any object that is itself a member of the object_reference extension.

1. Internal disable/enable mechanism for update scripts

Today, sql/object_reference--0.1.0--stable.sql uses SET LOCAL session_replication_role = replica to suppress the extension's own event triggers during its structural section (dropping/recreating views, columns, etc., where the triggers would otherwise fire on the extension's own internal changes and do the wrong thing). #38 found this needs to change to ALTER EVENT TRIGGER ... DISABLE/ENABLE, uniformly, for all the extension's event triggers (the three existing ones, soon a fourth) — not a differentiated approach. _etg_drop has no way to recognize "this DROP is part of our own extension's update script" the way the other two can via pg_event_trigger_ddl_commands()'s in_extension column (pg_event_trigger_dropped_objects() has no equivalent), so it needs the literal disable/enable bracket regardless — and once that mechanism/privilege is needed for one trigger, extending it to all of them uniformly costs nothing further and removes a second, in_extension-based mechanism to reason about, test, and get subtly wrong. Resolved: no trigger uses in_extension self-recognition; all of them go through the same disable/enable bracket.

Rather than hand-writing ALTER EVENT TRIGGER ... DISABLE/ENABLE (or any other future disabling need) inline in each update script, build a standard, internal-only function (or pair of functions) that update scripts call instead — e.g. __object_reference.begin_internal_update() / __object_reference.end_internal_update(), or whatever shape makes sense once designed. This is needed regardless of what #38's final design looks like: any extension that modifies its own schema in an update script needs a clean way to say "the following statements are our own internal restructuring, not something our own event triggers/checks should react to" — today's session_replication_role trick already does part of this job informally; this issue is about making it a real, deliberate, reusable mechanism rather than an ad hoc pattern repeated (and potentially gotten subtly wrong) in every future update script.

This mechanism has a second consumer, not just update scripts: #38's pg_upgrade__pre()/pg_upgrade__post() functions are, conceptually, doing exactly what an update script's structural section does — mucking with object_reference's own member objects (dropping/recreating _sentry_mv). They should call the same begin/end functions this issue produces around their own DROP/CREATE MATERIALIZED VIEW statements, rather than inventing their own bracket or relying on any _sentry_mv-specific recognition trick.

Things to work out:

  • Whether that list of triggers needing disabling is stable, or something future updates might need to extend — design the mechanism so it doesn't need to be re-derived per update script.
  • Safety if an update script (or pg_upgrade__pre()/pg_upgrade__post()) errors out partway through the "disabled" window — should the disable/re-enable be structured so a failure always cleanly reverts (e.g. relying on transactional DDL rollback) rather than needing explicit cleanup logic.
  • Whether this belongs as its own tracked capability with its own tests (a fresh update-script author should not be able to forget to re-enable something, or should get a clear failure if they do).
  • Deferred follow-up, not blocking: it's not yet confirmed whether ALTER EVENT TRIGGER ... DISABLE/ENABLE has any wider locking effect (event triggers are evaluated database-wide for every DDL statement — worth checking whether toggling one serializes unrelated DDL elsewhere in the database until the enclosing transaction commits). Track as its own investigation once this mechanism is in use; don't block on it now.

2. Refuse to track any object that belongs to object_reference itself

object_reference should never allow one of its own extension-member objects (its tables, functions, event triggers, schemas, the private helper schema, etc.) to become a tracked object in _object_reference.object/_object_oid. There's no legitimate use case for this, and allowing it is a direct path to subtle, hard-to-diagnose bugs — especially during an extension update, where the extension's own objects are exactly the things being dropped/recreated/renamed as part of normal version migration. If one of those objects were also a tracked object, the update script's own structural changes would trip the same rename-detection/repair machinery meant for user objects, creating a confusing bootstrapping problem: the tracking system reacting to changes in itself while it's mid-update, exactly the kind of self-referential interaction the disable mechanism in part 1 exists to avoid, from a different angle.

Concretely: object_reference.object__getsert() (and whatever else can register a new tracked object, including the future check-and-repair mechanism from #38, which resolves an object's identity by name/OID and could otherwise re-add a self-referential row if one somehow existed) should check whether the target object is a member of the object_reference extension (pg_depend with deptype = 'e' and refobjid = object_reference's own extension OID, or equivalent) and refuse — loudly, with a clear error — rather than silently tracking it.

This should land as validation at registration time, not just documentation — a test should confirm that attempting to track e.g. _object_reference.object itself, or one of the event trigger functions, is rejected.

Note: tracking an object that belongs to some other extension is a different, legitimate case, not covered by this refusal — see the separate follow-up issue about requiring an explicit opt-in for that case.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions