Alternativa mais simples para sua resposta postada. Deve ter um desempenho muito melhor.
Esta função recupera uma linha de uma determinada tabela (
in_table_name
) e o valor da chave primária (in_row_pk
), e a insere como uma nova linha na mesma tabela, com alguns valores substituídos (in_override_values
). O novo valor da chave primária como padrão é retornado (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;
Ligar:
SELECT f_clone_row('t', 1, '"col1"=>"foo_new","col2"=>"bar_new"'::hstore);
SQL Fiddle.
-
Useregclass
como tipo de parâmetro de entrada, portanto, apenas nomes de tabela válidos podem ser usados para começar e a injeção de SQL é descartada. A função também falha mais cedo e com mais facilidade se você fornecer um nome de tabela ilegal.
-
Use umOUT
parâmetro (pk_new
) para simplificar a sintaxe.
-
Não há necessidade de descobrir o próximo valor para a chave primária manualmente. Ele é inserido automaticamente e retornado após o fato. Isso não é apenas mais simples e rápido, você também evita números de sequência desperdiçados ou fora de ordem.
-
Useformato()
para simplificar a montagem da string de consulta dinâmica e torná-la menos propensa a erros. Observe como eu uso parâmetros posicionais para identificadores e strings, respectivamente.
-
Baseio-me na sua suposição implícita que as tabelas permitidas tenham uma coluna de chave primária única do tipo inteiro com uma coluna padrão . Normalmenteserial
colunas.
-
O elemento chave da função é oINSERT
final :
- Mesclar valores de substituição com a linha existente usando o
#=
operador em uma subseleção e decomponha a linha resultante imediatamente. - Então você pode selecionar apenas colunas relevantes no
SELECT
principal . - Deixe o Postgres atribuir o valor padrão para o PK e recuperá-lo com o
RETURNING
cláusula. - Escreva o valor retornado no
OUT
parâmetro diretamente. - Tudo feito em um único comando SQL, que geralmente é mais rápido.
- Mesclar valores de substituição com a linha existente usando o