sqlite query not prepared row insterted but empty values - c++

I am writing a function to prepare a sql query and execute it against a sqlite db. I am using a query to insert values in to a table, the query looks like this
"insert into files values (?, ?, ?, ?, ?, 0)";
A row is inserted but all values for text fields are empty except the last field which is 0. The first 5 fields are of type TEXT
// If I hard code a value for value.data() then the row is inserted correctly with my hardcoded data, the exact line is below
status = sqlite3_bind_text(ppStmt, index, /* if I hardcode it works*/ value.data(), -1, SQLITE_STATIC);
The full function is shown below.The list is created on stack by the calller, I am not sure why it is not replacing question marks with my string args, hardcoded ones work though.. no error codes are returned
//db is already open before I call this
void MediaCache::prepareAndExecuteQuery(string query, list<string> args)
{
sqlite3_stmt *ppStmt = 0;
const char **pzTail = 0;
int status = 0;
if( sqlite3_prepare_v2(db, query.data(), query.length(), &ppStmt, pzTail) != SQLITE_OK )
{
string error = sqlite3_errmsg(db);
//throw an exception
}
if(ppStmt)
{
list<string>::iterator current = args.begin();
int index = 1;
for(current = args.begin() ; current != args.end(); current++)
{
string value = *current;
status = sqlite3_bind_text(ppStmt, index, value.data(), -1, SQLITE_STATIC);
if(status != SQLITE_OK)
{
//log error;
}
index++;
}
status = sqlite3_step(ppStmt);
status = sqlite3_finalize(ppStmt);
//sqlite3_exec(db, "COMMIT", NULL, NULL, NULL);
}
else
{
//ppStmt is null
//throw an exception
}
}

I suspect the problem is here:
string value = *current;
status = sqlite3_bind_text(ppStmt, index, value.data(), -1, SQLITE_STATIC);
In this case value goes out of scope before the execution of the statement takes place. So the pointers given to sqlite3_bind_text are no longer valid. To "fix" that, you could use SQLITE_TRANSIENT, which would force the library to make its own copy of the data before returning.
Also, if you are not using C++11, I don't believe string::data() is guaranteed to be NULL terminated in which case the -1 parameter could be incorrect. It should maybe be value.length() instead.

Related

MySQL Trouble transitioning from Injection to Paramterization

I have the following code here that executes a query. Originally, I used SQL Injection to return row results. Hearing I should use parametrization, I rearranged my code and read the MySQL docs on how to do so. I'm using MySQL's C library in a C++ application.
However, it's not returning the results.
I know my SQL statement is 100% fine. It has been tested. The only thing I changed was changing %d (injection) to ?, which accepts the player's ID.
This returns -1. It's a SELECT statement though, so maybe it's normal?
// Get the number of affected rows
affected_rows = mysql_stmt_affected_rows(m_stmt);
This returns 2. This is correct. I have two fields being returned.
// Store the field count
m_fieldCount = mysql_field_count(&m_conn);
This returns 0 (success)
if (mysql_stmt_store_result(m_stmt))
Finally, this returns null.
m_result = mysql_store_result(&m_conn);
I need m_result so I can read the rows. "mysql_stmt_store_result" sounds similar, but doesn't return MYSQL_RESULT.
m_result = mysql_store_result(&m_conn);
/// <summary>
/// Executes a query.
/// </summary>
/// <param name="query">The query to execute.</param>
/// <returns>Returns true on success, else false.</returns>
bool SQLConnection::executeQuery_New(const char *query)
{
int param_count = 0;
int affected_rows = 0;
// Validate connection.
if (!m_connected)
return false;
// Initialize the statement
m_stmt = mysql_stmt_init(&m_conn);
if (!m_stmt) {
fprintf(stderr, " mysql_stmt_init(), out of memory\n");
return false;
}
// Prepare the statement
if (mysql_stmt_prepare(m_stmt, query, strlen(query))) {
fprintf(stderr, " mysql_stmt_prepare(), INSERT failed\n");
fprintf(stderr, " %s\n", mysql_stmt_error(m_stmt));
return false;
}
// Get the parameter count from the statement
param_count = mysql_stmt_param_count(m_stmt);
if (param_count != m_bind.size()) {
fprintf(stderr, " invalid parameter count returned by MySQL\n");
return false;
}
// Bind buffers
// The parameter binds are stored in std::vector<MYSQL_BIND>
// I need to convert std::vector<MYSQL_BIND> m_bind to MYSQL_BIND *bnd
MYSQL_BIND *bind = new MYSQL_BIND[m_bind.size() + 1];
memset(bind, 0, sizeof(bind) * m_bind.size());
for (int i = 0; i < param_count; i++)
bind[i] = m_bind[i];
if (mysql_stmt_bind_param(m_stmt, &bind[0]))
{
fprintf(stderr, " mysql_stmt_bind_param() failed\n");
fprintf(stderr, " %s\n", mysql_stmt_error(m_stmt));
return false;
}
// Execute the query
if (mysql_stmt_execute(m_stmt)) {
fprintf(stderr, " mysql_stmt_execute(), 1 failed\n");
fprintf(stderr, " %s\n", mysql_stmt_error(m_stmt));
return false;
}
// Get the number of affected rows
affected_rows = mysql_stmt_affected_rows(m_stmt);
//if (affected_rows == -1) {
// fprintf(stderr, " mysql_stmt_execute(), 1 failed\n");
// fprintf(stderr, " %s\n", mysql_stmt_error(m_stmt));
// return false;
//}
// Store the field count
m_fieldCount = mysql_field_count(&m_conn);
// Store the result
if (mysql_stmt_store_result(m_stmt))
{
fprintf(stderr, " failed retrieving result\n");
fprintf(stderr, " %s\n", mysql_error(&m_conn));
int d = mysql_errno(&m_conn);
return false;
}
// This looks similar to the last above statement, but I need m_result. I used mysql_store_result earlier when using injection and it worked fine, but here in this case it returns null.
m_result = mysql_store_result(&m_conn);
// Close the statement
if (mysql_stmt_close(m_stmt)) {
/* mysql_stmt_close() invalidates stmt, so call */
/* mysql_error(mysql) rather than mysql_stmt_error(stmt) */
fprintf(stderr, " failed while closing the statement\n");
fprintf(stderr, " %s\n", mysql_error(&m_conn));
return false;
}
// Delete bind array
if (bind) {
delete[] bind;
bind = NULL;
}
return true;
}
How I'm adding an int parameter (player's id):
void SQLConnection::addParam(int buffer, enum_field_types type, unsigned long length)
{
MYSQL_BIND bind;
memset(&bind, 0, sizeof(bind));
bind.buffer = (char *)&buffer;
bind.buffer_type = type;
bind.is_null = 0;
bind.length = &length;
m_bind.push_back(bind);
}
My variables and their types:
class SQLConnection
{
private:
MYSQL m_conn;
MYSQL_ROW m_row;
MYSQL_RES *m_result;
char m_errorMessage[ERROR_MSG_MAX];
bool m_connected;
MYSQL_STMT *m_stmt;
std::vector<MYSQL_BIND> m_bind;
int m_fieldCount;
// ...
And finally its calling function at the end of the SQL statement:
...WHERE player_id = ?;");
conn.addParam(m_id, MYSQL_TYPE_LONGLONG, 0);
if (!conn.executeQuery_New(buffer)) {
conn.close();
return "";
}
// Close the connection.
conn.close();
std::string s = conn.getField("max_value_column_name");
The error code I get is 2014:
https://dev.mysql.com/doc/refman/5.7/en/commands-out-of-sync.html
Just for the sake of interest, this is a prior function I used. This worked fine for injection. Using the new function above with parameterization is the one causing the issues.
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;
}
After I started having language religious wars in my head about using C# over C++, I thought I'd give one last attempt here. Any help is appreciated.
Edit:
This is how I read in column names prior to parameterization (maybe this code needs to be updated after calling mysql_stmt_store_result(m_stmt)?
std::string SQLConnection::getField(const char *fieldName)
{
MYSQL_FIELD *field = NULL;
unsigned int name_field = 0;
mysql_stmt_data_seek(m_stmt, 0);
mysql_stmt_fetch_column(m_stmt, &bind, 0, 0);
//mysql_data_seek(m_result, 0);
//mysql_field_seek(m_result, 0);
const unsigned int num_fields = mysql_stmt_field_count(m_stmt);
// const unsigned int num_fields = mysql_num_fields(m_result);
std::vector<char *> headers(num_fields);
for (unsigned int i = 0; (field = mysql_fetch_field(m_result)); i++)
{
headers[i] = field->name;
if (strcmp(fieldName, headers[i]) == 0)
name_field = i;
}
while ((m_row = mysql_fetch_row(m_result))) {
return std::string(m_row[name_field]);
}
return "";
}
Edit:
What I'm finding is in this last function there are equivalent functions for statements, like mysql_num_fields() is mysql_stmt_field_count(). I'm thinking these need to be updated because it's using m_stmt and not m_result anymore, which gives reason to update the functions so m_stmt is used. It's not very apparent how to update the second half of the function though.
You may need a better understanding of how stmt works.You can't get the final results by mysql_store_result() when you're using stmt.
You shoud bind several buffers for the statement you're using to accept the result set. You can finish this by mysql_stmt_bind_result(), just like mysql_stmt_bind_param().
Then you can use the buffers bound by mysql_stmt_bind_result() to return row data. You can finish this by mysql_stmt_fetch().
Do the fetch method repeatedly, so you can get the whole result set row by row.
The basic sequence of calls:
mysql_stmt_init
mysql_stmt_prepare
mysql_stmt_bind_param
mysql_stmt_execute
mysql_stmt_bind_result
mysql_stmt_store_result
mysql_stmt_fetch (repeatedly, row by row)
mysql_stmt_free_result
It works for me.
It's a long time since I finished this part of my project, you'd better read the manual carefully and find more examples of stmt.
Sorry for my poor English. Good luck!

if(result == NULL) is returning false always even when the query returns zero rows

I am using the Cassandra C++ driver in my application. I am observing many crashes. After debugging I identified that even when the query output is zero rows , the if (result == NULL) is false and when I iterate through the result, one place or other it is crashing. Below is the code sample. Please suggest me any solution for this.
const char* query = "SELECT variable_id, variable_name FROM aqm_wfvariables WHERE template_id = ?;";
CassError rc = CASS_OK;
CassSession* session = NULL;
if((session=CassandraDbConnect::getInstance()->getSessionForCassandra())==NULL){
return false;
}
CassStatement* statement = cass_statement_new(query, 1);
cass_statement_bind_int32(statement, 0, wf_template_id );
CassFuture* query_future = cass_session_execute(session, statement);
cass_future_wait(query_future);
rc = cass_future_error_code(query_future);
if (rc != CASS_OK) {
logMsg(DEBUG, 7, "cass_session_execute failed for query #%d:%s:%s", 1, __FILE__, query);
cass_statement_free(statement);
return false;
}
cass_statement_free(statement);
const CassResult* result = cass_future_get_result(query_future);
if (result == NULL) {
cass_future_free(query_future);
logMsg(DEBUG, 7, "No values are returned for query #%d:%s:%s", 1, __FILE__, query);
return false;
}
cass_future_free(query_future);
CassIterator* row_iterator = cass_iterator_from_result(result);
while (cass_iterator_next(row_iterator)) {
const CassRow* row = cass_iterator_get_row(row_iterator);
/* Copy data from the row */
You should use
(result.cass_result_row_count>0)
instead of
(result == NULL)
to verify if query returns zero rows. In your code, result is always an instance of CassResult and not a null reference when zero rows are returned.

Have successfully inserted blob data to SQLite database but can't get the data inserted

I use BLOB to insert an object to a SQLite databse. After the insertion, I can get the data with "SELECT" sentence and the data is correct, although the row of "TASK_HEAD" is "Empty" when browsing the database with "SQLite Database Browser".
However, if I destroy the object which has just been inserted, I can't get the correct data anymore, with the pointer "pHead" points to an address where the content of its "id" member is "铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪UG?" when read in VS2008 in debug mode.
Here is an example:
// user-defined data type
typedef std::string TASK_ID;
struct TASK_HEAD
{
TASK_ID id;
std::string userData;
int Size()
{
return (id.size() + userData.size()) * sizeof(TCHAR);
}
};
// when TEST_INSIDE is defined, pHead is invalid; but if undef it, I can get the "head" I just inserted
// and if the blob data is a string (when USING_STRING is defined), I can get the string inserted into the db even though the "test" string has been destroyed
void CDBWriter::WriteTestData()
{
// open db
sqlite3* db = NULL;
int nRet = sqlite3_open(DATABASE_NAME.c_str(), &db);
if (nRet != SQLITE_OK)
{
return;
}
if (db != NULL)
{
// create a table
std::string cmdCreate("CREATE TABLE IF NOT EXISTS testTable (id TEXT NOT NULL, TASK_HEAD BLOB, PRIMARY KEY(id));");
char* errMsg = NULL;
nRet = sqlite3_exec( db , cmdCreate.c_str() , 0 , 0 , &errMsg );
if( errMsg != NULL )
{
sqlite3_free( errMsg );
errMsg = NULL;
return;
}
//#define USING_STRING
#define TEST_INSIDE
#ifndef TEST_INSIDE
TASK_HEAD head;
#endif // TEST_INSIDE
// insert blob data
const TASK_ID newID(NewGUID()); // NewGUID returns string like this: "5811307F-7AA7-4C44-831F-774FC5832627"
string query = "INSERT OR REPLACE INTO testTable (id, TASK_HEAD) VALUES ('";
query += newID;
query += "', ?1);";
sqlite3_stmt* res = NULL;
nRet = sqlite3_prepare_v2(db, query.c_str(), query.length(), &res, 0);
{
#ifdef TEST_INSIDE
TASK_HEAD head;
#endif // TEST_INSIDE
head.id = newID;
#ifdef USING_STRING
std::string test("ewsjoafijdoaijeofsafsd");
nRet = sqlite3_bind_blob (res, 1, test.c_str(), test.size(), SQLITE_TRANSIENT);
#else
int nsizeHead = sizeof(head);
int nSizeHeadSt = sizeof(TASK_HEAD);
int sizeString = sizeof(std::string);
size_t nLen = newID.size();
//nRet = sqlite3_bind_blob (res, 1, &head, sizeof(head), SQLITE_TRANSIENT);
nRet = sqlite3_bind_blob (res, 1, &head, head.Size(), SQLITE_TRANSIENT);
#endif // USING_STRING
if (SQLITE_OK == nRet)
{
nRet = sqlite3_step(res);
}
if (nRet != SQLITE_OK && nRet != SQLITE_DONE)
{
return;
}
}
// get all columns in the database
query = "SELECT * FROM testTable;";
nRet = sqlite3_prepare_v2 (db, query.c_str(), query.length(), &res, 0);
if (SQLITE_OK == nRet)
{
while (SQLITE_ROW == sqlite3_step(res))
{
#ifdef USING_STRING
const char* pHead = (const char*)sqlite3_column_blob(res, 1);
#else
const TASK_HEAD *pHead = (const TASK_HEAD*)sqlite3_column_blob(res, 1);
#endif // USING_STRING
continue;
}
}
sqlite3_finalize(res);
sqlite3_close(db);
}
}
At first, I thought it might be the problem of bytes passed to sqlite3_bind_blob, so I get the bytes of the object with a stupid method, as you can see here (the size() function of TASK_HEAD), but that doesn't help.
Then I tried to use SQLITE_STATIC instead of SQLITE_TRANSIENT, still not working.
What's wrong?
Ps: I know it's a bad solution to insert an object to the db, and I just wanna know why I can't read back my data inserted into the db.
The content of userData is likely to be stored on the heap. Even if it's stored inside the std::string (for SSO) it still may use a pointer to itself internally, and so it won't work when you bitwise copy it to another place in memory (what you're doing is equivalent to a memcpy).
However, it doesn't matter why exactly it doesn't work, since it's just undefined behavior. Just don't "insert an object to the db" like this. Either serialize it using some serialization library and then insert it, or use two columns in the table, one for id and one for userData.
I think the problem is at:
nRet = sqlite3_bind_blob (res, 1, &head, head.Size(), SQLITE_TRANSIENT);
You cannot get the address of the TASK_HEAD structure and pass it to sqlite like this. To build a blob you need flat data, nothing with pointers and dynamic buffers like std::string objects.
You need to serialize the TASK_HEAD structure in a buffer before the binding operation.
For instance:
struct TASK_HEAD
{
TASK_ID id;
std::string userData;
std::string Data()
{
return id+userData;
}
int Size()
{
return (id.size() + userData.size()) * sizeof(TCHAR);
}
};
and:
nRet = sqlite3_bind_blob (res, 1, head.Data().c_str(), head.Size(), SQLITE_TRANSIENT);
Please note adding the fields to serialize as shown above is very poor (since this format cannot be unserialized). To deal with blobs, you need to find a good serialization library or format (protocol buffer, message pack, JSON, etc ...) or roll your own.
There is a second issue in your code at:
const TASK_HEAD *pHead = (const TASK_HEAD*)sqlite3_column_blob(res, 1);
This will not work, for a similar reason.

Sqlite C++ Image to Blob wont store nothing

I googled a lot and couldn't find any solution. I'm currently writing myself an
app in Codegear 2007 C++. I'm writing this app for my little kittens, its kinda' a dairy
but I call it KittyBook.
So i have two tables(sorry didn't understand how to CodeBlock) :
Kitten Info
Kitten Data.
KittenInfo Stores their names and their ID ( primary key ), their gender and their birth. This one works.
The other one should store a Blob. So after trying so many ways. It won't be stored in the table, not even the other data if I do the Query with normal insert but excluding the blob table.
So, I dunno what I'm doing wrong BUT I love SQLite so far. No turning back then eh?
The function is :
void CDatabase::InsertKittenData(int Kitten_ID, int kittenDay, bool DayOrWeek,
char * kitten_weight, char * Kitten_Comment, string PhotoFile) {
unsigned char * blob;
ifstream::pos_type size;
int size2 = 0;
if (FileExists(PhotoFile.c_str())) {
ifstream file(PhotoFile.c_str(), ios::in | ios::binary | ios::ate);
if (file.is_open()) {
size = file.tellg();
blob = new char[size];
file.seekg(0, ios::beg);
file.read(blob, size);
file.close();
}
}
else {
blob = NULL;
}
sqlite3 *dbp;
sqlite3_stmt *ppStmt;
// NULL = primary key autoinc.
char * Sql = "INSERT INTO KittenData VALUES ( NULL, ? , ? ,? , ? , ? , ?);";
int rc = sqlite3_open("KittyBook.db", &dbp);
if (rc)
return;
if (sqlite3_prepare_v2(dbp, Sql, -1, &ppStmt, NULL) != SQLITE_OK) {
return;
}
if (ppStmt) {
if (sqlite3_bind_int(ppStmt, 1, Kitten_ID) != SQLITE_OK)
return;
if (sqlite3_bind_int(ppStmt, 2, kittenDay) != SQLITE_OK)
return;
if (sqlite3_bind_int(ppStmt, 3, DayOrWeek) != SQLITE_OK)
return;
if (sqlite3_bind_text(ppStmt, 4, // Index of wildcard
kitten_weight, strlen(kitten_weight), // length of text
SQLITE_STATIC) != SQLITE_OK)
return;
if (sqlite3_bind_text(ppStmt, 5, // Index of wildcard
Kitten_Comment, strlen(Kitten_Comment), // length of text
SQLITE_STATIC) != SQLITE_OK)
return;
if (sqlite3_bind_blob(ppStmt, 6, blob, size2, SQLITE_TRANSIENT)
!= SQLITE_OK)
return;
if (sqlite3_step(ppStmt) != SQLITE_DONE)
return;
}
sqlite3_finalize(ppStmt);
sqlite3_exec(dbp, "COMMIT", NULL, NULL, NULL);
sqlite3_close(dbp);
}
You declare and initialize size2 as zero:
int size2 = 0;
then the next use of size2 is when you bind the blob:
sqlite3_bind_blob(ppStmt, 6, blob, size2, SQLITE_TRANSIENT)
From the fine manual:
In those routines that have a fourth argument, its value is the number of bytes in the parameter. To be clear: the value is the number of bytes in the value, not the number of characters.
and the fourth argument in this case is size2 and that's zero. The result is that you're telling SQLite to bind a zero-length blob and then you're wondering why nothing gets stored. Well, nothing gets stored because you're asking SQLite to store zero bytes and it is only doing what it is told.
Perhaps you want to use size instead of size2.
mu is too short, gave me the idea to do the Function step by step and increasing column
by column. Dunno what i did wrong, but its working now.
Cheers mu is too short :)

SQLite multi insert from C++ just adding the first one

I have the following code for SQLite:
std::vector<std::vector<std::string> > InternalDatabaseManager::query(std::string query)
{
sqlite3_stmt *statement;
std::vector<std::vector<std::string> > results;
if(sqlite3_prepare_v2(internalDbManager, query.c_str(), -1, &statement, 0) == SQLITE_OK)
{
int cols = sqlite3_column_count(statement);
int result = 0;
while(true)
{
result = sqlite3_step(statement);
std::vector<std::string> values;
if(result == SQLITE_ROW)
{
for(int col = 0; col < cols; col++)
{
std::string s;
char *ptr = (char*)sqlite3_column_text(statement, col);
if(ptr) s = ptr;
values.push_back(s);
}
results.push_back(values);
} else
{
break;
}
}
sqlite3_finalize(statement);
}
std::string error = sqlite3_errmsg(internalDbManager);
if(error != "not an error") std::cout << query << " " << error << std::endl;
return results;
}
When I try to pass a query string like:
INSERT INTO CpuUsage (NODE_ID, TIME_ID, CORE_ID, USER, NICE, SYSMODE, IDLE, IOWAIT, IRQ, SOFTIRQ, STEAL, GUEST) VALUES (1, 1, -1, 1014711, 117915, 175551, 5908257, 112996, 2613, 4359, 0, 0); INSERT INTO CpuUsage (NODE_ID, TIME_ID, CORE_ID, USER, NICE, SYSMODE, IDLE, IOWAIT, IRQ, SOFTIRQ, STEAL, GUEST) VALUES (1, 1, 0, 1014711, 117915, 175551, 5908257, 112996, 2613, 4359, 0, 0); INSERT INTO CpuUsage (NODE_ID, TIME_ID, CORE_ID, USER, NICE, SYSMODE, IDLE, IOWAIT, IRQ, SOFTIRQ, STEAL, GUEST) VALUES (1, 1, 1, 1014711, 117915, 175551, 5908257, 112996, 2613, 4359, 0, 0);
It results just inserting the first insert. Using some other tool lite SQLiteStudio it performs ok.
Any ideas to help me, please?
Thanks,
Pedro
EDIT
My query is a std::string.
const char** pzTail;
const char* q = query.c_str();
int result = -1;
do {
result = sqlite3_prepare_v2(internalDbManager, q, -1, &statement, pzTail);
q = *pzTail;
}
while(result == SQLITE_OK);
This gives me Description: cannot convert ‘const char*’ to ‘const char**’ for argument ‘5’ to ‘int sqlite3_prepare_v2(sqlite3*, const char*, int, sqlite3_stmt*, const char*)’
SQLite's prepare_v2 will only create a statement from the first insert in your string. You can think of it as a "pop front" mechanism.
int sqlite3_prepare_v2(
sqlite3 *db, /* Database handle */
const char *zSql, /* SQL statement, UTF-8 encoded */
int nByte, /* Maximum length of zSql in bytes. */
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
const char **pzTail /* OUT: Pointer to unused portion of zSql */
);
From http://www.sqlite.org/c3ref/prepare.html
If pzTail is not NULL then *pzTail is made to point to the first byte
past the end of the first SQL statement in zSql. These routines only
compile the first statement in zSql, so *pzTail is left pointing to
what remains uncompiled.
The pzTail parameter will point to the rest of the inserts, so you can loop through them all until they have all been prepared.
The other option is to only do one insert at a time, which makes the rest of your handling code a little bit simpler usually.
Typically I have seen people do this sort of thing under the impression that they will be evaluated under the same transaction. This is not the case, though. The second one may fail and the first and third will still succeed.