NS_ERROR_FILE_IS_LOCKED when several requests using SQLite on Mozilla plateform - sqlite

I'm trying to use Storage mechanism in Mozilla platform (in thundebird 3.0).
The following code is used after each test to erase the table present in the database:
function tearDown()
{
let database = new Database();
let req1 = "SELECT name FROM sqlite_master WHERE type='table'";
let statement = database.connection.createStatement(req1);
let tables = [];
while(statement.executeStep()) {
tables.push(statement.row.name);
}
statement.reset();
for(table in tables) {
let req2 = "DROP TABLE " + tables[table];
database.connection.executeSimpleSQL(req2);
}
}
But I've an error during executeSimpleSQL of req2 (NS_ERROR_FILE_IS_LOCKED), It seems that SQLite doesn't release the lock from the first statement. I've tried reset(), finalize(), but nothing works. How can I properly release the lock of the first statement?

Answering myself: I forgot to release a previous statement in previous code of my application.
Final story: when you use
statement.executeStep()
Check:
be sure that the last call of this statement return false
or never forgot to release it:
statement.reset();

var statement = dbConn.createStatement("SELECT COUNT(name) AS nameOcurrences FROM Table1 WHERE name = '" + aName + "';");
var occurrences;
while(statement.executeStep()) {
occurrences = statement.row.nameOcurrences;
}

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.

How can I see the SQL generated by SQLite.NET PCL in Xamarin Studio?

I researched this and all I can find is a suggestion to turn on .Trace = true like this:
db1 = DependencyService.Get<ISQLite>().GetConnection();
db1.Trace = true;
I also tried this:
db2.Trace = true;
var categories = db2.Query<Category>("SELECT * FROM Category ORDER BY Name").ToList();
Debug.WriteLine("xxxx");
Well I did this and then restarted the application. When I view the Application output I just see information on threads started and the xxxx but don't see any SQL trace information.
Can anyone give me advice on this. Thanks
You need to set Trace and Tracer (action) properties on your SQLiteConnection to print queries to output:
db.Tracer = new Action<string>(q => Debug.WriteLine(q));
db.Trace = true;
Look in the Application Output window for lines that begin Executing
Example Output after setting Trace to true:
Executing: create table if not exists "Valuation"(
"Id" integer primary key autoincrement not null ,
"StockId" integer ,
"Time" datetime ,
"Price" float )
Executing Query: pragma table_info("Valuation")
Executing: create index if not exists "Valuation_StockId" on "Valuation"("StockId")
Executing: insert into "Stock"("Symbol") values (?)
Executing Query: select * from "Stock" where ("Symbol" like (? || '%'))
0: A
Ref: https://github.com/praeclarum/sqlite-net/blob/38a5ae07c886d6f62cecd8fdeb8910d9b5a77546/src/SQLite.cs
The SQLite PCL uses Debug.WriteLine which means that the logs are only included in Debug builds of the PCL.
Remove your nuget reference to the sqlite.net PCL (leave the native reference), and instead add SQLite.cs as a class to your project, and execute a debug build, with the Trace flag set, and you'll see the tracing.
I didn't have to do anything special other than include the SQLite.cs file in my Xamarin iOS project for this to work:
using (var conn = new SQLite.SQLiteConnection("mydb.sqlite") { Trace = true }) {
var rows = conn.Table<PodcastMetadata>().Where(row => row.DurationMinutes < 10).Select(row => new { row.Title });
foreach (var row in rows) {
Debug.WriteLine(row);
}
}
Output:
Executing Query: select * from "PodcastMetadata" where ("DurationMinutes" < ?)
0: 10

Firefox addon development: SQLite Database Connection

So I am developing this add-on using the MDN's Add-on Builder and need to connect with the SQLite Database. The connection gets created fine and insertion is fine as long as I am inserting values without binding parameters(that is, through executeSimpleSQL()). As soon as I use the createStatement() method to INSERT values, it does not work. Here's what I have done so far.
let file = FileUtils.getFile("Desk", ["my_db_file_name.sqlite"]);
let mDBConn = Services.storage.openDatabase(file);
mDBConn.executeSimpleSQL("CREATE TEMP TABLE IF NOT EXISTS element (rating VARCHAR(50))");
let stmt = mDBConn.createStatement("INSERT INTO element (rating) VALUES(:value)");
stmt.params.value = 13;
//mDBConn.executeSimpleSQL("INSERT INTO element (rating) VALUES(13)");
var statement = mDBConn.createStatement("SELECT * FROM element WHERE rating = :rat");
statement.params.rat = 13;
try {
while (statement.step()) {
let value = statement.row.rating;
console.log(value);
}
}
finally {
statement.reset();
}
Note that the SELECT statement with the bound parameters works fine, it's just the INSERT statement that's problematic.
Any ideas?
You forgot to call execute().

Batch Insert using a loop to add to table storage in windows azure

I am trying to do a batch insert from a list of objects to table storage in Windows Azure.
I am using the storage emulator for now.
I get this error:
"Unexpected response code for operation".
I tried to search for anyone who encountered similar problems but to no avail.
My keys are setup this way:
PartitionKey = "projects" + CompanyID.toString();
RowKey = ProjectID.toString();
It is inserted like this:
foreach (vProject item in projectList)
{
TableOperation retrieveOperation = TableOperation.Retrieve<mMultipleSave.ViewProjectEntity>("projects" + CompanyID.toString(), item.ProjectID.ToString());
TableResult retrievedResult = table.Execute(retrieveOperation);
if (retrievedResult.Result != null)
{
mMultipleSave.ViewProjectEntity updateEntity = (mMultipleSave.ViewProjectEntity)retrievedResult.Result;
if (!item.isProjectArchived)
{
//update entity in table storage
updateEntity.ProjectClient = item.ClientName;
updateEntity.ProjectCompany = item.Company;
updateEntity.ProjectName = item.ProjectName;
batchUpdateOperation.Replace(updateEntity);
}
else {
//delete project in table storage if it is archived in the database
batchDeleteOperation.Delete(updateEntity);
}
}
else //if it does not exist in table storage insert
{
mMultipleSave.ViewProjectEntity entity = new mMultipleSave.ViewProjectEntity(CompanyID, item.ProjectID);
entity.ProjectClient = item.ClientName;
entity.ProjectCompany = item.Company;
entity.ProjectName = item.ProjectName;
batchInsertOperation.Insert(entity);
}
}
if (batchInsertOperation.Count > 0)
table.ExecuteBatch(batchInsertOperation);
if (batchUpdateOperation.Count > 0)
table.ExecuteBatch(batchUpdateOperation);
if (batchDeleteOperation.Count > 0)
table.ExecuteBatch(batchDeleteOperation);
It gets an error on table.ExecuteBatch(batchInsertOperation);
Please help.
I have solved the problem. Update your windows azure tools including the emulator to the latest version. As of today, it is in version 2.0

firefox extension SQLite saving and getting

Ok now for the juicy stuff. All attempts failed to save my string so far.
Here is the code for saving it in sqllite in firefox extension:
var file = Components.classes["#mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties)
.get("ProfD", Components.interfaces.nsIFile);
file.append("my_db_file_name.sqlite");
var storageService = Components.classes["#mozilla.org/storage/service;1"]
.getService(Components.interfaces.mozIStorageService);
var mDBConn = storageService.openDatabase(file);
mDBConn.execute("CREATE TABLE IF NOT EXISTS log_det (id INTEGER PRIMARY KEY AUTOINCREMENT, acc STRING)");
mDBConn.execute("INSERT INTO log_det (acc) VALUES(" + window['gluistr']+ ")");
mDBConn.drop();
And the code for retrieving the value:
var file = Components.classes["#mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties)
.get("ProfD", Components.interfaces.nsIFile);
file.append("my_db_file_name.sqlite");
var storageService = Components.classes["#mozilla.org/storage/service;1"]
.getService(Components.interfaces.mozIStorageService);
var mDBConn = storageService.openDatabase(file);
var res = mDBConn.execute("SELECT * FROM log_det");
mDBConn.drop();
Is not working. Anybody knows why? Is "execute" ok or do I need "createStatement" or "executeSimpleSQL". I am confused.
Use executeSimpleSQL.
openDatabase returns a mozIStorageConnection instance, which does not have any method named execute. You can use executeSimpleSQL any time you want to execute a SQL statement without bound parameters (which is what you're doing).
You were probably thinking of mozIStorageStatement's execute method. executeSimpleSQL is not sufficient when bound parameters are necessary. Instead, you need to create a statement, bind any parameters, and then execute it:
var statement = mDBConn.createStatement(
"SELECT * FROM log_det WHERE column_name = :parameter");
statement.bindStringParameter(0, "value");
statement.execute();
statement.reset();
Also note that mozIStorageConnection does not have any method named drop. Maybe you meant to write mDBConn.close()?
All of this is covered here:
https://developer.mozilla.org/en/storage

Resources