dom/indexedDB/IDBTransaction.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "base/basictypes.h"
michael@0 8
michael@0 9 #include "IDBTransaction.h"
michael@0 10
michael@0 11 #include "nsIAppShell.h"
michael@0 12 #include "nsIScriptContext.h"
michael@0 13
michael@0 14 #include "mozilla/dom/quota/QuotaManager.h"
michael@0 15 #include "mozilla/storage.h"
michael@0 16 #include "nsDOMClassInfoID.h"
michael@0 17 #include "mozilla/dom/DOMStringList.h"
michael@0 18 #include "mozilla/EventDispatcher.h"
michael@0 19 #include "nsPIDOMWindow.h"
michael@0 20 #include "nsProxyRelease.h"
michael@0 21 #include "nsThreadUtils.h"
michael@0 22 #include "nsWidgetsCID.h"
michael@0 23
michael@0 24 #include "AsyncConnectionHelper.h"
michael@0 25 #include "DatabaseInfo.h"
michael@0 26 #include "IDBCursor.h"
michael@0 27 #include "IDBEvents.h"
michael@0 28 #include "IDBFactory.h"
michael@0 29 #include "IDBObjectStore.h"
michael@0 30 #include "IndexedDatabaseManager.h"
michael@0 31 #include "ProfilerHelpers.h"
michael@0 32 #include "ReportInternalError.h"
michael@0 33 #include "TransactionThreadPool.h"
michael@0 34
michael@0 35 #include "ipc/IndexedDBChild.h"
michael@0 36
michael@0 37 #define SAVEPOINT_NAME "savepoint"
michael@0 38
michael@0 39 using namespace mozilla;
michael@0 40 using namespace mozilla::dom;
michael@0 41 USING_INDEXEDDB_NAMESPACE
michael@0 42 using mozilla::dom::quota::QuotaManager;
michael@0 43 using mozilla::ErrorResult;
michael@0 44
michael@0 45 namespace {
michael@0 46
michael@0 47 NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
michael@0 48
michael@0 49 #ifdef MOZ_ENABLE_PROFILER_SPS
michael@0 50 uint64_t gNextTransactionSerialNumber = 1;
michael@0 51 #endif
michael@0 52
michael@0 53 PLDHashOperator
michael@0 54 DoomCachedStatements(const nsACString& aQuery,
michael@0 55 nsCOMPtr<mozIStorageStatement>& aStatement,
michael@0 56 void* aUserArg)
michael@0 57 {
michael@0 58 CommitHelper* helper = static_cast<CommitHelper*>(aUserArg);
michael@0 59 helper->AddDoomedObject(aStatement);
michael@0 60 return PL_DHASH_REMOVE;
michael@0 61 }
michael@0 62
michael@0 63 // This runnable doesn't actually do anything beyond "prime the pump" and get
michael@0 64 // transactions in the right order on the transaction thread pool.
michael@0 65 class StartTransactionRunnable : public nsIRunnable
michael@0 66 {
michael@0 67 public:
michael@0 68 NS_DECL_ISUPPORTS
michael@0 69
michael@0 70 NS_IMETHOD Run()
michael@0 71 {
michael@0 72 // NOP
michael@0 73 return NS_OK;
michael@0 74 }
michael@0 75 };
michael@0 76
michael@0 77 // Could really use those NS_REFCOUNTING_HAHA_YEAH_RIGHT macros here.
michael@0 78 NS_IMETHODIMP_(MozExternalRefCountType) StartTransactionRunnable::AddRef()
michael@0 79 {
michael@0 80 return 2;
michael@0 81 }
michael@0 82
michael@0 83 NS_IMETHODIMP_(MozExternalRefCountType) StartTransactionRunnable::Release()
michael@0 84 {
michael@0 85 return 1;
michael@0 86 }
michael@0 87
michael@0 88 NS_IMPL_QUERY_INTERFACE(StartTransactionRunnable, nsIRunnable)
michael@0 89
michael@0 90 } // anonymous namespace
michael@0 91
michael@0 92
michael@0 93 // static
michael@0 94 already_AddRefed<IDBTransaction>
michael@0 95 IDBTransaction::CreateInternal(IDBDatabase* aDatabase,
michael@0 96 const Sequence<nsString>& aObjectStoreNames,
michael@0 97 Mode aMode,
michael@0 98 bool aDispatchDelayed,
michael@0 99 bool aIsVersionChangeTransactionChild)
michael@0 100 {
michael@0 101 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 102 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess() || !aDispatchDelayed,
michael@0 103 "No support for delayed-dispatch transactions in child "
michael@0 104 "process!");
michael@0 105 NS_ASSERTION(!aIsVersionChangeTransactionChild ||
michael@0 106 (!IndexedDatabaseManager::IsMainProcess() &&
michael@0 107 aMode == IDBTransaction::VERSION_CHANGE),
michael@0 108 "Busted logic!");
michael@0 109
michael@0 110 nsRefPtr<IDBTransaction> transaction = new IDBTransaction(aDatabase);
michael@0 111
michael@0 112 transaction->SetScriptOwner(aDatabase->GetScriptOwner());
michael@0 113 transaction->mDatabase = aDatabase;
michael@0 114 transaction->mMode = aMode;
michael@0 115 transaction->mDatabaseInfo = aDatabase->Info();
michael@0 116 transaction->mObjectStoreNames.AppendElements(aObjectStoreNames);
michael@0 117 transaction->mObjectStoreNames.Sort();
michael@0 118
michael@0 119 IndexedDBTransactionChild* actor = nullptr;
michael@0 120
michael@0 121 if (IndexedDatabaseManager::IsMainProcess()) {
michael@0 122 if (aMode != IDBTransaction::VERSION_CHANGE) {
michael@0 123 TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate();
michael@0 124 NS_ENSURE_TRUE(pool, nullptr);
michael@0 125
michael@0 126 static StartTransactionRunnable sStartTransactionRunnable;
michael@0 127 pool->Dispatch(transaction, &sStartTransactionRunnable, false, nullptr);
michael@0 128 }
michael@0 129 }
michael@0 130 else if (!aIsVersionChangeTransactionChild) {
michael@0 131 IndexedDBDatabaseChild* dbActor = aDatabase->GetActorChild();
michael@0 132 NS_ASSERTION(dbActor, "Must have an actor here!");
michael@0 133
michael@0 134 ipc::NormalTransactionParams params;
michael@0 135 params.names().AppendElements(aObjectStoreNames);
michael@0 136 params.mode() = aMode;
michael@0 137
michael@0 138 actor = new IndexedDBTransactionChild();
michael@0 139
michael@0 140 dbActor->SendPIndexedDBTransactionConstructor(actor, params);
michael@0 141 }
michael@0 142
michael@0 143 if (!aDispatchDelayed) {
michael@0 144 nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
michael@0 145 NS_ENSURE_TRUE(appShell, nullptr);
michael@0 146
michael@0 147 nsresult rv = appShell->RunBeforeNextEvent(transaction);
michael@0 148 NS_ENSURE_SUCCESS(rv, nullptr);
michael@0 149
michael@0 150 transaction->mCreating = true;
michael@0 151 }
michael@0 152
michael@0 153 if (actor) {
michael@0 154 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
michael@0 155 actor->SetTransaction(transaction);
michael@0 156 }
michael@0 157
michael@0 158 return transaction.forget();
michael@0 159 }
michael@0 160
michael@0 161 IDBTransaction::IDBTransaction(IDBDatabase* aDatabase)
michael@0 162 : IDBWrapperCache(aDatabase),
michael@0 163 mReadyState(IDBTransaction::INITIAL),
michael@0 164 mMode(IDBTransaction::READ_ONLY),
michael@0 165 mPendingRequests(0),
michael@0 166 mSavepointCount(0),
michael@0 167 mActorChild(nullptr),
michael@0 168 mActorParent(nullptr),
michael@0 169 mAbortCode(NS_OK),
michael@0 170 #ifdef MOZ_ENABLE_PROFILER_SPS
michael@0 171 mSerialNumber(gNextTransactionSerialNumber++),
michael@0 172 #endif
michael@0 173 mCreating(false)
michael@0 174 #ifdef DEBUG
michael@0 175 , mFiredCompleteOrAbort(false)
michael@0 176 #endif
michael@0 177 {
michael@0 178 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 179 }
michael@0 180
michael@0 181 IDBTransaction::~IDBTransaction()
michael@0 182 {
michael@0 183 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 184 NS_ASSERTION(!mPendingRequests, "Should have no pending requests here!");
michael@0 185 NS_ASSERTION(!mSavepointCount, "Should have released them all!");
michael@0 186 NS_ASSERTION(!mConnection, "Should have called CommitOrRollback!");
michael@0 187 NS_ASSERTION(!mCreating, "Should have been cleared already!");
michael@0 188 NS_ASSERTION(mFiredCompleteOrAbort, "Should have fired event!");
michael@0 189
michael@0 190 NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!");
michael@0 191 if (mActorChild) {
michael@0 192 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
michael@0 193 mActorChild->Send__delete__(mActorChild);
michael@0 194 NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!");
michael@0 195 }
michael@0 196 }
michael@0 197
michael@0 198 void
michael@0 199 IDBTransaction::OnNewRequest()
michael@0 200 {
michael@0 201 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 202 if (!mPendingRequests) {
michael@0 203 NS_ASSERTION(mReadyState == IDBTransaction::INITIAL,
michael@0 204 "Reusing a transaction!");
michael@0 205 mReadyState = IDBTransaction::LOADING;
michael@0 206 }
michael@0 207 ++mPendingRequests;
michael@0 208 }
michael@0 209
michael@0 210 void
michael@0 211 IDBTransaction::OnRequestFinished()
michael@0 212 {
michael@0 213 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 214 NS_ASSERTION(mPendingRequests, "Mismatched calls!");
michael@0 215 --mPendingRequests;
michael@0 216 if (!mPendingRequests) {
michael@0 217 NS_ASSERTION(NS_FAILED(mAbortCode) || mReadyState == IDBTransaction::LOADING,
michael@0 218 "Bad state!");
michael@0 219 mReadyState = IDBTransaction::COMMITTING;
michael@0 220 CommitOrRollback();
michael@0 221 }
michael@0 222 }
michael@0 223
michael@0 224 void
michael@0 225 IDBTransaction::OnRequestDisconnected()
michael@0 226 {
michael@0 227 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 228 NS_ASSERTION(mPendingRequests, "Mismatched calls!");
michael@0 229 --mPendingRequests;
michael@0 230 }
michael@0 231
michael@0 232 void
michael@0 233 IDBTransaction::RemoveObjectStore(const nsAString& aName)
michael@0 234 {
michael@0 235 NS_ASSERTION(mMode == IDBTransaction::VERSION_CHANGE,
michael@0 236 "Only remove object stores on VERSION_CHANGE transactions");
michael@0 237
michael@0 238 mDatabaseInfo->RemoveObjectStore(aName);
michael@0 239
michael@0 240 for (uint32_t i = 0; i < mCreatedObjectStores.Length(); i++) {
michael@0 241 if (mCreatedObjectStores[i]->Name() == aName) {
michael@0 242 nsRefPtr<IDBObjectStore> objectStore = mCreatedObjectStores[i];
michael@0 243 mCreatedObjectStores.RemoveElementAt(i);
michael@0 244 mDeletedObjectStores.AppendElement(objectStore);
michael@0 245 break;
michael@0 246 }
michael@0 247 }
michael@0 248 }
michael@0 249
michael@0 250 void
michael@0 251 IDBTransaction::SetTransactionListener(IDBTransactionListener* aListener)
michael@0 252 {
michael@0 253 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 254 NS_ASSERTION(!mListener, "Shouldn't already have a listener!");
michael@0 255 mListener = aListener;
michael@0 256 }
michael@0 257
michael@0 258 nsresult
michael@0 259 IDBTransaction::CommitOrRollback()
michael@0 260 {
michael@0 261 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 262
michael@0 263 if (!IndexedDatabaseManager::IsMainProcess()) {
michael@0 264 if (mActorChild) {
michael@0 265 mActorChild->SendAllRequestsFinished();
michael@0 266 }
michael@0 267
michael@0 268 return NS_OK;
michael@0 269 }
michael@0 270
michael@0 271 nsRefPtr<CommitHelper> helper =
michael@0 272 new CommitHelper(this, mListener, mCreatedObjectStores);
michael@0 273
michael@0 274 TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate();
michael@0 275 NS_ENSURE_STATE(pool);
michael@0 276
michael@0 277 mCachedStatements.Enumerate(DoomCachedStatements, helper);
michael@0 278 NS_ASSERTION(!mCachedStatements.Count(), "Statements left!");
michael@0 279
michael@0 280 nsresult rv = pool->Dispatch(this, helper, true, helper);
michael@0 281 NS_ENSURE_SUCCESS(rv, rv);
michael@0 282
michael@0 283 return NS_OK;
michael@0 284 }
michael@0 285
michael@0 286 bool
michael@0 287 IDBTransaction::StartSavepoint()
michael@0 288 {
michael@0 289 NS_PRECONDITION(!NS_IsMainThread(), "Wrong thread!");
michael@0 290 NS_PRECONDITION(mConnection, "No connection!");
michael@0 291
michael@0 292 nsCOMPtr<mozIStorageStatement> stmt = GetCachedStatement(NS_LITERAL_CSTRING(
michael@0 293 "SAVEPOINT " SAVEPOINT_NAME
michael@0 294 ));
michael@0 295 NS_ENSURE_TRUE(stmt, false);
michael@0 296
michael@0 297 mozStorageStatementScoper scoper(stmt);
michael@0 298
michael@0 299 nsresult rv = stmt->Execute();
michael@0 300 NS_ENSURE_SUCCESS(rv, false);
michael@0 301
michael@0 302 if (IsWriteAllowed()) {
michael@0 303 mUpdateFileRefcountFunction->StartSavepoint();
michael@0 304 }
michael@0 305
michael@0 306 ++mSavepointCount;
michael@0 307
michael@0 308 return true;
michael@0 309 }
michael@0 310
michael@0 311 nsresult
michael@0 312 IDBTransaction::ReleaseSavepoint()
michael@0 313 {
michael@0 314 NS_PRECONDITION(!NS_IsMainThread(), "Wrong thread!");
michael@0 315 NS_PRECONDITION(mConnection, "No connection!");
michael@0 316
michael@0 317 NS_ASSERTION(mSavepointCount, "Mismatch!");
michael@0 318
michael@0 319 nsCOMPtr<mozIStorageStatement> stmt = GetCachedStatement(NS_LITERAL_CSTRING(
michael@0 320 "RELEASE SAVEPOINT " SAVEPOINT_NAME
michael@0 321 ));
michael@0 322 NS_ENSURE_TRUE(stmt, NS_OK);
michael@0 323
michael@0 324 mozStorageStatementScoper scoper(stmt);
michael@0 325
michael@0 326 nsresult rv = stmt->Execute();
michael@0 327 NS_ENSURE_SUCCESS(rv, NS_OK);
michael@0 328
michael@0 329 if (IsWriteAllowed()) {
michael@0 330 mUpdateFileRefcountFunction->ReleaseSavepoint();
michael@0 331 }
michael@0 332
michael@0 333 --mSavepointCount;
michael@0 334
michael@0 335 return NS_OK;
michael@0 336 }
michael@0 337
michael@0 338 void
michael@0 339 IDBTransaction::RollbackSavepoint()
michael@0 340 {
michael@0 341 NS_PRECONDITION(!NS_IsMainThread(), "Wrong thread!");
michael@0 342 NS_PRECONDITION(mConnection, "No connection!");
michael@0 343
michael@0 344 NS_ASSERTION(mSavepointCount == 1, "Mismatch!");
michael@0 345 mSavepointCount = 0;
michael@0 346
michael@0 347 nsCOMPtr<mozIStorageStatement> stmt = GetCachedStatement(NS_LITERAL_CSTRING(
michael@0 348 "ROLLBACK TO SAVEPOINT " SAVEPOINT_NAME
michael@0 349 ));
michael@0 350 NS_ENSURE_TRUE_VOID(stmt);
michael@0 351
michael@0 352 mozStorageStatementScoper scoper(stmt);
michael@0 353
michael@0 354 nsresult rv = stmt->Execute();
michael@0 355 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 356
michael@0 357 if (IsWriteAllowed()) {
michael@0 358 mUpdateFileRefcountFunction->RollbackSavepoint();
michael@0 359 }
michael@0 360 }
michael@0 361
michael@0 362 nsresult
michael@0 363 IDBTransaction::GetOrCreateConnection(mozIStorageConnection** aResult)
michael@0 364 {
michael@0 365 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
michael@0 366 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
michael@0 367
michael@0 368 PROFILER_LABEL("IndexedDB", "IDBTransaction::GetOrCreateConnection");
michael@0 369
michael@0 370 if (mDatabase->IsInvalidated()) {
michael@0 371 return NS_ERROR_NOT_AVAILABLE;
michael@0 372 }
michael@0 373
michael@0 374 if (!mConnection) {
michael@0 375 nsCOMPtr<mozIStorageConnection> connection =
michael@0 376 IDBFactory::GetConnection(mDatabase->FilePath(), mDatabase->Type(),
michael@0 377 mDatabase->Group(), mDatabase->Origin());
michael@0 378 NS_ENSURE_TRUE(connection, NS_ERROR_FAILURE);
michael@0 379
michael@0 380 nsresult rv;
michael@0 381
michael@0 382 nsRefPtr<UpdateRefcountFunction> function;
michael@0 383 nsCString beginTransaction;
michael@0 384 if (mMode != IDBTransaction::READ_ONLY) {
michael@0 385 function = new UpdateRefcountFunction(Database()->Manager());
michael@0 386 NS_ENSURE_TRUE(function, NS_ERROR_OUT_OF_MEMORY);
michael@0 387
michael@0 388 rv = connection->CreateFunction(
michael@0 389 NS_LITERAL_CSTRING("update_refcount"), 2, function);
michael@0 390 NS_ENSURE_SUCCESS(rv, rv);
michael@0 391
michael@0 392 beginTransaction.AssignLiteral("BEGIN IMMEDIATE TRANSACTION;");
michael@0 393 }
michael@0 394 else {
michael@0 395 beginTransaction.AssignLiteral("BEGIN TRANSACTION;");
michael@0 396 }
michael@0 397
michael@0 398 nsCOMPtr<mozIStorageStatement> stmt;
michael@0 399 rv = connection->CreateStatement(beginTransaction, getter_AddRefs(stmt));
michael@0 400 NS_ENSURE_SUCCESS(rv, rv);
michael@0 401
michael@0 402 rv = stmt->Execute();
michael@0 403 NS_ENSURE_SUCCESS(rv, rv);
michael@0 404
michael@0 405 function.swap(mUpdateFileRefcountFunction);
michael@0 406 connection.swap(mConnection);
michael@0 407 }
michael@0 408
michael@0 409 nsCOMPtr<mozIStorageConnection> result(mConnection);
michael@0 410 result.forget(aResult);
michael@0 411 return NS_OK;
michael@0 412 }
michael@0 413
michael@0 414 already_AddRefed<mozIStorageStatement>
michael@0 415 IDBTransaction::GetCachedStatement(const nsACString& aQuery)
michael@0 416 {
michael@0 417 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
michael@0 418 NS_ASSERTION(!aQuery.IsEmpty(), "Empty sql statement!");
michael@0 419 NS_ASSERTION(mConnection, "No connection!");
michael@0 420
michael@0 421 nsCOMPtr<mozIStorageStatement> stmt;
michael@0 422
michael@0 423 if (!mCachedStatements.Get(aQuery, getter_AddRefs(stmt))) {
michael@0 424 nsresult rv = mConnection->CreateStatement(aQuery, getter_AddRefs(stmt));
michael@0 425 #ifdef DEBUG
michael@0 426 if (NS_FAILED(rv)) {
michael@0 427 nsCString error;
michael@0 428 error.AppendLiteral("The statement `");
michael@0 429 error.Append(aQuery);
michael@0 430 error.AppendLiteral("` failed to compile with the error message `");
michael@0 431 nsCString msg;
michael@0 432 (void)mConnection->GetLastErrorString(msg);
michael@0 433 error.Append(msg);
michael@0 434 error.AppendLiteral("`.");
michael@0 435 NS_ERROR(error.get());
michael@0 436 }
michael@0 437 #endif
michael@0 438 NS_ENSURE_SUCCESS(rv, nullptr);
michael@0 439
michael@0 440 mCachedStatements.Put(aQuery, stmt);
michael@0 441 }
michael@0 442
michael@0 443 return stmt.forget();
michael@0 444 }
michael@0 445
michael@0 446 bool
michael@0 447 IDBTransaction::IsOpen() const
michael@0 448 {
michael@0 449 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 450
michael@0 451 // If we haven't started anything then we're open.
michael@0 452 if (mReadyState == IDBTransaction::INITIAL) {
michael@0 453 return true;
michael@0 454 }
michael@0 455
michael@0 456 // If we've already started then we need to check to see if we still have the
michael@0 457 // mCreating flag set. If we do (i.e. we haven't returned to the event loop
michael@0 458 // from the time we were created) then we are open. Otherwise check the
michael@0 459 // currently running transaction to see if it's the same. We only allow other
michael@0 460 // requests to be made if this transaction is currently running.
michael@0 461 if (mReadyState == IDBTransaction::LOADING) {
michael@0 462 if (mCreating) {
michael@0 463 return true;
michael@0 464 }
michael@0 465
michael@0 466 if (AsyncConnectionHelper::GetCurrentTransaction() == this) {
michael@0 467 return true;
michael@0 468 }
michael@0 469 }
michael@0 470
michael@0 471 return false;
michael@0 472 }
michael@0 473
michael@0 474 already_AddRefed<IDBObjectStore>
michael@0 475 IDBTransaction::GetOrCreateObjectStore(const nsAString& aName,
michael@0 476 ObjectStoreInfo* aObjectStoreInfo,
michael@0 477 bool aCreating)
michael@0 478 {
michael@0 479 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 480 NS_ASSERTION(aObjectStoreInfo, "Null pointer!");
michael@0 481 NS_ASSERTION(!aCreating || GetMode() == IDBTransaction::VERSION_CHANGE,
michael@0 482 "How else can we create here?!");
michael@0 483
michael@0 484 nsRefPtr<IDBObjectStore> retval;
michael@0 485
michael@0 486 for (uint32_t index = 0; index < mCreatedObjectStores.Length(); index++) {
michael@0 487 nsRefPtr<IDBObjectStore>& objectStore = mCreatedObjectStores[index];
michael@0 488 if (objectStore->Name() == aName) {
michael@0 489 retval = objectStore;
michael@0 490 return retval.forget();
michael@0 491 }
michael@0 492 }
michael@0 493
michael@0 494 retval = IDBObjectStore::Create(this, aObjectStoreInfo, mDatabaseInfo->id,
michael@0 495 aCreating);
michael@0 496
michael@0 497 mCreatedObjectStores.AppendElement(retval);
michael@0 498
michael@0 499 return retval.forget();
michael@0 500 }
michael@0 501
michael@0 502 already_AddRefed<FileInfo>
michael@0 503 IDBTransaction::GetFileInfo(nsIDOMBlob* aBlob)
michael@0 504 {
michael@0 505 nsRefPtr<FileInfo> fileInfo;
michael@0 506 mCreatedFileInfos.Get(aBlob, getter_AddRefs(fileInfo));
michael@0 507 return fileInfo.forget();
michael@0 508 }
michael@0 509
michael@0 510 void
michael@0 511 IDBTransaction::AddFileInfo(nsIDOMBlob* aBlob, FileInfo* aFileInfo)
michael@0 512 {
michael@0 513 mCreatedFileInfos.Put(aBlob, aFileInfo);
michael@0 514 }
michael@0 515
michael@0 516 void
michael@0 517 IDBTransaction::ClearCreatedFileInfos()
michael@0 518 {
michael@0 519 mCreatedFileInfos.Clear();
michael@0 520 }
michael@0 521
michael@0 522 nsresult
michael@0 523 IDBTransaction::AbortInternal(nsresult aAbortCode,
michael@0 524 already_AddRefed<DOMError> aError)
michael@0 525 {
michael@0 526 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 527
michael@0 528 nsRefPtr<DOMError> error = aError;
michael@0 529
michael@0 530 if (IsFinished()) {
michael@0 531 return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
michael@0 532 }
michael@0 533
michael@0 534 if (mActorChild) {
michael@0 535 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
michael@0 536 mActorChild->SendAbort(aAbortCode);
michael@0 537 }
michael@0 538
michael@0 539 bool needToCommitOrRollback = mReadyState == IDBTransaction::INITIAL;
michael@0 540
michael@0 541 mAbortCode = aAbortCode;
michael@0 542 mReadyState = IDBTransaction::DONE;
michael@0 543 mError = error.forget();
michael@0 544
michael@0 545 if (GetMode() == IDBTransaction::VERSION_CHANGE) {
michael@0 546 // If a version change transaction is aborted, we must revert the world
michael@0 547 // back to its previous state.
michael@0 548 mDatabase->RevertToPreviousState();
michael@0 549
michael@0 550 DatabaseInfo* dbInfo = mDatabase->Info();
michael@0 551
michael@0 552 for (uint32_t i = 0; i < mCreatedObjectStores.Length(); i++) {
michael@0 553 nsRefPtr<IDBObjectStore>& objectStore = mCreatedObjectStores[i];
michael@0 554 ObjectStoreInfo* info = dbInfo->GetObjectStore(objectStore->Name());
michael@0 555
michael@0 556 if (!info) {
michael@0 557 info = new ObjectStoreInfo(*objectStore->Info());
michael@0 558 info->indexes.Clear();
michael@0 559 }
michael@0 560
michael@0 561 objectStore->SetInfo(info);
michael@0 562 }
michael@0 563
michael@0 564 for (uint32_t i = 0; i < mDeletedObjectStores.Length(); i++) {
michael@0 565 nsRefPtr<IDBObjectStore>& objectStore = mDeletedObjectStores[i];
michael@0 566 ObjectStoreInfo* info = dbInfo->GetObjectStore(objectStore->Name());
michael@0 567
michael@0 568 if (!info) {
michael@0 569 info = new ObjectStoreInfo(*objectStore->Info());
michael@0 570 info->indexes.Clear();
michael@0 571 }
michael@0 572
michael@0 573 objectStore->SetInfo(info);
michael@0 574 }
michael@0 575
michael@0 576 // and then the db must be closed
michael@0 577 mDatabase->Close();
michael@0 578 }
michael@0 579
michael@0 580 // Fire the abort event if there are no outstanding requests. Otherwise the
michael@0 581 // abort event will be fired when all outstanding requests finish.
michael@0 582 if (needToCommitOrRollback) {
michael@0 583 return CommitOrRollback();
michael@0 584 }
michael@0 585
michael@0 586 return NS_OK;
michael@0 587 }
michael@0 588
michael@0 589 nsresult
michael@0 590 IDBTransaction::Abort(IDBRequest* aRequest)
michael@0 591 {
michael@0 592 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 593 NS_ASSERTION(aRequest, "This is undesirable.");
michael@0 594
michael@0 595 ErrorResult rv;
michael@0 596 nsRefPtr<DOMError> error = aRequest->GetError(rv);
michael@0 597
michael@0 598 return AbortInternal(aRequest->GetErrorCode(), error.forget());
michael@0 599 }
michael@0 600
michael@0 601 nsresult
michael@0 602 IDBTransaction::Abort(nsresult aErrorCode)
michael@0 603 {
michael@0 604 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 605
michael@0 606 nsRefPtr<DOMError> error = new DOMError(GetOwner(), aErrorCode);
michael@0 607 return AbortInternal(aErrorCode, error.forget());
michael@0 608 }
michael@0 609
michael@0 610 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBTransaction)
michael@0 611
michael@0 612 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBTransaction,
michael@0 613 IDBWrapperCache)
michael@0 614 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDatabase)
michael@0 615 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
michael@0 616 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCreatedObjectStores)
michael@0 617 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeletedObjectStores)
michael@0 618 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
michael@0 619
michael@0 620 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBTransaction, IDBWrapperCache)
michael@0 621 // Don't unlink mDatabase!
michael@0 622 NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
michael@0 623 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCreatedObjectStores)
michael@0 624 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeletedObjectStores)
michael@0 625 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
michael@0 626
michael@0 627 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBTransaction)
michael@0 628 NS_INTERFACE_MAP_ENTRY(nsIRunnable)
michael@0 629 NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache)
michael@0 630
michael@0 631 NS_IMPL_ADDREF_INHERITED(IDBTransaction, IDBWrapperCache)
michael@0 632 NS_IMPL_RELEASE_INHERITED(IDBTransaction, IDBWrapperCache)
michael@0 633
michael@0 634 JSObject*
michael@0 635 IDBTransaction::WrapObject(JSContext* aCx)
michael@0 636 {
michael@0 637 return IDBTransactionBinding::Wrap(aCx, this);
michael@0 638 }
michael@0 639
michael@0 640 mozilla::dom::IDBTransactionMode
michael@0 641 IDBTransaction::GetMode(ErrorResult& aRv) const
michael@0 642 {
michael@0 643 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 644
michael@0 645 switch (mMode) {
michael@0 646 case READ_ONLY:
michael@0 647 return mozilla::dom::IDBTransactionMode::Readonly;
michael@0 648
michael@0 649 case READ_WRITE:
michael@0 650 return mozilla::dom::IDBTransactionMode::Readwrite;
michael@0 651
michael@0 652 case VERSION_CHANGE:
michael@0 653 return mozilla::dom::IDBTransactionMode::Versionchange;
michael@0 654
michael@0 655 case MODE_INVALID:
michael@0 656 default:
michael@0 657 aRv.Throw(NS_ERROR_UNEXPECTED);
michael@0 658 return mozilla::dom::IDBTransactionMode::Readonly;
michael@0 659 }
michael@0 660 }
michael@0 661
michael@0 662 DOMError*
michael@0 663 IDBTransaction::GetError(ErrorResult& aRv)
michael@0 664 {
michael@0 665 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 666
michael@0 667 if (IsOpen()) {
michael@0 668 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
michael@0 669 return nullptr;
michael@0 670 }
michael@0 671
michael@0 672 return mError;
michael@0 673 }
michael@0 674
michael@0 675 already_AddRefed<DOMStringList>
michael@0 676 IDBTransaction::GetObjectStoreNames(ErrorResult& aRv)
michael@0 677 {
michael@0 678 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 679
michael@0 680 nsRefPtr<DOMStringList> list(new DOMStringList());
michael@0 681
michael@0 682 if (mMode == IDBTransaction::VERSION_CHANGE) {
michael@0 683 mDatabaseInfo->GetObjectStoreNames(list->StringArray());
michael@0 684 }
michael@0 685 else {
michael@0 686 list->StringArray() = mObjectStoreNames;
michael@0 687 }
michael@0 688
michael@0 689 return list.forget();
michael@0 690 }
michael@0 691
michael@0 692 already_AddRefed<IDBObjectStore>
michael@0 693 IDBTransaction::ObjectStore(const nsAString& aName, ErrorResult& aRv)
michael@0 694 {
michael@0 695 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 696
michael@0 697 if (IsFinished()) {
michael@0 698 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
michael@0 699 return nullptr;
michael@0 700 }
michael@0 701
michael@0 702 ObjectStoreInfo* info = nullptr;
michael@0 703
michael@0 704 if (mMode == IDBTransaction::VERSION_CHANGE ||
michael@0 705 mObjectStoreNames.Contains(aName)) {
michael@0 706 info = mDatabaseInfo->GetObjectStore(aName);
michael@0 707 }
michael@0 708
michael@0 709 if (!info) {
michael@0 710 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
michael@0 711 return nullptr;
michael@0 712 }
michael@0 713
michael@0 714 nsRefPtr<IDBObjectStore> objectStore =
michael@0 715 GetOrCreateObjectStore(aName, info, false);
michael@0 716 if (!objectStore) {
michael@0 717 IDB_WARNING("Failed to get or create object store!");
michael@0 718 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 719 return nullptr;
michael@0 720 }
michael@0 721
michael@0 722 return objectStore.forget();
michael@0 723 }
michael@0 724
michael@0 725 nsresult
michael@0 726 IDBTransaction::PreHandleEvent(EventChainPreVisitor& aVisitor)
michael@0 727 {
michael@0 728 aVisitor.mCanHandle = true;
michael@0 729 aVisitor.mParentTarget = mDatabase;
michael@0 730 return NS_OK;
michael@0 731 }
michael@0 732
michael@0 733 NS_IMETHODIMP
michael@0 734 IDBTransaction::Run()
michael@0 735 {
michael@0 736 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 737
michael@0 738 // We're back at the event loop, no longer newborn.
michael@0 739 mCreating = false;
michael@0 740
michael@0 741 // Maybe set the readyState to DONE if there were no requests generated.
michael@0 742 if (mReadyState == IDBTransaction::INITIAL) {
michael@0 743 mReadyState = IDBTransaction::DONE;
michael@0 744
michael@0 745 if (NS_FAILED(CommitOrRollback())) {
michael@0 746 NS_WARNING("Failed to commit!");
michael@0 747 }
michael@0 748 }
michael@0 749
michael@0 750 return NS_OK;
michael@0 751 }
michael@0 752
michael@0 753 CommitHelper::CommitHelper(
michael@0 754 IDBTransaction* aTransaction,
michael@0 755 IDBTransactionListener* aListener,
michael@0 756 const nsTArray<nsRefPtr<IDBObjectStore> >& aUpdatedObjectStores)
michael@0 757 : mTransaction(aTransaction),
michael@0 758 mListener(aListener),
michael@0 759 mAbortCode(aTransaction->mAbortCode)
michael@0 760 {
michael@0 761 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 762
michael@0 763 mConnection.swap(aTransaction->mConnection);
michael@0 764 mUpdateFileRefcountFunction.swap(aTransaction->mUpdateFileRefcountFunction);
michael@0 765
michael@0 766 for (uint32_t i = 0; i < aUpdatedObjectStores.Length(); i++) {
michael@0 767 ObjectStoreInfo* info = aUpdatedObjectStores[i]->Info();
michael@0 768 if (info->comittedAutoIncrementId != info->nextAutoIncrementId) {
michael@0 769 mAutoIncrementObjectStores.AppendElement(aUpdatedObjectStores[i]);
michael@0 770 }
michael@0 771 }
michael@0 772 }
michael@0 773
michael@0 774 CommitHelper::CommitHelper(IDBTransaction* aTransaction,
michael@0 775 nsresult aAbortCode)
michael@0 776 : mTransaction(aTransaction),
michael@0 777 mAbortCode(aAbortCode)
michael@0 778 {
michael@0 779 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 780 }
michael@0 781
michael@0 782 CommitHelper::~CommitHelper()
michael@0 783 {
michael@0 784 }
michael@0 785
michael@0 786 NS_IMPL_ISUPPORTS(CommitHelper, nsIRunnable)
michael@0 787
michael@0 788 NS_IMETHODIMP
michael@0 789 CommitHelper::Run()
michael@0 790 {
michael@0 791 if (NS_IsMainThread()) {
michael@0 792 PROFILER_MAIN_THREAD_LABEL("IndexedDB", "CommitHelper::Run");
michael@0 793
michael@0 794 NS_ASSERTION(mDoomedObjects.IsEmpty(), "Didn't release doomed objects!");
michael@0 795
michael@0 796 mTransaction->mReadyState = IDBTransaction::DONE;
michael@0 797
michael@0 798 // Release file infos on the main thread, so they will eventually get
michael@0 799 // destroyed on correct thread.
michael@0 800 mTransaction->ClearCreatedFileInfos();
michael@0 801 if (mUpdateFileRefcountFunction) {
michael@0 802 mUpdateFileRefcountFunction->ClearFileInfoEntries();
michael@0 803 mUpdateFileRefcountFunction = nullptr;
michael@0 804 }
michael@0 805
michael@0 806 nsCOMPtr<nsIDOMEvent> event;
michael@0 807 if (NS_FAILED(mAbortCode)) {
michael@0 808 if (mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE) {
michael@0 809 // This will make the database take a snapshot of it's DatabaseInfo
michael@0 810 mTransaction->Database()->Close();
michael@0 811 // Then remove the info from the hash as it contains invalid data.
michael@0 812 DatabaseInfo::Remove(mTransaction->Database()->Id());
michael@0 813 }
michael@0 814
michael@0 815 event = CreateGenericEvent(mTransaction,
michael@0 816 NS_LITERAL_STRING(ABORT_EVT_STR),
michael@0 817 eDoesBubble, eNotCancelable);
michael@0 818
michael@0 819 // The transaction may already have an error object (e.g. if one of the
michael@0 820 // requests failed). If it doesn't, and it wasn't aborted
michael@0 821 // programmatically, create one now.
michael@0 822 if (!mTransaction->mError &&
michael@0 823 mAbortCode != NS_ERROR_DOM_INDEXEDDB_ABORT_ERR) {
michael@0 824 mTransaction->mError = new DOMError(mTransaction->GetOwner(), mAbortCode);
michael@0 825 }
michael@0 826 }
michael@0 827 else {
michael@0 828 event = CreateGenericEvent(mTransaction,
michael@0 829 NS_LITERAL_STRING(COMPLETE_EVT_STR),
michael@0 830 eDoesNotBubble, eNotCancelable);
michael@0 831 }
michael@0 832 IDB_ENSURE_TRUE(event, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 833
michael@0 834 if (mListener) {
michael@0 835 mListener->NotifyTransactionPreComplete(mTransaction);
michael@0 836 }
michael@0 837
michael@0 838 IDB_PROFILER_MARK("IndexedDB Transaction %llu: Complete (rv = %lu)",
michael@0 839 "IDBTransaction[%llu] MT Complete",
michael@0 840 mTransaction->GetSerialNumber(), mAbortCode);
michael@0 841
michael@0 842 bool dummy;
michael@0 843 if (NS_FAILED(mTransaction->DispatchEvent(event, &dummy))) {
michael@0 844 NS_WARNING("Dispatch failed!");
michael@0 845 }
michael@0 846
michael@0 847 #ifdef DEBUG
michael@0 848 mTransaction->mFiredCompleteOrAbort = true;
michael@0 849 #endif
michael@0 850
michael@0 851 if (mListener) {
michael@0 852 mListener->NotifyTransactionPostComplete(mTransaction);
michael@0 853 }
michael@0 854
michael@0 855 mTransaction = nullptr;
michael@0 856
michael@0 857 return NS_OK;
michael@0 858 }
michael@0 859
michael@0 860 PROFILER_LABEL("IndexedDB", "CommitHelper::Run");
michael@0 861
michael@0 862 IDBDatabase* database = mTransaction->Database();
michael@0 863 if (database->IsInvalidated()) {
michael@0 864 IDB_REPORT_INTERNAL_ERR();
michael@0 865 mAbortCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
michael@0 866 }
michael@0 867
michael@0 868 if (mConnection) {
michael@0 869 QuotaManager::SetCurrentWindow(database->GetOwner());
michael@0 870
michael@0 871 if (NS_SUCCEEDED(mAbortCode) && mUpdateFileRefcountFunction &&
michael@0 872 NS_FAILED(mUpdateFileRefcountFunction->WillCommit(mConnection))) {
michael@0 873 IDB_REPORT_INTERNAL_ERR();
michael@0 874 mAbortCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
michael@0 875 }
michael@0 876
michael@0 877 if (NS_SUCCEEDED(mAbortCode) && NS_FAILED(WriteAutoIncrementCounts())) {
michael@0 878 IDB_REPORT_INTERNAL_ERR();
michael@0 879 mAbortCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
michael@0 880 }
michael@0 881
michael@0 882 if (NS_SUCCEEDED(mAbortCode)) {
michael@0 883 NS_NAMED_LITERAL_CSTRING(release, "COMMIT TRANSACTION");
michael@0 884 nsresult rv = mConnection->ExecuteSimpleSQL(release);
michael@0 885 if (NS_SUCCEEDED(rv)) {
michael@0 886 if (mUpdateFileRefcountFunction) {
michael@0 887 mUpdateFileRefcountFunction->DidCommit();
michael@0 888 }
michael@0 889 CommitAutoIncrementCounts();
michael@0 890 }
michael@0 891 else if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
michael@0 892 // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE,
michael@0 893 // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR.
michael@0 894 mAbortCode = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
michael@0 895 }
michael@0 896 else {
michael@0 897 IDB_REPORT_INTERNAL_ERR();
michael@0 898 mAbortCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
michael@0 899 }
michael@0 900 }
michael@0 901
michael@0 902 if (NS_FAILED(mAbortCode)) {
michael@0 903 if (mUpdateFileRefcountFunction) {
michael@0 904 mUpdateFileRefcountFunction->DidAbort();
michael@0 905 }
michael@0 906 RevertAutoIncrementCounts();
michael@0 907 NS_NAMED_LITERAL_CSTRING(rollback, "ROLLBACK TRANSACTION");
michael@0 908 if (NS_FAILED(mConnection->ExecuteSimpleSQL(rollback))) {
michael@0 909 NS_WARNING("Failed to rollback transaction!");
michael@0 910 }
michael@0 911 }
michael@0 912 }
michael@0 913
michael@0 914 mDoomedObjects.Clear();
michael@0 915
michael@0 916 if (mConnection) {
michael@0 917 if (mUpdateFileRefcountFunction) {
michael@0 918 nsresult rv = mConnection->RemoveFunction(
michael@0 919 NS_LITERAL_CSTRING("update_refcount"));
michael@0 920 if (NS_FAILED(rv)) {
michael@0 921 NS_WARNING("Failed to remove function!");
michael@0 922 }
michael@0 923 }
michael@0 924
michael@0 925 mConnection->Close();
michael@0 926 mConnection = nullptr;
michael@0 927
michael@0 928 QuotaManager::SetCurrentWindow(nullptr);
michael@0 929 }
michael@0 930
michael@0 931 return NS_OK;
michael@0 932 }
michael@0 933
michael@0 934 nsresult
michael@0 935 CommitHelper::WriteAutoIncrementCounts()
michael@0 936 {
michael@0 937 nsCOMPtr<mozIStorageStatement> stmt;
michael@0 938 nsresult rv;
michael@0 939 for (uint32_t i = 0; i < mAutoIncrementObjectStores.Length(); i++) {
michael@0 940 ObjectStoreInfo* info = mAutoIncrementObjectStores[i]->Info();
michael@0 941 if (!stmt) {
michael@0 942 rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
michael@0 943 "UPDATE object_store SET auto_increment = :ai "
michael@0 944 "WHERE id = :osid;"), getter_AddRefs(stmt));
michael@0 945 NS_ENSURE_SUCCESS(rv, rv);
michael@0 946 }
michael@0 947 else {
michael@0 948 stmt->Reset();
michael@0 949 }
michael@0 950
michael@0 951 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), info->id);
michael@0 952 NS_ENSURE_SUCCESS(rv, rv);
michael@0 953
michael@0 954 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("ai"),
michael@0 955 info->nextAutoIncrementId);
michael@0 956 NS_ENSURE_SUCCESS(rv, rv);
michael@0 957
michael@0 958 rv = stmt->Execute();
michael@0 959 NS_ENSURE_SUCCESS(rv, rv);
michael@0 960 }
michael@0 961
michael@0 962 return NS_OK;
michael@0 963 }
michael@0 964
michael@0 965 void
michael@0 966 CommitHelper::CommitAutoIncrementCounts()
michael@0 967 {
michael@0 968 for (uint32_t i = 0; i < mAutoIncrementObjectStores.Length(); i++) {
michael@0 969 ObjectStoreInfo* info = mAutoIncrementObjectStores[i]->Info();
michael@0 970 info->comittedAutoIncrementId = info->nextAutoIncrementId;
michael@0 971 }
michael@0 972 }
michael@0 973
michael@0 974 void
michael@0 975 CommitHelper::RevertAutoIncrementCounts()
michael@0 976 {
michael@0 977 for (uint32_t i = 0; i < mAutoIncrementObjectStores.Length(); i++) {
michael@0 978 ObjectStoreInfo* info = mAutoIncrementObjectStores[i]->Info();
michael@0 979 info->nextAutoIncrementId = info->comittedAutoIncrementId;
michael@0 980 }
michael@0 981 }
michael@0 982
michael@0 983 NS_IMPL_ISUPPORTS(UpdateRefcountFunction, mozIStorageFunction)
michael@0 984
michael@0 985 NS_IMETHODIMP
michael@0 986 UpdateRefcountFunction::OnFunctionCall(mozIStorageValueArray* aValues,
michael@0 987 nsIVariant** _retval)
michael@0 988 {
michael@0 989 *_retval = nullptr;
michael@0 990
michael@0 991 uint32_t numEntries;
michael@0 992 nsresult rv = aValues->GetNumEntries(&numEntries);
michael@0 993 NS_ENSURE_SUCCESS(rv, rv);
michael@0 994 NS_ASSERTION(numEntries == 2, "unexpected number of arguments");
michael@0 995
michael@0 996 #ifdef DEBUG
michael@0 997 int32_t type1 = mozIStorageValueArray::VALUE_TYPE_NULL;
michael@0 998 aValues->GetTypeOfIndex(0, &type1);
michael@0 999
michael@0 1000 int32_t type2 = mozIStorageValueArray::VALUE_TYPE_NULL;
michael@0 1001 aValues->GetTypeOfIndex(1, &type2);
michael@0 1002
michael@0 1003 NS_ASSERTION(!(type1 == mozIStorageValueArray::VALUE_TYPE_NULL &&
michael@0 1004 type2 == mozIStorageValueArray::VALUE_TYPE_NULL),
michael@0 1005 "Shouldn't be called!");
michael@0 1006 #endif
michael@0 1007
michael@0 1008 rv = ProcessValue(aValues, 0, eDecrement);
michael@0 1009 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1010
michael@0 1011 rv = ProcessValue(aValues, 1, eIncrement);
michael@0 1012 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1013
michael@0 1014 return NS_OK;
michael@0 1015 }
michael@0 1016
michael@0 1017 nsresult
michael@0 1018 UpdateRefcountFunction::WillCommit(mozIStorageConnection* aConnection)
michael@0 1019 {
michael@0 1020 DatabaseUpdateFunction function(aConnection, this);
michael@0 1021
michael@0 1022 mFileInfoEntries.EnumerateRead(DatabaseUpdateCallback, &function);
michael@0 1023
michael@0 1024 nsresult rv = function.ErrorCode();
michael@0 1025 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1026
michael@0 1027 rv = CreateJournals();
michael@0 1028 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1029
michael@0 1030 return NS_OK;
michael@0 1031 }
michael@0 1032
michael@0 1033 void
michael@0 1034 UpdateRefcountFunction::DidCommit()
michael@0 1035 {
michael@0 1036 mFileInfoEntries.EnumerateRead(FileInfoUpdateCallback, nullptr);
michael@0 1037
michael@0 1038 nsresult rv = RemoveJournals(mJournalsToRemoveAfterCommit);
michael@0 1039 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 1040 }
michael@0 1041
michael@0 1042 void
michael@0 1043 UpdateRefcountFunction::DidAbort()
michael@0 1044 {
michael@0 1045 nsresult rv = RemoveJournals(mJournalsToRemoveAfterAbort);
michael@0 1046 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 1047 }
michael@0 1048
michael@0 1049 nsresult
michael@0 1050 UpdateRefcountFunction::ProcessValue(mozIStorageValueArray* aValues,
michael@0 1051 int32_t aIndex,
michael@0 1052 UpdateType aUpdateType)
michael@0 1053 {
michael@0 1054 int32_t type;
michael@0 1055 aValues->GetTypeOfIndex(aIndex, &type);
michael@0 1056 if (type == mozIStorageValueArray::VALUE_TYPE_NULL) {
michael@0 1057 return NS_OK;
michael@0 1058 }
michael@0 1059
michael@0 1060 nsString ids;
michael@0 1061 aValues->GetString(aIndex, ids);
michael@0 1062
michael@0 1063 nsTArray<int64_t> fileIds;
michael@0 1064 nsresult rv = IDBObjectStore::ConvertFileIdsToArray(ids, fileIds);
michael@0 1065 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1066
michael@0 1067 for (uint32_t i = 0; i < fileIds.Length(); i++) {
michael@0 1068 int64_t id = fileIds.ElementAt(i);
michael@0 1069
michael@0 1070 FileInfoEntry* entry;
michael@0 1071 if (!mFileInfoEntries.Get(id, &entry)) {
michael@0 1072 nsRefPtr<FileInfo> fileInfo = mFileManager->GetFileInfo(id);
michael@0 1073 NS_ASSERTION(fileInfo, "Shouldn't be null!");
michael@0 1074
michael@0 1075 nsAutoPtr<FileInfoEntry> newEntry(new FileInfoEntry(fileInfo));
michael@0 1076 mFileInfoEntries.Put(id, newEntry);
michael@0 1077 entry = newEntry.forget();
michael@0 1078 }
michael@0 1079
michael@0 1080 if (mInSavepoint) {
michael@0 1081 mSavepointEntriesIndex.Put(id, entry);
michael@0 1082 }
michael@0 1083
michael@0 1084 switch (aUpdateType) {
michael@0 1085 case eIncrement:
michael@0 1086 entry->mDelta++;
michael@0 1087 if (mInSavepoint) {
michael@0 1088 entry->mSavepointDelta++;
michael@0 1089 }
michael@0 1090 break;
michael@0 1091 case eDecrement:
michael@0 1092 entry->mDelta--;
michael@0 1093 if (mInSavepoint) {
michael@0 1094 entry->mSavepointDelta--;
michael@0 1095 }
michael@0 1096 break;
michael@0 1097 default:
michael@0 1098 NS_NOTREACHED("Unknown update type!");
michael@0 1099 }
michael@0 1100 }
michael@0 1101
michael@0 1102 return NS_OK;
michael@0 1103 }
michael@0 1104
michael@0 1105 nsresult
michael@0 1106 UpdateRefcountFunction::CreateJournals()
michael@0 1107 {
michael@0 1108 nsCOMPtr<nsIFile> journalDirectory = mFileManager->GetJournalDirectory();
michael@0 1109 NS_ENSURE_TRUE(journalDirectory, NS_ERROR_FAILURE);
michael@0 1110
michael@0 1111 for (uint32_t i = 0; i < mJournalsToCreateBeforeCommit.Length(); i++) {
michael@0 1112 int64_t id = mJournalsToCreateBeforeCommit[i];
michael@0 1113
michael@0 1114 nsCOMPtr<nsIFile> file =
michael@0 1115 mFileManager->GetFileForId(journalDirectory, id);
michael@0 1116 NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
michael@0 1117
michael@0 1118 nsresult rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
michael@0 1119 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1120
michael@0 1121 mJournalsToRemoveAfterAbort.AppendElement(id);
michael@0 1122 }
michael@0 1123
michael@0 1124 return NS_OK;
michael@0 1125 }
michael@0 1126
michael@0 1127 nsresult
michael@0 1128 UpdateRefcountFunction::RemoveJournals(const nsTArray<int64_t>& aJournals)
michael@0 1129 {
michael@0 1130 nsCOMPtr<nsIFile> journalDirectory = mFileManager->GetJournalDirectory();
michael@0 1131 NS_ENSURE_TRUE(journalDirectory, NS_ERROR_FAILURE);
michael@0 1132
michael@0 1133 for (uint32_t index = 0; index < aJournals.Length(); index++) {
michael@0 1134 nsCOMPtr<nsIFile> file =
michael@0 1135 mFileManager->GetFileForId(journalDirectory, aJournals[index]);
michael@0 1136 NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
michael@0 1137
michael@0 1138 if (NS_FAILED(file->Remove(false))) {
michael@0 1139 NS_WARNING("Failed to removed journal!");
michael@0 1140 }
michael@0 1141 }
michael@0 1142
michael@0 1143 return NS_OK;
michael@0 1144 }
michael@0 1145
michael@0 1146 PLDHashOperator
michael@0 1147 UpdateRefcountFunction::DatabaseUpdateCallback(const uint64_t& aKey,
michael@0 1148 FileInfoEntry* aValue,
michael@0 1149 void* aUserArg)
michael@0 1150 {
michael@0 1151 if (!aValue->mDelta) {
michael@0 1152 return PL_DHASH_NEXT;
michael@0 1153 }
michael@0 1154
michael@0 1155 DatabaseUpdateFunction* function =
michael@0 1156 static_cast<DatabaseUpdateFunction*>(aUserArg);
michael@0 1157
michael@0 1158 if (!function->Update(aKey, aValue->mDelta)) {
michael@0 1159 return PL_DHASH_STOP;
michael@0 1160 }
michael@0 1161
michael@0 1162 return PL_DHASH_NEXT;
michael@0 1163 }
michael@0 1164
michael@0 1165 PLDHashOperator
michael@0 1166 UpdateRefcountFunction::FileInfoUpdateCallback(const uint64_t& aKey,
michael@0 1167 FileInfoEntry* aValue,
michael@0 1168 void* aUserArg)
michael@0 1169 {
michael@0 1170 if (aValue->mDelta) {
michael@0 1171 aValue->mFileInfo->UpdateDBRefs(aValue->mDelta);
michael@0 1172 }
michael@0 1173
michael@0 1174 return PL_DHASH_NEXT;
michael@0 1175 }
michael@0 1176
michael@0 1177 PLDHashOperator
michael@0 1178 UpdateRefcountFunction::RollbackSavepointCallback(const uint64_t& aKey,
michael@0 1179 FileInfoEntry* aValue,
michael@0 1180 void* aUserArg)
michael@0 1181 {
michael@0 1182 aValue->mDelta -= aValue->mSavepointDelta;
michael@0 1183
michael@0 1184 return PL_DHASH_NEXT;
michael@0 1185 }
michael@0 1186
michael@0 1187 bool
michael@0 1188 UpdateRefcountFunction::DatabaseUpdateFunction::Update(int64_t aId,
michael@0 1189 int32_t aDelta)
michael@0 1190 {
michael@0 1191 nsresult rv = UpdateInternal(aId, aDelta);
michael@0 1192 if (NS_FAILED(rv)) {
michael@0 1193 mErrorCode = rv;
michael@0 1194 return false;
michael@0 1195 }
michael@0 1196
michael@0 1197 return true;
michael@0 1198 }
michael@0 1199
michael@0 1200 nsresult
michael@0 1201 UpdateRefcountFunction::DatabaseUpdateFunction::UpdateInternal(int64_t aId,
michael@0 1202 int32_t aDelta)
michael@0 1203 {
michael@0 1204 nsresult rv;
michael@0 1205
michael@0 1206 if (!mUpdateStatement) {
michael@0 1207 rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1208 "UPDATE file SET refcount = refcount + :delta WHERE id = :id"
michael@0 1209 ), getter_AddRefs(mUpdateStatement));
michael@0 1210 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1211 }
michael@0 1212
michael@0 1213 mozStorageStatementScoper updateScoper(mUpdateStatement);
michael@0 1214
michael@0 1215 rv = mUpdateStatement->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta);
michael@0 1216 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1217
michael@0 1218 rv = mUpdateStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId);
michael@0 1219 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1220
michael@0 1221 rv = mUpdateStatement->Execute();
michael@0 1222 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1223
michael@0 1224 int32_t rows;
michael@0 1225 rv = mConnection->GetAffectedRows(&rows);
michael@0 1226 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1227
michael@0 1228 if (rows > 0) {
michael@0 1229 if (!mSelectStatement) {
michael@0 1230 rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1231 "SELECT id FROM file where id = :id"
michael@0 1232 ), getter_AddRefs(mSelectStatement));
michael@0 1233 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1234 }
michael@0 1235
michael@0 1236 mozStorageStatementScoper selectScoper(mSelectStatement);
michael@0 1237
michael@0 1238 rv = mSelectStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId);
michael@0 1239 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1240
michael@0 1241 bool hasResult;
michael@0 1242 rv = mSelectStatement->ExecuteStep(&hasResult);
michael@0 1243 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1244
michael@0 1245 if (!hasResult) {
michael@0 1246 // Don't have to create the journal here, we can create all at once,
michael@0 1247 // just before commit
michael@0 1248 mFunction->mJournalsToCreateBeforeCommit.AppendElement(aId);
michael@0 1249 }
michael@0 1250
michael@0 1251 return NS_OK;
michael@0 1252 }
michael@0 1253
michael@0 1254 if (!mInsertStatement) {
michael@0 1255 rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1256 "INSERT INTO file (id, refcount) VALUES(:id, :delta)"
michael@0 1257 ), getter_AddRefs(mInsertStatement));
michael@0 1258 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1259 }
michael@0 1260
michael@0 1261 mozStorageStatementScoper insertScoper(mInsertStatement);
michael@0 1262
michael@0 1263 rv = mInsertStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId);
michael@0 1264 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1265
michael@0 1266 rv = mInsertStatement->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta);
michael@0 1267 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1268
michael@0 1269 rv = mInsertStatement->Execute();
michael@0 1270 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1271
michael@0 1272 mFunction->mJournalsToRemoveAfterCommit.AppendElement(aId);
michael@0 1273
michael@0 1274 return NS_OK;
michael@0 1275 }

mercurial