How to emulate Edit/Update mechanism of ADO for SQLite in C++? - 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 ?

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

C++ wrapper class SQLite advice

I'm learning C++ and have a question about classes and wrappers. I'm writing an application for a raspberry pi. I have a class called SensorClass whose methods read data from various sensors attached to the board.
class SensorClass {
public:
SensorClass();
virtual ~SensorClass();
int getTemperature();
int getPressue();
;
I want to write the data to a local sqlite database when it is read. On the SQLite website there are a number of wrapper classes.
SQLite wrappers
I'm wondering if I should use one of these to for example insert data into the database when it has been read.
I'm thinking then I would be separating the code and just calling for example the SQLite insert method in the getTemperature() function. Would this be a good idea? Which wrapper should I use?
Example SQlite wrapper class
Alternatively I could hard code the database operations in the getTemperature() method like this.
int SensorClass::getTemperature(){
// read temperature
//insert into database
/* Create SQL statement */
sql = "INSERT INTO DATAPOINTS (Temperature) " \
"VALUES (15); " \
/* Execute SQL statement */
rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
}
Thanks for your advice
It would generally be better to separate the two things. i.e. make the sensor class do the job on sensing stuff well and only that.
Then have a separate class that does the job of logging sensor data to the database well. You may find it is better to insert entire rows into the database in one go. And you may also decide that you want to only log data periodically at a fixed sampling rate.
Then in your main application loop / via an event driven timer, you can do measurements and record data as separate steps.
e.g.
void APP_tick(void)
{
SensorValues values = sensors.readValues();
logger.writeValues(values);
}
By separating responsibility, you can then change the logger class out easily - you may end up deciding that you don't want to use a database and would rather just log the data into flat files in order to use less disk space and improve performance.
If using SQLite then you might find it worthwhile using prepared statements to avoid having to compile the SQL query every time you execute it (which is expensive in CPU terms and you are running this on a fairly limited system).

SQLite database update in C++

I have an application where I show data from a database. In fact we can say it's a database editor.
Now I want to perform update/delete command on this opened database. Using the following commands, the database opens successfully.
int nRet = sqlite3_open(szFile, &mpDB);
From C# (.net api) I am able to update data from database
dbCmd5 = New SQLiteCommand(
"update Tbl_Tmp_Cal_Res Load_Time=5 WHERE Part_Index= 5", g_dbFlow);
dbCmd5.ExecuteNonQuery()
But from C++ I am getting error 5 (database is locked)
C++ code
int nRet = sqlite3_open(szFile, &mpDB);//database opened successfully.
sqlite3_exec(mpDB, "UPDATE query", 0, 0, &szError);//Error for this statement
Multithreading is not used in application.
Is the database used from another location in the code? Since something else clearly seems to have the database locked, I would guess that you're using the database from another location in the code and have forgotten to call sqlite3_finalize on a select statement or something similar.
maybe you have forgotten authentication step (username/password & etc)

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