"Incorrect string value" for polish characters (QSqlTableModel, MySQL) - c++

I'm having problem when adding record to QSqlTableModel:
QString name = out.getName().left(384/6); //UTF-16 max bytes/char = 6
qDebug() << "name:" << name;
record.setValue("name", name); //VARCHAR(384)
record.setValue("data", out.getData());
if (!boardLayoutsModel->insertRecord(-1, record)) {
qDebug() << "err:" << boardLayoutsModel->lastError().text();
}
If name contains only base characters, everything is fine: code returns
name: "Nowy uklad tablicy"
Name is taken from QLineEdit, so if it contains polish characters, e.g. "Nowy układ tablicy" is visible on the text field, it returns:
name: "Nowy uk³ad tablicy"
err: "Incorrect string value: '\xB3ad ta...' for column 'name' at row 1 QMYSQL3: Unable to execute statement"
I figured to use QString::toUtf8, and then there is no error and SELECT on the table returns good value (with "ł"), but what Qt is getting back from database is wrong and yet different:
Nowy ukÅad tablicy
Now I've changed the collation of database, from utf8_general_ci to utf16_unicode_ci as it's native collation of QString AFAIK. Still the same error appears. I would just use QString::fromUtf8 to read the value, but QSqlTableModel works on it's own. Weird thing is that it's not my first time doing MySQL/Qt integration via QSqlTableModel, but I've never had even similar problems before... I've just upgraded Qt, so that may be that. Any ideas?

Fixed with:
QLocale::setDefault(QLocale(QLocale::Polish, QLocale::Poland));
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));

Related

SQLite3 C++ Column value wont increment by 1

I have a column in one of my tables I'm trying to update via the UPDATE query in sqlite3 in C++. Using v3.8.10.2 amalgation of sqlite3.
I can set this value to whatever value I want, however I cannot increment it.
The value starts at 0. Once I increment it, it increments to 1. Afterwards, if I try to increment it again, it does not increment and it stays at 1.
The same goes if I replace the value 1 with something like 8. It stays at 8, doesn't go any higher (even though it should go to 16).
char* query = "UPDATE THETABLE SET ATTEMPTS = ATTEMPTS + 1 WHERE TESTFIELD = 'Hello';"
int error = sqlite3_exec(db, query.c_str(), NULL, 0, &errMsg);
if(error != SQLITE_OK){
//This doesn't happen, error is = to SQLITE_OK
cout << "SQL Error: " << error << " message: " << errMsg << endl;
sqlite3_close(db);
return;
}
cout << "Updated value!" << endl;
I used the exact same query via the command prompt console in Windows, it works, it increments. In C++, it stays at 1. Why?
EDIT:
I have also tried this query:
"UPDATE THETABLE SET ATTEMPTS = +1 WHERE TESTFIELD = 'Hello';"
Same thing, it's stuck at 1. Doesn't go any higher.
EDIT 2:
Here is the schema for that column:
"ATTEMPTS INTEGER DEFAULT 0,"
Is this a bug in the sqlite3 C++ implementation?
The problem was when I was initially adding a row, I was doing an:
INSERT OR REPLACE
I did not set the value for the field, I had thought it wouldn't get touched but it did, it got set to the default value of 0.
I changed it to:
INSERT OR IGNORE
and it works now.

Mongodb Update using C++ API

I have written a C++ function to update a mongodb document. It is as below.
void
LocationDb::setServingRouter(string sensor, string oAp, string nAp) {
m_conn.update("location_db.ldb",
BSON("id" << sensor << "name" << "/temp/s/1" << "ap" << BSON("name" << oAp)),
BSON("id" << sensor << "name" << "/temp/s/1" << "ap" << BSON("name" << nAp)));
}
This update query is working; however, I believe it has some limitation i.e. every time I update, I need to pass it the old ap name (oAp).
I find this very annoying in the sense that I just want to update the ap name without bothering was old name is. As per this query implementation, it is first finds the matching record in the document, and then update it.
How can I just update by matching the query partially? Bottom line is that I don't want to remember what old record was. Any help?

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 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.