CREATE CAST — 定义一个新的转换
CREATE CAST (source_typeAStarget_type) WITH FUNCTIONfunction_name[ (argument_type[, ...]) ] [ AS ASSIGNMENT | AS IMPLICIT ] CREATE CAST (source_typeAStarget_type) WITHOUT FUNCTION [ AS ASSIGNMENT | AS IMPLICIT ] CREATE CAST (source_typeAStarget_type) WITH INOUT [ AS ASSIGNMENT | AS IMPLICIT ]
CREATE CAST 定义一个新的转换。转换指定了如何在两种数据类型之间执行转换。例如,
SELECT CAST(42 AS float8);
会通过调用先前指定的函数把整数常量 42 转换为 float8 类型,在这里该函数是 float8(int4)。(如果没有定义合适的转换,该转换将会失败。)
两种类型可以是二进制可强制转换的,这意味着可以“免费”执行该转换而无需调用任何函数。这要求对应的值使用相同的内部表示。例如,text 和 varchar 类型在两个方向上都是二进制可强制转换的。二进制可强制转换并不一定是对称的关系。例如,从 xml 到 text 的转换在当前的实现中可以免费完成,但反方向则需要一个至少执行语法检查的函数。(双向二进制可强制转换的两种类型也被称为二进制兼容的。)
你可以把一个转换定义为I/O 转换转换,方法是使用 WITH INOUT 语法。I/O 转换转换通过调用源数据类型的输出函数,并将得到的字符串传递给目标数据类型的输入函数来执行。在许多常见情况下,该特性避免了为转换单独编写转换函数的需要。I/O 转换转换的行为与常规的基于函数的转换相同;只是实现方式不同。
默认情况下,只有显式的转换请求才能调用一个转换,即显式的 CAST( 或 x AS typename)x::typename 结构。
如果转换被标记为 AS ASSIGNMENT,那么在给目标数据类型的列赋值时可以隐式调用它。例如,假设 foo.f1 是一个 text 类型的列,那么:
INSERT INTO foo (f1) VALUES (42);
如果从 integer 类型到 text 类型的转换被标记为 AS ASSIGNMENT,则该语句将被允许,否则不允许。(我们通常使用术语赋值转换来描述这种转换。)
如果转换被标记为 AS IMPLICIT,那么它可以在任何上下文中被隐式调用,无论是在赋值中还是在表达式内部。(我们通常使用术语隐式转换来描述这种转换。)例如,考虑这个查询:
SELECT 2 + 4.0;
解析器最初将这两个常量分别标记为 integer 和 numeric 类型。系统目录中没有 integer + numeric 操作符,但有 numeric + numeric 操作符。因此,如果存在从 integer 到 numeric 的转换且被标记为 AS IMPLICIT,该查询就会成功 — 事实上确实如此。解析器将应用该隐式转换并解析这个查询,就像它被写成
SELECT CAST ( 2 AS numeric ) + 4.0;
一样。
此外,目录中也提供了从 numeric 到 integer 的转换。如果该转换被标记为 AS IMPLICIT — 实际上并没有 — 那么解析器将面临在上面的解释与另一种解释之间做出选择,后者是把 numeric 常量转换为 integer 并应用 integer + integer 操作符。由于缺乏关于应优先选择哪种方案的知识,它会放弃并宣布该查询有歧义。两个转换中只有一个是隐式的,这正是我们教解析器优先将 numeric 与 integer 的混合表达式解析为 numeric 的方式;关于这一点并没有内置的知识。
将转换标记为隐式时应保持保守,这是明智的做法。过多的隐式转换路径可能导致 PostgreSQL 对命令选择令人意外的解释,或者由于存在多种可能的解释而完全无法解析命令。一个好的经验法则是,仅对同一大致类型类别中保持信息的转换才允许隐式调用。例如,从 int2 到 int4 的转换可以合理地设为隐式,但从 float8 到 int4 的转换最好仅限于赋值。跨类型类别的转换(例如从 text 到 int4)最好设为仅显式。
有时出于易用性或符合标准的原因,需要在一组类型之间提供多个隐式转换,从而产生无法像上面那样避免的歧义。解析器有一种基于类型类别和首选类型的后备启发式方法,可以在这种情况下帮助提供期望的行为。更多信息请参阅 CREATE TYPE。
要能够创建转换,你必须拥有源或目标数据类型,并且在另一种类型上拥有 USAGE 特权。要创建二进制可强制转换的转换,你必须是超级用户。(做出这一限制是因为错误的二进制可强制转换很容易导致服务器崩溃。)
source_type该转换的源数据类型的名称。
target_type该转换的目标数据类型的名称。
function_name[(argument_type [, ...])]用于执行该转换的函数。函数名可以带模式限定。如果不带,则将在模式搜索路径中查找该函数。函数的结果数据类型必须与转换的目标类型匹配。其参数将在下面讨论。如果没有指定参数列表,则函数名在其模式中必须是唯一的。
WITHOUT FUNCTION表示源类型到目标类型是二进制可强制转换的,因此执行该转换不需要函数。
WITH INOUT表示该转换是一个 I/O 转换转换,通过调用源数据类型的输出函数,并将得到的字符串传递给目标数据类型的输入函数来执行。
AS ASSIGNMENT表示该转换可以在赋值上下文中被隐式调用。
AS IMPLICIT表示该转换可以在任何上下文中被隐式调用。
转换实现函数可以有一到三个参数。第一个参数的类型必须与转换的源类型相同,或者可以二进制强制转换自源类型。第二个参数(如果存在)必须是 integer 类型;它接收与目标类型相关联的类型修饰符,如果没有则为 -1。第三个参数(如果存在)必须是 boolean 类型;如果该转换是显式转换,它接收 true,否则接收 false。(奇怪的是,SQL 标准在某些情况下要求显式转换和隐式转换具有不同的行为。对于必须实现这类转换的函数,需要提供这个参数。不建议你把自己的数据类型设计得让这个参数变得重要。)
转换函数的返回类型必须与转换的目标类型相同,或者可以二进制强制转换为该目标类型。
通常,转换的源和目标数据类型必须不同。但是,如果转换具有一个带多个参数的转换实现函数,则允许声明源类型和目标类型相同的转换。这用于在系统目录中表示特定于类型的长度强制函数。被指定的函数用于把该类型的一个值强制转换为第二个参数给出的类型修饰符值。
当转换具有不同的源和目标类型,并且其函数带多个参数时,它支持在单个步骤中完成从一种类型到另一种类型的转换以及长度强制。当没有这样的条目可用时,强制转换为使用类型修饰符的类型需要两个转换步骤:一个用于在数据类型之间转换,第二个用于应用类型修饰符。
目前,转换到域类型或从域类型转换出来没有任何效果。转换到域或从域转换出来会使用与其基础类型相关联的转换。
使用 DROP CAST 移除用户定义的转换。
请记住,如果希望能够双向转换类型,你需要显式地声明两个方向的转换。
通常不需要在用户定义类型与标准字符串类型(text、varchar 和 char(,以及被定义在字符串类别中的用户定义类型)之间创建转换。PostgreSQL 为此提供了自动的 I/O 转换转换。到字符串类型的自动转换被视为赋值转换,而从字符串类型来的自动转换则是仅显式的。你可以声明自己的转换来替换自动转换以覆盖这种行为,但通常这样做的唯一原因是希望该转换比标准的仅赋值或仅显式设置更容易被调用。另一种可能的原因是你希望该转换的行为与该类型的 I/O 函数不同;但这已经足够出人意料,你应该三思这是否是个好主意。(少数内建类型在转换方面确实有不同的行为,主要是因为 SQL 标准的要求。) n)
虽然并非必需,但建议你继续遵循以目标数据类型命名转换实现函数的这种旧惯例。许多用户习惯于使用函数风格的记法来转换数据类型,即 typename(x)。这种记法实际上就是调用转换实现函数;它并没有被特殊地当作转换处理。如果你的转换函数没有按照支持这种惯例的方式命名,就会让用户感到意外。由于 PostgreSQL 允许用不同的参数类型重载相同的函数名,因此让来自不同类型的多个转换函数都使用目标类型的名称没有任何困难。
实际上,前一段的描述是过于简化的:有两种情况会把函数调用结构当作转换请求处理,而没有将它匹配到实际的函数。如果函数调用 name(x) 不能精确匹配任何现有的函数,但 name 是一个数据类型的名称,并且 pg_cast 提供了从 x 的类型到该类型的二进制可强制转换,那么该调用将被解释为一个二进制可强制转换。做出这种例外是为了让二进制可强制转换可以使用函数式语法调用,即使它们没有任何函数。同样地,如果没有 pg_cast 条目,但该转换是转换到字符串类型或从字符串类型转换出来,那么该调用将被解释为一个 I/O 转换转换。这种例外允许使用函数式语法调用 I/O 转换转换。
这个例外还有一个例外:从组合类型到字符串类型的 I/O 转换转换不能使用函数式语法调用,而必须写成显式转换语法(CAST 或 :: 记法)。增加这个例外是因为在引入自动提供的 I/O 转换转换之后,人们发现在本意是函数或列引用时很容易意外调用这样的转换。
创建一个从 bigint 类型到 int4 类型的赋值转换,使用函数 int4(bigint):
CREATE CAST (bigint AS int4) WITH FUNCTION int4(bigint) AS ASSIGNMENT;
(这个转换已经在系统中预先定义。)
CREATE CAST 命令符合 SQL 标准,不同之处在于 SQL 没有对二进制可强制转换的类型或实现函数的额外参数做出规定。AS IMPLICIT 也是 PostgreSQL 的扩展。