Skip to content

fix(view): refresh a skipped view's metadata only when it is stale - #847

Open
axellpadilla wants to merge 1 commit into
masterfrom
fix/838-conditional-view-metadata-refresh
Open

axellpadilla wants to merge 1 commit into
masterfrom
fix/838-conditional-view-metadata-refresh

Conversation

@axellpadilla

@axellpadilla axellpadilla commented Sep 19, 2026

Copy link
Copy Markdown
Collaborator

Improves the #838 fix. That fix ran sp_refreshview on every skipped view, which advances sys.objects.modify_date exactly as an ALTER does — so every unchanged view in a project churned that column on every run, and it could no longer tell a skipped run from a rebuild. Anything watching the catalog for drift saw noise, and the test that used to assert "no rebuild" had to give up its signal.

What changed

The skip decision already fetched the stored definition. It now gets the view's cached column shape in the same query, compares it against what the body resolves to (sys.dm_exec_describe_first_result_set, which compiles the body without executing it), and runs sp_refreshview only where the two disagree.

  • Same round trip as before, one compile of the body per unchanged view.
  • A view whose sources have not moved is left completely untouched, so modify_date works as a signal again.
  • Fails toward refreshing: a body that cannot be described is refreshed rather than assumed current.

The check is a new macro, get_view_skip_check_sql, rather than a second column on get_view_definition_sql — a project overriding that macro would otherwise silently lose a column the materialization then indexes, on the path that runs for every unchanged view.

The text comparison is unchanged and still the gate. A shape check cannot replace it: where amount > 100> 200 resolves to an identical column list, so a probe-only skip test would skip a real edit — the failure mode #807 fixed.

Notes

  • Both sysname comparisons are pinned with COLLATE DATABASE_DEFAULT. Without it every view model fails with Cannot resolve the collation conflict wherever the catalog and database collations differ — caught by the functional suite, not by review.
  • is_nullable is deliberately not compared: describe's inference over expressions can disagree with what CREATE VIEW recorded, which would refresh that view on every run forever.
  • sys.columns is left unhinted, per the existing policy for catalog reads that act as correctness guards.

Testing

Three test classes collapse into one — TestSkippedViewTracksItsSourceShape, one project, one method walking a single view's life: build → quiet run → added column → retyped column → quiet run. It pins both directions, including that a repaired view goes quiet again. TestViewMaterializationNoOp gets its modify_date assertion back.

Verified against SQL Server 2022: 646 passed, 5 skipped.

Targets the 1.12.0rc4 release being prepared in #841. The 1.11 backport branch (backport/1.11-838-view-refreshview) carries the same sp_refreshview behaviour and will need this separately.

🤖 Generated with Claude Code

The skip path ran sp_refreshview on every unchanged view. That advances
sys.objects.modify_date exactly as an ALTER does, so every unchanged view in a
project churned that column on every run, and it could no longer tell a skipped
run from a rebuild.

The skip decision now reads the view's cached column shape alongside the stored
definition it already fetched, compares it against what the body resolves to via
sys.dm_exec_describe_first_result_set, and refreshes only where the two disagree.
Same round trip as before, one compile of the body, and a view whose sources have
not moved is left untouched.

The check is a new macro rather than a second column on get_view_definition_sql,
whose one-column contract a project override may depend on. Both sysname
comparisons are pinned with COLLATE DATABASE_DEFAULT - without it every view
model fails outright wherever the catalog and database collations differ. The
sys.columns read carries information_schema_hints() like every other catalog
read: it runs for every view on every run, so it must not wait indefinitely on
another writer's catalog locks, and a torn read costs only one needless refresh
or one run's delay.

Verified against SQL Server 2022: an added column and a retyped column each reach
a skipped view, and the quiet runs either side leave modify_date unmoved.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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