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

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.

Related

C++ user input to sqlite3

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!

Parsing a CSV file using MS Text Driver / CDatabase

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;
}
}

Poco 1.5.2 C++ ODBC throws exceptions on insert

I have a program which reads information about 3D meshes in from a text file, stores the data as a mesh,performs some post-processing on the mesh, then collects information like volume, center of mass, etc.
The program has two threads:
Main thread starts the second thread, reads the file, does the processing, then waits for the second thread. As a part of the processing, it puts information about the meshes its just read onto a queue.
Second thread connects to SQL Server using Poco ODBC, puts some initial information about the file its reading into a database table, then gets information off the queue and assembles a potentially lengthy insert command. When it is done doing so, it submits the command, performs a final update command regarding the results of operations performed, then lets the main thread know it's done, and terminates the 2nd thread.
Everything works, right up until the moment it submits the large insert command. It throws an exception, and i can't figure out why.
Here i will give a simplistic outline of the code that is executing. Assume variables exist and are initialized.
The poco commands i run are:
using namespace Poco;
using namespace Poco::Data::Keywords;
using namespace Poco::Data;
ODBC::Connector::registerConnector();
Session session(SessionFactory::instance().create("ODBC", "Driver={SQL Server};Server=<hostname>;Database=<DB name>;Uid=<username>;Pwd=<password>;"));
session << "INSERT INTO TableName1 (SourceFileName,UserName) VALUES (?,?)",use(_filename),use(username),now;
session << "SELECT SCOPE_IDENTITY()", into(runID),now; //This always runs and returns 0.
string queryString = "Insert into TableName2 (Field1, field2, field3, field4, ...) "+
"VALUES (val1, val2, val3, val4, ...)"+
",(valA, valB, valC, valD, ...),..."
session << queryString,now;
Statement update(session);
update << "UPDATE TableName1 SET Field2 = ?, Field3 = ?, Field4 = ? WHERE Field1 = ?", use(data2), use(data3), use(data3), use(identity);
update.execute();
ODBC::Connector::unregisterConnector();
<send signal to main thread indicating being done.>
I'm trying to figure out a few key things.
How can I tell what state the Session is in?
Is there a way to ask Poco what went wrong and have it print an error message?
Are there any special things I need to set up to be able to specify a big insert statement all together in text like I am? I have tried it using ? placeholders, or executing individual statements, but it always gives me an exception on that part.
Is there a way to have statements execute under the same connection for sure? Normally I would do my INSERT INTO TableName1(...)VALUES(...) SELECT SCOPE_IDENTITY() all as a single operation. I've tried my commands in SQL Server Management Studio and it works properly. Right now, it is always returning a 0 aka NULL, like the statements run in separate connections.
More information:
String query = "INSERT INTO TableName1 (SourceFileName,UserName) VALUES ('"+ __file + "','" + __username + "')";
Statement insertStmt = (session << query);
try{insertStmt.execute(true);}
catch(Poco::Exception exc)
{
cout << exc.displayText() << endl;
}
try{session << "SELECT SCOPE_IDENTITY() as SCOPE_IDENTITY", into(runID), now;
cout << "Run ID: " << runID << endl;}
catch(Poco::Exception exc)
{
cout << exc.displayText() << endl;
}
I greatly appreciate your help or any suggestions on how I can improve this question.
1.:
There are various query members in the Session class - isConnected(), canTransact(), isTransaction() ... (if that is what you are looking for; if not, see the next answer)
1. and 2.:
Wrap your statement into try/catch block:
#include "Poco/Data/ODBC/ODBCException.h"
//...
try
{
session << "INSERT INTO TableName1 (SourceFileName, UserName) VALUES (?, ?) ",use(_filename),use(username),now;
}
catch(Poco::Data::ODBC::ConnectionException& ce){ std::cout << ce.toString() << std::endl; }
catch(Poco::Data::ODBC::StatementException& se){ std::cout << se.toString() << std::endl; }
3.:
I don't think the problem is too large statement. There is a configurable internal setting limiting the string size to 1K, but this applies to value strings, not the whole SQL statement.
If you still think that is the problem, you can increase the max field size, e.g.:
std::size_t fieldSize = 4096; //4K
session.impl()->setProperty("maxFieldSize", Poco::Any(fieldSize));
4.:
Poco::Data::ODBC does not parse or analyze the SQL statement in any way; so from that standpoint, whatever works with your ODBC driver will be fine.

How do I insert a variable result into a string in C++

I just started learning C++ in Qt and I was wondering how can I put a variables result in a string? I'm trying to use this for a simple application where someone puts their name in a text field then presses a button and it displays there name in a sentence. I know in objective-c it would be like,
NSString *name = [NSString stringWithFormatting:#"Hello, %#", [nameField stringValue]];
[nameField setStringValue:name];
How would I go about doing something like this with C++? Thanks for the help
I assume we're talking about Qt's QString class here. In this case, you can use the arg method:
int i; // current file's number
long total; // number of files to process
QString fileName; // current file's name
QString status = QString("Processing file %1 of %2: %3")
.arg(i).arg(total).arg(fileName);
See the QString documentation for more details about the many overloads of the arg method.
You donĀ“t mention what type your string is. If you are using the standard library then it would be something along the lines of
std::string name = "Hello, " + nameField;
That works for concatenating strings, if you want to insert other complex types you can use a stringstream like this:
std::ostringstream stream;
stream << "Hello, " << nameField;
stream << ", here is an int " << 7;
std::string text = stream.str();
Qt probably has its own string types, which should work in a similar fashion.
I would use a stringstream but I'm not 100% sure how that fits into your NSString case...
stringstream ss (stringstream::in);
ss << "hello my name is " << nameField;
I think QString has some nifty helpers that might do the same thing...
QString hello("hello ");
QString message = hello % nameField;
You could use QString::sprintf. I haven't found a good example of it's use yet, though. (If someone else finds one, feel free to edit it in to this answer).
You might be interested in seeing information about the difference between QString::sprintf and QString::arg.

Problem using mysql++ library

I've read the tutorial at, and I generally get how that works:
http://tangentsoft.net/mysql++/doc/html/userman/tutorial.html#simple
I am trying to build this mysql++ code, and I'm getting an error:
std::ostringstream query3;
query3<<"select pipe_id from pipe where version_id='"<<id<<"'";
std::storeQueryResult ares=query3.store();
for(size_t i=0;i<ares.num_rows();i++)
cout<<ares[i]["version_id"]<<ares[i]["pipe_id"]<<std::endl;
mysql_query(&mysql,query3.str().c_str());
The error is that store is not a member of ostringstream. I'm not sure how to correct the problem.
Hi Merlyn,
Thanks for the code and looking at my problem.
I tried the above code but again I am getting the error
error: request for member 'query' in 'connection' which is non-class type 'MYSQL*'
on this line of code
// Construct a query object with the query string mysqlpp::Query query =
connection.query(query_string);
kindly help where I am going wrong?
The problem is that you have to use a mysql++ query object to execute a query, not an ostringstream. The ostringstream just lets you build the query string, but won't let you perform the query.
There is a tutorial that shows basic usage at:
http://tangentsoft.net/mysql++/doc/html/userman/tutorial.html#simple
To get from your code to a query that works, you need to take your dynamic query, turn it into a string, and use it to construct the mysql++ query object.
// todo: create the connection here
// Construct the query string. You were already doing this in your code
std::ostringstream query_builder;
query_builder << "select pipe_id from pipe where version_id='" << id << "'";
// Convert the ostringstream to a string
std::string query_string = query_builder.str();
// Construct a query object with the query string
mysqlpp::Query query = connection.query(query_string);
// Perform the query
mysqlpp::StoreQueryResult result = query.store();
for(size_t i = 0; i < result.num_rows(); i++)
std::cout << result[i]["version_id"] << result[i]["pipe_id"] << std::endl;