Skip to main content

How the comparison was measured

The numbers on the front page come from one run of seven formatters over five corpora. This page is the method, so the run can be argued with.

The tools​

toolversionimplementationtarget width
dfmtthis releasenative binaryyes
sqlfmt0.32.0Pythonyes (--line-length)
sqlfluff4.3.0Pythonyes (max_line_length)
sqruff0.26.5Rustyes (max_line_length)
pg_format5.11Perlyes (-w)
sqlformat (sqlparse)0.6.0Pythonno
sql-formatter15.8.2JavaScriptno

The last two have no target-width setting. They reindent; they do not fit a statement to a column count, which is the expensive part. Their timings are not comparable with the rest and the table keeps them off its scale rather than ranking them.

The corpora​

Public repositories, pinned to an exact commit. A dbt project (Snowflake models with Jinja), Oracle PL/SQL package bodies, a PostgreSQL schema, a T-SQL pipeline, and Hive queries.

How each run works​

Every tool formats in place, from a fresh copy of the corpus, once per run. In place, because "format my repository" is the operation people actually run, and check-only modes do different amounts of work in different tools. Fresh copy, because a second pass over already-formatted files is an easier workload.

  • Median of three runs, on an otherwise idle machine.
  • Target width 80 for every tool that has one. This matters more than it looks: shorter lines are more layout work. An early run left one tool on its default 88 and another on 120 while dfmt ran at 80; correcting that moved one of them from 3.18s to 4.69s.
  • All cores. dfmt and sqlfmt thread by default; sqlfluff is given --processes 0. The three tools with no directory mode are driven the way you would drive them, find | xargs -P, and their process starts are part of their cost.
  • Two figures per cell: the files the tool actually rewrote, counted by comparing against the pristine copy, and the wall time. Neither means much alone.

What we found about the other tools​

These are properties of the tools as installed, at the versions above, not opinions.

  • sqruff has no oracle and no tsql dialect. Asking for one it does not have panics (called Result::unwrap() on an Err value: VariantNotFound) rather than reporting an unsupported dialect. Where it has no dialect for a corpus we run it as ansi and mark the cell.
  • sqruff panics inside its linter on dbt models with the Jinja templater. With raw templating it survives but reads {{ as literal text.
  • sqruff panics when its output is not a terminal unless given --force.
  • sqlfmt exits 0 while leaving a file untouched on T-SQL that uses GO as a batch separator. Run against a directory, one such file's RecursionError aborts the whole run: 187 files in, 0 formatted.
  • sqlfmt's own safety check refuses 29 of 282 Oracle package bodies, reporting that formatting would have changed the token count. Catching that is to its credit; it still cannot format the file.

If you think a tool was configured unfairly, tell us what to change and we will re-run it.