Search code examples
postgresqlrecordplpgsqldynamic-sqlcomposite

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


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.


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.