storage/src/mozStorageConnection.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     2  * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include <stdio.h>
     9 #include "nsError.h"
    10 #include "nsIMutableArray.h"
    11 #include "nsAutoPtr.h"
    12 #include "nsIMemoryReporter.h"
    13 #include "nsThreadUtils.h"
    14 #include "nsIFile.h"
    15 #include "nsIFileURL.h"
    16 #include "mozilla/Telemetry.h"
    17 #include "mozilla/Mutex.h"
    18 #include "mozilla/CondVar.h"
    19 #include "mozilla/Attributes.h"
    21 #include "mozIStorageAggregateFunction.h"
    22 #include "mozIStorageCompletionCallback.h"
    23 #include "mozIStorageFunction.h"
    25 #include "mozStorageAsyncStatementExecution.h"
    26 #include "mozStorageSQLFunctions.h"
    27 #include "mozStorageConnection.h"
    28 #include "mozStorageService.h"
    29 #include "mozStorageStatement.h"
    30 #include "mozStorageAsyncStatement.h"
    31 #include "mozStorageArgValueArray.h"
    32 #include "mozStoragePrivateHelpers.h"
    33 #include "mozStorageStatementData.h"
    34 #include "StorageBaseStatementInternal.h"
    35 #include "SQLCollations.h"
    36 #include "FileSystemModule.h"
    37 #include "mozStorageHelper.h"
    38 #include "GeckoProfiler.h"
    40 #include "prlog.h"
    41 #include "prprf.h"
    42 #include "nsProxyRelease.h"
    43 #include <algorithm>
    45 #define MIN_AVAILABLE_BYTES_PER_CHUNKED_GROWTH 524288000 // 500 MiB
    47 // Maximum size of the pages cache per connection.
    48 #define MAX_CACHE_SIZE_KIBIBYTES 2048 // 2 MiB
    50 #ifdef PR_LOGGING
    51 PRLogModuleInfo* gStorageLog = nullptr;
    52 #endif
    54 namespace mozilla {
    55 namespace storage {
    57 namespace {
    59 ////////////////////////////////////////////////////////////////////////////////
    60 //// Variant Specialization Functions (variantToSQLiteT)
    62 int
    63 sqlite3_T_int(sqlite3_context *aCtx,
    64               int aValue)
    65 {
    66   ::sqlite3_result_int(aCtx, aValue);
    67   return SQLITE_OK;
    68 }
    70 int
    71 sqlite3_T_int64(sqlite3_context *aCtx,
    72                 sqlite3_int64 aValue)
    73 {
    74   ::sqlite3_result_int64(aCtx, aValue);
    75   return SQLITE_OK;
    76 }
    78 int
    79 sqlite3_T_double(sqlite3_context *aCtx,
    80                  double aValue)
    81 {
    82   ::sqlite3_result_double(aCtx, aValue);
    83   return SQLITE_OK;
    84 }
    86 int
    87 sqlite3_T_text(sqlite3_context *aCtx,
    88                const nsCString &aValue)
    89 {
    90   ::sqlite3_result_text(aCtx,
    91                         aValue.get(),
    92                         aValue.Length(),
    93                         SQLITE_TRANSIENT);
    94   return SQLITE_OK;
    95 }
    97 int
    98 sqlite3_T_text16(sqlite3_context *aCtx,
    99                  const nsString &aValue)
   100 {
   101   ::sqlite3_result_text16(aCtx,
   102                           aValue.get(),
   103                           aValue.Length() * 2, // Number of bytes.
   104                           SQLITE_TRANSIENT);
   105   return SQLITE_OK;
   106 }
   108 int
   109 sqlite3_T_null(sqlite3_context *aCtx)
   110 {
   111   ::sqlite3_result_null(aCtx);
   112   return SQLITE_OK;
   113 }
   115 int
   116 sqlite3_T_blob(sqlite3_context *aCtx,
   117                const void *aData,
   118                int aSize)
   119 {
   120   ::sqlite3_result_blob(aCtx, aData, aSize, NS_Free);
   121   return SQLITE_OK;
   122 }
   124 #include "variantToSQLiteT_impl.h"
   126 ////////////////////////////////////////////////////////////////////////////////
   127 //// Modules
   129 struct Module
   130 {
   131   const char* name;
   132   int (*registerFunc)(sqlite3*, const char*);
   133 };
   135 Module gModules[] = {
   136   { "filesystem", RegisterFileSystemModule }
   137 };
   139 ////////////////////////////////////////////////////////////////////////////////
   140 //// Local Functions
   142 #ifdef PR_LOGGING
   143 void tracefunc (void *aClosure, const char *aStmt)
   144 {
   145   PR_LOG(gStorageLog, PR_LOG_DEBUG, ("sqlite3_trace on %p for '%s'", aClosure,
   146                                      aStmt));
   147 }
   148 #endif
   150 struct FFEArguments
   151 {
   152     nsISupports *target;
   153     bool found;
   154 };
   155 PLDHashOperator
   156 findFunctionEnumerator(const nsACString &aKey,
   157                        Connection::FunctionInfo aData,
   158                        void *aUserArg)
   159 {
   160   FFEArguments *args = static_cast<FFEArguments *>(aUserArg);
   161   if (aData.function == args->target) {
   162     args->found = true;
   163     return PL_DHASH_STOP;
   164   }
   165   return PL_DHASH_NEXT;
   166 }
   168 PLDHashOperator
   169 copyFunctionEnumerator(const nsACString &aKey,
   170                        Connection::FunctionInfo aData,
   171                        void *aUserArg)
   172 {
   173   NS_PRECONDITION(aData.type == Connection::FunctionInfo::SIMPLE ||
   174                   aData.type == Connection::FunctionInfo::AGGREGATE,
   175                   "Invalid function type!");
   177   Connection *connection = static_cast<Connection *>(aUserArg);
   178   if (aData.type == Connection::FunctionInfo::SIMPLE) {
   179     mozIStorageFunction *function =
   180       static_cast<mozIStorageFunction *>(aData.function.get());
   181     (void)connection->CreateFunction(aKey, aData.numArgs, function);
   182   }
   183   else {
   184     mozIStorageAggregateFunction *function =
   185       static_cast<mozIStorageAggregateFunction *>(aData.function.get());
   186     (void)connection->CreateAggregateFunction(aKey, aData.numArgs, function);
   187   }
   189   return PL_DHASH_NEXT;
   190 }
   192 void
   193 basicFunctionHelper(sqlite3_context *aCtx,
   194                     int aArgc,
   195                     sqlite3_value **aArgv)
   196 {
   197   void *userData = ::sqlite3_user_data(aCtx);
   199   mozIStorageFunction *func = static_cast<mozIStorageFunction *>(userData);
   201   nsRefPtr<ArgValueArray> arguments(new ArgValueArray(aArgc, aArgv));
   202   if (!arguments)
   203       return;
   205   nsCOMPtr<nsIVariant> result;
   206   if (NS_FAILED(func->OnFunctionCall(arguments, getter_AddRefs(result)))) {
   207     NS_WARNING("User function returned error code!");
   208     ::sqlite3_result_error(aCtx,
   209                            "User function returned error code",
   210                            -1);
   211     return;
   212   }
   213   int retcode = variantToSQLiteT(aCtx, result);
   214   if (retcode == SQLITE_IGNORE) {
   215     ::sqlite3_result_int(aCtx, SQLITE_IGNORE);
   216   } else if (retcode != SQLITE_OK) {
   217     NS_WARNING("User function returned invalid data type!");
   218     ::sqlite3_result_error(aCtx,
   219                            "User function returned invalid data type",
   220                            -1);
   221   }
   222 }
   224 void
   225 aggregateFunctionStepHelper(sqlite3_context *aCtx,
   226                             int aArgc,
   227                             sqlite3_value **aArgv)
   228 {
   229   void *userData = ::sqlite3_user_data(aCtx);
   230   mozIStorageAggregateFunction *func =
   231     static_cast<mozIStorageAggregateFunction *>(userData);
   233   nsRefPtr<ArgValueArray> arguments(new ArgValueArray(aArgc, aArgv));
   234   if (!arguments)
   235     return;
   237   if (NS_FAILED(func->OnStep(arguments)))
   238     NS_WARNING("User aggregate step function returned error code!");
   239 }
   241 void
   242 aggregateFunctionFinalHelper(sqlite3_context *aCtx)
   243 {
   244   void *userData = ::sqlite3_user_data(aCtx);
   245   mozIStorageAggregateFunction *func =
   246     static_cast<mozIStorageAggregateFunction *>(userData);
   248   nsRefPtr<nsIVariant> result;
   249   if (NS_FAILED(func->OnFinal(getter_AddRefs(result)))) {
   250     NS_WARNING("User aggregate final function returned error code!");
   251     ::sqlite3_result_error(aCtx,
   252                            "User aggregate final function returned error code",
   253                            -1);
   254     return;
   255   }
   257   if (variantToSQLiteT(aCtx, result) != SQLITE_OK) {
   258     NS_WARNING("User aggregate final function returned invalid data type!");
   259     ::sqlite3_result_error(aCtx,
   260                            "User aggregate final function returned invalid data type",
   261                            -1);
   262   }
   263 }
   265 /**
   266  * This code is heavily based on the sample at:
   267  *   http://www.sqlite.org/unlock_notify.html
   268  */
   269 class UnlockNotification
   270 {
   271 public:
   272   UnlockNotification()
   273   : mMutex("UnlockNotification mMutex")
   274   , mCondVar(mMutex, "UnlockNotification condVar")
   275   , mSignaled(false)
   276   {
   277   }
   279   void Wait()
   280   {
   281     MutexAutoLock lock(mMutex);
   282     while (!mSignaled) {
   283       (void)mCondVar.Wait();
   284     }
   285   }
   287   void Signal()
   288   {
   289     MutexAutoLock lock(mMutex);
   290     mSignaled = true;
   291     (void)mCondVar.Notify();
   292   }
   294 private:
   295   Mutex mMutex;
   296   CondVar mCondVar;
   297   bool mSignaled;
   298 };
   300 void
   301 UnlockNotifyCallback(void **aArgs,
   302                      int aArgsSize)
   303 {
   304   for (int i = 0; i < aArgsSize; i++) {
   305     UnlockNotification *notification =
   306       static_cast<UnlockNotification *>(aArgs[i]);
   307     notification->Signal();
   308   }
   309 }
   311 int
   312 WaitForUnlockNotify(sqlite3* aDatabase)
   313 {
   314   UnlockNotification notification;
   315   int srv = ::sqlite3_unlock_notify(aDatabase, UnlockNotifyCallback,
   316                                     &notification);
   317   MOZ_ASSERT(srv == SQLITE_LOCKED || srv == SQLITE_OK);
   318   if (srv == SQLITE_OK) {
   319     notification.Wait();
   320   }
   322   return srv;
   323 }
   325 } // anonymous namespace
   327 ////////////////////////////////////////////////////////////////////////////////
   328 //// Local Classes
   330 namespace {
   332 class AsyncCloseConnection MOZ_FINAL: public nsRunnable
   333 {
   334 public:
   335   AsyncCloseConnection(Connection *aConnection,
   336                        sqlite3 *aNativeConnection,
   337                        nsIRunnable *aCallbackEvent,
   338                        already_AddRefed<nsIThread> aAsyncExecutionThread)
   339   : mConnection(aConnection)
   340   , mNativeConnection(aNativeConnection)
   341   , mCallbackEvent(aCallbackEvent)
   342   , mAsyncExecutionThread(aAsyncExecutionThread)
   343   {
   344   }
   346   NS_METHOD Run()
   347   {
   348 #ifdef DEBUG
   349     // This code is executed on the background thread
   350     bool onAsyncThread = false;
   351     (void)mAsyncExecutionThread->IsOnCurrentThread(&onAsyncThread);
   352     MOZ_ASSERT(onAsyncThread);
   353 #endif // DEBUG
   355     // Internal close.
   356     (void)mConnection->internalClose(mNativeConnection);
   358     // Callback
   359     if (mCallbackEvent) {
   360       nsCOMPtr<nsIThread> thread;
   361       (void)NS_GetMainThread(getter_AddRefs(thread));
   362       (void)thread->Dispatch(mCallbackEvent, NS_DISPATCH_NORMAL);
   363     }
   365     return NS_OK;
   366   }
   368   ~AsyncCloseConnection() {
   369     nsCOMPtr<nsIThread> thread;
   370     (void)NS_GetMainThread(getter_AddRefs(thread));
   371     // Handle ambiguous nsISupports inheritance.
   372     Connection *rawConnection = nullptr;
   373     mConnection.swap(rawConnection);
   374     (void)NS_ProxyRelease(thread,
   375                           NS_ISUPPORTS_CAST(mozIStorageConnection *,
   376                                             rawConnection));
   377     (void)NS_ProxyRelease(thread, mCallbackEvent);
   378   }
   379 private:
   380   nsRefPtr<Connection> mConnection;
   381   sqlite3 *mNativeConnection;
   382   nsCOMPtr<nsIRunnable> mCallbackEvent;
   383   nsCOMPtr<nsIThread> mAsyncExecutionThread;
   384 };
   386 /**
   387  * An event used to initialize the clone of a connection.
   388  *
   389  * Must be executed on the clone's async execution thread.
   390  */
   391 class AsyncInitializeClone MOZ_FINAL: public nsRunnable
   392 {
   393 public:
   394   /**
   395    * @param aConnection The connection being cloned.
   396    * @param aClone The clone.
   397    * @param aReadOnly If |true|, the clone is read only.
   398    * @param aCallback A callback to trigger once initialization
   399    *                  is complete. This event will be called on
   400    *                  aClone->threadOpenedOn.
   401    */
   402   AsyncInitializeClone(Connection* aConnection,
   403                        Connection* aClone,
   404                        const bool aReadOnly,
   405                        mozIStorageCompletionCallback* aCallback)
   406     : mConnection(aConnection)
   407     , mClone(aClone)
   408     , mReadOnly(aReadOnly)
   409     , mCallback(aCallback)
   410   {
   411     MOZ_ASSERT(NS_IsMainThread());
   412   }
   414   NS_IMETHOD Run() {
   415     MOZ_ASSERT (NS_GetCurrentThread() == mClone->getAsyncExecutionTarget());
   417     nsresult rv = mConnection->initializeClone(mClone, mReadOnly);
   418     if (NS_FAILED(rv)) {
   419       return Dispatch(rv, nullptr);
   420     }
   421     return Dispatch(NS_OK,
   422                     NS_ISUPPORTS_CAST(mozIStorageAsyncConnection*, mClone));
   423   }
   425 private:
   426   nsresult Dispatch(nsresult aResult, nsISupports* aValue) {
   427     nsRefPtr<CallbackComplete> event = new CallbackComplete(aResult,
   428                                                             aValue,
   429                                                             mCallback.forget());
   430     return mClone->threadOpenedOn->Dispatch(event, NS_DISPATCH_NORMAL);
   431   }
   433   ~AsyncInitializeClone() {
   434     nsCOMPtr<nsIThread> thread;
   435     DebugOnly<nsresult> rv = NS_GetMainThread(getter_AddRefs(thread));
   436     MOZ_ASSERT(NS_SUCCEEDED(rv));
   438     // Handle ambiguous nsISupports inheritance.
   439     Connection *rawConnection = nullptr;
   440     mConnection.swap(rawConnection);
   441     (void)NS_ProxyRelease(thread, NS_ISUPPORTS_CAST(mozIStorageConnection *,
   442                                                     rawConnection));
   444     Connection *rawClone = nullptr;
   445     mClone.swap(rawClone);
   446     (void)NS_ProxyRelease(thread, NS_ISUPPORTS_CAST(mozIStorageConnection *,
   447                                                     rawClone));
   449     // Generally, the callback will be released by CallbackComplete.
   450     // However, if for some reason Run() is not executed, we still
   451     // need to ensure that it is released here.
   452     mozIStorageCompletionCallback *rawCallback = nullptr;
   453     mCallback.swap(rawCallback);
   454     (void)NS_ProxyRelease(thread, rawCallback);
   455   }
   457   nsRefPtr<Connection> mConnection;
   458   nsRefPtr<Connection> mClone;
   459   const bool mReadOnly;
   460   nsCOMPtr<mozIStorageCompletionCallback> mCallback;
   461 };
   463 } // anonymous namespace
   465 ////////////////////////////////////////////////////////////////////////////////
   466 //// Connection
   468 Connection::Connection(Service *aService,
   469                        int aFlags,
   470                        bool aAsyncOnly)
   471 : sharedAsyncExecutionMutex("Connection::sharedAsyncExecutionMutex")
   472 , sharedDBMutex("Connection::sharedDBMutex")
   473 , threadOpenedOn(do_GetCurrentThread())
   474 , mDBConn(nullptr)
   475 , mAsyncExecutionThreadShuttingDown(false)
   476 , mConnectionClosed(false)
   477 , mTransactionInProgress(false)
   478 , mProgressHandler(nullptr)
   479 , mFlags(aFlags)
   480 , mStorageService(aService)
   481 , mAsyncOnly(aAsyncOnly)
   482 {
   483   mStorageService->registerConnection(this);
   484 }
   486 Connection::~Connection()
   487 {
   488   (void)Close();
   490   MOZ_ASSERT(!mAsyncExecutionThread,
   491              "AsyncClose has not been invoked on this connection!");
   492 }
   494 NS_IMPL_ADDREF(Connection)
   496 NS_INTERFACE_MAP_BEGIN(Connection)
   497   NS_INTERFACE_MAP_ENTRY(mozIStorageAsyncConnection)
   498   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   499   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(mozIStorageConnection, !mAsyncOnly)
   500   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageConnection)
   501 NS_INTERFACE_MAP_END
   503 // This is identical to what NS_IMPL_RELEASE provides, but with the
   504 // extra |1 == count| case.
   505 NS_IMETHODIMP_(MozExternalRefCountType) Connection::Release(void)
   506 {
   507   NS_PRECONDITION(0 != mRefCnt, "dup release");
   508   nsrefcnt count = --mRefCnt;
   509   NS_LOG_RELEASE(this, count, "Connection");
   510   if (1 == count) {
   511     // If the refcount is 1, the single reference must be from
   512     // gService->mConnections (in class |Service|).  Which means we can
   513     // unregister it safely.
   514     mStorageService->unregisterConnection(this);
   515   } else if (0 == count) {
   516     mRefCnt = 1; /* stabilize */
   517 #if 0 /* enable this to find non-threadsafe destructors: */
   518     NS_ASSERT_OWNINGTHREAD(Connection);
   519 #endif
   520     delete (this);
   521     return 0;
   522   }
   523   return count;
   524 }
   526 int32_t
   527 Connection::getSqliteRuntimeStatus(int32_t aStatusOption, int32_t* aMaxValue)
   528 {
   529   MOZ_ASSERT(mDBConn, "A connection must exist at this point");
   530   int curr = 0, max = 0;
   531   DebugOnly<int> rc = ::sqlite3_db_status(mDBConn, aStatusOption, &curr, &max, 0);
   532   MOZ_ASSERT(NS_SUCCEEDED(convertResultCode(rc)));
   533   if (aMaxValue)
   534     *aMaxValue = max;
   535   return curr;
   536 }
   538 nsIEventTarget *
   539 Connection::getAsyncExecutionTarget()
   540 {
   541   MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
   543   // If we are shutting down the asynchronous thread, don't hand out any more
   544   // references to the thread.
   545   if (mAsyncExecutionThreadShuttingDown)
   546     return nullptr;
   548   if (!mAsyncExecutionThread) {
   549     nsresult rv = ::NS_NewThread(getter_AddRefs(mAsyncExecutionThread));
   550     if (NS_FAILED(rv)) {
   551       NS_WARNING("Failed to create async thread.");
   552       return nullptr;
   553     }
   554     static nsThreadPoolNaming naming;
   555     naming.SetThreadPoolName(NS_LITERAL_CSTRING("mozStorage"),
   556                              mAsyncExecutionThread);
   557   }
   559   return mAsyncExecutionThread;
   560 }
   562 nsresult
   563 Connection::initialize()
   564 {
   565   NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
   566   PROFILER_LABEL("storage", "Connection::initialize");
   568   // in memory database requested, sqlite uses a magic file name
   569   int srv = ::sqlite3_open_v2(":memory:", &mDBConn, mFlags, nullptr);
   570   if (srv != SQLITE_OK) {
   571     mDBConn = nullptr;
   572     return convertResultCode(srv);
   573   }
   575   return initializeInternal(nullptr);
   576 }
   578 nsresult
   579 Connection::initialize(nsIFile *aDatabaseFile)
   580 {
   581   NS_ASSERTION (aDatabaseFile, "Passed null file!");
   582   NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
   583   PROFILER_LABEL("storage", "Connection::initialize");
   585   mDatabaseFile = aDatabaseFile;
   587   nsAutoString path;
   588   nsresult rv = aDatabaseFile->GetPath(path);
   589   NS_ENSURE_SUCCESS(rv, rv);
   591   int srv = ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path).get(), &mDBConn,
   592                               mFlags, nullptr);
   593   if (srv != SQLITE_OK) {
   594     mDBConn = nullptr;
   595     return convertResultCode(srv);
   596   }
   598   rv = initializeInternal(aDatabaseFile);
   599   NS_ENSURE_SUCCESS(rv, rv);
   601   mDatabaseFile = aDatabaseFile;
   603   return NS_OK;
   604 }
   606 nsresult
   607 Connection::initialize(nsIFileURL *aFileURL)
   608 {
   609   NS_ASSERTION (aFileURL, "Passed null file URL!");
   610   NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
   611   PROFILER_LABEL("storage", "Connection::initialize");
   613   nsCOMPtr<nsIFile> databaseFile;
   614   nsresult rv = aFileURL->GetFile(getter_AddRefs(databaseFile));
   615   NS_ENSURE_SUCCESS(rv, rv);
   617   nsAutoCString spec;
   618   rv = aFileURL->GetSpec(spec);
   619   NS_ENSURE_SUCCESS(rv, rv);
   621   int srv = ::sqlite3_open_v2(spec.get(), &mDBConn, mFlags, nullptr);
   622   if (srv != SQLITE_OK) {
   623     mDBConn = nullptr;
   624     return convertResultCode(srv);
   625   }
   627   rv = initializeInternal(databaseFile);
   628   NS_ENSURE_SUCCESS(rv, rv);
   630   mFileURL = aFileURL;
   631   mDatabaseFile = databaseFile;
   633   return NS_OK;
   634 }
   637 nsresult
   638 Connection::initializeInternal(nsIFile* aDatabaseFile)
   639 {
   640   // Properly wrap the database handle's mutex.
   641   sharedDBMutex.initWithMutex(sqlite3_db_mutex(mDBConn));
   643 #ifdef PR_LOGGING
   644   if (!gStorageLog)
   645     gStorageLog = ::PR_NewLogModule("mozStorage");
   647   ::sqlite3_trace(mDBConn, tracefunc, this);
   649   nsAutoCString leafName(":memory");
   650   if (aDatabaseFile)
   651     (void)aDatabaseFile->GetNativeLeafName(leafName);
   652   PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Opening connection to '%s' (%p)",
   653                                       leafName.get(), this));
   654 #endif
   656   int64_t pageSize = Service::getDefaultPageSize();
   658   // Set page_size to the preferred default value.  This is effective only if
   659   // the database has just been created, otherwise, if the database does not
   660   // use WAL journal mode, a VACUUM operation will updated its page_size.
   661   nsAutoCString pageSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
   662                               "PRAGMA page_size = ");
   663   pageSizeQuery.AppendInt(pageSize);
   664   nsresult rv = ExecuteSimpleSQL(pageSizeQuery);
   665   NS_ENSURE_SUCCESS(rv, rv);
   667   // Setting the cache_size forces the database open, verifying if it is valid
   668   // or corrupt.  So this is executed regardless it being actually needed.
   669   // The cache_size is calculated from the actual page_size, to save memory.
   670   nsAutoCString cacheSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
   671                                "PRAGMA cache_size = ");
   672   cacheSizeQuery.AppendInt(-MAX_CACHE_SIZE_KIBIBYTES);
   673   int srv = executeSql(mDBConn, cacheSizeQuery.get());
   674   if (srv != SQLITE_OK) {
   675     ::sqlite3_close(mDBConn);
   676     mDBConn = nullptr;
   677     return convertResultCode(srv);
   678   }
   680   // Register our built-in SQL functions.
   681   srv = registerFunctions(mDBConn);
   682   if (srv != SQLITE_OK) {
   683     ::sqlite3_close(mDBConn);
   684     mDBConn = nullptr;
   685     return convertResultCode(srv);
   686   }
   688   // Register our built-in SQL collating sequences.
   689   srv = registerCollations(mDBConn, mStorageService);
   690   if (srv != SQLITE_OK) {
   691     ::sqlite3_close(mDBConn);
   692     mDBConn = nullptr;
   693     return convertResultCode(srv);
   694   }
   696   // Set the synchronous PRAGMA, according to the preference.
   697   switch (Service::getSynchronousPref()) {
   698     case 2:
   699       (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   700           "PRAGMA synchronous = FULL;"));
   701       break;
   702     case 0:
   703       (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   704           "PRAGMA synchronous = OFF;"));
   705       break;
   706     case 1:
   707     default:
   708       (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   709           "PRAGMA synchronous = NORMAL;"));
   710       break;
   711   }
   713   return NS_OK;
   714 }
   716 nsresult
   717 Connection::databaseElementExists(enum DatabaseElementType aElementType,
   718                                   const nsACString &aElementName,
   719                                   bool *_exists)
   720 {
   721   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
   723   // When constructing the query, make sure to SELECT the correct db's sqlite_master
   724   // if the user is prefixing the element with a specific db. ex: sample.test
   725   nsCString query("SELECT name FROM (SELECT * FROM ");
   726   nsDependentCSubstring element;
   727   int32_t ind = aElementName.FindChar('.');
   728   if (ind == kNotFound) {
   729     element.Assign(aElementName);
   730   }
   731   else {
   732     nsDependentCSubstring db(Substring(aElementName, 0, ind + 1));
   733     element.Assign(Substring(aElementName, ind + 1, aElementName.Length()));
   734     query.Append(db);
   735   }
   736   query.Append("sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type = '");
   738   switch (aElementType) {
   739     case INDEX:
   740       query.Append("index");
   741       break;
   742     case TABLE:
   743       query.Append("table");
   744       break;
   745   }
   746   query.Append("' AND name ='");
   747   query.Append(element);
   748   query.Append("'");
   750   sqlite3_stmt *stmt;
   751   int srv = prepareStatement(mDBConn, query, &stmt);
   752   if (srv != SQLITE_OK)
   753     return convertResultCode(srv);
   755   srv = stepStatement(mDBConn, stmt);
   756   // we just care about the return value from step
   757   (void)::sqlite3_finalize(stmt);
   759   if (srv == SQLITE_ROW) {
   760     *_exists = true;
   761     return NS_OK;
   762   }
   763   if (srv == SQLITE_DONE) {
   764     *_exists = false;
   765     return NS_OK;
   766   }
   768   return convertResultCode(srv);
   769 }
   771 bool
   772 Connection::findFunctionByInstance(nsISupports *aInstance)
   773 {
   774   sharedDBMutex.assertCurrentThreadOwns();
   775   FFEArguments args = { aInstance, false };
   776   (void)mFunctions.EnumerateRead(findFunctionEnumerator, &args);
   777   return args.found;
   778 }
   780 /* static */ int
   781 Connection::sProgressHelper(void *aArg)
   782 {
   783   Connection *_this = static_cast<Connection *>(aArg);
   784   return _this->progressHandler();
   785 }
   787 int
   788 Connection::progressHandler()
   789 {
   790   sharedDBMutex.assertCurrentThreadOwns();
   791   if (mProgressHandler) {
   792     bool result;
   793     nsresult rv = mProgressHandler->OnProgress(this, &result);
   794     if (NS_FAILED(rv)) return 0; // Don't break request
   795     return result ? 1 : 0;
   796   }
   797   return 0;
   798 }
   800 nsresult
   801 Connection::setClosedState()
   802 {
   803   // Ensure that we are on the correct thread to close the database.
   804   bool onOpenedThread;
   805   nsresult rv = threadOpenedOn->IsOnCurrentThread(&onOpenedThread);
   806   NS_ENSURE_SUCCESS(rv, rv);
   807   if (!onOpenedThread) {
   808     NS_ERROR("Must close the database on the thread that you opened it with!");
   809     return NS_ERROR_UNEXPECTED;
   810   }
   812   // Flag that we are shutting down the async thread, so that
   813   // getAsyncExecutionTarget knows not to expose/create the async thread.
   814   {
   815     MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
   816     NS_ENSURE_FALSE(mAsyncExecutionThreadShuttingDown, NS_ERROR_UNEXPECTED);
   817     mAsyncExecutionThreadShuttingDown = true;
   818   }
   820   // Set the property to null before closing the connection, otherwise the other
   821   // functions in the module may try to use the connection after it is closed.
   822   mDBConn = nullptr;
   824   return NS_OK;
   825 }
   827 bool
   828 Connection::connectionReady()
   829 {
   830   return mDBConn != nullptr;
   831 }
   833 bool
   834 Connection::isClosing()
   835 {
   836   bool shuttingDown = false;
   837   {
   838     MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
   839     shuttingDown = mAsyncExecutionThreadShuttingDown;
   840   }
   841   return shuttingDown && !isClosed();
   842 }
   844 bool
   845 Connection::isClosed()
   846 {
   847   MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
   848   return mConnectionClosed;
   849 }
   851 nsresult
   852 Connection::internalClose(sqlite3 *aNativeConnection)
   853 {
   854   // Sanity checks to make sure we are in the proper state before calling this.
   855   MOZ_ASSERT(aNativeConnection, "Database connection is invalid!");
   856   MOZ_ASSERT(!isClosed());
   858 #ifdef DEBUG
   859   { // Make sure we have marked our async thread as shutting down.
   860     MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
   861     NS_ASSERTION(mAsyncExecutionThreadShuttingDown,
   862                  "Did not call setClosedState!");
   863   }
   864 #endif // DEBUG
   866 #ifdef PR_LOGGING
   867   nsAutoCString leafName(":memory");
   868   if (mDatabaseFile)
   869       (void)mDatabaseFile->GetNativeLeafName(leafName);
   870   PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Closing connection to '%s'",
   871                                       leafName.get()));
   872 #endif
   874   // At this stage, we may still have statements that need to be
   875   // finalized. Attempt to close the database connection. This will
   876   // always disconnect any virtual tables and cleanly finalize their
   877   // internal statements. Once this is done, closing may fail due to
   878   // unfinalized client statements, in which case we need to finalize
   879   // these statements and close again.
   880   {
   881     MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
   882     mConnectionClosed = true;
   883   }
   884   int srv = sqlite3_close(aNativeConnection);
   886   if (srv == SQLITE_BUSY) {
   887     // We still have non-finalized statements. Finalize them.
   889     sqlite3_stmt *stmt = nullptr;
   890     while ((stmt = ::sqlite3_next_stmt(aNativeConnection, stmt))) {
   891       PR_LOG(gStorageLog, PR_LOG_NOTICE,
   892              ("Auto-finalizing SQL statement '%s' (%x)",
   893               ::sqlite3_sql(stmt),
   894               stmt));
   896 #ifdef DEBUG
   897       char *msg = ::PR_smprintf("SQL statement '%s' (%x) should have been finalized before closing the connection",
   898                                 ::sqlite3_sql(stmt),
   899                                 stmt);
   900       NS_WARNING(msg);
   901       ::PR_smprintf_free(msg);
   902 #endif // DEBUG
   904       srv = ::sqlite3_finalize(stmt);
   906 #ifdef DEBUG
   907       if (srv != SQLITE_OK) {
   908         char *msg = ::PR_smprintf("Could not finalize SQL statement '%s' (%x)",
   909                                   ::sqlite3_sql(stmt),
   910                                   stmt);
   911         NS_WARNING(msg);
   912         ::PR_smprintf_free(msg);
   913       }
   914 #endif // DEBUG
   916       // Ensure that the loop continues properly, whether closing has succeeded
   917       // or not.
   918       if (srv == SQLITE_OK) {
   919         stmt = nullptr;
   920       }
   921     }
   923     // Now that all statements have been finalized, we
   924     // should be able to close.
   925     srv = ::sqlite3_close(aNativeConnection);
   927   }
   929   if (srv != SQLITE_OK) {
   930     MOZ_ASSERT(srv == SQLITE_OK,
   931                "sqlite3_close failed. There are probably outstanding statements that are listed above!");
   932   }
   934   return convertResultCode(srv);
   935 }
   937 nsCString
   938 Connection::getFilename()
   939 {
   940   nsCString leafname(":memory:");
   941   if (mDatabaseFile) {
   942     (void)mDatabaseFile->GetNativeLeafName(leafname);
   943   }
   944   return leafname;
   945 }
   947 int
   948 Connection::stepStatement(sqlite3 *aNativeConnection, sqlite3_stmt *aStatement)
   949 {
   950   MOZ_ASSERT(aStatement);
   951   bool checkedMainThread = false;
   952   TimeStamp startTime = TimeStamp::Now();
   954   // The connection may have been closed if the executing statement has been
   955   // created and cached after a call to asyncClose() but before the actual
   956   // sqlite3_close().  This usually happens when other tasks using cached
   957   // statements are asynchronously scheduled for execution and any of them ends
   958   // up after asyncClose. See bug 728653 for details.
   959   if (isClosed())
   960     return SQLITE_MISUSE;
   962   (void)::sqlite3_extended_result_codes(aNativeConnection, 1);
   964   int srv;
   965   while ((srv = ::sqlite3_step(aStatement)) == SQLITE_LOCKED_SHAREDCACHE) {
   966     if (!checkedMainThread) {
   967       checkedMainThread = true;
   968       if (::NS_IsMainThread()) {
   969         NS_WARNING("We won't allow blocking on the main thread!");
   970         break;
   971       }
   972     }
   974     srv = WaitForUnlockNotify(aNativeConnection);
   975     if (srv != SQLITE_OK) {
   976       break;
   977     }
   979     ::sqlite3_reset(aStatement);
   980   }
   982   // Report very slow SQL statements to Telemetry
   983   TimeDuration duration = TimeStamp::Now() - startTime;
   984   const uint32_t threshold =
   985     NS_IsMainThread() ? Telemetry::kSlowSQLThresholdForMainThread
   986                       : Telemetry::kSlowSQLThresholdForHelperThreads;
   987   if (duration.ToMilliseconds() >= threshold) {
   988     nsDependentCString statementString(::sqlite3_sql(aStatement));
   989     Telemetry::RecordSlowSQLStatement(statementString, getFilename(),
   990                                       duration.ToMilliseconds());
   991   }
   993   (void)::sqlite3_extended_result_codes(aNativeConnection, 0);
   994   // Drop off the extended result bits of the result code.
   995   return srv & 0xFF;
   996 }
   998 int
   999 Connection::prepareStatement(sqlite3 *aNativeConnection, const nsCString &aSQL,
  1000                              sqlite3_stmt **_stmt)
  1002   // We should not even try to prepare statements after the connection has
  1003   // been closed.
  1004   if (isClosed())
  1005     return SQLITE_MISUSE;
  1007   bool checkedMainThread = false;
  1009   (void)::sqlite3_extended_result_codes(aNativeConnection, 1);
  1011   int srv;
  1012   while((srv = ::sqlite3_prepare_v2(aNativeConnection,
  1013                                     aSQL.get(),
  1014                                     -1,
  1015                                     _stmt,
  1016                                     nullptr)) == SQLITE_LOCKED_SHAREDCACHE) {
  1017     if (!checkedMainThread) {
  1018       checkedMainThread = true;
  1019       if (::NS_IsMainThread()) {
  1020         NS_WARNING("We won't allow blocking on the main thread!");
  1021         break;
  1025     srv = WaitForUnlockNotify(aNativeConnection);
  1026     if (srv != SQLITE_OK) {
  1027       break;
  1031   if (srv != SQLITE_OK) {
  1032     nsCString warnMsg;
  1033     warnMsg.AppendLiteral("The SQL statement '");
  1034     warnMsg.Append(aSQL);
  1035     warnMsg.AppendLiteral("' could not be compiled due to an error: ");
  1036     warnMsg.Append(::sqlite3_errmsg(aNativeConnection));
  1038 #ifdef DEBUG
  1039     NS_WARNING(warnMsg.get());
  1040 #endif
  1041     PR_LOG(gStorageLog, PR_LOG_ERROR, ("%s", warnMsg.get()));
  1044   (void)::sqlite3_extended_result_codes(aNativeConnection, 0);
  1045   // Drop off the extended result bits of the result code.
  1046   int rc = srv & 0xFF;
  1047   // sqlite will return OK on a comment only string and set _stmt to nullptr.
  1048   // The callers of this function are used to only checking the return value,
  1049   // so it is safer to return an error code.
  1050   if (rc == SQLITE_OK && *_stmt == nullptr) {
  1051     return SQLITE_MISUSE;
  1054   return rc;
  1058 int
  1059 Connection::executeSql(sqlite3 *aNativeConnection, const char *aSqlString)
  1061   if (isClosed())
  1062     return SQLITE_MISUSE;
  1064   TimeStamp startTime = TimeStamp::Now();
  1065   int srv = ::sqlite3_exec(aNativeConnection, aSqlString, nullptr, nullptr,
  1066                            nullptr);
  1068   // Report very slow SQL statements to Telemetry
  1069   TimeDuration duration = TimeStamp::Now() - startTime;
  1070   const uint32_t threshold =
  1071     NS_IsMainThread() ? Telemetry::kSlowSQLThresholdForMainThread
  1072                       : Telemetry::kSlowSQLThresholdForHelperThreads;
  1073   if (duration.ToMilliseconds() >= threshold) {
  1074     nsDependentCString statementString(aSqlString);
  1075     Telemetry::RecordSlowSQLStatement(statementString, getFilename(),
  1076                                       duration.ToMilliseconds());
  1079   return srv;
  1082 ////////////////////////////////////////////////////////////////////////////////
  1083 //// nsIInterfaceRequestor
  1085 NS_IMETHODIMP
  1086 Connection::GetInterface(const nsIID &aIID,
  1087                          void **_result)
  1089   if (aIID.Equals(NS_GET_IID(nsIEventTarget))) {
  1090     nsIEventTarget *background = getAsyncExecutionTarget();
  1091     NS_IF_ADDREF(background);
  1092     *_result = background;
  1093     return NS_OK;
  1095   return NS_ERROR_NO_INTERFACE;
  1098 ////////////////////////////////////////////////////////////////////////////////
  1099 //// mozIStorageConnection
  1101 NS_IMETHODIMP
  1102 Connection::Close()
  1104   if (!mDBConn)
  1105     return NS_ERROR_NOT_INITIALIZED;
  1107   { // Make sure we have not executed any asynchronous statements.
  1108     // If this fails, the mDBConn will be left open, resulting in a leak.
  1109     // Ideally we'd schedule some code to destroy the mDBConn once all its
  1110     // async statements have finished executing;  see bug 704030.
  1111     MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
  1112     bool asyncCloseWasCalled = !mAsyncExecutionThread;
  1113     NS_ENSURE_TRUE(asyncCloseWasCalled, NS_ERROR_UNEXPECTED);
  1116   // setClosedState nullifies our connection pointer, so we take a raw pointer
  1117   // off it, to pass it through the close procedure.
  1118   sqlite3 *nativeConn = mDBConn;
  1119   nsresult rv = setClosedState();
  1120   NS_ENSURE_SUCCESS(rv, rv);
  1122   return internalClose(nativeConn);
  1125 NS_IMETHODIMP
  1126 Connection::AsyncClose(mozIStorageCompletionCallback *aCallback)
  1128   if (!NS_IsMainThread()) {
  1129     return NS_ERROR_NOT_SAME_THREAD;
  1131   if (!mDBConn)
  1132     return NS_ERROR_NOT_INITIALIZED;
  1134   nsIEventTarget *asyncThread = getAsyncExecutionTarget();
  1135   NS_ENSURE_TRUE(asyncThread, NS_ERROR_NOT_INITIALIZED);
  1137   // setClosedState nullifies our connection pointer, so we take a raw pointer
  1138   // off it, to pass it through the close procedure.
  1139   sqlite3 *nativeConn = mDBConn;
  1140   nsresult rv = setClosedState();
  1141   NS_ENSURE_SUCCESS(rv, rv);
  1143   // Create our callback event if we were given a callback.
  1144   nsCOMPtr<nsIRunnable> completeEvent;
  1145   if (aCallback) {
  1146     completeEvent = newCompletionEvent(aCallback);
  1149   // Create and dispatch our close event to the background thread.
  1150   nsCOMPtr<nsIRunnable> closeEvent;
  1152     // We need to lock because we're modifying mAsyncExecutionThread
  1153     MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
  1154     closeEvent = new AsyncCloseConnection(this,
  1155                                           nativeConn,
  1156                                           completeEvent,
  1157                                           mAsyncExecutionThread.forget());
  1160   rv = asyncThread->Dispatch(closeEvent, NS_DISPATCH_NORMAL);
  1161   NS_ENSURE_SUCCESS(rv, rv);
  1163   return NS_OK;
  1166 NS_IMETHODIMP
  1167 Connection::AsyncClone(bool aReadOnly,
  1168                        mozIStorageCompletionCallback *aCallback)
  1170   PROFILER_LABEL("storage", "Connection::Clone");
  1171   if (!NS_IsMainThread()) {
  1172     return NS_ERROR_NOT_SAME_THREAD;
  1174   if (!mDBConn)
  1175     return NS_ERROR_NOT_INITIALIZED;
  1176   if (!mDatabaseFile)
  1177     return NS_ERROR_UNEXPECTED;
  1179   int flags = mFlags;
  1180   if (aReadOnly) {
  1181     // Turn off SQLITE_OPEN_READWRITE, and set SQLITE_OPEN_READONLY.
  1182     flags = (~SQLITE_OPEN_READWRITE & flags) | SQLITE_OPEN_READONLY;
  1183     // Turn off SQLITE_OPEN_CREATE.
  1184     flags = (~SQLITE_OPEN_CREATE & flags);
  1187   nsRefPtr<Connection> clone = new Connection(mStorageService, flags,
  1188                                               mAsyncOnly);
  1190   nsRefPtr<AsyncInitializeClone> initEvent =
  1191     new AsyncInitializeClone(this, clone, aReadOnly, aCallback);
  1192   nsCOMPtr<nsIEventTarget> target = clone->getAsyncExecutionTarget();
  1193   if (!target) {
  1194     return NS_ERROR_UNEXPECTED;
  1196   return target->Dispatch(initEvent, NS_DISPATCH_NORMAL);
  1199 nsresult
  1200 Connection::initializeClone(Connection* aClone, bool aReadOnly)
  1202   nsresult rv = mFileURL ? aClone->initialize(mFileURL)
  1203                          : aClone->initialize(mDatabaseFile);
  1204   if (NS_FAILED(rv)) {
  1205     return rv;
  1208   // Copy over pragmas from the original connection.
  1209   static const char * pragmas[] = {
  1210     "cache_size",
  1211     "temp_store",
  1212     "foreign_keys",
  1213     "journal_size_limit",
  1214     "synchronous",
  1215     "wal_autocheckpoint",
  1216   };
  1217   for (uint32_t i = 0; i < ArrayLength(pragmas); ++i) {
  1218     // Read-only connections just need cache_size and temp_store pragmas.
  1219     if (aReadOnly && ::strcmp(pragmas[i], "cache_size") != 0 &&
  1220                      ::strcmp(pragmas[i], "temp_store") != 0) {
  1221       continue;
  1224     nsAutoCString pragmaQuery("PRAGMA ");
  1225     pragmaQuery.Append(pragmas[i]);
  1226     nsCOMPtr<mozIStorageStatement> stmt;
  1227     rv = CreateStatement(pragmaQuery, getter_AddRefs(stmt));
  1228     MOZ_ASSERT(NS_SUCCEEDED(rv));
  1229     bool hasResult = false;
  1230     if (stmt && NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
  1231       pragmaQuery.AppendLiteral(" = ");
  1232       pragmaQuery.AppendInt(stmt->AsInt32(0));
  1233       rv = aClone->ExecuteSimpleSQL(pragmaQuery);
  1234       MOZ_ASSERT(NS_SUCCEEDED(rv));
  1238   // Copy any functions that have been added to this connection.
  1239   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
  1240   (void)mFunctions.EnumerateRead(copyFunctionEnumerator, aClone);
  1242   return NS_OK;
  1245 NS_IMETHODIMP
  1246 Connection::Clone(bool aReadOnly,
  1247                   mozIStorageConnection **_connection)
  1249   MOZ_ASSERT(threadOpenedOn == NS_GetCurrentThread());
  1251   PROFILER_LABEL("storage", "Connection::Clone");
  1252   if (!mDBConn)
  1253     return NS_ERROR_NOT_INITIALIZED;
  1254   if (!mDatabaseFile)
  1255     return NS_ERROR_UNEXPECTED;
  1257   int flags = mFlags;
  1258   if (aReadOnly) {
  1259     // Turn off SQLITE_OPEN_READWRITE, and set SQLITE_OPEN_READONLY.
  1260     flags = (~SQLITE_OPEN_READWRITE & flags) | SQLITE_OPEN_READONLY;
  1261     // Turn off SQLITE_OPEN_CREATE.
  1262     flags = (~SQLITE_OPEN_CREATE & flags);
  1265   nsRefPtr<Connection> clone = new Connection(mStorageService, flags,
  1266                                               mAsyncOnly);
  1268   nsresult rv = initializeClone(clone, aReadOnly);
  1269   if (NS_FAILED(rv)) {
  1270     return rv;
  1273   NS_IF_ADDREF(*_connection = clone);
  1274   return NS_OK;
  1277 NS_IMETHODIMP
  1278 Connection::GetDefaultPageSize(int32_t *_defaultPageSize)
  1280   *_defaultPageSize = Service::getDefaultPageSize();
  1281   return NS_OK;
  1284 NS_IMETHODIMP
  1285 Connection::GetConnectionReady(bool *_ready)
  1287   *_ready = connectionReady();
  1288   return NS_OK;
  1291 NS_IMETHODIMP
  1292 Connection::GetDatabaseFile(nsIFile **_dbFile)
  1294   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1296   NS_IF_ADDREF(*_dbFile = mDatabaseFile);
  1298   return NS_OK;
  1301 NS_IMETHODIMP
  1302 Connection::GetLastInsertRowID(int64_t *_id)
  1304   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1306   sqlite_int64 id = ::sqlite3_last_insert_rowid(mDBConn);
  1307   *_id = id;
  1309   return NS_OK;
  1312 NS_IMETHODIMP
  1313 Connection::GetAffectedRows(int32_t *_rows)
  1315   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1317   *_rows = ::sqlite3_changes(mDBConn);
  1319   return NS_OK;
  1322 NS_IMETHODIMP
  1323 Connection::GetLastError(int32_t *_error)
  1325   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1327   *_error = ::sqlite3_errcode(mDBConn);
  1329   return NS_OK;
  1332 NS_IMETHODIMP
  1333 Connection::GetLastErrorString(nsACString &_errorString)
  1335   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1337   const char *serr = ::sqlite3_errmsg(mDBConn);
  1338   _errorString.Assign(serr);
  1340   return NS_OK;
  1343 NS_IMETHODIMP
  1344 Connection::GetSchemaVersion(int32_t *_version)
  1346   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1348   nsCOMPtr<mozIStorageStatement> stmt;
  1349   (void)CreateStatement(NS_LITERAL_CSTRING("PRAGMA user_version"),
  1350                         getter_AddRefs(stmt));
  1351   NS_ENSURE_TRUE(stmt, NS_ERROR_OUT_OF_MEMORY);
  1353   *_version = 0;
  1354   bool hasResult;
  1355   if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult)
  1356     *_version = stmt->AsInt32(0);
  1358   return NS_OK;
  1361 NS_IMETHODIMP
  1362 Connection::SetSchemaVersion(int32_t aVersion)
  1364   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1366   nsAutoCString stmt(NS_LITERAL_CSTRING("PRAGMA user_version = "));
  1367   stmt.AppendInt(aVersion);
  1369   return ExecuteSimpleSQL(stmt);
  1372 NS_IMETHODIMP
  1373 Connection::CreateStatement(const nsACString &aSQLStatement,
  1374                             mozIStorageStatement **_stmt)
  1376   NS_ENSURE_ARG_POINTER(_stmt);
  1377   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1379   nsRefPtr<Statement> statement(new Statement());
  1380   NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
  1382   nsresult rv = statement->initialize(this, mDBConn, aSQLStatement);
  1383   NS_ENSURE_SUCCESS(rv, rv);
  1385   Statement *rawPtr;
  1386   statement.forget(&rawPtr);
  1387   *_stmt = rawPtr;
  1388   return NS_OK;
  1391 NS_IMETHODIMP
  1392 Connection::CreateAsyncStatement(const nsACString &aSQLStatement,
  1393                                  mozIStorageAsyncStatement **_stmt)
  1395   NS_ENSURE_ARG_POINTER(_stmt);
  1396   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1398   nsRefPtr<AsyncStatement> statement(new AsyncStatement());
  1399   NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
  1401   nsresult rv = statement->initialize(this, mDBConn, aSQLStatement);
  1402   NS_ENSURE_SUCCESS(rv, rv);
  1404   AsyncStatement *rawPtr;
  1405   statement.forget(&rawPtr);
  1406   *_stmt = rawPtr;
  1407   return NS_OK;
  1410 NS_IMETHODIMP
  1411 Connection::ExecuteSimpleSQL(const nsACString &aSQLStatement)
  1413   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1415   int srv = executeSql(mDBConn, PromiseFlatCString(aSQLStatement).get());
  1416   return convertResultCode(srv);
  1419 NS_IMETHODIMP
  1420 Connection::ExecuteAsync(mozIStorageBaseStatement **aStatements,
  1421                          uint32_t aNumStatements,
  1422                          mozIStorageStatementCallback *aCallback,
  1423                          mozIStoragePendingStatement **_handle)
  1425   nsTArray<StatementData> stmts(aNumStatements);
  1426   for (uint32_t i = 0; i < aNumStatements; i++) {
  1427     nsCOMPtr<StorageBaseStatementInternal> stmt = 
  1428       do_QueryInterface(aStatements[i]);
  1430     // Obtain our StatementData.
  1431     StatementData data;
  1432     nsresult rv = stmt->getAsynchronousStatementData(data);
  1433     NS_ENSURE_SUCCESS(rv, rv);
  1435     NS_ASSERTION(stmt->getOwner() == this,
  1436                  "Statement must be from this database connection!");
  1438     // Now append it to our array.
  1439     NS_ENSURE_TRUE(stmts.AppendElement(data), NS_ERROR_OUT_OF_MEMORY);
  1442   // Dispatch to the background
  1443   return AsyncExecuteStatements::execute(stmts, this, mDBConn, aCallback,
  1444                                          _handle);
  1447 NS_IMETHODIMP
  1448 Connection::ExecuteSimpleSQLAsync(const nsACString &aSQLStatement,
  1449                                   mozIStorageStatementCallback *aCallback,
  1450                                   mozIStoragePendingStatement **_handle)
  1452   if (!NS_IsMainThread()) {
  1453     return NS_ERROR_NOT_SAME_THREAD;
  1456   nsCOMPtr<mozIStorageAsyncStatement> stmt;
  1457   nsresult rv = CreateAsyncStatement(aSQLStatement, getter_AddRefs(stmt));
  1458   if (NS_FAILED(rv)) {
  1459     return rv;
  1462   nsCOMPtr<mozIStoragePendingStatement> pendingStatement;
  1463   rv = stmt->ExecuteAsync(aCallback, getter_AddRefs(pendingStatement));
  1464   if (NS_FAILED(rv)) {
  1465     return rv;
  1468   NS_ADDREF(*_handle = pendingStatement);
  1469   return rv;
  1472 NS_IMETHODIMP
  1473 Connection::TableExists(const nsACString &aTableName,
  1474                         bool *_exists)
  1476     return databaseElementExists(TABLE, aTableName, _exists);
  1479 NS_IMETHODIMP
  1480 Connection::IndexExists(const nsACString &aIndexName,
  1481                         bool* _exists)
  1483     return databaseElementExists(INDEX, aIndexName, _exists);
  1486 NS_IMETHODIMP
  1487 Connection::GetTransactionInProgress(bool *_inProgress)
  1489   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1491   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
  1492   *_inProgress = mTransactionInProgress;
  1493   return NS_OK;
  1496 NS_IMETHODIMP
  1497 Connection::BeginTransaction()
  1499   return BeginTransactionAs(mozIStorageConnection::TRANSACTION_DEFERRED);
  1502 NS_IMETHODIMP
  1503 Connection::BeginTransactionAs(int32_t aTransactionType)
  1505   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1507   return beginTransactionInternal(mDBConn, aTransactionType);
  1510 nsresult
  1511 Connection::beginTransactionInternal(sqlite3 *aNativeConnection,
  1512                                      int32_t aTransactionType)
  1514   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
  1515   if (mTransactionInProgress)
  1516     return NS_ERROR_FAILURE;
  1517   nsresult rv;
  1518   switch(aTransactionType) {
  1519     case TRANSACTION_DEFERRED:
  1520       rv = convertResultCode(executeSql(aNativeConnection, "BEGIN DEFERRED"));
  1521       break;
  1522     case TRANSACTION_IMMEDIATE:
  1523       rv = convertResultCode(executeSql(aNativeConnection, "BEGIN IMMEDIATE"));
  1524       break;
  1525     case TRANSACTION_EXCLUSIVE:
  1526       rv = convertResultCode(executeSql(aNativeConnection, "BEGIN EXCLUSIVE"));
  1527       break;
  1528     default:
  1529       return NS_ERROR_ILLEGAL_VALUE;
  1531   if (NS_SUCCEEDED(rv))
  1532     mTransactionInProgress = true;
  1533   return rv;
  1536 NS_IMETHODIMP
  1537 Connection::CommitTransaction()
  1539   if (!mDBConn)
  1540     return NS_ERROR_NOT_INITIALIZED;
  1542   return commitTransactionInternal(mDBConn);
  1545 nsresult
  1546 Connection::commitTransactionInternal(sqlite3 *aNativeConnection)
  1548   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
  1549   if (!mTransactionInProgress)
  1550     return NS_ERROR_UNEXPECTED;
  1551   nsresult rv =
  1552     convertResultCode(executeSql(aNativeConnection, "COMMIT TRANSACTION"));
  1553   if (NS_SUCCEEDED(rv))
  1554     mTransactionInProgress = false;
  1555   return rv;
  1558 NS_IMETHODIMP
  1559 Connection::RollbackTransaction()
  1561   if (!mDBConn)
  1562     return NS_ERROR_NOT_INITIALIZED;
  1564   return rollbackTransactionInternal(mDBConn);
  1567 nsresult
  1568 Connection::rollbackTransactionInternal(sqlite3 *aNativeConnection)
  1570   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
  1571   if (!mTransactionInProgress)
  1572     return NS_ERROR_UNEXPECTED;
  1574   nsresult rv =
  1575     convertResultCode(executeSql(aNativeConnection, "ROLLBACK TRANSACTION"));
  1576   if (NS_SUCCEEDED(rv))
  1577     mTransactionInProgress = false;
  1578   return rv;
  1581 NS_IMETHODIMP
  1582 Connection::CreateTable(const char *aTableName,
  1583                         const char *aTableSchema)
  1585   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1587   char *buf = ::PR_smprintf("CREATE TABLE %s (%s)", aTableName, aTableSchema);
  1588   if (!buf)
  1589     return NS_ERROR_OUT_OF_MEMORY;
  1591   int srv = executeSql(mDBConn, buf);
  1592   ::PR_smprintf_free(buf);
  1594   return convertResultCode(srv);
  1597 NS_IMETHODIMP
  1598 Connection::CreateFunction(const nsACString &aFunctionName,
  1599                            int32_t aNumArguments,
  1600                            mozIStorageFunction *aFunction)
  1602   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1604   // Check to see if this function is already defined.  We only check the name
  1605   // because a function can be defined with the same body but different names.
  1606   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
  1607   NS_ENSURE_FALSE(mFunctions.Get(aFunctionName, nullptr), NS_ERROR_FAILURE);
  1609   int srv = ::sqlite3_create_function(mDBConn,
  1610                                       nsPromiseFlatCString(aFunctionName).get(),
  1611                                       aNumArguments,
  1612                                       SQLITE_ANY,
  1613                                       aFunction,
  1614                                       basicFunctionHelper,
  1615                                       nullptr,
  1616                                       nullptr);
  1617   if (srv != SQLITE_OK)
  1618     return convertResultCode(srv);
  1620   FunctionInfo info = { aFunction,
  1621                         Connection::FunctionInfo::SIMPLE,
  1622                         aNumArguments };
  1623   mFunctions.Put(aFunctionName, info);
  1625   return NS_OK;
  1628 NS_IMETHODIMP
  1629 Connection::CreateAggregateFunction(const nsACString &aFunctionName,
  1630                                     int32_t aNumArguments,
  1631                                     mozIStorageAggregateFunction *aFunction)
  1633   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1635   // Check to see if this function name is already defined.
  1636   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
  1637   NS_ENSURE_FALSE(mFunctions.Get(aFunctionName, nullptr), NS_ERROR_FAILURE);
  1639   // Because aggregate functions depend on state across calls, you cannot have
  1640   // the same instance use the same name.  We want to enumerate all functions
  1641   // and make sure this instance is not already registered.
  1642   NS_ENSURE_FALSE(findFunctionByInstance(aFunction), NS_ERROR_FAILURE);
  1644   int srv = ::sqlite3_create_function(mDBConn,
  1645                                       nsPromiseFlatCString(aFunctionName).get(),
  1646                                       aNumArguments,
  1647                                       SQLITE_ANY,
  1648                                       aFunction,
  1649                                       nullptr,
  1650                                       aggregateFunctionStepHelper,
  1651                                       aggregateFunctionFinalHelper);
  1652   if (srv != SQLITE_OK)
  1653     return convertResultCode(srv);
  1655   FunctionInfo info = { aFunction,
  1656                         Connection::FunctionInfo::AGGREGATE,
  1657                         aNumArguments };
  1658   mFunctions.Put(aFunctionName, info);
  1660   return NS_OK;
  1663 NS_IMETHODIMP
  1664 Connection::RemoveFunction(const nsACString &aFunctionName)
  1666   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1668   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
  1669   NS_ENSURE_TRUE(mFunctions.Get(aFunctionName, nullptr), NS_ERROR_FAILURE);
  1671   int srv = ::sqlite3_create_function(mDBConn,
  1672                                       nsPromiseFlatCString(aFunctionName).get(),
  1673                                       0,
  1674                                       SQLITE_ANY,
  1675                                       nullptr,
  1676                                       nullptr,
  1677                                       nullptr,
  1678                                       nullptr);
  1679   if (srv != SQLITE_OK)
  1680     return convertResultCode(srv);
  1682   mFunctions.Remove(aFunctionName);
  1684   return NS_OK;
  1687 NS_IMETHODIMP
  1688 Connection::SetProgressHandler(int32_t aGranularity,
  1689                                mozIStorageProgressHandler *aHandler,
  1690                                mozIStorageProgressHandler **_oldHandler)
  1692   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1694   // Return previous one
  1695   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
  1696   NS_IF_ADDREF(*_oldHandler = mProgressHandler);
  1698   if (!aHandler || aGranularity <= 0) {
  1699     aHandler = nullptr;
  1700     aGranularity = 0;
  1702   mProgressHandler = aHandler;
  1703   ::sqlite3_progress_handler(mDBConn, aGranularity, sProgressHelper, this);
  1705   return NS_OK;
  1708 NS_IMETHODIMP
  1709 Connection::RemoveProgressHandler(mozIStorageProgressHandler **_oldHandler)
  1711   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1713   // Return previous one
  1714   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
  1715   NS_IF_ADDREF(*_oldHandler = mProgressHandler);
  1717   mProgressHandler = nullptr;
  1718   ::sqlite3_progress_handler(mDBConn, 0, nullptr, nullptr);
  1720   return NS_OK;
  1723 NS_IMETHODIMP
  1724 Connection::SetGrowthIncrement(int32_t aChunkSize, const nsACString &aDatabaseName)
  1726   // Bug 597215: Disk space is extremely limited on Android
  1727   // so don't preallocate space. This is also not effective
  1728   // on log structured file systems used by Android devices
  1729 #if !defined(ANDROID) && !defined(MOZ_PLATFORM_MAEMO)
  1730   // Don't preallocate if less than 500MiB is available.
  1731   int64_t bytesAvailable;
  1732   nsresult rv = mDatabaseFile->GetDiskSpaceAvailable(&bytesAvailable);
  1733   NS_ENSURE_SUCCESS(rv, rv);
  1734   if (bytesAvailable < MIN_AVAILABLE_BYTES_PER_CHUNKED_GROWTH) {
  1735     return NS_ERROR_FILE_TOO_BIG;
  1738   (void)::sqlite3_file_control(mDBConn,
  1739                                aDatabaseName.Length() ? nsPromiseFlatCString(aDatabaseName).get()
  1740                                                       : nullptr,
  1741                                SQLITE_FCNTL_CHUNK_SIZE,
  1742                                &aChunkSize);
  1743 #endif
  1744   return NS_OK;
  1747 NS_IMETHODIMP
  1748 Connection::EnableModule(const nsACString& aModuleName)
  1750   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1752   for (size_t i = 0; i < ArrayLength(gModules); i++) {
  1753     struct Module* m = &gModules[i];
  1754     if (aModuleName.Equals(m->name)) {
  1755       int srv = m->registerFunc(mDBConn, m->name);
  1756       if (srv != SQLITE_OK)
  1757         return convertResultCode(srv);
  1759       return NS_OK;
  1763   return NS_ERROR_FAILURE;
  1766 } // namespace storage
  1767 } // namespace mozilla

mercurial