sqlite prepared statements - how to debug - c++

I'm writing some c++ code that uses the sqlite3 library. I'm using a prepared statement to which I bind a variable at runtime.
How do I examine the SQL query in the statement after the bindings?
For example, the code below doesn't return a row. When using a premade string and sqlite3_exec, I get the results I expect.
sqlite3_stmt *statement;
const char *query = "SELECT * FROM foo WHERE (name='?');";
sqlite3_prepare_v2(db, query, strlen(query), &statemtnt, NULL);
sqlite3_bind_text(statement, 1, "bar", -1, SQLITE3_STATIC);
int result = sqlite3_step(statement);
// expected: result = SQLITE_ROW
// actual: result = SQLITE_DONE
edit: As Ferdinand stated below, the problem in the query above is the quotes around the ?. However, for the future, I'd still like to know how to inspect the sqlite3_stmt for the actual query that will be executed.

The SQL query does not change after the bindings -- your variables aren't inserted into the SQL string or anything.
In addition to what Neil said, drop the quotation marks around the ? placeholder:
"SELECT * FROM foo WHERE name = ?"
Otherwise SQLite won't replace the question mark but will treat it as the string "?".

Yes, you can do it by defining a profile function like this:
static void profile(void *context, const char *sql, sqlite3_uint64 ns) {
fprintf(stderr, "Query: %s\n", sql);
fprintf(stderr, "Execution Time: %llu ms\n", ns / 1000000);}
Then right after you open your database using sqlite3_open, make this call:
sqlite3_profile(fDBLink, &profile, NULL);

The third parameter of sqlite3_bind_text is supposed to be the value you want to bind - in your code you are trying to bind the query to itself!
Also, lose the semicolon at the end of the SELECT.

Don't know sqlite all that well, but the actual query might be logged or you might be able to flip a switch to get it to be logged.

Related

Creating a char* of specific format with specific values

I am attempting to create a char* type in a specific format, using values given by the other areas of the program, the values within the VALUES() brackets are the values that are given by the program.
The format should look like so:
char* sql = "INSERT INTO RecurringEvents (title,description,duration,recurtype,startfrom,endingtype,dateend,occurences,venueid) " \
"VALUES ('title','description','duration','recurtype','startfrom','endingtype','dateend',occurences,venueid); "
As you can see, text values must be within ' ' punctuation, while int values are left alone, so a usual command may be like so:
"INSERT INTO RecurringEvents (title,description,duration,recurtype,startfrom,endingtype,dateend,occurences,venueid) " \
"VALUES ('thetitle','thedesc','theduration','recurtype','startfrom','enddddtype','dateend',2,4); ";
The function in which this is required is below,not that it all matters, but to explain, it converts the event's(class) data all into string/int values, so they can be used to form an INSERT command(this is the proble), and then executed on a database, once this is done (and the record is verified for plausability) its added to the vector and the database is closed.
void addRecEvent(newRecurringEvent event, vector <newRecurringEvent> &events){
sqlite3 *db;
int rc;
char *sql;
int tableCheck;
char *zErrMsg = 0;
rc = sqlite3_open("data.sqlite", &db);
string title = event.getTitle();
string description = event.getDescription();
string duration = to_string(event.getDuration());
string recurType = recToString(event.getRecurType());
string startfrom = to_string( event.getStartFrom());
string endingtype = etypeToStr(event.getEndingType());
string dateend = to_string(event.getDateEnd());
int occurences = event.getOccurences();
int venueid = event.getVenuid();
/*CREATE INSERT COMMAND USING char*sql IN FORMAT REQUIRED*/
rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg); //execute the command
if (rc != SQLITE_OK){
cout << stderr << "SQL error: %s \n", zErrMsg;
}
else{
cout << stdout << "Records created succesfully";
events.push_back(event);
}
sqlite3_close(db);
}
I once attempted to create the format all in string within another function (by passing the values to it), and then returning it as a char*, but came accross the problem of the single quotation marks used on the text fields (like title,description etc).
Sorry if any of this is confusing, but to make it short, I just want to form a character sequence in the format in the first snippet of code, that uses given values to form its sequence.Any help is appreciated, as I am new to c++.
The comment left by whozcraig solved my question, I must use a prepared statement to feed my values to the statement

How to use variables in query

I have to use a user input value in my query to select a row which has that value........
I have written this code in qt but it doesn't work...how can I fix it?
void MainWindow::on_pushButton_clicked()
{
int j=0;
float t;
t=ui->T_lineEdit->text().toFloat();
QSqlQuery qry;
if(qry.exec("select * from table where te='+t+'"))
{
ui->u_lineEdit->setText("hello");
}
}
You should use a prepared statement using QSqlQuery.
QSqlQuery qry;
qry.prepare("select * from table where te=?");
qry.addBindValue(t);
if(qry.exec())
{
ui->u_lineEdit->setText("hello");
}
Note that concatenating user's raw string to a SQL query is highly vulnerable to SQL Injection.
I'm not familliar with qt, but it seems to me that you used concatenation within a string, so "select * from table where te='+t+'" should be "select * from table where te='"+t+"'" .
Of course it is strange that you should place single quotes around a float value, but I don't know the type of column te. It could be necessary to convert t to string first:
"select * from table where te='" + QString::number(t) + "'"
Finally I have to add that this looks suspiciously like code where you allow SQL Injection. You should look into this and make sure to avoid it. See Frogatto's answer for that.

sqlite3_step() returns SQLITE_MISUSE in tutorial situation

I am using SQLite3 for the first time, and I can't figure out the following error. Here are the relevant lines:
//open db; I checked, and sqlite3_open() returns 0 here (SQLITE_OK)
rc = sqlite3_open(filename.c_str(),&db);
//...
//create & prepare query.
string query = "SELECT * FROM MOCK_DATA;"; //I am positive MOCK_DATA exists and is non empty
sqlite3_stmt* stmt;
cout<<sqlite3_prepare(db,query.c_str(),0,&stmt,NULL)<<endl; //this prints 0: SQLITE_OK
//here we get the error
cout<<sqlite3_step(stmt)<<endl; //returns 21: SQLITE_MISUSE
Could anyone tell me what I am doing wrong? :) Thank you!
The second argument to sqlite3_prepare() is the length of the SQL string. Zero means that your SQL is empty. Supply a negative value such as -1 to make sqlite3 read up to the first NUL character as in a C string.

How can I get output parameter to stored procedure in POCO?

I'm using to poco libraries version 1.4.6
I want make program to connecting database, call stored procedure and get out parameters.
Firstly, I select value like this.
conn.Connect(host, user, password, db);
Poco::Data::Session* session = conn.Ptr();
int myNum;
std::string myStr;
*session << "SELECT `my_number`, `my_string` FROM `my_table`;",
Poco::Data::into(myNum),
Poco::Data::into(myStr),
Poco::Data::now;
That was available.
I want to call stored procedure and get output parameter value. so wrote like this.
// `my_sp` was simple stored procedure like this.
// `my_sp`(in inum int, in istr varchar(50), out onum int, out ostr varchar(50))
// SET onum = inum;
// SET ostr = istr;
int inNum, outNum;
std::string inStr, outStr;
*session << "CALL `my_sp`(?,?,?,?);",
Poco::Data::use(inNum),
Poco::Data::use(inStr),
Poco::Data::into(outNum),
Poco::Data::into(outStr),
Poco::Data::now;
But it was not available.
I tried like that.
*session << "CALL `my_sp`(1234, \'abcd\', #o_num, #o_str);",
Poco::Data::now;
*session << "SELECT #o_num;",
Poco::Data::into(outNum),
Poco::Data::now;
//*session << "SELECT #o_num, #o_str;",
// Poco::Data::into(outNum),
// Poco::Data::into(outStr),
// Poco::Data::now;
I can get out number through select. But i can't get out string. if I select outStr, throw exception.
[MySQL]: [Comment]: mysql_stmt_fetch error [mysql_stmt_error]: [mysql_stmt_errno]: 0 [mysql_stmt_sqlstate]: 00000 [statemnt]: SELECT #o_num, #o_str;
Why throw exception? I don't understand. Because I'm not goot at English.
I tried find another question like me. but other user was unlike me.
I think that reason was I'm not good at English. so hard to learn poco-document.
I want using stored procedure and get output parameter.
Please help me!
Stored procedures are not supported in 1.4.x. You should use a 1.5.x release and Data::ODBC back-end for full stored procedure I/O support.

sqllite query with encrypted std::string (unrecognized token)

I have a C++ std::string which is encrypted using AES128 and want to write it into a sqllite database. I figured out already, that I have to escape ' characters with '' and " with "", but there seems to be another problem.
It says:
unrecognized token: "'""\235\211g\264\376\247\3348( ]tu\202\346\360\226h\205D\322-\373\347y"
My query looks like:
UPDATE tablename
SET column='""\235\211g\264\376\247\3348( ]tu\202\346\360\226h\205D\322-\373\347y\315\|`\3206\245\220j6
\215&\301ww/\222R\352]\253,\362&\233ï\2530\322搜\377\321!\334t\224\271ќVu\214Z\\256""\242O\254\241\254\365\360<P\364\356\370\225jnۢ\231\335($\243\377fH\225\215\224\223\254\316'
WHERE index='1';
The same query with the unencrypted string works. Any ideas?
You are doing it wrong.
You should not, ever, write out the parameters in full within the query; but instead you should use bound parameters: Binding Values To Prepared Statements.
The main advantage ? Bound parameters do not have to be escaped, which completely prevents any risk of SQL injections, and also greatly simplifies your life!
Also, prepared statements can be reused for greater efficiency, so let me give a full example.
//
// WARNING: for concision purposes there is no error handling
// and no attempt at making this code even remotely exception-safe.
//
// !!! DO NOT USE IN REAL LIFE !!!
//
void update(std::map<int, std::string> const& blobs) {
// 1. Prepare statement
sqlite3_stmt *stmt;
sqlite3_prepare(db,
"update tablename set column = ? where index = ?",
-1, // statement is a C-string
&stmt,
0 // Pointer to unused portion of stmt
);
// 2. Use statement as many times as necessary
for (auto const& pair: blobs) {
int const index = pair.first;
std::string const& blob = pair.second;
// 2.1 Bind 1st parameter
sqlite3_bind_text(stmt,
1, // 1-based index: 1st parameter
blob.data(),
blob.size(),
0 // no need for sqlite to free this argument
);
// 2.2 Bind 2nd parameter
sqlite3_bind_int(stmt,
2, // 1-based index: 2nd parameter
index
);
// 2.3 Execute statement
sqlite3_step(stmt);
// 2.4 Reset bindings
sqlite3_reset(stmt);
}
// 3. Free prepared query
sqlite3_finalize(stmt);
} // update
Note: you can of course keep the prepared statement around for an even longer time.