SQLsmith 已被证明是查找 PostgreSQL 服务器及其他产品不同领域错误的有效工具,包括安全错误,从执行器错误到类型和索引方法实现中的段错误。
然而,SQLsmith 生成的触发某些错误的随机查询通常非常大,并且包含大量对错误无关紧要的“噪音”。到目前为止,需要手动检查查询并进行繁琐的编辑,才能将示例简化为开发人员可以用来修复问题的最小复现器。
这个问题由 SQLreduce 解决。SQLreduce 以任意 SQL 查询作为输入,然后针对 PostgreSQL 服务器运行该查询。在每一步之后,都会应用各种简化步骤,并检查简化后的查询是否仍然会触发 PostgreSQL 的相同错误。最终结果是一个复杂度最小的 SQL 查询。
SQLreduce 在将来自 SQLsmith 原始错误报告 的查询简化为与手动简化查询匹配的查询方面非常有效。
更多关于 工作原理的详细信息,请参阅博客文章。
2018 年,SQLsmith 在运行 Git 版本 039eb6e92f 的 PostgreSQL 中发现了一个段错误。当时用于复现错误的查询是一个巨大的 40 行、2.2KB 的查询
select case when pg_catalog.lastval() < pg_catalog.pg_stat_get_bgwriter_maxwritten_clean() then case when pg_catalog.circle_sub_pt( cast(cast(null as circle) as circle), cast((select location from public.emp limit 1 offset 13) as point)) ~ cast(nullif(case when cast(null as box) &> (select boxcol from public.brintest limit 1 offset 2) then (select f1 from public.circle_tbl limit 1 offset 4) else (select f1 from public.circle_tbl limit 1 offset 4) end, case when (select pg_catalog.max(class) from public.f_star) ~~ ref_0.c then cast(null as circle) else cast(null as circle) end ) as circle) then ref_0.a else ref_0.a end else case when pg_catalog.circle_sub_pt( cast(cast(null as circle) as circle), cast((select location from public.emp limit 1 offset 13) as point)) ~ cast(nullif(case when cast(null as box) &> (select boxcol from public.brintest limit 1 offset 2) then (select f1 from public.circle_tbl limit 1 offset 4) else (select f1 from public.circle_tbl limit 1 offset 4) end, case when (select pg_catalog.max(class) from public.f_star) ~~ ref_0.c then cast(null as circle) else cast(null as circle) end ) as circle) then ref_0.a else ref_0.a end end as c0, case when (select intervalcol from public.brintest limit 1 offset 1) >= cast(null as "interval") then case when ((select pg_catalog.max(roomno) from public.room) !~~ ref_0.c) and (cast(null as xid) <> 100) then ref_0.b else ref_0.b end else case when ((select pg_catalog.max(roomno) from public.room) !~~ ref_0.c) and (cast(null as xid) <> 100) then ref_0.b else ref_0.b end end as c1, ref_0.a as c2, (select a from public.idxpart1 limit 1 offset 5) as c3, ref_0.b as c4, pg_catalog.stddev( cast((select pg_catalog.sum(float4col) from public.brintest) as float4)) over (partition by ref_0.a,ref_0.b,ref_0.c order by ref_0.b) as c5, cast(nullif(ref_0.b, ref_0.a) as int4) as c6, ref_0.b as c7, ref_0.c as c8 from public.mlparted3 as ref_0 where true;
SQLreduce 可以有效地将那个庞然大物简化为如下内容:
SELECT pg_catalog.stddev(NULL) OVER () AS c5 FROM public.mlparted3 AS ref_0
SQLreduce 是开源的,基于 MIT 许可证。源代码在 GitHub 上:https://github.com/credativ/sqlreduce
Debian/Ubuntu 的 sqlreduce 包已在 apt.postgresql.org 上发布。
SQLreduce 是 credativ GmbH 的一款开源产品。