I want to insert data into an SQLite database using QSqlDatabase and QSqlTableModel.
The database is being created in an SqlDatabaseController class:
SqlDatabaseController.h
// ...
class SqlDatabaseController : public QObject
{
Q_OBJECT
// ...
private:
QSqlDatabase _database;
SqlContactModel* _contactModel { nullptr };
// ...
SqlDatabaseController.cpp
SqlDatabaseController::SqlDatabaseController(QObject *parent) : QObject(parent)
{
// create database driver
_database = QSqlDatabase::database();
_contactModel = new SqlContactModel(this, _database);
}
// ...
void SqlDatabaseController::connectToDatabase(const QString &name, const QString &pwd)
if (!_database.isValid()) {
_database = QSqlDatabase::addDatabase("QSQLITE");
if (!_database.isValid()) {
qFatal("[SQL] SqlDatabaseController: Cannot add database: %s", qPrintable(_database.lastError().text()));
}
}
const QDir writeDir { QStandardPaths::writableLocation(QStandardPaths::AppDataLocation).append("/").append(name) };
if (!writeDir.exists() && !writeDir.mkpath(".")) {
qFatal("[SQL] SqlDatabaseController: Failed to create writable directory at %s", qPrintable(writeDir.absolutePath()));
}
// Ensure that we have a writable location on all devices.
const QString fileName { writeDir.absolutePath() + "/db.sqlite3" };
// When using the SQLite driver, open() will create the SQLite database if it doesn't exist.
_database.setDatabaseName(fileName);
if (!_database.open()) {
qFatal("[SQL] SqlDatabaseController: Cannot open database: %s", qPrintable(_database.lastError().text()));
QFile::remove(fileName);
return;
}
emit databaseConnected();
}
For model data storing, I'm subclassing QSqlTableModel:
SqlContactModel.cpp
SqlContactModel::SqlContactModel(QObject* parent, QSqlDatabase db) :
QSqlTableModel(parent, db),
_tableName("Contacts")
{
}
As you can see, I'm passing the database to the TableModel so it's able to use this database for all queries.
Afterwards, I'm creating the tables:
SqlContactModel.cpp
void SqlContactModel::create()
{
createTable();
setTable(_tableName);
setSort(2, Qt::DescendingOrder);
// Ensures that the model is sorted correctly after submitting a new row.
setEditStrategy(QSqlTableModel::OnManualSubmit);
}
// ...
void SqlContactModel::createTable()
{
qDebug() << "[SQL] Creating table" << _tableName << "for database" << QSqlDatabase::database().databaseName() << ".";
if (QSqlDatabase::database().tables().contains(_tableName)) {
qDebug() << "[SQL] Database table" << _tableName << "already exists. Exiting createTable().";
emit contactTableCreated();
return;
}
QSqlQuery query { "SOME_QUERY" };
if (!query.exec()) {
qFatal("[SQL] SqlContactModel: Failed to create table: %s", qPrintable(query.lastError().text()));
return;
}
if (!submitAll()) {
qFatal("[SQL] SqlContactModel: Failed to submit createTable query: %s", qPrintable(lastError().text()));
}
emit contactTableCreated();
qDebug() << "Done creating table" << _tableName << ".";
}
Now, I want to insert something into my table like this:
SqlContactModel.cpp
// ...
void SqlContactModel::addContact(const QString& contactName, const QString& id, const QByteArray& avatar)
{
QSqlQuery query;
query.prepare("INSERT OR REPLACE INTO '" +
_tableName +
"'(id, name" +
(avatar.isEmpty() ? "" : ", avatar") +
") VALUES (:Id, :Name" + (avatar.isEmpty() ? "" : ", :Avatar") +
")");
query.bindValue(":Id", id);
query.bindValue(":Name", contactName);
if (!avatar.isEmpty()) {
query.bindValue(":Avatar", avatar);
}
if (!query.exec()) {
qFatal("[SQL] SqlContactModel: Unable to execute query in addContact: %s", qPrintable(query.lastError().text()));
return;
}
for (auto const& table : QSqlDatabase::database().tables()) {
qDebug() << "Table: " << table;
}
if (!submitAll()) { // <-- here's where it breaks
qFatal("[SQL] SqlContactModel: Failed to submit addContact query: %s", qPrintable(lastError().text()));
return;
}
qDebug() << "[SQL] Added contact " << contactName << "(" << id << ") to table " << _tableName << ".";
// emit contactListChanged();
}
The query executes just fine and the data is being stored in the database, however since I'm using setEditStrategy(QSqlTableModel::OnManualSubmit); on my model, I would need to call submitAll() on my model to be updated properly. When calling submitAll(), however, I keep getting an error:
[SQL] SqlContactModel: Failed to submit addContact query: Unable to find table Contacts.
As you can see in the code above, I iterated through all tables of the database and I get the following output:
Table: "Contacts"
Table: "Conversations"
which is why I'm quite sure the table does exist.
Is this some kind of encoding error or why is submitAll() not able to find my table Contacts in the database? What am I missing here?
Thanks in advance.
bool run_query(sqlite3* db, const std::string& sql, std::vector< user_record >& records)
{
// clear any prior results
records.clear();
char* error_message;
if(sqlite3_exec(db, sql.c_str(), callback, &records, &error_message) != SQLITE_OK)
{
std::cout << "Data failed to be queried from USERS table. ERROR = " << error_message << std::endl;
sqlite3_free(error_message);
return false;
}
return true;
}
How do I fix this method to fail and display an error if there is a suspected SQL Injection?
My code is as follow:
main.cc
int main(void){
int port, sqlPort, ConnectionNum, threadNum;
string sqlUser, sqlPwd, DBName;
try{
Config Settings("./config.ini");
Settings.Read("port",port);
Settings.Read("sqlPort",sqlPort);
Settings.Read("sqlUser",sqlUser);
Settings.Read("sqlPwd",sqlPwd);
Settings.Read("DBName",DBName);
Settings.Read("ConnectionNum",ConnectionNum);
Settings.Read("threadNum",threadNum);
}catch(File_Not_Found& e){
cerr << "Cannot Find the Configuration File:" << e.what() << endl;
exit(1);
}catch(Key_Not_Found& e){
cerr << "Cannot Find the Keyword:" << e.what() << endl;
exit(1);
}
WebServer server(port, 5000, sqlPort, sqlUser, sqlPwd,DBName, ConnectionNum, threadNum );
//server.Start();
return 0;
}
WebServer.cc
WebServer::WebServer(int port, int timeoutMS, int sqlPort,
const std::string& sqlUser, const std::string& sqlPwd,
const std::string& dbName, int connPoolNum, int threadNum)
:m_port(port), m_timeoutMS(timeoutMS),
m_epoller(new Epoller()),m_timer(new TimerHeap()){
assert(port < 65535 && port > 1024);
m_srcDir = get_current_dir_name();
assert(m_srcDir);
strncat(m_srcDir, "/resources/", 16);
HttpConn::userCnt = 0;
HttpConn::srcDir = m_srcDir;
connPool->init("localhost", sqlUser, sqlPwd, dbName, sqlPort, connPoolNum);
threadPool->start(threadNum);
m_isRunning = InitSocket();
if(m_isRunning){
LOG_INFO << "===================== Server Init Success =====================";
LOG_INFO << "Port : " << m_port << ", srcDir :" << m_srcDir;
LOG_INFO << "threadNum : " << threadNum << ", ConnectionNum : " << connPoolNum;
}else{
LOG_FATAL << "Socket Init Failure";
}
}
SqlConnectionPool.cc
ConnectionPool *ConnectionPool::GetInstance()
{
static ConnectionPool connPool;
return &connPool;
}
void ConnectionPool::init(string url, string User, string PassWord, string DBName, int Port, int MaxConn)
{
m_url = url;
m_Port = Port;
m_User = User;
m_PassWord = PassWord;
m_DatabaseName = DBName;
for (int i = 0; i < MaxConn; i++)
{
MYSQL *con = NULL;
con = mysql_init(con); //segmentation fault happened!!
if (con == NULL)
{
LOG_FATAL << "MySQL Init Error";
}
con = mysql_real_connect(con, url.c_str(), User.c_str(), PassWord.c_str(), DBName.c_str(), Port, NULL, 0);
if (con == NULL)
{
LOG_FATAL << "MySQL Get Real Connection Error";
}
connList.push_back(con);
++m_FreeConn;
}
m_MaxConn = m_FreeConn;
}
As is shown in the picture above, the pointer con is always nullptr and it leads to segmentation fault. And I write another code to test SqlConnectionPool.cc file, but this time it works perfectly.
SqlConnectionPool_test.cc(part of code)
ConnectionPool* connPool = ConnectionPool::GetInstance();
unordered_map<string, string> users;
vector<thread> thread_pool;
int main(void){
connPool->init("localhost", "root", "123123", "zjwdb", 3306, 8);
{
MYSQL *mysql = NULL;
connectionRAII mysqlcon(&mysql, connPool);
if(mysql_query(mysql, "CREATE TABLE user_test(username VARCHAR(100) NOT NULL,passwd VARCHAR(60) NOT NULL,PRIMARY KEY ( username ));")){
LOG_ERROR << "CREATE TABLE user_test Failed : " << mysql_error(mysql);
}
if(!Register("test1", "test1")){
LOG_ERROR << "INSERT test user Failed";
}
if(!Register("test2", "test2")){
LOG_ERROR << "INSERT test user Failed";
}
if(!Register("test3", "test3")){
LOG_ERROR << "INSERT test user Failed";
}
showUsers(mysql);
}
CountDownLatch latch(4);
thread_pool.emplace_back(login, "test1", "test1");
thread_pool.emplace_back(login, "test5", "test5");
thread_pool.emplace_back(login, "test1", "test");
thread_pool.emplace_back([](CountDownLatch& latch, string const& name, string const& passwd)
{
latch.countDown();
latch.wait();
Register(name, passwd);
login(name, passwd);
}
, std::ref(latch), "test_user1", "123123");
thread_pool.emplace_back([](CountDownLatch& latch, string const& name, string const& passwd)
{
latch.countDown();
latch.wait();
Register(name, passwd);
login(name, passwd);
}
, std::ref(latch), "test_user2", "123123");
thread_pool.emplace_back([](CountDownLatch& latch, string const& name, string const& passwd)
{
latch.countDown();
latch.wait();
Register(name, passwd);
login(name, passwd);
}
, std::ref(latch), "test_user3", "123123");
thread_pool.emplace_back([](CountDownLatch& latch, string const& name, string const& passwd)
{
latch.countDown();
latch.wait();
Register(name, passwd);
login(name, passwd);
}
, std::ref(latch), "test3", "test3");
for(auto& th : thread_pool){
if(th.joinable())
th.join();
}
MYSQL* conn_temp = NULL;
connectionRAII mysqlconn_temp(&conn_temp, connPool);
showUsers(conn_temp);
dropTheTable();
return 0;
}
That really confuse me. Is it a bug of libmysql.lib?? If it is, how should I slove it?
PS:
My Ubuntu version is Ubuntu-16.04 and the lib version is libmysqlclient.so.20.
The SqlConnectionPool_test.cc is running in a multithread envirnment. Reason has it that this test file is more likely to crash becuase mysql_init is not thread-safe. But it can work perfectly.
Please let me know if you need more information, thanks!
Im trying to find out what goes wrong when doing a get request in qt.
I have the following slots attached to my networkmanager:
connect(mgr,SIGNAL(finished(QNetworkReply*)),this,SLOT(requestFinished(QNetworkReply*)));
connect(mgr, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotError(QNetworkReply::NetworkError)));
The request finished is like:
void FirebaseInteractor::requestFinished(QNetworkReply *rep)
{
QVariant statusCode = rep->attribute( QNetworkRequest::HttpStatusCodeAttribute );
int status = statusCode.toInt();
if ( status != 200 )
{
QString reason = rep->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString();
qDebug() << "Pushnotification Request failed : " <<reason;
}
else{
qDebug() << "Pushnotification has been send: ";
}
if ( !statusCode.isValid() )
{
QString status = statusCode.toString(); // or status_code.toInt();
qDebug() << "Failing " << status;
int code = statusCode.toInt();
qDebug() << "Pushnotification Request failed invalid status code." << QString::number(code);
QString reason = rep->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString();
qDebug() << "reason " << reason;
return;
}
}
However Status is always empty Failing and reason is printed but there is no value after it (i was expecting a reason e.g. timeout, 401 etc).
I also tried:
int status = statusCode.toInt();
if ( status != 200 )
{
QString reason = rep->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString();
qDebug() << "Pushnotification Request failed : " <<reason;
}
else{
qDebug() << "Pushnotification has been send: ";
}
But all reasons/codes are empty.
I also added:
void FirebaseInteractor::slotError(QNetworkReply::NetworkError error)
{
qDebug() << "slotError" << error;
}
but this is not called.
How can I find out whats going wrong?
You can get the error directly using:
qDebug() << reply->error();
This won't work if the network request never happened:
if ( !statusCode.isValid() )
Because this means that the QVariant itself is invalid and has type QMetaType::UnknownType Documentation. Hence it will not give any information about what went wrong in the http request. To fix this, here's a simple example:
if (statusCode.isValid()) { // it needs to be valid
qDebug() << "Status Code: " << statusCode.toString();
}
Alternatively, you can switch on QNetworkReply::Error()
void FirebaseInteractor::requestFinished(QNetworkReply *rep)
{
switch (rep->error())
{
case QNetworkReply::NoError:
// No error
return;
case QNetworkReply::TimeoutError:
{
auto httpStatus =
reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
auto httpStatusMessage = reply->attribute(
QNetworkRequest::HttpReasonPhraseAttribute).toByteArray();
//...
break;
}
}
}
Juggling between receiving 2 different errors.
void summary::on_pushButton_saveSummary_clicked()
{
if(db.open())
{
query.exec("insert or replace into [PN:"+partNum+" CN:"+chargeNum+"](total, defects, rust) values(1, 2, 3)");
if (!query.exec())
{
qDebug() << query.lastError();
qDebug() << query.exec()<<endl;
}
}
else
{
qDebug() << db.lastError();
}
}
The above gives error: QSqlError("", "Unable to fetch row", "No query")
While:
void summary::on_pushButton_saveSummary_clicked()
{
if(db.open())
{
qDebug() << "db open";
int a = 3;
int b = 1;
int c = 3;
query.prepare("insert into [PN:"+partNum+" CN:"+chargeNum+"](total, defects, rust) values(:total, :defects, :rust)");
query.bindValue(":total", a);
query.bindValue(":defects", b);
query.bindValue(":rust", c);
if (!query.exec())
{
qDebug() << query.lastError();
qDebug() << query.exec()<<endl;
}
}
else
{
qDebug() << db.lastError();
}
}
Yields: QSqlError("", "Parameter count mismatch", "")
The Constructor has:
db.setDatabaseName("/home/igraves/Databases/testdb");
db.open();
QString partNum = "134345";
QString chargeNum = "3452";
query.prepare("create table if not exists [PN:"+partNum+" CN:"+chargeNum+"](total int, defects int, rust int)");
query.exec();
The table is being create, I can see it. So I am guessing syntax? Although it is as the Qt wiki writes it...
Edit:
Adding some .h stuff
QString partNum;
QString chargeNum;
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
QSqlQuery query;
Answer: It was my QString variables being temporary, after the constructor was done, they went out of scope. Changed my .h
QString partNum = "124124";
QString chargeNum = "234234";
I see at least the following errors:
You do not set the database path.
Do not open the database when creating the table.
"134345" and "3452" are being assigned to temporary variables instead of class members
Considering the above, the following must be in the constructor:
partNum = "134345";
chargeNum = "3452";
db.setDatabaseName("/path/of/database.db");
if(db.open())
query.exec(QString("create table if not exists [PN:%1 CN:%2](total int, defects int, rust int)")
.arg(partNum)
.arg(chargeNum));