Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include <limits.h>
8 : #include <stdio.h>
9 :
10 : #include "nsError.h"
11 : #include "nsMemory.h"
12 : #include "nsThreadUtils.h"
13 : #include "nsIClassInfoImpl.h"
14 : #include "Variant.h"
15 :
16 : #include "mozIStorageError.h"
17 :
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"
27 :
28 : #include "mozilla/Logging.h"
29 : #include "mozilla/Printf.h"
30 :
31 :
32 : extern mozilla::LazyLogModule gStorageLog;
33 :
34 : namespace mozilla {
35 : namespace storage {
36 :
37 : ////////////////////////////////////////////////////////////////////////////////
38 : //// nsIClassInfo
39 :
40 0 : NS_IMPL_CI_INTERFACE_GETTER(Statement,
41 : mozIStorageStatement,
42 : mozIStorageBaseStatement,
43 : mozIStorageBindingParams,
44 : mozIStorageValueArray,
45 : mozilla::storage::StorageBaseStatementInternal)
46 :
47 : class StatementClassInfo : public nsIClassInfo
48 : {
49 : public:
50 : constexpr StatementClassInfo() {}
51 :
52 : NS_DECL_ISUPPORTS_INHERITED
53 :
54 : NS_IMETHOD
55 0 : GetInterfaces(uint32_t *_count, nsIID ***_array) override
56 : {
57 0 : return NS_CI_INTERFACE_GETTER_NAME(Statement)(_count, _array);
58 : }
59 :
60 : NS_IMETHOD
61 0 : GetScriptableHelper(nsIXPCScriptable **_helper) override
62 : {
63 0 : static StatementJSHelper sJSHelper;
64 0 : *_helper = &sJSHelper;
65 0 : return NS_OK;
66 : }
67 :
68 : NS_IMETHOD
69 0 : GetContractID(char **_contractID) override
70 : {
71 0 : *_contractID = nullptr;
72 0 : return NS_OK;
73 : }
74 :
75 : NS_IMETHOD
76 0 : GetClassDescription(char **_desc) override
77 : {
78 0 : *_desc = nullptr;
79 0 : return NS_OK;
80 : }
81 :
82 : NS_IMETHOD
83 0 : GetClassID(nsCID **_id) override
84 : {
85 0 : *_id = nullptr;
86 0 : return NS_OK;
87 : }
88 :
89 : NS_IMETHOD
90 0 : GetFlags(uint32_t *_flags) override
91 : {
92 0 : *_flags = 0;
93 0 : return NS_OK;
94 : }
95 :
96 : NS_IMETHOD
97 0 : GetClassIDNoAlloc(nsCID *_cid) override
98 : {
99 0 : return NS_ERROR_NOT_AVAILABLE;
100 : }
101 : };
102 :
103 0 : NS_IMETHODIMP_(MozExternalRefCountType) StatementClassInfo::AddRef() { return 2; }
104 0 : NS_IMETHODIMP_(MozExternalRefCountType) StatementClassInfo::Release() { return 1; }
105 0 : NS_IMPL_QUERY_INTERFACE(StatementClassInfo, nsIClassInfo)
106 :
107 : static StatementClassInfo sStatementClassInfo;
108 :
109 : ////////////////////////////////////////////////////////////////////////////////
110 : //// Statement
111 :
112 29 : Statement::Statement()
113 : : StorageBaseStatementInternal()
114 : , mDBStatement(nullptr)
115 : , mColumnNames()
116 29 : , mExecuting(false)
117 : {
118 29 : }
119 :
120 : nsresult
121 29 : Statement::initialize(Connection *aDBConnection,
122 : sqlite3 *aNativeConnection,
123 : const nsACString &aSQLStatement)
124 : {
125 29 : MOZ_ASSERT(aDBConnection, "No database connection given!");
126 29 : MOZ_ASSERT(aDBConnection->isConnectionReadyOnThisThread(),
127 : "Database connection should be valid");
128 29 : MOZ_ASSERT(!mDBStatement, "Statement already initialized!");
129 29 : MOZ_ASSERT(aNativeConnection, "No native connection given!");
130 :
131 29 : int srv = aDBConnection->prepareStatement(aNativeConnection,
132 58 : PromiseFlatCString(aSQLStatement),
133 29 : &mDBStatement);
134 29 : if (srv != SQLITE_OK) {
135 0 : MOZ_LOG(gStorageLog, LogLevel::Error,
136 : ("Sqlite statement prepare error: %d '%s'", srv,
137 : ::sqlite3_errmsg(aNativeConnection)));
138 0 : MOZ_LOG(gStorageLog, LogLevel::Error,
139 : ("Statement was: '%s'", PromiseFlatCString(aSQLStatement).get()));
140 0 : return NS_ERROR_FAILURE;
141 : }
142 :
143 29 : MOZ_LOG(gStorageLog, LogLevel::Debug, ("Initialized statement '%s' (0x%p)",
144 : PromiseFlatCString(aSQLStatement).get(),
145 : mDBStatement));
146 :
147 29 : mDBConnection = aDBConnection;
148 29 : mNativeConnection = aNativeConnection;
149 29 : mParamCount = ::sqlite3_bind_parameter_count(mDBStatement);
150 29 : mResultColumnCount = ::sqlite3_column_count(mDBStatement);
151 29 : mColumnNames.Clear();
152 :
153 29 : nsCString* columnNames = mColumnNames.AppendElements(mResultColumnCount);
154 91 : for (uint32_t i = 0; i < mResultColumnCount; i++) {
155 62 : const char *name = ::sqlite3_column_name(mDBStatement, i);
156 62 : columnNames[i].Assign(name);
157 : }
158 :
159 : #ifdef DEBUG
160 : // We want to try and test for LIKE and that consumers are using
161 : // escapeStringForLIKE instead of just trusting user input. The idea to
162 : // check to see if they are binding a parameter after like instead of just
163 : // using a string. We only do this in debug builds because it's expensive!
164 29 : const nsCaseInsensitiveCStringComparator c;
165 29 : nsACString::const_iterator start, end, e;
166 29 : aSQLStatement.BeginReading(start);
167 29 : aSQLStatement.EndReading(end);
168 29 : e = end;
169 31 : while (::FindInReadable(NS_LITERAL_CSTRING(" LIKE"), start, e, c)) {
170 : // We have a LIKE in here, so we perform our tests
171 : // FindInReadable moves the iterator, so we have to get a new one for
172 : // each test we perform.
173 1 : nsACString::const_iterator s1, s2, s3;
174 1 : s1 = s2 = s3 = start;
175 :
176 4 : if (!(::FindInReadable(NS_LITERAL_CSTRING(" LIKE ?"), s1, end, c) ||
177 3 : ::FindInReadable(NS_LITERAL_CSTRING(" LIKE :"), s2, end, c) ||
178 1 : ::FindInReadable(NS_LITERAL_CSTRING(" LIKE @"), s3, end, c))) {
179 : // At this point, we didn't find a LIKE statement followed by ?, :,
180 : // or @, all of which are valid characters for binding a parameter.
181 : // We will warn the consumer that they may not be safely using LIKE.
182 : NS_WARNING("Unsafe use of LIKE detected! Please ensure that you "
183 : "are using mozIStorageStatement::escapeStringForLIKE "
184 : "and that you are binding that result to the statement "
185 0 : "to prevent SQL injection attacks.");
186 : }
187 :
188 : // resetting start and e
189 1 : start = e;
190 1 : e = end;
191 : }
192 : #endif
193 :
194 29 : return NS_OK;
195 : }
196 :
197 : mozIStorageBindingParams *
198 29 : Statement::getParams()
199 : {
200 : nsresult rv;
201 :
202 : // If we do not have an array object yet, make it.
203 29 : if (!mParamsArray) {
204 26 : nsCOMPtr<mozIStorageBindingParamsArray> array;
205 13 : rv = NewBindingParamsArray(getter_AddRefs(array));
206 13 : NS_ENSURE_SUCCESS(rv, nullptr);
207 :
208 13 : mParamsArray = static_cast<BindingParamsArray *>(array.get());
209 : }
210 :
211 : // If there isn't already any rows added, we'll have to add one to use.
212 29 : if (mParamsArray->length() == 0) {
213 39 : RefPtr<BindingParams> params(new BindingParams(mParamsArray, this));
214 13 : NS_ENSURE_TRUE(params, nullptr);
215 :
216 13 : rv = mParamsArray->AddParams(params);
217 13 : NS_ENSURE_SUCCESS(rv, nullptr);
218 :
219 : // We have to unlock our params because AddParams locks them. This is safe
220 : // because no reference to the params object was, or ever will be given out.
221 13 : params->unlock(this);
222 :
223 : // We also want to lock our array at this point - we don't want anything to
224 : // be added to it. Nothing has, or will ever get a reference to it, but we
225 : // will get additional safety checks via assertions by doing this.
226 13 : mParamsArray->lock();
227 : }
228 :
229 29 : return *mParamsArray->begin();
230 : }
231 :
232 40 : Statement::~Statement()
233 : {
234 20 : (void)internalFinalize(true);
235 20 : }
236 :
237 : ////////////////////////////////////////////////////////////////////////////////
238 : //// nsISupports
239 :
240 153 : NS_IMPL_ADDREF(Statement)
241 164 : NS_IMPL_RELEASE(Statement)
242 :
243 86 : NS_INTERFACE_MAP_BEGIN(Statement)
244 86 : NS_INTERFACE_MAP_ENTRY(mozIStorageStatement)
245 13 : NS_INTERFACE_MAP_ENTRY(mozIStorageBaseStatement)
246 13 : NS_INTERFACE_MAP_ENTRY(mozIStorageBindingParams)
247 13 : NS_INTERFACE_MAP_ENTRY(mozIStorageValueArray)
248 13 : NS_INTERFACE_MAP_ENTRY(mozilla::storage::StorageBaseStatementInternal)
249 0 : if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
250 0 : foundInterface = static_cast<nsIClassInfo *>(&sStatementClassInfo);
251 : }
252 : else
253 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageStatement)
254 0 : NS_INTERFACE_MAP_END
255 :
256 :
257 : ////////////////////////////////////////////////////////////////////////////////
258 : //// StorageBaseStatementInternal
259 :
260 : Connection *
261 0 : Statement::getOwner()
262 : {
263 0 : return mDBConnection;
264 : }
265 :
266 : int
267 0 : Statement::getAsyncStatement(sqlite3_stmt **_stmt)
268 : {
269 : // If we have no statement, we shouldn't be calling this method!
270 0 : NS_ASSERTION(mDBStatement != nullptr, "We have no statement to clone!");
271 :
272 : // If we do not yet have a cached async statement, clone our statement now.
273 0 : if (!mAsyncStatement) {
274 0 : nsDependentCString sql(::sqlite3_sql(mDBStatement));
275 0 : int rc = mDBConnection->prepareStatement(mNativeConnection, sql,
276 0 : &mAsyncStatement);
277 0 : if (rc != SQLITE_OK) {
278 0 : *_stmt = nullptr;
279 0 : return rc;
280 : }
281 :
282 0 : MOZ_LOG(gStorageLog, LogLevel::Debug,
283 : ("Cloned statement 0x%p to 0x%p", mDBStatement, mAsyncStatement));
284 : }
285 :
286 0 : *_stmt = mAsyncStatement;
287 0 : return SQLITE_OK;
288 : }
289 :
290 : nsresult
291 0 : Statement::getAsynchronousStatementData(StatementData &_data)
292 : {
293 0 : if (!mDBStatement)
294 0 : return NS_ERROR_UNEXPECTED;
295 :
296 : sqlite3_stmt *stmt;
297 0 : int rc = getAsyncStatement(&stmt);
298 0 : if (rc != SQLITE_OK)
299 0 : return convertResultCode(rc);
300 :
301 0 : _data = StatementData(stmt, bindingParamsArray(), this);
302 :
303 0 : return NS_OK;
304 : }
305 :
306 : already_AddRefed<mozIStorageBindingParams>
307 0 : Statement::newBindingParams(mozIStorageBindingParamsArray *aOwner)
308 : {
309 0 : nsCOMPtr<mozIStorageBindingParams> params = new BindingParams(aOwner, this);
310 0 : return params.forget();
311 : }
312 :
313 :
314 : ////////////////////////////////////////////////////////////////////////////////
315 : //// mozIStorageStatement
316 :
317 : // proxy to StorageBaseStatementInternal using its define helper.
318 13 : MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(Statement, (void)0;)
319 :
320 : NS_IMETHODIMP
321 0 : Statement::Clone(mozIStorageStatement **_statement)
322 : {
323 0 : RefPtr<Statement> statement(new Statement());
324 0 : NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
325 :
326 0 : nsAutoCString sql(::sqlite3_sql(mDBStatement));
327 0 : nsresult rv = statement->initialize(mDBConnection, mNativeConnection, sql);
328 0 : NS_ENSURE_SUCCESS(rv, rv);
329 :
330 0 : statement.forget(_statement);
331 0 : return NS_OK;
332 : }
333 :
334 : NS_IMETHODIMP
335 0 : Statement::Finalize()
336 : {
337 0 : return internalFinalize(false);
338 : }
339 :
340 : nsresult
341 20 : Statement::internalFinalize(bool aDestructing)
342 : {
343 20 : if (!mDBStatement)
344 0 : return NS_OK;
345 :
346 20 : int srv = SQLITE_OK;
347 :
348 : {
349 : // If the statement ends up being finalized twice, the second finalization
350 : // would apply to a dangling pointer and may cause unexpected consequences.
351 : // Thus we must be sure that the connection state won't change during this
352 : // operation, to avoid racing with finalizations made by the closing
353 : // connection. See Connection::internalClose().
354 40 : MutexAutoLock lockedScope(mDBConnection->sharedAsyncExecutionMutex);
355 20 : if (!mDBConnection->isClosed(lockedScope)) {
356 20 : MOZ_LOG(gStorageLog, LogLevel::Debug, ("Finalizing statement '%s' during garbage-collection",
357 : ::sqlite3_sql(mDBStatement)));
358 20 : srv = ::sqlite3_finalize(mDBStatement);
359 : }
360 : #ifdef DEBUG
361 : else {
362 : // The database connection is closed. The sqlite
363 : // statement has either been finalized already by the connection
364 : // or is about to be finalized by the connection.
365 : //
366 : // Finalizing it here would be useless and segfaultish.
367 : //
368 : // Note that we can't display the statement itself, as the data structure
369 : // is not valid anymore. However, the address shown here should help
370 : // developers correlate with the more complete debug message triggered
371 : // by AsyncClose().
372 :
373 : SmprintfPointer msg = ::mozilla::Smprintf("SQL statement (%p) should have been finalized"
374 : " before garbage-collection. For more details on this statement, set"
375 : " NSPR_LOG_MESSAGES=mozStorage:5 .",
376 0 : mDBStatement);
377 0 : NS_WARNING(msg.get());
378 :
379 : // Use %s so we aren't exposing random strings to printf interpolation.
380 0 : MOZ_LOG(gStorageLog, LogLevel::Warning, ("%s", msg.get()));
381 : }
382 : #endif // DEBUG
383 : }
384 :
385 20 : mDBStatement = nullptr;
386 :
387 20 : if (mAsyncStatement) {
388 : // If the destructor called us, there are no pending async statements (they
389 : // hold a reference to us) and we can/must just kill the statement directly.
390 0 : if (aDestructing)
391 0 : destructorAsyncFinalize();
392 : else
393 0 : asyncFinalize();
394 : }
395 :
396 : // Release the holders, so they can release the reference to us.
397 20 : mStatementParamsHolder = nullptr;
398 20 : mStatementRowHolder = nullptr;
399 :
400 20 : return convertResultCode(srv);
401 : }
402 :
403 : NS_IMETHODIMP
404 13 : Statement::GetParameterCount(uint32_t *_parameterCount)
405 : {
406 13 : if (!mDBStatement)
407 0 : return NS_ERROR_NOT_INITIALIZED;
408 :
409 13 : *_parameterCount = mParamCount;
410 13 : return NS_OK;
411 : }
412 :
413 : NS_IMETHODIMP
414 0 : Statement::GetParameterName(uint32_t aParamIndex,
415 : nsACString &_name)
416 : {
417 0 : if (!mDBStatement)
418 0 : return NS_ERROR_NOT_INITIALIZED;
419 0 : ENSURE_INDEX_VALUE(aParamIndex, mParamCount);
420 :
421 0 : const char *name = ::sqlite3_bind_parameter_name(mDBStatement,
422 0 : aParamIndex + 1);
423 0 : if (name == nullptr) {
424 : // this thing had no name, so fake one
425 0 : nsAutoCString fakeName(":");
426 0 : fakeName.AppendInt(aParamIndex);
427 0 : _name.Assign(fakeName);
428 : }
429 : else {
430 0 : _name.Assign(nsDependentCString(name));
431 : }
432 :
433 0 : return NS_OK;
434 : }
435 :
436 : NS_IMETHODIMP
437 27 : Statement::GetParameterIndex(const nsACString &aName,
438 : uint32_t *_index)
439 : {
440 27 : if (!mDBStatement)
441 0 : return NS_ERROR_NOT_INITIALIZED;
442 :
443 : // We do not accept any forms of names other than ":name", but we need to add
444 : // the colon for SQLite.
445 54 : nsAutoCString name(":");
446 27 : name.Append(aName);
447 27 : int ind = ::sqlite3_bind_parameter_index(mDBStatement, name.get());
448 27 : if (ind == 0) // Named parameter not found.
449 0 : return NS_ERROR_INVALID_ARG;
450 :
451 27 : *_index = ind - 1; // SQLite indexes are 1-based, we are 0-based.
452 :
453 27 : return NS_OK;
454 : }
455 :
456 : NS_IMETHODIMP
457 0 : Statement::GetColumnCount(uint32_t *_columnCount)
458 : {
459 0 : if (!mDBStatement)
460 0 : return NS_ERROR_NOT_INITIALIZED;
461 :
462 0 : *_columnCount = mResultColumnCount;
463 0 : return NS_OK;
464 : }
465 :
466 : NS_IMETHODIMP
467 0 : Statement::GetColumnName(uint32_t aColumnIndex,
468 : nsACString &_name)
469 : {
470 0 : if (!mDBStatement)
471 0 : return NS_ERROR_NOT_INITIALIZED;
472 0 : ENSURE_INDEX_VALUE(aColumnIndex, mResultColumnCount);
473 :
474 0 : const char *cname = ::sqlite3_column_name(mDBStatement, aColumnIndex);
475 0 : _name.Assign(nsDependentCString(cname));
476 :
477 0 : return NS_OK;
478 : }
479 :
480 : NS_IMETHODIMP
481 0 : Statement::GetColumnIndex(const nsACString &aName,
482 : uint32_t *_index)
483 : {
484 0 : if (!mDBStatement)
485 0 : return NS_ERROR_NOT_INITIALIZED;
486 :
487 : // Surprisingly enough, SQLite doesn't provide an API for this. We have to
488 : // determine it ourselves sadly.
489 0 : for (uint32_t i = 0; i < mResultColumnCount; i++) {
490 0 : if (mColumnNames[i].Equals(aName)) {
491 0 : *_index = i;
492 0 : return NS_OK;
493 : }
494 : }
495 :
496 0 : return NS_ERROR_INVALID_ARG;
497 : }
498 :
499 : NS_IMETHODIMP
500 17 : Statement::Reset()
501 : {
502 17 : if (!mDBStatement)
503 0 : return NS_ERROR_NOT_INITIALIZED;
504 :
505 : #ifdef DEBUG
506 17 : MOZ_LOG(gStorageLog, LogLevel::Debug, ("Resetting statement: '%s'",
507 : ::sqlite3_sql(mDBStatement)));
508 :
509 17 : checkAndLogStatementPerformance(mDBStatement);
510 : #endif
511 :
512 17 : mParamsArray = nullptr;
513 17 : (void)sqlite3_reset(mDBStatement);
514 17 : (void)sqlite3_clear_bindings(mDBStatement);
515 :
516 17 : mExecuting = false;
517 :
518 17 : return NS_OK;
519 : }
520 :
521 : NS_IMETHODIMP
522 0 : Statement::BindParameters(mozIStorageBindingParamsArray *aParameters)
523 : {
524 0 : NS_ENSURE_ARG_POINTER(aParameters);
525 :
526 0 : if (!mDBStatement)
527 0 : return NS_ERROR_NOT_INITIALIZED;
528 :
529 0 : BindingParamsArray *array = static_cast<BindingParamsArray *>(aParameters);
530 0 : if (array->getOwner() != this)
531 0 : return NS_ERROR_UNEXPECTED;
532 :
533 0 : if (array->length() == 0)
534 0 : return NS_ERROR_UNEXPECTED;
535 :
536 0 : mParamsArray = array;
537 0 : mParamsArray->lock();
538 :
539 0 : return NS_OK;
540 : }
541 :
542 : NS_IMETHODIMP
543 3 : Statement::Execute()
544 : {
545 3 : if (!mDBStatement)
546 0 : return NS_ERROR_NOT_INITIALIZED;
547 :
548 : bool ret;
549 3 : nsresult rv = ExecuteStep(&ret);
550 3 : nsresult rv2 = Reset();
551 :
552 3 : return NS_FAILED(rv) ? rv : rv2;
553 : }
554 :
555 : NS_IMETHODIMP
556 37 : Statement::ExecuteStep(bool *_moreResults)
557 : {
558 74 : AUTO_PROFILER_LABEL("Statement::ExecuteStep", STORAGE);
559 :
560 37 : if (!mDBStatement)
561 0 : return NS_ERROR_NOT_INITIALIZED;
562 :
563 : // Bind any parameters first before executing.
564 37 : if (mParamsArray) {
565 : // If we have more than one row of parameters to bind, they shouldn't be
566 : // calling this method (and instead use executeAsync).
567 13 : if (mParamsArray->length() != 1)
568 0 : return NS_ERROR_UNEXPECTED;
569 :
570 13 : BindingParamsArray::iterator row = mParamsArray->begin();
571 : nsCOMPtr<IStorageBindingParamsInternal> bindingInternal =
572 26 : do_QueryInterface(*row);
573 26 : nsCOMPtr<mozIStorageError> error = bindingInternal->bind(mDBStatement);
574 13 : if (error) {
575 : int32_t srv;
576 0 : (void)error->GetResult(&srv);
577 0 : return convertResultCode(srv);
578 : }
579 :
580 : // We have bound, so now we can clear our array.
581 13 : mParamsArray = nullptr;
582 : }
583 37 : int srv = mDBConnection->stepStatement(mNativeConnection, mDBStatement);
584 :
585 37 : if (srv != SQLITE_ROW && srv != SQLITE_DONE && MOZ_LOG_TEST(gStorageLog, LogLevel::Debug)) {
586 0 : nsAutoCString errStr;
587 0 : (void)mDBConnection->GetLastErrorString(errStr);
588 0 : MOZ_LOG(gStorageLog, LogLevel::Debug,
589 : ("Statement::ExecuteStep error: %s", errStr.get()));
590 : }
591 :
592 : // SQLITE_ROW and SQLITE_DONE are non-errors
593 37 : if (srv == SQLITE_ROW) {
594 : // we got a row back
595 23 : mExecuting = true;
596 23 : *_moreResults = true;
597 23 : return NS_OK;
598 : }
599 14 : else if (srv == SQLITE_DONE) {
600 : // statement is done (no row returned)
601 14 : mExecuting = false;
602 14 : *_moreResults = false;
603 14 : return NS_OK;
604 : }
605 0 : else if (srv == SQLITE_BUSY || srv == SQLITE_MISUSE) {
606 0 : mExecuting = false;
607 : }
608 0 : else if (mExecuting) {
609 0 : MOZ_LOG(gStorageLog, LogLevel::Error,
610 : ("SQLite error after mExecuting was true!"));
611 0 : mExecuting = false;
612 : }
613 :
614 0 : return convertResultCode(srv);
615 : }
616 :
617 : NS_IMETHODIMP
618 0 : Statement::GetState(int32_t *_state)
619 : {
620 0 : if (!mDBStatement)
621 0 : *_state = MOZ_STORAGE_STATEMENT_INVALID;
622 0 : else if (mExecuting)
623 0 : *_state = MOZ_STORAGE_STATEMENT_EXECUTING;
624 : else
625 0 : *_state = MOZ_STORAGE_STATEMENT_READY;
626 :
627 0 : return NS_OK;
628 : }
629 :
630 : ////////////////////////////////////////////////////////////////////////////////
631 : //// mozIStorageValueArray (now part of mozIStorageStatement too)
632 :
633 : NS_IMETHODIMP
634 0 : Statement::GetNumEntries(uint32_t *_length)
635 : {
636 0 : *_length = mResultColumnCount;
637 0 : return NS_OK;
638 : }
639 :
640 : NS_IMETHODIMP
641 11 : Statement::GetTypeOfIndex(uint32_t aIndex,
642 : int32_t *_type)
643 : {
644 11 : if (!mDBStatement)
645 0 : return NS_ERROR_NOT_INITIALIZED;
646 :
647 11 : ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
648 :
649 11 : if (!mExecuting)
650 0 : return NS_ERROR_UNEXPECTED;
651 :
652 11 : int t = ::sqlite3_column_type(mDBStatement, aIndex);
653 11 : switch (t) {
654 : case SQLITE_INTEGER:
655 0 : *_type = mozIStorageStatement::VALUE_TYPE_INTEGER;
656 0 : break;
657 : case SQLITE_FLOAT:
658 0 : *_type = mozIStorageStatement::VALUE_TYPE_FLOAT;
659 0 : break;
660 : case SQLITE_TEXT:
661 9 : *_type = mozIStorageStatement::VALUE_TYPE_TEXT;
662 9 : break;
663 : case SQLITE_BLOB:
664 0 : *_type = mozIStorageStatement::VALUE_TYPE_BLOB;
665 0 : break;
666 : case SQLITE_NULL:
667 2 : *_type = mozIStorageStatement::VALUE_TYPE_NULL;
668 2 : break;
669 : default:
670 0 : return NS_ERROR_FAILURE;
671 : }
672 :
673 11 : return NS_OK;
674 : }
675 :
676 : NS_IMETHODIMP
677 22 : Statement::GetInt32(uint32_t aIndex,
678 : int32_t *_value)
679 : {
680 22 : if (!mDBStatement)
681 0 : return NS_ERROR_NOT_INITIALIZED;
682 :
683 22 : ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
684 :
685 22 : if (!mExecuting)
686 0 : return NS_ERROR_UNEXPECTED;
687 :
688 22 : *_value = ::sqlite3_column_int(mDBStatement, aIndex);
689 22 : return NS_OK;
690 : }
691 :
692 : NS_IMETHODIMP
693 4 : Statement::GetInt64(uint32_t aIndex,
694 : int64_t *_value)
695 : {
696 4 : if (!mDBStatement)
697 0 : return NS_ERROR_NOT_INITIALIZED;
698 :
699 4 : ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
700 :
701 4 : if (!mExecuting)
702 0 : return NS_ERROR_UNEXPECTED;
703 :
704 4 : *_value = ::sqlite3_column_int64(mDBStatement, aIndex);
705 :
706 4 : return NS_OK;
707 : }
708 :
709 : NS_IMETHODIMP
710 0 : Statement::GetDouble(uint32_t aIndex,
711 : double *_value)
712 : {
713 0 : if (!mDBStatement)
714 0 : return NS_ERROR_NOT_INITIALIZED;
715 :
716 0 : ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
717 :
718 0 : if (!mExecuting)
719 0 : return NS_ERROR_UNEXPECTED;
720 :
721 0 : *_value = ::sqlite3_column_double(mDBStatement, aIndex);
722 :
723 0 : return NS_OK;
724 : }
725 :
726 : NS_IMETHODIMP
727 9 : Statement::GetUTF8String(uint32_t aIndex,
728 : nsACString &_value)
729 : {
730 : // Get type of Index will check aIndex for us, so we don't have to.
731 : int32_t type;
732 9 : nsresult rv = GetTypeOfIndex(aIndex, &type);
733 9 : NS_ENSURE_SUCCESS(rv, rv);
734 9 : if (type == mozIStorageStatement::VALUE_TYPE_NULL) {
735 : // NULL columns should have IsVoid set to distinguish them from the empty
736 : // string.
737 0 : _value.SetIsVoid(true);
738 : }
739 : else {
740 : const char *value =
741 9 : reinterpret_cast<const char *>(::sqlite3_column_text(mDBStatement,
742 9 : aIndex));
743 9 : _value.Assign(value, ::sqlite3_column_bytes(mDBStatement, aIndex));
744 : }
745 9 : return NS_OK;
746 : }
747 :
748 : NS_IMETHODIMP
749 1 : Statement::GetString(uint32_t aIndex,
750 : nsAString &_value)
751 : {
752 : // Get type of Index will check aIndex for us, so we don't have to.
753 : int32_t type;
754 1 : nsresult rv = GetTypeOfIndex(aIndex, &type);
755 1 : NS_ENSURE_SUCCESS(rv, rv);
756 1 : if (type == mozIStorageStatement::VALUE_TYPE_NULL) {
757 : // NULL columns should have IsVoid set to distinguish them from the empty
758 : // string.
759 1 : _value.SetIsVoid(true);
760 : } else {
761 : const char16_t *value =
762 0 : static_cast<const char16_t *>(::sqlite3_column_text16(mDBStatement,
763 0 : aIndex));
764 0 : _value.Assign(value, ::sqlite3_column_bytes16(mDBStatement, aIndex) / 2);
765 : }
766 1 : return NS_OK;
767 : }
768 :
769 : NS_IMETHODIMP
770 0 : Statement::GetBlob(uint32_t aIndex,
771 : uint32_t *_size,
772 : uint8_t **_blob)
773 : {
774 0 : if (!mDBStatement)
775 0 : return NS_ERROR_NOT_INITIALIZED;
776 :
777 0 : ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
778 :
779 0 : if (!mExecuting)
780 0 : return NS_ERROR_UNEXPECTED;
781 :
782 0 : int size = ::sqlite3_column_bytes(mDBStatement, aIndex);
783 0 : void *blob = nullptr;
784 0 : if (size) {
785 0 : blob = nsMemory::Clone(::sqlite3_column_blob(mDBStatement, aIndex), size);
786 0 : NS_ENSURE_TRUE(blob, NS_ERROR_OUT_OF_MEMORY);
787 : }
788 :
789 0 : *_blob = static_cast<uint8_t *>(blob);
790 0 : *_size = size;
791 0 : return NS_OK;
792 : }
793 :
794 : NS_IMETHODIMP
795 0 : Statement::GetBlobAsString(uint32_t aIndex, nsAString& aValue)
796 : {
797 0 : return DoGetBlobAsString(this, aIndex, aValue);
798 : }
799 :
800 : NS_IMETHODIMP
801 0 : Statement::GetBlobAsUTF8String(uint32_t aIndex, nsACString& aValue)
802 : {
803 0 : return DoGetBlobAsString(this, aIndex, aValue);
804 : }
805 :
806 : NS_IMETHODIMP
807 0 : Statement::GetSharedUTF8String(uint32_t aIndex,
808 : uint32_t *_length,
809 : const char **_value)
810 : {
811 0 : if (_length)
812 0 : *_length = ::sqlite3_column_bytes(mDBStatement, aIndex);
813 :
814 0 : *_value = reinterpret_cast<const char *>(::sqlite3_column_text(mDBStatement,
815 : aIndex));
816 0 : return NS_OK;
817 : }
818 :
819 : NS_IMETHODIMP
820 0 : Statement::GetSharedString(uint32_t aIndex,
821 : uint32_t *_length,
822 : const char16_t **_value)
823 : {
824 0 : if (_length)
825 0 : *_length = ::sqlite3_column_bytes16(mDBStatement, aIndex);
826 :
827 0 : *_value = static_cast<const char16_t *>(::sqlite3_column_text16(mDBStatement,
828 : aIndex));
829 0 : return NS_OK;
830 : }
831 :
832 : NS_IMETHODIMP
833 0 : Statement::GetSharedBlob(uint32_t aIndex,
834 : uint32_t *_size,
835 : const uint8_t **_blob)
836 : {
837 0 : *_size = ::sqlite3_column_bytes(mDBStatement, aIndex);
838 0 : *_blob = static_cast<const uint8_t *>(::sqlite3_column_blob(mDBStatement,
839 : aIndex));
840 0 : return NS_OK;
841 : }
842 :
843 : NS_IMETHODIMP
844 1 : Statement::GetIsNull(uint32_t aIndex,
845 : bool *_isNull)
846 : {
847 : // Get type of Index will check aIndex for us, so we don't have to.
848 : int32_t type;
849 1 : nsresult rv = GetTypeOfIndex(aIndex, &type);
850 1 : NS_ENSURE_SUCCESS(rv, rv);
851 1 : *_isNull = (type == mozIStorageStatement::VALUE_TYPE_NULL);
852 1 : return NS_OK;
853 : }
854 :
855 : ////////////////////////////////////////////////////////////////////////////////
856 : //// mozIStorageBindingParams
857 :
858 29 : BOILERPLATE_BIND_PROXIES(
859 : Statement,
860 : if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
861 : )
862 :
863 : } // namespace storage
864 : } // namespace mozilla
|