Bug 1324062. Part 1 - Add a "Report Site Issue" button to the panel menu for NIGHTLY_...
[gecko.git] / storage / mozStorageConnection.cpp
blob6129ac4442a17cc345a1fa3b91a5a080e2cc69d7
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include <stdio.h>
9 #include "nsError.h"
10 #include "nsIMutableArray.h"
11 #include "nsAutoPtr.h"
12 #include "nsIMemoryReporter.h"
13 #include "nsThreadUtils.h"
14 #include "nsIFile.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"
44 #include "prprf.h"
45 #include "nsProxyRelease.h"
46 #include <algorithm>
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.
57 #ifdef DEBUG
58 #define CHECK_MAINTHREAD_ABUSE() \
59 do { \
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."); \
65 } while(0)
66 #else
67 #define CHECK_MAINTHREAD_ABUSE() do { /* Nothing */ } while(0)
68 #endif
70 namespace mozilla {
71 namespace storage {
73 using mozilla::dom::quota::QuotaObject;
75 namespace {
77 int
78 nsresultToSQLiteResult(nsresult aXPCOMResultCode)
80 if (NS_SUCCEEDED(aXPCOMResultCode)) {
81 return SQLITE_OK;
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:
90 return SQLITE_BUSY;
91 case NS_ERROR_FILE_IS_LOCKED:
92 return SQLITE_LOCKED;
93 case NS_ERROR_FILE_READ_ONLY:
94 return SQLITE_READONLY;
95 case NS_ERROR_STORAGE_IOERR:
96 return SQLITE_IOERR;
97 case NS_ERROR_FILE_NO_DEVICE_SPACE:
98 return SQLITE_FULL;
99 case NS_ERROR_OUT_OF_MEMORY:
100 return SQLITE_NOMEM;
101 case NS_ERROR_UNEXPECTED:
102 return SQLITE_MISUSE;
103 case NS_ERROR_ABORT:
104 return SQLITE_ABORT;
105 case NS_ERROR_STORAGE_CONSTRAINT:
106 return SQLITE_CONSTRAINT;
107 default:
108 return SQLITE_ERROR;
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,
119 int aValue)
121 ::sqlite3_result_int(aCtx, aValue);
122 return SQLITE_OK;
126 sqlite3_T_int64(sqlite3_context *aCtx,
127 sqlite3_int64 aValue)
129 ::sqlite3_result_int64(aCtx, aValue);
130 return SQLITE_OK;
134 sqlite3_T_double(sqlite3_context *aCtx,
135 double aValue)
137 ::sqlite3_result_double(aCtx, aValue);
138 return SQLITE_OK;
142 sqlite3_T_text(sqlite3_context *aCtx,
143 const nsCString &aValue)
145 ::sqlite3_result_text(aCtx,
146 aValue.get(),
147 aValue.Length(),
148 SQLITE_TRANSIENT);
149 return SQLITE_OK;
153 sqlite3_T_text16(sqlite3_context *aCtx,
154 const nsString &aValue)
156 ::sqlite3_result_text16(aCtx,
157 aValue.get(),
158 aValue.Length() * 2, // Number of bytes.
159 SQLITE_TRANSIENT);
160 return SQLITE_OK;
164 sqlite3_T_null(sqlite3_context *aCtx)
166 ::sqlite3_result_null(aCtx);
167 return SQLITE_OK;
171 sqlite3_T_blob(sqlite3_context *aCtx,
172 const void *aData,
173 int aSize)
175 ::sqlite3_result_blob(aCtx, aData, aSize, free);
176 return SQLITE_OK;
179 #include "variantToSQLiteT_impl.h"
181 ////////////////////////////////////////////////////////////////////////////////
182 //// Modules
184 struct Module
186 const char* name;
187 int (*registerFunc)(sqlite3*, const char*);
190 Module gModules[] = {
191 { "filesystem", RegisterFileSystemModule }
194 ////////////////////////////////////////////////////////////////////////////////
195 //// Local Functions
197 int tracefunc (unsigned aReason, void *aClosure, void *aP, void *aX)
199 switch (aReason) {
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));
210 } else {
211 char* sql = ::sqlite3_expanded_sql(stmt);
212 MOZ_LOG(gStorageLog, LogLevel::Debug,
213 ("TRACE_STMT on %p: '%s'", aClosure, sql));
214 ::sqlite3_free(sql);
216 break;
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;
222 if (time > 0) {
223 MOZ_LOG(gStorageLog, LogLevel::Debug,
224 ("TRACE_TIME on %p: %dms", aClosure, time));
226 break;
229 return 0;
232 void
233 basicFunctionHelper(sqlite3_context *aCtx,
234 int aArgc,
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));
242 if (!arguments)
243 return;
245 nsCOMPtr<nsIVariant> result;
246 nsresult rv = func->OnFunctionCall(arguments, getter_AddRefs(result));
247 if (NS_FAILED(rv)) {
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));
257 return;
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",
266 -1);
270 void
271 aggregateFunctionStepHelper(sqlite3_context *aCtx,
272 int aArgc,
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));
280 if (!arguments)
281 return;
283 if (NS_FAILED(func->OnStep(arguments)))
284 NS_WARNING("User aggregate step function returned error code!");
287 void
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",
299 -1);
300 return;
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",
307 -1);
312 * This code is heavily based on the sample at:
313 * http://www.sqlite.org/unlock_notify.html
315 class UnlockNotification
317 public:
318 UnlockNotification()
319 : mMutex("UnlockNotification mMutex")
320 , mCondVar(mMutex, "UnlockNotification condVar")
321 , mSignaled(false)
325 void Wait()
327 MutexAutoLock lock(mMutex);
328 while (!mSignaled) {
329 (void)mCondVar.Wait();
333 void Signal()
335 MutexAutoLock lock(mMutex);
336 mSignaled = true;
337 (void)mCondVar.Notify();
340 private:
341 Mutex mMutex;
342 CondVar mCondVar;
343 bool mSignaled;
346 void
347 UnlockNotifyCallback(void **aArgs,
348 int aArgsSize)
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,
362 &notification);
363 MOZ_ASSERT(srv == SQLITE_LOCKED || srv == SQLITE_OK);
364 if (srv == SQLITE_OK) {
365 notification.Wait();
368 return srv;
371 } // namespace
373 ////////////////////////////////////////////////////////////////////////////////
374 //// Local Classes
376 namespace {
378 class AsyncCloseConnection final: public Runnable
380 public:
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
394 #ifdef DEBUG
395 // This code is executed on the background thread
396 bool onAsyncThread = false;
397 (void)mAsyncExecutionThread->IsOnCurrentThread(&onAsyncThread);
398 MOZ_ASSERT(onAsyncThread);
399 #endif // DEBUG
401 nsCOMPtr<nsIRunnable> event = NewRunnableMethod<nsCOMPtr<nsIThread>>
402 (mConnection, &Connection::shutdownAsyncThread, mAsyncExecutionThread);
403 (void)NS_DispatchToMainThread(event);
405 // Internal close.
406 (void)mConnection->internalClose(mNativeConnection);
408 // Callback
409 if (mCallbackEvent) {
410 nsCOMPtr<nsIThread> thread;
411 (void)NS_GetMainThread(getter_AddRefs(thread));
412 (void)thread->Dispatch(mCallbackEvent, NS_DISPATCH_NORMAL);
415 return NS_OK;
418 ~AsyncCloseConnection() override {
419 NS_ReleaseOnMainThread(mConnection.forget());
420 NS_ReleaseOnMainThread(mCallbackEvent.forget());
422 private:
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
436 public:
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,
446 Connection* aClone,
447 const bool aReadOnly,
448 mozIStorageCompletionCallback* aCallback)
449 : mConnection(aConnection)
450 , mClone(aClone)
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);
461 if (NS_FAILED(rv)) {
462 return Dispatch(rv, nullptr);
464 return Dispatch(NS_OK,
465 NS_ISUPPORTS_CAST(mozIStorageAsyncConnection*, mClone));
468 private:
469 nsresult Dispatch(nsresult aResult, nsISupports* aValue) {
470 RefPtr<CallbackComplete> event = new CallbackComplete(aResult,
471 aValue,
472 mCallback.forget());
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;
497 } // namespace
499 ////////////////////////////////////////////////////////////////////////////////
500 //// Connection
502 Connection::Connection(Service *aService,
503 int aFlags,
504 bool aAsyncOnly,
505 bool aIgnoreLockingMode)
506 : sharedAsyncExecutionMutex("Connection::sharedAsyncExecutionMutex")
507 , sharedDBMutex("Connection::sharedDBMutex")
508 , threadOpenedOn(do_GetCurrentThread())
509 , mDBConn(nullptr)
510 , mAsyncExecutionThreadShuttingDown(false)
511 #ifdef DEBUG
512 , mAsyncExecutionThreadIsAlive(false)
513 #endif
514 , mConnectionClosed(false)
515 , mTransactionInProgress(false)
516 , mProgressHandler(nullptr)
517 , mFlags(aFlags)
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()
529 (void)Close();
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)
544 NS_INTERFACE_MAP_END
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");
553 if (1 == count) {
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);
562 #endif
563 delete (this);
564 return 0;
566 return count;
569 int32_t
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)));
576 if (aMaxValue)
577 *aMaxValue = max;
578 return curr;
581 nsIEventTarget *
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)
589 return nullptr;
591 if (!mAsyncExecutionThread) {
592 static nsThreadPoolNaming naming;
593 nsresult rv = NS_NewNamedThread(naming.GetNextThreadName("mozStorage"),
594 getter_AddRefs(mAsyncExecutionThread));
595 if (NS_FAILED(rv)) {
596 NS_WARNING("Failed to create async thread.");
597 return nullptr;
601 #ifdef DEBUG
602 mAsyncExecutionThreadIsAlive = true;
603 #endif
605 return mAsyncExecutionThread;
608 nsresult
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) {
619 mDBConn = nullptr;
620 return convertResultCode(srv);
623 // Do not set mDatabaseFile or mFileURL here since this is a "memory"
624 // database.
626 nsresult rv = initializeInternal();
627 NS_ENSURE_SUCCESS(rv, rv);
629 return NS_OK;
632 nsresult
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;
642 nsAutoString path;
643 nsresult rv = aDatabaseFile->GetPath(path);
644 NS_ENSURE_SUCCESS(rv, rv);
646 #ifdef XP_WIN
647 static const char* sIgnoreLockingVFS = "win32-none";
648 #else
649 static const char* sIgnoreLockingVFS = "unix-none";
650 #endif
651 const char* vfs = mIgnoreLockingMode ? sIgnoreLockingVFS : nullptr;
653 int srv = ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path).get(), &mDBConn,
654 mFlags, vfs);
655 if (srv != SQLITE_OK) {
656 mDBConn = nullptr;
657 return convertResultCode(srv);
660 // Do not set mFileURL here since this is database does not have an associated
661 // URL.
662 mDatabaseFile = aDatabaseFile;
664 rv = initializeInternal();
665 NS_ENSURE_SUCCESS(rv, rv);
667 return NS_OK;
670 nsresult
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);
682 nsAutoCString spec;
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) {
688 mDBConn = nullptr;
689 return convertResultCode(srv);
692 // Set both mDatabaseFile and mFileURL here.
693 mFileURL = aFileURL;
694 mDatabaseFile = databaseFile;
696 rv = initializeInternal();
697 NS_ENSURE_SUCCESS(rv, rv);
699 return NS_OK;
702 nsresult
703 Connection::initializeInternal()
705 MOZ_ASSERT(mDBConn);
707 if (mFileURL) {
708 const char* dbPath = ::sqlite3_db_filename(mDBConn, "main");
709 MOZ_ASSERT(dbPath);
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,
734 tracefunc, this);
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);
760 mDBConn = nullptr;
761 return convertResultCode(srv);
764 #if defined(MOZ_MEMORY_TEMP_STORE_PRAGMA)
765 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA temp_store = 2;"));
766 #endif
768 // Register our built-in SQL functions.
769 srv = registerFunctions(mDBConn);
770 if (srv != SQLITE_OK) {
771 ::sqlite3_close(mDBConn);
772 mDBConn = nullptr;
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);
780 mDBConn = nullptr;
781 return convertResultCode(srv);
784 // Set the synchronous PRAGMA, according to the preference.
785 switch (Service::getSynchronousPref()) {
786 case 2:
787 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
788 "PRAGMA synchronous = FULL;"));
789 break;
790 case 0:
791 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
792 "PRAGMA synchronous = OFF;"));
793 break;
794 case 1:
795 default:
796 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
797 "PRAGMA synchronous = NORMAL;"));
798 break;
801 return NS_OK;
804 nsresult
805 Connection::databaseElementExists(enum DatabaseElementType aElementType,
806 const nsACString &aElementName,
807 bool *_exists)
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);
819 else {
820 nsDependentCSubstring db(Substring(aElementName, 0, ind + 1));
821 element.Assign(Substring(aElementName, ind + 1, aElementName.Length()));
822 query.Append(db);
824 query.AppendLiteral("sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type = '");
826 switch (aElementType) {
827 case INDEX:
828 query.AppendLiteral("index");
829 break;
830 case TABLE:
831 query.AppendLiteral("table");
832 break;
834 query.AppendLiteral("' AND name ='");
835 query.Append(element);
836 query.Append('\'');
838 sqlite3_stmt *stmt;
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) {
848 *_exists = true;
849 return NS_OK;
851 if (srv == SQLITE_DONE) {
852 *_exists = false;
853 return NS_OK;
856 return convertResultCode(srv);
859 bool
860 Connection::findFunctionByInstance(nsISupports *aInstance)
862 sharedDBMutex.assertCurrentThreadOwns();
864 for (auto iter = mFunctions.Iter(); !iter.Done(); iter.Next()) {
865 if (iter.UserData().function == aInstance) {
866 return true;
869 return false;
872 /* static */ int
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) {
884 bool result;
885 nsresult rv = mProgressHandler->OnProgress(this, &result);
886 if (NS_FAILED(rv)) return 0; // Don't break request
887 return result ? 1 : 0;
889 return 0;
892 nsresult
893 Connection::setClosedState()
895 // Ensure that we are on the correct thread to close the database.
896 bool onOpenedThread;
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.
914 mDBConn = nullptr;
916 return NS_OK;
919 bool
920 Connection::connectionReady()
922 return mDBConn != nullptr;
925 bool
926 Connection::isClosing()
928 bool shuttingDown = false;
930 MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
931 shuttingDown = mAsyncExecutionThreadShuttingDown;
933 return shuttingDown && !isClosed();
936 bool
937 Connection::isClosed()
939 MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
940 return mConnectionClosed;
943 void
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));
951 #ifdef DEBUG
952 mAsyncExecutionThreadIsAlive = false;
953 #endif
956 nsresult
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());
964 #ifdef DEBUG
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!");
970 #endif // DEBUG
972 if (MOZ_LOG_TEST(gStorageLog, LogLevel::Debug)) {
973 nsAutoCString leafName(":memory");
974 if (mDatabaseFile)
975 (void)mDatabaseFile->GetNativeLeafName(leafName);
976 MOZ_LOG(gStorageLog, LogLevel::Debug, ("Closing connection to '%s'",
977 leafName.get()));
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)
993 return NS_OK;
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),
1005 stmt));
1007 #ifdef DEBUG
1008 char *msg = ::PR_smprintf("SQL statement '%s' (%x) should have been finalized before closing the connection",
1009 ::sqlite3_sql(stmt),
1010 stmt);
1011 NS_WARNING(msg);
1012 ::PR_smprintf_free(msg);
1013 msg = nullptr;
1014 #endif // DEBUG
1016 srv = ::sqlite3_finalize(stmt);
1018 #ifdef DEBUG
1019 if (srv != SQLITE_OK) {
1020 msg = ::PR_smprintf("Could not finalize SQL statement '%s' (%x)",
1021 ::sqlite3_sql(stmt),
1022 stmt);
1023 NS_WARNING(msg);
1024 ::PR_smprintf_free(msg);
1025 msg = nullptr;
1027 #endif // DEBUG
1029 // Ensure that the loop continues properly, whether closing has succeeded
1030 // or not.
1031 if (srv == SQLITE_OK) {
1032 stmt = nullptr;
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);
1050 nsCString
1051 Connection::getFilename()
1053 nsCString leafname(":memory:");
1054 if (mDatabaseFile) {
1055 (void)mDatabaseFile->GetNativeLeafName(leafname);
1057 return 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.
1072 if (isClosed())
1073 return SQLITE_MISUSE;
1075 (void)::sqlite3_extended_result_codes(aNativeConnection, 1);
1077 int srv;
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!");
1083 break;
1087 srv = WaitForUnlockNotify(aNativeConnection);
1088 if (srv != SQLITE_OK) {
1089 break;
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.
1108 return srv & 0xFF;
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
1116 // been closed.
1117 if (isClosed())
1118 return SQLITE_MISUSE;
1120 bool checkedMainThread = false;
1122 (void)::sqlite3_extended_result_codes(aNativeConnection, 1);
1124 int srv;
1125 while((srv = ::sqlite3_prepare_v2(aNativeConnection,
1126 aSQL.get(),
1128 _stmt,
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!");
1134 break;
1138 srv = WaitForUnlockNotify(aNativeConnection);
1139 if (srv != SQLITE_OK) {
1140 break;
1144 if (srv != SQLITE_OK) {
1145 nsCString warnMsg;
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));
1151 #ifdef DEBUG
1152 NS_WARNING(warnMsg.get());
1153 #endif
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;
1167 return rc;
1172 Connection::executeSql(sqlite3 *aNativeConnection, const char *aSqlString)
1174 if (isClosed())
1175 return SQLITE_MISUSE;
1177 TimeStamp startTime = TimeStamp::Now();
1178 int srv = ::sqlite3_exec(aNativeConnection, aSqlString, nullptr, nullptr,
1179 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());
1192 return srv;
1195 ////////////////////////////////////////////////////////////////////////////////
1196 //// nsIInterfaceRequestor
1198 NS_IMETHODIMP
1199 Connection::GetInterface(const nsIID &aIID,
1200 void **_result)
1202 if (aIID.Equals(NS_GET_IID(nsIEventTarget))) {
1203 nsIEventTarget *background = getAsyncExecutionTarget();
1204 NS_IF_ADDREF(background);
1205 *_result = background;
1206 return NS_OK;
1208 return NS_ERROR_NO_INTERFACE;
1211 ////////////////////////////////////////////////////////////////////////////////
1212 //// mozIStorageConnection
1214 NS_IMETHODIMP
1215 Connection::Close()
1217 if (!mDBConn)
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);
1238 NS_IMETHODIMP
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
1253 // async thread.
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;
1300 if (aCallback) {
1301 completeEvent = newCompletionEvent(aCallback);
1304 if (!asyncThread) {
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());
1315 return Close();
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,
1330 nativeConn,
1331 completeEvent,
1332 mAsyncExecutionThread.forget());
1335 rv = asyncThread->Dispatch(closeEvent, NS_DISPATCH_NORMAL);
1336 NS_ENSURE_SUCCESS(rv, rv);
1338 return NS_OK;
1341 NS_IMETHODIMP
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;
1351 if (!mDBConn)
1352 return NS_ERROR_NOT_INITIALIZED;
1353 if (!mDatabaseFile)
1354 return NS_ERROR_UNEXPECTED;
1356 int flags = mFlags;
1357 if (aReadOnly) {
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,
1365 mAsyncOnly);
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();
1373 if (!target) {
1374 return NS_ERROR_UNEXPECTED;
1376 return target->Dispatch(initEvent, NS_DISPATCH_NORMAL);
1379 nsresult
1380 Connection::initializeClone(Connection* aClone, bool aReadOnly)
1382 nsresult rv = mFileURL ? aClone->initialize(mFileURL)
1383 : aClone->initialize(mDatabaseFile);
1384 if (NS_FAILED(rv)) {
1385 return 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) {
1396 nsAutoCString name;
1397 rv = stmt->GetUTF8String(1, name);
1398 if (NS_SUCCEEDED(rv) && !name.Equals(NS_LITERAL_CSTRING("main")) &&
1399 !name.Equals(NS_LITERAL_CSTRING("temp"))) {
1400 nsCString path;
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[] = {
1413 "cache_size",
1414 "temp_store",
1415 "foreign_keys",
1416 "journal_size_limit",
1417 "synchronous",
1418 "wal_autocheckpoint",
1419 "busy_timeout"
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) {
1425 continue;
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");
1460 } else {
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");
1470 return NS_OK;
1473 NS_IMETHODIMP
1474 Connection::Clone(bool aReadOnly,
1475 mozIStorageConnection **_connection)
1477 MOZ_ASSERT(threadOpenedOn == NS_GetCurrentThread());
1479 PROFILER_LABEL("mozStorageConnection", "Clone",
1480 js::ProfileEntry::Category::STORAGE);
1482 if (!mDBConn)
1483 return NS_ERROR_NOT_INITIALIZED;
1484 if (!mDatabaseFile)
1485 return NS_ERROR_UNEXPECTED;
1487 int flags = mFlags;
1488 if (aReadOnly) {
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,
1496 mAsyncOnly);
1498 nsresult rv = initializeClone(clone, aReadOnly);
1499 if (NS_FAILED(rv)) {
1500 return rv;
1503 NS_IF_ADDREF(*_connection = clone);
1504 return NS_OK;
1507 NS_IMETHODIMP
1508 Connection::GetDefaultPageSize(int32_t *_defaultPageSize)
1510 *_defaultPageSize = Service::getDefaultPageSize();
1511 return NS_OK;
1514 NS_IMETHODIMP
1515 Connection::GetConnectionReady(bool *_ready)
1517 *_ready = connectionReady();
1518 return NS_OK;
1521 NS_IMETHODIMP
1522 Connection::GetDatabaseFile(nsIFile **_dbFile)
1524 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1526 NS_IF_ADDREF(*_dbFile = mDatabaseFile);
1528 return NS_OK;
1531 NS_IMETHODIMP
1532 Connection::GetLastInsertRowID(int64_t *_id)
1534 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1536 sqlite_int64 id = ::sqlite3_last_insert_rowid(mDBConn);
1537 *_id = id;
1539 return NS_OK;
1542 NS_IMETHODIMP
1543 Connection::GetAffectedRows(int32_t *_rows)
1545 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1547 *_rows = ::sqlite3_changes(mDBConn);
1549 return NS_OK;
1552 NS_IMETHODIMP
1553 Connection::GetLastError(int32_t *_error)
1555 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1557 *_error = ::sqlite3_errcode(mDBConn);
1559 return NS_OK;
1562 NS_IMETHODIMP
1563 Connection::GetLastErrorString(nsACString &_errorString)
1565 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1567 const char *serr = ::sqlite3_errmsg(mDBConn);
1568 _errorString.Assign(serr);
1570 return NS_OK;
1573 NS_IMETHODIMP
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);
1583 *_version = 0;
1584 bool hasResult;
1585 if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult)
1586 *_version = stmt->AsInt32(0);
1588 return NS_OK;
1591 NS_IMETHODIMP
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);
1602 NS_IMETHODIMP
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);
1615 Statement *rawPtr;
1616 statement.forget(&rawPtr);
1617 *_stmt = rawPtr;
1618 return NS_OK;
1621 NS_IMETHODIMP
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);
1636 *_stmt = rawPtr;
1637 return NS_OK;
1640 NS_IMETHODIMP
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);
1650 NS_IMETHODIMP
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.
1662 StatementData data;
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,
1675 _handle);
1678 NS_IMETHODIMP
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)) {
1690 return rv;
1693 nsCOMPtr<mozIStoragePendingStatement> pendingStatement;
1694 rv = stmt->ExecuteAsync(aCallback, getter_AddRefs(pendingStatement));
1695 if (NS_FAILED(rv)) {
1696 return rv;
1699 pendingStatement.forget(_handle);
1700 return rv;
1703 NS_IMETHODIMP
1704 Connection::TableExists(const nsACString &aTableName,
1705 bool *_exists)
1707 return databaseElementExists(TABLE, aTableName, _exists);
1710 NS_IMETHODIMP
1711 Connection::IndexExists(const nsACString &aIndexName,
1712 bool* _exists)
1714 return databaseElementExists(INDEX, aIndexName, _exists);
1717 NS_IMETHODIMP
1718 Connection::GetTransactionInProgress(bool *_inProgress)
1720 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1722 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1723 *_inProgress = mTransactionInProgress;
1724 return NS_OK;
1727 NS_IMETHODIMP
1728 Connection::BeginTransaction()
1730 return BeginTransactionAs(mozIStorageConnection::TRANSACTION_DEFERRED);
1733 NS_IMETHODIMP
1734 Connection::BeginTransactionAs(int32_t aTransactionType)
1736 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1738 return beginTransactionInternal(mDBConn, aTransactionType);
1741 nsresult
1742 Connection::beginTransactionInternal(sqlite3 *aNativeConnection,
1743 int32_t aTransactionType)
1745 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1746 if (mTransactionInProgress)
1747 return NS_ERROR_FAILURE;
1748 nsresult rv;
1749 switch(aTransactionType) {
1750 case TRANSACTION_DEFERRED:
1751 rv = convertResultCode(executeSql(aNativeConnection, "BEGIN DEFERRED"));
1752 break;
1753 case TRANSACTION_IMMEDIATE:
1754 rv = convertResultCode(executeSql(aNativeConnection, "BEGIN IMMEDIATE"));
1755 break;
1756 case TRANSACTION_EXCLUSIVE:
1757 rv = convertResultCode(executeSql(aNativeConnection, "BEGIN EXCLUSIVE"));
1758 break;
1759 default:
1760 return NS_ERROR_ILLEGAL_VALUE;
1762 if (NS_SUCCEEDED(rv))
1763 mTransactionInProgress = true;
1764 return rv;
1767 NS_IMETHODIMP
1768 Connection::CommitTransaction()
1770 if (!mDBConn)
1771 return NS_ERROR_NOT_INITIALIZED;
1773 return commitTransactionInternal(mDBConn);
1776 nsresult
1777 Connection::commitTransactionInternal(sqlite3 *aNativeConnection)
1779 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1780 if (!mTransactionInProgress)
1781 return NS_ERROR_UNEXPECTED;
1782 nsresult rv =
1783 convertResultCode(executeSql(aNativeConnection, "COMMIT TRANSACTION"));
1784 if (NS_SUCCEEDED(rv))
1785 mTransactionInProgress = false;
1786 return rv;
1789 NS_IMETHODIMP
1790 Connection::RollbackTransaction()
1792 if (!mDBConn)
1793 return NS_ERROR_NOT_INITIALIZED;
1795 return rollbackTransactionInternal(mDBConn);
1798 nsresult
1799 Connection::rollbackTransactionInternal(sqlite3 *aNativeConnection)
1801 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1802 if (!mTransactionInProgress)
1803 return NS_ERROR_UNEXPECTED;
1805 nsresult rv =
1806 convertResultCode(executeSql(aNativeConnection, "ROLLBACK TRANSACTION"));
1807 if (NS_SUCCEEDED(rv))
1808 mTransactionInProgress = false;
1809 return rv;
1812 NS_IMETHODIMP
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);
1819 if (!buf)
1820 return NS_ERROR_OUT_OF_MEMORY;
1822 int srv = executeSql(mDBConn, buf);
1823 ::PR_smprintf_free(buf);
1825 return convertResultCode(srv);
1828 NS_IMETHODIMP
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(),
1842 aNumArguments,
1843 SQLITE_ANY,
1844 aFunction,
1845 basicFunctionHelper,
1846 nullptr,
1847 nullptr);
1848 if (srv != SQLITE_OK)
1849 return convertResultCode(srv);
1851 FunctionInfo info = { aFunction,
1852 Connection::FunctionInfo::SIMPLE,
1853 aNumArguments };
1854 mFunctions.Put(aFunctionName, info);
1856 return NS_OK;
1859 NS_IMETHODIMP
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(),
1877 aNumArguments,
1878 SQLITE_ANY,
1879 aFunction,
1880 nullptr,
1881 aggregateFunctionStepHelper,
1882 aggregateFunctionFinalHelper);
1883 if (srv != SQLITE_OK)
1884 return convertResultCode(srv);
1886 FunctionInfo info = { aFunction,
1887 Connection::FunctionInfo::AGGREGATE,
1888 aNumArguments };
1889 mFunctions.Put(aFunctionName, info);
1891 return NS_OK;
1894 NS_IMETHODIMP
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(),
1905 SQLITE_ANY,
1906 nullptr,
1907 nullptr,
1908 nullptr,
1909 nullptr);
1910 if (srv != SQLITE_OK)
1911 return convertResultCode(srv);
1913 mFunctions.Remove(aFunctionName);
1915 return NS_OK;
1918 NS_IMETHODIMP
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) {
1930 aHandler = nullptr;
1931 aGranularity = 0;
1933 mProgressHandler = aHandler;
1934 ::sqlite3_progress_handler(mDBConn, aGranularity, sProgressHelper, this);
1936 return NS_OK;
1939 NS_IMETHODIMP
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);
1951 return NS_OK;
1954 NS_IMETHODIMP
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()
1971 : nullptr,
1972 SQLITE_FCNTL_CHUNK_SIZE,
1973 &aChunkSize);
1974 #endif
1975 return NS_OK;
1978 NS_IMETHODIMP
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);
1990 return NS_OK;
1994 return NS_ERROR_FAILURE;
1997 // Implemented in TelemetryVFS.cpp
1998 already_AddRefed<QuotaObject>
1999 GetQuotaObjectForFile(sqlite3_file *pFile);
2001 NS_IMETHODIMP
2002 Connection::GetQuotaObjects(QuotaObject** aDatabaseQuotaObject,
2003 QuotaObject** aJournalQuotaObject)
2005 MOZ_ASSERT(aDatabaseQuotaObject);
2006 MOZ_ASSERT(aJournalQuotaObject);
2008 if (!mDBConn) {
2009 return NS_ERROR_NOT_INITIALIZED;
2012 sqlite3_file* file;
2013 int srv = ::sqlite3_file_control(mDBConn,
2014 nullptr,
2015 SQLITE_FCNTL_FILE_POINTER,
2016 &file);
2017 if (srv != SQLITE_OK) {
2018 return convertResultCode(srv);
2021 RefPtr<QuotaObject> databaseQuotaObject = GetQuotaObjectForFile(file);
2023 srv = ::sqlite3_file_control(mDBConn,
2024 nullptr,
2025 SQLITE_FCNTL_JOURNAL_POINTER,
2026 &file);
2027 if (srv != SQLITE_OK) {
2028 return convertResultCode(srv);
2031 RefPtr<QuotaObject> journalQuotaObject = GetQuotaObjectForFile(file);
2033 databaseQuotaObject.forget(aDatabaseQuotaObject);
2034 journalQuotaObject.forget(aJournalQuotaObject);
2035 return NS_OK;
2038 } // namespace storage
2039 } // namespace mozilla