通用触发器,用于基于计数限制插入 [英] Generic trigger to restrict insertions based on count
问题描述
在PostgreSQL 9.0数据库中,存在各种具有多对多关系的表.这些关系的数量必须受到限制.几个示例表包括:
In a PostgreSQL 9.0 database, there are various tables that have many-to-many relationships. The number of those relationships must be restricted. A couple of example tables include:
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
s.
Different relationships, however, have different limitations. For example, in the authentication
table, a given ip_address
is allowed 1024 cookie
values; whereas, in the tag_comment
table, each comment_id
can have 10 associated tag_name_id
s.
当前,许多功能已对这些限制进行了硬编码.将限制分散到整个数据库中,并防止它们被动态更改.
Currently, a number of functions have these restrictions hard-coded; scattering the limitations throughout the database, and preventing them from being changed dynamically.
您将如何以通用方式对表施加最大的多对多关系限制?
How would you impose a maximum many-to-many relationship limit on tables in a generic fashion?
创建一个表以跟踪限制:
Create a table to track the limits:
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;
将触发器附加到每个表:
Attach the trigger to every table:
CREATE TRIGGER trigger_authentication_impose_maximum
BEFORE INSERT
ON authentication
FOR EACH ROW
EXECUTE PROCEDURE impose_maximum();
很显然,它不能按书面形式工作...有办法使其起作用,或者以其他方式强制执行以下限制:
Obviously it won't work as written... is there a way to make it work, or otherwise enforce the restrictions such that they are:
- 在单个位置;和
- 不是硬编码的吗?
谢谢!
推荐答案
我一直在做类似类型的通用触发器.
最棘手的部分是根据列名获取NEW
记录中的值条目.
I've been doing a similar type of generic triggers.
The most tricky part is to get the value entry in the NEW
record based on the column name.
我正在通过以下方式进行操作:
I'm doing it the following way:
- 将
NEW
数据转换为数组; - 找到列的
attnum
并将其用作数组的索引.
- convert
NEW
data into array; - find the
attnum
of the column and use it as an index for the array.
只要数据中没有逗号,此方法就有效 :(我不知道如何将NEW
或OLD
变量转换为值数组的其他方法
This approach works as long as there're no commas in the data :( I don't know of other ways how to convert NEW
or OLD
variables into the array of values.
以下功能可能会有所帮助:
The following function might help:
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;
此功能将检查为给定表定义的所有条件.
This function will check for all conditions defined for a given table.
这篇关于通用触发器,用于基于计数限制插入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!