Using SQLite with Qt - c++

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

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

QT 5.01 QSqlDatabase connects, QSqlQuery executes, however nothing is found from the sqlite database

As stated in the title, I have written code for QT to connect to a sqlite database.
bool FilterData::initDatabase(){
QDir d;
_db = new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE"));
_db->setDatabaseName("OMBI.db");
return _db->open();
}
void FilterData::loadFromDB(){
if(initDatabase()){
//set up the query
QSqlQuery query(*_db);
//vectors for storing results from the query
QVector<QString>* measures = new QVector<QString>();
QVector<QString>* title = new QVector<QString>();
QVector<QString>* type = new QVector<QString>();
query.prepare("SELECT * FROM measures");
query.exec();
while (query.next()) {
measures->push_front(query.value(0).toString());
title->push_front(query.value(1).toString());
type->push_front(query.value(2).toString());
}
std::cout<<"Passed reading query results"<<std::endl;
std::cout<<measures->size()<<std::endl;
emit measuresReady(*measures, *title, *type);
}
}
The database connects and opens just fine, however on testing the result of query.first(),
query.next(), query.isValid(), query.isActive(), and query.isSelect() are all false. OMBI.db has been placed in the project folder for QT, as this is my first time working with QT I wasn't sure if this was correct protocol or not.
I've been trying to find a solution to this for far too long, and I have searched quite a bit, but to no avail. Hopefully some of you gurus can shed some light on what I'm doing wrong.
edit:
I've determined that exec() is failing and query.lastError() is reporting "No query unable to fetch row". I am now trying to determine what is causing the query to fail so miserably. I checked _db->lastError() but it was empty which I assume is a good thing.

Do I need an SQL server to work with Qt's QtSql library?

I am a beginner with Qt, so my question might be a bit basic.
My intention is to work with an ODBC database located in my hard drive. I have tried to open it with this code:
QSqlDatabase db = QSqlDatabase::addDatabase("QODBC");
db.setHostName("");
db.setDatabaseName("c:\\database.mdb");
bool ok = db.open();
QSqlQuery query;
query.exec("SELECT name FROM results WHERE tag>10");
while (query.next()) {
QString name1 = query.value(0).toString();
qDebug() << name1;
}
Now, the problem is that the program can't find the database, failing at the db.open() line. I suspect that Qt can't open a database directly, but instead has to deal with an SQL server. Is this so? If that's the case, I'd be grateful if you could give me some clues on how to go ahead, particularly regarding host name (is it localhost?).
Also, I am not sure of whether the path to the file must be included in DatabaseName.
PS: I have no problem shifting to a different kind of database/server, e.g. MySQL. So if your solution requires this, I'd be happy with it!
Thanks in advance
D
Unless you specifically need a Jet/MS Access format database for something else you'd be better off going with SQLite. Qt has SQLite support built-in (QSQLITE driver) - you just point it at the database file and go. No need to setup ODBC data sources or anything.
According to the documentation, you should set the setDatabaseName to the ODBC datasource. You then configure the ODBC datasource to point to the appropriate file.
For future reference:
Just as Werne Strydom said, the argument of setDatabaseName is not the database file name, but the name of the ODBC datasource that points to your database. Therefore, you need to create an ODBC that points to your database.
The usual way to do this (in Windows) would be to go to Control Panel\System & security\Administrative tools\Data Sources (ODBC). But if you're in a 64-bit machine and want to work with a 32-bit driver, go instead to C:\windows\SysWOW64 and run odbcad32.exe
When you do this, a dialog opens (same dialog regardless of 64/32-bits). Here you create your ODBC, give it a name and link it to your actual database. In my case, as I am working with a local database, I used the "User DNS" tab.
Then, back in Qt, you put that ODBC name as argument for setDatabaseName. And it works! (Or it did for me...)
The new bit of code looks like:
QSqlDatabase db = QSqlDatabase::addDatabase("QODBC");
db.setHostName("localhost");
db.setDatabaseName("MyDataSource");
bool ok = db.open();
QSqlQuery query;
query.exec("SELECT name FROM results WHERE tag>10");
while (query.next()) {
QString name1 = query.value(0).toString();
qDebug() << name1;
}
where "MyDataSource" is the name I gave to the ODCB.

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 ?