Postgres Cast复合类型

时间:2017-11-02 14:05:50

标签: postgresql types casting composite

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

我已经将复合类型“vector”声明为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;

函数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$$
;

该函数在null上返回null,或者两种输入格式的矢量类型都是json-string,如'{“x”:0,“y”:0,“z”:0}'或类似的构造函数表达式'(0,0,0)'

问题:

对于 json-like inputs ,函数返回错误

invalid input syntax for type double precision: "(0,0,0)"

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

为什么不能将已经输入 vector 的已转换值分配到向量中?不明白!

1 个答案:

答案 0 :(得分:2)

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

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

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

 my_record 
-----------
 (1,2,3)
(1 row) 

如果你想使用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)

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

  

保守地将标记视为隐含是明智的。过多的隐式转换路径可能会导致PostgreSQL选择令人惊讶的命令解释,或者根本无法解析命令,因为有多种可能的解释。一个好的经验法则是,只允许在同一类型类别中的类型之间保留信息的隐式调用。例如,从int2到int4的转换可以合理地隐式,但是从float8到int4的转换应该只是赋值。交叉类型转换(例如text to int4)最好只显示为。