# syntaqlite builds its parser from SQLite's own grammar, so version drift stops

> syntaqlite is a Rust project that formats, analyzes and serves SQLite SQL, with a parser and tokenizer derived from SQLite's own source rather than from a generic SQL grammar. The result understands version-specific syntax and compile flags, and it ships as a CLI, a language server and an embeddable library.

**LalitMaganti/syntaqlite** — A fast parser, formatter, static analyzer, and language server for SQLite SQL.

- Repository: https://github.com/LalitMaganti/syntaqlite
- Stars: 828 · Forks: 17
- Language: Rust
- License: Apache-2.0
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/lalitmaganti-syntaqlite

## The parser is generated from SQLite's parse.y

The problem syntaqlite names in its own README is that most SQL tooling treats SQLite as a variation of generic SQL. That works for common queries, but it misses SQLite specific syntax and the differences introduced by SQLite versions and compile time flags.

The fix is architectural. syntaqlite builds its parser and tokenizer from SQLite's own source. The How it works section explains the pipeline: the parser generator consumes SQLite's parse.y grammar and combines the generated parser with SQLite's tokenizer, and then a small hand maintained layer folds the concrete syntax tree into an AST.

That last detail is the design decision worth understanding. Keeping the folding layer separate from the grammar is what makes SQLite version updates less fragile. The grammar itself is not reimplemented, it is taken from upstream, so a new SQLite release brings its new syntax along, and the small layer that converts a parse tree into a nicer AST is the only place that needs attention.

The language split follows from it. The parser and tokenizer are written in C, so they can be used in the same environments as SQLite itself. The formatter, the analyzer and the language server are written in Rust. The primary language of the repository is Rust, and the C core is there to inherit SQLite's own portability guarantees.

The correctness claim is specific and checkable. The parser has been checked against roughly 396,000 statements from SQLite's upstream test suite, with about 99.7 percent agreement on whether a statement should parse. That is a conformance statement about accepting and rejecting the same SQL as SQLite, not a speed claim.

## Global version and cflag options shape every subcommand

The most useful feature in the tool is also the one most SQL linters do not have. The SQLite version and the compile flags are global options shared by the parser, the formatter, the analyzer and the language server, so one setting applies everywhere.

The README gives a concrete demonstration with RETURNING, a clause that only exists from SQLite 3.35.0 onwards. Checking a query as SQLite 3.32.0 produces a syntax error.

```bash
syntaqlite --sqlite-version 3.32.0 analyze \
  -e "DELETE FROM users WHERE id = 1 RETURNING *;"
```

```text
error: syntax error near 'RETURNING'
 --> <expression>:1:32
  |
1 | DELETE FROM users WHERE id = 1 RETURNING *;
  |                                ^~~~~~~~~
```

That is a real difference in kind from a generic linter. A generic parser either knows RETURNING or it does not, and when it does not, every user of that syntax sees an error whether or not their database supports it. Here the version is a parameter, so a project with a mix of SQLite versions can check each query against the version it will actually meet.

The same mechanism handles optional compile time features.

```bash
syntaqlite --sqlite-cflag SQLITE_ENABLE_MATH_FUNCTIONS analyze query.sql
```

This matters because a fair number of SQLite behaviours are gated behind flags at build time rather than by version. A parser that ignores the flags will disagree with the deployed binary in a way that is very hard to diagnose from the error message alone.

The option ordering is worth noting: the flags come before the subcommand, so they are global rather than per invocation.

## The analyzer runs against a schema file, not a live database

The analyzer is the part that finds mistakes, and the mechanism is worth understanding: it takes your schema and works without opening a database.

Give it a schema file and a query and it reports errors that would otherwise only appear at runtime.

```bash
syntaqlite analyze --schema schema.sql -e "SELECT nme, email FROM users"
```

```text
error: unknown column 'nme'
 --> <expression>:1:8
  |
1 | SELECT nme, email FROM users
  |        ^~~
  = help: did you mean 'name'?
```

The diagnostic format is a source span with a caret under the offending token, and it includes a suggestion. That suggestion matters: a column typo caught in CI with a did you mean hint is a different experience from the same typo surfacing as an OperationalError in production.

The README also states that the analyzer can report multiple independent diagnostics in one pass. One error per run would make it unusable in CI over a large migration directory, and the analyzer in this project is explicitly built for that mode.

The schema is supplied as a file, and a syntaqlite.toml file can associate different groups of SQL files with different schema files. The CLI and the language server both read that configuration, so a monorepo with several databases can keep the mapping in one place and get per directory schemas in both the command line and the editor.

## Formatting and the parse tree view

The formatter is the part you will use most, and the CLI exposes it through the fmt subcommand with an expression flag for inline SQL and a path for files.

```bash
syntaqlite fmt -e "select id,name,email from users where active=1 and role='admin' order by name"
```

```sql
SELECT id, name, email
FROM users
WHERE
  active = 1
  AND role = 'admin'
ORDER BY
  name;
```

The output is uppercased keywords, one column per line in the select list, and boolean conditions split across lines under the WHERE clause with the AND at the start of the continuation line. That layout is a style choice, and it is the kind of thing teams either adopt wholesale or not at all.

Two flags matter for CI. The -i flag formats a file in place, and the --check flag checks formatting without changing anything, which is the form you want in a pre-commit hook or a pipeline step.

There is also a parse subcommand that prints the full abstract syntax tree for a query, which is the debug view for when the parser and your expectation disagree.

```bash
syntaqlite parse -e "SELECT 1 + 2"
```

That capability exists because the AST retains comments and whitespace. The README calls this out specifically as making the library suitable for migration tools, code generation and other source to source work. A tree that drops comments is fine for analysis and useless for rewriting, so the retention decision shapes what the library can be used for beyond linting.

## One library, four language surfaces

syntaqlite is two products in one repository. The CLI and the language server are the user facing tools. libsyntaqlite is the embeddable core, and the README is explicit that it provides the parser, formatter and static analyzer used by both of them.

The library is available through several ecosystems.

- Rust: `cargo add syntaqlite`
- Python: `pip install syntaqlite`
- JavaScript/WASM: `npm install syntaqlite`
- C: parser, tokenizer, formatter and analyzer APIs

The C API matters more than it might look. Since the parser and tokenizer are C and can be used in the same environments as SQLite, an application that already embeds SQLite can link the same parsing code rather than shelling out or reimplementing.

The JavaScript/WASM build is what lets the same analyzer run in a browser, and the repository carries a syntaqlite-wasm crate alongside a web directory with a playground site at playground.syntaqlite.com. The Python package is the one most likely to land in an existing data pipeline, since it can analyse a directory of migrations without a database anywhere in sight.

The repository is a Cargo workspace with crates split by concern: syntaqlite-common, syntaqlite-syntax, syntaqlite, syntaqlite-buildtools and syntaqlite-wasm, plus a syntaqlite-cli crate and a tests/benches member. There is also a dialects directory, and the README describes how SQLite based dialects can add grammar, AST nodes, functions and formatting rules while reusing the same runtime, with a code generation workflow in the custom dialect guide.

That last point is the most interesting for anyone with a fork of SQLite. The project is not only a tool for reading SQLite SQL, it is a base for building SQLite variants.

## Editor support and the language server

The language server provides diagnostics, completion, formatting, semantic highlighting, rename and navigation, and like the analyzer it needs no live database connection. The completion and navigation come from the project wide index the analyzer maintains over your schema files.

The README lists the setup routes. There is a VS Code extension named syntaqlite: SQLite language server and formatter on the marketplace, a Zed setup guide, a generic route for other editors, and a Claude Code plugin.

The plugin install command is claude plugin install syntaqlite@lalitmaganti-plugins.

For any other editor the route is to configure the LSP client to start the server yourself.

The server is started with syntaqlite lsp.

The no-database requirement is what makes the editor experience different from a database-backed tool. Connecting your editor to a live database means credentials in your editor configuration, a connection that can be down, and diagnostics that depend on the current state of the data. Reading schema files instead means the diagnostics are reproducible, work offline, and match what your CI will see.

The syntaqlite.toml configuration is shared between the CLI and the language server, so the schema mapping you set up for the command line applies in the editor without a second configuration file.

## Where syntaqlite is the wrong tool, and what the project says about itself

The scope is narrow by design, and it excludes most SQL work. This is a tool for SQLite only. A PostgreSQL or MySQL project gains nothing from it, because the parser is generated from SQLite's parse.y and the tokenizer is SQLite's own. The dialects directory extends the SQLite family, not the SQL family.

The second limitation is the version model. The SQLite version and compile flags are global options on the command line, and the README gives no hint of a per-file or per-project version setting beyond the schema mapping in syntaqlite.toml. If your repository contains SQL written for several SQLite versions at once, the analysis is only as accurate as the version you pass on the command line.

The third is the project status, and the README does not soften it. It says syntaqlite is usable today, but that it is still a 0.x project, and then it lists the parser, formatter, analyzer, language server and libsyntaqlite APIs as all available. So the feature set is complete while the versioning promises are not, and a 0.x version number conventionally means breaking changes are allowed.

There is also a performance claim to treat carefully. The README states that in reproducible head to head throughput benchmarks syntaqlite had the lowest total run time among the parsers, formatters and analyzers tested, and that the measurements include process startup as well as time inside the library, with the full comparison linked for numbers, methodology and tool versions. The design rationale behind that is credible on its own terms: the tokenizer and parser are written in C, parsing is incremental, and the library APIs reuse allocations across calls. But the comparison is against the tools the project chose to test, and the numbers are on the project own site.

The maintenance position is solid. The repository is not archived, the last push was on 2026-10-14, and releases v0.12.0, v0.12.1 and v0.13.0 landed between 2026-09-27 and 2026-10-13. The licence is Apache-2.0, which carries the usual patent grant and requires you to keep the notice and state changes.

## How it compares with a generic SQL linter

The realistic alternative is a general purpose SQL formatter and linter, or a database specific tool that connects to a live instance.

The difference is the source of truth. A generic linter has its own grammar, and where SQLite diverges from the standard, the linter either accepts things SQLite rejects or rejects things SQLite accepts. The second case is the expensive one, because it means the linter is wrong about code that runs fine in production. syntaqlite's answer is to not have its own opinion: it uses SQLite's grammar and SQLite's tokenizer, so acceptance matches upstream, and the reported agreement figure of about 99.7 percent on SQLite's own test suite is the measurement of exactly that property.

The second difference is the input. A tool that connects to a database knows the live schema, which sounds like an advantage, but it couples your editor and your CI to a running service and to credentials. syntaqlite analyses schema files, so a migration can be checked in a pull request with nothing running. For teams with several environments and schema drift, file based analysis is the more repeatable option, and it is what makes the editor experience work offline.

The third difference is the output surface. The AST keeps comments and whitespace, which is what turns the library from a linter into a rewriting tool. A generic linter gives you diagnostics. This one gives you a tree you can walk and rewrite, which is what migration tooling and code generation need.

The cost is specificity. You give up support for every other database, you accept a 0.x API, and you take the dependency on a project whose grammar pipeline has to be kept in step with SQLite releases.

## Conclusion

syntaqlite fits a team that works in SQLite files, migrations or embedded queries and needs a formatter, a schema-aware analyzer and editor support that understand SQLite syntax rather than a generic SQL dialect, including the version and compile-time flags a deployment actually runs with. It does not fit a project on PostgreSQL, MySQL or any other engine, since the whole design is anchored to SQLite's parse.y and tokenizer. Before adopting, pin the SQLite version and compile flags your deployment uses rather than accepting the defaults, run the analyzer against your real schema files, and read the project status section in full, because the README states plainly that this is still a 0.x project even though every part of the feature set is available.

## FAQ

### How do I install syntaqlite?

On macOS, Linux or Windows you can run curl -sSf https://raw.githubusercontent.com/LalitMaganti/syntaqlite/main/tools/syntaqlite | python3 - install. The README also lists mise use github:LalitMaganti/syntaqlite, pip install syntaqlite, brew install LalitMaganti/tap/syntaqlite and cargo install syntaqlite-cli as alternatives.

### How does syntaqlite check SQL against a schema?

Pass the schema file to the analyzer with the --schema option, for example syntaqlite analyze --schema schema.sql -e "SELECT nme, email FROM users". The analyzer works without opening a database and can report multiple independent diagnostics in one pass.

### How do I make syntaqlite aware of my SQLite version?

Use the global --sqlite-version option before the subcommand, for example syntaqlite --sqlite-version 3.32.0 analyze. Optional compile time features are enabled the same way with --sqlite-cflag, such as SQLITE_ENABLE_MATH_FUNCTIONS.

### Which editors does syntaqlite support?

There is a VS Code extension, a Zed setup guide, a Claude Code plugin installed with claude plugin install syntaqlite@lalitmaganti-plugins, and a generic route for any other editor by configuring the LSP client to start syntaqlite lsp. The language server works without a live database connection.

### Can I use syntaqlite as a library from Python or JavaScript?

Yes. libsyntaqlite exposes the parser, formatter and static analyzer through Rust, Python, JavaScript/WASM and C APIs. The Python install is pip install syntaqlite, the npm package is syntaqlite, and the Rust crate is added with cargo add syntaqlite.

### Is syntaqlite still a 0.x project?

The README says syntaqlite is usable today but is still a 0.x project, while also stating that the parser, formatter, analyzer, language server and libsyntaqlite APIs are all available. The most recent releases are v0.13.0 on 2026-10-13, v0.12.1 on 2026-09-28 and v0.12.0 on 2026-09-27.

## Sources

- [Issues](https://github.com/LalitMaganti/syntaqlite/issues)
- [LalitMaganti/syntaqlite on GitHub](https://github.com/LalitMaganti/syntaqlite)
- [License: Apache-2.0](https://github.com/LalitMaganti/syntaqlite/blob/main/LICENSE)
- [README](https://github.com/LalitMaganti/syntaqlite/blob/main/README.md)
- [Releases](https://github.com/LalitMaganti/syntaqlite/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/lalitmaganti-syntaqlite
