From 488e12717c223909f2fe67da2c7a07a983769432 Mon Sep 17 00:00:00 2001 From: Jonas Sicking Date: Fri, 16 Dec 2011 13:09:42 -0800 Subject: [PATCH] Back out fb4d12d2a2da, bug 701772, due to leaks. --- dom/indexedDB/DatabaseInfo.cpp | 73 +- dom/indexedDB/DatabaseInfo.h | 45 +- dom/indexedDB/IDBDatabase.cpp | 37 +- dom/indexedDB/IDBFactory.cpp | 42 +- dom/indexedDB/IDBFactory.h | 8 +- dom/indexedDB/IDBIndex.cpp | 458 +++++++---- dom/indexedDB/IDBIndex.h | 6 + dom/indexedDB/IDBObjectStore.cpp | 728 +++++++++++++----- dom/indexedDB/IDBObjectStore.h | 11 +- dom/indexedDB/IDBTransaction.cpp | 244 +++--- dom/indexedDB/IDBTransaction.h | 42 +- dom/indexedDB/IndexedDatabaseManager.cpp | 6 +- dom/indexedDB/Key.h | 6 +- dom/indexedDB/OpenDatabaseHelper.cpp | 244 +++--- dom/indexedDB/OpenDatabaseHelper.h | 7 +- dom/indexedDB/nsIIDBObjectStore.idl | 4 +- dom/indexedDB/test/Makefile.in | 1 - ...rror_events_abort_transactions_iframe.html | 6 +- dom/indexedDB/test/helpers.js | 6 - dom/indexedDB/test/test_add_put.html | 18 +- dom/indexedDB/test/test_autoIncrement.html | 384 --------- .../test/test_autoIncrement_indexes.html | 9 +- dom/indexedDB/test/test_setVersion_abort.html | 21 +- 23 files changed, 1233 insertions(+), 1173 deletions(-) delete mode 100644 dom/indexedDB/test/test_autoIncrement.html diff --git a/dom/indexedDB/DatabaseInfo.cpp b/dom/indexedDB/DatabaseInfo.cpp index 1d35d9a8f4b4..91958ff6ccda 100644 --- a/dom/indexedDB/DatabaseInfo.cpp +++ b/dom/indexedDB/DatabaseInfo.cpp @@ -71,13 +71,14 @@ CloneObjectStoreInfo(const nsAString& aKey, { ObjectStoreInfoHash* hash = static_cast(aUserArg); - nsRefPtr newInfo(new ObjectStoreInfo(*aData)); + nsAutoPtr newInfo(new ObjectStoreInfo(*aData)); if (!hash->Put(aKey, newInfo)) { NS_WARNING("Out of memory?"); return PL_DHASH_STOP; } + newInfo.forget(); return PL_DHASH_NEXT; } @@ -91,23 +92,12 @@ DatabaseInfo::~DatabaseInfo() } } -ObjectStoreInfo::ObjectStoreInfo(ObjectStoreInfo& aOther) -: name(aOther.name), - id(aOther.id), - keyPath(aOther.keyPath), - indexes(aOther.indexes), - nextAutoIncrementId(aOther.nextAutoIncrementId), - comittedAutoIncrementId(aOther.comittedAutoIncrementId) -{ - // Doesn't copy the refcount - MOZ_COUNT_CTOR(ObjectStoreInfo); -} - #ifdef NS_BUILD_REFCNT_LOGGING IndexInfo::IndexInfo() : id(LL_MININT), unique(false), + autoIncrement(false), multiEntry(false) { MOZ_COUNT_CTOR(IndexInfo); @@ -118,6 +108,7 @@ IndexInfo::IndexInfo(const IndexInfo& aOther) name(aOther.name), keyPath(aOther.keyPath), unique(aOther.unique), + autoIncrement(aOther.autoIncrement), multiEntry(aOther.multiEntry) { MOZ_COUNT_CTOR(IndexInfo); @@ -130,8 +121,19 @@ IndexInfo::~IndexInfo() ObjectStoreInfo::ObjectStoreInfo() : id(0), - nextAutoIncrementId(0), - comittedAutoIncrementId(0) + autoIncrement(false), + databaseId(0) +{ + MOZ_COUNT_CTOR(ObjectStoreInfo); +} + +ObjectStoreInfo::ObjectStoreInfo(ObjectStoreInfo& aOther) +: name(aOther.name), + id(aOther.id), + keyPath(aOther.keyPath), + autoIncrement(aOther.autoIncrement), + databaseId(aOther.databaseId), + indexes(aOther.indexes) { MOZ_COUNT_CTOR(ObjectStoreInfo); } @@ -204,6 +206,11 @@ DatabaseInfo::Remove(nsIAtom* aId) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + DatabaseInfo* info = nsnull; + + DebugOnly got = Get(aId, &info); + NS_ASSERTION(got && info, "Don't know anything about this one!"); + if (gDatabaseHash) { gDatabaseHash->Remove(aId); @@ -214,31 +221,6 @@ DatabaseInfo::Remove(nsIAtom* aId) } } -PLDHashOperator -EnumerateDatabasesRemoveOrigin(nsISupports* aId, - DatabaseInfo*& aDatabaseInfo, - void* aUserArg) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - const nsACString* origin = static_cast(aUserArg); - return aDatabaseInfo->origin.Equals(*origin) ? - PL_DHASH_REMOVE : - PL_DHASH_NEXT; -} - -// static -void -DatabaseInfo::RemoveAllForOrigin(const nsACString& aOrigin) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (gDatabaseHash) { - gDatabaseHash->Enumerate(EnumerateDatabasesRemoveOrigin, - const_cast(&aOrigin)); - } -} - bool DatabaseInfo::GetObjectStoreNames(nsTArray& aNames) { @@ -259,16 +241,17 @@ DatabaseInfo::ContainsStoreName(const nsAString& aName) return objectStoreHash && objectStoreHash->Get(aName, nsnull); } -ObjectStoreInfo* -DatabaseInfo::GetObjectStore(const nsAString& aName) +bool +DatabaseInfo::GetObjectStore(const nsAString& aName, + ObjectStoreInfo** aInfo) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); if (objectStoreHash) { - return objectStoreHash->GetWeak(aName); + return objectStoreHash->Get(aName, aInfo); } - return nsnull; + return false; } bool @@ -298,7 +281,7 @@ void DatabaseInfo::RemoveObjectStore(const nsAString& aName) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(GetObjectStore(aName), "Don't know about this one!"); + NS_ASSERTION(GetObjectStore(aName, nsnull), "Don't know about this one!"); if (objectStoreHash) { objectStoreHash->Remove(aName); diff --git a/dom/indexedDB/DatabaseInfo.h b/dom/indexedDB/DatabaseInfo.h index 1c03c545945c..bb9cd9449b61 100644 --- a/dom/indexedDB/DatabaseInfo.h +++ b/dom/indexedDB/DatabaseInfo.h @@ -46,14 +46,14 @@ #include "Key.h" #include "IDBObjectStore.h" -#include "nsRefPtrHashtable.h" +#include "nsClassHashtable.h" #include "nsHashKeys.h" BEGIN_INDEXEDDB_NAMESPACE struct ObjectStoreInfo; -typedef nsRefPtrHashtable +typedef nsClassHashtable ObjectStoreInfoHash; class IDBDatabase; @@ -77,15 +77,14 @@ private: static bool Put(DatabaseInfo* aInfo); -public: static void Remove(nsIAtom* aId); - static void RemoveAllForOrigin(const nsACString& aOrigin); - +public: bool GetObjectStoreNames(nsTArray& aNames); bool ContainsStoreName(const nsAString& aName); - ObjectStoreInfo* GetObjectStore(const nsAString& aName); + bool GetObjectStore(const nsAString& aName, + ObjectStoreInfo** aInfo); bool PutObjectStore(ObjectStoreInfo* aInfo); @@ -94,9 +93,8 @@ public: already_AddRefed Clone(); nsString name; - nsCString origin; PRUint64 version; - nsCOMPtr id; + nsIAtom* id; nsString filePath; PRInt64 nextObjectStoreId; PRInt64 nextIndexId; @@ -115,13 +113,14 @@ struct IndexInfo ~IndexInfo(); #else IndexInfo() - : id(LL_MININT), unique(false), multiEntry(false) { } + : id(LL_MININT), unique(false), autoIncrement(false) { } #endif PRInt64 id; nsString name; nsString keyPath; bool unique; + bool autoIncrement; bool multiEntry; }; @@ -129,39 +128,19 @@ struct ObjectStoreInfo { #ifdef NS_BUILD_REFCNT_LOGGING ObjectStoreInfo(); -#else - ObjectStoreInfo() - : id(0), nextAutoIncrementId(0), comittedAutoIncrementId(0) { } -#endif - ObjectStoreInfo(ObjectStoreInfo& aOther); - -private: -#ifdef NS_BUILD_REFCNT_LOGGING ~ObjectStoreInfo(); #else - ~ObjectStoreInfo() {} + ObjectStoreInfo() + : id(0), autoIncrement(false), databaseId(0) { } #endif -public: - // Constant members, can be gotten on any thread nsString name; PRInt64 id; nsString keyPath; - - // Main-thread only members. This must *not* be touced on the database thread + bool autoIncrement; + nsIAtom* databaseId; nsTArray indexes; - - // Database-thread members. After the ObjectStoreInfo has been initialized, - // these can *only* be touced on the database thread. - PRInt64 nextAutoIncrementId; - PRInt64 comittedAutoIncrementId; - - // This is threadsafe since the ObjectStoreInfos are created on the database - // thread but then only used from the main thread. Ideal would be if we - // could transfer ownership from the database thread to the main thread, but - // we don't have that ability yet. - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ObjectStoreInfo) }; struct IndexUpdateInfo diff --git a/dom/indexedDB/IDBDatabase.cpp b/dom/indexedDB/IDBDatabase.cpp index 235648d60e94..beb65297cb18 100644 --- a/dom/indexedDB/IDBDatabase.cpp +++ b/dom/indexedDB/IDBDatabase.cpp @@ -124,24 +124,24 @@ NS_STACK_CLASS class AutoRemoveObjectStore { public: - AutoRemoveObjectStore(DatabaseInfo* aInfo, const nsAString& aName) - : mInfo(aInfo), mName(aName) + AutoRemoveObjectStore(IDBDatabase* aDatabase, const nsAString& aName) + : mDatabase(aDatabase), mName(aName) { } ~AutoRemoveObjectStore() { - if (mInfo) { - mInfo->RemoveObjectStore(mName); + if (mDatabase) { + mDatabase->Info()->RemoveObjectStore(mName); } } void forget() { - mInfo = nsnull; + mDatabase = nsnull; } private: - DatabaseInfo* mInfo; + IDBDatabase* mDatabase; nsString mName; }; @@ -384,7 +384,7 @@ IDBDatabase::CreateObjectStore(const nsAString& aName, return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; } - DatabaseInfo* databaseInfo = transaction->DBInfo(); + DatabaseInfo* databaseInfo = Info(); nsString keyPath; keyPath.SetIsVoid(true); @@ -446,24 +446,25 @@ IDBDatabase::CreateObjectStore(const nsAString& aName, } } - nsRefPtr newInfo(new ObjectStoreInfo()); + nsAutoPtr newInfo(new ObjectStoreInfo()); newInfo->name = aName; newInfo->id = databaseInfo->nextObjectStoreId++; newInfo->keyPath = keyPath; - newInfo->nextAutoIncrementId = autoIncrement ? 1 : 0; - newInfo->comittedAutoIncrementId = newInfo->nextAutoIncrementId; + newInfo->autoIncrement = autoIncrement; + newInfo->databaseId = mDatabaseId; - if (!databaseInfo->PutObjectStore(newInfo)) { + if (!Info()->PutObjectStore(newInfo)) { NS_WARNING("Put failed!"); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } + ObjectStoreInfo* objectStoreInfo = newInfo.forget(); // Don't leave this in the hash if we fail below! - AutoRemoveObjectStore autoRemove(databaseInfo, aName); + AutoRemoveObjectStore autoRemove(this, aName); nsRefPtr objectStore = - transaction->GetOrCreateObjectStore(aName, newInfo); + transaction->GetOrCreateObjectStore(aName, objectStoreInfo); NS_ENSURE_TRUE(objectStore, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); nsRefPtr helper = @@ -490,9 +491,9 @@ IDBDatabase::DeleteObjectStore(const nsAString& aName) return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; } - DatabaseInfo* info = transaction->DBInfo(); - ObjectStoreInfo* objectStoreInfo = info->GetObjectStore(aName); - if (!objectStoreInfo) { + DatabaseInfo* info = Info(); + ObjectStoreInfo* objectStoreInfo; + if (!info->GetObjectStore(aName, &objectStoreInfo)) { return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR; } @@ -501,7 +502,9 @@ IDBDatabase::DeleteObjectStore(const nsAString& aName) nsresult rv = helper->DispatchToTransactionPool(); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - transaction->RemoveObjectStore(aName); + info->RemoveObjectStore(aName); + + transaction->ReleaseCachedObjectStore(aName); return NS_OK; } diff --git a/dom/indexedDB/IDBFactory.cpp b/dom/indexedDB/IDBFactory.cpp index 248fe16515d3..5511deb9b373 100644 --- a/dom/indexedDB/IDBFactory.cpp +++ b/dom/indexedDB/IDBFactory.cpp @@ -226,8 +226,9 @@ IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection, bool hasResult; while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { - nsRefPtr* element = + nsAutoPtr* element = aObjectStores.AppendElement(new ObjectStoreInfo()); + NS_ENSURE_TRUE(element, NS_ERROR_OUT_OF_MEMORY); ObjectStoreInfo* info = element->get(); @@ -249,8 +250,8 @@ IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection, NS_ENSURE_SUCCESS(rv, rv); } - info->nextAutoIncrementId = stmt->AsInt64(3); - info->comittedAutoIncrementId = info->nextAutoIncrementId; + info->autoIncrement = !!stmt->AsInt32(3); + info->databaseId = aDatabaseId; ObjectStoreInfoMap* mapEntry = infoMap.AppendElement(); NS_ENSURE_TRUE(mapEntry, NS_ERROR_OUT_OF_MEMORY); @@ -262,7 +263,8 @@ IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection, // Load index information rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( - "SELECT object_store_id, id, name, key_path, unique_index, multientry " + "SELECT object_store_id, id, name, key_path, unique_index, multientry, " + "object_store_autoincrement " "FROM object_store_index" ), getter_AddRefs(stmt)); NS_ENSURE_SUCCESS(rv, rv); @@ -296,6 +298,7 @@ IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection, indexInfo->unique = !!stmt->AsInt32(4); indexInfo->multiEntry = !!stmt->AsInt32(5); + indexInfo->autoIncrement = !!stmt->AsInt32(6); } NS_ENSURE_SUCCESS(rv, rv); @@ -324,28 +327,35 @@ IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection, // static nsresult -IDBFactory::SetDatabaseMetadata(DatabaseInfo* aDatabaseInfo, - PRUint64 aVersion, - ObjectStoreInfoArray& aObjectStores) +IDBFactory::UpdateDatabaseMetadata(DatabaseInfo* aDatabaseInfo, + PRUint64 aVersion, + ObjectStoreInfoArray& aObjectStores) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(aDatabaseInfo, "Null pointer!"); ObjectStoreInfoArray objectStores; - objectStores.SwapElements(aObjectStores); - -#ifdef DEBUG - { - nsTArray existingNames; - aDatabaseInfo->GetObjectStoreNames(existingNames); - NS_ASSERTION(existingNames.IsEmpty(), "Should be an empty DatabaseInfo"); + if (!objectStores.SwapElements(aObjectStores)) { + NS_WARNING("Out of memory!"); + return NS_ERROR_OUT_OF_MEMORY; + } + + nsAutoTArray existingNames; + if (!aDatabaseInfo->GetObjectStoreNames(existingNames)) { + NS_WARNING("Out of memory!"); + return NS_ERROR_OUT_OF_MEMORY; + } + + // Remove all the old ones. + for (PRUint32 index = 0; index < existingNames.Length(); index++) { + aDatabaseInfo->RemoveObjectStore(existingNames[index]); } -#endif aDatabaseInfo->version = aVersion; for (PRUint32 index = 0; index < objectStores.Length(); index++) { - nsRefPtr& info = objectStores[index]; + nsAutoPtr& info = objectStores[index]; + NS_ASSERTION(info->databaseId == aDatabaseInfo->id, "Huh?!"); if (!aDatabaseInfo->PutObjectStore(info)) { NS_WARNING("Out of memory!"); diff --git a/dom/indexedDB/IDBFactory.h b/dom/indexedDB/IDBFactory.h index 8a1871fe180c..e0e13d6372e7 100644 --- a/dom/indexedDB/IDBFactory.h +++ b/dom/indexedDB/IDBFactory.h @@ -59,7 +59,7 @@ struct ObjectStoreInfo; class IDBFactory : public nsIIDBFactory { - typedef nsTArray > ObjectStoreInfoArray; + typedef nsTArray > ObjectStoreInfoArray; public: NS_DECL_ISUPPORTS NS_DECL_NSIIDBFACTORY @@ -90,9 +90,9 @@ public: ObjectStoreInfoArray& aObjectStores); static nsresult - SetDatabaseMetadata(DatabaseInfo* aDatabaseInfo, - PRUint64 aVersion, - ObjectStoreInfoArray& aObjectStores); + UpdateDatabaseMetadata(DatabaseInfo* aDatabaseInfo, + PRUint64 aVersion, + ObjectStoreInfoArray& aObjectStores); private: IDBFactory(); diff --git a/dom/indexedDB/IDBIndex.cpp b/dom/indexedDB/IDBIndex.cpp index ed33e718eaef..afeb1ee4e62e 100644 --- a/dom/indexedDB/IDBIndex.cpp +++ b/dom/indexedDB/IDBIndex.cpp @@ -321,13 +321,15 @@ IDBIndex::Create(IDBObjectStore* aObjectStore, index->mKeyPath = aIndexInfo->keyPath; index->mUnique = aIndexInfo->unique; index->mMultiEntry = aIndexInfo->multiEntry; + index->mAutoIncrement = aIndexInfo->autoIncrement; return index.forget(); } IDBIndex::IDBIndex() : mId(LL_MININT), - mUnique(false) + mUnique(false), + mAutoIncrement(false) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); } @@ -691,12 +693,26 @@ GetKeyHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) { NS_ASSERTION(mKeyRange, "Must have a key range here!"); + nsCString keyColumn; nsCString indexTable; - if (mIndex->IsUnique()) { - indexTable.AssignLiteral("unique_index_data"); + + if (mIndex->IsAutoIncrement()) { + keyColumn.AssignLiteral("ai_object_data_id"); + if (mIndex->IsUnique()) { + indexTable.AssignLiteral("ai_unique_index_data"); + } + else { + indexTable.AssignLiteral("ai_index_data"); + } } else { - indexTable.AssignLiteral("index_data"); + keyColumn.AssignLiteral("object_data_key"); + if (mIndex->IsUnique()) { + indexTable.AssignLiteral("unique_index_data"); + } + else { + indexTable.AssignLiteral("index_data"); + } } nsCString keyRangeClause; @@ -704,10 +720,12 @@ GetKeyHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!"); - nsCString query = NS_LITERAL_CSTRING("SELECT object_data_key FROM ") + - indexTable + - NS_LITERAL_CSTRING(" WHERE index_id = :index_id") + - keyRangeClause + + NS_NAMED_LITERAL_CSTRING(indexId, "index_id"); + + nsCString query = NS_LITERAL_CSTRING("SELECT ") + keyColumn + + NS_LITERAL_CSTRING(" FROM ") + indexTable + + NS_LITERAL_CSTRING(" WHERE ") + indexId + + NS_LITERAL_CSTRING(" = :") + indexId + keyRangeClause + NS_LITERAL_CSTRING(" LIMIT 1"); nsCOMPtr stmt = mTransaction->GetCachedStatement(query); @@ -715,8 +733,7 @@ GetKeyHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) mozStorageStatementScoper scoper(stmt); - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), - mIndex->Id()); + nsresult rv = stmt->BindInt64ByName(indexId, mIndex->Id()); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); rv = mKeyRange->BindToStatement(stmt); @@ -746,12 +763,29 @@ GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) { NS_ASSERTION(mKeyRange, "Must have a key range here!"); - nsCString indexTable; - if (mIndex->IsUnique()) { - indexTable.AssignLiteral("unique_index_data"); + nsCString objectTable; + nsCString joinTable; + nsCString objectColumn; + + if (mIndex->IsAutoIncrement()) { + objectTable.AssignLiteral("ai_object_data"); + objectColumn.AssignLiteral("ai_object_data_id"); + if (mIndex->IsUnique()) { + joinTable.AssignLiteral("ai_unique_index_data"); + } + else { + joinTable.AssignLiteral("ai_index_data"); + } } else { - indexTable.AssignLiteral("index_data"); + objectTable.AssignLiteral("object_data"); + objectColumn.AssignLiteral("object_data_id"); + if (mIndex->IsUnique()) { + joinTable.AssignLiteral("unique_index_data"); + } + else { + joinTable.AssignLiteral("index_data"); + } } nsCString keyRangeClause; @@ -759,12 +793,15 @@ GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!"); - nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM object_data " - "INNER JOIN ") + indexTable + - NS_LITERAL_CSTRING(" AS index_table ON object_data.id = ") + - NS_LITERAL_CSTRING("index_table.object_data_id WHERE " - "index_id = :index_id") + - keyRangeClause + + NS_NAMED_LITERAL_CSTRING(indexId, "index_id"); + + nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM ") + objectTable + + NS_LITERAL_CSTRING(" INNER JOIN ") + joinTable + + NS_LITERAL_CSTRING(" ON ") + objectTable + + NS_LITERAL_CSTRING(".id = ") + joinTable + + NS_LITERAL_CSTRING(".") + objectColumn + + NS_LITERAL_CSTRING(" WHERE ") + indexId + + NS_LITERAL_CSTRING(" = :") + indexId + keyRangeClause + NS_LITERAL_CSTRING(" LIMIT 1"); nsCOMPtr stmt = mTransaction->GetCachedStatement(query); @@ -772,8 +809,7 @@ GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) mozStorageStatementScoper scoper(stmt); - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), - mIndex->Id()); + nsresult rv = stmt->BindInt64ByName(indexId, mIndex->Id()); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); rv = mKeyRange->BindToStatement(stmt); @@ -807,12 +843,26 @@ GetHelper::GetSuccessResult(JSContext* aCx, nsresult GetAllKeysHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) { + nsCString keyColumn; nsCString tableName; - if (mIndex->IsUnique()) { - tableName.AssignLiteral("unique_index_data"); + + if (mIndex->IsAutoIncrement()) { + keyColumn.AssignLiteral("ai_object_data_id"); + if (mIndex->IsUnique()) { + tableName.AssignLiteral("ai_unique_index_data"); + } + else { + tableName.AssignLiteral("ai_index_data"); + } } else { - tableName.AssignLiteral("index_data"); + keyColumn.AssignLiteral("object_data_key"); + if (mIndex->IsUnique()) { + tableName.AssignLiteral("unique_index_data"); + } + else { + tableName.AssignLiteral("index_data"); + } } nsCString keyRangeClause; @@ -826,18 +876,20 @@ GetAllKeysHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) limitClause.AppendInt(mLimit); } - nsCString query = NS_LITERAL_CSTRING("SELECT object_data_key FROM ") + - tableName + - NS_LITERAL_CSTRING(" WHERE index_id = :index_id") + - keyRangeClause + limitClause; + NS_NAMED_LITERAL_CSTRING(indexId, "index_id"); + + nsCString query = NS_LITERAL_CSTRING("SELECT ") + keyColumn + + NS_LITERAL_CSTRING(" FROM ") + tableName + + NS_LITERAL_CSTRING(" WHERE ") + indexId + + NS_LITERAL_CSTRING(" = :") + indexId + keyRangeClause + + limitClause; nsCOMPtr stmt = mTransaction->GetCachedStatement(query); NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); mozStorageStatementScoper scoper(stmt); - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), - mIndex->Id()); + nsresult rv = stmt->BindInt64ByName(indexId, mIndex->Id()); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); if (mKeyRange) { @@ -915,14 +967,33 @@ GetAllKeysHelper::GetSuccessResult(JSContext* aCx, nsresult GetAllHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) { - nsCString indexTable; - if (mIndex->IsUnique()) { - indexTable.AssignLiteral("unique_index_data"); + nsCString dataTableName; + nsCString objectDataId; + nsCString indexTableName; + + if (mIndex->IsAutoIncrement()) { + dataTableName.AssignLiteral("ai_object_data"); + objectDataId.AssignLiteral("ai_object_data_id"); + if (mIndex->IsUnique()) { + indexTableName.AssignLiteral("ai_unique_index_data"); + } + else { + indexTableName.AssignLiteral("ai_index_data"); + } } else { - indexTable.AssignLiteral("index_data"); + dataTableName.AssignLiteral("object_data"); + objectDataId.AssignLiteral("object_data_id"); + if (mIndex->IsUnique()) { + indexTableName.AssignLiteral("unique_index_data"); + } + else { + indexTableName.AssignLiteral("index_data"); + } } + NS_NAMED_LITERAL_CSTRING(indexId, "index_id"); + nsCString keyRangeClause; if (mKeyRange) { mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("value"), keyRangeClause); @@ -934,20 +1005,21 @@ GetAllHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) limitClause.AppendInt(mLimit); } - nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM object_data " - "INNER JOIN ") + indexTable + - NS_LITERAL_CSTRING(" AS index_table ON object_data.id = " - "index_table.object_data_id " - "WHERE index_id = :index_id") + - keyRangeClause + limitClause; + nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM ") + dataTableName + + NS_LITERAL_CSTRING(" INNER JOIN ") + indexTableName + + NS_LITERAL_CSTRING(" ON ") + dataTableName + + NS_LITERAL_CSTRING(".id = ") + indexTableName + + NS_LITERAL_CSTRING(".") + objectDataId + + NS_LITERAL_CSTRING(" WHERE ") + indexId + + NS_LITERAL_CSTRING(" = :") + indexId + keyRangeClause + + limitClause; nsCOMPtr stmt = mTransaction->GetCachedStatement(query); NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); mozStorageStatementScoper scoper(stmt); - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), - mIndex->Id()); + nsresult rv = stmt->BindInt64ByName(indexId, mIndex->Id()); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); if (mKeyRange) { @@ -997,13 +1069,31 @@ OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) NS_ASSERTION(aConnection, "Passed a null connection!"); nsCString table; - if (mIndex->IsUnique()) { - table.AssignLiteral("unique_index_data"); + nsCString keyColumn; + + if (mIndex->IsAutoIncrement()) { + keyColumn.AssignLiteral("ai_object_data_id"); + if (mIndex->IsUnique()) { + table.AssignLiteral("ai_unique_index_data"); + } + else { + table.AssignLiteral("ai_index_data"); + } } else { - table.AssignLiteral("index_data"); + keyColumn.AssignLiteral("object_data_key"); + if (mIndex->IsUnique()) { + table.AssignLiteral("unique_index_data"); + } + else { + table.AssignLiteral("index_data"); + } } + NS_NAMED_LITERAL_CSTRING(id, "id"); + NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key"); + NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key"); + NS_NAMED_LITERAL_CSTRING(value, "value"); nsCString keyRangeClause; @@ -1011,27 +1101,30 @@ OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) mKeyRange->GetBindingClause(value, keyRangeClause); } - nsCAutoString directionClause(" ORDER BY value "); + nsCAutoString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + value; switch (mDirection) { case nsIIDBCursor::NEXT: case nsIIDBCursor::NEXT_NO_DUPLICATE: - directionClause += NS_LITERAL_CSTRING("ASC, object_data_key ASC"); + directionClause += NS_LITERAL_CSTRING(" ASC, ") + keyColumn + + NS_LITERAL_CSTRING(" ASC"); break; case nsIIDBCursor::PREV: - directionClause += NS_LITERAL_CSTRING("DESC, object_data_key DESC"); + directionClause += NS_LITERAL_CSTRING(" DESC, ") + keyColumn + + NS_LITERAL_CSTRING(" DESC"); break; case nsIIDBCursor::PREV_NO_DUPLICATE: - directionClause += NS_LITERAL_CSTRING("DESC, object_data_key ASC"); + directionClause += NS_LITERAL_CSTRING(" DESC, ") + keyColumn + + NS_LITERAL_CSTRING(" ASC"); break; default: NS_NOTREACHED("Unknown direction!"); } - nsCString firstQuery = NS_LITERAL_CSTRING("SELECT value, object_data_key " - "FROM ") + table + - NS_LITERAL_CSTRING(" WHERE index_id = :index_id") + + nsCString firstQuery = NS_LITERAL_CSTRING("SELECT value, ") + keyColumn + + NS_LITERAL_CSTRING(" FROM ") + table + + NS_LITERAL_CSTRING(" WHERE index_id = :") + id + keyRangeClause + directionClause + NS_LITERAL_CSTRING(" LIMIT 1"); @@ -1041,8 +1134,7 @@ OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) mozStorageStatementScoper scoper(stmt); - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), - mIndex->Id()); + nsresult rv = stmt->BindInt64ByName(id, mIndex->Id()); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); if (mKeyRange) { @@ -1066,11 +1158,13 @@ OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) NS_ENSURE_SUCCESS(rv, rv); // Now we need to make the query to get the next match. - nsCAutoString queryStart = NS_LITERAL_CSTRING("SELECT value, object_data_key" - " FROM ") + table + - NS_LITERAL_CSTRING(" WHERE index_id = :id"); + nsCAutoString queryStart = NS_LITERAL_CSTRING("SELECT value, ") + keyColumn + + NS_LITERAL_CSTRING(" FROM ") + table + + NS_LITERAL_CSTRING(" WHERE index_id = :") + id; + NS_NAMED_LITERAL_CSTRING(currentKey, "current_key"); NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key"); + NS_NAMED_LITERAL_CSTRING(objectKey, "object_key"); switch (mDirection) { case nsIIDBCursor::NEXT: @@ -1079,18 +1173,14 @@ OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) queryStart); mRangeKey = mKeyRange->Upper(); } - mContinueQuery = - queryStart + - NS_LITERAL_CSTRING(" AND value >= :current_key AND " - "( value > :current_key OR " - " object_data_key > :object_key )") + - directionClause + - NS_LITERAL_CSTRING(" LIMIT "); - mContinueToQuery = - queryStart + - NS_LITERAL_CSTRING(" AND value >= :current_key ") + - directionClause + - NS_LITERAL_CSTRING(" LIMIT "); + mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND value >= :") + + currentKey + NS_LITERAL_CSTRING(" AND ( value > :") + + currentKey + NS_LITERAL_CSTRING(" OR ") + keyColumn + + NS_LITERAL_CSTRING(" > :") + objectKey + + NS_LITERAL_CSTRING(" )") + directionClause + + NS_LITERAL_CSTRING(" LIMIT "); + mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND value >= :") + + currentKey + NS_LITERAL_CSTRING(" LIMIT "); break; case nsIIDBCursor::NEXT_NO_DUPLICATE: @@ -1099,14 +1189,12 @@ OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) queryStart); mRangeKey = mKeyRange->Upper(); } - mContinueQuery = - queryStart + NS_LITERAL_CSTRING(" AND value > :current_key") + - directionClause + - NS_LITERAL_CSTRING(" LIMIT "); - mContinueToQuery = - queryStart + NS_LITERAL_CSTRING(" AND value >= :current_key") + - directionClause + - NS_LITERAL_CSTRING(" LIMIT "); + mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND value > :") + + currentKey + directionClause + + NS_LITERAL_CSTRING(" LIMIT "); + mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND value >= :") + + currentKey + directionClause + + NS_LITERAL_CSTRING(" LIMIT "); break; case nsIIDBCursor::PREV: @@ -1116,18 +1204,14 @@ OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) mRangeKey = mKeyRange->Lower(); } - mContinueQuery = - queryStart + - NS_LITERAL_CSTRING(" AND value <= :current_key AND " - "( value < :current_key OR " - " object_data_key < :object_key )") + - directionClause + - NS_LITERAL_CSTRING(" LIMIT "); - mContinueToQuery = - queryStart + - NS_LITERAL_CSTRING(" AND value <= :current_key ") + - directionClause + - NS_LITERAL_CSTRING(" LIMIT "); + mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND value <= :") + + currentKey + NS_LITERAL_CSTRING(" AND ( value < :") + + currentKey + NS_LITERAL_CSTRING(" OR ") + keyColumn + + NS_LITERAL_CSTRING(" < :") + objectKey + + NS_LITERAL_CSTRING(" ) ") + directionClause + + NS_LITERAL_CSTRING(" LIMIT "); + mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND value <= :") + + currentKey + NS_LITERAL_CSTRING(" LIMIT "); break; case nsIIDBCursor::PREV_NO_DUPLICATE: @@ -1136,16 +1220,12 @@ OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) queryStart); mRangeKey = mKeyRange->Lower(); } - mContinueQuery = - queryStart + - NS_LITERAL_CSTRING(" AND value < :current_key") + - directionClause + - NS_LITERAL_CSTRING(" LIMIT "); - mContinueToQuery = - queryStart + - NS_LITERAL_CSTRING(" AND value <= :current_key") + - directionClause + - NS_LITERAL_CSTRING(" LIMIT "); + mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND value < :") + + currentKey + directionClause + + NS_LITERAL_CSTRING(" LIMIT "); + mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND value <= :") + + currentKey + directionClause + + NS_LITERAL_CSTRING(" LIMIT "); break; default: @@ -1178,52 +1258,81 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) NS_ASSERTION(aConnection, "Passed a null connection!"); nsCString indexTable; - if (mIndex->IsUnique()) { - indexTable.AssignLiteral("unique_index_data"); + nsCString objectTable; + nsCString objectDataIdColumn; + nsCString keyValueColumn; + + if (mIndex->IsAutoIncrement()) { + objectTable.AssignLiteral("ai_object_data"); + objectDataIdColumn.AssignLiteral("ai_object_data_id"); + keyValueColumn.AssignLiteral("ai_object_data_id"); + if (mIndex->IsUnique()) { + indexTable.AssignLiteral("ai_unique_index_data"); + } + else { + indexTable.AssignLiteral("ai_index_data"); + } } else { - indexTable.AssignLiteral("index_data"); + objectTable.AssignLiteral("object_data"); + objectDataIdColumn.AssignLiteral("object_data_id"); + keyValueColumn.AssignLiteral("object_data_key"); + if (mIndex->IsUnique()) { + indexTable.AssignLiteral("unique_index_data"); + } + else { + indexTable.AssignLiteral("index_data"); + } } - NS_NAMED_LITERAL_CSTRING(value, "index_table.value"); + nsCString value = indexTable + NS_LITERAL_CSTRING(".value"); + nsCString keyValue = indexTable + NS_LITERAL_CSTRING(".") + keyValueColumn; nsCString keyRangeClause; if (mKeyRange) { mKeyRange->GetBindingClause(value, keyRangeClause); } - nsCAutoString directionClause(" ORDER BY index_table.value "); + nsCAutoString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + value; switch (mDirection) { case nsIIDBCursor::NEXT: case nsIIDBCursor::NEXT_NO_DUPLICATE: - directionClause += - NS_LITERAL_CSTRING("ASC, index_table.object_data_key ASC"); + directionClause += NS_LITERAL_CSTRING(" ASC, ") + keyValue + + NS_LITERAL_CSTRING(" ASC"); break; case nsIIDBCursor::PREV: - directionClause += - NS_LITERAL_CSTRING("DESC, index_table.object_data_key DESC"); + directionClause += NS_LITERAL_CSTRING(" DESC, ") + keyValue + + NS_LITERAL_CSTRING(" DESC"); break; case nsIIDBCursor::PREV_NO_DUPLICATE: - directionClause += - NS_LITERAL_CSTRING("DESC, index_table.object_data_key ASC"); + directionClause += NS_LITERAL_CSTRING(" DESC, ") + keyValue + + NS_LITERAL_CSTRING(" ASC"); break; default: NS_NOTREACHED("Unknown direction!"); } - nsCString firstQuery = - NS_LITERAL_CSTRING("SELECT index_table.value, " - "index_table.object_data_key, object_data.data, " - "object_data.file_ids FROM ") + - indexTable + - NS_LITERAL_CSTRING(" AS index_table INNER JOIN object_data ON " - "index_table.object_data_id = object_data.id " - "WHERE index_table.index_id = :id") + - keyRangeClause + directionClause + - NS_LITERAL_CSTRING(" LIMIT 1"); + NS_NAMED_LITERAL_CSTRING(id, "id"); + NS_NAMED_LITERAL_CSTRING(dot, "."); + NS_NAMED_LITERAL_CSTRING(commaspace, ", "); + + nsCString data = objectTable + NS_LITERAL_CSTRING(".data"); + nsCString fileIds = objectTable + NS_LITERAL_CSTRING(".file_ids"); + + nsCString firstQuery = NS_LITERAL_CSTRING("SELECT ") + value + commaspace + + keyValue + commaspace + data + commaspace + fileIds + + NS_LITERAL_CSTRING(" FROM ") + indexTable + + NS_LITERAL_CSTRING(" INNER JOIN ") + objectTable + + NS_LITERAL_CSTRING(" ON ") + indexTable + dot + + objectDataIdColumn + NS_LITERAL_CSTRING(" = ") + + objectTable + dot + id + + NS_LITERAL_CSTRING(" WHERE ") + indexTable + + NS_LITERAL_CSTRING(".index_id = :") + id + + keyRangeClause + directionClause + + NS_LITERAL_CSTRING(" LIMIT 1"); nsCOMPtr stmt = mTransaction->GetCachedStatement(firstQuery); @@ -1231,7 +1340,7 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) mozStorageStatementScoper scoper(stmt); - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mIndex->Id()); + nsresult rv = stmt->BindInt64ByName(id, mIndex->Id()); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); if (mKeyRange) { @@ -1259,17 +1368,29 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) NS_ENSURE_SUCCESS(rv, rv); // Now we need to make the query to get the next match. - nsCAutoString queryStart = - NS_LITERAL_CSTRING("SELECT index_table.value, " - "index_table.object_data_key, object_data.data, " - "object_data.file_ids FROM ") + - indexTable + - NS_LITERAL_CSTRING(" AS index_table INNER JOIN object_data ON " - "index_table.object_data_id = object_data.id " - "WHERE index_table.index_id = :id"); + nsCAutoString queryStart = NS_LITERAL_CSTRING("SELECT ") + value + + commaspace + keyValue + commaspace + data + + commaspace + fileIds + + NS_LITERAL_CSTRING(" FROM ") + indexTable + + NS_LITERAL_CSTRING(" INNER JOIN ") + objectTable + + NS_LITERAL_CSTRING(" ON ") + indexTable + dot + + objectDataIdColumn + NS_LITERAL_CSTRING(" = ") + + objectTable + dot + id + + NS_LITERAL_CSTRING(" WHERE ") + indexTable + + NS_LITERAL_CSTRING(".index_id = :") + id; + NS_NAMED_LITERAL_CSTRING(currentKey, "current_key"); NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key"); + NS_NAMED_LITERAL_CSTRING(objectKey, "object_key"); + NS_NAMED_LITERAL_CSTRING(andStr, " AND "); + NS_NAMED_LITERAL_CSTRING(orStr, " OR "); + NS_NAMED_LITERAL_CSTRING(ge, " >= :"); + NS_NAMED_LITERAL_CSTRING(gt, " > :"); + NS_NAMED_LITERAL_CSTRING(le, " <= :"); + NS_NAMED_LITERAL_CSTRING(lt, " < :"); + NS_NAMED_LITERAL_CSTRING(openparen, " ( "); + NS_NAMED_LITERAL_CSTRING(closeparen, " ) "); NS_NAMED_LITERAL_CSTRING(limit, " LIMIT "); switch (mDirection) { @@ -1279,16 +1400,11 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) queryStart); mRangeKey = mKeyRange->Upper(); } - mContinueQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value >= :current_key AND " - "( index_table.value > :current_key OR " - " index_table.object_data_key > :object_key ) ") + - directionClause + limit; - mContinueToQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value >= :current_key") + - directionClause + limit; + mContinueQuery = queryStart + andStr + value + ge + currentKey + andStr + + openparen + value + gt + currentKey + orStr + keyValue + + gt + objectKey + closeparen + directionClause + limit; + mContinueToQuery = queryStart + andStr + value + ge + currentKey + + directionClause + limit; break; case nsIIDBCursor::NEXT_NO_DUPLICATE: @@ -1297,14 +1413,10 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) queryStart); mRangeKey = mKeyRange->Upper(); } - mContinueQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value > :current_key") + - directionClause + limit; - mContinueToQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value >= :current_key") + - directionClause + limit; + mContinueQuery = queryStart + andStr + value + gt + currentKey + + directionClause + limit; + mContinueToQuery = queryStart + andStr + value + ge + currentKey + + directionClause + limit; break; case nsIIDBCursor::PREV: @@ -1313,16 +1425,11 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) queryStart); mRangeKey = mKeyRange->Lower(); } - mContinueQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value <= :current_key AND " - "( index_table.value < :current_key OR " - " index_table.object_data_key < :object_key ) ") + - directionClause + limit; - mContinueToQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value <= :current_key") + - directionClause + limit; + mContinueQuery = queryStart + andStr + value + le + currentKey + andStr + + openparen + value + lt + currentKey + orStr + keyValue + + lt + objectKey + closeparen + directionClause + limit; + mContinueToQuery = queryStart + andStr + value + le + currentKey + + directionClause + limit; break; case nsIIDBCursor::PREV_NO_DUPLICATE: @@ -1331,14 +1438,10 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) queryStart); mRangeKey = mKeyRange->Lower(); } - mContinueQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value < :current_key") + - directionClause + limit; - mContinueToQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value <= :current_key") + - directionClause + limit; + mContinueQuery = queryStart + andStr + value + lt + currentKey + + directionClause +limit; + mContinueToQuery = queryStart + andStr + value + le + currentKey + + directionClause + limit; break; default: @@ -1372,11 +1475,22 @@ nsresult CountHelper::DoDatabaseWork(mozIStorageConnection* aConnection) { nsCString table; - if (mIndex->IsUnique()) { - table.AssignLiteral("unique_index_data"); + + if (mIndex->IsAutoIncrement()) { + if (mIndex->IsUnique()) { + table.AssignLiteral("ai_unique_index_data"); + } + else { + table.AssignLiteral("ai_index_data"); + } } else { - table.AssignLiteral("index_data"); + if (mIndex->IsUnique()) { + table.AssignLiteral("unique_index_data"); + } + else { + table.AssignLiteral("index_data"); + } } NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key"); @@ -1395,8 +1509,10 @@ CountHelper::DoDatabaseWork(mozIStorageConnection* aConnection) } } + NS_NAMED_LITERAL_CSTRING(id, "id"); + nsCString query = NS_LITERAL_CSTRING("SELECT count(*) FROM ") + table + - NS_LITERAL_CSTRING(" WHERE index_id = :id") + + NS_LITERAL_CSTRING(" WHERE index_id = :") + id + keyRangeClause; nsCOMPtr stmt = mTransaction->GetCachedStatement(query); @@ -1404,7 +1520,7 @@ CountHelper::DoDatabaseWork(mozIStorageConnection* aConnection) mozStorageStatementScoper scoper(stmt); - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mIndex->Id()); + nsresult rv = stmt->BindInt64ByName(id, mIndex->Id()); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); if (mKeyRange) { diff --git a/dom/indexedDB/IDBIndex.h b/dom/indexedDB/IDBIndex.h index 4ed9c378afee..643ae942de0c 100644 --- a/dom/indexedDB/IDBIndex.h +++ b/dom/indexedDB/IDBIndex.h @@ -92,6 +92,11 @@ public: return mMultiEntry; } + bool IsAutoIncrement() const + { + return mAutoIncrement; + } + const nsString& KeyPath() const { return mKeyPath; @@ -111,6 +116,7 @@ private: nsString mKeyPath; bool mUnique; bool mMultiEntry; + bool mAutoIncrement; }; END_INDEXEDDB_NAMESPACE diff --git a/dom/indexedDB/IDBObjectStore.cpp b/dom/indexedDB/IDBObjectStore.cpp index 6cff1d9f865d..6475376ed069 100644 --- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -382,18 +382,23 @@ NS_STACK_CLASS class AutoRemoveIndex { public: - AutoRemoveIndex(ObjectStoreInfo* aObjectStoreInfo, + AutoRemoveIndex(IDBDatabase* aDatabase, + const nsAString& aObjectStoreName, const nsAString& aIndexName) - : mObjectStoreInfo(aObjectStoreInfo), mIndexName(aIndexName) + : mDatabase(aDatabase), mObjectStoreName(aObjectStoreName), + mIndexName(aIndexName) { } ~AutoRemoveIndex() { - if (mObjectStoreInfo) { - for (PRUint32 i = 0; i < mObjectStoreInfo->indexes.Length(); i++) { - if (mObjectStoreInfo->indexes[i].name == mIndexName) { - mObjectStoreInfo->indexes.RemoveElementAt(i); - break; + if (mDatabase) { + ObjectStoreInfo* info; + if (mDatabase->Info()->GetObjectStore(mObjectStoreName, &info)) { + for (PRUint32 index = 0; index < info->indexes.Length(); index++) { + if (info->indexes[index].name == mIndexName) { + info->indexes.RemoveElementAt(index); + break; + } } } } @@ -401,11 +406,12 @@ public: void forget() { - mObjectStoreInfo = nsnull; + mDatabase = nsnull; } private: - ObjectStoreInfo* mObjectStoreInfo; + IDBDatabase* mDatabase; + nsString mObjectStoreName; nsString mIndexName; }; @@ -497,8 +503,7 @@ JSClass gDummyPropClass = { // static already_AddRefed IDBObjectStore::Create(IDBTransaction* aTransaction, - ObjectStoreInfo* aStoreInfo, - nsIAtom* aDatabaseId) + const ObjectStoreInfo* aStoreInfo) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); @@ -511,9 +516,8 @@ IDBObjectStore::Create(IDBTransaction* aTransaction, objectStore->mName = aStoreInfo->name; objectStore->mId = aStoreInfo->id; objectStore->mKeyPath = aStoreInfo->keyPath; - objectStore->mAutoIncrement = !!aStoreInfo->nextAutoIncrementId; - objectStore->mDatabaseId = aDatabaseId; - objectStore->mInfo = aStoreInfo; + objectStore->mAutoIncrement = aStoreInfo->autoIncrement; + objectStore->mDatabaseId = aStoreInfo->databaseId; return objectStore.forget(); } @@ -621,26 +625,72 @@ nsresult IDBObjectStore::UpdateIndexes(IDBTransaction* aTransaction, PRInt64 aObjectStoreId, const Key& aObjectStoreKey, + bool aAutoIncrement, bool aOverwrite, PRInt64 aObjectDataId, const nsTArray& aUpdateInfoArray) { + NS_ASSERTION(!aAutoIncrement || aObjectDataId != LL_MININT, + "Bad objectData id!"); + nsCOMPtr stmt; nsresult rv; - NS_ASSERTION(aObjectDataId != LL_MININT, "Bad objectData id!"); - - NS_NAMED_LITERAL_CSTRING(objectDataId, "object_data_id"); - - if (aOverwrite) { + if (aObjectDataId == LL_MININT) { stmt = aTransaction->GetCachedStatement( - "DELETE FROM unique_index_data " - "WHERE object_data_id = :object_data_id; " - "DELETE FROM index_data " - "WHERE object_data_id = :object_data_id"); + "SELECT id " + "FROM object_data " + "WHERE object_store_id = :osid " + "AND key_value = :key_value" + ); + NS_ENSURE_TRUE(stmt, NS_ERROR_FAILURE); mozStorageStatementScoper scoper(stmt); + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), aObjectStoreId); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ASSERTION(!aObjectStoreKey.IsUnset(), "This shouldn't happen!"); + + rv = aObjectStoreKey.BindToStatement(stmt, NS_LITERAL_CSTRING("key_value")); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + NS_ENSURE_SUCCESS(rv, rv); + + if (!hasResult) { + NS_ERROR("This is bad, we just added this value! Where'd it go?!"); + return NS_ERROR_FAILURE; + } + + aObjectDataId = stmt->AsInt64(0); + } + + NS_ASSERTION(aObjectDataId != LL_MININT, "Bad objectData id!"); + + NS_NAMED_LITERAL_CSTRING(indexId, "index_id"); + NS_NAMED_LITERAL_CSTRING(objectDataId, "object_data_id"); + NS_NAMED_LITERAL_CSTRING(objectDataKey, "object_data_key"); + NS_NAMED_LITERAL_CSTRING(value, "value"); + + if (aOverwrite) { + stmt = aTransaction->IndexDataDeleteStatement(aAutoIncrement, false); + NS_ENSURE_TRUE(stmt, NS_ERROR_FAILURE); + + mozStorageStatementScoper scoper2(stmt); + + rv = stmt->BindInt64ByName(objectDataId, aObjectDataId); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->Execute(); + NS_ENSURE_SUCCESS(rv, rv); + + stmt = aTransaction->IndexDataDeleteStatement(aAutoIncrement, true); + NS_ENSURE_TRUE(stmt, NS_ERROR_FAILURE); + + mozStorageStatementScoper scoper3(stmt); + rv = stmt->BindInt64ByName(objectDataId, aObjectDataId); NS_ENSURE_SUCCESS(rv, rv); @@ -653,33 +703,24 @@ IDBObjectStore::UpdateIndexes(IDBTransaction* aTransaction, const IndexUpdateInfo& updateInfo = aUpdateInfoArray[i]; // Insert new values. - - stmt = updateInfo.indexUnique ? - aTransaction->GetCachedStatement( - "INSERT INTO unique_index_data " - "(index_id, object_data_id, object_data_key, value) " - "VALUES (:index_id, :object_data_id, :object_data_key, :value)") : - aTransaction->GetCachedStatement( - "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)"); - + stmt = aTransaction->IndexDataInsertStatement(aAutoIncrement, + updateInfo.indexUnique); NS_ENSURE_TRUE(stmt, NS_ERROR_FAILURE); mozStorageStatementScoper scoper4(stmt); - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), - updateInfo.indexId); + rv = stmt->BindInt64ByName(indexId, updateInfo.indexId); NS_ENSURE_SUCCESS(rv, rv); rv = stmt->BindInt64ByName(objectDataId, aObjectDataId); NS_ENSURE_SUCCESS(rv, rv); - rv = aObjectStoreKey.BindToStatement(stmt, - NS_LITERAL_CSTRING("object_data_key")); - NS_ENSURE_SUCCESS(rv, rv); + if (!aAutoIncrement) { + rv = aObjectStoreKey.BindToStatement(stmt, objectDataKey); + NS_ENSURE_SUCCESS(rv, rv); + } - rv = updateInfo.value.BindToStatement(stmt, NS_LITERAL_CSTRING("value")); + rv = updateInfo.value.BindToStatement(stmt, value); NS_ENSURE_SUCCESS(rv, rv); rv = stmt->Execute(); @@ -1088,6 +1129,28 @@ IDBObjectStore::ConvertFileIdsToArray(const nsAString& aFileIds, return NS_OK; } +nsresult +IDBObjectStore::ModifyValueForNewKey(StructuredCloneWriteInfo& aCloneWriteInfo, + Key& aKey) +{ + NS_ASSERTION(IsAutoIncrement() && aKey.IsInteger(), "Don't call me!"); + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread"); + + // This is a duplicate of the js engine's byte munging here + union { + jsdouble d; + PRUint64 u; + } pun; + + pun.d = SwapBytes(static_cast(aKey.ToInteger())); + + JSAutoStructuredCloneBuffer& buffer = aCloneWriteInfo.mCloneBuffer; + PRUint64 offsetToKeyProp = aCloneWriteInfo.mOffsetToKeyProp; + + memcpy((char*)buffer.data() + offsetToKeyProp, &pun.u, sizeof(PRUint64)); + return NS_OK; +} + IDBObjectStore::IDBObjectStore() : mId(LL_MININT), mAutoIncrement(false) @@ -1137,10 +1200,15 @@ IDBObjectStore::GetAddInfo(JSContext* aCx, } // Figure out indexes and the index values to update here. - PRUint32 count = mInfo->indexes.Length(); + ObjectStoreInfo* info; + if (!mTransaction->Database()->Info()->GetObjectStore(mName, &info)) { + NS_ERROR("This should never fail!"); + } + + PRUint32 count = info->indexes.Length(); aUpdateInfoArray.SetCapacity(count); // Pretty good estimate for (PRUint32 indexesIndex = 0; indexesIndex < count; indexesIndex++) { - const IndexInfo& indexInfo = mInfo->indexes[indexesIndex]; + const IndexInfo& indexInfo = info->indexes[indexesIndex]; rv = AppendIndexUpdateInfo(indexInfo.id, indexInfo.keyPath, indexInfo.unique, indexInfo.multiEntry, @@ -1305,6 +1373,11 @@ IDBObjectStore::AddOrPut(const jsval& aValue, return rv; } + // Put requires a key, unless this is an autoIncrementing objectStore. + if (aOverwrite && !mAutoIncrement && key.IsUnset()) { + return NS_ERROR_DOM_INDEXEDDB_DATA_ERR; + } + nsRefPtr request = GenerateRequest(this); NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); @@ -1380,25 +1453,21 @@ IDBObjectStore::GetTransaction(nsIIDBTransaction** aTransaction) return NS_OK; } -NS_IMETHODIMP -IDBObjectStore::GetAutoIncrement(bool* aAutoIncrement) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - *aAutoIncrement = mAutoIncrement; - return NS_OK; -} - NS_IMETHODIMP IDBObjectStore::GetIndexNames(nsIDOMDOMStringList** aIndexNames) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + ObjectStoreInfo* info; + if (!mTransaction->Database()->Info()->GetObjectStore(mName, &info)) { + NS_ERROR("This should never fail!"); + } + nsRefPtr list(new nsDOMStringList()); - PRUint32 count = mInfo->indexes.Length(); + PRUint32 count = info->indexes.Length(); for (PRUint32 index = 0; index < count; index++) { - NS_ENSURE_TRUE(list->Add(mInfo->indexes[index].name), + NS_ENSURE_TRUE(list->Add(info->indexes[index].name), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); } @@ -1630,10 +1699,15 @@ IDBObjectStore::CreateIndex(const nsAString& aName, return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; } + ObjectStoreInfo* info; + if (!mTransaction->Database()->Info()->GetObjectStore(mName, &info)) { + NS_ERROR("This should never fail!"); + } + bool found = false; - PRUint32 indexCount = mInfo->indexes.Length(); + PRUint32 indexCount = info->indexes.Length(); for (PRUint32 index = 0; index < indexCount; index++) { - if (mInfo->indexes[index].name == aName) { + if (info->indexes[index].name == aName) { found = true; break; } @@ -1683,17 +1757,23 @@ IDBObjectStore::CreateIndex(const nsAString& aName, multiEntry = !!boolVal; } - DatabaseInfo* databaseInfo = mTransaction->DBInfo(); + DatabaseInfo* databaseInfo = mTransaction->Database()->Info(); + + IndexInfo* indexInfo = info->indexes.AppendElement(); + if (!indexInfo) { + NS_WARNING("Out of memory!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } - IndexInfo* indexInfo = mInfo->indexes.AppendElement(); indexInfo->id = databaseInfo->nextIndexId++; 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! - AutoRemoveIndex autoRemove(mInfo, aName); + AutoRemoveIndex autoRemove(mTransaction->Database(), mName, aName); #ifdef DEBUG for (PRUint32 index = 0; index < mCreatedIndexes.Length(); index++) { @@ -1732,11 +1812,16 @@ IDBObjectStore::Index(const nsAString& aName, return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR; } + ObjectStoreInfo* info; + if (!mTransaction->Database()->Info()->GetObjectStore(mName, &info)) { + NS_ERROR("This should never fail!"); + } + IndexInfo* indexInfo = nsnull; - PRUint32 indexCount = mInfo->indexes.Length(); + PRUint32 indexCount = info->indexes.Length(); for (PRUint32 index = 0; index < indexCount; index++) { - if (mInfo->indexes[index].name == aName) { - indexInfo = &(mInfo->indexes[index]); + if (info->indexes[index].name == aName) { + indexInfo = &(info->indexes[index]); break; } } @@ -1783,14 +1868,19 @@ IDBObjectStore::DeleteIndex(const nsAString& aName) NS_ASSERTION(mTransaction->IsOpen(), "Impossible!"); + ObjectStoreInfo* info; + if (!mTransaction->Database()->Info()->GetObjectStore(mName, &info)) { + NS_ERROR("This should never fail!"); + } + PRUint32 index = 0; - for (; index < mInfo->indexes.Length(); index++) { - if (mInfo->indexes[index].name == aName) { + for (; index < info->indexes.Length(); index++) { + if (info->indexes[index].name == aName) { break; } } - if (index == mInfo->indexes.Length()) { + if (index == info->indexes.Length()) { return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR; } @@ -1800,7 +1890,7 @@ IDBObjectStore::DeleteIndex(const nsAString& aName) nsresult rv = helper->DispatchToTransactionPool(); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - mInfo->indexes.RemoveElementAt(index); + info->indexes.RemoveElementAt(index); for (PRUint32 i = 0; i < mCreatedIndexes.Length(); i++) { if (mCreatedIndexes[i]->Name() == aName) { @@ -1873,90 +1963,67 @@ AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection) NS_PRECONDITION(aConnection, "Passed a null connection!"); nsresult rv; - bool keyUnset = mKey.IsUnset(); + bool mayOverwrite = mOverwrite; + bool unsetKey = mKey.IsUnset(); + + bool autoIncrement = mObjectStore->IsAutoIncrement(); PRInt64 osid = mObjectStore->Id(); const nsString& keyPath = mObjectStore->KeyPath(); - // The "|| keyUnset" here is mostly a debugging tool. If a key isn't - // specified we should never have a collision and so it shouldn't matter - // if we allow overwrite or not. By not allowing overwrite we raise - // detectable errors rather than corrupting data - nsCOMPtr stmt = !mOverwrite || keyUnset ? - mTransaction->GetCachedStatement( - "INSERT INTO object_data (object_store_id, key_value, data, file_ids) " - "VALUES (:osid, :key_value, :data, :file_ids)") : - mTransaction->GetCachedStatement( - "INSERT OR REPLACE INTO object_data (object_store_id, key_value, data, file_ids) " - "VALUES (:osid, :key_value, :data, :file_ids)"); - NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + if (unsetKey) { + NS_ASSERTION(autoIncrement, "Must have a key for non-autoIncrement!"); - mozStorageStatementScoper scoper(stmt); + // Will need to add first and then set the key later. + mayOverwrite = false; + } - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), osid); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + if (autoIncrement && !unsetKey) { + mayOverwrite = true; + } - NS_ASSERTION(!keyUnset || mObjectStore->IsAutoIncrement(), - "Should have key unless autoincrement"); + nsCOMPtr stmt; + if (!mOverwrite && !unsetKey) { + // Make sure the key doesn't exist already + NS_NAMED_LITERAL_CSTRING(id, "id"); + NS_NAMED_LITERAL_CSTRING(osidStr, "osid"); - PRInt64 autoIncrementNum = 0; + nsCString table; + nsCString value; - if (mObjectStore->IsAutoIncrement()) { - if (keyUnset) { - autoIncrementNum = mObjectStore->Info()->nextAutoIncrementId; - if (autoIncrementNum > (1LL << 53)) { - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - mKey.SetFromInteger(autoIncrementNum); + if (autoIncrement) { + table.AssignLiteral("ai_object_data"); + value = id; } - else if (mKey.IsInteger() && - mKey.ToInteger() >= mObjectStore->Info()->nextAutoIncrementId) { - // XXX Once we support floats, we should use floor(mKey.ToFloat()) here - autoIncrementNum = mKey.ToInteger(); + else { + table.AssignLiteral("object_data"); + value.AssignLiteral("key_value"); } - if (keyUnset && !keyPath.IsEmpty()) { - // Special case where someone put an object into an autoIncrement'ing - // objectStore with no key in its keyPath set. We needed to figure out - // which row id we would get above before we could set that properly. + nsCString query = NS_LITERAL_CSTRING("SELECT data FROM ") + table + + NS_LITERAL_CSTRING(" WHERE ") + value + + NS_LITERAL_CSTRING(" = :") + id + + NS_LITERAL_CSTRING(" AND object_store_id = :") + osidStr; - // This is a duplicate of the js engine's byte munging here - union { - jsdouble d; - PRUint64 u; - } pun; - - pun.d = SwapBytes(static_cast(mKey.ToInteger())); + stmt = mTransaction->GetCachedStatement(query); + NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - JSAutoStructuredCloneBuffer& buffer = mCloneWriteInfo.mCloneBuffer; - PRUint64 offsetToKeyProp = mCloneWriteInfo.mOffsetToKeyProp; + mozStorageStatementScoper scoper(stmt); - memcpy((char*)buffer.data() + offsetToKeyProp, &pun.u, sizeof(PRUint64)); + rv = stmt->BindInt64ByName(osidStr, osid); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = mKey.BindToStatement(stmt, NS_LITERAL_CSTRING("id")); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (hasResult) { + return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; } } - mKey.BindToStatement(stmt, NS_LITERAL_CSTRING("key_value")); - - // Compress the bytes before adding into the database. - const char* uncompressed = - reinterpret_cast(mCloneWriteInfo.mCloneBuffer.data()); - size_t uncompressedLength = mCloneWriteInfo.mCloneBuffer.nbytes(); - - size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength); - // This will hold our compressed data until the end of the method. The - // BindBlobByName function will copy it. - nsAutoArrayPtr compressed(new char[compressedLength]); - - snappy::RawCompress(uncompressed, uncompressedLength, compressed.get(), - &compressedLength); - - const PRUint8* dataBuffer = reinterpret_cast(compressed.get()); - size_t dataBufferLength = compressedLength; - - rv = stmt->BindBlobByName(NS_LITERAL_CSTRING("data"), dataBuffer, - dataBufferLength); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - // Handle blobs nsRefPtr fileManager = mDatabase->Manager(); nsCOMPtr directory = fileManager->GetDirectory(); NS_ENSURE_TRUE(directory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); @@ -2015,6 +2082,60 @@ AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection) fileIds.AppendInt(id); } + // Now we add it to the database (or update, depending on our variables). + stmt = mTransaction->AddStatement(true, mayOverwrite, autoIncrement); + NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper(stmt); + + NS_NAMED_LITERAL_CSTRING(keyValue, "key_value"); + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), osid); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (!autoIncrement || mayOverwrite) { + NS_ASSERTION(!mKey.IsUnset(), "This shouldn't happen!"); + + rv = mKey.BindToStatement(stmt, keyValue); + NS_ENSURE_SUCCESS(rv, rv); + } + + NS_NAMED_LITERAL_CSTRING(data, "data"); + + // This will hold our compressed data until the end of the method. The + // BindBlobByName function will copy it. + nsAutoArrayPtr compressed; + + // This points to the compressed buffer. + const PRUint8* dataBuffer = nsnull; + size_t dataBufferLength = 0; + + // If we're going to modify the buffer later to add a key property on an + // autoIncrement objectStore then we will wait to compress our data until we + // have the appropriate key value. + if (autoIncrement && !mOverwrite && !keyPath.IsEmpty() && unsetKey) { + rv = stmt->BindInt32ByName(data, 0); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + else { + // Compress the bytes before adding into the database. + const char* uncompressed = + reinterpret_cast(mCloneWriteInfo.mCloneBuffer.data()); + size_t uncompressedLength = mCloneWriteInfo.mCloneBuffer.nbytes(); + + size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength); + compressed = new char[compressedLength]; + + snappy::RawCompress(uncompressed, uncompressedLength, compressed.get(), + &compressedLength); + + dataBuffer = reinterpret_cast(compressed.get()); + dataBufferLength = compressedLength; + + rv = stmt->BindBlobByName(data, dataBuffer, dataBufferLength); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + if (fileIds.IsEmpty()) { rv = stmt->BindNullByName(NS_LITERAL_CSTRING("file_ids")); } else { @@ -2023,19 +2144,124 @@ AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection) NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); rv = stmt->Execute(); - if (rv == NS_ERROR_STORAGE_CONSTRAINT) { - NS_ASSERTION(!keyUnset, "Generated key had a collision!?"); - return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; - } - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + if (NS_FAILED(rv)) { + if (mayOverwrite && rv == NS_ERROR_STORAGE_CONSTRAINT) { + scoper.Abandon(); - PRInt64 objectDataId; - rv = aConnection->GetLastInsertRowID(&objectDataId); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + rv = stmt->Reset(); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + stmt = mTransaction->AddStatement(false, true, autoIncrement); + NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper2(stmt); + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), osid); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + NS_ASSERTION(!mKey.IsUnset(), "This shouldn't happen!"); + + rv = mKey.BindToStatement(stmt, keyValue); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ASSERTION(dataBuffer && dataBufferLength, "These should be set!"); + + rv = stmt->BindBlobByName(data, dataBuffer, dataBufferLength); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (fileIds.IsEmpty()) { + rv = stmt->BindNullByName(NS_LITERAL_CSTRING("file_ids")); + } else { + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("file_ids"), fileIds); + } + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = stmt->Execute(); + } + + if (NS_FAILED(rv)) { + return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; + } + } + + // If we are supposed to generate a key, get the new id. + if (autoIncrement && !mayOverwrite) { +#ifdef DEBUG + PRInt64 oldKey = unsetKey ? 0 : mKey.ToInteger(); +#endif + + PRInt64 newIntKey; + rv = aConnection->GetLastInsertRowID(&newIntKey); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = mKey.SetFromInteger(newIntKey); + NS_ENSURE_SUCCESS(rv, rv); + +#ifdef DEBUG + NS_ASSERTION(mKey.IsInteger(), "Bad key value!"); + if (!unsetKey) { + NS_ASSERTION(mKey.ToInteger() == oldKey, "Something went haywire!"); + } +#endif + + if (!keyPath.IsEmpty() && unsetKey) { + // Special case where someone put an object into an autoIncrement'ing + // objectStore with no key in its keyPath set. We needed to figure out + // which row id we would get above before we could set that properly. + rv = mObjectStore->ModifyValueForNewKey(mCloneWriteInfo, mKey); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + scoper.Abandon(); + rv = stmt->Reset(); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + stmt = mTransaction->AddStatement(false, true, true); + NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper2(stmt); + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), osid); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = mKey.BindToStatement(stmt, keyValue); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ASSERTION(!dataBuffer && !dataBufferLength, "These should be unset!"); + + const char* uncompressed = + reinterpret_cast(mCloneWriteInfo.mCloneBuffer.data()); + size_t uncompressedLength = mCloneWriteInfo.mCloneBuffer.nbytes(); + + size_t compressedLength = + snappy::MaxCompressedLength(uncompressedLength); + compressed = new char[compressedLength]; + + snappy::RawCompress(uncompressed, uncompressedLength, compressed.get(), + &compressedLength); + + dataBuffer = reinterpret_cast(compressed.get()); + dataBufferLength = compressedLength; + + rv = stmt->BindBlobByName(data, dataBuffer, dataBufferLength); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (fileIds.IsEmpty()) { + rv = stmt->BindNullByName(NS_LITERAL_CSTRING("file_ids")); + } else { + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("file_ids"), fileIds); + } + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = stmt->Execute(); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + } // Update our indexes if needed. if (mOverwrite || !mIndexUpdateInfo.IsEmpty()) { - rv = IDBObjectStore::UpdateIndexes(mTransaction, osid, mKey, mOverwrite, + PRInt64 objectDataId = autoIncrement ? mKey.ToInteger() : LL_MININT; + rv = IDBObjectStore::UpdateIndexes(mTransaction, osid, mKey, + autoIncrement, mOverwrite, objectDataId, mIndexUpdateInfo); if (rv == NS_ERROR_STORAGE_CONSTRAINT) { return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; @@ -2043,10 +2269,6 @@ AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection) NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); } - if (autoIncrementNum) { - mObjectStore->Info()->nextAutoIncrementId = autoIncrementNum + 1; - } - return NS_OK; } @@ -2066,13 +2288,26 @@ GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) { NS_ASSERTION(mKeyRange, "Must have a key range here!"); + nsCString table; + nsCString value; + if (mObjectStore->IsAutoIncrement()) { + table.AssignLiteral("ai_object_data"); + value.AssignLiteral("id"); + } + else { + table.AssignLiteral("object_data"); + value.AssignLiteral("key_value"); + } + nsCString keyRangeClause; - mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("key_value"), keyRangeClause); + mKeyRange->GetBindingClause(value, keyRangeClause); NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!"); - nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM object_data " - "WHERE object_store_id = :osid") + + NS_NAMED_LITERAL_CSTRING(osid, "osid"); + + nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM ") + table + + NS_LITERAL_CSTRING(" WHERE object_store_id = :") + osid + keyRangeClause + NS_LITERAL_CSTRING(" LIMIT 1"); nsCOMPtr stmt = mTransaction->GetCachedStatement(query); @@ -2080,7 +2315,7 @@ GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) mozStorageStatementScoper scoper(stmt); - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mObjectStore->Id()); + nsresult rv = stmt->BindInt64ByName(osid, mObjectStore->Id()); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); rv = mKeyRange->BindToStatement(stmt); @@ -2116,13 +2351,26 @@ DeleteHelper::DoDatabaseWork(mozIStorageConnection* /*aConnection */) { NS_ASSERTION(mKeyRange, "Must have a key range here!"); + nsCString table; + nsCString value; + if (mObjectStore->IsAutoIncrement()) { + table.AssignLiteral("ai_object_data"); + value.AssignLiteral("id"); + } + else { + table.AssignLiteral("object_data"); + value.AssignLiteral("key_value"); + } + nsCString keyRangeClause; - mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("key_value"), keyRangeClause); + mKeyRange->GetBindingClause(value, keyRangeClause); NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!"); - nsCString query = NS_LITERAL_CSTRING("DELETE FROM object_data " - "WHERE object_store_id = :osid") + + NS_NAMED_LITERAL_CSTRING(osid, "osid"); + + nsCString query = NS_LITERAL_CSTRING("DELETE FROM ") + table + + NS_LITERAL_CSTRING(" WHERE object_store_id = :") + osid + keyRangeClause; nsCOMPtr stmt = mTransaction->GetCachedStatement(query); @@ -2130,8 +2378,7 @@ DeleteHelper::DoDatabaseWork(mozIStorageConnection* /*aConnection */) mozStorageStatementScoper scoper(stmt); - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), - mObjectStore->Id()); + nsresult rv = stmt->BindInt64ByName(osid, mObjectStore->Id()); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); rv = mKeyRange->BindToStatement(stmt); @@ -2156,10 +2403,18 @@ ClearHelper::DoDatabaseWork(mozIStorageConnection* aConnection) { NS_PRECONDITION(aConnection, "Passed a null connection!"); - nsCOMPtr stmt = - mTransaction->GetCachedStatement( - NS_LITERAL_CSTRING("DELETE FROM object_data " - "WHERE object_store_id = :osid")); + nsCString table; + if (mObjectStore->IsAutoIncrement()) { + table.AssignLiteral("ai_object_data"); + } + else { + table.AssignLiteral("object_data"); + } + + nsCString query = NS_LITERAL_CSTRING("DELETE FROM ") + table + + NS_LITERAL_CSTRING(" WHERE object_store_id = :osid"); + + nsCOMPtr stmt = mTransaction->GetCachedStatement(query); NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); mozStorageStatementScoper scoper(stmt); @@ -2177,33 +2432,45 @@ ClearHelper::DoDatabaseWork(mozIStorageConnection* aConnection) nsresult OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) { - NS_NAMED_LITERAL_CSTRING(keyValue, "key_value"); + nsCString table; + nsCString keyColumn; + + if (mObjectStore->IsAutoIncrement()) { + table.AssignLiteral("ai_object_data"); + keyColumn.AssignLiteral("id"); + } + else { + table.AssignLiteral("object_data"); + keyColumn.AssignLiteral("key_value"); + } + + NS_NAMED_LITERAL_CSTRING(id, "id"); nsCString keyRangeClause; if (mKeyRange) { - mKeyRange->GetBindingClause(keyValue, keyRangeClause); + mKeyRange->GetBindingClause(keyColumn, keyRangeClause); } - nsCAutoString directionClause; + nsCAutoString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + keyColumn; switch (mDirection) { case nsIIDBCursor::NEXT: case nsIIDBCursor::NEXT_NO_DUPLICATE: - directionClause.AssignLiteral(" ORDER BY key_value ASC"); + directionClause += NS_LITERAL_CSTRING(" ASC"); break; case nsIIDBCursor::PREV: case nsIIDBCursor::PREV_NO_DUPLICATE: - directionClause.AssignLiteral(" ORDER BY key_value DESC"); + directionClause += NS_LITERAL_CSTRING(" DESC"); break; default: NS_NOTREACHED("Unknown direction type!"); } - nsCString firstQuery = NS_LITERAL_CSTRING("SELECT key_value, data, file_ids " - "FROM object_data " - "WHERE object_store_id = :id") + - keyRangeClause + directionClause + + nsCString firstQuery = NS_LITERAL_CSTRING("SELECT ") + keyColumn + + NS_LITERAL_CSTRING(", data, file_ids FROM ") + table + + NS_LITERAL_CSTRING(" WHERE object_store_id = :") + + id + keyRangeClause + directionClause + NS_LITERAL_CSTRING(" LIMIT 1"); nsCOMPtr stmt = @@ -2212,8 +2479,7 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) mozStorageStatementScoper scoper(stmt); - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), - mObjectStore->Id()); + nsresult rv = stmt->BindInt64ByName(id, mObjectStore->Id()); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); if (mKeyRange) { @@ -2247,14 +2513,14 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) switch (mDirection) { case nsIIDBCursor::NEXT: case nsIIDBCursor::NEXT_NO_DUPLICATE: - AppendConditionClause(keyValue, currentKey, false, false, + AppendConditionClause(keyColumn, currentKey, false, false, keyRangeClause); - AppendConditionClause(keyValue, currentKey, false, true, + AppendConditionClause(keyColumn, currentKey, false, true, continueToKeyRangeClause); if (mKeyRange && !mKeyRange->Upper().IsUnset()) { - AppendConditionClause(keyValue, rangeKey, true, + AppendConditionClause(keyColumn, rangeKey, true, !mKeyRange->IsUpperOpen(), keyRangeClause); - AppendConditionClause(keyValue, rangeKey, true, + AppendConditionClause(keyColumn, rangeKey, true, !mKeyRange->IsUpperOpen(), continueToKeyRangeClause); mRangeKey = mKeyRange->Upper(); @@ -2263,13 +2529,13 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) case nsIIDBCursor::PREV: case nsIIDBCursor::PREV_NO_DUPLICATE: - AppendConditionClause(keyValue, currentKey, true, false, keyRangeClause); - AppendConditionClause(keyValue, currentKey, true, true, + AppendConditionClause(keyColumn, currentKey, true, false, keyRangeClause); + AppendConditionClause(keyColumn, currentKey, true, true, continueToKeyRangeClause); if (mKeyRange && !mKeyRange->Lower().IsUnset()) { - AppendConditionClause(keyValue, rangeKey, false, + AppendConditionClause(keyColumn, rangeKey, false, !mKeyRange->IsLowerOpen(), keyRangeClause); - AppendConditionClause(keyValue, rangeKey, false, + AppendConditionClause(keyColumn, rangeKey, false, !mKeyRange->IsLowerOpen(), continueToKeyRangeClause); mRangeKey = mKeyRange->Lower(); @@ -2280,14 +2546,16 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) NS_NOTREACHED("Unknown direction type!"); } - NS_NAMED_LITERAL_CSTRING(queryStart, "SELECT key_value, data, file_ids " - "FROM object_data " - "WHERE object_store_id = :id"); - - mContinueQuery = queryStart + keyRangeClause + directionClause + + mContinueQuery = NS_LITERAL_CSTRING("SELECT ") + keyColumn + + NS_LITERAL_CSTRING(", data, file_ids FROM ") + table + + NS_LITERAL_CSTRING(" WHERE object_store_id = :") + id + + keyRangeClause + directionClause + NS_LITERAL_CSTRING(" LIMIT "); - mContinueToQuery = queryStart + continueToKeyRangeClause + directionClause + + mContinueToQuery = NS_LITERAL_CSTRING("SELECT ") + keyColumn + + NS_LITERAL_CSTRING(", data, file_ids FROM ") + table + + NS_LITERAL_CSTRING(" WHERE object_store_id = :") + id + + continueToKeyRangeClause + directionClause + NS_LITERAL_CSTRING(" LIMIT "); return NS_OK; @@ -2407,8 +2675,9 @@ CreateIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection) nsCOMPtr stmt = mTransaction->GetCachedStatement( "INSERT INTO object_store_index (id, name, key_path, unique_index, " - "multientry, object_store_id) " - "VALUES (:id, :name, :key_path, :unique, :multientry, :osid)" + "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); @@ -2437,6 +2706,10 @@ CreateIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection) mIndex->ObjectStore()->Id()); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("os_auto_increment"), + mIndex->IsAutoIncrement() ? 1 : 0); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + if (NS_FAILED(stmt->Execute())) { return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; } @@ -2461,10 +2734,22 @@ CreateIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection) nsresult CreateIndexHelper::InsertDataFromObjectStore(mozIStorageConnection* aConnection) { - nsCOMPtr stmt = - mTransaction->GetCachedStatement( - NS_LITERAL_CSTRING("SELECT id, data, file_ids, key_value FROM object_data " - "WHERE object_store_id = :osid")); + nsCAutoString table; + nsCAutoString columns; + if (mIndex->IsAutoIncrement()) { + table.AssignLiteral("ai_object_data"); + columns.AssignLiteral("id, data, file_ids"); + } + else { + table.AssignLiteral("object_data"); + columns.AssignLiteral("id, data, file_ids, key_value"); + } + + nsCString query = NS_LITERAL_CSTRING("SELECT ") + columns + + NS_LITERAL_CSTRING(" FROM ") + table + + NS_LITERAL_CSTRING(" WHERE object_store_id = :osid"); + + nsCOMPtr stmt = mTransaction->GetCachedStatement(query); NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); mozStorageStatementScoper scoper(stmt); @@ -2528,11 +2813,17 @@ CreateIndexHelper::InsertDataFromObjectStore(mozIStorageConnection* aConnection) PRInt64 objectDataID = stmt->AsInt64(0); Key key; - rv = key.SetFromStatement(stmt, 3); - NS_ENSURE_SUCCESS(rv, rv); + if (!mIndex->IsAutoIncrement()) { + rv = key.SetFromStatement(stmt, 3); + NS_ENSURE_SUCCESS(rv, rv); + } + else { + key.SetFromInteger(objectDataID); + } rv = IDBObjectStore::UpdateIndexes(mTransaction, mIndex->Id(), - key, false, objectDataID, updateInfo); + key, mIndex->IsAutoIncrement(), + false, objectDataID, updateInfo); NS_ENSURE_SUCCESS(rv, rv); } while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&hasResult)) && hasResult); @@ -2568,13 +2859,26 @@ DeleteIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection) nsresult GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection) { + nsCString table; + nsCString keyColumn; + + if (mObjectStore->IsAutoIncrement()) { + table.AssignLiteral("ai_object_data"); + keyColumn.AssignLiteral("id"); + } + else { + table.AssignLiteral("object_data"); + keyColumn.AssignLiteral("key_value"); + } + + NS_NAMED_LITERAL_CSTRING(osid, "osid"); NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key"); NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key"); nsCAutoString keyRangeClause; if (mKeyRange) { if (!mKeyRange->Lower().IsUnset()) { - keyRangeClause = NS_LITERAL_CSTRING(" AND key_value"); + keyRangeClause = NS_LITERAL_CSTRING(" AND ") + keyColumn; if (mKeyRange->IsLowerOpen()) { keyRangeClause.AppendLiteral(" > :"); } @@ -2585,7 +2889,7 @@ GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection) } if (!mKeyRange->Upper().IsUnset()) { - keyRangeClause += NS_LITERAL_CSTRING(" AND key_value"); + keyRangeClause += NS_LITERAL_CSTRING(" AND ") + keyColumn; if (mKeyRange->IsUpperOpen()) { keyRangeClause.AppendLiteral(" < :"); } @@ -2602,11 +2906,10 @@ GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection) limitClause.AppendInt(mLimit); } - nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM object_data " - "WHERE object_store_id = :osid") + - keyRangeClause + - NS_LITERAL_CSTRING(" ORDER BY key_value ASC") + - limitClause; + nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM ") + table + + NS_LITERAL_CSTRING(" WHERE object_store_id = :") + osid + + keyRangeClause + NS_LITERAL_CSTRING(" ORDER BY ") + + keyColumn + NS_LITERAL_CSTRING(" ASC") + limitClause; mCloneReadInfos.SetCapacity(50); @@ -2615,8 +2918,7 @@ GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection) mozStorageStatementScoper scoper(stmt); - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), - mObjectStore->Id()); + nsresult rv = stmt->BindInt64ByName(osid, mObjectStore->Id()); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); if (mKeyRange) { @@ -2670,13 +2972,26 @@ GetAllHelper::GetSuccessResult(JSContext* aCx, nsresult CountHelper::DoDatabaseWork(mozIStorageConnection* aConnection) { + nsCString table; + nsCString keyColumn; + + if (mObjectStore->IsAutoIncrement()) { + table.AssignLiteral("ai_object_data"); + keyColumn.AssignLiteral("id"); + } + else { + table.AssignLiteral("object_data"); + keyColumn.AssignLiteral("key_value"); + } + + NS_NAMED_LITERAL_CSTRING(osid, "osid"); NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key"); NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key"); nsCAutoString keyRangeClause; if (mKeyRange) { if (!mKeyRange->Lower().IsUnset()) { - keyRangeClause = NS_LITERAL_CSTRING(" AND key_value"); + keyRangeClause = NS_LITERAL_CSTRING(" AND ") + keyColumn; if (mKeyRange->IsLowerOpen()) { keyRangeClause.AppendLiteral(" > :"); } @@ -2687,7 +3002,7 @@ CountHelper::DoDatabaseWork(mozIStorageConnection* aConnection) } if (!mKeyRange->Upper().IsUnset()) { - keyRangeClause += NS_LITERAL_CSTRING(" AND key_value"); + keyRangeClause += NS_LITERAL_CSTRING(" AND ") + keyColumn; if (mKeyRange->IsUpperOpen()) { keyRangeClause.AppendLiteral(" < :"); } @@ -2698,8 +3013,8 @@ CountHelper::DoDatabaseWork(mozIStorageConnection* aConnection) } } - nsCString query = NS_LITERAL_CSTRING("SELECT count(*) FROM object_data " - "WHERE object_store_id = :osid") + + nsCString query = NS_LITERAL_CSTRING("SELECT count(*) FROM ") + table + + NS_LITERAL_CSTRING(" WHERE object_store_id = :") + osid + keyRangeClause; nsCOMPtr stmt = mTransaction->GetCachedStatement(query); @@ -2707,8 +3022,7 @@ CountHelper::DoDatabaseWork(mozIStorageConnection* aConnection) mozStorageStatementScoper scoper(stmt); - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), - mObjectStore->Id()); + nsresult rv = stmt->BindInt64ByName(osid, mObjectStore->Id()); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); if (mKeyRange) { diff --git a/dom/indexedDB/IDBObjectStore.h b/dom/indexedDB/IDBObjectStore.h index c8fc9f714f0a..f36f18e56dd8 100644 --- a/dom/indexedDB/IDBObjectStore.h +++ b/dom/indexedDB/IDBObjectStore.h @@ -72,8 +72,7 @@ public: static already_AddRefed Create(IDBTransaction* aTransaction, - ObjectStoreInfo* aInfo, - nsIAtom* aDatabaseId); + const ObjectStoreInfo* aInfo); static bool IsValidKeyPath(JSContext* aCx, const nsAString& aKeyPath); @@ -91,6 +90,7 @@ public: UpdateIndexes(IDBTransaction* aTransaction, PRInt64 aObjectStoreId, const Key& aObjectStoreKey, + bool aAutoIncrement, bool aOverwrite, PRInt64 aObjectDataId, const nsTArray& aUpdateInfoArray); @@ -167,10 +167,8 @@ public: return mTransaction; } - ObjectStoreInfo* Info() - { - return mInfo; - } + nsresult ModifyValueForNewKey(StructuredCloneWriteInfo& aCloneWriteInfo, + Key& aKey); protected: IDBObjectStore(); @@ -201,7 +199,6 @@ private: nsString mKeyPath; bool mAutoIncrement; nsCOMPtr mDatabaseId; - nsRefPtr mInfo; PRUint32 mStructuredCloneVersion; nsTArray > mCreatedIndexes; diff --git a/dom/indexedDB/IDBTransaction.cpp b/dom/indexedDB/IDBTransaction.cpp index ce3a0b5d17f1..1b9e18bcc070 100644 --- a/dom/indexedDB/IDBTransaction.cpp +++ b/dom/indexedDB/IDBTransaction.cpp @@ -121,8 +121,6 @@ IDBTransaction::Create(IDBDatabase* aDatabase, transaction->mDatabase = aDatabase; transaction->mMode = aMode; - - transaction->mDatabaseInfo = aDatabase->Info(); if (!transaction->mObjectStoreNames.AppendElements(aObjectStoreNames)) { NS_ERROR("Out of memory!"); @@ -213,13 +211,8 @@ IDBTransaction::OnRequestFinished() } void -IDBTransaction::RemoveObjectStore(const nsAString& aName) +IDBTransaction::ReleaseCachedObjectStore(const nsAString& aName) { - NS_ASSERTION(mMode == nsIIDBTransaction::VERSION_CHANGE, - "Only remove object stores on VERSION_CHANGE transactions"); - - mDatabaseInfo->RemoveObjectStore(aName); - for (PRUint32 i = 0; i < mCreatedObjectStores.Length(); i++) { if (mCreatedObjectStores[i]->Name() == aName) { mCreatedObjectStores.RemoveElementAt(i); @@ -244,8 +237,7 @@ IDBTransaction::CommitOrRollback() TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate(); NS_ENSURE_STATE(pool); - nsRefPtr helper(new CommitHelper(this, mListener, - mCreatedObjectStores)); + nsRefPtr helper(new CommitHelper(this, mListener)); mCachedStatements.Enumerate(DoomCachedStatements, helper); NS_ASSERTION(!mCachedStatements.Count(), "Statements left!"); @@ -371,6 +363,119 @@ IDBTransaction::GetOrCreateConnection(mozIStorageConnection** aResult) return NS_OK; } +already_AddRefed +IDBTransaction::AddStatement(bool aCreate, + bool aOverwrite, + bool aAutoIncrement) +{ +#ifdef DEBUG + if (!aCreate) { + NS_ASSERTION(aOverwrite, "Bad param combo!"); + } +#endif + + if (aAutoIncrement) { + if (aCreate) { + if (aOverwrite) { + return GetCachedStatement( + "INSERT OR FAIL INTO ai_object_data (object_store_id, id, data, " + "file_ids) " + "VALUES (:osid, :key_value, :data, :file_ids)" + ); + } + return GetCachedStatement( + "INSERT INTO ai_object_data (object_store_id, data, file_ids) " + "VALUES (:osid, :data, :file_ids)" + ); + } + return GetCachedStatement( + "UPDATE ai_object_data " + "SET data = :data, file_ids = :file_ids " + "WHERE object_store_id = :osid " + "AND id = :key_value" + ); + } + if (aCreate) { + if (aOverwrite) { + return GetCachedStatement( + "INSERT OR FAIL INTO object_data (object_store_id, key_value, data, " + "file_ids) " + "VALUES (:osid, :key_value, :data, :file_ids)" + ); + } + return GetCachedStatement( + "INSERT INTO object_data (object_store_id, key_value, data, file_ids) " + "VALUES (:osid, :key_value, :data, :file_ids)" + ); + } + return GetCachedStatement( + "UPDATE object_data " + "SET data = :data, file_ids = :file_ids " + "WHERE object_store_id = :osid " + "AND key_value = :key_value" + ); +} + +already_AddRefed +IDBTransaction::IndexDataInsertStatement(bool aAutoIncrement, + bool aUnique) +{ + if (aAutoIncrement) { + if (aUnique) { + return GetCachedStatement( + "INSERT INTO ai_unique_index_data " + "(index_id, ai_object_data_id, value) " + "VALUES (:index_id, :object_data_id, :value)" + ); + } + return GetCachedStatement( + "INSERT OR IGNORE INTO ai_index_data " + "(index_id, ai_object_data_id, value) " + "VALUES (:index_id, :object_data_id, :value)" + ); + } + if (aUnique) { + return GetCachedStatement( + "INSERT INTO unique_index_data " + "(index_id, object_data_id, object_data_key, value) " + "VALUES (:index_id, :object_data_id, :object_data_key, :value)" + ); + } + return GetCachedStatement( + "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)" + ); +} + +already_AddRefed +IDBTransaction::IndexDataDeleteStatement(bool aAutoIncrement, + bool aUnique) +{ + if (aAutoIncrement) { + if (aUnique) { + return GetCachedStatement( + "DELETE FROM ai_unique_index_data " + "WHERE ai_object_data_id = :object_data_id" + ); + } + return GetCachedStatement( + "DELETE FROM ai_index_data " + "WHERE ai_object_data_id = :object_data_id" + ); + } + if (aUnique) { + return GetCachedStatement( + "DELETE FROM unique_index_data " + "WHERE object_data_id = :object_data_id" + ); + } + return GetCachedStatement( + "DELETE FROM index_data " + "WHERE object_data_id = :object_data_id" + ); +} + already_AddRefed IDBTransaction::GetCachedStatement(const nsACString& aQuery) { @@ -452,9 +557,13 @@ IDBTransaction::GetOrCreateObjectStore(const nsAString& aName, } } - retval = IDBObjectStore::Create(this, aObjectStoreInfo, mDatabaseInfo->id); + retval = IDBObjectStore::Create(this, aObjectStoreInfo); + NS_ENSURE_TRUE(retval, nsnull); - mCreatedObjectStores.AppendElement(retval); + if (!mCreatedObjectStores.AppendElement(retval)) { + NS_WARNING("Out of memory!"); + return nsnull; + } return retval.forget(); } @@ -549,7 +658,12 @@ IDBTransaction::GetObjectStoreNames(nsIDOMDOMStringList** aObjectStores) nsTArray* arrayOfNames; if (mMode == IDBTransaction::VERSION_CHANGE) { - mDatabaseInfo->GetObjectStoreNames(stackArray); + DatabaseInfo* info = mDatabase->Info(); + + if (!info->GetObjectStoreNames(stackArray)) { + NS_ERROR("Out of memory!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } arrayOfNames = &stackArray; } @@ -581,7 +695,7 @@ IDBTransaction::ObjectStore(const nsAString& aName, if (mMode == nsIIDBTransaction::VERSION_CHANGE || mObjectStoreNames.Contains(aName)) { - info = mDatabaseInfo->GetObjectStore(aName); + mDatabase->Info()->GetObjectStore(aName, &info); } if (!info) { @@ -733,23 +847,15 @@ IDBTransaction::AfterProcessNextEvent(nsIThreadInternal* aThread, return NS_OK; } -CommitHelper::CommitHelper( - IDBTransaction* aTransaction, - IDBTransactionListener* aListener, - const nsTArray >& aUpdatedObjectStores) +CommitHelper::CommitHelper(IDBTransaction* aTransaction, + IDBTransactionListener* aListener) : mTransaction(aTransaction), mListener(aListener), - mAborted(!!aTransaction->mAborted) + mAborted(!!aTransaction->mAborted), + mHaveMetadata(false) { mConnection.swap(aTransaction->mConnection); mUpdateFileRefcountFunction.swap(aTransaction->mUpdateFileRefcountFunction); - - for (PRUint32 i = 0; i < aUpdatedObjectStores.Length(); i++) { - ObjectStoreInfo* info = aUpdatedObjectStores[i]->Info(); - if (info->comittedAutoIncrementId != info->nextAutoIncrementId) { - mAutoIncrementObjectStores.AppendElement(aUpdatedObjectStores[i]); - } - } } CommitHelper::~CommitHelper() @@ -776,11 +882,19 @@ CommitHelper::Run() nsCOMPtr event; if (mAborted) { - if (mTransaction->Mode() == nsIIDBTransaction::VERSION_CHANGE) { - // This will make the database take a snapshot of it's DatabaseInfo - mTransaction->Database()->Close(); - // Then remove the info from the hash as it contains invalid data. - DatabaseInfo::Remove(mTransaction->Database()->Id()); + if (mHaveMetadata) { + NS_ASSERTION(mTransaction->Mode() == nsIIDBTransaction::VERSION_CHANGE, + "Bad transaction type!"); + + DatabaseInfo* dbInfo = mTransaction->Database()->Info(); + + if (NS_FAILED(IDBFactory::UpdateDatabaseMetadata(dbInfo, mOldVersion, + mOldObjectStores))) { + NS_WARNING("Failed to update database metadata!"); + } + else { + NS_ASSERTION(mOldObjectStores.IsEmpty(), "Should have swapped!"); + } } event = CreateGenericEvent(NS_LITERAL_STRING(ABORT_EVT_STR), @@ -824,17 +938,12 @@ CommitHelper::Run() mAborted = true; } - if (!mAborted && NS_FAILED(WriteAutoIncrementCounts())) { - mAborted = true; - } - if (!mAborted) { NS_NAMED_LITERAL_CSTRING(release, "COMMIT TRANSACTION"); if (NS_SUCCEEDED(mConnection->ExecuteSimpleSQL(release))) { if (mUpdateFileRefcountFunction) { mUpdateFileRefcountFunction->UpdateFileInfos(); } - CommitAutoIncrementCounts(); } else { mAborted = true; @@ -842,11 +951,23 @@ CommitHelper::Run() } if (mAborted) { - RevertAutoIncrementCounts(); NS_NAMED_LITERAL_CSTRING(rollback, "ROLLBACK TRANSACTION"); if (NS_FAILED(mConnection->ExecuteSimpleSQL(rollback))) { NS_WARNING("Failed to rollback transaction!"); } + + if (mTransaction->Mode() == nsIIDBTransaction::VERSION_CHANGE) { + nsresult rv = + IDBFactory::LoadDatabaseInformation(mConnection, + mTransaction->Database()->Id(), + &mOldVersion, mOldObjectStores); + if (NS_SUCCEEDED(rv)) { + mHaveMetadata = true; + } + else { + NS_WARNING("Failed to get database information!"); + } + } } } @@ -870,55 +991,6 @@ CommitHelper::Run() return NS_OK; } -nsresult -CommitHelper::WriteAutoIncrementCounts() -{ - nsCOMPtr stmt; - nsresult rv; - for (PRUint32 i = 0; i < mAutoIncrementObjectStores.Length(); i++) { - ObjectStoreInfo* info = mAutoIncrementObjectStores[i]->Info(); - if (!stmt) { - rv = mConnection->CreateStatement(NS_LITERAL_CSTRING( - "UPDATE object_store SET auto_increment = :ai " - "WHERE id = :osid;"), getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - } - else { - stmt->Reset(); - } - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), info->id); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("ai"), - info->nextAutoIncrementId); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -} - -void -CommitHelper::CommitAutoIncrementCounts() -{ - for (PRUint32 i = 0; i < mAutoIncrementObjectStores.Length(); i++) { - ObjectStoreInfo* info = mAutoIncrementObjectStores[i]->Info(); - info->comittedAutoIncrementId = info->nextAutoIncrementId; - } -} - -void -CommitHelper::RevertAutoIncrementCounts() -{ - for (PRUint32 i = 0; i < mAutoIncrementObjectStores.Length(); i++) { - ObjectStoreInfo* info = mAutoIncrementObjectStores[i]->Info(); - info->nextAutoIncrementId = info->comittedAutoIncrementId; - } -} - nsresult UpdateRefcountFunction::Init() { diff --git a/dom/indexedDB/IDBTransaction.h b/dom/indexedDB/IDBTransaction.h index 4d6cdfff7250..329102debfbc 100644 --- a/dom/indexedDB/IDBTransaction.h +++ b/dom/indexedDB/IDBTransaction.h @@ -107,7 +107,7 @@ public: void OnNewRequest(); void OnRequestFinished(); - void RemoveObjectStore(const nsAString& aName); + void ReleaseCachedObjectStore(const nsAString& aName); void SetTransactionListener(IDBTransactionListener* aListener); @@ -118,6 +118,19 @@ public: // Only meant to be called on mStorageThread! nsresult GetOrCreateConnection(mozIStorageConnection** aConnection); + already_AddRefed + AddStatement(bool aCreate, + bool aOverwrite, + bool aAutoIncrement); + + already_AddRefed + IndexDataInsertStatement(bool aAutoIncrement, + bool aUnique); + + already_AddRefed + IndexDataDeleteStatement(bool aAutoIncrement, + bool aUnique); + already_AddRefed GetCachedStatement(const nsACString& aQuery); @@ -125,7 +138,9 @@ public: already_AddRefed GetCachedStatement(const char (&aQuery)[N]) { - return GetCachedStatement(NS_LITERAL_CSTRING(aQuery)); + nsCString query; + query.AssignLiteral(aQuery); + return GetCachedStatement(query); } bool IsOpen() const; @@ -152,11 +167,6 @@ public: return mDatabase; } - DatabaseInfo* DBInfo() const - { - return mDatabaseInfo; - } - already_AddRefed GetOrCreateObjectStore(const nsAString& aName, ObjectStoreInfo* aObjectStoreInfo); @@ -172,7 +182,6 @@ private: nsresult CommitOrRollback(); nsRefPtr mDatabase; - nsRefPtr mDatabaseInfo; nsTArray mObjectStoreNames; PRUint16 mReadyState; PRUint16 mMode; @@ -215,8 +224,7 @@ public: NS_DECL_NSIRUNNABLE CommitHelper(IDBTransaction* aTransaction, - IDBTransactionListener* aListener, - const nsTArray >& mUpdatedObjectStores); + IDBTransactionListener* aListener); ~CommitHelper(); template @@ -233,23 +241,17 @@ public: } private: - // Writes new autoincrement counts to database - nsresult WriteAutoIncrementCounts(); - - // Updates counts after a successful commit - void CommitAutoIncrementCounts(); - - // Reverts counts when a transaction is aborted - void RevertAutoIncrementCounts(); - nsRefPtr mTransaction; nsRefPtr mListener; nsCOMPtr mConnection; nsRefPtr mUpdateFileRefcountFunction; nsAutoTArray, 10> mDoomedObjects; - nsAutoTArray, 10> mAutoIncrementObjectStores; + + PRUint64 mOldVersion; + nsTArray > mOldObjectStores; bool mAborted; + bool mHaveMetadata; }; class UpdateRefcountFunction : public mozIStorageFunction diff --git a/dom/indexedDB/IndexedDatabaseManager.cpp b/dom/indexedDB/IndexedDatabaseManager.cpp index a46c8f9873e6..25156a610bf3 100644 --- a/dom/indexedDB/IndexedDatabaseManager.cpp +++ b/dom/indexedDB/IndexedDatabaseManager.cpp @@ -38,7 +38,6 @@ * ***** END LICENSE BLOCK ***** */ #include "IndexedDatabaseManager.h" -#include "DatabaseInfo.h" #include "nsIFile.h" #include "nsIObserverService.h" @@ -80,6 +79,9 @@ // Preference that users can set to override DEFAULT_QUOTA_MB #define PREF_INDEXEDDB_QUOTA "dom.indexedDB.warningQuota" +// A bad TLS index number. +#define BAD_TLS_INDEX (PRUintn)-1 + USING_INDEXEDDB_NAMESPACE using namespace mozilla::services; using mozilla::Preferences; @@ -1181,8 +1183,6 @@ IndexedDatabaseManager::ClearDatabasesForURI(nsIURI* aURI) for (PRUint32 index = 0; index < liveDatabases.Length(); index++) { liveDatabases[index]->Invalidate(); } - - DatabaseInfo::RemoveAllForOrigin(origin); // After everything has been invalidated the helper should be dispatched to // the end of the event queue. diff --git a/dom/indexedDB/Key.h b/dom/indexedDB/Key.h index c39f2b20c198..e6476ed56314 100644 --- a/dom/indexedDB/Key.h +++ b/dom/indexedDB/Key.h @@ -238,10 +238,12 @@ public: if (IsString()) { rv = aStatement->BindStringByName(aParamName, ToString()); } - else { - NS_ASSERTION(IsInteger(), "Bad key!"); + else if (IsInteger()) { rv = aStatement->BindInt64ByName(aParamName, ToInteger()); } + else { + NS_NOTREACHED("Bad key!"); + } return NS_SUCCEEDED(rv) ? NS_OK : NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } diff --git a/dom/indexedDB/OpenDatabaseHelper.cpp b/dom/indexedDB/OpenDatabaseHelper.cpp index 0f17c583609e..b377cd212b53 100644 --- a/dom/indexedDB/OpenDatabaseHelper.cpp +++ b/dom/indexedDB/OpenDatabaseHelper.cpp @@ -60,7 +60,7 @@ namespace { PR_STATIC_ASSERT(JS_STRUCTURED_CLONE_VERSION == 1); // Major schema version. Bump for almost everything. -const PRUint32 kMajorSchemaVersion = 11; +const PRUint32 kMajorSchemaVersion = 10; // Minor schema version. Should almost always be 0 (maybe bump on release // branches if we have to). @@ -169,6 +169,38 @@ CreateFileTables(mozIStorageConnection* aDBConn) )); NS_ENSURE_SUCCESS(rv, rv); + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER ai_object_data_insert_trigger " + "AFTER INSERT ON ai_object_data " + "FOR EACH ROW " + "WHEN NEW.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(NULL, NEW.file_ids); " + "END;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER ai_object_data_update_trigger " + "AFTER UPDATE OF file_ids ON ai_object_data " + "FOR EACH ROW " + "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(OLD.file_ids, NEW.file_ids); " + "END;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER ai_object_data_delete_trigger " + "AFTER DELETE ON ai_object_data " + "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(OLD.file_ids, NULL); " + "END;" + )); + NS_ENSURE_SUCCESS(rv, rv); + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( "CREATE TRIGGER file_update_trigger " "AFTER UPDATE ON file " @@ -225,15 +257,31 @@ CreateTables(mozIStorageConnection* aDBConn) )); NS_ENSURE_SUCCESS(rv, rv); + // Table `ai_object_data` + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE ai_object_data (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "object_store_id INTEGER NOT NULL, " + "data BLOB NOT NULL, " + "file_ids TEXT, " + "UNIQUE (object_store_id, id), " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + // Table `index` rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( "CREATE TABLE object_store_index (" - "id INTEGER PRIMARY KEY, " + "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, " + "multientry INTEGER NOT NULL DEFAULT 0, " + "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" @@ -288,6 +336,53 @@ CreateTables(mozIStorageConnection* aDBConn) )); NS_ENSURE_SUCCESS(rv, rv); + // Table `ai_index_data` + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE ai_index_data (" + "index_id INTEGER NOT NULL, " + "value NOT NULL, " + "ai_object_data_id INTEGER NOT NULL, " + "PRIMARY KEY (index_id, value, ai_object_data_id), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE, " + "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Need this to make cascading deletes from ai_object_data and object_store + // fast. + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX ai_index_data_ai_object_data_id_index " + "ON ai_index_data (ai_object_data_id);" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Table `ai_unique_index_data` + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE ai_unique_index_data (" + "index_id INTEGER NOT NULL, " + "value NOT NULL, " + "ai_object_data_id INTEGER NOT NULL, " + "UNIQUE (index_id, value), " + "PRIMARY KEY (index_id, value, ai_object_data_id), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE, " + "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Need this to make cascading deletes from ai_object_data and object_store + // fast. + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX ai_unique_index_data_ai_object_data_id_index " + "ON ai_unique_index_data (ai_object_data_id);" + )); + NS_ENSURE_SUCCESS(rv, rv); + rv = CreateFileTables(aDBConn); NS_ENSURE_SUCCESS(rv, rv); @@ -963,121 +1058,6 @@ UpgradeSchemaFrom9_0To10_0(mozIStorageConnection* aConnection) return NS_OK; } -nsresult -UpgradeSchemaFrom10_0To11_0(mozIStorageConnection* aConnection) -{ - nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "id, " - "object_store_id, " - "name, " - "key_path, " - "unique_index, " - "multientry" - ");" - )); - 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, multientry " - "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 PRIMARY KEY, " - "object_store_id INTEGER NOT NULL, " - "name TEXT NOT NULL, " - "key_path TEXT NOT NULL, " - "unique_index INTEGER NOT NULL, " - "multientry INTEGER NOT NULL, " - "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, multientry " - "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->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO object_data (object_store_id, key_value, data, file_ids) " - "SELECT object_store_id, id, data, file_ids " - "FROM ai_object_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO index_data (index_id, value, object_data_key, object_data_id) " - "SELECT ai_index_data.index_id, ai_index_data.value, ai_index_data.ai_object_data_id, object_data.id " - "FROM ai_index_data " - "INNER JOIN object_store_index ON " - "object_store_index.id = ai_index_data.index_id " - "INNER JOIN object_data ON " - "object_data.object_store_id = object_store_index.object_store_id AND " - "object_data.key_value = ai_index_data.ai_object_data_id;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO unique_index_data (index_id, value, object_data_key, object_data_id) " - "SELECT ai_unique_index_data.index_id, ai_unique_index_data.value, ai_unique_index_data.ai_object_data_id, object_data.id " - "FROM ai_unique_index_data " - "INNER JOIN object_store_index ON " - "object_store_index.id = ai_unique_index_data.index_id " - "INNER JOIN object_data ON " - "object_data.object_store_id = object_store_index.object_store_id AND " - "object_data.key_value = ai_unique_index_data.ai_object_data_id;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "UPDATE object_store " - "SET auto_increment = (SELECT max(id) FROM ai_object_data) + 1 " - "WHERE auto_increment;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE ai_unique_index_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE ai_index_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE ai_object_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->SetSchemaVersion(MakeSchemaVersion(11, 0)); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - class VersionChangeEventsRunnable; class SetVersionHelper : public AsyncConnectionHelper, @@ -1383,7 +1363,7 @@ OpenDatabaseHelper::DoDatabaseWork() } for (PRUint32 i = 0; i < mObjectStores.Length(); i++) { - nsRefPtr& objectStoreInfo = mObjectStores[i]; + nsAutoPtr& objectStoreInfo = mObjectStores[i]; for (PRUint32 j = 0; j < objectStoreInfo->indexes.Length(); j++) { IndexInfo& indexInfo = objectStoreInfo->indexes[j]; mLastIndexId = NS_MAX(indexInfo.id, mLastIndexId); @@ -1529,7 +1509,7 @@ OpenDatabaseHelper::CreateDatabaseConnection( } else { // This logic needs to change next time we change the schema! - PR_STATIC_ASSERT(kSQLiteSchemaVersion == PRInt32((11 << 4) + 0)); + PR_STATIC_ASSERT(kSQLiteSchemaVersion == PRInt32((10 << 4) + 0)); while (schemaVersion != kSQLiteSchemaVersion) { if (schemaVersion == 4) { @@ -1551,9 +1531,6 @@ OpenDatabaseHelper::CreateDatabaseConnection( else if (schemaVersion == MakeSchemaVersion(9, 0)) { rv = UpgradeSchemaFrom9_0To10_0(connection); } - else if (schemaVersion == MakeSchemaVersion(10, 0)) { - rv = UpgradeSchemaFrom10_0To11_0(connection); - } else { NS_WARNING("Unable to open IndexedDB database, no upgrade path is " "available!"); @@ -1704,8 +1681,6 @@ OpenDatabaseHelper::Run() // Destroy the database now (we should have the only ref). mDatabase = nsnull; - DatabaseInfo::Remove(mDatabaseId); - IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); NS_ASSERTION(mgr, "This should never fail!"); @@ -1774,14 +1749,18 @@ OpenDatabaseHelper::EnsureSuccessResult() PRUint32 objectStoreCount = mObjectStores.Length(); for (PRUint32 index = 0; index < objectStoreCount; index++) { - nsRefPtr& info = mObjectStores[index]; + nsAutoPtr& info = mObjectStores[index]; + NS_ASSERTION(info->databaseId == mDatabaseId, "Huh?!"); - ObjectStoreInfo* otherInfo = dbInfo->GetObjectStore(info->name); - NS_ASSERTION(otherInfo, "ObjectStore not known!"); + ObjectStoreInfo* otherInfo; + NS_ASSERTION(dbInfo->GetObjectStore(info->name, &otherInfo), + "ObjectStore not known!"); NS_ASSERTION(info->name == otherInfo->name && info->id == otherInfo->id && - info->keyPath == otherInfo->keyPath, + info->keyPath == otherInfo->keyPath && + info->autoIncrement == otherInfo->autoIncrement && + info->databaseId == otherInfo->databaseId, "Metadata mismatch!"); NS_ASSERTION(dbInfo->ContainsStoreName(info->name), "Object store names out of date!"); @@ -1800,6 +1779,8 @@ OpenDatabaseHelper::EnsureSuccessResult() "Bad index keyPath!"); NS_ASSERTION(indexInfo.unique == otherIndexInfo.unique, "Bad index unique value!"); + NS_ASSERTION(indexInfo.autoIncrement == otherIndexInfo.autoIncrement, + "Bad index autoIncrement value!"); } } } @@ -1810,7 +1791,6 @@ OpenDatabaseHelper::EnsureSuccessResult() nsRefPtr newInfo(new DatabaseInfo()); newInfo->name = mName; - newInfo->origin = mASCIIOrigin; newInfo->id = mDatabaseId; newInfo->filePath = mDatabaseFilePath; @@ -1821,8 +1801,8 @@ OpenDatabaseHelper::EnsureSuccessResult() newInfo.swap(dbInfo); - nsresult rv = IDBFactory::SetDatabaseMetadata(dbInfo, mCurrentVersion, - mObjectStores); + nsresult rv = IDBFactory::UpdateDatabaseMetadata(dbInfo, mCurrentVersion, + mObjectStores); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); NS_ASSERTION(mObjectStores.IsEmpty(), "Should have swapped!"); diff --git a/dom/indexedDB/OpenDatabaseHelper.h b/dom/indexedDB/OpenDatabaseHelper.h index d90a5f4f3b49..1e7b7be41e62 100644 --- a/dom/indexedDB/OpenDatabaseHelper.h +++ b/dom/indexedDB/OpenDatabaseHelper.h @@ -62,7 +62,7 @@ public: mASCIIOrigin(aASCIIOrigin), mRequestedVersion(aRequestedVersion), mForDeletion(aForDeletion), mDatabaseId(nsnull), mCurrentVersion(0), mLastObjectStoreId(0), mLastIndexId(0), mState(eCreated), - mResultCode(NS_OK), mLoadDBMetadata(false) + mResultCode(NS_OK) { NS_ASSERTION(!aForDeletion || !aRequestedVersion, "Can't be for deletion and request a version!"); @@ -132,7 +132,7 @@ private: nsCOMPtr mDatabaseId; // Out-params. - nsTArray > mObjectStores; + nsTArray > mObjectStores; PRUint64 mCurrentVersion; nsString mDatabaseFilePath; PRInt64 mLastObjectStoreId; @@ -153,9 +153,6 @@ private: nsresult mResultCode; nsRefPtr mFileManager; - - nsRefPtr mDBInfo; - bool mLoadDBMetadata; }; END_INDEXEDDB_NAMESPACE diff --git a/dom/indexedDB/nsIIDBObjectStore.idl b/dom/indexedDB/nsIIDBObjectStore.idl index 921a43f3c531..a00b9f003f0c 100644 --- a/dom/indexedDB/nsIIDBObjectStore.idl +++ b/dom/indexedDB/nsIIDBObjectStore.idl @@ -50,7 +50,7 @@ interface nsIDOMDOMStringList; * http://dev.w3.org/2006/webapi/WebSimpleDB/#idl-def-nsIIDBObjectStore * for more information. */ -[scriptable, builtinclass, uuid(e93c5ca4-89da-4eb4-b839-271ba4f65a27)] +[scriptable, builtinclass, uuid(adc6a1e2-9fd7-4d28-a7f9-9c653313124b)] interface nsIIDBObjectStore : nsISupports { readonly attribute DOMString name; @@ -61,8 +61,6 @@ interface nsIIDBObjectStore : nsISupports readonly attribute nsIIDBTransaction transaction; - readonly attribute boolean autoIncrement; - // Success fires IDBTransactionEvent, result == value for key [implicit_jscontext] nsIIDBRequest diff --git a/dom/indexedDB/test/Makefile.in b/dom/indexedDB/test/Makefile.in index c7e85250d28d..3c111d457040 100644 --- a/dom/indexedDB/test/Makefile.in +++ b/dom/indexedDB/test/Makefile.in @@ -57,7 +57,6 @@ TEST_FILES = \ test_add_twice_failure.html \ test_advance.html \ test_autoIncrement_indexes.html \ - test_autoIncrement.html \ test_bfcache.html \ test_clear.html \ test_cmp.html \ diff --git a/dom/indexedDB/test/error_events_abort_transactions_iframe.html b/dom/indexedDB/test/error_events_abort_transactions_iframe.html index e0a5b758ff75..f81f83917b89 100644 --- a/dom/indexedDB/test/error_events_abort_transactions_iframe.html +++ b/dom/indexedDB/test/error_events_abort_transactions_iframe.html @@ -88,13 +88,15 @@ event = yield; is(event.type, "abort", "Got a transaction abort event"); - is(db.version, 1, "Correct version"); - is(db.objectStoreNames.length, 1, "Correct objectStoreNames length"); + //todo(db.version, 1, "Correct version"); + is(db.objectStoreNames.length, 0, "Correct objectStoreNames length"); event = yield; is(event.type, "error", "Got request error event"); is(event.target.errorCode, IDBDatabaseException.ABORT_ERR, "Right error code"); + db.close(); + let request = mozIndexedDB.open(window.location.pathname, 1); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; diff --git a/dom/indexedDB/test/helpers.js b/dom/indexedDB/test/helpers.js index 9e6f9dff359d..4fc3b3a094b1 100644 --- a/dom/indexedDB/test/helpers.js +++ b/dom/indexedDB/test/helpers.js @@ -47,11 +47,6 @@ function continueToNextStep() }); } -function continueToNextStepSync() -{ - testGenerator.next(); -} - function errorHandler(event) { ok(false, "indexedDB error, code " + event.target.errorCode); @@ -80,7 +75,6 @@ ExpectError.prototype = { is(event.type, "error", "Got an error event"); is(this._code, event.target.errorCode, "Expected error was thrown."); event.preventDefault(); - event.stopPropagation(); grabEventAndContinueHandler(event); } }; diff --git a/dom/indexedDB/test/test_add_put.html b/dom/indexedDB/test/test_add_put.html index 108671024844..9d9949808b1a 100644 --- a/dom/indexedDB/test/test_add_put.html +++ b/dom/indexedDB/test/test_add_put.html @@ -82,8 +82,6 @@ let expected = expectedResult(method, keypath, explicit, autoincrement, existing); - let valueJSON = JSON.stringify(value); - ok(true, "making call" + test); // Make function call for throwing functions @@ -96,16 +94,13 @@ ok(true, "did throw" + test); ok(ex instanceof IDBDatabaseException, "Got a IDBDatabaseException" + test); is(ex.code, IDBDatabaseException.DATA_ERR, "expect a DATA_ERR" + test); - is(JSON.stringify(value), valueJSON, "call didn't modify value" + test); } continue; } // Make non-throwing function call let req = store[method].apply(store, args); - is(JSON.stringify(value), valueJSON, "call didn't modify value" + test); - - req.onsuccess = req.onerror = grabEventAndContinueHandler; + req.onsuccess = req.onerror = grabEventAndContinueHandler let e = yield; // Figure out what key we used @@ -128,7 +123,16 @@ } is(e.type, "success", "write should succeed" + test); - is(e.target.result, key, "write should return correct key" + test); + if (autoincrement && speccedNoKey) { + todo_is(e.target.result, key, "(fix ai) write should return correct key" + test); + key = e.target.result; + if (keypath) { + value.id = key; + } + } + else { + is(e.target.result, key, "write should return correct key" + test); + } store.get(key).onsuccess = grabEventAndContinueHandler; e = yield; diff --git a/dom/indexedDB/test/test_autoIncrement.html b/dom/indexedDB/test/test_autoIncrement.html deleted file mode 100644 index ea2c890c1bec..000000000000 --- a/dom/indexedDB/test/test_autoIncrement.html +++ /dev/null @@ -1,384 +0,0 @@ - - - - Indexed Database Property Test - - - - - - - - - - - diff --git a/dom/indexedDB/test/test_autoIncrement_indexes.html b/dom/indexedDB/test/test_autoIncrement_indexes.html index 366604a1d8af..951be60eef92 100644 --- a/dom/indexedDB/test/test_autoIncrement_indexes.html +++ b/dom/indexedDB/test/test_autoIncrement_indexes.html @@ -31,7 +31,8 @@ objectStore.add(data).onsuccess = grabEventAndContinueHandler; event = yield; - is(event.target.result, 1, "Added entry"); + let key = event.target.result; + ok(key, "Added entry"); request.onsuccess = grabEventAndContinueHandler; event = yield; @@ -44,17 +45,17 @@ first.get("foo").onsuccess = grabEventAndContinueHandler; event = yield; - is (event.target.result.id, 1, "Entry in first"); + is (event.target.result.id, key, "Entry in first"); second.get("foo").onsuccess = grabEventAndContinueHandler; event = yield; - is (event.target.result.id, 1, "Entry in second"); + is (event.target.result.id, key, "Entry in second"); third.get("foo").onsuccess = grabEventAndContinueHandler; event = yield; - is (event.target.result.id, 1, "Entry in third"); + is (event.target.result.id, key, "Entry in third"); finishTest(); yield; diff --git a/dom/indexedDB/test/test_setVersion_abort.html b/dom/indexedDB/test/test_setVersion_abort.html index 2dfef6152ae6..54d7070c9f43 100644 --- a/dom/indexedDB/test/test_setVersion_abort.html +++ b/dom/indexedDB/test/test_setVersion_abort.html @@ -21,7 +21,7 @@ function testSteps() let request = mozIndexedDB.open(name, 1, description); request.onerror = grabEventAndContinueHandler; - request.onsuccess = unexpectedSuccessHandler; + request.onsuccess = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; let event = yield; @@ -43,28 +43,13 @@ function testSteps() is(event.type, "abort", "Got transaction abort event"); is(event.target, transaction, "Right target"); - is(db.version, 1, "Correct version"); - is(db.objectStoreNames.length, 1, "Correct objectStoreNames length"); + todo(db.version, 1, "Correct version"); + is(db.objectStoreNames.length, 0, "Correct objectStoreNames length"); event = yield; is(event.type, "error", "Got request error event"); is(event.target, request, "Right target"); - request = mozIndexedDB.open(name, 1, description); - request.onerror = grabEventAndContinueHandler; - request.onsuccess = unexpectedSuccessHandler; - request.onupgradeneeded = grabEventAndContinueHandler; - let event = yield; - - let db2 = event.target.result; - - isnot(db, db2, "Should give a different db instance"); - is(db2.version, 1, "Correct version"); - is(db2.objectStoreNames.length, 0, "Correct objectStoreNames length"); - - request.onsuccess = grabEventAndContinueHandler; - yield; - finishTest(); yield; }