Title: [92364] trunk
Revision
92364
Author
[email protected]
Date
2011-08-04 02:32:25 -0700 (Thu, 04 Aug 2011)

Log Message

IndexedDB: Fix index data invalidation bugs.
https://bugs.webkit.org/show_bug.cgi?id=65547

Reviewed by Tony Chang.

Source/WebCore:

The function that checks whether a key exists in an index failed
to check whether that key was still valid or not.

Deleting a record from an object store must also delete its exists
entry, thus invalidating index keys pointing to that record.

Test: storage/indexeddb/index-unique.html

* storage/IDBLevelDBBackingStore.cpp:
(WebCore::IDBLevelDBBackingStore::deleteObjectStoreRecord):
(WebCore::findKeyInIndex):
(WebCore::IDBLevelDBBackingStore::getPrimaryKeyViaIndex):
(WebCore::IDBLevelDBBackingStore::keyExistsInIndex):
(WebCore::CursorOptions::IndexKeyCursorImpl::loadCurrentRow):

LayoutTests:

Add layout test to verify that update object store data causes old
index data to be invalidated.

* storage/indexeddb/index-unique.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (92363 => 92364)


--- trunk/LayoutTests/ChangeLog	2011-08-04 09:10:31 UTC (rev 92363)
+++ trunk/LayoutTests/ChangeLog	2011-08-04 09:32:25 UTC (rev 92364)
@@ -1,3 +1,15 @@
+2011-08-02  Hans Wennborg  <[email protected]>
+
+        IndexedDB: Fix index data invalidation bugs.
+        https://bugs.webkit.org/show_bug.cgi?id=65547
+
+        Reviewed by Tony Chang.
+
+        Add layout test to verify that update object store data causes old
+        index data to be invalidated.
+
+        * storage/indexeddb/index-unique.html: Added.
+
 2011-08-04  Shinichiro Hamaji  <[email protected]>
 
         Layout Test fast/box-shadow/box-shadow-clipped-slices.html is failing

Added: trunk/LayoutTests/storage/indexeddb/index-unique-expected.txt (0 => 92364)


--- trunk/LayoutTests/storage/indexeddb/index-unique-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/storage/indexeddb/index-unique-expected.txt	2011-08-04 09:32:25 UTC (rev 92364)
@@ -0,0 +1,40 @@
+Test features of IndexedDB's unique indices.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+webkitIndexedDB.open('index-unique')
+db = event.target.result
+db.setVersion('new version')
+deleteExisting():
+trans = event.target.result
+PASS trans !== null is true
+Deleted all object stores.
+db.createObjectStore('store')
+store.createIndex('index', 'x', {unique: true})
+setVersionCompleted():
+transaction = db.transaction([], webkitIDBTransaction.READ_WRITE)
+transaction.objectStore('store').put({x: 1}, 'foo')
+addMoreData():
+transaction.objectStore('store').put({x: 1}, 'bar')
+addMoreDataFailed():
+event.preventDefault()
+PASS event.target.errorCode is webkitIDBDatabaseException.CONSTRAINT_ERR
+transaction.objectStore('store').put({x: 0}, 'foo')
+changeDataSuccess():
+transaction.objectStore('store').index('index').openCursor(webkitIDBKeyRange.lowerBound(1))
+cursorSuccess():
+PASS event.target.result is null
+transaction.objectStore('store').index('index').openKeyCursor(webkitIDBKeyRange.lowerBound(1))
+keyCursorSuccess():
+PASS event.target.result is null
+transaction.objectStore('store').put({x: 1}, 'bar')
+addMoreDataSucces():
+transaction.objectStore('store').delete('bar')
+deleteSuccess():
+transaction.objectStore('store').put({x: 1}, 'baz')
+finalAddSuccess():
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Property changes on: trunk/LayoutTests/storage/indexeddb/index-unique-expected.txt
___________________________________________________________________

Added: svn:eol-style

Added: trunk/LayoutTests/storage/indexeddb/index-unique.html (0 => 92364)


--- trunk/LayoutTests/storage/indexeddb/index-unique.html	                        (rev 0)
+++ trunk/LayoutTests/storage/indexeddb/index-unique.html	2011-08-04 09:32:25 UTC (rev 92364)
@@ -0,0 +1,147 @@
+<html>
+<head>
+<link rel="stylesheet" href=""
+<script src=""
+<script src=""
+<script src=""
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+
+description("Test features of IndexedDB's unique indices.");
+if (window.layoutTestController)
+    layoutTestController.waitUntilDone();
+
+function test()
+{
+    request = evalAndLog("webkitIndexedDB.open('index-unique')");
+    request._onsuccess_ = setVersion;
+    request._onerror_ = unexpectedErrorCallback;
+}
+
+function setVersion()
+{
+    db = evalAndLog("db = event.target.result");
+
+    request = evalAndLog("db.setVersion('new version')");
+    request._onsuccess_ = deleteExisting;
+    request._onerror_ = unexpectedErrorCallback;
+}
+
+function deleteExisting()
+{
+    debug("deleteExisting():");
+    var trans = evalAndLog("trans = event.target.result");
+    shouldBeTrue("trans !== null");
+    trans._onabort_ = unexpectedAbortCallback;
+    trans._oncomplete_ = setVersionCompleted;
+
+    deleteAllObjectStores(db);
+
+    window.store = evalAndLog("db.createObjectStore('store')");
+    window.indexObject = evalAndLog("store.createIndex('index', 'x', {unique: true})");
+
+    // Let setVersion transaction complete.
+}
+
+function setVersionCompleted()
+{
+    debug("setVersionCompleted():");
+    window.transaction = evalAndLog("transaction = db.transaction([], webkitIDBTransaction.READ_WRITE)");
+
+    request = evalAndLog("transaction.objectStore('store').put({x: 1}, 'foo')");
+    request._onerror_ = unexpectedErrorCallback;
+    request._onsuccess_ = addMoreData;
+}
+
+function addMoreData()
+{
+    debug("addMoreData():");
+
+    // The x value violates the uniqueness constraint of the index.
+    request = evalAndLog("transaction.objectStore('store').put({x: 1}, 'bar')");
+    request._onerror_ = addMoreDataFailed;
+    request._onsuccess_ = unexpectedSuccessCallback;
+}
+
+function addMoreDataFailed()
+{
+    debug("addMoreDataFailed():");
+
+    // Don't abort the transaction.
+    evalAndLog("event.preventDefault()");
+
+    shouldBe("event.target.errorCode", "webkitIDBDatabaseException.CONSTRAINT_ERR");
+
+    // Update the 'foo' entry in object store, changing the value of x.
+    request = evalAndLog("transaction.objectStore('store').put({x: 0}, 'foo')");
+    request._onerror_ = unexpectedErrorCallback;
+    request._onsuccess_ = changeDataSuccess;
+}
+
+function changeDataSuccess()
+{
+    debug("changeDataSuccess():");
+
+    // An index cursor starting at 1 should not find anything.
+    var request = evalAndLog("transaction.objectStore('store').index('index').openCursor(webkitIDBKeyRange.lowerBound(1))");
+    request._onsuccess_ = cursorSuccess;
+    request._onerror_ = unexpectedErrorCallback;
+}
+
+function cursorSuccess()
+{
+    debug("cursorSuccess():");
+    shouldBe("event.target.result", "null");
+
+    // A key cursor starting at 1 should not find anything.
+    var request = evalAndLog("transaction.objectStore('store').index('index').openKeyCursor(webkitIDBKeyRange.lowerBound(1))");
+    request._onsuccess_ = keyCursorSuccess;
+    request._onerror_ = unexpectedErrorCallback;
+}
+
+function keyCursorSuccess()
+{
+    debug("keyCursorSuccess():");
+    shouldBe("event.target.result", "null");
+
+    // Now we should be able to add a value with x: 1.
+    request = evalAndLog("transaction.objectStore('store').put({x: 1}, 'bar')");
+    request._onerror_ = unexpectedErrorCallback;
+    request._onsuccess_ = addMoreDataSucces;
+}
+
+function addMoreDataSucces()
+{
+    debug("addMoreDataSucces():");
+
+    request = evalAndLog("transaction.objectStore('store').delete('bar')");
+    request._onerror_ = unexpectedErrorCallback;
+    request._onsuccess_ = deleteSuccess;
+}
+
+function deleteSuccess()
+{
+    debug("deleteSuccess():");
+
+    // Now we should be able to add a value with x: 1 again.
+    request = evalAndLog("transaction.objectStore('store').put({x: 1}, 'baz')");
+    request._onerror_ = unexpectedErrorCallback;
+    request._onsuccess_ = finalAddSuccess;
+}
+
+function finalAddSuccess() {
+    debug("finalAddSuccess():");
+    done();
+}
+
+test();
+
+var successfullyParsed = true;
+
+</script>
+</body>
+</html>
+
Property changes on: trunk/LayoutTests/storage/indexeddb/index-unique.html
___________________________________________________________________

Added: svn:eol-style

Modified: trunk/LayoutTests/storage/indexeddb/objectstore-basics-expected.txt (92363 => 92364)


--- trunk/LayoutTests/storage/indexeddb/objectstore-basics-expected.txt	2011-08-04 09:10:31 UTC (rev 92363)
+++ trunk/LayoutTests/storage/indexeddb/objectstore-basics-expected.txt	2011-08-04 09:32:25 UTC (rev 92364)
@@ -63,7 +63,7 @@
 PASS event.target.result is "key"
 event.target.source.add({x: 'foo'}, 'zzz')
 addAgainFailure():
-PASS event.target.errorCode is webkitIDBDatabaseException.UNKNOWN_ERR
+PASS event.target.errorCode is webkitIDBDatabaseException.CONSTRAINT_ERR
 event.preventDefault()
 db.transaction([], webkitIDBTransaction.READ_WRITE)
 store = transaction.objectStore('storeName')

Modified: trunk/LayoutTests/storage/indexeddb/objectstore-basics.html (92363 => 92364)


--- trunk/LayoutTests/storage/indexeddb/objectstore-basics.html	2011-08-04 09:10:31 UTC (rev 92363)
+++ trunk/LayoutTests/storage/indexeddb/objectstore-basics.html	2011-08-04 09:32:25 UTC (rev 92364)
@@ -176,8 +176,7 @@
 function addAgainFailure()
 {
     debug("addAgainFailure():");
-    // FIXME: This error code needs to be specced.
-    shouldBe("event.target.errorCode", "webkitIDBDatabaseException.UNKNOWN_ERR");
+    shouldBe("event.target.errorCode", "webkitIDBDatabaseException.CONSTRAINT_ERR");
 
     evalAndLog("event.preventDefault()");
 

Modified: trunk/Source/WebCore/ChangeLog (92363 => 92364)


--- trunk/Source/WebCore/ChangeLog	2011-08-04 09:10:31 UTC (rev 92363)
+++ trunk/Source/WebCore/ChangeLog	2011-08-04 09:32:25 UTC (rev 92364)
@@ -1,3 +1,25 @@
+2011-08-02  Hans Wennborg  <[email protected]>
+
+        IndexedDB: Fix index data invalidation bugs.
+        https://bugs.webkit.org/show_bug.cgi?id=65547
+
+        Reviewed by Tony Chang.
+
+        The function that checks whether a key exists in an index failed
+        to check whether that key was still valid or not.
+
+        Deleting a record from an object store must also delete its exists
+        entry, thus invalidating index keys pointing to that record.
+
+        Test: storage/indexeddb/index-unique.html
+
+        * storage/IDBLevelDBBackingStore.cpp:
+        (WebCore::IDBLevelDBBackingStore::deleteObjectStoreRecord):
+        (WebCore::findKeyInIndex):
+        (WebCore::IDBLevelDBBackingStore::getPrimaryKeyViaIndex):
+        (WebCore::IDBLevelDBBackingStore::keyExistsInIndex):
+        (WebCore::CursorOptions::IndexKeyCursorImpl::loadCurrentRow):
+
 2011-08-04  Alexandru Chiculita  <[email protected]>
 
         Move PODIntervalTree to Source/WebCore/platform

Modified: trunk/Source/WebCore/storage/IDBLevelDBBackingStore.cpp (92363 => 92364)


--- trunk/Source/WebCore/storage/IDBLevelDBBackingStore.cpp	2011-08-04 09:10:31 UTC (rev 92363)
+++ trunk/Source/WebCore/storage/IDBLevelDBBackingStore.cpp	2011-08-04 09:32:25 UTC (rev 92364)
@@ -512,8 +512,12 @@
 {
     ASSERT(m_currentTransaction);
     const LevelDBRecordIdentifier* levelDBRecordIdentifier = static_cast<const LevelDBRecordIdentifier*>(recordIdentifier);
-    const Vector<char> key = ObjectStoreDataKey::encode(databaseId, objectStoreId, levelDBRecordIdentifier->primaryKey());
-    m_currentTransaction->remove(key);
+
+    const Vector<char> objectStoreDataKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, levelDBRecordIdentifier->primaryKey());
+    m_currentTransaction->remove(objectStoreDataKey);
+
+    const Vector<char> existsEntryKey = ExistsEntryKey::encode(databaseId, objectStoreId, levelDBRecordIdentifier->primaryKey());
+    m_currentTransaction->remove(existsEntryKey);
 }
 
 double IDBLevelDBBackingStore::nextAutoIncrementNumber(int64_t databaseId, int64_t objectStoreId)
@@ -801,52 +805,60 @@
     return decodeInt(data.begin(), data.end()) == version;
 }
 
-PassRefPtr<IDBKey> IDBLevelDBBackingStore::getPrimaryKeyViaIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key)
+static bool findKeyInIndex(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key, Vector<char>& foundEncodedPrimaryKey)
 {
-    ASSERT(m_currentTransaction);
+    ASSERT(foundEncodedPrimaryKey.isEmpty());
+
     const Vector<char> leveldbKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, key, 0);
-    OwnPtr<LevelDBIterator> it = m_currentTransaction->createIterator();
+    OwnPtr<LevelDBIterator> it = transaction->createIterator();
     it->seek(leveldbKey);
 
     for (;;) {
         if (!it->isValid())
-            return 0;
+            return false;
         if (compareIndexKeys(it->key(), leveldbKey) > 0)
-            return 0;
+            return false;
 
         int64_t version;
         const char* p = decodeVarInt(it->value().begin(), it->value().end(), version);
         if (!p)
-            return 0;
-        Vector<char> encodedPrimaryKey;
-        encodedPrimaryKey.append(p, it->value().end() - p);
+            return false;
+        foundEncodedPrimaryKey.append(p, it->value().end() - p);
 
-        if (!versionExists(m_currentTransaction.get(), databaseId, objectStoreId, version, encodedPrimaryKey)) {
+        if (!versionExists(transaction, databaseId, objectStoreId, version, foundEncodedPrimaryKey)) {
             // Delete stale index data entry and continue.
-            m_currentTransaction->remove(it->key());
+            transaction->remove(it->key());
             it->next();
             continue;
         }
 
+        return true;
+    }
+}
+
+PassRefPtr<IDBKey> IDBLevelDBBackingStore::getPrimaryKeyViaIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key)
+{
+    ASSERT(m_currentTransaction);
+
+    Vector<char> foundEncodedPrimaryKey;
+    if (findKeyInIndex(m_currentTransaction.get(), databaseId, objectStoreId, indexId, key, foundEncodedPrimaryKey)) {
         RefPtr<IDBKey> primaryKey;
-        decodeIDBKey(encodedPrimaryKey.begin(), encodedPrimaryKey.end(), primaryKey);
+        decodeIDBKey(foundEncodedPrimaryKey.begin(), foundEncodedPrimaryKey.end(), primaryKey);
         return primaryKey.release();
     }
+
+    return 0;
 }
 
 bool IDBLevelDBBackingStore::keyExistsInIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key)
 {
     ASSERT(m_currentTransaction);
-    const Vector<char> levelDBKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, key, 0);
-    OwnPtr<LevelDBIterator> it = m_currentTransaction->createIterator();
 
-    bool found = false;
+    Vector<char> foundEncodedPrimaryKey;
+    if (findKeyInIndex(m_currentTransaction.get(), databaseId, objectStoreId, indexId, key, foundEncodedPrimaryKey))
+        return true;
 
-    it->seek(levelDBKey);
-    if (it->isValid() && !compareIndexKeys(it->key(), levelDBKey))
-        found = true;
-
-    return found;
+    return false;
 }
 
 namespace {
@@ -1088,7 +1100,7 @@
     if (!t)
         return false;
 
-    if (objectStoreDataVersion != indexDataVersion) { // FIXME: This is probably not very well covered by the layout tests.
+    if (objectStoreDataVersion != indexDataVersion) {
         m_transaction->remove(m_iterator->key());
         return false;
     }

Modified: trunk/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp (92363 => 92364)


--- trunk/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp	2011-08-04 09:10:31 UTC (rev 92363)
+++ trunk/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp	2011-08-04 09:32:25 UTC (rev 92364)
@@ -236,7 +236,7 @@
             return;
         }
         if (!it->second->addingKeyAllowed(key.get())) {
-            callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "One of the derived (from a keyPath) keys for an index does not satisfy its uniqueness requirements."));
+            callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::CONSTRAINT_ERR, "One of the derived (from a keyPath) keys for an index does not satisfy its uniqueness requirements."));
             return;
         }
         indexKeys.append(key.release());
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to