PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

INSERT mit dynamischem Tabellennamen in Triggerfunktion

PostgreSQL 9.1 oder höher

format() hat eine eingebaute Möglichkeit, Bezeichner zu maskieren. 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;

Funktioniert mit einem VALUES auch Ausdruck.

db<>hier fummeln
Altes sqlfiddle.

Wichtige Punkte

  • Verwenden Sie format() oder quote_ident() Bezeichner zu zitieren (automatisch und nur dort, wo es notwendig ist) und sich so gegen SQL-Injection und einfache Syntaxverletzungen zu wehren.
    Das ist notwendig , sogar mit eigenen Tabellennamen!
  • Schemaqualifizieren Sie den Tabellennamen. Abhängig vom aktuellen search_path Das Festlegen eines bloßen Tabellennamens könnte andernfalls zu einer anderen Tabelle mit demselben Namen in einem anderen Schema aufgelöst werden.
  • Verwenden Sie EXECUTE für dynamische DDL-Anweisungen.
  • Übergeben Sie Werte sicher mit USING Klausel.
  • Konsultieren Sie das feine Handbuch zum Ausführen dynamischer Befehle in plpgsql.
  • Beachten Sie, dassRETURN OLD; in der Triggerfunktion wird für einen Trigger BEFORE DELETE benötigt . Details im Handbuch hier.

Sie erhalten die Fehlermeldung in Ihrer fast erfolgreichen Version, weil OLD ist nicht sichtbar innerhalb von EXECUTE . Und wenn Sie einzelne Werte der zerlegten Zeile so verketten möchten, wie Sie es versucht haben, müssen Sie die Textdarstellung jeder einzelnen Spalte mit quote_literal() vorbereiten um eine gültige Syntax zu gewährleisten. Sie müssten es auch wissen Spaltennamen vorher zu bearbeiten oder die Systemkataloge abzufragen - was Ihrer Vorstellung einer einfachen, dynamischen Triggerfunktion widerspricht ...

Meine Lösung vermeidet all diese Komplikationen. Auch etwas vereinfacht.

PostgreSQL 9.0 oder früher

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;

Verwandte:

  • Wie verwende ich TG_TABLE_NAME dynamisch in PostgreSQL 8.2?