/** * @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; }
/** * @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; }
/** * @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; }
/** * @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; }
/** * @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; }