FirebirdSQL logo

Introduction

Ce tutoriel décrit la version orientée objet de l’API Firebird V4.0.+

Ce document est une traduction du fichier doc/Using_OO_API.html, qui est inclus dans les sources de Firebird.

Les interfaces décrites se trouvent dans le répertoire include/firebird/FirebirdInterface.idl. À l’aide de l’outil CLOOP sur la base de ce fichier IDL, vous pouvez générer un fichier d’interface pour un langage de programmation spécifique (IdlFbInterfaces.h ou Firebird.pas).

CLOOP — (Programmation orientée objet inter-langages). Cet outil n’est pas inclus dans Firebird. Il se trouve dans le code source de cloop.Une fois l’outil assemblé, vous pouvez générer une API pour votre langage de programmation.

Pour Object Pascal, le fichier API de l’objet est généré à l’aide de la commande suivante :

cloop FirebirdInterface.idl pascal Firebird.pas Firebird --uses SysUtils \
  --interfaceFile Pascal.interface.pas \
  --implementationFile Pascal.implementation.pas \
  --exceptionClass FbException --prefix I \
  --functionsFile fb_get_master_interface.pas

Les fichiers Pascal.interface.pas, Pascal.implementation.pas et fb_get_master_interface.pas se trouvent à l’adresse https://github.com/FirebirdSQL/firebird/tree/master/src/misc/pascal.

Note

Dans ce cas, les API Firebird seront préfixées par la lettre I, car c’est la coutume en Object Pascal.

Le fichier Firebird.pas qui en résulte ne contient pas les constantes isc_*. Ces constantes pour les langages C/C++ peuvent être trouvées à l’adressehttps://github.com/FirebirdSQL/firebird/tree/master/src/include/consts_pub.h.Pour obtenir les constantes en langage Pascal, utilisons un script AWK pour transformer la syntaxe. Sous Windows, vous devrez installer Gawk pour Windows ou utiliser le sous-système Windows pour Linux (disponible sur Windows 10). Pour ce faire, utilisez la commande suivante :

awk -f Pascal.Constants.awk consts_pub.h > const.pas

Le contenu du fichier résultant doit être copié dans la section const vide du fichier Firebird.pas immédiatement après l’implémentation. Le fichier Pascal.Constants.awk se trouve à l’adresse suivante : https://github.com/FirebirdSQL/firebird/tree/master/src/misc/pascal.

Firebird Interfaces

L’API orientée objet Firebird (API OO) est basée sur l’utilisation d’interfaces. Ces interfaces, bien qu’elles ressemblent à des interfaces OLE2 à certains égards (certaines d’entre elles ont des méthodes addRef() et release()), ne sont pas standard et ont des fonctionnalités que l’on ne trouve pas dans d’autres types d’interface couramment utilisés. Tout d’abord, les interfaces Firebird sont indépendantes du langage – ce qui signifie qu’elles n’ont pas besoin d’utiliser des constructions spécifiques au langage telles que class dans C++ pour les définir/utiliser, l’interface peut être définie en utilisant n’importe quel langage qui a le concept d’un tableau et d’un pointeur vers une procédure/fonction. De plus, les interfaces sont versionnées, ce qui signifie que nous prenons en charge différentes versions de la même interface. La disposition binaire des interfaces conçues pour prendre en charge ces fonctions est très efficace (il n’y a pas besoin d’appels virtuels supplémentaires comme dans OLE2/COM avec son QueryInterface), mais elle n’est pas pratique pour une utilisation directe dans la plupart des langages. Par conséquent, pour faciliter l’utilisation de l’API, il est préférable d’utiliser des wrappers spécifiques pour chaque différents langages. Nous avons actuellement des wrappers pour C++ et Pascal, Java arrive bientôt. Pour l’utilisateur final, il n’y a pas de différence entre les appels POV de C++ et Pascal, bien que Pascal n’ait pas certaines des fonctionnalités de langage présentes dans C++ (telles que la possibilité de désactiver la vérification automatique de l’état après les appels d’API) qui est absent en Pascal.

En règle générale, l’API OO est utilisée pour accéder aux données stockées dans la base de données. L’API Firebird OO accomplit certainement cette tâche, mais elle prend également en charge la création de vos propres plugins / UDRs – des modules qui vous permettent d’étendre les capacités de Firebird en fonction de vos besoins.C’est pourquoi ce document comporte deux grandes parties : l’accès aux bases de données et l’écriture de plugins. Bien sûr, certaines interfaces (par exemple le vecteur d’état) sont utilisées dans les deux parties de l’API, celles-ci seront discutées dans la partie accès aux données, et plus tard dans la discussion sur les plugins, nous nous y référerons librement. Donc, même si vous envisagez d’écrire une sorte de plugin, il est préférable de commencer par lire la première partie de ce document.De plus, de nombreux plugins eux-mêmes ont besoin d’accéder à des bases de données, ce qui nécessite généralement une API d’accès aux données.

Le paquet d’installation de Firebird contient un certain nombre d’exemples concrets d’utilisation de l’API OO, situés dans les répertoires examples/interfaces (accès à la base de données) et examples/dbcrypt (un plugin qui effectue un chiffrement factice de la base de données). Il est supposé que le lecteur soit familier avec l’API ISC utilisée dans Firebird depuis Interbase.

Ce document ne prétend pas être la documentation complete de Firebird – il décrit simplement l’API orientée objet, et le lecteur doit être familier avec les concepts de base de Firebird, la connaissance de l’API ISC est également un plus.Par exemple, lors de la description de l’utilisation des services, il n’y a pas d’explication de ce qu’est un service et de ce à quoi il sert, seulement une description de la façon d’obtenir l’interface IService et de la façon de l’utiliser. Notez également que les exemples de code ne tirent pas parti de la plupart des fonctionnalités puissantes de C++. Aucun pointeur de comptage de référence n’est utilisé, aucun modèle n’est utilisé (à l’exception de celui présent dans les en-têtes publics de Firebird), etc.L’objectif principal est de rendre ce texte applicable non seulement aux programmeurs C++ mais aussi à d’autres langages comme le Pascal.

Accès à la base de données

Création d’une base de données et connexion à une base de données existante

Tout d’abord, nous devons accéder à l’interface IMaster. IMaster est l’interface principale de Firebird, nécessaire pour accéder à toutes les autres interfaces. Par conséquent, il existe un moyen simple d’y accéder – la seule chose qui est nécessaire est d’utiliser une fonction de l’API OO appelée fb_get_master_interface(). Cette fonction n’a pas de paramètres et réussit toujours. Il n’y a qu’une seule instance de IMaster pour chaque bibliothèque cliente Firebird, il n’y a donc pas besoin de s’inquiéter de libérer de la mémoire utilisée par l’interface maître. Le moyen le plus simple d’y accéder à partir de votre programme est d’utiliser la variable globale ou statique appropriée :

static IMaster* master = fb_get_master_interface();

Pour la plupart des méthodes utilisées dans l’API OO de Firebird, le premier paramètre est IStatus. Il s’agit d’un remplacement logique de ISC_STATUS_ARRAY, mais il fonctionne différement avec les erreurs et les avertissements (sans les mélanger dans le même tableau), peut contenir un nombre illimité d’erreurs, et (c’est important si vous envisagez d’implémenter IStatus vous-même) stocke toujours les chaînes auxquelles il fait référence à l’intérieur de l’interface. Habituellement, au moins une instance de IStatus est nécessaire pour appeler d’autres méthodes. Vous pouvez l’obtenir à partir de iMaster :

IStatus* st = master->getStatus();

Si, pour une raison quelconque, la méthode getStatus() ne fonctionne pas (OOM par exemple), alors elle retourne NULL – auquel cas il est évident que nous ne pouvons pas utiliser une méthode générique pour les messages d’erreur basée sur IStatus.

Nous allons maintenant nous intéresser à la première interface, qui est directement liée à l’accès à la base de données. Il s’agit de l’interface IProvider, ainsi nommée parce que c’est l’interface que tout fournisseur dans Firebird devrait implémenter. La bibliothèque cliente Firebird possède sa propre implémentation de IProvider qui doit être utilisée pour exécuter toutes les opérations de base de données. Pour l’obtenir, nous appelons la méthode getDispatcher de l’interface IMaster :

IProvider* prov = master->getDispatcher();

Lors de la connexion à une base de données existante, ou encore plus lors de la création d’une nouvelle base de données, il est souvent nécessaire de passer de nombreux paramètres supplémentaires à l’appel de l’API (login/mot de passe, taille de page pour la nouvelle base de données, etc.). Il est presque impossible d’avoir des paramètres séparés au niveau de l’appel : nous devrons modifier l’appel trop souvent pour ajouter de nouveaux paramètres, et le nombre de paramètres peut être très important, même si vous n’avez généralement pas besoin d’en passer beaucoup.Par conséquent, une structure de données en mémoire spéciale appelée bloc de paramètres de base de données (DPB) est utilisée pour transmettre des paramètres supplémentaires. Son format est clairement défini, ce qui permet de construire des blocs de paramètres DPB octets par octets. Cependant, il est beaucoup plus facile d’utiliser une interface spéciale IXpbBuilder, ce qui facilite la création de blocs de divers paramètres.Pour obtenir une instance de IXpbBuilder, vous devez connaître une autre API générique de Firebird — IUtil. C’est une sorte de conteneur pour les appels qui ne se prêtent pas bien à d’autres endroits. Donc, ce que nous faisons, c’est

IUtil* utl = master->getUtilInterface();
IXpbBuilder* dpb = utl->getXpbBuilder(&status, IXpbBuilder::DPB, NULL, 0);

Ce code crée un constructeur de bloc de paramètres vide de type DPB. Maintenant, l’ajout du paramètre nécessaire est trivial :

dpb->insertInt(&status, isc_dpb_page_size, 4 * 1024);

créera une base de données avec une taille de page de 4 Ko et des valeurs dont le sens est clair.

dpb->insertString(&status, isc_dpb_user_name, "sysdba");
dpb->insertString(&status, isc_dpb_password, "masterkey");

Ce qui suit est spécifique à C++ : Nous sommes presque prêts à appeler la méthode createDatabase() de l’instance IProvider, mais avant cela, nous devons dire quelques mots sur le concept du Status Wrapper. Le Status Wrapper n’est pas une interface, c’est un wrapper très fin sur l’interface IStatus. Il permet de personnaliser le comportement de l’API C++ (changer la façon dont les erreurs retournées dans l’interface IStatus sont gérées).Dans un premier temps, nous vous recommandons d’utiliser ThrowStatusWrapper, qui lève une exception C++ chaque fois qu’une erreur est renvoyée dans IStatus.

ThrowStatusWrapper status(st);

Nous pouvons maintenant créer une nouvelle base de données vide :

IAttachment* att = prov->createDatabase(&status, "fbtests.fdb",
    dpb->getBufferLength(&status), dpb->getBuffer(&status));
printf("Database fbtests.fdb created\n");

Notez que nous ne vérifions pas l’état après avoir appelé createDatabase(), car si une erreur se produit, une exception C++ ou Pascal sera levée (il est donc très utile d’avoir la syntaxe try/catch/except dans votre code). Nous utilisons également deux nouvelles fonctions de IXpbBuildergetBufferLength() et getBuffer(), qui extraient les données de l’interface au format DPB natif. Comme vous pouvez le voir, il n’est pas nécessaire de vérifier explicitement l’état des fonctions en renvoyant des résultats intermédiaires.

La fermeture de la base de données nouvellement créée est trivial :

att->detach(&status);

Il ne reste plus qu’à entourer toutes les instructions d’un bloc try et à écrire un gestionnaire dans le bloc catch. Lorsque vous utilisez ThrowStatusWrapper, vous devez toujours intercepter la classe FbException définie dans l’API C++, en Pascal, vous devez également travailler avec la class FbException. Dans le cas le plus simple, le bloc de gestion des exceptions peut ressembler à ceci :

catch (const FbException& error)
{
    char buf[256];
    utl->formatStatus(buf, sizeof(buf), error.getStatus());
    fprintf(stderr, "%s\n", buf);
}

Notez qu’ici nous utilisons une autre fonction de IUtilformatStatus(). Il renvoie dans le tampon buf le texte décrivant l’erreur (avertissement) stocké dans le paramètre IStatus.

Pour vous connecter à une base de données existante, utilisez la méthode attachDatabase() de l’interface IProvider au lieu de createDatabase(). Tous les paramètres sont les mêmes pour les deux méthodes.

att = prov->attachDatabase(&status, "fbtests.fdb", 0, NULL);

Dans cet exemple, il n’utilise aucun paramètre DPB supplémentaire. Gardez à l’esprit que sans identifiant/mot de passe, toute connexion à distance échouera si l’authentification de confiance n’est pas configurée. Bien entendu, les informations de connexion peuvent être fournies par l’environnement (dans les variables ISC_USER et ISC_PASSWORD), comme c’était le cas auparavant.

Le dossier examples contient des exemples terminés, y compris des exemples de création d’une base de données, 01.create.cpp et en Pascal 01.create.pas. Lors de la lecture de ce document, il est utile de créer soit même les exemples et d’essayer de les exécuter.