I'm trying to create a database using C. I created a table called newCell, and inserted some values into it. Now, I want to iterate through all the rows and get values from 3 of the 6 columns per row. I've been trying to do something similar to this, and this link. It inserts the values into the table, but it does not seem to print them out. I expected to insert values into the table, then iterate through them and print the xCell, yCell, and zCell columns. Below is my code:
`#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
int main()
{
char* err;
sqlite3* db;
sqlite3_stmt* stmt;
sqlite3_open("DB_test.db", &db);
double result;
double x,y,z;
char table[]= "CREATE TABLE IF NOT EXISTS newCell\
(id INTEGER PRIMARY KEY AUTOINCREMENT,\
xCell REAL NOT NULL,\
yCell REAL,\
zCell REAL,\
volume REAL,\
count INTEGER DEFAULT 1,\
CONSTRAINT name_unique UNIQUE (xCell,yCell,zCell)\
);";
int rc = sqlite3_exec(db,table, NULL, NULL,&err);
if(rc != SQLITE_OK){
printf("error1: %s\n", err);
}
for(int i = 0; i<10; i++){
char query[]= "INSERT INTO newCell(xCell,yCell,zCell,volume,count)\
VALUES(3.8,6.3,4.22,2.112,1)\
ON CONFLICT(xCell,yCell,zCell) DO UPDATE SET count=count+1";
rc = sqlite3_exec(db,query,NULL,NULL,&err);
if(rc != SQLITE_OK){
printf("error2: %s\n", err);
}
}
double ret = sqlite3_prepare_v2(db,"SELECT xCell,yCell,zCell FROM
newCell", -1,&stmt,0);
if(ret){
printf("error3: %g\n",ret);
}
for(int i = 0; i<5; i++)
{
sqlite3_step(stmt);
if(ret == SQLITE_ROW){
x = sqlite3_column_double(stmt,0);
y = sqlite3_column_double(stmt,1);
z = sqlite3_column_double(stmt,2);
printf("%g %g %g",x,y,z);
} else if(ret == SQLITE_DONE){
printf("Identifier");
break;
} else{
sqlite3_finalize(stmt);
printf("some error encountered\n");
break;
}
}
sqlite3_finalize(stmt);
sqlite3_close(db);
return 0;
}`
I followed another stackoverflow answer, but did not get the result that I expected. I expected to insert values into the table, then iterate through them and print the xCell, yCell, and zCell columns.
ret is not getting updated during stepping, it still holds the return value of sqlite3_prepare_v2.
for(int i = 0; i<5; i++)
{
sqlite3_step(stmt);
if(ret == SQLITE_ROW){
Capture return value of sqlite3_step as below.
for(int i = 0; i<5; i++)
{
ret = sqlite3_step(stmt);
if(ret == SQLITE_ROW){
Related
The output of the program generates random results for a 100 rounds of 100 coin tosses. I need to get all of those H & T results (10,000) into SQLite for analysis.
Round 1:
TTTTTHHTHTTHHTTTHHTTTTTTTHHTTHHHHHHTTTHTHTTHHTTTHHHHHHTHTTTTHTHHTHTTTHTHTHTHTTHHTTTTTTHTHTTHHTTTTHTH
-
Round 99:
TTHHHTHTHHTTTHHTTHTHTHTTHHHHHTHTTTTHHHHTHTHTHTHHHHTTTTTHTTHHHTTTTHTTHHHHTTTTTTHHTHTTHTTTTHTHHTTHHTHT
Round 100:
THTHTHHHHHTTHTTTTTTTTTTTHTTHHTHHHTHHTHHHHTTHTHHTTHTHTHHTTHHHTHTHHTHTTTTTHTHTTHHTHTHHHTHTHHTHTHHTTTHH
I have little knowledge how to do this, so I looked into what others have done. Apparently Multi-row INSERT is not supported. Some sources say the only way to insert several rows in a batch is use a Select statement. How would I achieve this?
Assuming the database and table is already created, what code could I use in a C program to insert all this data into SQLite?
/* This file was mechanically generated from tests/check-pcg32.c */
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#include <string.h>
#include "pcg_basic.h"
int main(int argc, char** argv)
{
// Read command-line options
int rounds = 1000;
bool nondeterministic_seed = false;
int round, i;
++argv;
--argc;
if (argc > 0 && strcmp(argv[0], "-r") == 0) {
nondeterministic_seed = true;
++argv;
--argc;
}
if (argc > 0) {
rounds = atoi(argv[0]);
}
// In this version of the code, we'll use a local rng, rather than the
// global one.
pcg32_random_t rng;
// You should *always* seed the RNG. The usual time to do it is the
// point in time when you create RNG (typically at the beginning of the
// program).
//
// pcg32_srandom_r takes two 64-bit constants (the initial state, and the
// rng sequence selector; rngs with different sequence selectors will
// *never* have random sequences that coincide, at all) - the code below
// shows three possible ways to do so.
if (nondeterministic_seed) {
// Seed with external entropy -- the time and some program addresses
// (which will actually be somewhat random on most modern systems).
// A better solution, entropy_getbytes, using /dev/random, is provided
// in the full library.
pcg32_srandom_r(&rng, time(NULL) ^ (intptr_t)&printf,
(intptr_t)&rounds);
} else {
// Seed with a fixed constant
pcg32_srandom_r(&rng, 42u, 54u);
}
printf("pcg32_random_r:\n"
" - result: 32-bit unsigned int (uint32_t)\n"
" - period: 2^64 (* 2^63 streams)\n"
" - state type: pcg32_random_t (%zu bytes)\n"
" - output func: XSH-RR\n"
"\n",
sizeof(pcg32_random_t));
for (round = 1; round <= rounds; ++round) {
printf("Round %d:\n", round);
/* Make some 32-bit numbers */
printf(" 32bit:");
for (i = 0; i < 6; ++i)
printf(" 0x%08x", pcg32_random_r(&rng));
printf("\n");
/* Toss some coins */
printf(" Coins: ");
for (i = 0; i < 100; ++i)
printf("%c", pcg32_boundedrand_r(&rng, 2) ? 'H' : 'T');
printf("\n");
printf("\n");
}
return 0;
}
I guess I would start with a simple approach and if you need to access results based on rounds and tosses, your table in the database could consist of three fields, e.g. round, toss and result.
To create the database and the table, you could use the command line program sqlite3 as follows:
sqlite3 random.sqlite
Then enter the following command at the command prompt:
CREATE TABLE experiment (round INT, toss INT, result CHAR(1));
Now you have created a database random.sqlite and a table experiment.
How to fill this table from C?
One way would be:
open db
create a prepared insert statement
start a transaction
in a loop:
bind values to parameters
run the SQL
reset prepared statement
end transaction
finalize the statement and close the db to avoid resource leaks
A simple example of the flow described could look like this:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "sqlite3.h"
void exit_with_error(sqlite3 *db, const char * msg) {
fprintf(stderr, "%s: %s\n", msg, sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
char *pathToDB = "<a path to random.sqlite>";
sqlite3 *open_db(void);
sqlite3_stmt *prepeare_stmt(sqlite3 *db);
void close_db(sqlite3 *db, sqlite3_stmt *stmt);
int main() {
sqlite3 *db = open_db();
sqlite3_stmt *stmt = prepeare_stmt(db);
if(sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL) != SQLITE_OK) {
exit_with_error(db, "begin transaction failed");
}
char *result = "H";
for(int round = 0; round < 100; round++) {
for(int toss = 0; toss < 100; toss++) {
//for a short test simply output alternately "H" and "T"
result = strcmp(result, "H") == 0 ? "T" : "H";
//bind values to parameters
sqlite3_bind_int(stmt, 1, round);
sqlite3_bind_int(stmt, 2, toss);
sqlite3_bind_text(stmt, 3, result, -1, SQLITE_STATIC);
//run the SQL
if (sqlite3_step(stmt) != SQLITE_DONE) {
exit_with_error(db, "insert failed");
}
//reset prepared statement to be able to bind new values in next loop pass
if (sqlite3_reset(stmt) != SQLITE_OK) {
exit_with_error(db, "reset failed");
}
}
}
if(sqlite3_exec(db, "END TRANSACTION", NULL, NULL, NULL) != SQLITE_OK) {
exit_with_error(db, "end transaction failed");
}
//finalize the stmt and close db to avoid resource leaks
sqlite3_finalize(stmt);
sqlite3_close(db);
return 0;
}
sqlite3 *open_db(void) {
sqlite3 *db;
if (sqlite3_open(pathToDB, &db) != SQLITE_OK) {
exit_with_error(db, "can't open db: ");
}
return db;
}
sqlite3_stmt *prepeare_stmt(sqlite3 *db) {
sqlite3_stmt *stmt;
//create a prepared statement
int rc = sqlite3_prepare_v2(db, "INSERT INTO experiment VALUES (?1,?2,?3)", -1, &stmt, 0);
if (rc != SQLITE_OK) {
exit_with_error(db, "failure preparing insert");
}
return stmt;
}
Note: Instead of using random values, the code simply outputs 'T' and 'H' alternately to have a simple test case.
After executing the program, you can take a look at the stored values with the command line as follows:
sqlite3 random.sqlite
At the sqlite command prompt you can enter:
SELECT * FROM experiment;
The following should then be output in the console:
0|0|T
0|1|H
0|2|T
...
99|97|H
99|98|T
99|99|H
To get the value for the third toss in the first round you would issue the following sql statement from the command prompt:
SELECT * from experiment WHERE round=0 and toss=2;
that would output something like this on the console:
0|2|T
So I'm having some trouble with parameter binding with SQLite in C. I am using sqlite3_bind_* functions to insert BLOBs and strings into a database. After an insertion however, I inspect the database with SQLiteBrowser and find to my surprise that the types are all jumbled up! Here is some sample code that should reproduce the effect.
This chunk creates the table.
const char *TABLE_NAME = "PASSWORD_ENTRY";
const char *USER_ID_COLUMN_NAME = "USER_ID";
const char *INDEX_COLUMN_NAME = "INDEX_VALUE";
const char *SERVICE_COLUMN_NAME = "SERVICE";
const char *SYM_ENC_KEY_COLUMN_NAME = "SYM_ENC_KEY";
const char *ASYM_ENC_KEY_COLUMN_NAME = "ASYM_ENC_KEY";
const char *TIMESTAMP_COLUMN_NAME = "TIMESTAMP";
/* CREATE TABLE IF NOT EXISTS TABLE_NAME (
USER_ID_COLUMN_NAME INTEGER,
INDEX_COLUMN_NAME INTEGER,
SERVICE_COLUMN_NAME TEXT,
SYM_ENC_KEY_COLUMN_NAME BLOB,
ASYM_ENC_KEY_COLUMN_NAME BLOB,
TIME_STAMP_COLUMN_NAME BLOB,
PRIMARY KEY (USER_ID_COLUMN_NAME, INDEX_COLUMN_NAME));
*/
char *f = "CREATE TABLE IF NOT EXISTS %s (%s INTEGER, %s INTEGER, %s TEXT, %s BLOB, %s BLOB, %s BLOB, PRIMARY KEY (%s, %s));";
char *s = malloc(snprintf(NULL, 0, f, TABLE_NAME, USER_ID_COLUMN_NAME, INDEX_COLUMN_NAME, SERVICE_COLUMN_NAME, SYM_ENC_KEY_COLUMN_NAME, ASYM_ENC_KEY_COLUMN_NAME, TIMESTAMP_COLUMN_NAME, USER_ID_COLUMN_NAME, INDEX_COLUMN_NAME) + 1);
sprintf(s, f, TABLE_NAME, USER_ID_COLUMN_NAME, INDEX_COLUMN_NAME, SERVICE_COLUMN_NAME, SYM_ENC_KEY_COLUMN_NAME, ASYM_ENC_KEY_COLUMN_NAME, TIMESTAMP_COLUMN_NAME, USER_ID_COLUMN_NAME, INDEX_COLUMN_NAME);
const char *DB_NAME = "passwordmanager.db";
sqlite3* db;
int r = 0;
// Get the database
r = sqlite3_open(DB_NAME, &db);
if (r) {
printf("Error opening database: %s\n", sqlite3_errmsg(db));
return NULL;
}
printf("Database opened.\n");
r = sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL);
if (r) {
printf("Error preparing create table statement: %s\n", sqlite3_errmsg(db));
return 1;
}
r = sqlite3_step(stmt);
if (r != 101 && r) {
printf("Error executing create table statement: %s\n", sqlite3_errmsg(db));
return 1;
}
printf("Password entry table ready.\n");
sqlite3_finalize(stmt);
Now that that's done, I'll give you a sample insertion.
sqlite3_stmt *stmt2;
long userId = 50l;
short index = 2;
long timestamp = 100l;
char *service = "stackoverflow.com";
const int SYM_ENC_KEY_LEN = 10;
const int ASYM_ENC_KEY_LEN = 11;
char *symEncKey = "symEncKey";
char *asymEncKey = "asymEncKey";
char *f = "INSERT INTO PASSWORD_ENTRY (USER_ID, INDEX_VALUE, SERVICE, TIMESTAMP, SYM_ENC_KEY, ASYM_ENC_KEY) VALUES (?, ?, ?, ?, ?, ?);";
printf("SQL ready.\n");
r = sqlite3_prepare_v2(db, f, strlen(f), &stmt2, NULL);
if (r != 0) {
printf("Error preparing addition statement: %s\n", sqlite3_errmsg(db));
sqlite3_finalize(stmt2);
sqlite3_close(db);
return;
}
printf("Prepared the addition statement, binding...\n");
sqlite3_bind_int64(stmt2, 1, (sqlite3_int64) userId);
sqlite3_bind_int(stmt2, 2, (int) index);
sqlite3_bind_text(stmt2, 3, service, strlen(service) + 1, 0);
sqlite3_bind_int64(stmt2, 4, (sqlite_int64) timestamp);
sqlite3_bind_blob(stmt2, 5, (void *) symEncKey, SYM_ENC_KEY_LEN, 0);
sqlite3_bind_blob(stmt2, 6, (void *) asymEncKey, ASYM_ENC_KEY_LEN, 0);
// Execute the statement
r = sqlite3_step(stmt2);
if (r != 101) {
printf("Error executing addition statement: %s\n", sqlite3_errmsg(db));
sqlite3_finalize(stmt2);
sqlite3_close(db);
return;
}
printf("Executed the addition statement.\n");
sqlite3_finalize(stmt2);
sqlite3_close(db);
Now, if you'd care to view the database with SQLiteBrowser or any similar tool you may have, provided you have the same luck as me, you'll see that column SERVICE contains a BLOB and the SYM_ENC_KEY column contains a string, regardless of the fact that I used the opposite sqlite3_bind_* functions. Does anyone have any idea as to how this could be happening? If you need more information, please ask. I am a new poster.
sqlite3_bind_text(stmt2, 3, service, strlen(service) + 1, 0);
The zero terminator is not considered part of the string's data.
Remove the + 1, or better, just give -1.
The last parameter is wrong; you must provide a destructor function, or SQLITE_TRANSIENT or SQLITE_STATIC.
(The _blob calls have the same problems.)
However, the output of the .dump command in the command-line shell contains this:
INSERT INTO "PASSWORD_ENTRY" VALUES(50,2,'stackoverflow.com',X'73796D456E634B657900',X'6173796D456E634B657900',100);
This is correct. There is no data type problem.
I have a problem about inserting data into a sqlite3 database. The database permissions were set to 777 (just for the development environs), and the same goes with the directory.
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sqlite3.h>
#include <string.h>
#define MAXLENGTH 255
int random_add(int base, int mod, int interval)
{
int addend;
struct timeval tv;
gettimeofday(&tv, NULL);
srand((unsigned)tv.tv_usec);
addend = base + rand() % mod + (interval);
return addend;
}
int random_wlvl(int own_lvl)
{
int wlvl = random_add(own_lvl, 7, -3);
return wlvl;
}
int random_wstat(int pstat, int wlvl, int mod, int interval)
{
int wstat = random_add(pstat,11,-5);
while(--wlvl) {
wstat = random_add(wstat,mod,interval);
}
return wstat;
}
int main(void)
{
int own_id = 2;
int error = 0;
sqlite3 *conn;
sqlite3_stmt *res;
error = sqlite3_open("whorl.sl3", &conn);
if (error) {
puts("Cannot open database");
exit(0);
}
char temp[MAXLENGTH];
int i,battle_id;
// Start of Wild Nemesis Selection
int wlvl,whp,watk,wdef; //wild nemesis stats
int wpid = random_add(0,13,1);
int own_lvl,nem_id, own_hp;
sprintf(temp, "select nem_hp,nem_atk,nem_def from nemesis where nem_id = %d union all select own_lvl,nem_id, own_hp from owned where own_id = %d;",wpid,own_id);
error = sqlite3_prepare_v2(conn, temp, MAXLENGTH, &res, NULL);
if (error != SQLITE_OK) {
puts("Did not receive data from nemesis,owned");
exit(0);
}
i = 0;
while (sqlite3_step(res) == SQLITE_ROW) {
if (i == 0){
whp = sqlite3_column_int(res,0);
watk = sqlite3_column_int(res,1);
wdef = sqlite3_column_int(res,2);
} else {
own_lvl = sqlite3_column_int(res,0);
nem_id = sqlite3_column_int(res,1);
own_hp = sqlite3_column_int(res,2);
}
i++;
}
sqlite3_finalize(res);
//randomize wstats
wlvl = random_wlvl(own_lvl);
whp = random_wstat(whp,wlvl,5,6);
watk = random_wstat(watk,wlvl,5,1);
wdef = random_wstat(wdef,wlvl,5,1);
//create battle
sprintf(temp,"insert into battles(own_id,nem_id,own_hp,wpid,wlvl,whp,watk,wdef,wmaxhp) values(%d,%d,%d,%d,%d,%d,%d,%d,%d);",own_id,nem_id,own_hp,wpid,wlvl,whp,watk,wdef,whp);
error = sqlite3_exec(conn,temp,0,0,0);
if (error) {
puts("Cannot insert into battles");
exit(0);
}
sprintf(temp,"select battle_id from battles where own_id = %d;",own_id);
error = sqlite3_prepare_v2(conn,temp,MAXLENGTH,&res,NULL);
if (error != SQLITE_OK) {
puts("Cannot get battle_id");
}
while(sqlite3_step(res) == SQLITE_ROW) {
battle_id = sqlite3_column_int(res,0);
}
sqlite3_finalize(res);
sqlite3_close(conn);
printf("battle_id = %d\n", battle_id);
return 0;
}
Compiling, and then running yields the following:
$ ./a.out
Cannot insert into battles
Here is my schema for the battles
sqlite> .schema battles
CREATE TABLE battles(battle_id integer primary key autoincrement, own_id integer, pok_id integer, wpid integer, wlvl integer, whp integer, watk integer, wdef integer, wmaxhp integer);
What's wrong? Why can't I insert some data?
I'm starting from the bottom-up to learn iPad development after 15 years with Cold Fusion. I'm getting comfortable with Ansi C and xCode, but am stumped taking the next step with SQLite.
I've built a database (Airports.sqlite) with razorSQL and installed it in the same directory as main.c where I've also installed the amalgamated sqlite3.h and sqlite3.h files.
Everything compiles OK, but I get the following message when I Run...
Error in select statement select Length from Runways order by Length desc limit 5 [no such table: Runways].
The database definitely has the Runways table in it. Can someone set me straight? Here's the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sqlite3.h"
#include "weightbalance.h"
sqlite3* db;
int first_row;
int select_callback(void *p_data, int num_fields, char **p_fields, char **p_col_names) {
int i;
int *p_rn = (int*)p_data;
if (first_row) {
first_row = 0;
for(i=0; i < num_fields; i++) {
printf("%20s", p_col_names[i]);
}
printf("\n");
for(i=0; i< num_fields*20; i++) {
printf("=");
}
printf("\n");
}
(*p_rn)++;
for(i=0; i < num_fields; i++) {
printf("%20s", p_fields[i]);
}
printf("\n");
return 0;
}
void select_stmt(const char* stmt) {
char *errmsg;
int ret;
int nrecs = 0;
first_row = 1;
ret = sqlite3_exec(db, stmt, select_callback, &nrecs, &errmsg);
if(ret!=SQLITE_OK) {
printf("Error in select statement %s [%s].\n", stmt, errmsg);
}
else {
printf("\n %d records returned.\n", nrecs);
}
}
void sql_stmt(const char* stmt) {
char *errmsg;
int ret;
ret = sqlite3_exec(db, stmt, 0, 0, &errmsg);
if(ret != SQLITE_OK) {
printf("Error in statement: %s [%s].\n", stmt, errmsg);
}
}
int main() {
sqlite3_open("Airports.sqlite", &db);
if(db == 0) {
printf("Could not open database.");
return 1;
}
printf("\nSelecting Airports with the longest runways.\n\n");
select_stmt("select Length from Runways order by Length desc limit 5");
sqlite3_close(db);
return 0;
}
Most likely, the file "Airports.sqlite" opened in main() is not the one you think it is. Without path information, sqlite3_open() will just open the file in the current working directory.
As a debug step, add "printf(getwd(NULL))" just before your sqlite3_open() statement. Then you'll know whether you're opening your existing database or just creating a new, empty one that is missing your table.
Also, since you're using Xcode, you can just pass the path to your database as a command-line parameter (argv). In Xcode 4, choose Product->Edit Scheme. In the "run" section, add the path to "Arguments Pass On Launch". Then you can just pass argv[1] to your sqlite3_open().
I have the next SQLITE3 commands that generates a file with more than 60 million records:
.mode csv
.output matppp.csv
select mat, ppp from matppp order by mat;
.output stdout
How can I include these commands into a C program using:
sqlite3_exec(db, "..........", NULL, 0, &db_err);
?
When I attempt to do it myself, the c program generates an expression error when executing.
Thanks!!
If you want to do this within C (as opposed to piping something to sqlite3's command line program that has those nifty dot commands) then you will have to use a callback.
For your cutting and pasting convenience, here is the code, hacked out of the Apophenia library for statistical computing.
Part I:
sqlite3 *db=NULL; //The global database handle.
static int apop_db_open(char *filename){
if (!filename)
sqlite3_open(":memory:",&db);
else
sqlite3_open(filename,&db);
if (!db)
printf("Not sure why, but the database didn't open.\n");
return 0;
}
//From the SQLite manual:
#define ERRCHECK {if (err!=NULL) {printf("%s\n",err); sqlite3_free(err); return 0;}}
apop_data * apop_sqlite_query_to_screen(char *query){
char *err = NULL;
if (db==NULL)
apop_db_open(NULL);
sqlite3_exec(db, query, The_Callback, a_param, &err);
ERRCHECK
}
Part II:
The callback will have the following form, and runs once for each line returned. Notice how the parameter a_param transfers; if you don't need it (as in this example), just set it to NULL above.
int The_Callback(void *a_param, int argc, char **argv, char **column){
for (int i=0; i< argc; i++)
printf("%s,\t", argv[i]);
printf("\n");
return 0;
}
The companion web site of the book Using SQLite has some examples. In particular, chapter 7 has some examples of the C/C++ API.
Example code: http://examples.oreilly.com/9780596521196/
I think you really want to use a callback function and perhaps fprintf() to write your formatted output to a file. Fortunately, the prototype for the callback pointer contains an extra (optional) void * which could serve as a FILE * stream, making the callback more re-usable in the future.
AFAIK, sqlite3_exec() does not offer the same interface as the sqlite3 CLI. Its just for queries, not output modifiers.
Check out the example code at the bottom of the link I gave, its very easy to use a callback function.
I am doing some experiments with SQLite with a simple test harness using a single table that contains a char string key and a single integer value. The following are pieces of source from the experimental test harness that I am using. I pulled these pieces in order to show the creation of the table along with the function I use to create a record set from a select SQL statement using the call back functionality of SQLite. There are printf() statements and fprintf() statements in various places so that I can see the results of actions as this is a simple console type application for the test harness.
Notice that there are times when you do not need the call back argument so SQLite allows you to specify a NULL pointer indicating not to bother with the call back.
And as you read over the source just remember this is an experimental hack!
The function to create the table looks like:
int CreateSetupTable (sqlite3 *db)
{
char *zErrMsg = 0;
int rc;
char *aszSqlCreate = "create table tbl1(one varchar(10), two smallint)";
char *aszSqlCreateIndex01 = "create unique index index1 on tbl1 (one)";
do {
rc = sqlite3_exec(db, aszSqlCreate, 0, 0, &zErrMsg);
if( rc!=SQLITE_OK ){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
break;
}
rc = sqlite3_exec(db, aszSqlCreateIndex01, 0, 0, &zErrMsg);
if( rc!=SQLITE_OK ){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
break;
}
} while (0); // loop only once to allow breaks on errors
return rc;
}
I insert some records into this table and then have a function that I call to get one or more records from the table using a select SQL statement. The select function retrieves the records and uses a call back to convert each record returned into a C struct. The C struct look like:
typedef struct {
char cKey[20];
int iValue;
} Tbl1Record;
The call back used for the record set uses a struct that contains record select management data. By this what I mean is that the call back takes as its first argument a pointer to a struct that in turn points to the location to put the transformed data along with some information about the size of the memory area. Since a select might return more than one record depending on the where clause, the call back function uses the call back struct to know how many transformed records it can put into the memory area as well as an index so that as it is putting records, it can index through the memory area in order to return multiple transformed records.
The call back management struct looks like this:
typedef struct _RecordProcessor {
void *pRecordSet;
int nRecordSetMax;
int nRecordSetActual;
} RecordProcessor;
The select function looks like:
int SelectRecord (sqlite3 *db, char *cSelect, char *cKey)
{
char *zErrMsg = 0;
int rc;
char aszSqlSelect[128];
Tbl1Record myRec[20];
RecordProcessor myProcessor;
myProcessor.pRecordSet = myRec;
myProcessor.nRecordSetActual = 0;
myProcessor.nRecordSetMax = 20;
if (cKey) {
sprintf (aszSqlSelect, "select %s from tbl1 where one='%s'", cSelect, cKey);
} else {
sprintf (aszSqlSelect, "select %s from tbl1", cSelect);
}
rc = sqlite3_exec(db, aszSqlSelect, MyRecordProcessor, &myProcessor, &zErrMsg);
if( rc!=SQLITE_OK ){
fprintf(stderr, "SQL error SelectRecord: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
int i;
for (i = 0; i < myProcessor.nRecordSetActual; i++) {
printf ("Rec #%d cKey = %s iValue = %d\n", i+1, myRec[i].cKey, myRec[i].iValue);
}
}
return rc;
}
The call back that processes each record returned by the select looks like this:
static int MyRecordProcessor (void *callBackArg, int argc, char **argv, char **azColName)
{
int iRetStatus = 0;
char *colNameTable[] = {
"one",
"two"
};
Tbl1Record *pTbl1Record = (Tbl1Record *)((RecordProcessor *)callBackArg)->pRecordSet;
if (((RecordProcessor *)callBackArg)->nRecordSetActual < ((RecordProcessor *)callBackArg)->nRecordSetMax) {
int i, j;
int iIndex = ((RecordProcessor *)callBackArg)->nRecordSetActual;
memset (pTbl1Record + iIndex, 0, sizeof(Tbl1Record));
((RecordProcessor *)callBackArg)->nRecordSetActual++;
for (i = 0; i < argc; i++){
int j;
for (j = 0; j < sizeof (colNameTable)/sizeof(colNameTable[0]); j++) {
if (strcmp (azColName[i], colNameTable[j]) == 0) {
switch (j) {
case 0:
strncpy (pTbl1Record[iIndex].cKey, (argv[i] ? argv[i] : "NULL"), 19);
break;
case 1:
pTbl1Record[iIndex].iValue = atoi (argv[i] ? argv[i] : "0");
break;
default:
break;
}
break;
}
}
}
}
return iRetStatus;
}