pure-js-db 2.54.0 → 2.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -4
- package/native/index.node +0 -0
- package/package.json +3 -1
- package/src/executor/executor.js +159 -0
- package/src/executor/join-executor.js +42 -1
- package/src/executor/select-executor.js +6 -0
- package/src/executor/vectorized-columnar-engine.js +30 -1
- package/src/native/native-acceleration.js +4 -2
- package/src/sdk/java/pom.xml +1 -1
- package/src/sdk/java/pureclient/PureClient.java +1 -1
- package/src/sdk/java/pureclient/PureClientTest.java +1 -1
- package/src/sdk/php/composer.json +1 -1
- package/src/sdk/php/src/PureClient.php +1 -1
- package/src/sdk/python/pure_client.py +1 -1
- package/src/sdk/rust/Cargo.toml +1 -1
- package/src/sdk/rust/RELEASE-CHECKLIST.md +2 -2
- package/src/sdk/rust/src/lib.rs +1 -1
- package/src/sdk/wechat/stubs/empty.js +14 -0
- package/src/utils/join-engine.js +25 -6
- package/src/version.js +1 -1
- package/bindings/java/src/test/java/TestClient.java +0 -68
- package/bindings/python/test_client.py +0 -63
package/README.md
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
> **零依赖嵌入式数据库引擎 —— 单文件部署,同时支持向量搜索、MVCC 并发写入、CDC 变更捕获。**
|
|
4
4
|
> 如果你的 Node.js 应用需要 PostgreSQL + Milvus + Debezium 的能力但不想要三个独立服务,这里一个文件就够。
|
|
5
5
|
|
|
6
|
-
> 📌 **当前状态**:v2.
|
|
6
|
+
> 📌 **当前状态**:v2.55.0,成熟度 **Beta+(工程验证成熟)**。历史版本公告(E26 查询缓存/时间旅行、F1-F10 接线、安全审计加固等)详见 [CHANGELOG.md](CHANGELOG.md) 与 [docs/internal/audit/](docs/internal/audit/) 审计档案。安全模块默认 fail-closed,生产部署必须显式配置认证/RBAC/加密(见 [SECURITY.md](SECURITY.md) §2.2)。
|
|
7
7
|
|
|
8
8
|

|
|
9
9
|

|
|
10
|
-

|
|
11
11
|

|
|
12
12
|

|
|
13
13
|

|
|
@@ -48,6 +48,34 @@ console.log(result.rows);
|
|
|
48
48
|
|
|
49
49
|
---
|
|
50
50
|
|
|
51
|
+
## 🤖 给 AI Agent 一个本地记忆库(3 步)
|
|
52
|
+
|
|
53
|
+
零依赖纯 JS + 内置 MCP Server + 数据 100% 留在本地磁盘——这是 AI Agent 本地存储的最短路径(无需编译链、无需云服务、无需 Docker)。
|
|
54
|
+
|
|
55
|
+
**① 安装**:`npm i pure-js-db`
|
|
56
|
+
|
|
57
|
+
**② 配置 Claude Desktop / Cursor**(MCP stdio 直连):
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"mcpServers": {
|
|
62
|
+
"pure-js-db": {
|
|
63
|
+
"command": "node",
|
|
64
|
+
"args": [
|
|
65
|
+
"<项目路径>/node_modules/pure-js-db/scripts/mcp-server.mjs",
|
|
66
|
+
"--db-path", "./mcpdata", "--allow-write"
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**③ 对 AI 说**:"建一张 memories 表,把我的偏好存进去,下次会话能查到。" —— 完成。数据落盘 `./mcpdata`,跨会话持久,26 个 MCP 工具(SQL/事务/向量检索/图查询/备份/CDC)全部可用。
|
|
74
|
+
|
|
75
|
+
📘 完整教程(含探针脚本与 FAQ):[《用 Pure-JS-DB 给 Claude 一个本地记忆库》](docs/tutorials/ai-agent-local-memory.md) · MCP 详情见下文 [MCP Server 章节](#mcp-server600-行)。
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
51
79
|
## 按场景快速入门
|
|
52
80
|
|
|
53
81
|
### 场景 1:Web 服务需要并发写入
|
|
@@ -173,6 +201,27 @@ await db.save();
|
|
|
173
201
|
|
|
174
202
|
参见 [src/storage/persisted-db.js](src/storage/persisted-db.js) — `STABLE`
|
|
175
203
|
|
|
204
|
+
### 场景 5b:持久化到磁盘(文件系统 + 跨进程重启恢复)
|
|
205
|
+
|
|
206
|
+
> 用 `FileKVAdapter` 把数据真正落到磁盘目录,进程重启 / 换新实例后 `load()` 完整恢复数据 + 模式 + 索引。
|
|
207
|
+
|
|
208
|
+
```javascript
|
|
209
|
+
import { PersistedDatabase, FileKVAdapter } from 'pure-js-db';
|
|
210
|
+
|
|
211
|
+
// 建库,数据写到 ./mydb 目录(数据页 / 索引 / 元数据 / manifest)
|
|
212
|
+
const db = new PersistedDatabase('app', new FileKVAdapter('./mydb'));
|
|
213
|
+
await db.load();
|
|
214
|
+
// ... 写入 / 建索引 ...
|
|
215
|
+
await db.save();
|
|
216
|
+
await db.close();
|
|
217
|
+
|
|
218
|
+
// 模拟进程重启:指向同一目录即可完整恢复
|
|
219
|
+
const db2 = new PersistedDatabase('app', new FileKVAdapter('./mydb'));
|
|
220
|
+
await db2.load();
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
可复现演示(建库 → 写 → 关 → 新实例 load 恢复):`node scripts/demo-persist-roundtrip.mjs`(`npm run demo:persist`)
|
|
224
|
+
|
|
176
225
|
### 场景 6:用 ActiveRecord 风格 ORM 操作数据库(v1.29.0+)
|
|
177
226
|
|
|
178
227
|
> ✅ **v2.50.0 已接线**:ORM 模块已从主入口导出 `defineModel` / `Schema` / `QueryBuilder` / `transaction` 等 API(F5 接线 2026-08-17),以下示例可直接运行。npm 子路径导入请使用 `pure-js-db/src/orm/index.js`。
|
|
@@ -661,7 +710,7 @@ Pure-JS-DB 提供 **双引擎模式**,根据运行环境自动选择最优执
|
|
|
661
710
|
| 引擎 | 适用环境 | 性能特点 | 加速方式 |
|
|
662
711
|
|------|---------|---------|---------|
|
|
663
712
|
| **纯 JS 引擎** | 浏览器/小程序/边缘函数 | 零依赖,v2.27.0 优化后 DELETE 0.56ms/1K行 | 批量触发器合并 + WHERE 条件下推 |
|
|
664
|
-
| **Rust NAPI 加速** | Node.js + Rust 模块 | SIMD
|
|
713
|
+
| **Rust NAPI 加速** | Node.js + Rust 模块 | SIMD 加速。**2026-11-06 更新:2026-10-27 评测指出的全部未接线项已闭环**——TopN(性能门禁 12.2x,1M 行 top-10 完整路径 16x)、向量化过滤(列存 BitmapFilter 接线,低选择率 2.1x)、F32 批量聚合(sum/min/max Rust 实现补齐 + countGtI32 绑定名失配修复)、单组聚合(sumF64/minF64/maxF64)。旧基准"最高 45x"口径已废弃,以门禁与实测为准(`node scripts/perf-baseline-gate.cjs` 可复现) | AVX2 指令集 |
|
|
665
714
|
|
|
666
715
|
### 自动引擎选择
|
|
667
716
|
|
|
@@ -699,7 +748,7 @@ const backend = getAccelerationBackend();
|
|
|
699
748
|
|
|
700
749
|
### Rust NAPI 加速引擎性能 (SIMD)
|
|
701
750
|
|
|
702
|
-
> ⚠️ **2026-10-27 独立评测修正(2026-10-28 部分已修复)**:下表数据来自 v1.32 时代的旧基准。独立评测实测发现:Rust 加速层多数路径未在生产代码接线(4/5 性能声称失效);评测同时暴露三处 AVX2 正确性 bug(utf8 批量编码、Float64 min/max 全 NaN、null bitmap ≥0x80),该三处已在 2026-10-28 修复并重建 `native/index.node`、相关 Native 路径已恢复。下表加速比仅供参考,不构成当前版本(v2.
|
|
751
|
+
> ⚠️ **2026-10-27 独立评测修正(2026-10-28 部分已修复)**:下表数据来自 v1.32 时代的旧基准。独立评测实测发现:Rust 加速层多数路径未在生产代码接线(4/5 性能声称失效);评测同时暴露三处 AVX2 正确性 bug(utf8 批量编码、Float64 min/max 全 NaN、null bitmap ≥0x80),该三处已在 2026-10-28 修复并重建 `native/index.node`、相关 Native 路径已恢复。下表加速比仅供参考,不构成当前版本(v2.55.0)的性能承诺。
|
|
703
752
|
>
|
|
704
753
|
> 详见 [SIMD 优化报告](docs/simd/simd-final-optimization-report.md)
|
|
705
754
|
|
package/native/index.node
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pure-js-db",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.55.0",
|
|
4
4
|
"description": "Pure JavaScript 关系型数据库引擎 - 面向小程序/受限运行时环境。零 C/C++/WASM 依赖,支持 SQL 解析、B+树索引、查询优化、物化视图、数据分片、分布式事务、Worker Pool 并行、SharedArrayBuffer 共享内存、HTAP 混合存储、多语言客户端 SDK。可选 Rust NAPI 原生加速层(编译后性能提升 2-5x)。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"./internal": {
|
|
16
16
|
"default": "./src/internal/public-api.js"
|
|
17
17
|
},
|
|
18
|
+
"./version": "./src/version.js",
|
|
18
19
|
"./v3": "./src/index-v3.js"
|
|
19
20
|
},
|
|
20
21
|
"sideEffects": false,
|
|
@@ -53,6 +54,7 @@
|
|
|
53
54
|
"start:prod": "node --max-old-space-size=4096 --max-semi-space-size=64 --expose-gc scripts/start-server.js",
|
|
54
55
|
"cli": "node --max-old-space-size=4096 --max-semi-space-size=64 src/cli.js",
|
|
55
56
|
"demo": "node src/cli.js --init",
|
|
57
|
+
"demo:persist": "node scripts/demo-persist-roundtrip.mjs",
|
|
56
58
|
"load-test": "node test/benchmark/load-test.mjs --init --warmup",
|
|
57
59
|
"bench:storage": "node test/bench_binary_storage.js",
|
|
58
60
|
"bench:random-read": "node test/bench-random-read-compare.js",
|
package/src/executor/executor.js
CHANGED
|
@@ -507,6 +507,8 @@ export class Executor {
|
|
|
507
507
|
/** @type {string[]} v2.12 E16 M2-T2: 当前事务已创建的 SAVEPOINT 名栈(新库 join 时补录) */
|
|
508
508
|
this._txSavepoints = [];
|
|
509
509
|
this.optimizer = null; // 延迟初始化成本优化器
|
|
510
|
+
/** @type {Object|null} TPC-H 慢查询专项:JOIN 哈希表跨查询缓存(懒创建,见 _getJoinHashTableCache) */
|
|
511
|
+
this._joinHashTableCache = null;
|
|
510
512
|
/** @type {ViewManager|null} */
|
|
511
513
|
this.viewManager = new ViewManager(this.db, this);
|
|
512
514
|
/** @type {LineageStore} v2.47.0 P7-M1: 数据血缘存储(表级血缘自动记录) */
|
|
@@ -2659,6 +2661,25 @@ export class Executor {
|
|
|
2659
2661
|
return this.execSelectParallel(stmt, options);
|
|
2660
2662
|
}
|
|
2661
2663
|
|
|
2664
|
+
// TPC-H 慢查询专项:JOIN 复合行列裁剪——仅顶层 SELECT 分发注入
|
|
2665
|
+
// _requiredColumns;INSERT...SELECT / 子查询等内部 execSelect 调用不携带
|
|
2666
|
+
// 该标记 → 自动维持全列行为(安全边界见 _computeRequiredColumns 注释)。
|
|
2667
|
+
if (!options._requiredColumns) {
|
|
2668
|
+
const requiredColumns = this._computeRequiredColumns(stmt);
|
|
2669
|
+
if (requiredColumns) {
|
|
2670
|
+
options = { ...options, _requiredColumns: requiredColumns };
|
|
2671
|
+
// 无限定列引用 ⇒ __table__ 命名空间零消费 → 跳过赋值(复合行再降 ~40% 宽度)
|
|
2672
|
+
if (!requiredColumns.hasQualified) {
|
|
2673
|
+
options._skipTableNamespaces = true;
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
// TPC-H 慢查询专项:JOIN 哈希表跨查询缓存——v1.43.0 的 _hashTableCache
|
|
2678
|
+
// 机制一直无生产者,同一查询重复执行每次重建右表哈希表(SF=1 lineitem
|
|
2679
|
+
// 60K 行 ≈ 5-10ms/次)。失效以表对象 + Table._dataVersion(DML 必递增)为键。
|
|
2680
|
+
if (!options._hashTableCache && typeof this._getJoinHashTableCache === 'function') {
|
|
2681
|
+
options._hashTableCache = this._getJoinHashTableCache();
|
|
2682
|
+
}
|
|
2662
2683
|
result = this.execSelect(stmt, options);
|
|
2663
2684
|
} else if (stmt instanceof InsertStatement) {
|
|
2664
2685
|
result = this.execInsert(stmt, options);
|
|
@@ -3023,6 +3044,144 @@ export class Executor {
|
|
|
3023
3044
|
* @param {Object} [db] - 数据库实例(用于查目标表触发器)
|
|
3024
3045
|
* @returns {boolean}
|
|
3025
3046
|
*/
|
|
3047
|
+
/**
|
|
3048
|
+
* JOIN 复合行列裁剪(TPC-H 慢查询专项,2026-11-06)
|
|
3049
|
+
*
|
|
3050
|
+
* 计算整个 SELECT 语句引用的列名集合(全 AST 递归:SELECT/WHERE/GROUP BY/
|
|
3051
|
+
* HAVING/ORDER BY/JOIN ON/子查询/窗口),供 JoinEngine._hashJoin 在构建
|
|
3052
|
+
* 复合行时按需拷贝右表新键——复合行宽度从"全部表列"降到"查询实际引用列",
|
|
3053
|
+
* 展开成本随行宽线性下降(Q5 实测 JOIN 步耗时 -60%+)。
|
|
3054
|
+
*
|
|
3055
|
+
* 保守禁用(返回 null → 不裁剪,维持全列拷贝):
|
|
3056
|
+
* - 投影含 Star(SELECT * / 任何层 SELECT 的 columns 含 Star,含 COUNT(*) 场景按
|
|
3057
|
+
* 投影 Star 判定)——消费方可能按位置/全列使用;
|
|
3058
|
+
* - 集合操作(UNION/EXCEPT/INTERSECT,setOps)——列按位置对应,裁剪有错位风险;
|
|
3059
|
+
* - 无任何列引用(SELECT 1 等)——无收益。
|
|
3060
|
+
*
|
|
3061
|
+
* 适用边界:仅 execute() 的顶层 SELECT 分发注入 _requiredColumns;INSERT...SELECT、
|
|
3062
|
+
* 标量/EXISTS 子查询等内部 execSelect 调用不携带该标记 → 自动维持全列行为。
|
|
3063
|
+
*
|
|
3064
|
+
* @param {Object} stmt - SELECT 语句 AST
|
|
3065
|
+
* @returns {Set<string>|null} 需要的列名集合;null = 不裁剪
|
|
3066
|
+
*/
|
|
3067
|
+
_computeRequiredColumns(stmt) {
|
|
3068
|
+
const required = new Set();
|
|
3069
|
+
let hasStar = false;
|
|
3070
|
+
let hasSetOp = false;
|
|
3071
|
+
let hasQualified = false;
|
|
3072
|
+
const walk = (node, depth) => {
|
|
3073
|
+
if (!node || typeof node !== 'object' || depth > 64) return;
|
|
3074
|
+
if (Array.isArray(node)) {
|
|
3075
|
+
for (const item of node) walk(item, depth + 1);
|
|
3076
|
+
return;
|
|
3077
|
+
}
|
|
3078
|
+
const cname = node.constructor && node.constructor.name;
|
|
3079
|
+
if (cname === 'ColumnRef') {
|
|
3080
|
+
required.add(node.columnName);
|
|
3081
|
+
if (node.tableName) hasQualified = true;
|
|
3082
|
+
return;
|
|
3083
|
+
}
|
|
3084
|
+
// 集合操作节点(UNION/EXCEPT/INTERSECT 解析为 {left, operation, right})→ 禁裁剪
|
|
3085
|
+
if (typeof node.operation === 'string' && node.left && node.right) {
|
|
3086
|
+
hasSetOp = true;
|
|
3087
|
+
}
|
|
3088
|
+
// 投影 Star 检测:任何层 SELECT 的 columns 数组(元素为 SelectItem{expression} 或 Star);
|
|
3089
|
+
// COUNT(*) 的 args Star 不产生输出列,忽略
|
|
3090
|
+
if (Array.isArray(node.columns)) {
|
|
3091
|
+
for (const c of node.columns) {
|
|
3092
|
+
const expr = c && c.expression ? c.expression : c;
|
|
3093
|
+
if (expr && expr.constructor && expr.constructor.name === 'Star') hasStar = true;
|
|
3094
|
+
}
|
|
3095
|
+
}
|
|
3096
|
+
for (const key of Object.keys(node)) {
|
|
3097
|
+
if (key === 'loc' || key === '_parent' || key === '_optimized' || key === '__rowId') continue;
|
|
3098
|
+
const v = node[key];
|
|
3099
|
+
if (v && typeof v === 'object') walk(v, depth + 1);
|
|
3100
|
+
}
|
|
3101
|
+
};
|
|
3102
|
+
walk(stmt, 0);
|
|
3103
|
+
if (hasStar || hasSetOp || required.size === 0) return null;
|
|
3104
|
+
// hasQualified 挂在返回 Set 上:无限定列时 execute() 额外注入 _skipTableNamespaces
|
|
3105
|
+
// (_hashJoin 跳过 __table__ 命名空间赋值,复合行再降 ~40% 宽度)。
|
|
3106
|
+
// 无限定列 ⇒ __table__ 的全部消费点(限定列解析/_getTableRow/graph-table)均不被触发。
|
|
3107
|
+
required.hasQualified = hasQualified;
|
|
3108
|
+
return required;
|
|
3109
|
+
}
|
|
3110
|
+
|
|
3111
|
+
/**
|
|
3112
|
+
* JOIN 哈希表跨查询缓存(TPC-H 慢查询专项,2026-11-07)
|
|
3113
|
+
*
|
|
3114
|
+
* 背景:JoinEngine._hashJoin/_buildHashTable 的 `_hashTableCache` 机制(v1.43.0)
|
|
3115
|
+
* 一直只有消费者没有生产者——SELECT 链路从未注入,导致同一查询重复执行时每次
|
|
3116
|
+
* 重建右表哈希表(SF=1 下 lineitem 60K 行 build ≈ 5-10ms/次)。
|
|
3117
|
+
*
|
|
3118
|
+
* 失效正确性(本实现与旧"行数版"的关键差异):
|
|
3119
|
+
* 旧接口用 rightRows.length 做版本号——UPDATE/DELETE 不改行数时返回脏哈希表
|
|
3120
|
+
* (错误结果)。本缓存以【表对象(WeakMap)+ Table._dataVersion(DML 必递增)+
|
|
3121
|
+
* 键列名】为键:任何 DML 递增版本 → 自动失效;DROP 后 getTable 为空 → 不命中;
|
|
3122
|
+
* CREATE 同名新表为新对象(WeakMap 无旧缓存)→ 不命中。
|
|
3123
|
+
*
|
|
3124
|
+
* 缓存范围:仅键表达式为单列 ColumnRef(columnName 可提取)的等值 JOIN——复杂
|
|
3125
|
+
* 键表达式不缓存(保守)。上限 64 条,超限整段清空。
|
|
3126
|
+
*
|
|
3127
|
+
* @returns {Object} { get(tableName, rowLen, keyExpr), set(tableName, rowLen, keyExpr, ht, bloom) }
|
|
3128
|
+
*/
|
|
3129
|
+
_getJoinHashTableCache() {
|
|
3130
|
+
if (this._joinHashTableCache) {
|
|
3131
|
+
return this._joinHashTableCache;
|
|
3132
|
+
}
|
|
3133
|
+
const db = this.db;
|
|
3134
|
+
const MAX_ENTRIES = 64;
|
|
3135
|
+
const store = new Map(); // WeakMap<tableObj, Map<"version|colKey", {ht, bloom}>>
|
|
3136
|
+
|
|
3137
|
+
function keyOf(tableName, keyExpr, rowLen) {
|
|
3138
|
+
if (!keyExpr || keyExpr.constructor?.name !== 'ColumnRef' || !keyExpr.columnName) {
|
|
3139
|
+
return null; // 复杂键表达式不缓存
|
|
3140
|
+
}
|
|
3141
|
+
// 别名右表(FROM supplier s2)的 tableName 是别名而非基表名——getTable 对
|
|
3142
|
+
// 不存在的表抛错 → 缓存键解析失败必须保守返回 null(不缓存,走原路径)
|
|
3143
|
+
let table = null;
|
|
3144
|
+
try {
|
|
3145
|
+
table = db && typeof db.getTable === 'function' ? db.getTable(tableName) : null;
|
|
3146
|
+
} catch {
|
|
3147
|
+
return null;
|
|
3148
|
+
}
|
|
3149
|
+
if (!table || typeof table._dataVersion !== 'number') {
|
|
3150
|
+
return null; // 表不存在(DROP)/别名/版本不可用 → 不缓存
|
|
3151
|
+
}
|
|
3152
|
+
// 仅缓存全表扫描行集:下推/过滤后的部分行集(rightRows ⊂ 表行)其内容取决于
|
|
3153
|
+
// 具体 WHERE,仅按表+版本+键列做键会跨查询脏撞(不同 WHERE 同行数)→ 保守
|
|
3154
|
+
// 只在 build 行数 === 表行数 时缓存。
|
|
3155
|
+
if (rowLen !== table.rows.length) {
|
|
3156
|
+
return null;
|
|
3157
|
+
}
|
|
3158
|
+
return { table, version: table._dataVersion, colKey: keyExpr.columnName };
|
|
3159
|
+
}
|
|
3160
|
+
|
|
3161
|
+
function lookup(tableName, rowLen, keyExpr) {
|
|
3162
|
+
const k = keyOf(tableName, keyExpr, rowLen);
|
|
3163
|
+
if (!k) return null;
|
|
3164
|
+
const byVersion = store.get(k.table);
|
|
3165
|
+
if (!byVersion) return null;
|
|
3166
|
+
return byVersion.get(`${k.version}|${k.colKey}`) || null;
|
|
3167
|
+
}
|
|
3168
|
+
|
|
3169
|
+
function put(tableName, rowLen, keyExpr, ht, bloom) {
|
|
3170
|
+
const k = keyOf(tableName, keyExpr, rowLen);
|
|
3171
|
+
if (!k) return;
|
|
3172
|
+
let byVersion = store.get(k.table);
|
|
3173
|
+
if (!byVersion) {
|
|
3174
|
+
byVersion = new Map();
|
|
3175
|
+
store.set(k.table, byVersion);
|
|
3176
|
+
}
|
|
3177
|
+
if (byVersion.size >= MAX_ENTRIES) byVersion.clear();
|
|
3178
|
+
byVersion.set(`${k.version}|${k.colKey}`, { ht, bloom });
|
|
3179
|
+
}
|
|
3180
|
+
|
|
3181
|
+
this._joinHashTableCache = { get: lookup, set: put };
|
|
3182
|
+
return this._joinHashTableCache;
|
|
3183
|
+
}
|
|
3184
|
+
|
|
3026
3185
|
_isImmutableCacheableInsert(ast, db) {
|
|
3027
3186
|
const stmts = ast && ast.statements;
|
|
3028
3187
|
if (!stmts || stmts.length !== 1) {
|
|
@@ -368,7 +368,7 @@ export class JoinExecutor {
|
|
|
368
368
|
*/
|
|
369
369
|
_pushJoinPredicates(leftRows, rightRows, leftAlias, rightAlias, stmt, options, joinType = null) {
|
|
370
370
|
const conditions = this._flattenAND(stmt.where);
|
|
371
|
-
if (conditions.length
|
|
371
|
+
if (conditions.length === 0) {
|
|
372
372
|
return { leftRows, rightRows };
|
|
373
373
|
}
|
|
374
374
|
|
|
@@ -397,6 +397,47 @@ export class JoinExecutor {
|
|
|
397
397
|
continue;
|
|
398
398
|
}
|
|
399
399
|
}
|
|
400
|
+
// TPC-H 慢查询专项(2026-11-06):未限定列名的单表条件下推
|
|
401
|
+
// Q3/Q5 的 WHERE 全部使用未限定列名(r_name/o_orderdate/l_shipdate),上方
|
|
402
|
+
// `refs.every(r => r.tableName)` 全不满足 → 下推完全失效 → JOIN 中间行数翻倍
|
|
403
|
+
// (实测 Q5 第 1 步 orders 未过滤 100→50、region 未过滤 5→1)。
|
|
404
|
+
// 归属判定:未限定列按「列名在右表行 / 左行中的存在性」解析——
|
|
405
|
+
// 全部列仅在右表行 → 下推右表(OUTER 由 canPushRight 保护);
|
|
406
|
+
// 全部列仅在左行 → 下推左行(INNER 语义安全,OUTER 由 canPushLeft 保护);
|
|
407
|
+
// 歧义(左右都有)/空行 → 保持原位(保守,不改变语义)。
|
|
408
|
+
if (refs.length > 0 && refs.every(r => !r.tableName)) {
|
|
409
|
+
// TPC-H 慢查询专项边界:条件中含子查询(Subquery/SubqueryExpr 等节点)时,
|
|
410
|
+
// evaluate 需要 context._subqueryExecutor(_pushJoinPredicates 的 options
|
|
411
|
+
// 不保证携带),且子查询引用外层列时按存在性归属判定也可能误判 → 保守保持
|
|
412
|
+
// 原位(由 JOIN 后的 WHERE 阶段正常求值,语义不变)。
|
|
413
|
+
let hasSubquery = false;
|
|
414
|
+
this._walkExpr(cond, (n) => {
|
|
415
|
+
const cn = n.constructor && n.constructor.name;
|
|
416
|
+
// InSubquery/ExistsSubquery 等子查询类节点:evaluate 需要
|
|
417
|
+
// context._subqueryExecutor(本方法 options 不保证携带)→ 保守保持原位
|
|
418
|
+
if (cn && (cn.includes('Subquery') || cn === 'ExistsExpr')) {
|
|
419
|
+
hasSubquery = true;
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
if (!hasSubquery) {
|
|
423
|
+
const cols = refs.map(r => r.columnName);
|
|
424
|
+
const l0 = leftRows[0];
|
|
425
|
+
const r0 = rightRows[0];
|
|
426
|
+
if (l0 && r0) {
|
|
427
|
+
const inRight = cols.every(c => c !== '__rowId' && Object.prototype.hasOwnProperty.call(r0, c));
|
|
428
|
+
const inLeft = cols.every(c => c !== '__rowId' && Object.prototype.hasOwnProperty.call(l0, c));
|
|
429
|
+
if (inRight !== inLeft) {
|
|
430
|
+
if (inRight && canPushRight) {
|
|
431
|
+
rRows = rRows.filter(r => evaluate(cond, r, { params }) === true);
|
|
432
|
+
continue;
|
|
433
|
+
} else if (inLeft && canPushLeft) {
|
|
434
|
+
lRows = lRows.filter(r => evaluate(cond, r, { params }) === true);
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
400
441
|
remaining.push(cond);
|
|
401
442
|
}
|
|
402
443
|
|
|
@@ -326,6 +326,12 @@ export class SelectExecutor {
|
|
|
326
326
|
const joinOpts = {
|
|
327
327
|
params,
|
|
328
328
|
_subqueryExecutor: options._subqueryExecutor,
|
|
329
|
+
// TPC-H 慢查询专项:透传列裁剪/命名空间跳过/哈希表缓存标记——此前白名单式
|
|
330
|
+
// 重建 joinOpts 丢弃这三个标记,导致 _computeRequiredColumns 的列裁剪、
|
|
331
|
+
// _skipTableNamespaces 与 _hashTableCache 三项优化在 JOIN 主路径从未生效。
|
|
332
|
+
_requiredColumns: options._requiredColumns,
|
|
333
|
+
_skipTableNamespaces: options._skipTableNamespaces,
|
|
334
|
+
_hashTableCache: options._hashTableCache,
|
|
329
335
|
...(useParallelJoin
|
|
330
336
|
? {
|
|
331
337
|
parallel: true,
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
// 2026-10-27 性能评审修复:Rust 端仅实现 F64 聚合原语(sumF64/minF64/maxF64),
|
|
37
37
|
// 此前绑定层检查的 sumF32Batch 等导出在 Rust 中不存在,导致 F32 路径恒走 JS。
|
|
38
38
|
// 切换为 F64 原语使 Rust SIMD 聚合真正生效(2.5-5x),同时消除 Float32 累加精度风险。
|
|
39
|
-
import { sumF64, minF64, maxF64 } from '../native/native-acceleration.js';
|
|
39
|
+
import { sumF64, minF64, maxF64, filterI32Eq, filterI32Lt, filterI32Gt, filterI32Le, filterI32Ge, filterF64Eq, filterF64Lt, filterF64Gt, filterF64Le, filterF64Ge } from '../native/native-acceleration.js';
|
|
40
40
|
|
|
41
41
|
// === 常量定义 ============================================================
|
|
42
42
|
|
|
@@ -232,12 +232,41 @@ export class BitmapFilter {
|
|
|
232
232
|
|
|
233
233
|
/**
|
|
234
234
|
* 单列数值快速谓词(生成位图)
|
|
235
|
+
* v2.55.0: TypedArray 输入优先走 Rust 向量化过滤(filterI32 系 / filterF64 系原语,
|
|
236
|
+
* 2026-10-27 评测指出的"native 2.67x~7.55x 但零生产调用方"由此接线),返回索引
|
|
237
|
+
* 后转位图;Rust 不可用或非 TypedArray 回退纯 JS 循环。'!=' 无 Rust 导出,恒走 JS。
|
|
235
238
|
* @param {Float64Array|Int32Array|Array} column 列数据
|
|
236
239
|
* @param {string} op 操作符 '>', '<', '>=', '<=', '=', '!='
|
|
237
240
|
* @param {number} value 阈值
|
|
238
241
|
* @returns {Uint8Array} 位图
|
|
239
242
|
*/
|
|
240
243
|
static numeric(column, op, value) {
|
|
244
|
+
const isI32 = column instanceof Int32Array;
|
|
245
|
+
const isF64 = column instanceof Float64Array;
|
|
246
|
+
if (isI32 || isF64) {
|
|
247
|
+
const rustFn = isI32
|
|
248
|
+
? { '>': filterI32Gt, '<': filterI32Lt, '>=': filterI32Ge, '<=': filterI32Le, '=': filterI32Eq }[op]
|
|
249
|
+
: { '>': filterF64Gt, '<': filterF64Lt, '>=': filterF64Ge, '<=': filterF64Le, '=': filterF64Eq }[op];
|
|
250
|
+
if (rustFn) {
|
|
251
|
+
try {
|
|
252
|
+
const indices = rustFn(column, value);
|
|
253
|
+
const n = column.length;
|
|
254
|
+
// 选择率门槛(2026-11-06 实测):索引→位图是稀疏随机写(每 cache line 仅 1/64 有效),
|
|
255
|
+
// matches ≤ n/8 时全链(Rust 扫描 + 转换)约 3.6x 快于 JS 扫描;
|
|
256
|
+
// 高选择率时转换成本反超(50% 时合计 5.5ms vs JS 4.4ms),退回 JS 单次扫描。
|
|
257
|
+
if (indices.length <= n / 8) {
|
|
258
|
+
const bitmap = new Uint8Array(n);
|
|
259
|
+
for (let i = 0; i < indices.length; i++) {
|
|
260
|
+
bitmap[indices[i]] = 1;
|
|
261
|
+
}
|
|
262
|
+
return bitmap;
|
|
263
|
+
}
|
|
264
|
+
// matches 过多 → 落到下方 JS 扫描(已付出的 Rust 扫描最多 ~2.9ms/1M,可接受)
|
|
265
|
+
} catch {
|
|
266
|
+
// native 调用异常 → 回退 JS
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
241
270
|
const n = column.length;
|
|
242
271
|
const bitmap = new Uint8Array(n);
|
|
243
272
|
switch (op) {
|
|
@@ -1473,8 +1473,10 @@ export function batchMaxF32(data) {
|
|
|
1473
1473
|
export function batchCountGtI32(data, threshold) {
|
|
1474
1474
|
const arr = data instanceof Int32Array ? data : new Int32Array(data);
|
|
1475
1475
|
const mod = getNativeModule();
|
|
1476
|
-
|
|
1477
|
-
|
|
1476
|
+
// v2.55.0 修复:Rust 导出名为 countGtI32(vec_agg.rs count_gt_i32),
|
|
1477
|
+
// 此前检查的 countGtI32Batch 不存在 → 永远走 JS 回退(2026-10-27 评测"绑定名失配"的最后一处)。
|
|
1478
|
+
if (mod && typeof mod.countGtI32 === 'function') {
|
|
1479
|
+
return mod.countGtI32(arr, threshold);
|
|
1478
1480
|
}
|
|
1479
1481
|
return jsCountGtI32(arr, threshold);
|
|
1480
1482
|
}
|
package/src/sdk/java/pom.xml
CHANGED
|
@@ -62,7 +62,7 @@ import java.util.NoSuchElementException;
|
|
|
62
62
|
public class PureClient {
|
|
63
63
|
|
|
64
64
|
/** SDK 版本号。 */
|
|
65
|
-
public static final String SDK_VERSION = "2.
|
|
65
|
+
public static final String SDK_VERSION = "2.55.0";
|
|
66
66
|
|
|
67
67
|
/** 默认请求超时(秒)。 */
|
|
68
68
|
public static final long DEFAULT_TIMEOUT_SECONDS = 30;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "pure-js-db/php-sdk",
|
|
3
3
|
"description": "PHP SDK for pure-js-db — pure JavaScript database engine, via HTTP RESTful API.",
|
|
4
4
|
"type": "library",
|
|
5
|
-
"version": "2.
|
|
5
|
+
"version": "2.55.0",
|
|
6
6
|
"keywords": ["database", "sdk", "pure-js-db", "rest", "curl"],
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"require": {
|
package/src/sdk/rust/Cargo.toml
CHANGED
|
@@ -29,8 +29,8 @@ grep -E 'SDK 版本:\*\*[0-9]+\.[0-9]+\.[0-9]+\*\*' README.md # 期望: SDK
|
|
|
29
29
|
|
|
30
30
|
| 文件 | 当前值 |
|
|
31
31
|
|------|--------|
|
|
32
|
-
| `pure-js-db/package.json` | `2.
|
|
33
|
-
| `pure-js-db/src/index.js` `VERSION` | `'2.
|
|
32
|
+
| `pure-js-db/package.json` | `2.55.0` |
|
|
33
|
+
| `pure-js-db/src/index.js` `VERSION` | `'2.55.0'` |
|
|
34
34
|
|
|
35
35
|
---
|
|
36
36
|
|
package/src/sdk/rust/src/lib.rs
CHANGED
|
@@ -58,6 +58,20 @@ export const batchMaxF64 = noop;
|
|
|
58
58
|
export const sumF64 = noop;
|
|
59
59
|
export const minF64 = noop;
|
|
60
60
|
export const maxF64 = noop;
|
|
61
|
+
// Rust 向量化过滤原语(v2.55.0 列存 BitmapFilter 接线,微信 stub 同步提供 noop)
|
|
62
|
+
export const filterI32Eq = noop;
|
|
63
|
+
export const filterI32Lt = noop;
|
|
64
|
+
export const filterI32Gt = noop;
|
|
65
|
+
export const filterI32Le = noop;
|
|
66
|
+
export const filterI32Ge = noop;
|
|
67
|
+
export const filterF64Eq = noop;
|
|
68
|
+
export const filterF64Lt = noop;
|
|
69
|
+
export const filterF64Gt = noop;
|
|
70
|
+
export const filterF64Le = noop;
|
|
71
|
+
export const filterF64Ge = noop;
|
|
72
|
+
// Rust TopN 堆选择原语(v2.54.0 ORDER BY+LIMIT 快速路径,微信 stub 同步提供 noop)
|
|
73
|
+
export const topnF64Asc = noop;
|
|
74
|
+
export const topnF64Desc = noop;
|
|
61
75
|
|
|
62
76
|
// 常见的常量导出
|
|
63
77
|
export const PARALLEL_THRESHOLD = 0;
|
package/src/utils/join-engine.js
CHANGED
|
@@ -449,6 +449,13 @@ export class JoinEngine {
|
|
|
449
449
|
if (leftKeyExtractor) {
|
|
450
450
|
// 快速路径:预编译 extractor
|
|
451
451
|
const maxRows = options.maxRows;
|
|
452
|
+
// TPC-H 慢查询专项:复合行列裁剪——按 options._requiredColumns(全 AST 列集合,
|
|
453
|
+
// 由 Executor.execute 顶层 SELECT 注入)按需拷贝右表新键,复合行宽度从"全部表列"
|
|
454
|
+
// 降到"查询实际引用列",展开成本随行宽线性下降。null = 未启用(全列拷贝)。
|
|
455
|
+
const required = options._requiredColumns || null;
|
|
456
|
+
// 无限定列查询(_skipTableNamespaces)跳过 __table__ 赋值:该命名空间的全部
|
|
457
|
+
// 消费点(限定列解析/_getTableRow/graph-table)在此查询形状下均不被触发。
|
|
458
|
+
const skipNs = options._skipTableNamespaces === true;
|
|
452
459
|
for (const leftRow of leftRows) {
|
|
453
460
|
const key = leftKeyExtractor(leftRow);
|
|
454
461
|
if (key === null || key === undefined) {
|
|
@@ -459,14 +466,26 @@ export class JoinEngine {
|
|
|
459
466
|
if (matches) {
|
|
460
467
|
for (const rightRow of matches) {
|
|
461
468
|
const combined = { ...leftRow };
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
469
|
+
if (required) {
|
|
470
|
+
// 列裁剪路径:仅拷贝 required 列(flat);__table__ 引用随 spread 完整保留,
|
|
471
|
+
// 限定列访问(t.col)不受影响;未在 required 的列不进入复合行。
|
|
472
|
+
for (const k in rightRow) {
|
|
473
|
+
if (k !== '__rowId' && !(k in leftRow) && required.has(k)) {
|
|
474
|
+
combined[k] = rightRow[k];
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
} else {
|
|
478
|
+
// v2.11 性能: for...in 替代 Object.entries(免每次 entries 数组分配)
|
|
479
|
+
for (const k in rightRow) {
|
|
480
|
+
if (k !== '__rowId' && !(k in leftRow)) {
|
|
481
|
+
combined[k] = rightRow[k];
|
|
482
|
+
}
|
|
466
483
|
}
|
|
467
484
|
}
|
|
468
|
-
|
|
469
|
-
|
|
485
|
+
if (!skipNs) {
|
|
486
|
+
combined[rightTableKey] = rightRow;
|
|
487
|
+
combined[leftTableKey] = leftRow;
|
|
488
|
+
}
|
|
470
489
|
joined.push(combined);
|
|
471
490
|
// LIMIT 短路(2026-10-27 #4):仅当查询形状安全(无 ORDER BY/GROUP BY 等,
|
|
472
491
|
// 由 select-executor 判定后传入 maxRows)时提前终止,避免物化全部匹配对。
|
package/src/version.js
CHANGED
|
@@ -14,6 +14,6 @@
|
|
|
14
14
|
* 并校验所有 JS 文件不再硬编码版本号。
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
export const VERSION = '2.
|
|
17
|
+
export const VERSION = '2.55.0';
|
|
18
18
|
|
|
19
19
|
export const PHASE = 'v2.53.2 可靠性验证与发版演练:Raft 3 节点容器集群实测通过(Leader 选举 + 日志复制 + 宕机重选三项 PASS,raft-node-entry.js 新增 POST /raft/propose 提案端点 + raft-extended-verify.mjs 两阶段扩展验证 + run-raft-extended.sh 一键编排);可靠性周期重跑(2026-11-03)五项全 PASS(3 节点 2PC 30/30 TPS 95.85、网络分区注入 PARTITION_TEST_PASS、MVCC 同行冲突 SAME_ROW_CONFLICT_PASS、7 分钟 soak 7 cycle 0 abort 资金守恒、崩溃重启 6/6、断电模拟 8/8);npm 周下载 486 破零记录;' + 'v2.53.0 性能服务化(P0-5/P0-6 请求级查询取消与超时):执行器新增 options 级取消检查 _abortCheck(直接读 options.signal,避免共享 Executor 并发异步查询互相覆盖实例级取消状态),接线到全部分片让出检查点(分片过滤/分片 GROUP BY/_finishSelectAfterRows 阶段边界/JOIN 派发前/parallelHashJoinWithWorker 键计算让出点);executeSQLAsync 与 parallelHashJoinWithWorker 对取消错误(QUERY_ABORTED)上抛而非静默回退同步;routeSQL(sql, params, res) 为每个请求创建 AbortController——客户端断开(res 提前 close)即中止 + this._timeout 超时中止,取消在让出检查点真实生效(P0-6);query handler 主查询与 SELECT 端点传递 res;新增 query-cancel.test.js 6 项(预中止/JOIN 路径/运行中/routeSQL 断开/超时/正常回归)已注册 run-all;' + 'v2.52.0 性能服务化(P0-1 Worker 并行 JOIN + P0-3 HTTP WorkerPool):JoinEngine.parallelHashJoinWithWorker 把 Hash Join 探测(JOIN_PROBE)真实分发到 Worker 线程(主线程预计算键 + 浅拷贝行附加 __joinKey + execAll 分发 + 合并 + LEFT 补行 + 失败回退同步 _parallelHashJoin);Executor._performJoinAsync 委托 JoinExecutor 供异步路径使用;execSelectShardedAsync 支持单普通表等值 INNER/LEFT JOIN 的 Worker 并行(_canShardAsyncSelect 扩展 + WHERE 不得引用右表列的安全限制);DBServer 启动时初始化 WorkerPool(lazy,默认 CPU-1,可 enableWorkerPool=false 关闭)并在 stop 时终止,使 HTTP 服务中 JOIN/SORT/FILTER/GROUP_BY 并行算子真实可用;新增 worker-join.test.js 5 项(INNER/LEFT/无池回退/右表列回退/任务分发断言)已注册 run-all;' + 'v2.51.0 缺陷修复(物化视图注册为可查询关系 + WAL 大批次分块):物化视图作为只读关系注册到 Database(materializedViews 注册表 + hasTable/getTable 识别 + 只读表外观 + 命名冲突双向拒绝 + INFORMATION_SCHEMA.TABLES/COLUMNS 与 pg_class 可见 + CLI/.tables 与 HTTP 表列表纳入 + 持久化重建后重新注册 + SELECT/JOIN/EXPLAIN/跨库可解析),修复 CREATE MATERIALIZED VIEW 通过 SQL 调用时校验错误变为未处理 Promise 拒绝的问题(createMaterializedView 同步抛出 + execCreateMaterializedView 透传异步刷新);事务 WAL 按 maxEntriesPerBatch 分块,消除同步突发写入 >65535 条时 serializeBatch 的 RangeError 崩溃路径(守卫保留供防御测试);物化视图测试 +4 项(注册/冲突/只读/持久化),wal-count-bound 7/7 全绿;' + 'v2.50.0 T-05/T-06 架构清理:优化器 barrel 补齐 5 个生产可达类导出(ExplainVisualizer/JoinOrderOptimizer/QueryPlanCache/Histogram·HistogramBucket/TimestampQueryRewriter,经 engine/public-api.js 接主入口)+ StorageEngine 统一存储引擎接口(STORAGE_ENGINE_METHODS 契约 + isStorageEngine/assertStorageEngine 校验 + STORAGE_BACKENDS 四后端工厂 memory/file/binary/buffered + StorageEngine.switchBackend 运行时切换 + createStorageEngine)+ T-06 lockfree-worker.js 占位桩整体移除(lockfree-manager 移除 _startWorkers/useWorkers,单线程 COW 模式收敛);storage-engine.test 16 项(含绕过)+ optimizer-exports.test 4 项(含主入口接线)已注册 run-all,全量 806/806 全绿、幻影审计 A 类 0/B 类 0、dist 22/22 含新符号;' + 'v2.49.0 E34 数据血缘收尾(UPDATE 源追踪 + CREATE VIEW 事前环拒绝 + Mermaid 导出):extractUpdateColumnLineage(UPDATE SET 列级血缘,直接列/表达式/函数,常量不产生依赖,多表 UPDATE...FROM 无前缀 unresolved)+ recordStatement UpdateStatement 分支;wouldCreateCycle + DDLExecutor._assertNoLineageCycle(CREATE VIEW/CREATE MATERIALIZED VIEW 事前环拒绝,仅目标视图已存在时预检,保留 v2.11 E15 M2 T3 前向引用后补行为);toMermaid/getLineageMermaid(Mermaid flowchart 导出);生产入口导出 + lineage-e34 19 项 + 生产接线 4 项,run-all 804/804 全绿;' + 'v2.48.0 数据血缘全链路落地(P7 M1-M4 + P8 深化 + P9 版本历史):表级/列级血缘自动记录(CREATE VIEW/MATERIALIZED VIEW/INSERT...SELECT)、LINEAGE SQL 语句(LINEAGE TABLE/COLUMN + lineage-executor + MCP 工具)、任务级登记(CopyStream/Migrator)、持久化(meta v5 随 PersistedDatabase 落盘)、环检测 detectCycles、CTE 列级推导(collectCtes)、HTML 可视化 toHTML、版本历史(getLineageHistory/getLineageAtTime/getLineageEvolution)、变更影响分析(analyzeImpact),生产入口全导出 + 常驻测试 70 项;run-all 全量通过 + vitest 123/123 + c8 Lines 91.99%/Branches 88.43%;' + 'v2.45.2 双缺陷修复(多轮门禁盘点发现):① REGEXP (?g)/(?y) 全局/粘性标志剥离——编译产物跨行 LRU 缓存复用而 g/y 的 lastIndex 状态使求值行依赖((?g)a 四行 [1,0,1,0]),REGEXP 逐行独立求值语义下 g/y 无意义且有害,编译期剥离(i/m/s/u 保持);② WASM min/max 对齐区 NaN 传播——f32x4.min/max 按 IEEE 语义传播 NaN 与"NaN 被忽略"文档语义相悖(v2.45.1 激活 SIMD 路径后成为实际行为),NaN 结果 ⟺ 对齐区有 NaN lane,JS 重扫跳过 NaN 与降级路径对齐;绕过测试 v2450 +2(29 项)、wasm-simd-engine E3 守卫(23 项);' + 'v2.45.1 WASM SIMD 常量错位修复 + 覆盖率债务清零:f32x4_max 误写 0xEA(实为 f32x4.pmin)致 init 运行时验证恒败、引擎自引入即静默降级(SIMD 路径从未激活,全部流量走 TypedArray fallback)——修正 0xE9 并经验解码验证(0xE7=div/0xE8=min/0xE9=max/0xEA=pmin/0xEB=pmax),修复后 READY/WASM_SIMD 五算子精确正确;wasm-simd-engine 移出 c8 exclude 并补全路径测试(初始化/SIMD 数值/降级/扩容/单例 + 修复守卫);lockfree-worker.js 人工评估归档(确认占位桩:主路径不经 Worker,保持 exclude 豁免);' + 'v2.45.0 安满分诊 P1/P2 治理落地(eslint-security 58 errors 分诊第四节立项 ①②③④ 全部修复):REGEXP 用户正则防护(security/regex-guard.js compileUserRegex:模式长度上限 1024 + 嵌套量词灾难回溯编译期筛查,bitmap-filter _getRegex 接线)+ LIKE/GLOB/KEYS→regex 转换入口统一模式长度上限 1024(bitmap-filter _likeToRegex/_buildEscapeRegex、btree/offheap 部分索引 LikeExpr、cache-adapter KEYS、sqlite-kv-adapter LIKE、expression.js GLOB 共 7 处)+ oracle-adapter DECODE 线性解析重写(消除指数回溯,修复多分支 CASE 语义 + 括号/引号感知 + 嵌套 DECODE 递归转换)+ 输入长度上限(sql-injection-detector 256KB fail-closed、schema-versioning DDL 256KB + 顶层逗号线性切分);绕过测试 v2450-p0-bypass-boundary 通过;v2.0 长期路线图收官:v2.0-1..6 全部落地(JSONB 二进制 JSON / Lambda 表达式 x->x+1 / UNNEST 多列并行 / DEFERRABLE 约束 / EXCLUDE 排他约束 / LISTEN-NOTIFY 异步通知,DoD 5/5 各 27-39 用例);v2.0-7 WAL flush 慢 seq 初始化 flaky 修复 + wal-crash-validation 测试加固(10/10 稳定 + 绕过测试有效性验证)+ flush-timeout-simulation FT2/FT4 libuv 时钟边界修复;全量回归 run-all 459/459 全绿(历史首次);v2.0-8 SQL:2016 阶段1 聚合补全(PERCENTILE_CONT/DISC + STRING_AGG/ARRAY_AGG + MODE + BOOL_AND/OR/EVERY + WIDTH_BUCKET + REGR_*,DoD 5/5 37/37);v2.0-9 SQL:2016 阶段2 MATCH_RECOGNIZE 行模式识别(Part 2 §21.6 新执行器,DoD 5/5 30/30);v2.0-10 SQL:2016 阶段3 多态表函数 PTF(Part 2 §10.9:ANY TABLE/COLUMN/MAX(n) + RETURNS TABLE + 函数体 AST 重写 + 物化临时表 + JOIN 内调用 + 幂等性/零副作用,DoD 5/5 25/25,run-all 461/462 + audit-log A8 时间竞态 flaky 修复);v2.0-11 SQL:2016 阶段4 RATIO_TO_REPORT 分析函数(Part 2 §9.6 G9:分区求和比例 + NULL/除零语义 + 参数校验,DoD 5/5 14/14)+ 窗口函数源表零副作用 P0 修复(window-functions.js 浅拷贝行,156 项窗口回归全绿);v2.0-12 SQL:2016 阶段5 类型系统深化(Part 2 §4.4 G11 INTERVAL 完整类型:YEAR/MONTH/DAY/HOUR/MINUTE/SECOND 字段子句 + 单位内嵌 + 纯时间/天数自动判断 + DAY TO MINUTE HH:MM + DAY TO HOUR/MINUTE/SECOND 时间部分列宽 + date/timestamp±interval 日历月 clamp + 算术/比较/CAST/一元运算;G12 TIMESTAMP WITH TIME ZONE:带偏移解析→UTC 内部存储 + 会话时区显示 + SET TIME ZONE/SHOW TIME ZONE + INSERT/UPDATE 归一化 + 混合比较,DoD 5/5 62/62);v1.42.x 基础:STRUCT 类型 / LIST-ARRAY 完整操作 / WITHOUT ROWID 表 / VACUUM-REINDEX / user_version-application_id 持久化 + v1.42.1 全量回归闭环(16 个预存失败清零 + 2 个源码回归修复:query-isolation 原型链 / select-executor ORDER BY 聚合别名);v1.41.x 基础:ASOF JOIN / ATTACH DATABASE / COMMENT ON / PIVOT 多聚合 / WITH CHECK OPTION;v1.40.4 基础:PRAGMA 完整集(25+ SQLite 标准)+ 版本号多 SDK 单一来源同步;v1.40.3 基础:3 个 P0 安全修复真实现 + SECURITY DEFINER 真实现 + 循环依赖 0;v2.11(E15)遗留清零:WAL 恢复数据版本一致性 + 视图 lossy 根治 + 多视图 CASCADED 链 + SECURITY DEFINER 真实现 + RedisCacheAdapter 真实现 + Arrow 有限宽度 guard + WAL count 上界 + executor 拆分(sql-reconstructor);v2.12(E16)稳定性与分布式闭环:flaky 三件套根治(2pc-perf/index-build/soak-test + vector-executor 门槛统一)+ 跨库 DDL/事务 + WAL hot-standby 探针 + 分布式真实网络验证(raft/3PC/2PC + PK fastpath WAL 缺陷修复 + 2PC rejection 修复)+ executor PRAGMA 族拆离(pragma-executor,-550 行)+ 全量回归 612/612 + DL-D3 时序 flaky 修复;v2.13(E17)收尾治理与 SQL/XML:版本号全量对齐 + SQL:2016 阶段6 G10(XMLSERIALIZE/XMLAGG/XMLTABLE)+ 全量 c8 复验 + 跨库 TPC-H 数据刷新 + SQL:2023 roadmap 收敛;v2.14(E18)列存文件格式 M1+M2:PJDC 二进制列文件格式(表签名三层校验:schemaHash + rowCount + columnCount)+ save/load 持久化加速缓存 + 列存 roundtrip 持久化验证(22+8 用例全绿);v2.15(E19)列存文件格式 M3:WAL _invalidateTableIndexes 修复(checkpoint 后索引失效重建)+ run-all 618/618 全绿;v2.15(E20)工程化基线清零:parquet.js 3 处 setUint32 guard 补零 + 超限绕过测试 + 版本号同步 2.13.0→2.15.0 + c8 覆盖率实测 + 审计章节归档;';
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import purejsdb.PureJsDBClient;
|
|
2
|
-
import purejsdb.TransactionClient;
|
|
3
|
-
|
|
4
|
-
public class TestClient {
|
|
5
|
-
public static void main(String[] args) throws Exception {
|
|
6
|
-
testBasic();
|
|
7
|
-
System.out.println();
|
|
8
|
-
testTransaction();
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
static void testBasic() throws Exception {
|
|
12
|
-
PureJsDBClient client = new PureJsDBClient();
|
|
13
|
-
System.out.println("[Java SDK Test]");
|
|
14
|
-
|
|
15
|
-
String result = client.query("SELECT 1 AS test");
|
|
16
|
-
assert result != null && result.contains("data") : "query should return data";
|
|
17
|
-
System.out.println(" ✅ query: SELECT 1");
|
|
18
|
-
|
|
19
|
-
String tables = client.getTables();
|
|
20
|
-
assert tables != null : "getTables should return data";
|
|
21
|
-
System.out.println(" ✅ getTables");
|
|
22
|
-
|
|
23
|
-
String status = client.getStatus();
|
|
24
|
-
assert status != null : "getStatus should return data";
|
|
25
|
-
System.out.println(" ✅ getStatus");
|
|
26
|
-
|
|
27
|
-
System.out.println("\nAll Java SDK tests passed!");
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
static void testTransaction() throws Exception {
|
|
31
|
-
TransactionClient client = new TransactionClient();
|
|
32
|
-
System.out.println("[Java Transaction SDK Test]");
|
|
33
|
-
|
|
34
|
-
String txResult = client.begin();
|
|
35
|
-
assert txResult != null : "begin should return data";
|
|
36
|
-
System.out.println(" ✅ begin");
|
|
37
|
-
|
|
38
|
-
// Extract transaction id from response (simplified)
|
|
39
|
-
String txId = extractTxId(txResult);
|
|
40
|
-
|
|
41
|
-
String execResult = client.executeInTx("SELECT 1 AS tx_test", txId);
|
|
42
|
-
assert execResult != null : "executeInTx should return data";
|
|
43
|
-
System.out.println(" ✅ executeInTx");
|
|
44
|
-
|
|
45
|
-
String commitResult = client.commit(txId);
|
|
46
|
-
assert commitResult != null : "commit should return data";
|
|
47
|
-
System.out.println(" ✅ commit");
|
|
48
|
-
|
|
49
|
-
System.out.println("\nAll Java Transaction SDK tests passed!");
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
private static String extractTxId(String response) {
|
|
53
|
-
// Simple extraction - looks for "sessionId" or "transactionId" in JSON
|
|
54
|
-
if (response.contains("\"sessionId\"")) {
|
|
55
|
-
int start = response.indexOf("\"sessionId\"") + 12;
|
|
56
|
-
start = response.indexOf('"', start) + 1;
|
|
57
|
-
int end = response.indexOf('"', start);
|
|
58
|
-
return response.substring(start, end);
|
|
59
|
-
}
|
|
60
|
-
if (response.contains("\"transactionId\"")) {
|
|
61
|
-
int start = response.indexOf("\"transactionId\"") + 16;
|
|
62
|
-
start = response.indexOf('"', start) + 1;
|
|
63
|
-
int end = response.indexOf('"', start);
|
|
64
|
-
return response.substring(start, end);
|
|
65
|
-
}
|
|
66
|
-
return "unknown";
|
|
67
|
-
}
|
|
68
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
"""Python SDK 测试"""
|
|
2
|
-
from purejsdb.client import PureJsDBClient, TransactionClient
|
|
3
|
-
import sys
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def test_basic():
|
|
7
|
-
client = PureJsDBClient()
|
|
8
|
-
print('[Python SDK Test]')
|
|
9
|
-
|
|
10
|
-
# 测试 get_status
|
|
11
|
-
status = client.get_status()
|
|
12
|
-
assert status is not None, 'get_status should return data'
|
|
13
|
-
print(f' ✅ get_status: {status.get("status", "OK")}')
|
|
14
|
-
|
|
15
|
-
# 测试 get_tables
|
|
16
|
-
tables = client.get_tables()
|
|
17
|
-
assert tables is not None, 'get_tables should return data'
|
|
18
|
-
print(f' ✅ get_tables')
|
|
19
|
-
|
|
20
|
-
# 测试 query
|
|
21
|
-
result = client.query('SELECT 1 AS test')
|
|
22
|
-
assert result is not None, 'query should return data'
|
|
23
|
-
print(f' ✅ query: SELECT 1')
|
|
24
|
-
|
|
25
|
-
# 测试 health
|
|
26
|
-
health = client.health()
|
|
27
|
-
assert health is not None, 'health should return data'
|
|
28
|
-
print(f' ✅ health')
|
|
29
|
-
|
|
30
|
-
print('\nAll Python SDK tests passed!')
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def test_transaction():
|
|
34
|
-
"""测试事务客户端(需要服务器支持事务)"""
|
|
35
|
-
client = TransactionClient()
|
|
36
|
-
print('[Python Transaction SDK Test]')
|
|
37
|
-
|
|
38
|
-
# 开始事务
|
|
39
|
-
tx = client.begin()
|
|
40
|
-
assert tx is not None, 'begin should return data'
|
|
41
|
-
tx_id = tx.get('sessionId') or tx.get('transactionId')
|
|
42
|
-
print(f' ✅ begin: {tx_id}')
|
|
43
|
-
|
|
44
|
-
# 在事务中执行
|
|
45
|
-
result = client.execute_in_tx('SELECT 1 AS tx_test', tx_id)
|
|
46
|
-
assert result is not None, 'execute_in_tx should return data'
|
|
47
|
-
print(f' ✅ execute_in_tx')
|
|
48
|
-
|
|
49
|
-
# 提交事务
|
|
50
|
-
result = client.commit(tx_id)
|
|
51
|
-
assert result is not None, 'commit should return data'
|
|
52
|
-
print(f' ✅ commit')
|
|
53
|
-
|
|
54
|
-
print('\nAll Python Transaction SDK tests passed!')
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if __name__ == '__main__':
|
|
58
|
-
test_basic()
|
|
59
|
-
print()
|
|
60
|
-
try:
|
|
61
|
-
test_transaction()
|
|
62
|
-
except Exception as e:
|
|
63
|
-
print(f' ⚠️ Transaction test skipped (server may not support transactions): {e}')
|