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

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.

Related

Using OTL library (c++) to INSERT without binding parameters

I am attempting to use OTL to do a basic insert into a sql server db. For this insert I don't need to bind any parameters.
otl_stream o_stream;
std::string query = "INSERT INTO common VALUES (1,2,3);";
o_stream.open(1, query.c_str(), db_);
o_stream.flush();
o_stream.close();
However, even after flushing and closing the otl_stream, the db is locked on that table (can't read via separate application). I have to close my otl application to unlock the table.
If I parameterize the insert statement then everything works as it should (inserts successful, no table lock).
otl_stream o_stream;
std::string query = "INSERT INTO common VALUES (1,2,a:<int>);";
o_stream.open(1, query.c_str(), db_);
o_stream << 3;
That's not ideal, since ideally I'd like to avoid parameterizing/binding if it's not necessary.
Any suggestions?
EDIT:
Answer Below
From the author of the OTL library:
otl_streams are meant to be reused, that is, a INSERT statement stream
needs to have at least one bind variable to be "flushable". For cases
when there is 0 bind variables, there is this:
http://otl.sourceforge.net/otl3_const_sql.htm.
Missing from that link is the required db.commit() call. Without the commit call the table will be locked.
Solution for the example given in the question:
std::string query = "INSERT INTO common VALUES (1,2,3);";
db_.direct_exec(query.c_str());
db_.commit();

How to use Qt QSqlDriver::subscribeToNotification with SQLite3?

I'm writing a Qt application where different models can insert/delete/update the same table. When one model changes the database, I would like the other models to be notified of the change, so they can update their views accordingly.
It seems that the best way to monitor inserts, deletes and updates in SQLite is to use QSqlDriver::subscribeToNotification and then react to the notification signal. I know the syntax is along the lines of:
db.driver()->subscribeToNotification("anEventId");
However, I'm not sure what anEventId means. Is anEventId a constant provided by SQLite or do I code these specific events into SQLite using triggers or something else and then subscribe to them?
The subscribeToNotification implementation in the Qt sqlite driver relies upon the sqlite3_update_hook function of the sqlite C API. The Qt driver, however, is not forwarding the operation performed, just the table name, and that should be the misterious anEventId argument to pass to subscribeToNotification. In short, you can listen to events occurring in any table (given it is a rowid table, but it generally is) passing the table name to the subscribeToNotificationmethod. When your slot catches the notification signal, though, you only know that an operation occurred in that table, but (sadly) Qt won't tell you which one (INSERT, UPDATE, or DELETE).
So, given a QSqlDriver * driver:
driver->subscribeToNotification("mytable1");
driver->subscribeToNotification("mytable2");
driver->subscribeToNotification("mytable3");
then in your slot:
void MyClass::notificationSlot(const QString &name)
{
if(name == "mytable1")
{
// do something
}
else if(name == "mytable2")
{
//etc...

How to delete a row from SQLite database using QSqlQueryModel?

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().

Is there any qt signal for sql database change?

I wrote a C++ program using Qt. some variables inside my algorithm are changed outside of my program and in a web page. every time the user changes the variable values in the web page I modify a pre-created SQL database.
Now I want my code to change the variables value during run time without to stop the code. there is two options :
Every n seconds check the database and retrieve the variables value -> this is not good since I have to check if database content is changed every n seconds (it might be without any change for years. Also I don't want to check if the database content is changed)
Every time the database is changed my Qt program emits a signal so by catching this signal I can refresh the variables value, This seems an optimal solution and I want to write a code for this part
The C++ part of my code is:
void Update Database()
{
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
db.setHostName("localhost");
db.setDatabaseName("Mydataset");
db.setUserName("user");
db.setPassword("pass");
if(!db.open())
{
qDebug()<<"Error is: "<<db.lastError();
qFatal("Failed To Connect");
}
QSqlQuery qry;
qry.exec("SELECT * from tblsystemoptions");
QSqlRecord rec = qry.record();
int cols = rec.count();
qry.next();
MCH = qry.value(0).toString(); //some global variables used in other functions
MCh = qry.value(1).toString();
// ... this goes on ...
}
QSqlDriver supports notifications which emit a signal when a specific event has occurred. To subscribe to an event just use QSqlDriver::subscribeToNotification( const QString & name ). When an event that you’re subscribing to is posted by the database the driver will emit the notification() signal and your application can take appropriate action.
db.driver()->subscribeToNotification("someEventId");
The message can be posted automatically from a trigger or a stored procedure. The message is very lightweight: nothing more than a string containing the name of the event that occurred.
You can connect the notification(const QString&)signal to your slot like:
QObject::connect(db.driver(), SIGNAL(notification(const QString&)), this, SLOT(refreshView()));
I should note that this feature is not supported by MySQL as it does not have an event posting mechanism.
There is no such thing. The Qt event loop and the database are not connected in any way. You only fetch/alter/delete/insert/... data and that's it. Option 1 is the one you have to do. There are ways to use TRIGGER on the server side to launch external scripts but this would not help you very much.

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