sqlite3: detach database produces database is locked error - sqlite

I have an sqlite3 database open. I attach a second database, check whether the first and second database have the same value in a table, and then detach the second database. The detach command gives the error "database db2 is locked".
The process is not multi-threaded. There are no other processes accessing the databases. I get the same results on NFS and the local disk. I tried inserting a delay before the detach in case the lock mentioned in the error is related to the file system.
If I comment out the SELECT statement in the code below, the detach works. So basically, attach then detach works, but if I execute SQL code before the detach I receive SQLITE_ERROR return status and the error message shown above.
SQLite version 3.22.0.1
BTW, I realize I could SELECT count(s.id) instead of looping over each row returned from SELECT *, but other posts talk about making sure sqlite3_step() is called until SQLITE_DONE is returned and I wanted to try everything before posting here.
sqlite3_stmt *stmt;
char *errMsg;
const char *zLeftover;
int count = 0;
// DB declared as:
// sqlite3 *DB;
// opened as:
// sqlite3_open(":memory:", &DB);
sqlret = sqlite3_exec(DB, "ATTACH DATABASE foo.db AS db2;", NULL, 0, &errMsg);
assert(sqlret == SQLITE_OK);
sqlret = sqlite3_prepare_v2(DB,
"SELECT * FROM sessions s INNER JOIN db2.sessions s2 ON s2.id=s.id;",
-1, &stmt, &zLeftover);
assert(sqlret == SQLITE_OK);
sqlret = sqlite3_step(stmt);
while (sqlret == SQLITE_ROW) {
++count;
sqlret = sqlite3_step(stmt);
}
sqlret = sqlite3_finalize(stmt);
assert(sqlret == SQLITE_OK);
sqlret = sqlite3_exec(DB, "DETACH DATABASE db2", NULL, 0, &errMsg);
// This assert fails with SQLITE_ERROR
assert(sqlret == SQLITE_OK);
I expect the sqlite3_finalize(stmt) call to remove any internal locking on the attached database. Is there another call or additional SQL that must be executed before the detach? A pragma setting? A configuration option on the lib?
I've searched the usual places with no luck. Thanks in advance for you help.

Related

Get count of rows in sqlite table using c in a program

How to get count of rows in sqlite table using c in a program? Is it only by running "select * from table" ? and incrementing a variable?
sqlQuery = "select count (*) from company;";
rc = sqlite3_prepare_v2(db, sqlQuery, -1, &stmt, NULL);
printf("prepare status for count : %d\n", rc);
rowcount = sqlite3_step(stmt);
sqlQuery = "select * from company;";
rc = sqlite3_prepare_v2(db, sqlQuery, -1, &stmt, NULL);
printf("prepare status for count : %d\n", rc);
while (SQLITE_ROW == sqlite3_step(stmt)) {
rowcount ++;
}
I am not getting the row count with 1,
But I do get it when I perform 2.
The sqlite3_step() function does not return the results of the query. It returns SQLITE_ROW if there is another row in the result set, or SQLITE_DONE if there isn't. It also returns an error code if something bad happened.
sqlQuery = "select count(*) from company;";
rc = sqlite3_prepare_v2(db, sqlQuery, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
// error handling -> statement not prepared
}
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW) {
// error handling -> no rows returned, or an error occurred
}
rowcount = sqlite3_column_int(stmt, 0);
To get the values for the current row in the result set, you have to access them by column index, using the prepared statement that is executing the query. Since there is only one column in this query, the index of 0 is used for the left-most, or only, column in the result. And, since the value of that column is an integer, we use the sqlite3_column_int() function, which returns the value as a 32-bit integer.
For most ordinary database operations, you only need to use a few of the functions in the SQLite C API. You should really read and absorb the introduction. It will help you to understand the normal workflow for database operations.

Use SqlCipher on Visual Studio

I´ve already compiled SQLCipher for windows and encrypt/decrypt a database file using SQLCipher at command line. The problem is at the last part of this tutorial when I use SQLCipher in Visual Studio.
It looks like the application skips this code line, because it is not encrypting neither decrypting, i'm not getting any error.
if (sqlite3_exec(db, (const char*)"PRAGMA key ='password'", NULL, NULL, NULL) == SQLITE_OK){
printf("Accepted Key\n");
};
Can anyone tell me what's wrong? or provide some sample C code?
Thanks in advance.
There are a couple of items to address here. First, checking for SQLITE_OK as a status code from sqlite3_exec does not mean that the provided password is valid for the database. In order to validate that, once the database has been keyed, we recommend you perform a query such as:
SELECT count(*) FROM sqlite_master;
This table will always be present, regardless of the state of your application schema.
Next, to verify that you have properly integrated the SQLCipher library within your application, we can query the database for the PRAGMA cipher_version; reported by the library. Below is a sample for doing so:
int rc;
sqlite3 *db;
sqlite3_stmt *stmt;
rc = sqlite3_open(":memory:", &db);
if(rc != SQLITE_OK){
printf("failed to open database\n");
}
rc = sqlite3_prepare(db, "PRAGMA cipher_version;", -1, &stmt, NULL);
rc = sqlite3_step(stmt);
if(rc == SQLITE_ROW){
printf("SQLCipher version:%s\n", sqlite3_column_text(stmt, 0));
}
sqlite3_finalize(stmt);
if(db != NULL){
sqlite3_close(db);
}

Sqlite - insert into multiple tables with an auto-increment PID

I'm using SQLite. I have two tables:
Log:
pid INTEGER PRIMARY KEY AUTOINCREMENT
action INTEGER NOT NULL
.... ect.
ErrorExtras:
pid INTEGER REFERENCES log(pid)
msg TEXT,
request TEXT
Now when an error occurs, I want to insert into both. Inserting into Log is easy, as it generates the PID, however, inserting into ErrorExtras is harder since I don't know the PID without doing a query. If I insert into Log, query for the PID and then insert into ErrorExtras, that seems quie messy. Is there some kind of shortcut for these kinds of inserts?
In my case, this is possible because the other info in log uniquely identifies the pid (so there's a combined key), but what if this weren't the case?
You needn't query for the insert_id, just use the last_insert_id in the select statement in ErrorExtras and Bob's your uncle.
If you have control of your SQL commands, you can use the last_insert_rowid SQL function like this:
INSERT INTO Log(action) VALUES(42);
INSERT INTO ErrorExtras(pid, msg) VALUES(last_insert_rowid(), 'x');
(But this works only for the very next INSERT command, because afterwards, last_insert_rowid() returns the rowid of the ErrorExtras record.)
If you are using the C API, you can use the sqlite3_last_insert_rowid function:
sqlite3_prepare_v2(db, "INSERT INTO Log(action) VALUES(42)", -1, &stmt, NULL);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
sqlite3_prepare_v2(db, "INSERT INTO ErrorExtras(pid,msg) VALUES(?,?)", -1, &stmt, NULL);
sqlite3_bind_int64(stmt, 1, sqlite3_last_insert_rowid(db));
sqlite3_bind_text(stmt, 2, "x", -1, SQLITE_TRANSIENT);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
The APIs of other languages typically also have some mechanism to get the last inserted rowid.
For example, in Android, the insert function returns it:
ContentValues cv = new ContentValues();
cv.put("action", 42);
long log_rowid = db.insert("Log", null, cv);
ContentValues cv = new ContentValues();
cv.put("pid", log_rowid);
cv.put("msg", "x");
db.insert("ErrorExtras", null, cv);

qt sqlite insert into autoincrement table yields two rows

Hi
I have a sqlite db which I am manipulating using qts built in sqlite database driver.
I have a small test app that allows me to run an sql query from a line edit and it will be executed and the results are then updated in a view of the relevant model.
I have created a table which uses autoincremented primary key values, but if I execute an insert statement without providing the key, I get two rows inserted, each with an autoincremented value.
If I provide the key value, only one row is created. Any ideas why this is?
Table is simple enough, e.g
CREATE TABLE GroupNames ( ID integer PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE, Name varchar(50))
and when I run the query
insert into groupnames (name) values ("testName");
I get two new rows with autoincremented ids. However, if I run
insert into groupnames (id, name) values (100, "testName");
I get one row as expected, with the correct id 100.
Also of note is that if I try
insert into table groupnames (id, name) values (100, "testName");
insert into table groupnames (name) values ("testName");
the query does not run.
The qt code to run the query could not be simpler:
QSqlQuery *DbCore::run_query(const QString &query_string)
{
QSqlDatabase db = QSqlDatabase::database(defConnectionName);
if(!db.isOpen())
return NULL;
QSqlQuery *q = new QSqlQuery(query_string, db);
q->exec();
return q;
}
I have added some logging code to check that the query is executed once:
QSqlDatabase db = QSqlDatabase::database(defConnectionName);
if(!db.isOpen())
return NULL;
qDebug() << "Running query:" << query_string;
QSqlQuery *q = new QSqlQuery(query_string, db);
if(!q->exec())
qDebug() << "Error running query:" << q->lastError();
return q;
The log confirms that I'm only executing once:
Running query: "insert into groupnames (name) values ("hello")"
If i then check the database using sqlite3 shell (to remove any doubt about qt views etc):
sqlite> select * from groupnames;
1|hello
2|hello
question was answered above in a comment:
As i see in the documentation, when you create a QSqlQuery the way you do, the query, if not empty, is executed. To create the QSqlQuery and execute the query, use this: QSqlQuery *q = new QSqlQuery(db); q->exec(query_string) To see the last executed query, use QSqlQuery::lastQuery() And for the last query that was successfully executed QSqlQuery::executedQuery() Hope this helps. – Hector Mar 16 at

Delete All Data with SQLite delete syntax?

I've made a button that removes all the objects in my array, which is shown in my tableView. Then it reloads data and the tableview is empty. But how can I delete all the data from my SQLite database as well, and not just the array? Now the data occurs when I restart. I have tried this:
Button void:
- (void) deleteAllButton_Clicked:(id)sender {
[appDelegate removeAllBooks:nil];
[self.tableView reloadData];
}
Appdelegate void:
-(void) removeAllBooks:(Book *)bookObj {
//Delete it from the database.
[bookObj deleteAllBooks];
//Remove it from the array.
[bookArray removeAllObjects];
}
Delete syntax in Book.m
- (void) deleteAllBooks {
if(deleteStmt == nil) {
const char *sql = "WHAT DO I HAVE TO DO TO DELETE ALL THE ROWS?";
if(sqlite3_prepare_v2(database, sql, -1, &deleteStmt, NULL) != SQLITE_OK)
NSAssert1(0, #"Error while creating delete statement. '%s'", sqlite3_errmsg(database));
}
//When binding parameters, index starts from 1 and not zero.
sqlite3_bind_int(deleteStmt, 1, bookID);
if (SQLITE_DONE != sqlite3_step(deleteStmt))
NSAssert1(0, #"Error while deleting. '%s'", sqlite3_errmsg(database));
sqlite3_reset(deleteStmt);
}
Well, the normal SQL syntax would be:
DELETE FROM tablename
If you have a standard primary id column (and you should) you can do
DELETE FROM tablename WHERE id > -1;
And this will delete all rows in the table since there is no id less than 0.
DELETE FROM tablename did not work for me. I was using a sqlite3 database on iPhone, which I assume the poster was, as well. For me to get this to work, I needed to:
DROP table tablename
followed by a CREATE statement, if I wanted the table to still exist (without any rows).
Of course, that requires knowing the right CREATE statement. Using the SQLManager plugin for Firefox quickly reverse-engineered for me the correct CREATE statement to use. I never figured out why DELETE FROM tablename didn't work, but it definitely didn't, in my case.

Resources