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 +2 -1
- package/lib/sql.js +13 -1
- package/package.json +1 -1
- package/test/btree.test.js +9 -0
- package/test/sql-parser.test.js +14 -0
- package/wasm/browser_bg.mjs +73 -0
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
|
-
|
|
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':
|
|
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
package/test/btree.test.js
CHANGED
|
@@ -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);
|
package/test/sql-parser.test.js
CHANGED
|
@@ -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
|
{
|
package/wasm/browser_bg.mjs
CHANGED
|
@@ -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;
|