Bug 1744266 [wpt PR 31876] - WebSockets is its own standard now, a=testonly
[gecko.git] / storage / mozStorageStatement.cpp
blob21a43cf5da33660162ae6e4fb6965f42e918c44d
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 "nsMemory.h"
12 #include "nsThreadUtils.h"
13 #include "nsIClassInfoImpl.h"
14 #include "Variant.h"
16 #include "mozIStorageError.h"
18 #include "mozStorageBindingParams.h"
19 #include "mozStorageConnection.h"
20 #include "mozStorageStatementJSHelper.h"
21 #include "mozStoragePrivateHelpers.h"
22 #include "mozStorageStatementParams.h"
23 #include "mozStorageStatementRow.h"
24 #include "mozStorageStatement.h"
26 #include "mozilla/Logging.h"
27 #include "mozilla/Printf.h"
28 #include "mozilla/ProfilerLabels.h"
30 extern mozilla::LazyLogModule gStorageLog;
32 namespace mozilla {
33 namespace storage {
35 ////////////////////////////////////////////////////////////////////////////////
36 //// nsIClassInfo
38 NS_IMPL_CI_INTERFACE_GETTER(Statement, mozIStorageStatement,
39 mozIStorageBaseStatement, mozIStorageBindingParams,
40 mozIStorageValueArray,
41 mozilla::storage::StorageBaseStatementInternal)
43 class StatementClassInfo : public nsIClassInfo {
44 public:
45 constexpr StatementClassInfo() {}
47 NS_DECL_ISUPPORTS_INHERITED
49 NS_IMETHOD
50 GetInterfaces(nsTArray<nsIID>& _array) override {
51 return NS_CI_INTERFACE_GETTER_NAME(Statement)(_array);
54 NS_IMETHOD
55 GetScriptableHelper(nsIXPCScriptable** _helper) override {
56 static StatementJSHelper sJSHelper;
57 *_helper = &sJSHelper;
58 return NS_OK;
61 NS_IMETHOD
62 GetContractID(nsACString& aContractID) override {
63 aContractID.SetIsVoid(true);
64 return NS_OK;
67 NS_IMETHOD
68 GetClassDescription(nsACString& aDesc) override {
69 aDesc.SetIsVoid(true);
70 return NS_OK;
73 NS_IMETHOD
74 GetClassID(nsCID** _id) override {
75 *_id = nullptr;
76 return NS_OK;
79 NS_IMETHOD
80 GetFlags(uint32_t* _flags) override {
81 *_flags = 0;
82 return NS_OK;
85 NS_IMETHOD
86 GetClassIDNoAlloc(nsCID* _cid) override { return NS_ERROR_NOT_AVAILABLE; }
89 NS_IMETHODIMP_(MozExternalRefCountType) StatementClassInfo::AddRef() {
90 return 2;
92 NS_IMETHODIMP_(MozExternalRefCountType) StatementClassInfo::Release() {
93 return 1;
95 NS_IMPL_QUERY_INTERFACE(StatementClassInfo, nsIClassInfo)
97 static StatementClassInfo sStatementClassInfo;
99 ////////////////////////////////////////////////////////////////////////////////
100 //// Statement
102 Statement::Statement()
103 : StorageBaseStatementInternal(),
104 mDBStatement(nullptr),
105 mParamCount(0),
106 mResultColumnCount(0),
107 mColumnNames(),
108 mExecuting(false),
109 mQueryStatusRecorded(false),
110 mHasExecuted(false) {}
112 nsresult Statement::initialize(Connection* aDBConnection,
113 sqlite3* aNativeConnection,
114 const nsACString& aSQLStatement) {
115 MOZ_ASSERT(aDBConnection, "No database connection given!");
116 MOZ_ASSERT(aDBConnection->isConnectionReadyOnThisThread(),
117 "Database connection should be valid");
118 MOZ_ASSERT(!mDBStatement, "Statement already initialized!");
119 MOZ_ASSERT(aNativeConnection, "No native connection given!");
121 int srv = aDBConnection->prepareStatement(
122 aNativeConnection, PromiseFlatCString(aSQLStatement), &mDBStatement);
123 if (srv != SQLITE_OK) {
124 MOZ_LOG(gStorageLog, LogLevel::Error,
125 ("Sqlite statement prepare error: %d '%s'", srv,
126 ::sqlite3_errmsg(aNativeConnection)));
127 MOZ_LOG(gStorageLog, LogLevel::Error,
128 ("Statement was: '%s'", PromiseFlatCString(aSQLStatement).get()));
130 aDBConnection->RecordQueryStatus(srv);
131 mQueryStatusRecorded = true;
132 return NS_ERROR_FAILURE;
135 MOZ_LOG(gStorageLog, LogLevel::Debug,
136 ("Initialized statement '%s' (0x%p)",
137 PromiseFlatCString(aSQLStatement).get(), mDBStatement));
139 mDBConnection = aDBConnection;
140 mNativeConnection = aNativeConnection;
141 mParamCount = ::sqlite3_bind_parameter_count(mDBStatement);
142 mResultColumnCount = ::sqlite3_column_count(mDBStatement);
143 mColumnNames.Clear();
145 nsCString* columnNames = mColumnNames.AppendElements(mResultColumnCount);
146 for (uint32_t i = 0; i < mResultColumnCount; i++) {
147 const char* name = ::sqlite3_column_name(mDBStatement, i);
148 columnNames[i].Assign(name);
151 #ifdef DEBUG
152 // We want to try and test for LIKE and that consumers are using
153 // escapeStringForLIKE instead of just trusting user input. The idea to
154 // check to see if they are binding a parameter after like instead of just
155 // using a string. We only do this in debug builds because it's expensive!
156 auto c = nsCaseInsensitiveCStringComparator;
157 nsACString::const_iterator start, end, e;
158 aSQLStatement.BeginReading(start);
159 aSQLStatement.EndReading(end);
160 e = end;
161 while (::FindInReadable(" LIKE"_ns, start, e, c)) {
162 // We have a LIKE in here, so we perform our tests
163 // FindInReadable moves the iterator, so we have to get a new one for
164 // each test we perform.
165 nsACString::const_iterator s1, s2, s3;
166 s1 = s2 = s3 = start;
168 if (!(::FindInReadable(" LIKE ?"_ns, s1, end, c) ||
169 ::FindInReadable(" LIKE :"_ns, s2, end, c) ||
170 ::FindInReadable(" LIKE @"_ns, s3, end, c))) {
171 // At this point, we didn't find a LIKE statement followed by ?, :,
172 // or @, all of which are valid characters for binding a parameter.
173 // We will warn the consumer that they may not be safely using LIKE.
174 NS_WARNING(
175 "Unsafe use of LIKE detected! Please ensure that you "
176 "are using mozIStorageStatement::escapeStringForLIKE "
177 "and that you are binding that result to the statement "
178 "to prevent SQL injection attacks.");
181 // resetting start and e
182 start = e;
183 e = end;
185 #endif
187 return NS_OK;
190 mozIStorageBindingParams* Statement::getParams() {
191 nsresult rv;
193 // If we do not have an array object yet, make it.
194 if (!mParamsArray) {
195 nsCOMPtr<mozIStorageBindingParamsArray> array;
196 rv = NewBindingParamsArray(getter_AddRefs(array));
197 NS_ENSURE_SUCCESS(rv, nullptr);
199 mParamsArray = static_cast<BindingParamsArray*>(array.get());
202 // If there isn't already any rows added, we'll have to add one to use.
203 if (mParamsArray->length() == 0) {
204 RefPtr<BindingParams> params(new BindingParams(mParamsArray, this));
205 NS_ENSURE_TRUE(params, nullptr);
207 rv = mParamsArray->AddParams(params);
208 NS_ENSURE_SUCCESS(rv, nullptr);
210 // We have to unlock our params because AddParams locks them. This is safe
211 // because no reference to the params object was, or ever will be given out.
212 params->unlock(this);
214 // We also want to lock our array at this point - we don't want anything to
215 // be added to it. Nothing has, or will ever get a reference to it, but we
216 // will get additional safety checks via assertions by doing this.
217 mParamsArray->lock();
220 return *mParamsArray->begin();
223 void Statement::MaybeRecordQueryStatus(int srv, bool isResetting) {
224 // If the statement hasn't been executed synchronously since it was last reset
225 // or created then there is no need to record anything. Asynchronous
226 // statements have their status tracked and recorded by StatementData.
227 if (!mHasExecuted) {
228 return;
231 if (!isResetting && !isErrorCode(srv)) {
232 // Non-errors will be recorded when finalizing.
233 return;
236 // We only record a status if no status has been recorded previously.
237 if (!mQueryStatusRecorded && mDBConnection) {
238 mDBConnection->RecordQueryStatus(srv);
241 // Allow another status to be recorded if we are resetting this statement.
242 mQueryStatusRecorded = !isResetting;
245 Statement::~Statement() { (void)internalFinalize(true); }
247 ////////////////////////////////////////////////////////////////////////////////
248 //// nsISupports
250 NS_IMPL_ADDREF(Statement)
251 NS_IMPL_RELEASE(Statement)
253 NS_INTERFACE_MAP_BEGIN(Statement)
254 NS_INTERFACE_MAP_ENTRY(mozIStorageStatement)
255 NS_INTERFACE_MAP_ENTRY(mozIStorageBaseStatement)
256 NS_INTERFACE_MAP_ENTRY(mozIStorageBindingParams)
257 NS_INTERFACE_MAP_ENTRY(mozIStorageValueArray)
258 NS_INTERFACE_MAP_ENTRY(mozilla::storage::StorageBaseStatementInternal)
259 if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
260 foundInterface = static_cast<nsIClassInfo*>(&sStatementClassInfo);
261 } else
262 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageStatement)
263 NS_INTERFACE_MAP_END
265 ////////////////////////////////////////////////////////////////////////////////
266 //// StorageBaseStatementInternal
268 Connection* Statement::getOwner() { return mDBConnection; }
270 int Statement::getAsyncStatement(sqlite3_stmt** _stmt) {
271 // If we have no statement, we shouldn't be calling this method!
272 NS_ASSERTION(mDBStatement != nullptr, "We have no statement to clone!");
274 // If we do not yet have a cached async statement, clone our statement now.
275 if (!mAsyncStatement) {
276 nsDependentCString sql(::sqlite3_sql(mDBStatement));
277 int rc = mDBConnection->prepareStatement(mNativeConnection, sql,
278 &mAsyncStatement);
279 if (rc != SQLITE_OK) {
280 mDBConnection->RecordQueryStatus(rc);
281 *_stmt = nullptr;
282 return rc;
285 MOZ_LOG(gStorageLog, LogLevel::Debug,
286 ("Cloned statement 0x%p to 0x%p", mDBStatement, mAsyncStatement));
289 *_stmt = mAsyncStatement;
290 return SQLITE_OK;
293 nsresult Statement::getAsynchronousStatementData(StatementData& _data) {
294 if (!mDBStatement) return NS_ERROR_UNEXPECTED;
296 sqlite3_stmt* stmt;
297 int rc = getAsyncStatement(&stmt);
298 if (rc != SQLITE_OK) return convertResultCode(rc);
300 _data = StatementData(stmt, bindingParamsArray(), this);
302 return NS_OK;
305 already_AddRefed<mozIStorageBindingParams> Statement::newBindingParams(
306 mozIStorageBindingParamsArray* aOwner) {
307 nsCOMPtr<mozIStorageBindingParams> params = new BindingParams(aOwner, this);
308 return params.forget();
311 ////////////////////////////////////////////////////////////////////////////////
312 //// mozIStorageStatement
314 // proxy to StorageBaseStatementInternal using its define helper.
315 MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(Statement, (void)0;)
317 NS_IMETHODIMP
318 Statement::Clone(mozIStorageStatement** _statement) {
319 RefPtr<Statement> statement(new Statement());
320 NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
322 nsAutoCString sql(::sqlite3_sql(mDBStatement));
323 nsresult rv = statement->initialize(mDBConnection, mNativeConnection, sql);
324 NS_ENSURE_SUCCESS(rv, rv);
326 statement.forget(_statement);
327 return NS_OK;
330 NS_IMETHODIMP
331 Statement::Finalize() { return internalFinalize(false); }
333 nsresult Statement::internalFinalize(bool aDestructing) {
334 if (!mDBStatement) return NS_OK;
336 int srv = SQLITE_OK;
339 // If the statement ends up being finalized twice, the second finalization
340 // would apply to a dangling pointer and may cause unexpected consequences.
341 // Thus we must be sure that the connection state won't change during this
342 // operation, to avoid racing with finalizations made by the closing
343 // connection. See Connection::internalClose().
344 MutexAutoLock lockedScope(mDBConnection->sharedAsyncExecutionMutex);
345 if (!mDBConnection->isClosed(lockedScope)) {
346 MOZ_LOG(gStorageLog, LogLevel::Debug,
347 ("Finalizing statement '%s' during garbage-collection",
348 ::sqlite3_sql(mDBStatement)));
349 srv = ::sqlite3_finalize(mDBStatement);
351 #ifdef DEBUG
352 else {
353 // The database connection is closed. The sqlite
354 // statement has either been finalized already by the connection
355 // or is about to be finalized by the connection.
357 // Finalizing it here would be useless and segfaultish.
359 // Note that we can't display the statement itself, as the data structure
360 // is not valid anymore. However, the address shown here should help
361 // developers correlate with the more complete debug message triggered
362 // by AsyncClose().
364 SmprintfPointer msg = ::mozilla::Smprintf(
365 "SQL statement (%p) should have been finalized"
366 " before garbage-collection. For more details on this statement, set"
367 " NSPR_LOG_MESSAGES=mozStorage:5 .",
368 mDBStatement);
369 NS_WARNING(msg.get());
371 // Use %s so we aren't exposing random strings to printf interpolation.
372 MOZ_LOG(gStorageLog, LogLevel::Warning, ("%s", msg.get()));
374 #endif // DEBUG
377 // This will be a no-op if the status has already been recorded or if this
378 // statement has not been executed. Async statements have their status
379 // tracked and recorded in StatementData.
380 MaybeRecordQueryStatus(srv, true);
382 mDBStatement = nullptr;
384 if (mAsyncStatement) {
385 // If the destructor called us, there are no pending async statements (they
386 // hold a reference to us) and we can/must just kill the statement directly.
387 if (aDestructing)
388 destructorAsyncFinalize();
389 else
390 asyncFinalize();
393 // Release the holders, so they can release the reference to us.
394 mStatementParamsHolder = nullptr;
395 mStatementRowHolder = nullptr;
397 return convertResultCode(srv);
400 NS_IMETHODIMP
401 Statement::GetParameterCount(uint32_t* _parameterCount) {
402 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
404 *_parameterCount = mParamCount;
405 return NS_OK;
408 NS_IMETHODIMP
409 Statement::GetParameterName(uint32_t aParamIndex, nsACString& _name) {
410 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
411 ENSURE_INDEX_VALUE(aParamIndex, mParamCount);
413 const char* name =
414 ::sqlite3_bind_parameter_name(mDBStatement, aParamIndex + 1);
415 if (name == nullptr) {
416 // this thing had no name, so fake one
417 nsAutoCString fakeName(":");
418 fakeName.AppendInt(aParamIndex);
419 _name.Assign(fakeName);
420 } else {
421 _name.Assign(nsDependentCString(name));
424 return NS_OK;
427 NS_IMETHODIMP
428 Statement::GetParameterIndex(const nsACString& aName, uint32_t* _index) {
429 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
431 // We do not accept any forms of names other than ":name", but we need to add
432 // the colon for SQLite.
433 nsAutoCString name(":");
434 name.Append(aName);
435 int ind = ::sqlite3_bind_parameter_index(mDBStatement, name.get());
436 if (ind == 0) // Named parameter not found.
437 return NS_ERROR_INVALID_ARG;
439 *_index = ind - 1; // SQLite indexes are 1-based, we are 0-based.
441 return NS_OK;
444 NS_IMETHODIMP
445 Statement::GetColumnCount(uint32_t* _columnCount) {
446 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
448 *_columnCount = mResultColumnCount;
449 return NS_OK;
452 NS_IMETHODIMP
453 Statement::GetColumnName(uint32_t aColumnIndex, nsACString& _name) {
454 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
455 ENSURE_INDEX_VALUE(aColumnIndex, mResultColumnCount);
457 const char* cname = ::sqlite3_column_name(mDBStatement, aColumnIndex);
458 _name.Assign(nsDependentCString(cname));
460 return NS_OK;
463 NS_IMETHODIMP
464 Statement::GetColumnIndex(const nsACString& aName, uint32_t* _index) {
465 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
467 // Surprisingly enough, SQLite doesn't provide an API for this. We have to
468 // determine it ourselves sadly.
469 for (uint32_t i = 0; i < mResultColumnCount; i++) {
470 if (mColumnNames[i].Equals(aName)) {
471 *_index = i;
472 return NS_OK;
476 return NS_ERROR_INVALID_ARG;
479 NS_IMETHODIMP
480 Statement::Reset() {
481 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
483 #ifdef DEBUG
484 MOZ_LOG(gStorageLog, LogLevel::Debug,
485 ("Resetting statement: '%s'", ::sqlite3_sql(mDBStatement)));
487 checkAndLogStatementPerformance(mDBStatement);
488 #endif
490 mParamsArray = nullptr;
491 (void)sqlite3_reset(mDBStatement);
492 (void)sqlite3_clear_bindings(mDBStatement);
494 mExecuting = false;
496 // This will be a no-op if the status has already been recorded or if this
497 // statement has not been executed. Async statements have their status
498 // tracked and recorded in StatementData.
499 MaybeRecordQueryStatus(SQLITE_OK, true);
500 mHasExecuted = false;
502 return NS_OK;
505 NS_IMETHODIMP
506 Statement::BindParameters(mozIStorageBindingParamsArray* aParameters) {
507 NS_ENSURE_ARG_POINTER(aParameters);
509 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
511 BindingParamsArray* array = static_cast<BindingParamsArray*>(aParameters);
512 if (array->getOwner() != this) return NS_ERROR_UNEXPECTED;
514 if (array->length() == 0) return NS_ERROR_UNEXPECTED;
516 mParamsArray = array;
517 mParamsArray->lock();
519 return NS_OK;
522 NS_IMETHODIMP
523 Statement::Execute() {
524 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
526 bool ret;
527 nsresult rv = ExecuteStep(&ret);
528 nsresult rv2 = Reset();
530 return NS_FAILED(rv) ? rv : rv2;
533 NS_IMETHODIMP
534 Statement::ExecuteStep(bool* _moreResults) {
535 AUTO_PROFILER_LABEL("Statement::ExecuteStep", OTHER);
537 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
539 // Bind any parameters first before executing.
540 if (mParamsArray) {
541 // If we have more than one row of parameters to bind, they shouldn't be
542 // calling this method (and instead use executeAsync).
543 if (mParamsArray->length() != 1) return NS_ERROR_UNEXPECTED;
545 BindingParamsArray::iterator row = mParamsArray->begin();
546 nsCOMPtr<IStorageBindingParamsInternal> bindingInternal =
547 do_QueryInterface(*row);
548 nsCOMPtr<mozIStorageError> error = bindingInternal->bind(mDBStatement);
549 if (error) {
550 int32_t srv;
551 (void)error->GetResult(&srv);
552 return convertResultCode(srv);
555 // We have bound, so now we can clear our array.
556 mParamsArray = nullptr;
558 int srv = mDBConnection->stepStatement(mNativeConnection, mDBStatement);
559 mHasExecuted = true;
560 MaybeRecordQueryStatus(srv);
562 if (srv != SQLITE_ROW && srv != SQLITE_DONE &&
563 MOZ_LOG_TEST(gStorageLog, LogLevel::Debug)) {
564 nsAutoCString errStr;
565 (void)mDBConnection->GetLastErrorString(errStr);
566 MOZ_LOG(gStorageLog, LogLevel::Debug,
567 ("Statement::ExecuteStep error: %s", errStr.get()));
570 // SQLITE_ROW and SQLITE_DONE are non-errors
571 if (srv == SQLITE_ROW) {
572 // we got a row back
573 mExecuting = true;
574 *_moreResults = true;
575 return NS_OK;
576 } else if (srv == SQLITE_DONE) {
577 // statement is done (no row returned)
578 mExecuting = false;
579 *_moreResults = false;
580 return NS_OK;
581 } else if (srv == SQLITE_BUSY || srv == SQLITE_MISUSE) {
582 mExecuting = false;
583 } else if (mExecuting) {
584 MOZ_LOG(gStorageLog, LogLevel::Error,
585 ("SQLite error after mExecuting was true!"));
586 mExecuting = false;
589 return convertResultCode(srv);
592 NS_IMETHODIMP
593 Statement::GetState(int32_t* _state) {
594 if (!mDBStatement)
595 *_state = MOZ_STORAGE_STATEMENT_INVALID;
596 else if (mExecuting)
597 *_state = MOZ_STORAGE_STATEMENT_EXECUTING;
598 else
599 *_state = MOZ_STORAGE_STATEMENT_READY;
601 return NS_OK;
604 ////////////////////////////////////////////////////////////////////////////////
605 //// mozIStorageValueArray (now part of mozIStorageStatement too)
607 NS_IMETHODIMP
608 Statement::GetNumEntries(uint32_t* _length) {
609 *_length = mResultColumnCount;
610 return NS_OK;
613 NS_IMETHODIMP
614 Statement::GetTypeOfIndex(uint32_t aIndex, int32_t* _type) {
615 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
617 ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
619 if (!mExecuting) return NS_ERROR_UNEXPECTED;
621 int t = ::sqlite3_column_type(mDBStatement, aIndex);
622 switch (t) {
623 case SQLITE_INTEGER:
624 *_type = mozIStorageStatement::VALUE_TYPE_INTEGER;
625 break;
626 case SQLITE_FLOAT:
627 *_type = mozIStorageStatement::VALUE_TYPE_FLOAT;
628 break;
629 case SQLITE_TEXT:
630 *_type = mozIStorageStatement::VALUE_TYPE_TEXT;
631 break;
632 case SQLITE_BLOB:
633 *_type = mozIStorageStatement::VALUE_TYPE_BLOB;
634 break;
635 case SQLITE_NULL:
636 *_type = mozIStorageStatement::VALUE_TYPE_NULL;
637 break;
638 default:
639 return NS_ERROR_FAILURE;
642 return NS_OK;
645 NS_IMETHODIMP
646 Statement::GetInt32(uint32_t aIndex, int32_t* _value) {
647 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
649 ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
651 if (!mExecuting) return NS_ERROR_UNEXPECTED;
653 *_value = ::sqlite3_column_int(mDBStatement, aIndex);
654 return NS_OK;
657 NS_IMETHODIMP
658 Statement::GetInt64(uint32_t aIndex, int64_t* _value) {
659 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
661 ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
663 if (!mExecuting) return NS_ERROR_UNEXPECTED;
665 *_value = ::sqlite3_column_int64(mDBStatement, aIndex);
667 return NS_OK;
670 NS_IMETHODIMP
671 Statement::GetDouble(uint32_t aIndex, double* _value) {
672 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
674 ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
676 if (!mExecuting) return NS_ERROR_UNEXPECTED;
678 *_value = ::sqlite3_column_double(mDBStatement, aIndex);
680 return NS_OK;
683 NS_IMETHODIMP
684 Statement::GetUTF8String(uint32_t aIndex, nsACString& _value) {
685 // Get type of Index will check aIndex for us, so we don't have to.
686 int32_t type;
687 nsresult rv = GetTypeOfIndex(aIndex, &type);
688 NS_ENSURE_SUCCESS(rv, rv);
689 if (type == mozIStorageStatement::VALUE_TYPE_NULL) {
690 // NULL columns should have IsVoid set to distinguish them from the empty
691 // string.
692 _value.SetIsVoid(true);
693 } else {
694 const char* value = reinterpret_cast<const char*>(
695 ::sqlite3_column_text(mDBStatement, aIndex));
696 _value.Assign(value, ::sqlite3_column_bytes(mDBStatement, aIndex));
698 return NS_OK;
701 NS_IMETHODIMP
702 Statement::GetString(uint32_t aIndex, nsAString& _value) {
703 // Get type of Index will check aIndex for us, so we don't have to.
704 int32_t type;
705 nsresult rv = GetTypeOfIndex(aIndex, &type);
706 NS_ENSURE_SUCCESS(rv, rv);
707 if (type == mozIStorageStatement::VALUE_TYPE_NULL) {
708 // NULL columns should have IsVoid set to distinguish them from the empty
709 // string.
710 _value.SetIsVoid(true);
711 } else {
712 const char16_t* value = static_cast<const char16_t*>(
713 ::sqlite3_column_text16(mDBStatement, aIndex));
714 _value.Assign(value, ::sqlite3_column_bytes16(mDBStatement, aIndex) /
715 sizeof(char16_t));
717 return NS_OK;
720 NS_IMETHODIMP
721 Statement::GetVariant(uint32_t aIndex, nsIVariant** _value) {
722 if (!mDBStatement) {
723 return NS_ERROR_NOT_INITIALIZED;
726 ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
728 if (!mExecuting) {
729 return NS_ERROR_UNEXPECTED;
732 nsCOMPtr<nsIVariant> variant;
733 int type = ::sqlite3_column_type(mDBStatement, aIndex);
734 switch (type) {
735 case SQLITE_INTEGER:
736 variant =
737 new IntegerVariant(::sqlite3_column_int64(mDBStatement, aIndex));
738 break;
739 case SQLITE_FLOAT:
740 variant = new FloatVariant(::sqlite3_column_double(mDBStatement, aIndex));
741 break;
742 case SQLITE_TEXT: {
743 const char16_t* value = static_cast<const char16_t*>(
744 ::sqlite3_column_text16(mDBStatement, aIndex));
745 nsDependentString str(
746 value,
747 ::sqlite3_column_bytes16(mDBStatement, aIndex) / sizeof(char16_t));
748 variant = new TextVariant(str);
749 break;
751 case SQLITE_NULL:
752 variant = new NullVariant();
753 break;
754 case SQLITE_BLOB: {
755 int size = ::sqlite3_column_bytes(mDBStatement, aIndex);
756 const void* data = ::sqlite3_column_blob(mDBStatement, aIndex);
757 variant = new BlobVariant(std::pair<const void*, int>(data, size));
758 break;
761 NS_ENSURE_TRUE(variant, NS_ERROR_UNEXPECTED);
763 variant.forget(_value);
764 return NS_OK;
767 NS_IMETHODIMP
768 Statement::GetBlob(uint32_t aIndex, uint32_t* _size, uint8_t** _blob) {
769 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
771 ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
773 if (!mExecuting) return NS_ERROR_UNEXPECTED;
775 int size = ::sqlite3_column_bytes(mDBStatement, aIndex);
776 void* blob = nullptr;
777 if (size) {
778 blob = moz_xmemdup(::sqlite3_column_blob(mDBStatement, aIndex), size);
781 *_blob = static_cast<uint8_t*>(blob);
782 *_size = size;
783 return NS_OK;
786 NS_IMETHODIMP
787 Statement::GetBlobAsString(uint32_t aIndex, nsAString& aValue) {
788 return DoGetBlobAsString(this, aIndex, aValue);
791 NS_IMETHODIMP
792 Statement::GetBlobAsUTF8String(uint32_t aIndex, nsACString& aValue) {
793 return DoGetBlobAsString(this, aIndex, aValue);
796 NS_IMETHODIMP
797 Statement::GetSharedUTF8String(uint32_t aIndex, uint32_t* _byteLength,
798 const char** _value) {
799 *_value = reinterpret_cast<const char*>(
800 ::sqlite3_column_text(mDBStatement, aIndex));
801 if (_byteLength) {
802 *_byteLength = ::sqlite3_column_bytes(mDBStatement, aIndex);
804 return NS_OK;
807 NS_IMETHODIMP
808 Statement::GetSharedString(uint32_t aIndex, uint32_t* _byteLength,
809 const char16_t** _value) {
810 *_value = static_cast<const char16_t*>(
811 ::sqlite3_column_text16(mDBStatement, aIndex));
812 if (_byteLength) {
813 *_byteLength = ::sqlite3_column_bytes16(mDBStatement, aIndex);
815 return NS_OK;
818 NS_IMETHODIMP
819 Statement::GetSharedBlob(uint32_t aIndex, uint32_t* _byteLength,
820 const uint8_t** _blob) {
821 *_blob =
822 static_cast<const uint8_t*>(::sqlite3_column_blob(mDBStatement, aIndex));
823 if (_byteLength) {
824 *_byteLength = ::sqlite3_column_bytes(mDBStatement, aIndex);
826 return NS_OK;
829 NS_IMETHODIMP
830 Statement::GetIsNull(uint32_t aIndex, bool* _isNull) {
831 // Get type of Index will check aIndex for us, so we don't have to.
832 int32_t type;
833 nsresult rv = GetTypeOfIndex(aIndex, &type);
834 NS_ENSURE_SUCCESS(rv, rv);
835 *_isNull = (type == mozIStorageStatement::VALUE_TYPE_NULL);
836 return NS_OK;
839 ////////////////////////////////////////////////////////////////////////////////
840 //// mozIStorageBindingParams
842 BOILERPLATE_BIND_PROXIES(Statement,
843 if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;)
845 } // namespace storage
846 } // namespace mozilla