Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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/. */
7 #include <stdio.h>
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"
21 #include "mozIStorageAggregateFunction.h"
22 #include "mozIStorageCompletionCallback.h"
23 #include "mozIStorageFunction.h"
25 #include "mozStorageAsyncStatementExecution.h"
26 #include "mozStorageSQLFunctions.h"
27 #include "mozStorageConnection.h"
28 #include "mozStorageService.h"
29 #include "mozStorageStatement.h"
30 #include "mozStorageAsyncStatement.h"
31 #include "mozStorageArgValueArray.h"
32 #include "mozStoragePrivateHelpers.h"
33 #include "mozStorageStatementData.h"
34 #include "StorageBaseStatementInternal.h"
35 #include "SQLCollations.h"
36 #include "FileSystemModule.h"
37 #include "mozStorageHelper.h"
38 #include "GeckoProfiler.h"
40 #include "prlog.h"
41 #include "prprf.h"
42 #include "nsProxyRelease.h"
43 #include <algorithm>
45 #define MIN_AVAILABLE_BYTES_PER_CHUNKED_GROWTH 524288000 // 500 MiB
47 // Maximum size of the pages cache per connection.
48 #define MAX_CACHE_SIZE_KIBIBYTES 2048 // 2 MiB
50 #ifdef PR_LOGGING
51 PRLogModuleInfo* gStorageLog = nullptr;
52 #endif
54 namespace mozilla {
55 namespace storage {
57 namespace {
59 ////////////////////////////////////////////////////////////////////////////////
60 //// Variant Specialization Functions (variantToSQLiteT)
62 int
63 sqlite3_T_int(sqlite3_context *aCtx,
64 int aValue)
65 {
66 ::sqlite3_result_int(aCtx, aValue);
67 return SQLITE_OK;
68 }
70 int
71 sqlite3_T_int64(sqlite3_context *aCtx,
72 sqlite3_int64 aValue)
73 {
74 ::sqlite3_result_int64(aCtx, aValue);
75 return SQLITE_OK;
76 }
78 int
79 sqlite3_T_double(sqlite3_context *aCtx,
80 double aValue)
81 {
82 ::sqlite3_result_double(aCtx, aValue);
83 return SQLITE_OK;
84 }
86 int
87 sqlite3_T_text(sqlite3_context *aCtx,
88 const nsCString &aValue)
89 {
90 ::sqlite3_result_text(aCtx,
91 aValue.get(),
92 aValue.Length(),
93 SQLITE_TRANSIENT);
94 return SQLITE_OK;
95 }
97 int
98 sqlite3_T_text16(sqlite3_context *aCtx,
99 const nsString &aValue)
100 {
101 ::sqlite3_result_text16(aCtx,
102 aValue.get(),
103 aValue.Length() * 2, // Number of bytes.
104 SQLITE_TRANSIENT);
105 return SQLITE_OK;
106 }
108 int
109 sqlite3_T_null(sqlite3_context *aCtx)
110 {
111 ::sqlite3_result_null(aCtx);
112 return SQLITE_OK;
113 }
115 int
116 sqlite3_T_blob(sqlite3_context *aCtx,
117 const void *aData,
118 int aSize)
119 {
120 ::sqlite3_result_blob(aCtx, aData, aSize, NS_Free);
121 return SQLITE_OK;
122 }
124 #include "variantToSQLiteT_impl.h"
126 ////////////////////////////////////////////////////////////////////////////////
127 //// Modules
129 struct Module
130 {
131 const char* name;
132 int (*registerFunc)(sqlite3*, const char*);
133 };
135 Module gModules[] = {
136 { "filesystem", RegisterFileSystemModule }
137 };
139 ////////////////////////////////////////////////////////////////////////////////
140 //// Local Functions
142 #ifdef PR_LOGGING
143 void tracefunc (void *aClosure, const char *aStmt)
144 {
145 PR_LOG(gStorageLog, PR_LOG_DEBUG, ("sqlite3_trace on %p for '%s'", aClosure,
146 aStmt));
147 }
148 #endif
150 struct FFEArguments
151 {
152 nsISupports *target;
153 bool found;
154 };
155 PLDHashOperator
156 findFunctionEnumerator(const nsACString &aKey,
157 Connection::FunctionInfo aData,
158 void *aUserArg)
159 {
160 FFEArguments *args = static_cast<FFEArguments *>(aUserArg);
161 if (aData.function == args->target) {
162 args->found = true;
163 return PL_DHASH_STOP;
164 }
165 return PL_DHASH_NEXT;
166 }
168 PLDHashOperator
169 copyFunctionEnumerator(const nsACString &aKey,
170 Connection::FunctionInfo aData,
171 void *aUserArg)
172 {
173 NS_PRECONDITION(aData.type == Connection::FunctionInfo::SIMPLE ||
174 aData.type == Connection::FunctionInfo::AGGREGATE,
175 "Invalid function type!");
177 Connection *connection = static_cast<Connection *>(aUserArg);
178 if (aData.type == Connection::FunctionInfo::SIMPLE) {
179 mozIStorageFunction *function =
180 static_cast<mozIStorageFunction *>(aData.function.get());
181 (void)connection->CreateFunction(aKey, aData.numArgs, function);
182 }
183 else {
184 mozIStorageAggregateFunction *function =
185 static_cast<mozIStorageAggregateFunction *>(aData.function.get());
186 (void)connection->CreateAggregateFunction(aKey, aData.numArgs, function);
187 }
189 return PL_DHASH_NEXT;
190 }
192 void
193 basicFunctionHelper(sqlite3_context *aCtx,
194 int aArgc,
195 sqlite3_value **aArgv)
196 {
197 void *userData = ::sqlite3_user_data(aCtx);
199 mozIStorageFunction *func = static_cast<mozIStorageFunction *>(userData);
201 nsRefPtr<ArgValueArray> arguments(new ArgValueArray(aArgc, aArgv));
202 if (!arguments)
203 return;
205 nsCOMPtr<nsIVariant> result;
206 if (NS_FAILED(func->OnFunctionCall(arguments, getter_AddRefs(result)))) {
207 NS_WARNING("User function returned error code!");
208 ::sqlite3_result_error(aCtx,
209 "User function returned error code",
210 -1);
211 return;
212 }
213 int retcode = variantToSQLiteT(aCtx, result);
214 if (retcode == SQLITE_IGNORE) {
215 ::sqlite3_result_int(aCtx, SQLITE_IGNORE);
216 } else if (retcode != SQLITE_OK) {
217 NS_WARNING("User function returned invalid data type!");
218 ::sqlite3_result_error(aCtx,
219 "User function returned invalid data type",
220 -1);
221 }
222 }
224 void
225 aggregateFunctionStepHelper(sqlite3_context *aCtx,
226 int aArgc,
227 sqlite3_value **aArgv)
228 {
229 void *userData = ::sqlite3_user_data(aCtx);
230 mozIStorageAggregateFunction *func =
231 static_cast<mozIStorageAggregateFunction *>(userData);
233 nsRefPtr<ArgValueArray> arguments(new ArgValueArray(aArgc, aArgv));
234 if (!arguments)
235 return;
237 if (NS_FAILED(func->OnStep(arguments)))
238 NS_WARNING("User aggregate step function returned error code!");
239 }
241 void
242 aggregateFunctionFinalHelper(sqlite3_context *aCtx)
243 {
244 void *userData = ::sqlite3_user_data(aCtx);
245 mozIStorageAggregateFunction *func =
246 static_cast<mozIStorageAggregateFunction *>(userData);
248 nsRefPtr<nsIVariant> result;
249 if (NS_FAILED(func->OnFinal(getter_AddRefs(result)))) {
250 NS_WARNING("User aggregate final function returned error code!");
251 ::sqlite3_result_error(aCtx,
252 "User aggregate final function returned error code",
253 -1);
254 return;
255 }
257 if (variantToSQLiteT(aCtx, result) != SQLITE_OK) {
258 NS_WARNING("User aggregate final function returned invalid data type!");
259 ::sqlite3_result_error(aCtx,
260 "User aggregate final function returned invalid data type",
261 -1);
262 }
263 }
265 /**
266 * This code is heavily based on the sample at:
267 * http://www.sqlite.org/unlock_notify.html
268 */
269 class UnlockNotification
270 {
271 public:
272 UnlockNotification()
273 : mMutex("UnlockNotification mMutex")
274 , mCondVar(mMutex, "UnlockNotification condVar")
275 , mSignaled(false)
276 {
277 }
279 void Wait()
280 {
281 MutexAutoLock lock(mMutex);
282 while (!mSignaled) {
283 (void)mCondVar.Wait();
284 }
285 }
287 void Signal()
288 {
289 MutexAutoLock lock(mMutex);
290 mSignaled = true;
291 (void)mCondVar.Notify();
292 }
294 private:
295 Mutex mMutex;
296 CondVar mCondVar;
297 bool mSignaled;
298 };
300 void
301 UnlockNotifyCallback(void **aArgs,
302 int aArgsSize)
303 {
304 for (int i = 0; i < aArgsSize; i++) {
305 UnlockNotification *notification =
306 static_cast<UnlockNotification *>(aArgs[i]);
307 notification->Signal();
308 }
309 }
311 int
312 WaitForUnlockNotify(sqlite3* aDatabase)
313 {
314 UnlockNotification notification;
315 int srv = ::sqlite3_unlock_notify(aDatabase, UnlockNotifyCallback,
316 ¬ification);
317 MOZ_ASSERT(srv == SQLITE_LOCKED || srv == SQLITE_OK);
318 if (srv == SQLITE_OK) {
319 notification.Wait();
320 }
322 return srv;
323 }
325 } // anonymous namespace
327 ////////////////////////////////////////////////////////////////////////////////
328 //// Local Classes
330 namespace {
332 class AsyncCloseConnection MOZ_FINAL: public nsRunnable
333 {
334 public:
335 AsyncCloseConnection(Connection *aConnection,
336 sqlite3 *aNativeConnection,
337 nsIRunnable *aCallbackEvent,
338 already_AddRefed<nsIThread> aAsyncExecutionThread)
339 : mConnection(aConnection)
340 , mNativeConnection(aNativeConnection)
341 , mCallbackEvent(aCallbackEvent)
342 , mAsyncExecutionThread(aAsyncExecutionThread)
343 {
344 }
346 NS_METHOD Run()
347 {
348 #ifdef DEBUG
349 // This code is executed on the background thread
350 bool onAsyncThread = false;
351 (void)mAsyncExecutionThread->IsOnCurrentThread(&onAsyncThread);
352 MOZ_ASSERT(onAsyncThread);
353 #endif // DEBUG
355 // Internal close.
356 (void)mConnection->internalClose(mNativeConnection);
358 // Callback
359 if (mCallbackEvent) {
360 nsCOMPtr<nsIThread> thread;
361 (void)NS_GetMainThread(getter_AddRefs(thread));
362 (void)thread->Dispatch(mCallbackEvent, NS_DISPATCH_NORMAL);
363 }
365 return NS_OK;
366 }
368 ~AsyncCloseConnection() {
369 nsCOMPtr<nsIThread> thread;
370 (void)NS_GetMainThread(getter_AddRefs(thread));
371 // Handle ambiguous nsISupports inheritance.
372 Connection *rawConnection = nullptr;
373 mConnection.swap(rawConnection);
374 (void)NS_ProxyRelease(thread,
375 NS_ISUPPORTS_CAST(mozIStorageConnection *,
376 rawConnection));
377 (void)NS_ProxyRelease(thread, mCallbackEvent);
378 }
379 private:
380 nsRefPtr<Connection> mConnection;
381 sqlite3 *mNativeConnection;
382 nsCOMPtr<nsIRunnable> mCallbackEvent;
383 nsCOMPtr<nsIThread> mAsyncExecutionThread;
384 };
386 /**
387 * An event used to initialize the clone of a connection.
388 *
389 * Must be executed on the clone's async execution thread.
390 */
391 class AsyncInitializeClone MOZ_FINAL: public nsRunnable
392 {
393 public:
394 /**
395 * @param aConnection The connection being cloned.
396 * @param aClone The clone.
397 * @param aReadOnly If |true|, the clone is read only.
398 * @param aCallback A callback to trigger once initialization
399 * is complete. This event will be called on
400 * aClone->threadOpenedOn.
401 */
402 AsyncInitializeClone(Connection* aConnection,
403 Connection* aClone,
404 const bool aReadOnly,
405 mozIStorageCompletionCallback* aCallback)
406 : mConnection(aConnection)
407 , mClone(aClone)
408 , mReadOnly(aReadOnly)
409 , mCallback(aCallback)
410 {
411 MOZ_ASSERT(NS_IsMainThread());
412 }
414 NS_IMETHOD Run() {
415 MOZ_ASSERT (NS_GetCurrentThread() == mClone->getAsyncExecutionTarget());
417 nsresult rv = mConnection->initializeClone(mClone, mReadOnly);
418 if (NS_FAILED(rv)) {
419 return Dispatch(rv, nullptr);
420 }
421 return Dispatch(NS_OK,
422 NS_ISUPPORTS_CAST(mozIStorageAsyncConnection*, mClone));
423 }
425 private:
426 nsresult Dispatch(nsresult aResult, nsISupports* aValue) {
427 nsRefPtr<CallbackComplete> event = new CallbackComplete(aResult,
428 aValue,
429 mCallback.forget());
430 return mClone->threadOpenedOn->Dispatch(event, NS_DISPATCH_NORMAL);
431 }
433 ~AsyncInitializeClone() {
434 nsCOMPtr<nsIThread> thread;
435 DebugOnly<nsresult> rv = NS_GetMainThread(getter_AddRefs(thread));
436 MOZ_ASSERT(NS_SUCCEEDED(rv));
438 // Handle ambiguous nsISupports inheritance.
439 Connection *rawConnection = nullptr;
440 mConnection.swap(rawConnection);
441 (void)NS_ProxyRelease(thread, NS_ISUPPORTS_CAST(mozIStorageConnection *,
442 rawConnection));
444 Connection *rawClone = nullptr;
445 mClone.swap(rawClone);
446 (void)NS_ProxyRelease(thread, NS_ISUPPORTS_CAST(mozIStorageConnection *,
447 rawClone));
449 // Generally, the callback will be released by CallbackComplete.
450 // However, if for some reason Run() is not executed, we still
451 // need to ensure that it is released here.
452 mozIStorageCompletionCallback *rawCallback = nullptr;
453 mCallback.swap(rawCallback);
454 (void)NS_ProxyRelease(thread, rawCallback);
455 }
457 nsRefPtr<Connection> mConnection;
458 nsRefPtr<Connection> mClone;
459 const bool mReadOnly;
460 nsCOMPtr<mozIStorageCompletionCallback> mCallback;
461 };
463 } // anonymous namespace
465 ////////////////////////////////////////////////////////////////////////////////
466 //// Connection
468 Connection::Connection(Service *aService,
469 int aFlags,
470 bool aAsyncOnly)
471 : sharedAsyncExecutionMutex("Connection::sharedAsyncExecutionMutex")
472 , sharedDBMutex("Connection::sharedDBMutex")
473 , threadOpenedOn(do_GetCurrentThread())
474 , mDBConn(nullptr)
475 , mAsyncExecutionThreadShuttingDown(false)
476 , mConnectionClosed(false)
477 , mTransactionInProgress(false)
478 , mProgressHandler(nullptr)
479 , mFlags(aFlags)
480 , mStorageService(aService)
481 , mAsyncOnly(aAsyncOnly)
482 {
483 mStorageService->registerConnection(this);
484 }
486 Connection::~Connection()
487 {
488 (void)Close();
490 MOZ_ASSERT(!mAsyncExecutionThread,
491 "AsyncClose has not been invoked on this connection!");
492 }
494 NS_IMPL_ADDREF(Connection)
496 NS_INTERFACE_MAP_BEGIN(Connection)
497 NS_INTERFACE_MAP_ENTRY(mozIStorageAsyncConnection)
498 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
499 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(mozIStorageConnection, !mAsyncOnly)
500 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageConnection)
501 NS_INTERFACE_MAP_END
503 // This is identical to what NS_IMPL_RELEASE provides, but with the
504 // extra |1 == count| case.
505 NS_IMETHODIMP_(MozExternalRefCountType) Connection::Release(void)
506 {
507 NS_PRECONDITION(0 != mRefCnt, "dup release");
508 nsrefcnt count = --mRefCnt;
509 NS_LOG_RELEASE(this, count, "Connection");
510 if (1 == count) {
511 // If the refcount is 1, the single reference must be from
512 // gService->mConnections (in class |Service|). Which means we can
513 // unregister it safely.
514 mStorageService->unregisterConnection(this);
515 } else if (0 == count) {
516 mRefCnt = 1; /* stabilize */
517 #if 0 /* enable this to find non-threadsafe destructors: */
518 NS_ASSERT_OWNINGTHREAD(Connection);
519 #endif
520 delete (this);
521 return 0;
522 }
523 return count;
524 }
526 int32_t
527 Connection::getSqliteRuntimeStatus(int32_t aStatusOption, int32_t* aMaxValue)
528 {
529 MOZ_ASSERT(mDBConn, "A connection must exist at this point");
530 int curr = 0, max = 0;
531 DebugOnly<int> rc = ::sqlite3_db_status(mDBConn, aStatusOption, &curr, &max, 0);
532 MOZ_ASSERT(NS_SUCCEEDED(convertResultCode(rc)));
533 if (aMaxValue)
534 *aMaxValue = max;
535 return curr;
536 }
538 nsIEventTarget *
539 Connection::getAsyncExecutionTarget()
540 {
541 MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
543 // If we are shutting down the asynchronous thread, don't hand out any more
544 // references to the thread.
545 if (mAsyncExecutionThreadShuttingDown)
546 return nullptr;
548 if (!mAsyncExecutionThread) {
549 nsresult rv = ::NS_NewThread(getter_AddRefs(mAsyncExecutionThread));
550 if (NS_FAILED(rv)) {
551 NS_WARNING("Failed to create async thread.");
552 return nullptr;
553 }
554 static nsThreadPoolNaming naming;
555 naming.SetThreadPoolName(NS_LITERAL_CSTRING("mozStorage"),
556 mAsyncExecutionThread);
557 }
559 return mAsyncExecutionThread;
560 }
562 nsresult
563 Connection::initialize()
564 {
565 NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
566 PROFILER_LABEL("storage", "Connection::initialize");
568 // in memory database requested, sqlite uses a magic file name
569 int srv = ::sqlite3_open_v2(":memory:", &mDBConn, mFlags, nullptr);
570 if (srv != SQLITE_OK) {
571 mDBConn = nullptr;
572 return convertResultCode(srv);
573 }
575 return initializeInternal(nullptr);
576 }
578 nsresult
579 Connection::initialize(nsIFile *aDatabaseFile)
580 {
581 NS_ASSERTION (aDatabaseFile, "Passed null file!");
582 NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
583 PROFILER_LABEL("storage", "Connection::initialize");
585 mDatabaseFile = aDatabaseFile;
587 nsAutoString path;
588 nsresult rv = aDatabaseFile->GetPath(path);
589 NS_ENSURE_SUCCESS(rv, rv);
591 int srv = ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path).get(), &mDBConn,
592 mFlags, nullptr);
593 if (srv != SQLITE_OK) {
594 mDBConn = nullptr;
595 return convertResultCode(srv);
596 }
598 rv = initializeInternal(aDatabaseFile);
599 NS_ENSURE_SUCCESS(rv, rv);
601 mDatabaseFile = aDatabaseFile;
603 return NS_OK;
604 }
606 nsresult
607 Connection::initialize(nsIFileURL *aFileURL)
608 {
609 NS_ASSERTION (aFileURL, "Passed null file URL!");
610 NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
611 PROFILER_LABEL("storage", "Connection::initialize");
613 nsCOMPtr<nsIFile> databaseFile;
614 nsresult rv = aFileURL->GetFile(getter_AddRefs(databaseFile));
615 NS_ENSURE_SUCCESS(rv, rv);
617 nsAutoCString spec;
618 rv = aFileURL->GetSpec(spec);
619 NS_ENSURE_SUCCESS(rv, rv);
621 int srv = ::sqlite3_open_v2(spec.get(), &mDBConn, mFlags, nullptr);
622 if (srv != SQLITE_OK) {
623 mDBConn = nullptr;
624 return convertResultCode(srv);
625 }
627 rv = initializeInternal(databaseFile);
628 NS_ENSURE_SUCCESS(rv, rv);
630 mFileURL = aFileURL;
631 mDatabaseFile = databaseFile;
633 return NS_OK;
634 }
637 nsresult
638 Connection::initializeInternal(nsIFile* aDatabaseFile)
639 {
640 // Properly wrap the database handle's mutex.
641 sharedDBMutex.initWithMutex(sqlite3_db_mutex(mDBConn));
643 #ifdef PR_LOGGING
644 if (!gStorageLog)
645 gStorageLog = ::PR_NewLogModule("mozStorage");
647 ::sqlite3_trace(mDBConn, tracefunc, this);
649 nsAutoCString leafName(":memory");
650 if (aDatabaseFile)
651 (void)aDatabaseFile->GetNativeLeafName(leafName);
652 PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Opening connection to '%s' (%p)",
653 leafName.get(), this));
654 #endif
656 int64_t pageSize = Service::getDefaultPageSize();
658 // Set page_size to the preferred default value. This is effective only if
659 // the database has just been created, otherwise, if the database does not
660 // use WAL journal mode, a VACUUM operation will updated its page_size.
661 nsAutoCString pageSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
662 "PRAGMA page_size = ");
663 pageSizeQuery.AppendInt(pageSize);
664 nsresult rv = ExecuteSimpleSQL(pageSizeQuery);
665 NS_ENSURE_SUCCESS(rv, rv);
667 // Setting the cache_size forces the database open, verifying if it is valid
668 // or corrupt. So this is executed regardless it being actually needed.
669 // The cache_size is calculated from the actual page_size, to save memory.
670 nsAutoCString cacheSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
671 "PRAGMA cache_size = ");
672 cacheSizeQuery.AppendInt(-MAX_CACHE_SIZE_KIBIBYTES);
673 int srv = executeSql(mDBConn, cacheSizeQuery.get());
674 if (srv != SQLITE_OK) {
675 ::sqlite3_close(mDBConn);
676 mDBConn = nullptr;
677 return convertResultCode(srv);
678 }
680 // Register our built-in SQL functions.
681 srv = registerFunctions(mDBConn);
682 if (srv != SQLITE_OK) {
683 ::sqlite3_close(mDBConn);
684 mDBConn = nullptr;
685 return convertResultCode(srv);
686 }
688 // Register our built-in SQL collating sequences.
689 srv = registerCollations(mDBConn, mStorageService);
690 if (srv != SQLITE_OK) {
691 ::sqlite3_close(mDBConn);
692 mDBConn = nullptr;
693 return convertResultCode(srv);
694 }
696 // Set the synchronous PRAGMA, according to the preference.
697 switch (Service::getSynchronousPref()) {
698 case 2:
699 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
700 "PRAGMA synchronous = FULL;"));
701 break;
702 case 0:
703 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
704 "PRAGMA synchronous = OFF;"));
705 break;
706 case 1:
707 default:
708 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
709 "PRAGMA synchronous = NORMAL;"));
710 break;
711 }
713 return NS_OK;
714 }
716 nsresult
717 Connection::databaseElementExists(enum DatabaseElementType aElementType,
718 const nsACString &aElementName,
719 bool *_exists)
720 {
721 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
723 // When constructing the query, make sure to SELECT the correct db's sqlite_master
724 // if the user is prefixing the element with a specific db. ex: sample.test
725 nsCString query("SELECT name FROM (SELECT * FROM ");
726 nsDependentCSubstring element;
727 int32_t ind = aElementName.FindChar('.');
728 if (ind == kNotFound) {
729 element.Assign(aElementName);
730 }
731 else {
732 nsDependentCSubstring db(Substring(aElementName, 0, ind + 1));
733 element.Assign(Substring(aElementName, ind + 1, aElementName.Length()));
734 query.Append(db);
735 }
736 query.Append("sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type = '");
738 switch (aElementType) {
739 case INDEX:
740 query.Append("index");
741 break;
742 case TABLE:
743 query.Append("table");
744 break;
745 }
746 query.Append("' AND name ='");
747 query.Append(element);
748 query.Append("'");
750 sqlite3_stmt *stmt;
751 int srv = prepareStatement(mDBConn, query, &stmt);
752 if (srv != SQLITE_OK)
753 return convertResultCode(srv);
755 srv = stepStatement(mDBConn, stmt);
756 // we just care about the return value from step
757 (void)::sqlite3_finalize(stmt);
759 if (srv == SQLITE_ROW) {
760 *_exists = true;
761 return NS_OK;
762 }
763 if (srv == SQLITE_DONE) {
764 *_exists = false;
765 return NS_OK;
766 }
768 return convertResultCode(srv);
769 }
771 bool
772 Connection::findFunctionByInstance(nsISupports *aInstance)
773 {
774 sharedDBMutex.assertCurrentThreadOwns();
775 FFEArguments args = { aInstance, false };
776 (void)mFunctions.EnumerateRead(findFunctionEnumerator, &args);
777 return args.found;
778 }
780 /* static */ int
781 Connection::sProgressHelper(void *aArg)
782 {
783 Connection *_this = static_cast<Connection *>(aArg);
784 return _this->progressHandler();
785 }
787 int
788 Connection::progressHandler()
789 {
790 sharedDBMutex.assertCurrentThreadOwns();
791 if (mProgressHandler) {
792 bool result;
793 nsresult rv = mProgressHandler->OnProgress(this, &result);
794 if (NS_FAILED(rv)) return 0; // Don't break request
795 return result ? 1 : 0;
796 }
797 return 0;
798 }
800 nsresult
801 Connection::setClosedState()
802 {
803 // Ensure that we are on the correct thread to close the database.
804 bool onOpenedThread;
805 nsresult rv = threadOpenedOn->IsOnCurrentThread(&onOpenedThread);
806 NS_ENSURE_SUCCESS(rv, rv);
807 if (!onOpenedThread) {
808 NS_ERROR("Must close the database on the thread that you opened it with!");
809 return NS_ERROR_UNEXPECTED;
810 }
812 // Flag that we are shutting down the async thread, so that
813 // getAsyncExecutionTarget knows not to expose/create the async thread.
814 {
815 MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
816 NS_ENSURE_FALSE(mAsyncExecutionThreadShuttingDown, NS_ERROR_UNEXPECTED);
817 mAsyncExecutionThreadShuttingDown = true;
818 }
820 // Set the property to null before closing the connection, otherwise the other
821 // functions in the module may try to use the connection after it is closed.
822 mDBConn = nullptr;
824 return NS_OK;
825 }
827 bool
828 Connection::connectionReady()
829 {
830 return mDBConn != nullptr;
831 }
833 bool
834 Connection::isClosing()
835 {
836 bool shuttingDown = false;
837 {
838 MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
839 shuttingDown = mAsyncExecutionThreadShuttingDown;
840 }
841 return shuttingDown && !isClosed();
842 }
844 bool
845 Connection::isClosed()
846 {
847 MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
848 return mConnectionClosed;
849 }
851 nsresult
852 Connection::internalClose(sqlite3 *aNativeConnection)
853 {
854 // Sanity checks to make sure we are in the proper state before calling this.
855 MOZ_ASSERT(aNativeConnection, "Database connection is invalid!");
856 MOZ_ASSERT(!isClosed());
858 #ifdef DEBUG
859 { // Make sure we have marked our async thread as shutting down.
860 MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
861 NS_ASSERTION(mAsyncExecutionThreadShuttingDown,
862 "Did not call setClosedState!");
863 }
864 #endif // DEBUG
866 #ifdef PR_LOGGING
867 nsAutoCString leafName(":memory");
868 if (mDatabaseFile)
869 (void)mDatabaseFile->GetNativeLeafName(leafName);
870 PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Closing connection to '%s'",
871 leafName.get()));
872 #endif
874 // At this stage, we may still have statements that need to be
875 // finalized. Attempt to close the database connection. This will
876 // always disconnect any virtual tables and cleanly finalize their
877 // internal statements. Once this is done, closing may fail due to
878 // unfinalized client statements, in which case we need to finalize
879 // these statements and close again.
880 {
881 MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
882 mConnectionClosed = true;
883 }
884 int srv = sqlite3_close(aNativeConnection);
886 if (srv == SQLITE_BUSY) {
887 // We still have non-finalized statements. Finalize them.
889 sqlite3_stmt *stmt = nullptr;
890 while ((stmt = ::sqlite3_next_stmt(aNativeConnection, stmt))) {
891 PR_LOG(gStorageLog, PR_LOG_NOTICE,
892 ("Auto-finalizing SQL statement '%s' (%x)",
893 ::sqlite3_sql(stmt),
894 stmt));
896 #ifdef DEBUG
897 char *msg = ::PR_smprintf("SQL statement '%s' (%x) should have been finalized before closing the connection",
898 ::sqlite3_sql(stmt),
899 stmt);
900 NS_WARNING(msg);
901 ::PR_smprintf_free(msg);
902 #endif // DEBUG
904 srv = ::sqlite3_finalize(stmt);
906 #ifdef DEBUG
907 if (srv != SQLITE_OK) {
908 char *msg = ::PR_smprintf("Could not finalize SQL statement '%s' (%x)",
909 ::sqlite3_sql(stmt),
910 stmt);
911 NS_WARNING(msg);
912 ::PR_smprintf_free(msg);
913 }
914 #endif // DEBUG
916 // Ensure that the loop continues properly, whether closing has succeeded
917 // or not.
918 if (srv == SQLITE_OK) {
919 stmt = nullptr;
920 }
921 }
923 // Now that all statements have been finalized, we
924 // should be able to close.
925 srv = ::sqlite3_close(aNativeConnection);
927 }
929 if (srv != SQLITE_OK) {
930 MOZ_ASSERT(srv == SQLITE_OK,
931 "sqlite3_close failed. There are probably outstanding statements that are listed above!");
932 }
934 return convertResultCode(srv);
935 }
937 nsCString
938 Connection::getFilename()
939 {
940 nsCString leafname(":memory:");
941 if (mDatabaseFile) {
942 (void)mDatabaseFile->GetNativeLeafName(leafname);
943 }
944 return leafname;
945 }
947 int
948 Connection::stepStatement(sqlite3 *aNativeConnection, sqlite3_stmt *aStatement)
949 {
950 MOZ_ASSERT(aStatement);
951 bool checkedMainThread = false;
952 TimeStamp startTime = TimeStamp::Now();
954 // The connection may have been closed if the executing statement has been
955 // created and cached after a call to asyncClose() but before the actual
956 // sqlite3_close(). This usually happens when other tasks using cached
957 // statements are asynchronously scheduled for execution and any of them ends
958 // up after asyncClose. See bug 728653 for details.
959 if (isClosed())
960 return SQLITE_MISUSE;
962 (void)::sqlite3_extended_result_codes(aNativeConnection, 1);
964 int srv;
965 while ((srv = ::sqlite3_step(aStatement)) == SQLITE_LOCKED_SHAREDCACHE) {
966 if (!checkedMainThread) {
967 checkedMainThread = true;
968 if (::NS_IsMainThread()) {
969 NS_WARNING("We won't allow blocking on the main thread!");
970 break;
971 }
972 }
974 srv = WaitForUnlockNotify(aNativeConnection);
975 if (srv != SQLITE_OK) {
976 break;
977 }
979 ::sqlite3_reset(aStatement);
980 }
982 // Report very slow SQL statements to Telemetry
983 TimeDuration duration = TimeStamp::Now() - startTime;
984 const uint32_t threshold =
985 NS_IsMainThread() ? Telemetry::kSlowSQLThresholdForMainThread
986 : Telemetry::kSlowSQLThresholdForHelperThreads;
987 if (duration.ToMilliseconds() >= threshold) {
988 nsDependentCString statementString(::sqlite3_sql(aStatement));
989 Telemetry::RecordSlowSQLStatement(statementString, getFilename(),
990 duration.ToMilliseconds());
991 }
993 (void)::sqlite3_extended_result_codes(aNativeConnection, 0);
994 // Drop off the extended result bits of the result code.
995 return srv & 0xFF;
996 }
998 int
999 Connection::prepareStatement(sqlite3 *aNativeConnection, const nsCString &aSQL,
1000 sqlite3_stmt **_stmt)
1001 {
1002 // We should not even try to prepare statements after the connection has
1003 // been closed.
1004 if (isClosed())
1005 return SQLITE_MISUSE;
1007 bool checkedMainThread = false;
1009 (void)::sqlite3_extended_result_codes(aNativeConnection, 1);
1011 int srv;
1012 while((srv = ::sqlite3_prepare_v2(aNativeConnection,
1013 aSQL.get(),
1014 -1,
1015 _stmt,
1016 nullptr)) == SQLITE_LOCKED_SHAREDCACHE) {
1017 if (!checkedMainThread) {
1018 checkedMainThread = true;
1019 if (::NS_IsMainThread()) {
1020 NS_WARNING("We won't allow blocking on the main thread!");
1021 break;
1022 }
1023 }
1025 srv = WaitForUnlockNotify(aNativeConnection);
1026 if (srv != SQLITE_OK) {
1027 break;
1028 }
1029 }
1031 if (srv != SQLITE_OK) {
1032 nsCString warnMsg;
1033 warnMsg.AppendLiteral("The SQL statement '");
1034 warnMsg.Append(aSQL);
1035 warnMsg.AppendLiteral("' could not be compiled due to an error: ");
1036 warnMsg.Append(::sqlite3_errmsg(aNativeConnection));
1038 #ifdef DEBUG
1039 NS_WARNING(warnMsg.get());
1040 #endif
1041 PR_LOG(gStorageLog, PR_LOG_ERROR, ("%s", warnMsg.get()));
1042 }
1044 (void)::sqlite3_extended_result_codes(aNativeConnection, 0);
1045 // Drop off the extended result bits of the result code.
1046 int rc = srv & 0xFF;
1047 // sqlite will return OK on a comment only string and set _stmt to nullptr.
1048 // The callers of this function are used to only checking the return value,
1049 // so it is safer to return an error code.
1050 if (rc == SQLITE_OK && *_stmt == nullptr) {
1051 return SQLITE_MISUSE;
1052 }
1054 return rc;
1055 }
1058 int
1059 Connection::executeSql(sqlite3 *aNativeConnection, const char *aSqlString)
1060 {
1061 if (isClosed())
1062 return SQLITE_MISUSE;
1064 TimeStamp startTime = TimeStamp::Now();
1065 int srv = ::sqlite3_exec(aNativeConnection, aSqlString, nullptr, nullptr,
1066 nullptr);
1068 // Report very slow SQL statements to Telemetry
1069 TimeDuration duration = TimeStamp::Now() - startTime;
1070 const uint32_t threshold =
1071 NS_IsMainThread() ? Telemetry::kSlowSQLThresholdForMainThread
1072 : Telemetry::kSlowSQLThresholdForHelperThreads;
1073 if (duration.ToMilliseconds() >= threshold) {
1074 nsDependentCString statementString(aSqlString);
1075 Telemetry::RecordSlowSQLStatement(statementString, getFilename(),
1076 duration.ToMilliseconds());
1077 }
1079 return srv;
1080 }
1082 ////////////////////////////////////////////////////////////////////////////////
1083 //// nsIInterfaceRequestor
1085 NS_IMETHODIMP
1086 Connection::GetInterface(const nsIID &aIID,
1087 void **_result)
1088 {
1089 if (aIID.Equals(NS_GET_IID(nsIEventTarget))) {
1090 nsIEventTarget *background = getAsyncExecutionTarget();
1091 NS_IF_ADDREF(background);
1092 *_result = background;
1093 return NS_OK;
1094 }
1095 return NS_ERROR_NO_INTERFACE;
1096 }
1098 ////////////////////////////////////////////////////////////////////////////////
1099 //// mozIStorageConnection
1101 NS_IMETHODIMP
1102 Connection::Close()
1103 {
1104 if (!mDBConn)
1105 return NS_ERROR_NOT_INITIALIZED;
1107 { // Make sure we have not executed any asynchronous statements.
1108 // If this fails, the mDBConn will be left open, resulting in a leak.
1109 // Ideally we'd schedule some code to destroy the mDBConn once all its
1110 // async statements have finished executing; see bug 704030.
1111 MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
1112 bool asyncCloseWasCalled = !mAsyncExecutionThread;
1113 NS_ENSURE_TRUE(asyncCloseWasCalled, NS_ERROR_UNEXPECTED);
1114 }
1116 // setClosedState nullifies our connection pointer, so we take a raw pointer
1117 // off it, to pass it through the close procedure.
1118 sqlite3 *nativeConn = mDBConn;
1119 nsresult rv = setClosedState();
1120 NS_ENSURE_SUCCESS(rv, rv);
1122 return internalClose(nativeConn);
1123 }
1125 NS_IMETHODIMP
1126 Connection::AsyncClose(mozIStorageCompletionCallback *aCallback)
1127 {
1128 if (!NS_IsMainThread()) {
1129 return NS_ERROR_NOT_SAME_THREAD;
1130 }
1131 if (!mDBConn)
1132 return NS_ERROR_NOT_INITIALIZED;
1134 nsIEventTarget *asyncThread = getAsyncExecutionTarget();
1135 NS_ENSURE_TRUE(asyncThread, NS_ERROR_NOT_INITIALIZED);
1137 // setClosedState nullifies our connection pointer, so we take a raw pointer
1138 // off it, to pass it through the close procedure.
1139 sqlite3 *nativeConn = mDBConn;
1140 nsresult rv = setClosedState();
1141 NS_ENSURE_SUCCESS(rv, rv);
1143 // Create our callback event if we were given a callback.
1144 nsCOMPtr<nsIRunnable> completeEvent;
1145 if (aCallback) {
1146 completeEvent = newCompletionEvent(aCallback);
1147 }
1149 // Create and dispatch our close event to the background thread.
1150 nsCOMPtr<nsIRunnable> closeEvent;
1151 {
1152 // We need to lock because we're modifying mAsyncExecutionThread
1153 MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
1154 closeEvent = new AsyncCloseConnection(this,
1155 nativeConn,
1156 completeEvent,
1157 mAsyncExecutionThread.forget());
1158 }
1160 rv = asyncThread->Dispatch(closeEvent, NS_DISPATCH_NORMAL);
1161 NS_ENSURE_SUCCESS(rv, rv);
1163 return NS_OK;
1164 }
1166 NS_IMETHODIMP
1167 Connection::AsyncClone(bool aReadOnly,
1168 mozIStorageCompletionCallback *aCallback)
1169 {
1170 PROFILER_LABEL("storage", "Connection::Clone");
1171 if (!NS_IsMainThread()) {
1172 return NS_ERROR_NOT_SAME_THREAD;
1173 }
1174 if (!mDBConn)
1175 return NS_ERROR_NOT_INITIALIZED;
1176 if (!mDatabaseFile)
1177 return NS_ERROR_UNEXPECTED;
1179 int flags = mFlags;
1180 if (aReadOnly) {
1181 // Turn off SQLITE_OPEN_READWRITE, and set SQLITE_OPEN_READONLY.
1182 flags = (~SQLITE_OPEN_READWRITE & flags) | SQLITE_OPEN_READONLY;
1183 // Turn off SQLITE_OPEN_CREATE.
1184 flags = (~SQLITE_OPEN_CREATE & flags);
1185 }
1187 nsRefPtr<Connection> clone = new Connection(mStorageService, flags,
1188 mAsyncOnly);
1190 nsRefPtr<AsyncInitializeClone> initEvent =
1191 new AsyncInitializeClone(this, clone, aReadOnly, aCallback);
1192 nsCOMPtr<nsIEventTarget> target = clone->getAsyncExecutionTarget();
1193 if (!target) {
1194 return NS_ERROR_UNEXPECTED;
1195 }
1196 return target->Dispatch(initEvent, NS_DISPATCH_NORMAL);
1197 }
1199 nsresult
1200 Connection::initializeClone(Connection* aClone, bool aReadOnly)
1201 {
1202 nsresult rv = mFileURL ? aClone->initialize(mFileURL)
1203 : aClone->initialize(mDatabaseFile);
1204 if (NS_FAILED(rv)) {
1205 return rv;
1206 }
1208 // Copy over pragmas from the original connection.
1209 static const char * pragmas[] = {
1210 "cache_size",
1211 "temp_store",
1212 "foreign_keys",
1213 "journal_size_limit",
1214 "synchronous",
1215 "wal_autocheckpoint",
1216 };
1217 for (uint32_t i = 0; i < ArrayLength(pragmas); ++i) {
1218 // Read-only connections just need cache_size and temp_store pragmas.
1219 if (aReadOnly && ::strcmp(pragmas[i], "cache_size") != 0 &&
1220 ::strcmp(pragmas[i], "temp_store") != 0) {
1221 continue;
1222 }
1224 nsAutoCString pragmaQuery("PRAGMA ");
1225 pragmaQuery.Append(pragmas[i]);
1226 nsCOMPtr<mozIStorageStatement> stmt;
1227 rv = CreateStatement(pragmaQuery, getter_AddRefs(stmt));
1228 MOZ_ASSERT(NS_SUCCEEDED(rv));
1229 bool hasResult = false;
1230 if (stmt && NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
1231 pragmaQuery.AppendLiteral(" = ");
1232 pragmaQuery.AppendInt(stmt->AsInt32(0));
1233 rv = aClone->ExecuteSimpleSQL(pragmaQuery);
1234 MOZ_ASSERT(NS_SUCCEEDED(rv));
1235 }
1236 }
1238 // Copy any functions that have been added to this connection.
1239 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1240 (void)mFunctions.EnumerateRead(copyFunctionEnumerator, aClone);
1242 return NS_OK;
1243 }
1245 NS_IMETHODIMP
1246 Connection::Clone(bool aReadOnly,
1247 mozIStorageConnection **_connection)
1248 {
1249 MOZ_ASSERT(threadOpenedOn == NS_GetCurrentThread());
1251 PROFILER_LABEL("storage", "Connection::Clone");
1252 if (!mDBConn)
1253 return NS_ERROR_NOT_INITIALIZED;
1254 if (!mDatabaseFile)
1255 return NS_ERROR_UNEXPECTED;
1257 int flags = mFlags;
1258 if (aReadOnly) {
1259 // Turn off SQLITE_OPEN_READWRITE, and set SQLITE_OPEN_READONLY.
1260 flags = (~SQLITE_OPEN_READWRITE & flags) | SQLITE_OPEN_READONLY;
1261 // Turn off SQLITE_OPEN_CREATE.
1262 flags = (~SQLITE_OPEN_CREATE & flags);
1263 }
1265 nsRefPtr<Connection> clone = new Connection(mStorageService, flags,
1266 mAsyncOnly);
1268 nsresult rv = initializeClone(clone, aReadOnly);
1269 if (NS_FAILED(rv)) {
1270 return rv;
1271 }
1273 NS_IF_ADDREF(*_connection = clone);
1274 return NS_OK;
1275 }
1277 NS_IMETHODIMP
1278 Connection::GetDefaultPageSize(int32_t *_defaultPageSize)
1279 {
1280 *_defaultPageSize = Service::getDefaultPageSize();
1281 return NS_OK;
1282 }
1284 NS_IMETHODIMP
1285 Connection::GetConnectionReady(bool *_ready)
1286 {
1287 *_ready = connectionReady();
1288 return NS_OK;
1289 }
1291 NS_IMETHODIMP
1292 Connection::GetDatabaseFile(nsIFile **_dbFile)
1293 {
1294 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1296 NS_IF_ADDREF(*_dbFile = mDatabaseFile);
1298 return NS_OK;
1299 }
1301 NS_IMETHODIMP
1302 Connection::GetLastInsertRowID(int64_t *_id)
1303 {
1304 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1306 sqlite_int64 id = ::sqlite3_last_insert_rowid(mDBConn);
1307 *_id = id;
1309 return NS_OK;
1310 }
1312 NS_IMETHODIMP
1313 Connection::GetAffectedRows(int32_t *_rows)
1314 {
1315 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1317 *_rows = ::sqlite3_changes(mDBConn);
1319 return NS_OK;
1320 }
1322 NS_IMETHODIMP
1323 Connection::GetLastError(int32_t *_error)
1324 {
1325 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1327 *_error = ::sqlite3_errcode(mDBConn);
1329 return NS_OK;
1330 }
1332 NS_IMETHODIMP
1333 Connection::GetLastErrorString(nsACString &_errorString)
1334 {
1335 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1337 const char *serr = ::sqlite3_errmsg(mDBConn);
1338 _errorString.Assign(serr);
1340 return NS_OK;
1341 }
1343 NS_IMETHODIMP
1344 Connection::GetSchemaVersion(int32_t *_version)
1345 {
1346 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1348 nsCOMPtr<mozIStorageStatement> stmt;
1349 (void)CreateStatement(NS_LITERAL_CSTRING("PRAGMA user_version"),
1350 getter_AddRefs(stmt));
1351 NS_ENSURE_TRUE(stmt, NS_ERROR_OUT_OF_MEMORY);
1353 *_version = 0;
1354 bool hasResult;
1355 if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult)
1356 *_version = stmt->AsInt32(0);
1358 return NS_OK;
1359 }
1361 NS_IMETHODIMP
1362 Connection::SetSchemaVersion(int32_t aVersion)
1363 {
1364 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1366 nsAutoCString stmt(NS_LITERAL_CSTRING("PRAGMA user_version = "));
1367 stmt.AppendInt(aVersion);
1369 return ExecuteSimpleSQL(stmt);
1370 }
1372 NS_IMETHODIMP
1373 Connection::CreateStatement(const nsACString &aSQLStatement,
1374 mozIStorageStatement **_stmt)
1375 {
1376 NS_ENSURE_ARG_POINTER(_stmt);
1377 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1379 nsRefPtr<Statement> statement(new Statement());
1380 NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
1382 nsresult rv = statement->initialize(this, mDBConn, aSQLStatement);
1383 NS_ENSURE_SUCCESS(rv, rv);
1385 Statement *rawPtr;
1386 statement.forget(&rawPtr);
1387 *_stmt = rawPtr;
1388 return NS_OK;
1389 }
1391 NS_IMETHODIMP
1392 Connection::CreateAsyncStatement(const nsACString &aSQLStatement,
1393 mozIStorageAsyncStatement **_stmt)
1394 {
1395 NS_ENSURE_ARG_POINTER(_stmt);
1396 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1398 nsRefPtr<AsyncStatement> statement(new AsyncStatement());
1399 NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
1401 nsresult rv = statement->initialize(this, mDBConn, aSQLStatement);
1402 NS_ENSURE_SUCCESS(rv, rv);
1404 AsyncStatement *rawPtr;
1405 statement.forget(&rawPtr);
1406 *_stmt = rawPtr;
1407 return NS_OK;
1408 }
1410 NS_IMETHODIMP
1411 Connection::ExecuteSimpleSQL(const nsACString &aSQLStatement)
1412 {
1413 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1415 int srv = executeSql(mDBConn, PromiseFlatCString(aSQLStatement).get());
1416 return convertResultCode(srv);
1417 }
1419 NS_IMETHODIMP
1420 Connection::ExecuteAsync(mozIStorageBaseStatement **aStatements,
1421 uint32_t aNumStatements,
1422 mozIStorageStatementCallback *aCallback,
1423 mozIStoragePendingStatement **_handle)
1424 {
1425 nsTArray<StatementData> stmts(aNumStatements);
1426 for (uint32_t i = 0; i < aNumStatements; i++) {
1427 nsCOMPtr<StorageBaseStatementInternal> stmt =
1428 do_QueryInterface(aStatements[i]);
1430 // Obtain our StatementData.
1431 StatementData data;
1432 nsresult rv = stmt->getAsynchronousStatementData(data);
1433 NS_ENSURE_SUCCESS(rv, rv);
1435 NS_ASSERTION(stmt->getOwner() == this,
1436 "Statement must be from this database connection!");
1438 // Now append it to our array.
1439 NS_ENSURE_TRUE(stmts.AppendElement(data), NS_ERROR_OUT_OF_MEMORY);
1440 }
1442 // Dispatch to the background
1443 return AsyncExecuteStatements::execute(stmts, this, mDBConn, aCallback,
1444 _handle);
1445 }
1447 NS_IMETHODIMP
1448 Connection::ExecuteSimpleSQLAsync(const nsACString &aSQLStatement,
1449 mozIStorageStatementCallback *aCallback,
1450 mozIStoragePendingStatement **_handle)
1451 {
1452 if (!NS_IsMainThread()) {
1453 return NS_ERROR_NOT_SAME_THREAD;
1454 }
1456 nsCOMPtr<mozIStorageAsyncStatement> stmt;
1457 nsresult rv = CreateAsyncStatement(aSQLStatement, getter_AddRefs(stmt));
1458 if (NS_FAILED(rv)) {
1459 return rv;
1460 }
1462 nsCOMPtr<mozIStoragePendingStatement> pendingStatement;
1463 rv = stmt->ExecuteAsync(aCallback, getter_AddRefs(pendingStatement));
1464 if (NS_FAILED(rv)) {
1465 return rv;
1466 }
1468 NS_ADDREF(*_handle = pendingStatement);
1469 return rv;
1470 }
1472 NS_IMETHODIMP
1473 Connection::TableExists(const nsACString &aTableName,
1474 bool *_exists)
1475 {
1476 return databaseElementExists(TABLE, aTableName, _exists);
1477 }
1479 NS_IMETHODIMP
1480 Connection::IndexExists(const nsACString &aIndexName,
1481 bool* _exists)
1482 {
1483 return databaseElementExists(INDEX, aIndexName, _exists);
1484 }
1486 NS_IMETHODIMP
1487 Connection::GetTransactionInProgress(bool *_inProgress)
1488 {
1489 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1491 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1492 *_inProgress = mTransactionInProgress;
1493 return NS_OK;
1494 }
1496 NS_IMETHODIMP
1497 Connection::BeginTransaction()
1498 {
1499 return BeginTransactionAs(mozIStorageConnection::TRANSACTION_DEFERRED);
1500 }
1502 NS_IMETHODIMP
1503 Connection::BeginTransactionAs(int32_t aTransactionType)
1504 {
1505 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1507 return beginTransactionInternal(mDBConn, aTransactionType);
1508 }
1510 nsresult
1511 Connection::beginTransactionInternal(sqlite3 *aNativeConnection,
1512 int32_t aTransactionType)
1513 {
1514 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1515 if (mTransactionInProgress)
1516 return NS_ERROR_FAILURE;
1517 nsresult rv;
1518 switch(aTransactionType) {
1519 case TRANSACTION_DEFERRED:
1520 rv = convertResultCode(executeSql(aNativeConnection, "BEGIN DEFERRED"));
1521 break;
1522 case TRANSACTION_IMMEDIATE:
1523 rv = convertResultCode(executeSql(aNativeConnection, "BEGIN IMMEDIATE"));
1524 break;
1525 case TRANSACTION_EXCLUSIVE:
1526 rv = convertResultCode(executeSql(aNativeConnection, "BEGIN EXCLUSIVE"));
1527 break;
1528 default:
1529 return NS_ERROR_ILLEGAL_VALUE;
1530 }
1531 if (NS_SUCCEEDED(rv))
1532 mTransactionInProgress = true;
1533 return rv;
1534 }
1536 NS_IMETHODIMP
1537 Connection::CommitTransaction()
1538 {
1539 if (!mDBConn)
1540 return NS_ERROR_NOT_INITIALIZED;
1542 return commitTransactionInternal(mDBConn);
1543 }
1545 nsresult
1546 Connection::commitTransactionInternal(sqlite3 *aNativeConnection)
1547 {
1548 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1549 if (!mTransactionInProgress)
1550 return NS_ERROR_UNEXPECTED;
1551 nsresult rv =
1552 convertResultCode(executeSql(aNativeConnection, "COMMIT TRANSACTION"));
1553 if (NS_SUCCEEDED(rv))
1554 mTransactionInProgress = false;
1555 return rv;
1556 }
1558 NS_IMETHODIMP
1559 Connection::RollbackTransaction()
1560 {
1561 if (!mDBConn)
1562 return NS_ERROR_NOT_INITIALIZED;
1564 return rollbackTransactionInternal(mDBConn);
1565 }
1567 nsresult
1568 Connection::rollbackTransactionInternal(sqlite3 *aNativeConnection)
1569 {
1570 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1571 if (!mTransactionInProgress)
1572 return NS_ERROR_UNEXPECTED;
1574 nsresult rv =
1575 convertResultCode(executeSql(aNativeConnection, "ROLLBACK TRANSACTION"));
1576 if (NS_SUCCEEDED(rv))
1577 mTransactionInProgress = false;
1578 return rv;
1579 }
1581 NS_IMETHODIMP
1582 Connection::CreateTable(const char *aTableName,
1583 const char *aTableSchema)
1584 {
1585 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1587 char *buf = ::PR_smprintf("CREATE TABLE %s (%s)", aTableName, aTableSchema);
1588 if (!buf)
1589 return NS_ERROR_OUT_OF_MEMORY;
1591 int srv = executeSql(mDBConn, buf);
1592 ::PR_smprintf_free(buf);
1594 return convertResultCode(srv);
1595 }
1597 NS_IMETHODIMP
1598 Connection::CreateFunction(const nsACString &aFunctionName,
1599 int32_t aNumArguments,
1600 mozIStorageFunction *aFunction)
1601 {
1602 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1604 // Check to see if this function is already defined. We only check the name
1605 // because a function can be defined with the same body but different names.
1606 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1607 NS_ENSURE_FALSE(mFunctions.Get(aFunctionName, nullptr), NS_ERROR_FAILURE);
1609 int srv = ::sqlite3_create_function(mDBConn,
1610 nsPromiseFlatCString(aFunctionName).get(),
1611 aNumArguments,
1612 SQLITE_ANY,
1613 aFunction,
1614 basicFunctionHelper,
1615 nullptr,
1616 nullptr);
1617 if (srv != SQLITE_OK)
1618 return convertResultCode(srv);
1620 FunctionInfo info = { aFunction,
1621 Connection::FunctionInfo::SIMPLE,
1622 aNumArguments };
1623 mFunctions.Put(aFunctionName, info);
1625 return NS_OK;
1626 }
1628 NS_IMETHODIMP
1629 Connection::CreateAggregateFunction(const nsACString &aFunctionName,
1630 int32_t aNumArguments,
1631 mozIStorageAggregateFunction *aFunction)
1632 {
1633 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1635 // Check to see if this function name is already defined.
1636 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1637 NS_ENSURE_FALSE(mFunctions.Get(aFunctionName, nullptr), NS_ERROR_FAILURE);
1639 // Because aggregate functions depend on state across calls, you cannot have
1640 // the same instance use the same name. We want to enumerate all functions
1641 // and make sure this instance is not already registered.
1642 NS_ENSURE_FALSE(findFunctionByInstance(aFunction), NS_ERROR_FAILURE);
1644 int srv = ::sqlite3_create_function(mDBConn,
1645 nsPromiseFlatCString(aFunctionName).get(),
1646 aNumArguments,
1647 SQLITE_ANY,
1648 aFunction,
1649 nullptr,
1650 aggregateFunctionStepHelper,
1651 aggregateFunctionFinalHelper);
1652 if (srv != SQLITE_OK)
1653 return convertResultCode(srv);
1655 FunctionInfo info = { aFunction,
1656 Connection::FunctionInfo::AGGREGATE,
1657 aNumArguments };
1658 mFunctions.Put(aFunctionName, info);
1660 return NS_OK;
1661 }
1663 NS_IMETHODIMP
1664 Connection::RemoveFunction(const nsACString &aFunctionName)
1665 {
1666 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1668 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1669 NS_ENSURE_TRUE(mFunctions.Get(aFunctionName, nullptr), NS_ERROR_FAILURE);
1671 int srv = ::sqlite3_create_function(mDBConn,
1672 nsPromiseFlatCString(aFunctionName).get(),
1673 0,
1674 SQLITE_ANY,
1675 nullptr,
1676 nullptr,
1677 nullptr,
1678 nullptr);
1679 if (srv != SQLITE_OK)
1680 return convertResultCode(srv);
1682 mFunctions.Remove(aFunctionName);
1684 return NS_OK;
1685 }
1687 NS_IMETHODIMP
1688 Connection::SetProgressHandler(int32_t aGranularity,
1689 mozIStorageProgressHandler *aHandler,
1690 mozIStorageProgressHandler **_oldHandler)
1691 {
1692 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1694 // Return previous one
1695 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1696 NS_IF_ADDREF(*_oldHandler = mProgressHandler);
1698 if (!aHandler || aGranularity <= 0) {
1699 aHandler = nullptr;
1700 aGranularity = 0;
1701 }
1702 mProgressHandler = aHandler;
1703 ::sqlite3_progress_handler(mDBConn, aGranularity, sProgressHelper, this);
1705 return NS_OK;
1706 }
1708 NS_IMETHODIMP
1709 Connection::RemoveProgressHandler(mozIStorageProgressHandler **_oldHandler)
1710 {
1711 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1713 // Return previous one
1714 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1715 NS_IF_ADDREF(*_oldHandler = mProgressHandler);
1717 mProgressHandler = nullptr;
1718 ::sqlite3_progress_handler(mDBConn, 0, nullptr, nullptr);
1720 return NS_OK;
1721 }
1723 NS_IMETHODIMP
1724 Connection::SetGrowthIncrement(int32_t aChunkSize, const nsACString &aDatabaseName)
1725 {
1726 // Bug 597215: Disk space is extremely limited on Android
1727 // so don't preallocate space. This is also not effective
1728 // on log structured file systems used by Android devices
1729 #if !defined(ANDROID) && !defined(MOZ_PLATFORM_MAEMO)
1730 // Don't preallocate if less than 500MiB is available.
1731 int64_t bytesAvailable;
1732 nsresult rv = mDatabaseFile->GetDiskSpaceAvailable(&bytesAvailable);
1733 NS_ENSURE_SUCCESS(rv, rv);
1734 if (bytesAvailable < MIN_AVAILABLE_BYTES_PER_CHUNKED_GROWTH) {
1735 return NS_ERROR_FILE_TOO_BIG;
1736 }
1738 (void)::sqlite3_file_control(mDBConn,
1739 aDatabaseName.Length() ? nsPromiseFlatCString(aDatabaseName).get()
1740 : nullptr,
1741 SQLITE_FCNTL_CHUNK_SIZE,
1742 &aChunkSize);
1743 #endif
1744 return NS_OK;
1745 }
1747 NS_IMETHODIMP
1748 Connection::EnableModule(const nsACString& aModuleName)
1749 {
1750 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1752 for (size_t i = 0; i < ArrayLength(gModules); i++) {
1753 struct Module* m = &gModules[i];
1754 if (aModuleName.Equals(m->name)) {
1755 int srv = m->registerFunc(mDBConn, m->name);
1756 if (srv != SQLITE_OK)
1757 return convertResultCode(srv);
1759 return NS_OK;
1760 }
1761 }
1763 return NS_ERROR_FAILURE;
1764 }
1766 } // namespace storage
1767 } // namespace mozilla