@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/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
- // 数値: 整数 or 小数(123 / 3.14)
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 !== "STRFUNC_COL") return false;
1216
- return column.expr.args.some((arg) => this.stringFuncArgHasAggregate(arg));
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
- if (arg.type === "STRING_FUNC") {
1221
- return arg.args.some((nested) => this.stringFuncArgHasAggregate(nested));
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 { type: "NUMBER", value: -operand.value };
1266
- return { type: "AGG_ARITH", left: { type: "NUMBER", value: 0 }, op: "-", right: operand };
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 { type: "NUMBER", value: Number(tok.value) };
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 { type: "NUMBER", value: Number(number.value) };
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 { type: "NUMBER", value: -operand.value };
1331
- return { type: "ARITH", left: { type: "NUMBER", value: 0 }, op: "-", right: operand };
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 { type: "NUMBER", value: Number(tok.value) };
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
- const tok = this.peek();
1553
- if (tok.kind === "STRING" /* STRING */) {
1554
- this.advance();
1555
- return { type: "STRING", value: tok.value };
1556
- }
1557
- if (this.tryStringFuncName() !== null) {
1558
- return this.parseStringFuncExpr();
1559
- }
1560
- const startPos = this.pos;
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.pos = startPos;
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 { type: "NUMBER", value: expr.value };
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({ type: "NUMBER", value: Number(tok.value) });
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 */ ? -1 : 1;
2017
- values.push({ type: "NUMBER", value: sign * Number(number.value) });
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
- const value = Number(number.value);
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({ type: "NUMBER", value: Number(tok.value) });
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 String(value.value);
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) : String(item.value)
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
- collectArithNode(arg, out);
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
- if (arg.type === "STRING_FUNC") return hasAggregateInStringFuncExpr(arg);
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
- walkArith(arg, phase);
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 === "STRING_FUNC") {
3415
- walkStringFunc(result, phase);
3721
+ if (result.type === "FIELD_REF" || result.type === "ARITH") {
3722
+ walkArith(result, phase);
3416
3723
  return;
3417
3724
  }
3418
- walkArith(result, phase);
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 String(node.value);
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 arithNodeLabel(a);
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 triCompare(a.value, b.value);
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 numericCmp = triCompare(Number(left), Number(right));
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
- return String(evalArithExpr(arg, row));
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 String(value.value);
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 === "STRING") return result.value;
5049
- if (result.type === "STRING_FUNC") return evalStringFunc(result, row);
5050
- if (result.type === "FIELD_REF") return row[result.field] ?? "";
5051
- return String(evalArithExpr(result, row));
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
- collectArithNode2(arg, out);
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
- collectArithNode2(result, out);
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
- return String(evalArithExpr(result, row));
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 String(value.value);
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 String(node.value);
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 String(n.value);
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 arithColDefaultKey(a);
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
- if (arg.type === "STRING_FUNC") return hasAggregateInStringFuncExpr2(arg);
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
- return { type: "NUMBER", value: evalAggArithExpr(arg, rows, resolveAggSortKind) };
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
- if (!isFiniteDecimal(text)) {
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) && (raw.type === "STRING" || raw.type === "NUMBER")) return String(raw.value);
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 === "STRING" || sql.type === "NUMBER") return String(sql.value ?? "");
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, { type: "string", value: String(value.value) });
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 String(operand.value);
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 if (arg.type === "STRING_FUNC") {
8269
- collectStringFuncAggregateRefs(arg, out);
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
- const source = resolveField2(aggregateFieldRef(result.field));
8450
- return source ?? unknownStringColumnMeta();
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 t = v.trim();
8965
- if (t !== "" && !Number.isNaN(Number(t))) return String(Number(t));
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
- if (selectResult.columns.length !== stmt.fields.length) {
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 resolved = await fetchRecordsForSharedPlan(client.getRecords, getParams.app, getParams.query, [...getParams.fields], {
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
- const resolved = await resolveDmlTargetIds(client.getRecords, getParams.app, getParams.query, {
9618
- maxRecords: options.maxRecords ?? 1e4,
9619
- parallel: options.fetchParallel ?? 1
9620
- });
9621
- records = updateToPutBatches(stmt, resolved.ids, fieldTypes).flatMap((batch) => batch.records);
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 sourceFields = [...new Set(stmt.assignments.filter((a) => a.value.type === "SOURCE_FIELD").map((a) => a.value.type === "SOURCE_FIELD" ? a.value.field : ""))];
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([...sourceByKey.keys()], UPDATE_FROM_KEY_CHUNK_SIZE)) {
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 text2 = raw.trim();
9789
- const id = Number(text2);
9790
- if (text2 === "" || !Number.isSafeInteger(id) || id <= 0) {
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 text = raw.trim();
9796
- if (!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(text)) {
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
- let unsigned = text;
9800
- let negative = false;
9801
- if (unsigned.startsWith("-") || unsigned.startsWith("+")) {
9802
- negative = unsigned[0] === "-";
9803
- unsigned = unsigned.slice(1);
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
- let [whole, fraction = ""] = unsigned.split(".");
9806
- whole = (whole || "0").replace(/^0+(?=\d)/, "");
9807
- fraction = fraction.replace(/0+$/, "");
9808
- const zero = /^0*$/.test(whole) && fraction === "";
9809
- const canonical = fraction === "" ? whole : `${whole}.${fraction}`;
9810
- return negative && !zero ? `-${canonical}` : canonical;
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
- await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
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
- await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
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" ? String(val.value) : val.type === "CASE_VALUE" ? evalCaseWhen(val.expr, {}) : val.elements.map((e) => e.value).join(",");
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 String(value.value);
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 String(value.value);
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
- await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
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 !== "STRING" && arg.type !== "AGG_REF" && arg.type !== "AGG_ARITH") {
11164
- collectArithNodeRefs(arg, out);
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 String(node.value);
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;