Is this a safe implementation of a generic SQL execute function? - c++

Suppose the following implementation of a generic function to query a database using MySQL:
bool execute(const sql::SQLString query, std::vector<sql::ResultSet*> &results)
{
// ConnectionPool holds premade connections to the database
sql::Connection* conn = ConnectionPool::getInstance()::getConnection();
std::unique_ptr<sql::Statement> stmt;
bool success = false;
try
{
stmt.reset( conn->createStatement() );
stmt->execute( query );
do
{
results.push_back( stmt->getResultSet() );
} while ( stmt->getMoreResults() )
success = true;
}
catch ( ... )
{
// Other catch() statements are not a part of this question
std::cerr << "Exception caught!" << std::endl;
success = false;
}
conn->commit();
ConnectionPool::getInstance()::returnConnection( conn );
return success;
}
According to this example for retrieving results from a query, the ResultSet needs to be explicitly deleted. In regard to the implementation above, does this mean the vector of ResultSet pointers is safe to use (i.e., the objects they point to are not deleted by the deletion of the creating Statement)?
Also, am I doing anything unspeakably evil with this implementation?

The following implementation is safer. This is because the ResultSet is tied to the Connection, rather than the Statement. If the connection is closed, destroyed, or used for another purpose prior to the results being read, the ResultSet objects may become defunct and cause an abort condition. It is safer to let the calling function handle the Connection on its own.
Note that a better implementation would be for the sql::Connection* parameter to be std::unique_ptr< sql::Connection >&, but my personal needs dictate this cannot be done at this time.
bool execute(sql::Connection *conn, const sql::SQLString query, std::vector< std::unique_ptr<sql::ResultSet> > &results)
{
std::unique_ptr<sql::Statement> stmt;
bool success = false;
try
{
stmt.reset( conn->createStatement() );
stmt->execute( query );
do
{
results.emplace_back( stmt->getResultSet() );
} while ( stmt->getMoreResults() )
success = true;
}
catch ( ... )
{
// Other catch() statements are not a part of this question
std::cerr << "Exception caught!" << std::endl;
success = false;
}
conn->commit();
return success;
}

Related

How to declare an empty rowset properly with SOCI?

Imagine that I have the following function. In case of invalid parameters or exception, the function has to exit with an empty rowset.
rowset<row> SelectAllFromTable(string tableName)
{
session sql(odbc, "...");
// if parameters are not valid -> return empty rowset<row>
if (tableName == "")
{
// query that returns 0 result
rowset<row> res = (sql.prepare << "SELECT ID FROM T1 WHERE ID = -9999");
return res;
}
string query = "SELECT * FROM " + tableName;
try
{
rowset<row> rs = sql.prepare << query;
return rs;
}
catch (exception const &e)
{
cerr << "Error: " << e.what() << endl;
// query that returns 0 result
rowset<row> res = (sql.prepare << "SELECT ID FROM T1 WHERE ID = -9999");
return res;
}
// query that returns 0 result
rowset<row> res = (sql.prepare << "SELECT ID FROM T1 WHERE ID = -9999");
return res;
}
The solution I wrote above works but my question is : Is there a better way to return an empty rowset with SOCI ?
Since the documentation hasn't much to offer to this I looked into the rowset Header: There is no default constructor for it and no public method to set the iterators, ergo you can't get an empty rowset by yourself.
Despite why don't you use exceptions which are just perfect for that case. Just don't catch the soci_error exception, then the caller SelectAllFromTable could catch it. This would have many advantages:
The caller would know if there is really no data in the table or there is no table
The caller could know why he can't use the table (misspelled or security reasons)
The caller could know if there are other troubles and take action or if not, rethrow it, so his caller might can.

Is the return of last_insert_id always correct?

If I have the 2 following functions:
int AccessDb::InsertColValue(string tableName, string col, string val)
{
try
{
sql::Statement *stmt;
bool ret;
if ((nomTable != "") && (col != "") && (val != ""))
{
string query = "INSERT INTO " + tableName + "(" + col + ") values (";
query += val + ");";
stmt = con->createStatement();
ret = stmt->execute(query);
}
delete stmt;
return 0;
}
catch (sql::SQLException &e)
{
return -1;
}
}
and
long AccessDb::LastInsertId()
{
try
{
sql::Statement *stmt;
sql::ResultSet *res;
string query = "SELECT LAST_INSERT_ID() AS LAST_ID";
stmt = con->createStatement();
res = stmt->executeQuery(query);
delete stmt;
long lastId;
while (res->next())
{
lastId = res->getInt("LAST_ID");
}
return lastId;
}
catch (sql::SQLException &e)
{
return -1;
}
}
Can I be sure that the return of LastInsertId() will always give me the correct id if I write the following lines and if the id is auto generated by the database?
AccessDb adb; // initialize the connexion with the db
int ret = adb.InsertColValue("people", "name", "John");
if (ret == 0)
long lastId = adb.LastInsertId();
If the previous code is called somewhere else at the same time, can I have a wrong value in my lastId variable ? If yes, do I have to use locks and unlocks on my table to avoid that or another solution ?
Here's what the docs says:
The ID that was generated is maintained in the server on a
per-connection basis. This means that the value returned by the
function to a given client is the first AUTO_INCREMENT value generated
for most recent statement affecting an AUTO_INCREMENT column by that
client. This value cannot be affected by other clients, even if they
generate AUTO_INCREMENT values of their own. This behavior ensures
that each client can retrieve its own ID without concern for the
activity of other clients, and without the need for locks or
transactions.
So, unless your own code on the client is sharing a connection between several threads (Which it looks like you're not, since there are no mutexes or locks in your code) you can be sure SELECT LAST_INSERT_ID() isn't mixed up with any other connection or client.
I can't find the docs for the C++ mysql library but verify what the return value of ret = stmt->execute(query); in your InsertColValue() function means, such that you're sure the only possible way that you fail to insert anything is when an exception is thrown.

NPAPI: need to RetainObject() a handler twice, otherwise SIGBUS

In my NPAPI plugin, some of the objects have an "onEvent" property that is readable and writeable, and which is called on certain events.
What I have in my Javascript code will look like this:
myObject.onEvent = function( event ) {
console.log("Event: " + event );
}
// if I put this next line, the next call to the 'onEvent' handler will SIGBUS
// when there's no RetainObject() in the getter.
console.log("Event handler : " + myObject.onEvent);
And on the C++ side of the plugin, I have that kind of code:
bool MyPluginObject::getOnEvent(NPIdentifier id, NPVariant *result)
{
if( _onEvent )
{
OBJECT_TO_NPVARIANT( _onEvent, *result);
NPN_RetainObject( _onEvent ); // needed ???? why??
}
else
VOID_TO_NPVARIANT(*result);
return true;
}
bool MyPluginObject::setOnEvent( NPIdentifier id, const NPVariant *value )
{
if ( value && NPVARIANT_IS_OBJECT( *value ) )
{
if( _onEvent != NULL )
{
// release any previous function retained
NPN_ReleaseObject( _onEvent );
}
_onEvent = NPVARIANT_TO_OBJECT( *value );
NPN_RetainObject( _onEvent ); // normal retain
return true;
}
return false;
}
void MyPluginObject::onEvent(void)
{
NPVariant event = [...];
if ( _onEvent!= NULL )
{
NPVariant retVal;
bool success = NPN_InvokeDefault( _Npp, _onEvent, &event, 1, &retVal );
if( success )
{
NPN_ReleaseVariantValue(&retVal);
}
}
}
What's strange is that I've been struggling with a SIGBUS problem for a while, and once I added the NPN_RetainObject() in the getter, as you can see above, everything went fine.
I didn't find in the statement that it is needed in the Mozilla doc, neither in Taxilian's awesome doc about NPAPI.
I don't get it: when the browser requests a property that I've retained, why do I have to retain it a second time?
Should I maybe retain the function when calling InvokeDefault() on it instead? But then, why?? I already stated that I wanted to retain it.
Does getProperty() or InvokeDefault() actually does an NPN_ReleaseObject() without telling me?
You always have to retain object out-parameters with NPAPI, this is not specific to property getters.
In your specific case the object may stay alive anyway, but not in the general case:
Consider returning an object to the caller that you don't plan on keeping alive from your plugin. You have to transfer ownership to the caller and you can't return objects with a retain count of 0.

MySQL C++ query Access Violation

I have a DLL where I make a connection to a MySQL database. I have Open(), Close(), Update(), and Find() functions. The Update() functions inserts data into a table and this works just fine. The Find() function however is just doing a simple query against the same table. When I call the resultset getXX() function I'm getting an Access Violation error and I can't figure out why. What am I missing? Note the query is a view and not a direct table but I wouldn't think that would matter.
MT4_EXPFUNC int __stdcall Find(char* pair)
{
try
{
sql::Statement *stmt;
sql::ResultSet* res;
string p = pair;
string buysell = "";
string qry = "select * from forex.GPBUSD_CURRENT_PRICE";
stmt = _connection->createStatement();
res = stmt->executeQuery(qry);
// if we have a record it means we have a trade chance
if(res->next())
{
buysell = res->getString(1); // ACCESS VIOLATION ERROR HERE
}
// clean up
delete res;
delete stmt;
if(buysell == "SELL")
return 1;
else if(buysell == "BUY")
return 2;
else
return 0;
}
catch(sql::SQLException &e)
{
return -1;
}
}
have you considered if getString is a zero indexed method? Or if it's a null datatype your trying to access? or shoot just because res->next() works and doesn't crash, doesn't mean that res is a valid ptr.

MySQL Transactions using C++?

How would I go about wrapping an amount of queries in a transaction in C++? I'm working on Ubuntu 10, using this file:
#include "/usr/include/mysql/mysql.h"
with C++ to interact with a MySQL database.
EDIT: Right now I'm running queries through a small wrapper class, like so:
MYSQL_RES* PDB::query(string query)
{
int s = mysql_query(this->connection, query.c_str());
if( s != 0 )
{
cout << mysql_error(&this->mysql) << endl;
}
return mysql_store_result(this->connection);
}
MYSQL_ROW PDB::getarray(MYSQL_RES *res)
{
return mysql_fetch_row( res );
}
// example one
MYSQL_RES res = db->query( "SELECT * FROM `table` WHERE 1" );
while( MYSQL_ROW row = db->getarray( res ) )
{
cout << row[0] << endl;
}
If you use MySQL++, you get RAII transaction behavior with Transaction objects:
mysqlpp::Connection con( /* login parameters here */ );
auto query = con.query("UPDATE foo SET bar='qux' WHERE ...");
mysqlpp::Transaction trans(con);
if (auto res = query.execute()) {
// query succeeded; optionally use res
trans.commit(); // commit DB changes
}
// else, commit() not called, so changes roll back when
// 'trans' goes out of scope, possibly by stack unwinding
// due to a thrown exception.
You could always just run START TRANSACTION / COMMIT / ... manually.
Another way would be to create a wrapper class which runs START TRANSACTION in the constructor, provides commit/rollback functions and, depending on your use case, does a rollback upon destruction.