LCOV - code coverage report
Current view: directory - dom/indexedDB - IDBTransaction.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 497 376 75.7 %
Date: 2012-06-02 Functions: 66 54 81.8 %

       1                 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* vim: set ts=2 et sw=2 tw=80: */
       3                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Indexed Database.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  * The Mozilla Foundation.
      20                 :  * Portions created by the Initial Developer are Copyright (C) 2010
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Ben Turner <bent.mozilla@gmail.com>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      28                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include "IDBTransaction.h"
      41                 : 
      42                 : #include "nsIScriptContext.h"
      43                 : 
      44                 : #include "mozilla/storage.h"
      45                 : #include "nsContentUtils.h"
      46                 : #include "nsDOMClassInfoID.h"
      47                 : #include "nsDOMLists.h"
      48                 : #include "nsEventDispatcher.h"
      49                 : #include "nsPIDOMWindow.h"
      50                 : #include "nsProxyRelease.h"
      51                 : #include "nsThreadUtils.h"
      52                 : 
      53                 : #include "AsyncConnectionHelper.h"
      54                 : #include "DatabaseInfo.h"
      55                 : #include "IDBCursor.h"
      56                 : #include "IDBEvents.h"
      57                 : #include "IDBFactory.h"
      58                 : #include "IDBObjectStore.h"
      59                 : #include "IndexedDatabaseManager.h"
      60                 : #include "TransactionThreadPool.h"
      61                 : 
      62                 : #define SAVEPOINT_NAME "savepoint"
      63                 : 
      64                 : USING_INDEXEDDB_NAMESPACE
      65                 : 
      66                 : namespace {
      67                 : 
      68                 : PLDHashOperator
      69            2053 : DoomCachedStatements(const nsACString& aQuery,
      70                 :                      nsCOMPtr<mozIStorageStatement>& aStatement,
      71                 :                      void* aUserArg)
      72                 : {
      73            2053 :   CommitHelper* helper = static_cast<CommitHelper*>(aUserArg);
      74            2053 :   helper->AddDoomedObject(aStatement);
      75            2053 :   return PL_DHASH_REMOVE;
      76                 : }
      77                 : 
      78                 : // This runnable doesn't actually do anything beyond "prime the pump" and get
      79                 : // transactions in the right order on the transaction thread pool.
      80                 : class StartTransactionRunnable : public nsIRunnable
      81            1464 : {
      82                 : public:
      83                 :   NS_DECL_ISUPPORTS
      84                 : 
      85             469 :   NS_IMETHOD Run()
      86                 :   {
      87                 :     // NOP
      88             469 :     return NS_OK;
      89                 :   }
      90                 : };
      91                 : 
      92                 : // Could really use those NS_REFCOUNTING_HAHA_YEAH_RIGHT macros here.
      93            2050 : NS_IMETHODIMP_(nsrefcnt) StartTransactionRunnable::AddRef()
      94                 : {
      95            2050 :   return 2;
      96                 : }
      97                 : 
      98            2050 : NS_IMETHODIMP_(nsrefcnt) StartTransactionRunnable::Release()
      99                 : {
     100            2050 :   return 1;
     101                 : }
     102                 : 
     103            1025 : NS_IMPL_QUERY_INTERFACE1(StartTransactionRunnable, nsIRunnable)
     104                 : 
     105            1464 : StartTransactionRunnable gStartTransactionRunnable;
     106                 : 
     107                 : } // anonymous namespace
     108                 : 
     109                 : // static
     110                 : already_AddRefed<IDBTransaction>
     111             540 : IDBTransaction::Create(IDBDatabase* aDatabase,
     112                 :                        nsTArray<nsString>& aObjectStoreNames,
     113                 :                        Mode aMode,
     114                 :                        bool aDispatchDelayed)
     115                 : {
     116             540 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     117                 : 
     118            1080 :   nsRefPtr<IDBTransaction> transaction = new IDBTransaction();
     119                 : 
     120             540 :   transaction->BindToOwner(aDatabase);
     121             540 :   if (!transaction->SetScriptOwner(aDatabase->GetScriptOwner())) {
     122               0 :     return nsnull;
     123                 :   }
     124                 : 
     125             540 :   transaction->mDatabase = aDatabase;
     126             540 :   transaction->mMode = aMode;
     127                 :   
     128             540 :   transaction->mDatabaseInfo = aDatabase->Info();
     129                 : 
     130             540 :   if (!transaction->mObjectStoreNames.AppendElements(aObjectStoreNames)) {
     131               0 :     NS_ERROR("Out of memory!");
     132               0 :     return nsnull;
     133                 :   }
     134                 : 
     135             540 :   if (!transaction->mCachedStatements.Init()) {
     136               0 :     NS_ERROR("Failed to initialize hash!");
     137               0 :     return nsnull;
     138                 :   }
     139                 : 
     140             540 :   if (!aDispatchDelayed) {
     141                 :     nsCOMPtr<nsIThreadInternal> thread =
     142             938 :       do_QueryInterface(NS_GetCurrentThread());
     143             469 :     NS_ENSURE_TRUE(thread, nsnull);
     144                 : 
     145                 :     // We need the current recursion depth first.
     146                 :     PRUint32 depth;
     147             469 :     nsresult rv = thread->GetRecursionDepth(&depth);
     148             469 :     NS_ENSURE_SUCCESS(rv, nsnull);
     149                 : 
     150             469 :     NS_ASSERTION(depth, "This should never be 0!");
     151             469 :     transaction->mCreatedRecursionDepth = depth - 1;
     152                 : 
     153             469 :     rv = thread->AddObserver(transaction);
     154             469 :     NS_ENSURE_SUCCESS(rv, nsnull);
     155                 : 
     156             938 :     transaction->mCreating = true;
     157                 :   }
     158                 : 
     159             540 :   if (aMode != IDBTransaction::VERSION_CHANGE) {
     160             469 :     TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate();
     161             469 :     pool->Dispatch(transaction, &gStartTransactionRunnable, false, nsnull);
     162                 :   }
     163                 : 
     164             540 :   return transaction.forget();
     165                 : }
     166                 : 
     167             540 : IDBTransaction::IDBTransaction()
     168                 : : mReadyState(IDBTransaction::INITIAL),
     169                 :   mMode(IDBTransaction::READ_ONLY),
     170                 :   mPendingRequests(0),
     171                 :   mCreatedRecursionDepth(0),
     172                 :   mSavepointCount(0),
     173                 :   mAborted(false),
     174                 :   mCreating(false)
     175                 : #ifdef DEBUG
     176             540 :   , mFiredCompleteOrAbort(false)
     177                 : #endif
     178                 : {
     179             540 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     180             540 : }
     181                 : 
     182            1620 : IDBTransaction::~IDBTransaction()
     183                 : {
     184             540 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     185             540 :   NS_ASSERTION(!mPendingRequests, "Should have no pending requests here!");
     186             540 :   NS_ASSERTION(!mSavepointCount, "Should have released them all!");
     187             540 :   NS_ASSERTION(!mConnection, "Should have called CommitOrRollback!");
     188             540 :   NS_ASSERTION(!mCreating, "Should have been cleared already!");
     189             540 :   NS_ASSERTION(mFiredCompleteOrAbort, "Should have fired event!");
     190                 : 
     191             540 :   nsContentUtils::ReleaseWrapper(static_cast<nsIDOMEventTarget*>(this), this);
     192            2160 : }
     193                 : 
     194                 : void
     195            4670 : IDBTransaction::OnNewRequest()
     196                 : {
     197            4670 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     198            4670 :   if (!mPendingRequests) {
     199             529 :     NS_ASSERTION(mReadyState == IDBTransaction::INITIAL,
     200                 :                  "Reusing a transaction!");
     201             529 :     mReadyState = IDBTransaction::LOADING;
     202                 :   }
     203            4670 :   ++mPendingRequests;
     204            4670 : }
     205                 : 
     206                 : void
     207            4670 : IDBTransaction::OnRequestFinished()
     208                 : {
     209            4670 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     210            4670 :   NS_ASSERTION(mPendingRequests, "Mismatched calls!");
     211            4670 :   --mPendingRequests;
     212            4670 :   if (!mPendingRequests) {
     213             529 :     NS_ASSERTION(mAborted || mReadyState == IDBTransaction::LOADING,
     214                 :                  "Bad state!");
     215             529 :     mReadyState = IDBTransaction::COMMITTING;
     216             529 :     CommitOrRollback();
     217                 :   }
     218            4670 : }
     219                 : 
     220                 : void
     221             123 : IDBTransaction::RemoveObjectStore(const nsAString& aName)
     222                 : {
     223             123 :   NS_ASSERTION(mMode == IDBTransaction::VERSION_CHANGE,
     224                 :                "Only remove object stores on VERSION_CHANGE transactions");
     225                 : 
     226             123 :   mDatabaseInfo->RemoveObjectStore(aName);
     227                 : 
     228             123 :   for (PRUint32 i = 0; i < mCreatedObjectStores.Length(); i++) {
     229             123 :     if (mCreatedObjectStores[i]->Name() == aName) {
     230             123 :       mCreatedObjectStores.RemoveElementAt(i);
     231             123 :       break;
     232                 :     }
     233                 :   }
     234             123 : }
     235                 : 
     236                 : void
     237              71 : IDBTransaction::SetTransactionListener(IDBTransactionListener* aListener)
     238                 : {
     239              71 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     240              71 :   NS_ASSERTION(!mListener, "Shouldn't already have a listener!");
     241              71 :   mListener = aListener;
     242              71 : }
     243                 : 
     244                 : nsresult
     245             540 : IDBTransaction::CommitOrRollback()
     246                 : {
     247             540 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     248                 : 
     249             540 :   TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate();
     250             540 :   NS_ENSURE_STATE(pool);
     251                 : 
     252                 :   nsRefPtr<CommitHelper> helper(new CommitHelper(this, mListener,
     253            1620 :                                                  mCreatedObjectStores));
     254                 : 
     255             540 :   mCachedStatements.Enumerate(DoomCachedStatements, helper);
     256             540 :   NS_ASSERTION(!mCachedStatements.Count(), "Statements left!");
     257                 : 
     258             540 :   nsresult rv = pool->Dispatch(this, helper, true, helper);
     259             540 :   NS_ENSURE_SUCCESS(rv, rv);
     260                 : 
     261             540 :   return NS_OK;
     262                 : }
     263                 : 
     264                 : bool
     265            4670 : IDBTransaction::StartSavepoint()
     266                 : {
     267            4670 :   NS_PRECONDITION(!NS_IsMainThread(), "Wrong thread!");
     268            4670 :   NS_PRECONDITION(mConnection, "No connection!");
     269                 : 
     270            4670 :   nsCOMPtr<mozIStorageStatement> stmt = GetCachedStatement(NS_LITERAL_CSTRING(
     271                 :     "SAVEPOINT " SAVEPOINT_NAME
     272            9340 :   ));
     273            4670 :   NS_ENSURE_TRUE(stmt, false);
     274                 : 
     275            9340 :   mozStorageStatementScoper scoper(stmt);
     276                 : 
     277            4670 :   nsresult rv = stmt->Execute();
     278            4670 :   NS_ENSURE_SUCCESS(rv, false);
     279                 : 
     280            4670 :   ++mSavepointCount;
     281                 : 
     282            4670 :   return true;
     283                 : }
     284                 : 
     285                 : nsresult
     286            4651 : IDBTransaction::ReleaseSavepoint()
     287                 : {
     288            4651 :   NS_PRECONDITION(!NS_IsMainThread(), "Wrong thread!");
     289            4651 :   NS_PRECONDITION(mConnection, "No connection!");
     290                 : 
     291            4651 :   NS_ASSERTION(mSavepointCount, "Mismatch!");
     292                 : 
     293            4651 :   nsCOMPtr<mozIStorageStatement> stmt = GetCachedStatement(NS_LITERAL_CSTRING(
     294                 :     "RELEASE SAVEPOINT " SAVEPOINT_NAME
     295            9302 :   ));
     296            4651 :   NS_ENSURE_TRUE(stmt, false);
     297                 : 
     298            9302 :   mozStorageStatementScoper scoper(stmt);
     299                 : 
     300            4651 :   nsresult rv = stmt->Execute();
     301            4651 :   NS_ENSURE_SUCCESS(rv, false);
     302                 : 
     303            4651 :   --mSavepointCount;
     304                 : 
     305            4651 :   return NS_OK;
     306                 : }
     307                 : 
     308                 : void
     309              19 : IDBTransaction::RollbackSavepoint()
     310                 : {
     311              19 :   NS_PRECONDITION(!NS_IsMainThread(), "Wrong thread!");
     312              19 :   NS_PRECONDITION(mConnection, "No connection!");
     313                 : 
     314              19 :   NS_ASSERTION(mSavepointCount == 1, "Mismatch!");
     315              19 :   mSavepointCount = 0;
     316                 : 
     317              19 :   nsCOMPtr<mozIStorageStatement> stmt = GetCachedStatement(NS_LITERAL_CSTRING(
     318                 :     "ROLLBACK TO SAVEPOINT " SAVEPOINT_NAME
     319              38 :   ));
     320              19 :   NS_ENSURE_TRUE(stmt,);
     321                 : 
     322              38 :   mozStorageStatementScoper scoper(stmt);
     323                 : 
     324              19 :   nsresult rv = stmt->Execute();
     325              19 :   NS_ENSURE_SUCCESS(rv,);
     326                 : }
     327                 : 
     328                 : nsresult
     329            4670 : IDBTransaction::GetOrCreateConnection(mozIStorageConnection** aResult)
     330                 : {
     331            4670 :   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
     332                 : 
     333            4670 :   if (mDatabase->IsInvalidated()) {
     334               0 :     return NS_ERROR_NOT_AVAILABLE;
     335                 :   }
     336                 : 
     337            4670 :   if (!mConnection) {
     338                 :     nsCOMPtr<mozIStorageConnection> connection =
     339            1058 :       IDBFactory::GetConnection(mDatabase->FilePath());
     340             529 :     NS_ENSURE_TRUE(connection, NS_ERROR_FAILURE);
     341                 : 
     342                 :     nsresult rv;
     343                 : 
     344            1058 :     nsRefPtr<UpdateRefcountFunction> function;
     345            1058 :     nsCString beginTransaction;
     346             529 :     if (mMode != IDBTransaction::READ_ONLY) {
     347             734 :       function = new UpdateRefcountFunction(Database()->Manager());
     348             367 :       NS_ENSURE_TRUE(function, NS_ERROR_OUT_OF_MEMORY);
     349                 : 
     350             367 :       rv = function->Init();
     351             367 :       NS_ENSURE_SUCCESS(rv, rv);
     352                 : 
     353             367 :       rv = connection->CreateFunction(
     354             367 :         NS_LITERAL_CSTRING("update_refcount"), 2, function);
     355             367 :       NS_ENSURE_SUCCESS(rv, rv);
     356                 : 
     357             367 :       beginTransaction.AssignLiteral("BEGIN IMMEDIATE TRANSACTION;");
     358                 :     }
     359                 :     else {
     360             162 :       beginTransaction.AssignLiteral("BEGIN TRANSACTION;");
     361                 :     }
     362                 : 
     363            1058 :     nsCOMPtr<mozIStorageStatement> stmt;
     364             529 :     rv = connection->CreateStatement(beginTransaction, getter_AddRefs(stmt));
     365             529 :     NS_ENSURE_SUCCESS(rv, rv);
     366                 : 
     367             529 :     rv = stmt->Execute();
     368             529 :     NS_ENSURE_SUCCESS(rv, rv);
     369                 : 
     370             529 :     function.swap(mUpdateFileRefcountFunction);
     371            1058 :     connection.swap(mConnection);
     372                 :   }
     373                 : 
     374            9340 :   nsCOMPtr<mozIStorageConnection> result(mConnection);
     375            4670 :   result.forget(aResult);
     376            4670 :   return NS_OK;
     377                 : }
     378                 : 
     379                 : already_AddRefed<mozIStorageStatement>
     380           14453 : IDBTransaction::GetCachedStatement(const nsACString& aQuery)
     381                 : {
     382           14453 :   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
     383           14453 :   NS_ASSERTION(!aQuery.IsEmpty(), "Empty sql statement!");
     384           14453 :   NS_ASSERTION(mConnection, "No connection!");
     385                 : 
     386           28906 :   nsCOMPtr<mozIStorageStatement> stmt;
     387                 : 
     388           14453 :   if (!mCachedStatements.Get(aQuery, getter_AddRefs(stmt))) {
     389            2053 :     nsresult rv = mConnection->CreateStatement(aQuery, getter_AddRefs(stmt));
     390                 : #ifdef DEBUG
     391            2053 :     if (NS_FAILED(rv)) {
     392               0 :       nsCString error;
     393               0 :       error.AppendLiteral("The statement `");
     394               0 :       error.Append(aQuery);
     395               0 :       error.AppendLiteral("` failed to compile with the error message `");
     396               0 :       nsCString msg;
     397               0 :       (void)mConnection->GetLastErrorString(msg);
     398               0 :       error.Append(msg);
     399               0 :       error.AppendLiteral("`.");
     400               0 :       NS_ERROR(error.get());
     401                 :     }
     402                 : #endif
     403            2053 :     NS_ENSURE_SUCCESS(rv, nsnull);
     404                 : 
     405            2053 :     if (!mCachedStatements.Put(aQuery, stmt)) {
     406               0 :       NS_ERROR("Out of memory?!");
     407                 :     }
     408                 :   }
     409                 : 
     410           14453 :   return stmt.forget();
     411                 : }
     412                 : 
     413                 : bool
     414            9267 : IDBTransaction::IsOpen() const
     415                 : {
     416            9267 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     417                 : 
     418                 :   // If we haven't started anything then we're open.
     419            9267 :   if (mReadyState == IDBTransaction::INITIAL) {
     420             958 :     NS_ASSERTION(AsyncConnectionHelper::GetCurrentTransaction() != this,
     421                 :                  "This should be some other transaction (or null)!");
     422             958 :     return true;
     423                 :   }
     424                 : 
     425                 :   // If we've already started then we need to check to see if we still have the
     426                 :   // mCreating flag set. If we do (i.e. we haven't returned to the event loop
     427                 :   // from the time we were created) then we are open. Otherwise check the
     428                 :   // currently running transaction to see if it's the same. We only allow other
     429                 :   // requests to be made if this transaction is currently running.
     430            8309 :   if (mReadyState == IDBTransaction::LOADING) {
     431            8281 :     if (mCreating) {
     432              42 :       return true;
     433                 :     }
     434                 : 
     435            8239 :     if (AsyncConnectionHelper::GetCurrentTransaction() == this) {
     436            8239 :       return true;
     437                 :     }
     438                 :   }
     439                 : 
     440              28 :   return false;
     441                 : }
     442                 : 
     443                 : already_AddRefed<IDBObjectStore>
     444             679 : IDBTransaction::GetOrCreateObjectStore(const nsAString& aName,
     445                 :                                        ObjectStoreInfo* aObjectStoreInfo)
     446                 : {
     447             679 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     448             679 :   NS_ASSERTION(aObjectStoreInfo, "Null pointer!");
     449                 : 
     450            1358 :   nsRefPtr<IDBObjectStore> retval;
     451                 : 
     452             791 :   for (PRUint32 index = 0; index < mCreatedObjectStores.Length(); index++) {
     453             124 :     nsRefPtr<IDBObjectStore>& objectStore = mCreatedObjectStores[index];
     454             124 :     if (objectStore->Name() == aName) {
     455              12 :       retval = objectStore;
     456              12 :       return retval.forget();
     457                 :     }
     458                 :   }
     459                 : 
     460             667 :   retval = IDBObjectStore::Create(this, aObjectStoreInfo, mDatabaseInfo->id);
     461                 : 
     462             667 :   mCreatedObjectStores.AppendElement(retval);
     463                 : 
     464             667 :   return retval.forget();
     465                 : }
     466                 : 
     467                 : void
     468               0 : IDBTransaction::OnNewFileInfo(FileInfo* aFileInfo)
     469                 : {
     470               0 :   mCreatedFileInfos.AppendElement(aFileInfo);
     471               0 : }
     472                 : 
     473                 : void
     474             540 : IDBTransaction::ClearCreatedFileInfos()
     475                 : {
     476             540 :   mCreatedFileInfos.Clear();
     477             540 : }
     478                 : 
     479            1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(IDBTransaction)
     480                 : 
     481             519 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBTransaction,
     482                 :                                                   IDBWrapperCache)
     483             519 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mDatabase,
     484                 :                                                        nsIDOMEventTarget)
     485             519 :   NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(error)
     486             519 :   NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(complete)
     487             519 :   NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(abort)
     488                 : 
     489            1063 :   for (PRUint32 i = 0; i < tmp->mCreatedObjectStores.Length(); i++) {
     490             544 :     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCreatedObjectStores[i]");
     491                 :     cb.NoteXPCOMChild(static_cast<nsIIDBObjectStore*>(
     492             544 :                       tmp->mCreatedObjectStores[i].get()));
     493                 :   }
     494             519 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     495                 : 
     496             519 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBTransaction, IDBWrapperCache)
     497                 :   // Don't unlink mDatabase!
     498             519 :   NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(error)
     499             519 :   NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(complete)
     500             519 :   NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(abort)
     501                 : 
     502             519 :   tmp->mCreatedObjectStores.Clear();
     503                 : 
     504             519 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     505                 : 
     506           47771 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBTransaction)
     507           27694 :   NS_INTERFACE_MAP_ENTRY(nsIIDBTransaction)
     508           24495 :   NS_INTERFACE_MAP_ENTRY(nsIThreadObserver)
     509           24026 :   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBTransaction)
     510           23541 : NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache)
     511                 : 
     512           34997 : NS_IMPL_ADDREF_INHERITED(IDBTransaction, IDBWrapperCache)
     513           34997 : NS_IMPL_RELEASE_INHERITED(IDBTransaction, IDBWrapperCache)
     514                 : 
     515                 : DOMCI_DATA(IDBTransaction, IDBTransaction)
     516                 : 
     517               0 : NS_IMPL_EVENT_HANDLER(IDBTransaction, error);
     518             278 : NS_IMPL_EVENT_HANDLER(IDBTransaction, complete);
     519               8 : NS_IMPL_EVENT_HANDLER(IDBTransaction, abort);
     520                 : 
     521                 : NS_IMETHODIMP
     522              28 : IDBTransaction::GetDb(nsIIDBDatabase** aDB)
     523                 : {
     524              28 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     525                 : 
     526              28 :   NS_ADDREF(*aDB = mDatabase);
     527              28 :   return NS_OK;
     528                 : }
     529                 : 
     530                 : NS_IMETHODIMP
     531             118 : IDBTransaction::GetMode(nsAString& aMode)
     532                 : {
     533             118 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     534                 : 
     535             118 :   switch(mMode) {
     536                 :     case READ_ONLY:
     537              85 :       aMode.AssignLiteral("readonly");
     538              85 :       break;
     539                 :     case READ_WRITE:
     540               0 :       aMode.AssignLiteral("readwrite");
     541               0 :       break;
     542                 :     case VERSION_CHANGE:
     543              33 :       aMode.AssignLiteral("versionchange");
     544                 :   }
     545                 : 
     546             118 :   return NS_OK;
     547                 : }
     548                 : 
     549                 : NS_IMETHODIMP
     550             142 : IDBTransaction::GetObjectStoreNames(nsIDOMDOMStringList** aObjectStores)
     551                 : {
     552             142 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     553                 : 
     554             284 :   nsRefPtr<nsDOMStringList> list(new nsDOMStringList());
     555                 : 
     556             284 :   nsAutoTArray<nsString, 10> stackArray;
     557                 :   nsTArray<nsString>* arrayOfNames;
     558                 : 
     559             142 :   if (mMode == IDBTransaction::VERSION_CHANGE) {
     560             142 :     mDatabaseInfo->GetObjectStoreNames(stackArray);
     561                 : 
     562             142 :     arrayOfNames = &stackArray;
     563                 :   }
     564                 :   else {
     565               0 :     arrayOfNames = &mObjectStoreNames;
     566                 :   }
     567                 : 
     568             142 :   PRUint32 count = arrayOfNames->Length();
     569            1173 :   for (PRUint32 index = 0; index < count; index++) {
     570            1031 :     NS_ENSURE_TRUE(list->Add(arrayOfNames->ElementAt(index)),
     571                 :                    NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
     572                 :   }
     573                 : 
     574             142 :   list.forget(aObjectStores);
     575             142 :   return NS_OK;
     576                 : }
     577                 : 
     578                 : NS_IMETHODIMP
     579             481 : IDBTransaction::ObjectStore(const nsAString& aName,
     580                 :                             nsIIDBObjectStore** _retval)
     581                 : {
     582             481 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     583                 : 
     584             481 :   if (!IsOpen()) {
     585               2 :     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
     586                 :   }
     587                 : 
     588             479 :   ObjectStoreInfo* info = nsnull;
     589                 : 
     590             952 :   if (mMode == IDBTransaction::VERSION_CHANGE ||
     591             473 :       mObjectStoreNames.Contains(aName)) {
     592             479 :     info = mDatabaseInfo->GetObjectStore(aName);
     593                 :   }
     594                 : 
     595             479 :   if (!info) {
     596               1 :     return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR;
     597                 :   }
     598                 : 
     599             956 :   nsRefPtr<IDBObjectStore> objectStore = GetOrCreateObjectStore(aName, info);
     600             478 :   NS_ENSURE_TRUE(objectStore, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
     601                 : 
     602             478 :   objectStore.forget(_retval);
     603             478 :   return NS_OK;
     604                 : }
     605                 : 
     606                 : NS_IMETHODIMP
     607              14 : IDBTransaction::Abort()
     608                 : {
     609              14 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     610                 : 
     611                 :   // We can't use IsOpen here since we need it to be possible to call Abort()
     612                 :   // even from outside of transaction callbacks.
     613              14 :   if (mReadyState != IDBTransaction::INITIAL &&
     614                 :       mReadyState != IDBTransaction::LOADING) {
     615               4 :     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
     616                 :   }
     617                 : 
     618              10 :   bool needToCommitOrRollback = mReadyState == IDBTransaction::INITIAL;
     619                 : 
     620              10 :   mAborted = true;
     621              10 :   mReadyState = IDBTransaction::DONE;
     622                 : 
     623                 :   if (Mode() == IDBTransaction::VERSION_CHANGE) {
     624                 :     // If a version change transaction is aborted, the db must be closed
     625                 :     mDatabase->Close();
     626                 :   }
     627                 : 
     628                 :   // Fire the abort event if there are no outstanding requests. Otherwise the
     629                 :   // abort event will be fired when all outstanding requests finish.
     630              10 :   if (needToCommitOrRollback) {
     631               1 :     return CommitOrRollback();
     632                 :   }
     633                 : 
     634               9 :   return NS_OK;
     635                 : }
     636                 : 
     637                 : nsresult
     638            4777 : IDBTransaction::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
     639                 : {
     640            4777 :   aVisitor.mCanHandle = true;
     641            4777 :   aVisitor.mParentTarget = mDatabase;
     642            4777 :   return NS_OK;
     643                 : }
     644                 : 
     645                 : // XXX Once nsIThreadObserver gets split this method will disappear.
     646                 : NS_IMETHODIMP
     647               0 : IDBTransaction::OnDispatchedEvent(nsIThreadInternal* aThread)
     648                 : {
     649               0 :   NS_NOTREACHED("Don't call me!");
     650               0 :   return NS_ERROR_NOT_IMPLEMENTED;
     651                 : }
     652                 : 
     653                 : NS_IMETHODIMP
     654               1 : IDBTransaction::OnProcessNextEvent(nsIThreadInternal* aThread,
     655                 :                                    bool aMayWait,
     656                 :                                    PRUint32 aRecursionDepth)
     657                 : {
     658               1 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     659               1 :   NS_ASSERTION(aRecursionDepth > mCreatedRecursionDepth,
     660                 :                "Should be impossible!");
     661               1 :   NS_ASSERTION(mCreating, "Should be true!");
     662               1 :   return NS_OK;
     663                 : }
     664                 : 
     665                 : NS_IMETHODIMP
     666             470 : IDBTransaction::AfterProcessNextEvent(nsIThreadInternal* aThread,
     667                 :                                       PRUint32 aRecursionDepth)
     668                 : {
     669             470 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     670             470 :   NS_ASSERTION(aThread, "This should never be null!");
     671             470 :   NS_ASSERTION(aRecursionDepth >= mCreatedRecursionDepth,
     672                 :                "Should be impossible!");
     673             470 :   NS_ASSERTION(mCreating, "Should be true!");
     674                 : 
     675             470 :   if (aRecursionDepth == mCreatedRecursionDepth) {
     676                 :     // We're back at the event loop, no longer newborn.
     677             469 :     mCreating = false;
     678                 : 
     679                 :     // Maybe set the readyState to DONE if there were no requests generated.
     680             469 :     if (mReadyState == IDBTransaction::INITIAL) {
     681              10 :       mReadyState = IDBTransaction::DONE;
     682                 : 
     683              10 :       if (NS_FAILED(CommitOrRollback())) {
     684               0 :         NS_WARNING("Failed to commit!");
     685                 :       }
     686                 :     }
     687                 : 
     688                 :     // No longer need to observe thread events.
     689             469 :     if(NS_FAILED(aThread->RemoveObserver(this))) {
     690               0 :       NS_ERROR("Failed to remove observer!");
     691                 :     }
     692                 :   }
     693                 : 
     694             470 :   return NS_OK;
     695                 : }
     696                 : 
     697             540 : CommitHelper::CommitHelper(
     698                 :               IDBTransaction* aTransaction,
     699                 :               IDBTransactionListener* aListener,
     700                 :               const nsTArray<nsRefPtr<IDBObjectStore> >& aUpdatedObjectStores)
     701                 : : mTransaction(aTransaction),
     702                 :   mListener(aListener),
     703             540 :   mAborted(!!aTransaction->mAborted)
     704                 : {
     705             540 :   mConnection.swap(aTransaction->mConnection);
     706             540 :   mUpdateFileRefcountFunction.swap(aTransaction->mUpdateFileRefcountFunction);
     707                 : 
     708            1084 :   for (PRUint32 i = 0; i < aUpdatedObjectStores.Length(); i++) {
     709             544 :     ObjectStoreInfo* info = aUpdatedObjectStores[i]->Info();
     710             544 :     if (info->comittedAutoIncrementId != info->nextAutoIncrementId) {
     711             277 :       mAutoIncrementObjectStores.AppendElement(aUpdatedObjectStores[i]);
     712                 :     }
     713                 :   }
     714             540 : }
     715                 : 
     716             540 : CommitHelper::~CommitHelper()
     717                 : {
     718             540 : }
     719                 : 
     720            7020 : NS_IMPL_THREADSAFE_ISUPPORTS1(CommitHelper, nsIRunnable)
     721                 : 
     722                 : NS_IMETHODIMP
     723            1080 : CommitHelper::Run()
     724                 : {
     725            1080 :   if (NS_IsMainThread()) {
     726             540 :     NS_ASSERTION(mDoomedObjects.IsEmpty(), "Didn't release doomed objects!");
     727                 : 
     728             540 :     mTransaction->mReadyState = IDBTransaction::DONE;
     729                 : 
     730                 :     // Release file infos on the main thread, so they will eventually get
     731                 :     // destroyed on correct thread.
     732             540 :     mTransaction->ClearCreatedFileInfos();
     733             540 :     if (mUpdateFileRefcountFunction) {
     734             367 :       mUpdateFileRefcountFunction->ClearFileInfoEntries();
     735             367 :       mUpdateFileRefcountFunction = nsnull;
     736                 :     }
     737                 : 
     738            1080 :     nsCOMPtr<nsIDOMEvent> event;
     739             540 :     if (mAborted) {
     740              10 :       if (mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE) {
     741                 :         // This will make the database take a snapshot of it's DatabaseInfo
     742               1 :         mTransaction->Database()->Close();
     743                 :         // Then remove the info from the hash as it contains invalid data.
     744               1 :         DatabaseInfo::Remove(mTransaction->Database()->Id());
     745                 :       }
     746                 : 
     747              10 :       event = CreateGenericEvent(NS_LITERAL_STRING(ABORT_EVT_STR),
     748              10 :                                  eDoesBubble, eNotCancelable);
     749                 :     }
     750                 :     else {
     751             530 :       event = CreateGenericEvent(NS_LITERAL_STRING(COMPLETE_EVT_STR),
     752             530 :                                  eDoesNotBubble, eNotCancelable);
     753                 :     }
     754             540 :     NS_ENSURE_TRUE(event, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
     755                 : 
     756                 :     bool dummy;
     757             540 :     if (NS_FAILED(mTransaction->DispatchEvent(event, &dummy))) {
     758               0 :       NS_WARNING("Dispatch failed!");
     759                 :     }
     760                 : 
     761                 : #ifdef DEBUG
     762             540 :     mTransaction->mFiredCompleteOrAbort = true;
     763                 : #endif
     764                 : 
     765                 :     // Tell the listener (if we have one) that we're done
     766             540 :     if (mListener) {
     767              71 :       mListener->NotifyTransactionComplete(mTransaction);
     768                 :     }
     769                 : 
     770             540 :     mTransaction = nsnull;
     771                 : 
     772             540 :     return NS_OK;
     773                 :   }
     774                 : 
     775             540 :   IDBDatabase* database = mTransaction->Database();
     776             540 :   if (database->IsInvalidated()) {
     777               0 :     mAborted = true;
     778                 :   }
     779                 : 
     780             540 :   if (mConnection) {
     781             529 :     IndexedDatabaseManager::SetCurrentWindow(database->GetOwner());
     782                 : 
     783             891 :     if (!mAborted && mUpdateFileRefcountFunction &&
     784             362 :         NS_FAILED(mUpdateFileRefcountFunction->UpdateDatabase(mConnection))) {
     785               0 :       mAborted = true;
     786                 :     }
     787                 : 
     788             529 :     if (!mAborted && NS_FAILED(WriteAutoIncrementCounts())) {
     789               0 :       mAborted = true;
     790                 :     }
     791                 : 
     792             529 :     if (!mAborted) {
     793            1040 :       NS_NAMED_LITERAL_CSTRING(release, "COMMIT TRANSACTION");
     794             520 :       if (NS_SUCCEEDED(mConnection->ExecuteSimpleSQL(release))) {
     795             520 :         if (mUpdateFileRefcountFunction) {
     796             362 :           mUpdateFileRefcountFunction->UpdateFileInfos();
     797                 :         }
     798             520 :         CommitAutoIncrementCounts();
     799                 :       }
     800                 :       else {
     801               0 :         mAborted = true;
     802                 :       }
     803                 :     }
     804                 : 
     805             529 :     if (mAborted) {
     806               9 :       RevertAutoIncrementCounts();
     807              18 :       NS_NAMED_LITERAL_CSTRING(rollback, "ROLLBACK TRANSACTION");
     808               9 :       if (NS_FAILED(mConnection->ExecuteSimpleSQL(rollback))) {
     809               0 :         NS_WARNING("Failed to rollback transaction!");
     810                 :       }
     811                 :     }
     812                 :   }
     813                 : 
     814             540 :   mDoomedObjects.Clear();
     815                 : 
     816             540 :   if (mConnection) {
     817             529 :     if (mUpdateFileRefcountFunction) {
     818             367 :       nsresult rv = mConnection->RemoveFunction(
     819             367 :         NS_LITERAL_CSTRING("update_refcount"));
     820             367 :       if (NS_FAILED(rv)) {
     821               0 :         NS_WARNING("Failed to remove function!");
     822                 :       }
     823                 :     }
     824                 : 
     825             529 :     mConnection->Close();
     826             529 :     mConnection = nsnull;
     827                 : 
     828             529 :     IndexedDatabaseManager::SetCurrentWindow(nsnull);
     829                 :   }
     830                 : 
     831             540 :   return NS_OK;
     832                 : }
     833                 : 
     834                 : nsresult
     835             520 : CommitHelper::WriteAutoIncrementCounts()
     836                 : {
     837            1040 :   nsCOMPtr<mozIStorageStatement> stmt;
     838                 :   nsresult rv;
     839             794 :   for (PRUint32 i = 0; i < mAutoIncrementObjectStores.Length(); i++) {
     840             274 :     ObjectStoreInfo* info = mAutoIncrementObjectStores[i]->Info();
     841             274 :     if (!stmt) {
     842             546 :       rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
     843                 :         "UPDATE object_store SET auto_increment = :ai "
     844             546 :         "WHERE id = :osid;"), getter_AddRefs(stmt));
     845             273 :       NS_ENSURE_SUCCESS(rv, rv);
     846                 :     }
     847                 :     else {
     848               1 :       stmt->Reset();
     849                 :     }
     850                 : 
     851             274 :     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), info->id);
     852             274 :     NS_ENSURE_SUCCESS(rv, rv);
     853                 :     
     854             548 :     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("ai"),
     855             274 :                                info->nextAutoIncrementId);
     856             274 :     NS_ENSURE_SUCCESS(rv, rv);
     857                 : 
     858             274 :     rv = stmt->Execute();
     859             274 :     NS_ENSURE_SUCCESS(rv, rv);
     860                 :   }
     861                 :   
     862             520 :   return NS_OK;
     863                 : }
     864                 : 
     865                 : void
     866             520 : CommitHelper::CommitAutoIncrementCounts()
     867                 : {
     868             794 :   for (PRUint32 i = 0; i < mAutoIncrementObjectStores.Length(); i++) {
     869             274 :     ObjectStoreInfo* info = mAutoIncrementObjectStores[i]->Info();
     870             274 :     info->comittedAutoIncrementId = info->nextAutoIncrementId;
     871                 :   }
     872             520 : }
     873                 : 
     874                 : void
     875               9 : CommitHelper::RevertAutoIncrementCounts()
     876                 : {
     877              12 :   for (PRUint32 i = 0; i < mAutoIncrementObjectStores.Length(); i++) {
     878               3 :     ObjectStoreInfo* info = mAutoIncrementObjectStores[i]->Info();
     879               3 :     info->nextAutoIncrementId = info->comittedAutoIncrementId;
     880                 :   }
     881               9 : }
     882                 : 
     883                 : nsresult
     884             367 : UpdateRefcountFunction::Init()
     885                 : {
     886             367 :   NS_ENSURE_TRUE(mFileInfoEntries.Init(), NS_ERROR_OUT_OF_MEMORY);
     887                 : 
     888             367 :   return NS_OK;
     889                 : }
     890                 : 
     891            3303 : NS_IMPL_THREADSAFE_ISUPPORTS1(UpdateRefcountFunction, mozIStorageFunction)
     892                 : 
     893                 : NS_IMETHODIMP
     894               0 : UpdateRefcountFunction::OnFunctionCall(mozIStorageValueArray* aValues,
     895                 :                                        nsIVariant** _retval)
     896                 : {
     897               0 :   *_retval = nsnull;
     898                 : 
     899                 :   PRUint32 numEntries;
     900               0 :   nsresult rv = aValues->GetNumEntries(&numEntries);
     901               0 :   NS_ENSURE_SUCCESS(rv, rv);
     902               0 :   NS_ASSERTION(numEntries == 2, "unexpected number of arguments");
     903                 : 
     904                 : #ifdef DEBUG
     905               0 :   PRInt32 type1 = mozIStorageValueArray::VALUE_TYPE_NULL;
     906               0 :   aValues->GetTypeOfIndex(0, &type1);
     907                 : 
     908               0 :   PRInt32 type2 = mozIStorageValueArray::VALUE_TYPE_NULL;
     909               0 :   aValues->GetTypeOfIndex(1, &type2);
     910                 : 
     911               0 :   NS_ASSERTION(!(type1 == mozIStorageValueArray::VALUE_TYPE_NULL &&
     912                 :                  type2 == mozIStorageValueArray::VALUE_TYPE_NULL),
     913                 :                "Shouldn't be called!");
     914                 : #endif
     915                 : 
     916               0 :   rv = ProcessValue(aValues, 0, eDecrement);
     917               0 :   NS_ENSURE_SUCCESS(rv, rv);
     918                 : 
     919               0 :   rv = ProcessValue(aValues, 1, eIncrement);
     920               0 :   NS_ENSURE_SUCCESS(rv, rv);
     921                 : 
     922               0 :   return NS_OK;
     923                 : }
     924                 : 
     925                 : nsresult
     926               0 : UpdateRefcountFunction::ProcessValue(mozIStorageValueArray* aValues,
     927                 :                                      PRInt32 aIndex,
     928                 :                                      UpdateType aUpdateType)
     929                 : {
     930                 :   PRInt32 type;
     931               0 :   aValues->GetTypeOfIndex(aIndex, &type);
     932               0 :   if (type == mozIStorageValueArray::VALUE_TYPE_NULL) {
     933               0 :     return NS_OK;
     934                 :   }
     935                 : 
     936               0 :   nsString ids;
     937               0 :   aValues->GetString(aIndex, ids);
     938                 : 
     939               0 :   nsTArray<PRInt64> fileIds;
     940               0 :   nsresult rv = IDBObjectStore::ConvertFileIdsToArray(ids, fileIds);
     941               0 :   NS_ENSURE_SUCCESS(rv, rv);
     942                 : 
     943               0 :   for (PRUint32 i = 0; i < fileIds.Length(); i++) {
     944               0 :     PRInt64 id = fileIds.ElementAt(i);
     945                 : 
     946                 :     FileInfoEntry* entry;
     947               0 :     if (!mFileInfoEntries.Get(id, &entry)) {
     948               0 :       nsRefPtr<FileInfo> fileInfo = mFileManager->GetFileInfo(id);
     949               0 :       NS_ASSERTION(fileInfo, "Shouldn't be null!");
     950                 : 
     951               0 :       nsAutoPtr<FileInfoEntry> newEntry(new FileInfoEntry(fileInfo));
     952               0 :       if (!mFileInfoEntries.Put(id, newEntry)) {
     953               0 :         NS_WARNING("Out of memory?");
     954               0 :         return NS_ERROR_OUT_OF_MEMORY;
     955                 :       }
     956               0 :       entry = newEntry.forget();
     957                 :     }
     958                 : 
     959               0 :     switch (aUpdateType) {
     960                 :       case eIncrement:
     961               0 :         entry->mDelta++;
     962               0 :         break;
     963                 :       case eDecrement:
     964               0 :         entry->mDelta--;
     965               0 :         break;
     966                 :       default:
     967               0 :         NS_NOTREACHED("Unknown update type!");
     968                 :     }
     969                 :   }
     970                 : 
     971               0 :   return NS_OK;
     972                 : }
     973                 : 
     974                 : PLDHashOperator
     975               0 : UpdateRefcountFunction::DatabaseUpdateCallback(const PRUint64& aKey,
     976                 :                                                FileInfoEntry* aValue,
     977                 :                                                void* aUserArg)
     978                 : {
     979               0 :   if (!aValue->mDelta) {
     980               0 :     return PL_DHASH_NEXT;
     981                 :   }
     982                 : 
     983                 :   DatabaseUpdateFunction* function =
     984               0 :     static_cast<DatabaseUpdateFunction*>(aUserArg);
     985                 : 
     986               0 :   if (!function->Update(aKey, aValue->mDelta)) {
     987               0 :     return PL_DHASH_STOP;
     988                 :   }
     989                 : 
     990               0 :   return PL_DHASH_NEXT;
     991                 : }
     992                 : 
     993                 : PLDHashOperator
     994               0 : UpdateRefcountFunction::FileInfoUpdateCallback(const PRUint64& aKey,
     995                 :                                                FileInfoEntry* aValue,
     996                 :                                                void* aUserArg)
     997                 : {
     998               0 :   if (aValue->mDelta) {
     999               0 :     aValue->mFileInfo->UpdateDBRefs(aValue->mDelta);
    1000                 :   }
    1001                 : 
    1002               0 :   return PL_DHASH_NEXT;
    1003                 : }
    1004                 : 
    1005                 : bool
    1006               0 : UpdateRefcountFunction::DatabaseUpdateFunction::Update(PRInt64 aId,
    1007                 :                                                        PRInt32 aDelta)
    1008                 : {
    1009               0 :   nsresult rv = UpdateInternal(aId, aDelta);
    1010               0 :   if (NS_FAILED(rv)) {
    1011               0 :     mErrorCode = rv;
    1012               0 :     return false;
    1013                 :   }
    1014                 : 
    1015               0 :   return true;
    1016                 : }
    1017                 : 
    1018                 : nsresult
    1019               0 : UpdateRefcountFunction::DatabaseUpdateFunction::UpdateInternal(PRInt64 aId,
    1020                 :                                                                PRInt32 aDelta)
    1021                 : {
    1022                 :   nsresult rv;
    1023                 : 
    1024               0 :   if (!mUpdateStatement) {
    1025               0 :     rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
    1026                 :       "UPDATE file SET refcount = refcount + :delta WHERE id = :id"
    1027               0 :     ), getter_AddRefs(mUpdateStatement));
    1028               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1029                 :   }
    1030                 : 
    1031               0 :   mozStorageStatementScoper updateScoper(mUpdateStatement);
    1032                 : 
    1033               0 :   rv = mUpdateStatement->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta);
    1034               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1035                 : 
    1036               0 :   rv = mUpdateStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId);
    1037               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1038                 : 
    1039               0 :   rv = mUpdateStatement->Execute();
    1040               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1041                 : 
    1042                 :   PRInt32 rows;
    1043               0 :   rv = mConnection->GetAffectedRows(&rows);
    1044               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1045                 : 
    1046               0 :   if (rows > 0) {
    1047               0 :     return NS_OK;
    1048                 :   }
    1049                 : 
    1050               0 :   if (!mInsertStatement) {
    1051               0 :     rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
    1052                 :       "INSERT INTO file (id, refcount) VALUES(:id, :delta)"
    1053               0 :     ), getter_AddRefs(mInsertStatement));
    1054               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1055                 :   }
    1056                 : 
    1057               0 :   mozStorageStatementScoper insertScoper(mInsertStatement);
    1058                 : 
    1059               0 :   rv = mInsertStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId);
    1060               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1061                 : 
    1062               0 :   rv = mInsertStatement->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta);
    1063               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1064                 : 
    1065               0 :   rv = mInsertStatement->Execute();
    1066               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1067                 : 
    1068               0 :   return NS_OK;
    1069            4392 : }

Generated by: LCOV version 1.7