LCOV - code coverage report
Current view: top level - storage - mozStorageAsyncStatementExecution.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 140 221 63.3 %
Date: 2017-07-14 16:53:18 Functions: 17 21 81.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
       2             :  * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "nsAutoPtr.h"
       8             : 
       9             : #include "sqlite3.h"
      10             : 
      11             : #include "mozIStorageStatementCallback.h"
      12             : #include "mozStorageBindingParams.h"
      13             : #include "mozStorageHelper.h"
      14             : #include "mozStorageResultSet.h"
      15             : #include "mozStorageRow.h"
      16             : #include "mozStorageConnection.h"
      17             : #include "mozStorageError.h"
      18             : #include "mozStoragePrivateHelpers.h"
      19             : #include "mozStorageStatementData.h"
      20             : #include "mozStorageAsyncStatementExecution.h"
      21             : 
      22             : #include "mozilla/DebugOnly.h"
      23             : #include "mozilla/Telemetry.h"
      24             : 
      25             : namespace mozilla {
      26             : namespace storage {
      27             : 
      28             : /**
      29             :  * The following constants help batch rows into result sets.
      30             :  * MAX_MILLISECONDS_BETWEEN_RESULTS was chosen because any user-based task that
      31             :  * takes less than 200 milliseconds is considered to feel instantaneous to end
      32             :  * users.  MAX_ROWS_PER_RESULT was arbitrarily chosen to reduce the number of
      33             :  * dispatches to calling thread, while also providing reasonably-sized sets of
      34             :  * data for consumers.  Both of these constants are used because we assume that
      35             :  * consumers are trying to avoid blocking their execution thread for long
      36             :  * periods of time, and dispatching many small events to the calling thread will
      37             :  * end up blocking it.
      38             :  */
      39             : #define MAX_MILLISECONDS_BETWEEN_RESULTS 75
      40             : #define MAX_ROWS_PER_RESULT 15
      41             : 
      42             : ////////////////////////////////////////////////////////////////////////////////
      43             : //// AsyncExecuteStatements
      44             : 
      45             : /* static */
      46             : nsresult
      47          11 : AsyncExecuteStatements::execute(StatementDataArray &aStatements,
      48             :                                 Connection *aConnection,
      49             :                                 sqlite3 *aNativeConnection,
      50             :                                 mozIStorageStatementCallback *aCallback,
      51             :                                 mozIStoragePendingStatement **_stmt)
      52             : {
      53             :   // Create our event to run in the background
      54             :   RefPtr<AsyncExecuteStatements> event =
      55             :     new AsyncExecuteStatements(aStatements, aConnection, aNativeConnection,
      56          22 :                                aCallback);
      57          11 :   NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
      58             : 
      59             :   // Dispatch it to the background
      60          11 :   nsIEventTarget *target = aConnection->getAsyncExecutionTarget();
      61             : 
      62             :   // If we don't have a valid target, this is a bug somewhere else. In the past,
      63             :   // this assert found cases where a Run method would schedule a new statement
      64             :   // without checking if asyncClose had been called. The caller must prevent
      65             :   // that from happening or, if the work is not critical, just avoid creating
      66             :   // the new statement during shutdown. See bug 718449 for an example.
      67          11 :   MOZ_ASSERT(target);
      68          11 :   if (!target) {
      69           0 :     return NS_ERROR_NOT_AVAILABLE;
      70             :   }
      71             : 
      72          11 :   nsresult rv = target->Dispatch(event, NS_DISPATCH_NORMAL);
      73          11 :   NS_ENSURE_SUCCESS(rv, rv);
      74             : 
      75             :   // Return it as the pending statement object and track it.
      76          11 :   event.forget(_stmt);
      77          11 :   return NS_OK;
      78             : }
      79             : 
      80          11 : AsyncExecuteStatements::AsyncExecuteStatements(StatementDataArray &aStatements,
      81             :                                                Connection *aConnection,
      82             :                                                sqlite3 *aNativeConnection,
      83          11 :                                                mozIStorageStatementCallback *aCallback)
      84             : : mConnection(aConnection)
      85             : , mNativeConnection(aNativeConnection)
      86             : , mHasTransaction(false)
      87             : , mCallback(aCallback)
      88          22 : , mCallingThread(::do_GetCurrentThread())
      89             : , mMaxWait(TimeDuration::FromMilliseconds(MAX_MILLISECONDS_BETWEEN_RESULTS))
      90             : , mIntervalStart(TimeStamp::Now())
      91             : , mState(PENDING)
      92             : , mCancelRequested(false)
      93             : , mMutex(aConnection->sharedAsyncExecutionMutex)
      94             : , mDBMutex(aConnection->sharedDBMutex)
      95          33 : , mRequestStartDate(TimeStamp::Now())
      96             : {
      97          11 :   (void)mStatements.SwapElements(aStatements);
      98          11 :   NS_ASSERTION(mStatements.Length(), "We weren't given any statements!");
      99          11 : }
     100             : 
     101           6 : AsyncExecuteStatements::~AsyncExecuteStatements()
     102             : {
     103           3 :   MOZ_ASSERT(!mCallback, "Never called the Completion callback!");
     104           3 :   MOZ_ASSERT(!mHasTransaction, "There should be no transaction at this point");
     105           3 :   if (mCallback) {
     106             :     NS_ProxyRelease("AsyncExecuteStatements::mCallback", mCallingThread,
     107           0 :                     mCallback.forget());
     108             :   }
     109           3 : }
     110             : 
     111             : bool
     112           3 : AsyncExecuteStatements::shouldNotify()
     113             : {
     114             : #ifdef DEBUG
     115           3 :   mMutex.AssertNotCurrentThreadOwns();
     116             : 
     117           3 :   bool onCallingThread = false;
     118           3 :   (void)mCallingThread->IsOnCurrentThread(&onCallingThread);
     119           3 :   NS_ASSERTION(onCallingThread, "runEvent not running on the calling thread!");
     120             : #endif
     121             : 
     122             :   // We do not need to acquire mMutex here because it can only ever be written
     123             :   // to on the calling thread, and the only thread that can call us is the
     124             :   // calling thread, so we know that our access is serialized.
     125           3 :   return !mCancelRequested;
     126             : }
     127             : 
     128             : bool
     129           6 : AsyncExecuteStatements::bindExecuteAndProcessStatement(StatementData &aData,
     130             :                                                        bool aLastStatement)
     131             : {
     132           6 :   mMutex.AssertNotCurrentThreadOwns();
     133             : 
     134           6 :   sqlite3_stmt *aStatement = nullptr;
     135             :   // This cannot fail; we are only called if it's available.
     136           6 :   (void)aData.getSqliteStatement(&aStatement);
     137           6 :   NS_ASSERTION(aStatement, "You broke the code; do not call here like that!");
     138           6 :   BindingParamsArray *paramsArray(aData);
     139             : 
     140             :   // Iterate through all of our parameters, bind them, and execute.
     141           6 :   bool continueProcessing = true;
     142           6 :   BindingParamsArray::iterator itr = paramsArray->begin();
     143           6 :   BindingParamsArray::iterator end = paramsArray->end();
     144          18 :   while (itr != end && continueProcessing) {
     145             :     // Bind the data to our statement.
     146             :     nsCOMPtr<IStorageBindingParamsInternal> bindingInternal =
     147          12 :       do_QueryInterface(*itr);
     148          12 :     nsCOMPtr<mozIStorageError> error = bindingInternal->bind(aStatement);
     149           6 :     if (error) {
     150             :       // Set our error state.
     151           0 :       mState = ERROR;
     152             : 
     153             :       // And notify.
     154           0 :       (void)notifyError(error);
     155           0 :       return false;
     156             :     }
     157             : 
     158             :     // Advance our iterator, execute, and then process the statement.
     159           6 :     itr++;
     160           6 :     bool lastStatement = aLastStatement && itr == end;
     161           6 :     continueProcessing = executeAndProcessStatement(aStatement, lastStatement);
     162             : 
     163             :     // Always reset our statement.
     164           6 :     (void)::sqlite3_reset(aStatement);
     165             :   }
     166             : 
     167           6 :   return continueProcessing;
     168             : }
     169             : 
     170             : bool
     171          11 : AsyncExecuteStatements::executeAndProcessStatement(sqlite3_stmt *aStatement,
     172             :                                                    bool aLastStatement)
     173             : {
     174          11 :   mMutex.AssertNotCurrentThreadOwns();
     175             : 
     176             :   // Execute our statement
     177             :   bool hasResults;
     178          15 :   do {
     179          15 :     hasResults = executeStatement(aStatement);
     180             : 
     181             :     // If we had an error, bail.
     182          15 :     if (mState == ERROR)
     183           0 :       return false;
     184             : 
     185             :     // If we have been canceled, there is no point in going on...
     186             :     {
     187          30 :       MutexAutoLock lockedScope(mMutex);
     188          15 :       if (mCancelRequested) {
     189           0 :         mState = CANCELED;
     190           0 :         return false;
     191             :       }
     192             :     }
     193             : 
     194             :     // Build our result set and notify if we got anything back and have a
     195             :     // callback to notify.
     196          19 :     if (mCallback && hasResults &&
     197           4 :         NS_FAILED(buildAndNotifyResults(aStatement))) {
     198             :       // We had an error notifying, so we notify on error and stop processing.
     199           0 :       mState = ERROR;
     200             : 
     201             :       // Notify, and stop processing statements.
     202             :       (void)notifyError(mozIStorageError::ERROR,
     203           0 :                         "An error occurred while notifying about results");
     204             : 
     205           0 :       return false;
     206             :     }
     207             :   } while (hasResults);
     208             : 
     209             : #ifndef MOZ_STORAGE_SORTWARNING_SQL_DUMP
     210          11 :   if (MOZ_LOG_TEST(gStorageLog, LogLevel::Warning))
     211             : #endif
     212             :   {
     213             :     // Check to make sure that this statement was smart about what it did.
     214           0 :     checkAndLogStatementPerformance(aStatement);
     215             :   }
     216             : 
     217             :   // If we are done, we need to set our state accordingly while we still hold
     218             :   // our mutex.  We would have already returned if we were canceled or had
     219             :   // an error at this point.
     220          11 :   if (aLastStatement)
     221          11 :     mState = COMPLETED;
     222             : 
     223          11 :   return true;
     224             : }
     225             : 
     226             : bool
     227          15 : AsyncExecuteStatements::executeStatement(sqlite3_stmt *aStatement)
     228             : {
     229          15 :   mMutex.AssertNotCurrentThreadOwns();
     230          30 :   Telemetry::AutoTimer<Telemetry::MOZ_STORAGE_ASYNC_REQUESTS_MS> finallySendExecutionDuration(mRequestStartDate);
     231             :   while (true) {
     232             :     // lock the sqlite mutex so sqlite3_errmsg cannot change
     233          15 :     SQLiteMutexAutoLock lockedScope(mDBMutex);
     234             : 
     235          15 :     int rc = mConnection->stepStatement(mNativeConnection, aStatement);
     236             :     // Stop if we have no more results.
     237          15 :     if (rc == SQLITE_DONE)
     238             :     {
     239          11 :       Telemetry::Accumulate(Telemetry::MOZ_STORAGE_ASYNC_REQUESTS_SUCCESS, true);
     240          11 :       return false;
     241             :     }
     242             : 
     243             :     // If we got results, we can return now.
     244           4 :     if (rc == SQLITE_ROW)
     245             :     {
     246           4 :       Telemetry::Accumulate(Telemetry::MOZ_STORAGE_ASYNC_REQUESTS_SUCCESS, true);
     247           4 :       return true;
     248             :     }
     249             : 
     250             :     // Some errors are not fatal, and we can handle them and continue.
     251           0 :     if (rc == SQLITE_BUSY) {
     252             :       // Don't hold the lock while we call outside our module.
     253           0 :       SQLiteMutexAutoUnlock unlockedScope(mDBMutex);
     254             : 
     255             :       // Yield, and try again
     256           0 :       (void)::PR_Sleep(PR_INTERVAL_NO_WAIT);
     257           0 :       continue;
     258             :     }
     259             : 
     260             :     // Set an error state.
     261           0 :     mState = ERROR;
     262           0 :     Telemetry::Accumulate(Telemetry::MOZ_STORAGE_ASYNC_REQUESTS_SUCCESS, false);
     263             : 
     264             :     // Construct the error message before giving up the mutex (which we cannot
     265             :     // hold during the call to notifyError).
     266             :     nsCOMPtr<mozIStorageError> errorObj(
     267           0 :       new Error(rc, ::sqlite3_errmsg(mNativeConnection))
     268           0 :     );
     269             :     // We cannot hold the DB mutex while calling notifyError.
     270           0 :     SQLiteMutexAutoUnlock unlockedScope(mDBMutex);
     271           0 :     (void)notifyError(errorObj);
     272             : 
     273             :     // Finally, indicate that we should stop processing.
     274           0 :     return false;
     275           0 :   }
     276             : }
     277             : 
     278             : nsresult
     279           4 : AsyncExecuteStatements::buildAndNotifyResults(sqlite3_stmt *aStatement)
     280             : {
     281           4 :   NS_ASSERTION(mCallback, "Trying to dispatch results without a callback!");
     282           4 :   mMutex.AssertNotCurrentThreadOwns();
     283             : 
     284             :   // Build result object if we need it.
     285           4 :   if (!mResultSet)
     286           3 :     mResultSet = new ResultSet();
     287           4 :   NS_ENSURE_TRUE(mResultSet, NS_ERROR_OUT_OF_MEMORY);
     288             : 
     289           8 :   RefPtr<Row> row(new Row());
     290           4 :   NS_ENSURE_TRUE(row, NS_ERROR_OUT_OF_MEMORY);
     291             : 
     292           4 :   nsresult rv = row->initialize(aStatement);
     293           4 :   NS_ENSURE_SUCCESS(rv, rv);
     294             : 
     295           4 :   rv = mResultSet->add(row);
     296           4 :   NS_ENSURE_SUCCESS(rv, rv);
     297             : 
     298             :   // If we have hit our maximum number of allowed results, or if we have hit
     299             :   // the maximum amount of time we want to wait for results, notify the
     300             :   // calling thread about it.
     301           4 :   TimeStamp now = TimeStamp::Now();
     302           4 :   TimeDuration delta = now - mIntervalStart;
     303           4 :   if (mResultSet->rows() >= MAX_ROWS_PER_RESULT || delta > mMaxWait) {
     304             :     // Notify the caller
     305           0 :     rv = notifyResults();
     306           0 :     if (NS_FAILED(rv))
     307           0 :       return NS_OK; // we'll try again with the next result
     308             : 
     309             :     // Reset our start time
     310           0 :     mIntervalStart = now;
     311             :   }
     312             : 
     313           4 :   return NS_OK;
     314             : }
     315             : 
     316             : nsresult
     317          11 : AsyncExecuteStatements::notifyComplete()
     318             : {
     319          11 :   mMutex.AssertNotCurrentThreadOwns();
     320          11 :   NS_ASSERTION(mState != PENDING,
     321             :                "Still in a pending state when calling Complete!");
     322             : 
     323             :   // Reset our statements before we try to commit or rollback.  If we are
     324             :   // canceling and have statements that think they have pending work, the
     325             :   // rollback will fail.
     326          22 :   for (uint32_t i = 0; i < mStatements.Length(); i++)
     327          11 :     mStatements[i].reset();
     328             : 
     329             :   // Release references to the statement data as soon as possible. If this
     330             :   // is the last reference, statements will be finalized immediately on the
     331             :   // async thread, hence avoiding several bounces between threads and possible
     332             :   // race conditions with AsyncClose().
     333          11 :   mStatements.Clear();
     334             : 
     335             :   // Handle our transaction, if we have one
     336          11 :   if (mHasTransaction) {
     337           0 :     if (mState == COMPLETED) {
     338           0 :       nsresult rv = mConnection->commitTransactionInternal(mNativeConnection);
     339           0 :       if (NS_FAILED(rv)) {
     340           0 :         mState = ERROR;
     341             :         (void)notifyError(mozIStorageError::ERROR,
     342           0 :                           "Transaction failed to commit");
     343             :       }
     344             :     }
     345             :     else {
     346             :       DebugOnly<nsresult> rv =
     347           0 :         mConnection->rollbackTransactionInternal(mNativeConnection);
     348           0 :       NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Transaction failed to rollback");
     349             :     }
     350           0 :     mHasTransaction = false;
     351             :   }
     352             : 
     353             :   // This will take ownership of mCallback and make sure its destruction will
     354             :   // happen on the owner thread.
     355          22 :   Unused << mCallingThread->Dispatch(
     356          22 :     NewRunnableMethod("AsyncExecuteStatements::notifyCompleteOnCallingThread",
     357             :                       this, &AsyncExecuteStatements::notifyCompleteOnCallingThread),
     358          11 :     NS_DISPATCH_NORMAL);
     359             : 
     360          11 :   return NS_OK;
     361             : }
     362             : 
     363             : nsresult
     364          11 : AsyncExecuteStatements::notifyCompleteOnCallingThread() {
     365          11 :   MOZ_ASSERT(mCallingThread->IsOnCurrentThread());
     366             :   // Take ownership of mCallback and responsibility for freeing it when we
     367             :   // release it.  Any notifyResultsOnCallingThread and notifyErrorOnCallingThread
     368             :   // calls on the stack spinning the event loop have guaranteed their safety by
     369             :   // creating their own strong reference before invoking the callback.
     370          22 :   nsCOMPtr<mozIStorageStatementCallback> callback = mCallback.forget();
     371          11 :   if (callback) {
     372          10 :     Unused << callback->HandleCompletion(mState);
     373             :   }
     374          22 :   return NS_OK;
     375             : }
     376             : 
     377             : nsresult
     378           0 : AsyncExecuteStatements::notifyError(int32_t aErrorCode,
     379             :                                     const char *aMessage)
     380             : {
     381           0 :   mMutex.AssertNotCurrentThreadOwns();
     382           0 :   mDBMutex.assertNotCurrentThreadOwns();
     383             : 
     384           0 :   if (!mCallback)
     385           0 :     return NS_OK;
     386             : 
     387           0 :   nsCOMPtr<mozIStorageError> errorObj(new Error(aErrorCode, aMessage));
     388           0 :   NS_ENSURE_TRUE(errorObj, NS_ERROR_OUT_OF_MEMORY);
     389             : 
     390           0 :   return notifyError(errorObj);
     391             : }
     392             : 
     393             : nsresult
     394           0 : AsyncExecuteStatements::notifyError(mozIStorageError *aError)
     395             : {
     396           0 :   mMutex.AssertNotCurrentThreadOwns();
     397           0 :   mDBMutex.assertNotCurrentThreadOwns();
     398             : 
     399           0 :   if (!mCallback)
     400           0 :     return NS_OK;
     401             : 
     402           0 :   Unused << mCallingThread->Dispatch(
     403           0 :     NewRunnableMethod<nsCOMPtr<mozIStorageError>>("AsyncExecuteStatements::notifyErrorOnCallingThread",
     404             :                                                   this, &AsyncExecuteStatements::notifyErrorOnCallingThread, aError),
     405           0 :     NS_DISPATCH_NORMAL);
     406             : 
     407           0 :   return NS_OK;
     408             : }
     409             : 
     410             : nsresult
     411           0 : AsyncExecuteStatements::notifyErrorOnCallingThread(mozIStorageError *aError) {
     412           0 :   MOZ_ASSERT(mCallingThread->IsOnCurrentThread());
     413             :   // Acquire our own strong reference so that if the callback spins a nested
     414             :   // event loop and notifyCompleteOnCallingThread is executed, forgetting
     415             :   // mCallback, we still have a valid/strong reference that won't be freed until
     416             :   // we exit.
     417           0 :   nsCOMPtr<mozIStorageStatementCallback> callback = mCallback;
     418           0 :   if (shouldNotify() && callback) {
     419           0 :     Unused << callback->HandleError(aError);
     420             :   }
     421           0 :   return NS_OK;
     422             : }
     423             : 
     424             : nsresult
     425           3 : AsyncExecuteStatements::notifyResults()
     426             : {
     427           3 :   mMutex.AssertNotCurrentThreadOwns();
     428           3 :   MOZ_ASSERT(mCallback, "notifyResults called without a callback!");
     429             : 
     430             :   // This takes ownership of mResultSet, a new one will be generated in
     431             :   // buildAndNotifyResults() when further results will arrive.
     432           6 :   Unused << mCallingThread->Dispatch(
     433           6 :     NewRunnableMethod<RefPtr<ResultSet>>("AsyncExecuteStatements::notifyResultsOnCallingThread",
     434           6 :                                          this, &AsyncExecuteStatements::notifyResultsOnCallingThread, mResultSet.forget()),
     435           3 :     NS_DISPATCH_NORMAL);
     436             : 
     437           3 :   return NS_OK;
     438             : }
     439             : 
     440             : nsresult
     441           3 : AsyncExecuteStatements::notifyResultsOnCallingThread(ResultSet *aResultSet)
     442             : {
     443           3 :   MOZ_ASSERT(mCallingThread->IsOnCurrentThread());
     444             :   // Acquire our own strong reference so that if the callback spins a nested
     445             :   // event loop and notifyCompleteOnCallingThread is executed, forgetting
     446             :   // mCallback, we still have a valid/strong reference that won't be freed until
     447             :   // we exit.
     448           6 :   nsCOMPtr<mozIStorageStatementCallback> callback = mCallback;
     449           3 :   if (shouldNotify() && callback) {
     450           3 :     Unused << callback->HandleResult(aResultSet);
     451             :   }
     452           6 :   return NS_OK;
     453             : }
     454             : 
     455         257 : NS_IMPL_ISUPPORTS(
     456             :   AsyncExecuteStatements,
     457             :   nsIRunnable,
     458             :   mozIStoragePendingStatement
     459             : )
     460             : 
     461             : bool
     462          11 : AsyncExecuteStatements::statementsNeedTransaction()
     463             : {
     464             :   // If there is more than one write statement, run in a transaction.
     465             :   // Additionally, if we have only one statement but it needs a transaction, due
     466             :   // to multiple BindingParams, we will wrap it in one.
     467          22 :   for (uint32_t i = 0, transactionsCount = 0; i < mStatements.Length(); ++i) {
     468          11 :     transactionsCount += mStatements[i].needsTransaction();
     469          11 :     if (transactionsCount > 1) {
     470           0 :       return true;
     471             :     }
     472             :   }
     473          11 :   return false;
     474             : }
     475             : 
     476             : ////////////////////////////////////////////////////////////////////////////////
     477             : //// mozIStoragePendingStatement
     478             : 
     479             : NS_IMETHODIMP
     480           0 : AsyncExecuteStatements::Cancel()
     481             : {
     482             : #ifdef DEBUG
     483           0 :   bool onCallingThread = false;
     484           0 :   (void)mCallingThread->IsOnCurrentThread(&onCallingThread);
     485           0 :   NS_ASSERTION(onCallingThread, "Not canceling from the calling thread!");
     486             : #endif
     487             : 
     488             :   // If we have already canceled, we have an error, but always indicate that
     489             :   // we are trying to cancel.
     490           0 :   NS_ENSURE_FALSE(mCancelRequested, NS_ERROR_UNEXPECTED);
     491             : 
     492             :   {
     493           0 :     MutexAutoLock lockedScope(mMutex);
     494             : 
     495             :     // We need to indicate that we want to try and cancel now.
     496           0 :     mCancelRequested = true;
     497             :   }
     498             : 
     499           0 :   return NS_OK;
     500             : }
     501             : 
     502             : ////////////////////////////////////////////////////////////////////////////////
     503             : //// nsIRunnable
     504             : 
     505             : NS_IMETHODIMP
     506          11 : AsyncExecuteStatements::Run()
     507             : {
     508          11 :   MOZ_ASSERT(mConnection->isConnectionReadyOnThisThread());
     509             : 
     510             :   // Do not run if we have been canceled.
     511             :   {
     512          22 :     MutexAutoLock lockedScope(mMutex);
     513          11 :     if (mCancelRequested)
     514           0 :       mState = CANCELED;
     515             :   }
     516          11 :   if (mState == CANCELED)
     517           0 :     return notifyComplete();
     518             : 
     519          11 :   if (statementsNeedTransaction() && mConnection->getAutocommit()) {
     520           0 :     if (NS_SUCCEEDED(mConnection->beginTransactionInternal(mNativeConnection,
     521             :                                                            mozIStorageConnection::TRANSACTION_IMMEDIATE))) {
     522           0 :       mHasTransaction = true;
     523             :     }
     524             : #ifdef DEBUG
     525             :     else {
     526           0 :       NS_WARNING("Unable to create a transaction for async execution.");
     527             :     }
     528             : #endif
     529             :   }
     530             : 
     531             :   // Execute each statement, giving the callback results if it returns any.
     532          22 :   for (uint32_t i = 0; i < mStatements.Length(); i++) {
     533          11 :     bool finished = (i == (mStatements.Length() - 1));
     534             : 
     535             :     sqlite3_stmt *stmt;
     536             :     { // lock the sqlite mutex so sqlite3_errmsg cannot change
     537          22 :       SQLiteMutexAutoLock lockedScope(mDBMutex);
     538             : 
     539          11 :       int rc = mStatements[i].getSqliteStatement(&stmt);
     540          11 :       if (rc != SQLITE_OK) {
     541             :         // Set our error state.
     542           0 :         mState = ERROR;
     543             : 
     544             :         // Build the error object; can't call notifyError with the lock held
     545             :         nsCOMPtr<mozIStorageError> errorObj(
     546           0 :           new Error(rc, ::sqlite3_errmsg(mNativeConnection))
     547           0 :         );
     548             :         {
     549             :           // We cannot hold the DB mutex and call notifyError.
     550           0 :           SQLiteMutexAutoUnlock unlockedScope(mDBMutex);
     551           0 :           (void)notifyError(errorObj);
     552             :         }
     553           0 :         break;
     554             :       }
     555             :     }
     556             : 
     557             :     // If we have parameters to bind, bind them, execute, and process.
     558          11 :     if (mStatements[i].hasParametersToBeBound()) {
     559           6 :       if (!bindExecuteAndProcessStatement(mStatements[i], finished))
     560           0 :         break;
     561             :     }
     562             :     // Otherwise, just execute and process the statement.
     563           5 :     else if (!executeAndProcessStatement(stmt, finished)) {
     564           0 :       break;
     565             :     }
     566             :   }
     567             : 
     568             :   // If we still have results that we haven't notified about, take care of
     569             :   // them now.
     570          11 :   if (mResultSet)
     571           3 :     (void)notifyResults();
     572             : 
     573             :   // Notify about completion
     574          11 :   return notifyComplete();
     575             : }
     576             : 
     577             : } // namespace storage
     578             : } // namespace mozilla

Generated by: LCOV version 1.13