跳到主要内容

Union Type

UNION type(不要与 SQL UNION operator 混淆)是一种嵌套类型,可在多个“备选”值中持有其一,类似 C 语言中的 union。主要区别在于,这里的 UNION 类型是 tagged unions,因此总会携带一个用于区分当前持有哪个备选值的“tag”,即使内部值本身为 null 也是如此。因此,UNION 类型更接近 C++17 的 std::variant、Rust 的 Enum,或多数函数式语言中的“sum type”。

UNION 类型必须至少包含一个成员。它们可以包含多个相同类型的成员,但 tag 名称必须唯一。UNION 类型最多可有 256 个成员。

在实现层面,UNION 类型构建在 STRUCT 类型之上,并将“tag”作为第一个条目存储。

UNION 值可通过 union_value(tag := expr) 函数创建,也可通过从成员类型进行转换创建。

示例

创建一个包含 UNION 列的表:

CREATE TABLE tbl1 (u UNION(num INTEGER, str VARCHAR));
INSERT INTO tbl1 VALUES (1), ('two'), (union_value(str := 'three'));

任意类型都可以隐式转换为包含该类型的 UNION。任意 UNION 也可隐式转换为另一个 UNION,前提是源 UNION 的成员是目标 UNION 成员的子集(且该转换无歧义)。

UNION 在转换为 VARCHAR 时会使用其成员类型对应的 VARCHAR 转换函数:

SELECT u FROM tbl1;
u
1
two
three

选取所有 str 成员:

SELECT union_extract(u, 'str') AS str
FROM tbl1;
str
NULL
two
three

另外,你也可以像 STRUCTs 一样使用“点语法”。

SELECT u.str
FROM tbl1;
str
NULL
two
three

UNION 当前激活的 tag 以 ENUM 形式选出。

SELECT union_tag(u) AS t
FROM tbl1;
t
num
str
str

Union 转换

与其他嵌套类型相比,UNION 允许一组隐式转换,以便在将其成员作为“子类型”使用时更加自然、低侵入。 不过,这些转换遵循两个设计原则:避免歧义,避免导致信息丢失的转换。这使 UNION 不会变得完全“透明”,同时仍允许 UNION 类型与其成员之间形成“超类型”关系。

因此,通常情况下 UNION 类型不能隐式转换为其任一成员类型,因为不匹配目标类型的其他成员信息会“丢失”。如果你希望将 UNION 强制为其某个成员,应显式使用 union_extract 函数。

唯一例外是将 UNION 转换为 VARCHAR 的情况,此时各成员都会使用其对应的 VARCHAR 转换。由于所有内容都可转换为 VARCHAR,在某种意义上这是“安全”的。

转换到 Union

如果某个类型可以隐式转换为某个 UNION 成员类型,则它总可以隐式转换为该 UNION

  • 若存在多个候选目标,内置的隐式转换优先级规则会决定目标类型。例如,FLOATUNION(i INTEGER, v VARCHAR) 总会优先将 FLOAT 转到 INTEGER 成员,而不是 VARCHAR
  • 若转换仍有歧义(即存在多个具有相同隐式转换优先级的候选),会报错。这通常发生在 UNION 含有多个相同类型成员时,例如 FLOATUNION(i INTEGER, num INTEGER) 始终有歧义。

如果我们想创建一个包含多个相同类型成员的 UNION,该如何消除歧义?可以使用 union_value 函数,并通过关键字参数指定 tag。例如,union_value(num := 2::INTEGER) 会创建一个只有 INTEGER 成员且 tag 为 numUNION。随后可在显式(或隐式,见下文)UNIONUNION 的转换中用于消歧,例如 CAST(union_value(b := 2) AS UNION(a INTEGER, b INTEGER))

Union 之间的转换

如果源类型是目标类型的“子集”,UNION 类型之间就可以互相转换。换言之,源 UNION 中的所有 tag 都必须在目标 UNION 中存在,且匹配 tag 的类型在源与目标之间必须可隐式转换。本质上,这意味着 UNION 类型相对于其成员是协变的。

是否可行SourceTarget说明
UNION(a A, b B)UNION(a A, b B, c C)
UNION(a A, b B)UNION(a A, b C)B 可隐式转换为 C
UNION(a A, b B, c C)UNION(a A, b B)
UNION(a A, b B)UNION(a A, b C)B 不能隐式转换为 C
UNION(A, B, D)UNION(A, B, C)

比较与排序

由于 UNION 类型在内部基于 STRUCT 类型实现,因此它们可用于所有比较运算符,也可用于 WHEREHAVING 子句,并遵循与 STRUCTs 相同的语义。“tag” 始终存储为 struct 的第一个条目,这保证了 UNION 类型会先按 “tag” 进行比较和排序。

函数

参见 Union Functions