Recommend Me


Mercredi 22 février 2006

Jouer avec MySQL 5 et les procédures stockées

(Linux Magazine France n°61 - Mai 2004 - sources)

La version 5 (beta) de MySQL nous laisse entrevoir des améliorations majeures et en particulier le tant attendu support des procédures stockées. Malheureusement les triggers, compagnons inséparables des procédures, ne sont pas encore disponibles et certains bugs persistants ne permettent pas d’envisager une utilisation en production. Tout cela mérite tout de même que nous regardions de près ce que nous prépare MySQL AB et puisqu’on ne nous invite qu’a faire des tests, je vous propose de voir cela avec un exemple qui n’a rien à voir avec ce que l’on fait habituellement avec des procédures et fonctions : un jeu des tours de Hanoi !

1. Procédures et fonctions

Le principe des procédures stockées est assez simple, s’il s’agit d’enregistrer un ensemble de commandes directement dans la base de données. Dans le cas de MySQL, la définition des procédures est placée dans la table mysql.proc. Nous reviendrons sur le contenu de cette table un peu plus loin.

La création d’une procédure se fait en utilisant la commande CREATE PROCEDURE :

CREATE PROCEDURE maproc( [paramètre[, ...]] )    -- caractéristiques BEGIN    -- corps END;

En fait nous avons tendance à parler de “procédure stockée” de façon abusive. En effet il est tout à fait possible de créer des fonctions sur le même modèle :

CREATE FUNCTION mafunc( [paramètre[, ...]] ) RETURNS type   -- caractèristiques                                                                                                                           BEGIN   -- corps END;                                                                                                                                          

Même si nous devrions plutôt parler de “routines stockées”, nous continuerons à utiliser le terme procédure en faisant la distinction quand cela est nécessaire.

Comme le montre les deux définitions précédentes, il est possible de caractériser les procédures ou fonctions :

  • language : permet de préciser le langage utilisé. Dans la version actuelle seule le type SQL est supporté, mais on nous promet un support de PHP pour le futur et gageons que les possibilités ne se limiteront pas uniquement a ce dernier. La valeur par défaut est language sql.
  • [not] deterministic : ceci est utilisé pour optimiser le traitement dans le cadre d’une fonction. Si le résultat renvoyé est toujours le même la fonction est dite déterministe. Pour le moment cette caractéristique n’est pas utilisée. Par défaut une fonction est considérée comme non déterministe (not deterministic).
  • sql securety [definer|invoker] permet de préciser si l’exécution doit se faire avec les droits du créateur (definer) ou de celui qui appel (invoker) la procédure. La valeur par défaut est definer.
  • Il est possible d’ajouter un commentaire à une routine grâce au mot clé comment. Cette information sera visible via les commandes show create [procedure | function] ou show [procedure | function] status.

La principale différence entre une procédure est une fonction tiens au fait que les secondes renvoient un résultat. Ceci a une incidence sur la méthode d’appel. Dans le cas d’une fonction nous ferons un appel par select :

mysql> SELECT ma_fonction( ... );

alors que pour une procédure nous utiliserons un call :

mysql> call ma_procedure( ... );

Ceci a une importance si nous souhaitons utiliser le résultat dans une requête ou une sous requête.

2. Retour et paramètres

Les paramètres et le retour d’une fonction peuvent être d’un type MySQL quelconque (INT, DATE, TIME, TEXT, VARCHAR, …). Reportez-vous à la documentation[1] pour plus d’informations. Cependant une seule valeur peut être retournée par une fonction.

mysql> delimiter ! mysql> CREATE FUNCTION message() RETURNS TEXT     -> BEGIN     ->   RETURN "Je suis une fonction de test !";     -> END     -> ! Query OK, 0 rows affected (0.00 sec) mysql> SELECT message()! +--------------------------------+ | message()                      | +--------------------------------+ | Je suis une fonction de test ! | +--------------------------------+ 1 row IN SET (0.00 sec) mysql>

Ceci peut être contourné en utilisant les paramètres. En effet, ces derniers peuvent être de trois sortes :

  • IN : paramètre en entrée uniquement, donc non modifié en sortie.
  • OUT : paramètre en sortie uniquement, donc non affecté en entrée.
  • INOUT : paramètre en entrée et en sortie, donc pouvant affecter (ou non) en entrée et modifié (ou on) en sortie.

Cependant ceci est valable uniquement pour les procédures. Dans le cas des fonctions les paramètres sont forcement en entrée (IN). Il n’est d’ailleurs même pas nécessaire de le préciser lors de la déclaration. De même si aucune précision n’est donnée lors de la création d’une procédure, les paramètres sont par défaut en entrée.

mysql> CREATE PROCEDURE bidon( IN i INTEGER, INOUT j INTEGER, OUT k INTEGER )     -> BEGIN     ->   SET k = i * 2;     ->   SET j = j * 2;     -> END     -> ! Query OK, 0 rows affected (0.00 sec)

MySQL est suffisamment permissive pour nous permettre d’appeler la procédure bidon de la façon suivante :

mysql> CALL bidon( 1, 2, 3 )! Query OK, 0 rows affected (0.01 sec) mysql>

Mais cela ne présente pas en soi un grand intérêt et il serait beaucoup plus utile de définir des variables donc on pourrait examiner le contenu après l’appel. Pour ce faire il suffit d’utiliser la commande SET :

mysql> SET @a = 1! SET @b = 2! SET @c = 3! Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected (0.00 sec) mysql> CALL bidon( @a, @b, @c )! Query OK, 0 rows affected (0.00 sec) mysql> SELECT @a, @b, @c! +------+------+------+ | @a   | @b   | @c   | +------+------+------+ | 1    | 4    | 2    | +------+------+------+ 1 row IN SET (0.00 sec) mysql>

Vous aurez certainement remarqué que, bien que nous ayons affecté @c, cela n’a eu aucune incidence sur le fait de l’utiliser comme variable de sortie uniquement lors de l’appel de la procédure.

3. ALTER, SHOW, DROP et autre DELIMITER

L’instruction alter permet de modifier une procédure ou une fonction. Ceci est malheureusement limité aux simples caractéristiques et encore, uniquement au nom, au niveau de sécurité et au commentaire associé :

mysql> ALTER FUNCTION message     -> NAME test     -> SQL SECURITY INVOKER     -> COMMENT "Cette fonction etait avant appelee message"     -> ! Query OK, 0 rows affected (0.00 sec) mysql> SELECT test()! +--------------------------------+ | test()                         | +--------------------------------+ | Je suis une fonction de test ! | +--------------------------------+ 1 row IN SET (0.00 sec) mysql>

Pour vérifier ces modifications, nous pouvons utiliser la commande show. Pour cela deux formes d’appels sont possibles :

  • show {procedure|function} status! Nous récupérons alors la liste de toutes les procédures (ou fonctions) avec pour chacune son nom, son type, l’utilisateur qui l’a créé, les dates de dernière modification et de création, le type de sécurité et le commentaire associé.
  • show create {procedure|function} ! nous donne le nom et le code associé d’une procédure nommée.

Nous l’avons vu, la commande alter ne nous permet pas de modifier le corps d’une procédure ; il faudra passer par une étape de destruction, réécriture pour réaliser un tel changement. La suppression d’une procédure se fait en utilisant la commande drop :

mysql> DROP PROCEDURE IF EXISTS test! Query OK, 0 rows affected (0.00 sec) mysql>

Vous avez certainement constaté que je n’ai pas utilisé le délimiteur classique lors du passage de commandes depuis le début de cette présentation. En effet, par défaut MySQL utilise le point virgule (;) comme délimiteur d’instructions. Or c’est ce même délimiteur qui est utilisé dans le langage de création des procédures stockées. Il est donc important de commencer par redéfinir ce délimiteur pour éviter les conflits. Ceci est fait par la commande delimiter ! par laquelle nous précisons (ici) utiliser le point d’exclamation (!) en lieu et place du point virgule (;). Ceci permettra d’éviter de se faire injurier par la base.

mysql> CREATE PROCEDURE plantage()     -> BEGIN     ->   SELECT "la, ca va planter"; ERROR 1064 (42000): You have an error IN your SQL syntax.  CHECK the manual that corresponds TO your MySQL server version FOR the RIGHT syntax TO USE near 'SELECT "la, ca va planter"' at line 3 mysql> delimiter ! mysql> CREATE PROCEDURE pas_plantage()     -> BEGIN     ->   SELECT "et hop, plus de plantage";     -> END     -> ! Query OK, 0 rows affected (0.00 sec) mysql>

Notez que vous n’êtes pas du tout obligé d’utiliser le point d’exclamation, tout autre chaîne de caractères, selon votre convenance, fera parfaitement l’affaire :

mysql> delimiter T*t& mysql> SELECT pas_plantage() T*t& +--------------------------+ | pas_plantage()           | +--------------------------+ | et hop, plus de plantage | +--------------------------+ 1 row IN SET (0.00 sec) mysql>

4. Les tours de Hanoï

Il est temps de revenir à l’idée première de cet article : écrire un jeu de tours de Hanoï en utilisant des procédures stockées.

Avant de rentrer dans le détail il n’est pas inutile de rappeler les règles du jeu[2]. Sur un socle sont fixées trois axes verticaux. Sur le premier axe, on enfile des disques percés dans l’ordre décroissant. Le but du jeu est de faire passer tous les disques sur une autre tige en respectant les deux contraintes suivantes :

  • On ne peut déplacer les disques que un par un et en les plaçant toujours sur un axe.
  • Aucun disque ne doit être posé sur un disque plus petit.

La première chose que nous devons faire consiste à représenter le plateau du jeu sous forme de table dans la base. Pour chaque disque nous stockerons sa valeur et sa représentation, sachant que le plus petit disque aura comme valeur 1, celui de taille juste supérieure 2 et ainsi de suite. Nous représenterons les disques graphiquement en utilisant une succession de signes égales (=) et des crochets ([]) pour les bords. Un disque est placé sur une ligne donc dans le cas ou nous avons trois disques, nous avons trois lignes dans la table.

Du point de vue pratique, nous appellerons notre table hanoi. Les colonnes servant à conserver les valeurs des disques seront notées h_one_value, h_two_value et h_three_value pour les trois axes respectivement. Dans le même ordre d’idée, nous utiliserons des colonnes nommées h_one_data, h_two_data et h_three_data pour les représentations des disques. Pour des raisons que nous expliquerons plus tard nous ajouterons une colonne hanoi_id donnant le numéro de chaque ligne. Pour le moment retenons simplement que plus l’identifiant d’une ligne est grand, plus elle est “basse” sur le plateau.

Nous pouvons donc écrire le début de notre script avec la définition de la table hanoi.

hanoi.sql :

     1 DROP TABLE IF EXISTS hanoi;       2 CREATE TABLE hanoi (       3   hanoi_id     INTEGER NOT NULL,       4   h_one_val    INTEGER NOT NULL DEFAULT 9999,       5   h_one_data   VARCHAR(128) DEFAULT '',       6   h_two_val    INTEGER NOT NULL DEFAULT 9999,       7   h_two_data   VARCHAR(128) DEFAULT '',       8   h_three_val  INTEGER NOT NULL DEFAULT 9999,       9   h_three_data VARCHAR(128) DEFAULT ''      10 );

Si nous nous amusons à remplir cette table à la main pour trois disques en position de départ, nous obtiendrons ceci :

mysql> SELECT * FROM hanoi; +----------+-----------+------------+-----------+------------+-------------+--------------+ | hanoi_id | h_one_val | h_one_data | h_two_val | h_two_data | h_three_val | h_three_data | +----------+-----------+------------+-----------+------------+-------------+--------------+ |        3 |         3 |  [=======] |      9999 |            |        9999 |              | |        2 |         2 |   [=====]  |      9999 |            |        9999 |              | |        1 |         1 |    [===]   |      9999 |            |        9999 |              | +----------+-----------+------------+-----------+------------+-------------+--------------+ 3 rows IN SET (0.00 sec)

Comme vous pouvez le remarquer, j’ai considéré qu’un “disque absent” d’un axe à pour valeur 9999.

Si nous souhaitons maintenant visualiser le jeu sans “pollution” il suffit d’afficher uniquement les colonnes “data” ordonnées par hanoi_id (d’où son intérêt). Comme nous pourrons être amenés à faire cela fréquemment, nous allons créer une procédure hanoi_view permettant d’afficher le plateau du jeu :

hanoi.sql (suite) :

    12 delimiter !      ...      26 DROP PROCEDURE IF EXISTS hanoi_view;      27 CREATE PROCEDURE hanoi_view()      28 BEGIN      29   SELECT h_one_data "Depart (1)", h_two_data "(2)", h_three_data "Arrive (3)" FROM hanoi ORDER BY hanoi_id;      30 END      31 !

Vérifions…

mysql> call hanoi_view()! +------------+------+------------+ | Depart (1) | (2)  | Arrive (3) | +------------+------+------------+ |    [===]   |      |            | |   [=====]  |      |            | |  [=======] |      |            | +------------+------+------------+ 3 rows IN SET (0.00 sec) Query OK, 0 rows affected (0.00 sec)

Pour jouer nous allons créer deux procédures. La première servant à initialiser le plateau en précisant avec combien de disque nous souhaitons jouer et la seconde à déplacer un disque en donnant le numéro de la colonne de départ et celle d’arrivée.

Cette procédure d’initialisation, que nous appellerons hanoi_start, prend en paramètre le nombre de disques et remplie la table hanoi dans la configuration de départ. Elle commencera par effacer le contenu de la table puis, pour chaque disque, elle insérera dans la colonne h_one_value la valeur du disque et dans h_one_data sa représentation .De plus, la valeur de hanoi_id sera mise à jour pour chaque ligne.

hanoi.sql (suite) :

    33 DROP PROCEDURE IF EXISTS hanoi_start!      34 CREATE PROCEDURE hanoi_start( i INTEGER )      35 BEGIN      36   DECLARE tower_data VARCHAR(128);      37   DECLARE tower_max INTEGER;      38   DECLARE tower_size INTEGER;      39   DECLARE tower_blank INTEGER;      40      41   DELETE FROM hanoi;      42      43   SET tower_max = i * 2 + 5;      44   WHILE i > 0 DO      45      46     SET tower_size  = i * 2 + 3;      47     SET tower_blank = ( tower_max - tower_size ) / 2;      48      49     SET tower_data = CONCAT( REPEAT( " ", tower_blank ), "[", REPEAT( "=", tower_size ), "]", REPEAT( " ", tower_blank ) );      50     INSERT INTO hanoi ( hanoi_id, h_one_val, h_one_data ) VALUES ( i, i, tower_data );      51      52     SET i = i - 1;      53      54   END WHILE;      55      56   CALL hanoi_view();      57 END      58 !

Pour construire la représentation d’un disque nous utilisons trois variables :

  • tower_size qui est la taille du disque.
  • tower_blank qui représente le nombre d’espaces à placer avant et après une représentation de disque afin de la centrer et donc donner une impression de tour.
  • tower_max et la taille du disque à laquelle on additionne le nombre d’espaces avant et après la représentation.

Le schéma suivant explique tout cela beaucoup plus clairement (et il donne un côté sérieux à cet article ;)) :

La déclaration d’une variable se fait en utilisant le mot clé DECLARE suivie du nom de la variable puis de son type. Pour affecter une valeur à une variable nous utilisons SET suivi de l’opération d’affectation. Vous noterez aussi l’utilisation de l’instruction WHILE…END WHILE pour boucler sur le nombre de disques. Nous pouvions aussi bien employer l’instruction LOOP…END LOOP ce qui aurait donné :

... DECLARE j INTEGER; ... SET j = 0; label: LOOP   SET j = j + 1;   SET tower_size  = j * 2 + 3;   ...   IF j <= i THEN     ITERATE label;   END IF;   LEAVE label; END LOOP label; ...

Petite remarque avant d’aller plus loin. Nous avons défini comme taille maximum des colonnes de data 128 caractères. Or la taille maximale d’un disque étant égale à i*2+5 nous ne pourrons pas jouer avec plus de 61 disques (i = (128 - 5)/2 = 61,5).

Nous l’avons déjà dis, la procédure de déplacement de disque prendra deux paramètres en entrée : l’axe de départ et celui d’arrivé. Pour chacun cette information sera donnée par le numéro de la colonne tel qu’il est donné par le résultat de la procédure d’affichage du plateau (hanoi_view).

hanoi.sql (suite) :

    60 DROP PROCEDURE IF EXISTS hanoi_play;      61 CREATE PROCEDURE hanoi_play( d INTEGER, a INTEGER )

Nous définissons ensuite un certain nombre de variables dont le rôle sera expliqué par la suite.

hanoi.sql (suite) :

    62 BEGIN      63   DECLARE start_val INTEGER;      64   DECLARE end_val INTEGER;      65      66   DECLARE one_val INTEGER;      67   DECLARE two_val INTEGER;      68   DECLARE three_val INTEGER;      69      70   DECLARE start_id INTEGER;      71   DECLARE end_id INTEGER;      72      73   DECLARE one_id INTEGER;      74   DECLARE two_id INTEGER;      75   DECLARE three_id INTEGER;      76      77   DECLARE cur_max CURSOR FOR SELECT COUNT(*) FROM hanoi;      78   DECLARE tower_max INTEGER;      79   DECLARE tower_data VARCHAR(128);      80   DECLARE tower_size INTEGER;      81   DECLARE tower_blank INTEGER;

Tout ceci est relativement sans surprise. Nous avons globalement deux types de variables : INTEGER et VARCHAR. Le choix de définir la variable cur_max de type CURSOR et ici purement arbitraire et n’a pour seul objectif que de vous présenter deux solutions pour affecter une valeur à une variable.

La première méthode consiste à utiliser un curseur. (ligne 77) cur_max à été déclaré curseur pour SELECT COUNT(*) FROM hanoi;. Donc la récupération du résultat de cette requête se fait en ouvrant le curseur (ligne 83) puis en stockant le résultat dans une variable (ligne 84) avant de pouvoir l’utiliser (ligne 85).

hanoi.sql (suite) :

    82      83   OPEN cur_max;      84   FETCH cur_max INTO tower_max;      85   SET tower_max = tower_max * 2 + 5;

L’utilisation d’un curseur n’a aucun intérêt ici puisque nous somme assuré que la requête ne renverra toujours qu’une seule ligne de résultat. L’utilisation de curseurs prend tout son sens lorsque nous souhaitons récupérer des données via une requête qui est susceptible de retourner plusieurs lignes. Ce cas ne se présentant pas dans le jeu, voyons cela sur un petit exemple.

Imaginons que nous avons une table d’employés et que nous souhaitons faire une modification particulière (augmentation de salaire de 10% pour tous par exemple) sur tous les enregistrements de cette table, sans distinction. Pour cela, nous pouvons créer une procédure utilisant un curseur permettant de récupérer et manipuler chaque enregistrement :

CREATE PROCEDURE upgradesalaire( percent INTEGER ) BEGIN   DECLARE done INT DEFAULT 0;   DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;   DECLARE cur CURSOR FOR SELECT nom, prenom, salaire FROM employes;   DECLARE fname, lname VARCHAR(128);   DECLARE salary INT;   OPEN cur;   REPEAT     FETCH cur INTO fname, lname, salary;     IF NOT done THEN       SET salary = salary * (100 + percent) / 100;       UPDATE employes SET salaire = salary WHERE nom = fname AND prenom = lname;     END IF;   UNTIL done END REPEAT;   CLOSE cur; END

La seconde méthode, pour récupérer une valeur stockée dans une table consiste à utiliser un SELECT … INTO. C’est certainement beaucoup plus simple dans notre cas (lignes 87 à 94) :

hanoi.sql (suite) :

    86      87   SELECT MIN( h_one_val ) INTO one_val FROM hanoi;      88   SELECT MAX( hanoi_id ) INTO one_id FROM hanoi WHERE h_one_val = one_val;      89      90   SELECT MIN( h_two_val ) INTO two_val FROM hanoi;      91   SELECT MAX( hanoi_id ) INTO two_id FROM hanoi WHERE h_two_val = two_val;      92      93   SELECT MIN( h_three_val ) INTO three_val FROM hanoi;      94   SELECT MAX( hanoi_id ) INTO three_id FROM hanoi WHERE h_three_val = three_val;

Listons l’ensemble des valeurs que nous avons. Dans tower_max nous avons la taille maximale d’un disque (espaces d’alignement compris), dans one_val, two_val et three_val nous avons la valeur du plus petit disque de chaque axe et dans one_id, two_id et three_id nous avons l’identifiant de la ligne de chacun de ces disques.

Grâce à ces valeurs, nous somme en mesure de déterminer la valeur (start_val) et la position/ligne (start_id) du disque dont on demande le déplacement. Ainsi que celles sur lequel il ira se placer (end_val, end_id) :

hanoi.sql (suite) :

    95      96   IF d = 1 THEN      97     SET start_val = one_val;      98     SET start_id = one_id;      99   ELSEIF d = 2 THEN     100     SET start_val = two_val;     101     SET start_id = two_id;     102   ELSEIF d = 3 THEN     103     SET start_val = three_val;     104     SET start_id = three_id;     105   ELSE     106     SET start_val = -1;     107   END IF;     108     109   IF a = 1 THEN     110     SET end_val = one_val;     111     SET end_id = one_id;     112   ELSEIF a = 2 THEN     113     SET end_val = two_val;     114     SET end_id = two_id;     115   ELSEIF a = 3 THEN     116     SET end_val = three_val;     117     SET end_id = three_id;     118   ELSE     119     SET end_val = -1;     120   END IF;     121

Pour respecter les règles, nous devons ensuite contrôler que le joueur n’essaye pas de faire un déplacement impossible. Ceci peut arriver :

  • S’il essaye de déplacer un disque là où il n’y en à pas (start_val = 9999).
  • Si la position de départ est identique à celle d’arrivé (d = a), ce n’est pas vraiment une infraction aux règles du jeu, mais c’est un coup inutile.
  • Si le numéro de l’axe de départ, ou celui d’arrivé, est invalide (start_val = -1 or end_val = -1). Même remarque, il n’y à pas tentative de tricherie, simplement incompréhension des règles…
  • Si on essaye de placer un disque sur un autre plus petit (start_var > end_val).

hanoi.sql (suite) :

   122   IF start_val = 9999 OR d = a OR start_val = -1 OR end_val = -1 THEN     123     CALL hanoi_msg( "Coup impossible !" );     124   ELSEIF start_val <> 9999 AND start_val > end_val THEN     125     CALL hanoi_msg( "Coup impossible !!!" );     126   ELSE

Si les règles du jeu sont respectées, nous pouvons modifier la table hanoi de façon à présenter la nouvelle situation. Comme dans la procédure d’initialisation nous stockons dans une variable la représentation du disque à déplacer (tower_data). Les lignes 131 à 133 permettent de s’assurer que l’on ne va pas “fusionner” le disque déplacé avec celui sur lequel on souhaiterait le poser.

hanoi.sql (suite) :

   127     SET tower_size  = start_val * 2 + 3;     128     SET tower_blank = ( tower_max - tower_size ) / 2;     129     SET tower_data = CONCAT( REPEAT( " ", tower_blank ), "[", REPEAT( "=", tower_size ), "]", REPEAT( " ", tower_blank ) );     130     131     IF end_val <> 9999 THEN     132       SET end_id = end_id - 1;     133     END IF;     134

Le déplacement proprement dit se fait en supprimant les données du disque déplacé de sa position de départ puis en mettant à jour les données à la position d’arrivée, dans la table hanoi.

hanoi.sql (suite) :

   135     IF d = 1 THEN     136       UPDATE hanoi SET h_one_data = '', h_one_val = 9999 WHERE hanoi_id = start_id;     137     ELSEIF d = 2 THEN     138       UPDATE hanoi SET h_two_data = '', h_two_val = 9999 WHERE hanoi_id = start_id;     139     ELSEIF d = 3 THEN     140       UPDATE hanoi SET h_three_data = '', h_three_val = 9999 WHERE hanoi_id = start_id;     141     END IF;     142     143     IF a = 1 THEN     144       UPDATE hanoi SET h_one_data = tower_data, h_one_val = start_val WHERE hanoi_id = end_id;     145     ELSEIF a = 2 THEN     146       UPDATE hanoi SET h_two_data = tower_data, h_two_val = start_val WHERE hanoi_id = end_id;     147     ELSEIF a = 3 THEN     148       UPDATE hanoi SET h_three_data = tower_data, h_three_val = start_val WHERE hanoi_id = end_id;     149     END IF;     150

Nous terminons en affichant la nouvelle configuration du plateau de jeu via la procédure hanoi_view.

hanoi.sql (suite) :

   151     CALL hanoi_view();     152   END IF;     153 END     154 !

Ceux d’entre vous qui ont suivi attentivement auront remarqué l’appel à hanoi_msg permettant d’afficher un message dans le cas ou un coup impossible est tenté.

hanoi.sql (suite) :

    14 DROP FUNCTION IF EXISTS hanoi_msg_f;      15 CREATE FUNCTION hanoi_msg_f( m TEXT ) RETURNS TEXT      16   RETURN CONCAT( m );      17 !      18      19 DROP PROCEDURE IF EXISTS hanoi_msg;      20 CREATE PROCEDURE hanoi_msg( m TEXT )      21 BEGIN      22   SELECT hanoi_msg_f( m ) "Message";      23 END      24 !

Nous pourrions encore faire quelques améliorations à notre jeu tel que donner la possibilité de détecter que la solution a été trouvée ou permettre de demander à voir la solution. Je vous laisse ce plaisir en vous faisant tout de même remarquer que pour le moment (mais cela aura peut être changé quand vous lirez ces lignes) MySQL ne support par la récursivité dans les procédures stockées.

5. Et les triggers

Tout le monde l’aura compris, l’exemple que nous venons de voir n’a d’autre intérêt que de montrer comment écrire des procédures stockées. Un tel déploiement d’efforts prend tout son sens quand nous voulons créer des triggers. Malheureusement je ne pourrais pas vous en parler. En effet la documentation nous informe qu’ils ne seront, pour le moment, pas disponibles avant la version 5.1. Je laisse à un autre (qui sera peut-être moi) le plaisir de vous en reparler plus tard.

6. Coquille

Avant de clore cet article, je voudrais remercier Jérôme Delamarche qui m’a fait remarquer la présence d’une coquille dans l’article sur la réplication avec MySQL paru dans le HS n°18. En effet, contrairement à ce que j’ai écrit, il est tout à fait possible de faire de la réplication spécifique, soit en choisissant les tables et/ou bases à répliquer. Mais ceci se fait au niveau du fichier de configuration de la base (le fameux my.cnf) de la base esclave. Pour plus de détails je vous renvoie à la documentation [3].


[1] http://www.mysql.com/documentation/mysql/bychapter/manual_Column_types.html
[2] http://perso.wanadoo.fr/robert.mellet/jeux/hanoi_01.htm
[3] http://www.nexen.net/docs/mysql/annotee/replication-options.php?lien=replication

• • •

Pas de commentaire »

Pas encore de commentaire.

RSS des commentairesTrackBack URI

Laisser un commentaire

You must be logged in to post a comment.

Powered by: WordPress • Template adapted from the Simple Green' Wench theme - RSS