@rex0220/kintone-sql-tools 3.2.0 → 3.3.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),
@@ -465,8 +478,94 @@ function isJapanese(cp) {
465
478
  return cp >= 12352 && cp <= 12543 || cp >= 13312 && cp <= 40959 || cp >= 63744 && cp <= 64255 || cp >= 65281 && cp <= 65376;
466
479
  }
467
480
 
481
+ // src/core/exactDecimal.ts
482
+ var DECIMAL_PATTERN = /^([+-]?)(?:(\d+)(?:\.(\d*))?|\.(\d+))(?:[eE]([+-]?)(\d+))?$/;
483
+ function parseSafeExponent(sign, digits) {
484
+ if (digits === void 0) return 0;
485
+ let value = 0;
486
+ for (const digit of digits) {
487
+ value = value * 10 + (digit.charCodeAt(0) - 48);
488
+ if (!Number.isSafeInteger(value)) return null;
489
+ }
490
+ return sign === "-" ? -value : value;
491
+ }
492
+ function parseExactDecimal(input) {
493
+ const match = DECIMAL_PATTERN.exec(input.trim());
494
+ if (match === null) return null;
495
+ const exponent = parseSafeExponent(match[5], match[6]);
496
+ if (exponent === null) return null;
497
+ const fraction = match[3] ?? match[4] ?? "";
498
+ let coefficient = `${match[2] ?? ""}${fraction}`.replace(/^0+/, "");
499
+ if (coefficient === "") return { sign: 0, coefficient: "0", scale: 0 };
500
+ let scale = fraction.length - exponent;
501
+ if (!Number.isSafeInteger(scale)) return null;
502
+ const trailingZeros = /0+$/.exec(coefficient)?.[0].length ?? 0;
503
+ if (trailingZeros > 0) {
504
+ coefficient = coefficient.slice(0, -trailingZeros);
505
+ scale -= trailingZeros;
506
+ if (!Number.isSafeInteger(scale)) return null;
507
+ }
508
+ if (!Number.isSafeInteger(coefficient.length - scale)) return null;
509
+ const sign = match[1] === "-" ? -1 : 1;
510
+ return { sign, coefficient, scale };
511
+ }
512
+ function formatPlainDecimal(dec) {
513
+ if (dec.sign === 0) return "0";
514
+ const digits = dec.coefficient;
515
+ let magnitude;
516
+ if (dec.scale <= 0) {
517
+ magnitude = `${digits}${"0".repeat(-dec.scale)}`;
518
+ } else if (digits.length > dec.scale) {
519
+ const point = digits.length - dec.scale;
520
+ magnitude = `${digits.slice(0, point)}.${digits.slice(point)}`;
521
+ } else {
522
+ magnitude = `0.${"0".repeat(dec.scale - digits.length)}${digits}`;
523
+ }
524
+ return dec.sign === -1 ? `-${magnitude}` : magnitude;
525
+ }
526
+ function toPlainDecimal(input) {
527
+ const dec = parseExactDecimal(input);
528
+ return dec === null ? null : formatPlainDecimal(dec);
529
+ }
530
+ function compareMagnitudes(left, right) {
531
+ const leftPoint = left.coefficient.length - left.scale;
532
+ const rightPoint = right.coefficient.length - right.scale;
533
+ if (!Number.isSafeInteger(leftPoint) || !Number.isSafeInteger(rightPoint)) {
534
+ throw new Error("ArgumentError: exact decimal scale is outside the supported range.");
535
+ }
536
+ if (leftPoint !== rightPoint) return leftPoint < rightPoint ? -1 : 1;
537
+ const width = Math.max(left.coefficient.length, right.coefficient.length);
538
+ for (let index = 0; index < width; index++) {
539
+ const a = index < left.coefficient.length ? left.coefficient.charCodeAt(index) : 48;
540
+ const b = index < right.coefficient.length ? right.coefficient.charCodeAt(index) : 48;
541
+ if (a !== b) return a < b ? -1 : 1;
542
+ }
543
+ return 0;
544
+ }
545
+ function compareExactDecimal(left, right) {
546
+ if (left.sign !== right.sign) return left.sign < right.sign ? -1 : 1;
547
+ if (left.sign === 0) return 0;
548
+ const magnitude = compareMagnitudes(left, right);
549
+ return left.sign === -1 ? magnitude === 0 ? 0 : magnitude === -1 ? 1 : -1 : magnitude;
550
+ }
551
+ function compareDecimal(left, right) {
552
+ const a = parseExactDecimal(left);
553
+ const b = parseExactDecimal(right);
554
+ if (a === null || b === null) {
555
+ throw new Error("ArgumentError: compareDecimal requires finite decimal inputs.");
556
+ }
557
+ return compareExactDecimal(a, b);
558
+ }
559
+
468
560
  // src/types/ast.ts
469
561
  var NO_FROM_CTE_NAME = "__NO_FROM__";
562
+ function makeNumberLiteral(raw) {
563
+ return { type: "NUMBER", value: Number(raw), raw };
564
+ }
565
+ function numberLiteralText(node) {
566
+ const source = node.raw ?? String(node.value);
567
+ return toPlainDecimal(source) ?? source;
568
+ }
470
569
 
471
570
  // src/parser/parser.ts
472
571
  var MAX_BATCH_STATEMENTS = 20;
@@ -499,6 +598,9 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
499
598
  "COALESCE" /* COALESCE */,
500
599
  "NULLIF" /* NULLIF */,
501
600
  "ISNULL" /* ISNULL */,
601
+ "REGEXP_LIKE" /* REGEXP_LIKE */,
602
+ "REGEXP_REPLACE" /* REGEXP_REPLACE */,
603
+ "REGEXP_SUBSTR" /* REGEXP_SUBSTR */,
502
604
  "LEFT" /* LEFT */,
503
605
  "RIGHT" /* RIGHT */,
504
606
  "INSTR" /* INSTR */,
@@ -1262,12 +1364,12 @@ var Parser = class {
1262
1364
  if (this.peek().kind === "-" /* MINUS */) {
1263
1365
  this.advance();
1264
1366
  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 };
1367
+ if (operand.type === "NUMBER") return makeNumberLiteral(`-${numberLiteralText(operand)}`);
1368
+ return { type: "AGG_ARITH", left: makeNumberLiteral("0"), op: "-", right: operand };
1267
1369
  }
1268
1370
  if (this.peek().kind === "NUMBER" /* NUMBER */) {
1269
1371
  const tok = this.advance();
1270
- return { type: "NUMBER", value: Number(tok.value) };
1372
+ return makeNumberLiteral(tok.value);
1271
1373
  }
1272
1374
  const aggFunc = this.tryAggregateFunc();
1273
1375
  if (aggFunc !== null) {
@@ -1319,7 +1421,7 @@ var Parser = class {
1319
1421
  if (this.allowUnaryPlusNumber && this.peek().kind === "+" /* PLUS */) {
1320
1422
  this.advance();
1321
1423
  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) };
1424
+ return makeNumberLiteral(`+${number.value}`);
1323
1425
  }
1324
1426
  if (this.peek().kind === "-" /* MINUS */) {
1325
1427
  this.advance();
@@ -1327,8 +1429,8 @@ var Parser = class {
1327
1429
  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
1430
  }
1329
1431
  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 };
1432
+ if (operand.type === "NUMBER") return makeNumberLiteral(`-${numberLiteralText(operand)}`);
1433
+ return { type: "ARITH", left: makeNumberLiteral("0"), op: "-", right: operand };
1332
1434
  }
1333
1435
  if (this.tryStringFuncName() !== null) {
1334
1436
  return this.parseStringFuncExpr();
@@ -1336,7 +1438,7 @@ var Parser = class {
1336
1438
  const tok = this.peek();
1337
1439
  if (tok.kind === "NUMBER" /* NUMBER */) {
1338
1440
  this.advance();
1339
- return { type: "NUMBER", value: Number(tok.value) };
1441
+ return makeNumberLiteral(tok.value);
1340
1442
  }
1341
1443
  if (tok.kind === "IDENT" /* IDENT */ || tok.kind === "BIDENT" /* BIDENT */) {
1342
1444
  this.advance();
@@ -1440,6 +1542,9 @@ var Parser = class {
1440
1542
  ["SUBSTR" /* SUBSTR */]: "SUBSTRING",
1441
1543
  ["CONCAT" /* CONCAT */]: "CONCAT",
1442
1544
  ["REPLACE" /* REPLACE */]: "REPLACE",
1545
+ ["REGEXP_LIKE" /* REGEXP_LIKE */]: "REGEXP_LIKE",
1546
+ ["REGEXP_REPLACE" /* REGEXP_REPLACE */]: "REGEXP_REPLACE",
1547
+ ["REGEXP_SUBSTR" /* REGEXP_SUBSTR */]: "REGEXP_SUBSTR",
1443
1548
  ["TRANSLATE" /* TRANSLATE */]: "TRANSLATE",
1444
1549
  ["COALESCE" /* COALESCE */]: "COALESCE",
1445
1550
  ["NULLIF" /* NULLIF */]: "NULLIF",
@@ -1979,7 +2084,7 @@ var Parser = class {
1979
2084
  }
1980
2085
  if (tok.kind === "NUMBER" /* NUMBER */ || tok.kind === "IDENT" /* IDENT */ || tok.kind === "BIDENT" /* BIDENT */ || tok.kind === "(" /* LPAREN */ || tok.kind === "-" /* MINUS */ || this.tryStringFuncName() !== null) {
1981
2086
  const expr = this.parseArithAddSub();
1982
- if (expr.type === "NUMBER") return { type: "NUMBER", value: expr.value };
2087
+ if (expr.type === "NUMBER") return expr;
1983
2088
  return { type: "ARITH_VALUE", expr };
1984
2089
  }
1985
2090
  throw new ParseError(
@@ -2006,15 +2111,15 @@ var Parser = class {
2006
2111
  if (tok.kind === "STRING" /* STRING */) {
2007
2112
  values.push({ type: "STRING", value: tok.value });
2008
2113
  } else if (tok.kind === "NUMBER" /* NUMBER */) {
2009
- values.push({ type: "NUMBER", value: Number(tok.value) });
2114
+ values.push(makeNumberLiteral(tok.value));
2010
2115
  } else if (tok.kind === "-" /* MINUS */ || tok.kind === "+" /* PLUS */) {
2011
2116
  const number = this.peek();
2012
2117
  if (number.kind !== "NUMBER" /* NUMBER */) {
2013
2118
  throw new ParseError(invalidValueMessage, tok);
2014
2119
  }
2015
2120
  this.advance();
2016
- const sign = tok.kind === "-" /* MINUS */ ? -1 : 1;
2017
- values.push({ type: "NUMBER", value: sign * Number(number.value) });
2121
+ const sign = tok.kind === "-" /* MINUS */ ? "-" : "+";
2122
+ values.push(makeNumberLiteral(`${sign}${number.value}`));
2018
2123
  } else if (tok.kind === "VARIABLE" /* VARIABLE */) {
2019
2124
  values.push({ type: "VARIABLE", name: tok.value.slice(1).toLowerCase() });
2020
2125
  } else {
@@ -2204,14 +2309,13 @@ var Parser = class {
2204
2309
  } else if (this.peek().kind === "-" /* MINUS */ || this.peek().kind === "+" /* PLUS */) {
2205
2310
  const sign = this.advance();
2206
2311
  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 });
2312
+ row.push(makeNumberLiteral(`${sign.kind === "-" /* MINUS */ ? "-" : "+"}${number.value}`));
2209
2313
  } else {
2210
2314
  const tok = this.advance();
2211
2315
  if (tok.kind === "STRING" /* STRING */) {
2212
2316
  row.push({ type: "STRING", value: tok.value });
2213
2317
  } else if (tok.kind === "NUMBER" /* NUMBER */) {
2214
- row.push({ type: "NUMBER", value: Number(tok.value) });
2318
+ row.push(makeNumberLiteral(tok.value));
2215
2319
  } else {
2216
2320
  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
2321
  }
@@ -3045,7 +3149,7 @@ function convertValue(value, op) {
3045
3149
  case "STRING":
3046
3150
  return convertString(value);
3047
3151
  case "NUMBER":
3048
- return String(value.value);
3152
+ return numberLiteralText(value);
3049
3153
  case "KINTONE_FUNC":
3050
3154
  return convertKintoneFunc(value);
3051
3155
  case "IN_LIST":
@@ -3074,7 +3178,7 @@ function convertInList(v, op) {
3074
3178
  }
3075
3179
  assertResolvedInListValues(v.values);
3076
3180
  const values = v.values.map(
3077
- (item) => item.type === "STRING" ? convertString(item) : String(item.value)
3181
+ (item) => item.type === "STRING" ? convertString(item) : numberLiteralText(item)
3078
3182
  ).join(",");
3079
3183
  return `(${values})`;
3080
3184
  }
@@ -3587,7 +3691,7 @@ function aggregateSyntheticName(func, distinct, arg) {
3587
3691
  }
3588
3692
  function arithNodeLabel(node) {
3589
3693
  if (node.type === "FIELD_REF") return node.field;
3590
- if (node.type === "NUMBER") return String(node.value);
3694
+ if (node.type === "NUMBER") return numberLiteralText(node);
3591
3695
  if (node.type === "STRING_FUNC") return stringFuncLabel(node);
3592
3696
  return `(${arithNodeLabel(node.left)}${node.op}${arithNodeLabel(node.right)})`;
3593
3697
  }
@@ -3744,7 +3848,7 @@ function isNumericCandidate(expr, options) {
3744
3848
  if (!isTargetField(expr.left, options)) return false;
3745
3849
  if (expr.right.type !== "NUMBER") return false;
3746
3850
  if (expr.op === "=") return true;
3747
- return (expr.op === "<" || expr.op === ">") && Number.isSafeInteger(expr.right.value);
3851
+ return (expr.op === "<" || expr.op === ">") && /^[+-]?\d+$/.test(numberLiteralText(expr.right)) && Number.isSafeInteger(expr.right.value);
3748
3852
  }
3749
3853
  function isSelectionInCandidate(expr, options) {
3750
3854
  if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
@@ -4349,9 +4453,10 @@ function triCompare(left, right) {
4349
4453
  }
4350
4454
  function numberKey(value) {
4351
4455
  if (value === "") return { band: 0 };
4456
+ const decimal = parseExactDecimal(value);
4457
+ if (decimal !== null) return { band: 2, value: decimal };
4352
4458
  const numeric = Number(value);
4353
4459
  if (numeric === Number.NEGATIVE_INFINITY) return { band: 1 };
4354
- if (Number.isFinite(numeric)) return { band: 2, value: numeric };
4355
4460
  if (numeric === Number.POSITIVE_INFINITY) return { band: 3 };
4356
4461
  if (value === "NaN") return { band: 4 };
4357
4462
  return { band: 5, value };
@@ -4360,7 +4465,7 @@ function compareNumbers(left, right) {
4360
4465
  const a = numberKey(left);
4361
4466
  const b = numberKey(right);
4362
4467
  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);
4468
+ if (a.band === 2 && b.band === 2) return compareExactDecimal(a.value, b.value);
4364
4469
  if (a.band === 5 && b.band === 5) return compareCodePointStrings(a.value, b.value);
4365
4470
  return 0;
4366
4471
  }
@@ -4460,7 +4565,9 @@ function selectScalarExtreme(values, extreme) {
4460
4565
  const numeric = candidates.every((value) => !Number.isNaN(Number(value)));
4461
4566
  const compare = (left, right) => {
4462
4567
  if (numeric) {
4463
- const numericCmp = triCompare(Number(left), Number(right));
4568
+ const leftDecimal = parseExactDecimal(left);
4569
+ const rightDecimal = parseExactDecimal(right);
4570
+ const numericCmp = leftDecimal !== null && rightDecimal !== null ? compareExactDecimal(leftDecimal, rightDecimal) : triCompare(Number(left), Number(right));
4464
4571
  if (numericCmp !== 0) return numericCmp;
4465
4572
  }
4466
4573
  return compareCodePointStrings(left, right);
@@ -4583,6 +4690,112 @@ function makeSafePadding(pad, gap) {
4583
4690
  const repeated = pad.repeat(Math.ceil(gap / pad.length));
4584
4691
  return sliceSafePrefix(repeated, gap);
4585
4692
  }
4693
+ var REGEXP_CACHE_MAX = 200;
4694
+ var regexpCache = /* @__PURE__ */ new Map();
4695
+ function normalizeRegexpFlags(flags) {
4696
+ if (/[^ims]/.test(flags)) {
4697
+ throw new Error("ArgumentError: regular expression flags may contain only i, m, or s.");
4698
+ }
4699
+ if (new Set(flags).size !== flags.length) {
4700
+ throw new Error("ArgumentError: regular expression flags must not contain duplicates.");
4701
+ }
4702
+ return `${flags}u`;
4703
+ }
4704
+ function compileRegexp(pattern, flags, global = false) {
4705
+ const normalizedFlags = normalizeRegexpFlags(flags) + (global ? "g" : "");
4706
+ const key = `${pattern}\0${normalizedFlags}`;
4707
+ const cached = regexpCache.get(key);
4708
+ if (cached !== void 0) {
4709
+ cached.lastIndex = 0;
4710
+ return cached;
4711
+ }
4712
+ let regexp;
4713
+ try {
4714
+ regexp = new RegExp(pattern, normalizedFlags);
4715
+ } catch (error) {
4716
+ const detail = error instanceof Error ? error.message : String(error);
4717
+ throw new Error(`ArgumentError: invalid regular expression: ${detail}`);
4718
+ }
4719
+ if (regexpCache.size >= REGEXP_CACHE_MAX) {
4720
+ const oldest = regexpCache.keys().next().value;
4721
+ if (oldest !== void 0) regexpCache.delete(oldest);
4722
+ }
4723
+ regexpCache.set(key, regexp);
4724
+ return regexp;
4725
+ }
4726
+ function assertRegexpReplacement(replacement) {
4727
+ if (replacement.includes("$`") || replacement.includes("$'")) {
4728
+ throw new Error("ArgumentError: REGEXP_REPLACE replacement must not contain $` or $'.");
4729
+ }
4730
+ }
4731
+ function parseRegexpOccurrence(arg) {
4732
+ if (arg === void 0) return 0;
4733
+ if (!/^\d+$/.test(arg)) {
4734
+ throw new Error("ArgumentError: REGEXP_REPLACE occurrence must be a non-negative integer.");
4735
+ }
4736
+ return Number(arg);
4737
+ }
4738
+ function expandRegexpReplacement(replacement, match, captures, namedGroups) {
4739
+ let result = "";
4740
+ for (let i = 0; i < replacement.length; i += 1) {
4741
+ const char = replacement[i];
4742
+ if (char !== "$" || i + 1 >= replacement.length) {
4743
+ result += char;
4744
+ continue;
4745
+ }
4746
+ const next = replacement[i + 1];
4747
+ if (next === "$") {
4748
+ result += "$";
4749
+ i += 1;
4750
+ continue;
4751
+ }
4752
+ if (next === "&") {
4753
+ result += match;
4754
+ i += 1;
4755
+ continue;
4756
+ }
4757
+ if (next === "<" && namedGroups !== void 0) {
4758
+ const end = replacement.indexOf(">", i + 2);
4759
+ if (end >= 0) {
4760
+ result += namedGroups[replacement.slice(i + 2, end)] ?? "";
4761
+ i = end;
4762
+ continue;
4763
+ }
4764
+ }
4765
+ if (/\d/.test(next)) {
4766
+ const secondDigit = replacement[i + 2];
4767
+ if (secondDigit !== void 0 && /\d/.test(secondDigit)) {
4768
+ const twoDigitIndex = Number(next + secondDigit);
4769
+ if (twoDigitIndex >= 1 && twoDigitIndex <= captures.length) {
4770
+ result += captures[twoDigitIndex - 1] ?? "";
4771
+ i += 2;
4772
+ continue;
4773
+ }
4774
+ }
4775
+ const oneDigitIndex = Number(next);
4776
+ if (oneDigitIndex >= 1 && oneDigitIndex <= captures.length) {
4777
+ result += captures[oneDigitIndex - 1] ?? "";
4778
+ i += 1;
4779
+ continue;
4780
+ }
4781
+ }
4782
+ result += "$";
4783
+ }
4784
+ return result;
4785
+ }
4786
+ function replaceNthMatch(input, globalRe, replacement, n) {
4787
+ let matchCount = 0;
4788
+ return input.replace(globalRe, (match, ...callbackArgs) => {
4789
+ matchCount += 1;
4790
+ if (matchCount !== n) return match;
4791
+ const lastArg = callbackArgs[callbackArgs.length - 1];
4792
+ const hasNamedGroups = typeof lastArg === "object" && lastArg !== null;
4793
+ const capturesEnd = callbackArgs.length - (hasNamedGroups ? 3 : 2);
4794
+ const captures = callbackArgs.slice(0, capturesEnd);
4795
+ const namedGroups = hasNamedGroups ? lastArg : void 0;
4796
+ return expandRegexpReplacement(replacement, match, captures, namedGroups);
4797
+ });
4798
+ }
4586
4799
  function evalStringFunc(expr, row) {
4587
4800
  const args = expr.args.map((a) => evalStringFuncArg(a, row));
4588
4801
  switch (expr.func) {
@@ -4646,6 +4859,21 @@ function evalStringFunc(expr, row) {
4646
4859
  const to = args[2] ?? "";
4647
4860
  return from === "" ? str : str.split(from).join(to);
4648
4861
  }
4862
+ case "REGEXP_LIKE": {
4863
+ assertArity("REGEXP_LIKE", args, 2, 3);
4864
+ return compileRegexp(args[1], args[2] ?? "").test(args[0]) ? "1" : "0";
4865
+ }
4866
+ case "REGEXP_REPLACE": {
4867
+ assertArity("REGEXP_REPLACE", args, 3, 5);
4868
+ assertRegexpReplacement(args[2]);
4869
+ const occurrence = parseRegexpOccurrence(args[4]);
4870
+ const regexp = compileRegexp(args[1], args[3] ?? "", true);
4871
+ return occurrence === 0 ? args[0].replace(regexp, args[2]) : replaceNthMatch(args[0], regexp, args[2], occurrence);
4872
+ }
4873
+ case "REGEXP_SUBSTR": {
4874
+ assertArity("REGEXP_SUBSTR", args, 2, 3);
4875
+ return compileRegexp(args[1], args[2] ?? "").exec(args[0])?.[0] ?? "";
4876
+ }
4649
4877
  case "TRANSLATE": {
4650
4878
  assertArity("TRANSLATE", args, 3, 3);
4651
4879
  const from = [...args[1]];
@@ -4824,7 +5052,7 @@ function evalStringFuncArg(arg, row) {
4824
5052
  if (arg.type === "STRING") return arg.value;
4825
5053
  if (arg.type === "STRING_FUNC") return evalStringFunc(arg, row);
4826
5054
  if (arg.type === "FIELD_REF") return resolveFieldRef(row, arg.field);
4827
- if (arg.type === "NUMBER") return String(arg.value);
5055
+ if (arg.type === "NUMBER") return numberLiteralText(arg);
4828
5056
  if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return "";
4829
5057
  return String(evalArithExpr(arg, row));
4830
5058
  }
@@ -4873,7 +5101,7 @@ function evalOp(op, leftStr, right, row, fieldType, resolveFieldType, semantics
4873
5101
  let values = null;
4874
5102
  if (right.type === "IN_LIST") {
4875
5103
  assertResolvedInListValues2(right.values);
4876
- values = new Set(right.values.map((v) => String(v.value)));
5104
+ values = new Set(right.values.map((v) => v.type === "NUMBER" ? fieldType === "NUMBER" ? numberLiteralText(v) : String(v.value) : v.value));
4877
5105
  }
4878
5106
  if (right.type === "SUBQUERY_IN_LIST") {
4879
5107
  values = right.resolved;
@@ -4953,6 +5181,10 @@ var SINGLE_OBJECT_FIELD_TYPES = /* @__PURE__ */ new Set(["CREATOR", "MODIFIER"])
4953
5181
  function typedInContains(leftStr, values, fieldType) {
4954
5182
  const fallback = () => values.has(leftStr);
4955
5183
  if (fieldType === void 0) return fallback();
5184
+ if (fieldType === "NUMBER") {
5185
+ const semantics = syntheticSemantics("number");
5186
+ return [...values].some((value) => compareScalarValues("=", leftStr, value, semantics));
5187
+ }
4956
5188
  let parsed;
4957
5189
  if (STRING_ARRAY_FIELD_TYPES.has(fieldType) || OBJECT_ARRAY_FIELD_TYPES.has(fieldType) || SINGLE_OBJECT_FIELD_TYPES.has(fieldType)) {
4958
5190
  try {
@@ -5011,7 +5243,7 @@ function resolveValue(value, row, resolveFieldType, resolveFieldSemantics2) {
5011
5243
  case "STRING":
5012
5244
  return value.value;
5013
5245
  case "NUMBER":
5014
- return String(value.value);
5246
+ return numberLiteralText(value);
5015
5247
  case "KINTONE_FUNC":
5016
5248
  return resolveKintoneFunc(value.name);
5017
5249
  case "IN_LIST":
@@ -5451,7 +5683,7 @@ function convertDmlSqlValue(value, fieldType) {
5451
5683
  case "STRING":
5452
5684
  return convertString2(value.value, fieldType);
5453
5685
  case "NUMBER":
5454
- return String(value.value);
5686
+ return numberLiteralText(value);
5455
5687
  case "ARRAY":
5456
5688
  return convertArray(value.elements.map((e) => e.value), fieldType);
5457
5689
  case "KINTONE_FUNC":
@@ -6083,7 +6315,7 @@ function evalAggArithExpr(node, rows, resolveAggSortKind) {
6083
6315
  }
6084
6316
  }
6085
6317
  function aggArithDefaultKey(node) {
6086
- if (node.type === "NUMBER") return String(node.value);
6318
+ if (node.type === "NUMBER") return numberLiteralText(node);
6087
6319
  if (node.type === "AGG_REF") return aggregateSyntheticName2(node.func, node.distinct, node.arg);
6088
6320
  return `${aggArithDefaultKey(node.left)}${node.op}${aggArithDefaultKey(node.right)}`;
6089
6321
  }
@@ -6433,7 +6665,7 @@ function stripParentShortcutColumns(row) {
6433
6665
  function arithColDefaultKey(expr) {
6434
6666
  const nodeLabel = (n) => {
6435
6667
  if (n.type === "FIELD_REF") return n.field;
6436
- if (n.type === "NUMBER") return String(n.value);
6668
+ if (n.type === "NUMBER") return numberLiteralText(n);
6437
6669
  if (n.type === "STRING_FUNC") return stringFuncDefaultKey(n);
6438
6670
  return `(${nodeLabel(n.left)}${n.op}${nodeLabel(n.right)})`;
6439
6671
  };
@@ -6462,10 +6694,11 @@ function hasAggregateInStringFuncExpr2(expr) {
6462
6694
  function resolveAggInStringFuncArg(arg, rows, resolveAggSortKind) {
6463
6695
  if (arg.type === "AGG_REF") {
6464
6696
  const value = evalAggregate(arg.func, arg.distinct, arg.arg, arg.separator, rows, resolveAggSortKind);
6465
- return typeof value === "number" ? { type: "NUMBER", value } : { type: "STRING", value };
6697
+ return typeof value === "number" ? { type: "NUMBER", value, raw: String(value) } : { type: "STRING", value };
6466
6698
  }
6467
6699
  if (arg.type === "AGG_ARITH") {
6468
- return { type: "NUMBER", value: evalAggArithExpr(arg, rows, resolveAggSortKind) };
6700
+ const value = evalAggArithExpr(arg, rows, resolveAggSortKind);
6701
+ return { type: "NUMBER", value, raw: String(value) };
6469
6702
  }
6470
6703
  if (arg.type === "STRING_FUNC") {
6471
6704
  return resolveAggInStringFuncExpr(arg, rows, resolveAggSortKind);
@@ -6583,10 +6816,43 @@ function toFlatString(value) {
6583
6816
  }
6584
6817
  }
6585
6818
 
6819
+ // src/core/numberPrecision.ts
6820
+ function parseIntegerSetting(value, name, min, max) {
6821
+ if (typeof value !== "string" || !/^\d+$/.test(value)) {
6822
+ throw new Error(`SettingsError: numberPrecision.${name} must be an integer string.`);
6823
+ }
6824
+ let parsed = 0;
6825
+ for (const digit of value) parsed = parsed * 10 + digit.charCodeAt(0) - 48;
6826
+ if (parsed < min || parsed > max) {
6827
+ throw new Error(`SettingsError: numberPrecision.${name} must be between ${min} and ${max}.`);
6828
+ }
6829
+ return parsed;
6830
+ }
6831
+ function parseNumberPrecisionSettings(response) {
6832
+ const raw = response.numberPrecision;
6833
+ if (raw === void 0 || raw === null || typeof raw !== "object") {
6834
+ throw new Error("SettingsError: numberPrecision is missing from app settings.");
6835
+ }
6836
+ const digits = parseIntegerSetting(raw.digits, "digits", 1, 30);
6837
+ const decimalPlaces = parseIntegerSetting(raw.decimalPlaces, "decimalPlaces", 0, 10);
6838
+ const roundingMode = raw.roundingMode;
6839
+ if (roundingMode !== "HALF_EVEN" && roundingMode !== "UP" && roundingMode !== "DOWN") {
6840
+ throw new Error("SettingsError: numberPrecision.roundingMode is unsupported.");
6841
+ }
6842
+ return { digits, decimalPlaces, roundingMode };
6843
+ }
6844
+ function exactDecimalDigitCounts(value) {
6845
+ if (value.sign === 0) return { integerDigits: 0, fractionDigits: 0 };
6846
+ return {
6847
+ integerDigits: Math.max(value.coefficient.length - value.scale, 0),
6848
+ fractionDigits: Math.max(value.scale, 0)
6849
+ };
6850
+ }
6851
+
6586
6852
  // src/core/dmlValidation.ts
6587
6853
  var ARRAY_TYPES2 = /* @__PURE__ */ new Set(["CHECK_BOX", "MULTI_SELECT"]);
6588
6854
  var CHOICE_TYPES = /* @__PURE__ */ new Set(["DROP_DOWN", "RADIO_BUTTON", "CHECK_BOX", "MULTI_SELECT"]);
6589
- function validateAndNormalizeDmlValue(raw, field) {
6855
+ function validateAndNormalizeDmlValue(raw, field, numberPrecision) {
6590
6856
  if (field.fieldType === "DATE" || field.fieldType === "TIME" || field.fieldType === "DATETIME") {
6591
6857
  const original = rawScalarText(raw);
6592
6858
  if (original !== "" && !isValidTemporalInput(original, field.fieldType)) {
@@ -6605,7 +6871,8 @@ function validateAndNormalizeDmlValue(raw, field) {
6605
6871
  }
6606
6872
  if (!isEmpty(value) && field.fieldType === "NUMBER") {
6607
6873
  const text = String(value);
6608
- if (!isFiniteDecimal(text)) {
6874
+ const decimal = parseExactDecimal(text);
6875
+ if (decimal === null) {
6609
6876
  return { ok: false, code: "ERR_TYPE_NUMBER", message: `${field.code} \u306F\u6570\u5024\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044` };
6610
6877
  }
6611
6878
  if (field.minValue != null && compareDecimal(text, field.minValue) < 0) {
@@ -6614,6 +6881,17 @@ function validateAndNormalizeDmlValue(raw, field) {
6614
6881
  if (field.maxValue != null && compareDecimal(text, field.maxValue) > 0) {
6615
6882
  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
6883
  }
6884
+ if (numberPrecision !== void 0) {
6885
+ const { integerDigits } = exactDecimalDigitCounts(decimal);
6886
+ const integerBudget = numberPrecision.digits - numberPrecision.decimalPlaces;
6887
+ if (integerDigits > integerBudget) {
6888
+ return {
6889
+ ok: false,
6890
+ code: "ERR_NUMBER_INTEGER_DIGITS",
6891
+ 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})`
6892
+ };
6893
+ }
6894
+ }
6617
6895
  }
6618
6896
  if (!isEmpty(value) && (field.fieldType === "DATE" || field.fieldType === "TIME" || field.fieldType === "DATETIME")) {
6619
6897
  if (!isValidTemporal(String(value), field.fieldType)) {
@@ -6641,7 +6919,8 @@ function validateAndNormalizeDmlValue(raw, field) {
6641
6919
  }
6642
6920
  function rawScalarText(raw) {
6643
6921
  if (raw == null) return "";
6644
- if (isSqlValue(raw) && (raw.type === "STRING" || raw.type === "NUMBER")) return String(raw.value);
6922
+ if (isSqlValue(raw) && raw.type === "NUMBER") return numberLiteralText(raw);
6923
+ if (isSqlValue(raw) && raw.type === "STRING") return raw.value;
6645
6924
  return typeof raw === "string" || typeof raw === "number" ? String(raw) : "";
6646
6925
  }
6647
6926
  function isValidTemporalInput(value, type) {
@@ -6691,34 +6970,6 @@ function isEmpty(value) {
6691
6970
  function typeCode(type) {
6692
6971
  return type === "NUMBER" ? "ERR_TYPE_NUMBER" : "ERR_TYPE_DATE";
6693
6972
  }
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
6973
  function isValidTemporal(value, type) {
6723
6974
  if (type === "TIME") {
6724
6975
  const m2 = /^(\d{2}):(\d{2})(?::(\d{2}))?$/.exec(value);
@@ -6746,7 +6997,7 @@ var VALIDATION_META_COLUMNS = [
6746
6997
  "$err_code",
6747
6998
  "$err_message"
6748
6999
  ];
6749
- function validateDmlCandidates(candidates, operation, payloadFields, targetFields, fieldInfos, statementNumber) {
7000
+ function validateDmlCandidates(candidates, operation, payloadFields, targetFields, fieldInfos, statementNumber, numberPrecision) {
6750
7001
  const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
6751
7002
  const errors = [];
6752
7003
  const invalid = /* @__PURE__ */ new Set();
@@ -6754,7 +7005,7 @@ function validateDmlCandidates(candidates, operation, payloadFields, targetField
6754
7005
  candidate.record ??= {};
6755
7006
  const rowErrors = [...candidate.preErrors];
6756
7007
  for (const code of targetFields) {
6757
- const result = validateAndNormalizeDmlValue(candidate.payload.get(code), infoByCode.get(code));
7008
+ const result = validateAndNormalizeDmlValue(candidate.payload.get(code), infoByCode.get(code), numberPrecision);
6758
7009
  if (!result.ok) rowErrors.push({ field: code, code: result.code, message: result.message });
6759
7010
  else candidate.record[code] = { value: result.value };
6760
7011
  }
@@ -6764,14 +7015,14 @@ function validateDmlCandidates(candidates, operation, payloadFields, targetField
6764
7015
  if (candidate.payload.has(info.code)) continue;
6765
7016
  const emptyDefault = isEmptyDmlValue(info.defaultValue);
6766
7017
  if (!emptyDefault) {
6767
- const defaultResult = validateAndNormalizeDmlValue(info.defaultValue, info);
7018
+ const defaultResult = validateAndNormalizeDmlValue(info.defaultValue, info, numberPrecision);
6768
7019
  if (!defaultResult.ok) rowErrors.push({
6769
7020
  field: info.code,
6770
7021
  code: defaultResult.code,
6771
7022
  message: `\u65E2\u5B9A\u5024: ${defaultResult.message}`
6772
7023
  });
6773
7024
  } else {
6774
- const emptyResult = validateAndNormalizeDmlValue("", info);
7025
+ const emptyResult = validateAndNormalizeDmlValue("", info, numberPrecision);
6775
7026
  if (!emptyResult.ok) {
6776
7027
  rowErrors.push({ field: info.code, code: emptyResult.code, message: emptyResult.message });
6777
7028
  } else if (info.required) {
@@ -6799,7 +7050,8 @@ function renderValidationValue(value) {
6799
7050
  if (value == null) return "";
6800
7051
  if (typeof value === "object" && "type" in value) {
6801
7052
  const sql = value;
6802
- if (sql.type === "STRING" || sql.type === "NUMBER") return String(sql.value ?? "");
7053
+ if (sql.type === "NUMBER") return sql.raw ?? String(sql.value ?? "");
7054
+ if (sql.type === "STRING") return String(sql.value ?? "");
6803
7055
  if (sql.type === "ARRAY") return JSON.stringify(sql.elements?.map((e) => e.value) ?? []);
6804
7056
  }
6805
7057
  if (Array.isArray(value)) return JSON.stringify(value);
@@ -7041,6 +7293,7 @@ function createEmptyMetrics() {
7041
7293
  putCalls: 0,
7042
7294
  deleteCalls: 0,
7043
7295
  fieldCalls: 0,
7296
+ numberPrecisionCalls: 0,
7044
7297
  appsCalls: 0,
7045
7298
  processStatusCalls: 0,
7046
7299
  cursorCreateCalls: 0,
@@ -7126,6 +7379,10 @@ function wrapClientWithMetrics(client, metrics) {
7126
7379
  metrics.fieldCalls += 1;
7127
7380
  return client.getFields(appId);
7128
7381
  },
7382
+ getNumberPrecision: (appId) => {
7383
+ metrics.numberPrecisionCalls += 1;
7384
+ return client.getNumberPrecision(appId);
7385
+ },
7129
7386
  getProcessStatuses: (appId) => {
7130
7387
  metrics.processStatusCalls += 1;
7131
7388
  return client.getProcessStatuses(appId);
@@ -7393,7 +7650,7 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
7393
7650
  const first = resolvedStmt2.expr.query.columns[0];
7394
7651
  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
7652
  const numberValue = numeric ? Number(value) : Number.NaN;
7396
- variables.set(stmt.name, numeric && Number.isFinite(numberValue) ? { type: "number", value: numberValue } : { type: "string", value });
7653
+ variables.set(stmt.name, numeric && Number.isFinite(numberValue) ? { type: "number", value: numberValue, raw: value } : { type: "string", value });
7397
7654
  } catch (e) {
7398
7655
  if (e instanceof ScalarSubqueryError) {
7399
7656
  throw new Error(`ArgumentError: ${e.message}`);
@@ -7411,7 +7668,10 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
7411
7668
  variables.set(stmt.name, { type: "string", value: injected[stmt.name] });
7412
7669
  } else {
7413
7670
  const value = evaluateScalarExpr(stmt.default);
7414
- variables.set(stmt.name, { type: "string", value: String(value.value) });
7671
+ variables.set(stmt.name, {
7672
+ type: "string",
7673
+ value: value.type === "number" ? value.raw ?? String(value.value) : value.value
7674
+ });
7415
7675
  }
7416
7676
  return {};
7417
7677
  }
@@ -7587,7 +7847,7 @@ function evaluateScalarExpr(expr) {
7587
7847
  case "STRING":
7588
7848
  return { type: "string", value: expr.value };
7589
7849
  case "NUMBER":
7590
- return { type: "number", value: expr.value };
7850
+ return { type: "number", value: expr.value, raw: numberLiteralText(expr) };
7591
7851
  case "KINTONE_FUNC":
7592
7852
  return { type: "string", value: resolveKintoneFunc(expr.name) };
7593
7853
  case "STRING_FUNC":
@@ -7597,7 +7857,7 @@ function evaluateScalarExpr(expr) {
7597
7857
  if (!Number.isFinite(value)) {
7598
7858
  throw new Error("ArgumentError: SET scalar arithmetic produced a non-finite number.");
7599
7859
  }
7600
- return { type: "number", value };
7860
+ return { type: "number", value, raw: String(value) };
7601
7861
  }
7602
7862
  }
7603
7863
  }
@@ -7612,7 +7872,7 @@ function resolveVariableRefs(node, variables) {
7612
7872
  if (value === void 0) {
7613
7873
  throw new Error(`ParseError: variable @${obj["name"]} is not defined in this batch.`);
7614
7874
  }
7615
- return value.type === "number" ? { type: "NUMBER", value: value.value } : { type: "STRING", value: value.value };
7875
+ return value.type === "number" ? { type: "NUMBER", value: value.value, raw: value.raw ?? String(value.value) } : { type: "STRING", value: value.value };
7616
7876
  }
7617
7877
  return Object.fromEntries(
7618
7878
  Object.entries(obj).map(([key, value]) => [key, resolveVariableRefs(value, variables)])
@@ -7678,7 +7938,7 @@ async function evalAssertOperand(operand, client, options, cacheContext, tempTab
7678
7938
  case "VARIABLE":
7679
7939
  throw new Error(`ParseError: unresolved batch variable @${operand.name}.`);
7680
7940
  case "NUMBER":
7681
- return String(operand.value);
7941
+ return numberLiteralText(operand);
7682
7942
  case "STRING":
7683
7943
  return operand.value;
7684
7944
  case "ARITH":
@@ -8961,8 +9221,8 @@ async function fetchTableRecordsForFullScan(stmt, table, client, maxRecords, par
8961
9221
  }
8962
9222
  var UPSERT_IN_CHUNK_SIZE = 50;
8963
9223
  function normalizeKeyPart(v) {
8964
- const t = v.trim();
8965
- if (t !== "" && !Number.isNaN(Number(t))) return String(Number(t));
9224
+ const decimal = parseExactDecimal(v);
9225
+ if (decimal !== null) return JSON.stringify(decimal);
8966
9226
  return v;
8967
9227
  }
8968
9228
  function upsertCompositeKey(parts) {
@@ -9114,6 +9374,7 @@ var optionOrderCache = /* @__PURE__ */ new Map();
9114
9374
  var sortKindCache = /* @__PURE__ */ new Map();
9115
9375
  var fieldInfoCache = /* @__PURE__ */ new Map();
9116
9376
  var processStatusCache = /* @__PURE__ */ new Map();
9377
+ var numberPrecisionCache = /* @__PURE__ */ new Map();
9117
9378
  function getScopedCacheValue(root, cacheContext, appId) {
9118
9379
  return root.get(cacheContext)?.get(appId);
9119
9380
  }
@@ -9135,6 +9396,13 @@ async function getFieldsCached(appId, client, cacheContext) {
9135
9396
  setScopedCacheValue(fieldInfoCache, cacheContext, appId, loading);
9136
9397
  return loading;
9137
9398
  }
9399
+ async function getNumberPrecisionCached(appId, client, cacheContext) {
9400
+ const cached = getScopedCacheValue(numberPrecisionCache, cacheContext, appId);
9401
+ if (cached) return cached;
9402
+ const loading = client.getNumberPrecision(appId);
9403
+ setScopedCacheValue(numberPrecisionCache, cacheContext, appId, loading);
9404
+ return loading;
9405
+ }
9138
9406
  async function getProcessStatusesCached(appId, client, cacheContext) {
9139
9407
  const cached = getScopedCacheValue(processStatusCache, cacheContext, appId);
9140
9408
  if (cached) return cached;
@@ -9409,6 +9677,23 @@ async function loadWritableTopLevelDmlFields(appId, targetFields, client, cacheC
9409
9677
  assertWritableTopLevelDmlFields(appId, targetFields, fieldInfos);
9410
9678
  return fieldInfos;
9411
9679
  }
9680
+ async function loadNumberPrecisionForTargets(appId, targetFields, fieldInfos, client, cacheContext) {
9681
+ const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
9682
+ return targetFields.some((code) => infoByCode.get(code)?.fieldType === "NUMBER") ? getNumberPrecisionCached(appId, client, cacheContext) : void 0;
9683
+ }
9684
+ function assertValidDmlRecords(records, targetFields, fieldInfos, numberPrecision) {
9685
+ const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
9686
+ records.forEach((record, rowIndex) => {
9687
+ for (const code of targetFields) {
9688
+ const info = infoByCode.get(code);
9689
+ const result = validateAndNormalizeDmlValue(record[code]?.value ?? "", info, numberPrecision);
9690
+ if (!result.ok) {
9691
+ throw new Error(`DmlValidationError: ${result.code} ${result.message} (row=${rowIndex + 1}, field=${code})`);
9692
+ }
9693
+ record[code] = { value: result.value };
9694
+ }
9695
+ });
9696
+ }
9412
9697
  async function executeDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber) {
9413
9698
  return (await prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber)).result;
9414
9699
  }
@@ -9436,6 +9721,13 @@ async function prepareDmlValidation(stmt, client, options, cacheContext, tempTab
9436
9721
  await assertDmlWhereCapability(stmt, client, cacheContext);
9437
9722
  }
9438
9723
  const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
9724
+ const numberPrecision = await loadNumberPrecisionForTargets(
9725
+ stmt.appId,
9726
+ targetFields,
9727
+ fieldInfos,
9728
+ client,
9729
+ cacheContext
9730
+ );
9439
9731
  const candidates = await materializeValidationCandidates(stmt, operation, client, options, cacheContext, tempTables, infoByCode);
9440
9732
  const { errors, invalidRows, invalidRowNumbers } = validateDmlCandidates(
9441
9733
  candidates,
@@ -9443,7 +9735,8 @@ async function prepareDmlValidation(stmt, client, options, cacheContext, tempTab
9443
9735
  payloadFields,
9444
9736
  targetFields,
9445
9737
  fieldInfos,
9446
- statementNumber
9738
+ statementNumber,
9739
+ numberPrecision
9447
9740
  );
9448
9741
  const columns = [...payloadFields, ...VALIDATION_META_COLUMNS];
9449
9742
  const result = {
@@ -9667,6 +9960,7 @@ async function resolveUpdateFromMatchedRecords(stmt, from, client, options, cach
9667
9960
  tempTables
9668
9961
  );
9669
9962
  const sourceByKey = /* @__PURE__ */ new Map();
9963
+ const sourceQueryByKey = /* @__PURE__ */ new Map();
9670
9964
  for (const row of sourceRows) {
9671
9965
  if (!Object.prototype.hasOwnProperty.call(row, from.joinKeyField)) {
9672
9966
  throw new Error(`ArgumentError: UPDATE ... FROM source column ${from.joinKeyField} does not exist.`);
@@ -9676,6 +9970,7 @@ async function resolveUpdateFromMatchedRecords(stmt, from, client, options, cach
9676
9970
  throw new Error(`ArgumentError: UPDATE ... FROM source has multiple rows for normalized key ${key}.`);
9677
9971
  }
9678
9972
  sourceByKey.set(key, row);
9973
+ sourceQueryByKey.set(key, String(row[from.joinKeyField]).trim());
9679
9974
  }
9680
9975
  if (sourceByKey.size === 0) return [];
9681
9976
  const maxRecords = options.maxRecords ?? 1e4;
@@ -9684,7 +9979,7 @@ async function resolveUpdateFromMatchedRecords(stmt, from, client, options, cach
9684
9979
  const targetRecords = [];
9685
9980
  const seenTargetIds = /* @__PURE__ */ new Set();
9686
9981
  let fetchedTargetCount = 0;
9687
- for (const keys of splitChunks([...sourceByKey.keys()], UPDATE_FROM_KEY_CHUNK_SIZE)) {
9982
+ for (const keys of splitChunks([...sourceQueryByKey.values()], UPDATE_FROM_KEY_CHUNK_SIZE)) {
9688
9983
  const keyQuery = `${from.targetJoinField} in (${keys.map(sqlQuote).join(",")})`;
9689
9984
  const query = filterQuery ? `(${keyQuery}) and (${filterQuery})` : keyQuery;
9690
9985
  const resolved = await fetchRecordsForSharedPlan(
@@ -9785,37 +10080,28 @@ function normalizeUpdateFromJoinKey(raw, kind, side) {
9785
10080
  }
9786
10081
  if (kind === "number" && side === "target" && raw === "") return null;
9787
10082
  if (kind === "id") {
9788
- const text2 = raw.trim();
9789
- const id = Number(text2);
9790
- if (text2 === "" || !Number.isSafeInteger(id) || id <= 0) {
10083
+ const text = raw.trim();
10084
+ const id = Number(text);
10085
+ if (text === "" || !Number.isSafeInteger(id) || id <= 0) {
9791
10086
  throw new Error(`ArgumentError: UPDATE ... FROM ${side} key must be a positive safe integer: ${raw}`);
9792
10087
  }
9793
10088
  return String(id);
9794
10089
  }
9795
- const text = raw.trim();
9796
- if (!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(text)) {
10090
+ const decimal = parseExactDecimal(raw);
10091
+ if (decimal === null) {
9797
10092
  throw new Error(`ArgumentError: UPDATE ... FROM ${side} key must be a finite decimal: ${raw}`);
9798
10093
  }
9799
- let unsigned = text;
9800
- let negative = false;
9801
- if (unsigned.startsWith("-") || unsigned.startsWith("+")) {
9802
- negative = unsigned[0] === "-";
9803
- unsigned = unsigned.slice(1);
9804
- }
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;
10094
+ return JSON.stringify(decimal);
9811
10095
  }
9812
10096
  async function executeInsert(stmt, client, options, cacheContext) {
9813
10097
  if (stmt.subtableCode) {
9814
10098
  return executeInsertSubtable(stmt, client, options, cacheContext);
9815
10099
  }
9816
- await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
10100
+ const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
10101
+ const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
9817
10102
  const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
9818
10103
  const batches = insertToPostBatches(stmt, fieldTypes);
10104
+ assertValidDmlRecords(batches.flatMap((batch) => batch.records), stmt.fields, fieldInfos, numberPrecision);
9819
10105
  const createdIds = [];
9820
10106
  for (const batch of batches) {
9821
10107
  const res = await client.postRecords(batch);
@@ -9828,7 +10114,8 @@ async function executeInsert(stmt, client, options, cacheContext) {
9828
10114
  };
9829
10115
  }
9830
10116
  async function executeInsertSelect(stmt, client, options, cacheContext, cteCache) {
9831
- await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
10117
+ const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
10118
+ const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
9832
10119
  const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
9833
10120
  const { rows, columns } = selectResult;
9834
10121
  if (columns.length !== stmt.fields.length) {
@@ -9850,6 +10137,7 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
9850
10137
  });
9851
10138
  return record;
9852
10139
  });
10140
+ assertValidDmlRecords(allRecords, stmt.fields, fieldInfos, numberPrecision);
9853
10141
  const createdIds = [];
9854
10142
  for (let i = 0; i < allRecords.length; i += 100) {
9855
10143
  const batch = allRecords.slice(i, i + 100);
@@ -9867,12 +10155,20 @@ async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
9867
10155
  await assertDmlWhereCapability(stmt, client, cacheContext);
9868
10156
  return executeUpdateSubtable(stmt, client, options, cacheContext);
9869
10157
  }
9870
- await loadWritableTopLevelDmlFields(
10158
+ const fieldInfos = await loadWritableTopLevelDmlFields(
9871
10159
  stmt.appId,
9872
10160
  stmt.assignments.map((assignment) => assignment.field),
9873
10161
  client,
9874
10162
  cacheContext
9875
10163
  );
10164
+ const targetFields = stmt.assignments.map((assignment) => assignment.field);
10165
+ const numberPrecision = await loadNumberPrecisionForTargets(
10166
+ stmt.appId,
10167
+ targetFields,
10168
+ fieldInfos,
10169
+ client,
10170
+ cacheContext
10171
+ );
9876
10172
  await assertDmlWhereCapability(stmt, client, cacheContext);
9877
10173
  if (stmt.from != null) {
9878
10174
  return executeUpdateFrom(stmt, stmt.from, client, options, cacheContext, tempTables);
@@ -9890,11 +10186,12 @@ async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
9890
10186
  { maxRecords, parallel: options.fetchParallel ?? 1 }
9891
10187
  );
9892
10188
  const records = resolved2.records;
10189
+ const batches2 = updateToPutBatchesArith(stmt, records, fieldTypes);
10190
+ assertValidDmlRecords(batches2.flatMap((batch) => batch.records.map((entry) => entry.record)), targetFields, fieldInfos, numberPrecision);
9893
10191
  if (options.confirm) {
9894
10192
  const ok = await options.confirm(records.length, "UPDATE");
9895
10193
  if (!ok) throw new OperationCancelledError("UPDATE", records.length);
9896
10194
  }
9897
- const batches2 = updateToPutBatchesArith(stmt, records, fieldTypes);
9898
10195
  for (const batch of batches2) {
9899
10196
  await client.putRecords(batch);
9900
10197
  }
@@ -9908,11 +10205,12 @@ async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
9908
10205
  { maxRecords, parallel: options.fetchParallel ?? 1 }
9909
10206
  );
9910
10207
  const ids = resolved.ids;
10208
+ const batches = updateToPutBatches(stmt, ids, fieldTypes);
10209
+ assertValidDmlRecords(batches.flatMap((batch) => batch.records.map((entry) => entry.record)), targetFields, fieldInfos, numberPrecision);
9911
10210
  if (options.confirm) {
9912
10211
  const ok = await options.confirm(ids.length, "UPDATE");
9913
10212
  if (!ok) throw new OperationCancelledError("UPDATE", ids.length);
9914
10213
  }
9915
- const batches = updateToPutBatches(stmt, ids, fieldTypes);
9916
10214
  for (const batch of batches) {
9917
10215
  await client.putRecords(batch);
9918
10216
  }
@@ -9920,12 +10218,16 @@ async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
9920
10218
  }
9921
10219
  async function executeUpdateFrom(stmt, from, client, options, cacheContext, tempTables) {
9922
10220
  const matched = await resolveUpdateFromMatchedRecords(stmt, from, client, options, cacheContext, tempTables);
10221
+ const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
10222
+ const batches = updateFromToPutBatches(stmt, matched, fieldTypes);
10223
+ const targetFields = stmt.assignments.map((assignment) => assignment.field);
10224
+ const fieldInfos = await getFieldsCached(stmt.appId, client, cacheContext);
10225
+ const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, targetFields, fieldInfos, client, cacheContext);
10226
+ assertValidDmlRecords(batches.flatMap((batch) => batch.records.map((entry) => entry.record)), targetFields, fieldInfos, numberPrecision);
9923
10227
  if (options.confirm) {
9924
10228
  const ok = await options.confirm(matched.length, "UPDATE");
9925
10229
  if (!ok) throw new OperationCancelledError("UPDATE", matched.length);
9926
10230
  }
9927
- const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
9928
- const batches = updateFromToPutBatches(stmt, matched, fieldTypes);
9929
10231
  for (const batch of batches) await client.putRecords(batch);
9930
10232
  return { type: "UPDATE", updatedCount: matched.length };
9931
10233
  }
@@ -9974,7 +10276,8 @@ async function executeDelete(stmt, client, options, cacheContext) {
9974
10276
  return { type: "DELETE", deletedCount: ids.length };
9975
10277
  }
9976
10278
  async function executeUpsert(stmt, client, options, cacheContext) {
9977
- await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
10279
+ const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
10280
+ const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
9978
10281
  const toInsert = [];
9979
10282
  const toUpdate = [];
9980
10283
  const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
@@ -9983,7 +10286,7 @@ async function executeUpsert(stmt, client, options, cacheContext) {
9983
10286
  const idx = stmt.fields.indexOf(key);
9984
10287
  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
10288
  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(",");
10289
+ 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
10290
  })
9988
10291
  );
9989
10292
  const targetIndex = await resolveUpsertTargets(stmt.appId, stmt.keyFields, rowKeyValues, client, options, fieldTypes);
@@ -10004,6 +10307,12 @@ async function executeUpsert(stmt, client, options, cacheContext) {
10004
10307
  toInsert.push(record);
10005
10308
  }
10006
10309
  });
10310
+ assertValidDmlRecords(
10311
+ [...toInsert, ...toUpdate.map((entry) => entry.record)],
10312
+ stmt.fields,
10313
+ fieldInfos,
10314
+ numberPrecision
10315
+ );
10007
10316
  if (options.confirm && toInsert.length + toUpdate.length > 0) {
10008
10317
  const total = toInsert.length + toUpdate.length;
10009
10318
  const ok = await options.confirm(total, "UPDATE");
@@ -10279,14 +10588,14 @@ function buildSubtableReorderPutParams(appId, parentId, revision, subtableCode,
10279
10588
  }
10280
10589
  function evalAssignmentValueForSubtable(value, row, resolveFieldType) {
10281
10590
  if (value.type === "STRING") return value.value;
10282
- if (value.type === "NUMBER") return String(value.value);
10591
+ if (value.type === "NUMBER") return numberLiteralText(value);
10283
10592
  if (value.type === "ARITH") return String(evalArithExpr(value, row));
10284
10593
  if (value.type === "CASE_VALUE") return evalCaseWhen(value.expr, row, resolveFieldType);
10285
10594
  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
10595
  }
10287
10596
  function valueToString(value) {
10288
10597
  if (value.type === "STRING") return value.value;
10289
- if (value.type === "NUMBER") return String(value.value);
10598
+ if (value.type === "NUMBER") return numberLiteralText(value);
10290
10599
  if (value.type === "CASE_VALUE") return evalCaseWhen(value.expr, {});
10291
10600
  return value.elements.map((e) => e.value).join(",");
10292
10601
  }
@@ -10401,7 +10710,8 @@ function evalOrderKeyForRow(key, row) {
10401
10710
  }
10402
10711
  }
10403
10712
  async function executeUpsertSelect(stmt, client, options, cacheContext, cteCache) {
10404
- await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
10713
+ const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
10714
+ const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
10405
10715
  const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
10406
10716
  const { rows, columns } = selectResult;
10407
10717
  if (columns.length !== stmt.fields.length) {
@@ -10424,6 +10734,7 @@ async function executeUpsertSelect(stmt, client, options, cacheContext, cteCache
10424
10734
  });
10425
10735
  return record;
10426
10736
  });
10737
+ assertValidDmlRecords(records, stmt.fields, fieldInfos, numberPrecision);
10427
10738
  const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
10428
10739
  const rowKeyValues = records.map(
10429
10740
  (record) => stmt.keyFields.map((key) => String(record[key]?.value ?? ""))
@@ -11182,7 +11493,7 @@ function formatArithExprStr(expr) {
11182
11493
  }
11183
11494
  function formatArithNodeStr(node) {
11184
11495
  if (node.type === "FIELD_REF") return node.field;
11185
- if (node.type === "NUMBER") return String(node.value);
11496
+ if (node.type === "NUMBER") return numberLiteralText(node);
11186
11497
  if (node.type === "ARITH") return `(${formatArithExprStr(node)})`;
11187
11498
  return "...";
11188
11499
  }
@@ -11651,6 +11962,7 @@ function withRequestGate(client, gate) {
11651
11962
  },
11652
11963
  getApps: () => gate.runReadOnly(() => client.getApps()),
11653
11964
  getFields: (appId) => gate.runReadOnly(() => client.getFields(appId)),
11965
+ getNumberPrecision: (appId) => gate.runReadOnly(() => client.getNumberPrecision(appId)),
11654
11966
  getProcessStatuses: (appId) => gate.runReadOnly(() => client.getProcessStatuses(appId)),
11655
11967
  postRecords: (params) => gate.runMutation(() => client.postRecords(params)),
11656
11968
  putRecords: (params) => gate.runMutation(() => client.putRecords(params)),
@@ -12212,6 +12524,16 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
12212
12524
  );
12213
12525
  return flattenFormFieldProperties(res.properties);
12214
12526
  },
12527
+ async getNumberPrecision(appId) {
12528
+ const qs = new URLSearchParams();
12529
+ qs.set("app", String(appId));
12530
+ const res = await requestJson(
12531
+ `${apiBasePath}/app/settings.json?${qs.toString()}`,
12532
+ { method: "GET" },
12533
+ appId
12534
+ );
12535
+ return parseNumberPrecisionSettings(res);
12536
+ },
12215
12537
  async getProcessStatuses(appId) {
12216
12538
  const qs = new URLSearchParams();
12217
12539
  qs.set("app", String(appId));
@@ -13342,6 +13664,9 @@ function createDryRunClient() {
13342
13664
  getFields: notUsed,
13343
13665
  async getProcessStatuses() {
13344
13666
  return { enable: false, states: [] };
13667
+ },
13668
+ async getNumberPrecision() {
13669
+ return { digits: 30, decimalPlaces: 10, roundingMode: "HALF_EVEN" };
13345
13670
  }
13346
13671
  };
13347
13672
  }
@@ -14359,6 +14684,12 @@ async function run() {
14359
14684
  if (!routed) throw new Error(`AuthError: profile "${pName}" is not resolved for APP${appId}.`);
14360
14685
  return routed.getFields(binding.appId);
14361
14686
  },
14687
+ getNumberPrecision: (appId) => {
14688
+ const binding = appBindingByMappedApp.get(appId) ?? { appId, profile: profileName.toLowerCase() };
14689
+ const routed = profileClientMap.get(binding.profile);
14690
+ if (!routed) throw new Error(`AuthError: profile "${binding.profile}" is not resolved for APP${appId}.`);
14691
+ return routed.getNumberPrecision(binding.appId);
14692
+ },
14362
14693
  getProcessStatuses: (appId) => {
14363
14694
  const binding = appBindingByMappedApp.get(appId) ?? { appId, profile: profileName.toLowerCase() };
14364
14695
  const pName = binding.profile;