@rex0220/kintone-sql-tools 3.1.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
@@ -117,10 +117,14 @@ var KEYWORDS = /* @__PURE__ */ new Map([
117
117
  ["LTRIM", "LTRIM" /* LTRIM */],
118
118
  ["RTRIM", "RTRIM" /* RTRIM */],
119
119
  ["LENGTH", "LENGTH" /* LENGTH */],
120
+ ["LENGTH_CHAR", "LENGTH_CHAR" /* LENGTH_CHAR */],
120
121
  ["SUBSTRING", "SUBSTRING" /* SUBSTRING */],
121
122
  ["SUBSTR", "SUBSTR" /* SUBSTR */],
122
123
  ["CONCAT", "CONCAT" /* CONCAT */],
123
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 */],
124
128
  ["COALESCE", "COALESCE" /* COALESCE */],
125
129
  ["NULLIF", "NULLIF" /* NULLIF */],
126
130
  ["ISNULL", "ISNULL" /* ISNULL */],
@@ -129,6 +133,7 @@ var KEYWORDS = /* @__PURE__ */ new Map([
129
133
  ["LEAST", "LEAST" /* LEAST */],
130
134
  ["LPAD", "LPAD" /* LPAD */],
131
135
  ["RPAD", "RPAD" /* RPAD */],
136
+ ["TRANSLATE", "TRANSLATE" /* TRANSLATE */],
132
137
  ["CAST", "CAST" /* CAST */],
133
138
  ["CONVERT", "CONVERT" /* CONVERT */],
134
139
  ["FORMAT", "FORMAT" /* FORMAT */],
@@ -252,7 +257,7 @@ var Lexer = class {
252
257
  );
253
258
  }
254
259
  // ----------------------------------------------------------
255
- // 数値: 整数 or 小数(123 / 3.14)
260
+ // 数値: digits[.digits][e[+-]digits](先頭/末尾 dot は受理しない)
256
261
  // ----------------------------------------------------------
257
262
  readNumber(start) {
258
263
  while (this.pos < this.input.length && isDigit(this.input[this.pos])) {
@@ -264,6 +269,16 @@ var Lexer = class {
264
269
  this.pos++;
265
270
  }
266
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
+ }
267
282
  return this.makeToken(
268
283
  "NUMBER" /* NUMBER */,
269
284
  this.input.slice(start, this.pos),
@@ -463,8 +478,94 @@ function isJapanese(cp) {
463
478
  return cp >= 12352 && cp <= 12543 || cp >= 13312 && cp <= 40959 || cp >= 63744 && cp <= 64255 || cp >= 65281 && cp <= 65376;
464
479
  }
465
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
+
466
560
  // src/types/ast.ts
467
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
+ }
468
569
 
469
570
  // src/parser/parser.ts
470
571
  var MAX_BATCH_STATEMENTS = 20;
@@ -488,13 +589,18 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
488
589
  "LTRIM" /* LTRIM */,
489
590
  "RTRIM" /* RTRIM */,
490
591
  "LENGTH" /* LENGTH */,
592
+ "LENGTH_CHAR" /* LENGTH_CHAR */,
491
593
  "SUBSTRING" /* SUBSTRING */,
492
594
  "SUBSTR" /* SUBSTR */,
493
595
  "CONCAT" /* CONCAT */,
494
596
  "REPLACE" /* REPLACE */,
597
+ "TRANSLATE" /* TRANSLATE */,
495
598
  "COALESCE" /* COALESCE */,
496
599
  "NULLIF" /* NULLIF */,
497
600
  "ISNULL" /* ISNULL */,
601
+ "REGEXP_LIKE" /* REGEXP_LIKE */,
602
+ "REGEXP_REPLACE" /* REGEXP_REPLACE */,
603
+ "REGEXP_SUBSTR" /* REGEXP_SUBSTR */,
498
604
  "LEFT" /* LEFT */,
499
605
  "RIGHT" /* RIGHT */,
500
606
  "INSTR" /* INSTR */,
@@ -543,6 +649,7 @@ var ParseError = class extends Error {
543
649
  var Parser = class {
544
650
  constructor(tokens) {
545
651
  this.tokens = tokens;
652
+ this.allowUnaryPlusNumber = false;
546
653
  this.pos = 0;
547
654
  /** WITH 句で定義された CTE 名のセット(parseTableRef で参照) */
548
655
  this.cteNames = /* @__PURE__ */ new Set();
@@ -1257,12 +1364,12 @@ var Parser = class {
1257
1364
  if (this.peek().kind === "-" /* MINUS */) {
1258
1365
  this.advance();
1259
1366
  const operand = this.parseAggPrimary();
1260
- if (operand.type === "NUMBER") return { type: "NUMBER", value: -operand.value };
1261
- 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 };
1262
1369
  }
1263
1370
  if (this.peek().kind === "NUMBER" /* NUMBER */) {
1264
1371
  const tok = this.advance();
1265
- return { type: "NUMBER", value: Number(tok.value) };
1372
+ return makeNumberLiteral(tok.value);
1266
1373
  }
1267
1374
  const aggFunc = this.tryAggregateFunc();
1268
1375
  if (aggFunc !== null) {
@@ -1311,11 +1418,19 @@ var Parser = class {
1311
1418
  this.expect(")" /* RPAREN */);
1312
1419
  return expr;
1313
1420
  }
1421
+ if (this.allowUnaryPlusNumber && this.peek().kind === "+" /* PLUS */) {
1422
+ this.advance();
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");
1424
+ return makeNumberLiteral(`+${number.value}`);
1425
+ }
1314
1426
  if (this.peek().kind === "-" /* MINUS */) {
1315
1427
  this.advance();
1428
+ if (this.peek().kind === "-" /* MINUS */ || this.peek().kind === "+" /* PLUS */) {
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());
1430
+ }
1316
1431
  const operand = this.parseArithPrimary();
1317
- if (operand.type === "NUMBER") return { type: "NUMBER", value: -operand.value };
1318
- 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 };
1319
1434
  }
1320
1435
  if (this.tryStringFuncName() !== null) {
1321
1436
  return this.parseStringFuncExpr();
@@ -1323,7 +1438,7 @@ var Parser = class {
1323
1438
  const tok = this.peek();
1324
1439
  if (tok.kind === "NUMBER" /* NUMBER */) {
1325
1440
  this.advance();
1326
- return { type: "NUMBER", value: Number(tok.value) };
1441
+ return makeNumberLiteral(tok.value);
1327
1442
  }
1328
1443
  if (tok.kind === "IDENT" /* IDENT */ || tok.kind === "BIDENT" /* BIDENT */) {
1329
1444
  this.advance();
@@ -1422,10 +1537,15 @@ var Parser = class {
1422
1537
  ["LTRIM" /* LTRIM */]: "LTRIM",
1423
1538
  ["RTRIM" /* RTRIM */]: "RTRIM",
1424
1539
  ["LENGTH" /* LENGTH */]: "LENGTH",
1540
+ ["LENGTH_CHAR" /* LENGTH_CHAR */]: "LENGTH_CHAR",
1425
1541
  ["SUBSTRING" /* SUBSTRING */]: "SUBSTRING",
1426
1542
  ["SUBSTR" /* SUBSTR */]: "SUBSTRING",
1427
1543
  ["CONCAT" /* CONCAT */]: "CONCAT",
1428
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",
1548
+ ["TRANSLATE" /* TRANSLATE */]: "TRANSLATE",
1429
1549
  ["COALESCE" /* COALESCE */]: "COALESCE",
1430
1550
  ["NULLIF" /* NULLIF */]: "NULLIF",
1431
1551
  ["ISNULL" /* ISNULL */]: "ISNULL",
@@ -1964,7 +2084,7 @@ var Parser = class {
1964
2084
  }
1965
2085
  if (tok.kind === "NUMBER" /* NUMBER */ || tok.kind === "IDENT" /* IDENT */ || tok.kind === "BIDENT" /* BIDENT */ || tok.kind === "(" /* LPAREN */ || tok.kind === "-" /* MINUS */ || this.tryStringFuncName() !== null) {
1966
2086
  const expr = this.parseArithAddSub();
1967
- if (expr.type === "NUMBER") return { type: "NUMBER", value: expr.value };
2087
+ if (expr.type === "NUMBER") return expr;
1968
2088
  return { type: "ARITH_VALUE", expr };
1969
2089
  }
1970
2090
  throw new ParseError(
@@ -1991,15 +2111,15 @@ var Parser = class {
1991
2111
  if (tok.kind === "STRING" /* STRING */) {
1992
2112
  values.push({ type: "STRING", value: tok.value });
1993
2113
  } else if (tok.kind === "NUMBER" /* NUMBER */) {
1994
- values.push({ type: "NUMBER", value: Number(tok.value) });
2114
+ values.push(makeNumberLiteral(tok.value));
1995
2115
  } else if (tok.kind === "-" /* MINUS */ || tok.kind === "+" /* PLUS */) {
1996
2116
  const number = this.peek();
1997
2117
  if (number.kind !== "NUMBER" /* NUMBER */) {
1998
2118
  throw new ParseError(invalidValueMessage, tok);
1999
2119
  }
2000
2120
  this.advance();
2001
- const sign = tok.kind === "-" /* MINUS */ ? -1 : 1;
2002
- values.push({ type: "NUMBER", value: sign * Number(number.value) });
2121
+ const sign = tok.kind === "-" /* MINUS */ ? "-" : "+";
2122
+ values.push(makeNumberLiteral(`${sign}${number.value}`));
2003
2123
  } else if (tok.kind === "VARIABLE" /* VARIABLE */) {
2004
2124
  values.push({ type: "VARIABLE", name: tok.value.slice(1).toLowerCase() });
2005
2125
  } else {
@@ -2186,12 +2306,16 @@ var Parser = class {
2186
2306
  } else if (this.peek().kind === "IF" /* IF */) {
2187
2307
  const expr = this.parseIfExpr();
2188
2308
  row.push({ type: "CASE_VALUE", expr });
2309
+ } else if (this.peek().kind === "-" /* MINUS */ || this.peek().kind === "+" /* PLUS */) {
2310
+ const sign = this.advance();
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");
2312
+ row.push(makeNumberLiteral(`${sign.kind === "-" /* MINUS */ ? "-" : "+"}${number.value}`));
2189
2313
  } else {
2190
2314
  const tok = this.advance();
2191
2315
  if (tok.kind === "STRING" /* STRING */) {
2192
2316
  row.push({ type: "STRING", value: tok.value });
2193
2317
  } else if (tok.kind === "NUMBER" /* NUMBER */) {
2194
- row.push({ type: "NUMBER", value: Number(tok.value) });
2318
+ row.push(makeNumberLiteral(tok.value));
2195
2319
  } else {
2196
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);
2197
2321
  }
@@ -2215,6 +2339,12 @@ var Parser = class {
2215
2339
  const { appId, subtableCode } = extractTableRef(name, this.prev());
2216
2340
  this.expect("SET" /* SET */);
2217
2341
  const assignments = this.parseAssignments();
2342
+ if (subtableCode && assignments.some((a) => a.value.type === "STRING_FUNC")) {
2343
+ throw new ParseError(
2344
+ "\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE SET \u3067\u306F\u6587\u5B57\u5217\u95A2\u6570\u3092\u76F4\u63A5\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093",
2345
+ this.prev()
2346
+ );
2347
+ }
2218
2348
  let from = null;
2219
2349
  if (this.consume("FROM" /* FROM */)) {
2220
2350
  const table = this.parseTableRef();
@@ -2258,7 +2388,14 @@ var Parser = class {
2258
2388
  from.targetFilter = decomposed.targetFilter;
2259
2389
  } else if (assignments.some((a) => a.value.type === "SOURCE_FIELD")) {
2260
2390
  throw new ParseError(
2261
- "SET \u306E\u5024\u306B\u306F\u30EA\u30C6\u30E9\u30EB\u30FB\u7B97\u8853\u5F0F\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u30D5\u30A3\u30FC\u30EB\u30C9\u53C2\u7167\u306E\u307F\u306F\u4E0D\u53EF\uFF09",
2391
+ "SET \u306E\u5024\u306B\u30D5\u30A3\u30FC\u30EB\u30C9\u53C2\u7167\u3092\u5358\u72EC\u3067\u6307\u5B9A\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093",
2392
+ whereTok
2393
+ );
2394
+ } else if (assignments.some(
2395
+ (a) => a.value.type === "STRING_FUNC" && this.nodeContainsAnyQualifier(a.value)
2396
+ )) {
2397
+ throw new ParseError(
2398
+ "UPDATE SET \u306E\u6587\u5B57\u5217\u95A2\u6570\u3067\u306F\u66F4\u65B0\u5148\u30D5\u30A3\u30FC\u30EB\u30C9\u3092\u4FEE\u98FE\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044",
2262
2399
  whereTok
2263
2400
  );
2264
2401
  }
@@ -2325,6 +2462,12 @@ var Parser = class {
2325
2462
  }
2326
2463
  validateUpdateFromAssignments(assignments, sourceAlias, tok) {
2327
2464
  for (const assignment of assignments) {
2465
+ if (assignment.value.type === "STRING_FUNC") {
2466
+ throw new ParseError(
2467
+ "UPDATE ... FROM \u306E SET \u3067\u306F\u6587\u5B57\u5217\u95A2\u6570\u3092\u76F4\u63A5\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093",
2468
+ tok
2469
+ );
2470
+ }
2328
2471
  if (assignment.value.type === "SOURCE_FIELD") {
2329
2472
  if (assignment.value.alias.toLowerCase() !== sourceAlias.toLowerCase()) {
2330
2473
  throw new ParseError(`UPDATE ... FROM \u306E SET \u53C2\u7167\u306F\u30BD\u30FC\u30B9 alias ${sourceAlias} \u3067\u4FEE\u98FE\u3057\u3066\u304F\u3060\u3055\u3044`, tok);
@@ -2464,9 +2607,17 @@ var Parser = class {
2464
2607
  this.expect(")" /* RPAREN */);
2465
2608
  return { type: "SCALAR_SUBQUERY", query };
2466
2609
  }
2467
- const node = this.parseArithAddSub();
2610
+ const previousAllowUnaryPlusNumber = this.allowUnaryPlusNumber;
2611
+ this.allowUnaryPlusNumber = true;
2612
+ let node;
2613
+ try {
2614
+ node = this.parseArithAddSub();
2615
+ } finally {
2616
+ this.allowUnaryPlusNumber = previousAllowUnaryPlusNumber;
2617
+ }
2468
2618
  if (node.type === "NUMBER") return node;
2469
2619
  if (node.type === "ARITH") return node;
2620
+ if (node.type === "STRING_FUNC") return node;
2470
2621
  if (node.type === "FIELD_REF") {
2471
2622
  const dot = node.field.indexOf(".");
2472
2623
  if (dot > 0 && dot < node.field.length - 1) {
@@ -2474,7 +2625,7 @@ var Parser = class {
2474
2625
  }
2475
2626
  }
2476
2627
  throw new ParseError(
2477
- "SET \u306E\u5024\u306B\u306F\u30EA\u30C6\u30E9\u30EB\u30FB\u7B97\u8853\u5F0F\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u30D5\u30A3\u30FC\u30EB\u30C9\u53C2\u7167\u306E\u307F\u306F\u4E0D\u53EF\uFF09",
2628
+ "SET \u306E\u5024\u306B\u30D5\u30A3\u30FC\u30EB\u30C9\u53C2\u7167\u3092\u5358\u72EC\u3067\u6307\u5B9A\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093",
2478
2629
  tok
2479
2630
  );
2480
2631
  }
@@ -2998,7 +3149,7 @@ function convertValue(value, op) {
2998
3149
  case "STRING":
2999
3150
  return convertString(value);
3000
3151
  case "NUMBER":
3001
- return String(value.value);
3152
+ return numberLiteralText(value);
3002
3153
  case "KINTONE_FUNC":
3003
3154
  return convertKintoneFunc(value);
3004
3155
  case "IN_LIST":
@@ -3027,7 +3178,7 @@ function convertInList(v, op) {
3027
3178
  }
3028
3179
  assertResolvedInListValues(v.values);
3029
3180
  const values = v.values.map(
3030
- (item) => item.type === "STRING" ? convertString(item) : String(item.value)
3181
+ (item) => item.type === "STRING" ? convertString(item) : numberLiteralText(item)
3031
3182
  ).join(",");
3032
3183
  return `(${values})`;
3033
3184
  }
@@ -3540,7 +3691,7 @@ function aggregateSyntheticName(func, distinct, arg) {
3540
3691
  }
3541
3692
  function arithNodeLabel(node) {
3542
3693
  if (node.type === "FIELD_REF") return node.field;
3543
- if (node.type === "NUMBER") return String(node.value);
3694
+ if (node.type === "NUMBER") return numberLiteralText(node);
3544
3695
  if (node.type === "STRING_FUNC") return stringFuncLabel(node);
3545
3696
  return `(${arithNodeLabel(node.left)}${node.op}${arithNodeLabel(node.right)})`;
3546
3697
  }
@@ -3697,7 +3848,7 @@ function isNumericCandidate(expr, options) {
3697
3848
  if (!isTargetField(expr.left, options)) return false;
3698
3849
  if (expr.right.type !== "NUMBER") return false;
3699
3850
  if (expr.op === "=") return true;
3700
- 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);
3701
3852
  }
3702
3853
  function isSelectionInCandidate(expr, options) {
3703
3854
  if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
@@ -4302,9 +4453,10 @@ function triCompare(left, right) {
4302
4453
  }
4303
4454
  function numberKey(value) {
4304
4455
  if (value === "") return { band: 0 };
4456
+ const decimal = parseExactDecimal(value);
4457
+ if (decimal !== null) return { band: 2, value: decimal };
4305
4458
  const numeric = Number(value);
4306
4459
  if (numeric === Number.NEGATIVE_INFINITY) return { band: 1 };
4307
- if (Number.isFinite(numeric)) return { band: 2, value: numeric };
4308
4460
  if (numeric === Number.POSITIVE_INFINITY) return { band: 3 };
4309
4461
  if (value === "NaN") return { band: 4 };
4310
4462
  return { band: 5, value };
@@ -4313,7 +4465,7 @@ function compareNumbers(left, right) {
4313
4465
  const a = numberKey(left);
4314
4466
  const b = numberKey(right);
4315
4467
  if (a.band !== b.band) return a.band < b.band ? -1 : 1;
4316
- 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);
4317
4469
  if (a.band === 5 && b.band === 5) return compareCodePointStrings(a.value, b.value);
4318
4470
  return 0;
4319
4471
  }
@@ -4413,7 +4565,9 @@ function selectScalarExtreme(values, extreme) {
4413
4565
  const numeric = candidates.every((value) => !Number.isNaN(Number(value)));
4414
4566
  const compare = (left, right) => {
4415
4567
  if (numeric) {
4416
- 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));
4417
4571
  if (numericCmp !== 0) return numericCmp;
4418
4572
  }
4419
4573
  return compareCodePointStrings(left, right);
@@ -4499,6 +4653,149 @@ function applyRoundOp(op, num, digits) {
4499
4653
  if (digits > 0) return String(parseFloat(raw.toFixed(digits)));
4500
4654
  return String(raw);
4501
4655
  }
4656
+ function isHighSurrogate(codeUnit) {
4657
+ return codeUnit >= 55296 && codeUnit <= 56319;
4658
+ }
4659
+ function isLowSurrogate(codeUnit) {
4660
+ return codeUnit >= 56320 && codeUnit <= 57343;
4661
+ }
4662
+ function splitsSurrogatePair(value, index) {
4663
+ return index > 0 && index < value.length && isHighSurrogate(value.charCodeAt(index - 1)) && isLowSurrogate(value.charCodeAt(index));
4664
+ }
4665
+ function normalizeSliceIndex(index, length) {
4666
+ if (Number.isNaN(index) || index === Number.NEGATIVE_INFINITY) return 0;
4667
+ if (index === Number.POSITIVE_INFINITY) return length;
4668
+ const integer = Math.trunc(index);
4669
+ return integer < 0 ? Math.max(length + integer, 0) : Math.min(integer, length);
4670
+ }
4671
+ function sliceSafePrefix(value, budget) {
4672
+ let end = Math.min(Math.max(0, budget), value.length);
4673
+ if (splitsSurrogatePair(value, end)) end -= 1;
4674
+ return value.slice(0, end);
4675
+ }
4676
+ function sliceSafeSuffix(value, budget) {
4677
+ let start = Math.max(0, value.length - budget);
4678
+ if (splitsSurrogatePair(value, start)) start += 1;
4679
+ return value.slice(start);
4680
+ }
4681
+ function sliceSafeRange(value, rawStart, rawEnd) {
4682
+ let start = normalizeSliceIndex(rawStart, value.length);
4683
+ let end = normalizeSliceIndex(rawEnd, value.length);
4684
+ if (end <= start) return "";
4685
+ if (splitsSurrogatePair(value, start)) start += 1;
4686
+ if (splitsSurrogatePair(value, end)) end -= 1;
4687
+ return value.slice(start, Math.max(start, end));
4688
+ }
4689
+ function makeSafePadding(pad, gap) {
4690
+ const repeated = pad.repeat(Math.ceil(gap / pad.length));
4691
+ return sliceSafePrefix(repeated, gap);
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
+ }
4502
4799
  function evalStringFunc(expr, row) {
4503
4800
  const args = expr.args.map((a) => evalStringFuncArg(a, row));
4504
4801
  switch (expr.func) {
@@ -4514,23 +4811,26 @@ function evalStringFunc(expr, row) {
4514
4811
  return (args[0] ?? "").trimEnd();
4515
4812
  case "LENGTH":
4516
4813
  return String((args[0] ?? "").length);
4814
+ case "LENGTH_CHAR":
4815
+ assertArity("LENGTH_CHAR", args, 1, 1);
4816
+ return String([...args[0] ?? ""].length);
4517
4817
  case "SUBSTRING": {
4518
4818
  const str = args[0] ?? "";
4519
4819
  const start = Math.max(0, Number(args[1] ?? "1") - 1);
4520
4820
  const len = args[2] !== void 0 ? Number(args[2]) : void 0;
4521
- return len !== void 0 ? str.slice(start, start + len) : str.slice(start);
4821
+ return sliceSafeRange(str, start, len !== void 0 ? start + len : str.length);
4522
4822
  }
4523
4823
  case "LEFT": {
4524
4824
  assertArity("LEFT", args, 2, 2);
4525
4825
  const str = args[0];
4526
4826
  const n = Math.trunc(Number(args[1]));
4527
- return Number.isNaN(n) || n <= 0 ? "" : str.slice(0, n);
4827
+ return Number.isNaN(n) || n <= 0 ? "" : sliceSafePrefix(str, n);
4528
4828
  }
4529
4829
  case "RIGHT": {
4530
4830
  assertArity("RIGHT", args, 2, 2);
4531
4831
  const str = args[0];
4532
4832
  const n = Math.trunc(Number(args[1]));
4533
- return Number.isNaN(n) || n <= 0 ? "" : str.slice(Math.max(0, str.length - n));
4833
+ return Number.isNaN(n) || n <= 0 ? "" : sliceSafeSuffix(str, n);
4534
4834
  }
4535
4835
  case "INSTR":
4536
4836
  assertArity("INSTR", args, 2, 2);
@@ -4541,10 +4841,11 @@ function evalStringFunc(expr, row) {
4541
4841
  const str = args[0];
4542
4842
  const n = Math.trunc(Number(args[1]));
4543
4843
  if (Number.isNaN(n) || n <= 0) return "";
4544
- if (str.length >= n) return str.slice(0, n);
4844
+ if (str.length >= n) return sliceSafePrefix(str, n);
4545
4845
  const pad = args[2] ?? " ";
4546
4846
  if (pad === "") return str;
4547
- return expr.func === "LPAD" ? str.padStart(n, pad) : str.padEnd(n, pad);
4847
+ const padding = makeSafePadding(pad, n - str.length);
4848
+ return expr.func === "LPAD" ? padding + str : str + padding;
4548
4849
  }
4549
4850
  case "GREATEST":
4550
4851
  case "LEAST":
@@ -4558,6 +4859,36 @@ function evalStringFunc(expr, row) {
4558
4859
  const to = args[2] ?? "";
4559
4860
  return from === "" ? str : str.split(from).join(to);
4560
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
+ }
4877
+ case "TRANSLATE": {
4878
+ assertArity("TRANSLATE", args, 3, 3);
4879
+ const from = [...args[1]];
4880
+ const to = [...args[2]];
4881
+ if (from.length !== to.length) {
4882
+ throw new Error(
4883
+ `ArgumentError: TRANSLATE \u306E from \u3068 to \u306F\u540C\u3058\u6587\u5B57\u6570\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\uFF08from=${from.length}, to=${to.length}\uFF09`
4884
+ );
4885
+ }
4886
+ const map = /* @__PURE__ */ new Map();
4887
+ from.forEach((ch, i) => {
4888
+ if (!map.has(ch)) map.set(ch, to[i]);
4889
+ });
4890
+ return [...args[0]].map((ch) => map.get(ch) ?? ch).join("");
4891
+ }
4561
4892
  case "COALESCE":
4562
4893
  return args.find((a) => a !== "") ?? "";
4563
4894
  case "NULLIF":
@@ -4721,7 +5052,7 @@ function evalStringFuncArg(arg, row) {
4721
5052
  if (arg.type === "STRING") return arg.value;
4722
5053
  if (arg.type === "STRING_FUNC") return evalStringFunc(arg, row);
4723
5054
  if (arg.type === "FIELD_REF") return resolveFieldRef(row, arg.field);
4724
- if (arg.type === "NUMBER") return String(arg.value);
5055
+ if (arg.type === "NUMBER") return numberLiteralText(arg);
4725
5056
  if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return "";
4726
5057
  return String(evalArithExpr(arg, row));
4727
5058
  }
@@ -4770,7 +5101,7 @@ function evalOp(op, leftStr, right, row, fieldType, resolveFieldType, semantics
4770
5101
  let values = null;
4771
5102
  if (right.type === "IN_LIST") {
4772
5103
  assertResolvedInListValues2(right.values);
4773
- 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));
4774
5105
  }
4775
5106
  if (right.type === "SUBQUERY_IN_LIST") {
4776
5107
  values = right.resolved;
@@ -4795,6 +5126,7 @@ function evalOp(op, leftStr, right, row, fieldType, resolveFieldType, semantics
4795
5126
  }
4796
5127
  var NUMERIC_STRING_FUNCTIONS = /* @__PURE__ */ new Set([
4797
5128
  "LENGTH",
5129
+ "LENGTH_CHAR",
4798
5130
  "INSTR",
4799
5131
  "ROUND",
4800
5132
  "FLOOR",
@@ -4849,6 +5181,10 @@ var SINGLE_OBJECT_FIELD_TYPES = /* @__PURE__ */ new Set(["CREATOR", "MODIFIER"])
4849
5181
  function typedInContains(leftStr, values, fieldType) {
4850
5182
  const fallback = () => values.has(leftStr);
4851
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
+ }
4852
5188
  let parsed;
4853
5189
  if (STRING_ARRAY_FIELD_TYPES.has(fieldType) || OBJECT_ARRAY_FIELD_TYPES.has(fieldType) || SINGLE_OBJECT_FIELD_TYPES.has(fieldType)) {
4854
5190
  try {
@@ -4907,7 +5243,7 @@ function resolveValue(value, row, resolveFieldType, resolveFieldSemantics2) {
4907
5243
  case "STRING":
4908
5244
  return value.value;
4909
5245
  case "NUMBER":
4910
- return String(value.value);
5246
+ return numberLiteralText(value);
4911
5247
  case "KINTONE_FUNC":
4912
5248
  return resolveKintoneFunc(value.name);
4913
5249
  case "IN_LIST":
@@ -5042,7 +5378,7 @@ function updateToPutBatches(stmt, ids, fieldTypes = /* @__PURE__ */ new Map()) {
5042
5378
  function buildUpdateRecord(assignments, fieldTypes) {
5043
5379
  const record = {};
5044
5380
  for (const { field, value } of assignments) {
5045
- if (value.type === "ARITH" || value.type === "CASE_VALUE" || value.type === "SOURCE_FIELD") continue;
5381
+ if (value.type === "ARITH" || value.type === "CASE_VALUE" || value.type === "STRING_FUNC" || value.type === "SOURCE_FIELD") continue;
5046
5382
  record[field] = { value: toKintoneValue(value, fieldTypes.get(field)) };
5047
5383
  }
5048
5384
  return record;
@@ -5052,12 +5388,19 @@ function hasArithAssignment(stmt) {
5052
5388
  (a) => a.value.type === "ARITH" || a.value.type === "CASE_VALUE"
5053
5389
  );
5054
5390
  }
5391
+ function hasRowDependentAssignment(stmt) {
5392
+ return stmt.assignments.some(
5393
+ (a) => a.value.type === "ARITH" || a.value.type === "CASE_VALUE" || a.value.type === "STRING_FUNC"
5394
+ );
5395
+ }
5055
5396
  function updateToGetQueryForArith(stmt) {
5056
5397
  assertDmlWhereIsSafe(stmt.where);
5057
5398
  const refFields = /* @__PURE__ */ new Set();
5058
5399
  for (const { value } of stmt.assignments) {
5059
5400
  if (value.type === "ARITH") {
5060
5401
  collectArithFields2(value, refFields);
5402
+ } else if (value.type === "STRING_FUNC") {
5403
+ collectStringFuncFields2(value, refFields);
5061
5404
  } else if (value.type === "CASE_VALUE") {
5062
5405
  collectCaseFields(value.expr, refFields);
5063
5406
  }
@@ -5144,6 +5487,8 @@ function updateToPutBatchesArith(stmt, records, fieldTypes = /* @__PURE__ */ new
5144
5487
  for (const { field, value } of stmt.assignments) {
5145
5488
  if (value.type === "ARITH") {
5146
5489
  record[field] = { value: String(evalArith(value, raw)) };
5490
+ } else if (value.type === "STRING_FUNC") {
5491
+ record[field] = { value: evalStringFunc(value, row) };
5147
5492
  } else if (value.type === "CASE_VALUE") {
5148
5493
  record[field] = { value: evalCaseWhenValue(value.expr, row, fieldTypes.get(field)) };
5149
5494
  } else if (value.type === "SOURCE_FIELD") {
@@ -5192,6 +5537,8 @@ function updateFromToPutBatches(stmt, matched, fieldTypes = /* @__PURE__ */ new
5192
5537
  throw new DmlConvertError(`\u6570\u5024\u30D5\u30A3\u30FC\u30EB\u30C9 ${field} \u306B\u5909\u63DB\u3067\u304D\u306A\u3044\u5024\u3067\u3059: ${raw}`);
5193
5538
  }
5194
5539
  record[field] = { value: toKintoneValue({ type: "STRING", value: raw }, fieldType) };
5540
+ } else if (value.type === "STRING_FUNC") {
5541
+ throw new DmlConvertError("UPDATE ... FROM \u306E SET \u3067\u306F\u6587\u5B57\u5217\u95A2\u6570\u3092\u76F4\u63A5\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093");
5195
5542
  } else if (value.type === "ARITH") {
5196
5543
  record[field] = { value: String(evalArith(value, target)) };
5197
5544
  } else if (value.type === "CASE_VALUE") {
@@ -5336,7 +5683,7 @@ function convertDmlSqlValue(value, fieldType) {
5336
5683
  case "STRING":
5337
5684
  return convertString2(value.value, fieldType);
5338
5685
  case "NUMBER":
5339
- return String(value.value);
5686
+ return numberLiteralText(value);
5340
5687
  case "ARRAY":
5341
5688
  return convertArray(value.elements.map((e) => e.value), fieldType);
5342
5689
  case "KINTONE_FUNC":
@@ -5968,7 +6315,7 @@ function evalAggArithExpr(node, rows, resolveAggSortKind) {
5968
6315
  }
5969
6316
  }
5970
6317
  function aggArithDefaultKey(node) {
5971
- if (node.type === "NUMBER") return String(node.value);
6318
+ if (node.type === "NUMBER") return numberLiteralText(node);
5972
6319
  if (node.type === "AGG_REF") return aggregateSyntheticName2(node.func, node.distinct, node.arg);
5973
6320
  return `${aggArithDefaultKey(node.left)}${node.op}${aggArithDefaultKey(node.right)}`;
5974
6321
  }
@@ -6083,6 +6430,7 @@ function compareSortKeys(a, b, meta) {
6083
6430
  }
6084
6431
  var NUMERIC_ORDER_FUNCTIONS = /* @__PURE__ */ new Set([
6085
6432
  "LENGTH",
6433
+ "LENGTH_CHAR",
6086
6434
  "INSTR",
6087
6435
  "ROUND",
6088
6436
  "FLOOR",
@@ -6317,7 +6665,7 @@ function stripParentShortcutColumns(row) {
6317
6665
  function arithColDefaultKey(expr) {
6318
6666
  const nodeLabel = (n) => {
6319
6667
  if (n.type === "FIELD_REF") return n.field;
6320
- if (n.type === "NUMBER") return String(n.value);
6668
+ if (n.type === "NUMBER") return numberLiteralText(n);
6321
6669
  if (n.type === "STRING_FUNC") return stringFuncDefaultKey(n);
6322
6670
  return `(${nodeLabel(n.left)}${n.op}${nodeLabel(n.right)})`;
6323
6671
  };
@@ -6346,10 +6694,11 @@ function hasAggregateInStringFuncExpr2(expr) {
6346
6694
  function resolveAggInStringFuncArg(arg, rows, resolveAggSortKind) {
6347
6695
  if (arg.type === "AGG_REF") {
6348
6696
  const value = evalAggregate(arg.func, arg.distinct, arg.arg, arg.separator, rows, resolveAggSortKind);
6349
- return typeof value === "number" ? { type: "NUMBER", value } : { type: "STRING", value };
6697
+ return typeof value === "number" ? { type: "NUMBER", value, raw: String(value) } : { type: "STRING", value };
6350
6698
  }
6351
6699
  if (arg.type === "AGG_ARITH") {
6352
- return { type: "NUMBER", value: evalAggArithExpr(arg, rows, resolveAggSortKind) };
6700
+ const value = evalAggArithExpr(arg, rows, resolveAggSortKind);
6701
+ return { type: "NUMBER", value, raw: String(value) };
6353
6702
  }
6354
6703
  if (arg.type === "STRING_FUNC") {
6355
6704
  return resolveAggInStringFuncExpr(arg, rows, resolveAggSortKind);
@@ -6467,10 +6816,43 @@ function toFlatString(value) {
6467
6816
  }
6468
6817
  }
6469
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
+
6470
6852
  // src/core/dmlValidation.ts
6471
6853
  var ARRAY_TYPES2 = /* @__PURE__ */ new Set(["CHECK_BOX", "MULTI_SELECT"]);
6472
6854
  var CHOICE_TYPES = /* @__PURE__ */ new Set(["DROP_DOWN", "RADIO_BUTTON", "CHECK_BOX", "MULTI_SELECT"]);
6473
- function validateAndNormalizeDmlValue(raw, field) {
6855
+ function validateAndNormalizeDmlValue(raw, field, numberPrecision) {
6474
6856
  if (field.fieldType === "DATE" || field.fieldType === "TIME" || field.fieldType === "DATETIME") {
6475
6857
  const original = rawScalarText(raw);
6476
6858
  if (original !== "" && !isValidTemporalInput(original, field.fieldType)) {
@@ -6489,7 +6871,8 @@ function validateAndNormalizeDmlValue(raw, field) {
6489
6871
  }
6490
6872
  if (!isEmpty(value) && field.fieldType === "NUMBER") {
6491
6873
  const text = String(value);
6492
- if (!isFiniteDecimal(text)) {
6874
+ const decimal = parseExactDecimal(text);
6875
+ if (decimal === null) {
6493
6876
  return { ok: false, code: "ERR_TYPE_NUMBER", message: `${field.code} \u306F\u6570\u5024\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044` };
6494
6877
  }
6495
6878
  if (field.minValue != null && compareDecimal(text, field.minValue) < 0) {
@@ -6498,6 +6881,17 @@ function validateAndNormalizeDmlValue(raw, field) {
6498
6881
  if (field.maxValue != null && compareDecimal(text, field.maxValue) > 0) {
6499
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` };
6500
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
+ }
6501
6895
  }
6502
6896
  if (!isEmpty(value) && (field.fieldType === "DATE" || field.fieldType === "TIME" || field.fieldType === "DATETIME")) {
6503
6897
  if (!isValidTemporal(String(value), field.fieldType)) {
@@ -6525,7 +6919,8 @@ function validateAndNormalizeDmlValue(raw, field) {
6525
6919
  }
6526
6920
  function rawScalarText(raw) {
6527
6921
  if (raw == null) return "";
6528
- 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;
6529
6924
  return typeof raw === "string" || typeof raw === "number" ? String(raw) : "";
6530
6925
  }
6531
6926
  function isValidTemporalInput(value, type) {
@@ -6575,34 +6970,6 @@ function isEmpty(value) {
6575
6970
  function typeCode(type) {
6576
6971
  return type === "NUMBER" ? "ERR_TYPE_NUMBER" : "ERR_TYPE_DATE";
6577
6972
  }
6578
- function isFiniteDecimal(value) {
6579
- return /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(value.trim());
6580
- }
6581
- function compareDecimal(left, right) {
6582
- const normalize = (input) => {
6583
- let s = input.trim();
6584
- let sign = 1;
6585
- if (s.startsWith("-")) {
6586
- sign = -1;
6587
- s = s.slice(1);
6588
- } else if (s.startsWith("+")) s = s.slice(1);
6589
- let [whole, fraction = ""] = s.split(".");
6590
- whole = (whole || "0").replace(/^0+(?=\d)/, "");
6591
- fraction = fraction.replace(/0+$/, "");
6592
- if (/^0*$/.test(whole) && fraction === "") sign = 1;
6593
- return { sign, whole, fraction };
6594
- };
6595
- const a = normalize(left);
6596
- const b = normalize(right);
6597
- if (a.sign !== b.sign) return a.sign < b.sign ? -1 : 1;
6598
- const direction = a.sign;
6599
- if (a.whole.length !== b.whole.length) return a.whole.length < b.whole.length ? -direction : direction;
6600
- if (a.whole !== b.whole) return a.whole < b.whole ? -direction : direction;
6601
- const width = Math.max(a.fraction.length, b.fraction.length);
6602
- const af = a.fraction.padEnd(width, "0");
6603
- const bf = b.fraction.padEnd(width, "0");
6604
- return af === bf ? 0 : af < bf ? -direction : direction;
6605
- }
6606
6973
  function isValidTemporal(value, type) {
6607
6974
  if (type === "TIME") {
6608
6975
  const m2 = /^(\d{2}):(\d{2})(?::(\d{2}))?$/.exec(value);
@@ -6630,7 +6997,7 @@ var VALIDATION_META_COLUMNS = [
6630
6997
  "$err_code",
6631
6998
  "$err_message"
6632
6999
  ];
6633
- function validateDmlCandidates(candidates, operation, payloadFields, targetFields, fieldInfos, statementNumber) {
7000
+ function validateDmlCandidates(candidates, operation, payloadFields, targetFields, fieldInfos, statementNumber, numberPrecision) {
6634
7001
  const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
6635
7002
  const errors = [];
6636
7003
  const invalid = /* @__PURE__ */ new Set();
@@ -6638,7 +7005,7 @@ function validateDmlCandidates(candidates, operation, payloadFields, targetField
6638
7005
  candidate.record ??= {};
6639
7006
  const rowErrors = [...candidate.preErrors];
6640
7007
  for (const code of targetFields) {
6641
- const result = validateAndNormalizeDmlValue(candidate.payload.get(code), infoByCode.get(code));
7008
+ const result = validateAndNormalizeDmlValue(candidate.payload.get(code), infoByCode.get(code), numberPrecision);
6642
7009
  if (!result.ok) rowErrors.push({ field: code, code: result.code, message: result.message });
6643
7010
  else candidate.record[code] = { value: result.value };
6644
7011
  }
@@ -6648,14 +7015,14 @@ function validateDmlCandidates(candidates, operation, payloadFields, targetField
6648
7015
  if (candidate.payload.has(info.code)) continue;
6649
7016
  const emptyDefault = isEmptyDmlValue(info.defaultValue);
6650
7017
  if (!emptyDefault) {
6651
- const defaultResult = validateAndNormalizeDmlValue(info.defaultValue, info);
7018
+ const defaultResult = validateAndNormalizeDmlValue(info.defaultValue, info, numberPrecision);
6652
7019
  if (!defaultResult.ok) rowErrors.push({
6653
7020
  field: info.code,
6654
7021
  code: defaultResult.code,
6655
7022
  message: `\u65E2\u5B9A\u5024: ${defaultResult.message}`
6656
7023
  });
6657
7024
  } else {
6658
- const emptyResult = validateAndNormalizeDmlValue("", info);
7025
+ const emptyResult = validateAndNormalizeDmlValue("", info, numberPrecision);
6659
7026
  if (!emptyResult.ok) {
6660
7027
  rowErrors.push({ field: info.code, code: emptyResult.code, message: emptyResult.message });
6661
7028
  } else if (info.required) {
@@ -6683,7 +7050,8 @@ function renderValidationValue(value) {
6683
7050
  if (value == null) return "";
6684
7051
  if (typeof value === "object" && "type" in value) {
6685
7052
  const sql = value;
6686
- 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 ?? "");
6687
7055
  if (sql.type === "ARRAY") return JSON.stringify(sql.elements?.map((e) => e.value) ?? []);
6688
7056
  }
6689
7057
  if (Array.isArray(value)) return JSON.stringify(value);
@@ -6925,6 +7293,7 @@ function createEmptyMetrics() {
6925
7293
  putCalls: 0,
6926
7294
  deleteCalls: 0,
6927
7295
  fieldCalls: 0,
7296
+ numberPrecisionCalls: 0,
6928
7297
  appsCalls: 0,
6929
7298
  processStatusCalls: 0,
6930
7299
  cursorCreateCalls: 0,
@@ -7010,6 +7379,10 @@ function wrapClientWithMetrics(client, metrics) {
7010
7379
  metrics.fieldCalls += 1;
7011
7380
  return client.getFields(appId);
7012
7381
  },
7382
+ getNumberPrecision: (appId) => {
7383
+ metrics.numberPrecisionCalls += 1;
7384
+ return client.getNumberPrecision(appId);
7385
+ },
7013
7386
  getProcessStatuses: (appId) => {
7014
7387
  metrics.processStatusCalls += 1;
7015
7388
  return client.getProcessStatuses(appId);
@@ -7277,7 +7650,7 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
7277
7650
  const first = resolvedStmt2.expr.query.columns[0];
7278
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");
7279
7652
  const numberValue = numeric ? Number(value) : Number.NaN;
7280
- 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 });
7281
7654
  } catch (e) {
7282
7655
  if (e instanceof ScalarSubqueryError) {
7283
7656
  throw new Error(`ArgumentError: ${e.message}`);
@@ -7295,7 +7668,10 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
7295
7668
  variables.set(stmt.name, { type: "string", value: injected[stmt.name] });
7296
7669
  } else {
7297
7670
  const value = evaluateScalarExpr(stmt.default);
7298
- 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
+ });
7299
7675
  }
7300
7676
  return {};
7301
7677
  }
@@ -7471,7 +7847,7 @@ function evaluateScalarExpr(expr) {
7471
7847
  case "STRING":
7472
7848
  return { type: "string", value: expr.value };
7473
7849
  case "NUMBER":
7474
- return { type: "number", value: expr.value };
7850
+ return { type: "number", value: expr.value, raw: numberLiteralText(expr) };
7475
7851
  case "KINTONE_FUNC":
7476
7852
  return { type: "string", value: resolveKintoneFunc(expr.name) };
7477
7853
  case "STRING_FUNC":
@@ -7481,7 +7857,7 @@ function evaluateScalarExpr(expr) {
7481
7857
  if (!Number.isFinite(value)) {
7482
7858
  throw new Error("ArgumentError: SET scalar arithmetic produced a non-finite number.");
7483
7859
  }
7484
- return { type: "number", value };
7860
+ return { type: "number", value, raw: String(value) };
7485
7861
  }
7486
7862
  }
7487
7863
  }
@@ -7496,7 +7872,7 @@ function resolveVariableRefs(node, variables) {
7496
7872
  if (value === void 0) {
7497
7873
  throw new Error(`ParseError: variable @${obj["name"]} is not defined in this batch.`);
7498
7874
  }
7499
- 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 };
7500
7876
  }
7501
7877
  return Object.fromEntries(
7502
7878
  Object.entries(obj).map(([key, value]) => [key, resolveVariableRefs(value, variables)])
@@ -7562,7 +7938,7 @@ async function evalAssertOperand(operand, client, options, cacheContext, tempTab
7562
7938
  case "VARIABLE":
7563
7939
  throw new Error(`ParseError: unresolved batch variable @${operand.name}.`);
7564
7940
  case "NUMBER":
7565
- return String(operand.value);
7941
+ return numberLiteralText(operand);
7566
7942
  case "STRING":
7567
7943
  return operand.value;
7568
7944
  case "ARITH":
@@ -8303,6 +8679,7 @@ function systemColumnMeta(field) {
8303
8679
  }
8304
8680
  var NUMBER_RETURNING_STRING_FUNCTIONS = /* @__PURE__ */ new Set([
8305
8681
  "LENGTH",
8682
+ "LENGTH_CHAR",
8306
8683
  "INSTR",
8307
8684
  "ROUND",
8308
8685
  "FLOOR",
@@ -8844,8 +9221,8 @@ async function fetchTableRecordsForFullScan(stmt, table, client, maxRecords, par
8844
9221
  }
8845
9222
  var UPSERT_IN_CHUNK_SIZE = 50;
8846
9223
  function normalizeKeyPart(v) {
8847
- const t = v.trim();
8848
- if (t !== "" && !Number.isNaN(Number(t))) return String(Number(t));
9224
+ const decimal = parseExactDecimal(v);
9225
+ if (decimal !== null) return JSON.stringify(decimal);
8849
9226
  return v;
8850
9227
  }
8851
9228
  function upsertCompositeKey(parts) {
@@ -8997,6 +9374,7 @@ var optionOrderCache = /* @__PURE__ */ new Map();
8997
9374
  var sortKindCache = /* @__PURE__ */ new Map();
8998
9375
  var fieldInfoCache = /* @__PURE__ */ new Map();
8999
9376
  var processStatusCache = /* @__PURE__ */ new Map();
9377
+ var numberPrecisionCache = /* @__PURE__ */ new Map();
9000
9378
  function getScopedCacheValue(root, cacheContext, appId) {
9001
9379
  return root.get(cacheContext)?.get(appId);
9002
9380
  }
@@ -9018,6 +9396,13 @@ async function getFieldsCached(appId, client, cacheContext) {
9018
9396
  setScopedCacheValue(fieldInfoCache, cacheContext, appId, loading);
9019
9397
  return loading;
9020
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
+ }
9021
9406
  async function getProcessStatusesCached(appId, client, cacheContext) {
9022
9407
  const cached = getScopedCacheValue(processStatusCache, cacheContext, appId);
9023
9408
  if (cached) return cached;
@@ -9270,6 +9655,45 @@ var NON_WRITABLE_FIELD_TYPES = /* @__PURE__ */ new Set([
9270
9655
  "CATEGORY",
9271
9656
  "REFERENCE_TABLE"
9272
9657
  ]);
9658
+ function assertWritableTopLevelDmlFields(appId, targetFields, fieldInfos) {
9659
+ const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
9660
+ for (const code of targetFields) {
9661
+ const info = infoByCode.get(code);
9662
+ if (!info) {
9663
+ throw new Error(`ArgumentError: DML target field ${code} does not exist.`);
9664
+ }
9665
+ if (info.inSubtable) {
9666
+ throw new Error(
9667
+ `ArgumentError: DML target field ${code} is inside a subtable. Use subtable DML syntax (for example, APP${appId}$\u30C6\u30FC\u30D6\u30EB).`
9668
+ );
9669
+ }
9670
+ if (info.writable === false || NON_WRITABLE_FIELD_TYPES.has(info.fieldType)) {
9671
+ throw new Error(`ArgumentError: DML target field ${code} is not writable (${info.fieldType}).`);
9672
+ }
9673
+ }
9674
+ }
9675
+ async function loadWritableTopLevelDmlFields(appId, targetFields, client, cacheContext) {
9676
+ const fieldInfos = await getFieldsCached(appId, client, cacheContext);
9677
+ assertWritableTopLevelDmlFields(appId, targetFields, fieldInfos);
9678
+ return fieldInfos;
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
+ }
9273
9697
  async function executeDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber) {
9274
9698
  return (await prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber)).result;
9275
9699
  }
@@ -9281,24 +9705,29 @@ var RejectLimitExceededError = class extends Error {
9281
9705
  }
9282
9706
  };
9283
9707
  async function prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber) {
9284
- if (stmt.type === "UPDATE") {
9285
- await assertDmlWhereCapability(stmt, client, cacheContext);
9286
- }
9287
9708
  const operation = stmt.type === "UPDATE" ? "UPDATE" : stmt.type.startsWith("UPSERT") ? "UPSERT" : "INSERT";
9288
9709
  const payloadFields = stmt.type === "UPDATE" ? ["$id", ...stmt.assignments.map((a) => a.field)] : [...stmt.fields];
9289
9710
  if (new Set(payloadFields).size !== payloadFields.length) {
9290
9711
  throw new Error("ArgumentError: DML target fields contain duplicates.");
9291
9712
  }
9292
- const fieldInfos = await getFieldsCached(stmt.appId, client, cacheContext);
9293
- const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
9294
9713
  const targetFields = stmt.type === "UPDATE" ? stmt.assignments.map((a) => a.field) : stmt.fields;
9295
- for (const code of targetFields) {
9296
- const info = infoByCode.get(code);
9297
- if (!info) throw new Error(`ArgumentError: DML target field ${code} does not exist.`);
9298
- if (info.writable === false || NON_WRITABLE_FIELD_TYPES.has(info.fieldType)) {
9299
- throw new Error(`ArgumentError: DML target field ${code} is not writable (${info.fieldType}).`);
9300
- }
9714
+ const fieldInfos = await loadWritableTopLevelDmlFields(
9715
+ stmt.appId,
9716
+ targetFields,
9717
+ client,
9718
+ cacheContext
9719
+ );
9720
+ if (stmt.type === "UPDATE") {
9721
+ await assertDmlWhereCapability(stmt, client, cacheContext);
9301
9722
  }
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
+ );
9302
9731
  const candidates = await materializeValidationCandidates(stmt, operation, client, options, cacheContext, tempTables, infoByCode);
9303
9732
  const { errors, invalidRows, invalidRowNumbers } = validateDmlCandidates(
9304
9733
  candidates,
@@ -9306,7 +9735,8 @@ async function prepareDmlValidation(stmt, client, options, cacheContext, tempTab
9306
9735
  payloadFields,
9307
9736
  targetFields,
9308
9737
  fieldInfos,
9309
- statementNumber
9738
+ statementNumber,
9739
+ numberPrecision
9310
9740
  );
9311
9741
  const columns = [...payloadFields, ...VALIDATION_META_COLUMNS];
9312
9742
  const result = {
@@ -9467,7 +9897,7 @@ async function materializeUpdateValidationCandidates(stmt, client, options, cach
9467
9897
  await resolveSetSubqueries(stmt.assignments, client, options, cacheContext);
9468
9898
  const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
9469
9899
  let records;
9470
- if (hasArithAssignment(stmt)) {
9900
+ if (hasRowDependentAssignment(stmt)) {
9471
9901
  const getParams = updateToGetQueryForArith(stmt);
9472
9902
  const resolved = await fetchRecordsForSharedPlan(client.getRecords, getParams.app, getParams.query, [...getParams.fields], {
9473
9903
  maxRecords: options.maxRecords ?? 1e4,
@@ -9530,6 +9960,7 @@ async function resolveUpdateFromMatchedRecords(stmt, from, client, options, cach
9530
9960
  tempTables
9531
9961
  );
9532
9962
  const sourceByKey = /* @__PURE__ */ new Map();
9963
+ const sourceQueryByKey = /* @__PURE__ */ new Map();
9533
9964
  for (const row of sourceRows) {
9534
9965
  if (!Object.prototype.hasOwnProperty.call(row, from.joinKeyField)) {
9535
9966
  throw new Error(`ArgumentError: UPDATE ... FROM source column ${from.joinKeyField} does not exist.`);
@@ -9539,6 +9970,7 @@ async function resolveUpdateFromMatchedRecords(stmt, from, client, options, cach
9539
9970
  throw new Error(`ArgumentError: UPDATE ... FROM source has multiple rows for normalized key ${key}.`);
9540
9971
  }
9541
9972
  sourceByKey.set(key, row);
9973
+ sourceQueryByKey.set(key, String(row[from.joinKeyField]).trim());
9542
9974
  }
9543
9975
  if (sourceByKey.size === 0) return [];
9544
9976
  const maxRecords = options.maxRecords ?? 1e4;
@@ -9547,7 +9979,7 @@ async function resolveUpdateFromMatchedRecords(stmt, from, client, options, cach
9547
9979
  const targetRecords = [];
9548
9980
  const seenTargetIds = /* @__PURE__ */ new Set();
9549
9981
  let fetchedTargetCount = 0;
9550
- 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)) {
9551
9983
  const keyQuery = `${from.targetJoinField} in (${keys.map(sqlQuote).join(",")})`;
9552
9984
  const query = filterQuery ? `(${keyQuery}) and (${filterQuery})` : keyQuery;
9553
9985
  const resolved = await fetchRecordsForSharedPlan(
@@ -9648,36 +10080,28 @@ function normalizeUpdateFromJoinKey(raw, kind, side) {
9648
10080
  }
9649
10081
  if (kind === "number" && side === "target" && raw === "") return null;
9650
10082
  if (kind === "id") {
9651
- const text2 = raw.trim();
9652
- const id = Number(text2);
9653
- 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) {
9654
10086
  throw new Error(`ArgumentError: UPDATE ... FROM ${side} key must be a positive safe integer: ${raw}`);
9655
10087
  }
9656
10088
  return String(id);
9657
10089
  }
9658
- const text = raw.trim();
9659
- if (!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(text)) {
10090
+ const decimal = parseExactDecimal(raw);
10091
+ if (decimal === null) {
9660
10092
  throw new Error(`ArgumentError: UPDATE ... FROM ${side} key must be a finite decimal: ${raw}`);
9661
10093
  }
9662
- let unsigned = text;
9663
- let negative = false;
9664
- if (unsigned.startsWith("-") || unsigned.startsWith("+")) {
9665
- negative = unsigned[0] === "-";
9666
- unsigned = unsigned.slice(1);
9667
- }
9668
- let [whole, fraction = ""] = unsigned.split(".");
9669
- whole = (whole || "0").replace(/^0+(?=\d)/, "");
9670
- fraction = fraction.replace(/0+$/, "");
9671
- const zero = /^0*$/.test(whole) && fraction === "";
9672
- const canonical = fraction === "" ? whole : `${whole}.${fraction}`;
9673
- return negative && !zero ? `-${canonical}` : canonical;
10094
+ return JSON.stringify(decimal);
9674
10095
  }
9675
10096
  async function executeInsert(stmt, client, options, cacheContext) {
9676
10097
  if (stmt.subtableCode) {
9677
10098
  return executeInsertSubtable(stmt, client, options, cacheContext);
9678
10099
  }
10100
+ const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
10101
+ const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
9679
10102
  const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
9680
10103
  const batches = insertToPostBatches(stmt, fieldTypes);
10104
+ assertValidDmlRecords(batches.flatMap((batch) => batch.records), stmt.fields, fieldInfos, numberPrecision);
9681
10105
  const createdIds = [];
9682
10106
  for (const batch of batches) {
9683
10107
  const res = await client.postRecords(batch);
@@ -9690,6 +10114,8 @@ async function executeInsert(stmt, client, options, cacheContext) {
9690
10114
  };
9691
10115
  }
9692
10116
  async function executeInsertSelect(stmt, client, options, cacheContext, cteCache) {
10117
+ const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
10118
+ const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
9693
10119
  const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
9694
10120
  const { rows, columns } = selectResult;
9695
10121
  if (columns.length !== stmt.fields.length) {
@@ -9711,6 +10137,7 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
9711
10137
  });
9712
10138
  return record;
9713
10139
  });
10140
+ assertValidDmlRecords(allRecords, stmt.fields, fieldInfos, numberPrecision);
9714
10141
  const createdIds = [];
9715
10142
  for (let i = 0; i < allRecords.length; i += 100) {
9716
10143
  const batch = allRecords.slice(i, i + 100);
@@ -9724,17 +10151,32 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
9724
10151
  };
9725
10152
  }
9726
10153
  async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
9727
- await assertDmlWhereCapability(stmt, client, cacheContext);
9728
10154
  if (stmt.subtableCode) {
10155
+ await assertDmlWhereCapability(stmt, client, cacheContext);
9729
10156
  return executeUpdateSubtable(stmt, client, options, cacheContext);
9730
10157
  }
10158
+ const fieldInfos = await loadWritableTopLevelDmlFields(
10159
+ stmt.appId,
10160
+ stmt.assignments.map((assignment) => assignment.field),
10161
+ client,
10162
+ cacheContext
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
+ );
10172
+ await assertDmlWhereCapability(stmt, client, cacheContext);
9731
10173
  if (stmt.from != null) {
9732
10174
  return executeUpdateFrom(stmt, stmt.from, client, options, cacheContext, tempTables);
9733
10175
  }
9734
10176
  const maxRecords = options.maxRecords ?? 1e4;
9735
10177
  await resolveSetSubqueries(stmt.assignments, client, options, cacheContext);
9736
10178
  const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
9737
- if (hasArithAssignment(stmt)) {
10179
+ if (hasRowDependentAssignment(stmt)) {
9738
10180
  const getParams2 = updateToGetQueryForArith(stmt);
9739
10181
  const resolved2 = await fetchRecordsForSharedPlan(
9740
10182
  client.getRecords,
@@ -9744,11 +10186,12 @@ async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
9744
10186
  { maxRecords, parallel: options.fetchParallel ?? 1 }
9745
10187
  );
9746
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);
9747
10191
  if (options.confirm) {
9748
10192
  const ok = await options.confirm(records.length, "UPDATE");
9749
10193
  if (!ok) throw new OperationCancelledError("UPDATE", records.length);
9750
10194
  }
9751
- const batches2 = updateToPutBatchesArith(stmt, records, fieldTypes);
9752
10195
  for (const batch of batches2) {
9753
10196
  await client.putRecords(batch);
9754
10197
  }
@@ -9762,11 +10205,12 @@ async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
9762
10205
  { maxRecords, parallel: options.fetchParallel ?? 1 }
9763
10206
  );
9764
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);
9765
10210
  if (options.confirm) {
9766
10211
  const ok = await options.confirm(ids.length, "UPDATE");
9767
10212
  if (!ok) throw new OperationCancelledError("UPDATE", ids.length);
9768
10213
  }
9769
- const batches = updateToPutBatches(stmt, ids, fieldTypes);
9770
10214
  for (const batch of batches) {
9771
10215
  await client.putRecords(batch);
9772
10216
  }
@@ -9774,12 +10218,16 @@ async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
9774
10218
  }
9775
10219
  async function executeUpdateFrom(stmt, from, client, options, cacheContext, tempTables) {
9776
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);
9777
10227
  if (options.confirm) {
9778
10228
  const ok = await options.confirm(matched.length, "UPDATE");
9779
10229
  if (!ok) throw new OperationCancelledError("UPDATE", matched.length);
9780
10230
  }
9781
- const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
9782
- const batches = updateFromToPutBatches(stmt, matched, fieldTypes);
9783
10231
  for (const batch of batches) await client.putRecords(batch);
9784
10232
  return { type: "UPDATE", updatedCount: matched.length };
9785
10233
  }
@@ -9828,6 +10276,8 @@ async function executeDelete(stmt, client, options, cacheContext) {
9828
10276
  return { type: "DELETE", deletedCount: ids.length };
9829
10277
  }
9830
10278
  async function executeUpsert(stmt, client, options, cacheContext) {
10279
+ const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
10280
+ const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
9831
10281
  const toInsert = [];
9832
10282
  const toUpdate = [];
9833
10283
  const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
@@ -9836,7 +10286,7 @@ async function executeUpsert(stmt, client, options, cacheContext) {
9836
10286
  const idx = stmt.fields.indexOf(key);
9837
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`);
9838
10288
  const val = row[idx];
9839
- 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(",");
9840
10290
  })
9841
10291
  );
9842
10292
  const targetIndex = await resolveUpsertTargets(stmt.appId, stmt.keyFields, rowKeyValues, client, options, fieldTypes);
@@ -9857,6 +10307,12 @@ async function executeUpsert(stmt, client, options, cacheContext) {
9857
10307
  toInsert.push(record);
9858
10308
  }
9859
10309
  });
10310
+ assertValidDmlRecords(
10311
+ [...toInsert, ...toUpdate.map((entry) => entry.record)],
10312
+ stmt.fields,
10313
+ fieldInfos,
10314
+ numberPrecision
10315
+ );
9860
10316
  if (options.confirm && toInsert.length + toUpdate.length > 0) {
9861
10317
  const total = toInsert.length + toUpdate.length;
9862
10318
  const ok = await options.confirm(total, "UPDATE");
@@ -10132,14 +10588,14 @@ function buildSubtableReorderPutParams(appId, parentId, revision, subtableCode,
10132
10588
  }
10133
10589
  function evalAssignmentValueForSubtable(value, row, resolveFieldType) {
10134
10590
  if (value.type === "STRING") return value.value;
10135
- if (value.type === "NUMBER") return String(value.value);
10591
+ if (value.type === "NUMBER") return numberLiteralText(value);
10136
10592
  if (value.type === "ARITH") return String(evalArithExpr(value, row));
10137
10593
  if (value.type === "CASE_VALUE") return evalCaseWhen(value.expr, row, resolveFieldType);
10138
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`);
10139
10595
  }
10140
10596
  function valueToString(value) {
10141
10597
  if (value.type === "STRING") return value.value;
10142
- if (value.type === "NUMBER") return String(value.value);
10598
+ if (value.type === "NUMBER") return numberLiteralText(value);
10143
10599
  if (value.type === "CASE_VALUE") return evalCaseWhen(value.expr, {});
10144
10600
  return value.elements.map((e) => e.value).join(",");
10145
10601
  }
@@ -10254,6 +10710,8 @@ function evalOrderKeyForRow(key, row) {
10254
10710
  }
10255
10711
  }
10256
10712
  async function executeUpsertSelect(stmt, client, options, cacheContext, cteCache) {
10713
+ const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
10714
+ const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
10257
10715
  const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
10258
10716
  const { rows, columns } = selectResult;
10259
10717
  if (columns.length !== stmt.fields.length) {
@@ -10276,6 +10734,7 @@ async function executeUpsertSelect(stmt, client, options, cacheContext, cteCache
10276
10734
  });
10277
10735
  return record;
10278
10736
  });
10737
+ assertValidDmlRecords(records, stmt.fields, fieldInfos, numberPrecision);
10279
10738
  const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
10280
10739
  const rowKeyValues = records.map(
10281
10740
  (record) => stmt.keyFields.map((key) => String(record[key]?.value ?? ""))
@@ -10889,6 +11348,8 @@ function buildInsertSelectPlan(stmt, label, capabilities, orderPlans) {
10889
11348
  }
10890
11349
  function buildUpdatePlan(stmt, label, capabilities, orderPlans) {
10891
11350
  const isArith = hasArithAssignment(stmt);
11351
+ const isStringFunc = stmt.assignments.some((a) => a.value.type === "STRING_FUNC");
11352
+ const isRowDependent = hasRowDependentAssignment(stmt);
10892
11353
  const isSubq = stmt.assignments.some((a) => a.value.type === "SCALAR_SUBQUERY");
10893
11354
  const lines = [];
10894
11355
  if (label) lines.push(label);
@@ -10905,10 +11366,11 @@ function buildUpdatePlan(stmt, label, capabilities, orderPlans) {
10905
11366
  lines.push(` api: GET /k/v1/records.json \u2192 PUT /k/v1/records.json`);
10906
11367
  const setTypes = [];
10907
11368
  if (isArith) setTypes.push("\u7B97\u8853 SET\uFF08\u73FE\u5728\u5024\u3092\u53D6\u5F97\u3057\u3066\u8A08\u7B97\uFF09");
11369
+ if (isStringFunc) setTypes.push("\u6587\u5B57\u5217\u95A2\u6570 SET\uFF08\u73FE\u5728\u5024\u3092\u53D6\u5F97\u3057\u3066\u8A55\u4FA1\uFF09");
10908
11370
  if (isSubq) setTypes.push("\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA SET");
10909
- if (!isArith && !isSubq) setTypes.push("\u5358\u7D14 SET");
11371
+ if (!isRowDependent && !isSubq) setTypes.push("\u5358\u7D14 SET");
10910
11372
  lines.push(` set type: ${setTypes.join(", ")}`);
10911
- if (isArith) {
11373
+ if (isRowDependent) {
10912
11374
  const refFields = collectArithRefFields(stmt);
10913
11375
  if (refFields.length > 0) {
10914
11376
  lines.push(` ref fields: ${refFields.join(", ")}\uFF08GET \u306B\u542B\u3081\u308B\uFF09`);
@@ -10994,6 +11456,7 @@ function collectArithRefFields(stmt) {
10994
11456
  const refs = /* @__PURE__ */ new Set();
10995
11457
  for (const { value } of stmt.assignments) {
10996
11458
  if (value.type === "ARITH") collectArithNodeRefs(value, refs);
11459
+ if (value.type === "STRING_FUNC") collectArithNodeRefs(value, refs);
10997
11460
  }
10998
11461
  return [...refs];
10999
11462
  }
@@ -11006,6 +11469,13 @@ function collectArithNodeRefs(node, out) {
11006
11469
  collectArithNodeRefs(node.left, out);
11007
11470
  collectArithNodeRefs(node.right, out);
11008
11471
  }
11472
+ if (node.type === "STRING_FUNC") {
11473
+ for (const arg of node.args) {
11474
+ if (arg.type !== "STRING" && arg.type !== "AGG_REF" && arg.type !== "AGG_ARITH") {
11475
+ collectArithNodeRefs(arg, out);
11476
+ }
11477
+ }
11478
+ }
11009
11479
  }
11010
11480
  function formatAssignment(a) {
11011
11481
  const v = a.value;
@@ -11013,6 +11483,7 @@ function formatAssignment(a) {
11013
11483
  if (v.type === "NUMBER") return `${a.field} = ${v.value}`;
11014
11484
  if (v.type === "ARITH") return `${a.field} = ${formatArithExprStr(v)}`;
11015
11485
  if (v.type === "CASE_VALUE") return `${a.field} = CASE WHEN ...`;
11486
+ if (v.type === "STRING_FUNC") return `${a.field} = ${v.func}(...)`;
11016
11487
  if (v.type === "SCALAR_SUBQUERY") return `${a.field} = (SELECT ...)`;
11017
11488
  if (v.type === "SOURCE_FIELD") return `${a.field} = ${v.alias}.${v.field}`;
11018
11489
  return `${a.field} = (${v.type})`;
@@ -11022,7 +11493,7 @@ function formatArithExprStr(expr) {
11022
11493
  }
11023
11494
  function formatArithNodeStr(node) {
11024
11495
  if (node.type === "FIELD_REF") return node.field;
11025
- if (node.type === "NUMBER") return String(node.value);
11496
+ if (node.type === "NUMBER") return numberLiteralText(node);
11026
11497
  if (node.type === "ARITH") return `(${formatArithExprStr(node)})`;
11027
11498
  return "...";
11028
11499
  }
@@ -11491,6 +11962,7 @@ function withRequestGate(client, gate) {
11491
11962
  },
11492
11963
  getApps: () => gate.runReadOnly(() => client.getApps()),
11493
11964
  getFields: (appId) => gate.runReadOnly(() => client.getFields(appId)),
11965
+ getNumberPrecision: (appId) => gate.runReadOnly(() => client.getNumberPrecision(appId)),
11494
11966
  getProcessStatuses: (appId) => gate.runReadOnly(() => client.getProcessStatuses(appId)),
11495
11967
  postRecords: (params) => gate.runMutation(() => client.postRecords(params)),
11496
11968
  putRecords: (params) => gate.runMutation(() => client.putRecords(params)),
@@ -12052,6 +12524,16 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
12052
12524
  );
12053
12525
  return flattenFormFieldProperties(res.properties);
12054
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
+ },
12055
12537
  async getProcessStatuses(appId) {
12056
12538
  const qs = new URLSearchParams();
12057
12539
  qs.set("app", String(appId));
@@ -13182,6 +13664,9 @@ function createDryRunClient() {
13182
13664
  getFields: notUsed,
13183
13665
  async getProcessStatuses() {
13184
13666
  return { enable: false, states: [] };
13667
+ },
13668
+ async getNumberPrecision() {
13669
+ return { digits: 30, decimalPlaces: 10, roundingMode: "HALF_EVEN" };
13185
13670
  }
13186
13671
  };
13187
13672
  }
@@ -14199,6 +14684,12 @@ async function run() {
14199
14684
  if (!routed) throw new Error(`AuthError: profile "${pName}" is not resolved for APP${appId}.`);
14200
14685
  return routed.getFields(binding.appId);
14201
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
+ },
14202
14693
  getProcessStatuses: (appId) => {
14203
14694
  const binding = appBindingByMappedApp.get(appId) ?? { appId, profile: profileName.toLowerCase() };
14204
14695
  const pName = binding.profile;