web-dev-qa-db-fra.com

sqlite3_exec () Clarification de la fonction de rappel

J'ai du mal à comprendre l'utilisation de la fonction de rappel dans une base de données SQLite3.

Je comprends qu'il est utilisé pour parcourir les instructions SELECT avec plusieurs enregistrements. Mais je ne comprends pas comment cela fait cela ou comment faire mon propre rappel utile. J'ai lu TutorialsPoint plusieurs fois pour essayer de comprendre, mais ce n'est tout simplement pas le faire pour moi.

Lorsque j'utilise leur exemple et débogue dans Visual Studio pour voir comment les tableaux d'arguments sont remplis et parcourus, je me perds. De plus, VS n'affiche que l'emplacement actuel de la baie, pas la baie entière elle-même.

Si vous avez besoin d'éclaircissements, faites-le moi savoir car je suis ici pour apprendre!

Je demande à quelqu'un d'expliquer comment le rappel est utilisé. Peut-être quelques exemples de la façon dont d'autres l'ont utilisé. Juste une explication de ce que celui-ci fait même:

static int callback(void *data, int argc, char **argv, char **azColName){
   int i;
   fprintf(stderr, "%s: ", (const char*)data);
   for(i=0; i<argc; i++){
      printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
   }
   printf("\n");
   return 0;
}
19
Slvrfn

Supposons que vous ayez une table très simple appelée User qui ressemble à ceci:

 ╔════╦══════════╗ 
 ║ ID ║ Nom ║ 
 ╟────╫─────── ───╢ 
 ║ 1 ║ Slvrfn ║ 
 ║ 2 ║ Sean ║ 
 ║ 3 ║ Drew ║ 
 ║ 4 ║ mah ║ 
 ╚ ════╩══════════╝ 

Et vous appelez sqlite3_exec comme ceci (les arguments sont décrits en détail dans la documentation ):

/* Error handling omitted for brevity */
sqlite3_exec(db, "SELECT * FROM User", my_special_callback, NULL, NULL);

SQLite exécutera l'instruction SQL passée et pour chaque ligne de résultat qu'il trouvera, il appellera my_special_callback. Donc, avec notre exemple User table, my_special_callback sera appelé 4 fois. Créons donc my_special_callback:

/*
 * Arguments:
 *
 *   unused - Ignored in this case, see the documentation for sqlite3_exec
 *    count - The number of columns in the result set
 *     data - The row's data
 *  columns - The column names
 */
static int my_special_callback(void *unused, int count, char **data, char **columns)
{
    int idx;

    printf("There are %d column(s)\n", count);

    for (idx = 0; idx < count; idx++) {
        printf("The data in column \"%s\" is: %s\n", columns[idx], data[idx]);
    }

    printf("\n");

    return 0;
}

Compte tenu de notre exemple de table et de données, la sortie ressemblera à ceci:

 Il y a 2 colonne (s) 
 Les données de la colonne "ID" sont: 1 
 Les données de la colonne "Nom" sont: Slvrfn 
 
 Il y a 2 colonne (s) 
 Les données de la colonne "ID" sont: 2 
 Les données de la colonne "Nom" sont: Sean 
 
 Là sont de 2 colonne (s) 
 Les données de la colonne "ID" sont: 3 
 Les données de la colonne "Nom" sont: Drew 
 
 Il y a 2 colonnes (s) 
 Les données de la colonne "ID" sont: 4 
 Les données de la colonne "Nom" sont: mah 

Maintenant, comment rendre cela utile, c'est là que le 4ème argument de sqlite3_exec entre en jeu. D'après la documentation:

Le 4ème argument de sqlite3_exec () est relayé jusqu'au 1er argument de chaque appel de rappel.

Disons donc que nous voulons exécuter notre SQL et construire une liste chaînée des noms de tous nos utilisateurs. La première chose que nous devons faire est de changer la façon dont nous appelons sqlite3_exec:

/* Create my fictional linked list */
struct my_linked_list *head = my_linked_list_alloc();

/*
 * Pass a pointer to my list as the 4th argument to sqlite3_exec. Error
 * handling omitted for brevity
 */
sqlite3_exec(db, "SELECT * FROM User", my_special_callback, head, NULL);

/* My list is now built, I can do stuff with it... */
my_linked_list_traverse(head, /* ... Stuff ... */);

Et modifiez my_special_callback pour l'utiliser

/*
 * Arguments:
 *
 *     list - Pointer to a linked list of names
 *    count - The number of columns in the result set
 *     data - The row's data
 *  columns - The column names
 */
static int my_special_callback(void *list, int count, char **data, char **columns)
{
    struct my_linked_list *head = list;

    /*
     * We know that the value from the Name column is in the second slot
     * of the data array.
     */
    my_linked_list_append(head, data[1]);

    return 0;
}

Maintenant, si vous deviez utiliser le callback que vous avez inclus dans votre question, vous l'appelleriez comme ceci:

/*
 * Pass the table name as the 4th argument to sqlite3_exec. Error
 * handling omitted for brevity
 */
sqlite3_exec(db, "SELECT * FROM User", callback, "User", NULL);

La sortie serait:

 Utilisateur: 
 ID = 1 
 Nom = Slvrfn 
 
 Utilisateur: 
 ID = 2 
 Nom = Sean 
 
 ... etc ... 

(À l'exception du User: une partie serait imprimée sur stderr au lieu de stdout)

J'espère que cela vous aidera à clarifier les choses. Faites-moi savoir s'il y a encore quelque chose que vous ne comprenez pas.

46
Sean Bright

Ce tutoriel est horrible, car il n'utilise rien d'autre que sqlite3_exec().

Dans le cas général, le uniquement moyen utile d'utiliser sqlite3_exec() est de le remplacer par sqlite3_prepare_v2 () =/ sqlite3_step () / sqlite3_column _ * () / sqlite3_finalize () appels afin que vous puissiez lire les données au même endroit où vous l'avez réellement besoin de le gérer:

sqlite3_stmt *stmt;
const char *sql = "SELECT ID, Name FROM User";
int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
    print("error: ", sqlite3_errmsg(db));
    return;
}
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
    int id           = sqlite3_column_int (stmt, 0);
    const char *name = sqlite3_column_text(stmt, 1);
    // ...
}
if (rc != SQLITE_DONE) {
    print("error: ", sqlite3_errmsg(db));
}
sqlite3_finalize(stmt);
27
CL.