Bug 1831567 [wpt PR 39864] - Reduce calls to pip, a=testonly
[gecko.git] / storage / mozStorageStatement.cpp
blob3aad04d29a308f11a4de61638c335040b1b3dc00
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() {}
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 : StorageBaseStatementInternal(),
103 mDBStatement(nullptr),
104 mParamCount(0),
105 mResultColumnCount(0),
106 mColumnNames(),
107 mExecuting(false),
108 mQueryStatusRecorded(false),
109 mHasExecuted(false) {}
111 nsresult Statement::initialize(Connection* aDBConnection,
112 sqlite3* aNativeConnection,
113 const nsACString& aSQLStatement) {
114 MOZ_ASSERT(aDBConnection, "No database connection given!");
115 MOZ_ASSERT(aDBConnection->isConnectionReadyOnThisThread(),
116 "Database connection should be valid");
117 MOZ_ASSERT(!mDBStatement, "Statement already initialized!");
118 MOZ_ASSERT(aNativeConnection, "No native connection given!");
120 int srv = aDBConnection->prepareStatement(
121 aNativeConnection, PromiseFlatCString(aSQLStatement), &mDBStatement);
122 if (srv != SQLITE_OK) {
123 MOZ_LOG(gStorageLog, LogLevel::Error,
124 ("Sqlite statement prepare error: %d '%s'", srv,
125 ::sqlite3_errmsg(aNativeConnection)));
126 MOZ_LOG(gStorageLog, LogLevel::Error,
127 ("Statement was: '%s'", PromiseFlatCString(aSQLStatement).get()));
129 aDBConnection->RecordQueryStatus(srv);
130 mQueryStatusRecorded = true;
131 return convertResultCode(srv);
134 MOZ_LOG(gStorageLog, LogLevel::Debug,
135 ("Initialized statement '%s' (0x%p)",
136 PromiseFlatCString(aSQLStatement).get(), mDBStatement));
138 mDBConnection = aDBConnection;
139 mNativeConnection = aNativeConnection;
140 mParamCount = ::sqlite3_bind_parameter_count(mDBStatement);
141 mResultColumnCount = ::sqlite3_column_count(mDBStatement);
142 mColumnNames.Clear();
144 nsCString* columnNames = mColumnNames.AppendElements(mResultColumnCount);
145 for (uint32_t i = 0; i < mResultColumnCount; i++) {
146 const char* name = ::sqlite3_column_name(mDBStatement, i);
147 columnNames[i].Assign(name);
150 #ifdef DEBUG
151 // We want to try and test for LIKE and that consumers are using
152 // escapeStringForLIKE instead of just trusting user input. The idea to
153 // check to see if they are binding a parameter after like instead of just
154 // using a string. We only do this in debug builds because it's expensive!
155 auto c = nsCaseInsensitiveCStringComparator;
156 nsACString::const_iterator start, end, e;
157 aSQLStatement.BeginReading(start);
158 aSQLStatement.EndReading(end);
159 e = end;
160 while (::FindInReadable(" LIKE"_ns, start, e, c)) {
161 // We have a LIKE in here, so we perform our tests
162 // FindInReadable moves the iterator, so we have to get a new one for
163 // each test we perform.
164 nsACString::const_iterator s1, s2, s3;
165 s1 = s2 = s3 = start;
167 if (!(::FindInReadable(" LIKE ?"_ns, s1, end, c) ||
168 ::FindInReadable(" LIKE :"_ns, s2, end, c) ||
169 ::FindInReadable(" LIKE @"_ns, s3, end, c))) {
170 // At this point, we didn't find a LIKE statement followed by ?, :,
171 // or @, all of which are valid characters for binding a parameter.
172 // We will warn the consumer that they may not be safely using LIKE.
173 NS_WARNING(
174 "Unsafe use of LIKE detected! Please ensure that you "
175 "are using mozIStorageStatement::escapeStringForLIKE "
176 "and that you are binding that result to the statement "
177 "to prevent SQL injection attacks.");
180 // resetting start and e
181 start = e;
182 e = end;
184 #endif
186 return NS_OK;
189 mozIStorageBindingParams* Statement::getParams() {
190 nsresult rv;
192 // If we do not have an array object yet, make it.
193 if (!mParamsArray) {
194 nsCOMPtr<mozIStorageBindingParamsArray> array;
195 rv = NewBindingParamsArray(getter_AddRefs(array));
196 NS_ENSURE_SUCCESS(rv, nullptr);
198 mParamsArray = static_cast<BindingParamsArray*>(array.get());
201 // If there isn't already any rows added, we'll have to add one to use.
202 if (mParamsArray->length() == 0) {
203 RefPtr<BindingParams> params(new BindingParams(mParamsArray, this));
204 NS_ENSURE_TRUE(params, nullptr);
206 rv = mParamsArray->AddParams(params);
207 NS_ENSURE_SUCCESS(rv, nullptr);
209 // We have to unlock our params because AddParams locks them. This is safe
210 // because no reference to the params object was, or ever will be given out.
211 params->unlock(this);
213 // We also want to lock our array at this point - we don't want anything to
214 // be added to it. Nothing has, or will ever get a reference to it, but we
215 // will get additional safety checks via assertions by doing this.
216 mParamsArray->lock();
219 return *mParamsArray->begin();
222 void Statement::MaybeRecordQueryStatus(int srv, bool isResetting) {
223 // If the statement hasn't been executed synchronously since it was last reset
224 // or created then there is no need to record anything. Asynchronous
225 // statements have their status tracked and recorded by StatementData.
226 if (!mHasExecuted) {
227 return;
230 if (!isResetting && !isErrorCode(srv)) {
231 // Non-errors will be recorded when finalizing.
232 return;
235 // We only record a status if no status has been recorded previously.
236 if (!mQueryStatusRecorded && mDBConnection) {
237 mDBConnection->RecordQueryStatus(srv);
240 // Allow another status to be recorded if we are resetting this statement.
241 mQueryStatusRecorded = !isResetting;
244 Statement::~Statement() { (void)internalFinalize(true); }
246 ////////////////////////////////////////////////////////////////////////////////
247 //// nsISupports
249 NS_IMPL_ADDREF(Statement)
250 NS_IMPL_RELEASE(Statement)
252 NS_INTERFACE_MAP_BEGIN(Statement)
253 NS_INTERFACE_MAP_ENTRY(mozIStorageStatement)
254 NS_INTERFACE_MAP_ENTRY(mozIStorageBaseStatement)
255 NS_INTERFACE_MAP_ENTRY(mozIStorageBindingParams)
256 NS_INTERFACE_MAP_ENTRY(mozIStorageValueArray)
257 NS_INTERFACE_MAP_ENTRY(mozilla::storage::StorageBaseStatementInternal)
258 if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
259 foundInterface = static_cast<nsIClassInfo*>(&sStatementClassInfo);
260 } else
261 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageStatement)
262 NS_INTERFACE_MAP_END
264 ////////////////////////////////////////////////////////////////////////////////
265 //// StorageBaseStatementInternal
267 Connection* Statement::getOwner() { return mDBConnection; }
269 int Statement::getAsyncStatement(sqlite3_stmt** _stmt) {
270 // If we have no statement, we shouldn't be calling this method!
271 NS_ASSERTION(mDBStatement != nullptr, "We have no statement to clone!");
273 // If we do not yet have a cached async statement, clone our statement now.
274 if (!mAsyncStatement) {
275 nsDependentCString sql(::sqlite3_sql(mDBStatement));
276 int rc = mDBConnection->prepareStatement(mNativeConnection, sql,
277 &mAsyncStatement);
278 if (rc != SQLITE_OK) {
279 mDBConnection->RecordQueryStatus(rc);
280 *_stmt = nullptr;
281 return rc;
284 MOZ_LOG(gStorageLog, LogLevel::Debug,
285 ("Cloned statement 0x%p to 0x%p", mDBStatement, mAsyncStatement));
288 *_stmt = mAsyncStatement;
289 return SQLITE_OK;
292 nsresult Statement::getAsynchronousStatementData(StatementData& _data) {
293 if (!mDBStatement) return NS_ERROR_UNEXPECTED;
295 sqlite3_stmt* stmt;
296 int rc = getAsyncStatement(&stmt);
297 if (rc != SQLITE_OK) return convertResultCode(rc);
299 _data = StatementData(stmt, bindingParamsArray(), this);
301 return NS_OK;
304 already_AddRefed<mozIStorageBindingParams> Statement::newBindingParams(
305 mozIStorageBindingParamsArray* aOwner) {
306 nsCOMPtr<mozIStorageBindingParams> params = new BindingParams(aOwner, this);
307 return params.forget();
310 ////////////////////////////////////////////////////////////////////////////////
311 //// mozIStorageStatement
313 // proxy to StorageBaseStatementInternal using its define helper.
314 MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(Statement, (void)0;)
316 NS_IMETHODIMP
317 Statement::Clone(mozIStorageStatement** _statement) {
318 RefPtr<Statement> statement(new Statement());
319 NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
321 nsAutoCString sql(::sqlite3_sql(mDBStatement));
322 nsresult rv = statement->initialize(mDBConnection, mNativeConnection, sql);
323 NS_ENSURE_SUCCESS(rv, rv);
325 statement.forget(_statement);
326 return NS_OK;
329 NS_IMETHODIMP
330 Statement::Finalize() { return internalFinalize(false); }
332 nsresult Statement::internalFinalize(bool aDestructing) {
333 if (!mDBStatement) return NS_OK;
335 int srv = SQLITE_OK;
338 // If the statement ends up being finalized twice, the second finalization
339 // would apply to a dangling pointer and may cause unexpected consequences.
340 // Thus we must be sure that the connection state won't change during this
341 // operation, to avoid racing with finalizations made by the closing
342 // connection. See Connection::internalClose().
343 MutexAutoLock lockedScope(mDBConnection->sharedAsyncExecutionMutex);
344 if (!mDBConnection->isClosed(lockedScope)) {
345 MOZ_LOG(gStorageLog, LogLevel::Debug,
346 ("Finalizing statement '%s' during garbage-collection",
347 ::sqlite3_sql(mDBStatement)));
348 srv = ::sqlite3_finalize(mDBStatement);
350 #ifdef DEBUG
351 else {
352 // The database connection is closed. The sqlite
353 // statement has either been finalized already by the connection
354 // or is about to be finalized by the connection.
356 // Finalizing it here would be useless and segfaultish.
358 // Note that we can't display the statement itself, as the data structure
359 // is not valid anymore. However, the address shown here should help
360 // developers correlate with the more complete debug message triggered
361 // by AsyncClose().
363 SmprintfPointer msg = ::mozilla::Smprintf(
364 "SQL statement (%p) should have been finalized"
365 " before garbage-collection. For more details on this statement, set"
366 " NSPR_LOG_MESSAGES=mozStorage:5 .",
367 mDBStatement);
368 NS_WARNING(msg.get());
370 // Use %s so we aren't exposing random strings to printf interpolation.
371 MOZ_LOG(gStorageLog, LogLevel::Warning, ("%s", msg.get()));
373 #endif // DEBUG
376 // This will be a no-op if the status has already been recorded or if this
377 // statement has not been executed. Async statements have their status
378 // tracked and recorded in StatementData.
379 MaybeRecordQueryStatus(srv, true);
381 mDBStatement = nullptr;
383 if (mAsyncStatement) {
384 // If the destructor called us, there are no pending async statements (they
385 // hold a reference to us) and we can/must just kill the statement directly.
386 if (aDestructing)
387 destructorAsyncFinalize();
388 else
389 asyncFinalize();
392 // Release the holders, so they can release the reference to us.
393 mStatementParamsHolder = nullptr;
394 mStatementRowHolder = nullptr;
396 return convertResultCode(srv);
399 NS_IMETHODIMP
400 Statement::GetParameterCount(uint32_t* _parameterCount) {
401 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
403 *_parameterCount = mParamCount;
404 return NS_OK;
407 NS_IMETHODIMP
408 Statement::GetParameterName(uint32_t aParamIndex, nsACString& _name) {
409 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
410 ENSURE_INDEX_VALUE(aParamIndex, mParamCount);
412 const char* name =
413 ::sqlite3_bind_parameter_name(mDBStatement, aParamIndex + 1);
414 if (name == nullptr) {
415 // this thing had no name, so fake one
416 nsAutoCString fakeName(":");
417 fakeName.AppendInt(aParamIndex);
418 _name.Assign(fakeName);
419 } else {
420 _name.Assign(nsDependentCString(name));
423 return NS_OK;
426 NS_IMETHODIMP
427 Statement::GetParameterIndex(const nsACString& aName, uint32_t* _index) {
428 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
430 // We do not accept any forms of names other than ":name", but we need to add
431 // the colon for SQLite.
432 nsAutoCString name(":");
433 name.Append(aName);
434 int ind = ::sqlite3_bind_parameter_index(mDBStatement, name.get());
435 if (ind == 0) // Named parameter not found.
436 return NS_ERROR_INVALID_ARG;
438 *_index = ind - 1; // SQLite indexes are 1-based, we are 0-based.
440 return NS_OK;
443 NS_IMETHODIMP
444 Statement::GetColumnCount(uint32_t* _columnCount) {
445 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
447 *_columnCount = mResultColumnCount;
448 return NS_OK;
451 NS_IMETHODIMP
452 Statement::GetColumnName(uint32_t aColumnIndex, nsACString& _name) {
453 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
454 ENSURE_INDEX_VALUE(aColumnIndex, mResultColumnCount);
456 const char* cname = ::sqlite3_column_name(mDBStatement, aColumnIndex);
457 _name.Assign(nsDependentCString(cname));
459 return NS_OK;
462 NS_IMETHODIMP
463 Statement::GetColumnIndex(const nsACString& aName, uint32_t* _index) {
464 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
466 // Surprisingly enough, SQLite doesn't provide an API for this. We have to
467 // determine it ourselves sadly.
468 for (uint32_t i = 0; i < mResultColumnCount; i++) {
469 if (mColumnNames[i].Equals(aName)) {
470 *_index = i;
471 return NS_OK;
475 return NS_ERROR_INVALID_ARG;
478 NS_IMETHODIMP
479 Statement::Reset() {
480 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
482 #ifdef DEBUG
483 MOZ_LOG(gStorageLog, LogLevel::Debug,
484 ("Resetting statement: '%s'", ::sqlite3_sql(mDBStatement)));
486 checkAndLogStatementPerformance(mDBStatement);
487 #endif
489 mParamsArray = nullptr;
490 (void)sqlite3_reset(mDBStatement);
491 (void)sqlite3_clear_bindings(mDBStatement);
493 mExecuting = false;
495 // This will be a no-op if the status has already been recorded or if this
496 // statement has not been executed. Async statements have their status
497 // tracked and recorded in StatementData.
498 MaybeRecordQueryStatus(SQLITE_OK, true);
499 mHasExecuted = false;
501 return NS_OK;
504 NS_IMETHODIMP
505 Statement::BindParameters(mozIStorageBindingParamsArray* aParameters) {
506 NS_ENSURE_ARG_POINTER(aParameters);
508 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
510 BindingParamsArray* array = static_cast<BindingParamsArray*>(aParameters);
511 if (array->getOwner() != this) return NS_ERROR_UNEXPECTED;
513 if (array->length() == 0) return NS_ERROR_UNEXPECTED;
515 mParamsArray = array;
516 mParamsArray->lock();
518 return NS_OK;
521 NS_IMETHODIMP
522 Statement::Execute() {
523 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
525 bool ret;
526 nsresult rv = ExecuteStep(&ret);
527 nsresult rv2 = Reset();
529 return NS_FAILED(rv) ? rv : rv2;
532 NS_IMETHODIMP
533 Statement::ExecuteStep(bool* _moreResults) {
534 AUTO_PROFILER_LABEL("Statement::ExecuteStep", OTHER);
536 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
538 // Bind any parameters first before executing.
539 if (mParamsArray) {
540 // If we have more than one row of parameters to bind, they shouldn't be
541 // calling this method (and instead use executeAsync).
542 if (mParamsArray->length() != 1) return NS_ERROR_UNEXPECTED;
544 BindingParamsArray::iterator row = mParamsArray->begin();
545 nsCOMPtr<IStorageBindingParamsInternal> bindingInternal =
546 do_QueryInterface(*row);
547 nsCOMPtr<mozIStorageError> error = bindingInternal->bind(mDBStatement);
548 if (error) {
549 int32_t srv;
550 (void)error->GetResult(&srv);
551 return convertResultCode(srv);
554 // We have bound, so now we can clear our array.
555 mParamsArray = nullptr;
557 int srv = mDBConnection->stepStatement(mNativeConnection, mDBStatement);
558 mHasExecuted = true;
559 MaybeRecordQueryStatus(srv);
561 if (srv != SQLITE_ROW && srv != SQLITE_DONE &&
562 MOZ_LOG_TEST(gStorageLog, LogLevel::Debug)) {
563 nsAutoCString errStr;
564 (void)mDBConnection->GetLastErrorString(errStr);
565 MOZ_LOG(gStorageLog, LogLevel::Debug,
566 ("Statement::ExecuteStep error: %s", errStr.get()));
569 // SQLITE_ROW and SQLITE_DONE are non-errors
570 if (srv == SQLITE_ROW) {
571 // we got a row back
572 mExecuting = true;
573 *_moreResults = true;
574 return NS_OK;
575 } else if (srv == SQLITE_DONE) {
576 // statement is done (no row returned)
577 mExecuting = false;
578 *_moreResults = false;
579 return NS_OK;
580 } else if (srv == SQLITE_BUSY || srv == SQLITE_MISUSE) {
581 mExecuting = false;
582 } else if (mExecuting) {
583 MOZ_LOG(gStorageLog, LogLevel::Error,
584 ("SQLite error after mExecuting was true!"));
585 mExecuting = false;
588 return convertResultCode(srv);
591 NS_IMETHODIMP
592 Statement::GetState(int32_t* _state) {
593 if (!mDBStatement)
594 *_state = MOZ_STORAGE_STATEMENT_INVALID;
595 else if (mExecuting)
596 *_state = MOZ_STORAGE_STATEMENT_EXECUTING;
597 else
598 *_state = MOZ_STORAGE_STATEMENT_READY;
600 return NS_OK;
603 ////////////////////////////////////////////////////////////////////////////////
604 //// mozIStorageValueArray (now part of mozIStorageStatement too)
606 NS_IMETHODIMP
607 Statement::GetNumEntries(uint32_t* _length) {
608 *_length = mResultColumnCount;
609 return NS_OK;
612 NS_IMETHODIMP
613 Statement::GetTypeOfIndex(uint32_t aIndex, int32_t* _type) {
614 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
616 ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
618 if (!mExecuting) return NS_ERROR_UNEXPECTED;
620 int t = ::sqlite3_column_type(mDBStatement, aIndex);
621 switch (t) {
622 case SQLITE_INTEGER:
623 *_type = mozIStorageStatement::VALUE_TYPE_INTEGER;
624 break;
625 case SQLITE_FLOAT:
626 *_type = mozIStorageStatement::VALUE_TYPE_FLOAT;
627 break;
628 case SQLITE_TEXT:
629 *_type = mozIStorageStatement::VALUE_TYPE_TEXT;
630 break;
631 case SQLITE_BLOB:
632 *_type = mozIStorageStatement::VALUE_TYPE_BLOB;
633 break;
634 case SQLITE_NULL:
635 *_type = mozIStorageStatement::VALUE_TYPE_NULL;
636 break;
637 default:
638 return NS_ERROR_FAILURE;
641 return NS_OK;
644 NS_IMETHODIMP
645 Statement::GetInt32(uint32_t aIndex, int32_t* _value) {
646 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
648 ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
650 if (!mExecuting) return NS_ERROR_UNEXPECTED;
652 *_value = ::sqlite3_column_int(mDBStatement, aIndex);
653 return NS_OK;
656 NS_IMETHODIMP
657 Statement::GetInt64(uint32_t aIndex, int64_t* _value) {
658 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
660 ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
662 if (!mExecuting) return NS_ERROR_UNEXPECTED;
664 *_value = ::sqlite3_column_int64(mDBStatement, aIndex);
666 return NS_OK;
669 NS_IMETHODIMP
670 Statement::GetDouble(uint32_t aIndex, double* _value) {
671 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
673 ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
675 if (!mExecuting) return NS_ERROR_UNEXPECTED;
677 *_value = ::sqlite3_column_double(mDBStatement, aIndex);
679 return NS_OK;
682 NS_IMETHODIMP
683 Statement::GetUTF8String(uint32_t aIndex, nsACString& _value) {
684 // Get type of Index will check aIndex for us, so we don't have to.
685 int32_t type;
686 nsresult rv = GetTypeOfIndex(aIndex, &type);
687 NS_ENSURE_SUCCESS(rv, rv);
688 if (type == mozIStorageStatement::VALUE_TYPE_NULL) {
689 // NULL columns should have IsVoid set to distinguish them from the empty
690 // string.
691 _value.SetIsVoid(true);
692 } else {
693 const char* value = reinterpret_cast<const char*>(
694 ::sqlite3_column_text(mDBStatement, aIndex));
695 _value.Assign(value, ::sqlite3_column_bytes(mDBStatement, aIndex));
697 return NS_OK;
700 NS_IMETHODIMP
701 Statement::GetString(uint32_t aIndex, nsAString& _value) {
702 // Get type of Index will check aIndex for us, so we don't have to.
703 int32_t type;
704 nsresult rv = GetTypeOfIndex(aIndex, &type);
705 NS_ENSURE_SUCCESS(rv, rv);
706 if (type == mozIStorageStatement::VALUE_TYPE_NULL) {
707 // NULL columns should have IsVoid set to distinguish them from the empty
708 // string.
709 _value.SetIsVoid(true);
710 } else {
711 const char16_t* value = static_cast<const char16_t*>(
712 ::sqlite3_column_text16(mDBStatement, aIndex));
713 _value.Assign(value, ::sqlite3_column_bytes16(mDBStatement, aIndex) /
714 sizeof(char16_t));
716 return NS_OK;
719 NS_IMETHODIMP
720 Statement::GetVariant(uint32_t aIndex, nsIVariant** _value) {
721 if (!mDBStatement) {
722 return NS_ERROR_NOT_INITIALIZED;
725 ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
727 if (!mExecuting) {
728 return NS_ERROR_UNEXPECTED;
731 nsCOMPtr<nsIVariant> variant;
732 int type = ::sqlite3_column_type(mDBStatement, aIndex);
733 switch (type) {
734 case SQLITE_INTEGER:
735 variant =
736 new IntegerVariant(::sqlite3_column_int64(mDBStatement, aIndex));
737 break;
738 case SQLITE_FLOAT:
739 variant = new FloatVariant(::sqlite3_column_double(mDBStatement, aIndex));
740 break;
741 case SQLITE_TEXT: {
742 const char16_t* value = static_cast<const char16_t*>(
743 ::sqlite3_column_text16(mDBStatement, aIndex));
744 nsDependentString str(
745 value,
746 ::sqlite3_column_bytes16(mDBStatement, aIndex) / sizeof(char16_t));
747 variant = new TextVariant(str);
748 break;
750 case SQLITE_NULL:
751 variant = new NullVariant();
752 break;
753 case SQLITE_BLOB: {
754 int size = ::sqlite3_column_bytes(mDBStatement, aIndex);
755 const void* data = ::sqlite3_column_blob(mDBStatement, aIndex);
756 variant = new BlobVariant(std::pair<const void*, int>(data, size));
757 break;
760 NS_ENSURE_TRUE(variant, NS_ERROR_UNEXPECTED);
762 variant.forget(_value);
763 return NS_OK;
766 NS_IMETHODIMP
767 Statement::GetBlob(uint32_t aIndex, uint32_t* _size, uint8_t** _blob) {
768 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
770 ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
772 if (!mExecuting) return NS_ERROR_UNEXPECTED;
774 int size = ::sqlite3_column_bytes(mDBStatement, aIndex);
775 void* blob = nullptr;
776 if (size) {
777 blob = moz_xmemdup(::sqlite3_column_blob(mDBStatement, aIndex), size);
780 *_blob = static_cast<uint8_t*>(blob);
781 *_size = size;
782 return NS_OK;
785 NS_IMETHODIMP
786 Statement::GetBlobAsString(uint32_t aIndex, nsAString& aValue) {
787 return DoGetBlobAsString(this, aIndex, aValue);
790 NS_IMETHODIMP
791 Statement::GetBlobAsUTF8String(uint32_t aIndex, nsACString& aValue) {
792 return DoGetBlobAsString(this, aIndex, aValue);
795 NS_IMETHODIMP
796 Statement::GetSharedUTF8String(uint32_t aIndex, uint32_t* _byteLength,
797 const char** _value) {
798 *_value = reinterpret_cast<const char*>(
799 ::sqlite3_column_text(mDBStatement, aIndex));
800 if (_byteLength) {
801 *_byteLength = ::sqlite3_column_bytes(mDBStatement, aIndex);
803 return NS_OK;
806 NS_IMETHODIMP
807 Statement::GetSharedString(uint32_t aIndex, uint32_t* _byteLength,
808 const char16_t** _value) {
809 *_value = static_cast<const char16_t*>(
810 ::sqlite3_column_text16(mDBStatement, aIndex));
811 if (_byteLength) {
812 *_byteLength = ::sqlite3_column_bytes16(mDBStatement, aIndex);
814 return NS_OK;
817 NS_IMETHODIMP
818 Statement::GetSharedBlob(uint32_t aIndex, uint32_t* _byteLength,
819 const uint8_t** _blob) {
820 *_blob =
821 static_cast<const uint8_t*>(::sqlite3_column_blob(mDBStatement, aIndex));
822 if (_byteLength) {
823 *_byteLength = ::sqlite3_column_bytes(mDBStatement, aIndex);
825 return NS_OK;
828 NS_IMETHODIMP
829 Statement::GetIsNull(uint32_t aIndex, bool* _isNull) {
830 // Get type of Index will check aIndex for us, so we don't have to.
831 int32_t type;
832 nsresult rv = GetTypeOfIndex(aIndex, &type);
833 NS_ENSURE_SUCCESS(rv, rv);
834 *_isNull = (type == mozIStorageStatement::VALUE_TYPE_NULL);
835 return NS_OK;
838 ////////////////////////////////////////////////////////////////////////////////
839 //// mozIStorageBindingParams
841 BOILERPLATE_BIND_PROXIES(Statement,
842 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;)
844 } // namespace storage
845 } // namespace mozilla