sqlite3_exec() Callback function clarification - c++

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

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.

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

Related

C++ SQLite return some values?

I am coding a serverside program that uses SQLite. When a client logs in, I want to get its informations from a Userinfo table.
Its columns are
Userno(int),
money(int),
bankmoney(int),
gender(string),
name,
surname
I want to write these informations to a user structure
struct userstruct
{
int userno;
int money;
int bankmoney
....
}
When a user logs in I want to create a new struct and set infos of user from this table via SQLite.
The following:
static int callback(void *data, int argc, char **argv, char **azColName)
void *data - can be used to give pointer to an object - aka this, or anything else, can be null
int argc - column count - number of selected records
char **argv - ARRAY of column values.
char **azColName - ARRAY of column names
btw: better use prepare - bind - step, like the following sample, this way it works without a callback:
sqlite3_prepare_v2(db, "select distinct name, age from demo where age > ? order by 2,1;", -1, &stmt, NULL);
sqlite3_bind_int(stmt, 1, 16); /* 1 */
while ( (rc = sqlite3_step(stmt)) == SQLITE_ROW) { /* 2 */
printf("%s is %d years old\n", sqlite3_column_text(stmt, 0), sqlite3_column_int(stmt, 1)); /* 3 */
}

MySQL C API: How to work with row from mysql_fetch_row()?

This is some example code from my app:
int page_id;
string page_name;
enum COLUMNS {
PAGE_ID,
PAGE_NAME
};
if (mysql_query(conn, "SELECT page_id, page_name FROM pages")) {
exit_mysql_error(conn);
}
MYSQL_RES *res = mysql_use_result(conn);
while (MYSQL_ROW row = mysql_fetch_row(res)) {
page_id = *(int *)res[PAGE_ID];
page_name = res[PAGE_NAME];
// do some stuff where I need the page_id and page_name int/string
}
So imho this isn't the right way to obtain the integer value from the result (yet it works) but I couldn't find good example on how to do it. Also I'm quite new to programming in C++, so any feedback on the code sample is welcome.
page_id = atoi(row[0]);
page_name = row[1]; // not really sure
You can find here a complete example on how to use the Mysql API in C/C++:
Writing into mysql database from a single board computer using c
To get your fields:
while ((row = mysql_fetch_row (query_result)))
{
/* Each field in represented by the string row[i] */
/* If the field you want to extract is an int, so you need to convert it */
page_id = atoi(row[i]);
page_name = row[2];
last_filed = row[number_of_fields_in_your_table - 1];
}
You can also construct a buffer with the result of your query and parse it with a separator
while ((row = mysql_fetch_row (query_result)))
{
memset(buffer, '\0', sizeof(buffer));
for (i = 0; i < num_fields - 1; i++)
{
strncat(buffer, row[i], strlen(row[i]) + 1);
strncat(buffer, ";", 2);
}
/* After this your buffer will contain
* buffer = "page_id;page_name;...;"
* and you can retreive all fields with snprintf for example
*/
snprintf(buffer, sizeof(buffer), "%d;%s;...", page_id, page_name,...);
}
You could use a stringstream
std::istringstream strStream(res[PAGE_ID]);
strStream >> page_id;
you would need to #include<sstream> for this. Here I am assuming that your PAGE_ID value is correctly set to the column index to get the right column. I assumed from the question that your only worry was the casting from string to int* and that your column indices are already correct

Tcl API how to get a list from Tcl

Tcl 8.4
In my Tcl script:
set foo1 false
set foo2 "yes"
set foo3 [list item1 item2 item3]
There's a API to get scalars like foo1 or foo2. Eg: Tcl_GetVar(tcl_interp, string("foo1").c_str(), flags). I was wondering if there's any API to get list (like foo3) from Tcl?
It's a two-stage thing. You first fetch the value with one of the Tcl_GetVar family of functions, then you get the pieces of the list that you're interested in (with Tcl_SplitList or Tcl_ListObjGetElements, normally).
As a more concrete example:
////// FETCH FROM VARIABLE //////
// The NULL is conventional when you're dealing with scalar variable,
// and the 0 could be TCL_GLOBAL_ONLY or
Tcl_Obj *theList = Tcl_GetVar2Ex(interp, string("foo1").c_str(), NULL, TCL_LEAVE_ERR_MSG);
if (theList == NULL) {
// Was an error; message in interpreter result...
}
////// EXTRACT ELEMENTS //////
int objc;
Tcl_Obj **objv;
if (Tcl_ListObjGetElements(interp, theList, &objc, &objv) == TCL_ERROR) {
// Not a list! error message in interpreter result...
}
////// WORK WITH VALUES //////
for (int i=0 ; i<objc ; i++) {
const char *value = Tcl_GetString(objv[i]);
// Whatever...
}
I'm not sure, but Tcl_ListObjGetElements looks like what you want. Or, alternatively, Tcl_ObjGetVar2 would return a Tcl_Obj which you could then manipulate using the rest of the Tcl API for working with list objects.
Hi i have found this useful link containing an example for dealing the list:
Reference:: https://www.tcl.tk/man/tclx8.2/TclCommandWriting.3.html
int Tcl_LreverseObjCmd(notUsed, interp, objc, objv)
ClientData notUsed; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int objc; /* Number of arguments. */
Tcl_Obj **obj; /* Argument strings. */
{
int listObjc, lowListIndex, hiListIndex;
Tcl_Obj **listObjv;
char *temp, *resultList;
Tcl_Obj **newListObjv;
/* Verify argument count. Since we take only one argument, argument
* count must be 2 (command plus one argument).
*/
if (objc != 2)
return TclX_WrongArgs (interp, objv [0], "list");
/* Create an object to handle the new list we're creating */
newListObjv = Tcl_NewObj();
/* Crack the list at objv[1] into its own count and array of object
* pointers.
*/
if (Tcl_ListObjGetElements (interp, objv[1], &listObjc, &listObjv) != TCL_OK) {
return TCL_ERROR;
}
/* For each element in the source list from last to first, append an
* element to the new list.
*/
for (listIndex = listObjc - 1; listIndex >= 0; listIndex--) {
Tcl_ListObjAppendElement (interp, newListObjv, listObjv[listIndex]);
}
FIX: NEED TO RETURN THE LIST.
return TCL_OK;
}

Sqlite C++ Image to Blob wont store nothing

I googled a lot and couldn't find any solution. I'm currently writing myself an
app in Codegear 2007 C++. I'm writing this app for my little kittens, its kinda' a dairy
but I call it KittyBook.
So i have two tables(sorry didn't understand how to CodeBlock) :
Kitten Info
Kitten Data.
KittenInfo Stores their names and their ID ( primary key ), their gender and their birth. This one works.
The other one should store a Blob. So after trying so many ways. It won't be stored in the table, not even the other data if I do the Query with normal insert but excluding the blob table.
So, I dunno what I'm doing wrong BUT I love SQLite so far. No turning back then eh?
The function is :
void CDatabase::InsertKittenData(int Kitten_ID, int kittenDay, bool DayOrWeek,
char * kitten_weight, char * Kitten_Comment, string PhotoFile) {
unsigned char * blob;
ifstream::pos_type size;
int size2 = 0;
if (FileExists(PhotoFile.c_str())) {
ifstream file(PhotoFile.c_str(), ios::in | ios::binary | ios::ate);
if (file.is_open()) {
size = file.tellg();
blob = new char[size];
file.seekg(0, ios::beg);
file.read(blob, size);
file.close();
}
}
else {
blob = NULL;
}
sqlite3 *dbp;
sqlite3_stmt *ppStmt;
// NULL = primary key autoinc.
char * Sql = "INSERT INTO KittenData VALUES ( NULL, ? , ? ,? , ? , ? , ?);";
int rc = sqlite3_open("KittyBook.db", &dbp);
if (rc)
return;
if (sqlite3_prepare_v2(dbp, Sql, -1, &ppStmt, NULL) != SQLITE_OK) {
return;
}
if (ppStmt) {
if (sqlite3_bind_int(ppStmt, 1, Kitten_ID) != SQLITE_OK)
return;
if (sqlite3_bind_int(ppStmt, 2, kittenDay) != SQLITE_OK)
return;
if (sqlite3_bind_int(ppStmt, 3, DayOrWeek) != SQLITE_OK)
return;
if (sqlite3_bind_text(ppStmt, 4, // Index of wildcard
kitten_weight, strlen(kitten_weight), // length of text
SQLITE_STATIC) != SQLITE_OK)
return;
if (sqlite3_bind_text(ppStmt, 5, // Index of wildcard
Kitten_Comment, strlen(Kitten_Comment), // length of text
SQLITE_STATIC) != SQLITE_OK)
return;
if (sqlite3_bind_blob(ppStmt, 6, blob, size2, SQLITE_TRANSIENT)
!= SQLITE_OK)
return;
if (sqlite3_step(ppStmt) != SQLITE_DONE)
return;
}
sqlite3_finalize(ppStmt);
sqlite3_exec(dbp, "COMMIT", NULL, NULL, NULL);
sqlite3_close(dbp);
}
You declare and initialize size2 as zero:
int size2 = 0;
then the next use of size2 is when you bind the blob:
sqlite3_bind_blob(ppStmt, 6, blob, size2, SQLITE_TRANSIENT)
From the fine manual:
In those routines that have a fourth argument, its value is the number of bytes in the parameter. To be clear: the value is the number of bytes in the value, not the number of characters.
and the fourth argument in this case is size2 and that's zero. The result is that you're telling SQLite to bind a zero-length blob and then you're wondering why nothing gets stored. Well, nothing gets stored because you're asking SQLite to store zero bytes and it is only doing what it is told.
Perhaps you want to use size instead of size2.
mu is too short, gave me the idea to do the Function step by step and increasing column
by column. Dunno what i did wrong, but its working now.
Cheers mu is too short :)

Error with parameter datatype in PostgreSql query

I'm trying to execute a query in PostgreSQL using the following code. It's written in C/C++ and I keep getting the following error when declaring a cursor:
DECLARE CURSOR failed: ERROR: could not determine data type of parameter $1
Searching here and on google, I can't find a solution. Can anyone find where I have made and error and why this is happening?
void searchdb( PGconn *conn, char* name, char* offset )
{
// Will hold the number of field in table
int nFields;
// Start a transaction block
PGresult *res = PQexec(conn, "BEGIN");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
printf("BEGIN command failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
// Clear result
PQclear(res);
printf("BEGIN command - OK\n");
//set the values to use
const char *values[3] = {(char*)name, (char*)RESULTS_LIMIT, (char*)offset};
//calculate the lengths of each of the values
int lengths[3] = {strlen((char*)name), sizeof(RESULTS_LIMIT), sizeof(offset)};
//state which parameters are binary
int binary[3] = {0, 0, 1};
res = PQexecParams(conn, "DECLARE emprec CURSOR for SELECT name, id, 'Events' as source FROM events_basic WHERE name LIKE '$1::varchar%' UNION ALL "
" SELECT name, fsq_id, 'Venues' as source FROM venues_cache WHERE name LIKE '$1::varchar%' UNION ALL "
" SELECT name, geo_id, 'Cities' as source FROM static_cities WHERE name LIKE '$1::varchar%' OR FIND_IN_SET('$1::varchar%', alternate_names) != 0 LIMIT $2::int4 OFFSET $3::int4",
3, //number of parameters
NULL, //ignore the Oid field
values, //values to substitute $1 and $2
lengths, //the lengths, in bytes, of each of the parameter values
binary, //whether the values are binary or not
0); //we want the result in text format
// Fetch rows from table
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
printf("DECLARE CURSOR failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
// Clear result
PQclear(res);
res = PQexec(conn, "FETCH ALL in emprec");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
printf("FETCH ALL failed");
PQclear(res);
exit_nicely(conn);
}
// Get the field name
nFields = PQnfields(res);
// Prepare the header with table field name
printf("\nFetch record:");
printf("\n********************************************************************\n");
for (int i = 0; i < nFields; i++)
printf("%-30s", PQfname(res, i));
printf("\n********************************************************************\n");
// Next, print out the record for each row
for (int i = 0; i < PQntuples(res); i++)
{
for (int j = 0; j < nFields; j++)
printf("%-30s", PQgetvalue(res, i, j));
printf("\n");
}
PQclear(res);
// Close the emprec
res = PQexec(conn, "CLOSE emprec");
PQclear(res);
// End the transaction
res = PQexec(conn, "END");
// Clear result
PQclear(res);
}
Try replacing
WHERE name LIKE '$1::varchar%'
with
WHERE name LIKE ($1::varchar || '%')
Treat all other occurances of '$1::varchar%' similarly.
More information on string concatenation in the manual.