次のような方法を実現する方法がわかりません。
CREATE OR REPLACE FUNCTION fnJobQueueBEFORE() RETURNS trigger AS $$
DECLARE
shadowname varchar := TG_TABLE_NAME || 'shadow';
BEGIN
INSERT INTO shadowname VALUES(OLD.*);
RETURN OLD;
END;
$$
LANGUAGE plpgsql;
つまり動的に生成された名前でテーブルに値を挿入する。
上記のコードを実行すると、次の結果が得られます。
ERROR: relation "shadowname" does not exist
LINE 1: INSERT INTO shadowname VALUES(OLD.*)
変数がテーブル名として展開/許可されていないことを示唆しているようです。 Postgresのマニュアルにはこれについての言及はありません。
私はすでにEXECUTE
を次のように実験しました:
EXECUTE 'INSERT INTO ' || quote_ident(shadowname) || ' VALUES ' || OLD.*;
しかし運がない:
ERROR: syntax error at or near ","
LINE 1: INSERT INTO personenshadow VALUES (1,sven,,,)
RECORD
タイプが失われたようです:OLD.*
は文字列に変換されて再解析されるようで、あらゆる種類の型の問題(NULL
値など)につながります。
何か案は?
format()
には、識別子をエスケープする組み込みの方法があります。以前よりシンプル:
_CREATE OR REPLACE FUNCTION foo_before()
RETURNS trigger AS
$func$
BEGIN
EXECUTE format('INSERT INTO %I.%I SELECT $1.*'
, TG_TABLE_SCHEMA, TG_TABLE_NAME || 'shadow')
USING OLD;
RETURN OLD;
END
$func$ LANGUAGE plpgsql;
_
VALUES
式でも機能します。
db <> fiddle ここ
古い sqlfiddle。
format()
または quote_ident()
を使用して識別子を引用し(自動的かつ必要な場合のみ)、それによって SQLインジェクション および単純な構文違反。search_path
_設定 に応じて、裸のテーブル名は別の方法で別のスキーマの同じ名前の別のテーブルに解決される場合があります。EXECUTE
を使用します。USING
句を使用してvaluesを安全に渡します。RETURN OLD;
_が必要です_BEFORE DELETE
_。 マニュアルの詳細はこちらOLD
は非表示EXECUTE
なので、ほぼ成功したバージョンではエラーメッセージが表示されます。また、試したように分解された行の個々の値を連結したい場合は、quote_literal()
を使用してすべての列のテキスト表現を準備し、有効な構文を保証する必要があります。列名を事前に知っている列名を処理したり、システムカタログをクエリしたりする必要もあります。これは、シンプルで動的なトリガー関数を使用するという考えに反します...
私のソリューションは、これらの複雑化をすべて回避します。また、少し簡略化しました。
format()
はまだ使用できないため、次のようになります。
_CREATE OR REPLACE FUNCTION foo_before()
RETURNS trigger AS
$func$
BEGIN
EXECUTE 'INSERT INTO ' || quote_ident(TG_TABLE_SCHEMA)
|| '.' || quote_ident(TG_TABLE_NAME || 'shadow')
|| ' SELECT $1.*'
USING OLD;
RETURN OLD;
END
$func$ LANGUAGE plpgsql;
_
関連:
動的INSTEAD OF DELETE
トリガーを探していたので、これに遭遇しました。質問と回答に感謝して、Postgres 9.3のソリューションを投稿します。
CREATE OR REPLACE FUNCTION set_deleted_instead_of_delete()
RETURNS TRIGGER AS $$
BEGIN
EXECUTE format('UPDATE %I set deleted = now() WHERE id = $1.id', TG_TABLE_NAME)
USING OLD;
RETURN NULL;
END;
$$ language plpgsql;