Skip to main content

Custom Quality Checks

A custom quality check is a SQL check defined once, outside any contract, and used by name from as many contracts as need it. The contract names the check and supplies its arguments; the check holds the query.

schema:
- name: orders
properties:
- name: amount
logicalType: number
quality:
- type: custom
engine: datacontract-cli
implementation:
check: between
arguments:
min: 0
max: 10000

Point datacontract test at the folder the checks live in:

datacontract test datacontract.yaml --custom-quality-checks ./custom-quality-checks

or set DATACONTRACT_CUSTOM_QUALITY_CHECKS. See the test command reference.

Defining a check​

The folder holds one YAML file per check. The file name is the check's name: between.yaml defines between.

# custom-quality-checks/between.yaml
description: All ${column} values are between ${arguments.min} and ${arguments.max}.
owner: data-platform
dimension: conformity
arguments:
min:
max:
queries:
ansi: |
SELECT COUNT(*) FROM ${table}
WHERE ${column} < ${arguments.min} OR ${column} > ${arguments.max}
mustBe: 0
KeyMeaning
descriptionWhat the check verifies. Becomes the check's name in the results, with the placeholders and arguments filled in, unless the rule has a description of its own.
ownerWho maintains the check. Documentation only.
dimensionThe quality dimension the check measures. A dimension on the rule takes precedence.
argumentsThe arguments the check takes, each with an optional type (value, the default, number or identifier) and default. An argument without a default is required.
queriesAt least one query, keyed by SQL dialect.
mustBe, mustBeGreaterThan, …The default expected result, one of the SQL rule comparators.

Arguments​

A value argument stands for data the query compares against and becomes a SQL literal: 0, 'EUR', a list becomes 'A', 'B'. A number argument becomes a number literal even when its value is text, such as a variable; declare one for arithmetic and date math like - ${arguments.days}. An identifier argument names a column or table and becomes a name, quoted like the placeholders:

# custom-quality-checks/recent_rows.yaml
arguments:
timestamp_column: {type: identifier}
days: {type: number, default: 1}
queries:
ansi: |
SELECT COUNT(*) FROM ${table}
WHERE ${arguments.timestamp_column} >= CURRENT_DATE - ${arguments.days} * INTERVAL '1' DAY
mustBeGreaterThan: 0

Arguments are always inserted as escaped literals or identifiers, never as raw SQL, so a contract can't alter a check's query. Argument values may use variables, which resolve to text: max: ${MAX_AMOUNT} becomes '1000', or 1000 if max is a number argument. A check file can't reference variables itself; pass them in through an argument.

Placeholders​

The queries use the same placeholders as SQL rules — ${table}, ${column}, ${schema}, and so on. A check whose queries use ${column} (or ${field}, ${property}) can only be declared on a property. As in placeholders, the $ in an argument reference is optional: {arguments.min} works the same as ${arguments.min}.

Dialects​

A run uses the query for the SQL dialect of the server, and otherwise the ansi query:

queries:
ansi: |
SELECT COUNT(*) FROM ${table}
WHERE ${arguments.timestamp_column} >= CURRENT_DATE - ${arguments.days} * INTERVAL '1' DAY
tsql: |
SELECT COUNT(*) FROM ${table}
WHERE ${arguments.timestamp_column} >= DATEADD(day, -${arguments.days}, CAST(GETDATE() AS date))

The keys are the dialect names in the SQL dialect table, so a check for a mysql server needs a duckdb query. The ansi query is optional, but when present it must parse as generic, portable SQL. On SAP HANA, only the ansi query runs.

Overriding the expected result​

The check's expected result is a default. A comparator on the rule replaces it:

quality:
- type: custom
engine: datacontract-cli
implementation:
check: recent_rows
arguments:
timestamp_column: created_at
mustBeGreaterThan: 10000

Results​

A custom quality check is reported in the quality category with the type field_quality_custom or model_quality_custom. Its implementation is the SQL that ran.

SituationResult
No folder configured, no check by that name, or an invalid check fileerror
An argument that is missing, undeclared, an empty list or of the wrong kind, a column check on a schema, no expected resultwarning
No query for the server's dialect and no ansi querywarning
A query that is not a single read-only statementfailed

The API server reads the folder from DATACONTRACT_CUSTOM_QUALITY_CHECKS in its own environment only; a request cannot choose it.