SQL MERGE 补丁状态

来自 PostgreSQL 维基
跳转到导航跳转到搜索

先前历史记录

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 子句向数据库写入数据