Ce chapitre décrit un grand nombre de notions que vous devez connaître
lorsque vous travaillez sur le code de MySQL. Si vous envisagez de contribuer
au développement de MySQL, que vous voulez accéder à du code ultra récent,
ou que vous souhaitez simplement vous tenir au courant du développement,
suivez les instructions de la section section 2.3.4 Installer à partir de l'arbre source de développement.
Si vous êtes intéressés par les rouages internes de MySQL, il est
recommandé de vous inscrire à notre liste de diffusion interne
internals. Cette liste est relativement calme. Pour les détails
d'inscription, voyez section 1.7.1.1 Les listes de diffusions de MySQL.
Tous les développeurs de MySQL AB sont sur la liste internals et nous
aidons ceux qui travaillent sur le code de MySQL. Utilisez cette
liste pour poser des questions sur le code, et partagez vos
patchs que vous voulez voir intégré au projet MySQL.
Le serveur MySQL crée les threads suivants :
process_alarm() pour forcer les délais
d'expiration des connexions qui sont inactives.
mysqld est compilé avec l'option -DUSE_ALARM_THREAD, un thread
dédié aux alarmes est créé. Il est uniquement utilisé sur certains systèmes
où il y a des problèmes avec la fonction sigwait() ou si vous voulez
utiliser la fonction thr_alarm() dans des applications qui n'ont
pas de thread dédié aux signaux.
--flush_time=#, un thread dédié est
créé pour vider les tables de la mémoire à intervalle régulier.
INSERT DELAYED sont pratiquées
reçoit un thread.
--master-host, un thread de réplication sera démarré
pour lire et appliquer les modifications du maître.
mysqladmin processlist affiche uniquement les threads de connexion, ceux
de INSERT DELAYED et ceux de réplication.
Jusqu'à récemment, notre suite de test principale étaient basée sur des données
propriétaires de client, et pour cette raison, il n'a jamais été publié. Le seul
système de test public actuel est notre script crash-me, qui est un script
Perl DBI/DBD qui se trouve dans le dossier sql-bench, et divers tests qui
font parti du dossier tests. Le manque d'une suite de test publique et standardisée
rend difficile à nos utilisateurs et nos développeurs les possibilités de tests
de régressions. Pour corriger ce problème, nous avons créé un nouveau système
de tests qui est inclus dans les distributions source et binaires, à partir
de la version 3.23.29.
Le jeu de test actuel ne couvre pas toutes les situations en MySQL, mais il permet d'identifier les bogues les plus courants lors de requêtes SQL, les problèmes de librairies et aussi les problèmes de réplication. Notre but final est de lui faire couvrir 100% du code. Nous apprécions les contributions à notre suite de test. Vous pouvez notamment fournir des test qui examine certaines fonctions critiques de votre système, et qui assureront que les futures versions de MySQL le prennent en compte.
Le système de tests est constituté d'un interpréteur de langage de tests
(mysqltest), un script shell qui exécute tous les scripts
(mysql-test-run), les cas de tests réels, écrits dans un langage
spécial de tests, et leur résultats attendus. Pour exécuter ces tests sur votre
système après une compilation, tapez make test ou
mysql-test/mysql-test-run depuis la racine de la distribution. Si vous avez
installé une distribution binaire, cd jusqu'au dossier d'installation
(eg. /usr/local/mysql), et exécutez scripts/mysql-test-run.
Tous les tests doivent réussir. Si ce n'est pas le cas, vous devriez
essayer de trouver pourquoi, et faire un rapport de bogues à MySQL.
See section 9.1.2.3 Rapporter des bugs dans la suite de tests MySQL.
Si vous avez une copie de mysqld qui fonctionne sur la machine où vous voulez
faire des tests, vous n'avez pas à l'arrêter, tant qu'elle n'utilise pas les ports
9306 et 9307. Si l'un de ces ports est pris, vous devriez éditer le script
mysql-test-run et changer les valeurs des ports des maîtres et esclaves, en les remplaçant
par des ports libres.
Vous pouvez exécuter des tests individuels avec
mysql-test/mysql-test-run test_name.
Si l'un des tests échoue, vous devriez tester mysql-test-run avec
l'option --force pour vérifier si aucun autre test n'échoue.
Vous pouvez utiliser le langage de mysqltest pour écrire vos propres
cas de tests. Malheureusement, nous n'avons pas encore écrit une documentation
complète pour ce logiciel, et nous prévoyons de le faire rapidement. Vous pouvez,
toutefois, utiliser les cas de tests actuels comme exemples. Les points suivants
devraient vous mettre le pied à l'étrier.
mysql-test/t/*.test
;,
et est similaire aux données d'entrées du client mysql. Une commande
est par défaut une commande envoyée au serveur MySQL, à moins qu'il ne soit
reconnu comme une commande interne (par exemple, sleep).
SELECT, SHOW,
EXPLAIN, etc., doivent être précédées par @/path/to/result/file.
Le fichier contient alors les résultats attendus. Un moyen simple pour générer
le résultat du fichier est d'exécuter mysqltest -r < t/test-case-name.test
depuis le dossier de tests mysql-test, puis d'éditer le fichier résultant,
si nécessaire, pour ajuster le contenu. Dans ce cas, soyez très prudent lors de
l'ajout ou la suppression de caractères invisibles : assurez vous de ne changer
que du texte, ou d'effacer des lignes. Vous pouvez utiliser od -c pour vous
assurer que votre éditeur n'a pas perturbé le fichier durant l'édition. Bien sur,
nous espérons que vous n'aurez jamais a éditer le résultat du fichier
mysqltest -r ca vous n'avez à faire cela que lorsque vous découvrez un
bug.
mysql-test/r et les nommer test_name.result.
Si le test produit plus qu'un résultat, vous devez utiliser test_name.a.result,
test_name.b.result, etc.
--error error-number. Le numéro d'erreur peut être une liste
d'erreurs possibles, séparées par des virgules ','.
source include/master-slave.inc;.
Pour passer entre le maître et l'esclave, utilisez connection master; et
connection slave;. Si vous avez besoin d'utiliser une connexion alternative,
vous pouvez utiliser connection master1; pour le maître, et connection slave1;
pour l'esclave.
let $1=1000;
while ($1)
{
# votre requête ici
dec $1;
}
sleep.
Elle supporte les fraction de secondes, ce qui vous permet d'utiliser
sleep 1.3;, pour attendre 1,3 secondes.
mysql-test/t/test_name-slave.opt. Pour le maître, ajoutez les
dans mysql-test/t/test_name-master.opt.
Si votre versionn de MySQL ne passe pas un teste, vous devez faire ceci :
mysqlbug, pour que nous puissions obtenir un maximum d'informations
sur votre système et la version de MySQL. See section 1.7.1.3 Comment rapporter un bogue ou un problème.
mysql-test-run, ainsi que le contenu
de tous les fichiers .reject du dossier mysql-test/r.
cd mysql-test mysql-test-run --local test-nameSi cela échoue, alors vous devriez configurer MySQL avec
--with-debug et exécuter mysql-test-run avec l'option
--debug. Si cela échoue aussi, envoyez le fichier de trace
`var/tmp/master.trace' à ftp://support.mysql.com/pub/mysql/secret
pour que nous puissions l'examiner. N'oubliez pas d'inclure une description
complète de votre système, ainsi que de la version de l'exécutable
mysqld, et de sa compilation.
mysql-test-run avec l'option --force pour
voir si il n'y a pas d'autres tests qui échouent.
Result length mismatch ou
Result content mismatch, cela signifie que le résultat de la suite de tests
n'a pas la taille attendue. Cela peut être un bug de MySQL, ou que votre
version de MySQL fournit un résultat d'une autre taille, dans certaines
circonstances.
Les résultats de tests qui ont échoués sont placés dans un fichier avec
le même nom de base que le fichier de test, et avec l'extension .reject.
Si votre test échoue, faites un diff sur les deux fichiers. Si vous ne
pouvez pas voir où ils diffèrent, examinez ces deux fichiers avec od -c,
et vérifiez leur tailles respectives.
mysql-test/var/log, pour avoir des indices sur ce qui
a échoué.
mysql-test-run avec --gdb et/ou --debug.
See section D.1.2 Créer un fichier de traçage.
Si vous n'avez pas compilé MySQL pour le débogage, vous devriez essayer
de le faire. Spécifiez simplement l'option --with-debug dans le script
de configure! See section 2.3 Installer MySQL à partir des sources.
Il y a deux méthodes pour ajouter des fonctions à MySQL :
CREATE FUNCTION et DROP FUNCTION.
See section 9.2.1 Syntaxe de CREATE FUNCTION/DROP FUNCTION.
mysqld et
sont disponibles en permanence.
Chaque méthode a ses avantages et inconvénients :
Quelque soit la méthode que vous utilisez pour ajouter de nouvelles
fonctions, ces fonctions pourront être utilisées comme des
fonctions natives telles que ABS() ou SOUNDEX().
CREATE FUNCTION/DROP FUNCTION
CREATE [AGGREGATE] FUNCTION nom_fonction RETURNS {STRING|REAL|INTEGER}
SONAME nom_librairie_partagée
DROP FUNCTION nom_fonction
Une fonction définie par un utilisateur (UDF) est une méthode pour intégrer
une fonction qui fonctionne de la même façon qu'une fonction native de MySQL,
comme ABS() et CONCAT().
AGGREGATE est une nouvelle option pour MySQL version 3.23.
Une fonction AGGREGATE fonctionne exactement comme une fonction native
comme SUM ou COUNT().
CREATE FUNCTION enregistre le nom de la fonction, le type, et le nom des
librairies partagées dans la table mysql.func. Vous devez avoir les droits
INSERT et DELETE dans la base mysql pour créer et supprimer
les fonctions.
Toutes les fonctions actives sont rechargées chaque fois que le serveur démarre,
sauf si vous démarrez mysqld avec l'option --skip-grant-tables.
Dans ce cas, l'utilisation des UDF n'est pas prise en compte et les UDFs ne sont
pas disponibles. (Une fonction active est une fonction qui peut être chargée avec
CREATE FUNCTION et supprimée par REMOVE FUNCTION).
Concernant l'écriture des UDFs, section 9.2 Ajouter des fonctions à MySQL. Pour que le mécanisme des
fonctions UDF fonctionne, les fonctions doivent être écrites en C ou C++, votre
système doit supporter le chargement dynamique et vous devez avoir compilé
mysqld dynamiquement (pas statiquement).
Notez que pour faire fonctionner AGGREGATE, vous devez avoir une table
mysql.func qui contient la colonne type. Si ce n'est pas le cas,
vous devez exécuter le script mysql_fix_privilege_table pour résoudre
ce problème.
Pour que le mécanisme UDF fonctionne, les fonctions doivent êtres écrites en C ou C++ et votre système doit supporter le chargement dynamique. Les sources de MySQL incluent un fichier `sql/udf_example.cc' qui définit 5 nouvelles fonctions. Consultez ce fichier pour voir comment marchent les conventions d'appels des UDF.
Pour que mysqld puisse utiliser les fonctions UDF, vous devez configurer
MySQL avec l'option --with-mysqld-ldflags=-rdynamic.
La raison est que sur diverses plate-formes, (Linux inclus) vous pouvez charger
une librairie dynamique (avec dlopen()) depuis un programme statique lié,
que vous pouvez obtenir si vous utilisez l'option --with-mysql-ldflags=-all-static.
Si vous voulez utiliser une UDF qui nécessite un accès aux symboles de mysqld
(comme l'exemple methaphone dans `sql/udf_example.cc' qui utilise
default_charset_info), vous devez lier le programme avec -rdynamic
(voir man dlopen).
Pour chaque fonction que vous voulez utiliser dans SQL, vous devez définir
les fonctions correspondantes en C (ou C++). Dans la discussion ci-dessous,
le nom ``xxx'' est utilisé comme un exemple de nom de fonction. Pour faire
la différence entre l'usage de SQL et de C/C++, XXX() (majuscules)
indique l'appel d'une fonction SQL et xxx() (minuscules) indique
l'appel d'une fonction C/C++.
Les fonctions C/C++ que vous écrivez pour l'implémentation de l'interface
de XXX() sont :
xxx() (requis)
| SQL type | C/C++ type |
STRING | char *
|
INTEGER | long long
|
REAL | double
|
xxx_init() (optionnel)
xxx(). Elle peut-être utilisée pour :
XXX().
REAL) le nombre maximal de décimales.
NULL.
xxx_deinit() (optionnel)
xxx(). Elle doit dés-allouer toute
la mémoire allouée par l'initialisation de la fonction.
Quand une requête SQL fait appel à XXX(), MySQL appelle l'initialisation
de la fonction xxx_init(), pour laisser exécuter n'importe quelle action
exigée, telle que la vérification d'arguments ou l'allocation de mémoire.
Si xxx_init() retourne une erreur, la requête SQL est annulée avec un message
d'erreur et la fonction principale et la fonction de dés-initialisation ne sont pas
appelées. Autrement, la fonction principale xxx() est appelée une fois pour
chaque ligne. Après que toutes les lignes aient été traitées, la fonction de dés-initialisation
xxx_deinit() est appelée pour procéder aux nettoyages requis.
Pour les fonctions d'agrégat (comme SUM()), vous pouvez également ajouter
les fonctions suivantes :
xxx_reset() (requise)
xxx_add() (requise)
Quand vous utilisez les UDF d'agrégat, MySQL opère comme suit :
All functions must be thread-safe (not just the main function,
but the initialisation and deinitialisation functions as well). This means
that you are not allowed to allocate any global or static variables that
change! If you need memory, you should allocate it in xxx_init()
and free it in xxx_deinit().
xxx_init() pour laisser la fonction d'agrégat allouer la
mémoire dont elle aura besoin pour stocker les résultats.
GROUP BY.
xxx_reset().
xxx_add().
xxx()
pour obtenir le résultat de l'agrégat.
xxx_deinit() pour libérer la mémoire allouée.
Toutes les fonctions doivent être sûrs pour les threads (pas seulement la fonction principale, mais
les fonctions d'initialisation et de dés-initialisation également). Cela signifie que vous n'êtes
pas autorisés à allouer une variable globale ou statique qui change ! Si vous avez besoin de mémoire,
vous devez l'allouer avec la fonction xxx_init() et la libérer avec xxx_deinit().
La fonction principale doit être déclarée comme illustrée ici. Notez que le
type de retour et les paramètres diffèrent, suivant que vous voulez déclarer
une fonction SQL XXX() qui retournera une STRING, un INTEGER ou
un REAL dans la commande CREATE FUNCTION :
Pour les fonctions de chînes STRING :
char *xxx(UDF_INIT *initid, UDF_ARGS *args,
char *result, unsigned long *length,
char *is_null, char *error);
Pour les fonctions d'entiers INTEGER :
long long xxx(UDF_INIT *initid, UDF_ARGS *args,
char *is_null, char *error);
Pour les fonctions de nombres à virgule flottante REAL :
double xxx(UDF_INIT *initid, UDF_ARGS *args,
char *is_null, char *error);
Les fonctions d'initialisation et de terminaison sont déclarées comme ceci :
my_bool xxx_init(UDF_INIT *initid, UDF_ARGS *args, char *message); void xxx_deinit(UDF_INIT *initid);
Le paramètre initid est passé aux trois fonctions. Il pointe sur une
structure UDF_INIT qui est utilisée pour communiquer des informations
entre les fonctions. Les membres de la structure UDF_INIT sont ceux qui sont
listés ci-dessous. La fonction d'initialisation doit préparer les membres qu'elle
veut, et notamment leur donner une valeur initiale (pour utiliser les valeurs
par défaut, laissez le membre intact) :
my_bool maybe_null
xxx_init() doit remplacer maybe_null par 1 si xxx()
peut retourner NULL. La valeur par défaut est 1 si l'un des arguments
n'est déclaré comme maybe_null.
unsigned int decimals
1.34, 1.345, and 1.3, ce nombre sera 3, car 1.345 a 3
décimales.
unsigned int max_length
initid->decimals. Pour les fonctions numériques, la taille inclut
le signe et le séparateur décimal.
Si vous voulez retourner un BLOB, vous devez donner à ce membre la valeur
de 65K ou 16M; cet espace mémoire ne sera pas alloué, mais utilisé pour décider
quel type de colonne utiliser, si il y a un besoin de stockage temporaire.
char *ptr
initid->ptr pour transférer de la mémoire allouée entre
les trois fonctions. En xxx_init(), allouez de la mémoire, et assignez la à
ce pointeur :
initid->ptr = allocated_memory;En
xxx() et xxx_deinit(), utilisez initid->ptr pour exploiter ou
supprimer la mémoire.
Voici une description des différentes fonctions que vous devez définir pour réaliser des calculs sur des regroupements, avec une fonction utilisateur :
char *xxx_reset(UDF_INIT *initid, UDF_ARGS *args,
char *is_null, char *error);
Cette fonction est appelée lorsque MySQL trouve la première ligne dans un nouveau groupe. Dans cette fonction, vous devez remettre à zéro des variables internes de sommaire, puis indique le nouvel argument comme premier membre du nouveau groupe.
Dans de nombreuses situations, cela se fait en interne en remettant à zéro
toutes les variables, et en appelant xxx_add().
char *xxx_add(UDF_INIT *initid, UDF_ARGS *args,
char *is_null, char *error);
Cette fonction est appelée à chaque fois qu'une ligne qui appartient à un groupe est trouvée, hormis la première ligne. Durant cette fonction, vous devez ajouter les données dans votre variable interne de sommaire.
La fonction xxx() doit être déclarée de manière identique à
celle d'un fonction utilisateur simple. See section 9.2.2.1 Fonctions utiliateur : appeler des fonctions simples.
Cette fonction est appelée lorsque toutes les lignes d'un groupe ont été
traitées. Vous ne devez normalement pas accéder à la variable args
ici, mais retourner votre valeur, à partir des valeurs du sommaire interne.
Tous les traitements des arguments de xxx_reset() et xxx_add()
doivent être fait d'une manière similaire à celle des fonctions UDF normales.
See section 9.2.2.3 Traitement des arguments.
La gestion de la valeur retournée par xxx() doit être identique à celle d'une
fonction utilisateur classique. See section 9.2.2.4 Valeurs de retour et gestion d'erreurs..
Le pointeur argument de is_null et error sont les mêmes pour
tous les appels de xxx_reset(), xxx_add() et xxx().
Vous pouvez utiliser ces valeur pour vous rappeler si vous avez rencontré une erreur,
ou si la fonction xxx() doit retourner NULL. Notez que vous ne devez pas
stocker de chaîne dans *error! C'est un conteneur d'un seul octet!
is_null est remis à zéro pour chaque (avant d'appeler xxx_reset().
error n'est jamais remis à zéro.
Si isnull ou error sont modifiés après xxx(), alors MySQL
va retourner NULL comme résultat de la fonction de groupement.
Le paramètre args pointe sur une structure UDF_ARGS qui dispose des membre
suivants :
unsigned int arg_count
if (args->arg_count != 2)
{
strcpy(message,"XXX() requires two arguments");
return 1;
}
enum Item_result *arg_type
STRING_RESULT, INT_RESULT et REAL_RESULT.
Pour s'assurer que les arguments sont d'un type donné, et retourner une erreur
dans le cas contraire, vérifiez le tableau arg_type durant la fonction
d'initialisation. Par exemple :
if (args->arg_type[0] != STRING_RESULT ||
args->arg_type[1] != INT_RESULT)
{
strcpy(message,"XXX() requires a string and an integer");
return 1;
}
Comme alternative à l'imposition d'un type particulier pour les arguments des
fonctions, vous pouvez utiliser la fonction pour qu'elle modifie le type des arguments
et donne aux valeurs de arg_type le type que vous souhaitez. Cela fait que
MYSQL va forcer les arguments à un type donnée, pour chaque appel de la fonction
xxx(). Par exemple, pour forcer le type des deux premiers arguments en
chaîne et entier, vous pouvez utiliser la fonction d'initisalition
xxx_init() :
args->arg_type[0] = STRING_RESULT; args->arg_type[1] = INT_RESULT;
char **args
args->args communique les informations à la fonction d'initialisation,
ainsi que la nature des arguments avec laquelle elle a été appelée. Pour un
argument constant i, args->args[i] pointe sur la valeur de l'argument.
Voir plus bas pour les instructions d'accès à cette valeur. Pour les valeurs
non constantes, args->args[i] vaut 0. Un argument constant est une
expression qui utilise des constantes, comme 3 ou 4*7-2 ou SIN(3.14).
Un argument non-constant est une expression qui fait référence aux valeurs qui peuvent
changer de ligne en ligne, par exemple des noms de colonnes ou des fonctions
qui sont appelées avec des arguments non-constants.
Pour chaque invocation de la fonction principale, args->args contient les
arguments réels qui sont passés à la ligne qui sera traitée.
Les fonctions peuvent faire référence à un argument i comme ceci :
STRING_RESULT est donné sous la forme d'un pointeur de chaîne,
plus une longueur, pour permettre la gestion des données binaires ou des données de taille
arbitraire. Le contenu des chaînes est disponible avec l'expression args->args[i]
et la taille de la chaîne est args->lengths[i]. Ne supposez pas que les chaînes sont
terminés par le caractère nul.
INT_RESULT, vous devez transtyper la valeur
args->args[i] en valeur long long :
long long int_val; int_val = *((long long*) args->args[i]);
REAL_RESULT, vous devez transtyper la valeur
args->args[i] en valeur double :
double real_val; real_val = *((double*) args->args[i]);
unsigned long *lengths
lengths indique
la taille maximale des chaînes pour chaque argument. Vous ne devez pas les
modifier. Pour chaque appel de la fonction principale, lengths contient
la taille réelle de toutes les chaînes arguments qui sont passé pour la ligne
traitée. Pour les arguments de type INT_RESULT ou
REAL_RESULT, lengths contient toujours la taille maximale de l'argument
(comme pour la fonction d'initialisation).
La fonction d'initialisation doit retourner 0 si aucune erreur
ne s'est produite et 1 sinon. Si une erreur s'est produite,
xxx_init() doit stocker un message se terminant par un NULL
dans le paramètre message. Le message sera retourné au client.
La taille du tampon du message est de MYSQL_ERRMSG_SIZE caractères,
mais vous devez essayer de garder une taille de message inférieure à 80
caractères, sinon, il remplit la largeur d'un écran de terminal standard.
La valeur de retour de la fonction principale xxx() est la valeur
de la fonction, pour les fonctions long long et double.
Une fonction de chaîne de caractères doit retourner un pointeur vers le résultat
et stocker la taille de la chaîne de caractères dans l'argument length.
Affectez cette valeur au contenu et à la longueur de la valeur retournée. Par exemple :
memcpy(result, "chaîne retournée", 16); *length = 16;
Le tampon result qui est passé à la fonction calc à une taille de
255 bits. Si votre résultat dépasse ceci, ne vous inquietez pas de
l'allocation de mémoire pour ce résultat.
Si votre fonction de chaînes de caractères a besoin de retourner une
chaîne de caractères plus grande que 255 bits, vous devez allouer de
l'espace pour cela avec malloc() dans votre fonction xxx_init().
Vous pouvez stocker la mémoire allouée dans le slot ptr de la structure
UDF_INIT pour être ré-utilisée par les appels futurs de xxx().
See section 9.2.2.1 Fonctions utiliateur : appeler des fonctions simples.
Pour indiquer une valeur de retour NULL dans la fonction principale,
mettez is_null à 1 :
*is_null = 1;
Pour indiquer une erreur retournée dans la fonction principale, mettez le
paramètre error à 1:
*error = 1;
Si xxx() met *error à 1 pour chaque ligne, la valeur de
la fonction est NULL pour la ligne en question et pour chaque ligne
suivante traitée par le processus dans lequel XXX() est invoqué.
(xxx() ne sera même pas appelé pour les lignes suivantes.)
Remarque : dans les versions antérieures à 3.22.10, vous devez définir
*error et *is_null :
*error = 1; *is_null = 1;
Les fichiers qui implémentent des fonctions utilisateurs doivent être compilés et installés sur le même hôte que celui du serveur. Ce processus est décrit plus bas, avec le fichier `udf_example.cc' qui est inclut dans les sources MysQL. Ce fichier contient les fonctions suivantes :
metaphon() retourne la version métaphone de la chaîne en argument.
C'est une technique proche du soundex, mais elle est bien plus optimisée pour
l'anglais.
myfunc_double() retourne la moyenne des codes ASCII des caractères de la chaîne
passée en argument.
myfunc_int() retourne la somme de tailles des arguments.
sequence([const int]) retourne une séquence, commençant à partir du nombr
choisit ou 1, si aucun nombre n'a été fourni.
lookup() retourne l'adresse IP numérique d'un hôte.
reverse_lookup() retourne le nom d'hôte pour une adresse IP.
Cette fonction peut être appelée avec une chaîne au format "xxx.xxx.xxx.xxx" ou
quatre nombres.
Un fichier dynamiquement chargeable doit être compilé sous la forme d'un objet partagé, grâce à une commande comme celle-ci :
shell> gcc -shared -o udf_example.so myfunc.cc
Vous pouvez facilement trouver les options correctes pour la compilation en exécutant cette commande dans le dossier `sql' de votre installation source :
shell> make udf_example.o
Vous devez exécuter une commande de compilation similaire à celle que le
make affiche, sauf que vous devrez supprimer l'option -c près
de la fin de la ligne, et ajouter -o udf_example.so à la fin de la ligne.
Sur certains systèmes, vous devrez aussi supprimer -c de la commande).
Une fois que vous compilez un objet partagés contenant des fonctions utilisateurs,
vous devez les installer, et prévenir le serveur MYSQL. Compilier un objet partagé
avec `udf_example.cc' produit un fichier qui s'appelle
`udf_example.so' (le nom exact peut varier suivant la plate-forme).
Copiez ce fichier dans l'un des dossiers utilisé par ld, tel que
`/usr/lib'. Sur de nombreux systèmes, vous pouvez faire pointer la variable
d'environnement LD_LIBRARY ou LD_LIBRARY_PATH pour qu'elle pointe
dans le dossier où vous avez vos fichiers de fonctions. Le manuel de dlopen
vous indiquera quelle variable utiliser sur votre système. Vous devriez indiquer
cette valeur dans les options de démarrage de mysql.server et safe_mysqld,
et redémarrer mysqld.
Après que la librairie ait été installée, indiquez à mysqld ces nouvelles
fonctions avec ces commandes :
mysql> CREATE FUNCTION metaphon RETURNS STRING SONAME "udf_example.so";
mysql> CREATE FUNCTION myfunc_double RETURNS REAL SONAME "udf_example.so";
mysql> CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "udf_example.so";
mysql> CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so";
mysql> CREATE FUNCTION reverse_lookup
-> RETURNS STRING SONAME "udf_example.so";
mysql> CREATE AGGREGATE FUNCTION avgcost
-> RETURNS REAL SONAME "udf_example.so";
Les fonctions peuvent être effacées plus tard avec DROP FUNCTION:
mysql> DROP FUNCTION metaphon; mysql> DROP FUNCTION myfunc_double; mysql> DROP FUNCTION myfunc_int; mysql> DROP FUNCTION lookup; mysql> DROP FUNCTION reverse_lookup; mysql> DROP FUNCTION avgcost;
Les commandes CREATE FUNCTION et DROP FUNCTION modifient la
table système func dans la base mysql. Le nom de la fonction,
son type et le nom de la librairie partagée sont alors sauvés dans la table. Vous devez avoir
les droits de INSERT et DELETE dans la base mysql pour ajouter
et effacer des fonctions.
Vous ne devez pas utiliser la commande CREATE FUNCTION pour ajouter une fonction
qui a déjà été créée. Si vous devez reinstaller une fonction, vous devez la supprimer
avec la commande DROP FUNCTION puis la reinstaller avec CREATE FUNCTION.
Vous devrez faire cela, par exemple, si vous recompilez une nouvelle version de
votre fonction, pour que mysqld utilise cette nouvelle version. Sinon,
le serveur va continuer à utiliser l'ancienne version.
Les fonctions actives sont rechargées à chaque fois que le serveur démarre, à moins
que vous ne démarriez le serveur mysqld avec l'option --skip-grant-tables.
Dans ce cas, l'initialisation des fonctions utilisateurs sont ignorées, et ces
fonctions sont inutilisables. Une fonction active doit avoir été créée avec
CREATE FUNCTION et pas supprimée avec DROP FUNCTION.
La procédure pour ajouter une nouvelle fonction native est décrite ici. Notez que vous ne pouvez ajouter de fonctions natives à une distribution binaire car la procédure implique la modifications du code source de MySQL. Vous devez compiler MySQL vous-même à partir d'une distribution des sources. Notez aussi que si vous migrez vers une autre version de MySQL (par exemple, quand une nouvelle version est réalisée), vous devrez répéter la procédure avec la nouvelle version.
Pour ajouter une nouvelle fonction native à MySQL, suivez les étapes suivantes :
sql_functions[].
sql_functions[]
et ajouter une fonction qui crée un objet fonction dans `item_create.cc'.
Regardez "ABS" et create_funcs_abs() pour un exemple.
Si le prototype de la fonction est compliqué (par exemple, elle prend un nombre
variable d'arguments), vous devez ajouter deux lignes à `sql_yacc.yy'. Une
qui indique le symbole préprocesseur que yacc doit définir (cela doit
être ajouté au début du fichier). Puis définir les paramètres de la fonction
et un ``item'' avec ces paramètres à la règle simple_expr.
Pour un exemple, vérifiez toutes les occurences de ATAN dans `sql_yacc.yy'
pour voir comment cela se fait.
Item_num_func ou de
Item_str_func, selon que vvotre fonction retourne une nombre ou un chaîne.
double Item_func_newname::val() longlong Item_func_newname::val_int() String *Item_func_newname::Str(String *str)Si vous héritez votre objet de l'un des éléments standards (comme
Item_num_func) vous n'aurez probablement qu'à définir l'une des
fonctions décrites ci-dessus et laisser l'objet parent prendre soin des
autres fonctions.
Par exemple, la classe Item_str_func définit une fonction val()
qui exécute atof() sur la valeur retournée par ::str().
void Item_func_newname::fix_length_and_dec()Cette fonction doit au moins calculer
max_length en se basant sur les
arguments donnés. max_length est le nombre maximal de caractères
que la fonction peut retourner. Cette fonction doit aussi définir maybe_null
= 0 si la fonction principale ne peut pas retourner une valeur NULL. La
fonction peut vérifier si l'un de ses arguments peut retourner NULL
en vérifiant la variable maybe_null des arguments. Vous pouvez regarder
Item_func_mod::fix_length_and_dec pour avoir un exemple concret.
Toutes les fonctions doivent être sûres pour les threads (en d'autres termes, n'utilisez aucune variable statique ou globale dans la fonction sans les protéger avec mutexes).
Si vous voulez retourner NULL, à partir de ::val(), ::val_int()
ou ::str() vous devez mettre null_value à 1 et retourner 0.
Pour les fonctions de l'objet ::str(), il y a d'autres considérations à prendre
en compte :
String *str fournit un tampon de chaîne qui peut être utilisé
pour contenir le résultat. (Pour plus d'informations à propos du type String,
regardez le fichier `sql_string.h'.)
::str() doit retourner la chaîne contenant le résultat ou
(char*) 0 si celui-ci est NULL.
Avec MySQL, vous pouvez définir une procédure en C++ qui accède et modifie les
données dans la requête avant que celle-ci ne soit envoyée au client.
La modification peut être faite ligne par ligne ou au niveau de GROUP BY.
Nous avons crée une procédure d'exemple avec la version 3.23 de MySQL pour vous montrer comment cela fonctionne.
De plus, nous vous recommandons de jeter un oeil à mylua.
Avec cela, vous pouvez utiliser le langage LUA pour charger une dynamiquement une
procédure dans mysqld.
analyse([max elements,[max memory]])
Cette procédure est définie dans le fichier `sql/sql_analyse.cc'. Elle examine les résultats de vos requêtes et en retourne une analyse :
max elements (256 par défaut) est le nombre maximal de valeurs distinctes que
analyse retiendra par colonne. C'est utilisé par analyse pour vérifier
si le type optimal de la colonne ne serait pas le type ENUM.
max memory (8192 par défaut) est le maximum de mémoire que analyse doit
allouer par colonne quand elle essaye de trouver toutes les valeurs distinctes.
SELECT ... FROM ... WHERE ... PROCEDURE ANALYSE([max elements,[max memory]])
Pour le moment, la seule documentation à ce sujet est constituée par les sources.
Vous pourrez trouver toutes les informations sur les procédures en examinant les fichiers suivants :
Go to the first, previous, next, last section, table of contents.