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
Related
I have a quick question. I'm new in terms of trying to combine both sql and c++ together. My question is when I enter in the author to look for in the database, it say unknown column 'insert author last name here'. It's because of the input variable 'AuthorLast' not having quotes in the select statement. Thing is, I don't know how to fix it or change it.
#include<mysql.h>
#include<string>
#include<iostream>
using namespace std;
int main()
{
string AuthorLast;
mysql_library_init(0, NULL, NULL);
MYSQL* con = mysql_init(NULL);
if (con == NULL)
{
cout << mysql_error(con);
exit(1);
}
if (mysql_real_connect(con, "Insert Host here", "Insert ID here", "Password", "DataBase here", 0, NULL, 0) == NULL)
{
cout << mysql_error(con);
exit(1);
}
cout << "Enter in an author from the database: ";
getline(cin, AuthorLast);
string sql;
sql = "SELECT AuthorLast FROM Author WHERE AuthorLast= " + AuthorLast + ";";
const char* C = sql.c_str();
mysql_query(con, C);
MYSQL_RES* result = mysql_store_result(con);
if (result == NULL)
{
cout << mysql_error(con);
exit(1);
}
int field_nums = mysql_num_fields(result);
MYSQL_ROW row;
while (row = mysql_fetch_row(result))
{
for (int i = 0; i < field_nums; i++)
cout << row[i] << endl;
}
mysql_free_result(result);
mysql_close(con);
}
As other people have said, just adding single quotes in the SQL text would do the trick, but that leaves you vulnerable to SQL injection. Imagine that someone asks for an author name (written in another line for clarity):
SomeAuthor' or ''='
That would result in:
SELECT AuthorLast FROM Author WHERE AuthorLast= 'SomeAuthor' or ''='';
Which would result in your query returning all of the last names of authors. Although this may seem irrelevant for you, if (for example) you use the same approach in a password checking query, it can lead to an attacker being able to login without knowing the user password (in essence, you are allowing the user to modify your query).
You should thoroughly sanitize the user's input (this is, ensuring that it does not include unexpected characters) before including it in the query or (better still) use prepared statements (for mysql, I think that you can take a look at the mysql_stmt_* methods).
Prepared statements are more or less like telling the database server to execute "SELECT AuthorLast FROM Author WHERE AuthorLast=?", and telling it to use "MyAuthorLast" in place of the ?. So if someone tries to include quotes in the name, the server automatically sanitizes the input for you by adding any required escape characters.
I have a class assignment that asks us to write a C++ program that tracks spending, allows modification of the records, and returns "satisfaction" numbers about individual expenses (i.e. how good the user felt about spending that money). Our instructor has indicated that he'd like us to use sqlite3 in this program. He's given us a sample program that builds a table in sqlite3 and inputs predetermined values for the columns. This program runs just fine without issue.
What I am trying to do is modify the program to accept user inputs and store them in the sqlite3 database. This is the code I have thus far:
int main()
{
string salesDesc;
int price;
int satisf;
sqlite3 *db;
char *szErrMsg = 0;
cout << "Description of Expense: ";
cin >> saleDesc;
cout << endl;
cout << "Price: ";
cin >> price;
cout << endl;
cout << "Your Satisfaction: ";
cin >> satisf;
cout << endl;
// open database
int rc = sqlite3_open("spending_track.sqlite", &db);
if (rc)
{
cout << "Cannot open database\n";
}
else
{
cout << "Database opened successfully\n";
}
const char *pSQL[6];
pSQL[0] = "CREATE TABLE IF NOT EXISTS expenses(id INTEGER PRIMARY KEY "
"AUTOINCREMENT NOT NULL, logged TIMESTAMP DEFAULT "
"CURRENT_TIMESTAMP NOT NULL, desc VARCHAR(40), price INT,"
"satisfaction INT)";
pSQL[1] = "INSERT INTO expenses('" + string(saleDesc) + "'," price "," satisf ")";
pSQL[2] = "SELECT * FROM expenses";
pSQL[3] = "SELECT sum(satisf) FROM expenses";
// blablabla the rest of the program
When I try to compile this, I receive the following error:
error: cannot convert 'std::_cxx11::basic_string' to 'const char*' in assignment
pSQL[1] = "INSERT INTO expenses('" + string(saleDesc) + "'," price "," satisf ")";
If I change string(saleDesc) to saleDesc, I get the same error.
If I change string saleDesc; to char* saleDesc;, I receive the following error:
error: invalid operands of types 'const char[23]' and 'char*' to binary 'operator+'
pSQL[1] = "INSERT INTO expenses('" + string(saleDesc) + "'," price "," satisf ")";
I'm not sure what else to try to get this to work. I have also heard that it's a bad idea to allow users to directly input to sqlite3 tables. What would be a more "proper" way to do this?
Since this is just a class assignment, I doubt that you are going to have to worry about SQL injection attacks, so I wouldn't bother trying to sanitize your input.
Your other issue is you are confusing char*s and std::strings. The sqlite API requires you to pass it char*s so it can be used from C code, however that doesn't mean you need to use them. std::string is a wrapper for the char array, which you can get with the c_str() method. I don't think you really need to put the SQL statements in an array at the end. How about something like this:
std::string addTable = "CREATE TABLE IF NOT EXISTS expenses(id INTEGER PRIMARY KEY "
"AUTOINCREMENT NOT NULL, logged TIMESTAMP DEFAULT "
"CURRENT_TIMESTAMP NOT NULL, desc VARCHAR(40), price INT,"
"satisfaction INT)";
std::string insertExpense = "INSERT INTO expenses('" + saleDesc + "'," + std::to_string(price) "," + std::to_string(satisf) + ")";
std::string selectAllExpenses = "SELECT * FROM expenses";
Then when you want to pass it to the sqlite API, you could use c_str()
sqlite3_exec(db, addTable.c_str(), ...
Thanks everyone for the responses. I spent about an hour and a half with my professor yesterday going over this, and this actually stumped him. I eventually found a way to make this work with the array, but I want to stress that the solution I came up with is pretty much only good for this assignment. For anyone reading this with a similar problem, this method is not only messy, but also allows for SQL injection which should be avoided.
The problem, as many here have mentioned in comments, was that I was trying to stick a string into a char* array. The workaround we came up with was to add the SQL commands with the variables expanded in them directly to a string variable, like so:
string insertExpense = "INSERT INTO expenses(desc, price, satisf) VALUES ('" + saleDesc + "', "
""+ to_string(price) + ", " + to_string(satisf) + ")";
We then made that variable a c_str and assigned it to a char* variable, like so:
const char *line1 = insertExpense.c_str();
We then simply assigned this char* variable directly to the correct position in the array, like so:
const char *pSQL[6];
pSQL[0] = "CREATE TABLE IF NOT EXISTS expenses(id INTEGER PRIMARY KEY "
"AUTOINCREMENT NOT NULL, logged TIMESTAMP DEFAULT "
"CURRENT_TIMESTAMP NOT NULL, desc VARCHAR(40), price REAL,"
"satisf INT)";
pSQL[1] = line1;
pSQL[2] = "SELECT * FROM expenses";
pSQL[3] = "SELECT sum(satisf) FROM expenses";
This method correctly makes the SQL table and populates it with the correct statements as stored in their respective variables. I want to stress again that this method is both very messy and dangerous, and for anyone with a similar issue, it is probably a much better idea to use prepared statements, as others in the comments have already mentioned. Thank you everyone!
I'm trying to apply file's extension filters to the file's selection dialog.
This way works:
ofn.lpstrFilter =
"(*.exe) Windows Executable\0*.exe\0"
"(*.ini) Windows Initialization file \0*.ini\0"
"(*.dll) Dynamic Link Library \0*.dll\0"
"(*.lib) Windows Library file \0*.lib\0"
"(*.conf) Windows Configuration file \0*.conf\0";
But when I'm assigning extension filters dynamically, via parameters, it fails, filters don't appear in the combo box:
LPCSTR filter = (LPCSTR)extFilter; //Contains string "bmp"
stringstream s;
s << "(*.exe) Windows Executable\0" << "*." << filter << "\0";
string ffilter = s.str();
ofn.lpstrFilter = ffilter.c_str();
I'm assuming the problem is in strings conversion, but can't figure it out.
This line:
s << "(*.exe) Windows Executable\0" << "*." << filter << "\0";
Is passing null-terminated char* strings to operator<<(), and thus is effectively behaving the same as this code at runtime:
s << "(*.exe) Windows Executable" << "*." << filter << "";
The nulls never make it into s.
To insert the nulls correctly, you need to assign them to the stringstream as individual char values and not as char* values:
s << "(*.exe) Windows Executable" << '\0' << "*." << filter << '\0';
Also, the fact that you are type-casting extFilter is suspicious. If you have to do that to get rid of a compiler error then extFilter is not a compatible data type to begin with, the type-cast is hiding a bug in your code. Get rid of the type-cast:
LPCSTR filter = extFilter; //Contains string "bmp"
If the code fails to compile then you are doing something wrong and need to fix it properly.
On the other hand, if extFilter is a null-terminated char string to begin with, you don't need to assign it to a variable before passing it to operator<<():
s << "(*.exe) Windows Executable" << '\0' << "*." << extFilter << '\0';
You are using a pointer to some temporary string that, according to http://www.cplusplus.com/reference/string/string/c_str/, "may be invalidated by further calls to other member functions that modify the object."
Finally found an answer:
const char * extensionFilter = myParamVar; //Contains "JPG" string
string sFilter;
sFilter.append("Format: ");
sFilter.append(extensionFilter);
sFilter.push_back('\0');
sFilter.append("*.");
sFilter.append(extensionFilter);
sFilter.push_back('\0');
//Current filter content --> Format: JPG\0*.JPG\0
const char * filter = sFilter.c_str(); //Char string conversion
ofn.lpstrFilter = filter; //Set the filter to the sctructure's member.
//Opens the dialog and it successfully applies the filter.
if (GetOpenFileName(&ofn)==TRUE){
. . .
A shorter version:
ofn.lpstrFilter = _T("Format: XML\0*.xml\0");
I have a CSV file that I am trying to process that looks similar to the one below. This format was is already in use within legacy software so unfortunately I can't change it. As you can see the file is separated into two sections- in this example one section for items, and one section for parts within those items.
ID,Description,Make,Model,Serial,Parts
200,Fridge,Samsung,S4450,SX05948596,1x34.4x22
354,Dishwasher,Bobs,BB45,BFDD34848,3x34.1x55.4x2
ENDITEMS
STARTPARTS
ID,Description,Price,Created
34,Bolt,4.33,08/05/15
22,Nut,1.20,10/10/12
ENDPARTS
I am currently trying to use the Microsoft text driver and CDatabase/CRecordset to parse the file, but am running into an issue. It seems the engine gets confused with some fields about what data type to use - specifically the fields where the first section and the second section use differing types.
For example, if I call recordSet.GetFieldValue() on index 1 (Description), that is fine and it parses it as a string as both section 1 and 2 use strings. If I were to call it on index 4 I run into issues - for the first section that index (Model) uses strings, but in the second section that index (Created) uses a date type. The results in the GetFieldValue() call returning null.
I have tried calling CRecordSet.GetFieldValue(index, CDBVariant, SQL_C_CHAR) to force it to read the index as a string, but I'm still getting a null returned. If possible I'd like to avoid having to chop up the file before parsing.
Now I'm fairly new to C++ still, so there may be some glaring errors here, but here is my test code (the printType() method just prints out the type and value of the CDBVarient):
CString fileDir = "C:\\";
CString fileName = "test.CSV";
CString conString;
CString queryString;
conString.Format("DRIVER={Microsoft Text Driver (*.txt; *.csv)};DSN='';DBQ=%s;", fileDir);
queryString.Format("SELECT * FROM [%s]", fileName);
CDatabase db;
CRecordset rs;
rs.m_pDatabase = &db;
db.OpenEx(conString);
if (rs.Open(AFX_DB_USE_DEFAULT_TYPE, queryString))
{
int count = rs.GetODBCFieldCount();
for (int i = 0; i < count; i++)
{
CODBCFieldInfo fieldInfo;
rs.GetODBCFieldInfo(i, fieldInfo);
CDBVariant varValue;
rs.GetFieldValue(i, varValue, SQL_C_CHAR);
std::cout << i << " " << fieldInfo.m_strName << ":\t";
printType(&varValue);
std::cout << std::endl;
}
}
I have built a database in MS Access.
There I have a table called Customers which also has a cell called Employee type: integer.
I also built a program in C++ which controls all data.
Let's say I have a string like this:
string sqlString = "SELECT * FROM Customers Where Customers.Employee = '" + id + "' ";
Id passes through my function correctly and is an integer, so I get an error in compilation saying: "Invalid pointer addition".
If I declare id as a string of course there's no error but there are no results in my form also. If I declare in database cell Employee as text and build my query like this:
string sqlString = "SELECT * FROM Customers WHERE Customers.Employee = 128";
I get results, but I need that Employee as an integer cause its a foreign key from another table.
So, what should I do with my query to have results passing integer as parameter through variable id, to be ok with the cell Employee from database which is also integer?
Any ideas? I would really appreciate some help here.
As I said, if I convert id to string, there are no results in my form since Employee in database is an integer. So this:
std::ostringstream buf;
buf << "SELECT * FROM Customers Where Customers.Employee = '" << id << "' ";
string str = buf.str();
won't do the job or any other conversion.
How can I pass id as an integer in my query?
You could use sprintf, but in C++ you can do:
std::ostringstream buf;
buf << "SELECT * FROM Customers Where Customers.Employee = '" << id << "' ";
string str = buf.str();
(untested)
You need to convert id to a string, then your first approach should work.
See this question for how to do the conversion:
Alternative to itoa() for converting integer to string C++?
use
std::ostringstream buf; buf << "SELECT * FROM Customers Where Customers.Employee = " << id ; string str = buf.str();
This should work please try '12' --- quote should not be placed before and after 12
you can use boost::format with boost::str
string = boost::str(boost::format("This is a string with some %s and %d numbers") %"strings" %42);
this should be better approach since you will have all the replacement variable in one place at the end.