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