EXECUTE...INTO...USING statement in PL/pgSQL can't execute into a record?

composite, dynamic-sql, plpgsql, postgresql, record

Solution

Since I didn't want to have to use any external functions for speed purposes, I created a solution using hstores to insert a record into a table:

CREATE OR REPLACE FUNCTION fn_clone_row(in_table_name character varying, in_row_pk integer, in_override_values hstore)
RETURNS integer
LANGUAGE plpgsql
AS $function$
DECLARE

my_table_pk_col_name    varchar;
my_key                  text;
my_value                text;
my_row                  record;
my_pk_default           text;
my_pk_new               integer;
my_pk_new_text          text;
my_row_hstore           hstore;
my_row_keys             text[];
my_row_keys_list        text;
my_row_values           text[];
my_row_values_list      text;

BEGIN

-- Get the next value of the pk column for the table.
SELECT ad.adsrc,
       at.attname
  INTO my_pk_default,
       my_table_pk_col_name
  FROM pg_attrdef ad
  JOIN pg_attribute at
    ON at.attnum = ad.adnum
   AND at.attrelid = ad.adrelid
  JOIN pg_class c
    ON c.oid = at.attrelid
  JOIN pg_constraint cn
    ON cn.conrelid = c.oid
   AND cn.contype = 'p'
   AND cn.conkey[1] = at.attnum
  JOIN pg_namespace n
    ON n.oid = c.relnamespace
 WHERE c.relname = in_table_name
   AND n.nspname = 'public';

-- Get the next value of the pk in a local variable
EXECUTE ' SELECT ' || my_pk_default
   INTO my_pk_new;

-- Set the integer value back to text for the hstore
my_pk_new_text := my_pk_new::text;


-- Add the next value statement to the hstore of changes to make.
in_override_values := in_override_values || hstore( my_table_pk_col_name, my_pk_new_text );


-- Copy over only the given row to the record.
EXECUTE ' SELECT * '
        '   FROM ' || quote_ident( in_table_name ) ||
        '  WHERE ' || quote_ident( my_table_pk_col_name ) ||
                   '    = ' || quote_nullable( in_row_pk )
   INTO my_row;


-- Replace the values that need to be changed in the column name array
my_row := my_row #= in_override_values;


-- Create an hstore of my record
my_row_hstore := hstore( my_row );


-- Create a string of comma-delimited, quote-enclosed column names
my_row_keys := akeys( my_row_hstore );
SELECT array_to_string( array_agg( quote_ident( x.colname ) ), ',' )
  INTO my_row_keys_list
  FROM ( SELECT unnest( my_row_keys ) AS colname ) x;


-- Create a string of comma-delimited, quote-enclosed column values
my_row_values := avals( my_row_hstore );
SELECT array_to_string( array_agg( quote_nullable( x.value ) ), ',' )
  INTO my_row_values_list
  FROM ( SELECT unnest( my_row_values ) AS value ) x;


-- Insert the values into the columns of a new row
EXECUTE 'INSERT INTO ' || in_table_name || '(' || my_row_keys_list || ')'
        '     VALUES (' || my_row_values_list || ')';


RETURN my_pk_new;

END
$function$;

It's quite a bit longer than what I had envisioned, but it works and is actually quite speedy.

Problem

I'm attempting to write an area of a function in PL/pgSQL that loops through an `hstore` and sets a record's column(the key of the `hstore`) to a specific value (the value of the `hstore`). I'm using Postgres 9.1. The `hstore` will look like: `' "column1"=>"value1","column2"=>"value2" '` Generally, here is what I want from a function that takes in an `hstore` and has a record with values to modify: ``` FOR my_key, my_value IN SELECT key, value FROM EACH( in_hstore ) LOOP EXECUTE 'SELECT $1' INTO my_row.my_key USING my_value; END LOOP; ``` The error which I am getting with this code: `"myrow" has no field "my_key"`. I've been searching for quite a while now for a solution, but everything else I've tried to achieve the same result hasn't worked.

Original source