Thread: Passing row set into PL/pgSQL function.

Passing row set into PL/pgSQL function.

From
Lucas Clemente Vella
Date:
I am trying to write a generic "upsert" function in PL/pgSQL, in a way
that I can specify the table were I want to insert/update, the columns
whose values I want to specify, and the values to be inserted.

So far I have come up with a solution whose signature is:

CREATE OR REPLACE FUNCTION upsert(IN tname text, IN cnames text[],
VARIADIC vals anyarray) RETURNS void

Whose tname is the table, cnames are the columns ans vals the values.
The problem I have is when I try to call the function: I can only pass
values of a previously defined type, like:

SELECT upsert('my_table', ARRAY['key', 'data'], (10,
'hello')::my_table, (20, 'world')::my_table);

Instead of:

SELECT upsert('my_table', ARRAY['key', 'data'], (10, 'hello'), (20, 'world'));

What gives me the error:

ERROR:  PL/pgSQL functions cannot accept type record[]

This later approach would be much preferable, since I don't always
want to specify the full table row, but just some fields, and I would
be able to specify the columns in any order I want (as given in
cnames). Since PL/pgSQL is unable to receive a record[] parameter, is
there any alternative for passing a set of arbitrary compound values?
Is there any way of passing a table, like "VALUES (10, 'hello'), (20,
'world')" or a CTE?

Could I use any other language that does not require superuser
privileges to be installed? PL/pgSQL is preferable due to
availability, but using another language would be OK.

--
Lucas Clemente Vella
lvella@gmail.com


Re: Passing row set into PL/pgSQL function.

From
Craig Ringer
Date:
On 09/20/2012 05:37 AM, Lucas Clemente Vella wrote:
> I am trying to write a generic "upsert" function in PL/pgSQL, in a way
> that I can specify the table were I want to insert/update, the columns
> whose values I want to specify, and the values to be inserted.

http://www.depesz.com/2012/06/10/why-is-upsert-so-complicated/

--
Craig Ringer


Re: Passing row set into PL/pgSQL function.

From
Craig Ringer
Date:
On 09/20/2012 01:47 PM, Lucas Clemente Vella wrote:
>> http://www.depesz.com/2012/06/10/why-is-upsert-so-complicated/
>
> I have already seen this page, I am OK in running SERIALIZABLE
> transactions, and have no problem in replaying failed transactions due
> to race condition. Anyway, that is completely off my issue: I need
> upsert and I am prepared to deal with it. I just want to save typing
> by creating a reusable function.

In that case, maybe you could have your function accept a `refcursor`?

DECLARE some_curs CURSOR FOR VALUES ('a',1), ('b',2), ('c',3);
SELECT funky_upsert('table', ARRAY['col1','col2'], 'some_curs');
CLOSE some_curs;

Internally it could fetch rows from the refcursor into record fields and
do what it needed.

Personally I'd just do the work app-side.

--
Craig Ringer



Re: Passing row set into PL/pgSQL function.

From
Merlin Moncure
Date:
On Wed, Sep 19, 2012 at 4:37 PM, Lucas Clemente Vella <lvella@gmail.com> wrote:
> I am trying to write a generic "upsert" function in PL/pgSQL, in a way
> that I can specify the table were I want to insert/update, the columns
> whose values I want to specify, and the values to be inserted.
>
> So far I have come up with a solution whose signature is:
>
> CREATE OR REPLACE FUNCTION upsert(IN tname text, IN cnames text[],
> VARIADIC vals anyarray) RETURNS void
>
> Whose tname is the table, cnames are the columns ans vals the values.
> The problem I have is when I try to call the function: I can only pass
> values of a previously defined type, like:
>
> SELECT upsert('my_table', ARRAY['key', 'data'], (10,
> 'hello')::my_table, (20, 'world')::my_table);
>
> Instead of:
>
> SELECT upsert('my_table', ARRAY['key', 'data'], (10, 'hello'), (20, 'world'));
>
> What gives me the error:
>
> ERROR:  PL/pgSQL functions cannot accept type record[]

note, pl/pgsql functions can take arrays of non-anonymous record types
-- either tables, or composite types.  you're just not allowed to pass
anonymous rows in.

for key value pairs, also you should take a look at hstore.  You can
also make arrays of hstore.

merlin


Re: Passing row set into PL/pgSQL function.

From
Lucas Clemente Vella
Date:
> http://www.depesz.com/2012/06/10/why-is-upsert-so-complicated/

I have already seen this page, I am OK in running SERIALIZABLE
transactions, and have no problem in replaying failed transactions due
to race condition. Anyway, that is completely off my issue: I need
upsert and I am prepared to deal with it. I just want to save typing
by creating a reusable function.

--
Lucas Clemente Vella
lvella@gmail.com