CLI tool
SQLMesh/sqlmesh avatar
SQLMesh/sqlmesh

sqlmesh plans like Terraform and writes its own unit tests

Scalable and efficient data transformation framework - backwards compatible with dbt.

3,304 stars468 forksPythonApache-2.0

At a glance

What is it?
A data transformation framework that plans and applies changes the way Terraform does, transpiles SQL across dialects at run time, and generates unit tests from a live query. Its dependency list excludes one DuckDB release by name, and its Makefile edits pyproject.toml in place to test old dbt versions.
Who is it for?
sqlmesh suits a data team that wants plan and apply review on model changes, isolated development environments that do not bill warehouse compute, and unit tests generated from a query it already ran. It does not suit a team that wants an unpinned dependency surface, because the ceilings on pandas and dateparser and the single excluded DuckDB release are there for a reason.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 3, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Virtual environments and plan/apply are the core mechanism

The mental model the project asks you to adopt is Terraform's, applied to SQL models. You get virtual data environments, which let you create isolated development environments without data warehouse costs, because the work happens against a local engine rather than against production compute. On top of that sits a plan and apply workflow, described explicitly as being like Terraform, whose stated purpose is to let you understand the potential impact of a change before you commit to it. A CI/CD bot is offered for blue-green deployments, so the plan artefact can become a review gate rather than a local habit. Two supporting claims sit underneath that. The first is that a table is never built more than once, and the second is that SQLMesh tracks which data has been modified so that incremental models run only the transformations that are actually necessary. The stated efficiency argument is narrower than the marketing around it, which matters if you are evaluating it against an existing dbt setup.

SQL in, warehouse dialect out, resolved at run time

You write SQL in any dialect and the framework transpiles it to the target dialect on the fly before sending it to the warehouse, which is what lets the project claim you can debug transformation errors before they run in your warehouse, across 10 or more SQL dialects. The transpiler is `sqlglot`, pinned as `sqlglot~=30.8.0`, so the dialect coverage you get is the dialect coverage of that single line in the dependency list, and a change there changes your SQL semantics. The other stated benefit is that model definitions are plain SQL, with the file making a point of there being no need for redundant and confusing Jinja plus YAML. That is a real difference in approach from dbt-style projects, where the same model is often split between a templated YAML block and a SQL body. Column-level lineage is listed alongside, feeding the same impact analysis the plan step performs. DuckDB is the engine the installer suggests when you initialise a project, so the local development path does not need a warehouse connection to get started.

The generated unit test fixture ends mid-output row

Unit tests here are generated from a live query rather than written by hand, and the command is short:

bash
sqlmesh create_test tcloud_demo.stg_payments --query tcloud_demo.seed_raw_payments "select * from tcloud_demo.seed_raw_payments limit 5"

# run the unit test
sqlmesh test

It writes a file into `tests/`, named after the model. The generated YAML shown alongside carries five input rows, all of them `payment_method: coupon`, and then an `outputs` block whose expectation stops partway through:

yaml
outputs:
    query:
      - payment_id: 66
        order_id:

The last line is a key with no value, so the expected output as printed has one populated column and one null. That is the tail of the example the file uses to illustrate the whole feature, and it ends exactly there. Two smaller things are worth noticing in the same model definition it tests. The amount column is divided by a hundred with a comment saying the source stores cents, and a literal `new_column` is selected as a deliberate non-breaking change example, so the fixture doubles as a demonstration of how the framework treats additive schema changes.

One DuckDB release is excluded by name, and two ceilings are hard

The runtime dependency list is short and mostly open-ended, with three exceptions that are worth reading before you install. DuckDB is requested as `duckdb>=0.10.0,!=0.10.3`, so a single named release in the whole supported range is carved out. `dateparser` is held at `<=1.2.1` and `pandas` at `<3.0.0`, both hard ceilings rather than compatible-release ranges. Everything else floats: click, croniter, humanize, ipywidgets, jinja2, packaging, python-dotenv, requests, ruamel.yaml, tenacity, time-machine and json-stream have no version constraint at all, while `pydantic>=2.0.0` and `hyperscript>=0.1.0` set only floors. `rich[jupyter]` brings the notebook extras along, and `importlib-metadata` is pulled in only on Python below 3.12. `requires-python` is `>= 3.9`, which is a wide floor for a project whose optional extras include one adapter gated on `python_version>="3.10"`. These are the pins that make an install reproducible today and the pins most likely to need touching when an upstream release lands.

bigframes is split out because of a SQLGlot pin conflict

One optional dependency carries a comment explaining why it exists at all, and that comment is the most useful dependency documentation in the project. The `bigframes` extra installs `bigframes>=1.32.0` and nothing else. The stated reason is that it has to be separate to support environments with an older `google-cloud-bigquery` pin, because that pin pulls in an older bigframes, and the bigframes team pinned an older SQLGlot which is incompatible with SQLMesh. So the split is not tidiness, it is a transitive version collision that has to be resolved by letting the caller choose. The same pattern shows up across the adapter extras: athena installs `PyAthena[Pandas]`, azuresql installs `pymssql`, azuresql-odbc installs `pyodbc>=5.0.0`, azuresql-mssql-python installs `mssql-python>=1.1.0` but only on Python 3.10 and up, bigquery installs both `google-cloud-bigquery[pandas]` and `google-cloud-bigquery-storage`, clickhouse installs `clickhouse-connect`, and databricks installs `databricks-sql-connector[pyarrow]>=4.2.6`. Azure alone has three, so picking a warehouse means picking an extra, and the SQLGlot interaction is the reason some of them are shaped the way they are.

The Makefile rewrites pyproject.toml in place to test old dbt versions

The most interesting machinery is not in the product, it is in the compatibility testing. The Makefile has a pattern target, `install-dev-dbt-%`, that takes a dbt version, copies `pyproject.toml` to `pyproject.toml.backup`, and then edits the real file with `sed` before installing. It normalises a bare version into a three-part one first, rewrites the `pydantic>=2.0.0` pin down to a bare `pydantic`, and then pins the dbt packages. For dbt 1.10.0, 1.11.0 and 1.12.0 it takes a separate branch that pins the core package with a compatible-release operator and strips the version constraint from the adapters for bigquery, duckdb, snowflake, athena-community, clickhouse, redshift and trino, while pinning dbt-databricks separately. Everything else gets a single sweep across every `dbt-` package. An `awk` check then adds a `numpy<2` constraint for dbt 1.3 through 1.5 and 1.10, and 1.6.0 has its own handling. Two environment details: the package installer is `uv pip` when `UV` is set and `pip3` otherwise, and the in-place `sed` flag is chosen by uname, with an extra empty argument on Darwin. Every one of those edits lands on a tracked file.

The install block activates the venv twice and pulls the lsp extra

The getting started sequence is the standard virtualenv dance with two additions:

bash
mkdir sqlmesh-example
cd sqlmesh-example
python -m venv .venv
source .venv/bin/activate
pip install 'sqlmesh[lsp]' # install the sqlmesh package with extensions to work with VSCode
source .venv/bin/activate # reactivate the venv to ensure you're using the right installation
sqlmesh init # follow the prompts to get started (choose DuckDB)

The activation line appears twice on purpose, with a comment explaining that it is to make sure you are using the right installation, and it is repeated the same way in the Windows variant, which swaps in `.\.venv\Scripts\Activate.ps1`. Note what is being installed: `sqlmesh[lsp]` is not the minimal package, it is the package plus the extensions for working with VSCode, and the README's first line of guidance therefore hands every new user the editor integration whether or not they want it. There is a note that you may need `python3` or `pip3` instead, depending on the install. The development installer is broader still: the Makefile installs the project with the `dev`, `web`, `slack`, `dlt` and `lsp` extras, and installs `./examples/custom_materializations` as an editable package alongside it.

Three build systems share one tree, and the licences differ by directory

The repository holds more than one project. The Python package lives in `sqlmesh/`, the dbt compatibility layer in `sqlmesh_dbt/`, and alongside them sit `benchmarks/`, `docs/`, `examples/`, `tests/`, `tooling/`, a `vscode/` directory and a `web/` directory. That last pair is a pnpm workspace: `package.json` requires node 22 or newer and pnpm 10 or newer, and its scripts are formatting and lint only, running prettier over `vscode`, `web/client` and `web/common` and delegating with `pnpm run -r`. There is no JavaScript build step in that manifest. Documentation is a third system again, with `mkdocs.yml`, `.readthedocs.yaml` and a `pdoc/` directory. Governance is unusually prominent for a code repository: `GOVERNANCE.md`, a `DCO` file, a `SECURITY.md`, a `CODE_OF_CONDUCT.md`, a `CLAUDE.md` and a `sqlmesh-technical-charter.pdf` at the root, plus a Linux Foundation membership line in the README. Licensing is split rather than uniform: the code is Apache 2.0 and the documentation is CC-BY-4.0.

Editorial conclusion

sqlmesh suits a data team that wants plan and apply review on model changes, isolated development environments that do not bill warehouse compute, and unit tests generated from a query it already ran. It does not suit a team that wants an unpinned dependency surface, because the ceilings on pandas and dateparser and the single excluded DuckDB release are there for a reason. Before adopting, check that your warehouse adapter exists as an optional extra and that the dbt version you rely on is one the Makefile can install, since those targets rewrite pyproject.toml in place. The code is Apache 2.0, the documentation is CC-BY-4.0, and version 0.236.2 shipped on 2026-09-08.

Frequently asked questions

what is sqlmesh

A data transformation framework for SQL and Python models that runs and deploys transformations with what it calls visibility and control at any size. Its distinguishing pieces are virtual data environments for isolated development without warehouse cost, a Terraform-style plan and apply workflow, unit tests generated from a live query, and SQL transpiled to the target dialect at run time.

how to install sqlmesh

Create a directory, make a virtual environment, activate it, then run `pip install 'sqlmesh[lsp]'` and `sqlmesh init`, where the prompts ask you to choose DuckDB. A note says you may need `python3` or `pip3` instead, and a Windows variant of the same block uses `.\.venv\Scripts\Activate.ps1`.

how to use sqlmesh

Follow the quickstart guide, the crash course with its cheat sheet, or the incremental time full walkthrough. The core moves are plan and apply for model changes, `sqlmesh test` to run the generated unit tests, and table diffs between prod and dev scoped to the tables and views a change touches.

is sqlmesh open source

Yes. The project is licensed under the Apache License 2.0 for code, and its documentation is licensed separately under CC-BY-4.0. The README also states that SQLMesh is a project of the Linux Foundation.

what is sqlmesh used for

Running and deploying data transformations written in SQL or Python, with impact analysis before a change reaches the warehouse. That covers isolated development environments without warehouse cost, incremental models that run only what changed, automated audits, table diffs between production and development, and column-level lineage.

Official sources

  1. License: Apache-2.0
  2. Project website
  3. README
  4. Releases
  5. SQLMesh/sqlmesh on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/sqlmesh-sqlmesh.svg)](https://hysenlabs.com/projects/sqlmesh-sqlmesh)