# sqlt
Multi-dialect SQL parser and translator in Rust.
Parses SQL from MySQL, MariaDB, PostgreSQL, MSSQL (T-SQL), and SQLite into a JSON AST, emits SQL back from JSON, and translates between dialects.
> Status: **early development** — see `CHANGELOG.md` for what's shipped and `OUT-OF-SCOPE.md` for what isn't.
## Install
```bash
# Homebrew (macOS / Linux) — via the codedeviate tap
brew install codedeviate/cli/sqlt
# crates.io
cargo install sqlt
# From a clone of this repo
cargo install --path .
```
## Usage
```bash
# Parse SQL → JSON AST
# Emit JSON AST → SQL
# Translate SQL between dialects
sqlt translate --from mariadb --to postgres input.sql
# Lint SQL for common pitfalls and improvement suggestions
sqlt lint --from mariadb schema.sql
sqlt lint --from mariadb --to postgres schema.sql # adds translation pre-flight checks
sqlt lint --from mariadb --format json schema.sql # JSON output
sqlt lint --from mariadb --format sarif schema.sql # SARIF for GitHub code scanning
sqlt lint --explain SQLT0300 # rule documentation
```
Reads from a file path or stdin (use `-` or omit the path).
### Dialects
`mysql`, `mariadb`, `postgres` (alias `postgresql`), `mssql` (alias `tsql`), `sqlite`, `generic`.
MariaDB is a first-class target — features that diverge from MySQL (`RETURNING` on DML, `CREATE SEQUENCE`, system versioning, `FOR SYSTEM_TIME`, Oracle-compat packages) are recognized rather than silently aliased.
### Non-UTF-8 input
Pass `--encoding`/`-e` to read or write SQL in a non-UTF-8 code page. Useful when one of your systems still emits ISO-8859-1 / Latin-1.
```bash
sqlt parse --from mysql -e latin1 export.sql # decode latin1, emit UTF-8 JSON
sqlt translate --from mysql --to mariadb -e latin1 in.sql > out.sql # latin1 in and out
sqlt emit --to mysql -e windows-1252 tree.json # JSON (UTF-8) -> windows-1252 SQL
```
Supported encodings: `utf-8` (default), `iso-8859-1` / `latin1`, `windows-1252` / `cp1252`. Decoding is strict — invalid bytes for the declared encoding are rejected with exit 1 rather than silently replaced.
### Translation gaps
When a construct has no faithful equivalent in the target dialect (e.g. translating `RETURNING` to MySQL), `sqlt translate` emits the closest equivalent and prints a warning to stderr. Pass `--strict` to make any warning a non-zero exit.
### Lint
`sqlt lint` runs ~38 rules across seven categories: dialect cross-contamination, translation pre-flight (when `--to` is set), join hygiene, subquery improvements, performance pitfalls, correctness pitfalls, style/readability, and DDL hygiene. Every rule has a stable id (`SQLT0500`), a slug (`select-star`), and inline documentation (`sqlt lint --explain SQLT0500`).
Common flags:
```
--from <dialect> required; the source SQL dialect
--to <dialect> optional; enables translation pre-flight rules
--no-rule <id>... disable a specific rule (repeatable)
--severity error|warning|info filter the output (rules still run)
--exit-on error|warning|info controls exit code 1 (default error)
--explain <id> print rule documentation and exit 0
-e, --encoding same encoding handling as parse/translate
```
Exit codes match the rest of the CLI: `0` clean, `1` lint findings at or above `--exit-on` (or a parse error), `2` usage error, `3` reserved.
## Development
```bash
cargo build
cargo test
cargo clippy -- -D warnings
cargo fmt --check
```
See `CLAUDE.md` for project conventions (semver, conventional commits, changelog, module map).
## License
MIT — see [`LICENSE`](LICENSE).