How can I perform SQL Count with ESP32 SQLite3? - sqlite

I have spent ages searching for an answer to this, but i can't quite get my head around it.
Basically.I have a sqlite db on a sd card connected to a esp32. SQLite works perfectly and will return when using the standard sample code. For example:
rc = db_exec(db2, "Select * from domain_rank where domain between 'google.com' and 'google.com.z'");
if (rc != SQLITE_OK) {
sqlite3_close(db1);
sqlite3_close(db2);
return;
}`
this looks at a "db_exec" function in the sketch then passes it to a "callback" function. Is there a way to do without this? All I want to do is count the number of records in a select statement.
Thanks
Andrew

Ok I think I may have found a solution
Putting it here just in case anyone else is interested.
String sql = "Select count(*) from surnames where name = 'MICHELLE'";
rc = sqlite3_prepare_v2(db1, sql.c_str(), 1000, &res, &tail);
if (rc != SQLITE_OK) {
String resp = "Failed to fetch data: ";
Serial.println(resp.c_str());
return;
}
while (sqlite3_step(res) == SQLITE_ROW) {
rec_count = sqlite3_column_int(res, 0);
}
Serial.println(rec_count);
can't quite get my head around what the while loop is doing counting the cells, i thought the sql count* query would just return an int value I could use directly. So I'm still a little confused.
Comments would be welcomed! If anyone has any better ideas?
Thanks
Andrew

Related

SQLite via JDBC: SQLITE_BUSY when inserting after selecting

I'm getting the error code SQLITE_BUSY when trying to write to a table after selecting from it. The select statement and result is properly closed prior to my insert.
If I'm removing the select part the insert works fine. And this is what I'm not getting. According to the documentation SQLITE_BUSY should mean that a different process or connection (which is definetly not the case here) is blocking the database.
There's no SQLite manager running. Also jdbcConn is the only connection to the database I have. No parallel running threads aswell.
Here's my code:
try {
if(!jdbcConn.isClosed()) {
ArrayList<String> variablesToAdd = new ArrayList<String>();
String sql = "SELECT * FROM VARIABLES WHERE Name = ?";
try (PreparedStatement stmt = jdbcConn.prepareStatement(sql)) {
for(InVariable variable : this.variables.values()) {
stmt.setString(1, variable.getName());
try(ResultSet rs = stmt.executeQuery()) {
if(!rs.next()) {
variablesToAdd.add(variable.getName());
}
}
}
}
if(variablesToAdd.size() > 0) {
String sqlInsert = "INSERT INTO VARIABLES(Name, Var_Value) VALUES(?, '')";
try(PreparedStatement stmtInsert = jdbcConn.prepareStatement(sqlInsert)) {
for(String name : variablesToAdd) {
stmtInsert.setString(1, name);
int affectedRows = stmtInsert.executeUpdate();
if(affectedRows == 0) {
LogManager.getLogger().error("Error while trying to add missing database variable '" + name + "'.");
}
}
}
jdbcConn.commit();
}
}
}
catch(Exception e) {
LogManager.getLogger().error("Error creating potentially missing database variables.", e);
}
This crashes on int affectedRows = stmtInsert.executeUpdate();. Now if I remove the first block (and manually add a value to the variablesToAdd list) the value inserts fine into the database.
Am I missing something? Am I not closing the ResultSet and PreparedStatement properly? Maybe I'm blind to my mistake from looking at it for too long.
Edit: Also executing the select in a separate thread does the trick. But that can't be the solution. Am I trying to insert into the database too fast after closing previous statements?
Edit2: I came across a busy_timeout, which promised to make updates/queries wait for a specified amount of time before returning with SQLITE_BUSY. I tried setting the busy timeout like so:
if(jdbcConn.prepareStatement("PRAGMA busy_timeout = 30000").execute()) {
jdbcConn.commit();
}
The executeUpdate() function still immedeiately returns with SQLITE_BUSY.
I'm dumb.
I was so thrown off by the fact that removing the select statement worked (still not sure why that worked, probably bad timing) that I missed a different thread using the same file.
Made both threads use the same java.sql.Connection and everything works fine now.
Thank you for pushing me in the right direction #GordThompson. Wasn't aware of the jdbc:sqlite::memory: option which led to me finding the issue.

Redis Scan Count in production

I am in the process of replacing a redis KEYS command in favor of SCAN. However, the keyspace is about 3 Million Keys. What would be a good COUNT parameter to use without affecting I/O performance?
I am also kind of facing the same problem. But I tried this. Hope, this will help...
getKeyList (pattern) {
console.log('pattern', pattern)
let found = []
let cursor = '0'
while(true){
const getAsync = promisify(this.client.scan).bind(this.client)
const reply = getAsync(cursor, 'MATCH', pattern)
cursor = reply[0];
if(reply[1] != false){
found.push(reply[1])
}
if(cursor == 0){
break
}
}
return found
}
*Note that you have to manipulate the return as you want.

Take a column and convert value to int

public int AuthenticatedUserAge(String User_name)
{
string sql = "SELECT UserName,Age FROM tblDataProg WHERE (UserName ='" + User_name + "')";
ds = GetDataSet(sql);
int help = int.Parse(ds.Tables[0].Rows[0]["Age"].ToString());
return help;
}
I can't figure why this line doesn't convert the age to type int and return a value:
int help = int.Parse(ds.Tables[0].Rows[0]["Age"].ToString());
Completely offtopic to the question, but I think it's worth mentioning. Please don't create your SQL statements by concatenating strings. This creates SQL Injection attack possibility. Instead consider SqlParameter class and compose your WHERE predicates using such parameters.
Here you get nice example (look especially at convenient AddWithValue method).
Thanks!
Test your assumptions more. You are assuming GetDataSet is returning a row but it possibly isn't: -
int help = 0;
if (ds.Tables[0].Rows.Count == 0)
{
throw new ApplicationException("no rows were returned, here is an error to deal with");
}
else if (ds.Tables[0].Rows[0]["Age"] == System.DbNull.Value)
{
throw new ApplicationException("a row was found but Age is null, here is an error to deal with");
}
else
{
help = int.Parse(ds.Tables[0].Rows[0]["Age"].ToString());
}
try
int help;
int.TryParse(Convert.ToString(ds.Tables[0].Rows[0]["Age"]),out help);
and see the code in debug mode

Retrieving row count from QSqlQuery, but got -1

I'm trying to get the row count of a QSqlQuery, the database driver is qsqlite
bool Database::runSQL(QSqlQueryModel *model, const QString & q)
{
Q_ASSERT (model);
model->setQuery(QSqlQuery(q, my_db));
rowCount = model->query().size();
return my_db.lastError().isValid();
}
The query here is a select query, but I still get -1;
If I use model->rowCount() I get only ones that got displayed, e.g 256, but select count(*) returns 120k results.
What's wrong about it?
This row count code extract works for SQLite3 based tables as well as handles the "fetchMore" issue associated with certain SQLite versions.
QSqlQuery query( m_database );
query.prepare( QString( "SELECT * FROM MyDatabaseTable WHERE SampleNumber = ?;"));
query.addBindValue( _sample_number );
bool table_ok = query.exec();
if ( !table_ok )
{
DATABASETHREAD_REPORT_ERROR( "Error from MyDataBaseTable", query.lastError() );
}
else
{
// only way to get a row count, size function does not work for SQLite3
query.last();
int row_count = query.at() + 1;
qDebug() << "getNoteCounts = " << row_count;
}
The documentation says:
Returns ... -1 if the size cannot be determined or if the database does not support reporting information about query sizes.
SQLite indeed does not support this.
Please note that caching 120k records is not very efficient (nobody will look at all those); you should somehow filter them to get the result down to a manageable size.

qt sqlite select statement

I decided to test sqlite db for my Qt application.
I have created the sqlite db file with the proper statements (create table etc. and Inserted some rows of data).
My problem is that when I execute a select statement I don't get any records.
This is the code I use:
qq.sprintf("SELECT * from descriptors WHERE descriptors.id=%d ",idx);
query.exec(qq);
if( query.isSelect() ){
while (query.next()){
int fff = query.value(0).toInt();
}}
The problem is that I never get inside the while loop. query.next() doesn't seem to work.
any hints?
thanks in advance,
Thodoris
p.s. I forgot to write my configuration so: Qt 4.7.3, windows 7, visual studio 2008
Other than the mistake hexa posted, query.isSelect() will always return true even if the query failed. You need to check the result of exec():
QSqlQuery query;
query.prepare( "SELECT * FROM descriptors WHERE id = ?" );
query.bindValue( 0, idx ); // assuming idx is an integer/long/QVariant value
if( !query.exec() )
{
// Error Handling, check query.lastError(), probably return
}
// Note: if you don't return in case of an error, put this into the else{} part
while( query.next() )
{
int fff = query.value( 0 ).toInt();
}
In my case, backward iteration over QSqlQueries worked. I think this could be a bug somewhere in the QSQLite Driver implementation.
QSqlQuery q = db.exec("SELECT * FROM Table");
if (q.last()) {
do {
// Do something with row...
} while (q.previous());
}

Resources