Как динамически использовать TG_TABLE_NAME в PostgreSQL 8.2? - PullRequest
5 голосов
/ 22 сентября 2011

Я пытаюсь написать триггерную функцию в PostgreSQL 8.2, которая будет динамически использовать TG_TABLE_NAME для генерации и выполнения оператора SQL. Я могу найти все виды примеров для более поздних версий PostgreSQL, но я застрял на 8.2 из-за некоторых требований. Вот моя функция в ее нынешнем виде, которая работает, но вряд ли она динамична:

CREATE OR REPLACE FUNCTION cdc_TABLENAME_function() RETURNS trigger AS $cdc_function$
        DECLARE 
        op  cdc_operation_enum;
    BEGIN
        op = TG_OP;

        IF (TG_WHEN = 'BEFORE') THEN
            IF (TG_OP = 'UPDATE') THEN
                op = 'UPDATE_BEFORE';
            END IF;

            INSERT INTO cdc_test VALUES (DEFAULT,DEFAULT,op,DEFAULT,DEFAULT,OLD.*); 
        ELSE
            IF (TG_OP = 'UPDATE') THEN
                op = 'UPDATE_AFTER';
            END IF;

            INSERT INTO cdc_test VALUES (DEFAULT,DEFAULT,op,DEFAULT,DEFAULT,NEW.*); 
        END IF;

        IF (TG_OP = 'DELETE') THEN
            RETURN OLD;
        ELSE
            RETURN NEW;
        END IF;
    END;

То, как это в настоящее время написано, я должен был бы написать отдельную функцию триггера для каждой таблицы. Я хотел бы использовать TG_TABLE_NAME для динамического построения моего оператора INSERT и просто поставить перед ним префикс «cdc_», поскольку все таблицы следуют одному и тому же соглашению об именах. Тогда я могу иметь каждый триггер для каждого вызова таблицы только одну функцию.

Ответы [ 2 ]

13 голосов
/ 01 октября 2011

Я искал то же самое пару лет назад.Одна триггерная функция, чтобы управлять ими всеми!Я спрашивал в списках usenet, пробовал разные подходы, но безрезультатно.Консенсус по этому вопросу был , это не могло быть сделано .Недостаток PostgreSQL 8.3 или старше.

Начиная с PostgreSQL 8.4 вы можете просто:

EXECUTE 'INSERT INTO ' || TG_RELID::regclass::text || ' SELECT ($1).*'
USING NEW;

С pg 8.2 у вас возникла проблема:

  • не может динамически обращаться к столбцам NEW / OLD.Вам нужно знать имена столбцов во время написания триггерной функции.
  • NEW / OLD не видны внутри EXECUTE.
  • EXECUTE .. USING еще не родился.

Однако есть хитрость.

Каждое имя таблицы в системе может служить составным типом с таким же именем.Поэтому вы можете создать функцию, которая принимает NEW / OLD в качестве параметра и выполнять ее.Вы можете динамически создавать и уничтожать эту функцию при каждом событии триггера:

Функция триггера:

CREATE OR REPLACE FUNCTION trg_cdc()
  RETURNS trigger AS
$func$
DECLARE
   op      text := TG_OP || '_' || TG_WHEN;
   tbl     text := quote_ident(TG_TABLE_SCHEMA) || '.'
                || quote_ident(TG_TABLE_NAME);
   cdc_tbl text := quote_ident(TG_TABLE_SCHEMA) || '.'
                || quote_ident('cdc_' || TG_TABLE_NAME);
BEGIN

EXECUTE 'CREATE FUNCTION f_cdc(n ' || tbl || ', op text)
  RETURNS void AS $x$ BEGIN
  INSERT INTO ' || cdc_tbl || ' SELECT op, (n).*;
END $x$ LANGUAGE plpgsql';

CASE TG_OP
WHEN 'INSERT', 'UPDATE' THEN
   PERFORM f_cdc(NEW, op);
WHEN 'DELETE' THEN
   PERFORM f_cdc(OLD, op);
ELSE
   RAISE EXCEPTION 'Unknown TG_OP: "%". Should not occur!', TG_OP;
END CASE;

EXECUTE 'DROP FUNCTION f_cdc(' || tbl || ', text)';

IF TG_OP = 'DELETE' THEN
    RETURN OLD;
ELSE
    RETURN NEW;
END IF;

END
$func$  LANGUAGE plpgsql;

Триггер:

CREATE TRIGGER cdc
BEFORE INSERT OR UPDATE OR DELETE ON my_tbl
FOR EACH ROW EXECUTE PROCEDURE trg_cdc();

Имена таблиц должны обрабатываться как пользовательвход.Используйте quote_ident() для защиты от внедрения SQL.

Однако , таким образом вы создаете и удаляете функцию для каждого отдельного события триггера.Довольно накладные расходы, я бы не пошел на это.Вам придется много пылесосить некоторые каталожные таблицы.

Средний уровень

PostgreSQL поддерживает перегрузку функции .Следовательно, одна функция на таблицу с одним и тем же базовым именем (но с другим типом параметра) может сосуществовать.Вы можете занять среднее положение и значительно уменьшить шум, создав f_cdc(..) один раз для каждой таблицы в то же время, когда вы создаете триггер.Это одна крошечная функция на таблицу.Вы должны наблюдать за изменениями в определениях таблиц, но таблицы не должны так часто меняться.Удалите CREATE и DROP FUNCTION из функции триггера, чтобы получить небольшой, быстрый и элегантный триггер.

Я мог видеть, что я делаю это на стр. 8.2.За исключением того, что я больше не вижу, как я делаю что-нибудь в pg 8.2.В декабре 2011 года достиг конца жизни .Может быть, вы все-таки сможете как-нибудь обновить.

0 голосов
/ 03 октября 2011

Я также задавал похожий вопрос пару лет назад.

Посмотрите на этот вопрос и посмотрите, дает ли он вам какие-нибудь полезные идеи:

Вставка NEW. *из общего триггера, используя EXECUTE в PL / pgsql

...