How can i save a struct in SQLITE C++ - c++

I try to save a simpel struct (vector3) in the sqlite3_bind_blob but I have no idea how this works. If I debug this code the console window crashes. How can i fix this problem?
struct vector3
{
int X;
int Y;
int Z;
};
int main ()
{
sqlite3 *db = NULL;
sqlite3_stmt *res = NULL;
sqlite3_open_v2("SaveGame1.sav", &db, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, NULL);
string query = "CREATE TABLE IF NOT EXISTS TestPlayer (vector BLOB)";
sqlite3_prepare_v2 (db, query.c_str(), query.length(), &res, 0);
sqlite3_step(res);
// Try to add mij struct
vector3 MyLocation;
MyLocation.X = 100;
MyLocation.Y = 100;
MyLocation.Z = 100;
query = "INSERT INTO TestPlayer (location) VALUES (?);";
sqlite3_prepare_v2 (db, query.c_str(), query.length(), &res, 0);
sqlite3_bind_blob (res, 1, &MyLocation, sizeof(vector3), SQLITE_TRANSIENT);
sqlite3_step(res);
query = "SELECT * FROM TestPlayer;";
sqlite3_prepare_v2 (db, query.c_str(), query.length(), &res, 0);
sqlite3_step(res);
const vector3 *GetLoc = (const vector3 *) sqlite3_column_blob(res, 0);
cout << GetLoc->X << endl;
sqlite3_finalize(res);
sqlite3_close(db);
return 0;
}

I believe that sqlite3_column_blob() must be used on the results of a query operation. Try issuing a query for the data that you just inserted

Related

The database is locked, how do we find and fix its cause? [SQL]

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.

Cout doesn't display all fields (C++, MySQL)

I have a function:
int main()
{
MySQL::Connect("127.0.0.1", 3306, "root", "", "player");
MySQL::ExecuteQuery("select * from player");
while (row = mysql_fetch_row(res))
{
std::cout << row[2] << "\n";
MySQL::SetDatabase("account");
MySQL::ExecuteQuery("select * from account"); // This function causes a problem.
// while (row = mysql_fetch_row(res))
// break;
}
return 0;
}
Which should get everything of player names from player table what it does and what it display in console (I'm posting a screenshot of table in Navicat):
https://i.stack.imgur.com/n6HJQ.png
However, when MySQL::ExecuteQuery("select * from account"); function is used which selects everything in account table, the earlier std::cout display only one player name instead of two:
https://i.stack.imgur.com/q9ZkP.png
What can I do in this situation? Or is there another simple way to connect to MySQL in C++? Please help.
I attach files such as MySQL_Func.cpp and MySQL_Func.h which include problematic function:
.cpp:
#include "MySQL_Func.h"
#include "../Log.hpp"
MYSQL* conn;
MYSQL_ROW row;
MYSQL_RES* res;
std::string conf_ip;
unsigned int conf_port;
std::string conf_db;
std::string conf_login;
std::string conf_password;
std::string error = mysql_error(conn);
int err = 0;
namespace MySQL
{
void Connect(std::string ip, unsigned int port, std::string login, std::string password, std::string db)
{
conf_ip = ip;
conf_port = port;
conf_login = login;
conf_password = password;
conf_db = db;
if (conn != 0)
{
SendLog(0, "MySQL has been restared.");
mysql_close(conn);
}
conn = mysql_init(0);
if (!mysql_real_connect(conn, ip.c_str(), login.c_str(), password.c_str(), db.c_str(), port, NULL, 0))
{
error = mysql_error(conn);
SendLog(1, "Connection with database was failed: " + error + ".");
exit(1);
}
else
{
SendLog(0, "Successfully connected with database!");
}
}
void ExecuteQuery(std::string query)
{
err = mysql_query(conn, query.c_str());
res = mysql_store_result(conn);
if (res != 0) // Protection against NullPointer.
{
int total_rows = mysql_num_fields(res);
if (total_rows != 0) // If total rows isn't 0.
{
if (err)
{
error = mysql_error(conn);
SendLog(1, "Query execute failed:" + error + ".");
mysql_free_result(res);
exit(1);
}
else
{
SendLog(0, "Query has been sent (" + query + ")!");
}
}
else
{
SendLog(1, "Query has been sent: (" + query + ") but its value is 0.");
exit(0);
}
}
else
{
exit(1);
}
}
void SetDatabase(std::string current_db)
{
if (current_db != conf_db) // If current_db isn't conf_db.
MySQL::Connect(conf_ip, conf_port, conf_login, conf_password, current_db);
}
}
.h:
#pragma once
#include <iostream>
#include <mysql.h>
#include <string>
extern MYSQL* conn;
extern MYSQL_ROW row;
extern MYSQL_RES* res;
extern std::string conf_ip;
extern unsigned int conf_port;
extern std::string conf_db;
extern std::string conf_login;
extern std::string conf_password;
namespace MySQL
{
void Connect(std::string ip, unsigned int port, std::string login, std::string password, std::string db);
void ExecuteQuery(std::string query);
void SetDatabase(std::string database);
}
Your two calls to ExecuteQuery share state, namely the "currently active query" and the buffered resultset you downloaded with mysql_result_row.
This is called a non-reentrant function.
Some options:
have a dedicated connection for each query
Do a single query that somehow combines the results (ie JOIN the players and accounts table)
Fetch all the players first into a dedicated datastructure (eg std::vector or std::map), then do the query for accounts later.

Can't create SQLite table in C++ (android)

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

How do I declare and use an array of struct in a for loop?

I am working on a native winapi application using cpp and sqlite http://github.com/jacksiro/vsongbook-cpp using the falcon c++ IDE. This is my database:
void CreateDatabase()
{
sqlite3 * db = NULL;
int db_qry;
const char * sqlCreateTables =
"CREATE TABLE my_friends(friend_name VARCHAR(20), "
"friend_job VARCHAR(20), friend_job INTEGER(11));";
db_qry = sqlite3_open("friends.db", &db);
db_qry = sqlite3_exec(db, sqlCreateTables, NULL, NULL, NULL);
sqlite3_close(db);
}
And the database I insert it with this:
void InsertToDatabase()
{
int db_qry;
char *error;
sqlite3 *db;
const char *sqlInsert =
"INSERT INTO my_friends(friend_name, friend_job, friend_age) "
"VALUES('Ngunjiri James', 'Teacher', 25);"
"INSERT INTO my_friends(friend_name, friend_job, friend_age) "
"VALUES('Wafula Shem', 'Capernter', 30);"
"INSERT INTO my_friends(friend_name, friend_job, friend_age) "
"VALUES('Jane Akinyi', 'Nurse', 23);";
db_qry = sqlite3_open("friends.db", &db);
db_qry = sqlite3_exec(db, sqlInsert, NULL, NULL, &error);
sqlite3_close(db);
}
Now I have a simple listbox in my window where I populate it with values from my sqlite table like this.
inline UINT AddStringList(const HWND hList,const ustring& s)
{
return static_cast<UINT>(SendMessage(hList,LB_ADDSTRING,0,
reinterpret_cast<LPARAM>(s.c_str())));
}
//I call this method in my WM_CREATE in WinMain and it works very well
void AddFriendListBox(const HWND hList)
{
sqlite3 * db = NULL;
sqlite3_stmt * stmt;
int i, db_qry;
const char * tail;
const char * sqlSelect = "SELECT * FROM my_friends";
db_qry = sqlite3_open("friends.db", &db);
if(sqlite3_prepare(db, sqlSelect, -1, &stmt, &tail) == SQLITE_OK)
{
while(sqlite3_step(stmt) != SQLITE_DONE)
{
for(i = 0; i < sqlite3_column_count(stmt); i++) {
const unsigned char * p = reinterpret_cast<const unsigned char *>
(sqlite3_column_text(stmt, 0));
const char * finaltext = (const char *)p;
AddStringList(hList,_T(finaltext));
}
}
sqlite3_finalize(stmt);
}
sqlite3_close(db);
}
I came across some code http://zetcode.com/gui/winapi/advancedcontrols/ on creating a list box and populating it using a struct:
typedef struct {
wchar_t name[30];
wchar_t job[20];
int age;
} Friends;
Friends friends[] = {
{L"Lucy", L"waitress", 18},
{L"Thomas", L"programmer", 25},
{L"George", L"police officer", 26},
{L"Michael", L"producer", 38},
{L"Jane", L"steward", 28},
};
Then using it as below:
case WM_CREATE:
hwndList = CreateWindowW(WC_LISTBOXW , NULL, WS_CHILD
| WS_VISIBLE | LBS_NOTIFY, 10, 10, 150, 120, hwnd,
(HMENU) IDC_LIST, NULL, NULL);
for (int i = 0; i < ARRAYSIZE(friends); i++) {
SendMessageW(hwndList, LB_ADDSTRING, 0, (LPARAM) friends[i].name);
}
break;
Since I could not understand the ARRAYSIZE used above. I substituted it with an int 5 as below:
case WM_CREATE:
hwndList = CreateWindowW(WC_LISTBOXW , NULL, WS_CHILD
| WS_VISIBLE | LBS_NOTIFY, 10, 10, 150, 120, hwnd,
(HMENU) IDC_LIST, NULL, NULL);
for (int i = 0; i < 5; i++) {
SendMessageW(hwndList, LB_ADDSTRING, 0, (LPARAM) friends[i].name);
}
break;
Okay now that said and done.
How do I create a struct similar to the one above of friends and populate it with values from my sqlite3 table using the for loop?
I am still a novice in c/c++.
Since I could not uderstand the "ARRAYSIZE" used above I substituted it with an int 5 as below
It's most likely a macro, which can defined as:
#define ARRAYSIZE(array) (sizeof(array)/sizeof(array[0]))
This works fine for arrays that are defined in the same function where the macro is used. It won't work when the array is passed to a function. In a function that get an array as an argument, sizeof(array) will evaluate to the size of a pointer, not the size of the array.
E.g.
void foo(int array[])
{
int s = ARRAYSIZE(array); // Won't work here.
}
void foo()
{
int array[] = {1, 3, 20};
int s = ARRAYSIZE(array); // Will work here.
}
If you have the option, you should use std::vector. That will make coding a lot easier in the long run.
In C++11, you can define friends as:
std::vector<Friends> friends = {
{L"Lucy", L"waitress", 18},
{L"Thomas", L"programmer", 25},
{L"George", L"police officer", 26},
{L"Michael", L"producer", 38},
{L"Jane", L"steward", 28},
};

SQlite query - How do I retrieve multiple column data?

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