Надеюсь, простой вопрос, но тот, за который я не нашел подходящего ответа. Я уверенно информирован о том, что хранимые процедуры (пользовательские функции DB) в 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 выполняется без предварительная СТАВКА НАЧАЛА или НАЧАТЬ, это будет казаться недействительным, поскольку транзакция будет немедленно завершена.
Что оставляет меня озадаченным, так как если я назову одиночный оператор SELECT add_new_row('foo');
, который я ожидал бы (если бы я не отключил autocommit), 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?
Я поклонник стандартов и документированных лучших практик, поэтому любые достойные ссылки будут оценены.