可更新视图

来自 PostgreSQL Wiki
跳转到导航跳转到搜索

本项目旨在实现兼容 SQL92 的可更新视图。 目前,已有一个原型实现单关系视图,通过现有的规则重写系统,支持本地和级联检查选项。

建议的补丁已提交以包含在 PostgreSQL 8.2 版本中,但是,有一些严重的担忧,使得需要重新思考整个实现和规则重写系统。 附件中的讨论总结了一些最需要在最终实现中解决的重要问题。

创建此页面是为了展示开发过程,提交想法,并在设计新的核心功能时为开发人员提供支持。


可更新视图至少应该做什么?

绝对最小的目标是达到与 SQL-92 标准的兼容性。 它们描述如下

  • 只允许一个基本关系。 如果找到一个联接视图,我们将该视图视为不可更新。
  • 不允许分组、区分、联合或聚合
  • 视图的目标列表应仅包含“真实”列,这些列直接从底层视图/关系派生而来。 我们可以区分“可插入”、“可能可插入”和“不可插入”,如 SQL-99 中建议的那样,但是一旦我们获得函数、常量或服务器端变量,我们将整个目标列表视为不可插入。
  • 检查选项只能应用于可更新(可插入)视图。

到目前为止,我们应该能够执行以下操作

CREATE TABLE foo(id INTEGER PRIMARY KEY, 
                 name TEXT NOT NULL);


CREATE VIEW vfoo 
AS 
SELECT 
   id, 
   name 
FROM 
   foo 
WHERE
   id BETWEEN 1 AND 10000 
WITH CHECK OPTION;

这将创建一个表 foo 和一个可更新视图 vfoo,允许插入元组,元组的 ID 值介于 1 和 10000 之间。 其他值将被拒绝,并且无法更新值以包含违反此条件的 id 值。 如图所示,我们应该将视图限定符与视图创建时的 WITH CHECK OPTION 选项一起考虑为视图约束,并根据该约束验证插入或更新的任何数据。 我认为我们不需要关心删除操作,因为我们无法从视图中删除不在视图中的数据。

SQL 标准为 CHECK OPTION 定义了另外两个关键字

  • 级联检查选项
  • 本地检查选项
CREATE VIEW vfoo_name 
AS 
SELECT 
   id, name 
FROM 
   vfoo 
WHERE
  name LIKE 'bernd%'
WITH CASCADED CHECK OPTION;

CASCADED 强制检查所有底层视图定义定义的所有视图约束。 LOCAL 仅检查当前选定视图上定义的视图约束。 这使事情变得更加复杂,因为我们需要递归地检查视图更新链中是否有 CASCADEDLOCAL 检查选项。 上面的示例将根据视图 vfoo_name vfoo 定义的视图限定符(视图约束)检查插入和更新操作。 如果我们将 CASCADED 替换为 LOCAL,则仅根据选定视图 vfoo_name 的视图约束验证新元组。


当前实现

转换器

当前实现涵盖了 SQL-92 标准定义的所有要求,如上所述。 我们不支持联接视图和部分可更新列列表(如 SQL-99 中描述的可插入、可插入和不可插入视图)。

整个功能是在现有的规则重写系统之上实现的。 可更新视图功能的入口点是 src/backend/commands/view.c,函数 DefineView()。 这将创建必要的虚拟关系和内容,最后但并非最不重要的是通过 DefineViewRules() 创建可更新视图的规则。 这将级联到 CreateViewUpdateRules(),它是 src/backends/rewrite/viewUpdate.c 中用于自动创建所有可更新视图规则的入口点。

我们在那里定义了三个转换器,它们将给定的 SELECT 查询树转换为其相应的 DELETEUPDATEINSERT 查询树

  • transform_select_to_delete()
  • transform_select_to_update()
  • transform_select_to_insert()

将给定的查询树转换为特定备用操作并非易事,但在第一眼看上去并不难。 PostgreSQL 的 Query 结构在那里定义了一些重要的字段,我们需要触碰它们

  • targetList
  • joinTree(包含 FROMWHERE 表达式/子句)
  • rtable

大多数工作是重写 varno 和 resno,并调整目标条目的顺序,因为视图可能具有不同的列“顺序”。 请注意,在 SQL 中没有“排序列”的定义,但所有列都以特定顺序存储在内部,因此我们必须注意这一点。

在进入任何转换器之前,我们调用 checkTree() 函数来检查给定的 SQL 查询树,以匹配 SQL-92 要求。 如果发现给定的查询树违反了上面描述的规则,我们将拒绝任何尝试将其转换为相应的更新操作。

这些操作的结果是三个规则,它们提供了可更新视图的必需 DML 功能(根据 SQL-92),命名如下

  • _INSERT:插入规则
  • _UPDATE:更新规则
  • _DELETE:删除规则

当使用 REPLACE 关键字修改现有视图时,我们需要注意替换这些规则。

现有实现的问题

CHECK OPTION 和多表达式评估

建议的 CHECK OPTION 实现存在一些问题,主要是由于易失函数在规则中产生不正确的结果,因为表达式在规则重写系统 [2] 中被双重评估。

我们需要重新考虑现有实现,并以允许以安全方式评估任何视图条件的方式重新实现 CHECK OPTION。 一个想法是将所有 CHECK OPTION 推入表约束 [4]。 然后,重写器负责收集所有必要的约束,并将它们应用于当前查询树。 但是,为了可靠地实现这一点,我们需要一种计划失效机制来调整缓存的查询计划,并向它们教授在那里收集的新约束。 Tom Lane 计划在 8.3 中实现此功能,所以让我们拭目以待。 另一个问题是,当我们想在检查选项中覆盖子查询时,就会出现。 SQL 标准似乎允许这样做,其他数据库也相应地做到了。 我们需要检查我们的约束代码是否能够处理此类表达式。

另一个想法是使用类似于 RI 触发器的东西(与 PostgreSQL 中实现外键检查的方式非常相似)。 我不知道 SQL 标准是否允许在 CHECK OPTION 中使用子查询,但如果是真的,这将很难在检查约束中实现。 我没有深入研究这个想法,但我认为值得考虑。

规则系统中的缺陷

事务可见性逻辑在规则上强制执行,就像在任何其他 SQL 命令上一样。 对于多操作规则,我们需要考虑在每个单独操作之间有一个 CommandCounterIncrement(),它迫使后续操作“看到”任何先前操作的修改。 当我们准备更新联接视图及其联接键(例如在 UPDATE 命令中)时,这一点变得很重要。

相关链接