Bug 1867190 - Add prefs for PHC probablities r=glandium
[gecko.git] / storage / mozStorageAsyncStatement.cpp
blobb9332f043b367d9cd12926f4b4cbb061600f9c3c
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() = default;
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() : mFinalized(false) {}
99 nsresult AsyncStatement::initialize(Connection* aDBConnection,
100 sqlite3* aNativeConnection,
101 const nsACString& aSQLStatement) {
102 MOZ_ASSERT(aDBConnection, "No database connection given!");
103 MOZ_ASSERT(aDBConnection->isConnectionReadyOnThisThread(),
104 "Database connection should be valid");
105 MOZ_ASSERT(aNativeConnection, "No native connection given!");
107 mDBConnection = aDBConnection;
108 mNativeConnection = aNativeConnection;
109 mSQLString = aSQLStatement;
111 MOZ_LOG(gStorageLog, LogLevel::Debug,
112 ("Inited async statement '%s' (0x%p)", mSQLString.get(), this));
114 #ifdef DEBUG
115 // We want to try and test for LIKE and that consumers are using
116 // escapeStringForLIKE instead of just trusting user input. The idea to
117 // check to see if they are binding a parameter after like instead of just
118 // using a string. We only do this in debug builds because it's expensive!
119 auto c = nsCaseInsensitiveCStringComparator;
120 nsACString::const_iterator start, end, e;
121 aSQLStatement.BeginReading(start);
122 aSQLStatement.EndReading(end);
123 e = end;
124 while (::FindInReadable(" LIKE"_ns, start, e, c)) {
125 // We have a LIKE in here, so we perform our tests
126 // FindInReadable moves the iterator, so we have to get a new one for
127 // each test we perform.
128 nsACString::const_iterator s1, s2, s3;
129 s1 = s2 = s3 = start;
131 if (!(::FindInReadable(" LIKE ?"_ns, s1, end, c) ||
132 ::FindInReadable(" LIKE :"_ns, s2, end, c) ||
133 ::FindInReadable(" LIKE @"_ns, s3, end, c))) {
134 // At this point, we didn't find a LIKE statement followed by ?, :,
135 // or @, all of which are valid characters for binding a parameter.
136 // We will warn the consumer that they may not be safely using LIKE.
137 NS_WARNING(
138 "Unsafe use of LIKE detected! Please ensure that you "
139 "are using mozIStorageAsyncStatement::escapeStringForLIKE "
140 "and that you are binding that result to the statement "
141 "to prevent SQL injection attacks.");
144 // resetting start and e
145 start = e;
146 e = end;
148 #endif
150 return NS_OK;
153 mozIStorageBindingParams* AsyncStatement::getParams() {
154 nsresult rv;
156 // If we do not have an array object yet, make it.
157 if (!mParamsArray) {
158 nsCOMPtr<mozIStorageBindingParamsArray> array;
159 rv = NewBindingParamsArray(getter_AddRefs(array));
160 NS_ENSURE_SUCCESS(rv, nullptr);
162 mParamsArray = static_cast<BindingParamsArray*>(array.get());
165 // If there isn't already any rows added, we'll have to add one to use.
166 if (mParamsArray->length() == 0) {
167 RefPtr<AsyncBindingParams> params(new AsyncBindingParams(mParamsArray));
168 NS_ENSURE_TRUE(params, nullptr);
170 rv = mParamsArray->AddParams(params);
171 NS_ENSURE_SUCCESS(rv, nullptr);
173 // We have to unlock our params because AddParams locks them. This is safe
174 // because no reference to the params object was, or ever will be given out.
175 params->unlock(nullptr);
177 // We also want to lock our array at this point - we don't want anything to
178 // be added to it.
179 mParamsArray->lock();
182 return *mParamsArray->begin();
186 * If we are here then we know there are no pending async executions relying on
187 * us (StatementData holds a reference to us; this also goes for our own
188 * AsyncStatementFinalizer which proxies its release to the calling event
189 * target) and so it is always safe to destroy our sqlite3_stmt if one exists.
190 * We can be destroyed on the caller event target by
191 * garbage-collection/reference counting or on the async event target by the
192 * last execution of a statement that already lost its main-thread refs.
194 AsyncStatement::~AsyncStatement() {
195 destructorAsyncFinalize();
197 // If we are getting destroyed on the wrong event target, proxy the connection
198 // release to the right one.
199 if (!IsOnCurrentSerialEventTarget(mDBConnection->eventTargetOpenedOn)) {
200 // NS_ProxyRelase only magic forgets for us if mDBConnection is an
201 // nsCOMPtr. Which it is not; it's a RefPtr.
202 nsCOMPtr<nsIEventTarget> target(mDBConnection->eventTargetOpenedOn);
203 NS_ProxyRelease("AsyncStatement::mDBConnection", target,
204 mDBConnection.forget());
208 ////////////////////////////////////////////////////////////////////////////////
209 //// nsISupports
211 NS_IMPL_ADDREF(AsyncStatement)
212 NS_IMPL_RELEASE(AsyncStatement)
214 NS_INTERFACE_MAP_BEGIN(AsyncStatement)
215 NS_INTERFACE_MAP_ENTRY(mozIStorageAsyncStatement)
216 NS_INTERFACE_MAP_ENTRY(mozIStorageBaseStatement)
217 NS_INTERFACE_MAP_ENTRY(mozIStorageBindingParams)
218 NS_INTERFACE_MAP_ENTRY(mozilla::storage::StorageBaseStatementInternal)
219 if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
220 foundInterface = static_cast<nsIClassInfo*>(&sAsyncStatementClassInfo);
221 } else
222 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageAsyncStatement)
223 NS_INTERFACE_MAP_END
225 ////////////////////////////////////////////////////////////////////////////////
226 //// StorageBaseStatementInternal
228 Connection* AsyncStatement::getOwner() { return mDBConnection; }
230 int AsyncStatement::getAsyncStatement(sqlite3_stmt** _stmt) {
231 #ifdef DEBUG
232 // Make sure we are never called on the connection's owning event target.
233 NS_ASSERTION(
234 !IsOnCurrentSerialEventTarget(mDBConnection->eventTargetOpenedOn),
235 "We should only be called on the async event target!");
236 #endif
238 if (!mAsyncStatement) {
239 int rc = mDBConnection->prepareStatement(mNativeConnection, mSQLString,
240 &mAsyncStatement);
241 if (rc != SQLITE_OK) {
242 MOZ_LOG(gStorageLog, LogLevel::Error,
243 ("Sqlite statement prepare error: %d '%s'", rc,
244 ::sqlite3_errmsg(mNativeConnection)));
245 MOZ_LOG(gStorageLog, LogLevel::Error,
246 ("Statement was: '%s'", mSQLString.get()));
247 *_stmt = nullptr;
248 return rc;
250 MOZ_LOG(gStorageLog, LogLevel::Debug,
251 ("Initialized statement '%s' (0x%p)", mSQLString.get(),
252 mAsyncStatement));
255 *_stmt = mAsyncStatement;
256 return SQLITE_OK;
259 nsresult AsyncStatement::getAsynchronousStatementData(StatementData& _data) {
260 if (mFinalized) return NS_ERROR_UNEXPECTED;
262 // Pass null for the sqlite3_stmt; it will be requested on demand from the
263 // async event target.
264 _data = StatementData(nullptr, bindingParamsArray(), this);
266 return NS_OK;
269 already_AddRefed<mozIStorageBindingParams> AsyncStatement::newBindingParams(
270 mozIStorageBindingParamsArray* aOwner) {
271 if (mFinalized) return nullptr;
273 nsCOMPtr<mozIStorageBindingParams> params(new AsyncBindingParams(aOwner));
274 return params.forget();
277 ////////////////////////////////////////////////////////////////////////////////
278 //// mozIStorageAsyncStatement
280 // (nothing is specific to mozIStorageAsyncStatement)
282 ////////////////////////////////////////////////////////////////////////////////
283 //// StorageBaseStatementInternal
285 // proxy to StorageBaseStatementInternal using its define helper.
286 MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(
287 AsyncStatement, if (mFinalized) return NS_ERROR_UNEXPECTED;)
289 NS_IMETHODIMP
290 AsyncStatement::Finalize() {
291 if (mFinalized) return NS_OK;
293 mFinalized = true;
295 MOZ_LOG(gStorageLog, LogLevel::Debug,
296 ("Finalizing statement '%s'", mSQLString.get()));
298 asyncFinalize();
300 // Release the params holder, so it can release the reference to us.
301 mStatementParamsHolder = nullptr;
303 return NS_OK;
306 NS_IMETHODIMP
307 AsyncStatement::BindParameters(mozIStorageBindingParamsArray* aParameters) {
308 if (mFinalized) return NS_ERROR_UNEXPECTED;
310 BindingParamsArray* array = static_cast<BindingParamsArray*>(aParameters);
311 if (array->getOwner() != this) return NS_ERROR_UNEXPECTED;
313 if (array->length() == 0) return NS_ERROR_UNEXPECTED;
315 mParamsArray = array;
316 mParamsArray->lock();
318 return NS_OK;
321 NS_IMETHODIMP
322 AsyncStatement::GetState(int32_t* _state) {
323 if (mFinalized)
324 *_state = MOZ_STORAGE_STATEMENT_INVALID;
325 else
326 *_state = MOZ_STORAGE_STATEMENT_READY;
328 return NS_OK;
331 ////////////////////////////////////////////////////////////////////////////////
332 //// mozIStorageBindingParams
334 BOILERPLATE_BIND_PROXIES(AsyncStatement,
335 if (mFinalized) return NS_ERROR_UNEXPECTED;)
337 } // namespace storage
338 } // namespace mozilla