| 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 |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 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" |
| 21 | extern mozilla::LazyLogModule gStorageLog; |
| 22 | |
| 23 | namespace mozilla { |
| 24 | namespace 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 */ |
| 44 | nsresult 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 | |
| 74 | AsyncExecuteStatements::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 | |
| 94 | AsyncExecuteStatements::~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 | |
| 103 | bool 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 | |
| 118 | bool 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 | |
| 160 | bool 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 | |
| 216 | bool 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 | |
| 288 | nsresult 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 | |
| 323 | nsresult 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 | |
| 371 | nsresult 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 | |
| 385 | nsresult 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 | |
| 398 | nsresult 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 | |
| 413 | nsresult 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 | |
| 427 | nsresult 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 | |
| 443 | nsresult 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 | |
| 457 | NS_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 | |
| 460 | bool 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 | |
| 476 | NS_IMETHODIMPnsresult |
| 477 | AsyncExecuteStatements::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 | |
| 501 | NS_IMETHODIMPnsresult |
| 502 | AsyncExecuteStatements::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 |