Il vous est sans doute déjà arrivé de devoir renommer une base de données. Par exemple, parce que vous constatez après l'avoir créée que vous vous êtes trompé dans son nom. Ou parce qu'au cours du développement votre base a évolué et que son nom ne reflète plus la réalité de votre projet. Ou pour plein d'autres raisons qui vous sont personnelles...
Si vous relisez la première phrase de cet article, vous verrez que j'ai utilisé le verbe devoir. Mais peut-être que vouloir aurait été préférable? En tout cas, comme MySQL ne dispose pas d'un utilitaire permettant d'effectuer cette opération, cela reste trop souvent un vœu pieux. Pour vous aider à concrétiser vos envies, nous allons voir dans cet article comment renommer une base MySQL.
RENAME DATABASE
Avant d'aller plus loin un avertissement: Entre les versions 5.1.7 et 5.1.23, MySQL a possédé une requête SQL RENAME DATABASE. L'utilisation de cette requête est fortement déconseillée [1] car elle peut entraîner des pertes de données!
Attention:
On m'a rapporté que la solution présentée ici semble ne pas fonctionner avec les tables InnoDB (?). Voyez dans la section "Ressources" à la fin de cet article pour des solutions alternatives.
Sommaire
Qu'est-ce qu'une base MySQL?
Sur le disque, une base de données MySQL, n'est rien de plus qu'une collection de fichiers stockés dans un dossier [2]. Selon votre système d'exploitation et la manière dont vous avez installé MySQL, l'emplacement de ce dossier varie. Par exemple, sous Debian/Lenny – avec MySQL5 installé à partir des paquets, les bases sont dans /var/lib/mysql. Une recherche sur votre disque dur devrait vous permettre de trouver sans trop de difficulté l'emplacement correspondant à votre installation.
Côte mysql
Afin d'examiner de plus près le contenu de ce dossier, nous allons nous appuyer sur un exemple:
sh$ mysql -u root -p Enter password: mysql> CREATE DATABASE MaBDD; Query OK, 1 row affected (0.06 sec) mysql> SHOW DATABASES; +--------------------+ | Database | +--------------------+ | information_schema | | MaBDD | | mysql | +--------------------+ 3 rows in set (0.08 sec) mysql> CREATE TABLE MaBDD.MaTable (MaColonne INT); Query OK, 0 rows affected (0.03 sec) mysql> GRANT SELECT,INSERT,UPDATE ON MaBDD.* TO MonUtilisateur IDENTIFIED BY "password"; Query OK, 0 rows affected (0.04 sec)
Vous savez que les requêtes précédentes ont respectivement créé une base de données et une table dans celle-ci. J'ai aussi créé un utilisateur qui peut lire, insérer ou modifier des données:
sh$ mysql -u MonUtilisateur -p
Enter password:
mysql> INSERT INTO MaBDD.MaTable VALUES(1);
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM MaBDD.MaTable;
+-----------+
| MaColonne |
+-----------+
| 1 |
+-----------+
1 row in set (0.00 sec)
Sur le disque
Allons voir maintenant dans /var/lib/mysql (ou l'emplacement correspondant sur votre système) ce qui s'est passé suite aux opérations effectuées précédemment:
sg$ ls -l /var/lib/mysql
total 20532
-rw-r--r-- 1 root root 0 2010-02-10 14:21 debian-5.0.flag
-rw-rw---- 1 mysql mysql 10485760 2010-02-10 14:21 ibdata1
-rw-rw---- 1 mysql mysql 5242880 2010-02-10 14:21 ib_logfile0
-rw-rw---- 1 mysql mysql 5242880 2010-02-10 14:21 ib_logfile1
drwx------ 2 mysql mysql 4096 2010-02-10 14:54 MaBDD
drwxr-xr-x 2 mysql root 4096 2010-02-10 14:21 mysql
-rw------- 1 root root 7 2010-02-10 14:21 mysql_upgrade_info
Comme vous le constatez un dossier portant le nom MaBDD est présent. C'est dans ce dossier que sont stockées les données (et les méta-informations) de la base que je viens de créer. Remarquez aussi le dossier mysql. C'est lui qui contient la base de données administrative mysql (celle qui, entre autre, gère les permissions des utilisateurs). Vous voyez aussi un certain nombre de fichiers de journaux pour le moteur InnoDB, et pour le fonctionnement interne de MySQL. Plus, un marqueur installé par le paquet MySQL de Debian.
Mais revenons sur le dossier qui nous intéresse en premier lieu ici: /var/lib/mysql/MaBDD:
sh# ls -l /var/lib/mysql/MaBDD/ total 20 -rw-rw---- 1 mysql mysql 65 2010-02-10 14:52 db.opt -rw-rw---- 1 mysql mysql 8570 2010-02-10 14:54 MaTable.frm -rw-rw---- 1 mysql mysql 0 2010-02-10 14:54 MaTable.MYD -rw-rw---- 1 mysql mysql 1024 2010-02-10 14:54 MaTable.MYI
Le contenu de ce dossier va dans le sens des explications données jusqu'à présent. Ainsi, on y trouve les fichiers correspondants à la table que j'ai créé.
Renommer la base/le dossier
Les manipulations précédentes avaient pour but d'illustrer ce qui est expliqué dans la documentation de MySQL – à savoir qu'une base MySQL correspond à un dossier. Du coup, on comprend qu'il est possible de renommer une base MySQL simplement en renommant le dossier qu'elle contient:
sh# mv /var/lib/mysql/MaBDD /var/lib/mysql/UneBDD
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| UneBDD |
| mysql |
+--------------------+
3 rows in set (0.00 sec)
On essaye notre nouvelle base?
sh$ mysql -u MonUtilisateur -p Enter password: mysql> INSERT INTO MaBDD.MaTable VALUES(1); Query OK, 1 row affected (0.00 sec)
Hein? Ça marche avec l'ancien nom de la base? Mais alors, j'ai renommé ma base ou pas? Ré-essayons – mais cette fois-ci après avoir redémarré MySQL:
sh# /etc/init.d/mysql restart Stopping MySQL database server: mysqld. Starting MySQL database server: mysqld. Checking for corrupt, not cleanly closed and upgrade needing tables..
sh$ mysql -u MonUtilisateur -p Enter password: mysql> INSERT INTO MaBDD.MaTable VALUES(1); ERROR 1146 (42S02): Table 'MaBDD.MaTable' doesn't exist
Comment ça? Un coup ça marche, un coup ça marche pas? La différence entre ces deux exemples est que j'ai redémarré le serveur. En effet, la première tentative de connexion a fonctionné parce que le serveur avait déjà ouvert les fichiers de la base. Dans ces conditions, renommer le dossier qui contient ces fichiers est relativement transparent. Par contre, quand on redémarre le serveur, évidemment les tables ne sont plus ouvertes. Et là, par contre, le nom de la base est important pour retrouver ces fameux fichiers.
Remarque:
J'aurais pu aussi forcer la fermeture des tables avec la requête FLUSH TABLES. En production – ou même sur une machine de développement hébergeant plusieurs bases – c'est sans doute plus élégant!
Qu'à cela ne tienne, tentons d'insérer avec la bonne base:
mysql> INSERT INTO UneBDD.MaTable VALUES(1); ERROR 1142 (42000): INSERT command denied to user 'MonUtilisateur'@'localhost' for table 'MaTable'
Et oui! MonUtilisateur a les permissions sur MaBDD, pas sur UneBDD...
Permissions
Ainsi, le serveur MySQL n'a pas automatiquement transféré les permissions vers la nouvelle base. Remarquez que cela se comprend, vu que le renommage a plus ou moins eu lieu dans le dos du serveur! C'est donc à vous de transférer les droits. Là deux écoles. Avec toutes deux leurs défauts:
- recréer toutes les permissions à la main
- c'est fastidieux – et source d'erreur. En plus, après avoir recréé les permissions il faudra aussi penser à supprimer celles sur l'ancienne base.
- migrer les permissions
- avec cette solution on met à jour la table des permissions en changeant l'ancien nom de la base par le nouveau. Avec pour inconvénient qu'il n'est pas forcément prudent d'aller modifier sauvagement les tables administratives de MySQL – et que cette technique est susceptible de devenir obsolète le jour ou MySQL AB décidera de changer la manière de gérer les permissions...
Avec toutes les réserves évoquées, c'est tout de même la seconde option que je vais détailler. En effet, le première n'étant qu'une succession de GRANT et REVOKE, elle ne présente que peu d'intérêt à étudier.
Bref, pour gérer les permissions, MySQL utilise des données stockées dans un certain nombre de tables: mysql.db, mysql.host, mysql.tables_priv, mysql.columns_priv, mysql.procs_priv [3]. Il va donc falloir modifier dans chacune de ces tables le nom de la base de données pour refléter les changements effectués au niveau du dossier dans /var/lib/mysql:
mysql> UPDATE mysql.db SET Db='UneBDD' WHERE Db='MaBDD'; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> UPDATE mysql.host SET Db='UneBDD' WHERE Db='MaBDD'; Query OK, 0 rows affected (0.00 sec) Rows matched: 0 Changed: 0 Warnings: 0 mysql> UPDATE mysql.tables_priv SET Db='UneBDD' WHERE Db='MaBDD'; Query OK, 0 rows affected (0.00 sec) Rows matched: 0 Changed: 0 Warnings: 0 mysql> UPDATE mysql.columns_priv SET Db='UneBDD' WHERE Db='MaBDD'; Query OK, 0 rows affected (0.00 sec) Rows matched: 0 Changed: 0 Warnings: 0 mysql> UPDATE mysql.procs_priv SET Db='UneBDD' WHERE Db='MaBDD'; Query OK, 0 rows affected (0.00 sec) Rows matched: 0 Changed: 0 Warnings: 0
Remarque:
Selon les types de permissions et les objets concernés, certaines des tables précédentes peuvent être ou ne pas être utilisées. C'est le cas dans mon exemple très basique où seule la table mysql.db a été réellement modifiée. Ailleurs aucune ligne n'a été affectée par l'UPDATE.
Néanmoins, c'est une bonne idée de systématiquement (tenter d') altérer chacune de ces tables: ça ne coûte rien, et ça peut éviter les oublis malheureux.
Dernière opération, après avoir modifié manuellement les tables des permissions, il est conseillé de forcer le serveur à les recharger:
mysql> FLUSH PRIVILEGES; Query OK, 0 rows affected (0.00 sec)
Ressource
Des solutions alternatives:
- http://lists.mysql.com/mysql/206163
- http://lists.mysql.com/mysql/203793
- http://lists.mysql.com/mysql/217005
- http://dev.mysql.com/doc/refman/5.1/en/rename-database.html – Pourquoi il ne faut pas utiliser RENAME DATABASE
- http://dev.mysql.com/doc/refman/5.1/en/create-database.html – "A database in MySQL is implemented as a directory containing files that correspond to tables in the database"
