Являются ли функции PostgreSQL транзакционными?

Является ли функция PostgreSQL, такая как автоматическая транзакция?

CREATE OR REPLACE FUNCTION refresh_materialized_view(name)
  RETURNS integer AS
$BODY$
 DECLARE
     _table_name ALIAS FOR $1;
     _entry materialized_views%ROWTYPE;
     _result INT;
 BEGIN          

     EXECUTE 'TRUNCATE TABLE ' || _table_name;

     UPDATE materialized_views
     SET    last_refresh = CURRENT_TIMESTAMP
     WHERE  table_name = _table_name;

     RETURN 1;
END
$BODY$
  LANGUAGE plpgsql VOLATILE SECURITY DEFINER;


Другими словами, если во время выполнения функции возникает ошибка, будут ли отменены какие-либо изменения? Если это не поведение по умолчанию, как я могу сделать функцию транзакционной?

Ответ 1

Обновление PostgreSQL 12: ограниченная поддержка PROCEDURE верхнего уровня, которая может управлять транзакциями. Вы по-прежнему не можете управлять транзакциями в обычных вызываемых SQL-функциях, поэтому приведенное ниже остается верным, за исключением случаев использования новых процедур верхнего уровня.


Функции являются частью транзакции, из которой они вызваны. Их эффекты откатываются, если транзакция откатывается. Их работа фиксируется, если транзакция фиксируется. Любые блоки BEGIN... EXCEPT внутри функции работают подобно (и скрытно используются) точкам сохранения, таким как операторы SQL SAVEPOINT и ROLLBACK TO SAVEPOINT.

Функция либо завершается успешно, либо завершается с ошибкой целиком, за исключением обработки ошибок BEGIN... EXCEPT. Если в функции возникает ошибка, которая не обрабатывается, транзакция, вызывающая функцию, прерывается. Прерванные транзакции не могут зафиксировать, и если они пытаются зафиксировать, COMMIT обрабатывается как ROLLBACK, так же, как и для любой другой транзакции с ошибкой. Заметим:

regress=# BEGIN;
BEGIN
regress=# SELECT 1/0;
ERROR:  division by zero
regress=# COMMIT;
ROLLBACK

Посмотрите, как транзакция, которая находится в состоянии ошибки из-за нулевого деления, откатывается на COMMIT?

Если вы вызываете функцию без явной окружающей транзакции, правила точно такие же, как и для любого другого оператора Pg:

BEGIN;
SELECT refresh_materialized_view(name);
COMMIT;

(где COMMIT потерпит неудачу, если SELECT вызвал ошибку).

PostgreSQL (пока) не поддерживает автономные транзакции в функциях, где процедура/функция может фиксировать/откатывать независимо от вызывающей транзакции. Это может быть смоделировано с использованием нового сеанса через dblink.

НО, вещи, которые не являются транзакционными или несовершенно транзакционными, существуют в PostgreSQL. Если он имеет нетранзакционное поведение в обычном режиме BEGIN; do stuff; COMMIT; BEGIN; do stuff; COMMIT; блок, он также имеет нетранзакционное поведение в функции. Например, nextval и setval, TRUNCATE и т.д.

Ответ 2

Поскольку мои знания PostgreSQL менее глубоки, чем у Крейга Рингера, я постараюсь дать более короткий ответ: Да.

Если вы выполняете функцию с ошибкой, ни один из шагов не повлияет на базу данных.

Также, если вы выполняете запрос в PgAdmin то же самое происходит.

Например, если вы выполняете запрос:

update your_table yt set column1 = 10 where yt.id=20;

select anything_that_do_not_exists;

Обновление в строке id = 20 your_table не будет сохранено в базе данных.

ОБНОВЛЕНИЕ сентябрь - 2018

Чтобы прояснить концепцию, я сделал небольшой пример с нетранзакционной функцией nextval.

Сначала давайте создадим последовательность:

create sequence test_sequence start 100;

Тогда давайте выполним:

update your_table yt set column1 = 10 where yt.id=20; select nextval('test_sequence'); select anything_that_do_not_exists;

Теперь, если мы откроем еще один запрос и выполните

select nextval('test_sequence');

Мы получим 101, потому что первое значение (100) использовалось в последнем запросе (то есть, потому что последовательности не являются транзакционными), хотя обновление не было зафиксировано.

Ответ 3

На уровне функции он не является транснациональным. Другими словами, каждый оператор в функции принадлежит одной транзакции, которая является значением автоматической фиксации db по умолчанию. Автообложение по умолчанию истинно. Но в любом случае вам нужно вызвать функцию, используя

select schemaName.functionName()

Вышеуказанный оператор "select schemaName.functionName()" представляет собой единую транзакцию, пусть именует транзакцию T1, и поэтому все утверждения в функции принадлежат транзакции T1. Таким образом, функция находится в одной транзакции.

Ответ 4

https://www.postgresql.org/docs/current/static/plpgsql-structure.html

Важно не путать использование BEGIN/END для группировки операторов в PL/pgSQL с помощью так называемых SQL-команд для управления транзакциями. PL/pgSQL BEGIN/END предназначены только для группировки; они не запускают и не завершают транзакцию. Функции и триггерные процедуры всегда выполняются в транзакции, установленной внешним запросом - они не могут запускать или фиксировать эту транзакцию, поскольку для них не будет никакого контекста. Однако блок, содержащий предложение EXCEPTION, фактически формирует субтранзакцию, которая может откатываться, не затрагивая внешнюю транзакцию. Подробнее об этом см. В разделе 39.6.6.