jsql-neo 6.3.0 → 6.3.1

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/lib/btree.js CHANGED
@@ -127,7 +127,8 @@ class BTree {
127
127
  */
128
128
  _route(node, key) {
129
129
  let i = this._lowerBound(node.keys, key);
130
- if (i < node.keys.length && node.keys[i] === key) {
130
+ // 叶子节点没有子节点可路由(children[i] 为 undefined),直接返回下界,避免崩溃。
131
+ if (i < node.keys.length && node.keys[i] === key && node.children[i]) {
131
132
  return this._getMax(node.children[i]).key === key ? i : i + 1;
132
133
  }
133
134
  return i;
package/lib/sql.js CHANGED
@@ -2945,7 +2945,19 @@ function applyScalarFunction(fnNode, row, ctx) {
2945
2945
  case 'LTRIM': return args[0] == null ? null : String(args[0]).replace(/^\s+/, '');
2946
2946
  case 'RTRIM': return args[0] == null ? null : String(args[0]).replace(/\s+$/, '');
2947
2947
  case 'ABS': return args[0] == null ? null : Math.abs(args[0]);
2948
- case 'ROUND': return args[0] == null ? null : (args[1] !== undefined ? Number(args[0].toFixed(args[1])) : Math.round(args[0]));
2948
+ case 'ROUND': {
2949
+ // MySQL 语义:四舍五入「半远离零」(Math.round 是半向 +∞,对负数会错,如 -3.5→-3)
2950
+ // 位数 D 可为负(向小数点左侧舍入);非数值/非法输入返回 null,绝不抛 TypeError/RangeError。
2951
+ if (args[0] == null) return null;
2952
+ const x = Number(args[0]);
2953
+ if (!Number.isFinite(x)) return null;
2954
+ const digits = args[1] === undefined ? 0 : (Math.trunc(Number(args[1])) || 0);
2955
+ const factor = Math.pow(10, digits);
2956
+ if (!Number.isFinite(factor)) return x; // 位数过大:值不变
2957
+ if (factor === 0) return 0; // 位数过小:舍入到 0
2958
+ const sign = x < 0 ? -1 : 1;
2959
+ return sign * Math.round(Math.abs(x) * factor) / factor;
2960
+ }
2949
2961
  case 'FLOOR': return args[0] == null ? null : Math.floor(args[0]);
2950
2962
  case 'CEIL': case 'CEILING': return args[0] == null ? null : Math.ceil(args[0]);
2951
2963
  case 'MOD': return (args[0] == null || args[1] === 0) ? null : args[0] % args[1];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsql-neo",
3
- "version": "6.3.0",
3
+ "version": "6.3.1",
4
4
  "description": "JSQL-NEO — Rust-powered embedded database with WASM, REST API, B-Tree indexes, WAL, crash recovery",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -58,6 +58,15 @@ function validate(bt) {
58
58
  ok('non-unique remove value keeps key until last', n.search('a').length === 0 && n.size === 1);
59
59
  }
60
60
 
61
+ /* ---- _route() 对叶子节点不再崩溃(单键根节点) ---- */
62
+ {
63
+ const bt = new BTree();
64
+ bt.insert(5, 0);
65
+ let threw = false;
66
+ try { bt._route(bt._root, 5); } catch (e) { threw = true; }
67
+ ok('_route() on leaf root no crash', !threw && bt._route(bt._root, 5) === 0);
68
+ }
69
+
61
70
  /* ---- 二分查找 / IN / 前缀 ---- */
62
71
  {
63
72
  const bt = new BTree(16);
@@ -268,6 +268,20 @@ const rowsOf = (r) => (Array.isArray(r) ? r[0] : r).rows;
268
268
  ok('未绑定参数不应在解析期报错', threw === null);
269
269
  }
270
270
 
271
+ /* ================= BUG 修复: ROUND 非数值/负数位数不再崩溃 ================= */
272
+ console.log('\n--- BUG 修复: ROUND MySQL 语义 ---');
273
+ {
274
+ const roundOf = async (expr) => rowsOf(await executeSQL(engine, `SELECT ${expr} AS v`, OPTS))[0][0];
275
+ ok("ROUND('abc', 0) 返回 null 而非 TypeError", await roundOf("ROUND('abc', 0)") === null);
276
+ ok("ROUND('abc') 返回 null", await roundOf("ROUND('abc')") === null);
277
+ ok('ROUND(3.14159, 2) = 3.14', await roundOf('ROUND(3.14159, 2)') === 3.14);
278
+ ok('ROUND(3.5) = 4', await roundOf('ROUND(3.5)') === 4);
279
+ ok('ROUND(-3.5) = -4(半远离零,与 MySQL 一致)', await roundOf('ROUND(-3.5)') === -4);
280
+ ok('ROUND(-2.5) = -3', await roundOf('ROUND(-2.5)') === -3);
281
+ ok('ROUND(123.456, -1) = 120(负数位数)', await roundOf('ROUND(123.456, -1)') === 120);
282
+ ok('ROUND(1.5, 101) 不再抛 RangeError', await roundOf('ROUND(1.5, 101)') === 1.5);
283
+ }
284
+
271
285
  /* ================= 5.6.0: 相关子查询(曾静默返回 null) ================= */
272
286
  console.log('\n--- 5.6.0: 相关子查询 ---');
273
287
  {
@@ -616,3 +616,76 @@ let wasm;
616
616
  export function __wbg_set_wasm(val) {
617
617
  wasm = val;
618
618
  }
619
+
620
+ // ============================================================
621
+ // WASM 初始化(原构建产物缺失这一段,导致 browser.mjs 无法加载:
622
+ // 它 `import init from './browser_bg.mjs'`,但本文件没有 default export)
623
+ // ============================================================
624
+
625
+ function __wbg_get_imports() {
626
+ const import0 = {
627
+ __proto__: null,
628
+ __wbg___wbindgen_string_get_b0ca35b86a603356: function(arg0, arg1) {
629
+ const obj = getObject(arg1);
630
+ const ret = typeof(obj) === 'string' ? obj : undefined;
631
+ var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_export, wasm.__wbindgen_export2);
632
+ var len1 = WASM_VECTOR_LEN;
633
+ getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
634
+ getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
635
+ },
636
+ __wbg___wbindgen_throw_344f42d3211c4765: function(arg0, arg1) {
637
+ throw new Error(getStringFromWasm0(arg0, arg1));
638
+ },
639
+ __wbg_new_cc984128914cfc6f: function(arg0) {
640
+ const ret = new Date(getObject(arg0));
641
+ return addHeapObject(ret);
642
+ },
643
+ __wbg_now_86c0d4ba3fa605b8: function() {
644
+ const ret = Date.now();
645
+ return ret;
646
+ },
647
+ __wbg_toISOString_706fbe321055ee58: function(arg0) {
648
+ const ret = getObject(arg0).toISOString();
649
+ return addHeapObject(ret);
650
+ },
651
+ __wbindgen_cast_0000000000000001: function(arg0) {
652
+ // Cast intrinsic for `F64 -> Externref`.
653
+ const ret = arg0;
654
+ return addHeapObject(ret);
655
+ },
656
+ __wbindgen_object_drop_ref: function(arg0) {
657
+ takeObject(arg0);
658
+ },
659
+ };
660
+ return { './jsql_neo_wasm_bg.js': import0 };
661
+ }
662
+
663
+ /**
664
+ * 初始化 WASM 模块(default export,供 browser.mjs 的 `await init()` 调用)。
665
+ * @param {string|URL|Request|Response|ArrayBuffer|WebAssembly.Module} [input]
666
+ * 默认加载同目录的 jsql_neo_wasm_bg.wasm
667
+ */
668
+ async function __wbg_init(input) {
669
+ if (wasm !== undefined) return wasm;
670
+ const imports = __wbg_get_imports();
671
+ const path = input === undefined
672
+ ? new URL('./jsql_neo_wasm_bg.wasm', import.meta.url)
673
+ : input;
674
+
675
+ let instance;
676
+ if (path instanceof WebAssembly.Module) {
677
+ instance = (await WebAssembly.instantiate(path, imports)).instance;
678
+ } else if (typeof Response !== 'undefined' && path instanceof Response) {
679
+ instance = (await WebAssembly.instantiate(await path.arrayBuffer(), imports)).instance;
680
+ } else if (path instanceof ArrayBuffer || ArrayBuffer.isView(path)) {
681
+ instance = (await WebAssembly.instantiate(path, imports)).instance;
682
+ } else {
683
+ // 用 instantiate 而非 instantiateStreaming:某些 CDN 不返回 application/wasm MIME
684
+ const bytes = await (await fetch(path)).arrayBuffer();
685
+ instance = (await WebAssembly.instantiate(bytes, imports)).instance;
686
+ }
687
+ __wbg_set_wasm(instance.exports);
688
+ return instance.exports;
689
+ }
690
+
691
+ export default __wbg_init;