std::vector<std::vector<Value>> ExecuteTest(executor::AbstractExecutor* executor) { time_point_ start, end; bool status = false; // Run all the executors status = executor->Init(); if (status == false) { throw Exception("Init failed"); } std::vector<std::vector<Value>> logical_tile_values; // Execute stuff while (executor->Execute() == true) { std::unique_ptr<executor::LogicalTile> result_tile( executor->GetOutput()); if(result_tile == nullptr) break; auto column_count = result_tile->GetColumnCount(); for (oid_t tuple_id : *result_tile) { expression::ContainerTuple<executor::LogicalTile> cur_tuple(result_tile.get(), tuple_id); std::vector<Value> tuple_values; for (oid_t column_itr = 0; column_itr < column_count; column_itr++){ auto value = cur_tuple.GetValue(column_itr); tuple_values.push_back(value); } // Move the tuple list logical_tile_values.push_back(std::move(tuple_values)); } } return std::move(logical_tile_values); }
void Table::build_keys( vector<string> key_list ) { // construct file pat string path( "./data/" ); path.append( name ); path.append( ".tbl" ); ifstream fs( path.c_str() ); if ( !fs.is_open() ) { cout << path << endl; cerr << "ERROR: file does not exist in Table::build_keys(). " << endl; return; } for ( int i = 0; i < key_list.size(); i++ ) { if( has_index_file( key_list[i] ) ) { build_key_from_file( key_list[i] ); continue; } int pos = get_col_pos( key_list[i] ); streampos offset = 0; BTree * btree = new BTree ( 5, 6 ); string line; getline( fs, line ); // get rid of the first line offset = fs.tellg(); while ( getline ( fs, line ) && line.size() > 0) { // to ignore the deleted lines //cout << line << endl; if( line[0] == '@' ) { //cout << line << endl; offset = fs.tellg(); continue; } string entry = get_entry( line, pos ); if ( is_number ( entry ) ) { Tuple cur_tuple( atoi( entry.c_str() ), offset ); btree->insert( cur_tuple ); } else { Tuple cur_tuple( entry.c_str() , offset ); btree->insert( cur_tuple ); } offset = fs.tellg(); } keys.insert( pair<string, BTree*>( key_list[i], btree ) ); fs.clear(); fs.seekg( fs.beg ); BTreeNode * node = btree->get_first(); dump_tree( key_list[i], node ); } fs.close(); }
/** * @brief Creates logical tile(s) wrapping the results of aggregation. * @return true on success, false otherwise. */ bool AggregateExecutor::DExecute() { // Already performed the aggregation if (done) { if (result_itr == INVALID_OID || result_itr == result.size()) { return false; } else { // Return appropriate tile and go to next tile SetOutput(result[result_itr]); result_itr++; return true; } } // Grab info from plan node const planner::AggregatePlan &node = GetPlanNode<planner::AggregatePlan>(); // Get an aggregator std::unique_ptr<AbstractAggregator> aggregator(nullptr); // Get input tiles and aggregate them while (children_[0]->Execute() == true) { std::unique_ptr<LogicalTile> tile(children_[0]->GetOutput()); if (nullptr == aggregator.get()) { // Initialize the aggregator switch (node.GetAggregateStrategy()) { case AGGREGATE_TYPE_HASH: LOG_TRACE("Use HashAggregator"); aggregator.reset(new HashAggregator( &node, output_table, executor_context_, tile->GetColumnCount())); break; case AGGREGATE_TYPE_SORTED: LOG_TRACE("Use SortedAggregator"); aggregator.reset(new SortedAggregator( &node, output_table, executor_context_, tile->GetColumnCount())); break; case AGGREGATE_TYPE_PLAIN: LOG_TRACE("Use PlainAggregator"); aggregator.reset( new PlainAggregator(&node, output_table, executor_context_)); break; default: LOG_ERROR("Invalid aggregate type. Return."); return false; } } LOG_TRACE("Looping over tile.."); for (oid_t tuple_id : *tile) { std::unique_ptr<expression::ContainerTuple<LogicalTile>> cur_tuple( new expression::ContainerTuple<LogicalTile>(tile.get(), tuple_id)); if (aggregator->Advance(cur_tuple.get()) == false) { return false; } } LOG_TRACE("Finished processing logical tile"); } LOG_TRACE("Finalizing.."); if (!aggregator.get() || !aggregator->Finalize()) { // If there's no tuples and no group-by, count() aggregations should return // 0 according to the test in MySQL. // TODO: We only checked whether all AggTerms are counts here. If there're // mixed terms, we should return 0 for counts and null for others. bool all_count_aggs = true; for (oid_t aggno = 0; aggno < node.GetUniqueAggTerms().size(); aggno++) { auto agg_type = node.GetUniqueAggTerms()[aggno].aggtype; if (agg_type != EXPRESSION_TYPE_AGGREGATE_COUNT && agg_type != EXPRESSION_TYPE_AGGREGATE_COUNT_STAR) all_count_aggs = false; } // If there's no tuples in the table and only if no group-by in the // query, // we should return a NULL tuple // this is required by SQL if (!aggregator.get() && node.GetGroupbyColIds().empty()) { LOG_TRACE( "No tuples received and no group-by. Should insert a NULL tuple " "here."); std::unique_ptr<storage::Tuple> tuple( new storage::Tuple(output_table->GetSchema(), true)); if (all_count_aggs == true) { tuple->SetAllZeros(); } else { tuple->SetAllNulls(); } auto location = output_table->InsertTuple(tuple.get()); PL_ASSERT(location.block != INVALID_OID); auto &manager = catalog::Manager::GetInstance(); auto tile_group_header = manager.GetTileGroup(location.block)->GetHeader(); tile_group_header->SetTransactionId(location.offset, INITIAL_TXN_ID); } else { done = true; return false; } } // Transform output table into result auto tile_group_count = output_table->GetTileGroupCount(); if (tile_group_count == 0) return false; for (oid_t tile_group_itr = 0; tile_group_itr < tile_group_count; tile_group_itr++) { auto tile_group = output_table->GetTileGroup(tile_group_itr); // Get the logical tiles corresponding to the given tile group auto logical_tile = LogicalTileFactory::WrapTileGroup(tile_group); result.push_back(logical_tile); } done = true; LOG_TRACE("Result tiles : %lu ", result.size()); SetOutput(result[result_itr]); result_itr++; return true; }
/** * @brief Adds a column to the logical tile, using the position lists. * @return true on success, false otherwise. */ bool InsertExecutor::DExecute() { if (done_) return false; assert(!done_); assert(executor_context_ != nullptr); const planner::InsertPlan &node = GetPlanNode<planner::InsertPlan>(); storage::DataTable *target_table_ = node.GetTable(); oid_t bulk_insert_count = node.GetBulkInsertCount(); assert(target_table_); auto transaction_ = executor_context_->GetTransaction(); auto executor_pool = executor_context_->GetExecutorContextPool(); // Inserting a logical tile. if (children_.size() == 1) { LOG_INFO("Insert executor :: 1 child \n"); if (!children_[0]->Execute()) { return false; } std::unique_ptr<LogicalTile> logical_tile(children_[0]->GetOutput()); assert(logical_tile.get() != nullptr); auto target_table_schema = target_table_->GetSchema(); auto column_count = target_table_schema->GetColumnCount(); std::unique_ptr<storage::Tuple> tuple( new storage::Tuple(target_table_schema, true)); // Go over the logical tile for (oid_t tuple_id : *logical_tile) { expression::ContainerTuple<LogicalTile> cur_tuple(logical_tile.get(), tuple_id); // Materialize the logical tile tuple for (oid_t column_itr = 0; column_itr < column_count; column_itr++) tuple->SetValue(column_itr, cur_tuple.GetValue(column_itr), executor_pool); peloton::ItemPointer location = target_table_->InsertTuple(transaction_, tuple.get()); if (location.block == INVALID_OID) { transaction_->SetResult(peloton::Result::RESULT_FAILURE); return false; } transaction_->RecordInsert(location); executor_context_->num_processed += 1; // insert one } return true; } // Inserting a collection of tuples from plan node else if (children_.size() == 0) { LOG_INFO("Insert executor :: 0 child \n"); // Extract expressions from plan node and construct the tuple. // For now we just handle a single tuple auto schema = target_table_->GetSchema(); std::unique_ptr<storage::Tuple> tuple(new storage::Tuple(schema, true)); auto project_info = node.GetProjectInfo(); // There should be no direct maps assert(project_info); assert(project_info->GetDirectMapList().size() == 0); for (auto target : project_info->GetTargetList()) { peloton::Value value = target.second->Evaluate(nullptr, nullptr, executor_context_); tuple->SetValue(target.first, value, executor_pool); } // Bulk Insert Mode for (oid_t insert_itr = 0; insert_itr < bulk_insert_count; insert_itr++) { // Carry out insertion ItemPointer location = target_table_->InsertTuple(transaction_, tuple.get()); LOG_INFO("Inserted into location: %lu, %lu", location.block, location.offset); if (location.block == INVALID_OID) { LOG_INFO("Failed to Insert. Set txn failure."); transaction_->SetResult(peloton::Result::RESULT_FAILURE); return false; } transaction_->RecordInsert(location); // Logging { auto &log_manager = logging::LogManager::GetInstance(); if (log_manager.IsInLoggingMode()) { auto logger = log_manager.GetBackendLogger(); auto record = logger->GetTupleRecord( LOGRECORD_TYPE_TUPLE_INSERT, transaction_->GetTransactionId(), target_table_->GetOid(), location, INVALID_ITEMPOINTER, tuple.get()); logger->Log(record); } } } executor_context_->num_processed += 1; // insert one done_ = true; return true; } return true; }
/** * @brief Creates logical tile(s) wrapping the results of aggregation. * @return true on success, false otherwise. */ bool AggregateExecutor::DExecute() { // Already performed the aggregation if (done) { if (result_itr == INVALID_OID || result_itr == result.size()) { return false; } else { // Return appropriate tile and go to next tile SetOutput(result[result_itr]); result_itr++; return true; } } // Grab info from plan node const planner::AggregatePlan &node = GetPlanNode<planner::AggregatePlan>(); // Get an aggregator std::unique_ptr<AbstractAggregator> aggregator(nullptr); // Get input tiles and aggregate them while (children_[0]->Execute() == true) { std::unique_ptr<LogicalTile> tile(children_[0]->GetOutput()); if (nullptr == aggregator.get()) { // Initialize the aggregator switch (node.GetAggregateStrategy()) { case AGGREGATE_TYPE_HASH: LOG_INFO("Use HashAggregator\n"); aggregator.reset(new HashAggregator( &node, output_table, executor_context_, tile->GetColumnCount())); break; case AGGREGATE_TYPE_SORTED: LOG_INFO("Use SortedAggregator\n"); aggregator.reset(new SortedAggregator( &node, output_table, executor_context_, tile->GetColumnCount())); break; case AGGREGATE_TYPE_PLAIN: LOG_INFO("Use PlainAggregator\n"); aggregator.reset( new PlainAggregator(&node, output_table, executor_context_)); break; default: LOG_ERROR("Invalid aggregate type. Return.\n"); return false; } } LOG_INFO("Looping over tile.."); for (oid_t tuple_id : *tile) { std::unique_ptr<expression::ContainerTuple<LogicalTile>> cur_tuple( new expression::ContainerTuple<LogicalTile>(tile.get(), tuple_id)); if (aggregator->Advance(cur_tuple.get()) == false) { return false; } } LOG_TRACE("Finished processing logical tile"); } LOG_INFO("Finalizing.."); if (!aggregator.get() || !aggregator->Finalize()) { // If there's no tuples in the table and only if no group-by in the query, // we should return a NULL tuple // this is required by SQL if (!aggregator.get() && node.GetGroupbyColIds().empty()) { LOG_INFO( "No tuples received and no group-by. Should insert a NULL tuple " "here."); std::unique_ptr<storage::Tuple> tuple( new storage::Tuple(output_table->GetSchema(), true)); tuple->SetAllNulls(); output_table->InsertTuple(executor_context_->GetTransaction(), tuple.get()); } else { done = true; return false; } } // Transform output table into result auto tile_group_count = output_table->GetTileGroupCount(); if (tile_group_count == 0) return false; for (oid_t tile_group_itr = 0; tile_group_itr < tile_group_count; tile_group_itr++) { auto tile_group = output_table->GetTileGroup(tile_group_itr); // Get the logical tiles corresponding to the given tile group auto logical_tile = LogicalTileFactory::WrapTileGroup(tile_group); result.push_back(logical_tile); } done = true; LOG_INFO("Result tiles : %lu \n", result.size()); SetOutput(result[result_itr]); result_itr++; return true; }
/** * @brief Adds a column to the logical tile, using the position lists. * @return true on success, false otherwise. */ bool InsertExecutor::DExecute() { if (done_) return false; PELOTON_ASSERT(!done_); PELOTON_ASSERT(executor_context_ != nullptr); const planner::InsertPlan &node = GetPlanNode<planner::InsertPlan>(); storage::DataTable *target_table = node.GetTable(); oid_t bulk_insert_count = node.GetBulkInsertCount(); auto &transaction_manager = concurrency::TransactionManagerFactory::GetInstance(); auto current_txn = executor_context_->GetTransaction(); if (!target_table) { transaction_manager.SetTransactionResult(current_txn, peloton::ResultType::FAILURE); return false; } LOG_TRACE("Number of tuples in table before insert: %lu", target_table->GetTupleCount()); auto executor_pool = executor_context_->GetPool(); 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_INSERT_STATEMENT)) { LOG_TRACE("target table has per-statement-before-insert triggers!"); trigger_list->ExecTriggers(TriggerType::BEFORE_INSERT_STATEMENT, current_txn); } } // Inserting a logical tile. if (children_.size() == 1) { if (!children_[0]->Execute()) { return false; } std::unique_ptr<LogicalTile> logical_tile(children_[0]->GetOutput()); // FIXME: Wrong? What if the result of select is nothing? Michael PELOTON_ASSERT(logical_tile.get() != nullptr); auto target_table_schema = target_table->GetSchema(); auto column_count = target_table_schema->GetColumnCount(); std::unique_ptr<storage::Tuple> tuple( new storage::Tuple(target_table_schema, true)); // Go over the logical tile for (oid_t tuple_id : *logical_tile) { ContainerTuple<LogicalTile> cur_tuple(logical_tile.get(), tuple_id); // Materialize the logical tile tuple for (oid_t column_itr = 0; column_itr < column_count; column_itr++) { type::Value val = (cur_tuple.GetValue(column_itr)); tuple->SetValue(column_itr, val, executor_pool); } // insert tuple into the table. ItemPointer *index_entry_ptr = nullptr; peloton::ItemPointer location = target_table->InsertTuple(tuple.get(), current_txn, &index_entry_ptr); // it is possible that some concurrent transactions have inserted the same // tuple. // in this case, abort the transaction. if (location.block == INVALID_OID) { transaction_manager.SetTransactionResult(current_txn, peloton::ResultType::FAILURE); return false; } transaction_manager.PerformInsert(current_txn, location, index_entry_ptr); executor_context_->num_processed += 1; // insert one } // execute after-insert-statement triggers and // record on-commit-insert-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_INSERT_STATEMENT)) { LOG_TRACE("target table has per-statement-after-insert triggers!"); trigger_list->ExecTriggers(TriggerType::AFTER_INSERT_STATEMENT, current_txn); } if (trigger_list->HasTriggerType( TriggerType::ON_COMMIT_INSERT_STATEMENT)) { LOG_TRACE("target table has per-statement-on-commit-insert triggers!"); trigger_list->ExecTriggers(TriggerType::ON_COMMIT_INSERT_STATEMENT, current_txn); } } return true; } // Inserting a collection of tuples from plan node else if (children_.size() == 0) { // Extract expressions from plan node and construct the tuple. // For now we just handle a single tuple auto schema = target_table->GetSchema(); auto project_info = node.GetProjectInfo(); auto tuple = node.GetTuple(0); std::unique_ptr<storage::Tuple> storage_tuple; // Check if this is not a raw tuple if (project_info) { // Otherwise, there must exist a project info PELOTON_ASSERT(project_info); // There should be no direct maps PELOTON_ASSERT(project_info->GetDirectMapList().size() == 0); storage_tuple.reset(new storage::Tuple(schema, true)); for (auto target : project_info->GetTargetList()) { auto value = target.second.expr->Evaluate(nullptr, nullptr, executor_context_); storage_tuple->SetValue(target.first, value, executor_pool); } // Set tuple to point to temporary project tuple tuple = storage_tuple.get(); } // Bulk Insert Mode for (oid_t insert_itr = 0; insert_itr < bulk_insert_count; insert_itr++) { // if we are doing a bulk insert from values not project_info if (!project_info) { tuple = node.GetTuple(insert_itr); if (tuple == nullptr) { storage_tuple.reset(new storage::Tuple(schema, true)); // read from values uint32_t num_columns = schema->GetColumnCount(); for (uint32_t col_id = 0; col_id < num_columns; col_id++) { auto value = node.GetValue(col_id + insert_itr * num_columns); storage_tuple->SetValue(col_id, value, executor_pool); } // Set tuple to point to temporary project tuple tuple = storage_tuple.get(); } } trigger::TriggerList *trigger_list = target_table->GetTriggerList(); auto new_tuple = tuple; if (trigger_list != nullptr) { LOG_TRACE("size of trigger list in target table: %d", trigger_list->GetTriggerListSize()); if (trigger_list->HasTriggerType(TriggerType::BEFORE_INSERT_ROW)) { LOG_TRACE("target table has per-row-before-insert triggers!"); LOG_TRACE("address of the origin tuple before firing triggers: 0x%lx", long(tuple)); trigger_list->ExecTriggers(TriggerType::BEFORE_INSERT_ROW, current_txn, const_cast<storage::Tuple *>(tuple), executor_context_, nullptr, &new_tuple); LOG_TRACE("address of the new tuple after firing triggers: 0x%lx", long(new_tuple)); } } if (new_tuple == nullptr) { // trigger doesn't allow this tuple to be inserted LOG_TRACE("this tuple is rejected by trigger"); continue; } // Carry out insertion ItemPointer *index_entry_ptr = nullptr; ItemPointer location = target_table->InsertTuple(new_tuple, current_txn, &index_entry_ptr); if (new_tuple->GetColumnCount() > 2) { type::Value val = (new_tuple->GetValue(2)); LOG_TRACE("value: %s", val.GetInfo().c_str()); } if (location.block == INVALID_OID) { LOG_TRACE("Failed to Insert. Set txn failure."); transaction_manager.SetTransactionResult(current_txn, ResultType::FAILURE); return false; } transaction_manager.PerformInsert(current_txn, location, index_entry_ptr); LOG_TRACE("Number of tuples in table after insert: %lu", target_table->GetTupleCount()); executor_context_->num_processed += 1; // insert one // execute after-insert-row triggers and // record on-commit-insert-row triggers into current transaction new_tuple = tuple; if (trigger_list != nullptr) { LOG_TRACE("size of trigger list in target table: %d", trigger_list->GetTriggerListSize()); if (trigger_list->HasTriggerType(TriggerType::AFTER_INSERT_ROW)) { LOG_TRACE("target table has per-row-after-insert triggers!"); LOG_TRACE("address of the origin tuple before firing triggers: 0x%lx", long(tuple)); trigger_list->ExecTriggers(TriggerType::AFTER_INSERT_ROW, current_txn, const_cast<storage::Tuple *>(tuple), executor_context_, nullptr, &new_tuple); LOG_TRACE("address of the new tuple after firing triggers: 0x%lx", long(new_tuple)); } if (trigger_list->HasTriggerType(TriggerType::ON_COMMIT_INSERT_ROW)) { LOG_TRACE("target table has per-row-on-commit-insert triggers!"); LOG_TRACE("address of the origin tuple before firing triggers: 0x%lx", long(tuple)); trigger_list->ExecTriggers(TriggerType::ON_COMMIT_INSERT_ROW, current_txn, const_cast<storage::Tuple *>(tuple), executor_context_, nullptr, &new_tuple); LOG_TRACE("address of the new tuple after firing triggers: 0x%lx", long(new_tuple)); } } } // execute after-insert-statement triggers and // record on-commit-insert-statement triggers into current transaction 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::AFTER_INSERT_STATEMENT)) { LOG_TRACE("target table has per-statement-after-insert triggers!"); trigger_list->ExecTriggers(TriggerType::AFTER_INSERT_STATEMENT, current_txn); } if (trigger_list->HasTriggerType( TriggerType::ON_COMMIT_INSERT_STATEMENT)) { LOG_TRACE("target table has per-statement-on-commit-insert triggers!"); trigger_list->ExecTriggers(TriggerType::ON_COMMIT_INSERT_STATEMENT, current_txn); } } done_ = true; return true; } return true; }
/** * @brief Build a executor tree and execute it. * Use std::vector<Value> as params to make it more elegant for networking * Before ExecutePlan, a node first receives value list, so we should pass * value list directly rather than passing Postgres's ParamListInfo * @return status of execution. */ peloton_status PlanExecutor::ExecutePlan(const planner::AbstractPlan *plan, const std::vector<Value> ¶ms, TupleDesc tuple_desc) { peloton_status p_status; if (plan == nullptr) return p_status; LOG_TRACE("PlanExecutor Start "); bool status; bool init_failure = false; bool single_statement_txn = false; List *slots = NULL; auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); auto txn = peloton::concurrency::current_txn; // This happens for single statement queries in PG if (txn == nullptr) { single_statement_txn = true; txn = txn_manager.BeginTransaction(); } PL_ASSERT(txn); LOG_TRACE("Txn ID = %lu ", txn->GetTransactionId()); LOG_TRACE("Building the executor tree"); // Use const std::vector<Value> ¶ms to make it more elegant for network std::unique_ptr<executor::ExecutorContext> executor_context( BuildExecutorContext(params, txn)); //auto executor_context = BuildExecutorContext(param_list, txn); // Build the executor tree std::unique_ptr<executor::AbstractExecutor> executor_tree( BuildExecutorTree(nullptr, plan, executor_context.get())); LOG_TRACE("Initializing the executor tree"); // Initialize the executor tree status = executor_tree->Init(); // Abort and cleanup if (status == false) { init_failure = true; txn->SetResult(Result::RESULT_FAILURE); goto cleanup; } LOG_TRACE("Running the executor tree"); // Execute the tree until we get result tiles from root node for (;;) { status = executor_tree->Execute(); // Stop if (status == false) { break; } std::unique_ptr<executor::LogicalTile> logical_tile( executor_tree->GetOutput()); // Some executors don't return logical tiles (e.g., Update). if (logical_tile.get() == nullptr) { continue; } // Go over the logical tile for (oid_t tuple_id : *logical_tile) { expression::ContainerTuple<executor::LogicalTile> cur_tuple( logical_tile.get(), tuple_id); auto slot = TupleTransformer::GetPostgresTuple(&cur_tuple, tuple_desc); if (slot != nullptr) { slots = lappend(slots, slot); } } } // Set the result p_status.m_processed = executor_context->num_processed; p_status.m_result_slots = slots; // final cleanup cleanup: LOG_TRACE("About to commit: single stmt: %d, init_failure: %d, status: %d", single_statement_txn, init_failure, txn->GetResult()); // should we commit or abort ? if (single_statement_txn == true || init_failure == true) { auto status = txn->GetResult(); switch (status) { case Result::RESULT_SUCCESS: // Commit p_status.m_result = txn_manager.CommitTransaction(); break; case Result::RESULT_FAILURE: default: // Abort p_status.m_result = txn_manager.AbortTransaction(); } } // clean up executor tree CleanExecutorTree(executor_tree.get()); return p_status; }
void SimpleCheckpoint::Scan(storage::DataTable *target_table, oid_t database_oid) { auto schema = target_table->GetSchema(); PL_ASSERT(schema); std::vector<oid_t> column_ids; column_ids.resize(schema->GetColumnCount()); std::iota(column_ids.begin(), column_ids.end(), 0); oid_t current_tile_group_offset = START_OID; auto table_tile_group_count = target_table->GetTileGroupCount(); CheckpointTileScanner scanner; // TODO scan assigned tile in multi-thread checkpoint while (current_tile_group_offset < table_tile_group_count) { // Retrieve a tile group auto tile_group = target_table->GetTileGroup(current_tile_group_offset); // Retrieve a logical tile std::unique_ptr<executor::LogicalTile> logical_tile( scanner.Scan(tile_group, column_ids, start_commit_id_)); // Empty result if (!logical_tile) { current_tile_group_offset++; continue; } auto tile_group_id = logical_tile->GetColumnInfo(0) .base_tile->GetTileGroup() ->GetTileGroupId(); // Go over the logical tile for (oid_t tuple_id : *logical_tile) { expression::ContainerTuple<executor::LogicalTile> cur_tuple( logical_tile.get(), tuple_id); // Logging { // construct a physical tuple from the logical tuple std::unique_ptr<storage::Tuple> tuple(new storage::Tuple(schema, true)); for (auto column_id : column_ids) { tuple->SetValue(column_id, cur_tuple.GetValue(column_id), this->pool.get()); } ItemPointer location(tile_group_id, tuple_id); // TODO is it possible to avoid `new` for checkpoint? std::shared_ptr<LogRecord> record(logger_->GetTupleRecord( LOGRECORD_TYPE_TUPLE_INSERT, INITIAL_TXN_ID, target_table->GetOid(), database_oid, location, INVALID_ITEMPOINTER, tuple.get())); PL_ASSERT(record); CopySerializeOutput output_buffer; record->Serialize(output_buffer); LOG_TRACE("Insert a new record for checkpoint (%u, %u)", tile_group_id, tuple_id); records_.push_back(record); } } // persist to file once per tile Persist(); current_tile_group_offset++; } }