Fire an action once a transaction ends - sqlite

I'm using Stephen Celis iOS lib for handling SQLite3 databases, here is the github link.
Taking the example on the git :
try db.transaction {
let rowid = try db.run(users.insert(email <- "betty#icloud.com"))
try db.run(users.insert(email <- "cathy#icloud.com", managerId <- rowid))
}
// BEGIN DEFERRED TRANSACTION
// INSERT INTO "users" ("email") VALUES ('betty#icloud.com')
// INSERT INTO "users" ("email", "manager_id") VALUES ('cathy#icloud.com', 2)
// COMMIT TRANSACTION
I tried to implement the commitHook block but it is fired for each insert. I'd like to fire an action only when all the requests are sent :-D
What should I do ?
Cheers
Edit :
Here is how I implemented the commit hook.
for bay in list{
try! self.themanager.db.transaction {
try! self.themanager.db.run(self.themanager.bays.insert(
//insert values
))
self.themanager.db.commitHook({
print("end commit hook")
})
}
}
Maybe it's related to my main loop :/

From SQLite TRIGGER docs:
At this time SQLite supports only FOR EACH ROW triggers, not FOR EACH STATEMENT triggers. Hence explicitly specifying FOR EACH ROW is optional. FOR EACH ROW implies that the SQL statements specified in the trigger may be executed (depending on the WHEN clause) for each database row being inserted, updated or deleted by the statement causing the trigger to fire.
Commit hooks work like triggers. Unfortunately, the "FOR EACH STATEMENT" behavior is not supported yet.

Related

TypeOrm QueryBuilder dynamic auto delete function with crons job not working

Token for email verification is created with User registration and needs to be deleted from database within 24 hours with crons job help. In a delete function using query builder, token gets deleted only if date value is manually provided in form of string: {delDate: "2021-02-08T17:59:48.485Z" }. Here, all tokens with date before or equal 2021-02-08 get deleted, and is working fine. But thats a static input, manually put in hard code!
Since this must be a dynamic input, I set up variable 'delTime',which stores current date minus 24 hrs in it, but it seems .where condition will not take a variable as value, and will not delete, as: {delDate: deltime}. In fact, 'delDate' consoles exactly the info I need, but it will only work in form of string.
There is a ton of content online teaching how to delete stuff with a static value in typeorm querybuilder, but so hard to find with dynamic values....
How else can I make this work in a dynamic way ?
async delete(req: Request, res: Response){
try {
const tokenRepository = getRepository(Token);
var delTime = new Date();
delTime.setDate( delTime.getDate() - 1 );
console.log(delTime) //consoles 24 hors ago
await tokenRepository
.createQueryBuilder()
.delete()
.from(Token)
.where("tokenDate <= :deleteTime", { deleteTime: delTime})//value dynamically stored in variable does not delete
//.where("tokenDate <= :deleteTime", { deleteTime: "2021-02-08T18:01:10.489Z"})//static hard code value deletes
//.where("tokenDate <= :delTime", { delTime})//Variable put this way will not work either...
.execute();
} catch (error) {
res.status(404).send("Tokens not found");
return;
}
res.status(200).send('Tokens deleted successfuly');
}
Your parameterized query looks correct.
Switch on TypeOrm Query Logging to see the generated SQL, maybe you will be able to see what's going wrong. Paste the generated SQL into the SQL query console to debug.
Alternatively you can write your delete query without parameters and let Sqlite calculate current date -1 day:
.where("tokenDate <= DateTime('Now', '-1 Day'"); // for Sqlite
Note 1: Sqlite DateTime('Now') uses UTC time, so your tokenDate should use UTC also.
Note 2: This syntax is specific to Sqlite, you can do the same in other databases but the syntax is different, e.g. Microsoft SQL Server:
.where("tokenDate <= DATEADD(day, -1, SYSUTCDATETIME()); // for MS SQL Server

How can I track a change to sqlite3 in golang?

everybody. I use the library "github.com/mattn/go-sqlite3" for working with the database.
I need to track the changes made to the database and after these changes are complete, execute some code.
I need to track the changes that another process makes to this database..
For example, there is a Products table with the name and id fields I want to get a notification after the name field has been changed
How can I do this? any solution Thanks
sqlite3conn := []*sqlite3.SQLiteConn{}
sql.Register("sqlite3_with_hook_example",
&sqlite3.SQLiteDriver{
ConnectHook: func(conn *sqlite3.SQLiteConn) error {
sqlite3conn = append(sqlite3conn, conn)
conn.RegisterUpdateHook(func(op int, db string, table string, rowid int64) {
switch op {
case sqlite3.SQLITE_INSERT:
log.Println("Notified of insert on db - ", db, "table:", table, "rowid:", rowid)
}
})
return nil
},
})
This code only tracks the changes that the Go script makes, if I make an insert from the console, it doesn't work

FIRDatabaseReference observe gets empty updates while another reference is running a transaction

We're using Firebase DB together with RxSwift and are running into problems with transactions. I don't think they're related to the combination with RxSwift but that's our context.
Im observing a data in Firebase DB for any value changes:
let child = dbReference.child(uniqueId)
let dbObserverHandle = child.observe(.value, with: { snapshot -> () in
guard snapshot.exists() else {
log.error("empty snapshot - child not found in database")
observer.onError(FirebaseDatabaseConsumerError(type: .notFound))
return
}
//more checks
...
//read the data into our object
...
//finally send the object as Rx event
observer.onNext(parsedObject)
}, withCancel: { _ in
log.error("could not read from database")
observer.onError(FirebaseDatabaseConsumerError(type: .databaseFailure))
})
No problems with this alone. Data is read and observed without any problems. Changes in data are propagated as they should.
Problems occur as soon as another part of the application modifies the data that is observer with a transaction:
dbReference.runTransactionBlock({ (currentData: FIRMutableData) -> FIRTransactionResult in
log.debug("begin transaction to modify the observed data")
guard var ourData = currentData.value as? [String : AnyObject] else {
//seems to be nil data because data is not available yet, retry as stated in the transaction example https://firebase.google.com/docs/database/ios/read-and-write
return TransactionResult.success(withValue: currentData)
}
...
//read and modify data during the transaction
...
log.debug("complete transaction")
return FIRTransactionResult.success(withValue: currentData)
}) { error, committed, _ in
if committed {
log.debug("transaction commited")
observer(.completed)
} else {
let error = error ?? FirebaseDatabaseConsumerError(type: .databaseFailure)
log.error("transaction failed - \(error)")
observer(.error(error))
}
}
The transaction receives nil data at first try (which is something you should be able to handle. We just just call
return TransactionResult.success(withValue: currentData)
in that case.
But this is propagated to the observer described above. The observer runs into the "empty snapshot - child not found in database" case because it receives an empty snapshot.
The transaction is run again, updates the data and commits successfully. And the observer receives another update with the updated data and everything is fine again.
My questions:
Is there any better way to handle the nil-data during the transaction than writing it to the database with FIRTransactionResult.success
This seems to be the only way to complete this transaction run and trigger a re-run with fresh data but maybe I'm missing something-
Why are we receiving the empty currentData at all? The data is obviously there because it's observed.
The transactions seem to be unusable with that behavior if it triggers a 'temporary delete' to all observers of that data.
Update
Gave up and restructured the data to get rid of the necessity to use transactions. With a different datastructure we were able to update the dataset concurrently without risking data corruption.

If one of the multiple adds in a saveChangesAsync fails do the others get added?

I have this function in my application. If the insert of Phrase fails then can someone tell me if the Audit entry still gets added? If that's the case then is there a way that I can package these into a single transaction that could be rolled back.
Also if it fails can I catch this and then still have the procedure exit with an exception?
[Route("Post")]
[ValidateModel]
public async Task<IHttpActionResult> Post([FromBody]Phrase phrase)
{
phrase.StatusId = (int)EStatus.Saved;
UpdateHepburn(phrase);
db.Phrases.Add(phrase);
var audit = new Audit()
{
Entity = (int)EEntity.Phrase,
Action = (int)EAudit.Insert,
Note = phrase.English,
UserId = userId,
Date = DateTime.UtcNow,
Id = phrase.PhraseId
};
db.Audits.Add(audit);
await db.SaveChangesAsync();
return Ok(phrase);
}
I have this function in my application. If the insert of Phrase fails
then can someone tell me if the Audit entry still gets added?
You have written your code in a correct way by calling await db.SaveChangesAsync(); only one time after doing all your modifications on the DbContext.
The answer to your question is: No, the Audit will not be added if Phrase fails.
Because you are calling await db.SaveChangesAsync(); after doing all your things with your entities, Entity Framework wil generate all the required SQL Queries and put them in a single SQL transaction which makes the whole queries as an atomic operation to your database. If one of the generated query e.g. Auditgenerated query failed then the transaction will be rolled back. So every modification that have been done to your database will be removed and so Entity Framework will let your database in a coherent state.

PhoneGap + SQLite Issues

For some reason, the following code doesn't work:
var users = window.SQLitePlugin.openDatabase("users","1.0","Demo",-1);
users.transaction(function(tx){
KovarApp.lib.say("Transaction Started");
var createString = "CREATE TABLE IF NOT EXISTS 'users' ('id' integer(11) primary key, 'username' varchar(25),'password' varchar(100),'firstname' varchar(25),'lastname' varchar(25),'qdatabase' varchar(20),'ugroup' varchar(50),'status' varchar(50))";
tx.executeSql(createString,[],function(tx,res){
KovarApp.lib.say("Create Worked");
},function(){
KovarApp.lib.say("Create Failed");
});
var insertString = "INSERT INTO users (username,password,firstname,lastname,qdatabase,ugroup,status) values ('jktest','e10adc3949ba59abbe56e057f20f883e','Andrew','Rhyne','basictemplate','admin','yes')";
tx.executeSql(insertString,[],function(tx,res){
KovarApp.lib.say("Insert Worked");
},function(){
KovarApp.lib.say("Insert Failed");
});
var testString = "SELECT 1 FROM users WHERE username = 'jktest'";
tx.executeSql(testString,[],function(tx,res){
KovarApp.lib.say("res.rows.length: " + res.rows.length + " -- should be 1");
});
KovarApp.lib.say('Transaction Ended');
});
My log function spits out transaction started and transaction ended, but the callbacks aren't firing for the individual queries. I am using Brody's sqlite3 plugin: https://github.com/brodyspark/PhoneGap-SQLitePlugin-Android
Any help here would be awesome. I don't really know what I am doing wrong but it isn't working at all. As a side note, he has specified that there isn't any limit on database size, and this is denoted by the -1 (meaning no size limit).
All callback functions are executed asynchronously.
In your code, the insert/select commands are started before the previous commands have finished, and the transaction ends before the individual commands have finished.
Move the code to start a command into the success callback function of the previous command.
In other words, the transaction and executeSql calls should always be the last statement in their containing function.
The Javascript files within Brody's project are platform-dependent for the time being. It looks like there will eventually be abstraction in place where the javascript is all portable, but this was the issue - I had the iOS version of the JS file running on Android. Duhh

Resources