I'm writing a program using C++ and the MySQL C API (version 5.1.31 ubuntu2). However, if the query is UPDATE then I get a Segmentation Fault error when executing the line "RowsReturned = mysql_num_rows( Result );".
//this code snippet contains only the relevant code
MYSQL_RES *Result;
long RowsReturned;
MYSQL_RES *MYSQLDB::RunQuery( const char* Query )
{
if( mysql_query( Connection, Query) )
{
std::cout << "Error: " << mysql_error( Connection );
exit(1);
}
Result = mysql_store_result( Connection );
RowsReturned = mysql_num_rows( Result );
return Result;
}
Compiled using g++ 4.3.3 (g++ test.cpp -I/usr/include/mysql -L/usr/lib/mysql -lmysqlclient_r -o Test)
Thanks in advance.
//this snippet contains the entire class code
class MYSQLDB
{
public:
void Connect( const char* DB );
MYSQL_RES *RunQuery( const char* Query );
long NumRows();
void Close();
MYSQL_ROW GetRow();
private:
MYSQL *Connection;
MYSQL_RES *Result;
MYSQL_ROW Row;
long RowsReturned;
};
void MYSQLDB::Connect( const char* DB )
{
Connection = mysql_init( NULL );
if( Connection == NULL )
{
std::cout << "Error: " << mysql_error( Connection );
exit( 1 );
}
if ( mysql_real_connect( Connection, "localhost", "root", "password", DB, 0, NULL, 0 ) == NULL)
{
std::cout << "Error: " << mysql_error( Connection );
exit(1);
}
}
MYSQL_RES *MYSQLDB::RunQuery( const char* Query )
{
if( mysql_query( Connection, Query) )
{
std::cout << "Error: " << mysql_error( Connection );
exit(1);
}
Result = mysql_store_result( Connection );
RowsReturned = (long)mysql_num_rows( Result ); //ERROR!!!!!!!
return Result;
}
long MYSQLDB::NumRows()
{
return RowsReturned;
}
void MYSQLDB::Close()
{
mysql_free_result( Result );
mysql_close( Connection );
}
MYSQL_ROW MYSQLDB::GetRow()
{
return mysql_fetch_row( Result );
}
From the mysql documentation:
mysql_store_result() returns a null pointer if the statement didn't return a result set (for example, if it was an INSERT statement).
You are updating so you have a NULL as results.
Try something like this:
Result = mysql_store_result( Connection );
if (Result) {
RowsReturned = mysql_num_rows( Result );
} else {
RowsReturned = 0;
}
Related
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.
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 am trying to store the public key (PK) and signature (generated by crypto++ library) to sqlite database. I first encoded the keys as:
pk[node].BEREncode(CryptoPP::StringSink(pkstring).Ref());
and then store them in the database. But as the PK and signature have special characteres, they are not stored correctly into the database. What I used for storing is:
char * PK = (char*) malloc (BC.trans[l].PK.length()+1); //use malloc to manually manage the memory
std::strcpy(PK,BC.trans[l].PK.c_str());
char *zSQL = sqlite3_mprintf("INSERT INTO BC (PK VALUES ('%q');", PK);
rc = sqlite3_exec(db, zSQL, callback, (void*)data, &zErrMsg);// executes the command in sqlite and returns the status (whether stroing was successful or not.
free(PK);
The string should be copied to the char, and the issue happens exactly here in copying the content into the char. I think its because the string has special characters. For hash I have the same follow but it works perfectly fine. Only for PK and signature. I used memcpy and strncpy as well. Nothing works for this and still the keys are not stored correctly in the database.
Any thoughts?
I've updated with the full code. Now I can store the PK and its seems to be fine, but when I ask the database to remove a bunch of records, nothing happens. That means something is wrong again.
Here is the code:
int Network_Nodes =10;
int Block_Size=10;
int BC_lenght=0;
int Fin_BC_size =50;
std::vector<CryptoPP::RSA::PrivateKey> prk;
std::vector<CryptoPP::RSA::PublicKey> pk;
std::vector<std::string> prev_t;
struct Block {
std::string block_hash;
std::string block_num;
struct transactions {
std::string TransactionID;
std::string previousTransactionID;
std::string PK;
std::string Sign;
};
std::vector<transactions> trans;
} BC;
int generatekey()
{
for (int i=0;i<Network_Nodes;i++)
{
CryptoPP::AutoSeededRandomPool rng;
CryptoPP::InvertibleRSAFunction param;
param.GenerateRandomWithKeySize(rng,3072);
CryptoPP::RSA::PrivateKey privatekey(param);
CryptoPP::RSA::PublicKey publickey (param);
prk.push_back(privatekey);
pk.push_back(publickey);
}
return 0;
}
///////////////////////////////////////////////////////////////
void initialization()
{
for (int i=0;i<=Network_Nodes;i++)
{
prev_t.push_back("NULL");
}
//Creating database
sqlite3 *db;
char *zErrMsg = 0;
int rc;
char *sql;
const char* data = "Callback function called";
/* Open database */
rc = sqlite3_open("RemovableBC.db", &db);
if( rc ) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
return;
} else {
// fprintf(stdout, "Opened database successfully\n");
}
sql = "DROP TABLE BC";
rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
/* Create a table in the data base to be used for storing data. Create SQL statement */
sql = "CREATE TABLE BC(" \
"T_ID TEXT ," \
"P_T_ID TEXT ," \
"PK BLOB ," \
"Signature BLOB ," \
"Block_ID TEXT );";
/* Execute SQL statement */
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, "Table created successfully\n");
}
sqlite3_close(db);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
void fill_the_block(std::string block_content)
{
const char* data = "Callback function called";
SHA256 hash;
string digest;
sqlite3 *db=NULL;
char *sql;
char *zErrMsg = 0;
int rc;
char sqlStr[2000];
/* Open database */
rc = sqlite3_open("RemovableBC.db", &db);
if( rc ) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
return;
} else {
// fprintf(stdout, "Opened database successfully\n");
}
StringSource s(block_content, true, new HashFilter(hash, new HexEncoder(new StringSink(digest))));
BC.block_hash=digest;
for (int l=0 ; l<Block_Size ; l++)
{
char *begin = sqlStr;
char *end = begin + sizeof(sqlStr);
std::fill(begin, end, 0);
char *zSQL = sqlite3_mprintf("INSERT INTO BC ( T_ID , P_T_ID , PK , Signature , Block_ID ) VALUES ('%q','%q','%q','%q','%q');", BC.trans[l].TransactionID.c_str() ,BC.trans[l].previousTransactionID.c_str() ,BC.trans[l].PK.c_str() ,BC.trans[l].Sign.c_str(),BC.block_hash.c_str());
rc = sqlite3_exec(db, zSQL, callback, (void*)data, &zErrMsg);
sqlite3_free(zSQL);
if( rc != SQLITE_OK ) {
fprintf(stderr, "SQL error in populating : %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
}
sqlite3_close(db); //We close the connection we have with the database
}
///////////////////////////////////////////////////////////////////////////
void populate_bc ()
{
int generated_blocks=0;
int stored_trans_in_block=0;
int node=0;
std::string block_content,trans_cont;
std::string pkstring;
std::string signature;
std::string mes ="This message is going to be singed";
while (BC_lenght <=Fin_BC_size )
{
if (generated_blocks <= 10){
if (node >= Network_Nodes)
{
node=0; //cout << "step 4" <<endl;
}
if (stored_trans_in_block >= Block_Size)
{
cout << "block size is "<< BC_lenght<<endl;
fill_the_block(block_content);
BC_lenght++;
block_content.clear();
stored_trans_in_block=0;
for(int o=0; o<stored_trans_in_block;o++)
{
BC.trans[o] = {};
}
BC.trans = {};
BC ={};
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (prev_t[node]=="NULL")
{
// cout << "We are populating on behafe of "<< node<< endl;
trans_cont="";
BC.trans.push_back(Block::transactions());
BC.trans[stored_trans_in_block].previousTransactionID ="NULL";
// byte public_key[] = pk[node];
std::string endoced_pub;
Base64Encoder pubKeySink(new StringSink(endoced_pub));
pk[node].DEREncode(pubKeySink);
pk[node].BEREncode(CryptoPP::StringSink(pkstring).Ref());
BC.trans[stored_trans_in_block].PK=endoced_pub;
CryptoPP::AutoSeededRandomPool rng;
CryptoPP::RSASSA_PKCS1v15_SHA_Signer signer(prk[node]);
CryptoPP::StringSource ss1(mes, true,
new CryptoPP::SignerFilter(rng, signer,
new CryptoPP::StringSink(signature)
) // SignerFilter
); // StringSource
// cout << "step 1" <<endl;
BC.trans[stored_trans_in_block].Sign=signature;
trans_cont = "NULL" + pkstring + signature;
SHA256 hash;
string digest;
StringSource s(trans_cont, true, new HashFilter(hash, new HexEncoder(new StringSink(digest))));
// cout << "step 2" <<endl;
BC.trans[stored_trans_in_block].TransactionID=digest;
prev_t[node] =digest; // This keeps the previous transaction of each node in an array and thus we can later use to populate the BC
block_content+=digest; // This is to calculate the ID of the block which is the hash of all TIDs (hash of hash of all trnasctions)
node++;
stored_trans_in_block++;
// cout << "step 3" <<endl;
}//end of if (prev_t[node]=="NULL")
else
{// cout << "step 6" <<endl;
trans_cont="";
BC.trans.push_back(Block::transactions());
BC.trans[stored_trans_in_block].previousTransactionID =prev_t[node];
std::string endoced_pub;
Base64Encoder pubKeySink(new StringSink(endoced_pub));
pk[node].DEREncode(pubKeySink);
// pubKeySink.MessageEnd();
// pk[node].BEREncode(CryptoPP::StringSink(pkstring).Ref());
BC.trans[stored_trans_in_block].PK = endoced_pub;
// BC.trans[stored_trans_in_block].PK= pk[node];
CryptoPP::AutoSeededRandomPool rng;
CryptoPP::RSASSA_PKCS1v15_SHA_Signer signer(prk[node]);
CryptoPP::StringSource ss1(mes, true,
new CryptoPP::SignerFilter(rng, signer,
new CryptoPP::StringSink(signature)
) // SignerFilter
); // StringSource
BC.trans[stored_trans_in_block].Sign=signature;
trans_cont = prev_t[node] + pkstring + signature;
SHA256 hash;
string digest;
StringSource s(trans_cont, true, new HashFilter(hash, new HexEncoder(new StringSink(digest))));
BC.trans[stored_trans_in_block].TransactionID=digest;
prev_t[node] =digest;
block_content+=digest;
node++;
stored_trans_in_block++;
}
generated_blocks++;
}
else
{
generated_blocks=0;
}
}
}
//////////////////////////////////////////////////////////////////////////////////////
void remove_node (int i)
{
std::string search_node; //This is the ID of the transaction that we need to search for in the BC,
sqlite3 *db;
std::string endoced_pub;
Base64Encoder pubKeySink(new StringSink(endoced_pub));
pk[i].DEREncode(pubKeySink);
// pubKeySink.MessageEnd();
char *sql;
int rc;
char *zErrMsg = 0;
const char* data = "Callback function called";
/* Open database */
rc = sqlite3_open("RemovableBC.db", &db);
if( rc ) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
return;
} else {
// fprintf(stderr, "Opened database successfully\n");
}
/* Execute SQL statement */
// char *zSQL = sqlite3_mprintf("DELETE from BC");
char * Search_NODE = (char*) malloc (endoced_pub.length()+1);
std::strcpy(Search_NODE,endoced_pub.c_str());
std::strcpy(Search_NODE,search_node.c_str());
// char *zSQL = sqlite3_mprintf("DELETE from BC where PK = '%q';", Search_NODE);
char *zSQL = sqlite3_mprintf("UPDATE BC set Signature = null and PK = null where PK = '%q';", endoced_pub.c_str());
rc = sqlite3_exec(db, zSQL, callback, (void*)data, &zErrMsg);
cout<< endoced_pub.c_str()<<endl;
if( rc != SQLITE_OK ) {
fprintf(stderr, "SQL error in removing a record: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
// fprintf(stdout, "Operation done successfully\n");
}
sqlite3_close(db);
free(Search_NODE);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
int main() {
auto initial = std::chrono::high_resolution_clock::now();
generatekey();
cout << "Keys are generated for " <<Network_Nodes << " nodes..." << endl;
clock_t begin_block_pup = clock();
initialization();
cout << "Initializing previous transaction vector..." << endl;
//clock_t ti =clock();
//double time = double (ti)/CLOCKS_PER_SEC;
clock_t ti2 =clock();
double time2 = double (ti2)/CLOCKS_PER_SEC;
//cout << "second time is " << done <<endl;
populate_bc();
cout << "BC has been populated with "<< BC_lenght <<" blocks..." << endl;
return 0;
}
The problem with your system, is that you need to store the PK data as either a BLOB or encode it into 7 bit ASCII.
Sqlite has some internationalization support, and may convert raw binary data stored as a string into a different form. (e.g. UTF-8 conversion)
So either use a blob as in the answer to this question SO : sqlite blob,
or first convert the bytes to 7 bit ascii (e.g. b64 encode), then store the data.
Remembering you would need to b64 decode the results afterwards.
I'm using dev c++, Wininet lib to download a file from web. I'm trying to change the referer or user agent. I use this code, it downloads successfully, but I don't know how to change http headers. Thanks.
#include <Windows.h>
#include <Wininet.h>
#include <iostream>
#include <fstream>
namespace {
::HINTERNET netstart ()
{
const ::HINTERNET handle =
::InternetOpenW(0, INTERNET_OPEN_TYPE_DIRECT, 0, 0, 0);
if ( handle == 0 )
{
const ::DWORD error = ::GetLastError();
std::cerr
<< "InternetOpen(): " << error << "."
<< std::endl;
}
return (handle);
}
void netclose ( ::HINTERNET object )
{
const ::BOOL result = ::InternetCloseHandle(object);
if ( result == FALSE )
{
const ::DWORD error = ::GetLastError();
std::cerr
<< "InternetClose(): " << error << "."
<< std::endl;
}
}
::HINTERNET netopen ( ::HINTERNET session, ::LPCWSTR url )
{
const ::HINTERNET handle =
::InternetOpenUrlW(session, url, 0, 0, 0, 0);
if ( handle == 0 )
{
const ::DWORD error = ::GetLastError();
std::cerr
<< "InternetOpenUrl(): " << error << "."
<< std::endl;
}
return (handle);
}
void netfetch ( ::HINTERNET istream, std::ostream& ostream )
{
static const ::DWORD SIZE = 1024;
::DWORD error = ERROR_SUCCESS;
::BYTE data[SIZE];
::DWORD size = 0;
do {
::BOOL result = ::InternetReadFile(istream, data, SIZE, &size);
if ( result == FALSE )
{
error = ::GetLastError();
std::cerr
<< "InternetReadFile(): " << error << "."
<< std::endl;
}
ostream.write((const char*)data, size);
}
while ((error == ERROR_SUCCESS) && (size > 0));
}
}
int main ( int, char ** )
{
const ::WCHAR URL[] = L"http://google.com";
const ::HINTERNET session = ::netstart();
if ( session != 0 )
{
const ::HINTERNET istream = ::netopen(session, URL);
if ( istream != 0 )
{
std::ofstream ostream("googleindex.html", std::ios::binary);
if ( ostream.is_open() ) {
::netfetch(istream, ostream);
}
else {
std::cerr << "Could not open 'googleindex.html'." << std::endl;
}
::netclose(istream);
}
::netclose(session);
}
}
#pragma comment ( lib, "Wininet.lib" )
You pass user agent string as the first parameter to InternetOpen
Use HttpOpenRequest and HttpSendRequest in place of InternetOpenUrl. Referer string is the 5th parameter to HttpOpenRequest
The third parameter of InternetOpenUrl is lpszHeaders [in] (from MSDN):
A pointer to a null-terminated string that specifies the headers to be sent to the HTTP server. For more information, see the description of the lpszHeaders parameter in the HttpSendRequest function.
You can set Referer and User agent like that:
LPWSTR headers = L"User-Agent: myagent\r\nReferer: my.referer.com\r\n\r\n\r\n";
//and then call
::InternetOpenUrlW(session, url, headers, -1, 0, 0);
You must separate every header with \r\n and close the block with \r\n\r\n
I am writing a program that uses MySQLe as embedded backend. The database library is owned by an object called "Domain". This Domain object runs within the main thread.
The program launches another thread running a XML-RPC server (boost::thread and xmlrpc_c::serverAbyss). It is linked to the Domain object.
When the XML-RPC server makes the Domain object execute an SQL query the program crashes:
Program received signal: “EXC_BAD_ACCESS”.
[Switching to process 73191]
[Switching to process 73191]
Xcode could not locate source file: regex.cpp (line: 74)
When the master thread calls Domain object's method that executes SQL queries the program still runs.
/*
* Ports listening
*
* - create a Rpc_Server object
* - create a dedicated thread
*/
Rpc_Server server(&domain, &conf_params, &router);
boost::thread server_thread(boost::bind(&Rpc_Server::run, &server)); // This thread makes the server crash
/*
* Domain routine
*
* - Check for ready jobs every minute
*/
while (1) {
v_jobs jobs = domain.get_ready_jobs(conf_params.get_param("node_name")); // This method does NOT make the server crash
sleep(60);
}
Both the Domain object's methods and the Database object's methods lock a mutex to avoid multi access.
bool Mysql::execute(const std::string* query) {
MYSQL_RES* res;
MYSQL_ROW row;
if ( query == NULL )
return false;
this->updates_mutex.lock();
std::cout << query->c_str() << std::endl;
if ( mysql_query(this->mysql, query->c_str()) != 0 ) {
std::cerr << query << std::endl << mysql_error(this->mysql);
UNLOCK_MUTEX;
return false;
}
res = mysql_store_result(this->mysql);
if (res)
while ( ( row = mysql_fetch_row(res) ) )
for ( uint i=0 ; i < mysql_num_fields(res) ; i++ )
std::cout << row[i] << std::endl;
else
if ( mysql_field_count(this->mysql) != 0 ) {
std::cerr << "Erreur : " << mysql_error(this->mysql) << std::endl;
mysql_free_result(res);
this->updates_mutex.unlock();
return false;
}
mysql_free_result(res);
this->updates_mutex.unlock();
return true;
}
bool Domain::add_node(const std::string* running_node, const std::string* n, const int* w) {
std::string query;
this->updates_mutex.lock();
query = "START TRANSACTION;";
if ( this->database.execute(&query) == false ) {
this->updates_mutex.unlock();
return false;
}
query = "REPLACE INTO node (node_name,node_weight) VALUES ('";
query += n->c_str();
query += "','";
query += boost::lexical_cast<std::string>(*w);
query += "');";
if ( this->database.execute(&query) == false ) {
query = "ROLLBACK;";
this->database.execute(&query);
this->updates_mutex.unlock();
return false;
}
query = "COMMIT;"
if ( this->database.execute(&query) == false ) {
this->updates_mutex.unlock();
return false;
} else
this->updates_mutex.unlock();
return true;
}
The MySQLe is created there:
bool Mysql::prepare(const std::string* node_name, const std::string* db_skeleton) {
static char* server_args[] = {"this_program","--datadir=."};
static char* server_groups[] = {"embedded","server","this_program_SERVER",(char *)NULL};
std::string query("CREATE DATABASE IF NOT EXISTS ");
// DB init
if ( mysql_library_init(sizeof(server_args) / sizeof(char *), server_args, server_groups) )
std::cerr << "could not initialize MySQL library" << std::endl;
std::cout << "mysql init..." << std::endl;
if ( (this->mysql = mysql_init(NULL)) == NULL )
std::cerr << mysql_error(this->mysql) << std::endl;
if ( ! mysql_thread_safe() ) {
std::cerr << "MySQL is NOT theadsafe !" << std::endl;
return false;
}
mysql_options(this->mysql, MYSQL_READ_DEFAULT_GROUP, "embedded");
mysql_options(this->mysql, MYSQL_OPT_USE_EMBEDDED_CONNECTION, NULL);
mysql_real_connect(this->mysql, NULL, NULL, NULL, NULL, 0, NULL, 0);
// Creates the schema
query += this->translate_into_db(node_name);
query += ";";
if ( this->execute(&query) == false )
return false;
// Creates the schema
query = "CREATE SCHEMA IF NOT EXISTS ";
query += this->translate_into_db(node_name);
query += " DEFAULT CHARACTER SET latin1;";
this->execute(&query);
// Uses it
query = "USE " + this->translate_into_db(node_name) + ";";
this->execute(&query);
// Loads the skeleton from file
return this->load_file(db_skeleton->c_str());
}
Am I wrong somewhere?
Do you have an example to show me?
I found the solution to my problem. Each thread needs to initialize the MySQL environment. That is to say execute some mysql_* functions.
Here are the modified / new methods :
bool Mysql::atomic_execute(const std::string* query) {
MYSQL_RES* res;
MYSQL_ROW row;
boost::regex empty_string("^\\s+$", boost::regex::perl);
if ( query == NULL )
return false;
if ( query->empty() == true or boost::regex_match(*query, empty_string) == true ) {
std::cerr << "Error : query is empty !" << std::endl;
return false;
}
this->updates_mutex.lock();
if ( mysql_query(this->mysql, query->c_str()) != 0 ) {
std::cerr << query << std::endl << mysql_error(this->mysql);
this->updates_mutex.unlock();;
return false;
}
res = mysql_store_result(this->mysql);
if (res)
while ( ( row = mysql_fetch_row(res) ) )
for ( uint i=0 ; i < mysql_num_fields(res) ; i++ )
std::cout << row[i] << std::endl;
else
if ( mysql_field_count(this->mysql) != 0 ) {
std::cerr << "Erreur : " << mysql_error(this->mysql) << std::endl;
mysql_free_result(res);
this->updates_mutex.unlock();
return false;
}
mysql_free_result(res);
this->updates_mutex.unlock();
return true;
}
bool Mysql::standalone_execute(const v_queries* queries) {
MYSQL* local_mysql = this->init();
std::string query = "START TRANSACTION;";
if ( this->atomic_execute(&query) == false ) {
mysql_close(local_mysql);
return false;
}
BOOST_FOREACH(std::string q, *queries) {
std::cout << q.c_str() << std::endl;
if ( this->atomic_execute(&q) == false ) {
query = "ROLLBACK";
this->atomic_execute(&query);
mysql_close(local_mysql);
return false;
}
}
query = "COMMIT";
if ( this->atomic_execute(&query) == false ) {
mysql_close(local_mysql);
return false;
}
mysql_close(local_mysql);
return true;
}
MYSQL* Mysql::init() {
MYSQL* local_mysql;
local_mysql = mysql_init(this->mysql);
mysql_options(this->mysql, MYSQL_READ_DEFAULT_GROUP, "embedded");
mysql_options(this->mysql, MYSQL_OPT_USE_EMBEDDED_CONNECTION, NULL);
mysql_real_connect(local_mysql, NULL, NULL, NULL, NULL, 0, NULL, 0);
return local_mysql;
}
The atomic_execute method is used to send single queries to the server.
The standalone_execute method initializes a connection and a transaction, then it sends the whole queries to the server using atomic_execute.
I do not know if a ROLLBACK is useful in case of COMMIT's failure...
The code might need some improvements but it works.