storage/src/mozStorageConnection.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/storage/src/mozStorageConnection.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1767 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include <stdio.h>
    1.11 +
    1.12 +#include "nsError.h"
    1.13 +#include "nsIMutableArray.h"
    1.14 +#include "nsAutoPtr.h"
    1.15 +#include "nsIMemoryReporter.h"
    1.16 +#include "nsThreadUtils.h"
    1.17 +#include "nsIFile.h"
    1.18 +#include "nsIFileURL.h"
    1.19 +#include "mozilla/Telemetry.h"
    1.20 +#include "mozilla/Mutex.h"
    1.21 +#include "mozilla/CondVar.h"
    1.22 +#include "mozilla/Attributes.h"
    1.23 +
    1.24 +#include "mozIStorageAggregateFunction.h"
    1.25 +#include "mozIStorageCompletionCallback.h"
    1.26 +#include "mozIStorageFunction.h"
    1.27 +
    1.28 +#include "mozStorageAsyncStatementExecution.h"
    1.29 +#include "mozStorageSQLFunctions.h"
    1.30 +#include "mozStorageConnection.h"
    1.31 +#include "mozStorageService.h"
    1.32 +#include "mozStorageStatement.h"
    1.33 +#include "mozStorageAsyncStatement.h"
    1.34 +#include "mozStorageArgValueArray.h"
    1.35 +#include "mozStoragePrivateHelpers.h"
    1.36 +#include "mozStorageStatementData.h"
    1.37 +#include "StorageBaseStatementInternal.h"
    1.38 +#include "SQLCollations.h"
    1.39 +#include "FileSystemModule.h"
    1.40 +#include "mozStorageHelper.h"
    1.41 +#include "GeckoProfiler.h"
    1.42 +
    1.43 +#include "prlog.h"
    1.44 +#include "prprf.h"
    1.45 +#include "nsProxyRelease.h"
    1.46 +#include <algorithm>
    1.47 +
    1.48 +#define MIN_AVAILABLE_BYTES_PER_CHUNKED_GROWTH 524288000 // 500 MiB
    1.49 +
    1.50 +// Maximum size of the pages cache per connection.
    1.51 +#define MAX_CACHE_SIZE_KIBIBYTES 2048 // 2 MiB
    1.52 +
    1.53 +#ifdef PR_LOGGING
    1.54 +PRLogModuleInfo* gStorageLog = nullptr;
    1.55 +#endif
    1.56 +
    1.57 +namespace mozilla {
    1.58 +namespace storage {
    1.59 +
    1.60 +namespace {
    1.61 +
    1.62 +////////////////////////////////////////////////////////////////////////////////
    1.63 +//// Variant Specialization Functions (variantToSQLiteT)
    1.64 +
    1.65 +int
    1.66 +sqlite3_T_int(sqlite3_context *aCtx,
    1.67 +              int aValue)
    1.68 +{
    1.69 +  ::sqlite3_result_int(aCtx, aValue);
    1.70 +  return SQLITE_OK;
    1.71 +}
    1.72 +
    1.73 +int
    1.74 +sqlite3_T_int64(sqlite3_context *aCtx,
    1.75 +                sqlite3_int64 aValue)
    1.76 +{
    1.77 +  ::sqlite3_result_int64(aCtx, aValue);
    1.78 +  return SQLITE_OK;
    1.79 +}
    1.80 +
    1.81 +int
    1.82 +sqlite3_T_double(sqlite3_context *aCtx,
    1.83 +                 double aValue)
    1.84 +{
    1.85 +  ::sqlite3_result_double(aCtx, aValue);
    1.86 +  return SQLITE_OK;
    1.87 +}
    1.88 +
    1.89 +int
    1.90 +sqlite3_T_text(sqlite3_context *aCtx,
    1.91 +               const nsCString &aValue)
    1.92 +{
    1.93 +  ::sqlite3_result_text(aCtx,
    1.94 +                        aValue.get(),
    1.95 +                        aValue.Length(),
    1.96 +                        SQLITE_TRANSIENT);
    1.97 +  return SQLITE_OK;
    1.98 +}
    1.99 +
   1.100 +int
   1.101 +sqlite3_T_text16(sqlite3_context *aCtx,
   1.102 +                 const nsString &aValue)
   1.103 +{
   1.104 +  ::sqlite3_result_text16(aCtx,
   1.105 +                          aValue.get(),
   1.106 +                          aValue.Length() * 2, // Number of bytes.
   1.107 +                          SQLITE_TRANSIENT);
   1.108 +  return SQLITE_OK;
   1.109 +}
   1.110 +
   1.111 +int
   1.112 +sqlite3_T_null(sqlite3_context *aCtx)
   1.113 +{
   1.114 +  ::sqlite3_result_null(aCtx);
   1.115 +  return SQLITE_OK;
   1.116 +}
   1.117 +
   1.118 +int
   1.119 +sqlite3_T_blob(sqlite3_context *aCtx,
   1.120 +               const void *aData,
   1.121 +               int aSize)
   1.122 +{
   1.123 +  ::sqlite3_result_blob(aCtx, aData, aSize, NS_Free);
   1.124 +  return SQLITE_OK;
   1.125 +}
   1.126 +
   1.127 +#include "variantToSQLiteT_impl.h"
   1.128 +
   1.129 +////////////////////////////////////////////////////////////////////////////////
   1.130 +//// Modules
   1.131 +
   1.132 +struct Module
   1.133 +{
   1.134 +  const char* name;
   1.135 +  int (*registerFunc)(sqlite3*, const char*);
   1.136 +};
   1.137 +
   1.138 +Module gModules[] = {
   1.139 +  { "filesystem", RegisterFileSystemModule }
   1.140 +};
   1.141 +
   1.142 +////////////////////////////////////////////////////////////////////////////////
   1.143 +//// Local Functions
   1.144 +
   1.145 +#ifdef PR_LOGGING
   1.146 +void tracefunc (void *aClosure, const char *aStmt)
   1.147 +{
   1.148 +  PR_LOG(gStorageLog, PR_LOG_DEBUG, ("sqlite3_trace on %p for '%s'", aClosure,
   1.149 +                                     aStmt));
   1.150 +}
   1.151 +#endif
   1.152 +
   1.153 +struct FFEArguments
   1.154 +{
   1.155 +    nsISupports *target;
   1.156 +    bool found;
   1.157 +};
   1.158 +PLDHashOperator
   1.159 +findFunctionEnumerator(const nsACString &aKey,
   1.160 +                       Connection::FunctionInfo aData,
   1.161 +                       void *aUserArg)
   1.162 +{
   1.163 +  FFEArguments *args = static_cast<FFEArguments *>(aUserArg);
   1.164 +  if (aData.function == args->target) {
   1.165 +    args->found = true;
   1.166 +    return PL_DHASH_STOP;
   1.167 +  }
   1.168 +  return PL_DHASH_NEXT;
   1.169 +}
   1.170 +
   1.171 +PLDHashOperator
   1.172 +copyFunctionEnumerator(const nsACString &aKey,
   1.173 +                       Connection::FunctionInfo aData,
   1.174 +                       void *aUserArg)
   1.175 +{
   1.176 +  NS_PRECONDITION(aData.type == Connection::FunctionInfo::SIMPLE ||
   1.177 +                  aData.type == Connection::FunctionInfo::AGGREGATE,
   1.178 +                  "Invalid function type!");
   1.179 +
   1.180 +  Connection *connection = static_cast<Connection *>(aUserArg);
   1.181 +  if (aData.type == Connection::FunctionInfo::SIMPLE) {
   1.182 +    mozIStorageFunction *function =
   1.183 +      static_cast<mozIStorageFunction *>(aData.function.get());
   1.184 +    (void)connection->CreateFunction(aKey, aData.numArgs, function);
   1.185 +  }
   1.186 +  else {
   1.187 +    mozIStorageAggregateFunction *function =
   1.188 +      static_cast<mozIStorageAggregateFunction *>(aData.function.get());
   1.189 +    (void)connection->CreateAggregateFunction(aKey, aData.numArgs, function);
   1.190 +  }
   1.191 +
   1.192 +  return PL_DHASH_NEXT;
   1.193 +}
   1.194 +
   1.195 +void
   1.196 +basicFunctionHelper(sqlite3_context *aCtx,
   1.197 +                    int aArgc,
   1.198 +                    sqlite3_value **aArgv)
   1.199 +{
   1.200 +  void *userData = ::sqlite3_user_data(aCtx);
   1.201 +
   1.202 +  mozIStorageFunction *func = static_cast<mozIStorageFunction *>(userData);
   1.203 +
   1.204 +  nsRefPtr<ArgValueArray> arguments(new ArgValueArray(aArgc, aArgv));
   1.205 +  if (!arguments)
   1.206 +      return;
   1.207 +
   1.208 +  nsCOMPtr<nsIVariant> result;
   1.209 +  if (NS_FAILED(func->OnFunctionCall(arguments, getter_AddRefs(result)))) {
   1.210 +    NS_WARNING("User function returned error code!");
   1.211 +    ::sqlite3_result_error(aCtx,
   1.212 +                           "User function returned error code",
   1.213 +                           -1);
   1.214 +    return;
   1.215 +  }
   1.216 +  int retcode = variantToSQLiteT(aCtx, result);
   1.217 +  if (retcode == SQLITE_IGNORE) {
   1.218 +    ::sqlite3_result_int(aCtx, SQLITE_IGNORE);
   1.219 +  } else if (retcode != SQLITE_OK) {
   1.220 +    NS_WARNING("User function returned invalid data type!");
   1.221 +    ::sqlite3_result_error(aCtx,
   1.222 +                           "User function returned invalid data type",
   1.223 +                           -1);
   1.224 +  }
   1.225 +}
   1.226 +
   1.227 +void
   1.228 +aggregateFunctionStepHelper(sqlite3_context *aCtx,
   1.229 +                            int aArgc,
   1.230 +                            sqlite3_value **aArgv)
   1.231 +{
   1.232 +  void *userData = ::sqlite3_user_data(aCtx);
   1.233 +  mozIStorageAggregateFunction *func =
   1.234 +    static_cast<mozIStorageAggregateFunction *>(userData);
   1.235 +
   1.236 +  nsRefPtr<ArgValueArray> arguments(new ArgValueArray(aArgc, aArgv));
   1.237 +  if (!arguments)
   1.238 +    return;
   1.239 +
   1.240 +  if (NS_FAILED(func->OnStep(arguments)))
   1.241 +    NS_WARNING("User aggregate step function returned error code!");
   1.242 +}
   1.243 +
   1.244 +void
   1.245 +aggregateFunctionFinalHelper(sqlite3_context *aCtx)
   1.246 +{
   1.247 +  void *userData = ::sqlite3_user_data(aCtx);
   1.248 +  mozIStorageAggregateFunction *func =
   1.249 +    static_cast<mozIStorageAggregateFunction *>(userData);
   1.250 +
   1.251 +  nsRefPtr<nsIVariant> result;
   1.252 +  if (NS_FAILED(func->OnFinal(getter_AddRefs(result)))) {
   1.253 +    NS_WARNING("User aggregate final function returned error code!");
   1.254 +    ::sqlite3_result_error(aCtx,
   1.255 +                           "User aggregate final function returned error code",
   1.256 +                           -1);
   1.257 +    return;
   1.258 +  }
   1.259 +
   1.260 +  if (variantToSQLiteT(aCtx, result) != SQLITE_OK) {
   1.261 +    NS_WARNING("User aggregate final function returned invalid data type!");
   1.262 +    ::sqlite3_result_error(aCtx,
   1.263 +                           "User aggregate final function returned invalid data type",
   1.264 +                           -1);
   1.265 +  }
   1.266 +}
   1.267 +
   1.268 +/**
   1.269 + * This code is heavily based on the sample at:
   1.270 + *   http://www.sqlite.org/unlock_notify.html
   1.271 + */
   1.272 +class UnlockNotification
   1.273 +{
   1.274 +public:
   1.275 +  UnlockNotification()
   1.276 +  : mMutex("UnlockNotification mMutex")
   1.277 +  , mCondVar(mMutex, "UnlockNotification condVar")
   1.278 +  , mSignaled(false)
   1.279 +  {
   1.280 +  }
   1.281 +
   1.282 +  void Wait()
   1.283 +  {
   1.284 +    MutexAutoLock lock(mMutex);
   1.285 +    while (!mSignaled) {
   1.286 +      (void)mCondVar.Wait();
   1.287 +    }
   1.288 +  }
   1.289 +
   1.290 +  void Signal()
   1.291 +  {
   1.292 +    MutexAutoLock lock(mMutex);
   1.293 +    mSignaled = true;
   1.294 +    (void)mCondVar.Notify();
   1.295 +  }
   1.296 +
   1.297 +private:
   1.298 +  Mutex mMutex;
   1.299 +  CondVar mCondVar;
   1.300 +  bool mSignaled;
   1.301 +};
   1.302 +
   1.303 +void
   1.304 +UnlockNotifyCallback(void **aArgs,
   1.305 +                     int aArgsSize)
   1.306 +{
   1.307 +  for (int i = 0; i < aArgsSize; i++) {
   1.308 +    UnlockNotification *notification =
   1.309 +      static_cast<UnlockNotification *>(aArgs[i]);
   1.310 +    notification->Signal();
   1.311 +  }
   1.312 +}
   1.313 +
   1.314 +int
   1.315 +WaitForUnlockNotify(sqlite3* aDatabase)
   1.316 +{
   1.317 +  UnlockNotification notification;
   1.318 +  int srv = ::sqlite3_unlock_notify(aDatabase, UnlockNotifyCallback,
   1.319 +                                    &notification);
   1.320 +  MOZ_ASSERT(srv == SQLITE_LOCKED || srv == SQLITE_OK);
   1.321 +  if (srv == SQLITE_OK) {
   1.322 +    notification.Wait();
   1.323 +  }
   1.324 +
   1.325 +  return srv;
   1.326 +}
   1.327 +
   1.328 +} // anonymous namespace
   1.329 +
   1.330 +////////////////////////////////////////////////////////////////////////////////
   1.331 +//// Local Classes
   1.332 +
   1.333 +namespace {
   1.334 +
   1.335 +class AsyncCloseConnection MOZ_FINAL: public nsRunnable
   1.336 +{
   1.337 +public:
   1.338 +  AsyncCloseConnection(Connection *aConnection,
   1.339 +                       sqlite3 *aNativeConnection,
   1.340 +                       nsIRunnable *aCallbackEvent,
   1.341 +                       already_AddRefed<nsIThread> aAsyncExecutionThread)
   1.342 +  : mConnection(aConnection)
   1.343 +  , mNativeConnection(aNativeConnection)
   1.344 +  , mCallbackEvent(aCallbackEvent)
   1.345 +  , mAsyncExecutionThread(aAsyncExecutionThread)
   1.346 +  {
   1.347 +  }
   1.348 +
   1.349 +  NS_METHOD Run()
   1.350 +  {
   1.351 +#ifdef DEBUG
   1.352 +    // This code is executed on the background thread
   1.353 +    bool onAsyncThread = false;
   1.354 +    (void)mAsyncExecutionThread->IsOnCurrentThread(&onAsyncThread);
   1.355 +    MOZ_ASSERT(onAsyncThread);
   1.356 +#endif // DEBUG
   1.357 +
   1.358 +    // Internal close.
   1.359 +    (void)mConnection->internalClose(mNativeConnection);
   1.360 +
   1.361 +    // Callback
   1.362 +    if (mCallbackEvent) {
   1.363 +      nsCOMPtr<nsIThread> thread;
   1.364 +      (void)NS_GetMainThread(getter_AddRefs(thread));
   1.365 +      (void)thread->Dispatch(mCallbackEvent, NS_DISPATCH_NORMAL);
   1.366 +    }
   1.367 +
   1.368 +    return NS_OK;
   1.369 +  }
   1.370 +
   1.371 +  ~AsyncCloseConnection() {
   1.372 +    nsCOMPtr<nsIThread> thread;
   1.373 +    (void)NS_GetMainThread(getter_AddRefs(thread));
   1.374 +    // Handle ambiguous nsISupports inheritance.
   1.375 +    Connection *rawConnection = nullptr;
   1.376 +    mConnection.swap(rawConnection);
   1.377 +    (void)NS_ProxyRelease(thread,
   1.378 +                          NS_ISUPPORTS_CAST(mozIStorageConnection *,
   1.379 +                                            rawConnection));
   1.380 +    (void)NS_ProxyRelease(thread, mCallbackEvent);
   1.381 +  }
   1.382 +private:
   1.383 +  nsRefPtr<Connection> mConnection;
   1.384 +  sqlite3 *mNativeConnection;
   1.385 +  nsCOMPtr<nsIRunnable> mCallbackEvent;
   1.386 +  nsCOMPtr<nsIThread> mAsyncExecutionThread;
   1.387 +};
   1.388 +
   1.389 +/**
   1.390 + * An event used to initialize the clone of a connection.
   1.391 + *
   1.392 + * Must be executed on the clone's async execution thread.
   1.393 + */
   1.394 +class AsyncInitializeClone MOZ_FINAL: public nsRunnable
   1.395 +{
   1.396 +public:
   1.397 +  /**
   1.398 +   * @param aConnection The connection being cloned.
   1.399 +   * @param aClone The clone.
   1.400 +   * @param aReadOnly If |true|, the clone is read only.
   1.401 +   * @param aCallback A callback to trigger once initialization
   1.402 +   *                  is complete. This event will be called on
   1.403 +   *                  aClone->threadOpenedOn.
   1.404 +   */
   1.405 +  AsyncInitializeClone(Connection* aConnection,
   1.406 +                       Connection* aClone,
   1.407 +                       const bool aReadOnly,
   1.408 +                       mozIStorageCompletionCallback* aCallback)
   1.409 +    : mConnection(aConnection)
   1.410 +    , mClone(aClone)
   1.411 +    , mReadOnly(aReadOnly)
   1.412 +    , mCallback(aCallback)
   1.413 +  {
   1.414 +    MOZ_ASSERT(NS_IsMainThread());
   1.415 +  }
   1.416 +
   1.417 +  NS_IMETHOD Run() {
   1.418 +    MOZ_ASSERT (NS_GetCurrentThread() == mClone->getAsyncExecutionTarget());
   1.419 +
   1.420 +    nsresult rv = mConnection->initializeClone(mClone, mReadOnly);
   1.421 +    if (NS_FAILED(rv)) {
   1.422 +      return Dispatch(rv, nullptr);
   1.423 +    }
   1.424 +    return Dispatch(NS_OK,
   1.425 +                    NS_ISUPPORTS_CAST(mozIStorageAsyncConnection*, mClone));
   1.426 +  }
   1.427 +
   1.428 +private:
   1.429 +  nsresult Dispatch(nsresult aResult, nsISupports* aValue) {
   1.430 +    nsRefPtr<CallbackComplete> event = new CallbackComplete(aResult,
   1.431 +                                                            aValue,
   1.432 +                                                            mCallback.forget());
   1.433 +    return mClone->threadOpenedOn->Dispatch(event, NS_DISPATCH_NORMAL);
   1.434 +  }
   1.435 +
   1.436 +  ~AsyncInitializeClone() {
   1.437 +    nsCOMPtr<nsIThread> thread;
   1.438 +    DebugOnly<nsresult> rv = NS_GetMainThread(getter_AddRefs(thread));
   1.439 +    MOZ_ASSERT(NS_SUCCEEDED(rv));
   1.440 +
   1.441 +    // Handle ambiguous nsISupports inheritance.
   1.442 +    Connection *rawConnection = nullptr;
   1.443 +    mConnection.swap(rawConnection);
   1.444 +    (void)NS_ProxyRelease(thread, NS_ISUPPORTS_CAST(mozIStorageConnection *,
   1.445 +                                                    rawConnection));
   1.446 +
   1.447 +    Connection *rawClone = nullptr;
   1.448 +    mClone.swap(rawClone);
   1.449 +    (void)NS_ProxyRelease(thread, NS_ISUPPORTS_CAST(mozIStorageConnection *,
   1.450 +                                                    rawClone));
   1.451 +
   1.452 +    // Generally, the callback will be released by CallbackComplete.
   1.453 +    // However, if for some reason Run() is not executed, we still
   1.454 +    // need to ensure that it is released here.
   1.455 +    mozIStorageCompletionCallback *rawCallback = nullptr;
   1.456 +    mCallback.swap(rawCallback);
   1.457 +    (void)NS_ProxyRelease(thread, rawCallback);
   1.458 +  }
   1.459 +
   1.460 +  nsRefPtr<Connection> mConnection;
   1.461 +  nsRefPtr<Connection> mClone;
   1.462 +  const bool mReadOnly;
   1.463 +  nsCOMPtr<mozIStorageCompletionCallback> mCallback;
   1.464 +};
   1.465 +
   1.466 +} // anonymous namespace
   1.467 +
   1.468 +////////////////////////////////////////////////////////////////////////////////
   1.469 +//// Connection
   1.470 +
   1.471 +Connection::Connection(Service *aService,
   1.472 +                       int aFlags,
   1.473 +                       bool aAsyncOnly)
   1.474 +: sharedAsyncExecutionMutex("Connection::sharedAsyncExecutionMutex")
   1.475 +, sharedDBMutex("Connection::sharedDBMutex")
   1.476 +, threadOpenedOn(do_GetCurrentThread())
   1.477 +, mDBConn(nullptr)
   1.478 +, mAsyncExecutionThreadShuttingDown(false)
   1.479 +, mConnectionClosed(false)
   1.480 +, mTransactionInProgress(false)
   1.481 +, mProgressHandler(nullptr)
   1.482 +, mFlags(aFlags)
   1.483 +, mStorageService(aService)
   1.484 +, mAsyncOnly(aAsyncOnly)
   1.485 +{
   1.486 +  mStorageService->registerConnection(this);
   1.487 +}
   1.488 +
   1.489 +Connection::~Connection()
   1.490 +{
   1.491 +  (void)Close();
   1.492 +
   1.493 +  MOZ_ASSERT(!mAsyncExecutionThread,
   1.494 +             "AsyncClose has not been invoked on this connection!");
   1.495 +}
   1.496 +
   1.497 +NS_IMPL_ADDREF(Connection)
   1.498 +
   1.499 +NS_INTERFACE_MAP_BEGIN(Connection)
   1.500 +  NS_INTERFACE_MAP_ENTRY(mozIStorageAsyncConnection)
   1.501 +  NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   1.502 +  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(mozIStorageConnection, !mAsyncOnly)
   1.503 +  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageConnection)
   1.504 +NS_INTERFACE_MAP_END
   1.505 +
   1.506 +// This is identical to what NS_IMPL_RELEASE provides, but with the
   1.507 +// extra |1 == count| case.
   1.508 +NS_IMETHODIMP_(MozExternalRefCountType) Connection::Release(void)
   1.509 +{
   1.510 +  NS_PRECONDITION(0 != mRefCnt, "dup release");
   1.511 +  nsrefcnt count = --mRefCnt;
   1.512 +  NS_LOG_RELEASE(this, count, "Connection");
   1.513 +  if (1 == count) {
   1.514 +    // If the refcount is 1, the single reference must be from
   1.515 +    // gService->mConnections (in class |Service|).  Which means we can
   1.516 +    // unregister it safely.
   1.517 +    mStorageService->unregisterConnection(this);
   1.518 +  } else if (0 == count) {
   1.519 +    mRefCnt = 1; /* stabilize */
   1.520 +#if 0 /* enable this to find non-threadsafe destructors: */
   1.521 +    NS_ASSERT_OWNINGTHREAD(Connection);
   1.522 +#endif
   1.523 +    delete (this);
   1.524 +    return 0;
   1.525 +  }
   1.526 +  return count;
   1.527 +}
   1.528 +
   1.529 +int32_t
   1.530 +Connection::getSqliteRuntimeStatus(int32_t aStatusOption, int32_t* aMaxValue)
   1.531 +{
   1.532 +  MOZ_ASSERT(mDBConn, "A connection must exist at this point");
   1.533 +  int curr = 0, max = 0;
   1.534 +  DebugOnly<int> rc = ::sqlite3_db_status(mDBConn, aStatusOption, &curr, &max, 0);
   1.535 +  MOZ_ASSERT(NS_SUCCEEDED(convertResultCode(rc)));
   1.536 +  if (aMaxValue)
   1.537 +    *aMaxValue = max;
   1.538 +  return curr;
   1.539 +}
   1.540 +
   1.541 +nsIEventTarget *
   1.542 +Connection::getAsyncExecutionTarget()
   1.543 +{
   1.544 +  MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
   1.545 +
   1.546 +  // If we are shutting down the asynchronous thread, don't hand out any more
   1.547 +  // references to the thread.
   1.548 +  if (mAsyncExecutionThreadShuttingDown)
   1.549 +    return nullptr;
   1.550 +
   1.551 +  if (!mAsyncExecutionThread) {
   1.552 +    nsresult rv = ::NS_NewThread(getter_AddRefs(mAsyncExecutionThread));
   1.553 +    if (NS_FAILED(rv)) {
   1.554 +      NS_WARNING("Failed to create async thread.");
   1.555 +      return nullptr;
   1.556 +    }
   1.557 +    static nsThreadPoolNaming naming;
   1.558 +    naming.SetThreadPoolName(NS_LITERAL_CSTRING("mozStorage"),
   1.559 +                             mAsyncExecutionThread);
   1.560 +  }
   1.561 +
   1.562 +  return mAsyncExecutionThread;
   1.563 +}
   1.564 +
   1.565 +nsresult
   1.566 +Connection::initialize()
   1.567 +{
   1.568 +  NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
   1.569 +  PROFILER_LABEL("storage", "Connection::initialize");
   1.570 +
   1.571 +  // in memory database requested, sqlite uses a magic file name
   1.572 +  int srv = ::sqlite3_open_v2(":memory:", &mDBConn, mFlags, nullptr);
   1.573 +  if (srv != SQLITE_OK) {
   1.574 +    mDBConn = nullptr;
   1.575 +    return convertResultCode(srv);
   1.576 +  }
   1.577 +
   1.578 +  return initializeInternal(nullptr);
   1.579 +}
   1.580 +
   1.581 +nsresult
   1.582 +Connection::initialize(nsIFile *aDatabaseFile)
   1.583 +{
   1.584 +  NS_ASSERTION (aDatabaseFile, "Passed null file!");
   1.585 +  NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
   1.586 +  PROFILER_LABEL("storage", "Connection::initialize");
   1.587 +
   1.588 +  mDatabaseFile = aDatabaseFile;
   1.589 +
   1.590 +  nsAutoString path;
   1.591 +  nsresult rv = aDatabaseFile->GetPath(path);
   1.592 +  NS_ENSURE_SUCCESS(rv, rv);
   1.593 +
   1.594 +  int srv = ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path).get(), &mDBConn,
   1.595 +                              mFlags, nullptr);
   1.596 +  if (srv != SQLITE_OK) {
   1.597 +    mDBConn = nullptr;
   1.598 +    return convertResultCode(srv);
   1.599 +  }
   1.600 +
   1.601 +  rv = initializeInternal(aDatabaseFile);
   1.602 +  NS_ENSURE_SUCCESS(rv, rv);
   1.603 +
   1.604 +  mDatabaseFile = aDatabaseFile;
   1.605 +
   1.606 +  return NS_OK;
   1.607 +}
   1.608 +
   1.609 +nsresult
   1.610 +Connection::initialize(nsIFileURL *aFileURL)
   1.611 +{
   1.612 +  NS_ASSERTION (aFileURL, "Passed null file URL!");
   1.613 +  NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
   1.614 +  PROFILER_LABEL("storage", "Connection::initialize");
   1.615 +
   1.616 +  nsCOMPtr<nsIFile> databaseFile;
   1.617 +  nsresult rv = aFileURL->GetFile(getter_AddRefs(databaseFile));
   1.618 +  NS_ENSURE_SUCCESS(rv, rv);
   1.619 +
   1.620 +  nsAutoCString spec;
   1.621 +  rv = aFileURL->GetSpec(spec);
   1.622 +  NS_ENSURE_SUCCESS(rv, rv);
   1.623 +
   1.624 +  int srv = ::sqlite3_open_v2(spec.get(), &mDBConn, mFlags, nullptr);
   1.625 +  if (srv != SQLITE_OK) {
   1.626 +    mDBConn = nullptr;
   1.627 +    return convertResultCode(srv);
   1.628 +  }
   1.629 +
   1.630 +  rv = initializeInternal(databaseFile);
   1.631 +  NS_ENSURE_SUCCESS(rv, rv);
   1.632 +
   1.633 +  mFileURL = aFileURL;
   1.634 +  mDatabaseFile = databaseFile;
   1.635 +
   1.636 +  return NS_OK;
   1.637 +}
   1.638 +
   1.639 +
   1.640 +nsresult
   1.641 +Connection::initializeInternal(nsIFile* aDatabaseFile)
   1.642 +{
   1.643 +  // Properly wrap the database handle's mutex.
   1.644 +  sharedDBMutex.initWithMutex(sqlite3_db_mutex(mDBConn));
   1.645 +
   1.646 +#ifdef PR_LOGGING
   1.647 +  if (!gStorageLog)
   1.648 +    gStorageLog = ::PR_NewLogModule("mozStorage");
   1.649 +
   1.650 +  ::sqlite3_trace(mDBConn, tracefunc, this);
   1.651 +
   1.652 +  nsAutoCString leafName(":memory");
   1.653 +  if (aDatabaseFile)
   1.654 +    (void)aDatabaseFile->GetNativeLeafName(leafName);
   1.655 +  PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Opening connection to '%s' (%p)",
   1.656 +                                      leafName.get(), this));
   1.657 +#endif
   1.658 +
   1.659 +  int64_t pageSize = Service::getDefaultPageSize();
   1.660 +
   1.661 +  // Set page_size to the preferred default value.  This is effective only if
   1.662 +  // the database has just been created, otherwise, if the database does not
   1.663 +  // use WAL journal mode, a VACUUM operation will updated its page_size.
   1.664 +  nsAutoCString pageSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
   1.665 +                              "PRAGMA page_size = ");
   1.666 +  pageSizeQuery.AppendInt(pageSize);
   1.667 +  nsresult rv = ExecuteSimpleSQL(pageSizeQuery);
   1.668 +  NS_ENSURE_SUCCESS(rv, rv);
   1.669 +
   1.670 +  // Setting the cache_size forces the database open, verifying if it is valid
   1.671 +  // or corrupt.  So this is executed regardless it being actually needed.
   1.672 +  // The cache_size is calculated from the actual page_size, to save memory.
   1.673 +  nsAutoCString cacheSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
   1.674 +                               "PRAGMA cache_size = ");
   1.675 +  cacheSizeQuery.AppendInt(-MAX_CACHE_SIZE_KIBIBYTES);
   1.676 +  int srv = executeSql(mDBConn, cacheSizeQuery.get());
   1.677 +  if (srv != SQLITE_OK) {
   1.678 +    ::sqlite3_close(mDBConn);
   1.679 +    mDBConn = nullptr;
   1.680 +    return convertResultCode(srv);
   1.681 +  }
   1.682 +
   1.683 +  // Register our built-in SQL functions.
   1.684 +  srv = registerFunctions(mDBConn);
   1.685 +  if (srv != SQLITE_OK) {
   1.686 +    ::sqlite3_close(mDBConn);
   1.687 +    mDBConn = nullptr;
   1.688 +    return convertResultCode(srv);
   1.689 +  }
   1.690 +
   1.691 +  // Register our built-in SQL collating sequences.
   1.692 +  srv = registerCollations(mDBConn, mStorageService);
   1.693 +  if (srv != SQLITE_OK) {
   1.694 +    ::sqlite3_close(mDBConn);
   1.695 +    mDBConn = nullptr;
   1.696 +    return convertResultCode(srv);
   1.697 +  }
   1.698 +
   1.699 +  // Set the synchronous PRAGMA, according to the preference.
   1.700 +  switch (Service::getSynchronousPref()) {
   1.701 +    case 2:
   1.702 +      (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   1.703 +          "PRAGMA synchronous = FULL;"));
   1.704 +      break;
   1.705 +    case 0:
   1.706 +      (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   1.707 +          "PRAGMA synchronous = OFF;"));
   1.708 +      break;
   1.709 +    case 1:
   1.710 +    default:
   1.711 +      (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   1.712 +          "PRAGMA synchronous = NORMAL;"));
   1.713 +      break;
   1.714 +  }
   1.715 +
   1.716 +  return NS_OK;
   1.717 +}
   1.718 +
   1.719 +nsresult
   1.720 +Connection::databaseElementExists(enum DatabaseElementType aElementType,
   1.721 +                                  const nsACString &aElementName,
   1.722 +                                  bool *_exists)
   1.723 +{
   1.724 +  if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
   1.725 +
   1.726 +  // When constructing the query, make sure to SELECT the correct db's sqlite_master
   1.727 +  // if the user is prefixing the element with a specific db. ex: sample.test
   1.728 +  nsCString query("SELECT name FROM (SELECT * FROM ");
   1.729 +  nsDependentCSubstring element;
   1.730 +  int32_t ind = aElementName.FindChar('.');
   1.731 +  if (ind == kNotFound) {
   1.732 +    element.Assign(aElementName);
   1.733 +  }
   1.734 +  else {
   1.735 +    nsDependentCSubstring db(Substring(aElementName, 0, ind + 1));
   1.736 +    element.Assign(Substring(aElementName, ind + 1, aElementName.Length()));
   1.737 +    query.Append(db);
   1.738 +  }
   1.739 +  query.Append("sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type = '");
   1.740 +
   1.741 +  switch (aElementType) {
   1.742 +    case INDEX:
   1.743 +      query.Append("index");
   1.744 +      break;
   1.745 +    case TABLE:
   1.746 +      query.Append("table");
   1.747 +      break;
   1.748 +  }
   1.749 +  query.Append("' AND name ='");
   1.750 +  query.Append(element);
   1.751 +  query.Append("'");
   1.752 +
   1.753 +  sqlite3_stmt *stmt;
   1.754 +  int srv = prepareStatement(mDBConn, query, &stmt);
   1.755 +  if (srv != SQLITE_OK)
   1.756 +    return convertResultCode(srv);
   1.757 +
   1.758 +  srv = stepStatement(mDBConn, stmt);
   1.759 +  // we just care about the return value from step
   1.760 +  (void)::sqlite3_finalize(stmt);
   1.761 +
   1.762 +  if (srv == SQLITE_ROW) {
   1.763 +    *_exists = true;
   1.764 +    return NS_OK;
   1.765 +  }
   1.766 +  if (srv == SQLITE_DONE) {
   1.767 +    *_exists = false;
   1.768 +    return NS_OK;
   1.769 +  }
   1.770 +
   1.771 +  return convertResultCode(srv);
   1.772 +}
   1.773 +
   1.774 +bool
   1.775 +Connection::findFunctionByInstance(nsISupports *aInstance)
   1.776 +{
   1.777 +  sharedDBMutex.assertCurrentThreadOwns();
   1.778 +  FFEArguments args = { aInstance, false };
   1.779 +  (void)mFunctions.EnumerateRead(findFunctionEnumerator, &args);
   1.780 +  return args.found;
   1.781 +}
   1.782 +
   1.783 +/* static */ int
   1.784 +Connection::sProgressHelper(void *aArg)
   1.785 +{
   1.786 +  Connection *_this = static_cast<Connection *>(aArg);
   1.787 +  return _this->progressHandler();
   1.788 +}
   1.789 +
   1.790 +int
   1.791 +Connection::progressHandler()
   1.792 +{
   1.793 +  sharedDBMutex.assertCurrentThreadOwns();
   1.794 +  if (mProgressHandler) {
   1.795 +    bool result;
   1.796 +    nsresult rv = mProgressHandler->OnProgress(this, &result);
   1.797 +    if (NS_FAILED(rv)) return 0; // Don't break request
   1.798 +    return result ? 1 : 0;
   1.799 +  }
   1.800 +  return 0;
   1.801 +}
   1.802 +
   1.803 +nsresult
   1.804 +Connection::setClosedState()
   1.805 +{
   1.806 +  // Ensure that we are on the correct thread to close the database.
   1.807 +  bool onOpenedThread;
   1.808 +  nsresult rv = threadOpenedOn->IsOnCurrentThread(&onOpenedThread);
   1.809 +  NS_ENSURE_SUCCESS(rv, rv);
   1.810 +  if (!onOpenedThread) {
   1.811 +    NS_ERROR("Must close the database on the thread that you opened it with!");
   1.812 +    return NS_ERROR_UNEXPECTED;
   1.813 +  }
   1.814 +
   1.815 +  // Flag that we are shutting down the async thread, so that
   1.816 +  // getAsyncExecutionTarget knows not to expose/create the async thread.
   1.817 +  {
   1.818 +    MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
   1.819 +    NS_ENSURE_FALSE(mAsyncExecutionThreadShuttingDown, NS_ERROR_UNEXPECTED);
   1.820 +    mAsyncExecutionThreadShuttingDown = true;
   1.821 +  }
   1.822 +
   1.823 +  // Set the property to null before closing the connection, otherwise the other
   1.824 +  // functions in the module may try to use the connection after it is closed.
   1.825 +  mDBConn = nullptr;
   1.826 +
   1.827 +  return NS_OK;
   1.828 +}
   1.829 +
   1.830 +bool
   1.831 +Connection::connectionReady()
   1.832 +{
   1.833 +  return mDBConn != nullptr;
   1.834 +}
   1.835 +
   1.836 +bool
   1.837 +Connection::isClosing()
   1.838 +{
   1.839 +  bool shuttingDown = false;
   1.840 +  {
   1.841 +    MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
   1.842 +    shuttingDown = mAsyncExecutionThreadShuttingDown;
   1.843 +  }
   1.844 +  return shuttingDown && !isClosed();
   1.845 +}
   1.846 +
   1.847 +bool
   1.848 +Connection::isClosed()
   1.849 +{
   1.850 +  MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
   1.851 +  return mConnectionClosed;
   1.852 +}
   1.853 +
   1.854 +nsresult
   1.855 +Connection::internalClose(sqlite3 *aNativeConnection)
   1.856 +{
   1.857 +  // Sanity checks to make sure we are in the proper state before calling this.
   1.858 +  MOZ_ASSERT(aNativeConnection, "Database connection is invalid!");
   1.859 +  MOZ_ASSERT(!isClosed());
   1.860 +
   1.861 +#ifdef DEBUG
   1.862 +  { // Make sure we have marked our async thread as shutting down.
   1.863 +    MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
   1.864 +    NS_ASSERTION(mAsyncExecutionThreadShuttingDown,
   1.865 +                 "Did not call setClosedState!");
   1.866 +  }
   1.867 +#endif // DEBUG
   1.868 +
   1.869 +#ifdef PR_LOGGING
   1.870 +  nsAutoCString leafName(":memory");
   1.871 +  if (mDatabaseFile)
   1.872 +      (void)mDatabaseFile->GetNativeLeafName(leafName);
   1.873 +  PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Closing connection to '%s'",
   1.874 +                                      leafName.get()));
   1.875 +#endif
   1.876 +
   1.877 +  // At this stage, we may still have statements that need to be
   1.878 +  // finalized. Attempt to close the database connection. This will
   1.879 +  // always disconnect any virtual tables and cleanly finalize their
   1.880 +  // internal statements. Once this is done, closing may fail due to
   1.881 +  // unfinalized client statements, in which case we need to finalize
   1.882 +  // these statements and close again.
   1.883 +  {
   1.884 +    MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
   1.885 +    mConnectionClosed = true;
   1.886 +  }
   1.887 +  int srv = sqlite3_close(aNativeConnection);
   1.888 +
   1.889 +  if (srv == SQLITE_BUSY) {
   1.890 +    // We still have non-finalized statements. Finalize them.
   1.891 +
   1.892 +    sqlite3_stmt *stmt = nullptr;
   1.893 +    while ((stmt = ::sqlite3_next_stmt(aNativeConnection, stmt))) {
   1.894 +      PR_LOG(gStorageLog, PR_LOG_NOTICE,
   1.895 +             ("Auto-finalizing SQL statement '%s' (%x)",
   1.896 +              ::sqlite3_sql(stmt),
   1.897 +              stmt));
   1.898 +
   1.899 +#ifdef DEBUG
   1.900 +      char *msg = ::PR_smprintf("SQL statement '%s' (%x) should have been finalized before closing the connection",
   1.901 +                                ::sqlite3_sql(stmt),
   1.902 +                                stmt);
   1.903 +      NS_WARNING(msg);
   1.904 +      ::PR_smprintf_free(msg);
   1.905 +#endif // DEBUG
   1.906 +
   1.907 +      srv = ::sqlite3_finalize(stmt);
   1.908 +
   1.909 +#ifdef DEBUG
   1.910 +      if (srv != SQLITE_OK) {
   1.911 +        char *msg = ::PR_smprintf("Could not finalize SQL statement '%s' (%x)",
   1.912 +                                  ::sqlite3_sql(stmt),
   1.913 +                                  stmt);
   1.914 +        NS_WARNING(msg);
   1.915 +        ::PR_smprintf_free(msg);
   1.916 +      }
   1.917 +#endif // DEBUG
   1.918 +
   1.919 +      // Ensure that the loop continues properly, whether closing has succeeded
   1.920 +      // or not.
   1.921 +      if (srv == SQLITE_OK) {
   1.922 +        stmt = nullptr;
   1.923 +      }
   1.924 +    }
   1.925 +
   1.926 +    // Now that all statements have been finalized, we
   1.927 +    // should be able to close.
   1.928 +    srv = ::sqlite3_close(aNativeConnection);
   1.929 +
   1.930 +  }
   1.931 +
   1.932 +  if (srv != SQLITE_OK) {
   1.933 +    MOZ_ASSERT(srv == SQLITE_OK,
   1.934 +               "sqlite3_close failed. There are probably outstanding statements that are listed above!");
   1.935 +  }
   1.936 +
   1.937 +  return convertResultCode(srv);
   1.938 +}
   1.939 +
   1.940 +nsCString
   1.941 +Connection::getFilename()
   1.942 +{
   1.943 +  nsCString leafname(":memory:");
   1.944 +  if (mDatabaseFile) {
   1.945 +    (void)mDatabaseFile->GetNativeLeafName(leafname);
   1.946 +  }
   1.947 +  return leafname;
   1.948 +}
   1.949 +
   1.950 +int
   1.951 +Connection::stepStatement(sqlite3 *aNativeConnection, sqlite3_stmt *aStatement)
   1.952 +{
   1.953 +  MOZ_ASSERT(aStatement);
   1.954 +  bool checkedMainThread = false;
   1.955 +  TimeStamp startTime = TimeStamp::Now();
   1.956 +
   1.957 +  // The connection may have been closed if the executing statement has been
   1.958 +  // created and cached after a call to asyncClose() but before the actual
   1.959 +  // sqlite3_close().  This usually happens when other tasks using cached
   1.960 +  // statements are asynchronously scheduled for execution and any of them ends
   1.961 +  // up after asyncClose. See bug 728653 for details.
   1.962 +  if (isClosed())
   1.963 +    return SQLITE_MISUSE;
   1.964 +
   1.965 +  (void)::sqlite3_extended_result_codes(aNativeConnection, 1);
   1.966 +
   1.967 +  int srv;
   1.968 +  while ((srv = ::sqlite3_step(aStatement)) == SQLITE_LOCKED_SHAREDCACHE) {
   1.969 +    if (!checkedMainThread) {
   1.970 +      checkedMainThread = true;
   1.971 +      if (::NS_IsMainThread()) {
   1.972 +        NS_WARNING("We won't allow blocking on the main thread!");
   1.973 +        break;
   1.974 +      }
   1.975 +    }
   1.976 +
   1.977 +    srv = WaitForUnlockNotify(aNativeConnection);
   1.978 +    if (srv != SQLITE_OK) {
   1.979 +      break;
   1.980 +    }
   1.981 +
   1.982 +    ::sqlite3_reset(aStatement);
   1.983 +  }
   1.984 +
   1.985 +  // Report very slow SQL statements to Telemetry
   1.986 +  TimeDuration duration = TimeStamp::Now() - startTime;
   1.987 +  const uint32_t threshold =
   1.988 +    NS_IsMainThread() ? Telemetry::kSlowSQLThresholdForMainThread
   1.989 +                      : Telemetry::kSlowSQLThresholdForHelperThreads;
   1.990 +  if (duration.ToMilliseconds() >= threshold) {
   1.991 +    nsDependentCString statementString(::sqlite3_sql(aStatement));
   1.992 +    Telemetry::RecordSlowSQLStatement(statementString, getFilename(),
   1.993 +                                      duration.ToMilliseconds());
   1.994 +  }
   1.995 +
   1.996 +  (void)::sqlite3_extended_result_codes(aNativeConnection, 0);
   1.997 +  // Drop off the extended result bits of the result code.
   1.998 +  return srv & 0xFF;
   1.999 +}
  1.1000 +
  1.1001 +int
  1.1002 +Connection::prepareStatement(sqlite3 *aNativeConnection, const nsCString &aSQL,
  1.1003 +                             sqlite3_stmt **_stmt)
  1.1004 +{
  1.1005 +  // We should not even try to prepare statements after the connection has
  1.1006 +  // been closed.
  1.1007 +  if (isClosed())
  1.1008 +    return SQLITE_MISUSE;
  1.1009 +
  1.1010 +  bool checkedMainThread = false;
  1.1011 +
  1.1012 +  (void)::sqlite3_extended_result_codes(aNativeConnection, 1);
  1.1013 +
  1.1014 +  int srv;
  1.1015 +  while((srv = ::sqlite3_prepare_v2(aNativeConnection,
  1.1016 +                                    aSQL.get(),
  1.1017 +                                    -1,
  1.1018 +                                    _stmt,
  1.1019 +                                    nullptr)) == SQLITE_LOCKED_SHAREDCACHE) {
  1.1020 +    if (!checkedMainThread) {
  1.1021 +      checkedMainThread = true;
  1.1022 +      if (::NS_IsMainThread()) {
  1.1023 +        NS_WARNING("We won't allow blocking on the main thread!");
  1.1024 +        break;
  1.1025 +      }
  1.1026 +    }
  1.1027 +
  1.1028 +    srv = WaitForUnlockNotify(aNativeConnection);
  1.1029 +    if (srv != SQLITE_OK) {
  1.1030 +      break;
  1.1031 +    }
  1.1032 +  }
  1.1033 +
  1.1034 +  if (srv != SQLITE_OK) {
  1.1035 +    nsCString warnMsg;
  1.1036 +    warnMsg.AppendLiteral("The SQL statement '");
  1.1037 +    warnMsg.Append(aSQL);
  1.1038 +    warnMsg.AppendLiteral("' could not be compiled due to an error: ");
  1.1039 +    warnMsg.Append(::sqlite3_errmsg(aNativeConnection));
  1.1040 +
  1.1041 +#ifdef DEBUG
  1.1042 +    NS_WARNING(warnMsg.get());
  1.1043 +#endif
  1.1044 +    PR_LOG(gStorageLog, PR_LOG_ERROR, ("%s", warnMsg.get()));
  1.1045 +  }
  1.1046 +
  1.1047 +  (void)::sqlite3_extended_result_codes(aNativeConnection, 0);
  1.1048 +  // Drop off the extended result bits of the result code.
  1.1049 +  int rc = srv & 0xFF;
  1.1050 +  // sqlite will return OK on a comment only string and set _stmt to nullptr.
  1.1051 +  // The callers of this function are used to only checking the return value,
  1.1052 +  // so it is safer to return an error code.
  1.1053 +  if (rc == SQLITE_OK && *_stmt == nullptr) {
  1.1054 +    return SQLITE_MISUSE;
  1.1055 +  }
  1.1056 +
  1.1057 +  return rc;
  1.1058 +}
  1.1059 +
  1.1060 +
  1.1061 +int
  1.1062 +Connection::executeSql(sqlite3 *aNativeConnection, const char *aSqlString)
  1.1063 +{
  1.1064 +  if (isClosed())
  1.1065 +    return SQLITE_MISUSE;
  1.1066 +
  1.1067 +  TimeStamp startTime = TimeStamp::Now();
  1.1068 +  int srv = ::sqlite3_exec(aNativeConnection, aSqlString, nullptr, nullptr,
  1.1069 +                           nullptr);
  1.1070 +
  1.1071 +  // Report very slow SQL statements to Telemetry
  1.1072 +  TimeDuration duration = TimeStamp::Now() - startTime;
  1.1073 +  const uint32_t threshold =
  1.1074 +    NS_IsMainThread() ? Telemetry::kSlowSQLThresholdForMainThread
  1.1075 +                      : Telemetry::kSlowSQLThresholdForHelperThreads;
  1.1076 +  if (duration.ToMilliseconds() >= threshold) {
  1.1077 +    nsDependentCString statementString(aSqlString);
  1.1078 +    Telemetry::RecordSlowSQLStatement(statementString, getFilename(),
  1.1079 +                                      duration.ToMilliseconds());
  1.1080 +  }
  1.1081 +
  1.1082 +  return srv;
  1.1083 +}
  1.1084 +
  1.1085 +////////////////////////////////////////////////////////////////////////////////
  1.1086 +//// nsIInterfaceRequestor
  1.1087 +
  1.1088 +NS_IMETHODIMP
  1.1089 +Connection::GetInterface(const nsIID &aIID,
  1.1090 +                         void **_result)
  1.1091 +{
  1.1092 +  if (aIID.Equals(NS_GET_IID(nsIEventTarget))) {
  1.1093 +    nsIEventTarget *background = getAsyncExecutionTarget();
  1.1094 +    NS_IF_ADDREF(background);
  1.1095 +    *_result = background;
  1.1096 +    return NS_OK;
  1.1097 +  }
  1.1098 +  return NS_ERROR_NO_INTERFACE;
  1.1099 +}
  1.1100 +
  1.1101 +////////////////////////////////////////////////////////////////////////////////
  1.1102 +//// mozIStorageConnection
  1.1103 +
  1.1104 +NS_IMETHODIMP
  1.1105 +Connection::Close()
  1.1106 +{
  1.1107 +  if (!mDBConn)
  1.1108 +    return NS_ERROR_NOT_INITIALIZED;
  1.1109 +
  1.1110 +  { // Make sure we have not executed any asynchronous statements.
  1.1111 +    // If this fails, the mDBConn will be left open, resulting in a leak.
  1.1112 +    // Ideally we'd schedule some code to destroy the mDBConn once all its
  1.1113 +    // async statements have finished executing;  see bug 704030.
  1.1114 +    MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
  1.1115 +    bool asyncCloseWasCalled = !mAsyncExecutionThread;
  1.1116 +    NS_ENSURE_TRUE(asyncCloseWasCalled, NS_ERROR_UNEXPECTED);
  1.1117 +  }
  1.1118 +
  1.1119 +  // setClosedState nullifies our connection pointer, so we take a raw pointer
  1.1120 +  // off it, to pass it through the close procedure.
  1.1121 +  sqlite3 *nativeConn = mDBConn;
  1.1122 +  nsresult rv = setClosedState();
  1.1123 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1124 +
  1.1125 +  return internalClose(nativeConn);
  1.1126 +}
  1.1127 +
  1.1128 +NS_IMETHODIMP
  1.1129 +Connection::AsyncClose(mozIStorageCompletionCallback *aCallback)
  1.1130 +{
  1.1131 +  if (!NS_IsMainThread()) {
  1.1132 +    return NS_ERROR_NOT_SAME_THREAD;
  1.1133 +  }
  1.1134 +  if (!mDBConn)
  1.1135 +    return NS_ERROR_NOT_INITIALIZED;
  1.1136 +
  1.1137 +  nsIEventTarget *asyncThread = getAsyncExecutionTarget();
  1.1138 +  NS_ENSURE_TRUE(asyncThread, NS_ERROR_NOT_INITIALIZED);
  1.1139 +
  1.1140 +  // setClosedState nullifies our connection pointer, so we take a raw pointer
  1.1141 +  // off it, to pass it through the close procedure.
  1.1142 +  sqlite3 *nativeConn = mDBConn;
  1.1143 +  nsresult rv = setClosedState();
  1.1144 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1145 +
  1.1146 +  // Create our callback event if we were given a callback.
  1.1147 +  nsCOMPtr<nsIRunnable> completeEvent;
  1.1148 +  if (aCallback) {
  1.1149 +    completeEvent = newCompletionEvent(aCallback);
  1.1150 +  }
  1.1151 +
  1.1152 +  // Create and dispatch our close event to the background thread.
  1.1153 +  nsCOMPtr<nsIRunnable> closeEvent;
  1.1154 +  {
  1.1155 +    // We need to lock because we're modifying mAsyncExecutionThread
  1.1156 +    MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
  1.1157 +    closeEvent = new AsyncCloseConnection(this,
  1.1158 +                                          nativeConn,
  1.1159 +                                          completeEvent,
  1.1160 +                                          mAsyncExecutionThread.forget());
  1.1161 +  }
  1.1162 +
  1.1163 +  rv = asyncThread->Dispatch(closeEvent, NS_DISPATCH_NORMAL);
  1.1164 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1165 +
  1.1166 +  return NS_OK;
  1.1167 +}
  1.1168 +
  1.1169 +NS_IMETHODIMP
  1.1170 +Connection::AsyncClone(bool aReadOnly,
  1.1171 +                       mozIStorageCompletionCallback *aCallback)
  1.1172 +{
  1.1173 +  PROFILER_LABEL("storage", "Connection::Clone");
  1.1174 +  if (!NS_IsMainThread()) {
  1.1175 +    return NS_ERROR_NOT_SAME_THREAD;
  1.1176 +  }
  1.1177 +  if (!mDBConn)
  1.1178 +    return NS_ERROR_NOT_INITIALIZED;
  1.1179 +  if (!mDatabaseFile)
  1.1180 +    return NS_ERROR_UNEXPECTED;
  1.1181 +
  1.1182 +  int flags = mFlags;
  1.1183 +  if (aReadOnly) {
  1.1184 +    // Turn off SQLITE_OPEN_READWRITE, and set SQLITE_OPEN_READONLY.
  1.1185 +    flags = (~SQLITE_OPEN_READWRITE & flags) | SQLITE_OPEN_READONLY;
  1.1186 +    // Turn off SQLITE_OPEN_CREATE.
  1.1187 +    flags = (~SQLITE_OPEN_CREATE & flags);
  1.1188 +  }
  1.1189 +
  1.1190 +  nsRefPtr<Connection> clone = new Connection(mStorageService, flags,
  1.1191 +                                              mAsyncOnly);
  1.1192 +
  1.1193 +  nsRefPtr<AsyncInitializeClone> initEvent =
  1.1194 +    new AsyncInitializeClone(this, clone, aReadOnly, aCallback);
  1.1195 +  nsCOMPtr<nsIEventTarget> target = clone->getAsyncExecutionTarget();
  1.1196 +  if (!target) {
  1.1197 +    return NS_ERROR_UNEXPECTED;
  1.1198 +  }
  1.1199 +  return target->Dispatch(initEvent, NS_DISPATCH_NORMAL);
  1.1200 +}
  1.1201 +
  1.1202 +nsresult
  1.1203 +Connection::initializeClone(Connection* aClone, bool aReadOnly)
  1.1204 +{
  1.1205 +  nsresult rv = mFileURL ? aClone->initialize(mFileURL)
  1.1206 +                         : aClone->initialize(mDatabaseFile);
  1.1207 +  if (NS_FAILED(rv)) {
  1.1208 +    return rv;
  1.1209 +  }
  1.1210 +
  1.1211 +  // Copy over pragmas from the original connection.
  1.1212 +  static const char * pragmas[] = {
  1.1213 +    "cache_size",
  1.1214 +    "temp_store",
  1.1215 +    "foreign_keys",
  1.1216 +    "journal_size_limit",
  1.1217 +    "synchronous",
  1.1218 +    "wal_autocheckpoint",
  1.1219 +  };
  1.1220 +  for (uint32_t i = 0; i < ArrayLength(pragmas); ++i) {
  1.1221 +    // Read-only connections just need cache_size and temp_store pragmas.
  1.1222 +    if (aReadOnly && ::strcmp(pragmas[i], "cache_size") != 0 &&
  1.1223 +                     ::strcmp(pragmas[i], "temp_store") != 0) {
  1.1224 +      continue;
  1.1225 +    }
  1.1226 +
  1.1227 +    nsAutoCString pragmaQuery("PRAGMA ");
  1.1228 +    pragmaQuery.Append(pragmas[i]);
  1.1229 +    nsCOMPtr<mozIStorageStatement> stmt;
  1.1230 +    rv = CreateStatement(pragmaQuery, getter_AddRefs(stmt));
  1.1231 +    MOZ_ASSERT(NS_SUCCEEDED(rv));
  1.1232 +    bool hasResult = false;
  1.1233 +    if (stmt && NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
  1.1234 +      pragmaQuery.AppendLiteral(" = ");
  1.1235 +      pragmaQuery.AppendInt(stmt->AsInt32(0));
  1.1236 +      rv = aClone->ExecuteSimpleSQL(pragmaQuery);
  1.1237 +      MOZ_ASSERT(NS_SUCCEEDED(rv));
  1.1238 +    }
  1.1239 +  }
  1.1240 +
  1.1241 +  // Copy any functions that have been added to this connection.
  1.1242 +  SQLiteMutexAutoLock lockedScope(sharedDBMutex);
  1.1243 +  (void)mFunctions.EnumerateRead(copyFunctionEnumerator, aClone);
  1.1244 +
  1.1245 +  return NS_OK;
  1.1246 +}
  1.1247 +
  1.1248 +NS_IMETHODIMP
  1.1249 +Connection::Clone(bool aReadOnly,
  1.1250 +                  mozIStorageConnection **_connection)
  1.1251 +{
  1.1252 +  MOZ_ASSERT(threadOpenedOn == NS_GetCurrentThread());
  1.1253 +
  1.1254 +  PROFILER_LABEL("storage", "Connection::Clone");
  1.1255 +  if (!mDBConn)
  1.1256 +    return NS_ERROR_NOT_INITIALIZED;
  1.1257 +  if (!mDatabaseFile)
  1.1258 +    return NS_ERROR_UNEXPECTED;
  1.1259 +
  1.1260 +  int flags = mFlags;
  1.1261 +  if (aReadOnly) {
  1.1262 +    // Turn off SQLITE_OPEN_READWRITE, and set SQLITE_OPEN_READONLY.
  1.1263 +    flags = (~SQLITE_OPEN_READWRITE & flags) | SQLITE_OPEN_READONLY;
  1.1264 +    // Turn off SQLITE_OPEN_CREATE.
  1.1265 +    flags = (~SQLITE_OPEN_CREATE & flags);
  1.1266 +  }
  1.1267 +
  1.1268 +  nsRefPtr<Connection> clone = new Connection(mStorageService, flags,
  1.1269 +                                              mAsyncOnly);
  1.1270 +
  1.1271 +  nsresult rv = initializeClone(clone, aReadOnly);
  1.1272 +  if (NS_FAILED(rv)) {
  1.1273 +    return rv;
  1.1274 +  }
  1.1275 +
  1.1276 +  NS_IF_ADDREF(*_connection = clone);
  1.1277 +  return NS_OK;
  1.1278 +}
  1.1279 +
  1.1280 +NS_IMETHODIMP
  1.1281 +Connection::GetDefaultPageSize(int32_t *_defaultPageSize)
  1.1282 +{
  1.1283 +  *_defaultPageSize = Service::getDefaultPageSize();
  1.1284 +  return NS_OK;
  1.1285 +}
  1.1286 +
  1.1287 +NS_IMETHODIMP
  1.1288 +Connection::GetConnectionReady(bool *_ready)
  1.1289 +{
  1.1290 +  *_ready = connectionReady();
  1.1291 +  return NS_OK;
  1.1292 +}
  1.1293 +
  1.1294 +NS_IMETHODIMP
  1.1295 +Connection::GetDatabaseFile(nsIFile **_dbFile)
  1.1296 +{
  1.1297 +  if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1.1298 +
  1.1299 +  NS_IF_ADDREF(*_dbFile = mDatabaseFile);
  1.1300 +
  1.1301 +  return NS_OK;
  1.1302 +}
  1.1303 +
  1.1304 +NS_IMETHODIMP
  1.1305 +Connection::GetLastInsertRowID(int64_t *_id)
  1.1306 +{
  1.1307 +  if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1.1308 +
  1.1309 +  sqlite_int64 id = ::sqlite3_last_insert_rowid(mDBConn);
  1.1310 +  *_id = id;
  1.1311 +
  1.1312 +  return NS_OK;
  1.1313 +}
  1.1314 +
  1.1315 +NS_IMETHODIMP
  1.1316 +Connection::GetAffectedRows(int32_t *_rows)
  1.1317 +{
  1.1318 +  if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1.1319 +
  1.1320 +  *_rows = ::sqlite3_changes(mDBConn);
  1.1321 +
  1.1322 +  return NS_OK;
  1.1323 +}
  1.1324 +
  1.1325 +NS_IMETHODIMP
  1.1326 +Connection::GetLastError(int32_t *_error)
  1.1327 +{
  1.1328 +  if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1.1329 +
  1.1330 +  *_error = ::sqlite3_errcode(mDBConn);
  1.1331 +
  1.1332 +  return NS_OK;
  1.1333 +}
  1.1334 +
  1.1335 +NS_IMETHODIMP
  1.1336 +Connection::GetLastErrorString(nsACString &_errorString)
  1.1337 +{
  1.1338 +  if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1.1339 +
  1.1340 +  const char *serr = ::sqlite3_errmsg(mDBConn);
  1.1341 +  _errorString.Assign(serr);
  1.1342 +
  1.1343 +  return NS_OK;
  1.1344 +}
  1.1345 +
  1.1346 +NS_IMETHODIMP
  1.1347 +Connection::GetSchemaVersion(int32_t *_version)
  1.1348 +{
  1.1349 +  if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1.1350 +
  1.1351 +  nsCOMPtr<mozIStorageStatement> stmt;
  1.1352 +  (void)CreateStatement(NS_LITERAL_CSTRING("PRAGMA user_version"),
  1.1353 +                        getter_AddRefs(stmt));
  1.1354 +  NS_ENSURE_TRUE(stmt, NS_ERROR_OUT_OF_MEMORY);
  1.1355 +
  1.1356 +  *_version = 0;
  1.1357 +  bool hasResult;
  1.1358 +  if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult)
  1.1359 +    *_version = stmt->AsInt32(0);
  1.1360 +
  1.1361 +  return NS_OK;
  1.1362 +}
  1.1363 +
  1.1364 +NS_IMETHODIMP
  1.1365 +Connection::SetSchemaVersion(int32_t aVersion)
  1.1366 +{
  1.1367 +  if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1.1368 +
  1.1369 +  nsAutoCString stmt(NS_LITERAL_CSTRING("PRAGMA user_version = "));
  1.1370 +  stmt.AppendInt(aVersion);
  1.1371 +
  1.1372 +  return ExecuteSimpleSQL(stmt);
  1.1373 +}
  1.1374 +
  1.1375 +NS_IMETHODIMP
  1.1376 +Connection::CreateStatement(const nsACString &aSQLStatement,
  1.1377 +                            mozIStorageStatement **_stmt)
  1.1378 +{
  1.1379 +  NS_ENSURE_ARG_POINTER(_stmt);
  1.1380 +  if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1.1381 +
  1.1382 +  nsRefPtr<Statement> statement(new Statement());
  1.1383 +  NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
  1.1384 +
  1.1385 +  nsresult rv = statement->initialize(this, mDBConn, aSQLStatement);
  1.1386 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1387 +
  1.1388 +  Statement *rawPtr;
  1.1389 +  statement.forget(&rawPtr);
  1.1390 +  *_stmt = rawPtr;
  1.1391 +  return NS_OK;
  1.1392 +}
  1.1393 +
  1.1394 +NS_IMETHODIMP
  1.1395 +Connection::CreateAsyncStatement(const nsACString &aSQLStatement,
  1.1396 +                                 mozIStorageAsyncStatement **_stmt)
  1.1397 +{
  1.1398 +  NS_ENSURE_ARG_POINTER(_stmt);
  1.1399 +  if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1.1400 +
  1.1401 +  nsRefPtr<AsyncStatement> statement(new AsyncStatement());
  1.1402 +  NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
  1.1403 +
  1.1404 +  nsresult rv = statement->initialize(this, mDBConn, aSQLStatement);
  1.1405 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1406 +
  1.1407 +  AsyncStatement *rawPtr;
  1.1408 +  statement.forget(&rawPtr);
  1.1409 +  *_stmt = rawPtr;
  1.1410 +  return NS_OK;
  1.1411 +}
  1.1412 +
  1.1413 +NS_IMETHODIMP
  1.1414 +Connection::ExecuteSimpleSQL(const nsACString &aSQLStatement)
  1.1415 +{
  1.1416 +  if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1.1417 +
  1.1418 +  int srv = executeSql(mDBConn, PromiseFlatCString(aSQLStatement).get());
  1.1419 +  return convertResultCode(srv);
  1.1420 +}
  1.1421 +
  1.1422 +NS_IMETHODIMP
  1.1423 +Connection::ExecuteAsync(mozIStorageBaseStatement **aStatements,
  1.1424 +                         uint32_t aNumStatements,
  1.1425 +                         mozIStorageStatementCallback *aCallback,
  1.1426 +                         mozIStoragePendingStatement **_handle)
  1.1427 +{
  1.1428 +  nsTArray<StatementData> stmts(aNumStatements);
  1.1429 +  for (uint32_t i = 0; i < aNumStatements; i++) {
  1.1430 +    nsCOMPtr<StorageBaseStatementInternal> stmt = 
  1.1431 +      do_QueryInterface(aStatements[i]);
  1.1432 +
  1.1433 +    // Obtain our StatementData.
  1.1434 +    StatementData data;
  1.1435 +    nsresult rv = stmt->getAsynchronousStatementData(data);
  1.1436 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1437 +
  1.1438 +    NS_ASSERTION(stmt->getOwner() == this,
  1.1439 +                 "Statement must be from this database connection!");
  1.1440 +
  1.1441 +    // Now append it to our array.
  1.1442 +    NS_ENSURE_TRUE(stmts.AppendElement(data), NS_ERROR_OUT_OF_MEMORY);
  1.1443 +  }
  1.1444 +
  1.1445 +  // Dispatch to the background
  1.1446 +  return AsyncExecuteStatements::execute(stmts, this, mDBConn, aCallback,
  1.1447 +                                         _handle);
  1.1448 +}
  1.1449 +
  1.1450 +NS_IMETHODIMP
  1.1451 +Connection::ExecuteSimpleSQLAsync(const nsACString &aSQLStatement,
  1.1452 +                                  mozIStorageStatementCallback *aCallback,
  1.1453 +                                  mozIStoragePendingStatement **_handle)
  1.1454 +{
  1.1455 +  if (!NS_IsMainThread()) {
  1.1456 +    return NS_ERROR_NOT_SAME_THREAD;
  1.1457 +  }
  1.1458 +
  1.1459 +  nsCOMPtr<mozIStorageAsyncStatement> stmt;
  1.1460 +  nsresult rv = CreateAsyncStatement(aSQLStatement, getter_AddRefs(stmt));
  1.1461 +  if (NS_FAILED(rv)) {
  1.1462 +    return rv;
  1.1463 +  }
  1.1464 +
  1.1465 +  nsCOMPtr<mozIStoragePendingStatement> pendingStatement;
  1.1466 +  rv = stmt->ExecuteAsync(aCallback, getter_AddRefs(pendingStatement));
  1.1467 +  if (NS_FAILED(rv)) {
  1.1468 +    return rv;
  1.1469 +  }
  1.1470 +
  1.1471 +  NS_ADDREF(*_handle = pendingStatement);
  1.1472 +  return rv;
  1.1473 +}
  1.1474 +
  1.1475 +NS_IMETHODIMP
  1.1476 +Connection::TableExists(const nsACString &aTableName,
  1.1477 +                        bool *_exists)
  1.1478 +{
  1.1479 +    return databaseElementExists(TABLE, aTableName, _exists);
  1.1480 +}
  1.1481 +
  1.1482 +NS_IMETHODIMP
  1.1483 +Connection::IndexExists(const nsACString &aIndexName,
  1.1484 +                        bool* _exists)
  1.1485 +{
  1.1486 +    return databaseElementExists(INDEX, aIndexName, _exists);
  1.1487 +}
  1.1488 +
  1.1489 +NS_IMETHODIMP
  1.1490 +Connection::GetTransactionInProgress(bool *_inProgress)
  1.1491 +{
  1.1492 +  if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1.1493 +
  1.1494 +  SQLiteMutexAutoLock lockedScope(sharedDBMutex);
  1.1495 +  *_inProgress = mTransactionInProgress;
  1.1496 +  return NS_OK;
  1.1497 +}
  1.1498 +
  1.1499 +NS_IMETHODIMP
  1.1500 +Connection::BeginTransaction()
  1.1501 +{
  1.1502 +  return BeginTransactionAs(mozIStorageConnection::TRANSACTION_DEFERRED);
  1.1503 +}
  1.1504 +
  1.1505 +NS_IMETHODIMP
  1.1506 +Connection::BeginTransactionAs(int32_t aTransactionType)
  1.1507 +{
  1.1508 +  if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1.1509 +
  1.1510 +  return beginTransactionInternal(mDBConn, aTransactionType);
  1.1511 +}
  1.1512 +
  1.1513 +nsresult
  1.1514 +Connection::beginTransactionInternal(sqlite3 *aNativeConnection,
  1.1515 +                                     int32_t aTransactionType)
  1.1516 +{
  1.1517 +  SQLiteMutexAutoLock lockedScope(sharedDBMutex);
  1.1518 +  if (mTransactionInProgress)
  1.1519 +    return NS_ERROR_FAILURE;
  1.1520 +  nsresult rv;
  1.1521 +  switch(aTransactionType) {
  1.1522 +    case TRANSACTION_DEFERRED:
  1.1523 +      rv = convertResultCode(executeSql(aNativeConnection, "BEGIN DEFERRED"));
  1.1524 +      break;
  1.1525 +    case TRANSACTION_IMMEDIATE:
  1.1526 +      rv = convertResultCode(executeSql(aNativeConnection, "BEGIN IMMEDIATE"));
  1.1527 +      break;
  1.1528 +    case TRANSACTION_EXCLUSIVE:
  1.1529 +      rv = convertResultCode(executeSql(aNativeConnection, "BEGIN EXCLUSIVE"));
  1.1530 +      break;
  1.1531 +    default:
  1.1532 +      return NS_ERROR_ILLEGAL_VALUE;
  1.1533 +  }
  1.1534 +  if (NS_SUCCEEDED(rv))
  1.1535 +    mTransactionInProgress = true;
  1.1536 +  return rv;
  1.1537 +}
  1.1538 +
  1.1539 +NS_IMETHODIMP
  1.1540 +Connection::CommitTransaction()
  1.1541 +{
  1.1542 +  if (!mDBConn)
  1.1543 +    return NS_ERROR_NOT_INITIALIZED;
  1.1544 +
  1.1545 +  return commitTransactionInternal(mDBConn);
  1.1546 +}
  1.1547 +
  1.1548 +nsresult
  1.1549 +Connection::commitTransactionInternal(sqlite3 *aNativeConnection)
  1.1550 +{
  1.1551 +  SQLiteMutexAutoLock lockedScope(sharedDBMutex);
  1.1552 +  if (!mTransactionInProgress)
  1.1553 +    return NS_ERROR_UNEXPECTED;
  1.1554 +  nsresult rv =
  1.1555 +    convertResultCode(executeSql(aNativeConnection, "COMMIT TRANSACTION"));
  1.1556 +  if (NS_SUCCEEDED(rv))
  1.1557 +    mTransactionInProgress = false;
  1.1558 +  return rv;
  1.1559 +}
  1.1560 +
  1.1561 +NS_IMETHODIMP
  1.1562 +Connection::RollbackTransaction()
  1.1563 +{
  1.1564 +  if (!mDBConn)
  1.1565 +    return NS_ERROR_NOT_INITIALIZED;
  1.1566 +
  1.1567 +  return rollbackTransactionInternal(mDBConn);
  1.1568 +}
  1.1569 +
  1.1570 +nsresult
  1.1571 +Connection::rollbackTransactionInternal(sqlite3 *aNativeConnection)
  1.1572 +{
  1.1573 +  SQLiteMutexAutoLock lockedScope(sharedDBMutex);
  1.1574 +  if (!mTransactionInProgress)
  1.1575 +    return NS_ERROR_UNEXPECTED;
  1.1576 +
  1.1577 +  nsresult rv =
  1.1578 +    convertResultCode(executeSql(aNativeConnection, "ROLLBACK TRANSACTION"));
  1.1579 +  if (NS_SUCCEEDED(rv))
  1.1580 +    mTransactionInProgress = false;
  1.1581 +  return rv;
  1.1582 +}
  1.1583 +
  1.1584 +NS_IMETHODIMP
  1.1585 +Connection::CreateTable(const char *aTableName,
  1.1586 +                        const char *aTableSchema)
  1.1587 +{
  1.1588 +  if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1.1589 +
  1.1590 +  char *buf = ::PR_smprintf("CREATE TABLE %s (%s)", aTableName, aTableSchema);
  1.1591 +  if (!buf)
  1.1592 +    return NS_ERROR_OUT_OF_MEMORY;
  1.1593 +
  1.1594 +  int srv = executeSql(mDBConn, buf);
  1.1595 +  ::PR_smprintf_free(buf);
  1.1596 +
  1.1597 +  return convertResultCode(srv);
  1.1598 +}
  1.1599 +
  1.1600 +NS_IMETHODIMP
  1.1601 +Connection::CreateFunction(const nsACString &aFunctionName,
  1.1602 +                           int32_t aNumArguments,
  1.1603 +                           mozIStorageFunction *aFunction)
  1.1604 +{
  1.1605 +  if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1.1606 +
  1.1607 +  // Check to see if this function is already defined.  We only check the name
  1.1608 +  // because a function can be defined with the same body but different names.
  1.1609 +  SQLiteMutexAutoLock lockedScope(sharedDBMutex);
  1.1610 +  NS_ENSURE_FALSE(mFunctions.Get(aFunctionName, nullptr), NS_ERROR_FAILURE);
  1.1611 +
  1.1612 +  int srv = ::sqlite3_create_function(mDBConn,
  1.1613 +                                      nsPromiseFlatCString(aFunctionName).get(),
  1.1614 +                                      aNumArguments,
  1.1615 +                                      SQLITE_ANY,
  1.1616 +                                      aFunction,
  1.1617 +                                      basicFunctionHelper,
  1.1618 +                                      nullptr,
  1.1619 +                                      nullptr);
  1.1620 +  if (srv != SQLITE_OK)
  1.1621 +    return convertResultCode(srv);
  1.1622 +
  1.1623 +  FunctionInfo info = { aFunction,
  1.1624 +                        Connection::FunctionInfo::SIMPLE,
  1.1625 +                        aNumArguments };
  1.1626 +  mFunctions.Put(aFunctionName, info);
  1.1627 +
  1.1628 +  return NS_OK;
  1.1629 +}
  1.1630 +
  1.1631 +NS_IMETHODIMP
  1.1632 +Connection::CreateAggregateFunction(const nsACString &aFunctionName,
  1.1633 +                                    int32_t aNumArguments,
  1.1634 +                                    mozIStorageAggregateFunction *aFunction)
  1.1635 +{
  1.1636 +  if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1.1637 +
  1.1638 +  // Check to see if this function name is already defined.
  1.1639 +  SQLiteMutexAutoLock lockedScope(sharedDBMutex);
  1.1640 +  NS_ENSURE_FALSE(mFunctions.Get(aFunctionName, nullptr), NS_ERROR_FAILURE);
  1.1641 +
  1.1642 +  // Because aggregate functions depend on state across calls, you cannot have
  1.1643 +  // the same instance use the same name.  We want to enumerate all functions
  1.1644 +  // and make sure this instance is not already registered.
  1.1645 +  NS_ENSURE_FALSE(findFunctionByInstance(aFunction), NS_ERROR_FAILURE);
  1.1646 +
  1.1647 +  int srv = ::sqlite3_create_function(mDBConn,
  1.1648 +                                      nsPromiseFlatCString(aFunctionName).get(),
  1.1649 +                                      aNumArguments,
  1.1650 +                                      SQLITE_ANY,
  1.1651 +                                      aFunction,
  1.1652 +                                      nullptr,
  1.1653 +                                      aggregateFunctionStepHelper,
  1.1654 +                                      aggregateFunctionFinalHelper);
  1.1655 +  if (srv != SQLITE_OK)
  1.1656 +    return convertResultCode(srv);
  1.1657 +
  1.1658 +  FunctionInfo info = { aFunction,
  1.1659 +                        Connection::FunctionInfo::AGGREGATE,
  1.1660 +                        aNumArguments };
  1.1661 +  mFunctions.Put(aFunctionName, info);
  1.1662 +
  1.1663 +  return NS_OK;
  1.1664 +}
  1.1665 +
  1.1666 +NS_IMETHODIMP
  1.1667 +Connection::RemoveFunction(const nsACString &aFunctionName)
  1.1668 +{
  1.1669 +  if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1.1670 +
  1.1671 +  SQLiteMutexAutoLock lockedScope(sharedDBMutex);
  1.1672 +  NS_ENSURE_TRUE(mFunctions.Get(aFunctionName, nullptr), NS_ERROR_FAILURE);
  1.1673 +
  1.1674 +  int srv = ::sqlite3_create_function(mDBConn,
  1.1675 +                                      nsPromiseFlatCString(aFunctionName).get(),
  1.1676 +                                      0,
  1.1677 +                                      SQLITE_ANY,
  1.1678 +                                      nullptr,
  1.1679 +                                      nullptr,
  1.1680 +                                      nullptr,
  1.1681 +                                      nullptr);
  1.1682 +  if (srv != SQLITE_OK)
  1.1683 +    return convertResultCode(srv);
  1.1684 +
  1.1685 +  mFunctions.Remove(aFunctionName);
  1.1686 +
  1.1687 +  return NS_OK;
  1.1688 +}
  1.1689 +
  1.1690 +NS_IMETHODIMP
  1.1691 +Connection::SetProgressHandler(int32_t aGranularity,
  1.1692 +                               mozIStorageProgressHandler *aHandler,
  1.1693 +                               mozIStorageProgressHandler **_oldHandler)
  1.1694 +{
  1.1695 +  if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1.1696 +
  1.1697 +  // Return previous one
  1.1698 +  SQLiteMutexAutoLock lockedScope(sharedDBMutex);
  1.1699 +  NS_IF_ADDREF(*_oldHandler = mProgressHandler);
  1.1700 +
  1.1701 +  if (!aHandler || aGranularity <= 0) {
  1.1702 +    aHandler = nullptr;
  1.1703 +    aGranularity = 0;
  1.1704 +  }
  1.1705 +  mProgressHandler = aHandler;
  1.1706 +  ::sqlite3_progress_handler(mDBConn, aGranularity, sProgressHelper, this);
  1.1707 +
  1.1708 +  return NS_OK;
  1.1709 +}
  1.1710 +
  1.1711 +NS_IMETHODIMP
  1.1712 +Connection::RemoveProgressHandler(mozIStorageProgressHandler **_oldHandler)
  1.1713 +{
  1.1714 +  if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1.1715 +
  1.1716 +  // Return previous one
  1.1717 +  SQLiteMutexAutoLock lockedScope(sharedDBMutex);
  1.1718 +  NS_IF_ADDREF(*_oldHandler = mProgressHandler);
  1.1719 +
  1.1720 +  mProgressHandler = nullptr;
  1.1721 +  ::sqlite3_progress_handler(mDBConn, 0, nullptr, nullptr);
  1.1722 +
  1.1723 +  return NS_OK;
  1.1724 +}
  1.1725 +
  1.1726 +NS_IMETHODIMP
  1.1727 +Connection::SetGrowthIncrement(int32_t aChunkSize, const nsACString &aDatabaseName)
  1.1728 +{
  1.1729 +  // Bug 597215: Disk space is extremely limited on Android
  1.1730 +  // so don't preallocate space. This is also not effective
  1.1731 +  // on log structured file systems used by Android devices
  1.1732 +#if !defined(ANDROID) && !defined(MOZ_PLATFORM_MAEMO)
  1.1733 +  // Don't preallocate if less than 500MiB is available.
  1.1734 +  int64_t bytesAvailable;
  1.1735 +  nsresult rv = mDatabaseFile->GetDiskSpaceAvailable(&bytesAvailable);
  1.1736 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1737 +  if (bytesAvailable < MIN_AVAILABLE_BYTES_PER_CHUNKED_GROWTH) {
  1.1738 +    return NS_ERROR_FILE_TOO_BIG;
  1.1739 +  }
  1.1740 +
  1.1741 +  (void)::sqlite3_file_control(mDBConn,
  1.1742 +                               aDatabaseName.Length() ? nsPromiseFlatCString(aDatabaseName).get()
  1.1743 +                                                      : nullptr,
  1.1744 +                               SQLITE_FCNTL_CHUNK_SIZE,
  1.1745 +                               &aChunkSize);
  1.1746 +#endif
  1.1747 +  return NS_OK;
  1.1748 +}
  1.1749 +
  1.1750 +NS_IMETHODIMP
  1.1751 +Connection::EnableModule(const nsACString& aModuleName)
  1.1752 +{
  1.1753 +  if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
  1.1754 +
  1.1755 +  for (size_t i = 0; i < ArrayLength(gModules); i++) {
  1.1756 +    struct Module* m = &gModules[i];
  1.1757 +    if (aModuleName.Equals(m->name)) {
  1.1758 +      int srv = m->registerFunc(mDBConn, m->name);
  1.1759 +      if (srv != SQLITE_OK)
  1.1760 +        return convertResultCode(srv);
  1.1761 +
  1.1762 +      return NS_OK;
  1.1763 +    }
  1.1764 +  }
  1.1765 +
  1.1766 +  return NS_ERROR_FAILURE;
  1.1767 +}
  1.1768 +
  1.1769 +} // namespace storage
  1.1770 +} // namespace mozilla

mercurial