Related
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 );
I need a code example. I'd like to see how we can enumerate columns names in table. (It's essential for me to use esent.dll/esent.lib and C language)
I tried to use attached code (found a guide but it doesn't work as I expect).
JET_COLUMNLIST column_info;
JET_RETRIEVECOLUMN j_rc[4];
err = JetGetTableColumnInfo(sessionID, curr_table.tableID, NULL, &column_info, sizeof(JET_COLUMNLIST), JET_ColInfoList);
j_rc[0].columnid = column_info.columnidcolumnname;
j_rc[0].cbData = sizeof(char)*JET_cbNameMost;
j_rc[0].itagSequence = 1;
j_rc[0].grbit = 0;
char buf[JET_cbNameMost] = { 0 };
j_rc[0].pvData = buf;
printf("\nRetrieving columns information:\n");
printf("Row\tId\tType\tName:\n");
unsigned long columns_qnt = 0;
for (err = JetMove(sessionID, curr_table.tableID, JET_MoveFirst, 0);
JET_errSuccess == err;
err = JetMove(sessionID, curr_table.tableID, JET_MoveNext, 0))
{
err = JetRetrieveColumns(sessionID, curr_table.tableID, j_rc, 4);
columns_qnt++;
printf("%u\t%s\n", columns_qnt, buf);
memset(buf, 0, JET_cbNameMost);
}
Please show an example. If you know good guides for ESE C programming or just some resources with describing of how it works, please share it with me. (Despite I googled a lot, don't be shy to share obvious for you resourses)
Inside table "MSysObjects" (which exists in every ESE database as service table) are 2 interisting for us columns: "Type" and "Name".
JetOpenTable(sessionID, dbid, "MSysObjects", NULL, NULL, JET_bitTableSequential, &tableID);
JET_COLUMNBASE j_cb_name, j_cb_type, j_cb_coltype;
JetGetColumnInfo(sessionID, dbid, "MSysObjects", "Name", &j_cb_name, sizeof(JET_COLUMNBASE), JET_ColInfoBase);
JetGetColumnInfo(sessionID, dbid, "MSysObjects", "Type", &j_cb_type, sizeof(JET_COLUMNBASE), JET_ColInfoBase);
JET_RETRIEVECOLUMN j_rc[2];
Here we fill structure JET_RETRIEVECOLUMN to get this 2 columns by JetRetrieveColumns
j_rc[0].columnid = j_cb_name.columnid;
j_rc[0].cbData = 1024;
j_rc[0].itagSequence = 1;
j_rc[0].grbit = NULL;
char buf[1024] = { 0 };
j_rc[0].pvData = buf;
j_rc[1].columnid = j_cb_type.columnid;
j_rc[1].cbData = sizeof(unsigned short);
j_rc[1].itagSequence = 1;
j_rc[1].grbit = NULL;
unsigned short type;
j_rc[1].pvData = &type;
for (err = JetMove(sessionID, root_tableID, JET_MoveFirst, 0);
JET_errSuccess == err;
err = JetMove(sessionID, root_tableID, JET_MoveNext, 0))
{
JetRetrieveColumns(sessionID, root_tableID, j_rc, 2);
We got them here. If type == 1 it means, that record we got is describing a table and if type == 2, then it's describing a column . (There are also other types) There is strict order, first you will get record with type 1 (table) then you will get records with type 2 that describes columns of that table (in that moment buf keeps column name), then you can get records with other types (except type == 1) that refers to that table. And finally you will get record with type 1, that means that next information we get is about another table.
}
Feel free to say that my english is awful and I wrote some junk, I'll try to explain in other way then:)
If you just want a list of column names for a particular table without using MSysObjects, here's my approach. The temporary table created by "JetGetTableColumnInfo" contains only the column ID and column Name, so it's pretty fast:
JET_ERR GetEseTableColumnNames(JET_SESID hEseSession, JET_TABLEID hEseTable)
{ JET_ERR rc;
JET_COLUMNLIST cl { };
/* Sort order for the temporary table is column name order */
rc = ::JetGetTableColumnInfo(hEseSession, hEseTable, nullptr, &cl, sizeof(cl), JET_ColInfoList | JET_ColInfoGrbitMinimalInfo);
/* Temporary table ("cl.tableid") is opened and positioned on first record */
if (rc == JET_errSuccess && cl.cRecord > 0)
{ wchar_t wszColumnName[MAX_ESE_OBJECT_NAME + 1]; // ESE doesn't play well with std::strings
unsigned long cbActual;
for (uint32_t i = 0; i < cl.cRecord; ++i)
{
rc = ::JetRetrieveColumn(hEseSession, cl.tableid, cl.columnidcolumnname, wszColumnName, sizeof(wszColumnName), &cbActual, 0, nullptr);
if (rc == JET_errSuccess)
{
/* ESE does not null terminate strings */
wszColumnName[cbActual / sizeof(wchar_t)] = L'\0';
//********
// Okay, so do something with the column name here
//********
/* Next record in temporary table */
if (i < cl.cRecord - 1)
::JetMove(hEseSession, cl.tableid, JET_MoveNext, 0);
}
else
break;
}
}
/* Close the temporary table */
::JetCloseTable(hEseSession, cl.tableid);
return rc;
}
I know other folks use MSysObjects to short-cut the process, but this works fine for me. And yes, my code looks old fashioned - I'm stuck in Hungarian!
I'm writing a light, fast Gtk application small enough for an air-gapped stand-alone Rasberry Pi setup for my work colleagues to self-roster with. Output would be CSV files on a thumb-drive for my Line Manager to open in Excel. Reading and writing to .xlsx files may be a step too far for me, for now ... ^_~
Sqlite database for permanent storage.
I need to know if there's a function enabling me to systematically query the rows to copy the data into arrays (maybe structs too) for analysis in C (roster clashes, etc). I've read that Java has Cursors, what's the C equivalent?
I need to know if there's a function enabling me to systematically query the rows to copy the data into arrays (maybe structs too) for analysis in C
There is no function to associate an entire array or struct with an SQLite row in SQLite's C API. You will need to retrieve each element of your struct or array using sqlite3_column_xxx() (e.g. sqlite3_column_int() to retrieve an integer value).
Suppose, for example, you had a struct such as the following:
typedef struct {
int employee_id;
int day_of_month;
int shift_no;
} roster_entry_t;
and an SQLite table with a matching schema, you would retrieve an array of these structs using a function such as the following:
int get_roster_entries(sqlite3 *db, roster_entry_t *roster, int max,
int *count) {
sqlite3_stmt *stmt = NULL;
int rc = 0;
int i = 0;
rc = sqlite3_prepare_v2(
db, "SELECT employee_id, day_of_month, shift_no FROM roster LIMIT ?",
-1, &stmt, NULL);
if (rc != SQLITE_OK) {
fprintf(stderr, "Failed to prepare SQL: %s\n", sqlite3_errmsg(db));
return 1;
}
rc = sqlite3_bind_int(stmt, 1, max);
if (rc != SQLITE_OK) {
fprintf(stderr, "Problem setting limit: %s\n", sqlite3_errmsg(db));
return 1;
}
do {
roster_entry_t *entry = &roster[i++];
rc = sqlite3_step(stmt);
if (rc == SQLITE_DONE) {
printf("No more rows ...\n");
break;
} else if (rc != SQLITE_ROW) {
fprintf(stderr, "Problem: %s\n", sqlite3_errmsg(db));
sqlite3_finalize(stmt);
return 1;
}
entry->employee_id = sqlite3_column_int(stmt, 0);
entry->day_of_month = sqlite3_column_int(stmt, 1);
entry->shift_no = sqlite3_column_int(stmt, 2);
} while (i < max);
*count = i - 1;
sqlite3_finalize(stmt);
return 0;
}
Notes
Variables in your program can be associated with placeholders in your SQL using sqlite3_bind_xxx().
Even though it makes the code a lot more verbose, it is really worthwhile to check the return values of the SQLite functions.
Array indices for sqlite3_bind_xxx() start at 1, while those for sqlite3_column_xxx() start 0 (somewhat confusingly).
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 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;
}