背景
在 PostgreSQL 9.0 数据库中,存在具有多对多关系的各种表。这些关系的数量必须受到限制。几个示例表包括:
CREATE TABLE authentication (
id bigserial NOT NULL, -- Primary key
cookie character varying(64) NOT NULL, -- Authenticates the user with a cookie
ip_address character varying(40) NOT NULL -- Device IP address (IPv6-friendly)
)
CREATE TABLE tag_comment (
id bigserial NOT NULL, -- Primary key
comment_id bigint, -- Foreign key to the comment table
tag_name_id bigint -- Foreign key to the tag name table
)
但是,不同的关系有不同的限制。例如,在 authentication
表中,给定的 ip_address
允许 1024 个 cookie
值;而在 tag_comment
表中,每个 comment_id
可以有 10 个关联的 tag_name_id
。
问题
目前,许多函数都对这些限制进行了硬编码;将限制分散到整个数据库中,并防止它们被动态更改。
问题
如何以通用方式对表施加最大多对多关系限制?
想法
创建一个表来跟踪限制:
CREATE TABLE imposed_maximums (
id serial NOT NULL,
table_name character varying(128) NOT NULL,
column_group character varying(128) NOT NULL,
column_count character varying(128) NOT NULL,
max_size INTEGER
)
建立限制:
INSERT INTO imposed_maximums
(table_name, column_group, column_count, max_size) VALUES
('authentication', 'ip_address', 'cookie', 1024);
INSERT INTO imposed_maximums
(table_name, column_group, column_count, max_size) VALUES
('tag_comment', 'comment_id', 'tag_id', 10);
创建触发函数:
CREATE OR REPLACE FUNCTION impose_maximum()
RETURNS trigger AS
$BODY$
BEGIN
-- Join this up with imposed_maximums somehow?
select
count(1)
from
-- the table name
where
-- the group column = NEW value to INSERT;
RETURN NEW;
END;
将触发器附加到每个表:
CREATE TRIGGER trigger_authentication_impose_maximum
BEFORE INSERT
ON authentication
FOR EACH ROW
EXECUTE PROCEDURE impose_maximum();
显然它不会像写的那样工作......有没有办法让它工作,或者以其他方式强制执行限制,例如:
- 在一个位置;和
- 没有硬编码?
谢谢!
最佳答案
我一直在做类似类型的通用触发器。
最棘手的部分是根据列名获取 NEW
记录中的值条目。
我的做法是:
- 将
NEW
数据转换成数组; - 找到该列的
attnum
并将其用作数组的索引。
这种方法只要数据中没有逗号就有效 :( 我不知道如何转换 NEW
或 OLD 的其他方法
变量到值数组中。
以下函数可能会有所帮助:
CREATE OR REPLACE FUNCTION impose_maximum() RETURNS trigger AS $impose_maximum$
DECLARE
_sql text;
_cnt int8;
_vals text[];
_anum int4;
_im record;
BEGIN
_vals := string_to_array(translate(trim(NEW::text), '()', ''), ',');
FOR _im IN SELECT * FROM imposed_maximums WHERE table_name = TG_TABLE_NAME LOOP
SELECT attnum INTO _anum FROM pg_catalog.pg_attribute a
JOIN pg_catalog.pg_class t ON t.oid = a.attrelid
WHERE t.relkind = 'r' AND t.relname = TG_TABLE_NAME
AND NOT a.attisdropped AND a.attname = _im.column_group;
_sql := 'SELECT count('||quote_ident(_im.column_count)||')'||
' FROM '||quote_ident(_im.table_name)||
' WHERE '||quote_ident(_im.column_group)||' = $1';
EXECUTE _sql INTO _cnt USING _vals[_anum];
IF _cnt > CAST(_im.max_size AS int8) THEN
RAISE EXCEPTION 'Maximum of % hit for column % in table %(%=%)',
_im.max_size, _im.column_count,
_im.table_name, _im.column_group, _vals[_anum];
END IF;
END LOOP;
RETURN NEW;
END; $impose_maximum$ LANGUAGE plpgsql;
此函数将检查为给定表定义的所有条件。
关于postgresql - 根据计数限制插入的通用触发器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11199125/