Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

sqlite3_exec() Callback function clarification

I am having trouble understanding the use of the callback function in a SQLite3 database.

I understand it is used to traverse SELECT statements with multiple records. But I do not understand how it does that or how to make my own useful callback. I have read through this tutorial several times to try to understand, but that is just not doing it for me.

When I use their example and debug in Visual Studio to see how the argument arrays are populated and traversed i get lost. Also VS only shows the current slot in the array, not the entire array itself.

If you need any clarification please let me know as I am here to learn!

I am asking for someone to explain how the callback is used. Maybe some examples of how others have used it. Just an explanation of what this one is doing even:

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;
}
like image 220
Slvrfn Avatar asked Jun 30 '15 19:06

Slvrfn


2 Answers

Let's assume you have a very simple table called User that looks something like this:

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

And you call sqlite3_exec like this (the arguments are described in detail in the documentation):

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

SQLite will execute the passed SQL statement and for every result row that it finds it will call my_special_callback. So with our example User table, my_special_callback will be called 4 times. So let's create 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;
}

Given our example table and data, the output will look like this:

There are 2 column(s)
The data in column "ID" is: 1
The data in column "Name" is: Slvrfn

There are 2 column(s)
The data in column "ID" is: 2
The data in column "Name" is: Sean

There are 2 column(s)
The data in column "ID" is: 3
The data in column "Name" is: Drew

There are 2 column(s)
The data in column "ID" is: 4
The data in column "Name" is: mah

Now to how to make this useful, that is where the 4th argument to sqlite3_exec comes in. From the documentation:

The 4th argument to sqlite3_exec() is relayed through to the 1st argument of each callback invocation.

So let's say that we want to run our SQL and build a linked list of the names of all of our users. The first thing we need to do is change how we are calling 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 ... */);

And modify my_special_callback to use it

/*
 * 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;
}

Now, if you were to use the callback you included in your question, you would call it like this:

/*
 * 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);

The output would be:

User: 
ID = 1
Name = Slvrfn

User: 
ID = 2
Name = Sean

... etc ...

(Except the User: part would be printed to stderr instead of stdout)

Hopefully this helps clear things up for you. Let me know if there is still something that you don't understand.

like image 119
Sean Bright Avatar answered Oct 18 '22 12:10

Sean Bright


That tutorial is horrible, because it does not use anything but sqlite3_exec().

In the general case, the only useful way to use sqlite3_exec() is to replace it with sqlite3_prepare_v2()/sqlite3_step()/sqlite3_column_*()/sqlite3_finalize() calls so that you can read the data in the same place where you actually need to handle it:

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);
like image 43
CL. Avatar answered Oct 18 '22 14:10

CL.