Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/storage/StorageBaseStatementInternal.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2
 * vim: sw=2 ts=2 sts=2 expandtab
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 "StorageBaseStatementInternal.h"
8
9
#include "nsProxyRelease.h"
10
11
#include "mozStorageBindingParamsArray.h"
12
#include "mozStorageStatementData.h"
13
#include "mozStorageAsyncStatementExecution.h"
14
15
namespace mozilla {
16
namespace storage {
17
18
////////////////////////////////////////////////////////////////////////////////
19
//// Local Classes
20
21
/**
22
 * Used to finalize an asynchronous statement on the background thread.
23
 */
24
class AsyncStatementFinalizer : public Runnable
25
{
26
public:
27
  /**
28
   * Constructor for the event.
29
   *
30
   * @param aStatement
31
   *        We need the AsyncStatement to be able to get at the sqlite3_stmt;
32
   *        we only access/create it on the async thread.
33
   * @param aConnection
34
   *        We need the connection to know what thread to release the statement
35
   *        on.  We release the statement on that thread since releasing the
36
   *        statement might end up releasing the connection too.
37
   */
38
  AsyncStatementFinalizer(StorageBaseStatementInternal* aStatement,
39
                          Connection* aConnection)
40
    : Runnable("storage::AsyncStatementFinalizer")
41
    , mStatement(aStatement)
42
    , mConnection(aConnection)
43
0
  {
44
0
  }
45
46
  NS_IMETHOD Run() override
47
0
  {
48
0
    if (mStatement->mAsyncStatement) {
49
0
      sqlite3_finalize(mStatement->mAsyncStatement);
50
0
      mStatement->mAsyncStatement = nullptr;
51
0
    }
52
0
53
0
    nsCOMPtr<nsIThread> targetThread(mConnection->threadOpenedOn);
54
0
    NS_ProxyRelease(
55
0
      "AsyncStatementFinalizer::mStatement", targetThread, mStatement.forget());
56
0
    return NS_OK;
57
0
  }
58
private:
59
  RefPtr<StorageBaseStatementInternal> mStatement;
60
  RefPtr<Connection> mConnection;
61
};
62
63
/**
64
 * Finalize a sqlite3_stmt on the background thread for a statement whose
65
 * destructor was invoked and the statement was non-null.
66
 */
67
class LastDitchSqliteStatementFinalizer : public Runnable
68
{
69
public:
70
  /**
71
   * Event constructor.
72
   *
73
   * @param aConnection
74
   *        Used to keep the connection alive.  If we failed to do this, it
75
   *        is possible that the statement going out of scope invoking us
76
   *        might have the last reference to the connection and so trigger
77
   *        an attempt to close the connection which is doomed to fail
78
   *        (because the asynchronous execution thread must exist which will
79
   *        trigger the failure case).
80
   * @param aStatement
81
   *        The sqlite3_stmt to finalize.  This object takes ownership /
82
   *        responsibility for the instance and all other references to it
83
   *        should be forgotten.
84
   */
85
  LastDitchSqliteStatementFinalizer(RefPtr<Connection>& aConnection,
86
                                    sqlite3_stmt* aStatement)
87
    : Runnable("storage::LastDitchSqliteStatementFinalizer")
88
    , mConnection(aConnection)
89
    , mAsyncStatement(aStatement)
90
0
  {
91
0
    MOZ_ASSERT(aConnection, "You must provide a Connection");
92
0
  }
93
94
  NS_IMETHOD Run() override
95
0
  {
96
0
    (void)::sqlite3_finalize(mAsyncStatement);
97
0
    mAsyncStatement = nullptr;
98
0
99
0
    nsCOMPtr<nsIThread> target(mConnection->threadOpenedOn);
100
0
    (void)::NS_ProxyRelease(
101
0
      "LastDitchSqliteStatementFinalizer::mConnection",
102
0
      target, mConnection.forget());
103
0
    return NS_OK;
104
0
  }
105
private:
106
  RefPtr<Connection> mConnection;
107
  sqlite3_stmt *mAsyncStatement;
108
};
109
110
////////////////////////////////////////////////////////////////////////////////
111
//// StorageBaseStatementInternal
112
113
StorageBaseStatementInternal::StorageBaseStatementInternal()
114
: mNativeConnection(nullptr)
115
, mAsyncStatement(nullptr)
116
0
{
117
0
}
118
119
void
120
StorageBaseStatementInternal::asyncFinalize()
121
0
{
122
0
  nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget();
123
0
  if (target) {
124
0
    // Attempt to finalize asynchronously
125
0
    nsCOMPtr<nsIRunnable> event =
126
0
      new AsyncStatementFinalizer(this, mDBConnection);
127
0
128
0
    // Dispatch. Note that dispatching can fail, typically if
129
0
    // we have a race condition with asyncClose(). It's ok,
130
0
    // let asyncClose() win.
131
0
    (void)target->Dispatch(event, NS_DISPATCH_NORMAL);
132
0
  }
133
0
  // If we cannot get the background thread,
134
0
  // mozStorageConnection::AsyncClose() has already been called and
135
0
  // the statement either has been or will be cleaned up by
136
0
  // internalClose().
137
0
}
138
139
void
140
StorageBaseStatementInternal::destructorAsyncFinalize()
141
0
{
142
0
  if (!mAsyncStatement)
143
0
    return;
144
0
145
0
  bool isOwningThread = false;
146
0
  (void)mDBConnection->threadOpenedOn->IsOnCurrentThread(&isOwningThread);
147
0
  if (isOwningThread) {
148
0
    // If we are the owning thread (currently that means we're also the
149
0
    // main thread), then we can get the async target and just dispatch
150
0
    // to it.
151
0
    nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget();
152
0
    if (target) {
153
0
      nsCOMPtr<nsIRunnable> event =
154
0
        new LastDitchSqliteStatementFinalizer(mDBConnection, mAsyncStatement);
155
0
      (void)target->Dispatch(event, NS_DISPATCH_NORMAL);
156
0
    }
157
0
  } else {
158
0
    // If we're not the owning thread, assume we're the async thread, and
159
0
    // just run the statement.
160
0
    nsCOMPtr<nsIRunnable> event =
161
0
      new LastDitchSqliteStatementFinalizer(mDBConnection, mAsyncStatement);
162
0
    (void)event->Run();
163
0
  }
164
0
165
0
166
0
  // We might not be able to dispatch to the background thread,
167
0
  // presumably because it is being shutdown. Since said shutdown will
168
0
  // finalize the statement, we just need to clean-up around here.
169
0
  mAsyncStatement = nullptr;
170
0
}
171
172
NS_IMETHODIMP
173
StorageBaseStatementInternal::NewBindingParamsArray(
174
  mozIStorageBindingParamsArray **_array
175
)
176
0
{
177
0
  nsCOMPtr<mozIStorageBindingParamsArray> array = new BindingParamsArray(this);
178
0
  NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);
179
0
180
0
  array.forget(_array);
181
0
  return NS_OK;
182
0
}
183
184
NS_IMETHODIMP
185
StorageBaseStatementInternal::ExecuteAsync(
186
  mozIStorageStatementCallback *aCallback,
187
  mozIStoragePendingStatement **_stmt
188
)
189
0
{
190
0
  // We used to call Connection::ExecuteAsync but it takes a
191
0
  // mozIStorageBaseStatement signature because it is also a public API.  Since
192
0
  // our 'this' has no static concept of mozIStorageBaseStatement and Connection
193
0
  // would just QI it back across to a StorageBaseStatementInternal and the
194
0
  // actual logic is very simple, we now roll our own.
195
0
  nsTArray<StatementData> stmts(1);
196
0
  StatementData data;
197
0
  nsresult rv = getAsynchronousStatementData(data);
198
0
  NS_ENSURE_SUCCESS(rv, rv);
199
0
  NS_ENSURE_TRUE(stmts.AppendElement(data), NS_ERROR_OUT_OF_MEMORY);
200
0
201
0
  // Dispatch to the background
202
0
  return AsyncExecuteStatements::execute(stmts, mDBConnection,
203
0
                                         mNativeConnection, aCallback, _stmt);
204
0
}
205
206
NS_IMETHODIMP
207
StorageBaseStatementInternal::EscapeStringForLIKE(
208
  const nsAString &aValue,
209
  const char16_t aEscapeChar,
210
  nsAString &_escapedString
211
)
212
0
{
213
0
  const char16_t MATCH_ALL('%');
214
0
  const char16_t MATCH_ONE('_');
215
0
216
0
  _escapedString.Truncate(0);
217
0
218
0
  for (uint32_t i = 0; i < aValue.Length(); i++) {
219
0
    if (aValue[i] == aEscapeChar || aValue[i] == MATCH_ALL ||
220
0
        aValue[i] == MATCH_ONE) {
221
0
      _escapedString += aEscapeChar;
222
0
    }
223
0
    _escapedString += aValue[i];
224
0
  }
225
0
  return NS_OK;
226
0
}
227
228
} // namespace storage
229
} // namespace mozilla