blob: 6c70d0faabc83bda5efc4d958727f70b969b0c6b [file] [log] [blame]
/**
* Copyright 2011-2015 Quickstep Technologies LLC.
* Copyright 2015 Pivotal Software, Inc.
*
* Licensed 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/SplitRowStoreTupleStorageSubBlock.hpp"
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <utility>
#include <vector>
#include "catalog/CatalogRelationSchema.hpp"
#include "storage/SplitRowStoreValueAccessor.hpp"
#include "storage/StorageBlockLayout.pb.h"
#include "storage/StorageErrors.hpp"
#include "storage/SubBlockTypeRegistry.hpp"
#include "storage/ValueAccessor.hpp"
#include "storage/ValueAccessorUtil.hpp"
#include "types/TypedValue.hpp"
#include "utility/BitVector.hpp"
#include "utility/Macros.hpp"
#include "utility/ScopedBuffer.hpp"
namespace quickstep {
QUICKSTEP_REGISTER_TUPLE_STORE(SplitRowStoreTupleStorageSubBlock, SPLIT_ROW_STORE);
namespace {
template <typename ValueAccessorT, bool nullable_attrs>
inline std::size_t CalculateVariableSize(
const CatalogRelationSchema &relation,
const ValueAccessorT &accessor) {
std::size_t total_size = 0;
attribute_id accessor_attr_id = 0;
for (CatalogRelationSchema::const_iterator attr_it = relation.begin();
attr_it != relation.end();
++attr_it, ++accessor_attr_id) {
if (!attr_it->getType().isVariableLength()) {
continue;
}
TypedValue value(accessor.getTypedValue(accessor_attr_id));
if (nullable_attrs && value.isNull()) {
continue;
}
total_size += value.getDataSize();
}
return total_size;
}
template <typename ValueAccessorT, bool nullable_attrs>
inline std::size_t CalculateVariableSizeWithRemappedAttributes(
const CatalogRelationSchema &relation,
const ValueAccessorT &accessor,
const std::vector<attribute_id> &attribute_map) {
std::size_t total_size = 0;
std::vector<attribute_id>::const_iterator attr_map_it = attribute_map.begin();
for (CatalogRelationSchema::const_iterator attr_it = relation.begin();
attr_it != relation.end();
++attr_it, ++attr_map_it) {
if (!attr_it->getType().isVariableLength()) {
continue;
}
TypedValue value(accessor.getTypedValue(*attr_map_it));
if (nullable_attrs && value.isNull()) {
continue;
}
total_size += value.getDataSize();
}
return total_size;
}
} // namespace
SplitRowStoreTupleStorageSubBlock::SplitRowStoreTupleStorageSubBlock(
const CatalogRelationSchema &relation,
const TupleStorageSubBlockDescription &description,
const bool new_block,
void *sub_block_memory,
const std::size_t sub_block_memory_size)
: TupleStorageSubBlock(relation,
description,
new_block,
sub_block_memory,
sub_block_memory_size),
header_(static_cast<Header*>(sub_block_memory)) {
if (!DescriptionIsValid(relation_, description_)) {
FATAL_ERROR("Attempted to construct a SplitRowStoreTupleStorageSubBlock from an invalid description.");
}
if (sub_block_memory_size < sizeof(Header)) {
throw BlockMemoryTooSmall("SplitRowStoreTupleStorageSubBlock", sub_block_memory_size);
}
// Slot format is a bitmap of null attributes, followed by fixed-length
// attribute storage, followed by references to variable-length attribute
// values (each is a pair of uint32_ts indicating the offset in the
// tuple-storage region and the size of the value).
per_tuple_null_bitmap_bytes_ = BitVector<true>::BytesNeeded(relation.numNullableAttributes());
tuple_slot_bytes_ = per_tuple_null_bitmap_bytes_
+ relation.getFixedByteLength()
+ relation.numVariableLengthAttributes() * (sizeof(std::uint32_t) * 2);
// Size the occupancy bitmap by calculating the maximum tuples that can fit
// assuming the bare-minimum per tuple storage is used (no variable-length
// storage used for nullable variable-length attributes, otherwise the
// minimum possible.
//
// TODO(chasseur): This will slightly over-size the occupancy bitmap, since
// it doesn't take into account bytes used to store the bitmap itself. Adding
// some generic size-estimation functions to BitVector might be a good idea.
std::size_t max_num_tuples
= (sub_block_memory_size_ - sizeof(Header))
/ (tuple_slot_bytes_ + relation_.getMinimumVariableByteLengthExcludingNullable());
occupancy_bitmap_bytes_ = BitVector<false>::BytesNeeded(max_num_tuples);
if (sub_block_memory_size < occupancy_bitmap_bytes_ + sizeof(Header)) {
throw BlockMemoryTooSmall("SplitRowStoreTupleStorageSubBlock", sub_block_memory_size);
}
occupancy_bitmap_.reset(
new BitVector<false>(static_cast<char*>(sub_block_memory_) + sizeof(Header),
max_num_tuples));
tuple_storage_ = static_cast<char*>(sub_block_memory_)
+ sizeof(Header) + occupancy_bitmap_bytes_;
tuple_storage_bytes_ = sub_block_memory_size_ - (sizeof(Header) + occupancy_bitmap_bytes_);
if (new_block) {
// Only need to initialize these fields, the rest of the block will be
// zeroed-out by the StorageManager.
header_->max_tid = -1;
header_->variable_length_storage_compact = true;
}
}
bool SplitRowStoreTupleStorageSubBlock::DescriptionIsValid(
const CatalogRelationSchema &relation,
const TupleStorageSubBlockDescription &description) {
// Make sure description is initialized and specifies SplitRowStore.
if (!description.IsInitialized()) {
return false;
}
if (description.sub_block_type() != TupleStorageSubBlockDescription::SPLIT_ROW_STORE) {
return false;
}
return true;
}
std::size_t SplitRowStoreTupleStorageSubBlock::EstimateBytesPerTuple(
const CatalogRelationSchema &relation,
const TupleStorageSubBlockDescription &description) {
DEBUG_ASSERT(DescriptionIsValid(relation, description));
return relation.getFixedByteLength() // Fixed-length attrs
+ BitVector<true>::BytesNeeded(relation.numNullableAttributes()) // Null bitmap
+ relation.numVariableLengthAttributes() * (sizeof(std::uint32_t) * 2) // Variable-length ref
+ relation.getEstimatedVariableByteLength(); // Variable-length attrs
}
TupleStorageSubBlock::InsertResult SplitRowStoreTupleStorageSubBlock::insertTuple(
const Tuple &tuple) {
if (relation_.hasNullableAttributes()) {
if (relation_.isVariableLength()) {
return insertTupleImpl<true, true>(tuple);
} else {
return insertTupleImpl<true, false>(tuple);
}
} else {
if (relation_.isVariableLength()) {
return insertTupleImpl<false, true>(tuple);
} else {
return insertTupleImpl<false, false>(tuple);
}
}
}
tuple_id SplitRowStoreTupleStorageSubBlock::bulkInsertTuples(ValueAccessor *accessor) {
const tuple_id original_num_tuples = header_->num_tuples;
tuple_id pos = 0;
InvokeOnAnyValueAccessor(
accessor,
[&](auto *accessor) -> void { // NOLINT(build/c++11)
if (relation_.hasNullableAttributes()) {
if (relation_.isVariableLength()) {
while (accessor->next()) {
// If packed, insert at the end of the slot array, otherwise find the
// first hole.
pos = this->isPacked() ? header_->num_tuples
: occupancy_bitmap_->firstZero(pos);
const std::size_t tuple_variable_bytes
= CalculateVariableSize<decltype(*accessor), true>(relation_, *accessor);
if (!this->spaceToInsert(pos, tuple_variable_bytes)) {
accessor->previous();
break;
}
// Allocate variable-length storage.
header_->variable_length_bytes_allocated += tuple_variable_bytes;
// Find the slot and locate its sub-structures.
void *tuple_slot = static_cast<char*>(tuple_storage_) + pos * tuple_slot_bytes_;
BitVector<true> tuple_null_bitmap(tuple_slot,
relation_.numNullableAttributes());
tuple_null_bitmap.clear();
char *fixed_length_attr_storage = static_cast<char*>(tuple_slot) + per_tuple_null_bitmap_bytes_;
std::uint32_t *variable_length_info_array = reinterpret_cast<std::uint32_t*>(
fixed_length_attr_storage + relation_.getFixedByteLength());
// Start writing variable-length data at the beginning of the newly
// allocated range.
std::uint32_t current_variable_position
= tuple_storage_bytes_ - header_->variable_length_bytes_allocated;
attribute_id accessor_attr_id = 0;
for (CatalogRelationSchema::const_iterator attr_it = relation_.begin();
attr_it != relation_.end();
++attr_it, ++accessor_attr_id) {
const int nullable_idx = relation_.getNullableAttributeIndex(attr_it->getID());
const int variable_idx = relation_.getVariableLengthAttributeIndex(attr_it->getID());
TypedValue attr_value(accessor->getTypedValue(accessor_attr_id));
if ((nullable_idx != -1) && (attr_value.isNull())) {
// Set null bit and move on.
tuple_null_bitmap.setBit(nullable_idx, true);
continue;
}
if (variable_idx != -1) {
// Write offset and size into the slot, then copy the actual
// value into the variable-length storage region.
const std::size_t attr_size = attr_value.getDataSize();
variable_length_info_array[variable_idx << 1] = current_variable_position;
variable_length_info_array[(variable_idx << 1) + 1] = attr_size;
attr_value.copyInto(static_cast<char*>(tuple_storage_) + current_variable_position);
current_variable_position += attr_size;
} else {
// Copy fixed-length value directly into the slot.
attr_value.copyInto(fixed_length_attr_storage
+ relation_.getFixedLengthAttributeOffset(attr_it->getID()));
}
}
// Update occupancy bitmap and header.
occupancy_bitmap_->setBit(pos, true);
++(header_->num_tuples);
if (pos > header_->max_tid) {
header_->max_tid = pos;
}
}
} else {
// Same as above, but skip variable-length checks.
while (accessor->next()) {
pos = this->isPacked() ? header_->num_tuples
: occupancy_bitmap_->firstZero(pos);
if (!this->spaceToInsert(pos, 0)) {
accessor->previous();
break;
}
void *tuple_slot = static_cast<char*>(tuple_storage_) + pos * tuple_slot_bytes_;
BitVector<true> tuple_null_bitmap(tuple_slot,
relation_.numNullableAttributes());
tuple_null_bitmap.clear();
char *fixed_length_attr_storage = static_cast<char*>(tuple_slot) + per_tuple_null_bitmap_bytes_;
attribute_id accessor_attr_id = 0;
for (CatalogRelationSchema::const_iterator attr_it = relation_.begin();
attr_it != relation_.end();
++attr_it, ++accessor_attr_id) {
const int nullable_idx = relation_.getNullableAttributeIndex(attr_it->getID());
if (nullable_idx != -1) {
const void *attr_value = accessor->template getUntypedValue<true>(accessor_attr_id);
if (attr_value == nullptr) {
tuple_null_bitmap.setBit(nullable_idx, true);
} else {
std::memcpy(fixed_length_attr_storage
+ relation_.getFixedLengthAttributeOffset(attr_it->getID()),
attr_value,
attr_it->getType().maximumByteLength());
}
} else {
const void *attr_value = accessor->template getUntypedValue<false>(accessor_attr_id);
std::memcpy(fixed_length_attr_storage
+ relation_.getFixedLengthAttributeOffset(attr_it->getID()),
attr_value,
attr_it->getType().maximumByteLength());
}
}
occupancy_bitmap_->setBit(pos, true);
++(header_->num_tuples);
if (pos > header_->max_tid) {
header_->max_tid = pos;
}
}
}
} else {
if (relation_.isVariableLength()) {
// Same as most general case above, but skip null checks.
while (accessor->next()) {
pos = this->isPacked() ? header_->num_tuples
: occupancy_bitmap_->firstZero(pos);
const std::size_t tuple_variable_bytes
= CalculateVariableSize<decltype(*accessor), false>(relation_, *accessor);
if (!this->spaceToInsert(pos, tuple_variable_bytes)) {
accessor->previous();
break;
}
header_->variable_length_bytes_allocated += tuple_variable_bytes;
void *tuple_slot = static_cast<char*>(tuple_storage_) + pos * tuple_slot_bytes_;
char *fixed_length_attr_storage = static_cast<char*>(tuple_slot) + per_tuple_null_bitmap_bytes_;
std::uint32_t *variable_length_info_array = reinterpret_cast<std::uint32_t*>(
fixed_length_attr_storage + relation_.getFixedByteLength());
std::uint32_t current_variable_position
= tuple_storage_bytes_ - header_->variable_length_bytes_allocated;
attribute_id accessor_attr_id = 0;
for (CatalogRelationSchema::const_iterator attr_it = relation_.begin();
attr_it != relation_.end();
++attr_it, ++accessor_attr_id) {
const int variable_idx = relation_.getVariableLengthAttributeIndex(attr_it->getID());
TypedValue attr_value(accessor->getTypedValue(accessor_attr_id));
if (variable_idx != -1) {
const std::size_t attr_size = attr_value.getDataSize();
variable_length_info_array[variable_idx << 1] = current_variable_position;
variable_length_info_array[(variable_idx << 1) + 1] = attr_size;
attr_value.copyInto(static_cast<char*>(tuple_storage_) + current_variable_position);
current_variable_position += attr_size;
} else {
attr_value.copyInto(fixed_length_attr_storage
+ relation_.getFixedLengthAttributeOffset(attr_it->getID()));
}
}
occupancy_bitmap_->setBit(pos, true);
++(header_->num_tuples);
if (pos > header_->max_tid) {
header_->max_tid = pos;
}
}
} else {
// Simplest case: skip both null and variable-length checks.
while (accessor->next()) {
pos = this->isPacked() ? header_->num_tuples
: occupancy_bitmap_->firstZero(pos);
if (!this->spaceToInsert(pos, 0)) {
accessor->previous();
break;
}
void *tuple_slot = static_cast<char*>(tuple_storage_) + pos * tuple_slot_bytes_;
char *fixed_length_attr_storage = static_cast<char*>(tuple_slot) + per_tuple_null_bitmap_bytes_;
attribute_id accessor_attr_id = 0;
for (CatalogRelationSchema::const_iterator attr_it = relation_.begin();
attr_it != relation_.end();
++attr_it, ++accessor_attr_id) {
const void *attr_value = accessor->template getUntypedValue<false>(accessor_attr_id);
std::memcpy(fixed_length_attr_storage
+ relation_.getFixedLengthAttributeOffset(attr_it->getID()),
attr_value,
attr_it->getType().maximumByteLength());
}
occupancy_bitmap_->setBit(pos, true);
++(header_->num_tuples);
if (pos > header_->max_tid) {
header_->max_tid = pos;
}
}
}
}
});
return header_->num_tuples - original_num_tuples;
}
tuple_id SplitRowStoreTupleStorageSubBlock::bulkInsertTuplesWithRemappedAttributes(
const std::vector<attribute_id> &attribute_map,
ValueAccessor *accessor) {
DEBUG_ASSERT(attribute_map.size() == relation_.size());
const tuple_id original_num_tuples = header_->num_tuples;
tuple_id pos = 0;
InvokeOnAnyValueAccessor(
accessor,
[&](auto *accessor) -> void { // NOLINT(build/c++11)
if (relation_.hasNullableAttributes()) {
if (relation_.isVariableLength()) {
while (accessor->next()) {
pos = this->isPacked() ? header_->num_tuples
: occupancy_bitmap_->firstZero(pos);
const std::size_t tuple_variable_bytes
= CalculateVariableSizeWithRemappedAttributes<decltype(*accessor), true>(
relation_, *accessor, attribute_map);
if (!this->spaceToInsert(pos, tuple_variable_bytes)) {
accessor->previous();
break;
}
header_->variable_length_bytes_allocated += tuple_variable_bytes;
void *tuple_slot = static_cast<char*>(tuple_storage_) + pos * tuple_slot_bytes_;
BitVector<true> tuple_null_bitmap(tuple_slot,
relation_.numNullableAttributes());
tuple_null_bitmap.clear();
char *fixed_length_attr_storage = static_cast<char*>(tuple_slot) + per_tuple_null_bitmap_bytes_;
std::uint32_t *variable_length_info_array = reinterpret_cast<std::uint32_t*>(
fixed_length_attr_storage + relation_.getFixedByteLength());
std::uint32_t current_variable_position
= tuple_storage_bytes_ - header_->variable_length_bytes_allocated;
std::vector<attribute_id>::const_iterator attr_map_it = attribute_map.begin();
for (CatalogRelationSchema::const_iterator attr_it = relation_.begin();
attr_it != relation_.end();
++attr_it, ++attr_map_it) {
const int nullable_idx = relation_.getNullableAttributeIndex(attr_it->getID());
const int variable_idx = relation_.getVariableLengthAttributeIndex(attr_it->getID());
TypedValue attr_value(accessor->getTypedValue(*attr_map_it));
if ((nullable_idx != -1) && (attr_value.isNull())) {
tuple_null_bitmap.setBit(nullable_idx, true);
continue;
}
if (variable_idx != -1) {
const std::size_t attr_size = attr_value.getDataSize();
variable_length_info_array[variable_idx << 1] = current_variable_position;
variable_length_info_array[(variable_idx << 1) + 1] = attr_size;
attr_value.copyInto(static_cast<char*>(tuple_storage_) + current_variable_position);
current_variable_position += attr_size;
} else {
attr_value.copyInto(fixed_length_attr_storage
+ relation_.getFixedLengthAttributeOffset(attr_it->getID()));
}
}
occupancy_bitmap_->setBit(pos, true);
++(header_->num_tuples);
if (pos > header_->max_tid) {
header_->max_tid = pos;
}
}
} else {
while (accessor->next()) {
pos = this->isPacked() ? header_->num_tuples
: occupancy_bitmap_->firstZero(pos);
if (!this->spaceToInsert(pos, 0)) {
accessor->previous();
break;
}
void *tuple_slot = static_cast<char*>(tuple_storage_) + pos * tuple_slot_bytes_;
BitVector<true> tuple_null_bitmap(tuple_slot,
relation_.numNullableAttributes());
tuple_null_bitmap.clear();
char *fixed_length_attr_storage = static_cast<char*>(tuple_slot) + per_tuple_null_bitmap_bytes_;
std::vector<attribute_id>::const_iterator attr_map_it = attribute_map.begin();
for (CatalogRelationSchema::const_iterator attr_it = relation_.begin();
attr_it != relation_.end();
++attr_it, ++attr_map_it) {
const int nullable_idx = relation_.getNullableAttributeIndex(attr_it->getID());
if (nullable_idx != -1) {
const void *attr_value = accessor->template getUntypedValue<true>(*attr_map_it);
if (attr_value == nullptr) {
tuple_null_bitmap.setBit(nullable_idx, true);
} else {
std::memcpy(fixed_length_attr_storage
+ relation_.getFixedLengthAttributeOffset(attr_it->getID()),
attr_value,
attr_it->getType().maximumByteLength());
}
} else {
const void *attr_value = accessor->template getUntypedValue<false>(*attr_map_it);
std::memcpy(fixed_length_attr_storage
+ relation_.getFixedLengthAttributeOffset(attr_it->getID()),
attr_value,
attr_it->getType().maximumByteLength());
}
}
occupancy_bitmap_->setBit(pos, true);
++(header_->num_tuples);
if (pos > header_->max_tid) {
header_->max_tid = pos;
}
}
}
} else {
if (relation_.isVariableLength()) {
while (accessor->next()) {
pos = this->isPacked() ? header_->num_tuples
: occupancy_bitmap_->firstZero(pos);
const std::size_t tuple_variable_bytes
= CalculateVariableSizeWithRemappedAttributes<decltype(*accessor), false>(
relation_, *accessor, attribute_map);
if (!this->spaceToInsert(pos, tuple_variable_bytes)) {
accessor->previous();
break;
}
header_->variable_length_bytes_allocated += tuple_variable_bytes;
void *tuple_slot = static_cast<char*>(tuple_storage_) + pos * tuple_slot_bytes_;
char *fixed_length_attr_storage = static_cast<char*>(tuple_slot) + per_tuple_null_bitmap_bytes_;
std::uint32_t *variable_length_info_array = reinterpret_cast<std::uint32_t*>(
fixed_length_attr_storage + relation_.getFixedByteLength());
std::uint32_t current_variable_position
= tuple_storage_bytes_ - header_->variable_length_bytes_allocated;
std::vector<attribute_id>::const_iterator attr_map_it = attribute_map.begin();
for (CatalogRelationSchema::const_iterator attr_it = relation_.begin();
attr_it != relation_.end();
++attr_it, ++attr_map_it) {
const int variable_idx = relation_.getVariableLengthAttributeIndex(attr_it->getID());
TypedValue attr_value(accessor->getTypedValue(*attr_map_it));
if (variable_idx != -1) {
const std::size_t attr_size = attr_value.getDataSize();
variable_length_info_array[variable_idx << 1] = current_variable_position;
variable_length_info_array[(variable_idx << 1) + 1] = attr_size;
attr_value.copyInto(static_cast<char*>(tuple_storage_) + current_variable_position);
current_variable_position += attr_size;
} else {
attr_value.copyInto(fixed_length_attr_storage
+ relation_.getFixedLengthAttributeOffset(attr_it->getID()));
}
}
occupancy_bitmap_->setBit(pos, true);
++(header_->num_tuples);
if (pos > header_->max_tid) {
header_->max_tid = pos;
}
}
} else {
while (accessor->next()) {
pos = this->isPacked() ? header_->num_tuples
: occupancy_bitmap_->firstZero(pos);
if (!this->spaceToInsert(pos, 0)) {
accessor->previous();
break;
}
void *tuple_slot = static_cast<char*>(tuple_storage_) + pos * tuple_slot_bytes_;
char *fixed_length_attr_storage = static_cast<char*>(tuple_slot) + per_tuple_null_bitmap_bytes_;
std::vector<attribute_id>::const_iterator attr_map_it = attribute_map.begin();
for (CatalogRelationSchema::const_iterator attr_it = relation_.begin();
attr_it != relation_.end();
++attr_it, ++attr_map_it) {
const void *attr_value = accessor->template getUntypedValue<false>(*attr_map_it);
std::memcpy(fixed_length_attr_storage
+ relation_.getFixedLengthAttributeOffset(attr_it->getID()),
attr_value,
attr_it->getType().maximumByteLength());
}
occupancy_bitmap_->setBit(pos, true);
++(header_->num_tuples);
if (pos > header_->max_tid) {
header_->max_tid = pos;
}
}
}
}
});
return header_->num_tuples - original_num_tuples;
}
const void* SplitRowStoreTupleStorageSubBlock::getAttributeValue(
const tuple_id tuple,
const attribute_id attr) const {
DEBUG_ASSERT(occupancy_bitmap_->getBit(tuple));
DEBUG_ASSERT(relation_.hasAttributeWithId(attr));
const char *tuple_slot = static_cast<const char*>(tuple_storage_)
+ tuple_slot_bytes_ * tuple;
const int nullable_idx = relation_.getNullableAttributeIndex(attr);
if (nullable_idx != -1) {
// const_cast is safe here. We will only be using read-only methods of
// BitVector.
BitVector<true> tuple_null_bitmap(const_cast<void*>(static_cast<const void*>(tuple_slot)),
relation_.numNullableAttributes());
if (tuple_null_bitmap.getBit(nullable_idx)) {
return nullptr;
}
}
const int variable_length_idx = relation_.getVariableLengthAttributeIndex(attr);
if (variable_length_idx == -1) {
// Fixed-length, stored in-line in slot.
return tuple_slot
+ per_tuple_null_bitmap_bytes_
+ relation_.getFixedLengthAttributeOffset(attr);
} else {
// Variable-length, stored at back of block.
const std::uint32_t pos = *reinterpret_cast<const std::uint32_t*>(
tuple_slot + per_tuple_null_bitmap_bytes_
+ relation_.getFixedByteLength()
+ variable_length_idx * 2 * sizeof(std::uint32_t));
return static_cast<const char*>(tuple_storage_) + pos;
}
}
TypedValue SplitRowStoreTupleStorageSubBlock::getAttributeValueTyped(
const tuple_id tuple,
const attribute_id attr) const {
DEBUG_ASSERT(occupancy_bitmap_->getBit(tuple));
DEBUG_ASSERT(relation_.hasAttributeWithId(attr));
const Type &attr_type = relation_.getAttributeById(attr)->getType();
const char *tuple_slot = static_cast<const char*>(tuple_storage_)
+ tuple_slot_bytes_ * tuple;
const int nullable_idx = relation_.getNullableAttributeIndex(attr);
if (nullable_idx != -1) {
// const_cast is safe here. We will only be using read-only methods of
// BitVector.
BitVector<true> tuple_null_bitmap(const_cast<void*>(static_cast<const void*>(tuple_slot)),
relation_.numNullableAttributes());
if (tuple_null_bitmap.getBit(nullable_idx)) {
return attr_type.makeNullValue();
}
}
const int variable_length_idx = relation_.getVariableLengthAttributeIndex(attr);
if (variable_length_idx == -1) {
// Fixed-length, stored in-line in slot.
return attr_type.makeValue(
tuple_slot + per_tuple_null_bitmap_bytes_ + relation_.getFixedLengthAttributeOffset(attr),
attr_type.maximumByteLength());
} else {
// Variable-length, stored at back of block.
const std::uint32_t *pos_ptr = reinterpret_cast<const std::uint32_t*>(
tuple_slot + per_tuple_null_bitmap_bytes_
+ relation_.getFixedByteLength()
+ variable_length_idx * 2 * sizeof(std::uint32_t));
return attr_type.makeValue(static_cast<const char*>(tuple_storage_) + pos_ptr[0],
pos_ptr[1]);
}
}
ValueAccessor* SplitRowStoreTupleStorageSubBlock::createValueAccessor(
const TupleIdSequence *sequence) const {
SplitRowStoreValueAccessor *base_accessor
= new SplitRowStoreValueAccessor(relation_,
header_->num_tuples,
header_->max_tid,
*occupancy_bitmap_,
tuple_storage_,
tuple_slot_bytes_,
per_tuple_null_bitmap_bytes_);
if (sequence == nullptr) {
return base_accessor;
} else {
return new TupleIdSequenceAdapterValueAccessor<SplitRowStoreValueAccessor>(
base_accessor,
*sequence);
}
}
bool SplitRowStoreTupleStorageSubBlock::canSetAttributeValuesInPlaceTyped(
const tuple_id tuple,
const std::unordered_map<attribute_id, TypedValue> &new_values) const {
DEBUG_ASSERT(hasTupleWithID(tuple));
if (!relation_.isVariableLength()) {
return true;
}
const void *tuple_slot = static_cast<const char*>(tuple_storage_) + tuple * tuple_slot_bytes_;
// const_cast is safe. Only read-only methods of BitVector are used here.
BitVector<true> tuple_null_bitmap(const_cast<void*>(tuple_slot),
relation_.numNullableAttributes());
const std::uint32_t *variable_length_info_array = reinterpret_cast<const std::uint32_t*>(
static_cast<const char*>(tuple_slot)
+ per_tuple_null_bitmap_bytes_
+ relation_.getFixedByteLength());
std::size_t extra_variable_bytes = 0;
for (const std::pair<const attribute_id, TypedValue> &update_pair : new_values) {
int nullable_idx = relation_.getNullableAttributeIndex(update_pair.first);
bool original_null = false;
if (nullable_idx != -1) {
if (update_pair.second.isNull()) {
// If new attribute value is NULL, no extra storage is needed.
continue;
}
original_null = tuple_null_bitmap.getBit(nullable_idx);
}
int variable_idx = relation_.getVariableLengthAttributeIndex(update_pair.first);
if (variable_idx == -1) {
// If attribute is fixed-length, it can be overwritten inline in the slot.
continue;
}
const std::size_t update_size = update_pair.second.getDataSize();
if (original_null
|| (update_size > variable_length_info_array[(variable_idx << 1) + 1])) {
// If value's size is less than or equal to the existing value, we can
// overwrite it in place. We only need to allocate more storage if it is
// larger.
extra_variable_bytes += update_size;
}
}
if ((header_->max_tid + 1) * tuple_slot_bytes_
+ header_->variable_length_bytes_allocated
+ extra_variable_bytes
> tuple_storage_bytes_) {
return false;
}
return true;
}
void SplitRowStoreTupleStorageSubBlock::setAttributeValueInPlaceTyped(
const tuple_id tuple,
const attribute_id attr,
const TypedValue &value) {
DEBUG_ASSERT(hasTupleWithID(tuple));
DEBUG_ASSERT(relation_.hasAttributeWithId(attr));
void *tuple_slot = static_cast<char*>(tuple_storage_) + tuple * tuple_slot_bytes_;
const int nullable_idx = relation_.getNullableAttributeIndex(attr);
bool original_null = false;
if (nullable_idx != -1) {
// Set bit in null bitmap.
BitVector<true> tuple_null_bitmap(tuple_slot, relation_.numNullableAttributes());
original_null = tuple_null_bitmap.getBit(nullable_idx);
if (value.isNull()) {
tuple_null_bitmap.setBit(nullable_idx, true);
return;
} else {
tuple_null_bitmap.setBit(nullable_idx, false);
}
}
DEBUG_ASSERT(!value.isNull());
const int variable_length_idx = relation_.getVariableLengthAttributeIndex(attr);
if (variable_length_idx == -1) {
// Copy fixed-length value into inline position in slot.
value.copyInto(static_cast<char*>(tuple_slot)
+ per_tuple_null_bitmap_bytes_
+ relation_.getFixedLengthAttributeOffset(attr));
} else {
const std::size_t value_size = value.getDataSize();
std::uint32_t *variable_length_info_array = reinterpret_cast<std::uint32_t*>(
static_cast<char*>(tuple_slot)
+ per_tuple_null_bitmap_bytes_
+ relation_.getFixedByteLength());
if (original_null
|| (value_size > variable_length_info_array[(variable_length_idx << 1) + 1])) {
// Allocate more space.
DEBUG_ASSERT((header_->max_tid + 1) * tuple_slot_bytes_
+ header_->variable_length_bytes_allocated
+ value_size
<= tuple_storage_bytes_);
header_->variable_length_bytes_allocated += value_size;
// Point to the newly-allocated location.
variable_length_info_array[variable_length_idx << 1]
= tuple_storage_bytes_ - header_->variable_length_bytes_allocated;
header_->variable_length_storage_compact = false;
} else if (value_size < variable_length_info_array[(variable_length_idx << 1) + 1]) {
// If we are overwriting in place and using less than exactly the same
// number of bytes as the original value, storage is no longer compact.
header_->variable_length_storage_compact = false;
}
// Actually copy the value into variable-length storage.
value.copyInto(static_cast<char*>(tuple_storage_)
+ variable_length_info_array[variable_length_idx << 1]);
// Update the size slot.
variable_length_info_array[(variable_length_idx << 1) + 1] = value_size;
}
}
bool SplitRowStoreTupleStorageSubBlock::deleteTuple(const tuple_id tuple) {
DEBUG_ASSERT(hasTupleWithID(tuple));
occupancy_bitmap_->setBit(tuple, false);
--(header_->num_tuples);
if (header_->num_tuples) {
if (tuple == header_->max_tid) {
header_->max_tid = occupancy_bitmap_->lastOne();
// Since 'header_->num_tuples' is nonzero, there should be at least one
// bit still set in '*occupancy_bitmap_'.
DEBUG_ASSERT(static_cast<std::size_t>(header_->max_tid) != occupancy_bitmap_->size());
}
header_->variable_length_storage_compact = false;
} else {
header_->max_tid = -1;
header_->variable_length_bytes_allocated = 0;
header_->variable_length_storage_compact = true;
}
return false;
}
bool SplitRowStoreTupleStorageSubBlock::bulkDeleteTuples(TupleIdSequence *tuples) {
occupancy_bitmap_->unsetFrom(tuples->getInternalBitVector());
header_->num_tuples = occupancy_bitmap_->onesCount();
if (header_->num_tuples) {
header_->max_tid = occupancy_bitmap_->lastOne();
header_->variable_length_storage_compact = false;
} else {
header_->max_tid = -1;
header_->variable_length_bytes_allocated = 0;
header_->variable_length_storage_compact = true;
}
return false;
}
OrderedTupleIdSequence* SplitRowStoreTupleStorageSubBlock::getExistenceList() const {
if (isPacked()) {
return TupleStorageSubBlock::getExistenceList();
}
OrderedTupleIdSequence *existence_list = new OrderedTupleIdSequence();
existence_list->reserve(header_->num_tuples);
for (std::size_t pos = occupancy_bitmap_->firstOne();
pos < occupancy_bitmap_->size();
occupancy_bitmap_->firstOne(pos + 1)) {
existence_list->push_back(pos);
}
return existence_list;
}
void SplitRowStoreTupleStorageSubBlock::rebuild() {
// First, pack all tuple slots in the front of the block.
if (!isPacked()) {
// The packing algorithm scans for holes starting at the beginning of the
// slot array and fills them with tuples taken from the back. This
// minimizes the total number of moves.
std::size_t dest = occupancy_bitmap_->firstZero();
std::size_t src = occupancy_bitmap_->lastOne(header_->max_tid + 1);
while (dest < static_cast<std::size_t>(header_->num_tuples)) {
std::memcpy(static_cast<char*>(tuple_storage_) + dest * tuple_slot_bytes_,
static_cast<char*>(tuple_storage_) + src * tuple_slot_bytes_,
tuple_slot_bytes_);
dest = occupancy_bitmap_->firstZero(dest + 1);
src = occupancy_bitmap_->lastOne(src);
}
// Update header and reset occupancy bitmap.
header_->max_tid = header_->num_tuples - 1;
occupancy_bitmap_->clear();
occupancy_bitmap_->setBitRange(0, header_->num_tuples, true);
}
if (relation_.isVariableLength()
&& !header_->variable_length_storage_compact
&& (header_->variable_length_bytes_allocated != 0)) {
// Some variable-length storage is used, but it has holes.
// Allocate a temporary buffer to store packed variable-length values.
ScopedBuffer packed_buffer(header_->variable_length_bytes_allocated);
std::size_t buffer_pos = 0;
for (tuple_id tid = 0; tid < header_->num_tuples; ++tid) {
void *tuple_slot = static_cast<char*>(tuple_storage_) + tid * tuple_slot_bytes_;
BitVector<true> tuple_null_bitmap(tuple_slot, relation_.numNullableAttributes());
std::uint32_t *variable_length_info_array = reinterpret_cast<std::uint32_t*>(
static_cast<char*>(tuple_slot)
+ per_tuple_null_bitmap_bytes_
+ relation_.getFixedByteLength());
for (CatalogRelationSchema::const_iterator attr_it = relation_.begin();
attr_it != relation_.end();
++attr_it) {
const int nullable_idx = relation_.getNullableAttributeIndex(attr_it->getID());
if ((nullable_idx != -1) && tuple_null_bitmap.getBit(nullable_idx)) {
// Nothing to copy for null values.
continue;
}
const int variable_idx = relation_.getVariableLengthAttributeIndex(attr_it->getID());
if (variable_idx != -1) {
// Copy into the temporary buffer.
std::memcpy(static_cast<char*>(packed_buffer.get()) + buffer_pos,
static_cast<char*>(tuple_storage_)
+ variable_length_info_array[variable_idx << 1],
variable_length_info_array[(variable_idx << 1) + 1]);
// Temporarily reset the offset entry in the slot to the offset in
// the temp buffer (will be corrected in a second pass below).
variable_length_info_array[variable_idx << 1] = buffer_pos;
buffer_pos += variable_length_info_array[(variable_idx << 1) + 1];
}
}
}
if (buffer_pos != 0) {
// Copy temporary packing buffer back into block.
const std::uint32_t variable_start = tuple_storage_bytes_ - buffer_pos;
std::memcpy(static_cast<char*>(tuple_storage_) + variable_start,
packed_buffer.get(),
buffer_pos);
// Correct offsets of variable-length attribute storage to point to the
// actual location in the block.
for (tuple_id tid = 0; tid < header_->num_tuples; ++tid) {
void *tuple_slot = static_cast<char*>(tuple_storage_) + tid * tuple_slot_bytes_;
std::uint32_t *variable_length_info_array = reinterpret_cast<std::uint32_t*>(
static_cast<char*>(tuple_slot)
+ per_tuple_null_bitmap_bytes_
+ relation_.getFixedByteLength());
// Note that we skip null checks, as doing arithmetic on meaningless
// offsets for null values is harmless and avoids a branch.
for (std::size_t variable_idx = 0;
variable_idx < relation_.numVariableLengthAttributes();
++variable_idx) {
variable_length_info_array[variable_idx << 1] += variable_start;
}
}
}
header_->variable_length_bytes_allocated = buffer_pos;
}
header_->variable_length_storage_compact = true;
}
template <bool nullable_attrs, bool variable_length_attrs>
TupleStorageSubBlock::InsertResult SplitRowStoreTupleStorageSubBlock::insertTupleImpl(
const Tuple &tuple) {
tuple_id pos = isPacked() ? header_->num_tuples
: occupancy_bitmap_->firstZero();
if ((pos + 1) * tuple_slot_bytes_ + header_->variable_length_bytes_allocated > tuple_storage_bytes_) {
// Early check: if tuple would cause us to run out of space without even
// counting variable length storage, fail immediately.
return InsertResult(-1, false);
}
std::size_t variable_length_value_pos = 0;
// Calculate the required variable-length storage.
if (variable_length_attrs) {
std::size_t total_variable_length_bytes = 0;
Tuple::const_iterator value_it = tuple.begin();
CatalogRelationSchema::const_iterator attr_it = relation_.begin();
for (; value_it != tuple.end(); ++value_it, ++attr_it) {
if (nullable_attrs && value_it->isNull()) {
continue;
} else if (attr_it->getType().isVariableLength()) {
total_variable_length_bytes += value_it->getDataSize();
}
}
if (!spaceToInsert(pos, total_variable_length_bytes)) {
return InsertResult(-1, false);
}
// Allocate variable-length storage.
header_->variable_length_bytes_allocated += total_variable_length_bytes;
variable_length_value_pos = tuple_storage_bytes_ - header_->variable_length_bytes_allocated;
}
// Locate the slot and its sub-structures.
void *tuple_slot = static_cast<char*>(tuple_storage_) + pos * tuple_slot_bytes_;
BitVector<true> tuple_null_bitmap(tuple_slot,
relation_.numNullableAttributes());
tuple_null_bitmap.clear();
char *fixed_length_attr_storage = static_cast<char*>(tuple_slot) + per_tuple_null_bitmap_bytes_;
std::uint32_t *variable_length_info_array
= variable_length_attrs ? reinterpret_cast<std::uint32_t*>(
fixed_length_attr_storage + relation_.getFixedByteLength())
: nullptr;
Tuple::const_iterator value_it = tuple.begin();
CatalogRelationSchema::const_iterator attr_it = relation_.begin();
for (; value_it != tuple.end(); ++value_it, ++attr_it) {
const attribute_id attr_id = attr_it->getID();
if (nullable_attrs) {
// Set null bit if needed.
const int nullable_idx = relation_.getNullableAttributeIndex(attr_id);
if ((nullable_idx != -1) && value_it->isNull()) {
tuple_null_bitmap.setBit(nullable_idx, true);
continue;
}
}
if (variable_length_attrs) {
const int variable_length_idx = relation_.getVariableLengthAttributeIndex(attr_id);
if (variable_length_idx != -1) {
// If attribute is variable-length, point to it and record its size
// in the slot, then copy it into the variable-length storage region.
const std::size_t data_size = value_it->getDataSize();
variable_length_info_array[variable_length_idx << 1] = variable_length_value_pos;
variable_length_info_array[(variable_length_idx << 1) + 1] = data_size;
value_it->copyInto(static_cast<char*>(tuple_storage_) + variable_length_value_pos);
variable_length_value_pos += data_size;
continue;
}
}
// Not null or variable-length, so copy directly into slot.
value_it->copyInto(fixed_length_attr_storage + relation_.getFixedLengthAttributeOffset(attr_id));
}
++(header_->num_tuples);
if (pos > header_->max_tid) {
header_->max_tid = pos;
}
occupancy_bitmap_->setBit(pos, true);
return InsertResult(pos, false);
}
} // namespace quickstep