Les prepared statements côté serveur sont une des nouvelles fonctionnalités les plus intéressantes
de MySQL 4.1 (récemment sorti en production release 4.1.7).
Dans cet article, nous verrons ce que sont les prepared
statements, pour quelles raisons, quand et comment les utiliser.
1. Que sont les prepared statements côté serveur ?
Les prepared statements sont un dispositif permettant de mettre en place (" préparer ") une requête une fois pour toutes, puis de l'exécuter plusieurs fois avec des paramètres différents.
Ils ont pour but de remplacer les requêtes classiques, en étant plus sûrs et plus efficaces. Un prepared statement typique ressemble à ceci :
SELECT * FROM Pays WHERE code = ?
Le point d'interrogation est ce qu'on appelle un paramètre fictif (placeholder). Lorsque la requête ci-dessus est exécutée, vous devez spécifier la valeur qui vient remplacer le ? dans cette requête.
2. Pourquoi utiliser les prepared statements ?
Il y a de nombreux avantages à utiliser des prepared statements dans vos applications, à la fois en termes de sécurité et de performance.
Les prepared statements peuvent contribuer à améliorer la sécurité en séparant la logique SQL des données fournies.
Cette séparation peut aider à prévenir un type très courant de vulnérabilité appelée attaque par injection de code SQL. En temps normal, lorsque vous avez affaire à une requête classique, il vous faut être très vigilant en manipulant les données envoyées par l'utilisateur. Cela implique d'utiliser des fonctions qui échappent tous les caractères pouvant poser problème, comme les guillemets simples, doubles et les anti-slashes.
C'est inutile dans le cas des prepared statements. La séparation des données permet à MySQL de prendre automatiquement en compte ces caractères et ils n'ont pas besoin d'être échappés par une fonction quelconque.
Le gain de performance dû aux prepared statements peut avoir plusieurs origines différentes.
Tout d'abord, il n'est nécessaire de parser la requête qu'une seule fois. Au début, lorsque vous préparez le statement, MySQL analyse l'instruction pour vérifier la syntaxe et met en place la requête à exécuter.
Par la suite, si vous exécutez la requête plusieurs fois, il n'y aura plus ce préalable. Ce pré-parsing peut engendrer un gain de rapidité si vous devez effectuer la même requête de nombreuses fois, par exemple lorsque vous faites beaucoup d'INSERT.
(Note : Bien que ça ne soit pas le cas avec MySQL 4.1, les versions futures mettront aussi en cache le plan d'exécution des prepared statements, ce qui éliminera une autre partie des opérations préalables à l'exécution de chaque requête.)
Le deuxième facteur qui peut faire augmenter les performances est un nouveau protocole binaire utilisé par les prepared statements. Le protocole traditionnel de MySQL convertit toujours les données en des chaînes de caractères qui sont souvent plus longues que les données originales, les envoie à travers le réseau (ou autres) vers le serveur, qui décode finalement la chaîne dans le type de données correct. Le protocole binaire supprime ce passage obligé par la conversion. Tous les types sont envoyés dans leur forme binaire native, ce qui économise le temps CPU de conversion, et peut aussi diminuer l'utilisation réseau.
3. Quand utiliser des prepared statements ?
Les prepared statements peuvent être utiles pour toutes les raisons évoquées ci-dessus, toutefois ils ne devraient (et ne peuvent) pas être utilisés pour tout et n'importe quoi dans votre application. Tout d'abord, le type de requête sur lequel ils marchent se limite au DML (Data Manipulation Language) : INSERT, REPLACE, UPDATE, et DELETE, ainsi qu'aux requêtes CREATE TABLE et SELECT. La gestion d'autres types de requêtes sera ajoutée dans les versions futures afin de rendre l'API pour les prepared statements plus générique.
Parfois, les prepared statements peuvent en fait être plus lents que des requêtes normales. En effet, il y a deux aller-retours jusqu'au serveur (un pour la préparation, l'autre pour l'exécution), ce qui peut ralentir des requêtes simples exécutées seulement une fois. Dans des cas comme celui-ci, à vous de décider si les bénéfices des prepared statements en termes de sécurité valent l'impact sur les performances de cet aller-retour supplémentaire.
4. Comment utiliser les prepared statements ?
La plupart des langages de programmation couramment utilisés avec MySQL supportent déjà les prepared statements côté serveur, et beaucoup d'autres les intègreront bientôt.
La première API dans laquelle les prepared statements ont été implémentés est l'API C. Elle a subi quelques changements au cours de ses cycles de vie alpha et beta, mais elle est maintenant considérée comme stable pour une utilisation courante. Vous trouverez plus de renseignements sur l'API C dans la section "C API Prepared Statements" du manuel de référence de MySQL.
Une autre API qui supporte les prepared statements est PHP. PHP 5 possède une nouvelle interface pour MySQL appelée "mysqli". Pour plus de précisions sur celle-ci, veuillez consulter la section mysqli du manuel PHP. L'API fournie par l'extension mysqli ressemble presque trait pour trait à l'API C, il pourrait donc aussi être utile de consulter la documentation de cette dernière.
1. Un exemple de prepared statement avec PHP
// Connexion
$conn = mysqli_connect("localhost", "user", "password", "base");
$code = "FIN";
// Préparation du statement
if ($stmt = mysqli_prepare($conn, " SELECT nom FROM Pays WHERE code = ?")) {
// Attachement des paramètres et exécution
mysqli_stmt_bind_param($stmt, "s", $code);
mysqli_stmt_execute($stmt);
// Récupération et affichage du résultat
mysqli_stmt_bind_result($stmt, $nom);
mysqli_stmt_fetch($stmt);
echo"Nom du Pays : ". $nom;
mysqli_stmt_close($stmt);
}
mysqli_close($conn);
Ceux d'entre vous qui utilisent les langages Java ou Perl disposent de prepared statements depuis assez longtemps. Cependant, ceux-ci étaient des prepared statements côté client. Les prepared statements côté client fournissent le même gain de sécurité, mais pas d'augmentation des performances. Ne vous inquiétez pas pour autant, MySQL Connector/J supporte les prepared statements côté serveur dans la nouvelle version 3.1. Le driver DBD::mysql de Perl les inclura dans la prochaine release du development tree 2.9. La bonne nouvelle, c'est que votre code est déjà écrit pour les utiliser, tout ce que vous avez à faire pour profiter de cette nouvelle fonctionnalité est donc de mettre à jour le driver qui se trouve derrière.
Le nouveau provider MySQL Connector/Net pour la plateforme .NET inclut aussi le support des prepared statements (à la fois côté serveur pour une utilisation avec MySQL 4.1 et suivants, et côté client pour les versions plus anciennes du serveur).
Il n'y a pas pour l'instant de release de Connector/ODBC qui supporte les prepared statements côté serveur, mais les versions actuelles les émulent au sein du driver. Le support sera inclus dans la prochaine grosse release de Connector/ODBC, la version 3.53, actuellement en cours de développement.
Enfin, il existe aussi une interface SQL pour les prepared statements. En d'autres termes, vous pouvez faire des prepared statements sans support spécifique de l'API de votre langage de programmation. Trois nouvelles instructions SQL le permettent : PREPARE, EXECUTE, et DEALLOCATE PREPARE. Elles ne tirent pas parti du nouveau protocole binaire, et devraient donc être utilisées seulement pour des tests ou lorsqu'aucune API native n'est disponible.
2. Un exemple de prepared statement en SQL
mysql> PREPARE nom_stmt FROM"SELECT nom FROM Pays WHERE code = ?";
Query OK, 0 rows affected (0.09 sec)
Statement prepared
mysql> SET @param = "FIN";
Query OK, 0 rows affected (0.00 sec)
mysql> EXECUTE nom_stmt USING @param;
+----------+
| nom |
+----------+
| Finlande |
+----------+
1 rowinset (0.03 sec)
mysql> DEALLOCATE PREPARE nom_stmt;
Query OK, 0 rows affected (0.00 sec)
5. Conclusion
Vous devriez à présent avoir une bonne idée des avantages et inconvénients liés à l'utilisation des prepared statements. Ils peuvent être synonymes d'un code plus rapide et plus sûr si on s'en sert correctement ; en outre ils fonctionnent plutôt bien en complément des autres APIs actuellement disponibles.
Cet article a été publié dans sa version originale sur la page Tech Resources du site de MySQL (http://dev.mysql.com).
Merci à Harrisson Fisk pour m'avoir autorisé à adapter son article et à christopheJ pour la relecture de la version traduite.