1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
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/. */
7 #include "mozilla/Attributes.h"
8 #include "mozilla/DebugOnly.h"
10 #include "mozStorageService.h"
11 #include "mozStorageConnection.h"
13 #include "nsAutoPtr.h"
14 #include "nsCollationCID.h"
15 #include "nsEmbedCID.h"
16 #include "nsThreadUtils.h"
17 #include "mozStoragePrivateHelpers.h"
18 #include "nsILocale.h"
19 #include "nsILocaleService.h"
20 #include "nsIXPConnect.h"
21 #include "nsIObserverService.h"
22 #include "nsIPropertyBag2.h"
23 #include "mozilla/Services.h"
24 #include "mozilla/Preferences.h"
25 #include "mozilla/LateWriteChecks.h"
26 #include "mozIStorageCompletionCallback.h"
27 #include "mozIStoragePendingStatement.h"
32 // "windows.h" was included and it can #define lots of things we care about...
36 #include "nsIPromptService.h"
38 #ifdef MOZ_STORAGE_MEMORY
39 # include "mozmemory.h"
45 ////////////////////////////////////////////////////////////////////////////////
48 #define PREF_TS_SYNCHRONOUS "toolkit.storage.synchronous"
49 #define PREF_TS_SYNCHRONOUS_DEFAULT 1
51 #define PREF_TS_PAGESIZE "toolkit.storage.pageSize"
53 // This value must be kept in sync with the value of SQLITE_DEFAULT_PAGE_SIZE in
54 // db/sqlite3/src/Makefile.in.
55 #define PREF_TS_PAGESIZE_DEFAULT 32768
60 ////////////////////////////////////////////////////////////////////////////////
64 static mozilla::Atomic
<size_t> gSqliteMemoryUsed
;
68 StorageSQLiteDistinguishedAmount()
70 return ::sqlite3_memory_used();
74 * Passes a single SQLite memory statistic to a memory reporter callback.
76 * @param aHandleReport
79 * The data for the callback.
81 * The SQLite connection.
83 * Head of the path for the memory report.
85 * The memory report statistic kind, one of "stmt", "cache" or
88 * The memory report description.
90 * The SQLite constant for getting the measurement.
92 * The accumulator for the measurement.
95 ReportConn(nsIHandleReportCallback
*aHandleReport
,
98 const nsACString
&aPathHead
,
99 const nsACString
&aKind
,
100 const nsACString
&aDesc
,
104 nsCString
path(aPathHead
);
106 path
.AppendLiteral("-used");
108 int32_t val
= aConn
->getSqliteRuntimeStatus(aOption
);
109 nsresult rv
= aHandleReport
->Callback(EmptyCString(), path
,
110 nsIMemoryReporter::KIND_HEAP
,
111 nsIMemoryReporter::UNITS_BYTES
,
112 int64_t(val
), aDesc
, aData
);
113 NS_ENSURE_SUCCESS(rv
, rv
);
119 // Warning: To get a Connection's measurements requires holding its lock.
120 // There may be a delay getting the lock if another thread is accessing the
121 // Connection. This isn't very nice if CollectReports is called from the main
122 // thread! But at the time of writing this function is only called when
123 // about:memory is loaded (not, for example, when telemetry pings occur) and
124 // any delays in that case aren't so bad.
126 Service::CollectReports(nsIHandleReportCallback
*aHandleReport
,
127 nsISupports
*aData
, bool aAnonymize
)
130 size_t totalConnSize
= 0;
132 nsTArray
<RefPtr
<Connection
> > connections
;
133 getConnections(connections
);
135 for (uint32_t i
= 0; i
< connections
.Length(); i
++) {
136 RefPtr
<Connection
> &conn
= connections
[i
];
138 // Someone may have closed the Connection, in which case we skip it.
140 (void)conn
->GetConnectionReady(&isReady
);
145 nsCString
pathHead("explicit/storage/sqlite/");
146 // This filename isn't privacy-sensitive, and so is never anonymized.
147 pathHead
.Append(conn
->getFilename());
148 pathHead
.Append('/');
150 SQLiteMutexAutoLock
lockedScope(conn
->sharedDBMutex
);
152 NS_NAMED_LITERAL_CSTRING(stmtDesc
,
153 "Memory (approximate) used by all prepared statements used by "
154 "connections to this database.");
155 rv
= ReportConn(aHandleReport
, aData
, conn
, pathHead
,
156 NS_LITERAL_CSTRING("stmt"), stmtDesc
,
157 SQLITE_DBSTATUS_STMT_USED
, &totalConnSize
);
158 NS_ENSURE_SUCCESS(rv
, rv
);
160 NS_NAMED_LITERAL_CSTRING(cacheDesc
,
161 "Memory (approximate) used by all pager caches used by connections "
162 "to this database.");
163 rv
= ReportConn(aHandleReport
, aData
, conn
, pathHead
,
164 NS_LITERAL_CSTRING("cache"), cacheDesc
,
165 SQLITE_DBSTATUS_CACHE_USED
, &totalConnSize
);
166 NS_ENSURE_SUCCESS(rv
, rv
);
168 NS_NAMED_LITERAL_CSTRING(schemaDesc
,
169 "Memory (approximate) used to store the schema for all databases "
170 "associated with connections to this database.");
171 rv
= ReportConn(aHandleReport
, aData
, conn
, pathHead
,
172 NS_LITERAL_CSTRING("schema"), schemaDesc
,
173 SQLITE_DBSTATUS_SCHEMA_USED
, &totalConnSize
);
174 NS_ENSURE_SUCCESS(rv
, rv
);
178 if (::sqlite3_memory_used() != int64_t(gSqliteMemoryUsed
)) {
179 NS_WARNING("memory consumption reported by SQLite doesn't match "
185 int64_t other
= ::sqlite3_memory_used() - totalConnSize
;
187 rv
= aHandleReport
->Callback(
189 NS_LITERAL_CSTRING("explicit/storage/sqlite/other"),
190 KIND_HEAP
, UNITS_BYTES
, other
,
191 NS_LITERAL_CSTRING("All unclassified sqlite memory."),
193 NS_ENSURE_SUCCESS(rv
, rv
);
198 ////////////////////////////////////////////////////////////////////////////////
208 Service
*Service::gService
= nullptr;
211 Service::getSingleton()
218 // Ensure that we are using the same version of SQLite that we compiled with
219 // or newer. Our configure check ensures we are using a new enough version
221 if (SQLITE_VERSION_NUMBER
> ::sqlite3_libversion_number()) {
222 nsCOMPtr
<nsIPromptService
> ps(do_GetService(NS_PROMPTSERVICE_CONTRACTID
));
224 nsAutoString title
, message
;
225 title
.AppendLiteral("SQLite Version Error");
226 message
.AppendLiteral("The application has been updated, but your version "
227 "of SQLite is too old and the application cannot "
229 (void)ps
->Alert(nullptr, title
.get(), message
.get());
234 // The first reference to the storage service must be obtained on the
236 NS_ENSURE_TRUE(NS_IsMainThread(), nullptr);
237 gService
= new Service();
240 if (NS_FAILED(gService
->initialize()))
241 NS_RELEASE(gService
);
247 nsIXPConnect
*Service::sXPConnect
= nullptr;
250 already_AddRefed
<nsIXPConnect
>
251 Service::getXPConnect()
253 NS_PRECONDITION(NS_IsMainThread(),
254 "Must only get XPConnect on the main thread!");
255 NS_PRECONDITION(gService
,
256 "Can not get XPConnect without an instance of our service!");
258 // If we've been shutdown, sXPConnect will be null. To prevent leaks, we do
259 // not cache the service after this point.
260 nsCOMPtr
<nsIXPConnect
> xpc(sXPConnect
);
262 xpc
= do_GetService(nsIXPConnect::GetCID());
263 NS_ASSERTION(xpc
, "Could not get XPConnect!");
267 int32_t Service::sSynchronousPref
;
271 Service::getSynchronousPref()
273 return sSynchronousPref
;
276 int32_t Service::sDefaultPageSize
= PREF_TS_PAGESIZE_DEFAULT
;
279 : mMutex("Service::mMutex")
280 , mSqliteVFS(nullptr)
281 , mRegistrationMutex("Service::mRegistrationMutex")
288 mozilla::UnregisterWeakMemoryReporter(this);
289 mozilla::UnregisterStorageSQLiteDistinguishedAmount();
291 int rc
= sqlite3_vfs_unregister(mSqliteVFS
);
293 NS_WARNING("Failed to unregister sqlite vfs wrapper.");
295 // Shutdown the sqlite3 API. Warn if shutdown did not turn out okay, but
296 // there is nothing actionable we can do in that case.
297 rc
= ::sqlite3_shutdown();
299 NS_WARNING("sqlite3 did not shutdown cleanly.");
301 DebugOnly
<bool> shutdownObserved
= !sXPConnect
;
302 NS_ASSERTION(shutdownObserved
, "Shutdown was not observed!");
306 mSqliteVFS
= nullptr;
310 Service::registerConnection(Connection
*aConnection
)
312 mRegistrationMutex
.AssertNotCurrentThreadOwns();
313 MutexAutoLock
mutex(mRegistrationMutex
);
314 (void)mConnections
.AppendElement(aConnection
);
318 Service::unregisterConnection(Connection
*aConnection
)
320 // If this is the last Connection it might be the only thing keeping Service
321 // alive. So ensure that Service is destroyed only after the Connection is
322 // cleanly unregistered and destroyed.
323 RefPtr
<Service
> kungFuDeathGrip(this);
325 mRegistrationMutex
.AssertNotCurrentThreadOwns();
326 MutexAutoLock
mutex(mRegistrationMutex
);
328 for (uint32_t i
= 0 ; i
< mConnections
.Length(); ++i
) {
329 if (mConnections
[i
] == aConnection
) {
330 nsCOMPtr
<nsIThread
> thread
= mConnections
[i
]->threadOpenedOn
;
332 // Ensure the connection is released on its opening thread. Note, we
333 // must use .forget().take() so that we can manually cast to an
334 // unambiguous nsISupports type.
335 NS_ProxyRelease(thread
,
336 static_cast<mozIStorageConnection
*>(mConnections
[i
].forget().take()));
338 mConnections
.RemoveElementAt(i
);
343 MOZ_ASSERT_UNREACHABLE("Attempt to unregister unknown storage connection!");
348 Service::getConnections(/* inout */ nsTArray
<RefPtr
<Connection
> >& aConnections
)
350 mRegistrationMutex
.AssertNotCurrentThreadOwns();
351 MutexAutoLock
mutex(mRegistrationMutex
);
352 aConnections
.Clear();
353 aConnections
.AppendElements(mConnections
);
357 Service::minimizeMemory()
359 nsTArray
<RefPtr
<Connection
> > connections
;
360 getConnections(connections
);
362 for (uint32_t i
= 0; i
< connections
.Length(); i
++) {
363 RefPtr
<Connection
> conn
= connections
[i
];
364 if (!conn
->connectionReady())
367 NS_NAMED_LITERAL_CSTRING(shrinkPragma
, "PRAGMA shrink_memory");
368 nsCOMPtr
<mozIStorageConnection
> syncConn
= do_QueryInterface(
369 NS_ISUPPORTS_CAST(mozIStorageAsyncConnection
*, conn
));
370 bool onOpenedThread
= false;
373 // This is a mozIStorageAsyncConnection, it can only be used on the main
374 // thread, so we can do a straight API call.
375 nsCOMPtr
<mozIStoragePendingStatement
> ps
;
376 DebugOnly
<nsresult
> rv
=
377 conn
->ExecuteSimpleSQLAsync(shrinkPragma
, nullptr, getter_AddRefs(ps
));
378 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Should have purged sqlite caches");
379 } else if (NS_SUCCEEDED(conn
->threadOpenedOn
->IsOnCurrentThread(&onOpenedThread
)) &&
381 // We are on the opener thread, so we can just proceed.
382 conn
->ExecuteSimpleSQL(shrinkPragma
);
384 // We are on the wrong thread, the query should be executed on the
385 // opener thread, so we must dispatch to it.
386 nsCOMPtr
<nsIRunnable
> event
=
387 NS_NewRunnableMethodWithArg
<const nsCString
>(
388 conn
, &Connection::ExecuteSimpleSQL
, shrinkPragma
);
389 conn
->threadOpenedOn
->Dispatch(event
, NS_DISPATCH_NORMAL
);
397 NS_IF_RELEASE(sXPConnect
);
400 sqlite3_vfs
*ConstructTelemetryVFS();
402 #ifdef MOZ_STORAGE_MEMORY
406 // By default, SQLite tracks the size of all its heap blocks by adding an extra
407 // 8 bytes at the start of the block to hold the size. Unfortunately, this
408 // causes a lot of 2^N-sized allocations to be rounded up by jemalloc
409 // allocator, wasting memory. For example, a request for 1024 bytes has 8
410 // bytes added, becoming a request for 1032 bytes, and jemalloc rounds this up
411 // to 2048 bytes, wasting 1012 bytes. (See bug 676189 for more details.)
413 // So we register jemalloc as the malloc implementation, which avoids this
414 // 8-byte overhead, and thus a lot of waste. This requires us to provide a
415 // function, sqliteMemRoundup(), which computes the actual size that will be
416 // allocated for a given request. SQLite uses this function before all
417 // allocations, and may be able to use any excess bytes caused by the rounding.
419 // Note: the wrappers for malloc, realloc and moz_malloc_usable_size are
420 // necessary because the sqlite_mem_methods type signatures differ slightly
421 // from the standard ones -- they use int instead of size_t. But we don't need
422 // a wrapper for free.
426 // sqlite does its own memory accounting, and we use its numbers in our memory
427 // reporters. But we don't want sqlite's heap blocks to show up in DMD's
428 // output as unreported, so we mark them as reported when they're allocated and
429 // mark them as unreported when they are freed.
431 // In other words, we are marking all sqlite heap blocks as reported even
432 // though we're not reporting them ourselves. Instead we're trusting that
433 // sqlite is fully and correctly accounting for all of its heap blocks via its
434 // own memory accounting. Well, we don't have to trust it entirely, because
435 // it's easy to keep track (while doing this DMD-specific marking) of exactly
436 // how much memory SQLite is using. And we can compare that against what
437 // SQLite reports it is using.
439 MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(SqliteMallocSizeOfOnAlloc
)
440 MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(SqliteMallocSizeOfOnFree
)
444 static void *sqliteMemMalloc(int n
)
446 void* p
= ::malloc(n
);
448 gSqliteMemoryUsed
+= SqliteMallocSizeOfOnAlloc(p
);
453 static void sqliteMemFree(void *p
)
456 gSqliteMemoryUsed
-= SqliteMallocSizeOfOnFree(p
);
461 static void *sqliteMemRealloc(void *p
, int n
)
464 gSqliteMemoryUsed
-= SqliteMallocSizeOfOnFree(p
);
465 void *pnew
= ::realloc(p
, n
);
467 gSqliteMemoryUsed
+= SqliteMallocSizeOfOnAlloc(pnew
);
469 // realloc failed; undo the SqliteMallocSizeOfOnFree from above
470 gSqliteMemoryUsed
+= SqliteMallocSizeOfOnAlloc(p
);
474 return ::realloc(p
, n
);
478 static int sqliteMemSize(void *p
)
480 return ::moz_malloc_usable_size(p
);
483 static int sqliteMemRoundup(int n
)
485 n
= malloc_good_size(n
);
487 // jemalloc can return blocks of size 2 and 4, but SQLite requires that all
488 // allocations be 8-aligned. So we round up sub-8 requests to 8. This
489 // wastes a small amount of memory but is obviously safe.
490 return n
<= 8 ? 8 : n
;
493 static int sqliteMemInit(void *p
)
498 static void sqliteMemShutdown(void *p
)
502 const sqlite3_mem_methods memMethods
= {
515 #endif // MOZ_STORAGE_MEMORY
517 static const char* sObserverTopics
[] = {
520 "xpcom-shutdown-threads"
524 Service::initialize()
526 MOZ_ASSERT(NS_IsMainThread(), "Must be initialized on the main thread");
530 #ifdef MOZ_STORAGE_MEMORY
531 rc
= ::sqlite3_config(SQLITE_CONFIG_MALLOC
, &memMethods
);
533 return convertResultCode(rc
);
536 // TODO (bug 1191405): do not preallocate the connections caches until we
537 // have figured the impact on our consumers and memory.
538 sqlite3_config(SQLITE_CONFIG_PAGECACHE
, NULL
, 0, 0);
540 // Explicitly initialize sqlite3. Although this is implicitly called by
541 // various sqlite3 functions (and the sqlite3_open calls in our case),
542 // the documentation suggests calling this directly. So we do.
543 rc
= ::sqlite3_initialize();
545 return convertResultCode(rc
);
547 mSqliteVFS
= ConstructTelemetryVFS();
549 rc
= sqlite3_vfs_register(mSqliteVFS
, 1);
551 return convertResultCode(rc
);
553 NS_WARNING("Failed to register telemetry VFS");
556 // Register for xpcom-shutdown so we can cleanup after ourselves. The
557 // observer service can only be used on the main thread.
558 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
559 NS_ENSURE_TRUE(os
, NS_ERROR_FAILURE
);
561 for (size_t i
= 0; i
< ArrayLength(sObserverTopics
); ++i
) {
562 nsresult rv
= os
->AddObserver(this, sObserverTopics
[i
], false);
563 if (NS_WARN_IF(NS_FAILED(rv
))) {
568 // We cache XPConnect for our language helpers. XPConnect can only be
569 // used on the main thread.
570 (void)CallGetService(nsIXPConnect::GetCID(), &sXPConnect
);
572 // We need to obtain the toolkit.storage.synchronous preferences on the main
573 // thread because the preference service can only be accessed there. This
574 // is cached in the service for all future Open[Unshared]Database calls.
576 Preferences::GetInt(PREF_TS_SYNCHRONOUS
, PREF_TS_SYNCHRONOUS_DEFAULT
);
578 // We need to obtain the toolkit.storage.pageSize preferences on the main
579 // thread because the preference service can only be accessed there. This
580 // is cached in the service for all future Open[Unshared]Database calls.
582 Preferences::GetInt(PREF_TS_PAGESIZE
, PREF_TS_PAGESIZE_DEFAULT
);
584 mozilla::RegisterWeakMemoryReporter(this);
585 mozilla::RegisterStorageSQLiteDistinguishedAmount(StorageSQLiteDistinguishedAmount
);
591 Service::localeCompareStrings(const nsAString
&aStr1
,
592 const nsAString
&aStr2
,
593 int32_t aComparisonStrength
)
595 // The implementation of nsICollation.CompareString() is platform-dependent.
596 // On Linux it's not thread-safe. It may not be on Windows and OS X either,
597 // but it's more difficult to tell. We therefore synchronize this method.
598 MutexAutoLock
mutex(mMutex
);
600 nsICollation
*coll
= getLocaleCollation();
602 NS_ERROR("Storage service has no collation");
607 nsresult rv
= coll
->CompareString(aComparisonStrength
, aStr1
, aStr2
, &res
);
609 NS_ERROR("Collation compare string failed");
617 Service::getLocaleCollation()
619 mMutex
.AssertCurrentThreadOwns();
621 if (mLocaleCollation
)
622 return mLocaleCollation
;
624 nsCOMPtr
<nsILocaleService
> svc(do_GetService(NS_LOCALESERVICE_CONTRACTID
));
626 NS_WARNING("Could not get locale service");
630 nsCOMPtr
<nsILocale
> appLocale
;
631 nsresult rv
= svc
->GetApplicationLocale(getter_AddRefs(appLocale
));
633 NS_WARNING("Could not get application locale");
637 nsCOMPtr
<nsICollationFactory
> collFact
=
638 do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID
);
640 NS_WARNING("Could not create collation factory");
644 rv
= collFact
->CreateCollation(appLocale
, getter_AddRefs(mLocaleCollation
));
646 NS_WARNING("Could not create collation");
650 return mLocaleCollation
;
653 ////////////////////////////////////////////////////////////////////////////////
654 //// mozIStorageService
658 Service::OpenSpecialDatabase(const char *aStorageKey
,
659 mozIStorageConnection
**_connection
)
663 nsCOMPtr
<nsIFile
> storageFile
;
664 if (::strcmp(aStorageKey
, "memory") == 0) {
665 // just fall through with nullptr storageFile, this will cause the storage
666 // connection to use a memory DB.
669 return NS_ERROR_INVALID_ARG
;
672 RefPtr
<Connection
> msc
= new Connection(this, SQLITE_OPEN_READWRITE
, false);
674 rv
= storageFile
? msc
->initialize(storageFile
) : msc
->initialize();
675 NS_ENSURE_SUCCESS(rv
, rv
);
677 msc
.forget(_connection
);
684 class AsyncInitDatabase final
: public nsRunnable
687 AsyncInitDatabase(Connection
* aConnection
,
688 nsIFile
* aStorageFile
,
689 int32_t aGrowthIncrement
,
690 mozIStorageCompletionCallback
* aCallback
)
691 : mConnection(aConnection
)
692 , mStorageFile(aStorageFile
)
693 , mGrowthIncrement(aGrowthIncrement
)
694 , mCallback(aCallback
)
696 MOZ_ASSERT(NS_IsMainThread());
701 MOZ_ASSERT(!NS_IsMainThread());
702 nsresult rv
= mStorageFile
? mConnection
->initialize(mStorageFile
)
703 : mConnection
->initialize();
705 nsCOMPtr
<nsIRunnable
> closeRunnable
=
706 NS_NewRunnableMethodWithArg
<mozIStorageCompletionCallback
*>(
708 &Connection::AsyncClose
,
710 MOZ_ASSERT(closeRunnable
);
711 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(closeRunnable
)));
713 return DispatchResult(rv
, nullptr);
716 if (mGrowthIncrement
>= 0) {
717 // Ignore errors. In the future, we might wish to log them.
718 (void)mConnection
->SetGrowthIncrement(mGrowthIncrement
, EmptyCString());
721 return DispatchResult(NS_OK
, NS_ISUPPORTS_CAST(mozIStorageAsyncConnection
*,
726 nsresult
DispatchResult(nsresult aStatus
, nsISupports
* aValue
) {
727 RefPtr
<CallbackComplete
> event
=
728 new CallbackComplete(aStatus
,
731 return NS_DispatchToMainThread(event
);
736 nsCOMPtr
<nsIThread
> thread
;
737 DebugOnly
<nsresult
> rv
= NS_GetMainThread(getter_AddRefs(thread
));
738 MOZ_ASSERT(NS_SUCCEEDED(rv
));
739 (void)NS_ProxyRelease(thread
, mStorageFile
);
741 // Handle ambiguous nsISupports inheritance.
742 Connection
*rawConnection
= nullptr;
743 mConnection
.swap(rawConnection
);
744 (void)NS_ProxyRelease(thread
, NS_ISUPPORTS_CAST(mozIStorageConnection
*,
747 // Generally, the callback will be released by CallbackComplete.
748 // However, if for some reason Run() is not executed, we still
749 // need to ensure that it is released here.
750 mozIStorageCompletionCallback
*rawCallback
= nullptr;
751 mCallback
.swap(rawCallback
);
752 (void)NS_ProxyRelease(thread
, rawCallback
);
755 RefPtr
<Connection
> mConnection
;
756 nsCOMPtr
<nsIFile
> mStorageFile
;
757 int32_t mGrowthIncrement
;
758 RefPtr
<mozIStorageCompletionCallback
> mCallback
;
764 Service::OpenAsyncDatabase(nsIVariant
*aDatabaseStore
,
765 nsIPropertyBag2
*aOptions
,
766 mozIStorageCompletionCallback
*aCallback
)
768 if (!NS_IsMainThread()) {
769 return NS_ERROR_NOT_SAME_THREAD
;
771 NS_ENSURE_ARG(aDatabaseStore
);
772 NS_ENSURE_ARG(aCallback
);
774 nsCOMPtr
<nsIFile
> storageFile
;
775 int flags
= SQLITE_OPEN_READWRITE
;
777 nsCOMPtr
<nsISupports
> dbStore
;
778 nsresult rv
= aDatabaseStore
->GetAsISupports(getter_AddRefs(dbStore
));
779 if (NS_SUCCEEDED(rv
)) {
780 // Generally, aDatabaseStore holds the database nsIFile.
781 storageFile
= do_QueryInterface(dbStore
, &rv
);
783 return NS_ERROR_INVALID_ARG
;
786 rv
= storageFile
->Clone(getter_AddRefs(storageFile
));
787 MOZ_ASSERT(NS_SUCCEEDED(rv
));
789 // Ensure that SQLITE_OPEN_CREATE is passed in for compatibility reasons.
790 flags
|= SQLITE_OPEN_CREATE
;
792 // Extract and apply the shared-cache option.
795 rv
= aOptions
->GetPropertyAsBool(NS_LITERAL_STRING("shared"), &shared
);
796 if (NS_FAILED(rv
) && rv
!= NS_ERROR_NOT_AVAILABLE
) {
797 return NS_ERROR_INVALID_ARG
;
800 flags
|= shared
? SQLITE_OPEN_SHAREDCACHE
: SQLITE_OPEN_PRIVATECACHE
;
802 // Sometimes, however, it's a special database name.
803 nsAutoCString keyString
;
804 rv
= aDatabaseStore
->GetAsACString(keyString
);
805 if (NS_FAILED(rv
) || !keyString
.EqualsLiteral("memory")) {
806 return NS_ERROR_INVALID_ARG
;
809 // Just fall through with nullptr storageFile, this will cause the storage
810 // connection to use a memory DB.
813 int32_t growthIncrement
= -1;
814 if (aOptions
&& storageFile
) {
815 rv
= aOptions
->GetPropertyAsInt32(NS_LITERAL_STRING("growthIncrement"),
817 if (NS_FAILED(rv
) && rv
!= NS_ERROR_NOT_AVAILABLE
) {
818 return NS_ERROR_INVALID_ARG
;
822 // Create connection on this thread, but initialize it on its helper thread.
823 RefPtr
<Connection
> msc
= new Connection(this, flags
, true);
824 nsCOMPtr
<nsIEventTarget
> target
= msc
->getAsyncExecutionTarget();
825 MOZ_ASSERT(target
, "Cannot initialize a connection that has been closed already");
827 RefPtr
<AsyncInitDatabase
> asyncInit
=
828 new AsyncInitDatabase(msc
,
832 return target
->Dispatch(asyncInit
, nsIEventTarget::DISPATCH_NORMAL
);
836 Service::OpenDatabase(nsIFile
*aDatabaseFile
,
837 mozIStorageConnection
**_connection
)
839 NS_ENSURE_ARG(aDatabaseFile
);
841 // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
843 int flags
= SQLITE_OPEN_READWRITE
| SQLITE_OPEN_SHAREDCACHE
|
845 RefPtr
<Connection
> msc
= new Connection(this, flags
, false);
847 nsresult rv
= msc
->initialize(aDatabaseFile
);
848 NS_ENSURE_SUCCESS(rv
, rv
);
850 msc
.forget(_connection
);
855 Service::OpenUnsharedDatabase(nsIFile
*aDatabaseFile
,
856 mozIStorageConnection
**_connection
)
858 NS_ENSURE_ARG(aDatabaseFile
);
860 // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
862 int flags
= SQLITE_OPEN_READWRITE
| SQLITE_OPEN_PRIVATECACHE
|
864 RefPtr
<Connection
> msc
= new Connection(this, flags
, false);
866 nsresult rv
= msc
->initialize(aDatabaseFile
);
867 NS_ENSURE_SUCCESS(rv
, rv
);
869 msc
.forget(_connection
);
874 Service::OpenDatabaseWithFileURL(nsIFileURL
*aFileURL
,
875 mozIStorageConnection
**_connection
)
877 NS_ENSURE_ARG(aFileURL
);
879 // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
881 int flags
= SQLITE_OPEN_READWRITE
| SQLITE_OPEN_SHAREDCACHE
|
882 SQLITE_OPEN_CREATE
| SQLITE_OPEN_URI
;
883 RefPtr
<Connection
> msc
= new Connection(this, flags
, false);
885 nsresult rv
= msc
->initialize(aFileURL
);
886 NS_ENSURE_SUCCESS(rv
, rv
);
888 msc
.forget(_connection
);
893 Service::BackupDatabaseFile(nsIFile
*aDBFile
,
894 const nsAString
&aBackupFileName
,
895 nsIFile
*aBackupParentDirectory
,
899 nsCOMPtr
<nsIFile
> parentDir
= aBackupParentDirectory
;
901 // This argument is optional, and defaults to the same parent directory
902 // as the current file.
903 rv
= aDBFile
->GetParent(getter_AddRefs(parentDir
));
904 NS_ENSURE_SUCCESS(rv
, rv
);
907 nsCOMPtr
<nsIFile
> backupDB
;
908 rv
= parentDir
->Clone(getter_AddRefs(backupDB
));
909 NS_ENSURE_SUCCESS(rv
, rv
);
911 rv
= backupDB
->Append(aBackupFileName
);
912 NS_ENSURE_SUCCESS(rv
, rv
);
914 rv
= backupDB
->CreateUnique(nsIFile::NORMAL_FILE_TYPE
, 0600);
915 NS_ENSURE_SUCCESS(rv
, rv
);
917 nsAutoString fileName
;
918 rv
= backupDB
->GetLeafName(fileName
);
919 NS_ENSURE_SUCCESS(rv
, rv
);
921 rv
= backupDB
->Remove(false);
922 NS_ENSURE_SUCCESS(rv
, rv
);
924 backupDB
.forget(backup
);
926 return aDBFile
->CopyTo(parentDir
, fileName
);
929 ////////////////////////////////////////////////////////////////////////////////
933 Service::Observe(nsISupports
*, const char *aTopic
, const char16_t
*)
935 if (strcmp(aTopic
, "memory-pressure") == 0) {
937 } else if (strcmp(aTopic
, "xpcom-shutdown") == 0) {
939 } else if (strcmp(aTopic
, "xpcom-shutdown-threads") == 0) {
940 nsCOMPtr
<nsIObserverService
> os
=
941 mozilla::services::GetObserverService();
943 for (size_t i
= 0; i
< ArrayLength(sObserverTopics
); ++i
) {
944 (void)os
->RemoveObserver(this, sObserverTopics
[i
]);
947 bool anyOpen
= false;
949 nsTArray
<RefPtr
<Connection
> > connections
;
950 getConnections(connections
);
952 for (uint32_t i
= 0; i
< connections
.Length(); i
++) {
953 RefPtr
<Connection
> &conn
= connections
[i
];
954 if (conn
->isClosing()) {
960 nsCOMPtr
<nsIThread
> thread
= do_GetCurrentThread();
961 NS_ProcessNextEvent(thread
);
965 if (gShutdownChecks
== SCM_CRASH
) {
966 nsTArray
<RefPtr
<Connection
> > connections
;
967 getConnections(connections
);
968 for (uint32_t i
= 0, n
= connections
.Length(); i
< n
; i
++) {
969 if (!connections
[i
]->isClosed()) {
979 } // namespace storage
980 } // namespace mozilla