Я хочу знать количество строк, которые будут затронуты UPDATE
запрос в BEFORE
на триггер оператора. Это возможно?
Проблема состоит в том, что я хочу позволить только запросы, которые обновят до 4 строк. Если затронутое количество строк равняется 5 или больше, я хочу повысить ошибку.
Я не хочу делать это в коде, потому что мне нужно, это проверяет уровень дб. Это вообще возможно?
Заранее спасибо за любые подсказки об этом
Я создал что-то вроде этого:
begin;
create table test (
id integer
);
insert into test(id) select generate_series(1,100);
create or replace function trg_check_max_4_updated_records()
returns trigger as $$
declare
counter_ integer := 0;
tablename_ text := 'temptable';
begin
raise notice 'trigger fired';
select count(42) into counter_
from pg_catalog.pg_tables where tablename = tablename_;
if counter_ = 0 then
raise notice 'Creating table %', tablename_;
execute 'create temporary table ' || tablename_ || ' (counter integer) on commit drop';
execute 'insert into ' || tablename_ || ' (counter) values(1)';
execute 'select counter from ' || tablename_ into counter_;
raise notice 'Actual value for counter= [%]', counter_;
else
execute 'select counter from ' || tablename_ into counter_;
execute 'update ' || tablename_ || ' set counter = counter + 1';
raise notice 'updating';
execute 'select counter from ' || tablename_ into counter_;
raise notice 'Actual value for counter= [%]', counter_;
if counter_ > 4 then
raise exception 'Cannot change more than 4 rows in one trancation';
end if;
end if;
return new;
end; $$ language plpgsql;
create trigger trg_bu_test before
update on test
for each row
execute procedure trg_check_max_4_updated_records();
update test set id = 10 where id <= 1;
update test set id = 10 where id <= 2;
update test set id = 10 where id <= 3;
update test set id = 10 where id <= 4;
update test set id = 10 where id <= 5;
rollback;
Основная идея состоит в том, чтобы иметь триггер «перед обновлением для каждой строки», который создает (при необходимости) временную таблицу (которая удаляется в конце транзакции). В этой таблице есть только одна строка с одним значением, то есть количеством обновленных строк в текущей транзакции. Для каждого обновления значение увеличивается. Если значение больше 4, транзакция останавливается.
Но я думаю, что это неправильное решение вашей проблемы. В чем проблема, выполнить такой неправильный запрос, о котором вы писали, дважды, чтобы у вас было изменено 8 строк. А как насчет удаления строк или их усечения?
Попробуйте использовать Serializable Isolation Level. Я считаю, что это даст вам согласованное представление данных базы данных внутри транзакции. Тогда вы сможете использовать вариант №1, который упомянул MusiGenesis, без уязвимости по времени. Конечно, протестируйте его для проверки.
Саймон имел хорошую идею, но его реализация излишне сложна. Это мое предложение:
create or replace function trg_check_max_4()
returns trigger as $$
begin
perform true from pg_class
where relname='check_max_4' and relnamespace=pg_my_temp_schema();
if not FOUND then
create temporary table check_max_4
(value int check (value<=4))
on commit drop;
insert into check_max_4 values (0);
end if;
update check_max_4 set value=value+1;
return new;
end; $$ language plpgsql;
Напишите функцию, которая обновляет строки за вас или выполняет откат. Извините за плохое форматирование стиля.
create function update_max(varchar, int)
RETURNS void AS
$BODY$
DECLARE
sql ALIAS FOR $1;
max ALIAS FOR $2;
rcount INT;
BEGIN
EXECUTE sql;
GET DIAGNOSTICS rcount = ROW_COUNT;
IF rcount > max THEN
--ROLLBACK;
RAISE EXCEPTION 'Too much rows affected (%).', rcount;
END IF;
--COMMIT;
END;
$BODY$ LANGUAGE plpgsql
Затем назовите это как
select update_max('update t1 set id=id+10 where id < 4', 3);
, где первый параметр - это ваш sql-оператор, а второй - максимальное количество строк.
Я никогда не работал с postgresql, поэтому мой ответ может быть неприменим. В SQL Server ваш триггер может вызывать хранимую процедуру, которая будет делать одно из двух:
Нет. 1 является уязвимым по времени (количество записей, затронутых UPDATE, может измениться между проверкой COUNT (*) и фактическим UPDATE. № 2 довольно неэффективен, если есть много случаев, когда количество обновленных строк больше 4.
PostgreSQL имеет два типа триггеров : триггеры строк и операторов. Триггеры строк работают только в контексте строки, поэтому вы не можете их использовать. К сожалению, триггеры оператора «до» не видят , какие изменения должны произойти, поэтому я не верю, что вы можете их использовать.
Исходя из этого, я бы сказал, что вряд ли вы сможете встроить такую защиту в базу данных с помощью триггеров, если только вы не против использования триггера «после» и отката транзакции, если условие не устраивает. Не возражал бы, если бы ошиблись. :)