Catching DatabaseException in Flutter - sqlite

I wanted to know how I can handle and succesfully catch my Database Exception. I had a primary key ID and another key called name in my Sqlite database. Im using the flutter plugin sqflite. I wanted to handle the case when there is already a entry in my database with the given ID i want to display ID already exists, for that I want to know how I can handle the exceptions.
I tried to surround the code in a try catch block but Im not able to make FLutter realize about the DatabaseException class, it gives me error saying "DatabaseException" isn't a type and can't be used in on catch clause. I want to add a new Subject into my database after a button is pressed and if user enters the same ID value as already existing one I want to handle the error.
onSubmitted: (test) async {
Subject newSubject = Subject(name: name, id: test);
try {
await DBProvider.db.newSubject(newSubject);
} on DatabaseException {}
},

You should import sqflite in the dart file where you're using the exception, add
this:
import 'package:sqflite/sqflite.dart';

Related

Flutter listen to one document change using provider

I have seen examples of listening to document changes in streambuilder, but is it possible to use it in providers? I want to listen to changes in the document in userinfo collection.
Here is my code:
in databaseservice.dart
Stream <DocumentSnapshot> get info{
return userinfo.doc(uid).snapshots();
}
In main
return MultiProvider(
providers: [
StreamProvider<DocumentSnapshot>.value(
value: DatabaseService().info
), // other providers
In wrapper where I need to see the change:
final info = Provider.of<DocumentSnapshot>(context).data();
However, I'll first get error:
The method 'data' was called on null.
Receiver: null
Tried calling: data()
And later, the info variable is giving me null instead of a map.
I want to let users input their name and age after their first signup, but not when they sign in. So my idea is that when users sign up, there will be a new document in the collection, "userinfo", which sets their age and name as null at first.
Then the wrapper checks if the name is null. If null, it will turn to the gather information page. If it has a value, it will turn to the home page.
Could anyone tell me where I am doing wrong with my document snapshot thing, or have a better idea to implement this?

Sqflite Database exception type in flutter

I am using package Sqflite for database management in flutter, I want to discriminate database exception, i.e. I want to know what went wrong during INSERT query, whether it was missing table or a mismatch in column name or trying to insert duplicate value in a column with UNIQUE constraint.
In following code I am able to catch exceptions but I am unable to discriminate them.
DatabaseHelper helper = DatabaseHelper.instance;
try {
id = await helper.insertMeter(dataMap);
print('inserted row: $id');
} catch (ex) {
// Here I want to know what caused exception.
print('Failed to insert: ' + ex.toString());
}
Please help.
Unfortunately, the native report we get is not always consistent between iOS and Android. There are some helpers you can use if you catch explicitely a DatabaseException such as isUniqueConstraintError, isNoSuchTableError. It is based on parsing the exception text thrown for a few example tested. Some could be added assuming we can parse a relevant error but it cannot be more than what we can extract from the string you currently print.

Firebase Cloud Firestore create entry or update if it already exists

I have an issue of knowing when to add or update an entry to a Firebase Firestore database.
Using doc_ref.set will add a document if it does not exist. It will also override all of a documents fields if it already exists and set is called.
Using doc_ref.update will update fields of a document if the document exists. If the document does not exist, nothing happens.
How do I add a new field to a document if the field does not currently exist, or update the field if it does exist? I could read the database and check if the field exists, and then use either set or update, but surely there is an easier way?
What you're looking for is the merge option in the set operation. From the documentation on setting a document:
var cityRef = db.collection('cities').doc('BJ');
var setWithMerge = cityRef.set({
capital: true
}, { merge: true });
If you're not sure whether the document exists, pass the option to merge the new data with any existing document to avoid overwriting entire documents.
I had similar problem, but I wanted to update array field using FieldValue.arrayUnion, FieldValue.arrayRemove. Therefore could not use set/merge.
To avoid checking if document exists before update (costs 1 read), I wrapped update statement with try/catch and check for error code 5 (NOT FOUND). In this case, set value instead of update.
Code example:
try {
await docRef.update({
arrayField: admin.firestore.FieldValue.arrayUnion(...dates),
});
} catch (error) {
if (error.code === 5) {
// Error: 5 NOT_FOUND: no entity to update:
await docRef.set({
arrayField: dates,
});
}
}

Vue-Firebase: Updating children & writing to child

I started working with Firebase and Vue also with VueFire and i dont understand how to update child nodes at Firebase.
I opened a firebase project and connected to it and i can push data to it.
Firebase
I made a vue component
import db from '../FireBase'
let team = db.ref('Teams');//Reference to Teams at firebase
let miss = db.ref().child('Teams'); //Attempt to get to the children of Teams
export default {
name: "App",
firebase: {
Teams_loc: db.ref('Teams'),
Mission: this.Teams_loc.child('Teams'),
missionKey: db.ref().child('Teams').push("").key,
},
...
I manage to get the Teams from firebase and send data to it:
this.$firebaseRefs.Teams_loc.push({
"test": "tester"
});
Which works but when i try to update the children inside
this.miss.push({
"where": "am i"
})
I get the following error
Cannot read property 'child' of undefined
And when i try to update a child
this.$firebaseRefs.missionKey.update(arr[0]);//arr[0] is an object
I tried looking at quite a few places but nothing seems to do the trick.
Thanks,
When you do the following you are doing an error at the second line.
Teams_loc: db.ref('Teams'),
Mission: this.Teams_loc.child('Teams'),
There is no child of the Teams node that has a key with the value `Teams.
So if you want to update an item, you first have to get its key (e.g. -LEzOBT-mp.....) and do as follows, as explained in the doc:
updateItem: function (item) {
// create a copy of the item
const copy = {...item}
// remove the .key attribute
delete copy['.key']
//possibly update (or add) some values of (to) the item
this.$firebaseRefs.Teams_loc.child(item['.key']).set(copy)
}
Also (if I am not mistaking) doing db.ref() will generate an error because you have to pass a value to ref().
I suggest that you study a bit more the doc and the example: https://github.com/vuejs/vuefire and https://github.com/vuejs/vuefire/blob/master/examples/todo-app/index.html
Update following your comment. Details on how to "know the random generated key"
According to the documentation:
Each record in the bound array will contain a .key property which
specifies the key where the record is stored. So if you have data at
/Teams/-LEzOBT-mp...../, the record for that data will have a .key of
"-LEzOBT-mp.....".
Look at this part of the doc: https://github.com/vuejs/vuefire#array-bindings.
So with this you will get all the keys of the Teams object. You have now to choose the item you want to update.
You could also declare a query in your firebase object, like:
firebase: {
team21483: this.database
.ref('Teams')
.orderByChild('teamCode')
.equalTo('21483')
}
and you would get an array with only one team, the one with TeamCode = 21483.
The best approach, in this latest case, is to manually bind to this Firebase query with the $bindAsArray (or possibly the $bindAsObject) instance methods, using a variable that you pass to equalTo().

Trouble inserting data into sqlite database with JDBC

This is what my code currently looks like:
import java.sql.*
import java.sql.SQLException
class SqliteDB {
val conn = DriverManager.getConnection("jdbc:sqlite:cs2820-database.db")
fun createUser123(userID: String, password: String, adminStatus: String) {
val statement = conn.prepareStatement("INSERT INTO Users(id,pass,admin) VALUES(?,?,?)")
statement.setString(1, userID)
statement.setString(2,password)
statement.setString(3,adminStatus)
println("123")
statement.executeUpdate()
conn.commit()
println("User Created")
}
// create a user
fun createUser(userID: String, password: String, adminStatus: String) {
println("inside createUser")
val sql = "INSERT INTO Users(id,pass,admin) VALUES(?,?,?)"
Class.forName("org.sqlite.JDBC")
try {
conn.use { conn ->
conn.prepareStatement(sql).use { pstmt ->
pstmt.setString(1, userID)
pstmt.setString(2, password)
pstmt.setString(3, adminStatus)
pstmt.executeUpdate()
//conn.commit()
pstmt.close()
}
}
} catch (e: SQLException) {
println(e.message)
}
}
fun main(args: Array<String>) {
val db = SqliteDB()
db.createUser("Jim", "password", "false")
}
I have tested two different createUser methods and most everything I have tried will return the error [SQLITE_BUSY] The database file is locked (database is locked). I have several other methods within the same SqliteDB class that query data ("SELECT") that all work correctly, but every time I try to perform any kind of update of information I am given the same error. I am at a loss for what to do at this point having searched many different forums and posts about syntax and such.
The full stacktrace is as follows:
Exception in thread "main" org.sqlite.SQLiteException: [SQLITE_BUSY] The database file is locked (database is locked)
at org.sqlite.core.DB.newSQLException(DB.java:909)
at org.sqlite.core.DB.newSQLException(DB.java:921)
at org.sqlite.core.DB.execute(DB.java:822)
at org.sqlite.core.DB.executeUpdate(DB.java:863)
at org.sqlite.jdbc3.JDBC3PreparedStatement.executeUpdate(JDBC3PreparedStatement.java:99)
at SqliteDB.createUser(SqliteDB.kt:50)
at SqliteDBKt.main(SqliteDB.kt:122)
I don't believe the issue is with an open connection as the error seems to occur as soon as I try to execute the update to the User table. The insert seems to hit some sort of loop of some kind at the execution stage.
EDIT: Something else I noticed, is that when attempting to create a new user with a primary key (userID) that already exists, I am given a uniqueness error, suggesting the update is going thru and realizing the userID is already in the table; however, there is still the issue with the INSERT creating a new row in the table. I'm just not sure how to go about debugging that specific issue.
So this has been quite an issue for me for the past week or so and it looks like I have been able to figure out the issue.
I am aware that you have to make sure to close any connections to the database before opening a new one to avoid locks and any other similar issues. What I did not realize is that I have been using a program in IntelliJ called "DB Browser". This plugin creates a UI that much more easily allows you to access and change any aspect of the database you want without using actual SQL commands. What I didn't realize is this plugin takes up the only writeable connection the SQLite and JDBC allow to the database.
So, after deleting the connection to the database through the DB Browser plugin, all of my functions are working properly.

Resources