What's Changed
- Added a pluggable actions and alerting subsystem (#1289). DQX now supports extensible actions that run when checked data violates an optional condition evaluated against the summary metrics produced by
DQMetricsObserver. The built-inDQAlertaction can send notifications to Slack, Microsoft Teams, a generic HTTPS webhook, or the log, so pipelines can react to data quality regressions without custom plumbing. You can create your own custom actions as well, and custom alerting is possible via the callback destination, which invokes an in-process Python callable for each alert. - Added an MCP (Model Context Protocol) server for DQX (#1252). The server exposes DQX's data quality capabilities as tools that any MCP-compatible AI agent (Claude, Genie Code, Cursor, Mosaic AI) can discover and orchestrate. It runs as a Databricks App with on-behalf-of (OBO) authentication, so all data access is governed by the calling user's Unity Catalog permissions.
- Added support for summary metrics in Lakeflow Declarative Pipelines (LDP/DLT) (#1301). A new
DQEngine.compute_summary_metrics(...)produces the same row counts, per-check breakdown, and custom observer metrics as a lazy aggregation over the results DataFrame, so metrics can be computed inside Spark Declarative Pipelines where the observer- and streaming-listener-based paths cannot be used. - Added the
aggr_matches_datasetdataset-level check (#1309). The check 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, enabling reconciliation-style validations against a source of truth. - Added the
has_no_gaps_per_time_windowdataset-level check (#1370). It detects gaps in a time series — windows of a configurable size that contain no rows between windows that do — with optional grouping and trailing-gap handling. - Added semantic validation of rulesets (#1203). A new
ChecksSemanticValidatorinspects declarative check metadata and reports duplicate rules (same function, arguments, criticality, and filter) and conflicting rules, surfacing authoring mistakes before checks run. - Added the
has_valid_string_caserow-level check (#1347). It validates consistent string casing withupper,lower,title, andsentencemodes, casting non-string columns to strings before comparison. - Added the
is_valid_national_idrow-level check (#1346). It validates national identification numbers per country (defaultUS), covering format, ranges, and obvious structural errors; it does not verify that a number was actually issued. - Added the
is_valid_currency_coderow-level check (#1368). It validates values against ISO 4217 currency codes, supporting both the three-letter alphabetic (e.g.USD) and three-digit numeric (e.g.840) representations viacode_format. - Added the
is_valid_country_coderow-level check (#1369). It validates values against ISO 3166-1 country codes in alpha-2 (default), alpha-3, or numeric form viacode_format. - Added the
is_valid_language_coderow-level check (#1403). It validates values against ISO 639 language codes in alpha-2 (ISO 639-1) or alpha-3 (ISO 639-3) form. - Added the
is_valid_subdivision_coderow-level check (#1404). It validates values against ISO 3166-2 country subdivision codes (e.g.US-CA,GB-ENG), with optional cross-column country consistency viacountry_column. - Added the
is_valid_uuidrow-level check (#1436). It validates values against the canonical RFC 9562 UUID string form (case-insensitive), mirroring the other pure pattern-match checks. - Added a profiler profile builder for the
has_no_outlierscheck (#1317). The profiler can now generate ahas_no_outlierscheck, and the MAD-based calculations and profiler defaults were refactored into shared constants. Disabled by default to retain existing performance. - Added Databricks runtime requirements verification (#1278). DQX now verifies the minimum Databricks environment version before executing checks (including geospatial checks with specific runtime requirements), while preserving backward compatibility.
- Migrated configs and rules to Pydantic v2 (#1259, #1302). DQX data models and the declarative checks path moved from dataclasses to Pydantic v2 for automatic validation and simpler YAML/JSON (de)serialization. Behavior is preserved: callers never see raw
pydanticValidationError— every entry point still raises DQX error types with the pre-migration message format. - Persisted full check metadata across table storage backends (#1319). Checks saved to Delta or Lakebase now faithfully round-trip all logical fields (including
message_exprand typeduser_metadata), matching the File/Volume backends. See Breaking Changes for theuser_metadataat-rest encoding change on the Delta backend. - Guarded LLM-generated
sql_queryrules against unsafe SQL (#1275). Both LLM-assisted rule-generation paths now drop any generatedsql_queryrule whose query contains unsafe (DML/DDL) SQL before returning it to the caller. - Guarded check
filterandrow_filteragainst unsafe SQL (#1303). All filter compile sites now route through a sharedsafe_filter_exprhelper that rejects destructive SQL keywords, and a check with an unsafe filter is treated as skipped rather than failing the run. - Decoupled AI-assisted rule generation from Spark (#1422). Rule generation no longer requires an active Spark session — column metadata is fetched via the Databricks SDK, and Spark sessions are created lazily only when required.
- Added a public parameter-order contract for built-in checks (#1408). A test now pins the intended public parameter order of every registered check in
check_funcs.py, making any reordering that would break positional callers visible during review. - DQX Studio: overhauled rule authoring with data-contract and AI generation, insights, and internationalization (#1262). The app can import ODCS v3.x data contracts and generate predefined, property-based, and natural-language rules via a new "from contract" UI, alongside richer authoring and insights.
- DQX Studio: added a French (
fr) locale (#1330). French joins the existing English, Brazilian Portuguese, Italian, and Spanish translations. - DQX Studio: simplified deployment to a natively declarative bundle (#1300). Legacy post-deploy grant scripts and one-time resource binding were removed in favor of a natively declarative Databricks Asset Bundle deploy.
- Deployed the MCP server with native DAB grants (#1430). The MCP server's Unity Catalog grants are now native bundle declarations and the post-deploy setup job was dropped, so
make mcp-deployis a single end-to-end command. - Made telemetry best-effort so it can never fail a job (#1401).
log_telemetryno longer makes a blocking per-check control-plane call that could raise aTimeoutErrorand terminate Structured Streaming jobs; telemetry is now non-throwing, deduplicated per process, and bounded by a short timeout. - Stopped reconfiguring the root logger on import (#1359). Importing DQX no longer calls blueprint's
install_logger(), which previously removed existing root handlers and overwrote the logging configuration of applications using DQX as a library. - Fixed filtered
is_uniqueviolations (#1442).is_uniquenow requires the current row to match its filter before reporting a duplicate, so unfiltered rows sharing a key with filtered rows are no longer falsely flagged. - Fixed aggregate checks over
*combined with a row filter (#1453). Dataset-level aggregate checks that aggregate over*with arow_filterno longer raiseINVALID_USAGE_OF_STAR_OR_REGEXwhen constructed withF.col("*"); unfilteredcount/count_distinctover*continue to work, and unsupported star/aggregate combinations now raise a clearInvalidParameterError. - Fixed row-level pattern checks accepting a trailing newline (#1454).
is_valid_email,is_valid_ipv4_address,is_valid_uuid,is_valid_national_id, and theis_ipv4_address_in_cidrvalue path now reject values with a trailing newline, which Java/Sparkrlikepreviously accepted because$also matches before a final line terminator. - Fixed null-safe result joins (#1356). Custom SQL and grouped-check results are now reattached with null-safe key matching, preserving violations for rows and groups with null key components.
- Fixed NULL group matching in time-window gap checks (#1387).
has_no_gaps_per_time_windownow preserves gap violations for groups with null key components. - Fixed non-deterministic fingerprints for set-valued arguments (#1345). Set and frozenset rule arguments are canonicalized after normalization, so rule fingerprints are stable regardless of set iteration order.
- Fixed validation of columns requiring SQL identifier escaping (#1342). Bare column names that need escaping (e.g.
Customer Name) now validate via a two-pass fallback. - Preserved fractional seconds in timestamp profiles (#1399). The profiler now preserves microsecond precision when converting timestamp min/max aggregates to profile values for
TimestampTypeandTimestampNTZType. - DQX Studio: fixed the rule source value for rules imported through the app (#1318).
- DQX Studio: fixed installation on Windows (#1327).
build_app.pynow appends the.cmdsuffix to Node binaries on Windows, and CI sweeps orphaned jobs. - Reported MCP server integration test coverage (#1429). The MCP integration suite now runs on every pull request and installs DQX built from the repository (production keeps the published pin), so a change anywhere in the library is exercised end to end.
- Fixed the
set_utc_timezonetest fixture to actually apply UTC (#1402). - Added documentation versioning tags (#1388). User-guide pages and subsections can now be tagged with a feature lifecycle stage and the version a feature was introduced.
- Added a Genie Space connection guide to the Quality Dashboard documentation (#1263).
- Simplified tolerance docstring examples in the check functions (#1254).
- Fixed data contract examples in the documentation (#1250).
BREAKING CHANGES!
is_in_list,is_not_in_list, andis_not_null_and_is_in_listnow resolve theirallowed/forbiddenstring values as column expressions (consistent with the comparison checks), not string literals. A bare string is interpreted as a column reference, a numeric string (e.g."3") is parsed as a number, and an ISO-date string (e.g."2024-01-01") as a date. To match a string literal, single-quote the value (e.g.'value') or wrap it inF.lit("value"). Existing checks that relied on bare strings being treated as literals must quote them. (#1419)user_metadatasaved through the Delta table storage backend is now JSON-encoded at rest to preserve non-string types through theMAP<STRING, STRING>column. Save→load via DQX is transparent (you get the original typed value back), but the stored representation changes: direct SQL/dashboard consumers now read JSON-encoded values (decode withfrom_json), existing tables are not migrated, and legacy string values that look like JSON atoms ("true","1","null") read back as typed values (True/1/None) — re-save affected rule sets after upgrading to normalize. The File/Volume (YAML/JSON) and Lakebase (JSONB) backends are unaffected. (#1319)
Full Changelog: v0.15.0...v0.16.0
Contributors
@mwojtyczka, @ghanse, @vb-dbrks, @SreeramaYeshwanthGowd, @mattfaltyn, @fedeflowers, @aarushisingh04, @IvannKurchenko, @abhyuday1203, @arnoN7, @AtomicGlance, @berrybluecode, @laurencewells, @neeraj-bhadani-08, @SaptarshiAcharyya99, @souravg-db2, @STEFANOVIVAS, @SyedIshmumAhnaf, @Vsatyam013