The way I resolved this was to:
Always set the keyPath
to be "key"
when creating the IDBStore. This is to take into account the fact that the IndexedDB Shim was using the property called key
to refer to the index of each record in the WebSQL database.
I had to add a clause in the IDBWrapper code:
// cope with WebSQL implementation, keyPath must always be "key" for this to work.
if (!cursor.primaryKey) {
cursor.value.key = cursor.key;
}
Into the _getAllCursor
function as shown (line 682 for me):
_getAllCursor: function (getAllTransaction, store, onSuccess, onError) {
var all = [],
hasSuccess = false,
result = null;
getAllTransaction.oncomplete = function () {
var callback = hasSuccess ? onSuccess : onError;
callback(result);
};
getAllTransaction.onabort = onError;
getAllTransaction.onerror = onError;
var cursorRequest = store.openCursor();
cursorRequest.onsuccess = function (event) {
var cursor = event.target.result;
if (cursor) {
// cope with WebSQL implementation, keyPath must always be key for this to work.
if (!cursor.primaryKey) {
cursor.value.key = cursor.key;
}
all.push(cursor.value);
cursor['continue']();
}
else {
hasSuccess = true;
result = all;
}
};
cursorRequest.onError = onError;
},
Basically what this is doing is, if there is no primaryKey on cursor, from testing with Chrome and Safari, this seemed to be an indicator that the code was working within a WebSQL environment. There may be more solid ways of detecting a lack of IDB support, but this works well for me. So within the if
, just set the 'key' property of the cursor onto the value so that when it ends up being saved to WebSQL it has the right index on there.
However because I don't have the time to test his library and changeover to it, and he was very helpful, I am going to give Kyaw Tun the benefit of the doubt and accept his answer, especially as it appears that IDBWrapper and IndexedDBShim (and JQuery IndexedDB plugin and IndexedDBShim, according to GemK) don't appear to work together when using inline keys. Arguably it is nicer to have 1 dependent library rather than 2.
UPDATE
I did run into problems further down the line with this approach, in one of those "how did this ever work?" scenarios.
Just as I was about to remove IndexedDBShim and IDBWrapper, and replace with ydn-db, I had another look in Safari.
As mentioned, I have no idea how this ever worked, but in
IDBOjectStore.prototype.put
on line 1090 (for me) of IndexedDBShim.js, the key was undefined and then never used in an update.
So I had to add
if (key === undefined && value.key !== undefined) {
key = value.key;
}
UPDATE 2
Really, just ignore this post now, looks like IDBShim is going to be fixed, I am sure that will be a better solution.
However, this solution does have another issue. The check against cursor.primaryKey
isn't definitive, sometimes I have found it does contain the key value in there. So just set cursor.value.key
anyway.
UPDATE 3
In case anyone has not heeded my earlier warning to ignore all this. If you set the key manually it will fail, silently in my case, because if the key is set and keypath also has a value, WebSQL cannot determine if you want inline keys (i.e. you defined keypath) or out-of-line keys (i.e. you actually provided a key value, so presumably you don't want to auto-increment). It is a misconception that to update the record you need to provide the key outside of the object value, as long as the object value you are saving has a property named after the keypath string, containing the correct key, that should be enough.
Saying this, why did I make the change detailed in the first UPDATE originally? Because on ocassion, duplicate records were being added instead of being updated.
The real problem was, for me on line 1003. The primary key is extracted from the object, and then evaluated using !
. For me, in some browsers, the first key was being generated as 0
. This fails the evaluation and therefore WebSQL generates a new auto-increment key.
I changed:
var primaryKey = eval("value['" + props.keyPath + "']");
if (!primaryKey) {
if (props.autoInc === "true") {
getNextAutoIncKey();
}
else {
idbModules.util.throwDOMException(0, "Data Error - Could not eval key from keyPath");
}
}
else {
callback(primaryKey);
}
To:
var primaryKey = eval("value['" + props.keyPath + "']");
if (primaryKey === undefined) {
if (props.autoInc === "true") {
getNextAutoIncKey();
}
else {
idbModules.util.throwDOMException(0, "Data Error - Could not eval key from keyPath");
}
}
else {
callback(primaryKey);
}