Bug 1468221 [wpt PR 11464] - Fix #11454: Set --verify-log-full to False for ci/check_...
[gecko.git] / storage / mozStorageAsyncStatement.cpp
blobe94c0985a01af6fb3ba7193eaca4fef5146cdc88
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
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 <limits.h>
8 #include <stdio.h>
10 #include "nsError.h"
11 #include "nsMemory.h"
12 #include "nsProxyRelease.h"
13 #include "nsThreadUtils.h"
14 #include "nsIClassInfoImpl.h"
15 #include "Variant.h"
17 #include "mozIStorageError.h"
19 #include "mozStorageBindingParams.h"
20 #include "mozStorageConnection.h"
21 #include "mozStorageAsyncStatementJSHelper.h"
22 #include "mozStorageAsyncStatementParams.h"
23 #include "mozStoragePrivateHelpers.h"
24 #include "mozStorageStatementRow.h"
25 #include "mozStorageStatement.h"
27 #include "mozilla/Logging.h"
29 extern mozilla::LazyLogModule gStorageLog;
31 namespace mozilla {
32 namespace storage {
34 ////////////////////////////////////////////////////////////////////////////////
35 //// nsIClassInfo
37 NS_IMPL_CI_INTERFACE_GETTER(AsyncStatement,
38 mozIStorageAsyncStatement,
39 mozIStorageBaseStatement,
40 mozIStorageBindingParams,
41 mozilla::storage::StorageBaseStatementInternal)
43 class AsyncStatementClassInfo : public nsIClassInfo
45 public:
46 constexpr AsyncStatementClassInfo() {}
48 NS_DECL_ISUPPORTS_INHERITED
50 NS_IMETHOD
51 GetInterfaces(uint32_t *_count, nsIID ***_array) override
53 return NS_CI_INTERFACE_GETTER_NAME(AsyncStatement)(_count, _array);
56 NS_IMETHOD
57 GetScriptableHelper(nsIXPCScriptable **_helper) override
59 static AsyncStatementJSHelper sJSHelper;
60 *_helper = &sJSHelper;
61 return NS_OK;
64 NS_IMETHOD
65 GetContractID(nsACString& aContractID) override
67 aContractID.SetIsVoid(true);
68 return NS_OK;
71 NS_IMETHOD
72 GetClassDescription(nsACString& aDesc) override
74 aDesc.SetIsVoid(true);
75 return NS_OK;
78 NS_IMETHOD
79 GetClassID(nsCID **_id) override
81 *_id = nullptr;
82 return NS_OK;
85 NS_IMETHOD
86 GetFlags(uint32_t *_flags) override
88 *_flags = 0;
89 return NS_OK;
92 NS_IMETHOD
93 GetClassIDNoAlloc(nsCID *_cid) override
95 return NS_ERROR_NOT_AVAILABLE;
99 NS_IMETHODIMP_(MozExternalRefCountType) AsyncStatementClassInfo::AddRef() { return 2; }
100 NS_IMETHODIMP_(MozExternalRefCountType) AsyncStatementClassInfo::Release() { return 1; }
101 NS_IMPL_QUERY_INTERFACE(AsyncStatementClassInfo, nsIClassInfo)
103 static AsyncStatementClassInfo sAsyncStatementClassInfo;
105 ////////////////////////////////////////////////////////////////////////////////
106 //// AsyncStatement
108 AsyncStatement::AsyncStatement()
109 : StorageBaseStatementInternal()
110 , mFinalized(false)
114 nsresult
115 AsyncStatement::initialize(Connection *aDBConnection,
116 sqlite3 *aNativeConnection,
117 const nsACString &aSQLStatement)
119 MOZ_ASSERT(aDBConnection, "No database connection given!");
120 MOZ_ASSERT(aDBConnection->isConnectionReadyOnThisThread(), "Database connection should be valid");
121 MOZ_ASSERT(aNativeConnection, "No native connection given!");
123 mDBConnection = aDBConnection;
124 mNativeConnection = aNativeConnection;
125 mSQLString = aSQLStatement;
127 MOZ_LOG(gStorageLog, LogLevel::Debug, ("Inited async statement '%s' (0x%p)",
128 mSQLString.get(), this));
130 #ifdef DEBUG
131 // We want to try and test for LIKE and that consumers are using
132 // escapeStringForLIKE instead of just trusting user input. The idea to
133 // check to see if they are binding a parameter after like instead of just
134 // using a string. We only do this in debug builds because it's expensive!
135 const nsCaseInsensitiveCStringComparator c;
136 nsACString::const_iterator start, end, e;
137 aSQLStatement.BeginReading(start);
138 aSQLStatement.EndReading(end);
139 e = end;
140 while (::FindInReadable(NS_LITERAL_CSTRING(" LIKE"), start, e, c)) {
141 // We have a LIKE in here, so we perform our tests
142 // FindInReadable moves the iterator, so we have to get a new one for
143 // each test we perform.
144 nsACString::const_iterator s1, s2, s3;
145 s1 = s2 = s3 = start;
147 if (!(::FindInReadable(NS_LITERAL_CSTRING(" LIKE ?"), s1, end, c) ||
148 ::FindInReadable(NS_LITERAL_CSTRING(" LIKE :"), s2, end, c) ||
149 ::FindInReadable(NS_LITERAL_CSTRING(" LIKE @"), s3, end, c))) {
150 // At this point, we didn't find a LIKE statement followed by ?, :,
151 // or @, all of which are valid characters for binding a parameter.
152 // We will warn the consumer that they may not be safely using LIKE.
153 NS_WARNING("Unsafe use of LIKE detected! Please ensure that you "
154 "are using mozIStorageAsyncStatement::escapeStringForLIKE "
155 "and that you are binding that result to the statement "
156 "to prevent SQL injection attacks.");
159 // resetting start and e
160 start = e;
161 e = end;
163 #endif
165 return NS_OK;
168 mozIStorageBindingParams *
169 AsyncStatement::getParams()
171 nsresult rv;
173 // If we do not have an array object yet, make it.
174 if (!mParamsArray) {
175 nsCOMPtr<mozIStorageBindingParamsArray> array;
176 rv = NewBindingParamsArray(getter_AddRefs(array));
177 NS_ENSURE_SUCCESS(rv, nullptr);
179 mParamsArray = static_cast<BindingParamsArray *>(array.get());
182 // If there isn't already any rows added, we'll have to add one to use.
183 if (mParamsArray->length() == 0) {
184 RefPtr<AsyncBindingParams> params(new AsyncBindingParams(mParamsArray));
185 NS_ENSURE_TRUE(params, nullptr);
187 rv = mParamsArray->AddParams(params);
188 NS_ENSURE_SUCCESS(rv, nullptr);
190 // We have to unlock our params because AddParams locks them. This is safe
191 // because no reference to the params object was, or ever will be given out.
192 params->unlock(nullptr);
194 // We also want to lock our array at this point - we don't want anything to
195 // be added to it.
196 mParamsArray->lock();
199 return *mParamsArray->begin();
203 * If we are here then we know there are no pending async executions relying on
204 * us (StatementData holds a reference to us; this also goes for our own
205 * AsyncStatementFinalizer which proxies its release to the calling thread) and
206 * so it is always safe to destroy our sqlite3_stmt if one exists. We can be
207 * destroyed on the caller thread by garbage-collection/reference counting or on
208 * the async thread by the last execution of a statement that already lost its
209 * main-thread refs.
211 AsyncStatement::~AsyncStatement()
213 destructorAsyncFinalize();
215 // If we are getting destroyed on the wrong thread, proxy the connection
216 // release to the right thread. I'm not sure why we do this.
217 bool onCallingThread = false;
218 (void)mDBConnection->threadOpenedOn->IsOnCurrentThread(&onCallingThread);
219 if (!onCallingThread) {
220 // NS_ProxyRelase only magic forgets for us if mDBConnection is an
221 // nsCOMPtr. Which it is not; it's an nsRefPtr.
222 nsCOMPtr<nsIThread> targetThread(mDBConnection->threadOpenedOn);
223 NS_ProxyRelease(
224 "AsyncStatement::mDBConnection",
225 targetThread, mDBConnection.forget());
229 ////////////////////////////////////////////////////////////////////////////////
230 //// nsISupports
232 NS_IMPL_ADDREF(AsyncStatement)
233 NS_IMPL_RELEASE(AsyncStatement)
235 NS_INTERFACE_MAP_BEGIN(AsyncStatement)
236 NS_INTERFACE_MAP_ENTRY(mozIStorageAsyncStatement)
237 NS_INTERFACE_MAP_ENTRY(mozIStorageBaseStatement)
238 NS_INTERFACE_MAP_ENTRY(mozIStorageBindingParams)
239 NS_INTERFACE_MAP_ENTRY(mozilla::storage::StorageBaseStatementInternal)
240 if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
241 foundInterface = static_cast<nsIClassInfo *>(&sAsyncStatementClassInfo);
243 else
244 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageAsyncStatement)
245 NS_INTERFACE_MAP_END
248 ////////////////////////////////////////////////////////////////////////////////
249 //// StorageBaseStatementInternal
251 Connection *
252 AsyncStatement::getOwner()
254 return mDBConnection;
258 AsyncStatement::getAsyncStatement(sqlite3_stmt **_stmt)
260 #ifdef DEBUG
261 // Make sure we are never called on the connection's owning thread.
262 bool onOpenedThread = false;
263 (void)mDBConnection->threadOpenedOn->IsOnCurrentThread(&onOpenedThread);
264 NS_ASSERTION(!onOpenedThread,
265 "We should only be called on the async thread!");
266 #endif
268 if (!mAsyncStatement) {
269 int rc = mDBConnection->prepareStatement(mNativeConnection, mSQLString,
270 &mAsyncStatement);
271 if (rc != SQLITE_OK) {
272 MOZ_LOG(gStorageLog, LogLevel::Error,
273 ("Sqlite statement prepare error: %d '%s'", rc,
274 ::sqlite3_errmsg(mNativeConnection)));
275 MOZ_LOG(gStorageLog, LogLevel::Error,
276 ("Statement was: '%s'", mSQLString.get()));
277 *_stmt = nullptr;
278 return rc;
280 MOZ_LOG(gStorageLog, LogLevel::Debug, ("Initialized statement '%s' (0x%p)",
281 mSQLString.get(),
282 mAsyncStatement));
285 *_stmt = mAsyncStatement;
286 return SQLITE_OK;
289 nsresult
290 AsyncStatement::getAsynchronousStatementData(StatementData &_data)
292 if (mFinalized)
293 return NS_ERROR_UNEXPECTED;
295 // Pass null for the sqlite3_stmt; it will be requested on demand from the
296 // async thread.
297 _data = StatementData(nullptr, bindingParamsArray(), this);
299 return NS_OK;
302 already_AddRefed<mozIStorageBindingParams>
303 AsyncStatement::newBindingParams(mozIStorageBindingParamsArray *aOwner)
305 if (mFinalized)
306 return nullptr;
308 nsCOMPtr<mozIStorageBindingParams> params(new AsyncBindingParams(aOwner));
309 return params.forget();
313 ////////////////////////////////////////////////////////////////////////////////
314 //// mozIStorageAsyncStatement
316 // (nothing is specific to mozIStorageAsyncStatement)
318 ////////////////////////////////////////////////////////////////////////////////
319 //// StorageBaseStatementInternal
321 // proxy to StorageBaseStatementInternal using its define helper.
322 MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(
323 AsyncStatement,
324 if (mFinalized) return NS_ERROR_UNEXPECTED;)
326 NS_IMETHODIMP
327 AsyncStatement::Finalize()
329 if (mFinalized)
330 return NS_OK;
332 mFinalized = true;
334 MOZ_LOG(gStorageLog, LogLevel::Debug, ("Finalizing statement '%s'",
335 mSQLString.get()));
337 asyncFinalize();
339 // Release the params holder, so it can release the reference to us.
340 mStatementParamsHolder = nullptr;
342 return NS_OK;
345 NS_IMETHODIMP
346 AsyncStatement::BindParameters(mozIStorageBindingParamsArray *aParameters)
348 if (mFinalized)
349 return NS_ERROR_UNEXPECTED;
351 BindingParamsArray *array = static_cast<BindingParamsArray *>(aParameters);
352 if (array->getOwner() != this)
353 return NS_ERROR_UNEXPECTED;
355 if (array->length() == 0)
356 return NS_ERROR_UNEXPECTED;
358 mParamsArray = array;
359 mParamsArray->lock();
361 return NS_OK;
364 NS_IMETHODIMP
365 AsyncStatement::GetState(int32_t *_state)
367 if (mFinalized)
368 *_state = MOZ_STORAGE_STATEMENT_INVALID;
369 else
370 *_state = MOZ_STORAGE_STATEMENT_READY;
372 return NS_OK;
375 ////////////////////////////////////////////////////////////////////////////////
376 //// mozIStorageBindingParams
378 BOILERPLATE_BIND_PROXIES(
379 AsyncStatement,
380 if (mFinalized) return NS_ERROR_UNEXPECTED;
383 } // namespace storage
384 } // namespace mozilla