SQLite3 - Taking function parameters as values - c++

I'm trying to make a class using sqlite3 database with a method that should insert the data in the table when the user adds the files to the application, but it is not working as intended,
void Database::InsertSample(int Favorite, std::string Filename,
std::string SamplePack, int Channels, int Length,
int SampleRate, int Bitrate, std::string Comment,
std::string Path)
{
try
{
rc = sqlite3_open("Samples.db", &DB);
sql = "INSERT INTO SAMPLES (FAVORITE, FILENAME, SAMPLEPACK, CHANNELS, \
LENGTH, SAMPLERATE, BITRATE, BITSPERSAMPLE, PATH) \
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);";
rc = sqlite3_prepare_v2(DB, sql.c_str(), 0, &stmt, 0); // create the prepared statement
rc = sqlite3_bind_int(stmt, 1, Favorite);
rc = sqlite3_bind_text(stmt, 2, Filename.c_str(), Filename.size(), SQLITE_STATIC);
rc = sqlite3_bind_text(stmt, 3, SamplePack.c_str(), SamplePack.size(), SQLITE_STATIC);
rc = sqlite3_bind_int(stmt, 4, Channels);
rc = sqlite3_bind_text(stmt, 5, Filename.c_str(), Filename.size(), SQLITE_STATIC);
rc = sqlite3_bind_int(stmt, 6, Length);
rc = sqlite3_bind_int(stmt, 7, SampleRate);
rc = sqlite3_bind_int(stmt, 8, Bitrate);
rc = sqlite3_bind_text(stmt, 9, Comment.c_str(), Comment.size(), SQLITE_STATIC);
rc = sqlite3_bind_text(stmt, 10, Path.c_str(), Path.size(), SQLITE_STATIC);
rc = sqlite3_step(stmt);
rc = sqlite3_exec(DB, Sample.c_str(), NULL, 0, &ErrorMessage);
if (rc != SQLITE_OK)
{
std::cerr << "Error! Cannot insert data into table." << std::endl;
sqlite3_free(ErrorMessage);
}
else
{
std::cout << "Data inserted successfully." << std::endl;
}
sqlite3_close(DB);
}
catch (const std::exception &exception)
{
std::cerr << exception.what();
}
}
But this fails, throwing the error statement "Error! Cannot insert data into table.". Am I doing something wrong here.
I'm using this function in another class as,
void Browser::OnClickDirCtrl(wxCommandEvent& event)
{
TagLib::FileRef File (DirCtrl->GetFilePath());
TagLib::String Artist = File.tag()->artist();
TagLib::String Album = File.tag()->album();
TagLib::String Genre = File.tag()->genre();
TagLib::String Title = File.tag()->title();
TagLib::String Comment = File.tag()->comment();
int Bitrate = File.audioProperties()->bitrate();
int Channels = File.audioProperties()->channels();
int Length = File.audioProperties()->lengthInMilliseconds();
int LengthSec = File.audioProperties()->lengthInSeconds();
int SampleRate = File.audioProperties()->sampleRate();
wxVector<wxVariant> Data;
Data.clear();
Data.push_back(false);
Data.push_back(TagLibTowx(Title));
Data.push_back(TagLibTowx(Artist));
Data.push_back(wxString::Format("%d",Channels));
Data.push_back(wxString::Format("%d",LengthSec));
Data.push_back(wxString::Format("%d",SampleRate));
Data.push_back(wxString::Format("%d",Bitrate));
Data.push_back(TagLibTowx(Comment));
SampleListView->AppendItem(Data);
db.InsertSample(0, Title.to8Bit(), Artist.to8Bit(), Channels, Length, SampleRate, Bitrate, Comment.to8Bit(), DirCtrl->GetFilePath().ToStdString());
}
This just a part of the function that should add the files to the database. As you can see, I am storing the path of the files in the database which is important data that I need for the project.
/---------/
EDIT: Adding a short sample,
main.cpp
#include "testdb.hpp"
int main()
{
Database db;
db.InsertData("Hello, World!");
return 0;
}
testdb.hpp
#include <sqlite3.h>
#include <string>
class Database
{
public:
Database();
~Database();
public:
sqlite3* DB;
int rc;
char* ErrorMessage;
std::string Test;
std::string sql;
sqlite3_stmt* stmt;
public:
void InsertData(std::string Path);
};
testdb.cpp
#include <exception>
#include <iostream>
#include <string>
#include "testdb.hpp"
Database::Database()
{
/* Create SQL statement */
Test = "CREATE TABLE TEST("
"TEST TEXT NOT NULL);";
try
{
rc = sqlite3_open("Test.db", &DB);
rc = sqlite3_exec(DB, Test.c_str(), NULL, 0, &ErrorMessage);
if (rc != SQLITE_OK)
{
std::cerr << "Error! Cannot create table." << std::endl;
sqlite3_free(ErrorMessage);
}
else
{
std::cout << "Table created successfuly." << std::endl;
}
sqlite3_close(DB);
}
catch (const std::exception &exception)
{
std::cerr << exception.what();
}
}
void Database::InsertData(std::string Test)
{
try
{
rc = sqlite3_open("Test.db", &DB);
sql = "INSERT INTO TEST (PATH) VALUES (?);";
rc = sqlite3_prepare_v2(DB, sql.c_str(), 10, &stmt, 0); // create the prepared statement
// error handling goes here
rc = sqlite3_bind_text(stmt, 10, Test.c_str(), Test.size(), SQLITE_STATIC);
// error handling goes here
rc = sqlite3_step(stmt);
// error handling goes here
rc = sqlite3_finalize(stmt);
if (rc != SQLITE_OK)
{
std::cerr << "Error! Cannot insert data into table." << std::endl;
sqlite3_free(ErrorMessage);
}
else if (rc == SQLITE_BUSY)
{
std::cout << "BUSY" << std::endl;
}
else if (rc == SQLITE_DONE)
{
std::cout << "DONE" << std::endl;
}
else if (rc == SQLITE_ERROR)
{
std::cout << "ERROR" << std::endl;
}
else if (rc == SQLITE_MISUSE)
{
std::cout << "MISUSE" << std::endl;
}
else
{
std::cout << "Data inserted successfully." << ErrorMessage << std::endl;
}
sqlite3_close(DB);
}
catch (const std::exception &exception)
{
std::cerr << exception.what();
}
}
Database::~Database(){}
Compile using g++ main.cpp testdb.cpp -l sqlite3 -o db.
Same thing happening here, it says data inserted but database shows empty in sqlitebrowser.

You have a sqlite3_step followed by sqlite3_exec, which is probably not what you intended. It's certainly not good. You must call sqlite3_reset or sqlite3_finalize to complete the prepared statement. It will also provide a specific error code that better describes the error if you get one from sqlite3_step.
See this explanation of `sqlite3_step'

Related

free():invalid size, but no new() or free() was called - SQLite3 using C++

Hi I am playing around with sqlite3 using C++. I am storing an array as BLOB and try to extract it from the database. However, when I select data from the database I got free():invalid size error after the function selectTable() finishes and before main() finishes. So I am a bit confused.
The output on the command line is
selectTable func
Opened database successfully
before finalzie
after close DB
free(): invalid size
Aborted
Below is backtrace from gdb and the code
Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig#entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
51 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0 __GI_raise (sig=sig#entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1 0x00007ffff717a801 in __GI_abort () at abort.c:79
#2 0x00007ffff71c3897 in __libc_message (action=action#entry=do_abort,
fmt=fmt#entry=0x7ffff72f0b9a "%s\n") at ../sysdeps/posix/libc_fatal.c:181
#3 0x00007ffff71ca90a in malloc_printerr (
str=str#entry=0x7ffff72eeda0 "free(): invalid size") at malloc.c:5350
#4 0x00007ffff71d1e2c in _int_free (have_lock=0,
p=0x7ffff7de5990 <_dl_init+864>, av=0x7ffff7525c40 <main_arena>)
at malloc.c:4161
#5 __GI___libc_free (mem=0x7ffff7de59a0 <_dl_fini>) at malloc.c:3124
#6 0x0000555555555b48 in main ()
int main() {
const char* dir = "testBLOB.db";
sqlite3* DB;
//createDB(dir);
//createTable(dir);
//clearTable(dir);
for(int i = 0; i < 50; i++) {
//insertTable(dir);
}
cout << "selectTable func" << endl;
selectTable(dir);
cout << "Out of selectTable func" << endl;
return 0;
}
static int createDB(const char* s) {
sqlite3* DB;
int exit = 0;
exit = sqlite3_open(s, &DB);
if(!exit) {
//cout << "opened db" << endl;
}
sqlite3_close(DB);
return 0;
}
static int createTable(const char* s) {
sqlite3* DB;
string sql = "CREATE TABLE IF NOT EXISTS test("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"name TEXT NOT NULL, "
"vector BLOB"
");";
try {
int exit = 0;
exit = sqlite3_open(s, &DB);
char* messageError;
exit = sqlite3_exec(DB, sql.c_str(), NULL, 0, &messageError);
//cout << "Hello, World!" << endl;
if(exit != SQLITE_OK) {
cerr << "Error creating table" << endl;
} else {
sqlite3_close(DB);
}
} catch (const exception & e) {
cerr << e.what();
}
return 0;
};
static int insertTable(const char* s) {
sqlite3* DB;
long double nums [100] = {63.3448897849,56.5751700134,75.6611442415,44.4839808971,12.1012110357,39.8311006389,55.4628689524,44.1856083913,31.8941372232,36.7439371495,64.1919152947,46.6041307466,16.8129922477,26.4797506344,33.0942127905,87.7671523992,76.4743660304,26.895728291,71.6418628151,39.4279836297,33.1511476055,21.9412133656,49.2985876344,54.7288568945,61.7644413811,39.5360580159,48.2049240243,15.0657890486,74.8453755862,3.9697162327,46.3309272981,3.6967671205,29.4109279366,35.2237159178,71.2944696538,83.2668276235,8.6042287278,29.018178646,53.9090162771,74.1875874704,13.362141479,85.534882156,46.1570578637,16.6535624939,60.4663891357,46.4002226626,43.5334234645,5.9192657242,6.4233909782,70.9674985846,16.7364288218,6.140383666,1.6295357671,41.5132888558,7.0105695897,93.7402324433,67.9067125381,79.2202639865,73.9691802822,83.7211616527,95.1748897322,77.6210081594,52.6691899363,45.9668407426,30.0850082804,99.8853106577,44.4116175004,66.7492926269,80.344211132,2.9125625332,13.1282558744,77.1323341686,31.6479987137,12.0439293147,52.34792652,10.0973593886,11.1281788415,42.3558223361,26.0832770714,78.8802218674,65.7943757958,39.4931075012,62.3669620437,40.5895497709,75.5671730948,32.0576337297,97.9215110968,9.9871548843,46.6131220896,11.0937537323,59.9746432863,25.2756546914,39.1604177152,21.5325516688,55.5211448299,29.0247732257,10.4586600007,53.0534949121,77.3997550972,48.2887426956};
int rc = sqlite3_open_v2(s, &DB, SQLITE_OPEN_READWRITE,NULL);
if(rc != SQLITE_OK) {
} else {
string sql = "INSERT INTO test (name, vector) VALUES ('test', ?)";
try {
sqlite3_stmt *stmt = NULL;
rc = sqlite3_prepare_v2(DB, sql.c_str(), -1, &stmt, NULL);
if(rc != SQLITE_OK) {
cerr << "Error preparing stmt" << endl;
} else {
rc = sqlite3_bind_blob(stmt,1, nums, 1600, SQLITE_STATIC);
if(rc != SQLITE_OK) {
cerr << "bind failed" << endl;
} else {
rc = sqlite3_step(stmt);
if(rc != SQLITE_DONE) {
cerr << "no" << endl;
}
}
}
sqlite3_finalize(stmt);
} catch (const exception & e) {
cerr << e.what();
}
}
sqlite3_close(DB);
return 0;
};
static string selectTable(const char* s) {
sqlite3 *db;
int rc;
/* Open database */
rc = sqlite3_open(s, &db);
if( rc ) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
return(0);
} else {
fprintf(stderr, "Opened database successfully\n");
}
sqlite3_stmt *stmt;
/* Create SQL statement */
const char* sql = "SELECT * FROM test";
if (sqlite3_prepare_v2(db, sql, -1, &stmt, 0) != SQLITE_OK)
{
return 0;
}
int result = 0;
while (true)
{
result = sqlite3_step(stmt);
if (result == SQLITE_ROW)
{
// Get the size of the vector
int size = sqlite3_column_bytes(stmt, 2);
// Get the pointer to data
long double * p = (long double *)sqlite3_column_blob(stmt,2);
// Initialize the vector with the data
vector<long double> data(p, p+size);
allVects.push_back(data);
}
else
{
//cout << "not SQLITE_ROW" << endl;
break;
}
}
cout<< "before finalzie" << endl;
sqlite3_finalize(stmt);
while(sqlite3_close(db)!= SQLITE_OK) {
cout << "not ok" << endl;
}
cout<< "after close db" << endl;
}
I figured it out by myself. It's because the return type of my function selectTable() is string, however inside function it does not return a string. It's weird that my compiler did not complaint about it tho...
Similar issue is observed when return type of a function is bool and nothing is returned from that function. Even when return type is bool, call stack shows "invalid free" performed on a string. This was quite misleading.
Behavior is undefined when return statement is missing in a function except main().
In my scenario -
Crash is observed when the code is built with g++ version 8.3.0 on debian10(buster slim).
Same code works fine when it is built with g++ version 6.3.0 on debian9(stretch slim).
Developer will be able to detect this during the compile time based on the compiler options enabled. for eg: -Wall
Please note that this is independent of SQLite usage.

How to read string from sqlite using sqlite3_prepare_v2()?

i have a sqlite3 database and i need to query on it using visual c++ 2013?
i am using sqlite3.h for creating connection an manipulate database;
i am using this code to retraive data :
sqlite3_stmt *stmt;
sqlite3_prepare_v2(db, "SELECT * FROM response where list_id =?", -1, &stmt, NULL);
sqlite3_bind_int(stmt, 1, *2*);
tree<SensorState>::iterator itr = sensorTree.begin();
for (;;)
{
int rc = sqlite3_step(stmt);
if (rc == SQLITE_DONE){
break;
}
else if (rc != SQLITE_ROW){
cout << "error in reading sensore with error No: " << rc;
break;
}
const char* name = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
int value = sqlite3_column_int(stmt, 3);
cout << name<< "->" << value <<endl;
}
now in terminal i am getting this outbut:
but it must show this output:
s1->5
s2->2
s4->2
how can i rea string correctly from sqlite3 using c++?

sqlite3-Errors in Code

I want to use SQLite in my c++-Code, so I included SQLite3. When I use the following code in an external *.exe, it works without any problems:
void create_Database(char * file_name)
{
std::ofstream debug("C:\\Users\\Roland-User\\Desktop\\Debug.txt");
//const std::string&filename = file_name;
std::string filename = "C:\\Users\\Roland-User\\Desktop\\data";
std::string appendix = ".database";
std::string file = filename + appendix;
debug << "Creating database\n";
sqlite3 *db;
char *zErrMsg = 0;
int rc;
char * sql;
rc = sqlite3_open(file.c_str(), &db);
if(rc)
exit(-1);
sql = "CREATE TABLE DATABASE(HEADERDATA INT, WAVENUMBER INT, POSX INT, POSY INT, COUNT INT);";
rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
if(rc == 1)
{
sql = "DROP DATABASE DATABASE;";
rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
sqlite3_free(zErrMsg);
sql = "DROP TABLE DATABASE;";
rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
sqlite3_free(zErrMsg);
debug << "Having to drop database!\n";
};
sql = "CREATE TABLE DATABASE(HEADERDATA INT, WAVENUMBER INT, POSX INT, POSY INT, COUNT INT);";
rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
if(rc!=SQLITE_OK)
{
debug << "Still there are errors, I am confused!\n";
debug << "With error codes: " << rc << '\n';
sqlite3_free(zErrMsg);
exit(-1);
};
debug << "Finished with creating database\n";
debug.close();
sqlite3_close(db);
};
But when I insert this code in a DLL, I always get an error in my second check (where I get the debug information: "With error codes: "). I always get the error code 1. How can I fix this, and what am I doing wrong?
Thank you very much!

Data not inserted into the sqlite table c++

I am trying to save the contents of the key and value of a map into a database table. The .dbo file is created, but nothing goes into the table. It doesn't create table but it doesn't exit. I wonder what is wrong with my code.
void names_table( std::map<std::string, unsigned int> &names ){
std::string sql;
std::string str1;
std::string str2;
std::string str3;
sqlite3_stmt *stmt;
const char *file_names = create_db_file( ); /* default to temp db */
sqlite3 *db;
sqlite3_initialize( );
int rc = sqlite3_open_v2( file_names, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
if ( rc != SQLITE_OK) {
sqlite3_close( db );
cout << "Error: Database cannot open!" << endl;
exit( EXIT_FAILURE);
}
sql = "CREATE TABLE IF NOT EXISTS names_table (offset INTEGER PRIMARY KEY, stname TEXT);";
sqlite3_prepare_v2(db, sql.c_str(), sql.size(), &stmt, NULL);
if (sqlite3_step(stmt) != SQLITE_DONE) cout << "Didn't Create Table!" << endl;
for (auto pm = names.begin(); pm != names.end(); pm++) {
str2 = "'" + pm->first + "'";
char tmp[15];
sprintf(tmp,"%u",pm->second);
str3 = tmp;
str1 = (((("INSERT INTO names_table VALUES(" + str3) + ", ") + str2) + ");");
std::cout << str1 << std::endl;
sql = (char *)str1.c_str();
// stmt = NULL;
rc = sqlite3_prepare_v2(db, sql.c_str(), sql.size(), &stmt, NULL);
if ( rc != SQLITE_OK) {
sqlite3_close(db);
cout << "Error: Data cannot be inserted!" << endl;
exit ( EXIT_FAILURE);
}
}
sqlite3_close( db );
}
INSERT INTO names_table VALUES(ramsar, 8329) - I hope you're aware that string literals in SQL need to be enclosed in quotes. Try this: INSERT INTO names_table VALUES('ramsar', 8329).
EDIT: Actually, your code will never do what you want, because you're not even calling sqlite3_step after sqlite3_prepare_v2, which means that you're only compiling your SQL statement, but never evaluating it. Where did you find this bad example? See here and here decent examples on how to use the SQLite C++ interface properly.
PS: Stop messing around with sprintf in C++. You have std::stringstream for it.

Proper use of callback function of sqlite3 in C++

I have the following C++ code for testing purposes in conjunction with SQLite3.
It's a class called customer with a callback function declared. This callback function is called whenever sqlite3_exec() returns results (records) from the SQLite database.
What I don't like about this construction is that source code to process the results is located in a call back function outside of the class rather than the results being processed by the class method from which sqlite3_exec() is called.
I could use global variables that will be used in the class method after the callback function has finished extracting the values from the SQL query results. But what if there is more than one record and the call back function is called several times. Then I need to work with arrays unless I make sure that I will only have single results.
Do I need to forget about the callback function and go into deeper calls of the SQLite API?
Or do I need to go to a C++ wrapper, I suppose that there is no call back mechanism there and the results being passed back to the class method itself?
// customer
#include "Customer\customer.h"
//## begin module%50E6CCB50119.additionalDeclarations preserve=yes
static int callback(void *NotUsed, int argc, char **argv, char **azColName)
{
int i;
char* columnName;
char* columnValueString;
short int columnValueShortInt = 0;
int columnValueInt = 0;
cout << "begin of callback function\n";
for(i=0; i<argc; i++)
{
columnName = azColName[i];
if (strcmp(columnName, "FirstName")==0 || strcmp(columnName, "LastName")==0)
{
columnValueString = argv[i];
cout << "columnName = " << columnName << "; value = " << columnValueString <<"\n";
}
else
{
if(strcmp(columnName, "Age")==0)
{
stringstream(argv[i]) >> columnValueShortInt;
cout << "columnName = " << columnName << "; value = " << columnValueShortInt <<"\n";
}
else // strcmp(columnName, "Id")==0)
{
stringstream(argv[i]) >> columnValueInt;
cout << "columnName = " << columnName << "; value = " << columnValueInt <<"\n";
}
}
}
cout << "end of call back function \n";
return 0;
}
//## end module%50E6CCB50119.additionalDeclarations
// Class customer
customer::customer ()
//## begin customer::customer%50F969EE01E4.hasinit preserve=no
//## end customer::customer%50F969EE01E4.hasinit
//## begin customer::customer%50F969EE01E4.initialization preserve=yes
//## end customer::customer%50F969EE01E4.initialization
{
//## begin customer::customer%50F969EE01E4.body preserve=yes
customerId = 0;
zErrMsg = 0;
customerDataBaseRc = sqlite3_open("customerdb", &customerDataBase);
if(customerDataBaseRc)
{
fprintf(stderr, "Can't open database %s\n", sqlite3_errmsg(customerDataBase));
sqlite3_close(customerDataBase);
}
const char * pSQL[6];
const char * sqlStatement;
pSQL[0] = "create table customerTable (Id int, FirstName varchar(30), LastName varchar(30), Age smallint)";
// execute all the sql statements
for(int i = 0; i < 1; i++)
{
customerDataBaseRc = sqlite3_exec(customerDataBase, pSQL[i], callback, 0, &zErrMsg);
if( customerDataBaseRc !=SQLITE_OK )
{
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
break; // break the loop if error occur
}
}
//## end customer::customer%50F969EE01E4.body
}
customer::~customer ()
{
//## begin customer::~customer%50F93279003E.body preserve=yes
const char *pSQL[6];
// Remove all data in customerTable
pSQL[0] = "delete from customerTable";
// Drop the table from database
pSQL[1] = "drop table customerTable";
// execute all the sql statements
for(int i = 0; i < 2; i++)
{
customerDataBaseRc = sqlite3_exec(customerDataBase, pSQL[i], callback, 0, &zErrMsg);
if( customerDataBaseRc !=SQLITE_OK )
{
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
break; // break the loop if error occur
}
}
cout << "destructor";
//## end customer::~customer%50F93279003E.body
}
//## Other Operations (implementation)
unsigned int customer::createCustomer (char iCustomerFirstName[20], char iCustomerLastName[20], unsigned short iCustomerAge)
{
//## begin customer::createCustomer%50EBFFA3036B.body preserve=yes
const char *sqlStatement;
string result; // string which will contain the result
ostringstream convert; // stream used for the conversion
convert << "insert into customerTable (Id, FirstName, LastName, Age) values (" << customerId << ", '" << iCustomerFirstName << "', '" << iCustomerLastName << "', " << iCustomerAge << ")";
result = convert.str(); // set 'Result' to the contents of the stream
sqlStatement = result.c_str();
// Execute sql statement
customerDataBaseRc = sqlite3_exec(customerDataBase, sqlStatement, callback, 0, &zErrMsg);
// Check for errors
if(customerDataBaseRc !=SQLITE_OK )
{
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
return customerId++;
//## end customer::createCustomer%50EBFFA3036B.body
}
char * customer::getCustomer (unsigned int iCustomerId)
{
//## begin customer::getCustomer%50ED3D700186.body preserve=yes
const char *sqlStatement;
char *tmp ="blabla";
string result; // string which will contain the result
ostringstream convert; // stream used for the conversion
convert << "select * from customerTable where Id = " << iCustomerId;
result = convert.str(); // set 'Result' to the contents of the stream
sqlStatement = result.c_str();
// Execute the sql statement
customerDataBaseRc = sqlite3_exec(customerDataBase, sqlStatement, callback, 0, &zErrMsg);
// Check for errors
if(customerDataBaseRc !=SQLITE_OK )
{
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
return tmp;
//## end customer::getCustomer%50ED3D700186.body
}
// Additional Declarations
//## begin customer%50E6CCB50119.declarations preserve=yes
//## end customer%50E6CCB50119.declarations
//## begin module%50E6CCB50119.epilog preserve=yes
//## end module%50E6CCB50119.epilog
What one typically does in this case is take advantage of the void * (which you call NotUsed) parameter of the callback -- a parameter you define when you install the callback. For C++, you would typically set that parameter to the this pointer to your interested object, and you would make the callback (an extern "C" function in a c++ source file) a friend method to your class (if necessary).
This would look like this:
class customer
{
...
public:
int callback(int argc, char **argv, char **azColName);
};
static int c_callback(void *param, int argc, char **argv, char **azColName)
{
customer* cust = reinterpret_cast<customer*>(param);
return cust->callback(argc, argv, azColName);
}
char* customer::getCustomer(int id)
{
...
rc = sqlite3_exec(db, sql, c_callback, this, &errMsg);
...
}
int customer::callback(int argc, char **argv, char **azColName)
{
...
}
Using sqlite3_exec has the disadvantages that you have to convert some values back from a string to a number, and that it needs to allocate memory for all result records (which can lead to problems when reading large tables).
Furthermore, the callback always is a separate function (even if it's in the same class).
For your example query, using the sqlite3_prepare/sqlite3_step/sqlite3_finalize API would look like this:
void one_customer::readFromDB(sqlite3* db, int id)
{
sqlite3_stmt *stmt;
int rc = sqlite3_prepare_v2(db, "SELECT FirstName, LastName, Age"
" FROM customerTable"
" WHERE Id = ?", -1, &stmt, NULL);
if (rc != SQLITE_OK)
throw string(sqlite3_errmsg(db));
rc = sqlite3_bind_int(stmt, 1, id); // Using parameters ("?") is not
if (rc != SQLITE_OK) { // really necessary, but recommended
string errmsg(sqlite3_errmsg(db)); // (especially for strings) to avoid
sqlite3_finalize(stmt); // formatting problems and SQL
throw errmsg; // injection attacks.
}
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
string errmsg(sqlite3_errmsg(db));
sqlite3_finalize(stmt);
throw errmsg;
}
if (rc == SQLITE_DONE) {
sqlite3_finalize(stmt);
throw string("customer not found");
}
this->id = id;
this->first_name = string(sqlite3_column_text(stmt, 0));
this->last_name = string(sqlite3_column_text(stmt, 1));
this->age = sqlite3_column_int(stmt, 2);
sqlite3_finalize(stmt);
}
(This code handles errors by just throwing a string with the error message.)