Postgres 铸造复合材料类型

Posted

技术标签:

【中文标题】Postgres 铸造复合材料类型【英文标题】:Postgres Cast Composite Types 【发布时间】:2017-11-02 14:05:49 【问题描述】:

在 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;

函数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 字符串,如 '"x":0, "y":0, "z":0' 或一个构造函数表达式,如'(0,0,0)'。

问题:

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

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

只要选择语句包含 into result 行。如果我删除此行或将输出变量从 result 更改为 text 类型,则函数将按预期工作。

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

【问题讨论】:

【参考方案1】:

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

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 的转换可能应该是仅赋值的。跨类型类别的强制转换,例如将文本转换为 int4,最好仅显式地进行。

【讨论】:

以上是关于Postgres 铸造复合材料类型的主要内容,如果未能解决你的问题,请参考以下文章

在 postgres 中使用 sqlalchemy 访问复合数据类型

Postgres 复合类型 drop cascade 仅删除依赖列

如何在 Postgres/Postgis 中转换类型(并使用 sequelizejs)

Postgres Plus Advanced Server (PPAS) 9.5 是不是支持将属性更改(删除/添加)复合对象类型?

Postgres:如何做复合键?

Postgres中唯一的部分复合主键