Bug 711972 - Use a StatementCache in localStorage.
authorMarco Bonardo <mbonardo@mozilla.com>
Wed, 11 Jan 2012 11:43:12 +0100
changeset 85485 37c00830f5991e2f0283631bd51288d9a03b34c2
parent 85484 52edf42878930241de0331a601481eca9d410360
child 85486 bf9bc81cfc0cc0936595658db3b2a0e901358ad4
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs711972
milestone12.0a1
Bug 711972 - Use a StatementCache in localStorage. r=honzab
dom/src/storage/nsDOMStoragePersistentDB.cpp
dom/src/storage/nsDOMStoragePersistentDB.h
--- a/dom/src/storage/nsDOMStoragePersistentDB.cpp
+++ b/dom/src/storage/nsDOMStoragePersistentDB.cpp
@@ -97,16 +97,17 @@ class nsIsOfflineSQLFunction : public mo
 {
   NS_DECL_ISUPPORTS
   NS_DECL_MOZISTORAGEFUNCTION
 };
 
 NS_IMPL_ISUPPORTS1(nsIsOfflineSQLFunction, mozIStorageFunction)
 
 nsDOMStoragePersistentDB::nsDOMStoragePersistentDB()
+: mStatements(mConnection)
 {
   mTempTableLoads.Init(16);
 }
 
 NS_IMETHODIMP
 nsIsOfflineSQLFunction::OnFunctionCall(
     mozIStorageValueArray *aFunctionArguments, nsIVariant **aResult)
 {
@@ -315,191 +316,66 @@ nsDOMStoragePersistentDB::Init(const nsS
                                 "FROM moz_webappsstore"));
       NS_ENSURE_SUCCESS(rv, rv);
 
       rv = mConnection->ExecuteSimpleSQL(
              NS_LITERAL_CSTRING("DROP TABLE moz_webappsstore"));
       NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  // temporary - disk synchronization statements
-  rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
-         "INSERT INTO webappsstore2_temp"
-         " SELECT * FROM webappsstore2"
-         " WHERE scope = :scope AND NOT EXISTS ("
-            "SELECT scope, key FROM webappsstore2_temp "
-            "WHERE scope = webappsstore2.scope AND key = webappsstore2.key)"),
-         getter_AddRefs(mCopyToTempTableStatement));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
-         "INSERT OR REPLACE INTO webappsstore2"
-         " SELECT * FROM webappsstore2_temp"
-         " WHERE scope = :scope;"),
-         getter_AddRefs(mCopyBackToDiskStatement));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
-         "DELETE FROM webappsstore2_temp"
-         " WHERE scope = :scope;"),
-         getter_AddRefs(mDeleteTemporaryTableStatement));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // retrieve all keys associated with a domain
-  rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
-         "SELECT key, value, secure FROM webappsstore2_temp "
-         "WHERE scope = :scope"),
-         getter_AddRefs(mGetAllKeysStatement));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // retrieve a value given a domain and a key
-  rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
-         "SELECT value, secure FROM webappsstore2_temp "
-         "WHERE scope = :scope "
-         "AND key = :key"),
-         getter_AddRefs(mGetKeyValueStatement));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // insert a new key
-  rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
-         "INSERT OR REPLACE INTO "
-         "webappsstore2_temp(scope, key, value, secure) "
-         "VALUES (:scope, :key, :value, :secure)"),
-         getter_AddRefs(mInsertKeyStatement));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // update the secure status of an existing key
-  rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
-         "UPDATE webappsstore2_temp "
-         "SET secure = :secure "
-         "WHERE scope = :scope "
-         "AND key = :key "),
-         getter_AddRefs(mSetSecureStatement));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // remove a key
-  rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
-         "DELETE FROM webappsstore2_view "
-         "WHERE scope = :scope "
-         "AND key = :key"),
-         getter_AddRefs(mRemoveKeyStatement));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // remove keys owned by a specific domain
-  rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
-         "DELETE FROM webappsstore2_view "
-         "WHERE scope GLOB :scope"),
-         getter_AddRefs(mRemoveOwnerStatement));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // remove keys belonging exactly only to a specific domain
-  rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
-         "DELETE FROM webappsstore2_view "
-         "WHERE scope = :scope"),
-         getter_AddRefs(mRemoveStorageStatement));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // remove all keys
-  rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
-         "DELETE FROM webappsstore2_view"),
-         getter_AddRefs(mRemoveAllStatement));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // check the usage for a given owner that is an offline-app allowed domain
-  rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
-         "SELECT SUM(LENGTH(key) + LENGTH(value)) "
-         "FROM ("
-           "SELECT key,value FROM webappsstore2_temp "
-           "WHERE scope GLOB :scope "
-           "UNION ALL "
-           "SELECT key,value FROM webappsstore2 "
-           "WHERE scope GLOB :scope "
-           "AND NOT EXISTS ("
-             "SELECT scope, key "
-             "FROM webappsstore2_temp "
-             "WHERE scope = webappsstore2.scope "
-             "AND key = webappsstore2.key"
-           ")"
-         ")"),
-         getter_AddRefs(mGetFullUsageStatement));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // check the usage for a given owner that is not an offline-app allowed domain
-  rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
-         "SELECT SUM(LENGTH(key) + LENGTH(value)) "
-         "FROM ("
-           "SELECT key, value FROM webappsstore2_temp "
-           "WHERE scope GLOB :scope "
-           "AND NOT ISOFFLINE(scope) "
-           "UNION ALL "
-           "SELECT key, value FROM webappsstore2 "
-           "WHERE scope GLOB :scope "
-           "AND NOT ISOFFLINE(scope) "
-           "AND NOT EXISTS ("
-             "SELECT scope, key "
-             "FROM webappsstore2_temp "
-             "WHERE scope = webappsstore2.scope "
-             "AND key = webappsstore2.key"
-           ")"
-         ")"),
-         getter_AddRefs(mGetOfflineExcludedUsageStatement));
-  NS_ENSURE_SUCCESS(rv, rv);
-
   rv = transaction.Commit();
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 void
 nsDOMStoragePersistentDB::Close()
 {
-  // Null the statements, this will finalize them.
-  mCopyToTempTableStatement = nsnull;
-  mCopyBackToDiskStatement = nsnull;
-  mDeleteTemporaryTableStatement = nsnull;
-  mGetAllKeysStatement = nsnull;
-  mGetKeyValueStatement = nsnull;
-  mInsertKeyStatement = nsnull;
-  mSetSecureStatement = nsnull;
-  mRemoveKeyStatement = nsnull;
-  mRemoveOwnerStatement = nsnull;
-  mRemoveStorageStatement = nsnull;
-  mRemoveAllStatement = nsnull;
-  mGetOfflineExcludedUsageStatement = nsnull;
-  mGetFullUsageStatement = nsnull;
+  // Finalize the cached statements.
+  mStatements.FinalizeStatements();
 
   DebugOnly<nsresult> rv = mConnection->Close();
   MOZ_ASSERT(NS_SUCCEEDED(rv));
 }
 
 nsresult
 nsDOMStoragePersistentDB::EnsureLoadTemporaryTableForStorage(DOMStorageImpl* aStorage)
 {
   TimeStamp timeStamp;
 
   if (!mTempTableLoads.Get(aStorage->GetScopeDBKey(), &timeStamp)) {
     nsresult rv;
 
     rv = MaybeCommitInsertTransaction();
     NS_ENSURE_SUCCESS(rv, rv);
 
-    mozStorageStatementScoper scope(mCopyToTempTableStatement);
+    nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
+      "INSERT INTO webappsstore2_temp "
+        "SELECT * FROM webappsstore2 "
+        "WHERE scope = :scope "
+          "AND NOT EXISTS ( "
+            "SELECT scope, key FROM webappsstore2_temp "
+            "WHERE scope = webappsstore2.scope AND key = webappsstore2.key "
+          ") "
+    );
+    NS_ENSURE_STATE(stmt);
+    mozStorageStatementScoper scope(stmt);
 
-    Binder binder(mCopyToTempTableStatement, &rv);
+    Binder binder(stmt, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"), 
                                       aStorage->GetScopeDBKey());
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = binder.Add();
     NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = mCopyToTempTableStatement->Execute();
+    rv = stmt->Execute();
     NS_ENSURE_SUCCESS(rv, rv);
 
     mTempTableLoads.Put(aStorage->GetScopeDBKey(), TimeStamp::Now());
 
     DOMStorageImpl::gStorageDB->EnsureTempTableFlushTimer();
   }
 
   return NS_OK;
@@ -513,44 +389,57 @@ nsDOMStoragePersistentDB::FlushTemporary
 {
   FlushTemporaryTableData* data = (FlushTemporaryTableData*)aUserArg;
 
   if (!data->mForce && 
       ((TimeStamp::Now() - aData).ToSeconds() < TEMP_TABLE_MAX_AGE))
     return PL_DHASH_NEXT;
 
   {
-    mozStorageStatementScoper scope(data->mDB->mCopyBackToDiskStatement);
+    nsCOMPtr<mozIStorageStatement> stmt =
+      data->mDB->mStatements.GetCachedStatement(
+        "INSERT OR REPLACE INTO webappsstore2 "
+         "SELECT * FROM webappsstore2_temp "
+         "WHERE scope = :scope "
+      );
+    NS_ENSURE_TRUE(stmt, PL_DHASH_STOP);
+    mozStorageStatementScoper scope(stmt);
 
-    Binder binder(data->mDB->mCopyBackToDiskStatement, &data->mRV);
+    Binder binder(stmt, &data->mRV);
     NS_ENSURE_SUCCESS(data->mRV, PL_DHASH_STOP);
 
     data->mRV = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"), aKey);
     NS_ENSURE_SUCCESS(data->mRV, PL_DHASH_STOP);
 
     data->mRV = binder.Add();
     NS_ENSURE_SUCCESS(data->mRV, PL_DHASH_STOP);
 
-    data->mRV = data->mDB->mCopyBackToDiskStatement->Execute();
+    data->mRV = stmt->Execute();
     NS_ENSURE_SUCCESS(data->mRV, PL_DHASH_STOP);
   }
 
   {
-    mozStorageStatementScoper scope(data->mDB->mDeleteTemporaryTableStatement);
+    nsCOMPtr<mozIStorageStatement> stmt =
+      data->mDB->mStatements.GetCachedStatement(
+        "DELETE FROM webappsstore2_temp "
+         "WHERE scope = :scope "
+      );
+    NS_ENSURE_TRUE(stmt, PL_DHASH_STOP);
+    mozStorageStatementScoper scope(stmt);
 
-    Binder binder(data->mDB->mDeleteTemporaryTableStatement, &data->mRV);
+    Binder binder(stmt, &data->mRV);
     NS_ENSURE_SUCCESS(data->mRV, PL_DHASH_STOP);
 
     data->mRV = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"), aKey);
     NS_ENSURE_SUCCESS(data->mRV, PL_DHASH_STOP);
 
     data->mRV = binder.Add();
     NS_ENSURE_SUCCESS(data->mRV, PL_DHASH_STOP);
 
-    data->mRV = data->mDB->mDeleteTemporaryTableStatement->Execute();
+    data->mRV = stmt->Execute();
     NS_ENSURE_SUCCESS(data->mRV, PL_DHASH_STOP);
   }
 
   return PL_DHASH_REMOVE;
 }
 
 nsresult
 nsDOMStoragePersistentDB::FlushTemporaryTables(bool force)
@@ -583,42 +472,45 @@ nsDOMStoragePersistentDB::GetAllKeys(DOM
   nsresult rv;
 
   rv = MaybeCommitInsertTransaction();
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = EnsureLoadTemporaryTableForStorage(aStorage);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mozStorageStatementScoper scope(mGetAllKeysStatement);
+  nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
+    "SELECT key, value, secure FROM webappsstore2_temp "
+    "WHERE scope = :scope "
+  );
+  NS_ENSURE_STATE(stmt);
+  mozStorageStatementScoper scope(stmt);
 
-  Binder binder(mGetAllKeysStatement, &rv);
+  Binder binder(stmt, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
                                     aStorage->GetScopeDBKey());
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = binder.Add();
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool exists;
-  while (NS_SUCCEEDED(rv = mGetAllKeysStatement->ExecuteStep(&exists)) &&
-         exists) {
-
+  while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&exists)) && exists) {
     nsAutoString key;
-    rv = mGetAllKeysStatement->GetString(0, key);
+    rv = stmt->GetString(0, key);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsAutoString value;
-    rv = mGetAllKeysStatement->GetString(1, value);
+    rv = stmt->GetString(1, value);
     NS_ENSURE_SUCCESS(rv, rv);
 
     PRInt32 secureInt = 0;
-    rv = mGetAllKeysStatement->GetInt32(2, &secureInt);
+    rv = stmt->GetInt32(2, &secureInt);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsSessionStorageEntry* entry = aKeys->PutEntry(key);
     NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
 
     entry->mItem = new nsDOMStorageItem(aStorage, key, value, secureInt);
     if (!entry->mItem) {
       aKeys->RawRemoveEntry(entry);
@@ -638,41 +530,47 @@ nsDOMStoragePersistentDB::GetKeyValue(DO
   nsresult rv;
 
   rv = MaybeCommitInsertTransaction();
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = EnsureLoadTemporaryTableForStorage(aStorage);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mozStorageStatementScoper scope(mGetKeyValueStatement);
+  nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
+    "SELECT value, secure FROM webappsstore2_temp "
+    "WHERE scope = :scope "
+      "AND key = :key "
+  );
+  NS_ENSURE_STATE(stmt);
+  mozStorageStatementScoper scope(stmt);
 
-  Binder binder(mGetKeyValueStatement, &rv);
+  Binder binder(stmt, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
                                     aStorage->GetScopeDBKey());
   NS_ENSURE_SUCCESS(rv, rv);
   rv = binder->BindStringByName(NS_LITERAL_CSTRING("key"),
                                 aKey);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = binder.Add();
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool exists;
-  rv = mGetKeyValueStatement->ExecuteStep(&exists);
+  rv = stmt->ExecuteStep(&exists);
   NS_ENSURE_SUCCESS(rv, rv);
 
   PRInt32 secureInt = 0;
   if (exists) {
-    rv = mGetKeyValueStatement->GetString(0, aValue);
+    rv = stmt->GetString(0, aValue);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = mGetKeyValueStatement->GetInt32(1, &secureInt);
+    rv = stmt->GetInt32(1, &secureInt);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   else {
     rv = NS_ERROR_DOM_NOT_FOUND_ERR;
   }
 
   *aSecure = !!secureInt;
 
@@ -714,19 +612,24 @@ nsDOMStoragePersistentDB::SetKey(DOMStor
 
   if (usage > aQuota) {
     return NS_ERROR_DOM_QUOTA_REACHED;
   }
 
   rv = EnsureInsertTransaction();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mozStorageStatementScoper scopeinsert(mInsertKeyStatement);
+  nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
+    "INSERT OR REPLACE INTO webappsstore2_temp (scope, key, value, secure) "
+    "VALUES (:scope, :key, :value, :secure) "
+  );
+  NS_ENSURE_STATE(stmt);
+  mozStorageStatementScoper scopeinsert(stmt);
 
-  Binder binder(mInsertKeyStatement, &rv);
+  Binder binder(stmt, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
                                     aStorage->GetScopeDBKey());
   NS_ENSURE_SUCCESS(rv, rv);
   rv = binder->BindStringByName(NS_LITERAL_CSTRING("key"),
                                 aKey);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -735,17 +638,17 @@ nsDOMStoragePersistentDB::SetKey(DOMStor
   NS_ENSURE_SUCCESS(rv, rv);
   rv = binder->BindInt32ByName(NS_LITERAL_CSTRING("secure"),
                                aSecure ? 1 : 0);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = binder.Add();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = mInsertKeyStatement->Execute();
+  rv = stmt->Execute();
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!aStorage->GetQuotaDomainDBKey(!aExcludeOfflineFromUsage).IsEmpty()) {
     // No need to set mCachedOwner since it was set by GetUsage()
     mCachedUsage = usage;
   }
 
   *aNewUsage = usage;
@@ -763,35 +666,42 @@ nsDOMStoragePersistentDB::SetSecure(DOMS
   nsresult rv;
 
   rv = EnsureLoadTemporaryTableForStorage(aStorage);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = EnsureInsertTransaction();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mozStorageStatementScoper scope(mSetSecureStatement);
+  nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
+    "UPDATE webappsstore2_temp "
+    "SET secure = :secure "
+    "WHERE scope = :scope "
+      "AND key = :key "
+  );
+  NS_ENSURE_STATE(stmt);
+  mozStorageStatementScoper scope(stmt);
 
-  Binder binder(mSetSecureStatement, &rv);
+  Binder binder(stmt, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
                                     aStorage->GetScopeDBKey());
   NS_ENSURE_SUCCESS(rv, rv);
   rv = binder->BindStringByName(NS_LITERAL_CSTRING("key"),
                                 aKey);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = binder->BindInt32ByName(NS_LITERAL_CSTRING("secure"),
                                aSecure ? 1 : 0);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = binder.Add();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = mSetSecureStatement->Execute();
+  rv = stmt->Execute();
   NS_ENSURE_SUCCESS(rv, rv);
 
   MarkScopeDirty(aStorage);
 
   return NS_OK;
 }
 
 nsresult
@@ -800,110 +710,126 @@ nsDOMStoragePersistentDB::RemoveKey(DOMS
                                     bool aExcludeOfflineFromUsage,
                                     PRInt32 aKeyUsage)
 {
   nsresult rv;
 
   rv = MaybeCommitInsertTransaction();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mozStorageStatementScoper scope(mRemoveKeyStatement);
+  nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
+    "DELETE FROM webappsstore2_view "
+    "WHERE scope = :scope "
+      "AND key = :key "
+  );
+  NS_ENSURE_STATE(stmt);
+  mozStorageStatementScoper scope(stmt);
 
   if (DomainMaybeCached(
       aStorage->GetQuotaDomainDBKey(!aExcludeOfflineFromUsage))) {
     mCachedUsage = 0;
     mCachedOwner.Truncate();
   }
 
-  Binder binder(mRemoveKeyStatement, &rv);
+  Binder binder(stmt, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
                                     aStorage->GetScopeDBKey());
   NS_ENSURE_SUCCESS(rv, rv);
   rv = binder->BindStringByName(NS_LITERAL_CSTRING("key"),
                                 aKey);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = binder.Add();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = mRemoveKeyStatement->Execute();
+  rv = stmt->Execute();
   NS_ENSURE_SUCCESS(rv, rv);
 
   MarkScopeDirty(aStorage);
 
   return NS_OK;
 }
 
 nsresult
 nsDOMStoragePersistentDB::ClearStorage(DOMStorageImpl* aStorage)
 {
   nsresult rv;
 
   rv = MaybeCommitInsertTransaction();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mozStorageStatementScoper scope(mRemoveStorageStatement);
+  nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
+    "DELETE FROM webappsstore2_view "
+    "WHERE scope = :scope "
+  );
+  NS_ENSURE_STATE(stmt);
+  mozStorageStatementScoper scope(stmt);
 
   mCachedUsage = 0;
   mCachedOwner.Truncate();
 
-  Binder binder(mRemoveStorageStatement, &rv);
+  Binder binder(stmt, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
                                     aStorage->GetScopeDBKey());
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = binder.Add();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = mRemoveStorageStatement->Execute();
+  rv = stmt->Execute();
   NS_ENSURE_SUCCESS(rv, rv);
 
   MarkScopeDirty(aStorage);
 
   return NS_OK;
 }
 
 nsresult
 nsDOMStoragePersistentDB::RemoveOwner(const nsACString& aOwner,
                                       bool aIncludeSubDomains)
 {
   nsresult rv;
 
   rv = MaybeCommitInsertTransaction();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mozStorageStatementScoper scope(mRemoveOwnerStatement);
+  nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
+    "DELETE FROM webappsstore2_view "
+    "WHERE scope GLOB :scope "
+  );
+  NS_ENSURE_STATE(stmt);
+  mozStorageStatementScoper scope(stmt);
 
   nsCAutoString subdomainsDBKey;
   nsDOMStorageDBWrapper::CreateDomainScopeDBKey(aOwner, subdomainsDBKey);
 
   if (DomainMaybeCached(subdomainsDBKey)) {
     mCachedUsage = 0;
     mCachedOwner.Truncate();
   }
 
   if (!aIncludeSubDomains)
     subdomainsDBKey.AppendLiteral(":");
   subdomainsDBKey.AppendLiteral("*");
 
-  Binder binder(mRemoveOwnerStatement, &rv);
+  Binder binder(stmt, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
                                     subdomainsDBKey);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = binder.Add();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = mRemoveOwnerStatement->Execute();
+  rv = stmt->Execute();
   NS_ENSURE_SUCCESS(rv, rv);
 
   MarkAllScopesDirty();
 
   return NS_OK;
 }
 
 
@@ -993,19 +919,23 @@ nsDOMStoragePersistentDB::RemoveOwners(c
 nsresult
 nsDOMStoragePersistentDB::RemoveAll()
 {
   nsresult rv;
 
   rv = MaybeCommitInsertTransaction();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mozStorageStatementScoper scope(mRemoveAllStatement);
+  nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
+    "DELETE FROM webappsstore2_view "
+  );
+  NS_ENSURE_STATE(stmt);
+  mozStorageStatementScoper scope(stmt);
 
-  rv = mRemoveAllStatement->Execute();
+  rv = stmt->Execute();
   NS_ENSURE_SUCCESS(rv, rv);
 
   MarkAllScopesDirty();
 
   return NS_OK;
 }
 
 nsresult
@@ -1045,43 +975,79 @@ nsDOMStoragePersistentDB::GetUsageIntern
     return NS_OK;
   }
 
   nsresult rv;
 
   rv = MaybeCommitInsertTransaction();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mozIStorageStatement* statement = aExcludeOfflineFromUsage
-    ? mGetOfflineExcludedUsageStatement : mGetFullUsageStatement;
-
-  mozStorageStatementScoper scope(statement);
+  nsCOMPtr<mozIStorageStatement> stmt;
+  if (aExcludeOfflineFromUsage) {
+    stmt = mStatements.GetCachedStatement(
+      "SELECT SUM(LENGTH(key) + LENGTH(value)) "
+      "FROM ( "
+        "SELECT key, value FROM webappsstore2_temp "
+        "WHERE scope GLOB :scope "
+          "AND NOT ISOFFLINE(scope) "
+        "UNION ALL "
+        "SELECT key, value FROM webappsstore2 "
+        "WHERE scope GLOB :scope "
+          "AND NOT ISOFFLINE(scope) "
+          "AND NOT EXISTS ( "
+            "SELECT scope, key "
+            "FROM webappsstore2_temp "
+            "WHERE scope = webappsstore2.scope "
+              "AND key = webappsstore2.key "
+          ") "
+      ") "
+    );
+  } else {
+    stmt = mStatements.GetCachedStatement(
+      "SELECT SUM(LENGTH(key) + LENGTH(value)) "
+      "FROM ( "
+        "SELECT key,value FROM webappsstore2_temp "
+        "WHERE scope GLOB :scope "
+        "UNION ALL "
+        "SELECT key,value FROM webappsstore2 "
+        "WHERE scope GLOB :scope "
+          "AND NOT EXISTS ( "
+            "SELECT scope, key "
+            "FROM webappsstore2_temp "
+            "WHERE scope = webappsstore2.scope "
+              "AND key = webappsstore2.key "
+          ") "
+      ") "
+    );
+  }
+  NS_ENSURE_STATE(stmt);
+  mozStorageStatementScoper scope(stmt);
 
   nsCAutoString scopeValue(aQuotaDomainDBKey);
   scopeValue += NS_LITERAL_CSTRING("*");
 
-  Binder binder(statement, &rv);
+  Binder binder(stmt, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"), scopeValue);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = binder.Add();
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool exists;
-  rv = statement->ExecuteStep(&exists);
+  rv = stmt->ExecuteStep(&exists);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!exists) {
     *aUsage = 0;
     return NS_OK;
   }
 
-  rv = statement->GetInt32(0, aUsage);
+  rv = stmt->GetInt32(0, aUsage);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!aQuotaDomainDBKey.IsEmpty()) {
     mCachedOwner = aQuotaDomainDBKey;
     mCachedUsage = *aUsage;
   }
 
   return NS_OK;
--- a/dom/src/storage/nsDOMStoragePersistentDB.h
+++ b/dom/src/storage/nsDOMStoragePersistentDB.h
@@ -41,25 +41,28 @@
 
 #include "nscore.h"
 #include "nsDOMStorageBaseDB.h"
 #include "mozIStorageConnection.h"
 #include "mozIStorageStatement.h"
 #include "nsTHashtable.h"
 #include "nsDataHashtable.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/storage/StatementCache.h"
 
 class DOMStorageImpl;
 class nsSessionStorageEntry;
 
 using mozilla::TimeStamp;
 using mozilla::TimeDuration;
 
 class nsDOMStoragePersistentDB : public nsDOMStorageBaseDB
 {
+  typedef mozilla::storage::StatementCache<mozIStorageStatement> StatementCache;
+
 public:
   nsDOMStoragePersistentDB();
   ~nsDOMStoragePersistentDB() {}
 
   nsresult
   Init(const nsString& aDatabaseName);
 
   /**
@@ -186,31 +189,17 @@ protected:
     bool mForce;
     nsresult mRV;
   };
   static PLDHashOperator FlushTemporaryTable(nsCStringHashKey::KeyType aKey,
                                              TimeStamp& aData,
                                              void* aUserArg);       
 
   nsCOMPtr<mozIStorageConnection> mConnection;
-
-  nsCOMPtr<mozIStorageStatement> mCopyToTempTableStatement;
-  nsCOMPtr<mozIStorageStatement> mCopyBackToDiskStatement;
-  nsCOMPtr<mozIStorageStatement> mDeleteTemporaryTableStatement;
-  nsCOMPtr<mozIStorageStatement> mGetAllKeysStatement;
-  nsCOMPtr<mozIStorageStatement> mGetKeyValueStatement;
-  nsCOMPtr<mozIStorageStatement> mInsertKeyStatement;
-  nsCOMPtr<mozIStorageStatement> mSetSecureStatement;
-  nsCOMPtr<mozIStorageStatement> mRemoveKeyStatement;
-  nsCOMPtr<mozIStorageStatement> mRemoveOwnerStatement;
-  nsCOMPtr<mozIStorageStatement> mRemoveStorageStatement;
-  nsCOMPtr<mozIStorageStatement> mRemoveAllStatement;
-  nsCOMPtr<mozIStorageStatement> mGetOfflineExcludedUsageStatement;
-  nsCOMPtr<mozIStorageStatement> mGetFullUsageStatement;
-  // If you add an statement, remember to null in in Close.
+  StatementCache mStatements;
 
   nsCString mCachedOwner;
   PRInt32 mCachedUsage;
 
   // Maps ScopeDBKey to time of the temporary table load for that scope.
   // If a record is present, the temp table has been loaded. If it is not
   // present, the table has not yet been loaded or has alrady been flushed.
   nsDataHashtable<nsCStringHashKey, TimeStamp> mTempTableLoads;