Триггер PostgreSQL для динамической генерации кодов для нескольких таблиц - PullRequest
3 голосов
/ 18 июня 2011

Я хотел бы сгенерировать коды для многих таблиц в базе данных и прекратил рефакторинг своего решения, когда я был готов написать свою третью реализацию «получить код для таблицы X».

Мой кодэто:

-- Tenants receive a code that's composed of a portion of their subdomain and a unique number.
-- This number comes from this sequence.
CREATE SEQUENCE tenant_codes_seq MAXVALUE 9999 NO CYCLE;

CREATE TABLE tenants (
    subdomain   varchar(36) NOT NULL UNIQUE
  , tenant_code char(8)     NOT NULL UNIQUE
  , PRIMARY KEY (tenant_code)
);

-- This function expects four parameters:
-- 1. The column that's receiving the generated code (RECEIVING_COLUMN_NAME)
-- 2. The column that's used to salt the code (SALT_COLUMN_NAME)
-- 3. The number of characters to use from the salt column (SALT_LENGTH)
-- 4. The sequence name, but defaults to RECEIVING_COLUMN_NAME || 's'
CREATE OR REPLACE FUNCTION generate_table_code() RETURNS trigger AS $$
DECLARE
  receiving_column_name text;
  salt_column_name      text;
  salt_length           text;
  sequence_name         text;
BEGIN
  receiving_column_name := TG_ARGV[0];
  salt_column_name      := TG_ARGV[1];
  salt_length           := TG_ARGV[2];

  CASE
  WHEN TG_NARGS = 3 THEN
    sequence_name := receiving_column_name || 's';
  WHEN TG_NARGS = 4 THEN
    sequence_name := TG_ARGV[3];
  ELSE
    RAISE EXCEPTION '3 or 4 arguments expected, received %', TG_NARGS;
  END CASE;

  -- The intent is to return ABC-0001 when salt_column contains 'ABC'
  EXECUTE 'rpad(substr('                ||
          quote_ident(salt_column_name) ||
          ', 1, 4), 4, '                ||
          quote_literal('-')            ||
          ') || lpad(nextval('          ||
          quote_literal(sequence_name)  ||
          ')::text, '                   ||
          quote_literal(salt_length)    ||
          ', '                          ||
          quote_literal('0')            ||
          ')'
  INTO STRICT NEW;
  RETURN NEW;
END
$$ LANGUAGE plpgsql;

CREATE TRIGGER generate_tenant_code_trig
  BEFORE INSERT ON tenants FOR EACH ROW
  EXECUTE PROCEDURE generate_table_code('tenant_code', 'subdomain', 4);

Как мне присвоить NEW.tenant_code, NEW.user_code или NEW.table_whwhat_code?

Выполнение некоторых тестов приводит к правильному «утверждению», но я не могу показатьсяправильно назначить:

INSERT INTO tenants(subdomain) VALUES ('abc')


CREATE TABLE
ERROR:  syntax error at or near "NEW"
LINE 1: NEW.tenant_code := rpad(substr(subdomain, 1, 4), 4, '-') || ...
        ^
QUERY:  NEW.tenant_code := rpad(substr(subdomain, 1, 4), 4, '-') || lpad(nextval('tenant_codes')::text, '4', '0'::text)
CONTEXT:  PL/pgSQL function "generate_table_code" line 20 at EXECUTE statement

Ответы [ 2 ]

4 голосов
/ 18 июня 2011

Я бы с большим энтузиазмом выглядел неправильно (иногда мне это тоже нужно), но лучше всего я знаю, что обращение к именам столбцов с помощью переменных является одним из тех случаев, когда вам на самом деле нужно использовать триггеры PL / C, а не чем триггеры PL / PgSQL. Вы найдете примеры таких триггеров в contrib / spi и в PGXN.

В качестве альтернативы, назовите ваши столбцы последовательно, чтобы иметь возможность ссылаться на них напрямую, например NEW.tenant_code.

Лично я обычно пишу функцию, которая создает триггер:

create function create_tg_stuff(_table regclass, _args[] text[])
  returns void as $$
begin
  -- explore pg_catalog a bit
  execute $x$
  create function $x$ || quote_ident(_table || '_tg_stuff') || $x$()
    returns trigger as $t$
  begin
    -- more stuff
    return new;
  end;
  $t$ language plpgsql;
  $x$;
end;
$$ language plpgsql;
0 голосов
/ 18 июня 2011

NEW имеет тип RECORD, поэтому вы не можете присвоить этому AFAIK.

Чтобы установить значение столбца, присвойте NEW.column, например:

NEW.tenant_code := (SELECT some_calculation);

Возможно, ваш дизайн слишком сложен;PL / SQL - очень ограниченный язык - постарайтесь сделать свой код максимально простым

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...