ionic sqlite create multiple tables - sqlite

Using IONIC 3 and sqlite3 v2 native, testing on Android device.
When I use one table everything is fine, but when I am trying to create 3 tables then I have a problem with other two.
database.ts (in providers):
...
db: any;
isOpen: Boolean = false;
constructor(private sqlite: SQLite) {}
createDatabase() {
return new Promise((resolve, reject) => {
this.sqlite.create({
name: 'mydatabase.db',
location: 'default'
}).then((db: SQLiteObject) => {
this.isOpen = true;
resolve(db);
}).catch(err => reject(err));
});
}
openDb() {
return new Promise((resolve, reject) => {
if (this.isOpen) {
resolve({});
} else {
this.createDatabase().then(db => {
this.db = db;
this.db.executeSql("CREATE TABLE IF NOT EXISTS first (*my first query...)",{})
.then(("CREATE TABLE IF NOT EXISTS second (*my second query...))"),{})
.then(("CREATE TABLE IF NOT EXISTS third (*my third query...)"),{})
.then(res => resolve(res))
.catch(err=>reject(err));
}).catch(err => reject(err));
}
});
}
error message:
sqlite3_prepare_v2 failure: no such table: second, code 5
second attempt:
openDb() {
return new Promise((resolve, reject) => {
this.createDatabase().then(db => {
this.db = db;
this.db.executeSql("CREATE TABLE IF NOT EXISTS first (firstquery...)",{})
.then(()=>{this.db.executeSql("CREATE TABLE IF NOT EXISTS second (secondquery...)",{})})
.then(res=>resolve(res))
.catch(err=>reject(err));
});
second then never triggered and only first table created.
I used many methods but this above it seems to be a solution nearest to my problem.
Does this method seems right or I have to use something different, in order to make this work?

What executeSql returns is a promise telling whether the given query is successfully executed or not. If you wish to create another table only if the first table is created then you need to do as below
this.db.executeSql("CREATE TABLE IF NOT EXISTS first (*my first query...)",{})
.then(() =>{
this.db.executeSql("CREATE TABLE IF NOT EXISTS second (*my second query...))"),{}).then(()=>{
//Something else if required
}).catch(err => reject(err));
}).catch(err => reject(err));

Related

Updating displayed results after modifying Firestore doc React Native

I have a list of games that I'm able to add to without issue using UseEffect and onSnapshot. I can modify an item in the list without issue, and return one set of results (with the updated data properly displaying). When I try to modify another item (or the item same again), I get this error:
Could not update game: TypeError: undefined is not an object (evaluating '_doc.data().numPlayers') because the results/list of games are null. I'm sure I have something wrong with my code, but I can't figure it out.
Thanks in advance!
Here is my code:
useEffect(() => {
setIsLoading(true)
let results = [];
const unsubscribe = db
.collection('games')
.onSnapshot(
(querySnapshot) => {
querySnapshot.docChanges().forEach(change => {
const id = change.doc.id;
if (change.type === 'added') {
const gameData = change.doc.data();
gameData.id = id;
results.push(gameData);
}
if (change.type === 'modified') {
console.log('Modified game: ', id);
results = results.map(game => {
if (game.id === id) {
return change.doc.data()
}
return game
})
console.log(results)
}
if (change.type === 'removed') {
console.log('Removed game: ', id);
}
});
setIsLoading(false);
setGame(results);
return () => unsubscribe
},
(err) => {
setIsLoading(false);
console.log("Data could not be fetched", err);
}
);
}, []);
I forgot to add the doc ID to the gameData before adding it to the results. I did that in the "added" section, but not in the "modified" section (thinking that it was already included), forgetting that I hadn't added it as an actual field in the database (it just exists as the doc id).

Creating multiple tables in SQLite using React Native SQLite Storage Module using Transaction function

What I want to do is create multiple tables for my react-native android app but it keeps returning an unknown error each time the code execute(but it works fine with creating a single table).
This is the code I have
initDB() {
let db;
return new Promise((resolve) => {
console.log("Plugin integrity check ...");
SQLite.echoTest()
.then(() => {
console.log("Integrity check passed ...");
console.log("Opening database ...");
SQLite.openDatabase(
database_name,
database_version,
database_displayname,
database_size
)
.then(DB => {
db = DB;
console.log("Database OPEN");
db.executeSql('SELECT 1 FROM Feed LIMIT 1').then(() => {
console.log("Database is ready ... executing query ...");
}).catch((error) =>{
console.log("Received error: ", error);
console.log("Database not yet ready ... populating data");
db.transaction((tx) => {
tx.executeSql('CREATE TABLE IF NOT EXISTS Feed (feedId, feedName, feedDesc, feedPrice)');
tx.executeSql('CREATE TABLE IF NOT EXISTS Comment (commentId, feedId, commentDesc)');
tx.executeSql('CREATE TABLE IF NOT EXISTS User (userId, userName, userPass, userAdmin)');
}).then(() => {
console.log("Tables created successfully");
}).catch(error => {
console.log(error);
});
});
resolve(db);
})
.catch(error => {
console.log(error);
});
})
.catch(error => {
console.log("echoTest failed - plugin not functional");
});
});
}
How can I make it create multiple tables
// I believe this could help,
// The top-level 'executeSql' a single SELECT stmt with a catch for creating tables
db.executeSql('SELECT 1 FROM Feed LIMIT 1').then(() => {
console.log("Database is ready ... executing query ...");
}).catch((error) =>{// note- currently all CREATE tables are accually housed in this catch
console.log("Received error: ", error);
console.log("Database not yet ready ... populating data");
db.transaction((tx) => {// This is where you are currently creating all your tables.
// Note- the creation of these tables are actually dependent upon the top-level executeSql.
// In my opinion, I would only keep tables here if they are dependent on a primary table (one-to-many).
// It might be a good idea to move independent tables into top-level executeSql method
//
tx.executeSql('CREATE TABLE IF NOT EXISTS Feed (feedId, feedName, feedDesc, feedPrice)');
// Through observation Comment most likely depends on Feed.
// I would leave Feed and Comment together in this situation.
tx.executeSql('CREATE TABLE IF NOT EXISTS Comment (commentId, feedId, commentDesc)');
// Then, I would separate out User as it looks indepentent from the other tables.
tx.executeSql('CREATE TABLE IF NOT EXISTS User (userId, userName, userPass, userAdmin)');
}).then(() => {
console.log("Tables created successfully");
}).catch(error => {
console.log(error);
});
});
// Possible Solution
// Feed & Comment
db.executeSql('SELECT 1 FROM Feed LIMIT 1').then(() => {
console.log("Database is ready ... executing query ...");
}).catch((error) =>{
console.log("Received error: ", error);
console.log("Database not yet ready ... populating data");
db.transaction((tx) => {
tx.executeSql('CREATE TABLE IF NOT EXISTS Feed (feedId, feedName, feedDesc, feedPrice)');
// by observation, Feed -< Comment (one-to-many) relationship
// so I would say keeping this here is a good idea
tx.executeSql('CREATE TABLE IF NOT EXISTS Comment (commentId, feedId, commentDesc)');
}).then(() => {
console.log("Table created successfully");
}).catch(error => {
console.log(error);
});
});
// User - indepentent table, create own SELECT>>catch
db.executeSql('SELECT 1 FROM Feed LIMIT 1').then(() => {
console.log("Database is ready ... executing query ...");
}).catch((error) =>{
console.log("Received error: ", error);
console.log("Database not yet ready ... populating data");
db.transaction((tx) => {
tx.executeSql('CREATE TABLE IF NOT EXISTS User (userId, userName, userPass, userAdmin)');
}).then(() => {
console.log("Table created successfully");
}).catch(error => {
console.log(error);
});
});
// Please let me know if this helps. Thanks.

How to add column to table?

Using "react-native-sqlite-storage" the application is already live on Google Play. I want to add two columns to an existing database table in such a way the application will not crash for existing users.
When you will add column to existing table you will get error like
error: {"message":"no such column: todoStar","code":5} // todoStar is column name
In error block you can alter the table and add your column. Make sure you write the code inside error block
if (error.message.indexOf('no such column: todoStar') > -1) {
dbConnection.transaction(function (tx2) {
let query = 'ALTER TABLE todos ADD todoStar Int(32);'; // todos is tableName
tx2.executeSql(
query,
'',
async (tx3) => {
console.log('new column added');
},
(error) => {
console.error(
'error: while adding column' + JSON.stringify(error),
);
},
);
});
}
As usual official documentation only contains basic information, and there is not much information on migration or database upgrades.
After hours of long research, I found that SQLite has user_version which can be used to know which version the user currently has, based on that we can execute upgrade queries.
I am using expo-sqlite, but the below solution should also work for react-native-sqlite-storage
Example:
/** database.tsx **/
export function getUserDBVersion() {
return new Promise<SQLite.SQLResultSet>((resolve, reject) => {
database.transaction((tx) => {
tx.executeSql(
`PRAGMA user_version`,
[],
(_, res) => {
resolve(res);
},
(_, error) => {
reject(error);
return true;
}
);
});
});
}
export function updateUserDBVersion(version: number) {
return new Promise<SQLite.SQLResultSet>((resolve, reject) => {
database.transaction((tx) => {
tx.executeSql(
`PRAGMA user_version = ?`,
[version],
(_, res) => {
resolve(res);
},
(_, error) => {
reject(error);
return true;
}
);
});
});
}
/** App.tsx **/
useEffect(() => {
getUserDBVersion().then((res) => {
const user_version = res.rows._array[0].user_version;
if (user_version < 1) {
//User has old version
//(1) Run function to upgrade your database tables
alterTables().then(() => {
//(2) Update user_version so that next time you can check if < 2 for new updates
updateUserVersion(1);
});
}
});
}, []);
I think this is an elegant way for 2 reasons
You can update all of your databases and tables in one place
You can display the splash screen until all this migration is completed
*For readability I have not added error handling part above
*Most of the above code will be the same for react-native-sqlite-storage, if not update your code accordingly
*The above code is in typescript, simply remove types if you are using jsx

ionic3 SQLite unit testing

I'm using ionic3 and am trying to unit test (using Jasmine/karma) the following code. Can anyone offer any guidance.
createTable(databaseName: string): Promise<any> {
let sql = 'CREATE TABLE IF NOT EXISTS table)';
return new Promise((resolve, reject) => {
this.sqlite.create({
name: databaseName + '.db',
location: 'default'
}).then((db: SQLiteObject) => {
db.executeSql(sql, [])
.then(() => {
console.info('Executed SQL');
})
.catch((e) => {
console.error('createTable Error', e.message);
reject(e);
});
});
})
}
Here's my work in progress attempt to test
describe('createTable()', () => {
let db: SQLiteObject;
it('Should call function createTable', () => {
spyOn(service.sqlite, 'create').and.returnValue(Promise.resolve(
));
let databaseName = 'testDatabase';
service.createEncounterTable(databaseName);
expect(service.sqlite.create).toHaveBeenCalled();
});
});
I'm getting the following error return ERROR: 'Unhandled Promise rejection:', 'Cannot read property 'executeSql' of undefined', and dont seem to be able to gain access to executeSql.
As far as I can tell, from the lack of online resource on testing SQLite I have to assume that most people don't test this. Thanks in advance to any guidance.

Firestore Cloud functions looping for unknown reason

I have some basic code to retrieve data from my firestore data base. I am getting the correct data but for some reason that operation appears to be occurring twice.
The logs show all the documents printed out twice. This could be really troublesome with more complex operations. I feel life I'm probably doing something goofy.
exports.deleteProject = functions.firestore.document('{userID}/projects/easy/{projectID}').onDelete(event => {
.........
console.log(db)
var collectionRef = db.collection(userID).doc(xxx).collection(yyy);
console.log(collectionRef)
var getDoc = collectionRef.get()
.then(snapshot => {
snapshot.forEach(doc => {
console.log(doc.id, '=>', doc.data());
});
})
.catch(err => {
console.log('Error getting documents', err);
});
}
The "........" is just string stuff to reference to proper point in that database
Below "=>" indicates a doc pointing to its data. I cleaned it up for brevity.
Logs:
12:04:37.820 PM
info
deleteProject
tail => {
info => {
xxxxxxxxxxxxxxx => {
tail => {
info => {
xxxxxxxxxxxxxxx => {
yyyyyyyyyyyyyyy => {

Resources