2

Is it possible to dynamically access a column value from a record by its name?

I'm writing a trigger function that executes a dynamic SQL command and I would like to dynamically extract a column value from a NEW record by column name.

Here's a simplified example of what I'm trying to do:

$$
DECLARE
   command text := 'UPDATE $1 SET $2 = $3';
   myColumn := 'votes'
BEGIN
   EXECUTE command using 'anotherTable', myColumn, NEW.myColumn;
END
$$
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228

1 Answers1

2

That's possible, but the USING clause of EXECUTE can only pass values, while identifiers like table and column names must be concatenated in the command string. (Be wary of SQL injection!) Using format() it could work like this:

CREATE FUNCTION ... AS
$func$
DECLARE
   _command text := 'UPDATE %I SET %I = $1 WHERE ....';  -- add WHERE condition
   _col text := 'votes';
BEGIN
   EXECUTE format(_command, 'anotherTable', _col)
   USING  NEW.myColumn;
END
$func$;

Fixed a couple of minor problems in passing.

It must be mentioned that NEW is only available in trigger functions.

Be aware that 'anotherTable' is case sensitive here (being concatenated safely with double-quotes from a string), while NEW.myColumn is not (processed as unquoted identifier). Always use legal, lower case, unquoted identifiers in Postgres to make your life easier.

Related answers with more explanation and links:


To dynamically extract a column value from a NEW record by column name.

... you can use the hstore #= operator:

Or you can make it work with standard features of dynamic SQL as well:

CREATE FUNCTION ... AS
$func$
DECLARE
   _col text := 'votes';
   _new_col text := 'column_name_in_new';  -- case-sensitive column name here
BEGIN
   EXECUTE format(
       'UPDATE %I SET %I = $1.%I WHERE ... '  -- add WHERE condition
     , 'anotherTable', _col, _new_col)
   USING  NEW;  -- pass whole row
END
$func$;

Related:

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
  • 1
    Thanks for the great answer, however that's still not quite what I was trying to do. In your example you explicitly accessed `NEW.myColumn;` I would like to dynamically access a column based on a pre-existing value, something like `NEW[_column]`. Someone suggested elsewhere to use **hstore**. This works: `hstore(NEW)->_column` to access the _votes_ column in NEW dynamically. However I'm not familiar at all with hstore and I would prefer not to enable the extension if there's a simpler alternative. – Juan Jose Lenero Lozano Sep 10 '15 at 00:22
  • @RedHusky: I added another solution for that. – Erwin Brandstetter Sep 10 '15 at 01:15
  • thank you for this great answer, helped me to solve a problem. – YangombiUmpakati Feb 11 '23 at 10:35