storage/src/mozStorageStatement.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/storage/src/mozStorageStatement.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,922 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include <limits.h>
    1.11 +#include <stdio.h>
    1.12 +
    1.13 +#include "nsError.h"
    1.14 +#include "nsMemory.h"
    1.15 +#include "nsThreadUtils.h"
    1.16 +#include "nsIClassInfoImpl.h"
    1.17 +#include "nsIProgrammingLanguage.h"
    1.18 +#include "Variant.h"
    1.19 +
    1.20 +#include "mozIStorageError.h"
    1.21 +
    1.22 +#include "mozStorageBindingParams.h"
    1.23 +#include "mozStorageConnection.h"
    1.24 +#include "mozStorageStatementJSHelper.h"
    1.25 +#include "mozStoragePrivateHelpers.h"
    1.26 +#include "mozStorageStatementParams.h"
    1.27 +#include "mozStorageStatementRow.h"
    1.28 +#include "mozStorageStatement.h"
    1.29 +#include "GeckoProfiler.h"
    1.30 +#include "nsDOMClassInfo.h"
    1.31 +
    1.32 +#include "prlog.h"
    1.33 +
    1.34 +
    1.35 +#ifdef PR_LOGGING
    1.36 +extern PRLogModuleInfo* gStorageLog;
    1.37 +#endif
    1.38 +
    1.39 +namespace mozilla {
    1.40 +namespace storage {
    1.41 +
    1.42 +////////////////////////////////////////////////////////////////////////////////
    1.43 +//// nsIClassInfo
    1.44 +
    1.45 +NS_IMPL_CI_INTERFACE_GETTER(Statement,
    1.46 +                            mozIStorageStatement,
    1.47 +                            mozIStorageBaseStatement,
    1.48 +                            mozIStorageBindingParams,
    1.49 +                            mozIStorageValueArray,
    1.50 +                            mozilla::storage::StorageBaseStatementInternal)
    1.51 +
    1.52 +class StatementClassInfo : public nsIClassInfo
    1.53 +{
    1.54 +public:
    1.55 +  MOZ_CONSTEXPR StatementClassInfo() {}
    1.56 +
    1.57 +  NS_DECL_ISUPPORTS_INHERITED
    1.58 +
    1.59 +  NS_IMETHODIMP
    1.60 +  GetInterfaces(uint32_t *_count, nsIID ***_array)
    1.61 +  {
    1.62 +    return NS_CI_INTERFACE_GETTER_NAME(Statement)(_count, _array);
    1.63 +  }
    1.64 +
    1.65 +  NS_IMETHODIMP
    1.66 +  GetHelperForLanguage(uint32_t aLanguage, nsISupports **_helper)
    1.67 +  {
    1.68 +    if (aLanguage == nsIProgrammingLanguage::JAVASCRIPT) {
    1.69 +      static StatementJSHelper sJSHelper;
    1.70 +      *_helper = &sJSHelper;
    1.71 +      return NS_OK;
    1.72 +    }
    1.73 +
    1.74 +    *_helper = nullptr;
    1.75 +    return NS_OK;
    1.76 +  }
    1.77 +
    1.78 +  NS_IMETHODIMP
    1.79 +  GetContractID(char **_contractID)
    1.80 +  {
    1.81 +    *_contractID = nullptr;
    1.82 +    return NS_OK;
    1.83 +  }
    1.84 +
    1.85 +  NS_IMETHODIMP
    1.86 +  GetClassDescription(char **_desc)
    1.87 +  {
    1.88 +    *_desc = nullptr;
    1.89 +    return NS_OK;
    1.90 +  }
    1.91 +
    1.92 +  NS_IMETHODIMP
    1.93 +  GetClassID(nsCID **_id)
    1.94 +  {
    1.95 +    *_id = nullptr;
    1.96 +    return NS_OK;
    1.97 +  }
    1.98 +
    1.99 +  NS_IMETHODIMP
   1.100 +  GetImplementationLanguage(uint32_t *_language)
   1.101 +  {
   1.102 +    *_language = nsIProgrammingLanguage::CPLUSPLUS;
   1.103 +    return NS_OK;
   1.104 +  }
   1.105 +
   1.106 +  NS_IMETHODIMP
   1.107 +  GetFlags(uint32_t *_flags)
   1.108 +  {
   1.109 +    *_flags = 0;
   1.110 +    return NS_OK;
   1.111 +  }
   1.112 +
   1.113 +  NS_IMETHODIMP
   1.114 +  GetClassIDNoAlloc(nsCID *_cid)
   1.115 +  {
   1.116 +    return NS_ERROR_NOT_AVAILABLE;
   1.117 +  }
   1.118 +};
   1.119 +
   1.120 +NS_IMETHODIMP_(MozExternalRefCountType) StatementClassInfo::AddRef() { return 2; }
   1.121 +NS_IMETHODIMP_(MozExternalRefCountType) StatementClassInfo::Release() { return 1; }
   1.122 +NS_IMPL_QUERY_INTERFACE(StatementClassInfo, nsIClassInfo)
   1.123 +
   1.124 +static StatementClassInfo sStatementClassInfo;
   1.125 +
   1.126 +////////////////////////////////////////////////////////////////////////////////
   1.127 +//// Statement
   1.128 +
   1.129 +Statement::Statement()
   1.130 +: StorageBaseStatementInternal()
   1.131 +, mDBStatement(nullptr)
   1.132 +, mColumnNames()
   1.133 +, mExecuting(false)
   1.134 +{
   1.135 +}
   1.136 +
   1.137 +nsresult
   1.138 +Statement::initialize(Connection *aDBConnection,
   1.139 +                      sqlite3 *aNativeConnection,
   1.140 +                      const nsACString &aSQLStatement)
   1.141 +{
   1.142 +  MOZ_ASSERT(aDBConnection, "No database connection given!");
   1.143 +  MOZ_ASSERT(!aDBConnection->isClosed(), "Database connection should be valid");
   1.144 +  MOZ_ASSERT(!mDBStatement, "Statement already initialized!");
   1.145 +  MOZ_ASSERT(aNativeConnection, "No native connection given!");
   1.146 +
   1.147 +  int srv = aDBConnection->prepareStatement(aNativeConnection,
   1.148 +                                            PromiseFlatCString(aSQLStatement),
   1.149 +                                            &mDBStatement);
   1.150 +  if (srv != SQLITE_OK) {
   1.151 +      PR_LOG(gStorageLog, PR_LOG_ERROR,
   1.152 +             ("Sqlite statement prepare error: %d '%s'", srv,
   1.153 +              ::sqlite3_errmsg(aNativeConnection)));
   1.154 +      PR_LOG(gStorageLog, PR_LOG_ERROR,
   1.155 +             ("Statement was: '%s'", PromiseFlatCString(aSQLStatement).get()));
   1.156 +      return NS_ERROR_FAILURE;
   1.157 +    }
   1.158 +
   1.159 +  PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Initialized statement '%s' (0x%p)",
   1.160 +                                      PromiseFlatCString(aSQLStatement).get(),
   1.161 +                                      mDBStatement));
   1.162 +
   1.163 +  mDBConnection = aDBConnection;
   1.164 +  mNativeConnection = aNativeConnection;
   1.165 +  mParamCount = ::sqlite3_bind_parameter_count(mDBStatement);
   1.166 +  mResultColumnCount = ::sqlite3_column_count(mDBStatement);
   1.167 +  mColumnNames.Clear();
   1.168 +
   1.169 +  for (uint32_t i = 0; i < mResultColumnCount; i++) {
   1.170 +      const char *name = ::sqlite3_column_name(mDBStatement, i);
   1.171 +      (void)mColumnNames.AppendElement(nsDependentCString(name));
   1.172 +  }
   1.173 +
   1.174 +#ifdef DEBUG
   1.175 +  // We want to try and test for LIKE and that consumers are using
   1.176 +  // escapeStringForLIKE instead of just trusting user input.  The idea to
   1.177 +  // check to see if they are binding a parameter after like instead of just
   1.178 +  // using a string.  We only do this in debug builds because it's expensive!
   1.179 +  const nsCaseInsensitiveCStringComparator c;
   1.180 +  nsACString::const_iterator start, end, e;
   1.181 +  aSQLStatement.BeginReading(start);
   1.182 +  aSQLStatement.EndReading(end);
   1.183 +  e = end;
   1.184 +  while (::FindInReadable(NS_LITERAL_CSTRING(" LIKE"), start, e, c)) {
   1.185 +    // We have a LIKE in here, so we perform our tests
   1.186 +    // FindInReadable moves the iterator, so we have to get a new one for
   1.187 +    // each test we perform.
   1.188 +    nsACString::const_iterator s1, s2, s3;
   1.189 +    s1 = s2 = s3 = start;
   1.190 +
   1.191 +    if (!(::FindInReadable(NS_LITERAL_CSTRING(" LIKE ?"), s1, end, c) ||
   1.192 +          ::FindInReadable(NS_LITERAL_CSTRING(" LIKE :"), s2, end, c) ||
   1.193 +          ::FindInReadable(NS_LITERAL_CSTRING(" LIKE @"), s3, end, c))) {
   1.194 +      // At this point, we didn't find a LIKE statement followed by ?, :,
   1.195 +      // or @, all of which are valid characters for binding a parameter.
   1.196 +      // We will warn the consumer that they may not be safely using LIKE.
   1.197 +      NS_WARNING("Unsafe use of LIKE detected!  Please ensure that you "
   1.198 +                 "are using mozIStorageStatement::escapeStringForLIKE "
   1.199 +                 "and that you are binding that result to the statement "
   1.200 +                 "to prevent SQL injection attacks.");
   1.201 +    }
   1.202 +
   1.203 +    // resetting start and e
   1.204 +    start = e;
   1.205 +    e = end;
   1.206 +  }
   1.207 +#endif
   1.208 +
   1.209 +  return NS_OK;
   1.210 +}
   1.211 +
   1.212 +mozIStorageBindingParams *
   1.213 +Statement::getParams()
   1.214 +{
   1.215 +  nsresult rv;
   1.216 +
   1.217 +  // If we do not have an array object yet, make it.
   1.218 +  if (!mParamsArray) {
   1.219 +    nsCOMPtr<mozIStorageBindingParamsArray> array;
   1.220 +    rv = NewBindingParamsArray(getter_AddRefs(array));
   1.221 +    NS_ENSURE_SUCCESS(rv, nullptr);
   1.222 +
   1.223 +    mParamsArray = static_cast<BindingParamsArray *>(array.get());
   1.224 +  }
   1.225 +
   1.226 +  // If there isn't already any rows added, we'll have to add one to use.
   1.227 +  if (mParamsArray->length() == 0) {
   1.228 +    nsRefPtr<BindingParams> params(new BindingParams(mParamsArray, this));
   1.229 +    NS_ENSURE_TRUE(params, nullptr);
   1.230 +
   1.231 +    rv = mParamsArray->AddParams(params);
   1.232 +    NS_ENSURE_SUCCESS(rv, nullptr);
   1.233 +
   1.234 +    // We have to unlock our params because AddParams locks them.  This is safe
   1.235 +    // because no reference to the params object was, or ever will be given out.
   1.236 +    params->unlock(this);
   1.237 +
   1.238 +    // We also want to lock our array at this point - we don't want anything to
   1.239 +    // be added to it.  Nothing has, or will ever get a reference to it, but we
   1.240 +    // will get additional safety checks via assertions by doing this.
   1.241 +    mParamsArray->lock();
   1.242 +  }
   1.243 +
   1.244 +  return *mParamsArray->begin();
   1.245 +}
   1.246 +
   1.247 +Statement::~Statement()
   1.248 +{
   1.249 +  (void)internalFinalize(true);
   1.250 +}
   1.251 +
   1.252 +////////////////////////////////////////////////////////////////////////////////
   1.253 +//// nsISupports
   1.254 +
   1.255 +NS_IMPL_ADDREF(Statement)
   1.256 +NS_IMPL_RELEASE(Statement)
   1.257 +
   1.258 +NS_INTERFACE_MAP_BEGIN(Statement)
   1.259 +  NS_INTERFACE_MAP_ENTRY(mozIStorageStatement)
   1.260 +  NS_INTERFACE_MAP_ENTRY(mozIStorageBaseStatement)
   1.261 +  NS_INTERFACE_MAP_ENTRY(mozIStorageBindingParams)
   1.262 +  NS_INTERFACE_MAP_ENTRY(mozIStorageValueArray)
   1.263 +  NS_INTERFACE_MAP_ENTRY(mozilla::storage::StorageBaseStatementInternal)
   1.264 +  if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
   1.265 +    foundInterface = static_cast<nsIClassInfo *>(&sStatementClassInfo);
   1.266 +  }
   1.267 +  else
   1.268 +  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageStatement)
   1.269 +NS_INTERFACE_MAP_END
   1.270 +
   1.271 +
   1.272 +////////////////////////////////////////////////////////////////////////////////
   1.273 +//// StorageBaseStatementInternal
   1.274 +
   1.275 +Connection *
   1.276 +Statement::getOwner()
   1.277 +{
   1.278 +  return mDBConnection;
   1.279 +}
   1.280 +
   1.281 +int
   1.282 +Statement::getAsyncStatement(sqlite3_stmt **_stmt)
   1.283 +{
   1.284 +  // If we have no statement, we shouldn't be calling this method!
   1.285 +  NS_ASSERTION(mDBStatement != nullptr, "We have no statement to clone!");
   1.286 +
   1.287 +  // If we do not yet have a cached async statement, clone our statement now.
   1.288 +  if (!mAsyncStatement) {
   1.289 +    nsDependentCString sql(::sqlite3_sql(mDBStatement));
   1.290 +    int rc = mDBConnection->prepareStatement(mNativeConnection, sql,
   1.291 +                                             &mAsyncStatement);
   1.292 +    if (rc != SQLITE_OK) {
   1.293 +      *_stmt = nullptr;
   1.294 +      return rc;
   1.295 +    }
   1.296 +
   1.297 +    PR_LOG(gStorageLog, PR_LOG_NOTICE,
   1.298 +           ("Cloned statement 0x%p to 0x%p", mDBStatement, mAsyncStatement));
   1.299 +  }
   1.300 +
   1.301 +  *_stmt = mAsyncStatement;
   1.302 +  return SQLITE_OK;
   1.303 +}
   1.304 +
   1.305 +nsresult
   1.306 +Statement::getAsynchronousStatementData(StatementData &_data)
   1.307 +{
   1.308 +  if (!mDBStatement)
   1.309 +    return NS_ERROR_UNEXPECTED;
   1.310 +
   1.311 +  sqlite3_stmt *stmt;
   1.312 +  int rc = getAsyncStatement(&stmt);
   1.313 +  if (rc != SQLITE_OK)
   1.314 +    return convertResultCode(rc);
   1.315 +
   1.316 +  _data = StatementData(stmt, bindingParamsArray(), this);
   1.317 +
   1.318 +  return NS_OK;
   1.319 +}
   1.320 +
   1.321 +already_AddRefed<mozIStorageBindingParams>
   1.322 +Statement::newBindingParams(mozIStorageBindingParamsArray *aOwner)
   1.323 +{
   1.324 +  nsCOMPtr<mozIStorageBindingParams> params = new BindingParams(aOwner, this);
   1.325 +  return params.forget();
   1.326 +}
   1.327 +
   1.328 +
   1.329 +////////////////////////////////////////////////////////////////////////////////
   1.330 +//// mozIStorageStatement
   1.331 +
   1.332 +// proxy to StorageBaseStatementInternal using its define helper.
   1.333 +MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(Statement, (void)0;)
   1.334 +
   1.335 +NS_IMETHODIMP
   1.336 +Statement::Clone(mozIStorageStatement **_statement)
   1.337 +{
   1.338 +  nsRefPtr<Statement> statement(new Statement());
   1.339 +  NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
   1.340 +
   1.341 +  nsAutoCString sql(::sqlite3_sql(mDBStatement));
   1.342 +  nsresult rv = statement->initialize(mDBConnection, mNativeConnection, sql);
   1.343 +  NS_ENSURE_SUCCESS(rv, rv);
   1.344 +
   1.345 +  statement.forget(_statement);
   1.346 +  return NS_OK;
   1.347 +}
   1.348 +
   1.349 +NS_IMETHODIMP
   1.350 +Statement::Finalize()
   1.351 +{
   1.352 +  return internalFinalize(false);
   1.353 +}
   1.354 +
   1.355 +nsresult
   1.356 +Statement::internalFinalize(bool aDestructing)
   1.357 +{
   1.358 +  if (!mDBStatement)
   1.359 +    return NS_OK;
   1.360 +
   1.361 +  int srv = SQLITE_OK;
   1.362 +
   1.363 +  if (!mDBConnection->isClosed()) {
   1.364 +    //
   1.365 +    // The connection is still open. While statement finalization and
   1.366 +    // closing may, in some cases, take place in two distinct threads,
   1.367 +    // we have a guarantee that the connection will remain open until
   1.368 +    // this method terminates:
   1.369 +    //
   1.370 +    // a. The connection will be closed synchronously. In this case,
   1.371 +    // there is no race condition, as everything takes place on the
   1.372 +    // same thread.
   1.373 +    //
   1.374 +    // b. The connection is closed asynchronously and this code is
   1.375 +    // executed on the opener thread. In this case, asyncClose() has
   1.376 +    // not been called yet and will not be called before we return
   1.377 +    // from this function.
   1.378 +    //
   1.379 +    // c. The connection is closed asynchronously and this code is
   1.380 +    // executed on the async execution thread. In this case,
   1.381 +    // AsyncCloseConnection::Run() has not been called yet and will
   1.382 +    // not be called before we return from this function.
   1.383 +    //
   1.384 +    // In either case, the connection is still valid, hence closing
   1.385 +    // here is safe.
   1.386 +    //
   1.387 +    PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Finalizing statement '%s' during garbage-collection",
   1.388 +                                        ::sqlite3_sql(mDBStatement)));
   1.389 +    srv = ::sqlite3_finalize(mDBStatement);
   1.390 +  }
   1.391 +#ifdef DEBUG
   1.392 +  else {
   1.393 +    //
   1.394 +    // The database connection is either closed or closing. The sqlite
   1.395 +    // statement has either been finalized already by the connection
   1.396 +    // or is about to be finalized by the connection.
   1.397 +    //
   1.398 +    // Finalizing it here would be useless and segfaultish.
   1.399 +    //
   1.400 +
   1.401 +    char *msg = ::PR_smprintf("SQL statement (%x) should have been finalized"
   1.402 +      " before garbage-collection. For more details on this statement, set"
   1.403 +      " NSPR_LOG_MESSAGES=mozStorage:5 .",
   1.404 +      mDBStatement);
   1.405 +
   1.406 +    //
   1.407 +    // Note that we can't display the statement itself, as the data structure
   1.408 +    // is not valid anymore. However, the address shown here should help
   1.409 +    // developers correlate with the more complete debug message triggered
   1.410 +    // by AsyncClose().
   1.411 +    //
   1.412 +
   1.413 +#if 0
   1.414 +    // Deactivate the warning until we have fixed the exising culprit
   1.415 +    // (see bug 914070).
   1.416 +    NS_WARNING(msg);
   1.417 +#endif // 0
   1.418 +
   1.419 +    PR_LOG(gStorageLog, PR_LOG_WARNING, (msg));
   1.420 +
   1.421 +    ::PR_smprintf_free(msg);
   1.422 +  }
   1.423 +
   1.424 +#endif
   1.425 +
   1.426 +  mDBStatement = nullptr;
   1.427 +
   1.428 +  if (mAsyncStatement) {
   1.429 +    // If the destructor called us, there are no pending async statements (they
   1.430 +    // hold a reference to us) and we can/must just kill the statement directly.
   1.431 +    if (aDestructing)
   1.432 +      destructorAsyncFinalize();
   1.433 +    else
   1.434 +      asyncFinalize();
   1.435 +  }
   1.436 +
   1.437 +  // We are considered dead at this point, so any wrappers for row or params
   1.438 +  // need to lose their reference to us.
   1.439 +  if (mStatementParamsHolder) {
   1.440 +    nsCOMPtr<nsIXPConnectWrappedNative> wrapper =
   1.441 +        do_QueryInterface(mStatementParamsHolder);
   1.442 +    nsCOMPtr<mozIStorageStatementParams> iParams =
   1.443 +        do_QueryWrappedNative(wrapper);
   1.444 +    StatementParams *params = static_cast<StatementParams *>(iParams.get());
   1.445 +    params->mStatement = nullptr;
   1.446 +    mStatementParamsHolder = nullptr;
   1.447 +  }
   1.448 +
   1.449 +  if (mStatementRowHolder) {
   1.450 +    nsCOMPtr<nsIXPConnectWrappedNative> wrapper =
   1.451 +        do_QueryInterface(mStatementRowHolder);
   1.452 +    nsCOMPtr<mozIStorageStatementRow> iRow =
   1.453 +        do_QueryWrappedNative(wrapper);
   1.454 +    StatementRow *row = static_cast<StatementRow *>(iRow.get());
   1.455 +    row->mStatement = nullptr;
   1.456 +    mStatementRowHolder = nullptr;
   1.457 +  }
   1.458 +
   1.459 +  return convertResultCode(srv);
   1.460 +}
   1.461 +
   1.462 +NS_IMETHODIMP
   1.463 +Statement::GetParameterCount(uint32_t *_parameterCount)
   1.464 +{
   1.465 +  if (!mDBStatement)
   1.466 +    return NS_ERROR_NOT_INITIALIZED;
   1.467 +
   1.468 +  *_parameterCount = mParamCount;
   1.469 +  return NS_OK;
   1.470 +}
   1.471 +
   1.472 +NS_IMETHODIMP
   1.473 +Statement::GetParameterName(uint32_t aParamIndex,
   1.474 +                            nsACString &_name)
   1.475 +{
   1.476 +  if (!mDBStatement)
   1.477 +    return NS_ERROR_NOT_INITIALIZED;
   1.478 +  ENSURE_INDEX_VALUE(aParamIndex, mParamCount);
   1.479 +
   1.480 +  const char *name = ::sqlite3_bind_parameter_name(mDBStatement,
   1.481 +                                                   aParamIndex + 1);
   1.482 +  if (name == nullptr) {
   1.483 +    // this thing had no name, so fake one
   1.484 +    nsAutoCString name(":");
   1.485 +    name.AppendInt(aParamIndex);
   1.486 +    _name.Assign(name);
   1.487 +  }
   1.488 +  else {
   1.489 +    _name.Assign(nsDependentCString(name));
   1.490 +  }
   1.491 +
   1.492 +  return NS_OK;
   1.493 +}
   1.494 +
   1.495 +NS_IMETHODIMP
   1.496 +Statement::GetParameterIndex(const nsACString &aName,
   1.497 +                             uint32_t *_index)
   1.498 +{
   1.499 +  if (!mDBStatement)
   1.500 +    return NS_ERROR_NOT_INITIALIZED;
   1.501 +
   1.502 +  // We do not accept any forms of names other than ":name", but we need to add
   1.503 +  // the colon for SQLite.
   1.504 +  nsAutoCString name(":");
   1.505 +  name.Append(aName);
   1.506 +  int ind = ::sqlite3_bind_parameter_index(mDBStatement, name.get());
   1.507 +  if (ind  == 0) // Named parameter not found.
   1.508 +    return NS_ERROR_INVALID_ARG;
   1.509 +
   1.510 +  *_index = ind - 1; // SQLite indexes are 1-based, we are 0-based.
   1.511 +
   1.512 +  return NS_OK;
   1.513 +}
   1.514 +
   1.515 +NS_IMETHODIMP
   1.516 +Statement::GetColumnCount(uint32_t *_columnCount)
   1.517 +{
   1.518 +  if (!mDBStatement)
   1.519 +    return NS_ERROR_NOT_INITIALIZED;
   1.520 +
   1.521 +  *_columnCount = mResultColumnCount;
   1.522 +  return NS_OK;
   1.523 +}
   1.524 +
   1.525 +NS_IMETHODIMP
   1.526 +Statement::GetColumnName(uint32_t aColumnIndex,
   1.527 +                         nsACString &_name)
   1.528 +{
   1.529 +  if (!mDBStatement)
   1.530 +    return NS_ERROR_NOT_INITIALIZED;
   1.531 +  ENSURE_INDEX_VALUE(aColumnIndex, mResultColumnCount);
   1.532 +
   1.533 +  const char *cname = ::sqlite3_column_name(mDBStatement, aColumnIndex);
   1.534 +  _name.Assign(nsDependentCString(cname));
   1.535 +
   1.536 +  return NS_OK;
   1.537 +}
   1.538 +
   1.539 +NS_IMETHODIMP
   1.540 +Statement::GetColumnIndex(const nsACString &aName,
   1.541 +                          uint32_t *_index)
   1.542 +{
   1.543 +  if (!mDBStatement)
   1.544 +      return NS_ERROR_NOT_INITIALIZED;
   1.545 +
   1.546 +  // Surprisingly enough, SQLite doesn't provide an API for this.  We have to
   1.547 +  // determine it ourselves sadly.
   1.548 +  for (uint32_t i = 0; i < mResultColumnCount; i++) {
   1.549 +    if (mColumnNames[i].Equals(aName)) {
   1.550 +      *_index = i;
   1.551 +      return NS_OK;
   1.552 +    }
   1.553 +  }
   1.554 +
   1.555 +  return NS_ERROR_INVALID_ARG;
   1.556 +}
   1.557 +
   1.558 +NS_IMETHODIMP
   1.559 +Statement::Reset()
   1.560 +{
   1.561 +  if (!mDBStatement)
   1.562 +    return NS_ERROR_NOT_INITIALIZED;
   1.563 +
   1.564 +#ifdef DEBUG
   1.565 +  PR_LOG(gStorageLog, PR_LOG_DEBUG, ("Resetting statement: '%s'",
   1.566 +                                     ::sqlite3_sql(mDBStatement)));
   1.567 +
   1.568 +  checkAndLogStatementPerformance(mDBStatement);
   1.569 +#endif
   1.570 +
   1.571 +  mParamsArray = nullptr;
   1.572 +  (void)sqlite3_reset(mDBStatement);
   1.573 +  (void)sqlite3_clear_bindings(mDBStatement);
   1.574 +
   1.575 +  mExecuting = false;
   1.576 +
   1.577 +  return NS_OK;
   1.578 +}
   1.579 +
   1.580 +NS_IMETHODIMP
   1.581 +Statement::BindParameters(mozIStorageBindingParamsArray *aParameters)
   1.582 +{
   1.583 +  if (!mDBStatement)
   1.584 +    return NS_ERROR_NOT_INITIALIZED;
   1.585 +
   1.586 +  BindingParamsArray *array = static_cast<BindingParamsArray *>(aParameters);
   1.587 +  if (array->getOwner() != this)
   1.588 +    return NS_ERROR_UNEXPECTED;
   1.589 +
   1.590 +  if (array->length() == 0)
   1.591 +    return NS_ERROR_UNEXPECTED;
   1.592 +
   1.593 +  mParamsArray = array;
   1.594 +  mParamsArray->lock();
   1.595 +
   1.596 +  return NS_OK;
   1.597 +}
   1.598 +
   1.599 +NS_IMETHODIMP
   1.600 +Statement::Execute()
   1.601 +{
   1.602 +  if (!mDBStatement)
   1.603 +    return NS_ERROR_NOT_INITIALIZED;
   1.604 +
   1.605 +  bool ret;
   1.606 +  nsresult rv = ExecuteStep(&ret);
   1.607 +  nsresult rv2 = Reset();
   1.608 +
   1.609 +  return NS_FAILED(rv) ? rv : rv2;
   1.610 +}
   1.611 +
   1.612 +NS_IMETHODIMP
   1.613 +Statement::ExecuteStep(bool *_moreResults)
   1.614 +{
   1.615 +  PROFILER_LABEL("storage", "Statement::ExecuteStep");
   1.616 +  if (!mDBStatement)
   1.617 +    return NS_ERROR_NOT_INITIALIZED;
   1.618 +
   1.619 +  // Bind any parameters first before executing.
   1.620 +  if (mParamsArray) {
   1.621 +    // If we have more than one row of parameters to bind, they shouldn't be
   1.622 +    // calling this method (and instead use executeAsync).
   1.623 +    if (mParamsArray->length() != 1)
   1.624 +      return NS_ERROR_UNEXPECTED;
   1.625 +
   1.626 +    BindingParamsArray::iterator row = mParamsArray->begin();
   1.627 +    nsCOMPtr<IStorageBindingParamsInternal> bindingInternal = 
   1.628 +      do_QueryInterface(*row);
   1.629 +    nsCOMPtr<mozIStorageError> error = bindingInternal->bind(mDBStatement);
   1.630 +    if (error) {
   1.631 +      int32_t srv;
   1.632 +      (void)error->GetResult(&srv);
   1.633 +      return convertResultCode(srv);
   1.634 +    }
   1.635 +
   1.636 +    // We have bound, so now we can clear our array.
   1.637 +    mParamsArray = nullptr;
   1.638 +  }
   1.639 +  int srv = mDBConnection->stepStatement(mNativeConnection, mDBStatement);
   1.640 +
   1.641 +#ifdef PR_LOGGING
   1.642 +  if (srv != SQLITE_ROW && srv != SQLITE_DONE) {
   1.643 +      nsAutoCString errStr;
   1.644 +      (void)mDBConnection->GetLastErrorString(errStr);
   1.645 +      PR_LOG(gStorageLog, PR_LOG_DEBUG,
   1.646 +             ("Statement::ExecuteStep error: %s", errStr.get()));
   1.647 +  }
   1.648 +#endif
   1.649 +
   1.650 +  // SQLITE_ROW and SQLITE_DONE are non-errors
   1.651 +  if (srv == SQLITE_ROW) {
   1.652 +    // we got a row back
   1.653 +    mExecuting = true;
   1.654 +    *_moreResults = true;
   1.655 +    return NS_OK;
   1.656 +  }
   1.657 +  else if (srv == SQLITE_DONE) {
   1.658 +    // statement is done (no row returned)
   1.659 +    mExecuting = false;
   1.660 +    *_moreResults = false;
   1.661 +    return NS_OK;
   1.662 +  }
   1.663 +  else if (srv == SQLITE_BUSY || srv == SQLITE_MISUSE) {
   1.664 +    mExecuting = false;
   1.665 +  }
   1.666 +  else if (mExecuting) {
   1.667 +    PR_LOG(gStorageLog, PR_LOG_ERROR,
   1.668 +           ("SQLite error after mExecuting was true!"));
   1.669 +    mExecuting = false;
   1.670 +  }
   1.671 +
   1.672 +  return convertResultCode(srv);
   1.673 +}
   1.674 +
   1.675 +NS_IMETHODIMP
   1.676 +Statement::GetState(int32_t *_state)
   1.677 +{
   1.678 +  if (!mDBStatement)
   1.679 +    *_state = MOZ_STORAGE_STATEMENT_INVALID;
   1.680 +  else if (mExecuting)
   1.681 +    *_state = MOZ_STORAGE_STATEMENT_EXECUTING;
   1.682 +  else
   1.683 +    *_state = MOZ_STORAGE_STATEMENT_READY;
   1.684 +
   1.685 +  return NS_OK;
   1.686 +}
   1.687 +
   1.688 +NS_IMETHODIMP
   1.689 +Statement::GetColumnDecltype(uint32_t aParamIndex,
   1.690 +                             nsACString &_declType)
   1.691 +{
   1.692 +  if (!mDBStatement)
   1.693 +    return NS_ERROR_NOT_INITIALIZED;
   1.694 +
   1.695 +  ENSURE_INDEX_VALUE(aParamIndex, mResultColumnCount);
   1.696 +
   1.697 +  _declType.Assign(::sqlite3_column_decltype(mDBStatement, aParamIndex));
   1.698 +  return NS_OK;
   1.699 +}
   1.700 +
   1.701 +////////////////////////////////////////////////////////////////////////////////
   1.702 +//// mozIStorageValueArray (now part of mozIStorageStatement too)
   1.703 +
   1.704 +NS_IMETHODIMP
   1.705 +Statement::GetNumEntries(uint32_t *_length)
   1.706 +{
   1.707 +  *_length = mResultColumnCount;
   1.708 +  return NS_OK;
   1.709 +}
   1.710 +
   1.711 +NS_IMETHODIMP
   1.712 +Statement::GetTypeOfIndex(uint32_t aIndex,
   1.713 +                          int32_t *_type)
   1.714 +{
   1.715 +  if (!mDBStatement)
   1.716 +    return NS_ERROR_NOT_INITIALIZED;
   1.717 +
   1.718 +  ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
   1.719 +
   1.720 +  if (!mExecuting)
   1.721 +    return NS_ERROR_UNEXPECTED;
   1.722 +
   1.723 +  int t = ::sqlite3_column_type(mDBStatement, aIndex);
   1.724 +  switch (t) {
   1.725 +    case SQLITE_INTEGER:
   1.726 +      *_type = mozIStorageStatement::VALUE_TYPE_INTEGER;
   1.727 +      break;
   1.728 +    case SQLITE_FLOAT:
   1.729 +      *_type = mozIStorageStatement::VALUE_TYPE_FLOAT;
   1.730 +      break;
   1.731 +    case SQLITE_TEXT:
   1.732 +      *_type = mozIStorageStatement::VALUE_TYPE_TEXT;
   1.733 +      break;
   1.734 +    case SQLITE_BLOB:
   1.735 +      *_type = mozIStorageStatement::VALUE_TYPE_BLOB;
   1.736 +      break;
   1.737 +    case SQLITE_NULL:
   1.738 +      *_type = mozIStorageStatement::VALUE_TYPE_NULL;
   1.739 +      break;
   1.740 +    default:
   1.741 +      return NS_ERROR_FAILURE;
   1.742 +  }
   1.743 +
   1.744 +  return NS_OK;
   1.745 +}
   1.746 +
   1.747 +NS_IMETHODIMP
   1.748 +Statement::GetInt32(uint32_t aIndex,
   1.749 +                    int32_t *_value)
   1.750 +{
   1.751 +  if (!mDBStatement)
   1.752 +    return NS_ERROR_NOT_INITIALIZED;
   1.753 +
   1.754 +  ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
   1.755 +
   1.756 +  if (!mExecuting)
   1.757 +    return NS_ERROR_UNEXPECTED;
   1.758 +
   1.759 +  *_value = ::sqlite3_column_int(mDBStatement, aIndex);
   1.760 +  return NS_OK;
   1.761 +}
   1.762 +
   1.763 +NS_IMETHODIMP
   1.764 +Statement::GetInt64(uint32_t aIndex,
   1.765 +                    int64_t *_value)
   1.766 +{
   1.767 +  if (!mDBStatement)
   1.768 +    return NS_ERROR_NOT_INITIALIZED;
   1.769 +
   1.770 +  ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
   1.771 +
   1.772 +  if (!mExecuting)
   1.773 +    return NS_ERROR_UNEXPECTED;
   1.774 +
   1.775 +  *_value = ::sqlite3_column_int64(mDBStatement, aIndex);
   1.776 +
   1.777 +  return NS_OK;
   1.778 +}
   1.779 +
   1.780 +NS_IMETHODIMP
   1.781 +Statement::GetDouble(uint32_t aIndex,
   1.782 +                     double *_value)
   1.783 +{
   1.784 +  if (!mDBStatement)
   1.785 +    return NS_ERROR_NOT_INITIALIZED;
   1.786 +
   1.787 +  ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
   1.788 +
   1.789 +  if (!mExecuting)
   1.790 +    return NS_ERROR_UNEXPECTED;
   1.791 +
   1.792 +  *_value = ::sqlite3_column_double(mDBStatement, aIndex);
   1.793 +
   1.794 +  return NS_OK;
   1.795 +}
   1.796 +
   1.797 +NS_IMETHODIMP
   1.798 +Statement::GetUTF8String(uint32_t aIndex,
   1.799 +                         nsACString &_value)
   1.800 +{
   1.801 +  // Get type of Index will check aIndex for us, so we don't have to.
   1.802 +  int32_t type;
   1.803 +  nsresult rv = GetTypeOfIndex(aIndex, &type);
   1.804 +  NS_ENSURE_SUCCESS(rv, rv);
   1.805 +  if (type == mozIStorageStatement::VALUE_TYPE_NULL) {
   1.806 +    // NULL columns should have IsVoid set to distinguish them from the empty
   1.807 +    // string.
   1.808 +    _value.Truncate(0);
   1.809 +    _value.SetIsVoid(true);
   1.810 +  }
   1.811 +  else {
   1.812 +    const char *value =
   1.813 +      reinterpret_cast<const char *>(::sqlite3_column_text(mDBStatement,
   1.814 +                                                           aIndex));
   1.815 +    _value.Assign(value, ::sqlite3_column_bytes(mDBStatement, aIndex));
   1.816 +  }
   1.817 +  return NS_OK;
   1.818 +}
   1.819 +
   1.820 +NS_IMETHODIMP
   1.821 +Statement::GetString(uint32_t aIndex,
   1.822 +                     nsAString &_value)
   1.823 +{
   1.824 +  // Get type of Index will check aIndex for us, so we don't have to.
   1.825 +  int32_t type;
   1.826 +  nsresult rv = GetTypeOfIndex(aIndex, &type);
   1.827 +  NS_ENSURE_SUCCESS(rv, rv);
   1.828 +  if (type == mozIStorageStatement::VALUE_TYPE_NULL) {
   1.829 +    // NULL columns should have IsVoid set to distinguish them from the empty
   1.830 +    // string.
   1.831 +    _value.Truncate(0);
   1.832 +    _value.SetIsVoid(true);
   1.833 +  } else {
   1.834 +    const char16_t *value =
   1.835 +      static_cast<const char16_t *>(::sqlite3_column_text16(mDBStatement,
   1.836 +                                                             aIndex));
   1.837 +    _value.Assign(value, ::sqlite3_column_bytes16(mDBStatement, aIndex) / 2);
   1.838 +  }
   1.839 +  return NS_OK;
   1.840 +}
   1.841 +
   1.842 +NS_IMETHODIMP
   1.843 +Statement::GetBlob(uint32_t aIndex,
   1.844 +                   uint32_t *_size,
   1.845 +                   uint8_t **_blob)
   1.846 +{
   1.847 +  if (!mDBStatement)
   1.848 +    return NS_ERROR_NOT_INITIALIZED;
   1.849 +
   1.850 +  ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
   1.851 +
   1.852 +  if (!mExecuting)
   1.853 +     return NS_ERROR_UNEXPECTED;
   1.854 +
   1.855 +  int size = ::sqlite3_column_bytes(mDBStatement, aIndex);
   1.856 +  void *blob = nullptr;
   1.857 +  if (size) {
   1.858 +    blob = nsMemory::Clone(::sqlite3_column_blob(mDBStatement, aIndex), size);
   1.859 +    NS_ENSURE_TRUE(blob, NS_ERROR_OUT_OF_MEMORY);
   1.860 +  }
   1.861 +
   1.862 +  *_blob = static_cast<uint8_t *>(blob);
   1.863 +  *_size = size;
   1.864 +  return NS_OK;
   1.865 +}
   1.866 +
   1.867 +NS_IMETHODIMP
   1.868 +Statement::GetSharedUTF8String(uint32_t aIndex,
   1.869 +                               uint32_t *_length,
   1.870 +                               const char **_value)
   1.871 +{
   1.872 +  if (_length)
   1.873 +    *_length = ::sqlite3_column_bytes(mDBStatement, aIndex);
   1.874 +
   1.875 +  *_value = reinterpret_cast<const char *>(::sqlite3_column_text(mDBStatement,
   1.876 +                                                                 aIndex));
   1.877 +  return NS_OK;
   1.878 +}
   1.879 +
   1.880 +NS_IMETHODIMP
   1.881 +Statement::GetSharedString(uint32_t aIndex,
   1.882 +                           uint32_t *_length,
   1.883 +                           const char16_t **_value)
   1.884 +{
   1.885 +  if (_length)
   1.886 +    *_length = ::sqlite3_column_bytes16(mDBStatement, aIndex);
   1.887 +
   1.888 +  *_value = static_cast<const char16_t *>(::sqlite3_column_text16(mDBStatement,
   1.889 +                                                                   aIndex));
   1.890 +  return NS_OK;
   1.891 +}
   1.892 +
   1.893 +NS_IMETHODIMP
   1.894 +Statement::GetSharedBlob(uint32_t aIndex,
   1.895 +                         uint32_t *_size,
   1.896 +                         const uint8_t **_blob)
   1.897 +{
   1.898 +  *_size = ::sqlite3_column_bytes(mDBStatement, aIndex);
   1.899 +  *_blob = static_cast<const uint8_t *>(::sqlite3_column_blob(mDBStatement,
   1.900 +                                                              aIndex));
   1.901 +  return NS_OK;
   1.902 +}
   1.903 +
   1.904 +NS_IMETHODIMP
   1.905 +Statement::GetIsNull(uint32_t aIndex,
   1.906 +                     bool *_isNull)
   1.907 +{
   1.908 +  // Get type of Index will check aIndex for us, so we don't have to.
   1.909 +  int32_t type;
   1.910 +  nsresult rv = GetTypeOfIndex(aIndex, &type);
   1.911 +  NS_ENSURE_SUCCESS(rv, rv);
   1.912 +  *_isNull = (type == mozIStorageStatement::VALUE_TYPE_NULL);
   1.913 +  return NS_OK;
   1.914 +}
   1.915 +
   1.916 +////////////////////////////////////////////////////////////////////////////////
   1.917 +//// mozIStorageBindingParams
   1.918 +
   1.919 +BOILERPLATE_BIND_PROXIES(
   1.920 +  Statement, 
   1.921 +  if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
   1.922 +)
   1.923 +
   1.924 +} // namespace storage
   1.925 +} // namespace mozilla

mercurial