Open and closing ODBC connections causing crashes - c++

I wrote a program that used a SQLite database and it worked correctly. Now I'm trying to make it work with SQL Server. The application crashes on startup and I have worked out that it's to do with the way I am opening and closing database connections. I'm really unsure if I need to open the connection only once or if should I open and close it each time I run a query? Also is it advised to delete the pointer to the query after it executes? Removing the conn.connOpen and conn.connClose sections makes the program run, but its unstable.
Any advice on how to handle connections (since I have a lot of buttons that execute different queries) is greatly appreciated.
My connection string is stored in a header (mainwindow)
// mainwindows.h
public:
QSqlDatabase mydb;
void connClose()
{
connected = false;
mydb.close();
mydb.QSqlDatabase();
mydb.removeDatabase(QSqlDatabase::defaultConnection);
}
bool connOpen()
{
if( !connected )
{
mydb = QSqlDatabase::addDatabase("QODBC"); //uses dsn, connects fine.
mydb.setDatabaseName("Test");
if(!mydb.open())
{
qDebug() << mydb.lastError().text();
connected = false;
}
else
{
qDebug()<<"Connected";
connected = true;
}
}
return connected;
}
private:
static bool connected;
Here is an example of how I'm calling queries in my .cpp files;
Financelog::Financelog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Financelog)
{
ui->setupUi(this);
setWindowFlags( windowFlags() | Qt::WindowMinimizeButtonHint |
Qt::WindowContextHelpButtonHint | Qt::WindowMinMaxButtonsHint );
MainWindow conn; // call the connection string
if(!conn.connOpen())
ui->label_sec_status->setText("<font color='red'>Failed to Open Database</font>");
else
ui->label_sec_status->setText("<font color='green'>Connected</font>");
QSqlQueryModel * modal=new QSqlQueryModel();
conn.connOpen(); // ---- **DO I NEED THIS? REMOVING STOPS CRASHES.**
QSqlQuery* qry=new QSqlQuery(conn.mydb);
qry->prepare("select DEAL_DATE, DEAL_NUMB, CCICOMM, CCIPREM, INCOME from LOG");
qry->exec();
modal->setQuery(*qry);
ui->tableView->setModel(modal);
ui->tableView->resizeColumnsToContents();
ui->tableView->setAlternatingRowColors(true);
ui->tableView->setStyleSheet("alternate-background-color: #009900; background-color: #006600;");
//delete qry; **DO I NEED THIS TO RELEASE MEMORY?**
conn.connClose(); // **DO I NEED THIS?**
qDebug() << (modal->rowCount());
}

You should only open the connection once, and keep it open while using it. Not open and close for every query.
If you have long phases of nothing between 2 queries, you can use a QTimer to close the connection after beeing unused for a "long" time (e.g. 5 min). Do so if you see the connection timing out. But by default, not needed.
QSqlQuery, just like QSqlDatabase should be used as "value class", not as a pointer (See Qt Documentation). Instead of creating one with new, create it on the stack. Queries are copyable.
Code sample:
//only once, i.e. in your windows constructor
conn.connOpen();
//set up the model
QSqlQueryModel * modal=new QSqlQueryModel();
QSqlQuery qry(conn.mydb);
qry.prepare("...");
qry.exec();
modal->setQuery(qry);
//...
// do not delete the query or close the database connection!
qDebug() << (modal->rowCount());
You can close the connection in the destructor after the model has been destroyed:
model->deleteLater();
conn.connClose();

Related

QT - slot preemption / interrupt

I'm trying to write an app for testing ST board via serial port and I'm currently facing the following issue. (The code below is just a simplification of a problem.)
Widget::Widget(QWidget *parent)
: QWidget(parent)
, m_button(new QPushButton(this))
, m_timer(new QTimer(this))
{
m_timer->setSingleShot(true);
connect(m_button, &QPushButton::released, this, &Widget::RunTest);
connect(m_timer, &QTimer::timeout, this, &Widget::OnTimeout);
}
void Widget::RunTest()
{
qDebug() << "Start test";
m_timer->start(1000);
while (m_timeout != true);
qDebug() << "Start end";
}
void Widget::OnTimeout()
{
qDebug() << "Timeout";
m_timeout = true;
}
I want to have a seprate class for gathering and running tests. The tests are triggered by clicking on a button. Some tests will have to send data via serial port and wait for the reply. I would like to be able to implement a timeout feature (if board doesn't reply then finish test with failure). However I the app is waiting for the m_timeout flag indefinitely. So my question is: Is there any signal/slot mechanism similar to interrupt preemption? If no how sush problems are solved in Qt? Shall I create seprate QTimer object and run it in separate thread?

How to prevent name collisions when creating QSqlDatabase connection in multiple threads

I have multi-threaded QTcpServer and for each database request, it creates new Thread to keep server Responsive. So in each thread I have to creating new QSqlDatabase connection. But I keep getting name collisions between connections.
here is my sample code to recreate issue.:-
#include <QSqlDatabase>
class DBTask
{
public:
DBTask(ClientSocket *socket,ConnectionWorker *connectionWorker);
~DBTask();
static void initStatic();
private:
static QThreadPool *pool; // all addConnection() call be be called in QtConcurrent::run with this pool
static QString host, user, type, password, name;
static quint64 dbConnectionNumber;
QSqlDatabase db;
ClientSocket *socket;
ConnectionWorker *connectionWorker;
bool addDatabase() ;
};
quint64 DBTask::dbConnectionNumber=0;
DBTask::DBTask(ClientSocket *socket, ConnectionWorker *connectionWorker):
socket(socket),
connectionWorker(connectionWorker)
{
dbConnectionNumber++;
}
bool DBTask::addDatabase() {
QSqlDatabase db = QSqlDatabase::addDatabase(type,QString::number(dbConnectionNumber));
db.setHostName(host);
db.setDatabaseName(name);
db.setUserName(user);
db.setPassword(password);
if(!db.open()){
qWarning() << "Error while opening database for socket " << socket << '\n' << db.lastError();
return false;
}
else {
return true;
}
}
this works fine when I manually check my application with GUI with human speed But when I run a c++ test code which simulates thousands of requests like this:-
void connectionTest(){
QThreadPool pool;
pool.setMaxThreadCount(10);
for(int i=0;i<10;i++){
QtConcurrent::run(&pool,[this](){
for(int i=0;i<1000;i++){
login(i%2); // login function sends request to QTcpServer
}
});
}
}
I get multiple errors like this:-
QSqlDatabasePrivate::removeDatabase: connection '10' is still in use, all queries will cease to work.
QSqlDatabasePrivate::addDatabase: duplicate connection name '10', old connection removed.
QSqlDatabasePrivate::removeDatabase: connection '10' is still in use, all queries will cease to work.
QSqlDatabasePrivate::addDatabase: duplicate connection name '10', old connection removed.
and Server crashes with segfault
Even if you make the counter atomic, a thread can still get interrupted in the DBTask::addDatabase method (before creating the connection), another one can increment the counter and then they both continue and create 2 connections with the same id. You need to make both operations (increment the counter and the connection creation) in one transaction: inside the DBTask::addDatabase, by making use of a mutex lock.
After adding QMutex to addDatabase, it works:-
bool DBTask::addDatabase() {
mutex.lock();
dbConnectionNumber++;
db = QSqlDatabase::addDatabase(type,QString::number(dbConnectionNumber));
mutex.unlock();
...
}

How do you run a function in the background of your program (Specifically an autosave function)? QT / C++

In my code I would like to integrate an auto-save function that runs every couple seconds or so. I would like this to run in the background because I have other stuff that I am going to be running at the same time. So how would I do this?
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <fstream>
#include <QFile>
#include <QDebug>
using namespace std;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow) {
ui->setupUi(this);
// Setup code
ui->textEdit->setReadOnly(true);
ui->textEdit->append("Select one of the buttons on the left to pick a log");
}
MainWindow::~MainWindow() {
delete ui;
}
string lastSavedText[] = {
" ",
" "
};
QString qLastSavedTextHome, qLastSavedTextWork;
This is my first button
void MainWindow::on_homeButton_clicked() {
// Preparing text edit
ui->textEdit->setReadOnly(false);
ui->textEdit->clear();
ui->textEdit->setOverwriteMode(true);
// Loading previously saved text
QFile file { "home.apl" };
if ( !file.open(QIODevice::ReadOnly | QIODevice::Text) ) {
qDebug() << "Could not open file!";
return;
}
const auto& lastSavedText = file.readAll();
file.close();
ui->textEdit->setPlainText( lastSavedText );
}
This is my second one
void MainWindow::on_workButton_clicked() {
// Preparing text edit
ui->textEdit->setReadOnly(false);
ui->textEdit->clear();
ui->textEdit->setOverwriteMode(true);
// Loading previously saved text
QFile file2 { "work.apl" };
if ( !file2.open(QIODevice::ReadOnly | QIODevice::Text) ) {
qDebug() << "Could not open file!";
return;
}
const auto& lastSavedText = file2.readAll();
file2.close();
ui->textEdit->setPlainText( lastSavedText );
}
This is the save button I hope to eliminate with an autosave
void MainWindow::on_saveButton_clicked() {
// Converts textEdit to string
QString textEditText = ui->textEdit->toPlainText();
lastSavedText[0] = textEditText.toStdString();
// Saving files
ofstream home;
home.open("home.apl");
home << lastSavedText[0];
home.close();
ofstream work;
work.open("work.apl");
work << lastSavedText[1];
work.close();
}
There is 2 solutions.
Easy one
Use simply a timer that will execute the code of your save button. You can set the timer to execute any period of time.
QTimer
But this might cause the software to freeze if this operation takes too much time. In which case, you can put the function that saves inside a thread.
Threads
You can use threads to do that.
Thread, is basically a process that will detach from your main process and can be run at the same time, each thread doing its own work.
Note that to communicate between thread, the safest method is to use signals.
Qt Threads Documentation
Example
void MyObject::startWorkInAThread()
{
WorkerThread *workerThread = new WorkerThread(this);
connect(workerThread, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString)));
connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
workerThread->start();
}
You can use a QTimer with QtConcurrent::run, and then you get the simplicity with the benefit of running the saving on a different thread you don't need to manage.
Practically, try
QTimer::singleShot(time, this, Qt::TimerType::CoarseTime, QtConcurrent::run(this,&MainWindow::on_saveButton_clicked));
Here's a first approximation using a background thread (for the sake of brevity, it inherits QThread - for your real application, consider decoupling the QThread base-class from this worker thread object. That will also make it possible to give a father-object for t).
class Thread: public QThread {
Q_OBJECT
public:
Thread(QTextEdit *textEdit):textEdit(textEdit) {
QTimer *t = new QTimer;
connect(t, SIGNAL(timeout()), SLOT(saveOnce()));
t->moveToThread(this);
t->start(2000);
}
protected:
QTextEdit *textEdit;
std::string lastSavedText[2];
private slots:
QString text() const { return textEdit->toPlainText(); }
void saveOnce() {
QString textEditText;
QMetaObject::invokeMethod(this,
"text", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QString,textEditText));
lastSavedText[0] = textEditText.toStdString();
// Saving files
ofstream home;
home.open("home.apl");
home << lastSavedText[0];
home.close();
ofstream work;
work.open("work.apl");
work << lastSavedText[1];
work.close();
}
};
Care must be taken, when taking this approach with BlockingQueuedConnection, that the thread does not call invokeMethod while the main thread is waiting for it to exit - then a deadlock happens because the main-thread cannot process the text() queued call anymore.

PostgreSQL opening other database failed

Well, the problem is, when I close previous database and try to connect to the new one I get this:
Connection failed!
unterminated quoted string in connection info string
QPSQL:Unable to connect
Here full description of what I'am doing:
First I logging in with any avalible login and connect to the default "posgtres" database. Then I executing query select * from pg_database; to get list of all avalible databases. After that I close database.
void FdbToPg::connectToDatabase(){
database.setHostName(ui.lineIP->text());
database.setDatabaseName("postgres");
database.setUserName(ui.lineLogin->text());
database.setPassword(ui.linePassword->text());
database.setPort(ui.linePort->text().toInt());
QSqlQuery query;
if(database.open()){
QString dbOutput = "select * from pg_database;";
query.exec(dbOutput);
while(query.next()){
ui.comboBox->addItem(query.value(0).toString(),QVariant::Char);
}
database.close();
}else{
QMessageBox::information(this, "Error", "Cant' connect ot the database");
}
}
Then when on of the avalible databases been choosed I trying to connect to it. And there I get that error message
void FdbToPg::on_selectButton_clicked(){
database.setDatabaseName(ui.comboBox->itemData(ui.comboBox->currentIndex()).toString());
database.setHostName(ui.lineIP->text());
database.setUserName(ui.lineLogin->text());
database.setPassword(ui.linePassword->text());
database.setPort(ui.linePort->text().toInt());
bool ok = database.open();
if(ok != true){
QMessageBox::information(this, "Connection", "Connection failed! \n" + database.lastError().text());
} else {
QMessageBox::information(this, "Connection", "Connection worked!");
}
}
What's wrong here and how I properly suppose to close old database and open new one?
Well, the problem were here
database.setDatabaseName(ui.comboBox->itemData(ui.comboBox->currentIndex()).toString());
ui.comboBox->itemData(ui.comboBox->currentIndex()).toString() were return an empty string. I changed it to database.setDatabaseName(ui.comboBox->currentIndex()); and now it's ok.
But I still don't understand whats wrong with
ui.comboBox->itemData(ui.comboBox->currentIndex()).toString() command

Why can't I delete the SQLite database in Qt even after I call the database close function?

I'm writing a code to work with a SQLite database, all the code of the database works, but at end, when I try to delete the database, the database file is not deleted. I've called the close function, so why can't I delete the database?
The db.isOpen(); function returns false, and the QFile::remove(path); returns false too.
Note: On Windows Explorer i can delete the database only after closing the program window.
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
QString path = "path_to_db";
db.setDatabaseName(path);
if(db.open())
{
qDebug() << "Opened";
qDebug() << path;
}
else
{
qDebug() << db.lastError();
return a.exec();
}
QSqlQuery query(db);
//code to work with the database
db.close();
qDebug() << db.isOpen();
qDebug() << QFile::remove(path);
I found the solution, simply call query.clear(); before close the connection.