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"
15 #include "mozStorageBindingParamsArray.h"
18 namespace mozilla::storage
{
20 ////////////////////////////////////////////////////////////////////////////////
21 //// Local Helper Objects
25 struct BindingColumnData
{
26 BindingColumnData(sqlite3_stmt
* aStmt
, int aColumn
)
27 : stmt(aStmt
), column(aColumn
) {}
32 ////////////////////////////////////////////////////////////////////////////////
33 //// Variant Specialization Functions (variantToSQLiteT)
35 int sqlite3_T_int(BindingColumnData aData
, int aValue
) {
36 return ::sqlite3_bind_int(aData
.stmt
, aData
.column
+ 1, aValue
);
39 int sqlite3_T_int64(BindingColumnData aData
, sqlite3_int64 aValue
) {
40 return ::sqlite3_bind_int64(aData
.stmt
, aData
.column
+ 1, aValue
);
43 int sqlite3_T_double(BindingColumnData aData
, double aValue
) {
44 return ::sqlite3_bind_double(aData
.stmt
, aData
.column
+ 1, aValue
);
47 int sqlite3_T_text(BindingColumnData aData
, const nsCString
& aValue
) {
48 return ::sqlite3_bind_text(aData
.stmt
, aData
.column
+ 1, aValue
.get(),
49 aValue
.Length(), SQLITE_TRANSIENT
);
52 int sqlite3_T_text16(BindingColumnData aData
, const nsString
& aValue
) {
53 return ::sqlite3_bind_text16(
54 aData
.stmt
, aData
.column
+ 1, aValue
.get(),
55 aValue
.Length() * sizeof(char16_t
), // Length in bytes!
59 int sqlite3_T_null(BindingColumnData aData
) {
60 return ::sqlite3_bind_null(aData
.stmt
, aData
.column
+ 1);
63 int sqlite3_T_blob(BindingColumnData aData
, const void* aBlob
, int aSize
) {
64 return ::sqlite3_bind_blob(aData
.stmt
, aData
.column
+ 1, aBlob
, aSize
, free
);
67 #include "variantToSQLiteT_impl.h"
71 ////////////////////////////////////////////////////////////////////////////////
74 BindingParams::BindingParams(mozIStorageBindingParamsArray
* aOwningArray
,
75 Statement
* aOwningStatement
)
77 mOwningArray(aOwningArray
),
78 mOwningStatement(aOwningStatement
),
80 (void)mOwningStatement
->GetParameterCount(&mParamCount
);
81 mParameters
.SetCapacity(mParamCount
);
84 BindingParams::BindingParams(mozIStorageBindingParamsArray
* aOwningArray
)
86 mOwningArray(aOwningArray
),
87 mOwningStatement(nullptr),
90 AsyncBindingParams::AsyncBindingParams(
91 mozIStorageBindingParamsArray
* aOwningArray
)
92 : BindingParams(aOwningArray
) {}
94 void BindingParams::lock() {
95 NS_ASSERTION(mLocked
== false, "Parameters have already been locked!");
98 // We no longer need to hold a reference to our statement or our owning array.
99 // The array owns us at this point, and it will own a reference to the
101 mOwningStatement
= nullptr;
102 mOwningArray
= nullptr;
105 void BindingParams::unlock(Statement
* aOwningStatement
) {
106 NS_ASSERTION(mLocked
== true, "Parameters were not yet locked!");
108 mOwningStatement
= aOwningStatement
;
111 const mozIStorageBindingParamsArray
* BindingParams::getOwner() const {
115 ////////////////////////////////////////////////////////////////////////////////
118 NS_IMPL_ISUPPORTS(BindingParams
, mozIStorageBindingParams
,
119 IStorageBindingParamsInternal
)
121 ////////////////////////////////////////////////////////////////////////////////
122 //// IStorageBindingParamsInternal
124 already_AddRefed
<mozIStorageError
> BindingParams::bind(
125 sqlite3_stmt
* aStatement
) {
126 // Iterate through all of our stored data, and bind it.
127 for (size_t i
= 0; i
< mParameters
.Length(); i
++) {
128 int rc
= variantToSQLiteT(BindingColumnData(aStatement
, i
), mParameters
[i
]);
129 if (rc
!= SQLITE_OK
) {
130 // We had an error while trying to bind. Now we need to create an error
131 // object with the right message. Note that we special case
132 // SQLITE_MISMATCH, but otherwise get the message from SQLite.
133 const char* msg
= "Could not covert nsIVariant to SQLite type.";
134 if (rc
!= SQLITE_MISMATCH
)
135 msg
= ::sqlite3_errmsg(::sqlite3_db_handle(aStatement
));
137 nsCOMPtr
<mozIStorageError
> err(new Error(rc
, msg
));
145 already_AddRefed
<mozIStorageError
> AsyncBindingParams::bind(
146 sqlite3_stmt
* aStatement
) {
147 // We should bind by index using the super-class if there is nothing in our
149 if (!mNamedParameters
.Count()) return BindingParams::bind(aStatement
);
151 nsCOMPtr
<mozIStorageError
> err
;
153 for (const auto& entry
: mNamedParameters
) {
154 const nsACString
& key
= entry
.GetKey();
156 // We do not accept any forms of names other than ":name", but we need to
157 // add the colon for SQLite.
158 nsAutoCString
name(":");
160 int oneIdx
= ::sqlite3_bind_parameter_index(aStatement
, name
.get());
163 nsAutoCString
errMsg(key
);
164 errMsg
.AppendLiteral(" is not a valid named parameter.");
165 err
= new Error(SQLITE_RANGE
, errMsg
.get());
169 // XPCVariant's AddRef and Release are not thread-safe and so we must not
170 // do anything that would invoke them here on the async thread. As such we
171 // can't cram aValue into mParameters using ReplaceObjectAt so that
172 // we can freeload off of the BindingParams::Bind implementation.
173 int rc
= variantToSQLiteT(BindingColumnData(aStatement
, oneIdx
- 1),
175 if (rc
!= SQLITE_OK
) {
176 // We had an error while trying to bind. Now we need to create an error
177 // object with the right message. Note that we special case
178 // SQLITE_MISMATCH, but otherwise get the message from SQLite.
179 const char* msg
= "Could not covert nsIVariant to SQLite type.";
180 if (rc
!= SQLITE_MISMATCH
) {
181 msg
= ::sqlite3_errmsg(::sqlite3_db_handle(aStatement
));
183 err
= new Error(rc
, msg
);
191 ///////////////////////////////////////////////////////////////////////////////
192 //// mozIStorageBindingParams
195 BindingParams::BindByName(const nsACString
& aName
, nsIVariant
* aValue
) {
196 NS_ENSURE_FALSE(mLocked
, NS_ERROR_UNEXPECTED
);
198 // Get the column index that we need to store this at.
200 nsresult rv
= mOwningStatement
->GetParameterIndex(aName
, &index
);
201 NS_ENSURE_SUCCESS(rv
, rv
);
203 return BindByIndex(index
, aValue
);
207 AsyncBindingParams::BindByName(const nsACString
& aName
, nsIVariant
* aValue
) {
208 NS_ENSURE_FALSE(mLocked
, NS_ERROR_UNEXPECTED
);
210 RefPtr
<Variant_base
> variant
= convertVariantToStorageVariant(aValue
);
211 if (!variant
) return NS_ERROR_UNEXPECTED
;
213 mNamedParameters
.InsertOrUpdate(aName
, nsCOMPtr
<nsIVariant
>{variant
});
218 BindingParams::BindUTF8StringByName(const nsACString
& aName
,
219 const nsACString
& aValue
) {
220 nsCOMPtr
<nsIVariant
> value(new UTF8TextVariant(aValue
));
221 NS_ENSURE_TRUE(value
, NS_ERROR_OUT_OF_MEMORY
);
223 return BindByName(aName
, value
);
227 BindingParams::BindStringByName(const nsACString
& aName
,
228 const nsAString
& aValue
) {
229 nsCOMPtr
<nsIVariant
> value(new TextVariant(aValue
));
230 NS_ENSURE_TRUE(value
, NS_ERROR_OUT_OF_MEMORY
);
232 return BindByName(aName
, value
);
236 BindingParams::BindDoubleByName(const nsACString
& aName
, double aValue
) {
237 nsCOMPtr
<nsIVariant
> value(new FloatVariant(aValue
));
238 NS_ENSURE_TRUE(value
, NS_ERROR_OUT_OF_MEMORY
);
240 return BindByName(aName
, value
);
244 BindingParams::BindInt32ByName(const nsACString
& aName
, int32_t aValue
) {
245 nsCOMPtr
<nsIVariant
> value(new IntegerVariant(aValue
));
246 NS_ENSURE_TRUE(value
, NS_ERROR_OUT_OF_MEMORY
);
248 return BindByName(aName
, value
);
252 BindingParams::BindInt64ByName(const nsACString
& aName
, int64_t aValue
) {
253 nsCOMPtr
<nsIVariant
> value(new IntegerVariant(aValue
));
254 NS_ENSURE_TRUE(value
, NS_ERROR_OUT_OF_MEMORY
);
256 return BindByName(aName
, value
);
260 BindingParams::BindNullByName(const nsACString
& aName
) {
261 nsCOMPtr
<nsIVariant
> value(new NullVariant());
262 NS_ENSURE_TRUE(value
, NS_ERROR_OUT_OF_MEMORY
);
264 return BindByName(aName
, value
);
268 BindingParams::BindBlobByName(const nsACString
& aName
, const uint8_t* aValue
,
269 uint32_t aValueSize
) {
270 NS_ENSURE_ARG_MAX(aValueSize
, INT_MAX
);
271 std::pair
<const void*, int> data(static_cast<const void*>(aValue
),
273 nsCOMPtr
<nsIVariant
> value(new BlobVariant(data
));
274 NS_ENSURE_TRUE(value
, NS_ERROR_OUT_OF_MEMORY
);
276 return BindByName(aName
, value
);
280 BindingParams::BindBlobArrayByName(const nsACString
& aName
,
281 const nsTArray
<uint8_t>& aValue
) {
282 return BindBlobByName(aName
, aValue
.Elements(), aValue
.Length());
286 BindingParams::BindStringAsBlobByName(const nsACString
& aName
,
287 const nsAString
& aValue
) {
288 return DoBindStringAsBlobByName(this, aName
, aValue
);
292 BindingParams::BindUTF8StringAsBlobByName(const nsACString
& aName
,
293 const nsACString
& aValue
) {
294 return DoBindStringAsBlobByName(this, aName
, aValue
);
298 BindingParams::BindAdoptedBlobByName(const nsACString
& aName
, uint8_t* aValue
,
299 uint32_t aValueSize
) {
300 UniqueFreePtr
<uint8_t> uniqueValue(aValue
);
301 NS_ENSURE_ARG_MAX(aValueSize
, INT_MAX
);
302 std::pair
<uint8_t*, int> data(uniqueValue
.release(), int(aValueSize
));
303 nsCOMPtr
<nsIVariant
> value(new AdoptedBlobVariant(data
));
305 return BindByName(aName
, value
);
309 BindingParams::BindByIndex(uint32_t aIndex
, nsIVariant
* aValue
) {
310 NS_ENSURE_FALSE(mLocked
, NS_ERROR_UNEXPECTED
);
311 ENSURE_INDEX_VALUE(aIndex
, mParamCount
);
313 // Store the variant for later use.
314 RefPtr
<Variant_base
> variant
= convertVariantToStorageVariant(aValue
);
315 if (!variant
) return NS_ERROR_UNEXPECTED
;
316 if (mParameters
.Length() <= aIndex
) {
317 (void)mParameters
.SetLength(aIndex
);
318 (void)mParameters
.AppendElement(variant
);
320 mParameters
.ReplaceElementAt(aIndex
, variant
);
326 AsyncBindingParams::BindByIndex(uint32_t aIndex
, nsIVariant
* aValue
) {
327 NS_ENSURE_FALSE(mLocked
, NS_ERROR_UNEXPECTED
);
328 // In the asynchronous case we do not know how many parameters there are to
329 // bind to, so we cannot check the validity of aIndex.
331 RefPtr
<Variant_base
> variant
= convertVariantToStorageVariant(aValue
);
332 if (!variant
) return NS_ERROR_UNEXPECTED
;
333 if (mParameters
.Length() <= aIndex
) {
334 mParameters
.SetLength(aIndex
);
335 mParameters
.AppendElement(variant
);
337 mParameters
.ReplaceElementAt(aIndex
, variant
);
343 BindingParams::BindUTF8StringByIndex(uint32_t aIndex
,
344 const nsACString
& aValue
) {
345 nsCOMPtr
<nsIVariant
> value(new UTF8TextVariant(aValue
));
346 NS_ENSURE_TRUE(value
, NS_ERROR_OUT_OF_MEMORY
);
348 return BindByIndex(aIndex
, value
);
352 BindingParams::BindStringByIndex(uint32_t aIndex
, const nsAString
& aValue
) {
353 nsCOMPtr
<nsIVariant
> value(new TextVariant(aValue
));
354 NS_ENSURE_TRUE(value
, NS_ERROR_OUT_OF_MEMORY
);
356 return BindByIndex(aIndex
, value
);
360 BindingParams::BindDoubleByIndex(uint32_t aIndex
, double aValue
) {
361 nsCOMPtr
<nsIVariant
> value(new FloatVariant(aValue
));
362 NS_ENSURE_TRUE(value
, NS_ERROR_OUT_OF_MEMORY
);
364 return BindByIndex(aIndex
, value
);
368 BindingParams::BindInt32ByIndex(uint32_t aIndex
, int32_t aValue
) {
369 nsCOMPtr
<nsIVariant
> value(new IntegerVariant(aValue
));
370 NS_ENSURE_TRUE(value
, NS_ERROR_OUT_OF_MEMORY
);
372 return BindByIndex(aIndex
, value
);
376 BindingParams::BindInt64ByIndex(uint32_t aIndex
, int64_t aValue
) {
377 nsCOMPtr
<nsIVariant
> value(new IntegerVariant(aValue
));
378 NS_ENSURE_TRUE(value
, NS_ERROR_OUT_OF_MEMORY
);
380 return BindByIndex(aIndex
, value
);
384 BindingParams::BindNullByIndex(uint32_t aIndex
) {
385 nsCOMPtr
<nsIVariant
> value(new NullVariant());
386 NS_ENSURE_TRUE(value
, NS_ERROR_OUT_OF_MEMORY
);
388 return BindByIndex(aIndex
, value
);
392 BindingParams::BindBlobByIndex(uint32_t aIndex
, const uint8_t* aValue
,
393 uint32_t aValueSize
) {
394 NS_ENSURE_ARG_MAX(aValueSize
, INT_MAX
);
395 std::pair
<const void*, int> data(static_cast<const void*>(aValue
),
397 nsCOMPtr
<nsIVariant
> value(new BlobVariant(data
));
398 NS_ENSURE_TRUE(value
, NS_ERROR_OUT_OF_MEMORY
);
400 return BindByIndex(aIndex
, value
);
404 BindingParams::BindBlobArrayByIndex(uint32_t aIndex
,
405 const nsTArray
<uint8_t>& aValue
) {
406 return BindBlobByIndex(aIndex
, aValue
.Elements(), aValue
.Length());
410 BindingParams::BindStringAsBlobByIndex(uint32_t aIndex
,
411 const nsAString
& aValue
) {
412 return DoBindStringAsBlobByIndex(this, aIndex
, aValue
);
416 BindingParams::BindUTF8StringAsBlobByIndex(uint32_t aIndex
,
417 const nsACString
& aValue
) {
418 return DoBindStringAsBlobByIndex(this, aIndex
, aValue
);
422 BindingParams::BindAdoptedBlobByIndex(uint32_t aIndex
, uint8_t* aValue
,
423 uint32_t aValueSize
) {
424 UniqueFreePtr
<uint8_t> uniqueValue(aValue
);
425 NS_ENSURE_ARG_MAX(aValueSize
, INT_MAX
);
426 std::pair
<uint8_t*, int> data(uniqueValue
.release(), int(aValueSize
));
427 nsCOMPtr
<nsIVariant
> value(new AdoptedBlobVariant(data
));
429 return BindByIndex(aIndex
, value
);
432 } // namespace mozilla::storage