How to delete a row from SQLite database using QSqlQueryModel? - c++

I am trying to delete a row from QSqlQueryModel as follows:
void MainWindow::deleteRecord()
{
int row_index= ui->tableView->currentIndex().row();
model->removeRow(row_index);
}
But it is not working.
I tried the following as well:
void MainWindow::deleteRecord()
{
int row_index= ui->tableView->currentIndex().row();
if(!db_manager->delete_record(QString::number(row_no))){
ui->appStatus->setText("Error: data deletion ...");
} else{
ui->appStatus->setText("Record deleted ...");
}
}
Where in db_manager, the function delete_recod(QString row_no) is:
bool DatabaseManager::delete_record(QString row_index)
{
QSqlQuery query;
query.prepare("DELETE FROM personal_Info WHERE ref_no = (:ref_no)");
query.bindValue(":ref_no",row_index);
if (!query.exec())
{
qDebug() << "Error" << query.lastError().text();
return false;
}
return true;
}
But also not working. In both attempts, the application doesn't crash and no SQLite errors.
What am I doing wrong and how can I fix it?

The first approach is failing because QSqlQueryModel does not implement removeRows. You're not checking its return value (bad! bad! bad!), which is false, meaning failure.
And how could it possibly implement a row removal function? Your SQL query can be literally anything, including result sets for which it does not make any sense to remove rows.
Instead, consider using a QSqlTableModel -- it or may not apply to your case, but given the form of your DELETE statement, I would say it does. (QSqlTableModel only shows the contets of one table / view).
The second approach is instead possibly working already. The fact you don't see your UI updated does not imply anything at all -- you should check the actual database contents to see if the DELETE statement actually worked and deleted something.
Now note that there's nothing coming from the database telling Qt to update its views. You need to set up that infrastructure. Modern databases support triggers and signalling systems (which are wrapped in Qt by QSqlDriver::notification), which can be used for this purposes. In other cases you msut manually trigger a refresh of your SQL model, for instance by calling QSqlTableModel::select().

Related

Can I use the same QSQLQuery variable to execute multiple statements?

Can I use the same QSQLQuery variable to execute multiple statements in Qt 5.3.2 using SQLite? Should I call the finish or the clear function after each execution?
For example:
QSqlQuery query;
query.prepare("INSERT INTO myTable (id, name) VALUES (:id, :name)");
query.bindValue(":id", id);
query.bindValue(":name", name);
if( !query.exec() )
{
qDebug() << "Error";
}
query.finish() // or query.clear()?
query.prepare("INSERT INTO myProducts (product, price) VALUES (:product, :price)");
query.bindValue(":product", product);
query.bindValue(":price", price);
if( !query.exec() )
{
qDebug() << "Error";
}
query.finish() // or query.clear()?
Note: I think I need to use the finish function, but I didn't understand exactly what the clear function does. The documentation says:
Clears the result set and releases any resources held by the query.
Sets the query state to inactive.
There is no need to use any of these functions in your case, you can do just fine without any of the two lines you are asking about
Can I use the same QSQLQuery variable to execute multiple statements in Qt 5.3.2 using SQLite?
Of course you can, but you are not obligated to do so. For example, If you wanted to perform an SQL query and it happens that you already have a valid QSqlQuery object around that you are finished with (you are not willing to fetch any more data from it), you can just use that same QSqlQuery object with your new query as there is no need to create another QSqlQuery.
Should I call the finish or the clear function after each execution?
Here is what the docs say about QSqlQuery::finish():
Instruct the database driver that no more data will be fetched from this query until it is re-executed. There is normally no need to call this function, but it may be helpful in order to free resources such as locks or cursors if you intend to re-use the query at a later time.
This means that you only need to use it if you want to keep a QSqlQuery object that you are finished with for a long time, in order to use it. But there is really no need to do so, just let your object go out of scope when you are finished with it.
And about QSqlQuery::clear():
Clears the result set and releases any resources held by the query. Sets the query state to inactive. You should rarely if ever need to call this function.
You can have a look at the Master Detail Example and in particular createConnection() function, to see how the same QSqlQuery object is used multiple times.

How to return a simple QSqlQueryModel from a class to another class?

I just want to process on database and add the result to a model and send it to another class and view it in GUI. Abstract code is:
I have a public class member:
QSqlQueryModel *model;
Load data and add it to the model and return model:
QSqlQueryModel* PersistenceAdapter::loadServerList(){
cout<<"Loading data"<<endl;
QSqlQuery* qry = new QSqlQuery(db);
qry->prepare("select * from student1.SERVERLIST");
model = new QSqlQueryModel();
model->setQuery(*qry);
return model;
}
In other class I have a load list function. Error is coming from here:
void MainWindow::setServersList(QSqlQueryModel *myModel) {
widget.serverListView->setModel(myModel);
}
Then I call it from constructor of same class and here is code:
MainWindow::MainWindow() {
//Stablish connection to database
PersistenceAdapter *p = new PersistenceAdapter();
setServersList(p->loadServerList());
}
And error is:
RUN FINISHED; Segmentation fault; core dumped; real time: 210ms; user: 10ms; system: 40ms
Appreciate if anyone can help.
There are several mistakes in your code. Also, here are some tips'n'tricks for your situation:
Are you sure that your model member was initialized/instantiated? To check it just do if model!=NULL check.
Are you sure that your db (in PersistenceAdapter::loadServerList()) was instantiated? _To check it just do if model!=NULL check. Also, check if that db was successfully opened, and check if it isOpen at moment of use. Keep in mind that you can got some errors while doing multithreaded database access.
I do not recommend create QSqlQuery, instead you can use another setQuery method.
After you apply your query, do check: if (model.lastError().isValid()) qDebug() << model.lastError();
Check order of calling your functions -- if instantiating db goes earlier than accessing db and so on.
Hope this tips will throw light upon. Good luck!
widget.serverListView->setModel(myModel);
So, the crash can only be there. Since the widget seems to be on the heap (which is a bad idea), the only reason for the crash is that your server list view is not properly initialized at that stage, so it is either a null pointer or dangling.
This also looks logical since you do not seem to initialize the view of the widget in your main window constructor. If you do it so, the crash will go away.

what happens with auto commit is disabled and there is never a rollback?

So I've inherited a large c++ code base which does a lot of mysql work. The code always disables autocommit and tends to have functions which look like this:
int function() {
if(mysql_real_query(conn, ...)) {
return -1;
}
if(mysql_real_query(conn, ...)) {
return -1;
}
mysql_commit(conn);
return 0;
}
Obviously, the intention here is that the commit only happens if the queries are successful. But what happens if one of them isn't? Eventually, the mysql connection is properly closed, but there is no rollbacks in the code.
So when it closes, will it basically just commit any changes that were successful? Or will it rollback as if nothing happened?
My gut says that it makes sense to have a rollback if the second query fails in order to "undo" the successful first query. So this function ends up be transactional.
Of course, I find this code to be inherently broken because later other mysql code could do a commit leaving things in a "weird" state if some previous work failed. But before I go and change the behavior of the program, I wanted to make sure I understood what the current behavior was.
As Marc B said, this is actually easy enough to test. I was hoping for someone to be able to point at an authoritative source, but testing seems to be reasonable enough:
So anyway, I tried the following code:
MYSQL *conn = mysql_init(NULL);
mysql_real_connect(conn, host, use, password, database, 0, NULL, 0);
mysql_autocommit(conn, 0);
mysql_query(conn, "INSERT INTO test VALUES(1)");
mysql_query(conn, "INSERT INTO test VALUES(2)");
mysql_query(conn, "INSERT INTO test VALUES(3)");
mysql_query(conn, "INSERT INTO test VALUES(4)");
mysql_close(conn);
simple enough, turn off auto-commit, do a bunch of inserts, and never commit. In this case, result is that the queries are effectively rolled back. When I query the DB, the rows are not there.
Obviously simply adding a mysql_commit(conn); right before the mysql_close(conn); does in fact cause the rows to be created.

Using SQLite with Qt

I am thinking of using SQLite as a backend DB for a C++ applicatiojn I am writing. I have read the relevant docs on both teh trolltech site and sqlite, but the information seems a little disjointed, there is no simple snippet that shows a complete CRUD example.
I want to write a set of helper functions to allow me to execute CRUD actions in SQLite easily, from my app.
The following smippet is pseudocode for the helper functions I envisage writing. I would be grateful for suggestions on how to "fill up" the stub functions. One thing that is particularly frustrating is that there is no clear mention in any of the docs, on the relationship between a query and the database on which the query is being run - thus suggesting some kind of default connection/table.
In my application, I need to be able to explicitly specify the database on which queries are run, so it would be useful if any answers spell out how to explicitly specify the database/table involved in a query (or other database action for that matter).
My pseudocode follows below:
#include <boost/shared_ptr.hh>
typedef boost::shared_ptr<QSqlDatabase> dbPtr;
dbPtr createConnection(const QString& conn_type = "QSQLITE", const QString& dbname = ":memory:")
{
dbPtr db (new QSQlDatabase::QSqlDatabase());
if (db.get())
{
db->addDatabase(conn_type);
db->setDatabaseName(dbname);
if (!db.get()->open)
db.reset();
}
return db;
}
bool runQuery(const Qstring& sql)
{
//How does SQLite know which database to run this SQL statement against ?
//How to iterate over the results of the run query?
}
bool runPreparedStmtQuery(const QString query_name, const QString& params)
{
//How does SQLite know which database to run this SQL statement against ?
//How do I pass parameters (say a comma delimited list to a prepared statement ?
//How to iterate over the results of the run query?
}
bool doBulkInsertWithTran(const Qstring& tablename, const MyDataRows& rows)
{
//How does SQLite know which database to run this SQL statement against ?
//How to start/commit|rollback
}
In case what I'm asking is not clear, I am asking what would be the correct wat to implement each of the above functions (possibly with the exception of the first - unless it can be bettered of course).
[Edit]
Clarified question by removing requirement to explicitly specify a table (this is already done in the SQL query - I forgot. Thanks for pointing that out Tom
By default, Qt uses the application's default database to run queries against. That is the database that was added using the default connection name. See the Qt documentation for more information. I am not sure what you mean by the default database table, since the table to operate on is normally specified in the query itself?
To answer your question, here is an implementation for one of your methods. Note that instead of returning a bool I would return a QSqlQuery instance instead to be able to iterate over the results of a query.
QSqlQuery runQuery(const Qstring& sql)
{
// Implicitly uses the database that was added using QSqlDatabase::addDatabase()
// using the default connection name.
QSqlQuery query(sql);
query.exec();
return query;
}
You would use this as follows:
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setHostName("localhost");
db.setDatabaseName("data.db");
if (!db.open())
{
raise ...
}
QSqlQuery query = runQuery("SELECT * FROM user;");
while (query.next())
{
...
}
Note that it is also possible to explicitly specify for which database a query should be run by explicitly specifying the relevant QSqlDatabase instance as the second parameter for the QSqlQuery constructor:
QSqlDatabase myDb;
...
QSqlQuery query = QSqlQuery("SELECT * FROM user;", myDb);
...

How to emulate Edit/Update mechanism of ADO for SQLite in C++?

I have a C++ application that uses ADO to talk to an Oracle database. I'm updating the application to support an offline documents. I've decided to implement SQLite for the local side.
I've implemented a wrapper around the ADO classes that will call the appropriate code. However, ADO's way of adding/editing/deleting rows is a bit difficult to implement for SQLite.
For ADO I'd write something like:
CADODatabase db;
CADORecordset rs( &db );
db.Open( "connection string" );
rs.Open( "select * from table1 where table1key=123" );
if (!rs.IsEOF())
{
int value;
rs.GetFieldValue( "field", value );
if (value == 456)
{
rs.Edit();
rs.SetFieldValue( "field", 456 );
rs.Update();
}
}
rs.Close();
db.Close();
For this simple example I realize that I could have just issued an update, but the real code is considerable more complex.
How would I get calls between the Edit() and Update() to actually update the data? My first thought is to have the Edit() construct a separate query and the Update() actually run it, but I'm not sure what fields will be changed nor what keys from the table to limit an update query to.
" but I'm not sure what fields will be changed nor what keys from the table to limit an update query to."
How about just selecting ROWID with the rest of the fields and then building an update based on that ?