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);
où 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
.