mardi 22 mars 2011

Être consistent...

Rhaaa, IBM! Ils viennent de confirmer après 3 jours d'arrachage de cheveux qu'ils ne supportent pas une fonctionnalité qui semble pourtant basique.

DB2, ou plutôt son API C, répondant au doux nom de CLI, fournit un mode très efficace exécuter plusieurs fois une même requête. L'idée est de créer un tableau, de "binder" ce tableau à une colonne (via un appel à SQLBindParameter), puis d'exécuter la requête préparée. Tout est envoyé en une fois à la base, ce qui évite l'effet ping-pong entre le client et le serveur. Par exemple, une requête "insert into mytable values(?)" permet d'insérer des milliers de lignes en un seul appel.

Nous codons donc une belle couche d'abstraction en C++, et continuons joyeusement à coder.

Jusqu'au jour où je découvre que le binding par colonnes ne fonctionne pas avec les procédures stockées. Dans le genre, je créé une procédure qui fait un bête insert dans mytable, et j'appelle "call myproc(?)". Pire, DB2 va exécuter la procédure, mais seulement sur le premier élément, ce qui cause un beau bordel dans la base. La documentation est peu claire sur ce point précis, la spec ODBC dit que ça devrait marcher. Eh ben non.

Reprenons: Une fonctionnalité qui semble pourtant aller de soi manque, ce manquement est mal documenté, et au lieu d'envoyer un message d'erreur clair, seule une partie de la requête tourne, causant une corruption des données. Peut-être que la raison fondamentale est que insert, update et delete supportent un vrai mode d'insertion par tableau, mais dans ce cas pourquoi ne pas au moins tenter d'être consistent, et pour le moteur de base de données de revenir dans un mode ligne par ligne dans le cas des procédures stockées? J'ai du mal à penser qu'IBM n'a pas les moyens humains de fournir quelque chose qui semble aussi trivial.

La solution, c'est d'utiliser le binding par valeur pour toute requête contenant une procédure stockée, mais c'est difficile à abstraire, puisqu'il faut soit laisser à l'utilisateur le choix (et donc la chance de se tromper), soit tenter d'analyser la requête pour détecter si elle est optimisable. En l'occurence, j'ai choisi la sale mais raisonnablement triviale solution d'optimiser si le premier mot de la requête est "insert". Et le code de la couche d'abstraction devient plus moche car il doit supporter deux types de bindings, et donc deux chemins de code, avec les presque duplications et l'augmentation des risques de se bugs qui vont avec. Beurk.

Aucun commentaire: