在postgresql函数中选择自定义类型时,数组文字格式不正确



我有一个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);

最新更新