将复合类型的数组传递给存储过程

时间:2022-08-19 10:12:46

I'm probably doing something wrong with forming the literal. Suppose I have a simple stored procedure like this:

我可能在形成文字方面做错了。假设我有一个这样的简单存储过程:

CREATE OR REPLACE FUNCTION do_something(input_array composite_type[])
  RETURNS SETOF text AS
$BODY$
DECLARE
    temp_var composite_type;
BEGIN

    FOR temp_var IN SELECT unnest(input_array) LOOP
        return next temp_var.message;
    END LOOP;

END
$BODY$
  LANGUAGE plpgsql;

The composite_type is defined as:

composite_type定义为:

CREATE TYPE composite_type AS
   (message text,
    amount numeric(16,2));

Performing a query like this:

执行这样的查询:

SELECT * FROM do_something('{"(test,11)","(test2,22)"}')

Produces this result set:

生成此结果集:

(test,11.00)
(test2,22.00)

Instead of:

test
test2

Is it something wrong with my literal or should I access the message field in a different way? Thanks for any suggestions.

我的文字是否有问题,还是应该以不同的方式访问消息字段?谢谢你的任何建议。

2 个解决方案

#1


2  

How you specify your input appears fine, as the same behaviour is observed with row- and array-constructor syntax:

如何指定输入似乎很好,因为使用行和数组构造函数语法观察到相同的行为:

SELECT * FROM do_something( ARRAY[ ROW('test',11), ROW('test2',22) ]::composite_type[] );

And:

SELECT ARRAY[ ROW('test',11), ROW('test2',22) ]::composite_type[];

produces:

 '{"(test,11.00)","(test2,22.00)"}'

If you add a:

如果你添加一个:

 RAISE NOTICE '!%!',temp_var;

inside the loop the output is:

在循环内部输出是:

NOTICE:  !("(test,11.00)",)!
NOTICE:  !("(test2,22.00)",)!

showing that you're actually getting a tuple with "message" as the tuple text you expected and a null "amount".

显示您实际上正在使用“message”作为您期望的元组文本和null“amount”获取元组。

So. Why?

It's a bit of a subtle one. You're using:

这有点微妙。你正在使用:

SELECT unnest(input_array)

which seems to do what you want, right:

这似乎做你想要的,对:

regress=>     SELECT unnest( ARRAY[ ROW('test',11), ROW('test2',22) ]::composite_type[] );
    unnest     
---------------
 (test,11.00)
 (test2,22.00)
(2 rows)

... but actually, it's returning a single column of type composite_type. PL/PgSQL composite type assignment expects one column per type column instead. So the single col is being shoved into 'message' and there is no second col.

...但实际上,它返回的是一个类型为composite_type的列。 PL / PgSQL复合类型赋值需要每个类型列一列。因此,单个col被推入'消息'并且没有第二个col。

Instead, write:

SELECT * FROM unnest(input_array)

to unpack the composite for assignment. Then it works as expected:

解压缩组合以进行分配。然后它按预期工作:

regress=> SELECT * FROM do_something( ARRAY[ ROW('test',11), ROW('test2',22) ]::composite_type[] );
 do_something 
--------------
 test
 test2
(2 rows)

If the first field of composite_type were of a non-text type, you'd get an error that was rather more informative about this.

如果composite_type的第一个字段是非文本类型,那么您将收到一个相关信息更多的错误。

#2


1  

Craig explained well a reason for this behave - Assignment variable=value inside FOR statement expects zero nesting. So you should to do:

Craig很好地解释了这种行为的原因 - 在FOR语句中赋值变量=值期望零嵌套。所以你应该这样做:

CREATE OR REPLACE FUNCTION do_something(input_array composite_type[])
RETURNS SETOF text AS $BODY$
DECLARE
    temp_var record;
BEGIN
     -- unnesting
    FOR temp_var IN SELECT (unnest(input_array)).*
    LOOP
        RETURN NEXT temp_var.message;
    END LOOP;
    RETURN;
END
$BODY$ LANGUAGE plpgsql;

or -- preferable - newer use SetReturnedFunction inside "column list"

或者 - 更好 - 更新在“列列表”中使用SetReturnedFunction

CREATE OR REPLACE FUNCTION do_something(input_array composite_type[])
RETURNS SETOF text AS $BODY$
DECLARE
    temp_var record;
BEGIN
     -- SELECT FROM
    FOR temp_var IN SELECT * FROM unnest(input_array) 
    LOOP
        RETURN NEXT temp_var.message;
    END LOOP;
    RETURN;
END
$BODY$ LANGUAGE plpgsql;

#1


2  

How you specify your input appears fine, as the same behaviour is observed with row- and array-constructor syntax:

如何指定输入似乎很好,因为使用行和数组构造函数语法观察到相同的行为:

SELECT * FROM do_something( ARRAY[ ROW('test',11), ROW('test2',22) ]::composite_type[] );

And:

SELECT ARRAY[ ROW('test',11), ROW('test2',22) ]::composite_type[];

produces:

 '{"(test,11.00)","(test2,22.00)"}'

If you add a:

如果你添加一个:

 RAISE NOTICE '!%!',temp_var;

inside the loop the output is:

在循环内部输出是:

NOTICE:  !("(test,11.00)",)!
NOTICE:  !("(test2,22.00)",)!

showing that you're actually getting a tuple with "message" as the tuple text you expected and a null "amount".

显示您实际上正在使用“message”作为您期望的元组文本和null“amount”获取元组。

So. Why?

It's a bit of a subtle one. You're using:

这有点微妙。你正在使用:

SELECT unnest(input_array)

which seems to do what you want, right:

这似乎做你想要的,对:

regress=>     SELECT unnest( ARRAY[ ROW('test',11), ROW('test2',22) ]::composite_type[] );
    unnest     
---------------
 (test,11.00)
 (test2,22.00)
(2 rows)

... but actually, it's returning a single column of type composite_type. PL/PgSQL composite type assignment expects one column per type column instead. So the single col is being shoved into 'message' and there is no second col.

...但实际上,它返回的是一个类型为composite_type的列。 PL / PgSQL复合类型赋值需要每个类型列一列。因此,单个col被推入'消息'并且没有第二个col。

Instead, write:

SELECT * FROM unnest(input_array)

to unpack the composite for assignment. Then it works as expected:

解压缩组合以进行分配。然后它按预期工作:

regress=> SELECT * FROM do_something( ARRAY[ ROW('test',11), ROW('test2',22) ]::composite_type[] );
 do_something 
--------------
 test
 test2
(2 rows)

If the first field of composite_type were of a non-text type, you'd get an error that was rather more informative about this.

如果composite_type的第一个字段是非文本类型,那么您将收到一个相关信息更多的错误。

#2


1  

Craig explained well a reason for this behave - Assignment variable=value inside FOR statement expects zero nesting. So you should to do:

Craig很好地解释了这种行为的原因 - 在FOR语句中赋值变量=值期望零嵌套。所以你应该这样做:

CREATE OR REPLACE FUNCTION do_something(input_array composite_type[])
RETURNS SETOF text AS $BODY$
DECLARE
    temp_var record;
BEGIN
     -- unnesting
    FOR temp_var IN SELECT (unnest(input_array)).*
    LOOP
        RETURN NEXT temp_var.message;
    END LOOP;
    RETURN;
END
$BODY$ LANGUAGE plpgsql;

or -- preferable - newer use SetReturnedFunction inside "column list"

或者 - 更好 - 更新在“列列表”中使用SetReturnedFunction

CREATE OR REPLACE FUNCTION do_something(input_array composite_type[])
RETURNS SETOF text AS $BODY$
DECLARE
    temp_var record;
BEGIN
     -- SELECT FROM
    FOR temp_var IN SELECT * FROM unnest(input_array) 
    LOOP
        RETURN NEXT temp_var.message;
    END LOOP;
    RETURN;
END
$BODY$ LANGUAGE plpgsql;