CLI for SQL test validation
Project description
SQLCheck
SQLCheck turns SQL files into CI-grade tests with inline expectations. It scans SQL test source
files, extracts directives like {{ success(...) }} or {{ fail(...) }}, executes the compiled
SQL against a target engine, and reports per-test results with fast, parallel execution.
Note: SQLCheck supports DuckDB and Snowflake via built-in adapters (
--engine duckdbor--engine snowflake). For other databases, use thebaseengine with a custom command template viaSQLCHECK_ENGINE_COMMAND.
Features
- Directive-based expectations:
{{ success(...) }}and{{ fail(...) }}directives define expected behavior directly inside SQL test files. - Deterministic parse/compile stage: Directives are stripped to produce executable SQL plus
structured
sql_parsedstatement metadata. - Parallel execution: Run tests concurrently with a configurable worker pool (default: 5).
- CI-friendly outputs: Clear per-test failures, non-zero exit codes, and JSON/JUnit reports.
- Extensible assertions: Register custom functions via plugins.
Installation
From source (recommended during development)
git clone <repo-url>
cd sqlcheck
uv sync
source .venv/bin/activate
uv sync creates .venv by default and installs the sqlcheck entry point into it.
Prerequisites
- Python 3.10+
- SQL execution engine (optional): DuckDB CLI, Snowflake CLI, or custom command via
SQLCHECK_ENGINE_COMMAND
Install DuckDB CLI (optional)
curl https://install.duckdb.org | sh
Quick start
- Create a SQL test file (default pattern:
**/*.sql):
-- tests/example.sql
{{ success(name="basic insert") }}
CREATE TABLE t (id INT);
INSERT INTO t VALUES (1);
SELECT * FROM t;
- Run sqlcheck with your preferred engine:
# Using DuckDB (recommended for getting started)
sqlcheck run tests/ --engine duckdb
# Using Snowflake with connection profile
sqlcheck run tests/ --engine snowflake --engine-arg "-c dev"
# Using a custom engine command
SQLCHECK_ENGINE_COMMAND="psql -f {file_path}" sqlcheck run tests/
If any test fails, sqlcheck exits with a non-zero status code.
SQLTest directives
Directives are un-commented blocks in the SQL source:
{{ success(name="my test", tags=["smoke"], timeout=30, retries=1) }}
{{ fail(error_contains="permission", error_regex="denied") }}
success(...): Asserts the SQL executed without errors.fail(...): Asserts the SQL failed, optionally matching error text witherror_containsand/orerror_regex.
If no directive is provided, sqlcheck defaults to success(). The name parameter is optional;
when omitted, the test name defaults to the file path.
CLI usage
sqlcheck run TARGET [options]
Options
--pattern: Glob for discovery (default:**/*.sql).--workers: Parallel worker count (default: 5).--engine: Execution adapter (default:base).--engine-arg: Extra args for the engine command (supports shell-style quoting, repeatable).--json: Write JSON report to path.--junit: Write JUnit XML report to path.--plan-dir: Write per-test plan JSON files to a directory.--plugin: Load custom expectation functions (repeatable).
Engine configuration
SQLCheck supports multiple SQL engines through built-in adapters and custom command templates.
Built-in adapters
Use the --engine parameter to select a built-in adapter:
# DuckDB (in-memory database)
sqlcheck run tests/ --engine duckdb
# DuckDB with a persistent database file
sqlcheck run tests/ --engine duckdb --engine-arg /path/to/database.db
# Snowflake (uses snow CLI)
sqlcheck run tests/ --engine snowflake --engine-arg "-c dev"
Available engines:
duckdb- DuckDB CLI (requiresduckdbin PATH)snowflake- Snowflake CLI (requiressnowin PATH)base- Custom command viaSQLCHECK_ENGINE_COMMAND(default)
Custom engines with SQLCHECK_ENGINE_COMMAND
For engines without a built-in adapter, use the base engine with a custom command template:
# Using environment variable
SQLCHECK_ENGINE_COMMAND="psql -f {file_path}" sqlcheck run tests/
# With inline SQL (using stdin)
SQLCHECK_ENGINE_COMMAND="mysql -u root -p" sqlcheck run tests/
# With SQL as command argument
SQLCHECK_ENGINE_COMMAND="clickhouse-client --query {sql}" sqlcheck run tests/
Template variables:
{file_path}- Path to a temporary file containing the SQL{sql}- The SQL query as a command-line argument (properly quoted){engine_args}- Additional arguments passed via--engine-argflags
Examples with template variables:
# Databricks with engine args (each arg passed separately)
SQLCHECK_ENGINE_COMMAND="databricks sql --warehouse-id {engine_args}" \
sqlcheck run tests/ --engine-arg "abc123"
# Snowflake with multiple args
SQLCHECK_ENGINE_COMMAND="snow sql -c {engine_args} -f {file_path}" \
sqlcheck run tests/ --engine-arg "dev"
# PostgreSQL with multiple connection parameters
SQLCHECK_ENGINE_COMMAND="psql {engine_args} -f {file_path}" \
sqlcheck run tests/ --engine-arg "-h localhost -d mydb"
# Using inline SQL
SQLCHECK_ENGINE_COMMAND="psql -h localhost -d mydb -c {sql}" \
sqlcheck run tests/
How it works:
- If
{file_path}is used, SQLCheck creates a temporary.sqlfile - If
{sql}is used, SQL is passed as a command argument - If neither is used, SQL is piped to stdin (default behavior)
{engine_args}is replaced with all--engine-argvalues joined by spaces--engine-argsupports shell-style quoting, so you can write:--engine-arg "-c dev"(simple case, parsed into two args:-canddev)--engine-arg '-h localhost -d "my database"'(with quoted strings containing spaces)--engine-arg "-c" --engine-arg "dev"(or use multiple flags if you prefer)
Reports
- JSON: machine-readable summary of each test and its results.
- JUnit XML: CI-friendly test report format.
- Plan files: per-test JSON containing statement splits, directives, and metadata.
Contributing
Development setup
uv sync --extra dev
Plugin functions
Create a Python module with a register(registry) function:
# my_plugin.py
from sqlcheck.models import FunctionResult
def register(registry):
def assert_rows(sql_parsed, status, output, min_rows=1, **kwargs):
# Implement logic here based on stdout/stderr or engine-specific output
return FunctionResult(name="assert_rows", success=True)
registry.register("assert_rows", assert_rows)
Run with:
sqlcheck run tests/ --plugin my_plugin
Running tests
python -m unittest discover -s tests
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pysqlcheck-0.0.1.tar.gz.
File metadata
- Download URL: pysqlcheck-0.0.1.tar.gz
- Upload date:
- Size: 12.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a2009847e967ae838252acabb91a59a95508f9e0f5a10c2fafa762f3f1eaff51
|
|
| MD5 |
4082969b6428fffaab5539341c760234
|
|
| BLAKE2b-256 |
dd5271579542aed377fe85ff144aa47ce67275677c1ee493bdd7b9eca0b227e5
|
Provenance
The following attestation bundles were made for pysqlcheck-0.0.1.tar.gz:
Publisher:
deploy.yml on luisggc/sqlcheck
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pysqlcheck-0.0.1.tar.gz -
Subject digest:
a2009847e967ae838252acabb91a59a95508f9e0f5a10c2fafa762f3f1eaff51 - Sigstore transparency entry: 785617030
- Sigstore integration time:
-
Permalink:
luisggc/sqlcheck@949b4c1ec759edf8b8b684680affda0207525b06 -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/luisggc
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
deploy.yml@949b4c1ec759edf8b8b684680affda0207525b06 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pysqlcheck-0.0.1-py3-none-any.whl.
File metadata
- Download URL: pysqlcheck-0.0.1-py3-none-any.whl
- Upload date:
- Size: 17.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
37c400ef93db4acd25d56e7df4e562b22fb19fbb603df4f6bcc96981b859d8ed
|
|
| MD5 |
db186f2d2f7cfcc0c82f68eea5d9724a
|
|
| BLAKE2b-256 |
8ae7ed9e9fc6409f4e169bb15c4e027e9caa91c83f94230439d23b066295d823
|
Provenance
The following attestation bundles were made for pysqlcheck-0.0.1-py3-none-any.whl:
Publisher:
deploy.yml on luisggc/sqlcheck
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pysqlcheck-0.0.1-py3-none-any.whl -
Subject digest:
37c400ef93db4acd25d56e7df4e562b22fb19fbb603df4f6bcc96981b859d8ed - Sigstore transparency entry: 785617122
- Sigstore integration time:
-
Permalink:
luisggc/sqlcheck@949b4c1ec759edf8b8b684680affda0207525b06 -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/luisggc
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
deploy.yml@949b4c1ec759edf8b8b684680affda0207525b06 -
Trigger Event:
release
-
Statement type: