J'ai travaillé sur la tâche d'analyse de texte lorsque j'ai trouvé un comportement Postgres étrange. Mon code original exposant une erreur étrange a été écrit en Java avec la connectivité JDBC pour PostgreSQL (v8.3.3 et v8.4.2 testé), voici mon post original: Is it an error of PostgreSQL SQL engine and how to avoid (workaround) it?. Je viens de porter mon code Java donné à plpgsql pur et il donne les mêmes erreurs (même comportement que décrit dans la publication originale).Pourquoi ce code échoue dans PostgreSQL et comment le réparer (contourner)? Est-ce une faille du moteur Postgres SQL?
Code simplifié n'a plus rien à voir avec l'analyse syntaxique - il génère juste des pseudo-aléatoires (mais répétables) mots et les insère après normalisant (tableau spb_word
contient des mots et des ids uniques, ils sont référencés par id dans la table finale spb_obj_word
et une table spb_word4obj
fonctionne comme tampon d'entrée).
Voici mes tableaux (c & p de OP):
create sequence spb_word_seq;
create table spb_word (
id bigint not null primary key default nextval('spb_word_seq'),
word varchar(410) not null unique
);
create sequence spb_obj_word_seq;
create table spb_obj_word (
id int not null primary key default nextval('spb_obj_word_seq'),
doc_id int not null,
idx int not null,
word_id bigint not null references spb_word (id),
constraint spb_ak_obj_word unique (doc_id, word_id, idx)
);
create sequence spb_word4obj_seq;
create table spb_word4obj (
id int not null primary key default nextval('spb_word4obj_seq'),
doc_id int not null,
idx int not null,
word varchar(410) not null,
word_id bigint null references spb_word (id),
constraint spb_ak_word4obj unique (doc_id, word_id, idx),
constraint spb_ak_word4obj2 unique (doc_id, word, idx)
);
et le code porté à plpgsql à partir du code Java d'origine:
create sequence spb_wordnum_seq;
create or replace function spb_getWord() returns text as $$
declare
rn int;
letters varchar(255) := 'ąćęłńóśźżjklmnopqrstuvwxyz';
--'abcdefghijklmnopqrstuvwxyz';
llen int := length(letters);
res text := '';
wordnum int;
begin
select nextval('spb_wordnum_seq') into wordnum;
rn := 3 * (wordnum + llen * llen * llen);
rn := (rn + llen)/(rn % llen + 1);
rn := rn % (rn/2 + 10);
loop
res := res || substring(letters, rn % llen, 1);
rn := floor(rn/llen);
exit when rn = 0;
end loop;
--raise notice 'word for wordnum=% is %', wordnum, res;
return res;
end;
$$ language plpgsql;
create or replace function spb_runme() returns void as $$
begin
perform setval('spb_wordnum_seq', 1, false);
truncate table spb_word4obj, spb_word, spb_obj_word;
for j in 0 .. 50000-1 loop
if j % 100 = 0 then raise notice 'j = %', j; end if;
delete from spb_word4obj where doc_id = j;
for i in 0 .. 20 - 1 loop
insert into spb_word4obj (word, idx, doc_id) values (spb_getWord(), i, j);
end loop;
update spb_word4obj set word_id = w.id from spb_word w
where w.word = spb_word4obj.word and doc_id = j;
insert into spb_word (word)
select distinct word from spb_word4obj
where word_id is null and doc_id = j;
update spb_word4obj set word_id = w.id
from spb_word w
where w.word = spb_word4obj.word and
word_id is null and doc_id = j;
insert into spb_obj_word (word_id, idx, doc_id)
select word_id, idx, doc_id from spb_word4obj where doc_id = j;
end loop;
end;
$$ language plpgsql;
Pour exécuter ce simplement exécuter select spb_runme()
comme instruction SQL.
Voici premier exemple d'erreur:
NOTICE: j = 8200
ERROR: duplicate key value violates unique constraint "spb_word_word_key"
CONTEXT: SQL statement "insert into spb_word (word) select distinct word from spb_word4obj where word_id is null and doc_id = $1 "
PL/pgSQL function "spb_runme" line 18 at SQL statement
et seconde:
NOTICE: j = 500
ERROR: null value in column "word_id" violates not-null constraint
CONTEXT: SQL statement "insert into spb_obj_word (word_id, idx, doc_id) select word_id, idx, doc_id from spb_word4obj where doc_id = $1 "
PL/pgSQL function "spb_runme" line 27 at SQL statement
Ces erreurs se produisent de façon imprévisible - à chaque fois dans différentes itérations (j
) et avec différents mots provoquant une erreur .
Lorsque les caractères nationaux polonais (ąćęłńóśźż
) sont éliminés des mots générés (ligne letters varchar(255) := 'ąćęłńóśźżjklmnopqrstuvwxyz';
devient letters varchar(255) := 'abcdefghijklmnopqrstuvwxyz';
) il n'y a pas d'erreur! Mon DB est créé avec un encodage UTF-8, donc il ne devrait pas y avoir de problème avec les caractères non-ascii, mais apparemment c'est très important! Maintenant, ma question: quel est le problème avec mon code? Ou est-ce quelque chose de grave avec PostgreSQL? Comment contourner cette erreur?
BTW: S'il s'agit d'une erreur dans le moteur PostgreSQL, alors comment cette base de données peut-elle être fiable? Devrais-je passer à l'une des alternatives gratuites (par exemple MySQL)?
MISE À JOUR:explications supplémentaires (principalement pour OMG Poneys)
Si je retire delete
inutile - je toujours les mêmes erreurs.
La fonction spb_getWord()
doit générer des mots avec des doublons - elle simule l'analyse du texte et la divise en mots - et certains mots se répètent - c'est normal et le reste de mon code traite des doublons. En raison des doublons possibles générés par spb_getWord()
j'insère des mots à la table de tampon spb_word4obj
puis je mets à jour word_id
dans ce tableau pour les mots déjà traités de spb_word
. Alors maintenant - si la ligne dans spb_word4obj
a word_id
non nul - alors c'est un doublon, donc je ne vais pas insérer ce mot dans spb_word
.Mais - comme OMG Ponies mentionné, je reçois erreur duplicate key value violates unique constraint
ce qui signifie que mon code qui gère les doublons échoue correctement. C'est à dire. mon code échoue en raison d'une erreur Postgres interne - le code correct est en quelque sorte mal exécuté par Postgres et échoue.
Après avoir inséré de nouveaux mots (les doublons reconnus et marqués de ne pas être inséré) dans spb_word
mon code insère enfin mots normalisés dans spb_obj_word
- remplacer le corps de texte en référence à l'entrée non dupliquée dans spb_word
, mais cela échoue à nouveau, parfois à cause d'une erreur interne Postgres . Encore une fois je pense que mon code est correct mais il échoue car il y a un problème dans le moteur Postgres SQL lui-même. Ajouter ou supprimer polonais lettres nationales des mots générés par spb_getWord
seulement m'assure qu'il est étrange Postgres erreur - toutes les considérations uniques/doubles restent les mêmes, mais l'autorisation/interdisant certaines lettres de mots conduit à des erreurs ou les élimine. Ce n'est donc pas le cas d'une erreur dans mon code - une mauvaise manipulation des doublons.
La deuxième chose qui m'assure que ce n'est pas une erreur dans mon code est le moment imprévisible des erreurs qui sont détectées. Chaque exécution de mon code fait la même séquence de mots, donc il devrait toujours se casser au même endroit avec la même valeur causant l'erreur. Mais ce n'est pas - c'est un moment assez aléatoire qu'il échoue.
Vous rencontrez des erreurs de contrainte - rien à redire sur Postgres –
@OMG Poneys: oui Je suis en train de rencontrer des erreurs de contraintes. Mais je ne devrais pas! Lisez attentivement mon code - il ne devrait pas y avoir de telles erreurs. Et pourquoi ces erreurs viennent de manière imprévisible avec le code répétable? Pourquoi l'alphabet est-il important (si je supprime tous les caractères nationaux polonais, il n'y a pas d'erreur)? Pourriez-vous m'expliquer cela? – WildWezyr
Essayez d'expérimenter 'set client-encoding ...', voir http://www.postgresql.org/docs/current/static/multibyte.html. Si vous n'obtenez pas de meilleures réponses ici, apportez-le à la liste de diffusion postgresql (vous êtes sûr d'être aidé là-bas). – ChristopheD