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