Postgres 铸造复合类型

Cyr*_*amm 1 postgresql types casting composite

在 Postgres 9.6 中使用函数转换为复合类型时,我遇到了一个奇怪的行为。

我已将复合类型“向量”声明为 x,y,z - 每个双精度以及如下强制转换:

create type vector as(
 x double precision,
 y double precision,
 z double precision
);

create cast (text as vector)
 with function vector_from_text(text) as implicit;
Run Code Online (Sandbox Code Playgroud)

函数 vector_from_text 看起来像这样:

create or replace function vector_from_text(text, out result vector) strict immutable language plpgsql as $$
declare
    d double precision[];
begin
    result := null;
    if ($1 is null) then
        return;
    end if;

    begin
        with c1 as (
               select $1::jsonb src
        )
        select row((src->>'x')::double precision, (src->>'y')::double precision, (src->>'z')::double precision)::vector
        **into result** -- see below
        from c1;
    exception
        when others then
            d := translate($1, '()', '{}')::double precision[];
            result := row(d[1], d[2], d[3])::vector;
    end;
end$$
;
Run Code Online (Sandbox Code Playgroud)

该函数在 null 上返回 null,或者两种输入格式的向量类型,或者像 '{"x":0, "y":0, "z":0}' 这样的 json 字符串或者像 '(0 ,0,0)'。

问题:

对于类似 json 的输入,函数返回错误

invalid input syntax for type double precision: "(0,0,0)"
Run Code Online (Sandbox Code Playgroud)

一旦 select 语句包含进入 result的行。如果我删除此行或将输出变量从结果更改为文本类型,则函数会按预期工作。

为什么不能将已经到类型的向量转换值分配到向量中?不要明白!

kli*_*lin 5

您不需要(实际上也不应该)从文本创建演员表。创建复合类型时,您可以将文本转换为该类型,而无需任何其他步骤:

create type my_record as(
    x int,
    y int,
    z int
);

select '(1,2,3)'::my_record;

 my_record 
-----------
 (1,2,3)
(1 row) 
Run Code Online (Sandbox Code Playgroud)

如果要使用 jsonb,请创建从 jsonb 到类型的强制转换:

create or replace function my_record_from_jsonb(obj jsonb, out result my_record) 
strict immutable language plpgsql as $$
begin
    select (obj->>'x')::int, (obj->>'y')::int, (obj->>'z')::int
    into result;
end
$$;

create cast (jsonb as my_record)
    with function my_record_from_jsonb(jsonb);

select '{"x":1, "y":2, "z":3}'::jsonb::my_record;

 my_record 
-----------
 (1,2,3)
(1 row)
Run Code Online (Sandbox Code Playgroud)

不要试图以两种不同的方式解释文本文字。如果要使用 jsonb 语法,请使用 jsonb 类型。从文本创建自定义隐式转换特别不合理。阅读文档

明智的做法是将强制转换标记为隐式。过多的隐式转换路径会导致 PostgreSQL 选择对命令的令人惊讶的解释,或者根本无法解析命令,因为有多种可能的解释。一个好的经验法则是使强制转换仅对同一通用类型类别中的类型之间的信息保留转换可隐式调用。例如,从 int2 到 int4 的转换可以合理地是隐式的,但是从 float8 到 int4 的转换可能应该是仅赋值的。跨类型类别的强制转换,例如 text 到 int4,最好只进行显式转换。