| /** |
| * 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 <cstring> |
| #include <memory> |
| #include <sstream> |
| #include <string> |
| #include <unordered_map> |
| #include <utility> |
| #include <vector> |
| |
| #include "gtest/gtest.h" |
| |
| #include "catalog/CatalogAttribute.hpp" |
| #include "catalog/CatalogRelation.hpp" |
| #include "storage/PackedRowStoreTupleStorageSubBlock.hpp" |
| #include "storage/StorageBlockInfo.hpp" |
| #include "storage/StorageBlockLayout.hpp" |
| #include "storage/StorageBlockLayout.pb.h" |
| #include "storage/StorageConstants.hpp" |
| #include "storage/StorageErrors.hpp" |
| #include "storage/TupleIdSequence.hpp" |
| #include "storage/TupleStorageSubBlock.hpp" |
| #include "types/CharType.hpp" |
| #include "types/DoubleType.hpp" |
| #include "types/IntType.hpp" |
| #include "types/Type.hpp" |
| #include "types/TypeFactory.hpp" |
| #include "types/TypedValue.hpp" |
| #include "types/TypeID.hpp" |
| #include "types/containers/Tuple.hpp" |
| #include "types/operations/comparisons/Comparison.hpp" |
| #include "types/operations/comparisons/ComparisonFactory.hpp" |
| #include "types/operations/comparisons/ComparisonID.hpp" |
| #include "utility/BitVector.hpp" |
| #include "utility/ScopedBuffer.hpp" |
| |
| using std::make_pair; |
| using std::ostringstream; |
| using std::pair; |
| |
| namespace quickstep { |
| |
| class PackedRowStoreTupleStorageSubBlockTest : public ::testing::TestWithParam<bool> { |
| protected: |
| static const size_t kSubBlockSize = 0x100000; // 1 MB |
| static const size_t kTupleLength = 24; |
| |
| virtual void SetUp() { |
| // Create a sample relation with a variety of attribute types. |
| relation_.reset(new CatalogRelation(nullptr, "TestRelation")); |
| |
| // An integer. |
| CatalogAttribute *current_attr = new CatalogAttribute(relation_.get(), |
| "int_attr", |
| TypeFactory::GetType(kInt, GetParam())); |
| ASSERT_EQ(0, relation_->addAttribute(current_attr)); |
| eq_comp_int_.reset(ComparisonFactory::GetComparison(ComparisonID::kEqual).makeUncheckedComparatorForTypes( |
| current_attr->getType(), |
| current_attr->getType())); |
| |
| // A double. |
| current_attr = new CatalogAttribute(relation_.get(), |
| "double_attr", |
| TypeFactory::GetType(kDouble, GetParam())); |
| ASSERT_EQ(1, relation_->addAttribute(current_attr)); |
| eq_comp_double_.reset(ComparisonFactory::GetComparison(ComparisonID::kEqual).makeUncheckedComparatorForTypes( |
| current_attr->getType(), |
| current_attr->getType())); |
| |
| // A string. |
| current_attr = new CatalogAttribute(relation_.get(), |
| "char_attr", |
| TypeFactory::GetType(kChar, 12, GetParam())); |
| ASSERT_EQ(2, relation_->addAttribute(current_attr)); |
| eq_comp_char_.reset(ComparisonFactory::GetComparison(ComparisonID::kEqual).makeUncheckedComparatorForTypes( |
| current_attr->getType(), |
| current_attr->getType())); |
| |
| tuple_store_description_.reset(new TupleStorageSubBlockDescription()); |
| tuple_store_description_->set_sub_block_type(TupleStorageSubBlockDescription::PACKED_ROW_STORE); |
| |
| // Don't initialize the block yet. Different tests will use different |
| // params. |
| tuple_store_memory_.reset(); |
| tuple_store_.reset(); |
| } |
| |
| void initializeNewBlock(const size_t block_size) { |
| tuple_store_memory_.reset(block_size); |
| tuple_store_.reset(new PackedRowStoreTupleStorageSubBlock(*relation_, |
| *tuple_store_description_, |
| true, |
| tuple_store_memory_.get(), |
| block_size)); |
| } |
| |
| int computeRowCapacity() { |
| if (GetParam()) { |
| // Estimate using the same heuristic as |
| // PackedRowStoreTupleStorageSubBlock's constructor. |
| int row_capacity = ((kSubBlockSize - sizeof(tuple_id)) << 3) |
| / ((kTupleLength << 3) + relation_->numNullableAttributes()); |
| const size_t null_bitmap_bytes_ = BitVector<false>::BytesNeeded( |
| row_capacity * relation_->numNullableAttributes()); |
| return static_cast<int>(kSubBlockSize - sizeof(tuple_id) - null_bitmap_bytes_) / kTupleLength; |
| } else { |
| return static_cast<int>(kSubBlockSize - sizeof(tuple_id)) / kTupleLength; |
| } |
| } |
| |
| // Caller takes ownership of new heap-created Tuple. |
| Tuple* createSampleTuple(const int base_value) const { |
| std::vector<TypedValue> attrs; |
| |
| // int_attr |
| if (GetParam() && (base_value % 6 == 0)) { |
| // Throw in a NULL integer for every sixth value. |
| attrs.emplace_back(kInt); |
| } else { |
| attrs.emplace_back(base_value); |
| } |
| |
| // double_attr |
| if (GetParam() && (base_value % 6 == 2)) { |
| // NULL very sixth value. |
| attrs.emplace_back(kDouble); |
| } else { |
| attrs.emplace_back(static_cast<double>(0.25 * base_value)); |
| } |
| |
| // char_attr |
| if (GetParam() && (base_value % 6 == 4)) { |
| // NULL very sixth value. |
| attrs.emplace_back(CharType::InstanceNullable(12).makeNullValue()); |
| } else { |
| ostringstream char_buffer; |
| char_buffer << base_value; |
| std::string string_literal(char_buffer.str()); |
| attrs.emplace_back(CharType::InstanceNonNullable(12).makeValue(string_literal.c_str(), |
| string_literal.size() + 1)); |
| attrs.back().ensureNotReference(); |
| } |
| |
| return new Tuple(std::move(attrs)); |
| } |
| |
| void fillBlockWithSampleData() { |
| tuple_id current_tid = 0; |
| std::unique_ptr<Tuple> current_tuple(createSampleTuple(current_tid)); |
| while (tuple_store_->insertTupleInBatch(*current_tuple)) { |
| ++current_tid; |
| current_tuple.reset(createSampleTuple(current_tid)); |
| } |
| |
| tuple_store_->rebuild(); |
| } |
| |
| void checkTupleValuesUntyped(const tuple_id tid, |
| const int base_value) { |
| ASSERT_TRUE(tuple_store_->hasTupleWithID(tid)); |
| ASSERT_TRUE(tuple_store_->supportsUntypedGetAttributeValue(0)); |
| ASSERT_TRUE(tuple_store_->supportsUntypedGetAttributeValue(1)); |
| ASSERT_TRUE(tuple_store_->supportsUntypedGetAttributeValue(2)); |
| |
| std::unique_ptr<Tuple> comp_tuple(createSampleTuple(base_value)); |
| |
| if (comp_tuple->getAttributeValue(0).isNull()) { |
| assert(nullptr == tuple_store_->getAttributeValue(tid, 0)); |
| // NULL comparisons are always false (for now). |
| EXPECT_FALSE(eq_comp_int_->compareDataPtrs(nullptr, |
| tuple_store_->getAttributeValue(tid, 0))); |
| } else { |
| assert(eq_comp_int_->compareDataPtrs(comp_tuple->getAttributeValue(0).getDataPtr(), |
| tuple_store_->getAttributeValue(tid, 0))); |
| } |
| |
| if (comp_tuple->getAttributeValue(1).isNull()) { |
| EXPECT_EQ(nullptr, tuple_store_->getAttributeValue(tid, 1)); |
| // NULL comparisons are always false (for now). |
| EXPECT_FALSE(eq_comp_double_->compareDataPtrs(nullptr, |
| tuple_store_->getAttributeValue(tid, 1))); |
| } else { |
| EXPECT_TRUE(eq_comp_double_->compareDataPtrs(comp_tuple->getAttributeValue(1).getDataPtr(), |
| tuple_store_->getAttributeValue(tid, 1))); |
| } |
| |
| if (comp_tuple->getAttributeValue(2).isNull()) { |
| EXPECT_EQ(nullptr, tuple_store_->getAttributeValue(tid, 2)); |
| // NULL comparisons are always false (for now). |
| EXPECT_FALSE(eq_comp_char_->compareDataPtrs(nullptr, |
| tuple_store_->getAttributeValue(tid, 2))); |
| } else { |
| EXPECT_TRUE(eq_comp_char_->compareDataPtrs(comp_tuple->getAttributeValue(2).getDataPtr(), |
| tuple_store_->getAttributeValue(tid, 2))); |
| } |
| } |
| |
| void checkTupleValuesTyped(const tuple_id tid, |
| const int base_value) { |
| ASSERT_TRUE(tuple_store_->hasTupleWithID(tid)); |
| |
| std::unique_ptr<Tuple> comp_tuple(createSampleTuple(base_value)); |
| |
| if (comp_tuple->getAttributeValue(0).isNull()) { |
| EXPECT_TRUE(tuple_store_->getAttributeValueTyped(tid, 0).isNull()); |
| // NULL comparisons are always false (for now). |
| EXPECT_FALSE(eq_comp_int_->compareTypedValues(comp_tuple->getAttributeValue(0), |
| tuple_store_->getAttributeValueTyped(tid, 0))); |
| } else { |
| EXPECT_TRUE(eq_comp_int_->compareTypedValues(comp_tuple->getAttributeValue(0), |
| tuple_store_->getAttributeValueTyped(tid, 0))); |
| } |
| |
| if (comp_tuple->getAttributeValue(1).isNull()) { |
| EXPECT_TRUE(tuple_store_->getAttributeValueTyped(tid, 1).isNull()); |
| // NULL comparisons are always false (for now). |
| EXPECT_FALSE(eq_comp_double_->compareTypedValues(comp_tuple->getAttributeValue(1), |
| tuple_store_->getAttributeValueTyped(tid, 1))); |
| } else { |
| EXPECT_TRUE(eq_comp_double_->compareTypedValues(comp_tuple->getAttributeValue(1), |
| tuple_store_->getAttributeValueTyped(tid, 1))); |
| } |
| |
| if (comp_tuple->getAttributeValue(2).isNull()) { |
| EXPECT_TRUE(tuple_store_->getAttributeValueTyped(tid, 2).isNull()); |
| // NULL comparisons are always false (for now). |
| EXPECT_FALSE(eq_comp_char_->compareTypedValues(comp_tuple->getAttributeValue(2), |
| tuple_store_->getAttributeValueTyped(tid, 2))); |
| } else { |
| EXPECT_TRUE(eq_comp_char_->compareTypedValues(comp_tuple->getAttributeValue(2), |
| tuple_store_->getAttributeValueTyped(tid, 2))); |
| } |
| } |
| |
| std::unique_ptr<CatalogRelation> relation_; |
| ScopedBuffer tuple_store_memory_; |
| std::unique_ptr<TupleStorageSubBlockDescription> tuple_store_description_; |
| std::unique_ptr<PackedRowStoreTupleStorageSubBlock> tuple_store_; |
| |
| std::unique_ptr<UncheckedComparator> eq_comp_int_; |
| std::unique_ptr<UncheckedComparator> eq_comp_double_; |
| std::unique_ptr<UncheckedComparator> eq_comp_char_; |
| }; |
| |
| typedef PackedRowStoreTupleStorageSubBlockTest PackedRowStoreTupleStorageSubBlockDeathTest; |
| |
| TEST_P(PackedRowStoreTupleStorageSubBlockTest, DescriptionIsValidTest) { |
| // The descriptions we use for the other tests should be valid. |
| EXPECT_TRUE(PackedRowStoreTupleStorageSubBlock::DescriptionIsValid(*relation_, |
| *tuple_store_description_)); |
| |
| // An uninitialized description is not valid. |
| tuple_store_description_.reset(new TupleStorageSubBlockDescription()); |
| EXPECT_FALSE(PackedRowStoreTupleStorageSubBlock::DescriptionIsValid(*relation_, |
| *tuple_store_description_)); |
| |
| // A description that specifies the wrong sub_block_type is not valid. |
| tuple_store_description_->set_sub_block_type(TupleStorageSubBlockDescription::BASIC_COLUMN_STORE); |
| EXPECT_FALSE(PackedRowStoreTupleStorageSubBlock::DescriptionIsValid(*relation_, |
| *tuple_store_description_)); |
| |
| // A relation with a nullable attribute is OK. |
| std::unique_ptr<CatalogRelation> nullable_relation(new CatalogRelation(NULL, "nullable_relation")); |
| CatalogAttribute *nullable_attribute = new CatalogAttribute(nullable_relation.get(), |
| "nullable_attr", |
| TypeFactory::GetType(kInt, true)); |
| ASSERT_EQ(0, nullable_relation->addAttribute(nullable_attribute)); |
| tuple_store_description_.reset(new TupleStorageSubBlockDescription()); |
| tuple_store_description_->set_sub_block_type(TupleStorageSubBlockDescription::PACKED_ROW_STORE); |
| EXPECT_TRUE(PackedRowStoreTupleStorageSubBlock::DescriptionIsValid(*nullable_relation, |
| *tuple_store_description_)); |
| |
| // A relation with a variable-length attribute can't be used with this block type. |
| std::unique_ptr<CatalogRelation> variable_length_relation(new CatalogRelation(NULL, "variable_length_relation")); |
| CatalogAttribute *variable_length_attribute = new CatalogAttribute(variable_length_relation.get(), |
| "variable_length_attr", |
| TypeFactory::GetType(kVarChar, 20, false)); |
| ASSERT_EQ(0, variable_length_relation->addAttribute(variable_length_attribute)); |
| EXPECT_FALSE(PackedRowStoreTupleStorageSubBlock::DescriptionIsValid(*variable_length_relation, |
| *tuple_store_description_)); |
| } |
| |
| TEST_P(PackedRowStoreTupleStorageSubBlockDeathTest, ConstructWithInvalidDescriptionTest) { |
| tuple_store_description_.reset(new TupleStorageSubBlockDescription()); |
| tuple_store_description_->set_sub_block_type(TupleStorageSubBlockDescription::BASIC_COLUMN_STORE); |
| EXPECT_DEATH(initializeNewBlock(kSubBlockSize), ""); |
| } |
| |
| TEST_P(PackedRowStoreTupleStorageSubBlockTest, MemoryTooSmallTest) { |
| // 1 byte short. |
| EXPECT_THROW(initializeNewBlock(sizeof(tuple_id) - 1), |
| BlockMemoryTooSmall); |
| } |
| |
| TEST_P(PackedRowStoreTupleStorageSubBlockTest, InsertTest) { |
| initializeNewBlock(kSubBlockSize); |
| |
| int row_capacity = computeRowCapacity(); |
| |
| EXPECT_TRUE(tuple_store_->supportsAdHocInsert()); |
| EXPECT_TRUE(tuple_store_->adHocInsertIsEfficient()); |
| EXPECT_FALSE(tuple_store_->isCompressed()); |
| EXPECT_TRUE(tuple_store_->isEmpty()); |
| |
| std::unique_ptr<Tuple> current_tuple; |
| for (int tuple_num = 0; |
| tuple_num < row_capacity; |
| ++tuple_num) { |
| current_tuple.reset(createSampleTuple(tuple_num)); |
| TupleStorageSubBlock::InsertResult result = tuple_store_->insertTuple(*current_tuple); |
| ASSERT_EQ(tuple_num, result.inserted_id); |
| ASSERT_FALSE(result.ids_mutated); |
| |
| EXPECT_FALSE(tuple_store_->isEmpty()); |
| EXPECT_TRUE(tuple_store_->isPacked()); |
| EXPECT_EQ(tuple_num, tuple_store_->getMaxTupleID()); |
| EXPECT_EQ(tuple_num + 1, tuple_store_->numTuples()); |
| } |
| |
| current_tuple.reset(createSampleTuple(0)); |
| TupleStorageSubBlock::InsertResult result = tuple_store_->insertTuple(*current_tuple); |
| EXPECT_EQ(-1, result.inserted_id); |
| EXPECT_FALSE(result.ids_mutated); |
| |
| EXPECT_TRUE(tuple_store_->isPacked()); |
| EXPECT_EQ(row_capacity - 1, tuple_store_->getMaxTupleID()); |
| EXPECT_EQ(row_capacity, tuple_store_->numTuples()); |
| } |
| |
| TEST_P(PackedRowStoreTupleStorageSubBlockTest, InsertInBatchTest) { |
| initializeNewBlock(kSubBlockSize); |
| |
| int row_capacity = computeRowCapacity(); |
| |
| EXPECT_TRUE(tuple_store_->supportsAdHocInsert()); |
| EXPECT_TRUE(tuple_store_->adHocInsertIsEfficient()); |
| EXPECT_FALSE(tuple_store_->isCompressed()); |
| EXPECT_TRUE(tuple_store_->isEmpty()); |
| |
| std::unique_ptr<Tuple> current_tuple; |
| for (int tuple_num = 0; |
| tuple_num < row_capacity; |
| ++tuple_num) { |
| current_tuple.reset(createSampleTuple(tuple_num)); |
| EXPECT_TRUE(tuple_store_->insertTupleInBatch(*current_tuple)); |
| |
| EXPECT_FALSE(tuple_store_->isEmpty()); |
| EXPECT_TRUE(tuple_store_->isPacked()); |
| EXPECT_EQ(tuple_num, tuple_store_->getMaxTupleID()); |
| EXPECT_EQ(tuple_num + 1, tuple_store_->numTuples()); |
| } |
| |
| current_tuple.reset(createSampleTuple(0)); |
| EXPECT_FALSE(tuple_store_->insertTupleInBatch(*current_tuple)); |
| |
| EXPECT_TRUE(tuple_store_->isPacked()); |
| EXPECT_EQ(row_capacity - 1, tuple_store_->getMaxTupleID()); |
| EXPECT_EQ(row_capacity, tuple_store_->numTuples()); |
| } |
| |
| TEST_P(PackedRowStoreTupleStorageSubBlockTest, GetAttributeValueTest) { |
| initializeNewBlock(kSubBlockSize); |
| fillBlockWithSampleData(); |
| ASSERT_TRUE(tuple_store_->isPacked()); |
| |
| for (tuple_id tid = 0; |
| tid <= tuple_store_->getMaxTupleID(); |
| ++tid) { |
| checkTupleValuesUntyped(tid, tid); |
| } |
| } |
| |
| TEST_P(PackedRowStoreTupleStorageSubBlockTest, GetAttributeValueTypedTest) { |
| initializeNewBlock(kSubBlockSize); |
| fillBlockWithSampleData(); |
| ASSERT_TRUE(tuple_store_->isPacked()); |
| |
| for (tuple_id tid = 0; |
| tid <= tuple_store_->getMaxTupleID(); |
| ++tid) { |
| checkTupleValuesTyped(tid, tid); |
| } |
| } |
| |
| TEST_P(PackedRowStoreTupleStorageSubBlockTest, SetAttributeValueTypedTest) { |
| initializeNewBlock(kSubBlockSize); |
| fillBlockWithSampleData(); |
| ASSERT_TRUE(tuple_store_->isPacked()); |
| |
| // Alter every 16th tuple. |
| for (tuple_id tid = 0; |
| tid <= tuple_store_->getMaxTupleID(); |
| tid += 16) { |
| std::unique_ptr<Tuple> mod_tuple(createSampleTuple(-tid)); |
| |
| std::unordered_map<attribute_id, TypedValue> new_values; |
| new_values.insert(make_pair(0, mod_tuple->getAttributeValue(0))); |
| new_values.insert(make_pair(1, mod_tuple->getAttributeValue(1))); |
| new_values.insert(make_pair(2, mod_tuple->getAttributeValue(2))); |
| ASSERT_TRUE(tuple_store_->canSetAttributeValuesInPlaceTyped(tid, new_values)); |
| |
| tuple_store_->setAttributeValueInPlaceTyped(tid, 0, mod_tuple->getAttributeValue(0)); |
| tuple_store_->setAttributeValueInPlaceTyped(tid, 1, mod_tuple->getAttributeValue(1)); |
| tuple_store_->setAttributeValueInPlaceTyped(tid, 2, mod_tuple->getAttributeValue(2)); |
| } |
| |
| // Check all values. |
| for (tuple_id tid = 0; |
| tid <= tuple_store_->getMaxTupleID(); |
| ++tid) { |
| if (tid & 0xF) { |
| checkTupleValuesTyped(tid, tid); |
| } else { |
| checkTupleValuesTyped(tid, -tid); |
| } |
| } |
| } |
| |
| TEST_P(PackedRowStoreTupleStorageSubBlockTest, DeleteTest) { |
| initializeNewBlock(kSubBlockSize); |
| fillBlockWithSampleData(); |
| ASSERT_TRUE(tuple_store_->isPacked()); |
| |
| tuple_id original_num_tuples = tuple_store_->numTuples(); |
| |
| // Delete the last tuple. |
| EXPECT_FALSE(tuple_store_->deleteTuple(original_num_tuples - 1)); |
| |
| // Delete the first tuple. |
| EXPECT_TRUE(tuple_store_->deleteTuple(0)); |
| |
| // Delete a sequence of tuples. |
| TupleIdSequence delete_sequence(tuple_store_->getMaxTupleID() + 1); |
| for (tuple_id tid = 63; |
| tid <= tuple_store_->getMaxTupleID(); |
| tid += 64) { |
| delete_sequence.set(tid, true); |
| } |
| EXPECT_TRUE(tuple_store_->bulkDeleteTuples(&delete_sequence)); |
| |
| EXPECT_EQ(static_cast<tuple_id>(original_num_tuples - 2 - delete_sequence.numTuples()), |
| tuple_store_->numTuples()); |
| |
| tuple_id remaining_tid = 0; |
| for (tuple_id original_tid = 0; |
| original_tid < (original_num_tuples - 1); |
| ++original_tid) { |
| if (original_tid & 0x3F) { |
| checkTupleValuesUntyped(remaining_tid, original_tid); |
| ++remaining_tid; |
| } |
| } |
| } |
| |
| TEST(PackedRowStoreTupleStorageSubBlockNullTypeTest, NullTypeTest) { |
| // Set up a relation with a single NullType attribute. |
| CatalogRelation test_relation(nullptr, "TestRelation"); |
| CatalogAttribute *nulltype_attr = new CatalogAttribute(&test_relation, |
| "nulltype_attr", |
| TypeFactory::GetType(kNullType, true)); |
| ASSERT_EQ(0, test_relation.addAttribute(nulltype_attr)); |
| |
| // Set up a minimal StorageBlockLayoutDescription. |
| StorageBlockLayoutDescription layout_desc; |
| layout_desc.set_num_slots(1); |
| layout_desc.mutable_tuple_store_description()->set_sub_block_type( |
| TupleStorageSubBlockDescription::PACKED_ROW_STORE); |
| |
| // Check that the description is considered valid. |
| EXPECT_TRUE(StorageBlockLayout::DescriptionIsValid(test_relation, layout_desc)); |
| |
| StorageBlockLayout layout(test_relation, layout_desc); |
| |
| // Construct an actual PackedRowStoreTupleStorageSubBlock. |
| ScopedBuffer tuple_store_memory(kSlotSizeBytes); |
| PackedRowStoreTupleStorageSubBlock tuple_store(test_relation, |
| layout_desc.tuple_store_description(), |
| true, |
| tuple_store_memory.get(), |
| kSlotSizeBytes); |
| |
| // Insert some NullType values. |
| std::vector<TypedValue> attr_values; |
| attr_values.emplace_back(kNullType); |
| Tuple tuple(std::move(attr_values)); |
| |
| for (tuple_id tid = 0; tid < 100; ++tid) { |
| tuple_store.insertTuple(tuple); |
| } |
| |
| EXPECT_EQ(100, tuple_store.numTuples()); |
| |
| // Delete some values. |
| TupleIdSequence delete_sequence(100); |
| delete_sequence.set(5, true); |
| delete_sequence.set(25, true); |
| delete_sequence.set(45, true); |
| delete_sequence.set(65, true); |
| delete_sequence.set(85, true); |
| |
| EXPECT_TRUE(tuple_store.bulkDeleteTuples(&delete_sequence)); |
| EXPECT_EQ(95, tuple_store.numTuples()); |
| ASSERT_EQ(94, tuple_store.getMaxTupleID()); |
| |
| // Read out values. |
| for (tuple_id tid = 0; tid < 95; ++tid) { |
| ASSERT_TRUE(tuple_store.hasTupleWithID(tid)); |
| EXPECT_EQ(nullptr, tuple_store.getAttributeValue(tid, 0)); |
| |
| TypedValue value = tuple_store.getAttributeValueTyped(tid, 0); |
| EXPECT_TRUE(value.isNull()); |
| EXPECT_EQ(kNullType, value.getTypeID()); |
| } |
| } |
| |
| // Note: INSTANTIATE_TEST_CASE_P has variadic arguments part. If the variable argument part |
| // is empty, C++11 standard says it should produce a warning. A warning is converted |
| // to an error since we use -Werror as a compiler parameter. It causes Travis to build. |
| // This is the reason that we must give an empty string argument as a last parameter |
| // to supress warning that clang gives. |
| INSTANTIATE_TEST_CASE_P(WithAndWithoutNullableAttributes, |
| PackedRowStoreTupleStorageSubBlockTest, |
| ::testing::Bool(),); // NOLINT(whitespace/comma) |
| |
| INSTANTIATE_TEST_CASE_P(WithAndWithoutNullableAttributes, |
| PackedRowStoreTupleStorageSubBlockDeathTest, |
| ::testing::Bool(),); // NOLINT(whitespace/comma) |
| |
| } // namespace quickstep |