Dimitri Fontaine:介绍 sqlfmt:一款 gofmt 风格的 SQL 格式化工具

Dimitri Fontaine:介绍 sqlfmt:一款 gofmt 风格的 SQL 格式化工具

💡 原文英文,约1000词,阅读约需4分钟。
📝

内容提要

sqlfmt 是一款受 gofmt 启发的 SQL 格式化工具,采用单一风格,无配置选项。其核心是“河流对齐”,将 select、where 等关键字右对齐,并支持小写关键字、尾逗号等规则。工具基于分词器而非 AST,能处理注释和部分语法,提供 CLI、Web 演示及编辑器集成,适合独立 SQL 文件维护。

🔎

延伸解读

河流对齐的取舍

sqlfmt 的河流对齐风格将 select、where 等关键字右对齐,但 group by 和 order by 因长度较长而无法对齐,只能左对齐。这种设计并非例外,而是对齐规则的副作用。读者需注意,这种风格可能不适用于所有 SQL 场景,尤其是深度嵌套子查询或特殊 DDL,工具在这些情况下仍为尽力而为。

分词器与 AST 的权衡

sqlfmt 选择基于分词器而非 AST,主要因为注释处理和鲁棒性。PostgreSQL 解析器会丢弃注释,AST 方案需额外恢复注释;而分词器能优雅处理不完整或无效的 SQL,适合 Web 演示。但这也意味着它无法像 AST 那样深入理解查询结构,可能影响复杂语句的格式化准确性。

CI 集成与正确性保障

sqlfmt 的 CLI 支持 -l 参数,可在 CI 中检查格式是否合规,若文件需要格式化则退出码为 1,便于集成到 pull request 流程。同时,测试套件使用 pg_query_go 作为基准,通过指纹比对确保格式化不改变查询语义,为自动化格式化提供了正确性保障。

Q&A

sqlfmt 是什么?它的设计理念是什么?

sqlfmt 是一款受 gofmt 启发的 SQL 格式化工具,采用单一风格,无配置选项。其设计理念是提供一种固定的、无需配置的格式化方式,让开发者直接运行并提交结果。

sqlfmt 的核心风格是什么?

sqlfmt 的核心风格是“河流对齐”,即将每个查询嵌套级别的子句关键字(如 select、from、where 等)右对齐到同一列,使关键字形成垂直的“河流”,后面的表达式自然向右延伸。此外,它还采用小写关键字、尾逗号等规则。

sqlfmt 的 CLI 用法有哪些?如何集成到 CI 流程?

sqlfmt 的 CLI 用法与 gofmt 类似,支持 -w(原地重写)、-l(列出需要格式化的文件)、-d(显示差异)等参数。在 CI 中,可以使用 `sqlfmt -l $(git diff --name-only '*.sql')` 来检查格式,如果文件需要格式化则退出码为 1,从而强制风格检查。

sqlfmt 支持哪些编辑器集成?

sqlfmt 支持 Emacs 和 Vim/Neovim。在 Emacs 中,通过加载 sqlfmt.el 并启用 sqlfmt-mode,可以使用 C-M-h 选择语句并按 TAB 格式化,或通过 sqlfmt-before-save-hook 实现保存时格式化。在 Vim/Neovim 中,插件将 sqlfmt 接入 formatprg/equalprg,可以使用 gqip 或 gg=G 等操作格式化当前段落或整个缓冲区。

为什么 sqlfmt 选择使用分词器而不是 AST?

sqlfmt 选择分词器而非 AST 有两个主要原因:一是 PostgreSQL 解析器会丢弃注释,基于 AST 的格式化器需要额外的注释恢复步骤,失去优势;二是分词器对不完整或无效的输入更鲁棒,能优雅降级,适合 Web 演示等场景。此外,河流对齐关注的是 token 的位置而非语义结构,分词器更自然。

sqlfmt 如何确保格式化不改变查询语义?

sqlfmt 的测试套件使用 pganalyze/pg_query_go(包装了真实的 PostgreSQL C 解析器)作为正确性基准,通过比较格式化前后的查询指纹(fingerprint)来确保格式化不会改变查询的含义。

sqlfmt 当前的状态和局限性是什么?

sqlfmt 是一个可用的实现,核心功能(分词器、河流对齐布局引擎、注释处理、CLI)已就绪,并通过了真实书籍查询的往返测试。但深度嵌套的子查询和特殊 DDL 仍为尽力而为,可能无法完美格式化。

🏷️

标签

➡️

继续阅读