blob: 81eaaecfab2e81b97c897c0e862615203c4b36d1 [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 KUDU_COMMON_TYPES_H
#define KUDU_COMMON_TYPES_H
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <ostream>
#include <string>
#include <glog/logging.h>
#include "kudu/common/common.pb.h"
#include "kudu/gutil/macros.h"
#include "kudu/gutil/mathlimits.h"
#include "kudu/gutil/port.h"
#include "kudu/gutil/strings/escaping.h"
#include "kudu/gutil/strings/numbers.h"
#include "kudu/util/int128.h"
#include "kudu/util/int128_util.h" // IWYU pragma: keep
#include "kudu/util/slice.h"
// IWYU pragma: no_include "kudu/util/status.h"
namespace kudu {
// The size of the in-memory format of the largest
// type we support.
const int kLargestTypeSize = sizeof(Slice);
class TypeInfo;
// This is the important bit of this header:
// given a type enum, get the TypeInfo about it.
extern const TypeInfo* GetTypeInfo(DataType type);
// Information about a given type.
// This is a runtime equivalent of the DataTypeTraits template below.
class TypeInfo {
public:
// Returns the type mentioned in the schema.
DataType type() const { return type_; }
// Returns the type used to actually store the data.
DataType physical_type() const { return physical_type_; }
const std::string& name() const { return name_; }
const size_t size() const { return size_; }
void AppendDebugStringForValue(const void *ptr, std::string *str) const;
int Compare(const void *lhs, const void *rhs) const;
// Returns true if increment(a) is equal to b.
bool AreConsecutive(const void* a, const void* b) const;
void CopyMinValue(void* dst) const {
memcpy(dst, min_value_, size_);
}
bool IsMinValue(const void* value) const {
return Compare(value, min_value_) == 0;
}
bool IsMaxValue(const void* value) const {
return max_value_ != nullptr && Compare(value, max_value_) == 0;
}
bool is_virtual() const { return is_virtual_; }
private:
friend class TypeInfoResolver;
template<typename Type> explicit TypeInfo(Type t);
const DataType type_;
const DataType physical_type_;
const std::string name_;
const size_t size_;
const void* const min_value_;
// The maximum value of the type, or null if the type has no max value.
const void* const max_value_;
// Whether or not the type may only be used in projections, not tablet schemas.
const bool is_virtual_;
typedef void (*AppendDebugFunc)(const void *, std::string *);
const AppendDebugFunc append_func_;
typedef int (*CompareFunc)(const void *, const void *);
const CompareFunc compare_func_;
typedef bool (*AreConsecutiveFunc)(const void*, const void*);
const AreConsecutiveFunc are_consecutive_func_;
};
template<DataType Type> struct DataTypeTraits {};
template<DataType Type>
static int GenericCompare(const void *lhs, const void *rhs) {
typedef typename DataTypeTraits<Type>::cpp_type CppType;
CppType lhs_int = UnalignedLoad<CppType>(lhs);
CppType rhs_int = UnalignedLoad<CppType>(rhs);
if (lhs_int < rhs_int) {
return -1;
}
if (lhs_int > rhs_int) {
return 1;
}
return 0;
}
template<DataType Type>
static int AreIntegersConsecutive(const void* a, const void* b) {
typedef typename DataTypeTraits<Type>::cpp_type CppType;
CppType a_int = UnalignedLoad<CppType>(a);
CppType b_int = UnalignedLoad<CppType>(b);
// Avoid overflow by checking relative position first.
return a_int < b_int && a_int + 1 == b_int;
}
template<DataType Type>
static int AreFloatsConsecutive(const void* a, const void* b) {
typedef typename DataTypeTraits<Type>::cpp_type CppType;
CppType a_float = UnalignedLoad<CppType>(a);
CppType b_float = UnalignedLoad<CppType>(b);
return a_float < b_float && std::nextafter(a_float, b_float) == b_float;
}
template<>
struct DataTypeTraits<UINT8> {
static const DataType physical_type = UINT8;
typedef uint8_t cpp_type;
static const char *name() {
return "uint8";
}
static void AppendDebugStringForValue(const void *val, std::string *str) {
str->append(SimpleItoa(*reinterpret_cast<const uint8_t *>(val)));
}
static int Compare(const void *lhs, const void *rhs) {
return GenericCompare<UINT8>(lhs, rhs);
}
static bool AreConsecutive(const void* a, const void* b) {
return AreIntegersConsecutive<UINT8>(a, b);
}
static const cpp_type* min_value() {
return &MathLimits<cpp_type>::kMin;
}
static const cpp_type* max_value() {
return &MathLimits<cpp_type>::kMax;
}
static bool IsVirtual() {
return false;
}
};
template<>
struct DataTypeTraits<INT8> {
static const DataType physical_type = INT8;
typedef int8_t cpp_type;
static const char *name() {
return "int8";
}
static void AppendDebugStringForValue(const void *val, std::string *str) {
str->append(SimpleItoa(*reinterpret_cast<const int8_t *>(val)));
}
static int Compare(const void *lhs, const void *rhs) {
return GenericCompare<INT8>(lhs, rhs);
}
static bool AreConsecutive(const void* a, const void* b) {
return AreIntegersConsecutive<INT8>(a, b);
}
static const cpp_type* min_value() {
return &MathLimits<cpp_type>::kMin;
}
static const cpp_type* max_value() {
return &MathLimits<cpp_type>::kMax;
}
static bool IsVirtual() {
return false;
}
};
template<>
struct DataTypeTraits<UINT16> {
static const DataType physical_type = UINT16;
typedef uint16_t cpp_type;
static const char *name() {
return "uint16";
}
static void AppendDebugStringForValue(const void *val, std::string *str) {
str->append(SimpleItoa(*reinterpret_cast<const uint16_t *>(val)));
}
static int Compare(const void *lhs, const void *rhs) {
return GenericCompare<UINT16>(lhs, rhs);
}
static bool AreConsecutive(const void* a, const void* b) {
return AreIntegersConsecutive<UINT16>(a, b);
}
static const cpp_type* min_value() {
return &MathLimits<cpp_type>::kMin;
}
static const cpp_type* max_value() {
return &MathLimits<cpp_type>::kMax;
}
static bool IsVirtual() {
return false;
}
};
template<>
struct DataTypeTraits<INT16> {
static const DataType physical_type = INT16;
typedef int16_t cpp_type;
static const char *name() {
return "int16";
}
static void AppendDebugStringForValue(const void *val, std::string *str) {
str->append(SimpleItoa(*reinterpret_cast<const int16_t *>(val)));
}
static int Compare(const void *lhs, const void *rhs) {
return GenericCompare<INT16>(lhs, rhs);
}
static bool AreConsecutive(const void* a, const void* b) {
return AreIntegersConsecutive<INT16>(a, b);
}
static const cpp_type* min_value() {
return &MathLimits<cpp_type>::kMin;
}
static const cpp_type* max_value() {
return &MathLimits<cpp_type>::kMax;
}
static bool IsVirtual() {
return false;
}
};
template<>
struct DataTypeTraits<UINT32> {
static const DataType physical_type = UINT32;
typedef uint32_t cpp_type;
static const char *name() {
return "uint32";
}
static void AppendDebugStringForValue(const void *val, std::string *str) {
str->append(SimpleItoa(*reinterpret_cast<const uint32_t *>(val)));
}
static int Compare(const void *lhs, const void *rhs) {
return GenericCompare<UINT32>(lhs, rhs);
}
static bool AreConsecutive(const void* a, const void* b) {
return AreIntegersConsecutive<UINT32>(a, b);
}
static const cpp_type* min_value() {
return &MathLimits<cpp_type>::kMin;
}
static const cpp_type* max_value() {
return &MathLimits<cpp_type>::kMax;
}
static bool IsVirtual() {
return false;
}
};
template<>
struct DataTypeTraits<INT32> {
static const DataType physical_type = INT32;
typedef int32_t cpp_type;
static const char *name() {
return "int32";
}
static void AppendDebugStringForValue(const void *val, std::string *str) {
str->append(SimpleItoa(*reinterpret_cast<const int32_t *>(val)));
}
static int Compare(const void *lhs, const void *rhs) {
return GenericCompare<INT32>(lhs, rhs);
}
static bool AreConsecutive(const void* a, const void* b) {
return AreIntegersConsecutive<INT32>(a, b);
}
static const cpp_type* min_value() {
return &MathLimits<cpp_type>::kMin;
}
static const cpp_type* max_value() {
return &MathLimits<cpp_type>::kMax;
}
static bool IsVirtual() {
return false;
}
};
template<>
struct DataTypeTraits<UINT64> {
static const DataType physical_type = UINT64;
typedef uint64_t cpp_type;
static const char *name() {
return "uint64";
}
static void AppendDebugStringForValue(const void *val, std::string *str) {
str->append(SimpleItoa(*reinterpret_cast<const uint64_t *>(val)));
}
static int Compare(const void *lhs, const void *rhs) {
return GenericCompare<UINT64>(lhs, rhs);
}
static bool AreConsecutive(const void* a, const void* b) {
return AreIntegersConsecutive<UINT64>(a, b);
}
static const cpp_type* min_value() {
return &MathLimits<cpp_type>::kMin;
}
static const cpp_type* max_value() {
return &MathLimits<cpp_type>::kMax;
}
static bool IsVirtual() {
return false;
}
};
template<>
struct DataTypeTraits<INT64> {
static const DataType physical_type = INT64;
typedef int64_t cpp_type;
static const char *name() {
return "int64";
}
static void AppendDebugStringForValue(const void *val, std::string *str) {
str->append(SimpleItoa(*reinterpret_cast<const int64_t *>(val)));
}
static int Compare(const void *lhs, const void *rhs) {
return GenericCompare<INT64>(lhs, rhs);
}
static bool AreConsecutive(const void* a, const void* b) {
return AreIntegersConsecutive<INT64>(a, b);
}
static const cpp_type* min_value() {
return &MathLimits<cpp_type>::kMin;
}
static const cpp_type* max_value() {
return &MathLimits<cpp_type>::kMax;
}
static bool IsVirtual() {
return false;
}
};
template<>
struct DataTypeTraits<INT128> {
static const DataType physical_type = INT128;
typedef int128_t cpp_type;
static const char *name() {
return "int128";
}
static void AppendDebugStringForValue(const void *val, std::string *str) {
str->append(SimpleItoa(UnalignedLoad<int128_t>(val)));
}
static int Compare(const void *lhs, const void *rhs) {
return GenericCompare<INT128>(lhs, rhs);
}
static bool AreConsecutive(const void* a, const void* b) {
return AreIntegersConsecutive<INT128>(a, b);
}
static const cpp_type* min_value() {
return &INT128_MIN;
}
static const cpp_type* max_value() {
return &INT128_MAX;
}
static bool IsVirtual() {
return false;
}
};
template<>
struct DataTypeTraits<FLOAT> {
static const DataType physical_type = FLOAT;
typedef float cpp_type;
static const char *name() {
return "float";
}
static void AppendDebugStringForValue(const void *val, std::string *str) {
str->append(SimpleFtoa(*reinterpret_cast<const float *>(val)));
}
static int Compare(const void *lhs, const void *rhs) {
return GenericCompare<FLOAT>(lhs, rhs);
}
static bool AreConsecutive(const void* a, const void* b) {
return AreFloatsConsecutive<FLOAT>(a, b);
}
static const cpp_type* min_value() {
return &MathLimits<cpp_type>::kNegInf;
}
static const cpp_type* max_value() {
return &MathLimits<cpp_type>::kPosInf;
}
static bool IsVirtual() {
return false;
}
};
template<>
struct DataTypeTraits<DOUBLE> {
static const DataType physical_type = DOUBLE;
typedef double cpp_type;
static const char *name() {
return "double";
}
static void AppendDebugStringForValue(const void *val, std::string *str) {
str->append(SimpleDtoa(*reinterpret_cast<const double *>(val)));
}
static int Compare(const void *lhs, const void *rhs) {
return GenericCompare<DOUBLE>(lhs, rhs);
}
static bool AreConsecutive(const void* a, const void* b) {
return AreFloatsConsecutive<DOUBLE>(a, b);
}
static const cpp_type* min_value() {
return &MathLimits<cpp_type>::kNegInf;
}
static const cpp_type* max_value() {
return &MathLimits<cpp_type>::kPosInf;
}
static bool IsVirtual() {
return false;
}
};
template<>
struct DataTypeTraits<BINARY> {
static const DataType physical_type = BINARY;
typedef Slice cpp_type;
static const char *name() {
return "binary";
}
static void AppendDebugStringForValue(const void *val, std::string *str) {
const Slice *s = reinterpret_cast<const Slice *>(val);
str->push_back('"');
str->append(strings::CHexEscape(s->ToString()));
str->push_back('"');
}
static int Compare(const void *lhs, const void *rhs) {
const Slice *lhs_slice = reinterpret_cast<const Slice *>(lhs);
const Slice *rhs_slice = reinterpret_cast<const Slice *>(rhs);
return lhs_slice->compare(*rhs_slice);
}
static bool AreConsecutive(const void* a, const void* b) {
const Slice *a_slice = reinterpret_cast<const Slice *>(a);
const Slice *b_slice = reinterpret_cast<const Slice *>(b);
size_t a_size = a_slice->size();
size_t b_size = b_slice->size();
// Strings are consecutive if the larger is equal to the lesser with an
// additional null byte.
return a_size + 1 == b_size &&
(*b_slice)[a_size] == 0 &&
*a_slice == Slice(b_slice->data(), a_size);
}
static const cpp_type* min_value() {
static Slice s("");
return &s;
}
static const cpp_type* max_value() {
return nullptr;
}
static bool IsVirtual() {
return false;
}
};
template<>
struct DataTypeTraits<BOOL> {
static const DataType physical_type = BOOL;
typedef bool cpp_type;
static const char* name() {
return "bool";
}
static void AppendDebugStringForValue(const void* val, std::string* str) {
str->append(*reinterpret_cast<const bool *>(val) ? "true" : "false");
}
static int Compare(const void *lhs, const void *rhs) {
return GenericCompare<BOOL>(lhs, rhs);
}
static bool AreConsecutive(const void* a, const void* b) {
return AreIntegersConsecutive<BOOL>(a, b);
}
static const cpp_type* min_value() {
static bool b = false;
return &b;
}
static const cpp_type* max_value() {
static bool b = true;
return &b;
}
static bool IsVirtual() {
return false;
}
};
// Base class for types that are derived, that is that have some other type as the
// physical representation.
template<DataType PhysicalType>
struct DerivedTypeTraits {
typedef typename DataTypeTraits<PhysicalType>::cpp_type cpp_type;
static const DataType physical_type = PhysicalType;
static void AppendDebugStringForValue(const void *val, std::string *str) {
DataTypeTraits<PhysicalType>::AppendDebugStringForValue(val, str);
}
static int Compare(const void *lhs, const void *rhs) {
return DataTypeTraits<PhysicalType>::Compare(lhs, rhs);
}
static bool AreConsecutive(const void* a, const void* b) {
return DataTypeTraits<PhysicalType>::AreConsecutive(a, b);
}
static const cpp_type* min_value() {
return DataTypeTraits<PhysicalType>::min_value();
}
static const cpp_type* max_value() {
return DataTypeTraits<PhysicalType>::max_value();
}
static bool IsVirtual() {
return DataTypeTraits<PhysicalType>::IsVirtual();
}
};
template<>
struct DataTypeTraits<STRING> : public DerivedTypeTraits<BINARY>{
static const char* name() {
return "string";
}
static void AppendDebugStringForValue(const void *val, std::string *str) {
const Slice *s = reinterpret_cast<const Slice *>(val);
str->push_back('"');
str->append(strings::Utf8SafeCEscape(s->ToString()));
str->push_back('"');
}
};
template<>
struct DataTypeTraits<UNIXTIME_MICROS> : public DerivedTypeTraits<INT64>{
static const int kMicrosInSecond = 1000L * 1000L;
constexpr static const char* kDateFormat = "%Y-%m-%dT%H:%M:%S";
constexpr static const char* kDateMicrosAndTzFormat = "%s.%06dZ";
static const char* name() {
return "unixtime_micros";
}
static void AppendDebugStringForValue(const void* val, std::string* str) {
int64_t timestamp_micros = *reinterpret_cast<const int64_t *>(val);
time_t secs_since_epoch = timestamp_micros / kMicrosInSecond;
// If the time is negative we need to take into account that any microseconds
// will actually decrease the time in seconds by one.
int remaining_micros = static_cast<int>(timestamp_micros % kMicrosInSecond);
if (remaining_micros < 0) {
secs_since_epoch--;
remaining_micros = kMicrosInSecond - std::abs(remaining_micros);
}
struct tm tm_info;
gmtime_r(&secs_since_epoch, &tm_info);
char time_up_to_secs[24];
strftime(time_up_to_secs, sizeof(time_up_to_secs), kDateFormat, &tm_info);
char time[34];
snprintf(time, sizeof(time), kDateMicrosAndTzFormat, time_up_to_secs, remaining_micros);
str->append(time);
}
};
template<>
struct DataTypeTraits<DATE> : public DerivedTypeTraits<INT32>{
static constexpr int32_t kMinValue = -719162; // mktime(0001-01-01)
static constexpr int32_t kMaxValue = 2932896; // mktime(9999-12-31)
typedef int32_t cpp_type;
static const char* name() {
return "date";
}
static void AppendDebugStringForValue(const void* val, std::string* str);
static const cpp_type* min_value() {
static int32_t value = kMinValue;
return &value;
}
static const cpp_type* max_value() {
static int32_t value = kMaxValue;
return &value;
}
static bool IsValidValue(int32_t val) {
return val >= kMinValue && val <= kMaxValue;
}
};
template<>
struct DataTypeTraits<DECIMAL32> : public DerivedTypeTraits<INT32>{
static const char* name() {
return "decimal";
}
// AppendDebugStringForValue appends the (string representation of) the
// underlying integer value with the "_D32" suffix as there's no "full"
// type information available to format it.
static void AppendDebugStringForValue(const void *val, std::string *str) {
DataTypeTraits<physical_type>::AppendDebugStringForValue(val, str);
str->append("_D32");
}
};
template<>
struct DataTypeTraits<DECIMAL64> : public DerivedTypeTraits<INT64>{
static const char* name() {
return "decimal";
}
// AppendDebugStringForValue appends the (string representation of) the
// underlying integer value with the "_D64" suffix as there's no "full"
// type information available to format it.
static void AppendDebugStringForValue(const void *val, std::string *str) {
DataTypeTraits<physical_type>::AppendDebugStringForValue(val, str);
str->append("_D64");
}
};
template<>
struct DataTypeTraits<DECIMAL128> : public DerivedTypeTraits<INT128>{
static const char* name() {
return "decimal";
}
// AppendDebugStringForValue appends the (string representation of) the
// underlying integer value with the "_D128" suffix as there's no "full"
// type information available to format it.
static void AppendDebugStringForValue(const void *val, std::string *str) {
DataTypeTraits<physical_type>::AppendDebugStringForValue(val, str);
str->append("_D128");
}
};
template<>
struct DataTypeTraits<IS_DELETED> : public DerivedTypeTraits<BOOL>{
static const char* name() {
return "is_deleted";
}
static bool IsVirtual() {
return true;
}
};
template<>
struct DataTypeTraits<VARCHAR> : public DerivedTypeTraits<BINARY>{
static const char* name() {
return "varchar";
}
static void AppendDebugStringForValue(const void *val, std::string *str) {
const Slice *s = reinterpret_cast<const Slice *>(val);
str->push_back('"');
str->append(strings::Utf8SafeCEscape(s->ToString()));
str->push_back('"');
}
};
// Instantiate this template to get static access to the type traits.
template<DataType datatype>
struct TypeTraits : public DataTypeTraits<datatype> {
typedef typename DataTypeTraits<datatype>::cpp_type cpp_type;
static const DataType type = datatype;
static const size_t size = sizeof(cpp_type);
};
class Variant {
public:
Variant(DataType type, const void *value) {
Reset(type, value);
}
~Variant() {
Clear();
}
template<DataType Type>
void Reset(const typename DataTypeTraits<Type>::cpp_type& value) {
Reset(Type, &value);
}
// Set the variant to the specified type/value.
// The value must be of the relative type.
// In case of strings, the value must be a pointer to a Slice, and the data block
// will be copied, and released by the variant on the next set/clear call.
//
// Examples:
// uint16_t u16 = 512;
// Slice slice("Hello World");
// variant.set(UINT16, &u16);
// variant.set(STRING, &slice);
void Reset(DataType type, const void *value) {
CHECK(value != NULL) << "Variant value must be not NULL";
Clear();
type_ = type;
switch (type_) {
case UNKNOWN_DATA:
LOG(FATAL) << "Unreachable";
case IS_DELETED:
case BOOL:
numeric_.b1 = *static_cast<const bool *>(value);
break;
case INT8:
numeric_.i8 = *static_cast<const int8_t *>(value);
break;
case UINT8:
numeric_.u8 = *static_cast<const uint8_t *>(value);
break;
case INT16:
numeric_.i16 = *static_cast<const int16_t *>(value);
break;
case UINT16:
numeric_.u16 = *static_cast<const uint16_t *>(value);
break;
case DATE:
case DECIMAL32:
case INT32:
numeric_.i32 = *static_cast<const int32_t *>(value);
break;
case UINT32:
numeric_.u32 = *static_cast<const uint32_t *>(value);
break;
case DECIMAL64:
case UNIXTIME_MICROS:
case INT64:
numeric_.i64 = *static_cast<const int64_t *>(value);
break;
case UINT64:
numeric_.u64 = *static_cast<const uint64_t *>(value);
break;
case DECIMAL128:
case INT128:
numeric_.i128 = UnalignedLoad<int128_t>(value);
break;
case FLOAT:
numeric_.float_val = *static_cast<const float *>(value);
break;
case DOUBLE:
numeric_.double_val = *static_cast<const double *>(value);
break;
case STRING: // Fallthrough intended.
case VARCHAR:
case BINARY:
{
const Slice *str = static_cast<const Slice *>(value);
// In the case that str->size() == 0, then the 'Clear()' above has already
// set vstr_ to Slice(""). Otherwise, we need to allocate and copy the
// user's data.
if (str->size() > 0) {
auto blob = new uint8_t[str->size()];
memcpy(blob, str->data(), str->size());
vstr_ = Slice(blob, str->size());
}
}
break;
default: LOG(FATAL) << "Unknown data type: " << type_;
}
}
// Set the variant to a STRING type.
// The specified data block will be copied, and released by the variant
// on the next set/clear call.
void Reset(const std::string& data) {
Slice slice(data);
Reset(STRING, &slice);
}
// Set the variant to a STRING type.
// The specified data block will be copied, and released by the variant
// on the next set/clear call.
void Reset(const char *data, size_t size) {
Slice slice(data, size);
Reset(STRING, &slice);
}
// Returns the type of the Variant
DataType type() const {
return type_;
}
// Returns a pointer to the internal variant value
// The return value can be casted to the relative type()
// The return value will be valid until the next set() is called.
//
// Examples:
// static_cast<const int32_t *>(variant.value())
// static_cast<const Slice *>(variant.value())
const void *value() const {
switch (type_) {
case UNKNOWN_DATA: LOG(FATAL) << "Attempted to access value of unknown data type";
case IS_DELETED:
case BOOL: return &(numeric_.b1);
case INT8: return &(numeric_.i8);
case UINT8: return &(numeric_.u8);
case INT16: return &(numeric_.i16);
case UINT16: return &(numeric_.u16);
case DATE:
case DECIMAL32:
case INT32: return &(numeric_.i32);
case UINT32: return &(numeric_.u32);
case DECIMAL64:
case UNIXTIME_MICROS:
case INT64: return &(numeric_.i64);
case UINT64: return &(numeric_.u64);
case DECIMAL128:
case INT128: return &(numeric_.i128);
case FLOAT: return (&numeric_.float_val);
case DOUBLE: return (&numeric_.double_val);
case STRING:
case VARCHAR:
case BINARY: return &vstr_;
default: LOG(FATAL) << "Unknown data type: " << type_;
}
CHECK(false) << "not reached!";
return NULL;
}
bool Equals(const Variant *other) const {
if (other == NULL || type_ != other->type_)
return false;
return GetTypeInfo(type_)->Compare(value(), other->value()) == 0;
}
private:
DISALLOW_COPY_AND_ASSIGN(Variant);
void Clear() {
// No need to delete[] zero-length vstr_, because we always ensure that
// such a string would point to a constant "" rather than an allocated piece
// of memory.
if (vstr_.size() > 0) {
delete[] vstr_.mutable_data();
vstr_.clear();
}
}
union NumericValue {
bool b1;
int8_t i8;
uint8_t u8;
int16_t i16;
uint16_t u16;
int32_t i32;
uint32_t u32;
int64_t i64;
uint64_t u64;
int128_t i128;
float float_val;
double double_val;
};
DataType type_;
NumericValue numeric_;
Slice vstr_;
};
} // namespace kudu
#endif