blob: 3b1bcc0ac6ea798dbe6d0770bf7a985e171d7fd6 [file] [log] [blame]
/**
* 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