QSqlQuery last not working - c++

I'm trying to get the size of an executed query in SQLite but when I use last and try to use last its always false
Here is the code I'm trying to execute
void createDB() {
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE","CREATE_CON");
db.setDatabaseName("C:/Desktop/TestDb.db3");
db.open();
QSqlQuery q(db);
q.exec("CREATE TABLE IF NOT EXISTS Test(testCol TEXT PRIMARY KEY);");
}
int entries() {
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE","SELECT_CON");
db.setDatabaseName("C:/Desktop/TestDb.db3");
db.open();
QSqlQuery q(db);
q.exec("SELECT * FROM Test;");
if(q.last() == false) {
qDebug()<<q.lastError().text();
qDebug()<<db.lastError().text();
}
return q.at()+1;
}
The error text I get is empty so I don't know what I'm doing wrong.
I can create the database just fine so my database instance is working as it should.
Operating system: Windows 10
I'm using: Qt 5.10.1
Compilator: MinGW

The behavior is correct, if your table is empty there will not be a last element so last() will be false.
I will take this question to show you that you must validate all possible errors. A program can sometimes work but the duty of a good programmer is to prevent it from always working.
#include <QApplication>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QDebug>
#include <QPushButton>
static bool createDB() {
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE","CREATE_CON");
db.setDatabaseName("TestDb.db3");
if(!db.open()){
qDebug()<<db.lastError().text();
return false;
}
QSqlQuery q(db);
if(!q.exec("CREATE TABLE IF NOT EXISTS Test(testCol TEXT PRIMARY KEY);")){
qDebug()<<q.lastError().text();
return false;
}
return true;
}
static int entries() {
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE","SELECT_CON");
db.setDatabaseName("TestDb.db3");
if(!db.open()){
qDebug()<< db.lastError().text();
return -1;
}
QSqlQuery q(db);
if(!q.exec("SELECT * FROM Test;")){
qDebug()<<q.lastError().text();
return -2;
}
if(!q.last()) {
return 0;
}
qDebug()<<"not empty";
return q.at()+1;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
if(!createDB()){
return -1;
}
QPushButton button("call entries");
QObject::connect(&button, &QPushButton::clicked, [&](){
qDebug()<<entries();
});
button.show();
return a.exec();
}

Instead of :
q.exec("SELECT * FROM Test;");
if(q.last() == false) {
qDebug()<<q.lastError().text();
qDebug()<<db.lastError().text();
}
You should do:
if(!q.exec("SELECT * FROM Test;")) {
qDebug()<<q.lastError().text();
}
And if you want to know the size of the query, why don't you store it in a QString, and call whenever you want QString::length()?

Related

How to change values in a json file?

I have the following JSON-file :
{
"users":[
{"nom":"123",
"name":"John",
"family":"ala",
"cash":1000
}
,{"nom":"456",
"name":"Joe",
"family":"ala",
"cash":1000
}
,{"nom":"131",
"name":"David",
"family":"ala",
"cash":1000
}]
}
I would like to change John's cash.
This is how I am trying to achieve this:
QFile f("file address ...");
f.open(QIODevice::ReadOnly|QIODevice::Text|QIODevice::WriteOnly);
QByteArray b=f.readAll();
QJsonDocument d=QJsonDocument::fromJson(b);
QJsonObject o=d.object();
for (int i=0;i<o["users"].toArray().size();i++) {
if(o["users"].toArray()[i].toObject()["name"].toString()=="John")
o["users"].toArray()[i].toObject()["cash"].toInt()=2000;//error unable to assign
}
However, I am getting the following error:
error: unable to assign
How to fix this?
Cause
You get the error, because you are trying to assign a value to the return value of a function (QJsonValue::toInt in this case).
Solution
Assign the value to QJsonValue, as demonstrated in the JSON Save Game Example:
void Character::write(QJsonObject &json) const
{
json["name"] = mName;
json["level"] = mLevel;
json["classType"] = mClassType;
}
Example
Here is an example I have written for you, in order to demonstrate how your code could be changed to implement the proposed solution:
#include <QApplication>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QFile>
#include <QDebug>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QFile file("data.json");
if (file.open(QFile::ReadOnly | QFile::Text)) {
QJsonObject json = QJsonDocument::fromJson(file.readAll()).object();
QJsonArray users = json["users"].toArray();
file.close();
for (auto user : users) {
QJsonObject userObj = user.toObject();
if (userObj["name"].toString() == "John")
userObj["cash"] = 2000;
user = userObj;
}
qDebug() << users;
}
return a.exec();
}
Result
The given example produces the following result:
QJsonArray([{"cash":2000,"family":"ala","name":"John","nom":"123"},{"cash":1000,"family":"ala","name":"Joe","nom":"456"},{"cash":1000,"family":"ala","name":"David","nom":"131"}])
Please note, that the cash for John is set to 2000.

QSqlDatabase: How to avoid 'qt_sql_default_connection' still in use & duplicate connection related warning?

Sorry if this is a trivial question but I have been trying to build a small .ui that used QSQLITE as database and that uses QTableView to show 4 columns on a default database file as example.
I debugged the problem in every side, changed logical operations of the SQL and restructured the constructor in a more simple way but the error still stays.
After finishing to set up all parameters I am getting this error:
QSqlDatabasePrivate::removeDatabase: connection 'qt_sql_default_connection' is still in use, all queries will cease to work.
And
QSqlDatabasePrivate::addDatabase: duplicate connection name 'qt_sql_default_connection', old connection removed.
I looked on several sources that describe this error such as this source, this other source. Also this was useful but still nothing happens. Official documentation suggests a "wrong" and "right" way to do that here. but the error still stays. After all these different options I brought back the code to a more concise way and I hope that someone can shed light on this issue.
Below the snipped of code:
mainwindow.h
private:
QString temporaryFolder;
dataInfo *mNewDatabaseImages;
QSqlTableModel *mNewTableImages;
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
temporaryFolder = "/home/to/Desktop/tempDBFolder/tmp.db";
QFile dbRem(temporaryFolder);
dbRem.remove();
mNewDatabaseImages = new dataInfo(this);
mNewDatabaseImages->initDataBase(temporaryFolder);
mNewDatabaseImages->confDataBase();
mNewTableImages = new QSqlTableModel(this, mNewDatabaseImages->getDatabase());
mNewTableImages->setTable("leftCamTable");
mNewTableImages->select();
ui->bookMarkTableView->setModel(mNewTableImages);
ui->bookMarkTableView->showColumn(true);
}
datainfo.h
#ifndef DATAINFO_H
#define DATAINFO_H
#include <QObject>
#include <QSqlDatabase>
#include "imageparam.h"
class dataInfo : public QObject
{
Q_OBJECT
public:
explicit dataInfo(QObject *parent = nullptr);
bool initDataBase(const QString &nameDB);
bool confDataBase();
bool addItem(ImageParam* imageItem);
QSqlDatabase getDatabase();
private:
QString mError;
QSqlDatabase mDBImages;
};
#endif // DATAINFO_H
datainfo.cpp
#include "datainfo.h"
#include <QSqlQuery>
#include <QSqlError>
#include <QDebug>
#include <QVariant>
#include <QMessageBox>
#define CREATE_TABLE \
" CREATE TABLE IF NOT EXISTS imageTable" \
" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL" \
" path1 TEXT NOT NULL" \
" path2 TEXT NOT NULL" \
" imageA BLOB NOT NULL" \
" imageB BLOB NOT NULL)"
dataInfo::dataInfo(QObject *parent) : QObject(parent)
{}
bool dataInfo::initDataBase(const QString &nameDB)
{
mDBImages = QSqlDatabase::addDatabase("QSQLITE");
mDBImages.setDatabaseName(nameDB);
bool ok = mDBImages.open();
if(!ok) {
mError = mDBImages.lastError().text();
qDebug() << mError;
}
return ok;
}
bool dataInfo::confDataBase()
{
QSqlQuery qry;
bool ok = qry.exec(CREATE_TABLE);
if(!ok) {
mError = qry.lastError().text();
}
return ok;
}
bool dataInfo::addItem(ImageParam *imageItem)
{
QSqlQuery qry;
qry.prepare("INSERT INTO imageTable (path1, path2, imageA, imageB)" \
" VALUES (?,?,?,?)");
qry.addBindValue(imageItem->path1());
qry.addBindValue(imageItem->path2());
qry.addBindValue(imageItem->image1());
qry.addBindValue(imageItem->image2());
bool ok = qry.exec();
if(!ok) {
mError = qry.lastError().text();
}
return ok;
}
QSqlDatabase dataInfo::getDatabase()
{
return mDBImages;
}
I looked also at this post that suggested to set a name to the databse first but I already do that in the function initDataBase(const QString &nameDB). Here the post that suggested the procedure, which is below:
db->setDatabaseName("name");
if(!db->open()) {
qDebug() << "Error opening ";
return false;
}
Please shed light on the possible solution.
Short answer
You should specify the database you want to run your QSqlQuery on, otherwise they will run on the default database. You can specify the database in QSqlQuery's constructor with
QSqlQuery query(QSqlDatabase::database("my-db"));
You are keeping a copy of the QSqlDatabase as a member of your dataInfo class, which will prevent it prevent closing properly. Instead, just use the static QSqlDatabase::database("name") when needed.
auto db = QSqlDatabase::database("my-db");
Details
Providing the right database to QSqlQuery
Change all your uses of QSqlQuery. For example for confDataBase:
bool dataInfo::confDataBase()
{
// Explicitly provide your database to the query
// Otherwise the default database is used
QSqlQuery qry(getDatabase());
bool ok = qry.exec(CREATE_TABLE);
if(!ok) {
mError = qry.lastError().text();
}
return ok;
}
Not keeping QSqlDatabase attributes
From the documentation:
Warning: It is highly recommended that you do not keep a copy of the QSqlDatabase around as a member of a class, as this will prevent the instance from being correctly cleaned up on shutdown. If you need to access an existing QSqlDatabase, it should be accessed with database(). If you chose to have a QSqlDatabase member variable, this needs to be deleted before the QCoreApplication instance is deleted, otherwise it may lead to undefined behavior.
Store your database's name in your class instead, and change your getDatabase to
dataInfo.cpp
bool dataInfo::initDataBase(const QString &nameDB)
{
// Save database's name
mDBName = nameDB;
// Use the database locally, without storing it
auto dbImages = QSqlDatabase::addDatabase("QSQLITE", nameDB);
bool ok = dbImages.open();
if(!ok) {
mError = dbImages.lastError().text();
qDebug() << mError;
}
return ok;
}
QSqlDatabase dataInfo::getDatabase()
{
return QSqlDatabase::database(mDBName);
}
dataInfo.h
private:
QString mError;
QString mDBName;
Qt's code generating the warning
To have a look at the actual code producing the error: https://code.woboq.org/qt5/qtbase/src/sql/kernel/qsqldatabase.cpp.html#170
invalidateDb is used when connections are added or removed, and will trigger the error if the reference count > 1. As you are holding onto one, this will trigger the error.

Qt MySQL Query - Unable to bind value

I finally fixed my MySQL connection in C++ Qt. However, when I try to bind values, I get the following error:
QSqlError("2036", "QMYSQL3: Unable to bind value", "Using unsupported buffer type: 1701052421 (parameter: 1)")
I have these files:
Engine.h:
#ifndef ENGINE_H
#define ENGINE_H
#include "database/mysql.h"
class engine
{
private:
static mysql* _mysql;
public:
static void initialize();
static void destroy();
static mysql get_mysql();
};
#endif // ENGINE_H
Engine.cpp:
#include "engine.h"
#include "entities/member_controller.h"
#include <QDebug>
mysql* engine::_mysql;
void engine::initialize()
{
_mysql = new mysql();
member* mem = member_controller::get_member(1);
qDebug() << "mem name = " << mem->getFirstName() << " " << mem->getSecondName();
delete mem;
}
void engine::destroy()
{
delete _mysql;
}
mysql engine::get_mysql()
{
return *_mysql;
}
mysql.h:
#ifndef MYSQL_H
#define MYSQL_H
#include <QtSql>
#include <QString>
#include "mysql_result.h"
class mysql
{
private:
QSqlDatabase db;
public:
mysql();
~mysql();
mysql_result create_result(QString query);
QSqlError error();
QSqlQuery query_prepare(QString query1)
{
QSqlQuery query(this->db);
query.prepare(query1);
// this->query = query;
return query;
}
};
#endif // MYSQL_H
(query_prepare body temp. in header file just to test)
mysql.cpp
#include "mysql.h"
mysql::mysql()
{
this->db = QSqlDatabase::addDatabase("QMYSQL", "QMYSQL");
this->db.setHostName("localhost");
this->db.setUserName("root");
this->db.setPassword("Eequi4");
this->db.setDatabaseName("test");
this->db.open();
}
mysql::~mysql()
{
}
QSqlError mysql::error()
{
return this->db.lastError();
}
member_controller.h:
#ifndef MEMBER_CONTROLLER_H
#define MEMBER_CONTROLLER_H
#include <QString>
#include "member.h"
class member_controller
{
public:
static member* get_member(unsigned int id);
static member* get_member(QString email);
};
#endif // MEMBER_CONTROLLER_H
member_controller.cpp:
#include "member_controller.h"
#include "database/mysql_result.h"
#include "engine.h"
#include "database/mysql_result.h"
#include <QtSql/QSqlQuery>
member* member_controller::get_member(unsigned int id)
{
QSqlQuery result = engine::get_mysql().query_prepare("SELECT * FROM members WHERE member_id = :mem_id");
result.bindValue(":mem_id", id);
if (result.exec() && result.first())
{
return new member(id, result.value("first_name").toString(), result.value("second_name").toString(), result.value("screen_name").toString(), result.value("email").toString(), result.value("status").toString());
}
else
{
qDebug() << engine::get_mysql().error() << "\n";
qDebug() << result.lastError() << "\n";
}
return new member(0, "", "", "", "", "");
}
I hope this is all the code needed. I tried using questionmark except of :mem_id but no luck either.
I am not C++ or Qt expert and have no possibility to debug your code.
But just because of curiosity I've started to investigate your code and found suspicious line (the 2nd one) in your code:
mysql_result result = engine::get_mysql().create_result("SELECT * FROM members WHERE member_id = ?");
Since I am not expert and you didn't provide any includes I don't know neither what is your engine namespace nor function get_mysql() return type nor create_result return.
So I did some guess: get_mysql() probably return QSqlDatabase object? isn't it?
But that type does not support any create_result method! So I stuck there.
Next thanks to google I've found your another question made a week ago. I would please to include such important information in your post next time, so people can see your classes and functions like:
mysql_result mysql::create_result(QString query)
{
return mysql_result(this->db.exec(query));
}
and
mysql_result::mysql_result(QSqlQuery query)
{
this->query = query;
}
Now I can understand what that 2nd line of your code is trying to do.
The suspicious thing I see here is return mysql_result(this->db.exec(query));. That seems according to the function names that you are trying to execute query and get result from mysql server.
But according to the algorithm I see in your member_controller::get_member it seems to me that you are only on prepare stage but not executing yet. I see that Qt documentation is not clear enough (and again i am not an expert) because: Executes a SQL statement on the database and returns a QSqlQuery object. And technically you can say that you have QSqlQuery as a result and you can expect that this result is absolutely the same as if you do:
//mysql_result mysql::create_result(QString query)
QSqlQuery mysql::query_prepare(QString query)
{
QSqlQuery query(this->db);
query.prepare(query);
this->query = query;
return this->query;
}
But IMHO it isn't. In your case it is already executed, that is why you can't bind any parameter later. So I would suggest you to change your mysql_result mysql::create_result(QString query) or create another function QSqlQuery mysql::query_prepare(QString query) which has more sense to me. and change your first lines of code to:
member* member_controller::get_member(int id)
{
QSqlQuery query = engine::get_mysql().query_prepare("SELECT * FROM members WHERE member_id = ?");
query.addBindValue(value);
if (query.exec() && query.first())
...
...
And very last point I don't understand why are you trying to reinvent the weel? If you already have Qt mysql classes which looks very good to me why do you create your own class and methods with one line to call Qt method like:
void mysql_result::add_parameter(QVariant value)
{
this->queryObject.addBindValue(value);
}
Sorry but IMHO that is not very good idea and has almost no sense.
UPDATE Looking into your updated code I see:
result.bindValue(":mem_id", id);
where result type is QSqlQuery so you are calling to the native method bindValue where second parameter according to documentation is const QVariant & val. So I would change your call to:
result.bindValue(":mem_id", QVariant(id));
or
QVariant mem_id(id);
result.bindValue(":mem_id", mem_id);

Handling QSqlDatabase connections

What is the correct way to handle connections for QSqlDatabase?
In my program I am doing it this way:
DatabaseConnector *databaseConnector = 0;
try
{
databaseConnector = new DatabaseConnector();
//Do stuff...
delete databaseConnector;
}
catch(QString e)
{
delete databaseConnector;
QMessageBox::information(this,"Error",e);
}
databaseConnector.h
#ifndef DATABASECONNECTOR_H
#define DATABASECONNECTOR_H
#include <QtSql>
class DatabaseConnector
{
public:
DatabaseConnector();
DatabaseConnector(QString hostname, QString database, QString user, QString password);
~DatabaseConnector();
private:
QSqlDatabase db;
};
#endif // DATABASECONNECTOR_H
databaseconnector.cpp
#include "databaseconnector.h"
#include <QString>
DatabaseConnector::DatabaseConnector()
{
QSettings settings;
db = QSqlDatabase::addDatabase("QIBASE");
db.setHostName(settings.value("db/host").toString());
db.setDatabaseName(settings.value("db/name").toString());
db.setUserName(settings.value("db/user").toString());
db.setPassword(settings.value("db/pass").toString());
if(!db.open())
{
QString databaseConnectionError = db.lastError().text();
throw databaseConnectionError;
}
}
DatabaseConnector::DatabaseConnector(QString hostname, QString database, QString user, QString password)
{
db = QSqlDatabase::addDatabase("QIBASE");
db.setHostName(hostname);
db.setDatabaseName(database);
db.setUserName(user);
db.setPassword(password);
if(!db.open())
{
QString databaseConnectionError = db.lastError().text();
throw databaseConnectionError;
}
}
DatabaseConnector::~DatabaseConnector()
{
db.close();
}
I'm getting error even if I use QSqlDatabase::removeDatabase(db.connectionName());
QSqlDatabasePrivate::addDatabase: duplicate connection name 'qt_sql_default_connection', old connection removed.
Normally you don’t need to open the database connection more than once within your application.
When adding a database, you can name the connection :
QSqlDatabase::addDatabase( "QIBASE", "MyDBConnectionName" );
You can use the name to query for the connection :
if( QSqlDatabase::contains( "MyDBConnectionName" ) )
{
QSqlDatabase db = QSqlDatabase::database( "MyDBConnectionName" );
//Do stuff...
}
else
{
// connection not found, do something
}
Also notice that before calling QSqlDatabase::removeDatabase you should disconnect your database :
db.close();
QSqlDatabase::removeDatabase("MyDBConnectionName");
In order to add a new database, you need to give it a name. If you do not give a unique name, the default database is re-used. This is documented in the class reference.
Try:
db = QSqlDatabase::addDatabase("QIBASE", databaseName);
In main app:
QSqlDatabase db =QSqlDatabase::addDatabase( "QSQLITE");
in second app:
QSqlDatabase db2 =QSqlDatabase::database();

QTreeView / QFileSystemModel set header labels

Pretty simple task but I didn't manage to find anything useful in documentation. I want a QTreeView to contain a single column called "Files" with data from QFileSystemView. Here's what I've got:
QFileSystemModel *projectFiles = new QFileSystemModel();
projectFiles->setRootPath(QDir::currentPath());
ui->filesTree->setModel(projectFiles);
ui->filesTree->setRootIndex(projectFiles->index(QDir::currentPath()));
// hide all but first column
for (int i = 3; i > 0; --i)
{
ui->filesTree->hideColumn(i);
}
That gives me a single column with "Name" header. How do I rename this header?
QAbstractItemModel::setHeaderData() should work. If not, you can always inherit from QFileSystemModel and override headerData().
Quick but a little dirty trick (please note w.hideColumn()):
#include <QApplication>
#include <QFileSystemModel>
#include <QTreeView>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTreeView w;
QFileSystemModel m;
m.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
m.setRootPath("C:\\");
w.setModel(&m);
w.setRootIndex(m.index(m.rootPath()));
w.hideColumn(3);
w.hideColumn(2);
w.hideColumn(1);
w.show();
return a.exec();
}
You can subclass QFileSystemModel and overide method headerData(). For example, if you want only to change first header label and leave the rest with their original values, you can do:
QVariant MyFileSystemModel::headerData(int section, Qt::Orientation orientation, int role) const {
if ((section == 0) && (role == Qt::DisplayRole)) {
return "Folder";
} else {
return QFileSystemModel::headerData(section,orientation,role);
}
}