Les transactions avec Oracle et PHP

Eléphant du PHP | 246 Messages

01 mai 2009, 18:32

Hello!

J'aimerais m'assurer de quelque chose au niveau des transactions lorsqu'on utilise une base de donnée Oracle et qu'il y a une applic PHP.

Je suis sur un projet en ce moment et je remarque que dans les pages qui sont déjà créé, lorsqu'on insère un nouvel enregistrement dans la base, a aucun moment il n'y a de validation de la transaction (commit) et pourtant les enregistrement sont quand même bien enregistrer dans la base.

Voici comment ça se passe :

Utilisateur 1 :
- Se connecte
- Formulaire d'ajout
- Page de traitement ajoutant
- Page confirmant que l'enregistrement a réussi

Utilisateur 2
- Se connecte
- Voit l'enregistrement de l'utilisateur 1, alors qu'il ne devrait pas

Donc l'utilisateur 2 voit l'enregistrement de l'utilisateur 1 alors qu'il n'a jamais valider sa transaction. Dans une base de donnée, ça ne devrait pas arriver.

Dans une BD la transaction se fait soit :
-Manuelement avec le commande commit
- Lorsqu'on execute une commande de LDD
- Lorsque l'utilisateur se déconnecte

Donc ce que j'imagine, c'est qu'à la fin de la page de traitement, la connexion est fermée, et donc il y a une validation automatique. Est-ce que mon raisonnement est juste il y a une autre explication ?

Car je dois rendre un document qui explique comment est fait mon applic et il y a un chapitre sur les transactions. J'aimerais éviter d'écrire des truc faux ^^

Au fait, voici les fonction php :
odbc_connect() // Se connecter à la base
odbc_do() // Executer une requête
odbc_commit() // Valider la transaction, mais je ne l'ai jamais utilisé car la validation est automatique apparament...
Merci d'avance!

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 10684 Messages

01 mai 2009, 21:54

Je ne sais pas comment cela fonctionne avec odbc, mais avec les fonctions oci d'oracle, les requêtes sont par défaut automatiquement validées, donc nul besoin de commit. Je sais que tu peux modifier ce comportement avec oci pour créer des transactions qui nécessiteront donc une validation explicite pour être prises en compte.

Le comportement avec la connexion odbc est probablement similaire :)

Il n'y a pas d'autocommit sur la fermeture ou la perte de la connexion, cela ferait perdre tout l'intérêt du mode transaction (pouvoir ne pas valider en cas d'erreur)
Ce n'est pas en améliorant la bougie que l'on a inventé l'ampoule...

Eléphant du PHP | 246 Messages

01 mai 2009, 23:29

Merci pour ta réponse!

Donc si l'exécution d'une requête fait automatiquement un commit, il n'est pas possible de faire un rollback dans ma page ?

Le meilleur moyen est de tester mais tout est au boulot donc je ne saurai pas avant lundi...

Car je prend un exemple. Ma page permet d'ajouter un véhicule avec ses composants. Dans ma page des traitement j'ai dans l'ordre :

- Insérer le véhicule
- Boucle qui insère chacun des composants

Mais admettons que l'insertion des composants à échoué pour une raison X... On se retrouvera avec un véhicule avec 0 composants, et ça ne devrait pas exister. Dans ce cas là il faudrait faire un rollback si l'insertion des composants à échoué.

Pour l'instant dans ma page j'ai gérer ça en supprimant l'absence avec un DELETE et ça marche très bien aussi.

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 10684 Messages

02 mai 2009, 08:55

Tu peux effectivement le tester assez simplement en ouvrant 2 connexions dans un script. Avec la première tu fais une modification en base puis un rollback (qui n'aura à priori aucun effet, l'instruction ayant été auto-commité) et avec la 2nd connexion tu interroges ta base pour vérifier la modification. Tu peux même le faire avant et après le rollback.

Tu devrais dans les deux cas constater la modification, alors qu'aucun commit explicite n'a été réalisé, voire même qu'un rollback a été demandé :)

Je ne suis pas certain que tu puisses modifier ce comportement en odbc. En revanche si tu utilises le composant oci de php, la fonction oci_execute() te permet de spécifier le mode OCI_DEFAULT (au lieu de OCI_COMMIT_ON_SUCCESS utilisé par défaut).

Dès lors, aucune modification ne sera enregistrée en base s'il n'y a pas eu de commit explicite (oci_commit()) ou de modification de la structure de la bdd. Tu retrouves ainsi le comportement logique des transactions ;) (nota: ça augmente aussi la durée des locks sur des enregistrements pendant la transaction)
Ce n'est pas en améliorant la bougie que l'on a inventé l'ampoule...

Administrateur PHPfrance
Administrateur PHPfrance | 3131 Messages

02 mai 2009, 10:19

Avec OCI8 tu pourras en effet utiliser le "exec-mode" passé en deuxième paramètre de oci_execute. Le mieux est de faire un wrapper pour bien contrôler ça :
oci_execute($query, OCI_COMMIT_ON_SUCCESS); // hors transaction : auto-commit

oci_execute($query, OCI_DEFAULT); // pas d'auto-commit : comportement d'une transaction

try 
{
  ...
  oci_commit(); // commit
}
catch (Exception $e)
{
  oci_rollback(); // rollback
}
Comme je le disais, à mon avis il vaut mieux s'écrire un wrapper pour éviter de se mélanger avec ce "exec-mode", ou alors constamment travailler en mode transaction mais je ne sais pas si c'est bien utile.
class oci
{

  protected static $execMode = OCI_COMMIT_ON_SUCCESS;

  public static function execute($query)
  {
    return oci_execute($query, self::$execMode);
  }

  public static function begin()
  {
    self::$execMode = OCI_DEFAULT; // passage en mode "transaction"
  }

  public static function commit()
  {
    oci_commit(); // commit
    self::$execMode = OCI_COMMIT_ON_SUCCESS; // sortie du mode "transaction"
  }

  public static function rollback()
  {
    oci_rollback(); // rollback
    self::$execMode = OCI_COMMIT_ON_SUCCESS; // sortie du mode "transaction"
  }

}
oci::execute($query); // hors transaction : auto-commit

oci::begin(); // démarrage transaction
oci::execute($query); // pas d'auto-commit

try 
{
  ...
  oci::commit(); // commit
}
catch (Exception $e)
{
  oci::rollback(); // rollback
}
Par contre comme mon prédécesseur je n'ai pas d'info sur le connecteur odbc.
J'ai personnellement une très mauvaise expérience de la connexion avec une base Oracle : le driver OCI8 est buggé en version 5.1 et n'est pas compilable avec le support des BLOB et des CLOB. Il faut une 5.2 pour bénéficier de ce support :(

Je n'ai aucune expérience avec le driver ODBC, mais il doit forcément reposer sur une couche oracle genre "Instant Client" parce que leur mode de connexion (déclaration des host en particulier) est très singulier.

Eléphant du PHP | 246 Messages

07 mai 2009, 11:23

Merci pour vos réponse. J'ai trouvé qu'il y avait la fonction odbc_autocommit($cnx, FALSE) pour stoper les commit automatique.

J'ai tenté l'exemple suivant et mon en enregistrement reste enregistré. http://odbcphp.hello-design.fr/f-odbc_rollback.php

Bon... tempis ^^