Line data Source code
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 12 : 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 4 : AsyncStatementFinalizer(StorageBaseStatementInternal* aStatement,
39 : Connection* aConnection)
40 4 : : Runnable("storage::AsyncStatementFinalizer")
41 : , mStatement(aStatement)
42 4 : , mConnection(aConnection)
43 : {
44 4 : }
45 :
46 4 : NS_IMETHOD Run() override
47 : {
48 4 : if (mStatement->mAsyncStatement) {
49 4 : sqlite3_finalize(mStatement->mAsyncStatement);
50 4 : mStatement->mAsyncStatement = nullptr;
51 : }
52 :
53 8 : nsCOMPtr<nsIThread> targetThread(mConnection->threadOpenedOn);
54 : NS_ProxyRelease(
55 4 : "AsyncStatementFinalizer::mStatement", targetThread, mStatement.forget());
56 8 : return NS_OK;
57 : }
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 6 : 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 2 : LastDitchSqliteStatementFinalizer(RefPtr<Connection>& aConnection,
86 : sqlite3_stmt* aStatement)
87 2 : : Runnable("storage::LastDitchSqliteStatementFinalizer")
88 : , mConnection(aConnection)
89 2 : , mAsyncStatement(aStatement)
90 : {
91 2 : NS_PRECONDITION(aConnection, "You must provide a Connection");
92 2 : }
93 :
94 2 : NS_IMETHOD Run() override
95 : {
96 2 : (void)::sqlite3_finalize(mAsyncStatement);
97 2 : mAsyncStatement = nullptr;
98 :
99 4 : nsCOMPtr<nsIThread> target(mConnection->threadOpenedOn);
100 : (void)::NS_ProxyRelease(
101 : "LastDitchSqliteStatementFinalizer::mConnection",
102 2 : target, mConnection.forget());
103 4 : return NS_OK;
104 : }
105 : private:
106 : RefPtr<Connection> mConnection;
107 : sqlite3_stmt *mAsyncStatement;
108 : };
109 :
110 : ////////////////////////////////////////////////////////////////////////////////
111 : //// StorageBaseStatementInternal
112 :
113 45 : StorageBaseStatementInternal::StorageBaseStatementInternal()
114 45 : : mAsyncStatement(nullptr)
115 : {
116 45 : }
117 :
118 : void
119 4 : StorageBaseStatementInternal::asyncFinalize()
120 : {
121 4 : nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget();
122 4 : if (target) {
123 : // Attempt to finalize asynchronously
124 : nsCOMPtr<nsIRunnable> event =
125 12 : new AsyncStatementFinalizer(this, mDBConnection);
126 :
127 : // Dispatch. Note that dispatching can fail, typically if
128 : // we have a race condition with asyncClose(). It's ok,
129 : // let asyncClose() win.
130 4 : (void)target->Dispatch(event, NS_DISPATCH_NORMAL);
131 : }
132 : // If we cannot get the background thread,
133 : // mozStorageConnection::AsyncClose() has already been called and
134 : // the statement either has been or will be cleaned up by
135 : // internalClose().
136 4 : }
137 :
138 : void
139 2 : StorageBaseStatementInternal::destructorAsyncFinalize()
140 : {
141 2 : if (!mAsyncStatement)
142 0 : return;
143 :
144 2 : bool isOwningThread = false;
145 2 : (void)mDBConnection->threadOpenedOn->IsOnCurrentThread(&isOwningThread);
146 2 : if (isOwningThread) {
147 : // If we are the owning thread (currently that means we're also the
148 : // main thread), then we can get the async target and just dispatch
149 : // to it.
150 0 : nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget();
151 0 : if (target) {
152 : nsCOMPtr<nsIRunnable> event =
153 0 : new LastDitchSqliteStatementFinalizer(mDBConnection, mAsyncStatement);
154 0 : (void)target->Dispatch(event, NS_DISPATCH_NORMAL);
155 : }
156 : } else {
157 : // If we're not the owning thread, assume we're the async thread, and
158 : // just run the statement.
159 : nsCOMPtr<nsIRunnable> event =
160 4 : new LastDitchSqliteStatementFinalizer(mDBConnection, mAsyncStatement);
161 2 : (void)event->Run();
162 : }
163 :
164 :
165 : // We might not be able to dispatch to the background thread,
166 : // presumably because it is being shutdown. Since said shutdown will
167 : // finalize the statement, we just need to clean-up around here.
168 2 : mAsyncStatement = nullptr;
169 : }
170 :
171 : NS_IMETHODIMP
172 19 : StorageBaseStatementInternal::NewBindingParamsArray(
173 : mozIStorageBindingParamsArray **_array
174 : )
175 : {
176 38 : nsCOMPtr<mozIStorageBindingParamsArray> array = new BindingParamsArray(this);
177 19 : NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);
178 :
179 19 : array.forget(_array);
180 19 : return NS_OK;
181 : }
182 :
183 : NS_IMETHODIMP
184 8 : StorageBaseStatementInternal::ExecuteAsync(
185 : mozIStorageStatementCallback *aCallback,
186 : mozIStoragePendingStatement **_stmt
187 : )
188 : {
189 : // We used to call Connection::ExecuteAsync but it takes a
190 : // mozIStorageBaseStatement signature because it is also a public API. Since
191 : // our 'this' has no static concept of mozIStorageBaseStatement and Connection
192 : // would just QI it back across to a StorageBaseStatementInternal and the
193 : // actual logic is very simple, we now roll our own.
194 16 : nsTArray<StatementData> stmts(1);
195 16 : StatementData data;
196 8 : nsresult rv = getAsynchronousStatementData(data);
197 8 : NS_ENSURE_SUCCESS(rv, rv);
198 8 : NS_ENSURE_TRUE(stmts.AppendElement(data), NS_ERROR_OUT_OF_MEMORY);
199 :
200 : // Dispatch to the background
201 8 : return AsyncExecuteStatements::execute(stmts, mDBConnection,
202 8 : mNativeConnection, aCallback, _stmt);
203 : }
204 :
205 : NS_IMETHODIMP
206 1 : StorageBaseStatementInternal::EscapeStringForLIKE(
207 : const nsAString &aValue,
208 : const char16_t aEscapeChar,
209 : nsAString &_escapedString
210 : )
211 : {
212 1 : const char16_t MATCH_ALL('%');
213 1 : const char16_t MATCH_ONE('_');
214 :
215 1 : _escapedString.Truncate(0);
216 :
217 10 : for (uint32_t i = 0; i < aValue.Length(); i++) {
218 18 : if (aValue[i] == aEscapeChar || aValue[i] == MATCH_ALL ||
219 9 : aValue[i] == MATCH_ONE) {
220 0 : _escapedString += aEscapeChar;
221 : }
222 9 : _escapedString += aValue[i];
223 : }
224 1 : return NS_OK;
225 : }
226 :
227 : } // namespace storage
228 : } // namespace mozilla
|