我有一个用户表,例如:
create table "user" (
id serial primary key,
name text not null,
superuser boolean not null default false
);
以及包含作业的表格:
create table job (
id serial primary key,
description text
);
可以将作业分配给用户,但仅限于超级用户。其他用户无法分配作业。
所以我有一张表,我可以看到哪个作业被分配给哪个用户:
create table user_has_job (
user_id integer references "user"(id),
job_id integer references job(id),
constraint user_has_job_pk PRIMARY KEY (user_id, job_id)
);
但我想创建一个检查约束,user_id
引用具有user.superuser = True
的用户。
这可能吗?还是有另一种解决方案吗?
答案 0 :(得分:12)
这适用于INSERTS:
create or replace function is_superuser(int) returns boolean as $$
select exists (
select 1
from "user"
where id = $1
and superuser = true
);
$$ language sql;
然后在user_has_job表上检查约束:
create table user_has_job (
user_id integer references "user"(id),
job_id integer references job(id),
constraint user_has_job_pk PRIMARY KEY (user_id, job_id),
constraint chk_is_superuser check (is_superuser(user_id))
);
适用于插页:
postgres=# insert into "user" (name,superuser) values ('name1',false);
INSERT 0 1
postgres=# insert into "user" (name,superuser) values ('name2',true);
INSERT 0 1
postgres=# insert into job (description) values ('test');
INSERT 0 1
postgres=# insert into user_has_job (user_id,job_id) values (1,1);
ERROR: new row for relation "user_has_job" violates check constraint "chk_is_superuser"
DETAIL: Failing row contains (1, 1).
postgres=# insert into user_has_job (user_id,job_id) values (2,1);
INSERT 0 1
然而这是可能的:
postgres=# update "user" set superuser=false;
UPDATE 2
因此,如果您允许更新用户,则需要在users表上创建更新触发器,以防止用户有作业。
答案 1 :(得分:3)
我能想到的唯一方法是在(id, superuser)
表上添加一个唯一约束,并通过“复制”users
从user_has_job
表中引用该约束。那边标志:
superuser
由于create table users (
id serial primary key,
name text not null,
superuser boolean not null default false
);
-- as id is already unique there is no harm adding this additional
-- unique constraint (from a business perspective)
alter table users add constraint uc_users unique (id, superuser);
create table job (
id serial primary key,
description text
);
create table user_has_job (
user_id integer references users (id),
-- we need a column in order to be able to reference the unique constraint in users
-- the check constraint ensures we only reference superuser
superuser boolean not null default true check (superuser),
job_id integer references job(id),
constraint user_has_job_pk PRIMARY KEY (user_id, job_id),
foreign key (user_id, superuser) references users (id, superuser)
);
insert into users
(id, name, superuser)
values
(1, 'arthur', false),
(2, 'ford', true);
insert into job
(id, description)
values
(1, 'foo'),
(2, 'bar');
值,您在插入default
表时无需指定superuser
列。所以下面的插入有效:
user_has_job
但是试图将亚瑟插入表中失败了:
insert into user_has_job
(user_id, job_id)
values
(2, 1);
这也可以防止将福特变成非超级用户。以下更新:
insert into user_has_job
(user_id, job_id)
values
(1, 1);
失败并显示错误
错误:更新或删除表“user”违反表“user_has_job”上的外键约束“user_has_job_user_id_fkey1”
细节:密钥(id,超级用户)=(2,t)仍然从表“user_has_job”引用。
答案 2 :(得分:1)
创建一个单独的superuser
表,该表继承自user
表:
CREATE TABLE "user" (
id serial PRIMARY KEY,
name text NOT NULL,
);
CREATE TABLE superuser () INHERITS ("user");
然后user_has_job
表可以引用superuser
表:
CREATE TABLE user_has_job (
user_id integer REFERENCES superuser (id),
job_id integer REFERENCES job(id),
PRIMARY KEY (user_id, job_id)
);
根据需要通过插入和删除在表之间移动用户:
WITH promoted_user AS (
DELETE FROM "user" WHERE id = 1 RETURNING *
) INSERT INTO superuser (id, name) SELECT id, name FROM promoted_user;
答案 3 :(得分:0)
我不知道这是否是一个很好的方法,但似乎有效
INSERT INTO user_has_job (user_id, job_id) VALUES (you_user_id, your_job_id)
WHERE EXIST (
SELECT * FROM user WHERE id=your_user_id AND superuser=true
);