| /** |
| * 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/CompressedTupleStorageSubBlock.hpp" |
| |
| #include <cmath> |
| #include <cstddef> |
| #include <cstdint> |
| #include <limits> |
| #include <utility> |
| #include <vector> |
| |
| #include "catalog/CatalogRelationSchema.hpp" |
| #include "catalog/CatalogTypedefs.hpp" |
| #include "compression/CompressionDictionary.hpp" |
| #include "compression/CompressionDictionaryLite.hpp" |
| #include "expressions/predicate/ComparisonPredicate.hpp" |
| #include "expressions/predicate/Predicate.hpp" |
| #include "expressions/scalar/Scalar.hpp" |
| #include "expressions/scalar/ScalarAttribute.hpp" |
| #include "storage/CompressedBlockBuilder.hpp" |
| #include "storage/CompressedStoreUtil.hpp" |
| #include "storage/StorageBlockLayout.pb.h" |
| #include "storage/StorageErrors.hpp" |
| #include "storage/TupleIdSequence.hpp" |
| #include "storage/ValueAccessor.hpp" |
| #include "storage/ValueAccessorUtil.hpp" |
| #include "types/TypedValue.hpp" |
| #include "types/containers/Tuple.hpp" |
| #include "types/operations/comparisons/ComparisonID.hpp" |
| #include "utility/Macros.hpp" |
| |
| using std::ceil; |
| using std::floor; |
| using std::int64_t; |
| using std::numeric_limits; |
| using std::pair; |
| using std::size_t; |
| using std::uint32_t; |
| |
| namespace quickstep { |
| |
| CompressedTupleStorageSubBlock::CompressedTupleStorageSubBlock( |
| 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) { |
| if (new_block) { |
| if (sub_block_memory_size_ < sizeof(tuple_id) + sizeof(int)) { |
| throw BlockMemoryTooSmall("CompressedTupleStorageSubBlock", |
| sub_block_memory_size_); |
| } |
| |
| *static_cast<tuple_id*>(sub_block_memory_) = 0; |
| builder_.reset(new CompressedBlockBuilder(relation_, description_, sub_block_memory_size_)); |
| if (builder_->getMinimumRequiredBlockSize() > sub_block_memory_size_) { |
| throw BlockMemoryTooSmall("CompressedTupleStorageSubBlock", |
| sub_block_memory_size_); |
| } |
| } else { |
| if (sub_block_memory_size_ < sizeof(tuple_id) + sizeof(int)) { |
| throw MalformedBlock(); |
| } |
| if (*reinterpret_cast<const int*>(static_cast<const char*>(sub_block_memory_) + sizeof(tuple_id)) <= 0) { |
| throw MalformedBlock(); |
| } |
| if (*reinterpret_cast<const int*>(static_cast<const char*>(sub_block_memory_) + sizeof(tuple_id)) |
| + sizeof(int) + sizeof(tuple_id) |
| > sub_block_memory_size_) { |
| throw MalformedBlock(); |
| } |
| |
| if (*static_cast<tuple_id*>(sub_block_memory_) == 0) { |
| builder_.reset(new CompressedBlockBuilder(relation_, description_, sub_block_memory_size_)); |
| if (builder_->getMinimumRequiredBlockSize() > sub_block_memory_size_) { |
| throw MalformedBlock(); |
| } |
| } |
| } |
| } |
| |
| std::int64_t CompressedTupleStorageSubBlock::GetEffectiveLiteralValueForComparisonWithTruncatedAttribute( |
| const ComparisonID comp, |
| const TypedValue &right_literal, |
| const Type &right_literal_type) { |
| return CompressedAttributePredicateTransformer::GetEffectiveLiteralValueForComparisonWithTruncatedAttribute( |
| comp, right_literal, right_literal_type); |
| } |
| |
| bool CompressedTupleStorageSubBlock::insertTupleInBatch( |
| const Tuple &tuple) { |
| #ifdef QUICKSTEP_DEBUG |
| paranoidInsertTypeCheck(tuple); |
| #endif |
| |
| if (builder_.get() == nullptr) { |
| return false; |
| } |
| |
| return builder_->addTuple(tuple); |
| } |
| |
| tuple_id CompressedTupleStorageSubBlock::bulkInsertTuples( |
| ValueAccessor *accessor) { |
| tuple_id inserted_count = 0; |
| InvokeOnAnyValueAccessor( |
| accessor, |
| [&](auto *accessor) -> void { // NOLINT(build/c++11) |
| while (accessor->next()) { |
| if (!builder_->takeTuple(accessor->getTuple())) { |
| accessor->previous(); |
| break; |
| } |
| ++inserted_count; |
| } |
| }); |
| return inserted_count; |
| } |
| |
| tuple_id CompressedTupleStorageSubBlock::bulkInsertTuplesWithRemappedAttributes( |
| const std::vector<attribute_id> &attribute_map, |
| ValueAccessor *accessor) { |
| tuple_id inserted_count = 0; |
| InvokeOnAnyValueAccessor( |
| accessor, |
| [&](auto *accessor) -> void { // NOLINT(build/c++11) |
| while (accessor->next()) { |
| if (!builder_->takeTuple(accessor->getTupleWithAttributes(attribute_map))) { |
| accessor->previous(); |
| break; |
| } |
| ++inserted_count; |
| } |
| }); |
| return inserted_count; |
| } |
| |
| TupleIdSequence* CompressedTupleStorageSubBlock::getMatchesForPredicate( |
| const ComparisonPredicate &predicate, |
| const TupleIdSequence *filter) const { |
| DEBUG_ASSERT(builder_.get() == nullptr); |
| |
| // Determine if the predicate is a comparison of a compressed attribute with |
| // a literal. |
| if (predicate.isAttributeLiteralComparisonPredicate()) { |
| const CatalogAttribute *comparison_attribute = NULL; |
| if (predicate.getLeftOperand().hasStaticValue()) { |
| DEBUG_ASSERT(predicate.getRightOperand().getDataSource() == Scalar::kAttribute); |
| comparison_attribute |
| = &(static_cast<const ScalarAttribute&>(predicate.getRightOperand()).getAttribute()); |
| } else { |
| DEBUG_ASSERT(predicate.getLeftOperand().getDataSource() == Scalar::kAttribute); |
| comparison_attribute |
| = &(static_cast<const ScalarAttribute&>(predicate.getLeftOperand()).getAttribute()); |
| } |
| const attribute_id comparison_attribute_id = comparison_attribute->getID(); |
| |
| DEBUG_ASSERT(comparison_attribute->getParent().getID() == relation_.getID()); |
| if (dictionary_coded_attributes_[comparison_attribute_id] |
| || truncated_attributes_[comparison_attribute_id]) { |
| const CompressionDictionary *dictionary = nullptr; |
| if (dictionary_coded_attributes_[comparison_attribute_id]) { |
| dictionary = static_cast<const CompressionDictionary*>( |
| dictionaries_.find(comparison_attribute_id)->second); |
| } |
| PredicateTransformResult result |
| = CompressedAttributePredicateTransformer::TransformPredicateOnCompressedAttribute( |
| relation_, |
| predicate, |
| dictionary, |
| GetMaxTruncatedValue(compression_info_.attribute_size(comparison_attribute_id))); |
| |
| pair<uint32_t, uint32_t> match_range; |
| switch (result.type) { |
| case PredicateTransformResultType::kAll: |
| if (filter == nullptr) { |
| return getExistenceMap(); |
| } else { |
| TupleIdSequence *filter_copy = new TupleIdSequence(filter->length()); |
| filter_copy->assignFrom(*filter); |
| return filter_copy; |
| } |
| // Pass through to base version to get all tuples. |
| return TupleStorageSubBlock::getMatchesForPredicate(predicate, filter); |
| case PredicateTransformResultType::kNone: |
| // No matches. |
| return new TupleIdSequence(*static_cast<const tuple_id*>(sub_block_memory_)); |
| case PredicateTransformResultType::kBasicComparison: |
| switch (result.comp) { |
| case ComparisonID::kEqual: |
| return getEqualCodes(comparison_attribute_id, result.first_literal, filter); |
| case ComparisonID::kNotEqual: |
| if (result.exclude_nulls) { |
| return getNotEqualCodesExcludingNull(comparison_attribute_id, |
| result.first_literal, |
| result.second_literal, |
| filter); |
| } else { |
| return getNotEqualCodes(comparison_attribute_id, |
| result.first_literal, |
| filter); |
| } |
| case ComparisonID::kLess: |
| return getLessCodes(comparison_attribute_id, result.first_literal, filter); |
| case ComparisonID::kGreaterOrEqual: |
| return getGreaterOrEqualCodes(comparison_attribute_id, |
| result.first_literal, |
| filter); |
| default: |
| FATAL_ERROR("Unexpected ComparisonID in CompressedTupleStorageSubBlock::" |
| "getMatchesForPredicate()"); |
| } |
| case PredicateTransformResultType::kRangeComparison: |
| match_range.first = result.first_literal; |
| match_range.second = result.second_literal; |
| return getCodesInRange(comparison_attribute_id, match_range, filter); |
| default: |
| FATAL_ERROR("Unexpected PredicateTransformResultType in CompressedTupleStorageSubBlock::" |
| "getMatchesForPredicate()"); |
| } |
| } else { |
| FATAL_ERROR("Called CompressedTupleStorageSubBlock::getMatchesForPredicate() " |
| "with a predicate that can only be evaluated with a simple scan."); |
| } |
| } else { |
| FATAL_ERROR("Called CompressedTupleStorageSubBlock::getMatchesForPredicate() " |
| "with a predicate that can only be evaluated with a simple scan."); |
| } |
| } |
| |
| bool CompressedTupleStorageSubBlock::compressedComparisonIsAlwaysTrueForTruncatedAttribute( |
| const ComparisonID comp, |
| const attribute_id left_attr_id, |
| const TypedValue &right_literal, |
| const Type &right_literal_type) const { |
| DEBUG_ASSERT(truncated_attributes_[left_attr_id]); |
| |
| return CompressedAttributePredicateTransformer::CompressedComparisonIsAlwaysTrueForTruncatedAttribute( |
| comp, |
| right_literal, |
| right_literal_type, |
| GetMaxTruncatedValue(compression_info_.attribute_size(left_attr_id))); |
| } |
| |
| bool CompressedTupleStorageSubBlock::compressedComparisonIsAlwaysFalseForTruncatedAttribute( |
| const ComparisonID comp, |
| const attribute_id left_attr_id, |
| const TypedValue &right_literal, |
| const Type &right_literal_type) const { |
| DEBUG_ASSERT(truncated_attributes_[left_attr_id]); |
| |
| return CompressedAttributePredicateTransformer::CompressedComparisonIsAlwaysFalseForTruncatedAttribute( |
| comp, |
| right_literal, |
| right_literal_type, |
| GetMaxTruncatedValue(compression_info_.attribute_size(left_attr_id))); |
| } |
| |
| void* CompressedTupleStorageSubBlock::initializeCommon() { |
| if (!compression_info_.ParseFromArray( |
| static_cast<const char*>(sub_block_memory_) + sizeof(tuple_id) + sizeof(int), |
| *reinterpret_cast<const int*>(static_cast<const char*>(sub_block_memory_) + sizeof(tuple_id)))) { |
| throw MalformedBlock(); |
| } |
| |
| if ((relation_.getMaxAttributeId() + 1 != compression_info_.attribute_size_size()) |
| || (relation_.getMaxAttributeId() + 1 != compression_info_.dictionary_size_size())) { |
| throw MalformedBlock(); |
| } |
| |
| dictionary_coded_attributes_.resize(relation_.getMaxAttributeId() + 1, false); |
| truncated_attributes_.resize(relation_.getMaxAttributeId() + 1, false); |
| truncated_attribute_is_int_.resize(relation_.getMaxAttributeId() + 1, false); |
| size_t dictionary_offset = |
| sizeof(tuple_id) + sizeof(int) |
| + *reinterpret_cast<const int*>(static_cast<const char*>(sub_block_memory_) + sizeof(tuple_id)); |
| for (CatalogRelationSchema::const_iterator attr_it = relation_.begin(); |
| attr_it != relation_.end(); |
| ++attr_it) { |
| const Type &attr_type = attr_it->getType(); |
| if (attr_type.isVariableLength()) { |
| if (compression_info_.dictionary_size(attr_it->getID()) == 0) { |
| throw MalformedBlock(); |
| } |
| } |
| |
| if (compression_info_.dictionary_size(attr_it->getID()) > 0) { |
| dictionaries_.insert( |
| attr_it->getID(), |
| new CompressionDictionary( |
| attr_type, |
| static_cast<const char*>(sub_block_memory_) + dictionary_offset, |
| compression_info_.dictionary_size(attr_it->getID()))); |
| |
| dictionary_coded_attributes_[attr_it->getID()] = true; |
| dictionary_offset += compression_info_.dictionary_size(attr_it->getID()); |
| } else if (compression_info_.attribute_size(attr_it->getID()) |
| != attr_type.maximumByteLength()) { |
| switch (attr_type.getTypeID()) { |
| case kInt: |
| case kLong: |
| switch (compression_info_.attribute_size(attr_it->getID())) { |
| case 1: |
| case 2: |
| case 4: |
| truncated_attributes_[attr_it->getID()] = true; |
| truncated_attribute_is_int_[attr_it->getID()] = (attr_type.getTypeID() == kInt); |
| break; |
| default: |
| throw MalformedBlock(); |
| } |
| break; |
| default: |
| throw MalformedBlock(); |
| } |
| } |
| } |
| |
| return static_cast<char*>(sub_block_memory_) + dictionary_offset; |
| } |
| |
| } // namespace quickstep |