【问题标题】:Malformed array literal when selecting into custom type within a postgresql function在 postgresql 函数中选择自定义类型时出现格式错误的数组文字
【发布时间】:2016-03-24 12:45:10
【问题描述】:

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

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

【问题讨论】:

  • 当使用像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; 这样的构造时,它可以工作,但会增加调用函数三次而不是一次的开销。我确信有一种方法可以返回获取的类型,而无需从部分重构它。
  • 您忘记提供您的 Postgres 版本。

标签: sql arrays postgresql plpgsql literals


【解决方案1】:

解决方法是分配分解值:

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 允许一次分配目标变量列表
底线是:使用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)

相关:

简单替代

简单案例的简单替代方案:直接赋值(不带INTO):

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

简单赋值只允许单个目标开始。
或者直接返回结果:

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

【讨论】:

  • 哇,非常感谢!你在这件事上清除了我的天空。我的包装器直接返回有点复杂,但SELECT * FROM 应该适合我。我只是没有意识到它试图将结果放入我的复杂类型的第一列。
猜你喜欢
  • 2015-06-05
  • 1970-01-01
  • 1970-01-01
  • 2021-03-12
  • 1970-01-01
  • 2017-04-13
  • 1970-01-01
  • 1970-01-01
  • 2014-04-28
相关资源
最近更新 更多