Bug 1845715 - Check for failure when getting RegExp match result template r=iain
[gecko.git] / storage / mozStorageAsyncStatement.cpp
blobd0bd81e69b27cd73d6c248143a6669755ebd879d
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 "nsProxyRelease.h"
12 #include "nsThreadUtils.h"
13 #include "nsIClassInfoImpl.h"
14 #include "Variant.h"
16 #include "mozStorageBindingParams.h"
17 #include "mozStorageConnection.h"
18 #include "mozStorageAsyncStatementJSHelper.h"
19 #include "mozStorageAsyncStatementParams.h"
20 #include "mozStoragePrivateHelpers.h"
21 #include "mozStorageStatementRow.h"
22 #include "mozStorageStatement.h"
24 #include "mozilla/Logging.h"
26 extern mozilla::LazyLogModule gStorageLog;
28 namespace mozilla {
29 namespace storage {
31 ////////////////////////////////////////////////////////////////////////////////
32 //// nsIClassInfo
34 NS_IMPL_CI_INTERFACE_GETTER(AsyncStatement, mozIStorageAsyncStatement,
35 mozIStorageBaseStatement, mozIStorageBindingParams,
36 mozilla::storage::StorageBaseStatementInternal)
38 class AsyncStatementClassInfo : public nsIClassInfo {
39 public:
40 constexpr AsyncStatementClassInfo() {}
42 NS_DECL_ISUPPORTS_INHERITED
44 NS_IMETHOD
45 GetInterfaces(nsTArray<nsIID>& _array) override {
46 return NS_CI_INTERFACE_GETTER_NAME(AsyncStatement)(_array);
49 NS_IMETHOD
50 GetScriptableHelper(nsIXPCScriptable** _helper) override {
51 static AsyncStatementJSHelper sJSHelper;
52 *_helper = &sJSHelper;
53 return NS_OK;
56 NS_IMETHOD
57 GetContractID(nsACString& aContractID) override {
58 aContractID.SetIsVoid(true);
59 return NS_OK;
62 NS_IMETHOD
63 GetClassDescription(nsACString& aDesc) override {
64 aDesc.SetIsVoid(true);
65 return NS_OK;
68 NS_IMETHOD
69 GetClassID(nsCID** _id) override {
70 *_id = nullptr;
71 return NS_OK;
74 NS_IMETHOD
75 GetFlags(uint32_t* _flags) override {
76 *_flags = 0;
77 return NS_OK;
80 NS_IMETHOD
81 GetClassIDNoAlloc(nsCID* _cid) override { return NS_ERROR_NOT_AVAILABLE; }
84 NS_IMETHODIMP_(MozExternalRefCountType) AsyncStatementClassInfo::AddRef() {
85 return 2;
87 NS_IMETHODIMP_(MozExternalRefCountType) AsyncStatementClassInfo::Release() {
88 return 1;
90 NS_IMPL_QUERY_INTERFACE(AsyncStatementClassInfo, nsIClassInfo)
92 static AsyncStatementClassInfo sAsyncStatementClassInfo;
94 ////////////////////////////////////////////////////////////////////////////////
95 //// AsyncStatement
97 AsyncStatement::AsyncStatement()
98 : StorageBaseStatementInternal(), mFinalized(false) {}
100 nsresult AsyncStatement::initialize(Connection* aDBConnection,
101 sqlite3* aNativeConnection,
102 const nsACString& aSQLStatement) {
103 MOZ_ASSERT(aDBConnection, "No database connection given!");
104 MOZ_ASSERT(aDBConnection->isConnectionReadyOnThisThread(),
105 "Database connection should be valid");
106 MOZ_ASSERT(aNativeConnection, "No native connection given!");
108 mDBConnection = aDBConnection;
109 mNativeConnection = aNativeConnection;
110 mSQLString = aSQLStatement;
112 MOZ_LOG(gStorageLog, LogLevel::Debug,
113 ("Inited async statement '%s' (0x%p)", mSQLString.get(), this));
115 #ifdef DEBUG
116 // We want to try and test for LIKE and that consumers are using
117 // escapeStringForLIKE instead of just trusting user input. The idea to
118 // check to see if they are binding a parameter after like instead of just
119 // using a string. We only do this in debug builds because it's expensive!
120 auto c = nsCaseInsensitiveCStringComparator;
121 nsACString::const_iterator start, end, e;
122 aSQLStatement.BeginReading(start);
123 aSQLStatement.EndReading(end);
124 e = end;
125 while (::FindInReadable(" LIKE"_ns, start, e, c)) {
126 // We have a LIKE in here, so we perform our tests
127 // FindInReadable moves the iterator, so we have to get a new one for
128 // each test we perform.
129 nsACString::const_iterator s1, s2, s3;
130 s1 = s2 = s3 = start;
132 if (!(::FindInReadable(" LIKE ?"_ns, s1, end, c) ||
133 ::FindInReadable(" LIKE :"_ns, s2, end, c) ||
134 ::FindInReadable(" LIKE @"_ns, s3, end, c))) {
135 // At this point, we didn't find a LIKE statement followed by ?, :,
136 // or @, all of which are valid characters for binding a parameter.
137 // We will warn the consumer that they may not be safely using LIKE.
138 NS_WARNING(
139 "Unsafe use of LIKE detected! Please ensure that you "
140 "are using mozIStorageAsyncStatement::escapeStringForLIKE "
141 "and that you are binding that result to the statement "
142 "to prevent SQL injection attacks.");
145 // resetting start and e
146 start = e;
147 e = end;
149 #endif
151 return NS_OK;
154 mozIStorageBindingParams* AsyncStatement::getParams() {
155 nsresult rv;
157 // If we do not have an array object yet, make it.
158 if (!mParamsArray) {
159 nsCOMPtr<mozIStorageBindingParamsArray> array;
160 rv = NewBindingParamsArray(getter_AddRefs(array));
161 NS_ENSURE_SUCCESS(rv, nullptr);
163 mParamsArray = static_cast<BindingParamsArray*>(array.get());
166 // If there isn't already any rows added, we'll have to add one to use.
167 if (mParamsArray->length() == 0) {
168 RefPtr<AsyncBindingParams> params(new AsyncBindingParams(mParamsArray));
169 NS_ENSURE_TRUE(params, nullptr);
171 rv = mParamsArray->AddParams(params);
172 NS_ENSURE_SUCCESS(rv, nullptr);
174 // We have to unlock our params because AddParams locks them. This is safe
175 // because no reference to the params object was, or ever will be given out.
176 params->unlock(nullptr);
178 // We also want to lock our array at this point - we don't want anything to
179 // be added to it.
180 mParamsArray->lock();
183 return *mParamsArray->begin();
187 * If we are here then we know there are no pending async executions relying on
188 * us (StatementData holds a reference to us; this also goes for our own
189 * AsyncStatementFinalizer which proxies its release to the calling event
190 * target) and so it is always safe to destroy our sqlite3_stmt if one exists.
191 * We can be destroyed on the caller event target by
192 * garbage-collection/reference counting or on the async event target by the
193 * last execution of a statement that already lost its main-thread refs.
195 AsyncStatement::~AsyncStatement() {
196 destructorAsyncFinalize();
198 // If we are getting destroyed on the wrong event target, proxy the connection
199 // release to the right one.
200 if (!IsOnCurrentSerialEventTarget(mDBConnection->eventTargetOpenedOn)) {
201 // NS_ProxyRelase only magic forgets for us if mDBConnection is an
202 // nsCOMPtr. Which it is not; it's a RefPtr.
203 nsCOMPtr<nsIEventTarget> target(mDBConnection->eventTargetOpenedOn);
204 NS_ProxyRelease("AsyncStatement::mDBConnection", target,
205 mDBConnection.forget());
209 ////////////////////////////////////////////////////////////////////////////////
210 //// nsISupports
212 NS_IMPL_ADDREF(AsyncStatement)
213 NS_IMPL_RELEASE(AsyncStatement)
215 NS_INTERFACE_MAP_BEGIN(AsyncStatement)
216 NS_INTERFACE_MAP_ENTRY(mozIStorageAsyncStatement)
217 NS_INTERFACE_MAP_ENTRY(mozIStorageBaseStatement)
218 NS_INTERFACE_MAP_ENTRY(mozIStorageBindingParams)
219 NS_INTERFACE_MAP_ENTRY(mozilla::storage::StorageBaseStatementInternal)
220 if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
221 foundInterface = static_cast<nsIClassInfo*>(&sAsyncStatementClassInfo);
222 } else
223 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageAsyncStatement)
224 NS_INTERFACE_MAP_END
226 ////////////////////////////////////////////////////////////////////////////////
227 //// StorageBaseStatementInternal
229 Connection* AsyncStatement::getOwner() { return mDBConnection; }
231 int AsyncStatement::getAsyncStatement(sqlite3_stmt** _stmt) {
232 #ifdef DEBUG
233 // Make sure we are never called on the connection's owning event target.
234 NS_ASSERTION(
235 !IsOnCurrentSerialEventTarget(mDBConnection->eventTargetOpenedOn),
236 "We should only be called on the async event target!");
237 #endif
239 if (!mAsyncStatement) {
240 int rc = mDBConnection->prepareStatement(mNativeConnection, mSQLString,
241 &mAsyncStatement);
242 if (rc != SQLITE_OK) {
243 MOZ_LOG(gStorageLog, LogLevel::Error,
244 ("Sqlite statement prepare error: %d '%s'", rc,
245 ::sqlite3_errmsg(mNativeConnection)));
246 MOZ_LOG(gStorageLog, LogLevel::Error,
247 ("Statement was: '%s'", mSQLString.get()));
248 *_stmt = nullptr;
249 return rc;
251 MOZ_LOG(gStorageLog, LogLevel::Debug,
252 ("Initialized statement '%s' (0x%p)", mSQLString.get(),
253 mAsyncStatement));
256 *_stmt = mAsyncStatement;
257 return SQLITE_OK;
260 nsresult AsyncStatement::getAsynchronousStatementData(StatementData& _data) {
261 if (mFinalized) return NS_ERROR_UNEXPECTED;
263 // Pass null for the sqlite3_stmt; it will be requested on demand from the
264 // async event target.
265 _data = StatementData(nullptr, bindingParamsArray(), this);
267 return NS_OK;
270 already_AddRefed<mozIStorageBindingParams> AsyncStatement::newBindingParams(
271 mozIStorageBindingParamsArray* aOwner) {
272 if (mFinalized) return nullptr;
274 nsCOMPtr<mozIStorageBindingParams> params(new AsyncBindingParams(aOwner));
275 return params.forget();
278 ////////////////////////////////////////////////////////////////////////////////
279 //// mozIStorageAsyncStatement
281 // (nothing is specific to mozIStorageAsyncStatement)
283 ////////////////////////////////////////////////////////////////////////////////
284 //// StorageBaseStatementInternal
286 // proxy to StorageBaseStatementInternal using its define helper.
287 MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(
288 AsyncStatement, if (mFinalized) return NS_ERROR_UNEXPECTED;)
290 NS_IMETHODIMP
291 AsyncStatement::Finalize() {
292 if (mFinalized) return NS_OK;
294 mFinalized = true;
296 MOZ_LOG(gStorageLog, LogLevel::Debug,
297 ("Finalizing statement '%s'", mSQLString.get()));
299 asyncFinalize();
301 // Release the params holder, so it can release the reference to us.
302 mStatementParamsHolder = nullptr;
304 return NS_OK;
307 NS_IMETHODIMP
308 AsyncStatement::BindParameters(mozIStorageBindingParamsArray* aParameters) {
309 if (mFinalized) return NS_ERROR_UNEXPECTED;
311 BindingParamsArray* array = static_cast<BindingParamsArray*>(aParameters);
312 if (array->getOwner() != this) return NS_ERROR_UNEXPECTED;
314 if (array->length() == 0) return NS_ERROR_UNEXPECTED;
316 mParamsArray = array;
317 mParamsArray->lock();
319 return NS_OK;
322 NS_IMETHODIMP
323 AsyncStatement::GetState(int32_t* _state) {
324 if (mFinalized)
325 *_state = MOZ_STORAGE_STATEMENT_INVALID;
326 else
327 *_state = MOZ_STORAGE_STATEMENT_READY;
329 return NS_OK;
332 ////////////////////////////////////////////////////////////////////////////////
333 //// mozIStorageBindingParams
335 BOILERPLATE_BIND_PROXIES(AsyncStatement,
336 if (mFinalized) return NS_ERROR_UNEXPECTED;)
338 } // namespace storage
339 } // namespace mozilla