| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| **/ |
| |
| #include "storage/StorageBlockLayout.hpp" |
| |
| #include <cstring> |
| #include <string> |
| #include <vector> |
| |
| #include "glog/logging.h" |
| |
| #include "catalog/CatalogRelationSchema.hpp" |
| #include "storage/StorageBlockLayout.pb.h" |
| #include "storage/StorageConstants.hpp" |
| #include "storage/StorageErrors.hpp" |
| #include "storage/SubBlockTypeRegistry.hpp" |
| #include "utility/Macros.hpp" |
| |
| using std::size_t; |
| using std::string; |
| using std::strlen; |
| using std::vector; |
| |
| namespace quickstep { |
| |
| StorageBlockLayout::StorageBlockLayout(const CatalogRelationSchema &relation, |
| const StorageBlockLayoutDescription &proto) |
| : relation_(relation), |
| estimated_bytes_per_tuple_(0) { |
| layout_description_.Clear(); |
| layout_description_.CopyFrom(proto); |
| |
| finalize(); |
| } |
| |
| // TODO(chasseur): Align sub-blocks to cache line boundaries. |
| void StorageBlockLayout::finalize() { |
| CHECK(DescriptionIsValid(relation_, layout_description_)) |
| << "Called StorageBlockLayout::finalize() with incomplete or invalid layout."; |
| |
| // Reset the header and copy the layout from this StorageBlockLayout. |
| block_header_.Clear(); |
| block_header_.mutable_layout()->CopyFrom(layout_description_); |
| |
| // Temporarily set all sub-block sizes to zero, and set all indices as |
| // consistent. |
| block_header_.set_tuple_store_size(0); |
| for (int index_num = 0; |
| index_num < layout_description_.index_description_size(); |
| ++index_num) { |
| block_header_.add_index_size(0); |
| block_header_.add_index_consistent(true); |
| } |
| |
| DEBUG_ASSERT(block_header_.IsInitialized()); |
| |
| size_t header_size = getBlockHeaderSize(); |
| if (header_size > layout_description_.num_slots() * kSlotSizeBytes) { |
| throw BlockMemoryTooSmall("StorageBlockLayout", layout_description_.num_slots() * kSlotSizeBytes); |
| } |
| |
| // Assign block space to sub-blocks in proportion to the estimated number of |
| // bytes needed for each tuple in each sub-block. |
| estimated_bytes_per_tuple_ = 0; |
| |
| // Bytes for tuple store. |
| const TupleStorageSubBlockDescription &tuple_store_description |
| = layout_description_.tuple_store_description(); |
| const size_t tuple_store_size_factor = SubBlockTypeRegistry::EstimateBytesPerTupleForTupleStore( |
| relation_, |
| tuple_store_description); |
| estimated_bytes_per_tuple_ += tuple_store_size_factor; |
| |
| // Bytes for each index. |
| vector<size_t> index_size_factors; |
| index_size_factors.reserve(layout_description_.index_description_size()); |
| for (int index_num = 0; |
| index_num < layout_description_.index_description_size(); |
| ++index_num) { |
| const IndexSubBlockDescription &index_description = layout_description_.index_description(index_num); |
| const size_t index_size_factor = SubBlockTypeRegistry::EstimateBytesPerTupleForIndex( |
| relation_, |
| index_description); |
| index_size_factors.push_back(index_size_factor); |
| estimated_bytes_per_tuple_ += index_size_factor; |
| } |
| |
| if (estimated_bytes_per_tuple_ == 0) { |
| LOG(WARNING) |
| << "Estimated zero bytes per tuple when finalizing a " |
| << "StorageBlockLayout for relation \"" << relation_.getName() |
| << "\" (relation_id: " << relation_.getID() |
| << "). Adjusting to 1 byte."; |
| estimated_bytes_per_tuple_ = 1; |
| } |
| |
| size_t allocated_sub_block_space = 0; |
| size_t sub_block_space = (layout_description_.num_slots() * kSlotSizeBytes) - header_size; |
| |
| for (int index_num = 0; |
| index_num < layout_description_.index_description_size(); |
| ++index_num) { |
| size_t index_size = 0; |
| if (index_size_factors[index_num] != kZeroSize) { |
| // Estimated size to be consumed was specified per tuple. |
| index_size = (sub_block_space * index_size_factors[index_num]) / estimated_bytes_per_tuple_; |
| } else { |
| // Some indices define total size per block, instead of defining size per tuple. |
| const IndexSubBlockDescription &index_description = layout_description_.index_description(index_num); |
| index_size = SubBlockTypeRegistry::EstimateBytesPerBlockForIndex(relation_, index_description); |
| } |
| block_header_.set_index_size(index_num, index_size); |
| allocated_sub_block_space += index_size; |
| } |
| |
| block_header_.set_tuple_store_size(sub_block_space - allocated_sub_block_space); |
| |
| DEBUG_ASSERT(block_header_.IsInitialized()); |
| DEBUG_ASSERT(header_size == getBlockHeaderSize()); |
| } |
| |
| void StorageBlockLayout::copyHeaderTo(void *dest) const { |
| DEBUG_ASSERT(DescriptionIsValid(relation_, layout_description_)); |
| DEBUG_ASSERT(block_header_.IsInitialized()); |
| |
| *static_cast<int*>(dest) = block_header_.ByteSize(); |
| if (!block_header_.SerializeToArray(static_cast<char*>(dest) + sizeof(int), |
| block_header_.ByteSize())) { |
| FATAL_ERROR("Failed to do binary serialization of StorageBlockHeader in StorageBlockLayout::copyHeaderTo()"); |
| } |
| } |
| |
| StorageBlockLayout* StorageBlockLayout::GenerateDefaultLayout(const CatalogRelationSchema &relation, |
| const bool relation_variable_length) { |
| // TODO(marc): In the future we may use relation_variable_length (columnstores as intermediate |
| // blocks), but for now, all we do is add (void) so that the compiler will not complain |
| // about an unused variable. |
| (void) relation_variable_length; |
| StorageBlockLayout *layout = new StorageBlockLayout(relation); |
| |
| StorageBlockLayoutDescription *description = layout->getDescriptionMutable(); |
| description->set_num_slots(1); |
| |
| TupleStorageSubBlockDescription *tuple_store_description = description->mutable_tuple_store_description(); |
| tuple_store_description->set_sub_block_type(TupleStorageSubBlockDescription::SPLIT_ROW_STORE); |
| |
| layout->finalize(); |
| return layout; |
| } |
| |
| bool StorageBlockLayout::DescriptionIsValid(const CatalogRelationSchema &relation, |
| const StorageBlockLayoutDescription &description) { |
| return SubBlockTypeRegistry::LayoutDescriptionIsValid(relation, description); |
| } |
| |
| std::size_t StorageBlockLayout::estimateTuplesPerBlock() const { |
| DEBUG_ASSERT(block_header_.IsInitialized()); |
| return ((layout_description_.num_slots() * kSlotSizeBytes) - getBlockHeaderSize()) |
| / estimated_bytes_per_tuple_; |
| } |
| |
| } // namespace quickstep |