How does syntaqlite compare?

There are many SQL tools out there. This page tests them head-to-head on SQLite-specific SQL, the kind of syntax that trips up generic SQL parsers, and shows raw, reproducible results.

Generated on arm64-darwin with syntaqlite 0.1.0 on 2026-03-14. To reproduce: tools/run-comparison --setup && tools/run-comparison --all. Per-statement breakdowns and reproduction details are in the detailed results.


Parsing

What we test: 40 statements covering advanced SQLite syntax — UPSERT, RETURNING, STRICT tables, window frames with EXCLUDE, numeric underscores, IS NOT DISTINCT FROM, recursive CTEs, and more. Each statement is first validated against sqlite3 itself (the ground truth), then run through every parser. A tool scores "correct" only if it agrees with sqlite3.

Accuracy

ToolCorrectRejects ValidAccepts Invalid
syntaqlite40/40 (100%) ████████████████████--
lemon-rs40/40 (100%) ████████████████████--
sql-parser-cst39/40 (97%) ███████████████████1-
sqlglot[c]35/40 (87%) █████████████████5-
sqlfluff29/40 (72%) ██████████████11-
sqlparser-rs26/40 (65%) █████████████14-
node-sql-parser15/40 (37%) ███████25-

Speed

We benchmark two file sizes: a 40-statement file (startup-dominated) and that file repeated 30× (throughput-dominated).

bench.sql (1×)

CommandMean [ms]Min [ms]Max [ms]Relative
syntaqlite1.7 ± 0.31.66.71.12 ± 0.46
lemon-rs1.5 ± 0.61.320.31.00
sql-parser-cst78.9 ± 7.474.5115.251.81 ± 20.03
sqlglot[c]86.2 ± 3.282.193.456.61 ± 21.33
sqlparser-rs1.8 ± 0.21.63.81.18 ± 0.46
node-sql-parser73.3 ± 1.171.376.048.14 ± 18.07
sqlfluff445.7 ± 5.8437.6457.5292.56 ± 109.79

bench_30x.sql (30×)

CommandMean [ms]Min [ms]Max [ms]Relative
syntaqlite2.5 ± 0.22.45.31.00
lemon-rs4.1 ± 0.14.05.41.62 ± 0.12
sql-parser-cst141.8 ± 2.4139.5151.455.84 ± 4.05
sqlglot[c]182.2 ± 3.9179.4195.271.76 ± 5.28
sqlparser-rs11.0 ± 0.610.315.14.32 ± 0.39
node-sql-parser149.7 ± 1.7147.3155.258.94 ± 4.20
sqlfluff6382.9 ± 40.86333.26426.12513.49 ± 177.72

Formatting

What we test: Round-trip semantic preservation. Each of the same 40 statements is formatted, then we run EXPLAIN on both the original and formatted SQL and compare the bytecode sqlite3 produces. Identical bytecode means sqlite3 will execute the exact same operations, so the formatter preserved semantics, not just validity. Tools that crash or refuse to format score "refused". Tools whose output produces different bytecode score "corrupt".

Accuracy

ToolCorrectCorruptRefused
syntaqlite40/40 (100%) ████████████████████--
prettier-cst39/40 (97%) ███████████████████-1
sql-formatter39/40 (97%) ███████████████████-1
sleek37/40 (92%) ██████████████████3-
sqruff33/40 (82%) ████████████████25
sqlglot[c]31/40 (77%) ███████████████45

Speed

bench.sql (1×)

CommandMean [ms]Min [ms]Max [ms]Relative
syntaqlite1.8 ± 0.11.72.21.00
prettier-cst404.8 ± 5.0397.0412.1228.45 ± 7.74
sql-formatter75.3 ± 1.373.179.542.50 ± 1.53
sqlglot[c]87.4 ± 1.285.792.649.32 ± 1.70
sleek8.4 ± 0.37.99.94.76 ± 0.22
sqruff39.7 ± 0.638.542.022.41 ± 0.80

bench_30x.sql (30×)

CommandMean [ms]Min [ms]Max [ms]Relative
syntaqlite4.9 ± 0.14.85.71.00
prettier-cst558.1 ± 4.2553.9564.7113.23 ± 2.82
sql-formatter199.4 ± 2.8195.7204.640.45 ± 1.11
sqlglot[c]264.5 ± 2.1261.3269.753.66 ± 1.34
sleek27.2 ± 0.626.431.75.52 ± 0.18
sqruff3347.0 ± 62.43262.93432.5679.02 ± 20.51

Validation

What we test: Can the tool catch real SQL errors — unknown tables, bad column references, wrong function arity, CTE column mismatches — without running the query? We define 24 test cases (15 with intentional errors, 9 valid) against a known schema, and check each tool's verdict against sqlite3.

Accuracy

Schema: users, orders, products, order_items. Ground truth: sqlite3.

ToolApproachCorrectMissedFP
sqlite3runtime execution24/24 ████████████████████--
syntaqlitestatic semantic23/24 ███████████████████1-
sql-lintstructural checks12/24 ██████████111
sqlite-runner-lspruntime via LSP9/24 ███████15-

Diagnostic quality

A real query with 2 subtle errors — CTE declares 3 columns but SELECT produces 2, and a typo ROUDN instead of ROUND:

ToolApproachErrors FoundFinds AllDid-you-mean
syntaqlitestatic semantic2/2YesYes
sqlite3runtime execution1/2NoNo
sqlite-runner-lspruntime via LSP0/2NoNo
sql-lintstructural checks0/2NoNo

syntaqlite finds both errors in one pass, with source locations and a did-you-mean suggestion:

error: table 'monthly_stats' has 2 values for 3 columns
  --> /var/folders/rx/t6_rqmqx0f15l7kgp7yjhcbc0000gn/T/tmpico33e5u.sql:30:3
   |
30 |   monthly_stats(month, revenue, order_count) AS (
   |   ^~~~~~~~~~~~~
warning: unknown function 'ROUDN'
  --> /var/folders/rx/t6_rqmqx0f15l7kgp7yjhcbc0000gn/T/tmpico33e5u.sql:42:3
   |
42 |   ROUDN(ms.revenue / ms.order_count, 2) AS avg_order
   |   ^~~~~
   = help: did you mean 'round'?

sqlite3 stops at the first error (runtime execution):

Error: in prepare, table monthly_stats has 2 values for 3 columns

Speed

bench.sql (1×)

CommandMean [ms]Min [ms]Max [ms]Relative
syntaqlite2.0 ± 0.11.93.31.00
sqlite34.7 ± 0.24.46.12.34 ± 0.17
sqlite-runner-lsp10069.1 ± 7.510054.810075.04972.95 ± 270.92
sql-lint356.5 ± 5.9348.7364.1176.07 ± 10.03

bench_30x.sql (30×)

CommandMean [ms]Min [ms]Max [ms]Relative
syntaqlite6.1 ± 0.25.88.21.00
sqlite319.8 ± 8.19.768.53.24 ± 1.34
sqlite-runner-lsp10072.1 ± 4.210066.910078.61650.58 ± 62.91
sql-lint378.1 ± 3.0374.3382.161.97 ± 2.41

LSP (Language Server)

What we test: We start each language server, open a test document, and probe for completions, hover, diagnostics, and formatting. Results are from actual LSP protocol responses, not self-reported feature lists.

Key difference: sqls requires a live database connection for its features. syntaqlite and sql-language-server work offline.

Features

Featuresyntaqlitesqls ¹sql-language-server
CompletionYes (129 items)Yes (6 items)Yes (11 items)
HoverNoYesNo
Go to definitionYesYesNo
Find referencesYesNoNo
Diagnostics: syntaxYesNoYes
Diagnostics: semanticYesNoNo (style only)
FormattingYesYesNo
RenameYesYesYes
Signature helpYesYesNo
Requires DB connectionNoYesNo

¹ sqls requires a live database connection. Completion and hover results come from the connected database schema, not static analysis. Without a database, these features return no results.

Completion depth

ToolItems
syntaqlite129 ████████████████████
sql-language-server11 █
sqls ¹6 █

Startup + response speed

Time from server start → document open → diagnostics received → exit:

CommandMean [ms]Min [ms]Max [ms]Relative
syntaqlite32.3 ± 0.930.334.71.00
sqls10065.8 ± 2.810063.010069.3311.28 ± 8.73
sql-language-server470.5 ± 5.1464.1478.614.55 ± 0.44