ITADN
README.md
以下内容由 AI 翻译,如有问题请点此提交 issue 反馈

TinySQL

CI Go Reference DOI

TinySQL 是一个用 Go 编写的可嵌入 SQL 数据库引擎。它专为 学习数据库内部机制、本地工具、测试、浏览器/WASM 应用程序以及 需要强大 SQL 层但无需运行数据库服务器的单进程服务而设计。

试一试: 浏览器游乐场 · 交互式地图演示 · 视频

TinySQL 并非 PostgreSQL、MySQL 或集群 生产数据库的直接替代品。在将其用于 关键工作负载之前,请查阅局限性

目录

快速入门

需要 Go 1.26.5+

go get github.com/SimonWaldherr/tinySQL@latest

创建一个内存数据库,执行 SQL,并读取行:

package main

import (
	"context"
	"fmt"

	tinysql "github.com/SimonWaldherr/tinySQL"
)

func main() {
	ctx := context.Background()
	db := tinysql.NewDB()

	for _, query := range []string{
		`CREATE TABLE users (id INT PRIMARY KEY, name TEXT)`,
		`INSERT INTO users VALUES (1, 'Ada'), (2, 'Grace')`,
	} {
		stmt, err := tinysql.ParseSQL(query)
		if err != nil {
			panic(err)
		}
		if _, err := tinysql.Execute(ctx, db, "default", stmt); err != nil {
			panic(err)
		}
	}

	stmt, err := tinysql.ParseSQL(`SELECT id, name FROM users ORDER BY id`)
	if err != nil {
		panic(err)
	}
	result, err := tinysql.Execute(ctx, db, "default", stmt)
	if err != nil {
		panic(err)
	}
	for _, row := range result.Rows {
		id, _ := tinysql.GetVal(row, "id")
		name, _ := tinysql.GetVal(row, "name")
		fmt.Println(id, name)
	}
}

对于已经使用 database/sql 的应用,请使用 github.com/SimonWaldherr/tinySQL/driver

它能做什么

SQL 引擎

  • SELECTINSERTUPDATEDELETERETURNING、CTE、子查询、 连接、分组、窗口函数、PIVOTEXPLAIN,以及常见的 兼容 SQLite 的 PRAGMA
  • 视图、物化视图、触发器、表值函数、存储 过程、作业、多租户以及系统目录视图。
  • 约束,包括单列主键、唯一键、外键、 NOT NULL 以及字面量默认值。
  • 二级索引、通过 ANALYZE 实现的精确持久化统计信息,以及 规划器选择性估计。
  • JSON、YAML、文本、正则表达式、数学、日期、URL、哈希、位图、全文、向量、 混合搜索以及 RAG 辅助函数。

嵌入与交付

  • 纯 Go、进程内 API 以及 database/sql 驱动程序。
  • 内存、WAL、磁盘、JSON、索引、混合以及分页索引存储模式。
  • 浏览器/WASM 构建以及本地优先的 SQL 游乐场。
  • CLI、HTTP 服务器、文件查询工具以及健康/生命周期钩子。
  • 可选的审计日志、RBAC 以及针对支持的表文件 后端的静态加密。

数据与地图

  • CSV、TSV、JSON/NDJSON、XML、YAML、Excel、GeoJSON、TopoJSON、KML、OSM XML、 路由图、Shapefiles 和 MBTiles 导入路径。
  • GeoJSON 测量、包含、关系、编辑、清理、区域 (溶解/裁剪)和检查功能,以及空间搜索索引和 用于基于位置的 BI 仪表板的分级统计图分类(等间隔、自然断点、分位数)。
  • 从查询结果导出 GeoJSON 和 TopoJSON(-mode geojson|topojson), 这是 Power BI 形状地图和大多数映射工具首选的格式。
  • Web Mercator 瓦片寻址、MBTiles 导入/导出、就地瓦片访问, 以及可选的 XYZ 瓦片端点。

参见 FUNCTIONS.sqlexample_showcase.sql 以获取更广泛的 SQL 参考。

从 Go 中使用 SQL

ParseSQL + Execute 是基本 API。在编程方式组合 查询时,请使用流畅构建器:

query := tinysql.Select(tinysql.Col("name")).
	From("users").
	Where(tinysql.Eq(tinysql.Col("active"), tinysql.Val(true))).
	OrderBy("name").
	Build()

result, err := tinysql.Execute(ctx, db, "default", query)

The builder 支持投影、连接、CTE、排序、限制、表达式, 以及 Exists/NotExists 谓词。参见 ExampleExists 获取可运行的示例。

事务与触发器

Go 驱动程序支持跨语句事务,使用 BEGINCOMMIT、 和 ROLLBACK(或 BeginTx)。事务可以看到其自身的写入;并发 写入冲突将作为可重试的 ErrTransactionConflict 返回。

行触发器在 BEFORE/AFTER INSERTUPDATEDELETE 时运行, 包括没有 WHERE 子句的 DELETE。直接的多行变更及其 触发器效果是语句原子的。

import (
	"context"

	tsqldriver "github.com/SimonWaldherr/tinySQL/driver"
)

db, err := tsqldriver.OpenInMemory("default")
if err != nil { panic(err) }
defer db.Close()

tx, err := db.BeginTx(context.Background(), nil)
if err != nil { panic(err) }
if _, err := tx.Exec(`INSERT INTO users VALUES (3, 'Lin')`); err != nil {
	_ = tx.Rollback()
	panic(err)
}
if err := tx.Commit(); err != nil { panic(err) }

SAVEPOINT 和嵌套事务尚未实现。

GIS 和 GeoJSON

TinySQL 将几何数据作为普通的 GeoJSON 存储,既可以存储在 TEXT/JSON 列中, 也可以存储在专用的 GEOMETRY 列类型中,该类型在写入时进行验证(裸 数字或 Feature/FeatureCollection 将被拒绝 —— GEOMETRY 列 存储的是 Geometry),并将其规范化为稳定的、字节级一致文本。大多数 几何函数也返回 GeoJSON,因此结果可以直接传递给 后续的 SQL 调用或在地图客户端中绘制。

CREATE TABLE places (name TEXT, geometry GEOMETRY);
INSERT INTO places VALUES
  ('Berlin', GEO_POINT(13.4050, 52.5200)),
  ('Munich', GEO_POINT(11.5755, 48.1372));

SELECT GEO_DISTANCE(a.geometry, b.geometry) AS meters,
       GEO_BEARING(a.geometry, b.geometry) AS bearing,
       GEO_MIDPOINT(a.geometry, b.geometry) AS midpoint
FROM places a JOIN places b
  ON a.name = 'Berlin' AND b.name = 'Munich';

GEOMETRY/GEOM 是附加性的——现有的 TEXT/JSON 几何列以及 上述查询保持不变,继续有效。不支持 GEOMETRY(SRID) 风格的参数; 请使用裸关键字。CAST(x AS GEOMETRY) 的验证和规范化方式与列写入相同。

测量与谓词

任务函数PostGIS 风格别名
创建/读取点GEO_POINT, GEO_LON, GEO_LATST_POINT, ST_X, ST_Y
大圆距离GEO_DISTANCEST_DISTANCE, HAVERSINE
半径检查GEO_DWITHINST_DWITHIN
边界框检查GEO_WITHIN_BBOXST_WITHIN_BBOX
方位角 / 中点 / 目的地GEO_BEARING, GEO_MIDPOINT, GEO_DESTINATIONST_AZIMUTH, ST_MIDPOINT, ST_PROJECT
点在多边形/多重多边形内GEO_WITHIN_POLYGONST_WITHIN, ST_CONTAINS
多边形面积 / 线长度GEO_POLYGON_AREA, GEO_LENGTHST_AREA, ST_LENGTH
任意共享点(点/线/多边形,任意组合)GEO_INTERSECTSST_INTERSECTS
无共享点GEO_DISJOINTST_DISJOINT
相同坐标(与顺序/旋转/绕向无关)GEO_EQUALSST_EQUALS
点周围的圆形缓冲区GEO_BUFFER(point, meters[, segments])ST_BUFFER
几何体顶点的凸包GEO_CONVEX_HULLST_CONVEXHULL
作为多边形的边界框GEO_ENVELOPEST_ENVELOPE
线上某比例处的点GEO_LINE_INTERPOLATE(line, fraction)ST_LINE_INTERPOLATE_POINT
将几何体裁剪到凸边界GEO_CLIP(geometry, boundary[, allow_nonconvex])ST_CLIP

距离、方位角、目的地、长度、面积和缓冲区计算在球面上进行; 距离以米为单位,多边形面积以平方米为单位。 原始四元数形式的坐标使用 (lat, lon, lat, lon);GeoJSON 保持为 [lon, lat]GEO_WITHIN_POLYGON/ST_CONTAINS/GEO_POLYGON_AREA 接受 GeoJSON MultiPolygon 以及 Polygon —— 属于任何 部分即视为属于整体,且面积累加所有部分。 GEO_LINE_INTERPOLATE 按沿线的实际距离进行分割,而非按 顶点数量。GEO_CONVEX_HULL 在普通的经度/纬度空间中计算凸包 (一种标准的平面近似,而非严格的球面凸包)。

GEO_INTERSECTS/GEO_DISJOINT 涵盖点/线/多边形的任意组合, 并尊重多边形孔洞(嵌套在另一个多边形孔洞内的形状 与其不相交)。没有 ST_TOUCHES/ST_CROSSES/ST_OVERLAPS: 严格区分仅边界接触与内部重叠需要完整的 DE-9IM 计算,这超出了范围——一种天真的尝试会在 实际 GIS 数据中频繁遇到的共享边/孔洞情况下 静默地出错。GEO_EQUALS 的范围限于坐标/形状相等性(在任意旋转、反转或 Polygon-与-单部分-MultiPolygon 包裹之后匹配),而非完整的 OGC 点集相等性——两个覆盖相同 区域但顶点化不同的多边形不会被检测为相等。

GEO_CLIP 使用 Sutherland-Hodgman 多边形裁剪,该算法仅保证在凸边界下正确;默认情况下它会验证凸性,否则报错,allow_nonconvex=true 作为显式的尽力而为(best-effort)退出选项。GEO_CLIP 支持 Point/MultiPoint 和 Polygon/MultiPolygon 主体;LineString 裁剪需要不同的算法,目前不支持。

几何编辑与质量

任务函数PostGIS 风格别名
简化几何体GEO_SIMPLIFY(geometry, tolerance[, method])ST_SIMPLIFY
检查 bbox / 质心GEO_BBOX, GEO_CENTROIDST_BBOX, ST_CENTROID
平移、缩放、旋转GEO_AFFINEST_AFFINE
Chaikin 平滑GEO_SMOOTHST_SMOOTH
移除多边形孔洞GEO_DROP_HOLESST_REMOVE_HOLES
清理重复顶点并闭合环GEO_CLEANST_CLEAN
将 x/y 吸附到网格GEO_SNAP(geometry, gridSize)ST_SNAPTOGRID
结构性 GeoJSON 检查GEO_IS_VALIDST_ISVALID

GEO_SIMPLIFY 接受 Douglas-Peucker(dp,默认值)、 visvalingam-effectivevisvalingam-weighted。简化、仿射变换、 平滑、清理和吸附操作均在源坐标单位中进行。 GEO_SNAPGEO_CLEAN 会拒绝将线或多边形环 折叠至低于 GeoJSON 最小顶点数的结果。

GEO_IS_VALID 检查受支持的 GeoJSON 结构和顶点要求。它 目前尚不能检测自相交等拓扑问题。没有 R-tree,普通的 WHERE GEO_DWITHIN(...)/GEO_WITHIN_BBOX(...) 谓词不会获得规划器加速——它在普通索引 缩小范围之后过滤行。对于 BI 规模的点表,GEO_SEARCH(下文)提供了 显式的、基于索引的替代方案。

交互式地图演示 可以本地编辑内置或上传的 GeoJSON,显示源数据和结果,调整 参数,检查有效性,并下载结果。

区域操作、空间搜索和分级统计图分类

受 Mapshaper 启发的区域编辑动词和面向 BI 的辅助函数,用于将 原始几何数据转换为基于位置的 KPI 和仪表板:

任务函数
将一组多边形合并为一个(共享边溶解)GEO_DISSOLVE(geometry[, snap_grid_degrees])
相同操作,聚合式命名GEO_UNION_AGG, ST_UNION
跨越一组的边界框GEO_BBOX_AGG(geometry)
(可选加权的)跨越一组的质心GEO_CENTROID_AGG(geometry[, weight])
对表格进行索引化的 bbox/半径搜索GEO_SEARCH(table, geom_col, 'bbox'|'radius', ...)
等间隔分级统计图分类EQUAL_INTERVAL(n) OVER (ORDER BY kpi)
自然断点(Jenks)分级统计图分类NATURAL_BREAKS(n) OVER (ORDER BY kpi)
分位数分级统计图分类NTILE(n) OVER (ORDER BY kpi)(已存在)
-- Dissolve adjacent building footprints into one district boundary per region,
-- then bucket a KPI (e.g. building count) into 5 choropleth classes.
SELECT region, GEO_DISSOLVE(footprint) AS boundary, COUNT(*) AS buildings
FROM parcels
GROUP BY region;

SELECT region, buildings,
       NATURAL_BREAKS(5) OVER (ORDER BY buildings) AS class
FROM region_stats;

GEO_DISSOLVE/GEO_UNION_AGG/ST_UNION 通过抵消共享的有向边来合并多边形——适用于拓扑干净、顶点对齐的相邻输入(真实的 GIS 边界数据,或本项目自身的 dissolve 输出回传),而非针对重叠但未对齐输入的通用多边形布尔并集。组内的点/线会被拼接为 MultiPoint/ MultiLineString,而不是被溶解。GEO_CENTROID_AGG 的可选权重 与 GEO_CENTROID 自身的面积/长度权重相结合,因此 GEO_CENTROID_AGG(geom, population) 是已经过面积加权的逐行质心的 人口加权质心。

GEO_SEARCH 构建了一个惰性的、按表划分的网格索引(在写入时自动失效),对于 Point 列是精确的;对于多边形/线列,它按质心进行索引,因此如果一个大型形状的边缘——而非其质心——切入查询窗口,则在那里会产生假阴性。ST_INTERSECTS(上文) 仍然是直接测试形状重叠的精确、无索引方式。

分位数分类(NTILE)已经存在;EQUAL_INTERVALNATURAL_BREAKS 是新增的。NATURAL_BREAKS 的 Jenks 优化在每个分区上是 O(rows × classes²)(计算一次,而非每行计算)——对于现实中的分级统计图分区大小(市镇、邮政编码、区)来说是可以接受的, 但未针对数万行进行调优。

Map tiles and MBTiles

对于多吉字节、以读为主的数据集,请参阅 docs/mbtiles-artifacts.md 以了解有界的 dataset.tinysql 导入器、经过验证的工件格式、TMS 读取 API 以及 SQLite 比较流程。

地图演示在 WebAssembly 中通过实时 SQL 获取每个瓦片。其源代码位于 cmd/mbtilesdemo

Web 地图使用顶部原点 XYZ 行,而 MBTiles 存储底部原点 TMS 行。 在 SQL 边界处使用 TILE_FLIP_Y

SELECT tile_data FROM tiles
WHERE zoom_level = 14
  AND tile_column = TILE_X(13.405, 14)
  AND tile_row = TILE_FLIP_Y(TILE_Y(52.520, 14), 14);

其他瓦片辅助函数包括 TILE_ZXYTILE_BBOXTILE_LONTILE_LATTILE_QUADKEYTILE_FROM_QUADKEYTILE_PARENTTILE_CONTAINSTILE_COUNT。在提供常规瓦片表之前,添加一个索引:

CREATE INDEX tile_index ON tiles (zoom_level, tile_column, tile_row);

MBTiles 导入/导出使用可选的 sqliteimport 构建标签:

importer.ImportMBTiles(ctx, db, "default", "tiles", "city.mbtiles",
	&importer.ImportOptions{CreateTable: true, BatchSize: 1000})

importer.ExportMBTiles(ctx, db, "default", "out.mbtiles",
	&importer.ExportMBTilesOptions{TileRowIsTMS: true})

对于 HTTP 交付,运行 tinysqld -tiles

GET /tiles/{tileset}/{z}/{x}/{y}.{ext}
GET /tiles/{tileset}.json
GET /tiles/{tileset}/metadata

瓦片路由有意不进行身份验证,因为普通的地图客户端 无法在瓦片请求中附加 bearer token。如果需要访问限制,请在前面 放置一个进行身份验证的代理。请参阅 存储指南 以了解大型、分页索引的瓦片集。

导入、导出和可选构建标签

核心引擎没有 SQLite 或 Shapefile 运行时依赖。仅在需要它们的 构建中启用这些读写路径:

# SQLite files and MBTiles via pure-Go modernc SQLite (also required for
# ModeSQLite — a real .sqlite file as tinySQL's native storage; see the
# storage guide)
go build -tags=sqliteimport ./...

# ESRI Shapefile and Shapefile ZIP imports
go build -tags=shapefile ./...

# Both profiles
go build -tags=sqliteimport,shapefile ./...

没有标签时,相应的导入 API 仍然可用,并返回功能禁用的错误。对于已加载到 TinySQL 中的瓦片集,提供其服务时无需标签。

exporter 包将结果集写入为 CSV、TSV、JSON、NDJSON、XML、GOB、SQL、Excel (XLSX)、GeoJSON 或 TopoJSON。它使用自识别编码保留二进制值,并且可以输出包含模式、行数以及带类型行 SHA-256 指纹的表清单。参见 ExampleExportJSON

ExportGeoJSON/ExportTopoJSON 将查询结果的几何列(显式命名,或在恰好存在一个候选列时自动检测)以及所有其他选定的列转换为 GeoJSON FeatureCollection 或 TopoJSON Topology —— 即 Power BI Shape Maps、D3 和大多数映射工具所期望的格式,可直接将计算出的 KPI 放置到地图上。tinysql CLI 将两者暴露为 -mode geojson/-mode topojson(使用 -geom-col 可 覆盖自动检测)。TopoJSON 导出会将整个共享边界环去重为一个弧(由一个要素正向引用,由其相邻要素反向引用)—— 由于无论如何每个环都会为弧表进行哈希,因此成本较低, 并且正是 GEO_DISSOLVE 生成的或拓扑上干净的数据集所受益之处 —— 但不会拆分仅部分重合的边界(像 mapshaper 这样的真实拓扑构建工具会这样做; 这是文档中记录的 v1 范围裁剪,而非 bug)。ImportTopoJSON(或在 .topojson 文件上使用 ImportFile/ .import)将弧引用解析 —— 包括 反向(~i)约定以及由其他工具构建的拓扑中每个环的多弧拼接 —— 还原为普通的几何行。

存储、事务和运维

模式最佳适用场景
ModeMemory测试、浏览器/WASM 以及临时本地数据
ModeWAL带有预写日志恢复的内存表
ModeDisk每表一个 GOB 文件,支持延迟加载
ModeJSON人类可读、支持差异比较的每表文件
ModeIndex / ModeHybrid带有有界缓存的磁盘后端表
ModePagedIndex大型等值查找工作负载,例如 MBTiles
ModeSQLite真实的 .sqlite 文件,可被任何 SQLite 工具读取(需要 sqliteimport 构建标签)

使用 OpenDB 配合 StorageConfig 进行持久化存储。针对嵌入式服务,提供了健康检查、 只读操作、审计日志以及生命周期辅助功能。静态加密支持受支持的磁盘 后端中的表文件;有关确切范围,请参阅 存储指南

对于高重复率的向量搜索,ConfigureVectorCache 可以启用有界的、 进程本地的结果缓存以及匿名的形状/时序分析。在结合向量指标和重排序之前, 请参阅 RAG 指南

指南与开发

指南适用场景
开发者集成Go、database/sql 及浏览器嵌入
CLI 指南REPL、服务器及文件查询工具
存储指南后端、DSN、只读模式及大型瓦片集
RAG 指南向量、混合检索、重排序及上下文
TinyGo 指南TinyGo、嵌入式目标及 WASM
架构解析器、执行器、存储及不变量
开发指南测试、Make 目标及发布演示
基准测试可复现的性能测量

运行完整测试套件:

go test ./...

构建浏览器游乐场:

cd cmd/query_files_wasm
./build.sh --build-only

局限性

  • 仅支持单进程:没有内置的复制、集群、分片、 分布式事务或故障转移。
  • 不支持复合主键/外键、CHECKUPSERT/ON CONFLICTSAVEPOINTATTACH/DETACHVACUUM、部分索引、生成 列或持久化 ANN 向量索引文件。
  • 二级索引优化等值/前缀查找和数值范围;文本 和 BLOB 范围谓词仍需扫描。没有 R 树,且普通的 WHERE GEO_DWITHIN(...) 风格谓词未获得规划器加速; 显式的 GEO_SEARCH 表函数是大规模点列 bbox/半径查询的索引替代方案。
  • ModeIndexModeHybrid 在缓存未命中时仍使用全表遗留编解码器; 对于严格的大瓦片服务需求,请使用 SQLite 或 ModePagedIndex
  • RBAC 较为粗糙且面向单表。加密尚未覆盖 基于 WAL 的模式或元数据文件。
  • GIS 有效性是结构性的,而非完整的拓扑验证(没有 自相交检测)。GEO_DISSOLVE/GEO_UNION_AGG 仅处理 拓扑干净、顶点对齐的相邻多边形,而非通用的 多边形布尔并集;不支持 CRS/投影转换,且 ST_TOUCHES/ST_CROSSES/ST_OVERLAPS(需要完整的 DE-9IM 计算)尚未实现。

TinySQL 主要是一个用于教育和可嵌入的 SQL 引擎。它旨在使 解析器、规划器、执行器、存储后端和实用扩展易于检查、测试和适配。