tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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