PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Trigger di eventi ROLLBACK in postgresql

Non puoi usare una sequenza per questo. Hai bisogno di un unico punto di serializzazione attraverso il quale tutti gli inserti devono andare, altrimenti l'attributo "gapless" non può essere garantito. Devi anche assicurarti che nessuna riga venga mai eliminata da quella tabella.

La serializzazione significa anche che solo una singola transazione può inserire righe in quella tabella:tutti gli altri inserimenti devono attendere fino a quando l'inserto "precedente" non è stato eseguito il commit o il rollback.

Un modello su come questo può essere implementato è avere una tabella in cui sono archiviati i numeri di "sequenza". Supponiamo di averne bisogno per i numeri di fattura che devono essere privi di interruzioni per motivi legali.

Quindi prima creiamo la tabella per contenere il "valore corrente":

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

Ora abbiamo bisogno di una funzione che generi il numero successivo ma che garantisca che nessuna transazione possa ottenere il numero successivo contemporaneamente.

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 funzione incrementerà il contatore e di conseguenza restituirà il valore incrementato. A causa dell'update la riga per la sequenza è ora bloccata e nessun'altra transazione può aggiornare quel valore. Se viene eseguito il rollback della transazione chiamante, lo è anche l'aggiornamento del contatore di sequenza. Se viene eseguito il commit, il nuovo valore viene mantenuto.

Per garantire che ogni transazione utilizza la funzione, è necessario creare un trigger.

Crea la tabella in questione:

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

Ora crea la funzione trigger e il trigger:

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

Ora, se una transazione fa questo:

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

Viene generato il nuovo numero. Un secondo la transazione deve quindi attendere fino a quando non viene eseguito il commit o il rollback del primo inserimento.

Come ho detto:questa soluzione non è scalabile. Affatto. Rallenta enormemente la tua applicazione se ci sono molti inserti in quella tabella. Ma non puoi avere entrambi:un e scalabile corretta implementazione di una sequenza gapless.

Sono anche abbastanza sicuro che ci siano casi limite che non sono coperti dal codice sopra. Quindi è abbastanza probabile che tu possa ancora ritrovarti con delle lacune.