sqlanvil-sqlx-lint
A convention linter for SQLAnvil .sqlx files. SQL
linters (sqlfluff) check the SQL body; sqlanvil compile checks syntax and
sqlanvil validate checks against the warehouse. None of them see the
config-block and project conventions that keep a sqlanvil repo healthy, and
several Dataform habits that sqlanvil silently ignores or fails on at run time.
This tool does, in milliseconds, with no warehouse connection.
Zero dependencies (Python ≥ 3.11 standard library only). Designed for pre-commit. Warehouse-aware: PostgreSQL, Supabase, MySQL/MariaDB, and BigQuery-target projects.
pip install sqlanvil-sqlx-lint
Forked from acuantia/dataform-sqlx-lint (MIT), which was extracted from a production Dataform repository.
Rules
Conventions
| Code | Default | Checks |
|---|---|---|
| E001 | on | config {} block present and balanced |
| E002 | on | non-empty columns: {} on tables/views/incrementals/declarations (declarations may carry columnTypes: {} from sqlanvil introspect instead) |
| E003 | on | schema: must not hardcode an environment suffix (_prod/_dev/_test by default) — --schema-suffix and environments.<name>.schemaSuffix append it, so a literal doubles up (analytics_test_test) |
| E004 | on | name: matching the filename is redundant (declarations exempt) |
| E005 | opt-in | operations/assertions must not set schema: (hasOutput: true operations exempt — schema+name define ${self()}) |
| E006 | on | hardcoded table paths instead of ${ref()} — these silently break the dependency graph. Warehouse-aware: public.orders, "schema"."table", `db`.`table`, `project.dataset.table` |
| E007 | on* | configurable per-directory naming/type policies (*no-op until policies are configured) |
| W008 | opt-in | post_operations {} placed before the main SELECT (style preference) |
| E010 | on | every determinable output column appears in columns: {} — parses the main SELECT conservatively (unparseable expressions are skipped, never false-flagged) and follows select * through a single plain ${ref()} into the upstream file |
Why E002/E010 matter: sqlanvil writes description: and columns: {} into the
warehouse catalog (COMMENT ON TABLE / COMMENT ON COLUMN on Postgres, table
and column descriptions on BigQuery). That is the metadata data catalogs, BI
tools, and AI analytics agents read. Partial blocks leave silent gaps.
sqlanvil deltas
The places where a Dataform/BigQuery habit produces a sqlanvil project that compiles but does the wrong thing. The first three are failures no compiler catches.
| Code | Default | Checks | sqlanvil compile catches it? |
|---|---|---|---|
| S101 | on | bigquery: {}, partitionBy, clusterBy, bigqueryPolicyTags on a non-BigQuery warehouse — ignored silently, never applied |
no |
| S102 | on | ; separating statements in operations / pre_operations / post_operations — sqlanvil splits on a --- line, so the block runs as one statement and fails at run time |
no |
| S105 | on | ADD PRIMARY KEY / ADD CONSTRAINT in an incremental's operations block not wrapped in ${when(!incremental(), …)} — runs on every append and errors the second time |
no |
| S103 | on | postgres.indexes[].method given as a string — it is a numeric enum (BTREE=0, HASH=1, GIN=2, GIST=3, BRIN=4) |
yes |
| S104 | on | incrementalStrategy on a non-BigQuery warehouse |
yes (≥1.29) |
| S106 | on | assertions: sets both uniqueKey and uniqueKeys |
yes |
| S108 | on | .jitCode() / jitData() — no runtime in sqlanvil |
yes (≥1.30) |
Usage
sqlanvil-sqlx-lint definitions/outputs/my_table.sqlx [...]
# exit 0 = clean or warnings only; 1 = errors; 2 = bad config
Run from the project root. The target warehouse is read from
workflow_settings.yaml there (warehouse: postgres|supabase|mysql|bigquery),
and --definitions-root (default ./definitions) is indexed so ${ref()}
targets resolve for E010's star-resolution. Override with --warehouse or the
warehouse config key; the default with nothing configured is postgres.
pre-commit
repos:
- repo: https://github.com/SQLAnvil/sqlanvil-sqlx-lint
rev: v0.2.0
hooks:
- id: sqlanvil-sqlx-lint
Configuration
.sqlx-lint.toml in the project root, or a [tool.sqlx-lint] table in
pyproject.toml (the standalone file wins). All keys optional:
warehouse = "postgres" # else workflow_settings.yaml, else postgres
schema_suffixes = ["_prod", "_dev", "_test"] # E003 suffix list ([] disables)
documented_types = ["table", "view", "incremental", "declaration"] # E002
coverage_paths = ["definitions/outputs/"] # E010 scope; empty = everywhere
allowed_schemas = ["extensions"] # E006 may reference these directly
enable = ["E005", "W008"] # switch on opt-in rules
disable = ["E004"] # switch off default rules
[[dir_policies]] # E007 (repeatable)
path_contains = "definitions/outputs/"
require_prefix = "rpt_"
require_types = ["table", "incremental"]
severity = "error" # or "warning"
See examples/sqlx-lint.toml for a complete
sources → intermediate → outputs layout. System catalogs (pg_catalog,
information_schema, mysql, sys, …) are always allowed.
Adopting on an existing project
A migrated repository will be loud on the first run: E002 on every undocumented model and E010 wherever documentation is partial. Adopt in layers rather than suppressing:
- Start with
disable = ["E002", "E010"]so the S-series and E006 findings, which are actual defects, land first. - Re-enable E002 and scope E010 with
coverage_pathsto the BI-facing layer. - Widen
coverage_pathsas documentation catches up.
Suppressing findings
from public.legacy_events -- sqlx-lint: disable=E006 (declaration repoints at cutover)
or file-wide, anywhere in the file:
-- sqlx-lint: disable-file=E006
Suppress with a reason, sparingly — the convention is usually the fix.
Design notes
- Conservative by construction: the SQL projection parser only claims
column names it can determine (aliases, simple identifiers, resolvable
select *); anything ambiguous is skipped, so E010 never false-flags. - E006 looks only at table positions (
FROM,JOIN,INTO,UPDATE), ignoresFROMused as function syntax (extract(dow from d.date)), function calls (public.my_func(1)), and anything inside${…}. - S102 understands sqlanvil's text:
;inside string literals,$$dollar-quoted PL/pgSQL bodies, and${…}JavaScript regions never count; a single trailing;is fine. - Declarations are exempt from E003/E004 deliberately: raw source schemas
legitimately carry environment-suffixed names, and
name:is required. - Rule codes are stable; gaps in the numbering are historical.
Agent Skill
The sqlanvil-sqlx-lint Agent Skill teaches AI coding agents (Claude Code,
Codex CLI, Cursor, or any tool supporting the open
Agent Skills format) to run this linter on every
.sqlx file they create or modify. It lives with the other SQLAnvil skills:
npx skills add SQLAnvil/agent-skills
Source: https://github.com/SQLAnvil/agent-skills
Development
python3 -m venv .venv && .venv/bin/pip install -e . pytest
.venv/bin/pytest # 103 tests
License
MIT — see LICENSE.
Metadata
Release files for sqlanvil-sqlx-lint 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sqlanvil_sqlx_lint-0.2.1.tar.gz | 24.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sqlanvil_sqlx_lint-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 42.5 kB
Release files / sqlanvil_sqlx_lint-0.2.1.tar.gz
| Download URL | sqlanvil_sqlx_lint-0.2.1.tar.gz |
|---|---|
| Size | 24.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e3c4211dcb077c39d12ed893280afdfb215e5439d87ece0232fe624a0281abfe
|
|
BLAKE2b-256 checksum How to use checksums |
a0546b37dbd8eca3d0cae5e2581b61ecb599ab4d3e1e51636409aabdc32be75d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 2, 2026.
Transparency logRelease files / sqlanvil_sqlx_lint-0.2.1-py3-none-any.whl
| Download URL | sqlanvil_sqlx_lint-0.2.1-py3-none-any.whl |
|---|---|
| Size | 17.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fdfe978b41d59db371bf24bd03dc0224bb7dc93a068520142ddb45b05f6388d7
|
|
BLAKE2b-256 checksum How to use checksums |
55b4c971ab27e894ceef8a296b967f9b78b65bf2871be309a11cc18b02e19c0b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 2, 2026.
Transparency log