Skip to content

feat: reactive dependency-conflict primitives for targeted matview drops - #1

Merged
Th3Mouk merged 1 commit into
mainfrom
feat/reactive-conflict-parser
Jun 9, 2026
Merged

feat: reactive dependency-conflict primitives for targeted matview drops#1
Th3Mouk merged 1 commit into
mainfrom
feat/reactive-conflict-parser

Conversation

@Th3Mouk

@Th3Mouk Th3Mouk commented Jun 9, 2026

Copy link
Copy Markdown
Owner

Why

When a schema migration tries to DROP TABLE / DROP COLUMN or ALTER COLUMN … TYPE on a relation a managed materialized view reads, PostgreSQL blocks the DDL. The bundle's deploy lane currently clears this by dropping every managed materialized view up-front. This PR adds the framework-agnostic primitives needed to instead drop only the conflicting closure (a future reactive_retry lane strategy in the bundle, gated on th3mouk/materialized-view ^1.2).

Primitives only — no existing behaviour changes.

What

  • Core\Sql\DependencyConflictSqlState — recognises the two blocking SQLSTATEs by walking the DBAL driver-exception chain, never the (locale-dependent) message text:
    • 2BP01 dependent_objects_still_exist — blocked DROP TABLE / DROP COLUMN
    • 0A000 feature_not_supported — blocked ALTER COLUMN … TYPE
  • Core\Dependency\PostgresDependencyConflict — best-effort, locale-aware parse of the blocked relation + named dependents (the DETAIL list is only a seed; PostgreSQL caps it at 100 entries). Core\Sql\QualifiedName adds the quote-aware identifier scanner.
  • Core\Dependency\CatalogDependencyResolver::resolveConflictClosure() — resolves the authoritative closure from the catalog: seeds via to_regclass, walks pg_depend/pg_rewrite by OID from an arbitrary relation (a plain table is not a node in the managed graph), classifies each dependent managed/unmanaged, orders dependents-first. Reuses DependencyGraph for ordering.
  • Core\MaterializedViewManager::dropConflictClosure() — drops the managed closure in order, refusing (UnmanagedDependentFound) when any unmanaged dependent is present unless DropDependentPolicy::Cascade. The drop set is always confirmed against the catalog, never the error text alone.
  • Core\Sql\ManagementMarkerisManagedComment() / readHash() read-side helpers (top-level marker-key check, not a substring match).

Tests

  • Unit: SQLSTATE gate (chain-walk, message-decoy negatives), parser fixtures (2BP01 drop-table/drop-column incl. a column named like the of table marker, schema-qualified double-quoted names, 0A000 rule, truncation, non-English → not parsed), QualifiedName::scan/render, marker read helpers, closure VO merge.
  • PG integration (PG 14–17): real 2BP01 DROP COLUMN and 0A000 ALTER TYPE provoked against a live server → parsed, surgically cleared, blocked DDL then succeeds; managed drop-ordering from the blocked table; guard refusal when an unmanaged view is in the closure.

Gates green locally: cs:check, stan (level 8, no baseline), test:unit (590), test:pg (11).

Add the framework-agnostic primitives a consumer (the Symfony deploy lane)
needs to clear a migration blocked by a managed materialized view by dropping
only the conflicting closure instead of every managed view.

- DependencyConflictSqlState: gate on 2BP01 / 0A000 via the DBAL driver-exception
  chain, never the locale-dependent message text.
- PostgresDependencyConflict: best-effort, locale-aware parse of the blocked
  relation and dependents; QualifiedName adds the quote-aware identifier scanner.
- CatalogDependencyResolver::resolveConflictClosure(): catalog walk by OID from an
  arbitrary seed relation (e.g. a plain table, which is not a managed-graph node),
  classifying managed vs unmanaged dependents and ordering dependents-first.
- MaterializedViewManager::dropConflictClosure(): drop the managed closure in
  order, refusing (UnmanagedDependentFound) on any unmanaged dependent unless
  DropDependentPolicy::Cascade. The drop set is confirmed against the catalog.
- ManagementMarker: isManagedComment() / readHash() read-side helpers.

Primitives only; no existing behaviour changes. Covered by unit tests and PG
integration tests (2BP01 DROP COLUMN, 0A000 ALTER TYPE, drop ordering, guard
refusal on an unmanaged dependent).
@Th3Mouk
Th3Mouk merged commit 4754699 into main Jun 9, 2026
6 checks passed
@Th3Mouk
Th3Mouk deleted the feat/reactive-conflict-parser branch June 9, 2026 08:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant