App Crashing - probably because of SqlDataAccess - c++

I am using Blackberry Cascades.
I am trying to access my database to insert/edit etc. my database but the app crashes the moment I try to call on the query function favourite().
The app crashes and nothing gets logged in the console so I am not sure what exactly is causing it.
I have also declared in the header: SqlDataAccess * sda
#include "dbHelper.h"
#include <bb/data/SqlDataAccess>
#include <bb/data/DataAccessError>
#include <QtSql/QtSql>
#include <QDebug>
dbHelper::dbHelper()
{
initDatabase();
SqlDataAccess sda (mdbFilewithPath);
// or should I use: this-> sda = new SqlDataAccess();
}
void dbHelper::initDatabase(){
//Copy database to Data folder ... pretty sure this part of the code works.
mdbFilewithPath = QDir::homePath() + "/data.db";
}
void dbHelper::executeQuery (const QString query){
this->sda->execute(query);
if (this->sda->hasError()){
DataAccessError err = this->sda->error();
qWarning() << "SQL Error: " << err.errorType() <<err.errorMessage();
}
}
void dbHelper::favourite(const QString &rule_id)
{
QVariantMap rule;
rule["id"] = rule_id;
QString query ="INSERT INTO user (rule_id, isFavourite) VALUES (" + rule_id + ", 1)";
executeQuery(query);
}
I am not sure what is causing it? Would appreciate any help thanks!

Related

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.

facing issue to print data of a variable outside of the if statement done programming using qt

In Qt I'm trying to read some data using QStringList
like,
input:
name:xxxxx
id:yyyy
college:zzzzz
name:bbbb
.....will repeat with the same keywords
So, I'm searching these data by QStringList with tokens split ":" then in if statement I'm checking the keywords whether contains "name" or "id" and have fed data to QString. When after all data storage I'm appending these data like xxxx+yyyy+zzzz; these I'm trying to return as a QString then before this I'm trying to print appended string I'm getting data as
example:- xxxxx, "", ""
"" ,yyyyy,"" like this can anyone spot the issue would be helpful
I don't know how are you trying to do this without your code. If i get you right, you are doing something like this...
#include <QDebug>
#include <QStringList>
int main() {
QStringList strings;
strings.append("name:xxxxx");
strings.append("id:yyyy");
strings.append("college:zzzzz");
QString name;
QString college;
QString id;
foreach (const auto &str, strings) {
if (str.contains("name")) {
name = str.split(":").at(1);
}
if (str.contains("college")) {
college = str.split(":").at(1);
}
if (str.contains("id")) {
id = str.split(":").at(1);
}
}
QString finalString = name + id + college;
qDebug() << finalString;
return 0;
}
Output is "xxxxxyyyyzzzzz". I hope this helps.

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

QSqlDatbase is closed before the query finishes

I am trying to implement a database object for my application, but it became a nightmare when I started using multiple connections. Below, you can see my database C++ class code:
// here are the declarations
QString server_addr;
QString username;
QString password;
QString database_name;
QSqlDatabase connection;
QString error;
QString connectionName;
QSqlQuery m_query;
// and here are the definitions
database::database(QString connectionName) {
preferences p; p.read();
QString iConnectionName = (connectionName == "") ? default_connection_name : connectionName;
this->connectionName = iConnectionName;
if (QSqlDatabase::contains(iConnectionName))
this->connection = QSqlDatabase::database(iConnectionName);
else this->connection = QSqlDatabase::addDatabase("QMYSQL", iConnectionName);
this->connection.setHostName(p.database->server_addr);
this->connection.setUserName(p.database->username);
this->connection.setPassword(p.database->password);
this->connection.setDatabaseName(p.database->database_name);
this->connection.setPort(p.database->serverPort);
if (!connection.open())
{
this->error = this->connection.lastError().text();
}
else this->error = "";
}
QSqlQuery database::query(QString query_text) {
this->m_query = QSqlQuery(query_text, this->connection);
this->m_query.exec();
return m_query;
}
database::~database() {
if (!this->m_query.isActive()) QSqlDatabase::removeDatabase(this->connectionName);
qDebug() << "database object destroyed\n";
}
The problem occurs in a another class (that uses the database):
databaseAdaptor::databaseAdaptor() {
this->db = database();
// other construction operations
}
void databaseAdaptor::fetch() {
QSqlQuery q = db.query("SELECT * FROM `activities`");
qDebug() << "Rows count: " << q.numRowsAffected();
}
It worked some versions in the past, but for some reason, now the output from qDebug() is
Rows count: -1 // and it should be 2 for my current version of database.
In the Qt Documentation, it is said that this class' members are thread-safe. Could this be the problem? I mean, could the thread end before the queries finish their execution?
If so, how can I keep the connection open until all the queries finished their execution?
(I know it's much to read, but I am sure that other might have this problem at some point, so please take your time and read it). Thank you!

Can multiple WT application run on the same webpage?

so recently I asked a question to see if Can multiple WT applications run on same port? and the answer was a yes, (+1 to Jorge Núñez for that awesome answer). However, now I am trying to take his solution a step further to see if multiple WT applications can be run on the same page, by embedding them in a kind of host WT application. What I have done is created a host WT application that has an accessor for its root() and has a WTabWidget. Then in the CreateHostApplication function I created tabs for the 2 test applications, which also have accessors for their root(), and added their root() to the tab that belongs to my host application, then after all the apps were added to their individual tabs, I returned the host.
The cool part is that the widgets from the test applications showed up in their tabs as I expected, what I didn't expect is that the connect() calls to connect buttons to functions failed. So the widgets are functional as far as clicking them and editing text in boxes goes, but they do nothing else as they are not connected to any custom functions.
After debugging this for a bit, am pretty sure the connect calls failed on the test apps because they are not actually hosted by the browser, which brings me here. I have not been able to find a solution to this problem, is there a way to get the connect calls functional with this setup?
The code below is the solution by Jorge Núñez, with the above mentioned modifications. I am using visual studio2010 for development. Thank you in advance for any help!
#include <Wt/WApplication>
#include <Wt/WBreak>
#include <Wt/WContainerWidget>
#include <Wt/WLineEdit>
#include <Wt/WPushButton>
#include <Wt/WText>
#include <Wt/WException>
#include <Wt/WLogger>
#include <Wt/WServer>
#include <Wt/WTabWidget>
using namespace Wt;
class Host : public Wt::WApplication
{
public:
Host(const Wt::WEnvironment& env);
WContainerWidget* GetRoot()
{
return root();
}
};
Host::Host(const Wt::WEnvironment& env) : Wt::WApplication(env)
{
}
class TestApp1 : public Wt::WApplication
{
public:
TestApp1(const Wt::WEnvironment& env, const std::string& title);
WContainerWidget* GetRoot()
{
return root();
}
private:
Wt::WLineEdit* _name_edit;
Wt::WText* _greeting;
void Greet();
};
TestApp1::TestApp1(const Wt::WEnvironment& env, const std::string& title) : Wt::WApplication(env)
{
setTitle(title);
root()->addWidget(new Wt::WText("Your name, please ? "));
_name_edit = new Wt::WLineEdit(root());
Wt::WPushButton* button = new Wt::WPushButton("Greet me.", root());
root()->addWidget(new Wt::WBreak());
_greeting = new Wt::WText(root());
button->clicked().connect(this, &TestApp1::Greet);
}
void TestApp1::Greet()
{
_greeting->setText("Hello there, " + _name_edit->text());
}
class TestApp2 : public Wt::WApplication
{
public:
TestApp2(const Wt::WEnvironment& env, const std::string& title);
WContainerWidget* GetRoot()
{
return root();
}
private: Wt::WLineEdit *_name_edit;
Wt::WText *_greeting;
void greet();
};
TestApp2::TestApp2(const Wt::WEnvironment& env, const std::string& title) : Wt::WApplication(env)
{
setTitle(title);
root()->addWidget(new Wt::WText("Your name, please ? "));
_name_edit = new Wt::WLineEdit(root());
Wt::WPushButton* button = new Wt::WPushButton("Say goodbye.", root());
root()->addWidget(new Wt::WBreak());
_greeting = new Wt::WText(root());
button->clicked().connect(this, &TestApp2::greet);
}
void TestApp2::greet()
{
_greeting->setText("Goodbye, " + _name_edit->text());
}
Wt::WTabWidget* tab_widget;
Wt::WApplication* CreateHostApplication(const Wt::WEnvironment& env)
{
Host* host = new Host(env);
WContainerWidget* root = host->GetRoot();
tab_widget = new WTabWidget(root);
//Create tab for the app
WContainerWidget* Tab_TestApp1 = new WContainerWidget();
//Get a pointer to the ACE tab
tab_widget->addTab(Tab_TestApp1, "Test Application 1", Wt::WTabWidget::LoadPolicy::PreLoading);
//Create app
TestApp1* test_app_1 = new TestApp1(env, "Test Application 1");
//Add app root to the tab
Tab_TestApp1->addWidget(test_app_1->GetRoot());
//Create tab for the app
WContainerWidget* Tab_TestApp2 = new WContainerWidget();
//Get a pointer to the ACE tab
tab_widget->addTab(Tab_TestApp2, "Test Application 2", Wt::WTabWidget::LoadPolicy::PreLoading);
//Create app
TestApp2* test_app_2 = new TestApp2(env, "Test Application 2");
//Add app root to the tab
Tab_TestApp2->addWidget(test_app_2->GetRoot());
return host;
}
Wt::WApplication* CreateTestApp1(const Wt::WEnvironment& env)
{
return new TestApp1(env, "Test Application 1");
}
Wt::WApplication* CreateTestApp2(const Wt::WEnvironment& env)
{
return new TestApp2(env, "Test Application 2");
}
int TestWRun(int argc, char* argv[],
Wt::ApplicationCreator host_application,
std::vector<Wt::ApplicationCreator> applications)
{
try
{
// use argv[0] as the application name to match a suitable entry
// in the Wt configuration file, and use the default configuration
// file (which defaults to /etc/wt/wt_config.xml unless the environment
// variable WT_CONFIG_XML is set)
Wt::WServer server(argv[0],"");
// WTHTTP_CONFIGURATION is e.g. "/etc/wt/wthttpd"
server.setServerConfiguration(argc, argv, WTHTTP_CONFIGURATION);
// add a single entry point, at the default location (as determined
// by the server configuration's deploy-path)
server.addEntryPoint(Wt::Application, host_application);
unsigned int num_apps = applications.size();
for(unsigned int cur_app = 0; cur_app < num_apps; ++cur_app)
{
server.addEntryPoint(Wt::Application, applications[cur_app], "/" + boost::lexical_cast<std::string>(cur_app));
}
if (server.start())
{
int sig = Wt::WServer::waitForShutdown(argv[0]);
std::cerr << "Shutdown (signal = " << sig << ")" << std::endl;
server.stop();
}
}
catch (Wt::WServer::Exception& e)
{
std::cerr << e.what() << "\n";
return 1;
}
catch (std::exception& e)
{
std::cerr << "exception: " << e.what() << "\n";
return 1;
}
}
int main(int argc, char** argv)
{
std::vector<Wt::ApplicationCreator> applications;
applications.push_back(&CreateTestApp1);
applications.push_back(&CreateTestApp2);
return TestWRun(argc, argv, &CreateHostApplication, applications);
}
While it's possible with Wt, your approach is not 100% correct. AFAIK you have two options:
Use Wt's widgetset mode. See how the chat widget is integrated on Wt's home page: these are two independent Wt applications, that are shown simultaneously on the same page, and that are both active. Wt's widgetset mode is best compared to the Google maps widget: you put a small placeholder (a div + some JS) on a webpage where you want the application to be rendered. See examples/wt-home, examples/feature/widgetset
There is no problem at all in Wt to reuse widgets from one application in another application. Put the part that you want to reuse all together in a WContainerWidget (or a WCompositeWidget), and integrate that in your tabwidgets. Actually, that resembles pretty much to what you're doing now, but instead of taking the root() of TestApp1, organise your code so that you'd only put a single widget in the root of TestApp1, and use the same widget in the tabs of the Host application. In createApplication you must not instantiate more than one WApplication object. Widgetgallery uses this approach to integrate parts of the charts example.