Title: [114464] trunk
Revision
114464
Author
commit-qu...@webkit.org
Date
2012-04-17 16:26:02 -0700 (Tue, 17 Apr 2012)

Log Message

IndexedDB chooses wrong record on PREV_NO_DUPLICATE index cursor
https://bugs.webkit.org/show_bug.cgi?id=60746

Patch by Alec Flett <alecfl...@chromium.org> on 2012-04-17
Reviewed by Ojan Vafai.

Source/WebCore:

When iterating backwards with PREV_NO_DUPLICATE, keep walking past
the 'prev' key until it changes, then walk forward one time. This
covers the object store and index cases.

Test: storage/indexeddb/mozilla/index-prev-no-duplicate.html

* Modules/indexeddb/IDBLevelDBBackingStore.cpp:
(WebCore):

LayoutTests:

Test for PREV_NO_DUPLICATE.

* storage/indexeddb/cursor-prev-no-duplicate.html: Added.
* storage/indexeddb/cursor-prev-no-duplicate.txt: Added.
* storage/indexeddb/mozilla/index-prev-no-duplicate-expected.txt: Added.
* storage/indexeddb/mozilla/index-prev-no-duplicate.html: Added.
* storage/indexeddb/resources/cursor-prev-no-duplicate.js: Added.
(prepareDatabase.openreq.onsuccess.verreq.onsuccess):
(prepareDatabase.openreq.onsuccess):
(prepareDatabase):
(populateStore):
(runAllTests):
(waitFor):
(complete.fireCallback):
(complete):
(testFarRangeCursor):
(makeOpenKeyCursor):
(runTest.trans.oncomplete):
(runTest.testFunction):
(runTest):

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (114463 => 114464)


--- trunk/LayoutTests/ChangeLog	2012-04-17 23:20:45 UTC (rev 114463)
+++ trunk/LayoutTests/ChangeLog	2012-04-17 23:26:02 UTC (rev 114464)
@@ -1,3 +1,31 @@
+2012-04-17  Alec Flett  <alecfl...@chromium.org>
+
+        IndexedDB chooses wrong record on PREV_NO_DUPLICATE index cursor
+        https://bugs.webkit.org/show_bug.cgi?id=60746
+
+        Reviewed by Ojan Vafai.
+
+        Test for PREV_NO_DUPLICATE.
+
+        * storage/indexeddb/cursor-prev-no-duplicate.html: Added.
+        * storage/indexeddb/cursor-prev-no-duplicate.txt: Added.
+        * storage/indexeddb/mozilla/index-prev-no-duplicate-expected.txt: Added.
+        * storage/indexeddb/mozilla/index-prev-no-duplicate.html: Added.
+        * storage/indexeddb/resources/cursor-prev-no-duplicate.js: Added.
+        (prepareDatabase.openreq.onsuccess.verreq.onsuccess):
+        (prepareDatabase.openreq.onsuccess):
+        (prepareDatabase):
+        (populateStore):
+        (runAllTests):
+        (waitFor):
+        (complete.fireCallback):
+        (complete):
+        (testFarRangeCursor):
+        (makeOpenKeyCursor):
+        (runTest.trans.oncomplete):
+        (runTest.testFunction):
+        (runTest):
+
 2012-04-17  Yuta Kitamura  <yu...@chromium.org>
 
         Unreviewed, unskip some WebSocket worker tests that should not be skipped.

Added: trunk/LayoutTests/storage/indexeddb/cursor-prev-no-duplicate-expected.txt (0 => 114464)


--- trunk/LayoutTests/storage/indexeddb/cursor-prev-no-duplicate-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/storage/indexeddb/cursor-prev-no-duplicate-expected.txt	2012-04-17 23:26:02 UTC (rev 114464)
@@ -0,0 +1,125 @@
+Test IndexedDB behavior when iterating backwards with and without NO_DUPLICATE
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+indexedDB = self.indexedDB || self.webkitIndexedDB || self.mozIndexedDB || self.msIndexedDB || self.OIndexedDB;
+
+
+openreq = indexedDB.open('cursor-prev-no-duplicate')
+db = openreq.result
+verreq = db.setVersion('1')
+Deleted all object stores.
+store = db.createObjectStore('store')
+store.createIndex('index', 'sorted')
+
+populating store...
+trans = db.transaction('store', IDBTransaction.READ_WRITE)
+store = trans.objectStore('store');
+store.put({sorted: 3, value: 111}, 1)
+store.put({sorted: 2, value: 222}, 2)
+store.put({sorted: 1, value: 333}, 3)
+store.put({sorted: 10, value: 444}, 17)
+store.put({sorted: 10, value: 555}, 16)
+store.put({sorted: 10, value: 666}, 15)
+testFarRangeCursor: upper bound is well out of range, results always the same, whether open or closed
+storeReq = store.openCursor(IDBKeyRange.upperBound(7, false), 2)
+
+Testing: store.openCursor(IDBKeyRange.upperBound(7, false), 2)
+PASS cursor.key is 3
+PASS cursor.value.value is 333
+storeReq = store.openCursor(IDBKeyRange.upperBound(7, true), 2)
+DONE
+
+Testing: store.openCursor(IDBKeyRange.upperBound(7, true), 2)
+PASS cursor.key is 3
+PASS cursor.value.value is 333
+storeReq = index.openCursor(IDBKeyRange.upperBound(7, false), 2)
+DONE
+
+Testing: index.openCursor(IDBKeyRange.upperBound(7, false), 2)
+PASS cursor.key is 3
+PASS cursor.value.value is 111
+PASS cursor.primaryKey is 1
+storeReq = index.openCursor(IDBKeyRange.upperBound(7, true), 2)
+DONE
+
+Testing: index.openCursor(IDBKeyRange.upperBound(7, true), 2)
+PASS cursor.key is 3
+PASS cursor.value.value is 111
+PASS cursor.primaryKey is 1
+storeReq = index.openKeyCursor(IDBKeyRange.upperBound(7, false), 2)
+DONE
+
+Testing: index.openKeyCursor(IDBKeyRange.upperBound(7, false), 2)
+PASS cursor.key is 3
+PASS cursor.primaryKey is 1
+storeReq = index.openKeyCursor(IDBKeyRange.upperBound(7, true), 2)
+DONE
+
+Testing: index.openKeyCursor(IDBKeyRange.upperBound(7, true), 2)
+PASS cursor.key is 3
+PASS cursor.primaryKey is 1
+storeReq = store.openCursor(IDBKeyRange.upperBound(3, false), 2)
+DONE
+
+Testing: store.openCursor(IDBKeyRange.upperBound(3, false), 2)
+PASS cursor.key is 3
+PASS cursor.value.value is 333
+storeReq = store.openCursor(IDBKeyRange.upperBound(3, true), 2)
+DONE
+
+Testing: store.openCursor(IDBKeyRange.upperBound(3, true), 2)
+PASS cursor.key is 2
+PASS cursor.value.value is 222
+storeReq = index.openCursor(IDBKeyRange.upperBound(3, false), 2)
+DONE
+
+Testing: index.openCursor(IDBKeyRange.upperBound(3, false), 2)
+PASS cursor.key is 3
+PASS cursor.value.value is 111
+PASS cursor.primaryKey is 1
+storeReq = index.openCursor(IDBKeyRange.upperBound(3, true), 2)
+DONE
+
+Testing: index.openCursor(IDBKeyRange.upperBound(3, true), 2)
+PASS cursor.key is 2
+PASS cursor.value.value is 222
+PASS cursor.primaryKey is 2
+storeReq = index.openKeyCursor(IDBKeyRange.upperBound(3, false), 2)
+DONE
+
+Testing: index.openKeyCursor(IDBKeyRange.upperBound(3, false), 2)
+PASS cursor.key is 3
+PASS cursor.primaryKey is 1
+storeReq = index.openKeyCursor(IDBKeyRange.upperBound(3, true), 2)
+DONE
+
+Testing: index.openKeyCursor(IDBKeyRange.upperBound(3, true), 2)
+PASS cursor.key is 2
+PASS cursor.primaryKey is 2
+testNoDuplicate: there are 3 values, but we should return always the first one
+storeReq = store.openCursor(IDBKeyRange.upperBound(15, false), 3)
+DONE
+
+Testing: store.openCursor(IDBKeyRange.upperBound(15, false), 3)
+PASS cursor.key is 15
+PASS cursor.value.value is 666
+PASS cursor.primaryKey is 15
+storeReq = index.openCursor(IDBKeyRange.upperBound(15, false), 3)
+DONE
+
+Testing: index.openCursor(IDBKeyRange.upperBound(15, false), 3)
+PASS cursor.key is 10
+PASS cursor.value.value is 666
+PASS cursor.primaryKey is 15
+storeReq = index.openKeyCursor(IDBKeyRange.upperBound(15, false), 3)
+DONE
+
+Testing: index.openKeyCursor(IDBKeyRange.upperBound(15, false), 3)
+PASS cursor.key is 10
+PASS cursor.primaryKey is 15
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/storage/indexeddb/cursor-prev-no-duplicate.html (0 => 114464)


--- trunk/LayoutTests/storage/indexeddb/cursor-prev-no-duplicate.html	                        (rev 0)
+++ trunk/LayoutTests/storage/indexeddb/cursor-prev-no-duplicate.html	2012-04-17 23:26:02 UTC (rev 114464)
@@ -0,0 +1,10 @@
+<html>
+<head>
+<script src=""
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/storage/indexeddb/mozilla/index-prev-no-duplicate-expected.txt (0 => 114464)


--- trunk/LayoutTests/storage/indexeddb/mozilla/index-prev-no-duplicate-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/storage/indexeddb/mozilla/index-prev-no-duplicate-expected.txt	2012-04-17 23:26:02 UTC (rev 114464)
@@ -0,0 +1,183 @@
+Test IndexedDB: iterating backwards through an index, skipping duplicates
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB;
+PASS indexedDB == null is false
+IDBDatabaseException = window.IDBDatabaseException || window.webkitIDBDatabaseException;
+PASS IDBDatabaseException == null is false
+IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction;
+PASS IDBTransaction == null is false
+IDBCursor = window.IDBCursor || window.webkitIDBCursor;
+PASS IDBCursor == null is false
+IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange;
+PASS IDBKeyRange == null is false
+indexedDB.open(name, description)
+openSuccess():
+db = event.target.result
+request = db.setVersion('1')
+Deleted all object stores.
+objectStore = db.createObjectStore(objectStoreName);
+First, add all our data to the object store.
+request = objectStore.add(objectStoreData[i].value, objectStoreData[i].key);
+request = objectStore.add(objectStoreData[i].value, objectStoreData[i].key);
+request = objectStore.add(objectStoreData[i].value, objectStoreData[i].key);
+request = objectStore.add(objectStoreData[i].value, objectStoreData[i].key);
+request = objectStore.add(objectStoreData[i].value, objectStoreData[i].key);
+request = objectStore.add(objectStoreData[i].value, objectStoreData[i].key);
+request = objectStore.add(objectStoreData[i].value, objectStoreData[i].key);
+request = objectStore.add(objectStoreData[i].value, objectStoreData[i].key);
+request = objectStore.add(objectStoreData[i].value, objectStoreData[i].key);
+Now create the indexes.
+objectStore.createIndex(indexData[i].name, indexData[i].keyPath, indexData[i].options);
+objectStore.createIndex(indexData[i].name, indexData[i].keyPath, indexData[i].options);
+objectStore.createIndex(indexData[i].name, indexData[i].keyPath, indexData[i].options);
+testPrev()
+trans = db.transaction(objectStoreName)
+objectStore = trans.objectStore(objectStoreName);
+keyIndex = 8;
+request = objectStore.index('height').openCursor(null, IDBCursor.PREV);
+cursor = event.target.result;
+PASS cursor.key is 73
+PASS cursor.primaryKey is '237-23-7740'
+PASS cursor.value.name is 'Sam'
+PASS cursor.value.height is 73
+PASS cursor.value.weight is 110
+cursor.continue();
+keyIndex--;
+  => 7
+cursor = event.target.result;
+PASS cursor.key is 73
+PASS cursor.primaryKey is '237-23-7734'
+PASS cursor.value.name is 'Ron'
+PASS cursor.value.height is 73
+PASS cursor.value.weight is 180
+cursor.continue();
+keyIndex--;
+  => 6
+cursor = event.target.result;
+PASS cursor.key is 65
+PASS cursor.primaryKey is '237-23-7739'
+PASS cursor.value.name is 'Jef'
+PASS cursor.value.height is 65
+PASS cursor.value.weight is 120
+cursor.continue();
+keyIndex--;
+  => 5
+cursor = event.target.result;
+PASS cursor.key is 65
+PASS cursor.primaryKey is '237-23-7738'
+PASS cursor.value.name is 'Leo'
+PASS cursor.value.height is 65
+PASS cursor.value.weight is 180
+cursor.continue();
+keyIndex--;
+  => 4
+cursor = event.target.result;
+PASS cursor.key is 65
+PASS cursor.primaryKey is '237-23-7737'
+PASS cursor.value.name is 'Pat'
+PASS cursor.value.height is 65
+PASS cursor.value.weight is 100
+cursor.continue();
+keyIndex--;
+  => 3
+cursor = event.target.result;
+PASS cursor.key is 65
+PASS cursor.primaryKey is '237-23-7736'
+PASS cursor.value.name is 'Joe'
+PASS cursor.value.height is 65
+PASS cursor.value.weight is 150
+cursor.continue();
+keyIndex--;
+  => 2
+cursor = event.target.result;
+PASS cursor.key is 60
+PASS cursor.primaryKey is '237-23-7732'
+PASS cursor.value.name is 'Bob'
+PASS cursor.value.height is 60
+PASS cursor.value.weight is 120
+cursor.continue();
+keyIndex--;
+  => 1
+cursor = event.target.result;
+PASS cursor.key is 58
+PASS cursor.primaryKey is '237-23-7735'
+PASS cursor.value.name is 'Sue'
+PASS cursor.value.height is 58
+PASS cursor.value.weight is 130
+cursor.continue();
+keyIndex--;
+  => 0
+cursor = event.target.result;
+PASS cursor.key is 52
+PASS cursor.primaryKey is '237-23-7733'
+PASS cursor.value.name is 'Ann'
+PASS cursor.value.height is 52
+PASS cursor.value.weight is 110
+cursor.continue();
+keyIndex--;
+  => -1
+cursor = event.target.result;
+No cursor: null
+PASS keyIndex is -1
+testPrevNoDuplicate()
+objectStore = db.transaction(objectStoreName).objectStore(objectStoreName);
+keyIndex = 8;
+request = objectStore.index('height').openCursor(null, IDBCursor.PREV_NO_DUPLICATE);
+cursor = event.target.result;
+keyIndex -= 1
+  => Entering with keyIndex = 7
+PASS cursor.key is 73
+PASS cursor.primaryKey is '237-23-7734'
+PASS cursor.value.name is 'Ron'
+PASS cursor.value.height is 73
+PASS cursor.value.weight is 180
+cursor.continue();
+keyIndex--;
+cursor = event.target.result;
+keyIndex -= 3;
+  => Entering with keyIndex = 3
+PASS cursor.key is 65
+PASS cursor.primaryKey is '237-23-7736'
+PASS cursor.value.name is 'Joe'
+PASS cursor.value.height is 65
+PASS cursor.value.weight is 150
+cursor.continue();
+keyIndex--;
+cursor = event.target.result;
+  => Entering with keyIndex = 2
+PASS cursor.key is 60
+PASS cursor.primaryKey is '237-23-7732'
+PASS cursor.value.name is 'Bob'
+PASS cursor.value.height is 60
+PASS cursor.value.weight is 120
+cursor.continue();
+keyIndex--;
+cursor = event.target.result;
+  => Entering with keyIndex = 1
+PASS cursor.key is 58
+PASS cursor.primaryKey is '237-23-7735'
+PASS cursor.value.name is 'Sue'
+PASS cursor.value.height is 58
+PASS cursor.value.weight is 130
+cursor.continue();
+keyIndex--;
+cursor = event.target.result;
+  => Entering with keyIndex = 0
+PASS cursor.key is 52
+PASS cursor.primaryKey is '237-23-7733'
+PASS cursor.value.name is 'Ann'
+PASS cursor.value.height is 52
+PASS cursor.value.weight is 110
+cursor.continue();
+keyIndex--;
+cursor = event.target.result;
+  => Entering with keyIndex = -1
+No cursor: null
+PASS keyIndex is -1
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/storage/indexeddb/mozilla/index-prev-no-duplicate.html (0 => 114464)


--- trunk/LayoutTests/storage/indexeddb/mozilla/index-prev-no-duplicate.html	                        (rev 0)
+++ trunk/LayoutTests/storage/indexeddb/mozilla/index-prev-no-duplicate.html	2012-04-17 23:26:02 UTC (rev 114464)
@@ -0,0 +1,193 @@
+<!DOCTYPE html>
+<!--
+  original test: http://mxr.mozilla.org/mozilla2.0/source/dom/indexedDB/test/test_indexes.html?force=1
+  license of original test:
+    " Any copyright is dedicated to the Public Domain.
+      http://creativecommons.org/publicdomain/zero/1.0/ "
+-->
+<html>
+<head>
+<script src=""
+<script>
+    var jsTestIsAsync = true;
+</script>
+<script src=""
+</head>
+<body>
+<script>
+
+description("Test IndexedDB: iterating backwards through an index, skipping duplicates");
+if (window.layoutTestController)
+    layoutTestController.waitUntilDone();
+
+function test()
+{
+    indexedDB = evalAndLog("indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB;");
+    shouldBeFalse("indexedDB == null");
+    IDBDatabaseException = evalAndLog("IDBDatabaseException = window.IDBDatabaseException || window.webkitIDBDatabaseException;");
+    shouldBeFalse("IDBDatabaseException == null");
+    IDBTransaction = evalAndLog("IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction;");
+    shouldBeFalse("IDBTransaction == null");
+    IDBCursor = evalAndLog("IDBCursor = window.IDBCursor || window.webkitIDBCursor;");
+    shouldBeFalse("IDBCursor == null");
+    IDBKeyRange = evalAndLog("IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange;");
+    shouldBeFalse("IDBKeyRange == null");
+
+    name = window.location.pathname;
+    description = "My Test Database";
+    request = evalAndLog("indexedDB.open(name, description)");
+    request._onsuccess_ = openSuccess;
+    request._onerror_ = unexpectedErrorCallback;
+}
+
+function openSuccess()
+{
+    debug("openSuccess():");
+    db = evalAndLog("db = event.target.result");
+
+    objectStoreName = "People";
+
+    objectStoreData = [
+        { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } },
+        { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } },
+        { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } },
+        { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } },
+        { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } },
+        { key: "237-23-7737", value: { name: "Pat", height: 65, weight: 100 } },
+        { key: "237-23-7738", value: { name: "Leo", height: 65, weight: 180 } },
+        { key: "237-23-7739", value: { name: "Jef", height: 65, weight: 120 } },
+        { key: "237-23-7740", value: { name: "Sam", height: 73, weight: 110 } },
+    ];
+
+    indexData = [
+        { name: "name", keyPath: "name", options: { unique: true } },
+        { name: "height", keyPath: "height", options: { } },
+        { name: "weight", keyPath: "weight", options: { unique: false } }
+    ];
+
+    objectStoreDataHeightSort = [
+        { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } },
+        { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } },
+        { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } },
+        { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } },
+        { key: "237-23-7737", value: { name: "Pat", height: 65, weight: 100 } },
+        { key: "237-23-7738", value: { name: "Leo", height: 65, weight: 180 } },
+        { key: "237-23-7739", value: { name: "Jef", height: 65, weight: 120 } },
+        { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } },
+        { key: "237-23-7740", value: { name: "Sam", height: 73, weight: 110 } },
+    ];
+
+    request = evalAndLog("request = db.setVersion('1')");
+    request._onsuccess_ = createAndPopulateObjectStore;
+    request._onerror_ = unexpectedErrorCallback;
+}
+
+function createAndPopulateObjectStore()
+{
+    deleteAllObjectStores(db);
+
+    objectStore = evalAndLog("objectStore = db.createObjectStore(objectStoreName);");
+
+    debug("First, add all our data to the object store.");
+    addedData = 0;
+    for (i in objectStoreData) {
+      request = evalAndLog("request = objectStore.add(objectStoreData[i].value, objectStoreData[i].key);");
+      request._onerror_ = unexpectedErrorCallback;
+      request._onsuccess_ = function(event)
+      {
+        if (++addedData == objectStoreData.length) {
+          createIndexes();
+        }
+      }
+    }
+}
+
+function createIndexes()
+{
+    debug("Now create the indexes.");
+    for (i in indexData) {
+      evalAndLog("objectStore.createIndex(indexData[i].name, indexData[i].keyPath, indexData[i].options);");
+    }
+
+
+    testPrev();
+}
+
+function testPrev()
+{
+    debug("testPrev()");
+    trans = evalAndLog("trans = db.transaction(objectStoreName)");
+    objectStore = evalAndLog("objectStore = trans.objectStore(objectStoreName);");
+    keyIndex = evalAndLog("keyIndex = 8;");
+    // first try with just PREV
+    request = evalAndLog("request = objectStore.index('height').openCursor(null, IDBCursor.PREV);");
+    request._onerror_ = unexpectedErrorCallback;
+    request._onsuccess_ = function(event)
+    {
+        cursor = evalAndLog("cursor = event.target.result;");
+        if (cursor) {
+            shouldBe("cursor.key", "" + objectStoreDataHeightSort[keyIndex].value.height);
+            shouldBe("cursor.primaryKey", "'" + objectStoreDataHeightSort[keyIndex].key + "'");
+            shouldBe("cursor.value.name", "'" + objectStoreDataHeightSort[keyIndex].value.name + "'");
+            shouldBe("cursor.value.height", "" + objectStoreDataHeightSort[keyIndex].value.height);
+            if ('weight' in cursor.value) {
+                shouldBe("cursor.value.weight", "" + objectStoreDataHeightSort[keyIndex].value.weight);
+            }
+
+            evalAndLog("cursor.continue();");
+            evalAndLog("keyIndex--;");
+            debug("  => " + keyIndex);
+        }
+        else {
+            debug("No cursor: " + cursor);
+            shouldBe("keyIndex", "-1");
+        }
+    };
+    trans._oncomplete_ = testPrevNoDuplicate;
+}
+
+function testPrevNoDuplicate()
+{
+    debug("testPrevNoDuplicate()");
+    objectStore = evalAndLog("objectStore = db.transaction(objectStoreName).objectStore(objectStoreName);");
+    keyIndex = evalAndLog("keyIndex = 8;");
+    request = evalAndLog("request = objectStore.index('height').openCursor(null, IDBCursor.PREV_NO_DUPLICATE);");
+    request._onerror_ = unexpectedErrorCallback;
+    request._onsuccess_ = function (event)
+    {
+        cursor = evalAndLog("cursor = event.target.result;");
+            if (keyIndex == 8) {
+                evalAndLog("keyIndex -= 1");
+            }
+            if (keyIndex == 6) {
+                evalAndLog("keyIndex -= 3;");
+            }
+        debug("  => Entering with keyIndex = " + keyIndex);
+        if (cursor) {
+            shouldBe("cursor.key", "" + objectStoreDataHeightSort[keyIndex].value.height);
+            shouldBe("cursor.primaryKey", "'" + objectStoreDataHeightSort[keyIndex].key + "'");
+            shouldBe("cursor.value.name", "'" + objectStoreDataHeightSort[keyIndex].value.name + "'");
+            shouldBe("cursor.value.height", "" + objectStoreDataHeightSort[keyIndex].value.height);
+            if ('weight' in cursor.value) {
+                shouldBe("cursor.value.weight", "" + objectStoreDataHeightSort[keyIndex].value.weight);
+            }
+
+            evalAndLog("cursor.continue();");
+            evalAndLog("keyIndex--;");
+        }
+        else {
+            debug("No cursor: " + cursor);
+            shouldBe("keyIndex", "-1");
+            finishJSTest();
+        }
+    }
+}
+
+var successfullyParsed = true;
+
+test();
+
+</script>
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/storage/indexeddb/resources/cursor-prev-no-duplicate.js (0 => 114464)


--- trunk/LayoutTests/storage/indexeddb/resources/cursor-prev-no-duplicate.js	                        (rev 0)
+++ trunk/LayoutTests/storage/indexeddb/resources/cursor-prev-no-duplicate.js	2012-04-17 23:26:02 UTC (rev 114464)
@@ -0,0 +1,236 @@
+if (this.importScripts) {
+    importScripts('../../../fast/js/resources/js-test-pre.js');
+    importScripts('shared.js');
+}
+
+description("Test IndexedDB behavior when iterating backwards with and without NO_DUPLICATE");
+
+function test()
+{
+    removeVendorPrefixes();
+
+    prepareDatabase();
+}
+
+function prepareDatabase()
+{
+    debug("");
+    evalAndLog("openreq = indexedDB.open('cursor-prev-no-duplicate')");
+    openreq._onerror_ = unexpectedErrorCallback;
+    openreq._onsuccess_ = function()
+    {
+        evalAndLog("db = openreq.result");
+        evalAndLog("verreq = db.setVersion('1')");
+        verreq._onerror_ = unexpectedErrorCallback;
+        verreq._onsuccess_ = function()
+        {
+            deleteAllObjectStores(db);
+            store = evalAndLog("store = db.createObjectStore('store')");
+            evalAndLog("store.createIndex('index', 'sorted')");
+            verreq.result._oncomplete_ = populateStore;
+        };
+    };
+}
+
+function populateStore()
+{
+    debug("");
+    debug("populating store...");
+    evalAndLog("trans = db.transaction('store', IDBTransaction.READ_WRITE)");
+    evalAndLog("store = trans.objectStore('store');");
+    trans._onerror_ = unexpectedErrorCallback;
+    trans._onabort_ = unexpectedAbortCallback;
+
+    evalAndLog("store.put({sorted: 3, value: 111}, 1)");
+    evalAndLog("store.put({sorted: 2, value: 222}, 2)");
+    evalAndLog("store.put({sorted: 1, value: 333}, 3)");
+    evalAndLog("store.put({sorted: 10, value: 444}, 17)");
+    evalAndLog("store.put({sorted: 10, value: 555}, 16)");
+    evalAndLog("store.put({sorted: 10, value: 666}, 15)");
+    trans._oncomplete_ = testFarRangeCursor_closed;
+}
+
+
+function testFarRangeCursor_closed()
+{
+    debug("testFarRangeCursor: upper bound is well out of range, results always the same, whether open or closed");
+
+    runTest(makeOpenCursor("store", 7, false, IDBCursor.PREV),
+            { expectedValue: 333, expectedKey: 3})._onsuccess_ =
+                testFarRangeCursor_open;
+}
+
+function testFarRangeCursor_open()
+{
+    runTest(makeOpenCursor("store", 7, true, IDBCursor.PREV),
+            { expectedValue: 333, expectedKey: 3})._onsuccess_ =
+                testFarRangeCursor_indexClosed;
+}
+
+function testFarRangeCursor_indexClosed()
+{
+        // here '7' refers to the 'sorted' value
+        runTest(makeOpenCursor("index", 7, false, IDBCursor.PREV),
+                { expectedValue: 111, expectedKey: 3, expectedPrimaryKey: 1})._onsuccess_ =
+                    testFarRangeCursor_indexOpen;
+}
+function testFarRangeCursor_indexOpen()
+{
+    runTest(makeOpenCursor("index", 7, true, IDBCursor.PREV),
+            { expectedValue: 111, expectedKey: 3, expectedPrimaryKey: 1})._onsuccess_ =
+                testFarRangeCursor_indexKeyOpen;
+}
+
+function testFarRangeCursor_indexKeyOpen()
+{
+    // here '7' refers to the sorted value
+    runTest(makeOpenKeyCursor("index", 7, false, IDBCursor.PREV),
+            { expectedKey: 3, expectedPrimaryKey: 1})._onsuccess_ =
+                testFarRangeCursor_indexKeyClosed;
+}
+
+function testFarRangeCursor_indexKeyClosed()
+{
+    runTest(makeOpenKeyCursor("index", 7, true, IDBCursor.PREV),
+            { expectedKey: 3, expectedPrimaryKey: 1})._onsuccess_ =
+                testBoundaryCursor_closed;
+}
+
+function testBoundaryCursor_closed()
+{
+    runTest(makeOpenCursor("store", 3, false, IDBCursor.PREV),
+            { expectedValue: 333, expectedKey: 3})._onsuccess_ =
+                testBoundaryCursor_open;
+};
+
+function testBoundaryCursor_open()
+{
+    runTest(makeOpenCursor("store", 3, true, IDBCursor.PREV),
+            { expectedValue: 222, expectedKey: 2})._onsuccess_ =
+                testBoundaryCursor_indexClosed;
+}
+
+function testBoundaryCursor_indexClosed()
+{
+    // by index sort order, we should return them in a different order
+    runTest(makeOpenCursor("index", 3, false, IDBCursor.PREV),
+            { expectedValue: 111, expectedKey: 3, expectedPrimaryKey: 1})._onsuccess_ =
+                testBoundaryCursor_indexOpen;
+}
+
+function testBoundaryCursor_indexOpen()
+{
+    runTest(makeOpenCursor("index", 3, true, IDBCursor.PREV),
+            { expectedValue: 222, expectedKey: 2, expectedPrimaryKey: 2})._onsuccess_ =
+                testBoundaryCursor_indexKeyClosed;
+}
+
+function testBoundaryCursor_indexKeyClosed()
+{
+
+    // now the value doesn't matter, just the primary key
+    runTest(makeOpenKeyCursor("index", 3, false, IDBCursor.PREV),
+            { expectedKey: 3, expectedPrimaryKey: 1})._onsuccess_ =
+                testBoundaryCursor_indexKeyOpen;
+}
+
+function testBoundaryCursor_indexKeyOpen()
+{
+    runTest(makeOpenKeyCursor("index", 3, true, IDBCursor.PREV),
+            { expectedKey: 2, expectedPrimaryKey: 2})._onsuccess_ =
+                testNoDuplicate_closed;
+}
+
+function testNoDuplicate_closed()
+{
+    debug("testNoDuplicate: there are 3 values, but we should return always the first one");
+
+    // PREV_NO_DUPLICATE doesn't really affect non-indexed
+    // cursors, but we should make sure we get the right one
+    // anyway
+    runTest(makeOpenCursor("store", 15, false, IDBCursor.PREV_NO_DUPLICATE),
+            { expectedValue: 666, expectedKey: 15, expectedPrimaryKey: 15 })
+                ._onsuccess_ = testNoDuplicate_open;
+}
+
+function testNoDuplicate_open()
+{
+    // still three values, but now the index says we should return the
+    // second one
+    runTest(makeOpenCursor("index", 15, false, IDBCursor.PREV_NO_DUPLICATE),
+            { expectedValue: 666, expectedKey: 10, expectedPrimaryKey: 15})._onsuccess_ = testNoDuplicate_indexKeyClosed;
+}
+
+
+function testNoDuplicate_indexKeyClosed()
+{
+    // same behavior as above, without a value
+    runTest(makeOpenKeyCursor("index", 15, false, IDBCursor.PREV_NO_DUPLICATE),
+            { expectedKey: 10, expectedPrimaryKey: 15})._onsuccess_ =
+                finishJSTest;
+}
+
+
+function makeOpenCursor(obj, upperBound, open, direction)
+{
+    return obj + ".openCursor(IDBKeyRange.upperBound(" + upperBound + ", " +
+        open + "), " +
+            direction + ")";
+}
+
+function makeOpenKeyCursor(obj, upperBound, open, direction)
+{
+    return obj + ".openKeyCursor(IDBKeyRange.upperBound(" + upperBound + ", " +
+        open + "), " +
+            direction + ")";
+}
+
+function runTest(openCursor, exp)
+{
+    trans = db.transaction('store', IDBTransaction.READ_ONLY);
+
+    // expose these for code in openCursor
+    store = trans.objectStore('store');
+    index = store.index('index');
+    trans._onerror_ = unexpectedErrorCallback;
+    trans._onabort_ = unexpectedAbortCallback;
+    trans._oncomplete_ = function()
+    {
+        debug("DONE");
+    };
+
+    var result = {};
+
+    var testFunction = function()
+    {
+        // expose for evalAndLog
+        expectation = exp;
+
+        debug("\nTesting: " + openCursor);
+        cursor = event.target.result;
+        if (cursor === null) {
+            testFailed("null cursor");
+            return;
+        }
+
+        shouldBe("cursor.key", JSON.stringify(expectation.expectedKey));
+        if ("value" in cursor) {
+            shouldBe("cursor.value.value", JSON.stringify(expectation.expectedValue));
+        }
+        else if ("expectedValue" in expectation)
+            testFailed("Test broken: shouldn't have expectedValue");
+
+        if ("expectedPrimaryKey" in expectation) {
+            shouldBe("cursor.primaryKey", JSON.stringify(expectation.expectedPrimaryKey));
+        }
+
+        result.onsuccess();
+    };
+
+    storeReq = evalAndLog("storeReq = " + openCursor);
+    storeReq._onsuccess_ = testFunction;
+
+    return result;
+}
+
+test();
\ No newline at end of file

Modified: trunk/Source/WebCore/ChangeLog (114463 => 114464)


--- trunk/Source/WebCore/ChangeLog	2012-04-17 23:20:45 UTC (rev 114463)
+++ trunk/Source/WebCore/ChangeLog	2012-04-17 23:26:02 UTC (rev 114464)
@@ -1,3 +1,19 @@
+2012-04-17  Alec Flett  <alecfl...@chromium.org>
+
+        IndexedDB chooses wrong record on PREV_NO_DUPLICATE index cursor
+        https://bugs.webkit.org/show_bug.cgi?id=60746
+
+        Reviewed by Ojan Vafai.
+
+        When iterating backwards with PREV_NO_DUPLICATE, keep walking past
+        the 'prev' key until it changes, then walk forward one time. This
+        covers the object store and index cases.
+
+        Test: storage/indexeddb/mozilla/index-prev-no-duplicate.html
+
+        * Modules/indexeddb/IDBLevelDBBackingStore.cpp:
+        (WebCore):
+
 2012-04-17  Yi Shen  <yi.4.s...@nokia.com>
 
         Caret is not rendered properly inside an input element with text-indent

Modified: trunk/Source/WebCore/Modules/indexeddb/IDBLevelDBBackingStore.cpp (114463 => 114464)


--- trunk/Source/WebCore/Modules/indexeddb/IDBLevelDBBackingStore.cpp	2012-04-17 23:20:45 UTC (rev 114463)
+++ trunk/Source/WebCore/Modules/indexeddb/IDBLevelDBBackingStore.cpp	2012-04-17 23:26:02 UTC (rev 114464)
@@ -1039,24 +1039,48 @@
 {
     RefPtr<IDBKey> previousKey = m_currentKey;
 
+    // When iterating with PREV_NO_DUPLICATE, spec requires that the
+    // value we yield for each key is the first duplicate in forwards
+    // order.
+    RefPtr<IDBKey> lastDuplicateKey;
+
+    bool forward = m_cursorOptions.forward;
+
     for (;;) {
         if (nextState == Seek) {
-            if (m_cursorOptions.forward)
+            if (forward)
                 m_iterator->next();
             else
                 m_iterator->prev();
         } else
             nextState = Seek; // for subsequent iterations
 
-        if (!m_iterator->isValid())
+        if (!m_iterator->isValid()) {
+            if (!forward && lastDuplicateKey.get()) {
+                // We need to walk forward because we hit the end of
+                // the data.
+                forward = true;
+                continue;
+            }
+
             return false;
+        }
 
+        // The iterator will contain values deleted during iteration.
         Vector<char> trash;
         if (!m_transaction->get(m_iterator->key(), trash))
-             continue;
+            continue;
 
-        if (isPastBounds())
+        if (isPastBounds()) {
+            if (!forward && lastDuplicateKey.get()) {
+                // We need to walk forward because now we're beyond the
+                // bounds defined by the cursor.
+                forward = true;
+                continue;
+            }
+
             return false;
+        }
 
         if (!haveEnteredRange())
             continue;
@@ -1067,7 +1091,7 @@
             continue;
 
         if (key) {
-            if (m_cursorOptions.forward) {
+            if (forward) {
                 if (m_currentKey->isLessThan(key))
                     continue;
             } else {
@@ -1076,12 +1100,35 @@
             }
         }
 
-        if (m_cursorOptions.unique && m_currentKey->isEqual(previousKey.get()))
-            continue;
+        if (m_cursorOptions.unique) {
 
+            if (m_currentKey->isEqual(previousKey.get())) {
+                // We should never be able to walk forward all the way
+                // to the previous key.
+                ASSERT(!lastDuplicateKey.get());
+                continue;
+            }
+
+            if (!forward) {
+                if (!lastDuplicateKey.get()) {
+                    lastDuplicateKey = m_currentKey;
+                    continue;
+                }
+
+                // We need to walk forward because we hit the boundary
+                // between key ranges.
+                if (!lastDuplicateKey->isEqual(m_currentKey.get())) {
+                    forward = true;
+                    continue;
+                }
+
+                continue;
+            }
+        }
         break;
     }
 
+    ASSERT(!lastDuplicateKey.get() || (forward && lastDuplicateKey->isEqual(m_currentKey.get())));
     return true;
 }
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to