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.
Related
Using IXMLHttpRequest to fetch the data from the webservice.(actually, java servlets file). Using below code to send request and get response from webservice
IXMLHTTPRequestPtr pIXMLHTTPRequest = NULL;
CoInitialize(nullptr);
String usBuffer;
CATTry
{
hr = pIXMLHTTPRequest.CreateInstance("Msxml2.XMLHTTP.6.0");
if (SUCCEEDED(hr) && (pIXMLHTTPRequest != NULL))
{
hr = pIXMLHTTPRequest->open("POST", "URL", false);
if (SUCCEEDED(hr))
{
String lsusHeaderName;
lsusHeaderName.Append("Content-Type: application/x-www-form-urlencoded");
lsusHeaderName.Append("Accept: */*");
lsusHeaderName.Append("ticket: " + "ticketvalue");
lsusHeaderName.Append("Context: " + "securityvalue");
for (int i = 1; i <= ilsusHeaderName.Size(); i++)
{
BSTR bstrHeaderName;
BSTR bstrHeaderValue;
ilsusHeaderName[i].ConvertToBSTR(&bstrHeaderName);
hr = pIXMLHTTPRequest->setRequestHeader(bstrHeaderName, bstrHeaderValue);
}
String iusRequestParam = "Type=xxxx&Name=xxxxytr&Revision=09";
if (iusRequestParam != "")
{
hr = pIXMLHTTPRequest->send(iusRequestParam.ConvertToChar());
}
else
{
hr = pIXMLHTTPRequest->send();
}
{
struct __timeb64 t_stime;
struct __timeb64 t_ctime;
long lProcTime = 0;
memset(&t_stime, 0, sizeof(struct __timeb64));
memset(&t_ctime, 0, sizeof(struct __timeb64));
long nRedyState = READYSTATE_UNINITIALIZED;
_ftime64(&t_stime);
while (nRedyState != READYSTATE_COMPLETE)
{
_ftime64(&t_ctime);
lProcTime = (long)(1000 * (t_ctime.time - t_stime.time) + (t_ctime.millitm - t_stime.millitm));
if (lProcTime > HTTP_TIMEOUT)
{
break;
}
nRedyState = pIXMLHTTPRequest->readyState;
}
}
std::cout << "Request status : " << pIXMLHTTPRequest->status << std::endl;
if ((pIXMLHTTPRequest->status == 200))
{
_bstr_t spbstrResponse = pIXMLHTTPRequest->responseText;
BSTR bstrString = NULL;
bstrString = spbstrResponse.GetBSTR();
usBuffer.BuildFromBSTR(bstrString);
usOutput = usBuffer;
std::cout << "Output : " << usBuffer << std::endl;
bstrString = NULL;
}
else
{
std::cout << "Failed to send GET/POST Method." << std::endl;
}
}
else
{
hr = E_FAIL;
}
}
}
}
CATCatch(CATError, pError)
{
std::cout << "Failed to query XMLHTTP 6.0. Perhaps MSXML 6.0 is not exists." << std::endl;
}
CATEndTry;
This webservice call returns 200 status code, but response text is not good.It returns some html,javascript code as response text.
Expected result is one of the attribute value of the passed object. Example output:
{
"logy": "646F916E00005E78609AC53D0000974F"
}
Couldn't locate the issue in this code, why it returns html,javascript code.As I am new to WINAPI concepts, your help is highly appreciated.
Edit:
Checked webservice via postman and it returns expected result.
Header should be passed in this way. This solved the issue.
CATListOfCATUnicodeString lsusHeaderName;
CATListOfCATUnicodeString lsusHeaderValue;
lsusHeaderName.Append("Content-Type");
lsusHeaderValue.Append("application/x-www-form-urlencoded");
lsusHeaderName.Append("Login-ticket");
lsusHeaderValue.Append(usLoginTicket);
lsusHeaderName.Append("SecurityContext");
lsusHeaderValue.Append(usSecurityContext);
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.
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'm having problems getting this code to populate output parameters with MSSQL ODBC 10.0 client driver and 9.0 as well. I can confirm my binding direction is being set properly.
I am also calling SQLMoreResults, my stored procedure has NOCOUNT set and works fine under another ODBC-based library.
Unfortunately once I get to my execute statement, it succeeds but SQLMoreResults always says there are no more results, and I realize that those parameters aren't populated until it has gone through all of the result sets. The one output parameter I use is a bigint.
Unfortunately I don't know all of the intricacies of ODBC development and there must be something important I'm missing. I do try to reuse my statement handle but I reset it after my first call to SQLProcedureColumns and remove bound variables. Then I rebind.
Any ideas as to where I'm going astray?
bool ODBCConnection::Execute()
{
LLOG("Execute " << (void *)this << " " << (void *)session);
if(session->hstmt == SQL_NULL_HANDLE)
return false;
if(IsCurrent())
session->current = NULL;
session->FlushConnections();
last_insert_table.Clear();
number.Clear();
text.Clear();
time.Clear();
CParser p(statement);
/* parse for stored procedure */
bool isStoredProcedure = false;
if (p.Char('{'))
{
p.Spaces();
String procedure_name;
p.Id("?");
p.Id("=");
if (p.Id("call") || p.Id("CALL")) {
procedure_name = p.ReadId();
isStoredProcedure = true;
//Cout() << "Proc name: " << procedure_name << "\n";
}
SQLSetEnvAttr(session->henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, SQL_IS_INTEGER);
SDWORD cbValue5;
SDWORD cbValue4;
SQLSMALLINT ParameterType = SQL_PARAM_INPUT;
if (!IsOk(SQLProcedureColumns (
session->hstmt,
NULL,
0,
NULL,
0,
(SQLCHAR *)~procedure_name,
procedure_name.GetLength(),
NULL,
0
))) {
SQLFreeStmt(session->hstmt, SQL_CLOSE);
return false;
}
char parameter_name [20];
if (!IsOk(SQLBindCol(
session->hstmt,
4, // Column 4 returns column name
SQL_C_CHAR,
parameter_name,
sizeof(parameter_name),
&cbValue4
))) {
}
if (!IsOk(SQLBindCol(
session->hstmt,
5, // Column 5 returns whether parameter is input or output
SQL_C_SHORT,
&ParameterType,
0,
&cbValue5
))) {
}
int i = 0;
while (SQLFetch(session->hstmt) == SQL_SUCCESS) {
Param& p = param[i];
Cout() << ParameterType << "\n";
/*switch (ParameterType) {
case SQL_PARAM_INPUT:
case SQL_PARAM_OUTPUT:
case SQL_PARAM_INPUT_OUTPUT:
p.direction = ParameterType;
break;
case 5:
p.direction = SQL_PARAM_OUTPUT;
break;
default:
break;
}*/
if(ParameterType == 5)
p.direction = SQL_PARAM_OUTPUT;
else
p.direction = ParameterType;
i++;
}
SQLFreeStmt(session->hstmt, SQL_CLOSE);
SQLFreeStmt(session->hstmt, SQL_RESET_PARAMS);
SQLFreeStmt(session->hstmt, SQL_UNBIND);
}
if((p.Id("insert") || p.Id("INSERT")) && (p.Id("into") || p.Id("INTO")) && p.IsId())
last_insert_table = p.ReadId();
if(!IsOk(SQLPrepare(session->hstmt, (SQLCHAR *)~statement, statement.GetCount())))
return false;
parse = false;
bparam = param;
param.Clear();
for(int i = 0; i < bparam.GetCount(); i++) {
Param& p = bparam[i];
SQLSMALLINT DataType;
SQLULEN ParameterSize;
SQLSMALLINT DecimalDigits;
SQLSMALLINT Nullable;
Cout() << "Direction: " << p.direction << "\n";
Cout() << "Length: " << p.li << "\n";
if(!IsOk(SQLDescribeParam(session->hstmt, i + 1, &DataType, &ParameterSize, &DecimalDigits, &Nullable)))
return false;
if(!IsOk(SQLBindParameter(session->hstmt, i + 1, p.direction, p.ctype, DataType,
ParameterSize, DecimalDigits, (SQLPOINTER)~p.data, p.data.GetLength(),
&p.li)))
return false;
}
SQLSMALLINT ncol;
if(!isStoredProcedure)
{
if(!IsOk(SQLExecute(session->hstmt)) || !IsOk(SQLNumResultCols(session->hstmt, &ncol))) {
Cout() << "SQLExecute crashed\n";
SQLFreeStmt(session->hstmt, SQL_CLOSE);
return false;
}
}
else
{
Cout() << "statement: " << statement << "\n";
if(!IsOk(SQLExecute(session->hstmt))) {
Cout() << "SQLExecute crashed\n";
SQLFreeStmt(session->hstmt, SQL_CLOSE);
return false;
}
Cout() << "Calling SQLMoreResults...\n";
//SQLFreeStmt(session->hstmt, SQL_CLOSE);
int iReturn = SQLMoreResults(session->hstmt);
Cout() << "SQLMoreResults return code: " << iReturn << "\n";
while (iReturn == SQL_SUCCESS || iReturn == SQL_SUCCESS_WITH_INFO)
{
iReturn = SQLMoreResults(session->hstmt);
} ;
//SQLFreeStmt(session->hstmt, SQL_RESET_PARAMS);
//SQLFreeStmt(session->hstmt, SQL_UNBIND);
ncol = 0;
}
session->current = this;
info.Clear();
binary.Clear();
for(int i = 1; i <= ncol; i++) {
SQLCHAR ColumnName[256];
SQLSMALLINT NameLength;
SQLSMALLINT DataType;
SQLULEN ColumnSize;
SQLSMALLINT DecimalDigits;
SQLSMALLINT Nullable;
if(!IsOk(SQLDescribeCol(session->hstmt, i, ColumnName, 255, &NameLength, &DataType,
&ColumnSize, &DecimalDigits, &Nullable)))
return false;
binary.Add(false);
SqlColumnInfo& f = info.Add();
f.nullable = Nullable != SQL_NO_NULLS;
f.binary = false;
f.precision = DecimalDigits;
f.scale = 0;
f.width = ColumnSize;
f.name = (char *)ColumnName;
switch(DataType) {
case SQL_DECIMAL:
case SQL_NUMERIC:
case SQL_SMALLINT:
case SQL_INTEGER:
case SQL_REAL:
case SQL_FLOAT:
case SQL_DOUBLE:
case SQL_BIT:
case SQL_TINYINT:
f.type = DOUBLE_V;
break;
case SQL_BIGINT:
f.type = INT64_V;
break;
case SQL_TYPE_DATE:
case SQL_TYPE_TIMESTAMP:
f.type = TIME_V;
break;
case SQL_BINARY:
case SQL_VARBINARY:
case SQL_LONGVARBINARY:
f.type = STRING_V;
f.binary = true;
binary.Top() = true;
break;
default:
f.type = STRING_V;
break;
}
}
SQLLEN rc;
SQLRowCount(session->hstmt, &rc);
rowsprocessed = rc;
return true;
}
Have you tried turning on SQLProfile? Maybe fire that up and watch the actual SQL hitting the database then take those SQL statements and run directly in SSMS to confirm it's doing what you expect.
Never mind, I was being stupid. My parameter binding was actually bound to a copy of the data and not the actual data itself. Thanks to everyone who tried to help.
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;
}