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