postgresql - 根据计数限制插入的通用触发器

标签 postgresql triggers insert many-to-many plpgsql

背景

在 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 并将其用作数组的索引。

这种方法只要数据中没有逗号就有效 :( 我不知道如何转换 NEWOLD 的其他方法 变量到值数组中。

以下函数可能会有所帮助:

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/

相关文章:

javascript - Google Sheets onEdit 函数跳转到 A1 单元格

php - 如何将 php 中的多个条目以及静态信息插入表中

python - 如何使用 SQLAlchemy 和 contains_eager 中关系上定义的 order_by?

INSERT INTO 表 SELECT 与 COPY 的 PostgreSQL 性能

sql - 在 postgresql 的列中生成一系列月份

java - 数据映射器模式的数据一致性

sql - 一定数量的记录后,Postgres 服务器性能下降

MySQL 触发器根据插入和存储的值更新表

php - 插入 MySQL 和 PHP

python - MySQL/Python -> 语句中占位符的语法错误?