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 + ¬ification); 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