Qt: SELECT-Query to PostgreSQL-Server always responds with NULL - qt

I recently started with the Qt Creator and C++ and wanted to use a PostgreSQL database for my needs. So I figured out how to get the driver, included all the tags needed and started executing some querys.
I created new tables, inserted some data, updated them afterwards. It all worked just fine, until I tried to simply select some rows. The query just ended without an error-message and a response of NULL.
The command was a simple SELECT-Command:
query = db.exec("SELECT id FROM users WHERE name = 'Testuser';");
But a basic SELECT doesn't work either:
query = db.exec("SELECT * FROM users;");
If I now copy exactly this query and put it as a SQL-Statement directly into pgAdmin, it works just fine and responses with the user-id.
I tried quotation marks for the tablename, I tried the full row-names (SELECT users.id FROM public.users WHERE users.name = 'Testuser';) and large and small letters because of some tips from google - nothing worked.
Every query works just fine, but if I try a SELECT, it just always responses with NULL. Although every single of this SELECT-Querys works just fine in pgAdmin.
Has anyone an idea?
The following tags are used:
#include <QDebug>
#include <QtCore/QCoreApplication>
#include <QtSql/QPSQLDriver>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QCryptographicHash>
#include <QSqlError>
The full code looks like this:
QString response;
QSqlQuery query;
QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL");
db.setHostName("127.0.0.1");
db.setPort(5432);
db.setDatabaseName("postgres");
db.setUserName("postgres");
db.setPassword("password");
if (db.open())
{
query = db.exec("SELECT id FROM users WHERE name = 'Testuser';");
response = query.value(0).toString();
}
db.close();

Qt Assisstant says:
After the query is executed, the query is positioned on an invalid
record and must be navigated to a valid record before data values can
be retrieved (for example, using next()).
So, you should do this to get its first record:
query.first();
QString result = return query.record().value(0).toString();
Also you can iterate over its records:
QStringList results;
while (query.next())
{
QString result = return query.record().value(0).toString();
results.append(result);
}
And it's always a good idea to check query execution error status:
bool res = query.exec(...);
if (res == false)
{
qDebug() << "SQL ERROR: " << query->lastError().text();
}

Related

Qt QMap<int, MyClass> ignores insert command

I've a question that couldn't find anywhere. I have a QMap that's ignoring the QMap.insert(Key, Value) command. Here's the code:
//gets the selected problem index on the ProblemList
int selProblem = ui->tree_projects->currentItem()->data(0, Qt::UserRole).toInt();
//creates a new problem, sets its values and then replaces the old one on the ProblemsList variable
ProblemSets nProblem;
if(!problemsList.isEmpty()) //problemsList is an attribute of MainWindow
nProblem = problemsList.value(selProblem);
// some data collection that has been omitted because isn't important
// temporary maps that will carry the modifications
QMap<int, QString> nResName, nResType;
//data insertion into the maps
//these are fine
nResName.insert(fIdx, results_model->data(results_model->index(fIdx, 0)).toString());
nResType.insert(fIdx, results_model->data(results_model->index(fIdx, 1)).toString());
//replaces the old maps with the new ones
nProblem.SetProbResultsNames(nResName);
nProblem.SetProbResultsTypes(nResType);
//replaces the old problem with the new one
problemsList.insert(selProblem, nProblem); //this is the line that's doing nothing
}
That last line appears to be doing nothing! I've even tried to use
problemsList.remove(selProblem);
problemList.insert(selProblem, nProblem);
but got a similar result: the map not being inserted at the index selProblem. It got inserted, but with an outdated value - the same one of the deleted index -. I've checked on Debug and all the indexes and variables are correct, but when the .insert hits, nothing happens.
The most awkward thing is that this code is a copy/paste that I made from another method that I'm using that does similar thing, just changing the variable names, but that one works.
EDIT 1: This is the contents of nProblem, selProb and problemsList.value(selProblem)
Just before the Line:
problemsList.insert(selProblem, nProblem);
selProb: 0
nProblem:
ProbResultsNames: "NewRow0"
ProbResultsType: "Real"
problemsList.value(selProblem):
ProbResultsNames: non-existent
ProbResultsType: non-existent
After the line
problemsList.insert(selProblem, nProblem);
selProb: 0
nProblem:
ProbResultsNames: "NewRow0"
ProbResultsType: "Real"
problemsList.value(selProblem):
ProbResultsNames: non-existent
ProbResultsType: non-existent
EDIT 2:
class ProblemSets
{
public:
ProblemSets();
virtual ~ProblemSets();
ProblemSets(const ProblemSets& other);
ProblemSets& operator=(const ProblemSets& other);
//I hid getters and setters to avoid pollution on the post
private:
int index;
bool usingBenchmark;
QString functionSelected;
QString info;
QMap<int, QString> probVars_name, probVars_type, probResultsNames, probResultsTypes;
QMap<int, float> probVars_min, probVars_max;
QMap<int, int> probVars_stpSize, probVars_stp;
int varsNumber; // holds how many vars has been created, just for display purposes
int resNumber; // holds how many results has been created, just for display purposes
};
A simple test proves that QMap works as expected:
QMap<int, QString> mm;
mm.insert(1, "Test1");
qDebug() << mm[1]; // "Test1"
mm.remove(1);
qDebug() << mm[1]; // "" (default constructed value)
mm.insert(1, "Test2");
qDebug() << mm[1]; // "Test2"
Which means that the problem lies in your code.
This statement itself is highly suspicious:
That last line appears to be doing nothing!
Because then you go on to say that the map still contains the "old value". But you removed that key, so if the insert() method didn't work, you shouldn't be getting the old value, but a default constructed value.
Which means that the problem is most likely that nProblem has the same value as the one that is previously associated to that key in the map. The map works, you values are likely wrong.
Found the issue! I didn't have both the variables declared on the copy method of the ProblemSets class.
Solved simply adding them to the copy method
MainWindow::ProblemSets::ProblemSets(const ProblemSets& other)
{
// copy
index = other.index;
usingBenchmark = other.usingBenchmark;
functionSelected = other.functionSelected;
info = other.info;
probVars_name = other.probVars_name;
probVars_type = other.probVars_type;
probVars_min = other.probVars_min;
probVars_max = other.probVars_max;
probVars_stpSize = other.probVars_stpSize;
probVars_stp = other.probVars_stp;
//here
probResultsNames = other.probResultsNames;
probResultsTypes = other.probResultsTypes;
//
varsNumber = other.varsNumber;
resNumber = other.resNumber;
}
I had this issue before with the std::vector class, and that's why I suspected that could be that. Thanks to everyone that helped!

QSqlRelationalTableModel - insert record greater than 256

I have a table node={id,name}, and a table segment={id,nodeFrom,nodeTo} in a SQLite db, where node.id and segment.id are AUTOINCREMENT fields.
I'm creating a QSqlTableModel for Node, as follows:
nodeModel = new QSqlTableModel(this,db);
nodeModel->setTable("Node");
nodeModel->setEditStrategy(QSqlTableModel::OnFieldChange);
and I use the following code for inserting nodes:
int addNode(QString name) {
QSqlRecord newRec = nodeModel->record();
newRec.setGenerated("id",false);
newRec.setValue("name",name);
if (not nodeModel->insertRecord(-1,newRec))
qDebug() << nodeModel->lastError();
if (not nodeModel->submit())
qDebug() << nodeModel->lastError();
return nodeModel->query().lastInsertId().toInt();
}
This seems to work. Now, for segments I define a QSqlRelationalTableModel, as follows:
segModel = new QSqlRelationalTableModel(this,db);
segModel->setTable("Segment");
segModel->setEditStrategy(QSqlTableModel::OnManualSubmit);
segModel->setRelation(segModel->fieldIndex("nodeFrom"),
QSqlRelation("Node","id","name"));
segModel->setRelation(segModel->fieldIndex("nodeTo"),
QSqlRelation("Node","id","name"));
And then I have the following code for inserting segments:
int addSegment(int nodeFrom, int nodeTo) {
QSqlRecord newRec = segModel->record();
newRec.setGenerated("id",false);
newRec.setValue(1,nodeFrom);
newRec.setValue(2,nodeTo);
if (not segModel->insertRecord(-1,newRec)) // (*)
qDebug() << segModel->lastError();
if (not segModel->submitAll())
qDebug() << segModel->lastError(); // (*)
}
I can add successfully 280 nodes using addNode(). I can also add segments sucessfully if nodeFrom<=256 and nodeTo<=256. For any segment referencing a node greater or equal to 256 I get a
QSqlError("19", "Unable to fetch row", "Segment.nodeTo may not be NULL")
in one of the lines marked with a (*) of the addSegment function.
I've googled and found out that people are having other (apparently unrelated) problems when they hit the magical 256 record count. No solution seems to work with this particular problem.
What am I doing wrong?
Thanks!
The reason of this error lies in the void QRelation::populateDictionary() method which uses such a loop for (int i=0; i < model->rowCount(); ++i). If you use the database that does not report the size of the query back (e.g. SQLite), the rowCount() method will return this magical 256 value.
You can solve this by populating the relation model before using data(...) or setData(...). At first you can try with:
setRelation(nodeFromCol, QSqlRelation("Node", "id", "name"));
QSqlTableModel *model = relationModel(nodeFromCol);
while(model->canFetchMore())
model->fetchMore();
Try this way to fix
newRec.setValue(1,QVariant(nodeFrom));
newRec.setValue(2,QVariant(nodeTo));

Qt ActiveX "Unknown error"

I'm working on integrating T-Cube motor controller (http://www.thorlabs.de/newgrouppage9.cfm?objectgroup_id=2419) into the software based on Qt-4.8.1 package. Due to there is no manual or any sort of tutorial how to retrieve ActiveX object and how to call methods I did the following.
1) Looked through Windows registry looking for words similar to motor controller name. Found a candidate with CLSID "{3CE35BF3-1E13-4D2C-8C0B-DEF6314420B3}".
2) Tried initializing it in the following way (code provided is shortened, all result checks are removed in order to improve readability):
HRESULT h_result = CoInitializeEx(NULL, COINIT_MULTITHREADED);
pd->moto = new QAxObject();
initialized = moto->setControl( "{3CE35BF3-1E13-4D2C-8C0B-DEF6314420B3}" );
QString stri = browser->generateDocumentation();
obj->dynamicCall("SetHWSerialNum(int)", params);
QVariantList params;
params << 0;
params << 0.0;
int result = pd->moto->dynamicCall("GetPosition(int, double&)", params).toInt();
value = params[1].toFloat();
QVariantList params;
params << 0;
params << dist;
params << dist;
params << true;
int result = pd->moto->dynamicCall("MoveRelativeEx(int, double, double, bool)", params).toInt();
3) generateDocumentation() method gives perfect description of ~150 methods.
4) All dynamicCall() invocations cause "Error calling ...: Unknown error", where "..." is a first argument of dynamicCall() from the list generateDocumentation()'s given me.
5) If I insert into dynamicCall() any method which isn't presented in the documentation generated the output is different. So I suppose that methods in documentation generated really exist.
6) If I use #import directive and try calling directly avoiding QAxObject usage I see "mg17motor.tlh" file but none of interfaces described there contain any methods. So I can't use it directly as well. Is it normal?
I would be very much obliged for any advice.
You can find the ActiveX object using the OLE viewer. Then search for something like
APT.. or MG.. under all objects. Then find the parameter ProgID=MGMOTOR.MGMotorCtrl.1.
Now in Qt don't use QAxObject but QAxWidget. Then you get something like:
QAxWidget* aptMotor;
QVariant chanID = QVariant(0);
aptMotor = new QAxWidget();
aptMotor->setControl("MGMOTOR.MGMotorCtrl.1");
//Nice html documentation on available functions
QFile file("out.html");
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file);
out << aptMotor->generateDocumentation();
file.close();
aptMotor->setProperty("HWSerialNum",QVariant(83853493));
aptMotor->dynamicCall("StartCtrl");
aptMotor->dynamicCall("EnableHWChannel(QVariant)",chanID);
QThread::sleep(1); // Give it time to enable the channel
double pos(0);
aptMotor->dynamicCall("SetAbsMovePos(QVariant,QVariant)",chanID,QVariant(pos));
aptMotor->dynamicCall("MoveAbsolute(QVariant,QVariant,QVariant)",chanID,0);
aptMotor->dynamicCall("StopCtrl");

multiple sql statements in QSqlQuery using the sqlite3 driver

I have a file containing several SQL statements that I'd like to use to initialize a new sqlite3 database file. Apparently, sqlite3 only handles multiple statements in one query via the sqlite3_exec() function, and not through the prepare/step/finalize functions. That's all fine, but I'd like to use the QtSQL api rather than the c api directly. Loading in the same initializer file via QSqlQuery only executes the first statement, just like directly using the prepare/step/finalize functions from the sqlite3 api. Is there a way to get QSqlQuery to run multiple queries without having to have separate calls to query.exec() for each statement?
As clearly stated in Qt Documentation for QSqlQuery::prepare() and QSqlQuery::exec(),
For SQLite, the query string can contain only one statement at a time.
If more than one statements are give, the function returns false.
As you have already guessed the only known workaround to this limitation is having all the sql statements separated by some string, split the statements and execute each of them in a loop.
See the following example code (which uses ";" as separator, and assumes the same character not being used inside the queries..this lacks generality, as you may have the given character in string literals in where/insert/update statements):
QSqlDatabase database;
QSqlQuery query(database);
QFile scriptFile("/path/to/your/script.sql");
if (scriptFile.open(QIODevice::ReadOnly))
{
// The SQLite driver executes only a single (the first) query in the QSqlQuery
// if the script contains more queries, it needs to be splitted.
QStringList scriptQueries = QTextStream(&scriptFile).readAll().split(';');
foreach (QString queryTxt, scriptQueries)
{
if (queryTxt.trimmed().isEmpty()) {
continue;
}
if (!query.exec(queryTxt))
{
qFatal(QString("One of the query failed to execute."
" Error detail: " + query.lastError().text()).toLocal8Bit());
}
query.finish();
}
}
I wrote a simple function to read SQL from a file and execute it one statement at a time.
/**
* #brief executeQueriesFromFile Read each line from a .sql QFile
* (assumed to not have been opened before this function), and when ; is reached, execute
* the SQL gathered until then on the query object. Then do this until a COMMIT SQL
* statement is found. In other words, this function assumes each file is a single
* SQL transaction, ending with a COMMIT line.
*/
void executeQueriesFromFile(QFile *file, QSqlQuery *query)
{
while (!file->atEnd()){
QByteArray readLine="";
QString cleanedLine;
QString line="";
bool finished=false;
while(!finished){
readLine = file->readLine();
cleanedLine=readLine.trimmed();
// remove comments at end of line
QStringList strings=cleanedLine.split("--");
cleanedLine=strings.at(0);
// remove lines with only comment, and DROP lines
if(!cleanedLine.startsWith("--")
&& !cleanedLine.startsWith("DROP")
&& !cleanedLine.isEmpty()){
line+=cleanedLine;
}
if(cleanedLine.endsWith(";")){
break;
}
if(cleanedLine.startsWith("COMMIT")){
finished=true;
}
}
if(!line.isEmpty()){
query->exec(line);
}
if(!query->isActive()){
qDebug() << QSqlDatabase::drivers();
qDebug() << query->lastError();
qDebug() << "test executed query:"<< query->executedQuery();
qDebug() << "test last query:"<< query->lastQuery();
}
}
}
http://www.fluxitek.fi/2013/10/reading-sql-text-file-sqlite-database-qt/
https://gist.github.com/savolai/6852986

SQLite3: Insert BLOB with NULL characters in C++

I'm working on the development of a C++ API which uses custom-designed plugins
to interface with different database engines using their APIs and specific SQL
syntax.
Currently, I'm attempting to find a way of inserting BLOBs, but since NULL is
the terminating character in C/C++, the BLOB becomes truncated when constructing
the INSERT INTO query string. So far, I've worked with
//...
char* sql;
void* blob;
int len;
//...
blob = some_blob_already_in_memory;
len = length_of_blob_already_known;
sql = sqlite3_malloc(2*len+1);
sql = sqlite3_mprintf("INSERT INTO table VALUES (%Q)", (char*)blob);
//...
I expect that, if it is at all possible to do it in the SQLite3 interactive console, it should be possible to construct the query string with properly escaped NULL characters. Maybe there's a way to do this with standard SQL which is also supported by SQLite SQL syntax?
Surely someone must have faced the same situation before. I've googled and found some answers but were in other programming languages (Python).
Thank you in advance for your feedback.
Thank you all again for your feedback. This time I'm reporting how I solved the problem with the help of the indications provided here. Hopefully this will help others in the future.
As suggested by the first three posters, I did use prepared statements — additionally because I was also interested in getting the columns' data types, and a simple sqlite3_get_table() wouldn't do.
After preparing the SQL statement in the form of the following constant string:
INSERT INTO table VALUES(?,?,?,?);
it remains the binding of the corresponding values. This is done by issuing as many sqlite3_bind_blob() calls as the columns. (I also resorted to sqlite3_bind_text() for other "simple" data types because the API I'm working on can translate integers/doubles/etc into a string). So:
#include <stdio.h>
#include <string.h>
#include <sqlite3.h>
/* ... */
void* blobvalue[4] = { NULL, NULL, NULL, NULL };
int blobsize[4] = { 0, 0, 0, 0 };
const char* tail = NULL;
const char* sql = "INSERT INTO tabl VALUES(?,?,?,?)";
sqlite3_stmt* stmt = NULL;
sqlite3* db = NULL;
/* ... */
sqlite3_open("sqlite.db", &db);
sqlite3_prepare_v2(db,
sql, strlen(sql) + 1,
&stmt, &tail);
for(unsigned int i = 0; i < 4; i++) {
sqlite3_bind_blob(stmt,
i + 1, blobvalue[i], blobsize[i],
SQLITE_TRANSIENT);
}
if(sqlite3_step(stmt) != SQLITE_DONE) {
printf("Error message: %s\n", sqlite3_errmsg(db));
}
sqlite3_finalize(stmt);
sqlite3_close(db);
Note also that some functions (sqlite3_open_v2(), sqlite3_prepare_v2()) appear on the later SQLite versions (I suppose 3.5.x and later).
The SQLite table tabl in file sqlite.db can be created with (for example)
CREATE TABLE tabl(a TEXT PRIMARY KEY, b TEXT, c TEXT, d TEXT);
You'll want to use this function with a prepared statement.
int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
In C/C++, the standard way of dealing with NULLs in strings is to either store the beginning of the string and a length, or store a pointer to the beginning of a string and one to the end of the string.
You want to precompile the statement sqlite_prepare_v2(), and then bind the blob in using sqlite3_bind_blob(). Note that the statement you bind in will be INSERT INTO table VALUES (?).

Resources