Backout 2ea2669b53c3, Bug 917642 - [Helix] Please update the helix blobs
[gecko.git] / storage / src / mozStorageConnection.cpp
blob0711cc930de10e5b9b5f2742c134c472e0922d29
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)
66 ::sqlite3_result_int(aCtx, aValue);
67 return SQLITE_OK;
70 int
71 sqlite3_T_int64(sqlite3_context *aCtx,
72 sqlite3_int64 aValue)
74 ::sqlite3_result_int64(aCtx, aValue);
75 return SQLITE_OK;
78 int
79 sqlite3_T_double(sqlite3_context *aCtx,
80 double aValue)
82 ::sqlite3_result_double(aCtx, aValue);
83 return SQLITE_OK;
86 int
87 sqlite3_T_text(sqlite3_context *aCtx,
88 const nsCString &aValue)
90 ::sqlite3_result_text(aCtx,
91 aValue.get(),
92 aValue.Length(),
93 SQLITE_TRANSIENT);
94 return SQLITE_OK;
97 int
98 sqlite3_T_text16(sqlite3_context *aCtx,
99 const nsString &aValue)
101 ::sqlite3_result_text16(aCtx,
102 aValue.get(),
103 aValue.Length() * 2, // Number of bytes.
104 SQLITE_TRANSIENT);
105 return SQLITE_OK;
109 sqlite3_T_null(sqlite3_context *aCtx)
111 ::sqlite3_result_null(aCtx);
112 return SQLITE_OK;
116 sqlite3_T_blob(sqlite3_context *aCtx,
117 const void *aData,
118 int aSize)
120 ::sqlite3_result_blob(aCtx, aData, aSize, NS_Free);
121 return SQLITE_OK;
124 #include "variantToSQLiteT_impl.h"
126 ////////////////////////////////////////////////////////////////////////////////
127 //// Modules
129 struct Module
131 const char* name;
132 int (*registerFunc)(sqlite3*, const char*);
135 Module gModules[] = {
136 { "filesystem", RegisterFileSystemModule }
139 ////////////////////////////////////////////////////////////////////////////////
140 //// Local Functions
142 #ifdef PR_LOGGING
143 void tracefunc (void *aClosure, const char *aStmt)
145 PR_LOG(gStorageLog, PR_LOG_DEBUG, ("sqlite3_trace on %p for '%s'", aClosure,
146 aStmt));
148 #endif
150 struct FFEArguments
152 nsISupports *target;
153 bool found;
155 PLDHashOperator
156 findFunctionEnumerator(const nsACString &aKey,
157 Connection::FunctionInfo aData,
158 void *aUserArg)
160 FFEArguments *args = static_cast<FFEArguments *>(aUserArg);
161 if (aData.function == args->target) {
162 args->found = true;
163 return PL_DHASH_STOP;
165 return PL_DHASH_NEXT;
168 PLDHashOperator
169 copyFunctionEnumerator(const nsACString &aKey,
170 Connection::FunctionInfo aData,
171 void *aUserArg)
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);
183 else {
184 mozIStorageAggregateFunction *function =
185 static_cast<mozIStorageAggregateFunction *>(aData.function.get());
186 (void)connection->CreateAggregateFunction(aKey, aData.numArgs, function);
189 return PL_DHASH_NEXT;
192 void
193 basicFunctionHelper(sqlite3_context *aCtx,
194 int aArgc,
195 sqlite3_value **aArgv)
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;
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);
224 void
225 aggregateFunctionStepHelper(sqlite3_context *aCtx,
226 int aArgc,
227 sqlite3_value **aArgv)
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!");
241 void
242 aggregateFunctionFinalHelper(sqlite3_context *aCtx)
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;
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);
266 * This code is heavily based on the sample at:
267 * http://www.sqlite.org/unlock_notify.html
269 class UnlockNotification
271 public:
272 UnlockNotification()
273 : mMutex("UnlockNotification mMutex")
274 , mCondVar(mMutex, "UnlockNotification condVar")
275 , mSignaled(false)
279 void Wait()
281 MutexAutoLock lock(mMutex);
282 while (!mSignaled) {
283 (void)mCondVar.Wait();
287 void Signal()
289 MutexAutoLock lock(mMutex);
290 mSignaled = true;
291 (void)mCondVar.Notify();
294 private:
295 Mutex mMutex;
296 CondVar mCondVar;
297 bool mSignaled;
300 void
301 UnlockNotifyCallback(void **aArgs,
302 int aArgsSize)
304 for (int i = 0; i < aArgsSize; i++) {
305 UnlockNotification *notification =
306 static_cast<UnlockNotification *>(aArgs[i]);
307 notification->Signal();
312 WaitForUnlockNotify(sqlite3* aDatabase)
314 UnlockNotification notification;
315 int srv = ::sqlite3_unlock_notify(aDatabase, UnlockNotifyCallback,
316 &notification);
317 MOZ_ASSERT(srv == SQLITE_LOCKED || srv == SQLITE_OK);
318 if (srv == SQLITE_OK) {
319 notification.Wait();
322 return srv;
325 } // anonymous namespace
327 ////////////////////////////////////////////////////////////////////////////////
328 //// Local Classes
330 namespace {
332 class AsyncCloseConnection MOZ_FINAL: public nsRunnable
334 public:
335 AsyncCloseConnection(Connection *aConnection,
336 nsIRunnable *aCallbackEvent,
337 already_AddRefed<nsIThread> aAsyncExecutionThread)
338 : mConnection(aConnection)
339 , mCallbackEvent(aCallbackEvent)
340 , mAsyncExecutionThread(aAsyncExecutionThread)
344 NS_METHOD Run()
346 #ifdef DEBUG
347 // This code is executed on the background thread
348 bool onAsyncThread = false;
349 (void)mAsyncExecutionThread->IsOnCurrentThread(&onAsyncThread);
350 MOZ_ASSERT(onAsyncThread);
351 #endif // DEBUG
353 // Internal close.
354 (void)mConnection->internalClose();
356 // Callback
357 if (mCallbackEvent) {
358 nsCOMPtr<nsIThread> thread;
359 (void)NS_GetMainThread(getter_AddRefs(thread));
360 (void)thread->Dispatch(mCallbackEvent, NS_DISPATCH_NORMAL);
363 return NS_OK;
366 ~AsyncCloseConnection() {
367 nsCOMPtr<nsIThread> thread;
368 (void)NS_GetMainThread(getter_AddRefs(thread));
369 // Handle ambiguous nsISupports inheritance.
370 Connection *rawConnection = nullptr;
371 mConnection.swap(rawConnection);
372 (void)NS_ProxyRelease(thread,
373 NS_ISUPPORTS_CAST(mozIStorageConnection *,
374 rawConnection));
375 (void)NS_ProxyRelease(thread, mCallbackEvent);
377 private:
378 nsRefPtr<Connection> mConnection;
379 nsCOMPtr<nsIRunnable> mCallbackEvent;
380 nsCOMPtr<nsIThread> mAsyncExecutionThread;
384 * An event used to initialize the clone of a connection.
386 * Must be executed on the clone's async execution thread.
388 class AsyncInitializeClone MOZ_FINAL: public nsRunnable
390 public:
392 * @param aConnection The connection being cloned.
393 * @param aClone The clone.
394 * @param aReadOnly If |true|, the clone is read only.
395 * @param aCallback A callback to trigger once initialization
396 * is complete. This event will be called on
397 * aClone->threadOpenedOn.
399 AsyncInitializeClone(Connection* aConnection,
400 Connection* aClone,
401 const bool aReadOnly,
402 mozIStorageCompletionCallback* aCallback)
403 : mConnection(aConnection)
404 , mClone(aClone)
405 , mReadOnly(aReadOnly)
406 , mCallback(aCallback)
408 MOZ_ASSERT(NS_IsMainThread());
411 NS_IMETHOD Run() {
412 MOZ_ASSERT (NS_GetCurrentThread() == mClone->getAsyncExecutionTarget());
414 nsresult rv = mConnection->initializeClone(mClone, mReadOnly);
415 if (NS_FAILED(rv)) {
416 return Dispatch(rv, nullptr);
418 return Dispatch(NS_OK,
419 NS_ISUPPORTS_CAST(mozIStorageAsyncConnection*, mClone));
422 private:
423 nsresult Dispatch(nsresult aResult, nsISupports* aValue) {
424 nsRefPtr<CallbackComplete> event = new CallbackComplete(aResult,
425 aValue,
426 mCallback.forget());
427 return mClone->threadOpenedOn->Dispatch(event, NS_DISPATCH_NORMAL);
430 ~AsyncInitializeClone() {
431 nsCOMPtr<nsIThread> thread;
432 DebugOnly<nsresult> rv = NS_GetMainThread(getter_AddRefs(thread));
433 MOZ_ASSERT(NS_SUCCEEDED(rv));
435 // Handle ambiguous nsISupports inheritance.
436 Connection *rawConnection = nullptr;
437 mConnection.swap(rawConnection);
438 (void)NS_ProxyRelease(thread, NS_ISUPPORTS_CAST(mozIStorageConnection *,
439 rawConnection));
441 Connection *rawClone = nullptr;
442 mClone.swap(rawClone);
443 (void)NS_ProxyRelease(thread, NS_ISUPPORTS_CAST(mozIStorageConnection *,
444 rawClone));
446 // Generally, the callback will be released by CallbackComplete.
447 // However, if for some reason Run() is not executed, we still
448 // need to ensure that it is released here.
449 mozIStorageCompletionCallback *rawCallback = nullptr;
450 mCallback.swap(rawCallback);
451 (void)NS_ProxyRelease(thread, rawCallback);
454 nsRefPtr<Connection> mConnection;
455 nsRefPtr<Connection> mClone;
456 const bool mReadOnly;
457 nsCOMPtr<mozIStorageCompletionCallback> mCallback;
460 } // anonymous namespace
462 ////////////////////////////////////////////////////////////////////////////////
463 //// Connection
465 Connection::Connection(Service *aService,
466 int aFlags,
467 bool aAsyncOnly)
468 : sharedAsyncExecutionMutex("Connection::sharedAsyncExecutionMutex")
469 , sharedDBMutex("Connection::sharedDBMutex")
470 , threadOpenedOn(do_GetCurrentThread())
471 , mDBConn(nullptr)
472 , mAsyncExecutionThreadShuttingDown(false)
473 , mTransactionInProgress(false)
474 , mProgressHandler(nullptr)
475 , mFlags(aFlags)
476 , mStorageService(aService)
477 , mAsyncOnly(aAsyncOnly)
479 mStorageService->registerConnection(this);
482 Connection::~Connection()
484 (void)Close();
486 MOZ_ASSERT(!mAsyncExecutionThread,
487 "AsyncClose has not been invoked on this connection!");
490 NS_IMPL_ADDREF(Connection)
492 NS_INTERFACE_MAP_BEGIN(Connection)
493 NS_INTERFACE_MAP_ENTRY(mozIStorageAsyncConnection)
494 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
495 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(mozIStorageConnection, !mAsyncOnly)
496 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageConnection)
497 NS_INTERFACE_MAP_END
499 // This is identical to what NS_IMPL_RELEASE provides, but with the
500 // extra |1 == count| case.
501 NS_IMETHODIMP_(nsrefcnt) Connection::Release(void)
503 NS_PRECONDITION(0 != mRefCnt, "dup release");
504 nsrefcnt count = --mRefCnt;
505 NS_LOG_RELEASE(this, count, "Connection");
506 if (1 == count) {
507 // If the refcount is 1, the single reference must be from
508 // gService->mConnections (in class |Service|). Which means we can
509 // unregister it safely.
510 mStorageService->unregisterConnection(this);
511 } else if (0 == count) {
512 mRefCnt = 1; /* stabilize */
513 #if 0 /* enable this to find non-threadsafe destructors: */
514 NS_ASSERT_OWNINGTHREAD(Connection);
515 #endif
516 delete (this);
517 return 0;
519 return count;
522 nsIEventTarget *
523 Connection::getAsyncExecutionTarget()
525 MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
527 // If we are shutting down the asynchronous thread, don't hand out any more
528 // references to the thread.
529 if (mAsyncExecutionThreadShuttingDown)
530 return nullptr;
532 if (!mAsyncExecutionThread) {
533 nsresult rv = ::NS_NewThread(getter_AddRefs(mAsyncExecutionThread));
534 if (NS_FAILED(rv)) {
535 NS_WARNING("Failed to create async thread.");
536 return nullptr;
538 static nsThreadPoolNaming naming;
539 naming.SetThreadPoolName(NS_LITERAL_CSTRING("mozStorage"),
540 mAsyncExecutionThread);
543 return mAsyncExecutionThread;
546 nsresult
547 Connection::initialize()
549 NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
550 PROFILER_LABEL("storage", "Connection::initialize");
552 // in memory database requested, sqlite uses a magic file name
553 int srv = ::sqlite3_open_v2(":memory:", &mDBConn, mFlags, nullptr);
554 if (srv != SQLITE_OK) {
555 mDBConn = nullptr;
556 return convertResultCode(srv);
559 return initializeInternal(nullptr);
562 nsresult
563 Connection::initialize(nsIFile *aDatabaseFile)
565 NS_ASSERTION (aDatabaseFile, "Passed null file!");
566 NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
567 PROFILER_LABEL("storage", "Connection::initialize");
569 mDatabaseFile = aDatabaseFile;
571 nsAutoString path;
572 nsresult rv = aDatabaseFile->GetPath(path);
573 NS_ENSURE_SUCCESS(rv, rv);
575 int srv = ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path).get(), &mDBConn,
576 mFlags, nullptr);
577 if (srv != SQLITE_OK) {
578 mDBConn = nullptr;
579 return convertResultCode(srv);
582 rv = initializeInternal(aDatabaseFile);
583 NS_ENSURE_SUCCESS(rv, rv);
585 mDatabaseFile = aDatabaseFile;
587 return NS_OK;
590 nsresult
591 Connection::initialize(nsIFileURL *aFileURL)
593 NS_ASSERTION (aFileURL, "Passed null file URL!");
594 NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
595 PROFILER_LABEL("storage", "Connection::initialize");
597 nsCOMPtr<nsIFile> databaseFile;
598 nsresult rv = aFileURL->GetFile(getter_AddRefs(databaseFile));
599 NS_ENSURE_SUCCESS(rv, rv);
601 nsAutoCString spec;
602 rv = aFileURL->GetSpec(spec);
603 NS_ENSURE_SUCCESS(rv, rv);
605 int srv = ::sqlite3_open_v2(spec.get(), &mDBConn, mFlags, nullptr);
606 if (srv != SQLITE_OK) {
607 mDBConn = nullptr;
608 return convertResultCode(srv);
611 rv = initializeInternal(databaseFile);
612 NS_ENSURE_SUCCESS(rv, rv);
614 mFileURL = aFileURL;
615 mDatabaseFile = databaseFile;
617 return NS_OK;
621 nsresult
622 Connection::initializeInternal(nsIFile* aDatabaseFile)
624 // Properly wrap the database handle's mutex.
625 sharedDBMutex.initWithMutex(sqlite3_db_mutex(mDBConn));
627 #ifdef PR_LOGGING
628 if (!gStorageLog)
629 gStorageLog = ::PR_NewLogModule("mozStorage");
631 ::sqlite3_trace(mDBConn, tracefunc, this);
633 nsAutoCString leafName(":memory");
634 if (aDatabaseFile)
635 (void)aDatabaseFile->GetNativeLeafName(leafName);
636 PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Opening connection to '%s' (%p)",
637 leafName.get(), this));
638 #endif
640 int64_t pageSize = Service::getDefaultPageSize();
642 // Set page_size to the preferred default value. This is effective only if
643 // the database has just been created, otherwise, if the database does not
644 // use WAL journal mode, a VACUUM operation will updated its page_size.
645 nsAutoCString pageSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
646 "PRAGMA page_size = ");
647 pageSizeQuery.AppendInt(pageSize);
648 nsresult rv = ExecuteSimpleSQL(pageSizeQuery);
649 NS_ENSURE_SUCCESS(rv, rv);
651 // Setting the cache_size forces the database open, verifying if it is valid
652 // or corrupt. So this is executed regardless it being actually needed.
653 // The cache_size is calculated from the actual page_size, to save memory.
654 nsAutoCString cacheSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
655 "PRAGMA cache_size = ");
656 cacheSizeQuery.AppendInt(-MAX_CACHE_SIZE_KIBIBYTES);
657 int srv = executeSql(cacheSizeQuery.get());
658 if (srv != SQLITE_OK) {
659 ::sqlite3_close(mDBConn);
660 mDBConn = nullptr;
661 return convertResultCode(srv);
664 // Register our built-in SQL functions.
665 srv = registerFunctions(mDBConn);
666 if (srv != SQLITE_OK) {
667 ::sqlite3_close(mDBConn);
668 mDBConn = nullptr;
669 return convertResultCode(srv);
672 // Register our built-in SQL collating sequences.
673 srv = registerCollations(mDBConn, mStorageService);
674 if (srv != SQLITE_OK) {
675 ::sqlite3_close(mDBConn);
676 mDBConn = nullptr;
677 return convertResultCode(srv);
680 // Set the synchronous PRAGMA, according to the preference.
681 switch (Service::getSynchronousPref()) {
682 case 2:
683 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
684 "PRAGMA synchronous = FULL;"));
685 break;
686 case 0:
687 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
688 "PRAGMA synchronous = OFF;"));
689 break;
690 case 1:
691 default:
692 (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
693 "PRAGMA synchronous = NORMAL;"));
694 break;
697 return NS_OK;
700 nsresult
701 Connection::databaseElementExists(enum DatabaseElementType aElementType,
702 const nsACString &aElementName,
703 bool *_exists)
705 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
707 nsCString query("SELECT name FROM (SELECT * FROM sqlite_master UNION ALL "
708 "SELECT * FROM sqlite_temp_master) "
709 "WHERE type = '");
710 switch (aElementType) {
711 case INDEX:
712 query.Append("index");
713 break;
714 case TABLE:
715 query.Append("table");
716 break;
718 query.Append("' AND name ='");
719 query.Append(aElementName);
720 query.Append("'");
722 sqlite3_stmt *stmt;
723 int srv = prepareStatement(query, &stmt);
724 if (srv != SQLITE_OK)
725 return convertResultCode(srv);
727 srv = stepStatement(stmt);
728 // we just care about the return value from step
729 (void)::sqlite3_finalize(stmt);
731 if (srv == SQLITE_ROW) {
732 *_exists = true;
733 return NS_OK;
735 if (srv == SQLITE_DONE) {
736 *_exists = false;
737 return NS_OK;
740 return convertResultCode(srv);
743 bool
744 Connection::findFunctionByInstance(nsISupports *aInstance)
746 sharedDBMutex.assertCurrentThreadOwns();
747 FFEArguments args = { aInstance, false };
748 (void)mFunctions.EnumerateRead(findFunctionEnumerator, &args);
749 return args.found;
752 /* static */ int
753 Connection::sProgressHelper(void *aArg)
755 Connection *_this = static_cast<Connection *>(aArg);
756 return _this->progressHandler();
760 Connection::progressHandler()
762 sharedDBMutex.assertCurrentThreadOwns();
763 if (mProgressHandler) {
764 bool result;
765 nsresult rv = mProgressHandler->OnProgress(this, &result);
766 if (NS_FAILED(rv)) return 0; // Don't break request
767 return result ? 1 : 0;
769 return 0;
772 nsresult
773 Connection::setClosedState()
775 // Ensure that we are on the correct thread to close the database.
776 bool onOpenedThread;
777 nsresult rv = threadOpenedOn->IsOnCurrentThread(&onOpenedThread);
778 NS_ENSURE_SUCCESS(rv, rv);
779 if (!onOpenedThread) {
780 NS_ERROR("Must close the database on the thread that you opened it with!");
781 return NS_ERROR_UNEXPECTED;
784 // Flag that we are shutting down the async thread, so that
785 // getAsyncExecutionTarget knows not to expose/create the async thread.
787 MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
788 NS_ENSURE_FALSE(mAsyncExecutionThreadShuttingDown, NS_ERROR_UNEXPECTED);
789 mAsyncExecutionThreadShuttingDown = true;
792 return NS_OK;
795 bool
796 Connection::isClosing(bool aResultOnClosed) {
797 MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
798 return mAsyncExecutionThreadShuttingDown &&
799 (aResultOnClosed || ConnectionReady());
802 nsresult
803 Connection::internalClose()
805 #ifdef DEBUG
806 // Sanity checks to make sure we are in the proper state before calling this.
807 NS_ASSERTION(mDBConn, "Database connection is already null!");
809 { // Make sure we have marked our async thread as shutting down.
810 MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
811 NS_ASSERTION(mAsyncExecutionThreadShuttingDown,
812 "Did not call setClosedState!");
815 bool onOpeningThread = false;
816 (void)threadOpenedOn->IsOnCurrentThread(&onOpeningThread);
817 #endif // DEBUG
819 #ifdef PR_LOGGING
820 nsAutoCString leafName(":memory");
821 if (mDatabaseFile)
822 (void)mDatabaseFile->GetNativeLeafName(leafName);
823 PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Closing connection to '%s'",
824 leafName.get()));
825 #endif
827 // Set the property to null before closing the connection, otherwise the other
828 // functions in the module may try to use the connection after it is closed.
829 sqlite3 *dbConn = mDBConn;
830 mDBConn = nullptr;
832 // At this stage, we may still have statements that need to be
833 // finalized. Attempt to close the database connection. This will
834 // always disconnect any virtual tables and cleanly finalize their
835 // internal statements. Once this is done, closing may fail due to
836 // unfinalized client statements, in which case we need to finalize
837 // these statements and close again.
839 int srv = sqlite3_close(dbConn);
841 if (srv == SQLITE_BUSY) {
842 // We still have non-finalized statements. Finalize them.
844 sqlite3_stmt *stmt = nullptr;
845 while ((stmt = ::sqlite3_next_stmt(dbConn, stmt))) {
846 PR_LOG(gStorageLog, PR_LOG_NOTICE,
847 ("Auto-finalizing SQL statement '%s' (%x)",
848 ::sqlite3_sql(stmt),
849 stmt));
851 #ifdef DEBUG
852 char *msg = ::PR_smprintf("SQL statement '%s' (%x) should have been finalized before closing the connection",
853 ::sqlite3_sql(stmt),
854 stmt);
855 NS_WARNING(msg);
856 ::PR_smprintf_free(msg);
857 #endif // DEBUG
859 srv = ::sqlite3_finalize(stmt);
861 #ifdef DEBUG
862 if (srv != SQLITE_OK) {
863 char *msg = ::PR_smprintf("Could not finalize SQL statement '%s' (%x)",
864 ::sqlite3_sql(stmt),
865 stmt);
866 NS_WARNING(msg);
867 ::PR_smprintf_free(msg);
869 #endif // DEBUG
871 // Ensure that the loop continues properly, whether closing has succeeded
872 // or not.
873 if (srv == SQLITE_OK) {
874 stmt = nullptr;
878 // Now that all statements have been finalized, we
879 // should be able to close.
880 srv = ::sqlite3_close(dbConn);
884 if (srv != SQLITE_OK) {
885 MOZ_ASSERT(srv == SQLITE_OK,
886 "sqlite3_close failed. There are probably outstanding statements that are listed above!");
889 return convertResultCode(srv);
892 nsCString
893 Connection::getFilename()
895 nsCString leafname(":memory:");
896 if (mDatabaseFile) {
897 (void)mDatabaseFile->GetNativeLeafName(leafname);
899 return leafname;
903 Connection::stepStatement(sqlite3_stmt *aStatement)
905 MOZ_ASSERT(aStatement);
906 bool checkedMainThread = false;
907 TimeStamp startTime = TimeStamp::Now();
909 // mDBConn may be null if the executing statement has been created and cached
910 // after a call to asyncClose() but before the connection has been nullified
911 // by internalClose(). In such a case closing the connection fails due to
912 // the existence of prepared statements, but mDBConn is set to null
913 // regardless. This usually happens when other tasks using cached statements
914 // are asynchronously scheduled for execution and any of them ends up after
915 // asyncClose. See bug 728653 for details.
916 if (!mDBConn)
917 return SQLITE_MISUSE;
919 (void)::sqlite3_extended_result_codes(mDBConn, 1);
921 int srv;
922 while ((srv = ::sqlite3_step(aStatement)) == SQLITE_LOCKED_SHAREDCACHE) {
923 if (!checkedMainThread) {
924 checkedMainThread = true;
925 if (::NS_IsMainThread()) {
926 NS_WARNING("We won't allow blocking on the main thread!");
927 break;
931 srv = WaitForUnlockNotify(mDBConn);
932 if (srv != SQLITE_OK) {
933 break;
936 ::sqlite3_reset(aStatement);
939 // Report very slow SQL statements to Telemetry
940 TimeDuration duration = TimeStamp::Now() - startTime;
941 const uint32_t threshold =
942 NS_IsMainThread() ? Telemetry::kSlowSQLThresholdForMainThread
943 : Telemetry::kSlowSQLThresholdForHelperThreads;
944 if (duration.ToMilliseconds() >= threshold) {
945 nsDependentCString statementString(::sqlite3_sql(aStatement));
946 Telemetry::RecordSlowSQLStatement(statementString, getFilename(),
947 duration.ToMilliseconds());
950 (void)::sqlite3_extended_result_codes(mDBConn, 0);
951 // Drop off the extended result bits of the result code.
952 return srv & 0xFF;
956 Connection::prepareStatement(const nsCString &aSQL,
957 sqlite3_stmt **_stmt)
959 bool checkedMainThread = false;
961 (void)::sqlite3_extended_result_codes(mDBConn, 1);
963 int srv;
964 while((srv = ::sqlite3_prepare_v2(mDBConn,
965 aSQL.get(),
967 _stmt,
968 nullptr)) == SQLITE_LOCKED_SHAREDCACHE) {
969 if (!checkedMainThread) {
970 checkedMainThread = true;
971 if (::NS_IsMainThread()) {
972 NS_WARNING("We won't allow blocking on the main thread!");
973 break;
977 srv = WaitForUnlockNotify(mDBConn);
978 if (srv != SQLITE_OK) {
979 break;
983 if (srv != SQLITE_OK) {
984 nsCString warnMsg;
985 warnMsg.AppendLiteral("The SQL statement '");
986 warnMsg.Append(aSQL);
987 warnMsg.AppendLiteral("' could not be compiled due to an error: ");
988 warnMsg.Append(::sqlite3_errmsg(mDBConn));
990 #ifdef DEBUG
991 NS_WARNING(warnMsg.get());
992 #endif
993 #ifdef PR_LOGGING
994 PR_LOG(gStorageLog, PR_LOG_ERROR, ("%s", warnMsg.get()));
995 #endif
998 (void)::sqlite3_extended_result_codes(mDBConn, 0);
999 // Drop off the extended result bits of the result code.
1000 int rc = srv & 0xFF;
1001 // sqlite will return OK on a comment only string and set _stmt to nullptr.
1002 // The callers of this function are used to only checking the return value,
1003 // so it is safer to return an error code.
1004 if (rc == SQLITE_OK && *_stmt == nullptr) {
1005 return SQLITE_MISUSE;
1008 return rc;
1013 Connection::executeSql(const char *aSqlString)
1015 if (!mDBConn)
1016 return SQLITE_MISUSE;
1018 TimeStamp startTime = TimeStamp::Now();
1019 int srv = ::sqlite3_exec(mDBConn, aSqlString, nullptr, nullptr, nullptr);
1021 // Report very slow SQL statements to Telemetry
1022 TimeDuration duration = TimeStamp::Now() - startTime;
1023 const uint32_t threshold =
1024 NS_IsMainThread() ? Telemetry::kSlowSQLThresholdForMainThread
1025 : Telemetry::kSlowSQLThresholdForHelperThreads;
1026 if (duration.ToMilliseconds() >= threshold) {
1027 nsDependentCString statementString(aSqlString);
1028 Telemetry::RecordSlowSQLStatement(statementString, getFilename(),
1029 duration.ToMilliseconds());
1032 return srv;
1035 ////////////////////////////////////////////////////////////////////////////////
1036 //// nsIInterfaceRequestor
1038 NS_IMETHODIMP
1039 Connection::GetInterface(const nsIID &aIID,
1040 void **_result)
1042 if (aIID.Equals(NS_GET_IID(nsIEventTarget))) {
1043 nsIEventTarget *background = getAsyncExecutionTarget();
1044 NS_IF_ADDREF(background);
1045 *_result = background;
1046 return NS_OK;
1048 return NS_ERROR_NO_INTERFACE;
1051 ////////////////////////////////////////////////////////////////////////////////
1052 //// mozIStorageConnection
1054 NS_IMETHODIMP
1055 Connection::Close()
1057 if (!mDBConn)
1058 return NS_ERROR_NOT_INITIALIZED;
1060 { // Make sure we have not executed any asynchronous statements.
1061 // If this fails, the mDBConn will be left open, resulting in a leak.
1062 // Ideally we'd schedule some code to destroy the mDBConn once all its
1063 // async statements have finished executing; see bug 704030.
1064 MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
1065 bool asyncCloseWasCalled = !mAsyncExecutionThread;
1066 NS_ENSURE_TRUE(asyncCloseWasCalled, NS_ERROR_UNEXPECTED);
1069 nsresult rv = setClosedState();
1070 NS_ENSURE_SUCCESS(rv, rv);
1072 return internalClose();
1075 NS_IMETHODIMP
1076 Connection::AsyncClose(mozIStorageCompletionCallback *aCallback)
1078 if (!NS_IsMainThread()) {
1079 return NS_ERROR_NOT_SAME_THREAD;
1081 if (!mDBConn)
1082 return NS_ERROR_NOT_INITIALIZED;
1084 nsIEventTarget *asyncThread = getAsyncExecutionTarget();
1085 NS_ENSURE_TRUE(asyncThread, NS_ERROR_NOT_INITIALIZED);
1087 nsresult rv = setClosedState();
1088 NS_ENSURE_SUCCESS(rv, rv);
1090 // Create our callback event if we were given a callback.
1091 nsCOMPtr<nsIRunnable> completeEvent;
1092 if (aCallback) {
1093 completeEvent = newCompletionEvent(aCallback);
1096 // Create and dispatch our close event to the background thread.
1097 nsCOMPtr<nsIRunnable> closeEvent;
1099 // We need to lock because we're modifying mAsyncExecutionThread
1100 MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
1101 closeEvent = new AsyncCloseConnection(this,
1102 completeEvent,
1103 mAsyncExecutionThread.forget());
1106 rv = asyncThread->Dispatch(closeEvent, NS_DISPATCH_NORMAL);
1107 NS_ENSURE_SUCCESS(rv, rv);
1109 return NS_OK;
1112 NS_IMETHODIMP
1113 Connection::AsyncClone(bool aReadOnly,
1114 mozIStorageCompletionCallback *aCallback)
1116 PROFILER_LABEL("storage", "Connection::Clone");
1117 if (!NS_IsMainThread()) {
1118 return NS_ERROR_NOT_SAME_THREAD;
1120 if (!mDBConn)
1121 return NS_ERROR_NOT_INITIALIZED;
1122 if (!mDatabaseFile)
1123 return NS_ERROR_UNEXPECTED;
1125 int flags = mFlags;
1126 if (aReadOnly) {
1127 // Turn off SQLITE_OPEN_READWRITE, and set SQLITE_OPEN_READONLY.
1128 flags = (~SQLITE_OPEN_READWRITE & flags) | SQLITE_OPEN_READONLY;
1129 // Turn off SQLITE_OPEN_CREATE.
1130 flags = (~SQLITE_OPEN_CREATE & flags);
1133 nsRefPtr<Connection> clone = new Connection(mStorageService, flags,
1134 mAsyncOnly);
1136 nsRefPtr<AsyncInitializeClone> initEvent =
1137 new AsyncInitializeClone(this, clone, aReadOnly, aCallback);
1138 nsCOMPtr<nsIEventTarget> target = clone->getAsyncExecutionTarget();
1139 if (!target) {
1140 return NS_ERROR_UNEXPECTED;
1142 return target->Dispatch(initEvent, NS_DISPATCH_NORMAL);
1145 nsresult
1146 Connection::initializeClone(Connection* aClone, bool aReadOnly)
1148 nsresult rv = mFileURL ? aClone->initialize(mFileURL)
1149 : aClone->initialize(mDatabaseFile);
1150 if (NS_FAILED(rv)) {
1151 return rv;
1154 // Copy over pragmas from the original connection.
1155 static const char * pragmas[] = {
1156 "cache_size",
1157 "temp_store",
1158 "foreign_keys",
1159 "journal_size_limit",
1160 "synchronous",
1161 "wal_autocheckpoint",
1163 for (uint32_t i = 0; i < ArrayLength(pragmas); ++i) {
1164 // Read-only connections just need cache_size and temp_store pragmas.
1165 if (aReadOnly && ::strcmp(pragmas[i], "cache_size") != 0 &&
1166 ::strcmp(pragmas[i], "temp_store") != 0) {
1167 continue;
1170 nsAutoCString pragmaQuery("PRAGMA ");
1171 pragmaQuery.Append(pragmas[i]);
1172 nsCOMPtr<mozIStorageStatement> stmt;
1173 rv = CreateStatement(pragmaQuery, getter_AddRefs(stmt));
1174 MOZ_ASSERT(NS_SUCCEEDED(rv));
1175 bool hasResult = false;
1176 if (stmt && NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
1177 pragmaQuery.AppendLiteral(" = ");
1178 pragmaQuery.AppendInt(stmt->AsInt32(0));
1179 rv = aClone->ExecuteSimpleSQL(pragmaQuery);
1180 MOZ_ASSERT(NS_SUCCEEDED(rv));
1184 // Copy any functions that have been added to this connection.
1185 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1186 (void)mFunctions.EnumerateRead(copyFunctionEnumerator, aClone);
1188 return NS_OK;
1191 NS_IMETHODIMP
1192 Connection::Clone(bool aReadOnly,
1193 mozIStorageConnection **_connection)
1195 MOZ_ASSERT(threadOpenedOn == NS_GetCurrentThread());
1197 PROFILER_LABEL("storage", "Connection::Clone");
1198 if (!mDBConn)
1199 return NS_ERROR_NOT_INITIALIZED;
1200 if (!mDatabaseFile)
1201 return NS_ERROR_UNEXPECTED;
1203 int flags = mFlags;
1204 if (aReadOnly) {
1205 // Turn off SQLITE_OPEN_READWRITE, and set SQLITE_OPEN_READONLY.
1206 flags = (~SQLITE_OPEN_READWRITE & flags) | SQLITE_OPEN_READONLY;
1207 // Turn off SQLITE_OPEN_CREATE.
1208 flags = (~SQLITE_OPEN_CREATE & flags);
1211 nsRefPtr<Connection> clone = new Connection(mStorageService, flags,
1212 mAsyncOnly);
1214 nsresult rv = initializeClone(clone, aReadOnly);
1215 if (NS_FAILED(rv)) {
1216 return rv;
1219 NS_IF_ADDREF(*_connection = clone);
1220 return NS_OK;
1223 NS_IMETHODIMP
1224 Connection::GetDefaultPageSize(int32_t *_defaultPageSize)
1226 *_defaultPageSize = Service::getDefaultPageSize();
1227 return NS_OK;
1230 NS_IMETHODIMP
1231 Connection::GetConnectionReady(bool *_ready)
1233 *_ready = ConnectionReady();
1234 return NS_OK;
1237 NS_IMETHODIMP
1238 Connection::GetDatabaseFile(nsIFile **_dbFile)
1240 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1242 NS_IF_ADDREF(*_dbFile = mDatabaseFile);
1244 return NS_OK;
1247 NS_IMETHODIMP
1248 Connection::GetLastInsertRowID(int64_t *_id)
1250 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1252 sqlite_int64 id = ::sqlite3_last_insert_rowid(mDBConn);
1253 *_id = id;
1255 return NS_OK;
1258 NS_IMETHODIMP
1259 Connection::GetAffectedRows(int32_t *_rows)
1261 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1263 *_rows = ::sqlite3_changes(mDBConn);
1265 return NS_OK;
1268 NS_IMETHODIMP
1269 Connection::GetLastError(int32_t *_error)
1271 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1273 *_error = ::sqlite3_errcode(mDBConn);
1275 return NS_OK;
1278 NS_IMETHODIMP
1279 Connection::GetLastErrorString(nsACString &_errorString)
1281 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1283 const char *serr = ::sqlite3_errmsg(mDBConn);
1284 _errorString.Assign(serr);
1286 return NS_OK;
1289 NS_IMETHODIMP
1290 Connection::GetSchemaVersion(int32_t *_version)
1292 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1294 nsCOMPtr<mozIStorageStatement> stmt;
1295 (void)CreateStatement(NS_LITERAL_CSTRING("PRAGMA user_version"),
1296 getter_AddRefs(stmt));
1297 NS_ENSURE_TRUE(stmt, NS_ERROR_OUT_OF_MEMORY);
1299 *_version = 0;
1300 bool hasResult;
1301 if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult)
1302 *_version = stmt->AsInt32(0);
1304 return NS_OK;
1307 NS_IMETHODIMP
1308 Connection::SetSchemaVersion(int32_t aVersion)
1310 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1312 nsAutoCString stmt(NS_LITERAL_CSTRING("PRAGMA user_version = "));
1313 stmt.AppendInt(aVersion);
1315 return ExecuteSimpleSQL(stmt);
1318 NS_IMETHODIMP
1319 Connection::CreateStatement(const nsACString &aSQLStatement,
1320 mozIStorageStatement **_stmt)
1322 NS_ENSURE_ARG_POINTER(_stmt);
1323 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1325 nsRefPtr<Statement> statement(new Statement());
1326 NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
1328 nsresult rv = statement->initialize(this, aSQLStatement);
1329 NS_ENSURE_SUCCESS(rv, rv);
1331 Statement *rawPtr;
1332 statement.forget(&rawPtr);
1333 *_stmt = rawPtr;
1334 return NS_OK;
1337 NS_IMETHODIMP
1338 Connection::CreateAsyncStatement(const nsACString &aSQLStatement,
1339 mozIStorageAsyncStatement **_stmt)
1341 NS_ENSURE_ARG_POINTER(_stmt);
1342 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1344 nsRefPtr<AsyncStatement> statement(new AsyncStatement());
1345 NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
1347 nsresult rv = statement->initialize(this, aSQLStatement);
1348 NS_ENSURE_SUCCESS(rv, rv);
1350 AsyncStatement *rawPtr;
1351 statement.forget(&rawPtr);
1352 *_stmt = rawPtr;
1353 return NS_OK;
1356 NS_IMETHODIMP
1357 Connection::ExecuteSimpleSQL(const nsACString &aSQLStatement)
1359 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1361 int srv = executeSql(PromiseFlatCString(aSQLStatement).get());
1362 return convertResultCode(srv);
1365 NS_IMETHODIMP
1366 Connection::ExecuteAsync(mozIStorageBaseStatement **aStatements,
1367 uint32_t aNumStatements,
1368 mozIStorageStatementCallback *aCallback,
1369 mozIStoragePendingStatement **_handle)
1371 nsTArray<StatementData> stmts(aNumStatements);
1372 for (uint32_t i = 0; i < aNumStatements; i++) {
1373 nsCOMPtr<StorageBaseStatementInternal> stmt =
1374 do_QueryInterface(aStatements[i]);
1376 // Obtain our StatementData.
1377 StatementData data;
1378 nsresult rv = stmt->getAsynchronousStatementData(data);
1379 NS_ENSURE_SUCCESS(rv, rv);
1381 NS_ASSERTION(stmt->getOwner() == this,
1382 "Statement must be from this database connection!");
1384 // Now append it to our array.
1385 NS_ENSURE_TRUE(stmts.AppendElement(data), NS_ERROR_OUT_OF_MEMORY);
1388 // Dispatch to the background
1389 return AsyncExecuteStatements::execute(stmts, this, aCallback, _handle);
1392 NS_IMETHODIMP
1393 Connection::TableExists(const nsACString &aTableName,
1394 bool *_exists)
1396 return databaseElementExists(TABLE, aTableName, _exists);
1399 NS_IMETHODIMP
1400 Connection::IndexExists(const nsACString &aIndexName,
1401 bool* _exists)
1403 return databaseElementExists(INDEX, aIndexName, _exists);
1406 NS_IMETHODIMP
1407 Connection::GetTransactionInProgress(bool *_inProgress)
1409 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1411 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1412 *_inProgress = mTransactionInProgress;
1413 return NS_OK;
1416 NS_IMETHODIMP
1417 Connection::BeginTransaction()
1419 return BeginTransactionAs(mozIStorageConnection::TRANSACTION_DEFERRED);
1422 NS_IMETHODIMP
1423 Connection::BeginTransactionAs(int32_t aTransactionType)
1425 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1427 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1428 if (mTransactionInProgress)
1429 return NS_ERROR_FAILURE;
1430 nsresult rv;
1431 switch(aTransactionType) {
1432 case TRANSACTION_DEFERRED:
1433 rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN DEFERRED"));
1434 break;
1435 case TRANSACTION_IMMEDIATE:
1436 rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN IMMEDIATE"));
1437 break;
1438 case TRANSACTION_EXCLUSIVE:
1439 rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN EXCLUSIVE"));
1440 break;
1441 default:
1442 return NS_ERROR_ILLEGAL_VALUE;
1444 if (NS_SUCCEEDED(rv))
1445 mTransactionInProgress = true;
1446 return rv;
1449 NS_IMETHODIMP
1450 Connection::CommitTransaction()
1452 if (!mDBConn)
1453 return NS_ERROR_NOT_INITIALIZED;
1455 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1456 if (!mTransactionInProgress)
1457 return NS_ERROR_UNEXPECTED;
1459 nsresult rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("COMMIT TRANSACTION"));
1460 if (NS_SUCCEEDED(rv))
1461 mTransactionInProgress = false;
1462 return rv;
1465 NS_IMETHODIMP
1466 Connection::RollbackTransaction()
1468 if (!mDBConn)
1469 return NS_ERROR_NOT_INITIALIZED;
1471 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1472 if (!mTransactionInProgress)
1473 return NS_ERROR_UNEXPECTED;
1475 nsresult rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("ROLLBACK TRANSACTION"));
1476 if (NS_SUCCEEDED(rv))
1477 mTransactionInProgress = false;
1478 return rv;
1481 NS_IMETHODIMP
1482 Connection::CreateTable(const char *aTableName,
1483 const char *aTableSchema)
1485 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1487 char *buf = ::PR_smprintf("CREATE TABLE %s (%s)", aTableName, aTableSchema);
1488 if (!buf)
1489 return NS_ERROR_OUT_OF_MEMORY;
1491 int srv = executeSql(buf);
1492 ::PR_smprintf_free(buf);
1494 return convertResultCode(srv);
1497 NS_IMETHODIMP
1498 Connection::CreateFunction(const nsACString &aFunctionName,
1499 int32_t aNumArguments,
1500 mozIStorageFunction *aFunction)
1502 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1504 // Check to see if this function is already defined. We only check the name
1505 // because a function can be defined with the same body but different names.
1506 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1507 NS_ENSURE_FALSE(mFunctions.Get(aFunctionName, nullptr), NS_ERROR_FAILURE);
1509 int srv = ::sqlite3_create_function(mDBConn,
1510 nsPromiseFlatCString(aFunctionName).get(),
1511 aNumArguments,
1512 SQLITE_ANY,
1513 aFunction,
1514 basicFunctionHelper,
1515 nullptr,
1516 nullptr);
1517 if (srv != SQLITE_OK)
1518 return convertResultCode(srv);
1520 FunctionInfo info = { aFunction,
1521 Connection::FunctionInfo::SIMPLE,
1522 aNumArguments };
1523 mFunctions.Put(aFunctionName, info);
1525 return NS_OK;
1528 NS_IMETHODIMP
1529 Connection::CreateAggregateFunction(const nsACString &aFunctionName,
1530 int32_t aNumArguments,
1531 mozIStorageAggregateFunction *aFunction)
1533 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1535 // Check to see if this function name is already defined.
1536 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1537 NS_ENSURE_FALSE(mFunctions.Get(aFunctionName, nullptr), NS_ERROR_FAILURE);
1539 // Because aggregate functions depend on state across calls, you cannot have
1540 // the same instance use the same name. We want to enumerate all functions
1541 // and make sure this instance is not already registered.
1542 NS_ENSURE_FALSE(findFunctionByInstance(aFunction), NS_ERROR_FAILURE);
1544 int srv = ::sqlite3_create_function(mDBConn,
1545 nsPromiseFlatCString(aFunctionName).get(),
1546 aNumArguments,
1547 SQLITE_ANY,
1548 aFunction,
1549 nullptr,
1550 aggregateFunctionStepHelper,
1551 aggregateFunctionFinalHelper);
1552 if (srv != SQLITE_OK)
1553 return convertResultCode(srv);
1555 FunctionInfo info = { aFunction,
1556 Connection::FunctionInfo::AGGREGATE,
1557 aNumArguments };
1558 mFunctions.Put(aFunctionName, info);
1560 return NS_OK;
1563 NS_IMETHODIMP
1564 Connection::RemoveFunction(const nsACString &aFunctionName)
1566 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1568 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1569 NS_ENSURE_TRUE(mFunctions.Get(aFunctionName, nullptr), NS_ERROR_FAILURE);
1571 int srv = ::sqlite3_create_function(mDBConn,
1572 nsPromiseFlatCString(aFunctionName).get(),
1574 SQLITE_ANY,
1575 nullptr,
1576 nullptr,
1577 nullptr,
1578 nullptr);
1579 if (srv != SQLITE_OK)
1580 return convertResultCode(srv);
1582 mFunctions.Remove(aFunctionName);
1584 return NS_OK;
1587 NS_IMETHODIMP
1588 Connection::SetProgressHandler(int32_t aGranularity,
1589 mozIStorageProgressHandler *aHandler,
1590 mozIStorageProgressHandler **_oldHandler)
1592 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1594 // Return previous one
1595 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1596 NS_IF_ADDREF(*_oldHandler = mProgressHandler);
1598 if (!aHandler || aGranularity <= 0) {
1599 aHandler = nullptr;
1600 aGranularity = 0;
1602 mProgressHandler = aHandler;
1603 ::sqlite3_progress_handler(mDBConn, aGranularity, sProgressHelper, this);
1605 return NS_OK;
1608 NS_IMETHODIMP
1609 Connection::RemoveProgressHandler(mozIStorageProgressHandler **_oldHandler)
1611 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1613 // Return previous one
1614 SQLiteMutexAutoLock lockedScope(sharedDBMutex);
1615 NS_IF_ADDREF(*_oldHandler = mProgressHandler);
1617 mProgressHandler = nullptr;
1618 ::sqlite3_progress_handler(mDBConn, 0, nullptr, nullptr);
1620 return NS_OK;
1623 NS_IMETHODIMP
1624 Connection::SetGrowthIncrement(int32_t aChunkSize, const nsACString &aDatabaseName)
1626 // Bug 597215: Disk space is extremely limited on Android
1627 // so don't preallocate space. This is also not effective
1628 // on log structured file systems used by Android devices
1629 #if !defined(ANDROID) && !defined(MOZ_PLATFORM_MAEMO)
1630 // Don't preallocate if less than 500MiB is available.
1631 int64_t bytesAvailable;
1632 nsresult rv = mDatabaseFile->GetDiskSpaceAvailable(&bytesAvailable);
1633 NS_ENSURE_SUCCESS(rv, rv);
1634 if (bytesAvailable < MIN_AVAILABLE_BYTES_PER_CHUNKED_GROWTH) {
1635 return NS_ERROR_FILE_TOO_BIG;
1638 (void)::sqlite3_file_control(mDBConn,
1639 aDatabaseName.Length() ? nsPromiseFlatCString(aDatabaseName).get()
1640 : nullptr,
1641 SQLITE_FCNTL_CHUNK_SIZE,
1642 &aChunkSize);
1643 #endif
1644 return NS_OK;
1647 NS_IMETHODIMP
1648 Connection::EnableModule(const nsACString& aModuleName)
1650 if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
1652 for (size_t i = 0; i < ArrayLength(gModules); i++) {
1653 struct Module* m = &gModules[i];
1654 if (aModuleName.Equals(m->name)) {
1655 int srv = m->registerFunc(mDBConn, m->name);
1656 if (srv != SQLITE_OK)
1657 return convertResultCode(srv);
1659 return NS_OK;
1663 return NS_ERROR_FAILURE;
1666 } // namespace storage
1667 } // namespace mozilla