- 数据集成
- 数据工程
- 数据分析
【免费下载链接】cloudquery
Data pipelines for cloud config and security data. Build cloud asset inventory, CSPM, FinOps, and vulnerability management solutions. Extract from AWS, Azure, GCP, and 70+ cloud and SaaS sources.
本文围绕 CloudQuery 仓库中 SQLite 目标插件(
plugins/destination/sqlite)的官方类型说明文档(types.md)展开,系统讲解 Apache Arrow 列类型与 SQLite 存储类型之间的映射规则、SQLite 简化的类型系统原理,并结合插件源码验证每一类映射的底层实现。读完本文,你将掌握:哪些 Arrow 类型被 SQLite 插件支持、它们分别落到 SQLite 的哪个存储类型、为什么不支持的复杂类型统一退化为text,以及建表、写入、读取时这套映射如何闭环工作。
一、背景:CloudQuery 的列类型为什么需要转换
CloudQuery 从 AWS、Azure、GCP 以及 70+ 云与 SaaS 数据源抽取数据时,以 Apache Arrow 的内存列式格式承载数据。当数据落地到 SQLite 目标时,插件必须把 Arrow 的列类型翻译成 SQLite 能理解的类型。
SQLite 与大多数数据库不同,它采用动态类型系统,只有 5 种存储类(storage class):
| SQLite 存储类 | 说明 |
|---|---|
NULL | 空值 |
INTEGER | 有符号整数,按值大小自动使用 1/2/3/4/6/8 字节存储 |
REAL | 8 字节 IEEE 浮点数 |
TEXT | 文本字符串,采用数据库编码(UTF-8/UTF-16) |
BLOB | 按输入原样存储的二进制数据 |
因此,CloudQuery 的 SQLite 插件需要在 Arrow 的丰富类型体系与 SQLite 的 5 类存储之间做一次收敛映射。这一映射的官方定义就在 types.md 中,而它的代码实现位于 client/types.go 的arrowTypeToSqliteStr函数。
二、官方类型映射总表
以下是 types.md 中给出的完整映射表(原文 33 种 Arrow 列类型全部继承于此,并保留“不支持的列类型一律映射为text”这条核心规则):
| Arrow 列类型 | 是否支持 | SQLite 类型 |
|---|---|---|
| Binary | ✅ 支持 | blob |
| Boolean | ✅ 支持 | boolean |
| Date32 | ✅ 支持 | text |
| Date64 | ✅ 支持 | text |
| Decimal | ✅ 支持 | text |
| Dense Union | ✅ 支持 | text |
| Dictionary | ✅ 支持 | text |
| Duration | ✅ 支持 | text |
| Fixed Size List | ✅ 支持 | text |
| Float16 | ✅ 支持 | real |
| Float32 | ✅ 支持 | real |
| Float64 | ✅ 支持 | real |
| Inet | ✅ 支持 | text |
| Int8 | ✅ 支持 | integer |
| Int16 | ✅ 支持 | integer |
| Int32 | ✅ 支持 | integer |
| Int64 | ✅ 支持 | integer |
| Interval[DayTime] | ✅ 支持 | text |
| Interval[MonthDayNano] | ✅ 支持 | text |
| Interval[Month] | ✅ 支持 | text |
| JSON | ✅ 支持 | text |
| Large Binary | ✅ 支持 | blob |
| Large List | ✅ 支持 | text |
| Large String | ✅ 支持 | text |
| List | ✅ 支持 | text |
| MAC | ✅ 支持 | text |
| Map | ✅ 支持 | text |
| String | ✅ 支持 | text |
| Struct | ✅ 支持 | text |
| Time32 | ✅ 支持 | text |
| Time64 | ✅ 支持 | text |
| Timestamp | ✅ 支持 | timestamp |
| UUID | ✅ 支持 | text |
| Uint8 | ✅ 支持 | integer |
| Uint16 | ✅ 支持 | integer |
| Uint32 | ✅ 支持 | integer |
| Uint64 | ✅ 支持 | integer |
| Union | ✅ 支持 | text |
注意:表中列出的 33 种 Arrow 类型中,32 种被明确标记为「✅ 支持」,仅在前言声明“Unsupported types are always mapped to
text”(不支持的列类型总是映射为text),作为兜底策略存在。从实现上看,凡未进入arrowTypeToSqliteStr显式分支的类型,都会落入default分支返回text(见下文源码解析)。
三、官方 Notes:SQLite 类型系统的五个要点
types.md 在映射表之后给出了五条关键说明,这些是理解整张映射表的理论基础:
- SQLite 的类型系统极其简化:只有
NULL、INTEGER、REAL、TEXT、BLOB五种存储类; - 所有整数类型(有符号与无符号,8 位到 64 位)统一存储为 SQLite
integer; - 所有浮点类型(Float16、Float32、Float64)统一存储为 SQLite
real; - 复杂数据类型(如 JSON、List、Struct)会被序列化后以
text形式存储; - 二进制数据使用 SQLite 的
blob存储类; - SQLite 的动态类型系统允许数据在声明列类型之外灵活存储,因此即使列的声明类型与实际写入的值类型不一致,也不会造成写入失败。
第 5 点需要特别展开:SQLite 采用**动态类型(type affinity)**机制——建表时声明的类型只是“亲和性建议”,实际写入时每个值按自身情况独立选择存储类。这意味着即使某列被声明为integer,写入一个TEXT值也不会报错。这也正是 CloudQuery SQLite 插件可以放心地把大量复杂类型全部映射为text的底层原因:读取时再通过字符串反序列化还原成 Arrow 类型即可(见第六节)。
四、源码级验证:映射函数arrowTypeToSqliteStr
映射表的实现核心是 client/types.go 中的arrowTypeToSqliteStr函数。它按 Arrow 类型的 ID(arrow.DataType.ID())做 switch 分发,与文档映射表一一对应:
func (*Client) arrowTypeToSqliteStr(t arrow.DataType) string { switch t.ID() { case arrow.BINARY, arrow.LARGE_BINARY: return "blob" case arrow.INT8, arrow.INT16, arrow.INT32, arrow.INT64, arrow.UINT8, arrow.UINT16, arrow.UINT32, arrow.UINT64: return "integer" case arrow.FLOAT16, arrow.FLOAT32, arrow.FLOAT64: return "real" case arrow.BOOL: return "boolean" case arrow.TIMESTAMP: return "timestamp" default: return "text" } }这份实现可以逐一印证文档的映射规则:
blob分支:只有BINARY与LARGE_BINARY两类落入,与文档表中 Binary/Large Binary →blob完全一致;integer分支:8 种整数类型(Int8/16/32/64 与 Uint8/16/32/64)全部收敛到integer,印证了「所有整数类型统一存为 integer」;real分支:Float16/32/64 三种浮点类型全部收敛到real;boolean与timestamp:仅 Boolean 与 Timestamp 两个 Arrow 类型独享这两个 SQLite 类型名;default分支(text):包括 String、Date、JSON、List、Struct、Map、UUID、Decimal、所有 Interval/Union/Dictionary 等在内的其余全部类型都落到这里,印证了「复杂类型一律序列化为 text」以及「不支持的未知类型兜底为 text」。
配套的逆向映射:arrowTypeToSqlite与sqliteTypeToArrowType
同一文件中还有两个重要函数,共同构成完整的双向往返:
arrowTypeToSqlite(t arrow.DataType) arrow.DataType(types.go):在建表前把 Arrow 字段“归一化”,将各类整数统一为arrow.PrimitiveTypes.Int64、浮点统一为Float64、时间戳统一为Timestamp_us、其余一律归一为LargeString。这样从源头保证了写入 SQLite 的值类型与建表声明的类型一致,规避动态类型系统下的值/声明不一致问题。sqliteTypeToArrowType(t string) arrow.DataType(types.go):读取表结构(PRAGMA table_info)时把 SQLite 的integer/real/text/blob/boolean/timestamp还原回对应的 Arrow 类型,用于把本地 SQLite 表映射回schema.Table。
五、建表时如何应用映射:createTableIfNotExist
映射最终落地在 client/migrate.go 的createTableIfNotExist中。该函数遍历表的每一列,用arrowTypeToSqliteStr(col.Type)取得 SQLite 列类型,然后拼装CREATE TABLE IF NOT EXISTS语句:
for i, col := range table.Columns { sqlType := c.arrowTypeToSqliteStr(col.Type) if sqlType == "" { c.logger.Warn().Str("table", table.Name).Str("column", col.Name).Msg("Column type is not supported, skipping") continue } fieldDef := identifier(col.Name) + ` ` + sqlType if col.NotNull { fieldDef += " NOT NULL" } sb.WriteString(fieldDef) ... }这里有两个值得注意的工程细节:
NOT NULL约束:如果源列定义了NotNull,建表语句会追加NOT NULL,这会影响后续迁移(见canAutoMigrate对主键/非空列的保守处理);- 复合主键:当表存在主键列时,函数末尾会追加形如
CONSTRAINT "<table>_cqpk" PRIMARY KEY ("col1","col2")的复合主键约束(migrate.go),这一约束又决定了写入时走INSERT OR REPLACE的 upsert 路径(见下文)。
六、写入与读取:映射如何闭环
类型映射不只是“建表时定个列类型”,还贯穿写入与读取两个方向。
写入方向:Arrow Record → SQLite 值
写入路径的核心是 typeconv/values.go 的FromArray函数。它对不同 Arrow 数组做差异化取值:
- 整数/布尔/字符串/浮点:通过泛型辅助函数
primitiveValue(typeconv/primitive.go)直接取出底层值; - Float16:通过
float16Value(typeconv/special.go)把半精度浮点转成float32再落库; - Binary/FixedSizeBinary/LargeBinary:通过
byteArrValue(typeconv/special.go)把字节数组转为字符串存储(注意:二进制列在写库时转成了字符串,读回时再还原,见下文); - 其余所有类型(JSON、List、Struct、Map、UUID、时间、Decimal 等):统一走
valueStrData(typeconv/special.go),调用arr.ValueStr(i)获取该值的字符串表示后写入text列——这正是文档「复杂类型序列化存为 text」的实现细节。
写入 SQL 的生成在 client/write.go:
- 表没有主键时生成普通
INSERT INTO(insert函数,write.go); - 表有主键时生成
INSERT OR REPLACE INTO(upsert函数,write.go),实现基于主键的幂等覆盖写入,避免cloudquery sync重复同步时产生重复行。
读取方向:SQLite 值 → Arrow Record
读取路径在 client/read.go:
createResultsArray(read.go)按列类型选择sql.NullBool/[]byte/sql.NullInt64/sql.NullFloat64/sql.NullString作为扫描目标;reverseTransform(read.go)把扫描到的值按列类型还原为对应的 Arrow Builder 值:整数还原为 Int8/Int16/…/Uint64 各自的 Builder,浮点还原为 Float32/Float64,字符串还原为 String,二进制还原为BinaryBuilder.Append([]byte);- 对于以
text存储的复杂类型,reverseTransform的default分支调用appendFromString(实现见 client/append_from_string.go),把文本反序列化回原始 Arrow 类型,从而保证从 SQLite 读出的数据与源端 Arrow 类型一致。
七、实战建议与注意事项
结合 types.md 与源码实现,给出几条可直接落地的实践建议:
- 查询复杂字段注意序列化格式:JSON、List、Struct、Map、UUID、Date、Decimal 等字段在 SQLite 中都以
text存储。用sqlite3CLI 直接查看时看到的是序列化字符串;若需要以原生类型消费,建议仍通过 CloudQuery 的读取链路(或读回后反序列化),而不要假设某种固定的文本格式。具体以valueStrData调用 ArrowValueStr的语义为准(typeconv/special.go)。 - 二进制与 Blob:
Binary/Large Binary列在建表时声明为blob,但写入时byteArrValue会先转为字符串、读回时再[]byte还原(typeconv/special.go)。若需要在库外直接用 SQL 处理这些二进制内容,请先验证其实际存储形态。 - 无主键表是追加语义,有主键表是覆盖语义:
cloudquery sync重复运行同一任务时,带主键的表通过INSERT OR REPLACE覆盖更新(write.go),而无主键表会持续追加,可能出现重复行。 - 迁移模式选择:主键或
NOT NULL列的新增/删除无法自动迁移,此时cloudquery sync会报错提示改用migrate_mode: forced(相关逻辑见 migrate.go 的canAutoMigrate)。 - 快速上手:完整的最小配置示例见 configuration.md(
connection_string: ./db.sql),同步完成后即可用sqlite ./db.sql本地探索数据;插件整体能力概览可参考 overview.md。
八、小结
CloudQuery 的 SQLite 目标插件在「Arrow 的丰富类型体系」与「SQLite 的 5 种存储类」之间建立了一套清晰且保守的映射:能用原生存储类表达的(整数→integer、浮点→real、布尔→boolean、时间戳→timestamp、二进制→blob)尽量原生表达,其余复杂类型统一序列化为text兜底。这套规则在 types.md 中有权威定义,在 client/types.go、client/migrate.go、typeconv/values.go、client/read.go 等源码中得到了完整闭环的印证。理解这张映射表,是正确使用、查询与运维 CloudQuery SQLite 落地数据的前提。
- 数据集成
- 数据工程
- 数据分析
【免费下载链接】cloudquery
Data pipelines for cloud config and security data. Build cloud asset inventory, CSPM, FinOps, and vulnerability management solutions. Extract from AWS, Azure, GCP, and 70+ cloud and SaaS sources.
相关推荐
CloudQuery DuckDB 目标插件类型映射指南:Apache Arrow 与 DuckDB 数据类型的完整对应关系
CloudQuery DuckDB 目标插件类型映射指南:Apache Arrow 与 DuckDB 数据类型的完整对应关系 本指南聚焦 CloudQuery
数据集成数据工程数据分析CloudQuery Gremlin 目标插件类型映射指南:Apache Arrow 与 Gremlin/Neptune 数据类型全解析
CloudQuery Gremlin 目标插件类型映射指南:Apache Arrow 与 Gremlin/Neptune 数据类型全解析 Gremlin 目标插
数据集成数据工程数据分析CloudQuery MySQL 目标插件类型映射全解析:Apache Arrow 类型到 MySQL 数据类型的对照与源码实现
CloudQuery MySQL 目标插件类型映射全解析:Apache Arrow 类型到 MySQL 数据类型的对照与源码实现 导读 CloudQuery 的
数据集成数据工程数据分析
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考