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"
20 #include "mozilla/ErrorNames.h"
21 #include "mozilla/Unused.h"
22 #include "mozilla/dom/quota/QuotaObject.h"
24 #include "mozIStorageAggregateFunction.h"
25 #include "mozIStorageCompletionCallback.h"
26 #include "mozIStorageFunction.h"
28 #include "mozStorageAsyncStatementExecution.h"
29 #include "mozStorageSQLFunctions.h"
30 #include "mozStorageConnection.h"
31 #include "mozStorageService.h"
32 #include "mozStorageStatement.h"
33 #include "mozStorageAsyncStatement.h"
34 #include "mozStorageArgValueArray.h"
35 #include "mozStoragePrivateHelpers.h"
36 #include "mozStorageStatementData.h"
37 #include "StorageBaseStatementInternal.h"
38 #include "SQLCollations.h"
39 #include "FileSystemModule.h"
40 #include "mozStorageHelper.h"
41 #include "GeckoProfiler.h"
43 #include "mozilla/Logging.h"
45 #include "nsProxyRelease.h"
48 #define MIN_AVAILABLE_BYTES_PER_CHUNKED_GROWTH 524288000 // 500 MiB
50 // Maximum size of the pages cache per connection.
51 #define MAX_CACHE_SIZE_KIBIBYTES 2048 // 2 MiB
53 mozilla::LazyLogModule
gStorageLog("mozStorage");
55 // Checks that the protected code is running on the main-thread only if the
56 // connection was also opened on it.
58 #define CHECK_MAINTHREAD_ABUSE() \
60 nsCOMPtr<nsIThread> mainThread = do_GetMainThread(); \
61 NS_WARNING_ASSERTION( \
62 threadOpenedOn == mainThread || !NS_IsMainThread(), \
63 "Using Storage synchronous API on main-thread, but the connection was " \
64 "opened on another thread."); \
67 #define CHECK_MAINTHREAD_ABUSE() do { /* Nothing */ } while(0)
73 using mozilla::dom::quota::QuotaObject
;
78 nsresultToSQLiteResult(nsresult aXPCOMResultCode
)
80 if (NS_SUCCEEDED(aXPCOMResultCode
)) {
84 switch (aXPCOMResultCode
) {
85 case NS_ERROR_FILE_CORRUPTED
:
86 return SQLITE_CORRUPT
;
87 case NS_ERROR_FILE_ACCESS_DENIED
:
88 return SQLITE_CANTOPEN
;
89 case NS_ERROR_STORAGE_BUSY
:
91 case NS_ERROR_FILE_IS_LOCKED
:
93 case NS_ERROR_FILE_READ_ONLY
:
94 return SQLITE_READONLY
;
95 case NS_ERROR_STORAGE_IOERR
:
97 case NS_ERROR_FILE_NO_DEVICE_SPACE
:
99 case NS_ERROR_OUT_OF_MEMORY
:
101 case NS_ERROR_UNEXPECTED
:
102 return SQLITE_MISUSE
;
105 case NS_ERROR_STORAGE_CONSTRAINT
:
106 return SQLITE_CONSTRAINT
;
111 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Must return in switch above!");
114 ////////////////////////////////////////////////////////////////////////////////
115 //// Variant Specialization Functions (variantToSQLiteT)
118 sqlite3_T_int(sqlite3_context
*aCtx
,
121 ::sqlite3_result_int(aCtx
, aValue
);
126 sqlite3_T_int64(sqlite3_context
*aCtx
,
127 sqlite3_int64 aValue
)
129 ::sqlite3_result_int64(aCtx
, aValue
);
134 sqlite3_T_double(sqlite3_context
*aCtx
,
137 ::sqlite3_result_double(aCtx
, aValue
);
142 sqlite3_T_text(sqlite3_context
*aCtx
,
143 const nsCString
&aValue
)
145 ::sqlite3_result_text(aCtx
,
153 sqlite3_T_text16(sqlite3_context
*aCtx
,
154 const nsString
&aValue
)
156 ::sqlite3_result_text16(aCtx
,
158 aValue
.Length() * 2, // Number of bytes.
164 sqlite3_T_null(sqlite3_context
*aCtx
)
166 ::sqlite3_result_null(aCtx
);
171 sqlite3_T_blob(sqlite3_context
*aCtx
,
175 ::sqlite3_result_blob(aCtx
, aData
, aSize
, free
);
179 #include "variantToSQLiteT_impl.h"
181 ////////////////////////////////////////////////////////////////////////////////
187 int (*registerFunc
)(sqlite3
*, const char*);
190 Module gModules
[] = {
191 { "filesystem", RegisterFileSystemModule
}
194 ////////////////////////////////////////////////////////////////////////////////
197 int tracefunc (unsigned aReason
, void *aClosure
, void *aP
, void *aX
)
200 case SQLITE_TRACE_STMT
: {
201 // aP is a pointer to the prepared statement.
202 sqlite3_stmt
* stmt
= static_cast<sqlite3_stmt
*>(aP
);
203 // aX is a pointer to a string containing the unexpanded SQL or a comment,
204 // starting with "--"" in case of a trigger.
205 char* expanded
= static_cast<char*>(aX
);
206 // Simulate what sqlite_trace was doing.
207 if (!::strncmp(expanded
, "--", 2)) {
208 MOZ_LOG(gStorageLog
, LogLevel::Debug
,
209 ("TRACE_STMT on %p: '%s'", aClosure
, expanded
));
211 char* sql
= ::sqlite3_expanded_sql(stmt
);
212 MOZ_LOG(gStorageLog
, LogLevel::Debug
,
213 ("TRACE_STMT on %p: '%s'", aClosure
, sql
));
218 case SQLITE_TRACE_PROFILE
: {
219 // aX is pointer to a 64bit integer containing nanoseconds it took to
220 // execute the last command.
221 sqlite_int64 time
= *(static_cast<sqlite_int64
*>(aX
)) / 1000000;
223 MOZ_LOG(gStorageLog
, LogLevel::Debug
,
224 ("TRACE_TIME on %p: %dms", aClosure
, time
));
233 basicFunctionHelper(sqlite3_context
*aCtx
,
235 sqlite3_value
**aArgv
)
237 void *userData
= ::sqlite3_user_data(aCtx
);
239 mozIStorageFunction
*func
= static_cast<mozIStorageFunction
*>(userData
);
241 RefPtr
<ArgValueArray
> arguments(new ArgValueArray(aArgc
, aArgv
));
245 nsCOMPtr
<nsIVariant
> result
;
246 nsresult rv
= func
->OnFunctionCall(arguments
, getter_AddRefs(result
));
248 nsAutoCString errorMessage
;
249 GetErrorName(rv
, errorMessage
);
250 errorMessage
.InsertLiteral("User function returned ", 0);
251 errorMessage
.Append('!');
253 NS_WARNING(errorMessage
.get());
255 ::sqlite3_result_error(aCtx
, errorMessage
.get(), -1);
256 ::sqlite3_result_error_code(aCtx
, nsresultToSQLiteResult(rv
));
259 int retcode
= variantToSQLiteT(aCtx
, result
);
260 if (retcode
== SQLITE_IGNORE
) {
261 ::sqlite3_result_int(aCtx
, SQLITE_IGNORE
);
262 } else if (retcode
!= SQLITE_OK
) {
263 NS_WARNING("User function returned invalid data type!");
264 ::sqlite3_result_error(aCtx
,
265 "User function returned invalid data type",
271 aggregateFunctionStepHelper(sqlite3_context
*aCtx
,
273 sqlite3_value
**aArgv
)
275 void *userData
= ::sqlite3_user_data(aCtx
);
276 mozIStorageAggregateFunction
*func
=
277 static_cast<mozIStorageAggregateFunction
*>(userData
);
279 RefPtr
<ArgValueArray
> arguments(new ArgValueArray(aArgc
, aArgv
));
283 if (NS_FAILED(func
->OnStep(arguments
)))
284 NS_WARNING("User aggregate step function returned error code!");
288 aggregateFunctionFinalHelper(sqlite3_context
*aCtx
)
290 void *userData
= ::sqlite3_user_data(aCtx
);
291 mozIStorageAggregateFunction
*func
=
292 static_cast<mozIStorageAggregateFunction
*>(userData
);
294 RefPtr
<nsIVariant
> result
;
295 if (NS_FAILED(func
->OnFinal(getter_AddRefs(result
)))) {
296 NS_WARNING("User aggregate final function returned error code!");
297 ::sqlite3_result_error(aCtx
,
298 "User aggregate final function returned error code",
303 if (variantToSQLiteT(aCtx
, result
) != SQLITE_OK
) {
304 NS_WARNING("User aggregate final function returned invalid data type!");
305 ::sqlite3_result_error(aCtx
,
306 "User aggregate final function returned invalid data type",
312 * This code is heavily based on the sample at:
313 * http://www.sqlite.org/unlock_notify.html
315 class UnlockNotification
319 : mMutex("UnlockNotification mMutex")
320 , mCondVar(mMutex
, "UnlockNotification condVar")
327 MutexAutoLock
lock(mMutex
);
329 (void)mCondVar
.Wait();
335 MutexAutoLock
lock(mMutex
);
337 (void)mCondVar
.Notify();
347 UnlockNotifyCallback(void **aArgs
,
350 for (int i
= 0; i
< aArgsSize
; i
++) {
351 UnlockNotification
*notification
=
352 static_cast<UnlockNotification
*>(aArgs
[i
]);
353 notification
->Signal();
358 WaitForUnlockNotify(sqlite3
* aDatabase
)
360 UnlockNotification notification
;
361 int srv
= ::sqlite3_unlock_notify(aDatabase
, UnlockNotifyCallback
,
363 MOZ_ASSERT(srv
== SQLITE_LOCKED
|| srv
== SQLITE_OK
);
364 if (srv
== SQLITE_OK
) {
373 ////////////////////////////////////////////////////////////////////////////////
378 class AsyncCloseConnection final
: public Runnable
381 AsyncCloseConnection(Connection
*aConnection
,
382 sqlite3
*aNativeConnection
,
383 nsIRunnable
*aCallbackEvent
,
384 already_AddRefed
<nsIThread
> aAsyncExecutionThread
)
385 : mConnection(aConnection
)
386 , mNativeConnection(aNativeConnection
)
387 , mCallbackEvent(aCallbackEvent
)
388 , mAsyncExecutionThread(aAsyncExecutionThread
)
392 NS_IMETHOD
Run() override
395 // This code is executed on the background thread
396 bool onAsyncThread
= false;
397 (void)mAsyncExecutionThread
->IsOnCurrentThread(&onAsyncThread
);
398 MOZ_ASSERT(onAsyncThread
);
401 nsCOMPtr
<nsIRunnable
> event
= NewRunnableMethod
<nsCOMPtr
<nsIThread
>>
402 (mConnection
, &Connection::shutdownAsyncThread
, mAsyncExecutionThread
);
403 (void)NS_DispatchToMainThread(event
);
406 (void)mConnection
->internalClose(mNativeConnection
);
409 if (mCallbackEvent
) {
410 nsCOMPtr
<nsIThread
> thread
;
411 (void)NS_GetMainThread(getter_AddRefs(thread
));
412 (void)thread
->Dispatch(mCallbackEvent
, NS_DISPATCH_NORMAL
);
418 ~AsyncCloseConnection() override
{
419 NS_ReleaseOnMainThread(mConnection
.forget());
420 NS_ReleaseOnMainThread(mCallbackEvent
.forget());
423 RefPtr
<Connection
> mConnection
;
424 sqlite3
*mNativeConnection
;
425 nsCOMPtr
<nsIRunnable
> mCallbackEvent
;
426 nsCOMPtr
<nsIThread
> mAsyncExecutionThread
;
430 * An event used to initialize the clone of a connection.
432 * Must be executed on the clone's async execution thread.
434 class AsyncInitializeClone final
: public Runnable
438 * @param aConnection The connection being cloned.
439 * @param aClone The clone.
440 * @param aReadOnly If |true|, the clone is read only.
441 * @param aCallback A callback to trigger once initialization
442 * is complete. This event will be called on
443 * aClone->threadOpenedOn.
445 AsyncInitializeClone(Connection
* aConnection
,
447 const bool aReadOnly
,
448 mozIStorageCompletionCallback
* aCallback
)
449 : mConnection(aConnection
)
451 , mReadOnly(aReadOnly
)
452 , mCallback(aCallback
)
454 MOZ_ASSERT(NS_IsMainThread());
457 NS_IMETHOD
Run() override
{
458 MOZ_ASSERT (NS_GetCurrentThread() == mConnection
->getAsyncExecutionTarget());
460 nsresult rv
= mConnection
->initializeClone(mClone
, mReadOnly
);
462 return Dispatch(rv
, nullptr);
464 return Dispatch(NS_OK
,
465 NS_ISUPPORTS_CAST(mozIStorageAsyncConnection
*, mClone
));
469 nsresult
Dispatch(nsresult aResult
, nsISupports
* aValue
) {
470 RefPtr
<CallbackComplete
> event
= new CallbackComplete(aResult
,
473 return mClone
->threadOpenedOn
->Dispatch(event
, NS_DISPATCH_NORMAL
);
476 ~AsyncInitializeClone() override
{
477 nsCOMPtr
<nsIThread
> thread
;
478 DebugOnly
<nsresult
> rv
= NS_GetMainThread(getter_AddRefs(thread
));
479 MOZ_ASSERT(NS_SUCCEEDED(rv
));
481 // Handle ambiguous nsISupports inheritance.
482 NS_ProxyRelease(thread
, mConnection
.forget());
483 NS_ProxyRelease(thread
, mClone
.forget());
485 // Generally, the callback will be released by CallbackComplete.
486 // However, if for some reason Run() is not executed, we still
487 // need to ensure that it is released here.
488 NS_ProxyRelease(thread
, mCallback
.forget());
491 RefPtr
<Connection
> mConnection
;
492 RefPtr
<Connection
> mClone
;
493 const bool mReadOnly
;
494 nsCOMPtr
<mozIStorageCompletionCallback
> mCallback
;
499 ////////////////////////////////////////////////////////////////////////////////
502 Connection::Connection(Service
*aService
,
505 bool aIgnoreLockingMode
)
506 : sharedAsyncExecutionMutex("Connection::sharedAsyncExecutionMutex")
507 , sharedDBMutex("Connection::sharedDBMutex")
508 , threadOpenedOn(do_GetCurrentThread())
510 , mAsyncExecutionThreadShuttingDown(false)
512 , mAsyncExecutionThreadIsAlive(false)
514 , mConnectionClosed(false)
515 , mTransactionInProgress(false)
516 , mProgressHandler(nullptr)
518 , mIgnoreLockingMode(aIgnoreLockingMode
)
519 , mStorageService(aService
)
520 , mAsyncOnly(aAsyncOnly
)
522 MOZ_ASSERT(!mIgnoreLockingMode
|| mFlags
& SQLITE_OPEN_READONLY
,
523 "Can't ignore locking for a non-readonly connection!");
524 mStorageService
->registerConnection(this);
527 Connection::~Connection()
531 MOZ_ASSERT(!mAsyncExecutionThread
,
532 "AsyncClose has not been invoked on this connection!");
533 MOZ_ASSERT(!mAsyncExecutionThreadIsAlive
,
534 "The async execution thread should have been shutdown!");
537 NS_IMPL_ADDREF(Connection
)
539 NS_INTERFACE_MAP_BEGIN(Connection
)
540 NS_INTERFACE_MAP_ENTRY(mozIStorageAsyncConnection
)
541 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor
)
542 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(mozIStorageConnection
, !mAsyncOnly
)
543 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, mozIStorageConnection
)
546 // This is identical to what NS_IMPL_RELEASE provides, but with the
547 // extra |1 == count| case.
548 NS_IMETHODIMP_(MozExternalRefCountType
) Connection::Release(void)
550 NS_PRECONDITION(0 != mRefCnt
, "dup release");
551 nsrefcnt count
= --mRefCnt
;
552 NS_LOG_RELEASE(this, count
, "Connection");
554 // If the refcount is 1, the single reference must be from
555 // gService->mConnections (in class |Service|). Which means we can
556 // unregister it safely.
557 mStorageService
->unregisterConnection(this);
558 } else if (0 == count
) {
559 mRefCnt
= 1; /* stabilize */
560 #if 0 /* enable this to find non-threadsafe destructors: */
561 NS_ASSERT_OWNINGTHREAD(Connection
);
570 Connection::getSqliteRuntimeStatus(int32_t aStatusOption
, int32_t* aMaxValue
)
572 MOZ_ASSERT(mDBConn
, "A connection must exist at this point");
573 int curr
= 0, max
= 0;
574 DebugOnly
<int> rc
= ::sqlite3_db_status(mDBConn
, aStatusOption
, &curr
, &max
, 0);
575 MOZ_ASSERT(NS_SUCCEEDED(convertResultCode(rc
)));
582 Connection::getAsyncExecutionTarget()
584 MutexAutoLock
lockedScope(sharedAsyncExecutionMutex
);
586 // If we are shutting down the asynchronous thread, don't hand out any more
587 // references to the thread.
588 if (mAsyncExecutionThreadShuttingDown
)
591 if (!mAsyncExecutionThread
) {
592 static nsThreadPoolNaming naming
;
593 nsresult rv
= NS_NewNamedThread(naming
.GetNextThreadName("mozStorage"),
594 getter_AddRefs(mAsyncExecutionThread
));
596 NS_WARNING("Failed to create async thread.");
602 mAsyncExecutionThreadIsAlive
= true;
605 return mAsyncExecutionThread
;
609 Connection::initialize()
611 NS_ASSERTION (!mDBConn
, "Initialize called on already opened database!");
612 MOZ_ASSERT(!mIgnoreLockingMode
, "Can't ignore locking on an in-memory db.");
613 PROFILER_LABEL("mozStorageConnection", "initialize",
614 js::ProfileEntry::Category::STORAGE
);
616 // in memory database requested, sqlite uses a magic file name
617 int srv
= ::sqlite3_open_v2(":memory:", &mDBConn
, mFlags
, nullptr);
618 if (srv
!= SQLITE_OK
) {
620 return convertResultCode(srv
);
623 // Do not set mDatabaseFile or mFileURL here since this is a "memory"
626 nsresult rv
= initializeInternal();
627 NS_ENSURE_SUCCESS(rv
, rv
);
633 Connection::initialize(nsIFile
*aDatabaseFile
)
635 NS_ASSERTION (aDatabaseFile
, "Passed null file!");
636 NS_ASSERTION (!mDBConn
, "Initialize called on already opened database!");
637 PROFILER_LABEL("mozStorageConnection", "initialize",
638 js::ProfileEntry::Category::STORAGE
);
640 mDatabaseFile
= aDatabaseFile
;
643 nsresult rv
= aDatabaseFile
->GetPath(path
);
644 NS_ENSURE_SUCCESS(rv
, rv
);
647 static const char* sIgnoreLockingVFS
= "win32-none";
649 static const char* sIgnoreLockingVFS
= "unix-none";
651 const char* vfs
= mIgnoreLockingMode
? sIgnoreLockingVFS
: nullptr;
653 int srv
= ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path
).get(), &mDBConn
,
655 if (srv
!= SQLITE_OK
) {
657 return convertResultCode(srv
);
660 // Do not set mFileURL here since this is database does not have an associated
662 mDatabaseFile
= aDatabaseFile
;
664 rv
= initializeInternal();
665 NS_ENSURE_SUCCESS(rv
, rv
);
671 Connection::initialize(nsIFileURL
*aFileURL
)
673 NS_ASSERTION (aFileURL
, "Passed null file URL!");
674 NS_ASSERTION (!mDBConn
, "Initialize called on already opened database!");
675 PROFILER_LABEL("mozStorageConnection", "initialize",
676 js::ProfileEntry::Category::STORAGE
);
678 nsCOMPtr
<nsIFile
> databaseFile
;
679 nsresult rv
= aFileURL
->GetFile(getter_AddRefs(databaseFile
));
680 NS_ENSURE_SUCCESS(rv
, rv
);
683 rv
= aFileURL
->GetSpec(spec
);
684 NS_ENSURE_SUCCESS(rv
, rv
);
686 int srv
= ::sqlite3_open_v2(spec
.get(), &mDBConn
, mFlags
, nullptr);
687 if (srv
!= SQLITE_OK
) {
689 return convertResultCode(srv
);
692 // Set both mDatabaseFile and mFileURL here.
694 mDatabaseFile
= databaseFile
;
696 rv
= initializeInternal();
697 NS_ENSURE_SUCCESS(rv
, rv
);
703 Connection::initializeInternal()
708 const char* dbPath
= ::sqlite3_db_filename(mDBConn
, "main");
711 const char* telemetryFilename
=
712 ::sqlite3_uri_parameter(dbPath
, "telemetryFilename");
713 if (telemetryFilename
) {
714 if (NS_WARN_IF(*telemetryFilename
== '\0')) {
715 return NS_ERROR_INVALID_ARG
;
717 mTelemetryFilename
= telemetryFilename
;
721 if (mTelemetryFilename
.IsEmpty()) {
722 mTelemetryFilename
= getFilename();
723 MOZ_ASSERT(!mTelemetryFilename
.IsEmpty());
726 // Properly wrap the database handle's mutex.
727 sharedDBMutex
.initWithMutex(sqlite3_db_mutex(mDBConn
));
729 // SQLite tracing can slow down queries (especially long queries)
730 // significantly. Don't trace unless the user is actively monitoring SQLite.
731 if (MOZ_LOG_TEST(gStorageLog
, LogLevel::Debug
)) {
732 ::sqlite3_trace_v2(mDBConn
,
733 SQLITE_TRACE_STMT
| SQLITE_TRACE_PROFILE
,
736 MOZ_LOG(gStorageLog
, LogLevel::Debug
, ("Opening connection to '%s' (%p)",
737 mTelemetryFilename
.get(), this));
740 int64_t pageSize
= Service::getDefaultPageSize();
742 // Set page_size to the preferred default value. This is effective only if
743 // the database has just been created, otherwise, if the database does not
744 // use WAL journal mode, a VACUUM operation will updated its page_size.
745 nsAutoCString
pageSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
746 "PRAGMA page_size = ");
747 pageSizeQuery
.AppendInt(pageSize
);
748 nsresult rv
= ExecuteSimpleSQL(pageSizeQuery
);
749 NS_ENSURE_SUCCESS(rv
, rv
);
751 // Setting the cache_size forces the database open, verifying if it is valid
752 // or corrupt. So this is executed regardless it being actually needed.
753 // The cache_size is calculated from the actual page_size, to save memory.
754 nsAutoCString
cacheSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
755 "PRAGMA cache_size = ");
756 cacheSizeQuery
.AppendInt(-MAX_CACHE_SIZE_KIBIBYTES
);
757 int srv
= executeSql(mDBConn
, cacheSizeQuery
.get());
758 if (srv
!= SQLITE_OK
) {
759 ::sqlite3_close(mDBConn
);
761 return convertResultCode(srv
);
764 #if defined(MOZ_MEMORY_TEMP_STORE_PRAGMA)
765 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA temp_store = 2;"));
768 // Register our built-in SQL functions.
769 srv
= registerFunctions(mDBConn
);
770 if (srv
!= SQLITE_OK
) {
771 ::sqlite3_close(mDBConn
);
773 return convertResultCode(srv
);
776 // Register our built-in SQL collating sequences.
777 srv
= registerCollations(mDBConn
, mStorageService
);
778 if (srv
!= SQLITE_OK
) {
779 ::sqlite3_close(mDBConn
);
781 return convertResultCode(srv
);
784 // Set the synchronous PRAGMA, according to the preference.
785 switch (Service::getSynchronousPref()) {
787 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
788 "PRAGMA synchronous = FULL;"));
791 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
792 "PRAGMA synchronous = OFF;"));
796 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
797 "PRAGMA synchronous = NORMAL;"));
805 Connection::databaseElementExists(enum DatabaseElementType aElementType
,
806 const nsACString
&aElementName
,
809 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
811 // When constructing the query, make sure to SELECT the correct db's sqlite_master
812 // if the user is prefixing the element with a specific db. ex: sample.test
813 nsCString
query("SELECT name FROM (SELECT * FROM ");
814 nsDependentCSubstring element
;
815 int32_t ind
= aElementName
.FindChar('.');
816 if (ind
== kNotFound
) {
817 element
.Assign(aElementName
);
820 nsDependentCSubstring
db(Substring(aElementName
, 0, ind
+ 1));
821 element
.Assign(Substring(aElementName
, ind
+ 1, aElementName
.Length()));
824 query
.AppendLiteral("sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type = '");
826 switch (aElementType
) {
828 query
.AppendLiteral("index");
831 query
.AppendLiteral("table");
834 query
.AppendLiteral("' AND name ='");
835 query
.Append(element
);
839 int srv
= prepareStatement(mDBConn
, query
, &stmt
);
840 if (srv
!= SQLITE_OK
)
841 return convertResultCode(srv
);
843 srv
= stepStatement(mDBConn
, stmt
);
844 // we just care about the return value from step
845 (void)::sqlite3_finalize(stmt
);
847 if (srv
== SQLITE_ROW
) {
851 if (srv
== SQLITE_DONE
) {
856 return convertResultCode(srv
);
860 Connection::findFunctionByInstance(nsISupports
*aInstance
)
862 sharedDBMutex
.assertCurrentThreadOwns();
864 for (auto iter
= mFunctions
.Iter(); !iter
.Done(); iter
.Next()) {
865 if (iter
.UserData().function
== aInstance
) {
873 Connection::sProgressHelper(void *aArg
)
875 Connection
*_this
= static_cast<Connection
*>(aArg
);
876 return _this
->progressHandler();
880 Connection::progressHandler()
882 sharedDBMutex
.assertCurrentThreadOwns();
883 if (mProgressHandler
) {
885 nsresult rv
= mProgressHandler
->OnProgress(this, &result
);
886 if (NS_FAILED(rv
)) return 0; // Don't break request
887 return result
? 1 : 0;
893 Connection::setClosedState()
895 // Ensure that we are on the correct thread to close the database.
897 nsresult rv
= threadOpenedOn
->IsOnCurrentThread(&onOpenedThread
);
898 NS_ENSURE_SUCCESS(rv
, rv
);
899 if (!onOpenedThread
) {
900 NS_ERROR("Must close the database on the thread that you opened it with!");
901 return NS_ERROR_UNEXPECTED
;
904 // Flag that we are shutting down the async thread, so that
905 // getAsyncExecutionTarget knows not to expose/create the async thread.
907 MutexAutoLock
lockedScope(sharedAsyncExecutionMutex
);
908 NS_ENSURE_FALSE(mAsyncExecutionThreadShuttingDown
, NS_ERROR_UNEXPECTED
);
909 mAsyncExecutionThreadShuttingDown
= true;
912 // Set the property to null before closing the connection, otherwise the other
913 // functions in the module may try to use the connection after it is closed.
920 Connection::connectionReady()
922 return mDBConn
!= nullptr;
926 Connection::isClosing()
928 bool shuttingDown
= false;
930 MutexAutoLock
lockedScope(sharedAsyncExecutionMutex
);
931 shuttingDown
= mAsyncExecutionThreadShuttingDown
;
933 return shuttingDown
&& !isClosed();
937 Connection::isClosed()
939 MutexAutoLock
lockedScope(sharedAsyncExecutionMutex
);
940 return mConnectionClosed
;
944 Connection::shutdownAsyncThread(nsIThread
*aThread
) {
945 MOZ_ASSERT(!mAsyncExecutionThread
);
946 MOZ_ASSERT(mAsyncExecutionThreadIsAlive
);
947 MOZ_ASSERT(mAsyncExecutionThreadShuttingDown
);
949 DebugOnly
<nsresult
> rv
= aThread
->Shutdown();
950 MOZ_ASSERT(NS_SUCCEEDED(rv
));
952 mAsyncExecutionThreadIsAlive
= false;
957 Connection::internalClose(sqlite3
*aNativeConnection
)
959 // Sanity checks to make sure we are in the proper state before calling this.
960 // aNativeConnection can be null if OpenAsyncDatabase failed and is now just
961 // cleaning up the async thread.
962 MOZ_ASSERT(!isClosed());
965 { // Make sure we have marked our async thread as shutting down.
966 MutexAutoLock
lockedScope(sharedAsyncExecutionMutex
);
967 NS_ASSERTION(mAsyncExecutionThreadShuttingDown
,
968 "Did not call setClosedState!");
972 if (MOZ_LOG_TEST(gStorageLog
, LogLevel::Debug
)) {
973 nsAutoCString
leafName(":memory");
975 (void)mDatabaseFile
->GetNativeLeafName(leafName
);
976 MOZ_LOG(gStorageLog
, LogLevel::Debug
, ("Closing connection to '%s'",
980 // At this stage, we may still have statements that need to be
981 // finalized. Attempt to close the database connection. This will
982 // always disconnect any virtual tables and cleanly finalize their
983 // internal statements. Once this is done, closing may fail due to
984 // unfinalized client statements, in which case we need to finalize
985 // these statements and close again.
987 MutexAutoLock
lockedScope(sharedAsyncExecutionMutex
);
988 mConnectionClosed
= true;
991 // Nothing else needs to be done if we don't have a connection here.
992 if (!aNativeConnection
)
995 int srv
= sqlite3_close(aNativeConnection
);
997 if (srv
== SQLITE_BUSY
) {
998 // We still have non-finalized statements. Finalize them.
1000 sqlite3_stmt
*stmt
= nullptr;
1001 while ((stmt
= ::sqlite3_next_stmt(aNativeConnection
, stmt
))) {
1002 MOZ_LOG(gStorageLog
, LogLevel::Debug
,
1003 ("Auto-finalizing SQL statement '%s' (%x)",
1004 ::sqlite3_sql(stmt
),
1008 char *msg
= ::PR_smprintf("SQL statement '%s' (%x) should have been finalized before closing the connection",
1009 ::sqlite3_sql(stmt
),
1012 ::PR_smprintf_free(msg
);
1016 srv
= ::sqlite3_finalize(stmt
);
1019 if (srv
!= SQLITE_OK
) {
1020 msg
= ::PR_smprintf("Could not finalize SQL statement '%s' (%x)",
1021 ::sqlite3_sql(stmt
),
1024 ::PR_smprintf_free(msg
);
1029 // Ensure that the loop continues properly, whether closing has succeeded
1031 if (srv
== SQLITE_OK
) {
1036 // Now that all statements have been finalized, we
1037 // should be able to close.
1038 srv
= ::sqlite3_close(aNativeConnection
);
1042 if (srv
!= SQLITE_OK
) {
1043 MOZ_ASSERT(srv
== SQLITE_OK
,
1044 "sqlite3_close failed. There are probably outstanding statements that are listed above!");
1047 return convertResultCode(srv
);
1051 Connection::getFilename()
1053 nsCString
leafname(":memory:");
1054 if (mDatabaseFile
) {
1055 (void)mDatabaseFile
->GetNativeLeafName(leafname
);
1061 Connection::stepStatement(sqlite3
*aNativeConnection
, sqlite3_stmt
*aStatement
)
1063 MOZ_ASSERT(aStatement
);
1064 bool checkedMainThread
= false;
1065 TimeStamp startTime
= TimeStamp::Now();
1067 // The connection may have been closed if the executing statement has been
1068 // created and cached after a call to asyncClose() but before the actual
1069 // sqlite3_close(). This usually happens when other tasks using cached
1070 // statements are asynchronously scheduled for execution and any of them ends
1071 // up after asyncClose. See bug 728653 for details.
1073 return SQLITE_MISUSE
;
1075 (void)::sqlite3_extended_result_codes(aNativeConnection
, 1);
1078 while ((srv
= ::sqlite3_step(aStatement
)) == SQLITE_LOCKED_SHAREDCACHE
) {
1079 if (!checkedMainThread
) {
1080 checkedMainThread
= true;
1081 if (::NS_IsMainThread()) {
1082 NS_WARNING("We won't allow blocking on the main thread!");
1087 srv
= WaitForUnlockNotify(aNativeConnection
);
1088 if (srv
!= SQLITE_OK
) {
1092 ::sqlite3_reset(aStatement
);
1095 // Report very slow SQL statements to Telemetry
1096 TimeDuration duration
= TimeStamp::Now() - startTime
;
1097 const uint32_t threshold
=
1098 NS_IsMainThread() ? Telemetry::kSlowSQLThresholdForMainThread
1099 : Telemetry::kSlowSQLThresholdForHelperThreads
;
1100 if (duration
.ToMilliseconds() >= threshold
) {
1101 nsDependentCString
statementString(::sqlite3_sql(aStatement
));
1102 Telemetry::RecordSlowSQLStatement(statementString
, mTelemetryFilename
,
1103 duration
.ToMilliseconds());
1106 (void)::sqlite3_extended_result_codes(aNativeConnection
, 0);
1107 // Drop off the extended result bits of the result code.
1112 Connection::prepareStatement(sqlite3
*aNativeConnection
, const nsCString
&aSQL
,
1113 sqlite3_stmt
**_stmt
)
1115 // We should not even try to prepare statements after the connection has
1118 return SQLITE_MISUSE
;
1120 bool checkedMainThread
= false;
1122 (void)::sqlite3_extended_result_codes(aNativeConnection
, 1);
1125 while((srv
= ::sqlite3_prepare_v2(aNativeConnection
,
1129 nullptr)) == SQLITE_LOCKED_SHAREDCACHE
) {
1130 if (!checkedMainThread
) {
1131 checkedMainThread
= true;
1132 if (::NS_IsMainThread()) {
1133 NS_WARNING("We won't allow blocking on the main thread!");
1138 srv
= WaitForUnlockNotify(aNativeConnection
);
1139 if (srv
!= SQLITE_OK
) {
1144 if (srv
!= SQLITE_OK
) {
1146 warnMsg
.AppendLiteral("The SQL statement '");
1147 warnMsg
.Append(aSQL
);
1148 warnMsg
.AppendLiteral("' could not be compiled due to an error: ");
1149 warnMsg
.Append(::sqlite3_errmsg(aNativeConnection
));
1152 NS_WARNING(warnMsg
.get());
1154 MOZ_LOG(gStorageLog
, LogLevel::Error
, ("%s", warnMsg
.get()));
1157 (void)::sqlite3_extended_result_codes(aNativeConnection
, 0);
1158 // Drop off the extended result bits of the result code.
1159 int rc
= srv
& 0xFF;
1160 // sqlite will return OK on a comment only string and set _stmt to nullptr.
1161 // The callers of this function are used to only checking the return value,
1162 // so it is safer to return an error code.
1163 if (rc
== SQLITE_OK
&& *_stmt
== nullptr) {
1164 return SQLITE_MISUSE
;
1172 Connection::executeSql(sqlite3
*aNativeConnection
, const char *aSqlString
)
1175 return SQLITE_MISUSE
;
1177 TimeStamp startTime
= TimeStamp::Now();
1178 int srv
= ::sqlite3_exec(aNativeConnection
, aSqlString
, nullptr, nullptr,
1181 // Report very slow SQL statements to Telemetry
1182 TimeDuration duration
= TimeStamp::Now() - startTime
;
1183 const uint32_t threshold
=
1184 NS_IsMainThread() ? Telemetry::kSlowSQLThresholdForMainThread
1185 : Telemetry::kSlowSQLThresholdForHelperThreads
;
1186 if (duration
.ToMilliseconds() >= threshold
) {
1187 nsDependentCString
statementString(aSqlString
);
1188 Telemetry::RecordSlowSQLStatement(statementString
, mTelemetryFilename
,
1189 duration
.ToMilliseconds());
1195 ////////////////////////////////////////////////////////////////////////////////
1196 //// nsIInterfaceRequestor
1199 Connection::GetInterface(const nsIID
&aIID
,
1202 if (aIID
.Equals(NS_GET_IID(nsIEventTarget
))) {
1203 nsIEventTarget
*background
= getAsyncExecutionTarget();
1204 NS_IF_ADDREF(background
);
1205 *_result
= background
;
1208 return NS_ERROR_NO_INTERFACE
;
1211 ////////////////////////////////////////////////////////////////////////////////
1212 //// mozIStorageConnection
1218 return NS_ERROR_NOT_INITIALIZED
;
1220 { // Make sure we have not executed any asynchronous statements.
1221 // If this fails, the mDBConn will be left open, resulting in a leak.
1222 // Ideally we'd schedule some code to destroy the mDBConn once all its
1223 // async statements have finished executing; see bug 704030.
1224 MutexAutoLock
lockedScope(sharedAsyncExecutionMutex
);
1225 bool asyncCloseWasCalled
= !mAsyncExecutionThread
;
1226 NS_ENSURE_TRUE(asyncCloseWasCalled
, NS_ERROR_UNEXPECTED
);
1229 // setClosedState nullifies our connection pointer, so we take a raw pointer
1230 // off it, to pass it through the close procedure.
1231 sqlite3
*nativeConn
= mDBConn
;
1232 nsresult rv
= setClosedState();
1233 NS_ENSURE_SUCCESS(rv
, rv
);
1235 return internalClose(nativeConn
);
1239 Connection::AsyncClose(mozIStorageCompletionCallback
*aCallback
)
1241 if (!NS_IsMainThread()) {
1242 return NS_ERROR_NOT_SAME_THREAD
;
1245 // The two relevant factors at this point are whether we have a database
1246 // connection and whether we have an async execution thread. Here's what the
1247 // states mean and how we handle them:
1249 // - (mDBConn && asyncThread): The expected case where we are either an
1250 // async connection or a sync connection that has been used asynchronously.
1251 // Either way the caller must call us and not Close(). Nothing surprising
1252 // about this. We'll dispatch AsyncCloseConnection to the already-existing
1255 // - (mDBConn && !asyncThread): A somewhat unusual case where the caller
1256 // opened the connection synchronously and was planning to use it
1257 // asynchronously, but never got around to using it asynchronously before
1258 // needing to shutdown. This has been observed to happen for the cookie
1259 // service in a case where Firefox shuts itself down almost immediately
1260 // after startup (for unknown reasons). In the Firefox shutdown case,
1261 // we may also fail to create a new async execution thread if one does not
1262 // already exist. (nsThreadManager will refuse to create new threads when
1263 // it has already been told to shutdown.) As such, we need to handle a
1264 // failure to create the async execution thread by falling back to
1265 // synchronous Close() and also dispatching the completion callback because
1266 // at least Places likes to spin a nested event loop that depends on the
1267 // callback being invoked.
1269 // Note that we have considered not trying to spin up the async execution
1270 // thread in this case if it does not already exist, but the overhead of
1271 // thread startup (if successful) is significantly less expensive than the
1272 // worst-case potential I/O hit of synchronously closing a database when we
1273 // could close it asynchronously.
1275 // - (!mDBConn && asyncThread): This happens in some but not all cases where
1276 // OpenAsyncDatabase encountered a problem opening the database. If it
1277 // happened in all cases AsyncInitDatabase would just shut down the thread
1278 // directly and we would avoid this case. But it doesn't, so for simplicity
1279 // and consistency AsyncCloseConnection knows how to handle this and we
1280 // act like this was the (mDBConn && asyncThread) case in this method.
1282 // - (!mDBConn && !asyncThread): The database was never successfully opened or
1283 // Close() or AsyncClose() has already been called (at least) once. This is
1284 // undeniably a misuse case by the caller. We could optimize for this
1285 // case by adding an additional check of mAsyncExecutionThread without using
1286 // getAsyncExecutionTarget() to avoid wastefully creating a thread just to
1287 // shut it down. But this complicates the method for broken caller code
1288 // whereas we're still correct and safe without the special-case.
1289 nsIEventTarget
*asyncThread
= getAsyncExecutionTarget();
1291 // Create our callback event if we were given a callback. This will
1292 // eventually be dispatched in all cases, even if we fall back to Close() and
1293 // the database wasn't open and we return an error. The rationale is that
1294 // no existing consumer checks our return value and several of them like to
1295 // spin nested event loops until the callback fires. Given that, it seems
1296 // preferable for us to dispatch the callback in all cases. (Except the
1297 // wrong thread misuse case we bailed on up above. But that's okay because
1298 // that is statically wrong whereas these edge cases are dynamic.)
1299 nsCOMPtr
<nsIRunnable
> completeEvent
;
1301 completeEvent
= newCompletionEvent(aCallback
);
1305 // We were unable to create an async thread, so we need to fall back to
1306 // using normal Close(). Since there is no async thread, Close() will
1307 // not complain about that. (Close() may, however, complain if the
1308 // connection is closed, but that's okay.)
1309 if (completeEvent
) {
1310 // Closing the database is more important than returning an error code
1311 // about a failure to dispatch, especially because all existing native
1312 // callers ignore our return value.
1313 Unused
<< NS_DispatchToMainThread(completeEvent
.forget());
1318 // setClosedState nullifies our connection pointer, so we take a raw pointer
1319 // off it, to pass it through the close procedure.
1320 sqlite3
*nativeConn
= mDBConn
;
1321 nsresult rv
= setClosedState();
1322 NS_ENSURE_SUCCESS(rv
, rv
);
1324 // Create and dispatch our close event to the background thread.
1325 nsCOMPtr
<nsIRunnable
> closeEvent
;
1327 // We need to lock because we're modifying mAsyncExecutionThread
1328 MutexAutoLock
lockedScope(sharedAsyncExecutionMutex
);
1329 closeEvent
= new AsyncCloseConnection(this,
1332 mAsyncExecutionThread
.forget());
1335 rv
= asyncThread
->Dispatch(closeEvent
, NS_DISPATCH_NORMAL
);
1336 NS_ENSURE_SUCCESS(rv
, rv
);
1342 Connection::AsyncClone(bool aReadOnly
,
1343 mozIStorageCompletionCallback
*aCallback
)
1345 PROFILER_LABEL("mozStorageConnection", "AsyncClone",
1346 js::ProfileEntry::Category::STORAGE
);
1348 if (!NS_IsMainThread()) {
1349 return NS_ERROR_NOT_SAME_THREAD
;
1352 return NS_ERROR_NOT_INITIALIZED
;
1354 return NS_ERROR_UNEXPECTED
;
1358 // Turn off SQLITE_OPEN_READWRITE, and set SQLITE_OPEN_READONLY.
1359 flags
= (~SQLITE_OPEN_READWRITE
& flags
) | SQLITE_OPEN_READONLY
;
1360 // Turn off SQLITE_OPEN_CREATE.
1361 flags
= (~SQLITE_OPEN_CREATE
& flags
);
1364 RefPtr
<Connection
> clone
= new Connection(mStorageService
, flags
,
1367 RefPtr
<AsyncInitializeClone
> initEvent
=
1368 new AsyncInitializeClone(this, clone
, aReadOnly
, aCallback
);
1369 // Dispatch to our async thread, since the originating connection must remain
1370 // valid and open for the whole cloning process. This also ensures we are
1371 // properly serialized with a `close` operation, rather than race with it.
1372 nsCOMPtr
<nsIEventTarget
> target
= getAsyncExecutionTarget();
1374 return NS_ERROR_UNEXPECTED
;
1376 return target
->Dispatch(initEvent
, NS_DISPATCH_NORMAL
);
1380 Connection::initializeClone(Connection
* aClone
, bool aReadOnly
)
1382 nsresult rv
= mFileURL
? aClone
->initialize(mFileURL
)
1383 : aClone
->initialize(mDatabaseFile
);
1384 if (NS_FAILED(rv
)) {
1388 // Re-attach on-disk databases that were attached to the original connection.
1390 nsCOMPtr
<mozIStorageStatement
> stmt
;
1391 rv
= CreateStatement(NS_LITERAL_CSTRING("PRAGMA database_list"),
1392 getter_AddRefs(stmt
));
1393 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1394 bool hasResult
= false;
1395 while (stmt
&& NS_SUCCEEDED(stmt
->ExecuteStep(&hasResult
)) && hasResult
) {
1397 rv
= stmt
->GetUTF8String(1, name
);
1398 if (NS_SUCCEEDED(rv
) && !name
.Equals(NS_LITERAL_CSTRING("main")) &&
1399 !name
.Equals(NS_LITERAL_CSTRING("temp"))) {
1401 rv
= stmt
->GetUTF8String(2, path
);
1402 if (NS_SUCCEEDED(rv
) && !path
.IsEmpty()) {
1403 rv
= aClone
->ExecuteSimpleSQL(NS_LITERAL_CSTRING("ATTACH DATABASE '") +
1404 path
+ NS_LITERAL_CSTRING("' AS ") + name
);
1405 MOZ_ASSERT(NS_SUCCEEDED(rv
), "couldn't re-attach database to cloned connection");
1411 // Copy over pragmas from the original connection.
1412 static const char * pragmas
[] = {
1416 "journal_size_limit",
1418 "wal_autocheckpoint",
1421 for (auto& pragma
: pragmas
) {
1422 // Read-only connections just need cache_size and temp_store pragmas.
1423 if (aReadOnly
&& ::strcmp(pragma
, "cache_size") != 0 &&
1424 ::strcmp(pragma
, "temp_store") != 0) {
1428 nsAutoCString
pragmaQuery("PRAGMA ");
1429 pragmaQuery
.Append(pragma
);
1430 nsCOMPtr
<mozIStorageStatement
> stmt
;
1431 rv
= CreateStatement(pragmaQuery
, getter_AddRefs(stmt
));
1432 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1433 bool hasResult
= false;
1434 if (stmt
&& NS_SUCCEEDED(stmt
->ExecuteStep(&hasResult
)) && hasResult
) {
1435 pragmaQuery
.AppendLiteral(" = ");
1436 pragmaQuery
.AppendInt(stmt
->AsInt32(0));
1437 rv
= aClone
->ExecuteSimpleSQL(pragmaQuery
);
1438 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1442 // Copy any functions that have been added to this connection.
1443 SQLiteMutexAutoLock
lockedScope(sharedDBMutex
);
1444 for (auto iter
= mFunctions
.Iter(); !iter
.Done(); iter
.Next()) {
1445 const nsACString
&key
= iter
.Key();
1446 Connection::FunctionInfo data
= iter
.UserData();
1448 MOZ_ASSERT(data
.type
== Connection::FunctionInfo::SIMPLE
||
1449 data
.type
== Connection::FunctionInfo::AGGREGATE
,
1450 "Invalid function type!");
1452 if (data
.type
== Connection::FunctionInfo::SIMPLE
) {
1453 mozIStorageFunction
*function
=
1454 static_cast<mozIStorageFunction
*>(data
.function
.get());
1455 rv
= aClone
->CreateFunction(key
, data
.numArgs
, function
);
1456 if (NS_FAILED(rv
)) {
1457 NS_WARNING("Failed to copy function to cloned connection");
1461 mozIStorageAggregateFunction
*function
=
1462 static_cast<mozIStorageAggregateFunction
*>(data
.function
.get());
1463 rv
= aClone
->CreateAggregateFunction(key
, data
.numArgs
, function
);
1464 if (NS_FAILED(rv
)) {
1465 NS_WARNING("Failed to copy aggregate function to cloned connection");
1474 Connection::Clone(bool aReadOnly
,
1475 mozIStorageConnection
**_connection
)
1477 MOZ_ASSERT(threadOpenedOn
== NS_GetCurrentThread());
1479 PROFILER_LABEL("mozStorageConnection", "Clone",
1480 js::ProfileEntry::Category::STORAGE
);
1483 return NS_ERROR_NOT_INITIALIZED
;
1485 return NS_ERROR_UNEXPECTED
;
1489 // Turn off SQLITE_OPEN_READWRITE, and set SQLITE_OPEN_READONLY.
1490 flags
= (~SQLITE_OPEN_READWRITE
& flags
) | SQLITE_OPEN_READONLY
;
1491 // Turn off SQLITE_OPEN_CREATE.
1492 flags
= (~SQLITE_OPEN_CREATE
& flags
);
1495 RefPtr
<Connection
> clone
= new Connection(mStorageService
, flags
,
1498 nsresult rv
= initializeClone(clone
, aReadOnly
);
1499 if (NS_FAILED(rv
)) {
1503 NS_IF_ADDREF(*_connection
= clone
);
1508 Connection::GetDefaultPageSize(int32_t *_defaultPageSize
)
1510 *_defaultPageSize
= Service::getDefaultPageSize();
1515 Connection::GetConnectionReady(bool *_ready
)
1517 *_ready
= connectionReady();
1522 Connection::GetDatabaseFile(nsIFile
**_dbFile
)
1524 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1526 NS_IF_ADDREF(*_dbFile
= mDatabaseFile
);
1532 Connection::GetLastInsertRowID(int64_t *_id
)
1534 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1536 sqlite_int64 id
= ::sqlite3_last_insert_rowid(mDBConn
);
1543 Connection::GetAffectedRows(int32_t *_rows
)
1545 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1547 *_rows
= ::sqlite3_changes(mDBConn
);
1553 Connection::GetLastError(int32_t *_error
)
1555 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1557 *_error
= ::sqlite3_errcode(mDBConn
);
1563 Connection::GetLastErrorString(nsACString
&_errorString
)
1565 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1567 const char *serr
= ::sqlite3_errmsg(mDBConn
);
1568 _errorString
.Assign(serr
);
1574 Connection::GetSchemaVersion(int32_t *_version
)
1576 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1578 nsCOMPtr
<mozIStorageStatement
> stmt
;
1579 (void)CreateStatement(NS_LITERAL_CSTRING("PRAGMA user_version"),
1580 getter_AddRefs(stmt
));
1581 NS_ENSURE_TRUE(stmt
, NS_ERROR_OUT_OF_MEMORY
);
1585 if (NS_SUCCEEDED(stmt
->ExecuteStep(&hasResult
)) && hasResult
)
1586 *_version
= stmt
->AsInt32(0);
1592 Connection::SetSchemaVersion(int32_t aVersion
)
1594 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1596 nsAutoCString
stmt(NS_LITERAL_CSTRING("PRAGMA user_version = "));
1597 stmt
.AppendInt(aVersion
);
1599 return ExecuteSimpleSQL(stmt
);
1603 Connection::CreateStatement(const nsACString
&aSQLStatement
,
1604 mozIStorageStatement
**_stmt
)
1606 NS_ENSURE_ARG_POINTER(_stmt
);
1607 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1609 RefPtr
<Statement
> statement(new Statement());
1610 NS_ENSURE_TRUE(statement
, NS_ERROR_OUT_OF_MEMORY
);
1612 nsresult rv
= statement
->initialize(this, mDBConn
, aSQLStatement
);
1613 NS_ENSURE_SUCCESS(rv
, rv
);
1616 statement
.forget(&rawPtr
);
1622 Connection::CreateAsyncStatement(const nsACString
&aSQLStatement
,
1623 mozIStorageAsyncStatement
**_stmt
)
1625 NS_ENSURE_ARG_POINTER(_stmt
);
1626 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1628 RefPtr
<AsyncStatement
> statement(new AsyncStatement());
1629 NS_ENSURE_TRUE(statement
, NS_ERROR_OUT_OF_MEMORY
);
1631 nsresult rv
= statement
->initialize(this, mDBConn
, aSQLStatement
);
1632 NS_ENSURE_SUCCESS(rv
, rv
);
1634 AsyncStatement
*rawPtr
;
1635 statement
.forget(&rawPtr
);
1641 Connection::ExecuteSimpleSQL(const nsACString
&aSQLStatement
)
1643 CHECK_MAINTHREAD_ABUSE();
1644 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1646 int srv
= executeSql(mDBConn
, PromiseFlatCString(aSQLStatement
).get());
1647 return convertResultCode(srv
);
1651 Connection::ExecuteAsync(mozIStorageBaseStatement
**aStatements
,
1652 uint32_t aNumStatements
,
1653 mozIStorageStatementCallback
*aCallback
,
1654 mozIStoragePendingStatement
**_handle
)
1656 nsTArray
<StatementData
> stmts(aNumStatements
);
1657 for (uint32_t i
= 0; i
< aNumStatements
; i
++) {
1658 nsCOMPtr
<StorageBaseStatementInternal
> stmt
=
1659 do_QueryInterface(aStatements
[i
]);
1661 // Obtain our StatementData.
1663 nsresult rv
= stmt
->getAsynchronousStatementData(data
);
1664 NS_ENSURE_SUCCESS(rv
, rv
);
1666 NS_ASSERTION(stmt
->getOwner() == this,
1667 "Statement must be from this database connection!");
1669 // Now append it to our array.
1670 NS_ENSURE_TRUE(stmts
.AppendElement(data
), NS_ERROR_OUT_OF_MEMORY
);
1673 // Dispatch to the background
1674 return AsyncExecuteStatements::execute(stmts
, this, mDBConn
, aCallback
,
1679 Connection::ExecuteSimpleSQLAsync(const nsACString
&aSQLStatement
,
1680 mozIStorageStatementCallback
*aCallback
,
1681 mozIStoragePendingStatement
**_handle
)
1683 if (!NS_IsMainThread()) {
1684 return NS_ERROR_NOT_SAME_THREAD
;
1687 nsCOMPtr
<mozIStorageAsyncStatement
> stmt
;
1688 nsresult rv
= CreateAsyncStatement(aSQLStatement
, getter_AddRefs(stmt
));
1689 if (NS_FAILED(rv
)) {
1693 nsCOMPtr
<mozIStoragePendingStatement
> pendingStatement
;
1694 rv
= stmt
->ExecuteAsync(aCallback
, getter_AddRefs(pendingStatement
));
1695 if (NS_FAILED(rv
)) {
1699 pendingStatement
.forget(_handle
);
1704 Connection::TableExists(const nsACString
&aTableName
,
1707 return databaseElementExists(TABLE
, aTableName
, _exists
);
1711 Connection::IndexExists(const nsACString
&aIndexName
,
1714 return databaseElementExists(INDEX
, aIndexName
, _exists
);
1718 Connection::GetTransactionInProgress(bool *_inProgress
)
1720 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1722 SQLiteMutexAutoLock
lockedScope(sharedDBMutex
);
1723 *_inProgress
= mTransactionInProgress
;
1728 Connection::BeginTransaction()
1730 return BeginTransactionAs(mozIStorageConnection::TRANSACTION_DEFERRED
);
1734 Connection::BeginTransactionAs(int32_t aTransactionType
)
1736 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1738 return beginTransactionInternal(mDBConn
, aTransactionType
);
1742 Connection::beginTransactionInternal(sqlite3
*aNativeConnection
,
1743 int32_t aTransactionType
)
1745 SQLiteMutexAutoLock
lockedScope(sharedDBMutex
);
1746 if (mTransactionInProgress
)
1747 return NS_ERROR_FAILURE
;
1749 switch(aTransactionType
) {
1750 case TRANSACTION_DEFERRED
:
1751 rv
= convertResultCode(executeSql(aNativeConnection
, "BEGIN DEFERRED"));
1753 case TRANSACTION_IMMEDIATE
:
1754 rv
= convertResultCode(executeSql(aNativeConnection
, "BEGIN IMMEDIATE"));
1756 case TRANSACTION_EXCLUSIVE
:
1757 rv
= convertResultCode(executeSql(aNativeConnection
, "BEGIN EXCLUSIVE"));
1760 return NS_ERROR_ILLEGAL_VALUE
;
1762 if (NS_SUCCEEDED(rv
))
1763 mTransactionInProgress
= true;
1768 Connection::CommitTransaction()
1771 return NS_ERROR_NOT_INITIALIZED
;
1773 return commitTransactionInternal(mDBConn
);
1777 Connection::commitTransactionInternal(sqlite3
*aNativeConnection
)
1779 SQLiteMutexAutoLock
lockedScope(sharedDBMutex
);
1780 if (!mTransactionInProgress
)
1781 return NS_ERROR_UNEXPECTED
;
1783 convertResultCode(executeSql(aNativeConnection
, "COMMIT TRANSACTION"));
1784 if (NS_SUCCEEDED(rv
))
1785 mTransactionInProgress
= false;
1790 Connection::RollbackTransaction()
1793 return NS_ERROR_NOT_INITIALIZED
;
1795 return rollbackTransactionInternal(mDBConn
);
1799 Connection::rollbackTransactionInternal(sqlite3
*aNativeConnection
)
1801 SQLiteMutexAutoLock
lockedScope(sharedDBMutex
);
1802 if (!mTransactionInProgress
)
1803 return NS_ERROR_UNEXPECTED
;
1806 convertResultCode(executeSql(aNativeConnection
, "ROLLBACK TRANSACTION"));
1807 if (NS_SUCCEEDED(rv
))
1808 mTransactionInProgress
= false;
1813 Connection::CreateTable(const char *aTableName
,
1814 const char *aTableSchema
)
1816 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1818 char *buf
= ::PR_smprintf("CREATE TABLE %s (%s)", aTableName
, aTableSchema
);
1820 return NS_ERROR_OUT_OF_MEMORY
;
1822 int srv
= executeSql(mDBConn
, buf
);
1823 ::PR_smprintf_free(buf
);
1825 return convertResultCode(srv
);
1829 Connection::CreateFunction(const nsACString
&aFunctionName
,
1830 int32_t aNumArguments
,
1831 mozIStorageFunction
*aFunction
)
1833 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1835 // Check to see if this function is already defined. We only check the name
1836 // because a function can be defined with the same body but different names.
1837 SQLiteMutexAutoLock
lockedScope(sharedDBMutex
);
1838 NS_ENSURE_FALSE(mFunctions
.Get(aFunctionName
, nullptr), NS_ERROR_FAILURE
);
1840 int srv
= ::sqlite3_create_function(mDBConn
,
1841 nsPromiseFlatCString(aFunctionName
).get(),
1845 basicFunctionHelper
,
1848 if (srv
!= SQLITE_OK
)
1849 return convertResultCode(srv
);
1851 FunctionInfo info
= { aFunction
,
1852 Connection::FunctionInfo::SIMPLE
,
1854 mFunctions
.Put(aFunctionName
, info
);
1860 Connection::CreateAggregateFunction(const nsACString
&aFunctionName
,
1861 int32_t aNumArguments
,
1862 mozIStorageAggregateFunction
*aFunction
)
1864 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1866 // Check to see if this function name is already defined.
1867 SQLiteMutexAutoLock
lockedScope(sharedDBMutex
);
1868 NS_ENSURE_FALSE(mFunctions
.Get(aFunctionName
, nullptr), NS_ERROR_FAILURE
);
1870 // Because aggregate functions depend on state across calls, you cannot have
1871 // the same instance use the same name. We want to enumerate all functions
1872 // and make sure this instance is not already registered.
1873 NS_ENSURE_FALSE(findFunctionByInstance(aFunction
), NS_ERROR_FAILURE
);
1875 int srv
= ::sqlite3_create_function(mDBConn
,
1876 nsPromiseFlatCString(aFunctionName
).get(),
1881 aggregateFunctionStepHelper
,
1882 aggregateFunctionFinalHelper
);
1883 if (srv
!= SQLITE_OK
)
1884 return convertResultCode(srv
);
1886 FunctionInfo info
= { aFunction
,
1887 Connection::FunctionInfo::AGGREGATE
,
1889 mFunctions
.Put(aFunctionName
, info
);
1895 Connection::RemoveFunction(const nsACString
&aFunctionName
)
1897 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1899 SQLiteMutexAutoLock
lockedScope(sharedDBMutex
);
1900 NS_ENSURE_TRUE(mFunctions
.Get(aFunctionName
, nullptr), NS_ERROR_FAILURE
);
1902 int srv
= ::sqlite3_create_function(mDBConn
,
1903 nsPromiseFlatCString(aFunctionName
).get(),
1910 if (srv
!= SQLITE_OK
)
1911 return convertResultCode(srv
);
1913 mFunctions
.Remove(aFunctionName
);
1919 Connection::SetProgressHandler(int32_t aGranularity
,
1920 mozIStorageProgressHandler
*aHandler
,
1921 mozIStorageProgressHandler
**_oldHandler
)
1923 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1925 // Return previous one
1926 SQLiteMutexAutoLock
lockedScope(sharedDBMutex
);
1927 NS_IF_ADDREF(*_oldHandler
= mProgressHandler
);
1929 if (!aHandler
|| aGranularity
<= 0) {
1933 mProgressHandler
= aHandler
;
1934 ::sqlite3_progress_handler(mDBConn
, aGranularity
, sProgressHelper
, this);
1940 Connection::RemoveProgressHandler(mozIStorageProgressHandler
**_oldHandler
)
1942 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1944 // Return previous one
1945 SQLiteMutexAutoLock
lockedScope(sharedDBMutex
);
1946 NS_IF_ADDREF(*_oldHandler
= mProgressHandler
);
1948 mProgressHandler
= nullptr;
1949 ::sqlite3_progress_handler(mDBConn
, 0, nullptr, nullptr);
1955 Connection::SetGrowthIncrement(int32_t aChunkSize
, const nsACString
&aDatabaseName
)
1957 // Bug 597215: Disk space is extremely limited on Android
1958 // so don't preallocate space. This is also not effective
1959 // on log structured file systems used by Android devices
1960 #if !defined(ANDROID) && !defined(MOZ_PLATFORM_MAEMO)
1961 // Don't preallocate if less than 500MiB is available.
1962 int64_t bytesAvailable
;
1963 nsresult rv
= mDatabaseFile
->GetDiskSpaceAvailable(&bytesAvailable
);
1964 NS_ENSURE_SUCCESS(rv
, rv
);
1965 if (bytesAvailable
< MIN_AVAILABLE_BYTES_PER_CHUNKED_GROWTH
) {
1966 return NS_ERROR_FILE_TOO_BIG
;
1969 (void)::sqlite3_file_control(mDBConn
,
1970 aDatabaseName
.Length() ? nsPromiseFlatCString(aDatabaseName
).get()
1972 SQLITE_FCNTL_CHUNK_SIZE
,
1979 Connection::EnableModule(const nsACString
& aModuleName
)
1981 if (!mDBConn
) return NS_ERROR_NOT_INITIALIZED
;
1983 for (auto& gModule
: gModules
) {
1984 struct Module
* m
= &gModule
;
1985 if (aModuleName
.Equals(m
->name
)) {
1986 int srv
= m
->registerFunc(mDBConn
, m
->name
);
1987 if (srv
!= SQLITE_OK
)
1988 return convertResultCode(srv
);
1994 return NS_ERROR_FAILURE
;
1997 // Implemented in TelemetryVFS.cpp
1998 already_AddRefed
<QuotaObject
>
1999 GetQuotaObjectForFile(sqlite3_file
*pFile
);
2002 Connection::GetQuotaObjects(QuotaObject
** aDatabaseQuotaObject
,
2003 QuotaObject
** aJournalQuotaObject
)
2005 MOZ_ASSERT(aDatabaseQuotaObject
);
2006 MOZ_ASSERT(aJournalQuotaObject
);
2009 return NS_ERROR_NOT_INITIALIZED
;
2013 int srv
= ::sqlite3_file_control(mDBConn
,
2015 SQLITE_FCNTL_FILE_POINTER
,
2017 if (srv
!= SQLITE_OK
) {
2018 return convertResultCode(srv
);
2021 RefPtr
<QuotaObject
> databaseQuotaObject
= GetQuotaObjectForFile(file
);
2023 srv
= ::sqlite3_file_control(mDBConn
,
2025 SQLITE_FCNTL_JOURNAL_POINTER
,
2027 if (srv
!= SQLITE_OK
) {
2028 return convertResultCode(srv
);
2031 RefPtr
<QuotaObject
> journalQuotaObject
= GetQuotaObjectForFile(file
);
2033 databaseQuotaObject
.forget(aDatabaseQuotaObject
);
2034 journalQuotaObject
.forget(aJournalQuotaObject
);
2038 } // namespace storage
2039 } // namespace mozilla