@rex0220/kintone-sql-tools 3.2.0 → 3.4.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 +5 -4
- package/dist-cli/ksql.js +1236 -215
- package/dist-mcp/ksql-mcp.js +1239 -218
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-cli/ksql.js
CHANGED
|
@@ -122,6 +122,9 @@ var KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
122
122
|
["SUBSTR", "SUBSTR" /* SUBSTR */],
|
|
123
123
|
["CONCAT", "CONCAT" /* CONCAT */],
|
|
124
124
|
["REPLACE", "REPLACE" /* REPLACE */],
|
|
125
|
+
["REGEXP_LIKE", "REGEXP_LIKE" /* REGEXP_LIKE */],
|
|
126
|
+
["REGEXP_REPLACE", "REGEXP_REPLACE" /* REGEXP_REPLACE */],
|
|
127
|
+
["REGEXP_SUBSTR", "REGEXP_SUBSTR" /* REGEXP_SUBSTR */],
|
|
125
128
|
["COALESCE", "COALESCE" /* COALESCE */],
|
|
126
129
|
["NULLIF", "NULLIF" /* NULLIF */],
|
|
127
130
|
["ISNULL", "ISNULL" /* ISNULL */],
|
|
@@ -254,7 +257,7 @@ var Lexer = class {
|
|
|
254
257
|
);
|
|
255
258
|
}
|
|
256
259
|
// ----------------------------------------------------------
|
|
257
|
-
// 数値:
|
|
260
|
+
// 数値: digits[.digits][e[+-]digits](先頭/末尾 dot は受理しない)
|
|
258
261
|
// ----------------------------------------------------------
|
|
259
262
|
readNumber(start) {
|
|
260
263
|
while (this.pos < this.input.length && isDigit(this.input[this.pos])) {
|
|
@@ -266,6 +269,16 @@ var Lexer = class {
|
|
|
266
269
|
this.pos++;
|
|
267
270
|
}
|
|
268
271
|
}
|
|
272
|
+
if (this.pos < this.input.length && (this.input[this.pos] === "e" || this.input[this.pos] === "E")) {
|
|
273
|
+
this.pos++;
|
|
274
|
+
if (this.pos < this.input.length && (this.input[this.pos] === "+" || this.input[this.pos] === "-")) {
|
|
275
|
+
this.pos++;
|
|
276
|
+
}
|
|
277
|
+
if (this.pos >= this.input.length || !isDigit(this.input[this.pos])) {
|
|
278
|
+
throw new LexError("\u6307\u6570\u90E8\u306B\u306F\u6570\u5B57\u304C\u5FC5\u8981\u3067\u3059", start, this.input, this.pos >= this.input.length);
|
|
279
|
+
}
|
|
280
|
+
while (this.pos < this.input.length && isDigit(this.input[this.pos])) this.pos++;
|
|
281
|
+
}
|
|
269
282
|
return this.makeToken(
|
|
270
283
|
"NUMBER" /* NUMBER */,
|
|
271
284
|
this.input.slice(start, this.pos),
|
|
@@ -294,6 +307,10 @@ var Lexer = class {
|
|
|
294
307
|
this.pos += 2;
|
|
295
308
|
return this.makeToken("<=" /* LTE */, "<=", start);
|
|
296
309
|
}
|
|
310
|
+
if (ch === "|" && ch2 === "|") {
|
|
311
|
+
this.pos += 2;
|
|
312
|
+
return this.makeToken("||" /* CONCAT_OP */, "||", start);
|
|
313
|
+
}
|
|
297
314
|
switch (ch) {
|
|
298
315
|
case "=":
|
|
299
316
|
this.pos++;
|
|
@@ -465,8 +482,94 @@ function isJapanese(cp) {
|
|
|
465
482
|
return cp >= 12352 && cp <= 12543 || cp >= 13312 && cp <= 40959 || cp >= 63744 && cp <= 64255 || cp >= 65281 && cp <= 65376;
|
|
466
483
|
}
|
|
467
484
|
|
|
485
|
+
// src/core/exactDecimal.ts
|
|
486
|
+
var DECIMAL_PATTERN = /^([+-]?)(?:(\d+)(?:\.(\d*))?|\.(\d+))(?:[eE]([+-]?)(\d+))?$/;
|
|
487
|
+
function parseSafeExponent(sign, digits) {
|
|
488
|
+
if (digits === void 0) return 0;
|
|
489
|
+
let value = 0;
|
|
490
|
+
for (const digit of digits) {
|
|
491
|
+
value = value * 10 + (digit.charCodeAt(0) - 48);
|
|
492
|
+
if (!Number.isSafeInteger(value)) return null;
|
|
493
|
+
}
|
|
494
|
+
return sign === "-" ? -value : value;
|
|
495
|
+
}
|
|
496
|
+
function parseExactDecimal(input) {
|
|
497
|
+
const match = DECIMAL_PATTERN.exec(input.trim());
|
|
498
|
+
if (match === null) return null;
|
|
499
|
+
const exponent = parseSafeExponent(match[5], match[6]);
|
|
500
|
+
if (exponent === null) return null;
|
|
501
|
+
const fraction = match[3] ?? match[4] ?? "";
|
|
502
|
+
let coefficient = `${match[2] ?? ""}${fraction}`.replace(/^0+/, "");
|
|
503
|
+
if (coefficient === "") return { sign: 0, coefficient: "0", scale: 0 };
|
|
504
|
+
let scale = fraction.length - exponent;
|
|
505
|
+
if (!Number.isSafeInteger(scale)) return null;
|
|
506
|
+
const trailingZeros = /0+$/.exec(coefficient)?.[0].length ?? 0;
|
|
507
|
+
if (trailingZeros > 0) {
|
|
508
|
+
coefficient = coefficient.slice(0, -trailingZeros);
|
|
509
|
+
scale -= trailingZeros;
|
|
510
|
+
if (!Number.isSafeInteger(scale)) return null;
|
|
511
|
+
}
|
|
512
|
+
if (!Number.isSafeInteger(coefficient.length - scale)) return null;
|
|
513
|
+
const sign = match[1] === "-" ? -1 : 1;
|
|
514
|
+
return { sign, coefficient, scale };
|
|
515
|
+
}
|
|
516
|
+
function formatPlainDecimal(dec) {
|
|
517
|
+
if (dec.sign === 0) return "0";
|
|
518
|
+
const digits = dec.coefficient;
|
|
519
|
+
let magnitude;
|
|
520
|
+
if (dec.scale <= 0) {
|
|
521
|
+
magnitude = `${digits}${"0".repeat(-dec.scale)}`;
|
|
522
|
+
} else if (digits.length > dec.scale) {
|
|
523
|
+
const point = digits.length - dec.scale;
|
|
524
|
+
magnitude = `${digits.slice(0, point)}.${digits.slice(point)}`;
|
|
525
|
+
} else {
|
|
526
|
+
magnitude = `0.${"0".repeat(dec.scale - digits.length)}${digits}`;
|
|
527
|
+
}
|
|
528
|
+
return dec.sign === -1 ? `-${magnitude}` : magnitude;
|
|
529
|
+
}
|
|
530
|
+
function toPlainDecimal(input) {
|
|
531
|
+
const dec = parseExactDecimal(input);
|
|
532
|
+
return dec === null ? null : formatPlainDecimal(dec);
|
|
533
|
+
}
|
|
534
|
+
function compareMagnitudes(left, right) {
|
|
535
|
+
const leftPoint = left.coefficient.length - left.scale;
|
|
536
|
+
const rightPoint = right.coefficient.length - right.scale;
|
|
537
|
+
if (!Number.isSafeInteger(leftPoint) || !Number.isSafeInteger(rightPoint)) {
|
|
538
|
+
throw new Error("ArgumentError: exact decimal scale is outside the supported range.");
|
|
539
|
+
}
|
|
540
|
+
if (leftPoint !== rightPoint) return leftPoint < rightPoint ? -1 : 1;
|
|
541
|
+
const width = Math.max(left.coefficient.length, right.coefficient.length);
|
|
542
|
+
for (let index = 0; index < width; index++) {
|
|
543
|
+
const a = index < left.coefficient.length ? left.coefficient.charCodeAt(index) : 48;
|
|
544
|
+
const b = index < right.coefficient.length ? right.coefficient.charCodeAt(index) : 48;
|
|
545
|
+
if (a !== b) return a < b ? -1 : 1;
|
|
546
|
+
}
|
|
547
|
+
return 0;
|
|
548
|
+
}
|
|
549
|
+
function compareExactDecimal(left, right) {
|
|
550
|
+
if (left.sign !== right.sign) return left.sign < right.sign ? -1 : 1;
|
|
551
|
+
if (left.sign === 0) return 0;
|
|
552
|
+
const magnitude = compareMagnitudes(left, right);
|
|
553
|
+
return left.sign === -1 ? magnitude === 0 ? 0 : magnitude === -1 ? 1 : -1 : magnitude;
|
|
554
|
+
}
|
|
555
|
+
function compareDecimal(left, right) {
|
|
556
|
+
const a = parseExactDecimal(left);
|
|
557
|
+
const b = parseExactDecimal(right);
|
|
558
|
+
if (a === null || b === null) {
|
|
559
|
+
throw new Error("ArgumentError: compareDecimal requires finite decimal inputs.");
|
|
560
|
+
}
|
|
561
|
+
return compareExactDecimal(a, b);
|
|
562
|
+
}
|
|
563
|
+
|
|
468
564
|
// src/types/ast.ts
|
|
469
565
|
var NO_FROM_CTE_NAME = "__NO_FROM__";
|
|
566
|
+
function makeNumberLiteral(raw) {
|
|
567
|
+
return { type: "NUMBER", value: Number(raw), raw };
|
|
568
|
+
}
|
|
569
|
+
function numberLiteralText(node) {
|
|
570
|
+
const source = node.raw ?? String(node.value);
|
|
571
|
+
return toPlainDecimal(source) ?? source;
|
|
572
|
+
}
|
|
470
573
|
|
|
471
574
|
// src/parser/parser.ts
|
|
472
575
|
var MAX_BATCH_STATEMENTS = 20;
|
|
@@ -499,6 +602,9 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
|
|
|
499
602
|
"COALESCE" /* COALESCE */,
|
|
500
603
|
"NULLIF" /* NULLIF */,
|
|
501
604
|
"ISNULL" /* ISNULL */,
|
|
605
|
+
"REGEXP_LIKE" /* REGEXP_LIKE */,
|
|
606
|
+
"REGEXP_REPLACE" /* REGEXP_REPLACE */,
|
|
607
|
+
"REGEXP_SUBSTR" /* REGEXP_SUBSTR */,
|
|
502
608
|
"LEFT" /* LEFT */,
|
|
503
609
|
"RIGHT" /* RIGHT */,
|
|
504
610
|
"INSTR" /* INSTR */,
|
|
@@ -548,6 +654,8 @@ var Parser = class {
|
|
|
548
654
|
constructor(tokens) {
|
|
549
655
|
this.tokens = tokens;
|
|
550
656
|
this.allowUnaryPlusNumber = false;
|
|
657
|
+
this.scalarAllowsAggregateArgs = true;
|
|
658
|
+
this.scalarAllowsCase = true;
|
|
551
659
|
this.pos = 0;
|
|
552
660
|
/** WITH 句で定義された CTE 名のセット(parseTableRef で参照) */
|
|
553
661
|
this.cteNames = /* @__PURE__ */ new Set();
|
|
@@ -716,17 +824,19 @@ var Parser = class {
|
|
|
716
824
|
}
|
|
717
825
|
rejectNonScalarExpr(node, tok, context) {
|
|
718
826
|
if (node.type === "STRING" || node.type === "NUMBER") return;
|
|
719
|
-
if (node.type === "FIELD_REF" || node.type === "AGG_REF") {
|
|
827
|
+
if (node.type === "FIELD_REF" || node.type === "FIELD" || node.type === "VARIABLE" || node.type === "AGG_REF") {
|
|
720
828
|
throw new ParseError(`${context} \u306E\u53F3\u8FBA\u3067\u306F\u30D5\u30A3\u30FC\u30EB\u30C9\u53C2\u7167\u30FB\u96C6\u8A08\u95A2\u6570\u3092\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`, tok);
|
|
721
829
|
}
|
|
722
|
-
if (node.type === "ARITH" || node.type === "AGG_ARITH") {
|
|
830
|
+
if (node.type === "ARITH" || node.type === "SCALAR_ARITH" || node.type === "CONCAT_OP" || node.type === "AGG_ARITH") {
|
|
723
831
|
this.rejectNonScalarExpr(node.left, tok, context);
|
|
724
832
|
this.rejectNonScalarExpr(node.right, tok, context);
|
|
725
833
|
return;
|
|
726
834
|
}
|
|
727
835
|
if (node.type === "STRING_FUNC") {
|
|
728
836
|
for (const arg of node.args) this.rejectNonScalarExpr(arg, tok, context);
|
|
837
|
+
return;
|
|
729
838
|
}
|
|
839
|
+
throw new ParseError(`${context} \u306E\u53F3\u8FBA\u3067\u306F CASE \u3092\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`, tok);
|
|
730
840
|
}
|
|
731
841
|
// ----------------------------------------------------------
|
|
732
842
|
// CREATE TEMP TABLE / DROP TEMP TABLE(バッチ内一時テーブル)
|
|
@@ -1097,6 +1207,11 @@ var Parser = class {
|
|
|
1097
1207
|
if (this.consume("*" /* STAR */)) {
|
|
1098
1208
|
return { type: "WILDCARD" };
|
|
1099
1209
|
}
|
|
1210
|
+
if (this.hasTopLevelTokenBeforeValueEnd("||" /* CONCAT_OP */)) {
|
|
1211
|
+
const expr = this.parseScalarValueExpr({ allowAggregateArgs: true });
|
|
1212
|
+
const alias2 = this.consume("AS" /* AS */) ? this.parseAliasName() : null;
|
|
1213
|
+
return { type: "SCALAR_VALUE_COL", expr, alias: alias2 };
|
|
1214
|
+
}
|
|
1100
1215
|
const windowFunc = this.tryWindowFunc();
|
|
1101
1216
|
if (windowFunc !== null) {
|
|
1102
1217
|
return this.parseWindowColumn(windowFunc);
|
|
@@ -1212,13 +1327,25 @@ var Parser = class {
|
|
|
1212
1327
|
}
|
|
1213
1328
|
selectColumnHasAggregate(column) {
|
|
1214
1329
|
if (column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL") return true;
|
|
1215
|
-
if (column.type
|
|
1216
|
-
|
|
1330
|
+
if (column.type === "STRFUNC_COL") return column.expr.args.some((arg) => this.stringFuncArgHasAggregate(arg));
|
|
1331
|
+
if (column.type === "SCALAR_VALUE_COL") return this.scalarValueHasAggregate(column.expr);
|
|
1332
|
+
return false;
|
|
1217
1333
|
}
|
|
1218
1334
|
stringFuncArgHasAggregate(arg) {
|
|
1219
1335
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return true;
|
|
1220
|
-
|
|
1221
|
-
|
|
1336
|
+
return this.scalarValueHasAggregate(arg);
|
|
1337
|
+
}
|
|
1338
|
+
scalarValueHasAggregate(expr) {
|
|
1339
|
+
if (expr.type === "STRING_FUNC") return expr.args.some((arg) => this.stringFuncArgHasAggregate(arg));
|
|
1340
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
1341
|
+
return this.scalarValueHasAggregate(expr.left) || this.scalarValueHasAggregate(expr.right);
|
|
1342
|
+
}
|
|
1343
|
+
if (expr.type === "CASE_WHEN") {
|
|
1344
|
+
const results = [...expr.branches.map((b) => b.result), ...expr.elseResult ? [expr.elseResult] : []];
|
|
1345
|
+
return results.some((result) => {
|
|
1346
|
+
if (result.type === "ARRAY" || result.type === "FIELD_REF" || result.type === "ARITH") return false;
|
|
1347
|
+
return this.scalarValueHasAggregate(result);
|
|
1348
|
+
});
|
|
1222
1349
|
}
|
|
1223
1350
|
return false;
|
|
1224
1351
|
}
|
|
@@ -1262,12 +1389,12 @@ var Parser = class {
|
|
|
1262
1389
|
if (this.peek().kind === "-" /* MINUS */) {
|
|
1263
1390
|
this.advance();
|
|
1264
1391
|
const operand = this.parseAggPrimary();
|
|
1265
|
-
if (operand.type === "NUMBER") return {
|
|
1266
|
-
return { type: "AGG_ARITH", left:
|
|
1392
|
+
if (operand.type === "NUMBER") return makeNumberLiteral(`-${numberLiteralText(operand)}`);
|
|
1393
|
+
return { type: "AGG_ARITH", left: makeNumberLiteral("0"), op: "-", right: operand };
|
|
1267
1394
|
}
|
|
1268
1395
|
if (this.peek().kind === "NUMBER" /* NUMBER */) {
|
|
1269
1396
|
const tok = this.advance();
|
|
1270
|
-
return
|
|
1397
|
+
return makeNumberLiteral(tok.value);
|
|
1271
1398
|
}
|
|
1272
1399
|
const aggFunc = this.tryAggregateFunc();
|
|
1273
1400
|
if (aggFunc !== null) {
|
|
@@ -1276,6 +1403,105 @@ var Parser = class {
|
|
|
1276
1403
|
throw new ParseError("\u96C6\u8A08\u7B97\u8853\u5F0F\u306B\u306F\u96C6\u8A08\u95A2\u6570\u307E\u305F\u306F\u6570\u5024\u304C\u5FC5\u8981\u3067\u3059", this.peek());
|
|
1277
1404
|
}
|
|
1278
1405
|
// ──────────────────────────────────────────────────
|
|
1406
|
+
// 汎用スカラー値式パーサー(B38)
|
|
1407
|
+
// ──────────────────────────────────────────────────
|
|
1408
|
+
/** 比較・述語・集約・サブクエリを含まない値式の公開入口。 */
|
|
1409
|
+
parseScalarValueExpr(options = {}) {
|
|
1410
|
+
const previousAggregateArgs = this.scalarAllowsAggregateArgs;
|
|
1411
|
+
const previousCase = this.scalarAllowsCase;
|
|
1412
|
+
this.scalarAllowsAggregateArgs = options.allowAggregateArgs === true;
|
|
1413
|
+
this.scalarAllowsCase = options.allowCase !== false;
|
|
1414
|
+
let expr;
|
|
1415
|
+
try {
|
|
1416
|
+
expr = this.parseScalarAddSubConcat(this.scalarAllowsCase);
|
|
1417
|
+
} finally {
|
|
1418
|
+
this.scalarAllowsAggregateArgs = previousAggregateArgs;
|
|
1419
|
+
this.scalarAllowsCase = previousCase;
|
|
1420
|
+
}
|
|
1421
|
+
const next = this.peek();
|
|
1422
|
+
if (next.kind === "IS" /* IS */ || next.kind === "=" /* EQ */ || next.kind === "!=" /* NEQ */ || next.kind === "<>" /* LT_GT */ || next.kind === ">" /* GT */ || next.kind === "<" /* LT */ || next.kind === ">=" /* GTE */ || next.kind === "<=" /* LTE */ || next.kind === "LIKE" /* LIKE */ || next.kind === "KLIKE" /* KLIKE */ || next.kind === "IN" /* IN */ || next.kind === "BETWEEN" /* BETWEEN */) throw new ParseError("\u30B9\u30AB\u30E9\u30FC\u5024\u5F0F\u306B\u6BD4\u8F03\u30FB\u8FF0\u8A9E\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", next);
|
|
1423
|
+
return expr;
|
|
1424
|
+
}
|
|
1425
|
+
parseScalarAddSubConcat(allowCase) {
|
|
1426
|
+
let left = this.parseScalarMulDiv(allowCase);
|
|
1427
|
+
while (this.peek().kind === "+" /* PLUS */ || this.peek().kind === "-" /* MINUS */ || this.peek().kind === "||" /* CONCAT_OP */) {
|
|
1428
|
+
const token = this.advance();
|
|
1429
|
+
const right = this.parseScalarMulDiv(allowCase);
|
|
1430
|
+
left = token.kind === "||" /* CONCAT_OP */ ? { type: "CONCAT_OP", left, right } : { type: "SCALAR_ARITH", left, op: token.kind === "+" /* PLUS */ ? "+" : "-", right };
|
|
1431
|
+
}
|
|
1432
|
+
return left;
|
|
1433
|
+
}
|
|
1434
|
+
parseScalarMulDiv(allowCase) {
|
|
1435
|
+
let left = this.parseScalarPrimary(allowCase);
|
|
1436
|
+
while (this.peek().kind === "*" /* STAR */ || this.peek().kind === "/" /* SLASH */ || this.peek().kind === "%" /* PERCENT */) {
|
|
1437
|
+
const token = this.advance();
|
|
1438
|
+
const op = token.kind === "*" /* STAR */ ? "*" : token.kind === "/" /* SLASH */ ? "/" : "%";
|
|
1439
|
+
left = { type: "SCALAR_ARITH", left, op, right: this.parseScalarPrimary(allowCase) };
|
|
1440
|
+
}
|
|
1441
|
+
return left;
|
|
1442
|
+
}
|
|
1443
|
+
parseScalarPrimary(allowCase) {
|
|
1444
|
+
const tok = this.peek();
|
|
1445
|
+
if (tok.kind === "(" /* LPAREN */) {
|
|
1446
|
+
if (this.peekAt(1).kind === "SELECT" /* SELECT */) throw new ParseError("\u30B9\u30AB\u30E9\u30FC\u5024\u5F0F\u306B\u30B5\u30D6\u30AF\u30A8\u30EA\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", tok);
|
|
1447
|
+
this.advance();
|
|
1448
|
+
const expr = this.parseScalarAddSubConcat(allowCase);
|
|
1449
|
+
this.expect(")" /* RPAREN */);
|
|
1450
|
+
return expr;
|
|
1451
|
+
}
|
|
1452
|
+
if (tok.kind === "+" /* PLUS */ || tok.kind === "-" /* MINUS */) {
|
|
1453
|
+
this.advance();
|
|
1454
|
+
if (this.peek().kind === "+" /* PLUS */ || this.peek().kind === "-" /* MINUS */) {
|
|
1455
|
+
throw new ParseError("\u5358\u9805\u7B26\u53F7\u3092\u91CD\u306D\u3066\u6307\u5B9A\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093", this.peek());
|
|
1456
|
+
}
|
|
1457
|
+
const operand = this.parseScalarPrimary(allowCase);
|
|
1458
|
+
if (operand.type === "NUMBER") {
|
|
1459
|
+
return makeNumberLiteral(`${tok.kind === "-" /* MINUS */ ? "-" : "+"}${numberLiteralText(operand)}`);
|
|
1460
|
+
}
|
|
1461
|
+
if (tok.kind === "+" /* PLUS */) throw new ParseError("\u5358\u9805 + \u306E\u76F4\u5F8C\u306B\u306F\u6570\u5024\u30EA\u30C6\u30E9\u30EB\u304C\u5FC5\u8981\u3067\u3059", tok);
|
|
1462
|
+
return { type: "SCALAR_ARITH", left: makeNumberLiteral("0"), op: "-", right: operand };
|
|
1463
|
+
}
|
|
1464
|
+
if (tok.kind === "STRING" /* STRING */) {
|
|
1465
|
+
this.advance();
|
|
1466
|
+
return { type: "STRING", value: tok.value };
|
|
1467
|
+
}
|
|
1468
|
+
if (tok.kind === "NUMBER" /* NUMBER */) {
|
|
1469
|
+
this.advance();
|
|
1470
|
+
return makeNumberLiteral(tok.value);
|
|
1471
|
+
}
|
|
1472
|
+
if (tok.kind === "VARIABLE" /* VARIABLE */) {
|
|
1473
|
+
this.advance();
|
|
1474
|
+
return { type: "VARIABLE", name: tok.value.slice(1).toLowerCase() };
|
|
1475
|
+
}
|
|
1476
|
+
if (tok.kind === "CASE" /* CASE */) {
|
|
1477
|
+
if (!allowCase) throw new ParseError("\u3053\u306E\u30B9\u30AB\u30E9\u30FC\u5024\u5F0F\u3067\u306F CASE \u3092\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", tok);
|
|
1478
|
+
return this.parseCaseWhenExpr();
|
|
1479
|
+
}
|
|
1480
|
+
if (this.tryAggregateFunc() !== null) throw new ParseError("\u30B9\u30AB\u30E9\u30FC\u5024\u5F0F\u306B\u96C6\u7D04\u95A2\u6570\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", tok);
|
|
1481
|
+
if (this.tryStringFuncName() !== null) return this.parseStringFuncExpr();
|
|
1482
|
+
if (tok.kind === "IDENT" /* IDENT */ || tok.kind === "BIDENT" /* BIDENT */) {
|
|
1483
|
+
this.advance();
|
|
1484
|
+
if (this.consume("." /* DOT */)) return { type: "FIELD", tableAlias: tok.value, field: this.parseIdentifier() };
|
|
1485
|
+
return { type: "FIELD", tableAlias: null, field: tok.value };
|
|
1486
|
+
}
|
|
1487
|
+
throw new ParseError("\u30B9\u30AB\u30E9\u30FC\u5024\u5F0F\u306E\u30AA\u30DA\u30E9\u30F3\u30C9\u304C\u5FC5\u8981\u3067\u3059", tok);
|
|
1488
|
+
}
|
|
1489
|
+
/** 現在の値の終端までに指定トークンがあるか(括弧内も対象)。 */
|
|
1490
|
+
hasTopLevelTokenBeforeValueEnd(target) {
|
|
1491
|
+
let depth = 0;
|
|
1492
|
+
for (let i = this.pos; i < this.tokens.length; i++) {
|
|
1493
|
+
const kind = this.tokens[i].kind;
|
|
1494
|
+
if (kind === "(" /* LPAREN */ || kind === "[" /* LBRACKET */) depth++;
|
|
1495
|
+
else if (kind === ")" /* RPAREN */ || kind === "]" /* RBRACKET */) {
|
|
1496
|
+
if (depth === 0) break;
|
|
1497
|
+
depth--;
|
|
1498
|
+
}
|
|
1499
|
+
if (kind === target) return true;
|
|
1500
|
+
if (depth === 0 && (kind === "," /* COMMA */ || kind === "AS" /* AS */ || kind === "FROM" /* FROM */ || kind === "WHERE" /* WHERE */ || kind === "WHEN" /* WHEN */ || kind === "THEN" /* THEN */ || kind === "ELSE" /* ELSE */ || kind === "END" /* END */ || kind === ";" /* SEMICOLON */ || kind === "EOF" /* EOF */)) break;
|
|
1501
|
+
}
|
|
1502
|
+
return false;
|
|
1503
|
+
}
|
|
1504
|
+
// ──────────────────────────────────────────────────
|
|
1279
1505
|
// 算術式パーサー(演算子優先順位: * / > + -)
|
|
1280
1506
|
//
|
|
1281
1507
|
// parseArithAddSub : + -(左結合・低優先度)
|
|
@@ -1319,7 +1545,7 @@ var Parser = class {
|
|
|
1319
1545
|
if (this.allowUnaryPlusNumber && this.peek().kind === "+" /* PLUS */) {
|
|
1320
1546
|
this.advance();
|
|
1321
1547
|
const number = this.expect("NUMBER" /* NUMBER */, "\u5358\u9805 + \u306E\u76F4\u5F8C\u306B\u306F\u6570\u5024\u30EA\u30C6\u30E9\u30EB\u304C\u5FC5\u8981\u3067\u3059");
|
|
1322
|
-
return {
|
|
1548
|
+
return makeNumberLiteral(`+${number.value}`);
|
|
1323
1549
|
}
|
|
1324
1550
|
if (this.peek().kind === "-" /* MINUS */) {
|
|
1325
1551
|
this.advance();
|
|
@@ -1327,8 +1553,8 @@ var Parser = class {
|
|
|
1327
1553
|
throw new ParseError("\u5358\u9805\u7B26\u53F7\u3092\u91CD\u306D\u3066\u6307\u5B9A\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093", this.peek());
|
|
1328
1554
|
}
|
|
1329
1555
|
const operand = this.parseArithPrimary();
|
|
1330
|
-
if (operand.type === "NUMBER") return {
|
|
1331
|
-
return { type: "ARITH", left:
|
|
1556
|
+
if (operand.type === "NUMBER") return makeNumberLiteral(`-${numberLiteralText(operand)}`);
|
|
1557
|
+
return { type: "ARITH", left: makeNumberLiteral("0"), op: "-", right: operand };
|
|
1332
1558
|
}
|
|
1333
1559
|
if (this.tryStringFuncName() !== null) {
|
|
1334
1560
|
return this.parseStringFuncExpr();
|
|
@@ -1336,7 +1562,7 @@ var Parser = class {
|
|
|
1336
1562
|
const tok = this.peek();
|
|
1337
1563
|
if (tok.kind === "NUMBER" /* NUMBER */) {
|
|
1338
1564
|
this.advance();
|
|
1339
|
-
return
|
|
1565
|
+
return makeNumberLiteral(tok.value);
|
|
1340
1566
|
}
|
|
1341
1567
|
if (tok.kind === "IDENT" /* IDENT */ || tok.kind === "BIDENT" /* BIDENT */) {
|
|
1342
1568
|
this.advance();
|
|
@@ -1397,12 +1623,15 @@ var Parser = class {
|
|
|
1397
1623
|
this.expect("END" /* END */);
|
|
1398
1624
|
return { type: "CASE_WHEN", branches, elseResult };
|
|
1399
1625
|
}
|
|
1400
|
-
/** THEN / ELSE
|
|
1626
|
+
/** THEN / ELSE の結果値。`||` を含む場合だけ新スカラー文法へ渡す。 */
|
|
1401
1627
|
parseCaseResult() {
|
|
1402
1628
|
const tok = this.peek();
|
|
1403
1629
|
if (tok.kind === "[" /* LBRACKET */) {
|
|
1404
1630
|
return this.parseArrayLiteral();
|
|
1405
1631
|
}
|
|
1632
|
+
if (this.hasTopLevelTokenBeforeValueEnd("||" /* CONCAT_OP */)) {
|
|
1633
|
+
return this.parseScalarValueExpr({ allowAggregateArgs: true });
|
|
1634
|
+
}
|
|
1406
1635
|
if (tok.kind === "STRING" /* STRING */) {
|
|
1407
1636
|
this.advance();
|
|
1408
1637
|
return { type: "STRING", value: tok.value };
|
|
@@ -1440,6 +1669,9 @@ var Parser = class {
|
|
|
1440
1669
|
["SUBSTR" /* SUBSTR */]: "SUBSTRING",
|
|
1441
1670
|
["CONCAT" /* CONCAT */]: "CONCAT",
|
|
1442
1671
|
["REPLACE" /* REPLACE */]: "REPLACE",
|
|
1672
|
+
["REGEXP_LIKE" /* REGEXP_LIKE */]: "REGEXP_LIKE",
|
|
1673
|
+
["REGEXP_REPLACE" /* REGEXP_REPLACE */]: "REGEXP_REPLACE",
|
|
1674
|
+
["REGEXP_SUBSTR" /* REGEXP_SUBSTR */]: "REGEXP_SUBSTR",
|
|
1443
1675
|
["TRANSLATE" /* TRANSLATE */]: "TRANSLATE",
|
|
1444
1676
|
["COALESCE" /* COALESCE */]: "COALESCE",
|
|
1445
1677
|
["NULLIF" /* NULLIF */]: "NULLIF",
|
|
@@ -1547,25 +1779,19 @@ var Parser = class {
|
|
|
1547
1779
|
}
|
|
1548
1780
|
return { type: "STRING", value: normalized };
|
|
1549
1781
|
}
|
|
1550
|
-
/** 文字列関数の引数:
|
|
1782
|
+
/** 文字列関数の引数: ScalarValueExpr / 集計算術式 */
|
|
1551
1783
|
parseStringFuncArg() {
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
try {
|
|
1562
|
-
const left = this.parseAggPrimary();
|
|
1563
|
-
const expr = this.continueAggArith(left);
|
|
1564
|
-
if (this.hasAggregateOperand(expr)) return expr;
|
|
1565
|
-
} catch {
|
|
1784
|
+
if (this.scalarAllowsAggregateArgs) {
|
|
1785
|
+
const startPos = this.pos;
|
|
1786
|
+
try {
|
|
1787
|
+
const left = this.parseAggPrimary();
|
|
1788
|
+
const expr = this.continueAggArith(left);
|
|
1789
|
+
if (this.hasAggregateOperand(expr)) return expr;
|
|
1790
|
+
} catch {
|
|
1791
|
+
}
|
|
1792
|
+
this.pos = startPos;
|
|
1566
1793
|
}
|
|
1567
|
-
this.
|
|
1568
|
-
return this.parseArithAddSub();
|
|
1794
|
+
return this.parseScalarAddSubConcat(this.scalarAllowsCase);
|
|
1569
1795
|
}
|
|
1570
1796
|
hasAggregateOperand(node) {
|
|
1571
1797
|
if (node.type === "AGG_REF") return true;
|
|
@@ -1653,6 +1879,7 @@ var Parser = class {
|
|
|
1653
1879
|
const k = this.peek().kind;
|
|
1654
1880
|
if (k === "IDENT" /* IDENT */ || k === "BIDENT" /* BIDENT */) {
|
|
1655
1881
|
if (k === "IDENT" /* IDENT */ && this.peek().value.toUpperCase() === "VALIDATE" && this.peekAt(1).kind === "IDENT" /* IDENT */ && this.peekAt(1).value.toUpperCase() === "ONLY") return null;
|
|
1882
|
+
if (k === "IDENT" /* IDENT */ && this.peek().value.toUpperCase() === "CHECK" && this.peekAt(1).kind === "WHEN" /* WHEN */) return null;
|
|
1656
1883
|
return this.parseTableAliasName();
|
|
1657
1884
|
}
|
|
1658
1885
|
return null;
|
|
@@ -1979,7 +2206,7 @@ var Parser = class {
|
|
|
1979
2206
|
}
|
|
1980
2207
|
if (tok.kind === "NUMBER" /* NUMBER */ || tok.kind === "IDENT" /* IDENT */ || tok.kind === "BIDENT" /* BIDENT */ || tok.kind === "(" /* LPAREN */ || tok.kind === "-" /* MINUS */ || this.tryStringFuncName() !== null) {
|
|
1981
2208
|
const expr = this.parseArithAddSub();
|
|
1982
|
-
if (expr.type === "NUMBER") return
|
|
2209
|
+
if (expr.type === "NUMBER") return expr;
|
|
1983
2210
|
return { type: "ARITH_VALUE", expr };
|
|
1984
2211
|
}
|
|
1985
2212
|
throw new ParseError(
|
|
@@ -2006,15 +2233,15 @@ var Parser = class {
|
|
|
2006
2233
|
if (tok.kind === "STRING" /* STRING */) {
|
|
2007
2234
|
values.push({ type: "STRING", value: tok.value });
|
|
2008
2235
|
} else if (tok.kind === "NUMBER" /* NUMBER */) {
|
|
2009
|
-
values.push(
|
|
2236
|
+
values.push(makeNumberLiteral(tok.value));
|
|
2010
2237
|
} else if (tok.kind === "-" /* MINUS */ || tok.kind === "+" /* PLUS */) {
|
|
2011
2238
|
const number = this.peek();
|
|
2012
2239
|
if (number.kind !== "NUMBER" /* NUMBER */) {
|
|
2013
2240
|
throw new ParseError(invalidValueMessage, tok);
|
|
2014
2241
|
}
|
|
2015
2242
|
this.advance();
|
|
2016
|
-
const sign = tok.kind === "-" /* MINUS */ ? -
|
|
2017
|
-
values.push({
|
|
2243
|
+
const sign = tok.kind === "-" /* MINUS */ ? "-" : "+";
|
|
2244
|
+
values.push(makeNumberLiteral(`${sign}${number.value}`));
|
|
2018
2245
|
} else if (tok.kind === "VARIABLE" /* VARIABLE */) {
|
|
2019
2246
|
values.push({ type: "VARIABLE", name: tok.value.slice(1).toLowerCase() });
|
|
2020
2247
|
} else {
|
|
@@ -2118,8 +2345,9 @@ var Parser = class {
|
|
|
2118
2345
|
if (subtableCode) {
|
|
2119
2346
|
throw new ParseError("INSERT INTO ... SELECT \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB\u4EEE\u60F3\u30C6\u30FC\u30D6\u30EB\u3067\u306F\u672A\u5BFE\u5FDC\u3067\u3059", this.prev());
|
|
2120
2347
|
}
|
|
2348
|
+
const checkGroups2 = this.parseCheckGroups();
|
|
2121
2349
|
const validation2 = this.parseDmlControlSuffix();
|
|
2122
|
-
return { type: "INSERT_SELECT", appId, fields, select, ...validation2 };
|
|
2350
|
+
return { type: "INSERT_SELECT", appId, fields, select, ...checkGroups2, ...validation2 };
|
|
2123
2351
|
}
|
|
2124
2352
|
this.expect("VALUES" /* VALUES */);
|
|
2125
2353
|
const values = [];
|
|
@@ -2129,11 +2357,15 @@ var Parser = class {
|
|
|
2129
2357
|
this.expect(")" /* RPAREN */);
|
|
2130
2358
|
values.push(row);
|
|
2131
2359
|
} while (this.consume("," /* COMMA */));
|
|
2360
|
+
const checkGroups = this.parseCheckGroups();
|
|
2132
2361
|
const validation = this.parseDmlControlSuffix();
|
|
2362
|
+
if (subtableCode && checkGroups.checkGroups) {
|
|
2363
|
+
throw new ParseError("CHECK \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB INSERT \u306B\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u305B\u3093", this.prev());
|
|
2364
|
+
}
|
|
2133
2365
|
if (subtableCode && (validation.validateOnly || validation.onErrorSkip)) {
|
|
2134
2366
|
throw new ParseError("VALIDATE ONLY / ON ERROR SKIP \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB INSERT \u306B\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u305B\u3093", this.prev());
|
|
2135
2367
|
}
|
|
2136
|
-
return subtableCode ? { type: "INSERT", appId, subtableCode, fields, values, ...validation } : { type: "INSERT", appId, fields, values, ...validation };
|
|
2368
|
+
return subtableCode ? { type: "INSERT", appId, subtableCode, fields, values, ...checkGroups, ...validation } : { type: "INSERT", appId, fields, values, ...checkGroups, ...validation };
|
|
2137
2369
|
}
|
|
2138
2370
|
parseUpsert() {
|
|
2139
2371
|
this.expect("UPSERT" /* UPSERT */);
|
|
@@ -2150,8 +2382,9 @@ var Parser = class {
|
|
|
2150
2382
|
if (this.peek().kind === "SELECT" /* SELECT */) {
|
|
2151
2383
|
const select = this.parseSelect();
|
|
2152
2384
|
const keyFields2 = this.parseOnDuplicate();
|
|
2385
|
+
const checkGroups2 = this.parseCheckGroups();
|
|
2153
2386
|
const validation2 = this.parseDmlControlSuffix();
|
|
2154
|
-
return { type: "UPSERT_SELECT", appId, fields, select, keyFields: keyFields2, ...validation2 };
|
|
2387
|
+
return { type: "UPSERT_SELECT", appId, fields, select, keyFields: keyFields2, ...checkGroups2, ...validation2 };
|
|
2155
2388
|
}
|
|
2156
2389
|
this.expect("VALUES" /* VALUES */);
|
|
2157
2390
|
const values = [];
|
|
@@ -2161,8 +2394,9 @@ var Parser = class {
|
|
|
2161
2394
|
this.expect(")" /* RPAREN */);
|
|
2162
2395
|
} while (this.consume("," /* COMMA */));
|
|
2163
2396
|
const keyFields = this.parseOnDuplicate();
|
|
2397
|
+
const checkGroups = this.parseCheckGroups();
|
|
2164
2398
|
const validation = this.parseDmlControlSuffix();
|
|
2165
|
-
return { type: "UPSERT", appId, fields, values, keyFields, ...validation };
|
|
2399
|
+
return { type: "UPSERT", appId, fields, values, keyFields, ...checkGroups, ...validation };
|
|
2166
2400
|
}
|
|
2167
2401
|
parseOnDuplicate() {
|
|
2168
2402
|
this.expectKeyword("ON" /* ON */, "UPSERT \u306B\u306F ON DUPLICATE (\u30AD\u30FC\u30D5\u30A3\u30FC\u30EB\u30C9) \u304C\u5FC5\u8981\u3067\u3059");
|
|
@@ -2204,14 +2438,13 @@ var Parser = class {
|
|
|
2204
2438
|
} else if (this.peek().kind === "-" /* MINUS */ || this.peek().kind === "+" /* PLUS */) {
|
|
2205
2439
|
const sign = this.advance();
|
|
2206
2440
|
const number = this.expect("NUMBER" /* NUMBER */, "INSERT \u306E\u5358\u9805\u7B26\u53F7\u306E\u76F4\u5F8C\u306B\u306F\u6570\u5024\u30EA\u30C6\u30E9\u30EB\u304C\u5FC5\u8981\u3067\u3059");
|
|
2207
|
-
|
|
2208
|
-
row.push({ type: "NUMBER", value: sign.kind === "-" /* MINUS */ ? -value : value });
|
|
2441
|
+
row.push(makeNumberLiteral(`${sign.kind === "-" /* MINUS */ ? "-" : "+"}${number.value}`));
|
|
2209
2442
|
} else {
|
|
2210
2443
|
const tok = this.advance();
|
|
2211
2444
|
if (tok.kind === "STRING" /* STRING */) {
|
|
2212
2445
|
row.push({ type: "STRING", value: tok.value });
|
|
2213
2446
|
} else if (tok.kind === "NUMBER" /* NUMBER */) {
|
|
2214
|
-
row.push(
|
|
2447
|
+
row.push(makeNumberLiteral(tok.value));
|
|
2215
2448
|
} else {
|
|
2216
2449
|
throw new ParseError("INSERT \u306E\u5024\u306B\u306F\u6587\u5B57\u5217\u30FB\u6570\u5024\u30FB\u914D\u5217\u30EA\u30C6\u30E9\u30EB\u30FBCASE WHEN \u304C\u5FC5\u8981\u3067\u3059", tok);
|
|
2217
2450
|
}
|
|
@@ -2295,12 +2528,33 @@ var Parser = class {
|
|
|
2295
2528
|
whereTok
|
|
2296
2529
|
);
|
|
2297
2530
|
}
|
|
2531
|
+
const checkGroups = this.parseCheckGroups();
|
|
2298
2532
|
const validation = this.parseDmlControlSuffix();
|
|
2533
|
+
if (subtableCode && checkGroups.checkGroups) {
|
|
2534
|
+
throw new ParseError("CHECK \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE \u306B\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u305B\u3093", this.prev());
|
|
2535
|
+
}
|
|
2299
2536
|
if (subtableCode && (validation.validateOnly || validation.onErrorSkip)) {
|
|
2300
2537
|
throw new ParseError("VALIDATE ONLY / ON ERROR SKIP \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE \u306B\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u305B\u3093", this.prev());
|
|
2301
2538
|
}
|
|
2302
|
-
if (from !== null) return { type: "UPDATE", appId, assignments, where, from, ...validation };
|
|
2303
|
-
return subtableCode ? { type: "UPDATE", appId, subtableCode, assignments, where, ...validation } : { type: "UPDATE", appId, assignments, where, ...validation };
|
|
2539
|
+
if (from !== null) return { type: "UPDATE", appId, assignments, where, from, ...checkGroups, ...validation };
|
|
2540
|
+
return subtableCode ? { type: "UPDATE", appId, subtableCode, assignments, where, ...checkGroups, ...validation } : { type: "UPDATE", appId, assignments, where, ...checkGroups, ...validation };
|
|
2541
|
+
}
|
|
2542
|
+
/** CHECK WHEN ... THEN ... blocks. CHECK is a soft keyword. */
|
|
2543
|
+
parseCheckGroups() {
|
|
2544
|
+
const groups = [];
|
|
2545
|
+
while (this.isSoftKeyword("CHECK") && this.peekAt(1).kind === "WHEN" /* WHEN */) {
|
|
2546
|
+
const check = this.advance();
|
|
2547
|
+
const rules = [];
|
|
2548
|
+
while (this.consume("WHEN" /* WHEN */)) {
|
|
2549
|
+
const condition = this.parseWhereExpr();
|
|
2550
|
+
this.expect("THEN" /* THEN */, "CHECK WHEN \u306E\u6761\u4EF6\u306E\u5F8C\u306B\u306F THEN \u304C\u5FC5\u8981\u3067\u3059");
|
|
2551
|
+
const message = this.parseScalarValueExpr({ allowCase: false });
|
|
2552
|
+
rules.push({ condition, message });
|
|
2553
|
+
}
|
|
2554
|
+
if (rules.length === 0) throw new ParseError("CHECK \u306E\u5F8C\u306B\u306F WHEN \u304C\u6700\u4F4E 1 \u3064\u5FC5\u8981\u3067\u3059", check);
|
|
2555
|
+
groups.push({ rules });
|
|
2556
|
+
}
|
|
2557
|
+
return groups.length > 0 ? { checkGroups: groups } : {};
|
|
2304
2558
|
}
|
|
2305
2559
|
/** DML末尾の VALIDATE ONLY または ON ERROR SKIP。各語はsoft keyword。 */
|
|
2306
2560
|
parseDmlControlSuffix() {
|
|
@@ -2489,6 +2743,12 @@ var Parser = class {
|
|
|
2489
2743
|
*/
|
|
2490
2744
|
parseAssignmentValue() {
|
|
2491
2745
|
const tok = this.peek();
|
|
2746
|
+
if (this.hasTopLevelTokenBeforeValueEnd("||" /* CONCAT_OP */)) {
|
|
2747
|
+
const expr = this.parseScalarValueExpr();
|
|
2748
|
+
if (expr.type === "CONCAT_OP" || expr.type === "SCALAR_ARITH" || expr.type === "STRING_FUNC") return expr;
|
|
2749
|
+
if (expr.type === "CASE_WHEN") return { type: "CASE_VALUE", expr };
|
|
2750
|
+
throw new ParseError("SET \u306E\u5024\u306B\u306F\u9023\u7D50\u3092\u542B\u3080\u30B9\u30AB\u30E9\u30FC\u5024\u5F0F\u304C\u5FC5\u8981\u3067\u3059", tok);
|
|
2751
|
+
}
|
|
2492
2752
|
if (tok.kind === "VARIABLE" /* VARIABLE */) return this.parseSqlValue();
|
|
2493
2753
|
if (tok.kind === "STRING" /* STRING */) return this.parseSqlValue();
|
|
2494
2754
|
if (tok.kind === "TODAY" /* TODAY */ || tok.kind === "NOW" /* NOW */ || tok.kind === "LOGINUSER" /* LOGINUSER */) return this.parseSqlValue();
|
|
@@ -3045,7 +3305,7 @@ function convertValue(value, op) {
|
|
|
3045
3305
|
case "STRING":
|
|
3046
3306
|
return convertString(value);
|
|
3047
3307
|
case "NUMBER":
|
|
3048
|
-
return
|
|
3308
|
+
return numberLiteralText(value);
|
|
3049
3309
|
case "KINTONE_FUNC":
|
|
3050
3310
|
return convertKintoneFunc(value);
|
|
3051
3311
|
case "IN_LIST":
|
|
@@ -3074,7 +3334,7 @@ function convertInList(v, op) {
|
|
|
3074
3334
|
}
|
|
3075
3335
|
assertResolvedInListValues(v.values);
|
|
3076
3336
|
const values = v.values.map(
|
|
3077
|
-
(item) => item.type === "STRING" ? convertString(item) :
|
|
3337
|
+
(item) => item.type === "STRING" ? convertString(item) : numberLiteralText(item)
|
|
3078
3338
|
).join(",");
|
|
3079
3339
|
return `(${values})`;
|
|
3080
3340
|
}
|
|
@@ -3112,7 +3372,7 @@ function resolveSelectMode(stmt) {
|
|
|
3112
3372
|
if (stmt.distinct) return "FULL_SCAN";
|
|
3113
3373
|
if (hasWindowColumns(stmt.columns)) return "FULL_SCAN";
|
|
3114
3374
|
if (stmt.columns.some(
|
|
3115
|
-
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "SCALAR_SUBQUERY_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr(c.expr)
|
|
3375
|
+
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "SCALAR_SUBQUERY_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr(c.expr) || c.type === "SCALAR_VALUE_COL" && scalarValueHasAggregate(c.expr)
|
|
3116
3376
|
)) return "FULL_SCAN";
|
|
3117
3377
|
if (whereRequiresJsEval(stmt.where)) return "FULL_SCAN";
|
|
3118
3378
|
if (stmt.orderBy.some((o) => o.key.type !== "FIELD_NAME")) return "FULL_SCAN";
|
|
@@ -3207,6 +3467,8 @@ function extractFields(columns) {
|
|
|
3207
3467
|
collectArithNode(col.expr, fields);
|
|
3208
3468
|
} else if (col.type === "STRFUNC_COL") {
|
|
3209
3469
|
collectStringFuncFields(col.expr, fields);
|
|
3470
|
+
} else if (col.type === "SCALAR_VALUE_COL") {
|
|
3471
|
+
collectScalarValueFields(col.expr, fields);
|
|
3210
3472
|
}
|
|
3211
3473
|
}
|
|
3212
3474
|
return [...new Set(fields)];
|
|
@@ -3232,16 +3494,38 @@ function collectStringFuncFields(expr, out) {
|
|
|
3232
3494
|
}
|
|
3233
3495
|
}
|
|
3234
3496
|
function collectStringFuncArgFields(arg, out) {
|
|
3235
|
-
if (arg.type === "STRING") return;
|
|
3236
|
-
if (arg.type === "STRING_FUNC") {
|
|
3237
|
-
collectStringFuncFields(arg, out);
|
|
3238
|
-
return;
|
|
3239
|
-
}
|
|
3240
3497
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") {
|
|
3241
3498
|
collectAggOperandFields(arg, out);
|
|
3242
3499
|
return;
|
|
3243
3500
|
}
|
|
3244
|
-
|
|
3501
|
+
collectScalarValueFields(arg, out);
|
|
3502
|
+
}
|
|
3503
|
+
function collectScalarValueFields(expr, out) {
|
|
3504
|
+
if (expr.type === "FIELD") {
|
|
3505
|
+
out.push(normalizeSimpleFieldRef(expr.tableAlias ? `${expr.tableAlias}.${expr.field}` : expr.field));
|
|
3506
|
+
return;
|
|
3507
|
+
}
|
|
3508
|
+
if (expr.type === "STRING_FUNC") {
|
|
3509
|
+
collectStringFuncFields(expr, out);
|
|
3510
|
+
return;
|
|
3511
|
+
}
|
|
3512
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
3513
|
+
collectScalarValueFields(expr.left, out);
|
|
3514
|
+
collectScalarValueFields(expr.right, out);
|
|
3515
|
+
return;
|
|
3516
|
+
}
|
|
3517
|
+
if (expr.type === "CASE_WHEN") {
|
|
3518
|
+
for (const branch of expr.branches) collectCaseResultScalarFields(branch.result, out);
|
|
3519
|
+
if (expr.elseResult) collectCaseResultScalarFields(expr.elseResult, out);
|
|
3520
|
+
}
|
|
3521
|
+
}
|
|
3522
|
+
function collectCaseResultScalarFields(result, out) {
|
|
3523
|
+
if (result.type === "ARRAY") return;
|
|
3524
|
+
if (result.type === "FIELD_REF" || result.type === "ARITH") {
|
|
3525
|
+
collectArithNode(result, out);
|
|
3526
|
+
return;
|
|
3527
|
+
}
|
|
3528
|
+
collectScalarValueFields(result, out);
|
|
3245
3529
|
}
|
|
3246
3530
|
function collectAggOperandFields(node, out) {
|
|
3247
3531
|
if (node.type === "AGG_REF") {
|
|
@@ -3256,10 +3540,23 @@ function collectAggOperandFields(node, out) {
|
|
|
3256
3540
|
function hasAggregateInStringFuncExpr(expr) {
|
|
3257
3541
|
return expr.args.some((arg) => {
|
|
3258
3542
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return true;
|
|
3259
|
-
|
|
3260
|
-
return false;
|
|
3543
|
+
return scalarValueHasAggregate(arg);
|
|
3261
3544
|
});
|
|
3262
3545
|
}
|
|
3546
|
+
function scalarValueHasAggregate(expr) {
|
|
3547
|
+
if (expr.type === "STRING_FUNC") return hasAggregateInStringFuncExpr(expr);
|
|
3548
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
3549
|
+
return scalarValueHasAggregate(expr.left) || scalarValueHasAggregate(expr.right);
|
|
3550
|
+
}
|
|
3551
|
+
if (expr.type === "CASE_WHEN") {
|
|
3552
|
+
return expr.branches.some((b) => caseResultHasAggregate(b.result)) || expr.elseResult !== null && caseResultHasAggregate(expr.elseResult);
|
|
3553
|
+
}
|
|
3554
|
+
return false;
|
|
3555
|
+
}
|
|
3556
|
+
function caseResultHasAggregate(result) {
|
|
3557
|
+
if (result.type === "ARRAY" || result.type === "FIELD_REF" || result.type === "ARITH") return false;
|
|
3558
|
+
return scalarValueHasAggregate(result);
|
|
3559
|
+
}
|
|
3263
3560
|
function collectRequiredFieldsByTable(stmt) {
|
|
3264
3561
|
const physicalTables = [stmt.from, ...stmt.joins.map((j) => j.table)].filter((t) => t.cteName === null);
|
|
3265
3562
|
const states = /* @__PURE__ */ new Map();
|
|
@@ -3394,28 +3691,38 @@ function collectRequiredFieldsByTable(stmt) {
|
|
|
3394
3691
|
}
|
|
3395
3692
|
};
|
|
3396
3693
|
const walkStringArg = (arg, phase = "select") => {
|
|
3397
|
-
if (arg.type === "STRING") return;
|
|
3398
|
-
if (arg.type === "STRING_FUNC") {
|
|
3399
|
-
walkStringFunc(arg, phase);
|
|
3400
|
-
return;
|
|
3401
|
-
}
|
|
3402
3694
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") {
|
|
3403
3695
|
walkAgg(arg, phase);
|
|
3404
3696
|
return;
|
|
3405
3697
|
}
|
|
3406
|
-
|
|
3698
|
+
walkScalar(arg, phase);
|
|
3407
3699
|
};
|
|
3408
3700
|
const walkStringFunc = (expr, phase = "select") => {
|
|
3409
3701
|
for (const arg of expr.args) walkStringArg(arg, phase);
|
|
3410
3702
|
};
|
|
3703
|
+
const walkScalar = (expr, phase = "select") => {
|
|
3704
|
+
if (expr.type === "FIELD") {
|
|
3705
|
+
addFieldRef(expr.field, expr.tableAlias, phase);
|
|
3706
|
+
return;
|
|
3707
|
+
}
|
|
3708
|
+
if (expr.type === "STRING_FUNC") {
|
|
3709
|
+
walkStringFunc(expr, phase);
|
|
3710
|
+
return;
|
|
3711
|
+
}
|
|
3712
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
3713
|
+
walkScalar(expr.left, phase);
|
|
3714
|
+
walkScalar(expr.right, phase);
|
|
3715
|
+
return;
|
|
3716
|
+
}
|
|
3717
|
+
if (expr.type === "CASE_WHEN") walkCase(expr, phase);
|
|
3718
|
+
};
|
|
3411
3719
|
const walkCaseResult = (result, phase = "select") => {
|
|
3412
|
-
if (result.type === "STRING") return;
|
|
3413
3720
|
if (result.type === "ARRAY") return;
|
|
3414
|
-
if (result.type === "
|
|
3415
|
-
|
|
3721
|
+
if (result.type === "FIELD_REF" || result.type === "ARITH") {
|
|
3722
|
+
walkArith(result, phase);
|
|
3416
3723
|
return;
|
|
3417
3724
|
}
|
|
3418
|
-
|
|
3725
|
+
walkScalar(result, phase);
|
|
3419
3726
|
};
|
|
3420
3727
|
const walkCase = (expr, phase = "select") => {
|
|
3421
3728
|
for (const b of expr.branches) {
|
|
@@ -3521,6 +3828,9 @@ function collectRequiredFieldsByTable(stmt) {
|
|
|
3521
3828
|
case "STRFUNC_COL":
|
|
3522
3829
|
walkStringFunc(col.expr, "select");
|
|
3523
3830
|
break;
|
|
3831
|
+
case "SCALAR_VALUE_COL":
|
|
3832
|
+
walkScalar(col.expr, "select");
|
|
3833
|
+
break;
|
|
3524
3834
|
case "SCALAR_SUBQUERY_COL":
|
|
3525
3835
|
break;
|
|
3526
3836
|
case "WINDOW_COL":
|
|
@@ -3571,6 +3881,10 @@ function collectSelectOutputNames(columns) {
|
|
|
3571
3881
|
if (col.alias) names.add(col.alias);
|
|
3572
3882
|
continue;
|
|
3573
3883
|
}
|
|
3884
|
+
if (col.type === "SCALAR_VALUE_COL") {
|
|
3885
|
+
if (col.alias) names.add(col.alias);
|
|
3886
|
+
continue;
|
|
3887
|
+
}
|
|
3574
3888
|
if (col.type === "SCALAR_SUBQUERY_COL") {
|
|
3575
3889
|
names.add(col.alias ?? "(subquery)");
|
|
3576
3890
|
continue;
|
|
@@ -3587,20 +3901,38 @@ function aggregateSyntheticName(func, distinct, arg) {
|
|
|
3587
3901
|
}
|
|
3588
3902
|
function arithNodeLabel(node) {
|
|
3589
3903
|
if (node.type === "FIELD_REF") return node.field;
|
|
3590
|
-
if (node.type === "NUMBER") return
|
|
3904
|
+
if (node.type === "NUMBER") return numberLiteralText(node);
|
|
3591
3905
|
if (node.type === "STRING_FUNC") return stringFuncLabel(node);
|
|
3592
3906
|
return `(${arithNodeLabel(node.left)}${node.op}${arithNodeLabel(node.right)})`;
|
|
3593
3907
|
}
|
|
3594
3908
|
function stringFuncLabel(expr) {
|
|
3595
3909
|
const args = expr.args.map((a) => {
|
|
3596
|
-
if (a.type === "STRING") return `'${a.value}'`;
|
|
3597
|
-
if (a.type === "STRING_FUNC") return stringFuncLabel(a);
|
|
3598
3910
|
if (a.type === "AGG_REF") return aggregateSyntheticName(a.func, a.distinct, a.arg);
|
|
3599
3911
|
if (a.type === "AGG_ARITH") return "agg_arith";
|
|
3600
|
-
return
|
|
3912
|
+
return scalarValueLabel(a);
|
|
3601
3913
|
});
|
|
3602
3914
|
return `${expr.func}(${args.join(",")})`;
|
|
3603
3915
|
}
|
|
3916
|
+
function scalarValueLabel(expr) {
|
|
3917
|
+
switch (expr.type) {
|
|
3918
|
+
case "STRING":
|
|
3919
|
+
return `'${expr.value}'`;
|
|
3920
|
+
case "NUMBER":
|
|
3921
|
+
return numberLiteralText(expr);
|
|
3922
|
+
case "VARIABLE":
|
|
3923
|
+
return `@${expr.name}`;
|
|
3924
|
+
case "FIELD":
|
|
3925
|
+
return expr.tableAlias ? `${expr.tableAlias}.${expr.field}` : expr.field;
|
|
3926
|
+
case "STRING_FUNC":
|
|
3927
|
+
return stringFuncLabel(expr);
|
|
3928
|
+
case "CASE_WHEN":
|
|
3929
|
+
return "case";
|
|
3930
|
+
case "SCALAR_ARITH":
|
|
3931
|
+
return `(${scalarValueLabel(expr.left)}${expr.op}${scalarValueLabel(expr.right)})`;
|
|
3932
|
+
case "CONCAT_OP":
|
|
3933
|
+
return `(${scalarValueLabel(expr.left)}||${scalarValueLabel(expr.right)})`;
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3604
3936
|
function isAggregateSyntheticName(name) {
|
|
3605
3937
|
return /^(COUNT|SUM|AVG|MAX|MIN|GROUP_CONCAT)\(/i.test(name);
|
|
3606
3938
|
}
|
|
@@ -3744,7 +4076,7 @@ function isNumericCandidate(expr, options) {
|
|
|
3744
4076
|
if (!isTargetField(expr.left, options)) return false;
|
|
3745
4077
|
if (expr.right.type !== "NUMBER") return false;
|
|
3746
4078
|
if (expr.op === "=") return true;
|
|
3747
|
-
return (expr.op === "<" || expr.op === ">") && Number.isSafeInteger(expr.right.value);
|
|
4079
|
+
return (expr.op === "<" || expr.op === ">") && /^[+-]?\d+$/.test(numberLiteralText(expr.right)) && Number.isSafeInteger(expr.right.value);
|
|
3748
4080
|
}
|
|
3749
4081
|
function isSelectionInCandidate(expr, options) {
|
|
3750
4082
|
if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
|
|
@@ -4349,9 +4681,10 @@ function triCompare(left, right) {
|
|
|
4349
4681
|
}
|
|
4350
4682
|
function numberKey(value) {
|
|
4351
4683
|
if (value === "") return { band: 0 };
|
|
4684
|
+
const decimal = parseExactDecimal(value);
|
|
4685
|
+
if (decimal !== null) return { band: 2, value: decimal };
|
|
4352
4686
|
const numeric = Number(value);
|
|
4353
4687
|
if (numeric === Number.NEGATIVE_INFINITY) return { band: 1 };
|
|
4354
|
-
if (Number.isFinite(numeric)) return { band: 2, value: numeric };
|
|
4355
4688
|
if (numeric === Number.POSITIVE_INFINITY) return { band: 3 };
|
|
4356
4689
|
if (value === "NaN") return { band: 4 };
|
|
4357
4690
|
return { band: 5, value };
|
|
@@ -4360,7 +4693,7 @@ function compareNumbers(left, right) {
|
|
|
4360
4693
|
const a = numberKey(left);
|
|
4361
4694
|
const b = numberKey(right);
|
|
4362
4695
|
if (a.band !== b.band) return a.band < b.band ? -1 : 1;
|
|
4363
|
-
if (a.band === 2 && b.band === 2) return
|
|
4696
|
+
if (a.band === 2 && b.band === 2) return compareExactDecimal(a.value, b.value);
|
|
4364
4697
|
if (a.band === 5 && b.band === 5) return compareCodePointStrings(a.value, b.value);
|
|
4365
4698
|
return 0;
|
|
4366
4699
|
}
|
|
@@ -4460,7 +4793,9 @@ function selectScalarExtreme(values, extreme) {
|
|
|
4460
4793
|
const numeric = candidates.every((value) => !Number.isNaN(Number(value)));
|
|
4461
4794
|
const compare = (left, right) => {
|
|
4462
4795
|
if (numeric) {
|
|
4463
|
-
const
|
|
4796
|
+
const leftDecimal = parseExactDecimal(left);
|
|
4797
|
+
const rightDecimal = parseExactDecimal(right);
|
|
4798
|
+
const numericCmp = leftDecimal !== null && rightDecimal !== null ? compareExactDecimal(leftDecimal, rightDecimal) : triCompare(Number(left), Number(right));
|
|
4464
4799
|
if (numericCmp !== 0) return numericCmp;
|
|
4465
4800
|
}
|
|
4466
4801
|
return compareCodePointStrings(left, right);
|
|
@@ -4540,6 +4875,45 @@ function evalArithExpr(expr, row) {
|
|
|
4540
4875
|
return r !== 0 ? l % r : NaN;
|
|
4541
4876
|
}
|
|
4542
4877
|
}
|
|
4878
|
+
function evalScalarValueExpr(expr, row) {
|
|
4879
|
+
switch (expr.type) {
|
|
4880
|
+
case "STRING":
|
|
4881
|
+
return expr.value;
|
|
4882
|
+
case "NUMBER":
|
|
4883
|
+
return expr.value;
|
|
4884
|
+
case "FIELD":
|
|
4885
|
+
return resolveFieldRef(row, expr.tableAlias ? `${expr.tableAlias}.${expr.field}` : expr.field);
|
|
4886
|
+
case "VARIABLE":
|
|
4887
|
+
throw new Error(`ArgumentError: unresolved variable @${expr.name} reached scalar evaluator.`);
|
|
4888
|
+
case "STRING_FUNC":
|
|
4889
|
+
return evalStringFunc(expr, row);
|
|
4890
|
+
case "CASE_WHEN":
|
|
4891
|
+
return evalCaseWhen(expr, row);
|
|
4892
|
+
case "CONCAT_OP": {
|
|
4893
|
+
return evalStringFunc({
|
|
4894
|
+
type: "STRING_FUNC",
|
|
4895
|
+
func: "CONCAT",
|
|
4896
|
+
args: [expr.left, expr.right]
|
|
4897
|
+
}, row);
|
|
4898
|
+
}
|
|
4899
|
+
case "SCALAR_ARITH": {
|
|
4900
|
+
const left = Number(evalScalarValueExpr(expr.left, row));
|
|
4901
|
+
const right = Number(evalScalarValueExpr(expr.right, row));
|
|
4902
|
+
switch (expr.op) {
|
|
4903
|
+
case "+":
|
|
4904
|
+
return left + right;
|
|
4905
|
+
case "-":
|
|
4906
|
+
return left - right;
|
|
4907
|
+
case "*":
|
|
4908
|
+
return left * right;
|
|
4909
|
+
case "/":
|
|
4910
|
+
return right !== 0 ? left / right : NaN;
|
|
4911
|
+
case "%":
|
|
4912
|
+
return right !== 0 ? left % right : NaN;
|
|
4913
|
+
}
|
|
4914
|
+
}
|
|
4915
|
+
}
|
|
4916
|
+
}
|
|
4543
4917
|
function applyRoundOp(op, num, digits) {
|
|
4544
4918
|
const factor = Math.pow(10, digits);
|
|
4545
4919
|
const raw = Math[op](num * factor) / factor;
|
|
@@ -4583,6 +4957,112 @@ function makeSafePadding(pad, gap) {
|
|
|
4583
4957
|
const repeated = pad.repeat(Math.ceil(gap / pad.length));
|
|
4584
4958
|
return sliceSafePrefix(repeated, gap);
|
|
4585
4959
|
}
|
|
4960
|
+
var REGEXP_CACHE_MAX = 200;
|
|
4961
|
+
var regexpCache = /* @__PURE__ */ new Map();
|
|
4962
|
+
function normalizeRegexpFlags(flags) {
|
|
4963
|
+
if (/[^ims]/.test(flags)) {
|
|
4964
|
+
throw new Error("ArgumentError: regular expression flags may contain only i, m, or s.");
|
|
4965
|
+
}
|
|
4966
|
+
if (new Set(flags).size !== flags.length) {
|
|
4967
|
+
throw new Error("ArgumentError: regular expression flags must not contain duplicates.");
|
|
4968
|
+
}
|
|
4969
|
+
return `${flags}u`;
|
|
4970
|
+
}
|
|
4971
|
+
function compileRegexp(pattern, flags, global = false) {
|
|
4972
|
+
const normalizedFlags = normalizeRegexpFlags(flags) + (global ? "g" : "");
|
|
4973
|
+
const key = `${pattern}\0${normalizedFlags}`;
|
|
4974
|
+
const cached = regexpCache.get(key);
|
|
4975
|
+
if (cached !== void 0) {
|
|
4976
|
+
cached.lastIndex = 0;
|
|
4977
|
+
return cached;
|
|
4978
|
+
}
|
|
4979
|
+
let regexp;
|
|
4980
|
+
try {
|
|
4981
|
+
regexp = new RegExp(pattern, normalizedFlags);
|
|
4982
|
+
} catch (error) {
|
|
4983
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
4984
|
+
throw new Error(`ArgumentError: invalid regular expression: ${detail}`);
|
|
4985
|
+
}
|
|
4986
|
+
if (regexpCache.size >= REGEXP_CACHE_MAX) {
|
|
4987
|
+
const oldest = regexpCache.keys().next().value;
|
|
4988
|
+
if (oldest !== void 0) regexpCache.delete(oldest);
|
|
4989
|
+
}
|
|
4990
|
+
regexpCache.set(key, regexp);
|
|
4991
|
+
return regexp;
|
|
4992
|
+
}
|
|
4993
|
+
function assertRegexpReplacement(replacement) {
|
|
4994
|
+
if (replacement.includes("$`") || replacement.includes("$'")) {
|
|
4995
|
+
throw new Error("ArgumentError: REGEXP_REPLACE replacement must not contain $` or $'.");
|
|
4996
|
+
}
|
|
4997
|
+
}
|
|
4998
|
+
function parseRegexpOccurrence(arg) {
|
|
4999
|
+
if (arg === void 0) return 0;
|
|
5000
|
+
if (!/^\d+$/.test(arg)) {
|
|
5001
|
+
throw new Error("ArgumentError: REGEXP_REPLACE occurrence must be a non-negative integer.");
|
|
5002
|
+
}
|
|
5003
|
+
return Number(arg);
|
|
5004
|
+
}
|
|
5005
|
+
function expandRegexpReplacement(replacement, match, captures, namedGroups) {
|
|
5006
|
+
let result = "";
|
|
5007
|
+
for (let i = 0; i < replacement.length; i += 1) {
|
|
5008
|
+
const char = replacement[i];
|
|
5009
|
+
if (char !== "$" || i + 1 >= replacement.length) {
|
|
5010
|
+
result += char;
|
|
5011
|
+
continue;
|
|
5012
|
+
}
|
|
5013
|
+
const next = replacement[i + 1];
|
|
5014
|
+
if (next === "$") {
|
|
5015
|
+
result += "$";
|
|
5016
|
+
i += 1;
|
|
5017
|
+
continue;
|
|
5018
|
+
}
|
|
5019
|
+
if (next === "&") {
|
|
5020
|
+
result += match;
|
|
5021
|
+
i += 1;
|
|
5022
|
+
continue;
|
|
5023
|
+
}
|
|
5024
|
+
if (next === "<" && namedGroups !== void 0) {
|
|
5025
|
+
const end = replacement.indexOf(">", i + 2);
|
|
5026
|
+
if (end >= 0) {
|
|
5027
|
+
result += namedGroups[replacement.slice(i + 2, end)] ?? "";
|
|
5028
|
+
i = end;
|
|
5029
|
+
continue;
|
|
5030
|
+
}
|
|
5031
|
+
}
|
|
5032
|
+
if (/\d/.test(next)) {
|
|
5033
|
+
const secondDigit = replacement[i + 2];
|
|
5034
|
+
if (secondDigit !== void 0 && /\d/.test(secondDigit)) {
|
|
5035
|
+
const twoDigitIndex = Number(next + secondDigit);
|
|
5036
|
+
if (twoDigitIndex >= 1 && twoDigitIndex <= captures.length) {
|
|
5037
|
+
result += captures[twoDigitIndex - 1] ?? "";
|
|
5038
|
+
i += 2;
|
|
5039
|
+
continue;
|
|
5040
|
+
}
|
|
5041
|
+
}
|
|
5042
|
+
const oneDigitIndex = Number(next);
|
|
5043
|
+
if (oneDigitIndex >= 1 && oneDigitIndex <= captures.length) {
|
|
5044
|
+
result += captures[oneDigitIndex - 1] ?? "";
|
|
5045
|
+
i += 1;
|
|
5046
|
+
continue;
|
|
5047
|
+
}
|
|
5048
|
+
}
|
|
5049
|
+
result += "$";
|
|
5050
|
+
}
|
|
5051
|
+
return result;
|
|
5052
|
+
}
|
|
5053
|
+
function replaceNthMatch(input, globalRe, replacement, n) {
|
|
5054
|
+
let matchCount = 0;
|
|
5055
|
+
return input.replace(globalRe, (match, ...callbackArgs) => {
|
|
5056
|
+
matchCount += 1;
|
|
5057
|
+
if (matchCount !== n) return match;
|
|
5058
|
+
const lastArg = callbackArgs[callbackArgs.length - 1];
|
|
5059
|
+
const hasNamedGroups = typeof lastArg === "object" && lastArg !== null;
|
|
5060
|
+
const capturesEnd = callbackArgs.length - (hasNamedGroups ? 3 : 2);
|
|
5061
|
+
const captures = callbackArgs.slice(0, capturesEnd);
|
|
5062
|
+
const namedGroups = hasNamedGroups ? lastArg : void 0;
|
|
5063
|
+
return expandRegexpReplacement(replacement, match, captures, namedGroups);
|
|
5064
|
+
});
|
|
5065
|
+
}
|
|
4586
5066
|
function evalStringFunc(expr, row) {
|
|
4587
5067
|
const args = expr.args.map((a) => evalStringFuncArg(a, row));
|
|
4588
5068
|
switch (expr.func) {
|
|
@@ -4646,6 +5126,21 @@ function evalStringFunc(expr, row) {
|
|
|
4646
5126
|
const to = args[2] ?? "";
|
|
4647
5127
|
return from === "" ? str : str.split(from).join(to);
|
|
4648
5128
|
}
|
|
5129
|
+
case "REGEXP_LIKE": {
|
|
5130
|
+
assertArity("REGEXP_LIKE", args, 2, 3);
|
|
5131
|
+
return compileRegexp(args[1], args[2] ?? "").test(args[0]) ? "1" : "0";
|
|
5132
|
+
}
|
|
5133
|
+
case "REGEXP_REPLACE": {
|
|
5134
|
+
assertArity("REGEXP_REPLACE", args, 3, 5);
|
|
5135
|
+
assertRegexpReplacement(args[2]);
|
|
5136
|
+
const occurrence = parseRegexpOccurrence(args[4]);
|
|
5137
|
+
const regexp = compileRegexp(args[1], args[3] ?? "", true);
|
|
5138
|
+
return occurrence === 0 ? args[0].replace(regexp, args[2]) : replaceNthMatch(args[0], regexp, args[2], occurrence);
|
|
5139
|
+
}
|
|
5140
|
+
case "REGEXP_SUBSTR": {
|
|
5141
|
+
assertArity("REGEXP_SUBSTR", args, 2, 3);
|
|
5142
|
+
return compileRegexp(args[1], args[2] ?? "").exec(args[0])?.[0] ?? "";
|
|
5143
|
+
}
|
|
4649
5144
|
case "TRANSLATE": {
|
|
4650
5145
|
assertArity("TRANSLATE", args, 3, 3);
|
|
4651
5146
|
const from = [...args[1]];
|
|
@@ -4821,12 +5316,9 @@ function formatWithComma(num, digits) {
|
|
|
4821
5316
|
return decStr ? `${intFmt}.${decStr}` : intFmt;
|
|
4822
5317
|
}
|
|
4823
5318
|
function evalStringFuncArg(arg, row) {
|
|
4824
|
-
if (arg.type === "STRING") return arg.value;
|
|
4825
|
-
if (arg.type === "STRING_FUNC") return evalStringFunc(arg, row);
|
|
4826
|
-
if (arg.type === "FIELD_REF") return resolveFieldRef(row, arg.field);
|
|
4827
|
-
if (arg.type === "NUMBER") return String(arg.value);
|
|
4828
5319
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return "";
|
|
4829
|
-
|
|
5320
|
+
if (arg.type === "NUMBER") return numberLiteralText(arg);
|
|
5321
|
+
return String(evalScalarValueExpr(arg, row));
|
|
4830
5322
|
}
|
|
4831
5323
|
function resolveFieldRef(row, field) {
|
|
4832
5324
|
const direct = row[field];
|
|
@@ -4873,7 +5365,7 @@ function evalOp(op, leftStr, right, row, fieldType, resolveFieldType, semantics
|
|
|
4873
5365
|
let values = null;
|
|
4874
5366
|
if (right.type === "IN_LIST") {
|
|
4875
5367
|
assertResolvedInListValues2(right.values);
|
|
4876
|
-
values = new Set(right.values.map((v) => String(v.value)));
|
|
5368
|
+
values = new Set(right.values.map((v) => v.type === "NUMBER" ? fieldType === "NUMBER" ? numberLiteralText(v) : String(v.value) : v.value));
|
|
4877
5369
|
}
|
|
4878
5370
|
if (right.type === "SUBQUERY_IN_LIST") {
|
|
4879
5371
|
values = right.resolved;
|
|
@@ -4953,6 +5445,10 @@ var SINGLE_OBJECT_FIELD_TYPES = /* @__PURE__ */ new Set(["CREATOR", "MODIFIER"])
|
|
|
4953
5445
|
function typedInContains(leftStr, values, fieldType) {
|
|
4954
5446
|
const fallback = () => values.has(leftStr);
|
|
4955
5447
|
if (fieldType === void 0) return fallback();
|
|
5448
|
+
if (fieldType === "NUMBER") {
|
|
5449
|
+
const semantics = syntheticSemantics("number");
|
|
5450
|
+
return [...values].some((value) => compareScalarValues("=", leftStr, value, semantics));
|
|
5451
|
+
}
|
|
4956
5452
|
let parsed;
|
|
4957
5453
|
if (STRING_ARRAY_FIELD_TYPES.has(fieldType) || OBJECT_ARRAY_FIELD_TYPES.has(fieldType) || SINGLE_OBJECT_FIELD_TYPES.has(fieldType)) {
|
|
4958
5454
|
try {
|
|
@@ -5011,7 +5507,7 @@ function resolveValue(value, row, resolveFieldType, resolveFieldSemantics2) {
|
|
|
5011
5507
|
case "STRING":
|
|
5012
5508
|
return value.value;
|
|
5013
5509
|
case "NUMBER":
|
|
5014
|
-
return
|
|
5510
|
+
return numberLiteralText(value);
|
|
5015
5511
|
case "KINTONE_FUNC":
|
|
5016
5512
|
return resolveKintoneFunc(value.name);
|
|
5017
5513
|
case "IN_LIST":
|
|
@@ -5045,10 +5541,13 @@ function evalCaseWhen(expr, row, resolveFieldType, resolveFieldSemantics2) {
|
|
|
5045
5541
|
}
|
|
5046
5542
|
function evalCaseResult(result, row) {
|
|
5047
5543
|
if (result.type === "ARRAY") return result.elements.map((e) => e.value).join(",");
|
|
5048
|
-
if (result.type === "
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5544
|
+
if (result.type === "FIELD_REF") {
|
|
5545
|
+
return row[result.field] ?? "";
|
|
5546
|
+
}
|
|
5547
|
+
if (result.type === "ARITH") {
|
|
5548
|
+
return String(evalArithExpr(result, row));
|
|
5549
|
+
}
|
|
5550
|
+
return String(evalScalarValueExpr(result, row));
|
|
5052
5551
|
}
|
|
5053
5552
|
function resolveKintoneFunc(name) {
|
|
5054
5553
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -5092,6 +5591,63 @@ function matchLike(value, pattern) {
|
|
|
5092
5591
|
return regex.test(value);
|
|
5093
5592
|
}
|
|
5094
5593
|
|
|
5594
|
+
// src/core/dmlCustomCheck.ts
|
|
5595
|
+
function collectCheckFieldRefs(groups) {
|
|
5596
|
+
return collectRefs2(groups);
|
|
5597
|
+
}
|
|
5598
|
+
function collectCheckComparisonFieldRefs(groups) {
|
|
5599
|
+
return collectRefs2(groups.flatMap((group) => group.rules.map((rule) => rule.condition)));
|
|
5600
|
+
}
|
|
5601
|
+
function collectRefs2(root) {
|
|
5602
|
+
const refs = [];
|
|
5603
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5604
|
+
const visit = (node) => {
|
|
5605
|
+
if (Array.isArray(node)) {
|
|
5606
|
+
node.forEach(visit);
|
|
5607
|
+
return;
|
|
5608
|
+
}
|
|
5609
|
+
if (node === null || typeof node !== "object") return;
|
|
5610
|
+
const obj = node;
|
|
5611
|
+
if (obj.type === "EXISTS" || obj.type === "SUBQUERY_IN_LIST" || obj.type === "SCALAR_SUBQUERY") {
|
|
5612
|
+
throw customCheckParseError("CHECK \u306B\u30B5\u30D6\u30AF\u30A8\u30EA\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093");
|
|
5613
|
+
}
|
|
5614
|
+
if (obj.type === "FIELD" && typeof obj.field === "string") {
|
|
5615
|
+
add(typeof obj.tableAlias === "string" ? obj.tableAlias : null, obj.field);
|
|
5616
|
+
} else if (obj.type === "FIELD_REF" && typeof obj.field === "string") {
|
|
5617
|
+
const dot = obj.field.indexOf(".");
|
|
5618
|
+
add(dot > 0 ? obj.field.slice(0, dot) : null, dot > 0 ? obj.field.slice(dot + 1) : obj.field);
|
|
5619
|
+
}
|
|
5620
|
+
for (const value of Object.values(obj)) visit(value);
|
|
5621
|
+
};
|
|
5622
|
+
const add = (tableAlias, field) => {
|
|
5623
|
+
if (/^(COUNT|SUM|AVG|MIN|MAX|GROUP_CONCAT)\(/i.test(field)) {
|
|
5624
|
+
throw customCheckParseError("CHECK \u306B\u96C6\u7D04\u95A2\u6570\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093");
|
|
5625
|
+
}
|
|
5626
|
+
const key = `${tableAlias ?? ""}\0${field}`;
|
|
5627
|
+
if (!seen.has(key)) {
|
|
5628
|
+
seen.add(key);
|
|
5629
|
+
refs.push({ tableAlias, field });
|
|
5630
|
+
}
|
|
5631
|
+
};
|
|
5632
|
+
visit(root);
|
|
5633
|
+
return refs;
|
|
5634
|
+
}
|
|
5635
|
+
function customCheckParseError(message) {
|
|
5636
|
+
return new ParseError(message, { kind: "EOF" /* EOF */, value: "CHECK", pos: 0 });
|
|
5637
|
+
}
|
|
5638
|
+
function evaluateCustomChecks(groups, row, resolveFieldType) {
|
|
5639
|
+
const errors = [];
|
|
5640
|
+
groups.forEach((group, groupIndex) => {
|
|
5641
|
+
for (const rule of group.rules) {
|
|
5642
|
+
if (!evalWhere(rule.condition, row, resolveFieldType)) continue;
|
|
5643
|
+
const value = evalScalarValueExpr(rule.message, row);
|
|
5644
|
+
errors.push({ groupIndex, message: value == null ? "" : String(value) });
|
|
5645
|
+
break;
|
|
5646
|
+
}
|
|
5647
|
+
});
|
|
5648
|
+
return errors;
|
|
5649
|
+
}
|
|
5650
|
+
|
|
5095
5651
|
// src/converter/dmlToKintone.ts
|
|
5096
5652
|
function assertDmlWhereIsSafe(where) {
|
|
5097
5653
|
if (whereHasKlike(where)) {
|
|
@@ -5129,10 +5685,11 @@ function buildInsertRecord(fields, row, fieldTypes) {
|
|
|
5129
5685
|
}
|
|
5130
5686
|
function updateToGetQuery(stmt) {
|
|
5131
5687
|
assertDmlWhereIsSafe(stmt.where);
|
|
5688
|
+
const checkFields = collectUpdateCheckTargetFields(stmt);
|
|
5132
5689
|
return {
|
|
5133
5690
|
app: stmt.appId,
|
|
5134
5691
|
query: whereToKintone(stmt.where),
|
|
5135
|
-
fields: ["$id"],
|
|
5692
|
+
fields: ["$id", ...checkFields],
|
|
5136
5693
|
totalCount: false
|
|
5137
5694
|
};
|
|
5138
5695
|
}
|
|
@@ -5146,19 +5703,19 @@ function updateToPutBatches(stmt, ids, fieldTypes = /* @__PURE__ */ new Map()) {
|
|
|
5146
5703
|
function buildUpdateRecord(assignments, fieldTypes) {
|
|
5147
5704
|
const record = {};
|
|
5148
5705
|
for (const { field, value } of assignments) {
|
|
5149
|
-
if (value.type === "ARITH" || value.type === "CASE_VALUE" || value.type === "STRING_FUNC" || value.type === "SOURCE_FIELD") continue;
|
|
5706
|
+
if (value.type === "ARITH" || value.type === "SCALAR_ARITH" || value.type === "CONCAT_OP" || value.type === "CASE_VALUE" || value.type === "STRING_FUNC" || value.type === "SOURCE_FIELD") continue;
|
|
5150
5707
|
record[field] = { value: toKintoneValue(value, fieldTypes.get(field)) };
|
|
5151
5708
|
}
|
|
5152
5709
|
return record;
|
|
5153
5710
|
}
|
|
5154
5711
|
function hasArithAssignment(stmt) {
|
|
5155
5712
|
return stmt.assignments.some(
|
|
5156
|
-
(a) => a.value.type === "ARITH" || a.value.type === "CASE_VALUE"
|
|
5713
|
+
(a) => a.value.type === "ARITH" || a.value.type === "SCALAR_ARITH" || a.value.type === "CONCAT_OP" || a.value.type === "CASE_VALUE"
|
|
5157
5714
|
);
|
|
5158
5715
|
}
|
|
5159
5716
|
function hasRowDependentAssignment(stmt) {
|
|
5160
5717
|
return stmt.assignments.some(
|
|
5161
|
-
(a) => a.value.type === "ARITH" || a.value.type === "CASE_VALUE" || a.value.type === "STRING_FUNC"
|
|
5718
|
+
(a) => a.value.type === "ARITH" || a.value.type === "SCALAR_ARITH" || a.value.type === "CONCAT_OP" || a.value.type === "CASE_VALUE" || a.value.type === "STRING_FUNC"
|
|
5162
5719
|
);
|
|
5163
5720
|
}
|
|
5164
5721
|
function updateToGetQueryForArith(stmt) {
|
|
@@ -5167,12 +5724,15 @@ function updateToGetQueryForArith(stmt) {
|
|
|
5167
5724
|
for (const { value } of stmt.assignments) {
|
|
5168
5725
|
if (value.type === "ARITH") {
|
|
5169
5726
|
collectArithFields2(value, refFields);
|
|
5727
|
+
} else if (value.type === "SCALAR_ARITH" || value.type === "CONCAT_OP") {
|
|
5728
|
+
collectScalarValueFields2(value, refFields);
|
|
5170
5729
|
} else if (value.type === "STRING_FUNC") {
|
|
5171
5730
|
collectStringFuncFields2(value, refFields);
|
|
5172
5731
|
} else if (value.type === "CASE_VALUE") {
|
|
5173
5732
|
collectCaseFields(value.expr, refFields);
|
|
5174
5733
|
}
|
|
5175
5734
|
}
|
|
5735
|
+
collectUpdateCheckTargetFields(stmt).forEach((field) => refFields.add(field));
|
|
5176
5736
|
return {
|
|
5177
5737
|
app: stmt.appId,
|
|
5178
5738
|
query: whereToKintone(stmt.where),
|
|
@@ -5193,16 +5753,27 @@ function collectStringFuncFields2(expr, out) {
|
|
|
5193
5753
|
for (const arg of expr.args) collectStringFuncArgFields2(arg, out);
|
|
5194
5754
|
}
|
|
5195
5755
|
function collectStringFuncArgFields2(arg, out) {
|
|
5196
|
-
if (arg.type === "STRING") return;
|
|
5197
|
-
if (arg.type === "STRING_FUNC") {
|
|
5198
|
-
collectStringFuncFields2(arg, out);
|
|
5199
|
-
return;
|
|
5200
|
-
}
|
|
5201
5756
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") {
|
|
5202
5757
|
collectAggOperandFields2(arg, out);
|
|
5203
5758
|
return;
|
|
5204
5759
|
}
|
|
5205
|
-
|
|
5760
|
+
collectScalarValueFields2(arg, out);
|
|
5761
|
+
}
|
|
5762
|
+
function collectScalarValueFields2(expr, out) {
|
|
5763
|
+
if (expr.type === "FIELD") {
|
|
5764
|
+
out.add(expr.tableAlias ? `${expr.tableAlias}.${expr.field}` : expr.field);
|
|
5765
|
+
return;
|
|
5766
|
+
}
|
|
5767
|
+
if (expr.type === "STRING_FUNC") {
|
|
5768
|
+
collectStringFuncFields2(expr, out);
|
|
5769
|
+
return;
|
|
5770
|
+
}
|
|
5771
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
5772
|
+
collectScalarValueFields2(expr.left, out);
|
|
5773
|
+
collectScalarValueFields2(expr.right, out);
|
|
5774
|
+
return;
|
|
5775
|
+
}
|
|
5776
|
+
if (expr.type === "CASE_WHEN") collectCaseFields(expr, out);
|
|
5206
5777
|
}
|
|
5207
5778
|
function collectAggOperandFields2(node, out) {
|
|
5208
5779
|
if (node.type === "AGG_REF") {
|
|
@@ -5215,9 +5786,12 @@ function collectAggOperandFields2(node, out) {
|
|
|
5215
5786
|
}
|
|
5216
5787
|
}
|
|
5217
5788
|
function collectCaseResultFields(result, out) {
|
|
5218
|
-
if (result.type === "STRING") return;
|
|
5219
5789
|
if (result.type === "ARRAY") return;
|
|
5220
|
-
|
|
5790
|
+
if (result.type === "FIELD_REF" || result.type === "ARITH") {
|
|
5791
|
+
collectArithNode2(result, out);
|
|
5792
|
+
return;
|
|
5793
|
+
}
|
|
5794
|
+
collectScalarValueFields2(result, out);
|
|
5221
5795
|
}
|
|
5222
5796
|
function collectCaseFields(expr, out) {
|
|
5223
5797
|
for (const branch of expr.branches) {
|
|
@@ -5255,6 +5829,8 @@ function updateToPutBatchesArith(stmt, records, fieldTypes = /* @__PURE__ */ new
|
|
|
5255
5829
|
for (const { field, value } of stmt.assignments) {
|
|
5256
5830
|
if (value.type === "ARITH") {
|
|
5257
5831
|
record[field] = { value: String(evalArith(value, raw)) };
|
|
5832
|
+
} else if (value.type === "SCALAR_ARITH" || value.type === "CONCAT_OP") {
|
|
5833
|
+
record[field] = { value: String(evalScalarValueExpr(value, row)) };
|
|
5258
5834
|
} else if (value.type === "STRING_FUNC") {
|
|
5259
5835
|
record[field] = { value: evalStringFunc(value, row) };
|
|
5260
5836
|
} else if (value.type === "CASE_VALUE") {
|
|
@@ -5309,6 +5885,8 @@ function updateFromToPutBatches(stmt, matched, fieldTypes = /* @__PURE__ */ new
|
|
|
5309
5885
|
throw new DmlConvertError("UPDATE ... FROM \u306E SET \u3067\u306F\u6587\u5B57\u5217\u95A2\u6570\u3092\u76F4\u63A5\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093");
|
|
5310
5886
|
} else if (value.type === "ARITH") {
|
|
5311
5887
|
record[field] = { value: String(evalArith(value, target)) };
|
|
5888
|
+
} else if (value.type === "SCALAR_ARITH" || value.type === "CONCAT_OP") {
|
|
5889
|
+
record[field] = { value: String(evalScalarValueExpr(value, targetRow)) };
|
|
5312
5890
|
} else if (value.type === "CASE_VALUE") {
|
|
5313
5891
|
record[field] = { value: evalCaseWhenValue(value.expr, targetRow, fieldType) };
|
|
5314
5892
|
} else {
|
|
@@ -5419,7 +5997,15 @@ function evalCaseResultValue(result, row, fieldType) {
|
|
|
5419
5997
|
if (result.type === "STRING_FUNC") {
|
|
5420
5998
|
return evalStringFunc(result, row);
|
|
5421
5999
|
}
|
|
5422
|
-
|
|
6000
|
+
if (result.type === "FIELD_REF" || result.type === "ARITH") {
|
|
6001
|
+
return String(evalArithExpr(result, row));
|
|
6002
|
+
}
|
|
6003
|
+
return String(evalScalarValueExpr(result, row));
|
|
6004
|
+
}
|
|
6005
|
+
function collectUpdateCheckTargetFields(stmt) {
|
|
6006
|
+
if (!stmt.checkGroups) return [];
|
|
6007
|
+
const targetAlias = `app${stmt.appId}`.toLowerCase();
|
|
6008
|
+
return [...new Set(collectCheckFieldRefs(stmt.checkGroups).filter((ref) => ref.tableAlias === null || ref.tableAlias.toLowerCase() === targetAlias).map((ref) => ref.field).filter((field) => field !== "$id"))];
|
|
5423
6009
|
}
|
|
5424
6010
|
function evalCaseWhenValue(expr, row, fieldType) {
|
|
5425
6011
|
for (const branch of expr.branches) {
|
|
@@ -5451,7 +6037,7 @@ function convertDmlSqlValue(value, fieldType) {
|
|
|
5451
6037
|
case "STRING":
|
|
5452
6038
|
return convertString2(value.value, fieldType);
|
|
5453
6039
|
case "NUMBER":
|
|
5454
|
-
return
|
|
6040
|
+
return numberLiteralText(value);
|
|
5455
6041
|
case "ARRAY":
|
|
5456
6042
|
return convertArray(value.elements.map((e) => e.value), fieldType);
|
|
5457
6043
|
case "KINTONE_FUNC":
|
|
@@ -5972,7 +6558,7 @@ function applyFilter(rows, where, resolveFieldType, appliedKlikes, resolveFieldS
|
|
|
5972
6558
|
}
|
|
5973
6559
|
function hasAggregateColumns(columns) {
|
|
5974
6560
|
return columns.some(
|
|
5975
|
-
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(c.expr)
|
|
6561
|
+
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(c.expr) || c.type === "SCALAR_VALUE_COL" && scalarValueHasAggregate2(c.expr)
|
|
5976
6562
|
);
|
|
5977
6563
|
}
|
|
5978
6564
|
function applyGroupBy(rows, groupByKeys, columns, resolveAggSortKind) {
|
|
@@ -6009,6 +6595,10 @@ function applyGroupBy(rows, groupByKeys, columns, resolveAggSortKind) {
|
|
|
6009
6595
|
const outputKey = col.alias ?? stringFuncDefaultKey(col.expr);
|
|
6010
6596
|
const resolvedExpr = resolveAggInStringFuncExpr(col.expr, groupRows, resolveAggSortKind);
|
|
6011
6597
|
outRow[outputKey] = evalStringFunc(resolvedExpr, outRow);
|
|
6598
|
+
} else if (col.type === "SCALAR_VALUE_COL" && scalarValueHasAggregate2(col.expr)) {
|
|
6599
|
+
const outputKey = col.alias ?? scalarValueDefaultKey(col.expr);
|
|
6600
|
+
const resolvedExpr = resolveAggInScalarValue(col.expr, groupRows, resolveAggSortKind);
|
|
6601
|
+
outRow[outputKey] = String(evalScalarValueExpr(resolvedExpr, outRow));
|
|
6012
6602
|
}
|
|
6013
6603
|
}
|
|
6014
6604
|
result.push(outRow);
|
|
@@ -6083,7 +6673,7 @@ function evalAggArithExpr(node, rows, resolveAggSortKind) {
|
|
|
6083
6673
|
}
|
|
6084
6674
|
}
|
|
6085
6675
|
function aggArithDefaultKey(node) {
|
|
6086
|
-
if (node.type === "NUMBER") return
|
|
6676
|
+
if (node.type === "NUMBER") return numberLiteralText(node);
|
|
6087
6677
|
if (node.type === "AGG_REF") return aggregateSyntheticName2(node.func, node.distinct, node.arg);
|
|
6088
6678
|
return `${aggArithDefaultKey(node.left)}${node.op}${aggArithDefaultKey(node.right)}`;
|
|
6089
6679
|
}
|
|
@@ -6339,6 +6929,13 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns, re
|
|
|
6339
6929
|
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
6340
6930
|
break;
|
|
6341
6931
|
}
|
|
6932
|
+
case "SCALAR_VALUE_COL": {
|
|
6933
|
+
const key = outputKeys?.[colIdx] ?? col.alias ?? scalarValueDefaultKey(col.expr);
|
|
6934
|
+
const srcKey = scalarValueDefaultKey(col.expr);
|
|
6935
|
+
out[key] = scalarValueHasAggregate2(col.expr) ? row[col.alias ?? srcKey] ?? row[srcKey] ?? "" : String(evalScalarValueExpr(col.expr, row));
|
|
6936
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
6937
|
+
break;
|
|
6938
|
+
}
|
|
6342
6939
|
case "SCALAR_SUBQUERY_COL": {
|
|
6343
6940
|
const key = outputKeys?.[colIdx] ?? col.alias ?? "(subquery)";
|
|
6344
6941
|
out[key] = scalarCache?.get(colIdx) ?? "";
|
|
@@ -6389,6 +6986,8 @@ function computeOutputKey(col, colIdx, defaultFieldKeys) {
|
|
|
6389
6986
|
return col.alias ?? "case";
|
|
6390
6987
|
case "STRFUNC_COL":
|
|
6391
6988
|
return col.alias ?? stringFuncDefaultKey(col.expr);
|
|
6989
|
+
case "SCALAR_VALUE_COL":
|
|
6990
|
+
return col.alias ?? scalarValueDefaultKey(col.expr);
|
|
6392
6991
|
case "SCALAR_SUBQUERY_COL":
|
|
6393
6992
|
return col.alias ?? "(subquery)";
|
|
6394
6993
|
case "WINDOW_COL":
|
|
@@ -6433,7 +7032,7 @@ function stripParentShortcutColumns(row) {
|
|
|
6433
7032
|
function arithColDefaultKey(expr) {
|
|
6434
7033
|
const nodeLabel = (n) => {
|
|
6435
7034
|
if (n.type === "FIELD_REF") return n.field;
|
|
6436
|
-
if (n.type === "NUMBER") return
|
|
7035
|
+
if (n.type === "NUMBER") return numberLiteralText(n);
|
|
6437
7036
|
if (n.type === "STRING_FUNC") return stringFuncDefaultKey(n);
|
|
6438
7037
|
return `(${nodeLabel(n.left)}${n.op}${nodeLabel(n.right)})`;
|
|
6439
7038
|
};
|
|
@@ -6444,33 +7043,76 @@ function arithColDefaultKey(expr) {
|
|
|
6444
7043
|
}
|
|
6445
7044
|
function stringFuncDefaultKey(expr) {
|
|
6446
7045
|
const argStrs = expr.args.map((a) => {
|
|
6447
|
-
if (a.type === "STRING") return `'${a.value}'`;
|
|
6448
|
-
if (a.type === "STRING_FUNC") return stringFuncDefaultKey(a);
|
|
6449
7046
|
if (a.type === "AGG_REF" || a.type === "AGG_ARITH") return aggArithDefaultKey(a);
|
|
6450
|
-
return
|
|
7047
|
+
return scalarValueDefaultKey(a);
|
|
6451
7048
|
});
|
|
6452
7049
|
return `${expr.func}(${argStrs.join(",")})`;
|
|
6453
7050
|
}
|
|
7051
|
+
function scalarValueDefaultKey(expr) {
|
|
7052
|
+
switch (expr.type) {
|
|
7053
|
+
case "STRING":
|
|
7054
|
+
return `'${expr.value}'`;
|
|
7055
|
+
case "NUMBER":
|
|
7056
|
+
return numberLiteralText(expr);
|
|
7057
|
+
case "VARIABLE":
|
|
7058
|
+
return `@${expr.name}`;
|
|
7059
|
+
case "FIELD":
|
|
7060
|
+
return expr.tableAlias ? `${expr.tableAlias}.${expr.field}` : expr.field;
|
|
7061
|
+
case "STRING_FUNC":
|
|
7062
|
+
return stringFuncDefaultKey(expr);
|
|
7063
|
+
case "CASE_WHEN":
|
|
7064
|
+
return "case";
|
|
7065
|
+
case "SCALAR_ARITH":
|
|
7066
|
+
return `${scalarValueDefaultKey(expr.left)}${expr.op}${scalarValueDefaultKey(expr.right)}`;
|
|
7067
|
+
case "CONCAT_OP":
|
|
7068
|
+
return `${scalarValueDefaultKey(expr.left)}||${scalarValueDefaultKey(expr.right)}`;
|
|
7069
|
+
}
|
|
7070
|
+
}
|
|
6454
7071
|
function hasAggregateInStringFuncArg(arg) {
|
|
6455
7072
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return true;
|
|
6456
|
-
|
|
7073
|
+
return scalarValueHasAggregate2(arg);
|
|
7074
|
+
}
|
|
7075
|
+
function scalarValueHasAggregate2(expr) {
|
|
7076
|
+
if (expr.type === "STRING_FUNC") return hasAggregateInStringFuncExpr2(expr);
|
|
7077
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
7078
|
+
return scalarValueHasAggregate2(expr.left) || scalarValueHasAggregate2(expr.right);
|
|
7079
|
+
}
|
|
7080
|
+
if (expr.type === "CASE_WHEN") {
|
|
7081
|
+
return expr.branches.some((branch) => caseResultHasAggregate2(branch.result)) || expr.elseResult !== null && caseResultHasAggregate2(expr.elseResult);
|
|
7082
|
+
}
|
|
6457
7083
|
return false;
|
|
6458
7084
|
}
|
|
7085
|
+
function caseResultHasAggregate2(result) {
|
|
7086
|
+
if (result.type === "ARRAY" || result.type === "FIELD_REF" || result.type === "ARITH") return false;
|
|
7087
|
+
return scalarValueHasAggregate2(result);
|
|
7088
|
+
}
|
|
6459
7089
|
function hasAggregateInStringFuncExpr2(expr) {
|
|
6460
7090
|
return expr.args.some((arg) => hasAggregateInStringFuncArg(arg));
|
|
6461
7091
|
}
|
|
6462
7092
|
function resolveAggInStringFuncArg(arg, rows, resolveAggSortKind) {
|
|
6463
7093
|
if (arg.type === "AGG_REF") {
|
|
6464
7094
|
const value = evalAggregate(arg.func, arg.distinct, arg.arg, arg.separator, rows, resolveAggSortKind);
|
|
6465
|
-
return typeof value === "number" ? { type: "NUMBER", value } : { type: "STRING", value };
|
|
7095
|
+
return typeof value === "number" ? { type: "NUMBER", value, raw: String(value) } : { type: "STRING", value };
|
|
6466
7096
|
}
|
|
6467
7097
|
if (arg.type === "AGG_ARITH") {
|
|
6468
|
-
|
|
7098
|
+
const value = evalAggArithExpr(arg, rows, resolveAggSortKind);
|
|
7099
|
+
return { type: "NUMBER", value, raw: String(value) };
|
|
6469
7100
|
}
|
|
6470
7101
|
if (arg.type === "STRING_FUNC") {
|
|
6471
7102
|
return resolveAggInStringFuncExpr(arg, rows, resolveAggSortKind);
|
|
6472
7103
|
}
|
|
6473
|
-
return arg;
|
|
7104
|
+
return resolveAggInScalarValue(arg, rows, resolveAggSortKind);
|
|
7105
|
+
}
|
|
7106
|
+
function resolveAggInScalarValue(expr, rows, resolveAggSortKind) {
|
|
7107
|
+
if (expr.type === "STRING_FUNC") return resolveAggInStringFuncExpr(expr, rows, resolveAggSortKind);
|
|
7108
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
7109
|
+
return {
|
|
7110
|
+
...expr,
|
|
7111
|
+
left: resolveAggInScalarValue(expr.left, rows, resolveAggSortKind),
|
|
7112
|
+
right: resolveAggInScalarValue(expr.right, rows, resolveAggSortKind)
|
|
7113
|
+
};
|
|
7114
|
+
}
|
|
7115
|
+
return expr;
|
|
6474
7116
|
}
|
|
6475
7117
|
function resolveAggInStringFuncExpr(expr, rows, resolveAggSortKind) {
|
|
6476
7118
|
return {
|
|
@@ -6491,7 +7133,7 @@ function deriveOutputOrderSemantics(columns) {
|
|
|
6491
7133
|
} else if (column.func === "GROUP_CONCAT") {
|
|
6492
7134
|
result.set(column.alias, syntheticSemantics("string"));
|
|
6493
7135
|
}
|
|
6494
|
-
} else if (column.type === "LITERAL_COL" || column.type === "CASE_COL" || column.type === "SCALAR_SUBQUERY_COL") {
|
|
7136
|
+
} else if (column.type === "LITERAL_COL" || column.type === "CASE_COL" || column.type === "SCALAR_SUBQUERY_COL" || column.type === "SCALAR_VALUE_COL") {
|
|
6495
7137
|
result.set(column.alias, syntheticSemantics("string"));
|
|
6496
7138
|
} else if (column.type === "STRFUNC_COL") {
|
|
6497
7139
|
result.set(column.alias, syntheticSemantics(NUMERIC_ORDER_FUNCTIONS.has(column.expr.func) ? "number" : "string"));
|
|
@@ -6583,10 +7225,43 @@ function toFlatString(value) {
|
|
|
6583
7225
|
}
|
|
6584
7226
|
}
|
|
6585
7227
|
|
|
7228
|
+
// src/core/numberPrecision.ts
|
|
7229
|
+
function parseIntegerSetting(value, name, min, max) {
|
|
7230
|
+
if (typeof value !== "string" || !/^\d+$/.test(value)) {
|
|
7231
|
+
throw new Error(`SettingsError: numberPrecision.${name} must be an integer string.`);
|
|
7232
|
+
}
|
|
7233
|
+
let parsed = 0;
|
|
7234
|
+
for (const digit of value) parsed = parsed * 10 + digit.charCodeAt(0) - 48;
|
|
7235
|
+
if (parsed < min || parsed > max) {
|
|
7236
|
+
throw new Error(`SettingsError: numberPrecision.${name} must be between ${min} and ${max}.`);
|
|
7237
|
+
}
|
|
7238
|
+
return parsed;
|
|
7239
|
+
}
|
|
7240
|
+
function parseNumberPrecisionSettings(response) {
|
|
7241
|
+
const raw = response.numberPrecision;
|
|
7242
|
+
if (raw === void 0 || raw === null || typeof raw !== "object") {
|
|
7243
|
+
throw new Error("SettingsError: numberPrecision is missing from app settings.");
|
|
7244
|
+
}
|
|
7245
|
+
const digits = parseIntegerSetting(raw.digits, "digits", 1, 30);
|
|
7246
|
+
const decimalPlaces = parseIntegerSetting(raw.decimalPlaces, "decimalPlaces", 0, 10);
|
|
7247
|
+
const roundingMode = raw.roundingMode;
|
|
7248
|
+
if (roundingMode !== "HALF_EVEN" && roundingMode !== "UP" && roundingMode !== "DOWN") {
|
|
7249
|
+
throw new Error("SettingsError: numberPrecision.roundingMode is unsupported.");
|
|
7250
|
+
}
|
|
7251
|
+
return { digits, decimalPlaces, roundingMode };
|
|
7252
|
+
}
|
|
7253
|
+
function exactDecimalDigitCounts(value) {
|
|
7254
|
+
if (value.sign === 0) return { integerDigits: 0, fractionDigits: 0 };
|
|
7255
|
+
return {
|
|
7256
|
+
integerDigits: Math.max(value.coefficient.length - value.scale, 0),
|
|
7257
|
+
fractionDigits: Math.max(value.scale, 0)
|
|
7258
|
+
};
|
|
7259
|
+
}
|
|
7260
|
+
|
|
6586
7261
|
// src/core/dmlValidation.ts
|
|
6587
7262
|
var ARRAY_TYPES2 = /* @__PURE__ */ new Set(["CHECK_BOX", "MULTI_SELECT"]);
|
|
6588
7263
|
var CHOICE_TYPES = /* @__PURE__ */ new Set(["DROP_DOWN", "RADIO_BUTTON", "CHECK_BOX", "MULTI_SELECT"]);
|
|
6589
|
-
function validateAndNormalizeDmlValue(raw, field) {
|
|
7264
|
+
function validateAndNormalizeDmlValue(raw, field, numberPrecision) {
|
|
6590
7265
|
if (field.fieldType === "DATE" || field.fieldType === "TIME" || field.fieldType === "DATETIME") {
|
|
6591
7266
|
const original = rawScalarText(raw);
|
|
6592
7267
|
if (original !== "" && !isValidTemporalInput(original, field.fieldType)) {
|
|
@@ -6605,7 +7280,8 @@ function validateAndNormalizeDmlValue(raw, field) {
|
|
|
6605
7280
|
}
|
|
6606
7281
|
if (!isEmpty(value) && field.fieldType === "NUMBER") {
|
|
6607
7282
|
const text = String(value);
|
|
6608
|
-
|
|
7283
|
+
const decimal = parseExactDecimal(text);
|
|
7284
|
+
if (decimal === null) {
|
|
6609
7285
|
return { ok: false, code: "ERR_TYPE_NUMBER", message: `${field.code} \u306F\u6570\u5024\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044` };
|
|
6610
7286
|
}
|
|
6611
7287
|
if (field.minValue != null && compareDecimal(text, field.minValue) < 0) {
|
|
@@ -6614,6 +7290,17 @@ function validateAndNormalizeDmlValue(raw, field) {
|
|
|
6614
7290
|
if (field.maxValue != null && compareDecimal(text, field.maxValue) > 0) {
|
|
6615
7291
|
return { ok: false, code: "ERR_RANGE_MAX", message: `${field.code} \u306F ${field.maxValue} \u4EE5\u4E0B\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044` };
|
|
6616
7292
|
}
|
|
7293
|
+
if (numberPrecision !== void 0) {
|
|
7294
|
+
const { integerDigits } = exactDecimalDigitCounts(decimal);
|
|
7295
|
+
const integerBudget = numberPrecision.digits - numberPrecision.decimalPlaces;
|
|
7296
|
+
if (integerDigits > integerBudget) {
|
|
7297
|
+
return {
|
|
7298
|
+
ok: false,
|
|
7299
|
+
code: "ERR_NUMBER_INTEGER_DIGITS",
|
|
7300
|
+
message: `${field.code} \u306E\u6574\u6570\u90E8\u306F ${integerDigits} \u6841\u3067\u3059\u3002\u8A31\u5BB9\u306F ${integerBudget} \u6841\u307E\u3067\u3067\u3059 (digits=${numberPrecision.digits}, decimalPlaces=${numberPrecision.decimalPlaces})`
|
|
7301
|
+
};
|
|
7302
|
+
}
|
|
7303
|
+
}
|
|
6617
7304
|
}
|
|
6618
7305
|
if (!isEmpty(value) && (field.fieldType === "DATE" || field.fieldType === "TIME" || field.fieldType === "DATETIME")) {
|
|
6619
7306
|
if (!isValidTemporal(String(value), field.fieldType)) {
|
|
@@ -6641,7 +7328,8 @@ function validateAndNormalizeDmlValue(raw, field) {
|
|
|
6641
7328
|
}
|
|
6642
7329
|
function rawScalarText(raw) {
|
|
6643
7330
|
if (raw == null) return "";
|
|
6644
|
-
if (isSqlValue(raw) &&
|
|
7331
|
+
if (isSqlValue(raw) && raw.type === "NUMBER") return numberLiteralText(raw);
|
|
7332
|
+
if (isSqlValue(raw) && raw.type === "STRING") return raw.value;
|
|
6645
7333
|
return typeof raw === "string" || typeof raw === "number" ? String(raw) : "";
|
|
6646
7334
|
}
|
|
6647
7335
|
function isValidTemporalInput(value, type) {
|
|
@@ -6691,34 +7379,6 @@ function isEmpty(value) {
|
|
|
6691
7379
|
function typeCode(type) {
|
|
6692
7380
|
return type === "NUMBER" ? "ERR_TYPE_NUMBER" : "ERR_TYPE_DATE";
|
|
6693
7381
|
}
|
|
6694
|
-
function isFiniteDecimal(value) {
|
|
6695
|
-
return /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(value.trim());
|
|
6696
|
-
}
|
|
6697
|
-
function compareDecimal(left, right) {
|
|
6698
|
-
const normalize = (input) => {
|
|
6699
|
-
let s = input.trim();
|
|
6700
|
-
let sign = 1;
|
|
6701
|
-
if (s.startsWith("-")) {
|
|
6702
|
-
sign = -1;
|
|
6703
|
-
s = s.slice(1);
|
|
6704
|
-
} else if (s.startsWith("+")) s = s.slice(1);
|
|
6705
|
-
let [whole, fraction = ""] = s.split(".");
|
|
6706
|
-
whole = (whole || "0").replace(/^0+(?=\d)/, "");
|
|
6707
|
-
fraction = fraction.replace(/0+$/, "");
|
|
6708
|
-
if (/^0*$/.test(whole) && fraction === "") sign = 1;
|
|
6709
|
-
return { sign, whole, fraction };
|
|
6710
|
-
};
|
|
6711
|
-
const a = normalize(left);
|
|
6712
|
-
const b = normalize(right);
|
|
6713
|
-
if (a.sign !== b.sign) return a.sign < b.sign ? -1 : 1;
|
|
6714
|
-
const direction = a.sign;
|
|
6715
|
-
if (a.whole.length !== b.whole.length) return a.whole.length < b.whole.length ? -direction : direction;
|
|
6716
|
-
if (a.whole !== b.whole) return a.whole < b.whole ? -direction : direction;
|
|
6717
|
-
const width = Math.max(a.fraction.length, b.fraction.length);
|
|
6718
|
-
const af = a.fraction.padEnd(width, "0");
|
|
6719
|
-
const bf = b.fraction.padEnd(width, "0");
|
|
6720
|
-
return af === bf ? 0 : af < bf ? -direction : direction;
|
|
6721
|
-
}
|
|
6722
7382
|
function isValidTemporal(value, type) {
|
|
6723
7383
|
if (type === "TIME") {
|
|
6724
7384
|
const m2 = /^(\d{2}):(\d{2})(?::(\d{2}))?$/.exec(value);
|
|
@@ -6746,32 +7406,33 @@ var VALIDATION_META_COLUMNS = [
|
|
|
6746
7406
|
"$err_code",
|
|
6747
7407
|
"$err_message"
|
|
6748
7408
|
];
|
|
6749
|
-
function validateDmlCandidates(candidates, operation, payloadFields, targetFields, fieldInfos, statementNumber) {
|
|
7409
|
+
function validateDmlCandidates(candidates, operation, payloadFields, targetFields, fieldInfos, statementNumber, numberPrecision, checkGroups = [], validateMissingCreateFields = true, includePreErrors = true) {
|
|
6750
7410
|
const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
|
|
6751
7411
|
const errors = [];
|
|
6752
7412
|
const invalid = /* @__PURE__ */ new Set();
|
|
7413
|
+
let firstEvaluationError;
|
|
6753
7414
|
for (const candidate of candidates) {
|
|
6754
7415
|
candidate.record ??= {};
|
|
6755
|
-
const rowErrors = [...candidate.preErrors];
|
|
7416
|
+
const rowErrors = includePreErrors ? [...candidate.preErrors] : [];
|
|
6756
7417
|
for (const code of targetFields) {
|
|
6757
|
-
const result = validateAndNormalizeDmlValue(candidate.payload.get(code), infoByCode.get(code));
|
|
7418
|
+
const result = validateAndNormalizeDmlValue(candidate.payload.get(code), infoByCode.get(code), numberPrecision);
|
|
6758
7419
|
if (!result.ok) rowErrors.push({ field: code, code: result.code, message: result.message });
|
|
6759
7420
|
else candidate.record[code] = { value: result.value };
|
|
6760
7421
|
}
|
|
6761
|
-
if (candidate.mode === "create") {
|
|
7422
|
+
if (validateMissingCreateFields && candidate.mode === "create") {
|
|
6762
7423
|
for (const info of fieldInfos) {
|
|
6763
7424
|
if (info.inSubtable) continue;
|
|
6764
7425
|
if (candidate.payload.has(info.code)) continue;
|
|
6765
7426
|
const emptyDefault = isEmptyDmlValue(info.defaultValue);
|
|
6766
7427
|
if (!emptyDefault) {
|
|
6767
|
-
const defaultResult = validateAndNormalizeDmlValue(info.defaultValue, info);
|
|
7428
|
+
const defaultResult = validateAndNormalizeDmlValue(info.defaultValue, info, numberPrecision);
|
|
6768
7429
|
if (!defaultResult.ok) rowErrors.push({
|
|
6769
7430
|
field: info.code,
|
|
6770
7431
|
code: defaultResult.code,
|
|
6771
7432
|
message: `\u65E2\u5B9A\u5024: ${defaultResult.message}`
|
|
6772
7433
|
});
|
|
6773
7434
|
} else {
|
|
6774
|
-
const emptyResult = validateAndNormalizeDmlValue("", info);
|
|
7435
|
+
const emptyResult = validateAndNormalizeDmlValue("", info, numberPrecision);
|
|
6775
7436
|
if (!emptyResult.ok) {
|
|
6776
7437
|
rowErrors.push({ field: info.code, code: emptyResult.code, message: emptyResult.message });
|
|
6777
7438
|
} else if (info.required) {
|
|
@@ -6780,6 +7441,23 @@ function validateDmlCandidates(candidates, operation, payloadFields, targetField
|
|
|
6780
7441
|
}
|
|
6781
7442
|
}
|
|
6782
7443
|
}
|
|
7444
|
+
if (checkGroups.length > 0) {
|
|
7445
|
+
const row = candidate.evaluationRow ?? Object.fromEntries(
|
|
7446
|
+
[...candidate.payload].map(([field, value]) => [field, renderValidationValue(value)])
|
|
7447
|
+
);
|
|
7448
|
+
const types = candidate.evaluationFieldTypes;
|
|
7449
|
+
const resolveType = (field) => {
|
|
7450
|
+
const qualified = field.tableAlias ? `${field.tableAlias}.${field.field}` : field.field;
|
|
7451
|
+
return types?.get(qualified) ?? types?.get(field.field);
|
|
7452
|
+
};
|
|
7453
|
+
try {
|
|
7454
|
+
for (const custom of evaluateCustomChecks(checkGroups, row, resolveType)) {
|
|
7455
|
+
rowErrors.push({ field: "", code: "ERR_CHECK", message: custom.message });
|
|
7456
|
+
}
|
|
7457
|
+
} catch (error) {
|
|
7458
|
+
firstEvaluationError ??= error;
|
|
7459
|
+
}
|
|
7460
|
+
}
|
|
6783
7461
|
if (rowErrors.length > 0) invalid.add(candidate.rowNumber);
|
|
6784
7462
|
for (const error of rowErrors) {
|
|
6785
7463
|
const row = {};
|
|
@@ -6793,13 +7471,15 @@ function validateDmlCandidates(candidates, operation, payloadFields, targetField
|
|
|
6793
7471
|
errors.push(row);
|
|
6794
7472
|
}
|
|
6795
7473
|
}
|
|
7474
|
+
if (firstEvaluationError !== void 0) throw firstEvaluationError;
|
|
6796
7475
|
return { errors, invalidRows: invalid.size, invalidRowNumbers: invalid };
|
|
6797
7476
|
}
|
|
6798
7477
|
function renderValidationValue(value) {
|
|
6799
7478
|
if (value == null) return "";
|
|
6800
7479
|
if (typeof value === "object" && "type" in value) {
|
|
6801
7480
|
const sql = value;
|
|
6802
|
-
if (sql.type === "
|
|
7481
|
+
if (sql.type === "NUMBER") return sql.raw ?? String(sql.value ?? "");
|
|
7482
|
+
if (sql.type === "STRING") return String(sql.value ?? "");
|
|
6803
7483
|
if (sql.type === "ARRAY") return JSON.stringify(sql.elements?.map((e) => e.value) ?? []);
|
|
6804
7484
|
}
|
|
6805
7485
|
if (Array.isArray(value)) return JSON.stringify(value);
|
|
@@ -7041,6 +7721,7 @@ function createEmptyMetrics() {
|
|
|
7041
7721
|
putCalls: 0,
|
|
7042
7722
|
deleteCalls: 0,
|
|
7043
7723
|
fieldCalls: 0,
|
|
7724
|
+
numberPrecisionCalls: 0,
|
|
7044
7725
|
appsCalls: 0,
|
|
7045
7726
|
processStatusCalls: 0,
|
|
7046
7727
|
cursorCreateCalls: 0,
|
|
@@ -7126,6 +7807,10 @@ function wrapClientWithMetrics(client, metrics) {
|
|
|
7126
7807
|
metrics.fieldCalls += 1;
|
|
7127
7808
|
return client.getFields(appId);
|
|
7128
7809
|
},
|
|
7810
|
+
getNumberPrecision: (appId) => {
|
|
7811
|
+
metrics.numberPrecisionCalls += 1;
|
|
7812
|
+
return client.getNumberPrecision(appId);
|
|
7813
|
+
},
|
|
7129
7814
|
getProcessStatuses: (appId) => {
|
|
7130
7815
|
metrics.processStatusCalls += 1;
|
|
7131
7816
|
return client.getProcessStatuses(appId);
|
|
@@ -7393,7 +8078,7 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
|
|
|
7393
8078
|
const first = resolvedStmt2.expr.query.columns[0];
|
|
7394
8079
|
const numeric = first?.type === "ARITH_COL" || first?.type === "ARITH_AGG_COL" || first?.type === "WINDOW_COL" || first?.type === "AGGREGATE" && (first.func === "COUNT" || first.func === "SUM" || first.func === "AVG");
|
|
7395
8080
|
const numberValue = numeric ? Number(value) : Number.NaN;
|
|
7396
|
-
variables.set(stmt.name, numeric && Number.isFinite(numberValue) ? { type: "number", value: numberValue } : { type: "string", value });
|
|
8081
|
+
variables.set(stmt.name, numeric && Number.isFinite(numberValue) ? { type: "number", value: numberValue, raw: value } : { type: "string", value });
|
|
7397
8082
|
} catch (e) {
|
|
7398
8083
|
if (e instanceof ScalarSubqueryError) {
|
|
7399
8084
|
throw new Error(`ArgumentError: ${e.message}`);
|
|
@@ -7411,7 +8096,10 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
|
|
|
7411
8096
|
variables.set(stmt.name, { type: "string", value: injected[stmt.name] });
|
|
7412
8097
|
} else {
|
|
7413
8098
|
const value = evaluateScalarExpr(stmt.default);
|
|
7414
|
-
variables.set(stmt.name, {
|
|
8099
|
+
variables.set(stmt.name, {
|
|
8100
|
+
type: "string",
|
|
8101
|
+
value: value.type === "number" ? value.raw ?? String(value.value) : value.value
|
|
8102
|
+
});
|
|
7415
8103
|
}
|
|
7416
8104
|
return {};
|
|
7417
8105
|
}
|
|
@@ -7587,7 +8275,7 @@ function evaluateScalarExpr(expr) {
|
|
|
7587
8275
|
case "STRING":
|
|
7588
8276
|
return { type: "string", value: expr.value };
|
|
7589
8277
|
case "NUMBER":
|
|
7590
|
-
return { type: "number", value: expr.value };
|
|
8278
|
+
return { type: "number", value: expr.value, raw: numberLiteralText(expr) };
|
|
7591
8279
|
case "KINTONE_FUNC":
|
|
7592
8280
|
return { type: "string", value: resolveKintoneFunc(expr.name) };
|
|
7593
8281
|
case "STRING_FUNC":
|
|
@@ -7597,7 +8285,7 @@ function evaluateScalarExpr(expr) {
|
|
|
7597
8285
|
if (!Number.isFinite(value)) {
|
|
7598
8286
|
throw new Error("ArgumentError: SET scalar arithmetic produced a non-finite number.");
|
|
7599
8287
|
}
|
|
7600
|
-
return { type: "number", value };
|
|
8288
|
+
return { type: "number", value, raw: String(value) };
|
|
7601
8289
|
}
|
|
7602
8290
|
}
|
|
7603
8291
|
}
|
|
@@ -7612,7 +8300,7 @@ function resolveVariableRefs(node, variables) {
|
|
|
7612
8300
|
if (value === void 0) {
|
|
7613
8301
|
throw new Error(`ParseError: variable @${obj["name"]} is not defined in this batch.`);
|
|
7614
8302
|
}
|
|
7615
|
-
return value.type === "number" ? { type: "NUMBER", value: value.value } : { type: "STRING", value: value.value };
|
|
8303
|
+
return value.type === "number" ? { type: "NUMBER", value: value.value, raw: value.raw ?? String(value.value) } : { type: "STRING", value: value.value };
|
|
7616
8304
|
}
|
|
7617
8305
|
return Object.fromEntries(
|
|
7618
8306
|
Object.entries(obj).map(([key, value]) => [key, resolveVariableRefs(value, variables)])
|
|
@@ -7678,7 +8366,7 @@ async function evalAssertOperand(operand, client, options, cacheContext, tempTab
|
|
|
7678
8366
|
case "VARIABLE":
|
|
7679
8367
|
throw new Error(`ParseError: unresolved batch variable @${operand.name}.`);
|
|
7680
8368
|
case "NUMBER":
|
|
7681
|
-
return
|
|
8369
|
+
return numberLiteralText(operand);
|
|
7682
8370
|
case "STRING":
|
|
7683
8371
|
return operand.value;
|
|
7684
8372
|
case "ARITH":
|
|
@@ -7840,7 +8528,7 @@ function buildHavingFieldSemanticsResolver(stmt, rowResolver) {
|
|
|
7840
8528
|
}
|
|
7841
8529
|
} else if (column.type === "STRFUNC_COL") {
|
|
7842
8530
|
semantics = stringFunctionColumnMeta(column.expr).semantics;
|
|
7843
|
-
} else if (column.type === "LITERAL_COL" || column.type === "SCALAR_SUBQUERY_COL" || column.type === "CASE_COL") {
|
|
8531
|
+
} else if (column.type === "LITERAL_COL" || column.type === "SCALAR_SUBQUERY_COL" || column.type === "CASE_COL" || column.type === "SCALAR_VALUE_COL") {
|
|
7844
8532
|
semantics = syntheticSemantics("string");
|
|
7845
8533
|
}
|
|
7846
8534
|
if (semantics) aliases.set(column.alias, semantics);
|
|
@@ -7955,10 +8643,19 @@ function arithHasFieldRef(node) {
|
|
|
7955
8643
|
return false;
|
|
7956
8644
|
}
|
|
7957
8645
|
function stringFuncArgHasFieldRef(arg) {
|
|
7958
|
-
if (arg.type === "FIELD_REF") return true;
|
|
7959
|
-
if (arg.type === "ARITH") return arithHasFieldRef(arg);
|
|
7960
|
-
if (arg.type === "STRING_FUNC") return stringFuncHasFieldRef(arg);
|
|
7961
8646
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return true;
|
|
8647
|
+
return scalarValueHasFieldRef(arg);
|
|
8648
|
+
}
|
|
8649
|
+
function scalarValueHasFieldRef(expr) {
|
|
8650
|
+
if (expr.type === "FIELD") return true;
|
|
8651
|
+
if (expr.type === "STRING_FUNC") return stringFuncHasFieldRef(expr);
|
|
8652
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
8653
|
+
return scalarValueHasFieldRef(expr.left) || scalarValueHasFieldRef(expr.right);
|
|
8654
|
+
}
|
|
8655
|
+
if (expr.type === "CASE_WHEN") {
|
|
8656
|
+
const results = [...expr.branches.map((branch) => branch.result), ...expr.elseResult ? [expr.elseResult] : []];
|
|
8657
|
+
return results.some((result) => result.type !== "ARRAY" && (result.type === "FIELD_REF" || result.type === "ARITH" ? arithHasFieldRef(result) : scalarValueHasFieldRef(result)));
|
|
8658
|
+
}
|
|
7962
8659
|
return false;
|
|
7963
8660
|
}
|
|
7964
8661
|
function stringFuncHasFieldRef(expr) {
|
|
@@ -7979,6 +8676,11 @@ function validateNoFromColumns(stmt) {
|
|
|
7979
8676
|
throw new Error("ArgumentError: field reference is not allowed without FROM.");
|
|
7980
8677
|
}
|
|
7981
8678
|
break;
|
|
8679
|
+
case "SCALAR_VALUE_COL":
|
|
8680
|
+
if (scalarValueHasFieldRef(col.expr)) {
|
|
8681
|
+
throw new Error("ArgumentError: field reference is not allowed without FROM.");
|
|
8682
|
+
}
|
|
8683
|
+
break;
|
|
7982
8684
|
case "WINDOW_COL":
|
|
7983
8685
|
if (col.partitionBy.length > 0 || col.orderBy.length > 0) {
|
|
7984
8686
|
throw new Error("ArgumentError: field reference is not allowed without FROM.");
|
|
@@ -8265,8 +8967,26 @@ function collectStringFuncAggregateRefs(expr, out) {
|
|
|
8265
8967
|
for (const arg of expr.args) {
|
|
8266
8968
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") {
|
|
8267
8969
|
collectAggregateOperandRefs(arg, out);
|
|
8268
|
-
} else
|
|
8269
|
-
|
|
8970
|
+
} else {
|
|
8971
|
+
collectScalarAggregateRefs(arg, out);
|
|
8972
|
+
}
|
|
8973
|
+
}
|
|
8974
|
+
}
|
|
8975
|
+
function collectScalarAggregateRefs(expr, out) {
|
|
8976
|
+
if (expr.type === "STRING_FUNC") {
|
|
8977
|
+
collectStringFuncAggregateRefs(expr, out);
|
|
8978
|
+
return;
|
|
8979
|
+
}
|
|
8980
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
8981
|
+
collectScalarAggregateRefs(expr.left, out);
|
|
8982
|
+
collectScalarAggregateRefs(expr.right, out);
|
|
8983
|
+
return;
|
|
8984
|
+
}
|
|
8985
|
+
if (expr.type === "CASE_WHEN") {
|
|
8986
|
+
const results = [...expr.branches.map((branch) => branch.result), ...expr.elseResult ? [expr.elseResult] : []];
|
|
8987
|
+
for (const result of results) {
|
|
8988
|
+
if (result.type === "STRING_FUNC") collectStringFuncAggregateRefs(result, out);
|
|
8989
|
+
else if (result.type !== "ARRAY" && result.type !== "FIELD_REF" && result.type !== "ARITH") collectScalarAggregateRefs(result, out);
|
|
8270
8990
|
}
|
|
8271
8991
|
}
|
|
8272
8992
|
}
|
|
@@ -8279,6 +8999,8 @@ function collectSelectAggregateSortRefs(columns) {
|
|
|
8279
8999
|
collectAggregateOperandRefs(column.expr, refs);
|
|
8280
9000
|
} else if (column.type === "STRFUNC_COL") {
|
|
8281
9001
|
collectStringFuncAggregateRefs(column.expr, refs);
|
|
9002
|
+
} else if (column.type === "SCALAR_VALUE_COL") {
|
|
9003
|
+
collectScalarAggregateRefs(column.expr, refs);
|
|
8282
9004
|
}
|
|
8283
9005
|
}
|
|
8284
9006
|
return refs;
|
|
@@ -8444,10 +9166,11 @@ function stringFunctionColumnMeta(expr) {
|
|
|
8444
9166
|
function caseResultColumnMeta(result, resolveField2) {
|
|
8445
9167
|
if (result.type === "STRING") return syntheticColumnMeta("string");
|
|
8446
9168
|
if (result.type === "ARRAY") return unsupportedColumnMeta();
|
|
8447
|
-
if (result.type === "NUMBER" || result.type === "ARITH") return syntheticColumnMeta("number");
|
|
9169
|
+
if (result.type === "NUMBER" || result.type === "ARITH" || result.type === "SCALAR_ARITH") return syntheticColumnMeta("number");
|
|
8448
9170
|
if (result.type === "STRING_FUNC") return stringFunctionColumnMeta(result);
|
|
8449
|
-
|
|
8450
|
-
return
|
|
9171
|
+
if (result.type === "FIELD_REF") return resolveField2(aggregateFieldRef(result.field)) ?? unknownStringColumnMeta();
|
|
9172
|
+
if (result.type === "FIELD") return resolveField2(result) ?? unknownStringColumnMeta();
|
|
9173
|
+
return unknownStringColumnMeta();
|
|
8451
9174
|
}
|
|
8452
9175
|
function mergeExpressionColumnMeta(candidates) {
|
|
8453
9176
|
if (candidates.length === 0) return unknownStringColumnMeta();
|
|
@@ -8549,7 +9272,7 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
|
|
|
8549
9272
|
}
|
|
8550
9273
|
} else if (column.type === "ARITH_AGG_COL" || column.type === "ARITH_COL") {
|
|
8551
9274
|
meta = syntheticColumnMeta("number");
|
|
8552
|
-
} else if (column.type === "LITERAL_COL") {
|
|
9275
|
+
} else if (column.type === "LITERAL_COL" || column.type === "SCALAR_VALUE_COL") {
|
|
8553
9276
|
meta = syntheticColumnMeta("string");
|
|
8554
9277
|
} else if (column.type === "STRFUNC_COL") {
|
|
8555
9278
|
meta = stringFunctionColumnMeta(column.expr);
|
|
@@ -8961,8 +9684,8 @@ async function fetchTableRecordsForFullScan(stmt, table, client, maxRecords, par
|
|
|
8961
9684
|
}
|
|
8962
9685
|
var UPSERT_IN_CHUNK_SIZE = 50;
|
|
8963
9686
|
function normalizeKeyPart(v) {
|
|
8964
|
-
const
|
|
8965
|
-
if (
|
|
9687
|
+
const decimal = parseExactDecimal(v);
|
|
9688
|
+
if (decimal !== null) return JSON.stringify(decimal);
|
|
8966
9689
|
return v;
|
|
8967
9690
|
}
|
|
8968
9691
|
function upsertCompositeKey(parts) {
|
|
@@ -9114,6 +9837,7 @@ var optionOrderCache = /* @__PURE__ */ new Map();
|
|
|
9114
9837
|
var sortKindCache = /* @__PURE__ */ new Map();
|
|
9115
9838
|
var fieldInfoCache = /* @__PURE__ */ new Map();
|
|
9116
9839
|
var processStatusCache = /* @__PURE__ */ new Map();
|
|
9840
|
+
var numberPrecisionCache = /* @__PURE__ */ new Map();
|
|
9117
9841
|
function getScopedCacheValue(root, cacheContext, appId) {
|
|
9118
9842
|
return root.get(cacheContext)?.get(appId);
|
|
9119
9843
|
}
|
|
@@ -9135,6 +9859,13 @@ async function getFieldsCached(appId, client, cacheContext) {
|
|
|
9135
9859
|
setScopedCacheValue(fieldInfoCache, cacheContext, appId, loading);
|
|
9136
9860
|
return loading;
|
|
9137
9861
|
}
|
|
9862
|
+
async function getNumberPrecisionCached(appId, client, cacheContext) {
|
|
9863
|
+
const cached = getScopedCacheValue(numberPrecisionCache, cacheContext, appId);
|
|
9864
|
+
if (cached) return cached;
|
|
9865
|
+
const loading = client.getNumberPrecision(appId);
|
|
9866
|
+
setScopedCacheValue(numberPrecisionCache, cacheContext, appId, loading);
|
|
9867
|
+
return loading;
|
|
9868
|
+
}
|
|
9138
9869
|
async function getProcessStatusesCached(appId, client, cacheContext) {
|
|
9139
9870
|
const cached = getScopedCacheValue(processStatusCache, cacheContext, appId);
|
|
9140
9871
|
if (cached) return cached;
|
|
@@ -9248,7 +9979,7 @@ async function buildOrderSemanticsForSelect(stmt, client, cacheContext, material
|
|
|
9248
9979
|
if (column.type === "FIELD") meta = resolveField2(aggregateFieldRef(column.field));
|
|
9249
9980
|
else if (column.type === "ARITH_COL" || column.type === "ARITH_AGG_COL" || column.type === "WINDOW_COL") {
|
|
9250
9981
|
meta = syntheticColumnMeta("number");
|
|
9251
|
-
} else if (column.type === "LITERAL_COL") meta = syntheticColumnMeta("string");
|
|
9982
|
+
} else if (column.type === "LITERAL_COL" || column.type === "SCALAR_VALUE_COL") meta = syntheticColumnMeta("string");
|
|
9252
9983
|
else if (column.type === "STRFUNC_COL") meta = stringFunctionColumnMeta(column.expr);
|
|
9253
9984
|
else if (column.type === "SCALAR_SUBQUERY_COL") meta = unknownStringColumnMeta();
|
|
9254
9985
|
else if (column.type === "CASE_COL") {
|
|
@@ -9409,6 +10140,23 @@ async function loadWritableTopLevelDmlFields(appId, targetFields, client, cacheC
|
|
|
9409
10140
|
assertWritableTopLevelDmlFields(appId, targetFields, fieldInfos);
|
|
9410
10141
|
return fieldInfos;
|
|
9411
10142
|
}
|
|
10143
|
+
async function loadNumberPrecisionForTargets(appId, targetFields, fieldInfos, client, cacheContext) {
|
|
10144
|
+
const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
|
|
10145
|
+
return targetFields.some((code) => infoByCode.get(code)?.fieldType === "NUMBER") ? getNumberPrecisionCached(appId, client, cacheContext) : void 0;
|
|
10146
|
+
}
|
|
10147
|
+
function assertValidDmlRecords(records, targetFields, fieldInfos, numberPrecision) {
|
|
10148
|
+
const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
|
|
10149
|
+
records.forEach((record, rowIndex) => {
|
|
10150
|
+
for (const code of targetFields) {
|
|
10151
|
+
const info = infoByCode.get(code);
|
|
10152
|
+
const result = validateAndNormalizeDmlValue(record[code]?.value ?? "", info, numberPrecision);
|
|
10153
|
+
if (!result.ok) {
|
|
10154
|
+
throw new Error(`DmlValidationError: ${result.code} ${result.message} (row=${rowIndex + 1}, field=${code})`);
|
|
10155
|
+
}
|
|
10156
|
+
record[code] = { value: result.value };
|
|
10157
|
+
}
|
|
10158
|
+
});
|
|
10159
|
+
}
|
|
9412
10160
|
async function executeDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber) {
|
|
9413
10161
|
return (await prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber)).result;
|
|
9414
10162
|
}
|
|
@@ -9419,7 +10167,7 @@ var RejectLimitExceededError = class extends Error {
|
|
|
9419
10167
|
this.name = "RejectLimitExceededError";
|
|
9420
10168
|
}
|
|
9421
10169
|
};
|
|
9422
|
-
async function prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber) {
|
|
10170
|
+
async function prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber, validateMissingCreateFields = true, includePreErrors = true) {
|
|
9423
10171
|
const operation = stmt.type === "UPDATE" ? "UPDATE" : stmt.type.startsWith("UPSERT") ? "UPSERT" : "INSERT";
|
|
9424
10172
|
const payloadFields = stmt.type === "UPDATE" ? ["$id", ...stmt.assignments.map((a) => a.field)] : [...stmt.fields];
|
|
9425
10173
|
if (new Set(payloadFields).size !== payloadFields.length) {
|
|
@@ -9436,6 +10184,13 @@ async function prepareDmlValidation(stmt, client, options, cacheContext, tempTab
|
|
|
9436
10184
|
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
9437
10185
|
}
|
|
9438
10186
|
const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
|
|
10187
|
+
const numberPrecision = await loadNumberPrecisionForTargets(
|
|
10188
|
+
stmt.appId,
|
|
10189
|
+
targetFields,
|
|
10190
|
+
fieldInfos,
|
|
10191
|
+
client,
|
|
10192
|
+
cacheContext
|
|
10193
|
+
);
|
|
9439
10194
|
const candidates = await materializeValidationCandidates(stmt, operation, client, options, cacheContext, tempTables, infoByCode);
|
|
9440
10195
|
const { errors, invalidRows, invalidRowNumbers } = validateDmlCandidates(
|
|
9441
10196
|
candidates,
|
|
@@ -9443,7 +10198,11 @@ async function prepareDmlValidation(stmt, client, options, cacheContext, tempTab
|
|
|
9443
10198
|
payloadFields,
|
|
9444
10199
|
targetFields,
|
|
9445
10200
|
fieldInfos,
|
|
9446
|
-
statementNumber
|
|
10201
|
+
statementNumber,
|
|
10202
|
+
numberPrecision,
|
|
10203
|
+
stmt.checkGroups ?? [],
|
|
10204
|
+
validateMissingCreateFields,
|
|
10205
|
+
includePreErrors
|
|
9447
10206
|
);
|
|
9448
10207
|
const columns = [...payloadFields, ...VALIDATION_META_COLUMNS];
|
|
9449
10208
|
const result = {
|
|
@@ -9553,15 +10312,33 @@ async function executeOnErrorSkip(stmt, client, options, cacheContext, tempTable
|
|
|
9553
10312
|
async function materializeValidationCandidates(stmt, operation, client, options, cacheContext, tempTables, infoByCode) {
|
|
9554
10313
|
if (stmt.type === "UPDATE") return materializeUpdateValidationCandidates(stmt, client, options, cacheContext, tempTables);
|
|
9555
10314
|
let rows;
|
|
10315
|
+
let sourceRows;
|
|
10316
|
+
let evaluationTypes;
|
|
9556
10317
|
if (stmt.type === "INSERT" || stmt.type === "UPSERT") {
|
|
10318
|
+
assertInsertCheckRefs(stmt, stmt.fields);
|
|
10319
|
+
evaluationTypes = new Map(stmt.fields.map((field) => [field, infoByCode.get(field)?.fieldType ?? ""]));
|
|
10320
|
+
assertCheckComparisonTypes(stmt, evaluationTypes);
|
|
9557
10321
|
rows = stmt.values.map((row) => row.map(
|
|
9558
10322
|
(value, i) => value.type === "CASE_VALUE" ? evalCaseWhenValue(value.expr, {}, infoByCode.get(stmt.fields[i])?.fieldType) : value
|
|
9559
10323
|
));
|
|
9560
10324
|
} else {
|
|
9561
|
-
const selectResult = tempTables && tempTables.size > 0 ? await executeQueryWithCte(stmt.select, client, { ...options, onLimitReached: "error" }, tempTables, cacheContext) : await executeSelect(stmt.select, client, { ...options, onLimitReached: "error" }, cacheContext);
|
|
9562
|
-
|
|
10325
|
+
const selectResult = tempTables && tempTables.size > 0 ? await executeQueryWithCte(stmt.select, client, { ...options, onLimitReached: "error" }, tempTables, cacheContext) : await executeSelect(stmt.select, client, { ...options, onLimitReached: "error" }, cacheContext, void 0, true);
|
|
10326
|
+
const hasChecks = (stmt.checkGroups?.length ?? 0) > 0;
|
|
10327
|
+
if (selectResult.columns.length < stmt.fields.length || !hasChecks && selectResult.columns.length !== stmt.fields.length) {
|
|
9563
10328
|
throw new Error(`SELECT \u306E\u5217\u6570\uFF08${selectResult.columns.length}\uFF09\u3068 DML \u306E\u30D5\u30A3\u30FC\u30EB\u30C9\u6570\uFF08${stmt.fields.length}\uFF09\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093`);
|
|
9564
10329
|
}
|
|
10330
|
+
if (hasChecks && new Set(selectResult.columns).size !== selectResult.columns.length) {
|
|
10331
|
+
throw customCheckParseError("CHECK \u4ED8\u304D DML \u30BD\u30FC\u30B9 SELECT \u306E\u51FA\u529B\u540D\u306F\u4E00\u610F\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059");
|
|
10332
|
+
}
|
|
10333
|
+
assertInsertCheckRefs(stmt, selectResult.columns);
|
|
10334
|
+
sourceRows = selectResult.rows;
|
|
10335
|
+
const meta = materializedMetaBySelectResult.get(selectResult);
|
|
10336
|
+
evaluationTypes = new Map(selectResult.columns.map((column) => {
|
|
10337
|
+
const columnMeta = meta?.get(column);
|
|
10338
|
+
const type = columnMeta?.fieldType ?? (columnMeta?.semantics?.compareMode === "number" || columnMeta?.sortKind === "number" ? "NUMBER" : "SINGLE_LINE_TEXT");
|
|
10339
|
+
return [column, type];
|
|
10340
|
+
}));
|
|
10341
|
+
assertCheckComparisonTypes(stmt, evaluationTypes);
|
|
9565
10342
|
rows = selectResult.rows.map((row) => selectResult.columns.map((column) => row[column] ?? ""));
|
|
9566
10343
|
}
|
|
9567
10344
|
const candidates = rows.map((values, index) => ({
|
|
@@ -9570,7 +10347,11 @@ async function materializeValidationCandidates(stmt, operation, client, options,
|
|
|
9570
10347
|
mode: "create",
|
|
9571
10348
|
payload: new Map(stmt.fields.map((field, i) => [field, values[i]])),
|
|
9572
10349
|
preErrors: [],
|
|
9573
|
-
record: {}
|
|
10350
|
+
record: {},
|
|
10351
|
+
evaluationRow: sourceRows?.[index] ?? Object.fromEntries(
|
|
10352
|
+
stmt.fields.map((field, i) => [field, renderValidationValue(values[i])])
|
|
10353
|
+
),
|
|
10354
|
+
evaluationFieldTypes: evaluationTypes
|
|
9574
10355
|
}));
|
|
9575
10356
|
if (stmt.type !== "UPSERT" && stmt.type !== "UPSERT_SELECT") return candidates;
|
|
9576
10357
|
for (const key of stmt.keyFields) {
|
|
@@ -9599,26 +10380,74 @@ async function materializeValidationCandidates(stmt, operation, client, options,
|
|
|
9599
10380
|
});
|
|
9600
10381
|
return candidates;
|
|
9601
10382
|
}
|
|
10383
|
+
function checkRefs(stmt) {
|
|
10384
|
+
return stmt.checkGroups ? collectCheckFieldRefs(stmt.checkGroups) : [];
|
|
10385
|
+
}
|
|
10386
|
+
function assertInsertCheckRefs(stmt, available) {
|
|
10387
|
+
const names = new Set(available);
|
|
10388
|
+
for (const ref of checkRefs(stmt)) {
|
|
10389
|
+
if (ref.tableAlias !== null) {
|
|
10390
|
+
throw customCheckParseError(`CHECK \u306E\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.tableAlias}.${ref.field} \u306F\u3053\u306E\u8A55\u4FA1\u884C\u3067\u306F\u4FEE\u98FE\u3067\u304D\u307E\u305B\u3093`);
|
|
10391
|
+
}
|
|
10392
|
+
if (!names.has(ref.field)) {
|
|
10393
|
+
throw customCheckParseError(`CHECK \u306E\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u306F\u8A55\u4FA1\u884C\u306B\u5B58\u5728\u3057\u307E\u305B\u3093`);
|
|
10394
|
+
}
|
|
10395
|
+
}
|
|
10396
|
+
}
|
|
10397
|
+
var CHECK_UNSUPPORTED_COMPARISON_TYPES = /* @__PURE__ */ new Set([
|
|
10398
|
+
"CHECK_BOX",
|
|
10399
|
+
"MULTI_SELECT",
|
|
10400
|
+
"USER_SELECT",
|
|
10401
|
+
"ORGANIZATION_SELECT",
|
|
10402
|
+
"GROUP_SELECT",
|
|
10403
|
+
"FILE",
|
|
10404
|
+
"KSQL_ARRAY"
|
|
10405
|
+
]);
|
|
10406
|
+
function assertCheckComparisonTypes(stmt, types) {
|
|
10407
|
+
if (!stmt.checkGroups) return;
|
|
10408
|
+
for (const ref of collectCheckComparisonFieldRefs(stmt.checkGroups)) {
|
|
10409
|
+
const key = ref.tableAlias ? `${ref.tableAlias}.${ref.field}` : ref.field;
|
|
10410
|
+
const type = types.get(key) ?? types.get(ref.field);
|
|
10411
|
+
if (CHECK_UNSUPPORTED_COMPARISON_TYPES.has(type ?? "")) {
|
|
10412
|
+
throw customCheckParseError(`CHECK \u306E\u6BD4\u8F03\u3067\u306F ${type} \u30D5\u30A3\u30FC\u30EB\u30C9 ${key} \u3092\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`);
|
|
10413
|
+
}
|
|
10414
|
+
}
|
|
10415
|
+
}
|
|
9602
10416
|
async function materializeUpdateValidationCandidates(stmt, client, options, cacheContext, tempTables) {
|
|
9603
10417
|
if (stmt.from) return materializeUpdateFromValidationCandidates(stmt, stmt.from, client, options, cacheContext, tempTables);
|
|
9604
10418
|
await resolveSetSubqueries(stmt.assignments, client, options, cacheContext);
|
|
9605
10419
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
10420
|
+
const checkTargetFields = assertUpdateCheckRefs(stmt, fieldTypes);
|
|
10421
|
+
assertCheckComparisonTypes(stmt, updateEvaluationTypes(fieldTypes, stmt.appId));
|
|
9606
10422
|
let records;
|
|
10423
|
+
let evaluationById = /* @__PURE__ */ new Map();
|
|
9607
10424
|
if (hasRowDependentAssignment(stmt)) {
|
|
9608
10425
|
const getParams = updateToGetQueryForArith(stmt);
|
|
9609
|
-
const
|
|
10426
|
+
const fields = [.../* @__PURE__ */ new Set([...getParams.fields, ...checkTargetFields])];
|
|
10427
|
+
const resolved = await fetchRecordsForSharedPlan(client.getRecords, getParams.app, getParams.query, fields, {
|
|
9610
10428
|
maxRecords: options.maxRecords ?? 1e4,
|
|
9611
10429
|
parallel: options.fetchParallel ?? 1,
|
|
9612
10430
|
onLimit: "error"
|
|
9613
10431
|
});
|
|
10432
|
+
evaluationById = new Map(resolved.records.map((record) => [Number(record["$id"]?.value), record]));
|
|
9614
10433
|
records = updateToPutBatchesArith(stmt, resolved.records, fieldTypes).flatMap((batch) => batch.records);
|
|
9615
10434
|
} else {
|
|
9616
10435
|
const getParams = updateToGetQuery(stmt);
|
|
9617
|
-
|
|
9618
|
-
|
|
9619
|
-
|
|
9620
|
-
|
|
9621
|
-
|
|
10436
|
+
if (checkTargetFields.length > 0) {
|
|
10437
|
+
const resolved = await fetchRecordsForSharedPlan(client.getRecords, getParams.app, getParams.query, [.../* @__PURE__ */ new Set(["$id", ...checkTargetFields])], {
|
|
10438
|
+
maxRecords: options.maxRecords ?? 1e4,
|
|
10439
|
+
parallel: options.fetchParallel ?? 1,
|
|
10440
|
+
onLimit: "error"
|
|
10441
|
+
});
|
|
10442
|
+
evaluationById = new Map(resolved.records.map((record) => [Number(record["$id"]?.value), record]));
|
|
10443
|
+
records = updateToPutBatches(stmt, [...evaluationById.keys()], fieldTypes).flatMap((batch) => batch.records);
|
|
10444
|
+
} else {
|
|
10445
|
+
const resolved = await resolveDmlTargetIds(client.getRecords, getParams.app, getParams.query, {
|
|
10446
|
+
maxRecords: options.maxRecords ?? 1e4,
|
|
10447
|
+
parallel: options.fetchParallel ?? 1
|
|
10448
|
+
});
|
|
10449
|
+
records = updateToPutBatches(stmt, resolved.ids, fieldTypes).flatMap((batch) => batch.records);
|
|
10450
|
+
}
|
|
9622
10451
|
}
|
|
9623
10452
|
return records.sort((a, b) => a.id - b.id).map((entry, index) => ({
|
|
9624
10453
|
rowNumber: index + 1,
|
|
@@ -9627,13 +10456,48 @@ async function materializeUpdateValidationCandidates(stmt, client, options, cach
|
|
|
9627
10456
|
payload: new Map([["$id", String(entry.id)], ...stmt.assignments.map((a) => [a.field, entry.record[a.field]?.value ?? ""])]),
|
|
9628
10457
|
preErrors: [],
|
|
9629
10458
|
record: entry.record,
|
|
9630
|
-
targetId: entry.id
|
|
10459
|
+
targetId: entry.id,
|
|
10460
|
+
evaluationRow: updateEvaluationRow(evaluationById.get(entry.id), stmt.appId),
|
|
10461
|
+
evaluationFieldTypes: updateEvaluationTypes(fieldTypes, stmt.appId)
|
|
9631
10462
|
}));
|
|
9632
10463
|
}
|
|
10464
|
+
function assertUpdateCheckRefs(stmt, targetTypes) {
|
|
10465
|
+
if (stmt.from) return [];
|
|
10466
|
+
const fields = /* @__PURE__ */ new Set();
|
|
10467
|
+
for (const ref of checkRefs(stmt)) {
|
|
10468
|
+
if (ref.tableAlias !== null && ref.tableAlias.toLowerCase() !== `app${stmt.appId}`.toLowerCase()) {
|
|
10469
|
+
throw customCheckParseError(`CHECK \u306E\u4FEE\u98FE\u5B50 ${ref.tableAlias} \u306F\u66F4\u65B0\u5148 APP${stmt.appId} \u3067\u306F\u3042\u308A\u307E\u305B\u3093`);
|
|
10470
|
+
}
|
|
10471
|
+
if (ref.field !== "$id" && !targetTypes.has(ref.field)) {
|
|
10472
|
+
throw customCheckParseError(`CHECK \u306E\u30BF\u30FC\u30B2\u30C3\u30C8\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u306F\u5B58\u5728\u3057\u307E\u305B\u3093`);
|
|
10473
|
+
}
|
|
10474
|
+
fields.add(ref.field);
|
|
10475
|
+
}
|
|
10476
|
+
return [...fields];
|
|
10477
|
+
}
|
|
10478
|
+
function updateEvaluationRow(record, appId) {
|
|
10479
|
+
if (!record) return {};
|
|
10480
|
+
const plain = flatten(record, null);
|
|
10481
|
+
return Object.fromEntries([
|
|
10482
|
+
...Object.entries(plain),
|
|
10483
|
+
...Object.entries(plain).map(([field, value]) => [`APP${appId}.${field}`, value])
|
|
10484
|
+
]);
|
|
10485
|
+
}
|
|
10486
|
+
function updateEvaluationTypes(types, appId) {
|
|
10487
|
+
return new Map([
|
|
10488
|
+
...types,
|
|
10489
|
+
...[...types].map(([field, type]) => [`APP${appId}.${field}`, type]),
|
|
10490
|
+
["$id", "RECORD_NUMBER"],
|
|
10491
|
+
[`APP${appId}.$id`, "RECORD_NUMBER"]
|
|
10492
|
+
]);
|
|
10493
|
+
}
|
|
9633
10494
|
async function materializeUpdateFromValidationCandidates(stmt, from, client, options, cacheContext, tempTables) {
|
|
10495
|
+
const scope = await resolveUpdateFromCheckScope(stmt, from, client, cacheContext, tempTables);
|
|
10496
|
+
assertCheckComparisonTypes(stmt, scope.evaluationTypes);
|
|
9634
10497
|
const matched = await resolveUpdateFromMatchedRecords(stmt, from, client, options, cacheContext, tempTables);
|
|
9635
10498
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
9636
10499
|
const records = updateFromToPutBatches(stmt, matched, fieldTypes).flatMap((batch) => batch.records);
|
|
10500
|
+
const matchedById = new Map(matched.map((pair) => [Number(pair.target["$id"]?.value), pair]));
|
|
9637
10501
|
return records.sort((a, b) => a.id - b.id).map((entry, index) => ({
|
|
9638
10502
|
rowNumber: index + 1,
|
|
9639
10503
|
operation: "UPDATE",
|
|
@@ -9641,7 +10505,9 @@ async function materializeUpdateFromValidationCandidates(stmt, from, client, opt
|
|
|
9641
10505
|
payload: new Map([["$id", String(entry.id)], ...stmt.assignments.map((a) => [a.field, entry.record[a.field]?.value ?? ""])]),
|
|
9642
10506
|
preErrors: [],
|
|
9643
10507
|
record: entry.record,
|
|
9644
|
-
targetId: entry.id
|
|
10508
|
+
targetId: entry.id,
|
|
10509
|
+
evaluationRow: updateFromEvaluationRow(matchedById.get(entry.id), stmt.appId, from.alias),
|
|
10510
|
+
evaluationFieldTypes: scope.evaluationTypes
|
|
9645
10511
|
}));
|
|
9646
10512
|
}
|
|
9647
10513
|
var UPDATE_FROM_KEY_CHUNK_SIZE = UPSERT_IN_CHUNK_SIZE;
|
|
@@ -9655,7 +10521,8 @@ var UPDATE_FROM_UNSUPPORTED_SOURCE_TYPES = /* @__PURE__ */ new Set([
|
|
|
9655
10521
|
]);
|
|
9656
10522
|
async function resolveUpdateFromMatchedRecords(stmt, from, client, options, cacheContext, tempTables) {
|
|
9657
10523
|
const joinKind = await resolveUpdateFromTargetJoinKind(stmt, from, client, cacheContext);
|
|
9658
|
-
const
|
|
10524
|
+
const checkScope = await resolveUpdateFromCheckScope(stmt, from, client, cacheContext, tempTables);
|
|
10525
|
+
const sourceFields = [...new Set(stmt.assignments.filter((a) => a.value.type === "SOURCE_FIELD").map((a) => a.value.type === "SOURCE_FIELD" ? a.value.field : "").concat(checkScope.sourceFields))];
|
|
9659
10526
|
const requiredSourceFields = [.../* @__PURE__ */ new Set([from.joinKeyField, ...sourceFields])];
|
|
9660
10527
|
const sourceRows = await loadUpdateFromSourceRows(
|
|
9661
10528
|
from,
|
|
@@ -9667,6 +10534,7 @@ async function resolveUpdateFromMatchedRecords(stmt, from, client, options, cach
|
|
|
9667
10534
|
tempTables
|
|
9668
10535
|
);
|
|
9669
10536
|
const sourceByKey = /* @__PURE__ */ new Map();
|
|
10537
|
+
const sourceQueryByKey = /* @__PURE__ */ new Map();
|
|
9670
10538
|
for (const row of sourceRows) {
|
|
9671
10539
|
if (!Object.prototype.hasOwnProperty.call(row, from.joinKeyField)) {
|
|
9672
10540
|
throw new Error(`ArgumentError: UPDATE ... FROM source column ${from.joinKeyField} does not exist.`);
|
|
@@ -9676,15 +10544,16 @@ async function resolveUpdateFromMatchedRecords(stmt, from, client, options, cach
|
|
|
9676
10544
|
throw new Error(`ArgumentError: UPDATE ... FROM source has multiple rows for normalized key ${key}.`);
|
|
9677
10545
|
}
|
|
9678
10546
|
sourceByKey.set(key, row);
|
|
10547
|
+
sourceQueryByKey.set(key, String(row[from.joinKeyField]).trim());
|
|
9679
10548
|
}
|
|
9680
10549
|
if (sourceByKey.size === 0) return [];
|
|
9681
10550
|
const maxRecords = options.maxRecords ?? 1e4;
|
|
9682
|
-
const targetFields = collectUpdateFromTargetFields(stmt);
|
|
9683
|
-
const filterQuery = from.targetFilter === null ? "" : updateToGetQuery({ ...stmt, from: null, where: from.targetFilter }).query;
|
|
10551
|
+
const targetFields = [.../* @__PURE__ */ new Set([...collectUpdateFromTargetFields(stmt), ...checkScope.targetFields])];
|
|
10552
|
+
const filterQuery = from.targetFilter === null ? "" : updateToGetQuery({ ...stmt, from: null, where: from.targetFilter, checkGroups: void 0 }).query;
|
|
9684
10553
|
const targetRecords = [];
|
|
9685
10554
|
const seenTargetIds = /* @__PURE__ */ new Set();
|
|
9686
10555
|
let fetchedTargetCount = 0;
|
|
9687
|
-
for (const keys of splitChunks([...
|
|
10556
|
+
for (const keys of splitChunks([...sourceQueryByKey.values()], UPDATE_FROM_KEY_CHUNK_SIZE)) {
|
|
9688
10557
|
const keyQuery = `${from.targetJoinField} in (${keys.map(sqlQuote).join(",")})`;
|
|
9689
10558
|
const query = filterQuery ? `(${keyQuery}) and (${filterQuery})` : keyQuery;
|
|
9690
10559
|
const resolved = await fetchRecordsForSharedPlan(
|
|
@@ -9785,37 +10654,75 @@ function normalizeUpdateFromJoinKey(raw, kind, side) {
|
|
|
9785
10654
|
}
|
|
9786
10655
|
if (kind === "number" && side === "target" && raw === "") return null;
|
|
9787
10656
|
if (kind === "id") {
|
|
9788
|
-
const
|
|
9789
|
-
const id = Number(
|
|
9790
|
-
if (
|
|
10657
|
+
const text = raw.trim();
|
|
10658
|
+
const id = Number(text);
|
|
10659
|
+
if (text === "" || !Number.isSafeInteger(id) || id <= 0) {
|
|
9791
10660
|
throw new Error(`ArgumentError: UPDATE ... FROM ${side} key must be a positive safe integer: ${raw}`);
|
|
9792
10661
|
}
|
|
9793
10662
|
return String(id);
|
|
9794
10663
|
}
|
|
9795
|
-
const
|
|
9796
|
-
if (
|
|
10664
|
+
const decimal = parseExactDecimal(raw);
|
|
10665
|
+
if (decimal === null) {
|
|
9797
10666
|
throw new Error(`ArgumentError: UPDATE ... FROM ${side} key must be a finite decimal: ${raw}`);
|
|
9798
10667
|
}
|
|
9799
|
-
|
|
9800
|
-
|
|
9801
|
-
|
|
9802
|
-
|
|
9803
|
-
|
|
10668
|
+
return JSON.stringify(decimal);
|
|
10669
|
+
}
|
|
10670
|
+
async function executeCheckedPlainDml(stmt, client, options, cacheContext, tempTables) {
|
|
10671
|
+
const prepared = await prepareDmlValidation(
|
|
10672
|
+
stmt,
|
|
10673
|
+
client,
|
|
10674
|
+
options,
|
|
10675
|
+
cacheContext,
|
|
10676
|
+
tempTables,
|
|
10677
|
+
1,
|
|
10678
|
+
false,
|
|
10679
|
+
false
|
|
10680
|
+
);
|
|
10681
|
+
if (prepared.result.errors.length > 0) {
|
|
10682
|
+
const first = prepared.result.errors[0];
|
|
10683
|
+
throw new Error(
|
|
10684
|
+
`DmlValidationError: ${first["$err_code"]} ${first["$err_message"]} (row=${first["$err_row"]}, field=${first["$err_field"]})`
|
|
10685
|
+
);
|
|
10686
|
+
}
|
|
10687
|
+
const candidates = prepared.candidates;
|
|
10688
|
+
const confirmOperation = stmt.type.startsWith("INSERT") ? "INSERT" : "UPDATE";
|
|
10689
|
+
if (options.confirm && candidates.length > 0) {
|
|
10690
|
+
const ok = await options.confirm(candidates.length, confirmOperation);
|
|
10691
|
+
if (!ok) throw new OperationCancelledError(confirmOperation, candidates.length);
|
|
10692
|
+
}
|
|
10693
|
+
if (stmt.type === "INSERT" || stmt.type === "INSERT_SELECT") {
|
|
10694
|
+
const createdIds = [];
|
|
10695
|
+
for (let i = 0; i < candidates.length; i += 100) {
|
|
10696
|
+
const response = await client.postRecords({ app: stmt.appId, records: candidates.slice(i, i + 100).map((c) => c.record) });
|
|
10697
|
+
createdIds.push(response.ids);
|
|
10698
|
+
}
|
|
10699
|
+
return { type: "INSERT", createdIds, insertedCount: createdIds.flat().length };
|
|
10700
|
+
}
|
|
10701
|
+
if (stmt.type === "UPDATE") {
|
|
10702
|
+
const updates2 = candidates.map((candidate) => ({ id: candidate.targetId, record: candidate.record }));
|
|
10703
|
+
for (let i = 0; i < updates2.length; i += 100) await client.putRecords({ app: stmt.appId, records: updates2.slice(i, i + 100) });
|
|
10704
|
+
return { type: "UPDATE", updatedCount: updates2.length };
|
|
9804
10705
|
}
|
|
9805
|
-
|
|
9806
|
-
|
|
9807
|
-
|
|
9808
|
-
|
|
9809
|
-
|
|
9810
|
-
|
|
10706
|
+
const inserts = candidates.filter((candidate) => candidate.mode === "create");
|
|
10707
|
+
const updates = candidates.filter((candidate) => candidate.mode === "update").map((candidate) => ({ id: candidate.targetId, record: candidate.record }));
|
|
10708
|
+
let insertedCount = 0;
|
|
10709
|
+
for (let i = 0; i < inserts.length; i += 100) {
|
|
10710
|
+
const response = await client.postRecords({ app: stmt.appId, records: inserts.slice(i, i + 100).map((c) => c.record) });
|
|
10711
|
+
insertedCount += response.ids.length;
|
|
10712
|
+
}
|
|
10713
|
+
for (let i = 0; i < updates.length; i += 100) await client.putRecords({ app: stmt.appId, records: updates.slice(i, i + 100) });
|
|
10714
|
+
return { type: "UPSERT", insertedCount, updatedCount: updates.length };
|
|
9811
10715
|
}
|
|
9812
10716
|
async function executeInsert(stmt, client, options, cacheContext) {
|
|
10717
|
+
if (stmt.checkGroups?.length) return executeCheckedPlainDml(stmt, client, options, cacheContext);
|
|
9813
10718
|
if (stmt.subtableCode) {
|
|
9814
10719
|
return executeInsertSubtable(stmt, client, options, cacheContext);
|
|
9815
10720
|
}
|
|
9816
|
-
await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
10721
|
+
const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
10722
|
+
const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
|
|
9817
10723
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
9818
10724
|
const batches = insertToPostBatches(stmt, fieldTypes);
|
|
10725
|
+
assertValidDmlRecords(batches.flatMap((batch) => batch.records), stmt.fields, fieldInfos, numberPrecision);
|
|
9819
10726
|
const createdIds = [];
|
|
9820
10727
|
for (const batch of batches) {
|
|
9821
10728
|
const res = await client.postRecords(batch);
|
|
@@ -9828,7 +10735,9 @@ async function executeInsert(stmt, client, options, cacheContext) {
|
|
|
9828
10735
|
};
|
|
9829
10736
|
}
|
|
9830
10737
|
async function executeInsertSelect(stmt, client, options, cacheContext, cteCache) {
|
|
9831
|
-
|
|
10738
|
+
if (stmt.checkGroups?.length) return executeCheckedPlainDml(stmt, client, options, cacheContext, cteCache);
|
|
10739
|
+
const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
10740
|
+
const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
|
|
9832
10741
|
const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
|
|
9833
10742
|
const { rows, columns } = selectResult;
|
|
9834
10743
|
if (columns.length !== stmt.fields.length) {
|
|
@@ -9850,6 +10759,7 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
|
|
|
9850
10759
|
});
|
|
9851
10760
|
return record;
|
|
9852
10761
|
});
|
|
10762
|
+
assertValidDmlRecords(allRecords, stmt.fields, fieldInfos, numberPrecision);
|
|
9853
10763
|
const createdIds = [];
|
|
9854
10764
|
for (let i = 0; i < allRecords.length; i += 100) {
|
|
9855
10765
|
const batch = allRecords.slice(i, i + 100);
|
|
@@ -9863,16 +10773,25 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
|
|
|
9863
10773
|
};
|
|
9864
10774
|
}
|
|
9865
10775
|
async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
|
|
10776
|
+
if (stmt.checkGroups?.length) return executeCheckedPlainDml(stmt, client, options, cacheContext, tempTables);
|
|
9866
10777
|
if (stmt.subtableCode) {
|
|
9867
10778
|
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
9868
10779
|
return executeUpdateSubtable(stmt, client, options, cacheContext);
|
|
9869
10780
|
}
|
|
9870
|
-
await loadWritableTopLevelDmlFields(
|
|
10781
|
+
const fieldInfos = await loadWritableTopLevelDmlFields(
|
|
9871
10782
|
stmt.appId,
|
|
9872
10783
|
stmt.assignments.map((assignment) => assignment.field),
|
|
9873
10784
|
client,
|
|
9874
10785
|
cacheContext
|
|
9875
10786
|
);
|
|
10787
|
+
const targetFields = stmt.assignments.map((assignment) => assignment.field);
|
|
10788
|
+
const numberPrecision = await loadNumberPrecisionForTargets(
|
|
10789
|
+
stmt.appId,
|
|
10790
|
+
targetFields,
|
|
10791
|
+
fieldInfos,
|
|
10792
|
+
client,
|
|
10793
|
+
cacheContext
|
|
10794
|
+
);
|
|
9876
10795
|
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
9877
10796
|
if (stmt.from != null) {
|
|
9878
10797
|
return executeUpdateFrom(stmt, stmt.from, client, options, cacheContext, tempTables);
|
|
@@ -9890,11 +10809,12 @@ async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
|
|
|
9890
10809
|
{ maxRecords, parallel: options.fetchParallel ?? 1 }
|
|
9891
10810
|
);
|
|
9892
10811
|
const records = resolved2.records;
|
|
10812
|
+
const batches2 = updateToPutBatchesArith(stmt, records, fieldTypes);
|
|
10813
|
+
assertValidDmlRecords(batches2.flatMap((batch) => batch.records.map((entry) => entry.record)), targetFields, fieldInfos, numberPrecision);
|
|
9893
10814
|
if (options.confirm) {
|
|
9894
10815
|
const ok = await options.confirm(records.length, "UPDATE");
|
|
9895
10816
|
if (!ok) throw new OperationCancelledError("UPDATE", records.length);
|
|
9896
10817
|
}
|
|
9897
|
-
const batches2 = updateToPutBatchesArith(stmt, records, fieldTypes);
|
|
9898
10818
|
for (const batch of batches2) {
|
|
9899
10819
|
await client.putRecords(batch);
|
|
9900
10820
|
}
|
|
@@ -9908,11 +10828,12 @@ async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
|
|
|
9908
10828
|
{ maxRecords, parallel: options.fetchParallel ?? 1 }
|
|
9909
10829
|
);
|
|
9910
10830
|
const ids = resolved.ids;
|
|
10831
|
+
const batches = updateToPutBatches(stmt, ids, fieldTypes);
|
|
10832
|
+
assertValidDmlRecords(batches.flatMap((batch) => batch.records.map((entry) => entry.record)), targetFields, fieldInfos, numberPrecision);
|
|
9911
10833
|
if (options.confirm) {
|
|
9912
10834
|
const ok = await options.confirm(ids.length, "UPDATE");
|
|
9913
10835
|
if (!ok) throw new OperationCancelledError("UPDATE", ids.length);
|
|
9914
10836
|
}
|
|
9915
|
-
const batches = updateToPutBatches(stmt, ids, fieldTypes);
|
|
9916
10837
|
for (const batch of batches) {
|
|
9917
10838
|
await client.putRecords(batch);
|
|
9918
10839
|
}
|
|
@@ -9920,12 +10841,16 @@ async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
|
|
|
9920
10841
|
}
|
|
9921
10842
|
async function executeUpdateFrom(stmt, from, client, options, cacheContext, tempTables) {
|
|
9922
10843
|
const matched = await resolveUpdateFromMatchedRecords(stmt, from, client, options, cacheContext, tempTables);
|
|
10844
|
+
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
10845
|
+
const batches = updateFromToPutBatches(stmt, matched, fieldTypes);
|
|
10846
|
+
const targetFields = stmt.assignments.map((assignment) => assignment.field);
|
|
10847
|
+
const fieldInfos = await getFieldsCached(stmt.appId, client, cacheContext);
|
|
10848
|
+
const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, targetFields, fieldInfos, client, cacheContext);
|
|
10849
|
+
assertValidDmlRecords(batches.flatMap((batch) => batch.records.map((entry) => entry.record)), targetFields, fieldInfos, numberPrecision);
|
|
9923
10850
|
if (options.confirm) {
|
|
9924
10851
|
const ok = await options.confirm(matched.length, "UPDATE");
|
|
9925
10852
|
if (!ok) throw new OperationCancelledError("UPDATE", matched.length);
|
|
9926
10853
|
}
|
|
9927
|
-
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
9928
|
-
const batches = updateFromToPutBatches(stmt, matched, fieldTypes);
|
|
9929
10854
|
for (const batch of batches) await client.putRecords(batch);
|
|
9930
10855
|
return { type: "UPDATE", updatedCount: matched.length };
|
|
9931
10856
|
}
|
|
@@ -9974,7 +10899,9 @@ async function executeDelete(stmt, client, options, cacheContext) {
|
|
|
9974
10899
|
return { type: "DELETE", deletedCount: ids.length };
|
|
9975
10900
|
}
|
|
9976
10901
|
async function executeUpsert(stmt, client, options, cacheContext) {
|
|
9977
|
-
|
|
10902
|
+
if (stmt.checkGroups?.length) return executeCheckedPlainDml(stmt, client, options, cacheContext);
|
|
10903
|
+
const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
10904
|
+
const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
|
|
9978
10905
|
const toInsert = [];
|
|
9979
10906
|
const toUpdate = [];
|
|
9980
10907
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
@@ -9983,7 +10910,7 @@ async function executeUpsert(stmt, client, options, cacheContext) {
|
|
|
9983
10910
|
const idx = stmt.fields.indexOf(key);
|
|
9984
10911
|
if (idx === -1) throw new Error(`ON DUPLICATE \u306E\u30AD\u30FC\u300C${key}\u300D\u304C INSERT \u30D5\u30A3\u30FC\u30EB\u30C9\u306B\u542B\u307E\u308C\u3066\u3044\u307E\u305B\u3093`);
|
|
9985
10912
|
const val = row[idx];
|
|
9986
|
-
return val.type === "STRING" ? val.value : val.type === "NUMBER" ?
|
|
10913
|
+
return val.type === "STRING" ? val.value : val.type === "NUMBER" ? numberLiteralText(val) : val.type === "CASE_VALUE" ? evalCaseWhen(val.expr, {}) : val.elements.map((e) => e.value).join(",");
|
|
9987
10914
|
})
|
|
9988
10915
|
);
|
|
9989
10916
|
const targetIndex = await resolveUpsertTargets(stmt.appId, stmt.keyFields, rowKeyValues, client, options, fieldTypes);
|
|
@@ -10004,6 +10931,12 @@ async function executeUpsert(stmt, client, options, cacheContext) {
|
|
|
10004
10931
|
toInsert.push(record);
|
|
10005
10932
|
}
|
|
10006
10933
|
});
|
|
10934
|
+
assertValidDmlRecords(
|
|
10935
|
+
[...toInsert, ...toUpdate.map((entry) => entry.record)],
|
|
10936
|
+
stmt.fields,
|
|
10937
|
+
fieldInfos,
|
|
10938
|
+
numberPrecision
|
|
10939
|
+
);
|
|
10007
10940
|
if (options.confirm && toInsert.length + toUpdate.length > 0) {
|
|
10008
10941
|
const total = toInsert.length + toUpdate.length;
|
|
10009
10942
|
const ok = await options.confirm(total, "UPDATE");
|
|
@@ -10279,14 +11212,14 @@ function buildSubtableReorderPutParams(appId, parentId, revision, subtableCode,
|
|
|
10279
11212
|
}
|
|
10280
11213
|
function evalAssignmentValueForSubtable(value, row, resolveFieldType) {
|
|
10281
11214
|
if (value.type === "STRING") return value.value;
|
|
10282
|
-
if (value.type === "NUMBER") return
|
|
11215
|
+
if (value.type === "NUMBER") return numberLiteralText(value);
|
|
10283
11216
|
if (value.type === "ARITH") return String(evalArithExpr(value, row));
|
|
10284
11217
|
if (value.type === "CASE_VALUE") return evalCaseWhen(value.expr, row, resolveFieldType);
|
|
10285
11218
|
throw new Error(`${value.type} \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE \u306E\u5024\u3068\u3057\u3066\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`);
|
|
10286
11219
|
}
|
|
10287
11220
|
function valueToString(value) {
|
|
10288
11221
|
if (value.type === "STRING") return value.value;
|
|
10289
|
-
if (value.type === "NUMBER") return
|
|
11222
|
+
if (value.type === "NUMBER") return numberLiteralText(value);
|
|
10290
11223
|
if (value.type === "CASE_VALUE") return evalCaseWhen(value.expr, {});
|
|
10291
11224
|
return value.elements.map((e) => e.value).join(",");
|
|
10292
11225
|
}
|
|
@@ -10401,7 +11334,9 @@ function evalOrderKeyForRow(key, row) {
|
|
|
10401
11334
|
}
|
|
10402
11335
|
}
|
|
10403
11336
|
async function executeUpsertSelect(stmt, client, options, cacheContext, cteCache) {
|
|
10404
|
-
|
|
11337
|
+
if (stmt.checkGroups?.length) return executeCheckedPlainDml(stmt, client, options, cacheContext, cteCache);
|
|
11338
|
+
const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
11339
|
+
const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
|
|
10405
11340
|
const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
|
|
10406
11341
|
const { rows, columns } = selectResult;
|
|
10407
11342
|
if (columns.length !== stmt.fields.length) {
|
|
@@ -10424,6 +11359,7 @@ async function executeUpsertSelect(stmt, client, options, cacheContext, cteCache
|
|
|
10424
11359
|
});
|
|
10425
11360
|
return record;
|
|
10426
11361
|
});
|
|
11362
|
+
assertValidDmlRecords(records, stmt.fields, fieldInfos, numberPrecision);
|
|
10427
11363
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
10428
11364
|
const rowKeyValues = records.map(
|
|
10429
11365
|
(record) => stmt.keyFields.map((key) => String(record[key]?.value ?? ""))
|
|
@@ -11146,6 +12082,7 @@ function collectArithRefFields(stmt) {
|
|
|
11146
12082
|
for (const { value } of stmt.assignments) {
|
|
11147
12083
|
if (value.type === "ARITH") collectArithNodeRefs(value, refs);
|
|
11148
12084
|
if (value.type === "STRING_FUNC") collectArithNodeRefs(value, refs);
|
|
12085
|
+
if (value.type === "SCALAR_ARITH" || value.type === "CONCAT_OP") collectScalarNodeRefs(value, refs);
|
|
11149
12086
|
}
|
|
11150
12087
|
return [...refs];
|
|
11151
12088
|
}
|
|
@@ -11160,10 +12097,74 @@ function collectArithNodeRefs(node, out) {
|
|
|
11160
12097
|
}
|
|
11161
12098
|
if (node.type === "STRING_FUNC") {
|
|
11162
12099
|
for (const arg of node.args) {
|
|
11163
|
-
if (arg.type !== "
|
|
11164
|
-
|
|
12100
|
+
if (arg.type !== "AGG_REF" && arg.type !== "AGG_ARITH") collectScalarNodeRefs(arg, out);
|
|
12101
|
+
}
|
|
12102
|
+
}
|
|
12103
|
+
}
|
|
12104
|
+
async function resolveUpdateFromCheckScope(stmt, from, client, cacheContext, tempTables) {
|
|
12105
|
+
const refs = checkRefs(stmt);
|
|
12106
|
+
const targetTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
12107
|
+
const sourceTableName = from.cteName;
|
|
12108
|
+
const sourceTypes = sourceTableName !== null ? new Map((tempTables?.get(sourceTableName)?.columns ?? []).map((column) => [
|
|
12109
|
+
column,
|
|
12110
|
+
tempTables?.get(sourceTableName)?.columnMeta?.get(column)?.fieldType ?? (tempTables?.get(sourceTableName)?.columnMeta?.get(column)?.semantics?.compareMode === "number" ? "NUMBER" : "SINGLE_LINE_TEXT")
|
|
12111
|
+
])) : await getFieldTypeMap(from.appId, client, cacheContext);
|
|
12112
|
+
const targetFields = /* @__PURE__ */ new Set();
|
|
12113
|
+
const sourceFields = /* @__PURE__ */ new Set();
|
|
12114
|
+
for (const ref of refs) {
|
|
12115
|
+
if (ref.tableAlias !== null) {
|
|
12116
|
+
if (ref.tableAlias.toLowerCase() === `app${stmt.appId}`.toLowerCase()) {
|
|
12117
|
+
if (ref.field !== "$id" && !targetTypes.has(ref.field)) throw customCheckParseError(`CHECK \u306E\u30BF\u30FC\u30B2\u30C3\u30C8\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u306F\u5B58\u5728\u3057\u307E\u305B\u3093`);
|
|
12118
|
+
targetFields.add(ref.field);
|
|
12119
|
+
} else if (ref.tableAlias.toLowerCase() === from.alias.toLowerCase()) {
|
|
12120
|
+
if (ref.field !== "$id" && !sourceTypes.has(ref.field)) throw customCheckParseError(`CHECK \u306E\u30BD\u30FC\u30B9\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u306F\u5B58\u5728\u3057\u307E\u305B\u3093`);
|
|
12121
|
+
sourceFields.add(ref.field);
|
|
12122
|
+
} else {
|
|
12123
|
+
throw customCheckParseError(`CHECK \u306E\u4FEE\u98FE\u5B50 ${ref.tableAlias} \u306F\u66F4\u65B0\u5148\u307E\u305F\u306F FROM alias \u3067\u306F\u3042\u308A\u307E\u305B\u3093`);
|
|
11165
12124
|
}
|
|
12125
|
+
continue;
|
|
11166
12126
|
}
|
|
12127
|
+
const inTarget = ref.field === "$id" || targetTypes.has(ref.field);
|
|
12128
|
+
const inSource = ref.field === "$id" || sourceTypes.has(ref.field);
|
|
12129
|
+
if (!inTarget) {
|
|
12130
|
+
throw customCheckParseError(`UPDATE FROM \u306E CHECK \u3067\u306F\u30BD\u30FC\u30B9\u5217 ${ref.field} \u3092\u4FEE\u98FE\u3057\u3066\u304F\u3060\u3055\u3044`);
|
|
12131
|
+
}
|
|
12132
|
+
if (inSource) {
|
|
12133
|
+
throw customCheckParseError(`UPDATE FROM \u306E CHECK \u306E\u975E\u4FEE\u98FE\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u306F\u66D6\u6627\u3067\u3059`);
|
|
12134
|
+
}
|
|
12135
|
+
targetFields.add(ref.field);
|
|
12136
|
+
}
|
|
12137
|
+
const evaluationTypes = /* @__PURE__ */ new Map();
|
|
12138
|
+
for (const [field, type] of targetTypes) {
|
|
12139
|
+
evaluationTypes.set(field, type);
|
|
12140
|
+
evaluationTypes.set(`APP${stmt.appId}.${field}`, type);
|
|
12141
|
+
}
|
|
12142
|
+
evaluationTypes.set("$id", "RECORD_NUMBER");
|
|
12143
|
+
evaluationTypes.set(`APP${stmt.appId}.$id`, "RECORD_NUMBER");
|
|
12144
|
+
for (const [field, type] of sourceTypes) evaluationTypes.set(`${from.alias}.${field}`, type);
|
|
12145
|
+
return { targetFields: [...targetFields], sourceFields: [...sourceFields], evaluationTypes };
|
|
12146
|
+
}
|
|
12147
|
+
function updateFromEvaluationRow(pair, appId, sourceAlias) {
|
|
12148
|
+
if (!pair) return {};
|
|
12149
|
+
const target = flatten(pair.target, null);
|
|
12150
|
+
return Object.fromEntries([
|
|
12151
|
+
...Object.entries(target),
|
|
12152
|
+
...Object.entries(target).map(([field, value]) => [`APP${appId}.${field}`, value]),
|
|
12153
|
+
...Object.entries(pair.source).map(([field, value]) => [`${sourceAlias}.${field}`, value])
|
|
12154
|
+
]);
|
|
12155
|
+
}
|
|
12156
|
+
function collectScalarNodeRefs(node, out) {
|
|
12157
|
+
if (node.type === "FIELD") {
|
|
12158
|
+
out.add(node.tableAlias ? `${node.tableAlias}.${node.field}` : node.field);
|
|
12159
|
+
return;
|
|
12160
|
+
}
|
|
12161
|
+
if (node.type === "STRING_FUNC") {
|
|
12162
|
+
for (const arg of node.args) if (arg.type !== "AGG_REF" && arg.type !== "AGG_ARITH") collectScalarNodeRefs(arg, out);
|
|
12163
|
+
return;
|
|
12164
|
+
}
|
|
12165
|
+
if (node.type === "SCALAR_ARITH" || node.type === "CONCAT_OP") {
|
|
12166
|
+
collectScalarNodeRefs(node.left, out);
|
|
12167
|
+
collectScalarNodeRefs(node.right, out);
|
|
11167
12168
|
}
|
|
11168
12169
|
}
|
|
11169
12170
|
function formatAssignment(a) {
|
|
@@ -11182,7 +12183,7 @@ function formatArithExprStr(expr) {
|
|
|
11182
12183
|
}
|
|
11183
12184
|
function formatArithNodeStr(node) {
|
|
11184
12185
|
if (node.type === "FIELD_REF") return node.field;
|
|
11185
|
-
if (node.type === "NUMBER") return
|
|
12186
|
+
if (node.type === "NUMBER") return numberLiteralText(node);
|
|
11186
12187
|
if (node.type === "ARITH") return `(${formatArithExprStr(node)})`;
|
|
11187
12188
|
return "...";
|
|
11188
12189
|
}
|
|
@@ -11651,6 +12652,7 @@ function withRequestGate(client, gate) {
|
|
|
11651
12652
|
},
|
|
11652
12653
|
getApps: () => gate.runReadOnly(() => client.getApps()),
|
|
11653
12654
|
getFields: (appId) => gate.runReadOnly(() => client.getFields(appId)),
|
|
12655
|
+
getNumberPrecision: (appId) => gate.runReadOnly(() => client.getNumberPrecision(appId)),
|
|
11654
12656
|
getProcessStatuses: (appId) => gate.runReadOnly(() => client.getProcessStatuses(appId)),
|
|
11655
12657
|
postRecords: (params) => gate.runMutation(() => client.postRecords(params)),
|
|
11656
12658
|
putRecords: (params) => gate.runMutation(() => client.putRecords(params)),
|
|
@@ -12212,6 +13214,16 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
|
|
|
12212
13214
|
);
|
|
12213
13215
|
return flattenFormFieldProperties(res.properties);
|
|
12214
13216
|
},
|
|
13217
|
+
async getNumberPrecision(appId) {
|
|
13218
|
+
const qs = new URLSearchParams();
|
|
13219
|
+
qs.set("app", String(appId));
|
|
13220
|
+
const res = await requestJson(
|
|
13221
|
+
`${apiBasePath}/app/settings.json?${qs.toString()}`,
|
|
13222
|
+
{ method: "GET" },
|
|
13223
|
+
appId
|
|
13224
|
+
);
|
|
13225
|
+
return parseNumberPrecisionSettings(res);
|
|
13226
|
+
},
|
|
12215
13227
|
async getProcessStatuses(appId) {
|
|
12216
13228
|
const qs = new URLSearchParams();
|
|
12217
13229
|
qs.set("app", String(appId));
|
|
@@ -13342,6 +14354,9 @@ function createDryRunClient() {
|
|
|
13342
14354
|
getFields: notUsed,
|
|
13343
14355
|
async getProcessStatuses() {
|
|
13344
14356
|
return { enable: false, states: [] };
|
|
14357
|
+
},
|
|
14358
|
+
async getNumberPrecision() {
|
|
14359
|
+
return { digits: 30, decimalPlaces: 10, roundingMode: "HALF_EVEN" };
|
|
13345
14360
|
}
|
|
13346
14361
|
};
|
|
13347
14362
|
}
|
|
@@ -14359,6 +15374,12 @@ async function run() {
|
|
|
14359
15374
|
if (!routed) throw new Error(`AuthError: profile "${pName}" is not resolved for APP${appId}.`);
|
|
14360
15375
|
return routed.getFields(binding.appId);
|
|
14361
15376
|
},
|
|
15377
|
+
getNumberPrecision: (appId) => {
|
|
15378
|
+
const binding = appBindingByMappedApp.get(appId) ?? { appId, profile: profileName.toLowerCase() };
|
|
15379
|
+
const routed = profileClientMap.get(binding.profile);
|
|
15380
|
+
if (!routed) throw new Error(`AuthError: profile "${binding.profile}" is not resolved for APP${appId}.`);
|
|
15381
|
+
return routed.getNumberPrecision(binding.appId);
|
|
15382
|
+
},
|
|
14362
15383
|
getProcessStatuses: (appId) => {
|
|
14363
15384
|
const binding = appBindingByMappedApp.get(appId) ?? { appId, profile: profileName.toLowerCase() };
|
|
14364
15385
|
const pName = binding.profile;
|