blob: 74ac4f82522e1d51bd7c16e4c24a0c5710f285f8 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
**/
#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_