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’adresse https://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.

Utilisation des transactions

La création de bases de données vides n’est certainement pas suffisante pour travailler avec un SGBDR. Nous voulons être en mesure de créer divers objets dans la base de données (par exemple, des tables, etc.) et d’insérer des données dans ces tables. Dans Firebird, toute opération de base de données est transactionnelle. Par conséquent, tout d’abord, nous devons apprendre à démarrer une transaction. Nous ne parlons pas ici de transactions distribuées (prises en charge par l’interface IDtc) afin d’éviter une complexité inutile pour la plupart des utilisateurs. Le démarrage d’une transaction non distribuée est très simple et se fait via l’interface de connexion :

ITransaction* tra = att->startTransaction(&status, 0, NULL);

Dans cet exemple, les paramètres de transaction par défaut sont utilisés : le TPB n’est pas transmis à la méthode startTransaction(). Si vous avez besoin d’une transaction avec des paramètres autres que la valeur par défaut, vous pouvez créer un IXpbBuilder correspondant et y ajouter les éléments nécessaires :

IXpbBuilder* tpb = utl->getXpbBuilder(&status, IXpbBuilder::TPB, NULL, 0);
tpb->insertTag(&status, isc_tpb_read_committed);

et passez le TPB terminé à startTransaction() :

ITransaction* tra = att->startTransaction(&status, tpb->getBufferLength(&status),
    tpb->getBuffer(&status));

L’interface de transaction est utilisée comme paramètre dans de nombreux autres appels d’API, mais elle ne fait rien d’autre que valider/annuler la transaction, tout en conservant le contexte de transaction :

tra->commit(&status);

Vous pouvez voir comment démarrer et confirmer une transaction dans les exemples 01.create.cpp ou en Pascal 01.create.pas.

Exécution d’une instruction SQL sans paramètres d’entrée et avec des lignes renvoyées

Une fois la transaction lancée, nous sommes prêts à exécuter nos premières instructions SQL. La méthode execute() utilisée à cet effet dans IAttachment est assez générique, et peut également être utilisée pour exécuter des instructions SQL avec des paramètres d’entrée et de sortie (ce qui est typique de EXECUTE PROCEDURE), mais pour l’instant nous allons utiliser sa forme la plus simple. Les instructions DDL et DML peuvent être exécutées :

att->execute(&status, tra, 0, "create table dates_table (d1 date)",
    SQL_DIALECT_V6, NULL, NULL, NULL, NULL);
tra->commitRetaining(&status);
att->execute(&status, tra, 0, "insert into dates_table values (CURRENT_DATE)",
    SQL_DIALECT_V6, NULL, NULL, NULL, NULL);

Comme vous pouvez le voir, l’interface de transaction est un paramètre obligatoire pour la méthode execute() (ne doit être NULL que si vous exécutez l’instruction SET TRANSACTION). Le paramètre suivant est la longueur de l’instruction SQL (qui peut être nulle, auquel cas les règles C sont utilisées pour déterminer la longueur de la chaîne), puis le texte de l’instruction et le dialecte SQL qui doit être utilisé pour elle. Ceci est suivi de quelques NULLs qui sont substitués pour décrire les métadonnées, et les tampons des paramètres d’entrée et de sortie. Pour une description complète de cette méthode, consultez IAttachment.

Exécution d’instructions SQL avec des paramètres d’entrée

Il existe deux façons d’exécuter une instruction avec des paramètres d’entrée. Le choix de la bonne méthode dépend du fait que vous deviez l’exécuter plus d’une fois et que vous connaissiez ou non le format des paramètres au préalable. Lorsque ce format est connu et que vous n’avez besoin d’exécuter l’instruction qu’une seule fois, vous pouvez utiliser un seul appel à IAttachment::execute(). Sinon, vous devez d’abord préparer une requête SQL, puis vous pouvez l’exécuter à plusieurs reprises avec différents paramètres.

Pour préparer une instruction SQL en vue de son exécution, utilisez la méthode prepare() de l’interface IAttachment :

IStatement* stmt = att->prepare(&status, tra, 0,
    "UPDATE department SET budget = ? * budget + budget WHERE dept_no = ?",
    SQL_DIALECT_V6, IStatement::PREPARE_PREFETCH_METADATA);

Si vous n’avez pas l’intention d’utiliser la description des paramètres de Firebird (c’est-à-dire que vous pouvez fournir cette information vous-même), utilisez IStatement::PREPARE_PREFETCH_NONE au lieu de IStatement::PREPARE_PREFETCH_METADATA – cela réduira légèrement le trafic client/serveur et économisera des ressources.

Dans l’API ISC, la structure XSQLDA est utilisée pour décrire le format des paramètres de l’opérateur. La nouvelle API n’utilise pas XSQLDA — elle utilise IMessageMetadata à la place. Un ensemble de paramètres d’entrée (ainsi qu’un enregistrement extrait du curseur) est décrit dans l’API Firebird de la même manière, ci-après dénommé message. IMessageMetadata est transmis en tant que paramètre aux méthodes de messagerie entre le programme et le moteur de base de données. Il existe de nombreuses façons d’obtenir une instance de IMessageMetadata, en voici quelques-unes :

  • obtenir à partir de IStatement ;

  • construite à l’aide de l’interface IMetadataBuilder ;

  • Avec votre propre implémentation de cette interface.

La récupération des métadonnées à partir d’une requête préparée est très simple : la méthode getInputMetadata() renvoie une interface qui décrit le message d’entrée (c’est-à-dire les paramètres de l’opérateur), l’interface renvoyée par getOutputMetadata() décrit le message de sortie (c’est-à-dire une chaîne de données ou de valeurs sélectionnées renvoyées par la procédure). Dans notre cas, nous pouvons faire ce qui suit :

IMessageMetadata* meta = stmt->getInputMetadata(&status);

Ou nous pouvons créer nous-mêmes le message de métadonnées. Pour ce faire, tout d’abord, nous devons obtenir l’interface du constructeur :

IMetadataBuilder* builder = master->getMetadataBuilder(&status, 2);

Le deuxième paramètre est le nombre attendu de champs dans le message, il peut être modifié ultérieurement, c’est-à-dire qu’il n’est nécessaire que pour l’optimisation.

Ensuite, vous devez définir les caractéristiques individuelles des champs dans le générateur. Les types et longueurs de champ minimum requis sont les suivants :

builder->setType(&status, 0, SQL_DOUBLE + 1);

builder->setType(&status, 1, SQL_TEXT + 1);
builder->setLength(&status, 1, 3);

La nouvelle API utilise les anciennes constantes pour les types SQL, le plus petit bit est toujours utilisé pour indiquer la possibilité de prendre une valeur null. Dans certains cas, il est judicieux de définir un sous-type (pour les BLOB), un jeu de caractères (pour les zones de texte) ou un facteur d’échelle (pour les champs numériques). Enfin, il est temps d’obtenir une instance de IMessageMetadata :

IMessageMetadata* meta = builder->getMetadata(&status);

Nous ne discutons pas ici de notre propre implémentation de IMessageMetadata. Si cela vous intéresse, vous pouvez consulter l’exemple de 05.user_metadata.cpp.

Nous avons donc une instance de la description des métadonnées des paramètres d’entrée. Mais pour travailler avec le message, nous avons aussi besoin d’un tampon. La taille de la mémoire tampon est l’une des principales caractéristiques des messages de métadonnées et est renvoyée par la méthode getMessageLength() de IMessageMetadata :

char* buffer = new char[meta->getMessageLength(&status)];

Afin de traiter les valeurs individuelles à l’intérieur du tampon, le décalage par rapport à celles-ci doit être pris en compte. IMessageMetadata renseigne les décalages pour toutes les valeurs du message, en l’utilisant, nous pouvons créer des pointeurs vers celles-ci :

double* percent_inc = (double*) &buffer[meta->getOffset(&status, 0)];
char* dept_no = &buffer[meta->getOffset(&status, 1)];

N’oubliez pas non plus de traiter les flags NULL :

short* flag = (short*)&buffer[meta->getNullOffset(&status, 0)];
*flag = 0;

flag = (short*) &buffer[meta->getNullOffset(&status, 1)];
*flag = 0;

Une fois les manipulations de décalage terminées, nous sommes prêts à obtenir les valeurs des paramètres :

getInputValues(dept_no, percent_inc);

et exécutez l’instruction préparée :

stmt->execute(&status, tra, meta, buffer, NULL, NULL);

Les deux derniers paramètres NULL sont pour les messages de sortie et sont généralement utilisés pour l’instruction EXECUTE PROCEDURE.

Si vous n’avez pas besoin de récupérer les métadonnées de l’instruction et que vous prévoyez de ne l’exécuter qu’une seule fois, vous pouvez choisir une méthode plus simple — utilisez la méthode execute() de l’interface IAttachment :

att->execute(&status, tra, 0,
    "UPDATE department SET budget = ? * budget + budget WHERE dept_no = ?",
    SQL_DIALECT_V6, meta, buffer, NULL, NULL);

Dans ce cas, vous n’avez pas du tout besoin d’utiliser IStatement.

Un exemple d’exécution de l’instruction UPDATE avec des paramètres est présent dans 02.update.cpp ou l’exemple en Pascal 02.update.pas, vous verrez également comment une exception levée dans un déclencheur / procédure peut être interceptée dans un programme C++ ou Pascal.

Ouvrir un curseur et en extraire les données

La seule façon d’obtenir les lignes de données renvoyées par l’instruction SELECT dans l’API OO est d’utiliser IResultSet. Cette interface est retournée par la méthode openCursor() accessible à la fois dans IAttachment et IStatement. openCursor() est similaire à execute() dans la plupart des aspects, et décider comment ouvrir le curseur (en utilisant une instruction préparée ou directement à partir de l’interface de connexion) est la même chose. Les exemples 03.select.cpp version en Pascal 03.select.pas et 04.print_table.cpp utilisent les deux approches. Notez qu’une différence entre la méthode openCursor() et la méthode execute() est que rien n’est passé au tampon pour le message de sortie à openCursor(), il sera passé plus tard lorsque les données seront récupérées à partir du curseur. Cela vous permet d’ouvrir un curseur avec un format de message de sortie inconnu (NULL est passé à la place des métadonnées de sortie). Dans ce cas, Firebird utilise le format de message par défaut, qui peut être interrogé via l’interface IResultSet :

const char* sql = "select * from ..."; // some select statement

IResultSet* curs = att->openCursor(&status, tra, 0, sql, SQL_DIALECT_V6,
    NULL, NULL, NULL, NULL, 0);

IMessageMetadata* meta = curs->getMetadata(&status);

Par la suite, ces métadonnées peuvent être utilisées pour allouer une mémoire tampon aux données et analyser les lignes extraites.

Vous pouvez également préparer l’instruction, récupérer les métadonnées à partir de l’instruction préparée, puis ouvrir le curseur. Il s’agit de la méthode à privilégier si vous vous attendez à ce que le curseur soit ouvert plus d’une fois.

IStatement* stmt = att->prepare(&status, tra, 0, sql, SQL_DIALECT_V6,
    IStatement::PREPARE_PREFETCH_METADATA);

IMessageMetadata* meta = stmt->getOutputMetadata(&status);

IResultSet* curs = stmt->openCursor(&status, tra, NULL, NULL, NULL, 0);

Nous avons obtenu (d’une manière ou d’une autre) une instance de la description des métadonnées des champs de sortie (lignes dans le jeu de données). Pour travailler avec le message, nous avons également besoin d’un tampon :

unsigned char* buffer = new unsigned char[meta->getMessageLength(&status)];

Il existe de nombreuses méthodes de récupération différentes dans l’interface IResultSet, mais lorsque le curseur n’est pas ouvert avec l’option SCROLL, seule fetchNext() fonctionne, c’est-à-dire que vous ne pouvez avancer que dans les enregistrements suivant. En plus des erreurs et des avertissements dans l’état, la méthode fetchNext() renvoie un code de complétion qui est RESULT_OK (lorsque le tampon est rempli avec des valeurs pour la ligne suivante) ou RESULT_NO_DATA (lorsqu’il n’y a plus de lignes après le curseur). RESULT_NO_DATA n’est pas un état d’erreur, c’est un état normal une fois la méthode terminée, qui signale qu’il n’y a plus de données dans le curseur. Si un wrapper d’état est utilisé, aucune exception n’est levée si une erreur est renvoyée. Une autre valeur, RESULT_ERROR, peut être renvoyée, ce qui signifie qu’il n’y a pas de données dans le tampon et des erreurs dans l’état du vecteur. La méthode fetchNext() est généralement appelée dans une boucle :

while (curs->fetchNext(&status, buffer) == IStatus::RESULT_OK)
{
    // row processing
}

Ce qui se passe lorsque les enregistrements sont traitées dépend de vos besoins. Pour accéder à un champ spécifique, utilisez le décalage du champ :

unsigned char* field_N_ptr = buffer + meta->getOffset(&status, n);

n est le numéro du champ dans le message. Ce pointeur doit être affecté au type approprié, en fonction du type de champ. Par exemple, pour le champ VARCHAR, vous devez utiliser le cast dans la structure vary :

vary* v_ptr = (vary*) (buffer + meta->getOffset(&status, n));

Nous pouvons maintenant afficher la valeur du champ :

printf("field %s value is %*.*s\n",
       meta->getField(&status, n),
       v_ptr->vary_length,
       v_ptr->vary_length,
       v_ptr->vary_string);

Si vous voulez obtenir les meilleures performances, il est utile de mettre en cache les valeurs de métadonnées dont vous avez besoin, comme nous le faisons dans nos exemples 03.select.cpp version en Pascal 03.select.pas et 04.print_table.cpp .

Utilisation de la macros FB_MESSAGE pour les messages statiques

Travailler avec des données à l’aide de décalages de champs est assez efficace, mais cela nécessite beaucoup de code. Dans C++, ce problème peut être résolu avec des modèles, mais même par rapport à eux, la façon la plus pratique de travailler avec un message est de le présenter sous sa forme native : une structure en C/C++, un record en Pascal, etc. Bien entendu, cela ne fonctionne que si le format du message est connu à l’avance. Pour créer de telles structures en C++ dans Firebird, il y a une macro spéciale FB_MESSAGE.

FB_MESSAGE a 3 arguments : le nom du message (structure), le type d’enveloppe d’état et la liste des champs. L’utilisation du premier et du deuxième argument est évidente, la liste des champs contient des paires (field_type, field_name), où field_type est l’un des suivants :

  • FB_BIGINT

  • FB_BLOB

  • FB_BOOLEAN

  • FB_CHAR(len)

  • FB_DATE

  • FB_DECFLOAT16

  • FB_DECFLOAT34

  • FB_DOUBLE

  • FB_FLOAT

  • FB_INTEGER

  • FB_INTL_CHAR(len, charSet)

  • FB_INTL_VARCHAR(len, charSet)

  • FB_SCALED_BIGINT(x)

  • FB_SCALED_INTEGER(x)

  • FB_SCALED_SMALLINT(x)

  • FB_SMALLINT

  • FB_TIME

  • FB_TIME_TZ

  • FB_TIME_TZ_EX

  • FB_TIMESTAMP

  • FB_TIMESTAMP_TZ

  • FB_TIMESTAMP_TZ_EX

  • FB_VARCHAR(len)

Dans la structure générée par le préprocesseur, les types integer et float sont mappés aux types C correspondants, les types date et time sont mappés aux classes FbDate et FbTime (toutes les classes mentionnées ici sont dans l’espace de noms Firebird), le type timestamp est mappé à la classe FbTimestamp, qui contient deux membres de données publiques, la date et l’heure des classes respectives, et le type char est mappé à la structure du lien : #fbapi-objects-fbchar[FbChar] et varchar — avec la structure FbVarChar. Pour chaque champ, le préprocesseur crée deux membres de données : name pour la valeur du champ/paramètre et nameNull pour l’indicateur NULL. Le constructeur de message a 2 paramètres : un pointeur vers le wrapper d’état et une interface maître :

FB_MESSAGE(Output, ThrowStatusWrapper,
    (FB_SMALLINT, relationId)
    (FB_CHAR(31), relationName)
    (FB_VARCHAR(100), description)
) output(&status, master);

Pour les messages statiques, l’utilisation de FB_MESSAGE est le meilleur choix, mais ils peuvent facilement être passés aux méthodes execute, openCursor et fetch :

rs = att->openCursor(&status, tra, 0, sqlText,
      SQL_DIALECT_V6, NULL, NULL, output.getMetadata(), NULL, 0);

et est utilisé pour travailler avec les valeurs des champs individuels :

while (rs->fetchNext(&status, output.getData()) == IStatus::RESULT_OK)
{
  printf("%4d %31.31s %*.*s\n", output->relationId, output->relationName.str,
    output->descriptionNull ? 0 : output->description.length,
    output->descriptionNull ? 0 : output->description.length,
    output->description.str);
}

Pour obtenir un exemple d’utilisation de la macro FB_MESSAGE pour travailler avec des messages, consultez l’exemple 06.fb_message.cpp.

Travailler avec les BLOB

Pour les blobs, Firebird stocke un identifiant BLOB dans le tampon de message, qui est un objet de 8 octets qui doit être aligné sur une limite de 4 octets. L’identifiant est de type ISC_QUAD. L’interface IAttachment a 2 méthodes pour travailler avec les BLOBs, openBlob() et createBlob(), qui renvoient l’interface IBlob et ont le même ensemble de paramètres, mais effectuent des actions légèrement différentes : openBlob() prend l’ID BLOB du message et prépare le BLOB pour la lecture, et createBlob() crée un nouveau BLOB, met son identifiant dans le message et prépare le BLOB pour l’écriture.

Pour travailler avec des BLOB, vous devez d`abord inclure leurs identifiants BLOB dans le message. Si vous obtenez des métadonnées d`un champ du moteur Firebird du type approprié, cet identifiant sera déjà présent. Dans ce cas, vous utilisez simplement son offset (à condition que la variable blobFieldNumber contienne le numéro du champ BLOB) (et l`offset NULL correspondant pour vérifier NULL ou mettre le flag NULL) pour obtenir le pointeur dans le tampon du message :

ISC_QUAD* blobPtr =
  (ISC_QUAD*) &buffer[metadata->getOffset(&status, blobFieldNumber)];
ISC_SHORT* blobNullPtr =
  (ISC_SHORT*) &buffer[metadata->getNullOffset(&status, blobFieldNumber)];

Si vous utilisez les messages de macro statiques FB_MESSAGE, le champ BLOB sera déclaré comme étant de type FB_BLOB :

FB_MESSAGE(Msg, ThrowStatusWrapper,
    (FB_BLOB, b)
) message(&status, master);

ISC_QUAD* blobPtr = &message->b;
ISC_SHORT* blobNullPtr = &message->bNull;

Pour créer un nouveau BLOB, appelez la méthode createBlob() :

IBlob* blob = att->createBlob(status, tra, blobPtr, 0, NULL);

Les deux dernières options ne sont requises que si vous souhaitez utiliser des filtres d’objets blob ou un flux d’objets blob, qui ne sont pas abordés ici.

L’interface Blob est maintenant prête à accepter des données dans le BLOB. Utilisez la méthode putSegment() pour envoyer des données au moteur :

void* segmentData;
unsigned segmentLength;
while (userFunctionProvidingBlobData(&segmentData, &segmentLength))
    blob->putSegment(&status, segmentLength, segmentData);

Après avoir envoyé des données au BLOB, n`oubliez pas de fermer l`interface du BLOB :

blob->close(&status);

Assurez-vous que l’indicateur null n’est pas défini (non requis si vous avez vidé l’intégralité de la mémoire tampon de message avant de créer le BLOB) :

*blobNullPtr = 0;

et un message qui contient un objet BLOB peut être utilisé dans une instruction d’insertion ou de mise à jour. Une fois cette instruction exécutée, le nouvel objet blob est stocké dans la base de données.

Pour lire un blob, vous devez obtenir son identifiant dans un message du noyau firebird. Cela peut être fait en utilisant les méthodes fetch() ou execute(). Après cela, utilisez la méthode openBlob() :

IBlob* blob = att->openBlob(status, tra, blobPtr, 0, NULL);

L’interface Blob est prête à fournir des données BLOB. Utilisez la méthode getSegment() pour obtenir les données du moteur :

char buffer[BUFSIZE];
unsigned actualLength;

for(;;)
{
  switch (blob->getSegment(&status, sizeof(buffer), buffer, &actualLength))
  {
    case IStatus::RESULT_OK:
      userFunctionAcceptingBlobData(buffer, actualLength, true);
      continue;

    case IStatus::RESULT_SEGMENT:
      userFunctionAcceptingBlobData(buffer, actualLength, false);
      continue;

    default:
      break;
  }
}

Le dernier paramètre de userFunctionAcceptingBlobData() est l’indicateur de fin de segment — lorsque getSegment() renvoie le code de complétion RESULT_SEGMENT, qui sera notifié à la fonction (le dernier paramètre est passé false), c’est-à-dire que ce segment n’est pas lu complètement, et la suite est attendue lors de l’appel suivant.

Lorsque vous avez terminé avec le BLOB, n’oubliez pas de le fermer :

blob->close(&status);

Modification des données par lots

Étant donné que Firebird 4.0 prend en charge l’exécution par lots d’instructions avec des paramètres d’entrée, cela signifie l’envoi de plus d’un ensemble de paramètres lors de l’exécution de l’instruction. L’interface de traitement par lots est conçue (principalement) pour répondre aux exigences de JDBC en matière de traitement par lots des instructions préparées, mais elle présente un certain nombre de différences majeures :

  • Comme toutes les opérations de données dans Firebird, elles sont orientées message, pas orientées champ ;

  • En tant qu’extension importante, l’interface de traitement par lots prend en charge l’utilisation intégrée d’objets blob (particulièrement efficace lorsque vous travaillez avec de petits objets blob).

  • La méthode execute() ne renvoie pas un simple tableau d’entiers, mais une interface spéciale appelée IBatchCompletionState, qui peut (en fonction des paramètres de la création du paquet) contenir à la fois des informations sur les mises à jour d’enregistrements et, en plus de l’indicateur d’erreur, des vecteurs d’état détaillés pour les messages à l’origine des erreurs d’exécution.

L’interface IBatch (tout comme IResultSet) peut être créée de deux manières, en utilisant l’interface IStatement ou IAttachment, qui appellent toutes deux la méthode createBatch() de l’interface correspondante. Dans le second cas, le texte de l’instruction SQL à exécuter dans le lot est passé directement à createBatch(). Le traitement par lots est configuré à l’aide du bloc Paramètres Batch, dont le format est plus ou moins similaire à celui de DPB v.2 – au début, il y a une balise (IBatch::CURRENT_VERSION), suivie d’un ensemble de clusters larges : une balise de 1 octet, une longueur de 4 octets, la valeur de la longueur d’octet spécifiée. Les balises possibles sont décrites dans l’interface de traitement par lots. Le moyen le plus simple (et recommandé) de créer un bloc de paramètres pour la création par lots est d’utiliser l’interface appropriée IXpbBuilder :

IXpbBuilder* pb = utl->getXpbBuilder(&status, IXpbBuilder::BATCH, NULL, 0);
pb->insertInt(&status, IBatch::RECORD_COUNTS, 1);

L’utilisation d’un tel bloc de paramètres indique au paquet de renvoyer le nombre d’enregistrements mis à jour pour chaque message.

Pour créer une interface batch avec les paramètres requis, passez le bloc de paramètres à l’appel createBatch() :

IBatch* batch = att->createBatch(&status, tra, 0, sqlStmtText, SQL_DIALECT_V6, NULL,
  pb->getBufferLength(&status), pb->getBuffer(&status));

Dans cet exemple, l’interface de traitement par lots est créée avec le format de message par défaut, car « NULL » est transmis à la place du format de métadonnées d’entrée.

Pour travailler avec l’interface de traitement par lots créée, nous devons connaître le format des messages qu’elle contient. Il peut être récupéré à l’aide de la méthode getMetadata() :

IMessageMetadata* meta = batch->getMetadata(&status);

Bien sûr, si vous avez passé votre propre format de message à un lot, vous pouvez simplement l’utiliser.

De plus, je suppose qu’il existe une fonction fillNextMessage(unsigned char* data, IMessageMetadata* metadata) et qu’elle peut remplir le tampon data selon le format passé metadata. Pour travailler avec les messages, nous avons besoin d’un tampon pour les données :

unsigned char* data = new unsigned char[meta->getMessageLength(&status)];

Maintenant, nous pouvons ajouter plusieurs messages avec des données remplies au lot :

fillNextMessage(data, meta);
batch->add(&status, 1, data);

fillNextMessage(data, meta);
batch->add(&status, 1, data);

Une autre façon de travailler avec les messages (à l’aide de la macro FB_MESSAGE) est présente dans l’exemple de l’interface de traitement par lots 11.batch.cpp.

Enfin, le lot devrait être exécuté :

IBatchCompletionState* cs = batch->execute(&status, tra);

Nous avons demandé un décompte du nombre d’enregistrements modifiés (insérés, mis à jour ou supprimés) pour chaque publication. Pour l’afficher, nous devons utiliser l’interface IBatchCompletionState. Déterminez le nombre total de messages traités par le lot (il peut être inférieur au nombre de messages envoyés au lot si une erreur s’est produite et que l’option permettant de renvoyer plusieurs erreurs pendant le traitement par lots n’a pas été activée) :

unsigned total = cs->getSize(&status);

Affichons maintenant l’état de chaque message :

for (unsigned p = 0; p < total; ++p) printf("Msg %u state %d\n", p, cs->getState(&status, p));

Lorsque vous avez terminé d’analyser l’état d’achèvement, n’oubliez pas de le supprimer :

cs->dispose();

Un exemple complet d’affichage du contenu IBatchCompletionState peut être trouvé dans la fonction print_cs() dans l’exemple 11.batch.cpp.

Si, pour une raison quelconque, vous souhaitez vider les tampons de traitement sans les exécuter (c’est-à-dire pour préparer le traitement d’un nouveau lot de messages), utilisez la méthode cancel() :

batch->cancel(&status);

Comme le reste de nos interfaces d’accès aux données, IBatch dispose d’une méthode spéciale pour la fermer :

batch->close(&status);

Au lieu de cela, vous pouvez utiliser l’appel standard release() si vous ne vous souciez pas des erreurs :

batch->release();

Ces techniques vous aident à mettre en œuvre tout ce dont vous avez besoin pour les opérations par lots de type JDBC avec des instructions préparées.

Note
Remarque

JDBC ne recommande pas d’utiliser des lots trop volumineux, par exemple, « Oracle recommande de maintenir la taille des lots entre 50 et 100 ». Firebird prend en charge des lots volumineux, mais dans tous les cas, il doit limiter la taille maximale des lots - voir TAG_BUFFER_BYTES_SIZE. Si la taille totale des messages dépasse cette limite, une erreur isc_batch_too_big est renvoyée. Notez qu’en raison de la mise en mémoire tampon des données de paquets lors de leur envoi sur le réseau, vous n’obtiendrez pas cette erreur immédiatement, mais uniquement lorsque vous viderez les tampons du client vers le serveur. Cela peut se produire à la fois dans les méthodes add() et execute()execute() qui effectue le vidage final du tampon. Si vous souhaitez toujours exécuter le lot avec les messages qui ont rempli le tampon, vous pouvez le faire (lorsque la fonction execute() a renvoyé une erreur, il suffit de la réessayer). Le nombre réel de messages traités sera renvoyé à IBatchCompletionState. La taille optimale du lot doit être trouvée pour chaque cas, mais il y a de fortes chances que si elle est supérieure à 1000, il est peu probable que vous obteniez une augmentation majeure des performances.

Vous pouvez ajouter plus d’un message par appel à un lot. Cela étant dit, n’oubliez pas que les messages doivent être correctement alignés pour que cette fonctionnalité fonctionne correctement. L’alignement requis et la taille du message aligné doivent être obtenus à partir de l’interface IMessageMetadata, par exemple :

unsigned aligned = meta->getAlignedLength(&status);

Plus tard, cette taille sera utile lors de la sélection et de l’utilisation d’un tableau de messages :

unsigned char* data = new unsigned char[aligned * N]; // N est le nombre de messages souhaité

for (int n = 0; n < N; ++n)
  fillNextMessage(&data[aligned * n], meta);

batch->add(&status, N, data);

Après cela, le lot peut être exécuté ou le lot suivant de messages peut y être ajouté.

Les objets blob sont généralement incompatibles avec les lots : batch est efficace lorsque vous devez transférer beaucoup de petites données vers le serveur en une seule étape, les champs blob sont traités comme des objets volumineux, et il n’est donc pas logique de les utiliser dans des lots en général. Mais dans la pratique, il arrive souvent que les BLOB ne soient pas trop volumineux – auquel cas l’utilisation de l’API BLOB traditionnelle (création d’un blob, envoi de segments vers le serveur, fermeture du blob, passage de l’ID BLOB dans un message) tue les performances, en particulier lorsqu’il est utilisé sur le WAN. Par conséquent, dans Firebird, le paquet prend en charge l’envoi de BLOB au serveur avec d’autres messages. Pour utiliser cette fonctionnalité, vous devez d’abord définir la stratégie d’utilisation d’objets blob pour le package que vous créez (en tant qu’option dans le bloc Paramètres) :

pb->insertInt(&status, IBatch::BLOB_IDS, IBatch::BLOB_IDS_ENGINE);

Dans cet exemple, les objets BLOB temporaires nécessaires pour maintenir la communication entre le BLOB et le message dans lequel ils sont utilisés seront générés par le moteur Firebird : il s’agit de l’utilisation la plus simple et la plus courante. Imaginez que le message soit décrit comme suit :

FB_MESSAGE(Msg, ThrowStatusWrapper,
  (FB_VARCHAR(5), id)
  (FB_VARCHAR(10), name)
  (FB_BLOB, desc)
) project(&status, master);

Dans ce cas, pour envoyer un message contenant un objet blob au serveur, vous pouvez faire quelque chose comme ceci :

project->id = ++idCounter;
project->name.set(currentName);

batch->addBlob(&status, descriptionSize, descriptionText, &project->desc);

batch->add(&status, 1, project.getData());

Si un objet blob est trop grand pour tenir dans votre mémoire tampon existante, vous pouvez utiliser la méthode appendBlobData() au lieu de redimentionner la mémoire tampon. Il ajoute plus de données au dernier BLOB ajouté.

batch->addBlob(&status, descriptionSize, descriptionText, &project->desc, bpbLength, bpb);

Après avoir ajouté la première partie du BLOB, récupérez la donnée suivante dans descriptionText, avec la taille descriptionSize puis :

batch->appendBlobData(&status, descriptionSize, descriptionText);

Cela peut être fait en boucle, mais veillez à ne pas trop remplir les tampons de lots internes : leur taille est contrôlée par le paramètre BUFFER_BYTES_SIZE lors de la création de l’interface IBatch, mais ne peut pas dépasser 256 Mo (16 Mo par défaut). Si vous avez besoin de traiter un blob aussi volumineux (par exemple, dans le contexte de nombreux petits blobs, ce qui peut expliquer l’utilisation du traitement par lots), utilisez simplement l’API standard IBlob et la méthode registerBlob de l’interface IBatch.

Un autre choix possible pour une stratégie BLOB est BLOB_IDS_USER. À première vue, l’utilisation ne change pas grand-chose — avant d’appeler addBlob(), l’identifiant correct et unique de chaque blob doit être placé dans la mémoire référencée par le dernier paramètre. Bien entendu, le même identifiant doit être transmis dans le message pour le BLOB. Étant donné que la génération d’objets blob par le moteur est très rapide, une telle politique peut sembler inutile, mais imaginez un cas où vous obtenez des objets blob et d’autres données dans des threads indépendants (par exemple, des blocs de fichiers) et où de bons ID sont déjà présents. Dans ce cas, l’utilisation de BLOB fournis par l’utilisateur peut grandement simplifier le code.

Note
Note

Contrairement aux objets blob créés à l’aide de la fonction createBlob() standard, les objets blob créés à l’aide de l’interface IBatch sont diffusés en continu par défaut, et non partitionnés. Les BLOB segmentés n’ont rien d’intéressant par rapport aux blobs en streaming et ne sont donc pas recommandés pour une utilisation dans les nouveaux développements. Nous ne prenons en charge ce format que pour des raisons de rétrocompatibilité. Si vous voulez vraiment des objets blob partitionnés, vous pouvez remplacer cette valeur par défaut en appelant :

batch->setDefaultBpb(&status, bpbLength, bpb);

Bien entendu, le BPB téléchargé peut contenir d’autres paramètres de création de BLOB. Comme vous l’avez peut-être déjà remarqué, vous pouvez également passer BPB directement à addBlob(), mais si la plupart des blobs que vous allez ajouter sont dans le même format différent du format par défaut, alors l’utilisation de setDefaultBpb() est un peu plus efficace. Pour en revenir aux blobs partitionnés, l’appel de addBlob() ajoutera la première partition à l’objet blob, les appels suivants à appendBlobData() ajouteront d’autres partitions. N’oubliez pas que la taille du segment est limitée à « 64 Ko – 1 », essayer de transférer plus de données en un seul appel provoquera une erreur.

L’étape suivante consiste à travailler avec des flux BLOB existants, qui utilisent la méthode addBlobStream(). En l’utilisant, vous pouvez ajouter plus d’un BLOB à un lot en un seul appel. Un flux BLOB est une séquence de BLOB, chacun d’entre eux commençant par un en-tête BLOB. L’en-tête doit être correctement aligné — pour cela, il y a un appel spécial dans l’interface IBatch :

unsigned alignment = batch->getBlobAlignment(&status);

Il est supposé que tous les composants du flux BLOB dans le lot doivent être alignés au moins sur la limite d’alignement, y compris la taille des portions de flux passées à addBlobStream(), qui doit être un multiple de cet alignement. L’en-tête contient 3 champs : un objet BLOB de 8 octets (qui doit être différent de zéro), une taille totale de BLOB de 4 octets et une taille BPB de 4 octets. La taille totale d’un objet blob inclut le BPB à l’intérieur, ce qui signifie que vous pouvez toujours trouver l’objet blob suivant dans le flux dans un octet de taille d’objet blob après l’en-tête (y compris l’alignement). Le BPB (s’il est présent, c’est-à-dire si la taille du BPB n’est pas nul) est placé immédiatement après l’en-tête. Une fois que les données de l’objet blob BPB sont transférées, le format de l’objet blob varie selon que l’objet blob est diffusé en continu ou partitionné. Dans le cas d’un blob en streaming, il s’agit d’une simple séquence d’octets dont la taille est blob-size – BPB-size. Avec un blob partitionné, les choses sont un peu plus compliquées : les données de l’objet blob sont une collection de partitions, où chaque partition a le format suivant : la taille du segment est de 2 octets (elle doit être alignée sur la limite de IBatch::BLOB_SEGHDR_ALIGN), suivie des 2 octets du nombre d’octets qui y sont stockés.

Lorsqu’un BLOB est ajouté à un flux, sa taille n’est pas toujours connue à l’avance. Pour éviter d’avoir une mémoire tampon trop grande pour cet objet blob (n’oubliez pas que la taille doit être spécifiée dans l’en-tête BLOB avant les données de l’objet blob), vous pouvez utiliser un enregistrement de continuation BLOB. Dans l’en-tête BLOB, vous laissez la taille du BLOB avec la valeur connue lors de la création de l’en-tête, et ajoutez un enregistrement de continuation qui a exactement le même format que l’en-tête BLOB, mais ici l’ID BLOB doit être égal à zéro, et la taille du BPB doit toujours être également nulle. En règle générale, vous aurez besoin d’une entrée de continuation pour chaque appel à addBlobStream().

Cette dernière méthode, qui est utilisée pour travailler avec les BLOB, est distincte des trois premières, qui transmettent les données BLOB avec le reste des données du lot - elle doit s’enregistrer dans l’identifiant du package BLOB créé à l’aide de l’API BLOB standard. Cela peut être inévitable si vous souhaitez empaqueter un blob très volumineux. N’utilisez pas l’ID d’objet blob dans le lot, car cela entraînerait une erreur d’ID d’objet blob non valide lors de l’exécution du lot. Au lieu de cela, exécutez :

batch->registerBlob(&status, &realId, &msg->desc);

Si la politique BLOB amène le moteur Firebird à générer des BLOB, ce code est suffisant pour enregistrer correctement le BLOB existant dans le lot. Dans d`autres cas, vous devrez assigner l`identificateur correct (à partir du paquet POV) pour msg→desc.

Presque toutes les méthodes mentionnées sont utilisées dans 11.batch.cpp - utilisez-le pour voir un exemple en direct de traitement par lots dans firebird.

Quelques mots sur l’accès aux traitements par lots à partir de l’API ISC - vous pouvez exécuter une instruction ISC préparée en mode batch. Pour ce faire, deux nouvelles fonctions d’API ont été ajoutées, à savoir fb_get_transaction_interface et fb_get_statement_interface, qui permettent d’accéder à des interfaces pertinentes identiques aux descripteurs ISC existants. Un exemple de ceci est présent dans le 12.batch_isc.cpp.

Utilisation des événements

L’interface utilisateur de l’événement n’a pas été finalisé dans Firebird 4.0, nous nous attendons à ce qu’il y ait quelque chose de plus intéressant dans la prochaine version. Le support minimum existant est le suivant : IAttachment contient la méthode queEvents(), qui remplit presque les mêmes fonctions que l’appel isc_que_events(). Au lieu de la paire de paramètres FPTR_EVENT_CALLBACK ast et void* arg nécessaires pour appeler le code utilisateur lorsqu’un événement se produit dans Firebird, l’interface de rappel IEventCallback est utilisée. Il s’agit d’une approche traditionnelle qui permet d’éviter les appels vide* dangereux dans une fonction personnalisée. Une autre différence importante est qu’au lieu d’un identificateur d’événement (une sorte de gestionnaire), cette fonction renvoie une référence à l’interface IEvents, qui a une méthode cancel() utilisée pour arrêter l’événement d’écoute. Contrairement à l’identifiant, qui est détruit automatiquement à l’arrivée d’un événement, une interface ne peut pas être détruite automatiquement si l’événement est reçu juste avant l’appel de la méthode cancel(), cela provoquera une erreur de segmentation car l’interface sera déjà détruite. Par conséquent, une fois l’événement reçu, l’interface IEvents doit être explicitement libérée. Cela peut être fait, par exemple, juste avant de demander un événement à la file d’attente:

events->release();
events = NULL;

events = attachment->queEvents(&status, this, eveLen, eveBuffer);

Définir le pointeur de l’interface à NULL est utile en cas d’exception dans queEvents. À d’autres égards, la gestion des événements n’a pas changé par rapport à l’API ISC. Pour plus d’informations, utilisez notre exemple 08.events.cpp.

Utilisation des Services

Pour commencer à utiliser les services, vous devez d’abord vous connecter au gestionnaire de services. Cela se fait à l’aide de la méthode attachServiceManager() de l’interface IProvider. Cette méthode renvoie l’interface IService, qui est ensuite utilisée pour communiquer avec le service. Pour préparer le SPB à se connecter au Service Manager, vous pouvez utiliser IXpbBuilder :

IXpbBuilder* spb1 = utl->getXpbBuilder(&status, IXpbBuilder::SPB_ATTACH, NULL, 0);

spb1->insertString(&status, isc_spb_user_name, "sysdba");
spb1->insertString(&status, isc_spb_password, "masterkey");

et connectez-vous :

IService* svc = prov->attachServiceManager(&status, "service_mgr",
    spb1->getBufferLength(&status), spb1->getBuffer(&status));

À l’aide de IService, vous pouvez effectuer des actions disponibles pour les services, telles que l’exécution de services, ainsi que demander diverses informations sur les utilitaires en cours d’exécution et du serveur dans son ensemble. Lors de la demande d’informations, il y a une limitation : le format du bloc de paramètres utilisé par la méthode query() n’est pas pris en charge par IXpbBuilder dans Firebird 4. Le support sera probablement ajouté dans les futurs versions, dans Firebird 4 vous devrez créer et analyser ce bloc manuellement. Le format de ce bloc rdt identique à l’ancien format (utilisé dans l’API ISC) un pour un.

Pour démarrer le service, vous devez d’abord créer le SPB approprié :

IXpbBuilder* spb2 = utl->getXpbBuilder(&status, IXpbBuilder::SPB_START, NULL, 0);

et y ajouter les éléments nécessaires. Par exemple, pour imprimer les statistiques de chiffrement de la base de données « employé » dans SPB, vous devez mettre ce qui suit :

spb2->insertTag(&status, isc_action_svc_db_stats);
spb2->insertString(&status, isc_spb_dbname, "employee");
spb2->insertInt(&status, isc_spb_options, isc_spb_sts_encryption);

Après cela, le service peut être démarré à l’aide de la méthode start() de l’interface IService :

svc->start(&status, spb2->getBufferLength(&status), spb2->getBuffer(&status));

De nombreux services en cours d’exécution (y compris le gstat mentionné ici) renvoient des informations textuelles au moment de l’exécution. Pour l’afficher, vous devez demander ces informations au service en cours d’exécution ligne par ligne. Pour ce faire, appelez la méthode query() de l’interface IService avec les blocs de paramètres appropriés pour la réception et l’envoi. Le bloc d’envoi peut contenir diverses informations auxiliaires (par exemple, un délai d’expiration de la demande du service) ou des informations qui doivent être transmises à l’utilitaire stdin, où il peut être vide dans le cas le plus simple. Le bloc d’ingestion doit contenir une liste des balises que vous souhaitez recevoir du service. Pour la plupart des services publics, il s’agit de la seule « isc_info_svc_line » :

const unsigned char receiveItems1[] = {isc_info_svc_line};

De plus, il a besoin d’une mémoire tampon pour demander ces informations :

unsigned char results[1024];

Après ces étapes préliminaires, nous sommes prêts à demander des informations au service dans une boucle (chaque ligne est retournée en un seul appel à query()) :

do
{
    svc->query(&status, 0, NULL,
               sizeof(receiveItems1), receiveItems1,
               sizeof(results), results);
} while (printInfo(results, sizeof(results)));

Dans cet exemple, nous supposons que la fonction printInfo() renvoie TRUE tant que le service renvoie un bloc de résultat contenant la ligne de sortie suivante, c’est-à-dire jusqu’à la fin du flux de données du service. Le format du bloc de résultat varie d’un service à l’autre, et certains services, tels que gsec, créent des formats historiques qui ne sont pas triviaux pour l’analyse, mais qui dépassent le cadre de ce chapitre. L’exemple de travail minimal printInfo() est présent dans l’exemple 09.service.cpp.

La même méthode de requête est utilisée pour récupérer des informations sur le serveur, mais dans ce cas, la fonction de requête n’est pas appelée dans une boucle, c’est-à-dire que le tampon doit être suffisamment grand pour contenir toutes les informations à la fois. Ce n’est pas trop difficile, car ces appels ne renvoient généralement pas beaucoup de données. Comme dans le cas précédent, vous devez commencer par placer les éléments nécessaires dans le bloc d’accueil — dans notre exemple, il s’agit de isc_info_svc_server_version :

const unsigned char receiveItems2[] = {isc_info_svc_server_version};

Un tampon de résultat existant d’un appel précédent peut être réutilisé. Dans ce cas, vous n’avez pas besoin d’une boucle :

svc->query(&status, 0, NULL,
           sizeof(receiveItems2), receiveItems2,
           sizeof(results), results);

printInfo(results, sizeof(results));

Une fois les tâches de service terminées, n’oubliez pas de désactiver le service :

svc->detach(&status);

Écriture de plugins

Pour écrire un plugin, vous devez implémenter certaines interfaces et mettre votre implémentation dans une bibliothèque dynamique (.dll sous Windows ou .so sous Linux), qui s’appelle un module plugin ou simplement un module. Dans la plupart des cas, un seul plugin est hébergé dans une bibliothèque dynamique, mais pas nécessairement. L’une de ces interfaces, IPluginModule, est modulaire (comme son nom l’indique plus ou moins), les autres sont des plugins. De plus, chaque module de plugin doit contenir un point d’entrée spécial exporté firebird_plugin(), dont le nom est spécifié dans le fichier include/firebird/Interfaces.h comme FB_PLUGIN_ENTRY_POINT.

Dans la partie précédente, nous avons principalement décrit comment utiliser les interfaces existantes, ici l’accent sera mis sur l’implémentation des interfaces écrites par vous-même. Bien sûr, vous pouvez et devez utiliser les interfaces existantes communes pour accéder aux bases de données Firebird (déjà décrites) et quelques interfaces supplémentaires spécialement conçues pour les plugins.

Ensuite, l’exemple de plugin de chiffrement de base de données DbCrypt.cpp est activement utilisé. C’est une bonne idée de lire cet exemple pour vous faciliter la lecture du chapitre.

Mise en œuvre d’un module de plugin

Les plugins interagissent activement avec un composant spécial de Firebird appelé le gestionnaire de plugins. En particulier, le gestionnaire de plugins doit savoir quels modules de plugins ont été chargés et doit être averti si le système d’exploitation tente de décharger l’un de ces modules sans une commande explicite du gestionnaire de plugins (cela peut se produire principalement lors de l’utilisation du serveur embarqué — lorsque le programme appelle exit() ou que la bibliothèque principale de Firebird fbclient est déchargée). L’objectif principal de l’interface IPluginModule est la notification. Tout d’abord, vous devez décider comment informer Firebird que le module sera déchargé. Lorsqu’une bibliothèque dynamique est déchargée pour une raison quelconque, de nombreuses actions spécifiques au système d’exploitation sont effectuées, et certaines de ces actions peuvent être utilisées pour détecter ce fait dans le programme. Lors de l’écriture de plugins distribués avec firebird, nous utilisons toujours l’appel global de destructeur de variables. Le gros « plus » de cette méthode est qu’elle est indépendante du système d’exploitation (bien que la fonction exit() puisse également être utilisée avec succès). Mais l’utilisation d’un destructeur permet de concentrer facilement presque tout ce qui concerne la détection de déchargement dans une seule classe, tout en implémentant l’interface IPluginModule.

La mise en œuvre minimale est la suivante :

class PluginModule : public IPluginModuleImpl<PluginModule, CheckStatusWrapper>
{

private:
  IPluginManager* pluginManager;

public:
  PluginModule()
    : pluginManager(NULL)
  { }


  ~PluginModule()
  {
    if (pluginManager)
    {
      pluginManager->unregisterModule(this);
      doClean();
    }
  }

  void registerMe(IPluginManager* m)
  {
    pluginManager = m;
    pluginManager->registerModule(this);
  }

  void doClean()
  {
    pluginManager = NULL;
  }

};

Le seul membre des données est l’interface du gestionnaire de plugins IPluginManager. Il est passé à la fonction registerModule() et stocké dans une variable privée, tandis que le module est enregistré dans le gestionnaire de plugins en utilisant la méthode callModule() avec sa propre adresse comme seul paramètre. La variable pluginManager stocke non seulement un pointeur vers l’interface, mais sert également d’indicateur indiquant que le module est enregistré. Lorsque le destructeur d’un module enregistré est appelé, il avertit le gestionnaire de plugin d’un déchargement inattendu avec un appel à unregisterModule() qui passe le pointeur à lui-même. Lorsque le gestionnaire de plugins décharge le module sur une base régulière, la première chose que l’appel de la méthode doClean() est de changer l’état du module en unregistered, ce qui évite l’appel à unregisterModule().

Après avoir implémenté l’interface du plugin IPluginModule, nous sommes tombés sur la première interface nécessaire pour implémenter les plugins – IPluginManager. Il sera activement utilisé plus tard, il est peu probable que vous ayez besoin du reste des membres de cette classe après l’avoir copié dans votre programme. N’oubliez pas de déclarer une variable globale de ce type et d’appeler la fonction registerMe() à partir de FB_PLUGIN_ENTRY_POINT.

L’interface principale de n’importe quel plugin

Commençons à implémenter le plugin lui-même. Le type de l’interface principale dépend évidemment du type de plugin, mais ils sont tous basés sur l’interface générale IPluginBase avec comptage de références, qui effectue des tâches communes (et très simples) pour tous les plugins. Chaque plugin a un objet (également avec comptage de liens) auquel il appartient. Afin d’effectuer une gestion intelligente du cycle de vie des plugins, tout plugin doit être en mesure de stocker des informations sur le propriétaire et de les transmettre au gestionnaire de plugins sur demande. Cela signifie que chaque plugin doit implémenter les deux méthodes triviales setOwner() et getOwner() contenues dans l’interface IPluginBase. Les méthodes dépendantes du type de plugin sont certainement plus intéressantes — elles sont discutées dans la partie description de l’interface.

Jetons un coup d’œil à la partie implémentation typique de n’importe quel plugin (j’utilise spécifiquement le type SomePlugin inexistant ici) :

class MyPlugin : public ISomePluginImpl<MyPlugin, CheckStatusWrapper>
{
public:
  explicit MyPlugin(IPluginConfig* cnf) noexcept
     : config(cnf), refCounter(0), owner(NULL)
  {
    config->addRef();
  }
  ...

Le constructeur reçoit l’interface de configuration du plugin en tant que paramètre. Si vous comptez configurer le plugin d’une manière ou d’une autre, il est recommandé d’enregistrer cette interface dans votre plugin et de l’utiliser plus tard. Cela vous permettra d’utiliser le style de configuration global de Firebird, ce qui permettra aux utilisateurs d’avoir une configuration familière et de minimiser le codage. Bien sûr, lors de l’enregistrement d’une interface de lien, il est préférable de ne pas oublier d’y ajouter un lien. N’oubliez pas non plus de définir le nombre de liens sur 0 et le propriétaire du plugin sur NULL.

  ~MyPlugin()
  {
    config->release();
  }

Le destructeur libère l’interface de configuration. Notez que nous ne modifions pas le nombre de liens de notre propriété car elle ne nous appartient pas.

  // IRefCounted implementation
  int release()
  {
    if (--refCounter == 0)
    {
      delete this;
      return 0;
    }
    return 1;
  }


  void addRef()
  {
    ++refCounter;
  }

Il s’agit d’une implémentation tout à fait typique d’un objet avec comptage de références.

  // IPluginBase implementation
  void setOwner(IReferenceCounted* o)
  {
    owner = o;
  }

  IReferenceCounted* getOwner()
  {
    return owner;
  }

Comme promis, l’implémentation d’IPluginBase est triviale.

  // ISomePlugin implementation
  // … here go various methods required for particular plugin type
private:
  IPluginConfig* config;
  std::atomic_int refCounter;
  IReferenceCounted* owner;
};

Dans cet exemple, la partie formelle de l’implémentation de l’interface principale du plugin est terminée. Après avoir ajouté des méthodes spécifiques au type (et peut-être écrit du code pour les rendre utiles), l’interface est prête.

Constructeur de plugins

Une autre interface requise pour que le plugin fonctionne est IPluginFactory. La class instancie le plugin et le renvoie au gestionnaire de plugins. La class ressemble généralement à ceci :

class Factory : public IPluginFactoryImpl<Factory, CheckStatusWrapper>
{
public:
  IPluginBase* createPlugin(CheckStatusWrapper* status,
                            IPluginConfig* factoryParameter)
  {
    MyPlugin* p = new MyPlugin(factoryParameter);
    p->addRef();
    return p;
  }
};

Il faut faire attention au fait que même dans le cas où le code de la fonction peut lever des exceptions (le nouvel opérateur peut lever une exception lorsque la mémoire est épuisée), vous n’avez pas toujours besoin de définir manuellement le bloc try/catch - l’implémentation des interfaces Firebird fait le travail pour vous, dans l’implémentation IPluginFactory ce traitement a lieu dans le modèle IPluginFactoryImpl. Notez que les shims d’état par défaut ne gèrent que l’exception FbException. Mais si vous travaillez sur un grand projet, définissez votre propre wrapper, auquel cas vous pouvez gérer n’importe quel type d’exception C++ et transmettre des informations utiles à ce sujet à partir de votre plugin.

Point d’initialisation du module Plugin

Lorsque le gestionnaire de plugins charge un module de plugin, il appelle la routine d’initialisation du module, qui est la seule fonction FB_PLUGIN_ENTRY_POINT du plugin exportée. Pour écrire le code, il aura besoin de deux variables globales : le module de plugin et la constructeur de plugins. Dans notre cas, il s’agit de :

PluginModule module;

Factory factory;

Si un module contient plus d’un plugin, vous aurez besoin d’un constructeur pour chaque plugin.

Pour FB_PLUGIN_ENTRY_POINT il ne faut pas oublier qu’il doit être exporté depuis le module plugin, cela nécessite de prendre en compte certaines caractéristiques du système d’exploitation. Pour ce faire, nous utilisons la macro FB_DLL_EXPORT définie dans examples/interfaces/ifaceExamples.h. Si vous êtes sûr que vous n’utilisez le plugin que pour un système d’exploitation spécifique, vous pouvez rendre cet endroit un peu plus facile. Au minimum, la fonction doit enregistrer le module et toutes les constructeurs dans le gestionnaire de plugins :

extern "C" void FB_DLL_EXPORT FB_PLUGIN_ENTRY_POINT(IMaster* master)
{
  IPluginManager* pluginManager = master->getPluginManager();
  module.registerMe(pluginManager);
  pluginManager->registerPluginFactory(IPluginManager::TYPE_DB_CRYPT,
                                       "fbSampleDbCrypt",
                                       &factory);
}

Tout d’abord, nous appelons la fonction récemment écrite PluginModule::registerMe(), qui enregistre le IPluginManager pour une utilisation ultérieure et enregistre notre module de plugin. Enregistrez ensuite la class (ou les class s’il y aura plusieurs plugins dans un module). Nous devons passer le bon type de plugin (les types valides sont listés dans l’interface IPluginManager) et le nom sous lequel le plugin sera enregistré. Dans le cas le plus simple, il devrait être le même que le nom de la bibliothèque dynamique du plugin. Cette règle vous aidera à ne pas configurer le plugin manuellement dans plugins.conf.

Notez que contrairement aux applications, les plugins n’ont pas besoin d’utiliser fb_get_master_interface() pour obtenir iMaster. Au lieu de cela, vous devez utiliser l’instance passée à FB_PLUGIN_ENTRY_POINT. Si vous avez besoin d’une interface iMaster dans votre plugin, assurez-vous de la conserver dans cette fonctionnalité.

Interfaces de A à Z

Dans ce glossaire, nous ne répertorions pas les interfaces qui ne sont pas activement utilisées (par exemple, IRequest, qui sont principalement nécessaires pour prendre en charge les anciennes requêtes d’API ISC). La même référence peut être obtenue à partir de certaines méthodes (par exemple compileRequest() dans IAttachment). Pour les interfaces/méthodes qui ont une contrepartie directe dans l’ancienne API, cet analogie sera spécifié.

Interfaces principales

IAttachment

L’interface IAttachment remplace isc_db_handle.

  1. getInfo

    void getInfo(StatusType* status,
                 unsigned itemsLength,
                 const unsigned char* items,
                 unsigned bufferLength,
                 unsigned char* buffer)

    Remplace isc_database_info().

  2. startTransaction

    ITransaction* startTransaction(StatusType* status,
                                   unsigned tpbLength,
                                   const unsigned char* tpb)

    Remplace partiellement isc_start_multiple(), utilise le coordinateur pour exécuter plus d’une transaction distribuée. Permet de combiner 2 transactions en une seule transaction distribuée.

  3. reconnectTransaction

    ITransaction* reconnectTransaction(StatusType* status,
                                       unsigned length,
                                       const unsigned char* id)

    Permet de se connecter à une transaction en cours de traitement. Le paramètre Id contient le numéro de transaction dans le format de réseau de la longueur spécifiée.

  4. compileRequest

    IRequest* compileRequest(StatusType* status,
                             unsigned blrLength,
                             const unsigned char* blr)

    prise en charge de l’API ISC.

  5. transactRequest

    void transactRequest(StatusType* status,
                         ITransaction* transaction,
                         unsigned blrLength,
                         const unsigned char* blr,
                         unsigned inMsgLength,
                         const unsigned char* inMsg,
                         unsigned outMsgLength,
                         unsigned char* outMsg)

    prise en charge de l’API ISC.

  6. createBlob

    IBlob* createBlob(StatusType* status,
                      ITransaction* transaction,
                      ISC_QUAD* id,
                      unsigned bpbLength,
                      const unsigned char* bpb)

    Crée un nouvel objet blob, stocke son identificateur dans id, remplace isc_create_blob2().

  7. openBlob

    IBlob* openBlob(StatusType* status,
                    ITransaction* transaction,
                    ISC_QUAD* id,
                    unsigned bpbLength,
                    const unsigned char* bpb)

    Ouvre un objet blob existant, remplace isc_open_blob2().

  8. getSlice

    int getSlice(StatusType* status,
                 ITransaction* transaction,
                 ISC_QUAD* id,
                 unsigned sdlLength,
                 const unsigned char* sdl,
                 unsigned paramLength,
                 const unsigned char* param,
                 int sliceLength,
                 unsigned char* slice)

    prise en charge de l’API ISC.

  9. putSlice

    void putSlice(StatusType* status,
                  ITransaction* transaction,
                  ISC_QUAD* id,
                  unsigned sdlLength,
                  const unsigned char* sdl,
                  unsigned paramLength,
                  const unsigned char* param,
                  int sliceLength,
                  unsigned char* slice)

    prise en charge de l’API ISC.

  10. executeDyn

    void executeDyn(StatusType* status,
                    ITransaction* transaction,
                    unsigned length,
                    const unsigned char* dyn)

    prise en charge de l’API ISC.

  11. prepare

    IStatement* prepare(StatusType* status,
                        ITransaction* tra,
                        unsigned stmtLength,
                        const char* sqlStmt,
                        unsigned dialect,
                        unsigned flags)

    Remplace isc_dsql_prepare(). Un paramètre d’indicateurs supplémentaire vous permet de contrôler quelles informations seront préchargées à partir du moteur en une seule fois (c’est-à-dire dans un seul paquet réseau pour une opération à distance).

  12. execute

    ITransaction* execute(StatusType* status,
                          ITransaction* transaction,
                          unsigned stmtLength,
                          const char* sqlStmt,
                          unsigned dialect,
                          IMessageMetadata* inMetadata,
                          void* inBuffer,
                          IMessageMetadata* outMetadata,
                          void* outBuffer)

    Exécute n’importe quelle instruction SQL, à l’exception du renvoi de plusieurs lignes de données. Analogie partiel de isc_dsql_execute2() — Les entrées et sorties XSLQDA ont été remplacées par des messages d’entrée et de sortie avec les tampons correspondants.

  13. openCursor

    IResultSet* openCursor(StatusType* status,
                           ITransaction* transaction,
                           unsigned stmtLength,
                           const char* sqlStmt,
                           unsigned dialect,
                           IMessageMetadata* inMetadata,
                           void* inBuffer,
                           IMessageMetadata* outMetadata,
                           const char* cursorName,
                           unsigned cursorFlags)

    Exécute une instruction SQL qui renvoie potentiellement plusieurs lignes de données. Renvoie l’interface IResultSet, qui est utilisé pour récupérer ces données. Le format de la sortie est déterminé par le paramètre outMetadata, si NULL est défini, le format par défaut sera utilisé. Le paramètre cursorName spécifie le nom du curseur ouvert (analogue à isc_dsql_set_cursor_name()). Le paramètre cursorFlags est nécessaire pour ouvrir le pointeur de curseur bidirectionnel en spécifiant la valeur IStatement::CURSOR_TYPE_SCROLLABLE.

  14. queEvents

    IEvents* queEvents(StatusType* status,
                       IEventCallback* callback,
                       unsigned length,
                       const unsigned char* events)

    Remplace l’appel isc_que_events(). Au lieu d’une fonction avec un paramètre void*, une interface est utilisée.

  15. cancelOperation

    void cancelOperation(StatusType* status, int option)

    Remplace fb_cancel_operation().

  16. ping

    void ping(StatusType* status)

    Vérifie l’état de la connexion. Si le test échoue, la seule opération de connexion possible est de la fermer.

  17. getIdleTimeout

    unsigned getIdleTimeout(StatusType* status)

    Renvoie le délai d’inactivité de la connexion en secondes.

  18. setIdleTimeout

    void setIdleTimeout(StatusType* status, unsigned timeOut)

    Définit le délai d’inactivité de la connexion en secondes.

  19. getStatementTimeout

    unsigned getStatementTimeout(StatusType* status)

    Renvoie le délai d’exécution de la requête en millisecondes.

  20. setStatementTimeout

    void setStatementTimeout(StatusType* status, unsigned timeOut)

    Définit le délai d’exécution de la requête en millisecondes.

  21. createBatch

    IBatch* createBatch(StatusType* status,
                        ITransaction* transaction,
                        unsigned stmtLength,
                        const char* sqlStmt,
                        unsigned dialect,
                        IMessageMetadata* inMetadata,
                        unsigned parLength,
                        const unsigned char* par)

    Prépare sqlStmt et crée une interface IBatch prête à accepter plusieurs ensembles de paramètres d’entrée au format inMetadata. Si vous laissez NULL dans inMetadata, le package utilisera le format par défaut pour sqlStmt. Un bloc de paramètres peut être passé à la fonction createBatch(), qui permet de configurer le comportement du paquet.

  22. createReplicator

    IReplicator* createBatch(StatusType* status)

    Crée une instance du plugin de réplication avec l’interface IReplicator.

  23. detach

    void detach(StatusType* status)

    Se détache de la base de données courante. Remplace isc_detach_database(). En cas de succès, l’interface est libérée.

  24. dropDatabase

    void dropDatabase(StatusType* status)

    Supprime la base de données courante. Remplace isc_drop_database(). En cas de succès, l’interface est libérée.

IBatch

L’interface IBatch vous permet de traiter plusieurs ensembles de paramètres dans une seule instruction.

  1. add

    void add(StatusType* status, unsigned count, const void* inBuffer)

    Ajoute le nombre de messages de inBuffer au lot. La taille totale des messages pouvant être ajoutés à un lot est limitée par le paramètre « TAG_BUFFER_BYTES_SIZE » lors de la création du lot.

  2. addBlob

    void addBlob(StatusType* status,
                 unsigned length, const void* inBuffer,
                 ISC_QUAD* blobId,
                 unsigned bpbLength, const unsigned char* bpb)

    Ajoute un octet length de inBuffer au paquet, le BLOB est situé à blobId. Si le blob doit être créé avec des paramètres autres que la valeur par défaut, BPB peut être passé (le format est le même que celui utilisé dans Attachment::createBlob). La taille totale des objets blob intégrés qui peuvent être ajoutés à un package (y compris les BPB facultatifs, les en-têtes BLOB, la taille du segment avec alignement) est limitée par le paramètre TAG_BUFFER_BYTES_SIZE lors de la création du package (affecte toutes les méthodes orientées BLOB à l’exception de registerBlob()).

  3. appendBlobData

    void appendBlobData(StatusType* status, unsigned length, const void* inBuffer)

    Étend le dernier BLOB ajouté en ajoutant des octets de la longueur length prise à partir de l’adresse inBuffer.

  4. addBlobStream

    addBlobStream(StatusType* status, unsigned length, const void* inBuffer)

    Ajoute des données BLOB (il peut s’agir de plusieurs objets ou d’une partie d’un seul BLOB) à un lot. L’en-tête de chaque BLOB du flux est aligné sur la limite getBlobAlignment() et contient 3 champs : le premier est le BLOB de 8 octets (au format ISC_QUAD), le second est la longueur de 4 octets du BLOB et le troisième est la longueur de 4 octets du BPB. L’en-tête d’objet blob ne doit pas franchir les limites de la mémoire tampon dans cet appel de fonction. Les données BPB sont placées immédiatement après l’en-tête, suivies des données d’objet blob. La longueur de l’objet blob inclut le BPB (le cas échéant). Toutes les données peuvent être distribuées sur plusieurs appels à addBlobStream(). Les données d’objets blob, à leur tour, peuvent être structurées dans le cas d’un objet blob partitionné, voir le chapitre Modification des données par lots.

  5. registerBlob

    void registerBlob(StatusType* status, const ISC_QUAD* existingBlob, ISC_QUAD* blobId)

    Permet d’utiliser des BLOB ajoutés à l’aide de l’interface standard IBlob dans le package. Cette fonction contient 2 paramètres ISC_QUAD*, il est important de ne pas les confondre – le deuxième paramètre (existingBlob) est un pointeur vers le BLOB déjà ajouté en dehors de la portée du package, le troisième (blobId) pointe vers le BLOB qui sera placé dans le message de ce package`.

  6. execute

    IBatchCompletionState* execute(StatusType* status, ITransaction* transaction)

    Exécute le paquet avec les paramètres qui lui sont transmis dans les messages. Si le paramètre MULTIERROR n’est pas défini dans le bloc de paramètres lors de la création d’un lot, l’exécution du lot sera arrêtée après la première erreur, un nombre illimité d’erreurs peut se produire en mode MULTIERROR, après une erreur l’exécution se poursuit avec le message suivant. Cette fonction renvoie l’interface IBatchCompletionState, qui contient toutes les informations demandées sur les résultats de l’exécution du lot.

  7. cancel

    void cancel(StatusType* status)

    Efface les tampons de message et les objets BLOB, en rétablissant le paquet dans l’état dans lequel il se trouvait lors de sa création. Notez que l’interface de comptage de références IBatch ne contient pas de fonction spéciale pour la fermer, utilisez release() pour le faire.

  8. getBlobAlignment

    unsigned getBlobAlignment(StatusType* status)

    Renvoie l’alignement requis pour les données mises en mémoire tampon par la fonction addBlobStream().

  9. getMetadata

    IMessageMetadata* getMetadata(StatusType* status)

    Renvoie le format des métadonnées utilisées dans les messages batch.

  10. setDefaultBpb

    void setDefaultBpb(StatusType* status, unsigned parLength, const unsigned char* par)

    Spécifie le BPB qui sera utilisé pour tous les objets blob qui n’ont pas de BPB par défaut. Doit être appelé avant qu’un message ou un objet blob ne soit ajouté à un lot.

  11. getInfo

    void getInfo(StatusType* status,
                 unsigned itemsLength, const unsigned char* items,
                 unsigned bufferLength, unsigned char* buffer)

    Demande des informations sur le package.

Balise pour le bloc de paramètres :

  • VERSION1

Balises pour les clusters dans le bloc de paramètres :

  • TAG_MULTIERROR (0/1) – Peut avoir plus d’un message d’erreur.

  • TAG_RECORD_COUNTS (0/1) - Enregistrement des enregistrements de messages modifiés ;

  • TAG_BUFFER_BYTES_SIZE (integer) - Taille maximale possible de la mémoire tampon sur le serveur Firebird (16 Mo par défaut, 256 Mo maximum) ;

  • TAG_BLOB_POLICY - La stratégie utilisée pour stocker les objets blob

  • TAG_DETAILED_ERRORS (integer) - Nombre de vecteurs contenant des informations d’erreur détaillées sont stockés dans l’état d’achèvement (64 Mo par défaut, 256 Mo au maximum).

Stratégies utilisées pour stocker les objets BLOB :

  • BLOB_NONE – vous ne pouvez pas utiliser de blobs intégrés (registerBlob() fonctionne de toute façon) ;

  • BLOB_ID_ENGINE - Les BLOBs sont ajoutés un par un, les blobs sont générés par le moteur Firebird ;

  • BLOB_ID_USER - Les BLOB sont ajoutés un par un, les BLOBs sont générés par l’utilisateur ;

  • BLOB_STREAM - Les objets blob sont ajoutés au flux.

Éléments autorisés dans l’appel getInfo() :

  • INF_BUFFER_BYTES_SIZE – Taille maximale possible de la mémoire tampon (définie dans TAG_BUFFER_BYTES_SIZE) ;

  • INF_DATA_BYTES_SIZE - La taille des messages qui ont déjà été ajoutés ;

  • INF_BLOBS_BYTES_SIZE - Taille des objets blob qui ont déjà été ajoutés ;

  • INF_BLOB_ALIGNMENT - l’alignement requis pour les données BLOB (duplique getBlobAlignment).

IBatchCompletionState

IBatchCompletionState — Une interface à usage unique, toujours retournée par la méthode execute() de l’interface IBatch. Il contient plus ou moins (en fonction des paramètres passés lors de la création de l’IBatch) des informations détaillées sur les résultats de l’exécution du lot.

  1. getSize

    unsigned getSize(StatusType* status)

    Renvoie le nombre total de messages traités.

  2. getState

    int getState(StatusType* status, unsigned pos)

    Renvoie le résultat d’un message avec le numéro pos. Pour toute erreur avec le message, il s’agit d’une constante EXECUTE_FAILED, la valeur de retour en cas de succès dépend de la présence du paramètre RECORD_COUNTS lors de la création du paquet. Lorsqu’elle est présente et qu’elle a une valeur différente de zéro, le nombre d’enregistrements insérés, mis à jour ou supprimés lors du traitement d’un message particulier est renvoyé, sinon la constante SUCCESS_NO_INFO est renvoyée.

  3. findError

    unsigned findError(StatusType* status, unsigned pos)

    Recherche le message suivant (commençant par pos) à l’origine de l’erreur. En l’absence d’un tel message, la constante NO_MORE_ERRORS est renvoyée. Le nombre de vecteurs d’état renvoyés dans cette interface est limité par la valeur du paramètre DETAILED_ERRORS lors de la création du package.

  4. getStatus

    void getStatus(StatusType* status, IStatus* to, unsigned pos)

    Renvoie des informations détaillées (vecteur d’état complet) sur l’erreur qui s’est produite lors du traitement du message pos. Pour faire la distinction entre les erreurs (dans IBatch::execute() ou dans IBatchCompletionState::getStatus()), cet état est renvoyé dans un paramètre to séparé, par opposition aux erreurs dans cet appel, qui sont placées dans le paramètre status.

Valeurs spéciales renvoyées par getState() :

  • EXECUTE_FAILED — Une erreur s’est produite lors du traitement de ce message.

  • SUCCESS_NO_INFO — Aucune information sur la mise à jour des dossiers n’a été recueillie.

La valeur spéciale renvoyée par findError() est :

  • NO_MORE_ERRORS – Il n’y a plus de messages d’erreur dans ce package.

IBlob

L’interface IBlob remplace l’interface isc_blob_handle.

  1. getInfo

    void getInfo(StatusType* status,
                 unsigned itemsLength,
                 const unsigned char* items,
                 unsigned bufferLength,
                 unsigned char* buffer)

    Remplace isc_blob_info().

  2. getSegment

    int getSegment(StatusType* status,
                   unsigned bufferLength,
                   void* buffer,
                   unsigned* segmentLength)

    remplace isc_get_segment(). En revanche, les erreurs isc_segstr_eof et isc_segment (qui ne sont pas réellement des erreurs) ne sont jamais renvoyées, mais les codes de sortie IStatus::RESULT_NO_DATA et IStatus::RESULT_SEGMENT sont renvoyés, renvoyant généralement IStatus::RESULT_OK.

  3. putSegment

    void putSegment(StatusType* status,
                    unsigned length,
                    const void* buffer)

    remplace isc_put_segment().

  4. cancel

    void cancel(StatusType* status)

    remplace isc_cancel_blob(). En cas de succès, l’interface est libérée.

  5. close

    void close(StatusType* status)

    remplace isc_close_blob(). En cas de succès, l’interface est libérée.

  6. seek

    int seek(StatusType* status,
             int mode,
             int offset)

    remplace isc_seek_blob().

IConfig

Interface IConfig — L’interface générale du fichier de configuration.

  1. find

    IConfigEntry* find(StatusType* status, const char* name)

    Recherche un enregistrement par son nom.

  2. findValue

    IConfigEntry* findValue(StatusType* status, const char* name, const char* value)

    Recherche un enregistrement par son nom et sa valeur

  3. findPos

    IConfigEntry* findPos(StatusType* status, const char* name, unsigned pos)

    Recherche un enregistrement par son nom et sa position. Si le fichier de configuration contient les lignes suivantes :

    Db=DBA
    Db=DBB
    Db=DBC

    l’appel de findPos(status, « Db », 2) renverra une entrée avec la valeur DBB.

IConfigManager

Interface IConfigManager — Une interface commune pour accéder à divers objets de configuration.

  1. getDirectory

    const char* getDirectory(unsigned code)

    Renvoie l’emplacement du répertoire correspondant dans l’instance courante de Firebird. Les codes d’annuaire de cet appel sont ci-dessous.

  2. getFirebirdConf

    IFirebirdConf* getFirebirdConf()

    Renvoie une interface permettant d’accéder aux valeurs de configuration par défaut (à partir de firebird.conf).

  3. getDatabaseConf

    IFirebirdConf* getDatabaseConf(const char* dbName)

    Renvoie une interface permettant d’accéder à une configuration spécifique à la base de données (prend en compte firebird.conf et la partie correspondante de database.conf).

  4. getPluginConfig

    IConfig* getPluginConfig(const char* configuredPlugin)

    Renvoie une interface permettant d’accéder à la configuration du plugin nommé.

  5. getInstallDirectory

    const char* getInstallDirectory()

    Renvoie le répertoire dans lequel Firebird est installé.

  6. getRootDirectory

    const char* getRootDirectory()

    Renvoie le répertoire racine de l’instance courante, dans le cas d’une instance unique, généralement le même que le répertoire d’installation.

  7. getDefaultSecurityDb

    const char* getDefaultSecurityDb()

    Renvoie le chemin d’accès par défaut (c’est-à-dire à l’exclusion des fichiers de configuration de compte) à la base de données de sécurité, utilisé principalement pour un usage interne, afin de garantir un accès correct à la base de données de sécurité sur un serveur avec plusieurs fournisseurs sans aucune configuration.

Catalogue des codes :

  • DIR_BIN — bin (utilitaires comme isql, gbak, gstat) ;

  • DIR_SBIN — sbin (fbguard et firebird serveur);

  • DIR_CONF — Répertoire des fichiers de configuration (firebird.conf, databases.conf, plugins.conf);

  • DIR_LIB — lib (fbclient, ib_util);

  • DIR_INC — include (ibase.h, firebird/Interfaces.h);

  • DIR_DOC — dossier de la documentation ;

  • DIR_UDF — UDF (ib_udf, fbudf);

  • DIR_SAMPLE — Dossier des exemples ;

  • DIR_SAMPLEDB — le répertoire où se trouve la base de données d’exemples (employee.fdb) ;

  • DIR_HELP — qli help (help.fdb);

  • DIR_INTL — Catalogue des bibliothèques d’internationalisation (fbintl) ;

  • DIR_MISC — divers fichiers (tels que le manifeste du programme de désinstallation, etc.) ;

  • DIR_SECDB — le répertoire dans lequel se trouve la base de données de sécurité (securityN.fdb) ;

  • DIR_MSG — le répertoire où se trouve le fichier de message (firebird.msg) ;

  • DIR_LOG — le répertoire dans lequel se trouve le fichier journal (firebird.log) ;

  • DIR_GUARD — répertoire dans lequel se trouve le service (fb_guard);

  • DIR_PLUGINS — Répertoire des plugins ([lib]Engine13.\{dll|so}).

IConfigEntry

Interface IConfigEntry — représente une entrée (Clé = Valeurs avec sous-titres possibles (sous-entrées)) dans le fichier de configuration de firebird.

  1. getName

    const char* getName()

    Renvoie le nom de la clé.

  2. getValue

    const char* getValue()

    Renvoie une valeur sous la forme d’une chaîne de caractères.

  3. getIntValue

    ISC_INT64 getIntValue()

    Traite la valeur comme un entier et la renvoie.

  4. getBoolValue

    FB_BOOLEAN getBoolValue()

    Traite la valeur comme booléenne et la renvoie.

  5. getSubConfig

    IConfig* getSubConfig(StatusType* status)

    Traite les sous-en-têtes comme un fichier de configuration séparé et renvoie l’interface IConfig correspondante.

IDecFloat16

L’interface IDecFloat16 permet de travailler avec le type de données DECFLOAT(16).

  1. toBcd

    void toBcd(const FB_DEC16* from, int* sign, unsigned char* bcd, int* exp)

    Convertit une valeur décimale à virgule flottante en BCD.

  2. toString

    void toString(StatusType* status, const FB_DEC16* from, unsigned bufferLength, char* buffer)

    Convertit une valeur décimale à virgule flottante en chaîne.

  3. fromBcd

    void fromBcd(int sign, const unsigned char* bcd, int exp, FB_DEC16* to)

    Collecte une valeur décimale à virgule flottante à partir de BCD.

  4. fromString

    void fromString(StatusType* status, const char* from, FB_DEC16* to)

    Collecte une valeur décimale à virgule flottante à partir d’une chaîne.

IDecFloat34

L’interface IDecFloat34 permet de travailler avec le type de données DECFLOAT(34).

  1. toBcd

    void toBcd(const FB_DEC34* from, int* sign, unsigned char* bcd, int* exp)

    Convertit une valeur décimale à virgule flottante en BCD.

  2. toString

    void toString(StatusType* status, const FB_DEC34* from, unsigned bufferLength, char* buffer)

    Convertit une valeur décimale à virgule flottante en chaîne.

  3. fromBcd

    void fromBcd(int sign, const unsigned char* bcd, int exp, FB_DEC34* to)

    Collecte une valeur décimale à virgule flottante à partir de BCD.

  4. fromString

    void fromString(StatusType* status, const char* from, FB_DEC34* to)

    Collecte une valeur décimale à virgule flottante à partir d’une chaîne.

IDtc

Interface IDtc — Coordonnatrice des transactions distribuées Permet de démarrer une transaction distribuée (fonctionne avec deux connexions ou plus). Contrairement à l’approche pré-FB3, où une transaction distribuée doit être démarrée de cette manière dès le début, le Firebird 3 Distributed Transaction Coordinator vous permet également de joindre des transactions déjà en cours en une seule transaction distribuée.

  1. join

    ITransaction* join(StatusType* status, ITransaction* one, ITransaction* two)

    Combine 2 transactions indépendantes en une transaction distribuée. En cas de succès, les deux transactions passées à join() sont libérées et les pointeurs vers elles ne doivent plus être utilisés.

  2. startBuilder

    IDtcStart* startBuilder(StatusType* status)

    Renvoie l’interface IDtcStart.

IDtcStart

Interface IDtcStart — remplace le tableau des structures TEB (passé isc_start_multiple() à l’API ISC). Cette interface rassemble les connexions (et les TPB correspondants) pour lesquelles la transaction distribuée doit être démarrée.

  1. addAttachment

    void addAttachment(StatusType* status, IAttachment* att)

    Ajoute une connexion, la transaction pour celle-ci sera déclenchée avec TPB par défaut.

  2. addWithTpb

    void addWithTpb(StatusType* status, IAttachment* att, unsigned length, const unsigned char* tpb)

    Ajoute la connexion et le TPB qui seront utilisés pour déclencher la transaction pour cette connexion.

  3. start

    ITransaction* start(StatusType* status)

    Démarre une transaction distribuée pour les connexions collectées. En cas de succès, il renvoie l’interface IDtcStart.

IEventCallback

Interface IEventCallback — Remplace la fonction d’événement utilisée dans l’appel isc_que_events(). Doit être implémenté par l’utilisateur pour suivre les événements à l’aide de la méthode IAttachment::queEvents().

  1. eventCallbackFunction

    void eventCallbackFunction(unsigned length, const unsigned char* events)

    Appelé chaque fois qu’un événement se produit.

IEvents

Interface IEvents — Remplace l’ID d’événement lors de l’utilisation de la surveillance des événements.

  1. cancel

    void cancel(StatusType* status)

    Annule la surveillance des événements démarrée dans IAttachment::queEvents().

IFirebirdConf

Interface IFirebirdConf — l’accès à la configuration de base de Firebird. Il est utilisé à la fois pour la configuration par défaut spécifiée par la configuration firebird.conf et pour chaque base de données connectée avec database.conf. Pour accélérer l’accès aux valeurs de configuration, les appels qui accèdent aux valeurs réelles utilisent une clé entière au lieu d’un nom de paramètre symbolique. La clé est stable pendant que le serveur est en cours d’exécution (c’est-à-dire que le plugin peut la récupérer une fois et l’utiliser pour obtenir la valeur des paramètres de configuration pour différentes bases de données).

  1. getKey

    unsigned getKey(const char* name)

    Renvoie une clé pour un nom de paramètre donné. ~0 (tous les bits sont égaux à 1) est renvoyé s’il n’y a pas de tel paramètre.

  2. asInteger

    ISC_INT64 asInteger(unsigned key)

    Renvoie la valeur d’un paramètre entier.

  3. asString

    const char* asString(unsigned key)

    Renvoie la valeur d’un paramètre de chaîne

  4. asBoolean

    FB_BOOLEAN asBoolean(unsigned key)

    Renvoie la valeur d’un paramètre booléen. Les abréviations standard (1/true/t/yes/y) sont traitées comme vraies, toutes les autres abréviations sont traitées comme fausses.

  5. getVersion

    unsigned getVersion(StatusType* status)

    Renvoie la version de Configuration Manager associée à cette interface. Différentes versions de Configuration Manager peuvent coexister sur le même serveur, par exemple, lorsqu’un ancien moteur de base de données est utilisé sur un serveur moderne. Notez que les clés (voir getKey()) des différentes versions ne correspondent pas et retourneront toujours 0/nullptr/false si elles ne sont pas utilisées correctement.

IInt128

L’interface IInt128 permet de travailler avec des entiers de 128 bits, qui est utilisé comme type de base pour les nombres numériques et décimaux avec une précision de plus de 18.

  1. toString

    void toString(StatusType* status, const FB_I128* from, int scale, unsigned bufferLength, char* buffer)

    Convertit une valeur entière de 128 bits en une chaîne prenant en charge la mise à l’échelle.

  2. fromString

    void fromString(StatusType* status, int scale, const char* from, FB_I128* to)

    Assemble une valeur entière de 128 bits à partir d’une chaîne prenant en charge la mise à l’échelle.

IMaster

IMaster — L’interface principale à partir de laquelle toutes les opérations avec l’API Firebird commencent.

  1. getStatus

    IStatus* getStatus()

    Renvoie une instance de l’interface IStatus.

  2. getDispatcher

    IProvider* getDispatcher()

    Renvoie une instance de l’interface IProvider implémentée par YValve (l’instance principale du fournisseur).

  3. getPluginManager

    IPluginManager* getPluginManager()

    Renvoie une instance de l’interface IPluginManager.

  4. getTimerControl

    ITimerControl* getTimerControl()

    Renvoie une instance d’une interface ITimerControl.

  5. getDtc

    IDtc* getDtc()

    Renvoie une instance d’une interface IDtc.

  6. getUtilInterface

    IUtil* getUtilInterface()

    Renvoie une instance d’une interface IUtil.

  7. getConfigManager

    IConfigManager* getConfigManager()

    Renvoie une instance d’une interface IConfigManager.

IMessageMetadata

Interface MessageMetadata — analogue partiel de XSQLDA (ne contient pas de données de message, mais uniquement des informations sur le format du message). Utilisé dans les appels liés à l’exécution d’instructions SQL.

  1. getCount

    unsigned getCount(StatusType* status)

    Renvoie le nombre de champs/paramètres dans le message. Dans tous les appels qui contiennent un paramètre index, cette valeur doit être : 0 >= index < getCount().

  2. getField

    const char* getField(StatusType* status, unsigned index)

    Renvoie le nom du champ.

  3. getRelation

    const char* getRelation(StatusType* status, unsigned index)

    Renvoie le nom de la relation (à partir de laquelle le champ a été sélectionné).

  4. getOwner

    const char* getOwner(StatusType* status, unsigned index)

    Renvoie le nom du propriétaire de la relation.

  5. getAlias

    const char* getAlias(StatusType* status, unsigned index)

    Renvoie un alias de champ.

  6. getType

    unsigned getType(StatusType* status, unsigned index)

    Renvoie le type SQL du champ.

  7. isNullable

    FB_BOOLEAN isNullable(StatusType* status, unsigned index)

    Renvoie true si le champ peut être défini sur NULL.

  8. getSubType

    int getSubType(StatusType* status, unsigned index)

    Renvoie un sous-type du champ BLOB (0 pour le binaire, 1 pour le texte, etc.).

  9. getLength

    unsigned getLength(StatusType* status, unsigned index)

    Renvoie la longueur maximale du champ.

  10. getScale

    int getScale(StatusType* status, unsigned index)

    Renvoie l’échelle du champ numérique.

  11. getCharSet

    unsigned getCharSet(StatusType* status, unsigned index)

    Renvoie un jeu de caractères pour les champs de caractères et un objet blob de texte.

  12. getOffset

    unsigned getOffset(StatusType* status, unsigned index)

    Renvoie le décalage des données de champ dans la mémoire tampon de message (utilisez-la pour accéder aux données de la mémoire tampon du message).

  13. getNullOffset

    unsigned getNullOffset(StatusType* status, unsigned index)

    Renvoie le décalage NULL de l’indicateur pour le champ dans le tampon de message.

  14. getBuilder

    IMetadataBuilder* getBuilder(StatusType* status)

    Renvoie le lien d’interface :#fbapi-interfaces-imetadatabuilder[IMetadataBuilder] initialisé avec les métadonnées de ce message.

  15. getMessageLength

    unsigned getMessageLength(StatusType* status)

    Renvoie la longueur de la mémoire tampon du message (utilisez-la pour allouer de la mémoire pour le tampon).

  16. getAlignment

    unsigned getAlignment(StatusType* status)

    Renvoie l’alignement en octets.

  17. getAlignedLength

    unsigned getAlignedLength(StatusType* status)

    Renvoie la taille de la structure de métadonnées après l’alignement.

Un exemple de mise en cache des messages avec IMessageMetadata en Pascal:

type
  FBRecord = Record
    fFieldName : AnsiString;
    fRelation  : AnsiString;
    fOwner     : AnsiString;
    fAliasName : AnsiString;
    fTypeField : Cardinal;
    fisNullable: Boolean;
    fSubType   : Integer;
    fLengthByte: Cardinal;
    fScale     : Integer;
    fCharSet   : Cardinal;
    fOffset    : Cardinal;
    fNullOffset: Cardinal;
  end;

var
  _outFBRecord : Array of FBRecord;
  ty_count_in  : cardinal;
  master       : IMaster ;
  util         : IUtil;
  st           : IStatus;
  dpb          : IXpbBuilder;
  att          : IAttachment;
  tra          : ITransaction;
  stmt         : IStatement;
  outMetadata  : IMessageMetadata;

begin
  master := master_interface;
  util   := master.getUtilInterface;
  st     := master.getStatus;
  dpb    := util.getXpbBuilder(st, IXpbBuilder.DPB, nil, 0);
  dpb.insertString(st,isc_dpb_set_db_charset,PAnsiChar('NONE'));
  dpb.insertInt(st,isc_dpb_set_db_sql_dialect,3);
  dpb.insertString(st, isc_dpb_user_name,  PAnsiChar('SYSDBA'));
  dpb.insertString(st, isc_dpb_password, PAnsiChar('masterkey'));
  att    := prov.attachDatabase(st,PAnsiChar('11.5.0.145/4050:/ssd3/FB5/asciidoc.fb5'), dpb.getBufferLength(st), dpb.getBuffer(st));
  tra    := att.startTransaction(st, 0, nil);
  stmt   := att.prepare(st, tra, 0, PAnsiChar(' select * from doc order by id optimize for fist rows ;'), 3, IStatement.PREPARE_PREFETCH_METADATA);
  outMetadata  := stmt.getOutputMetadata(st);
  ty_count_in := outMetadata.getCount(st);

  setlength(_outFBRecord, ty_count_in);

  for i:=0 to ty_count_in-1 do
  begin
    _outFBRecord[i].fFieldName   :=  outMetadata.getField(st,i);
    _outFBRecord[i].fRelation    :=  outMetadata.getRelation(st,i);
    _outFBRecord[i].fOwner       :=  outMetadata.getOwner(st,i);
    _outFBRecord[i].fAliasName   :=  outMetadata.getAlias(st,i);
    _outFBRecord[i].fTypeField   :=  outMetadata.getType(st,i);
    _outFBRecord[i].fisNullable  :=  outMetadata.isNullable(st,i);
    _outFBRecord[i].fSubType     :=  outMetadata.getSubType(st,i);
    _outFBRecord[i].fLengthByte  :=  outMetadata.getLength(st,i);
    _outFBRecord[i].fScale       :=  outMetadata.getScale(st,i);
    _outFBRecord[i].fCharSet     :=  outMetadata.getCharSet(st,i);
    _outFBRecord[i].fOffset      :=  outMetadata.getOffset(st,i);
    _outFBRecord[i].fNullOffset  :=  outMetadata.getNullOffset(st,i);
  end;
  ....

end;

IMetadataBuilder

Interface IMetadataBuilder — Permet de décrire les types de données des messages existants ou de créer des métadonnées.

  1. setType

    void setType(StatusType* status, unsigned index, unsigned type)

    Définit le type SQL du champ.

  2. setSubType

    void setSubType(StatusType* status, unsigned index, int subType)

    Définit le sous-type BLOB du champ.

  3. setLength

    void setLength(StatusType* status, unsigned index, unsigned length)

    Définit la longueur maximale du champ de caractère.

  4. setCharSet

    void setCharSet(StatusType* status, unsigned index, unsigned charSet)

    Définit le jeu de caractères pour le champ de caractère et l’objet blob de texte.

  5. setScale

    void setScale(StatusType* status, unsigned index, unsigned scale)

    Définit l’échelle des champs numériques.

  6. truncate

    void truncate(StatusType* status, unsigned count)

    Tronque le message afin qu’il ne contienne pas plus qu’un nombre de champs.

  7. moveNameToIndex

    void moveNameToIndex(StatusType* status, const char* name, unsigned index)

    Réorganise les champs d’un message : déplace un champ nommé nom vers une position spécifiée.

  8. remove

    void remove(StatusType* status, unsigned index)

    Supprime le champ.

  9. addField

    unsigned addField(StatusType* status)

    Ajoute un champ.

  10. getMetadata

    IMessageMetadata* getMetadata(StatusType* status)

    Renvoie le lien d’interface :#fbapi-interfaces-imessagemetadata[IMessageMetadata] construit par le compilateur.

  11. setField

    void setField(StatusType* status, unsigned index, const char* field)

    Définit le nom du champ/de la colonne.

  12. setRelation

    void setRelation(StatusType* status, unsigned index, const char* relation)

    Définit le nom de la relation pour le champ.

  13. setOwner

    void setOwner(StatusType* status, unsigned index, const char* owner)

    Définit le nom du propriétaire.

  14. setAlias

    void setAlias(StatusType* status, unsigned index, const char* alias)

    Définit l’alias du champ.

IOffsetsCallback

Interface IOffsetsCallback

  1. setOffset

    void setOffset(StatusType* status, unsigned index, unsigned offset, unsigned nullOffset)

    Définis le décalage qui doit être assigné pour le champ/paramètre indexé. Doit être implémenté par l’utilisateur lors de l’implémentation de l’interface MessageMetadata et de l’utilisation de IUtil::setOffsets().

IPluginConfig

Interface IPluginConfig — est transmis au constructeur de plugins lorsque vous créez une instance du plugin (avec une configuration spécifique).

  1. getConfigFileName

    const char* getConfigFileName()

    Renvoie le nom recommandé du fichier dans lequel la configuration du plugin est censée être enregistrée.

  2. getDefaultConfig

    IConfig* getDefaultConfig(StatusType* status)

    Configuration du plugin chargée selon les règles standard.

  3. getFirebirdConf

    IFirebirdConf* getFirebirdConf(StatusType* status)

    Retourne la configuration principale de Firebird, en tenant compte des paramètres de la base de données avec laquelle la nouvelle instance de plugin fonctionnera.

  4. setReleaseDelay

    void setReleaseDelay(StatusType* status, ISC_UINT64 microSeconds)

    Utilisé par le plugin pour configurer le délai recommandé pendant lequel le module de plugin ne sera pas déchargé par le gestionnaire de plugin après que la dernière instance du plugin soit libérée de ce module.

IPluginFactory

Interface IPluginFactory — doit être implémenté par l’auteur du plugin lors de l’écriture du plugin.

  1. createPlugin

    IPluginBase* createPlugin(StatusType* status, IPluginConfig* factoryParameter)

    Crée une nouvelle instance du plugin avec la configuration recommandée transmise.

IPluginManager

Interface IPluginManager — API du gestionnaire de plugins.

  1. registerPluginFactory

    void registerPluginFactory(unsigned pluginType,
                               const char* defaultName,
                               IPluginFactory* factory)

    Enregistre un constructeur de plugins nommée de ce type.

  2. registerModule

    void registerModule(IPluginModule* cleanup)

    Enregistre le module de plugin.

  3. unregisterModule

    void unregisterModule(IPluginModule* cleanup)

    Annule l’enregistrement du module plugin.

  4. getPlugins

    IPluginSet* getPlugins(StatusType* status,
                           unsigned pluginType,
                           const char* namesList,
                           IFirebirdConf* firebirdConf)

    Renvoie une interface IPluginSet qui permet d’accéder à une liste de plugins de ce type. Les noms des plugins inclus sont tirés de la namesList, s’ils sont absents (NULL), alors ils proviennent des paramètres de configuration de ce type donné pluginType. Si le paramètre firebirdConf est spécifié, il est utilisé à toutes fins de configuration (y compris l’obtention d’une liste de plugins et la navigation vers la méthode PluginFactory::createPlugin()), s’il est manquant (NULL), alors le paramètre par défaut (de firebird.conf) est utilisé.

  5. getConfig

    IConfig* getConfig(StatusType* status, const char* filename)

    Renvoie l’interface IConfig pour le nom de fichier de configuration spécifié. Peut être utilisé par les plugins pour accéder aux fichiers de configuration avec un format standard, mais pas avec un nom par défaut.

  6. releasePlugin

    void releasePlugin(IPluginBase* plugin)

    Libérez le plugin. Devrait être utilisé pour les plugins au lieu du simple release() en raison de la nécessité d’effectuer des actions supplémentaires avec le propriétaire du plugin avant de le publier.

Constantes définies par l’interface IPluginManager (types de plugins) :

  • TYPE_PROVIDER

  • TYPE_AUTH_SERVER

  • TYPE_AUTH_CLIENT

  • TYPE_AUTH_USER_MANAGEMENT

  • TYPE_EXTERNAL_ENGINE

  • TYPE_TRACE

  • TYPE_WIRE_CRYPT

  • TYPE_DB_CRYPT

  • TYPE_KEY_HOLDER

  • TYPE_REPLICATOR

IPluginModule

Interface IPluginModule — représente un module de plugin (bibliothèque dynamique). Doit être implémenté par l’auteur du plugin dans chaque module du plugin (une instance par module).

  1. doClean

    void doClean()

    Appelé par le gestionnaire de plugin avant que le module de plugin ne soit déchargé normalement.

IPluginSet

Interface IPluginSet — est un ensemble de plugins de ce type. Couramment utilisé par le code interne de Firebird, mais recommandé pour une utilisation dans les plugins qui chargent d’autres plugins.

  1. getName

    const char* getName()

    Renvoie le nom du plugin courant dans l’ensemble.

  2. getModuleName

    const char* getModuleName()

    Retourne le nom du module du plugin courant dans l’ensemble (dans le cas le plus simple, le même que le nom du plugin).

  3. getPlugin

    IPluginBase* getPlugin(StatusType* status)

    renvoie une instance du plugin courant, l’interface retournée doit être convertie en l’interface du plugin principal du type demandé dans la méthode IPluginManager::getPlugins(). Renvoie NULL s’il n’y a plus de plugins dans l’ensemble. Le nombre de liens du plugin renvoyé par cette fonction est incrémenté lorsqu’il est renvoyé — n’oubliez pas d’utiliser la méthode releasePlugin() de l’interface IPluginManager pour libérer les plugins retournés par cette méthode.

  4. next

    void next(StatusType* status)

    Définit un bouton bascule pour passer au plugin suivant dans la liste.

  5. set

    void set(StatusType* status, const char* list)

    Réinitialise l’interface : la fait fonctionner avec la liste des plugins fournie par le paramètre list. Le type de plugins reste le même.

IProvider

Interface IPluginModule — L’interface principale pour démarrer l’accès à la base de données/service.

  1. attachDatabase

    IAttachment* attachDatabase(StatusType* status,
                                const char* fileName,
                                unsigned dpbLength,
                                const unsigned char* dpb)

    Crée une connexion à une base de données existante. Remplace isc_attach_database().

  2. createDatabase

    IAttachment* createDatabase(StatusType* status,
                                const char* fileName,
                                unsigned dpbLength,
                                const unsigned char* dpb)

    Crée une nouvelle base de données et renvoie l’interface pour s’y connecter. Remplace isc_create_database().

  3. attachServiceManager

    IService* attachServiceManager(StatusType* status,
                                   const char* service,
                                   unsigned spbLength,
                                   const unsigned char* spb)

    Remplace isc_service_attach().

  4. shutdown

    void shutdown(StatusType* status, unsigned timeout, const int reason)

    Remplace fb_shutdown().

  5. setDbCryptCallback

    void setDbCryptCallback(IStatus* status, ICryptKeyCallback* cryptCallback)

    Définit l’interface de retour de chiffrement de base de données qui sera utilisée pour les connexions ultérieures à la base de données et aux services.

IResultSet

Interface IResultSet — remplace (avec des fonctionnalités étendues) certaines des fonctions isc_stmt_handle. Cette interface est retournée en appelant openCursor() à partir de IAttachment ou IStatement. Tous les appels à fetch…​ à l’exception de fetchNext() ne fonctionnent que pour le curseur bidirectionnel (ouvert avec l’option CURSOR_TYPE_SCROLLABLE).

  1. fetchNext

    int fetchNext(StatusType* status, void* message)

    sélectionne l’entrée suivante, remplace isc_dsql_fetch(). Cette méthode (et d’autres méthodes de récupération) renvoie le code de complétion Status::RESULT_NO_DATA lorsque l’EOF est atteint, et le statut Status::RESULT_OK lorsqu’il réussit.

  2. fetchPrior

    int fetchPrior(StatusType* status, void* message)

    Sélectionne l’enregistrement précédent.

  3. fetchFirst

    int fetchFirst(StatusType* status, void* message)

    Sélectionne la première entrée.

  4. fetchLast

    int fetchLast(StatusType* status, void* message)

    Sélectionne la dernière entrée.

  5. fetchAbsolute

    int fetchAbsolute(StatusType* status, int position, void* message)

    Récupère l’enregistrement à la position absolue dans le jeu de résultats.

  6. fetchRelative

    int fetchRelative(StatusType* status, int offset, void* message)

    Récupère l’enregistrement par position par rapport à l’enregistrement actif.

  7. isEof

    FB_BOOLEAN isEof(StatusType* status)

    Vérification de l’état EOF.

  8. isBof

    FB_BOOLEAN isBof(StatusType* status)

    Vérification de l’état BOF.

  9. getMetadata

    IMessageMetadata* getMetadata(StatusType* status)

    renvoie les métadonnées des messages dans le jeu de résultats, particulièrement utile lorsque le jeu de résultats est ouvert en appelant IAttachment::openCursor() avec le paramètre de format de sortie des métadonnées défini sur NULL (c’est la seule façon d’obtenir le format du message dans ce cas).

  10. close

    void close(IStatus* status)

    Ferme le jeu de résultats, libère l’interface en cas de succès.

IService

Interface IService — Remplace isc_svc_handle.

  1. detach

    void detach(StatusType* status)

    Ferme la connexion au gestionnaire de services et, en cas de succès, libère l’interface. Remplace isc_service_detach().

  2. query

    void query(StatusType* status,
               unsigned sendLength,
               const unsigned char* sendItems,
               unsigned receiveLength,
               const unsigned char* receiveItems,
               unsigned bufferLength,
               unsigned char* buffer)

    Envoie et demande des informations vers/depuis le service, et receiveItems peut être utilisé à la fois pour exécuter des services et pour recevoir diverses informations sur le serveur. Remplace isc_service_query().

  3. start

    void start(StatusType* status,
               unsigned spbLength,
               const unsigned char* spb)

    Exécute l’utilitaire dans Service Manager. Remplace isc_service_start().

IStatement

Interface IStatement — remplace (partiellement) isc_stmt_handle.

  1. getInfo

    void getInfo(StatusType* status,
                 unsigned itemsLength,
                 const unsigned char* items,
                 unsigned bufferLength,
                 unsigned char* buffer)

    Remplace isc_dsql_sql_info().

  2. getType

    unsigned getType(StatusType* status)

    Le type d’opérateur ne se trouve actuellement que dans les sources Firebird dans dsql/dsql.h.

  3. getPlan

    const char* getPlan(StatusType* status, FB_BOOLEAN detailed)

    Renvoie le plan d’exécution de l’opérateur.

  4. getAffectedRecords

    ISC_UINT64 getAffectedRecords(StatusType* status)

    Renvoie le nombre d’enregistrements affectés par l’opérateur.

  5. getInputMetadata

    IMessageMetadata* getInputMetadata(StatusType* status)

    Renvoie les métadonnées des paramètres.

  6. getOutputMetadata

    IMessageMetadata* getOutputMetadata(StatusType* status)

    Renvoie les métadonnées des valeurs de sortie.

  7. execute

    ITransaction* execute(StatusType* status,
                          ITransaction* transaction,
                          IMessageMetadata* inMetadata,
                          void* inBuffer,
                          IMessageMetadata* outMetadata,
                          void* outBuffer)

    Exécute toutes les instructions SQL, à l’exception de celles qui renvoient plusieurs lignes de données. Analogique partiel de isc_dsql_execute2() — l’entrée et la sortie de XSLQDA ont été remplacées par des messages d’entrée et de sortie avec les tampons correspondants.

  8. openCursor

    IResultSet* openCursor(StatusType* status,
                           ITransaction* transaction,
                           IMessageMetadata* inMetadata,
                           void* inBuffer,
                           IMessageMetadata* outMetadata,
                           unsigned flags)

    Exécute une instruction SQL qui renvoie potentiellement plusieurs lignes de données. Renvoie l’interface IResultSet qui doit être utilisée pour récupérer ces données. Le format de la sortie est déterminé par le paramètre outMetadata, si NULL est spécifié, le format par défaut sera utilisé.

  9. setCursorName

    void setCursorName(StatusType* status, const char* name)

    Remplace isc_dsql_set_cursor_name().

  10. free

    void free(StatusType* status)

    Détruit l’opérateur, libère l’interface en cas de succès.

  11. getFlags

    unsigned getFlags(StatusType* status)

    Renvoie des indicateurs décrivant comment cette instruction doit être exécutée, un remplacement simplifié de la méthode getType().

  12. getTimeout

    unsigned getTimeout(StatusType* status)

    Renvoie le délai d’expiration de la requête SQL en millisecondes.

  13. setTimeout

    unsigned setTimeout(StatusType* status)

    Définit le délai d’exécution de la requête SQL en millisecondes.

  14. createBatch

    IBatch* createBatch(StatusType* status,
                        IMessageMetadata* inMetadata,
                        unsigned parLength,
                        const unsigned char* par)

    Crée une interface IBatch pour une instruction SQL avec des paramètres d’entrée, ce qui permet à cette instruction d’être exécutée avec plusieurs ensembles de paramètres. Le format des données d’entrée est déterminé par le paramètre inMetadata, le laissant NULL, le package utilise le format par défaut de cette interface. Un bloc de paramètres peut être passé à la fonction createBatch(), qui permet de configurer le comportement du paquet.

Constantes définies par l’interface IStatement

Flag IAttachment::prepare():

  • PREPARE_PREFETCH_NONE — constante pour ignorer les flags, valeur 0.

Les flags suivants peuvent être combinés avec OU pour produire l’effet souhaité :

  1. PREPARE_PREFETCH_TYPE

  2. PREPARE_PREFETCH_INPUT_PARAMETERS

  3. PREPARE_PREFETCH_OUTPUT_PARAMETERS

  4. PREPARE_PREFETCH_LEGACY_PLAN

  5. PREPARE_PREFETCH_DETAILED_PLAN

  6. PREPARE_PREFETCH_AFFECTED_RECORDS

  7. PREPARE_PREFETCH_FLAGS (flag renvoyés par le getFlags())

Pour les combinaisons d’indicateurs les plus couramment utilisées, vous pouvez utiliser les constantes suivantes :

  1. PREPARE_PREFETCH_METADATA

  2. PREPARE_PREFETCH_ALL

Les valeurs renvoyées par l’attribut getFlags():

  1. FLAG_HAS_CURSOR — Utiliser openCursor() pour exécuter cette instruction plutôt que execute()

  2. FLAG_REPEAT_EXECUTE — Lorsqu’une instruction préparée peut être exécutée plusieurs fois avec des paramètres différents.

Les flags passés à openCursor():

  1. CURSOR_TYPE_SCROLLABLE — Ouvre un curseur bidirectionnel.

IStatus

L’interface IStatus remplace ISC_STATUS_ARRAY. La fonctionnalité a été étendue : l’état a un accès séparé aux vecteurs d’erreur et d’avertissement, peut contenir des vecteurs de longueur illimitée, stocke indépendamment les chaînes utilisées dans les vecteurs et n’a pas besoin d’un tampon de chaînes de caractères. Dans C++, IStatus est toujours utilisé dans le wrapper d’état, l’API C++ fournit deux wrappers différents qui ont un comportement différent lorsqu’une erreur est renvoyée par un appel d’API. L’interface a été réduite au minimum (des méthodes telles que la conversion en ext ont été déplacées vers l’interface IUtil pour la rendre plus facile à implémenter par les utilisateurs en cas de besoin.

  1. init

    void init()

    Nettoie l’interface et la réinitialise à son état d’origine.

  2. getState

    unsigned getState()

    Renvoie l’état actuel de l’interface, les flags retournés peuvent être combinés avec OR.

  3. setErrors2

    void setErrors2(unsigned length, const intptr_t* value)

    Définit le contenu du vecteur d’erreur avec la longueur explicitement spécifiée dans l’appel.

  4. setWarnings2

    void setWarnings2(unsigned length, const intptr_t* value)

    Définit le contenu du vecteur d’alerte avec la longueur explicitement spécifiée dans l’appel.

  5. setErrors

    void setErrors(const intptr_t* value)

    définit le contenu du vecteur d’erreur, la longueur est déterminée par le contexte de valeur.

  6. setWarnings

    void setWarnings(const intptr_t* value)

    définit le contenu du vecteur d’alerte, la longueur est déterminée par le contexte de valeur.

  7. getErrors

    const intptr_t* getErrors()

    Renvoie un vecteur d’erreur.

  8. getWarnings

    const intptr_t* getWarnings()

    Renvoie un vecteur d’alerte.

  9. clone

    IStatus* clone()

    Crée un clone de l’interface courante.

Les constantes définies dans IStatus

Les flags renvoyés par l’attribut getState():

  • STATE_WARNINGS

  • STATE_ERRORS

Codes d’achèvement :

  • RESULT_ERROR

  • RESULT_OK

  • RESULT_NO_DATA

  • RESULT_SEGMENT

ITimer

Interface ITimer — Minuterie personnalisée. Une interface de rappel qui doit être implémentée par l’utilisateur pour utiliser le minuteur Firebird.

  1. handler

    void handler()

    La méthode est appelée lorsque le minuteur sonne (ou lorsque le serveur s’arrête).

ITimerControl

L’interface ITimerControl est une implémentation très simple et peu précise du timer. Nous en sommes arrivés là parce que les minuteries existantes sont très dépendantes du système d’exploitation et peuvent être utilisées dans des programmes qui nécessitent une portabilité et ne nécessitent pas une minuterie de très haute précision. De plus, l’exécution d’une minuterie donnée peut être reportée si l’autre minuterie n’a pas été terminée au moment où cette minuterie devrait être signalée.

  1. start

    void start(StatusType* status, ITimer* timer, ISC_UINT64 microSeconds)

    Lancez ITimer après le signal (en microsecondes, 10-6 secondes). La minuterie ne se réveillera qu’une seule fois après cet appel.

  2. stop

    void stop(StatusType* status, ITimer* timer)

    stop ITimer. N’arrêtez pas un chronomètre qui n’est pas en cours d’exécution, ce qui évitera les problèmes de conflit entre le signal stop() et le signal du minuteur.

ITransaction

Interface ITransaction — Remplace isc_tr_handle.

  1. getInfo

    void getInfo(StatusType* status,
                 unsigned itemsLength,
                 const unsigned char* items,
                 unsigned bufferLength,
                 unsigned char* buffer)

    Remplace isc_transaction_info().

  2. prepare

    void prepare(StatusType* status,
                 unsigned msgLength,
                 const unsigned char* message)

    Remplace isc_prepare_transaction2().

  3. commit

    void commit(StatusType* status)

    Remplace isc_commit_transaction().

  4. commitRetaining

    void commitRetaining(StatusType* status)

    Remplace isc_commit_retaining().

  5. rollback

    void rollback(StatusType* status)

    Remplace isc_rollback_transaction().

  6. rollbackRetaining

    void rollbackRetaining(StatusType* status)

    Remplace isc_rollback_retaining().

  7. disconnect

    void disconnect(StatusType* status)

    Remplace fb_disconnect_transaction().

  8. join

    ITransaction* join(StatusType* status, ITransaction* transaction)

    connecte la transaction en cours et la transaction passée en paramètre en une seule transaction distribuée (à l’aide de Dtc). En cas de réussite, la transaction en cours et la transaction passée en paramètre sont libérées et ne doivent plus être utilisées.

  9. validate

    ITransaction* validate(StatusType* status, IAttachment* attachment)

    Cette méthode est utilisée pour soutenir le coordinateur des transactions distribuées.

  10. enterDtc

    ITransaction* enterDtc(StatusType* status)

    Cette méthode est utilisée pour prendre en charge le coordinateur des transactions distribuées.

IVersionCallback

Interface IVersionCallback — Rappel pour IUtil::getFbVersion().

  1. callback

    void callback(StatusType* status, const char* text)

    Appelé par le moteur Firebird pour chaque ligne de la version multiligne du rapport. Permet d’imprimer ces lignes une par une, de les placer dans le champ de message de n’importe quelle interface graphique, etc.

IUtil

Interface IUtil — Diverses méthodes d’aide.

  1. getFbVersion

    void getFbVersion(StatusType* status,
                      IAttachment* att,
                      IVersionCallback* callback)

    Construit un rapport de version pour firebird. Cela peut être vu dans ISQL lorsqu’il est appelé avec l’option -Z.

  2. loadBlob

    void loadBlob(StatusType* status,
                  ISC_QUAD* blobId,
                  IAttachment* att,
                  ITransaction* tra,
                  const char* file,
                  FB_BOOLEAN txt)

    Chargez un BLOB à partir d’un fichier.

  3. dumpBlob

    void dumpBlob(StatusType* status,
                  ISC_QUAD* blobId,
                  IAttachment* att,
                  ITransaction* tra,
                  const char* file,
                  FB_BOOLEAN txt)

    Enregistre le BLOB dans un fichier.

  4. getPerfCounters

    void getPerfCounters(StatusType* status,
                         IAttachment* att,
                         const char* countersSet,
                         ISC_INT64* counters)

    Obtient des statistiques pour la connexion.

  5. executeCreateDatabase

    IAttachment* executeCreateDatabase(StatusType* status,
                                       unsigned stmtLength,
                                       const char* creatDBstatement,
                                       unsigned dialect,
                                       FB_BOOLEAN* stmtIsCreateDb)

    Exécute l’instruction CREATE DATABASE…​ - l’astuce ISC avec le handle d’opérateur NULL ne fonctionne pas avec les interfaces.

  6. decodeDate

    void decodeDate(ISC_DATE date,
                    unsigned* year,
                    unsigned* month,
                    unsigned* day)

    Remplace isc_decode_sql_date().

  7. decodeTime

    void decodeTime(ISC_TIME time,
                    unsigned* hours,
                    unsigned* minutes,
                    unsigned* seconds,
                    unsigned* fractions)

    Remplace isc_decode_sql_time().

  8. encodeDate

    ISC_DATE encodeDate(unsigned year, unsigned month, unsigned day)

    Remplace isc_encode_sql_date().

  9. encodeTime

    ISC_TIME encodeTime(unsigned hours,
                        unsigned minutes,
                        unsigned seconds,
                        unsigned fractions)

    Remplace isc_encode_sql_time().

  10. formatStatus

    unsigned formatStatus(char* buffer, unsigned bufferSize, IStatus* status)

    Remplace fb_interpret(). La taille de la mémoire tampon passée à cette méthode ne doit pas être inférieure à 50 octets.

  11. getClientVersion

    unsigned getClientVersion()

    Renvoie un entier contenant la version majeure dans l’octet 0 et la version mineure dans l’octet 1.

  12. getXpbBuilder

    IXpbBuilder* getXpbBuilder(StatusType* status,
                               unsigned kind,
                               const unsigned char* buf,
                               unsigned len)

    Renvoie le lien d’interface :#fbapi-interfaces-ixpbbuilder[IXpbBuilder]. Les kind valides sont listés dans IXpbBuilder.

  13. setOffsets

    unsigned setOffsets(StatusType* status,
                        IMessageMetadata* metadata,
                        IOffsetsCallback* callback)

    Définit les décalages autorisés sur IMessageMetadata. Effectue des appels de rappel à IOffsetsCallback pour chaque champ/paramètre.

  14. getDecFloat16

    IDecFloat16* getDecFloat16(StatusType* status)

    Renvoie l’interface IDecFloat16.

  15. getDecFloat34

    IDecFloat34* getDecFloat34(StatusType* status)

    Renvoie l’interface IDecFloat34.

  16. decodeTimeTz

    void decodeTimeTz(StatusType* status,
                      const ISC_TIME_TZ* timeTz,
                      unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions,
                      unsigned timeZoneBufferLength, char* timeZoneBuffer)

    Décode l’heure avec le fuseau horaire.

  17. decodeTimeStampTz

    void decodeTimeStampTz(StatusType* status,
                           const ISC_TIMESTAMP_TZ* timeStampTz,
                           unsigned* year, unsigned* month, unsigned* day,
                           unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions,
                           unsigned timeZoneBufferLength, char* timeZoneBuffer)

    Décode un horodatage (date-heure) avec un fuseau horaire.

  18. encodeTimeTz

    void encodeTimeTz(StatusType* status,
                      ISC_TIME_TZ* timeTz,
                      unsigned hours, unsigned minutes, unsigned seconds, unsigned fractions,
                      const char* timeZone)

    Encode l’heure avec le fuseau horaire.

  19. encodeTimeStampTz

    void encodeTimeStampTz(StatusType* status,
                           ISC_TIMESTAMP_TZ* timeStampTz,
                           unsigned year, unsigned month, unsigned day,
                           unsigned hours, unsigned minutes, unsigned seconds, unsigned fractions,
                           const char* timeZone)

    Encode un horodatage (date-heure) avec un fuseau horaire.

  20. getInt128

    IInt128* getInt128(StatusType* status)

    Renvoie l’interface IInt128.

  21. decodeTimeTzEx

    void decodeTimeTzEx(StatusType* status,
                        const ISC_TIME_TZ_EX* timeTz,
                        unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions,
                        unsigned timeZoneBufferLength, char* timeZoneBuffer)

    Décode l`heure dans un format de fuseau horaire étendu.

  22. decodeTimeStampTzEx

    void decodeTimeStampTzEx(StatusType* status,
                             const ISC_TIMESTAMP_TZ_EX* timeStampTz,
                             unsigned* year, unsigned* month, unsigned* day, unsigned* hours,
                             unsigned* minutes, unsigned* seconds, unsigned* fractions,
                             unsigned timeZoneBufferLength, char* timeZoneBuffer)

    Décode un horodatage (date-heure) dans un format de fuseau horaire étendu.

IXpbBuilder

Interface IXpbBuilder

  1. clear

    void clear(StatusType* status)

    Réinitialise le constructeur à un état vide.

  2. removeCurrent

    void removeCurrent(StatusType* status)

    Supprime le clumplet en cours.

  3. insertInt

    void insertInt(StatusType* status, unsigned char tag, int value)

    Insère un agrégat avec une valeur qui représente un entier dans le format réseau.

  4. insertBigInt

    void insertBigInt(StatusType* status, unsigned char tag, ISC_INT64 value)

    Insère un agrégat avec une valeur qui représente un entier 64 bits au format réseau.

  5. insertBytes

    void insertBytes(StatusType* status, unsigned char tag, const void* bytes, unsigned length)

    Insère un clumpet avec une valeur qui contient les octets passés.

  6. insertTag

    void insertTag(StatusType* status, unsigned char tag)

    Insère un clumpet sans valeur.

  7. isEof

    FB_BOOLEAN isEof(StatusType* status)

    Vérifie s`il y a un clumpet actuelle.

  8. moveNext

    void moveNext(StatusType* status)

    Passe au clumpet suivant.

  9. rewind

    void rewind(StatusType* status)

    Se positionne sur le premier clumpet

  10. findFirst

    FB_BOOLEAN findFirst(StatusType* status, unsigned char tag)

    Trouve le premier clumpet avec cette balise.

  11. findNext

    FB_BOOLEAN findNext(StatusType* status)

    Recherche le clumpet suivant avec la balise spécifiée.

  12. getTag

    unsigned char getTag(StatusType* status)

    Renvoie la balise du clumpet courant.

  13. getLength

    unsigned getLength(StatusType* status)

    Renvoie la longueur de la valeur actuel du clumpet.

  14. getInt

    int getInt(StatusType* status)

    Renvoie la valeur du clumpet courant sous forme d’un entier.

  15. getBigInt

    SC_INT64 getBigInt(StatusType* status)

    Renvoie la valeur du clumpet courant sous forme d’un 64 bits.

  16. getString

    const char* getString(StatusType* status)

    Renvoie la valeur de l’agrégat courant sous la forme d’un pointeur vers une chaîne de terminaisons null (le pointeur est valide jusqu’au prochain appel de cette méthode).

  17. getBytes

    const unsigned char* getBytes(StatusType* status)

    Renvoie la valeur du clumpet courant sous la forme d’un pointeur vers unsigned char.

  18. getBufferLength

    unsigned getBufferLength(StatusType* status)

    Renvoie la longueur du bloc de paramètres.

  19. getBuffer

    const unsigned char* getBuffer(StatusType* status)

    Renvoie un pointeur vers le bloc de paramètres.

Constantes définies par l’interface IXpbBuilder

Types de constructeurs valides :

  • BATCH (IBatch parameters block)

  • BPB (BLOB parameters block)

  • DPB (database attachment parameters block)

  • SPB_ATTACH (service attachment parameters block)

  • SPB_START (start service parameters)

  • SPB_SEND (send items in IService::query())

  • SPB_RECEIVE (receive items in IService::query())

  • SPB_RESPONSE (response from IService::query())

  • TPB (transaction parameters block)

Plugin pour le cryptage des données transmises sur le réseau

Les algorithmes permettant de crypter des données à différentes fins sont bien connus depuis de nombreuses années. Le seul "petit" problème typique qui subsiste est de savoir où obtenir la clé top secrète utilisée par cet algorithme. Heureusement, pour le cryptage du trafic réseau, il existe une bonne solution : une clé de cryptage unique doit être générée par le plugin d’authentification. Au moins, le plugin SRP par défaut peut produire une telle clé. Et cette clé est résistante aux attaques, y compris celles de type "man-in-the-middle". C’est pourquoi une méthode a été choisie pour fournir des clés au plugin wire crypt : les obtenir du plugin d’authentification. (Dans le cas où le plugin d’authentification utilisé ne peut pas fournir de clé, un pseudo-plugin peut être ajouté dans les listes AuthClient et AuthServer pour produire des clés, quelque chose comme deux paires privées/publiques asymétriques).

ICryptKey

L’interface ICryptKey est utilisée pour stocker la clé fournie par le plugin d’authentification et la transmettre au plugin de chiffrement du trafic réseau. Cette interface doit être utilisée de la manière suivante : lorsque le plugin d’authentification du serveur ou du client est prêt à fournir une clé, il demande IServerBlock ou IClientBlock pour créer une nouvelle interface ICryptKey et y stocke la clé. Un type de clé adapté à IWireCryptPlugin sera sélectionné par Firebird et passé à cette interface.

  1. setSymmetric

    void setSymmetric(StatusType* status,
                      const char* type,
                      unsigned keyLength,
                      const void* key)

    Stocke une clé symétrique du type spécifié.

  2. setAsymmetric

    void setAsymmetric(StatusType* status,
                       const char* type,
                       unsigned encryptKeyLength,
                       const void* encryptKey,
                       unsigned decryptKeyLength,
                       const void* decryptKey)

    Stocke une paire de clés asymétriques du type spécifié.

  3. getEncryptKey

    const void* getEncryptKey(unsigned* length)

    Renvoie la clé de chiffrement.

  4. getDecryptKey

    const void* getDecryptKey(unsigned* length))

    renvoie la clé à déchiffrer (dans le cas d’une clé symétrique, le résultat est le même que getEncryptKey()).

IWireCryptPlugin

L’interface IWireCryptPlugin est l’interface principale du plugin de chiffrement réseau. Comme toute autre interface de ce type, elle doit être implémentée par l’auteur du plugin.

  1. getKnownTypes

    const char* getKnownTypes(StatusType* status)

    Renvoie une liste de clés valides séparées par des espaces/tabulations/virgules.

  2. setKey

    void setKey(StatusType* status, ICryptKey* key)

    Le plugin doit utiliser la clé qui lui est passée par cet appel.

  3. encrypt

    void encrypt(StatusType* status,
                 unsigned length,
                 const void* from,
                 void* to)

    Chiffre le paquet qui doit être envoyé sur le réseau.

  4. decrypt

    void decrypt(StatusType* status,
                 unsigned length,
                 const void* from,
                 void* to)

    Déchiffre le paquet reçu du réseau.

plugin d’authentification côté serveur

Le plugin d’authentification contient deux parties obligatoires, une partie client et une partie serveur, et peut également contenir une troisième partie qui lui est associée, le gestionnaire d’utilisateurs. Pendant le processus d’authentification, le client Firebird appelle le plugin client et envoie les données générées par celui-ci au serveur, puis le serveur appelle le plugin serveur et envoie les données qu’il a générées au client. Ce processus est répété jusqu’à ce que les deux plugins renvoient le code AUTH_MORE_DATA.AUTH_SUCCESS renvoyé côté serveur signifie une authentification réussie, AUTH_FAILED de part et d’autre signifie une interruption immédiate du processus itératif et un rejet signalé au client, AUTH_CONTINUE signifie que le plugin suivant de la liste des plugins d’authentification configurés doit être coché.

Il n`y a pas d`exemples dédiés de plugins d`authentification, mais dans le code source de firebird, dans le répertoire src/auth, vous pouvez trouver le plugin AuthDbg, avec lequel vous pouvez apprendre à partir d`un exemple trivial (sans calculs complexes comme dans Srp, par exemple, et sans appeler des fonctions WinAPI folles comme dans AuthSspi), comment le côté client et le côté serveur effectuent l`authentification (handshake).

IAuth

L’interface IAuth ne contient pas de méthodes, seules quelques constantes de définition de code sont renvoyées par la méthode authenticate() à IClient et IServer.

  • AUTH_FAILED

  • AUTH_SUCCESS

  • AUTH_MORE_DATA

  • AUTH_CONTINUE

IWriter

Interface IWriter — Enregistre le bloc Paramètre d’authentification.

  1. reset

    void reset()

    Efface le bloc cible.

  2. add

    void add(StatusType* status, const char* name)

    Ajoute un nom d’utilisateur.

  3. setType

    void setType(StatusType* status, const char* value)

    Définit le type de connexion à ajouter (utilisateur, rôle, groupe, etc.).

  4. setDb

    void setDb(StatusType* status, const char* value)

    Installe la base de données de sécurité sur laquelle l`authentification a été effectuée.

IServerBlock

L’interface IServerBlock est utilisée par le serveur du module d’authentification pour communiquer avec le client.

  1. getLogin

    const char* getLogin()

    Renvoie le nom d’utilisateur transmis par le client.

  2. getData

    const unsigned char* getData(unsigned* length)

    Renvoie les données d’authentification transmises par le client.

  3. putData

    void putData(StatusType* status, unsigned length, const void* data)

    transmet les données d’authentification au client.

  4. newKey

    ICryptKey* newKey(StatusType* status)

    Crée une nouvelle clé de chiffrement et l’ajoute à la liste des plugins de chiffrement du trafic réseau disponibles.

IServer

L`interface IServer est l`interface principale du plugin d`authentification.

  1. authenticate

    int authenticate(StatusType* status,
                     IServerBlock* sBlock,
                     IWriter* writerInterface)

    Effectue une seule étape d’authentification. L’échange de données avec le client s’effectue à l’aide de l’interface sBlock. Lorsqu’un élément d’authentification est créé, il doit être ajouté au bloc d’authentification à l’aide de writerInterface. Les valeurs de retour possibles sont définies dans l’interface IAuth.

  2. setDbCryptCallback

    void setDbCryptCallback(StatusType* status, ICryptKeyCallback* cryptCallback)

    Définit l’interface de rappel de chiffrement de base de données qui sera utilisée pour les connexions ultérieures à la base de données et Services.

plugin d’authentification côté client

IClientBlock

L’interface IClientBlock est utilisée par le côté client du module d’authentification pour communiquer avec le serveur.

  1. getLogin

    const char* getLogin()

    Renvoie le nom d’utilisateur s’il est présent dans le DPB.

  2. getPassword

    const char* getPassword()

    Renvoie le mot de passe s`il est présent dans le DPB.

  3. getData

    const unsigned char* getData(unsigned* length)

    Renvoie les données d`authentification envoyées par le serveur.

  4. putData

    void putData(StatusType* status, unsigned length, const void* data)

    Transmet les données d’authentification au serveur.

  5. newKey

    ICryptKey* newKey(StatusType* status)

    Crée une nouvelle clé de chiffrement et l’ajoute à la liste des plugins de chiffrement du trafic réseau disponibles

  6. getAuthBlock

    IAuthBlock* getAuthBlock(StatusType* status)

IClient

L’interface IClient est l’interface principale côté client du module d’authentification.

  1. authenticate

    int authenticate(StatusType* status,
                     IClientBlock* cBlock)

    Effectue une seule étape d’authentification. L’échange de données avec le serveur s’effectue à l’aide de l’interface cBlock. Les valeurs de retour possibles sont définies dans l’interface IAuth.AUTH_SUCCESS est traité par le côté client comme AUTH_MORE_DATA (c’est-à-dire que le client envoie les données générées au serveur et attend une réponse de celui-ci).

Plugin de gestion des utilisateurs

Ce plugin est activement lié au backend d’authentification – il prépare une liste d’utilisateurs pour le plugin d’authentification. Chaque plugin d’authentification nécessite un gestionnaire d’utilisateurs – certains d’entre eux peuvent accéder à une liste d’utilisateurs créés à l’aide d’un logiciel autre que Firebird (par exemple, AuthSspi). Un enregistrement de description utilisateur se compose de plusieurs champs et peut prendre en charge plusieurs opérations telles que l’ajout d’un utilisateur, la modification d’un utilisateur, la récupération d’une liste d’utilisateurs, etc. Le plugin doit être capable d’interpréter les commandes reçues dans l’interface IUser.

IUserField

L’interface IUserField n’est pas utilisée comme une interface autonome, c’est l’interface de base pour ICharUserField et IIntUserField.

  1. entered

    int entered()

    Renvoie une valeur différente de zéro si une valeur a été saisie (affectée) au champ.

  2. specified

    int specified()

    Renvoie une valeur différente de zéro si la valeur du champ a été affectée à NULL.

  3. setEntered

    void setEntered(StatusType* status, int newValue)

    Définit l’indicateur saisi sur 0 ou une valeur différente de zéro pour le champ. Il n’y a aucun moyen d’assigner NULL à un champ car ce n’est jamais obligatoire.

NULL, S’ils sont utilisés, ils sont désignés par les implémentations comme des interfaces et ont donc un accès complet à leurs composants internes.

ICharUserField

Interface ICharUserField:

  1. get

    const char* get()

    renvoie la valeur du champ sous la forme d’une chaîne C (borne \0).

  2. set

    void set(StatusType* status, const char* newValue)

    Attribue une valeur au champ. Définit l’indicateur entré sur true.

IIntUserField

Interface IIntUserField:

  1. get

    int get()

    Renvoie la valeur du champ.

  2. set

    void set(StatusType* status, int newValue)

    Attribue une valeur au champ. Définit l’indicateur entré sur true.

IUser

L’interface IUser est une liste de méthodes permettant d’accéder aux champs inclus dans un enregistrement utilisateur.

  1. operation

    unsigned operation()

    Opcode (voir la liste ci-dessous)

  2. userName

    ICharUserField* userName()

    Nom d’utilisateur

  3. password

    ICharUserField* password()

    mot de passe.

  4. firstName

    ICharUserField* firstName()

    Ceci et les 2 composants suivants du nom d’utilisateur complet.

  5. lastName

    ICharUserField* lastName()
  6. middleName

    ICharUserField* middleName()
  7. comment

    ICharUserField* comment()

    Commentaire (à partir de l’instruction SQL COMMENT ON USER IS…​).

  8. attributes

    ICharUserField* attributes()

    sous la forme tag1=val1, tag2=val2, …​, tagN=valN. Val peut être vide, ce qui signifie que la balise sera supprimée.

  9. active

    IIntUserField* active()

    Modifie le paramètre ACTIVE/INACTIVE pour l’utilisateur.

  10. admin

    IIntUserField* admin()

    Définit/supprime les droits d’administrateur de l’utilisateur.

  11. clear

    void clear(StatusType* status)

    Spécifie que tous les champs ne sont pas saisis ou spécifiés.

Les constantes définies par l’interface utilisateur sont les opcodes effectifs.

  • OP_USER_ADD — Ajouter un utilisateur.

  • OP_USER_MODIFY — Modifiez l’utilisateur.

  • OP_USER_DELETE — Supprimer un utilisateur.

  • OP_USER_DISPLAY — Affichage de l’utilisateur.

  • OP_USER_SET_MAP — Activez les administrateurs Windows pour mapper au rôle « RDB$ADMIN ».

  • OP_USER_DROP_MAP — désactivation de l`affichage des administrateurs Windows sur le rôle RDB$ADMIN.

IListUsers

Interface IListUsers — Il s’agit du callback utilisé par le plugin d’authentification lors de la demande d’une liste d’utilisateurs. Le plugin remplit l’interface IUser pour tous les éléments de la liste d’utilisateurs un par un, et appelle la méthode list() de l’interface pour chaque utilisateur.

  1. list

    void list(StatusType* status, IUser* user)

    fonction de rappel. L’implémentation peut faire ce qu’elle veut avec les données reçues. Par exemple, il peut placer les données du paramètre utilisateur dans le flux de sortie du service, ou il peut les placer dans des tables spéciales SEC$ du groupe.

ILogonInfo

L’interface ILogonInfo contient les données transmises au plugin de gestion des utilisateurs pour se connecter à la base de données de sécurité avec des informations d’identification valides.

  1. name

    const char* name()

    Renvoie le nom d’utilisateur de la connexion actuelle.

  2. role

    const char* role()

    Renvoie le rôle actif de la connexion actuelle.

  3. networkProtocol

    const char* networkProtocol()

    Renvoie le journal réseau de la connexion en cours. Non utilisé actuellement par les plugins.

  4. remoteAddress

    const char* remoteAddress()

    Renvoie l’adresse distante de la connexion actuelle. Non utilisé actuellement par les plugins.

  5. authBlock

    const unsigned char* authBlock(unsigned* length)

    Renvoie le bloc d’authentification de la connexion en cours. S’il n’est pas NULL, réécrit le nom d’utilisateur.

IManagement

L’interface IManagement est l’interface principale du plugin de gestion des utilisateurs.

  1. start

    void start(StatusType* status, ILogonInfo* logonInfo)

    exécute le plugin, si nécessaire, il se connecte à la base de données de sécurité pour gérer les utilisateurs (qu’il faille ou non utiliser cette solution dépend du plugin) en utilisant les informations d’identification de logonInfo.

  2. execute

    int execute(StatusType* status, IUser* user, IListUsers* callback)

    Exécute la commande fournie par la méthode operation() du paramètre user. Si nécessaire, l’interface de rappel sera utilisée. Le paramètre callback peut être défini sur NULL pour les commandes qui ne nécessitent pas de liste d’utilisateurs.

  3. commit

    void commit(StatusType* status)

    Prend en compte les modifications apportées par les appels à la méthode execute().

  4. rollback

    void rollback(StatusType* status)

    Annule les modifications apportées par les appels à la méthode execute().

plugin de chiffrement de base de données

La possibilité de chiffrer la base de données est présente dans Firebird depuis l’époque d’Interbase, mais les endroits correspondants dans le code ont été commentés. La mise en œuvre était discutable : la clé de chiffrement était toujours envoyée du client au DPB, aucune tentative n’était faite pour la cacher au monde extérieur et aucun chemin n’était proposé pour chiffrer les bases de données existantes. Firebird 3.0 résout la plupart des problèmes, à l’exception probablement du pire : comment gérer les clés de chiffrement. Nous proposons différents types de solutions, mais elles demandent des efforts dans les plugins, c’est-à-dire qu’il n’y a pas de façon agréable de travailler avec des clés comme, par exemple, pour les plugins pour crypter le trafic réseau.

Avant d’exécuter votre propre plugin de chiffrement de base de données, vous devez tenir compte des éléments suivants : Il existe deux cas principaux dans lesquels le chiffrement de base de données est utilisé : premièrement, il peut être nécessaire d’éviter les fuites de données si le serveur de base de données est physiquement volé, et deuxièmement, il peut être utilisé pour protéger les données d’une base de données distribuée avec une application spéciale accédant à ces données. Les exigences pour ces cas sont complètement différentes. Dans le premier cas, nous pouvons faire confiance au serveur de base de données qu’il n’est pas modifié pour voler les clés transmises au plugin de sécurité, c’est-à-dire que nous nous attendons à ce que cette clé ne soit pas envoyée au mauvais serveur. Dans le second cas, le serveur peut être modifié d’une manière ou d’une autre pour voler des clés (si elles sont transmises de l’application au plugin via le code du serveur) ou même des données (en tant que dernier endroit pour vider le cache, où elles ne sont pas chiffrées). Par conséquent, votre plugin doit s’assurer qu’il fonctionne avec les binaires Firebird non modifiés et votre application avant d’envoyer la clé au plugin, par exemple, le plugin peut nécessiter une sorte de signature numérique de leur part. De plus, si vous utilisez l’accès réseau au serveur, il est conseillé de vérifier que le canal réseau est chiffré (en analysant la sortie de IUtil::getFbVersion()) ou en utilisant votre propre clé de chiffrement. Tout ce travail doit être effectué dans le plugin (et dans l`application qui fonctionne avec), ce qui signifie que l`algorithme de chiffrement des blocs de base de données lui-même peut être la partie la plus simple du plugin de chiffrement de base de données, en particulier lorsqu`il utilise une bibliothèque standard.

ICryptKeyCallback

L’interface ICryptKeyCallback doit permettre à la clé de chiffrement d’être transmise au plugin de chiffrement de base de données ou au plugin du détenteur de la clé.

  1. callback

    unsigned callback(unsigned dataLength,
                      const void* data,
                      unsigned bufferLength,
                      void* buffer)

    Lorsqu’un callback est exécuté, l’information est transmise dans les deux sens. La source de clé reçoit les octets de données dataLength et peut envoyer les octets bufferLength à la mémoire tampon. Renvoie le nombre réel d’octets mis en mémoire tampon.

  2. dispose

    void dispose()

    Invoqué lorsque l’interface n’est plus nécessaire. Permet d’éviter les fuites de mémoire dans les interfaces à état plein.

  3. afterAttach

    unsigned afterAttach(StatusType* status, const char* dbName, const IStatus* attStatus)

    Invoquée après l’attachement sur le système client. NULL dans attStatus signifie que l’attachement a réussi mais afterAttach() est quand même invoqué pour permettre au plugin d’effectuer le nettoyage nécessaire.

Les valeurs suivantes peuvent être renvoyées par cette fonction :

  • NO_RETRY - ne pas répéter les tentatives de connexion à la base de données.

  • DO_RETRY - réessayer l’attachement (ignoré si la fonction a été appelée sans attStatus).

IDbCryptInfo

L’interface IDbCryptInfo est passée au moteur IDbCryptPlugin. Le plugin peut enregistrer cette interface et l’utiliser en cas de besoin pour obtenir plus d’informations sur la base de données.

  1. getDatabaseFullPath

    const char* getDatabaseFullPath(StatusType* status)

    Renvoie le nom complet (y compris le chemin d’accès) du fichier de base de données principal.

IDbCryptPlugin

L’interface IDbCryptPlugin est l’interface principale du plugin de chiffrement de base de données.

  1. setKey

    void setKey(StatusType* status,
                unsigned length,
                IKeyHolderPlugin** sources,
                const char* keyName)

    est utilisé pour fournir des informations au plugin de chiffrement de base de données sur la clé de chiffrement. Firebird ne transmet jamais directement les clés de ce type de plugin. Au lieu de cela, le tableau IKeyHolderPlugins de la longueur spécifiée est passé au plugin de chiffrement, qui doit recevoir l’interface ICryptKeyCallback de l’un d’entre eux, puis récupérer la clé en l’utilisant. Le paramètre keyName est le nom de la clé qui a été saisie dans l’instruction ALTER DATABASE ENCRYPT …​.

  2. encrypt

    void encrypt(StatusType* status,
                 unsigned length,
                 const void* from,
                 void* to)

    Chiffre les données avant d’écrire un bloc dans un fichier de base de données

  3. decrypt

    void decrypt(StatusType* status,
                 unsigned length,
                 const void* from,
                 void* to)

    Déchiffre les données après avoir lu le bloc à partir du fichier de base de données.

  4. setInfo

    void setInfo(StatusType* status,
                 IDbCryptInfo* info)

    Dans cette méthode, le plugin de cryptage enregistre généralement l’interface d’information pour une utilisation ultérieure.

Key Keeper pour le plugin de chiffrement de base de données

Ce type de plugin est nécessaire pour différencier la fonctionnalité – le plugin de cryptage de base de données s’occupe du cryptage proprement dit, le détenteur de la clé résout les problèmes liés à la fourniture de la clé de manière sécurisée. Le plugin peut récupérer la clé à partir de l’application ou la télécharger d’une autre manière (jusqu’à utiliser une clé USB insérée dans le serveur au démarrage de Firebird).

IKeyHolderPlugin

L’interface IKeyHolderPlugin est l’interface principale pour le plugin de stockage de clé de chiffrement.

  1. keyCallback

    int keyCallback(StatusType* status,
                    ICryptKeyCallback* callback)

    est utilisé pour passer l’interface ICryptKeyCallback à la connexion (si elle est fournie par l’utilisateur avec l’appel IProvider::setDbCryptCallback()). Cet appel est toujours effectué au moment de la connexion à la base de données, et certains détenteurs de clés peuvent rejeter la connexion si aucune clé satisfaisante n’a pas été fournie.

  2. keyHandle

    ICryptKeyCallback* keyHandle(StatusType* status,
                                 const char* keyName)

    est destiné à être directement invoqué par l’interface IDbCryptPlugin pour obtenir l’interface de rappel de la clé nommée auprès du détenteur de la clé. Cela vous permet d’utiliser le code Firebird open-source afin de ne jamais toucher aux touches réelles, évitant ainsi la possibilité de vol de clé en modifiant le code Firebird. Après avoir reçu l’interface ICryptKeyCallback, le plugin de chiffrement démarre la communication à l’aide de celle-ci. Le détenteur de la clé peut (par exemple) vérifier la signature numérique du plugin de chiffrement avant de lui envoyer la clé afin d’éviter d’utiliser un plugin de chiffrement modifié qui pourrait voler la clé privée.

  3. useOnlyOwnKeys

    FB_BOOLEAN useOnlyOwnKeys(StatusType* status)

    informe Firebird si une clé fournie par un autre détenteur de clé sera utilisée ou non. Cela n’a de sens que pour SuperServer : seul SuperServer peut partager des clés de chiffrement de base de données entre les connexions. En renvoyant un FB_TRUE de cette méthode, il force Firebird à s’assurer que ce détenteur de clé particulier (et donc la connexion qui lui est associée) fournit la clé de chiffrement correcte avant de l’autoriser à fonctionner avec la base de données.

  4. chainHandle

    ICryptKeyCallback* chainHandle(StatusType* status)

    Prise en charge du porte-clés. Dans certains cas, la clé doit passer par plusieurs détenteurs de clés avant d’atteindre le plugin de chiffrement de la base de données. Ceci est nécessaire (par exemple) pour prendre en charge EXECUTE STATEMENT dans une base de données chiffrée. Ce n’est qu’un exemple – les chaînes sont également utilisées dans d’autres cas. L’interface de rappel renvoyée par cette méthode peut différer de celle renvoyée par la fonction keyHandle() (voir ci-dessus). En règle générale, il devrait être capable de dupliquer les clés reçues de IKeyHolderPlugin lors de l’appel de la fonction keyCallback().

Objets non-frontaux utilisés dans l’API

Note

Ils se trouvent dans l’en-tête spécial Message.h C++

Les 3 classes suivantes sont utilisées pour représenter les types DATE, TIME et TIMESTAMP (datetime) lors de l’utilisation de la macro FB_MESSAGE. Les membres de la structure de données qui représentent un message statique correspondent aux champs des types FB_DATE/FB_TIME/FB_TIMESTAMP et auront le type de l’une de ces classes. Pour accéder aux champs de date et d’heure dans les messages statiques, vous devez connaître les méthodes et les membres de la classe (qui sont suffisamment auto-descriptifs pour ne pas être décrits ici).

FbDate

Méthodes de classe FbDate:

  1. decode

    void decode(IUtil* util,
                unsigned* year,
                unsigned* month,
                unsigned* day)
  2. getYear

    unsigned getYear(IUtil* util)
  3. getMonth

    unsigned getMonth(IUtil* util)
  4. getDay

    unsigned getDay(IUtil* util)
  5. encode

    void encode(IUtil* util,
                unsigned year,
                unsigned month,
                unsigned day)

FbTime

Méthodes de classe FbTime:

  1. decode

    void decode(IUtil* util,
                unsigned* hours,
                unsigned* minutes,
                unsigned* seconds,
                unsigned* fractions)
  2. getHours

    unsigned getHours(IUtil* util)
  3. getMinutes

    unsigned getMinutes(IUtil* util)
  4. getSeconds

    unsigned getSeconds(IUtil* util)
  5. getFractions

    unsigned getFractions(IUtil* util)
  6. encode

    void encode(IUtil* util,
                unsigned hours,
                unsigned minutes,
                unsigned seconds,
                unsigned fractions)

FbTimestamp

Membres du groupe `FbTimestamp `:

  1. date

    FbDate date;
  2. time

    FbTime time;

FbChar et FbVarChar

Les deux modèles suivants sont utilisés dans les messages statiques pour représenter les champs CHAR(N) et VARCHAR(N).

template <unsigned N>
struct FbChar
{
    char str[N];
};
template <unsigned N>
struct FbVarChar
{
    ISC_USHORT length;
    char str[N];
    void set(const char* s);
};

Conclusion

Trois types de plugins sont absents de ce document : ExternalEngine, Trace et Replicator. Des informations à leur sujet seront disponibles dans le prochain numéro.