问题
I've defined a custom type:
create type pg_temp.MYTYPE as (f1 int, f2 text);
Then, inside a function or a block, I've declared an array of such type:
declare ax MYTYPE[];
I can access an element through the familiar syntax ax[1].f1
, but just for reading it.
When I use the same syntax for setting the field, I get a syntax error.
create type pg_temp.MYTYPE as (f1 int, f2 text);
do $$
declare x MYTYPE;
declare ax MYTYPE[];
declare f int;
begin
x.f1 = 10;
x.f2 = 'hello';
--assigning an element: OK
ax[1] = x;
--reading an element's field: OK
f = ax[1].f1;
--writing an elememt's field: SYNTAX ERROR:
ax[1].f1 = f;
end; $$
The error is the following:
psql:test.sql:28: ERROR: syntax error at or near "."
LINE 20: ax[1].f1 = f;
^
I've tried also the syntax (ax[1]).f1
with the same result.
Which is the correct syntax to do this?
Postgres server version: 9.2.2
回答1:
PLpgSQL is sometimes very simple, maybe too simple. The left part of assignment statement is a example - on the left can be variable, record field, or array field only. Any complex left part expression are not supported.
You need a auxiliary variable of array element type.
DECLARE aux MTYPE;
aux := ax[1];
aux.f := 100000;
ax[1] := aux;
回答2:
That seems particularly inconsequent of plpgsql since SQL itself can very well update a field of a composite type inside an array.
Demo:
CREATE TEMP TABLE mytype (f1 int, f2 text);
CREATE TEMP TABLE mycomp (c mytype);
INSERT INTO mycomp VALUES ('(10,hello)');
UPDATE mycomp
SET c.f1 = 12 -- note: without parentheses
WHERE (c).f1 = 10; -- note: with parentheses
TABLE mycomp;
c ------------ (12,hello)
CREATE TEMP TABLE mycomparr (ca mytype[]);
INSERT INTO mycomparr VALUES ('{"(10,hello)"}');
UPDATE mycomparr
SET ca[1].f1 = 12 -- works!
WHERE (ca[1]).f1 = 10;
TABLE mycomparr;
ca ---------------- {"(12,hello)"}
I wonder why plpgsql does not implement the same?
Be that as it may, another possible workaround would be to use UPDATE
on a temporary table:
DO
$$
DECLARE
ax mytype[] := '{"(10,hello)"}';
BEGIN
CREATE TEMP TABLE tmp_ax ON COMMIT DROP AS SELECT ax;
UPDATE tmp_ax SET ax[1].f1 = 12;
-- WHERE (ax[1]).f1 = 10; -- not necessary while only 1 row.
SELECT t.ax INTO ax FROM tmp_ax t; -- table-qualify column!
RAISE NOTICE '%', ax;
END
$$;
That's more overhead than with @Pavel's simpler workaround. I would not use it for the simple case. But if you have lots of assignments it might still pay to use the temporary table.
来源:https://stackoverflow.com/questions/41772518/pl-pgsql-accessing-fields-of-an-element-of-an-array-of-custom-type