Skip to content

fix: clone tables with row filters or column masks as views - #1745

Open
sd-db wants to merge 7 commits into
fix/clone-view-skip-mask-filter-checksfrom
fix/clone-governed-tables-as-view
Open

sd-db wants to merge 7 commits into
fix/clone-view-skip-mask-filter-checksfrom
fix/clone-governed-tables-as-view

Conversation

@sd-db

@sd-db sd-db commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #1744.

Description

dbt clone fails for any Unity Catalog table with a row filter or column mask, because Databricks refuses to shallow-clone it:

[ROW_LEVEL_SECURITY_TABLE_CLONE_SOURCE_NOT_SUPPORTED] SHALLOW clone from table ... with row level security policy is not supported.

Column masks fail the same way with COLUMN_MASKS_TABLE_CLONE_SOURCE_NOT_SUPPORTED. These are often the most sensitive tables, so CI jobs that clone production either fail or have to --exclude them.

Before shallow-cloning a Unity Catalog table, clone now checks the deferred table's catalog information_schema for a row filter or column mask. If it finds one, clone takes the view path it already uses for materialized views and streaming tables: a view whose SQL is select * from <deferred table>. The check reads the deferred table's actual policies, not the branch's config, so policies applied outside dbt count too. Hive metastore tables skip the check and shallow-clone as before.

Under clone, validate_view_model now rejects mask and row-filter config only for view models. Before, materialized views and streaming tables (#1744) were the only exceptions. A clone never applies the current config's policies: a shallow clone doesn't, and the view relies on the deferred object's policies.

Policies apply to whoever queries the view. A temporary service principal had SELECT on the cloned view and no grant on production. Production had a current_user() row filter, a current_user() mask and an is_account_group_member() mask. Through the view, the principal got its own filtered and masked rows (1 of 3, both columns ***), the same result it got reading production directly once granted access. The owner saw all 3 rows unmasked. The principal and its schemas were deleted afterwards. As with any clone, anyone granted the target schema can read production through the view without a production grant, but only the rows and values production's policies allow them.

Lookup: one extra query per cloned Unity Catalog table. With use_describe_as_json_for_relation_metadata on and a compute that supports it, clone reads the policies from DESCRIBE TABLE EXTENDED ... AS JSON (about 0.5 seconds on a SQL warehouse), as get_relation_config already does. Otherwise it queries the catalog's information_schema (about 0.6 to 0.8 seconds). The check is Jinja-only and adds no @available method.

Tests:

  • New functional tests in tests/functional/adapter/dbt_clone/. Each clones a governed production table and asserts that the target is a VIEW whose rows are filtered or masked by production's policy. With the fix reverted, all three fail.
    • TestCloneRowFilteredTable: a row filter set through the model config.
    • TestCloneRowFilterAppliedOutsideDbt: a row filter added with ALTER TABLE and absent from the config.
    • TestCloneColumnMaskedTable: a column mask, built with use_materialization_v2 because V1 doesn't apply column masks at create.
    • TestCloneRowFilterAppliedOutsideDbtDescribeAsJson and TestCloneColumnMaskedTableDescribeAsJson: the same cases with use_describe_as_json_for_relation_metadata on. With the information_schema fallback broken on purpose, both still passed and the information_schema-path class failed, so the DESCRIBE path ran.
  • Macro tests: the exact policy-lookup SQL, Hive metastore skipping the lookup, the DESCRIBE path detecting a row filter or column mask (and none on a plain table), the fallback to information_schema when the flag or capability is missing, and validate_view_model under clone for table and incremental models.
  • Ran the full clone suite on the UC SQL warehouse (16 passed, 2 skipped) and the plain shallow-clone test on a UC cluster. All unit tests pass (1642).

Checklist

  • I have run this code in development and it appears to resolve the stated issue
  • This PR includes tests, or tests are not required/relevant for this PR
  • I have updated the CHANGELOG.md and added information about my change to the "dbt-databricks next" section.
  • [Optional] I have run the dbt-databricks-pr-ready project skill for this PR and addressed its merge-readiness feedback

This pull request and its description were written by Isaac.

sd-db added a commit that referenced this pull request Oct 26, 2026
Co-authored-by: Isaac <no-reply@databricks.com>
@github-actions

Copy link
Copy Markdown

Coverage report

This PR does not seem to contain any modification to coverable code.

@sd-db
sd-db added this pull request to stack #1751 October 28, 2026 06:11
sd-db and others added 3 commits October 28, 2026 11:56
Unity Catalog can't shallow-clone a table with a row filter or column
mask, so dbt clone failed on governed tables. Clone now checks the
deferred table's policies in its catalog's information_schema and, when
it has any, builds the view over it that materialized views and
streaming tables already use. Under clone, validate_view_model rejects
mask and row-filter config only for view models.

Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
With use_describe_as_json_for_relation_metadata on and a compute that
supports DESCRIBE TABLE EXTENDED AS JSON, clone reads the deferred
table's row filter and column masks from it, as get_relation_config
does. Otherwise it keeps the information_schema query.

Co-authored-by: Isaac <no-reply@databricks.com>
@sd-db
sd-db force-pushed the fix/clone-governed-tables-as-view branch from ba9e03f to 9579c43 Compare October 28, 2026 06:52
sd-db and others added 4 commits October 28, 2026 17:10
Co-authored-by: Isaac <no-reply@databricks.com>
### Description

`TestViewLifecycleIgnoresMaterializationFlag` and
`TestViewLifecycleMaterializationFlagOn` ran `dbt --warn-error run`, so
any warning failed them, including warnings unrelated to the view
lifecycle. On the lowest-direct dependency floor, dbt-core 1.11.2
reports `view_update_via_alter` as an unrecognized config key
(`CustomKeyInConfigDeprecation`), and some auth paths emit warnings too.
The `--warn-error` was only defense in depth, so this PR removes it. All
server-state assertions (column comments and grants) stay.

This PR also moves the 1.13.0 release date in `CHANGELOG.md` to Oct 29,
2026.

Supersedes #1754.

Verified locally against a SQL warehouse (`databricks_uc_sql_endpoint`),
`tests/functional/adapter/views/test_views.py`:

- dbt-core 1.11.2 (lowest-direct lock): 29 passed, 7 skipped
- dbt-core 1.12.6 (`uv.lock`): 29 passed, 7 skipped

### Checklist

- [x] I have run this code in development and it appears to resolve the
stated issue
- [x] This PR includes tests, or tests are not required/relevant for
this PR
- [ ] I have updated the `CHANGELOG.md` and added information about my
change to the "dbt-databricks next" section. (test-only, no changelog
entry)

This pull request and its description were written by Isaac.

Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
This reverts commit 39a8c96.

Co-authored-by: Isaac <no-reply@databricks.com>

This branch has not been deployed

No deployments
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