1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * vim: sw=2 ts=2 sts=2 expandtab
3 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is the Mozilla Foundation.
19 : * Portions created by the Initial Developer are Copyright (C) 2010
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Andrew Sutherland <asutherland@asutherland.org>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "StorageBaseStatementInternal.h"
40 :
41 : #include "nsProxyRelease.h"
42 :
43 : #include "mozStorageBindingParamsArray.h"
44 : #include "mozStorageStatementData.h"
45 : #include "mozStorageAsyncStatementExecution.h"
46 :
47 : namespace mozilla {
48 : namespace storage {
49 :
50 : ////////////////////////////////////////////////////////////////////////////////
51 : //// Local Classes
52 :
53 : /**
54 : * Used to finalize an asynchronous statement on the background thread.
55 : */
56 : class AsyncStatementFinalizer : public nsRunnable
57 35084 : {
58 : public:
59 : /**
60 : * Constructor for the event.
61 : *
62 : * @param aStatement
63 : * We need the AsyncStatement to be able to get at the sqlite3_stmt;
64 : * we only access/create it on the async thread.
65 : * @param aConnection
66 : * We need the connection to know what thread to release the statement
67 : * on. We release the statement on that thread since releasing the
68 : * statement might end up releasing the connection too.
69 : */
70 8771 : AsyncStatementFinalizer(StorageBaseStatementInternal *aStatement,
71 : Connection *aConnection)
72 : : mStatement(aStatement)
73 8771 : , mConnection(aConnection)
74 : {
75 8771 : }
76 :
77 8771 : NS_IMETHOD Run()
78 : {
79 8771 : if (mStatement->mAsyncStatement) {
80 8758 : (void)::sqlite3_finalize(mStatement->mAsyncStatement);
81 8758 : mStatement->mAsyncStatement = nsnull;
82 : }
83 8771 : (void)::NS_ProxyRelease(mConnection->threadOpenedOn, mStatement);
84 8771 : return NS_OK;
85 : }
86 : private:
87 : nsRefPtr<StorageBaseStatementInternal> mStatement;
88 : nsRefPtr<Connection> mConnection;
89 : };
90 :
91 : /**
92 : * Finalize a sqlite3_stmt on the background thread for a statement whose
93 : * destructor was invoked and the statement was non-null.
94 : */
95 : class LastDitchSqliteStatementFinalizer : public nsRunnable
96 4332 : {
97 : public:
98 : /**
99 : * Event constructor.
100 : *
101 : * @param aConnection
102 : * Used to keep the connection alive. If we failed to do this, it
103 : * is possible that the statement going out of scope invoking us
104 : * might have the last reference to the connection and so trigger
105 : * an attempt to close the connection which is doomed to fail
106 : * (because the asynchronous execution thread must exist which will
107 : * trigger the failure case).
108 : * @param aStatement
109 : * The sqlite3_stmt to finalize. This object takes ownership /
110 : * responsibility for the instance and all other references to it
111 : * should be forgotten.
112 : */
113 1083 : LastDitchSqliteStatementFinalizer(nsRefPtr<Connection> &aConnection,
114 : sqlite3_stmt *aStatement)
115 : : mConnection(aConnection)
116 1083 : , mAsyncStatement(aStatement)
117 : {
118 1083 : NS_PRECONDITION(aConnection, "You must provide a Connection");
119 1083 : }
120 :
121 1083 : NS_IMETHOD Run()
122 : {
123 1083 : (void)::sqlite3_finalize(mAsyncStatement);
124 1083 : mAsyncStatement = nsnull;
125 :
126 : // Because of our ambiguous nsISupports we cannot use the NS_ProxyRelease
127 : // template helpers.
128 1083 : Connection *rawConnection = nsnull;
129 1083 : mConnection.swap(rawConnection);
130 : (void)::NS_ProxyRelease(
131 : rawConnection->threadOpenedOn,
132 1083 : NS_ISUPPORTS_CAST(mozIStorageConnection *, rawConnection));
133 1083 : return NS_OK;
134 : }
135 : private:
136 : nsRefPtr<Connection> mConnection;
137 : sqlite3_stmt *mAsyncStatement;
138 : };
139 :
140 : ////////////////////////////////////////////////////////////////////////////////
141 : //// StorageBaseStatementInternal
142 :
143 42729 : StorageBaseStatementInternal::StorageBaseStatementInternal()
144 42729 : : mAsyncStatement(NULL)
145 : {
146 42729 : }
147 :
148 : void
149 8771 : StorageBaseStatementInternal::asyncFinalize()
150 : {
151 8771 : nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget();
152 8771 : if (!target) {
153 : // If we cannot get the background thread, we have to assume it has been
154 : // shutdown (or is in the process of doing so). As a result, we should
155 : // just finalize it here and now.
156 0 : destructorAsyncFinalize();
157 : }
158 : else {
159 : nsCOMPtr<nsIRunnable> event =
160 26313 : new AsyncStatementFinalizer(this, mDBConnection);
161 :
162 : // If the dispatching did not go as planned, finalize now.
163 8771 : if (NS_FAILED(target->Dispatch(event, NS_DISPATCH_NORMAL))) {
164 0 : destructorAsyncFinalize();
165 : }
166 : }
167 8771 : }
168 :
169 : void
170 5918 : StorageBaseStatementInternal::destructorAsyncFinalize()
171 : {
172 5918 : if (!mAsyncStatement)
173 4729 : return;
174 :
175 1189 : nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget();
176 1189 : if (target) {
177 : nsCOMPtr<nsIRunnable> event =
178 2166 : new LastDitchSqliteStatementFinalizer(mDBConnection, mAsyncStatement);
179 1083 : if (NS_SUCCEEDED(target->Dispatch(event, NS_DISPATCH_NORMAL))) {
180 1083 : mAsyncStatement = nsnull;
181 : return;
182 : }
183 : }
184 : // (no async thread remains or we could not dispatch to it)
185 106 : (void)::sqlite3_finalize(mAsyncStatement);
186 106 : mAsyncStatement = nsnull;
187 : }
188 :
189 : NS_IMETHODIMP
190 121768 : StorageBaseStatementInternal::NewBindingParamsArray(
191 : mozIStorageBindingParamsArray **_array
192 : )
193 : {
194 243536 : nsCOMPtr<mozIStorageBindingParamsArray> array = new BindingParamsArray(this);
195 121768 : NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);
196 :
197 121768 : array.forget(_array);
198 121768 : return NS_OK;
199 : }
200 :
201 : NS_IMETHODIMP
202 37300 : StorageBaseStatementInternal::ExecuteAsync(
203 : mozIStorageStatementCallback *aCallback,
204 : mozIStoragePendingStatement **_stmt
205 : )
206 : {
207 : // We used to call Connection::ExecuteAsync but it takes a
208 : // mozIStorageBaseStatement signature because it is also a public API. Since
209 : // our 'this' has no static concept of mozIStorageBaseStatement and Connection
210 : // would just QI it back across to a StorageBaseStatementInternal and the
211 : // actual logic is very simple, we now roll our own.
212 74600 : nsTArray<StatementData> stmts(1);
213 74600 : StatementData data;
214 37300 : nsresult rv = getAsynchronousStatementData(data);
215 37300 : NS_ENSURE_SUCCESS(rv, rv);
216 37299 : NS_ENSURE_TRUE(stmts.AppendElement(data), NS_ERROR_OUT_OF_MEMORY);
217 :
218 : // Dispatch to the background
219 : return AsyncExecuteStatements::execute(stmts, mDBConnection, aCallback,
220 37299 : _stmt);
221 : }
222 :
223 : NS_IMETHODIMP
224 100 : StorageBaseStatementInternal::EscapeStringForLIKE(
225 : const nsAString &aValue,
226 : const PRUnichar aEscapeChar,
227 : nsAString &_escapedString
228 : )
229 : {
230 100 : const PRUnichar MATCH_ALL('%');
231 100 : const PRUnichar MATCH_ONE('_');
232 :
233 100 : _escapedString.Truncate(0);
234 :
235 1186 : for (PRUint32 i = 0; i < aValue.Length(); i++) {
236 2168 : if (aValue[i] == aEscapeChar || aValue[i] == MATCH_ALL ||
237 1082 : aValue[i] == MATCH_ONE) {
238 6 : _escapedString += aEscapeChar;
239 : }
240 1086 : _escapedString += aValue[i];
241 : }
242 100 : return NS_OK;
243 : }
244 :
245 : } // namespace storage
246 : } // namespace mozilla
|