SQL MERGE 补丁状态
先前历史记录
概述
针对 SQL 标准 MERGE 语句的补丁已提交到 PostgreSQL 核心 - 作者是来自 2ndQuadrant 的 Simon Riggs 和 Pavan Deolasee。vmWare 的 Peter Geoghegan 提供了大量审查意见。
当前补丁为 v14 [1]
下一个补丁 v15 预计于 2018 年 2 月中旬发布
待办事项
错误
- 在 MERGE UPDATE 的 SET 子句中使用子查询时,ERROR 的措辞不正确(由 PeterG 报告)
无法识别的节点类型 114 [2]
- 在 MERGE UPDATE 的 SET 子句中使用整行引用时,ERROR 的措辞不正确(由 PeterG 报告)
ERROR: XX000: 子计划目标列表中找不到变量 [3]
Peter Geoghegan 不同意将这两个错误归类为“措辞不正确”问题的当前描述.
- EXPLAIN ANALYZE 报告插入的行数不正确,没有考虑到 NOT MATCHED WHEN AND 条件跳过的行数(计算错误,报告处理的行数 - 更新 - 删除)(由 PeterG 报告)
- sqlsmith 报告了 3 个断言失败(由 Andreas Seltenreich 报告)
-- TRAP: FailedAssertion("!(!((((const Node*)(node))->type) == T_SubLink))", File: "clauses.c", Line: 440) MERGE INTO public.brin_test as target_0 USING pg_catalog.pg_database as ref_0
left join pg_catalog.pg_user_mapping as sample_0 tablesample system (2.3) on (pg_catalog.mul_d_interval( cast(pg_catalog.pi() as float8), cast(case when sample_0.umoptions is not NULL then (select write_lag from pg_catalog.pg_stat_replication limit 1 offset 2) else (select write_lag from pg_catalog.pg_stat_replication limit 1 offset 2) end as "interval")) = (select intervalcol from public.brintest limit 1 offset 2) )
ON target_0.a = ref_0.encoding WHEN NOT MATCHED AND cast(null as "timestamp") < cast(null as date)
THEN INSERT VALUES ( 62, 6)
WHEN NOT MATCHED
AND false THEN DO NOTHING;
-- TRAP: FailedAssertion("!(!((((const Node*)(node))->type) == T_SubLink))", File: "prepunion.c", Line: 2246) MERGE INTO public.onek2 as target_0 USING public.prt1 as ref_0
inner join public.tenk1 as ref_1 on ((select t from public.btree_tall_tbl limit 1 offset 63) is not NULL)
ON target_0.stringu1 = ref_1.stringu1 WHEN NOT MATCHED THEN DO NOTHING;
-- TRAP: FailedAssertion("!(!((((const Node*)(node))->type) == T_Query))", File: "var.c", Line: 248) MERGE INTO public.clstr_tst_inh as target_0 USING pg_catalog.pg_statio_sys_tables as ref_0
left join public.rule_and_refint_t3 as ref_1 on (((ref_0.heap_blks_hit is not NULL) or (((select f1 from public.path_tbl limit 1 offset 5) >= (select thepath from public.shighway limit 1 offset 33) ) or (cast(null as tsvector) <> cast(null as tsvector)))) and (ref_0.toast_blks_read is not NULL))
ON target_0.d = ref_1.data WHEN NOT MATCHED
AND cast(null as int2) = pg_catalog.lastval() THEN DO NOTHING;
次要
- 在文档中提到 DO NOTHING 是对 SQL 标准的扩展(由 Simon 报告,修复已提交,将在 v15 中发布)
主要
- 围绕并发性的讨论。如果并发更新/删除在 EvalPlanQual 之后使行不可用,我们应该抛出 ERROR 吗?
关于如何继续进行的先前共识的未解决变更请求 [4]
请根据当前补丁对并发性进行所有请求的更改。即明确说明需要更改哪个测试?用户可见行为的改变是什么?这使我们能够对具体问题发表评论,并将该测试作为开发目标。
应 Stephen Frost 在 Dev 会议上的要求,在并发性章节中添加了说明性文本。在此总结
SQl 标准指出“SQL 实现不允许进行不重要的独立更改的程度是实现定义的”,SQL-2016 基础,第 1178 页,4)“不重要”未定义。以下并发规则包含在 v14 中。
如果 MERGE 尝试执行 UPDATE 或 DELETE,并且该行被并发更新,则 MERGE 将与 UPDATE 或 DELETE 命令的行为相同,并使用标准 EvalPlanQual 对该行的最新版本执行其操作。MERGE 操作可以是条件性的,因此条件必须在最新行上重新评估。
如果 MERGE 尝试执行 UPDATE 或 DELETE,并且该行被并发删除,我们目前会抛出 ERROR。我们现在同意在这种情况下尝试 INSERT 是可能的且可取的,但我们还没有想出如何实现它。(*)
如果 MERGE 尝试执行 INSERT,并且存在唯一索引,并且新行是重复的,则会引发唯一性违规。MERGE 不会尝试通过尝试 UPDATE 来避免 ERROR。可以使用推测性插入来避免错误,但有些人反对这样做。(*)
由于 MERGE 语句的条件规则,INSERT ON CONFLICT UPDATE 可用的始终插入或更新的完全保证并不总是可能的,因此如果 INSERT 失败或 INSERT 如果 UPDATE 失败,我们只能尝试一次 UPDATE。这是为了确保命令的一致行为。
(*) 据了解,其他 DBMS 在这些情况下会抛出错误(需要事实核查)。一些 DBMS 通过将此类错误路由到在首次错误时创建的错误表来处理这种情况。
计划用于 PG11
- 分区(WIP 预计将在 v15 中发布) - 对于各种用例,可能需要多种不同的计划
- 制表符完成(WIP 预计将在 v15 中发布) - 可能无法拥有完整的语法,太复杂了
- RLS - 由于风险,要求不支持 PG11 - 会在具有 POLICY 的表上抛出 ERROR(已阻止)
- ruleutils.c 中的 get_query_def()
不计划用于 PG11
- MERGE UPDATE 目标列表中的整行变量
- WHEN AND 条件中的子查询(受 Oracle 和 SQLServer 支持,但是非标准的)
- INSERT OVERRIDING
- 语句触发器的过渡表 - 会在针对定义了过渡表的表使用时抛出 ERROR(已阻止)
- RETURNING - 不是标准的一部分 - 会抛出语法 ERROR(在 v15 中被阻止)
- CTE 支持 - 不是标准的一部分 - 会抛出语法 ERROR(在 v15 中被阻止)
自 v13 起关闭
v14 - 18/1/29
- 在 INSERT ON CONFLICT 和 MERGE 之间添加交叉引用文档
- 添加:新的直接自引用测试用例,没有发现错误
- 添加:与 INSERT .. ON CONFLICT 相同风格的 EXPLAIN ANALYZE
- 添加:允许在 WHEN AND 条件中使用 Oid 系统列
- 添加:根据 SQL 规范,防止 WHEN AND 子句向数据库写入数据