我有一个postgresql自定义类型,包含数组
CREATE TYPE route_part (
nodea bigint[],
edgea bigint[],
geom geometry
);
和一个函数,返回这种类型的
CREATE OR REPLACE FUNCTION net.get_route_part_dist(int8, int8, int4)
RETURNS route_part
AS
$BODY$
DECLARE routerec route_part;
BEGIN
SELECT INTO routerec
...
;
RETURN routerec;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
此函数按预期工作,并返回route_part
复合类型。我正试图在另一个"包装器"函数中使用它,它看起来像这样:
CREATE OR REPLACE FUNCTION net.get_route(beg_ int8, end_ int8, mida int8[], dist int4)
RETURNS route_part
AS
$BODY$
DECLARE routerec route_part;
BEGIN
SELECT INTO routerec net.get_route_part_dist(beg_, end_, dist);
RETURN routerec;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
我在选择查询中出错。
ERROR: malformed array literal: "(
{303513543,2289605239,...,306687989}","
{2585314,264212,...,1088633}",
0102000020110F000029000000AE47E11A81754F41C3F5280C07F25C)"
DETAIL: Array value must start with "{" or dimension information.
我不将类型强制转换为字符串或其他类型,所以我不明白为什么返回的值被认为具有格式错误的数组。有线索吗?
解决方案是分配分解值:
CREATE OR REPLACE FUNCTION net.get_route(beg_ int8, end_ int8, mida int8[], dist int4)
RETURNS route_part AS
$func$
DECLARE
routerec route_part;
BEGIN
SELECT INTO routerec * FROM net.get_route_part_dist(beg_, end_, dist);
RETURN routerec;
END
$func$ LANGUAGE plpgsql;
由于routerec
是行类型(复合类型),因此SELECT
列表的列必须与行类型的列匹配。您的表单将尝试将net.get_route_part_dist()
返回的值(作为一个整体)放入routerec
的第一列中。
引用手册:
如果一行或变量列表用作目标,则查询的结果列的数目必须与目标的结构完全匹配和数据类型
Postgres试图将您的复合类型(或者更确切地说,它的文本表示)放入bigint[]
,即复合类型routerec
的第一列。您引用的错误消息就是结果。
手册说明:
如果表达式的结果数据类型与变量的数据不匹配类型,则该值将被强制转换为赋值强制转换(请参阅第10.4节)。如果不知道数据对的赋值类型类型,PL/pgSQL解释器将尝试转换文本形式的result值,即通过应用结果类型的输出函数,然后是变量类型的输入函数。请注意这可能导致由输入函数生成的运行时错误,如果结果值的字符串形式对于输入函数来说是不可接受的。
这可能会让人困惑,第一次也会被愚弄。这种区别似乎是必要的,因为INTO
允许一次分配目标变量列表
底线是:当用INTO
分配给行/记录/复合类型时,用SELECT * FROM ...
分解行类型。否则,它将作为一个整体分配给第一个目标列。
避免这些低效形式:
就像你评论的:
SELECT INTO routerec (net.get_route_part_dist(beg_, end_, dist)).nodea
, (net.get_route_part_dist(beg_, end_, dist)).edgea
, (net.get_route_part_dist(beg_, end_, dist)).geom;
或者,不那么冗长,但同样效率低下:
SELECT INTO routerec (net.get_route_part_dist(beg_, end_, dist)).*;
每个人都会多次评估函数,而不是:SELECT INTO routerec * FROM net.get_route_part_dist(beg_, end_, dist)
相关:
- 在plpgsql中的FOR循环中使用自定义返回类型
- 将复合类型的数组传递到存储过程
简单的替代方案
您的简单案例的简单替代方案:直接分配(无INTO
):
routerec := net.get_route_part_dist(beg_, end_, dist);
RETURN routerec;
简单赋值只允许单个目标从开始
或者直接返回结果:
RETURN net.get_route_part_dist(beg_, end_, dist);