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