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/. */
9 #include "mozilla/UniquePtrExtensions.h"
12 #include "mozStorageError.h"
13 #include "mozStoragePrivateHelpers.h"
14 #include "mozStorageBindingParams.h"
17 namespace mozilla::storage
{
19 ////////////////////////////////////////////////////////////////////////////////
20 //// Local Helper Objects
24 struct BindingColumnData
{
25 BindingColumnData(sqlite3_stmt
* aStmt
, int aColumn
)
26 : stmt(aStmt
), column(aColumn
) {}
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!
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"
70 ////////////////////////////////////////////////////////////////////////////////
73 BindingParams::BindingParams(mozIStorageBindingParamsArray
* aOwningArray
,
74 Statement
* aOwningStatement
)
76 mOwningArray(aOwningArray
),
77 mOwningStatement(aOwningStatement
),
79 (void)mOwningStatement
->GetParameterCount(&mParamCount
);
80 mParameters
.SetCapacity(mParamCount
);
83 BindingParams::BindingParams(mozIStorageBindingParamsArray
* aOwningArray
)
85 mOwningArray(aOwningArray
),
86 mOwningStatement(nullptr),
89 AsyncBindingParams::AsyncBindingParams(
90 mozIStorageBindingParamsArray
* aOwningArray
)
91 : BindingParams(aOwningArray
) {}
93 void BindingParams::lock() {
94 NS_ASSERTION(mLocked
== false, "Parameters have already been locked!");
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
100 mOwningStatement
= nullptr;
101 mOwningArray
= nullptr;
104 void BindingParams::unlock(Statement
* aOwningStatement
) {
105 NS_ASSERTION(mLocked
== true, "Parameters were not yet locked!");
107 mOwningStatement
= aOwningStatement
;
110 const mozIStorageBindingParamsArray
* BindingParams::getOwner() const {
114 ////////////////////////////////////////////////////////////////////////////////
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
));
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
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(":");
159 int oneIdx
= ::sqlite3_bind_parameter_index(aStatement
, name
.get());
162 nsAutoCString
errMsg(key
);
163 errMsg
.AppendLiteral(" is not a valid named parameter.");
164 err
= new Error(SQLITE_RANGE
, errMsg
.get());
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),
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
);
190 ///////////////////////////////////////////////////////////////////////////////
191 //// mozIStorageBindingParams
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.
199 nsresult rv
= mOwningStatement
->GetParameterIndex(aName
, &index
);
200 NS_ENSURE_SUCCESS(rv
, rv
);
202 return BindByIndex(index
, aValue
);
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
});
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
);
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
);
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
);
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
);
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
);
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
);
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
),
272 nsCOMPtr
<nsIVariant
> value(new BlobVariant(data
));
273 NS_ENSURE_TRUE(value
, NS_ERROR_OUT_OF_MEMORY
);
275 return BindByName(aName
, value
);
279 BindingParams::BindBlobArrayByName(const nsACString
& aName
,
280 const nsTArray
<uint8_t>& aValue
) {
281 return BindBlobByName(aName
, aValue
.Elements(), aValue
.Length());
285 BindingParams::BindStringAsBlobByName(const nsACString
& aName
,
286 const nsAString
& aValue
) {
287 return DoBindStringAsBlobByName(this, aName
, aValue
);
291 BindingParams::BindUTF8StringAsBlobByName(const nsACString
& aName
,
292 const nsACString
& aValue
) {
293 return DoBindStringAsBlobByName(this, aName
, aValue
);
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
);
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
);
319 mParameters
.ReplaceElementAt(aIndex
, variant
);
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
);
336 mParameters
.ReplaceElementAt(aIndex
, variant
);
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
);
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
);
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
);
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
);
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
);
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
);
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
),
396 nsCOMPtr
<nsIVariant
> value(new BlobVariant(data
));
397 NS_ENSURE_TRUE(value
, NS_ERROR_OUT_OF_MEMORY
);
399 return BindByIndex(aIndex
, value
);
403 BindingParams::BindBlobArrayByIndex(uint32_t aIndex
,
404 const nsTArray
<uint8_t>& aValue
) {
405 return BindBlobByIndex(aIndex
, aValue
.Elements(), aValue
.Length());
409 BindingParams::BindStringAsBlobByIndex(uint32_t aIndex
,
410 const nsAString
& aValue
) {
411 return DoBindStringAsBlobByIndex(this, aIndex
, aValue
);
415 BindingParams::BindUTF8StringAsBlobByIndex(uint32_t aIndex
,
416 const nsACString
& aValue
) {
417 return DoBindStringAsBlobByIndex(this, aIndex
, aValue
);
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