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/. */
10 #include "nsIMutableArray.h"
11 #include "nsAutoPtr.h"
12 #include "nsIMemoryReporter.h"
13 #include "nsThreadUtils.h"
15 #include "nsIFileURL.h"
16 #include "mozilla/Telemetry.h"
17 #include "mozilla/Mutex.h"
18 #include "mozilla/CondVar.h"
19 #include "mozilla/Attributes.h"
21 #include "mozIStorageAggregateFunction.h"
22 #include "mozIStorageCompletionCallback.h"
23 #include "mozIStorageFunction.h"
25 #include "mozStorageAsyncStatementExecution.h"
26 #include "mozStorageSQLFunctions.h"
27 #include "mozStorageConnection.h"
28 #include "mozStorageService.h"
29 #include "mozStorageStatement.h"
30 #include "mozStorageAsyncStatement.h"
31 #include "mozStorageArgValueArray.h"
32 #include "mozStoragePrivateHelpers.h"
33 #include "mozStorageStatementData.h"
34 #include "StorageBaseStatementInternal.h"
35 #include "SQLCollations.h"
36 #include "FileSystemModule.h"
37 #include "mozStorageHelper.h"
38 #include "GeckoProfiler.h"
42 #include "nsProxyRelease.h"
45 #define MIN_AVAILABLE_BYTES_PER_CHUNKED_GROWTH 524288000 // 500 MiB
47 // Maximum size of the pages cache per connection.
48 #define MAX_CACHE_SIZE_KIBIBYTES 2048 // 2 MiB
51 PRLogModuleInfo
* gStorageLog
= nullptr;
59 ////////////////////////////////////////////////////////////////////////////////
60 //// Variant Specialization Functions (variantToSQLiteT)
63 sqlite3_T_int(sqlite3_context
*aCtx
,
66 ::sqlite3_result_int(aCtx
, aValue
);
71 sqlite3_T_int64(sqlite3_context
*aCtx
,
74 ::sqlite3_result_int64(aCtx
, aValue
);
79 sqlite3_T_double(sqlite3_context
*aCtx
,
82 ::sqlite3_result_double(aCtx
, aValue
);
87 sqlite3_T_text(sqlite3_context
*aCtx
,
88 const nsCString
&aValue
)
90 ::sqlite3_result_text(aCtx
,
98 sqlite3_T_text16(sqlite3_context
*aCtx
,
99 const nsString
&aValue
)
101 ::sqlite3_result_text16(aCtx
,
103 aValue
.Length() * 2, // Number of bytes.
109 sqlite3_T_null(sqlite3_context
*aCtx
)
111 ::sqlite3_result_null(aCtx
);
116 sqlite3_T_blob(sqlite3_context
*aCtx
,
120 ::sqlite3_result_blob(aCtx
, aData
, aSize
, NS_Free
);
124 #include "variantToSQLiteT_impl.h"
126 ////////////////////////////////////////////////////////////////////////////////
132 int (*registerFunc
)(sqlite3
*, const char*);
135 Module gModules
[] = {
136 { "filesystem", RegisterFileSystemModule
}
139 ////////////////////////////////////////////////////////////////////////////////
143 void tracefunc (void *aClosure
, const char *aStmt
)
145 PR_LOG(gStorageLog
, PR_LOG_DEBUG
, ("sqlite3_trace on %p for '%s'", aClosure
,
156 findFunctionEnumerator(const nsACString
&aKey
,
157 Connection::FunctionInfo aData
,
160 FFEArguments
*args
= static_cast<FFEArguments
*>(aUserArg
);
161 if (aData
.function
== args
->target
) {
163 return PL_DHASH_STOP
;
165 return PL_DHASH_NEXT
;
169 copyFunctionEnumerator(const nsACString
&aKey
,
170 Connection::FunctionInfo aData
,
173 NS_PRECONDITION(aData
.type
== Connection::FunctionInfo::SIMPLE
||
174 aData
.type
== Connection::FunctionInfo::AGGREGATE
,
175 "Invalid function type!");
177 Connection
*connection
= static_cast<Connection
*>(aUserArg
);
178 if (aData
.type
== Connection::FunctionInfo::SIMPLE
) {
179 mozIStorageFunction
*function
=
180 static_cast<mozIStorageFunction
*>(aData
.function
.get());
181 (void)connection
->CreateFunction(aKey
, aData
.numArgs
, function
);
184 mozIStorageAggregateFunction
*function
=
185 static_cast<mozIStorageAggregateFunction
*>(aData
.function
.get());
186 (void)connection
->CreateAggregateFunction(aKey
, aData
.numArgs
, function
);
189 return PL_DHASH_NEXT
;
193 basicFunctionHelper(sqlite3_context
*aCtx
,
195 sqlite3_value
**aArgv
)
197 void *userData
= ::sqlite3_user_data(aCtx
);
199 mozIStorageFunction
*func
= static_cast<mozIStorageFunction
*>(userData
);
201 nsRefPtr
<ArgValueArray
> arguments(new ArgValueArray(aArgc
, aArgv
));
205 nsCOMPtr
<nsIVariant
> result
;
206 if (NS_FAILED(func
->OnFunctionCall(arguments
, getter_AddRefs(result
)))) {
207 NS_WARNING("User function returned error code!");
208 ::sqlite3_result_error(aCtx
,
209 "User function returned error code",
213 int retcode
= variantToSQLiteT(aCtx
, result
);
214 if (retcode
== SQLITE_IGNORE
) {
215 ::sqlite3_result_int(aCtx
, SQLITE_IGNORE
);
216 } else if (retcode
!= SQLITE_OK
) {
217 NS_WARNING("User function returned invalid data type!");
218 ::sqlite3_result_error(aCtx
,
219 "User function returned invalid data type",
225 aggregateFunctionStepHelper(sqlite3_context
*aCtx
,
227 sqlite3_value
**aArgv
)
229 void *userData
= ::sqlite3_user_data(aCtx
);
230 mozIStorageAggregateFunction
*func
=
231 static_cast<mozIStorageAggregateFunction
*>(userData
);
233 nsRefPtr
<ArgValueArray
> arguments(new ArgValueArray(aArgc
, aArgv
));
237 if (NS_FAILED(func
->OnStep(arguments
)))
238 NS_WARNING("User aggregate step function returned error code!");
242 aggregateFunctionFinalHelper(sqlite3_context
*aCtx
)
244 void *userData
= ::sqlite3_user_data(aCtx
);
245 mozIStorageAggregateFunction
*func
=
246 static_cast<mozIStorageAggregateFunction
*>(userData
);
248 nsRefPtr
<nsIVariant
> result
;
249 if (NS_FAILED(func
->OnFinal(getter_AddRefs(result
)))) {
250 NS_WARNING("User aggregate final function returned error code!");
251 ::sqlite3_result_error(aCtx
,
252 "User aggregate final function returned error code",
257 if (variantToSQLiteT(aCtx
, result
) != SQLITE_OK
) {
258 NS_WARNING("User aggregate final function returned invalid data type!");
259 ::sqlite3_result_error(aCtx
,
260 "User aggregate final function returned invalid data type",
266 * This code is heavily based on the sample at:
267 * http://www.sqlite.org/unlock_notify.html
269 class UnlockNotification
273 : mMutex("UnlockNotification mMutex")
274 , mCondVar(mMutex
, "UnlockNotification condVar")
281 MutexAutoLock
lock(mMutex
);
283 (void)mCondVar
.Wait();
289 MutexAutoLock
lock(mMutex
);
291 (void)mCondVar
.Notify();
301 UnlockNotifyCallback(void **aArgs
,
304 for (int i
= 0; i
< aArgsSize
; i
++) {
305 UnlockNotification
*notification
=
306 static_cast<UnlockNotification
*>(aArgs
[i
]);
307 notification
->Signal();
312 WaitForUnlockNotify(sqlite3
* aDatabase
)
314 UnlockNotification notification
;
315 int srv
= ::sqlite3_unlock_notify(aDatabase
, UnlockNotifyCallback
,
317 MOZ_ASSERT(srv
== SQLITE_LOCKED
|| srv
== SQLITE_OK
);
318 if (srv
== SQLITE_OK
) {
325 } // anonymous namespace
327 ////////////////////////////////////////////////////////////////////////////////
332 class AsyncCloseConnection MOZ_FINAL
: public nsRunnable
335 AsyncCloseConnection(Connection
*aConnection
,
336 nsIRunnable
*aCallbackEvent
,
337 already_AddRefed
<nsIThread
> aAsyncExecutionThread
)
338 : mConnection(aConnection
)
339 , mCallbackEvent(aCallbackEvent
)
340 , mAsyncExecutionThread(aAsyncExecutionThread
)
347 // This code is executed on the background thread
348 bool onAsyncThread
= false;
349 (void)mAsyncExecutionThread
->IsOnCurrentThread(&onAsyncThread
);
350 MOZ_ASSERT(onAsyncThread
);
354 (void)mConnection
->internalClose();
357 if (mCallbackEvent
) {
358 nsCOMPtr
<nsIThread
> thread
;
359 (void)NS_GetMainThread(getter_AddRefs(thread
));
360 (void)thread
->Dispatch(mCallbackEvent
, NS_DISPATCH_NORMAL
);
366 ~AsyncCloseConnection() {
367 nsCOMPtr
<nsIThread
> thread
;
368 (void)NS_GetMainThread(getter_AddRefs(thread
));
369 // Handle ambiguous nsISupports inheritance.
370 Connection
*rawConnection
= nullptr;
371 mConnection
.swap(rawConnection
);
372 (void)NS_ProxyRelease(thread
,
373 NS_ISUPPORTS_CAST(mozIStorageConnection
*,
375 (void)NS_ProxyRelease(thread
, mCallbackEvent
);
378 nsRefPtr
<Connection
> mConnection
;
379 nsCOMPtr
<nsIRunnable
> mCallbackEvent
;
380 nsCOMPtr
<nsIThread
> mAsyncExecutionThread
;
384 * An event used to initialize the clone of a connection.
386 * Must be executed on the clone's async execution thread.
388 class AsyncInitializeClone MOZ_FINAL
: public nsRunnable
392 * @param aConnection The connection being cloned.
393 * @param aClone The clone.
394 * @param aReadOnly If |true|, the clone is read only.
395 * @param aCallback A callback to trigger once initialization
396 * is complete. This event will be called on
397 * aClone->threadOpenedOn.
399 AsyncInitializeClone(Connection
* aConnection
,
401 const bool aReadOnly
,
402 mozIStorageCompletionCallback
* aCallback
)
403 : mConnection(aConnection
)
405 , mReadOnly(aReadOnly
)
406 , mCallback(aCallback
)
408 MOZ_ASSERT(NS_IsMainThread());
412 MOZ_ASSERT (NS_GetCurrentThread() == mClone
->getAsyncExecutionTarget());
414 nsresult rv
= mConnection
->initializeClone(mClone
, mReadOnly
);
416 return Dispatch(rv
, nullptr);
418 return Dispatch(NS_OK
,
419 NS_ISUPPORTS_CAST(mozIStorageAsyncConnection
*, mClone
));
423 nsresult
Dispatch(nsresult aResult
, nsISupports
* aValue
) {
424 nsRefPtr
<CallbackComplete
> event
= new CallbackComplete(aResult
,
427 return mClone
->threadOpenedOn
->Dispatch(event
, NS_DISPATCH_NORMAL
);
430 ~AsyncInitializeClone() {
431 nsCOMPtr
<nsIThread
> thread
;
432 DebugOnly
<nsresult
> rv
= NS_GetMainThread(getter_AddRefs(thread
));
433 MOZ_ASSERT(NS_SUCCEEDED(rv
));
435 // Handle ambiguous nsISupports inheritance.
436 Connection
*rawConnection
= nullptr;
437 mConnection
.swap(rawConnection
);
438 (void)NS_ProxyRelease(thread
, NS_ISUPPORTS_CAST(mozIStorageConnection
*,
441 Connection
*rawClone
= nullptr;
442 mClone
.swap(rawClone
);
443 (void)NS_ProxyRelease(thread
, NS_ISUPPORTS_CAST(mozIStorageConnection
*,
446 // Generally, the callback will be released by CallbackComplete.
447 // However, if for some reason Run() is not executed, we still
448 // need to ensure that it is released here.
449 mozIStorageCompletionCallback
*rawCallback
= nullptr;
450 mCallback
.swap(rawCallback
);
451 (void)NS_ProxyRelease(thread
, rawCallback
);
454 nsRefPtr
<Connection
> mConnection
;
455 nsRefPtr
<Connection
> mClone
;
456 const bool mReadOnly
;
457 nsCOMPtr
<mozIStorageCompletionCallback
> mCallback
;
460 } // anonymous namespace
462 ////////////////////////////////////////////////////////////////////////////////
465 Connection::Connection(Service
*aService
,
468 : sharedAsyncExecutionMutex("Connection::sharedAsyncExecutionMutex")
469 , sharedDBMutex("Connection::sharedDBMutex")
470 , threadOpenedOn(do_GetCurrentThread())
472 , mAsyncExecutionThreadShuttingDown(false)
473 , mTransactionInProgress(false)
474 , mProgressHandler(nullptr)
476 , mStorageService(aService
)
477 , mAsyncOnly(aAsyncOnly
)
479 mStorageService
->registerConnection(this);
482 Connection::~Connection()
486 MOZ_ASSERT(!mAsyncExecutionThread
,
487 "AsyncClose has not been invoked on this connection!");
490 NS_IMPL_ADDREF(Connection
)
492 NS_INTERFACE_MAP_BEGIN(Connection
)
493 NS_INTERFACE_MAP_ENTRY(mozIStorageAsyncConnection
)
494 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor
)
495 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(mozIStorageConnection
, !mAsyncOnly
)
496 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, mozIStorageConnection
)
499 // This is identical to what NS_IMPL_RELEASE provides, but with the
500 // extra |1 == count| case.
501 NS_IMETHODIMP_(nsrefcnt
) Connection::Release(void)
503 NS_PRECONDITION(0 != mRefCnt
, "dup release");
504 nsrefcnt count
= --mRefCnt
;
505 NS_LOG_RELEASE(this, count
, "Connection");
507 // If the refcount is 1, the single reference must be from
508 // gService->mConnections (in class |Service|). Which means we can
509 // unregister it safely.
510 mStorageService
->unregisterConnection(this);
511 } else if (0 == count
) {
512 mRefCnt
= 1; /* stabilize */
513 #if 0 /* enable this to find non-threadsafe destructors: */
514 NS_ASSERT_OWNINGTHREAD(Connection
);
523 Connection::getAsyncExecutionTarget()
525 MutexAutoLock
lockedScope(sharedAsyncExecutionMutex
);
527 // If we are shutting down the asynchronous thread, don't hand out any more
528 // references to the thread.
529 if (mAsyncExecutionThreadShuttingDown
)
532 if (!mAsyncExecutionThread
) {
533 nsresult rv
= ::NS_NewThread(getter_AddRefs(mAsyncExecutionThread
));
535 NS_WARNING("Failed to create async thread.");
538 static nsThreadPoolNaming naming
;
539 naming
.SetThreadPoolName(NS_LITERAL_CSTRING("mozStorage"),
540 mAsyncExecutionThread
);
543 return mAsyncExecutionThread
;
547 Connection::initialize()
549 NS_ASSERTION (!mDBConn
, "Initialize called on already opened database!");
550 PROFILER_LABEL("storage", "Connection::initialize");
552 // in memory database requested, sqlite uses a magic file name
553 int srv
= ::sqlite3_open_v2(":memory:", &mDBConn
, mFlags
, nullptr);
554 if (srv
!= SQLITE_OK
) {
556 return convertResultCode(srv
);
559 return initializeInternal(nullptr);
563 Connection::initialize(nsIFile
*aDatabaseFile
)
565 NS_ASSERTION (aDatabaseFile
, "Passed null file!");
566 NS_ASSERTION (!mDBConn
, "Initialize called on already opened database!");
567 PROFILER_LABEL("storage", "Connection::initialize");
569 mDatabaseFile
= aDatabaseFile
;
572 nsresult rv
= aDatabaseFile
->GetPath(path
);
573 NS_ENSURE_SUCCESS(rv
, rv
);
575 int srv
= ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path
).get(), &mDBConn
,
577 if (srv
!= SQLITE_OK
) {
579 return convertResultCode(srv
);
582 rv
= initializeInternal(aDatabaseFile
);
583 NS_ENSURE_SUCCESS(rv
, rv
);
585 mDatabaseFile
= aDatabaseFile
;
591 Connection::initialize(nsIFileURL
*aFileURL
)
593 NS_ASSERTION (aFileURL
, "Passed null file URL!");
594 NS_ASSERTION (!mDBConn
, "Initialize called on already opened database!");
595 PROFILER_LABEL("storage", "Connection::initialize");
597 nsCOMPtr
<nsIFile
> databaseFile
;
598 nsresult rv
= aFileURL
->GetFile(getter_AddRefs(databaseFile
));
599 NS_ENSURE_SUCCESS(rv
, rv
);
602 rv
= aFileURL
->GetSpec(spec
);
603 NS_ENSURE_SUCCESS(rv
, rv
);
605 int srv
= ::sqlite3_open_v2(spec
.get(), &mDBConn
, mFlags
, nullptr);
606 if (srv
!= SQLITE_OK
) {
608 return convertResultCode(srv
);
611 rv
= initializeInternal(databaseFile
);
612 NS_ENSURE_SUCCESS(rv
, rv
);
615 mDatabaseFile
= databaseFile
;
622 Connection::initializeInternal(nsIFile
* aDatabaseFile
)
624 // Properly wrap the database handle's mutex.
625 sharedDBMutex
.initWithMutex(sqlite3_db_mutex(mDBConn
));
629 gStorageLog
= ::PR_NewLogModule("mozStorage");
631 ::sqlite3_trace(mDBConn
, tracefunc
, this);
633 nsAutoCString
leafName(":memory");
635 (void)aDatabaseFile
->GetNativeLeafName(leafName
);
636 PR_LOG(gStorageLog
, PR_LOG_NOTICE
, ("Opening connection to '%s' (%p)",
637 leafName
.get(), this));
640 int64_t pageSize
= Service::getDefaultPageSize();
642 // Set page_size to the preferred default value. This is effective only if
643 // the database has just been created, otherwise, if the database does not
644 // use WAL journal mode, a VACUUM operation will updated its page_size.
645 nsAutoCString
pageSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
646 "PRAGMA page_size = ");
647 pageSizeQuery
.AppendInt(pageSize
);
648 nsresult rv
= ExecuteSimpleSQL(pageSizeQuery
);
649 NS_ENSURE_SUCCESS(rv
, rv
);
651 // Setting the cache_size forces the database open, verifying if it is valid
652 // or corrupt. So this is executed regardless it being actually needed.
653 // The cache_size is calculated from the actual page_size, to save memory.
654 nsAutoCString
cacheSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
655 "PRAGMA cache_size = ");
656 cacheSizeQuery
.AppendInt(-MAX_CACHE_SIZE_KIBIBYTES
);
657 int srv
= executeSql(cacheSizeQuery
.get());
658 if (srv
!= SQLITE_OK
) {
659 ::sqlite3_close(mDBConn
);
661 return convertResultCode(srv
);
664 // Register our built-in SQL functions.
665 srv
= registerFunctions(mDBConn
);
666 if (srv
!= SQLITE_OK
) {
667 ::sqlite3_close(mDBConn
);
669 return convertResultCode(srv
);
672 // Register our built-in SQL collating sequences.
673 srv
= registerCollations(mDBConn
, mStorageService
);
674 if (srv
!= SQLITE_OK
) {
675 ::sqlite3_close(mDBConn
);
677 return convertResultCode(srv
);
680 // Set the synchronous PRAGMA, according to the preference.
681 switch (Service::getSynchronousPref()) {
683 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
684 "PRAGMA synchronous = FULL;"));
687 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
688 "PRAGMA synchronous = OFF;"));
692 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
693 "PRAGMA synchronous = NORMAL;"));
701 Connection::databaseElementExists(enum DatabaseElementType aElementType
,
702 const nsACString
&aElementName
,
705 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
707 nsCString
query("SELECT name FROM (SELECT * FROM sqlite_master UNION ALL "
708 "SELECT * FROM sqlite_temp_master) "
710 switch (aElementType
) {
712 query
.Append("index");
715 query
.Append("table");
718 query
.Append("' AND name ='");
719 query
.Append(aElementName
);
723 int srv
= prepareStatement(query
, &stmt
);
724 if (srv
!= SQLITE_OK
)
725 return convertResultCode(srv
);
727 srv
= stepStatement(stmt
);
728 // we just care about the return value from step
729 (void)::sqlite3_finalize(stmt
);
731 if (srv
== SQLITE_ROW
) {
735 if (srv
== SQLITE_DONE
) {
740 return convertResultCode(srv
);
744 Connection::findFunctionByInstance(nsISupports
*aInstance
)
746 sharedDBMutex
.assertCurrentThreadOwns();
747 FFEArguments args
= { aInstance
, false };
748 (void)mFunctions
.EnumerateRead(findFunctionEnumerator
, &args
);
753 Connection::sProgressHelper(void *aArg
)
755 Connection
*_this
= static_cast<Connection
*>(aArg
);
756 return _this
->progressHandler();
760 Connection::progressHandler()
762 sharedDBMutex
.assertCurrentThreadOwns();
763 if (mProgressHandler
) {
765 nsresult rv
= mProgressHandler
->OnProgress(this, &result
);
766 if (NS_FAILED(rv
)) return 0; // Don't break request
767 return result
? 1 : 0;
773 Connection::setClosedState()
775 // Ensure that we are on the correct thread to close the database.
777 nsresult rv
= threadOpenedOn
->IsOnCurrentThread(&onOpenedThread
);
778 NS_ENSURE_SUCCESS(rv
, rv
);
779 if (!onOpenedThread
) {
780 NS_ERROR("Must close the database on the thread that you opened it with!");
781 return NS_ERROR_UNEXPECTED
;
784 // Flag that we are shutting down the async thread, so that
785 // getAsyncExecutionTarget knows not to expose/create the async thread.
787 MutexAutoLock
lockedScope(sharedAsyncExecutionMutex
);
788 NS_ENSURE_FALSE(mAsyncExecutionThreadShuttingDown
, NS_ERROR_UNEXPECTED
);
789 mAsyncExecutionThreadShuttingDown
= true;
796 Connection::isClosing(bool aResultOnClosed
) {
797 MutexAutoLock
lockedScope(sharedAsyncExecutionMutex
);
798 return mAsyncExecutionThreadShuttingDown
&&
799 (aResultOnClosed
|| ConnectionReady());
803 Connection::internalClose()
806 // Sanity checks to make sure we are in the proper state before calling this.
807 NS_ASSERTION(mDBConn
, "Database connection is already null!");
809 { // Make sure we have marked our async thread as shutting down.
810 MutexAutoLock
lockedScope(sharedAsyncExecutionMutex
);
811 NS_ASSERTION(mAsyncExecutionThreadShuttingDown
,
812 "Did not call setClosedState!");
815 bool onOpeningThread
= false;
816 (void)threadOpenedOn
->IsOnCurrentThread(&onOpeningThread
);
820 nsAutoCString
leafName(":memory");
822 (void)mDatabaseFile
->GetNativeLeafName(leafName
);
823 PR_LOG(gStorageLog
, PR_LOG_NOTICE
, ("Closing connection to '%s'",
827 // Set the property to null before closing the connection, otherwise the other
828 // functions in the module may try to use the connection after it is closed.
829 sqlite3
*dbConn
= mDBConn
;
832 // At this stage, we may still have statements that need to be
833 // finalized. Attempt to close the database connection. This will
834 // always disconnect any virtual tables and cleanly finalize their
835 // internal statements. Once this is done, closing may fail due to
836 // unfinalized client statements, in which case we need to finalize
837 // these statements and close again.
839 int srv
= sqlite3_close(dbConn
);
841 if (srv
== SQLITE_BUSY
) {
842 // We still have non-finalized statements. Finalize them.
844 sqlite3_stmt
*stmt
= nullptr;
845 while ((stmt
= ::sqlite3_next_stmt(dbConn
, stmt
))) {
846 PR_LOG(gStorageLog
, PR_LOG_NOTICE
,
847 ("Auto-finalizing SQL statement '%s' (%x)",
852 char *msg
= ::PR_smprintf("SQL statement '%s' (%x) should have been finalized before closing the connection",
856 ::PR_smprintf_free(msg
);
859 srv
= ::sqlite3_finalize(stmt
);
862 if (srv
!= SQLITE_OK
) {
863 char *msg
= ::PR_smprintf("Could not finalize SQL statement '%s' (%x)",
867 ::PR_smprintf_free(msg
);
871 // Ensure that the loop continues properly, whether closing has succeeded
873 if (srv
== SQLITE_OK
) {
878 // Now that all statements have been finalized, we
879 // should be able to close.
880 srv
= ::sqlite3_close(dbConn
);
884 if (srv
!= SQLITE_OK
) {
885 MOZ_ASSERT(srv
== SQLITE_OK
,
886 "sqlite3_close failed. There are probably outstanding statements that are listed above!");
889 return convertResultCode(srv
);
893 Connection::getFilename()
895 nsCString
leafname(":memory:");
897 (void)mDatabaseFile
->GetNativeLeafName(leafname
);
903 Connection::stepStatement(sqlite3_stmt
*aStatement
)
905 MOZ_ASSERT(aStatement
);
906 bool checkedMainThread
= false;
907 TimeStamp startTime
= TimeStamp::Now();
909 // mDBConn may be null if the executing statement has been created and cached
910 // after a call to asyncClose() but before the connection has been nullified
911 // by internalClose(). In such a case closing the connection fails due to
912 // the existence of prepared statements, but mDBConn is set to null
913 // regardless. This usually happens when other tasks using cached statements
914 // are asynchronously scheduled for execution and any of them ends up after
915 // asyncClose. See bug 728653 for details.
917 return SQLITE_MISUSE
;
919 (void)::sqlite3_extended_result_codes(mDBConn
, 1);
922 while ((srv
= ::sqlite3_step(aStatement
)) == SQLITE_LOCKED_SHAREDCACHE
) {
923 if (!checkedMainThread
) {
924 checkedMainThread
= true;
925 if (::NS_IsMainThread()) {
926 NS_WARNING("We won't allow blocking on the main thread!");
931 srv
= WaitForUnlockNotify(mDBConn
);
932 if (srv
!= SQLITE_OK
) {
936 ::sqlite3_reset(aStatement
);
939 // Report very slow SQL statements to Telemetry
940 TimeDuration duration
= TimeStamp::Now() - startTime
;
941 const uint32_t threshold
=
942 NS_IsMainThread() ? Telemetry::kSlowSQLThresholdForMainThread
943 : Telemetry::kSlowSQLThresholdForHelperThreads
;
944 if (duration
.ToMilliseconds() >= threshold
) {
945 nsDependentCString
statementString(::sqlite3_sql(aStatement
));
946 Telemetry::RecordSlowSQLStatement(statementString
, getFilename(),
947 duration
.ToMilliseconds());
950 (void)::sqlite3_extended_result_codes(mDBConn
, 0);
951 // Drop off the extended result bits of the result code.
956 Connection::prepareStatement(const nsCString
&aSQL
,
957 sqlite3_stmt
**_stmt
)
959 bool checkedMainThread
= false;
961 (void)::sqlite3_extended_result_codes(mDBConn
, 1);
964 while((srv
= ::sqlite3_prepare_v2(mDBConn
,
968 nullptr)) == SQLITE_LOCKED_SHAREDCACHE
) {
969 if (!checkedMainThread
) {
970 checkedMainThread
= true;
971 if (::NS_IsMainThread()) {
972 NS_WARNING("We won't allow blocking on the main thread!");
977 srv
= WaitForUnlockNotify(mDBConn
);
978 if (srv
!= SQLITE_OK
) {
983 if (srv
!= SQLITE_OK
) {
985 warnMsg
.AppendLiteral("The SQL statement '");
986 warnMsg
.Append(aSQL
);
987 warnMsg
.AppendLiteral("' could not be compiled due to an error: ");
988 warnMsg
.Append(::sqlite3_errmsg(mDBConn
));
991 NS_WARNING(warnMsg
.get());
994 PR_LOG(gStorageLog
, PR_LOG_ERROR
, ("%s", warnMsg
.get()));
998 (void)::sqlite3_extended_result_codes(mDBConn
, 0);
999 // Drop off the extended result bits of the result code.
1000 int rc
= srv
& 0xFF;
1001 // sqlite will return OK on a comment only string and set _stmt to nullptr.
1002 // The callers of this function are used to only checking the return value,
1003 // so it is safer to return an error code.
1004 if (rc
== SQLITE_OK
&& *_stmt
== nullptr) {
1005 return SQLITE_MISUSE
;
1013 Connection::executeSql(const char *aSqlString
)
1016 return SQLITE_MISUSE
;
1018 TimeStamp startTime
= TimeStamp::Now();
1019 int srv
= ::sqlite3_exec(mDBConn
, aSqlString
, nullptr, nullptr, nullptr);
1021 // Report very slow SQL statements to Telemetry
1022 TimeDuration duration
= TimeStamp::Now() - startTime
;
1023 const uint32_t threshold
=
1024 NS_IsMainThread() ? Telemetry::kSlowSQLThresholdForMainThread
1025 : Telemetry::kSlowSQLThresholdForHelperThreads
;
1026 if (duration
.ToMilliseconds() >= threshold
) {
1027 nsDependentCString
statementString(aSqlString
);
1028 Telemetry::RecordSlowSQLStatement(statementString
, getFilename(),
1029 duration
.ToMilliseconds());
1035 ////////////////////////////////////////////////////////////////////////////////
1036 //// nsIInterfaceRequestor
1039 Connection::GetInterface(const nsIID
&aIID
,
1042 if (aIID
.Equals(NS_GET_IID(nsIEventTarget
))) {
1043 nsIEventTarget
*background
= getAsyncExecutionTarget();
1044 NS_IF_ADDREF(background
);
1045 *_result
= background
;
1048 return NS_ERROR_NO_INTERFACE
;
1051 ////////////////////////////////////////////////////////////////////////////////
1052 //// mozIStorageConnection
1058 return NS_ERROR_NOT_INITIALIZED
;
1060 { // Make sure we have not executed any asynchronous statements.
1061 // If this fails, the mDBConn will be left open, resulting in a leak.
1062 // Ideally we'd schedule some code to destroy the mDBConn once all its
1063 // async statements have finished executing; see bug 704030.
1064 MutexAutoLock
lockedScope(sharedAsyncExecutionMutex
);
1065 bool asyncCloseWasCalled
= !mAsyncExecutionThread
;
1066 NS_ENSURE_TRUE(asyncCloseWasCalled
, NS_ERROR_UNEXPECTED
);
1069 nsresult rv
= setClosedState();
1070 NS_ENSURE_SUCCESS(rv
, rv
);
1072 return internalClose();
1076 Connection::AsyncClose(mozIStorageCompletionCallback
*aCallback
)
1078 if (!NS_IsMainThread()) {
1079 return NS_ERROR_NOT_SAME_THREAD
;
1082 return NS_ERROR_NOT_INITIALIZED
;
1084 nsIEventTarget
*asyncThread
= getAsyncExecutionTarget();
1085 NS_ENSURE_TRUE(asyncThread
, NS_ERROR_NOT_INITIALIZED
);
1087 nsresult rv
= setClosedState();
1088 NS_ENSURE_SUCCESS(rv
, rv
);
1090 // Create our callback event if we were given a callback.
1091 nsCOMPtr
<nsIRunnable
> completeEvent
;
1093 completeEvent
= newCompletionEvent(aCallback
);
1096 // Create and dispatch our close event to the background thread.
1097 nsCOMPtr
<nsIRunnable
> closeEvent
;
1099 // We need to lock because we're modifying mAsyncExecutionThread
1100 MutexAutoLock
lockedScope(sharedAsyncExecutionMutex
);
1101 closeEvent
= new AsyncCloseConnection(this,
1103 mAsyncExecutionThread
.forget());
1106 rv
= asyncThread
->Dispatch(closeEvent
, NS_DISPATCH_NORMAL
);
1107 NS_ENSURE_SUCCESS(rv
, rv
);
1113 Connection::AsyncClone(bool aReadOnly
,
1114 mozIStorageCompletionCallback
*aCallback
)
1116 PROFILER_LABEL("storage", "Connection::Clone");
1117 if (!NS_IsMainThread()) {
1118 return NS_ERROR_NOT_SAME_THREAD
;
1121 return NS_ERROR_NOT_INITIALIZED
;
1123 return NS_ERROR_UNEXPECTED
;
1127 // Turn off SQLITE_OPEN_READWRITE, and set SQLITE_OPEN_READONLY.
1128 flags
= (~SQLITE_OPEN_READWRITE
& flags
) | SQLITE_OPEN_READONLY
;
1129 // Turn off SQLITE_OPEN_CREATE.
1130 flags
= (~SQLITE_OPEN_CREATE
& flags
);
1133 nsRefPtr
<Connection
> clone
= new Connection(mStorageService
, flags
,
1136 nsRefPtr
<AsyncInitializeClone
> initEvent
=
1137 new AsyncInitializeClone(this, clone
, aReadOnly
, aCallback
);
1138 nsCOMPtr
<nsIEventTarget
> target
= clone
->getAsyncExecutionTarget();
1140 return NS_ERROR_UNEXPECTED
;
1142 return target
->Dispatch(initEvent
, NS_DISPATCH_NORMAL
);
1146 Connection::initializeClone(Connection
* aClone
, bool aReadOnly
)
1148 nsresult rv
= mFileURL
? aClone
->initialize(mFileURL
)
1149 : aClone
->initialize(mDatabaseFile
);
1150 if (NS_FAILED(rv
)) {
1154 // Copy over pragmas from the original connection.
1155 static const char * pragmas
[] = {
1159 "journal_size_limit",
1161 "wal_autocheckpoint",
1163 for (uint32_t i
= 0; i
< ArrayLength(pragmas
); ++i
) {
1164 // Read-only connections just need cache_size and temp_store pragmas.
1165 if (aReadOnly
&& ::strcmp(pragmas
[i
], "cache_size") != 0 &&
1166 ::strcmp(pragmas
[i
], "temp_store") != 0) {
1170 nsAutoCString
pragmaQuery("PRAGMA ");
1171 pragmaQuery
.Append(pragmas
[i
]);
1172 nsCOMPtr
<mozIStorageStatement
> stmt
;
1173 rv
= CreateStatement(pragmaQuery
, getter_AddRefs(stmt
));
1174 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1175 bool hasResult
= false;
1176 if (stmt
&& NS_SUCCEEDED(stmt
->ExecuteStep(&hasResult
)) && hasResult
) {
1177 pragmaQuery
.AppendLiteral(" = ");
1178 pragmaQuery
.AppendInt(stmt
->AsInt32(0));
1179 rv
= aClone
->ExecuteSimpleSQL(pragmaQuery
);
1180 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1184 // Copy any functions that have been added to this connection.
1185 SQLiteMutexAutoLock
lockedScope(sharedDBMutex
);
1186 (void)mFunctions
.EnumerateRead(copyFunctionEnumerator
, aClone
);
1192 Connection::Clone(bool aReadOnly
,
1193 mozIStorageConnection
**_connection
)
1195 MOZ_ASSERT(threadOpenedOn
== NS_GetCurrentThread());
1197 PROFILER_LABEL("storage", "Connection::Clone");
1199 return NS_ERROR_NOT_INITIALIZED
;
1201 return NS_ERROR_UNEXPECTED
;
1205 // Turn off SQLITE_OPEN_READWRITE, and set SQLITE_OPEN_READONLY.
1206 flags
= (~SQLITE_OPEN_READWRITE
& flags
) | SQLITE_OPEN_READONLY
;
1207 // Turn off SQLITE_OPEN_CREATE.
1208 flags
= (~SQLITE_OPEN_CREATE
& flags
);
1211 nsRefPtr
<Connection
> clone
= new Connection(mStorageService
, flags
,
1214 nsresult rv
= initializeClone(clone
, aReadOnly
);
1215 if (NS_FAILED(rv
)) {
1219 NS_IF_ADDREF(*_connection
= clone
);
1224 Connection::GetDefaultPageSize(int32_t *_defaultPageSize
)
1226 *_defaultPageSize
= Service::getDefaultPageSize();
1231 Connection::GetConnectionReady(bool *_ready
)
1233 *_ready
= ConnectionReady();
1238 Connection::GetDatabaseFile(nsIFile
**_dbFile
)
1240 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1242 NS_IF_ADDREF(*_dbFile
= mDatabaseFile
);
1248 Connection::GetLastInsertRowID(int64_t *_id
)
1250 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1252 sqlite_int64 id
= ::sqlite3_last_insert_rowid(mDBConn
);
1259 Connection::GetAffectedRows(int32_t *_rows
)
1261 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1263 *_rows
= ::sqlite3_changes(mDBConn
);
1269 Connection::GetLastError(int32_t *_error
)
1271 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1273 *_error
= ::sqlite3_errcode(mDBConn
);
1279 Connection::GetLastErrorString(nsACString
&_errorString
)
1281 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1283 const char *serr
= ::sqlite3_errmsg(mDBConn
);
1284 _errorString
.Assign(serr
);
1290 Connection::GetSchemaVersion(int32_t *_version
)
1292 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1294 nsCOMPtr
<mozIStorageStatement
> stmt
;
1295 (void)CreateStatement(NS_LITERAL_CSTRING("PRAGMA user_version"),
1296 getter_AddRefs(stmt
));
1297 NS_ENSURE_TRUE(stmt
, NS_ERROR_OUT_OF_MEMORY
);
1301 if (NS_SUCCEEDED(stmt
->ExecuteStep(&hasResult
)) && hasResult
)
1302 *_version
= stmt
->AsInt32(0);
1308 Connection::SetSchemaVersion(int32_t aVersion
)
1310 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1312 nsAutoCString
stmt(NS_LITERAL_CSTRING("PRAGMA user_version = "));
1313 stmt
.AppendInt(aVersion
);
1315 return ExecuteSimpleSQL(stmt
);
1319 Connection::CreateStatement(const nsACString
&aSQLStatement
,
1320 mozIStorageStatement
**_stmt
)
1322 NS_ENSURE_ARG_POINTER(_stmt
);
1323 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1325 nsRefPtr
<Statement
> statement(new Statement());
1326 NS_ENSURE_TRUE(statement
, NS_ERROR_OUT_OF_MEMORY
);
1328 nsresult rv
= statement
->initialize(this, aSQLStatement
);
1329 NS_ENSURE_SUCCESS(rv
, rv
);
1332 statement
.forget(&rawPtr
);
1338 Connection::CreateAsyncStatement(const nsACString
&aSQLStatement
,
1339 mozIStorageAsyncStatement
**_stmt
)
1341 NS_ENSURE_ARG_POINTER(_stmt
);
1342 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1344 nsRefPtr
<AsyncStatement
> statement(new AsyncStatement());
1345 NS_ENSURE_TRUE(statement
, NS_ERROR_OUT_OF_MEMORY
);
1347 nsresult rv
= statement
->initialize(this, aSQLStatement
);
1348 NS_ENSURE_SUCCESS(rv
, rv
);
1350 AsyncStatement
*rawPtr
;
1351 statement
.forget(&rawPtr
);
1357 Connection::ExecuteSimpleSQL(const nsACString
&aSQLStatement
)
1359 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1361 int srv
= executeSql(PromiseFlatCString(aSQLStatement
).get());
1362 return convertResultCode(srv
);
1366 Connection::ExecuteAsync(mozIStorageBaseStatement
**aStatements
,
1367 uint32_t aNumStatements
,
1368 mozIStorageStatementCallback
*aCallback
,
1369 mozIStoragePendingStatement
**_handle
)
1371 nsTArray
<StatementData
> stmts(aNumStatements
);
1372 for (uint32_t i
= 0; i
< aNumStatements
; i
++) {
1373 nsCOMPtr
<StorageBaseStatementInternal
> stmt
=
1374 do_QueryInterface(aStatements
[i
]);
1376 // Obtain our StatementData.
1378 nsresult rv
= stmt
->getAsynchronousStatementData(data
);
1379 NS_ENSURE_SUCCESS(rv
, rv
);
1381 NS_ASSERTION(stmt
->getOwner() == this,
1382 "Statement must be from this database connection!");
1384 // Now append it to our array.
1385 NS_ENSURE_TRUE(stmts
.AppendElement(data
), NS_ERROR_OUT_OF_MEMORY
);
1388 // Dispatch to the background
1389 return AsyncExecuteStatements::execute(stmts
, this, aCallback
, _handle
);
1393 Connection::TableExists(const nsACString
&aTableName
,
1396 return databaseElementExists(TABLE
, aTableName
, _exists
);
1400 Connection::IndexExists(const nsACString
&aIndexName
,
1403 return databaseElementExists(INDEX
, aIndexName
, _exists
);
1407 Connection::GetTransactionInProgress(bool *_inProgress
)
1409 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1411 SQLiteMutexAutoLock
lockedScope(sharedDBMutex
);
1412 *_inProgress
= mTransactionInProgress
;
1417 Connection::BeginTransaction()
1419 return BeginTransactionAs(mozIStorageConnection::TRANSACTION_DEFERRED
);
1423 Connection::BeginTransactionAs(int32_t aTransactionType
)
1425 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1427 SQLiteMutexAutoLock
lockedScope(sharedDBMutex
);
1428 if (mTransactionInProgress
)
1429 return NS_ERROR_FAILURE
;
1431 switch(aTransactionType
) {
1432 case TRANSACTION_DEFERRED
:
1433 rv
= ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN DEFERRED"));
1435 case TRANSACTION_IMMEDIATE
:
1436 rv
= ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN IMMEDIATE"));
1438 case TRANSACTION_EXCLUSIVE
:
1439 rv
= ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN EXCLUSIVE"));
1442 return NS_ERROR_ILLEGAL_VALUE
;
1444 if (NS_SUCCEEDED(rv
))
1445 mTransactionInProgress
= true;
1450 Connection::CommitTransaction()
1453 return NS_ERROR_NOT_INITIALIZED
;
1455 SQLiteMutexAutoLock
lockedScope(sharedDBMutex
);
1456 if (!mTransactionInProgress
)
1457 return NS_ERROR_UNEXPECTED
;
1459 nsresult rv
= ExecuteSimpleSQL(NS_LITERAL_CSTRING("COMMIT TRANSACTION"));
1460 if (NS_SUCCEEDED(rv
))
1461 mTransactionInProgress
= false;
1466 Connection::RollbackTransaction()
1469 return NS_ERROR_NOT_INITIALIZED
;
1471 SQLiteMutexAutoLock
lockedScope(sharedDBMutex
);
1472 if (!mTransactionInProgress
)
1473 return NS_ERROR_UNEXPECTED
;
1475 nsresult rv
= ExecuteSimpleSQL(NS_LITERAL_CSTRING("ROLLBACK TRANSACTION"));
1476 if (NS_SUCCEEDED(rv
))
1477 mTransactionInProgress
= false;
1482 Connection::CreateTable(const char *aTableName
,
1483 const char *aTableSchema
)
1485 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1487 char *buf
= ::PR_smprintf("CREATE TABLE %s (%s)", aTableName
, aTableSchema
);
1489 return NS_ERROR_OUT_OF_MEMORY
;
1491 int srv
= executeSql(buf
);
1492 ::PR_smprintf_free(buf
);
1494 return convertResultCode(srv
);
1498 Connection::CreateFunction(const nsACString
&aFunctionName
,
1499 int32_t aNumArguments
,
1500 mozIStorageFunction
*aFunction
)
1502 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1504 // Check to see if this function is already defined. We only check the name
1505 // because a function can be defined with the same body but different names.
1506 SQLiteMutexAutoLock
lockedScope(sharedDBMutex
);
1507 NS_ENSURE_FALSE(mFunctions
.Get(aFunctionName
, nullptr), NS_ERROR_FAILURE
);
1509 int srv
= ::sqlite3_create_function(mDBConn
,
1510 nsPromiseFlatCString(aFunctionName
).get(),
1514 basicFunctionHelper
,
1517 if (srv
!= SQLITE_OK
)
1518 return convertResultCode(srv
);
1520 FunctionInfo info
= { aFunction
,
1521 Connection::FunctionInfo::SIMPLE
,
1523 mFunctions
.Put(aFunctionName
, info
);
1529 Connection::CreateAggregateFunction(const nsACString
&aFunctionName
,
1530 int32_t aNumArguments
,
1531 mozIStorageAggregateFunction
*aFunction
)
1533 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1535 // Check to see if this function name is already defined.
1536 SQLiteMutexAutoLock
lockedScope(sharedDBMutex
);
1537 NS_ENSURE_FALSE(mFunctions
.Get(aFunctionName
, nullptr), NS_ERROR_FAILURE
);
1539 // Because aggregate functions depend on state across calls, you cannot have
1540 // the same instance use the same name. We want to enumerate all functions
1541 // and make sure this instance is not already registered.
1542 NS_ENSURE_FALSE(findFunctionByInstance(aFunction
), NS_ERROR_FAILURE
);
1544 int srv
= ::sqlite3_create_function(mDBConn
,
1545 nsPromiseFlatCString(aFunctionName
).get(),
1550 aggregateFunctionStepHelper
,
1551 aggregateFunctionFinalHelper
);
1552 if (srv
!= SQLITE_OK
)
1553 return convertResultCode(srv
);
1555 FunctionInfo info
= { aFunction
,
1556 Connection::FunctionInfo::AGGREGATE
,
1558 mFunctions
.Put(aFunctionName
, info
);
1564 Connection::RemoveFunction(const nsACString
&aFunctionName
)
1566 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1568 SQLiteMutexAutoLock
lockedScope(sharedDBMutex
);
1569 NS_ENSURE_TRUE(mFunctions
.Get(aFunctionName
, nullptr), NS_ERROR_FAILURE
);
1571 int srv
= ::sqlite3_create_function(mDBConn
,
1572 nsPromiseFlatCString(aFunctionName
).get(),
1579 if (srv
!= SQLITE_OK
)
1580 return convertResultCode(srv
);
1582 mFunctions
.Remove(aFunctionName
);
1588 Connection::SetProgressHandler(int32_t aGranularity
,
1589 mozIStorageProgressHandler
*aHandler
,
1590 mozIStorageProgressHandler
**_oldHandler
)
1592 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1594 // Return previous one
1595 SQLiteMutexAutoLock
lockedScope(sharedDBMutex
);
1596 NS_IF_ADDREF(*_oldHandler
= mProgressHandler
);
1598 if (!aHandler
|| aGranularity
<= 0) {
1602 mProgressHandler
= aHandler
;
1603 ::sqlite3_progress_handler(mDBConn
, aGranularity
, sProgressHelper
, this);
1609 Connection::RemoveProgressHandler(mozIStorageProgressHandler
**_oldHandler
)
1611 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1613 // Return previous one
1614 SQLiteMutexAutoLock
lockedScope(sharedDBMutex
);
1615 NS_IF_ADDREF(*_oldHandler
= mProgressHandler
);
1617 mProgressHandler
= nullptr;
1618 ::sqlite3_progress_handler(mDBConn
, 0, nullptr, nullptr);
1624 Connection::SetGrowthIncrement(int32_t aChunkSize
, const nsACString
&aDatabaseName
)
1626 // Bug 597215: Disk space is extremely limited on Android
1627 // so don't preallocate space. This is also not effective
1628 // on log structured file systems used by Android devices
1629 #if !defined(ANDROID) && !defined(MOZ_PLATFORM_MAEMO)
1630 // Don't preallocate if less than 500MiB is available.
1631 int64_t bytesAvailable
;
1632 nsresult rv
= mDatabaseFile
->GetDiskSpaceAvailable(&bytesAvailable
);
1633 NS_ENSURE_SUCCESS(rv
, rv
);
1634 if (bytesAvailable
< MIN_AVAILABLE_BYTES_PER_CHUNKED_GROWTH
) {
1635 return NS_ERROR_FILE_TOO_BIG
;
1638 (void)::sqlite3_file_control(mDBConn
,
1639 aDatabaseName
.Length() ? nsPromiseFlatCString(aDatabaseName
).get()
1641 SQLITE_FCNTL_CHUNK_SIZE
,
1648 Connection::EnableModule(const nsACString
& aModuleName
)
1650 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1652 for (size_t i
= 0; i
< ArrayLength(gModules
); i
++) {
1653 struct Module
* m
= &gModules
[i
];
1654 if (aModuleName
.Equals(m
->name
)) {
1655 int srv
= m
->registerFunc(mDBConn
, m
->name
);
1656 if (srv
!= SQLITE_OK
)
1657 return convertResultCode(srv
);
1663 return NS_ERROR_FAILURE
;
1666 } // namespace storage
1667 } // namespace mozilla