По-проста алтернатива на публикувания от вас отговор. Трябва да работи много по-добре.
Тази функция извлича ред от дадена таблица (in_table_name
) и стойност на първичен ключ (in_row_pk
) и го вмъква като нов ред в същата таблица, като някои стойности са заменени (in_override_values
). Връща се новата стойност на първичния ключ по подразбиране (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;
Обаждане:
SELECT f_clone_row('t', 1, '"col1"=>"foo_new","col2"=>"bar_new"'::hstore);
-
Използвайте
regclass
като тип входен параметър, така че само валидни имена на таблици могат да се използват като начало и SQL инжектирането е изключено. Функцията също се проваля по-рано и по-елегантно, ако предоставите нелегално име на таблица. -
Използвайте
OUT
параметър (pk_new
), за да опростите синтаксиса. -
Няма нужда да намирате следващата стойност за първичния ключ ръчно. Вмъква се автоматично и се връща след факта. Това е не само по-просто и по-бързо, но също така избягвате пропилени или неправилни поредни номера.
-
Използвайте
format()код>
за да се опрости сглобяването на динамичния низ на заявката и да го направи по-малко податлив на грешки. Обърнете внимание как използвам позиционни параметри съответно за идентификатори и низове. -
Разчитам на вашето имплицитно предположение че разрешените таблици имат единична колона с първичен ключ от тип integer с колона по подразбиране . Обикновено
сериен
колони. -
Ключов елемент на функцията е крайният
INSERT
:- Слейте стойностите за замяна със съществуващия ред с помощта на
#=
оператор в подизбор и незабавно разложете получения ред. - След това можете да изберете само подходящи колони в главния
SELECT
. - Нека Postgres присвои стойността по подразбиране за PK и да го върне с
RETURNING
клауза. - Запишете върнатата стойност в
OUT
параметър директно. - Всичко се прави с една SQL команда, която обикновено е най-бърза.
- Слейте стойностите за замяна със съществуващия ред с помощта на