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