Frage

Ich habe eine Reihe von Tabellen, die die Postgres "Partitioning" -Funktion verwenden. Ich möchte vor dem Einfügen eines Zeilenauslösers in jeder Tabelle ein gemeinsames Einfügen auf jeder Tabelle definieren, die 1) die Partition dynamisch erzeugt, falls der Einsatz gegen die übergeordnete Tabelle auftritt und 2) den Einsatz gegen die Partition erneut ausführen.

Etwas wie:

CREATE OR REPLACE FUNCTION partition_insert_redirect( )
RETURNS trigger AS $BODY$
BEGIN
  ... create the new partition and set up the redirect Rules ...

  /* Redo the INSERT dynamically.  The new RULE will redirect it to the child table */
  EXECUTE 'INSERT INTO ' || quote_ident(TG_TABLE_SCHEMA) || '.' || quote_ident(TG_TABLE_NAME) ||
          ' SELECT NEW.*'
END

Aber der "neue" Datensatz ist im Execute SQL nicht sichtbar. Wie kann ich diese Arbeit so einfach wie möglich machen?

Kann ich als Alternative irgendwie in der neuen Platte über die Felder in der neuen Platte iterieren?

Ich habe darüber nachgedacht, einen Temperaturtisch zu verwenden:

EXECUTE 'CREATE TEMPORARY TABLE new_row (LIKE ' ||
        quote_ident(TG_TABLE_SCHEMA) || '.' || quote_ident(TG_TABLE_NAME) ||
        ') ON COMMIT DROP';

INSERT INTO new_row SELECT NEW.*;

EXECUTE 'INSERT INTO ' || quote_ident(TG_TABLE_SCHEMA) || '.' || quote_ident(TG_TABLE_NAME) ||
       ' SELECT * FROM new_row';
DROP TABLE new_row;

Dies funktioniert jedoch auch nicht aufgrund des zwischengespeicherten Verweiss auf einen Temperaturtisch: Warum erhalte ich "Beziehung zu OID #####, die keine Fehler gibt", wenn ich auf temporäre Tabellen in PL/PGSQL -Funktionen zugreift?

Ich benutze Postgres 8.2 und kann nicht zu einer anderen Version wechseln.

BEARBEITEN:
Wie @Alvherre betonte, kann dies wahrscheinlich in Postgres 8.4 mit der Ausführung ... mit Syntax erfolgen. Siehe ein Beispiel bei http://wiki.postgresql.org/wiki/pl/pgsql_dynamic_triggers

War es hilfreich?

Lösung 3

Ich habe es geschafft, dies zu bearbeiten, indem ich eine Funktion dynamisch zusammenstellt, die die neue Zeile als Parameter akzeptiert:

    EXECUTE 'create or replace function partition_insert(r ' || TG_TABLE_NAME || ') RETURNS void AS $FUNC$' || 
            'BEGIN ' ||
                'insert into ' || TG_TABLE_NAME || ' SELECT r.*; ' ||
            'END $FUNC$ LANGUAGE plpgsql VOLATILE';
    PERFORM partition_insert(NEW);

Da Postgres -Funktionen polymorph sind, erzeugt dies eine andere Funktion für jede Tabelle, die diesen Auslöser verwendet.

Obwohl dies ein hässlicher Kludge ist, scheint dies den Job zu erledigen.

Obwohl es so aussieht, als könnte ich jede polymorphe Variation im Voraus definieren, wenn ich das System erstelle, muss ich die Funktion aufgrund von Caching neu kompilieren, wenn ich eine untergeordnete Tabelle erstelle oder fallen lasse, damit die Funktion die neueste Einfügungsregel verwendet.

BEARBEITEN: Zusätzliche Falten
Diese Technik gibt es ein wenig GOTCHA: Wenn diese Ausführungs-/Ausführungsaktion beim ersten Versuch aufgrund eines anderen Fehlers (beispielsweise in meinem Fall ein Scheck-Einschränkungsfehler) im ersten Versuch gerollt wird, scheint die Funktion, die diesen Code enthält Die rollte Back-Partition_insert () -Funktion, die mit dem Ausführungs- und nachfolgenden Aufrufen erstellt wurde, fehlen, da ein zwischengespeichertes Objekt nicht gefunden wird.

Ich habe dies aufgelöst, indem ich Stubversionen der Funktion für jeden erforderlichen Parameter vom Tabellenstillsttyp beim Definieren der Datenbank vorab.

Andere Tipps

Sie können verwenden EXECUTE USING Neu an es weitergeben. Ihr Beispiel wäre

EXECUTE 'INSERT INTO ' || TG_RELID || '::regclass SELECT $1' USING NEW;

(Beachten Sie, dass ich TG_RELID verwende, das in Regclass gegossen wurde, anstatt mit TG_TABLE_SCHEMA und TABLE_NAME zu fummeln, weil es einfacher zu verwenden ist, wenn nicht standardisiert. Aber dann ist PLPGSQL sowieso nicht standardmäßig.)

Ja, Sie können Ausführungen verwenden ... Verwenden in 8.4. Zum Beispiel:

EXECUTE 'INSERT INTO ' || table_name || ' SELECT $1.*' USING NEW;

In niedrigeren Versionen (ich habe nur in 8.3 getestet) können Sie verwenden:

EXECUTE 'INSERT INTO ' || table_name ||
    ' SELECT (' || quote_literal(NEW) || '::' || TG_RELID::regclass || ').*';
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top