ATTACH INDEXEDDB DATABASE always fails on first try and succeeds on second - alasql

I am not sure what if anything I am doing wrong or if this is a bug.
My IndexedDB exists and has data in it. I am currently using alasql to query the data AFTER i retrieve it from IndexedDB, however I would much rather do this in a singe step as this make joins and other queries across multiple tables much simpler.
Here is my code:
const queryDB = async (dbName, query) => {
const result = await alasql.promise([
`CREATE INDEXEDDB DATABASE IF NOT EXISTS ${dbName};`,
`ATTACH INDEXEDDB DATABASE ${dbName};`,
`USE ${dbName};`,
]);
console.log(result);
return await alasql.promise(query);
};
(also tried without semicolon...)
the first call to this function fails with 'Error: Database ... does not exist' where ... is the dbName
while the second one succeeds and returns the value from the query.
I have been trying just about everything I can everything I can think of and the only working solution is using callbacks (as in the example on the wiki) which is NOT what I want to do.

Oddly enough this work:
const queryDB = async (dbName, query) => {
await alasql.promise(`ATTACH INDEXEDDB DATABASE ${dbName};USE DATABASE ${dbName};`);
return await alasql.promise(query);
};

Related

Is it possible to seed data only not exists with typeorm-seeding?

In ormconfig.ts, it can set seeds to point to a path for all the files.
{
...
seeds: ['db/seed/*.ts'],
},
Is it possible to run only one file with an option like
$ npm run seed:run -f db/seed/file1.ts
In other words, if the data already been seeded into db, run it again will create duplicated data. How to avoid it?
I just encountered this. What I did was check first if the data exists (get the records first then loop to each record), if not, push the updates to an array. Then call insert command with the updates.
const updates = [];
const records = await getRecords();
records.foreach(record => {
if (!exists) updates.push(...);
});
await connection
.createQueryBuilder()
.insert()
.into(TableName)
.values(updates)
.execute()

Cloud functions in Firebase trigger are not part of the transaction update

I am using the realtime database and I am using transactions to ensure the integrity of my data set. In my example below I am updating currentTime on every update.
export const updateTime = functions.database.ref("/users/{userId}/projects/{projectId}")
.onUpdate((snapshot) => {
const beforeData = snapshot.before.val();
const afterData = snapshot.after.val();
if (beforeData.currentTime !== afterData.currentTime) {
return Promise.resolve();
} else {
return snapshot.after.ref.update( {currentTime: new Date().getTime()})
.catch((err) =>{
console.error(err);
});
}
});
It seems the cloud function is not part of the transaction, but triggers multiple updates in my clients, which I try to avoid.
For example, I watched this starter tutorial which replaces :pizza: with a pizza emoji. In my client I would see :pizza: for one frame before it gets replaced with the emoji. I know, the pizza tutorial is just an example, but I am running into a similar issue. Any advice is highly appreciated!
Cloud Functions don't run as part of the database transaction indeed. They run after the database has been updated, and receive "before" and "after" snapshots of the affected data.
If you want a Cloud Function to serve as an approval process, the idiomatic approach is to have the clients write to a different location (typically called a pending queue) that the function listens to. The function then performs whatever operation it wants, and writes the result to the final location.

Is there a way to rebuild Dexie keys?

I had a working app that uses Dexie. After upgrading to iOS 10.3, lookups by key are not working. (This is actually an indexeddb problem, not Dexie per se, I'm sure.) I'm still in shock but I have been able to confirm that the data is there by doing db.table.each(function(p) {}, and the fields used in keys are there and correct. But if I do
db.table.get(primarykey, function(p) {}
or
db.table.where("somekey").equals(nonprimarykey).first(function(p) {}
p is undefined.
I tried doing .db.table.each and then putting each retrieved object back to see if that would rebuild the keys, and it worked in Firefox, but doesn't work in Safari or Chrome (still can't retrieve by key).
I also tried specifying a new version with the same key structure and an empty upgrade, and that didn't do anything (but I only tried it in Chrome).
Everything is fine if the database is created AFTER installing 10.3 but I'm hoping that my customers won't have to delete their databases.
Is there any way to repair this without losing data?
This seems to be an upgrade bug in Safari and should really be filed on bugs.webkit.org. Assume this is something that will be fixed there as the Safari team is very responsive when it comes to critical bugs. Please file it!
As for a workaround, I would suggest to recreate the database. Copy the database to a new database, delete it, then copy back and delete the intermediate copy. I've not verified the code below, so you have to test it.
function check_and_fix_IOS_10_3_upgrade_issue () {
return (somehowCheckIfWeNeedToDoThis) ?
recreateDatabase() : Promise.resolve();
}
function recreateDatabase () {
copyDatabase("dbName", "dbName_tmp").then(()=>{
return Dexie.delete("dbName");
}).then(()=>{
return copyDatabase("dbName_tmp", "dbName");
}).then(()=>{
return Dexie.delete("dbName_tmp");
});
}
function copyDatabase(fromDbName, toDbName) {
return new Dexie(fromDbName).open().then(db => {
let schema = db.tables.reduce((schema, table) => {
schema[table.name] = [table.schema.primKey.src]
.concat(table.schema.indexes.map(idx => idx.src))
.join(',');
}, {});
let dbCopy = new Dexie(toDbName);
dbCopy.version(db.verno).stores(schema);
return dbCopy.open().then(()=>{
// dbCopy is now successfully created with same version and schema as source db.
// Now also copy the data
return Promise.all(
db.tables.map(table =>
table.toArray().then(rows => dbCopy.table(table.name).bulkAdd(rows))));
}).finally(()=>{
db.close();
dbCopy.close();
});
})
}
Regarding "somehowCheckIfWeNeedToDoThis", I can't answer exactly how to do it. Maybe user-agent sniff + cookie (set persistent cookie when fixed, so that it wont be recreated over and over). Maybe you'll find a better solution.
Then before you open your database (maybe before your app is launched) you'd need to do something like:
check_and_fix_IOS_10_3_upgrade_issue()
.then(()=>app.start())
.catch(err => {
// Display error, to user
});
I ran into the same issue, using the db.js library. All of my app data is wiped on upgrade.
Based on my tests, it looks like the 10.2 -> 10.3 upgrade is wiping any data in tables that have autoIncrement set to false. Data saved in autoIncrement=true tables is still accessible after the upgrade.
If this is the case, it's a pretty serious bug. The autoIncrement function of Safari had a host of troubles and caused a lot of us to switch to managing our own IDs instead.
I haven't tested this theory with vanilla JS yet. if someone wants to do that please add your results to the bugs.webkit.org ticket

Invalidate or clear certain entity types from breeze.js cache

Given a fairly standard scenario in an application, of having a list of entities that can be added/edited/deleted etc. When I first load the page, I query the entites with breeze. All good. If I edit an entity, breeze sees this and the entity is saved AND updated in the cache, so when I return to the page with the list, the item shows any relevant changes. If I delete an entity, breeze will again save that change AND update it's cache so the entity no longer appears when I return to the list page and requery the data (locally from the cache I should point out).
However, if I add a new entity, it does not appear on the list page (assuming it satisfies the requirements of the query). I'm guessing that breeze is caching the results of a query specific to that query, rather than actually querying the cache (does that make sense)?
Assuming that is the case, is there a way of telling breeze to remove or invalidate cache items relating to a specific entity type, as opposed to clearing the cache completely? I can always avoid querying the cache and go directly to the server, but that seems a waste when I know breeze has the newly created entity in the cache, it just isn't showing it to me.
I use a single method for a lot of my queries, so it may just be the way I'm handling the local querying in that method, therefore I have included it below, in case that is the cause.
var defaultQuery = function (observable, resourceName, orderby, where, expand, forceRemote, localEntityName, localCountThreshold, page, count) {
var query = EntityQuery.from(resourceName);
if (orderby) {
query = query.orderBy(orderby);
if (page) {
query = query.skip(pageSize * (page()));
query = query.take(pageSize);
query = query.inlineCount();
}
}
if (where)
query = query.where(where);
if (expand)
query = query.expand(expand);
if (!forceRemote) {
if (localEntityName) {
query = query.toType(localEntityName);
}
var localResult = manager.executeQueryLocally(query);
if (localCountThreshold) {
if (localResult.length > localCountThreshold) {
observable(localResult);
return Q.resolve();
}
} else {
if (localResult.length > 0) {
observable(localResult);
return Q.resolve();
}
}
}
return query.using(manager)
.execute()
.then(function (data) {
if (observable) {
observable(data.results);
}
if (count) {
count(data.inlineCount);
}
var logMsg = 'Retrieved ' + resourceName + ' from remote data source with order by {' + orderby + '}, where clause {' + where + '} and expand properties {' + expand + '}';
if (page)
logMsg += ' for page {' + page() + '} with total record count {' + count() + '}';
log(logMsg, data, true);
})
.fail(queryFailed);
};
Usually in questions like this, I have just misunderstood some aspect of how breeze works, so if anyone can correct me, I'd be very appreciative.
Thanks!
Update
I have included the flow of actions step by step to show in more detail what I meant.
Execute Query for all objects
Query is executed locally first, returns nothing as no cache data exists.
Query is then executed against the database - returns [A,B,C]
Edit A - rename to AA - call saveChanges()
Execute same query for all objects
Query is executed locally first, returns [AA,B,C]
Query skips executing against database as results have been found from cache.
Delete B (setDeleted()) - call saveChanges()
Execute same query for all objects
Query is executed locally first, returns [AA,C]
Query skips executing against database as results have been found from cache.
Create new entity - set name to D - call saveChanges()
Execute same query for all objects
Query is executed locally first, returns [AA,C] (does not include new D object!)
The point I'm trying to discover is that local queries return changes saved for edits and deletes, but not for add operations. I could remove entities from the cache by using setDetached as Jay suggested, but I would need to do that for all entities of a specific type one-by-one. That could be a big process.
Update again
It appears the behaviour I saw was the result of some mistake by myself. Having double and triple checked everything as a result of Jay's assertion that the results should be there (see below), the 'added' objects are now appearing, but I honestly can't explain what I did to prevent them in the first place.
Just to be clear, Breeze updates the cache when you edit an entity, it does NOT save that entity until you call EntityManager.saveChanges(). So until you call "saveChanges" the cache and your database will be in different states.
What you might be seeing is the result of the idea that when you requery an entity that has already been changed in the EntityManager, the action of merging the server side data with the client side cache is controlled by the EntityQuery.queryOptions.mergeStrategy. By default, an EntityQuery has a MergeStrategy of PreserveChanges which means that a server side result will NOT overwrite any "modified" records in the cache.
Per the later part of your post, you can remove any entity from the local cache simply by calling the entity's "entityAspect.setDetached" method.

What local databases are available/recommended in trigger.io apps, if any?

I'm investigating building a mobile app with trigger.io, but I'm not finding good documentation on local database options. My app will send data to an external API, but needs to be able to store data locally as a draft (if the user is offline, the API is unavailable, whatever).
I see that there's a prefs module for storing data, but it does not seem like the right thing (correct me if I'm wrong). What options are recommended here? Is there something analogous to the SQLite plugin for PhoneGap, perhaps?
This probably depends on what your usage patterns are going to be.
For example, forge.prefs could get a bit fiddly if you want to do any kind of interesting queries, but could work well if you just want to persist a single JavaScript object structure. Using window.localStorage is likely to have similar pros/cons.
Alternatively, you can use the WebSQL API in your JavaScript: http://docs.trigger.io/en/v1.4/release-notes.html#v1-3-5. You don't need to use a module for this, it should work for any Android or iOS app built with Forge. This essentially gives you an SQLite database accessible from JavaScript. To give you a feel for the API, here's an example:
// create db
var db = openDatabase('mydb', '1.0', 'example database', 2 * 1024 * 1024);
db.transaction(function (tx) {
tx.executeSql('CREATE TABLE IF NOT EXISTS foo (id unique, text)');
tx.executeSql('INSERT INTO foo (id, text) VALUES (1, "foobar")');
});
// query db
db.transaction(function (tx) {
tx.executeSql('SELECT * FROM foo', [], function (tx, results) {
var rows = results.rows;
for (var i = 0; i < rows.length; ++i) {
forge.logging.info("row text: " + rows.item(i).text);
}
});
});
You should be able to find some tutorials about on the web!

Resources