storage/src/mozStorageConnection.cpp

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

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

mercurial