Line data Source code
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/. */
6 :
7 : #include <limits.h>
8 :
9 : #include "mozilla/UniquePtrExtensions.h"
10 : #include "nsString.h"
11 :
12 : #include "mozStorageError.h"
13 : #include "mozStoragePrivateHelpers.h"
14 : #include "mozStorageBindingParams.h"
15 : #include "mozStorageBindingParamsArray.h"
16 : #include "Variant.h"
17 :
18 : namespace mozilla {
19 : namespace storage {
20 :
21 : ////////////////////////////////////////////////////////////////////////////////
22 : //// Local Helper Objects
23 :
24 : namespace {
25 :
26 : struct BindingColumnData
27 : {
28 39 : BindingColumnData(sqlite3_stmt *aStmt,
29 : int aColumn)
30 39 : : stmt(aStmt)
31 39 : , column(aColumn)
32 : {
33 39 : }
34 : sqlite3_stmt *stmt;
35 : int column;
36 : };
37 :
38 : ////////////////////////////////////////////////////////////////////////////////
39 : //// Variant Specialization Functions (variantToSQLiteT)
40 :
41 : int
42 0 : sqlite3_T_int(BindingColumnData aData,
43 : int aValue)
44 : {
45 0 : return ::sqlite3_bind_int(aData.stmt, aData.column + 1, aValue);
46 : }
47 :
48 : int
49 17 : sqlite3_T_int64(BindingColumnData aData,
50 : sqlite3_int64 aValue)
51 : {
52 17 : return ::sqlite3_bind_int64(aData.stmt, aData.column + 1, aValue);
53 : }
54 :
55 : int
56 0 : sqlite3_T_double(BindingColumnData aData,
57 : double aValue)
58 : {
59 0 : return ::sqlite3_bind_double(aData.stmt, aData.column + 1, aValue);
60 : }
61 :
62 : int
63 13 : sqlite3_T_text(BindingColumnData aData,
64 : const nsCString& aValue)
65 : {
66 26 : return ::sqlite3_bind_text(aData.stmt,
67 13 : aData.column + 1,
68 : aValue.get(),
69 13 : aValue.Length(),
70 13 : SQLITE_TRANSIENT);
71 : }
72 :
73 : int
74 8 : sqlite3_T_text16(BindingColumnData aData,
75 : const nsString& aValue)
76 : {
77 16 : return ::sqlite3_bind_text16(aData.stmt,
78 8 : aData.column + 1,
79 8 : aValue.get(),
80 8 : aValue.Length() * 2, // Length in bytes!
81 8 : SQLITE_TRANSIENT);
82 : }
83 :
84 : int
85 1 : sqlite3_T_null(BindingColumnData aData)
86 : {
87 1 : return ::sqlite3_bind_null(aData.stmt, aData.column + 1);
88 : }
89 :
90 : int
91 0 : sqlite3_T_blob(BindingColumnData aData,
92 : const void *aBlob,
93 : int aSize)
94 : {
95 0 : return ::sqlite3_bind_blob(aData.stmt, aData.column + 1, aBlob, aSize,
96 0 : free);
97 :
98 : }
99 :
100 : #include "variantToSQLiteT_impl.h"
101 :
102 : } // namespace
103 :
104 : ////////////////////////////////////////////////////////////////////////////////
105 : //// BindingParams
106 :
107 13 : BindingParams::BindingParams(mozIStorageBindingParamsArray *aOwningArray,
108 13 : Statement *aOwningStatement)
109 : : mLocked(false)
110 : , mOwningArray(aOwningArray)
111 : , mOwningStatement(aOwningStatement)
112 13 : , mParamCount(0)
113 : {
114 13 : (void)mOwningStatement->GetParameterCount(&mParamCount);
115 13 : mParameters.SetCapacity(mParamCount);
116 13 : }
117 :
118 6 : BindingParams::BindingParams(mozIStorageBindingParamsArray *aOwningArray)
119 : : mLocked(false)
120 : , mOwningArray(aOwningArray)
121 : , mOwningStatement(nullptr)
122 6 : , mParamCount(0)
123 : {
124 6 : }
125 :
126 6 : AsyncBindingParams::AsyncBindingParams(
127 : mozIStorageBindingParamsArray *aOwningArray
128 6 : )
129 6 : : BindingParams(aOwningArray)
130 : {
131 6 : }
132 :
133 : void
134 19 : BindingParams::lock()
135 : {
136 19 : NS_ASSERTION(mLocked == false, "Parameters have already been locked!");
137 19 : mLocked = true;
138 :
139 : // We no longer need to hold a reference to our statement or our owning array.
140 : // The array owns us at this point, and it will own a reference to the
141 : // statement.
142 19 : mOwningStatement = nullptr;
143 19 : mOwningArray = nullptr;
144 19 : }
145 :
146 : void
147 19 : BindingParams::unlock(Statement *aOwningStatement)
148 : {
149 19 : NS_ASSERTION(mLocked == true, "Parameters were not yet locked!");
150 19 : mLocked = false;
151 19 : mOwningStatement = aOwningStatement;
152 19 : }
153 :
154 : const mozIStorageBindingParamsArray *
155 19 : BindingParams::getOwner() const
156 : {
157 19 : return mOwningArray;
158 : }
159 :
160 : ////////////////////////////////////////////////////////////////////////////////
161 : //// nsISupports
162 :
163 190 : NS_IMPL_ISUPPORTS(
164 : BindingParams
165 : , mozIStorageBindingParams
166 : , IStorageBindingParamsInternal
167 : )
168 :
169 :
170 : ////////////////////////////////////////////////////////////////////////////////
171 : //// IStorageBindingParamsInternal
172 :
173 : already_AddRefed<mozIStorageError>
174 13 : BindingParams::bind(sqlite3_stmt *aStatement)
175 : {
176 : // Iterate through all of our stored data, and bind it.
177 42 : for (size_t i = 0; i < mParameters.Length(); i++) {
178 29 : int rc = variantToSQLiteT(BindingColumnData(aStatement, i), mParameters[i]);
179 29 : if (rc != SQLITE_OK) {
180 : // We had an error while trying to bind. Now we need to create an error
181 : // object with the right message. Note that we special case
182 : // SQLITE_MISMATCH, but otherwise get the message from SQLite.
183 0 : const char *msg = "Could not covert nsIVariant to SQLite type.";
184 0 : if (rc != SQLITE_MISMATCH)
185 0 : msg = ::sqlite3_errmsg(::sqlite3_db_handle(aStatement));
186 :
187 0 : nsCOMPtr<mozIStorageError> err(new Error(rc, msg));
188 0 : return err.forget();
189 : }
190 : }
191 :
192 13 : return nullptr;
193 : }
194 :
195 : already_AddRefed<mozIStorageError>
196 6 : AsyncBindingParams::bind(sqlite3_stmt * aStatement)
197 : {
198 : // We should bind by index using the super-class if there is nothing in our
199 : // hashtable.
200 6 : if (!mNamedParameters.Count())
201 0 : return BindingParams::bind(aStatement);
202 :
203 12 : nsCOMPtr<mozIStorageError> err;
204 :
205 16 : for (auto iter = mNamedParameters.Iter(); !iter.Done(); iter.Next()) {
206 10 : const nsACString &key = iter.Key();
207 :
208 : // We do not accept any forms of names other than ":name", but we need to
209 : // add the colon for SQLite.
210 20 : nsAutoCString name(":");
211 10 : name.Append(key);
212 10 : int oneIdx = ::sqlite3_bind_parameter_index(aStatement, name.get());
213 :
214 10 : if (oneIdx == 0) {
215 0 : nsAutoCString errMsg(key);
216 0 : errMsg.AppendLiteral(" is not a valid named parameter.");
217 0 : err = new Error(SQLITE_RANGE, errMsg.get());
218 0 : break;
219 : }
220 :
221 : // XPCVariant's AddRef and Release are not thread-safe and so we must not
222 : // do anything that would invoke them here on the async thread. As such we
223 : // can't cram aValue into mParameters using ReplaceObjectAt so that
224 : // we can freeload off of the BindingParams::Bind implementation.
225 20 : int rc = variantToSQLiteT(BindingColumnData(aStatement, oneIdx - 1),
226 10 : iter.UserData());
227 10 : if (rc != SQLITE_OK) {
228 : // We had an error while trying to bind. Now we need to create an error
229 : // object with the right message. Note that we special case
230 : // SQLITE_MISMATCH, but otherwise get the message from SQLite.
231 0 : const char *msg = "Could not covert nsIVariant to SQLite type.";
232 0 : if (rc != SQLITE_MISMATCH) {
233 0 : msg = ::sqlite3_errmsg(::sqlite3_db_handle(aStatement));
234 : }
235 0 : err = new Error(rc, msg);
236 0 : break;
237 : }
238 : }
239 :
240 6 : return err.forget();
241 : }
242 :
243 :
244 : ///////////////////////////////////////////////////////////////////////////////
245 : //// mozIStorageBindingParams
246 :
247 : NS_IMETHODIMP
248 27 : BindingParams::BindByName(const nsACString &aName,
249 : nsIVariant *aValue)
250 : {
251 27 : NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED);
252 :
253 : // Get the column index that we need to store this at.
254 : uint32_t index;
255 27 : nsresult rv = mOwningStatement->GetParameterIndex(aName, &index);
256 27 : NS_ENSURE_SUCCESS(rv, rv);
257 :
258 27 : return BindByIndex(index, aValue);
259 : }
260 :
261 : NS_IMETHODIMP
262 10 : AsyncBindingParams::BindByName(const nsACString &aName,
263 : nsIVariant *aValue)
264 : {
265 10 : NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED);
266 :
267 20 : RefPtr<Variant_base> variant = convertVariantToStorageVariant(aValue);
268 10 : if (!variant)
269 0 : return NS_ERROR_UNEXPECTED;
270 :
271 10 : mNamedParameters.Put(aName, variant);
272 10 : return NS_OK;
273 : }
274 :
275 :
276 : NS_IMETHODIMP
277 13 : BindingParams::BindUTF8StringByName(const nsACString &aName,
278 : const nsACString &aValue)
279 : {
280 26 : nsCOMPtr<nsIVariant> value(new UTF8TextVariant(aValue));
281 13 : NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
282 :
283 13 : return BindByName(aName, value);
284 : }
285 :
286 : NS_IMETHODIMP
287 1 : BindingParams::BindStringByName(const nsACString &aName,
288 : const nsAString &aValue)
289 : {
290 2 : nsCOMPtr<nsIVariant> value(new TextVariant(aValue));
291 1 : NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
292 :
293 1 : return BindByName(aName, value);
294 : }
295 :
296 : NS_IMETHODIMP
297 0 : BindingParams::BindDoubleByName(const nsACString &aName,
298 : double aValue)
299 : {
300 0 : nsCOMPtr<nsIVariant> value(new FloatVariant(aValue));
301 0 : NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
302 :
303 0 : return BindByName(aName, value);
304 : }
305 :
306 : NS_IMETHODIMP
307 7 : BindingParams::BindInt32ByName(const nsACString &aName,
308 : int32_t aValue)
309 : {
310 14 : nsCOMPtr<nsIVariant> value(new IntegerVariant(aValue));
311 7 : NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
312 :
313 7 : return BindByName(aName, value);
314 : }
315 :
316 : NS_IMETHODIMP
317 6 : BindingParams::BindInt64ByName(const nsACString &aName,
318 : int64_t aValue)
319 : {
320 12 : nsCOMPtr<nsIVariant> value(new IntegerVariant(aValue));
321 6 : NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
322 :
323 6 : return BindByName(aName, value);
324 : }
325 :
326 : NS_IMETHODIMP
327 1 : BindingParams::BindNullByName(const nsACString &aName)
328 : {
329 2 : nsCOMPtr<nsIVariant> value(new NullVariant());
330 1 : NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
331 :
332 1 : return BindByName(aName, value);
333 : }
334 :
335 : NS_IMETHODIMP
336 0 : BindingParams::BindBlobByName(const nsACString &aName,
337 : const uint8_t *aValue,
338 : uint32_t aValueSize)
339 : {
340 0 : NS_ENSURE_ARG_MAX(aValueSize, INT_MAX);
341 : std::pair<const void *, int> data(
342 0 : static_cast<const void *>(aValue),
343 0 : int(aValueSize)
344 0 : );
345 0 : nsCOMPtr<nsIVariant> value(new BlobVariant(data));
346 0 : NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
347 :
348 0 : return BindByName(aName, value);
349 : }
350 :
351 : NS_IMETHODIMP
352 0 : BindingParams::BindStringAsBlobByName(const nsACString& aName,
353 : const nsAString& aValue)
354 : {
355 0 : return DoBindStringAsBlobByName(this, aName, aValue);
356 : }
357 :
358 : NS_IMETHODIMP
359 0 : BindingParams::BindUTF8StringAsBlobByName(const nsACString& aName,
360 : const nsACString& aValue)
361 : {
362 0 : return DoBindStringAsBlobByName(this, aName, aValue);
363 : }
364 :
365 :
366 : NS_IMETHODIMP
367 0 : BindingParams::BindAdoptedBlobByName(const nsACString &aName,
368 : uint8_t *aValue,
369 : uint32_t aValueSize)
370 : {
371 0 : UniqueFreePtr<uint8_t> uniqueValue(aValue);
372 0 : NS_ENSURE_ARG_MAX(aValueSize, INT_MAX);
373 0 : std::pair<uint8_t *, int> data(uniqueValue.release(), int(aValueSize));
374 0 : nsCOMPtr<nsIVariant> value(new AdoptedBlobVariant(data));
375 :
376 0 : return BindByName(aName, value);
377 : }
378 :
379 : NS_IMETHODIMP
380 29 : BindingParams::BindByIndex(uint32_t aIndex,
381 : nsIVariant *aValue)
382 : {
383 29 : NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED);
384 29 : ENSURE_INDEX_VALUE(aIndex, mParamCount);
385 :
386 : // Store the variant for later use.
387 58 : RefPtr<Variant_base> variant = convertVariantToStorageVariant(aValue);
388 29 : if (!variant)
389 0 : return NS_ERROR_UNEXPECTED;
390 29 : if (mParameters.Length() <= aIndex) {
391 25 : (void)mParameters.SetLength(aIndex);
392 25 : (void)mParameters.AppendElement(variant);
393 : }
394 : else {
395 4 : NS_ENSURE_TRUE(mParameters.ReplaceElementAt(aIndex, variant),
396 : NS_ERROR_OUT_OF_MEMORY);
397 : }
398 29 : return NS_OK;
399 : }
400 :
401 : NS_IMETHODIMP
402 0 : AsyncBindingParams::BindByIndex(uint32_t aIndex,
403 : nsIVariant *aValue)
404 : {
405 0 : NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED);
406 : // In the asynchronous case we do not know how many parameters there are to
407 : // bind to, so we cannot check the validity of aIndex.
408 :
409 0 : RefPtr<Variant_base> variant = convertVariantToStorageVariant(aValue);
410 0 : if (!variant)
411 0 : return NS_ERROR_UNEXPECTED;
412 0 : if (mParameters.Length() <= aIndex) {
413 0 : mParameters.SetLength(aIndex);
414 0 : mParameters.AppendElement(variant);
415 : }
416 : else {
417 0 : NS_ENSURE_TRUE(mParameters.ReplaceElementAt(aIndex, variant),
418 : NS_ERROR_OUT_OF_MEMORY);
419 : }
420 0 : return NS_OK;
421 : }
422 :
423 : NS_IMETHODIMP
424 0 : BindingParams::BindUTF8StringByIndex(uint32_t aIndex,
425 : const nsACString &aValue)
426 : {
427 0 : nsCOMPtr<nsIVariant> value(new UTF8TextVariant(aValue));
428 0 : NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
429 :
430 0 : return BindByIndex(aIndex, value);
431 : }
432 :
433 : NS_IMETHODIMP
434 0 : BindingParams::BindStringByIndex(uint32_t aIndex,
435 : const nsAString &aValue)
436 : {
437 0 : nsCOMPtr<nsIVariant> value(new TextVariant(aValue));
438 0 : NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
439 :
440 0 : return BindByIndex(aIndex, value);
441 : }
442 :
443 : NS_IMETHODIMP
444 0 : BindingParams::BindDoubleByIndex(uint32_t aIndex,
445 : double aValue)
446 : {
447 0 : nsCOMPtr<nsIVariant> value(new FloatVariant(aValue));
448 0 : NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
449 :
450 0 : return BindByIndex(aIndex, value);
451 : }
452 :
453 : NS_IMETHODIMP
454 1 : BindingParams::BindInt32ByIndex(uint32_t aIndex,
455 : int32_t aValue)
456 : {
457 2 : nsCOMPtr<nsIVariant> value(new IntegerVariant(aValue));
458 1 : NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
459 :
460 1 : return BindByIndex(aIndex, value);
461 : }
462 :
463 : NS_IMETHODIMP
464 1 : BindingParams::BindInt64ByIndex(uint32_t aIndex,
465 : int64_t aValue)
466 : {
467 2 : nsCOMPtr<nsIVariant> value(new IntegerVariant(aValue));
468 1 : NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
469 :
470 1 : return BindByIndex(aIndex, value);
471 : }
472 :
473 : NS_IMETHODIMP
474 0 : BindingParams::BindNullByIndex(uint32_t aIndex)
475 : {
476 0 : nsCOMPtr<nsIVariant> value(new NullVariant());
477 0 : NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
478 :
479 0 : return BindByIndex(aIndex, value);
480 : }
481 :
482 : NS_IMETHODIMP
483 0 : BindingParams::BindBlobByIndex(uint32_t aIndex,
484 : const uint8_t *aValue,
485 : uint32_t aValueSize)
486 : {
487 0 : NS_ENSURE_ARG_MAX(aValueSize, INT_MAX);
488 : std::pair<const void *, int> data(
489 0 : static_cast<const void *>(aValue),
490 0 : int(aValueSize)
491 0 : );
492 0 : nsCOMPtr<nsIVariant> value(new BlobVariant(data));
493 0 : NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
494 :
495 0 : return BindByIndex(aIndex, value);
496 : }
497 :
498 : NS_IMETHODIMP
499 0 : BindingParams::BindStringAsBlobByIndex(uint32_t aIndex, const nsAString& aValue)
500 : {
501 0 : return DoBindStringAsBlobByIndex(this, aIndex, aValue);
502 : }
503 :
504 : NS_IMETHODIMP
505 0 : BindingParams::BindUTF8StringAsBlobByIndex(uint32_t aIndex,
506 : const nsACString& aValue)
507 : {
508 0 : return DoBindStringAsBlobByIndex(this, aIndex, aValue);
509 : }
510 :
511 : NS_IMETHODIMP
512 0 : BindingParams::BindAdoptedBlobByIndex(uint32_t aIndex,
513 : uint8_t *aValue,
514 : uint32_t aValueSize)
515 : {
516 0 : UniqueFreePtr<uint8_t> uniqueValue(aValue);
517 0 : NS_ENSURE_ARG_MAX(aValueSize, INT_MAX);
518 0 : std::pair<uint8_t *, int> data(uniqueValue.release(), int(aValueSize));
519 0 : nsCOMPtr<nsIVariant> value(new AdoptedBlobVariant(data));
520 :
521 0 : return BindByIndex(aIndex, value);
522 : }
523 :
524 : } // namespace storage
525 : } // namespace mozilla
|