Предыдущая тема :: Следующая тема |
Автор |
Сообщение |
=Fluf=
Зарегистрирован: 17.11.2005 Сообщения: 4
|
Добавлено: Вс Мар 25 2007 18:53 Заголовок сообщения: pl/pgsql, тревиальная задача |
|
|
В m$sql есть функция nest_level(). она выдаёт уровень вложенности кода.
Я хочу решить тривиальную задачу на PostgreSQL.
Есть таблица tb0. на ней висит триггер tr0, который ведёт на триггерную функцию tf0. она добавляет некую строку в другую таблицу tb1 и возвращает NEW.
В целях соблюдения правила целостности данных, я хочу запритить инсерты, апдейты и делеты в таблицу tb1 со всех мест, кроме триггерной функции tf0, или других триггерных функций или процедур.
В m$ можно было бы повелить на эту таблицу tb1 триггер tr1 и триггерную функцию tf1, которая при срабатывании смотрела бы уровень вложенности когда. Так, если встретиться insetr into tb1 в триггерной функции tf0, то при выполнении функции nest_level() в триггерной функции tf1 мы получим 2. (Сам не пробавал, но так в документации написано =) m$ неузаю... ) ) А если бы исерт был напрямую из запроса, в таблицу tb1, то nest_level() в tf1 выдал бы 1.
Мне надо сделать нечто подобное в pgsql. А именно мне надо из кода в tf1 как либо узнать, был ли запущен триггер tr1 внешним запросом или он сработал из-за инсерта/апдейта/делета, совершённого из другой триггерной функции/хранимой процедуры.
====== Сам вопрос ========
Я уверен, что все вы решали эту задачу, но возможно совершенно другим потём. Материализую задание для понимания.
Это банальное написание лога.
Предположим есть таблица main и log. При добавлении в строки в main, автоматически заноситься запись в таблицу log, что такой то юзведь добавил строку в main. Если удалить строку из main - она удалиться, и в log автоматически запишеться, что такой то юзверь удалил строку из main.
Если кто то сделает инсерт в таблицу log о том, что некая строка в main была удалена, а на самом то деле её никто не удалял - получиться нарушение целостности данных.
Как это предотвратить?? |
|
Вернуться к началу |
|
|
критикан
Зарегистрирован: 18.02.2005 Сообщения: 247
|
Добавлено: Вт Апр 17 2007 10:41 Заголовок сообщения: зачем слону два хвоста? |
|
|
есть определённые сомнения в необходимости учёта уровня вложенности для контроля целостности. относительно вопроса о соответствии таблицы и лога: если в логе появятся подряд идущие записи "insert" и "delete", является ли это нарушением целостности? думаю -- нет. поэтому исходный вопрос относится не целостности базы данных, а к достоверности информации в ней, а это решается цифровой подписью, а не уровнем вложенности триггера
----------------
зачем слону два хвоста? |
|
Вернуться к началу |
|
|
=Fluf=
Зарегистрирован: 17.11.2005 Сообщения: 4
|
Добавлено: Вт Апр 17 2007 20:30 Заголовок сообщения: |
|
|
Хм, ну под целостностью данных в БД как раз и подразумевается непротипоречивость данных в разных частях БД (в разных таблицах). О достоверности речи не идёт.
Черь идёт о возможности грубой ошибки работы администратора (последующего суппорта).
Приведу наиболее простой пример.
В моей БД на некоторые таблицы вообще нельзя делать delete. В таблицы содержатся некие диапазоны бланков. А так же логин того, кто сделал запись. Удаление записи делается insert-ом, и вводить надо тот же диапазон, но с отрицательным знаком, атак же логин и пароль. Триггер проверяет логин и пароль, а затем делает delete соответствующего диапазона. Пользователю, а он же нерадивый администратор, делать delete самому нельзя.
Как мне поставить такой запрет?
Слишком долго расказывать все подробности моего приложения. Но суть такова - мне надо, что бы из некой таблицы всякий триггер пог сделать delete или update. А пользователь - нет. |
|
Вернуться к началу |
|
|
критикан
Зарегистрирован: 18.02.2005 Сообщения: 247
|
Добавлено: Пт Апр 20 2007 16:57 Заголовок сообщения: Танк запросто вспашет поле, но зачем трактористу танк? |
|
|
Под "целостностью" я тоже понимаю "непротипоречивость", а под "достоверностью" я понимаю уверенность в том, что "данное действие было произведено в данный момент времени данным пользователем с данным информативным смыслом".
Пример следующий: пусть есть таблица-инфо, содержащая ссылку на таблицу-справочник, предназначенный именно для таблицы-инфо.
структура таблицы-инфо: код, количество в штуках
структура таблицы-справочника: код, наименование
ситуация, когда имеет место:
-- целостность+достоверность (всё хорошо):
содержимое таблицы-инфо: 5, 15
содержимое таблицы-справочника: 5, 'стул'
-- нецелостность+достоверность (в справочнике отсутствует запись с кодом 5):
содержимое таблицы-инфо: 5, 15
содержимое таблицы-справочника: 4, 'стул'
-- целостность+недостоверность (воздух в штуках не измеряют, но СУБД об этом не знает, поэтому целостность не нарушена, а для потребителей это бессмыслица, поэтому информация недостоверна):
содержимое таблицы-инфо: 5, 15
содержимое таблицы-справочника: 5, 'воздух'
-- нецелостность+недостоверность (всё плохо):
содержимое таблицы-инфо: 5, 15
содержимое таблицы-справочника: 4, 'воздух'
Что касается вопроса о запрете удаления записей, то эта задача решается разделением функции администрирования на "администратора безопасности" и "администратора содержимого". "Администратору содержимого" (он же нерадивый администратор) ставят запрет на, в частности, прямое управление записями, а дают права на специальные процедуры, предназначенные для администрирования содержимого. Сами процедуры, естественно, должны быть сделаны так, чтобы не допускать нарушений безопасности -- в данном случае они должны вместо удаления выполнять ввод записей с отрицательным знаком.
------------------
Танк запросто вспашет поле, но зачем трактористу танк? |
|
Вернуться к началу |
|
|
=Fluf=
Зарегистрирован: 17.11.2005 Сообщения: 4
|
Добавлено: Пт Апр 20 2007 21:10 Заголовок сообщения: |
|
|
@@@@@
"Администратору содержимого" (он же нерадивый администратор) ставят запрет на, в частности, прямое управление записями, а дают права на специальные процедуры, предназначенные для администрирования содержимого
@@@@@
Так в этом то весь и вопрос - как это сделать??! С другими БД не было вопросов, но как это сделать менно в PostgreSQL??!! Ведь привелегии тут unix-образные, т.е. если пользователь не может делать делете из таблицы, то и запускаемая от его имени хранимаю процедура этот делет сделать тоже не сможет. Вот я сейчас написал таблицу, хранимую процедуру (только взял не делет а инсерт), запрос и вывод (запрос выполнен от пользователя 'math_user'):
######################
############## Таблица:
######################
CREATE TABLE math.qwe
(
q boolean NOT NULL,
CONSTRAINT qwe_pkey PRIMARY KEY (q)
)
WITHOUT OIDS;
ALTER TABLE math.qwe OWNER TO postgres;
#######################
####### Хранимая процедура:
#######################
CREATE OR REPLACE FUNCTION math.f_qwe()
RETURNS boolean AS
$BODY$begin
insert into math.qwe values (true);
return null;
end;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
ALTER FUNCTION math.f_qwe() OWNER TO postgres;
GRANT EXECUTE ON FUNCTION math.f_qwe() TO math_user;
####################
####### Запрос и вывод:
####################
select math.f_qwe();
ERROR: permission denied for relation qwe
SQL state: 42501
Context: SQL statement "INSERT INTO math.qwe values (true)"
PL/pgSQL function "f_qwe" line 2 at SQL statement |
|
Вернуться к началу |
|
|
критикан
Зарегистрирован: 18.02.2005 Сообщения: 247
|
Добавлено: Пн Апр 23 2007 09:39 Заголовок сообщения: Если нет сохи, то почему бы не воспользоваться плугом? |
|
|
Я с самим постгрессом не баловался, поэтому сразу не скажу, какие команды нужно дать, а общая идея -- запускать эти спецпроцедуры не от имени ограниченного пользователя (он же нерадивый админ), а от имени пользователя с большими правами. Наверняка, эта возможность имеется, и скорее всего она выглядит так: владельцем спецпроцедуры является пользователь с большими правами, а исполнять её может пользователь с маленькими правами. Во всяком случае, если постгресс придерживается политики юниксов в отношении прав, то в нём реализовано право, аналогичное атрибуту SUID, то есть право исполнения от имени владельца команды, а не от имени запускающего.
Итого: нужно поискать в пострессе опции запуска от имени другого пользователя
----------------
Если нет сохи, то почему бы не воспользоваться плугом? |
|
Вернуться к началу |
|
|
gray_hemp
Зарегистрирован: 13.05.2007 Сообщения: 1
|
Добавлено: Вс Май 13 2007 18:17 Заголовок сообщения: Re: pl/pgsql, тревиальная задача |
|
|
Код: | -- Главная таблица
CREATE TABLE main
(
id serial NOT NULL,
field1 varchar,
CONSTRAINT main_id PRIMARY KEY (id)
)
WITHOUT OIDS;
ALTER TABLE main OWNER TO postgres;
-- Все могут всё
GRANT ALL ON TABLE main TO postgres;
GRANT ALL ON TABLE main TO public;
GRANT ALL ON TABLE main_id_seq TO postgres;
GRANT ALL ON TABLE main_id_seq TO public;
-- Лог для главной таблицы
CREATE TABLE log
(
id int4 NOT NULL,
field1 varchar,
log_op varchar, -- операция (insert/update/delete)
log_id serial NOT NULL,
log_user varchar DEFAULT "session_user"(), -- ! тут обязательно session_user, объясню ниже
log_timestamp timestamp DEFAULT now(),
CONSTRAINT log_pkey PRIMARY KEY (log_id)
)
WITHOUT OIDS;
ALTER TABLE log OWNER TO postgres;
-- postgres может всё, другие только читать
GRANT ALL ON TABLE log TO postgres;
GRANT SELECT ON TABLE log TO public;
GRANT ALL ON TABLE log_log_id_seq TO postgres;
-- Собственно функция логирования, причём логирует только изменённые поля
CREATE OR REPLACE FUNCTION tr_logmain()
RETURNS "trigger" AS
$BODY$declare
changes main%rowtype;
begin
if TG_OP = 'INSERT' then
changes := new; -- при добавлении логируем все поля
elsif TG_OP = 'UPDATE' then
-- при обновлении
changes.id := old.id; -- идентифицирующие логируем всегда
changes.field1 := nullif(new.field1, old.field1); -- для других только изменения
else
changes.id := old.id; -- при удалении только идентифицирующие
end if;
insert into log select changes.*;
update log set log_op = TG_OP where log_id = lastval();
if TG_OP = 'INSERT' or TG_OP = 'UPDATE' then
return new;
else
return null;
end if;
end;$BODY$
LANGUAGE 'plpgsql' VOLATILE SECURITY DEFINER;
-- SECURITY DEFINER - выполняется с привелегиями создателя, т.е. postgres
-- поэтому используем session_user (см. выше)
ALTER FUNCTION tr_logmain() OWNER TO postgres;
CREATE TRIGGER tr_logmain_aiud
AFTER INSERT OR UPDATE OR DELETE
ON main
FOR EACH ROW
EXECUTE PROCEDURE tr_logmain();
-- А теперь тестик (выполнять под обычным юзером)
/*
insert into main (field1) values ('aaa');
update main set field1 = 'bbb';
update main set field1 = 'bbb';
update main set field1 = 'ccc';
delete from main where field1 = 'ccc';
insert into main (field1) values ('ddd');
update main set field1 = 'eee';
update main set field1 = 'fff';
update main set field1 = 'fff';
delete from main where field1 = 'fff';
*/ |
|
|
Вернуться к началу |
|
|
|