| /** |
| * 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. |
| **/ |
| |
| #ifndef QUICKSTEP_TYPES_TESTS_TYPE_TEST_COMMON_HPP_ |
| #define QUICKSTEP_TYPES_TESTS_TYPE_TEST_COMMON_HPP_ |
| |
| #include <algorithm> |
| #include <cstddef> |
| #include <cstdint> |
| #include <cstring> |
| #include <initializer_list> |
| #include <limits> |
| #include <string> |
| |
| #include "types/DoubleType.hpp" |
| #include "types/FloatType.hpp" |
| #include "types/IntType.hpp" |
| #include "types/LongType.hpp" |
| #include "types/NullType.hpp" |
| #include "types/Type.hpp" |
| #include "types/TypeFactory.hpp" |
| #include "types/TypeID.hpp" |
| #include "types/TypedValue.hpp" |
| |
| #include "gtest/gtest.h" |
| |
| namespace quickstep { |
| |
| namespace type_test { |
| |
| /** \addtogroup Types |
| * @{ |
| */ |
| |
| /** |
| * @brief Check that an implementation of Type::parseValueFromString() returns |
| * success and parses a string as expected. |
| * |
| * @param TypeT A type that is represented as a fixed-size literal. |
| * @param expected_literal The literal value that the string should be parsed |
| * as. |
| * @param value_string A string representation of expected_literal to attempt |
| * to parse. |
| **/ |
| template <typename TypeT> |
| void CheckSuccessfulParseLiteralValueFromString( |
| const typename TypeT::cpptype expected_literal, |
| const std::string &value_string) { |
| const Type &type = TypeT::InstanceNonNullable(); |
| TypedValue value; |
| ASSERT_TRUE(type.parseValueFromString(value_string, &value)); |
| EXPECT_EQ(expected_literal, value.getLiteral<typename TypeT::cpptype>()); |
| } |
| |
| /** |
| * @brief Check that a given Type is coercible from a specified set of types, |
| * and not from others. |
| * |
| * @param target_type The Type to test the isCoercibleFrom() method for. This |
| * should be non-nullable, but this function will also test the nullable |
| * version. |
| * @param expected_type A list of all the TypeIDs of Types that should be |
| * coercible to target_type. |
| **/ |
| void CheckIsCoercibleFrom( |
| const Type &target_type, |
| const std::initializer_list<TypeID> &expected_coercible) { |
| const Type &target_type_nullable = target_type.getNullableVersion(); |
| |
| for (const TypeID original_type_id : expected_coercible) { |
| const Type &original_type = TypeFactory::TypeRequiresLengthParameter(original_type_id) |
| ? TypeFactory::GetType(original_type_id, 10, false) |
| : TypeFactory::GetType(original_type_id, false); |
| const Type &original_type_nullable = original_type.getNullableVersion(); |
| |
| EXPECT_TRUE(target_type.isCoercibleFrom(original_type)); |
| // Can't coerce from nullable to non-nullable. |
| EXPECT_FALSE(target_type.isCoercibleFrom(original_type_nullable)); |
| |
| EXPECT_TRUE(target_type_nullable.isCoercibleFrom(original_type)); |
| EXPECT_TRUE(target_type_nullable.isCoercibleFrom(original_type_nullable)); |
| } |
| |
| // NullType is coercible to any nullable type. |
| EXPECT_FALSE(target_type.isCoercibleFrom(TypeFactory::GetType(kNullType, true))); |
| EXPECT_TRUE(target_type_nullable.isCoercibleFrom(TypeFactory::GetType(kNullType, true))); |
| |
| // Can't coerce from other types. |
| for (const TypeID original_type_id |
| : {kInt, kLong, kFloat, kDouble, kChar, kVarChar, |
| kDate, kDatetime, kDatetimeInterval, kYearMonthInterval}) { |
| if (std::find(expected_coercible.begin(), expected_coercible.end(), original_type_id) |
| == expected_coercible.end()) { |
| const Type &original_type = TypeFactory::TypeRequiresLengthParameter(original_type_id) |
| ? TypeFactory::GetType(original_type_id, 10, false) |
| : TypeFactory::GetType(original_type_id, false); |
| const Type &original_type_nullable = original_type.getNullableVersion(); |
| |
| EXPECT_FALSE(target_type.isCoercibleFrom(original_type)); |
| EXPECT_FALSE(target_type.isCoercibleFrom(original_type_nullable)); |
| EXPECT_FALSE(target_type_nullable.isCoercibleFrom(original_type)); |
| EXPECT_FALSE(target_type_nullable.isCoercibleFrom(original_type_nullable)); |
| } |
| } |
| } |
| |
| /** |
| * @brief Check that an ASCII string type (i.e. CharType or VarCharType) is |
| * coercible from exactly the appropriate types (i.e. from any other |
| * ASCII string type). |
| * |
| * @param target_type_id The TypeID of the string Type to test. |
| * @param target_length The length parameter of the string Type to test. |
| **/ |
| void CheckStringTypeIsCoercibleFrom(const TypeID target_type_id, |
| const std::size_t target_length) { |
| const Type &target_type = TypeFactory::GetType(target_type_id, target_length, false); |
| const Type &target_type_nullable = TypeFactory::GetType(target_type_id, target_length, true); |
| |
| // Other string types to test against. |
| const Type &shorter_char = TypeFactory::GetType(kChar, target_length - 1, false); |
| const Type &shorter_char_nullable = TypeFactory::GetType(kChar, target_length - 1, true); |
| const Type &same_char = TypeFactory::GetType(kChar, target_length, false); |
| const Type &same_char_nullable = TypeFactory::GetType(kChar, target_length, true); |
| const Type &longer_char = TypeFactory::GetType(kChar, target_length + 1, false); |
| const Type &longer_char_nullable = TypeFactory::GetType(kChar, target_length + 1, true); |
| |
| const Type &shorter_varchar = TypeFactory::GetType(kVarChar, target_length - 1, false); |
| const Type &shorter_varchar_nullable = TypeFactory::GetType(kVarChar, target_length - 1, true); |
| const Type &same_varchar = TypeFactory::GetType(kVarChar, target_length, false); |
| const Type &same_varchar_nullable = TypeFactory::GetType(kVarChar, target_length, true); |
| const Type &longer_varchar = TypeFactory::GetType(kVarChar, target_length + 1, false); |
| const Type &longer_varchar_nullable = TypeFactory::GetType(kVarChar, target_length + 1, true); |
| |
| // A non-nullable ASCII string type is coercible from any other non-nullable |
| // ASCII string type. |
| EXPECT_TRUE(target_type.isCoercibleFrom(shorter_char)); |
| EXPECT_FALSE(target_type.isCoercibleFrom(shorter_char_nullable)); |
| EXPECT_TRUE(target_type.isCoercibleFrom(same_char)); |
| EXPECT_FALSE(target_type.isCoercibleFrom(same_char_nullable)); |
| EXPECT_TRUE(target_type.isCoercibleFrom(longer_char)); |
| EXPECT_FALSE(target_type.isCoercibleFrom(longer_char_nullable)); |
| |
| EXPECT_TRUE(target_type.isCoercibleFrom(shorter_varchar)); |
| EXPECT_FALSE(target_type.isCoercibleFrom(shorter_varchar_nullable)); |
| EXPECT_TRUE(target_type.isCoercibleFrom(same_varchar)); |
| EXPECT_FALSE(target_type.isCoercibleFrom(same_varchar_nullable)); |
| EXPECT_TRUE(target_type.isCoercibleFrom(longer_varchar)); |
| EXPECT_FALSE(target_type.isCoercibleFrom(longer_varchar_nullable)); |
| |
| // A nullable ASCII string type is coercible from any other ASCII string |
| // type. |
| EXPECT_TRUE(target_type_nullable.isCoercibleFrom(shorter_char)); |
| EXPECT_TRUE(target_type_nullable.isCoercibleFrom(shorter_char_nullable)); |
| EXPECT_TRUE(target_type_nullable.isCoercibleFrom(same_char)); |
| EXPECT_TRUE(target_type_nullable.isCoercibleFrom(same_char_nullable)); |
| EXPECT_TRUE(target_type_nullable.isCoercibleFrom(longer_char)); |
| EXPECT_TRUE(target_type_nullable.isCoercibleFrom(longer_char_nullable)); |
| |
| EXPECT_TRUE(target_type_nullable.isCoercibleFrom(shorter_varchar)); |
| EXPECT_TRUE(target_type_nullable.isCoercibleFrom(shorter_varchar_nullable)); |
| EXPECT_TRUE(target_type_nullable.isCoercibleFrom(same_varchar)); |
| EXPECT_TRUE(target_type_nullable.isCoercibleFrom(same_varchar_nullable)); |
| EXPECT_TRUE(target_type_nullable.isCoercibleFrom(longer_varchar)); |
| EXPECT_TRUE(target_type_nullable.isCoercibleFrom(longer_varchar_nullable)); |
| |
| // NullType can be coerced to any other nullable type. |
| EXPECT_FALSE(target_type.isCoercibleFrom(TypeFactory::GetType(kNullType, true))); |
| EXPECT_TRUE(target_type_nullable.isCoercibleFrom(TypeFactory::GetType(kNullType, true))); |
| |
| // Other types are not coercible to strings. |
| for (const TypeID original_type_id |
| : {kInt, kLong, kFloat, kDouble, kDate, kDatetime, kDatetimeInterval, kYearMonthInterval}) { |
| const Type &original_type = TypeFactory::TypeRequiresLengthParameter(original_type_id) |
| ? TypeFactory::GetType(original_type_id, 10, false) |
| : TypeFactory::GetType(original_type_id, false); |
| const Type &original_type_nullable = original_type.getNullableVersion(); |
| |
| EXPECT_FALSE(target_type.isCoercibleFrom(original_type)); |
| EXPECT_FALSE(target_type.isCoercibleFrom(original_type_nullable)); |
| EXPECT_FALSE(target_type_nullable.isCoercibleFrom(original_type)); |
| EXPECT_FALSE(target_type_nullable.isCoercibleFrom(original_type_nullable)); |
| } |
| } |
| |
| /** |
| * @brief Check that a given Type is safely coercible from a specified set of |
| * types, and not from others. |
| * |
| * @param target_type The Type to test the isSafelyCoercibleFrom() method for. |
| * This should be non-nullable, but this function will also test the |
| * nullable version. |
| * @param expected_type A list of all the TypeIDs of Types that should be |
| * safely coercible to target_type. |
| **/ |
| void CheckIsSafelyCoercibleFrom( |
| const Type &target_type, |
| const std::initializer_list<TypeID> &expected_coercible) { |
| const Type &target_type_nullable = target_type.getNullableVersion(); |
| |
| for (const TypeID original_type_id : expected_coercible) { |
| const Type &original_type = TypeFactory::TypeRequiresLengthParameter(original_type_id) |
| ? TypeFactory::GetType(original_type_id, 10, false) |
| : TypeFactory::GetType(original_type_id, false); |
| const Type &original_type_nullable = original_type.getNullableVersion(); |
| |
| EXPECT_TRUE(target_type.isSafelyCoercibleFrom(original_type)); |
| // Can't coerce from nullable to non-nullable. |
| EXPECT_FALSE(target_type.isSafelyCoercibleFrom(original_type_nullable)); |
| |
| EXPECT_TRUE(target_type_nullable.isSafelyCoercibleFrom(original_type)); |
| EXPECT_TRUE(target_type_nullable.isSafelyCoercibleFrom(original_type_nullable)); |
| } |
| |
| // NullType is coercible to any nullable type. |
| EXPECT_FALSE(target_type.isSafelyCoercibleFrom(TypeFactory::GetType(kNullType, true))); |
| EXPECT_TRUE(target_type_nullable.isSafelyCoercibleFrom(TypeFactory::GetType(kNullType, true))); |
| |
| // Can't coerce from other types. |
| for (const TypeID original_type_id |
| : {kInt, kLong, kFloat, kDouble, kChar, kVarChar, |
| kDate, kDatetime, kDatetimeInterval, kYearMonthInterval}) { |
| if (std::find(expected_coercible.begin(), expected_coercible.end(), original_type_id) |
| == expected_coercible.end()) { |
| const Type &original_type = TypeFactory::TypeRequiresLengthParameter(original_type_id) |
| ? TypeFactory::GetType(original_type_id, 10, false) |
| : TypeFactory::GetType(original_type_id, false); |
| const Type &original_type_nullable = original_type.getNullableVersion(); |
| |
| EXPECT_FALSE(target_type.isSafelyCoercibleFrom(original_type)); |
| EXPECT_FALSE(target_type.isSafelyCoercibleFrom(original_type_nullable)); |
| EXPECT_FALSE(target_type_nullable.isSafelyCoercibleFrom(original_type)); |
| EXPECT_FALSE(target_type_nullable.isSafelyCoercibleFrom(original_type_nullable)); |
| } |
| } |
| } |
| |
| /** |
| * @brief Check that an ASCII string type (i.e. CharType or VarCharType) is |
| * safely coercible from exactly the appropriate types (i.e. from any |
| * ASCII string type with the same or shorter length). |
| * |
| * @param target_type_id The TypeID of the string Type to test. |
| * @param target_length The length parameter of the string Type to test. |
| **/ |
| void CheckStringTypeIsSafelyCoercibleFrom(const TypeID target_type_id, |
| const std::size_t target_length) { |
| const Type &target_type = TypeFactory::GetType(target_type_id, target_length, false); |
| const Type &target_type_nullable = TypeFactory::GetType(target_type_id, target_length, true); |
| |
| // Other string types to test against. |
| const Type &shorter_char = TypeFactory::GetType(kChar, target_length - 1, false); |
| const Type &shorter_char_nullable = TypeFactory::GetType(kChar, target_length - 1, true); |
| const Type &same_char = TypeFactory::GetType(kChar, target_length, false); |
| const Type &same_char_nullable = TypeFactory::GetType(kChar, target_length, true); |
| const Type &longer_char = TypeFactory::GetType(kChar, target_length + 1, false); |
| const Type &longer_char_nullable = TypeFactory::GetType(kChar, target_length + 1, true); |
| |
| const Type &shorter_varchar = TypeFactory::GetType(kVarChar, target_length - 1, false); |
| const Type &shorter_varchar_nullable = TypeFactory::GetType(kVarChar, target_length - 1, true); |
| const Type &same_varchar = TypeFactory::GetType(kVarChar, target_length, false); |
| const Type &same_varchar_nullable = TypeFactory::GetType(kVarChar, target_length, true); |
| const Type &longer_varchar = TypeFactory::GetType(kVarChar, target_length + 1, false); |
| const Type &longer_varchar_nullable = TypeFactory::GetType(kVarChar, target_length + 1, true); |
| |
| // A non-nullable ASCII string type is safely coercible from other |
| // non-nullable ASCII string types with the same or lesser length. |
| EXPECT_TRUE(target_type.isSafelyCoercibleFrom(shorter_char)); |
| EXPECT_FALSE(target_type.isSafelyCoercibleFrom(shorter_char_nullable)); |
| EXPECT_TRUE(target_type.isSafelyCoercibleFrom(same_char)); |
| EXPECT_FALSE(target_type.isSafelyCoercibleFrom(same_char_nullable)); |
| EXPECT_FALSE(target_type.isSafelyCoercibleFrom(longer_char)); |
| EXPECT_FALSE(target_type.isSafelyCoercibleFrom(longer_char_nullable)); |
| |
| EXPECT_TRUE(target_type.isSafelyCoercibleFrom(shorter_varchar)); |
| EXPECT_FALSE(target_type.isSafelyCoercibleFrom(shorter_varchar_nullable)); |
| EXPECT_TRUE(target_type.isSafelyCoercibleFrom(same_varchar)); |
| EXPECT_FALSE(target_type.isSafelyCoercibleFrom(same_varchar_nullable)); |
| EXPECT_FALSE(target_type.isSafelyCoercibleFrom(longer_varchar)); |
| EXPECT_FALSE(target_type.isSafelyCoercibleFrom(longer_varchar_nullable)); |
| |
| // A nullable ASCII string type is safely coercible from other ASCII string |
| // types with the same or lesser length. |
| EXPECT_TRUE(target_type_nullable.isSafelyCoercibleFrom(shorter_char)); |
| EXPECT_TRUE(target_type_nullable.isSafelyCoercibleFrom(shorter_char_nullable)); |
| EXPECT_TRUE(target_type_nullable.isSafelyCoercibleFrom(same_char)); |
| EXPECT_TRUE(target_type_nullable.isSafelyCoercibleFrom(same_char_nullable)); |
| EXPECT_FALSE(target_type_nullable.isSafelyCoercibleFrom(longer_char)); |
| EXPECT_FALSE(target_type_nullable.isSafelyCoercibleFrom(longer_char_nullable)); |
| |
| EXPECT_TRUE(target_type_nullable.isSafelyCoercibleFrom(shorter_varchar)); |
| EXPECT_TRUE(target_type_nullable.isSafelyCoercibleFrom(shorter_varchar_nullable)); |
| EXPECT_TRUE(target_type_nullable.isSafelyCoercibleFrom(same_varchar)); |
| EXPECT_TRUE(target_type_nullable.isSafelyCoercibleFrom(same_varchar_nullable)); |
| EXPECT_FALSE(target_type_nullable.isSafelyCoercibleFrom(longer_varchar)); |
| EXPECT_FALSE(target_type_nullable.isSafelyCoercibleFrom(longer_varchar_nullable)); |
| |
| // NullType can be safely coerced to any other nullable type. |
| EXPECT_FALSE(target_type.isSafelyCoercibleFrom(TypeFactory::GetType(kNullType, true))); |
| EXPECT_TRUE(target_type_nullable.isSafelyCoercibleFrom(TypeFactory::GetType(kNullType, true))); |
| |
| // Other types are not coercible to strings. |
| for (const TypeID original_type_id |
| : {kInt, kLong, kFloat, kDouble, kDate, kDatetime, kDatetimeInterval, kYearMonthInterval}) { |
| const Type &original_type = TypeFactory::TypeRequiresLengthParameter(original_type_id) |
| ? TypeFactory::GetType(original_type_id, 10, false) |
| : TypeFactory::GetType(original_type_id, false); |
| const Type &original_type_nullable = original_type.getNullableVersion(); |
| |
| EXPECT_FALSE(target_type.isSafelyCoercibleFrom(original_type)); |
| EXPECT_FALSE(target_type.isSafelyCoercibleFrom(original_type_nullable)); |
| EXPECT_FALSE(target_type_nullable.isSafelyCoercibleFrom(original_type)); |
| EXPECT_FALSE(target_type_nullable.isSafelyCoercibleFrom(original_type_nullable)); |
| } |
| } |
| |
| /** |
| * @brief Check coercion between two types represented as in-line literal |
| * values. |
| * |
| * @param TargetType The Type to coerce to. |
| * @param OriginalType The Type to coerce from. |
| * @param literal The underlying literal value for the original value from |
| * OriginalType. |
| **/ |
| template <typename TargetType, typename OriginalType> |
| void CheckLiteralCoerce(const typename OriginalType::cpptype literal) { |
| TypedValue value(literal); |
| TypedValue coerced = TargetType::InstanceNonNullable().coerceValue( |
| value, |
| OriginalType::InstanceNonNullable()); |
| EXPECT_EQ(static_cast<typename TargetType::cpptype>(literal), |
| coerced.getLiteral<typename TargetType::cpptype>()); |
| } |
| |
| /** |
| * @brief Check coercion of a NULL value. |
| * |
| * @param TargetType The Type to coerce to. |
| * @param OriginalType The Type to coerce from. |
| **/ |
| template <typename TargetType, typename OriginalType> |
| void CheckNullLiteralCoerce() { |
| TypedValue value(OriginalType::InstanceNullable().makeNullValue()); |
| TypedValue coerced = TargetType::InstanceNullable().coerceValue( |
| value, |
| OriginalType::InstanceNullable()); |
| EXPECT_TRUE(coerced.isNull()); |
| } |
| |
| /** |
| * @brief Run a battery of tests coercing values of various different numeric |
| * Types to a specified target Type. |
| * |
| * @param TargetType The Type to coerce to. |
| **/ |
| template <typename TargetType> |
| void CheckNumericCoerce() { |
| CheckLiteralCoerce<TargetType, IntType>(0); |
| CheckLiteralCoerce<TargetType, IntType>(123); |
| CheckLiteralCoerce<TargetType, IntType>(-123); |
| CheckLiteralCoerce<TargetType, IntType>(std::numeric_limits<int>::max()); |
| CheckLiteralCoerce<TargetType, IntType>(std::numeric_limits<int>::min()); |
| CheckNullLiteralCoerce<TargetType, IntType>(); |
| |
| CheckLiteralCoerce<TargetType, LongType>(INT64_C(0)); |
| CheckLiteralCoerce<TargetType, LongType>(INT64_C(123)); |
| CheckLiteralCoerce<TargetType, LongType>(INT64_C(-123)); |
| CheckLiteralCoerce<TargetType, LongType>(std::numeric_limits<std::int64_t>::max()); |
| CheckLiteralCoerce<TargetType, LongType>(std::numeric_limits<std::int64_t>::min()); |
| CheckNullLiteralCoerce<TargetType, LongType>(); |
| |
| CheckLiteralCoerce<TargetType, FloatType>(0.0f); |
| CheckLiteralCoerce<TargetType, FloatType>(123.45f); |
| CheckLiteralCoerce<TargetType, FloatType>(-123.45f); |
| CheckLiteralCoerce<TargetType, FloatType>(std::numeric_limits<float>::max()); |
| CheckLiteralCoerce<TargetType, FloatType>(std::numeric_limits<float>::min()); |
| CheckNullLiteralCoerce<TargetType, FloatType>(); |
| |
| CheckLiteralCoerce<TargetType, DoubleType>(0.0); |
| CheckLiteralCoerce<TargetType, DoubleType>(123.45); |
| CheckLiteralCoerce<TargetType, DoubleType>(-123.45); |
| CheckLiteralCoerce<TargetType, DoubleType>(std::numeric_limits<double>::max()); |
| CheckLiteralCoerce<TargetType, DoubleType>(std::numeric_limits<double>::min()); |
| CheckNullLiteralCoerce<TargetType, DoubleType>(); |
| |
| CheckNullLiteralCoerce<TargetType, NullType>(); |
| } |
| |
| /** |
| * @brief Check coercion of CHAR literal values WITHOUT a null-terminator to |
| * a string Type. |
| * |
| * @param type_id The ID of the string type to coerce to. |
| * @param literal The underlying string literal value to test coercion on. |
| **/ |
| void CheckStringCoerceFromCharWithoutNullTerminator(const TypeID type_id, |
| const std::string &literal) { |
| const Type &target_type = TypeFactory::GetType(type_id, literal.size(), false); |
| const Type &shorter_target_type = TypeFactory::GetType(type_id, literal.size() - 1, false); |
| const Type &longer_target_type = TypeFactory::GetType(type_id, literal.size() + 10, false); |
| |
| const Type &char_type = TypeFactory::GetType(kChar, literal.size(), false); |
| |
| TypedValue char_value = char_type.makeValue(literal.c_str(), literal.size()); |
| TypedValue nonref_char_value = char_value; |
| nonref_char_value.ensureNotReference(); |
| |
| // Test coercion from a CHAR with exactly the right length (original value |
| // has no null-terminator). |
| TypedValue coerced = target_type.coerceValue(char_value, char_type); |
| EXPECT_EQ(type_id, coerced.getTypeID()); |
| if (type_id == kChar) { |
| // Still a reference to the same data. |
| EXPECT_FALSE(coerced.ownsOutOfLineData()); |
| EXPECT_EQ(literal.size(), coerced.getDataSize()); |
| EXPECT_EQ(static_cast<const void*>(literal.c_str()), coerced.getDataPtr()); |
| } else { |
| // Coercing to VARCHAR requires appending a null-terminator. |
| EXPECT_TRUE(coerced.ownsOutOfLineData()); |
| EXPECT_EQ(literal.size() + 1, coerced.getDataSize()); |
| EXPECT_STREQ(literal.c_str(), static_cast<const char*>(coerced.getDataPtr())); |
| } |
| |
| // Coercing from a literal makes a copy. |
| TypedValue coerced_from_nonref = target_type.coerceValue(nonref_char_value, char_type); |
| EXPECT_EQ(type_id, coerced_from_nonref.getTypeID()); |
| EXPECT_TRUE(coerced_from_nonref.ownsOutOfLineData()); |
| if (type_id == kChar) { |
| ASSERT_EQ(literal.size(), coerced_from_nonref.getDataSize()); |
| EXPECT_EQ(0, std::strncmp(literal.c_str(), |
| static_cast<const char*>(coerced_from_nonref.getDataPtr()), |
| literal.size())); |
| } else { |
| EXPECT_EQ(literal.size() + 1, coerced_from_nonref.getDataSize()); |
| EXPECT_STREQ(literal.c_str(), static_cast<const char*>(coerced_from_nonref.getDataPtr())); |
| } |
| |
| // Test shortening from a CHAR. |
| TypedValue shortened = shorter_target_type.coerceValue(char_value, char_type); |
| EXPECT_EQ(type_id, shortened.getTypeID()); |
| if (type_id == kChar) { |
| // Still a reference to a shorter slice of the same data. |
| EXPECT_FALSE(shortened.ownsOutOfLineData()); |
| EXPECT_EQ(literal.size() - 1, shortened.getDataSize()); |
| EXPECT_EQ(static_cast<const void*>(literal.c_str()), shortened.getDataPtr()); |
| } else { |
| // End of string is trimmed and a null-terminator is appended. |
| EXPECT_TRUE(shortened.ownsOutOfLineData()); |
| ASSERT_EQ(literal.size(), shortened.getDataSize()); |
| EXPECT_EQ(0, std::strncmp(literal.c_str(), |
| static_cast<const char*>(shortened.getDataPtr()), |
| literal.size() - 1)); |
| EXPECT_EQ('\0', static_cast<const char*>(shortened.getDataPtr())[literal.size() - 1]); |
| } |
| |
| // Again, coercing from a literal makes a copy. |
| TypedValue shortened_from_nonref |
| = shorter_target_type.coerceValue(nonref_char_value, char_type); |
| EXPECT_EQ(type_id, shortened_from_nonref.getTypeID()); |
| EXPECT_TRUE(shortened_from_nonref.ownsOutOfLineData()); |
| if (type_id == kChar) { |
| ASSERT_EQ(literal.size() - 1, shortened_from_nonref.getDataSize()); |
| EXPECT_EQ(0, std::strncmp(literal.c_str(), |
| static_cast<const char*>(shortened_from_nonref.getDataPtr()), |
| literal.size() - 1)); |
| } else { |
| EXPECT_EQ(literal.size(), shortened_from_nonref.getDataSize()); |
| EXPECT_EQ(0, std::strncmp(literal.c_str(), |
| static_cast<const char*>(shortened_from_nonref.getDataPtr()), |
| literal.size() - 1)); |
| EXPECT_EQ('\0', static_cast<const char*>( |
| shortened_from_nonref.getDataPtr())[literal.size() - 1]); |
| } |
| |
| // Test lengthening from a CHAR. This always needs to make a copy and append |
| // a null-terminator. |
| TypedValue lengthened |
| = longer_target_type.coerceValue(char_value, char_type); |
| EXPECT_EQ(type_id, lengthened.getTypeID()); |
| EXPECT_TRUE(lengthened.ownsOutOfLineData()); |
| EXPECT_EQ(literal.size() + 1, lengthened.getDataSize()); |
| EXPECT_STREQ(literal.c_str(), static_cast<const char*>(lengthened.getDataPtr())); |
| |
| // Same deal if the original is itself a literal. |
| TypedValue lengthened_from_nonref |
| = longer_target_type.coerceValue(nonref_char_value, char_type); |
| EXPECT_EQ(type_id, lengthened_from_nonref.getTypeID()); |
| EXPECT_TRUE(lengthened_from_nonref.ownsOutOfLineData()); |
| EXPECT_EQ(literal.size() + 1, lengthened_from_nonref.getDataSize()); |
| EXPECT_STREQ(literal.c_str(), |
| static_cast<const char*>(lengthened_from_nonref.getDataPtr())); |
| } |
| |
| /** |
| * @brief Check coercion of string literal values WITH a null-terminator to a |
| * string Type. |
| * |
| * @param type_id The ID of the string type to coerce to. |
| * @param original_type_id The ID of the string type to coerce from. |
| * @param literal The underlying string literal value to test coercion on. |
| **/ |
| void CheckStringCoerceWithNullTerminator(const TypeID type_id, |
| const TypeID original_type_id, |
| const std::string &literal) { |
| const Type &target_type = TypeFactory::GetType(type_id, literal.size(), false); |
| const Type &shorter_target_type = TypeFactory::GetType(type_id, literal.size() - 1, false); |
| const Type &longer_target_type = TypeFactory::GetType(type_id, literal.size() + 10, false); |
| |
| const Type &original_type = TypeFactory::GetType( |
| original_type_id, |
| original_type_id == kChar ? literal.size() + 1 |
| : literal.size(), |
| false); |
| TypedValue value = original_type.makeValue(literal.c_str(), literal.size() + 1); |
| TypedValue nonref_value = value; |
| nonref_value.ensureNotReference(); |
| |
| // Should just be a reference to the original data. |
| TypedValue coerced = target_type.coerceValue(value, original_type); |
| EXPECT_EQ(type_id, coerced.getTypeID()); |
| EXPECT_FALSE(coerced.ownsOutOfLineData()); |
| EXPECT_EQ(type_id == kChar ? literal.size() : literal.size() + 1, |
| coerced.getDataSize()); |
| EXPECT_EQ(static_cast<const void*>(literal.c_str()), coerced.getDataPtr()); |
| |
| // Coercing from a literal makes a copy. |
| TypedValue coerced_from_nonref = target_type.coerceValue(nonref_value, original_type); |
| EXPECT_EQ(type_id, coerced_from_nonref.getTypeID()); |
| EXPECT_TRUE(coerced_from_nonref.ownsOutOfLineData()); |
| if (type_id == kChar) { |
| ASSERT_EQ(literal.size(), coerced_from_nonref.getDataSize()); |
| EXPECT_EQ(0, std::strncmp(literal.c_str(), |
| static_cast<const char*>(coerced_from_nonref.getDataPtr()), |
| literal.size())); |
| } else { |
| EXPECT_EQ(literal.size() + 1, coerced_from_nonref.getDataSize()); |
| EXPECT_STREQ(literal.c_str(), static_cast<const char*>(coerced_from_nonref.getDataPtr())); |
| } |
| |
| // Test shortening. |
| TypedValue shortened = shorter_target_type.coerceValue(value, original_type); |
| EXPECT_EQ(type_id, shortened.getTypeID()); |
| if (type_id == kChar) { |
| // Still a reference to a shorter slice of the same data. |
| EXPECT_FALSE(shortened.ownsOutOfLineData()); |
| EXPECT_EQ(literal.size() - 1, shortened.getDataSize()); |
| EXPECT_EQ(static_cast<const void*>(literal.c_str()), shortened.getDataPtr()); |
| } else { |
| // Shortening to a VARCHAR requires making a shorter copy with a |
| // null-terminator. |
| EXPECT_TRUE(shortened.ownsOutOfLineData()); |
| ASSERT_EQ(literal.size(), shortened.getDataSize()); |
| EXPECT_EQ(0, std::strncmp(literal.c_str(), |
| static_cast<const char*>(shortened.getDataPtr()), |
| literal.size() - 1)); |
| EXPECT_EQ('\0', static_cast<const char*>(shortened.getDataPtr())[literal.size() - 1]); |
| } |
| |
| // Again, coercing from a literal makes a copy. |
| TypedValue shortened_from_nonref = shorter_target_type.coerceValue(nonref_value, original_type); |
| EXPECT_EQ(type_id, shortened_from_nonref.getTypeID()); |
| EXPECT_TRUE(shortened_from_nonref.ownsOutOfLineData()); |
| if (type_id == kChar) { |
| ASSERT_EQ(literal.size() - 1, shortened_from_nonref.getDataSize()); |
| EXPECT_EQ(0, std::strncmp(literal.c_str(), |
| static_cast<const char*>(shortened_from_nonref.getDataPtr()), |
| literal.size() - 1)); |
| } else { |
| EXPECT_EQ(literal.size(), shortened_from_nonref.getDataSize()); |
| EXPECT_EQ(0, std::strncmp(literal.c_str(), |
| static_cast<const char*>(shortened_from_nonref.getDataPtr()), |
| literal.size() - 1)); |
| EXPECT_EQ('\0', static_cast<const char*>( |
| shortened_from_nonref.getDataPtr())[literal.size() - 1]); |
| } |
| |
| // Lengthening from a null-terminated original should stay as a reference to |
| // the original data. |
| TypedValue lengthened = longer_target_type.coerceValue(value, original_type); |
| EXPECT_EQ(type_id, lengthened.getTypeID()); |
| EXPECT_FALSE(lengthened.ownsOutOfLineData()); |
| EXPECT_EQ(literal.size() + 1, lengthened.getDataSize()); |
| EXPECT_EQ(static_cast<const void*>(literal.c_str()), lengthened.getDataPtr()); |
| |
| // If the original is a literal, a copy is made. |
| TypedValue lengthened_from_nonref = longer_target_type.coerceValue(nonref_value, original_type); |
| EXPECT_EQ(type_id, lengthened_from_nonref.getTypeID()); |
| EXPECT_TRUE(lengthened_from_nonref.ownsOutOfLineData()); |
| EXPECT_EQ(literal.size() + 1, lengthened_from_nonref.getDataSize()); |
| EXPECT_STREQ(literal.c_str(), |
| static_cast<const char*>(lengthened_from_nonref.getDataPtr())); |
| } |
| |
| /** @} */ |
| |
| } // namespace type_test |
| } // namespace quickstep |
| |
| #endif // QUICKSTEP_TYPES_TESTS_TYPE_TEST_COMMON_HPP_ |