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 #include <stdio.h>
8 
9 #include "nsError.h"
10 #include "nsThreadUtils.h"
11 #include "nsIFile.h"
12 #include "nsIFileURL.h"
13 #include "nsIXPConnect.h"
14 #include "mozilla/Telemetry.h"
15 #include "mozilla/Mutex.h"
16 #include "mozilla/CondVar.h"
17 #include "mozilla/Attributes.h"
18 #include "mozilla/ErrorNames.h"
19 #include "mozilla/Unused.h"
20 #include "mozilla/dom/quota/QuotaObject.h"
21 #include "mozilla/ScopeExit.h"
22 #include "mozilla/SpinEventLoopUntil.h"
23 #include "mozilla/StaticPrefs_storage.h"
24 
25 #include "mozIStorageCompletionCallback.h"
26 #include "mozIStorageFunction.h"
27 
28 #include "mozStorageAsyncStatementExecution.h"
29 #include "mozStorageSQLFunctions.h"
30 #include "mozStorageConnection.h"
31 #include "mozStorageService.h"
32 #include "mozStorageStatement.h"
33 #include "mozStorageAsyncStatement.h"
34 #include "mozStorageArgValueArray.h"
35 #include "mozStoragePrivateHelpers.h"
36 #include "mozStorageStatementData.h"
37 #include "StorageBaseStatementInternal.h"
38 #include "SQLCollations.h"
39 #include "FileSystemModule.h"
40 #include "mozStorageHelper.h"
41 
42 #include "mozilla/Logging.h"
43 #include "mozilla/Printf.h"
44 #include "mozilla/ProfilerLabels.h"
45 #include "nsProxyRelease.h"
46 #include "nsURLHelper.h"
47 
48 #include <algorithm>
49 
50 #define MIN_AVAILABLE_BYTES_PER_CHUNKED_GROWTH 524288000  // 500 MiB
51 
52 // Maximum size of the pages cache per connection.
53 #define MAX_CACHE_SIZE_KIBIBYTES 2048  // 2 MiB
54 
55 mozilla::LazyLogModule gStorageLog("mozStorage");
56 
57 // Checks that the protected code is running on the main-thread only if the
58 // connection was also opened on it.
59 #ifdef DEBUG
60 #  define CHECK_MAINTHREAD_ABUSE()                             \
61     do {                                                       \
62       nsCOMPtr<nsIThread> mainThread = do_GetMainThread();     \
63       NS_WARNING_ASSERTION(                                    \
64           threadOpenedOn == mainThread || !NS_IsMainThread(),  \
65           "Using Storage synchronous API on main-thread, but " \
66           "the connection was "                                \
67           "opened on another thread.");                        \
68     } while (0)
69 #else
70 #  define CHECK_MAINTHREAD_ABUSE() \
71     do { /* Nothing */             \
72     } while (0)
73 #endif
74 
75 namespace mozilla::storage {
76 
77 using mozilla::dom::quota::QuotaObject;
78 using mozilla::Telemetry::AccumulateCategoricalKeyed;
79 using mozilla::Telemetry::LABELS_SQLITE_STORE_OPEN;
80 using mozilla::Telemetry::LABELS_SQLITE_STORE_QUERY;
81 
82 const char* GetTelemetryVFSName(bool);
83 const char* GetObfuscatingVFSName();
84 
85 namespace {
86 
nsresultToSQLiteResult(nsresult aXPCOMResultCode)87 int nsresultToSQLiteResult(nsresult aXPCOMResultCode) {
88   if (NS_SUCCEEDED(aXPCOMResultCode)) {
89     return SQLITE_OK;
90   }
91 
92   switch (aXPCOMResultCode) {
93     case NS_ERROR_FILE_CORRUPTED:
94       return SQLITE_CORRUPT;
95     case NS_ERROR_FILE_ACCESS_DENIED:
96       return SQLITE_CANTOPEN;
97     case NS_ERROR_STORAGE_BUSY:
98       return SQLITE_BUSY;
99     case NS_ERROR_FILE_IS_LOCKED:
100       return SQLITE_LOCKED;
101     case NS_ERROR_FILE_READ_ONLY:
102       return SQLITE_READONLY;
103     case NS_ERROR_STORAGE_IOERR:
104       return SQLITE_IOERR;
105     case NS_ERROR_FILE_NO_DEVICE_SPACE:
106       return SQLITE_FULL;
107     case NS_ERROR_OUT_OF_MEMORY:
108       return SQLITE_NOMEM;
109     case NS_ERROR_UNEXPECTED:
110       return SQLITE_MISUSE;
111     case NS_ERROR_ABORT:
112       return SQLITE_ABORT;
113     case NS_ERROR_STORAGE_CONSTRAINT:
114       return SQLITE_CONSTRAINT;
115     default:
116       return SQLITE_ERROR;
117   }
118 
119   MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Must return in switch above!");
120 }
121 
122 ////////////////////////////////////////////////////////////////////////////////
123 //// Variant Specialization Functions (variantToSQLiteT)
124 
sqlite3_T_int(sqlite3_context * aCtx,int aValue)125 int sqlite3_T_int(sqlite3_context* aCtx, int aValue) {
126   ::sqlite3_result_int(aCtx, aValue);
127   return SQLITE_OK;
128 }
129 
sqlite3_T_int64(sqlite3_context * aCtx,sqlite3_int64 aValue)130 int sqlite3_T_int64(sqlite3_context* aCtx, sqlite3_int64 aValue) {
131   ::sqlite3_result_int64(aCtx, aValue);
132   return SQLITE_OK;
133 }
134 
sqlite3_T_double(sqlite3_context * aCtx,double aValue)135 int sqlite3_T_double(sqlite3_context* aCtx, double aValue) {
136   ::sqlite3_result_double(aCtx, aValue);
137   return SQLITE_OK;
138 }
139 
sqlite3_T_text(sqlite3_context * aCtx,const nsCString & aValue)140 int sqlite3_T_text(sqlite3_context* aCtx, const nsCString& aValue) {
141   ::sqlite3_result_text(aCtx, aValue.get(), aValue.Length(), SQLITE_TRANSIENT);
142   return SQLITE_OK;
143 }
144 
sqlite3_T_text16(sqlite3_context * aCtx,const nsString & aValue)145 int sqlite3_T_text16(sqlite3_context* aCtx, const nsString& aValue) {
146   ::sqlite3_result_text16(
147       aCtx, aValue.get(),
148       aValue.Length() * sizeof(char16_t),  // Number of bytes.
149       SQLITE_TRANSIENT);
150   return SQLITE_OK;
151 }
152 
sqlite3_T_null(sqlite3_context * aCtx)153 int sqlite3_T_null(sqlite3_context* aCtx) {
154   ::sqlite3_result_null(aCtx);
155   return SQLITE_OK;
156 }
157 
sqlite3_T_blob(sqlite3_context * aCtx,const void * aData,int aSize)158 int sqlite3_T_blob(sqlite3_context* aCtx, const void* aData, int aSize) {
159   ::sqlite3_result_blob(aCtx, aData, aSize, free);
160   return SQLITE_OK;
161 }
162 
163 #include "variantToSQLiteT_impl.h"
164 
165 ////////////////////////////////////////////////////////////////////////////////
166 //// Modules
167 
168 struct Module {
169   const char* name;
170   int (*registerFunc)(sqlite3*, const char*);
171 };
172 
173 Module gModules[] = {{"filesystem", RegisterFileSystemModule}};
174 
175 ////////////////////////////////////////////////////////////////////////////////
176 //// Local Functions
177 
tracefunc(unsigned aReason,void * aClosure,void * aP,void * aX)178 int tracefunc(unsigned aReason, void* aClosure, void* aP, void* aX) {
179   switch (aReason) {
180     case SQLITE_TRACE_STMT: {
181       // aP is a pointer to the prepared statement.
182       sqlite3_stmt* stmt = static_cast<sqlite3_stmt*>(aP);
183       // aX is a pointer to a string containing the unexpanded SQL or a comment,
184       // starting with "--"" in case of a trigger.
185       char* expanded = static_cast<char*>(aX);
186       // Simulate what sqlite_trace was doing.
187       if (!::strncmp(expanded, "--", 2)) {
188         MOZ_LOG(gStorageLog, LogLevel::Debug,
189                 ("TRACE_STMT on %p: '%s'", aClosure, expanded));
190       } else {
191         char* sql = ::sqlite3_expanded_sql(stmt);
192         MOZ_LOG(gStorageLog, LogLevel::Debug,
193                 ("TRACE_STMT on %p: '%s'", aClosure, sql));
194         ::sqlite3_free(sql);
195       }
196       break;
197     }
198     case SQLITE_TRACE_PROFILE: {
199       // aX is pointer to a 64bit integer containing nanoseconds it took to
200       // execute the last command.
201       sqlite_int64 time = *(static_cast<sqlite_int64*>(aX)) / 1000000;
202       if (time > 0) {
203         MOZ_LOG(gStorageLog, LogLevel::Debug,
204                 ("TRACE_TIME on %p: %lldms", aClosure, time));
205       }
206       break;
207     }
208   }
209   return 0;
210 }
211 
basicFunctionHelper(sqlite3_context * aCtx,int aArgc,sqlite3_value ** aArgv)212 void basicFunctionHelper(sqlite3_context* aCtx, int aArgc,
213                          sqlite3_value** aArgv) {
214   void* userData = ::sqlite3_user_data(aCtx);
215 
216   mozIStorageFunction* func = static_cast<mozIStorageFunction*>(userData);
217 
218   RefPtr<ArgValueArray> arguments(new ArgValueArray(aArgc, aArgv));
219   if (!arguments) return;
220 
221   nsCOMPtr<nsIVariant> result;
222   nsresult rv = func->OnFunctionCall(arguments, getter_AddRefs(result));
223   if (NS_FAILED(rv)) {
224     nsAutoCString errorMessage;
225     GetErrorName(rv, errorMessage);
226     errorMessage.InsertLiteral("User function returned ", 0);
227     errorMessage.Append('!');
228 
229     NS_WARNING(errorMessage.get());
230 
231     ::sqlite3_result_error(aCtx, errorMessage.get(), -1);
232     ::sqlite3_result_error_code(aCtx, nsresultToSQLiteResult(rv));
233     return;
234   }
235   int retcode = variantToSQLiteT(aCtx, result);
236   if (retcode != SQLITE_OK) {
237     NS_WARNING("User function returned invalid data type!");
238     ::sqlite3_result_error(aCtx, "User function returned invalid data type",
239                            -1);
240   }
241 }
242 
243 /**
244  * This code is heavily based on the sample at:
245  *   http://www.sqlite.org/unlock_notify.html
246  */
247 class UnlockNotification {
248  public:
UnlockNotification()249   UnlockNotification()
250       : mMutex("UnlockNotification mMutex"),
251         mCondVar(mMutex, "UnlockNotification condVar"),
252         mSignaled(false) {}
253 
Wait()254   void Wait() {
255     MutexAutoLock lock(mMutex);
256     while (!mSignaled) {
257       (void)mCondVar.Wait();
258     }
259   }
260 
Signal()261   void Signal() {
262     MutexAutoLock lock(mMutex);
263     mSignaled = true;
264     (void)mCondVar.Notify();
265   }
266 
267  private:
268   Mutex mMutex;
269   CondVar mCondVar;
270   bool mSignaled;
271 };
272 
UnlockNotifyCallback(void ** aArgs,int aArgsSize)273 void UnlockNotifyCallback(void** aArgs, int aArgsSize) {
274   for (int i = 0; i < aArgsSize; i++) {
275     UnlockNotification* notification =
276         static_cast<UnlockNotification*>(aArgs[i]);
277     notification->Signal();
278   }
279 }
280 
WaitForUnlockNotify(sqlite3 * aDatabase)281 int WaitForUnlockNotify(sqlite3* aDatabase) {
282   UnlockNotification notification;
283   int srv =
284       ::sqlite3_unlock_notify(aDatabase, UnlockNotifyCallback, &notification);
285   MOZ_ASSERT(srv == SQLITE_LOCKED || srv == SQLITE_OK);
286   if (srv == SQLITE_OK) {
287     notification.Wait();
288   }
289 
290   return srv;
291 }
292 
293 ////////////////////////////////////////////////////////////////////////////////
294 //// Local Classes
295 
296 class AsyncCloseConnection final : public Runnable {
297  public:
AsyncCloseConnection(Connection * aConnection,sqlite3 * aNativeConnection,nsIRunnable * aCallbackEvent)298   AsyncCloseConnection(Connection* aConnection, sqlite3* aNativeConnection,
299                        nsIRunnable* aCallbackEvent)
300       : Runnable("storage::AsyncCloseConnection"),
301         mConnection(aConnection),
302         mNativeConnection(aNativeConnection),
303         mCallbackEvent(aCallbackEvent) {}
304 
Run()305   NS_IMETHOD Run() override {
306     // This code is executed on the background thread
307     MOZ_ASSERT(NS_GetCurrentThread() != mConnection->threadOpenedOn);
308 
309     nsCOMPtr<nsIRunnable> event =
310         NewRunnableMethod("storage::Connection::shutdownAsyncThread",
311                           mConnection, &Connection::shutdownAsyncThread);
312     MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(event));
313 
314     // Internal close.
315     (void)mConnection->internalClose(mNativeConnection);
316 
317     // Callback
318     if (mCallbackEvent) {
319       nsCOMPtr<nsIThread> thread;
320       (void)NS_GetMainThread(getter_AddRefs(thread));
321       (void)thread->Dispatch(mCallbackEvent, NS_DISPATCH_NORMAL);
322     }
323 
324     return NS_OK;
325   }
326 
~AsyncCloseConnection()327   ~AsyncCloseConnection() override {
328     NS_ReleaseOnMainThread("AsyncCloseConnection::mConnection",
329                            mConnection.forget());
330     NS_ReleaseOnMainThread("AsyncCloseConnection::mCallbackEvent",
331                            mCallbackEvent.forget());
332   }
333 
334  private:
335   RefPtr<Connection> mConnection;
336   sqlite3* mNativeConnection;
337   nsCOMPtr<nsIRunnable> mCallbackEvent;
338 };
339 
340 /**
341  * An event used to initialize the clone of a connection.
342  *
343  * Must be executed on the clone's async execution thread.
344  */
345 class AsyncInitializeClone final : public Runnable {
346  public:
347   /**
348    * @param aConnection The connection being cloned.
349    * @param aClone The clone.
350    * @param aReadOnly If |true|, the clone is read only.
351    * @param aCallback A callback to trigger once initialization
352    *                  is complete. This event will be called on
353    *                  aClone->threadOpenedOn.
354    */
AsyncInitializeClone(Connection * aConnection,Connection * aClone,const bool aReadOnly,mozIStorageCompletionCallback * aCallback)355   AsyncInitializeClone(Connection* aConnection, Connection* aClone,
356                        const bool aReadOnly,
357                        mozIStorageCompletionCallback* aCallback)
358       : Runnable("storage::AsyncInitializeClone"),
359         mConnection(aConnection),
360         mClone(aClone),
361         mReadOnly(aReadOnly),
362         mCallback(aCallback) {
363     MOZ_ASSERT(NS_IsMainThread());
364   }
365 
Run()366   NS_IMETHOD Run() override {
367     MOZ_ASSERT(!NS_IsMainThread());
368     nsresult rv = mConnection->initializeClone(mClone, mReadOnly);
369     if (NS_FAILED(rv)) {
370       return Dispatch(rv, nullptr);
371     }
372     return Dispatch(NS_OK,
373                     NS_ISUPPORTS_CAST(mozIStorageAsyncConnection*, mClone));
374   }
375 
376  private:
Dispatch(nsresult aResult,nsISupports * aValue)377   nsresult Dispatch(nsresult aResult, nsISupports* aValue) {
378     RefPtr<CallbackComplete> event =
379         new CallbackComplete(aResult, aValue, mCallback.forget());
380     return mClone->threadOpenedOn->Dispatch(event, NS_DISPATCH_NORMAL);
381   }
382 
~AsyncInitializeClone()383   ~AsyncInitializeClone() override {
384     nsCOMPtr<nsIThread> thread;
385     DebugOnly<nsresult> rv = NS_GetMainThread(getter_AddRefs(thread));
386     MOZ_ASSERT(NS_SUCCEEDED(rv));
387 
388     // Handle ambiguous nsISupports inheritance.
389     NS_ProxyRelease("AsyncInitializeClone::mConnection", thread,
390                     mConnection.forget());
391     NS_ProxyRelease("AsyncInitializeClone::mClone", thread, mClone.forget());
392 
393     // Generally, the callback will be released by CallbackComplete.
394     // However, if for some reason Run() is not executed, we still
395     // need to ensure that it is released here.
396     NS_ProxyRelease("AsyncInitializeClone::mCallback", thread,
397                     mCallback.forget());
398   }
399 
400   RefPtr<Connection> mConnection;
401   RefPtr<Connection> mClone;
402   const bool mReadOnly;
403   nsCOMPtr<mozIStorageCompletionCallback> mCallback;
404 };
405 
406 /**
407  * A listener for async connection closing.
408  */
409 class CloseListener final : public mozIStorageCompletionCallback {
410  public:
411   NS_DECL_ISUPPORTS
CloseListener()412   CloseListener() : mClosed(false) {}
413 
Complete(nsresult,nsISupports *)414   NS_IMETHOD Complete(nsresult, nsISupports*) override {
415     mClosed = true;
416     return NS_OK;
417   }
418 
419   bool mClosed;
420 
421  private:
422   ~CloseListener() = default;
423 };
424 
425 NS_IMPL_ISUPPORTS(CloseListener, mozIStorageCompletionCallback)
426 
427 }  // namespace
428 
429 ////////////////////////////////////////////////////////////////////////////////
430 //// Connection
431 
Connection(Service * aService,int aFlags,ConnectionOperation aSupportedOperations,bool aIgnoreLockingMode)432 Connection::Connection(Service* aService, int aFlags,
433                        ConnectionOperation aSupportedOperations,
434                        bool aIgnoreLockingMode)
435     : sharedAsyncExecutionMutex("Connection::sharedAsyncExecutionMutex"),
436       sharedDBMutex("Connection::sharedDBMutex"),
437       threadOpenedOn(do_GetCurrentThread()),
438       mDBConn(nullptr),
439       mAsyncExecutionThreadShuttingDown(false),
440       mConnectionClosed(false),
441       mDefaultTransactionType(mozIStorageConnection::TRANSACTION_DEFERRED),
442       mDestroying(false),
443       mProgressHandler(nullptr),
444       mFlags(aFlags),
445       mIgnoreLockingMode(aIgnoreLockingMode),
446       mStorageService(aService),
447       mSupportedOperations(aSupportedOperations),
448       mTransactionNestingLevel(0) {
449   MOZ_ASSERT(!mIgnoreLockingMode || mFlags & SQLITE_OPEN_READONLY,
450              "Can't ignore locking for a non-readonly connection!");
451   mStorageService->registerConnection(this);
452 }
453 
~Connection()454 Connection::~Connection() {
455   // Failsafe Close() occurs in our custom Release method because of
456   // complications related to Close() potentially invoking AsyncClose() which
457   // will increment our refcount.
458   MOZ_ASSERT(!mAsyncExecutionThread,
459              "The async thread has not been shutdown properly!");
460 }
461 
462 NS_IMPL_ADDREF(Connection)
463 
NS_INTERFACE_MAP_BEGIN(Connection)464 NS_INTERFACE_MAP_BEGIN(Connection)
465   NS_INTERFACE_MAP_ENTRY(mozIStorageAsyncConnection)
466   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
467   NS_INTERFACE_MAP_ENTRY(mozIStorageConnection)
468   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageConnection)
469 NS_INTERFACE_MAP_END
470 
471 // This is identical to what NS_IMPL_RELEASE provides, but with the
472 // extra |1 == count| case.
473 NS_IMETHODIMP_(MozExternalRefCountType) Connection::Release(void) {
474   MOZ_ASSERT(0 != mRefCnt, "dup release");
475   nsrefcnt count = --mRefCnt;
476   NS_LOG_RELEASE(this, count, "Connection");
477   if (1 == count) {
478     // If the refcount went to 1, the single reference must be from
479     // gService->mConnections (in class |Service|).  And the code calling
480     // Release is either:
481     // - The "user" code that had created the connection, releasing on any
482     //   thread.
483     // - One of Service's getConnections() callers had acquired a strong
484     //   reference to the Connection that out-lived the last "user" reference,
485     //   and now that just got dropped.  Note that this reference could be
486     //   getting dropped on the main thread or Connection->threadOpenedOn
487     //   (because of the NewRunnableMethod used by minimizeMemory).
488     //
489     // Either way, we should now perform our failsafe Close() and unregister.
490     // However, we only want to do this once, and the reality is that our
491     // refcount could go back up above 1 and down again at any time if we are
492     // off the main thread and getConnections() gets called on the main thread,
493     // so we use an atomic here to do this exactly once.
494     if (mDestroying.compareExchange(false, true)) {
495       // Close the connection, dispatching to the opening thread if we're not
496       // on that thread already and that thread is still accepting runnables.
497       // We do this because it's possible we're on the main thread because of
498       // getConnections(), and we REALLY don't want to transfer I/O to the main
499       // thread if we can avoid it.
500       if (threadOpenedOn->IsOnCurrentThread()) {
501         // This could cause SpinningSynchronousClose() to be invoked and AddRef
502         // triggered for AsyncCloseConnection's strong ref if the conn was ever
503         // use for async purposes.  (Main-thread only, though.)
504         Unused << synchronousClose();
505       } else {
506         nsCOMPtr<nsIRunnable> event =
507             NewRunnableMethod("storage::Connection::synchronousClose", this,
508                               &Connection::synchronousClose);
509         if (NS_FAILED(
510                 threadOpenedOn->Dispatch(event.forget(), NS_DISPATCH_NORMAL))) {
511           // The target thread was dead and so we've just leaked our runnable.
512           // This should not happen because our non-main-thread consumers should
513           // be explicitly closing their connections, not relying on us to close
514           // them for them.  (It's okay to let a statement go out of scope for
515           // automatic cleanup, but not a Connection.)
516           MOZ_ASSERT(false,
517                      "Leaked Connection::synchronousClose(), ownership fail.");
518           Unused << synchronousClose();
519         }
520       }
521 
522       // This will drop its strong reference right here, right now.
523       mStorageService->unregisterConnection(this);
524     }
525   } else if (0 == count) {
526     mRefCnt = 1; /* stabilize */
527 #if 0            /* enable this to find non-threadsafe destructors: */
528     NS_ASSERT_OWNINGTHREAD(Connection);
529 #endif
530     delete (this);
531     return 0;
532   }
533   return count;
534 }
535 
getSqliteRuntimeStatus(int32_t aStatusOption,int32_t * aMaxValue)536 int32_t Connection::getSqliteRuntimeStatus(int32_t aStatusOption,
537                                            int32_t* aMaxValue) {
538   MOZ_ASSERT(connectionReady(), "A connection must exist at this point");
539   int curr = 0, max = 0;
540   DebugOnly<int> rc =
541       ::sqlite3_db_status(mDBConn, aStatusOption, &curr, &max, 0);
542   MOZ_ASSERT(NS_SUCCEEDED(convertResultCode(rc)));
543   if (aMaxValue) *aMaxValue = max;
544   return curr;
545 }
546 
getAsyncExecutionTarget()547 nsIEventTarget* Connection::getAsyncExecutionTarget() {
548   NS_ENSURE_TRUE(threadOpenedOn == NS_GetCurrentThread(), nullptr);
549 
550   // Don't return the asynchronous thread if we are shutting down.
551   if (mAsyncExecutionThreadShuttingDown) {
552     return nullptr;
553   }
554 
555   // Create the async thread if there's none yet.
556   if (!mAsyncExecutionThread) {
557     static nsThreadPoolNaming naming;
558     nsresult rv = NS_NewNamedThread(naming.GetNextThreadName("mozStorage"),
559                                     getter_AddRefs(mAsyncExecutionThread));
560     if (NS_FAILED(rv)) {
561       NS_WARNING("Failed to create async thread.");
562       return nullptr;
563     }
564     mAsyncExecutionThread->SetNameForWakeupTelemetry("mozStorage (all)"_ns);
565   }
566 
567   return mAsyncExecutionThread;
568 }
569 
RecordOpenStatus(nsresult rv)570 void Connection::RecordOpenStatus(nsresult rv) {
571   nsCString histogramKey = mTelemetryFilename;
572 
573   if (histogramKey.IsEmpty()) {
574     histogramKey.AssignLiteral("unknown");
575   }
576 
577   if (NS_SUCCEEDED(rv)) {
578     AccumulateCategoricalKeyed(histogramKey, LABELS_SQLITE_STORE_OPEN::success);
579     return;
580   }
581 
582   switch (rv) {
583     case NS_ERROR_FILE_CORRUPTED:
584       AccumulateCategoricalKeyed(histogramKey,
585                                  LABELS_SQLITE_STORE_OPEN::corrupt);
586       break;
587     case NS_ERROR_STORAGE_IOERR:
588       AccumulateCategoricalKeyed(histogramKey,
589                                  LABELS_SQLITE_STORE_OPEN::diskio);
590       break;
591     case NS_ERROR_FILE_ACCESS_DENIED:
592     case NS_ERROR_FILE_IS_LOCKED:
593     case NS_ERROR_FILE_READ_ONLY:
594       AccumulateCategoricalKeyed(histogramKey,
595                                  LABELS_SQLITE_STORE_OPEN::access);
596       break;
597     case NS_ERROR_FILE_NO_DEVICE_SPACE:
598       AccumulateCategoricalKeyed(histogramKey,
599                                  LABELS_SQLITE_STORE_OPEN::diskspace);
600       break;
601     default:
602       AccumulateCategoricalKeyed(histogramKey,
603                                  LABELS_SQLITE_STORE_OPEN::failure);
604   }
605 }
606 
RecordQueryStatus(int srv)607 void Connection::RecordQueryStatus(int srv) {
608   nsCString histogramKey = mTelemetryFilename;
609 
610   if (histogramKey.IsEmpty()) {
611     histogramKey.AssignLiteral("unknown");
612   }
613 
614   switch (srv) {
615     case SQLITE_OK:
616     case SQLITE_ROW:
617     case SQLITE_DONE:
618 
619     // Note that these are returned when we intentionally cancel a statement so
620     // they aren't indicating a failure.
621     case SQLITE_ABORT:
622     case SQLITE_INTERRUPT:
623       AccumulateCategoricalKeyed(histogramKey,
624                                  LABELS_SQLITE_STORE_QUERY::success);
625       break;
626     case SQLITE_CORRUPT:
627     case SQLITE_NOTADB:
628       AccumulateCategoricalKeyed(histogramKey,
629                                  LABELS_SQLITE_STORE_QUERY::corrupt);
630       break;
631     case SQLITE_PERM:
632     case SQLITE_CANTOPEN:
633     case SQLITE_LOCKED:
634     case SQLITE_READONLY:
635       AccumulateCategoricalKeyed(histogramKey,
636                                  LABELS_SQLITE_STORE_QUERY::access);
637       break;
638     case SQLITE_IOERR:
639     case SQLITE_NOLFS:
640       AccumulateCategoricalKeyed(histogramKey,
641                                  LABELS_SQLITE_STORE_QUERY::diskio);
642       break;
643     case SQLITE_FULL:
644     case SQLITE_TOOBIG:
645       AccumulateCategoricalKeyed(histogramKey,
646                                  LABELS_SQLITE_STORE_OPEN::diskspace);
647       break;
648     case SQLITE_CONSTRAINT:
649     case SQLITE_RANGE:
650     case SQLITE_MISMATCH:
651     case SQLITE_MISUSE:
652       AccumulateCategoricalKeyed(histogramKey,
653                                  LABELS_SQLITE_STORE_OPEN::misuse);
654       break;
655     case SQLITE_BUSY:
656       AccumulateCategoricalKeyed(histogramKey, LABELS_SQLITE_STORE_OPEN::busy);
657       break;
658     default:
659       AccumulateCategoricalKeyed(histogramKey,
660                                  LABELS_SQLITE_STORE_QUERY::failure);
661   }
662 }
663 
initialize(const nsACString & aStorageKey,const nsACString & aName)664 nsresult Connection::initialize(const nsACString& aStorageKey,
665                                 const nsACString& aName) {
666   MOZ_ASSERT(aStorageKey.Equals(kMozStorageMemoryStorageKey));
667   NS_ASSERTION(!connectionReady(),
668                "Initialize called on already opened database!");
669   MOZ_ASSERT(!mIgnoreLockingMode, "Can't ignore locking on an in-memory db.");
670   AUTO_PROFILER_LABEL("Connection::initialize", OTHER);
671 
672   mStorageKey = aStorageKey;
673   mName = aName;
674 
675   // in memory database requested, sqlite uses a magic file name
676 
677   const nsAutoCString path =
678       mName.IsEmpty() ? nsAutoCString(":memory:"_ns)
679                       : "file:"_ns + mName + "?mode=memory&cache=shared"_ns;
680 
681   mTelemetryFilename.AssignLiteral(":memory:");
682 
683   int srv = ::sqlite3_open_v2(path.get(), &mDBConn, mFlags,
684                               GetTelemetryVFSName(true));
685   if (srv != SQLITE_OK) {
686     mDBConn = nullptr;
687     nsresult rv = convertResultCode(srv);
688     RecordOpenStatus(rv);
689     return rv;
690   }
691 
692 #ifdef MOZ_SQLITE_FTS3_TOKENIZER
693   srv =
694       ::sqlite3_db_config(mDBConn, SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, 1, 0);
695   MOZ_ASSERT(srv == SQLITE_OK,
696              "SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER should be enabled");
697 #endif
698 
699   // Do not set mDatabaseFile or mFileURL here since this is a "memory"
700   // database.
701 
702   nsresult rv = initializeInternal();
703   RecordOpenStatus(rv);
704   NS_ENSURE_SUCCESS(rv, rv);
705 
706   return NS_OK;
707 }
708 
initialize(nsIFile * aDatabaseFile)709 nsresult Connection::initialize(nsIFile* aDatabaseFile) {
710   NS_ASSERTION(aDatabaseFile, "Passed null file!");
711   NS_ASSERTION(!connectionReady(),
712                "Initialize called on already opened database!");
713   AUTO_PROFILER_LABEL("Connection::initialize", OTHER);
714 
715   // Do not set mFileURL here since this is database does not have an associated
716   // URL.
717   mDatabaseFile = aDatabaseFile;
718   aDatabaseFile->GetNativeLeafName(mTelemetryFilename);
719 
720   nsAutoString path;
721   nsresult rv = aDatabaseFile->GetPath(path);
722   NS_ENSURE_SUCCESS(rv, rv);
723 
724 #ifdef XP_WIN
725   static const char* sIgnoreLockingVFS = "win32-none";
726 #else
727   static const char* sIgnoreLockingVFS = "unix-none";
728 #endif
729 
730   bool exclusive = StaticPrefs::storage_sqlite_exclusiveLock_enabled();
731   int srv;
732   if (mIgnoreLockingMode) {
733     exclusive = false;
734     srv = ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path).get(), &mDBConn, mFlags,
735                             sIgnoreLockingVFS);
736   } else {
737     srv = ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path).get(), &mDBConn, mFlags,
738                             GetTelemetryVFSName(exclusive));
739     if (exclusive && (srv == SQLITE_LOCKED || srv == SQLITE_BUSY)) {
740       // Retry without trying to get an exclusive lock.
741       exclusive = false;
742       srv = ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path).get(), &mDBConn,
743                               mFlags, GetTelemetryVFSName(false));
744     }
745   }
746   if (srv != SQLITE_OK) {
747     mDBConn = nullptr;
748     rv = convertResultCode(srv);
749     RecordOpenStatus(rv);
750     return rv;
751   }
752 
753   rv = initializeInternal();
754   if (exclusive &&
755       (rv == NS_ERROR_STORAGE_BUSY || rv == NS_ERROR_FILE_IS_LOCKED)) {
756     // Usually SQLite will fail to acquire an exclusive lock on opening, but in
757     // some cases it may successfully open the database and then lock on the
758     // first query execution. When initializeInternal fails it closes the
759     // connection, so we can try to restart it in non-exclusive mode.
760     srv = ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path).get(), &mDBConn, mFlags,
761                             GetTelemetryVFSName(false));
762     if (srv == SQLITE_OK) {
763       rv = initializeInternal();
764     }
765   }
766 
767   RecordOpenStatus(rv);
768   NS_ENSURE_SUCCESS(rv, rv);
769 
770   return NS_OK;
771 }
772 
initialize(nsIFileURL * aFileURL,const nsACString & aTelemetryFilename)773 nsresult Connection::initialize(nsIFileURL* aFileURL,
774                                 const nsACString& aTelemetryFilename) {
775   NS_ASSERTION(aFileURL, "Passed null file URL!");
776   NS_ASSERTION(!connectionReady(),
777                "Initialize called on already opened database!");
778   AUTO_PROFILER_LABEL("Connection::initialize", OTHER);
779 
780   nsCOMPtr<nsIFile> databaseFile;
781   nsresult rv = aFileURL->GetFile(getter_AddRefs(databaseFile));
782   NS_ENSURE_SUCCESS(rv, rv);
783 
784   // Set both mDatabaseFile and mFileURL here.
785   mFileURL = aFileURL;
786   mDatabaseFile = databaseFile;
787 
788   if (!aTelemetryFilename.IsEmpty()) {
789     mTelemetryFilename = aTelemetryFilename;
790   } else {
791     databaseFile->GetNativeLeafName(mTelemetryFilename);
792   }
793 
794   nsAutoCString spec;
795   rv = aFileURL->GetSpec(spec);
796   NS_ENSURE_SUCCESS(rv, rv);
797 
798   bool exclusive = StaticPrefs::storage_sqlite_exclusiveLock_enabled();
799 
800   // If there is a key specified, we need to use the obfuscating VFS.
801   nsAutoCString query;
802   rv = aFileURL->GetQuery(query);
803   NS_ENSURE_SUCCESS(rv, rv);
804   const char* const vfs =
805       URLParams::Parse(query,
806                        [](const nsAString& aName, const nsAString& aValue) {
807                          return aName.EqualsLiteral("key");
808                        })
809           ? GetObfuscatingVFSName()
810           : GetTelemetryVFSName(exclusive);
811   int srv = ::sqlite3_open_v2(spec.get(), &mDBConn, mFlags, vfs);
812   if (srv != SQLITE_OK) {
813     mDBConn = nullptr;
814     rv = convertResultCode(srv);
815     RecordOpenStatus(rv);
816     return rv;
817   }
818 
819   rv = initializeInternal();
820   RecordOpenStatus(rv);
821   NS_ENSURE_SUCCESS(rv, rv);
822 
823   return NS_OK;
824 }
825 
initializeInternal()826 nsresult Connection::initializeInternal() {
827   MOZ_ASSERT(mDBConn);
828   auto guard = MakeScopeExit([&]() { initializeFailed(); });
829 
830   mConnectionClosed = false;
831 
832 #ifdef MOZ_SQLITE_FTS3_TOKENIZER
833   DebugOnly<int> srv2 =
834       ::sqlite3_db_config(mDBConn, SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, 1, 0);
835   MOZ_ASSERT(srv2 == SQLITE_OK,
836              "SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER should be enabled");
837 #endif
838 
839   MOZ_ASSERT(!mTelemetryFilename.IsEmpty(),
840              "A telemetry filename should have been set by now.");
841 
842   // Properly wrap the database handle's mutex.
843   sharedDBMutex.initWithMutex(sqlite3_db_mutex(mDBConn));
844 
845   // SQLite tracing can slow down queries (especially long queries)
846   // significantly. Don't trace unless the user is actively monitoring SQLite.
847   if (MOZ_LOG_TEST(gStorageLog, LogLevel::Debug)) {
848     ::sqlite3_trace_v2(mDBConn, SQLITE_TRACE_STMT | SQLITE_TRACE_PROFILE,
849                        tracefunc, this);
850 
851     MOZ_LOG(
852         gStorageLog, LogLevel::Debug,
853         ("Opening connection to '%s' (%p)", mTelemetryFilename.get(), this));
854   }
855 
856   int64_t pageSize = Service::kDefaultPageSize;
857 
858   // Set page_size to the preferred default value.  This is effective only if
859   // the database has just been created, otherwise, if the database does not
860   // use WAL journal mode, a VACUUM operation will updated its page_size.
861   nsAutoCString pageSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
862                               "PRAGMA page_size = ");
863   pageSizeQuery.AppendInt(pageSize);
864   int srv = executeSql(mDBConn, pageSizeQuery.get());
865   if (srv != SQLITE_OK) {
866     return convertResultCode(srv);
867   }
868 
869   // Setting the cache_size forces the database open, verifying if it is valid
870   // or corrupt.  So this is executed regardless it being actually needed.
871   // The cache_size is calculated from the actual page_size, to save memory.
872   nsAutoCString cacheSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
873                                "PRAGMA cache_size = ");
874   cacheSizeQuery.AppendInt(-MAX_CACHE_SIZE_KIBIBYTES);
875   srv = executeSql(mDBConn, cacheSizeQuery.get());
876   if (srv != SQLITE_OK) {
877     return convertResultCode(srv);
878   }
879 
880   // Register our built-in SQL functions.
881   srv = registerFunctions(mDBConn);
882   if (srv != SQLITE_OK) {
883     return convertResultCode(srv);
884   }
885 
886   // Register our built-in SQL collating sequences.
887   srv = registerCollations(mDBConn, mStorageService);
888   if (srv != SQLITE_OK) {
889     return convertResultCode(srv);
890   }
891 
892   // Set the default synchronous value. Each consumer can switch this
893   // accordingly to their needs.
894 #if defined(ANDROID)
895   // Android prefers synchronous = OFF for performance reasons.
896   Unused << ExecuteSimpleSQL("PRAGMA synchronous = OFF;"_ns);
897 #else
898   // Normal is the suggested value for WAL journals.
899   Unused << ExecuteSimpleSQL("PRAGMA synchronous = NORMAL;"_ns);
900 #endif
901 
902   // Initialization succeeded, we can stop guarding for failures.
903   guard.release();
904   return NS_OK;
905 }
906 
initializeOnAsyncThread(nsIFile * aStorageFile)907 nsresult Connection::initializeOnAsyncThread(nsIFile* aStorageFile) {
908   MOZ_ASSERT(threadOpenedOn != NS_GetCurrentThread());
909   nsresult rv = aStorageFile
910                     ? initialize(aStorageFile)
911                     : initialize(kMozStorageMemoryStorageKey, VoidCString());
912   if (NS_FAILED(rv)) {
913     // Shutdown the async thread, since initialization failed.
914     MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
915     mAsyncExecutionThreadShuttingDown = true;
916     nsCOMPtr<nsIRunnable> event =
917         NewRunnableMethod("Connection::shutdownAsyncThread", this,
918                           &Connection::shutdownAsyncThread);
919     Unused << NS_DispatchToMainThread(event);
920   }
921   return rv;
922 }
923 
initializeFailed()924 void Connection::initializeFailed() {
925   {
926     MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
927     mConnectionClosed = true;
928   }
929   MOZ_ALWAYS_TRUE(::sqlite3_close(mDBConn) == SQLITE_OK);
930   mDBConn = nullptr;
931   sharedDBMutex.destroy();
932 }
933 
databaseElementExists(enum DatabaseElementType aElementType,const nsACString & aElementName,bool * _exists)934 nsresult Connection::databaseElementExists(
935     enum DatabaseElementType aElementType, const nsACString& aElementName,
936     bool* _exists) {
937   if (!connectionReady()) {
938     return NS_ERROR_NOT_AVAILABLE;
939   }
940   nsresult rv = ensureOperationSupported(SYNCHRONOUS);
941   if (NS_FAILED(rv)) {
942     return rv;
943   }
944 
945   // When constructing the query, make sure to SELECT the correct db's
946   // sqlite_master if the user is prefixing the element with a specific db. ex:
947   // sample.test
948   nsCString query("SELECT name FROM (SELECT * FROM ");
949   nsDependentCSubstring element;
950   int32_t ind = aElementName.FindChar('.');
951   if (ind == kNotFound) {
952     element.Assign(aElementName);
953   } else {
954     nsDependentCSubstring db(Substring(aElementName, 0, ind + 1));
955     element.Assign(Substring(aElementName, ind + 1, aElementName.Length()));
956     query.Append(db);
957   }
958   query.AppendLiteral(
959       "sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type = "
960       "'");
961 
962   switch (aElementType) {
963     case INDEX:
964       query.AppendLiteral("index");
965       break;
966     case TABLE:
967       query.AppendLiteral("table");
968       break;
969   }
970   query.AppendLiteral("' AND name ='");
971   query.Append(element);
972   query.Append('\'');
973 
974   sqlite3_stmt* stmt;
975   int srv = prepareStatement(mDBConn, query, &stmt);
976   if (srv != SQLITE_OK) {
977     RecordQueryStatus(srv);
978     return convertResultCode(srv);
979   }
980 
981   srv = stepStatement(mDBConn, stmt);
982   // we just care about the return value from step
983   (void)::sqlite3_finalize(stmt);
984 
985   RecordQueryStatus(srv);
986 
987   if (srv == SQLITE_ROW) {
988     *_exists = true;
989     return NS_OK;
990   }
991   if (srv == SQLITE_DONE) {
992     *_exists = false;
993     return NS_OK;
994   }
995 
996   return convertResultCode(srv);
997 }
998 
findFunctionByInstance(mozIStorageFunction * aInstance)999 bool Connection::findFunctionByInstance(mozIStorageFunction* aInstance) {
1000   sharedDBMutex.assertCurrentThreadOwns();
1001 
1002   for (const auto& data : mFunctions.Values()) {
1003     if (data.function == aInstance) {
1004       return true;
1005     }
1006   }
1007   return false;
1008 }
1009 
1010 /* static */
sProgressHelper(void * aArg)1011 int Connection::sProgressHelper(void* aArg) {
1012   Connection* _this = static_cast<Connection*>(aArg);
1013   return _this->progressHandler();
1014 }
1015 
progressHandler()1016 int Connection::progressHandler() {
1017   sharedDBMutex.assertCurrentThreadOwns();
1018   if (mProgressHandler) {
1019     bool result;
1020     nsresult rv = mProgressHandler->OnProgress(this, &result);
1021     if (NS_FAILED(rv)) return 0;  // Don't break request
1022     return result ? 1 : 0;
1023   }
1024   return 0;
1025 }
1026 
setClosedState()1027 nsresult Connection::setClosedState() {
1028   // Ensure that we are on the correct thread to close the database.
1029   bool onOpenedThread;
1030   nsresult rv = threadOpenedOn->IsOnCurrentThread(&onOpenedThread);
1031   NS_ENSURE_SUCCESS(rv, rv);
1032   if (!onOpenedThread) {
1033     NS_ERROR("Must close the database on the thread that you opened it with!");
1034     return NS_ERROR_UNEXPECTED;
1035   }
1036 
1037   // Flag that we are shutting down the async thread, so that
1038   // getAsyncExecutionTarget knows not to expose/create the async thread.
1039   {
1040     MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
1041     NS_ENSURE_FALSE(mAsyncExecutionThreadShuttingDown, NS_ERROR_UNEXPECTED);
1042     mAsyncExecutionThreadShuttingDown = true;
1043 
1044     // Set the property to null before closing the connection, otherwise the
1045     // other functions in the module may try to use the connection after it is
1046     // closed.
1047     mDBConn = nullptr;
1048   }
1049   return NS_OK;
1050 }
1051 
operationSupported(ConnectionOperation aOperationType)1052 bool Connection::operationSupported(ConnectionOperation aOperationType) {
1053   if (aOperationType == ASYNCHRONOUS) {
1054     // Async operations are supported for all connections, on any thread.
1055     return true;
1056   }
1057   // Sync operations are supported for sync connections (on any thread), and
1058   // async connections on a background thread.
1059   MOZ_ASSERT(aOperationType == SYNCHRONOUS);
1060   return mSupportedOperations == SYNCHRONOUS || !NS_IsMainThread();
1061 }
1062 
ensureOperationSupported(ConnectionOperation aOperationType)1063 nsresult Connection::ensureOperationSupported(
1064     ConnectionOperation aOperationType) {
1065   if (NS_WARN_IF(!operationSupported(aOperationType))) {
1066 #ifdef DEBUG
1067     if (NS_IsMainThread()) {
1068       nsCOMPtr<nsIXPConnect> xpc = nsIXPConnect::XPConnect();
1069       Unused << xpc->DebugDumpJSStack(false, false, false);
1070     }
1071 #endif
1072     MOZ_ASSERT(false,
1073                "Don't use async connections synchronously on the main thread");
1074     return NS_ERROR_NOT_AVAILABLE;
1075   }
1076   return NS_OK;
1077 }
1078 
isConnectionReadyOnThisThread()1079 bool Connection::isConnectionReadyOnThisThread() {
1080   MOZ_ASSERT_IF(connectionReady(), !mConnectionClosed);
1081   if (mAsyncExecutionThread && mAsyncExecutionThread->IsOnCurrentThread()) {
1082     return true;
1083   }
1084   return connectionReady();
1085 }
1086 
isClosing()1087 bool Connection::isClosing() {
1088   MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
1089   return mAsyncExecutionThreadShuttingDown && !mConnectionClosed;
1090 }
1091 
isClosed()1092 bool Connection::isClosed() {
1093   MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
1094   return mConnectionClosed;
1095 }
1096 
isClosed(MutexAutoLock & lock)1097 bool Connection::isClosed(MutexAutoLock& lock) { return mConnectionClosed; }
1098 
isAsyncExecutionThreadAvailable()1099 bool Connection::isAsyncExecutionThreadAvailable() {
1100   MOZ_ASSERT(threadOpenedOn == NS_GetCurrentThread());
1101   return mAsyncExecutionThread && !mAsyncExecutionThreadShuttingDown;
1102 }
1103 
shutdownAsyncThread()1104 void Connection::shutdownAsyncThread() {
1105   MOZ_ASSERT(threadOpenedOn == NS_GetCurrentThread());
1106   MOZ_ASSERT(mAsyncExecutionThread);
1107   MOZ_ASSERT(mAsyncExecutionThreadShuttingDown);
1108 
1109   MOZ_ALWAYS_SUCCEEDS(mAsyncExecutionThread->Shutdown());
1110   mAsyncExecutionThread = nullptr;
1111 }
1112 
internalClose(sqlite3 * aNativeConnection)1113 nsresult Connection::internalClose(sqlite3* aNativeConnection) {
1114 #ifdef DEBUG
1115   {  // Make sure we have marked our async thread as shutting down.
1116     MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
1117     MOZ_ASSERT(mAsyncExecutionThreadShuttingDown,
1118                "Did not call setClosedState!");
1119     MOZ_ASSERT(!isClosed(lockedScope), "Unexpected closed state");
1120   }
1121 #endif  // DEBUG
1122 
1123   if (MOZ_LOG_TEST(gStorageLog, LogLevel::Debug)) {
1124     nsAutoCString leafName(":memory");
1125     if (mDatabaseFile) (void)mDatabaseFile->GetNativeLeafName(leafName);
1126     MOZ_LOG(gStorageLog, LogLevel::Debug,
1127             ("Closing connection to '%s'", leafName.get()));
1128   }
1129 
1130   // At this stage, we may still have statements that need to be
1131   // finalized. Attempt to close the database connection. This will
1132   // always disconnect any virtual tables and cleanly finalize their
1133   // internal statements. Once this is done, closing may fail due to
1134   // unfinalized client statements, in which case we need to finalize
1135   // these statements and close again.
1136   {
1137     MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
1138     mConnectionClosed = true;
1139   }
1140 
1141   // Nothing else needs to be done if we don't have a connection here.
1142   if (!aNativeConnection) return NS_OK;
1143 
1144   int srv = ::sqlite3_close(aNativeConnection);
1145 
1146   if (srv == SQLITE_BUSY) {
1147     {
1148       // Nothing else should change the connection or statements status until we
1149       // are done here.
1150       SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1151       // We still have non-finalized statements. Finalize them.
1152       sqlite3_stmt* stmt = nullptr;
1153       while ((stmt = ::sqlite3_next_stmt(aNativeConnection, stmt))) {
1154         MOZ_LOG(gStorageLog, LogLevel::Debug,
1155                 ("Auto-finalizing SQL statement '%s' (%p)", ::sqlite3_sql(stmt),
1156                  stmt));
1157 
1158 #ifdef DEBUG
1159         SmprintfPointer msg = ::mozilla::Smprintf(
1160             "SQL statement '%s' (%p) should have been finalized before closing "
1161             "the connection",
1162             ::sqlite3_sql(stmt), stmt);
1163         NS_WARNING(msg.get());
1164 #endif  // DEBUG
1165 
1166         srv = ::sqlite3_finalize(stmt);
1167 
1168 #ifdef DEBUG
1169         if (srv != SQLITE_OK) {
1170           SmprintfPointer msg = ::mozilla::Smprintf(
1171               "Could not finalize SQL statement (%p)", stmt);
1172           NS_WARNING(msg.get());
1173         }
1174 #endif  // DEBUG
1175 
1176         // Ensure that the loop continues properly, whether closing has
1177         // succeeded or not.
1178         if (srv == SQLITE_OK) {
1179           stmt = nullptr;
1180         }
1181       }
1182       // Scope exiting will unlock the mutex before we invoke sqlite3_close()
1183       // again, since Sqlite will try to acquire it.
1184     }
1185 
1186     // Now that all statements have been finalized, we
1187     // should be able to close.
1188     srv = ::sqlite3_close(aNativeConnection);
1189     MOZ_ASSERT(false,
1190                "Had to forcibly close the database connection because not all "
1191                "the statements have been finalized.");
1192   }
1193 
1194   if (srv == SQLITE_OK) {
1195     sharedDBMutex.destroy();
1196   } else {
1197     MOZ_ASSERT(false,
1198                "sqlite3_close failed. There are probably outstanding "
1199                "statements that are listed above!");
1200   }
1201 
1202   return convertResultCode(srv);
1203 }
1204 
getFilename()1205 nsCString Connection::getFilename() { return mTelemetryFilename; }
1206 
stepStatement(sqlite3 * aNativeConnection,sqlite3_stmt * aStatement)1207 int Connection::stepStatement(sqlite3* aNativeConnection,
1208                               sqlite3_stmt* aStatement) {
1209   MOZ_ASSERT(aStatement);
1210 
1211   AUTO_PROFILER_LABEL_DYNAMIC_CSTR("Connection::stepStatement", OTHER,
1212                                    ::sqlite3_sql(aStatement));
1213 
1214   bool checkedMainThread = false;
1215   TimeStamp startTime = TimeStamp::Now();
1216 
1217   // The connection may have been closed if the executing statement has been
1218   // created and cached after a call to asyncClose() but before the actual
1219   // sqlite3_close().  This usually happens when other tasks using cached
1220   // statements are asynchronously scheduled for execution and any of them ends
1221   // up after asyncClose. See bug 728653 for details.
1222   if (!isConnectionReadyOnThisThread()) return SQLITE_MISUSE;
1223 
1224   (void)::sqlite3_extended_result_codes(aNativeConnection, 1);
1225 
1226   int srv;
1227   while ((srv = ::sqlite3_step(aStatement)) == SQLITE_LOCKED_SHAREDCACHE) {
1228     if (!checkedMainThread) {
1229       checkedMainThread = true;
1230       if (::NS_IsMainThread()) {
1231         NS_WARNING("We won't allow blocking on the main thread!");
1232         break;
1233       }
1234     }
1235 
1236     srv = WaitForUnlockNotify(aNativeConnection);
1237     if (srv != SQLITE_OK) {
1238       break;
1239     }
1240 
1241     ::sqlite3_reset(aStatement);
1242   }
1243 
1244   // Report very slow SQL statements to Telemetry
1245   TimeDuration duration = TimeStamp::Now() - startTime;
1246   const uint32_t threshold = NS_IsMainThread()
1247                                  ? Telemetry::kSlowSQLThresholdForMainThread
1248                                  : Telemetry::kSlowSQLThresholdForHelperThreads;
1249   if (duration.ToMilliseconds() >= threshold) {
1250     nsDependentCString statementString(::sqlite3_sql(aStatement));
1251     Telemetry::RecordSlowSQLStatement(statementString, mTelemetryFilename,
1252                                       duration.ToMilliseconds());
1253   }
1254 
1255   (void)::sqlite3_extended_result_codes(aNativeConnection, 0);
1256   // Drop off the extended result bits of the result code.
1257   return srv & 0xFF;
1258 }
1259 
prepareStatement(sqlite3 * aNativeConnection,const nsCString & aSQL,sqlite3_stmt ** _stmt)1260 int Connection::prepareStatement(sqlite3* aNativeConnection,
1261                                  const nsCString& aSQL, sqlite3_stmt** _stmt) {
1262   // We should not even try to prepare statements after the connection has
1263   // been closed.
1264   if (!isConnectionReadyOnThisThread()) return SQLITE_MISUSE;
1265 
1266   bool checkedMainThread = false;
1267 
1268   (void)::sqlite3_extended_result_codes(aNativeConnection, 1);
1269 
1270   int srv;
1271   while ((srv = ::sqlite3_prepare_v2(aNativeConnection, aSQL.get(), -1, _stmt,
1272                                      nullptr)) == SQLITE_LOCKED_SHAREDCACHE) {
1273     if (!checkedMainThread) {
1274       checkedMainThread = true;
1275       if (::NS_IsMainThread()) {
1276         NS_WARNING("We won't allow blocking on the main thread!");
1277         break;
1278       }
1279     }
1280 
1281     srv = WaitForUnlockNotify(aNativeConnection);
1282     if (srv != SQLITE_OK) {
1283       break;
1284     }
1285   }
1286 
1287   if (srv != SQLITE_OK) {
1288     nsCString warnMsg;
1289     warnMsg.AppendLiteral("The SQL statement '");
1290     warnMsg.Append(aSQL);
1291     warnMsg.AppendLiteral("' could not be compiled due to an error: ");
1292     warnMsg.Append(::sqlite3_errmsg(aNativeConnection));
1293 
1294 #ifdef DEBUG
1295     NS_WARNING(warnMsg.get());
1296 #endif
1297     MOZ_LOG(gStorageLog, LogLevel::Error, ("%s", warnMsg.get()));
1298   }
1299 
1300   (void)::sqlite3_extended_result_codes(aNativeConnection, 0);
1301   // Drop off the extended result bits of the result code.
1302   int rc = srv & 0xFF;
1303   // sqlite will return OK on a comment only string and set _stmt to nullptr.
1304   // The callers of this function are used to only checking the return value,
1305   // so it is safer to return an error code.
1306   if (rc == SQLITE_OK && *_stmt == nullptr) {
1307     return SQLITE_MISUSE;
1308   }
1309 
1310   return rc;
1311 }
1312 
executeSql(sqlite3 * aNativeConnection,const char * aSqlString)1313 int Connection::executeSql(sqlite3* aNativeConnection, const char* aSqlString) {
1314   if (!isConnectionReadyOnThisThread()) return SQLITE_MISUSE;
1315 
1316   AUTO_PROFILER_LABEL_DYNAMIC_CSTR("Connection::executeSql", OTHER, aSqlString);
1317 
1318   TimeStamp startTime = TimeStamp::Now();
1319   int srv =
1320       ::sqlite3_exec(aNativeConnection, aSqlString, nullptr, nullptr, nullptr);
1321   RecordQueryStatus(srv);
1322 
1323   // Report very slow SQL statements to Telemetry
1324   TimeDuration duration = TimeStamp::Now() - startTime;
1325   const uint32_t threshold = NS_IsMainThread()
1326                                  ? Telemetry::kSlowSQLThresholdForMainThread
1327                                  : Telemetry::kSlowSQLThresholdForHelperThreads;
1328   if (duration.ToMilliseconds() >= threshold) {
1329     nsDependentCString statementString(aSqlString);
1330     Telemetry::RecordSlowSQLStatement(statementString, mTelemetryFilename,
1331                                       duration.ToMilliseconds());
1332   }
1333 
1334   return srv;
1335 }
1336 
1337 ////////////////////////////////////////////////////////////////////////////////
1338 //// nsIInterfaceRequestor
1339 
1340 NS_IMETHODIMP
GetInterface(const nsIID & aIID,void ** _result)1341 Connection::GetInterface(const nsIID& aIID, void** _result) {
1342   if (aIID.Equals(NS_GET_IID(nsIEventTarget))) {
1343     nsIEventTarget* background = getAsyncExecutionTarget();
1344     NS_IF_ADDREF(background);
1345     *_result = background;
1346     return NS_OK;
1347   }
1348   return NS_ERROR_NO_INTERFACE;
1349 }
1350 
1351 ////////////////////////////////////////////////////////////////////////////////
1352 //// mozIStorageConnection
1353 
1354 NS_IMETHODIMP
Close()1355 Connection::Close() {
1356   nsresult rv = ensureOperationSupported(SYNCHRONOUS);
1357   if (NS_FAILED(rv)) {
1358     return rv;
1359   }
1360   return synchronousClose();
1361 }
1362 
synchronousClose()1363 nsresult Connection::synchronousClose() {
1364   if (!connectionReady()) {
1365     return NS_ERROR_NOT_INITIALIZED;
1366   }
1367 
1368 #ifdef DEBUG
1369   // Since we're accessing mAsyncExecutionThread, we need to be on the opener
1370   // thread. We make this check outside of debug code below in setClosedState,
1371   // but this is here to be explicit.
1372   bool onOpenerThread = false;
1373   (void)threadOpenedOn->IsOnCurrentThread(&onOpenerThread);
1374   MOZ_ASSERT(onOpenerThread);
1375 #endif  // DEBUG
1376 
1377   // Make sure we have not executed any asynchronous statements.
1378   // If this fails, the mDBConn may be left open, resulting in a leak.
1379   // We'll try to finalize the pending statements and close the connection.
1380   if (isAsyncExecutionThreadAvailable()) {
1381 #ifdef DEBUG
1382     if (NS_IsMainThread()) {
1383       nsCOMPtr<nsIXPConnect> xpc = nsIXPConnect::XPConnect();
1384       Unused << xpc->DebugDumpJSStack(false, false, false);
1385     }
1386 #endif
1387     MOZ_ASSERT(false,
1388                "Close() was invoked on a connection that executed asynchronous "
1389                "statements. "
1390                "Should have used asyncClose().");
1391     // Try to close the database regardless, to free up resources.
1392     Unused << SpinningSynchronousClose();
1393     return NS_ERROR_UNEXPECTED;
1394   }
1395 
1396   // setClosedState nullifies our connection pointer, so we take a raw pointer
1397   // off it, to pass it through the close procedure.
1398   sqlite3* nativeConn = mDBConn;
1399   nsresult rv = setClosedState();
1400   NS_ENSURE_SUCCESS(rv, rv);
1401 
1402   return internalClose(nativeConn);
1403 }
1404 
1405 NS_IMETHODIMP
SpinningSynchronousClose()1406 Connection::SpinningSynchronousClose() {
1407   nsresult rv = ensureOperationSupported(SYNCHRONOUS);
1408   if (NS_FAILED(rv)) {
1409     return rv;
1410   }
1411   if (threadOpenedOn != NS_GetCurrentThread()) {
1412     return NS_ERROR_NOT_SAME_THREAD;
1413   }
1414 
1415   // As currently implemented, we can't spin to wait for an existing AsyncClose.
1416   // Our only existing caller will never have called close; assert if misused
1417   // so that no new callers assume this works after an AsyncClose.
1418   MOZ_DIAGNOSTIC_ASSERT(connectionReady());
1419   if (!connectionReady()) {
1420     return NS_ERROR_UNEXPECTED;
1421   }
1422 
1423   RefPtr<CloseListener> listener = new CloseListener();
1424   rv = AsyncClose(listener);
1425   NS_ENSURE_SUCCESS(rv, rv);
1426   MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return listener->mClosed; }));
1427   MOZ_ASSERT(isClosed(), "The connection should be closed at this point");
1428 
1429   return rv;
1430 }
1431 
1432 NS_IMETHODIMP
AsyncClose(mozIStorageCompletionCallback * aCallback)1433 Connection::AsyncClose(mozIStorageCompletionCallback* aCallback) {
1434   NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_SAME_THREAD);
1435   // Check if AsyncClose or Close were already invoked.
1436   if (!connectionReady()) {
1437     return NS_ERROR_NOT_INITIALIZED;
1438   }
1439   nsresult rv = ensureOperationSupported(ASYNCHRONOUS);
1440   if (NS_FAILED(rv)) {
1441     return rv;
1442   }
1443 
1444   // The two relevant factors at this point are whether we have a database
1445   // connection and whether we have an async execution thread.  Here's what the
1446   // states mean and how we handle them:
1447   //
1448   // - (mDBConn && asyncThread): The expected case where we are either an
1449   //   async connection or a sync connection that has been used asynchronously.
1450   //   Either way the caller must call us and not Close().  Nothing surprising
1451   //   about this.  We'll dispatch AsyncCloseConnection to the already-existing
1452   //   async thread.
1453   //
1454   // - (mDBConn && !asyncThread): A somewhat unusual case where the caller
1455   //   opened the connection synchronously and was planning to use it
1456   //   asynchronously, but never got around to using it asynchronously before
1457   //   needing to shutdown.  This has been observed to happen for the cookie
1458   //   service in a case where Firefox shuts itself down almost immediately
1459   //   after startup (for unknown reasons).  In the Firefox shutdown case,
1460   //   we may also fail to create a new async execution thread if one does not
1461   //   already exist.  (nsThreadManager will refuse to create new threads when
1462   //   it has already been told to shutdown.)  As such, we need to handle a
1463   //   failure to create the async execution thread by falling back to
1464   //   synchronous Close() and also dispatching the completion callback because
1465   //   at least Places likes to spin a nested event loop that depends on the
1466   //   callback being invoked.
1467   //
1468   //   Note that we have considered not trying to spin up the async execution
1469   //   thread in this case if it does not already exist, but the overhead of
1470   //   thread startup (if successful) is significantly less expensive than the
1471   //   worst-case potential I/O hit of synchronously closing a database when we
1472   //   could close it asynchronously.
1473   //
1474   // - (!mDBConn && asyncThread): This happens in some but not all cases where
1475   //   OpenAsyncDatabase encountered a problem opening the database.  If it
1476   //   happened in all cases AsyncInitDatabase would just shut down the thread
1477   //   directly and we would avoid this case.  But it doesn't, so for simplicity
1478   //   and consistency AsyncCloseConnection knows how to handle this and we
1479   //   act like this was the (mDBConn && asyncThread) case in this method.
1480   //
1481   // - (!mDBConn && !asyncThread): The database was never successfully opened or
1482   //   Close() or AsyncClose() has already been called (at least) once.  This is
1483   //   undeniably a misuse case by the caller.  We could optimize for this
1484   //   case by adding an additional check of mAsyncExecutionThread without using
1485   //   getAsyncExecutionTarget() to avoid wastefully creating a thread just to
1486   //   shut it down.  But this complicates the method for broken caller code
1487   //   whereas we're still correct and safe without the special-case.
1488   nsIEventTarget* asyncThread = getAsyncExecutionTarget();
1489 
1490   // Create our callback event if we were given a callback.  This will
1491   // eventually be dispatched in all cases, even if we fall back to Close() and
1492   // the database wasn't open and we return an error.  The rationale is that
1493   // no existing consumer checks our return value and several of them like to
1494   // spin nested event loops until the callback fires.  Given that, it seems
1495   // preferable for us to dispatch the callback in all cases.  (Except the
1496   // wrong thread misuse case we bailed on up above.  But that's okay because
1497   // that is statically wrong whereas these edge cases are dynamic.)
1498   nsCOMPtr<nsIRunnable> completeEvent;
1499   if (aCallback) {
1500     completeEvent = newCompletionEvent(aCallback);
1501   }
1502 
1503   if (!asyncThread) {
1504     // We were unable to create an async thread, so we need to fall back to
1505     // using normal Close().  Since there is no async thread, Close() will
1506     // not complain about that.  (Close() may, however, complain if the
1507     // connection is closed, but that's okay.)
1508     if (completeEvent) {
1509       // Closing the database is more important than returning an error code
1510       // about a failure to dispatch, especially because all existing native
1511       // callers ignore our return value.
1512       Unused << NS_DispatchToMainThread(completeEvent.forget());
1513     }
1514     MOZ_ALWAYS_SUCCEEDS(synchronousClose());
1515     // Return a success inconditionally here, since Close() is unlikely to fail
1516     // and we want to reassure the consumer that its callback will be invoked.
1517     return NS_OK;
1518   }
1519 
1520   // setClosedState nullifies our connection pointer, so we take a raw pointer
1521   // off it, to pass it through the close procedure.
1522   sqlite3* nativeConn = mDBConn;
1523   rv = setClosedState();
1524   NS_ENSURE_SUCCESS(rv, rv);
1525 
1526   // Create and dispatch our close event to the background thread.
1527   nsCOMPtr<nsIRunnable> closeEvent =
1528       new AsyncCloseConnection(this, nativeConn, completeEvent);
1529   rv = asyncThread->Dispatch(closeEvent, NS_DISPATCH_NORMAL);
1530   NS_ENSURE_SUCCESS(rv, rv);
1531 
1532   return NS_OK;
1533 }
1534 
1535 NS_IMETHODIMP
AsyncClone(bool aReadOnly,mozIStorageCompletionCallback * aCallback)1536 Connection::AsyncClone(bool aReadOnly,
1537                        mozIStorageCompletionCallback* aCallback) {
1538   AUTO_PROFILER_LABEL("Connection::AsyncClone", OTHER);
1539 
1540   NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_SAME_THREAD);
1541   if (!connectionReady()) {
1542     return NS_ERROR_NOT_INITIALIZED;
1543   }
1544   nsresult rv = ensureOperationSupported(ASYNCHRONOUS);
1545   if (NS_FAILED(rv)) {
1546     return rv;
1547   }
1548   if (!mDatabaseFile) return NS_ERROR_UNEXPECTED;
1549 
1550   int flags = mFlags;
1551   if (aReadOnly) {
1552     // Turn off SQLITE_OPEN_READWRITE, and set SQLITE_OPEN_READONLY.
1553     flags = (~SQLITE_OPEN_READWRITE & flags) | SQLITE_OPEN_READONLY;
1554     // Turn off SQLITE_OPEN_CREATE.
1555     flags = (~SQLITE_OPEN_CREATE & flags);
1556   }
1557 
1558   // The cloned connection will still implement the synchronous API, but throw
1559   // if any synchronous methods are called on the main thread.
1560   RefPtr<Connection> clone =
1561       new Connection(mStorageService, flags, ASYNCHRONOUS);
1562 
1563   RefPtr<AsyncInitializeClone> initEvent =
1564       new AsyncInitializeClone(this, clone, aReadOnly, aCallback);
1565   // Dispatch to our async thread, since the originating connection must remain
1566   // valid and open for the whole cloning process.  This also ensures we are
1567   // properly serialized with a `close` operation, rather than race with it.
1568   nsCOMPtr<nsIEventTarget> target = getAsyncExecutionTarget();
1569   if (!target) {
1570     return NS_ERROR_UNEXPECTED;
1571   }
1572   return target->Dispatch(initEvent, NS_DISPATCH_NORMAL);
1573 }
1574 
initializeClone(Connection * aClone,bool aReadOnly)1575 nsresult Connection::initializeClone(Connection* aClone, bool aReadOnly) {
1576   nsresult rv;
1577   if (!mStorageKey.IsEmpty()) {
1578     rv = aClone->initialize(mStorageKey, mName);
1579   } else if (mFileURL) {
1580     rv = aClone->initialize(mFileURL, mTelemetryFilename);
1581   } else {
1582     rv = aClone->initialize(mDatabaseFile);
1583   }
1584   if (NS_FAILED(rv)) {
1585     return rv;
1586   }
1587 
1588   auto guard = MakeScopeExit([&]() { aClone->initializeFailed(); });
1589 
1590   rv = aClone->SetDefaultTransactionType(mDefaultTransactionType);
1591   NS_ENSURE_SUCCESS(rv, rv);
1592 
1593   // Re-attach on-disk databases that were attached to the original connection.
1594   {
1595     nsCOMPtr<mozIStorageStatement> stmt;
1596     rv = CreateStatement("PRAGMA database_list"_ns, getter_AddRefs(stmt));
1597     MOZ_ASSERT(NS_SUCCEEDED(rv));
1598     bool hasResult = false;
1599     while (stmt && NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
1600       nsAutoCString name;
1601       rv = stmt->GetUTF8String(1, name);
1602       if (NS_SUCCEEDED(rv) && !name.EqualsLiteral("main") &&
1603           !name.EqualsLiteral("temp")) {
1604         nsCString path;
1605         rv = stmt->GetUTF8String(2, path);
1606         if (NS_SUCCEEDED(rv) && !path.IsEmpty()) {
1607           nsCOMPtr<mozIStorageStatement> attachStmt;
1608           rv = aClone->CreateStatement("ATTACH DATABASE :path AS "_ns + name,
1609                                        getter_AddRefs(attachStmt));
1610           MOZ_ASSERT(NS_SUCCEEDED(rv));
1611           rv = attachStmt->BindUTF8StringByName("path"_ns, path);
1612           MOZ_ASSERT(NS_SUCCEEDED(rv));
1613           rv = attachStmt->Execute();
1614           MOZ_ASSERT(NS_SUCCEEDED(rv),
1615                      "couldn't re-attach database to cloned connection");
1616         }
1617       }
1618     }
1619   }
1620 
1621   // Copy over pragmas from the original connection.
1622   // LIMITATION WARNING!  Many of these pragmas are actually scoped to the
1623   // schema ("main" and any other attached databases), and this implmentation
1624   // fails to propagate them.  This is being addressed on trunk.
1625   static const char* pragmas[] = {
1626       "cache_size",  "temp_store",         "foreign_keys", "journal_size_limit",
1627       "synchronous", "wal_autocheckpoint", "busy_timeout"};
1628   for (auto& pragma : pragmas) {
1629     // Read-only connections just need cache_size and temp_store pragmas.
1630     if (aReadOnly && ::strcmp(pragma, "cache_size") != 0 &&
1631         ::strcmp(pragma, "temp_store") != 0) {
1632       continue;
1633     }
1634 
1635     nsAutoCString pragmaQuery("PRAGMA ");
1636     pragmaQuery.Append(pragma);
1637     nsCOMPtr<mozIStorageStatement> stmt;
1638     rv = CreateStatement(pragmaQuery, getter_AddRefs(stmt));
1639     MOZ_ASSERT(NS_SUCCEEDED(rv));
1640     bool hasResult = false;
1641     if (stmt && NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
1642       pragmaQuery.AppendLiteral(" = ");
1643       pragmaQuery.AppendInt(stmt->AsInt32(0));
1644       rv = aClone->ExecuteSimpleSQL(pragmaQuery);
1645       MOZ_ASSERT(NS_SUCCEEDED(rv));
1646     }
1647   }
1648 
1649   // Copy over temporary tables, triggers, and views from the original
1650   // connections. Entities in `sqlite_temp_master` are only visible to the
1651   // connection that created them.
1652   if (!aReadOnly) {
1653     rv = aClone->ExecuteSimpleSQL("BEGIN TRANSACTION"_ns);
1654     NS_ENSURE_SUCCESS(rv, rv);
1655 
1656     nsCOMPtr<mozIStorageStatement> stmt;
1657     rv = CreateStatement(nsLiteralCString("SELECT sql FROM sqlite_temp_master "
1658                                           "WHERE type IN ('table', 'view', "
1659                                           "'index', 'trigger')"),
1660                          getter_AddRefs(stmt));
1661     // Propagate errors, because failing to copy triggers might cause schema
1662     // coherency issues when writing to the database from the cloned connection.
1663     NS_ENSURE_SUCCESS(rv, rv);
1664     bool hasResult = false;
1665     while (stmt && NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
1666       nsAutoCString query;
1667       rv = stmt->GetUTF8String(0, query);
1668       NS_ENSURE_SUCCESS(rv, rv);
1669 
1670       // The `CREATE` SQL statements in `sqlite_temp_master` omit the `TEMP`
1671       // keyword. We need to add it back, or we'll recreate temporary entities
1672       // as persistent ones. `sqlite_temp_master` also holds `CREATE INDEX`
1673       // statements, but those don't need `TEMP` keywords.
1674       if (StringBeginsWith(query, "CREATE TABLE "_ns) ||
1675           StringBeginsWith(query, "CREATE TRIGGER "_ns) ||
1676           StringBeginsWith(query, "CREATE VIEW "_ns)) {
1677         query.Replace(0, 6, "CREATE TEMP");
1678       }
1679 
1680       rv = aClone->ExecuteSimpleSQL(query);
1681       NS_ENSURE_SUCCESS(rv, rv);
1682     }
1683 
1684     rv = aClone->ExecuteSimpleSQL("COMMIT"_ns);
1685     NS_ENSURE_SUCCESS(rv, rv);
1686   }
1687 
1688   // Copy any functions that have been added to this connection.
1689   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1690   for (const auto& entry : mFunctions) {
1691     const nsACString& key = entry.GetKey();
1692     Connection::FunctionInfo data = entry.GetData();
1693 
1694     rv = aClone->CreateFunction(key, data.numArgs, data.function);
1695     if (NS_FAILED(rv)) {
1696       NS_WARNING("Failed to copy function to cloned connection");
1697     }
1698   }
1699 
1700   guard.release();
1701   return NS_OK;
1702 }
1703 
1704 NS_IMETHODIMP
Clone(bool aReadOnly,mozIStorageConnection ** _connection)1705 Connection::Clone(bool aReadOnly, mozIStorageConnection** _connection) {
1706   MOZ_ASSERT(threadOpenedOn == NS_GetCurrentThread());
1707 
1708   AUTO_PROFILER_LABEL("Connection::Clone", OTHER);
1709 
1710   if (!connectionReady()) {
1711     return NS_ERROR_NOT_INITIALIZED;
1712   }
1713   nsresult rv = ensureOperationSupported(SYNCHRONOUS);
1714   if (NS_FAILED(rv)) {
1715     return rv;
1716   }
1717 
1718   int flags = mFlags;
1719   if (aReadOnly) {
1720     // Turn off SQLITE_OPEN_READWRITE, and set SQLITE_OPEN_READONLY.
1721     flags = (~SQLITE_OPEN_READWRITE & flags) | SQLITE_OPEN_READONLY;
1722     // Turn off SQLITE_OPEN_CREATE.
1723     flags = (~SQLITE_OPEN_CREATE & flags);
1724   }
1725 
1726   RefPtr<Connection> clone =
1727       new Connection(mStorageService, flags, mSupportedOperations);
1728 
1729   rv = initializeClone(clone, aReadOnly);
1730   if (NS_FAILED(rv)) {
1731     return rv;
1732   }
1733 
1734   NS_IF_ADDREF(*_connection = clone);
1735   return NS_OK;
1736 }
1737 
1738 NS_IMETHODIMP
Interrupt()1739 Connection::Interrupt() {
1740   MOZ_ASSERT(threadOpenedOn == NS_GetCurrentThread());
1741   if (!connectionReady()) {
1742     return NS_ERROR_NOT_INITIALIZED;
1743   }
1744   if (operationSupported(SYNCHRONOUS) || !(mFlags & SQLITE_OPEN_READONLY)) {
1745     // Interrupting a synchronous connection from the same thread doesn't make
1746     // sense, and read-write connections aren't safe to interrupt.
1747     return NS_ERROR_INVALID_ARG;
1748   }
1749   ::sqlite3_interrupt(mDBConn);
1750   return NS_OK;
1751 }
1752 
1753 NS_IMETHODIMP
GetDefaultPageSize(int32_t * _defaultPageSize)1754 Connection::GetDefaultPageSize(int32_t* _defaultPageSize) {
1755   *_defaultPageSize = Service::kDefaultPageSize;
1756   return NS_OK;
1757 }
1758 
1759 NS_IMETHODIMP
GetConnectionReady(bool * _ready)1760 Connection::GetConnectionReady(bool* _ready) {
1761   MOZ_ASSERT(threadOpenedOn == NS_GetCurrentThread());
1762   *_ready = connectionReady();
1763   return NS_OK;
1764 }
1765 
1766 NS_IMETHODIMP
GetDatabaseFile(nsIFile ** _dbFile)1767 Connection::GetDatabaseFile(nsIFile** _dbFile) {
1768   if (!connectionReady()) {
1769     return NS_ERROR_NOT_INITIALIZED;
1770   }
1771   nsresult rv = ensureOperationSupported(ASYNCHRONOUS);
1772   if (NS_FAILED(rv)) {
1773     return rv;
1774   }
1775 
1776   NS_IF_ADDREF(*_dbFile = mDatabaseFile);
1777 
1778   return NS_OK;
1779 }
1780 
1781 NS_IMETHODIMP
GetLastInsertRowID(int64_t * _id)1782 Connection::GetLastInsertRowID(int64_t* _id) {
1783   if (!connectionReady()) {
1784     return NS_ERROR_NOT_INITIALIZED;
1785   }
1786   nsresult rv = ensureOperationSupported(SYNCHRONOUS);
1787   if (NS_FAILED(rv)) {
1788     return rv;
1789   }
1790 
1791   sqlite_int64 id = ::sqlite3_last_insert_rowid(mDBConn);
1792   *_id = id;
1793 
1794   return NS_OK;
1795 }
1796 
1797 NS_IMETHODIMP
GetAffectedRows(int32_t * _rows)1798 Connection::GetAffectedRows(int32_t* _rows) {
1799   if (!connectionReady()) {
1800     return NS_ERROR_NOT_INITIALIZED;
1801   }
1802   nsresult rv = ensureOperationSupported(SYNCHRONOUS);
1803   if (NS_FAILED(rv)) {
1804     return rv;
1805   }
1806 
1807   *_rows = ::sqlite3_changes(mDBConn);
1808 
1809   return NS_OK;
1810 }
1811 
1812 NS_IMETHODIMP
GetLastError(int32_t * _error)1813 Connection::GetLastError(int32_t* _error) {
1814   if (!connectionReady()) {
1815     return NS_ERROR_NOT_INITIALIZED;
1816   }
1817   nsresult rv = ensureOperationSupported(SYNCHRONOUS);
1818   if (NS_FAILED(rv)) {
1819     return rv;
1820   }
1821 
1822   *_error = ::sqlite3_errcode(mDBConn);
1823 
1824   return NS_OK;
1825 }
1826 
1827 NS_IMETHODIMP
GetLastErrorString(nsACString & _errorString)1828 Connection::GetLastErrorString(nsACString& _errorString) {
1829   if (!connectionReady()) {
1830     return NS_ERROR_NOT_INITIALIZED;
1831   }
1832   nsresult rv = ensureOperationSupported(SYNCHRONOUS);
1833   if (NS_FAILED(rv)) {
1834     return rv;
1835   }
1836 
1837   const char* serr = ::sqlite3_errmsg(mDBConn);
1838   _errorString.Assign(serr);
1839 
1840   return NS_OK;
1841 }
1842 
1843 NS_IMETHODIMP
GetSchemaVersion(int32_t * _version)1844 Connection::GetSchemaVersion(int32_t* _version) {
1845   if (!connectionReady()) {
1846     return NS_ERROR_NOT_INITIALIZED;
1847   }
1848   nsresult rv = ensureOperationSupported(SYNCHRONOUS);
1849   if (NS_FAILED(rv)) {
1850     return rv;
1851   }
1852 
1853   nsCOMPtr<mozIStorageStatement> stmt;
1854   (void)CreateStatement("PRAGMA user_version"_ns, getter_AddRefs(stmt));
1855   NS_ENSURE_TRUE(stmt, NS_ERROR_OUT_OF_MEMORY);
1856 
1857   *_version = 0;
1858   bool hasResult;
1859   if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult)
1860     *_version = stmt->AsInt32(0);
1861 
1862   return NS_OK;
1863 }
1864 
1865 NS_IMETHODIMP
SetSchemaVersion(int32_t aVersion)1866 Connection::SetSchemaVersion(int32_t aVersion) {
1867   if (!connectionReady()) {
1868     return NS_ERROR_NOT_INITIALIZED;
1869   }
1870   nsresult rv = ensureOperationSupported(SYNCHRONOUS);
1871   if (NS_FAILED(rv)) {
1872     return rv;
1873   }
1874 
1875   nsAutoCString stmt("PRAGMA user_version = "_ns);
1876   stmt.AppendInt(aVersion);
1877 
1878   return ExecuteSimpleSQL(stmt);
1879 }
1880 
1881 NS_IMETHODIMP
CreateStatement(const nsACString & aSQLStatement,mozIStorageStatement ** _stmt)1882 Connection::CreateStatement(const nsACString& aSQLStatement,
1883                             mozIStorageStatement** _stmt) {
1884   NS_ENSURE_ARG_POINTER(_stmt);
1885   if (!connectionReady()) {
1886     return NS_ERROR_NOT_INITIALIZED;
1887   }
1888   nsresult rv = ensureOperationSupported(SYNCHRONOUS);
1889   if (NS_FAILED(rv)) {
1890     return rv;
1891   }
1892 
1893   RefPtr<Statement> statement(new Statement());
1894   NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
1895 
1896   rv = statement->initialize(this, mDBConn, aSQLStatement);
1897   NS_ENSURE_SUCCESS(rv, rv);
1898 
1899   Statement* rawPtr;
1900   statement.forget(&rawPtr);
1901   *_stmt = rawPtr;
1902   return NS_OK;
1903 }
1904 
1905 NS_IMETHODIMP
CreateAsyncStatement(const nsACString & aSQLStatement,mozIStorageAsyncStatement ** _stmt)1906 Connection::CreateAsyncStatement(const nsACString& aSQLStatement,
1907                                  mozIStorageAsyncStatement** _stmt) {
1908   NS_ENSURE_ARG_POINTER(_stmt);
1909   if (!connectionReady()) {
1910     return NS_ERROR_NOT_INITIALIZED;
1911   }
1912   nsresult rv = ensureOperationSupported(ASYNCHRONOUS);
1913   if (NS_FAILED(rv)) {
1914     return rv;
1915   }
1916 
1917   RefPtr<AsyncStatement> statement(new AsyncStatement());
1918   NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
1919 
1920   rv = statement->initialize(this, mDBConn, aSQLStatement);
1921   NS_ENSURE_SUCCESS(rv, rv);
1922 
1923   AsyncStatement* rawPtr;
1924   statement.forget(&rawPtr);
1925   *_stmt = rawPtr;
1926   return NS_OK;
1927 }
1928 
1929 NS_IMETHODIMP
ExecuteSimpleSQL(const nsACString & aSQLStatement)1930 Connection::ExecuteSimpleSQL(const nsACString& aSQLStatement) {
1931   CHECK_MAINTHREAD_ABUSE();
1932   if (!connectionReady()) {
1933     return NS_ERROR_NOT_INITIALIZED;
1934   }
1935   nsresult rv = ensureOperationSupported(SYNCHRONOUS);
1936   if (NS_FAILED(rv)) {
1937     return rv;
1938   }
1939 
1940   int srv = executeSql(mDBConn, PromiseFlatCString(aSQLStatement).get());
1941   return convertResultCode(srv);
1942 }
1943 
1944 NS_IMETHODIMP
ExecuteAsync(const nsTArray<RefPtr<mozIStorageBaseStatement>> & aStatements,mozIStorageStatementCallback * aCallback,mozIStoragePendingStatement ** _handle)1945 Connection::ExecuteAsync(
1946     const nsTArray<RefPtr<mozIStorageBaseStatement>>& aStatements,
1947     mozIStorageStatementCallback* aCallback,
1948     mozIStoragePendingStatement** _handle) {
1949   nsTArray<StatementData> stmts(aStatements.Length());
1950   for (uint32_t i = 0; i < aStatements.Length(); i++) {
1951     nsCOMPtr<StorageBaseStatementInternal> stmt =
1952         do_QueryInterface(aStatements[i]);
1953     NS_ENSURE_STATE(stmt);
1954 
1955     // Obtain our StatementData.
1956     StatementData data;
1957     nsresult rv = stmt->getAsynchronousStatementData(data);
1958     NS_ENSURE_SUCCESS(rv, rv);
1959 
1960     NS_ASSERTION(stmt->getOwner() == this,
1961                  "Statement must be from this database connection!");
1962 
1963     // Now append it to our array.
1964     stmts.AppendElement(data);
1965   }
1966 
1967   // Dispatch to the background
1968   return AsyncExecuteStatements::execute(std::move(stmts), this, mDBConn,
1969                                          aCallback, _handle);
1970 }
1971 
1972 NS_IMETHODIMP
ExecuteSimpleSQLAsync(const nsACString & aSQLStatement,mozIStorageStatementCallback * aCallback,mozIStoragePendingStatement ** _handle)1973 Connection::ExecuteSimpleSQLAsync(const nsACString& aSQLStatement,
1974                                   mozIStorageStatementCallback* aCallback,
1975                                   mozIStoragePendingStatement** _handle) {
1976   NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_SAME_THREAD);
1977 
1978   nsCOMPtr<mozIStorageAsyncStatement> stmt;
1979   nsresult rv = CreateAsyncStatement(aSQLStatement, getter_AddRefs(stmt));
1980   if (NS_FAILED(rv)) {
1981     return rv;
1982   }
1983 
1984   nsCOMPtr<mozIStoragePendingStatement> pendingStatement;
1985   rv = stmt->ExecuteAsync(aCallback, getter_AddRefs(pendingStatement));
1986   if (NS_FAILED(rv)) {
1987     return rv;
1988   }
1989 
1990   pendingStatement.forget(_handle);
1991   return rv;
1992 }
1993 
1994 NS_IMETHODIMP
TableExists(const nsACString & aTableName,bool * _exists)1995 Connection::TableExists(const nsACString& aTableName, bool* _exists) {
1996   return databaseElementExists(TABLE, aTableName, _exists);
1997 }
1998 
1999 NS_IMETHODIMP
IndexExists(const nsACString & aIndexName,bool * _exists)2000 Connection::IndexExists(const nsACString& aIndexName, bool* _exists) {
2001   return databaseElementExists(INDEX, aIndexName, _exists);
2002 }
2003 
2004 NS_IMETHODIMP
GetTransactionInProgress(bool * _inProgress)2005 Connection::GetTransactionInProgress(bool* _inProgress) {
2006   if (!connectionReady()) {
2007     return NS_ERROR_NOT_INITIALIZED;
2008   }
2009   nsresult rv = ensureOperationSupported(ASYNCHRONOUS);
2010   if (NS_FAILED(rv)) {
2011     return rv;
2012   }
2013 
2014   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
2015   *_inProgress = transactionInProgress(lockedScope);
2016   return NS_OK;
2017 }
2018 
2019 NS_IMETHODIMP
GetDefaultTransactionType(int32_t * _type)2020 Connection::GetDefaultTransactionType(int32_t* _type) {
2021   *_type = mDefaultTransactionType;
2022   return NS_OK;
2023 }
2024 
2025 NS_IMETHODIMP
SetDefaultTransactionType(int32_t aType)2026 Connection::SetDefaultTransactionType(int32_t aType) {
2027   NS_ENSURE_ARG_RANGE(aType, TRANSACTION_DEFERRED, TRANSACTION_EXCLUSIVE);
2028   mDefaultTransactionType = aType;
2029   return NS_OK;
2030 }
2031 
2032 NS_IMETHODIMP
GetVariableLimit(int32_t * _limit)2033 Connection::GetVariableLimit(int32_t* _limit) {
2034   if (!connectionReady()) {
2035     return NS_ERROR_NOT_INITIALIZED;
2036   }
2037   int limit = ::sqlite3_limit(mDBConn, SQLITE_LIMIT_VARIABLE_NUMBER, -1);
2038   if (limit < 0) {
2039     return NS_ERROR_UNEXPECTED;
2040   }
2041   *_limit = limit;
2042   return NS_OK;
2043 }
2044 
2045 NS_IMETHODIMP
BeginTransaction()2046 Connection::BeginTransaction() {
2047   if (!connectionReady()) {
2048     return NS_ERROR_NOT_INITIALIZED;
2049   }
2050   nsresult rv = ensureOperationSupported(SYNCHRONOUS);
2051   if (NS_FAILED(rv)) {
2052     return rv;
2053   }
2054 
2055   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
2056   return beginTransactionInternal(lockedScope, mDBConn,
2057                                   mDefaultTransactionType);
2058 }
2059 
beginTransactionInternal(const SQLiteMutexAutoLock & aProofOfLock,sqlite3 * aNativeConnection,int32_t aTransactionType)2060 nsresult Connection::beginTransactionInternal(
2061     const SQLiteMutexAutoLock& aProofOfLock, sqlite3* aNativeConnection,
2062     int32_t aTransactionType) {
2063   if (transactionInProgress(aProofOfLock)) {
2064     return NS_ERROR_FAILURE;
2065   }
2066   nsresult rv;
2067   switch (aTransactionType) {
2068     case TRANSACTION_DEFERRED:
2069       rv = convertResultCode(executeSql(aNativeConnection, "BEGIN DEFERRED"));
2070       break;
2071     case TRANSACTION_IMMEDIATE:
2072       rv = convertResultCode(executeSql(aNativeConnection, "BEGIN IMMEDIATE"));
2073       break;
2074     case TRANSACTION_EXCLUSIVE:
2075       rv = convertResultCode(executeSql(aNativeConnection, "BEGIN EXCLUSIVE"));
2076       break;
2077     default:
2078       return NS_ERROR_ILLEGAL_VALUE;
2079   }
2080   return rv;
2081 }
2082 
2083 NS_IMETHODIMP
CommitTransaction()2084 Connection::CommitTransaction() {
2085   if (!connectionReady()) {
2086     return NS_ERROR_NOT_INITIALIZED;
2087   }
2088   nsresult rv = ensureOperationSupported(SYNCHRONOUS);
2089   if (NS_FAILED(rv)) {
2090     return rv;
2091   }
2092 
2093   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
2094   return commitTransactionInternal(lockedScope, mDBConn);
2095 }
2096 
commitTransactionInternal(const SQLiteMutexAutoLock & aProofOfLock,sqlite3 * aNativeConnection)2097 nsresult Connection::commitTransactionInternal(
2098     const SQLiteMutexAutoLock& aProofOfLock, sqlite3* aNativeConnection) {
2099   if (!transactionInProgress(aProofOfLock)) {
2100     return NS_ERROR_UNEXPECTED;
2101   }
2102   nsresult rv =
2103       convertResultCode(executeSql(aNativeConnection, "COMMIT TRANSACTION"));
2104   return rv;
2105 }
2106 
2107 NS_IMETHODIMP
RollbackTransaction()2108 Connection::RollbackTransaction() {
2109   if (!connectionReady()) {
2110     return NS_ERROR_NOT_INITIALIZED;
2111   }
2112   nsresult rv = ensureOperationSupported(SYNCHRONOUS);
2113   if (NS_FAILED(rv)) {
2114     return rv;
2115   }
2116 
2117   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
2118   return rollbackTransactionInternal(lockedScope, mDBConn);
2119 }
2120 
rollbackTransactionInternal(const SQLiteMutexAutoLock & aProofOfLock,sqlite3 * aNativeConnection)2121 nsresult Connection::rollbackTransactionInternal(
2122     const SQLiteMutexAutoLock& aProofOfLock, sqlite3* aNativeConnection) {
2123   if (!transactionInProgress(aProofOfLock)) {
2124     return NS_ERROR_UNEXPECTED;
2125   }
2126 
2127   nsresult rv =
2128       convertResultCode(executeSql(aNativeConnection, "ROLLBACK TRANSACTION"));
2129   return rv;
2130 }
2131 
2132 NS_IMETHODIMP
CreateTable(const char * aTableName,const char * aTableSchema)2133 Connection::CreateTable(const char* aTableName, const char* aTableSchema) {
2134   if (!connectionReady()) {
2135     return NS_ERROR_NOT_INITIALIZED;
2136   }
2137   nsresult rv = ensureOperationSupported(SYNCHRONOUS);
2138   if (NS_FAILED(rv)) {
2139     return rv;
2140   }
2141 
2142   SmprintfPointer buf =
2143       ::mozilla::Smprintf("CREATE TABLE %s (%s)", aTableName, aTableSchema);
2144   if (!buf) return NS_ERROR_OUT_OF_MEMORY;
2145 
2146   int srv = executeSql(mDBConn, buf.get());
2147 
2148   return convertResultCode(srv);
2149 }
2150 
2151 NS_IMETHODIMP
CreateFunction(const nsACString & aFunctionName,int32_t aNumArguments,mozIStorageFunction * aFunction)2152 Connection::CreateFunction(const nsACString& aFunctionName,
2153                            int32_t aNumArguments,
2154                            mozIStorageFunction* aFunction) {
2155   if (!connectionReady()) {
2156     return NS_ERROR_NOT_INITIALIZED;
2157   }
2158   nsresult rv = ensureOperationSupported(ASYNCHRONOUS);
2159   if (NS_FAILED(rv)) {
2160     return rv;
2161   }
2162 
2163   // Check to see if this function is already defined.  We only check the name
2164   // because a function can be defined with the same body but different names.
2165   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
2166   NS_ENSURE_FALSE(mFunctions.Contains(aFunctionName), NS_ERROR_FAILURE);
2167 
2168   int srv = ::sqlite3_create_function(
2169       mDBConn, nsPromiseFlatCString(aFunctionName).get(), aNumArguments,
2170       SQLITE_ANY, aFunction, basicFunctionHelper, nullptr, nullptr);
2171   if (srv != SQLITE_OK) return convertResultCode(srv);
2172 
2173   FunctionInfo info = {aFunction, aNumArguments};
2174   mFunctions.InsertOrUpdate(aFunctionName, info);
2175 
2176   return NS_OK;
2177 }
2178 
2179 NS_IMETHODIMP
RemoveFunction(const nsACString & aFunctionName)2180 Connection::RemoveFunction(const nsACString& aFunctionName) {
2181   if (!connectionReady()) {
2182     return NS_ERROR_NOT_INITIALIZED;
2183   }
2184   nsresult rv = ensureOperationSupported(ASYNCHRONOUS);
2185   if (NS_FAILED(rv)) {
2186     return rv;
2187   }
2188 
2189   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
2190   NS_ENSURE_TRUE(mFunctions.Get(aFunctionName, nullptr), NS_ERROR_FAILURE);
2191 
2192   int srv = ::sqlite3_create_function(
2193       mDBConn, nsPromiseFlatCString(aFunctionName).get(), 0, SQLITE_ANY,
2194       nullptr, nullptr, nullptr, nullptr);
2195   if (srv != SQLITE_OK) return convertResultCode(srv);
2196 
2197   mFunctions.Remove(aFunctionName);
2198 
2199   return NS_OK;
2200 }
2201 
2202 NS_IMETHODIMP
SetProgressHandler(int32_t aGranularity,mozIStorageProgressHandler * aHandler,mozIStorageProgressHandler ** _oldHandler)2203 Connection::SetProgressHandler(int32_t aGranularity,
2204                                mozIStorageProgressHandler* aHandler,
2205                                mozIStorageProgressHandler** _oldHandler) {
2206   if (!connectionReady()) {
2207     return NS_ERROR_NOT_INITIALIZED;
2208   }
2209   nsresult rv = ensureOperationSupported(ASYNCHRONOUS);
2210   if (NS_FAILED(rv)) {
2211     return rv;
2212   }
2213 
2214   // Return previous one
2215   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
2216   NS_IF_ADDREF(*_oldHandler = mProgressHandler);
2217 
2218   if (!aHandler || aGranularity <= 0) {
2219     aHandler = nullptr;
2220     aGranularity = 0;
2221   }
2222   mProgressHandler = aHandler;
2223   ::sqlite3_progress_handler(mDBConn, aGranularity, sProgressHelper, this);
2224 
2225   return NS_OK;
2226 }
2227 
2228 NS_IMETHODIMP
RemoveProgressHandler(mozIStorageProgressHandler ** _oldHandler)2229 Connection::RemoveProgressHandler(mozIStorageProgressHandler** _oldHandler) {
2230   if (!connectionReady()) {
2231     return NS_ERROR_NOT_INITIALIZED;
2232   }
2233   nsresult rv = ensureOperationSupported(ASYNCHRONOUS);
2234   if (NS_FAILED(rv)) {
2235     return rv;
2236   }
2237 
2238   // Return previous one
2239   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
2240   NS_IF_ADDREF(*_oldHandler = mProgressHandler);
2241 
2242   mProgressHandler = nullptr;
2243   ::sqlite3_progress_handler(mDBConn, 0, nullptr, nullptr);
2244 
2245   return NS_OK;
2246 }
2247 
2248 NS_IMETHODIMP
SetGrowthIncrement(int32_t aChunkSize,const nsACString & aDatabaseName)2249 Connection::SetGrowthIncrement(int32_t aChunkSize,
2250                                const nsACString& aDatabaseName) {
2251   if (!connectionReady()) {
2252     return NS_ERROR_NOT_INITIALIZED;
2253   }
2254   nsresult rv = ensureOperationSupported(SYNCHRONOUS);
2255   if (NS_FAILED(rv)) {
2256     return rv;
2257   }
2258 
2259   // Bug 597215: Disk space is extremely limited on Android
2260   // so don't preallocate space. This is also not effective
2261   // on log structured file systems used by Android devices
2262 #if !defined(ANDROID) && !defined(MOZ_PLATFORM_MAEMO)
2263   // Don't preallocate if less than 500MiB is available.
2264   int64_t bytesAvailable;
2265   rv = mDatabaseFile->GetDiskSpaceAvailable(&bytesAvailable);
2266   NS_ENSURE_SUCCESS(rv, rv);
2267   if (bytesAvailable < MIN_AVAILABLE_BYTES_PER_CHUNKED_GROWTH) {
2268     return NS_ERROR_FILE_TOO_BIG;
2269   }
2270 
2271   (void)::sqlite3_file_control(mDBConn,
2272                                aDatabaseName.Length()
2273                                    ? nsPromiseFlatCString(aDatabaseName).get()
2274                                    : nullptr,
2275                                SQLITE_FCNTL_CHUNK_SIZE, &aChunkSize);
2276 #endif
2277   return NS_OK;
2278 }
2279 
2280 NS_IMETHODIMP
EnableModule(const nsACString & aModuleName)2281 Connection::EnableModule(const nsACString& aModuleName) {
2282   if (!connectionReady()) {
2283     return NS_ERROR_NOT_INITIALIZED;
2284   }
2285   nsresult rv = ensureOperationSupported(SYNCHRONOUS);
2286   if (NS_FAILED(rv)) {
2287     return rv;
2288   }
2289 
2290   for (auto& gModule : gModules) {
2291     struct Module* m = &gModule;
2292     if (aModuleName.Equals(m->name)) {
2293       int srv = m->registerFunc(mDBConn, m->name);
2294       if (srv != SQLITE_OK) return convertResultCode(srv);
2295 
2296       return NS_OK;
2297     }
2298   }
2299 
2300   return NS_ERROR_FAILURE;
2301 }
2302 
2303 // Implemented in TelemetryVFS.cpp
2304 already_AddRefed<QuotaObject> GetQuotaObjectForFile(sqlite3_file* pFile);
2305 
2306 NS_IMETHODIMP
GetQuotaObjects(QuotaObject ** aDatabaseQuotaObject,QuotaObject ** aJournalQuotaObject)2307 Connection::GetQuotaObjects(QuotaObject** aDatabaseQuotaObject,
2308                             QuotaObject** aJournalQuotaObject) {
2309   MOZ_ASSERT(aDatabaseQuotaObject);
2310   MOZ_ASSERT(aJournalQuotaObject);
2311 
2312   if (!connectionReady()) {
2313     return NS_ERROR_NOT_INITIALIZED;
2314   }
2315   nsresult rv = ensureOperationSupported(SYNCHRONOUS);
2316   if (NS_FAILED(rv)) {
2317     return rv;
2318   }
2319 
2320   sqlite3_file* file;
2321   int srv = ::sqlite3_file_control(mDBConn, nullptr, SQLITE_FCNTL_FILE_POINTER,
2322                                    &file);
2323   if (srv != SQLITE_OK) {
2324     return convertResultCode(srv);
2325   }
2326 
2327   RefPtr<QuotaObject> databaseQuotaObject = GetQuotaObjectForFile(file);
2328   if (NS_WARN_IF(!databaseQuotaObject)) {
2329     return NS_ERROR_FAILURE;
2330   }
2331 
2332   srv = ::sqlite3_file_control(mDBConn, nullptr, SQLITE_FCNTL_JOURNAL_POINTER,
2333                                &file);
2334   if (srv != SQLITE_OK) {
2335     return convertResultCode(srv);
2336   }
2337 
2338   RefPtr<QuotaObject> journalQuotaObject = GetQuotaObjectForFile(file);
2339   if (NS_WARN_IF(!journalQuotaObject)) {
2340     return NS_ERROR_FAILURE;
2341   }
2342 
2343   databaseQuotaObject.forget(aDatabaseQuotaObject);
2344   journalQuotaObject.forget(aJournalQuotaObject);
2345   return NS_OK;
2346 }
2347 
GetSharedDBMutex()2348 SQLiteMutex& Connection::GetSharedDBMutex() { return sharedDBMutex; }
2349 
GetTransactionNestingLevel(const mozilla::storage::SQLiteMutexAutoLock & aProofOfLock)2350 uint32_t Connection::GetTransactionNestingLevel(
2351     const mozilla::storage::SQLiteMutexAutoLock& aProofOfLock) {
2352   return mTransactionNestingLevel;
2353 }
2354 
IncreaseTransactionNestingLevel(const mozilla::storage::SQLiteMutexAutoLock & aProofOfLock)2355 uint32_t Connection::IncreaseTransactionNestingLevel(
2356     const mozilla::storage::SQLiteMutexAutoLock& aProofOfLock) {
2357   return ++mTransactionNestingLevel;
2358 }
2359 
DecreaseTransactionNestingLevel(const mozilla::storage::SQLiteMutexAutoLock & aProofOfLock)2360 uint32_t Connection::DecreaseTransactionNestingLevel(
2361     const mozilla::storage::SQLiteMutexAutoLock& aProofOfLock) {
2362   return --mTransactionNestingLevel;
2363 }
2364 
2365 }  // namespace mozilla::storage
2366