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());