📣 极限科技诚招搜索运维工程师(Elasticsearch/Easysearch)- 全职/北京 👉 : 立即申请加入

适用版本: 6.8-8.9

1. 错误说明 #

Debug FORMAT should be specified at most once 是 Elasticsearch SQL 解析阶段抛出的 ParsingException。当在一条 DEBUG 语句中多次声明 FORMAT 子句时,Elasticsearch 的 SQL 解析器会在语法校验阶段直接拒绝该请求,因为输出格式(FORMAT)本质上只能有一个确定值,重复声明会导致语义冲突。

该错误不会进入查询执行阶段,而是在 SQL 文本解析时即被拦截,因此不会触发任何数据查询或写入操作。

常见现象 #

  • 执行 SQL DEBUG 语句后立即返回 ParsingException,HTTP 状态码通常为 400
  • 错误发生在 SQL 解析阶段,而非查询执行阶段,因此不会在索引数据或集群状态上产生任何副作用。
  • 常见于调试工具、ORM 框架或自定义 SQL 构造逻辑中,自动追加了 FORMAT 子句,而用户侧又显式声明了一次,导致重复。
  • 如果通过程序化方式拼接 SQL(如字符串模板、动态参数注入),重复 FORMAT 的概率会显著升高。

典型报错与异常栈 #

ParsingException: Debug FORMAT should be specified at most once

在更完整的日志上下文中,可能看到类似如下堆栈信息:

org.elasticsearch.xpack.sql.parser.ParsingException: Debug FORMAT should be specified at most once
    at org.elasticsearch.xpack.sql.parser.SqlParser.fail(SqlParser.java:...)
    at org.elasticsearch.xpack.sql.parser.SqlParser.visitDebug(SqlParser.java:...)
    at org.elasticsearch.xpack.sql.parser.SqlParser.parse(SqlParser.java:...)

2. 原因分析 #

该错误的根因非常明确:Elasticsearch SQL 解析器对 DEBUG 语句中的 FORMAT 子句执行了数量校验。其底层逻辑类似如下代码:

if (ctx.FORMAT().size() > 1) {
    throw new ParsingException(source, "Debug FORMAT should be specified at most once");
}

具体触发场景包括:

  • 手动重复书写:用户在 SQL 文本中不小心写了两次 FORMAT,例如 DEBUG FORMAT JSON FORMAT JSON SELECT ...
  • 工具自动追加:某些 SQL 客户端、调试工具或中间件会在原始 SQL 后自动追加默认 FORMAT(如 FORMAT JSON),而用户原始 SQL 中已包含 FORMAT 子句。
  • 代码拼接逻辑缺陷:在应用代码中通过字符串拼接构造 SQL 时,未对 FORMAT 是否已存在做去重判断,导致最终 SQL 中出现重复。
  • 模板渲染问题:使用 SQL 模板引擎时,模板变量被渲染了两次,或父子模板各自携带了 FORMAT 声明。

3. 解决方案 #

排查步骤 #

  1. 获取最终发送的 SQL 文本:通过 Elasticsearch 审计日志、INFINI Gateway 请求日志或应用侧日志,确认实际发送到 Elasticsearch 的完整 SQL 字符串。
  2. 检索重复的 FORMAT 关键字:在 SQL 文本中搜索 FORMAT,确认是否出现多次。注意 FORMAT 不区分大小写,部分解析器也接受小写形式。
  3. 追溯 SQL 生成链路:检查应用代码中 SQL 的构造方式,确认是否有默认 FORMAT 后缀被自动追加。
  4. 检查调试工具配置:如果使用 SQL 调试工具、代理或中间件,查看其是否有自动注入 FORMAT 的行为。

修复方案 #

  • 删除多余的 FORMAT 子句,只保留一个。例如将:

    DEBUG FORMAT JSON FORMAT JSON SELECT * FROM my_index
    

    修正为:

    DEBUG FORMAT JSON SELECT * FROM my_index
    
  • 统一 SQL 生成逻辑:将 FORMAT 的指定收敛到单一位置(如一个配置项或一个方法),避免多处代码各自追加。

  • 在发送前做最终文本校验:对构造出的 SQL 做简单的字符串检查,确保 FORMAT 只出现一次。

  • 使用参数化方式指定格式:如果客户端支持,优先通过请求参数(如 ?format=json)而非 SQL 文本内嵌方式指定输出格式,减少文本拼接引入的重复风险。

4. 预防措施 #

  • 在 SQL 构造层增加唯一性校验:对 DEBUGEXPLAIN 等支持 FORMAT 的语句,在构造完成后做一次关键字去重检查。
  • 测试覆盖重复场景:在单元测试或集成测试中,显式覆盖"重复 FORMAT“的输入场景,确保构造逻辑不会生成非法 SQL。
  • 避免字符串拼接构建 SQL:尽量使用结构化方式构造 SQL 语句,或使用经过验证的 SQL 构建库,降低拼接错误的风险。
  • 调试工具配置审查:定期检查 SQL 调试工具、代理和中间件的配置,确认其自动追加行为是否符合预期,必要时关闭自动追加功能。

借助 INFINI 产品提升排障效率 #

  • INFINI Console 可查看发送到 Elasticsearch 的完整 SQL 请求内容,帮助快速确认最终 SQL 文本中是否存在重复 FORMAT
  • INFINI Gateway 可部署在应用与 Elasticsearch 之间,对 SQL 请求做观测、改写和限流,也可用于识别重复参数来自哪一层。

5. 小结 #

Debug FORMAT should be specified at most once 的根因是 DEBUG 语句中 FORMAT 子句被重复声明。该错误发生在 SQL 解析阶段,不会进入查询执行。排查时只需确认最终发送的 SQL 文本,删除多余的 FORMAT 声明即可修复。通过统一 SQL 生成逻辑、增加构造后校验和完善测试覆盖,可以有效预防此类问题再次发生。

相关错误 #

附:日志上下文 #

if (ctx.FORMAT().size() > 1) {
    throw new ParsingException(source, "Debug FORMAT should be specified at most once");
}