我正在使用postgres创建一个用户表。我的表格如下......
create table if not exists "user" (
user_id bigserial primary key,
username varchar(20) not null,
"password" varchar(100) not null,
created_datetime timestamp default (now() at time zone 'utc')
);
即使插入脚本尝试将时间戳插入created_datetime,create_datetime是否也可以仅使用默认列?如果插入脚本试图将时间戳插入create_datetime列,postgres甚至会抛出错误。
答案 0 :(得分:2)
创建一个触发器,填充该列,忽略INSERT
中提供的任何值:
create or replace function t_user_created_datetime() returns trigger as $$
begin
new.created_datetime := now() at time zone 'utc';
return new;
end;
$$ language plpgsql;
create trigger t_user_created_datetime
before insert on "user"
for each row execute procedure t_user_created_datetime();
检查:
test=# insert into "user"(user_id, username, "password", created_datetime) values(1, 'test', 'test', '1900-01-01'::timestamp);
INSERT 0 1
test=# select * from "user";
user_id | username | password | created_datetime
---------+----------+----------+----------------------------
1 | test | test | 2017-07-27 18:21:24.501701
(1 row)
使用此类触发器,您可以从表格的定义中移除default (now() at time zone 'utc')
,因为它变得无用。
如果您希望在INSERT
中明确设置列的值时看到错误,请将触发器功能更改为以下内容:
create or replace function t_user_created_datetime() returns trigger as $$
begin
if new.created_datetime is not null then
raise exception 'It is forbidden to explicitly set "created_datetime" during INSERT to "user" table.';
end if;
new.created_datetime := now() at time zone 'utc';
return new;
end;
$$ language plpgsql;
在这种情况下,created_datetime
列不得 default
因为否则你总会看到这个错误。
P.S。我强烈建议考虑使用timestamptz
- 它也是8个字节,如timestamp
,但如果您需要(或将来需要)处理多个时区,则可以省去很多工作。< / p>
答案 1 :(得分:2)
Revoke
来自所有角色的表的插入和更新权限:
revoke insert, update
on table "user"
from public, application_role
cascade
然后grant
仅对其他列的应用程序角色执行插入和更新权限:
grant
insert (user_id, username, "password"),
update (username, "password")
on table "user"
to application_role
只有表所有者才能在created_datetime
列上插入和更新。
答案 2 :(得分:1)
完成答案集。使用rules。
首先,我们将创建服务函数,以便能够从SQL语句中引发异常:
create or replace function public.fn_raise(msg text, cond boolean = true)
returns bool
immutable
language plpgsql
as $$
begin
if cond then
raise exception '%', msg;
end if;
return false;
end $$;
接下来让我们创建测试表:
create table t(i int, d timestamptz not null default current_timestamp);
最后,规则:
create or replace rule rul_t_insert as on insert to t
where new.d <> current_timestamp
do also
select fn_raise(format('Can not insert %s into table t', new.d), new.d <> current_timestamp);
让我们测试一下:
postgres=# insert into t(i) values(1) returning *;
┌───┬───────────────────────────────┐
│ i │ d │
╞═══╪═══════════════════════════════╡
│ 1 │ 2017-07-28 12:31:37.255392+03 │
└───┴───────────────────────────────┘
postgres=# insert into t(i,d) values(1,null) returning *;
ERROR: null value in column "d" violates not-null constraint
DETAIL: Failing row contains (1, null).
postgres=# insert into t(i,d) values(2,'2000-10-10') returning *;
ERROR: Can not insert 2000-10-10 00:00:00+03 into table t
我提到的问题只是insert
,但如果您还想阻止此字段的更新,则可以创建另一个规则:
create or replace rule rul_t_update as on update to t
where new.d <> old.d
do also
select fn_raise(format('Can not change t.d to %s', new.d), new.d <> old.d);
测试:
postgres=# update t set i = 3 where i = 1 returning *;
┌───┬───────────────────────────────┐
│ i │ d │
╞═══╪═══════════════════════════════╡
│ 3 │ 2017-07-28 12:31:37.255392+03 │
└───┴───────────────────────────────┘
postgres=# update t set i = 4, d = current_timestamp where i = 3 returning *;
ERROR: Can not change t.d to 2017-07-28 12:39:18.963852+03