PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Déclencheurs d'événements ROLLBACK dans postgresql

Vous ne pouvez pas utiliser une séquence pour cela. Vous avez besoin d'un point de sérialisation unique par lequel tous les insertions doivent aller - sinon l'attribut "gaples" ne peut pas être garanti. Vous devez également vous assurer qu'aucune ligne ne sera jamais supprimée de cette table.

La sérialisation signifie également qu'une seule transaction peut insérer des lignes dans cette table - toutes les autres insertions doivent attendre que l'insertion "précédente" ait été validée ou annulée.

Un modèle de mise en œuvre consiste à disposer d'une table dans laquelle les numéros de "séquence" sont stockés. Supposons que nous en ayons besoin pour les numéros de facture qui doivent être sans espace pour des raisons légales.

Donc, nous créons d'abord la table pour contenir la "valeur actuelle":

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

Nous avons maintenant besoin d'une fonction qui générera le nombre suivant mais qui garantit qu'aucune transaction ne peut obtenir le nombre suivant en même temps.

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 fonction incrémentera le compteur et renverra la valeur incrémentée en conséquence. En raison de la update la ligne de la séquence est maintenant verrouillée et aucune autre transaction ne peut mettre à jour cette valeur. Si la transaction appelante est annulée, la mise à jour du compteur de séquence l'est également. Si elle est validée, la nouvelle valeur est persistante.

Pour s'assurer que chaque transaction utilise la fonction, un déclencheur doit être créé.

Créez la table en question :

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

Créez maintenant la fonction déclencheur et le déclencheur :

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

Maintenant, si une transaction fait ceci :

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

Le nouveau numéro est généré. Une seconde la transaction doit alors attendre que la première insertion soit validée ou annulée.

Comme je l'ai dit :cette solution n'est pas évolutive. Pas du tout. Cela ralentira considérablement votre application s'il y a beaucoup d'insertions dans cette table. Mais vous ne pouvez pas avoir les deux :un évolutif et mise en œuvre correcte d'une séquence sans interruption.

Je suis également à peu près sûr qu'il existe des cas extrêmes qui ne sont pas couverts par le code ci-dessus. Il est donc fort probable que vous puissiez encore vous retrouver avec des lacunes.