16.3. Примеры#
16.3. Примеры
Эта глава предоставляет примеры использования autonomous transactions
в функциях PL/pgSQL
. Autonomous transactions
позволяют выполнять SQL-запросы в отдельной транзакции внутри функции PL/pgSQL
для реализации сложной логики и обработки ошибок.
Для использования автономных транзакций необходимо объявить PRAGMA AUTONOMOUS_TRANSACTION
в функции. Это запустит автономную сессию, в которой будут выполняться SQL-запросы в изолированной транзакции.
Примеры демонстрируют основные шаблоны использования, такие как массовые вставки, передача параметров из основной сессии и реализация надежной логики управления транзакциями.
Autonomous transactions
обеспечивают гибкость в проектировании функций PL/pgSQL
, которая невозможна с обычной обработкой исключений и транзакций. Однако их следует использовать осторожно, чтобы избежать проблем с параллелизмом и издержек при запуске сеансов.
16.3.1. Использование автономных транзакций в PL/pgSQL
Создание таблицы для отслеживания журналов:
-- Creating a table for log storage DROP TABLE IF EXISTS test1; CREATE TABLE table_tracking( log_id serial, username text, event_date timestamp, msg text );
Создание функции log_action_atx с автономной транзакцией:
-- Defining a function with autonomous transaction for logging CREATE OR REPLACE FUNCTION log_action_atx ( username text, event_date timestamp, msg text ) RETURNS VOID AS $body$ DECLARE PRAGMA AUTONOMOUS_TRANSACTION; BEGIN START TRANSACTION; INSERT INTO table_tracking VALUES ( nextval('table_tracking_log_id_seq'::regclass), username, event_date, msg ); COMMIT; END; $body$ LANGUAGE plpgsql;
Создание основной функции log_action, вызывающей log_action_atx:
-- Main function for performing actions and calling the logging function CREATE OR REPLACE FUNCTION log_action ( username text, event_date timestamp, msg text ) RETURNS VOID AS $body$ DECLARE v_query text; BEGIN -- Call the autonomous transaction for logging v_query := 'SELECT log_action_atx ( ' || quote_nullable(username) || ',' || quote_nullable(event_date) || ',' || quote_nullable(msg) || ' )'; EXECUTE v_query; -- Executing additional actions with an error RAISE NOTICE 'Division by zero'; PERFORM 5/0; END; $body$ LANGUAGE PLPGSQL;
Выполнение главной функции для демонстрации ее функциональности:
-- Calling the log_action function to demonstrate logging operation SELECT * FROM log_action( current_user::text, now()::timestamp, 'Test'::text );
Этот код демонстрирует использование autonomous transaction
внутри функции PL/pgSQL
для регистрации информации отдельно от основной транзакции. Вот что он делает:
Создается таблица для хранения записей отслеживания.
Определена функция
PL/pgSQL
под названием log_action_atx(), объявленная как автономная транзакцияAUTONOMOUS TRANSACTION
.Данная функция добавляет запись в журнал и тут же её подтверждает.
Основная функция log_action() обращается к log_action_atx() с целью записи в журнал.
После этого функция выполняет другие действия, которые приводят к ошибке.
При её вызове, запись в журнале производится в отдельной транзакции и сразу подтверждается.
Основная транзакция возвращается к начальному состоянию из-за ошибки.
Однако, запись в журнале остается, так как была подтверждена. Если бы функция не была вызвана как автономная транзакция
autonomous transactions
, подтверждения бы не произошло.
Итак, вкратце, autonomous transaction
позволяет вести журнал отдельно от основной транзакции. Это гарантирует, что журналы не будут потеряны при откате транзакции.
Секрет заключается в объявлении прагмы AUTONOMOUS_TRANSACTION
в log_action_atx().
NOTICE:
Division by zeroERROR:
division by zeroCONTEXT:
SQL statement "SELECT 5/0"PL/pgSQL function log_action(text,timestamp without time zone,text) line 11 at PERFORM
-- Query to check stored logs
SELECT msg FROM table_tracking;
msg
16.3.2. Автономная транзакция внутри анонимного блока с COMMIT
Создание процедуры с Autonomous Transaction
:
-- Creating a procedure with an autonomous transaction for independent operations CREATE OR REPLACE PROCEDURE insert_two_rows() LANGUAGE plpgsql AS $$ DECLARE PRAGMA AUTONOMOUS_TRANSACTION; BEGIN INSERT INTO dept VALUES (60, 'FINANCE', 'CHICAGO'); CALL insert_dept_70(); COMMIT; END; $$;
Основная транзакция выполняется с откатом:
-- Executing main transaction with operations and a rollback BEGIN; INSERT INTO dept VALUES (50, 'HR', 'DENVER'); CALL insert_two_rows(); ROLLBACK;
Запрос для отображения конечного состояния таблицы:
-- Display the final state of the table after transactions test=# SELECT * FROM dept; deptno | dname | loc --------+------------+------------- 10 | ACCOUNTING | NEW YORK 20 | RESEARCH | DALLAS 30 | SALES | CHICAGO 40 | OPERATIONS | BOSTON 60 | FINANCE | CHICAGO 70 | MARKETING | LOS ANGELES (6 rows)
Ключевые моменты:
Процедура insert_two_rows использует директиву
PRAGMA AUTONOMOUS_TRANSACTION
, позволяющую ей выполнять транзакции независимо от основной транзакции.Внутри insert_two_rows происходит вставка записи 60 и вызов insert_dept_70.
Процедура insert_two_rows заканчивается с COMMIT, сохраняя изменения, внесенные внутри ее
autonomous transaction
.Основная транзакция выполняет ROLLBACK, отменяя вставку записи 50, но не влияя на
autonomous transaction
.
Результат: Записи 60 и 70 сохраняются в таблице, но не 50.
16.3.3. Автономная транзакция внутри анонимного блока с COMMIT, включая процедуру с ROLLBACK
Создание процедуры для вставки отдела 70:
-- Creating a procedure to insert department 70 into the dept table CREATE OR REPLACE PROCEDURE insert_dept_70() LANGUAGE plpgsql AS $$ BEGIN INSERT INTO dept VALUES (70,'MARKETING','LOS ANGELES'); END; $$;
Создание процедуры с Autonomous Transaction
и откатом:
-- Creating a procedure with an autonomous transaction that inserts and then rolls back CREATE OR REPLACE PROCEDURE insert_to_values() LANGUAGE plpgsql AS $$ DECLARE PRAGMA AUTONOMOUS_TRANSACTION; BEGIN START TRANSACTION; INSERT INTO dept VALUES (60,'FINANCE','CHICAGO'); CALL insert_dept_70(); ROLLBACK; END; $$;
Выполнение основной транзакции и фиксация:
-- Executing main transaction that inserts a record and calls the insert_to_values procedure START TRANSACTION; INSERT INTO dept VALUES (50,'HR','DENVER'); CALL insert_to_values(); COMMIT;
Отобразить конечное состояние таблицы:
-- Display the final state of the dept table after transactions test=# SELECT * FROM dept; deptno | dname | loc --------+------------+------------- 10 | ACCOUNTING | NEW YORK 20 | RESEARCH | DALLAS 30 | SALES | CHICAGO 40 | OPERATIONS | BOSTON 50 | HR | DENVER 70 | MARKETING | LOS ANGELES (6 rows)
Ключевые моменты:
Процедура insert_to_values также использует
PRAGMA AUTONOMOUS_TRANSACTION
, обеспечивая автономию транзакции.Внутри insert_to_values происходит вставка записи 60, вызов insert_dept_70, за которым следует ROLLBACK, отменяющий все изменения внутри этой процедуры.
Основная транзакция завершается с COMMIT, сохраняя вставку записи 50.
Результат: Записи 50 и 70 сохраняются в таблице, но не 60.
Директива PRAGMA AUTONOMOUS_TRANSACTION
позволяет использовать autonomous transactions
внутри процедур, делая эти транзакции независимыми от основных или внешних транзакций. Таким образом, действия, такие как COMMIT или ROLLBACK в autonomous transaction
, не влияют на основную транзакцию и наоборот.
16.3.4. Автономные транзакции в триггерных функциях
Создание схемы и таблиц для аудита действий пользователей:
-- Setup for user activity auditing: schema and related tables CREATE SCHEMA audtrig; CREATE TABLE audtrig.user_activity ( id INT, user_name VARCHAR ); CREATE TABLE audtrig.log ( log_time timestamp, user_name varchar, event varchar );
Создание функции журналирования с Autonomous Transaction
:
-- Defining a function to autonomously log operations on user_activity CREATE OR REPLACE FUNCTION audtrig.log_func() RETURNS TRIGGER LANGUAGE plpgsql AS $$ DECLARE PRAGMA AUTONOMOUS_TRANSACTION; v_action VARCHAR; BEGIN IF (TG_OP = 'INSERT') THEN v_action := 'Added id=' || NEW.id::text; ELSIF (TG_OP = 'UPDATE') THEN v_action := 'Updated id=' || NEW.id::text; ELSIF (TG_OP = 'DELETE') THEN v_action := 'Deleted id=' || OLD.id::text; END IF; INSERT INTO audtrig.log VALUES (now(), current_user, v_action); RETURN NEW; END; $$;
Создание триггера для регистрации выполнения функции:
-- Trigger to capture and log changes in the user_activity table CREATE OR REPLACE TRIGGER audtrig_trig_func AFTER INSERT OR UPDATE OR DELETE ON audtrig.user_activity FOR EACH ROW EXECUTE PROCEDURE audtrig.log_func(); SELECT * FROM audtrig.log; SELECT * FROM audtrig.user_activity;
Выполнение операций с данными и их регистрация:
-- Inserting and deleting data in user_activity, and checking the resulting logs INSERT INTO audtrig.user_activity VALUES (9001,'SMITH'); INSERT INTO audtrig.user_activity VALUES (9002,'JONES'); DELETE FROM audtrig.user_activity WHERE id=9001; SELECT * FROM audtrig.log; SELECT * FROM audtrig.user_activity;
Тестирование отката транзакции и его влияние на журналы:
-- Demonstrating transaction rollback and its impact on logs TRUNCATE audtrig.log; TRUNCATE audtrig.user_activity; START TRANSACTION; INSERT INTO audtrig.user_activity VALUES (9001,'SMITH'); INSERT INTO audtrig.user_activity VALUES (9002,'JONES'); DELETE FROM audtrig.user_activity WHERE id=9001; ROLLBACK; SELECT * FROM audtrig.log; SELECT * FROM audtrig.user_activity;
Очистка: Удаление таблиц и схемы:
-- Cleaning up: Dropping the log and user_activity tables and the audtrig schema DROP TABLE audtrig.log; DROP TABLE audtrig.user_activity;
Операции и Результаты:
В таблицу user_activity вставлены две записи с идентификаторами 9001 и 9002.
Запись с идентификатором 9001 затем удаляется.
Путем выполнения запроса к таблице журнала можно увидеть журнал этих операций.
Таблицы log и user_activity затем обрезаются (очищаются).
Начинается новая транзакция, в которой в таблицу user_activity вставляются две записи, а затем удаляется запись с ID 9001. Однако, эта транзакция откатывается, поэтому изменения не сохраняются.
При выполнении запроса к таблице журнала мы все еще видим журналы операций, выполненных внутри откатываемой транзакции. Обратите внимание, что несмотря на то, что изменения в данных были отменены при откате, данные в журналах остались.