ECPG et Bytea

Pour une fois, voici un billet un peu plus technique. Hier, j'ai reçu un défi. Une personne avait un problème avec son programme écrit en ECPG. Ce qu'il pensait être un bogue l'empêchait d'enregistrer certaines données dans une colonne de type bytea.

ECPG est une sorte de langage macro permettant d'embarquer facilement des requêtes dans un programme en C. Le code écrit sera passé à un préprocesseur, ecpg, qui fournira un code C valide et pouvant être compilé avec gcc.

Voici un bout du code qu'il utilisait :

mon_contenuBytea = PQescapeBytea ( mon_contenu, taille1, &taille2 );
EXEC SQL INSERT INTO interieur (nom, contenu) VALUES ('pouet', :mon_contenuBytea::bytea);

PQescapeBytea permet de convertir les éléments pouvant poser problème dans une chaîne (caractère nul 0x00, autres caractères non affichables, etc). Il convertit donc son buffer, l'insère dans la requête et exécute cette dernière. En vérifiant ce qu'il obtient dans la base, il peut faire plusieurs constatations :

  • certains caractères sont de nouveau échappés ;
  • pour peu qu'il y ait un ' ou un \, l'insertion échoue.

Pourquoi ? je n'ai aucune preuve de ce que j'avance mais j'ai l'impression que l'instruction EXEC SQL INSERT INTO regarde les différents arguments et, si elle trouve un argument de type char*, elle appelle automatiquement PQescapeString (l'équivalent de PQescapeBytea mais pour les chaînes de caractères terminées par un caractère nul et sensibles à la locale utilisée). J'ai donc fait quelques tests, dont ce dernier :

sprintf(mon_contenu, "%c%c%c%c%c", 61, 0, 39, 61, 0);
taille1 = 5;
mon_contenuBytea = PQescapeBytea ( mon_contenu, taille1, &taille2 );
EXEC SQL INSERT INTO interieur (nom, contenu) VALUES ('pouet', :mon_contenuBytea::bytea);

Dans les traces du serveur PostgreSQL, je vois ça :

2005-06-28 09:33:06 7523 LOG:  statement: insert into interieur ( nom  , contenu  ) values ( 'pouet' ,  '=\\\\000\''=\\\\000' :: bytea  )

Donc au lieu d'avoir =\\000'=\\000, j'obtiens un doublement des ' et des \ ... preuve qu'une opération de type PQescapeString a eu lieu !

Je n'ai pas trouvé immédiatement la solution mais le moyen le plus simple pour que PostgreSQL évite de traiter les arguments est de ne pas lui donner (oui, oui, j'aime bien La Palisse). Il faut lui donner l'instruction complète. Voici l'exemple du dessus remanié :

marequete = calloc(255, sizeof(unsigned char));
sprintf(marequete, "INSERT INTO interieur (nom, contenu) VALUES ('pouet', '%s')", mon_contenuBytea);
EXEC SQL EXECUTE IMMEDIATE :marequete;

Ça fonctionne chez moi, tous mes autres test démontrent que cela passe à coup sûr ainsi. J'ai envoyé un mail à la personne qui m'avait lancé ce défi, j'attends sa réponse.

Ajouter un commentaire

Les commentaires peuvent être formatés en utilisant une syntaxe wiki simplifiée.

Fil des commentaires de ce billet