Tile *Tile::CopyTile(BackendType backend_type) { auto schema = GetSchema(); bool tile_columns_inlined = schema->IsInlined(); auto allocated_tuple_count = GetAllocatedTupleCount(); // Create a shallow copy of the old tile TileGroupHeader *new_header = GetHeader(); Tile *new_tile = TileFactory::GetTile( backend_type, INVALID_OID, INVALID_OID, INVALID_OID, INVALID_OID, new_header, *schema, tile_group, allocated_tuple_count); PL_MEMCPY(static_cast<void *>(new_tile->data), static_cast<void *>(data), tile_size); // Do a deep copy if some column is uninlined, so that // the values in that column point to the new pool if (!tile_columns_inlined) { auto uninlined_col_cnt = schema->GetUninlinedColumnCount(); // Go over each uninlined column, making a deep copy for (oid_t col_itr = 0; col_itr < uninlined_col_cnt; col_itr++) { auto uninlined_col_offset = schema->GetUninlinedColumn(col_itr); // Copy the column over to the new tile group for (oid_t tuple_itr = 0; tuple_itr < allocated_tuple_count; tuple_itr++) { type::Value val = (new_tile->GetValue(tuple_itr, uninlined_col_offset)); new_tile->SetValue(val, tuple_itr, uninlined_col_offset); } } } return new_tile; }
/** * Insert tuple at slot * NOTE : No checks, must be at valid slot. */ void Tile::InsertTuple(const oid_t tuple_offset, Tuple *tuple) { PL_ASSERT(tuple_offset < GetAllocatedTupleCount()); // Find slot location char *location = tuple_offset * tuple_length + data; // Copy over the tuple data into the tuple slot in the tile PL_MEMCPY(location, tuple->tuple_data_, tuple_length); }
/** * Insert tuple at slot * NOTE : No checks, must be at valid slot. */ void Tile::InsertTuple(const oid_t tuple_offset, Tuple *tuple) { assert(tuple_offset < GetAllocatedTupleCount()); // Find slot location char *location = tuple_offset * tuple_length + data; // Copy over the tuple data into the tuple slot in the tile std::memcpy(location, tuple->tuple_data, tuple_length); }
// column offset is the actual offset of the column within the tuple slot common::Value *Tile::GetValueFast(const oid_t tuple_offset, const size_t column_offset, const common::Type::TypeId column_type, const bool is_inlined) { PL_ASSERT(tuple_offset < GetAllocatedTupleCount()); PL_ASSERT(column_offset < schema.GetLength()); const char *tuple_location = GetTupleLocation(tuple_offset); const char *field_location = tuple_location + column_offset; return common::Value::DeserializeFrom(field_location, column_type, is_inlined); }
// column offset is the actual offset of the column within the tuple slot Value Tile::GetValueFast(const oid_t tuple_offset, const size_t column_offset, const ValueType column_type, const bool is_inlined) { assert(tuple_offset < GetAllocatedTupleCount()); assert(column_offset < schema.GetLength()); const char *tuple_location = GetTupleLocation(tuple_offset); const char *field_location = tuple_location + column_offset; return Value::InitFromTupleStorage(field_location, column_type, is_inlined); }
// column id is a 0-based column number common::Value *Tile::GetValue(const oid_t tuple_offset, const oid_t column_id) { PL_ASSERT(tuple_offset < GetAllocatedTupleCount()); PL_ASSERT(column_id < schema.GetColumnCount()); const common::Type::TypeId column_type = schema.GetType(column_id); const char *tuple_location = GetTupleLocation(tuple_offset); const char *field_location = tuple_location + schema.GetOffset(column_id); const bool is_inlined = schema.IsInlined(column_id); return common::Value::DeserializeFrom(field_location, column_type, is_inlined); }
// column id is a 0-based column number Value Tile::GetValue(const oid_t tuple_offset, const oid_t column_id) { assert(tuple_offset < GetAllocatedTupleCount()); assert(column_id < schema.GetColumnCount()); const ValueType column_type = schema.GetType(column_id); const char *tuple_location = GetTupleLocation(tuple_offset); const char *field_location = tuple_location + schema.GetOffset(column_id); const bool is_inlined = schema.IsInlined(column_id); return Value::InitFromTupleStorage(field_location, column_type, is_inlined); }
storage::TileGroup *DataTable::TransformTileGroup( const oid_t &tile_group_offset, const double &theta) { // First, check if the tile group is in this table if (tile_group_offset >= tile_groups_.GetSize()) { LOG_ERROR("Tile group offset not found in table : %u ", tile_group_offset); return nullptr; } auto tile_group_id = tile_groups_.FindValid(tile_group_offset, invalid_tile_group_id); // Get orig tile group from catalog auto &catalog_manager = catalog::Manager::GetInstance(); auto tile_group = catalog_manager.GetTileGroup(tile_group_id); auto diff = tile_group->GetSchemaDifference(default_partition_); // Check threshold for transformation if (diff < theta) { return nullptr; } LOG_TRACE("Transforming tile group : %u", tile_group_offset); // Get the schema for the new transformed tile group auto new_schema = TransformTileGroupSchema(tile_group.get(), default_partition_); // Allocate space for the transformed tile group std::shared_ptr<storage::TileGroup> new_tile_group( TileGroupFactory::GetTileGroup( tile_group->GetDatabaseId(), tile_group->GetTableId(), tile_group->GetTileGroupId(), tile_group->GetAbstractTable(), new_schema, default_partition_, tile_group->GetAllocatedTupleCount())); // Set the transformed tile group column-at-a-time SetTransformedTileGroup(tile_group.get(), new_tile_group.get()); // Set the location of the new tile group // and clean up the orig tile group catalog_manager.AddTileGroup(tile_group_id, new_tile_group); return new_tile_group.get(); }
// Validate that MVCC storage is correct, it assumes an old-to-new chain // Invariants // 1. Transaction id should either be INVALID_TXNID or INITIAL_TXNID // 2. Begin commit id should <= end commit id // 3. Timestamp consistence // 4. Version doubly linked list consistency static void ValidateMVCC_OldToNew(storage::DataTable *table) { auto &catalog_manager = catalog::Manager::GetInstance(); LOG_INFO("Validating MVCC storage"); int tile_group_count = table->GetTileGroupCount(); LOG_INFO("The table has %d tile groups in the table", tile_group_count); for (int tile_group_offset = 0; tile_group_offset < tile_group_count; tile_group_offset++) { LOG_INFO("Validate tile group #%d", tile_group_offset); auto tile_group = table->GetTileGroup(tile_group_offset); auto tile_group_header = tile_group->GetHeader(); size_t tuple_count = tile_group->GetAllocatedTupleCount(); LOG_INFO("Tile group #%d has allocated %lu tuples", tile_group_offset, tuple_count); // 1. Transaction id should either be INVALID_TXNID or INITIAL_TXNID for (oid_t tuple_slot = 0; tuple_slot < tuple_count; tuple_slot++) { txn_id_t txn_id = tile_group_header->GetTransactionId(tuple_slot); EXPECT_TRUE(txn_id == INVALID_TXN_ID || txn_id == INITIAL_TXN_ID) << "Transaction id is not INVALID_TXNID or INITIAL_TXNID"; } LOG_INFO("[OK] All tuples have valid txn id"); // double avg_version_chain_length = 0.0; for (oid_t tuple_slot = 0; tuple_slot < tuple_count; tuple_slot++) { txn_id_t txn_id = tile_group_header->GetTransactionId(tuple_slot); cid_t begin_cid = tile_group_header->GetBeginCommitId(tuple_slot); cid_t end_cid = tile_group_header->GetEndCommitId(tuple_slot); ItemPointer next_location = tile_group_header->GetNextItemPointer(tuple_slot); ItemPointer prev_location = tile_group_header->GetPrevItemPointer(tuple_slot); // 2. Begin commit id should <= end commit id EXPECT_TRUE(begin_cid <= end_cid) << "Tuple begin commit id is less than or equal to end commit id"; // This test assumes a oldest-to-newest version chain if (txn_id != INVALID_TXN_ID) { EXPECT_TRUE(begin_cid != MAX_CID) << "Non invalid txn shouldn't have a MAX_CID begin commit id"; // The version is an oldest version if (prev_location.IsNull()) { if (next_location.IsNull()) { EXPECT_EQ(end_cid, MAX_CID) << "Single version has a non MAX_CID end commit time"; } else { cid_t prev_end_cid = end_cid; ItemPointer prev_location(tile_group->GetTileGroupId(), tuple_slot); while (!next_location.IsNull()) { auto next_tile_group = catalog_manager.GetTileGroup(next_location.block); auto next_tile_group_header = next_tile_group->GetHeader(); txn_id_t next_txn_id = next_tile_group_header->GetTransactionId( next_location.offset); if (next_txn_id == INVALID_TXN_ID) { // If a version in the version chain has a INVALID_TXN_ID, it // must be at the tail // of the chain. It is either because we have deleted a tuple // (so append a invalid tuple), // or because this new version is aborted. EXPECT_TRUE( next_tile_group_header->GetNextItemPointer( next_location.offset).IsNull()) << "Invalid version in a version chain and is not delete"; } cid_t next_begin_cid = next_tile_group_header->GetBeginCommitId( next_location.offset); cid_t next_end_cid = next_tile_group_header->GetEndCommitId(next_location.offset); // 3. Timestamp consistence if (next_begin_cid == MAX_CID) { // It must be an aborted version, it should be at the end of the // chain EXPECT_TRUE( next_tile_group_header->GetNextItemPointer( next_location.offset).IsNull()) << "Version with MAX_CID begin cid is not version tail"; } else { EXPECT_EQ(prev_end_cid, next_begin_cid) << "Prev end commit id should equal net begin commit id"; ItemPointer next_prev_location = next_tile_group_header->GetPrevItemPointer( next_location.offset); // 4. Version doubly linked list consistency EXPECT_TRUE(next_prev_location.offset == prev_location.offset && next_prev_location.block == prev_location.block) << "Next version's prev version does not match"; } prev_location = next_location; prev_end_cid = next_end_cid; next_location = next_tile_group_header->GetNextItemPointer( next_location.offset); } // Now prev_location is at the tail of the version chain ItemPointer last_location = prev_location; auto last_tile_group = catalog_manager.GetTileGroup(last_location.block); auto last_tile_group_header = last_tile_group->GetHeader(); // txn_id_t last_txn_id = // last_tile_group_header->GetTransactionId(last_location.offset); cid_t last_end_cid = last_tile_group_header->GetEndCommitId(last_location.offset); EXPECT_TRUE( last_tile_group_header->GetNextItemPointer(last_location.offset) .IsNull()) << "Last version has a next pointer"; EXPECT_EQ(last_end_cid, MAX_CID) << "Last version doesn't end with MAX_CID"; } } } else { EXPECT_TRUE(tile_group_header->GetNextItemPointer(tuple_slot).IsNull()) << "Invalid tuple must not have next item pointer"; } } LOG_INFO("[OK] oldest-to-newest version chain validated"); } }