LCOV - code coverage report
Current view: top level - storage - mozStorageConnection.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 417 905 46.1 %
Date: 2017-07-14 16:53:18 Functions: 49 103 47.6 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : #include <stdio.h>
       8             : 
       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"
      23             : #include "mozilla/ScopeExit.h"
      24             : 
      25             : #include "mozIStorageAggregateFunction.h"
      26             : #include "mozIStorageCompletionCallback.h"
      27             : #include "mozIStorageFunction.h"
      28             : 
      29             : #include "mozStorageAsyncStatementExecution.h"
      30             : #include "mozStorageSQLFunctions.h"
      31             : #include "mozStorageConnection.h"
      32             : #include "mozStorageService.h"
      33             : #include "mozStorageStatement.h"
      34             : #include "mozStorageAsyncStatement.h"
      35             : #include "mozStorageArgValueArray.h"
      36             : #include "mozStoragePrivateHelpers.h"
      37             : #include "mozStorageStatementData.h"
      38             : #include "StorageBaseStatementInternal.h"
      39             : #include "SQLCollations.h"
      40             : #include "FileSystemModule.h"
      41             : #include "mozStorageHelper.h"
      42             : #include "GeckoProfiler.h"
      43             : 
      44             : #include "mozilla/Logging.h"
      45             : #include "mozilla/Printf.h"
      46             : #include "nsProxyRelease.h"
      47             : #include <algorithm>
      48             : 
      49             : #define MIN_AVAILABLE_BYTES_PER_CHUNKED_GROWTH 524288000 // 500 MiB
      50             : 
      51             : // Maximum size of the pages cache per connection.
      52             : #define MAX_CACHE_SIZE_KIBIBYTES 2048 // 2 MiB
      53             : 
      54             : mozilla::LazyLogModule gStorageLog("mozStorage");
      55             : 
      56             : // Checks that the protected code is running on the main-thread only if the
      57             : // connection was also opened on it.
      58             : #ifdef DEBUG
      59             : #define CHECK_MAINTHREAD_ABUSE() \
      60             :   do { \
      61             :     nsCOMPtr<nsIThread> mainThread = do_GetMainThread(); \
      62             :     NS_WARNING_ASSERTION( \
      63             :       threadOpenedOn == mainThread || !NS_IsMainThread(), \
      64             :       "Using Storage synchronous API on main-thread, but the connection was " \
      65             :       "opened on another thread."); \
      66             :   } while(0)
      67             : #else
      68             : #define CHECK_MAINTHREAD_ABUSE() do { /* Nothing */ } while(0)
      69             : #endif
      70             : 
      71             : namespace mozilla {
      72             : namespace storage {
      73             : 
      74             : using mozilla::dom::quota::QuotaObject;
      75             : 
      76             : namespace {
      77             : 
      78             : int
      79           0 : nsresultToSQLiteResult(nsresult aXPCOMResultCode)
      80             : {
      81           0 :   if (NS_SUCCEEDED(aXPCOMResultCode)) {
      82           0 :     return SQLITE_OK;
      83             :   }
      84             : 
      85           0 :   switch (aXPCOMResultCode) {
      86             :     case NS_ERROR_FILE_CORRUPTED:
      87           0 :       return SQLITE_CORRUPT;
      88             :     case NS_ERROR_FILE_ACCESS_DENIED:
      89           0 :       return SQLITE_CANTOPEN;
      90             :     case NS_ERROR_STORAGE_BUSY:
      91           0 :       return SQLITE_BUSY;
      92             :     case NS_ERROR_FILE_IS_LOCKED:
      93           0 :       return SQLITE_LOCKED;
      94             :     case NS_ERROR_FILE_READ_ONLY:
      95           0 :       return SQLITE_READONLY;
      96             :     case NS_ERROR_STORAGE_IOERR:
      97           0 :       return SQLITE_IOERR;
      98             :     case NS_ERROR_FILE_NO_DEVICE_SPACE:
      99           0 :       return SQLITE_FULL;
     100             :     case NS_ERROR_OUT_OF_MEMORY:
     101           0 :       return SQLITE_NOMEM;
     102             :     case NS_ERROR_UNEXPECTED:
     103           0 :       return SQLITE_MISUSE;
     104             :     case NS_ERROR_ABORT:
     105           0 :       return SQLITE_ABORT;
     106             :     case NS_ERROR_STORAGE_CONSTRAINT:
     107           0 :       return SQLITE_CONSTRAINT;
     108             :     default:
     109           0 :       return SQLITE_ERROR;
     110             :   }
     111             : 
     112             :   MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Must return in switch above!");
     113             : }
     114             : 
     115             : ////////////////////////////////////////////////////////////////////////////////
     116             : //// Variant Specialization Functions (variantToSQLiteT)
     117             : 
     118             : int
     119           1 : sqlite3_T_int(sqlite3_context *aCtx,
     120             :               int aValue)
     121             : {
     122           1 :   ::sqlite3_result_int(aCtx, aValue);
     123           1 :   return SQLITE_OK;
     124             : }
     125             : 
     126             : int
     127          18 : sqlite3_T_int64(sqlite3_context *aCtx,
     128             :                 sqlite3_int64 aValue)
     129             : {
     130          18 :   ::sqlite3_result_int64(aCtx, aValue);
     131          18 :   return SQLITE_OK;
     132             : }
     133             : 
     134             : int
     135           0 : sqlite3_T_double(sqlite3_context *aCtx,
     136             :                  double aValue)
     137             : {
     138           0 :   ::sqlite3_result_double(aCtx, aValue);
     139           0 :   return SQLITE_OK;
     140             : }
     141             : 
     142             : int
     143           0 : sqlite3_T_text(sqlite3_context *aCtx,
     144             :                const nsCString &aValue)
     145             : {
     146           0 :   ::sqlite3_result_text(aCtx,
     147             :                         aValue.get(),
     148           0 :                         aValue.Length(),
     149           0 :                         SQLITE_TRANSIENT);
     150           0 :   return SQLITE_OK;
     151             : }
     152             : 
     153             : int
     154          23 : sqlite3_T_text16(sqlite3_context *aCtx,
     155             :                  const nsString &aValue)
     156             : {
     157          23 :   ::sqlite3_result_text16(aCtx,
     158          23 :                           aValue.get(),
     159          23 :                           aValue.Length() * 2, // Number of bytes.
     160          23 :                           SQLITE_TRANSIENT);
     161          23 :   return SQLITE_OK;
     162             : }
     163             : 
     164             : int
     165           0 : sqlite3_T_null(sqlite3_context *aCtx)
     166             : {
     167           0 :   ::sqlite3_result_null(aCtx);
     168           0 :   return SQLITE_OK;
     169             : }
     170             : 
     171             : int
     172           0 : sqlite3_T_blob(sqlite3_context *aCtx,
     173             :                const void *aData,
     174             :                int aSize)
     175             : {
     176           0 :   ::sqlite3_result_blob(aCtx, aData, aSize, free);
     177           0 :   return SQLITE_OK;
     178             : }
     179             : 
     180             : #include "variantToSQLiteT_impl.h"
     181             : 
     182             : ////////////////////////////////////////////////////////////////////////////////
     183             : //// Modules
     184             : 
     185             : struct Module
     186             : {
     187             :   const char* name;
     188             :   int (*registerFunc)(sqlite3*, const char*);
     189             : };
     190             : 
     191             : Module gModules[] = {
     192             :   { "filesystem", RegisterFileSystemModule }
     193             : };
     194             : 
     195             : ////////////////////////////////////////////////////////////////////////////////
     196             : //// Local Functions
     197             : 
     198           0 : int tracefunc (unsigned aReason, void *aClosure, void *aP, void *aX)
     199             : {
     200           0 :   switch (aReason) {
     201             :     case SQLITE_TRACE_STMT: {
     202             :       // aP is a pointer to the prepared statement.
     203           0 :       sqlite3_stmt* stmt = static_cast<sqlite3_stmt*>(aP);
     204             :       // aX is a pointer to a string containing the unexpanded SQL or a comment,
     205             :       // starting with "--"" in case of a trigger.
     206           0 :       char* expanded = static_cast<char*>(aX);
     207             :       // Simulate what sqlite_trace was doing.
     208           0 :       if (!::strncmp(expanded, "--", 2)) {
     209           0 :         MOZ_LOG(gStorageLog, LogLevel::Debug,
     210             :           ("TRACE_STMT on %p: '%s'", aClosure, expanded));
     211             :       } else {
     212           0 :         char* sql = ::sqlite3_expanded_sql(stmt);
     213           0 :         MOZ_LOG(gStorageLog, LogLevel::Debug,
     214             :           ("TRACE_STMT on %p: '%s'", aClosure, sql));
     215           0 :         ::sqlite3_free(sql);
     216             :       }
     217           0 :       break;
     218             :     }
     219             :     case SQLITE_TRACE_PROFILE: {
     220             :       // aX is pointer to a 64bit integer containing nanoseconds it took to
     221             :       // execute the last command.
     222           0 :       sqlite_int64 time = *(static_cast<sqlite_int64*>(aX)) / 1000000;
     223           0 :       if (time > 0) {
     224           0 :         MOZ_LOG(gStorageLog, LogLevel::Debug,
     225             :           ("TRACE_TIME on %p: %lldms", aClosure, time));
     226             :       }
     227           0 :       break;
     228             :     }
     229             :   }
     230           0 :   return 0;
     231             : }
     232             : 
     233             : void
     234          42 : basicFunctionHelper(sqlite3_context *aCtx,
     235             :                     int aArgc,
     236             :                     sqlite3_value **aArgv)
     237             : {
     238          42 :   void *userData = ::sqlite3_user_data(aCtx);
     239             : 
     240          42 :   mozIStorageFunction *func = static_cast<mozIStorageFunction *>(userData);
     241             : 
     242          84 :   RefPtr<ArgValueArray> arguments(new ArgValueArray(aArgc, aArgv));
     243          42 :   if (!arguments)
     244           0 :       return;
     245             : 
     246          84 :   nsCOMPtr<nsIVariant> result;
     247          42 :   nsresult rv = func->OnFunctionCall(arguments, getter_AddRefs(result));
     248          42 :   if (NS_FAILED(rv)) {
     249           0 :     nsAutoCString errorMessage;
     250           0 :     GetErrorName(rv, errorMessage);
     251           0 :     errorMessage.InsertLiteral("User function returned ", 0);
     252           0 :     errorMessage.Append('!');
     253             : 
     254           0 :     NS_WARNING(errorMessage.get());
     255             : 
     256           0 :     ::sqlite3_result_error(aCtx, errorMessage.get(), -1);
     257           0 :     ::sqlite3_result_error_code(aCtx, nsresultToSQLiteResult(rv));
     258           0 :     return;
     259             :   }
     260          42 :   int retcode = variantToSQLiteT(aCtx, result);
     261          42 :   if (retcode == SQLITE_IGNORE) {
     262           0 :     ::sqlite3_result_int(aCtx, SQLITE_IGNORE);
     263          42 :   } else if (retcode != SQLITE_OK) {
     264           0 :     NS_WARNING("User function returned invalid data type!");
     265             :     ::sqlite3_result_error(aCtx,
     266             :                            "User function returned invalid data type",
     267           0 :                            -1);
     268             :   }
     269             : }
     270             : 
     271             : void
     272           0 : aggregateFunctionStepHelper(sqlite3_context *aCtx,
     273             :                             int aArgc,
     274             :                             sqlite3_value **aArgv)
     275             : {
     276           0 :   void *userData = ::sqlite3_user_data(aCtx);
     277             :   mozIStorageAggregateFunction *func =
     278           0 :     static_cast<mozIStorageAggregateFunction *>(userData);
     279             : 
     280           0 :   RefPtr<ArgValueArray> arguments(new ArgValueArray(aArgc, aArgv));
     281           0 :   if (!arguments)
     282           0 :     return;
     283             : 
     284           0 :   if (NS_FAILED(func->OnStep(arguments)))
     285           0 :     NS_WARNING("User aggregate step function returned error code!");
     286             : }
     287             : 
     288             : void
     289           0 : aggregateFunctionFinalHelper(sqlite3_context *aCtx)
     290             : {
     291           0 :   void *userData = ::sqlite3_user_data(aCtx);
     292             :   mozIStorageAggregateFunction *func =
     293           0 :     static_cast<mozIStorageAggregateFunction *>(userData);
     294             : 
     295           0 :   RefPtr<nsIVariant> result;
     296           0 :   if (NS_FAILED(func->OnFinal(getter_AddRefs(result)))) {
     297           0 :     NS_WARNING("User aggregate final function returned error code!");
     298             :     ::sqlite3_result_error(aCtx,
     299             :                            "User aggregate final function returned error code",
     300           0 :                            -1);
     301           0 :     return;
     302             :   }
     303             : 
     304           0 :   if (variantToSQLiteT(aCtx, result) != SQLITE_OK) {
     305           0 :     NS_WARNING("User aggregate final function returned invalid data type!");
     306             :     ::sqlite3_result_error(aCtx,
     307             :                            "User aggregate final function returned invalid data type",
     308           0 :                            -1);
     309             :   }
     310             : }
     311             : 
     312             : /**
     313             :  * This code is heavily based on the sample at:
     314             :  *   http://www.sqlite.org/unlock_notify.html
     315             :  */
     316           0 : class UnlockNotification
     317             : {
     318             : public:
     319           0 :   UnlockNotification()
     320           0 :   : mMutex("UnlockNotification mMutex")
     321             :   , mCondVar(mMutex, "UnlockNotification condVar")
     322           0 :   , mSignaled(false)
     323             :   {
     324           0 :   }
     325             : 
     326           0 :   void Wait()
     327             :   {
     328           0 :     MutexAutoLock lock(mMutex);
     329           0 :     while (!mSignaled) {
     330           0 :       (void)mCondVar.Wait();
     331             :     }
     332           0 :   }
     333             : 
     334           0 :   void Signal()
     335             :   {
     336           0 :     MutexAutoLock lock(mMutex);
     337           0 :     mSignaled = true;
     338           0 :     (void)mCondVar.Notify();
     339           0 :   }
     340             : 
     341             : private:
     342             :   Mutex mMutex;
     343             :   CondVar mCondVar;
     344             :   bool mSignaled;
     345             : };
     346             : 
     347             : void
     348           0 : UnlockNotifyCallback(void **aArgs,
     349             :                      int aArgsSize)
     350             : {
     351           0 :   for (int i = 0; i < aArgsSize; i++) {
     352             :     UnlockNotification *notification =
     353           0 :       static_cast<UnlockNotification *>(aArgs[i]);
     354           0 :     notification->Signal();
     355             :   }
     356           0 : }
     357             : 
     358             : int
     359           0 : WaitForUnlockNotify(sqlite3* aDatabase)
     360             : {
     361           0 :   UnlockNotification notification;
     362             :   int srv = ::sqlite3_unlock_notify(aDatabase, UnlockNotifyCallback,
     363           0 :                                     &notification);
     364           0 :   MOZ_ASSERT(srv == SQLITE_LOCKED || srv == SQLITE_OK);
     365           0 :   if (srv == SQLITE_OK) {
     366           0 :     notification.Wait();
     367             :   }
     368             : 
     369           0 :   return srv;
     370             : }
     371             : 
     372             : ////////////////////////////////////////////////////////////////////////////////
     373             : //// Local Classes
     374             : 
     375             : class AsyncCloseConnection final: public Runnable
     376             : {
     377             : public:
     378           1 :   AsyncCloseConnection(Connection *aConnection,
     379             :                        sqlite3 *aNativeConnection,
     380             :                        nsIRunnable *aCallbackEvent)
     381           1 :   : Runnable("storage::AsyncCloseConnection")
     382             :   , mConnection(aConnection)
     383             :   , mNativeConnection(aNativeConnection)
     384           1 :   , mCallbackEvent(aCallbackEvent)
     385             :   {
     386           1 :   }
     387             : 
     388           1 :   NS_IMETHOD Run() override
     389             :   {
     390             :     // This code is executed on the background thread
     391           1 :     MOZ_ASSERT(NS_GetCurrentThread() != mConnection->threadOpenedOn);
     392             : 
     393             :     nsCOMPtr<nsIRunnable> event =
     394           2 :       NewRunnableMethod("storage::Connection::shutdownAsyncThread",
     395           2 :                         mConnection, &Connection::shutdownAsyncThread);
     396           1 :     MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(event));
     397             : 
     398             :     // Internal close.
     399           1 :     (void)mConnection->internalClose(mNativeConnection);
     400             : 
     401             :     // Callback
     402           1 :     if (mCallbackEvent) {
     403           0 :       nsCOMPtr<nsIThread> thread;
     404           0 :       (void)NS_GetMainThread(getter_AddRefs(thread));
     405           0 :       (void)thread->Dispatch(mCallbackEvent, NS_DISPATCH_NORMAL);
     406             :     }
     407             : 
     408           2 :     return NS_OK;
     409             :   }
     410             : 
     411           3 :   ~AsyncCloseConnection() override {
     412             :     NS_ReleaseOnMainThread(
     413           1 :       "AsyncCloseConnection::mConnection", mConnection.forget());
     414             :     NS_ReleaseOnMainThread(
     415           1 :       "AsyncCloseConnection::mCallbackEvent", mCallbackEvent.forget());
     416           3 :   }
     417             : private:
     418             :   RefPtr<Connection> mConnection;
     419             :   sqlite3 *mNativeConnection;
     420             :   nsCOMPtr<nsIRunnable> mCallbackEvent;
     421             : };
     422             : 
     423             : /**
     424             :  * An event used to initialize the clone of a connection.
     425             :  *
     426             :  * Must be executed on the clone's async execution thread.
     427             :  */
     428             : class AsyncInitializeClone final: public Runnable
     429             : {
     430             : public:
     431             :   /**
     432             :    * @param aConnection The connection being cloned.
     433             :    * @param aClone The clone.
     434             :    * @param aReadOnly If |true|, the clone is read only.
     435             :    * @param aCallback A callback to trigger once initialization
     436             :    *                  is complete. This event will be called on
     437             :    *                  aClone->threadOpenedOn.
     438             :    */
     439           1 :   AsyncInitializeClone(Connection* aConnection,
     440             :                        Connection* aClone,
     441             :                        const bool aReadOnly,
     442             :                        mozIStorageCompletionCallback* aCallback)
     443           1 :     : Runnable("storage::AsyncInitializeClone")
     444             :     , mConnection(aConnection)
     445             :     , mClone(aClone)
     446             :     , mReadOnly(aReadOnly)
     447           1 :     , mCallback(aCallback)
     448             :   {
     449           1 :     MOZ_ASSERT(NS_IsMainThread());
     450           1 :   }
     451             : 
     452           1 :   NS_IMETHOD Run() override {
     453           1 :     MOZ_ASSERT(!NS_IsMainThread());
     454           1 :     nsresult rv = mConnection->initializeClone(mClone, mReadOnly);
     455           1 :     if (NS_FAILED(rv)) {
     456           0 :       return Dispatch(rv, nullptr);
     457             :     }
     458             :     return Dispatch(NS_OK,
     459           1 :                     NS_ISUPPORTS_CAST(mozIStorageAsyncConnection*, mClone));
     460             :   }
     461             : 
     462             : private:
     463           1 :   nsresult Dispatch(nsresult aResult, nsISupports* aValue) {
     464             :     RefPtr<CallbackComplete> event = new CallbackComplete(aResult,
     465             :                                                             aValue,
     466           3 :                                                             mCallback.forget());
     467           2 :     return mClone->threadOpenedOn->Dispatch(event, NS_DISPATCH_NORMAL);
     468             :   }
     469             : 
     470           3 :   ~AsyncInitializeClone() override {
     471           2 :     nsCOMPtr<nsIThread> thread;
     472           2 :     DebugOnly<nsresult> rv = NS_GetMainThread(getter_AddRefs(thread));
     473           1 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     474             : 
     475             :     // Handle ambiguous nsISupports inheritance.
     476             :     NS_ProxyRelease(
     477           1 :       "AsyncInitializeClone::mConnection", thread, mConnection.forget());
     478             :     NS_ProxyRelease(
     479           1 :         "AsyncInitializeClone::mClone", thread, mClone.forget());
     480             : 
     481             :     // Generally, the callback will be released by CallbackComplete.
     482             :     // However, if for some reason Run() is not executed, we still
     483             :     // need to ensure that it is released here.
     484             :     NS_ProxyRelease(
     485           1 :       "AsyncInitializeClone::mCallback", thread, mCallback.forget());
     486           3 :   }
     487             : 
     488             :   RefPtr<Connection> mConnection;
     489             :   RefPtr<Connection> mClone;
     490             :   const bool mReadOnly;
     491             :   nsCOMPtr<mozIStorageCompletionCallback> mCallback;
     492             : };
     493             : 
     494             : /**
     495             :  * A listener for async connection closing.
     496             :  */
     497             : class CloseListener final :  public mozIStorageCompletionCallback
     498             : {
     499             : public:
     500             :   NS_DECL_ISUPPORTS
     501           0 :   CloseListener()
     502           0 :     : mClosed(false)
     503             :   {
     504           0 :   }
     505             : 
     506           0 :   NS_IMETHOD Complete(nsresult, nsISupports*) override
     507             :   {
     508           0 :     mClosed = true;
     509           0 :     return NS_OK;
     510             :   }
     511             : 
     512             :   bool mClosed;
     513             : 
     514             : private:
     515             :   ~CloseListener() = default;
     516             : };
     517             : 
     518           0 : NS_IMPL_ISUPPORTS(CloseListener, mozIStorageCompletionCallback)
     519             : 
     520             : } // namespace
     521             : 
     522             : ////////////////////////////////////////////////////////////////////////////////
     523             : //// Connection
     524             : 
     525           8 : Connection::Connection(Service *aService,
     526             :                        int aFlags,
     527             :                        bool aAsyncOnly,
     528           8 :                        bool aIgnoreLockingMode)
     529             : : sharedAsyncExecutionMutex("Connection::sharedAsyncExecutionMutex")
     530             : , sharedDBMutex("Connection::sharedDBMutex")
     531          16 : , threadOpenedOn(do_GetCurrentThread())
     532             : , mDBConn(nullptr)
     533             : , mAsyncExecutionThreadShuttingDown(false)
     534             : , mConnectionClosed(false)
     535             : , mTransactionInProgress(false)
     536             : , mProgressHandler(nullptr)
     537             : , mFlags(aFlags)
     538             : , mIgnoreLockingMode(aIgnoreLockingMode)
     539             : , mStorageService(aService)
     540          24 : , mAsyncOnly(aAsyncOnly)
     541             : {
     542           8 :   MOZ_ASSERT(!mIgnoreLockingMode || mFlags & SQLITE_OPEN_READONLY,
     543             :              "Can't ignore locking for a non-readonly connection!");
     544           8 :   mStorageService->registerConnection(this);
     545           8 : }
     546             : 
     547           2 : Connection::~Connection()
     548             : {
     549           1 :   Unused << Close();
     550           1 :   MOZ_ASSERT(!mAsyncExecutionThread,
     551             :              "The async thread has not been shutdown properly!");
     552           1 : }
     553             : 
     554         153 : NS_IMPL_ADDREF(Connection)
     555             : 
     556         125 : NS_INTERFACE_MAP_BEGIN(Connection)
     557         125 :   NS_INTERFACE_MAP_ENTRY(mozIStorageAsyncConnection)
     558         119 :   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
     559         116 :   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(mozIStorageConnection, !mAsyncOnly)
     560         100 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageConnection)
     561          78 : NS_INTERFACE_MAP_END
     562             : 
     563             : // This is identical to what NS_IMPL_RELEASE provides, but with the
     564             : // extra |1 == count| case.
     565          91 : NS_IMETHODIMP_(MozExternalRefCountType) Connection::Release(void)
     566             : {
     567          91 :   NS_PRECONDITION(0 != mRefCnt, "dup release");
     568          91 :   nsrefcnt count = --mRefCnt;
     569          91 :   NS_LOG_RELEASE(this, count, "Connection");
     570          91 :   if (1 == count) {
     571             :     // If the refcount is 1, the single reference must be from
     572             :     // gService->mConnections (in class |Service|).  Which means we can
     573             :     // unregister it safely.
     574           1 :     mStorageService->unregisterConnection(this);
     575          90 :   } else if (0 == count) {
     576           1 :     mRefCnt = 1; /* stabilize */
     577             : #if 0 /* enable this to find non-threadsafe destructors: */
     578             :     NS_ASSERT_OWNINGTHREAD(Connection);
     579             : #endif
     580           1 :     delete (this);
     581           1 :     return 0;
     582             :   }
     583          90 :   return count;
     584             : }
     585             : 
     586             : int32_t
     587           0 : Connection::getSqliteRuntimeStatus(int32_t aStatusOption, int32_t* aMaxValue)
     588             : {
     589           0 :   MOZ_ASSERT(mDBConn, "A connection must exist at this point");
     590           0 :   int curr = 0, max = 0;
     591           0 :   DebugOnly<int> rc = ::sqlite3_db_status(mDBConn, aStatusOption, &curr, &max, 0);
     592           0 :   MOZ_ASSERT(NS_SUCCEEDED(convertResultCode(rc)));
     593           0 :   if (aMaxValue)
     594           0 :     *aMaxValue = max;
     595           0 :   return curr;
     596             : }
     597             : 
     598             : nsIEventTarget *
     599          20 : Connection::getAsyncExecutionTarget()
     600             : {
     601          20 :   NS_ENSURE_TRUE(threadOpenedOn == NS_GetCurrentThread(), nullptr);
     602             : 
     603             :   // Don't return the asynchronous thread if we are shutting down.
     604          20 :   if (mAsyncExecutionThreadShuttingDown) {
     605           0 :     return nullptr;
     606             :   }
     607             : 
     608             :   // Create the async thread if there's none yet.
     609          20 :   if (!mAsyncExecutionThread) {
     610           5 :     static nsThreadPoolNaming naming;
     611          10 :     nsresult rv = NS_NewNamedThread(naming.GetNextThreadName("mozStorage"),
     612          15 :                                     getter_AddRefs(mAsyncExecutionThread));
     613           5 :     if (NS_FAILED(rv)) {
     614           0 :       NS_WARNING("Failed to create async thread.");
     615           0 :       return nullptr;
     616             :     }
     617             :   }
     618             : 
     619          20 :   return mAsyncExecutionThread;
     620             : }
     621             : 
     622             : nsresult
     623           0 : Connection::initialize()
     624             : {
     625           0 :   NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
     626           0 :   MOZ_ASSERT(!mIgnoreLockingMode, "Can't ignore locking on an in-memory db.");
     627           0 :   AUTO_PROFILER_LABEL("Connection::initialize", STORAGE);
     628             : 
     629             :   // in memory database requested, sqlite uses a magic file name
     630           0 :   int srv = ::sqlite3_open_v2(":memory:", &mDBConn, mFlags, nullptr);
     631           0 :   if (srv != SQLITE_OK) {
     632           0 :     mDBConn = nullptr;
     633           0 :     return convertResultCode(srv);
     634             :   }
     635             : 
     636             :   // Do not set mDatabaseFile or mFileURL here since this is a "memory"
     637             :   // database.
     638             : 
     639           0 :   nsresult rv = initializeInternal();
     640           0 :   NS_ENSURE_SUCCESS(rv, rv);
     641             : 
     642           0 :   return NS_OK;
     643             : }
     644             : 
     645             : nsresult
     646           8 : Connection::initialize(nsIFile *aDatabaseFile)
     647             : {
     648           8 :   NS_ASSERTION (aDatabaseFile, "Passed null file!");
     649           8 :   NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
     650          16 :   AUTO_PROFILER_LABEL("Connection::initialize", STORAGE);
     651             : 
     652           8 :   mDatabaseFile = aDatabaseFile;
     653             : 
     654          16 :   nsAutoString path;
     655           8 :   nsresult rv = aDatabaseFile->GetPath(path);
     656           8 :   NS_ENSURE_SUCCESS(rv, rv);
     657             : 
     658             : #ifdef XP_WIN
     659             :   static const char* sIgnoreLockingVFS = "win32-none";
     660             : #else
     661             :   static const char* sIgnoreLockingVFS = "unix-none";
     662             : #endif
     663           8 :   const char* vfs = mIgnoreLockingMode ? sIgnoreLockingVFS : nullptr;
     664             : 
     665          16 :   int srv = ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path).get(), &mDBConn,
     666          16 :                               mFlags, vfs);
     667           8 :   if (srv != SQLITE_OK) {
     668           0 :     mDBConn = nullptr;
     669           0 :     return convertResultCode(srv);
     670             :   }
     671             : 
     672             :   // Do not set mFileURL here since this is database does not have an associated
     673             :   // URL.
     674           8 :   mDatabaseFile = aDatabaseFile;
     675             : 
     676           8 :   rv = initializeInternal();
     677           8 :   NS_ENSURE_SUCCESS(rv, rv);
     678             : 
     679           8 :   return NS_OK;
     680             : }
     681             : 
     682             : nsresult
     683           0 : Connection::initialize(nsIFileURL *aFileURL)
     684             : {
     685           0 :   NS_ASSERTION (aFileURL, "Passed null file URL!");
     686           0 :   NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
     687           0 :   AUTO_PROFILER_LABEL("Connection::initialize", STORAGE);
     688             : 
     689           0 :   nsCOMPtr<nsIFile> databaseFile;
     690           0 :   nsresult rv = aFileURL->GetFile(getter_AddRefs(databaseFile));
     691           0 :   NS_ENSURE_SUCCESS(rv, rv);
     692             : 
     693           0 :   nsAutoCString spec;
     694           0 :   rv = aFileURL->GetSpec(spec);
     695           0 :   NS_ENSURE_SUCCESS(rv, rv);
     696             : 
     697           0 :   int srv = ::sqlite3_open_v2(spec.get(), &mDBConn, mFlags, nullptr);
     698           0 :   if (srv != SQLITE_OK) {
     699           0 :     mDBConn = nullptr;
     700           0 :     return convertResultCode(srv);
     701             :   }
     702             : 
     703             :   // Set both mDatabaseFile and mFileURL here.
     704           0 :   mFileURL = aFileURL;
     705           0 :   mDatabaseFile = databaseFile;
     706             : 
     707           0 :   rv = initializeInternal();
     708           0 :   NS_ENSURE_SUCCESS(rv, rv);
     709             : 
     710           0 :   return NS_OK;
     711             : }
     712             : 
     713             : nsresult
     714           8 : Connection::initializeInternal()
     715             : {
     716           8 :   MOZ_ASSERT(mDBConn);
     717             : 
     718           0 :   auto guard = MakeScopeExit([&]() {
     719             :     {
     720           0 :       MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
     721           0 :       mConnectionClosed = true;
     722             :     }
     723           0 :     MOZ_ALWAYS_TRUE(::sqlite3_close(mDBConn) == SQLITE_OK);
     724           0 :     mDBConn = nullptr;
     725           0 :     sharedDBMutex.destroy();
     726          16 :   });
     727             : 
     728           8 :   if (mFileURL) {
     729           0 :     const char* dbPath = ::sqlite3_db_filename(mDBConn, "main");
     730           0 :     MOZ_ASSERT(dbPath);
     731             : 
     732             :     const char* telemetryFilename =
     733           0 :       ::sqlite3_uri_parameter(dbPath, "telemetryFilename");
     734           0 :     if (telemetryFilename) {
     735           0 :       if (NS_WARN_IF(*telemetryFilename == '\0')) {
     736           0 :         return NS_ERROR_INVALID_ARG;
     737             :       }
     738           0 :       mTelemetryFilename = telemetryFilename;
     739             :     }
     740             :   }
     741             : 
     742           8 :   if (mTelemetryFilename.IsEmpty()) {
     743           8 :     mTelemetryFilename = getFilename();
     744           8 :     MOZ_ASSERT(!mTelemetryFilename.IsEmpty());
     745             :   }
     746             : 
     747             :   // Properly wrap the database handle's mutex.
     748           8 :   sharedDBMutex.initWithMutex(sqlite3_db_mutex(mDBConn));
     749             : 
     750             :   // SQLite tracing can slow down queries (especially long queries)
     751             :   // significantly. Don't trace unless the user is actively monitoring SQLite.
     752           8 :   if (MOZ_LOG_TEST(gStorageLog, LogLevel::Debug)) {
     753           0 :     ::sqlite3_trace_v2(mDBConn,
     754             :                        SQLITE_TRACE_STMT | SQLITE_TRACE_PROFILE,
     755           0 :                        tracefunc, this);
     756             : 
     757           0 :     MOZ_LOG(gStorageLog, LogLevel::Debug, ("Opening connection to '%s' (%p)",
     758             :                                         mTelemetryFilename.get(), this));
     759             :   }
     760             : 
     761           8 :   int64_t pageSize = Service::getDefaultPageSize();
     762             : 
     763             :   // Set page_size to the preferred default value.  This is effective only if
     764             :   // the database has just been created, otherwise, if the database does not
     765             :   // use WAL journal mode, a VACUUM operation will updated its page_size.
     766             :   nsAutoCString pageSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
     767          16 :                               "PRAGMA page_size = ");
     768           8 :   pageSizeQuery.AppendInt(pageSize);
     769           8 :   int srv = executeSql(mDBConn, pageSizeQuery.get());
     770           8 :   if (srv != SQLITE_OK) {
     771           0 :     return convertResultCode(srv);
     772             :   }
     773             : 
     774             :   // Setting the cache_size forces the database open, verifying if it is valid
     775             :   // or corrupt.  So this is executed regardless it being actually needed.
     776             :   // The cache_size is calculated from the actual page_size, to save memory.
     777             :   nsAutoCString cacheSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
     778          16 :                                "PRAGMA cache_size = ");
     779           8 :   cacheSizeQuery.AppendInt(-MAX_CACHE_SIZE_KIBIBYTES);
     780           8 :   srv = executeSql(mDBConn, cacheSizeQuery.get());
     781           8 :   if (srv != SQLITE_OK) {
     782           0 :     return convertResultCode(srv);
     783             :   }
     784             : 
     785             : #if defined(MOZ_MEMORY_TEMP_STORE_PRAGMA)
     786             :   (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA temp_store = 2;"));
     787             : #endif
     788             : 
     789             :   // Register our built-in SQL functions.
     790           8 :   srv = registerFunctions(mDBConn);
     791           8 :   if (srv != SQLITE_OK) {
     792           0 :     return convertResultCode(srv);
     793             :   }
     794             : 
     795             :   // Register our built-in SQL collating sequences.
     796           8 :   srv = registerCollations(mDBConn, mStorageService);
     797           8 :   if (srv != SQLITE_OK) {
     798           0 :     return convertResultCode(srv);
     799             :   }
     800             : 
     801             :   // Set the synchronous PRAGMA, according to the preference.
     802           8 :   switch (Service::getSynchronousPref()) {
     803             :     case 2:
     804           0 :       (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     805           0 :           "PRAGMA synchronous = FULL;"));
     806           0 :       break;
     807             :     case 0:
     808           0 :       (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     809           0 :           "PRAGMA synchronous = OFF;"));
     810           0 :       break;
     811             :     case 1:
     812             :     default:
     813          24 :       (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     814          16 :           "PRAGMA synchronous = NORMAL;"));
     815           8 :       break;
     816             :   }
     817             : 
     818             :   // Initialization succeeded, we can stop guarding for failures.
     819           8 :   guard.release();
     820           8 :   return NS_OK;
     821             : }
     822             : 
     823             : nsresult
     824           0 : Connection::initializeOnAsyncThread(nsIFile* aStorageFile) {
     825           0 :   MOZ_ASSERT(threadOpenedOn != NS_GetCurrentThread());
     826           0 :   nsresult rv = aStorageFile ? initialize(aStorageFile)
     827           0 :                              : initialize();
     828           0 :   if (NS_FAILED(rv)) {
     829             :     // Shutdown the async thread, since initialization failed.
     830           0 :     MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
     831           0 :     mAsyncExecutionThreadShuttingDown = true;
     832             :     nsCOMPtr<nsIRunnable> event =
     833           0 :       NewRunnableMethod("Connection::shutdownAsyncThread",
     834           0 :                         this, &Connection::shutdownAsyncThread);
     835           0 :     Unused << NS_DispatchToMainThread(event);
     836             :   }
     837           0 :   return rv;
     838             : }
     839             : 
     840             : nsresult
     841           3 : Connection::databaseElementExists(enum DatabaseElementType aElementType,
     842             :                                   const nsACString &aElementName,
     843             :                                   bool *_exists)
     844             : {
     845           3 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
     846             : 
     847             :   // When constructing the query, make sure to SELECT the correct db's sqlite_master
     848             :   // if the user is prefixing the element with a specific db. ex: sample.test
     849           6 :   nsCString query("SELECT name FROM (SELECT * FROM ");
     850           6 :   nsDependentCSubstring element;
     851           3 :   int32_t ind = aElementName.FindChar('.');
     852           3 :   if (ind == kNotFound) {
     853           3 :     element.Assign(aElementName);
     854             :   }
     855             :   else {
     856           0 :     nsDependentCSubstring db(Substring(aElementName, 0, ind + 1));
     857           0 :     element.Assign(Substring(aElementName, ind + 1, aElementName.Length()));
     858           0 :     query.Append(db);
     859             :   }
     860           3 :   query.AppendLiteral("sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type = '");
     861             : 
     862           3 :   switch (aElementType) {
     863             :     case INDEX:
     864           1 :       query.AppendLiteral("index");
     865           1 :       break;
     866             :     case TABLE:
     867           2 :       query.AppendLiteral("table");
     868           2 :       break;
     869             :   }
     870           3 :   query.AppendLiteral("' AND name ='");
     871           3 :   query.Append(element);
     872           3 :   query.Append('\'');
     873             : 
     874             :   sqlite3_stmt *stmt;
     875           3 :   int srv = prepareStatement(mDBConn, query, &stmt);
     876           3 :   if (srv != SQLITE_OK)
     877           0 :     return convertResultCode(srv);
     878             : 
     879           3 :   srv = stepStatement(mDBConn, stmt);
     880             :   // we just care about the return value from step
     881           3 :   (void)::sqlite3_finalize(stmt);
     882             : 
     883           3 :   if (srv == SQLITE_ROW) {
     884           2 :     *_exists = true;
     885           2 :     return NS_OK;
     886             :   }
     887           1 :   if (srv == SQLITE_DONE) {
     888           1 :     *_exists = false;
     889           1 :     return NS_OK;
     890             :   }
     891             : 
     892           0 :   return convertResultCode(srv);
     893             : }
     894             : 
     895             : bool
     896           0 : Connection::findFunctionByInstance(nsISupports *aInstance)
     897             : {
     898           0 :   sharedDBMutex.assertCurrentThreadOwns();
     899             : 
     900           0 :   for (auto iter = mFunctions.Iter(); !iter.Done(); iter.Next()) {
     901           0 :     if (iter.UserData().function == aInstance) {
     902           0 :       return true;
     903             :     }
     904             :   }
     905           0 :   return false;
     906             : }
     907             : 
     908             : /* static */ int
     909           0 : Connection::sProgressHelper(void *aArg)
     910             : {
     911           0 :   Connection *_this = static_cast<Connection *>(aArg);
     912           0 :   return _this->progressHandler();
     913             : }
     914             : 
     915             : int
     916           0 : Connection::progressHandler()
     917             : {
     918           0 :   sharedDBMutex.assertCurrentThreadOwns();
     919           0 :   if (mProgressHandler) {
     920             :     bool result;
     921           0 :     nsresult rv = mProgressHandler->OnProgress(this, &result);
     922           0 :     if (NS_FAILED(rv)) return 0; // Don't break request
     923           0 :     return result ? 1 : 0;
     924             :   }
     925           0 :   return 0;
     926             : }
     927             : 
     928             : nsresult
     929           1 : Connection::setClosedState()
     930             : {
     931             :   // Ensure that we are on the correct thread to close the database.
     932             :   bool onOpenedThread;
     933           1 :   nsresult rv = threadOpenedOn->IsOnCurrentThread(&onOpenedThread);
     934           1 :   NS_ENSURE_SUCCESS(rv, rv);
     935           1 :   if (!onOpenedThread) {
     936           0 :     NS_ERROR("Must close the database on the thread that you opened it with!");
     937           0 :     return NS_ERROR_UNEXPECTED;
     938             :   }
     939             : 
     940             :   // Flag that we are shutting down the async thread, so that
     941             :   // getAsyncExecutionTarget knows not to expose/create the async thread.
     942             :   {
     943           2 :     MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
     944           1 :     NS_ENSURE_FALSE(mAsyncExecutionThreadShuttingDown, NS_ERROR_UNEXPECTED);
     945           1 :     mAsyncExecutionThreadShuttingDown = true;
     946             : 
     947             :     // Set the property to null before closing the connection, otherwise the other
     948             :     // functions in the module may try to use the connection after it is closed.
     949           1 :     mDBConn = nullptr;
     950             :   }
     951           1 :   return NS_OK;
     952             : }
     953             : 
     954             : bool
     955         150 : Connection::connectionReady()
     956             : {
     957         150 :   return mDBConn != nullptr;
     958             : }
     959             : 
     960             : bool
     961         222 : Connection::isConnectionReadyOnThisThread()
     962             : {
     963         222 :   MOZ_ASSERT_IF(mDBConn, !mConnectionClosed);
     964         305 :   if (mAsyncExecutionThread &&
     965         305 :       mAsyncExecutionThread->IsOnCurrentThread()) {
     966          73 :     return true;
     967             :   }
     968         149 :   return  connectionReady();
     969             : }
     970             : 
     971             : bool
     972           0 : Connection::isClosing()
     973             : {
     974           0 :   MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
     975           0 :   return mAsyncExecutionThreadShuttingDown && !mConnectionClosed;
     976             : }
     977             : 
     978             : bool
     979           0 : Connection::isClosed()
     980             : {
     981           0 :   MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
     982           0 :   return mConnectionClosed;
     983             : }
     984             : 
     985             : bool
     986          21 : Connection::isClosed(MutexAutoLock& lock)
     987             : {
     988          21 :   return mConnectionClosed;
     989             : }
     990             : 
     991             : bool
     992           0 : Connection::isAsyncExecutionThreadAvailable()
     993             : {
     994           0 :   MOZ_ASSERT(threadOpenedOn == NS_GetCurrentThread());
     995           0 :   return mAsyncExecutionThread && !mAsyncExecutionThreadShuttingDown;
     996             : }
     997             : 
     998             : void
     999           1 : Connection::shutdownAsyncThread() {
    1000           1 :   MOZ_ASSERT(threadOpenedOn == NS_GetCurrentThread());
    1001           1 :   MOZ_ASSERT(mAsyncExecutionThread);
    1002           1 :   MOZ_ASSERT(mAsyncExecutionThreadShuttingDown);
    1003             : 
    1004           1 :   MOZ_ALWAYS_SUCCEEDS(mAsyncExecutionThread->Shutdown());
    1005           1 :   mAsyncExecutionThread = nullptr;
    1006           1 : }
    1007             : 
    1008             : nsresult
    1009           1 : Connection::internalClose(sqlite3 *aNativeConnection)
    1010             : {
    1011             : #ifdef DEBUG
    1012             :   { // Make sure we have marked our async thread as shutting down.
    1013           2 :     MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
    1014           1 :     MOZ_ASSERT(mAsyncExecutionThreadShuttingDown,
    1015             :                "Did not call setClosedState!");
    1016           1 :     MOZ_ASSERT(!isClosed(lockedScope), "Unexpected closed state");
    1017             :   }
    1018             : #endif // DEBUG
    1019             : 
    1020           1 :   if (MOZ_LOG_TEST(gStorageLog, LogLevel::Debug)) {
    1021           0 :     nsAutoCString leafName(":memory");
    1022           0 :     if (mDatabaseFile)
    1023           0 :         (void)mDatabaseFile->GetNativeLeafName(leafName);
    1024           0 :     MOZ_LOG(gStorageLog, LogLevel::Debug, ("Closing connection to '%s'",
    1025             :                                         leafName.get()));
    1026             :   }
    1027             : 
    1028             :   // At this stage, we may still have statements that need to be
    1029             :   // finalized. Attempt to close the database connection. This will
    1030             :   // always disconnect any virtual tables and cleanly finalize their
    1031             :   // internal statements. Once this is done, closing may fail due to
    1032             :   // unfinalized client statements, in which case we need to finalize
    1033             :   // these statements and close again.
    1034             :   {
    1035           2 :     MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
    1036           1 :     mConnectionClosed = true;
    1037             :   }
    1038             : 
    1039             :   // Nothing else needs to be done if we don't have a connection here.
    1040           1 :   if (!aNativeConnection)
    1041           0 :     return NS_OK;
    1042             : 
    1043           1 :   int srv = ::sqlite3_close(aNativeConnection);
    1044             : 
    1045           1 :   if (srv == SQLITE_BUSY) {
    1046             :     {
    1047             :       // Nothing else should change the connection or statements status until we
    1048             :       // are done here.
    1049           0 :       SQLiteMutexAutoLock lockedScope(sharedDBMutex);
    1050             :       // We still have non-finalized statements. Finalize them.
    1051           0 :       sqlite3_stmt *stmt = nullptr;
    1052           0 :       while ((stmt = ::sqlite3_next_stmt(aNativeConnection, stmt))) {
    1053           0 :         MOZ_LOG(gStorageLog, LogLevel::Debug,
    1054             :               ("Auto-finalizing SQL statement '%s' (%p)",
    1055             :                 ::sqlite3_sql(stmt),
    1056             :                 stmt));
    1057             : 
    1058             : #ifdef DEBUG
    1059             :         SmprintfPointer msg = ::mozilla::Smprintf("SQL statement '%s' (%p) should have been finalized before closing the connection",
    1060             :                                           ::sqlite3_sql(stmt),
    1061           0 :                                           stmt);
    1062           0 :         NS_WARNING(msg.get());
    1063             : #endif // DEBUG
    1064             : 
    1065           0 :         srv = ::sqlite3_finalize(stmt);
    1066             : 
    1067             : #ifdef DEBUG
    1068           0 :         if (srv != SQLITE_OK) {
    1069             :           SmprintfPointer msg = ::mozilla::Smprintf("Could not finalize SQL statement (%p)",
    1070           0 :                                               stmt);
    1071           0 :           NS_WARNING(msg.get());
    1072             :         }
    1073             : #endif // DEBUG
    1074             : 
    1075             :         // Ensure that the loop continues properly, whether closing has succeeded
    1076             :         // or not.
    1077           0 :         if (srv == SQLITE_OK) {
    1078           0 :           stmt = nullptr;
    1079             :         }
    1080             :       }
    1081             :       // Scope exiting will unlock the mutex before we invoke sqlite3_close()
    1082             :       // again, since Sqlite will try to acquire it.
    1083             :     }
    1084             : 
    1085             :     // Now that all statements have been finalized, we
    1086             :     // should be able to close.
    1087           0 :     srv = ::sqlite3_close(aNativeConnection);
    1088           0 :     MOZ_ASSERT(false, "Had to forcibly close the database connection because not all the statements have been finalized.");
    1089             :   }
    1090             : 
    1091           1 :   if (srv == SQLITE_OK) {
    1092           1 :     sharedDBMutex.destroy();
    1093             :   } else {
    1094           0 :     MOZ_ASSERT(false,
    1095             :                "sqlite3_close failed. There are probably outstanding statements that are listed above!");
    1096             :   }
    1097             : 
    1098           1 :   return convertResultCode(srv);
    1099             : }
    1100             : 
    1101             : nsCString
    1102           8 : Connection::getFilename()
    1103             : {
    1104           8 :   nsCString leafname(":memory:");
    1105           8 :   if (mDatabaseFile) {
    1106           8 :     (void)mDatabaseFile->GetNativeLeafName(leafname);
    1107             :   }
    1108           8 :   return leafname;
    1109             : }
    1110             : 
    1111             : int
    1112          55 : Connection::stepStatement(sqlite3 *aNativeConnection, sqlite3_stmt *aStatement)
    1113             : {
    1114          55 :   MOZ_ASSERT(aStatement);
    1115          55 :   bool checkedMainThread = false;
    1116          55 :   TimeStamp startTime = TimeStamp::Now();
    1117             : 
    1118             :   // The connection may have been closed if the executing statement has been
    1119             :   // created and cached after a call to asyncClose() but before the actual
    1120             :   // sqlite3_close().  This usually happens when other tasks using cached
    1121             :   // statements are asynchronously scheduled for execution and any of them ends
    1122             :   // up after asyncClose. See bug 728653 for details.
    1123          55 :   if (!isConnectionReadyOnThisThread())
    1124           0 :     return SQLITE_MISUSE;
    1125             : 
    1126          55 :   (void)::sqlite3_extended_result_codes(aNativeConnection, 1);
    1127             : 
    1128             :   int srv;
    1129          55 :   while ((srv = ::sqlite3_step(aStatement)) == SQLITE_LOCKED_SHAREDCACHE) {
    1130           0 :     if (!checkedMainThread) {
    1131           0 :       checkedMainThread = true;
    1132           0 :       if (::NS_IsMainThread()) {
    1133           0 :         NS_WARNING("We won't allow blocking on the main thread!");
    1134           0 :         break;
    1135             :       }
    1136             :     }
    1137             : 
    1138           0 :     srv = WaitForUnlockNotify(aNativeConnection);
    1139           0 :     if (srv != SQLITE_OK) {
    1140           0 :       break;
    1141             :     }
    1142             : 
    1143           0 :     ::sqlite3_reset(aStatement);
    1144             :   }
    1145             : 
    1146             :   // Report very slow SQL statements to Telemetry
    1147          55 :   TimeDuration duration = TimeStamp::Now() - startTime;
    1148             :   const uint32_t threshold =
    1149          55 :     NS_IsMainThread() ? Telemetry::kSlowSQLThresholdForMainThread
    1150          55 :                       : Telemetry::kSlowSQLThresholdForHelperThreads;
    1151          55 :   if (duration.ToMilliseconds() >= threshold) {
    1152           0 :     nsDependentCString statementString(::sqlite3_sql(aStatement));
    1153           0 :     Telemetry::RecordSlowSQLStatement(statementString, mTelemetryFilename,
    1154           0 :                                       duration.ToMilliseconds());
    1155             :   }
    1156             : 
    1157          55 :   (void)::sqlite3_extended_result_codes(aNativeConnection, 0);
    1158             :   // Drop off the extended result bits of the result code.
    1159          55 :   return srv & 0xFF;
    1160             : }
    1161             : 
    1162             : int
    1163          42 : Connection::prepareStatement(sqlite3 *aNativeConnection, const nsCString &aSQL,
    1164             :                              sqlite3_stmt **_stmt)
    1165             : {
    1166             :   // We should not even try to prepare statements after the connection has
    1167             :   // been closed.
    1168          42 :   if (!isConnectionReadyOnThisThread())
    1169           0 :     return SQLITE_MISUSE;
    1170             : 
    1171          42 :   bool checkedMainThread = false;
    1172             : 
    1173          42 :   (void)::sqlite3_extended_result_codes(aNativeConnection, 1);
    1174             : 
    1175             :   int srv;
    1176          42 :   while((srv = ::sqlite3_prepare_v2(aNativeConnection,
    1177             :                                     aSQL.get(),
    1178             :                                     -1,
    1179             :                                     _stmt,
    1180             :                                     nullptr)) == SQLITE_LOCKED_SHAREDCACHE) {
    1181           0 :     if (!checkedMainThread) {
    1182           0 :       checkedMainThread = true;
    1183           0 :       if (::NS_IsMainThread()) {
    1184           0 :         NS_WARNING("We won't allow blocking on the main thread!");
    1185           0 :         break;
    1186             :       }
    1187             :     }
    1188             : 
    1189           0 :     srv = WaitForUnlockNotify(aNativeConnection);
    1190           0 :     if (srv != SQLITE_OK) {
    1191           0 :       break;
    1192             :     }
    1193             :   }
    1194             : 
    1195          42 :   if (srv != SQLITE_OK) {
    1196           0 :     nsCString warnMsg;
    1197           0 :     warnMsg.AppendLiteral("The SQL statement '");
    1198           0 :     warnMsg.Append(aSQL);
    1199           0 :     warnMsg.AppendLiteral("' could not be compiled due to an error: ");
    1200           0 :     warnMsg.Append(::sqlite3_errmsg(aNativeConnection));
    1201             : 
    1202             : #ifdef DEBUG
    1203           0 :     NS_WARNING(warnMsg.get());
    1204             : #endif
    1205           0 :     MOZ_LOG(gStorageLog, LogLevel::Error, ("%s", warnMsg.get()));
    1206             :   }
    1207             : 
    1208          42 :   (void)::sqlite3_extended_result_codes(aNativeConnection, 0);
    1209             :   // Drop off the extended result bits of the result code.
    1210          42 :   int rc = srv & 0xFF;
    1211             :   // sqlite will return OK on a comment only string and set _stmt to nullptr.
    1212             :   // The callers of this function are used to only checking the return value,
    1213             :   // so it is safer to return an error code.
    1214          42 :   if (rc == SQLITE_OK && *_stmt == nullptr) {
    1215           0 :     return SQLITE_MISUSE;
    1216             :   }
    1217             : 
    1218          42 :   return rc;
    1219             : }
    1220             : 
    1221             : 
    1222             : int
    1223          69 : Connection::executeSql(sqlite3 *aNativeConnection, const char *aSqlString)
    1224             : {
    1225          69 :   if (!isConnectionReadyOnThisThread())
    1226           0 :     return SQLITE_MISUSE;
    1227             : 
    1228          69 :   TimeStamp startTime = TimeStamp::Now();
    1229             :   int srv = ::sqlite3_exec(aNativeConnection, aSqlString, nullptr, nullptr,
    1230          69 :                            nullptr);
    1231             : 
    1232             :   // Report very slow SQL statements to Telemetry
    1233          69 :   TimeDuration duration = TimeStamp::Now() - startTime;
    1234             :   const uint32_t threshold =
    1235          69 :     NS_IsMainThread() ? Telemetry::kSlowSQLThresholdForMainThread
    1236          69 :                       : Telemetry::kSlowSQLThresholdForHelperThreads;
    1237          69 :   if (duration.ToMilliseconds() >= threshold) {
    1238           0 :     nsDependentCString statementString(aSqlString);
    1239           0 :     Telemetry::RecordSlowSQLStatement(statementString, mTelemetryFilename,
    1240           0 :                                       duration.ToMilliseconds());
    1241             :   }
    1242             : 
    1243          69 :   return srv;
    1244             : }
    1245             : 
    1246             : ////////////////////////////////////////////////////////////////////////////////
    1247             : //// nsIInterfaceRequestor
    1248             : 
    1249             : NS_IMETHODIMP
    1250           3 : Connection::GetInterface(const nsIID &aIID,
    1251             :                          void **_result)
    1252             : {
    1253           3 :   if (aIID.Equals(NS_GET_IID(nsIEventTarget))) {
    1254           3 :     nsIEventTarget *background = getAsyncExecutionTarget();
    1255           3 :     NS_IF_ADDREF(background);
    1256           3 :     *_result = background;
    1257           3 :     return NS_OK;
    1258             :   }
    1259           0 :   return NS_ERROR_NO_INTERFACE;
    1260             : }
    1261             : 
    1262             : ////////////////////////////////////////////////////////////////////////////////
    1263             : //// mozIStorageConnection
    1264             : 
    1265             : NS_IMETHODIMP
    1266           1 : Connection::Close()
    1267             : {
    1268           1 :   if (!mDBConn)
    1269           1 :     return NS_ERROR_NOT_INITIALIZED;
    1270             : 
    1271             : #ifdef DEBUG
    1272             :   // Since we're accessing mAsyncExecutionThread, we need to be on the opener thread.
    1273             :   // We make this check outside of debug code below in setClosedState, but this is
    1274             :   // here to be explicit.
    1275           0 :   bool onOpenerThread = false;
    1276           0 :   (void)threadOpenedOn->IsOnCurrentThread(&onOpenerThread);
    1277           0 :   MOZ_ASSERT(onOpenerThread);
    1278             : #endif // DEBUG
    1279             : 
    1280             :   // Make sure we have not executed any asynchronous statements.
    1281             :   // If this fails, the mDBConn may be left open, resulting in a leak.
    1282             :   // We'll try to finalize the pending statements and close the connection.
    1283           0 :   if (isAsyncExecutionThreadAvailable()) {
    1284             : #ifdef DEBUG
    1285           0 :     if (NS_IsMainThread()) {
    1286           0 :       nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
    1287           0 :       Unused << xpc->DebugDumpJSStack(false, false, false);
    1288             :     }
    1289             : #endif
    1290           0 :     MOZ_ASSERT(false,
    1291             :                "Close() was invoked on a connection that executed asynchronous statements. "
    1292             :                "Should have used asyncClose().");
    1293             :     // Try to close the database regardless, to free up resources.
    1294             :     Unused << SpinningSynchronousClose();
    1295             :     return NS_ERROR_UNEXPECTED;
    1296             :   }
    1297             : 
    1298             :   // setClosedState nullifies our connection pointer, so we take a raw pointer
    1299             :   // off it, to pass it through the close procedure.
    1300           0 :   sqlite3 *nativeConn = mDBConn;
    1301           0 :   nsresult rv = setClosedState();
    1302           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1303             : 
    1304           0 :   return internalClose(nativeConn);
    1305             : }
    1306             : 
    1307             : NS_IMETHODIMP
    1308           0 : Connection::SpinningSynchronousClose()
    1309             : {
    1310           0 :   if (threadOpenedOn != NS_GetCurrentThread()) {
    1311           0 :     return NS_ERROR_NOT_SAME_THREAD;
    1312             :   }
    1313             : 
    1314             :   // As currently implemented, we can't spin to wait for an existing AsyncClose.
    1315             :   // Our only existing caller will never have called close; assert if misused
    1316             :   // so that no new callers assume this works after an AsyncClose.
    1317           0 :   MOZ_DIAGNOSTIC_ASSERT(connectionReady());
    1318           0 :   if (!connectionReady()) {
    1319           0 :     return NS_ERROR_UNEXPECTED;
    1320             :   }
    1321             : 
    1322           0 :   RefPtr<CloseListener> listener = new CloseListener();
    1323           0 :   nsresult rv = AsyncClose(listener);
    1324           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1325           0 :   MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() {
    1326             :     return listener->mClosed;
    1327             :   }));
    1328           0 :   MOZ_ASSERT(isClosed(), "The connection should be closed at this point");
    1329             : 
    1330           0 :   return rv;
    1331             : }
    1332             : 
    1333             : NS_IMETHODIMP
    1334           1 : Connection::AsyncClose(mozIStorageCompletionCallback *aCallback)
    1335             : {
    1336           1 :   NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_SAME_THREAD);
    1337             :   // Check if AsyncClose or Close were already invoked.
    1338           1 :   if (!mDBConn) {
    1339           0 :     return NS_ERROR_NOT_INITIALIZED;
    1340             :   }
    1341             : 
    1342             :   // The two relevant factors at this point are whether we have a database
    1343             :   // connection and whether we have an async execution thread.  Here's what the
    1344             :   // states mean and how we handle them:
    1345             :   //
    1346             :   // - (mDBConn && asyncThread): The expected case where we are either an
    1347             :   //   async connection or a sync connection that has been used asynchronously.
    1348             :   //   Either way the caller must call us and not Close().  Nothing surprising
    1349             :   //   about this.  We'll dispatch AsyncCloseConnection to the already-existing
    1350             :   //   async thread.
    1351             :   //
    1352             :   // - (mDBConn && !asyncThread): A somewhat unusual case where the caller
    1353             :   //   opened the connection synchronously and was planning to use it
    1354             :   //   asynchronously, but never got around to using it asynchronously before
    1355             :   //   needing to shutdown.  This has been observed to happen for the cookie
    1356             :   //   service in a case where Firefox shuts itself down almost immediately
    1357             :   //   after startup (for unknown reasons).  In the Firefox shutdown case,
    1358             :   //   we may also fail to create a new async execution thread if one does not
    1359             :   //   already exist.  (nsThreadManager will refuse to create new threads when
    1360             :   //   it has already been told to shutdown.)  As such, we need to handle a
    1361             :   //   failure to create the async execution thread by falling back to
    1362             :   //   synchronous Close() and also dispatching the completion callback because
    1363             :   //   at least Places likes to spin a nested event loop that depends on the
    1364             :   //   callback being invoked.
    1365             :   //
    1366             :   //   Note that we have considered not trying to spin up the async execution
    1367             :   //   thread in this case if it does not already exist, but the overhead of
    1368             :   //   thread startup (if successful) is significantly less expensive than the
    1369             :   //   worst-case potential I/O hit of synchronously closing a database when we
    1370             :   //   could close it asynchronously.
    1371             :   //
    1372             :   // - (!mDBConn && asyncThread): This happens in some but not all cases where
    1373             :   //   OpenAsyncDatabase encountered a problem opening the database.  If it
    1374             :   //   happened in all cases AsyncInitDatabase would just shut down the thread
    1375             :   //   directly and we would avoid this case.  But it doesn't, so for simplicity
    1376             :   //   and consistency AsyncCloseConnection knows how to handle this and we
    1377             :   //   act like this was the (mDBConn && asyncThread) case in this method.
    1378             :   //
    1379             :   // - (!mDBConn && !asyncThread): The database was never successfully opened or
    1380             :   //   Close() or AsyncClose() has already been called (at least) once.  This is
    1381             :   //   undeniably a misuse case by the caller.  We could optimize for this
    1382             :   //   case by adding an additional check of mAsyncExecutionThread without using
    1383             :   //   getAsyncExecutionTarget() to avoid wastefully creating a thread just to
    1384             :   //   shut it down.  But this complicates the method for broken caller code
    1385             :   //   whereas we're still correct and safe without the special-case.
    1386           1 :   nsIEventTarget *asyncThread = getAsyncExecutionTarget();
    1387             : 
    1388             :   // Create our callback event if we were given a callback.  This will
    1389             :   // eventually be dispatched in all cases, even if we fall back to Close() and
    1390             :   // the database wasn't open and we return an error.  The rationale is that
    1391             :   // no existing consumer checks our return value and several of them like to
    1392             :   // spin nested event loops until the callback fires.  Given that, it seems
    1393             :   // preferable for us to dispatch the callback in all cases.  (Except the
    1394             :   // wrong thread misuse case we bailed on up above.  But that's okay because
    1395             :   // that is statically wrong whereas these edge cases are dynamic.)
    1396           2 :   nsCOMPtr<nsIRunnable> completeEvent;
    1397           1 :   if (aCallback) {
    1398           0 :     completeEvent = newCompletionEvent(aCallback);
    1399             :   }
    1400             : 
    1401           1 :   if (!asyncThread) {
    1402             :     // We were unable to create an async thread, so we need to fall back to
    1403             :     // using normal Close().  Since there is no async thread, Close() will
    1404             :     // not complain about that.  (Close() may, however, complain if the
    1405             :     // connection is closed, but that's okay.)
    1406           0 :     if (completeEvent) {
    1407             :       // Closing the database is more important than returning an error code
    1408             :       // about a failure to dispatch, especially because all existing native
    1409             :       // callers ignore our return value.
    1410           0 :       Unused << NS_DispatchToMainThread(completeEvent.forget());
    1411             :     }
    1412           0 :     MOZ_ALWAYS_SUCCEEDS(Close());
    1413             :     // Return a success inconditionally here, since Close() is unlikely to fail
    1414             :     // and we want to reassure the consumer that its callback will be invoked.
    1415           0 :     return NS_OK;
    1416             :   }
    1417             : 
    1418             :   // setClosedState nullifies our connection pointer, so we take a raw pointer
    1419             :   // off it, to pass it through the close procedure.
    1420           1 :   sqlite3 *nativeConn = mDBConn;
    1421           1 :   nsresult rv = setClosedState();
    1422           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1423             : 
    1424             :   // Create and dispatch our close event to the background thread.
    1425             :   nsCOMPtr<nsIRunnable> closeEvent = new AsyncCloseConnection(this,
    1426             :                                                               nativeConn,
    1427           3 :                                                               completeEvent);
    1428           1 :   rv = asyncThread->Dispatch(closeEvent, NS_DISPATCH_NORMAL);
    1429           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1430             : 
    1431           1 :   return NS_OK;
    1432             : }
    1433             : 
    1434             : NS_IMETHODIMP
    1435           1 : Connection::AsyncClone(bool aReadOnly,
    1436             :                        mozIStorageCompletionCallback *aCallback)
    1437             : {
    1438           2 :   AUTO_PROFILER_LABEL("Connection::AsyncClone", STORAGE);
    1439             : 
    1440           1 :   NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_SAME_THREAD);
    1441           1 :   if (!mDBConn)
    1442           0 :     return NS_ERROR_NOT_INITIALIZED;
    1443           1 :   if (!mDatabaseFile)
    1444           0 :     return NS_ERROR_UNEXPECTED;
    1445             : 
    1446           1 :   int flags = mFlags;
    1447           1 :   if (aReadOnly) {
    1448             :     // Turn off SQLITE_OPEN_READWRITE, and set SQLITE_OPEN_READONLY.
    1449           1 :     flags = (~SQLITE_OPEN_READWRITE & flags) | SQLITE_OPEN_READONLY;
    1450             :     // Turn off SQLITE_OPEN_CREATE.
    1451           1 :     flags = (~SQLITE_OPEN_CREATE & flags);
    1452             :   }
    1453             : 
    1454             :   RefPtr<Connection> clone = new Connection(mStorageService, flags,
    1455           3 :                                               mAsyncOnly);
    1456             : 
    1457             :   RefPtr<AsyncInitializeClone> initEvent =
    1458           3 :     new AsyncInitializeClone(this, clone, aReadOnly, aCallback);
    1459             :   // Dispatch to our async thread, since the originating connection must remain
    1460             :   // valid and open for the whole cloning process.  This also ensures we are
    1461             :   // properly serialized with a `close` operation, rather than race with it.
    1462           2 :   nsCOMPtr<nsIEventTarget> target = getAsyncExecutionTarget();
    1463           1 :   if (!target) {
    1464           0 :     return NS_ERROR_UNEXPECTED;
    1465             :   }
    1466           1 :   return target->Dispatch(initEvent, NS_DISPATCH_NORMAL);
    1467             : }
    1468             : 
    1469             : nsresult
    1470           2 : Connection::initializeClone(Connection* aClone, bool aReadOnly)
    1471             : {
    1472           4 :   nsresult rv = mFileURL ? aClone->initialize(mFileURL)
    1473           6 :                          : aClone->initialize(mDatabaseFile);
    1474           2 :   if (NS_FAILED(rv)) {
    1475           0 :     return rv;
    1476             :   }
    1477             : 
    1478             :   // Re-attach on-disk databases that were attached to the original connection.
    1479             :   {
    1480           4 :     nsCOMPtr<mozIStorageStatement> stmt;
    1481           6 :     rv = CreateStatement(NS_LITERAL_CSTRING("PRAGMA database_list"),
    1482           8 :                          getter_AddRefs(stmt));
    1483           2 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    1484           2 :     bool hasResult = false;
    1485          12 :     while (stmt && NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
    1486          10 :       nsAutoCString name;
    1487           5 :       rv = stmt->GetUTF8String(1, name);
    1488          16 :       if (NS_SUCCEEDED(rv) && !name.Equals(NS_LITERAL_CSTRING("main")) &&
    1489          11 :                               !name.Equals(NS_LITERAL_CSTRING("temp"))) {
    1490           2 :         nsCString path;
    1491           1 :         rv = stmt->GetUTF8String(2, path);
    1492           1 :         if (NS_SUCCEEDED(rv) && !path.IsEmpty()) {
    1493           5 :           rv = aClone->ExecuteSimpleSQL(NS_LITERAL_CSTRING("ATTACH DATABASE '") +
    1494           6 :             path + NS_LITERAL_CSTRING("' AS ") + name);
    1495           1 :           MOZ_ASSERT(NS_SUCCEEDED(rv), "couldn't re-attach database to cloned connection");
    1496             :         }
    1497             :       }
    1498             :     }
    1499             :   }
    1500             : 
    1501             :   // Copy over pragmas from the original connection.
    1502             :   static const char * pragmas[] = {
    1503             :     "cache_size",
    1504             :     "temp_store",
    1505             :     "foreign_keys",
    1506             :     "journal_size_limit",
    1507             :     "synchronous",
    1508             :     "wal_autocheckpoint",
    1509             :     "busy_timeout"
    1510             :   };
    1511          16 :   for (auto& pragma : pragmas) {
    1512             :     // Read-only connections just need cache_size and temp_store pragmas.
    1513          26 :     if (aReadOnly && ::strcmp(pragma, "cache_size") != 0 &&
    1514          12 :                      ::strcmp(pragma, "temp_store") != 0) {
    1515          10 :       continue;
    1516             :     }
    1517             : 
    1518           8 :     nsAutoCString pragmaQuery("PRAGMA ");
    1519           4 :     pragmaQuery.Append(pragma);
    1520           8 :     nsCOMPtr<mozIStorageStatement> stmt;
    1521           4 :     rv = CreateStatement(pragmaQuery, getter_AddRefs(stmt));
    1522           4 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    1523           4 :     bool hasResult = false;
    1524           4 :     if (stmt && NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
    1525           4 :       pragmaQuery.AppendLiteral(" = ");
    1526           4 :       pragmaQuery.AppendInt(stmt->AsInt32(0));
    1527           4 :       rv = aClone->ExecuteSimpleSQL(pragmaQuery);
    1528           4 :       MOZ_ASSERT(NS_SUCCEEDED(rv));
    1529             :     }
    1530             :   }
    1531             : 
    1532             :   // Copy any functions that have been added to this connection.
    1533           4 :   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
    1534          10 :   for (auto iter = mFunctions.Iter(); !iter.Done(); iter.Next()) {
    1535           8 :     const nsACString &key = iter.Key();
    1536          16 :     Connection::FunctionInfo data = iter.UserData();
    1537             : 
    1538           8 :     MOZ_ASSERT(data.type == Connection::FunctionInfo::SIMPLE ||
    1539             :                data.type == Connection::FunctionInfo::AGGREGATE,
    1540             :                "Invalid function type!");
    1541             : 
    1542           8 :     if (data.type == Connection::FunctionInfo::SIMPLE) {
    1543             :       mozIStorageFunction *function =
    1544           8 :         static_cast<mozIStorageFunction *>(data.function.get());
    1545           8 :       rv = aClone->CreateFunction(key, data.numArgs, function);
    1546           8 :       if (NS_FAILED(rv)) {
    1547           0 :         NS_WARNING("Failed to copy function to cloned connection");
    1548             :       }
    1549             : 
    1550             :     } else {
    1551             :       mozIStorageAggregateFunction *function =
    1552           0 :         static_cast<mozIStorageAggregateFunction *>(data.function.get());
    1553           0 :       rv = aClone->CreateAggregateFunction(key, data.numArgs, function);
    1554           0 :       if (NS_FAILED(rv)) {
    1555           0 :         NS_WARNING("Failed to copy aggregate function to cloned connection");
    1556             :       }
    1557             :     }
    1558             :   }
    1559             : 
    1560           2 :   return NS_OK;
    1561             : }
    1562             : 
    1563             : NS_IMETHODIMP
    1564           1 : Connection::Clone(bool aReadOnly,
    1565             :                   mozIStorageConnection **_connection)
    1566             : {
    1567           1 :   MOZ_ASSERT(threadOpenedOn == NS_GetCurrentThread());
    1568             : 
    1569           2 :   AUTO_PROFILER_LABEL("Connection::Clone", STORAGE);
    1570             : 
    1571           1 :   if (!mDBConn)
    1572           0 :     return NS_ERROR_NOT_INITIALIZED;
    1573           1 :   if (!mDatabaseFile)
    1574           0 :     return NS_ERROR_UNEXPECTED;
    1575             : 
    1576           1 :   int flags = mFlags;
    1577           1 :   if (aReadOnly) {
    1578             :     // Turn off SQLITE_OPEN_READWRITE, and set SQLITE_OPEN_READONLY.
    1579           1 :     flags = (~SQLITE_OPEN_READWRITE & flags) | SQLITE_OPEN_READONLY;
    1580             :     // Turn off SQLITE_OPEN_CREATE.
    1581           1 :     flags = (~SQLITE_OPEN_CREATE & flags);
    1582             :   }
    1583             : 
    1584             :   RefPtr<Connection> clone = new Connection(mStorageService, flags,
    1585           3 :                                               mAsyncOnly);
    1586             : 
    1587           1 :   nsresult rv = initializeClone(clone, aReadOnly);
    1588           1 :   if (NS_FAILED(rv)) {
    1589           0 :     return rv;
    1590             :   }
    1591             : 
    1592           1 :   NS_IF_ADDREF(*_connection = clone);
    1593           1 :   return NS_OK;
    1594             : }
    1595             : 
    1596             : NS_IMETHODIMP
    1597           0 : Connection::GetDefaultPageSize(int32_t *_defaultPageSize)
    1598             : {
    1599           0 :   *_defaultPageSize = Service::getDefaultPageSize();
    1600           0 :   return NS_OK;
    1601             : }
    1602             : 
    1603             : NS_IMETHODIMP
    1604           1 : Connection::GetConnectionReady(bool *_ready)
    1605             : {
    1606           1 :   MOZ_ASSERT(threadOpenedOn == NS_GetCurrentThread());
    1607           1 :   *_ready = connectionReady();
    1608           1 :   return NS_OK;
    1609             : }
    1610             : 
    1611             : NS_IMETHODIMP
    1612           3 : Connection::GetDatabaseFile(nsIFile **_dbFile)
    1613             : {
    1614           3 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1615             : 
    1616           3 :   NS_IF_ADDREF(*_dbFile = mDatabaseFile);
    1617             : 
    1618           3 :   return NS_OK;
    1619             : }
    1620             : 
    1621             : NS_IMETHODIMP
    1622           0 : Connection::GetLastInsertRowID(int64_t *_id)
    1623             : {
    1624           0 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1625             : 
    1626           0 :   sqlite_int64 id = ::sqlite3_last_insert_rowid(mDBConn);
    1627           0 :   *_id = id;
    1628             : 
    1629           0 :   return NS_OK;
    1630             : }
    1631             : 
    1632             : NS_IMETHODIMP
    1633           0 : Connection::GetAffectedRows(int32_t *_rows)
    1634             : {
    1635           0 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1636             : 
    1637           0 :   *_rows = ::sqlite3_changes(mDBConn);
    1638             : 
    1639           0 :   return NS_OK;
    1640             : }
    1641             : 
    1642             : NS_IMETHODIMP
    1643           0 : Connection::GetLastError(int32_t *_error)
    1644             : {
    1645           0 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1646             : 
    1647           0 :   *_error = ::sqlite3_errcode(mDBConn);
    1648             : 
    1649           0 :   return NS_OK;
    1650             : }
    1651             : 
    1652             : NS_IMETHODIMP
    1653           0 : Connection::GetLastErrorString(nsACString &_errorString)
    1654             : {
    1655           0 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1656             : 
    1657           0 :   const char *serr = ::sqlite3_errmsg(mDBConn);
    1658           0 :   _errorString.Assign(serr);
    1659             : 
    1660           0 :   return NS_OK;
    1661             : }
    1662             : 
    1663             : NS_IMETHODIMP
    1664           5 : Connection::GetSchemaVersion(int32_t *_version)
    1665             : {
    1666           5 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1667             : 
    1668          10 :   nsCOMPtr<mozIStorageStatement> stmt;
    1669          15 :   (void)CreateStatement(NS_LITERAL_CSTRING("PRAGMA user_version"),
    1670          20 :                         getter_AddRefs(stmt));
    1671           5 :   NS_ENSURE_TRUE(stmt, NS_ERROR_OUT_OF_MEMORY);
    1672             : 
    1673           5 :   *_version = 0;
    1674             :   bool hasResult;
    1675           5 :   if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult)
    1676           5 :     *_version = stmt->AsInt32(0);
    1677             : 
    1678           5 :   return NS_OK;
    1679             : }
    1680             : 
    1681             : NS_IMETHODIMP
    1682           0 : Connection::SetSchemaVersion(int32_t aVersion)
    1683             : {
    1684           0 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1685             : 
    1686           0 :   nsAutoCString stmt(NS_LITERAL_CSTRING("PRAGMA user_version = "));
    1687           0 :   stmt.AppendInt(aVersion);
    1688             : 
    1689           0 :   return ExecuteSimpleSQL(stmt);
    1690             : }
    1691             : 
    1692             : NS_IMETHODIMP
    1693          29 : Connection::CreateStatement(const nsACString &aSQLStatement,
    1694             :                             mozIStorageStatement **_stmt)
    1695             : {
    1696          29 :   NS_ENSURE_ARG_POINTER(_stmt);
    1697          29 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1698             : 
    1699          58 :   RefPtr<Statement> statement(new Statement());
    1700          29 :   NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
    1701             : 
    1702          29 :   nsresult rv = statement->initialize(this, mDBConn, aSQLStatement);
    1703          29 :   NS_ENSURE_SUCCESS(rv, rv);
    1704             : 
    1705             :   Statement *rawPtr;
    1706          29 :   statement.forget(&rawPtr);
    1707          29 :   *_stmt = rawPtr;
    1708          29 :   return NS_OK;
    1709             : }
    1710             : 
    1711             : NS_IMETHODIMP
    1712          16 : Connection::CreateAsyncStatement(const nsACString &aSQLStatement,
    1713             :                                  mozIStorageAsyncStatement **_stmt)
    1714             : {
    1715          16 :   NS_ENSURE_ARG_POINTER(_stmt);
    1716          16 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1717             : 
    1718          32 :   RefPtr<AsyncStatement> statement(new AsyncStatement());
    1719          16 :   NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
    1720             : 
    1721          16 :   nsresult rv = statement->initialize(this, mDBConn, aSQLStatement);
    1722          16 :   NS_ENSURE_SUCCESS(rv, rv);
    1723             : 
    1724             :   AsyncStatement *rawPtr;
    1725          16 :   statement.forget(&rawPtr);
    1726          16 :   *_stmt = rawPtr;
    1727          16 :   return NS_OK;
    1728             : }
    1729             : 
    1730             : NS_IMETHODIMP
    1731          53 : Connection::ExecuteSimpleSQL(const nsACString &aSQLStatement)
    1732             : {
    1733          53 :   CHECK_MAINTHREAD_ABUSE();
    1734          53 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1735             : 
    1736          53 :   int srv = executeSql(mDBConn, PromiseFlatCString(aSQLStatement).get());
    1737          53 :   return convertResultCode(srv);
    1738             : }
    1739             : 
    1740             : NS_IMETHODIMP
    1741           3 : Connection::ExecuteAsync(mozIStorageBaseStatement **aStatements,
    1742             :                          uint32_t aNumStatements,
    1743             :                          mozIStorageStatementCallback *aCallback,
    1744             :                          mozIStoragePendingStatement **_handle)
    1745             : {
    1746           6 :   nsTArray<StatementData> stmts(aNumStatements);
    1747           6 :   for (uint32_t i = 0; i < aNumStatements; i++) {
    1748             :     nsCOMPtr<StorageBaseStatementInternal> stmt =
    1749           6 :       do_QueryInterface(aStatements[i]);
    1750             : 
    1751             :     // Obtain our StatementData.
    1752           6 :     StatementData data;
    1753           3 :     nsresult rv = stmt->getAsynchronousStatementData(data);
    1754           3 :     NS_ENSURE_SUCCESS(rv, rv);
    1755             : 
    1756           3 :     NS_ASSERTION(stmt->getOwner() == this,
    1757             :                  "Statement must be from this database connection!");
    1758             : 
    1759             :     // Now append it to our array.
    1760           3 :     NS_ENSURE_TRUE(stmts.AppendElement(data), NS_ERROR_OUT_OF_MEMORY);
    1761             :   }
    1762             : 
    1763             :   // Dispatch to the background
    1764           3 :   return AsyncExecuteStatements::execute(stmts, this, mDBConn, aCallback,
    1765           3 :                                          _handle);
    1766             : }
    1767             : 
    1768             : NS_IMETHODIMP
    1769           0 : Connection::ExecuteSimpleSQLAsync(const nsACString &aSQLStatement,
    1770             :                                   mozIStorageStatementCallback *aCallback,
    1771             :                                   mozIStoragePendingStatement **_handle)
    1772             : {
    1773           0 :   NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_SAME_THREAD);
    1774             : 
    1775           0 :   nsCOMPtr<mozIStorageAsyncStatement> stmt;
    1776           0 :   nsresult rv = CreateAsyncStatement(aSQLStatement, getter_AddRefs(stmt));
    1777           0 :   if (NS_FAILED(rv)) {
    1778           0 :     return rv;
    1779             :   }
    1780             : 
    1781           0 :   nsCOMPtr<mozIStoragePendingStatement> pendingStatement;
    1782           0 :   rv = stmt->ExecuteAsync(aCallback, getter_AddRefs(pendingStatement));
    1783           0 :   if (NS_FAILED(rv)) {
    1784           0 :     return rv;
    1785             :   }
    1786             : 
    1787           0 :   pendingStatement.forget(_handle);
    1788           0 :   return rv;
    1789             : }
    1790             : 
    1791             : NS_IMETHODIMP
    1792           2 : Connection::TableExists(const nsACString &aTableName,
    1793             :                         bool *_exists)
    1794             : {
    1795           2 :     return databaseElementExists(TABLE, aTableName, _exists);
    1796             : }
    1797             : 
    1798             : NS_IMETHODIMP
    1799           1 : Connection::IndexExists(const nsACString &aIndexName,
    1800             :                         bool* _exists)
    1801             : {
    1802           1 :     return databaseElementExists(INDEX, aIndexName, _exists);
    1803             : }
    1804             : 
    1805             : NS_IMETHODIMP
    1806           0 : Connection::GetTransactionInProgress(bool *_inProgress)
    1807             : {
    1808           0 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1809             : 
    1810           0 :   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
    1811           0 :   *_inProgress = mTransactionInProgress;
    1812           0 :   return NS_OK;
    1813             : }
    1814             : 
    1815             : NS_IMETHODIMP
    1816           0 : Connection::BeginTransaction()
    1817             : {
    1818           0 :   return BeginTransactionAs(mozIStorageConnection::TRANSACTION_DEFERRED);
    1819             : }
    1820             : 
    1821             : NS_IMETHODIMP
    1822           0 : Connection::BeginTransactionAs(int32_t aTransactionType)
    1823             : {
    1824           0 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1825             : 
    1826           0 :   return beginTransactionInternal(mDBConn, aTransactionType);
    1827             : }
    1828             : 
    1829             : nsresult
    1830           0 : Connection::beginTransactionInternal(sqlite3 *aNativeConnection,
    1831             :                                      int32_t aTransactionType)
    1832             : {
    1833           0 :   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
    1834           0 :   if (mTransactionInProgress)
    1835           0 :     return NS_ERROR_FAILURE;
    1836             :   nsresult rv;
    1837           0 :   switch(aTransactionType) {
    1838             :     case TRANSACTION_DEFERRED:
    1839           0 :       rv = convertResultCode(executeSql(aNativeConnection, "BEGIN DEFERRED"));
    1840           0 :       break;
    1841             :     case TRANSACTION_IMMEDIATE:
    1842           0 :       rv = convertResultCode(executeSql(aNativeConnection, "BEGIN IMMEDIATE"));
    1843           0 :       break;
    1844             :     case TRANSACTION_EXCLUSIVE:
    1845           0 :       rv = convertResultCode(executeSql(aNativeConnection, "BEGIN EXCLUSIVE"));
    1846           0 :       break;
    1847             :     default:
    1848           0 :       return NS_ERROR_ILLEGAL_VALUE;
    1849             :   }
    1850           0 :   if (NS_SUCCEEDED(rv))
    1851           0 :     mTransactionInProgress = true;
    1852           0 :   return rv;
    1853             : }
    1854             : 
    1855             : NS_IMETHODIMP
    1856           0 : Connection::CommitTransaction()
    1857             : {
    1858           0 :   if (!mDBConn)
    1859           0 :     return NS_ERROR_NOT_INITIALIZED;
    1860             : 
    1861           0 :   return commitTransactionInternal(mDBConn);
    1862             : }
    1863             : 
    1864             : nsresult
    1865           0 : Connection::commitTransactionInternal(sqlite3 *aNativeConnection)
    1866             : {
    1867           0 :   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
    1868           0 :   if (!mTransactionInProgress)
    1869           0 :     return NS_ERROR_UNEXPECTED;
    1870             :   nsresult rv =
    1871           0 :     convertResultCode(executeSql(aNativeConnection, "COMMIT TRANSACTION"));
    1872           0 :   if (NS_SUCCEEDED(rv))
    1873           0 :     mTransactionInProgress = false;
    1874           0 :   return rv;
    1875             : }
    1876             : 
    1877             : NS_IMETHODIMP
    1878           0 : Connection::RollbackTransaction()
    1879             : {
    1880           0 :   if (!mDBConn)
    1881           0 :     return NS_ERROR_NOT_INITIALIZED;
    1882             : 
    1883           0 :   return rollbackTransactionInternal(mDBConn);
    1884             : }
    1885             : 
    1886             : nsresult
    1887           0 : Connection::rollbackTransactionInternal(sqlite3 *aNativeConnection)
    1888             : {
    1889           0 :   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
    1890           0 :   if (!mTransactionInProgress)
    1891           0 :     return NS_ERROR_UNEXPECTED;
    1892             : 
    1893             :   nsresult rv =
    1894           0 :     convertResultCode(executeSql(aNativeConnection, "ROLLBACK TRANSACTION"));
    1895           0 :   if (NS_SUCCEEDED(rv))
    1896           0 :     mTransactionInProgress = false;
    1897           0 :   return rv;
    1898             : }
    1899             : 
    1900             : NS_IMETHODIMP
    1901           0 : Connection::CreateTable(const char *aTableName,
    1902             :                         const char *aTableSchema)
    1903             : {
    1904           0 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1905             : 
    1906           0 :   SmprintfPointer buf = ::mozilla::Smprintf("CREATE TABLE %s (%s)", aTableName, aTableSchema);
    1907           0 :   if (!buf)
    1908           0 :     return NS_ERROR_OUT_OF_MEMORY;
    1909             : 
    1910           0 :   int srv = executeSql(mDBConn, buf.get());
    1911             : 
    1912           0 :   return convertResultCode(srv);
    1913             : }
    1914             : 
    1915             : NS_IMETHODIMP
    1916          16 : Connection::CreateFunction(const nsACString &aFunctionName,
    1917             :                            int32_t aNumArguments,
    1918             :                            mozIStorageFunction *aFunction)
    1919             : {
    1920          16 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1921             : 
    1922             :   // Check to see if this function is already defined.  We only check the name
    1923             :   // because a function can be defined with the same body but different names.
    1924          32 :   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
    1925          16 :   NS_ENSURE_FALSE(mFunctions.Get(aFunctionName, nullptr), NS_ERROR_FAILURE);
    1926             : 
    1927          16 :   int srv = ::sqlite3_create_function(mDBConn,
    1928          32 :                                       nsPromiseFlatCString(aFunctionName).get(),
    1929             :                                       aNumArguments,
    1930             :                                       SQLITE_ANY,
    1931             :                                       aFunction,
    1932             :                                       basicFunctionHelper,
    1933             :                                       nullptr,
    1934          16 :                                       nullptr);
    1935          16 :   if (srv != SQLITE_OK)
    1936           0 :     return convertResultCode(srv);
    1937             : 
    1938             :   FunctionInfo info = { aFunction,
    1939             :                         Connection::FunctionInfo::SIMPLE,
    1940          32 :                         aNumArguments };
    1941          16 :   mFunctions.Put(aFunctionName, info);
    1942             : 
    1943          16 :   return NS_OK;
    1944             : }
    1945             : 
    1946             : NS_IMETHODIMP
    1947           0 : Connection::CreateAggregateFunction(const nsACString &aFunctionName,
    1948             :                                     int32_t aNumArguments,
    1949             :                                     mozIStorageAggregateFunction *aFunction)
    1950             : {
    1951           0 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1952             : 
    1953             :   // Check to see if this function name is already defined.
    1954           0 :   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
    1955           0 :   NS_ENSURE_FALSE(mFunctions.Get(aFunctionName, nullptr), NS_ERROR_FAILURE);
    1956             : 
    1957             :   // Because aggregate functions depend on state across calls, you cannot have
    1958             :   // the same instance use the same name.  We want to enumerate all functions
    1959             :   // and make sure this instance is not already registered.
    1960           0 :   NS_ENSURE_FALSE(findFunctionByInstance(aFunction), NS_ERROR_FAILURE);
    1961             : 
    1962           0 :   int srv = ::sqlite3_create_function(mDBConn,
    1963           0 :                                       nsPromiseFlatCString(aFunctionName).get(),
    1964             :                                       aNumArguments,
    1965             :                                       SQLITE_ANY,
    1966             :                                       aFunction,
    1967             :                                       nullptr,
    1968             :                                       aggregateFunctionStepHelper,
    1969           0 :                                       aggregateFunctionFinalHelper);
    1970           0 :   if (srv != SQLITE_OK)
    1971           0 :     return convertResultCode(srv);
    1972             : 
    1973             :   FunctionInfo info = { aFunction,
    1974             :                         Connection::FunctionInfo::AGGREGATE,
    1975           0 :                         aNumArguments };
    1976           0 :   mFunctions.Put(aFunctionName, info);
    1977             : 
    1978           0 :   return NS_OK;
    1979             : }
    1980             : 
    1981             : NS_IMETHODIMP
    1982           0 : Connection::RemoveFunction(const nsACString &aFunctionName)
    1983             : {
    1984           0 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1985             : 
    1986           0 :   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
    1987           0 :   NS_ENSURE_TRUE(mFunctions.Get(aFunctionName, nullptr), NS_ERROR_FAILURE);
    1988             : 
    1989           0 :   int srv = ::sqlite3_create_function(mDBConn,
    1990           0 :                                       nsPromiseFlatCString(aFunctionName).get(),
    1991             :                                       0,
    1992             :                                       SQLITE_ANY,
    1993             :                                       nullptr,
    1994             :                                       nullptr,
    1995             :                                       nullptr,
    1996           0 :                                       nullptr);
    1997           0 :   if (srv != SQLITE_OK)
    1998           0 :     return convertResultCode(srv);
    1999             : 
    2000           0 :   mFunctions.Remove(aFunctionName);
    2001             : 
    2002           0 :   return NS_OK;
    2003             : }
    2004             : 
    2005             : NS_IMETHODIMP
    2006           0 : Connection::SetProgressHandler(int32_t aGranularity,
    2007             :                                mozIStorageProgressHandler *aHandler,
    2008             :                                mozIStorageProgressHandler **_oldHandler)
    2009             : {
    2010           0 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    2011             : 
    2012             :   // Return previous one
    2013           0 :   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
    2014           0 :   NS_IF_ADDREF(*_oldHandler = mProgressHandler);
    2015             : 
    2016           0 :   if (!aHandler || aGranularity <= 0) {
    2017           0 :     aHandler = nullptr;
    2018           0 :     aGranularity = 0;
    2019             :   }
    2020           0 :   mProgressHandler = aHandler;
    2021           0 :   ::sqlite3_progress_handler(mDBConn, aGranularity, sProgressHelper, this);
    2022             : 
    2023           0 :   return NS_OK;
    2024             : }
    2025             : 
    2026             : NS_IMETHODIMP
    2027           0 : Connection::RemoveProgressHandler(mozIStorageProgressHandler **_oldHandler)
    2028             : {
    2029           0 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    2030             : 
    2031             :   // Return previous one
    2032           0 :   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
    2033           0 :   NS_IF_ADDREF(*_oldHandler = mProgressHandler);
    2034             : 
    2035           0 :   mProgressHandler = nullptr;
    2036           0 :   ::sqlite3_progress_handler(mDBConn, 0, nullptr, nullptr);
    2037             : 
    2038           0 :   return NS_OK;
    2039             : }
    2040             : 
    2041             : NS_IMETHODIMP
    2042           2 : Connection::SetGrowthIncrement(int32_t aChunkSize, const nsACString &aDatabaseName)
    2043             : {
    2044             :   // Bug 597215: Disk space is extremely limited on Android
    2045             :   // so don't preallocate space. This is also not effective
    2046             :   // on log structured file systems used by Android devices
    2047             : #if !defined(ANDROID) && !defined(MOZ_PLATFORM_MAEMO)
    2048             :   // Don't preallocate if less than 500MiB is available.
    2049             :   int64_t bytesAvailable;
    2050           2 :   nsresult rv = mDatabaseFile->GetDiskSpaceAvailable(&bytesAvailable);
    2051           2 :   NS_ENSURE_SUCCESS(rv, rv);
    2052           2 :   if (bytesAvailable < MIN_AVAILABLE_BYTES_PER_CHUNKED_GROWTH) {
    2053           0 :     return NS_ERROR_FILE_TOO_BIG;
    2054             :   }
    2055             : 
    2056           2 :   (void)::sqlite3_file_control(mDBConn,
    2057           4 :                                aDatabaseName.Length() ? nsPromiseFlatCString(aDatabaseName).get()
    2058             :                                                       : nullptr,
    2059             :                                SQLITE_FCNTL_CHUNK_SIZE,
    2060           4 :                                &aChunkSize);
    2061             : #endif
    2062           2 :   return NS_OK;
    2063             : }
    2064             : 
    2065             : NS_IMETHODIMP
    2066           0 : Connection::EnableModule(const nsACString& aModuleName)
    2067             : {
    2068           0 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    2069             : 
    2070           0 :   for (auto& gModule : gModules) {
    2071           0 :     struct Module* m = &gModule;
    2072           0 :     if (aModuleName.Equals(m->name)) {
    2073           0 :       int srv = m->registerFunc(mDBConn, m->name);
    2074           0 :       if (srv != SQLITE_OK)
    2075           0 :         return convertResultCode(srv);
    2076             : 
    2077           0 :       return NS_OK;
    2078             :     }
    2079             :   }
    2080             : 
    2081           0 :   return NS_ERROR_FAILURE;
    2082             : }
    2083             : 
    2084             : // Implemented in TelemetryVFS.cpp
    2085             : already_AddRefed<QuotaObject>
    2086             : GetQuotaObjectForFile(sqlite3_file *pFile);
    2087             : 
    2088             : NS_IMETHODIMP
    2089           0 : Connection::GetQuotaObjects(QuotaObject** aDatabaseQuotaObject,
    2090             :                             QuotaObject** aJournalQuotaObject)
    2091             : {
    2092           0 :   MOZ_ASSERT(aDatabaseQuotaObject);
    2093           0 :   MOZ_ASSERT(aJournalQuotaObject);
    2094             : 
    2095           0 :   if (!mDBConn) {
    2096           0 :     return NS_ERROR_NOT_INITIALIZED;
    2097             :   }
    2098             : 
    2099             :   sqlite3_file* file;
    2100           0 :   int srv = ::sqlite3_file_control(mDBConn,
    2101             :                                    nullptr,
    2102             :                                    SQLITE_FCNTL_FILE_POINTER,
    2103           0 :                                    &file);
    2104           0 :   if (srv != SQLITE_OK) {
    2105           0 :     return convertResultCode(srv);
    2106             :   }
    2107             : 
    2108           0 :   RefPtr<QuotaObject> databaseQuotaObject = GetQuotaObjectForFile(file);
    2109             : 
    2110           0 :   srv = ::sqlite3_file_control(mDBConn,
    2111             :                                nullptr,
    2112             :                                SQLITE_FCNTL_JOURNAL_POINTER,
    2113           0 :                                &file);
    2114           0 :   if (srv != SQLITE_OK) {
    2115           0 :     return convertResultCode(srv);
    2116             :   }
    2117             : 
    2118           0 :   RefPtr<QuotaObject> journalQuotaObject = GetQuotaObjectForFile(file);
    2119             : 
    2120           0 :   databaseQuotaObject.forget(aDatabaseQuotaObject);
    2121           0 :   journalQuotaObject.forget(aJournalQuotaObject);
    2122           0 :   return NS_OK;
    2123             : }
    2124             : 
    2125             : } // namespace storage
    2126             : } // namespace mozilla

Generated by: LCOV version 1.13