I'm new in the world of C++.
I'm trying to store into a variable a value contained in a sqlite table that I've created but I don't know how to do (I research a lot befor asking here).
So, after I open the DB connection I execute this:
char* sql = new char[4096];
strcpy(sql, statement.c_str());
/* Execute SQL statement */
int rc = sqlite3_exec(DB, sql, callback, 0, &zErrMsg);
if( rc != SQLITE_OK ){
fprintf(stderr, "SQL ERROR: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
fprintf(stdout, "STATEMENT:\n\n%s\n\nEXECUTED SUCCESSFULLY!\n\n", statement.c_str());
}
And I get this:
OPENED DATABASE SUCCESSFULLY
sent = 0
sent = 0
sent = 0
sent = 0
sent = 0
sent = 1
sent = 1
sent = 1
STATEMENT:
SELECT sent FROM Message;
EXECUTED SUCCESSFULLY!
What I want to do is to store the value contained in "sent" (the datatype in the db is boolean) in a int variables that I can manipulate to check some condition. Or maybe to store all the values into a int array.
How can I do? Please help me!
Thanks a lot!
EDIT:
I'm using sqlite3 library.
And this is my callback function:
static int callback(void *NotUsed, int argc, char **argv, char **azColName) {
int i;
for(i = 0; i<argc; i++) {
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
Don't use sqlite3_exec() for anything that needs to do anything with the results of a query, or anything that involves user-supplied values. Use a prepared statement.
Something like
sqlite3_stmt *stmt;
int rc = sqlite3_prepare_v2(DB, statement.c_str(), statement.length(), &stmt, nullptr);
if (rc != SQLITE_OK) {
// handle the error
}
// Loop through the results, a row at a time.
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
int sent = sqlite3_column_int(stmt, 0);
// etc.
}
// Free the statement when done.
sqlite3_finalize(stmt);
callback is a function called for each row of the result set. You can assign the values to an array or vector in that function. https://www.sqlite.org/c3ref/exec.html
The 2nd argument to the sqlite3_exec() callback function is the number
of columns in the result. The 3rd argument to the sqlite3_exec()
callback is an array of pointers to strings obtained as if from
sqlite3_column_text(), one for each column. If an element of a result
row is NULL then the corresponding string pointer for the
sqlite3_exec() callback is a NULL pointer. The 4th argument to the
sqlite3_exec() callback is an array of pointers to strings where each
entry represents the name of corresponding result column as obtained
from sqlite3_column_name().
You need something like:
int callback(void *p, int size, char **column_text, char **column_name) {
if (size == 0) return -1;
auto &container = *static_cast<std::vector<std::string>*>(p);
if (!column_text[0]) container.push_back("NULL");
else container.push_back(column_text[0]);
return 0;
}
and then you can store the values in your container with:
std::vector<std::string> container;
/* Execute SQL statement */
int rc = sqlite3_exec(DB, statement.c_str(), callback, &container, &zErrMsg);
if( rc != SQLITE_OK ){
fprintf(stderr, "SQL ERROR: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
fprintf(stdout, "STATEMENT:\n\n%s\n\nEXECUTED SUCCESSFULLY!\n\n", statement.c_str());
}
Related
I'm making a cocos2dx (c++) android game and I'm trying to implement SQLite but I can't even create a table!
Here's the function where i open database and create a table inside:
void HelloWorld::initDB() {
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
std::string wpath = FileUtils::getInstance()->getWritablePath() + "save.db";
FILE *f = fopen(wpath.c_str(), "r");
if (f == NULL) {
firstLogin = true;
} else {
firstLogin = false;
}
fclose(f);
#endif
int result;
result = sqlite3_open(wpath.c_str(), &db);
if (result != SQLITE_OK) {
CCLOG("open database failed, number %d", result);
}
if(firstLogin) {
sql_query("CREATE TABLE IF NOT EXISTS data(variable, value)");
insertVariableInt("coins", 0);
insertVariableInt("timesPlayed", 0);
insertVariableInt("highScore", 0);
}
}
And here is my sql_query() function:
int HelloWorld::sql_query(const char * query,int (*callback)(void*,int,char**,char**), void* data)
{
char* errMsg = 0;
int result = sqlite3_exec(db, query, callback, data, &errMsg);
if (errMsg) {
CCLOG("SQLite3: %s", errMsg);
sqlite3_free(errMsg);
}
return result;
}
When I run my game, save.db file is created but there's no table inside, it's EMPTY! (0 bytes). And it gives me NO errors, it gives me just an empty save.db file.
How can I solve this problem? THANKS!
I tested your code and I could create the data table.
SQLite version 3.16.2 2017-01-06 16:32:41
Enter ".help" for usage hints.
sqlite> .tables
data
I see you're calling sql_query with only one parameter. Did you overload that method or did you define default argument values? Anyway here's the code I use. Note that you can call isFileExist instead of fopen.
MainMenu::MainMenu() {
std::string wpath = FileUtils::getInstance()->getWritablePath() + "save.db";
bool firstLogin = FileUtils::getInstance()->isFileExist(wpath);
int result = sqlite3_open(wpath.c_str(), &db);
if (result != SQLITE_OK) {
CCLOG("open database failed, number %d", result);
}
if(firstLogin) {
sql_query("CREATE TABLE IF NOT EXISTS data(variable, value)", nullptr, nullptr);
}
}
int MainMenu::sql_query(const char * query,int (*callback)(void*,int,char**,char**), void* data)
{
char* errMsg = 0;
int result = sqlite3_exec(db, query, callback, data, &errMsg);
if (errMsg) {
CCLOG("SQLite3: %s", errMsg);
sqlite3_free(errMsg);
}
return result;
}
I have the following query that is stored in buffer, and then is executed using my own conn.executeQuery().
char buffer[QUERY_MAX];
snprintf(buffer, QUERY_MAX, "SELECT * FROM players WHERE player_id = %d", 1);
conn.executeQuery(buffer);
While this works fine, I wonder if it can be simplified as something similar to...
conn.executeQuery("SELECT * FROM players WHERE player_id = "%d", 1);
My function:
bool SQLConnection::executeQuery(const char *query)
{
// Validate connection.
if (!m_connected)
return false;
// Execute the query
int status = mysql_query(&m_conn, query);
if (status != 0) {
sprintf(m_errorMessage, "Error: %s", mysql_error(&m_conn));
return false;
}
// Store the result
m_result = mysql_store_result(&m_conn);
return true;
}
I'm aware of varargs, and tried to follow the example here (Variable number of arguments in C++?), but I'm not simply trying to read the varargs, but to include them in the query, which is apparently troublesome for me.
Any thoughts are welcomed, thanks.
You need a prepared statement instead, like the following
MYSQL_STMT *stmt;
MYSQL_BIND params[1 /* Here it will be the number of arguments */];
MYSQL_BIND result[1 /* Here it will be the number of columns in the result row */];
int value;
const char *query;
int id;
memset(params, 0, sizeof params);
memset(result, 0, sizeof result);
// Assuming `mysql' is an initialized MYSQL object
stmt = mysql_stmt_init(mysql);
if (stmt == NULL)
return
// `SELECT ID' just to illustrate how you can select an integer
// value
query = "SELECT ID FROM players WHERE player_id = ?";
if (mysql_stmt_prepare(stmt, query, strlen(query)) != 0)
goto error;
value = 1;
result[0].buffer_type = MYSQL_TYPE_LONG;
result[0].buffer = &id;
params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = &value;
if (mysql_stmt_bind_param(stmt, params) != 0)
goto error;
if (mysql_stmt_bind_result(stmt, result) != 0)
goto error;
if (mysql_stmt_execute(stmt) != 0)
goto error;
if (mysql_stmt_fetch(stmt) != 0)
goto error;
// Now all the columns are in the buffers of `result'
// or the bound variables (which is why we pass their address)
fprintf(stdout, "%d\n", id);
error:
mysql_stmt_close(stmt);
I am implementing a function that receives any SQL statement and then executes it.
void dbExec(std::string str, bool vertical)
{
sqlite3 *db; // Create db object
char *zErrMsg = 0;
int rc;
const char *sql = str.c_str(); // Convert string to char
sqlite3_stmt *stmt = NULL; // SQL statement
/* Open Database */
rc = sqlite3_open("db/survey.db",&db);
if (rc)
{
fprintf(stderr, "DBOPEN_ERR: %s\n", sqlite3_errmsg(db));
}
/* Execute SQL statement */
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); // Prepare statement
if (rc != SQLITE_OK )
{ // Check error
fprintf(stderr, "DB error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
int cols = sqlite3_column_count(stmt); // Number of columns
const char *data = NULL; // data char pointer
if (vertical)
{
sqlite3_step( stmt );
for (int i = 0; i < cols; i++)
{
// Print column name and info
data = (const char*)sqlite3_column_text( stmt, i );
std::cout << std::setw(20) << sqlite3_column_name(stmt,i)
<< std::setw(30) << (data ? data : "[NULL]" );
printf("\n");
}
}
else
{
for (int i = 0; i < cols; i++)
{
// Print column name
std::cout << std::setw(15) << sqlite3_column_name(stmt,i);
}
printf("\n");
while ( sqlite3_step( stmt ) == SQLITE_ROW )
{
// print each row
for (int i = 0; i < cols; i++)
{
data = (const char*)sqlite3_column_text( stmt, i );
std::cout << std::setw(15) << (data ? data : "[NULL]" );
}
printf("\n");
}
}
/* Close Database */
sqlite3_close(db);
}
When the str argument is:
SELECT * FROM TABLE
it works perfect. If the str argument is:
INSERT INTO TABLE (COL1) VALUES(100)
it doesn't work.
However, if inside of the function I add the following line:
str = "INSERT INTO TABLE (COL1) VALUES(100)";
it works perfect. I tried many things but I still can't figure out what's going on... Any ideas?
Thanks!
EDIT:
The function dbExec is being called in this way:
void addBorehole()
{
std::string statement;
statement = "INSERT INTO BOREHOLE (TOTAL_DEPTH) VALUES (45)";
dbExec(statement, false);
}
OK, the problem was solved by writing the following line before closing the database:
sqlite3_finalize( stmt );
Info: https://sqlite.org/c3ref/close.html
If the database connection is associated with unfinalized prepared
statements or unfinished sqlite3_backup objects then sqlite3_close()
will leave the database connection open and return SQLITE_BUSY.
However, I still didn't get why it worked when I hardcoded the statement inside of the function.
Thank you!
Your problem is that the database is busy (SQLITE_BUSY). The documentation sais:
[...] SQLITE_BUSY indicates a conflict with a separate database connection, probably in a separate process [...]
So there must be a process that is blocking your database.
To get the process which is blocking the database you can do the following (Copied from the Stack Exchange network).
Linux:
$ fuser development.db
This command will show what process is locking the file:
> development.db: 5430
Just kill the process...
kill -9 5430
Windows:
PowerShell method:
IF((Test-Path -Path $FileOrFolderPath) -eq $false) {
Write-Warning "File or directory does not exist."
}
Else {
$LockingProcess = CMD /C "openfiles /query /fo table | find /I ""$FileOrFolderPath"""
Write-Host $LockingProcess
}
More details How to find out which process is locking a file or folder in Windows
The other method for Windows would be to use the ProcessExplorer.
In the following snippet I am attempting to manually iterate through the sqlite3 virtual machine to investigate opcodes.
I though that vdbe->nOp should return the number of operations for my query but it returns a number of operations that changes every time I run the code and seems wildly incorrect. This particular query should only have 20 opcodes according to .explain in sqlite3.exe. When I run the code below I see values for nOp in the millions, far larger than the size of vdbe->aOp. Code segfaults.
void debug_query(){
sqlite3_stmt *stmt;
sqlite3 *db;
sqlite3_open("./data/dummy_data.db", &db);
int rc = sqlite3_prepare_v2(db, "SELECT * FROM track", 256, &stmt, NULL);
if( rc!=SQLITE_OK ) {
printf("query failed\n");
return;
}
Vdbe *vdbe = (Vdbe*) stmt;
VdbeOp *o = vdbe->aOp;
std::cout << vdbe->nOp << std::endl;
int i;
for(i = 0; i < vdbe->nOp && o[i].opcode != OP_Next; i++);
}
I'm having great difficulty to find a working example on the net regarding getting multiple values from a SQlite DB using xcode and cocos2dx. Here is the sql query I have:
char sql_query[100];
sprintf(sql_query, "SELECT * FROM SQList WHERE ColumnD BETWEEN %d AND %d ORDER BY RANDOM() LIMIT 1", MinColumnD, MaxColumnD);
The query it self seems to work, the main problem is how do I get the values that I collect from 'select *' into another int or char parameter so that I can use it?
Some example I found referred to using a callback to a struct or mentioned about using sqlite3_prepare_v2 and the step method.
I'm unable to find an example for either methods though, please help!
When using sqlite3_exec, you have to convert all values from strings, and you have to use the callback's void * pointer or some global variable to return data:
struct MyData {
string A;
int B, C;
};
int exec_callback(void *ptr, int argc, char *argv[], char *names[])
{
vector<MyData> *list = reinterpret_cast<vector<MyData> *>(ptr);
MyData d;
d.A = argv[0] ? argv[0] : "";
d.B = atoi(argv[1]);
d.C = atoi(argv[2]);
list->push_back(d);
return 0;
}
void query_with_exec()
{
vector<MyData> list;
char *errmsg = NULL;
sqlite3_exec(db, "SELECT a, b, c FROM SQList /* WHERE ... */",
exec_callback, &list, &errmsg);
if (errmsg) {
printf("error: %s!\n", errmsg);
return;
}
// use list ...
}
When using sqlite3_prepare*, you have to call sqlite3_step in a loop until it does not return SQLITE_ROW anymore (when you expect only one record, you can call it only once):
void query_with_step()
{
vector<MyData> list;
sqlite3_stmt *stmt;
int rc = sqlite3_prepare_v2(db, "SELECT a, b, c FROM SQList /* WHERE ... */",
-1, &stmt, NULL);
if (rc != SQLITE_OK) {
printf("error: %s!\n", sqlite3_errmsg(db));
return;
}
for (;;) {
rc = sqlite3_step(stmt);
if (rc == SQLITE_DONE)
break;
if (rc != SQLITE_ROW) {
printf("error: %s!\n", sqlite3_errmsg(db));
break;
}
MyData d;
const char *text = (const char *)sqlite3_column_text(stmt, 0);
d.A = text ? text : "";
d.B = sqlite3_column_int(stmt, 1);
d.C = sqlite3_column_int(stmt, 2);
list.push_back(d);
}
sqlite3_finalize(stmt);
// use list ...
}
Get the values using "SELECT" query and store your char and int values in an array.Create a NSMUtableArray and store the values which you will get from SELECT query .The following code will help you
-(int)find_putts_count:(int)holeno{
sqlite3 *database;
NSString *databasePath;
NSMUtableArray *arr_tracking_details = [[NSMUtableArray alloc] init];
int putts = -1;
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:#"GolfElite.sqlite"];
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
// Setup the SQL Statement and compile it for faster access
NSString *sqlStatement = nil;
sqlStatement = [NSString stringWithFormat:#"select Putts from Holes WHERE HoleNo == %d and ShotNo == %d and PlayerId == '%#'",holeno,1,[arr_players_id objectAtIndex:scorecard_player_no]];
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, [sqlStatement UTF8String], -1, &compiledStatement, NULL) == SQLITE_OK)
{
if(sqlite3_step(compiledStatement) == SQLITE_ROW)
{
[arr_tracking_details addObject:[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 3)]];
[arr_tracking_details addObject:[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement,6)]];
[arr_tracking_details addObject:[NSString stringWithFormat:#"%d",(int)sqlite3_column_int(compiledStatement, 4)]];
putts = (int)sqlite3_column_int(compiledStatement, 0);
}
}
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
return putts;
}