SQLsmith (https://github.com/anse1/sqlsmith) 已被证明是一个在 PostgreSQL 服务器和其他产品中发现各类 bug 的有效工具,包括安全漏洞,涵盖从执行器 bug 到类型和索引方法实现中的段错误等问题。
然而,SQLsmith 生成的触发错误的随机查询通常非常庞大,包含大量与错误无关的噪声。此前,需要人工检查查询并进行繁琐的编辑,才能将示例简化为开发者可用于修复问题的最小复现用例。
SQLreduce (https://github.com/credativ/sqlreduce) 解决了这个问题。SQLreduce 接受任意 SQL 查询作为输入,然后针对 PostgreSQL 服务器执行该查询。它会应用各种简化步骤,在每一步之后检查简化后的查询是否仍然会触发 PostgreSQL 的相同错误。最终结果是一个复杂度最小的 SQL 查询。
SQLreduce 能有效地将 SQLsmith 原始错误报告 (https://github.com/anse1/sqlsmith/wiki#score-list) 中的查询简化为与手动简化结果相匹配的查询。
更多工作原理的详情请参阅博客文章 (https://www.credativ.de/en/blog/postgresql/sqlreduce-reduce-verbose-sql-queries-to-minimal-examples/)。
示例
2018 年,SQLsmith 发现了一个段错误 (https://www.postgresql.org/message-id/87woxi24uw.fsf@ansel.ydns.eu),该错误出现在运行 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
sqlreduce 的 Debian/Ubuntu 软件包通过 apt.postgresql.org (https://apt.postgresql.org) 分发。
SQLreduce 是 credativ GmbH (https://www.credativ.de/) 的开源产品。