QoreDB LogoQoreDB

Data contracts

Premium

Declarative data-quality assertions for your tables. Define rules in YAML, run them on demand, and get alerted when a passing rule starts failing after a mutation.

A data contract is a set of declarative assertions about a table: which columns may not be null, which values are allowed, how many rows the table should hold, which foreign keys must resolve. You describe the expectations once, and QoreDB checks the live data against them — turning "the data looks fine" into a list of rules that either pass or fail.

Contracts are dialect-aware but driver-agnostic: the same contract runs against PostgreSQL (and the Postgres family), MySQL/MariaDB, SQLite, DuckDB, SQL Server, and ClickHouse. The runner generates the dialect-specific SQL for each rule and executes it through the same engine as the rest of QoreDB.

How a contract is shaped

A contract targets one table on one connection and carries a list of rules:

name: orders_quality
version: 1
description: Invariants for the orders table
target:
  connection: prod-pg
  schema: public
  table: orders
rules:
  - id: id_present
    type: not_empty
    column: id
  - id: email_format
    type: regex_match
    column: customer_email
    pattern: "^[^@]+@[^@]+$"
    severity: warning
  - id: status_allowed
    type: allowed_values
    column: status
    values: [pending, paid, shipped, cancelled]
  - id: total_in_range
    type: numeric_range
    column: total_cents
    min: 0

Each rule has an id, a type, and rule-specific fields. The optional severity (error, warning, or info) and enabled flag let you tune how a failure is reported without deleting the rule.

Rule types

QoreDB ships twelve rule types covering the common data-quality checks:

RuleWhat it asserts
not_null_pctAt least threshold_min_pct % of a column is non-null.
not_emptyA column has no null (or empty) values.
regex_matchEvery value in a column matches a regular expression.
length_rangeA column's text length stays within min/max.
numeric_rangeA numeric column stays within min/max (inclusive bounds optional).
date_rangeA date/time column stays within min/max, or within a max_age.
allowed_valuesA column only contains values from a fixed list.
uniqueA column (or combination of columns) has no duplicates.
distinct_countThe number of distinct values stays within min/max.
foreign_key_integrityEvery value resolves to a row in a referenced table/column.
row_countThe table's row count stays within min/max.
custom_sqlA query you write returns no offending rows.

custom_sql is the escape hatch: write any SELECT that returns the rows you consider violations, and the rule fails when that query returns anything.

Running a contract

Open the Contracts panel, pick a contract, and run it against an active connection. The runner walks every enabled rule, executes the generated SQL, and aggregates the outcome into a single run:

  • Each rule reports a status — pass, fail, skipped, or error — along with the number of violations, a measured metric, and timing.
  • Failing rules collect a small sample of offending rows so you can see what tripped the check. Sampling can be turned off for very wide tables.
  • The run rolls up into pass / fail / error counts for the whole contract.

Regression alerts after a mutation

Contracts aren't only an on-demand tool. When you run a mutation through QoreDB on a table that has at least one enabled contract, QoreDB re-evaluates the matching contracts in the background. If a rule that was passing starts failing, it fires a contract.alert notification.

This is deliberately best-effort: it never blocks or slows the mutation, samples are skipped to avoid extra round-trips, and any failure in the check itself is logged and swallowed. The goal is a quiet safety net — you hear about a regression the moment your own edit introduces it, not hours later.

Where contracts live

Contracts are plain files inside the active workspace:

<workspace>/contracts/
  ├── orders_quality.yml          ← the contract (YAML, JSON also accepted)
  └── .history/orders_quality.jsonl   ← append-only run history

Because they're files, contracts version naturally with the rest of your project — commit them, review them in a pull request, share them with the team. The run history keeps roughly the last 200 runs per contract so the panel can show trends without growing unbounded. File names derive from the validated contract name ([A-Za-z_][A-Za-z0-9_]*), so there's no path traversal risk.

Where to go next

Newsletter

Stay updated on new releases

Subscribe to get product releases, new drivers notifications, and technical tutorials.

🎁 Bonus: Get our free SQL Performance Cheat Sheet — 9 pages, PostgreSQL / MySQL / SQLite (PDF)!