Erreur sur sql_insertq_multi() et transactions

Hello,

Quand on a une erreur sur cette fonction qui insère en une fois une liste d’enregistrements, tout s’arrête même si on a qu’un seule élément en cause, en particulier, si on a un enregistrement en doublon (duplicate key).

Ca n’est pas possible de laisser passer les duplicate key (ie les erreurs unitaires) et d’insérer correctement les enregistrements non en doublon ?
Je parle d’une possibilité autre que de passer les enregistrements un par un.

Nope (c’est le fonctionnement de sql). Au pire tu peux l’encadrer d’une transaction (en mysql) pour tout annuler en cas d’erreur. Note que en SQLite, on fait une transaction automatiquement sur les _multi() ; c’était car, à une époque au moins, ça accélérait clairement SQLite. Et donc sur 1 erreur, il annule toutes les insertions déjà réalisées.

Ben sans c’est déjà le cas justement.
Si j’envoie un ensemble de 50 enregistrements via un insertq_multi() et qu’un des 50 est un doublon tout l’ensemble des 50 enregistrements est justement refusé : il n’y a rien en base.
Moi j’aurais aimé que ceux qui sont bons passent.

Bon, je fais un petit point sur ce sujet de transaction.

Si j’utilise un insertq_multi() sur 200 lignes, par exemple :

  • en mysql, la fonction fait des paquets de max 100 lignes et les insèrent en une instruction. Aucune gestion des transactions n’est faite, donc si le premier paquet passe et le second non on se retrouve avec un contexte final bancal.
  • en sqlite, la fonction ne fait aucun paquet et exécute une seule requête qu’elle encadre par une transaction début/fin pour laquelle aucun rollback n’est effectué si la requête est en erreur. C’est déjà une différence. De ce que j’ai compris d’après les commentaires, la transaction n’est utilisée que pour améliorer les performances sqlite et non gérer la cohérence du contexte final.

Donc, conclusion : spip ne gère à aucun moment les transactions dans le but d’assurer la cohérence des exécutions multi-requêtes et comme sqlite lui est transactionnel et pas mysql ça ne permet pas d’assurer un comportement identique simplement.

Bon, alors je sais que c’est pas forcément une feature prioritaire mais je trouve que ça serait bien d’avoir un système de transactions complet et cohérent entre sqlite et mysql.
En particulier, je ne vois l’utilité de la fonction sql_preferer_transaction() qui répond non pour mysql.

Ensuite on pourrait :

  • compléter les fonctions actuelles utilisées uniquement pour sqlite sql_demarrer_transaction() et sql_terminer_transaction() par une version mysql (BEGIN, COMMIT, c’est d’ailleurs les mêmes instructions en mysql et sqlite).
  • ajouter une fonction de rollback, par exemple, sql_annuler_transaction() (ROLLBACK). Pour info, il existe une méthode sqlite annuler_transaction().
  • peut être aligner la gestion de l’insert_multi de sqlite sur celui de mysql en paquets de 100 et en gérant le fait que la transaction peut être déjà en cours

En attendant je pense utiliser sql_query() directement pour gérer les transactions

Ça vaudrait le coup de noter tout ça dans un ticket je pense :slight_smile:

Ok je fais ça demain.

tu veux dire #3781 - Finaliser les fonctions de transaction dans l'API SQL - spip - SPIP on GIT ?

Ah ben ouais c’est excellent, je m’en souvenais même plus, tu es trop fort !
Alors ça ce n’est qu’une partie du sujet, la cohérence de l’api transaction. En plus, j’ai pas l’impression que ce soit compliqué à rajouter vu la simplicité des instructions.

Mais il y a autre chose dans ce que j’ai dit : pour moi il y a une incohérence dans le traitement de insert_multi en sqlite et en mysql car on n’envoie tout en sqlite et par paquets en mysql.
Si on met des transactions il faut gommer cette incohérence (j’ai pas vérifié pour les autres fonctions de groupe).

Et il y a aussi le fait que l’utilisation des transactions en sqlite dans le but d’améliorer les performances est incrustée dans la fonction de groupe ce qu’il faudra supprimer ou à minima conditionner au fait qu’une transition n’est pas en cours.

Pour PG j’ai rien regardé. D’ailleurs si la mise en œuvre est obsolète ne devrait-on pas virer le fichier lui-même ?