jsql-neo 6.4.0-beta.11 → 6.4.0-beta.12
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 +1 -1
- package/index.js +1 -1
- package/lib/sql.js +19 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> **One engine to rule them all** — a Rust-powered embedded database that speaks your language:
|
|
4
4
|
> MySQL, PostgreSQL, MongoDB, Redis, SQL, TypeScript, **and the browser**.
|
|
5
5
|
|
|
6
|
-
> **v6.4.0-beta.
|
|
6
|
+
> **v6.4.0-beta.12** — official release build · [github.com/vexify-org/JSQL-neo](https://github.com/vexify-org/JSQL-neo) · [jsql.vexify.top](https://jsql.vexify.top)
|
|
7
7
|
|
|
8
8
|

|
|
9
9
|

|
package/index.js
CHANGED
package/lib/sql.js
CHANGED
|
@@ -392,6 +392,7 @@ class Parser {
|
|
|
392
392
|
this.pos = 0;
|
|
393
393
|
this.paramCount = 0; // 顺序占位符 ? 的序号(从 0 开始)
|
|
394
394
|
this.subquerySeq = 0; // FROM 子查询无别名时自动生成 __subN
|
|
395
|
+
this.depth = 0; // 子查询递归深度
|
|
395
396
|
}
|
|
396
397
|
|
|
397
398
|
peek(offset = 0) { return this.tokens[this.pos + offset]; }
|
|
@@ -1220,6 +1221,8 @@ class Parser {
|
|
|
1220
1221
|
}
|
|
1221
1222
|
|
|
1222
1223
|
parseSelect() {
|
|
1224
|
+
this.depth++;
|
|
1225
|
+
if (this.depth > 200) throw new Error('Subquery nesting too deep (max 200)');
|
|
1223
1226
|
this.expectKeyword('SELECT');
|
|
1224
1227
|
let distinct = false;
|
|
1225
1228
|
if (this.isKeyword('DISTINCT')) { this.next(); distinct = true; }
|
|
@@ -1390,9 +1393,18 @@ class Parser {
|
|
|
1390
1393
|
if (this.isKeyword('LIMIT')) {
|
|
1391
1394
|
this.next();
|
|
1392
1395
|
limit = this.parseValue();
|
|
1396
|
+
if (typeof limit === 'bigint') limit = Number(limit <= BigInt(Number.MAX_SAFE_INTEGER) ? limit : BigInt(Number.MAX_SAFE_INTEGER));
|
|
1393
1397
|
if (typeof limit === 'number' && limit < 0) throw new Error(`LIMIT must be a non-negative integer, got ${limit}`);
|
|
1394
|
-
if (this.isKeyword('OFFSET')) { this.next(); offset = this.parseValue(); if (typeof offset === 'number' && offset < 0) throw new Error(`OFFSET must be a non-negative integer, got ${offset}`); }
|
|
1395
|
-
else if (this.peek().type === 'op' && this.peek().value === ',') { this.next(); offset = limit; limit = this.parseValue(); if (typeof limit === 'number' && limit < 0) throw new Error(`LIMIT must be a non-negative integer, got ${limit}`); }
|
|
1398
|
+
if (this.isKeyword('OFFSET')) { this.next(); offset = this.parseValue(); if (typeof offset === 'bigint') offset = Number(offset <= BigInt(Number.MAX_SAFE_INTEGER) ? offset : BigInt(Number.MAX_SAFE_INTEGER)); if (typeof offset === 'number' && offset < 0) throw new Error(`OFFSET must be a non-negative integer, got ${offset}`); }
|
|
1399
|
+
else if (this.peek().type === 'op' && this.peek().value === ',') { this.next(); offset = limit; limit = this.parseValue(); if (typeof limit === 'bigint') limit = Number(limit <= BigInt(Number.MAX_SAFE_INTEGER) ? limit : BigInt(Number.MAX_SAFE_INTEGER)); if (typeof limit === 'number' && limit < 0) throw new Error(`LIMIT must be a non-negative integer, got ${limit}`); }
|
|
1400
|
+
}
|
|
1401
|
+
// OFFSET without LIMIT (MySQL compatible)
|
|
1402
|
+
if (limit === null && this.isKeyword('OFFSET')) {
|
|
1403
|
+
this.next();
|
|
1404
|
+
offset = this.parseValue();
|
|
1405
|
+
if (typeof offset === 'bigint') offset = Number(offset <= BigInt(Number.MAX_SAFE_INTEGER) ? offset : BigInt(Number.MAX_SAFE_INTEGER));
|
|
1406
|
+
if (typeof offset === 'number' && offset < 0) throw new Error(`OFFSET must be a non-negative integer, got ${offset}`);
|
|
1407
|
+
limit = Number.MAX_SAFE_INTEGER;
|
|
1396
1408
|
}
|
|
1397
1409
|
// 集合运算:UNION [ALL|DISTINCT] / INTERSECT [ALL|DISTINCT] / EXCEPT [ALL|DISTINCT]
|
|
1398
1410
|
let union = null, intersect = null, except = null;
|
|
@@ -1421,13 +1433,15 @@ class Parser {
|
|
|
1421
1433
|
if (this.isWord('FIRST') || this.isWord('NEXT')) this.next();
|
|
1422
1434
|
else throw new Error(`Expected FIRST or NEXT after FETCH, got '${this.peek().value}'`);
|
|
1423
1435
|
const nTok = this.next();
|
|
1424
|
-
|
|
1436
|
+
let n = nTok.type === 'number' ? nTok.value : Number(nTok.value);
|
|
1437
|
+
if (typeof n === 'bigint') n = Number(n <= BigInt(Number.MAX_SAFE_INTEGER) ? n : BigInt(Number.MAX_SAFE_INTEGER));
|
|
1425
1438
|
if (!Number.isFinite(n)) throw new Error(`Expected row count after FETCH FIRST, got '${nTok.value}'`);
|
|
1426
1439
|
limit = n;
|
|
1427
1440
|
if (this.isWord('ROW') || this.isWord('ROWS')) this.next();
|
|
1428
1441
|
if (this.isWord('ONLY')) this.next();
|
|
1429
1442
|
}
|
|
1430
1443
|
this.optionalTailSemicolon();
|
|
1444
|
+
this.depth--;
|
|
1431
1445
|
|
|
1432
1446
|
return { type: 'select', columns, aggregate, distinct, from, where, groupBy, having, orderBy, limit, offset, union, intersect, except, rollup };
|
|
1433
1447
|
}
|
|
@@ -1534,6 +1548,7 @@ class Parser {
|
|
|
1534
1548
|
}
|
|
1535
1549
|
let where = null;
|
|
1536
1550
|
if (this.isKeyword('WHERE')) { this.next(); where = this.parseExpr(); }
|
|
1551
|
+
else console.warn(`⚠️ UPDATE without WHERE clause — this will modify ALL rows in '${table}'`);
|
|
1537
1552
|
const returning = this.parseOptionalReturning();
|
|
1538
1553
|
this.optionalTailSemicolon();
|
|
1539
1554
|
return { type: 'update', table, assignments, where, returning };
|
|
@@ -1562,6 +1577,7 @@ class Parser {
|
|
|
1562
1577
|
if (this.isKeyword('LIMIT')) {
|
|
1563
1578
|
this.next();
|
|
1564
1579
|
limit = this.parseValue();
|
|
1580
|
+
if (typeof limit === 'bigint') limit = Number(limit <= BigInt(Number.MAX_SAFE_INTEGER) ? limit : BigInt(Number.MAX_SAFE_INTEGER));
|
|
1565
1581
|
if (typeof limit === 'number' && limit < 0) throw new Error(`LIMIT must be a non-negative integer, got ${limit}`);
|
|
1566
1582
|
}
|
|
1567
1583
|
const returning = this.parseOptionalReturning();
|
package/package.json
CHANGED