Velox 支持 scalar(有大小没有方向)类型和复杂类型。标量类型分为一组固定的物理类型和一组可扩展的逻辑类型。物理类型决定数据在内存中的布局。逻辑类型向物理类型添加附加语义。
Phsical Types
每个物理类型都是用c++ type实现的,不会存储实际数据,下表是支持的物理类型、其对应的c++类型和每个值所需的固定宽度字节
Physical Type | C++ Type | Fixed Width (bytes) |
---|---|---|
BOOLEAN | bool | 0.125 (i.e. 1 bit) |
TINYINT | int8_t | 1 |
SMALLINT | int16_t | 2 |
INTEGER | int32_t | 4 |
BIGINT | int64_t | 8 |
HUGEINT | int128_t | 16 |
REAL | float | 4 |
DOUBLE | double | 8 |
TIMESTAMP | struct Timestamp | 16 |
VARCHAR | struct StringView | 16 |
VARBINARY | struct StringView | 16 |
OPAQUE | std::shared_ptr | 16 |
UNKNOWN | struct UnknownValue | 0 |
除了VARCHAR and VARBINARY 所有的物理类型都有和 c++类型一对一的映射。c++类型也在vector类中用作模板参数。例如,64 位整数向量表示为 FlatVector<int64_t>,其类型为 BIGINT。
OPAQUE类型可用于定义自定义类型。OPAQUE类型必须与唯一的std::type_index一起指定。此类型的值必须以std::shared_ptr提供,其中T是C++类型。下面给出了有关何时使用OPAQUE类型定义自定义类型的更多详细信息。
VARCHAR、VARBINARY、OPAQUE 每个值使用可变的字节数。这些类型在 C++ 类型中存储固定宽度部分,在其他地方存储可变宽度部分。所有其他类型的每个值都使用固定宽度字节,如上表所示。VARCHAR和VARBINARY FlatVector将每个值的固定宽度部分存储在StringView中。StringView是一个结构,包含一个4字节大小字段、一个4字节前缀字段和一个指向可变宽度部分的8字节字段指针。每个值的可变宽度部分存储在stringBuffers中。OPAQUE类型将可变宽度部分存储在FlatVector之外。
UNKNOWN类型用于表示unknown type的empty或全为null的vector。例如,SELECT array() 返回 ARRAY(UNKNOWN()),因为无法确定元素的类型。这是有效的,因为array中没有元素。array方法作用:返回具有给定元素的数组
TIMESTAMP类型用于表示特定的时间点。 TIMESTAMP 定义为自 UNIX epoch 以来秒和纳秒的总和。struct Timestamp 包含一个表示秒的 64 位有符号整数和表示纳秒的另一个 64 位无符号整数。纳秒表示时间戳的高精度部分,小于1秒。纳秒的有效范围是 [0, 10^9)。纪元之前的时间戳是使用负秒值指定的。
Logical Types
逻辑类型由物理类型支持并包含附加语义。同一物理类型可以支持多种逻辑类型。因此,了解 C++ 类型不足以推断逻辑类型。下表显示了支持的逻辑类型及其相应的物理类型。
Logical Type | Physical Type |
---|---|
DATE | INTEGER |
DECIMAL | BIGINT if precision <= 18, HUGEINT if precision >= 19 |
INTERVAL DAY TO SECOND | BIGINT |
INTERVAL YEAR TO MONTH | INTEGER |
Custom Types
大多数自定义类型可以表示为逻辑类型,并且可以通过扩展现有的物理类型来构建。例如,下面描述的Presto类型是通过扩展物理类型来实现的。当没有物理类型可用于支持逻辑类型时,必须使用OPAQUE类型。
Complex Types
Velox 支持 ARRAY、MAP 和 ROW 复杂类型。复杂类型由标量(scalar)类型组成,可以与其他复杂类型嵌套。
例如: MAP<INTEGER, ARRAY> 是一个复杂类型,其key是标量类型 INTEGER,value是元素类型为 BIGINT 的复杂类型 ARRAY。
Array类型包含其元素类型。 Map类型包含键类型和值类型。row类型包含其字段类型及其名称。
源码解析
Type 定义了velox的内置类型的一些信息提供一些接口,不存储实际的数据
枚举类型TypeKind定义类型的种类
enum class TypeKind : int8_t {BOOLEAN = 0,TINYINT = 1,SMALLINT = 2,INTEGER = 3,BIGINT = 4,REAL = 5,DOUBLE = 6,VARCHAR = 7,VARBINARY = 8,TIMESTAMP = 9,HUGEINT = 10,// Enum values for ComplexTypes start after 30 to leave// some values space to accommodate adding new scalar/native// types above.ARRAY = 30,MAP = 31,ROW = 32,UNKNOWN = 33,FUNCTION = 34,OPAQUE = 35,INVALID = 36
};
下面是Type的继承结构
Type|TypeBase<T>/ | \ScalarType ArrayType MapType
Type 是抽象基类继承自Tree,复杂类型子类例如ArrayType和MapType会包含子类型,从而形成Tree
TypeBase:从Type派生而来的中间类,借助TypeTraits实现方法,TypeTraits使用模板特化技术根据TypeKind定义以下属性
template <TypeKind KIND>
struct TypeTraits {};template <>
struct TypeTraits<TypeKind::BOOLEAN> {using ImplType = ScalarType<TypeKind::BOOLEAN>; // velox实现的 typeusing NativeType = bool; // c++ type,对于无法映射的复杂类型这里是voidusing DeepCopiedType = NativeType;static constexpr uint32_t minSubTypes = 0;static constexpr uint32_t maxSubTypes = 0;static constexpr TypeKind typeKind = TypeKind::BOOLEAN;// TypeKindstatic constexpr bool isPrimitiveType = true; // 是否为c++原生类型static constexpr bool isFixedWidth = true;// 是否为固定长度static constexpr const char* name = "BOOLEAN";//类型名
};class TypeBase : public Type {public:using NativeType = TypeTraits<KIND>;TypeBase() : Type{KIND} {}bool isPrimitiveType() const override {return TypeTraits<KIND>::isPrimitiveType;}bool isFixedWidth() const override {return TypeTraits<KIND>::isFixedWidth;}const char* kindName() const override {return TypeTraits<KIND>::name;}const char* name() const override {return TypeTraits<KIND>::name;}const std::vector<TypeParameter>& parameters() const override {static const std::vector<TypeParameter> kEmpty = {};return kEmpty;}
};
ScalarType:标量类型类模板,标量对象只含单个元素,以下类型都从ScalarType类模板实例而来:
- 各种整型类型(对应8-64位宽度的整型,TinyintType、SmallintType、IntegerType、BigintType)
- RealType、DoubleType
- VarcharType、VarbinaryType
- TimestampType、DateType、IntervalDayTimeType
using IntegerType = ScalarType<TypeKind::INTEGER>;
using BooleanType = ScalarType<TypeKind::BOOLEAN>;
ArrayType:对应数组类型,包含一个TypePtr child_成分(也就是Tree的子节点),数组类型所有内部元素的类型相同(只有一个)。
MapType:对应映射类型,包含TypePtr key_ + TypePtr value_两个成分,从Key映射到Value,符合一般映射的语义。
RowType:对应行类型TypeKind=ROW,代表行的每一列的meta信息,包括2个成分。
- 一个string name_的列表(各列的名称)
- 一个TypePtr children_的列表(各列的Type信息)
类型工厂:TypeFactory
类模板TypeFactory实现create()接口,用于创建某种具体类型,参考设计模式的工厂方法。
对于标量类型,通常只需要一个唯一的类型实例,因为标量类型不包含额外状态信息(额外是指相对于Type本身状态外)。
对于非标量类型(TypeKind>=30),比如ArrayType、MapType、RowType等类型,因为每个各MapType的Key、Value并不相同,所以需要针对每个类型创建对应的Type。
template <TypeKind KIND>
struct TypeFactory {static std::shared_ptr<const typename TypeTraits<KIND>::ImplType> create() {return TypeTraits<KIND>::ImplType::create();}
};template <>
struct TypeFactory<TypeKind::UNKNOWN> {static std::shared_ptr<const UnknownType> create() {return std::make_shared<UnknownType>();}
};
利用宏和模板提供了获取类型实例的方法使得INTEGER()等价于return ScalarType<TypeKind::INTEGER>::create();
#define VELOX_SCALAR_ACCESSOR(KIND) \std::shared_ptr<const ScalarType<TypeKind::KIND>> KIND()
#define VELOX_DEFINE_SCALAR_ACCESSOR(KIND) \std::shared_ptr<const ScalarType<TypeKind::KIND>> KIND() { \return ScalarType<TypeKind::KIND>::create(); \}
VELOX_DEFINE_SCALAR_ACCESSOR(INTEGER);
CppToType支持根据c++ type获取velox type的相关信息(保存在TypeTraits),实现方法也是模板特化
template <typename T> struct CppToType {};
template <> struct CppToType<int128_t> : public CppToTypeBase<TypeKind::HUGEINT> {};
struct CppToTypeBase : public TypeTraits<KIND> {static auto create() {return TypeFactory<KIND>::create();}
};CppToType<T>::typeKind // 获取c++ type对应的TypeKind
参考
https://facebookincubator.github.io/velox/develop/types.html