2009-10-08 47 views
1

我想强制用户指定某个表(sometbl)的更新源,例如 。指定“本地”或“远程”(对于COL2) - 但这一要求应该发生在数据库级别检查时,执行UPDATE语句,以便:PostgreSQL 8.2:要求在UPDATE语句中存在特定的列

UPDATE sometbl SET col1 = 'abc'; 

应该抛出错误(异常),但:

UPDATE sometbl SET col1 = 'abc', col2 = 'remote'; 

...将成功。

我试图为该表创建BEFORE更新触发器,但我无法检查是否显式设置了NEW.col2的 。

我使用状态

IF NEW.col2 IS NULL THEN 
    RAISE EXCEPTION 'you must specify source of this update (local/remote)' 
END IF; 

但每次,当在更新未指定COL2(UPDATE SET sometbl COL1 = 'ABC')

我在NEW.col2得到了字段的当前值伪var,而不是假设NULL。

是否有任何解决方法来防止UPDATING指定的字段在UPDATE stmt中不存在?

回答

0

您可以创建第二个表,其中包含具有ID值的本地和远程条目,然后只需使用第一个表中该表的非空外键即可。

+0

该解决方案将不会在我的情况下工作,因为在第一个表中的行(或多个)具有有效的外键将在更新之前存在,所以即使没有指定外键字段值更新会成功。 – 2009-10-09 13:02:57

0

您可以创建几个存储过程,一拉

create or replace function update_remote(text) returns void 
    as 'update sometbl SET col1 = $1, col2 = ''remote''' 
    language SQL 
    volatile 
    strict; 
+0

这部分解决了我面临的问题 - 强制某人更新表格以明确设置该更新的来源。我的意思是部分因为必须告诉某人使用该过程而不使用直接的UPDATE语句。拥有权限的用户无法做到这一点 - 如果db用户对该函数有EXECUTE,为了通过该函数更新表,他还必须拥有UPDATE权限(这反过来允许在没有必需的字段集的情况下执行UPDATE)。 – 2009-10-09 12:58:21

1

如何约上几个triggers?一个在之前运行更新,并将列设置为空。一个更新和pukes(返回NULL),如果列仍然为空。 (如果触发器返回NULL,则更新失败。)

+0

一个聪明的解决方案。 – Kev 2009-10-09 20:25:26

+0

我会称之为“抓住吸管”。 :) – 2009-10-09 20:49:42

+0

初看起来,这对我来说是很有希望的解决方案......但是,您是否真的试过了这个实例?仍然不适合我。首先AFTER触发器可能会返回NULL,并且UPDATE不会失败,只有当您在AFTER触发器函数的主体中明确地抛出异常时它才会失败。你建议在触发之前将列设置为null - 但是如何?在那个触发器中使用单独的UPDATE stmt?这导致了递归调用相同的触发器,甚至如果这可以避免使用一些有条件的检查 - 不可能找出'col2'是否明确设置为updt stmt,因为我需要 – 2009-10-12 12:01:18

1

我想使用security definer函数,该函数由除admin和/或表所有者之外的唯一用户拥有,并具有sometbl的更新权限。

事情是这样的:

create table sometbl (
    id serial primary key, 
    col1 text, 
    col2 text not null, 
    check (col2 in ('local','remote')) 
); 
create role sometbl_updater; 
grant update on sometbl to sometbl_updater; 

create function update_sometbl(integer, text, text) 
returns void as 
$$ 
    update sometbl set col1=$2, col2=$3 where id=$1; 
$$ security definer volatile language sql; 
alter function update_sometbl(integer, text, text) 
    owner to sometbl_updater; 

但要小心security definer functions security

0

好的,读过文档并试用后,我可以报告一个BEFORE触发器是要走的路。触发器提供名称NEW和OLD绑定到新元组和旧元组。从BEFORE触发器返回NULL可防止更新。因此:

CREATE OR REPLACE FUNCTION prevent_not_changing_col2() 
    RETURNS trigger AS $$ 
begin 
    if NEW.col2 = OLD.col2 then return NULL; end if; 
    return NEW; 
end ; 
$$ LANGUAGE plpgsql; 

CREATE TRIGGER col2_check 
    BEFORE UPDATE 
    ON sometbl 
    FOR EACH ROW 
    EXECUTE PROCEDURE prevent_not_changing_col2(); 
+0

好的,但该解决方案有一个缺点 - 它会阻止UPDATE,其中col2被显式设置为与更新行相同的值,假设sometbl(id,col1,col2)中有行:1,'abc','local',我们尝试“UPDATE sometbl SET col1 ='def',col2 ='local'WHERE id = 1“,那么这个触发器也会阻止我想强制使用所有开发者的更新stmt(他们必须在updt stmts中提供col2,不管if它的价值改变了,或者不)。我对吗 ? – 2009-10-12 15:26:13