sql-dialect-fmt
sql-dialect-fmt is an opinionated Rust formatter and editor toolchain for Snowflake SQL and
Databricks SQL. It is built around a lossless lexer/parser pipeline: unsupported or malformed input
is preserved, comments survive formatting, and repeated formatting is stable.
Use the playground to run the WebAssembly formatter in the browser, or install the CLI for local and CI usage.
cargo install sql-dialect-fmt --version 1.22.3 --locked
sql-dialect-fmt --check sql/**/*.sql
sql-dialect-fmt --write sql/**/*.sql
Coverage
- Snowflake SELECT, DML, COPY, major DDL and object DDL.
- Snowflake Scripting and routine bodies in SQL, JavaScript, Python, Java, and Scala.
- Databricks mode for backtick identifiers, LATERAL VIEW, Delta DDL options, time travel, higher-order function lambdas, and SQL scripting blocks.
- LSP, semantic tokens, hover text, and a bundled VS Code/WASM extension. Tree-sitter work is retained but paused outside the active workspace and CI.
The detailed tracker lives in spec/seed/features.json; run:
python3 spec/snowflake_spec.py coverage
Playground
The formatter runs locally in the browser through sql-dialect-fmt-wasm; SQL text is not sent to a
server. The controls expose the same dialect, width, indentation, keyword-case, SELECT-layout,
comma-style, and line-ending options as the CLI, LSP, and VS Code extension.
CLI Reference
sql-dialect-fmt [OPTIONS] [PATH ...]
With no paths, or with -, the CLI reads stdin and writes formatted SQL to stdout. Directory
arguments are searched recursively for *.sql files while common generated directories such as
.git, node_modules, and target are skipped.
Modes
| Mode | Command | Exit behavior |
|---|---|---|
sql-dialect-fmt query.sql | formatted SQL on stdout | |
| Write | sql-dialect-fmt --write query.sql | rewrites files in place |
| Check | sql-dialect-fmt --check sql/ | exits 1 if any file would change |
| Diff | sql-dialect-fmt --check --diff sql/ | prints unified diffs for unformatted files |
Options
| Option | Meaning |
|---|---|
--dialect snowflake|databricks | Select SQL dialect. |
--line-width N | Target print width. |
--indent-width N | Spaces per indent level. |
--keyword-case upper|lower|preserve | Choose recognized keyword casing. |
--select-item-layout auto|vertical | Keep short SELECT lists inline or put one item per line. |
--comma-style trailing|leading | Place commas at the end or start of wrapped list lines. |
--line-ending auto|lf|crlf | Preserve or select output line endings. |
--no-uppercase | Deprecated shorthand for preserving keyword casing. |
--stdin-filepath PATH | Use a path for stdin config discovery and diagnostics. |
--no-config | Ignore sql-dialect-fmt.toml. |
--write | Rewrite path arguments in place. |
--check | Verify formatting without writing. |
--diff | Show diffs with --check. |
Homebrew
brew tap hjosugi/sql-dialect-fmt https://github.com/hjosugi/sql-dialect-fmt
brew install sql-dialect-fmt
CI
- uses: hjosugi/sql-dialect-fmt@v1
with:
args: "sql/**/*.sql"
docker run --rm -v "$PWD:/work" -w /work ghcr.io/hjosugi/sql-dialect-fmt:1.22.3 --check .
pre-commit
repos:
- repo: https://github.com/hjosugi/sql-dialect-fmt
rev: v1.22.3
hooks:
- id: sql-dialect-fmt
Use sql-dialect-fmt-check for verification-only hooks.
Formatting Style
The formatter is intentionally small on options. The expected shape is stable output that can run in CI without team-local style debates.
Statements
- SQL keywords are upper-cased by default.
- Statements end with semicolons.
- Top-level
SELECTitems are vertical by default;select_item_layout = "auto"enables width-aware inline lists. - Wrapped lists use trailing commas by default;
comma_style = "leading"moves separators to the next item. - Major clauses such as
SELECT,FROM,WHERE,GROUP BY,QUALIFY, andORDER BYstart on their own lines when the query is multi-line. - Other comma-separated lists use one item per line when they do not fit within the configured width.
SELECT
customer_id,
COUNT(*) AS orders,
SUM(total_amount) AS revenue
FROM analytics.orders
WHERE order_status = 'paid'
GROUP BY customer_id;
Losslessness
Formatting must preserve the meaningful token stream. Inputs with lexer or parser errors are returned unchanged rather than reformatted through an uncertain tree.
Comments are preserved as leading, trailing, or dangling comments according to their CST position. Routine bodies and embedded language blocks are kept inside their original SQL delimiters.
Width And Indentation
line_width controls when groups break. indent_width controls the spaces added for nested
structures. Both options are available through the CLI, config files, the VS Code extension, and the
WASM playground.
All entry points share these defaults:
| Option | Default |
|---|---|
line_width | 80 |
indent_width | 2 |
keyword_case | upper |
select_item_layout | vertical |
comma_style | trailing |
line_ending | auto |
Development
The workspace is split into small crates so parser, formatter, editor, and distribution changes can be tested at the layer that owns the behavior.
source bytes
-> sql-dialect-fmt-encoding
-> sql-dialect-fmt-lexer
-> sql-dialect-fmt-parser
-> sql-dialect-fmt-formatter
Standard Gates
cargo fmt --all --check
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings
RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps
cargo bench -p sql-dialect-fmt-formatter --bench format -- --test
Fuzzing
cargo install cargo-fuzz --locked
cargo +nightly fuzz run lexer_roundtrip
cargo +nightly fuzz run parser_lossless
cargo +nightly fuzz run formatter_idempotent
The scheduled fuzz workflow runs the same targets and uploads crash artifacts.
Corpus
The committed formatter corpus lives in
crates/sql-dialect-fmt-formatter/tests/corpus_sample/. Larger private or generated corpora should
use the external harness:
scripts/run-external-corpus.sh --path /path/to/sqls --limit 500
scripts/conformance-report.py --path crates/sql-dialect-fmt-formatter/tests/corpus_sample \
--out target/conformance-report.md
See also docs/ARCHITECTURE.md, docs/TESTING.md, and docs/CORPUS.md in the repository.
Release And Distribution
The project uses one workspace version in the root Cargo.toml. Published crates inherit that
version and internal dependency versions are centralized in [workspace.dependencies].
Release Gate
task test
task clippy
RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps
cargo bench -p sql-dialect-fmt-formatter --bench format -- --test
task fmt:check
scripts/run-external-corpus.sh --sample
scripts/conformance-report.py --path crates/sql-dialect-fmt-formatter/tests/corpus_sample \
--out target/conformance-report.md
Assets
task vscode:package builds and validates the VS Code VSIX under target/dist/. Version tags
create the GitHub Release, publish release binaries, and push the GHCR
image. Store publishing remains gated by repository variables and secrets documented in
docs/STORE_PUBLISHING.md.
Docs Site
scripts/build-docs-site.sh
The docs workflow builds this mdBook, copies the WebAssembly formatter into the site output, and
deploys GitHub Pages from main.