НОВАЯ вставка.* от универсального триггера использование ВЫПОЛНЯЮТСЯ в PL/pgsql

У меня есть много таблиц, которые используют функцию "Partitioning" Пост-ГРЭС. Я хочу определить общее, ПРЕЖДЕ ЧЕМ ВСТАВЯТ СТРОКИ, включают каждую таблицу, которая 1) динамично создаст раздел, должен вставка происходить против родительской таблицы и 2) повторно выполнит вставку против раздела.

Что-то как:

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

Но "НОВАЯ" запись не видима в ВЫПОЛНИТЬ SQL. Как я могу сделать эту работу максимально максимально?

Как альтернатива, я могу выполнить итерации по полям в НОВОЙ записи так или иначе?

Я думал об использовании поддающегося соблазну:

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;

Но это также не работает из-за кэшируемой ссылки на поддающееся соблазну: То, почему я получаю "отношение с OID #####, не существует" ошибки при доступе к временным таблицам в функциях PL/PgSQL?

Я использую Пост-ГРЭС 8.2, и я не могу измениться ни на какую другую версию.

Править:
Как @alvherre указанный, это может, вероятно, быть сделано в Пост-ГРЭС 8.4 с ВЫПОЛНЕНИЕМ... ИСПОЛЬЗУЯ Синтаксис. Посмотрите пример по http://wiki.postgresql.org/wiki/PL/pgSQL_Dynamic_Triggers

15
задан Adrian Pronk 4 January 2010 в 04:58
поделиться

3 ответа

Мне удалось заставить это работать, динамически компилируя функцию, которая принимает НОВУЮ строку в качестве параметра:

    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);

Так как функции Postgres полиморфны, то для каждой таблицы, использующей этот триггер, будет генерироваться своя функция.

Несмотря на то, что это уродливый кладж, похоже, что это делает работу.

Хотя похоже, что я могу определить каждую полиморфную вариацию заранее при сборке системы, из-за кэширования, я должен перекомпилировать функцию всякий раз, когда я создаю или бросаю дочернюю таблицу, чтобы функция использовала последнюю вставку RULE.

EDIT: Дополнительные морщины
Есть небольшая загвоздка с этой техникой: Если это действие EXECUTE/PERFORM откатывается при первой попытке из-за другой ошибки (например, в моем случае CHECK сбой ограничения), то функция, содержащая этот код, кажется, кэширует ссылку на функцию rolled-back partition_insert(), которую она создала с помощью EXECUTE, и последующие вызовы завершаются неудачей из-за того, что кэшируемый объект не найден.

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

.
1
ответ дан 1 December 2019 в 02:46
поделиться

Вы можете использовать EXECUTE USING для передачи НОВОГО. Ваш пример был бы

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

(Обратите внимание, что я использую TG_RELID casted to regclass вместо того, чтобы возиться с TG_TABLE_SCHEMA и TABLE_NAME, т.к. это проще в использовании, если это нестандартно. Но тогда plpgsql все равно нестандартен.)

.
22
ответ дан 1 December 2019 в 02:46
поделиться

Да, вы можете использовать EXECUTE ... USING в 8.4. Например:

ВЫПОЛНИТЬ 'INSERT INTO' || table_name || 'SELECT $ 1. *' USING NEW;

В более ранних версиях (я тестировал только в 8.3) вы можете использовать:

EXECUTE 'INSERT INTO ' || table_name ||
    ' SELECT (' || quote_literal(NEW) || '::' || TG_RELID::regclass || ').*';
3
ответ дан 1 December 2019 в 02:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: