LCOV - code coverage report
Current view: top level - storage - mozStorageHelper.h (source / functions) Hit Total Coverage
Test: output.info Lines: 35 60 58.3 %
Date: 2017-07-14 16:53:18 Functions: 5 7 71.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #ifndef MOZSTORAGEHELPER_H
       7             : #define MOZSTORAGEHELPER_H
       8             : 
       9             : #include "nsAutoPtr.h"
      10             : #include "nsStringGlue.h"
      11             : #include "mozilla/DebugOnly.h"
      12             : #include "nsIConsoleService.h"
      13             : #include "nsIScriptError.h"
      14             : 
      15             : #include "mozIStorageAsyncConnection.h"
      16             : #include "mozIStorageConnection.h"
      17             : #include "mozIStorageStatement.h"
      18             : #include "mozIStoragePendingStatement.h"
      19             : #include "nsError.h"
      20             : #include "nsIXPConnect.h"
      21             : 
      22             : /**
      23             :  * This class wraps a transaction inside a given C++ scope, guaranteeing that
      24             :  * the transaction will be completed even if you have an exception or
      25             :  * return early.
      26             :  *
      27             :  * A common use is to create an instance with aCommitOnComplete = false (rollback),
      28             :  * then call Commit() on this object manually when your function completes
      29             :  * successfully.
      30             :  *
      31             :  * @note nested transactions are not supported by Sqlite, so if a transaction
      32             :  * is already in progress, this object does nothing.  Note that in this case,
      33             :  * you may not get the transaction type you asked for, and you won't be able
      34             :  * to rollback.
      35             :  *
      36             :  * @param aConnection
      37             :  *        The connection to create the transaction on.
      38             :  * @param aCommitOnComplete
      39             :  *        Controls whether the transaction is committed or rolled back when
      40             :  *        this object goes out of scope.
      41             :  * @param aType [optional]
      42             :  *        The transaction type, as defined in mozIStorageConnection.  Defaults
      43             :  *        to TRANSACTION_DEFERRED.
      44             :  * @param aAsyncCommit [optional]
      45             :  *        Whether commit should be executed asynchronously on the helper thread.
      46             :  *        This is a special option introduced as an interim solution to reduce
      47             :  *        main-thread fsyncs in Places.  Can only be used on main-thread.
      48             :  *
      49             :  *        WARNING: YOU SHOULD _NOT_ WRITE NEW MAIN-THREAD CODE USING THIS!
      50             :  *
      51             :  *        Notice that async commit might cause synchronous statements to fail
      52             :  *        with SQLITE_BUSY.  A possible mitigation strategy is to use
      53             :  *        PRAGMA busy_timeout, but notice that might cause main-thread jank.
      54             :  *        Finally, if the database is using WAL journaling mode, other
      55             :  *        connections won't see the changes done in async committed transactions
      56             :  *        until commit is complete.
      57             :  *
      58             :  *        For all of the above reasons, this should only be used as an interim
      59             :  *        solution and avoided completely if possible.
      60             :  */
      61             : class mozStorageTransaction
      62             : {
      63             : public:
      64           6 :   mozStorageTransaction(mozIStorageConnection* aConnection,
      65             :                         bool aCommitOnComplete,
      66             :                         int32_t aType = mozIStorageConnection::TRANSACTION_DEFERRED,
      67             :                         bool aAsyncCommit = false)
      68           6 :     : mConnection(aConnection),
      69             :       mHasTransaction(false),
      70             :       mCommitOnComplete(aCommitOnComplete),
      71             :       mCompleted(false),
      72           6 :       mAsyncCommit(aAsyncCommit)
      73             :   {
      74           6 :     if (mConnection) {
      75          10 :       nsAutoCString query("BEGIN");
      76           5 :       switch(aType) {
      77             :         case mozIStorageConnection::TRANSACTION_IMMEDIATE:
      78           1 :           query.AppendLiteral(" IMMEDIATE");
      79           1 :           break;
      80             :         case mozIStorageConnection::TRANSACTION_EXCLUSIVE:
      81           0 :           query.AppendLiteral(" EXCLUSIVE");
      82           0 :           break;
      83             :         case mozIStorageConnection::TRANSACTION_DEFERRED:
      84           4 :           query.AppendLiteral(" DEFERRED");
      85           4 :           break;
      86             :         default:
      87           0 :           MOZ_ASSERT(false, "Unknown transaction type");
      88             :       }
      89             :       // If a transaction is already in progress, this will fail, since Sqlite
      90             :       // doesn't support nested transactions.
      91           5 :       mHasTransaction = NS_SUCCEEDED(mConnection->ExecuteSimpleSQL(query));
      92             :     }
      93           6 :   }
      94             : 
      95           6 :   ~mozStorageTransaction()
      96           6 :   {
      97           6 :     if (mConnection && mHasTransaction && !mCompleted) {
      98           1 :       if (mCommitOnComplete) {
      99           2 :         mozilla::DebugOnly<nsresult> rv = Commit();
     100           1 :         NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
     101             :                              "A transaction didn't commit correctly");
     102             :       }
     103             :       else {
     104           0 :         mozilla::DebugOnly<nsresult> rv = Rollback();
     105           0 :         NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
     106             :                              "A transaction didn't rollback correctly");
     107             :       }
     108             :     }
     109           6 :   }
     110             : 
     111             :   /**
     112             :    * Commits the transaction if one is in progress. If one is not in progress,
     113             :    * this is a NOP since the actual owner of the transaction outside of our
     114             :    * scope is in charge of finally committing or rolling back the transaction.
     115             :    */
     116           5 :   nsresult Commit()
     117             :   {
     118           5 :     if (!mConnection || mCompleted || !mHasTransaction)
     119           0 :       return NS_OK;
     120           5 :     mCompleted = true;
     121             : 
     122             :     // TODO (bug 559659): this might fail with SQLITE_BUSY, but we don't handle
     123             :     // it, thus the transaction might stay open until the next COMMIT.
     124             :     nsresult rv;
     125           5 :     if (mAsyncCommit) {
     126           0 :       nsCOMPtr<mozIStoragePendingStatement> ps;
     127           0 :       rv = mConnection->ExecuteSimpleSQLAsync(NS_LITERAL_CSTRING("COMMIT"),
     128           0 :                                               nullptr, getter_AddRefs(ps));
     129             :     }
     130             :     else {
     131           5 :       rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("COMMIT"));
     132             :     }
     133             : 
     134           5 :     if (NS_SUCCEEDED(rv))
     135           5 :       mHasTransaction = false;
     136             : 
     137           5 :     return rv;
     138             :   }
     139             : 
     140             :   /**
     141             :    * Rolls back the transaction if one is in progress. If one is not in progress,
     142             :    * this is a NOP since the actual owner of the transaction outside of our
     143             :    * scope is in charge of finally rolling back the transaction.
     144             :    */
     145           0 :   nsresult Rollback()
     146             :   {
     147           0 :     if (!mConnection || mCompleted || !mHasTransaction)
     148           0 :       return NS_OK;
     149           0 :     mCompleted = true;
     150             : 
     151             :     // TODO (bug 1062823): from Sqlite 3.7.11 on, rollback won't ever return
     152             :     // a busy error, so this handling can be removed.
     153           0 :     nsresult rv = NS_OK;
     154           0 :     do {
     155           0 :       rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("ROLLBACK"));
     156           0 :       if (rv == NS_ERROR_STORAGE_BUSY)
     157           0 :         (void)PR_Sleep(PR_INTERVAL_NO_WAIT);
     158           0 :     } while (rv == NS_ERROR_STORAGE_BUSY);
     159             : 
     160           0 :     if (NS_SUCCEEDED(rv))
     161           0 :       mHasTransaction = false;
     162             : 
     163           0 :     return rv;
     164             :   }
     165             : 
     166             : protected:
     167             :   nsCOMPtr<mozIStorageConnection> mConnection;
     168             :   bool mHasTransaction;
     169             :   bool mCommitOnComplete;
     170             :   bool mCompleted;
     171             :   bool mAsyncCommit;
     172             : };
     173             : 
     174             : /**
     175             :  * This class wraps a statement so that it is guaraneed to be reset when
     176             :  * this object goes out of scope.
     177             :  *
     178             :  * Note that this always just resets the statement. If the statement doesn't
     179             :  * need resetting, the reset operation is inexpensive.
     180             :  */
     181             : class MOZ_STACK_CLASS mozStorageStatementScoper
     182             : {
     183             : public:
     184          14 :   explicit mozStorageStatementScoper(mozIStorageStatement* aStatement)
     185          14 :       : mStatement(aStatement)
     186             :   {
     187          14 :   }
     188          14 :   ~mozStorageStatementScoper()
     189          14 :   {
     190          14 :     if (mStatement)
     191          14 :       mStatement->Reset();
     192          14 :   }
     193             : 
     194             :   /**
     195             :    * Call this to make the statement not reset. You might do this if you know
     196             :    * that the statement has been reset.
     197             :    */
     198           0 :   void Abandon()
     199             :   {
     200           0 :     mStatement = nullptr;
     201           0 :   }
     202             : 
     203             : protected:
     204             :   nsCOMPtr<mozIStorageStatement> mStatement;
     205             : };
     206             : 
     207             : // Use this to make queries uniquely identifiable in telemetry
     208             : // statistics, especially PRAGMAs.  We don't include __LINE__ so that
     209             : // queries are stable in the face of source code changes.
     210             : #define MOZ_STORAGE_UNIQUIFY_QUERY_STR "/* " __FILE__ " */ "
     211             : 
     212             : // Use this to show a console warning when using deprecated methods.
     213             : #define WARN_DEPRECATED()                                                          \
     214             :   PR_BEGIN_MACRO                                                                   \
     215             :                                                                                    \
     216             :   if (NS_IsMainThread()) {                                                         \
     217             :     nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);  \
     218             :                                                                                    \
     219             :     if (cs) {                                                                      \
     220             :       nsCString msg(__FUNCTION__);                                                 \
     221             :       msg.AppendLiteral(" is deprecated and will be removed soon.");               \
     222             :                                                                                    \
     223             :       nsCOMPtr<nsIScriptError> e = do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);   \
     224             :       if (e && NS_SUCCEEDED(e->Init(NS_ConvertUTF8toUTF16(msg), EmptyString(),     \
     225             :                                     EmptyString(), 0, 0,                           \
     226             :                                     nsIScriptError::errorFlag, "Storage"))) {      \
     227             :         cs->LogMessage(e);                                                         \
     228             :       }                                                                            \
     229             :     }                                                                              \
     230             :   }                                                                                \
     231             :   if (NS_IsMainThread()) {                                                         \
     232             :     nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());            \
     233             :     if (xpc) {                                                                     \
     234             :       mozilla::Unused << xpc->DebugDumpJSStack(false, false, false);               \
     235             :     }                                                                              \
     236             :   }                                                                                \
     237             :   MOZ_ASSERT(false, "You are trying to use a deprecated mozStorage method. "       \
     238             :                     "Check error message in console to identify the method name.");\
     239             :   PR_END_MACRO
     240             : 
     241             : #endif /* MOZSTORAGEHELPER_H */

Generated by: LCOV version 1.13