Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/storage/./../../storage/mozStorageAsyncStatementExecution.cpp
Warning:line 228, column 7
Value stored to 'busyRetry' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Unified_cpp_storage0.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/storage -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/storage -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D MOZ_SQLITE_PERSIST_AUXILIARY_FILES=1 -D MOZ_SQLITE_VEC0_EXT=1 -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D MOZ_SUPPORT_LEAKCHECKING -D STATIC_EXPORTABLE_JS_API -I /root/firefox-clang/storage -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/storage -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/ipc/ipdl/_ipdlheaders -I /root/firefox-clang/ipc/chromium/src -I /root/firefox-clang/third_party/abseil-cpp -I /root/firefox-clang/toolkit/components/telemetry -I /root/firefox-clang/xpcom/base -I /root/firefox-clang/dom/base -I /root/firefox-clang/third_party/sqlite3/ext -I /root/firefox-clang/third_party/sqlite3/src -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c++ Unified_cpp_storage0.cpp
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5#include "sqlite3.h"
6
7#include "mozIStorageStatementCallback.h"
8#include "mozStorageBindingParams.h"
9#include "mozStorageHelper.h"
10#include "mozStorageResultSet.h"
11#include "mozStorageRow.h"
12#include "mozStorageConnection.h"
13#include "mozStorageError.h"
14#include "mozStoragePrivateHelpers.h"
15#include "mozStorageStatementData.h"
16#include "mozStorageAsyncStatementExecution.h"
17
18#include "mozilla/DebugOnly.h"
19
20#include "mozilla/Logging.h"
21extern mozilla::LazyLogModule gStorageLog;
22
23namespace mozilla {
24namespace storage {
25
26/**
27 * The following constants help batch rows into result sets.
28 * MAX_MILLISECONDS_BETWEEN_RESULTS was chosen because any user-based task that
29 * takes less than 200 milliseconds is considered to feel instantaneous to end
30 * users. MAX_ROWS_PER_RESULT was arbitrarily chosen to reduce the number of
31 * dispatches to calling thread, while also providing reasonably-sized sets of
32 * data for consumers. Both of these constants are used because we assume that
33 * consumers are trying to avoid blocking their execution thread for long
34 * periods of time, and dispatching many small events to the calling thread will
35 * end up blocking it.
36 */
37#define MAX_MILLISECONDS_BETWEEN_RESULTS75 75
38#define MAX_ROWS_PER_RESULT15 15
39
40////////////////////////////////////////////////////////////////////////////////
41//// AsyncExecuteStatements
42
43/* static */
44nsresult AsyncExecuteStatements::execute(
45 StatementDataArray&& aStatements, Connection* aConnection,
46 sqlite3* aNativeConnection, mozIStorageStatementCallback* aCallback,
47 mozIStoragePendingStatement** _stmt) {
48 // Create our event to run in the background
49 RefPtr<AsyncExecuteStatements> event = new AsyncExecuteStatements(
50 std::move(aStatements), aConnection, aNativeConnection, aCallback);
51 NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY)do { if ((__builtin_expect(!!(!(event)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "event" ") failed", nullptr
, "./../../storage/mozStorageAsyncStatementExecution.cpp", 51
); return NS_ERROR_OUT_OF_MEMORY; } } while (false)
;
52
53 // Dispatch it to the background
54 nsIEventTarget* target = aConnection->getAsyncExecutionTarget();
55
56 // If we don't have a valid target, this is a bug somewhere else. In the past,
57 // this assert found cases where a Run method would schedule a new statement
58 // without checking if asyncClose had been called. The caller must prevent
59 // that from happening or, if the work is not critical, just avoid creating
60 // the new statement during shutdown. See bug 718449 for an example.
61 MOZ_ASSERT(target)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(target)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(target))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("target", "./../../storage/mozStorageAsyncStatementExecution.cpp"
, 61); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")");
do { MOZ_CrashSequence(__null, 61); __attribute__((nomerge))
::abort(); } while (false); } } while (false)
;
62 if (!target) {
63 return NS_ERROR_NOT_AVAILABLE;
64 }
65
66 nsresult rv = target->Dispatch(event, NS_DISPATCH_NORMALnsIEventTarget::DISPATCH_NORMAL);
67 NS_ENSURE_SUCCESS(rv, rv)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "rv", "rv", static_cast<uint32_t
>(__rv), name ? " (" : "", name ? name : "", name ? ")" : ""
); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "./../../storage/mozStorageAsyncStatementExecution.cpp"
, 67); return rv; } } while (false)
;
68
69 // Return it as the pending statement object and track it.
70 event.forget(_stmt);
71 return NS_OK;
72}
73
74AsyncExecuteStatements::AsyncExecuteStatements(
75 StatementDataArray&& aStatements, Connection* aConnection,
76 sqlite3* aNativeConnection, mozIStorageStatementCallback* aCallback)
77 : Runnable("AsyncExecuteStatements"),
78 mStatements(std::move(aStatements)),
79 mConnection(aConnection),
80 mNativeConnection(aNativeConnection),
81 mHasTransaction(false),
82 mCallback(aCallback),
83 mCallingThread(::do_GetCurrentThread()),
84 mMaxWait(
85 TimeDuration::FromMilliseconds(MAX_MILLISECONDS_BETWEEN_RESULTS75)),
86 mIntervalStart(TimeStamp::Now()),
87 mState(PENDING),
88 mCancelRequested(false),
89 mMutex(aConnection->sharedAsyncExecutionMutex),
90 mDBMutex(aConnection->sharedDBMutex) {
91 NS_ASSERTION(mStatements.Length(), "We weren't given any statements!")do { if (!(mStatements.Length())) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "We weren't given any statements!", "mStatements.Length()",
"./../../storage/mozStorageAsyncStatementExecution.cpp", 91)
; MOZ_PretendNoReturn(); } } while (0)
;
92}
93
94AsyncExecuteStatements::~AsyncExecuteStatements() {
95 MOZ_ASSERT(!mCallback, "Never called the Completion callback!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mCallback)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mCallback))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("!mCallback" " (" "Never called the Completion callback!"
")", "./../../storage/mozStorageAsyncStatementExecution.cpp"
, 95); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mCallback" ") ("
"Never called the Completion callback!" ")"); do { MOZ_CrashSequence
(__null, 95); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
96 MOZ_ASSERT(!mHasTransaction, "There should be no transaction at this point")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mHasTransaction)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mHasTransaction))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!mHasTransaction"
" (" "There should be no transaction at this point" ")", "./../../storage/mozStorageAsyncStatementExecution.cpp"
, 96); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mHasTransaction"
") (" "There should be no transaction at this point" ")"); do
{ MOZ_CrashSequence(__null, 96); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
97 if (mCallback) {
98 NS_ProxyRelease("AsyncExecuteStatements::mCallback", mCallingThread,
99 mCallback.forget());
100 }
101}
102
103bool AsyncExecuteStatements::shouldNotify() {
104#ifdef DEBUG1
105 mMutex.AssertNotCurrentThreadOwns();
106
107 bool onCallingThread = false;
108 (void)mCallingThread->IsOnCurrentThread(&onCallingThread);
109 NS_ASSERTION(onCallingThread, "runEvent not running on the calling thread!")do { if (!(onCallingThread)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "runEvent not running on the calling thread!", "onCallingThread"
, "./../../storage/mozStorageAsyncStatementExecution.cpp", 109
); MOZ_PretendNoReturn(); } } while (0)
;
110#endif
111
112 // We do not need to acquire mMutex here because it can only ever be written
113 // to on the calling thread, and the only thread that can call us is the
114 // calling thread, so we know that our access is serialized.
115 return !mCancelRequested;
116}
117
118bool AsyncExecuteStatements::bindExecuteAndProcessStatement(
119 StatementData& aData, bool aLastStatement) {
120 mMutex.AssertNotCurrentThreadOwns();
121
122 sqlite3_stmt* aStatement = nullptr;
123 // This cannot fail; we are only called if it's available.
124 (void)aData.getSqliteStatement(&aStatement);
125 MOZ_DIAGNOSTIC_ASSERT(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aStatement)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aStatement))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("aStatement" " (" "bindExecuteAndProcessStatement called without an initialized statement"
")", "./../../storage/mozStorageAsyncStatementExecution.cpp"
, 127); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "aStatement"
") (" "bindExecuteAndProcessStatement called without an initialized statement"
")"); do { MOZ_CrashSequence(__null, 127); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
126 aStatement,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aStatement)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aStatement))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("aStatement" " (" "bindExecuteAndProcessStatement called without an initialized statement"
")", "./../../storage/mozStorageAsyncStatementExecution.cpp"
, 127); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "aStatement"
") (" "bindExecuteAndProcessStatement called without an initialized statement"
")"); do { MOZ_CrashSequence(__null, 127); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
127 "bindExecuteAndProcessStatement called without an initialized statement")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aStatement)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aStatement))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("aStatement" " (" "bindExecuteAndProcessStatement called without an initialized statement"
")", "./../../storage/mozStorageAsyncStatementExecution.cpp"
, 127); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "aStatement"
") (" "bindExecuteAndProcessStatement called without an initialized statement"
")"); do { MOZ_CrashSequence(__null, 127); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
128 BindingParamsArray* paramsArray(aData);
129
130 // Iterate through all of our parameters, bind them, and execute.
131 bool continueProcessing = true;
132 BindingParamsArray::iterator itr = paramsArray->begin();
133 BindingParamsArray::iterator end = paramsArray->end();
134 while (itr != end && continueProcessing) {
135 // Bind the data to our statement.
136 nsCOMPtr<IStorageBindingParamsInternal> bindingInternal =
137 do_QueryInterface(*itr);
138 nsCOMPtr<mozIStorageError> error = bindingInternal->bind(aStatement);
139 if (error) {
140 // Set our error state.
141 mState = ERROR;
142
143 // And notify.
144 (void)notifyError(error);
145 return false;
146 }
147
148 // Advance our iterator, execute, and then process the statement.
149 itr++;
150 bool lastStatement = aLastStatement && itr == end;
151 continueProcessing = executeAndProcessStatement(aData, lastStatement);
152
153 // Always reset our statement.
154 (void)::sqlite3_reset(aStatement);
155 }
156
157 return continueProcessing;
158}
159
160bool AsyncExecuteStatements::executeAndProcessStatement(StatementData& aData,
161 bool aLastStatement) {
162 mMutex.AssertNotCurrentThreadOwns();
163
164 sqlite3_stmt* aStatement = nullptr;
165 // This cannot fail; we are only called if it's available.
166 (void)aData.getSqliteStatement(&aStatement);
167 MOZ_DIAGNOSTIC_ASSERT(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aStatement)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aStatement))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("aStatement" " (" "executeAndProcessStatement called without an initialized statement"
")", "./../../storage/mozStorageAsyncStatementExecution.cpp"
, 169); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "aStatement"
") (" "executeAndProcessStatement called without an initialized statement"
")"); do { MOZ_CrashSequence(__null, 169); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
168 aStatement,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aStatement)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aStatement))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("aStatement" " (" "executeAndProcessStatement called without an initialized statement"
")", "./../../storage/mozStorageAsyncStatementExecution.cpp"
, 169); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "aStatement"
") (" "executeAndProcessStatement called without an initialized statement"
")"); do { MOZ_CrashSequence(__null, 169); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
169 "executeAndProcessStatement called without an initialized statement")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aStatement)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aStatement))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("aStatement" " (" "executeAndProcessStatement called without an initialized statement"
")", "./../../storage/mozStorageAsyncStatementExecution.cpp"
, 169); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "aStatement"
") (" "executeAndProcessStatement called without an initialized statement"
")"); do { MOZ_CrashSequence(__null, 169); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
170
171 // Execute our statement
172 bool hasResults;
173 do {
174 hasResults = executeStatement(aData);
175
176 // If we had an error, bail.
177 if (mState == ERROR || mState == CANCELED) return false;
178
179 // If we have been canceled, there is no point in going on...
180 {
181 MutexAutoLock lockedScope(mMutex);
182 if (mCancelRequested) {
183 mState = CANCELED;
184 return false;
185 }
186 }
187
188 // Build our result set and notify if we got anything back and have a
189 // callback to notify.
190 if (mCallback && hasResults &&
191 NS_FAILED(buildAndNotifyResults(aStatement))((bool)(__builtin_expect(!!(NS_FAILED_impl(buildAndNotifyResults
(aStatement))), 0)))
) {
192 // We had an error notifying, so we notify on error and stop processing.
193 mState = ERROR;
194
195 // Notify, and stop processing statements.
196 (void)notifyError(mozIStorageError::ERROR,
197 "An error occurred while notifying about results");
198
199 return false;
200 }
201 } while (hasResults);
202
203 if (MOZ_LOG_TEST(gStorageLog, LogLevel::Warning)(__builtin_expect(!!(mozilla::detail::log_test(gStorageLog, LogLevel
::Warning)), 0))
) {
204 // Check to make sure that this statement was smart about what it did.
205 checkAndLogStatementPerformance(aStatement);
206 }
207
208 // If we are done, we need to set our state accordingly while we still hold
209 // our mutex. We would have already returned if we were canceled or had
210 // an error at this point.
211 if (aLastStatement) mState = COMPLETED;
212
213 return true;
214}
215
216bool AsyncExecuteStatements::executeStatement(StatementData& aData) {
217 mMutex.AssertNotCurrentThreadOwns();
218
219 sqlite3_stmt* aStatement = nullptr;
220 // This cannot fail; we are only called if it's available.
221 (void)aData.getSqliteStatement(&aStatement);
222 MOZ_DIAGNOSTIC_ASSERT(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aStatement)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aStatement))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("aStatement" " (" "executeStatement called without an initialized statement"
")", "./../../storage/mozStorageAsyncStatementExecution.cpp"
, 223); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "aStatement"
") (" "executeStatement called without an initialized statement"
")"); do { MOZ_CrashSequence(__null, 223); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
223 aStatement, "executeStatement called without an initialized statement")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aStatement)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aStatement))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("aStatement" " (" "executeStatement called without an initialized statement"
")", "./../../storage/mozStorageAsyncStatementExecution.cpp"
, 223); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "aStatement"
") (" "executeStatement called without an initialized statement"
")"); do { MOZ_CrashSequence(__null, 223); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
224
225 bool busyRetry = false;
226 while (true) {
227 if (busyRetry) {
228 busyRetry = false;
Value stored to 'busyRetry' is never read
229
230 // Yield, and try again
231 (void)PR_Sleep(PR_INTERVAL_NO_WAIT0UL);
232
233 // Check for cancellation before retrying
234 {
235 MutexAutoLock lockedScope(mMutex);
236 if (mCancelRequested) {
237 mState = CANCELED;
238 return false;
239 }
240 }
241 }
242
243 // lock the sqlite mutex so sqlite3_errmsg cannot change
244 SQLiteMutexAutoLock lockedScope(mDBMutex);
245
246 int rc = mConnection->stepStatement(mNativeConnection, aStatement);
247
248 // Some errors are not fatal, and we can handle them and continue.
249 if (rc == SQLITE_BUSY5) {
250 ::sqlite3_reset(aStatement);
251 busyRetry = true;
252 continue;
253 }
254
255 aData.MaybeRecordQueryStatus(rc);
256
257 // Stop if we have no more results.
258 if (rc == SQLITE_DONE101) {
259 return false;
260 }
261
262 // If we got results, we can return now.
263 if (rc == SQLITE_ROW100) {
264 return true;
265 }
266
267 if (rc == SQLITE_INTERRUPT9) {
268 mState = CANCELED;
269 return false;
270 }
271
272 // Set an error state.
273 mState = ERROR;
274
275 // Construct the error message before giving up the mutex (which we cannot
276 // hold during the call to notifyError).
277 nsCOMPtr<mozIStorageError> errorObj(
278 new Error(rc, ::sqlite3_errmsg(mNativeConnection)));
279 // We cannot hold the DB mutex while calling notifyError.
280 SQLiteMutexAutoUnlock unlockedScope(mDBMutex);
281 (void)notifyError(errorObj);
282
283 // Finally, indicate that we should stop processing.
284 return false;
285 }
286}
287
288nsresult AsyncExecuteStatements::buildAndNotifyResults(
289 sqlite3_stmt* aStatement) {
290 NS_ASSERTION(mCallback, "Trying to dispatch results without a callback!")do { if (!(mCallback)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Trying to dispatch results without a callback!"
, "mCallback", "./../../storage/mozStorageAsyncStatementExecution.cpp"
, 290); MOZ_PretendNoReturn(); } } while (0)
;
291 mMutex.AssertNotCurrentThreadOwns();
292
293 // Build result object if we need it.
294 if (!mResultSet) mResultSet = new ResultSet();
295 NS_ENSURE_TRUE(mResultSet, NS_ERROR_OUT_OF_MEMORY)do { if ((__builtin_expect(!!(!(mResultSet)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "mResultSet" ") failed",
nullptr, "./../../storage/mozStorageAsyncStatementExecution.cpp"
, 295); return NS_ERROR_OUT_OF_MEMORY; } } while (false)
;
296
297 RefPtr<Row> row(new Row());
298 NS_ENSURE_TRUE(row, NS_ERROR_OUT_OF_MEMORY)do { if ((__builtin_expect(!!(!(row)), 0))) { NS_DebugBreak(NS_DEBUG_WARNING
, "NS_ENSURE_TRUE(" "row" ") failed", nullptr, "./../../storage/mozStorageAsyncStatementExecution.cpp"
, 298); return NS_ERROR_OUT_OF_MEMORY; } } while (false)
;
299
300 nsresult rv = row->initialize(aStatement);
301 NS_ENSURE_SUCCESS(rv, rv)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "rv", "rv", static_cast<uint32_t
>(__rv), name ? " (" : "", name ? name : "", name ? ")" : ""
); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "./../../storage/mozStorageAsyncStatementExecution.cpp"
, 301); return rv; } } while (false)
;
302
303 rv = mResultSet->add(row);
304 NS_ENSURE_SUCCESS(rv, rv)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "rv", "rv", static_cast<uint32_t
>(__rv), name ? " (" : "", name ? name : "", name ? ")" : ""
); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "./../../storage/mozStorageAsyncStatementExecution.cpp"
, 304); return rv; } } while (false)
;
305
306 // If we have hit our maximum number of allowed results, or if we have hit
307 // the maximum amount of time we want to wait for results, notify the
308 // calling thread about it.
309 TimeStamp now = TimeStamp::Now();
310 TimeDuration delta = now - mIntervalStart;
311 if (mResultSet->rows() >= MAX_ROWS_PER_RESULT15 || delta > mMaxWait) {
312 // Notify the caller
313 rv = notifyResults();
314 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) return NS_OK; // we'll try again with the next result
315
316 // Reset our start time
317 mIntervalStart = now;
318 }
319
320 return NS_OK;
321}
322
323nsresult AsyncExecuteStatements::notifyComplete() {
324 mMutex.AssertNotCurrentThreadOwns();
325 NS_ASSERTION(mState != PENDING,do { if (!(mState != PENDING)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "Still in a pending state when calling Complete!", "mState != PENDING"
, "./../../storage/mozStorageAsyncStatementExecution.cpp", 326
); MOZ_PretendNoReturn(); } } while (0)
326 "Still in a pending state when calling Complete!")do { if (!(mState != PENDING)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "Still in a pending state when calling Complete!", "mState != PENDING"
, "./../../storage/mozStorageAsyncStatementExecution.cpp", 326
); MOZ_PretendNoReturn(); } } while (0)
;
327
328 // Reset our statements before we try to commit or rollback. If we are
329 // canceling and have statements that think they have pending work, the
330 // rollback will fail.
331 for (uint32_t i = 0; i < mStatements.Length(); i++) mStatements[i].reset();
332
333 // Release references to the statement data as soon as possible. If this
334 // is the last reference, statements will be finalized immediately on the
335 // async thread, hence avoiding several bounces between threads and possible
336 // race conditions with AsyncClose().
337 mStatements.Clear();
338
339 // Handle our transaction, if we have one
340 if (mHasTransaction) {
341 SQLiteMutexAutoLock lockedScope(mDBMutex);
342 if (mState == COMPLETED) {
343 nsresult rv = mConnection->commitTransactionInternal(lockedScope,
344 mNativeConnection);
345 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
346 mState = ERROR;
347 // We cannot hold the DB mutex while calling notifyError.
348 SQLiteMutexAutoUnlock unlockedScope(mDBMutex);
349 (void)notifyError(mozIStorageError::ERROR,
350 "Transaction failed to commit");
351 }
352 } else {
353 DebugOnly<nsresult> rv = mConnection->rollbackTransactionInternal(
354 lockedScope, mNativeConnection);
355 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Transaction failed to rollback")do { if (!(((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1
))))) { NS_DebugBreak(NS_DEBUG_WARNING, "Transaction failed to rollback"
, "NS_SUCCEEDED(rv)", "./../../storage/mozStorageAsyncStatementExecution.cpp"
, 355); } } while (false)
;
356 }
357 mHasTransaction = false;
358 }
359
360 // This will take ownership of mCallback and make sure its destruction will
361 // happen on the owner thread.
362 (void)mCallingThread->Dispatch(
363 NewRunnableMethod("AsyncExecuteStatements::notifyCompleteOnCallingThread",
364 this,
365 &AsyncExecuteStatements::notifyCompleteOnCallingThread),
366 NS_DISPATCH_NORMALnsIEventTarget::DISPATCH_NORMAL);
367
368 return NS_OK;
369}
370
371nsresult AsyncExecuteStatements::notifyCompleteOnCallingThread() {
372 MOZ_ASSERT(mCallingThread->IsOnCurrentThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mCallingThread->IsOnCurrentThread())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(mCallingThread->IsOnCurrentThread()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("mCallingThread->IsOnCurrentThread()"
, "./../../storage/mozStorageAsyncStatementExecution.cpp", 372
); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mCallingThread->IsOnCurrentThread()"
")"); do { MOZ_CrashSequence(__null, 372); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
373 // Take ownership of mCallback and responsibility for freeing it when we
374 // release it. Any notifyResultsOnCallingThread and
375 // notifyErrorOnCallingThread calls on the stack spinning the event loop have
376 // guaranteed their safety by creating their own strong reference before
377 // invoking the callback.
378 nsCOMPtr<mozIStorageStatementCallback> callback = std::move(mCallback);
379 if (callback) {
380 (void)callback->HandleCompletion(mState);
381 }
382 return NS_OK;
383}
384
385nsresult AsyncExecuteStatements::notifyError(int32_t aErrorCode,
386 const char* aMessage) {
387 mMutex.AssertNotCurrentThreadOwns();
388 mDBMutex.assertNotCurrentThreadOwns();
389
390 if (!mCallback) return NS_OK;
391
392 nsCOMPtr<mozIStorageError> errorObj(new Error(aErrorCode, aMessage));
393 NS_ENSURE_TRUE(errorObj, NS_ERROR_OUT_OF_MEMORY)do { if ((__builtin_expect(!!(!(errorObj)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "errorObj" ") failed", nullptr
, "./../../storage/mozStorageAsyncStatementExecution.cpp", 393
); return NS_ERROR_OUT_OF_MEMORY; } } while (false)
;
394
395 return notifyError(errorObj);
396}
397
398nsresult AsyncExecuteStatements::notifyError(mozIStorageError* aError) {
399 mMutex.AssertNotCurrentThreadOwns();
400 mDBMutex.assertNotCurrentThreadOwns();
401
402 if (!mCallback) return NS_OK;
403
404 (void)mCallingThread->Dispatch(
405 NewRunnableMethod<nsCOMPtr<mozIStorageError>>(
406 "AsyncExecuteStatements::notifyErrorOnCallingThread", this,
407 &AsyncExecuteStatements::notifyErrorOnCallingThread, aError),
408 NS_DISPATCH_NORMALnsIEventTarget::DISPATCH_NORMAL);
409
410 return NS_OK;
411}
412
413nsresult AsyncExecuteStatements::notifyErrorOnCallingThread(
414 mozIStorageError* aError) {
415 MOZ_ASSERT(mCallingThread->IsOnCurrentThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mCallingThread->IsOnCurrentThread())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(mCallingThread->IsOnCurrentThread()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("mCallingThread->IsOnCurrentThread()"
, "./../../storage/mozStorageAsyncStatementExecution.cpp", 415
); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mCallingThread->IsOnCurrentThread()"
")"); do { MOZ_CrashSequence(__null, 415); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
416 // Acquire our own strong reference so that if the callback spins a nested
417 // event loop and notifyCompleteOnCallingThread is executed, forgetting
418 // mCallback, we still have a valid/strong reference that won't be freed until
419 // we exit.
420 nsCOMPtr<mozIStorageStatementCallback> callback = mCallback;
421 if (shouldNotify() && callback) {
422 (void)callback->HandleError(aError);
423 }
424 return NS_OK;
425}
426
427nsresult AsyncExecuteStatements::notifyResults() {
428 mMutex.AssertNotCurrentThreadOwns();
429 MOZ_ASSERT(mCallback, "notifyResults called without a callback!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mCallback)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mCallback))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("mCallback" " (" "notifyResults called without a callback!"
")", "./../../storage/mozStorageAsyncStatementExecution.cpp"
, 429); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mCallback" ") ("
"notifyResults called without a callback!" ")"); do { MOZ_CrashSequence
(__null, 429); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
430
431 // This takes ownership of mResultSet, a new one will be generated in
432 // buildAndNotifyResults() when further results will arrive.
433 (void)mCallingThread->Dispatch(
434 NewRunnableMethod<RefPtr<ResultSet>>(
435 "AsyncExecuteStatements::notifyResultsOnCallingThread", this,
436 &AsyncExecuteStatements::notifyResultsOnCallingThread,
437 mResultSet.forget()),
438 NS_DISPATCH_NORMALnsIEventTarget::DISPATCH_NORMAL);
439
440 return NS_OK;
441}
442
443nsresult AsyncExecuteStatements::notifyResultsOnCallingThread(
444 ResultSet* aResultSet) {
445 MOZ_ASSERT(mCallingThread->IsOnCurrentThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mCallingThread->IsOnCurrentThread())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(mCallingThread->IsOnCurrentThread()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("mCallingThread->IsOnCurrentThread()"
, "./../../storage/mozStorageAsyncStatementExecution.cpp", 445
); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mCallingThread->IsOnCurrentThread()"
")"); do { MOZ_CrashSequence(__null, 445); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
446 // Acquire our own strong reference so that if the callback spins a nested
447 // event loop and notifyCompleteOnCallingThread is executed, forgetting
448 // mCallback, we still have a valid/strong reference that won't be freed until
449 // we exit.
450 nsCOMPtr<mozIStorageStatementCallback> callback = mCallback;
451 if (shouldNotify() && callback) {
452 (void)callback->HandleResult(aResultSet);
453 }
454 return NS_OK;
455}
456
457NS_IMPL_ISUPPORTS_INHERITED(AsyncExecuteStatements, Runnable,nsresult AsyncExecuteStatements::QueryInterface(const nsIID&
aIID, void** aInstancePtr) { do { if (!(aInstancePtr)) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "QueryInterface requires a non-NULL destination!"
, "aInstancePtr", "./../../storage/mozStorageAsyncStatementExecution.cpp"
, 458); MOZ_PretendNoReturn(); } } while (0); nsresult rv = NS_ERROR_FAILURE
; static_assert(1 > 0, "Need more arguments to NS_INTERFACE_TABLE_INHERITED"
); static const QITableEntry table[] = { {&mozilla::detail
::kImplementedIID<AsyncExecuteStatements, mozIStoragePendingStatement
>, int32_t( reinterpret_cast<char*>(static_cast<mozIStoragePendingStatement
*>((AsyncExecuteStatements*)0x1000)) - reinterpret_cast<
char*>((AsyncExecuteStatements*)0x1000))}, { nullptr, 0 } }
; static_assert(std::size(table) > 1, "need at least 1 interface"
); rv = NS_TableDrivenQI(static_cast<void*>(this), aIID
, aInstancePtr, table); if (((bool)(__builtin_expect(!!(!NS_FAILED_impl
(rv)), 1)))) return rv; return Runnable::QueryInterface(aIID,
aInstancePtr); } MozExternalRefCountType AsyncExecuteStatements
::AddRef(void) { static_assert(!std::is_destructible_v<AsyncExecuteStatements
>, "Reference-counted class " "AsyncExecuteStatements" " should not have a public destructor. "
"Make this class's destructor non-public"); nsrefcnt r = Runnable
::AddRef(); if constexpr (::mozilla::detail::ShouldLogInheritedRefcnt
<AsyncExecuteStatements>) { NS_LogAddRef((this), (r), (
"AsyncExecuteStatements"), (uint32_t)(sizeof(*this))); } return
r; } MozExternalRefCountType AsyncExecuteStatements::Release
(void) { nsrefcnt r = Runnable::Release(); if constexpr (::mozilla
::detail::ShouldLogInheritedRefcnt<AsyncExecuteStatements>
) { NS_LogRelease((this), (r), ("AsyncExecuteStatements")); }
return r; }
458 mozIStoragePendingStatement)nsresult AsyncExecuteStatements::QueryInterface(const nsIID&
aIID, void** aInstancePtr) { do { if (!(aInstancePtr)) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "QueryInterface requires a non-NULL destination!"
, "aInstancePtr", "./../../storage/mozStorageAsyncStatementExecution.cpp"
, 458); MOZ_PretendNoReturn(); } } while (0); nsresult rv = NS_ERROR_FAILURE
; static_assert(1 > 0, "Need more arguments to NS_INTERFACE_TABLE_INHERITED"
); static const QITableEntry table[] = { {&mozilla::detail
::kImplementedIID<AsyncExecuteStatements, mozIStoragePendingStatement
>, int32_t( reinterpret_cast<char*>(static_cast<mozIStoragePendingStatement
*>((AsyncExecuteStatements*)0x1000)) - reinterpret_cast<
char*>((AsyncExecuteStatements*)0x1000))}, { nullptr, 0 } }
; static_assert(std::size(table) > 1, "need at least 1 interface"
); rv = NS_TableDrivenQI(static_cast<void*>(this), aIID
, aInstancePtr, table); if (((bool)(__builtin_expect(!!(!NS_FAILED_impl
(rv)), 1)))) return rv; return Runnable::QueryInterface(aIID,
aInstancePtr); } MozExternalRefCountType AsyncExecuteStatements
::AddRef(void) { static_assert(!std::is_destructible_v<AsyncExecuteStatements
>, "Reference-counted class " "AsyncExecuteStatements" " should not have a public destructor. "
"Make this class's destructor non-public"); nsrefcnt r = Runnable
::AddRef(); if constexpr (::mozilla::detail::ShouldLogInheritedRefcnt
<AsyncExecuteStatements>) { NS_LogAddRef((this), (r), (
"AsyncExecuteStatements"), (uint32_t)(sizeof(*this))); } return
r; } MozExternalRefCountType AsyncExecuteStatements::Release
(void) { nsrefcnt r = Runnable::Release(); if constexpr (::mozilla
::detail::ShouldLogInheritedRefcnt<AsyncExecuteStatements>
) { NS_LogRelease((this), (r), ("AsyncExecuteStatements")); }
return r; }
459
460bool AsyncExecuteStatements::statementsNeedTransaction() {
461 // If there is more than one write statement, run in a transaction.
462 // Additionally, if we have only one statement but it needs a transaction, due
463 // to multiple BindingParams, we will wrap it in one.
464 for (uint32_t i = 0, transactionsCount = 0; i < mStatements.Length(); ++i) {
465 transactionsCount += mStatements[i].needsTransaction();
466 if (transactionsCount > 1) {
467 return true;
468 }
469 }
470 return false;
471}
472
473////////////////////////////////////////////////////////////////////////////////
474//// mozIStoragePendingStatement
475
476NS_IMETHODIMPnsresult
477AsyncExecuteStatements::Cancel() {
478#ifdef DEBUG1
479 bool onCallingThread = false;
480 (void)mCallingThread->IsOnCurrentThread(&onCallingThread);
481 NS_ASSERTION(onCallingThread, "Not canceling from the calling thread!")do { if (!(onCallingThread)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "Not canceling from the calling thread!", "onCallingThread"
, "./../../storage/mozStorageAsyncStatementExecution.cpp", 481
); MOZ_PretendNoReturn(); } } while (0)
;
482#endif
483
484 // If we have already canceled, we have an error, but always indicate that
485 // we are trying to cancel.
486 NS_ENSURE_FALSE(mCancelRequested, NS_ERROR_UNEXPECTED)do { if ((__builtin_expect(!!(!(!(mCancelRequested))), 0))) {
NS_DebugBreak(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "!(mCancelRequested)"
") failed", nullptr, "./../../storage/mozStorageAsyncStatementExecution.cpp"
, 486); return NS_ERROR_UNEXPECTED; } } while (false)
;
487
488 {
489 MutexAutoLock lockedScope(mMutex);
490
491 // We need to indicate that we want to try and cancel now.
492 mCancelRequested = true;
493 }
494
495 return NS_OK;
496}
497
498////////////////////////////////////////////////////////////////////////////////
499//// nsIRunnable
500
501NS_IMETHODIMPnsresult
502AsyncExecuteStatements::Run() {
503 MOZ_ASSERT(mConnection->isConnectionReadyOnThisThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mConnection->isConnectionReadyOnThisThread())>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(mConnection->isConnectionReadyOnThisThread()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("mConnection->isConnectionReadyOnThisThread()"
, "./../../storage/mozStorageAsyncStatementExecution.cpp", 503
); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mConnection->isConnectionReadyOnThisThread()"
")"); do { MOZ_CrashSequence(__null, 503); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
504
505 // Do not run if we have been canceled.
506 {
507 MutexAutoLock lockedScope(mMutex);
508 if (mCancelRequested) mState = CANCELED;
509 }
510 if (mState == CANCELED) return notifyComplete();
511
512 if (statementsNeedTransaction()) {
513 SQLiteMutexAutoLock lockedScope(mDBMutex);
514 if (!mConnection->transactionInProgress(lockedScope, mNativeConnection)) {
515 if (NS_SUCCEEDED(mConnection->beginTransactionInternal(((bool)(__builtin_expect(!!(!NS_FAILED_impl(mConnection->beginTransactionInternal
( lockedScope, mNativeConnection, mozIStorageConnection::TRANSACTION_IMMEDIATE
))), 1)))
516 lockedScope, mNativeConnection,((bool)(__builtin_expect(!!(!NS_FAILED_impl(mConnection->beginTransactionInternal
( lockedScope, mNativeConnection, mozIStorageConnection::TRANSACTION_IMMEDIATE
))), 1)))
517 mozIStorageConnection::TRANSACTION_IMMEDIATE))((bool)(__builtin_expect(!!(!NS_FAILED_impl(mConnection->beginTransactionInternal
( lockedScope, mNativeConnection, mozIStorageConnection::TRANSACTION_IMMEDIATE
))), 1)))
) {
518 mHasTransaction = true;
519 }
520#ifdef DEBUG1
521 else {
522 NS_WARNING("Unable to create a transaction for async execution.")NS_DebugBreak(NS_DEBUG_WARNING, "Unable to create a transaction for async execution."
, nullptr, "./../../storage/mozStorageAsyncStatementExecution.cpp"
, 522)
;
523 }
524#endif
525 }
526 }
527
528 // Execute each statement, giving the callback results if it returns any.
529 for (uint32_t i = 0; i < mStatements.Length(); i++) {
530 bool finished = (i == (mStatements.Length() - 1));
531
532 sqlite3_stmt* stmt;
533 { // lock the sqlite mutex so sqlite3_errmsg cannot change
534 SQLiteMutexAutoLock lockedScope(mDBMutex);
535
536 int rc = mStatements[i].getSqliteStatement(&stmt);
537 if (rc != SQLITE_OK0) {
538 // Set our error state.
539 mState = ERROR;
540
541 // Build the error object; can't call notifyError with the lock held
542 nsCOMPtr<mozIStorageError> errorObj(
543 new Error(rc, ::sqlite3_errmsg(mNativeConnection)));
544 {
545 // We cannot hold the DB mutex and call notifyError.
546 SQLiteMutexAutoUnlock unlockedScope(mDBMutex);
547 (void)notifyError(errorObj);
548 }
549 break;
550 }
551 }
552
553 // If we have parameters to bind, bind them, execute, and process.
554 if (mStatements[i].hasParametersToBeBound()) {
555 if (!bindExecuteAndProcessStatement(mStatements[i], finished)) break;
556 }
557 // Otherwise, just execute and process the statement.
558 else if (!executeAndProcessStatement(mStatements[i], finished)) {
559 break;
560 }
561 }
562
563 // If we still have results that we haven't notified about, take care of
564 // them now.
565 if (mResultSet) (void)notifyResults();
566
567 // Notify about completion
568 return notifyComplete();
569}
570
571} // namespace storage
572} // namespace mozilla