sql >> Base de Datos >  >> RDS >> PostgreSQL

Desencadenadores de eventos ROLLBACK en postgresql

No puedes usar una secuencia para esto. Necesita un único punto de serialización a través del cual todos las inserciones deben desaparecer; de lo contrario, no se puede garantizar el atributo "sin espacios". También debe asegurarse de que nunca se eliminen filas de esa tabla.

La serialización también significa que solo una sola transacción puede insertar filas en esa tabla; todas las demás inserciones deben esperar hasta que la inserción "anterior" se haya confirmado o revertido.

Un patrón de cómo se puede implementar esto es tener una tabla donde se almacenan los números de "secuencia". Supongamos que necesitamos esto para los números de factura que deben estar sin espacios por razones legales.

Así que primero creamos la tabla para contener el "valor actual":

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');

Ahora necesitamos una función que genere el siguiente número pero que garantice que dos transacciones no puedan obtener el siguiente número al mismo tiempo.

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;

La función incrementará el contador y devolverá el valor incrementado como resultado. Debido a la update la fila de la secuencia ahora está bloqueada y ninguna otra transacción puede actualizar ese valor. Si la transacción de llamada se retrotrae, también lo hace la actualización del contador de secuencia. Si se confirma, el nuevo valor se mantiene.

Para garantizar que cada transacción utiliza la función, se debe crear un disparador.

Cree la tabla en cuestión:

create table invoice 
(
  invoice_number integer not null primary key, 
  customer_id    integer not null,
  due_date       date not null
);

Ahora cree la función de activación y el activador:

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();

Ahora, si una transacción hace esto:

insert into invoice (customer_id, due_date) 
values (42, date '2015-12-01');

Se genera el nuevo número. Un segundo la transacción debe esperar hasta que la primera inserción se confirme o revierta.

Como dije:esta solución no es escalable. De nada. Ralentizará enormemente su aplicación si hay muchas inserciones en esa tabla. Pero no puede tener ambos:un y escalable implementación correcta de una secuencia sin pausas.

También estoy bastante seguro de que hay casos extremos que no están cubiertos por el código anterior. Por lo tanto, es bastante probable que aún pueda terminar con lagunas.