在postgresql函数中选择自定义类型时格式错误的数组文字

时间:2016-03-24 12:45:11

标签: sql arrays postgresql plpgsql literals

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

我没有将类型转换为字符串或其他类型,因此我无法弄清楚为什么返回值被认为具有格式错误的数组。 有线索吗?

1 个答案:

答案 0 :(得分:0)

解决方案是分配分解值:

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 第一列

Quoting the manual:

  

如果使用行或变量列表作为目标,则查询结果   列必须与目标的结构完全匹配   和数据类型

Postgres尝试将您的复合类型(或者更确切地说是其文本表示)放入bigint[],即复合类型routerec的第一列。您引用的错误消息是结果。

Explanation in the manual:

  

如果表达式的结果数据类型与变量的数据不匹配   类型,该值将被强制转换,就像通过赋值转换一样(请参阅   Section 10.4)。如果对于该对数据没有已知的赋值转换   涉及的类型,PL / pgSQL解释器将尝试转换   结果值是文本,即通过应用结果类型的输出   函数后跟变量类型的输入函数。注意   这可能导致输入函数生成运行时错误,   如果输入函数不接受结果值的字符串形式。

这也可能是第一次令人困惑和愚弄。由于INTO允许同时分配 目标变量列表 ,因此区分似乎是必要的。
底线是:在使用SELECT * FROM ...分配行/记录/复合类型时,使用INTO分解行类型。否则,它将作为一个整体分配给第一个目标列。

避免 这些效率低下的形式:
像你评论的那样:

<击> 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)

相关:

简单的替代

简单案例的简单替代方案:直接分配(不含INTO):

  routerec := net.get_route_part_dist(beg_, end_, dist);
  RETURN routerec;

简单分配只允许单个目标开始。
或直接返回结果:

  RETURN net.get_route_part_dist(beg_, end_, dist);