PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Acionadores de evento ROLLBACK no postgresql


Você não pode usar uma sequência para isso. Você precisa de um único ponto de serialização por meio do qual todos inserções têm que ir - caso contrário, o atributo "gapless" não pode ser garantido. Você também precisa garantir que nenhuma linha seja excluída dessa tabela.

A serialização também significa que apenas uma única transação pode inserir linhas nessa tabela - todas as outras inserções precisam esperar até que a inserção "anterior" seja confirmada ou revertida.

Um padrão de como isso pode ser implementado é ter uma tabela onde os números da "sequência" são armazenados. Vamos supor que precisamos disso para números de faturas que precisam ser preenchidos por motivos legais.

Então, primeiro criamos a tabela para armazenar o "valor atual":
create table slow_sequence 
(
  seq_name        varchar(100) not null primary key,
  current_value   integer not null default 0
);

-- create a "sequence" for invoices
insert into slow_sequence values ('invoice');

Agora precisamos de uma função que gere o próximo número, mas que garanta que duas transações não possam obter o próximo número ao mesmo tempo.
create or replace function next_number(p_seq_name text)
  returns integer
as
$$
  update slow_sequence
     set current_value = current_value + 1
  where seq_name = p_seq_name
  returning current_value;
$$
language sql;

A função incrementará o contador e retornará o valor incrementado como resultado. Devido à update a linha da sequência agora está bloqueada e nenhuma outra transação pode atualizar esse valor. Se a transação de chamada for revertida, a atualização do contador de sequência também será. Se for confirmado, o novo valor será persistido.

Para garantir que todos transação usa a função, um gatilho deve ser criado.

Crie a tabela em questão:
create table invoice 
(
  invoice_number integer not null primary key, 
  customer_id    integer not null,
  due_date       date not null
);

Agora crie a função de gatilho e o gatilho:
create or replace function f_invoice_trigger()
  returns trigger
as
$$
begin
  -- the number is assigned unconditionally so that this can't 
  -- be prevented by supplying a specific number
  new.invoice_number := next_number('invoice');
  return new;
end;
$$
language plpgsql;

create trigger invoice_trigger
  before insert on invoice
  for each row
  execute procedure f_invoice_trigger();

Agora, se uma transação faz isso:
insert into invoice (customer_id, due_date) 
values (42, date '2015-12-01');

O novo número é gerado. Um segundo A transação precisa esperar até que a primeira inserção seja confirmada ou revertida.

Como eu disse:esta solução não é escalável. De jeito nenhum. Isso tornará seu aplicativo muito lento se houver muitas inserções nessa tabela. Mas você não pode ter os dois:um e escalável implementação correta de uma sequência sem intervalos.

Também tenho certeza de que existem casos extremos que não são cobertos pelo código acima. Portanto, é bem provável que você ainda possa acabar com lacunas.