/**
 * @brief Create projected tuples based on one or two input.
 * Newly-created physical tiles are needed.
 *
 * @return true on success, false otherwise.
 */
bool ProjectionExecutor::DExecute() {
  PL_ASSERT(project_info_);
  PL_ASSERT(schema_);
  PL_ASSERT(children_.size() == 1);

  // NOTE: We only handle 1 child for now
  if (children_.size() == 1) {
    LOG_TRACE("Projection : child 1 ");

    // Execute child
    auto status = children_[0]->Execute();
    if (false == status) return false;

    // Get input from child
    std::unique_ptr<LogicalTile> source_tile(children_[0]->GetOutput());
    auto num_tuples = source_tile->GetTupleCount();

    // Create new physical tile where we store projected tuples
    std::shared_ptr<storage::Tile> dest_tile(
        storage::TileFactory::GetTempTile(*schema_, num_tuples));

    // Create projections tuple-at-a-time from original tile
    oid_t new_tuple_id = 0;
    for (oid_t old_tuple_id : *source_tile) {
      storage::Tuple *buffer = new storage::Tuple(schema_, true);
      expression::ContainerTuple<LogicalTile> tuple(source_tile.get(),
                                                    old_tuple_id);
      project_info_->Evaluate(buffer, &tuple, nullptr, executor_context_);

      // Insert projected tuple into the new tile
      dest_tile.get()->InsertTuple(new_tuple_id, buffer);

      delete buffer;
      new_tuple_id++;
    }

    // Wrap physical tile in logical tile and return it
    SetOutput(LogicalTileFactory::WrapTiles({dest_tile}));

    return true;
  }

  return false;
}
/**
 * @brief Creates materialized physical tile from logical tile and wraps it
 *        in a new logical tile.
 *
 * @return true on success, false otherwise.
 */
bool MaterializationExecutor::DExecute() {
  // Retrieve child tile.
  const bool success = children_[0]->Execute();
  if (!success) {
    return false;
  }

  std::unique_ptr<LogicalTile> source_tile(children_[0]->GetOutput());
  LogicalTile *output_tile = nullptr;

  // Check the number of tuples in input logical tile
  // If none, then just return false
  const int num_tuples = source_tile->GetTupleCount();
  if (num_tuples == 0) {
    return false;
  }

  auto node = GetRawNode();
  bool physify_flag = true;  // by default, we create a physical tile

  if (node != nullptr) {
    const planner::MaterializationPlan &node =
        GetPlanNode<planner::MaterializationPlan>();
    physify_flag = node.GetPhysifyFlag();
  }

  if (physify_flag) {
    /* create a physical tile and a logical tile wrapper to be the output */
    output_tile = Physify(source_tile.get());
  } else {
    /* just pass thru the underlying logical tile */
    output_tile = source_tile.release();
  }

  SetOutput(output_tile);

  return true;
}
Example #3
0
/**
 * @brief Delete the table tuples using the position list in the logical tile.
 *
 * If truncate is on, then it will truncate the table itself.
 * @return true on success, false otherwise.
 */
bool DeleteExecutor::DExecute() {
  PL_ASSERT(target_table_);
  // Retrieve next tile.
  if (!children_[0]->Execute()) {
    return false;
  }

  std::unique_ptr<LogicalTile> source_tile(children_[0]->GetOutput());

  auto &pos_lists = source_tile.get()->GetPositionLists();

  auto &transaction_manager =
      concurrency::TransactionManagerFactory::GetInstance();

  auto current_txn = executor_context_->GetTransaction();

  LOG_TRACE("Source tile : %p Tuples : %lu ", source_tile.get(),
            source_tile->GetTupleCount());

  LOG_TRACE("Source tile info: %s", source_tile->GetInfo().c_str());

  LOG_TRACE("Transaction ID: %" PRId64,
            executor_context_->GetTransaction()->GetTransactionId());

  auto executor_pool = executor_context_->GetPool();
  auto target_table_schema = target_table_->GetSchema();
  auto column_count = target_table_schema->GetColumnCount();

  trigger::TriggerList *trigger_list = target_table_->GetTriggerList();
  if (trigger_list != nullptr) {
    LOG_TRACE("size of trigger list in target table: %d",
              trigger_list->GetTriggerListSize());
    if (trigger_list->HasTriggerType(TriggerType::BEFORE_DELETE_STATEMENT)) {
      LOG_TRACE("target table has per-statement-before-delete triggers!");
      trigger_list->ExecTriggers(TriggerType::BEFORE_DELETE_STATEMENT,
                                 current_txn);
    }
  }

  // Delete each tuple
  for (oid_t visible_tuple_id : *source_tile) {
    storage::TileGroup *tile_group =
        source_tile->GetBaseTile(0)->GetTileGroup();
    storage::TileGroupHeader *tile_group_header = tile_group->GetHeader();

    oid_t physical_tuple_id = pos_lists[0][visible_tuple_id];

    ItemPointer old_location(tile_group->GetTileGroupId(), physical_tuple_id);

    LOG_TRACE("Visible Tuple id : %u, Physical Tuple id : %u ",
              visible_tuple_id, physical_tuple_id);

    // if running at snapshot isolation,
    // then we need to retrieve the latest version of this tuple.
    if (current_txn->GetIsolationLevel() == IsolationLevelType::SNAPSHOT) {
      old_location = *(tile_group_header->GetIndirection(physical_tuple_id));

      auto &manager = catalog::Manager::GetInstance();
      tile_group = manager.GetTileGroup(old_location.block).get();
      tile_group_header = tile_group->GetHeader();

      physical_tuple_id = old_location.offset;
    }

    ContainerTuple<storage::TileGroup> old_tuple(tile_group, physical_tuple_id);
    storage::Tuple prev_tuple(target_table_->GetSchema(), true);

    // Get a copy of the old tuple
    for (oid_t column_itr = 0; column_itr < target_table_schema->GetColumnCount(); column_itr++) {
      type::Value val = (old_tuple.GetValue(column_itr));
      prev_tuple.SetValue(column_itr, val, executor_context_->GetPool());
    }

    // Check the foreign key source table
    if (target_table_->CheckForeignKeySrcAndCascade(&prev_tuple,
                                                    nullptr,
                                                    current_txn,
                                                    executor_context_,
                                                    false) == false)
    {
      transaction_manager.SetTransactionResult(current_txn,
                                              peloton::ResultType::FAILURE);
      return false;
    }

    bool is_owner = transaction_manager.IsOwner(current_txn, tile_group_header,
                                                physical_tuple_id);

    bool is_written = transaction_manager.IsWritten(
        current_txn, tile_group_header, physical_tuple_id);

    // if the current transaction is the creator of this version.
    // which means the current transaction has already updated the version.

    std::unique_ptr<storage::Tuple> real_tuple(
        new storage::Tuple(target_table_schema, true));
    bool tuple_is_materialzed = false;

    // check whether there are per-row-before-delete triggers on this table
    // using trigger catalog
    if (trigger_list != nullptr) {
      LOG_TRACE("size of trigger list in target table: %d",
                trigger_list->GetTriggerListSize());
      if (trigger_list->HasTriggerType(TriggerType::BEFORE_DELETE_ROW)) {
        ContainerTuple<LogicalTile> logical_tile_tuple(source_tile.get(),
                                                       visible_tuple_id);
        // Materialize the logical tile tuple
        for (oid_t column_itr = 0; column_itr < column_count; column_itr++) {
          type::Value val = (logical_tile_tuple.GetValue(column_itr));
          real_tuple->SetValue(column_itr, val, executor_pool);
        }
        tuple_is_materialzed = true;
        LOG_TRACE("target table has per-row-before-delete triggers!");
        trigger_list->ExecTriggers(TriggerType::BEFORE_DELETE_ROW, current_txn,
                                   real_tuple.get(), executor_context_);
      }
    }

    if (is_owner == true && is_written == true) {
      // if the transaction is the owner of the tuple, then directly update in
      // place.
      LOG_TRACE("The current transaction is the owner of the tuple");
      transaction_manager.PerformDelete(current_txn, old_location);
    } else {
      bool is_ownable = is_owner ||
                        transaction_manager.IsOwnable(
                            current_txn, tile_group_header, physical_tuple_id);

      if (is_ownable == true) {
        // if the tuple is not owned by any transaction and is visible to
        // current transaction.
        LOG_TRACE("Thread is not the owner of the tuple, but still visible");

        bool acquire_ownership_success =
            is_owner ||
            transaction_manager.AcquireOwnership(current_txn, tile_group_header,
                                                 physical_tuple_id);

        if (acquire_ownership_success == false) {
          transaction_manager.SetTransactionResult(current_txn,
                                                   ResultType::FAILURE);
          return false;
        }
        // if it is the latest version and not locked by other threads, then
        // insert an empty version.
        ItemPointer new_location = target_table_->InsertEmptyVersion();

        // PerformDelete() will not be executed if the insertion failed.
        // There is a write lock acquired, but since it is not in the write set,
        // because we haven't yet put them into the write set.
        // the acquired lock can't be released when the txn is aborted.
        // the YieldOwnership() function helps us release the acquired write
        // lock.
        if (new_location.IsNull() == true) {
          LOG_TRACE("Fail to insert new tuple. Set txn failure.");
          if (is_owner == false) {
            // If the ownership is acquire inside this update executor, we
            // release it here
            transaction_manager.YieldOwnership(current_txn, tile_group_header,
                                               physical_tuple_id);
          }
          transaction_manager.SetTransactionResult(current_txn,
                                                   ResultType::FAILURE);
          return false;
        }
        transaction_manager.PerformDelete(current_txn, old_location,
                                          new_location);

        executor_context_->num_processed += 1;  // deleted one
      } else {
        // transaction should be aborted as we cannot update the latest version.
        LOG_TRACE("Fail to update tuple. Set txn failure.");
        transaction_manager.SetTransactionResult(current_txn,
                                                 ResultType::FAILURE);
        return false;
      }
    }
    // execute after-delete-row triggers and
    // record on-commit-delete-row triggers into current transaction
    if (trigger_list != nullptr) {
      LOG_TRACE("size of trigger list in target table: %d",
                trigger_list->GetTriggerListSize());
      if (trigger_list->HasTriggerType(TriggerType::AFTER_DELETE_ROW) ||
          trigger_list->HasTriggerType(TriggerType::ON_COMMIT_DELETE_ROW)) {
        if (!tuple_is_materialzed) {
          ContainerTuple<LogicalTile> logical_tile_tuple(source_tile.get(),
                                                         visible_tuple_id);
          // Materialize the logical tile tuple
          for (oid_t column_itr = 0; column_itr < column_count; column_itr++) {
            type::Value val = (logical_tile_tuple.GetValue(column_itr));
            real_tuple->SetValue(column_itr, val, executor_pool);
          }
        }
        if (trigger_list->HasTriggerType(TriggerType::AFTER_DELETE_ROW)) {
          LOG_TRACE("target table has per-row-after-delete triggers!");
          trigger_list->ExecTriggers(TriggerType::AFTER_DELETE_ROW, current_txn,
                                     real_tuple.get(), executor_context_);
        }
        if (trigger_list->HasTriggerType(TriggerType::ON_COMMIT_DELETE_ROW)) {
          LOG_TRACE("target table has per-row-on-commit-delete triggers!");
          trigger_list->ExecTriggers(TriggerType::ON_COMMIT_DELETE_ROW,
                                     current_txn, real_tuple.get(),
                                     executor_context_);
        }
      }
    }
  }
  // execute after-delete-statement triggers and
  // record on-commit-delete-statement triggers into current transaction
  if (trigger_list != nullptr) {
    LOG_TRACE("size of trigger list in target table: %d",
              trigger_list->GetTriggerListSize());
    if (trigger_list->HasTriggerType(TriggerType::AFTER_DELETE_STATEMENT)) {
      LOG_TRACE("target table has per-statement-after-delete triggers!");
      trigger_list->ExecTriggers(TriggerType::AFTER_DELETE_STATEMENT,
                                 current_txn);
    }
    if (trigger_list->HasTriggerType(TriggerType::ON_COMMIT_DELETE_STATEMENT)) {
      LOG_TRACE("target table has per-statement-on-commit-delete triggers!");
      trigger_list->ExecTriggers(TriggerType::ON_COMMIT_DELETE_STATEMENT,
                                 current_txn);
    }
  }
  return true;
}
/**
 * @brief updates a set of columns
 * @return true on success, false otherwise.
 */
bool UpdateExecutor::DExecute() {
  PL_ASSERT(children_.size() == 1);
  PL_ASSERT(executor_context_);

  // We are scanning over a logical tile.
  LOG_TRACE("Update executor :: 1 child ");

  if (!children_[0]->Execute()) {
    return false;
  }

  std::unique_ptr<LogicalTile> source_tile(children_[0]->GetOutput());

  auto &pos_lists = source_tile.get()->GetPositionLists();
  storage::Tile *tile = source_tile->GetBaseTile(0);
  storage::TileGroup *tile_group = tile->GetTileGroup();
  storage::TileGroupHeader *tile_group_header = tile_group->GetHeader();
  auto tile_group_id = tile_group->GetTileGroupId();

  auto &transaction_manager =
      concurrency::TransactionManagerFactory::GetInstance();

  // Update tuples in given table
  for (oid_t visible_tuple_id : *source_tile) {
    oid_t physical_tuple_id = pos_lists[0][visible_tuple_id];

    ItemPointer old_location(tile_group_id, physical_tuple_id);

    LOG_TRACE("Visible Tuple id : %u, Physical Tuple id : %u ",
              visible_tuple_id, physical_tuple_id);

    if (transaction_manager.IsOwner(tile_group_header, physical_tuple_id) ==
        true) {
      // Make a copy of the original tuple and allocate a new tuple
      expression::ContainerTuple<storage::TileGroup> old_tuple(
          tile_group, physical_tuple_id);
      // Create a temp copy
      std::unique_ptr<storage::Tuple> new_tuple(
          new storage::Tuple(target_table_->GetSchema(), true));
      // Execute the projections
      // FIXME: reduce memory copy by doing inplace update
      project_info_->Evaluate(new_tuple.get(), &old_tuple, nullptr,
                              executor_context_);


      // Current rb segment is OK, just overwrite the tuple in place
      tile_group->CopyTuple(new_tuple.get(), physical_tuple_id);
      transaction_manager.PerformUpdate(old_location);

    } else if (transaction_manager.IsOwnable(tile_group_header,
                                             physical_tuple_id) == true) {
      // if the tuple is not owned by any transaction and is visible to current
      // transaction.

      if (transaction_manager.AcquireOwnership(tile_group_header, tile_group_id,
                                               physical_tuple_id) == false) {
        LOG_TRACE("Fail to insert new tuple. Set txn failure.");
        transaction_manager.SetTransactionResult(Result::RESULT_FAILURE);
        return false;
      }
      // if it is the latest version and not locked by other threads, then
      // insert a new version.
      std::unique_ptr<storage::Tuple> new_tuple(
          new storage::Tuple(target_table_->GetSchema(), true));

      // Make a copy of the original tuple and allocate a new tuple
      expression::ContainerTuple<storage::TileGroup> old_tuple(
          tile_group, physical_tuple_id);
      // Execute the projections
      project_info_->Evaluate(new_tuple.get(), &old_tuple, nullptr,
                              executor_context_);

      // finally insert updated tuple into the table
      ItemPointer new_location =
          target_table_->InsertVersion(new_tuple.get());

      // FIXME: PerformUpdate() will not be executed if the insertion failed,
      // There is a write lock acquired, but since it is not in the write set,
      // because we haven't yet put them into the write set.
      // the acquired lock can't be released when the txn is aborted.
      if (new_location.IsNull() == true) {
        LOG_TRACE("Fail to insert new tuple. Set txn failure.");
        transaction_manager.SetTransactionResult(Result::RESULT_FAILURE);
        return false;
      }

      LOG_TRACE("perform update old location: %u, %u", old_location.block,
                old_location.offset);
      LOG_TRACE("perform update new location: %u, %u", new_location.block,
                new_location.offset);
      transaction_manager.PerformUpdate(old_location, new_location);


      // TODO: Why don't we also do this in the if branch above?
      executor_context_->num_processed += 1;  // updated one
    } else {
      // transaction should be aborted as we cannot update the latest version.
      LOG_TRACE("Fail to update tuple. Set txn failure.");
      transaction_manager.SetTransactionResult(Result::RESULT_FAILURE);
      return false;
    }
  }
  return true;
}
Example #5
0
/**
 * @brief Delete the table tuples using the position list in the logical tile.
 *
 * If truncate is on, then it will truncate the table itself.
 * @return true on success, false otherwise.
 */
bool DeleteExecutor::DExecute() {
  assert(target_table_);

  // Retrieve next tile.
  const bool success = children_[0]->Execute();
  if (!success) {
    return false;
  }

  std::unique_ptr<LogicalTile> source_tile(children_[0]->GetOutput());

  storage::Tile *tile = source_tile->GetBaseTile(0);
  storage::TileGroup *tile_group = tile->GetTileGroup();

  auto &pos_lists = source_tile.get()->GetPositionLists();
  auto tile_group_id = tile_group->GetTileGroupId();
  auto transaction_ = executor_context_->GetTransaction();

  LOG_INFO("Source tile : %p Tuples : %lu \n", source_tile.get(),
           source_tile->GetTupleCount());

  LOG_INFO("Transaction ID: %lu\n", transaction_->GetTransactionId());

  // Delete each tuple
  for (oid_t visible_tuple_id : *source_tile) {
    oid_t physical_tuple_id = pos_lists[0][visible_tuple_id];

    LOG_INFO("Visible Tuple id : %lu, Physical Tuple id : %lu \n",
             visible_tuple_id, physical_tuple_id);

    peloton::ItemPointer delete_location(tile_group_id, physical_tuple_id);

    // Logging
    {
      auto &log_manager = logging::LogManager::GetInstance();

      if (log_manager.IsInLoggingMode()) {
        auto logger = log_manager.GetBackendLogger();
        auto record = logger->GetTupleRecord(
            LOGRECORD_TYPE_TUPLE_DELETE, transaction_->GetTransactionId(),
            target_table_->GetOid(), INVALID_ITEMPOINTER, delete_location);

        logger->Log(record);
      }
    }

    // try to delete the tuple
    // this might fail due to a concurrent operation that has latched the tuple
    bool status = target_table_->DeleteTuple(transaction_, delete_location);

    if (status == false) {
      LOG_INFO("Fail to delete. Set txn failure");
      transaction_->SetResult(peloton::Result::RESULT_FAILURE);
      return false;
    }

    executor_context_->num_processed += 1;  // deleted one
    transaction_->RecordDelete(delete_location);
  }

  return true;
}
Example #6
0
/**
 * @brief updates a set of columns
 * @return true on success, false otherwise.
 */
bool UpdateExecutor::DExecute() {
  assert(children_.size() == 1);
  assert(executor_context_);

  // We are scanning over a logical tile.
  LOG_TRACE("Update executor :: 1 child ");

  if (!children_[0]->Execute()) {
    return false;
  }

  std::unique_ptr<LogicalTile> source_tile(children_[0]->GetOutput());

  auto &pos_lists = source_tile.get()->GetPositionLists();
  storage::Tile *tile = source_tile->GetBaseTile(0);
  storage::TileGroup *tile_group = tile->GetTileGroup();
  storage::TileGroupHeader *tile_group_header = tile_group->GetHeader();
  auto tile_group_id = tile_group->GetTileGroupId();

  auto &transaction_manager =
      concurrency::TransactionManagerFactory::GetInstance();

  // Update tuples in given table
  for (oid_t visible_tuple_id : *source_tile) {
    oid_t physical_tuple_id = pos_lists[0][visible_tuple_id];
    LOG_TRACE("Visible Tuple id : %lu, Physical Tuple id : %lu ",
              visible_tuple_id, physical_tuple_id);

    if (transaction_manager.IsOwner(tile_group_header, physical_tuple_id) == true) {
      // if the thread is the owner of the tuple, then directly update in place.
      storage::Tuple *new_tuple =
          new storage::Tuple(target_table_->GetSchema(), true);
      // Make a copy of the original tuple and allocate a new tuple
      expression::ContainerTuple<storage::TileGroup> old_tuple(
          tile_group, physical_tuple_id);
      // Execute the projections
      project_info_->Evaluate(new_tuple, &old_tuple, nullptr,
                              executor_context_);
      tile_group->CopyTuple(new_tuple, physical_tuple_id);

      transaction_manager.PerformUpdate(tile_group_id, physical_tuple_id);
      delete new_tuple;
      new_tuple = nullptr;

    } else if (transaction_manager.IsOwnable(tile_group_header,
                                                physical_tuple_id) == true) {
      // if the tuple is not owned by any transaction and is visible to current
      // transaction.

      if (transaction_manager.AcquireOwnership(tile_group_header, tile_group_id, physical_tuple_id) ==
          false) {
        LOG_TRACE("Fail to insert new tuple. Set txn failure.");
        transaction_manager.SetTransactionResult(Result::RESULT_FAILURE);
        return false;
      }
      // if it is the latest version and not locked by other threads, then
      // insert a new version.
      storage::Tuple *new_tuple =
          new storage::Tuple(target_table_->GetSchema(), true);

      // Make a copy of the original tuple and allocate a new tuple
      expression::ContainerTuple<storage::TileGroup> old_tuple(
          tile_group, physical_tuple_id);
      // Execute the projections
      project_info_->Evaluate(new_tuple, &old_tuple, nullptr,
                              executor_context_);

      // finally insert updated tuple into the table
      ItemPointer location = target_table_->InsertVersion(new_tuple);

      if (location.IsNull() == true) {
        delete new_tuple;
        new_tuple = nullptr;
        LOG_TRACE("Fail to insert new tuple. Set txn failure.");
        transaction_manager.SetTransactionResult(Result::RESULT_FAILURE);
        return false;
      }
      transaction_manager.PerformUpdate(tile_group_id, physical_tuple_id,
                                        location);

      executor_context_->num_processed += 1;  // updated one

      delete new_tuple;
      new_tuple = nullptr;
    } else {
      // transaction should be aborted as we cannot update the latest version.
      LOG_TRACE("Fail to update tuple. Set txn failure.");
      transaction_manager.SetTransactionResult(Result::RESULT_FAILURE);
      return false;
    }
  }
  return true;
}
Example #7
0
/**
 * @brief Delete the table tuples using the position list in the logical tile.
 *
 * If truncate is on, then it will truncate the table itself.
 * @return true on success, false otherwise.
 */
bool DeleteExecutor::DExecute() {
  PL_ASSERT(target_table_);
  // Retrieve next tile.
  if (!children_[0]->Execute()) {
    return false;
  }

  std::unique_ptr<LogicalTile> source_tile(children_[0]->GetOutput());

  auto &pos_lists = source_tile.get()->GetPositionLists();
  storage::Tile *tile = source_tile->GetBaseTile(0);
  storage::TileGroup *tile_group = tile->GetTileGroup();
  storage::TileGroupHeader *tile_group_header = tile_group->GetHeader();
  auto tile_group_id = tile_group->GetTileGroupId();

  auto &transaction_manager =
      concurrency::TransactionManagerFactory::GetInstance();

  auto current_txn = executor_context_->GetTransaction();

  LOG_TRACE("Source tile : %p Tuples : %lu ", source_tile.get(),
            source_tile->GetTupleCount());

  LOG_TRACE("Source tile info: %s", source_tile->GetInfo().c_str());

  LOG_TRACE("Transaction ID: %lu",
            executor_context_->GetTransaction()->GetTransactionId());

  // Delete each tuple
  for (oid_t visible_tuple_id : *source_tile) {
    oid_t physical_tuple_id = pos_lists[0][visible_tuple_id];

    ItemPointer old_location(tile_group_id, physical_tuple_id);

    LOG_TRACE("Visible Tuple id : %u, Physical Tuple id : %u ",
              visible_tuple_id, physical_tuple_id);

    bool is_owner = 
        transaction_manager.IsOwner(current_txn, tile_group_header, physical_tuple_id);
    bool is_written =
      transaction_manager.IsWritten(current_txn, tile_group_header, physical_tuple_id);
    PL_ASSERT((is_owner == false && is_written == true) == false);

    if (is_owner == true && is_written == true) {
      // if the thread is the owner of the tuple, then directly update in place.
      LOG_TRACE("Thread is owner of the tuple");
      transaction_manager.PerformDelete(current_txn, old_location);

    } else {

      bool is_ownable = is_owner ||
          transaction_manager.IsOwnable(current_txn, tile_group_header, physical_tuple_id);

      if (is_ownable == true) {
        // if the tuple is not owned by any transaction and is visible to current
        // transaction.
      	LOG_TRACE("Thread is not the owner of the tuple, but still visible");

        bool acquire_ownership_success = is_owner ||
            transaction_manager.AcquireOwnership(current_txn, tile_group_header, physical_tuple_id);


        if (acquire_ownership_success == false) {
          transaction_manager.SetTransactionResult(current_txn, Result::RESULT_FAILURE);
          return false;
        }
        // if it is the latest version and not locked by other threads, then
        // insert an empty version.
        ItemPointer new_location = target_table_->InsertEmptyVersion();

        // PerformUpdate() will not be executed if the insertion failed.
        // There is a write lock acquired, but since it is not in the write set,
        // because we haven't yet put them into the write set.
        // the acquired lock can't be released when the txn is aborted.
        // the YieldOwnership() function helps us release the acquired write lock.
        if (new_location.IsNull() == true) {
          LOG_TRACE("Fail to insert new tuple. Set txn failure.");
          if (is_owner == false) {
            // If the ownership is acquire inside this update executor, we release it here
            transaction_manager.YieldOwnership(current_txn, tile_group_id, physical_tuple_id);
          }
          transaction_manager.SetTransactionResult(current_txn, Result::RESULT_FAILURE);
          return false;
        }
        transaction_manager.PerformDelete(current_txn, old_location, new_location);

        executor_context_->num_processed += 1;  // deleted one

      } else {
        // transaction should be aborted as we cannot update the latest version.
        LOG_TRACE("Fail to update tuple. Set txn failure.");
        transaction_manager.SetTransactionResult(current_txn, Result::RESULT_FAILURE);
        return false;
      }
    }
  }

  return true;
}