sql >> Base de Datos >  >> RDS >> PostgreSQL

¿La instrucción EXECUTE...INTO...USING en PL/pgSQL no se puede ejecutar en un registro?

Alternativa más simple a su respuesta publicada. Debería funcionar mucho mejor.

Esta función recupera una fila de una tabla dada (in_table_name ) y el valor de la clave principal (in_row_pk ), y lo inserta como una fila nueva en la misma tabla, con algunos valores reemplazados (in_override_values ). Se devuelve el nuevo valor de clave principal predeterminado (pk_new ).

CREATE OR REPLACE FUNCTION f_clone_row(in_table_name regclass
                                     , in_row_pk int
                                     , in_override_values hstore
                                     , OUT pk_new int) AS
$func$
DECLARE
   _pk   text;  -- name of PK column
   _cols text;  -- list of names of other columns
BEGIN

-- Get name of PK column
SELECT INTO _pk  a.attname
FROM   pg_catalog.pg_index     i
JOIN   pg_catalog.pg_attribute a ON a.attrelid = i.indrelid
                                AND a.attnum   = i.indkey[0]  -- 1 PK col!
WHERE  i.indrelid = 't'::regclass
AND    i.indisprimary;

-- Get list of columns excluding PK column
_cols := array_to_string(ARRAY(
      SELECT quote_ident(attname)
      FROM   pg_catalog.pg_attribute
      WHERE  attrelid = in_table_name -- regclass used as OID
      AND    attnum > 0               -- exclude system columns
      AND    attisdropped = FALSE     -- exclude dropped columns
      AND    attname <> _pk           -- exclude PK column
      ), ',');

-- INSERT cloned row with override values, returning new PK
EXECUTE format('
   INSERT INTO %1$I (%2$s)
   SELECT %2$s
   FROM  (SELECT (t #= $1).* FROM %1$I t WHERE %3$I = $2) x
   RETURNING %3$I'
 , in_table_name, _cols, _pk)
USING   in_override_values, in_row_pk -- use override values directly
INTO    pk_new;                       -- return new pk directly

END
$func$ LANGUAGE plpgsql;

Llamar:

SELECT f_clone_row('t', 1, '"col1"=>"foo_new","col2"=>"bar_new"'::hstore);

SQL Fiddle.

  • Use regclass como tipo de parámetro de entrada, por lo que solo se pueden usar nombres de tabla válidos para empezar y se descarta la inyección de SQL. La función también falla antes y con más gracia si debe proporcionar un nombre de tabla ilegal.

  • Usa un OUT parámetro (pk_new ) para simplificar la sintaxis.

  • No es necesario averiguar el siguiente valor de la clave principal de forma manual. Se inserta automáticamente y se devuelve después del hecho. Eso no solo es más simple y rápido, sino que también evita números de secuencia desperdiciados o desordenados.

  • Use format() para simplificar el ensamblaje de la cadena de consulta dinámica y hacerla menos propensa a errores. Tenga en cuenta cómo uso parámetros posicionales para identificadores y cadenas respectivamente.

  • Me baso en su suposición implícita que las tablas permitidas tienen una columna de clave principal única de tipo entero con una columna predeterminada . Por lo general, serial columnas.

  • El elemento clave de la función es el INSERT final :

    • Combina los valores de anulación con la fila existente usando #= operador en una subselección y descomponga la fila resultante inmediatamente.
    • Luego puede seleccionar solo columnas relevantes en el SELECT principal .
    • Deje que Postgres asigne el valor predeterminado para el PK y recupérelo con RETURNING cláusula.
    • Escriba el valor devuelto en OUT parámetro directamente.
    • Todo hecho en un solo comando SQL, que generalmente es el más rápido.