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/. */
12 #include "nsThreadUtils.h"
13 #include "nsIClassInfoImpl.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"
25 #include "GeckoProfiler.h"
26 #include "nsDOMClassInfo.h"
28 #include "mozilla/Logging.h"
29 #include "mozilla/Printf.h"
32 extern mozilla::LazyLogModule gStorageLog
;
37 ////////////////////////////////////////////////////////////////////////////////
40 NS_IMPL_CI_INTERFACE_GETTER(Statement
,
42 mozIStorageBaseStatement
,
43 mozIStorageBindingParams
,
44 mozIStorageValueArray
,
45 mozilla::storage::StorageBaseStatementInternal
)
47 class StatementClassInfo
: public nsIClassInfo
50 constexpr StatementClassInfo() {}
52 NS_DECL_ISUPPORTS_INHERITED
55 GetInterfaces(uint32_t *_count
, nsIID
***_array
) override
57 return NS_CI_INTERFACE_GETTER_NAME(Statement
)(_count
, _array
);
61 GetScriptableHelper(nsIXPCScriptable
**_helper
) override
63 static StatementJSHelper sJSHelper
;
64 *_helper
= &sJSHelper
;
69 GetContractID(char **_contractID
) override
71 *_contractID
= nullptr;
76 GetClassDescription(char **_desc
) override
83 GetClassID(nsCID
**_id
) override
90 GetFlags(uint32_t *_flags
) override
97 GetClassIDNoAlloc(nsCID
*_cid
) override
99 return NS_ERROR_NOT_AVAILABLE
;
103 NS_IMETHODIMP_(MozExternalRefCountType
) StatementClassInfo::AddRef() { return 2; }
104 NS_IMETHODIMP_(MozExternalRefCountType
) StatementClassInfo::Release() { return 1; }
105 NS_IMPL_QUERY_INTERFACE(StatementClassInfo
, nsIClassInfo
)
107 static StatementClassInfo sStatementClassInfo
;
109 ////////////////////////////////////////////////////////////////////////////////
112 Statement::Statement()
113 : StorageBaseStatementInternal()
114 , mDBStatement(nullptr)
121 Statement::initialize(Connection
*aDBConnection
,
122 sqlite3
*aNativeConnection
,
123 const nsACString
&aSQLStatement
)
125 MOZ_ASSERT(aDBConnection
, "No database connection given!");
126 MOZ_ASSERT(!aDBConnection
->isClosed(), "Database connection should be valid");
127 MOZ_ASSERT(!mDBStatement
, "Statement already initialized!");
128 MOZ_ASSERT(aNativeConnection
, "No native connection given!");
130 int srv
= aDBConnection
->prepareStatement(aNativeConnection
,
131 PromiseFlatCString(aSQLStatement
),
133 if (srv
!= SQLITE_OK
) {
134 MOZ_LOG(gStorageLog
, LogLevel::Error
,
135 ("Sqlite statement prepare error: %d '%s'", srv
,
136 ::sqlite3_errmsg(aNativeConnection
)));
137 MOZ_LOG(gStorageLog
, LogLevel::Error
,
138 ("Statement was: '%s'", PromiseFlatCString(aSQLStatement
).get()));
139 return NS_ERROR_FAILURE
;
142 MOZ_LOG(gStorageLog
, LogLevel::Debug
, ("Initialized statement '%s' (0x%p)",
143 PromiseFlatCString(aSQLStatement
).get(),
146 mDBConnection
= aDBConnection
;
147 mNativeConnection
= aNativeConnection
;
148 mParamCount
= ::sqlite3_bind_parameter_count(mDBStatement
);
149 mResultColumnCount
= ::sqlite3_column_count(mDBStatement
);
150 mColumnNames
.Clear();
152 nsCString
* columnNames
= mColumnNames
.AppendElements(mResultColumnCount
);
153 for (uint32_t i
= 0; i
< mResultColumnCount
; i
++) {
154 const char *name
= ::sqlite3_column_name(mDBStatement
, i
);
155 columnNames
[i
].Assign(name
);
159 // We want to try and test for LIKE and that consumers are using
160 // escapeStringForLIKE instead of just trusting user input. The idea to
161 // check to see if they are binding a parameter after like instead of just
162 // using a string. We only do this in debug builds because it's expensive!
163 const nsCaseInsensitiveCStringComparator c
;
164 nsACString::const_iterator start
, end
, e
;
165 aSQLStatement
.BeginReading(start
);
166 aSQLStatement
.EndReading(end
);
168 while (::FindInReadable(NS_LITERAL_CSTRING(" LIKE"), start
, e
, c
)) {
169 // We have a LIKE in here, so we perform our tests
170 // FindInReadable moves the iterator, so we have to get a new one for
171 // each test we perform.
172 nsACString::const_iterator s1
, s2
, s3
;
173 s1
= s2
= s3
= start
;
175 if (!(::FindInReadable(NS_LITERAL_CSTRING(" LIKE ?"), s1
, end
, c
) ||
176 ::FindInReadable(NS_LITERAL_CSTRING(" LIKE :"), s2
, end
, c
) ||
177 ::FindInReadable(NS_LITERAL_CSTRING(" LIKE @"), s3
, end
, c
))) {
178 // At this point, we didn't find a LIKE statement followed by ?, :,
179 // or @, all of which are valid characters for binding a parameter.
180 // We will warn the consumer that they may not be safely using LIKE.
181 NS_WARNING("Unsafe use of LIKE detected! Please ensure that you "
182 "are using mozIStorageStatement::escapeStringForLIKE "
183 "and that you are binding that result to the statement "
184 "to prevent SQL injection attacks.");
187 // resetting start and e
196 mozIStorageBindingParams
*
197 Statement::getParams()
201 // If we do not have an array object yet, make it.
203 nsCOMPtr
<mozIStorageBindingParamsArray
> array
;
204 rv
= NewBindingParamsArray(getter_AddRefs(array
));
205 NS_ENSURE_SUCCESS(rv
, nullptr);
207 mParamsArray
= static_cast<BindingParamsArray
*>(array
.get());
210 // If there isn't already any rows added, we'll have to add one to use.
211 if (mParamsArray
->length() == 0) {
212 RefPtr
<BindingParams
> params(new BindingParams(mParamsArray
, this));
213 NS_ENSURE_TRUE(params
, nullptr);
215 rv
= mParamsArray
->AddParams(params
);
216 NS_ENSURE_SUCCESS(rv
, nullptr);
218 // We have to unlock our params because AddParams locks them. This is safe
219 // because no reference to the params object was, or ever will be given out.
220 params
->unlock(this);
222 // We also want to lock our array at this point - we don't want anything to
223 // be added to it. Nothing has, or will ever get a reference to it, but we
224 // will get additional safety checks via assertions by doing this.
225 mParamsArray
->lock();
228 return *mParamsArray
->begin();
231 Statement::~Statement()
233 (void)internalFinalize(true);
236 ////////////////////////////////////////////////////////////////////////////////
239 NS_IMPL_ADDREF(Statement
)
240 NS_IMPL_RELEASE(Statement
)
242 NS_INTERFACE_MAP_BEGIN(Statement
)
243 NS_INTERFACE_MAP_ENTRY(mozIStorageStatement
)
244 NS_INTERFACE_MAP_ENTRY(mozIStorageBaseStatement
)
245 NS_INTERFACE_MAP_ENTRY(mozIStorageBindingParams
)
246 NS_INTERFACE_MAP_ENTRY(mozIStorageValueArray
)
247 NS_INTERFACE_MAP_ENTRY(mozilla::storage::StorageBaseStatementInternal
)
248 if (aIID
.Equals(NS_GET_IID(nsIClassInfo
))) {
249 foundInterface
= static_cast<nsIClassInfo
*>(&sStatementClassInfo
);
252 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, mozIStorageStatement
)
256 ////////////////////////////////////////////////////////////////////////////////
257 //// StorageBaseStatementInternal
260 Statement::getOwner()
262 return mDBConnection
;
266 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
,
276 if (rc
!= SQLITE_OK
) {
281 MOZ_LOG(gStorageLog
, LogLevel::Debug
,
282 ("Cloned statement 0x%p to 0x%p", mDBStatement
, mAsyncStatement
));
285 *_stmt
= mAsyncStatement
;
290 Statement::getAsynchronousStatementData(StatementData
&_data
)
293 return NS_ERROR_UNEXPECTED
;
296 int rc
= getAsyncStatement(&stmt
);
298 return convertResultCode(rc
);
300 _data
= StatementData(stmt
, bindingParamsArray(), this);
305 already_AddRefed
<mozIStorageBindingParams
>
306 Statement::newBindingParams(mozIStorageBindingParamsArray
*aOwner
)
308 nsCOMPtr
<mozIStorageBindingParams
> params
= new BindingParams(aOwner
, this);
309 return params
.forget();
313 ////////////////////////////////////////////////////////////////////////////////
314 //// mozIStorageStatement
316 // proxy to StorageBaseStatementInternal using its define helper.
317 MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(Statement
, (void)0;)
320 Statement::Clone(mozIStorageStatement
**_statement
)
322 RefPtr
<Statement
> statement(new Statement());
323 NS_ENSURE_TRUE(statement
, NS_ERROR_OUT_OF_MEMORY
);
325 nsAutoCString
sql(::sqlite3_sql(mDBStatement
));
326 nsresult rv
= statement
->initialize(mDBConnection
, mNativeConnection
, sql
);
327 NS_ENSURE_SUCCESS(rv
, rv
);
329 statement
.forget(_statement
);
334 Statement::Finalize()
336 return internalFinalize(false);
340 Statement::internalFinalize(bool aDestructing
)
347 if (!mDBConnection
->isClosed()) {
349 // The connection is still open. While statement finalization and
350 // closing may, in some cases, take place in two distinct threads,
351 // we have a guarantee that the connection will remain open until
352 // this method terminates:
354 // a. The connection will be closed synchronously. In this case,
355 // there is no race condition, as everything takes place on the
358 // b. The connection is closed asynchronously and this code is
359 // executed on the opener thread. In this case, asyncClose() has
360 // not been called yet and will not be called before we return
361 // from this function.
363 // c. The connection is closed asynchronously and this code is
364 // executed on the async execution thread. In this case,
365 // AsyncCloseConnection::Run() has not been called yet and will
366 // not be called before we return from this function.
368 // In either case, the connection is still valid, hence closing
371 MOZ_LOG(gStorageLog
, LogLevel::Debug
, ("Finalizing statement '%s' during garbage-collection",
372 ::sqlite3_sql(mDBStatement
)));
373 srv
= ::sqlite3_finalize(mDBStatement
);
378 // The database connection is either closed or closing. The sqlite
379 // statement has either been finalized already by the connection
380 // or is about to be finalized by the connection.
382 // Finalizing it here would be useless and segfaultish.
385 char *msg
= ::mozilla::Smprintf("SQL statement (%p) should have been finalized"
386 " before garbage-collection. For more details on this statement, set"
387 " NSPR_LOG_MESSAGES=mozStorage:5 .",
391 // Note that we can't display the statement itself, as the data structure
392 // is not valid anymore. However, the address shown here should help
393 // developers correlate with the more complete debug message triggered
398 // Deactivate the warning until we have fixed the exising culprit
403 // Use %s so we aren't exposing random strings to printf interpolation.
404 MOZ_LOG(gStorageLog
, LogLevel::Warning
, ("%s", msg
));
406 ::mozilla::SmprintfFree(msg
);
411 mDBStatement
= nullptr;
413 if (mAsyncStatement
) {
414 // If the destructor called us, there are no pending async statements (they
415 // hold a reference to us) and we can/must just kill the statement directly.
417 destructorAsyncFinalize();
422 // Release the holders, so they can release the reference to us.
423 mStatementParamsHolder
= nullptr;
424 mStatementRowHolder
= nullptr;
426 return convertResultCode(srv
);
430 Statement::GetParameterCount(uint32_t *_parameterCount
)
433 return NS_ERROR_NOT_INITIALIZED
;
435 *_parameterCount
= mParamCount
;
440 Statement::GetParameterName(uint32_t aParamIndex
,
444 return NS_ERROR_NOT_INITIALIZED
;
445 ENSURE_INDEX_VALUE(aParamIndex
, mParamCount
);
447 const char *name
= ::sqlite3_bind_parameter_name(mDBStatement
,
449 if (name
== nullptr) {
450 // this thing had no name, so fake one
451 nsAutoCString
fakeName(":");
452 fakeName
.AppendInt(aParamIndex
);
453 _name
.Assign(fakeName
);
456 _name
.Assign(nsDependentCString(name
));
463 Statement::GetParameterIndex(const nsACString
&aName
,
467 return NS_ERROR_NOT_INITIALIZED
;
469 // We do not accept any forms of names other than ":name", but we need to add
470 // the colon for SQLite.
471 nsAutoCString
name(":");
473 int ind
= ::sqlite3_bind_parameter_index(mDBStatement
, name
.get());
474 if (ind
== 0) // Named parameter not found.
475 return NS_ERROR_INVALID_ARG
;
477 *_index
= ind
- 1; // SQLite indexes are 1-based, we are 0-based.
483 Statement::GetColumnCount(uint32_t *_columnCount
)
486 return NS_ERROR_NOT_INITIALIZED
;
488 *_columnCount
= mResultColumnCount
;
493 Statement::GetColumnName(uint32_t aColumnIndex
,
497 return NS_ERROR_NOT_INITIALIZED
;
498 ENSURE_INDEX_VALUE(aColumnIndex
, mResultColumnCount
);
500 const char *cname
= ::sqlite3_column_name(mDBStatement
, aColumnIndex
);
501 _name
.Assign(nsDependentCString(cname
));
507 Statement::GetColumnIndex(const nsACString
&aName
,
511 return NS_ERROR_NOT_INITIALIZED
;
513 // Surprisingly enough, SQLite doesn't provide an API for this. We have to
514 // determine it ourselves sadly.
515 for (uint32_t i
= 0; i
< mResultColumnCount
; i
++) {
516 if (mColumnNames
[i
].Equals(aName
)) {
522 return NS_ERROR_INVALID_ARG
;
529 return NS_ERROR_NOT_INITIALIZED
;
532 MOZ_LOG(gStorageLog
, LogLevel::Debug
, ("Resetting statement: '%s'",
533 ::sqlite3_sql(mDBStatement
)));
535 checkAndLogStatementPerformance(mDBStatement
);
538 mParamsArray
= nullptr;
539 (void)sqlite3_reset(mDBStatement
);
540 (void)sqlite3_clear_bindings(mDBStatement
);
548 Statement::BindParameters(mozIStorageBindingParamsArray
*aParameters
)
550 NS_ENSURE_ARG_POINTER(aParameters
);
553 return NS_ERROR_NOT_INITIALIZED
;
555 BindingParamsArray
*array
= static_cast<BindingParamsArray
*>(aParameters
);
556 if (array
->getOwner() != this)
557 return NS_ERROR_UNEXPECTED
;
559 if (array
->length() == 0)
560 return NS_ERROR_UNEXPECTED
;
562 mParamsArray
= array
;
563 mParamsArray
->lock();
572 return NS_ERROR_NOT_INITIALIZED
;
575 nsresult rv
= ExecuteStep(&ret
);
576 nsresult rv2
= Reset();
578 return NS_FAILED(rv
) ? rv
: rv2
;
582 Statement::ExecuteStep(bool *_moreResults
)
584 PROFILER_LABEL("Statement", "ExecuteStep",
585 js::ProfileEntry::Category::STORAGE
);
588 return NS_ERROR_NOT_INITIALIZED
;
590 // Bind any parameters first before executing.
592 // If we have more than one row of parameters to bind, they shouldn't be
593 // calling this method (and instead use executeAsync).
594 if (mParamsArray
->length() != 1)
595 return NS_ERROR_UNEXPECTED
;
597 BindingParamsArray::iterator row
= mParamsArray
->begin();
598 nsCOMPtr
<IStorageBindingParamsInternal
> bindingInternal
=
599 do_QueryInterface(*row
);
600 nsCOMPtr
<mozIStorageError
> error
= bindingInternal
->bind(mDBStatement
);
603 (void)error
->GetResult(&srv
);
604 return convertResultCode(srv
);
607 // We have bound, so now we can clear our array.
608 mParamsArray
= nullptr;
610 int srv
= mDBConnection
->stepStatement(mNativeConnection
, mDBStatement
);
612 if (srv
!= SQLITE_ROW
&& srv
!= SQLITE_DONE
&& MOZ_LOG_TEST(gStorageLog
, LogLevel::Debug
)) {
613 nsAutoCString errStr
;
614 (void)mDBConnection
->GetLastErrorString(errStr
);
615 MOZ_LOG(gStorageLog
, LogLevel::Debug
,
616 ("Statement::ExecuteStep error: %s", errStr
.get()));
619 // SQLITE_ROW and SQLITE_DONE are non-errors
620 if (srv
== SQLITE_ROW
) {
623 *_moreResults
= true;
626 else if (srv
== SQLITE_DONE
) {
627 // statement is done (no row returned)
629 *_moreResults
= false;
632 else if (srv
== SQLITE_BUSY
|| srv
== SQLITE_MISUSE
) {
635 else if (mExecuting
) {
636 MOZ_LOG(gStorageLog
, LogLevel::Error
,
637 ("SQLite error after mExecuting was true!"));
641 return convertResultCode(srv
);
645 Statement::GetState(int32_t *_state
)
648 *_state
= MOZ_STORAGE_STATEMENT_INVALID
;
650 *_state
= MOZ_STORAGE_STATEMENT_EXECUTING
;
652 *_state
= MOZ_STORAGE_STATEMENT_READY
;
657 ////////////////////////////////////////////////////////////////////////////////
658 //// mozIStorageValueArray (now part of mozIStorageStatement too)
661 Statement::GetNumEntries(uint32_t *_length
)
663 *_length
= mResultColumnCount
;
668 Statement::GetTypeOfIndex(uint32_t aIndex
,
672 return NS_ERROR_NOT_INITIALIZED
;
674 ENSURE_INDEX_VALUE(aIndex
, mResultColumnCount
);
677 return NS_ERROR_UNEXPECTED
;
679 int t
= ::sqlite3_column_type(mDBStatement
, aIndex
);
682 *_type
= mozIStorageStatement::VALUE_TYPE_INTEGER
;
685 *_type
= mozIStorageStatement::VALUE_TYPE_FLOAT
;
688 *_type
= mozIStorageStatement::VALUE_TYPE_TEXT
;
691 *_type
= mozIStorageStatement::VALUE_TYPE_BLOB
;
694 *_type
= mozIStorageStatement::VALUE_TYPE_NULL
;
697 return NS_ERROR_FAILURE
;
704 Statement::GetInt32(uint32_t aIndex
,
708 return NS_ERROR_NOT_INITIALIZED
;
710 ENSURE_INDEX_VALUE(aIndex
, mResultColumnCount
);
713 return NS_ERROR_UNEXPECTED
;
715 *_value
= ::sqlite3_column_int(mDBStatement
, aIndex
);
720 Statement::GetInt64(uint32_t aIndex
,
724 return NS_ERROR_NOT_INITIALIZED
;
726 ENSURE_INDEX_VALUE(aIndex
, mResultColumnCount
);
729 return NS_ERROR_UNEXPECTED
;
731 *_value
= ::sqlite3_column_int64(mDBStatement
, aIndex
);
737 Statement::GetDouble(uint32_t aIndex
,
741 return NS_ERROR_NOT_INITIALIZED
;
743 ENSURE_INDEX_VALUE(aIndex
, mResultColumnCount
);
746 return NS_ERROR_UNEXPECTED
;
748 *_value
= ::sqlite3_column_double(mDBStatement
, aIndex
);
754 Statement::GetUTF8String(uint32_t aIndex
,
757 // Get type of Index will check aIndex for us, so we don't have to.
759 nsresult rv
= GetTypeOfIndex(aIndex
, &type
);
760 NS_ENSURE_SUCCESS(rv
, rv
);
761 if (type
== mozIStorageStatement::VALUE_TYPE_NULL
) {
762 // NULL columns should have IsVoid set to distinguish them from the empty
764 _value
.SetIsVoid(true);
768 reinterpret_cast<const char *>(::sqlite3_column_text(mDBStatement
,
770 _value
.Assign(value
, ::sqlite3_column_bytes(mDBStatement
, aIndex
));
776 Statement::GetString(uint32_t aIndex
,
779 // Get type of Index will check aIndex for us, so we don't have to.
781 nsresult rv
= GetTypeOfIndex(aIndex
, &type
);
782 NS_ENSURE_SUCCESS(rv
, rv
);
783 if (type
== mozIStorageStatement::VALUE_TYPE_NULL
) {
784 // NULL columns should have IsVoid set to distinguish them from the empty
786 _value
.SetIsVoid(true);
788 const char16_t
*value
=
789 static_cast<const char16_t
*>(::sqlite3_column_text16(mDBStatement
,
791 _value
.Assign(value
, ::sqlite3_column_bytes16(mDBStatement
, aIndex
) / 2);
797 Statement::GetBlob(uint32_t aIndex
,
802 return NS_ERROR_NOT_INITIALIZED
;
804 ENSURE_INDEX_VALUE(aIndex
, mResultColumnCount
);
807 return NS_ERROR_UNEXPECTED
;
809 int size
= ::sqlite3_column_bytes(mDBStatement
, aIndex
);
810 void *blob
= nullptr;
812 blob
= nsMemory::Clone(::sqlite3_column_blob(mDBStatement
, aIndex
), size
);
813 NS_ENSURE_TRUE(blob
, NS_ERROR_OUT_OF_MEMORY
);
816 *_blob
= static_cast<uint8_t *>(blob
);
822 Statement::GetBlobAsString(uint32_t aIndex
, nsAString
& aValue
)
824 return DoGetBlobAsString(this, aIndex
, aValue
);
828 Statement::GetBlobAsUTF8String(uint32_t aIndex
, nsACString
& aValue
)
830 return DoGetBlobAsString(this, aIndex
, aValue
);
834 Statement::GetSharedUTF8String(uint32_t aIndex
,
839 *_length
= ::sqlite3_column_bytes(mDBStatement
, aIndex
);
841 *_value
= reinterpret_cast<const char *>(::sqlite3_column_text(mDBStatement
,
847 Statement::GetSharedString(uint32_t aIndex
,
849 const char16_t
**_value
)
852 *_length
= ::sqlite3_column_bytes16(mDBStatement
, aIndex
);
854 *_value
= static_cast<const char16_t
*>(::sqlite3_column_text16(mDBStatement
,
860 Statement::GetSharedBlob(uint32_t aIndex
,
862 const uint8_t **_blob
)
864 *_size
= ::sqlite3_column_bytes(mDBStatement
, aIndex
);
865 *_blob
= static_cast<const uint8_t *>(::sqlite3_column_blob(mDBStatement
,
871 Statement::GetIsNull(uint32_t aIndex
,
874 // Get type of Index will check aIndex for us, so we don't have to.
876 nsresult rv
= GetTypeOfIndex(aIndex
, &type
);
877 NS_ENSURE_SUCCESS(rv
, rv
);
878 *_isNull
= (type
== mozIStorageStatement::VALUE_TYPE_NULL
);
882 ////////////////////////////////////////////////////////////////////////////////
883 //// mozIStorageBindingParams
885 BOILERPLATE_BIND_PROXIES(
887 if (!mDBStatement
) return NS_ERROR_NOT_INITIALIZED
;
890 } // namespace storage
891 } // namespace mozilla