PostgreSQL码农集散地

pgvector 源码学习: 9 类型系统架构 (Type System Architecture)

pgvector 源码学习: 9 类型系统架构 (Type System Architecture)

今天系列文章一把梭了(接下来每半个小时发一篇, 敬请关注本公众号)。



本文对 pgvector 的内部类型系统架构 (type system architecture) 进行了技术深入解析,涵盖了内存布局 (memory layouts)、PostgreSQL 类型注册 (type registration)、输入/输出函数 (input/output functions)、类型修饰符 (type modifiers) 以及**类型转换系统 (cast system)**。

概述 (Overview)

pgvector 实现了四种自定义的 PostgreSQL 数据类型 (data types),每种都通过 PostgreSQL 的可扩展类型系统 (extensible type system) 进行注册。其实现遵循 PostgreSQL 的 varlena (可变长度 (variable-length)) 约定,并提供了完整的 I/O、二进制序列化 (binary serialization)、类型修改 (type modification) 和类型转换 (casting) 基础设施。

所有四种类型都通过以下方式集成到 PostgreSQL 的类型系统中:

  • 用于 SQL 字面量 (SQL literals) 的文本输入/输出函数 (Text input/output functions)
  • 用于协议序列化 (protocol serialization) 的二进制发送/接收函数 (Binary send/receive functions)
  • 用于维度约束 (dimension constraints) 的类型修饰符函数 (Type modifier functions)
  • 用于类型互操作性 (type interoperability) 的类型转换函数 (Cast functions)
  • 确保数据完整性 (data integrity) 的验证函数 (Validation functions)

来源:
src/vector.c 1-50src/halfvec.c 1-50src/sparsevec.c 1-50sql/vector.sql 1-50

内存布局与存储格式 (Memory Layout and Storage Format)

密集向量类型 (Dense Vector Types)

vector 和 halfvec 都使用一个简单的**密集数组表示 (dense array representation)**,并带有 **PostgreSQL varlena 头部 (header)**:



Image

类型 (Type)
头部大小 (Header Size)
元素大小 (Element Size)
最大维度 (Max Dimensions)
总大小公式 (Total Size Formula)
vector
8 字节 (bytes)
4 字节 (float32)
16,000
8 + 4×dim
halfvec
8 字节 (bytes)
2 字节 (float16)
16,000
8 + 2×dim

unused 字段保留供将来使用,并且必须始终为零。它提供对齐填充 (alignment padding) 和**扩展能力 (extension capability)**。

来源:
src/vector.h 1-31src/halfvec.h 1-70

稀疏向量类型 (Sparse Vector Type)

sparsevec 类型使用压缩稀疏行 (compressed sparse row, CSR) 格式,其中索引 (indices) 存储在值 (values) 之前:



Image

稀疏向量布局 (Sparse Vector Layout):

  • 头部 (Header): 16 字节 (vl_len_, dim, nnz, unused)
  • 索引数组 (Indices array): nnz × 4 字节 (int32 数组)
  • 值数组 (Values array): nnz × 4 字节 (float 数组)
  • 总大小 (Total size): 16 + 8×nnz 字节

宏 SPARSEVEC_SIZE(nnz) 计算总大小,而 SPARSEVEC_VALUES(x) 通过计算偏移量越过索引数组来返回指向值数组的**指针 (pointer)**。

来源:
src/sparsevec.h 1-41src/sparsevec.c 138-151

位向量类型 (Bit Vector Type)

位向量 (Bit vectors) 使用 PostgreSQL 内置的 varbit 类型(可变长度位字符串 (variable-length bit string)),并通过 binary_quantize() 函数创建,而不是拥有单独的类型定义 (type definition)。它们为每个维度 (dimension) 存储一位 (bit),从而提供了极高的**压缩率 (compression)**。

来源:
src/vector.c 941-967sql/vector.sql 55-56

PostgreSQL 类型注册 (Type Registration with PostgreSQL)

注册流程 (Registration Flow)



Image

每种类型都在 sql/vector.sql 中注册,并带有五个必需的函数:

函数 (Function)
目的 (Purpose)
C 实现 (C Implementation)
*_in
解析文本字面量 (text literals)
vector_in()
、halfvec_in()、sparsevec_in()
*_out
生成文本输出 (text output)
vector_out()
、halfvec_out()、sparsevec_out()
*_typmod_in
解析类型修饰符 (type modifiers)
vector_typmod_in()
、halfvec_typmod_in()、sparsevec_typmod_in()
*_recv反序列化二进制 (Deserialize binary)vector_recv()
、halfvec_recv()、sparsevec_recv()
*_send序列化二进制 (Serialize binary)vector_send()
、halfvec_send()、sparsevec_send()

所有类型都使用 STORAGE = external,以便在数据超过 PostgreSQL 的 TOAST 阈值 (TOAST threshold) 时,将其**行外存储 (out-of-line)**。

来源:
sql/vector.sql 4-30sql/vector.sql 336-360sql/vector.sql 693-717

输入/输出系统 (Input/Output System)

文本格式解析 (Text Format Parsing)

Image




向量输入 (Vector Input) (src/vector.c 165-270):

  • 解析格式: [value1,value2,...]
  • 使用 strtof() 进行**浮点数解析 (float parsing)(避免二次舍入 (double-rounding)**)
  • 使用 CheckElement() 验证每个**元素 (element)**(无 NaN (非数字),无 Infinity (无穷大))
  • 使用 CheckDim() 和 CheckExpectedDim() 验证维度计数 (dimension count)

稀疏向量输入 (Sparse Vector Input) (src/sparsevec.c 187-389):

  • 解析格式: {index1:value1,index2:value2,...}/dimensions
  • 文本中使用**基于 1 的编号 (1-based numbering)(内部转换为基于 0 的 (0-based)**)
  • 自动过滤零值 (zero values)
  • 使用 qsort() 和 CompareIndices() 对索引 (indices) 进行排序
  • 验证索引是唯一 (unique) 的、已排序 (sorted) 的且在范围内 (in-range)

半向量输入 (Half Vector Input) (src/halfvec.c 165-271):

  • 与 vector 相同的文本格式: [value1,value2,...]
  • 使用 Float4ToHalfUnchecked() 将浮点数 (float) 转换为半精度 (half-precision)
  • 额外的范围检查 (range checking) 以防止半精度溢出 (overflow)

来源:
src/vector.c 165-270src/sparsevec.c 187-389src/halfvec.c 165-271

二进制序列化 (Binary Serialization)

二进制发送/接收函数 (Binary send/receive functions) 使用 PostgreSQL 的 StringInfo **缓冲区 API (buffer API),并确保平台无关的字节顺序 (platform-independent byte ordering)**:

Image

vector 的二进制格式 (Binary Format for vector):

  • 2 字节: 维度 (int16)
  • 2 字节: unused 字段 (始终为 0)
  • dim × 4 字节: 浮点数值 (float values)

sparsevec 的二进制格式 (Binary Format for sparsevec):

  • 4 字节: 维度 (int32)
  • 4 字节: nnz (非零元素数量 (number of non-zero elements)) (int32)
  • 4 字节: unused 字段 (始终为 0)
  • nnz × 4 字节: 索引 (int32[], 基于 0)
  • nnz × 4 字节: 值 (float[])

半向量使用自定义的 pq_getmsghalf() 和 pq_sendhalf() 辅助函数来序列化 (serialize) 16 位的**半精度值 (half-precision values)**。

来源:
src/vector.c 363-411src/sparsevec.c 492-565src/halfvec.c 28-55

类型修饰符与验证 (Type Modifiers and Validation)

类型修饰符 (Type modifiers) 允许用户在类型级别指定**维度约束 (dimension constraints)**(例如 vector(3)、halfvec(128)):



Image

类型修饰符函数 (Type Modifier Functions):

函数 (Function)
验证逻辑 (Validation Logic)
无效时的错误 (Error on Invalid)
vector_typmod_in()1 ≤ dim ≤ 16000ERRCODE_INVALID_PARAMETER_VALUE
 (无效参数值错误码)
halfvec_typmod_in()1 ≤ dim ≤ 16000ERRCODE_INVALID_PARAMETER_VALUE
sparsevec_typmod_in()1 ≤ dim ≤ 1,000,000,000ERRCODE_INVALID_PARAMETER_VALUE

维度验证辅助函数 (Dimension Validation Helpers):

  • CheckDim(int dim) - 验证维度 (dimension) 是否在有效范围内
  • CheckExpectedDim(int32 typmod, int dim) - 强制执行类型修饰符约束
  • CheckDims(Type *a, Type *b) - 确保两个向量具有匹配的维度 (matching dimensions)

这些函数在以下过程中被调用:

  • 输入函数 (vector_in、halfvec_in、sparsevec_in)
  • 二进制接收 (vector_recv、halfvec_recv、sparsevec_recv)
  • 类型转换函数 (array_to_vector、halfvec_to_vector 等)
  • 距离函数 (Distance functions)(确保操作数 (operands) 兼容)

来源:
src/vector.c 72-96src/halfvec.c 72-96src/sparsevec.c 42-87src/vector.c 332-358

类型转换系统与类型转换 (Cast System and Type Conversions)

Cast Graph



Image

类型转换类别 (Cast Categories)

**隐式转换 (IMPLICIT casts)**(自动发生,无需显式转换):

  • vector ↔ halfvec
  • vector ↔ sparsevec
  • halfvec ↔ sparsevec
  • vector → real[]
  • vector → bit(通过 binary_quantize)
  • halfvec → bit(通过 binary_quantize)

**赋值转换 (ASSIGNMENT casts)(允许在赋值操作中进行,但在表达式中需要显式转换 (explicit cast)**):

  • int4[] → vector, halfvec, sparsevec
  • real[] → vector, halfvec, sparsevec
  • double precision[] → vector, halfvec, sparsevec
  • numeric[] → vector, halfvec, sparsevec
  • halfvec → real[]

来源:
sql/vector.sql 154-170sql/vector.sql 490-512sql/vector.sql 862-886

数组转换实现 (Array Conversion Implementation)

array_to_vector() 系列函数处理从 PostgreSQL 数组 (arrays) 进行的转换:



Image

数组到向量转换 (Array to Vector Conversion) (src/vector.c 432-501):

  • 接受 int4[]、real[]、double precision[]、numeric[]
  • 使用 deconstruct_array() 来提取元素
  • 对每个元素执行特定类型的转换 (type-specific conversion)
  • 验证所有元素都是有限 (finite) 的(无 NaN,无 Infinity)

数组到稀疏向量 (Array to Sparse Vector) (src/sparsevec.c 672-798):

  • 在第一次遍历 (pass) 中计算非零元素 (non-zero elements) 的数量
  • 根据 nnz 计数分配稀疏结构 (sparse structure)
  • 仅存储非零值及其索引
  • 对 MSVC /fp:fast 模式进行特殊处理,以检测 NaN/Infinity

来源:
src/vector.c 432-501src/sparsevec.c 672-798src/halfvec.c 425-494

密集-稀疏转换 (Dense-Sparse Conversions)



Image

密集到稀疏 (Dense to Sparse) (src/sparsevec.c 586-624):

  • 第一次遍历:计算非零元素的数量
  • 使用精确的 nnz 计数 (exact nnz count) 分配稀疏结构
  • 第二次遍历:复制索引和值

稀疏到密集 (Sparse to Dense) (src/vector.c 1303-1321):

  • 分配**密集向量 (dense vector)**(由 palloc0 **初始化为零 (initialized to zeros)**)
  • 使用索引将稀疏值分散 (Scatter sparse values) 到密集数组中

半精度到稀疏 (Half to Sparse) (src/sparsevec.c 629-667):

  • 使用 HalfIsZero() 检测零半精度值 (zero half-precision values)
  • 在复制过程中使用 HalfToFloat4() 将半精度转换为浮点数 (float)

来源:
src/sparsevec.c 586-624src/vector.c 1303-1321src/sparsevec.c 629-667

内部辅助函数与宏 (Internal Helper Functions and Macros)

内存分配辅助函数 (Memory Allocation Helpers)

每种类型都提供一个**初始化函数 (initialization function)**,用于分配和初始化结构:

函数 (Function)
实现 (Implementation)
用法 (Usage)
InitVector(dim)palloc0(VECTOR_SIZE(dim))SET_VARSIZE()src/vector.c
 118-130
InitHalfVector(dim)palloc0(HALFVEC_SIZE(dim))SET_VARSIZE()src/halfvec.c
 118-130
InitSparseVector(dim, nnz)palloc0(SPARSEVEC_SIZE(nnz))SET_VARSIZE()src/sparsevec.c
 138-151

所有函数都使用 palloc0() 进行**零初始化分配 (zero-initialized allocation)**,并使用 SET_VARSIZE() 设置 **varlena 头部 (varlena header)**。

Datum 提取宏 (Datum Extraction Macros)

// Vector type
#define DatumGetVector(x)       ((Vector *) PG_DETOAST_DATUM(x))  
#define PG_GETARG_VECTOR_P(x)   DatumGetVector(PG_GETARG_DATUM(x))  
#define PG_RETURN_VECTOR_P(x)   PG_RETURN_POINTER(x)  

// HalfVector type
#define DatumGetHalfVector(x)   ((HalfVector *) PG_DETOAST_DATUM(x))  
#define PG_GETARG_HALFVEC_P(x)  DatumGetHalfVector(PG_GETARG_DATUM(x))  
#define PG_RETURN_HALFVEC_P(x)  PG_RETURN_POINTER(x)  

// SparseVector type
#define DatumGetSparseVector(x)     ((SparseVector *) PG_DETOAST_DATUM(x))  
#define PG_GETARG_SPARSEVEC_P(x)    DatumGetSparseVector(PG_GETARG_DATUM(x))  
#define PG_RETURN_SPARSEVEC_P(x)    PG_RETURN_POINTER(x)  

这些宏 (macros) 自动处理 PostgreSQL 的 Datum 类型 和 TOAST(**超大属性存储技术 (The Oversized-Attribute Storage Technique))解压缩 (decompression)**。

来源:
src/vector.h 6-9src/halfvec.h 56-58src/sparsevec.h 7-9

稀疏向量值访问 (Sparse Vector Value Access)

由于稀疏向量值存储在索引数组之后,一个辅助函数会计算**值指针 (values pointer)**:

static inline float *  
SPARSEVEC_VALUES(SparseVector * x)  
{  
return (float *) (((char *) x) + offsetof(SparseVector, indices) +   
                      (x->nnz * sizeof(int32)));  
}  

这种指针运算 (pointer arithmetic) 跳过头部和索引,以到达值数组。

来源:
src/sparsevec.h 32-36

验证函数 (Validation Functions)



Image

所有验证函数 (validation functions) 都使用 ereport(ERROR, ...) 来引发带有相应错误码 (error codes) 的 **PostgreSQL 异常 (exceptions)**:

  • ERRCODE_DATA_EXCEPTION(数据异常错误码)- 无效的数据值
  • ERRCODE_PROGRAM_LIMIT_EXCEEDED(程序限制超出错误码)- 维度限制超出
  • ERRCODE_INVALID_PARAMETER_VALUE(无效参数值错误码)- 无效的类型修饰符

来源:
src/vector.c 60-113src/halfvec.c 60-113src/sparsevec.c 29-116

半精度实用工具 (Half-Precision Utilities)

halfvec 类型使用一个单独的**实用工具层 (utility layer),用于半精度浮点数转换 (half-precision float conversions)**:

转换函数 (Conversion Functions):

  • Float4ToHalf(float) - 将 float32 转换为半精度 (half),带有范围检查 (range checking)
  • Float4ToHalfUnchecked(float) - 转换时不进行溢出检查 (overflow checks)
  • HalfToFloat4(half) - 将半精度转换为 float32
  • HalfIsNan(half) - 检查 NaN (非数字)
  • HalfIsInf(half) - 检查无穷大 (infinity)
  • HalfIsZero(half) - 检查零值 (zero value)

平台调度 (Platform Dispatch):

系统使用运行时 CPU 检测 (runtime CPU detection) 来选择**优化的实现 (optimized implementations)**:

  • F16C 支持: 在带有 F16C 的 x86-64 上使用硬件指令 (hardware instructions)
  • FLT16 支持: 在可用时使用编译器的原生 _Float16 类型
  • 回退 (Fallback): 使用位操作 (bit manipulation) 的软件实现 (Software implementation)

来源:
src/halfvec.h 38-51src/halfutils.hsrc/halfutils.c

总结 (Summary)

pgvector 的类型系统架构 (type system architecture) 展示了清晰的**关注点分离 (separation of concerns)**:

  1. 内存布局 (Memory Layout): 紧凑的 varlena 结构 (varlena structures) 和特定于类型的优化 (type-specific optimizations)
  2. PostgreSQL 集成 (PostgreSQL Integration): 完整的 I/O、二进制序列化和类型修饰符支持
  3. 验证 (Validation): 在每个入口点 (entry point) 进行全面检查 (Comprehensive checking)
  4. 类型安全 (Type Safety): 维度检查 (Dimension checking) 和元素验证 (element validation) 可防止无效数据
  5. 互操作性 (Interoperability): 丰富的类型转换系统 (Rich cast system) 实现了类型之间的无缝转换 (seamless conversions)

该架构遵循 PostgreSQL 约定,同时增加了领域特定的优化 (domain-specific optimizations),例如稀疏存储 (sparse storage) 和半精度支持 (half-precision support)。所有公共 API (public APIs) 均通过SQL 函数定义 (SQL function definitions) 公开,这些定义委托给通过 PG_FUNCTION_INFO_V1 注册的 C 实现。

来源:
src/vector.c 1-1322src/halfvec.c 1-1364src/sparsevec.c 1-1257sql/vector.sql 1-1000