Backed out changeset f9ccd9f31b9c (bug 1917901) for causing networking crashes. a...
[gecko.git] / storage / mozStorageStatement.cpp
blob6de46e6ea8709ff7c83c2880f55e2ca985d9732c
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 "nsThreadUtils.h"
12 #include "nsIClassInfoImpl.h"
13 #include "Variant.h"
15 #include "mozIStorageError.h"
17 #include "mozStorageBindingParams.h"
18 #include "mozStorageConnection.h"
19 #include "mozStorageStatementJSHelper.h"
20 #include "mozStoragePrivateHelpers.h"
21 #include "mozStorageStatementParams.h"
22 #include "mozStorageStatementRow.h"
23 #include "mozStorageStatement.h"
25 #include "mozilla/Logging.h"
26 #include "mozilla/Printf.h"
27 #include "mozilla/ProfilerLabels.h"
29 extern mozilla::LazyLogModule gStorageLog;
31 namespace mozilla {
32 namespace storage {
34 ////////////////////////////////////////////////////////////////////////////////
35 //// nsIClassInfo
37 NS_IMPL_CI_INTERFACE_GETTER(Statement, mozIStorageStatement,
38 mozIStorageBaseStatement, mozIStorageBindingParams,
39 mozIStorageValueArray,
40 mozilla::storage::StorageBaseStatementInternal)
42 class StatementClassInfo : public nsIClassInfo {
43 public:
44 constexpr StatementClassInfo() = default;
46 NS_DECL_ISUPPORTS_INHERITED
48 NS_IMETHOD
49 GetInterfaces(nsTArray<nsIID>& _array) override {
50 return NS_CI_INTERFACE_GETTER_NAME(Statement)(_array);
53 NS_IMETHOD
54 GetScriptableHelper(nsIXPCScriptable** _helper) override {
55 static StatementJSHelper sJSHelper;
56 *_helper = &sJSHelper;
57 return NS_OK;
60 NS_IMETHOD
61 GetContractID(nsACString& aContractID) override {
62 aContractID.SetIsVoid(true);
63 return NS_OK;
66 NS_IMETHOD
67 GetClassDescription(nsACString& aDesc) override {
68 aDesc.SetIsVoid(true);
69 return NS_OK;
72 NS_IMETHOD
73 GetClassID(nsCID** _id) override {
74 *_id = nullptr;
75 return NS_OK;
78 NS_IMETHOD
79 GetFlags(uint32_t* _flags) override {
80 *_flags = 0;
81 return NS_OK;
84 NS_IMETHOD
85 GetClassIDNoAlloc(nsCID* _cid) override { return NS_ERROR_NOT_AVAILABLE; }
88 NS_IMETHODIMP_(MozExternalRefCountType) StatementClassInfo::AddRef() {
89 return 2;
91 NS_IMETHODIMP_(MozExternalRefCountType) StatementClassInfo::Release() {
92 return 1;
94 NS_IMPL_QUERY_INTERFACE(StatementClassInfo, nsIClassInfo)
96 static StatementClassInfo sStatementClassInfo;
98 ////////////////////////////////////////////////////////////////////////////////
99 //// Statement
101 Statement::Statement()
102 : mDBStatement(nullptr),
103 mParamCount(0),
104 mResultColumnCount(0),
105 mExecuting(false),
106 mQueryStatusRecorded(false),
107 mHasExecuted(false) {}
109 nsresult Statement::initialize(Connection* aDBConnection,
110 sqlite3* aNativeConnection,
111 const nsACString& aSQLStatement) {
112 MOZ_ASSERT(aDBConnection, "No database connection given!");
113 MOZ_ASSERT(aDBConnection->isConnectionReadyOnThisThread(),
114 "Database connection should be valid");
115 MOZ_ASSERT(!mDBStatement, "Statement already initialized!");
116 MOZ_ASSERT(aNativeConnection, "No native connection given!");
118 int srv = aDBConnection->prepareStatement(
119 aNativeConnection, PromiseFlatCString(aSQLStatement), &mDBStatement);
120 if (srv != SQLITE_OK) {
121 MOZ_LOG(gStorageLog, LogLevel::Error,
122 ("Sqlite statement prepare error: %d '%s'", srv,
123 ::sqlite3_errmsg(aNativeConnection)));
124 MOZ_LOG(gStorageLog, LogLevel::Error,
125 ("Statement was: '%s'", PromiseFlatCString(aSQLStatement).get()));
127 aDBConnection->RecordQueryStatus(srv);
128 mQueryStatusRecorded = true;
129 return convertResultCode(srv);
132 MOZ_LOG(gStorageLog, LogLevel::Debug,
133 ("Initialized statement '%s' (0x%p)",
134 PromiseFlatCString(aSQLStatement).get(), mDBStatement));
136 mDBConnection = aDBConnection;
137 mNativeConnection = aNativeConnection;
138 mParamCount = ::sqlite3_bind_parameter_count(mDBStatement);
139 mResultColumnCount = ::sqlite3_column_count(mDBStatement);
140 mColumnNames.Clear();
142 nsCString* columnNames = mColumnNames.AppendElements(mResultColumnCount);
143 for (uint32_t i = 0; i < mResultColumnCount; i++) {
144 const char* name = ::sqlite3_column_name(mDBStatement, i);
145 columnNames[i].Assign(name);
148 #ifdef DEBUG
149 // We want to try and test for LIKE and that consumers are using
150 // escapeStringForLIKE instead of just trusting user input. The idea to
151 // check to see if they are binding a parameter after like instead of just
152 // using a string. We only do this in debug builds because it's expensive!
153 auto c = nsCaseInsensitiveCStringComparator;
154 nsACString::const_iterator start, end, e;
155 aSQLStatement.BeginReading(start);
156 aSQLStatement.EndReading(end);
157 e = end;
158 while (::FindInReadable(" LIKE"_ns, start, e, c)) {
159 // We have a LIKE in here, so we perform our tests
160 // FindInReadable moves the iterator, so we have to get a new one for
161 // each test we perform.
162 nsACString::const_iterator s1, s2, s3;
163 s1 = s2 = s3 = start;
165 if (!(::FindInReadable(" LIKE ?"_ns, s1, end, c) ||
166 ::FindInReadable(" LIKE :"_ns, s2, end, c) ||
167 ::FindInReadable(" LIKE @"_ns, s3, end, c))) {
168 // At this point, we didn't find a LIKE statement followed by ?, :,
169 // or @, all of which are valid characters for binding a parameter.
170 // We will warn the consumer that they may not be safely using LIKE.
171 NS_WARNING(
172 "Unsafe use of LIKE detected! Please ensure that you "
173 "are using mozIStorageStatement::escapeStringForLIKE "
174 "and that you are binding that result to the statement "
175 "to prevent SQL injection attacks.");
178 // resetting start and e
179 start = e;
180 e = end;
182 #endif
184 return NS_OK;
187 mozIStorageBindingParams* Statement::getParams() {
188 nsresult rv;
190 // If we do not have an array object yet, make it.
191 if (!mParamsArray) {
192 nsCOMPtr<mozIStorageBindingParamsArray> array;
193 rv = NewBindingParamsArray(getter_AddRefs(array));
194 NS_ENSURE_SUCCESS(rv, nullptr);
196 mParamsArray = static_cast<BindingParamsArray*>(array.get());
199 // If there isn't already any rows added, we'll have to add one to use.
200 if (mParamsArray->length() == 0) {
201 RefPtr<BindingParams> params(new BindingParams(mParamsArray, this));
202 NS_ENSURE_TRUE(params, nullptr);
204 rv = mParamsArray->AddParams(params);
205 NS_ENSURE_SUCCESS(rv, nullptr);
207 // We have to unlock our params because AddParams locks them. This is safe
208 // because no reference to the params object was, or ever will be given out.
209 params->unlock(this);
211 // We also want to lock our array at this point - we don't want anything to
212 // be added to it. Nothing has, or will ever get a reference to it, but we
213 // will get additional safety checks via assertions by doing this.
214 mParamsArray->lock();
217 return *mParamsArray->begin();
220 void Statement::MaybeRecordQueryStatus(int srv, bool isResetting) {
221 // If the statement hasn't been executed synchronously since it was last reset
222 // or created then there is no need to record anything. Asynchronous
223 // statements have their status tracked and recorded by StatementData.
224 if (!mHasExecuted) {
225 return;
228 if (!isResetting && !isErrorCode(srv)) {
229 // Non-errors will be recorded when finalizing.
230 return;
233 // We only record a status if no status has been recorded previously.
234 if (!mQueryStatusRecorded && mDBConnection) {
235 mDBConnection->RecordQueryStatus(srv);
238 // Allow another status to be recorded if we are resetting this statement.
239 mQueryStatusRecorded = !isResetting;
242 Statement::~Statement() { (void)internalFinalize(true); }
244 ////////////////////////////////////////////////////////////////////////////////
245 //// nsISupports
247 NS_IMPL_ADDREF(Statement)
248 NS_IMPL_RELEASE(Statement)
250 NS_INTERFACE_MAP_BEGIN(Statement)
251 NS_INTERFACE_MAP_ENTRY(mozIStorageStatement)
252 NS_INTERFACE_MAP_ENTRY(mozIStorageBaseStatement)
253 NS_INTERFACE_MAP_ENTRY(mozIStorageBindingParams)
254 NS_INTERFACE_MAP_ENTRY(mozIStorageValueArray)
255 NS_INTERFACE_MAP_ENTRY(mozilla::storage::StorageBaseStatementInternal)
256 if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
257 foundInterface = static_cast<nsIClassInfo*>(&sStatementClassInfo);
258 } else
259 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageStatement)
260 NS_INTERFACE_MAP_END
262 ////////////////////////////////////////////////////////////////////////////////
263 //// StorageBaseStatementInternal
265 Connection* Statement::getOwner() { return mDBConnection; }
267 int Statement::getAsyncStatement(sqlite3_stmt** _stmt) {
268 // If we have no statement, we shouldn't be calling this method!
269 NS_ASSERTION(mDBStatement != nullptr, "We have no statement to clone!");
271 // If we do not yet have a cached async statement, clone our statement now.
272 if (!mAsyncStatement) {
273 nsDependentCString sql(::sqlite3_sql(mDBStatement));
274 int rc = mDBConnection->prepareStatement(mNativeConnection, sql,
275 &mAsyncStatement);
276 if (rc != SQLITE_OK) {
277 mDBConnection->RecordQueryStatus(rc);
278 *_stmt = nullptr;
279 return rc;
282 MOZ_LOG(gStorageLog, LogLevel::Debug,
283 ("Cloned statement 0x%p to 0x%p", mDBStatement, mAsyncStatement));
286 *_stmt = mAsyncStatement;
287 return SQLITE_OK;
290 nsresult Statement::getAsynchronousStatementData(StatementData& _data) {
291 if (!mDBStatement) return NS_ERROR_UNEXPECTED;
293 sqlite3_stmt* stmt;
294 int rc = getAsyncStatement(&stmt);
295 if (rc != SQLITE_OK) return convertResultCode(rc);
297 _data = StatementData(stmt, bindingParamsArray(), this);
299 return NS_OK;
302 already_AddRefed<mozIStorageBindingParams> Statement::newBindingParams(
303 mozIStorageBindingParamsArray* aOwner) {
304 nsCOMPtr<mozIStorageBindingParams> params = new BindingParams(aOwner, this);
305 return params.forget();
308 ////////////////////////////////////////////////////////////////////////////////
309 //// mozIStorageStatement
311 // proxy to StorageBaseStatementInternal using its define helper.
312 MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(Statement, (void)0;)
314 NS_IMETHODIMP
315 Statement::Clone(mozIStorageStatement** _statement) {
316 RefPtr<Statement> statement(new Statement());
317 NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
319 nsAutoCString sql(::sqlite3_sql(mDBStatement));
320 nsresult rv = statement->initialize(mDBConnection, mNativeConnection, sql);
321 NS_ENSURE_SUCCESS(rv, rv);
323 statement.forget(_statement);
324 return NS_OK;
327 NS_IMETHODIMP
328 Statement::Finalize() { return internalFinalize(false); }
330 nsresult Statement::internalFinalize(bool aDestructing) {
331 if (!mDBStatement) return NS_OK;
333 int srv = SQLITE_OK;
336 // If the statement ends up being finalized twice, the second finalization
337 // would apply to a dangling pointer and may cause unexpected consequences.
338 // Thus we must be sure that the connection state won't change during this
339 // operation, to avoid racing with finalizations made by the closing
340 // connection. See Connection::internalClose().
341 MutexAutoLock lockedScope(mDBConnection->sharedAsyncExecutionMutex);
342 if (!mDBConnection->isClosed(lockedScope)) {
343 MOZ_LOG(gStorageLog, LogLevel::Debug,
344 ("Finalizing statement '%s' during garbage-collection",
345 ::sqlite3_sql(mDBStatement)));
346 srv = ::sqlite3_finalize(mDBStatement);
348 #ifdef DEBUG
349 else {
350 // The database connection is closed. The sqlite
351 // statement has either been finalized already by the connection
352 // or is about to be finalized by the connection.
354 // Finalizing it here would be useless and segfaultish.
356 // Note that we can't display the statement itself, as the data structure
357 // is not valid anymore. However, the address shown here should help
358 // developers correlate with the more complete debug message triggered
359 // by AsyncClose().
361 SmprintfPointer msg = ::mozilla::Smprintf(
362 "SQL statement (%p) should have been finalized"
363 " before garbage-collection. For more details on this statement, set"
364 " NSPR_LOG_MESSAGES=mozStorage:5 .",
365 mDBStatement);
366 NS_WARNING(msg.get());
368 // Use %s so we aren't exposing random strings to printf interpolation.
369 MOZ_LOG(gStorageLog, LogLevel::Warning, ("%s", msg.get()));
371 #endif // DEBUG
374 // This will be a no-op if the status has already been recorded or if this
375 // statement has not been executed. Async statements have their status
376 // tracked and recorded in StatementData.
377 MaybeRecordQueryStatus(srv, true);
379 mDBStatement = nullptr;
381 if (mAsyncStatement) {
382 // If the destructor called us, there are no pending async statements (they
383 // hold a reference to us) and we can/must just kill the statement directly.
384 if (aDestructing)
385 destructorAsyncFinalize();
386 else
387 asyncFinalize();
390 // Release the holders, so they can release the reference to us.
391 mStatementParamsHolder = nullptr;
392 mStatementRowHolder = nullptr;
394 return convertResultCode(srv);
397 NS_IMETHODIMP
398 Statement::GetParameterCount(uint32_t* _parameterCount) {
399 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
401 *_parameterCount = mParamCount;
402 return NS_OK;
405 NS_IMETHODIMP
406 Statement::GetParameterName(uint32_t aParamIndex, nsACString& _name) {
407 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
408 ENSURE_INDEX_VALUE(aParamIndex, mParamCount);
410 const char* name =
411 ::sqlite3_bind_parameter_name(mDBStatement, aParamIndex + 1);
412 if (name == nullptr) {
413 // this thing had no name, so fake one
414 nsAutoCString fakeName(":");
415 fakeName.AppendInt(aParamIndex);
416 _name.Assign(fakeName);
417 } else {
418 _name.Assign(nsDependentCString(name));
421 return NS_OK;
424 NS_IMETHODIMP
425 Statement::GetParameterIndex(const nsACString& aName, uint32_t* _index) {
426 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
428 // We do not accept any forms of names other than ":name", but we need to add
429 // the colon for SQLite.
430 nsAutoCString name(":");
431 name.Append(aName);
432 int ind = ::sqlite3_bind_parameter_index(mDBStatement, name.get());
433 if (ind == 0) // Named parameter not found.
434 return NS_ERROR_INVALID_ARG;
436 *_index = ind - 1; // SQLite indexes are 1-based, we are 0-based.
438 return NS_OK;
441 NS_IMETHODIMP
442 Statement::GetColumnCount(uint32_t* _columnCount) {
443 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
445 *_columnCount = mResultColumnCount;
446 return NS_OK;
449 NS_IMETHODIMP
450 Statement::GetColumnName(uint32_t aColumnIndex, nsACString& _name) {
451 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
452 ENSURE_INDEX_VALUE(aColumnIndex, mResultColumnCount);
454 const char* cname = ::sqlite3_column_name(mDBStatement, aColumnIndex);
455 _name.Assign(nsDependentCString(cname));
457 return NS_OK;
460 NS_IMETHODIMP
461 Statement::GetColumnIndex(const nsACString& aName, uint32_t* _index) {
462 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
464 // Surprisingly enough, SQLite doesn't provide an API for this. We have to
465 // determine it ourselves sadly.
466 for (uint32_t i = 0; i < mResultColumnCount; i++) {
467 if (mColumnNames[i].Equals(aName)) {
468 *_index = i;
469 return NS_OK;
473 return NS_ERROR_INVALID_ARG;
476 NS_IMETHODIMP
477 Statement::Reset() {
478 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
480 #ifdef DEBUG
481 MOZ_LOG(gStorageLog, LogLevel::Debug,
482 ("Resetting statement: '%s'", ::sqlite3_sql(mDBStatement)));
484 checkAndLogStatementPerformance(mDBStatement);
485 #endif
487 mParamsArray = nullptr;
488 (void)sqlite3_reset(mDBStatement);
489 (void)sqlite3_clear_bindings(mDBStatement);
491 mExecuting = false;
493 // This will be a no-op if the status has already been recorded or if this
494 // statement has not been executed. Async statements have their status
495 // tracked and recorded in StatementData.
496 MaybeRecordQueryStatus(SQLITE_OK, true);
497 mHasExecuted = false;
499 return NS_OK;
502 NS_IMETHODIMP
503 Statement::BindParameters(mozIStorageBindingParamsArray* aParameters) {
504 NS_ENSURE_ARG_POINTER(aParameters);
506 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
508 BindingParamsArray* array = static_cast<BindingParamsArray*>(aParameters);
509 if (array->getOwner() != this) return NS_ERROR_UNEXPECTED;
511 if (array->length() == 0) return NS_ERROR_UNEXPECTED;
513 mParamsArray = array;
514 mParamsArray->lock();
516 return NS_OK;
519 NS_IMETHODIMP
520 Statement::Execute() {
521 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
523 bool ret;
524 nsresult rv = ExecuteStep(&ret);
525 nsresult rv2 = Reset();
527 return NS_FAILED(rv) ? rv : rv2;
530 NS_IMETHODIMP
531 Statement::ExecuteStep(bool* _moreResults) {
532 AUTO_PROFILER_LABEL("Statement::ExecuteStep", OTHER);
534 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
536 // Bind any parameters first before executing.
537 if (mParamsArray) {
538 // If we have more than one row of parameters to bind, they shouldn't be
539 // calling this method (and instead use executeAsync).
540 if (mParamsArray->length() != 1) return NS_ERROR_UNEXPECTED;
542 BindingParamsArray::iterator row = mParamsArray->begin();
543 nsCOMPtr<IStorageBindingParamsInternal> bindingInternal =
544 do_QueryInterface(*row);
545 nsCOMPtr<mozIStorageError> error = bindingInternal->bind(mDBStatement);
546 if (error) {
547 int32_t srv;
548 (void)error->GetResult(&srv);
549 return convertResultCode(srv);
552 // We have bound, so now we can clear our array.
553 mParamsArray = nullptr;
555 int srv = mDBConnection->stepStatement(mNativeConnection, mDBStatement);
556 mHasExecuted = true;
557 MaybeRecordQueryStatus(srv);
559 if (srv != SQLITE_ROW && srv != SQLITE_DONE &&
560 MOZ_LOG_TEST(gStorageLog, LogLevel::Debug)) {
561 nsAutoCString errStr;
562 (void)mDBConnection->GetLastErrorString(errStr);
563 MOZ_LOG(gStorageLog, LogLevel::Debug,
564 ("Statement::ExecuteStep error: %s", errStr.get()));
567 // SQLITE_ROW and SQLITE_DONE are non-errors
568 if (srv == SQLITE_ROW) {
569 // we got a row back
570 mExecuting = true;
571 *_moreResults = true;
572 return NS_OK;
573 } else if (srv == SQLITE_DONE) {
574 // statement is done (no row returned)
575 mExecuting = false;
576 *_moreResults = false;
577 return NS_OK;
578 } else if (srv == SQLITE_BUSY || srv == SQLITE_MISUSE) {
579 mExecuting = false;
580 } else if (mExecuting) {
581 MOZ_LOG(gStorageLog, LogLevel::Error,
582 ("SQLite error after mExecuting was true!"));
583 mExecuting = false;
586 return convertResultCode(srv);
589 NS_IMETHODIMP
590 Statement::GetState(int32_t* _state) {
591 if (!mDBStatement)
592 *_state = MOZ_STORAGE_STATEMENT_INVALID;
593 else if (mExecuting)
594 *_state = MOZ_STORAGE_STATEMENT_EXECUTING;
595 else
596 *_state = MOZ_STORAGE_STATEMENT_READY;
598 return NS_OK;
601 ////////////////////////////////////////////////////////////////////////////////
602 //// mozIStorageValueArray (now part of mozIStorageStatement too)
604 NS_IMETHODIMP
605 Statement::GetNumEntries(uint32_t* _length) {
606 *_length = mResultColumnCount;
607 return NS_OK;
610 NS_IMETHODIMP
611 Statement::GetTypeOfIndex(uint32_t aIndex, int32_t* _type) {
612 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
614 ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
616 if (!mExecuting) return NS_ERROR_UNEXPECTED;
618 int t = ::sqlite3_column_type(mDBStatement, aIndex);
619 switch (t) {
620 case SQLITE_INTEGER:
621 *_type = mozIStorageStatement::VALUE_TYPE_INTEGER;
622 break;
623 case SQLITE_FLOAT:
624 *_type = mozIStorageStatement::VALUE_TYPE_FLOAT;
625 break;
626 case SQLITE_TEXT:
627 *_type = mozIStorageStatement::VALUE_TYPE_TEXT;
628 break;
629 case SQLITE_BLOB:
630 *_type = mozIStorageStatement::VALUE_TYPE_BLOB;
631 break;
632 case SQLITE_NULL:
633 *_type = mozIStorageStatement::VALUE_TYPE_NULL;
634 break;
635 default:
636 return NS_ERROR_FAILURE;
639 return NS_OK;
642 NS_IMETHODIMP
643 Statement::GetInt32(uint32_t aIndex, int32_t* _value) {
644 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
646 ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
648 if (!mExecuting) return NS_ERROR_UNEXPECTED;
650 *_value = ::sqlite3_column_int(mDBStatement, aIndex);
651 return NS_OK;
654 NS_IMETHODIMP
655 Statement::GetInt64(uint32_t aIndex, int64_t* _value) {
656 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
658 ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
660 if (!mExecuting) return NS_ERROR_UNEXPECTED;
662 *_value = ::sqlite3_column_int64(mDBStatement, aIndex);
664 return NS_OK;
667 NS_IMETHODIMP
668 Statement::GetDouble(uint32_t aIndex, double* _value) {
669 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
671 ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
673 if (!mExecuting) return NS_ERROR_UNEXPECTED;
675 *_value = ::sqlite3_column_double(mDBStatement, aIndex);
677 return NS_OK;
680 NS_IMETHODIMP
681 Statement::GetUTF8String(uint32_t aIndex, nsACString& _value) {
682 // Get type of Index will check aIndex for us, so we don't have to.
683 int32_t type;
684 nsresult rv = GetTypeOfIndex(aIndex, &type);
685 NS_ENSURE_SUCCESS(rv, rv);
686 if (type == mozIStorageStatement::VALUE_TYPE_NULL) {
687 // NULL columns should have IsVoid set to distinguish them from the empty
688 // string.
689 _value.SetIsVoid(true);
690 } else {
691 const char* value = reinterpret_cast<const char*>(
692 ::sqlite3_column_text(mDBStatement, aIndex));
693 _value.Assign(value, ::sqlite3_column_bytes(mDBStatement, aIndex));
695 return NS_OK;
698 NS_IMETHODIMP
699 Statement::GetString(uint32_t aIndex, nsAString& _value) {
700 // Get type of Index will check aIndex for us, so we don't have to.
701 int32_t type;
702 nsresult rv = GetTypeOfIndex(aIndex, &type);
703 NS_ENSURE_SUCCESS(rv, rv);
704 if (type == mozIStorageStatement::VALUE_TYPE_NULL) {
705 // NULL columns should have IsVoid set to distinguish them from the empty
706 // string.
707 _value.SetIsVoid(true);
708 } else {
709 const char16_t* value = static_cast<const char16_t*>(
710 ::sqlite3_column_text16(mDBStatement, aIndex));
711 _value.Assign(value, ::sqlite3_column_bytes16(mDBStatement, aIndex) /
712 sizeof(char16_t));
714 return NS_OK;
717 NS_IMETHODIMP
718 Statement::GetVariant(uint32_t aIndex, nsIVariant** _value) {
719 if (!mDBStatement) {
720 return NS_ERROR_NOT_INITIALIZED;
723 ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
725 if (!mExecuting) {
726 return NS_ERROR_UNEXPECTED;
729 nsCOMPtr<nsIVariant> variant;
730 int type = ::sqlite3_column_type(mDBStatement, aIndex);
731 switch (type) {
732 case SQLITE_INTEGER:
733 variant =
734 new IntegerVariant(::sqlite3_column_int64(mDBStatement, aIndex));
735 break;
736 case SQLITE_FLOAT:
737 variant = new FloatVariant(::sqlite3_column_double(mDBStatement, aIndex));
738 break;
739 case SQLITE_TEXT: {
740 const char16_t* value = static_cast<const char16_t*>(
741 ::sqlite3_column_text16(mDBStatement, aIndex));
742 nsDependentString str(
743 value,
744 ::sqlite3_column_bytes16(mDBStatement, aIndex) / sizeof(char16_t));
745 variant = new TextVariant(str);
746 break;
748 case SQLITE_NULL:
749 variant = new NullVariant();
750 break;
751 case SQLITE_BLOB: {
752 int size = ::sqlite3_column_bytes(mDBStatement, aIndex);
753 const void* data = ::sqlite3_column_blob(mDBStatement, aIndex);
754 variant = new BlobVariant(std::pair<const void*, int>(data, size));
755 break;
758 NS_ENSURE_TRUE(variant, NS_ERROR_UNEXPECTED);
760 variant.forget(_value);
761 return NS_OK;
764 NS_IMETHODIMP
765 Statement::GetBlob(uint32_t aIndex, uint32_t* _size, uint8_t** _blob) {
766 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
768 ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
770 if (!mExecuting) return NS_ERROR_UNEXPECTED;
772 int size = ::sqlite3_column_bytes(mDBStatement, aIndex);
773 void* blob = nullptr;
774 if (size) {
775 blob = moz_xmemdup(::sqlite3_column_blob(mDBStatement, aIndex), size);
778 *_blob = static_cast<uint8_t*>(blob);
779 *_size = size;
780 return NS_OK;
783 NS_IMETHODIMP
784 Statement::GetBlobAsString(uint32_t aIndex, nsAString& aValue) {
785 return DoGetBlobAsString(this, aIndex, aValue);
788 NS_IMETHODIMP
789 Statement::GetBlobAsUTF8String(uint32_t aIndex, nsACString& aValue) {
790 return DoGetBlobAsString(this, aIndex, aValue);
793 NS_IMETHODIMP
794 Statement::GetSharedUTF8String(uint32_t aIndex, uint32_t* _byteLength,
795 const char** _value) {
796 *_value = reinterpret_cast<const char*>(
797 ::sqlite3_column_text(mDBStatement, aIndex));
798 if (_byteLength) {
799 *_byteLength = ::sqlite3_column_bytes(mDBStatement, aIndex);
801 return NS_OK;
804 NS_IMETHODIMP
805 Statement::GetSharedString(uint32_t aIndex, uint32_t* _byteLength,
806 const char16_t** _value) {
807 *_value = static_cast<const char16_t*>(
808 ::sqlite3_column_text16(mDBStatement, aIndex));
809 if (_byteLength) {
810 *_byteLength = ::sqlite3_column_bytes16(mDBStatement, aIndex);
812 return NS_OK;
815 NS_IMETHODIMP
816 Statement::GetSharedBlob(uint32_t aIndex, uint32_t* _byteLength,
817 const uint8_t** _blob) {
818 *_blob =
819 static_cast<const uint8_t*>(::sqlite3_column_blob(mDBStatement, aIndex));
820 if (_byteLength) {
821 *_byteLength = ::sqlite3_column_bytes(mDBStatement, aIndex);
823 return NS_OK;
826 NS_IMETHODIMP
827 Statement::GetIsNull(uint32_t aIndex, bool* _isNull) {
828 // Get type of Index will check aIndex for us, so we don't have to.
829 int32_t type;
830 nsresult rv = GetTypeOfIndex(aIndex, &type);
831 NS_ENSURE_SUCCESS(rv, rv);
832 *_isNull = (type == mozIStorageStatement::VALUE_TYPE_NULL);
833 return NS_OK;
836 ////////////////////////////////////////////////////////////////////////////////
837 //// mozIStorageBindingParams
839 BOILERPLATE_BIND_PROXIES(Statement,
840 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;)
842 } // namespace storage
843 } // namespace mozilla