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;
}
Related
I have seen other threads about this topic, but none of them helped me solve my specific issue.
Basically I am trying to access my database to update one column.
I have asynchronous functions, but currently I am not using multiple clients, so I don't think that's the problem (but correct me if I am wrong).
inline void update(const std::string& nickname)
{
sqlite3* database;
int result = sqlite3_open(databasePath, &database);
MANAGE_SQLITE_ERRORS(result, database);
std::string sqlCommand = "UPDATE Users SET IsOnline = (CASE WHEN IsOnline = 0 THEN 1 ELSE 0 END) WHERE Nickname = '" + nickname + "';";
sqlite3_stmt* statement;
result = sqlite3_prepare_v2(database, sqlCommand.c_str(), -1, &statement, nullptr);
MANAGE_SQLITE_ERRORS(result, database);
result = sqlite3_step(statement);
MANAGE_SQLITE_ERRORS(result, database);
result = sqlite3_finalize(statement);
MANAGE_SQLITE_ERRORS(result, database);
sqlite3_close(database);
}
And this function ^ is where the errors happen.
MANAGE_SQLITE_ERRORS is what reports the error:
#define MANAGE_SQLITE_ERRORS(result, database)\
if (result != SQLITE_OK){\
std::printf("Database error: %s\n", sqlite3_errmsg(database));\
sqlite3_close(database);\
return;\
}\
The function is called by this one:
inline void manage(Session& session)
{
session.asyncWriteRaw(session.data, session.data.size());
update(session.getPlayerNickname());
}
And asyncWriteRaw:
void Session::asyncWriteRaw(std::uint8_t* data, std::size_t size)
{
asio::async_write(m_socket, asio::buffer(data, size),
[self = shared_from_this()](const asio::error_code& errorCode, std::size_t)
{
if (!errorCode)
{
printf("message sent!\n");
}
else
{
printf("Failed to send server message: %s\n", errorCode.message().c_str());
}
});
}
EDIT
The following function (that only performs read) works fine:
inline int getPlayerInfo(
const std::string& username, const std::string& password)
{
sqlite3* database;
int result = sqlite3_open(databasePath, &database);
MANAGE_SQLITE_ERRORS_OPT(result, database);
std::string sqlCommand = "SELECT * FROM Users WHERE Username='" + username + "' AND Password='" + password + "'";
sqlite3_stmt* statement;
result = sqlite3_prepare_v2(database, sqlCommand.c_str(), -1, &statement, 0);
MANAGE_SQLITE_ERRORS_OPT(result, database);
/
int stepResult = sqlite3_step(statement);
if (stepResult == SQLITE_ROW)
{
int isSuspended = sqlite3_column_int(statement, 4);
if (!isSuspended)
{
// all the reads happen here to all the columns
}
else
{
return -1;
}
}
else
{
return -2;
}
sqlite3_close(database);
return 0;
}
I would appreciate any help, and I hope the code I gave is enough.
In my program I need to connect to sqlite db from string.
I receive the sql db (UTF-8) as string from the user, I'm using the sqlite3_open function that receives file path of the db file so I'm creating a file, write the string to it and using it to connect to the db:
sqlite3*
SQLiteUtils::ConnectToDatabase(std::string const& dbStr)
{
sqlite3* db;
std::ofstream dst("sql.db", std::ios::binary);
dst << dbStr;
int const ret = sqlite3_open("sql.db", &db);
if (SQLITE_OK != ret) {
LOG_ERROR("Can't open database: " + std::string(sqlite3_errmsg(db)));
CloseConnectionToDatabase(db);
return nullptr;
}
return db;
}
Is there a way to connect to string without writing the string to file and connect to the file?
Thanks to #Shawn in the comments I used sqlite3_serialize() and sqlite3_deserialize().
This solution work for me:
const char* IN_MEMORY_DB = ":memory:";
sqlite3* ImportDatabase(const std::string &dbStr)
{
sqlite3 *db;
int ret = sqlite3_open(IN_MEMORY_DB, &db);
if (SQLITE_OK != ret)
{
return nullptr;
}
// give serialized buffer ownership to SQLite
unsigned int flags = SQLITE_DESERIALIZE_FREEONCLOSE|SQLITE_DESERIALIZE_RESIZEABLE;
// allocate the serialized buffer from heap, SQLite requires to dynamic allocation for deserialize to work correctly
unsigned char* data = static_cast<unsigned char*>(sqlite3_malloc(dbStr.size()));
if (nullptr == data)
{
sqlite3_close(db);
return nullptr;
}
for (int i = 0; i < dbStr.size(); ++i)
{
data[i] = static_cast<unsigned char>(dbStr[i]);
}
int err = sqlite3_deserialize(db, "main", data, dbStr.size(), dbStr.size(), flags);
if (err != SQLITE_OK)
{
sqlite3_close(db);
return nullptr;
}
return db;
}
In this solution I allocate the sql memory and copy the string to it without writing the data string to file in the middle.
How to connect to sqlite3 db from string
The default behaviour of sqlite3_open is to open/create the db, and so the only thing you need to do is:
int const ret = sqlite3_open(dbStr.c_str(), &db);
// ^^^^^^^^^^^^^
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());
}
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'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;
}