Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/mozStorageHelper.h
Line
Count
Source (jump to first uncovered line)
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 "nsString.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. Uses the
43
 *        default transaction behavior for the connection if unspecified.
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
  mozStorageTransaction(mozIStorageConnection* aConnection,
65
                        bool aCommitOnComplete,
66
                        int32_t aType = mozIStorageConnection::TRANSACTION_DEFAULT,
67
                        bool aAsyncCommit = false)
68
    : mConnection(aConnection),
69
      mHasTransaction(false),
70
      mCommitOnComplete(aCommitOnComplete),
71
      mCompleted(false),
72
      mAsyncCommit(aAsyncCommit)
73
0
  {
74
0
    if (mConnection) {
75
0
      nsAutoCString query("BEGIN");
76
0
      int32_t type = aType;
77
0
      if (type == mozIStorageConnection::TRANSACTION_DEFAULT) {
78
0
        MOZ_ALWAYS_SUCCEEDS(mConnection->GetDefaultTransactionType(&type));
79
0
      }
80
0
      switch (type) {
81
0
        case mozIStorageConnection::TRANSACTION_IMMEDIATE:
82
0
          query.AppendLiteral(" IMMEDIATE");
83
0
          break;
84
0
        case mozIStorageConnection::TRANSACTION_EXCLUSIVE:
85
0
          query.AppendLiteral(" EXCLUSIVE");
86
0
          break;
87
0
        case mozIStorageConnection::TRANSACTION_DEFERRED:
88
0
          query.AppendLiteral(" DEFERRED");
89
0
          break;
90
0
        default:
91
0
          MOZ_ASSERT(false, "Unknown transaction type");
92
0
      }
93
0
      // If a transaction is already in progress, this will fail, since Sqlite
94
0
      // doesn't support nested transactions.
95
0
      mHasTransaction = NS_SUCCEEDED(mConnection->ExecuteSimpleSQL(query));
96
0
    }
97
0
  }
98
99
  ~mozStorageTransaction()
100
0
  {
101
0
    if (mConnection && mHasTransaction && !mCompleted) {
102
0
      if (mCommitOnComplete) {
103
0
        mozilla::DebugOnly<nsresult> rv = Commit();
104
0
        NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
105
0
                             "A transaction didn't commit correctly");
106
0
      }
107
0
      else {
108
0
        mozilla::DebugOnly<nsresult> rv = Rollback();
109
0
        NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
110
0
                             "A transaction didn't rollback correctly");
111
0
      }
112
0
    }
113
0
  }
114
115
  /**
116
   * Commits the transaction if one is in progress. If one is not in progress,
117
   * this is a NOP since the actual owner of the transaction outside of our
118
   * scope is in charge of finally committing or rolling back the transaction.
119
   */
120
  nsresult Commit()
121
0
  {
122
0
    if (!mConnection || mCompleted || !mHasTransaction)
123
0
      return NS_OK;
124
0
    mCompleted = true;
125
0
126
0
    // TODO (bug 559659): this might fail with SQLITE_BUSY, but we don't handle
127
0
    // it, thus the transaction might stay open until the next COMMIT.
128
0
    nsresult rv;
129
0
    if (mAsyncCommit) {
130
0
      nsCOMPtr<mozIStoragePendingStatement> ps;
131
0
      rv = mConnection->ExecuteSimpleSQLAsync(NS_LITERAL_CSTRING("COMMIT"),
132
0
                                              nullptr, getter_AddRefs(ps));
133
0
    }
134
0
    else {
135
0
      rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("COMMIT"));
136
0
    }
137
0
138
0
    if (NS_SUCCEEDED(rv))
139
0
      mHasTransaction = false;
140
0
141
0
    return rv;
142
0
  }
143
144
  /**
145
   * Rolls back the transaction if one is in progress. If one is not in progress,
146
   * this is a NOP since the actual owner of the transaction outside of our
147
   * scope is in charge of finally rolling back the transaction.
148
   */
149
  nsresult Rollback()
150
0
  {
151
0
    if (!mConnection || mCompleted || !mHasTransaction)
152
0
      return NS_OK;
153
0
    mCompleted = true;
154
0
155
0
    // TODO (bug 1062823): from Sqlite 3.7.11 on, rollback won't ever return
156
0
    // a busy error, so this handling can be removed.
157
0
    nsresult rv;
158
0
    do {
159
0
      rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("ROLLBACK"));
160
0
      if (rv == NS_ERROR_STORAGE_BUSY)
161
0
        (void)PR_Sleep(PR_INTERVAL_NO_WAIT);
162
0
    } while (rv == NS_ERROR_STORAGE_BUSY);
163
0
164
0
    if (NS_SUCCEEDED(rv))
165
0
      mHasTransaction = false;
166
0
167
0
    return rv;
168
0
  }
169
170
protected:
171
  nsCOMPtr<mozIStorageConnection> mConnection;
172
  bool mHasTransaction;
173
  bool mCommitOnComplete;
174
  bool mCompleted;
175
  bool mAsyncCommit;
176
};
177
178
/**
179
 * This class wraps a statement so that it is guaraneed to be reset when
180
 * this object goes out of scope.
181
 *
182
 * Note that this always just resets the statement. If the statement doesn't
183
 * need resetting, the reset operation is inexpensive.
184
 */
185
class MOZ_STACK_CLASS mozStorageStatementScoper
186
{
187
public:
188
  explicit mozStorageStatementScoper(mozIStorageStatement* aStatement)
189
      : mStatement(aStatement)
190
0
  {
191
0
  }
192
  ~mozStorageStatementScoper()
193
0
  {
194
0
    if (mStatement)
195
0
      mStatement->Reset();
196
0
  }
197
198
  /**
199
   * Call this to make the statement not reset. You might do this if you know
200
   * that the statement has been reset.
201
   */
202
  void Abandon()
203
0
  {
204
0
    mStatement = nullptr;
205
0
  }
206
207
protected:
208
  nsCOMPtr<mozIStorageStatement> mStatement;
209
};
210
211
// Use this to make queries uniquely identifiable in telemetry
212
// statistics, especially PRAGMAs.  We don't include __LINE__ so that
213
// queries are stable in the face of source code changes.
214
0
#define MOZ_STORAGE_UNIQUIFY_QUERY_STR "/* " __FILE__ " */ "
215
216
#endif /* MOZSTORAGEHELPER_H */