nsresult
Statement::internalFinalize(bool aDestructing)
{
  if (!mDBStatement)
    return NS_OK;

#ifdef PR_LOGGING
  PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Finalizing statement '%s'",
                                      ::sqlite3_sql(mDBStatement)));
#endif

  int srv = ::sqlite3_finalize(mDBStatement);
  mDBStatement = nullptr;

  if (mAsyncStatement) {
    // If the destructor called us, there are no pending async statements (they
    // hold a reference to us) and we can/must just kill the statement directly.
    if (aDestructing)
      destructorAsyncFinalize();
    else
      asyncFinalize();
  }

  // We are considered dead at this point, so any wrappers for row or params
  // need to lose their reference to us.
  if (mStatementParamsHolder) {
    nsCOMPtr<nsIXPConnectWrappedNative> wrapper =
        do_QueryInterface(mStatementParamsHolder);
    nsCOMPtr<mozIStorageStatementParams> iParams =
        do_QueryWrappedNative(wrapper);
    StatementParams *params = static_cast<StatementParams *>(iParams.get());
    params->mStatement = nullptr;
    mStatementParamsHolder = nullptr;
  }

  if (mStatementRowHolder) {
    nsCOMPtr<nsIXPConnectWrappedNative> wrapper =
        do_QueryInterface(mStatementRowHolder);
    nsCOMPtr<mozIStorageStatementRow> iRow =
        do_QueryWrappedNative(wrapper);
    StatementRow *row = static_cast<StatementRow *>(iRow.get());
    row->mStatement = nullptr;
    mStatementRowHolder = nullptr;
  }

  return convertResultCode(srv);
}
nsresult
Statement::internalFinalize(bool aDestructing)
{
  if (!mDBStatement)
    return NS_OK;

  int srv = SQLITE_OK;

  if (!mDBConnection->isClosed()) {
    //
    // The connection is still open. While statement finalization and
    // closing may, in some cases, take place in two distinct threads,
    // we have a guarantee that the connection will remain open until
    // this method terminates:
    //
    // a. The connection will be closed synchronously. In this case,
    // there is no race condition, as everything takes place on the
    // same thread.
    //
    // b. The connection is closed asynchronously and this code is
    // executed on the opener thread. In this case, asyncClose() has
    // not been called yet and will not be called before we return
    // from this function.
    //
    // c. The connection is closed asynchronously and this code is
    // executed on the async execution thread. In this case,
    // AsyncCloseConnection::Run() has not been called yet and will
    // not be called before we return from this function.
    //
    // In either case, the connection is still valid, hence closing
    // here is safe.
    //
    MOZ_LOG(gStorageLog, LogLevel::Debug, ("Finalizing statement '%s' during garbage-collection",
                                        ::sqlite3_sql(mDBStatement)));
    srv = ::sqlite3_finalize(mDBStatement);
  }
#ifdef DEBUG
  else {
    //
    // The database connection is either closed or closing. The sqlite
    // statement has either been finalized already by the connection
    // or is about to be finalized by the connection.
    //
    // Finalizing it here would be useless and segfaultish.
    //

    char *msg = ::PR_smprintf("SQL statement (%x) should have been finalized"
      " before garbage-collection. For more details on this statement, set"
      " NSPR_LOG_MESSAGES=mozStorage:5 .",
      mDBStatement);

    //
    // Note that we can't display the statement itself, as the data structure
    // is not valid anymore. However, the address shown here should help
    // developers correlate with the more complete debug message triggered
    // by AsyncClose().
    //

#if 0
    // Deactivate the warning until we have fixed the exising culprit
    // (see bug 914070).
    NS_WARNING(msg);
#endif // 0

    MOZ_LOG(gStorageLog, LogLevel::Warning, (msg));

    ::PR_smprintf_free(msg);
  }

#endif

  mDBStatement = nullptr;

  if (mAsyncStatement) {
    // If the destructor called us, there are no pending async statements (they
    // hold a reference to us) and we can/must just kill the statement directly.
    if (aDestructing)
      destructorAsyncFinalize();
    else
      asyncFinalize();
  }

  // Release the holders, so they can release the reference to us.
  mStatementParamsHolder = nullptr;
  mStatementRowHolder = nullptr;

  return convertResultCode(srv);
}