Bug 1850517 [wpt PR 41687] - WebKit export of https://bugs.webkit.org/show_bug.cgi...
[gecko.git] / storage / mozStorageBindingParams.cpp
blob1cd50c7a6f16569f0fbc1b777c195358aef0c447
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>
9 #include "mozilla/UniquePtrExtensions.h"
10 #include "nsString.h"
12 #include "mozStorageError.h"
13 #include "mozStoragePrivateHelpers.h"
14 #include "mozStorageBindingParams.h"
15 #include "Variant.h"
17 namespace mozilla::storage {
19 ////////////////////////////////////////////////////////////////////////////////
20 //// Local Helper Objects
22 namespace {
24 struct BindingColumnData {
25 BindingColumnData(sqlite3_stmt* aStmt, int aColumn)
26 : stmt(aStmt), column(aColumn) {}
27 sqlite3_stmt* stmt;
28 int column;
31 ////////////////////////////////////////////////////////////////////////////////
32 //// Variant Specialization Functions (variantToSQLiteT)
34 int sqlite3_T_int(BindingColumnData aData, int aValue) {
35 return ::sqlite3_bind_int(aData.stmt, aData.column + 1, aValue);
38 int sqlite3_T_int64(BindingColumnData aData, sqlite3_int64 aValue) {
39 return ::sqlite3_bind_int64(aData.stmt, aData.column + 1, aValue);
42 int sqlite3_T_double(BindingColumnData aData, double aValue) {
43 return ::sqlite3_bind_double(aData.stmt, aData.column + 1, aValue);
46 int sqlite3_T_text(BindingColumnData aData, const nsCString& aValue) {
47 return ::sqlite3_bind_text(aData.stmt, aData.column + 1, aValue.get(),
48 aValue.Length(), SQLITE_TRANSIENT);
51 int sqlite3_T_text16(BindingColumnData aData, const nsString& aValue) {
52 return ::sqlite3_bind_text16(
53 aData.stmt, aData.column + 1, aValue.get(),
54 aValue.Length() * sizeof(char16_t), // Length in bytes!
55 SQLITE_TRANSIENT);
58 int sqlite3_T_null(BindingColumnData aData) {
59 return ::sqlite3_bind_null(aData.stmt, aData.column + 1);
62 int sqlite3_T_blob(BindingColumnData aData, const void* aBlob, int aSize) {
63 return ::sqlite3_bind_blob(aData.stmt, aData.column + 1, aBlob, aSize, free);
66 #include "variantToSQLiteT_impl.h"
68 } // namespace
70 ////////////////////////////////////////////////////////////////////////////////
71 //// BindingParams
73 BindingParams::BindingParams(mozIStorageBindingParamsArray* aOwningArray,
74 Statement* aOwningStatement)
75 : mLocked(false),
76 mOwningArray(aOwningArray),
77 mOwningStatement(aOwningStatement),
78 mParamCount(0) {
79 (void)mOwningStatement->GetParameterCount(&mParamCount);
80 mParameters.SetCapacity(mParamCount);
83 BindingParams::BindingParams(mozIStorageBindingParamsArray* aOwningArray)
84 : mLocked(false),
85 mOwningArray(aOwningArray),
86 mOwningStatement(nullptr),
87 mParamCount(0) {}
89 AsyncBindingParams::AsyncBindingParams(
90 mozIStorageBindingParamsArray* aOwningArray)
91 : BindingParams(aOwningArray) {}
93 void BindingParams::lock() {
94 NS_ASSERTION(mLocked == false, "Parameters have already been locked!");
95 mLocked = true;
97 // We no longer need to hold a reference to our statement or our owning array.
98 // The array owns us at this point, and it will own a reference to the
99 // statement.
100 mOwningStatement = nullptr;
101 mOwningArray = nullptr;
104 void BindingParams::unlock(Statement* aOwningStatement) {
105 NS_ASSERTION(mLocked == true, "Parameters were not yet locked!");
106 mLocked = false;
107 mOwningStatement = aOwningStatement;
110 const mozIStorageBindingParamsArray* BindingParams::getOwner() const {
111 return mOwningArray;
114 ////////////////////////////////////////////////////////////////////////////////
115 //// nsISupports
117 NS_IMPL_ISUPPORTS(BindingParams, mozIStorageBindingParams,
118 IStorageBindingParamsInternal)
120 ////////////////////////////////////////////////////////////////////////////////
121 //// IStorageBindingParamsInternal
123 already_AddRefed<mozIStorageError> BindingParams::bind(
124 sqlite3_stmt* aStatement) {
125 // Iterate through all of our stored data, and bind it.
126 for (size_t i = 0; i < mParameters.Length(); i++) {
127 int rc = variantToSQLiteT(BindingColumnData(aStatement, i), mParameters[i]);
128 if (rc != SQLITE_OK) {
129 // We had an error while trying to bind. Now we need to create an error
130 // object with the right message. Note that we special case
131 // SQLITE_MISMATCH, but otherwise get the message from SQLite.
132 const char* msg = "Could not covert nsIVariant to SQLite type.";
133 if (rc != SQLITE_MISMATCH)
134 msg = ::sqlite3_errmsg(::sqlite3_db_handle(aStatement));
136 nsCOMPtr<mozIStorageError> err(new Error(rc, msg));
137 return err.forget();
141 return nullptr;
144 already_AddRefed<mozIStorageError> AsyncBindingParams::bind(
145 sqlite3_stmt* aStatement) {
146 // We should bind by index using the super-class if there is nothing in our
147 // hashtable.
148 if (!mNamedParameters.Count()) return BindingParams::bind(aStatement);
150 nsCOMPtr<mozIStorageError> err;
152 for (const auto& entry : mNamedParameters) {
153 const nsACString& key = entry.GetKey();
155 // We do not accept any forms of names other than ":name", but we need to
156 // add the colon for SQLite.
157 nsAutoCString name(":");
158 name.Append(key);
159 int oneIdx = ::sqlite3_bind_parameter_index(aStatement, name.get());
161 if (oneIdx == 0) {
162 nsAutoCString errMsg(key);
163 errMsg.AppendLiteral(" is not a valid named parameter.");
164 err = new Error(SQLITE_RANGE, errMsg.get());
165 break;
168 // XPCVariant's AddRef and Release are not thread-safe and so we must not
169 // do anything that would invoke them here on the async thread. As such we
170 // can't cram aValue into mParameters using ReplaceObjectAt so that
171 // we can freeload off of the BindingParams::Bind implementation.
172 int rc = variantToSQLiteT(BindingColumnData(aStatement, oneIdx - 1),
173 entry.GetWeak());
174 if (rc != SQLITE_OK) {
175 // We had an error while trying to bind. Now we need to create an error
176 // object with the right message. Note that we special case
177 // SQLITE_MISMATCH, but otherwise get the message from SQLite.
178 const char* msg = "Could not covert nsIVariant to SQLite type.";
179 if (rc != SQLITE_MISMATCH) {
180 msg = ::sqlite3_errmsg(::sqlite3_db_handle(aStatement));
182 err = new Error(rc, msg);
183 break;
187 return err.forget();
190 ///////////////////////////////////////////////////////////////////////////////
191 //// mozIStorageBindingParams
193 NS_IMETHODIMP
194 BindingParams::BindByName(const nsACString& aName, nsIVariant* aValue) {
195 NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED);
197 // Get the column index that we need to store this at.
198 uint32_t index;
199 nsresult rv = mOwningStatement->GetParameterIndex(aName, &index);
200 NS_ENSURE_SUCCESS(rv, rv);
202 return BindByIndex(index, aValue);
205 NS_IMETHODIMP
206 AsyncBindingParams::BindByName(const nsACString& aName, nsIVariant* aValue) {
207 NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED);
209 RefPtr<Variant_base> variant = convertVariantToStorageVariant(aValue);
210 if (!variant) return NS_ERROR_UNEXPECTED;
212 mNamedParameters.InsertOrUpdate(aName, nsCOMPtr<nsIVariant>{variant});
213 return NS_OK;
216 NS_IMETHODIMP
217 BindingParams::BindUTF8StringByName(const nsACString& aName,
218 const nsACString& aValue) {
219 nsCOMPtr<nsIVariant> value(new UTF8TextVariant(aValue));
220 NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
222 return BindByName(aName, value);
225 NS_IMETHODIMP
226 BindingParams::BindStringByName(const nsACString& aName,
227 const nsAString& aValue) {
228 nsCOMPtr<nsIVariant> value(new TextVariant(aValue));
229 NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
231 return BindByName(aName, value);
234 NS_IMETHODIMP
235 BindingParams::BindDoubleByName(const nsACString& aName, double aValue) {
236 nsCOMPtr<nsIVariant> value(new FloatVariant(aValue));
237 NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
239 return BindByName(aName, value);
242 NS_IMETHODIMP
243 BindingParams::BindInt32ByName(const nsACString& aName, int32_t aValue) {
244 nsCOMPtr<nsIVariant> value(new IntegerVariant(aValue));
245 NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
247 return BindByName(aName, value);
250 NS_IMETHODIMP
251 BindingParams::BindInt64ByName(const nsACString& aName, int64_t aValue) {
252 nsCOMPtr<nsIVariant> value(new IntegerVariant(aValue));
253 NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
255 return BindByName(aName, value);
258 NS_IMETHODIMP
259 BindingParams::BindNullByName(const nsACString& aName) {
260 nsCOMPtr<nsIVariant> value(new NullVariant());
261 NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
263 return BindByName(aName, value);
266 NS_IMETHODIMP
267 BindingParams::BindBlobByName(const nsACString& aName, const uint8_t* aValue,
268 uint32_t aValueSize) {
269 NS_ENSURE_ARG_MAX(aValueSize, INT_MAX);
270 std::pair<const void*, int> data(static_cast<const void*>(aValue),
271 int(aValueSize));
272 nsCOMPtr<nsIVariant> value(new BlobVariant(data));
273 NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
275 return BindByName(aName, value);
278 NS_IMETHODIMP
279 BindingParams::BindBlobArrayByName(const nsACString& aName,
280 const nsTArray<uint8_t>& aValue) {
281 return BindBlobByName(aName, aValue.Elements(), aValue.Length());
284 NS_IMETHODIMP
285 BindingParams::BindStringAsBlobByName(const nsACString& aName,
286 const nsAString& aValue) {
287 return DoBindStringAsBlobByName(this, aName, aValue);
290 NS_IMETHODIMP
291 BindingParams::BindUTF8StringAsBlobByName(const nsACString& aName,
292 const nsACString& aValue) {
293 return DoBindStringAsBlobByName(this, aName, aValue);
296 NS_IMETHODIMP
297 BindingParams::BindAdoptedBlobByName(const nsACString& aName, uint8_t* aValue,
298 uint32_t aValueSize) {
299 UniqueFreePtr<uint8_t> uniqueValue(aValue);
300 NS_ENSURE_ARG_MAX(aValueSize, INT_MAX);
301 std::pair<uint8_t*, int> data(uniqueValue.release(), int(aValueSize));
302 nsCOMPtr<nsIVariant> value(new AdoptedBlobVariant(data));
304 return BindByName(aName, value);
307 NS_IMETHODIMP
308 BindingParams::BindByIndex(uint32_t aIndex, nsIVariant* aValue) {
309 NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED);
310 ENSURE_INDEX_VALUE(aIndex, mParamCount);
312 // Store the variant for later use.
313 RefPtr<Variant_base> variant = convertVariantToStorageVariant(aValue);
314 if (!variant) return NS_ERROR_UNEXPECTED;
315 if (mParameters.Length() <= aIndex) {
316 (void)mParameters.SetLength(aIndex);
317 (void)mParameters.AppendElement(variant);
318 } else {
319 mParameters.ReplaceElementAt(aIndex, variant);
321 return NS_OK;
324 NS_IMETHODIMP
325 AsyncBindingParams::BindByIndex(uint32_t aIndex, nsIVariant* aValue) {
326 NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED);
327 // In the asynchronous case we do not know how many parameters there are to
328 // bind to, so we cannot check the validity of aIndex.
330 RefPtr<Variant_base> variant = convertVariantToStorageVariant(aValue);
331 if (!variant) return NS_ERROR_UNEXPECTED;
332 if (mParameters.Length() <= aIndex) {
333 mParameters.SetLength(aIndex);
334 mParameters.AppendElement(variant);
335 } else {
336 mParameters.ReplaceElementAt(aIndex, variant);
338 return NS_OK;
341 NS_IMETHODIMP
342 BindingParams::BindUTF8StringByIndex(uint32_t aIndex,
343 const nsACString& aValue) {
344 nsCOMPtr<nsIVariant> value(new UTF8TextVariant(aValue));
345 NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
347 return BindByIndex(aIndex, value);
350 NS_IMETHODIMP
351 BindingParams::BindStringByIndex(uint32_t aIndex, const nsAString& aValue) {
352 nsCOMPtr<nsIVariant> value(new TextVariant(aValue));
353 NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
355 return BindByIndex(aIndex, value);
358 NS_IMETHODIMP
359 BindingParams::BindDoubleByIndex(uint32_t aIndex, double aValue) {
360 nsCOMPtr<nsIVariant> value(new FloatVariant(aValue));
361 NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
363 return BindByIndex(aIndex, value);
366 NS_IMETHODIMP
367 BindingParams::BindInt32ByIndex(uint32_t aIndex, int32_t aValue) {
368 nsCOMPtr<nsIVariant> value(new IntegerVariant(aValue));
369 NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
371 return BindByIndex(aIndex, value);
374 NS_IMETHODIMP
375 BindingParams::BindInt64ByIndex(uint32_t aIndex, int64_t aValue) {
376 nsCOMPtr<nsIVariant> value(new IntegerVariant(aValue));
377 NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
379 return BindByIndex(aIndex, value);
382 NS_IMETHODIMP
383 BindingParams::BindNullByIndex(uint32_t aIndex) {
384 nsCOMPtr<nsIVariant> value(new NullVariant());
385 NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
387 return BindByIndex(aIndex, value);
390 NS_IMETHODIMP
391 BindingParams::BindBlobByIndex(uint32_t aIndex, const uint8_t* aValue,
392 uint32_t aValueSize) {
393 NS_ENSURE_ARG_MAX(aValueSize, INT_MAX);
394 std::pair<const void*, int> data(static_cast<const void*>(aValue),
395 int(aValueSize));
396 nsCOMPtr<nsIVariant> value(new BlobVariant(data));
397 NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
399 return BindByIndex(aIndex, value);
402 NS_IMETHODIMP
403 BindingParams::BindBlobArrayByIndex(uint32_t aIndex,
404 const nsTArray<uint8_t>& aValue) {
405 return BindBlobByIndex(aIndex, aValue.Elements(), aValue.Length());
408 NS_IMETHODIMP
409 BindingParams::BindStringAsBlobByIndex(uint32_t aIndex,
410 const nsAString& aValue) {
411 return DoBindStringAsBlobByIndex(this, aIndex, aValue);
414 NS_IMETHODIMP
415 BindingParams::BindUTF8StringAsBlobByIndex(uint32_t aIndex,
416 const nsACString& aValue) {
417 return DoBindStringAsBlobByIndex(this, aIndex, aValue);
420 NS_IMETHODIMP
421 BindingParams::BindAdoptedBlobByIndex(uint32_t aIndex, uint8_t* aValue,
422 uint32_t aValueSize) {
423 UniqueFreePtr<uint8_t> uniqueValue(aValue);
424 NS_ENSURE_ARG_MAX(aValueSize, INT_MAX);
425 std::pair<uint8_t*, int> data(uniqueValue.release(), int(aValueSize));
426 nsCOMPtr<nsIVariant> value(new AdoptedBlobVariant(data));
428 return BindByIndex(aIndex, value);
431 } // namespace mozilla::storage