Skip to content

feat: support TRIGGER ON UPDATE and SCHEDULE EVERY for MV and ST - #1434

Merged
sd-db merged 9 commits into
1.12.latestfrom
sd-db/feature/mv-st-trigger-schedule-syntax
May 13, 2026
Merged

feat: support TRIGGER ON UPDATE and SCHEDULE EVERY for MV and ST#1434
sd-db merged 9 commits into
1.12.latestfrom
sd-db/feature/mv-st-trigger-schedule-syntax

Conversation

@sd-db

@sd-db sd-db commented Apr 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds the full Databricks refresh-schedule grammar on materialized views and streaming tables: `SCHEDULE CRON`, `SCHEDULE EVERY`, and `TRIGGER ON UPDATE [AT MOST EVERY INTERVAL ...]`.

Closes #1293.

User-facing config:

config:
  schedule:
    cron: '0 0 * * * ? *'
    time_zone_value: 'America/Los_Angeles'
    # OR
    every: '2 HOURS'
    # OR
    on_update: true
    at_most_every: '15 MINUTES'

Test plan

  • Unit tests for parser, validator, diff, and refresh-schedule macros (873 unit tests passing).
  • Functional round-trip tests per mode for both MV and ST against live UC SQL endpoint.
  • Functional lifecycle tests walking each relation through MANUAL → CRON → ON_UPDATE → EVERY → (non-refresh component change) → MANUAL.
  • `pre-commit run --all-files` clean (ruff, ruff-format, mypy).

Adds the full Databricks refresh-schedule grammar on materialized
views and streaming tables: SCHEDULE CRON, SCHEDULE EVERY, and
TRIGGER ON UPDATE [AT MOST EVERY INTERVAL ...].

- RefreshConfig is a discriminated config with parse-time validation
  on the new modes.
- Idempotent runs no longer emit spurious ALTERs across user/server
  unit differences.
- Auto-REFRESH is suppressed for every/on_update modes.

Coverage:
- Unit tests for parser, validator, diff, and refresh-schedule macros.
- Functional round-trip tests per mode for both MV and ST.
- Functional lifecycle tests walking each relation through every mode
  transition plus a non-refresh-component change.
@github-actions

github-actions Bot commented Apr 30, 2026

Copy link
Copy Markdown

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  dbt/adapters/databricks/relation_configs
  refresh.py 54, 63, 74, 142, 194, 225, 234
  streaming_table.py
Project Total  

This report was generated by python-coverage-comment-action

Drop log-line and SQL-emission assertions, keep only mode-value checks
on the resulting relation. Remove tests that only inspected dbt logs.
@tejassp-db

This comment was marked as duplicate.

1 similar comment
@tejassp-db

Copy link
Copy Markdown
Contributor

/integration-test

@github-actions

github-actions Bot commented May 4, 2026

Copy link
Copy Markdown

Integration tests dispatched for PR #1434 by @tejassp-db. Track progress in the Actions tab.

@github-actions

github-actions Bot commented May 4, 2026

Copy link
Copy Markdown

Integration results for PR #1434 — UC cluster ❌ cancelled · SQL warehouse ❌ cancelled · All-purpose cluster ❌ failure

Run details.

@sd-db

sd-db commented May 8, 2026

Copy link
Copy Markdown
Collaborator Author

/integration-test

@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown

Integration tests dispatched for PR #1434 by @sd-db. Track progress in the Actions tab.

@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown

Integration results for PR #1434 — UC cluster ⏩ skipped · SQL warehouse ⏩ skipped · All-purpose cluster ⏩ skipped · Shard coverage ⏩ skipped

Run details.

@sd-db

sd-db commented May 11, 2026

Copy link
Copy Markdown
Collaborator Author

/integration-test

@github-actions

Copy link
Copy Markdown

Integration tests dispatched for PR #1434 by @sd-db. Track progress in the Actions tab.

@github-actions

Copy link
Copy Markdown

Integration results for PR #1434 — UC cluster ✅ success · SQL warehouse ✅ success · All-purpose cluster ✅ success · Shard coverage ✅ success

Run details.

Comment thread dbt/adapters/databricks/relation_configs/refresh.py
Comment thread dbt/adapters/databricks/relation_configs/refresh.py
Comment thread dbt/adapters/databricks/relation_configs/refresh.py
sd-db added 5 commits May 13, 2026 14:39
When a user sets `schedule = {'cron': '...'}` without `time_zone_value`,
the warehouse stores it as `Etc/UTC` in `DESCRIBE EXTENDED`. Previously
`RefreshConfig.__eq__` compared `"UTC" != "ETC/UTC"`, so dbt emitted a
no-op `ALTER` on every subsequent run.

Adds `_canonical_tz()` mapping None / UTC / Etc/UTC to the same value;
`__eq__` and `__hash__` use it on the CRON branch.

Test coverage:
- New EVERY-input matrix test per file (MV + ST) walks one relation
  through HOUR / DAY / WEEK via `on_configuration_change: apply`,
  asserting each round-trips against the warehouse.
- The existing CRON lifecycle step now uses a no-TZ fixture so the
  `Etc/UTC` round-trip is exercised on the existing CRON path.
- Unit tests cover the equivalence + parametrized server-emitted shapes
  (TRIGGER intervals normalized to SECOND, EVERY plural forms).
…trigger-schedule-syntax

# Conflicts:
#	tests/functional/adapter/materialized_view_tests/fixtures.py
The schedule-mode test files inlined six SQL constants each
(ST_EVERY_2_HOURS, ST_ON_UPDATE_BARE, ST_CRON, etc.) — out of line with
the convention used elsewhere in `streaming_tables/fixtures.py` and
`materialized_view_tests/fixtures.py` (`streaming_table`,
`complex_streaming_table`, `materialized_view`).

Moved each to its respective fixtures.py with snake_case names and
deduped two cases:
  - `ST_NO_SCHEDULE` is now the pre-existing `fixtures.streaming_table`.
  - `ST_EVERY_2_HOURS` / `MV_EVERY_2_HOURS` are now defined via the
    `streaming_table_with_every("2 HOURS")` / `materialized_view_with_every`
    helper added in the previous commit.
  - Added `materialized_view_no_schedule` (the existing `materialized_view`
    fixture has cron + tblproperties, so it can't double as the no-schedule
    case).

No behavioral change to the tests.
`test_every_mode_roundtrip` only asserted `refresh.mode.value == "every"`
for `EVERY 2 HOURS`. The `TestStreamingTableEveryAcceptedInputs` matrix
test (added previously) now walks HOUR / DAY / WEEK with strict equality
+ no-diff checks — strictly stronger coverage.

Remove the redundant test and the `streaming_table_every_2_hours` /
`materialized_view_every_2_hours` fixtures; the few remaining call sites
in the lifecycle and drop-and-readd tests use the
`streaming_table_with_every("2 HOURS")` / `materialized_view_with_every`
helper inline.
Both removed cases assert exactly what the lifecycle test already does:

- `TestStreamingTableManualMode` / `TestMaterializedViewManualMode` —
  identical to lifecycle's first step (CREATE no-schedule → assert manual).
- `test_on_update_rate_limited_mode_roundtrip` — same `"900" in
  refresh.at_most_every` assertion as the lifecycle ON_UPDATE step.

Leaves `test_on_update_bare_mode_roundtrip` (only coverage for bare
TRIGGER ON UPDATE on fresh CREATE; lifecycle doesn't include a bare
on_update transition).
@sd-db
sd-db merged commit 3d210a6 into 1.12.latest May 13, 2026
7 checks passed
@sd-db
sd-db deleted the sd-db/feature/mv-st-trigger-schedule-syntax branch May 13, 2026 11:07
sd-db added a commit that referenced this pull request May 15, 2026
…R 15.4 (#1461)

### Description

PR #1434 added `from pydantic import model_validator` to `refresh.py`, a
v2-only symbol. This breaks adapter import on Databricks Jobs DBR 15.4
LTS, which ships pydantic v1.10.x.

This PR rewrites the three v2-only call sites to v1-compatible
equivalents that also work in v2 via the deprecation shim:

| Before (v2 only) | After (v1 + v2) |
|---|---|
| `@model_validator(mode="after")` |
`@root_validator(skip_on_failure=True)` |
| `self.model_construct(**self.model_dump(), ...)` |
`self.copy(update=...)` |

### Verification

- Unit tests: 1081 passed, 6 skipped
- Local smoke (pydantic v2): 56 PASS / 0 ERROR
- Databricks Jobs verify on DBR 15.4 LTS (pydantic v1.10.x)
- Reproduced the post-merge `Min-Deps Parse` workflow (#1460) locally
against this branch: `uv pip install --resolution lowest-direct .`
resolved pydantic to 1.10.0; `dbt parse --project-dir
tests/min_deps_smoke --profiles-dir tests/min_deps_smoke` succeeds,
confirming the fix clears the same import path that the post-merge gate
exercises.

### Checklist

- [x] Added changes to CHANGELOG.md
- [x] Unit tests pass
- [x] Functional/smoke tests pass on Databricks Jobs (DBR 15.4 LTS)
mirnawong1 pushed a commit to dbt-labs/docs.getdbt.com that referenced this pull request Jun 12, 2026
… MVs and streaming tables (databricks/dbt-databricks#1434)

Materialized views and streaming tables now support three refresh-schedule modes via the `schedule` config: cron (`schedule: { cron, time_zone_value }`), interval (`schedule: { every: '2 HOURS' }` -> SCHEDULE EVERY, units HOURS/DAYS/WEEKS), and on-update trigger (`schedule: { on_update: true, at_most_every: '15 MINUTES' }` -> TRIGGER ON UPDATE [AT MOST EVERY INTERVAL ...], minimum 60 seconds). Exactly one of cron/every/on_update may be set, and time_zone_value is only valid alongside cron. For the interval and on-update modes, dbt no longer issues a manual REFRESH on no-op re-runs since Databricks auto-manages the refresh.
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.

2 participants