Line data Source code
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 "nsAutoPtr.h"
11 : #include "nsCOMPtr.h"
12 : #include "mozilla/Mutex.h"
13 : #include "nsProxyRelease.h"
14 : #include "nsThreadUtils.h"
15 : #include "nsIInterfaceRequestor.h"
16 :
17 : #include "nsDataHashtable.h"
18 : #include "mozIStorageProgressHandler.h"
19 : #include "SQLiteMutex.h"
20 : #include "mozIStorageConnection.h"
21 : #include "mozStorageService.h"
22 : #include "mozIStorageAsyncConnection.h"
23 : #include "mozIStorageCompletionCallback.h"
24 :
25 : #include "nsIMutableArray.h"
26 : #include "mozilla/Attributes.h"
27 :
28 : #include "sqlite3.h"
29 :
30 : class nsIFile;
31 : class nsIFileURL;
32 : class nsIEventTarget;
33 : class nsIThread;
34 :
35 : namespace mozilla {
36 : namespace storage {
37 :
38 : class Connection final : public mozIStorageConnection
39 : , public nsIInterfaceRequestor
40 : {
41 : public:
42 : NS_DECL_THREADSAFE_ISUPPORTS
43 : NS_DECL_MOZISTORAGEASYNCCONNECTION
44 : NS_DECL_MOZISTORAGECONNECTION
45 : NS_DECL_NSIINTERFACEREQUESTOR
46 :
47 : /**
48 : * Structure used to describe user functions on the database connection.
49 : */
50 64 : struct FunctionInfo {
51 : enum FunctionType {
52 : SIMPLE,
53 : AGGREGATE
54 : };
55 :
56 : nsCOMPtr<nsISupports> function;
57 : FunctionType type;
58 : int32_t numArgs;
59 : };
60 :
61 : /**
62 : * @param aService
63 : * Pointer to the storage service. Held onto for the lifetime of the
64 : * connection.
65 : * @param aFlags
66 : * The flags to pass to sqlite3_open_v2.
67 : * @param aAsyncOnly
68 : * If |true|, the Connection only implements asynchronous interface:
69 : * - |mozIStorageAsyncConnection|;
70 : * If |false|, the result also implements synchronous interface:
71 : * - |mozIStorageConnection|.
72 : * @param aIgnoreLockingMode
73 : * If |true|, ignore locks in force on the file. Only usable with
74 : * read-only connections. Defaults to false.
75 : * Use with extreme caution. If sqlite ignores locks, reads may fail
76 : * indicating database corruption (the database won't actually be
77 : * corrupt) or produce wrong results without any indication that has
78 : * happened.
79 : */
80 : Connection(Service *aService, int aFlags, bool aAsyncOnly,
81 : bool aIgnoreLockingMode = false);
82 :
83 : /**
84 : * Creates the connection to an in-memory database.
85 : */
86 : nsresult initialize();
87 :
88 : /**
89 : * Creates the connection to the database.
90 : *
91 : * @param aDatabaseFile
92 : * The nsIFile of the location of the database to open, or create if it
93 : * does not exist.
94 : */
95 : nsresult initialize(nsIFile *aDatabaseFile);
96 :
97 : /**
98 : * Creates the connection to the database.
99 : *
100 : * @param aFileURL
101 : * The nsIFileURL of the location of the database to open, or create if it
102 : * does not exist.
103 : */
104 : nsresult initialize(nsIFileURL *aFileURL);
105 :
106 : /**
107 : * Same as initialize, but to be used on the async thread.
108 : */
109 : nsresult initializeOnAsyncThread(nsIFile* aStorageFile);
110 :
111 : /**
112 : * Fetches runtime status information for this connection.
113 : *
114 : * @param aStatusOption One of the SQLITE_DBSTATUS options defined at
115 : * http://www.sqlite.org/c3ref/c_dbstatus_options.html
116 : * @param [optional] aMaxValue if provided, will be set to the highest
117 : * istantaneous value.
118 : * @return the current value for the specified option.
119 : */
120 : int32_t getSqliteRuntimeStatus(int32_t aStatusOption,
121 : int32_t* aMaxValue=nullptr);
122 : /**
123 : * Registers/unregisters a commit hook callback.
124 : *
125 : * @param aCallbackFn a callback function to be invoked on transactions
126 : * commit. Pass nullptr to unregister the current callback.
127 : * @param [optional] aData if provided, will be passed to the callback.
128 : * @see http://sqlite.org/c3ref/commit_hook.html
129 : */
130 : void setCommitHook(int (*aCallbackFn)(void *) , void *aData=nullptr) {
131 : MOZ_ASSERT(mDBConn, "A connection must exist at this point");
132 : ::sqlite3_commit_hook(mDBConn, aCallbackFn, aData);
133 : };
134 :
135 : /**
136 : * Gets autocommit status.
137 : */
138 0 : bool getAutocommit() {
139 0 : return mDBConn && static_cast<bool>(::sqlite3_get_autocommit(mDBConn));
140 : };
141 :
142 : /**
143 : * Lazily creates and returns a background execution thread. In the future,
144 : * the thread may be re-claimed if left idle, so you should call this
145 : * method just before you dispatch and not save the reference.
146 : *
147 : * This must be called from the opener thread.
148 : *
149 : * @return an event target suitable for asynchronous statement execution.
150 : * @note This method will return null once AsyncClose() has been called.
151 : */
152 : nsIEventTarget *getAsyncExecutionTarget();
153 :
154 : /**
155 : * Mutex used by asynchronous statements to protect state. The mutex is
156 : * declared on the connection object because there is no contention between
157 : * asynchronous statements (they are serialized on mAsyncExecutionThread).
158 : * Currently protects:
159 : * - Connection.mAsyncExecutionThreadShuttingDown
160 : * - Connection.mConnectionClosed
161 : * - AsyncExecuteStatements.mCancelRequested
162 : */
163 : Mutex sharedAsyncExecutionMutex;
164 :
165 : /**
166 : * Wraps the mutex that SQLite gives us from sqlite3_db_mutex. This is public
167 : * because we already expose the sqlite3* native connection and proper
168 : * operation of the deadlock detector requires everyone to use the same single
169 : * SQLiteMutex instance for correctness.
170 : */
171 : SQLiteMutex sharedDBMutex;
172 :
173 : /**
174 : * References the thread this database was opened on. This MUST be thread it is
175 : * closed on.
176 : */
177 : const nsCOMPtr<nsIThread> threadOpenedOn;
178 :
179 : /**
180 : * Closes the SQLite database, and warns about any non-finalized statements.
181 : */
182 : nsresult internalClose(sqlite3 *aDBConn);
183 :
184 : /**
185 : * Shuts down the passed-in async thread.
186 : */
187 : void shutdownAsyncThread();
188 :
189 : /**
190 : * Obtains the filename of the connection. Useful for logging.
191 : */
192 : nsCString getFilename();
193 :
194 : /**
195 : * Creates an sqlite3 prepared statement object from an SQL string.
196 : *
197 : * @param aNativeConnection
198 : * The underlying Sqlite connection to prepare the statement with.
199 : * @param aSQL
200 : * The SQL statement string to compile.
201 : * @param _stmt
202 : * New sqlite3_stmt object.
203 : * @return the result from sqlite3_prepare_v2.
204 : */
205 : int prepareStatement(sqlite3* aNativeConnection,
206 : const nsCString &aSQL, sqlite3_stmt **_stmt);
207 :
208 : /**
209 : * Performs a sqlite3_step on aStatement, while properly handling SQLITE_LOCKED
210 : * when not on the main thread by waiting until we are notified.
211 : *
212 : * @param aNativeConnection
213 : * The underlying Sqlite connection to step the statement with.
214 : * @param aStatement
215 : * A pointer to a sqlite3_stmt object.
216 : * @return the result from sqlite3_step.
217 : */
218 : int stepStatement(sqlite3* aNativeConnection, sqlite3_stmt* aStatement);
219 :
220 : /**
221 : * Raw connection transaction management.
222 : *
223 : * @see BeginTransactionAs, CommitTransaction, RollbackTransaction.
224 : */
225 : nsresult beginTransactionInternal(sqlite3 *aNativeConnection,
226 : int32_t aTransactionType=TRANSACTION_DEFERRED);
227 : nsresult commitTransactionInternal(sqlite3 *aNativeConnection);
228 : nsresult rollbackTransactionInternal(sqlite3 *aNativeConnection);
229 :
230 : bool connectionReady();
231 :
232 : /**
233 : * Thread-aware version of connectionReady, results per caller's thread are:
234 : * - owner thread: Same as connectionReady(). True means we have a valid,
235 : * un-closed database connection and it's not going away until you invoke
236 : * Close() or AsyncClose().
237 : * - async thread: Returns true at all times because you can't schedule
238 : * runnables against the async thread after AsyncClose() has been called.
239 : * Therefore, the connection is still around if your code is running.
240 : * - any other thread: Race-prone Lies! If you are main-thread code in
241 : * mozStorageService iterating over the list of connections, you need to
242 : * acquire the sharedAsyncExecutionMutex for the connection, invoke
243 : * connectionReady() while holding it, and then continue to hold it while
244 : * you do whatever you need to do. This is because of off-main-thread
245 : * consumers like dom/cache and IndexedDB and other QuotaManager clients.
246 : */
247 : bool isConnectionReadyOnThisThread();
248 :
249 : /**
250 : * True if this connection has inited shutdown.
251 : */
252 : bool isClosing();
253 :
254 : /**
255 : * True if the underlying connection is closed.
256 : * Any sqlite resources may be lost when this returns true, so nothing should
257 : * try to use them.
258 : * This locks on sharedAsyncExecutionMutex.
259 : */
260 : bool isClosed();
261 :
262 : /**
263 : * Same as isClosed(), but takes a proof-of-lock instead of locking internally.
264 : */
265 : bool isClosed(MutexAutoLock& lock);
266 :
267 : /**
268 : * True if the async execution thread is alive and able to be used (i.e., it
269 : * is not in the process of shutting down.)
270 : *
271 : * This must be called from the opener thread.
272 : */
273 : bool isAsyncExecutionThreadAvailable();
274 :
275 : nsresult initializeClone(Connection *aClone, bool aReadOnly);
276 :
277 : private:
278 : ~Connection();
279 : nsresult initializeInternal();
280 :
281 : /**
282 : * Sets the database into a closed state so no further actions can be
283 : * performed.
284 : *
285 : * @note mDBConn is set to nullptr in this method.
286 : */
287 : nsresult setClosedState();
288 :
289 : /**
290 : * Helper for calls to sqlite3_exec. Reports long delays to Telemetry.
291 : *
292 : * @param aNativeConnection
293 : * The underlying Sqlite connection to execute the query with.
294 : * @param aSqlString
295 : * SQL string to execute
296 : * @return the result from sqlite3_exec.
297 : */
298 : int executeSql(sqlite3 *aNativeConnection, const char *aSqlString);
299 :
300 : /**
301 : * Describes a certain primitive type in the database.
302 : *
303 : * Possible Values Are:
304 : * INDEX - To check for the existence of an index
305 : * TABLE - To check for the existence of a table
306 : */
307 : enum DatabaseElementType {
308 : INDEX,
309 : TABLE
310 : };
311 :
312 : /**
313 : * Determines if the specified primitive exists.
314 : *
315 : * @param aElementType
316 : * The type of element to check the existence of
317 : * @param aElementName
318 : * The name of the element to check for
319 : * @returns true if element exists, false otherwise
320 : */
321 : nsresult databaseElementExists(enum DatabaseElementType aElementType,
322 : const nsACString& aElementName,
323 : bool *_exists);
324 :
325 : bool findFunctionByInstance(nsISupports *aInstance);
326 :
327 : static int sProgressHelper(void *aArg);
328 : // Generic progress handler
329 : // Dispatch call to registered progress handler,
330 : // if there is one. Do nothing in other cases.
331 : int progressHandler();
332 :
333 : sqlite3 *mDBConn;
334 : nsCOMPtr<nsIFileURL> mFileURL;
335 : nsCOMPtr<nsIFile> mDatabaseFile;
336 :
337 : /**
338 : * The filename that will be reported to telemetry for this connection. By
339 : * default this will be the leaf of the path to the database file.
340 : */
341 : nsCString mTelemetryFilename;
342 :
343 : /**
344 : * Lazily created thread for asynchronous statement execution. Consumers
345 : * should use getAsyncExecutionTarget rather than directly accessing this
346 : * field.
347 : *
348 : * This must be modified only on the opener thread.
349 : */
350 : nsCOMPtr<nsIThread> mAsyncExecutionThread;
351 :
352 : /**
353 : * Set to true by Close() or AsyncClose() prior to shutdown.
354 : *
355 : * If false, we guarantee both that the underlying sqlite3 database
356 : * connection is still open and that getAsyncExecutionTarget() can
357 : * return a thread. Once true, either the sqlite3 database
358 : * connection is being shutdown or it has been
359 : * shutdown. Additionally, once true, getAsyncExecutionTarget()
360 : * returns null.
361 : *
362 : * This variable should be accessed while holding the
363 : * sharedAsyncExecutionMutex.
364 : */
365 : bool mAsyncExecutionThreadShuttingDown;
366 :
367 : /**
368 : * Set to true just prior to calling sqlite3_close on the
369 : * connection.
370 : *
371 : * This variable should be accessed while holding the
372 : * sharedAsyncExecutionMutex.
373 : */
374 : bool mConnectionClosed;
375 :
376 : /**
377 : * Tracks if we have a transaction in progress or not. Access protected by
378 : * sharedDBMutex.
379 : */
380 : bool mTransactionInProgress;
381 :
382 : /**
383 : * Stores the mapping of a given function by name to its instance. Access is
384 : * protected by sharedDBMutex.
385 : */
386 : nsDataHashtable<nsCStringHashKey, FunctionInfo> mFunctions;
387 :
388 : /**
389 : * Stores the registered progress handler for the database connection. Access
390 : * is protected by sharedDBMutex.
391 : */
392 : nsCOMPtr<mozIStorageProgressHandler> mProgressHandler;
393 :
394 : /**
395 : * Stores the flags we passed to sqlite3_open_v2.
396 : */
397 : const int mFlags;
398 :
399 : /**
400 : * Stores whether we should ask sqlite3_open_v2 to ignore locking.
401 : */
402 : const bool mIgnoreLockingMode;
403 :
404 : // This is here for two reasons: 1) It's used to make sure that the
405 : // connections do not outlive the service. 2) Our custom collating functions
406 : // call its localeCompareStrings() method.
407 : RefPtr<Service> mStorageService;
408 :
409 : /**
410 : * If |false|, this instance supports synchronous operations
411 : * and it can be cast to |mozIStorageConnection|.
412 : */
413 : const bool mAsyncOnly;
414 : };
415 :
416 :
417 : /**
418 : * A Runnable designed to call a mozIStorageCompletionCallback on
419 : * the appropriate thread.
420 : */
421 3 : class CallbackComplete final : public Runnable
422 : {
423 : public:
424 : /**
425 : * @param aValue The result to pass to the callback. It must
426 : * already be owned by the main thread.
427 : * @param aCallback The callback. It must already be owned by the
428 : * main thread.
429 : */
430 1 : CallbackComplete(nsresult aStatus,
431 : nsISupports* aValue,
432 : already_AddRefed<mozIStorageCompletionCallback> aCallback)
433 1 : : Runnable("storage::CallbackComplete")
434 : , mStatus(aStatus)
435 : , mValue(aValue)
436 1 : , mCallback(aCallback)
437 : {
438 1 : }
439 :
440 1 : NS_IMETHOD Run() override {
441 1 : MOZ_ASSERT(NS_IsMainThread());
442 1 : nsresult rv = mCallback->Complete(mStatus, mValue);
443 :
444 : // Ensure that we release on the main thread
445 1 : mValue = nullptr;
446 1 : mCallback = nullptr;
447 1 : return rv;
448 : }
449 :
450 : private:
451 : nsresult mStatus;
452 : nsCOMPtr<nsISupports> mValue;
453 : // This is a RefPtr<T> and not a nsCOMPtr<T> because
454 : // nsCOMP<T> would cause an off-main thread QI, which
455 : // is not a good idea (and crashes XPConnect).
456 : RefPtr<mozIStorageCompletionCallback> mCallback;
457 : };
458 :
459 : } // namespace storage
460 : } // namespace mozilla
461 :
462 : /**
463 : * Casting Connection to nsISupports is ambiguous.
464 : * This method handles that.
465 : */
466 : inline nsISupports*
467 8 : ToSupports(mozilla::storage::Connection* p)
468 : {
469 8 : return NS_ISUPPORTS_CAST(mozIStorageAsyncConnection*, p);
470 : }
471 :
472 : #endif // mozilla_storage_Connection_h
|