mozStorageConnection.h (19257B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_storage_Connection_h 8 #define mozilla_storage_Connection_h 9 10 #include "nsCOMPtr.h" 11 #include "mozilla/Atomics.h" 12 #include "mozilla/Mutex.h" 13 #include "nsProxyRelease.h" 14 #include "nsThreadUtils.h" 15 #include "nsIInterfaceRequestor.h" 16 17 #include "nsTHashMap.h" 18 #include "nsTHashSet.h" 19 #include "mozIStorageProgressHandler.h" 20 #include "SQLiteMutex.h" 21 #include "mozIStorageConnection.h" 22 #include "mozStorageService.h" 23 #include "mozIStorageAsyncConnection.h" 24 #include "mozIStorageCompletionCallback.h" 25 26 #include "mozilla/Attributes.h" 27 28 #include "sqlite3.h" 29 30 class nsIFile; 31 class nsIFileURL; 32 class nsIEventTarget; 33 class nsISerialEventTarget; 34 class nsIThread; 35 36 namespace mozilla::storage { 37 38 class Connection final : public mozIStorageConnection, 39 public nsIInterfaceRequestor { 40 public: 41 NS_DECL_THREADSAFE_ISUPPORTS 42 NS_DECL_MOZISTORAGEASYNCCONNECTION 43 NS_DECL_MOZISTORAGECONNECTION 44 NS_DECL_NSIINTERFACEREQUESTOR 45 46 /** 47 * Indicates if a database operation is synchronous or asynchronous. 48 * 49 * - Async operations may be called from any thread for all connections. 50 * - Sync operations may be called from any thread for sync connections, and 51 * from background threads for async connections. 52 */ 53 enum ConnectionOperation { ASYNCHRONOUS, SYNCHRONOUS }; 54 55 /** 56 * Structure used to describe user functions on the database connection. 57 */ 58 struct FunctionInfo { 59 nsCOMPtr<mozIStorageFunction> function; 60 int32_t numArgs; 61 }; 62 63 /** 64 * @param aService 65 * Pointer to the storage service. Held onto for the lifetime of the 66 * connection. 67 * @param aFlags 68 * The flags to pass to sqlite3_open_v2. 69 * @param aSupportedOperations 70 * The operation types supported on this connection. All connections 71 * implement both the async (`mozIStorageAsyncConnection`) and sync 72 * (`mozIStorageConnection`) interfaces, but async connections may not 73 * call sync operations from the main thread. 74 * @param aInterruptible 75 * If |true|, the pending operations can be interrupted by invokind the 76 * Interrupt() method. 77 * If |false|, method Interrupt() must not be used. 78 * @param aIgnoreLockingMode 79 * If |true|, ignore locks in force on the file. Only usable with 80 * read-only connections. Defaults to false. 81 * Use with extreme caution. If sqlite ignores locks, reads may fail 82 * indicating database corruption (the database won't actually be 83 * corrupt) or produce wrong results without any indication that has 84 * happened. 85 */ 86 Connection(Service* aService, int aFlags, 87 ConnectionOperation aSupportedOperations, 88 const nsCString& aTelemetryFilename, bool aInterruptible = false, 89 bool aIgnoreLockingMode = false, bool aOpenNotExclusive = false); 90 91 /** 92 * Creates the connection to an in-memory database. 93 */ 94 nsresult initialize(const nsACString& aStorageKey, const nsACString& aName); 95 96 /** 97 * Creates the connection to the database. 98 * 99 * @param aDatabaseFile 100 * The nsIFile of the location of the database to open, or create if it 101 * does not exist. 102 */ 103 nsresult initialize(nsIFile* aDatabaseFile); 104 105 /** 106 * Creates the connection to the database. 107 * 108 * @param aFileURL 109 * The nsIFileURL of the location of the database to open, or create if 110 * it does not exist. 111 */ 112 nsresult initialize(nsIFileURL* aFileURL); 113 114 /** 115 * Same as initialize, but to be used on the async thread. 116 */ 117 nsresult initializeOnAsyncThread(nsIFile* aStorageFile); 118 119 /** 120 * Fetches runtime status information for this connection. 121 * 122 * @param aStatusOption One of the SQLITE_DBSTATUS options defined at 123 * http://www.sqlite.org/c3ref/c_dbstatus_options.html 124 * @param [optional] aMaxValue if provided, will be set to the highest 125 * istantaneous value. 126 * @return the current value for the specified option. 127 */ 128 int32_t getSqliteRuntimeStatus(int32_t aStatusOption, 129 int32_t* aMaxValue = nullptr); 130 /** 131 * Registers/unregisters a commit hook callback. 132 * 133 * @param aCallbackFn a callback function to be invoked on transactions 134 * commit. Pass nullptr to unregister the current callback. 135 * @param [optional] aData if provided, will be passed to the callback. 136 * @see http://sqlite.org/c3ref/commit_hook.html 137 */ 138 void setCommitHook(int (*aCallbackFn)(void*), void* aData = nullptr) { 139 MOZ_ASSERT(mDBConn, "A connection must exist at this point"); 140 ::sqlite3_commit_hook(mDBConn, aCallbackFn, aData); 141 }; 142 143 /** 144 * Gets autocommit status. 145 */ 146 bool getAutocommit() { 147 return mDBConn && static_cast<bool>(::sqlite3_get_autocommit(mDBConn)); 148 }; 149 150 /** 151 * Lazily creates and returns a background execution thread. In the future, 152 * the thread may be re-claimed if left idle, so you should call this 153 * method just before you dispatch and not save the reference. 154 * 155 * This must be called from the opener thread. 156 * 157 * @return an event target suitable for asynchronous statement execution. 158 * @note This method will return null once AsyncClose() has been called. 159 */ 160 nsIEventTarget* getAsyncExecutionTarget(); 161 162 /** 163 * Mutex used by asynchronous statements to protect state. The mutex is 164 * declared on the connection object because there is no contention between 165 * asynchronous statements (they are serialized on mAsyncExecutionThread). 166 * Currently protects: 167 * - Connection.mAsyncExecutionThreadShuttingDown 168 * - Connection.mConnectionClosed 169 * - AsyncExecuteStatements.mCancelRequested 170 * - Connection.mLoadedExtensions 171 */ 172 Mutex sharedAsyncExecutionMutex MOZ_UNANNOTATED; 173 174 /** 175 * Wraps the mutex that SQLite gives us from sqlite3_db_mutex. This is public 176 * because we already expose the sqlite3* native connection and proper 177 * operation of the deadlock detector requires everyone to use the same single 178 * SQLiteMutex instance for correctness. 179 */ 180 SQLiteMutex sharedDBMutex; 181 182 /** 183 * References the event target this database was opened on. 184 */ 185 const nsCOMPtr<nsISerialEventTarget> eventTargetOpenedOn; 186 187 /** 188 * Closes the SQLite database, and warns about any non-finalized statements. 189 */ 190 nsresult internalClose(sqlite3* aNativeconnection); 191 192 /** 193 * Shuts down the passed-in async thread. 194 */ 195 void shutdownAsyncThread(); 196 197 /** 198 * Obtains the filename of the connection. Useful for logging. 199 */ 200 nsCString getFilename(); 201 202 /** 203 * Creates an sqlite3 prepared statement object from an SQL string. 204 * 205 * @param aNativeConnection 206 * The underlying Sqlite connection to prepare the statement with. 207 * @param aSQL 208 * The SQL statement string to compile. 209 * @param _stmt 210 * New sqlite3_stmt object. 211 * @return the result from sqlite3_prepare_v2. 212 */ 213 int prepareStatement(sqlite3* aNativeConnection, const nsCString& aSQL, 214 sqlite3_stmt** _stmt); 215 216 /** 217 * Performs a sqlite3_step on aStatement, while properly handling 218 * SQLITE_LOCKED when not on the main thread by waiting until we are notified. 219 * 220 * @param aNativeConnection 221 * The underlying Sqlite connection to step the statement with. 222 * @param aStatement 223 * A pointer to a sqlite3_stmt object. 224 * @return the result from sqlite3_step. 225 */ 226 int stepStatement(sqlite3* aNativeConnection, sqlite3_stmt* aStatement); 227 228 /** 229 * Raw connection transaction management. 230 * 231 * @see BeginTransactionAs, CommitTransaction, RollbackTransaction. 232 */ 233 nsresult beginTransactionInternal( 234 const SQLiteMutexAutoLock& aProofOfLock, sqlite3* aNativeConnection, 235 int32_t aTransactionType = TRANSACTION_DEFERRED); 236 nsresult commitTransactionInternal(const SQLiteMutexAutoLock& aProofOfLock, 237 sqlite3* aNativeConnection); 238 nsresult rollbackTransactionInternal(const SQLiteMutexAutoLock& aProofOfLock, 239 sqlite3* aNativeConnection); 240 241 /** 242 * Indicates if this database connection is open. 243 */ 244 inline bool connectionReady() { return mDBConn != nullptr; } 245 246 /** 247 * Indicates if this database connection has an open transaction. Because 248 * multiple threads can execute statements on the same connection, this method 249 * requires proof that the caller is holding `sharedDBMutex`. 250 * 251 * Per the SQLite docs, `sqlite3_get_autocommit` returns 0 if autocommit mode 252 * is disabled. `BEGIN` disables autocommit mode, and `COMMIT`, `ROLLBACK`, or 253 * an automatic rollback re-enables it. 254 */ 255 inline bool transactionInProgress(const SQLiteMutexAutoLock& aProofOfLock) { 256 return !getAutocommit(); 257 } 258 259 /** 260 * Indicates if this database connection supports the given operation. 261 * 262 * @param aOperationType 263 * The operation type, sync or async. 264 * @return `true` if the operation is supported, `false` otherwise. 265 */ 266 bool operationSupported(ConnectionOperation aOperationType); 267 268 /** 269 * Thread-aware version of connectionReady, results per caller's thread are: 270 * - owner thread: Same as connectionReady(). True means we have a valid, 271 * un-closed database connection and it's not going away until you invoke 272 * Close() or AsyncClose(). 273 * - async thread: Returns true at all times because you can't schedule 274 * runnables against the async thread after AsyncClose() has been called. 275 * Therefore, the connection is still around if your code is running. 276 * - any other thread: Race-prone Lies! If you are main-thread code in 277 * mozStorageService iterating over the list of connections, you need to 278 * acquire the sharedAsyncExecutionMutex for the connection, invoke 279 * connectionReady() while holding it, and then continue to hold it while 280 * you do whatever you need to do. This is because of off-main-thread 281 * consumers like dom/cache and IndexedDB and other QuotaManager clients. 282 */ 283 bool isConnectionReadyOnThisThread(); 284 285 /** 286 * True if this connection has inited shutdown. 287 */ 288 bool isClosing(); 289 290 /** 291 * True if the underlying connection is closed. 292 * Any sqlite resources may be lost when this returns true, so nothing should 293 * try to use them. 294 * This locks on sharedAsyncExecutionMutex. 295 */ 296 bool isClosed(); 297 298 /** 299 * Same as isClosed(), but takes a proof-of-lock instead of locking 300 * internally. 301 */ 302 bool isClosed(MutexAutoLock& lock); 303 304 /** 305 * True if the async execution thread is alive and able to be used (i.e., it 306 * is not in the process of shutting down.) 307 * 308 * This must be called from the opener thread. 309 */ 310 bool isAsyncExecutionThreadAvailable(); 311 312 nsresult initializeClone(Connection* aClone, bool aReadOnly); 313 314 /** 315 * Records a status from a sqlite statement. 316 * 317 * @param srv The sqlite result for the failure or SQLITE_OK. 318 */ 319 void RecordQueryStatus(int srv); 320 321 /** 322 * Returns the number of pages in the free list that can be removed. 323 * 324 * A database may use chunked growth to reduce filesystem fragmentation, then 325 * Sqlite will allocate and release multiple pages in chunks. We want to 326 * preserve the chunked space to reduce the likelihood of fragmentation, 327 * releasing free pages only when there's a large amount of them. This can be 328 * used to decide if it's worth vacuuming the database and how many pages can 329 * be vacuumed in case of incremental vacuum. 330 * Note this returns 0, and asserts, in case of errors. 331 */ 332 int32_t RemovablePagesInFreeList(const nsACString& aSchemaName); 333 334 /** 335 * Whether the statement currently running on the helper thread can be 336 * interrupted. 337 */ 338 Atomic<bool> mIsStatementOnHelperThreadInterruptible; 339 340 private: 341 ~Connection(); 342 nsresult initializeInternal(); 343 void initializeFailed(); 344 345 /** 346 * Records the status of an attempt to load a sqlite database to telemetry. 347 * 348 * @param rv The state of the load, success or failure. 349 */ 350 void RecordOpenStatus(nsresult rv); 351 352 /** 353 * Sets the database into a closed state so no further actions can be 354 * performed. 355 * 356 * @note mDBConn is set to nullptr in this method. 357 */ 358 nsresult setClosedState(); 359 360 /** 361 * Helper for calls to sqlite3_exec. Reports long delays to Telemetry. 362 * 363 * @param aNativeConnection 364 * The underlying Sqlite connection to execute the query with. 365 * @param aSqlString 366 * SQL string to execute 367 * @return the result from sqlite3_exec. 368 */ 369 int executeSql(sqlite3* aNativeConnection, const char* aSqlString); 370 371 /** 372 * Describes a certain primitive type in the database. 373 * 374 * Possible Values Are: 375 * INDEX - To check for the existence of an index 376 * TABLE - To check for the existence of a table 377 */ 378 enum DatabaseElementType { INDEX, TABLE }; 379 380 /** 381 * Determines if the specified primitive exists. 382 * 383 * @param aElementType 384 * The type of element to check the existence of 385 * @param aElementName 386 * The name of the element to check for 387 * @returns true if element exists, false otherwise 388 */ 389 nsresult databaseElementExists(enum DatabaseElementType aElementType, 390 const nsACString& aElementName, bool* _exists); 391 392 bool findFunctionByInstance(mozIStorageFunction* aInstance); 393 394 static int sProgressHelper(void* aArg); 395 // Generic progress handler 396 // Dispatch call to registered progress handler, 397 // if there is one. Do nothing in other cases. 398 int progressHandler(); 399 400 /** 401 * Like `operationSupported`, but throws (and, in a debug build, asserts) if 402 * the operation is unsupported. 403 */ 404 nsresult ensureOperationSupported(ConnectionOperation aOperationType); 405 406 sqlite3* mDBConn; 407 nsCString mStorageKey; 408 nsCString mName; 409 nsCOMPtr<nsIFileURL> mFileURL; 410 nsCOMPtr<nsIFile> mDatabaseFile; 411 412 /** 413 * Lazily created thread for asynchronous statement execution. Consumers 414 * should use getAsyncExecutionTarget rather than directly accessing this 415 * field. 416 * 417 * This must be modified only on the opener thread. 418 */ 419 nsCOMPtr<nsIThread> mAsyncExecutionThread; 420 421 /** 422 * The filename that will be reported to telemetry for this connection. By 423 * default this will be the leaf of the path to the database file. 424 */ 425 nsCString mTelemetryFilename; 426 427 /** 428 * Stores the default behavior for all transactions run on this connection. 429 */ 430 mozilla::Atomic<int32_t> mDefaultTransactionType; 431 432 /** 433 * Used to trigger cleanup logic only the first time our refcount hits 1. We 434 * may trigger a failsafe Close() that invokes SpinningSynchronousClose() 435 * which invokes AsyncClose() which may bump our refcount back up to 2 (and 436 * which will then fall back down to 1 again). It's also possible that the 437 * Service may bump our refcount back above 1 if getConnections() runs before 438 * we invoke unregisterConnection(). 439 */ 440 mozilla::Atomic<bool> mDestroying; 441 442 /** 443 * Stores the mapping of a given function by name to its instance. Access is 444 * protected by sharedDBMutex. 445 */ 446 nsTHashMap<nsCStringHashKey, FunctionInfo> mFunctions; 447 448 /** 449 * Stores the registered progress handler for the database connection. Access 450 * is protected by sharedDBMutex. 451 */ 452 nsCOMPtr<mozIStorageProgressHandler> mProgressHandler; 453 454 // This is here for two reasons: 1) It's used to make sure that the 455 // connections do not outlive the service. 2) Our custom collating functions 456 // call its localeCompareStrings() method. 457 RefPtr<Service> mStorageService; 458 459 nsresult synchronousClose(); 460 461 /** 462 * Stores the flags we passed to sqlite3_open_v2. 463 */ 464 const int mFlags; 465 466 uint32_t mTransactionNestingLevel; 467 468 /** 469 * Indicates which operations are supported on this connection. 470 */ 471 const ConnectionOperation mSupportedOperations; 472 473 /** 474 * Stores whether this connection is interruptible. 475 */ 476 const bool mInterruptible; 477 478 /** 479 * Stores whether we should ask sqlite3_open_v2 to ignore locking. 480 */ 481 const bool mIgnoreLockingMode; 482 483 /** 484 * Stores whether we should ask sqlite3_open_v2 to open without an exclusive 485 * lock. 486 */ 487 const bool mOpenNotExclusive; 488 489 /** 490 * Set to true by Close() or AsyncClose() prior to shutdown. 491 * 492 * If false, we guarantee both that the underlying sqlite3 database 493 * connection is still open and that getAsyncExecutionTarget() can 494 * return a thread. Once true, either the sqlite3 database 495 * connection is being shutdown or it has been 496 * shutdown. Additionally, once true, getAsyncExecutionTarget() 497 * returns null. 498 * 499 * This variable should be accessed while holding the 500 * sharedAsyncExecutionMutex. 501 */ 502 bool mAsyncExecutionThreadShuttingDown; 503 504 /** 505 * Set to true just prior to calling sqlite3_close on the 506 * connection. 507 * 508 * This variable should be accessed while holding the 509 * sharedAsyncExecutionMutex. 510 */ 511 bool mConnectionClosed; 512 513 /** 514 * Stores the growth increment chunk size, set through SetGrowthIncrement(). 515 */ 516 Atomic<int32_t> mGrowthChunkSize; 517 518 /** 519 * Stores a list of the SQLite extensions loaded for this connections. 520 * This is used to properly clone the connection. 521 * @note Hold sharedAsyncExecutionMutex while using this. 522 */ 523 nsTHashSet<nsCString> mLoadedExtensions 524 MOZ_GUARDED_BY(sharedAsyncExecutionMutex); 525 }; 526 527 /** 528 * A Runnable designed to call a mozIStorageCompletionCallback on 529 * the appropriate thread. 530 */ 531 class CallbackComplete final : public Runnable { 532 public: 533 /** 534 * @param aValue The result to pass to the callback. It must 535 * already be owned by the main thread. 536 * @param aCallback The callback. It must already be owned by the 537 * main thread. 538 */ 539 CallbackComplete(nsresult aStatus, nsISupports* aValue, 540 already_AddRefed<mozIStorageCompletionCallback> aCallback) 541 : Runnable("storage::CallbackComplete"), 542 mStatus(aStatus), 543 mValue(aValue), 544 mCallback(aCallback) {} 545 546 NS_IMETHOD Run() override { 547 MOZ_ASSERT(NS_IsMainThread()); 548 nsresult rv = mCallback->Complete(mStatus, mValue); 549 550 // Ensure that we release on the main thread 551 mValue = nullptr; 552 mCallback = nullptr; 553 return rv; 554 } 555 556 private: 557 nsresult mStatus; 558 nsCOMPtr<nsISupports> mValue; 559 // This is a RefPtr<T> and not a nsCOMPtr<T> because 560 // nsCOMP<T> would cause an off-main thread QI, which 561 // is not a good idea (and crashes XPConnect). 562 RefPtr<mozIStorageCompletionCallback> mCallback; 563 }; 564 565 } // namespace mozilla::storage 566 567 /** 568 * Casting Connection to nsISupports is ambiguous. 569 * This method handles that. 570 */ 571 inline nsISupports* ToSupports(mozilla::storage::Connection* p) { 572 return NS_ISUPPORTS_CAST(mozIStorageAsyncConnection*, p); 573 } 574 575 #endif // mozilla_storage_Connection_h