Backout 2ea2669b53c3, Bug 917642 - [Helix] Please update the helix blobs
[gecko.git] / storage / src / StorageBaseStatementInternal.cpp
blobd2f93292c7b618eae1ef3e2223d9a9366dc930b1
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/. */
7 #include "StorageBaseStatementInternal.h"
9 #include "nsProxyRelease.h"
11 #include "mozStorageBindingParamsArray.h"
12 #include "mozStorageStatementData.h"
13 #include "mozStorageAsyncStatementExecution.h"
15 namespace mozilla {
16 namespace storage {
18 ////////////////////////////////////////////////////////////////////////////////
19 //// Local Classes
21 /**
22 * Used to finalize an asynchronous statement on the background thread.
24 class AsyncStatementFinalizer : public nsRunnable
26 public:
27 /**
28 * Constructor for the event.
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.
38 AsyncStatementFinalizer(StorageBaseStatementInternal *aStatement,
39 Connection *aConnection)
40 : mStatement(aStatement)
41 , mConnection(aConnection)
45 NS_IMETHOD Run()
47 if (mStatement->mAsyncStatement) {
48 (void)::sqlite3_finalize(mStatement->mAsyncStatement);
49 mStatement->mAsyncStatement = nullptr;
51 (void)::NS_ProxyRelease(mConnection->threadOpenedOn, mStatement);
52 return NS_OK;
54 private:
55 nsRefPtr<StorageBaseStatementInternal> mStatement;
56 nsRefPtr<Connection> mConnection;
59 /**
60 * Finalize a sqlite3_stmt on the background thread for a statement whose
61 * destructor was invoked and the statement was non-null.
63 class LastDitchSqliteStatementFinalizer : public nsRunnable
65 public:
66 /**
67 * Event constructor.
69 * @param aConnection
70 * Used to keep the connection alive. If we failed to do this, it
71 * is possible that the statement going out of scope invoking us
72 * might have the last reference to the connection and so trigger
73 * an attempt to close the connection which is doomed to fail
74 * (because the asynchronous execution thread must exist which will
75 * trigger the failure case).
76 * @param aStatement
77 * The sqlite3_stmt to finalize. This object takes ownership /
78 * responsibility for the instance and all other references to it
79 * should be forgotten.
81 LastDitchSqliteStatementFinalizer(nsRefPtr<Connection> &aConnection,
82 sqlite3_stmt *aStatement)
83 : mConnection(aConnection)
84 , mAsyncStatement(aStatement)
86 NS_PRECONDITION(aConnection, "You must provide a Connection");
89 NS_IMETHOD Run()
91 (void)::sqlite3_finalize(mAsyncStatement);
92 mAsyncStatement = nullptr;
94 // Because of our ambiguous nsISupports we cannot use the NS_ProxyRelease
95 // template helpers.
96 Connection *rawConnection = nullptr;
97 mConnection.swap(rawConnection);
98 (void)::NS_ProxyRelease(
99 rawConnection->threadOpenedOn,
100 NS_ISUPPORTS_CAST(mozIStorageConnection *, rawConnection));
101 return NS_OK;
103 private:
104 nsRefPtr<Connection> mConnection;
105 sqlite3_stmt *mAsyncStatement;
108 ////////////////////////////////////////////////////////////////////////////////
109 //// StorageBaseStatementInternal
111 StorageBaseStatementInternal::StorageBaseStatementInternal()
112 : mAsyncStatement(nullptr)
116 void
117 StorageBaseStatementInternal::asyncFinalize()
119 nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget();
120 if (target) {
121 // Attempt to finalize asynchronously
122 nsCOMPtr<nsIRunnable> event =
123 new AsyncStatementFinalizer(this, mDBConnection);
125 // Dispatch. Note that dispatching can fail, typically if
126 // we have a race condition with asyncClose(). It's ok,
127 // let asyncClose() win.
128 (void)target->Dispatch(event, NS_DISPATCH_NORMAL);
130 // If we cannot get the background thread,
131 // mozStorageConnection::AsyncClose() has already been called and
132 // the statement either has been or will be cleaned up by
133 // internalClose().
136 void
137 StorageBaseStatementInternal::destructorAsyncFinalize()
139 if (!mAsyncStatement)
140 return;
142 // If we reach this point, our owner has not finalized this
143 // statement, yet we are being destructed. If possible, we want to
144 // auto-finalize it early, to release the resources early.
145 nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget();
146 if (target) {
147 // If we can get the async execution target, we can indeed finalize
148 // the statement, as the connection is still open.
149 bool isAsyncThread = false;
150 (void)target->IsOnCurrentThread(&isAsyncThread);
152 nsCOMPtr<nsIRunnable> event =
153 new LastDitchSqliteStatementFinalizer(mDBConnection, mAsyncStatement);
154 if (isAsyncThread) {
155 (void)event->Run();
156 } else {
157 (void)target->Dispatch(event, NS_DISPATCH_NORMAL);
161 // We might not be able to dispatch to the background thread,
162 // presumably because it is being shutdown. Since said shutdown will
163 // finalize the statement, we just need to clean-up around here.
164 mAsyncStatement = nullptr;
167 NS_IMETHODIMP
168 StorageBaseStatementInternal::NewBindingParamsArray(
169 mozIStorageBindingParamsArray **_array
172 nsCOMPtr<mozIStorageBindingParamsArray> array = new BindingParamsArray(this);
173 NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);
175 array.forget(_array);
176 return NS_OK;
179 NS_IMETHODIMP
180 StorageBaseStatementInternal::ExecuteAsync(
181 mozIStorageStatementCallback *aCallback,
182 mozIStoragePendingStatement **_stmt
185 // We used to call Connection::ExecuteAsync but it takes a
186 // mozIStorageBaseStatement signature because it is also a public API. Since
187 // our 'this' has no static concept of mozIStorageBaseStatement and Connection
188 // would just QI it back across to a StorageBaseStatementInternal and the
189 // actual logic is very simple, we now roll our own.
190 nsTArray<StatementData> stmts(1);
191 StatementData data;
192 nsresult rv = getAsynchronousStatementData(data);
193 NS_ENSURE_SUCCESS(rv, rv);
194 NS_ENSURE_TRUE(stmts.AppendElement(data), NS_ERROR_OUT_OF_MEMORY);
196 // Dispatch to the background
197 return AsyncExecuteStatements::execute(stmts, mDBConnection, aCallback,
198 _stmt);
201 NS_IMETHODIMP
202 StorageBaseStatementInternal::EscapeStringForLIKE(
203 const nsAString &aValue,
204 const PRUnichar aEscapeChar,
205 nsAString &_escapedString
208 const PRUnichar MATCH_ALL('%');
209 const PRUnichar MATCH_ONE('_');
211 _escapedString.Truncate(0);
213 for (uint32_t i = 0; i < aValue.Length(); i++) {
214 if (aValue[i] == aEscapeChar || aValue[i] == MATCH_ALL ||
215 aValue[i] == MATCH_ONE) {
216 _escapedString += aEscapeChar;
218 _escapedString += aValue[i];
220 return NS_OK;
223 } // namespace storage
224 } // namespace mozilla