Question concerning pointers in C API of SQLite - c

I am misunderstandng something likley fundamental concerning pointers in C.
In the code below, if db_open( "name.db", dbP ) is invoked from within another function, the database is opened/created but dbP is not recognized afterward in build_schema but is NULL. If db_open is changed to not have an argument sqlite3 *db and references the gloabal dbP, then dbP is recognized in build_schema and is not NULL.
The objective is to have db_open accept any database name and sqlite db handle.
Would you please explain what I am doing wrong and misunderstanding?
Thank you.
I added more code as requested. The database is created successfully and build_schema is invoked but the sqlite prepare statement returns an error code of 21 because dbP is null in that function if print it out. However, if alter db_open to not have the second argument and reference dbP in all functions, build_schema succeeds, the SQL statements are processed, the database has tables in it, and printing out dbP shows something like AL.
// function prototype
int db_open( char [], sqlite3 *db );
// Global variables.
sqlite3 *dbP; // The primary database.
int main( void )
{
int rc;
switch( rc = db_open( "path/db_name.db", dbP ) )
{
case 'c' :
build_schema( &J[ i ] );
break;
case 'o' :
get_base( &J[ i ] );
break;
default: ...;
}
}
int db_open( char db_name[], sqlite3 *db )
{
int rc;
// Attempt to open the database as if already existed; will fail if does not exist.
rc = sqlite3_open_v2( db_name, &db, SQLITE_OPEN_READWRITE, NULL );
if ( rc == 14 )
{
// Error code of 14 is a generic "unable to open database."
// First, assume failed to open because database doesn't exist; and modify flags to attempt to create it anew.
rc = sqlite3_open_v2( db_name, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL );
// If rc > 0 then failed to create a database; otherwise, return "c" indicating successful creation of database.
return rc ? 'e' : 'c';
}
else if ( rc )
// Correction of error codes other than 14 is not attempted, and user is informed.
return 'e';
else
// Existing database opened successfully.
return 'o';
} // close db_open
void build_schema( char *j )
{
sqlite3_stmt *stmt;
char *sql;
const char *tail;
int rc;
sql = "SQL statement_1 to create a table;"
"SQL statement_2 to create a table;"
"SQL statement_3; to create a table";
while ( sqlite3_complete( sql ) )
{
rc = sqlite3_prepare_v2( dbP, sql, -1, &stmt, &tail );
sqlite3_step( stmt );
sql = tail;
}
sqlite3_finalize( stmt );
}

The problem I see is that you are passing the value of dbP rather than the location of dbP to your helper function which then takes the location of its argument so when the sqlite3_open_v2 function writes. It is writing to the location of the argument of the db_open function db rather than the location of the global dbP.
int sqlite3_open_v2(
const char *filename, /* Database filename (UTF-8) */
sqlite3 **ppDb, /* OUT: SQLite db handle */
int flags, /* Flags */
const char *zVfs /* Name of VFS module to use */
);
db_open should take a sqlite3 ** and then it should just pass it along. This then puts the & in the right place. So see have a &dbP.
switch( rc = db_open( "path/db_name.db", &dbP ) )
int db_open( char db_name[], sqlite3 **db )
rc = sqlite3_open_v2( db_name, db, SQLITE_OPEN_READWRITE, NULL );

Related

Inserting many records

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

insert blob into sqlite 3 with C

So I'm trying to save onto a database a blob but it doesn't insert anything.
I checked the return errors and I always get "error 0: not an error"
I'm using the sqlite3.org example for blob management: https://www.sqlite.org/cvstrac/wiki?p=BlobExample
Anyway here is the exact code, maybe I'm forgetting something.
int Database::insertToDatabase(
const int zKey, /*Id of row */
const unsigned char *zBlob, /* Pointer to blob of data */
int nBlob, /* Length of data pointed to by zBlob */
){
char zSql[kMaxSqlFunctionSize];
snprintf(zSql, kMaxSqlFunctionSize, "INSERT INTO RECTS(ID,DATA)VALUES(%d,?)",zKey);
sqlite3_stmt *pStmt;
int rc;
do {
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, NULL);
if( rc!=SQLITE_OK ){
return rc;
}
sqlite3_bind_blob(pStmt, 1, zBlob, nBlob, SQLITE_STATIC);
rc = sqlite3_step(pStmt);
assert( rc!=SQLITE_ROW );
rc = sqlite3_finalize(pStmt);
} while( rc==SQLITE_SCHEMA );
return rc;
}
I use sqlite_open_v2 to open the database and the tables are created correctly (checked with sqlite studio), but idk why I can't get this insert to work.
(the data I'm trying to insert isn't more than 1k).
Thanks in advance.

SQLite in C. sqlite3_exec: parameter set in callback function prints incomplete data when called in main()

I am trying to achieve something very basic. But unfortunately I am unable to get the expected result.
I have a sqlite table named emp_infohaving 9 employee records in following fields:
SR_NO[0], NAME[1], AGE[2], SEX[3], ADDRESS[4], EMPID[5], CARDID[6] and SALARY[7].
To access the table and fetch the employee record as per sql query, I have following program in C:
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
#include <string.h>
static int callback(void *param, int argc, char *argv[], char **azColName){
int i=0;
if(argc == 0) return 0;
char **res = (char **)param;
*res = (char *)realloc(*res, sizeof(*res));
//for(i=0; i<argc; i++){
//printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
strcpy(*res, argv[1], sizeof(*res));
printf("%s\n", *res);
// }
return 0;
}
int main(int argc, char* argv[])
{
sqlite3 *db;
char *zErrMsg = 0;
int rc;
char *sql;
char *param;
rc = sqlite3_open("/DBsqlite3/foreign_key.db", &db);
if( rc )
{
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}
else
{
fprintf(stderr, "\nOpened database successfully\n\n");
}
sql="select * from emp_info;"
rc = sqlite3_exec(db, sql, callback, &param, &zErrMsg);
if( rc != SQLITE_OK )
{
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
else
{
fprintf(stdout, "Operation done successfully\n\n");
printf("the value of data is %s \n\n", param);
}
free(param);
sqlite3_close(db);
return 0;
}
The output is as follows:
`
Opened database successfully
AAA
BBB
CCC
DDD
EEE
FFF
GGG
HHH
III
Operation done successfully
the value of data is III
I expected that all the AAA,BBB,CCC,.... will get stored in param and will get printed. But that's not happening. param takes only the last record only.
Why is this happening ? I suspect strcpy() is creating the problem.
Kindly help.
Apart from this, my real aim is to get all the valid employee record (as per sql query) to get stored in param and it should display all the fields(SR_NO[0], NAME[1], AGE[2], SEX[3], ADDRESS[4], EMPID[5], CARDID[6] and SALARY[7]). say for example if i provide following query to sql
sql="SELECT * FROM emp_info WHERE AGE>40";
then the param should display result as follow
7 GGG 41 MALE G1G2G3G4 307 7777GGG 77777
8 HHH 42 FEMALE H1H2H3H4 308 8888HHH 88888
9 III 43 FEMALE I1I2I3I4 309 9999IIII 99999
And not just a column.How can I achieve this ?
Thank you in advance.
working on the suggestions suggested by CL. I started working on writing the code on cursor-based API. And I got the desired output.
#include <iostream>
#include <sqlite3.h>
#include <stdlib.h>
#include <list>
#include <iterator>
#include <algorithm>
using namespace std;
class sqliteDB
{
private:
int rc;
sqlite3 *db;
char *zErrMsg;
const char *sql;
sqlite3_stmt * stmt;
list<string> answer;
public:
//================================XX=====================================//
bool connectDB()
{
rc = sqlite3_open("/DBsqlite3/foreign_key.db", &db);
if( rc )
{
cerr << "Can't open database: " << sqlite3_errmsg(db) << endl;
sqlite3_close(db);
exit(1);
}
else
{
std::cout<<"\n\nDatabase opened successfully\n\n";
}
}
//===============================XX=====================================//
void allEmp()
{
int AGE;
//Preparing the statement
rc=sqlite3_prepare(db,"SELECT * FROM emp_info WHERE AGE>40;", -1, &stmt, NULL );
if (rc != SQLITE_OK){
throw string(sqlite3_errmsg(db));
}
else{
cout<<"\n\nThe statement was prepared successfully\n\n";
}
// Creating List Container //
cout << "\n\nList answer was created successfully\n\n";
while(sqlite3_step(stmt) == SQLITE_ROW) {
cout << "Rows" << endl;
int column = sqlite3_column_count(stmt);
for(int i = 0; i < column; i++)
{
cout << "Columns" << endl;
answer.push_back(string((const char *) sqlite3_column_text(stmt, i)));
}
}
sqlite3_finalize(stmt);
cout << "\n\nDatabase was closed successfully\n\n";
}
//==============================XX=====================================//
void printList(){
int s = answer.size();
for( list<std::string>::const_iterator it = answer.begin(); it != answer.end(); it++)
cout << it->c_str() << endl;
}
//===================================XX===============================//
};
int main()
{
sqliteDB object1;
object1.connectDB();
object1.allEmp();
object1.printList();
cout << "\n\nAll the statement were executed properly\n\n";
return 0;
}
//========================END OF CODE===========================//
The Output is as follows
Database opened successfully
The statement was prepared successfully
List answer was created successfully
Rows
Columns
Columns
Columns
Columns
Columns
Columns
Columns
Columns
Columns
Columns
Columns
Columns
Rows
Columns
Columns
Columns
Columns
Columns
Columns
Columns
Columns
Columns
Columns
Columns
Columns
Rows
Columns
Columns
Columns
Columns
Columns
Columns
Columns
Columns
Columns
Columns
Columns
Columns
Database was closed successfully
7
GGG
41
MALE
G1G2G3G4
307
7777GGG
77777
9833446677
abi#gmail.com
07:08:1947
NONE
8
HHH
42
FEMALE
H1H2H3H4
308
8888HHH
88888
9833446688
abj#gmail.com
08:09:1947
NONE
9
III
43
FEMALE
I1I2I3I4
309
9999IIII
99999
9833446699
abk#gmail.com
09:10:1947
NONE
All the statement were executed properly
The major problem I faced was with the overloading of "<<" operator.
Thank you CL.
I'm no expert in this, but a simple google search shows, the syntax of sqlite3_exec() is
int sqlite3_exec(
sqlite3*, /* An open database */
const char *sql, /* SQL to be evaluated */
int (*callback)(void*,int,char**,char**), /* Callback function */
void *, /* 1st argument to callback */
char **errmsg /* Error msg written here */
);
Note the 4th parameter, it is suppossed to be a void *.
As per your definition of variables and calling convention,
char *param;
and
rc = sqlite3_exec(db, sql, callback, &param, &zErrMsg);
you're getting the 4th param wrong. You may want to rewrite it as
rc = sqlite3_exec(db, sql, callback, (void *)param, &zErrMsg);
Note: [If not being taken care already by sqlite3_exec()] You may need to allocate memory to param before being passed to sqlite3_exec(). Also, instead of directly passing argv to the callback, you may want to have some sanity and validity check of the same.
You took the code from an answer where the goal was to read a single result row.
The strcpy overwrites the previous value of the same variable.
You should use the cursor-based API, and call sqlite3_step in a loop:
...
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
my_object *o = new ...;
o->set_field(sqlite3_column_xxx(stmt, 1);
...
my_list.add(o);
}
...

C SQLite Inserts BLOB instead of String and Vice Versa When Binding Parameters

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.

Use of sqlite3_exec

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

Resources