通用触发器,用于基于计数限制插入 [英] Generic trigger to restrict insertions based on count

查看:122
本文介绍了通用触发器,用于基于计数限制插入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在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_ids.

当前,许多功能已对这些限制进行了硬编码.将限制分散到整个数据库中,并防止它们被动态更改.

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.

只要数据中没有逗号,此方法就有效 :(我不知道如何将NEWOLD变量转换为值数组的其他方法

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆