Is there a way to rebuild Dexie keys? - ios

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

Related

ng-block-ui not working with Angular 7 concatmap

I'm using NgBlockUI and BlockUIHttpModule with blockAllRequestsInProgress set to true in an app I'm working on. In general it's working fine, but on one page I'm using a concat map to perform some action and then update the data. The first request, the update, triggers BlockUI fine, but the second one doesn't. Otherwise, it executes properly. It's just a little jarring for the user since the results seem to update without warning. Here's the code for the function:
onUpdate(event: items[]) {
this.updateService.update(event).concatMap(
_ => this.seachService.search(this.cachedSearch)
).subscribe(
resp => this.handleResponse(resp),
err => this.handleError(err)
);
}
I tried calling BlockUI directly, but still no luck. As a last resort, I'm going to make the whole thing one request, but I'd like to at least understand why this isn't working.
This happened to me as well. This issue occurs for sequential HTTP calls (usually with await) wherein the second request is not blocked by ng-block-ui.
As fix what I did was set blockAllRequestsInProgress to false. The behavior is just the same but setting it to false yields more predictable results:
BlockUIHttpModule.forRoot({
blockAllRequestsInProgress: false,
requestFilters: [urlFilter]
}),
I've also updated to ng-block-ui to latest version as of this writing:
"ng-block-ui": "^2.1.8",

firestore allow write for only one field

I have the following security rule set up in Firestore:
match /ads/{adId} {
allow read: if true;
allow write: if (request.auth.uid == request.resource.data.ownerId) ||
(request.auth != null &&
request.resource.data.size() == 1 &&
request.resource.data.keys().hasAll(['markedFavoriteBy']));
}
The owner of the document has all write permissions but other users can only write in one field - which is called 'markedFavoriteBy'. It seems to work in the Firestore simulator but not in Xcode (with iPhone simulator).
Important to note that the path does not go down to the document field - just to the document. I know that it is not possible to attach a security rule to a single field. However, with this workaround it should still work.
The iOS client code to update the field is the following:
let adRef = Firestore.firestore().collection("ads").document(ad.key!)
let value: FieldValue = ad.isFavorite ? FieldValue.arrayUnion([uid]) : FieldValue.arrayRemove([uid])
adRef.updateData([DatabaseKeys.markedFavoriteBy : value]) { (error) in
if let error = error {
print(error.localizedDescription)
}
}
The error printed to the console is of course: "Missing or insufficient permissions."
I can't find an error in the code - is this a bug that is somehow related to the very recent iOS Firestore SDK update? the functions arrayUnion and arrayRemove have been added only in that recent update.
Sanity check: I have changed the write rule to
allow write: if true;
Result: The array 'markedFavoriteBy' can be changed without any problem.
thanks for any hints in the right direction.
I'm not able to reproduce the entire scenario as I'm not sure of the actual contents of your database. However, what I suspect is the problem is that it doesn't grant permission because request.resource.data represents the new version of the entire document (which will be stored after the update is approved).
You probably want to use request.writeFields doc
Bear in mind that request.writeFields is not available in the simulator.

Rally SDk2.0 rc3 PrefManager update method issue

I had an app that worked fine with sdk 2.0rc3 namely updating a preference value with more strings. Recently this stopped working and when I investigated it turned out that if pref does not exist PrefManager.update method creates one with requested value correctly. But if I want to update already existing one then it fails to apply any sort of update.
Does anyone know if this is caused by the latest Rally changes on the platform?
Or maybe an idea what might be wrong?
_saveNewPrefs : function (prefValue){
var sortedPrefs = prefValue;//_.sortby(prefValue,'keyword');
var appPrefValueEncoded = Ext.JSON.encode(sortedPrefs);
// resave entire pref again with new build
var newPref = {};
newPref[this.appPrefName] = appPrefValueEncoded;
console.log ('workspace', this.appWorkspace);
console.log('newPref', newPref);
Rally.data.PreferenceManager.update({
settings: newPref,
workspace: this.appWorkspace,
success: function(updatedRecords, notUpdatedRecords) {
console.log ('Pair saved', updatedRecords);
console.log('this',this);
this._displayGrid();
},
scope : this
});
},
Error thrown is this
"Validation error: Preference.Name conflicts with buildList55 where buildList55 is the pre-existing pref name
Finally fixed it - so the issues was twofold - I needed to add the appID AND I needed to recreate the pref as the old one was someone locked for any updates since was updated without passing appID to the Pref Manager.

How to implement IndexDB in IOS

I am developing a mobile application using phonegap, Initially I have developed using WEBSQL but now I m planning to move it on INDEXDB. The problem is it does not have direct support on IOS , so on doing much R&D I came to know using IndexedDB Polyfil we can implement it on IOS too
http://blog.nparashuram.com/2012/10/indexeddb-example-on-cordova-phonegap.html
http://nparashuram.com/IndexedDBShim/
Can some please help me how to implement this as there are not enough documentation for this and I cannot figure out a any other solution / api except this
I have tested this on safari 5.1.7
Below is my code and Error Image
var request1 = indexedDB.open(dbName, 5);
request1.onsuccess = function (evt) {
db = request1.result;
var transaction = db.transaction(["AcceptedOrders"], "readwrite");
var objectStore = transaction.objectStore("AcceptedOrders");
for (var i in data) {
var request = objectStore.add(data[i]);
request.onsuccess = function (event) {
// alert("am again inserted")
// event.target.result == customerData[i].ssn;
};
}
};
request1.onerror = function (evt) {
alert("IndexedDB error: " + evt.target.errorCode);
};
Error Image
One blind guess
Maybe your dbName contains illegal characters for WebSQL database names. The polyfill doesn't translate your database names in any kind. So if you create a database called my-test, it would try to create a WebSQL database with the name my-test. This name is acceptable for an IndexedDB database, but in WebSQL you'll get in trouble because of the - character. So your database name has to match both, the IndexedDB and the WebSQL name conventions.
... otherwise use the debugger
You could set a break point onto your alert(...); line and use the debugger to look inside the evt object. This way you may get either more information about the error itself or more information to share with us.
To do so, enable the development menu in the Safari advanced settings, hit F10 and go to Developer > Start debugging JavaScript (something like that, my Safari is in a different language). Now open then "Scripts" tab in the developer window, select your script and set the break point by clicking on the line number. Reload the page and it should stop right in your error callback, where you can inspect the evt object.
If this doesn't help, you could get the non-minified version of the polyfill and try set some breakpoints around their open function to find the origin of this error.
You could try my open source library https://bitbucket.org/ytkyaw/ydn-db/wiki/Home. It works on iOS and Android.

Corrupting Access databases when inserting values

Recently, a program that creates an Access db (a requirement of our downstream partner), adds a table with all memo columns, and then inserts a bunch of records stopped working. Oddly, there were no changes in the environment that I could see and nothing in any diffs that could have affected it. Furthermore, this repros on any machine I've tried, whether it has Office or not and if it has Office, whether it's 32- or 64-bit.
The problem is that when you open the db after the program runs, the destination table is empty and instead there's a MSysCompactError table with a bunch of rows.
Here's the distilled code:
var connectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=corrupt.mdb;Jet OLEDB:Engine Type=5";
// create the db and make a table
var cat = new ADOX.Catalog();
try
{
cat.Create(connectionString);
var tbl = new ADOX.Table();
try
{
tbl.Name = "tbl";
tbl.Columns.Append("a", ADOX.DataTypeEnum.adLongVarWChar);
cat.Tables.Append(tbl);
}
finally
{
Marshal.ReleaseComObject(tbl);
}
}
finally
{
cat.ActiveConnection.Close();
Marshal.ReleaseComObject(cat);
}
using (var connection = new OleDbConnection(connectionString))
{
connection.Open();
// insert a value
using (var cmd = new OleDbCommand("INSERT INTO [tbl] VALUES ( 'x' )", connection))
cmd.ExecuteNonQuery();
}
Here are a couple of workarounds I've stumbled into:
If you insert a breakpoint between creating the table and inserting the value (line 28 above), and you open the mdb with Access and close it again, then when the app continues it will not corrupt the database.
Changing the engine type from 5 to 4 (line 1) will create an uncorrupted mdb. You end up with an obsolete mdb version but the table has values and there's no MSysCompactError. Note that I've tried creating a database this way and then upgrading it to 5 programmatically at the end with no luck. I end up with a corrupt db in the newest version.
If you change from memo to text fields by changing the adLongVarWChar on line 13 to adVarWChar, then the database isn't corrupt. You end up with text fields in the db instead of memo, though.
A final note: in my travels, I've seen that MSysCompactError is related to compacting the database, but I'm not doing anything explicit to make the db compact.
Any ideas?
As I replied to HasUp:
According MS support, creation of Jet databases programmatically is deprecated. I ended up checking in an empty model database and then copying it whenever I needed a new one. See http://support.microsoft.com/kb/318559 for more info.

Resources