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.
Related
I am trying to query an SQL Server (Express) through Visual C++ (express) and store the resulting dataset into a C++ vector (array would be great as well). To that end I researched the ADO library and found plenty of help on MSDN. In short, reference the msado15.dll library and use those features (especially the ADO Record Binding, which requires icrsint.h). In short, I have been able to query the database and display the field values with printf(); but am stumbling when I try to load the field values into a vector.
I originally tried loading values by casting everything as char* (due to despair after many type conversion errors) only to find the end result was a vector of pointers that all pointed to the same memory address. Next (and this is the code provided below) I attempted to assign the value of the memory location but am ending up with a vector of the first character of the memory location only. In short, I need help understanding how to pass the entire value stored by the Recordset Field Value (rs.symbol) pointer (at the time it is passed to the vector) instead of just the first character? In this circumstance the values returned from SQL are strings.
#include "stdafx.h"
#import "msado15.dll" no_namespace rename("EOF", "EndOfFile")
#include "iostream"
#include <icrsint.h>
#include <vector>
int j;
_COM_SMARTPTR_TYPEDEF(IADORecordBinding, __uuidof(IADORecordBinding));
inline void TESTHR(HRESULT _hr) { if FAILED(_hr) _com_issue_error(_hr); }
class CCustomRs : public CADORecordBinding {
BEGIN_ADO_BINDING(CCustomRs)
ADO_VARIABLE_LENGTH_ENTRY2(1, adVarChar, symbol, sizeof(symbol), symbolStatus, false)
END_ADO_BINDING()
public:
CHAR symbol[6];
ULONG symbolStatus;
};
int main() {
::CoInitialize(NULL);
std::vector<char> tickers;
try {
char sym;
_RecordsetPtr pRs("ADODB.Recordset");
CCustomRs rs;
IADORecordBindingPtr picRs(pRs);
pRs->Open(L"SELECT symbol From Test", L"driver={sql server};SERVER=(local);Database=Securities;Trusted_Connection=Yes;",
adOpenForwardOnly, adLockReadOnly, adCmdText);
TESTHR(picRs->BindToRecordset(&rs));
while (!pRs->EndOfFile) {
// Process data in the CCustomRs C++ instance variables.
//Try to load field value into a vector
printf("Name = %s\n",
(rs.symbolStatus == adFldOK ? rs.symbol: "<Error>"));
//This is likely where my mistake is
sym = *rs.symbol;//only seems to store the first character at the pointer's address
// Move to the next row of the Recordset. Fields in the new row will
// automatically be placed in the CCustomRs C++ instance variables.
//Try to load field value into a vector
tickers.push_back (sym); //I can redefine everything as char*, but I end up with an array of a single memory location...
pRs->MoveNext();
}
}
catch (_com_error &e) {
printf("Error:\n");
printf("Code = %08lx\n", e.Error());
printf("Meaning = %s\n", e.ErrorMessage());
printf("Source = %s\n", (LPCSTR)e.Source());
printf("Description = %s\n", (LPCSTR)e.Description());
}
::CoUninitialize();
//This is me running tests to ensure the data passes as expected, which it doesn't
std::cin.get();
std::cout << "the vector contains: " << tickers.size() << '\n';
std::cin.get();
j = 0;
while (j < tickers.size()) {
std::cout << j << ' ' << tickers.size() << ' ' << tickers[j] << '\n';
j++;
}
std::cin.get();
}
Thank you for any guidance you can provide.
A std::vector<char*> does not work because the same buffer is used for all the records. So when pRs->MoveNext() is called, the new content is loaded into the buffer, overwriting the previous content.
You need to make a copy of the content.
I would suggest using a std::vector<std::string>:
std::vector<std::string> tickers;
...
tickers.push_back(std::string(rs.symbol));
Why you did not use std::string instead of std::vector?
To add characters use one of these member functions:
basic_string& append( const CharT* s ); - for cstrings,
basic_string& append( const CharT* s,size_type count ); - otherwise.
Read more at: http://en.cppreference.com/w/cpp/string/basic_string/append.
If you want a line breaks, simply append '\n' where you want it.
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.
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.
This is my c++ code to retrieve html data with cgi.
char* fileContentLength;
int nContentLength;
fileContentLength = getenv("CONTENT_LENGTH");
if(fileContentLength == NULL)
return -1;
nContentLength = atoi(fileContentLength);
if(nContentLength == 0)
return -1;
data = (char*) malloc(nContentLength+1);
if(data == NULL)
return -1;
memset(data, 0, nContentLength+1);
if(fread(data, 1, nContentLength, stdin) == 0)
return -1;
if(ferror(stdin))
After executing this code, i got the below result to the variable "data".
f0=fname0&l0=lname0&f1=fname1&l1=lname1&f2=fname2&l2=lname2&f3=&l3=
Here f0,l0,f1,l1 are name of the input box of the HTML page. From this string i need to separate the values like fname0, lname0,fname1,lname1 and so on. I used sscanf function. but i could not retrieve the correct result. How can i assign the values from the above string to a local variable called firstname and lastname.
Check out e.g. the strtok function. Use it in a loop to split at the '&' to get all the key-value pairs into a vector (for example). Then go though the vector splitting each string (you can use strtok again here) at the '=' character. You can put the keys and values in a std::map, or use directly.
For an even more C++-specific method, use e.g. std::string::find and std::string::substr instead of strtok. Then you can put keys and values directly into the map instead of temporary storing them as strings in a vector.
Edit: How to get the last pair
The last key-value pair is not terminated by the '&' character, so you have to check for the last pair after the loop. This can be done by having a copy of your string, and then get the substring after the last '&'. Something like this perhaps:
char *copy = strdup(data);
// Loop getting key-value pairs using `strtok`
// ...
// Find the last '&' in the string
char *last_amp_pos = strrchr(copy, '&');
if (last_amp_pos != NULL && last_amp_pos < (copy + strlen(copy)))
{
last_amp_pos++; // Increase to point to first character after the ampersand
// `last_amp_pos` now points to the last key-value pair
}
// Must be free'd since we allocated a copy above
free(copy);
The reason we need to use a copy of the string, if because strtok modifies the string.
I still would recommend to you use C++ strings instead of relying on the old C functions. It would probably simplify everything, including you not needing to add the extra check for the last key-value pair.
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.