Ich bin nicht sicher, wie ich Folgendes erreichen kann:
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;
Das heißt Einfügen von Werten in eine Tabelle mit einem dynamisch generierten Namen.
Wenn Sie den obigen Code ausführen, erhalten Sie:
ERROR: relation "shadowname" does not exist
LINE 1: INSERT INTO shadowname VALUES(OLD.*)
Es scheint, dass Variablen nicht als Tabellennamen erweitert/erlaubt sind. Ich habe im Postgres-Handbuch keinen Hinweis darauf gefunden.
Ich habe bereits mit EXECUTE
experimentiert:
EXECUTE 'INSERT INTO ' || quote_ident(shadowname) || ' VALUES ' || OLD.*;
Aber kein glück:
ERROR: syntax error at or near ","
LINE 1: INSERT INTO personenshadow VALUES (1,sven,,,)
Der RECORD
-Typ scheint verloren zu sein: OLD.*
scheint in eine Zeichenfolge konvertiert zu werden und wird neu sortiert, was zu allen möglichen Typproblemen führt (z. B. NULL
-Werte).
Irgendwelche Ideen?
format()
verfügt über eine integrierte Möglichkeit, Kennungen zu umgehen. Einfacher als zuvor:
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;
SQL-Geige.
Funktioniert auch mit einem VALUES
-Ausdruck.
quote_ident()
, um ggf. Bezeichner anzugeben und gegen SQL-Injection zu schützen.search_path
kann ein bare Tabellenname ansonsten in eine andere Tabelle mit demselben Namen in einem anderen Schema aufgelöst werden.EXECUTE
für dynamische DDL-Anweisungen.USING
-Klausel.RETURN OLD;
in der Triggerfunktion für einen TriggerBEFORE DELETE
erforderlich ist. Details hier im Handbuch.Sie erhalten die Fehlermeldung in Ihrer fast erfolgreichen Version, weil OLD
nicht sichtbar ist in EXECUTE
. Wenn Sie einzelne Werte der zerlegten Zeile wie gewünscht verketten möchten, müssen Sie die Textdarstellung jeder einzelnen Spalte mit quote_literal()
vorbereiten, um eine gültige Syntax zu gewährleisten. Sie müssten auch vorher know Spaltennamen verwenden, um sie zu bearbeiten oder die Systemkataloge abzufragen - was Ihrer Vorstellung von einer einfachen, dynamischen Triggerfunktion entgegensteht.
Meine Lösung vermeidet all diese Komplikationen. Auch etwas vereinfacht.
format()
ist noch nicht verfügbar, also:
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;
Verbunden:
Ich bin gerade darüber gestolpert, weil ich nach einem dynamischen INSTEAD OF DELETE
-Auslöser gesucht habe. Als Dankeschön für die Frage und die Antworten werde ich meine Lösung für Postgres 9.3 veröffentlichen.
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;