From 692b293ecf385849c119b33e6edb3d5d3c8062fe Mon Sep 17 00:00:00 2001 From: Jonas Sicking Date: Fri, 2 Dec 2011 18:32:46 -0800 Subject: [PATCH] Bug 692630: Support multi-entry indexes. r=bent --- dom/base/nsDOMClassInfo.cpp | 3 + dom/base/nsDOMClassInfo.h | 1 + dom/indexedDB/DatabaseInfo.cpp | 6 +- dom/indexedDB/DatabaseInfo.h | 4 +- dom/indexedDB/IDBFactory.cpp | 5 +- dom/indexedDB/IDBIndex.cpp | 10 + dom/indexedDB/IDBIndex.h | 6 + dom/indexedDB/IDBObjectStore.cpp | 284 +++++++++++++----------- dom/indexedDB/IDBObjectStore.h | 18 +- dom/indexedDB/IDBTransaction.cpp | 4 +- dom/indexedDB/IndexedDatabase.h | 2 +- dom/indexedDB/OpenDatabaseHelper.cpp | 81 ++++++- dom/indexedDB/nsIIDBIndex.idl | 4 +- dom/indexedDB/test/Makefile.in | 1 + dom/indexedDB/test/test_multientry.html | 230 +++++++++++++++++++ 15 files changed, 512 insertions(+), 147 deletions(-) create mode 100644 dom/indexedDB/test/test_multientry.html diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index eb847987778..2f3fc012221 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -1641,6 +1641,7 @@ jsid nsDOMClassInfo::sURL_id = JSID_VOID; jsid nsDOMClassInfo::sKeyPath_id = JSID_VOID; jsid nsDOMClassInfo::sAutoIncrement_id = JSID_VOID; jsid nsDOMClassInfo::sUnique_id = JSID_VOID; +jsid nsDOMClassInfo::sMultiEntry_id = JSID_VOID; jsid nsDOMClassInfo::sOnload_id = JSID_VOID; jsid nsDOMClassInfo::sOnerror_id = JSID_VOID; @@ -1904,6 +1905,7 @@ nsDOMClassInfo::DefineStaticJSVals(JSContext *cx) SET_JSID_TO_STRING(sKeyPath_id, cx, "keyPath"); SET_JSID_TO_STRING(sAutoIncrement_id, cx, "autoIncrement"); SET_JSID_TO_STRING(sUnique_id, cx, "unique"); + SET_JSID_TO_STRING(sMultiEntry_id, cx, "multiEntry"); SET_JSID_TO_STRING(sOnload_id, cx, "onload"); SET_JSID_TO_STRING(sOnerror_id, cx, "onerror"); @@ -4902,6 +4904,7 @@ nsDOMClassInfo::ShutDown() sKeyPath_id = JSID_VOID; sAutoIncrement_id = JSID_VOID; sUnique_id = JSID_VOID; + sMultiEntry_id = JSID_VOID; sOnload_id = JSID_VOID; sOnerror_id = JSID_VOID; diff --git a/dom/base/nsDOMClassInfo.h b/dom/base/nsDOMClassInfo.h index 43ecee501d4..517636a53cb 100644 --- a/dom/base/nsDOMClassInfo.h +++ b/dom/base/nsDOMClassInfo.h @@ -295,6 +295,7 @@ public: static jsid sKeyPath_id; static jsid sAutoIncrement_id; static jsid sUnique_id; + static jsid sMultiEntry_id; static jsid sOnload_id; static jsid sOnerror_id; diff --git a/dom/indexedDB/DatabaseInfo.cpp b/dom/indexedDB/DatabaseInfo.cpp index bd6a1daf287..91958ff6ccd 100644 --- a/dom/indexedDB/DatabaseInfo.cpp +++ b/dom/indexedDB/DatabaseInfo.cpp @@ -97,7 +97,8 @@ DatabaseInfo::~DatabaseInfo() IndexInfo::IndexInfo() : id(LL_MININT), unique(false), - autoIncrement(false) + autoIncrement(false), + multiEntry(false) { MOZ_COUNT_CTOR(IndexInfo); } @@ -107,7 +108,8 @@ IndexInfo::IndexInfo(const IndexInfo& aOther) name(aOther.name), keyPath(aOther.keyPath), unique(aOther.unique), - autoIncrement(aOther.autoIncrement) + autoIncrement(aOther.autoIncrement), + multiEntry(aOther.multiEntry) { MOZ_COUNT_CTOR(IndexInfo); } diff --git a/dom/indexedDB/DatabaseInfo.h b/dom/indexedDB/DatabaseInfo.h index e80d2795e74..bb9cd9449b6 100644 --- a/dom/indexedDB/DatabaseInfo.h +++ b/dom/indexedDB/DatabaseInfo.h @@ -121,6 +121,7 @@ struct IndexInfo nsString keyPath; bool unique; bool autoIncrement; + bool multiEntry; }; struct ObjectStoreInfo @@ -149,7 +150,8 @@ struct IndexUpdateInfo ~IndexUpdateInfo(); #endif - IndexInfo info; + PRInt64 indexId; + bool indexUnique; Key value; }; diff --git a/dom/indexedDB/IDBFactory.cpp b/dom/indexedDB/IDBFactory.cpp index cfa57da93fd..1cef23dade7 100644 --- a/dom/indexedDB/IDBFactory.cpp +++ b/dom/indexedDB/IDBFactory.cpp @@ -273,7 +273,7 @@ IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection, // Load index information rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( - "SELECT object_store_id, id, name, key_path, unique_index, " + "SELECT object_store_id, id, name, key_path, unique_index, multientry, " "object_store_autoincrement " "FROM object_store_index" ), getter_AddRefs(stmt)); @@ -307,7 +307,8 @@ IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection, NS_ENSURE_SUCCESS(rv, rv); indexInfo->unique = !!stmt->AsInt32(4); - indexInfo->autoIncrement = !!stmt->AsInt32(5); + indexInfo->multiEntry = !!stmt->AsInt32(5); + indexInfo->autoIncrement = !!stmt->AsInt32(6); } NS_ENSURE_SUCCESS(rv, rv); diff --git a/dom/indexedDB/IDBIndex.cpp b/dom/indexedDB/IDBIndex.cpp index a7d6580f9e3..93a78fce98f 100644 --- a/dom/indexedDB/IDBIndex.cpp +++ b/dom/indexedDB/IDBIndex.cpp @@ -318,6 +318,7 @@ IDBIndex::Create(IDBObjectStore* aObjectStore, index->mName = aIndexInfo->name; index->mKeyPath = aIndexInfo->keyPath; index->mUnique = aIndexInfo->unique; + index->mMultiEntry = aIndexInfo->multiEntry; index->mAutoIncrement = aIndexInfo->autoIncrement; return index.forget(); @@ -396,6 +397,15 @@ IDBIndex::GetUnique(bool* aUnique) return NS_OK; } +NS_IMETHODIMP +IDBIndex::GetMultiEntry(bool* aMultiEntry) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + *aMultiEntry = mMultiEntry; + return NS_OK; +} + NS_IMETHODIMP IDBIndex::GetObjectStore(nsIIDBObjectStore** aObjectStore) { diff --git a/dom/indexedDB/IDBIndex.h b/dom/indexedDB/IDBIndex.h index b62c1cf8b36..643ae942de0 100644 --- a/dom/indexedDB/IDBIndex.h +++ b/dom/indexedDB/IDBIndex.h @@ -87,6 +87,11 @@ public: return mUnique; } + bool IsMultiEntry() const + { + return mMultiEntry; + } + bool IsAutoIncrement() const { return mAutoIncrement; @@ -110,6 +115,7 @@ private: nsString mName; nsString mKeyPath; bool mUnique; + bool mMultiEntry; bool mAutoIncrement; }; diff --git a/dom/indexedDB/IDBObjectStore.cpp b/dom/indexedDB/IDBObjectStore.cpp index 813e12673e7..fd2ecacc364 100644 --- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -97,9 +97,6 @@ public: AsyncConnectionHelper::ReleaseMainThreadObjects(); } - nsresult UpdateIndexes(mozIStorageConnection* aConnection, - PRInt64 aObjectDataId); - private: // In-params. nsRefPtr mObjectStore; @@ -422,10 +419,10 @@ typedef nsCharSeparatedTokenizerTemplate KeyPathTokenizer; inline nsresult -GetKeyFromValue(JSContext* aCx, - jsval aVal, - const nsAString& aKeyPath, - Key& aKey) +GetJSValFromKeyPath(JSContext* aCx, + jsval aVal, + const nsAString& aKeyPath, + jsval& aKey) { NS_ASSERTION(aCx, "Null pointer!"); // aVal can be primitive iff the key path is empty. @@ -452,8 +449,23 @@ GetKeyFromValue(JSContext* aCx, keyPathChars, keyPathLen, &intermediate); NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); } + + aKey = intermediate; + return NS_OK; +} - if (NS_FAILED(aKey.SetFromJSVal(aCx, intermediate))) { +inline +nsresult +GetKeyFromValue(JSContext* aCx, + jsval aVal, + const nsAString& aKeyPath, + Key& aKey) +{ + jsval key; + nsresult rv = GetJSValFromKeyPath(aCx, aVal, aKeyPath, key); + NS_ENSURE_SUCCESS(rv, rv); + + if (NS_FAILED(aKey.SetFromJSVal(aCx, key))) { aKey.Unset(); } @@ -571,78 +583,63 @@ IDBObjectStore::IsValidKeyPath(JSContext* aCx, // static nsresult -IDBObjectStore::GetKeyPathValueFromStructuredData(const PRUint8* aData, - PRUint32 aDataLength, - const nsAString& aKeyPath, - JSContext* aCx, - Key& aValue) +IDBObjectStore::AppendIndexUpdateInfo(PRInt64 aIndexID, + const nsAString& aKeyPath, + bool aUnique, + bool aMultiEntry, + JSContext* aCx, + jsval aObject, + nsTArray& aUpdateInfoArray) { - NS_ASSERTION(aData, "Null pointer!"); - NS_ASSERTION(aDataLength, "Empty data!"); - NS_ASSERTION(aCx, "Null pointer!"); - - JSAutoRequest ar(aCx); - - jsval clone; - if (!JS_ReadStructuredClone(aCx, reinterpret_cast(aData), - aDataLength, JS_STRUCTURED_CLONE_VERSION, - &clone, NULL, NULL)) { - return NS_ERROR_DOM_DATA_CLONE_ERR; - } - - if (JSVAL_IS_PRIMITIVE(clone) && !aKeyPath.IsEmpty()) { - // This isn't an object, so just leave the key unset. - aValue.Unset(); - return NS_OK; - } - - nsresult rv = GetKeyFromValue(aCx, clone, aKeyPath, aValue); + jsval key; + nsresult rv = GetJSValFromKeyPath(aCx, aObject, aKeyPath, key); NS_ENSURE_SUCCESS(rv, rv); - return NS_OK; -} - -/* static */ -nsresult -IDBObjectStore::GetIndexUpdateInfo(ObjectStoreInfo* aObjectStoreInfo, - JSContext* aCx, - jsval aObject, - nsTArray& aUpdateInfoArray) -{ - JSObject* cloneObj = nsnull; - - PRUint32 count = aObjectStoreInfo->indexes.Length(); - if (count) { - if (!aUpdateInfoArray.SetCapacity(count)) { - NS_ERROR("Out of memory!"); - return NS_ERROR_OUT_OF_MEMORY; + if (aMultiEntry && !JSVAL_IS_PRIMITIVE(key) && + JS_IsArrayObject(aCx, JSVAL_TO_OBJECT(key))) { + JSObject* array = JSVAL_TO_OBJECT(key); + jsuint arrayLength; + if (!JS_GetArrayLength(aCx, array, &arrayLength)) { + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } - for (PRUint32 indexesIndex = 0; indexesIndex < count; indexesIndex++) { - const IndexInfo& indexInfo = aObjectStoreInfo->indexes[indexesIndex]; + for (jsuint arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) { + jsval arrayItem; + if (!JS_GetElement(aCx, array, arrayIndex, &arrayItem)) { + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } Key value; - nsresult rv = GetKeyFromValue(aCx, aObject, indexInfo.keyPath, value); - NS_ENSURE_SUCCESS(rv, rv); - - if (value.IsUnset()) { + if (NS_FAILED(value.SetFromJSVal(aCx, arrayItem)) || + value.IsUnset()) { // Not a value we can do anything with, ignore it. continue; } IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement(); - updateInfo->info = indexInfo; + updateInfo->indexId = aIndexID; + updateInfo->indexUnique = aUnique; updateInfo->value = value; } } else { - aUpdateInfoArray.Clear(); + Key value; + if (NS_FAILED(value.SetFromJSVal(aCx, key)) || + value.IsUnset()) { + // Not a value we can do anything with, ignore it. + return NS_OK; + } + + IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement(); + updateInfo->indexId = aIndexID; + updateInfo->indexUnique = aUnique; + updateInfo->value = value; } return NS_OK; } -/* static */ +// static nsresult IDBObjectStore::UpdateIndexes(IDBTransaction* aTransaction, PRInt64 aObjectStoreId, @@ -652,21 +649,13 @@ IDBObjectStore::UpdateIndexes(IDBTransaction* aTransaction, PRInt64 aObjectDataId, const nsTArray& aUpdateInfoArray) { -#ifdef DEBUG - if (aAutoIncrement) { - NS_ASSERTION(aObjectDataId != LL_MININT, "Bad objectData id!"); - } - else { - NS_ASSERTION(aObjectDataId == LL_MININT, "Bad objectData id!"); - } -#endif - - PRUint32 indexCount = aUpdateInfoArray.Length(); + NS_ASSERTION(!aAutoIncrement || aObjectDataId != LL_MININT, + "Bad objectData id!"); nsCOMPtr stmt; nsresult rv; - if (!aAutoIncrement) { + if (aObjectDataId == LL_MININT) { stmt = aTransaction->GetCachedStatement( "SELECT id " "FROM object_data " @@ -728,25 +717,24 @@ IDBObjectStore::UpdateIndexes(IDBTransaction* aTransaction, NS_ENSURE_SUCCESS(rv, rv); } - for (PRUint32 indexIndex = 0; indexIndex < indexCount; indexIndex++) { - const IndexUpdateInfo& updateInfo = aUpdateInfoArray[indexIndex]; - - NS_ASSERTION(updateInfo.info.autoIncrement == aAutoIncrement, "Huh?!"); + PRUint32 infoCount = aUpdateInfoArray.Length(); + for (PRUint32 i = 0; i < infoCount; i++) { + const IndexUpdateInfo& updateInfo = aUpdateInfoArray[i]; // Insert new values. stmt = aTransaction->IndexDataInsertStatement(aAutoIncrement, - updateInfo.info.unique); + updateInfo.indexUnique); NS_ENSURE_TRUE(stmt, NS_ERROR_FAILURE); mozStorageStatementScoper scoper4(stmt); - rv = stmt->BindInt64ByName(indexId, updateInfo.info.id); + rv = stmt->BindInt64ByName(indexId, updateInfo.indexId); NS_ENSURE_SUCCESS(rv, rv); rv = stmt->BindInt64ByName(objectDataId, aObjectDataId); NS_ENSURE_SUCCESS(rv, rv); - if (!updateInfo.info.autoIncrement) { + if (!aAutoIncrement) { rv = aObjectStoreKey.BindToStatement(stmt, objectDataKey); NS_ENSURE_SUCCESS(rv, rv); } @@ -755,6 +743,23 @@ IDBObjectStore::UpdateIndexes(IDBTransaction* aTransaction, NS_ENSURE_SUCCESS(rv, rv); rv = stmt->Execute(); + if (rv == NS_ERROR_STORAGE_CONSTRAINT && updateInfo.indexUnique) { + // If we're inserting multiple entries for the same unique index, then + // we might have failed to insert due to colliding with another entry for + // the same index in which case we should ignore it. + + for (PRInt32 j = (PRInt32)i - 1; + j >= 0 && aUpdateInfoArray[j].indexId == updateInfo.indexId; + --j) { + if (updateInfo.value == aUpdateInfoArray[j].value) { + // We found a key with the same value for the same index. So we + // must have had a collision with a value we just inserted. + rv = NS_OK; + break; + } + } + } + if (NS_FAILED(rv)) { return rv; } @@ -929,8 +934,17 @@ IDBObjectStore::GetAddInfo(JSContext* aCx, NS_ERROR("This should never fail!"); } - rv = GetIndexUpdateInfo(info, aCx, aValue, aUpdateInfoArray); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + PRUint32 count = info->indexes.Length(); + aUpdateInfoArray.SetCapacity(count); // Pretty good estimate + for (PRUint32 indexesIndex = 0; indexesIndex < count; indexesIndex++) { + const IndexInfo& indexInfo = info->indexes[indexesIndex]; + + rv = AppendIndexUpdateInfo(indexInfo.id, indexInfo.keyPath, + indexInfo.unique, indexInfo.multiEntry, + aCx, aValue, aUpdateInfoArray); + NS_ENSURE_SUCCESS(rv, rv); + + } const jschar* keyPathChars = reinterpret_cast(mKeyPath.get()); @@ -1365,6 +1379,7 @@ IDBObjectStore::CreateIndex(const nsAString& aName, NS_ASSERTION(mTransaction->IsOpen(), "Impossible!"); bool unique = false; + bool multiEntry = false; // Get optional arguments. if (!JSVAL_IS_VOID(aOptions) && !JSVAL_IS_NULL(aOptions)) { @@ -1388,6 +1403,17 @@ IDBObjectStore::CreateIndex(const nsAString& aName, return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } unique = !!boolVal; + + if (!JS_GetPropertyById(aCx, options, nsDOMClassInfo::sMultiEntry_id, &val)) { + NS_WARNING("JS_GetPropertyById failed!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + if (!JS_ValueToBoolean(aCx, val, &boolVal)) { + NS_WARNING("JS_ValueToBoolean failed!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + multiEntry = !!boolVal; } DatabaseInfo* databaseInfo = mTransaction->Database()->Info(); @@ -1402,6 +1428,7 @@ IDBObjectStore::CreateIndex(const nsAString& aName, indexInfo->name = aName; indexInfo->keyPath = aKeyPath; indexInfo->unique = unique; + indexInfo->multiEntry = multiEntry; indexInfo->autoIncrement = mAutoIncrement; // Don't leave this in the list if we fail below! @@ -2153,8 +2180,9 @@ CreateIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection) nsCOMPtr stmt = mTransaction->GetCachedStatement( "INSERT INTO object_store_index (id, name, key_path, unique_index, " - "object_store_id, object_store_autoincrement) " - "VALUES (:id, :name, :key_path, :unique, :osid, :os_auto_increment)" + "multientry, object_store_id, object_store_autoincrement) " + "VALUES (:id, :name, :key_path, :unique, :multientry, :osid, " + ":os_auto_increment)" ); NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); @@ -2175,6 +2203,10 @@ CreateIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection) mIndex->IsUnique() ? 1 : 0); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("multientry"), + mIndex->IsMultiEntry() ? 1 : 0); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mIndex->ObjectStore()->Id()); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); @@ -2231,69 +2263,69 @@ CreateIndexHelper::InsertDataFromObjectStore(mozIStorageConnection* aConnection) mIndex->ObjectStore()->Id()); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + NS_ENSURE_TRUE(sTLSIndex != BAD_TLS_INDEX, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + bool hasResult; - while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { - nsCOMPtr insertStmt = - mTransaction->IndexDataInsertStatement(mIndex->IsAutoIncrement(), - mIndex->IsUnique()); - NS_ENSURE_TRUE(insertStmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + rv = stmt->ExecuteStep(&hasResult); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + if (!hasResult) { + // Bail early if we have no data to avoid creating the below runtime + return NS_OK; + } - mozStorageStatementScoper scoper2(insertStmt); + ThreadLocalJSRuntime* tlsEntry = + reinterpret_cast(PR_GetThreadPrivate(sTLSIndex)); - rv = insertStmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), - mIndex->Id()); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + if (!tlsEntry) { + tlsEntry = ThreadLocalJSRuntime::Create(); + NS_ENSURE_TRUE(tlsEntry, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - rv = insertStmt->BindInt64ByName(NS_LITERAL_CSTRING("object_data_id"), - stmt->AsInt64(0)); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + PR_SetThreadPrivate(sTLSIndex, tlsEntry); + } - if (!mIndex->IsAutoIncrement()) { - NS_NAMED_LITERAL_CSTRING(objectDataKey, "object_data_key"); - - Key key; - rv = key.SetFromStatement(stmt, 2); - NS_ENSURE_SUCCESS(rv, rv); - - rv = - key.BindToStatement(insertStmt, NS_LITERAL_CSTRING("object_data_key")); - NS_ENSURE_SUCCESS(rv, rv); - } + JSContext* cx = tlsEntry->Context(); + JSAutoRequest ar(cx); + do { const PRUint8* data; PRUint32 dataLength; rv = stmt->GetSharedBlob(1, &dataLength, &data); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - NS_ENSURE_TRUE(sTLSIndex != BAD_TLS_INDEX, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - ThreadLocalJSRuntime* tlsEntry = - reinterpret_cast(PR_GetThreadPrivate(sTLSIndex)); - - if (!tlsEntry) { - tlsEntry = ThreadLocalJSRuntime::Create(); - NS_ENSURE_TRUE(tlsEntry, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - PR_SetThreadPrivate(sTLSIndex, tlsEntry); + jsval clone; + if (!JS_ReadStructuredClone(cx, reinterpret_cast(data), + dataLength, JS_STRUCTURED_CLONE_VERSION, + &clone, NULL, NULL)) { + return NS_ERROR_DOM_DATA_CLONE_ERR; } + nsTArray updateInfo; + rv = IDBObjectStore::AppendIndexUpdateInfo(mIndex->Id(), + mIndex->KeyPath(), + mIndex->IsUnique(), + mIndex->IsMultiEntry(), + tlsEntry->Context(), + clone, updateInfo); + NS_ENSURE_SUCCESS(rv, rv); + + PRInt64 objectDataID = stmt->AsInt64(0); + Key key; - rv = IDBObjectStore::GetKeyPathValueFromStructuredData(data, dataLength, - mIndex->KeyPath(), - tlsEntry->Context(), - key); - NS_ENSURE_SUCCESS(rv, rv); - - if (key.IsUnset()) { - continue; + if (!mIndex->IsAutoIncrement()) { + rv = key.SetFromStatement(stmt, 2); + NS_ENSURE_SUCCESS(rv, rv); + } + else { + key.SetFromInteger(objectDataID); } - rv = key.BindToStatement(insertStmt, NS_LITERAL_CSTRING("value")); + rv = IDBObjectStore::UpdateIndexes(mTransaction, mIndex->Id(), + key, mIndex->IsAutoIncrement(), + false, objectDataID, updateInfo); NS_ENSURE_SUCCESS(rv, rv); - rv = insertStmt->Execute(); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } + } while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&hasResult)) && hasResult); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); return NS_OK; } diff --git a/dom/indexedDB/IDBObjectStore.h b/dom/indexedDB/IDBObjectStore.h index 2328335b6b2..34554c61b1e 100644 --- a/dom/indexedDB/IDBObjectStore.h +++ b/dom/indexedDB/IDBObjectStore.h @@ -76,17 +76,13 @@ public: IsValidKeyPath(JSContext* aCx, const nsAString& aKeyPath); static nsresult - GetKeyPathValueFromStructuredData(const PRUint8* aData, - PRUint32 aDataLength, - const nsAString& aKeyPath, - JSContext* aCx, - Key& aValue); - - static nsresult - GetIndexUpdateInfo(ObjectStoreInfo* aObjectStoreInfo, - JSContext* aCx, - jsval aObject, - nsTArray& aUpdateInfoArray); + AppendIndexUpdateInfo(PRInt64 aIndexID, + const nsAString& aKeyPath, + bool aUnique, + bool aMultiEntry, + JSContext* aCx, + jsval aObject, + nsTArray& aUpdateInfoArray); static nsresult UpdateIndexes(IDBTransaction* aTransaction, diff --git a/dom/indexedDB/IDBTransaction.cpp b/dom/indexedDB/IDBTransaction.cpp index 6079d62a41f..1506aabd84b 100644 --- a/dom/indexedDB/IDBTransaction.cpp +++ b/dom/indexedDB/IDBTransaction.cpp @@ -406,7 +406,7 @@ IDBTransaction::IndexDataInsertStatement(bool aAutoIncrement, ); } return GetCachedStatement( - "INSERT INTO ai_index_data " + "INSERT OR IGNORE INTO ai_index_data " "(index_id, ai_object_data_id, value) " "VALUES (:index_id, :object_data_id, :value)" ); @@ -419,7 +419,7 @@ IDBTransaction::IndexDataInsertStatement(bool aAutoIncrement, ); } return GetCachedStatement( - "INSERT INTO index_data (" + "INSERT OR IGNORE INTO index_data (" "index_id, object_data_id, object_data_key, value) " "VALUES (:index_id, :object_data_id, :object_data_key, :value)" ); diff --git a/dom/indexedDB/IndexedDatabase.h b/dom/indexedDB/IndexedDatabase.h index 2d8f549c341..7aebcd6b040 100644 --- a/dom/indexedDB/IndexedDatabase.h +++ b/dom/indexedDB/IndexedDatabase.h @@ -51,7 +51,7 @@ #include "nsStringGlue.h" #include "nsTArray.h" -#define DB_SCHEMA_VERSION 7 +#define DB_SCHEMA_VERSION 8 #define BEGIN_INDEXEDDB_NAMESPACE \ namespace mozilla { namespace dom { namespace indexedDB { diff --git a/dom/indexedDB/OpenDatabaseHelper.cpp b/dom/indexedDB/OpenDatabaseHelper.cpp index 4a4953bd2a3..2037d3863ce 100644 --- a/dom/indexedDB/OpenDatabaseHelper.cpp +++ b/dom/indexedDB/OpenDatabaseHelper.cpp @@ -165,6 +165,7 @@ CreateTables(mozIStorageConnection* aDBConn) "name TEXT NOT NULL, " "key_path TEXT NOT NULL, " "unique_index INTEGER NOT NULL, " + "multientry INTEGER NOT NULL DEFAULT 0, " "object_store_autoincrement INTERGER NOT NULL, " "PRIMARY KEY (id), " "UNIQUE (object_store_id, name), " @@ -819,6 +820,83 @@ UpgradeSchemaFrom6To7(mozIStorageConnection* aConnection) return NS_OK; } + +nsresult +UpgradeSchemaFrom7To8(mozIStorageConnection* aConnection) +{ + mozStorageTransaction transaction(aConnection, false, + mozIStorageConnection::TRANSACTION_IMMEDIATE); + + // Turn off foreign key constraints before we do anything here. + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "PRAGMA foreign_keys = OFF;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "id, " + "object_store_id, " + "name, " + "key_path, " + "unique_index, " + "object_store_autoincrement, " + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT id, object_store_id, name, key_path, " + "unique_index, object_store_autoincrement, " + "FROM object_store_index;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE object_store_index;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_store_index (" + "id INTEGER, " + "object_store_id INTEGER NOT NULL, " + "name TEXT NOT NULL, " + "key_path TEXT NOT NULL, " + "unique_index INTEGER NOT NULL, " + "multientry INTEGER NOT NULL, " + "object_store_autoincrement INTERGER NOT NULL, " + "PRIMARY KEY (id), " + "UNIQUE (object_store_id, name), " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO object_store_index " + "SELECT id, object_store_id, name, key_path, " + "unique_index, 0, object_store_autoincrement, " + "FROM temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->SetSchemaVersion(8); + NS_ENSURE_SUCCESS(rv, rv); + + rv = transaction.Commit(); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + nsresult CreateDatabaseConnection(const nsAString& aName, nsIFile* aDBFile, @@ -870,7 +948,7 @@ CreateDatabaseConnection(const nsAString& aName, } else if (schemaVersion != DB_SCHEMA_VERSION) { // This logic needs to change next time we change the schema! - PR_STATIC_ASSERT(DB_SCHEMA_VERSION == 7); + PR_STATIC_ASSERT(DB_SCHEMA_VERSION == 8); #define UPGRADE_SCHEMA_CASE(_from, _to) \ if (schemaVersion == _from) { \ @@ -886,6 +964,7 @@ CreateDatabaseConnection(const nsAString& aName, UPGRADE_SCHEMA_CASE(4, 5) UPGRADE_SCHEMA_CASE(5, 6) UPGRADE_SCHEMA_CASE(6, 7) + UPGRADE_SCHEMA_CASE(7, 8) #undef UPGRADE_SCHEMA_CASE diff --git a/dom/indexedDB/nsIIDBIndex.idl b/dom/indexedDB/nsIIDBIndex.idl index c4002f4db5f..0d544f720d3 100644 --- a/dom/indexedDB/nsIIDBIndex.idl +++ b/dom/indexedDB/nsIIDBIndex.idl @@ -47,7 +47,7 @@ interface nsIIDBRequest; * http://dev.w3.org/2006/webapi/WebSimpleDB/#idl-def-IDBIndex for more * information. */ -[scriptable, builtinclass, uuid(1da60889-3db4-4f66-9fd7-b78c1e7969b7)] +[scriptable, builtinclass, uuid(fcb9a158-833e-4aa9-ab19-ab90cbb50afc)] interface nsIIDBIndex : nsISupports { readonly attribute DOMString name; @@ -58,6 +58,8 @@ interface nsIIDBIndex : nsISupports readonly attribute boolean unique; + readonly attribute boolean multiEntry; + readonly attribute nsIIDBObjectStore objectStore; [implicit_jscontext] diff --git a/dom/indexedDB/test/Makefile.in b/dom/indexedDB/test/Makefile.in index 3e5c0a24495..fb2212c5c95 100644 --- a/dom/indexedDB/test/Makefile.in +++ b/dom/indexedDB/test/Makefile.in @@ -84,6 +84,7 @@ TEST_FILES = \ test_indexes_bad_values.html \ test_key_requirements.html \ test_leaving_page.html \ + test_multientry.html \ test_objectCursors.html \ test_objectStore_inline_autoincrement_key_added_on_put.html \ test_objectStore_remove_values.html \ diff --git a/dom/indexedDB/test/test_multientry.html b/dom/indexedDB/test/test_multientry.html new file mode 100644 index 00000000000..cfe30f366b3 --- /dev/null +++ b/dom/indexedDB/test/test_multientry.html @@ -0,0 +1,230 @@ + + + + Indexed Database Property Test + + + + + + + + + + +