Skip to content

Feature/validate upstream tables - #1309

Merged
mwojtyczka merged 17 commits into
databrickslabs:mainfrom
STEFANOVIVAS:feature/validate_upstream_tables
Jul 23, 2026
Merged

Feature/validate upstream tables#1309
mwojtyczka merged 17 commits into
databrickslabs:mainfrom
STEFANOVIVAS:feature/validate_upstream_tables

Conversation

@STEFANOVIVAS

@STEFANOVIVAS STEFANOVIVAS commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Changes

  • Added new check function: aggr_matches_dataset— a new dataset-level check that compares an aggregate metric (row count by default, or any curated/built-in aggregate) computed on the checked DataFrame against the same aggregate computed on a reference (upstream) table or DataFrame, with optional abs_tolerance/rel_tolerance and independent row_filter/ref_row_filter on each side.

Linked issues

Resolves #1046

Tests

  • manually tested
  • added unit tests
  • added integration tests
  • added end-to-end tests
  • added performance tests

Documentation and Demos

  • added/updated demos
  • added/updated docs
  • added/updated agent skills

@STEFANOVIVAS
STEFANOVIVAS requested a review from a team as a code owner July 7, 2026 02:52
@STEFANOVIVAS
STEFANOVIVAS requested review from pratikk-databricks and removed request for a team July 7, 2026 02:52
@CLAassistant

CLAassistant commented Jul 7, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new dataset-level quality check, validate_upstream_table, to validate ingestion correctness by comparing an aggregate metric (row count by default, or other curated/built-in aggregates) computed on the checked DataFrame against the same metric computed on an upstream reference (DataFrame via ref_df_name or Unity Catalog table via ref_table), with optional absolute/relative tolerances and independent row filters.

Changes:

  • Added validate_upstream_table to check_funcs.py, including parameter validation, aggregate evaluation on both sides, and tolerance/null-safe comparison logic.
  • Added unit and integration tests covering parameter validation, tolerance behavior, row filtering, '*'/count(*) usage, and ref-table vs ref-DataFrame paths.
  • Documented the new check in the quality checks reference, including YAML and Python usage examples.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
src/databricks/labs/dqx/check_funcs.py Adds the new dataset-level upstream aggregate comparison check implementation.
tests/unit/test_dataset_checks.py Adds rule-build-time parameter validation tests for the new check.
tests/integration/test_dataset_checks.py Adds end-to-end Spark integration tests for the new check’s runtime behavior.
docs/dqx/docs/reference/quality_checks.mdx Documents the new check and adds usage examples.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/databricks/labs/dqx/check_funcs.py Outdated
ghanse
ghanse previously requested changes Jul 7, 2026

@ghanse ghanse left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Headed in the right direction. Left some comments. Do you also want to add a similar check for a custom SQL query?

Comment thread src/databricks/labs/dqx/check_funcs.py Outdated
Comment thread src/databricks/labs/dqx/check_funcs.py Outdated
@ghanse ghanse added under-review This PR is currently being reviewed by one of DQX maintainers. needs-changes Changes required after review labels Jul 7, 2026
Comment thread docs/dqx/docs/reference/quality_checks.mdx Outdated

@mwojtyczka mwojtyczka left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review of validate_upstream_table. Useful check and largely correct; main themes are: it reimplements existing aggregate machinery (_is_aggr_compare + _get_ref_df) rather than composing it, and a few edges around filter validation, the empty-upstream message, and the exact-equality default. Inline below.

Cleared: rel_tolerance has no div-by-zero (guarded by max()); the apply closure captures spark/ref_dfs correctly (standard dataset-check pattern); .limit(1) matches _is_aggr_compare; behavioral coverage lives in the integration tests (dataset checks can't be unit-tested without Spark).

Comment thread src/databricks/labs/dqx/check_funcs.py Outdated
Comment thread src/databricks/labs/dqx/check_funcs.py Outdated
Comment thread src/databricks/labs/dqx/check_funcs.py Outdated
Comment thread src/databricks/labs/dqx/check_funcs.py Outdated
Comment thread src/databricks/labs/dqx/check_funcs.py Outdated
Comment thread src/databricks/labs/dqx/check_funcs.py Outdated
Comment thread src/databricks/labs/dqx/check_funcs.py Outdated
Comment thread src/databricks/labs/dqx/check_funcs.py Outdated

@mwojtyczka mwojtyczka left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going in the right direction, left some comments

@mwojtyczka mwojtyczka left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Just one request to rename the function to aggr_matches_dataset to make it more generic and update docs and docs strings accordingly

@STEFANOVIVAS

Copy link
Copy Markdown
Contributor Author

LGTM. Just one request to rename the function to aggr_matches_dataset to make it more generic and update docs and docs strings accordingly

Hi, @mwojtyczka! One thing to point out: Should we implement a group by clause in this check? _is_aggr_compare function already let us apply it in the input Dataframe. Could make sense because in many situations we need to make a per-group reconciliation (date, region,source_system,etc).

@mwojtyczka

Copy link
Copy Markdown
Contributor

LGTM. Just one request to rename the function to aggr_matches_dataset to make it more generic and update docs and docs strings accordingly

Hi, @mwojtyczka! One thing to point out: Should we implement a group by clause in this check? _is_aggr_compare function already let us apply it in the input Dataframe. Could make sense because in many situations we need to make a per-group reconciliation (date, region,source_system,etc).

yes makes sense

@mwojtyczka

mwojtyczka commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

@STEFANOVIVAS pls also add the check to tests/resources/all_dataset_checks.yaml, and adjust the relevant tests. We also need a performance test so that we can validate regressions in the future

@mwojtyczka

mwojtyczka commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Code review — aggr_matches_dataset

Overall this is a high-quality, well-tested addition (23 integration tests + unit exception cases, careful null-safety handling, behavior-preserving refactor of _is_aggr_compare). No blocking bugs found in the happy paths. Notes below are minor correctness edge-cases and test-coverage gaps.

Note: the non-null-safe grouped-join issue I initially flagged for count_distinct + group_by is already being fixed in #1356 (it replaces the df.join(agg_df, on=join_cols, how="left") in _is_aggr_compare with a null-safe helper). Once #1356 lands, aggr_matches_dataset's grouped count_distinct path inherits the fix — so it's excluded here.

1. Not wired into the "all dataset checks" coverage (test-coverage)

tests/resources/all_dataset_checks.yaml (+ the all-checks integration tests) — aggr_matches_dataset isn't included, unlike sibling dataset checks (compare_datasets, foreign_key, …). The declarative metadata / YAML round-trip for this check is therefore unexercised.

2. No aggr_matches_dataset test for count_distinct with group_by (test-coverage)

tests/integration/test_dataset_checks.py — the check is tested with count_distinct (no group_by) and with group_by (count/sum), but not both together. #1356 adds a grouped-count_distinct regression for the base is_aggr_* path; a small aggr_matches_dataset-specific case (ideally with a NULL group key, to lock in the #1356 fix for this check) would close the loop.

@mwojtyczka mwojtyczka left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good, left comments regading tests

- Wire aggr_matches_dataset into the all-dataset-checks coverage: add it to
  all_dataset_checks.yaml and both all-checks integration tests
  (test_apply_checks_all_checks_as_yaml already loads the YAML;
  test_apply_checks_all_checks_using_classes gains the class-based check plus
  ref_dfs), exercising the declarative/YAML round-trip like the sibling
  dataset checks.
- Add count_distinct + group_by integration tests (match, mismatch, and a
  NULL group key) to lock in grouped distinct-count behaviour for this check;
  the NULL-key case asserts flagging behaviour rather than exact message text
  since the window-incompatible join is not null-safe (documented).
- Add performance benchmarks: dataset-wide count comparison and the
  count_distinct + group_by two-stage join path.

Co-authored-by: Isaac
@mwojtyczka

Copy link
Copy Markdown
Contributor

Code review — aggr_matches_dataset

Overall this is a high-quality, well-tested addition (23 integration tests + unit exception cases, careful null-safety handling, behavior-preserving refactor of _is_aggr_compare). No blocking bugs found in the happy paths. Notes below are minor correctness edge-cases and test-coverage gaps.

Note: the non-null-safe grouped-join issue I initially flagged for count_distinct + group_by is already being fixed in #1356 (it replaces the df.join(agg_df, on=join_cols, how="left") in _is_aggr_compare with a null-safe helper). Once #1356 lands, aggr_matches_dataset's grouped count_distinct path inherits the fix — so it's excluded here.

1. Not wired into the "all dataset checks" coverage (test-coverage)

tests/resources/all_dataset_checks.yaml (+ the all-checks integration tests) — aggr_matches_dataset isn't included, unlike sibling dataset checks (compare_datasets, foreign_key, …). The declarative metadata / YAML round-trip for this check is therefore unexercised.

2. No aggr_matches_dataset test for count_distinct with group_by (test-coverage)

tests/integration/test_dataset_checks.py — the check is tested with count_distinct (no group_by) and with group_by (count/sum), but not both together. #1356 adds a grouped-count_distinct regression for the base is_aggr_* path; a small aggr_matches_dataset-specific case (ideally with a NULL group key, to lock in the #1356 fix for this check) would close the loop.

I fixed these

@mwojtyczka mwojtyczka left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@mwojtyczka mwojtyczka left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@mwojtyczka mwojtyczka added Approved to Merge When PR is reviewed and approved. To be merged once all tests pass and removed under-review This PR is currently being reviewed by one of DQX maintainers. labels Jul 23, 2026
# Conflicts:
#	src/databricks/labs/dqx/check_funcs.py
The MCP suite (tests/integration_mcp) only ran on PRs touching mcp-server/,
src/databricks/labs/dqx/, or the mcp workflow — so nightly never exercised it.
Add an mcp-tests job to nightly.yml mirroring the mcp.yml PR job (setup-env,
prebuild-wheel, install Databricks CLI, acceptance harness with the
integration_mcp codegen), minus the PR-only fork gate since nightly is
schedule-triggered. No coverage upload (the MCP server/runner execute remotely).

Co-authored-by: Isaac
@mwojtyczka

mwojtyczka commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Heads-up: I pushed a small CI addition to this branch (96ef0896) — an mcp-tests job in .github/workflows/nightly.yml so the MCP integration suite runs on the nightly schedule.

It's unrelated to the aggr_matches_dataset change here — it's a gap we missed when the MCP server landed: mcp.yml only triggers on PRs touching mcp-server/, so nightly never exercised the suite. Bundling the fix here for expediency.

@mwojtyczka
mwojtyczka dismissed ghanse’s stale review July 23, 2026 16:11

implemented requested changes

@mwojtyczka
mwojtyczka merged commit e9d2cca into databrickslabs:main Jul 23, 2026
42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Approved to Merge When PR is reviewed and approved. To be merged once all tests pass

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE]: Validation of upstream tables

6 participants