syntaqlite
syntaqlite provides a CLI and language server for parsing, formatting, and statically analyzing SQLite SQL. libsyntaqlite contains the parser, formatter, and analyzer used by those tools, with APIs for Rust, Python, JavaScript/WASM, and C.
Both are built from SQLite's own grammar and tokenizer and can target specific SQLite versions and compile-time flags.
Note: syntaqlite and libsyntaqlite are at 0.x. Their APIs and command-line interface may change before 1.0.
What it does
Format
Line width, keyword casing, and indentation are configurable, while the same input always produces the same deterministic output.
select u.id,u.name, p.title from
users u join posts p on u.id
=p.user_id where u.active
=1 and p.published=true
order by p.created_at
desc limit 10
SELECT u.id, u.name, p.title
FROM users u
JOIN posts p ON u.id = p.user_id
WHERE u.active = 1
AND p.published = true
ORDER BY p.created_at DESC
LIMIT 10;
Validate
The analyzer checks tables, columns, and functions against your schema
without opening a database. These are the same errors that
sqlite3_prepare catches, but syntaqlite can report multiple
errors from one analysis and include a source location for each:
CREATE TABLE orders (id, status, total, created_at);
WITH
monthly_stats(month, revenue, order_count) AS (
SELECT strftime('%Y-%m', o.created_at), SUM(o.total)
FROM orders o WHERE o.status = 'completed'
GROUP BY strftime('%Y-%m', o.created_at)
)
SELECT ms.month, ms.revenue, ms.order_count,
ROUDN(ms.revenue / ms.order_count, 2) AS avg_order
FROM monthly_stats ms;
Two errors: CTE declares 3 columns but the SELECT produces 2, and ROUDN is a typo for ROUND.
Error: in prepare, table monthly_stats
has 2 values for 3 columns
Stops after the first error and therefore does not report the ROUDN typo.
error: table 'monthly_stats' has 2
values for 3 columns
|
2 | monthly_stats(month, revenue,
| ^~~~~~~~~~~~~
warning: unknown function 'ROUDN'
|
14 | ROUDN(ms.revenue / ms.order_count,
| ^~~~~
= help: did you mean 'round'?
Version and compile-flag aware
SQLite syntax changes between releases, and compile-time flags enable optional features. syntaqlite accounts for both across the parser, formatter, validator, and LSP.
# RETURNING was added in SQLite 3.35.0
# Check for incompatibility with Android 13's SQLite 3.32.2:
syntaqlite --sqlite-version 3.32.0 analyze \
-e "DELETE FROM users WHERE id = 1 RETURNING *;"
error: syntax error near 'RETURNING'
--> <stdin>:1:32
|
1 | DELETE FROM users WHERE id = 1 RETURNING *;
| ^~~~~~~~~
Validate SQL inside other languages experimental
SQL lives inside Python and TypeScript strings in most real codebases. syntaqlite extracts and validates these strings while preserving interpolation holes and suggesting corrections for misspelled names.
def get_user_stats(user_id: int):
return conn.execute(
f"SELECT nme, ROUDN(score, 2) FROM users WHERE id = {user_id}"
)
warning: unknown function 'ROUDN'
--> app.py:3:23
|
3 | f"SELECT nme, ROUDN(score, 2) FROM users WHERE id = {user_id}"
| ^~~~~
= help: did you mean 'round'?
Parse
The parser returns an abstract syntax tree with side tables for tokens, comments, and whitespace boundaries.
SelectStmt
columns:
ResultColumn
expr:
ColumnRef
column: "id"
ResultColumn
expr:
ColumnRef
column: "name"
from_clause:
TableRef
table_name: "users"
where_clause:
BinaryExpr
op: EQ
left:
ColumnRef
column: "active"
right:
Literal "1"
Editor integration
The language server provides diagnostics, format on save, completions for keywords, functions, tables, and columns, and semantic highlighting without requiring a database connection.
# Install the VS Code extension from the marketplace
ext install syntaqlite.syntaqlite
# Or point any LSP client at this command:
syntaqlite lsp
Design principles
- Reliability: syntaqlite uses SQLite's own tokenizer and grammar rules, verified by running the full SQLite test suite through the parser.
- Speed: in the comparison benchmark, syntaqlite parses 3,500 lines of SQL in 2.6ms, formats them in 4.9ms, and validates them in 7.3ms. The tokenizer avoids copying, the parser uses an arena, and the formatter reuses allocations across inputs.
- Portability: the runtime has no dependencies beyond the C and Rust standard libraries, and runs natively, in WASM, or as a shared library.
- Extensibility: the grammar system supports database engines that extend SQLite's syntax. Define custom grammar rules, AST nodes, and formatting recipes, then load your dialect as a shared library at runtime.
syntaqlite grew out of 8+ years of maintaining PerfettoSQL and scaling it to 100K+ line SQL codebases. See the comparison for how it stacks up against other tools.
What it does not do
- It does not support other SQL engines. syntaqlite is SQLite-only by design, which allows it to use the real grammar rather than a lowest-common-denominator subset.
-
It does not do runtime type checking. It catches what
sqlite3_preparecatches (syntax errors, unknown names), not data-dependent errors like division by zero or type mismatches.
Get started
Pick your starting point: