Надеюсь, простой вопрос, но на который я так и не нашел достойного ответа.Я достоверно проинформирован о том, что хранимые процедуры (определяемые пользователем функции БД) в PostgreSQL (в частности, версия 9.0.4) по своей сути транзакционные, поскольку они вызываются через оператор SELECT, который сам по себе является транзакцией.Так как же выбрать уровень изоляции хранимой процедуры?Я полагаю, что в других СУБД требуемый транзакционный блок будет заключен в блок START TRANSACTION, для которого требуемый уровень изоляции является необязательным параметром.
В качестве конкретного выдуманного примера, скажем, я хочу сделать это:
CREATE FUNCTION add_new_row(rowtext TEXT)
RETURNS VOID AS
$$
BEGIN
INSERT INTO data_table VALUES (rowtext);
UPDATE row_counts_table SET count=count+1;
END;
$$
LANGUAGE plpgsql
SECURITY DEFINER;
И представьте, что я хочу убедиться, что эта функция всегда выполняется как сериализуемая транзакция (да, да, PostgreSQL SERIALIZABLE некорректно сериализуем, но это не главное).Я не хочу требовать, чтобы он назывался
START TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT add_new_row('foo');
COMMIT;
Итак, как мне ввести требуемый уровень изоляции в функцию?Я считаю, что не могу просто указать уровень изоляции в операторе BEGIN
, так как в руководстве написано
Важно не путать использование BEGIN / END для группировки операторовв PL / pgSQL с одноименными командами SQL для управления транзакциями.PL / pgSQL BEGIN / END предназначены только для группировки;они не начинают и не заканчивают транзакцию.Функции и процедуры триггера всегда выполняются в транзакции, установленной внешним запросом - они не могут запустить или зафиксировать эту транзакцию, поскольку не было бы контекста, в котором они могли бы выполняться.
Наиболее очевидный подход кЯ бы хотел использовать SET TRANSACTION
где-нибудь в определении функции, например:
CREATE FUNCTION add_new_row(rowtext TEXT)
RETURNS VOID AS
$$
BEGIN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
INSERT INTO data_table VALUES (rowtext);
UPDATE row_counts_table SET count=count+1;
END;
$$
LANGUAGE plpgsql
SECURITY DEFINER;
Хотя это и будет принято, неясно, на что я могу положиться, чтобы это работало.Документация для SET TRANSACTION
гласит:
Если SET TRANSACTION выполняется без предшествующего START TRANSACTION или BEGIN, это, похоже, не будет иметь никакого эффекта, поскольку транзакция немедленно завершится.
Что вызывает у меня недоумение, поскольку, если я вызову одиночный оператор SELECT add_new_row('foo');
, я ожидаю (при условии, что я не отключил автокоммит) SELECT будет запущен как однострочная транзакция с сеансомуровень изоляции по умолчанию.
В руководстве также сказано:
Уровень изоляции транзакции не может быть изменен после первого запроса или оператора изменения данных (SELECT, INSERT, DELETE, UPDATE, FETCH или COPY) транзакции были выполнены.
Так что же происходит, если функция вызывается из транзакции с более низким уровнем изоляции, например:
START TRANSACTION ISOLATION LEVEL READ COMMITTED;
UPDATE row_counts_table SET count=0;
SELECT add_new_row('foo');
COMMIT;
Для дополнительного вопроса: имеет ли значение язык функции?Можно ли установить уровень изоляции в PL / pgSQL иначе, чем в обычном SQL?
Я фанат стандартов и документированных лучших практик, поэтому любые достойные ссылки будут оценены.